refactor(frontend): migrate remaining EmptyState patterns (8 files)
Replaced hand-rolled border-dashed empty states with <EmptyState>:
- HealthMatrix, ProviderConfig, ApiKeysTab, dashboards/+page,
git/+page, MetricsTable, ColumnsTable, DashboardDataGrid
Table empty states wrapped in <tr><td colspan={N}>. Custom SVG icons
preserved via {#snippet icon()}.
This commit is contained in:
@@ -31,7 +31,7 @@
|
||||
<script lang="ts">
|
||||
// [SECTION: IMPORTS]
|
||||
import { t } from '$lib/i18n/index.svelte.js';
|
||||
import { Button, Input } from "$lib/ui";
|
||||
import { Button, EmptyState, Input } from "$lib/ui";
|
||||
|
||||
// [SECTION: TYPE EXPORTS]
|
||||
export interface Column {
|
||||
@@ -209,12 +209,13 @@
|
||||
|
||||
<!-- ── Empty State ── -->
|
||||
{:else if !loading && displayData.length === 0}
|
||||
<div class="flex flex-col items-center justify-center py-12 text-text-subtle rounded-lg border border-dashed border-border">
|
||||
<svg class="w-12 h-12 mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
||||
</svg>
|
||||
<p class="text-sm">{emptyText || $t.common?.no_data || "No items"}</p>
|
||||
</div>
|
||||
<EmptyState title={emptyText || $t.common?.no_data || "No items"}>
|
||||
{#snippet icon()}
|
||||
<svg class="w-12 h-12" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
||||
</svg>
|
||||
{/snippet}
|
||||
</EmptyState>
|
||||
|
||||
<!-- ── Table ── -->
|
||||
{:else}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<!-- @UX_STATE Active -> One card highlighted (the current filter). -->
|
||||
<script lang="ts">
|
||||
import { t } from '$lib/i18n/index.svelte.js';
|
||||
import { EmptyState } from "$lib/ui";
|
||||
|
||||
let {
|
||||
pass_count = 0,
|
||||
@@ -116,12 +117,16 @@
|
||||
</div>
|
||||
|
||||
{#if total === 0}
|
||||
<div class="mt-4 p-8 text-center border-2 border-dashed border-border rounded-xl text-text-subtle">
|
||||
<svg class="w-10 h-10 mx-auto mb-2 text-text-subtle" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
||||
</svg>
|
||||
<p>{$t.health?.no_records_for_environment || 'No validation records for this environment'}</p>
|
||||
</div>
|
||||
<EmptyState
|
||||
title={$t.health?.no_records_for_environment || 'No validation records for this environment'}
|
||||
class="mt-4"
|
||||
>
|
||||
{#snippet icon()}
|
||||
<svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
||||
</svg>
|
||||
{/snippet}
|
||||
</EmptyState>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -16,7 +16,7 @@ import { SvelteSet } from "svelte/reactivity";
|
||||
import { requestApi } from "$lib/api";
|
||||
import { addToast } from "$lib/toasts.svelte.js";
|
||||
import HelpTooltip from "$lib/ui/HelpTooltip.svelte";
|
||||
import { ConfirmDialog } from "$lib/ui";
|
||||
import { ConfirmDialog, EmptyState } from "$lib/ui";
|
||||
|
||||
let {
|
||||
providers = [],
|
||||
@@ -757,11 +757,7 @@ import { SvelteSet } from "svelte/reactivity";
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div
|
||||
class="text-center py-8 text-text-muted border-2 border-dashed rounded-lg"
|
||||
>
|
||||
{$t.llm.no_providers}
|
||||
</div>
|
||||
<EmptyState title={$t.llm.no_providers} />
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
import { t } from '$lib/i18n/index.svelte.js';
|
||||
import { api } from '$lib/api.js';
|
||||
import { addToast } from '$lib/toasts.svelte.js';
|
||||
import { ConfirmDialog } from '$lib/ui';
|
||||
import { ConfirmDialog, EmptyState } from '$lib/ui';
|
||||
|
||||
let keys = $state([]);
|
||||
let environments = $state([]);
|
||||
@@ -321,10 +321,7 @@
|
||||
{/each}
|
||||
</div>
|
||||
{:else if keys.length === 0}
|
||||
<!-- Empty State -->
|
||||
<div class="text-center py-8 text-text-muted text-sm border-2 border-dashed border-border rounded-lg">
|
||||
{$t.api_keys?.empty || 'No API keys configured.'}
|
||||
</div>
|
||||
<EmptyState title={$t.api_keys?.empty || 'No API keys configured.'} />
|
||||
{:else}
|
||||
<!-- Keys Table -->
|
||||
<div class="overflow-x-auto border border-border rounded-lg">
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
import DashboardMaintenanceBadge from "$lib/components/DashboardMaintenanceBadge.svelte";
|
||||
|
||||
import { environmentContextStore, initializeEnvironmentContext, setSelectedEnvironment } from "$lib/stores/environmentContext.svelte.js";
|
||||
import { Button, Icon, Pagination, Skeleton } from "$lib/ui";
|
||||
import { Button, EmptyState, Icon, Pagination, Skeleton } from "$lib/ui";
|
||||
import ColumnFilterPopover from "./ColumnFilterPopover.svelte";
|
||||
import { formatDate, getSortIndicator } from "./dashboard-helpers.js";
|
||||
import { DashboardHubModel } from "$lib/models/DashboardHubModel.svelte.ts";
|
||||
@@ -234,34 +234,15 @@
|
||||
</div>
|
||||
</div>
|
||||
{:else if m.dashboards.length === 0}
|
||||
<!-- Empty State -->
|
||||
<div class="py-12 text-center text-text-muted">
|
||||
<svg
|
||||
class="w-16 h-16 mx-auto mb-4 text-text-subtle"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path d="M3 3h18v18H3V3zm16 16V5H5v14h14z" />
|
||||
</svg>
|
||||
|
||||
{#if m.effectiveProfileFilter?.applied}
|
||||
<p>{$t.profile?.filter_empty_state || "No dashboards found for active profile filters. Try adjusting your filter settings."}</p>
|
||||
<div class="mt-4">
|
||||
<button
|
||||
type="button"
|
||||
class="rounded border border-primary bg-surface-card px-3 py-1.5 text-xs font-medium text-primary hover:bg-primary-light"
|
||||
onclick={() => m.handleTemporaryShowAll()}
|
||||
>
|
||||
{$t.profile?.filter_show_all_temporarily || "Show all dashboards temporarily"}
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<p>{$t.dashboard?.empty}</p>
|
||||
{/if}
|
||||
</div>
|
||||
{#if m.effectiveProfileFilter?.applied}
|
||||
<EmptyState
|
||||
title={$t.profile?.filter_empty_state || "No dashboards found for active profile filters. Try adjusting your filter settings."}
|
||||
actionLabel={$t.profile?.filter_show_all_temporarily || "Show all dashboards temporarily"}
|
||||
onAction={() => m.handleTemporaryShowAll()}
|
||||
/>
|
||||
{:else}
|
||||
<EmptyState title={$t.dashboard?.empty} />
|
||||
{/if}
|
||||
{:else}
|
||||
<!-- Toolbar -->
|
||||
<div class="flex items-center justify-between mb-4 gap-4">
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<script lang="ts">
|
||||
import { t } from "$lib/i18n/index.svelte.js";
|
||||
import { api } from "$lib/api.js";
|
||||
import { EmptyState } from "$lib/ui";
|
||||
|
||||
let { columns = [], datasetId, envId } = $props();
|
||||
|
||||
@@ -71,7 +72,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
{#if columns.length === 0}
|
||||
<tr><td colspan="4" class="py-4 text-center text-text-subtle">{$t.datasets?.detail_empty ?? 'No data'}</td></tr>
|
||||
<tr><td colspan="4"><EmptyState title={$t.datasets?.detail_empty ?? 'No data'} /></td></tr>
|
||||
{:else}
|
||||
{#each columns as col (col.id)}
|
||||
<tr class="border-b border-border hover:bg-surface-muted {editingRowId && editingRowId !== col.id ? 'opacity-50' : ''}">
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<script lang="ts">
|
||||
import { t } from "$lib/i18n/index.svelte.js";
|
||||
import { api } from "$lib/api.js";
|
||||
import { EmptyState } from "$lib/ui";
|
||||
|
||||
let { metrics = [], datasetId, envId } = $props();
|
||||
|
||||
@@ -59,7 +60,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
{#if metrics.length === 0}
|
||||
<tr><td colspan="3" class="py-4 text-center text-text-subtle">{$t.datasets?.detail_empty ?? 'No data'}</td></tr>
|
||||
<tr><td colspan="3"><EmptyState title={$t.datasets?.detail_empty ?? 'No data'} /></td></tr>
|
||||
{:else}
|
||||
{#each metrics as metric (metric.id)}
|
||||
<tr class="border-b border-border hover:bg-surface-muted {editingRowId && editingRowId !== metric.id ? 'opacity-50' : ''}">
|
||||
|
||||
@@ -21,7 +21,7 @@ import { SvelteSet } from "svelte/reactivity";
|
||||
import { gitService } from '../../services/gitService.js';
|
||||
import type { DashboardMetadata } from '../../types/dashboard';
|
||||
import { t } from '$lib/i18n/index.svelte.js';
|
||||
import { Card, PageHeader, Select } from '$lib/ui';
|
||||
import { Card, EmptyState, PageHeader, Select } from '$lib/ui';
|
||||
import {
|
||||
environmentContextStore,
|
||||
initializeEnvironmentContext,
|
||||
@@ -244,11 +244,11 @@ import { SvelteSet } from "svelte/reactivity";
|
||||
repositoriesOnly={activeTab === 'repos'}
|
||||
/>
|
||||
{:else}
|
||||
<p class="text-text-muted italic">
|
||||
{activeTab === 'repos'
|
||||
<EmptyState
|
||||
title={activeTab === 'repos'
|
||||
? ($t.git?.no_repositories_selected || "No repositories found")
|
||||
: ($t.dashboard?.no_dashboards || "No dashboards")}
|
||||
</p>
|
||||
/>
|
||||
{/if}
|
||||
</Card>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user