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

This commit is contained in:
2026-07-14 07:39:18 +07:00
parent 7d3fdefa3f
commit 487e9eb8ea
9 changed files with 145 additions and 17 deletions

View File

@@ -16,6 +16,7 @@ import {
interface AttendancePanelProps {
classId: number;
onClose?: () => void;
}
function formatQldtTime(iso?: string): string {
@@ -28,7 +29,7 @@ function formatQldtTime(iso?: string): string {
});
}
export const AttendancePanel: React.FC<AttendancePanelProps> = ({ classId }) => {
export const AttendancePanel: React.FC<AttendancePanelProps> = ({ classId, onClose }) => {
const today = new Date().toISOString().slice(0, 10);
const [date, setDate] = useState(today);
const [period, setPeriod] = useState(1);
@@ -231,7 +232,25 @@ export const AttendancePanel: React.FC<AttendancePanelProps> = ({ classId }) =>
const qldtDirty = Boolean(shiftInfo?.qldtDirty);
return (
<div className="attendance-panel">
<div className="modal-overlay attendance-modal-overlay">
<div className="modal-container attendance-modal-container">
<div className="modal-header attendance-modal-header" style={{ padding: '0.75rem 1.25rem', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div>
<h3 className="modal-title" style={{ margin: 0, fontSize: '1.1rem', display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
📋 Quản điểm danh ca học
</h3>
</div>
<button
type="button"
className="modal-close-btn"
style={{ fontSize: '1.5rem', border: 'none', background: 'none', color: 'var(--text-muted)', cursor: 'pointer', padding: '0 0.5rem' }}
onClick={onClose}
>
&times;
</button>
</div>
<div className="modal-body attendance-modal-body" style={{ padding: '1rem', display: 'flex', flexDirection: 'column', gap: '0.75rem', flex: 1, minHeight: 0, overflowY: 'auto' }}>
<div className="attendance-toolbar">
<label className="attendance-field">
<span>Ngày</span>
@@ -370,13 +389,13 @@ export const AttendancePanel: React.FC<AttendancePanelProps> = ({ classId }) =>
{/* Leave Requests Approval Modal */}
{showLeaveModal && courseId && (
<div className="modal-overlay" onClick={() => setShowLeaveModal(false)}>
<div className="modal-overlay" style={{ zIndex: 200 }} onClick={() => setShowLeaveModal(false)}>
<div className="modal-container" style={{ maxWidth: '680px', width: '95%', display: 'flex', flexDirection: 'column' }} onClick={e => e.stopPropagation()}>
<div className="modal-header" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div>
<h3 className="modal-title" style={{ margin: 0 }}> Đơn xin nghỉ phép trong ca</h3>
<p style={{ margin: '4px 0 0 0', color: 'var(--text-secondary)', fontSize: '0.8rem' }}>
Ca {period} ngày {date} - Giao diện duyệt đơn xin nghỉ từ cổng QLĐT portal.
Ca {period} ngày {date} - Giao diện duyệt đơn xin nghỉ học.
</p>
</div>
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
@@ -395,6 +414,11 @@ export const AttendancePanel: React.FC<AttendancePanelProps> = ({ classId }) =>
</div>
</div>
<div className="modal-body" style={{ padding: '1rem', overflowY: 'auto', maxHeight: '60vh', display: 'flex', flexDirection: 'column', gap: '10px' }}>
<div style={{ padding: '0.65rem 0.85rem', background: '#e0f2fe', border: '1px solid #bae6fd', borderRadius: '6px', color: '#0369a1', fontSize: '0.82rem', display: 'flex', flexDirection: 'column', gap: '0.25rem', lineHeight: '1.4' }}>
<div>💡 <strong>Lưu ý duyệt đơn phép:</strong> Thao tác duyệt/từ chối đơn tại đây chỉ đưc ghi nhận <strong>nội bộ trên hệ thống Simple Care</strong> (không đng bộ ngược lên QLĐT).</div>
<div style={{ paddingLeft: '1.15rem' }}> Khi duyệt đơn, hệ thống sẽ tự đng đi trạng thái điểm danh của sinh viên này thành <strong>&quot;Nghỉ phép&quot;</strong>.</div>
<div style={{ paddingLeft: '1.15rem' }}> Trạng thái này sẽ đưc cập nhật lên QLĐT khi thầy bấm nút <strong>&quot;Đy QLĐT&quot;</strong> giao diện điểm danh ca học.</div>
</div>
{loadingLeave ? (
<div style={{ display: 'flex', justifyContent: 'center', padding: '2rem 0' }}>
<div className="sync-spinner" style={{ width: 28, height: 28 }} />
@@ -624,6 +648,8 @@ export const AttendancePanel: React.FC<AttendancePanelProps> = ({ classId }) =>
</table>
)}
</div>
</div>
</div>
</div>
);
};

View File

@@ -446,7 +446,7 @@ export const ClassWorkspace: React.FC<ClassWorkspaceProps> = ({ classId, sourceT
<div className={`workspace-panel-body ${activeSubTab === 'attendance' || activeSubTab === 'logs' || activeSubTab === 'grid' || activeSubTab === 'roster' || activeSubTab === 'violations' ? 'workspace-panel-fill' : ''}`}>
{activeSubTab === 'attendance' ? (
<AttendancePanel classId={classId} />
<AttendancePanel classId={classId} onClose={() => setActiveSubTab('roster')} />
) : activeSubTab === 'violations' ? (
<ViolationsPanel mode="class" classId={classId} />
) : activeSubTab === 'grid' ? (

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';
import {
apiFetchClassViolations,
apiFetchExamViolations,
@@ -24,7 +24,7 @@ function formatTime(iso?: string): string {
return d.toLocaleString('vi-VN');
}
export const ViolationsPanel: React.FC<Props> = (props) => {
export const ViolationsPanel = (props: Props) => {
const [date, setDate] = useState(todayLocal);
const [kind, setKind] = useState('');
const [rows, setRows] = useState<StudentViolationItem[]>([]);
@@ -54,6 +54,23 @@ export const ViolationsPanel: React.FC<Props> = (props) => {
return (
<div className="session-logs-panel" style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem', height: '100%' }}>
{props.mode === 'exam' && (
<div style={{
padding: '0.65rem 0.85rem',
background: '#fef3c7',
border: '1px solid #fcd34d',
borderRadius: '6px',
color: '#92400e',
fontSize: '0.82rem',
display: 'flex',
alignItems: 'center',
gap: '0.35rem',
margin: '0',
lineHeight: '1.4'
}}>
<strong>Lưu ý:</strong> Chức năng theo dõi vi phạm đang đưc theo dõi đánh giá tính chuẩn xác, hiện tại kết quả chạy thử chỉ mang tính chất tham khảo thu thập dữ liệu theo các loại máy.
</div>
)}
<div className="attendance-toolbar" style={{ marginBottom: 0, flexWrap: 'wrap' }}>
<label className="attendance-field">
<span>Ngày</span>

View File

@@ -2037,6 +2037,40 @@ input:checked + .slider:before {
height: 100%;
}
.attendance-modal-overlay {
z-index: 150 !important;
background-color: rgba(15, 23, 42, 0.6) !important;
padding: 1.5rem;
backdrop-filter: blur(4px);
display: flex;
align-items: center;
justify-content: center;
}
.attendance-modal-container {
width: 98vw !important;
height: 96vh !important;
max-width: 1440px !important;
max-height: 96vh !important;
border-radius: var(--radius-lg);
display: flex;
flex-direction: column;
background-color: var(--bg-app);
box-shadow: var(--shadow-2xl);
border: 1px solid var(--border-color);
animation: modalScaleUp 0.2s ease-out;
}
.attendance-modal-body {
flex: 1;
min-height: 0;
overflow-y: auto;
padding: 1.25rem;
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.attendance-toolbar {
display: flex;
flex-wrap: wrap;