From af7c4e94761f433b27843839f47fcd0e79e05bf7 Mon Sep 17 00:00:00 2001 From: zaragoza444 Date: Fri, 3 Jul 2026 02:15:55 -0700 Subject: [PATCH] chore(frontend): add dashboard styles and ecosystem hub modules for production build Co-authored-by: Cursor --- .../src/components/layout/MobileBottomNav.tsx | 43 ++ .../src/components/layout/MobileNavDrawer.tsx | 80 +++ .../src/config/ecosystemLinkRouting.ts | 16 + frontend-dapp/src/config/ecosystemLinks.ts | 71 +++ frontend-dapp/src/config/onexShiva.ts | 24 + .../src/features/hub/DashboardSectionFold.tsx | 29 + .../src/features/hub/EcosystemLinkGrid.tsx | 128 +++++ .../src/features/hub/FeaturedAppsStrip.tsx | 56 ++ .../office24/Office24BankCorridors.tsx | 116 ++++ .../office24/Office24EcosystemSubjects.tsx | 65 +++ frontend-dapp/src/styles/app-dashboard.css | 498 ++++++++++++++++++ frontend-dapp/src/styles/dbis-dashboard.css | 369 +++++++++++++ frontend-dapp/src/styles/dbis-green-ui.css | 13 + .../token-aggregation/public/omnl-api-base.js | 8 + 14 files changed, 1516 insertions(+) create mode 100644 frontend-dapp/src/components/layout/MobileBottomNav.tsx create mode 100644 frontend-dapp/src/components/layout/MobileNavDrawer.tsx create mode 100644 frontend-dapp/src/config/ecosystemLinkRouting.ts create mode 100644 frontend-dapp/src/config/ecosystemLinks.ts create mode 100644 frontend-dapp/src/config/onexShiva.ts create mode 100644 frontend-dapp/src/features/hub/DashboardSectionFold.tsx create mode 100644 frontend-dapp/src/features/hub/EcosystemLinkGrid.tsx create mode 100644 frontend-dapp/src/features/hub/FeaturedAppsStrip.tsx create mode 100644 frontend-dapp/src/features/office24/Office24BankCorridors.tsx create mode 100644 frontend-dapp/src/features/office24/Office24EcosystemSubjects.tsx create mode 100644 frontend-dapp/src/styles/app-dashboard.css create mode 100644 frontend-dapp/src/styles/dbis-dashboard.css create mode 100644 frontend-dapp/src/styles/dbis-green-ui.css create mode 100644 services/token-aggregation/public/omnl-api-base.js diff --git a/frontend-dapp/src/components/layout/MobileBottomNav.tsx b/frontend-dapp/src/components/layout/MobileBottomNav.tsx new file mode 100644 index 0000000..1c43fcd --- /dev/null +++ b/frontend-dapp/src/components/layout/MobileBottomNav.tsx @@ -0,0 +1,43 @@ +import { Link, useLocation } from 'react-router-dom'; +import NasaIcon from '../icons/NasaIcon'; + +type Props = { + onOpenMenu: () => void; +}; + +const TABS = [ + { to: '/hub', label: 'Home', icon: 'dashboard' as const }, + { to: '/bridge', label: 'Bridge', icon: 'bridge' as const }, + { to: '/trade', label: 'Trade', icon: 'trade' as const }, + { to: '/central-bank', label: 'Z Bank', icon: 'central-bank' as const }, +]; + +export default function MobileBottomNav({ onOpenMenu }: Props) { + const location = useLocation(); + + const isActive = (path: string) => { + if (path === '/hub') return location.pathname === '/hub' || location.pathname === '/'; + if (path === '/bridge') return location.pathname === '/bridge' || location.pathname.startsWith('/bridge'); + if (path === '/') return location.pathname === '/'; + return location.pathname.startsWith(path); + }; + + return ( + + ); +} diff --git a/frontend-dapp/src/components/layout/MobileNavDrawer.tsx b/frontend-dapp/src/components/layout/MobileNavDrawer.tsx new file mode 100644 index 0000000..ce789ad --- /dev/null +++ b/frontend-dapp/src/components/layout/MobileNavDrawer.tsx @@ -0,0 +1,80 @@ +import { useEffect } from 'react'; +import { Link } from 'react-router-dom'; + +export type MobileNavItem = { + to?: string; + href?: string; + label: string; + active?: boolean; + onClick?: () => void; +}; + +type Props = { + open: boolean; + onClose: () => void; + title: string; + items: MobileNavItem[]; +}; + +export default function MobileNavDrawer({ open, onClose, title, items }: Props) { + useEffect(() => { + if (!open) return; + const prev = document.body.style.overflow; + document.body.style.overflow = 'hidden'; + return () => { + document.body.style.overflow = prev; + }; + }, [open]); + + if (!open) return null; + + return ( +
+ +
+ + + + ); +} diff --git a/frontend-dapp/src/config/ecosystemLinkRouting.ts b/frontend-dapp/src/config/ecosystemLinkRouting.ts new file mode 100644 index 0000000..208a2c1 --- /dev/null +++ b/frontend-dapp/src/config/ecosystemLinkRouting.ts @@ -0,0 +1,16 @@ +import type { EcosystemLink } from './ecosystemLinks'; + +/** Paths served outside the React SPA (static HTML or proxied APIs). */ +export function isStaticPortalPath(href: string): boolean { + return ( + href.startsWith('/omnl') || + href.startsWith('/api') || + href.startsWith('/settlement') || + href.startsWith('/exchange') || + href.startsWith('/reserve/institutional') + ); +} + +export function ecosystemLinkUsesAnchor(link: EcosystemLink): boolean { + return Boolean(link.external) || link.href.startsWith('http') || isStaticPortalPath(link.href); +} diff --git a/frontend-dapp/src/config/ecosystemLinks.ts b/frontend-dapp/src/config/ecosystemLinks.ts new file mode 100644 index 0000000..579a726 --- /dev/null +++ b/frontend-dapp/src/config/ecosystemLinks.ts @@ -0,0 +1,71 @@ +import type { NasaIconName } from '../components/icons/NasaIcon'; +import { defaultFrontendExplorerUrl } from './networks'; + +export type EcosystemLinkAccent = 'green' | 'teal' | 'blue' | 'gold' | 'purple' | 'slate'; + +export type EcosystemLink = { + id: string; + label: string; + description: string; + icon: NasaIconName; + href: string; + external?: boolean; + accent?: EcosystemLinkAccent; +}; + +/** In-app React routes (same origin) */ +export const IN_APP_LINKS: EcosystemLink[] = [ + { id: 'bridge', label: 'Bridge', description: 'Cross-chain bridge, CCIP, XRPL, trustless', icon: 'bridge', href: '/bridge', accent: 'teal' }, + { id: 'swap', label: 'Digital Swap', description: 'M2 load, token swap, settlement rails', icon: 'swap', href: '/swap', accent: 'blue' }, + { id: 'trade', label: 'DBIS Trade', description: 'Markets, order book, spot trading', icon: 'trade', href: '/trade', accent: 'gold' }, + { id: 'central-bank', label: 'Z Online Bank', description: 'Zardasht banking · balances & payments', icon: 'central-bank', href: '/central-bank', accent: 'green' }, + { id: 'z-full-settlement', label: 'Z M0-M4 settlement', description: 'Full money-supply settlement panel', icon: 'money', href: '/central-bank#z-full-settlement', accent: 'green' }, + { id: 'wire-protocols', label: 'Wire protocols', description: 'MT102, MT103 LC, SBLC, BG settlement', icon: 'terminal', href: '/central-bank#z-settlement-protocols', accent: 'gold' }, + { id: 'office-24', label: 'Office 24 Central Bank', description: '128-bank corridors · IPSAS settlement', icon: 'office', href: '/office-24', accent: 'purple' }, + { id: 'reserve', label: 'Reserve', description: 'Coverage, attestation, peg status', icon: 'money', href: '/reserve', accent: 'green' }, + { id: 'history', label: 'History', description: 'Bridge transfer lookup & tracking', icon: 'ledger', href: '/history', accent: 'slate' }, + { id: 'wallets', label: 'Wallets', description: 'OneX Wallet · email, social, passkey on Shiva', icon: 'wallet', href: '/wallets', accent: 'blue' }, + { id: 'admin', label: 'Admin', description: 'Multisig, channels, bridge operators', icon: 'admin', href: '/admin', accent: 'slate' }, + { id: 'docs', label: 'Developers', description: 'API reference & integration', icon: 'code', href: '/docs', accent: 'teal' }, + { id: 'hub', label: 'DBIS Dashboard', description: 'All apps, consoles & API links', icon: 'dashboard', href: '/hub', accent: 'green' }, +]; + +/** Primary apps shown in dashboard quick-access strip */ +export const FEATURED_APP_LINKS: EcosystemLink[] = [ + IN_APP_LINKS.find((l) => l.id === 'central-bank')!, + IN_APP_LINKS.find((l) => l.id === 'bridge')!, + IN_APP_LINKS.find((l) => l.id === 'trade')!, + IN_APP_LINKS.find((l) => l.id === 'swap')!, + IN_APP_LINKS.find((l) => l.id === 'office-24')!, +]; + +/** Static operator consoles — served from /omnl/* (portal-serve or token-aggregation :3000) */ +export const OPERATOR_CONSOLE_LINKS: EcosystemLink[] = [ + { id: 'compliance', label: 'OMNL Compliance Console', description: 'Posture, pending actions, Safe tx', icon: 'shield', href: '/omnl/compliance', accent: 'purple' }, + { id: 'omnl-dash', label: 'OMNL API Snapshot', description: 'Registry and Fineract compare JSON', icon: 'chart', href: '/omnl/dashboard', accent: 'blue' }, + { id: 'reserve-dash', label: 'Reserve Dashboard', description: 'Proof rows, coverage, institutional view', icon: 'money', href: '/reserve', accent: 'green' }, + { id: 'settlement-term', label: 'Settlement Terminal', description: 'Operator settlement terminal UI', icon: 'terminal', href: '/omnl/terminal', accent: 'gold' }, + { id: 'token-admin', label: 'Token Aggregation API', description: 'API catalog JSON', icon: 'ledger', href: '/api/v1/omnl/catalog', accent: 'teal' }, +]; + +/** Public production portals */ +export const PUBLIC_PORTAL_LINKS: EcosystemLink[] = [ + { id: 'pub-bank', label: 'Z Online Bank', description: 'online.omdnl.org', icon: 'globe', href: 'https://online.omdnl.org/central-bank', external: true, accent: 'green' }, + { id: 'pub-cb', label: 'Central Bank', description: 'secure.omdnl.org', icon: 'central-bank', href: 'https://secure.omdnl.org/central-bank', external: true, accent: 'green' }, + { id: 'pub-o24', label: 'Office 24 Central Bank', description: 'office24.omdnl.org', icon: 'office', href: 'https://office24.omdnl.org/office-24', external: true, accent: 'purple' }, + { id: 'pub-trade', label: 'Exchange', description: 'exchange.omdnl.org', icon: 'trade', href: 'https://exchange.omdnl.org/trade', external: true, accent: 'gold' }, + { id: 'pub-forex', label: 'Forex', description: 'forex.omdnl.org', icon: 'trade', href: 'https://forex.omdnl.org/trade', external: true, accent: 'gold' }, + { id: 'pub-swap', label: 'Digital Swap', description: 'digital.omdnl.org', icon: 'swap', href: 'https://digital.omdnl.org/swap', external: true, accent: 'teal' }, + { id: 'explorer', label: 'Chain 138 Explorer', description: 'Blocks, contracts, transactions', icon: 'globe', href: defaultFrontendExplorerUrl, external: true, accent: 'slate' }, +]; + +/** API & docs endpoints — same-origin via portal-serve proxy on tunnel/local */ +export const API_LINKS: EcosystemLink[] = [ + { id: 'openapi', label: 'OpenAPI JSON', description: 'Machine-readable OMNL API spec', icon: 'code', href: '/api/v1/omnl/openapi.json', accent: 'teal' }, + { id: 'catalog', label: 'API Catalog', description: 'All OMNL routes and auth notes', icon: 'ledger', href: '/api/v1/omnl/catalog', accent: 'blue' }, + { id: 'integration', label: 'Integration Status', description: 'Configured env groups (no secrets)', icon: 'check', href: '/api/v1/omnl/integration-status', accent: 'green' }, + { id: 'registry', label: 'IPSAS Registry', description: 'GL registry JSON', icon: 'money', href: '/api/v1/omnl/ipsas/registry', accent: 'gold' }, + { id: 'settlement-health', label: 'Settlement health', description: '128-chain settlement middleware', icon: 'check', href: '/settlement/health', accent: 'green' }, + { id: 'exchange-health', label: 'Exchange health', description: 'DBIS exchange service', icon: 'check', href: '/exchange/health', accent: 'green' }, + { id: 'fineract-swagger', label: 'Fineract Swagger', description: 'omnl.hybxfinance.io', icon: 'chart', href: 'https://omnl.hybxfinance.io/fineract-provider/swagger-ui/index.html', external: true, accent: 'purple' }, +]; diff --git a/frontend-dapp/src/config/onexShiva.ts b/frontend-dapp/src/config/onexShiva.ts new file mode 100644 index 0000000..12472af --- /dev/null +++ b/frontend-dapp/src/config/onexShiva.ts @@ -0,0 +1,24 @@ +import onexShivaConfig from '../../../config/onex-shiva-integration.v1.json'; + +export type OnexShivaConfig = typeof onexShivaConfig; + +export const onexShiva = onexShivaConfig; + +export const SHIVA_ONEX_CHAIN_ID = Number( + import.meta.env.VITE_SHIVA_ONEX_CHAIN_ID || onexShiva.chain.chainId, +); + +export const shivaOnexRpcUrl = + import.meta.env.VITE_RPC_URL_900001 || + import.meta.env.VITE_SHIVA_ONEX_RPC_URL || + onexShiva.chain.rpcDefault; + +export const shivaOnexExplorerUrl = + import.meta.env.VITE_SHIVA_ONEX_EXPLORER_URL || onexShiva.chain.explorerUrl; + +export const onexWalletEnabled = + import.meta.env.VITE_ENABLE_ONEX_SHIVA_WALLET !== 'false' && + import.meta.env.VITE_ENABLE_ONEX_SHIVA_WALLET !== '0'; + +export const ONEX_BANK_OFFICE_ID = onexShiva.fineract.officeId; +export const ONEX_HYBX_WALLET_ID = onexShiva.fineract.hybxWalletId; diff --git a/frontend-dapp/src/features/hub/DashboardSectionFold.tsx b/frontend-dapp/src/features/hub/DashboardSectionFold.tsx new file mode 100644 index 0000000..13f82cb --- /dev/null +++ b/frontend-dapp/src/features/hub/DashboardSectionFold.tsx @@ -0,0 +1,29 @@ +import type { ReactNode } from 'react'; +import type { NasaIconName } from '../../components/icons/NasaIcon'; +import NasaIcon from '../../components/icons/NasaIcon'; + +type Props = { + title: string; + subtitle?: string; + icon: NasaIconName; + defaultOpen?: boolean; + children: ReactNode; +}; + +export default function DashboardSectionFold({ title, subtitle, icon, defaultOpen = true, children }: Props) { + return ( +
+ + + + + + {title} + {subtitle && {subtitle}} + + + +
{children}
+
+ ); +} diff --git a/frontend-dapp/src/features/hub/EcosystemLinkGrid.tsx b/frontend-dapp/src/features/hub/EcosystemLinkGrid.tsx new file mode 100644 index 0000000..423fef4 --- /dev/null +++ b/frontend-dapp/src/features/hub/EcosystemLinkGrid.tsx @@ -0,0 +1,128 @@ +import { Link } from 'react-router-dom'; +import NasaIcon, { type NasaIconName } from '../../components/icons/NasaIcon'; +import type { EcosystemLink, EcosystemLinkAccent } from '../../config/ecosystemLinks'; +import { ecosystemLinkUsesAnchor } from '../../config/ecosystemLinkRouting'; + +type Props = { + title: string; + subtitle?: string; + links: EcosystemLink[]; + columns?: '2' | '3'; + centered?: boolean; + sectionIcon?: NasaIconName; + variant?: 'grid' | 'list'; + hideHeader?: boolean; +}; + +const ACCENT_STYLES: Record = { + green: { tile: 'bg-green-500/15 border-green-500/30 shadow-[0_0_20px_rgba(34,197,94,0.1)]', icon: 'text-green-400' }, + teal: { tile: 'bg-teal-500/15 border-teal-500/30 shadow-[0_0_20px_rgba(20,184,166,0.1)]', icon: 'text-teal-400' }, + blue: { tile: 'bg-sky-500/15 border-sky-500/30 shadow-[0_0_20px_rgba(56,189,248,0.1)]', icon: 'text-sky-400' }, + gold: { tile: 'bg-amber-500/15 border-amber-500/30 shadow-[0_0_20px_rgba(245,158,11,0.1)]', icon: 'text-amber-400' }, + purple: { tile: 'bg-violet-500/15 border-violet-500/30 shadow-[0_0_20px_rgba(139,92,246,0.1)]', icon: 'text-violet-400' }, + slate: { tile: 'bg-slate-500/15 border-slate-500/30', icon: 'text-slate-300' }, +}; + +function LinkCard({ link, variant }: { link: EcosystemLink; variant: 'grid' | 'list' }) { + const accent = link.accent ?? 'green'; + const styles = ACCENT_STYLES[accent]; + const isExternal = link.external || link.href.startsWith('http'); + const useAnchor = ecosystemLinkUsesAnchor(link); + const isList = variant === 'list'; + const className = [ + 'hub-link-card group flex no-underline text-inherit rounded-xl border border-white/10 bg-[var(--nasa-panel)]', + 'hover:border-green-500/40 hover:bg-[var(--nasa-panel-elevated)] active:scale-[0.98] transition-all duration-150 shadow-sm', + isList + ? 'hub-link-card--list flex-row items-center text-left gap-3 p-3 min-h-[60px]' + : 'flex-col items-center text-center gap-3 p-4 min-h-[148px] hover:-translate-y-0.5', + ].join(' '); + + const inner = ( + <> + + + +
+
+

{link.label}

+ {isExternal && } +
+

+ {link.description} +

+
+ {!isList && ( + + Open + + + )} + {isList && } + + ); + + if (isExternal) { + return ( + + {inner} + + ); + } + + if (useAnchor) { + return ( + + {inner} + + ); + } + + return ( + + {inner} + + ); +} + +export default function EcosystemLinkGrid({ + title, + subtitle, + links, + columns = '3', + centered = true, + sectionIcon = 'link', + variant = 'grid', + hideHeader = false, +}: Props) { + const gridClass = + variant === 'list' + ? 'grid grid-cols-1 gap-2' + : columns === '2' + ? 'grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4' + : 'grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3 sm:gap-4'; + + return ( +
+ {!hideHeader && ( + + )} +
+ {links.map((link) => ( + + ))} +
+
+ ); +} diff --git a/frontend-dapp/src/features/hub/FeaturedAppsStrip.tsx b/frontend-dapp/src/features/hub/FeaturedAppsStrip.tsx new file mode 100644 index 0000000..b0d47ec --- /dev/null +++ b/frontend-dapp/src/features/hub/FeaturedAppsStrip.tsx @@ -0,0 +1,56 @@ +import { Link } from 'react-router-dom'; +import NasaIcon from '../../components/icons/NasaIcon'; +import { FEATURED_APP_LINKS, type EcosystemLink, type EcosystemLinkAccent } from '../../config/ecosystemLinks'; +import { ecosystemLinkUsesAnchor } from '../../config/ecosystemLinkRouting'; + +const ACCENT_TILE: Record = { + green: 'bg-green-500/15 border-green-500/30 text-green-400', + teal: 'bg-teal-500/15 border-teal-500/30 text-teal-400', + blue: 'bg-sky-500/15 border-sky-500/30 text-sky-400', + gold: 'bg-amber-500/15 border-amber-500/30 text-amber-400', + purple: 'bg-violet-500/15 border-violet-500/30 text-violet-400', + slate: 'bg-slate-500/15 border-slate-500/30 text-slate-300', +}; + +function FeaturedItem({ link }: { link: EcosystemLink }) { + const accent = link.accent ?? 'green'; + const tile = ACCENT_TILE[accent]; + const short = link.label.replace('DBIS ', '').replace('Digital ', ''); + + const className = 'dbis-featured__item'; + const inner = ( + <> + + + + {short} + + ); + + if (ecosystemLinkUsesAnchor(link)) { + return ( + + {inner} + + ); + } + + return ( + + {inner} + + ); +} + +export default function FeaturedAppsStrip() { + return ( +
+

Quick access

+
+ {FEATURED_APP_LINKS.map((link) => ( + + ))} +
+
+ ); +} diff --git a/frontend-dapp/src/features/office24/Office24BankCorridors.tsx b/frontend-dapp/src/features/office24/Office24BankCorridors.tsx new file mode 100644 index 0000000..9c87a16 --- /dev/null +++ b/frontend-dapp/src/features/office24/Office24BankCorridors.tsx @@ -0,0 +1,116 @@ +import { useEffect, useState } from 'react'; +import { fetchOmnlChainRegistry, type ChainRegistryResponse } from '../../config/supported-chains'; +import NasaIcon, { StatusIcon } from '../../components/icons/NasaIcon'; +import DashboardSection from '../omnl-dashboard/DashboardSection'; + +/** Office 24 Central Bank — 128-bank corridor status (settlement + SWIFT + M2 rails). */ +export default function Office24BankCorridors() { + const [registry, setRegistry] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + fetchOmnlChainRegistry() + .then(setRegistry) + .finally(() => setLoading(false)); + }, []); + + const chains = registry?.chains ?? []; + const active = chains.filter((c) => c.status === 'active'); + const openCorridors = active.length; + + return ( + + {loading ? 'Loading…' : `${openCorridors} / ${registry?.maxCapacity ?? 128} corridors open`} + + } + > +
+
+

