diff --git a/management/src/App.tsx b/management/src/App.tsx index c5e331b..a3a7a8a 100644 --- a/management/src/App.tsx +++ b/management/src/App.tsx @@ -4,7 +4,7 @@ import { ClassesTab } from './components/ClassesTab'; import { StudentsTab } from './components/StudentsTab'; import { LearningTab } from './components/LearningTab'; import { NetworkTab } from './components/NetworkTab'; -import { AccountsTab } from './components/AccountsTab'; +import { EmailDomainsTab, MyAccountTab } from './components/AccountsTab'; import { ChatWidget } from './components/ChatWidget'; import { StaffChatSocket } from './hooks/useStaffChatSocket'; import { ClassWorkspace } from './components/ClassWorkspace'; @@ -50,10 +50,10 @@ const IconNetwork = () => ( ); -const IconAccounts = () => ( +const IconEmail = () => ( - - + + ); @@ -144,21 +144,29 @@ function App() {
Hệ thống
  • -
    - {staff?.email} -
    - +
    @@ -182,7 +190,8 @@ function App() { {route.tab === 'students' && } {route.tab === 'learning' && } {route.tab === 'network' && } - {route.tab === 'accounts' && } + {route.tab === 'email-domains' && } + {route.tab === 'profile' && } )} diff --git a/management/src/components/AccountsTab.tsx b/management/src/components/AccountsTab.tsx index 1b7eb4a..120bddd 100644 --- a/management/src/components/AccountsTab.tsx +++ b/management/src/components/AccountsTab.tsx @@ -64,14 +64,9 @@ export function ChangePasswordPage({ forced }: { forced?: boolean }) { ); } -export function AccountsTab() { - const { staff, changePassword } = useAuth(); +export function EmailDomainsTab() { const [domains, setDomains] = useState([]); const [newDomain, setNewDomain] = useState(''); - const [oldPw, setOldPw] = useState(''); - const [newPw, setNewPw] = useState(''); - const [msg, setMsg] = useState(''); - const [err, setErr] = useState(''); const load = async () => { const res = await apiAdmin.listEmailDomains(); @@ -95,32 +90,13 @@ export function AccountsTab() { await load(); }; - const submitPw = async (e: React.FormEvent) => { - e.preventDefault(); - setErr(''); - setMsg(''); - try { - await changePassword(oldPw, newPw); - setMsg('Đã đổi mật khẩu'); - setOldPw(''); - setNewPw(''); - } catch (e: any) { - setErr(e?.message || 'Lỗi'); - } - }; - return (
    -

    Tài khoản & bảo mật

    -

    Quản lý đuôi email được phép và đổi mật khẩu.

    +

    Đuôi email

    +

    Cấu hình đuôi email được phép đăng ký và đăng nhập.

    -
    -

    Tài khoản hiện tại

    -

    {staff?.email}

    -
    -

    Đuôi email được phép

    @@ -145,6 +121,45 @@ export function AccountsTab() { ))}

    +
    + ); +} + +export function MyAccountTab() { + const { staff, changePassword } = useAuth(); + const [oldPw, setOldPw] = useState(''); + const [newPw, setNewPw] = useState(''); + const [msg, setMsg] = useState(''); + const [err, setErr] = useState(''); + + const submitPw = async (e: React.FormEvent) => { + e.preventDefault(); + setErr(''); + setMsg(''); + try { + await changePassword(oldPw, newPw); + setMsg('Đã đổi mật khẩu'); + setOldPw(''); + setNewPw(''); + } catch (e: any) { + setErr(e?.message || 'Lỗi'); + } + }; + + const displayName = staff?.fullName?.trim() || staff?.email?.split('@')[0] || 'Tài khoản'; + + return ( +
    +
    +

    Tài khoản của tôi

    +

    Thông tin đăng nhập và bảo mật cá nhân.

    +
    + +
    +

    Thông tin

    +

    {displayName}

    +

    {staff?.email}

    +

    Đổi mật khẩu

    diff --git a/management/src/index.css b/management/src/index.css index dc18732..8b91241 100644 --- a/management/src/index.css +++ b/management/src/index.css @@ -237,6 +237,51 @@ body { flex-shrink: 0; } +.sidebar-user-btn { + width: 100%; + text-align: left; + border: 1px solid transparent; + border-radius: var(--radius-sm); + background: none; + padding: 0.5rem 0.6rem; + cursor: pointer; + display: flex; + flex-direction: column; + gap: 0.15rem; + transition: background 0.15s, border-color 0.15s; +} + +.sidebar-user-btn:hover { + background: var(--bg-subtle); +} + +.sidebar-user-btn.active { + background: var(--accent-light); + border-color: var(--accent); +} + +.sidebar-user-btn.active .sidebar-user-name, +.sidebar-user-btn.active .sidebar-user-email { + color: var(--accent); +} + +.sidebar-user-name { + font-size: 0.82rem; + font-weight: 600; + color: var(--text-primary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.sidebar-user-email { + font-size: 0.72rem; + color: var(--text-muted); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + .token-badge { background-color: var(--bg-subtle); border: 1px solid var(--border-color); diff --git a/management/src/navigation.ts b/management/src/navigation.ts index 0915b00..eb0c2f6 100644 --- a/management/src/navigation.ts +++ b/management/src/navigation.ts @@ -1,4 +1,4 @@ -export type TabId = 'dashboard' | 'classes' | 'students' | 'learning' | 'network' | 'accounts'; +export type TabId = 'dashboard' | 'classes' | 'students' | 'learning' | 'network' | 'email-domains' | 'profile'; export interface NavEntry { id: string; @@ -15,20 +15,22 @@ export const TAB_LABELS: Record = { students: 'Sinh viên', learning: 'Giám sát & Lịch học', network: 'Quản lý mạng', - accounts: 'Tài khoản', + 'email-domains': 'Đuôi email', + profile: 'Tài khoản của tôi', }; const HISTORY_KEY = 'sc_nav_history'; const MAX_HISTORY = 10; function isTabId(value: string | null): value is TabId { - return value === 'dashboard' || value === 'classes' || value === 'students' || value === 'learning' || value === 'network' || value === 'accounts'; + return value === 'dashboard' || value === 'classes' || value === 'students' || value === 'learning' || value === 'network' || value === 'email-domains' || value === 'profile'; } export function parseRoute(): { tab: TabId; classId: number | null } { const params = new URLSearchParams(window.location.search); const tabParam = params.get('tab'); - const tab: TabId = isTabId(tabParam) ? tabParam : 'dashboard'; + let tab: TabId = isTabId(tabParam) ? tabParam : 'dashboard'; + if (tabParam === 'accounts') tab = 'email-domains'; const classIdRaw = params.get('classId'); const classId = classIdRaw ? Number(classIdRaw) : null; return {