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>
</div>
{!isCollapsed && (
<>
<div
className={`attendance-qldt-banner${
qldtSynced ? (qldtDirty ? ' attendance-qldt-banner--stale' : ' attendance-qldt-banner--synced') : ''
}`}
>
{qldtSynced ? (
qldtDirty ? (
<>
<strong>Đã lưu QLĐT thay đi mới</strong>
<span>
Lần đy: {formatQldtTime(shiftInfo?.pushedToQldtAt)} · Cần đy lại sau khi chỉnh sửa
</span>
</>
) : (
<>
<strong>Đã lưu QLĐT</strong>
<span>Lần đy gần nhất: {formatQldtTime(shiftInfo?.pushedToQldtAt)}</span>
</>
)
<div className={`attendance-collapsible-wrapper ${isCollapsed ? 'collapsed' : ''}`}>
<div
className={`attendance-qldt-banner${
qldtSynced ? (qldtDirty ? ' attendance-qldt-banner--stale' : ' attendance-qldt-banner--synced') : ''
}`}
>
{qldtSynced ? (
qldtDirty ? (
<>
<strong>Đã lưu QLĐT thay đi mới</strong>
<span>
Lần đy: {formatQldtTime(shiftInfo?.pushedToQldtAt)} · Cần đy lại sau khi chỉnh sửa
</span>
</>
) : (
<>
<strong>Chưa đy lên 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>
<strong>Đã lưu QLĐT</strong>
<span>Lần đy gần nhất: {formatQldtTime(shiftInfo?.pushedToQldtAt)}</span>
</>
)}
</div>
{currentShift && (
<div className="attendance-shift-info">
<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>
)
) : (
<>
<strong>Chưa đy lên 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>
</>
)}
</div>
<div className="attendance-status-summary">
{ATTENDANCE_STATUS_OPTIONS.map(opt => (
<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>
{currentShift && (
<div className="attendance-shift-info">
<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>
)}
<p className="schedule-hint" style={{ margin: '0 0 8px 0' }}>
Sửa trạng thái thủ công sẽ đưc khóa hệ thống tự tính sẽ không ghi đè.
</p>
<div className="attendance-status-summary">
{ATTENDANCE_STATUS_OPTIONS.map(opt => (
<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' }}>
💡 <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>
</>
)}
<p className="schedule-hint" style={{ margin: '0 0 4px 0' }}>
Sửa trng thái thủ công sẽ đưc khóa hệ thống t tính sẽ không ghi đè.
</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 && (
<div className="attendance-bulk-actions">

View File

@@ -4653,10 +4653,13 @@ input:checked + .slider:before {
}
}
/* ── System management ── */
.system-page {
overflow-y: auto;
padding-bottom: 1.5rem;
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
overflow: hidden;
padding-bottom: 1rem;
}
.system-subnav {
@@ -4728,20 +4731,30 @@ input:checked + .slider:before {
}
.system-panel {
min-height: 200px;
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.system-section {
display: flex;
flex-direction: column;
gap: 1rem;
flex: 1;
min-height: 0;
overflow: hidden;
}
.system-section--grid {
display: grid;
grid-template-columns: 1fr 280px;
gap: 1rem;
align-items: start;
align-items: stretch;
flex: 1;
min-height: 0;
overflow: hidden;
}
.system-block {
@@ -4750,6 +4763,9 @@ input:checked + .slider:before {
border-radius: var(--radius-md);
padding: 1.15rem 1.25rem;
box-shadow: var(--shadow-sm);
display: flex;
flex-direction: column;
min-height: 0;
}
.system-block--info {
@@ -4825,6 +4841,9 @@ input:checked + .slider:before {
display: flex;
flex-direction: column;
gap: 0.45rem;
overflow-y: auto;
flex: 1;
min-height: 0;
}
.system-domain-item {
@@ -4890,7 +4909,10 @@ input:checked + .slider:before {
.system-table-wrap {
overflow-x: auto;
overflow-y: auto;
margin-bottom: 0.75rem;
flex: 1;
min-height: 0;
}
.template-grid {
@@ -5414,3 +5436,21 @@ input:checked + .slider:before {
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;
}