{registry?.stats?.active ?? '—'}

+

Active chains

+
+
+

{registry?.primaryChainId ?? 138}

+

Settlement hub

+
+
+

{registry?.mirrorChainId ?? 651940}

+

Mirror chain

+
+
+

{registry?.stats?.staged ?? '—'}

+

Staged

+
+
+ +

+ + All active chains have full OMNL integration: Fineract posting, SWIFT MT102/MT103, trade finance (SBLC/BG/LC), M2 + token load, and bridge/swap when enabled in registry. +

+ +
+ + + + + + + + + + + + + + + {chains.slice(0, 128).map((c) => { + const open = c.status === 'active'; + return ( + + + + + + + + + + + ); + })} + {!chains.length && !loading && ( + + + + )} + +
ChainNetworkCorridorSWIFTSettlementM2SwapBridge
{c.chainId}{c.name} + + {open ? 'OPEN' : c.status} + + + + + + + + + + + +
+ Chain registry unavailable — settlement API /chains +
+
+
+ ); +} diff --git a/frontend-dapp/src/features/office24/Office24EcosystemSubjects.tsx b/frontend-dapp/src/features/office24/Office24EcosystemSubjects.tsx new file mode 100644 index 0000000..779d0b7 --- /dev/null +++ b/frontend-dapp/src/features/office24/Office24EcosystemSubjects.tsx @@ -0,0 +1,65 @@ +import { Link } from 'react-router-dom'; +import { FEATURED_APP_LINKS, IN_APP_LINKS, OPERATOR_CONSOLE_LINKS, PUBLIC_PORTAL_LINKS } from '../../config/ecosystemLinks'; +import { ecosystemLinkUsesAnchor } from '../../config/ecosystemLinkRouting'; +import NasaIcon, { StatusIcon } from '../../components/icons/NasaIcon'; +import DashboardSection from '../omnl-dashboard/DashboardSection'; + +const SUBJECT_GROUPS = [ + { title: 'Core banking apps', links: FEATURED_APP_LINKS }, + { title: 'Ecosystem subjects', links: IN_APP_LINKS.filter((l) => !FEATURED_APP_LINKS.some((f) => f.id === l.id)) }, + { title: 'Operator consoles', links: OPERATOR_CONSOLE_LINKS }, + { title: 'Production portals', links: PUBLIC_PORTAL_LINKS }, +]; + +export default function Office24EcosystemSubjects() { + return ( + +
+ {SUBJECT_GROUPS.map((group) => ( +
+

+ + {group.title} +

+
    + {group.links.map((link) => ( +
  • + {ecosystemLinkUsesAnchor(link) ? ( + + + + {link.label} + + + + ) : ( + + + + {link.label} + + + + )} +
  • + ))} +
+
+ ))} +
+
+ ); +} diff --git a/frontend-dapp/src/styles/app-dashboard.css b/frontend-dapp/src/styles/app-dashboard.css new file mode 100644 index 0000000..24842f5 --- /dev/null +++ b/frontend-dapp/src/styles/app-dashboard.css @@ -0,0 +1,498 @@ +/** + * App dashboard — standard dark UI (replaces NASA mission-control styling). + * Class names kept for compatibility; visuals use normal Inter typography and rounded cards. + */ + +.omnl-app, +.app-shell, +.nasa-mission-root { + --nasa-bg: #141a16; + --nasa-bg-deep: #0f1412; + --nasa-panel: #1e2329; + --nasa-panel-elevated: #252830; + --nasa-panel-border: rgba(255, 255, 255, 0.1); + --nasa-telemetry: #22c55e; + --nasa-nominal: #22c55e; + --nasa-caution: #f59e0b; + --nasa-critical: #ef4444; + --nasa-text: #eaecef; + --nasa-muted: #848e9c; + --nasa-mono: Inter, system-ui, sans-serif; +} + +html:has(.omnl-app), +html:has(.omnl-app) body { + background: var(--nasa-bg); + min-height: 100%; +} + +.omnl-app { + background: var(--nasa-bg); + color: var(--nasa-text); +} + +.nasa-icon--telemetry { color: var(--nasa-telemetry); } +.nasa-icon--nominal { color: var(--nasa-nominal); } +.nasa-icon--critical { color: var(--nasa-critical); } +.nasa-icon--caution { color: var(--nasa-caution); } +.nasa-icon--spin { animation: nasa-spin 1s linear infinite; } + +@keyframes nasa-spin { + to { transform: rotate(360deg); } +} + +.dash-page, .o24-page, .nasa-page, .cb-page { + min-height: calc(100vh - 56px); + font-family: Inter, system-ui, sans-serif; +} + +.nasa-panel, .dash-card, .o24-card, .cb-card { + background: var(--nasa-panel); + border: 1px solid var(--nasa-panel-border); + border-radius: 12px; + padding: 1.25rem; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); +} + +.nasa-header { + border: none; + background: transparent; + padding: 0 0 1.5rem; + margin-bottom: 0.5rem; + border-bottom: 1px solid var(--nasa-panel-border); +} + +.nasa-header__meta { + display: none; +} + +.nasa-header__body { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + gap: 1rem; + align-items: flex-start; +} + +.nasa-header--centered .nasa-header__body { + justify-content: center; + align-items: center; + text-align: center; +} + +.nasa-header--centered .nasa-header__title-row { + flex-direction: column; + align-items: center; +} + +.nasa-header--centered .dash-page-subtitle { + margin-left: auto; + margin-right: auto; +} + +.app-page-shell .dash-section-subtitle, +.hub-page .dash-section-subtitle { + margin-left: auto; + margin-right: auto; +} + +.app-page-shell { + width: 100%; + max-width: 1100px; + margin-left: auto; + margin-right: auto; +} + +.app-page-shell .dash-card, +.hub-page .dash-card { + width: 100%; +} + +.nasa-header__title-row { + display: flex; + gap: 1rem; + align-items: flex-start; +} + +.nasa-header__icon-wrap { + width: 44px; + height: 44px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 10px; + border: 1px solid var(--nasa-panel-border); + background: var(--nasa-panel-elevated); + color: var(--nasa-telemetry); + flex-shrink: 0; +} + +.nasa-title, .dash-page-title { + font-family: Inter, system-ui, sans-serif; + font-size: 1.75rem; + font-weight: 700; + letter-spacing: -0.02em; + text-transform: none; + color: var(--nasa-text); + line-height: 1.25; +} + +.dash-page-subtitle { + margin-top: 0.35rem; + color: var(--nasa-muted); + font-size: 0.95rem; + max-width: 48rem; + line-height: 1.5; +} + +.nasa-badge, .dash-badge { + display: inline-flex; + align-items: center; + gap: 0.35rem; + padding: 0.25rem 0.65rem; + border-radius: 6px; + font-family: Inter, system-ui, sans-serif; + font-size: 0.75rem; + font-weight: 500; + letter-spacing: normal; + text-transform: none; +} + +.nasa-badge--telemetry, .dash-badge--blue { + background: rgba(34, 197, 94, 0.12); + color: #4ade80; + border: 1px solid rgba(34, 197, 94, 0.25); +} + +.nasa-badge--nominal, .dash-badge--green { + background: rgba(34, 197, 94, 0.12); + color: #4ade80; + border: 1px solid rgba(34, 197, 94, 0.25); +} + +.nasa-badge--caution, .dash-badge--gold { + background: rgba(245, 158, 11, 0.12); + color: #fbbf24; + border: 1px solid rgba(245, 158, 11, 0.25); +} + +.dash-banner { + border-radius: 10px; + padding: 1rem 1.15rem; + border: 1px solid var(--nasa-panel-border); + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + gap: 0.75rem; +} + +.dash-banner__title { + font-size: 0.95rem; + font-weight: 600; + font-family: Inter, system-ui, sans-serif; + letter-spacing: normal; + text-transform: none; + margin-bottom: 0.25rem; +} + +.dash-banner__detail { + font-size: 0.875rem; + color: var(--nasa-muted); + line-height: 1.5; +} + +.dash-banner--ok { + background: rgba(34, 197, 94, 0.08); + border-color: rgba(34, 197, 94, 0.25); +} +.dash-banner--ok .dash-banner__title { color: #4ade80; } + +.dash-banner--warn { + background: rgba(245, 158, 11, 0.08); + border-color: rgba(245, 158, 11, 0.25); +} +.dash-banner--warn .dash-banner__title { color: #fbbf24; } + +.dash-banner--error { + background: rgba(239, 68, 68, 0.08); + border-color: rgba(239, 68, 68, 0.25); +} +.dash-banner--error .dash-banner__title { color: #f87171; } + +.dash-banner--loading { + background: var(--nasa-panel); +} + +.dash-stat-label { + font-family: Inter, system-ui, sans-serif; + font-size: 0.75rem; + font-weight: 500; + color: var(--nasa-muted); + text-transform: none; + letter-spacing: normal; + margin-bottom: 0.35rem; + text-align: center; +} + +.dash-stat-value { + font-family: Inter, system-ui, sans-serif; + font-size: 1.5rem; + font-weight: 700; + color: var(--nasa-text); + font-variant-numeric: tabular-nums; + text-align: center; +} + +.dash-stat-hint { + margin-top: 0.35rem; + font-size: 0.8rem; + color: var(--nasa-muted); + text-align: center; +} + +.dash-section-title { + font-family: Inter, system-ui, sans-serif; + font-size: 1rem; + font-weight: 600; + letter-spacing: normal; + text-transform: none; + color: var(--nasa-text); +} + +.dash-section-subtitle { + margin-top: 0.25rem; + font-size: 0.875rem; + color: var(--nasa-muted); + text-align: center; +} + +.dash-kv { + display: flex; + justify-content: space-between; + gap: 1rem; + padding: 0.5rem 0; + font-size: 0.875rem; + border-bottom: 1px solid var(--nasa-panel-border); +} + +.dash-kv:last-child { border-bottom: none; } +.dash-kv dt { color: var(--nasa-muted); flex-shrink: 0; } +.dash-kv dd { color: var(--nasa-text); text-align: right; word-break: break-word; } +.dash-kv .dash-kv-value--ok { color: #4ade80; font-weight: 500; } + +.dash-quick-links__label { + font-family: Inter, system-ui, sans-serif; + font-size: 0.75rem; + font-weight: 500; + text-transform: none; + letter-spacing: normal; + color: var(--nasa-muted); + margin-bottom: 0.5rem; +} + +.dash-quick-links__chip { + display: inline-flex; + align-items: center; + gap: 0.35rem; + padding: 0.4rem 0.85rem; + font-family: Inter, system-ui, sans-serif; + font-size: 0.8rem; + letter-spacing: normal; + color: var(--nasa-text); + background: var(--nasa-panel-elevated); + border: 1px solid var(--nasa-panel-border); + border-radius: 999px; + transition: background 0.15s, border-color 0.15s; +} + +.dash-quick-links__chip:hover { + border-color: var(--nasa-telemetry); + color: var(--nasa-telemetry); + background: rgba(34, 197, 94, 0.08); +} + +.dash-btn, .nasa-btn { + padding: 0.55rem 1rem; + border: 1px solid var(--nasa-panel-border); + border-radius: 8px; + font-family: Inter, system-ui, sans-serif; + font-size: 0.875rem; + font-weight: 500; + letter-spacing: normal; + text-transform: none; + background: var(--nasa-panel-elevated); + color: var(--nasa-text); + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.4rem; + transition: background 0.15s, border-color 0.15s; +} + +.dash-btn:hover, .nasa-btn:hover { + border-color: rgba(34, 197, 94, 0.4); + background: rgba(34, 197, 94, 0.08); +} + +.nasa-btn--primary { + background: #16a34a; + border-color: #16a34a; + color: #fff; +} + +.nasa-btn--primary:hover { + background: #22c55e; + border-color: #22c55e; + color: #fff; +} + +.dash-btn:disabled, .nasa-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.nasa-input, .nasa-select { + width: 100%; + min-height: 44px; + padding: 0.5rem 0.75rem; + background: var(--nasa-panel-elevated); + border: 1px solid var(--nasa-panel-border); + border-radius: 8px; + color: var(--nasa-text); + font-family: Inter, system-ui, sans-serif; + font-size: 0.9rem; +} + +.nasa-input:focus, .nasa-select:focus { + outline: none; + border-color: var(--nasa-telemetry); + box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.2); +} + +.nasa-inset-panel { + background: var(--nasa-panel-elevated); + border: 1px solid var(--nasa-panel-border); + border-radius: 8px; + padding: 1rem; +} + +.omnl-app-header { + background: #1a241c !important; + border-color: rgba(34, 197, 94, 0.2) !important; + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2); +} + +.nasa-nav-brand { + font-family: Inter, system-ui, sans-serif; + letter-spacing: normal; + text-transform: none; + color: var(--nasa-text) !important; + font-weight: 600; +} + +.nasa-nav-link { + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.5rem 0.75rem; + font-family: Inter, system-ui, sans-serif; + font-size: 0.875rem; + font-weight: 500; + letter-spacing: normal; + text-transform: none; + color: var(--nasa-muted); + border-radius: 8px; + border: 1px solid transparent; + transition: color 0.15s, background 0.15s; +} + +.nasa-nav-link:hover { + color: var(--nasa-text); + background: rgba(255, 255, 255, 0.05); +} + +.nasa-nav-link--active { + color: #fff !important; + background: #16a34a !important; + border-color: transparent; +} + +.nasa-table { + width: 100%; + border-collapse: collapse; +} + +.nasa-table thead { + background: var(--nasa-panel-elevated); + font-family: Inter, system-ui, sans-serif; + font-size: 0.75rem; + font-weight: 500; + letter-spacing: normal; + text-transform: none; + color: var(--nasa-muted); +} + +.nasa-table tbody tr { border-top: 1px solid var(--nasa-panel-border); } +.nasa-table tbody tr:hover { background: rgba(255, 255, 255, 0.03); } + +.trade-layout { + display: grid; + grid-template-columns: 240px 1fr; + min-height: calc(100vh - 56px); + background: var(--nasa-bg); +} + +.trade-markets, .trade-panel, .trade-pair-header, .trade-recent, .trade-book { + background: var(--nasa-panel) !important; + border-color: var(--nasa-panel-border) !important; +} + +.trade-center { + min-height: 0; + background: var(--nasa-bg); +} + +.trade-chart { + background: var(--nasa-panel) !important; + border-color: var(--nasa-panel-border) !important; +} + +.trade-chart__canvas { + min-height: 240px; + border-radius: 8px; + border: 1px solid var(--nasa-panel-border); + background: var(--nasa-panel-elevated); +} + +.trade-market-row:hover { background: rgba(255, 255, 255, 0.04); } +.trade-market-row--active { + background: rgba(34, 197, 94, 0.1); + border-left: 2px solid var(--nasa-telemetry) !important; +} + +.trade-orderbook { max-height: 360px; overflow-y: auto; font-family: ui-monospace, monospace; font-size: 0.8rem; } + +@media (max-width: 1023px) { + .trade-layout { + display: flex; + flex-direction: column; + min-height: calc(100dvh - 56px); + } + + .trade-center { order: 2; } + + .trade-panel { + order: 3; + width: 100% !important; + max-width: 100%; + border-left: none !important; + border-top: 1px solid var(--nasa-panel-border); + } + + .trade-markets { display: none !important; } +} + +@media (min-width: 1024px) { + .trade-layout { grid-template-columns: 240px 1fr 320px; } +} diff --git a/frontend-dapp/src/styles/dbis-dashboard.css b/frontend-dapp/src/styles/dbis-dashboard.css new file mode 100644 index 0000000..644a414 --- /dev/null +++ b/frontend-dapp/src/styles/dbis-dashboard.css @@ -0,0 +1,369 @@ +/** + * DBIS Dashboard — mobile-first hub styling + */ + +.dbis-dashboard { + background: + radial-gradient(ellipse 120% 80% at 50% -20%, rgba(34, 197, 94, 0.12), transparent 55%), + var(--nasa-bg, #141a16); +} + +.dbis-dashboard__inner { + width: 100%; + max-width: 56rem; + margin: 0 auto; + padding: 1rem 0.75rem 5.5rem; +} + +@media (min-width: 640px) { + .dbis-dashboard__inner { + padding: 1.5rem 1rem 2rem; + } +} + +.dbis-dashboard__hero { + text-align: center; + margin-bottom: 1.5rem; +} + +@media (min-width: 640px) { + .dbis-dashboard__hero { + margin-bottom: 2rem; + } +} + +.dbis-dashboard__logo { + display: inline-flex; + align-items: center; + justify-content: center; + width: 4rem; + height: 4rem; + border-radius: 1rem; + background: linear-gradient(145deg, #16a34a, #065f46); + color: #fff; + border: 1px solid rgba(74, 222, 128, 0.35); + box-shadow: 0 8px 32px rgba(22, 163, 74, 0.35); + margin-bottom: 0.75rem; +} + +@media (min-width: 640px) { + .dbis-dashboard__logo { + width: 4.5rem; + height: 4.5rem; + border-radius: 1.125rem; + } +} + +.dbis-dashboard__eyebrow { + font-size: 0.65rem; + font-weight: 600; + letter-spacing: 0.14em; + text-transform: uppercase; + color: #22c55e; + margin-bottom: 0.35rem; +} + +.dbis-dashboard__title { + font-size: 1.75rem; + font-weight: 700; + letter-spacing: -0.03em; + color: var(--nasa-text, #eaecef); + line-height: 1.15; +} + +@media (min-width: 640px) { + .dbis-dashboard__title { + font-size: 2.25rem; + } +} + +.dbis-dashboard__subtitle { + margin-top: 0.5rem; + font-size: 0.875rem; + color: var(--nasa-muted, #848e9c); + max-width: 28rem; + margin-left: auto; + margin-right: auto; + line-height: 1.5; +} + +.dbis-status-pills { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 0.5rem; + margin-top: 1rem; +} + +.dbis-status-pill { + display: inline-flex; + align-items: center; + gap: 0.35rem; + padding: 0.35rem 0.65rem; + border-radius: 999px; + font-size: 0.7rem; + font-weight: 500; + border: 1px solid rgba(34, 197, 94, 0.25); + background: rgba(34, 197, 94, 0.08); + color: #4ade80; +} + +/* Featured horizontal strip */ +.dbis-featured { + margin-bottom: 1.5rem; +} + +.dbis-featured__label { + font-size: 0.75rem; + font-weight: 600; + color: var(--nasa-muted); + margin-bottom: 0.65rem; + padding-left: 0.15rem; +} + +.dbis-featured__scroll { + display: flex; + gap: 0.65rem; + overflow-x: auto; + padding-bottom: 0.35rem; + -webkit-overflow-scrolling: touch; + scroll-snap-type: x mandatory; + scrollbar-width: none; +} + +.dbis-featured__scroll::-webkit-scrollbar { + display: none; +} + +.dbis-featured__item { + flex: 0 0 auto; + scroll-snap-align: start; + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; + min-width: 5.25rem; + max-width: 5.5rem; + padding: 0.75rem 0.5rem; + border-radius: 1rem; + border: 1px solid rgba(255, 255, 255, 0.08); + background: var(--nasa-panel); + text-decoration: none; + color: inherit; + transition: border-color 0.2s, transform 0.2s, background 0.2s; + min-height: 5.5rem; +} + +.dbis-featured__item:active { + transform: scale(0.97); +} + +.dbis-featured__item:hover { + border-color: rgba(34, 197, 94, 0.4); + background: var(--nasa-panel-elevated); +} + +.dbis-featured__icon { + display: flex; + align-items: center; + justify-content: center; + width: 2.75rem; + height: 2.75rem; + border-radius: 0.875rem; + border: 1px solid transparent; +} + +.dbis-featured__name { + font-size: 0.65rem; + font-weight: 600; + text-align: center; + line-height: 1.2; + color: var(--nasa-text); +} + +@media (min-width: 768px) { + .dbis-featured__scroll { + display: grid; + grid-template-columns: repeat(5, 1fr); + overflow: visible; + gap: 0.75rem; + } + + .dbis-featured__item { + min-width: 0; + max-width: none; + } +} + +/* Collapsible sections on mobile */ +.dbis-section-fold { + margin-bottom: 0.75rem; + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 0.875rem; + background: rgba(30, 35, 41, 0.5); + overflow: hidden; +} + +.dbis-section-fold__summary { + display: flex; + align-items: center; + gap: 0.65rem; + padding: 0.875rem 1rem; + cursor: pointer; + list-style: none; + min-height: 3rem; + font-weight: 600; + font-size: 0.9rem; + color: var(--nasa-text); + user-select: none; +} + +.dbis-section-fold__summary::-webkit-details-marker { + display: none; +} + +.dbis-section-fold__chevron { + margin-left: auto; + color: var(--nasa-muted); + transition: transform 0.2s; +} + +.dbis-section-fold[open] .dbis-section-fold__chevron { + transform: rotate(180deg); +} + +.dbis-section-fold__body { + padding: 0 0.75rem 0.875rem; +} + +@media (min-width: 768px) { + .dbis-section-fold { + border: none; + background: transparent; + border-radius: 0; + margin-bottom: 0; + } + + .dbis-section-fold__summary { + display: none; + } + + .dbis-section-fold__body { + padding: 0; + } +} + +/* Link cards */ +.hub-link-card { + min-height: 8.5rem; + touch-action: manipulation; +} + +@media (max-width: 639px) { + .hub-link-card { + min-height: 7.5rem; + padding: 0.875rem; + } + + .hub-link-card__icon { + width: 3rem !important; + height: 3rem !important; + border-radius: 0.75rem !important; + } +} + +.hub-link-card--list { + flex-direction: row !important; + align-items: center !important; + text-align: left !important; + min-height: 3.75rem !important; + padding: 0.75rem 1rem !important; + gap: 0.75rem !important; +} + +.hub-link-card--list .hub-link-card__icon { + width: 2.5rem !important; + height: 2.5rem !important; + flex-shrink: 0; +} + +.hub-link-card--list .hub-link-card__text { + flex: 1; + min-width: 0; +} + +.hub-link-card--list .hub-link-card__open { + display: none; +} + +.hub-link-card--list h3 { + text-align: left; +} + +.hub-link-card--list p { + text-align: left; + margin-top: 0.15rem !important; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +/* Mobile bottom nav */ +.dbis-bottom-nav { + position: fixed; + left: 0; + right: 0; + bottom: 0; + z-index: 60; + display: flex; + justify-content: space-around; + align-items: stretch; + min-height: 3.5rem; + padding-bottom: env(safe-area-inset-bottom, 0); + background: rgba(26, 36, 28, 0.96); + border-top: 1px solid rgba(34, 197, 94, 0.2); + backdrop-filter: blur(12px); +} + +@media (min-width: 768px) { + .dbis-bottom-nav { + display: none; + } +} + +.dbis-bottom-nav__item { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0.15rem; + padding: 0.4rem 0.25rem; + min-height: 3.5rem; + font-size: 0.6rem; + font-weight: 500; + color: #848e9c; + text-decoration: none; + border: none; + background: transparent; + cursor: pointer; + touch-action: manipulation; +} + +.dbis-bottom-nav__item--active { + color: #4ade80; +} + +.dbis-bottom-nav__item:active { + background: rgba(255, 255, 255, 0.05); +} + +.dbis-has-bottom-nav { + padding-bottom: calc(3.75rem + env(safe-area-inset-bottom, 0)); +} + +@media (min-width: 768px) { + .dbis-has-bottom-nav { + padding-bottom: 0; + } +} diff --git a/frontend-dapp/src/styles/dbis-green-ui.css b/frontend-dapp/src/styles/dbis-green-ui.css new file mode 100644 index 0000000..3ec5137 --- /dev/null +++ b/frontend-dapp/src/styles/dbis-green-ui.css @@ -0,0 +1,13 @@ +@layer components { + .ui-btn-primary { + @apply bg-green-600 text-white font-medium rounded-xl hover:bg-green-500 transition-colors focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 focus:ring-offset-[#141a16] min-h-[44px] px-4 py-2; + } + + .ui-btn-secondary { + @apply bg-[#1a241c] text-white font-medium rounded-xl border border-green-500/30 hover:bg-green-900/30 transition-colors min-h-[44px] px-4 py-2; + } + + .ui-btn-icon { + @apply p-2 rounded-lg text-green-400 hover:bg-green-500/10 min-h-[44px] min-w-[44px]; + } +} diff --git a/services/token-aggregation/public/omnl-api-base.js b/services/token-aggregation/public/omnl-api-base.js new file mode 100644 index 0000000..2f79ad0 --- /dev/null +++ b/services/token-aggregation/public/omnl-api-base.js @@ -0,0 +1,8 @@ +(function () { + /** auto → same-origin /api/v1 (vite proxy, portal-serve, or token-aggregation). */ + window.resolveOmnlApiBase = function () { + const meta = document.querySelector('meta[name="omnl-api-base"]')?.getAttribute('content')?.trim(); + if (meta && meta !== 'auto') return meta.replace(/\/$/, ''); + return '/api/v1'; + }; +})();