CI / changes (push) Successful in 5s
CI / openapi (push) Successful in 39s
CI / web (push) Successful in 55s
CI / go (push) Successful in 2m40s
CI / bird2 (push) Successful in 16s
CI / commitlint (push) Skipped
CI / release (push) Successful in 18s
Выровнять KPI на IconTile, вынести settings из settings-7, Sheet/AlertDialog и skip-link в общем chrome. Co-authored-by: Cursor <[email protected]>
115 lines
4.3 KiB
TypeScript
115 lines
4.3 KiB
TypeScript
import { useQuery } from '@tanstack/react-query'
|
||
import { MonitorIcon } from 'lucide-react'
|
||
|
||
import { SettingRow } from '@/components/settings/setting-row'
|
||
import { SettingsCard } from '@/components/settings/settings-card'
|
||
import { SettingsFieldGroup } from '@/components/settings/settings-field-group'
|
||
import { Badge } from '@/components/reui/badge'
|
||
import { QueryState } from '@/components/query-state'
|
||
import { TOKEN_STORAGE_KEY } from '@/lib/api-client'
|
||
import { authSessionQueryOptions } from '@/queries/auth'
|
||
import {
|
||
Item,
|
||
ItemContent,
|
||
ItemDescription,
|
||
ItemGroup,
|
||
ItemMedia,
|
||
ItemTitle,
|
||
} from '@evobgp/ui/components/item'
|
||
|
||
const ROLE_LABELS: Record<string, string> = {
|
||
viewer: 'Просмотр',
|
||
editor: 'Редактор',
|
||
operator: 'Оператор',
|
||
node: 'Нода',
|
||
}
|
||
|
||
export function SessionSettingsTab() {
|
||
const hasStoredToken = Boolean(
|
||
typeof window !== 'undefined' && window.localStorage.getItem(TOKEN_STORAGE_KEY)?.trim(),
|
||
)
|
||
|
||
const sessionQ = useQuery({
|
||
...authSessionQueryOptions(),
|
||
enabled: hasStoredToken,
|
||
})
|
||
|
||
return (
|
||
<div className="space-y-6">
|
||
<SettingsCard title="Текущая сессия" description="Проверка токена через GET /v1/auth/session">
|
||
<QueryState
|
||
data={sessionQ.data}
|
||
isLoading={sessionQ.isLoading && hasStoredToken}
|
||
isError={sessionQ.isError}
|
||
error={sessionQ.error}
|
||
empty={!hasStoredToken}
|
||
emptyTitle="Токен не задан"
|
||
emptyDescription="Сохраните API-токен во вкладке «Подключение»."
|
||
skeleton={<div className="h-24 px-5 py-4" />}
|
||
onRetry={() => sessionQ.refetch()}
|
||
>
|
||
{(session) => (
|
||
<ItemGroup className="gap-0">
|
||
<Item className="min-h-0 items-center gap-4 px-5 py-3.5">
|
||
<ItemMedia className="self-center!">
|
||
<Item className="bg-muted/60 border-background flex size-8 shrink-0 items-center justify-center border-2 p-0 shadow-[0_1px_3px_0_rgba(0,0,0,0.14)] [&_svg]:size-4 [&_svg]:opacity-60">
|
||
<MonitorIcon aria-hidden="true" />
|
||
</Item>
|
||
</ItemMedia>
|
||
|
||
<ItemContent className="min-w-0 justify-center gap-0.5 self-center">
|
||
<ItemTitle className="gap-2 leading-5">
|
||
Этот браузер
|
||
<Badge variant="success-light" size="xs">
|
||
Активна
|
||
</Badge>
|
||
</ItemTitle>
|
||
<ItemDescription className="leading-5">
|
||
Tenant:{' '}
|
||
<code className="text-foreground font-mono text-xs">{session.tenant_id}</code>
|
||
</ItemDescription>
|
||
</ItemContent>
|
||
</Item>
|
||
</ItemGroup>
|
||
)}
|
||
</QueryState>
|
||
</SettingsCard>
|
||
|
||
<SettingsCard title="Роль и доступ" description="Права текущего API-ключа">
|
||
<SettingsFieldGroup
|
||
legend="Роль"
|
||
description="Уровень доступа, определённый API-ключом."
|
||
>
|
||
<SettingRow
|
||
title="Роль"
|
||
description="Определяет доступ к операциям control plane и CRUD."
|
||
titleAddon={
|
||
sessionQ.data ? (
|
||
<Badge variant="info-light" size="sm">
|
||
{ROLE_LABELS[sessionQ.data.role] ?? sessionQ.data.role}
|
||
</Badge>
|
||
) : (
|
||
<Badge variant="outline" size="sm">
|
||
неизвестно
|
||
</Badge>
|
||
)
|
||
}
|
||
last
|
||
>
|
||
{sessionQ.data ? (
|
||
<p className="text-muted-foreground text-sm">
|
||
Ключ с ролью <strong>{sessionQ.data.role}</strong> в tenant{' '}
|
||
<code className="font-mono text-xs">{sessionQ.data.tenant_id}</code>.
|
||
</p>
|
||
) : (
|
||
<p className="text-muted-foreground text-sm">
|
||
Сохраните токен и дождитесь проверки сессии.
|
||
</p>
|
||
)}
|
||
</SettingRow>
|
||
</SettingsFieldGroup>
|
||
</SettingsCard>
|
||
</div>
|
||
)
|
||
}
|