refactor: enhance module display and input handling in module pages
CI / changes (push) Successful in 6s
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 1m8s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Successful in 1m7s
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
CI / changes (push) Successful in 6s
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 1m8s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Successful in 1m7s
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
Updated the module display logic to improve the handling of nullable values for refresh intervals and community selections. Introduced new utility functions for managing nullable select values, ensuring better user experience and data integrity. Additionally, enhanced the visual presentation of module metadata with updated icons and styling, improving overall clarity and usability in the module pages.
This commit is contained in:
@@ -237,7 +237,9 @@
|
||||
</TableCell>
|
||||
<TableCell class="text-muted-foreground">{m.priority}</TableCell>
|
||||
<TableCell class="text-muted-foreground font-mono text-xs">
|
||||
{m.cron_expr ?? (m.refresh_interval_sec ? `${m.refresh_interval_sec}с` : '—')}
|
||||
{m.cron_expr ?? (m.refresh_interval_sec !== null && m.refresh_interval_sec !== undefined
|
||||
? `${m.refresh_interval_sec}с`
|
||||
: '—')}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{#if m.enabled}
|
||||
|
||||
@@ -74,6 +74,11 @@
|
||||
import Save from '@lucide/svelte/icons/save';
|
||||
import Upload from '@lucide/svelte/icons/upload';
|
||||
import Download from '@lucide/svelte/icons/download';
|
||||
import Blocks from '@lucide/svelte/icons/blocks';
|
||||
import ArrowDownUp from '@lucide/svelte/icons/arrow-down-up';
|
||||
import Timer from '@lucide/svelte/icons/timer';
|
||||
import ShieldCheck from '@lucide/svelte/icons/shield-check';
|
||||
import Network from '@lucide/svelte/icons/network';
|
||||
import { moduleTypeRu } from '$lib/ui-labels.js';
|
||||
|
||||
const moduleId = $derived(page.params.moduleId);
|
||||
@@ -150,6 +155,7 @@
|
||||
let csvImporting = $state(false);
|
||||
let csvExporting = $state(false);
|
||||
let csvFileInput = $state<HTMLInputElement | null>(null);
|
||||
const NONE_OPTION = '__none__';
|
||||
const selectedAsCount = $derived(selectedAsIds.size);
|
||||
const selectedCdnCount = $derived(selectedCdnIds.size);
|
||||
const selectedDomainCount = $derived(selectedDomainIds.size);
|
||||
@@ -716,6 +722,14 @@
|
||||
return t || c.community;
|
||||
}
|
||||
|
||||
function nullableSelectValue(value: string | null | undefined): string {
|
||||
return value ?? NONE_OPTION;
|
||||
}
|
||||
|
||||
function fromNullableSelect(value: string): string | null {
|
||||
return value === NONE_OPTION ? null : value;
|
||||
}
|
||||
|
||||
const activeTab = $derived.by(() => {
|
||||
if (!mod) return 'entries';
|
||||
switch (mod.type) {
|
||||
@@ -749,6 +763,12 @@
|
||||
<Button variant="ghost" size="icon-sm" href={resolve('/modules')}>
|
||||
<ArrowLeft class="size-4" />
|
||||
</Button>
|
||||
<div
|
||||
class="bg-chart-2/15 text-chart-2 flex size-11 shrink-0 items-center justify-center rounded-xl"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<Blocks class="size-6" />
|
||||
</div>
|
||||
<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>
|
||||
@@ -780,22 +800,22 @@
|
||||
|
||||
<!-- Module metadata -->
|
||||
<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>
|
||||
<Card class="bg-chart-3/10 p-3">
|
||||
<p class="text-muted-foreground flex items-center gap-1.5 text-xs"><ArrowDownUp class="size-3.5" />Приоритет</p>
|
||||
<p class="font-semibold">{mod.priority}</p>
|
||||
</Card>
|
||||
<Card class="p-3">
|
||||
<p class="text-muted-foreground text-xs">Интервал</p>
|
||||
<Card class="bg-chart-5/10 p-3">
|
||||
<p class="text-muted-foreground flex items-center gap-1.5 text-xs"><Timer class="size-3.5" />Интервал</p>
|
||||
<p class="font-semibold font-mono text-sm">
|
||||
{mod.cron_expr ?? (mod.refresh_interval_sec ? `${mod.refresh_interval_sec}с` : '—')}
|
||||
{mod.cron_expr ?? (mod.refresh_interval_sec !== null && mod.refresh_interval_sec !== undefined ? `${mod.refresh_interval_sec}с` : '—')}
|
||||
</p>
|
||||
</Card>
|
||||
<Card class="p-3">
|
||||
<p class="text-muted-foreground text-xs">DoH профиль</p>
|
||||
<Card class="bg-chart-4/10 p-3">
|
||||
<p class="text-muted-foreground flex items-center gap-1.5 text-xs"><Network class="size-3.5" />DoH профиль</p>
|
||||
<p class="font-semibold text-sm">{mod.doh_profile_id ? mod.doh_profile_id.slice(0, 8) + '…' : '—'}</p>
|
||||
</Card>
|
||||
<Card class="p-3">
|
||||
<p class="text-muted-foreground text-xs">Community по умолч.</p>
|
||||
<Card class="bg-chart-2/10 p-3">
|
||||
<p class="text-muted-foreground flex items-center gap-1.5 text-xs"><ShieldCheck class="size-3.5" />Community по умолч.</p>
|
||||
<p class="font-semibold text-sm break-all">{communityLabel(mod.default_community_id)}</p>
|
||||
</Card>
|
||||
</div>
|
||||
@@ -1032,7 +1052,11 @@
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell class="text-muted-foreground text-sm">{communityLabel(src.community_id)}</TableCell>
|
||||
<TableCell class="text-muted-foreground text-sm">{src.refresh_interval_sec ? `${src.refresh_interval_sec}с` : '—'}</TableCell>
|
||||
<TableCell class="text-muted-foreground text-sm">
|
||||
{src.refresh_interval_sec !== null && src.refresh_interval_sec !== undefined
|
||||
? `${src.refresh_interval_sec}с`
|
||||
: '—'}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div class="flex gap-1">
|
||||
<Button variant="ghost" size="icon-sm" onclick={() => openCdnEdit(src)}><Pencil class="size-3.5" /></Button>
|
||||
@@ -1252,12 +1276,18 @@
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
<Label for="e-comm">Community по умолчанию</Label>
|
||||
<Select type="single" value={editForm.default_community_id ?? ''} onValueChange={(v) => { editForm.default_community_id = v || null; }}>
|
||||
<Select
|
||||
type="single"
|
||||
value={nullableSelectValue(editForm.default_community_id)}
|
||||
onValueChange={(v) => {
|
||||
editForm.default_community_id = fromNullableSelect(v);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger id="e-comm" class="w-full">
|
||||
{editForm.default_community_id ? communityLabel(editForm.default_community_id) : 'Не выбрано'}
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="">Не выбрано</SelectItem>
|
||||
<SelectItem value={NONE_OPTION}>Не выбрано</SelectItem>
|
||||
{#each communities as c (c.id)}
|
||||
<SelectItem value={c.id}>{communityOptionLabel(c)}</SelectItem>
|
||||
{/each}
|
||||
@@ -1266,12 +1296,18 @@
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
<Label for="e-doh">DoH профиль</Label>
|
||||
<Select type="single" value={editForm.doh_profile_id ?? ''} onValueChange={(v) => { editForm.doh_profile_id = v || null; }}>
|
||||
<Select
|
||||
type="single"
|
||||
value={nullableSelectValue(editForm.doh_profile_id)}
|
||||
onValueChange={(v) => {
|
||||
editForm.doh_profile_id = fromNullableSelect(v);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger id="e-doh" class="w-full">
|
||||
{editForm.doh_profile_id ? (dohProfiles.find(d => d.id === editForm.doh_profile_id)?.url ?? editForm.doh_profile_id) : 'Не выбрано'}
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="">Не выбрано</SelectItem>
|
||||
<SelectItem value={NONE_OPTION}>Не выбрано</SelectItem>
|
||||
{#each dohProfiles as d (d.id)}
|
||||
<SelectItem value={d.id}>{d.url}</SelectItem>
|
||||
{/each}
|
||||
@@ -1334,12 +1370,18 @@
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
<Label for="as-comm">Community</Label>
|
||||
<Select type="single" value={asForm.community_id ?? ''} onValueChange={(v) => { asForm.community_id = v || null; }}>
|
||||
<Select
|
||||
type="single"
|
||||
value={nullableSelectValue(asForm.community_id)}
|
||||
onValueChange={(v) => {
|
||||
asForm.community_id = fromNullableSelect(v);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger id="as-comm" class="w-full">
|
||||
{asForm.community_id ? communityLabel(asForm.community_id) : 'Не выбрано'}
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="">Не выбрано</SelectItem>
|
||||
<SelectItem value={NONE_OPTION}>Не выбрано</SelectItem>
|
||||
{#each communities as c (c.id)}
|
||||
<SelectItem value={c.id}>{communityOptionLabel(c)}</SelectItem>
|
||||
{/each}
|
||||
@@ -1431,12 +1473,18 @@
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
<Label for="cdn-comm">Community</Label>
|
||||
<Select type="single" value={cdnForm.community_id ?? ''} onValueChange={(v) => { cdnForm.community_id = v || null; }}>
|
||||
<Select
|
||||
type="single"
|
||||
value={nullableSelectValue(cdnForm.community_id)}
|
||||
onValueChange={(v) => {
|
||||
cdnForm.community_id = fromNullableSelect(v);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger id="cdn-comm" class="w-full">
|
||||
{cdnForm.community_id ? communityLabel(cdnForm.community_id) : 'Не выбрано'}
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="">Не выбрано</SelectItem>
|
||||
<SelectItem value={NONE_OPTION}>Не выбрано</SelectItem>
|
||||
{#each communities as c (c.id)}
|
||||
<SelectItem value={c.id}>{communityOptionLabel(c)}</SelectItem>
|
||||
{/each}
|
||||
@@ -1518,12 +1566,18 @@
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
<Label for="dom-comm">Community</Label>
|
||||
<Select type="single" value={domainForm.community_id ?? ''} onValueChange={(v) => { domainForm.community_id = v || null; }}>
|
||||
<Select
|
||||
type="single"
|
||||
value={nullableSelectValue(domainForm.community_id)}
|
||||
onValueChange={(v) => {
|
||||
domainForm.community_id = fromNullableSelect(v);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger id="dom-comm" class="w-full">
|
||||
{domainForm.community_id ? communityLabel(domainForm.community_id) : 'Не выбрано'}
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="">Не выбрано</SelectItem>
|
||||
<SelectItem value={NONE_OPTION}>Не выбрано</SelectItem>
|
||||
{#each communities as c (c.id)}
|
||||
<SelectItem value={c.id}>{communityOptionLabel(c)}</SelectItem>
|
||||
{/each}
|
||||
|
||||
Reference in New Issue
Block a user