refactor(frontend): replace raw buttons with /ui/Button in SummaryPanel

- Replace raw <button> with <Button variant="ghost">
- Dim zero-count badges with opacity-50
- Add transition-colors duration-300 to count numbers
- Replace inline reconnect link with themed <Button>
This commit is contained in:
2026-07-05 15:50:41 +03:00
parent c75017f1e4
commit 95308273b3

View File

@@ -1,18 +1,21 @@
<!-- #region SummaryPanel [C:4] [TYPE Component] [SEMANTICS component, summary, dashboard, websocket-reactive, task-status-center] -->
<!-- @BRIEF Summary dashboard panel — grid of clickable cards showing task counts by type × status. -->
<!-- @LAYER UI Component -->
<!-- @LAYER UI -->
<!-- @UX_STATE idle: cards rendered normally, clickable -->
<!-- @UX_STATE loading: skeleton placeholders (4 cards with pulse animation) -->
<!-- @UX_STATE empty: all cards show zero counts -->
<!-- @UX_STATE reconnecting: last known counts visible, yellow pulsing indicator -->
<!-- @UX_STATE disconnected: last known counts visible, opacity reduced, "disconnected" badge -->
<!-- @UX_FEEDBACK Count change: number animates (CSS transition). Card click: emits filter event. -->
<!-- @UX_FEEDBACK Count change: number animates (CSS transition). Card click: emits filter event. Zero-count badges remain dimmed but visible. -->
<!-- @UX_RECOVERY Disconnected: auto-reconnect indicator. Click → manual reconnect button. -->
<!-- @UX_REACTIVITY Props: summary: TaskSummary | null, screenState: ScreenState. Derived: by_type grid. -->
<!-- @RELATION BINDS_TO -> [TaskCenterModel] -->
<!-- @RELATION BINDS_TO -> [TaskCenter.Model] -->
<!-- @PRE summary is non-null when screenState is 'ready' or 'disconnected'. -->
<!-- @POST Clicking a card calls onFilterByTypeAndStatus(taskType, status). -->
<!-- @DATA_CONTRACT Input: TaskSummary + ScreenState → Output: clickable status summary cards. -->
<!-- @SIDE_EFFECT Calls parent callbacks only; no direct network or store mutation. -->
<script lang="ts">
import { Button } from '$lib/ui';
import type { TaskType, ReportStatus, ScreenState, StatusCounts, TaskSummary } from '$types/reports';
const STATUS_CONFIG: { key: keyof StatusCounts; label: string; colorClass: string }[] = [
@@ -79,15 +82,17 @@
{#each STATUS_CONFIG as status (status.key)}
{@const count = getCount(typeSummary, status.key)}
{@const isActive = isStatusActive(typeSummary, status.key)}
<button
class="flex min-h-11 items-center gap-2 rounded-lg border px-2 py-2 text-left cursor-pointer transition-all hover:border-border-strong focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-ring {screenState === 'disconnected' ? 'opacity-60' : ''} {status.colorClass} {isActive ? 'border-primary-ring ring-2 ring-primary-ring/50' : 'border-border'}"
<Button
variant="ghost"
size="sm"
class="flex min-h-11 w-full items-center justify-start gap-2 rounded-lg border px-2 py-2 text-left cursor-pointer transition-all hover:border-border-strong focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-ring {screenState === 'disconnected' ? 'opacity-60' : ''} {status.colorClass} {count === 0 ? 'opacity-50' : ''} {isActive ? 'border-primary-ring ring-2 ring-primary-ring/50' : 'border-border'}"
onclick={() => handleCardClick(typeSummary, status.key)}
aria-label="{typeSummary.display_label}, {status.label}: {count}"
aria-pressed={isActive}
>
<span class="text-sm font-mono min-w-[1.5rem] text-center font-semibold">{count}</span>
<span class="text-sm font-mono min-w-[1.5rem] text-center font-semibold transition-colors duration-300">{count}</span>
<span class="text-xs truncate">{status.label}</span>
</button>
</Button>
{/each}
</div>
{/each}
@@ -95,7 +100,7 @@
<div class="flex items-center gap-2 text-xs text-text-muted mt-2">
<span class="w-2 h-2 rounded-full bg-destructive"></span>
Данные могут быть неактуальны
<button class="underline cursor-pointer" onclick={onReconnect}>Подключиться</button>
<Button variant="ghost" size="sm" class="h-auto p-0 underline" onclick={onReconnect}>Подключиться</Button>
</div>
{/if}
</div>