Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 104 additions & 1 deletion client/components/asset-allocation-chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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
Expand All @@ -64,11 +71,25 @@ export function AssetAllocationChart() {
)
}
return null

}


const [isLoading, setIsLoading ] = useState(true);

useEffect(() =>
{
setTimeout(() => {
setIsLoading(false);
}, 2000);
})
if (isLoading) {
return <AssetAllocationSkeleton />
}

return (
<div className="grid gap-4 md:grid-cols-2">
{/* Your Holdings Chart */}
{/* Holdings Chart */}
<Card className="glass border-[#2A2A2A] bg-[#111111] overflow-hidden">
<CardHeader className="p-4 pb-2">
<CardTitle className="text-base md:text-lg text-[#E8E8E8] font-serif">Your Asset Allocation</CardTitle>
Expand Down Expand Up @@ -213,3 +234,85 @@ export function AssetAllocationChart() {
</div>
)
}


function AssetAllocationSkeleton() {
return (
<div className="grid gap-4 md:grid-cols-2">

{/* 1. Left Card: Your Asset Allocation */}
<Card className="border-[#2A2A2A] bg-[#111111]">
<CardHeader className="p-4 pb-2 space-y-2">
<Skeleton className="h-6 w-48 bg-[#1A1A1A]" /> {/* Title */}
<Skeleton className="h-3 w-32 bg-[#1A1A1A]" /> {/* Desc */}
</CardHeader>
<CardContent className="p-4 py-8 flex flex-col items-center justify-center gap-6">

{/* Donut Chart Placeholder */}
<div className="relative h-[180px] w-[180px] md:h-[200px] md:w-[200px] shrink-0">
<Skeleton className="h-full w-full rounded-full bg-[#1A1A1A]" />
<div className="absolute inset-4 rounded-full bg-[#111111]" />
</div>

{/* Legend Grid Placeholder */}
<div className="w-full max-w-sm">
<div className="grid grid-cols-3 gap-2">
{[...Array(3)].map((_, i) => (
<div key={i} className="flex flex-col items-center gap-2">
<div className="flex items-center gap-2">
<Skeleton className="h-2 w-2 rounded-full bg-[#1A1A1A]" />
<Skeleton className="h-3 w-8 bg-[#1A1A1A]" />
</div>
<Skeleton className="h-4 w-12 bg-[#1A1A1A]" />
<Skeleton className="h-2 w-8 bg-[#1A1A1A]" />
</div>
))}
</div>
{/* Divider Line */}
<div className="h-px w-full bg-[#2A2A2A] mt-4" />
</div>
</CardContent>
</Card>

{/* Right Card: APX-i Index Token */}
<Card className="border-[#2A2A2A] bg-[#111111]">
<CardHeader className="p-4 pb-2">
<div className="flex items-center gap-2">
<Skeleton className="h-8 w-8 rounded-full bg-[#1A1A1A]" />
<div className="space-y-1">
<Skeleton className="h-5 w-40 bg-[#1A1A1A]" />
<Skeleton className="h-3 w-24 bg-[#1A1A1A]" />
</div>
</div>
</CardHeader>
<CardContent className="p-4 py-8 flex flex-col items-center justify-center gap-6">

{/* Donut Chart Placeholder */}
<div className="relative h-[180px] w-[180px] md:h-[200px] md:w-[200px] shrink-0">
<Skeleton className="h-full w-full rounded-full bg-[#1A1A1A]" />
<div className="absolute inset-4 rounded-full bg-[#111111]" />
</div>

<div className="w-full max-w-sm">
{/* Legend Grid */}
<div className="grid grid-cols-3 gap-2 mb-6">
{[...Array(3)].map((_, i) => (
<div key={i} className="flex flex-col items-center gap-2">
<div className="flex items-center gap-2">
<Skeleton className="h-2 w-2 rounded-full bg-[#1A1A1A]" />
<Skeleton className="h-3 w-8 bg-[#1A1A1A]" />
</div>
<Skeleton className="h-4 w-12 bg-[#1A1A1A]" />
</div>
))}
</div>

{/* Bottom Holdings Box Placeholder */}
<Skeleton className="h-14 w-full rounded-lg bg-[#1A1A1A]" />
</div>
</CardContent>
</Card>

</div>
)
}
53 changes: 53 additions & 0 deletions client/components/portfolio-overview.tsx
Original file line number Diff line number Diff line change
@@ -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()
Expand Down Expand Up @@ -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 <PortfolioOverviewSkeleton />
}
return (
<div className="grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{/* Primary Card - Spans 2 cols for visibility */}
Expand Down Expand Up @@ -122,3 +136,42 @@ export function PortfolioOverview() {
</div>
)
}

