feat(frontend): DashboardDataGrid — configurable grid component

Consolidate dashboard grid patterns into a single reusable component with
opt-in features: selection, sorting, filtering, pagination, loading skeleton,
empty state, and bulk actions snippet.

- Add Dashboard.DataGrid component with () state management
- Replace DashboardGrid usage in migration page (removes Validate/Git/Status columns)
- Deprecate DashboardGrid (no longer used by any active route)
- Update RepositoryDashboardGrid header with consolidation rationale
- Add 14 vitest tests covering all features and edge cases

Strategy B consolidation: migration page now uses clean grid with only
Title + Last Modified columns. DashboardGrid marked @DEPRECATED.
RepositoryDashboardGrid noted as future consolidation candidate.
This commit is contained in:
2026-06-17 14:01:15 +03:00
parent 4c8b4084c8
commit 432c498330
5 changed files with 609 additions and 20 deletions

View File

@@ -6,7 +6,7 @@
@LAYER UI
@RELATION BINDS_TO -> [MigrationModel]
@RELATION DEPENDS_ON ->[EnvSelector]
@RELATION DEPENDS_ON ->[DashboardGrid]
@RELATION DEPENDS_ON ->[Dashboard.DataGrid]
@RELATION DEPENDS_ON ->[MappingTable]
@RELATION DEPENDS_ON ->[EXT:frontend:TaskRunner]
@RELATION DEPENDS_ON ->[TaskHistory]
@@ -37,7 +37,7 @@
<script lang="ts">
import { onMount } from "svelte";
import EnvSelector from "$lib/components/ui/EnvSelector.svelte";
import DashboardGrid from "$lib/components/dashboard/DashboardGrid.svelte";
import DashboardDataGrid from "$lib/components/dashboard/DashboardDataGrid.svelte";
import MappingTable from "$lib/components/ui/MappingTable.svelte";
import TaskRunner from "$lib/components/tasks/TaskRunner.svelte";
import TaskHistory from "$lib/components/tasks/TaskHistory.svelte";
@@ -131,7 +131,7 @@
{ step: 4, label: $t.migration?.start || "Migrate" },
] as s}
<!-- Step indicator buttons — custom styling for step wizard (not standard Button) -->
<button
<Button variant="ghost"
onclick={() => model.goToStep(s.step)}
disabled={s.step > model.currentStep && !((s.step === 2 && model.stepReady[1]) || (s.step === 3 && model.stepReady[1] && model.stepReady[2]) || (s.step === 4 && model.dryRunResult))}
class="flex flex-col items-center group"
@@ -157,7 +157,7 @@
<span class="mt-2 text-xs font-medium hidden sm:block
${model.currentStep === s.step ? 'text-primary' : 'text-text-muted'}
">{s.label}</span>
</button>
</Button>
{#if s.step < 4}
<div class="flex-1 mx-2 mb-6">
<div class="h-0.5 bg-surface-muted rounded">
@@ -268,10 +268,20 @@
{#if model.sourceEnvId}
<div class="mb-6">
<DashboardGrid
dashboards={model.dashboards}
<DashboardDataGrid
data={model.dashboards}
columns={[
{ key: 'title', label: $t.dashboard?.title || 'Title', sortable: true },
{ key: 'last_modified', label: $t.dashboard?.last_modified || 'Last Modified', sortable: true },
]}
bind:selectedIds={model.selectedDashboardIds}
environmentId={model.sourceEnvId}
selectable
sortable
filterable
paginated
pageSize={20}
filterPlaceholder={$t.dashboard?.search || "Search..."}
loading={model.loading}
/>
</div>
{:else}