Files
electric-cherry/src/lib/ui/components/NotificationHistory.svelte
T
josh 8ac420fe61 Improve operational UX, recovery flows, and action safety
Implement the approved UX review across Fleet, Betas, Library, deployments, and Settings. Add reviewed promotion and reconciliation plans, atomic queued-removal cancellation, recoverable beta drafts and launch inputs, accessible controls, and mobile improvements.

Validated with 473 tests, a clean typecheck, production build, and isolated desktop/mobile browser walkthroughs. Include the review and implementation coverage notes.
2026-09-19 21:34:34 -04:00

25 lines
1.7 KiB
Svelte

<script lang="ts">
import Dialog from './primitives/Dialog.svelte';
import Button from './primitives/Button.svelte';
import { notificationHistory, markNotificationsRead, clearNotifications } from '$ui/toast.js';
let { open = $bindable(false) }: { open?: boolean } = $props();
$effect(() => { if (open) markNotificationsRead(); });
</script>
<Dialog bind:open title="Recent notifications" description="Recent action results and completed lobby watches in this browser tab. Watches run while the app is open.">
{#if $notificationHistory.length}
<ol>{#each $notificationHistory as item (item.id)}<li class:attention={item.variant === 'danger' || item.variant === 'warning'}><p>{item.message}</p><time datetime={item.createdAt}>{new Date(item.createdAt).toLocaleString()}</time>{#if item.href}<a href={item.href} onclick={() => open = false}>View details →</a>{/if}</li>{/each}</ol>
{:else}<p class="empty">No recent notifications. Completed lobby watches and action results will appear here.</p>{/if}
{#snippet actions()}<Button disabled={!$notificationHistory.length} onclick={clearNotifications}>Clear history</Button><Button onclick={() => open = false}>Close</Button>{/snippet}
</Dialog>
<style>
ol { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
li { border: 1px solid var(--border-strong); padding: 12px; border-radius: var(--radius-md); }
li.attention { border-color: var(--state-warning); }
p { margin: 0 0 8px; font-size: var(--text-sm); }
time { display: block; color: var(--text-secondary); font-size: var(--text-xs); }
a { display: inline-block; margin-top: 8px; font-size: var(--text-sm); }
.empty { color: var(--text-secondary); }
</style>