diff --git a/client/components/asset-allocation-chart.tsx b/client/components/asset-allocation-chart.tsx
index 0eedb6b..e8486ad 100644
--- a/client/components/asset-allocation-chart.tsx
+++ b/client/components/asset-allocation-chart.tsx
@@ -3,6 +3,8 @@
import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip } from 'recharts'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { useAPAXStore, formatCurrency } from '@/lib/store'
+import { Skeleton } from '@/components/ui/skeleton'
+import {useEffect, useState } from 'react'
const COLORS = {
gold: '#D4AF37',
@@ -50,6 +52,11 @@ export function AssetAllocationChart() {
{ name: 'Platinum', value: 10, color: COLORS.platinum }
]
+
+
+
+
+
const CustomTooltip = ({ active, payload }: { active?: boolean; payload?: Array<{ payload: typeof data[0] }> }) => {
if (active && payload && payload.length) {
const item = payload[0].payload
@@ -64,11 +71,25 @@ export function AssetAllocationChart() {
)
}
return null
+
}
+
+ const [isLoading, setIsLoading ] = useState(true);
+
+ useEffect(() =>
+ {
+ setTimeout(() => {
+ setIsLoading(false);
+ }, 2000);
+ })
+ if (isLoading) {
+ return
+ }
+
return (
- {/* Your Holdings Chart */}
+ {/* Holdings Chart */}
Your Asset Allocation
@@ -213,3 +234,85 @@ export function AssetAllocationChart() {
)
}
+
+
+function AssetAllocationSkeleton() {
+ return (
+
+
+ {/* 1. Left Card: Your Asset Allocation */}
+
+
+ {/* Title */}
+ {/* Desc */}
+
+
+
+ {/* Donut Chart Placeholder */}
+
+
+ {/* Legend Grid Placeholder */}
+
+
+ {[...Array(3)].map((_, i) => (
+
+ ))}
+
+ {/* Divider Line */}
+
+
+
+
+
+ {/* Right Card: APX-i Index Token */}
+
+
+
+
+
+
+ {/* Donut Chart Placeholder */}
+
+
+
+ {/* Legend Grid */}
+
+ {[...Array(3)].map((_, i) => (
+
+ ))}
+
+
+ {/* Bottom Holdings Box Placeholder */}
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/client/components/portfolio-overview.tsx b/client/components/portfolio-overview.tsx
index 00bccd6..7bdd7b2 100644
--- a/client/components/portfolio-overview.tsx
+++ b/client/components/portfolio-overview.tsx
@@ -1,8 +1,11 @@
'use client'
+import { useEffect, useState } from 'react'
import { TrendUp, TrendDown, Wallet, Coins } from '@phosphor-icons/react'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { useAPAXStore, formatCurrency, formatWeight } from '@/lib/store'
+import { Skeleton } from '@/components/ui/skeleton'
+import React from 'react'
export function PortfolioOverview() {
const { userHoldings, metalPrices } = useAPAXStore()
@@ -49,6 +52,17 @@ export function PortfolioOverview() {
}
]
+ const [isLoading, setIsLoading ] = useState(true)
+
+ useEffect(() => {
+ const timer = setTimeout(() => setIsLoading(false), 2000)
+ return () => clearTimeout(timer)
+ }, [])
+
+
+ if(isLoading) {
+ return
+ }
return (
{/* Primary Card - Spans 2 cols for visibility */}
@@ -122,3 +136,42 @@ export function PortfolioOverview() {
)
}
+
+ function PortfolioOverviewSkeleton() {
+ return (
+
+
+ {/* 1. Primary Card Skeleton (Total Portfolio) */}
+
+
+
+
+
+
+
+
+ {/* Trend Badge & Subtext */}
+
+
+
+
+
+
+
+ {/* Metal Token Cards Skeletons (Gold, Silver, Platinum) */}
+ {[...Array(3)].map((_, i) => (
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+
+ )
+}
\ No newline at end of file
diff --git a/client/components/sharia-certification-hub.tsx b/client/components/sharia-certification-hub.tsx
index 6932a37..603b9cb 100644
--- a/client/components/sharia-certification-hub.tsx
+++ b/client/components/sharia-certification-hub.tsx
@@ -4,6 +4,9 @@ import { FileText, Download, Shield, CheckCircle, ExternalLink } from 'lucide-re
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
+import { Skeleton } from '@/components/ui/skeleton'
+import {useEffect, useState } from 'react'
+
const certifications = [
{
@@ -51,7 +54,25 @@ const typeIcons = {
report: '📋'
}
+
+
+
+
+
export function ShariaCertificationHub() {
+
+ const [isLoading, setIsLoading ] = useState(true);
+
+ useEffect(() =>
+ {
+ setTimeout(() => {
+ setIsLoading(false);
+ }, 2000);
+ })
+
+ if (isLoading) {
+ return
+ }
return (
@@ -153,3 +174,76 @@ export function ShariaCertificationHub() {
)
}
+
+function ShariaCertificationHubSkeleton() {
+ return (
+
+
+
+
+ {/* Shield Icon Placeholder */}
+
+
+
+ {/* Title Placeholder */}
+
+ {/* Description Placeholder (Hidden on mobile to match real component) */}
+
+
+
+
+ {/* Badge Placeholder */}
+
+
+
+
+
+ {/* Certificate Grid Skeleton */}
+
+ {/* Generate 2 placeholders to match typical list size */}
+ {[...Array(2)].map((_, i) => (
+
+
+ {/* Certificate Icon */}
+
+
+
+ {/* Cert Title */}
+
+ {/* Cert Description (2 lines) */}
+
+
+
+
+ {/* Meta Row (Issuer | Date) */}
+
+
+
+
+ {/* Action Buttons Row */}
+
+
+
+
+
+ ))}
+
+
+ {/* Bottom Verification Banner Skeleton */}
+
+
+
+ )
+}
\ No newline at end of file