summaryrefslogtreecommitdiff
path: root/frontend/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/components')
-rw-r--r--frontend/src/components/Status.svelte35
1 files changed, 25 insertions, 10 deletions
diff --git a/frontend/src/components/Status.svelte b/frontend/src/components/Status.svelte
index ce2a753..7f06d95 100644
--- a/frontend/src/components/Status.svelte
+++ b/frontend/src/components/Status.svelte
@@ -1,5 +1,7 @@
<script lang="ts">
- import { Alert } from '$lib/components/ui/alert';
+ import type { Snippet } from 'svelte';
+ import { Alert, AlertTitle, AlertDescription } from '$lib/components/ui/alert';
+ import { Skeleton } from '$lib/components/ui/skeleton';
import * as Card from '$lib/components/ui/card';
interface Props {
@@ -8,25 +10,38 @@
busyText?: string;
empty?: string | boolean | null;
emptyHint?: string;
+ emptyCta?: Snippet;
+ errorCta?: Snippet;
warnings?: string[];
+ skeletonRows?: number;
}
- let { error, busy, busyText = '加载中…', empty, emptyHint, warnings }: Props = $props();
+ let { error, busy, busyText = '加载中…', empty, emptyHint, emptyCta, errorCta, warnings, skeletonRows = 3 }: Props = $props();
</script>
{#if busy}
- <div class="text-muted-foreground flex items-center gap-2 py-3 text-sm" role="status">
- <span class="inline-block size-4 rounded-full border-2 border-primary border-t-transparent align-[-0.125em] animate-spin" aria-hidden="true"></span>
- {busyText}
+ <div class="flex flex-col gap-1" data-testid="busy-skeleton" aria-label={busyText} role="status">
+ {#each Array.from({ length: skeletonRows }) as _, i (i)}
+ <Skeleton class="h-10 w-full" />
+ {/each}
</div>
{:else if error}
- <Alert variant="destructive">
+ <Alert variant="destructive" data-testid="load-error">
<span aria-hidden="true">⚠</span>
- <span>{error}</span>
+ <AlertTitle>加载失败</AlertTitle>
+ <AlertDescription>{error ?? busyText}</AlertDescription>
+ {#if errorCta}
+ <div class="mt-2">
+ {@render errorCta()}
+ </div>
+ {/if}
</Alert>
{:else if empty !== null && empty !== undefined && empty !== false}
- <Card.Root class="border-dashed text-center py-8 px-4 text-muted-foreground">
- <p class="font-semibold mb-1">{empty}</p>
- {#if emptyHint}<p class="text-sm text-muted-foreground">{emptyHint}</p>{/if}
+ <Card.Root class="border-dashed text-center py-10 px-6" data-testid="empty-state">
+ <p class="text-lg font-semibold mb-1">{empty}</p>
+ {#if emptyHint}
+ <p class="text-xs text-muted-foreground mb-4">{emptyHint}</p>
+ {/if}
+ {#if emptyCta}{@render emptyCta()}{/if}
</Card.Root>
{/if}
{#if warnings && warnings.length > 0}