From d4c2dd965d015ffaf6dcbbb5b346622c3795fde5 Mon Sep 17 00:00:00 2001 From: Saman Pandey Date: Sun, 3 May 2026 13:22:15 +0530 Subject: [PATCH 1/3] style: added new skeletons, improved typography, improved UI/UX states --- dashboard/app/analytics/page.js | 42 +++++++------- dashboard/app/components/AgentPanel.jsx | 6 +- dashboard/app/components/NavBar.jsx | 14 ++++- dashboard/app/components/ui/EmptyState.jsx | 27 +++++++++ dashboard/app/components/ui/GlobeSkeleton.jsx | 26 +++++++++ dashboard/app/components/ui/PanelSkeleton.jsx | 25 +++++++++ dashboard/app/components/ui/Skeleton.jsx | 17 ++++++ dashboard/app/developers/page.js | 18 +++--- dashboard/app/globals.css | 44 +++++++++++++++ dashboard/app/page.js | 26 +++++---- dashboard/app/shipments/page.js | 56 ++++++++----------- dashboard/app/visualize/page.js | 27 +++++---- 12 files changed, 240 insertions(+), 88 deletions(-) create mode 100644 dashboard/app/components/ui/EmptyState.jsx create mode 100644 dashboard/app/components/ui/GlobeSkeleton.jsx create mode 100644 dashboard/app/components/ui/PanelSkeleton.jsx create mode 100644 dashboard/app/components/ui/Skeleton.jsx diff --git a/dashboard/app/analytics/page.js b/dashboard/app/analytics/page.js index 36ffeba..7cac13c 100644 --- a/dashboard/app/analytics/page.js +++ b/dashboard/app/analytics/page.js @@ -15,7 +15,9 @@ import { YAxis, } from 'recharts'; import { motion } from 'framer-motion'; -import { BarChart3 } from 'lucide-react'; +import { BarChart3, ChevronUp, ChevronDown } from 'lucide-react'; +import { Skeleton } from '../components/ui/Skeleton'; +import { EmptyState } from '../components/ui/EmptyState'; import NavBar from '../components/NavBar.jsx'; import { useTheme } from '../providers/ThemeProvider.jsx'; import { PAGE_ENTER, STAGGER_CHILDREN, CARD_ITEM } from '../lib/motion.js'; @@ -28,15 +30,15 @@ function KpiCard({ label, value, sub, trend, accentColor = 'var(--accent-cyan)' variants={CARD_ITEM} className="glass-panel glass-edge relative overflow-hidden p-6 group" > -

{label}

+

{label}

{value}

-

{sub}

+

{sub}

{trend !== undefined && ( -
0 ? 'text-[var(--accent-green)]' : 'text-[var(--accent-red)]'}`}> - {trend > 0 ? '▲' : '▼'} +
0 ? 'text-[var(--accent-green)]' : 'text-[var(--accent-red)]'}`}> + {trend > 0 ? : } {Math.abs(trend)}%
)} @@ -57,11 +59,11 @@ function ChartPanel({ title, subtitle, children }) { className="glass-panel p-6 space-y-6" >
-

+

{title}

{subtitle && ( -

{subtitle}

+

{subtitle}

)}
@@ -115,16 +117,16 @@ export default function AnalyticsPage() {
-
+
{[0, 1, 2, 3].map((i) => ( -
+ ))}
-
+
-
-
+ +
@@ -164,19 +166,17 @@ export default function AnalyticsPage() {
-

Operations Intelligence

+

Operations Intelligence

30-Day Performance

{(!data || Object.keys(data).length === 0) ? ( -
-
- -
-

No Intelligence Data

-

- Historical performance metrics are calculated every 24 hours. Check back once your first disruption protocol has been executed. -

+
+
) : ( <> diff --git a/dashboard/app/components/AgentPanel.jsx b/dashboard/app/components/AgentPanel.jsx index 54a5e56..5417ef0 100644 --- a/dashboard/app/components/AgentPanel.jsx +++ b/dashboard/app/components/AgentPanel.jsx @@ -4,14 +4,16 @@ import { useEffect, useRef } from 'react'; import dynamic from 'next/dynamic'; import { AnimatePresence, motion } from 'framer-motion'; +import { PanelSkeleton } from './ui/PanelSkeleton'; + const AgentChatSidebar = dynamic(() => import('./agent/AgentChatSidebar.jsx'), { ssr: false, - loading: () =>
Loading agent reasoning...
, + loading: () => , }); const NewsFeed = dynamic(() => import('./news/NewsFeed.jsx'), { ssr: false, - loading: () =>
Loading news intelligence...
, + loading: () => , }); const TABS = [ diff --git a/dashboard/app/components/NavBar.jsx b/dashboard/app/components/NavBar.jsx index 1e8c2e3..394fbf2 100644 --- a/dashboard/app/components/NavBar.jsx +++ b/dashboard/app/components/NavBar.jsx @@ -84,18 +84,26 @@ export default function NavBar() { : pathname.startsWith(href); const showDivider = idx > 0 && NAV_ITEMS[idx - 1].section !== section; + const sectionLabel = showDivider ? (NAV_ITEMS[idx].section === 'live' ? 'Monitor' : 'Intel') : null; return ( -
+
{showDivider && ( -
+
+
+ {sectionLabel && ( + + {sectionLabel} + + )} +
)} +
+ {icon} +
+
+

+ {title} +

+

+ {description} +

+
+ {action && ( +
+ {action} +
+ )} +
+ ); +} diff --git a/dashboard/app/components/ui/GlobeSkeleton.jsx b/dashboard/app/components/ui/GlobeSkeleton.jsx new file mode 100644 index 0000000..d6e7bf5 --- /dev/null +++ b/dashboard/app/components/ui/GlobeSkeleton.jsx @@ -0,0 +1,26 @@ +import { Skeleton } from './Skeleton'; + +export function GlobeSkeleton() { + return ( +
+
+
+
+
+
+
+
+
+
+ Initializing Orbital Command +
+
+
+
+
+
+
+
+
+ ); +} diff --git a/dashboard/app/components/ui/PanelSkeleton.jsx b/dashboard/app/components/ui/PanelSkeleton.jsx new file mode 100644 index 0000000..216ab68 --- /dev/null +++ b/dashboard/app/components/ui/PanelSkeleton.jsx @@ -0,0 +1,25 @@ +import { Skeleton } from './Skeleton'; + +export function PanelSkeleton() { + return ( +
+
+ +
+ + +
+
+
+ + + + +
+
+ + +
+
+ ); +} diff --git a/dashboard/app/components/ui/Skeleton.jsx b/dashboard/app/components/ui/Skeleton.jsx new file mode 100644 index 0000000..32c91fb --- /dev/null +++ b/dashboard/app/components/ui/Skeleton.jsx @@ -0,0 +1,17 @@ +import clsx from 'clsx'; + +export function Skeleton({ className, variant = 'line', ...props }) { + return ( +
+ ); +} diff --git a/dashboard/app/developers/page.js b/dashboard/app/developers/page.js index 56ddb11..efd10e9 100644 --- a/dashboard/app/developers/page.js +++ b/dashboard/app/developers/page.js @@ -1,7 +1,7 @@ 'use client'; import { useMemo, useState } from 'react'; -import { Copy, Check, Terminal, Zap, Key, Webhook, Package, AlertTriangle } from 'lucide-react'; +import { Copy, Check, Terminal, Zap, Key, Webhook, Package, AlertTriangle, ChevronUp, ChevronDown } from 'lucide-react'; import NavBar from '../components/NavBar.jsx'; const FALLBACK_BASE = 'https://your-deployed-url.vercel.app'; @@ -51,23 +51,25 @@ function EndpointCard({ method, path, description, example, response }) { onClick={() => setOpen((value) => !value)} className="w-full flex items-center gap-4 p-4 bg-[var(--bg-elevated)]/40 hover:bg-[var(--bg-elevated)] transition-colors text-left" > - {method} - {path} - {description} - {open ? '▲' : '▼'} + {method} + {path} + {description} + + {open ? : } + {open && (
-

{description}

+

{description}

{example && (
-

curl example

+

curl example

)} {response && (
-

response shape

+

response shape

)} diff --git a/dashboard/app/globals.css b/dashboard/app/globals.css index 1515af6..279e1cb 100644 --- a/dashboard/app/globals.css +++ b/dashboard/app/globals.css @@ -46,6 +46,14 @@ /* ── Scrollbar ── */ --scrollbar-thumb: rgba(255, 255, 255, 0.12); --scrollbar-track: transparent; + + /* ── Typography floor ── */ + --text-xs: 11px; + --text-sm: 13px; + --text-base: 14px; + --text-md: 15px; + --tracking-wide: 0.12em; + --tracking-wider: 0.18em; } [data-theme="dark"] { @@ -91,6 +99,14 @@ /* ── Scrollbar ── */ --scrollbar-thumb: rgba(255, 255, 255, 0.12); --scrollbar-track: transparent; + + /* ── Typography floor ── */ + --text-xs: 11px; + --text-sm: 13px; + --text-base: 14px; + --text-md: 15px; + --tracking-wide: 0.12em; + --tracking-wider: 0.18em; } /* ── Light theme ── */ @@ -137,6 +153,14 @@ /* ── Scrollbar ── */ --scrollbar-thumb: rgba(15, 23, 42, 0.12); --scrollbar-track: transparent; + + /* ── Typography floor ── */ + --text-xs: 11px; + --text-sm: 13px; + --text-base: 14px; + --text-md: 15px; + --tracking-wide: 0.12em; + --tracking-wider: 0.18em; } /* ── Globe page always stays dark regardless of theme ── */ @@ -181,6 +205,14 @@ /* ── Scrollbar ── */ --scrollbar-thumb: rgba(255,255,255,0.15); --scrollbar-track: transparent; + + /* ── Typography floor ── */ + --text-xs: 11px; + --text-sm: 13px; + --text-base: 14px; + --text-md: 15px; + --tracking-wide: 0.12em; + --tracking-wider: 0.18em; } @theme inline { @@ -394,3 +426,15 @@ body { opacity: 1; pointer-events: auto; } + +/* ── Shimmer Animation for Skeletons ── */ +@keyframes shimmer { + 0% { background-position: 200% 0; } + 100% { background-position: -200% 0; } +} + +.animate-shimmer { + background: linear-gradient(90deg, transparent 25%, rgba(255,255,255,0.05) 50%, transparent 75%); + background-size: 200% 100%; + animation: shimmer 2s infinite linear; +} diff --git a/dashboard/app/page.js b/dashboard/app/page.js index 851bebc..2f41081 100644 --- a/dashboard/app/page.js +++ b/dashboard/app/page.js @@ -13,6 +13,8 @@ import MinimalErrorFallback from './components/MinimalErrorFallback.jsx'; import { registerPushSubscription } from './lib/pushNotifications.js'; import { useShipmentStore } from './store/shipmentStore.js'; +import { GlobeSkeleton } from './components/ui/GlobeSkeleton'; + const AlertToastController = dynamic(() => import('./components/alerts/AlertToast.jsx'), { ssr: false, loading: () => null, @@ -31,11 +33,11 @@ const GlobeActivationToggle = dynamic(() => import('./components/globe/GlobeActi }); const GlobeView = dynamic(() => import('./components/globe/GlobeView.jsx'), { ssr: false, - loading: () =>
Loading globe...
, + loading: () => , }); const MobileView = dynamic(() => import('./components/globe/MobileView.jsx'), { ssr: false, - loading: () =>
Loading mobile view...
, + loading: () => , }); const STATUS_FILTERS = [ @@ -89,10 +91,10 @@ function ShipmentFilters({ onFilterChange }) { className={`w-full group flex flex-col items-start gap-1 p-2 rounded-lg transition-all relative ${active ? 'bg-white/5' : 'hover:bg-white/[0.03]'}`} >
- - {f.label} [{f.key}] + + {f.label} [{f.key}] - {count} + {count}
{f.id !== 'all' && ( @@ -228,7 +230,7 @@ export default function Home() {
-
Pipeline Impact
+
Pipeline Impact
Cargo under protection: ${(cargoUnderProtectionUSD / 1e6).toFixed(1)}M
@@ -249,7 +251,7 @@ export default function Home() {
-

Operational Filter

+

Operational Filter

@@ -270,7 +272,7 @@ export default function Home() { {shouldLoadGlobe ? ( ) : ( -
Loading globe...
+ )}
@@ -283,7 +285,7 @@ export default function Home() {
-

Standby Mode

+

Standby Mode

Globe paused · Tab inactive

@@ -295,7 +297,7 @@ export default function Home() { @@ -305,7 +307,7 @@ export default function Home() {
-
- ); -} function ShipmentsPageSkeleton() { return (
-
+
{[0, 1, 2, 3, 4, 5].map((i) => ( -
+ ))}
-
-
+ +
-
+
); diff --git a/dashboard/app/visualize/page.js b/dashboard/app/visualize/page.js index 68789ff..6d0a848 100644 --- a/dashboard/app/visualize/page.js +++ b/dashboard/app/visualize/page.js @@ -11,6 +11,7 @@ import { ShieldCheck, TerminalSquare, } from 'lucide-react'; +import { Skeleton } from '../components/ui/Skeleton'; import NavBar from '../components/NavBar.jsx'; const AGENT_METRICS = [ @@ -494,7 +495,7 @@ export default function VisualizePage() {
-

AI Agent Pipeline

+

AI Agent Pipeline

System Logic Visualization

Real-time view of monitor → impact → resolution → execute stages. Monitor the live reasoning and automated decision flow across global shipping corridors. @@ -504,7 +505,7 @@ export default function VisualizePage() {

-
Pipeline Flow
+
Pipeline Flow
End-to-End Status
@@ -541,7 +542,7 @@ export default function VisualizePage() {
-
Live Reasoning
+
Live Reasoning
Trace {selectedTraceId ? `#${selectedTraceId.slice(-8)}` : '--'}
@@ -563,9 +564,14 @@ export default function VisualizePage() {
{traceLoading && !traceDetails ? ( -
- - Loading trace... +
+ +
+ + + + +
) : traceDetails ? (
@@ -650,7 +656,7 @@ export default function VisualizePage() {
-
Disruption Timeline
+
Disruption Timeline
Last {timeline.length} traces
{selectedTimelineItem ? ( @@ -661,9 +667,10 @@ export default function VisualizePage() {
{timelineLoading ? ( -
- - Loading timeline... +
+ {[0, 1, 2, 3, 4].map((i) => ( + + ))}
) : timeline.length ? (
From 35c41cb2d33b7dc8481d87ec2ea9087ea895742d Mon Sep 17 00:00:00 2001 From: Saman Pandey Date: Sun, 3 May 2026 13:50:13 +0530 Subject: [PATCH 2/3] style: removed emojis and made UI professional --- dashboard/app/demo/page.js | 134 ++++++++++++------ dashboard/app/layout.js | 4 +- dashboard/app/login/page.js | 12 +- dashboard/app/onboarding/page.js | 48 +++---- .../app/shipments/components/ShipmentsTab.jsx | 8 +- dashboard/app/shipments/page.js | 5 +- dashboard/app/visualize/page.js | 22 +-- 7 files changed, 141 insertions(+), 92 deletions(-) diff --git a/dashboard/app/demo/page.js b/dashboard/app/demo/page.js index 82781d9..156f711 100644 --- a/dashboard/app/demo/page.js +++ b/dashboard/app/demo/page.js @@ -12,6 +12,24 @@ import { } from 'firebase/firestore'; import { db, isFirebaseConfigured } from '../lib/firebase.js'; import { watchDisruptionSupabase, watchImpactSupabase, watchResolutionSupabase } from '../lib/supabaseWatcher.js'; +import { + Wind, + Anchor, + Construction, + Circle, + Zap, + Radio, + BarChart3, + Bot, + Scale, + CheckCircle2, + FileText, + AlertTriangle, + ChevronRight, + Download, + RotateCcw, + X +} from 'lucide-react'; import NavBar from '../components/NavBar.jsx'; const SCENARIOS = [ @@ -20,7 +38,7 @@ const SCENARIOS = [ label: 'Super Typhoon Mawar', region: 'Western Pacific', severity: 9, - icon: '🌀', + icon: Wind, tag: 'CAT 5 STORM', tagColor: 'var(--accent-red)', preview: 'Shanghai → LA corridor at risk. Manila, Kaohsiung, HK vessel advisories active.', @@ -30,7 +48,7 @@ const SCENARIOS = [ label: 'Suez Canal Emergency', region: 'Red Sea / Egypt', severity: 10, - icon: '⚓', + icon: Anchor, tag: 'CANAL BLOCKED', tagColor: 'var(--accent-amber)', preview: "$12B daily trade halted. 43 vessels held. Lloyd's suspended war-risk coverage.", @@ -40,7 +58,7 @@ const SCENARIOS = [ label: 'Mumbai JNPT Strike', region: 'South Asia', severity: 7, - icon: '🏗', + icon: Construction, tag: 'PORT STRIKE', tagColor: 'var(--accent-blue)', preview: '4,800 dockworkers AWOL. 5M TEU/yr port offline. Mundra at 85% capacity.', @@ -48,14 +66,14 @@ const SCENARIOS = [ ]; const STAGES = [ - { id: 'idle', label: 'Ready', icon: '◎', color: 'var(--text-muted)' }, - { id: 'injected', label: 'Disruption Injected',icon: '⚡', color: 'var(--accent-amber)' }, - { id: 'monitoring',label: 'Monitor Agent', icon: '📡', color: 'var(--accent-blue)' }, - { id: 'impact', label: 'Impact Analysis', icon: '📊', color: 'var(--accent-red)' }, - { id: 'resolution',label: 'AI Resolution', icon: '🤖', color: 'var(--accent-cyan)' }, - { id: 'decision', label: 'Human Decision', icon: '⚖️', color: 'var(--accent-amber)' }, - { id: 'applied', label: 'Protocol Applied', icon: '✓', color: 'var(--accent-green)' }, - { id: 'report', label: 'Report Ready', icon: '📄', color: 'var(--accent-cyan)' }, + { id: 'idle', label: 'Ready', icon: Circle, color: 'var(--text-muted)' }, + { id: 'injected', label: 'Disruption Injected',icon: Zap, color: 'var(--accent-amber)' }, + { id: 'monitoring',label: 'Monitor Agent', icon: Radio, color: 'var(--accent-blue)' }, + { id: 'impact', label: 'Impact Analysis', icon: BarChart3, color: 'var(--accent-red)' }, + { id: 'resolution',label: 'AI Resolution', icon: Bot, color: 'var(--accent-cyan)' }, + { id: 'decision', label: 'Human Decision', icon: Scale, color: 'var(--accent-amber)' }, + { id: 'applied', label: 'Protocol Applied', icon: CheckCircle2, color: 'var(--accent-green)' }, + { id: 'report', label: 'Report Ready', icon: FileText, color: 'var(--accent-cyan)' }, ]; const STAGE_INDEX = Object.fromEntries(STAGES.map((s, i) => [s.id, i])); @@ -114,11 +132,14 @@ function StageNode({ stage, index, currentIndex }) { flexShrink: 0, }} > - {done ? '✓' : stage.icon} + {(() => { + const Icon = done ? CheckCircle2 : stage.icon; + return ; + })()}
- + {label} {value} @@ -266,7 +287,7 @@ function OptionCardInline({ option, onApprove, isApproving, approvedRank }) {
-
+
AI Confidence
@@ -342,7 +363,7 @@ function OptionCardInline({ option, onApprove, isApproving, approvedRank }) { textTransform: 'uppercase', }} > - Protocol Deployed + Protocol Deployed
) : (
)} @@ -761,10 +782,15 @@ export default function DemoPage() { : 'none', }} > -
{sc.icon}
+
+ {(() => { + const Icon = sc.icon; + return ; + })()} +
{sc.preview}
@@ -817,7 +843,15 @@ export default function DemoPage() { boxShadow: selectedScenario ? '0 0 24px rgba(34,211,238,0.3)' : 'none', }} > - {launching ? '⚡ Injecting…' : '⚡ Launch Demo Pipeline'} + {launching ? ( + + Injecting… + + ) : ( + + Launch Demo Pipeline + + )}
)} @@ -886,7 +920,7 @@ export default function DemoPage() {
{disruption.severity != null && (
-
+
Severity Score
@@ -976,9 +1010,9 @@ export default function DemoPage() { }} > {stage === 'report' - ? '✓ Protocol Applied — Report Ready' + ? Protocol Applied — Report Ready : stage === 'applied' - ? '⚡ Executing Protocol…' + ? Executing Protocol… : stage === 'decision' ? 'Human Decision Required' : 'AI Generated 3 Resolution Strategies'} @@ -1075,7 +1109,9 @@ export default function DemoPage() { marginBottom: 4, }} > - ✓ Pipeline Complete + + Pipeline Complete +
Executive incident report generated. Download the PDF or run another scenario. @@ -1098,7 +1134,9 @@ export default function DemoPage() { cursor: 'pointer', }} > - 📄 Download PDF + + Download PDF + )}
@@ -1139,7 +1179,7 @@ export default function DemoPage() { >
{activeScenario && (
-
Scenario
-
- {activeScenario.icon} {activeScenario.label} +
Scenario
+
+ {(() => { + const Icon = activeScenario.icon; + return ; + })()} {activeScenario.label}
)} {disruptionId && (
-
Disruption ID
-
+
Disruption ID
+
{disruptionId}
)} {traceId && (
-
Trace ID
-
+
Trace ID
+
{traceId}
)}
-
Stage
-
- {STAGES[currentStageIndex]?.icon} {STAGES[currentStageIndex]?.label} +
Stage
+
+ {(() => { + const Icon = STAGES[currentStageIndex]?.icon; + return Icon ? : null; + })()} {STAGES[currentStageIndex]?.label}
@@ -1203,7 +1249,7 @@ export default function DemoPage() { gap: 8, }} > - + Agent Console {stage !== 'idle' && stage !== 'report' && } @@ -1238,14 +1284,16 @@ export default function DemoPage() { background: 'var(--bg-elevated)', color: 'var(--text-muted)', fontWeight: 700, - fontSize: 10, + fontSize: 11, letterSpacing: '0.1em', textTransform: 'uppercase', border: '1px solid var(--border-subtle)', cursor: 'pointer', }} > - ✕ Abort & Reset + + Abort & Reset + )}
diff --git a/dashboard/app/layout.js b/dashboard/app/layout.js index e86960f..13d99fc 100644 --- a/dashboard/app/layout.js +++ b/dashboard/app/layout.js @@ -38,10 +38,8 @@ export const viewport = { export default function RootLayout({ children }) { return ( - -