feat(ui): общий футер на всех страницах, кроме комнаты
`AppFooter` — версия инстанса из публичного `/api/health` (не требует авторизации, поэтому работает и на экранах входа; поля `version` может не быть — тогда версия не показывается) плюс копирайт с ТЕКУЩИМ годом и ссылкой на профиль автора. Подключён во все раскладки: `page-shell` (календарь, админка, профиль, мои конференции), `AuthLayout` (вход, регистрация, подтверждение email), `join-shell` и `lobby-shell`. В комнате конференции футера нет — там полноэкранное видео. Прежний футер админки (`.admin-footer`) удалён вместе со стилем — дубля не осталось. В брендовой панели экранов auth строка «© 2026 VidConf · self-hosted» ужата до «self-hosted»: год там был захардкожен, а два копирайта на одном экране — дубль.
This commit is contained in:
@@ -1,11 +1,10 @@
|
||||
import { useState } from 'react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { ShellTopbar } from '@/components/layout/ShellTopbar'
|
||||
import { AppFooter } from '@/components/layout/AppFooter'
|
||||
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'
|
||||
@@ -30,10 +29,6 @@ const TABS: { value: AdminTab; label: string }[] = [
|
||||
*/
|
||||
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">
|
||||
@@ -63,11 +58,8 @@ export function AdminPage() {
|
||||
{tab === 'users' && <AdminUsersTab />}
|
||||
{tab === 'teams' && <AdminTeamsTab />}
|
||||
{tab === 'settings' && <AdminSettingsTab />}
|
||||
|
||||
<footer className="admin-footer">
|
||||
VidConf{health?.version ? ` · v${health.version}` : ''}
|
||||
</footer>
|
||||
</main>
|
||||
<AppFooter />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useCallback, useMemo, useState } from 'react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { Plus } from 'lucide-react'
|
||||
import { ShellTopbar } from '@/components/layout/ShellTopbar'
|
||||
import { AppFooter } from '@/components/layout/AppFooter'
|
||||
import { ConferenceCalendar, type CalendarViewMode } from '@/components/calendar/ConferenceCalendar'
|
||||
import { ConferenceFormCard } from '@/components/calendar/ConferenceFormCard'
|
||||
import { ConferenceOccurrenceDialog } from '@/components/calendar/ConferenceOccurrenceDialog'
|
||||
@@ -102,6 +103,7 @@ export function CalendarPage() {
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
<AppFooter />
|
||||
|
||||
{isFormOpen && (
|
||||
<ConferenceFormModal
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from '@/api/conferences'
|
||||
import { ApiError, errorDetail } from '@/api/client'
|
||||
import { useAuth } from '@/auth/useAuth'
|
||||
import { AppFooter } from '@/components/layout/AppFooter'
|
||||
import { LogoMark } from '@/components/ui/LogoMark'
|
||||
import { ThemeToggle } from '@/components/ui/ThemeToggle'
|
||||
import { parseJoinQuery } from '@/lib/parseJoinQuery'
|
||||
@@ -409,6 +410,7 @@ export function JoinPage() {
|
||||
</section>
|
||||
)}
|
||||
</main>
|
||||
<AppFooter />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { Calendar, Link2, User, Video } from 'lucide-react'
|
||||
import { ShellTopbar } from '@/components/layout/ShellTopbar'
|
||||
import { AppFooter } from '@/components/layout/AppFooter'
|
||||
import { LogoMedallion } from '@/components/ui/LogoMark'
|
||||
import { createConference } from '@/api/conferences'
|
||||
import { useToast } from '@/components/ui/ToastProvider'
|
||||
@@ -113,6 +114,7 @@ export function LobbyPage() {
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<AppFooter />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { AlertTriangle, CheckCircle2, Clock, Lock, Pencil, Play, Repeat } from 'lucide-react'
|
||||
import { ShellTopbar } from '@/components/layout/ShellTopbar'
|
||||
import { AppFooter } from '@/components/layout/AppFooter'
|
||||
import { ConferenceFormCard } from '@/components/calendar/ConferenceFormCard'
|
||||
import { ConferenceHoverCard } from '@/components/ui/ConferenceHoverCard'
|
||||
import { CopyPill } from '@/components/ui/CopyPill'
|
||||
@@ -212,6 +213,7 @@ export function MyConferencesPage() {
|
||||
</section>
|
||||
)}
|
||||
</main>
|
||||
<AppFooter />
|
||||
|
||||
{editingConference && (
|
||||
<div className="modal-overlay" role="dialog" aria-modal="true" onClick={() => setEditingConference(null)}>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useRef, useState, type FormEvent } from 'react'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { Trash2, Upload } from 'lucide-react'
|
||||
import { ShellTopbar } from '@/components/layout/ShellTopbar'
|
||||
import { AppFooter } from '@/components/layout/AppFooter'
|
||||
import { Avatar } from '@/components/ui/Avatar'
|
||||
import { Select } from '@/components/ui/Select'
|
||||
import { useToast } from '@/components/ui/ToastProvider'
|
||||
@@ -271,6 +272,7 @@ export function ProfilePage() {
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
<AppFooter />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user