fix ui
All checks were successful
Deploy on Master Change / deploy (push) Successful in 48s

This commit is contained in:
2026-07-03 18:33:49 +07:00
parent ea73c15738
commit d054fce432
2 changed files with 100 additions and 62 deletions

View File

@@ -236,71 +236,69 @@ export const AttendancePanel: React.FC<AttendancePanelProps> = ({ classId }) =>
</button> </button>
</div> </div>
{!isCollapsed && ( <div className={`attendance-collapsible-wrapper ${isCollapsed ? 'collapsed' : ''}`}>
<> <div
<div className={`attendance-qldt-banner${
className={`attendance-qldt-banner${ qldtSynced ? (qldtDirty ? ' attendance-qldt-banner--stale' : ' attendance-qldt-banner--synced') : ''
qldtSynced ? (qldtDirty ? ' attendance-qldt-banner--stale' : ' attendance-qldt-banner--synced') : '' }`}
}`} >
> {qldtSynced ? (
{qldtSynced ? ( qldtDirty ? (
qldtDirty ? ( <>
<> <strong>Đã lưu QLĐT thay đi mới</strong>
<strong>Đã lưu QLĐT thay đi mới</strong> <span>
<span> Lần đy: {formatQldtTime(shiftInfo?.pushedToQldtAt)} · Cần đy lại sau khi chỉnh sửa
Lần đy: {formatQldtTime(shiftInfo?.pushedToQldtAt)} · Cần đy lại sau khi chỉnh sửa </span>
</span> </>
</>
) : (
<>
<strong>Đã lưu QLĐT</strong>
<span>Lần đy gần nhất: {formatQldtTime(shiftInfo?.pushedToQldtAt)}</span>
</>
)
) : ( ) : (
<> <>
<strong>Chưa đy lên QLĐT</strong> <strong>Đã lưu QLĐT</strong>
<span>Ca {period} ngày {date} bấm &quot;Đy QLĐT&quot; sau khi kiểm tra trạng thái</span> <span>Lần đy gần nhất: {formatQldtTime(shiftInfo?.pushedToQldtAt)}</span>
</> </>
)} )
</div> ) : (
<>
{currentShift && ( <strong>Chưa đy lên QLĐT</strong>
<div className="attendance-shift-info"> <span>Ca {period} ngày {date} bấm &quot;Đy QLĐT&quot; sau khi kiểm tra trạng thái</span>
<span>{currentShift.startTime}{currentShift.endTime}</span> </>
<span>{currentShift.courseName || 'Chưa chọn môn'}</span>
{!currentShift.isActive && <span className="badge badge-muted">Ca tắt</span>}
</div>
)} )}
</div>
<div className="attendance-status-summary"> {currentShift && (
{ATTENDANCE_STATUS_OPTIONS.map(opt => ( <div className="attendance-shift-info">
<button <span>{currentShift.startTime}{currentShift.endTime}</span>
key={opt.value} <span>{currentShift.courseName || 'Chưa chọn môn'}</span>
type="button" {!currentShift.isActive && <span className="badge badge-muted">Ca tắt</span>}
className={`attendance-summary-chip ${attendanceStatusClass(opt.value)}`}
onClick={() => setSearchQuery('')}
title={`${opt.label}: ${statusCounts[opt.value] ?? 0} sinh viên`}
>
<span className="attendance-summary-chip-label">{opt.short}</span>
<span className="attendance-summary-chip-count">{statusCounts[opt.value] ?? 0}</span>
</button>
))}
<span className="attendance-summary-total">
{filteredRows.length}/{rows.length} SV
{searchQuery.trim() ? ' (đã lọc)' : ''}
</span>
</div> </div>
)}
<p className="schedule-hint" style={{ margin: '0 0 8px 0' }}> <div className="attendance-status-summary">
Sửa trạng thái thủ công sẽ đưc khóa hệ thống tự tính sẽ không ghi đè. {ATTENDANCE_STATUS_OPTIONS.map(opt => (
</p> <button
key={opt.value}
type="button"
className={`attendance-summary-chip ${attendanceStatusClass(opt.value)}`}
onClick={() => setSearchQuery('')}
title={`${opt.label}: ${statusCounts[opt.value] ?? 0} sinh viên`}
>
<span className="attendance-summary-chip-label">{opt.short}</span>
<span className="attendance-summary-chip-count">{statusCounts[opt.value] ?? 0}</span>
</button>
))}
<span className="attendance-summary-total">
{filteredRows.length}/{rows.length} SV
{searchQuery.trim() ? ' (đã lọc)' : ''}
</span>
</div>
<div style={{ padding: '0.65rem 0.85rem', background: '#fef3c7', border: '1px solid #fcd34d', borderRadius: '4px', color: '#92400e', fontSize: '0.82rem', display: 'flex', alignItems: 'center', gap: '0.35rem', margin: '4px 0 10px 0', lineHeight: '1.4' }}> <p className="schedule-hint" style={{ margin: '0 0 4px 0' }}>
💡 <strong>Lưu ý:</strong> Hệ thống hiện tại chỉ ghi nhận điểm danh học tập nội bộ <strong>KHÔNG tự đng lưu lên QLĐT</strong>. Thầy vui lòng kiểm tra kỹ trạng thái của sinh viên, sau đó bấm nút <strong>Đy QLĐT</strong> góc phải bên trên đ đng bộ điểm danh chính thức. Sửa trng thái thủ công sẽ đưc khóa hệ thống t tính sẽ không ghi đè.
</div> </p>
</>
)} <div style={{ padding: '0.65rem 0.85rem', background: '#fef3c7', border: '1px solid #fcd34d', borderRadius: '4px', color: '#92400e', fontSize: '0.82rem', display: 'flex', alignItems: 'center', gap: '0.35rem', margin: '4px 0 4px 0', lineHeight: '1.4' }}>
💡 <strong>Lưu ý:</strong> Hệ thống hiện tại chỉ ghi nhận điểm danh học tập nội bộ <strong>KHÔNG tự đng lưu lên QLĐT</strong>. Thầy vui lòng kiểm tra kỹ trạng thái của sinh viên, sau đó bấm nút <strong>Đy QLĐT</strong> góc phải bên trên đ đng bộ điểm danh chính thức.
</div>
</div>
{selectedStudentRkIds.length > 0 && ( {selectedStudentRkIds.length > 0 && (
<div className="attendance-bulk-actions"> <div className="attendance-bulk-actions">

View File

@@ -4653,10 +4653,13 @@ input:checked + .slider:before {
} }
} }
/* ── System management ── */
.system-page { .system-page {
overflow-y: auto; display: flex;
padding-bottom: 1.5rem; flex-direction: column;
flex: 1;
min-height: 0;
overflow: hidden;
padding-bottom: 1rem;
} }
.system-subnav { .system-subnav {
@@ -4728,20 +4731,30 @@ input:checked + .slider:before {
} }
.system-panel { .system-panel {
min-height: 200px; flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
} }
.system-section { .system-section {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1rem; gap: 1rem;
flex: 1;
min-height: 0;
overflow: hidden;
} }
.system-section--grid { .system-section--grid {
display: grid; display: grid;
grid-template-columns: 1fr 280px; grid-template-columns: 1fr 280px;
gap: 1rem; gap: 1rem;
align-items: start; align-items: stretch;
flex: 1;
min-height: 0;
overflow: hidden;
} }
.system-block { .system-block {
@@ -4750,6 +4763,9 @@ input:checked + .slider:before {
border-radius: var(--radius-md); border-radius: var(--radius-md);
padding: 1.15rem 1.25rem; padding: 1.15rem 1.25rem;
box-shadow: var(--shadow-sm); box-shadow: var(--shadow-sm);
display: flex;
flex-direction: column;
min-height: 0;
} }
.system-block--info { .system-block--info {
@@ -4825,6 +4841,9 @@ input:checked + .slider:before {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0.45rem; gap: 0.45rem;
overflow-y: auto;
flex: 1;
min-height: 0;
} }
.system-domain-item { .system-domain-item {
@@ -4890,7 +4909,10 @@ input:checked + .slider:before {
.system-table-wrap { .system-table-wrap {
overflow-x: auto; overflow-x: auto;
overflow-y: auto;
margin-bottom: 0.75rem; margin-bottom: 0.75rem;
flex: 1;
min-height: 0;
} }
.template-grid { .template-grid {
@@ -5414,3 +5436,21 @@ input:checked + .slider:before {
margin-bottom: 0.75rem; margin-bottom: 0.75rem;
} }
.attendance-collapsible-wrapper {
max-height: 500px;
opacity: 1;
overflow: hidden;
transition: max-height 0.3s ease-in-out, opacity 0.25s ease-in-out, margin 0.3s ease-in-out;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.attendance-collapsible-wrapper.collapsed {
max-height: 0;
opacity: 0;
margin: 0 !important;
padding: 0 !important;
pointer-events: none;
}