feat: enhance BadgeTabs and Auth components with layout improvements and API integration
CI / changes (push) Successful in 13s
CI / commitlint (push) Has been skipped
CI / openapi (push) Has been skipped
CI / web (push) Successful in 1m7s
CI / go (push) Successful in 1m14s
CI / bird2 (push) Successful in 20s
CI / release (push) Successful in 4m52s

Added layout metrics logging to the BadgeTabs component for better debugging and analysis. Updated the Auth route to include a reason for redirection when a token is required, improving user feedback. Enhanced the Settings component to display an alert when API token access is needed, ensuring clarity for users. Additionally, refined the Tabs component to improve responsiveness based on orientation, contributing to a more cohesive user experience.
This commit is contained in:
Denozordec
2026-07-09 20:54:35 +07:00
parent 538daea0f1
commit 1152bb40f7
5 changed files with 89 additions and 32 deletions
+60 -26
View File
@@ -1,4 +1,5 @@
import type { ComponentProps, ReactNode } from 'react'
import { useEffect, useRef } from 'react'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@evobgp/ui/components/tabs'
import { cn } from '@evobgp/ui/lib/utils'
@@ -35,34 +36,67 @@ export function BadgeTabs({
listClassName,
contentClassName,
}: BadgeTabsProps) {
const tabsRef = useRef<HTMLDivElement>(null)
useEffect(() => {
const el = tabsRef.current?.querySelector('[data-slot=tabs]')
if (!el || !(el instanceof HTMLElement)) return
const flexDirection = getComputedStyle(el).flexDirection
const listRect = el.querySelector('[data-slot=tabs-list]')?.getBoundingClientRect()
const panelRect = el.querySelector('[data-slot=tabs-content]')?.getBoundingClientRect()
// #region agent log
fetch('http://127.0.0.1:7311/ingest/6b35c3ae-1bcd-4c9c-81eb-f157c9347393', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-Debug-Session-Id': 'c10bcb' },
body: JSON.stringify({
sessionId: 'c10bcb',
runId: 'post-fix',
hypothesisId: 'H1',
location: 'badge-tabs.tsx:useEffect',
message: 'BadgeTabs layout metrics',
data: {
flexDirection,
orientation: el.getAttribute('data-orientation'),
listLeft: listRect?.left,
panelLeft: panelRect?.left,
stacked: flexDirection === 'column',
},
timestamp: Date.now(),
}),
}).catch(() => {})
// #endregion
}, [value, defaultValue])
return (
<Tabs
value={value}
defaultValue={defaultValue}
onValueChange={onValueChange}
className={cn('w-full', className)}
>
<TabsList
variant="line"
className={cn(
'mb-4 w-full justify-start gap-6',
listClassName,
)}
<div ref={tabsRef} className={cn('w-full', className)}>
<Tabs
value={value}
defaultValue={defaultValue}
onValueChange={onValueChange}
className="w-full"
>
{items.map((item) => (
<TabsTrigger key={item.value} value={item.value} className="gap-2">
{item.icon}
{item.label}
{item.count !== undefined ? (
<Badge variant={item.badgeVariant ?? 'primary-light'} size="sm">
{item.count}
</Badge>
) : null}
</TabsTrigger>
))}
</TabsList>
<div className={cn('w-full min-w-0', contentClassName)}>{children}</div>
</Tabs>
<TabsList
variant="line"
className={cn(
'mb-4 w-full justify-start gap-6',
listClassName,
)}
>
{items.map((item) => (
<TabsTrigger key={item.value} value={item.value} className="gap-2">
{item.icon}
{item.label}
{item.count !== undefined ? (
<Badge variant={item.badgeVariant ?? 'primary-light'} size="sm">
{item.count}
</Badge>
) : null}
</TabsTrigger>
))}
</TabsList>
<div className={cn('w-full min-w-0', contentClassName)}>{children}</div>
</Tabs>
</div>
)
}
+1 -1
View File
@@ -9,7 +9,7 @@ export const Route = createFileRoute('/_auth')({
const raw =
typeof window !== 'undefined' ? window.localStorage.getItem(TOKEN_STORAGE_KEY) : null
if (!raw || !normalizeApiToken(raw)) {
throw redirect({ to: '/settings' })
throw redirect({ to: '/settings', search: { reason: 'token-required' } })
}
},
component: AuthLayout,
+13 -1
View File
@@ -1,7 +1,8 @@
import { createFileRoute, useNavigate } from '@tanstack/react-router'
import { createFileRoute, useNavigate, useRouterState } from '@tanstack/react-router'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { Button } from '@evobgp/ui/components/button'
import { Alert, AlertDescription } from '@evobgp/ui/components/alert'
import { PanelCard } from '@/components/panel-card'
import { Input } from '@evobgp/ui/components/input'
import { Label } from '@evobgp/ui/components/label'
@@ -28,6 +29,9 @@ const THEME_SELECT_ITEMS = [
function SettingsComponent() {
const navigate = useNavigate()
const tokenRequired = useRouterState({
select: (s) => new URLSearchParams(s.location.search).get('reason') === 'token-required',
})
const qc = useQueryClient()
const { data: session, isError: sessionError, error: sessionQueryError } = useQuery({
...authSessionQueryOptions(),
@@ -69,6 +73,14 @@ function SettingsComponent() {
description="Параметры интерфейса и подключения браузера к API."
/>
{tokenRequired ? (
<Alert>
<AlertDescription>
Для доступа к разделу нужен API-токен. Сохраните токен ниже или нажмите «Использовать dev».
</AlertDescription>
</Alert>
) : null}
<PanelCard
title="Подключение к API"
description="Токен хранится только в этом браузере (localStorage). Управление ключами tenant — в разделе «Права доступа»."