diff --git a/dashboard/src/App.tsx b/dashboard/src/App.tsx index 7639b972..3c35f2ec 100644 --- a/dashboard/src/App.tsx +++ b/dashboard/src/App.tsx @@ -1,3 +1,4 @@ +import { useState, useRef, useEffect } from 'react'; /** * App.tsx * @@ -27,6 +28,106 @@ import { useTheme } from './hooks/useTheme'; import { DeliveryHeatmap } from './components/DeliveryHeatmap'; import { useEventStore } from './store/eventStore'; +type Tab = + | 'explorer' + | 'timeline' + | 'activity' + | 'webhooks' + | 'export-history' + | 'search' + | 'preferences' + | 'templates'; + +const TAB_ITEMS: { id: Tab; label: string }[] = [ + { id: 'explorer', label: 'Event Explorer' }, + { id: 'timeline', label: 'Delivery Timeline' }, + { id: 'activity', label: 'Activity Feed' }, + { id: 'webhooks', label: 'Webhook Performance' }, + { id: 'export-history', label: 'Export History' }, + { id: 'search', label: 'Notification Search' }, + { id: 'preferences', label: 'Preferences' }, + { id: 'templates', label: 'Templates' }, +]; + +export function App() { + const [tab, setTab] = useState('explorer'); + const [menuOpen, setMenuOpen] = useState(false); + const { theme, toggleTheme } = useTheme(); + const events = useEventStore((state) => state.events); + const menuRef = useRef(null); + + useEffect(() => { + function handleClickOutside(event: MouseEvent) { + if (menuRef.current && !menuRef.current.contains(event.target as Node)) { + setMenuOpen(false); + } + } + + function handleEscape(event: KeyboardEvent) { + if (event.key === 'Escape') { + setMenuOpen(false); + } + } + + if (menuOpen) { + document.addEventListener('mousedown', handleClickOutside); + document.addEventListener('keydown', handleEscape); + } + + return () => { + document.removeEventListener('mousedown', handleClickOutside); + document.removeEventListener('keydown', handleEscape); + }; + }, [menuOpen]); + + const handleTabChange = (newTab: Tab) => { + setTab(newTab); + setMenuOpen(false); + }; + + return ( +
+
+
+

NotifyChain

+

Dashboard

+
+
+ +
+ + +
+
+
export function App() { const [tab, setTab] = useState('explorer'); const [drawerOpen, setDrawerOpen] = useState(false); diff --git a/dashboard/src/components/ExportHistorySkeleton.tsx b/dashboard/src/components/ExportHistorySkeleton.tsx new file mode 100644 index 00000000..73933aae --- /dev/null +++ b/dashboard/src/components/ExportHistorySkeleton.tsx @@ -0,0 +1,65 @@ +cliinterface ExportHistorySkeletonProps { + rows?: number; +} + +function SkeletonCell({ width }: { width: string }) { + return ( +

- {totalCount.toLocaleString()} {totalCount === 1 ? 'record' : 'records'} + {isLoading ? '—' : `${totalCount.toLocaleString()} ${totalCount === 1 ? 'record' : 'records'}`}

+ {/* ── Skeleton (loading) ───────────────────────────────────── */} + {isLoading && } + {/* ── Table or empty state ─────────────────────────────────── */} - {displayedExports.length > 0 ? ( + {!isLoading && displayedExports.length > 0 && ( + )} + + {!isLoading && displayedExports.length === 0 && ( +
+

No export records found

+

+ Try modifying your search query or status filter to locate matching exports. +

+
) : ( + {!isLoading && ( + + )} ); } diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx index 7122a38e..c5bb65fa 100644 --- a/frontend/app/layout.tsx +++ b/frontend/app/layout.tsx @@ -4,6 +4,7 @@ import "./globals.css"; export const metadata: Metadata = { title: "NotifyChain - Analytics Dashboard", description: "Actionable insights into notification performance", + viewport: "width=device-width, initial-scale=1", }; export default function RootLayout({ diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index f08f8dc7..6f880d94 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -2,13 +2,13 @@ import Link from "next/link"; export default function Home() { return ( -
-
-

NotifyChain

-

Your analytics dashboard is ready!

+
+
+

NotifyChain

+

Your analytics dashboard is ready!

View Analytics Dashboard diff --git a/index.html b/index.html new file mode 100644 index 00000000..67943781 --- /dev/null +++ b/index.html @@ -0,0 +1,531 @@ + + + + + +Kairos Elite — Dashboard + + + + + + + +
+
+ + + + + +
+
+ +

Dashboard

+
+ + +
+ + + +
+ + +
+
+

Welcome back

+

Here's what's happening across your projects today.

+
+ +
+
+

Active projects

+

18

+ ▲ 3 this month +
+
+

Models in production

+

7

+ ▲ 1 this week +
+
+

Uptime, 30 days

+

99.92%

+ ▼ 0.03% +
+
+

Open tickets

+

5

+ ▲ 2 today +
+
+ +
+
+

Recent activity

+
    +
  • +
    +

    Deployment succeeded — Atlas NLP v2.3

    Cloud DevOps · Production
    + 2m ago +
  • +
  • +
    +

    New training run started — Sahel vision model

    AI / ML pipeline
    + 41m ago +
  • +
  • +
    +

    Design review approved — Client portal UI

    UI / UX design
    + 3h ago +
  • +
  • +
    +

    Smart contract audit completed — Ledger Vault

    Blockchain
    + Yesterday +
  • +
+
+ +
+

System status

+
API gatewayOperational
+
Inference clusterOperational
+
Data pipeline (Lagos)Degraded
+
Notification serviceOperational
+
BillingIncident
+
+
+
+ + +
+
+

Notifications

+

Updates from your projects, deployments, and team.

+
+ +
+ + + + +
+ +
+

Today

+
    +
  • +
    +

    Billing incident detected

    Payment webhook failing for 3 client accounts. Investigating now.
    + 9:41 AM +
  • +
  • +
    +

    Atlas NLP v2.3 deployed to production

    Rollout completed across all regions with zero downtime.
    + 8:15 AM +
  • +
+
+ +
+

Yesterday

+
    +
  • +
    +

    Data pipeline latency above threshold

    Lagos region ingest is running 40% slower than baseline.
    + 6:02 PM +
  • +
  • +
    +

    New comment on Client portal UI

    Amara left feedback on the onboarding flow mockups.
    + 2:47 PM +
  • +
  • +
    +

    Ledger Vault audit report ready

    Smart contract audit finished with no critical findings.
    + 11:20 AM +
  • +
+
+
+ +
+
+
+ + + + \ No newline at end of file