feat(redesign): project mockup richness onto live dashboard
Bringing the Aurora dashboard mockup to feature parity in the real app:
- NavIcon component: thin-stroke SVG icon set covering ~30 nav-tier
glyphs (dashboard, server, target, radar, bots, console, email,
matrix, webhook, slack, bullhorn, settings, theme, search, logout,
chevrons, plus/arrow). Falls back to MdiIcon for unknown names so
every existing usage keeps working.
- layout.svelte: sidebar nav swapped from MdiIcon to NavIcon — dropping
the dense filled glyphs in favour of soft 1.6px outlines that match
the mockup. Main container max-width bumped from 5xl (1024px) to
7xl (1280px) so the dashboard finally gets to breathe.
- +page.svelte (dashboard): full rewrite of the markup to project
every mockup section onto live data:
* Editorial Hero — 'Tonight, everything is flowing.' with gradient
italic emphasis, live/attention pill driven by failure count,
and a big mono throughput meter (24h events, armed/total,
providers, targets) on the right.
* Stats — kept the 4-card grid, refined accent palette per card.
* Two-column row — Signal stream (events with full routing trail:
tracker → provider, gradient avatar tile per event-type) on the
left, On-watch provider deck (per-provider activity bar fill +
live/idle pulse, derived from recent_events) on the right.
* Pulse panel wrapping the existing EventChart with the new title
treatment.
* Active wires — Sankey-style 'Tracker → Target' route summary
derived from notificationTrackers.tracker_targets + targetsCache
with event counts per route.
* Compose band — gradient call-to-action strip with new-tracker CTA.
- i18n: en.json + ru.json get the full set of new dashboard keys
(hero copy, panel titles, compose band, wires labels).
Build: 0 errors, 57 pre-existing warnings unchanged.
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
<script lang="ts">
|
||||
/**
|
||||
* Thin-stroke SVG icon set for navigation surfaces.
|
||||
*
|
||||
* Mirrors the visual language of the Aurora design mockups — soft outline
|
||||
* glyphs at 1.6px stroke. Falls back to MdiIcon for any name we don't
|
||||
* have a hand-drawn version of, so the existing navEntries config keeps
|
||||
* working unchanged.
|
||||
*/
|
||||
import MdiIcon from './MdiIcon.svelte';
|
||||
|
||||
interface Props {
|
||||
name: string;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
const { name, size = 18 }: Props = $props();
|
||||
</script>
|
||||
|
||||
{#if name === 'mdiViewDashboard'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12h4l3-9 4 18 3-9h4"/></svg>
|
||||
{:else if name === 'mdiServer'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/></svg>
|
||||
{:else if name === 'mdiBellOutline' || name === 'mdiBell'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10 21a2 2 0 0 0 4 0"/></svg>
|
||||
{:else if name === 'mdiConsoleLine'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M7 9l3 3-3 3M13 15h4"/></svg>
|
||||
{:else if name === 'mdiRobotOutline' || name === 'mdiRobot'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="6" width="16" height="14" rx="3"/><circle cx="9" cy="12" r="1.2"/><circle cx="15" cy="12" r="1.2"/><path d="M8 17c1.5 1 6.5 1 8 0M12 3v3"/></svg>
|
||||
{:else if name === 'mdiTarget'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18"/></svg>
|
||||
{:else if name === 'mdiCogOutline' || name === 'mdiCog'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1.1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1.1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z"/></svg>
|
||||
{:else if name === 'mdiRadar'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><circle cx="12" cy="12" r="8"/><path d="M12 4v3M12 17v3M4 12h3M17 12h3"/></svg>
|
||||
{:else if name === 'mdiFileDocumentEdit'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/><path d="M14 2v6h6"/><path d="M18 14l3 3-5 5h-3v-3z"/></svg>
|
||||
{:else if name === 'mdiCodeBracesBox'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M9 8a2 2 0 0 0-2 2v1.5a1 1 0 0 1-1 1 1 1 0 0 1 1 1V15a2 2 0 0 0 2 2M15 8a2 2 0 0 1 2 2v1.5a1 1 0 0 0 1 1 1 1 0 0 0-1 1V15a2 2 0 0 1-2 2"/></svg>
|
||||
{:else if name === 'mdiPlayCircleOutline'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M10 9l5 3-5 3z" fill="currentColor"/></svg>
|
||||
{:else if name === 'mdiSendCircle' || name === 'mdiSend'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M22 2L11 13M22 2l-7 20-4-9-9-4z"/></svg>
|
||||
{:else if name === 'mdiEmailOutline'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M3 7l9 7 9-7"/></svg>
|
||||
{:else if name === 'mdiMatrix'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="3" height="18"/><rect x="18" y="3" width="3" height="18"/><path d="M6 6h2M6 18h2M16 6h2M16 18h2"/></svg>
|
||||
{:else if name === 'mdiWebhook'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="18" r="3"/><circle cx="18" cy="18" r="3"/><circle cx="12" cy="5" r="3"/><path d="M12 8l-4 7M15 18H9M16 8l4 7"/></svg>
|
||||
{:else if name === 'mdiChat'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
|
||||
{:else if name === 'mdiSlack'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="3" width="3" height="9" rx="1.5"/><rect x="14" y="9" width="7" height="3" rx="1.5"/><rect x="12" y="14" width="3" height="7" rx="1.5"/><rect x="3" y="12" width="7" height="3" rx="1.5"/></svg>
|
||||
{:else if name === 'mdiBullhorn'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M3 11v3a1 1 0 0 0 1 1h3l5 4V6L7 10H4a1 1 0 0 0-1 1z"/><path d="M16 8a5 5 0 0 1 0 8M19 5a9 9 0 0 1 0 14"/></svg>
|
||||
{:else if name === 'mdiBackupRestore'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 3-6.7"/><path d="M3 4v5h5"/><path d="M12 7v5l3 2"/></svg>
|
||||
{:else if name === 'mdiAccountGroup'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="8" r="3.5"/><path d="M2 21a7 7 0 0 1 14 0"/><circle cx="17" cy="6" r="3"/><path d="M22 18a5 5 0 0 0-5-5"/></svg>
|
||||
{:else if name === 'mdiChevronRight'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M9 6l6 6-6 6"/></svg>
|
||||
{:else if name === 'mdiChevronLeft'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M15 6l-6 6 6 6"/></svg>
|
||||
{:else if name === 'mdiChevronDown'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>
|
||||
{:else if name === 'mdiMagnify'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>
|
||||
{:else if name === 'mdiLogout'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9"/></svg>
|
||||
{:else if name === 'mdiKeyVariant'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="15" r="4"/><path d="M11 13l9-9 2 2-2 2 2 2-3 3-2-2"/></svg>
|
||||
{:else if name === 'mdiApi'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="3"/><path d="M7 16V9a2 2 0 1 1 4 0v7M7 13h4M14 9v7M17 9v7"/></svg>
|
||||
{:else if name === 'mdiWeatherNight'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>
|
||||
{:else if name === 'mdiWeatherSunny'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 3v2M12 19v2M3 12h2M19 12h2M5.6 5.6l1.4 1.4M17 17l1.4 1.4M5.6 18.4L7 17M17 7l1.4-1.4"/></svg>
|
||||
{:else if name === 'mdiDesktopTowerMonitor'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="4" width="14" height="10" rx="1"/><path d="M9 14v3M6 17h6"/><rect x="18" y="4" width="4" height="16" rx="1"/></svg>
|
||||
{:else if name === 'mdiFilterOff'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3l18 18M22 3H6l3.4 4.4M14 13v8l-4-2v-4"/></svg>
|
||||
{:else if name === 'mdiDotsHorizontal'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="currentColor"><circle cx="6" cy="12" r="1.6"/><circle cx="12" cy="12" r="1.6"/><circle cx="18" cy="12" r="1.6"/></svg>
|
||||
{:else if name === 'mdiPulse'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12h4l3-9 4 18 3-9h4"/></svg>
|
||||
{:else if name === 'mdiPlus'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12h14"/></svg>
|
||||
{:else if name === 'mdiArrowRight'}
|
||||
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
|
||||
{:else}
|
||||
<MdiIcon {name} {size} />
|
||||
{/if}
|
||||
@@ -103,7 +103,39 @@
|
||||
"last14days": "Last 14 days",
|
||||
"event": "event",
|
||||
"events": "events",
|
||||
"noChartData": "No event data yet"
|
||||
"noChartData": "No event data yet",
|
||||
"live": "Live",
|
||||
"attention": "Attention",
|
||||
"heroPrefix": "Tonight,",
|
||||
"heroEmphasis": "everything",
|
||||
"heroSuffix": "is flowing.",
|
||||
"heroSummary": "{providers} providers listening, {armed} of {total} trackers armed, {throughput} events dispatched across {targets} targets in 24h.",
|
||||
"throughput24h": "throughput · 24h",
|
||||
"eventsShort": "events",
|
||||
"armedShort": "armed",
|
||||
"providersShort": "providers",
|
||||
"targetsShort": "targets",
|
||||
"trackersShort": "trackers",
|
||||
"streamTitle": "Signal",
|
||||
"streamEmphasis": "stream",
|
||||
"eventsLabel": "events",
|
||||
"onWatchTitle": "On",
|
||||
"onWatchEmphasis": "watch",
|
||||
"noProviders": "No providers yet.",
|
||||
"addProvider": "Add provider",
|
||||
"addProviderHint": "Connect a service to start tracking",
|
||||
"pulseTitle": "Pulse",
|
||||
"pulseEmphasis": "· last 14 days",
|
||||
"pulseSub": "Events grouped by day",
|
||||
"wiresTitle": "Active",
|
||||
"wiresEmphasis": "wires",
|
||||
"wiresSub": "routes",
|
||||
"composeTitle": "Pick a source. Choose a channel.",
|
||||
"composeEmphasis": "Compose the wire.",
|
||||
"composeSub": "Walk from provider → tracker → template → target. Or paste a webhook URL and we'll infer the rest.",
|
||||
"viewTrackers": "View trackers",
|
||||
"newTracker": "New tracker",
|
||||
"eventsTotal": "Events"
|
||||
},
|
||||
"providers": {
|
||||
"title": "Providers",
|
||||
|
||||
@@ -103,7 +103,39 @@
|
||||
"last14days": "Последние 14 дней",
|
||||
"event": "событие",
|
||||
"events": "событий",
|
||||
"noChartData": "Нет данных о событиях"
|
||||
"noChartData": "Нет данных о событиях",
|
||||
"live": "В эфире",
|
||||
"attention": "Внимание",
|
||||
"heroPrefix": "Сегодня",
|
||||
"heroEmphasis": "всё",
|
||||
"heroSuffix": "идёт по плану.",
|
||||
"heroSummary": "{providers} провайдеров на связи, {armed} из {total} трекеров активны, {throughput} событий доставлено в {targets} каналов за сутки.",
|
||||
"throughput24h": "пропускная способность · 24ч",
|
||||
"eventsShort": "событий",
|
||||
"armedShort": "активны",
|
||||
"providersShort": "провайдеров",
|
||||
"targetsShort": "каналов",
|
||||
"trackersShort": "трекеров",
|
||||
"streamTitle": "Поток",
|
||||
"streamEmphasis": "сигналов",
|
||||
"eventsLabel": "событий",
|
||||
"onWatchTitle": "На",
|
||||
"onWatchEmphasis": "слежении",
|
||||
"noProviders": "Пока нет провайдеров.",
|
||||
"addProvider": "Добавить",
|
||||
"addProviderHint": "Подключите сервис, чтобы начать слежение",
|
||||
"pulseTitle": "Пульс",
|
||||
"pulseEmphasis": "· 14 дней",
|
||||
"pulseSub": "События по дням",
|
||||
"wiresTitle": "Активные",
|
||||
"wiresEmphasis": "линии",
|
||||
"wiresSub": "маршрутов",
|
||||
"composeTitle": "Выберите источник, выберите канал.",
|
||||
"composeEmphasis": "Свяжите.",
|
||||
"composeSub": "Проведите путь от провайдера → трекер → шаблон → цель. Или вставьте webhook URL — остальное мы определим сами.",
|
||||
"viewTrackers": "К трекерам",
|
||||
"newTracker": "Новый трекер",
|
||||
"eventsTotal": "Событий"
|
||||
},
|
||||
"providers": {
|
||||
"title": "Провайдеры",
|
||||
|
||||
Reference in New Issue
Block a user