diff --git a/client/app/globals.css b/client/app/globals.css index ac468bd..3108cde 100644 --- a/client/app/globals.css +++ b/client/app/globals.css @@ -116,7 +116,6 @@ @layer base { * { @apply border-border outline-ring/50; - /* Global scrollbar for Firefox */ scrollbar-width: thin; scrollbar-color: rgba(212, 175, 55, 0.6) #0A0A0A; @@ -132,6 +131,8 @@ /* 2% Noise texture - De-digitalizing */ position: relative; + + } body::before { diff --git a/client/components/charts/metal-price-history.tsx b/client/components/charts/metal-price-history.tsx new file mode 100644 index 0000000..4b88bd7 --- /dev/null +++ b/client/components/charts/metal-price-history.tsx @@ -0,0 +1,100 @@ +'use client' + +import React, { useMemo } from 'react' +import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts' +import { useAPAXStore } from '@/lib/store' + +// mock data generate +const generateHistoricalData = (currentPrice: number) => { + return [ + { time: '08:00', price: currentPrice * 0.982 }, + { time: '10:00', price: currentPrice * 0.988 }, + { time: '12:00', price: currentPrice * 0.995 }, + { time: '14:00', price: currentPrice * 0.991 }, + { time: '16:00', price: currentPrice * 1.008 }, + { time: '18:00', price: currentPrice * 1.003 }, + { time: '20:00', price: currentPrice }, + ] +} + +export function MetalPriceHistoryChart() { + + //global state access + const { metalPrices } = useAPAXStore() + + const chartData = useMemo(() => + generateHistoricalData(metalPrices.gold), + [metalPrices.gold]) + + //chart + return ( +
+
+ + + + + + + + + + + + + + + + + { + if (active && payload && payload.length) { + return ( +
+

+ {payload[0].payload.time} UTC +

+

+ ${payload[0].value?.toLocaleString(undefined, { minimumFractionDigits: 2 })} +

+
+ ); + } + return null; + }} + /> + + +
+
+
+ ) +} \ No newline at end of file diff --git a/client/components/dashboard-layout.tsx b/client/components/dashboard-layout.tsx index d550719..aa6d3a9 100644 --- a/client/components/dashboard-layout.tsx +++ b/client/components/dashboard-layout.tsx @@ -82,7 +82,7 @@ export function DashboardLayout({ children}: DashboardLayoutProps) { {/* Main Content Area */}
- + {children} diff --git a/client/components/shared/MotionWrapper.tsx b/client/components/shared/MotionWrapper.tsx index eba2545..5a25f5e 100644 --- a/client/components/shared/MotionWrapper.tsx +++ b/client/components/shared/MotionWrapper.tsx @@ -6,19 +6,21 @@ import { motion, HTMLMotionProps, Variants } from 'framer-motion'; interface MotionWrapperProps extends HTMLMotionProps<'div'> { children: React.ReactNode; delay?: number; - stagger?: boolean; // prop for orchestration + stagger?: boolean; } const variants: Variants = { - initial: { opacity: 0, y: 20 }, + initial: { + opacity: 0, + y: 20 + }, animate: (delay: number) => ({ opacity: 1, y: 0, transition: { delay, - duration: 0.4, - ease: [0.25, 0.1, 0.25, 1], - // if this is a container, stagger the children + duration: 0.5, + ease: [0.16, 1, 0.3, 1], staggerChildren: 0.05, delayChildren: delay, } @@ -26,17 +28,19 @@ const variants: Variants = { exit: { opacity: 0, y: -20, - transition: { duration: 0.2 } + transition: { duration: 0.3, ease: [0.16, 1, 0.3, 1] } } }; -// item variant for children so they can inherit the stagger export const childVariants: Variants = { initial: { opacity: 0, y: 20 }, animate: { opacity: 1, y: 0, - transition: { duration: 0.4, ease: [0.25, 0.1, 0.25, 1] } + transition: { + duration: 0.4, + ease: [0.16, 1, 0.3, 1] + } } }; @@ -55,12 +59,24 @@ export const MotionWrapper = ({ exit="exit" custom={delay} className={className} + style={{ + contain: 'paint', + willChange: 'transform, opacity' + }} {...props} > {stagger - ? React.Children.map(children, (child) => ( - {child} - )) + ? React.Children.map(children, (child) => { + if (!React.isValidElement(child)) return child; + return ( + + {child} + + ); + }) : children} ); diff --git a/client/components/shared/live-security-terminal.tsx b/client/components/shared/live-security-terminal.tsx new file mode 100644 index 0000000..d896b1c --- /dev/null +++ b/client/components/shared/live-security-terminal.tsx @@ -0,0 +1,136 @@ +'use client' + +import React, { useEffect, useState, useRef, memo } from 'react' +import { motion, AnimatePresence } from 'framer-motion' +import { AuditLog } from '@/lib/store' + +// scanning effect +const ScanningBeam = () => ( +
+
+
+
+
+
+
+
+) + +const AuditLogCard = memo(({ + log, + index, + isLast, + totalLogs, + beamDuration +}: { + log: AuditLog; + index: number; + isLast: boolean; + totalLogs: number; + beamDuration: number; +}) => { + //reveal sync + const revealDelay = (index / totalLogs) * beamDuration; + + return ( + +
+
+ {!isLast && ( +
+ )} +
+ +
+
+

+ [{log.event}] +

+ + {new Date().toLocaleTimeString([], { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' })} + +
+

+ {log.details} +

+
+ TX_SIG + + {log.txHash} + +
+
+ + ) +}) + +AuditLogCard.displayName = 'AuditLogCard' + +export function LiveSecurityTerminal({ logs }: { logs: AuditLog[] }) { + const [isBeamVisible, setIsBeamVisible] = useState(false) + const [beamKey, setBeamKey] = useState(0) + const displayLogs = logs.slice(-20); + const BEAM_DURATION = 4 // beam speed + + useEffect(() => { + if (logs.length > 0) { + setIsBeamVisible(false); + + const timer = setTimeout(() => { + setIsBeamVisible(true); + setBeamKey(prev => prev + 1); + }, 50); // small buffer to stabilize the engine + + return () => clearTimeout(timer); + } + }, [logs.length]); // watch the count for new log renders + + return ( +
+ + {isBeamVisible && ( + setIsBeamVisible(false)} + className="absolute inset-x-0 z-30 pointer-events-none" + style={{ height: '100%', willChange: 'transform' }} + > +
+ +
+
+ )} +
+ +
+ {/* reverse column layout */} +
+ {displayLogs.map((log, i) => ( + + ))} +
+
+
+ ) +} \ No newline at end of file diff --git a/client/components/views/dashboard-view.tsx b/client/components/views/dashboard-view.tsx index 93869d2..f49e080 100644 --- a/client/components/views/dashboard-view.tsx +++ b/client/components/views/dashboard-view.tsx @@ -1,5 +1,10 @@ -'use client' +"use client"; +import Image from "next/image"; +import { PortfolioOverview } from "@/components/portfolio-overview"; +import { AssetAllocationChart } from "@/components/asset-allocation-chart"; +import { ShariaCertificationHub } from "@/components/sharia-certification-hub"; +import { MetalPriceHistoryChart } from "@/components/charts/metal-price-history"; import Image from 'next/image' import { PortfolioOverview } from '@/components/portfolio-overview' import { AssetAllocationChart } from '@/components/asset-allocation-chart' @@ -23,10 +28,14 @@ export function DashboardView() {
- Network Connectivity: 100% + + Network Connectivity: 100% +
Latest Block: #8,421,093 - Vault A1-X: Re-verified by Lead Auditor + + Vault A1-X: Re-verified by Lead Auditor + Last Synced: 2s ago
))} @@ -52,6 +61,9 @@ export function DashboardView() {
+ {/* Metal price history chart */} + + {/* Portfolio Overview Stats */} @@ -88,5 +100,5 @@ export function DashboardView() {
- ) + ); } diff --git a/client/components/views/por-view.tsx b/client/components/views/por-view.tsx index 27140c3..64caf84 100644 --- a/client/components/views/por-view.tsx +++ b/client/components/views/por-view.tsx @@ -8,6 +8,7 @@ import { Badge } from '@/components/ui/badge' import { Progress } from '@/components/ui/progress' import { ScrollArea } from '@/components/ui/scroll-area' import { useAPAXStore, formatWeight } from '@/lib/store' +import { LiveSecurityTerminal } from '@/components/shared/live-security-terminal' export function PorView() { const { vaultData, auditLogs } = useAPAXStore() @@ -196,7 +197,7 @@ export function PorView() { {/* Total Tokens */} -
+

Total APX Tokens Minted

@@ -282,45 +283,12 @@ export function PorView() {

- - -
- {auditLogs.map((log, index) => ( -
-
-
- {index < auditLogs.length - 1 && ( -
- )} -
-
-
-

- [{log.event.toUpperCase()}] -

- - {formatTimeAgo(log.timestamp)} - -
-

- {log.details} -

-
- TX::HASH - - {log.txHash} - -
-
-
- ))} -
+ + + {/* LST */} + + - {/* Scanning Line Effect */} -
diff --git a/client/pnpm-lock.yaml b/client/pnpm-lock.yaml index 5dedbb3..57a2756 100644 --- a/client/pnpm-lock.yaml +++ b/client/pnpm-lock.yaml @@ -353,89 +353,105 @@ packages: resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-arm@1.2.4': resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-ppc64@1.2.4': resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-riscv64@1.2.4': resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-s390x@1.2.4': resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-x64@1.2.4': resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.2.4': resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.2.4': resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-linux-arm64@0.34.5': resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-linux-arm@0.34.5': resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-linux-ppc64@0.34.5': resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-linux-riscv64@0.34.5': resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-linux-s390x@0.34.5': resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-linux-x64@0.34.5': resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-linuxmusl-arm64@0.34.5': resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-linuxmusl-x64@0.34.5': resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-wasm32@0.34.5': resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} @@ -502,24 +518,28 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@next/swc-linux-arm64-musl@16.1.6': resolution: {integrity: sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@next/swc-linux-x64-gnu@16.1.6': resolution: {integrity: sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@next/swc-linux-x64-musl@16.1.6': resolution: {integrity: sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@next/swc-win32-arm64-msvc@16.1.6': resolution: {integrity: sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==} @@ -1259,24 +1279,28 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.1.18': resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.1.18': resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.1.18': resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@tailwindcss/oxide-wasm32-wasi@4.1.18': resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==} @@ -1457,41 +1481,49 @@ packages: resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} cpu: [arm64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-arm64-musl@1.11.1': resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} cpu: [arm64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} cpu: [riscv64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} cpu: [s390x] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-gnu@1.11.1': resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} cpu: [x64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-musl@1.11.1': resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} cpu: [x64] os: [linux] + libc: [musl] '@unrs/resolver-binding-wasm32-wasi@1.11.1': resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} @@ -2353,24 +2385,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.30.2: resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.30.2: resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.30.2: resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.30.2: resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==}