This commit is contained in:
@@ -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 — có thay đổi mới</strong>
|
||||||
<strong>Đã lưu QLĐT — có 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 "Đẩy QLĐT" 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 "Đẩy QLĐT" 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ộ và <strong>KHÔNG tự động lưu lên QLĐT</strong>. Thầy cô 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 trạng 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ộ và <strong>KHÔNG tự động lưu lên QLĐT</strong>. Thầy cô 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">
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user