feat: improve UI layout and responsiveness in module and operations pages. Refactor component structures for better alignment and spacing, ensuring a more consistent user experience across different screen sizes. Enhance button placements and text wrapping for improved readability.

This commit is contained in:
Denozordec
2026-04-06 18:48:20 +07:00
parent 4ba5b03f0b
commit 233f29b437
2 changed files with 42 additions and 37 deletions
+21 -21
View File
@@ -498,14 +498,14 @@
{:else if mod}
<div class="space-y-6">
<!-- Header -->
<div class="flex items-start justify-between">
<div class="flex items-start gap-3">
<div class="flex flex-col gap-3 xl:flex-row xl:items-start xl:justify-between">
<div class="flex min-w-0 items-start gap-3">
<Button variant="ghost" size="icon-sm" href={resolve('/modules')}>
<ArrowLeft class="size-4" />
</Button>
<div>
<div class="flex items-center gap-2">
<h1 class="text-2xl font-semibold tracking-tight">{mod.name}</h1>
<div class="min-w-0">
<div class="flex flex-wrap items-center gap-2">
<h1 class="min-w-0 break-words text-2xl font-semibold tracking-tight">{mod.name}</h1>
<Badge variant="outline">{mod.type}</Badge>
{#if mod.enabled}
<Badge variant="default" class="text-xs">вкл</Badge>
@@ -513,10 +513,10 @@
<Badge variant="secondary" class="text-xs">выкл</Badge>
{/if}
</div>
<p class="text-muted-foreground mt-0.5 text-sm font-mono text-xs">{mod.id}</p>
<p class="text-muted-foreground mt-0.5 break-all font-mono text-xs">{mod.id}</p>
</div>
</div>
<div class="flex gap-2">
<div class="flex flex-wrap gap-2">
<Button variant="outline" size="sm" onclick={refreshMod} disabled={refreshing}>
<RefreshCw class={refreshing ? 'animate-spin' : ''} />
Refresh
@@ -533,7 +533,7 @@
</div>
<!-- Module metadata -->
<div class="grid grid-cols-2 gap-3 sm:grid-cols-4">
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2 xl:grid-cols-4">
<Card class="p-3">
<p class="text-muted-foreground text-xs">Приоритет</p>
<p class="font-semibold">{mod.priority}</p>
@@ -550,21 +550,21 @@
</Card>
<Card class="p-3">
<p class="text-muted-foreground text-xs">Community по умолч.</p>
<p class="font-semibold text-sm">{communityLabel(mod.default_community_id)}</p>
<p class="font-semibold text-sm break-all">{communityLabel(mod.default_community_id)}</p>
</Card>
</div>
<!-- Entries by type -->
{#if mod.type === 'AS_PREFIXES'}
<Card>
<CardHeader class="flex flex-row items-center justify-between pb-2">
<div>
<CardHeader class="flex flex-col gap-3 pb-2 sm:flex-row sm:items-center sm:justify-between">
<div class="min-w-0 flex-1">
<CardTitle class="text-base">AS-записи</CardTitle>
<CardDescription>
Номер AS и community; имя, число префиксов и дата обновляются при успешном обновлении модуля (RIPEstat)
</CardDescription>
</div>
<Button size="sm" onclick={openAsCreate}><Plus />Добавить</Button>
<Button size="sm" class="shrink-0 self-start sm:self-auto" onclick={openAsCreate}><Plus />Добавить</Button>
</CardHeader>
<CardContent class="p-0">
<Table>
@@ -615,12 +615,12 @@
</Card>
{:else if mod.type === 'CDN_CIDRS'}
<Card>
<CardHeader class="flex flex-row items-center justify-between pb-2">
<div>
<CardHeader class="flex flex-col gap-3 pb-2 sm:flex-row sm:items-center sm:justify-between">
<div class="min-w-0 flex-1">
<CardTitle class="text-base">CDN-источники</CardTitle>
<CardDescription>URL источников для скачивания списков CIDR</CardDescription>
</div>
<Button size="sm" onclick={openCdnCreate}><Plus />Добавить</Button>
<Button size="sm" class="shrink-0 self-start sm:self-auto" onclick={openCdnCreate}><Plus />Добавить</Button>
</CardHeader>
<CardContent class="p-0">
<Table>
@@ -667,12 +667,12 @@
</Card>
{:else if mod.type === 'DOMAINS'}
<Card>
<CardHeader class="flex flex-row items-center justify-between pb-2">
<div>
<CardHeader class="flex flex-col gap-3 pb-2 sm:flex-row sm:items-center sm:justify-between">
<div class="min-w-0 flex-1">
<CardTitle class="text-base">Домены</CardTitle>
<CardDescription>FQDN для резолвинга через DoH</CardDescription>
</div>
<Button size="sm" onclick={openDomainCreate}><Plus />Добавить</Button>
<Button size="sm" class="shrink-0 self-start sm:self-auto" onclick={openDomainCreate}><Plus />Добавить</Button>
</CardHeader>
<CardContent class="p-0">
<Table>
@@ -706,12 +706,12 @@
</Card>
{:else if mod.type === 'IP_RANGES'}
<Card>
<CardHeader class="flex flex-row items-center justify-between pb-2">
<div>
<CardHeader class="flex flex-col gap-3 pb-2 sm:flex-row sm:items-center sm:justify-between">
<div class="min-w-0 flex-1">
<CardTitle class="text-base">IP-диапазоны</CardTitle>
<CardDescription>Статические CIDR для анонса</CardDescription>
</div>
<Button size="sm" onclick={openIpCreate}><Plus />Добавить</Button>
<Button size="sm" class="shrink-0 self-start sm:self-auto" onclick={openIpCreate}><Plus />Добавить</Button>
</CardHeader>
<CardContent class="p-0">
<Table>
+21 -16
View File
@@ -446,24 +446,29 @@
<!-- Quick actions + локальный статус BIRD (хост API, если задан EVOBGP_BIRDC_SOCKET) -->
<div class="grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
<Card class="p-4">
<div class="flex items-center justify-between">
<div>
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
<div class="min-w-0 flex-1">
<p class="font-semibold">Apply all speakers</p>
<p class="text-muted-foreground text-sm">Применить текущую конфигурацию на всех спикерах</p>
</div>
<Button onclick={() => (applyConfirm = true)} disabled={applying}>
<Button class="shrink-0 self-start sm:self-auto" onclick={() => (applyConfirm = true)} disabled={applying}>
<Play />
Apply
</Button>
</div>
</Card>
<Card class="p-4">
<div class="flex items-center justify-between">
<div>
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
<div class="min-w-0 flex-1">
<p class="font-semibold">BIRD Reload</p>
<p class="text-muted-foreground text-sm">Перезагрузить конфигурацию BIRD на всех спикерах</p>
</div>
<Button variant="outline" onclick={() => (reloadConfirm = true)} disabled={reloading}>
<Button
variant="outline"
class="shrink-0 self-start sm:self-auto"
onclick={() => (reloadConfirm = true)}
disabled={reloading}
>
<RotateCcw />
Reload
</Button>
@@ -526,12 +531,12 @@
<!-- Revisions -->
<TabsContent value="revisions" class="mt-4">
<Card>
<CardHeader class="flex flex-row items-center justify-between pb-2">
<div>
<CardHeader class="flex flex-col gap-3 pb-2 sm:flex-row sm:items-center sm:justify-between">
<div class="min-w-0 flex-1">
<CardTitle class="text-base">История ревизий</CardTitle>
<CardDescription>Создаются задачей module_refresh (CDN / IP / AS и т.д.); в превью есть полный текст BIRD с инклюдами</CardDescription>
</div>
<Button variant="outline" size="sm" onclick={loadRevisions} disabled={revLoading}>
<Button variant="outline" size="sm" class="shrink-0 self-start sm:self-auto" onclick={loadRevisions} disabled={revLoading}>
<RefreshCw class={revLoading ? 'animate-spin' : ''} />
</Button>
</CardHeader>
@@ -585,20 +590,20 @@
<CardDescription>Выберите ID двух ревизий для сравнения</CardDescription>
</CardHeader>
<CardContent>
<div class="flex gap-2">
<select bind:value={diffRevA} class="border-border bg-background h-8 flex-1 rounded-md border px-2 text-sm">
<div class="flex flex-col gap-2 sm:flex-row">
<select bind:value={diffRevA} class="border-border bg-background h-8 min-w-0 flex-1 rounded-md border px-2 text-sm">
<option value="">Ревизия A</option>
{#each revisions as rev}
<option value={rev.id}>{rev.id.slice(0, 8)} ({formatDate(rev.created_at)})</option>
{/each}
</select>
<select bind:value={diffRevB} class="border-border bg-background h-8 flex-1 rounded-md border px-2 text-sm">
<select bind:value={diffRevB} class="border-border bg-background h-8 min-w-0 flex-1 rounded-md border px-2 text-sm">
<option value="">Ревизия B</option>
{#each revisions as rev}
<option value={rev.id}>{rev.id.slice(0, 8)} ({formatDate(rev.created_at)})</option>
{/each}
</select>
<Button size="sm" onclick={loadDiff} disabled={diffLoading}>
<Button size="sm" class="shrink-0 self-start sm:self-auto" onclick={loadDiff} disabled={diffLoading}>
{diffLoading ? 'Загрузка…' : 'Сравнить'}
</Button>
</div>
@@ -633,12 +638,12 @@
<!-- Jobs -->
<TabsContent value="jobs" class="mt-4">
<Card>
<CardHeader class="flex flex-row items-center justify-between pb-2">
<div>
<CardHeader class="flex flex-col gap-3 pb-2 sm:flex-row sm:items-center sm:justify-between">
<div class="min-w-0 flex-1">
<CardTitle class="text-base">Задачи</CardTitle>
<CardDescription>Фоновые задачи (ingest, apply, refresh)</CardDescription>
</div>
<Button variant="outline" size="sm" onclick={loadJobs} disabled={jobsLoading}>
<Button variant="outline" size="sm" class="shrink-0 self-start sm:self-auto" onclick={loadJobs} disabled={jobsLoading}>
<RefreshCw class={jobsLoading ? 'animate-spin' : ''} />
</Button>
</CardHeader>