function PortfolioOverviewSkeleton() {
return (
<div className="grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">

{/* 1. Primary Card Skeleton (Total Portfolio) */}
<Card className="sm:col-span-2 border-[#2A2A2A] bg-[#111111]">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<Skeleton className="h-4 w-32 bg-[#1A1A1A]" />
<Skeleton className="h-7 w-7 rounded-lg bg-[#1A1A1A]" />
</CardHeader>
<CardContent>
<Skeleton className="h-10 w-48 mb-3 bg-[#1A1A1A]" />

{/* Trend Badge & Subtext */}
<div className="flex items-center gap-2">
<Skeleton className="h-5 w-24 rounded-full bg-[#1A1A1A]" />
<Skeleton className="h-3 w-16 bg-[#1A1A1A]" />
</div>
</CardContent>
</Card>

{/* Metal Token Cards Skeletons (Gold, Silver, Platinum) */}
{[...Array(3)].map((_, i) => (
<Card key={i} className="border-[#2A2A2A] bg-[#111111]">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-1.5">
<Skeleton className="h-4 w-24 bg-[#1A1A1A]" />
<Skeleton className="h-7 w-7 rounded-lg bg-[#1A1A1A]" />
</CardHeader>
<CardContent>
<Skeleton className="h-8 w-32 mb-1.5 bg-[#1A1A1A]" />
<Skeleton className="h-3 w-20 bg-[#1A1A1A]" />
</CardContent>
</Card>
))}

</div>
)
}
94 changes: 94 additions & 0 deletions client/components/sharia-certification-hub.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
{
Expand Down Expand Up @@ -51,7 +54,25 @@ const typeIcons = {
report: '📋'
}






export function ShariaCertificationHub() {

const [isLoading, setIsLoading ] = useState(true);

useEffect(() =>
{
setTimeout(() => {
setIsLoading(false);
}, 2000);
})

if (isLoading) {
return <ShariaCertificationHubSkeleton />
}
return (
<Card className="glass border-[#2A2A2A] bg-[#111111]">
<CardHeader className="p-4 pb-2">
Expand Down Expand Up @@ -153,3 +174,76 @@ export function ShariaCertificationHub() {
</Card>
)
}

function ShariaCertificationHubSkeleton() {
return (
<Card className="glass border-[#2A2A2A] bg-[#111111]">
<CardHeader className="p-4 pb-2">
<div className="flex items-center justify-between gap-4">
<div className="flex items-center gap-2.5 min-w-0">
{/* Shield Icon Placeholder */}
<Skeleton className="h-7 w-7 rounded-lg bg-[#1A1A1A] shrink-0" />

<div className="min-w-0 space-y-1.5">
{/* Title Placeholder */}
<Skeleton className="h-4 w-40 bg-[#1A1A1A]" />
{/* Description Placeholder (Hidden on mobile to match real component) */}
<Skeleton className="h-2 w-48 bg-[#1A1A1A] hidden sm:block" />
</div>
</div>

{/* Badge Placeholder */}
<Skeleton className="h-6 w-20 rounded-full bg-[#1A1A1A] shrink-0" />
</div>
</CardHeader>

<CardContent className="p-4 pt-0">
{/* Certificate Grid Skeleton */}
<div className="grid gap-3 grid-cols-1 sm:grid-cols-2 lg:grid-cols-1 2xl:grid-cols-2">
{/* Generate 2 placeholders to match typical list size */}
{[...Array(2)].map((_, i) => (
<div
key={i}
className="p-3 rounded-lg border border-[#2A2A2A] bg-[#0A0A0A]"
>
<div className="flex items-start gap-2.5">
{/* Certificate Icon */}
<Skeleton className="h-10 w-10 rounded-md bg-[#1A1A1A] shrink-0" />

<div className="flex-1 space-y-2 min-w-0">
{/* Cert Title */}
<Skeleton className="h-3.5 w-3/4 bg-[#1A1A1A]" />
{/* Cert Description (2 lines) */}
<div className="space-y-1">
<Skeleton className="h-2.5 w-full bg-[#1A1A1A]" />
<Skeleton className="h-2.5 w-5/6 bg-[#1A1A1A]" />
</div>
{/* Meta Row (Issuer | Date) */}
<Skeleton className="h-2 w-1/2 bg-[#1A1A1A]" />
</div>
</div>

{/* Action Buttons Row */}
<div className="flex items-center gap-2 mt-2 pt-2 border-t border-[#2A2A2A]">
<Skeleton className="h-7 flex-1 rounded bg-[#1A1A1A]" />
<Skeleton className="h-7 flex-1 rounded bg-[#1A1A1A]" />
</div>
</div>
))}
</div>

{/* Bottom Verification Banner Skeleton */}
<div className="mt-3 p-3 rounded-lg border border-[#2A2A2A] bg-[#1A1A1A]/30">
<div className="flex items-center gap-3">
<Skeleton className="h-4 w-4 rounded-sm bg-[#1A1A1A] shrink-0" />
<div className="flex-1 space-y-1.5">
<Skeleton className="h-3 w-48 bg-[#1A1A1A]" />
<Skeleton className="h-2 w-32 bg-[#1A1A1A]" />
</div>
<Skeleton className="h-7 w-16 rounded bg-[#1A1A1A] shrink-0" />
</div>
</div>
</CardContent>
</Card>
)
}