Compare commits

...
2 Commits
Author SHA1 Message Date
Denozordec 6a7284245f feat: enhance UI components with improved styling and layout adjustments. Update button sizes, text classes, and overflow handling in various components for better readability and user experience across the operations page.
CI / changes (push) Successful in 5s
CI / openapi (push) Has been skipped
CI / go (push) Has been skipped
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, , evobgp-web) (push) Successful in 59s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Successful in 1m1s
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Has been skipped
CI / docker-go-prime (push) Has been skipped
CI / docker-go (deploy/docker/evobgp-agent/Dockerfile, , evobgp-agent) (push) Has been skipped
CI / docker-go (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, , evobgp-all) (push) Has been skipped
CI / docker-go (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, , evobgp-api) (push) Has been skipped
CI / docker-go (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, , evobgp-deploy) (push) Has been skipped
CI / docker-go (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, , evobgp-ingest) (push) Has been skipped
CI / docker-go (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, , evobgp-node) (push) Has been skipped
CI / docker-go (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, , evobgp-render) (push) Has been skipped
CI / docker-go (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, , evobgp-scheduler) (push) Has been skipped
2026-04-06 22:52:37 +07:00
Denozordec 67ce135b46 feat: enhance job detail UI with improved overflow handling and layout adjustments. Update card components to manage overflow and ensure better readability of job reports and details, enhancing the overall user experience. 2026-04-06 22:36:23 +07:00
5 changed files with 23 additions and 21 deletions

No files matched your search

