Files
telemt-api/web/src/lib/components/ip-server-presence.svelte
T
Denozordec 46d080e70a
Publish telemt-api gateway Docker image / test (push) Successful in 27s
Publish telemt-api gateway Docker image / build-and-push (push) Successful in 2m36s
Enhance IP display and server presence in Svelte components
- Added `IpServerPresence` component to streamline the display of active and recent servers for IPs, improving code readability and maintainability.
- Introduced tooltips for the "Servers" column in tables to provide additional context on active and recent server lists, enhancing user experience.
- Updated text descriptions for clarity and consistency across the IP and user pages, ensuring better understanding of the displayed data.
2026-03-30 23:58:05 +07:00

121 lines
4.2 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts">
import { Badge } from '$lib/components/ui/badge/index.js';
import { Separator } from '$lib/components/ui/separator/index.js';
import * as Tooltip from '$lib/components/ui/tooltip/index.js';
import { cn } from '$lib/utils.js';
import InfoIcon from '@lucide/svelte/icons/info';
import StarIcon from '@lucide/svelte/icons/star';
let {
active_on_servers,
recent_on_servers,
primary_server,
linkToServer = true,
class: className
}: {
active_on_servers?: string[] | null;
recent_on_servers?: string[] | null;
primary_server?: string | null;
/** Ссылка на `/servers/{alias}` у бейджей */
linkToServer?: boolean;
class?: string;
} = $props();
const activeServers = $derived(((active_on_servers ?? []) as string[]).filter(Boolean));
const recentServers = $derived(((recent_on_servers ?? []) as string[]).filter(Boolean));
const recentOnly = $derived(recentServers.filter((s) => !activeServers.includes(s)));
const soleActiveTip =
'Единственный сервер в активных на этом снимке (не приоритет, а однозначность).';
function serverHref(alias: string): string | undefined {
if (!linkToServer) return undefined;
return `/servers/${encodeURIComponent(alias)}`;
}
</script>
<div class={cn('space-y-2 text-xs', className)}>
{#if activeServers.length > 0}
<div class="space-y-1">
<div class="flex flex-wrap items-center gap-1">
<span class="text-muted-foreground">Активные</span>
<Tooltip.Root>
<Tooltip.Trigger
class="inline-flex size-3.5 shrink-0 items-center justify-center rounded-sm text-muted-foreground hover:text-foreground"
aria-label="Пояснение: активные"
>
<InfoIcon class="size-3.5" />
</Tooltip.Trigger>
<Tooltip.Content side="top" class="max-w-xs">
<p class="text-xs">
Серверы, где этот IP есть в списке активных уникальных адресов на момент снимка
шлюза.
</p>
</Tooltip.Content>
</Tooltip.Root>
</div>
<div class="flex flex-wrap items-center gap-1">
{#each activeServers as srv (srv)}
{#if primary_server && srv === primary_server && activeServers.length === 1}
<Tooltip.Root>
<Tooltip.Trigger class="inline-flex">
<Badge
variant="secondary"
class="font-normal px-2 py-0.5 gap-1"
href={serverHref(srv)}
>
<StarIcon class="size-3 shrink-0 opacity-90" aria-hidden="true" />
{srv}
</Badge>
</Tooltip.Trigger>
<Tooltip.Content side="bottom" class="max-w-xs">
<p class="text-xs">{soleActiveTip}</p>
</Tooltip.Content>
</Tooltip.Root>
{:else}
<Badge variant="secondary" class="font-normal px-2 py-0.5" href={serverHref(srv)}>
{srv}
</Badge>
{/if}
{/each}
</div>
</div>
{:else}
<span class="text-muted-foreground">
{recentOnly.length > 0 ? 'Нет в активных' : '—'}
</span>
{/if}
{#if recentOnly.length > 0}
{#if activeServers.length > 0}
<Separator class="my-1" />
{/if}
<div class="space-y-1">
<div class="flex flex-wrap items-center gap-1">
<span class="text-muted-foreground">Только недавние</span>
<Tooltip.Root>
<Tooltip.Trigger
class="inline-flex size-3.5 shrink-0 items-center justify-center rounded-sm text-muted-foreground hover:text-foreground"
aria-label="Пояснение: только недавние"
>
<InfoIcon class="size-3.5" />
</Tooltip.Trigger>
<Tooltip.Content side="top" class="max-w-xs">
<p class="text-xs">
Серверы, где IP есть в недавнем списке, но на этом снимке не в активных — например
недавнее подключение с другого шлюза.
</p>
</Tooltip.Content>
</Tooltip.Root>
</div>
<div class="flex flex-wrap items-center gap-1">
{#each recentOnly as srv (srv)}
<Badge variant="outline" class="font-normal px-2 py-0.5" href={serverHref(srv)}>
{srv}
</Badge>
{/each}
</div>
</div>
{/if}
</div>