Первоначальная версия VidConf

This commit is contained in:
2026-07-23 01:04:01 +03:00
commit 896455381a
335 changed files with 61527 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
import { useState } from 'react'
import { useQuery } from '@tanstack/react-query'
import { ShellTopbar } from '@/components/layout/ShellTopbar'
import { AdminConferencesTab } from '@/components/admin/AdminConferencesTab'
import { AdminUsersTab } from '@/components/admin/AdminUsersTab'
import { AdminTeamsTab } from '@/components/admin/AdminTeamsTab'
import { AdminSettingsTab } from '@/components/admin/AdminSettingsTab'
import { getHealth } from '@/api/health'
import '@/styles/lobby.css'
import '@/styles/calendar.css'
import '@/styles/admin.css'
type AdminTab = 'conferences' | 'users' | 'teams' | 'settings'
const TABS: { value: AdminTab; label: string }[] = [
{ value: 'conferences', label: 'Конференции' },
{ value: 'users', label: 'Пользователи' },
{ value: 'teams', label: 'Команды' },
{ value: 'settings', label: 'Настройки' },
]
/**
* Администрирование VidConf: управление конференциями, доступом
* пользователей и настройками инстанса. Доступ ограничен ролью `admin`
* (route-guard — см. `RequireAdmin` в App.tsx).
*
* Вкладки «Команды» и «Настройки» отсутствуют в макете
* `design/mockups/admin.html` — реализованы по общему паттерну
* `section-tab`/`table-card`/`toolbar-row` того же макета и DESIGN_SYSTEM.md.
*/
export function AdminPage() {
const [tab, setTab] = useState<AdminTab>('conferences')
// Версия инстанса для футера — поле `version` в `/api/health`;
// если его нет в ответе, футер версию просто не показывает
// (см. `HealthOut.version`).
const { data: health } = useQuery({ queryKey: ['health'], queryFn: getHealth, retry: false })
return (
<div className="admin-page page-shell">
<ShellTopbar />
<main className="page-main">
<div className="page-head">
<div>
<h1>Администрирование</h1>
<p>Управление конференциями, доступом и пользователями VidConf</p>
</div>
</div>
<div className="section-tabs">
{TABS.map((t) => (
<button
key={t.value}
type="button"
className={`section-tab${tab === t.value ? ' is-active' : ''}`}
onClick={() => setTab(t.value)}
>
{t.label}
</button>
))}
</div>
{tab === 'conferences' && <AdminConferencesTab />}
{tab === 'users' && <AdminUsersTab />}
{tab === 'teams' && <AdminTeamsTab />}
{tab === 'settings' && <AdminSettingsTab />}
<footer className="admin-footer">
VidConf{health?.version ? ` · v${health.version}` : ''}
</footer>
</main>
</div>
)
}