@@ -64,7 +64,7 @@
<CardContent class="space-y-3 p-3 sm:p-4"> <CardContent class="space-y-3 p-3 sm:p-4">
{#each jobs as job (job.job_id)} {#each jobs as job (job.job_id)}
<div class="bg-card min-w-0 rounded-lg border"> <div class="bg-card min-w-0 overflow-hidden rounded-lg border">
<div class="flex min-w-0 flex-col gap-3 p-3 sm:p-4"> <div class="flex min-w-0 flex-col gap-3 p-3 sm:p-4">
<div class="flex min-w-0 flex-wrap items-start justify-between gap-2"> <div class="flex min-w-0 flex-wrap items-start justify-between gap-2">
<div class="min-w-0 flex items-center gap-2"> <div class="min-w-0 flex items-center gap-2">
@@ -76,11 +76,11 @@
aria-expanded={isJobExpanded(job.job_id)} aria-expanded={isJobExpanded(job.job_id)}
onclick={() => void onToggleJobExpanded(job)} onclick={() => void onToggleJobExpanded(job)}
> >
<ChevronDown class={cn('size-3.5 transition-transform', isJobExpanded(job.job_id) && 'rotate-180')} /> <ChevronDown class={cn('size-4 transition-transform', isJobExpanded(job.job_id) && 'rotate-180')} />
</Button> </Button>
<div class="min-w-0"> <div class="min-w-0">
<p class="font-medium break-words">{job.kind}</p> <p class="font-medium break-words">{job.kind}</p>
<p class="text-muted-foreground font-mono text-[11px] break-all">{job.job_id}</p> <p class="text-muted-foreground font-mono text-xs break-all">{job.job_id}</p>
</div> </div>
</div> </div>
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
@@ -128,8 +128,8 @@
{@const jobReport = jobReportsById.get(job.job_id)} {@const jobReport = jobReportsById.get(job.job_id)}
{@const logEntries = getJobLogEntries(detailedJob)} {@const logEntries = getJobLogEntries(detailedJob)}
{@const logTotal = getJobLogTotal(detailedJob, logEntries)} {@const logTotal = getJobLogTotal(detailedJob, logEntries)}
<div class="border-t bg-muted/10 p-3 sm:p-4"> <div class="border-t bg-muted/10 p-3 sm:p-4 max-h-[70vh] overflow-y-auto overscroll-contain">
<div class="space-y-3"> <div class="space-y-3 pr-1">
{#if isDetailsLoading} {#if isDetailsLoading}
<p class="text-muted-foreground text-xs">Догружаем свежие детали задачи…</p> <p class="text-muted-foreground text-xs">Догружаем свежие детали задачи…</p>
{/if} {/if}
@@ -209,20 +209,20 @@
{/if} {/if}
</div> </div>
</div> </div>
<div class="grid gap-2 md:grid-cols-2 xl:grid-cols-4"> <div class="grid min-w-0 gap-2 md:grid-cols-2 xl:grid-cols-4">
<div class="rounded-md border bg-muted/40 p-2.5"> <div class="min-w-0 overflow-hidden rounded-md border bg-muted/40 p-2.5">
<p class="text-muted-foreground text-[11px] uppercase">Агрегация</p> <p class="text-muted-foreground text-[11px] uppercase">Агрегация</p>
<p class="text-sm font-semibold">{jobReport.aggregationTotal} префиксов</p> <p class="text-sm font-semibold">{jobReport.aggregationTotal} префиксов</p>
</div> </div>
<div class="rounded-md border bg-muted/40 p-2.5"> <div class="min-w-0 overflow-hidden rounded-md border bg-muted/40 p-2.5">
<p class="text-muted-foreground text-[11px] uppercase">Домены</p> <p class="text-muted-foreground text-[11px] uppercase">Домены</p>
<p class="text-sm font-semibold">{jobReport.domains.length}</p> <p class="text-sm font-semibold">{jobReport.domains.length}</p>
</div> </div>
<div class="rounded-md border bg-muted/40 p-2.5"> <div class="min-w-0 overflow-hidden rounded-md border bg-muted/40 p-2.5">
<p class="text-muted-foreground text-[11px] uppercase">ASN</p> <p class="text-muted-foreground text-[11px] uppercase">ASN</p>
<p class="text-sm font-semibold">{jobReport.asn.length}</p> <p class="text-sm font-semibold">{jobReport.asn.length}</p>
</div> </div>
<div class="rounded-md border bg-muted/40 p-2.5"> <div class="min-w-0 overflow-hidden rounded-md border bg-muted/40 p-2.5">
<p class="text-muted-foreground text-[11px] uppercase">CDN/IP Range</p> <p class="text-muted-foreground text-[11px] uppercase">CDN/IP Range</p>
<p class="text-sm font-semibold">{jobReport.cdn.length}/{jobReport.ipRanges.length}</p> <p class="text-sm font-semibold">{jobReport.cdn.length}/{jobReport.ipRanges.length}</p>
</div> </div>
@@ -238,8 +238,8 @@
</div> </div>
{/if} {/if}
<div class="grid gap-3 xl:grid-cols-2"> <div class="grid min-w-0 gap-3 xl:grid-cols-2">
<div class="space-y-1.5 rounded-lg border p-3"> <div class="min-w-0 overflow-hidden space-y-1.5 rounded-lg border p-3">
<p class="text-sm font-medium">Домены: какой домен какие IP/префиксы вернул</p> <p class="text-sm font-medium">Домены: какой домен какие IP/префиксы вернул</p>
<ScrollArea class="max-h-44 rounded border bg-muted/25 p-2"> <ScrollArea class="max-h-44 rounded border bg-muted/25 p-2">
{#each jobReport.domains as row (`${job.job_id}-domain-${row.source}`)} {#each jobReport.domains as row (`${job.job_id}-domain-${row.source}`)}
@@ -253,7 +253,7 @@
{/each} {/each}
</ScrollArea> </ScrollArea>
</div> </div>
<div class="space-y-1.5 rounded-lg border p-3"> <div class="min-w-0 overflow-hidden space-y-1.5 rounded-lg border p-3">
<p class="text-sm font-medium">ASN: сколько префиксов получено по AS</p> <p class="text-sm font-medium">ASN: сколько префиксов получено по AS</p>
<ScrollArea class="max-h-44 rounded border bg-muted/25 p-2"> <ScrollArea class="max-h-44 rounded border bg-muted/25 p-2">
{#each jobReport.asn as row (`${job.job_id}-asn-${row.asn}`)} {#each jobReport.asn as row (`${job.job_id}-asn-${row.asn}`)}
@@ -266,7 +266,7 @@
{/each} {/each}
</ScrollArea> </ScrollArea>
</div> </div>
<div class="space-y-1.5 rounded-lg border p-3"> <div class="min-w-0 overflow-hidden space-y-1.5 rounded-lg border p-3">
<p class="text-sm font-medium">CDN: из каждой ссылки полученные IP/префиксы</p> <p class="text-sm font-medium">CDN: из каждой ссылки полученные IP/префиксы</p>
<ScrollArea class="max-h-44 rounded border bg-muted/25 p-2"> <ScrollArea class="max-h-44 rounded border bg-muted/25 p-2">
{#each jobReport.cdn as row (`${job.job_id}-cdn-${row.source}`)} {#each jobReport.cdn as row (`${job.job_id}-cdn-${row.source}`)}
@@ -280,7 +280,7 @@
{/each} {/each}
</ScrollArea> </ScrollArea>
</div> </div>
<div class="space-y-1.5 rounded-lg border p-3"> <div class="min-w-0 overflow-hidden space-y-1.5 rounded-lg border p-3">
<p class="text-sm font-medium">IP range: итог по статическим диапазонам</p> <p class="text-sm font-medium">IP range: итог по статическим диапазонам</p>
<ScrollArea class="max-h-44 rounded border bg-muted/25 p-2"> <ScrollArea class="max-h-44 rounded border bg-muted/25 p-2">
{#each jobReport.ipRanges as row (`${job.job_id}-ipr-${row.source}`)} {#each jobReport.ipRanges as row (`${job.job_id}-ipr-${row.source}`)}
@@ -77,14 +77,14 @@
<Badge variant={birdHealthyBadgeVariant(birdStatus.healthy)}>{birdHealthyShortLabel(birdStatus.healthy)}</Badge> <Badge variant={birdHealthyBadgeVariant(birdStatus.healthy)}>{birdHealthyShortLabel(birdStatus.healthy)}</Badge>
{/if} {/if}
</div> </div>
<p class="text-muted-foreground max-w-[56ch] text-sm"> <p class="text-foreground/80 max-w-[56ch] text-sm">
Локально на хосте API: <code class="bg-muted rounded px-1 text-xs">birdc show protocols</code>. Не заменяет мониторинг спикеров. Локально на хосте API: <code class="bg-muted rounded px-1 text-xs">birdc show protocols</code>. Не заменяет мониторинг спикеров.
</p> </p>
{#if birdLoading} {#if birdLoading}
<p class="text-muted-foreground text-sm">Загрузка…</p> <p class="text-foreground/80 text-sm">Загрузка…</p>
{:else if birdStatus} {:else if birdStatus}
{#if !birdStatus.birdc_configured} {#if !birdStatus.birdc_configured}
<p class="text-muted-foreground text-sm">{birdStatus.message ?? 'birdc не настроен на API.'}</p> <p class="text-foreground/80 text-sm">{birdStatus.message ?? 'birdc не настроен на API.'}</p>
{:else if birdStatus.error} {:else if birdStatus.error}
<p class="text-destructive text-sm">{birdStatus.error}</p> <p class="text-destructive text-sm">{birdStatus.error}</p>
{:else} {:else}
@@ -97,7 +97,7 @@
</p> </p>
{/if} {/if}
{:else} {:else}
<p class="text-muted-foreground text-sm">Статус не загружен</p> <p class="text-foreground/80 text-sm">Статус не загружен</p>
{/if} {/if}
</div> </div>
@@ -39,6 +39,7 @@
</Button> </Button>
</CardHeader> </CardHeader>
<CardContent class="p-0"> <CardContent class="p-0">
<div class="overflow-x-auto [scrollbar-gutter:stable]">
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow> <TableRow>
@@ -76,5 +77,6 @@
{/each} {/each}
</TableBody> </TableBody>
</Table> </Table>
</div>
</CardContent> </CardContent>
</Card> </Card>
@@ -10,6 +10,6 @@
}: WithElementRef<HTMLAttributes<HTMLTableRowElement>> = $props(); }: WithElementRef<HTMLAttributes<HTMLTableRowElement>> = $props();
</script> </script>
<tr bind:this={ref} data-slot="table-row" class={cn("hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors", className)} {...restProps}> <tr bind:this={ref} data-slot="table-row" class={cn("hover:bg-muted/70 data-[state=selected]:bg-muted border-b transition-colors", className)} {...restProps}>
{@render children?.()} {@render children?.()}
</tr> </tr>
+1 -1
View File
@@ -791,7 +791,7 @@
<DialogTitle>Задача: {jobDetail?.kind}</DialogTitle> <DialogTitle>Задача: {jobDetail?.kind}</DialogTitle>
</DialogHeader> </DialogHeader>
{#if jobDetail} {#if jobDetail}
<div class={dialogBodyPanel}> <div class={cn(dialogBodyPanel, 'max-h-[80vh] overflow-y-auto')}>
<div class="grid grid-cols-2 gap-x-4 gap-y-1 text-sm"> <div class="grid grid-cols-2 gap-x-4 gap-y-1 text-sm">
<span class="text-muted-foreground">ID</span><span class="font-mono break-all">{jobDetail.job_id}</span> <span class="text-muted-foreground">ID</span><span class="font-mono break-all">{jobDetail.job_id}</span>
<span class="text-muted-foreground">Статус</span><span><Badge variant={jobStatusVariant(jobDetail.status)}>{jobDetail.status}</Badge></span> <span class="text-muted-foreground">Статус</span><span><Badge variant={jobStatusVariant(jobDetail.status)}>{jobDetail.status}</Badge></span>