add tai lieu system
All checks were successful
Deploy on Master Change / deploy (push) Successful in 43s

This commit is contained in:
2026-07-01 14:47:46 +07:00
parent 6919513165
commit dd766206f7
15 changed files with 952 additions and 140 deletions

View File

@@ -266,32 +266,41 @@ export const LearningTab: React.FC = () => {
<div className="content-card">
{loading && (
<div className="learning-empty">
<span className="learning-empty-icon">🔄</span>
<p>Đang tải...</p>
<div className="learning-empty" style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '0.75rem', padding: '3rem 1.5rem' }}>
<div className="sync-spinner" style={{ width: '36px', height: '36px', borderWidth: '3px' }}></div>
<p style={{ color: 'var(--text-secondary)', fontSize: '0.9rem' }}>Đang tải dữ liệu...</p>
</div>
)}
{!loading && activeClasses.length === 0 && (
<div className="learning-empty">
<span className="learning-empty-icon">📅</span>
<p>Chưa lớp nào đưc tạo lịch học.</p>
<button className="btn btn-secondary" onClick={handleOpenAddClass}>
<div className="learning-empty" style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '0.75rem', padding: '3rem 1.5rem' }}>
<svg viewBox="0 0 24 24" width="48" height="48" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" style={{ color: 'var(--text-muted)', marginBottom: '0.5rem' }}>
<rect x="3" y="4" width="18" height="18" rx="2" ry="2" />
<line x1="16" y1="2" x2="16" y2="6" />
<line x1="8" y1="2" x2="8" y2="6" />
<line x1="3" y1="10" x2="21" y2="10" />
</svg>
<p style={{ color: 'var(--text-secondary)', fontSize: '0.95rem', fontWeight: 500 }}>Chưa lớp nào đưc tạo lịch học.</p>
<button className="btn btn-secondary" onClick={handleOpenAddClass} style={{ marginTop: '0.5rem' }}>
Bắt đu thêm lớp đu tiên
</button>
</div>
)}
{!loading && activeClasses.length > 0 && filteredClasses.length === 0 && (
<div className="learning-empty">
<span className="learning-empty-icon">🔍</span>
<p>Không tìm thấy lớp phù hợp với bộ lọc hiện tại.</p>
<div className="learning-empty" style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '0.75rem', padding: '3rem 1.5rem' }}>
<svg viewBox="0 0 24 24" width="48" height="48" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" style={{ color: 'var(--text-muted)', marginBottom: '0.5rem' }}>
<circle cx="11" cy="11" r="8" />
<line x1="21" y1="21" x2="16.65" y2="16.65" />
</svg>
<p style={{ color: 'var(--text-secondary)', fontSize: '0.95rem', fontWeight: 500 }}>Không tìm thấy lớp phù hợp với bộ lọc hiện tại.</p>
<button
className="btn btn-secondary"
onClick={() => {
setSearchQuery('');
setDayFilter(null);
}}
style={{ marginTop: '0.5rem' }}
>
Xóa bộ lọc
</button>
@@ -385,8 +394,8 @@ export const LearningTab: React.FC = () => {
</div>
{configClass && (
<div className="modal-overlay">
<div className="modal-container" style={{ maxWidth: '800px', width: '90%' }}>
<div className="modal-overlay" onClick={() => setConfigClass(null)}>
<div className="modal-container" style={{ maxWidth: '800px', width: '90%' }} onClick={e => e.stopPropagation()}>
<div className="modal-header">
<div>
<h2 className="modal-title">Cấu hình lịch học & ng dụng</h2>
@@ -394,9 +403,9 @@ export const LearningTab: React.FC = () => {
{configClass.name} ({configClass.classCode})
</p>
</div>
<button className="btn btn-secondary close-btn" onClick={() => setConfigClass(null)}>Đóng</button>
<button className="modal-close-btn" onClick={() => setConfigClass(null)} aria-label="Đóng">&times;</button>
</div>
<div style={{ padding: '1rem 0', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1.5rem' }}>
<div className="modal-body" style={{ padding: '1.75rem', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1.5rem' }}>
<div style={{ borderRight: '1px solid var(--border-color)', paddingRight: '1.5rem' }}>
<h4 style={{ margin: '0 0 1rem 0' }}>Ca học trong tuần</h4>
<ScheduleEditor
@@ -412,11 +421,25 @@ export const LearningTab: React.FC = () => {
Từ khóa ngăn chặn các ng dụng ngoài luồng trên máy sinh viên.
</p>
<textarea
rows={6}
rows={8}
value={allowedApps}
onChange={e => setAllowedApps(e.target.value)}
placeholder="chrome,idea64,vscode,wails,simple_care"
className="app-textarea"
style={{
width: '100%',
padding: '0.75rem',
borderRadius: 'var(--radius-sm)',
border: '1px solid var(--border-color)',
fontFamily: 'monospace',
fontSize: '0.85rem',
color: 'var(--text-primary)',
backgroundColor: '#fff',
outline: 'none',
transition: 'var(--transition)',
resize: 'vertical'
}}
onFocus={(e) => e.target.style.borderColor = 'var(--accent)'}
onBlur={(e) => e.target.style.borderColor = 'var(--border-color)'}
/>
</div>
<button className="btn btn-primary" onClick={handleSaveApps} style={{ width: '100%', marginTop: '15px' }}>
@@ -424,6 +447,9 @@ export const LearningTab: React.FC = () => {
</button>
</div>
</div>
<div className="modal-footer">
<button className="btn btn-secondary" onClick={() => setConfigClass(null)}>Đóng</button>
</div>
</div>
</div>
)}
@@ -500,33 +526,64 @@ export const LearningTab: React.FC = () => {
)}
{showAddClassModal && (
<div className="modal-overlay">
<div className="modal-container" style={{ maxWidth: '500px' }}>
<div className="modal-overlay" onClick={() => setShowAddClassModal(false)}>
<div className="modal-container" style={{ maxWidth: '500px' }} onClick={e => e.stopPropagation()}>
<div className="modal-header">
<h2 className="modal-title">Chọn lớp học từ hệ thống</h2>
<button className="btn btn-secondary close-btn" onClick={() => setShowAddClassModal(false)}>Đóng</button>
<button className="modal-close-btn" onClick={() => setShowAddClassModal(false)} aria-label="Đóng">&times;</button>
</div>
<div style={{ padding: '1rem 0' }}>
<input
type="search"
className="app-pool-search"
placeholder="Tìm tên lớp hoặc mã lớp..."
value={searchClassQuery}
onChange={e => setSearchClassQuery(e.target.value)}
/>
<div style={{ maxHeight: '300px', overflowY: 'auto', marginTop: '1rem' }}>
<div className="modal-body" style={{ padding: '1.5rem' }}>
<div className="search-input-wrapper" style={{ width: '100%', marginBottom: '1.25rem' }}>
<input
type="search"
className="search-input"
placeholder="Tìm tên lớp hoặc mã lớp..."
value={searchClassQuery}
onChange={e => setSearchClassQuery(e.target.value)}
/>
<span className="search-icon">
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<circle cx="11" cy="11" r="8" />
<line x1="21" y1="21" x2="16.65" y2="16.65" />
</svg>
</span>
</div>
<div style={{ maxHeight: '300px', overflowY: 'auto' }}>
{filteredAddClasses.length === 0 ? (
<p style={{ color: 'var(--text-muted)', textAlign: 'center' }}>Không tìm thấy lớp phù hợp.</p>
<p style={{ color: 'var(--text-muted)', textAlign: 'center', padding: '1rem 0' }}>Không tìm thấy lớp phù hợp.</p>
) : (
<ul className="learning-add-list">
<ul style={{ listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
{filteredAddClasses.map(c => (
<li key={c.rkId} className="learning-add-item">
<li key={c.rkId} style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '0.85rem 1rem',
backgroundColor: 'var(--bg-app)',
border: '1px solid var(--border-color)',
borderRadius: 'var(--radius-sm)',
transition: 'var(--transition)'
}}
onMouseEnter={(e) => {
e.currentTarget.style.borderColor = 'rgba(187, 33, 38, 0.25)';
e.currentTarget.style.backgroundColor = '#fff';
e.currentTarget.style.boxShadow = 'var(--shadow-sm)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.borderColor = 'var(--border-color)';
e.currentTarget.style.backgroundColor = 'var(--bg-app)';
e.currentTarget.style.boxShadow = 'none';
}}
>
<div>
<div className="learning-class-code">{c.classCode}</div>
<div className="learning-class-meta">{c.name}</div>
<div style={{ fontWeight: 700, color: 'var(--text-primary)', fontSize: '0.9rem' }}>{c.classCode}</div>
<div style={{ fontSize: '0.8rem', color: 'var(--text-secondary)', marginTop: '2px' }}>{c.name}</div>
</div>
<button className="btn btn-primary btn-sm" onClick={() => handleActivateClass(c)}>
Chọn
<button className="btn btn-primary btn-sm" onClick={() => handleActivateClass(c)} style={{ display: 'inline-flex', alignItems: 'center', gap: '4px' }}>
Kích hoạt
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<path d="M5 12h14M12 5l7 7-7 7" />
</svg>
</button>
</li>
))}
@@ -534,6 +591,9 @@ export const LearningTab: React.FC = () => {
)}
</div>
</div>
<div className="modal-footer">
<button className="btn btn-secondary" onClick={() => setShowAddClassModal(false)}>Đóng</button>
</div>
</div>
</div>
)}