- 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.
121 lines
4.2 KiB
Svelte
121 lines
4.2 KiB
Svelte
<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>
|