This commit is contained in:
2026-06-30 09:31:33 +07:00
parent c736326162
commit bbf8336664
77 changed files with 12601 additions and 556 deletions

View File

@@ -1,65 +1,165 @@
import { useState } from 'react';
import { useEffect } from 'react';
import { DashboardTab } from './components/DashboardTab';
import { ClassesTab } from './components/ClassesTab';
import { StudentsTab } from './components/StudentsTab';
import { LearningTab } from './components/LearningTab';
import { NetworkTab } from './components/NetworkTab';
import { ClassWorkspace } from './components/ClassWorkspace';
import { NavHistoryBar, useRoute } from './components/NavHistoryBar';
import { goBack, navigate, parseRoute, pushNav, TAB_LABELS, type TabId } from './navigation';
const IconDashboard = () => (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<rect x="3" y="3" width="7" height="7" rx="1" /><rect x="14" y="3" width="7" height="7" rx="1" />
<rect x="3" y="14" width="7" height="7" rx="1" /><rect x="14" y="14" width="7" height="7" rx="1" />
</svg>
);
const IconClass = () => (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
<polyline points="9 22 9 12 15 12 15 22" />
</svg>
);
const IconStudent = () => (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
<circle cx="9" cy="7" r="4" />
<path d="M23 21v-2a4 4 0 0 0-3-3.87" /><path d="M16 3.13a4 4 0 0 1 0 7.75" />
</svg>
);
const IconLearning = () => (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" />
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" />
</svg>
);
const IconNetwork = () => (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M5 12.55a11 11 0 0 1 14.08 0" />
<path d="M1.42 9a16 16 0 0 1 21.16 0" />
<path d="M8.53 16.11a6 6 0 0 1 6.95 0" />
<circle cx="12" cy="20" r="1" />
</svg>
);
function App() {
const [activeTab, setActiveTab] = useState<string>('dashboard');
const route = useRoute();
useEffect(() => {
const initial = parseRoute();
if (initial.classId) {
return;
}
pushNav({ kind: 'tab', tab: initial.tab, label: TAB_LABELS[initial.tab] });
}, []);
const setActiveTab = (tab: string) => {
navigate(tab as TabId);
};
const handleBackFromClass = () => {
goBack(route.tab);
};
return (
<>
{/* Sidebar navigation panel */}
<aside className="sidebar">
<div className="brand">
<div className="brand-logo">SC</div>
<span className="brand-name">Simple Care</span>
<div className="brand-logo">RE</div>
<div className="brand-text">
<span className="brand-name">Simple Care</span>
<span className="brand-sub">Rikkei Education</span>
</div>
</div>
<nav>
<ul className="nav-links">
<div className="nav-header">Tổng quan</div>
<li className="nav-item">
<button
className={`nav-btn ${activeTab === 'dashboard' ? 'active' : ''}`}
onClick={() => setActiveTab('dashboard')}
className={`nav-btn ${route.tab === 'dashboard' && !route.classId ? 'active' : ''}`}
onClick={() => navigate('dashboard')}
>
<span className="nav-icon">📊</span>
Tổng Quan
<span className="nav-icon"><IconDashboard /></span>
Tổng quan
</button>
</li>
<div className="nav-header">CSDL liên kết</div>
<li className="nav-item">
<button
className={`nav-btn ${route.tab === 'classes' && !route.classId ? 'active' : ''}`}
onClick={() => navigate('classes')}
>
<span className="nav-icon"><IconClass /></span>
Lớp học
</button>
</li>
<li className="nav-item">
<button
className={`nav-btn ${activeTab === 'classes' ? 'active' : ''}`}
onClick={() => setActiveTab('classes')}
className={`nav-btn ${route.tab === 'students' && !route.classId ? 'active' : ''}`}
onClick={() => navigate('students')}
>
<span className="nav-icon">🏫</span>
Lớp Học
<span className="nav-icon"><IconStudent /></span>
Sinh viên
</button>
</li>
<div className="nav-header">Quản học tập</div>
<li className="nav-item">
<button
className={`nav-btn ${route.tab === 'learning' && !route.classId ? 'active' : ''}`}
onClick={() => navigate('learning')}
>
<span className="nav-icon"><IconLearning /></span>
Giám sát & Lịch học
</button>
</li>
<li className="nav-item">
<button
className={`nav-btn ${activeTab === 'students' ? 'active' : ''}`}
onClick={() => setActiveTab('students')}
className={`nav-btn ${route.tab === 'network' && !route.classId ? 'active' : ''}`}
onClick={() => navigate('network')}
>
<span className="nav-icon">🎓</span>
Sinh Viên
<span className="nav-icon"><IconNetwork /></span>
Quản mạng
</button>
</li>
</ul>
</nav>
<div className="sidebar-footer">
<div style={{ fontSize: '0.8rem', color: 'var(--text-muted)' }}>Cấu hình Token:</div>
<div style={{ fontSize: '0.75rem', color: 'var(--text-muted)', fontWeight: 500 }}>Kết nối hệ thống</div>
<div className="token-badge" title="Token QLDT_TOKEN load từ server .env">
🔑 QLDT_TOKEN (Loaded)
QLDT_TOKEN đã kết nối
</div>
</div>
</aside>
{/* Main dashboard content container */}
<main className="main-content">
{activeTab === 'dashboard' && <DashboardTab onNavigate={setActiveTab} />}
{activeTab === 'classes' && <ClassesTab />}
{activeTab === 'students' && <StudentsTab />}
<div className="page-viewport">
{route.classId ? (
<ClassWorkspace
classId={route.classId}
sourceTab={route.tab}
onBack={handleBackFromClass}
renderNavBar={(className) => (
<NavHistoryBar classLabel={className} onBack={handleBackFromClass} />
)}
/>
) : (
<>
{route.tab === 'dashboard' && <DashboardTab onNavigate={setActiveTab} />}
{route.tab === 'classes' && <ClassesTab />}
{route.tab === 'students' && <StudentsTab />}
{route.tab === 'learning' && <LearningTab />}
{route.tab === 'network' && <NetworkTab />}
</>
)}
</div>
</main>
</>
);