add tai lieu system
All checks were successful
Deploy on Master Change / deploy (push) Successful in 43s
All checks were successful
Deploy on Master Change / deploy (push) Successful in 43s
This commit is contained in:
@@ -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 có 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 có 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">×</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">×</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>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user