Compare commits

...

2 Commits

Author SHA1 Message Date
CycroftX
3d95b99502 Fix: Dashboard layout overlap and grid structure 2026-02-16 00:21:49 +05:30
CycroftX
557accf0c2 Refactor: Global Theme System and Finance Modules Redesign 2026-02-16 00:09:52 +05:30
52 changed files with 3077 additions and 1148 deletions

BIN
dist.tar.gz Normal file

Binary file not shown.

View File

@@ -3,6 +3,7 @@ import { Toaster as Sonner } from "@/components/ui/sonner";
import { TooltipProvider } from "@/components/ui/tooltip";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { AuthProvider } from "@/hooks/use-auth";
import { ThemeProvider } from "@/context/ThemeContext";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Landing from "./pages/Landing";
import Index from "./pages/Index";
@@ -16,14 +17,16 @@ const App = () => (
<Toaster />
<Sonner />
<AuthProvider>
<BrowserRouter>
<Routes>
<Route path="/" element={<Landing />} />
<Route path="/app" element={<Index />} />
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
<ThemeProvider>
<BrowserRouter>
<Routes>
<Route path="/" element={<Landing />} />
<Route path="/app" element={<Index />} />
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
</ThemeProvider>
</AuthProvider>
</TooltipProvider>
</QueryClientProvider>

View File

@@ -1,5 +1,6 @@
import { motion } from 'framer-motion';
import { cn } from '@/lib/utils';
import { cn, formatAmount } from '@/lib/utils';
import { DirhamIcon } from '@/components/ui/custom-icons';
import React from 'react';
interface BudgetRingProps {
@@ -53,7 +54,17 @@ export const BudgetRing = ({ spent, total, label, color = "text-primary", size =
</div>
<div className="mt-2 text-center">
<p className="text-sm font-semibold text-slate-700">{label}</p>
<p className="text-xs text-slate-400">{spent / 1000}k / {total / 1000}k</p>
<div className="flex items-center justify-center gap-1 text-xs text-slate-400">
<div className="flex items-center gap-0.5">
<DirhamIcon className="w-3 h-3" />
{formatAmount(spent)}
</div>
<span>/</span>
<div className="flex items-center gap-0.5">
<DirhamIcon className="w-3 h-3" />
{formatAmount(total)}
</div>
</div>
</div>
</div>
);

View File

@@ -1,69 +1,41 @@
import React from 'react';
import { motion } from 'framer-motion';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Progress } from '@/components/ui/progress';
import { Input } from '@/components/ui/input';
import { GlassCard } from '@/components/ui/GlassCard';
import { BudgetRing } from '@/components/BudgetRing';
import { formatCurrency } from '@/lib/utils';
import {
TrendingUp,
TrendingDown,
Wallet,
Target,
CreditCard,
PiggyBank,
AlertTriangle,
CheckCircle,
ArrowUpRight,
ArrowDownRight,
PieChart,
Search,
Sparkles,
Shield
} from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Search, Sparkles } from 'lucide-react';
import { DirhamIcon } from '@/components/ui/custom-icons';
// Import New Modular Components
import HeroStats from './dashboard/HeroStats';
import TransactionsList from './dashboard/TransactionsList';
import GoalsList from './dashboard/GoalsList';
import InvestmentPerformance from './dashboard/InvestmentPerformance';
import AIInsights from './dashboard/AIInsights';
import QuickActions from './dashboard/QuickActions';
const Dashboard: React.FC = () => {
const portfolioData = {
totalValue: 350000,
change: 3.2,
isPositive: true
};
const budgetData = {
spent: 4500,
budget: 6500,
categories: [
{ name: 'Food', spent: 1250, budget: 1800, color: 'text-orange-500' },
{ name: 'Transport', spent: 850, budget: 1200, color: 'text-blue-500' },
{ name: 'Entertainment', spent: 600, budget: 800, color: 'text-purple-500' },
{ name: 'Shopping', spent: 1800, budget: 2700, color: 'text-pink-500' }
]
};
const savingsGoals = [
{ name: 'Emergency Fund', current: 45000, target: 80000, color: 'from-green-400 to-green-600' },
{ name: 'Dubai Trip', current: 8500, target: 15000, color: 'from-blue-400 to-blue-600' },
{ name: 'New Car', current: 62000, target: 120000, color: 'from-purple-400 to-purple-600' }
];
const creditScore = 768;
const currentHour = new Date().getHours();
const greeting = currentHour < 12 ? 'Good morning' : currentHour < 18 ? 'Good afternoon' : 'Good evening';
// Hardcoded Header Data as per Target Design Reference
const currentDate = "Sunday, February 15, 2026";
const monthlyWealthGrowth = "425,600"; // AED value
return (
<div className="space-y-8 animate-fade-in text-slate-800">
{/* Hero Section */}
<div className="space-y-8 animate-fade-in text-slate-800 pb-10">
{/* Header Section */}
<section className="space-y-6">
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4">
<div>
<div className="text-sm text-slate-500 font-medium mb-1">{currentDate}</div>
<h1 className="text-2xl sm:text-3xl md:text-4xl font-light tracking-tight text-slate-900">
{greeting}, <span className="font-semibold bg-clip-text text-transparent bg-gradient-to-r from-purple-600 to-pink-600">Anjali</span>
Good evening, <span className="font-semibold bg-clip-text text-transparent bg-gradient-to-r from-purple-600 to-pink-600">Anjali!</span>
</h1>
<p className="text-slate-500 mt-1">Here's your financial snapshot for today.</p>
<div className="flex items-center gap-2 mt-2 text-slate-600 bg-white/50 backdrop-blur-sm px-3 py-1.5 rounded-full w-fit shadow-sm border border-purple-100">
<span className="text-sm">Monthly Wealth Growth:</span>
<div className="flex items-center font-bold text-purple-700">
<DirhamIcon className="w-4 h-4 mr-1" /> {monthlyWealthGrowth}
</div>
</div>
</div>
{/* Search Bar */}
<div className="relative w-full md:w-96 group">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Search className="h-5 w-5 text-slate-400 group-focus-within:text-purple-500 transition-colors" />
@@ -82,137 +54,45 @@ const Dashboard: React.FC = () => {
</div>
</section>
{/* Key Metrics - Budget Rings */}
<section>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
{budgetData.categories.map((cat, idx) => (
<GlassCard key={idx} className="flex flex-col items-center justify-center py-6" hoverEffect>
<BudgetRing
spent={cat.spent}
total={cat.budget}
label={cat.name}
color={cat.color}
size="md"
/>
</GlassCard>
))}
{/* Hero Stats (Top Row) */}
<section className="relative z-10 text-slate-800">
<HeroStats />
</section>
{/* Main Content Area (2 Columns) */}
<section className="grid grid-cols-1 lg:grid-cols-2 gap-6 items-start relative z-10">
{/* Left Column: Recent Transactions */}
<div className="space-y-6">
<TransactionsList />
</div>
{/* Right Column: Financial Goals */}
<div className="space-y-6">
<GoalsList />
</div>
</section>
{/* Portfolio & Net Worth - Sliding Cards */}
<section className="space-y-4">
<h2 className="text-xl font-medium text-slate-700">Portfolio Highlights</h2>
{/* Horizontal Scroll / Slider */}
<div className="flex gap-4 overflow-x-auto pb-4 snap-x snap-mandatory scrollbar-hide">
{/* Total Net Worth Card */}
<GlassCard className="min-w-[280px] md:min-w-[320px] snap-center flex-shrink-0 p-6 bg-gradient-to-br from-white/60 to-purple-50/30">
<div className="flex justify-between items-start mb-8">
<div className="p-2 rounded-xl bg-purple-100/50 text-purple-600">
<Wallet className="h-6 w-6" />
</div>
<span className="text-xs font-semibold px-2 py-1 rounded-full bg-green-100 text-green-700 flex items-center">
<TrendingUp className="w-3 h-3 mr-1" /> +3.2%
</span>
</div>
<div>
<p className="text-sm text-slate-500 font-medium">Total Net Worth</p>
<h3 className="text-3xl font-bold text-slate-800 mt-1">{formatCurrency(portfolioData.totalValue)}</h3>
</div>
</GlassCard>
{/* Bottom Section (Full Width) - Performance & AI Insights */}
<section className="grid grid-cols-1 lg:grid-cols-3 gap-6 items-start relative z-10">
{/* Investment Performance (Takes 2 columns) */}
<div className="lg:col-span-2">
<InvestmentPerformance />
</div>
{/* Savings Card */}
<GlassCard className="min-w-[280px] md:min-w-[320px] snap-center flex-shrink-0 p-6">
<div className="flex justify-between items-start mb-8">
<div className="p-2 rounded-xl bg-blue-100/50 text-blue-600">
<PiggyBank className="h-6 w-6" />
</div>
<span className="text-xs font-semibold px-2 py-1 rounded-full bg-blue-50 text-blue-600">
28% Rate
</span>
</div>
<div>
<p className="text-sm text-slate-500 font-medium">Total Savings</p>
<h3 className="text-3xl font-bold text-slate-800 mt-1">{formatCurrency(portfolioData.totalValue * 0.15)}</h3>
</div>
</GlassCard>
{/* Credit Score Card */}
<GlassCard className="min-w-[280px] md:min-w-[320px] snap-center flex-shrink-0 p-6">
<div className="flex justify-between items-start mb-8">
<div className="p-2 rounded-xl bg-green-100/50 text-green-600">
<Shield className="h-6 w-6" />
</div>
<span className="text-xs font-semibold px-2 py-1 rounded-full bg-green-50 text-green-600">
Excellent
</span>
</div>
<div>
<p className="text-sm text-slate-500 font-medium">Credit Score</p>
<h3 className="text-3xl font-bold text-slate-800 mt-1">{creditScore}</h3>
</div>
</GlassCard>
{/* AI Insights (Takes 1 column) */}
<div className="lg:col-span-1">
<AIInsights />
</div>
</section>
{/* AI Insights & Goals */}
<section className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<GlassCard className="p-6">
<div className="flex items-center gap-2 mb-6">
<Sparkles className="w-5 h-5 text-purple-500" />
<h3 className="text-lg font-semibold text-slate-800">AI Recommendations</h3>
</div>
<div className="space-y-4">
<div className="p-4 rounded-xl bg-purple-50/50 border border-purple-100 flex gap-4 items-start">
<div className="p-2 bg-purple-100 rounded-lg text-purple-600 mt-1">
<TrendingUp className="w-4 h-4" />
</div>
<div>
<h4 className="font-medium text-slate-800">Optimize Savings</h4>
<p className="text-sm text-slate-500 mt-1 leading-relaxed">Cancel unused streaming subscription to save {formatCurrency(150)}/year. You haven't used it in 3 months.</p>
</div>
</div>
<div className="p-4 rounded-xl bg-pink-50/50 border border-pink-100 flex gap-4 items-start">
<div className="p-2 bg-pink-100 rounded-lg text-pink-600 mt-1">
<CreditCard className="w-4 h-4" />
</div>
<div>
<h4 className="font-medium text-slate-800">Credit utilization</h4>
<p className="text-sm text-slate-500 mt-1 leading-relaxed">Pay down {formatCurrency(1200)} on ADCB card to keep utilization under 30%.</p>
</div>
</div>
</div>
</GlassCard>
<GlassCard className="p-6">
<div className="flex items-center gap-2 mb-6">
<Target className="w-5 h-5 text-blue-500" />
<h3 className="text-lg font-semibold text-slate-800">Goal Progress</h3>
</div>
<div className="space-y-6">
{savingsGoals.map((goal, idx) => (
<div key={idx}>
<div className="flex justify-between text-sm mb-2">
<span className="font-medium text-slate-700">{goal.name}</span>
<span className="text-slate-500">{formatCurrency(goal.current)} / {formatCurrency(goal.target)}</span>
</div>
<div className="h-2 w-full bg-slate-100 rounded-full overflow-hidden">
<motion.div
className={`h-full bg-gradient-to-r ${goal.color} rounded-full`}
initial={{ width: "0%" }}
animate={{ width: `${(goal.current / goal.target) * 100}%` }}
transition={{ duration: 1, ease: "easeOut" }}
/>
</div>
</div>
))}
<Button className="w-full mt-2 bg-gradient-to-r from-blue-500 to-cyan-500 text-white rounded-xl shadow-lg border-0 hover:shadow-xl transition-all">
View All Goals
</Button>
</div>
</GlassCard>
{/* Footer Actions */}
<section className="relative z-10">
<h3 className="text-lg font-medium text-slate-700 mb-4 px-1">Quick Actions</h3>
<QuickActions />
</section>
</div>
);
};
export default Dashboard;

View File

@@ -12,6 +12,7 @@ import {
} from 'lucide-react';
import { ExpandableTabs } from '@/components/ui/expandable-tabs';
import { cn } from '@/lib/utils';
import { useTheme } from '@/context/ThemeContext';
interface LayoutProps {
children: React.ReactNode;
@@ -43,6 +44,8 @@ export const Layout = ({ children, activeSection, onSectionChange, onLogout }: L
const currentNavIndex = navItems.findIndex(item => item.id === activeSection);
const selectedIndex = currentNavIndex >= 0 ? currentNavIndex : null;
const { theme } = useTheme();
const handleTabChange = (index: number | null) => {
if (index === null) return;
@@ -54,13 +57,15 @@ export const Layout = ({ children, activeSection, onSectionChange, onLogout }: L
};
return (
<div className="min-h-screen w-full relative overflow-x-hidden font-sans text-slate-800">
{/* Ambient Background Mesh */}
<div className="fixed inset-0 z-0 pointer-events-none">
<div className="absolute top-[-10%] left-[-10%] w-[50%] h-[50%] rounded-full bg-purple-200/30 blur-[100px]" />
<div className="absolute bottom-[-10%] right-[-10%] w-[50%] h-[50%] rounded-full bg-blue-200/30 blur-[100px]" />
<div className="absolute top-[40%] left-[40%] w-[30%] h-[30%] rounded-full bg-pink-200/20 blur-[100px]" />
</div>
<div className={cn("min-h-screen w-full relative overflow-x-hidden font-sans transition-colors duration-500", theme === 'dubai' ? 'text-white' : 'text-slate-800')}>
{/* Ambient Background Mesh - Only show for default theme */}
{theme === 'default' && (
<div className="fixed inset-0 z-0 pointer-events-none">
<div className="absolute top-[-10%] left-[-10%] w-[50%] h-[50%] rounded-full bg-purple-200/30 blur-[100px]" />
<div className="absolute bottom-[-10%] right-[-10%] w-[50%] h-[50%] rounded-full bg-blue-200/30 blur-[100px]" />
<div className="absolute top-[40%] left-[40%] w-[30%] h-[30%] rounded-full bg-pink-200/20 blur-[100px]" />
</div>
)}
{/* Main Content Area */}
<main className="relative z-10 pb-28 min-h-screen">

View File

@@ -0,0 +1,57 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { DirhamIcon } from '@/components/ui/custom-icons';
const AddExpenseForm: React.FC = () => {
return (
<div className="flex justify-center items-center h-[400px]">
<GlassCard className="p-8 w-full max-w-md">
<h3 className="text-xl font-bold text-slate-800 mb-6 text-center">Add New Expense</h3>
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="amount" className="text-slate-600">Amount (AED)</Label>
<div className="relative">
<div className="absolute inset-y-0 left-3 flex items-center pointer-events-none">
<DirhamIcon className="w-4 h-4 text-slate-400" />
</div>
<Input id="amount" placeholder="0.00" className="pl-9 bg-white/50 border-slate-200" />
</div>
</div>
<div className="space-y-2">
<Label htmlFor="category" className="text-slate-600">Category</Label>
<Select>
<SelectTrigger id="category" className="bg-white/50 border-slate-200">
<SelectValue placeholder="Select Category" />
</SelectTrigger>
<SelectContent>
<SelectItem value="food">Food & Dining</SelectItem>
<SelectItem value="transport">Transportation</SelectItem>
<SelectItem value="shopping">Shopping</SelectItem>
<SelectItem value="entertainment">Entertainment</SelectItem>
<SelectItem value="utilities">Utilities</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="desc" className="text-slate-600">Description</Label>
<Input id="desc" placeholder="e.g. Grocery shopping" className="bg-white/50 border-slate-200" />
</div>
<Button className="w-full mt-4 bg-slate-900 text-white hover:bg-slate-800 shadow-md">
Add Expense
</Button>
</div>
</GlassCard>
</div>
);
};
export default AddExpenseForm;

View File

@@ -0,0 +1,68 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { formatAmount } from '@/lib/utils';
import { DirhamIcon } from '@/components/ui/custom-icons';
import { Badge } from '@/components/ui/badge';
import { Utensils, Car, ShoppingBag, Clapperboard, HeartPulse, Home } from 'lucide-react';
import { motion } from 'framer-motion';
const BudgetCategoryGrid: React.FC = () => {
const categories = [
{ name: 'Food & Dining', spent: 1250, limit: 3000, icon: Utensils, color: 'bg-green-500', iconBg: 'bg-green-100 text-green-600' },
{ name: 'Transportation', spent: 950, limit: 1000, icon: Car, color: 'bg-orange-500', iconBg: 'bg-orange-100 text-orange-600' },
{ name: 'Shopping', spent: 2200, limit: 2000, icon: ShoppingBag, color: 'bg-red-500', iconBg: 'bg-red-100 text-red-600' },
{ name: 'Entertainment', spent: 800, limit: 1500, icon: Clapperboard, color: 'bg-purple-500', iconBg: 'bg-purple-100 text-purple-600' },
{ name: 'Healthcare', spent: 300, limit: 1000, icon: HeartPulse, color: 'bg-blue-500', iconBg: 'bg-blue-100 text-blue-600' },
{ name: 'Housing', spent: 6500, limit: 7000, icon: Home, color: 'bg-indigo-500', iconBg: 'bg-indigo-100 text-indigo-600' },
];
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{categories.map((cat, index) => {
const percentage = Math.min((cat.spent / cat.limit) * 100, 100);
const remaining = cat.limit - cat.spent;
const isOverGroups = cat.spent > cat.limit;
return (
<GlassCard key={index} className="p-6 relative overflow-hidden group" hoverEffect>
<div className="flex justify-between items-start mb-4">
<div className={`p-3 rounded-xl ${cat.iconBg}`}>
<cat.icon className="w-6 h-6" />
</div>
<Badge variant="outline" className={isOverGroups ? "bg-red-50 text-red-600 border-red-200" : "bg-green-50 text-green-600 border-green-200"}>
{isOverGroups ? "Over Budget" : "Active"}
</Badge>
</div>
<h3 className="text-lg font-bold text-slate-800 mb-1">{cat.name}</h3>
<div className={`text-sm font-medium mb-4 ${remaining < 0 ? 'text-red-500' : 'text-slate-500'}`}>
{remaining < 0 ? (
<span className="flex items-center gap-1">Over by <DirhamIcon className="w-3 h-3" /> {formatAmount(Math.abs(remaining))}</span>
) : (
<span className="flex items-center gap-1"><DirhamIcon className="w-3 h-3" /> {formatAmount(remaining)} left</span>
)}
</div>
<div className="w-full bg-slate-100 rounded-full h-2 overflow-hidden mb-2">
<motion.div
initial={{ width: 0 }}
animate={{ width: `${percentage}%` }}
transition={{ duration: 1 }}
className={`h-full ${cat.color}`}
/>
</div>
<div className="flex justify-between text-xs text-slate-400">
<span className="flex items-center gap-0.5"><DirhamIcon className="w-3 h-3" /> {formatAmount(cat.spent)}</span>
<span className="flex items-center gap-0.5"><DirhamIcon className="w-3 h-3" /> {formatAmount(cat.limit)}</span>
</div>
</GlassCard>
);
})}
</div>
);
};
export default BudgetCategoryGrid;

View File

@@ -0,0 +1,68 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { formatAmount } from '@/lib/utils';
import { DirhamIcon } from '@/components/ui/custom-icons';
import { Badge } from '@/components/ui/badge';
import { motion } from 'framer-motion';
const BudgetOverviewList: React.FC = () => {
const categories = [
{ name: 'Food & Dining', spent: 1250, limit: 3000, status: 'On Track', color: 'bg-green-500' },
{ name: 'Transportation', spent: 950, limit: 1000, status: 'Near Limit', color: 'bg-orange-500' },
{ name: 'Shopping', spent: 2200, limit: 2000, status: 'Over Budget', color: 'bg-red-500' },
{ name: 'Entertainment', spent: 800, limit: 1500, status: 'On Track', color: 'bg-purple-500' },
{ name: 'Healthcare', spent: 300, limit: 1000, status: 'On Track', color: 'bg-blue-500' },
];
const getStatusColor = (status: string) => {
switch (status) {
case 'On Track': return 'bg-green-100 text-green-700 border-green-200';
case 'Over Budget': return 'bg-red-100 text-red-700 border-red-200';
case 'Near Limit': return 'bg-orange-100 text-orange-700 border-orange-200';
default: return 'bg-slate-100 text-slate-700';
}
};
return (
<GlassCard className="p-6 h-full">
<h3 className="text-lg font-semibold text-slate-800 mb-6">Budget Overview</h3>
<div className="space-y-6">
{categories.map((cat, index) => {
const percentage = Math.min((cat.spent / cat.limit) * 100, 100);
return (
<div key={index} className="space-y-2">
<div className="flex justify-between items-center text-sm">
<span className="font-medium text-slate-700">{cat.name}</span>
<Badge variant="outline" className={`${getStatusColor(cat.status)} text-[10px] px-2 py-0 border`}>
{cat.status}
</Badge>
</div>
<div className="flex justify-between text-xs text-slate-500 mb-1">
<div className="flex items-center gap-1">
<span className="font-semibold text-slate-900"><DirhamIcon className="w-3 h-3 inline" /> {formatAmount(cat.spent)}</span>
</div>
<div className="flex items-center gap-1">
of <DirhamIcon className="w-3 h-3 inline" /> {formatAmount(cat.limit)}
</div>
</div>
<div className="w-full bg-slate-100 rounded-full h-2 overflow-hidden">
<motion.div
initial={{ width: 0 }}
animate={{ width: `${percentage}%` }}
transition={{ duration: 1, delay: index * 0.1 }}
className={`h-full ${cat.color} transition-all duration-1000`}
/>
</div>
</div>
);
})}
</div>
</GlassCard>
);
};
export default BudgetOverviewList;

View File

@@ -0,0 +1,92 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { formatAmount } from '@/lib/utils';
import { DirhamIcon } from '@/components/ui/custom-icons';
import {
Wallet,
ShoppingBag,
TrendingDown,
PiggyBank
} from 'lucide-react';
const BudgetSummaryCards: React.FC = () => {
// Hardcoded design values
const data = {
income: 17500,
spent: 9600,
budget: 15000,
savingsRate: 24
};
const remaining = data.budget - data.spent;
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
{/* Total Income */}
<GlassCard className="p-6">
<div className="flex items-center justify-between mb-4">
<p className="text-sm font-medium text-slate-500">Total Income</p>
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-green-400 to-green-600 flex items-center justify-center shadow-lg shadow-green-200">
<Wallet className="w-5 h-5 text-white" />
</div>
</div>
<div className="flex items-center gap-1">
<DirhamIcon className="w-6 h-6 text-slate-900" />
<p className="text-2xl font-bold text-slate-900">{formatAmount(data.income)}</p>
</div>
<p className="text-xs text-slate-400 mt-2">Monthly inflow</p>
</GlassCard>
{/* Total Spent */}
<GlassCard className="p-6">
<div className="flex items-center justify-between mb-4">
<p className="text-sm font-medium text-slate-500">Total Spent</p>
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-red-400 to-red-600 flex items-center justify-center shadow-lg shadow-red-200">
<ShoppingBag className="w-5 h-5 text-white" />
</div>
</div>
<div className="flex items-center gap-1">
<DirhamIcon className="w-6 h-6 text-slate-900" />
<p className="text-2xl font-bold text-slate-900">{formatAmount(data.spent)}</p>
</div>
<p className="text-xs text-slate-400 mt-2">
{(data.spent / data.income * 100).toFixed(1)}% of income
</p>
</GlassCard>
{/* Budget Remaining */}
<GlassCard className="p-6">
<div className="flex items-center justify-between mb-4">
<p className="text-sm font-medium text-slate-500">Budget Remaining</p>
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-blue-400 to-blue-600 flex items-center justify-center shadow-lg shadow-blue-200">
<TrendingDown className="w-5 h-5 text-white" />
</div>
</div>
<div className="flex items-center gap-1">
<DirhamIcon className="w-6 h-6 text-slate-900" />
<p className="text-2xl font-bold text-slate-900">{formatAmount(remaining)}</p>
</div>
<div className="w-full bg-slate-100 rounded-full h-1.5 mt-3 overflow-hidden">
<div className="h-full bg-blue-500" style={{ width: `${(remaining / data.budget) * 100}%` }}></div>
</div>
</GlassCard>
{/* Savings Rate */}
<GlassCard className="p-6">
<div className="flex items-center justify-between mb-4">
<p className="text-sm font-medium text-slate-500">Savings Rate</p>
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-purple-400 to-purple-600 flex items-center justify-center shadow-lg shadow-purple-200">
<PiggyBank className="w-5 h-5 text-white" />
</div>
</div>
<p className="text-2xl font-bold text-slate-900">{data.savingsRate}%</p>
<p className="text-xs text-green-600 font-medium mt-2 bg-green-50 inline-block px-2 py-0.5 rounded-full">
+2% from last month
</p>
</GlassCard>
</div>
);
};
export default BudgetSummaryCards;

View File

@@ -0,0 +1,69 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { formatAmount } from '@/lib/utils';
import { DirhamIcon } from '@/components/ui/custom-icons';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Badge } from '@/components/ui/badge';
const FullExpensesTable: React.FC = () => {
// Mock Data using requested transaction details
const transactions = [
{ date: 'Feb 15, 2026', desc: 'Carrefour Market', category: 'Food & Dining', amount: -450, type: 'expense' },
{ date: 'Feb 14, 2026', desc: 'Uber Ride', category: 'Transportation', amount: -65, type: 'expense' },
{ date: 'Feb 12, 2026', desc: 'Freelance Payout', category: 'Income', amount: 3500, type: 'income' },
{ date: 'Feb 10, 2026', desc: 'Zara Dubail Mall', category: 'Shopping', amount: -320, type: 'expense' },
{ date: 'Feb 08, 2026', desc: 'DEWA Monthly Bill', category: 'Utilities', amount: -650, type: 'expense' },
{ date: 'Feb 05, 2026', desc: 'Vox Cinemas', category: 'Entertainment', amount: -120, type: 'expense' },
{ date: 'Feb 01, 2026', desc: 'Salary Credit', category: 'Income', amount: 14000, type: 'income' },
];
return (
<GlassCard className="overflow-hidden">
<Table>
<TableHeader className="bg-slate-50/50">
<TableRow>
<TableHead className="w-[180px]">Date</TableHead>
<TableHead>Description</TableHead>
<TableHead>Category</TableHead>
<TableHead>Type</TableHead>
<TableHead className="text-right">Amount</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{transactions.map((tx, idx) => (
<TableRow key={idx} className="hover:bg-slate-50/50">
<TableCell className="text-slate-500 font-medium">{tx.date}</TableCell>
<TableCell className="font-semibold text-slate-800">{tx.desc}</TableCell>
<TableCell>
<Badge variant="outline" className="bg-white/50 text-slate-600 font-normal">
{tx.category}
</Badge>
</TableCell>
<TableCell>
<span className={`text-xs font-bold uppercase ${tx.type === 'income' ? 'text-green-600' : 'text-slate-500'}`}>
{tx.type}
</span>
</TableCell>
<TableCell className={`text-right font-bold ${tx.type === 'income' ? 'text-green-600' : 'text-slate-800'}`}>
<span className="flex items-center justify-end gap-1">
{tx.type === 'income' ? '+' : '-'} <DirhamIcon className="w-3 h-3" /> {formatAmount(Math.abs(tx.amount))}
</span>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</GlassCard>
);
};
export default FullExpensesTable;

View File

@@ -0,0 +1,45 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { formatAmount } from '@/lib/utils';
import { DirhamIcon } from '@/components/ui/custom-icons';
import { ArrowUpRight, ArrowDownLeft } from 'lucide-react';
const RecentTransactionsCard: React.FC = () => {
const transactions = [
{ date: 'Today, 2:30 PM', name: 'Carrefour', category: 'Food & Dining', amount: -450, type: 'expense' },
{ date: 'Yesterday, 8:15 PM', name: 'Uber', category: 'Transportation', amount: -65, type: 'expense' },
{ date: 'Feb 12, 10:00 AM', name: 'Freelance Project', category: 'Income', amount: 3500, type: 'income' },
{ date: 'Feb 10, 6:45 PM', name: 'Zara', category: 'Shopping', amount: -320, type: 'expense' },
{ date: 'Feb 08, 1:20 PM', name: 'DEWA Bill', category: 'Utilities', amount: -650, type: 'expense' },
];
return (
<GlassCard className="p-6 h-full flex flex-col">
<h3 className="text-lg font-semibold text-slate-800 mb-6">Recent Transactions</h3>
<div className="space-y-4 flex-1">
{transactions.map((tx, index) => (
<div key={index} className="flex items-center justify-between p-3 rounded-xl hover:bg-slate-50/80 transition-colors border border-transparent hover:border-slate-100">
<div className="flex items-center gap-4">
<div className={`w-10 h-10 rounded-full flex items-center justify-center ${tx.type === 'income' ? 'bg-green-100 text-green-600' : 'bg-slate-100 text-slate-600'}`}>
{tx.type === 'income' ? <ArrowDownLeft className="w-5 h-5" /> : <ArrowUpRight className="w-5 h-5" />}
</div>
<div>
<p className="text-sm font-bold text-slate-900">{tx.name}</p>
<p className="text-xs text-slate-500">{tx.date} {tx.category}</p>
</div>
</div>
<div className={`text-right font-bold text-sm ${tx.type === 'income' ? 'text-green-600' : 'text-slate-900'}`}>
<span className="flex items-center gap-0.5">
{tx.type === 'income' ? '+' : '-'} <DirhamIcon className="w-3 h-3" /> {formatAmount(Math.abs(tx.amount))}
</span>
</div>
</div>
))}
</div>
</GlassCard>
);
};
export default RecentTransactionsCard;

View File

@@ -0,0 +1,52 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { DirhamIcon } from '@/components/ui/custom-icons';
const SetBudgetForm: React.FC = () => {
return (
<div className="flex justify-center items-center h-[400px]">
<GlassCard className="p-8 w-full max-w-md">
<h3 className="text-xl font-bold text-slate-800 mb-6 text-center">Set Monthly Budget</h3>
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="category-budget" className="text-slate-600">Category</Label>
<Select>
<SelectTrigger id="category-budget" className="bg-white/50 border-slate-200">
<SelectValue placeholder="Select Category" />
</SelectTrigger>
<SelectContent>
<SelectItem value="food">Food & Dining</SelectItem>
<SelectItem value="transport">Transportation</SelectItem>
<SelectItem value="shopping">Shopping</SelectItem>
<SelectItem value="entertainment">Entertainment</SelectItem>
<SelectItem value="utilities">Utilities</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="budget-amount" className="text-slate-600">Budget Limit (AED)</Label>
<div className="relative">
<div className="absolute inset-y-0 left-3 flex items-center pointer-events-none">
<DirhamIcon className="w-4 h-4 text-slate-400" />
</div>
<Input id="budget-amount" placeholder="0.00" className="pl-9 bg-white/50 border-slate-200" />
</div>
</div>
<Button className="w-full mt-4 bg-gradient-to-r from-blue-600 to-indigo-600 text-white hover:from-blue-700 hover:to-indigo-700 shadow-md">
Set Budget
</Button>
</div>
</GlassCard>
</div>
);
};
export default SetBudgetForm;

View File

@@ -0,0 +1,45 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Sparkles } from 'lucide-react';
const AddCommitmentForm: React.FC = () => {
return (
<div className="flex justify-center items-center h-[500px]">
<GlassCard className="p-8 w-full max-w-md">
<h3 className="text-xl font-bold text-slate-800 mb-6 text-center">Add New Commitment</h3>
<div className="space-y-5">
<div className="space-y-2">
<Label htmlFor="comm-name" className="text-slate-600">Commitment Name</Label>
<Input id="comm-name" placeholder="e.g. Health Insurance" className="bg-white/50 border-slate-200" />
</div>
<div className="space-y-2">
<Label htmlFor="comm-amount" className="text-slate-600">Amount (AED)</Label>
<Input id="comm-amount" type="number" placeholder="0.00" className="bg-white/50 border-slate-200" />
</div>
<div className="space-y-2">
<Label htmlFor="comm-date" className="text-slate-600">Due Date</Label>
<Input id="comm-date" type="date" className="bg-white/50 border-slate-200" />
</div>
<div className="space-y-2">
<Label htmlFor="comm-type" className="text-slate-600">Type</Label>
<Input id="comm-type" placeholder="e.g. Insurance, Utility" className="bg-white/50 border-slate-200" />
</div>
<Button className="w-full mt-4 bg-slate-900 text-white hover:bg-slate-800 shadow-md flex items-center gap-2 justify-center">
<Sparkles className="w-4 h-4 text-yellow-400" /> Add Commitment
</Button>
</div>
</GlassCard>
</div>
);
};
export default AddCommitmentForm;

View File

@@ -0,0 +1,63 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { Button } from '@/components/ui/button';
import { Zap, ArrowRight, Lightbulb } from 'lucide-react';
const CommitmentAITips: React.FC = () => {
const tips = [
{
title: 'Automate Recurring Payments',
desc: 'Set up auto-pay for Rent to avoid late fees. Potential saving: AED 200/yr.',
color: 'border-l-4 border-green-500 bg-green-50/30'
},
{
title: 'Track Due Dates',
desc: 'Your Car Loan is due in 3 days. Ensure sufficient balance in your primary account.',
color: 'border-l-4 border-orange-500 bg-orange-50/30'
},
{
title: 'Review Subscriptions',
desc: 'You have 2 unused subscriptions (Gym, Netflix). Cancel to save AED 450/mo.',
color: 'border-l-4 border-blue-500 bg-blue-50/30'
},
];
return (
<div className="space-y-6">
<GlassCard className="p-6 bg-gradient-to-br from-indigo-50 to-purple-50 border-indigo-100">
<div className="flex items-center gap-2 mb-2">
<Zap className="w-5 h-5 text-indigo-600 fill-indigo-600" />
<h3 className="font-bold text-indigo-900">AI Financial Insights</h3>
</div>
<p className="text-sm text-indigo-700/80 mb-4">Smart recommendations to optimize your cash flow.</p>
<div className="space-y-3">
{tips.map((tip, idx) => (
<div key={idx} className={`p-4 rounded-r-xl ${tip.color} shadow-sm`}>
<h4 className="font-semibold text-slate-800 text-sm mb-1">{tip.title}</h4>
<p className="text-xs text-slate-600 leading-relaxed">{tip.desc}</p>
</div>
))}
</div>
</GlassCard>
<GlassCard className="p-6">
<div className="flex items-center gap-3 mb-4">
<div className="p-2 bg-yellow-100 text-yellow-600 rounded-lg">
<Lightbulb className="w-5 h-5" />
</div>
<div>
<h4 className="font-bold text-slate-800">Did you know?</h4>
<p className="text-xs text-slate-500">Paying bills 2 days early improves credit score.</p>
</div>
</div>
<Button variant="outline" className="w-full text-xs border-slate-200">
View Credit Impact
</Button>
</GlassCard>
</div>
);
};
export default CommitmentAITips;

View File

@@ -0,0 +1,85 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { formatAmount } from '@/lib/utils';
import { DirhamIcon } from '@/components/ui/custom-icons';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Check, Trash2 } from 'lucide-react';
const CommitmentCalendarTable: React.FC = () => {
const commitments = [
{ name: 'Rent Payment', type: 'Housing', amount: 6500, date: 'Oct 01, 2024', status: 'Due' },
{ name: 'Netflix Subscription', type: 'Entertainment', amount: 45, date: 'Oct 03, 2024', status: 'Paid' },
{ name: 'Car Loan EMI', type: 'Loan', amount: 2100, date: 'Oct 05, 2024', status: 'Upcoming' },
{ name: 'Electricity Bill', type: 'Utility', amount: 450, date: 'Oct 10, 2024', status: 'Upcoming' },
{ name: 'Gym Membership', type: 'Health', amount: 350, date: 'Oct 15, 2024', status: 'Upcoming' },
];
const getStatusBadge = (status: string) => {
switch (status) {
case 'Due': return <Badge variant="destructive" className="bg-red-100 text-red-700 hover:bg-red-100 border-red-200">Due</Badge>;
case 'Paid': return <Badge variant="outline" className="bg-green-100 text-green-700 border-green-200">Paid</Badge>;
default: return <Badge variant="secondary" className="bg-blue-100 text-blue-700 hover:bg-blue-100 border-blue-200">Upcoming</Badge>;
}
};
return (
<GlassCard className="overflow-hidden">
<div className="p-6 border-b border-slate-100">
<h3 className="text-lg font-semibold text-slate-800">Commitment Calendar</h3>
<p className="text-sm text-slate-500">View and manage all your scheduled payments.</p>
</div>
<Table>
<TableHeader className="bg-slate-50/50">
<TableRow>
<TableHead>Commitment</TableHead>
<TableHead>Type</TableHead>
<TableHead className="text-right">Amount (AED)</TableHead>
<TableHead>Due Date</TableHead>
<TableHead>Status</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{commitments.map((item, idx) => (
<TableRow key={idx} className="hover:bg-slate-50/50">
<TableCell className="font-semibold text-slate-800">{item.name}</TableCell>
<TableCell className="text-slate-500">{item.type}</TableCell>
<TableCell className="text-right font-bold text-slate-900">
<span className="flex items-center justify-end gap-1">
<DirhamIcon className="w-3 h-3" /> {formatAmount(item.amount)}
</span>
</TableCell>
<TableCell className="text-slate-600">{item.date}</TableCell>
<TableCell>{getStatusBadge(item.status)}</TableCell>
<TableCell className="text-right">
<div className="flex items-center justify-end gap-2">
{item.status !== 'Paid' && (
<Button variant="ghost" size="sm" className="h-8 w-8 p-0 text-green-600 hover:text-green-700 hover:bg-green-50">
<Check className="w-4 h-4" />
</Button>
)}
<Button variant="ghost" size="sm" className="h-8 w-8 p-0 text-slate-400 hover:text-red-600 hover:bg-red-50">
<Trash2 className="w-4 h-4" />
</Button>
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</GlassCard>
);
};
export default CommitmentCalendarTable;

View File

@@ -0,0 +1,90 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { formatAmount } from '@/lib/utils';
import { DirhamIcon } from '@/components/ui/custom-icons';
import { Home, CreditCard, Zap, Trash2, Check } from 'lucide-react';
const UpcomingCommitmentsList: React.FC = () => {
const commitments = [
{
id: 1,
title: 'Rent Payment',
type: 'Housing',
amount: 6500,
dueDate: 'Oct 01, 2024',
icon: Home,
iconColor: 'bg-blue-100 text-blue-600',
},
{
id: 2,
title: 'Car Loan EMI',
type: 'Loan',
amount: 2100,
dueDate: 'Oct 05, 2024',
icon: CreditCard,
iconColor: 'bg-purple-100 text-purple-600',
},
{
id: 3,
title: 'Electricity Bill',
type: 'Utility',
amount: 450,
dueDate: 'Oct 10, 2024',
icon: Zap,
iconColor: 'bg-yellow-100 text-yellow-600',
},
];
return (
<GlassCard className="p-6 h-full">
<h3 className="text-lg font-semibold text-slate-800 mb-6">Upcoming Commitments</h3>
<div className="space-y-4">
{commitments.map((item) => (
<div key={item.id} className="flex items-center justify-between p-4 rounded-xl hover:bg-slate-50 border border-transparent hover:border-slate-100 transition-all group">
<div className="flex items-center gap-4">
<div className={`p-3 rounded-xl ${item.iconColor}`}>
<item.icon className="w-5 h-5" />
</div>
<div>
<h4 className="font-semibold text-slate-900">{item.title}</h4>
<p className="text-sm text-slate-500">{item.type}</p>
</div>
</div>
<div className="flex items-center gap-8">
<div className="text-right">
<p className="font-bold text-slate-900 flex items-center justify-end gap-1">
<DirhamIcon className="w-4 h-4" /> {formatAmount(item.amount)}
</p>
<p className="text-xs text-slate-500">Due {item.dueDate}</p>
</div>
<div className="flex items-center gap-2 opacity-0 group-hover:opacity-100 transition-opacity">
<Button variant="outline" size="sm" className="h-8 border-slate-900 text-slate-900 bg-white hover:bg-slate-50">
<Check className="w-3 h-3 mr-1" /> Mark Paid
</Button>
<Button variant="ghost" size="sm" className="h-8 w-8 p-0 text-red-500 hover:text-red-600 hover:bg-red-50">
<Trash2 className="w-4 h-4" />
</Button>
</div>
</div>
</div>
))}
</div>
<div className="mt-6 pt-6 border-t border-slate-100 flex justify-between items-center px-2">
<div>
<p className="text-sm text-slate-500">Total Due This Month</p>
<p className="text-2xl font-bold text-slate-900 mt-1 flex items-center gap-1">
<DirhamIcon className="w-5 h-5" /> {formatAmount(9050)}
</p>
</div>
<Button variant="ghost" className="text-blue-600 hover:text-blue-700 hover:bg-blue-50">View All History</Button>
</div>
</GlassCard>
);
};
export default UpcomingCommitmentsList;

View File

@@ -0,0 +1,49 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import {
Table,
TableBody,
TableCell,
TableRow,
} from "@/components/ui/table";
const AECBInfoCard: React.FC = () => {
// AECB score ranges
const ranges = [
{ label: 'Excellent', range: '781 - 900', color: 'text-green-600 font-bold' },
{ label: 'Very Good', range: '721 - 780', color: 'text-emerald-600 font-medium' },
{ label: 'Good', range: '661 - 720', color: 'text-blue-600 font-medium' },
{ label: 'Fair', range: '601 - 660', color: 'text-yellow-600 font-medium' },
{ label: 'Poor', range: '300 - 600', color: 'text-red-500 font-medium' },
];
return (
<GlassCard className="p-6 h-full flex flex-col">
<div className="mb-6">
<h3 className="text-lg font-bold text-slate-900">Al Etihad Credit Bureau (AECB)</h3>
<p className="text-sm text-slate-500 mt-1">Understanding your score range.</p>
</div>
<Table>
<TableBody>
{ranges.map((range, idx) => (
<TableRow key={idx} className="hover:bg-slate-50/50 border-b-slate-100">
<TableCell className={range.color}>{range.label}</TableCell>
<TableCell className="text-right font-mono text-slate-600">{range.range}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<div className="mt-auto pt-6 border-t border-slate-100">
<p className="text-sm text-slate-600 text-center font-medium bg-slate-50 p-3 rounded-lg border border-slate-200">
Your current score of <span className="font-bold text-slate-900">785</span> puts you in the top <span className="text-green-600 font-bold">15%</span> of UAE residents!
</p>
</div>
</GlassCard>
);
};
export default AECBInfoCard;

View File

@@ -0,0 +1,127 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { formatAmount } from '@/lib/utils';
import { DirhamIcon } from '@/components/ui/custom-icons';
import { MoreHorizontal } from 'lucide-react';
const CreditCardsList: React.FC = () => {
// Detailed Card Data
const cards = [
{
name: 'Emirates NBD Skywards Infinite',
network: 'Visa',
balance: 8500,
limit: 50000,
rate: 2.99,
minDue: 450,
dueDate: 'Feb 28',
imageGradient: 'from-slate-800 to-slate-900'
},
{
name: 'ADCB TouchPoints Platinum',
network: 'Mastercard',
balance: 12000,
limit: 25000,
rate: 3.25,
minDue: 600,
dueDate: 'Mar 05',
imageGradient: 'from-blue-600 to-blue-800'
},
{
name: 'FAB Cashback',
network: 'Visa',
balance: 3200,
limit: 15000,
rate: 3.10,
minDue: 150,
dueDate: 'Mar 12',
imageGradient: 'from-teal-600 to-teal-800'
},
];
return (
<div className="space-y-6">
{cards.map((card, idx) => {
const utilization = Math.round((card.balance / card.limit) * 100);
return (
<GlassCard key={idx} className="p-0 overflow-hidden flex flex-col md:flex-row">
{/* Visual Card Representation */}
<div className={`w-full md:w-64 bg-gradient-to-br ${card.imageGradient} p-6 flex flex-col justify-between text-white relative`}>
<div className="flex justify-between items-start">
<div className="w-10 h-6 bg-white/20 rounded-md backdrop-blur-sm" />
<span className="font-bold italic opacity-80">{card.network}</span>
</div>
<div>
<p className="text-xs opacity-70 mb-1">Current Balance</p>
<p className="text-xl font-bold flex items-center gap-1"><DirhamIcon className="w-4 h-4" /> {formatAmount(card.balance)}</p>
</div>
<div className="flex justify-between items-end">
<span className="text-xs opacity-70">**** **** **** 4242</span>
<span className="text-xs font-mono opacity-80">EXP 12/28</span>
</div>
</div>
{/* Details & Actions */}
<div className="flex-1 p-6">
<div className="flex justify-between items-start mb-6">
<div>
<h3 className="text-lg font-bold text-slate-900">{card.name}</h3>
<div className="flex items-center gap-3 mt-1">
<Badge variant="outline" className="text-xs font-normal text-slate-500 border-slate-200">{card.network}</Badge>
<span className="text-xs text-slate-400">Due Date: {card.dueDate}</span>
</div>
</div>
<Button variant="ghost" size="sm" className="h-8 w-8 p-0 text-slate-400">
<MoreHorizontal className="w-5 h-5" />
</Button>
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
<div>
<p className="text-xs text-slate-400 mb-1">Credit Limit</p>
<p className="font-semibold text-slate-700 flex items-center gap-0.5"><DirhamIcon className="w-3 h-3" /> {formatAmount(card.limit)}</p>
</div>
<div>
<p className="text-xs text-slate-400 mb-1">Utilization</p>
<p className={`font-semibold ${utilization > 30 ? 'text-orange-600' : 'text-green-600'}`}>{utilization}%</p>
</div>
<div>
<p className="text-xs text-slate-400 mb-1">Interest Rate</p>
<p className="font-semibold text-slate-700">{card.rate}% / mo</p>
</div>
<div>
<p className="text-xs text-slate-400 mb-1">Available Crdt.</p>
<p className="font-semibold text-slate-700 flex items-center gap-0.5"><DirhamIcon className="w-3 h-3" /> {formatAmount(card.limit - card.balance)}</p>
</div>
</div>
<div className="flex flex-col sm:flex-row items-center justify-between gap-4 pt-4 border-t border-slate-100">
<div>
<p className="text-xs text-slate-500 font-medium">Min Payment Due</p>
<p className="text-lg font-bold text-slate-900 flex items-center gap-1">
<DirhamIcon className="w-4 h-4" /> {formatAmount(card.minDue)}
</p>
</div>
<div className="flex gap-3 w-full sm:w-auto">
<Button variant="outline" className="flex-1 sm:flex-none border-slate-200 text-slate-700 hover:bg-slate-50">
Pay Custom
</Button>
<Button className="flex-1 sm:flex-none bg-black text-white hover:bg-slate-800">
Pay Min
</Button>
</div>
</div>
</div>
</GlassCard>
);
})}
</div>
);
};
export default CreditCardsList;

View File

@@ -0,0 +1,72 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { Badge } from '@/components/ui/badge';
import { CheckCircle2, AlertCircle } from 'lucide-react';
import { motion } from 'framer-motion';
const CreditScoreProgress: React.FC = () => {
const score = 785;
const minScore = 300;
const maxScore = 850;
const range = maxScore - minScore;
const percentage = ((score - minScore) / range) * 100;
const factors = [
{ name: 'Payment History', status: 'Excellent', color: 'text-green-600', icon: CheckCircle2 },
{ name: 'Credit Utilization', status: 'Good', color: 'text-green-600', icon: CheckCircle2 },
{ name: 'Credit Age', status: 'Average', color: 'text-yellow-600', icon: AlertCircle },
{ name: 'Total Accounts', status: 'Excellent', color: 'text-green-600', icon: CheckCircle2 },
];
return (
<GlassCard className="p-6 h-full flex flex-col justify-between">
<div>
<h3 className="text-lg font-semibold text-slate-800 mb-6">Credit Score Progress</h3>
<div className="mb-8">
<div className="flex justify-between items-end mb-2">
<span className="text-sm text-slate-500 font-medium">Current Sore</span>
<span className="text-4xl font-bold text-blue-600">{score}</span>
</div>
{/* Custom Range Bar */}
<div className="relative h-4 w-full bg-slate-100 rounded-full mb-1">
<div className="absolute top-0 left-0 h-full w-full rounded-full bg-gradient-to-r from-red-400 via-yellow-400 to-green-500 opacity-30"></div>
<motion.div
className="absolute top-0 h-full w-1 bg-blue-600 rounded-full shadow-[0_0_10px_rgba(37,99,235,0.5)] z-10"
style={{ left: `${percentage}%` }}
initial={{ height: 0 }}
animate={{ height: '16px' }}
/>
<motion.div
className="absolute -top-1 w-3 h-6 bg-white border-2 border-blue-600 rounded-full z-20 shadow-md"
style={{ left: `${percentage}%`, transform: 'translateX(-50%)' }}
/>
</div>
<div className="flex justify-between text-xs text-slate-400 font-medium mt-2">
<span>300</span>
<span>850</span>
</div>
</div>
</div>
<div className="space-y-4">
<h4 className="text-sm font-semibold text-slate-700 uppercase tracking-wider">Score Factors</h4>
{factors.map((factor, idx) => (
<div key={idx} className="flex items-center justify-between p-3 rounded-xl bg-slate-50/50 border border-slate-100">
<div className="flex items-center gap-3">
<factor.icon className={`w-4 h-4 ${factor.color}`} />
<span className="text-sm font-medium text-slate-700">{factor.name}</span>
</div>
<Badge variant="outline" className={`${factor.color === 'text-green-600' ? 'text-green-700 bg-green-50 border-green-200' : 'text-yellow-700 bg-yellow-50 border-yellow-200'} text-xs`}>
{factor.status}
</Badge>
</div>
))}
</div>
</GlassCard>
);
};
export default CreditScoreProgress;

View File

@@ -0,0 +1,58 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { Check, Info, AlertTriangle, ShieldCheck } from 'lucide-react';
const CreditScoreTips: React.FC = () => {
const tips = [
{
title: 'Keep Utilization Low',
desc: 'Try to keep your balance below 30% of your limit.',
icon: Info,
color: 'bg-green-50 border-green-100',
iconColor: 'bg-green-100 text-green-600'
},
{
title: 'Pay On Time',
desc: 'Missed payments have the biggest impact on score.',
icon: Check,
color: 'bg-blue-50 border-blue-100',
iconColor: 'bg-blue-100 text-blue-600'
},
{
title: "Don't Close Old Cards",
desc: 'Credit age is a significant factor. Keep them open.',
icon: ShieldCheck,
color: 'bg-purple-50 border-purple-100',
iconColor: 'bg-purple-100 text-purple-600'
},
{
title: 'Limit New Applications',
desc: 'Hard inquiries can temporarily lower your score.',
icon: AlertTriangle,
color: 'bg-yellow-50 border-yellow-100',
iconColor: 'bg-yellow-100 text-yellow-600'
}
];
return (
<div className="space-y-4">
{tips.map((tip, idx) => (
<GlassCard key={idx} className={`p-4 border ${tip.color} shadow-sm`}>
<div className="flex items-start gap-4">
<div className={`p-2 rounded-lg ${tip.iconColor}`}>
<tip.icon className="w-5 h-5" />
</div>
<div>
<h4 className="font-bold text-slate-800 text-sm">{tip.title}</h4>
<p className="text-xs text-slate-500 mt-1 leading-relaxed">{tip.desc}</p>
</div>
</div>
</GlassCard>
))}
</div>
);
};
export default CreditScoreTips;

View File

@@ -0,0 +1,80 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { formatAmount } from '@/lib/utils';
import { DirhamIcon } from '@/components/ui/custom-icons';
import { ShieldCheck, Calendar, CreditCard, Percent } from 'lucide-react';
const CreditSummaryCards: React.FC = () => {
// Hardcoded target values
const data = {
score: 785,
balance: 23700,
utilization: 12,
minPayment: 850
};
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
{/* Credit Score */}
<GlassCard className="p-6">
<div className="flex items-center justify-between mb-4">
<p className="text-sm font-medium text-slate-500">Credit Score</p>
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-indigo-400 to-indigo-600 flex items-center justify-center shadow-lg shadow-indigo-200">
<ShieldCheck className="w-5 h-5 text-white" />
</div>
</div>
<p className="text-2xl font-bold text-slate-900">{data.score}</p>
<p className="text-xs text-green-600 font-medium mt-2">Excellent Top 15%</p>
</GlassCard>
{/* Total Balance */}
<GlassCard className="p-6">
<div className="flex items-center justify-between mb-4">
<p className="text-sm font-medium text-slate-500">Total Balance</p>
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-red-400 to-red-600 flex items-center justify-center shadow-lg shadow-red-200">
<CreditCard className="w-5 h-5 text-white" />
</div>
</div>
<div className="flex items-center gap-1">
<DirhamIcon className="w-6 h-6 text-slate-900" />
<p className="text-2xl font-bold text-slate-900">{formatAmount(data.balance)}</p>
</div>
<div className="flex items-center mt-2">
<span className="text-xs text-red-500 font-medium">+ AED 1,200 from last month</span>
</div>
</GlassCard>
{/* Utilization */}
<GlassCard className="p-6">
<div className="flex items-center justify-between mb-4">
<p className="text-sm font-medium text-slate-500">Utilization</p>
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-blue-400 to-blue-600 flex items-center justify-center shadow-lg shadow-blue-200">
<Percent className="w-5 h-5 text-white" />
</div>
</div>
<p className="text-2xl font-bold text-slate-900">{data.utilization}%</p>
<div className="w-full bg-slate-100 rounded-full h-1.5 mt-3 overflow-hidden">
<div className="h-full bg-green-500" style={{ width: `${data.utilization}%` }}></div>
</div>
</GlassCard>
{/* Min Payment */}
<GlassCard className="p-6">
<div className="flex items-center justify-between mb-4">
<p className="text-sm font-medium text-slate-500">Min Payment Due</p>
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-orange-400 to-orange-600 flex items-center justify-center shadow-lg shadow-orange-200">
<Calendar className="w-5 h-5 text-white" />
</div>
</div>
<div className="flex items-center gap-1">
<DirhamIcon className="w-6 h-6 text-slate-900" />
<p className="text-2xl font-bold text-slate-900">{formatAmount(data.minPayment)}</p>
</div>
<p className="text-xs text-orange-600 font-medium mt-2">Due in 3 days</p>
</GlassCard>
</div>
);
};
export default CreditSummaryCards;

View File

@@ -0,0 +1,48 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { Badge } from '@/components/ui/badge';
import { Progress } from '@/components/ui/progress';
import { formatAmount } from '@/lib/utils';
import { DirhamIcon } from '@/components/ui/custom-icons';
const CreditUtilizationList: React.FC = () => {
const cards = [
{ name: 'Emirates NBD Skywards', balance: 8500, limit: 50000, color: 'bg-indigo-500' },
{ name: 'ADCB TouchPoints', balance: 12000, limit: 25000, color: 'bg-blue-500' },
{ name: 'FAB Cashback', balance: 3200, limit: 15000, color: 'bg-teal-500' },
];
return (
<GlassCard className="p-6 h-full">
<h3 className="text-lg font-semibold text-slate-800 mb-6">Credit Utilization</h3>
<div className="space-y-6">
{cards.map((card, index) => {
const utilization = Math.round((card.balance / card.limit) * 100);
let badgeColor = 'bg-green-100 text-green-700 border-green-200';
if (utilization > 30) badgeColor = 'bg-yellow-100 text-yellow-700 border-yellow-200';
if (utilization > 70) badgeColor = 'bg-red-100 text-red-700 border-red-200';
return (
<div key={index} className="space-y-2">
<div className="flex justify-between items-center">
<span className="font-medium text-slate-700">{card.name}</span>
<Badge variant="outline" className={`${badgeColor} text-xs`}>
{utilization}% Utilized
</Badge>
</div>
<Progress value={utilization} className="h-2" indicatorClassName={card.color} />
<div className="flex justify-between text-xs text-slate-500 mt-1">
<span className="font-semibold text-slate-900 flex items-center gap-0.5"><DirhamIcon className="w-3 h-3" /> {formatAmount(card.balance)}</span>
<span className="flex items-center gap-0.5">Limit: <DirhamIcon className="w-3 h-3" /> {formatAmount(card.limit)}</span>
</div>
</div>
)
})}
</div>
</GlassCard>
);
};
export default CreditUtilizationList;

View File

@@ -0,0 +1,55 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { DirhamIcon } from '@/components/ui/custom-icons';
import { TrendingUp, AlertTriangle, ShieldCheck } from 'lucide-react';
const AIInsights: React.FC = () => {
const insights = [
{
title: 'Portfolio Optimization',
description: 'Rebalance to maintain 60/40 split.',
type: 'optimization',
icon: <TrendingUp className="w-5 h-5" />,
colors: 'bg-green-50 border-green-100 text-green-700',
iconColor: 'bg-green-100 text-green-600'
},
{
title: 'Islamic Investment',
description: 'New Sukuk opportunities available.',
type: 'opportunity',
icon: <DirhamIcon className="w-5 h-5" />,
colors: 'bg-blue-50 border-blue-100 text-blue-700',
iconColor: 'bg-blue-100 text-blue-600'
},
{
title: 'Risk Management',
description: 'Diversify sector exposure.',
type: 'risk',
icon: <ShieldCheck className="w-5 h-5" />,
colors: 'bg-purple-50 border-purple-100 text-purple-700',
iconColor: 'bg-purple-100 text-purple-600'
}
];
return (
<GlassCard className="p-6 h-full">
<h3 className="text-lg font-semibold text-slate-800 mb-4">AI Insights</h3>
<div className="space-y-3">
{insights.map((insight, idx) => (
<div key={idx} className={`p-4 rounded-xl border flex gap-3 items-start ${insight.colors} transition-transform hover:scale-[1.02]`}>
<div className={`p-2 rounded-lg shrink-0 ${insight.iconColor}`}>
{insight.icon}
</div>
<div>
<h4 className="font-semibold text-sm">{insight.title}</h4>
<p className="text-xs opacity-80 mt-0.5">{insight.description}</p>
</div>
</div>
))}
</div>
</GlassCard>
);
};
export default AIInsights;

View File

@@ -0,0 +1,63 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { Button } from '@/components/ui/button';
import { Progress } from '@/components/ui/progress';
import { Target } from 'lucide-react';
const GoalsList: React.FC = () => {
const goals = [
{
name: 'Emergency Fund',
progress: 65,
amount: '45k / 80k',
color: 'bg-green-500',
trackColor: 'bg-green-100'
},
{
name: 'Dubai Marina Apt',
progress: 42,
amount: '850k / 2.0M',
color: 'bg-blue-500',
trackColor: 'bg-blue-100'
},
{
name: 'Retirement Portfolio',
progress: 28,
amount: '1.2M / 4.5M',
color: 'bg-purple-500',
trackColor: 'bg-purple-100'
}
];
return (
<GlassCard className="p-6 h-full flex flex-col">
<div className="flex justify-between items-center mb-6">
<h3 className="text-lg font-semibold text-slate-800 flex items-center gap-2">
<Target className="w-5 h-5 text-purple-500" /> Financial Goals
</h3>
</div>
<div className="flex-1 space-y-6">
{goals.map((goal, idx) => (
<div key={idx} className="space-y-2">
<div className="flex justify-between text-sm">
<span className="font-medium text-slate-700">{goal.name}</span>
<span className="text-slate-500 text-xs font-semibold">{goal.amount}</span>
</div>
<div className="h-2.5 w-full bg-slate-100 rounded-full overflow-hidden">
<div className={`h-full ${goal.color} rounded-full`} style={{ width: `${goal.progress}%` }}></div>
</div>
<p className="text-xs text-right text-slate-400">{goal.progress}% Completed</p>
</div>
))}
</div>
<Button className="w-full mt-6 bg-gradient-to-r from-purple-600 to-indigo-600 hover:from-purple-700 hover:to-indigo-700 text-white shadow-md">
Manage Goals
</Button>
</GlassCard>
);
};
export default GoalsList;

View File

@@ -0,0 +1,85 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { DirhamIcon } from '@/components/ui/custom-icons';
import {
TrendingUp,
DollarSign,
PieChart,
Shield
} from 'lucide-react';
import { formatAmount } from '@/lib/utils'; // Keep import for potential reuse, though hardcoded values requested for now
const HeroStats: React.FC = () => {
// Hardcoded data as per Target Design Reference
const stats = [
{
label: 'Portfolio Value',
value: '2,847,500',
change: '+12.3%',
isPositive: true,
icon: <TrendingUp className="h-6 w-6 text-white" />,
color: 'from-purple-500 to-indigo-600',
iconBg: 'bg-white/20'
},
{
label: 'Monthly Contributions',
value: '12,470',
change: '+5.7%',
isPositive: true,
icon: <DollarSign className="h-6 w-6 text-white" />,
color: 'from-blue-500 to-cyan-600',
iconBg: 'bg-white/20'
},
{
label: 'Annual Returns',
value: '12.7%',
change: '+2.1%',
isPositive: true,
icon: <PieChart className="h-6 w-6 text-white" />,
color: 'from-emerald-500 to-teal-600',
iconBg: 'bg-white/20',
isPercentage: true
},
{
label: 'Risk Score',
value: '3.2/10',
change: '-0.3',
isPositive: true, // Lower risk score change might be positive contextually, but design shows -0.3. Assuming red for negative change generally unless specified. Design says (-0.3).
icon: <Shield className="h-6 w-6 text-white" />,
color: 'from-orange-500 to-red-600',
iconBg: 'bg-white/20',
isScore: true
}
];
return (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
{stats.map((stat, idx) => (
<GlassCard key={idx} className="p-6 relative overflow-hidden group">
<div className="relative z-10">
<div className="flex justify-between items-start mb-4">
<div className={`p-3 rounded-xl bg-gradient-to-br ${stat.color} shadow-lg`}>
{stat.icon}
</div>
<span className={`text-xs font-semibold px-2 py-1 rounded-full flex items-center ${stat.change.startsWith('+') ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'}`}>
{stat.change}
</span>
</div>
<div>
<p className="text-sm text-slate-500 font-medium">{stat.label}</p>
<div className="flex items-center gap-1 mt-1">
{!stat.isPercentage && !stat.isScore && <DirhamIcon className="w-5 h-5 text-slate-800" />}
<h3 className="text-2xl font-bold text-slate-800">{stat.value}</h3>
</div>
</div>
</div>
{/* Decorative background element */}
<div className={`absolute -right-6 -bottom-6 w-24 h-24 rounded-full bg-gradient-to-br ${stat.color} opacity-10 blur-xl group-hover:opacity-20 transition-opacity duration-500`} />
</GlassCard>
))}
</div>
);
};
export default HeroStats;

View File

@@ -0,0 +1,73 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { AreaChart, Area, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts';
import { DirhamIcon } from '@/components/ui/custom-icons';
const InvestmentPerformance: React.FC = () => {
// Dummy chart data for visualization
const data = [
{ name: 'Jan', value: 2400000 },
{ name: 'Feb', value: 2450000 },
{ name: 'Mar', value: 2550000 },
{ name: 'Apr', value: 2500000 },
{ name: 'May', value: 2650000 },
{ name: 'Jun', value: 2847500 },
];
return (
<GlassCard className="p-6 h-full flex flex-col">
<div className="flex justify-between items-center mb-6">
<h3 className="text-lg font-semibold text-slate-800">Investment Performance</h3>
<select className="bg-slate-50 border-none text-sm text-slate-600 rounded-lg p-2 focus:ring-0 cursor-pointer">
<option>Last 6 Months</option>
<option>Year to Date</option>
<option>All Time</option>
</select>
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
<div>
<p className="text-xs text-slate-500">Total Portfolio</p>
<div className="flex items-center gap-1 font-bold text-slate-800 text-lg">
<DirhamIcon className="w-4 h-4" /> 2.85M
</div>
</div>
<div>
<p className="text-xs text-slate-500">Annual Return</p>
<p className="font-bold text-green-600 text-lg">+12.7%</p>
</div>
<div>
<p className="text-xs text-slate-500">Monthly Contrib.</p>
<div className="flex items-center gap-1 font-bold text-slate-800 text-lg">
<DirhamIcon className="w-4 h-4" /> 12.5K
</div>
</div>
<div>
<p className="text-xs text-slate-500">Risk Score</p>
<p className="font-bold text-orange-500 text-lg">3.2/10</p>
</div>
</div>
<div className="flex-1 min-h-[200px] w-full">
<ResponsiveContainer width="100%" height="100%">
<AreaChart data={data}>
<defs>
<linearGradient id="colorValue" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#8b5cf6" stopOpacity={0.3} />
<stop offset="95%" stopColor="#8b5cf6" stopOpacity={0} />
</linearGradient>
</defs>
<XAxis dataKey="name" axisLine={false} tickLine={false} tick={{ fontSize: 12, fill: '#94a3b8' }} />
<Tooltip
contentStyle={{ borderRadius: '12px', border: 'none', boxShadow: '0 4px 6px -1px rgb(0 0 0 / 0.1)' }}
/>
<Area type="monotone" dataKey="value" stroke="#8b5cf6" strokeWidth={3} fillOpacity={1} fill="url(#colorValue)" />
</AreaChart>
</ResponsiveContainer>
</div>
</GlassCard>
);
};
export default InvestmentPerformance;

View File

@@ -0,0 +1,49 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { PlusCircle, Target, FileText, PiggyBank } from 'lucide-react';
import { Button } from '@/components/ui/button';
const QuickActions: React.FC = () => {
const actions = [
{
label: 'New Investment',
icon: <PlusCircle className="w-5 h-5 mb-2" />,
color: 'hover:border-green-200 hover:bg-green-50/50 hover:text-green-700'
},
{
label: 'Set Goals',
icon: <Target className="w-5 h-5 mb-2" />,
color: 'hover:border-blue-200 hover:bg-blue-50/50 hover:text-blue-700'
},
{
label: 'View Reports',
icon: <FileText className="w-5 h-5 mb-2" />,
color: 'hover:border-purple-200 hover:bg-purple-50/50 hover:text-purple-700'
},
{
label: 'Savings Plan',
icon: <PiggyBank className="w-5 h-5 mb-2" />,
color: 'hover:border-pink-200 hover:bg-pink-50/50 hover:text-pink-700'
}
];
return (
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{actions.map((action, idx) => (
<GlassCard
key={idx}
className={`p-4 flex flex-col items-center justify-center cursor-pointer transition-all active:scale-95 ${action.color} group`}
hoverEffect
>
<div className="p-3 rounded-full bg-white shadow-sm mb-2 group-hover:scale-110 transition-transform">
{action.icon}
</div>
<span className="text-sm font-medium text-slate-700">{action.label}</span>
</GlassCard>
))}
</div>
);
};
export default QuickActions;

View File

@@ -0,0 +1,90 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { Button } from '@/components/ui/button';
import { DirhamIcon } from '@/components/ui/custom-icons';
import { ArrowUpRight, ArrowDownRight, Clock } from 'lucide-react';
const TransactionsList: React.FC = () => {
const transactions = [
{
id: 1,
name: 'Emirates NBD',
date: 'Today, 2:30 PM',
amount: '5,000',
status: 'Successful',
type: 'debit',
icon: '🏦'
},
{
id: 2,
name: 'Dubai Properties',
date: 'Yesterday, 10:00 AM',
amount: '12,500',
status: 'Processing',
type: 'debit',
icon: '🏢'
},
{
id: 3,
name: 'Gold Investment',
date: 'Feb 12, 4:15 PM',
amount: '3,200',
status: 'Successful',
type: 'credit', // Assuming investment purchase
icon: '🪙'
},
{
id: 4,
name: 'ADCB Islamic Fund',
date: 'Feb 10, 9:00 AM',
amount: '1,500',
status: 'Successful',
type: 'debit',
icon: '📈'
}
];
return (
<GlassCard className="p-6 h-full flex flex-col">
<div className="flex justify-between items-center mb-6">
<h3 className="text-lg font-semibold text-slate-800">Recent Transactions</h3>
</div>
<div className="flex-1 space-y-4">
{transactions.map((tx) => (
<div key={tx.id} className="flex items-center justify-between p-3 hover:bg-slate-50 rounded-xl transition-colors group">
<div className="flex items-center gap-4">
<div className="w-10 h-10 rounded-full bg-slate-100 flex items-center justify-center text-xl shadow-sm group-hover:scale-105 transition-transform">
{tx.icon}
</div>
<div>
<p className="font-semibold text-slate-800 text-sm">{tx.name}</p>
<div className="flex items-center gap-1 text-xs text-slate-500">
<Clock className="w-3 h-3" /> {tx.date}
</div>
</div>
</div>
<div className="text-right">
<div className="flex items-center justify-end gap-1 font-bold text-slate-800">
<DirhamIcon className="w-3 h-3" /> {tx.amount}
</div>
<span className={`text-xs px-2 py-0.5 rounded-full ${tx.status === 'Successful'
? 'bg-green-50 text-green-600'
: 'bg-yellow-50 text-yellow-600'
}`}>
{tx.status}
</span>
</div>
</div>
))}
</div>
<Button variant="outline" className="w-full mt-6 text-slate-600 hover:text-purple-600 hover:border-purple-200">
View All Transactions
</Button>
</GlassCard>
);
};
export default TransactionsList;

View File

@@ -1,197 +1,81 @@
import React from 'react';
import { motion } from 'framer-motion';
import { GlassCard } from '@/components/ui/GlassCard';
import { Button } from '@/components/ui/button';
import { Progress } from '@/components/ui/progress';
import {
Wallet,
TrendingUp,
Home,
Car,
Utensils,
ShoppingCart,
Target,
ArrowRight,
AlertCircle
} from 'lucide-react';
import { cn, formatCurrency } from '@/lib/utils';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Plus, Settings } from 'lucide-react';
// Mock Data
const budgetData = {
limit: 15000,
spent: 12500,
categories: [
{ name: 'Housing', icon: Home, spent: 6500, limit: 7000, color: 'bg-blue-500' },
{ name: 'Food & Dining', icon: Utensils, spent: 2800, limit: 3000, color: 'bg-green-500' },
{ name: 'Transportation', icon: Car, spent: 1200, limit: 1500, color: 'bg-orange-500' },
{ name: 'Shopping', icon: ShoppingCart, spent: 2000, limit: 1500, color: 'bg-pink-500' }, // Over budget
],
goals: [
{ name: 'Buy a House', target: 800000, current: 200000, color: 'bg-purple-500' },
{ name: 'Retirement', target: 2000000, current: 150000, color: 'bg-indigo-500' },
]
};
// Import New Modular Budget Components
import BudgetSummaryCards from '@/components/budget/BudgetSummaryCards';
import BudgetOverviewList from '@/components/budget/BudgetOverviewList';
import RecentTransactionsCard from '@/components/budget/RecentTransactionsCard';
import BudgetCategoryGrid from '@/components/budget/BudgetCategoryGrid';
import FullExpensesTable from '@/components/budget/FullExpensesTable';
import AddExpenseForm from '@/components/budget/AddExpenseForm';
import SetBudgetForm from '@/components/budget/SetBudgetForm';
export default function BudgetManager() {
const percentage = Math.min(100, (budgetData.spent / budgetData.limit) * 100);
const circumference = 2 * Math.PI * 120; // Radius 120
const strokeDashoffset = circumference - (percentage / 100) * circumference;
return (
<div className="max-w-6xl mx-auto space-y-8 pb-20 animate-fade-in">
{/* Header */}
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
<div className="space-y-6 animate-fade-in text-slate-800 pb-10">
{/* Header Section */}
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4 mb-8">
<div>
<h1 className="text-2xl sm:text-3xl font-bold text-slate-900">Monthly Budget & Goals</h1>
<p className="text-sm sm:text-base text-slate-500">Track your spending and save for the future.</p>
<h1 className="text-3xl font-light tracking-tight text-slate-900">
Budget <span className="font-semibold bg-clip-text text-transparent bg-gradient-to-r from-green-600 to-teal-600">Manager</span>
</h1>
<p className="text-slate-500 mt-1">Track every dirham with precision.</p>
</div>
<Button className="w-full sm:w-auto gap-2 bg-slate-900 text-white hover:bg-slate-800 rounded-full">
<Wallet className="w-4 h-4" /> Edit Budget
</Button>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 sm:gap-8">
{/* Main Budget Ring */}
<GlassCard className="col-span-1 lg:col-span-1 flex flex-col items-center justify-center p-6 sm:p-8 relative overflow-hidden">
<div className="relative w-64 h-64 sm:w-72 sm:h-72 flex items-center justify-center">
{/* Background Circle */}
<svg className="w-full h-full transform -rotate-90">
<circle
cx="50%"
cy="50%"
r="45%"
stroke="currentColor"
strokeWidth="24"
fill="transparent"
className="text-slate-100"
/>
{/* Progress Circle */}
<motion.circle
initial={{ strokeDashoffset: circumference }}
animate={{ strokeDashoffset }}
transition={{ duration: 1.5, ease: "easeOut" }}
cx="50%"
cy="50%"
r="45%"
stroke="currentColor"
strokeWidth="24"
fill="transparent"
strokeDasharray={circumference}
strokeLinecap="round"
className={cn(
"text-blue-500 drop-shadow-lg",
percentage > 90 ? "text-red-500" : percentage > 75 ? "text-orange-500" : "text-blue-500"
)}
/>
</svg>
<div className="absolute flex flex-col items-center">
<span className="text-xs sm:text-sm text-slate-500 font-medium uppercase tracking-wider">Total Spent</span>
<span className="text-3xl sm:text-4xl font-bold text-slate-900 mt-1">
{formatCurrency(budgetData.spent)}
</span>
<span className="text-xs sm:text-sm text-slate-400 mt-1">
of {formatCurrency(budgetData.limit)}
</span>
</div>
</div>
{percentage > 90 && (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="mt-6 flex items-center gap-2 text-red-600 bg-red-50 px-4 py-2 rounded-full border border-red-100"
>
<AlertCircle className="w-4 h-4" />
<span className="text-sm font-medium">You've used {percentage.toFixed(0)}% of your budget!</span>
</motion.div>
)}
</GlassCard>
{/* Categories & Goals */}
<div className="col-span-1 lg:col-span-2 space-y-8">
{/* Categories Breakdown */}
<GlassCard className="p-6">
<h3 className="text-lg font-semibold mb-6 flex items-center gap-2">
<Utensils className="w-5 h-5 text-slate-500" /> Category Breakdown
</h3>
<div className="space-y-6">
{budgetData.categories.map((cat, index) => (
<motion.div
key={cat.name}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: index * 0.1 }}
className="space-y-2"
>
<div className="flex justify-between items-center text-sm">
<div className="flex items-center gap-2">
<div className={cn("p-1.5 rounded-lg text-white", cat.color)}>
<cat.icon className="w-3.5 h-3.5" />
</div>
<span className="font-medium text-slate-700">{cat.name}</span>
</div>
<div className="flex items-center gap-2">
<span className="font-semibold">{formatCurrency(cat.spent)}</span>
<span className="text-slate-400">/ {formatCurrency(cat.limit)}</span>
</div>
</div>
<div className="h-2.5 bg-slate-100 rounded-full overflow-hidden">
<motion.div
initial={{ width: 0 }}
animate={{ width: `${Math.min(100, (cat.spent / cat.limit) * 100)}%` }}
transition={{ duration: 1, delay: 0.5 + (index * 0.1) }}
className={cn("h-full rounded-full", cat.color)}
/>
</div>
</motion.div>
))}
</div>
</GlassCard>
{/* Long-term Goals */}
<div>
<h3 className="text-lg font-semibold mb-4 flex items-center gap-2">
<Target className="w-5 h-5 text-slate-500" /> Long-term Planning
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{budgetData.goals.map((goal, index) => (
<GlassCard key={goal.name} className="p-5 relative overflow-hidden group">
<div className="relative z-10">
<div className="flex justify-between items-start mb-4">
<div>
<h4 className="font-semibold text-slate-900">{goal.name}</h4>
<div className="bg-green-100 text-green-700 text-xs px-2 py-0.5 rounded-full inline-block mt-1 font-medium">
On Track
</div>
</div>
<div className={cn("p-2 rounded-xl text-white opacity-80", goal.color)}>
<Target className="w-5 h-5" />
</div>
</div>
<div className="space-y-2">
<div className="flex justify-between text-sm">
<span className="text-slate-500">Progress</span>
<span className="font-bold text-slate-900">{Math.round((goal.current / goal.target) * 100)}%</span>
</div>
<Progress value={(goal.current / goal.target) * 100} className="h-2" indicatorClassName={goal.color} />
<div className="flex justify-between text-xs text-slate-400 pt-1">
<span>{formatCurrency(goal.current)}</span>
<span>{formatCurrency(goal.target)}</span>
</div>
</div>
</div>
{/* Hover Effect Background */}
<div className={cn(
"absolute inset-0 opacity-0 group-hover:opacity-10 transition-opacity duration-500",
goal.color
)} />
</GlassCard>
))}
</div>
</div>
<div className="flex gap-3">
<Button variant="outline" className="border-slate-200 text-slate-700 bg-white hover:bg-slate-50">
<Plus className="w-4 h-4 mr-2" /> Add Expense
</Button>
<Button className="bg-black text-white hover:bg-slate-800 shadow-md">
<Settings className="w-4 h-4 mr-2" /> Set Budget
</Button>
</div>
</div>
{/* Summary Cards */}
<BudgetSummaryCards />
{/* Main Content Tabs */}
<Tabs defaultValue="overview" className="space-y-6 w-full">
<TabsList className="bg-white/60 p-1.5 rounded-2xl backdrop-blur-md border border-white/40 inline-flex w-auto flex-wrap lg:flex-nowrap shadow-sm h-auto">
<TabsTrigger value="overview" className="rounded-xl px-4 lg:px-6 py-2.5 text-sm font-medium data-[state=active]:bg-white data-[state=active]:text-slate-900 data-[state=active]:shadow-sm text-slate-500 transition-all">Overview</TabsTrigger>
<TabsTrigger value="budgets" className="rounded-xl px-4 lg:px-6 py-2.5 text-sm font-medium data-[state=active]:bg-white data-[state=active]:text-slate-900 data-[state=active]:shadow-sm text-slate-500 transition-all">Budgets</TabsTrigger>
<TabsTrigger value="expenses" className="rounded-xl px-4 lg:px-6 py-2.5 text-sm font-medium data-[state=active]:bg-white data-[state=active]:text-slate-900 data-[state=active]:shadow-sm text-slate-500 transition-all">Expenses</TabsTrigger>
<TabsTrigger value="add-expense" className="rounded-xl px-4 lg:px-6 py-2.5 text-sm font-medium data-[state=active]:bg-white data-[state=active]:text-slate-900 data-[state=active]:shadow-sm text-slate-500 transition-all">Add Expense</TabsTrigger>
<TabsTrigger value="set-budget" className="rounded-xl px-4 lg:px-6 py-2.5 text-sm font-medium data-[state=active]:bg-white data-[state=active]:text-slate-900 data-[state=active]:shadow-sm text-slate-500 transition-all">Set Budget</TabsTrigger>
</TabsList>
{/* Tab 1: Overview */}
<TabsContent value="overview" className="outline-none animate-in fade-in slide-in-from-bottom-2 duration-500">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 h-auto lg:h-[450px]">
<BudgetOverviewList />
<RecentTransactionsCard />
</div>
</TabsContent>
{/* Tab 2: Budgets */}
<TabsContent value="budgets" className="outline-none animate-in fade-in slide-in-from-bottom-2 duration-500">
<BudgetCategoryGrid />
</TabsContent>
{/* Tab 3: Expenses */}
<TabsContent value="expenses" className="outline-none animate-in fade-in slide-in-from-bottom-2 duration-500">
<FullExpensesTable />
</TabsContent>
{/* Tab 4: Add Expense */}
<TabsContent value="add-expense" className="outline-none animate-in fade-in slide-in-from-bottom-2 duration-500">
<AddExpenseForm />
</TabsContent>
{/* Tab 5: Set Budget */}
<TabsContent value="set-budget" className="outline-none animate-in fade-in slide-in-from-bottom-2 duration-500">
<SetBudgetForm />
</TabsContent>
</Tabs>
</div>
);
}

View File

@@ -1,187 +1,65 @@
import React, { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { GlassCard } from '@/components/ui/GlassCard';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import {
Calendar,
List,
CheckCircle,
AlertCircle,
CreditCard,
Home,
Zap,
Clock,
ArrowRight
} from 'lucide-react';
import { cn } from '@/lib/utils';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Calendar, Plus } from 'lucide-react';
// Mock Data
const commitments = [
{
id: 1,
title: 'Rent Payment',
amount: 6500,
dueDate: '2024-10-01',
daysLeft: 3,
icon: Home,
status: 'Due Soon',
color: 'bg-blue-100 text-blue-600'
},
{
id: 2,
title: 'Car Loan EMI',
amount: 2100,
dueDate: '2024-10-05',
daysLeft: 7,
icon: CreditCard,
status: 'Upcoming',
color: 'bg-purple-100 text-purple-600'
},
{
id: 3,
title: 'Electricity Bill',
amount: 450,
dueDate: '2024-10-10',
daysLeft: 12,
icon: Zap,
status: 'Upcoming',
color: 'bg-yellow-100 text-yellow-600'
}
];
const aiTips = [
{
title: 'Automate Rent',
desc: 'Set up auto-pay to avoid late fees (AED 200 potential saving)',
color: 'border-blue-200 bg-blue-50'
},
{
title: 'Subscription Alert',
desc: 'Unused Gym membership detected. Cancel to save AED 350.',
color: 'border-red-200 bg-red-50'
}
];
// Import New Modular Commitment Components
import UpcomingCommitmentsList from '@/components/commitment/UpcomingCommitmentsList';
import CommitmentAITips from '@/components/commitment/CommitmentAITips';
import CommitmentCalendarTable from '@/components/commitment/CommitmentCalendarTable';
import AddCommitmentForm from '@/components/commitment/AddCommitmentForm';
export default function CommitmentAdvisor() {
const [view, setView] = useState<'list' | 'calendar'>('list');
return (
<div className="max-w-6xl mx-auto space-y-6 sm:space-y-8 pb-20 animate-fade-in">
<div className="header flex flex-col sm:flex-row justify-between items-start sm:items-end gap-4">
<div>
<h1 className="text-2xl sm:text-3xl font-bold text-slate-900 flex items-center gap-3">
<Calendar className="w-6 h-6 sm:w-8 sm:h-8 text-blue-600" /> Commitment Advisor
</h1>
<p className="text-sm sm:text-base text-slate-500 mt-1">Stay on top of your bills and financial obligations.</p>
</div>
<div className="space-y-6 animate-fade-in text-slate-800 pb-10">
<div className="bg-slate-100 p-1 rounded-lg flex w-full sm:w-auto">
<button
onClick={() => setView('list')}
className={cn("flex-1 sm:flex-none px-3 py-1.5 rounded-md text-sm font-medium transition-all flex items-center justify-center gap-2", view === 'list' ? "bg-white shadow-sm text-slate-900" : "text-slate-500 hover:text-slate-700")}
>
<List className="w-4 h-4" /> List
</button>
<button
onClick={() => setView('calendar')}
className={cn("flex-1 sm:flex-none px-3 py-1.5 rounded-md text-sm font-medium transition-all flex items-center justify-center gap-2", view === 'calendar' ? "bg-white shadow-sm text-slate-900" : "text-slate-500 hover:text-slate-700")}
>
<Calendar className="w-4 h-4" /> Calendar
</button>
{/* Header Section */}
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4 mb-6">
<div>
<h1 className="text-3xl font-light tracking-tight text-slate-900 flex items-center gap-3">
<Calendar className="w-6 h-6 text-blue-600" />
<span>Commitment <span className="font-semibold bg-clip-text text-transparent bg-gradient-to-r from-blue-600 to-cyan-600">Advisor</span></span>
</h1>
<p className="text-slate-500 mt-1 ml-9">Stay on top of your bills and financial obligations.</p>
</div>
<div>
<Button variant="outline" className="border-slate-200 text-slate-700 bg-white hover:bg-slate-50">
<Plus className="w-4 h-4 mr-2" /> Add Commitment
</Button>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 sm:gap-8">
{/* Main Content */}
<div className="col-span-1 lg:col-span-2 space-y-4">
<AnimatePresence mode="wait">
{view === 'list' ? (
<motion.div
key="list"
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: 20 }}
className="space-y-4"
>
{commitments.map((item) => (
<GlassCard key={item.id} className="p-5 flex items-center justify-between group hover:border-blue-200 transition-all">
<div className="flex items-center gap-4">
<div className={cn("p-3 rounded-xl", item.color)}>
<item.icon className="w-6 h-6" />
</div>
<div>
<h3 className="font-semibold text-slate-900">{item.title}</h3>
<div className="flex items-center gap-2 text-sm text-slate-500">
<Clock className="w-3.5 h-3.5" /> Due {item.dueDate}
</div>
</div>
</div>
{/* Main Content Tabs */}
<Tabs defaultValue="overview" className="space-y-6 w-full">
<TabsList className="bg-white/60 p-1.5 rounded-2xl backdrop-blur-md border border-white/40 inline-flex w-auto flex-wrap lg:flex-nowrap shadow-sm h-auto">
<TabsTrigger value="overview" className="rounded-xl px-4 lg:px-6 py-2.5 text-sm font-medium data-[state=active]:bg-white data-[state=active]:text-slate-900 data-[state=active]:shadow-sm text-slate-500 transition-all">Overview</TabsTrigger>
<TabsTrigger value="calendar" className="rounded-xl px-4 lg:px-6 py-2.5 text-sm font-medium data-[state=active]:bg-white data-[state=active]:text-slate-900 data-[state=active]:shadow-sm text-slate-500 transition-all">Calendar</TabsTrigger>
<TabsTrigger value="add" className="rounded-xl px-4 lg:px-6 py-2.5 text-sm font-medium data-[state=active]:bg-white data-[state=active]:text-slate-900 data-[state=active]:shadow-sm text-slate-500 transition-all">Add</TabsTrigger>
</TabsList>
<div className="text-right flex items-center gap-6">
<div>
<p className="font-bold text-lg text-slate-900">AED {item.amount.toLocaleString()}</p>
<Badge variant={item.daysLeft <= 3 ? "destructive" : "secondary"}>
{item.status} ({item.daysLeft} days)
</Badge>
</div>
<Button size="icon" variant="ghost" className="opacity-0 group-hover:opacity-100 transition-opacity">
<CheckCircle className="w-6 h-6 text-green-500" />
</Button>
</div>
</GlassCard>
))}
</motion.div>
) : (
<motion.div
key="calendar"
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
>
<GlassCard className="p-8 text-center min-h-[400px] flex flex-col items-center justify-center">
<Calendar className="w-16 h-16 text-slate-200 mb-4" />
<h3 className="text-lg font-semibold text-slate-700">Calendar View</h3>
<p className="text-slate-500 max-w-sm">
Calendar visualization is currently in development. Please use the List view to manage your upcoming payments.
</p>
</GlassCard>
</motion.div>
)}
</AnimatePresence>
</div>
{/* Sidebar */}
<div className="col-span-1 space-y-6">
<div className="bg-slate-900 text-white p-6 rounded-3xl relative overflow-hidden">
<div className="relative z-10">
<h3 className="font-bold text-lg mb-2">Total Due this Month</h3>
<p className="text-3xl font-bold">AED 9,050</p>
<p className="text-slate-400 text-sm mt-1">3 payments remaining</p>
<div className="h-1.5 w-full bg-slate-800 rounded-full mt-4 overflow-hidden">
<div className="h-full bg-blue-500 w-2/3 rounded-full" />
</div>
{/* Tab 1: Overview */}
<TabsContent value="overview" className="outline-none animate-in fade-in slide-in-from-bottom-2 duration-500">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="lg:col-span-2 h-auto">
<UpcomingCommitmentsList />
</div>
<div className="absolute top-0 right-0 p-4 opacity-10">
<CreditCard className="w-32 h-32" />
<div className="lg:col-span-1 h-auto">
<CommitmentAITips />
</div>
</div>
</TabsContent>
<h3 className="font-bold text-slate-800 flex items-center gap-2">
<Zap className="w-5 h-5 text-yellow-500" /> AI Insights
</h3>
{/* Tab 2: Calendar */}
<TabsContent value="calendar" className="outline-none animate-in fade-in slide-in-from-bottom-2 duration-500">
<CommitmentCalendarTable />
</TabsContent>
{aiTips.map((tip, idx) => (
<GlassCard key={idx} className={cn("p-4 border-l-4", tip.color)}>
<h4 className="font-semibold text-sm mb-1">{tip.title}</h4>
<p className="text-xs text-slate-600">{tip.desc}</p>
<Button variant="link" className="h-auto p-0 text-xs mt-2 text-slate-900">
Take Action <ArrowRight className="w-3 h-3 ml-1" />
</Button>
</GlassCard>
))}
</div>
</div>
{/* Tab 3: Add */}
<TabsContent value="add" className="outline-none animate-in fade-in slide-in-from-bottom-2 duration-500">
<AddCommitmentForm />
</TabsContent>
</Tabs>
</div>
);
}

View File

@@ -1,179 +1,75 @@
import React from 'react';
import { motion } from 'framer-motion';
import { GlassCard } from '@/components/ui/GlassCard';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Progress } from '@/components/ui/progress';
import {
ShieldCheck,
TrendingUp,
CreditCard,
AlertTriangle,
ArrowRight,
Snowflake,
Landmark
} from 'lucide-react';
import { cn } from '@/lib/utils';
import { ResponsiveContainer, BarChart, Bar, XAxis, YAxis, Tooltip, Legend } from 'recharts';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { ShieldCheck, FileText, Plus } from 'lucide-react';
// Mock Data
const creditData = {
score: 768,
status: 'Excellent',
history: [
{ month: 'Jan', score: 720 },
{ month: 'Mar', score: 745 },
{ month: 'Jun', score: 755 },
{ month: 'Sep', score: 768 },
],
debts: [
{ name: 'Credit Card A', principal: 15000, interest: 2500, rate: '18%', minPay: 800 },
{ name: 'Personal Loan', principal: 45000, interest: 8000, rate: '12%', minPay: 2200 },
{ name: 'Car Loan', principal: 85000, interest: 12000, rate: '9%', minPay: 3500 },
],
strategy: {
method: 'Snowball',
recommendation: 'Pay off Credit Card A first to save AED 450 in interest.',
savedInterest: 450
}
};
const CreditGauge = ({ score }: { score: number }) => {
const percentage = (score / 900) * 100;
const circumference = 2 * Math.PI * 80;
const strokeDashoffset = circumference - (percentage / 100) * circumference;
return (
<div className="relative w-48 h-48 flex items-center justify-center">
<svg className="w-full h-full transform -rotate-90">
<circle
cx="96"
cy="96"
r="80"
stroke="#e2e8f0"
strokeWidth="16"
fill="transparent"
/>
<motion.circle
initial={{ strokeDashoffset: circumference }}
animate={{ strokeDashoffset }}
transition={{ duration: 1.5, ease: "easeOut" }}
cx="96"
cy="96"
r="80"
stroke="#10b981" // Green-500
strokeWidth="16"
fill="transparent"
strokeDasharray={circumference}
strokeLinecap="round"
/>
</svg>
<div className="absolute flex flex-col items-center">
<span className="text-4xl font-bold text-slate-900">{score}</span>
<Badge className="bg-green-100 text-green-700 hover:bg-green-100 mt-1">Excellent</Badge>
</div>
</div>
);
};
// Import New Modular Credit Components
import CreditSummaryCards from '@/components/credit/CreditSummaryCards';
import CreditScoreProgress from '@/components/credit/CreditScoreProgress';
import CreditUtilizationList from '@/components/credit/CreditUtilizationList';
import CreditCardsList from '@/components/credit/CreditCardsList';
import CreditScoreTips from '@/components/credit/CreditScoreTips';
import AECBInfoCard from '@/components/credit/AECBInfoCard';
export default function CreditManager() {
return (
<div className="max-w-6xl mx-auto space-y-6 sm:space-y-8 pb-20 animate-fade-in">
<div className="header">
<h1 className="text-2xl sm:text-3xl font-bold text-slate-900 flex items-center gap-3">
<ShieldCheck className="w-6 h-6 sm:w-8 sm:h-8 text-green-600" /> AI Credit Slashing System
</h1>
<p className="text-sm sm:text-base text-slate-500 mt-1">Master your credit health and eliminate debt faster.</p>
</div>
<div className="space-y-6 animate-fade-in text-slate-800 pb-10">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 sm:gap-8">
{/* Credit Health Score */}
<GlassCard className="col-span-1 p-6 flex flex-col items-center justify-center space-y-4">
<h3 className="font-semibold text-slate-700 self-start w-full flex justify-between items-center">
Credit Health
<span className="text-green-600 text-xs sm:text-sm flex items-center bg-green-50 px-2 py-1 rounded">
<TrendingUp className="w-3 h-3 mr-1" /> +13 pts
</span>
</h3>
<CreditGauge score={creditData.score} />
<p className="text-sm text-center text-slate-500 max-w-[200px]">
Your score is higher than 85% of users in UAE. Keep it up!
</p>
</GlassCard>
{/* Strategy & Visualizer */}
<div className="col-span-1 lg:col-span-2 space-y-6">
{/* Strategy Card */}
<GlassCard className="p-6 bg-gradient-to-r from-blue-50 to-indigo-50 border-blue-100">
<div className="flex items-start gap-4">
<div className="p-3 bg-white rounded-xl shadow-sm text-blue-600">
<Snowflake className="w-6 h-6" />
</div>
<div>
<h3 className="text-lg font-bold text-slate-900">Strategy: {creditData.strategy.method} Method Recommended</h3>
<p className="text-slate-600 mt-1">{creditData.strategy.recommendation}</p>
<Button size="sm" className="mt-4 bg-blue-600 hover:bg-blue-700 text-white gap-2">
Apply Strategy <ArrowRight className="w-4 h-4" />
</Button>
</div>
</div>
</GlassCard>
{/* Debt Visualizer */}
<GlassCard className="p-6">
<h3 className="font-semibold text-slate-700 mb-6 flex items-center gap-2">
<Landmark className="w-5 h-5 text-slate-500" /> Debt Breakdown
</h3>
<div className="h-64 w-full">
<ResponsiveContainer width="100%" height="100%">
<BarChart data={creditData.debts} layout="vertical" barSize={20}>
<XAxis type="number" hide />
<YAxis dataKey="name" type="category" width={100} tick={{ fontSize: 12 }} />
<Tooltip cursor={{ fill: 'transparent' }} contentStyle={{ borderRadius: '8px' }} />
<Legend />
<Bar dataKey="principal" stackId="a" fill="#94a3b8" name="Principal" radius={[0, 0, 0, 0]} />
<Bar dataKey="interest" stackId="a" fill="#ef4444" name="Interest" radius={[0, 10, 10, 0]} />
</BarChart>
</ResponsiveContainer>
</div>
</GlassCard>
{/* Header Section */}
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4 mb-8">
<div>
<h1 className="text-3xl font-light tracking-tight text-slate-900 flex items-center gap-3">
<ShieldCheck className="w-6 h-6 text-green-600" />
<span>Credit <span className="font-semibold bg-clip-text text-transparent bg-gradient-to-r from-blue-600 to-indigo-600">Manager</span></span>
</h1>
<p className="text-slate-500 mt-1 ml-9">Master your credit health and eliminate debt.</p>
</div>
<div className="flex gap-3">
<Button variant="outline" className="border-slate-200 text-slate-700 bg-white hover:bg-slate-50">
<FileText className="w-4 h-4 mr-2" /> Credit Report
</Button>
<Button className="bg-black text-white hover:bg-slate-800 shadow-md">
<Plus className="w-4 h-4 mr-2" /> Apply for Card
</Button>
</div>
</div>
{/* Offers / Cards */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<GlassCard className="p-5 flex items-center gap-4 bg-gradient-to-br from-slate-900 to-slate-800 text-white">
<div className="p-3 bg-white/10 rounded-xl">
<CreditCard className="w-6 h-6 text-white" />
</div>
<div>
<p className="text-sm text-slate-300">Total Credit Utilization</p>
<p className="text-2xl font-bold">12%</p>
</div>
<Progress value={12} className="w-16 h-16 ml-auto rounded-full" />
</GlassCard>
{/* Summary Cards */}
<CreditSummaryCards />
<GlassCard className="p-5 flex items-center justify-between">
<div className="space-y-1">
<p className="font-semibold text-slate-900">Next Payment</p>
<p className="text-sm text-slate-500">Credit Card A Due in 3 days</p>
</div>
<div className="text-right">
<p className="font-bold text-slate-900">AED 800</p>
<Button size="sm" variant="outline" className="h-7 text-xs mt-1">Pay Now</Button>
</div>
</GlassCard>
{/* Main Content Tabs */}
<Tabs defaultValue="overview" className="space-y-6 w-full">
<TabsList className="bg-white/60 p-1.5 rounded-2xl backdrop-blur-md border border-white/40 inline-flex w-auto flex-wrap lg:flex-nowrap shadow-sm h-auto">
<TabsTrigger value="overview" className="rounded-xl px-4 lg:px-6 py-2.5 text-sm font-medium data-[state=active]:bg-white data-[state=active]:text-slate-900 data-[state=active]:shadow-sm text-slate-500 transition-all">Overview</TabsTrigger>
<TabsTrigger value="cards" className="rounded-xl px-4 lg:px-6 py-2.5 text-sm font-medium data-[state=active]:bg-white data-[state=active]:text-slate-900 data-[state=active]:shadow-sm text-slate-500 transition-all">Cards</TabsTrigger>
<TabsTrigger value="score-tips" className="rounded-xl px-4 lg:px-6 py-2.5 text-sm font-medium data-[state=active]:bg-white data-[state=active]:text-slate-900 data-[state=active]:shadow-sm text-slate-500 transition-all">Score Tips</TabsTrigger>
</TabsList>
<GlassCard className="p-5 border-yellow-200 bg-yellow-50/50">
<div className="flex gap-3">
<AlertTriangle className="w-5 h-5 text-yellow-600" />
<div>
<h4 className="font-semibold text-yellow-800 text-sm">Action Required</h4>
<p className="text-xs text-yellow-700 mt-1">Car Loan interest rate revised to 9.2%. Review refinance options.</p>
{/* Tab 1: Overview */}
<TabsContent value="overview" className="outline-none animate-in fade-in slide-in-from-bottom-2 duration-500">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 h-auto lg:h-[450px]">
<CreditScoreProgress />
<CreditUtilizationList />
</div>
</TabsContent>
{/* Tab 2: Cards */}
<TabsContent value="cards" className="outline-none animate-in fade-in slide-in-from-bottom-2 duration-500">
<CreditCardsList />
</TabsContent>
{/* Tab 3: Score Tips */}
<TabsContent value="score-tips" className="outline-none animate-in fade-in slide-in-from-bottom-2 duration-500">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div className="space-y-4">
<h3 className="text-lg font-semibold text-slate-800">Tips to Improve</h3>
<CreditScoreTips />
</div>
<AECBInfoCard />
</div>
</GlassCard>
</div>
</TabsContent>
</Tabs>
</div>
);
}

View File

@@ -21,7 +21,8 @@ import {
AlertCircle,
Plus
} from 'lucide-react';
import { formatCurrency } from '@/lib/utils';
import { cn, formatAmount } from '@/lib/utils';
import { DirhamIcon } from '@/components/ui/custom-icons';
const GoalPlanning: React.FC = () => {
const financialGoals = [
@@ -262,7 +263,10 @@ const GoalPlanning: React.FC = () => {
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-muted-foreground">Target Amount</p>
<p className="text-2xl font-bold">{formatCurrency(progressInsights.totalTargetAmount)}</p>
<div className="flex items-center gap-1">
<DirhamIcon className="w-5 h-5 text-slate-900" />
<p className="text-2xl font-bold">{formatAmount(progressInsights.totalTargetAmount)}</p>
</div>
<p className="text-sm text-muted-foreground">Total across goals</p>
</div>
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-blue-400 to-blue-600 flex items-center justify-center">
@@ -277,7 +281,10 @@ const GoalPlanning: React.FC = () => {
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-muted-foreground">Monthly SIP</p>
<p className="text-2xl font-bold">{formatCurrency(progressInsights.monthlyCommitment)}</p>
<div className="flex items-center gap-1">
<DirhamIcon className="w-5 h-5 text-slate-900" />
<p className="text-2xl font-bold">{formatAmount(progressInsights.monthlyCommitment)}</p>
</div>
<p className="text-sm text-muted-foreground">Total commitment</p>
</div>
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-purple-400 to-purple-600 flex items-center justify-center">
@@ -334,7 +341,9 @@ const GoalPlanning: React.FC = () => {
<div>
<div className="flex justify-between text-sm mb-2">
<span>Progress</span>
<span>{formatCurrency(goal.currentAmount)} / {formatCurrency(goal.targetAmount)}</span>
<div className="flex items-center gap-1">
<DirhamIcon className="w-3 h-3" /> {formatAmount(goal.currentAmount)} / <DirhamIcon className="w-3 h-3" /> {formatAmount(goal.targetAmount)}
</div>
</div>
<Progress value={(goal.currentAmount / goal.targetAmount) * 100} className="h-3" />
<p className="text-xs text-muted-foreground mt-1">
@@ -345,7 +354,9 @@ const GoalPlanning: React.FC = () => {
<div className="grid grid-cols-2 gap-4 text-sm">
<div>
<p className="text-muted-foreground">Monthly SIP</p>
<p className="font-semibold">{formatCurrency(goal.monthlyContribution)}</p>
<div className="flex items-center gap-1 font-semibold">
<DirhamIcon className="w-3 h-3" /> {formatAmount(goal.monthlyContribution)}
</div>
</div>
<div>
<p className="text-muted-foreground">Time Remaining</p>
@@ -406,15 +417,21 @@ const GoalPlanning: React.FC = () => {
<div className="grid grid-cols-3 gap-4 text-sm">
<div>
<p className="text-muted-foreground">Current</p>
<p className="font-semibold">{formatCurrency(goal.currentAmount)}</p>
<div className="flex items-center gap-0.5 font-semibold">
<DirhamIcon className="w-3 h-3" /> {formatAmount(goal.currentAmount)}
</div>
</div>
<div>
<p className="text-muted-foreground">Monthly SIP</p>
<p className="font-semibold">{formatCurrency(goal.monthlyContribution)}</p>
<div className="flex items-center gap-0.5 font-semibold">
<DirhamIcon className="w-3 h-3" /> {formatAmount(goal.monthlyContribution)}
</div>
</div>
<div>
<p className="text-muted-foreground">Remaining</p>
<p className="font-semibold">{formatCurrency(goal.targetAmount - goal.currentAmount)}</p>
<div className="flex items-center gap-0.5 font-semibold">
<DirhamIcon className="w-3 h-3" /> {formatAmount(goal.targetAmount - goal.currentAmount)}
</div>
</div>
</div>
</div>
@@ -511,30 +528,26 @@ const GoalPlanning: React.FC = () => {
<div className="space-y-3">
{goal.milestones.map((milestone, index) => (
<div key={index} className="flex items-center justify-between p-3 rounded-lg bg-gray-50">
<div className="flex items-center">
<div className="flex items-center gap-3 w-full">
{milestone.completed ? (
<CheckCircle className="w-5 h-5 text-green-500 mr-3" />
<CheckCircle className="w-5 h-5 text-green-500 shrink-0" />
) : (
<Clock className="w-5 h-5 text-gray-400 mr-3" />
<Clock className="w-5 h-5 text-gray-400 shrink-0" />
)}
<div>
<p className="font-medium">{formatCurrency(milestone.amount)}</p>
<p className="text-sm text-muted-foreground">
Target: {new Date(milestone.date).toLocaleDateString()}
</p>
<div className="flex-1 flex justify-between items-center text-sm">
<div className="flex items-center gap-1 font-medium">
<DirhamIcon className="w-3 h-3" /> {formatAmount(milestone.amount)}
</div>
<span className={cn(
"px-2.5 py-0.5 rounded-full text-xs font-medium border",
milestone.completed
? "bg-green-50 text-green-700 border-green-200"
: "bg-slate-50 text-slate-600 border-slate-200"
)}>
{milestone.completed ? 'Completed' : 'Pending'}
</span>
</div>
</div>
<div className="text-right">
{milestone.completed ? (
<Badge className="text-green-600 bg-green-50 border-green-200">
Completed
</Badge>
) : (
<Badge className="text-gray-600 bg-gray-50 border-gray-200">
Pending
</Badge>
)}
</div>
</div>
))}
</div>
@@ -545,7 +558,7 @@ const GoalPlanning: React.FC = () => {
</Card>
</TabsContent>
</Tabs>
</div>
</div >
);
};

View File

@@ -1,21 +1,17 @@
import React, { useState } from 'react';
import { CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { GlassCard } from '@/components/ui/GlassCard';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { formatCurrency } from '@/lib/utils';
import {
TrendingUp,
TrendingDown,
BarChart3,
Target,
ArrowUpRight,
ArrowDownRight,
PieChart,
Wallet,
Coins // Replaced IndianRupee with Coins as a generic financial icon or could use a custom Dirham icon
} from 'lucide-react';
import { formatAmount } from '@/lib/utils';
import { DirhamIcon } from '@/components/ui/custom-icons';
import { Plus } from 'lucide-react';
// Import New Modular Portfolio Components
import PortfolioSummaryCards from '@/components/portfolio/PortfolioSummaryCards';
import AssetAllocation from '@/components/portfolio/AssetAllocation';
import QuickInvestmentForm from '@/components/portfolio/QuickInvestmentForm';
import HoldingsTable from '@/components/portfolio/HoldingsTable';
import RiskAnalysis from '@/components/portfolio/RiskAnalysis';
import PortfolioAIInsights from '@/components/portfolio/PortfolioAIInsights';
const PortfolioManager: React.FC = () => {
const [selectedPeriod, setSelectedPeriod] = useState('1M');
@@ -76,28 +72,28 @@ const PortfolioManager: React.FC = () => {
}
];
const sectorAllocation = [
{ sector: 'Banking', percentage: 35, value: 122500, color: 'from-blue-400 to-blue-600' },
{ sector: 'Real Estate', percentage: 25, value: 87500, color: 'from-green-400 to-green-600' },
{ sector: 'Utilities', percentage: 20, value: 70000, color: 'from-purple-400 to-purple-600' },
{ sector: 'Telecom', percentage: 15, value: 52500, color: 'from-red-400 to-red-600' },
{ sector: 'Others', percentage: 5, value: 17500, color: 'from-yellow-400 to-yellow-600' }
];
const recommendations = [
{
type: 'rebalance',
title: 'Rebalance Portfolio',
description: 'Your real estate allocation is 5% above target. Consider reducing exposure.',
priority: 'medium',
action: `Sell ${formatCurrency(5000)} worth of real estate stocks`
description: 'Your real estate exposure is above target allocation.',
priority: 'high',
action: (
<span className="flex items-center gap-1">
Sell <DirhamIcon className="w-3 h-3 inline" /> {formatAmount(5000)} worth of real estate stocks
</span>
)
},
{
type: 'opportunity',
title: 'Investment Opportunity',
description: 'Renewable energy ETF showing strong momentum with low correlation to your existing holdings.',
priority: 'high',
action: `Consider investing ${formatCurrency(10000)}`
title: 'Sector Opportunity',
description: 'Tech sector is showing strong momentum.',
priority: 'medium',
action: (
<span className="flex items-center gap-1">
Consider investing <DirhamIcon className="w-3 h-3 inline" /> {formatAmount(10000)}
</span>
)
},
{
type: 'risk',
@@ -108,173 +104,63 @@ const PortfolioManager: React.FC = () => {
}
];
const getPriorityColor = (priority: string) => {
switch (priority) {
case 'high': return 'text-red-600 bg-red-50 border-red-200';
case 'medium': return 'text-yellow-600 bg-yellow-50 border-yellow-200';
case 'low': return 'text-green-600 bg-green-50 border-green-200';
default: return 'text-slate-600 bg-slate-50 border-slate-200';
}
};
return (
<div className="space-y-6 animate-fade-in text-slate-800">
<div className="fade-in mb-8">
<h1 className="text-3xl font-light tracking-tight text-slate-900">
Portfolio <span className="font-semibold bg-clip-text text-transparent bg-gradient-to-r from-blue-600 to-cyan-600">Manager</span>
</h1>
<p className="text-slate-500 mt-2">
AI-powered insights for your investments.
</p>
<div className="space-y-6 animate-fade-in text-slate-800 pb-10">
{/* Header Section */}
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4 mb-8">
<div>
<h1 className="text-3xl font-light tracking-tight text-slate-900">
Portfolio <span className="font-semibold bg-clip-text text-transparent bg-gradient-to-r from-blue-600 to-cyan-600">Manager</span>
</h1>
<p className="text-slate-500 mt-1">Smart insights for your investments.</p>
</div>
<div className="flex gap-3">
<Button variant="outline" className="border-slate-200 text-slate-700 bg-white hover:bg-slate-50">
Auto Rebalance
</Button>
<Button className="bg-black text-white hover:bg-slate-800 shadow-md">
<Plus className="w-4 h-4 mr-2" /> Add Investment
</Button>
</div>
</div>
{/* Portfolio Overview */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 slide-up">
<GlassCard className="p-6">
<div className="flex items-center justify-between mb-4">
<p className="text-sm font-medium text-slate-500">Total Value</p>
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-green-400 to-green-600 flex items-center justify-center shadow-lg shadow-green-200">
<BarChart3 className="w-5 h-5 text-white" />
</div>
</div>
<p className="text-2xl font-bold text-slate-900">{formatCurrency(portfolioOverview.totalValue)}</p>
<div className="flex items-center mt-2">
<TrendingUp className="w-4 h-4 text-green-500 mr-1" />
<span className="text-sm text-green-600 font-medium">+{portfolioOverview.gainPercentage}%</span>
</div>
</GlassCard>
{/* Summary Cards */}
<PortfolioSummaryCards overview={portfolioOverview} holdingsCount={holdings.length} />
<GlassCard className="p-6">
<div className="flex items-center justify-between mb-4">
<p className="text-sm font-medium text-slate-500">Total Gains</p>
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-blue-400 to-blue-600 flex items-center justify-center shadow-lg shadow-blue-200">
<TrendingUp className="w-5 h-5 text-white" />
</div>
</div>
<p className="text-2xl font-bold text-slate-900">{formatCurrency(portfolioOverview.totalGains)}</p>
<p className="text-xs text-slate-400 mt-2">vs {formatCurrency(portfolioOverview.totalInvested)} invested</p>
</GlassCard>
<GlassCard className="p-6">
<div className="flex items-center justify-between mb-4">
<p className="text-sm font-medium text-slate-500">Day Change</p>
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-cyan-400 to-cyan-600 flex items-center justify-center shadow-lg shadow-cyan-200">
<Coins className="w-5 h-5 text-white" />
</div>
</div>
<p className="text-2xl font-bold text-slate-900">{formatCurrency(portfolioOverview.dayChange)}</p>
<div className="flex items-center mt-2">
<ArrowUpRight className="w-4 h-4 text-green-500 mr-1" />
<span className="text-sm text-green-600 font-medium">+{portfolioOverview.dayChangePercent}%</span>
</div>
</GlassCard>
<GlassCard className="p-6">
<div className="flex items-center justify-between mb-4">
<p className="text-sm font-medium text-slate-500">Risk Score</p>
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-purple-400 to-purple-600 flex items-center justify-center shadow-lg shadow-purple-200">
<Target className="w-5 h-5 text-white" />
</div>
</div>
<p className="text-2xl font-bold text-slate-900">7.2</p>
<p className="text-xs text-yellow-600 font-medium mt-2 bg-yellow-100 inline-block px-2 py-0.5 rounded-full">Moderate</p>
</GlassCard>
</div>
<Tabs defaultValue="holdings" className="space-y-6 w-full">
<TabsList className="grid w-full grid-cols-3 bg-white/40 p-1 rounded-2xl backdrop-blur-md border border-white/20">
<TabsTrigger value="holdings" className="rounded-xl data-[state=active]:bg-white data-[state=active]:shadow-sm">Holdings</TabsTrigger>
<TabsTrigger value="allocation" className="rounded-xl data-[state=active]:bg-white data-[state=active]:shadow-sm">Allocation</TabsTrigger>
<TabsTrigger value="recommendations" className="rounded-xl data-[state=active]:bg-white data-[state=active]:shadow-sm">AI Insights</TabsTrigger>
{/* Main Content Tabs */}
<Tabs defaultValue="overview" className="space-y-6 w-full">
<TabsList className="bg-white/60 p-1.5 rounded-2xl backdrop-blur-md border border-white/40 inline-flex w-auto shadow-sm">
<TabsTrigger value="overview" className="rounded-xl px-6 py-2.5 text-sm font-medium data-[state=active]:bg-white data-[state=active]:text-slate-900 data-[state=active]:shadow-sm text-slate-500 transition-all">Overview</TabsTrigger>
<TabsTrigger value="holdings" className="rounded-xl px-6 py-2.5 text-sm font-medium data-[state=active]:bg-white data-[state=active]:text-slate-900 data-[state=active]:shadow-sm text-slate-500 transition-all">Holdings</TabsTrigger>
<TabsTrigger value="analytics" className="rounded-xl px-6 py-2.5 text-sm font-medium data-[state=active]:bg-white data-[state=active]:text-slate-900 data-[state=active]:shadow-sm text-slate-500 transition-all">Analytics</TabsTrigger>
</TabsList>
<TabsContent value="holdings" className="space-y-4 outline-none">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{holdings.map((holding, index) => (
<GlassCard key={index} className="p-5 flex flex-col justify-between h-full" hoverEffect>
<div>
<div className="flex justify-between items-start mb-4">
<div className="p-2 bg-slate-100 rounded-lg">
<span className="font-bold text-slate-700">{holding.symbol.substring(0, 4)}</span>
</div>
<Badge variant="outline" className="bg-white/50">{holding.sector}</Badge>
</div>
<h3 className="font-bold text-lg text-slate-900">{holding.name}</h3>
<p className="text-sm text-slate-500">{holding.shares} shares @ {formatCurrency(holding.currentPrice)}</p>
</div>
<div className="mt-6 pt-4 border-t border-slate-100">
<div className="flex justify-between items-end">
<div>
<p className="text-xs text-slate-400">Current Value</p>
<p className="font-semibold text-lg">{formatCurrency(holding.currentValue)}</p>
</div>
<div className={`text-right ${holding.gain > 0 ? 'text-green-600' : 'text-red-600'}`}>
<p className="text-xs font-medium flex items-center justify-end">
{holding.gain > 0 ? <ArrowUpRight className="w-3 h-3 mr-1" /> : <ArrowDownRight className="w-3 h-3 mr-1" />}
{holding.gainPercent}%
</p>
<p className="text-sm font-bold">
{holding.gain > 0 ? '+' : ''}{formatCurrency(holding.gain)}
</p>
</div>
</div>
</div>
</GlassCard>
))}
{/* Tab 1: Overview */}
<TabsContent value="overview" className="outline-none animate-in fade-in slide-in-from-bottom-2 duration-500">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 h-[400px]">
<AssetAllocation holdings={holdings} totalValue={portfolioOverview.totalValue} />
<QuickInvestmentForm holdings={holdings} />
</div>
</TabsContent>
<TabsContent value="allocation" className="space-y-4 outline-none">
<GlassCard className="p-6">
<div className="space-y-6">
{sectorAllocation.map((sector, index) => (
<div key={index} className="space-y-2">
<div className="flex justify-between items-center">
<div className="flex items-center gap-3">
<div className={`w-3 h-3 rounded-full bg-gradient-to-r ${sector.color}`}></div>
<span className="font-medium text-slate-700">{sector.sector}</span>
</div>
<div className="text-right">
<span className="font-bold text-slate-900">{sector.percentage}%</span>
<p className="text-xs text-slate-500">{formatCurrency(sector.value)}</p>
</div>
</div>
<div className="w-full bg-slate-100 rounded-full h-2 overflow-hidden">
<div
className={`h-full bg-gradient-to-r ${sector.color} transition-all duration-1000`}
style={{ width: `${sector.percentage}%` }}
></div>
</div>
</div>
))}
{/* Tab 2: Holdings */}
<TabsContent value="holdings" className="outline-none animate-in fade-in slide-in-from-bottom-2 duration-500">
<HoldingsTable holdings={holdings} />
</TabsContent>
{/* Tab 3: Analytics */}
<TabsContent value="analytics" className="outline-none animate-in fade-in slide-in-from-bottom-2 duration-500">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="lg:col-span-1 h-full">
<RiskAnalysis />
</div>
<div className="lg:col-span-2 h-full">
<PortfolioAIInsights recommendations={recommendations} />
</div>
</GlassCard>
</TabsContent>
<TabsContent value="recommendations" className="space-y-4 outline-none">
<div className="grid gap-4">
{recommendations.map((rec, index) => (
<GlassCard key={index} className={`border-l-4 ${getPriorityColor(rec.priority)} p-6`}>
<div className="flex items-start justify-between mb-4">
<div>
<h3 className="text-lg font-semibold text-slate-800">{rec.title}</h3>
<p className="text-slate-600 mt-1">{rec.description}</p>
</div>
<Badge variant="outline" className={`${getPriorityColor(rec.priority)} bg-white/50 backdrop-blur-sm`}>
{rec.priority.charAt(0).toUpperCase() + rec.priority.slice(1)}
</Badge>
</div>
<div className="flex items-center justify-between pt-4 border-t border-slate-100/50">
<p className="text-sm font-medium text-slate-700">{rec.action}</p>
<Button variant="ghost" size="sm" className="hover:bg-slate-100">
Take Action
</Button>
</div>
</GlassCard>
))}
</div>
</TabsContent>
</Tabs>
</div>
);

View File

@@ -5,6 +5,7 @@ import { Switch } from '@/components/ui/switch';
import { Label } from '@/components/ui/label';
import { Separator } from '@/components/ui/separator';
import { Badge } from '@/components/ui/badge';
import { useTheme } from '@/context/ThemeContext';
import {
User,
ShieldCheck,
@@ -15,10 +16,12 @@ import {
Trash2,
LogOut,
CheckCircle,
Building
Building,
Sparkles
} from 'lucide-react';
export const Profile = () => {
const { theme, setTheme } = useTheme();
const [isShariaEnabled, setIsShariaEnabled] = useState(false);
const [notifications, setNotifications] = useState(true);
const [currency, setCurrency] = useState('AED');
@@ -137,6 +140,40 @@ export const Profile = () => {
</div>
</GlassCard>
{/* Appearance & Themes */}
<GlassCard className="p-6 space-y-6">
<div className="flex items-center gap-3 mb-4">
<div className="p-2 bg-pink-100 rounded-lg text-pink-600">
<Sparkles className="w-5 h-5" />
</div>
<h3 className="text-lg font-semibold">Appearance</h3>
</div>
<div className="grid grid-cols-2 gap-3">
{['default', 'dubai', 'minimal', 'nature'].map((themeName) => (
<button
key={themeName}
onClick={() => setTheme(themeName as any)}
className={`relative h-20 rounded-xl overflow-hidden border-2 transition-all ${theme === themeName ? 'border-blue-600 shadow-md scale-[1.02]' : 'border-transparent hover:scale-[1.02]'}`}
>
{themeName === 'default' && <div className="absolute inset-0 bg-gradient-to-br from-purple-100 to-blue-100" />}
{themeName === 'dubai' && <img src="https://images.unsplash.com/photo-1512453979798-5ea9ba6a80f4?q=80&w=300&auto=format&fit=crop" className="absolute inset-0 w-full h-full object-cover" />}
{themeName === 'minimal' && <img src="https://images.unsplash.com/photo-1550684848-fac1c5b4e853?q=80&w=300&auto=format&fit=crop" className="absolute inset-0 w-full h-full object-cover" />}
{themeName === 'nature' && <img src="https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?q=80&w=300&auto=format&fit=crop" className="absolute inset-0 w-full h-full object-cover" />}
<div className="absolute inset-0 bg-black/20 flex items-center justify-center">
<span className="text-white font-medium text-xs capitalized drop-shadow-md capitalize">{themeName}</span>
</div>
{theme === themeName && (
<div className="absolute top-1 right-1 bg-blue-600 rounded-full p-0.5">
<CheckCircle className="w-3 h-3 text-white" />
</div>
)}
</button>
))}
</div>
</GlassCard>
{/* Data Privacy (UAE PDPL) */}
<GlassCard className="p-6 space-y-6 border-red-100/50">
<div className="flex items-center gap-3 mb-4">

View File

@@ -1,235 +1,82 @@
import React from 'react';
import { motion } from 'framer-motion';
import { GlassCard } from '@/components/ui/GlassCard';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import {
TrendingUp,
Zap,
ArrowRight,
Repeat,
Layers,
CheckCircle2,
PlayCircle
} from 'lucide-react';
import { cn, formatCurrency } from '@/lib/utils';
import { ResponsiveContainer, AreaChart, Area, XAxis, YAxis, Tooltip } from 'recharts';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Zap, Plus, Calculator } from 'lucide-react';
// Mock Data
const savingsData = {
current: 5000,
potential: 7200,
score: 6.8, // Out of 10
yearlyProjection: [
{ month: 'Jan', current: 5000, optimized: 7200 },
{ month: 'Mar', current: 15000, optimized: 21600 },
{ month: 'Jun', current: 30000, optimized: 43200 },
{ month: 'Sep', current: 45000, optimized: 64800 },
{ month: 'Dec', current: 60000, optimized: 86400 },
],
opportunities: [
{
id: 1,
type: 'Duplicate',
title: 'Netflix & Prime Video (Entertainment)',
action: 'Merge/Cancel',
impact: '+ AED 45/mo',
icon: Repeat,
color: 'text-red-500 bg-red-100'
},
{
id: 2,
type: 'Alternative',
title: 'Switch Credit Card (Cashback 5% vs 1%)',
action: 'Apply Now',
impact: '+ AED 150/mo',
icon: Shuffle,
color: 'text-blue-500 bg-blue-100'
},
{
id: 3,
type: 'Bundle',
title: 'Etisalat Home + Mobile Bundle',
action: 'View Deal',
impact: '+ AED 80/mo',
icon: Layers,
color: 'text-purple-500 bg-purple-100'
},
]
};
function Shuffle(props: any) {
return (
<svg
{...props}
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M2 18h1.4c1.3 0 2.5-.6 3.3-1.7l14.2-9.4c.5-.3 1.1-.5 1.7-.5H22" />
<path d="M2 5h1.4c1.3 0 2.5.6 3.3 1.7l14.2 9.4c.5.3 1.1.5 1.7.5H22" />
</svg>
)
}
const RadialGauge = ({ value }: { value: number }) => {
const rotation = -90 + (value / 10) * 180; // Map 0-10 to -90 to 90 degrees
const color = value < 5 ? 'text-red-500' : value < 8 ? 'text-yellow-500' : 'text-green-500';
return (
<div className="relative w-40 h-24 overflow-hidden flex items-end justify-center">
{/* Background Arc */}
<div className="absolute w-40 h-40 rounded-full border-[12px] border-slate-100 top-0 left-0" />
{/* Value Arc (Simulated with rotation) */}
<div
className="absolute w-40 h-40 rounded-full border-[12px] border-transparent border-t-current top-0 left-0 transition-transform duration-1000 ease-out origin-bottom"
style={{
transform: `rotate(${rotation}deg)`,
color: value < 5 ? '#ef4444' : value < 8 ? '#eab308' : '#22c55e'
}}
/>
<div className="text-center z-10 mb-2">
<span className={cn("text-4xl font-bold", color)}>{value}</span>
<span className="text-xs text-slate-400 block uppercase tracking-wider">Score</span>
</div>
</div>
);
};
// Import New Modular Savings Components
import SavingsSummaryCards from '@/components/savings/SavingsSummaryCards';
import SavingsGoalsList from '@/components/savings/SavingsGoalsList';
import SavingsAccountsList from '@/components/savings/SavingsAccountsList';
import SavingsGoalsGrid from '@/components/savings/SavingsGoalsGrid';
import SavingsAccountsTable from '@/components/savings/SavingsAccountsTable';
import CompoundInterestCalculator from '@/components/savings/CompoundInterestCalculator';
import AddGoalForm from '@/components/savings/AddGoalForm';
export default function SavingsBooster() {
return (
<div className="max-w-6xl mx-auto space-y-6 sm:space-y-8 pb-20 animate-fade-in">
<div className="header">
<h1 className="text-2xl sm:text-3xl font-bold text-slate-900 flex items-center gap-3">
<Zap className="w-6 h-6 sm:w-8 sm:h-8 text-yellow-500 fill-yellow-500" /> Personal Savings Booster
</h1>
<p className="text-sm sm:text-base text-slate-500 mt-1">AI-driven optimization to maximize your wealth.</p>
</div>
<div className="space-y-6 animate-fade-in text-slate-800 pb-10">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4 sm:gap-6">
{/* Hero Comparison */}
<div className="col-span-1 lg:col-span-2 space-y-4 sm:space-y-6">
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
<GlassCard className="p-6 flex flex-col items-center justify-center text-center">
<span className="text-slate-400 text-sm mb-2">Optimization Score</span>
<RadialGauge value={savingsData.score} />
</GlassCard>
<GlassCard className="p-6 flex flex-col justify-center space-y-2 relative overflow-hidden">
<span className="text-slate-500 text-sm">Monthly Potential</span>
<div className="flex items-baseline gap-2">
<span className="text-3xl font-bold text-slate-900">{formatCurrency(savingsData.potential)}</span>
<span className="text-sm text-slate-400">/mo</span>
</div>
<div className="flex items-center gap-2 text-green-600 text-sm font-medium bg-green-50 px-2 py-1 rounded w-fit">
<TrendingUp className="w-3 h-3" />
+ {formatCurrency(savingsData.potential - savingsData.current)} vs current
</div>
<div className="absolute top-0 right-0 p-4 opacity-10">
<TrendingUp className="w-24 h-24 text-green-500" />
</div>
</GlassCard>
<GlassCard className="p-6 flex flex-col justify-center space-y-4">
<h3 className="font-semibold text-slate-700">Quick Actions</h3>
<Button className="w-full justify-between bg-slate-900 text-white hover:bg-slate-800">
Auto-Optimize <PlayCircle className="w-4 h-4" />
</Button>
</GlassCard>
</div>
{/* Projection Chart */}
<GlassCard className="p-6">
<h3 className="font-semibold text-slate-700 mb-6">Yearly Projection: Current vs Optimized</h3>
<div className="h-64 w-full">
<ResponsiveContainer width="100%" height="100%">
<AreaChart data={savingsData.yearlyProjection}>
<defs>
<linearGradient id="colorOptimized" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#22c55e" stopOpacity={0.1} />
<stop offset="95%" stopColor="#22c55e" stopOpacity={0} />
</linearGradient>
</defs>
<XAxis dataKey="month" axisLine={false} tickLine={false} tick={{ fill: '#94a3b8', fontSize: 12 }} />
<YAxis hide />
<Tooltip
contentStyle={{ backgroundColor: 'white', borderRadius: '12px', border: 'none', boxShadow: '0 4px 6px -1px rgb(0 0 0 / 0.1)' }}
/>
<Area
type="monotone"
dataKey="current"
stroke="#94a3b8"
strokeWidth={2}
fill="transparent"
strokeDasharray="5 5"
/>
<Area
type="monotone"
dataKey="optimized"
stroke="#22c55e"
strokeWidth={3}
fill="url(#colorOptimized)"
/>
</AreaChart>
</ResponsiveContainer>
</div>
</GlassCard>
{/* Header Section */}
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4 mb-8">
<div>
<h1 className="text-3xl font-light tracking-tight text-slate-900 flex items-center gap-3">
<Zap className="w-6 h-6 text-yellow-500 fill-yellow-500" />
<span>Savings <span className="font-semibold bg-clip-text text-transparent bg-gradient-to-r from-indigo-600 to-purple-600">Booster</span></span>
</h1>
<p className="text-slate-500 mt-1 ml-9">Accelerate your wealth with smart goals.</p>
</div>
<div className="flex gap-3">
<Button variant="outline" className="border-slate-200 text-slate-700 bg-white hover:bg-slate-50">
<Calculator className="w-4 h-4 mr-2" /> Calculator
</Button>
<Button className="bg-black text-white hover:bg-slate-800 shadow-md">
<Plus className="w-4 h-4 mr-2" /> New Goal
</Button>
</div>
{/* AI Opportunities Sidebar */}
<GlassCard className="col-span-1 p-6 h-full">
<div className="flex items-center justify-between mb-6">
<h3 className="font-bold text-lg text-slate-800">AI Opportunities</h3>
<Badge variant="outline" className="bg-blue-50 text-blue-600 border-blue-100">3 New</Badge>
</div>
<div className="space-y-4">
{savingsData.opportunities.map((opp, index) => (
<motion.div
key={opp.id}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1 }}
className="group relative p-4 rounded-2xl bg-white/50 border border-white/60 hover:bg-white/80 transition-all hover:shadow-sm cursor-pointer"
>
<div className="flex gap-3 mb-3">
<div className={cn("p-2 rounded-xl h-fit", opp.color)}>
<opp.icon className="w-5 h-5" />
</div>
<div>
<h4 className="font-semibold text-sm text-slate-900 leading-tight mb-1">{opp.title}</h4>
<span className="text-xs text-slate-500 font-medium">{opp.type}</span>
</div>
</div>
<div className="flex items-center justify-between mt-2 pl-1">
<span className="text-green-600 font-bold text-sm bg-green-50 px-2 py-1 rounded-md">
{opp.impact}
</span>
<Button size="sm" variant="ghost" className="text-blue-600 hover:text-blue-700 hover:bg-blue-50 p-0 h-auto font-medium flex gap-1">
{opp.action} <ArrowRight className="w-3 h-3" />
</Button>
</div>
</motion.div>
))}
</div>
<div className="mt-6 p-4 rounded-2xl bg-gradient-to-br from-indigo-500 to-purple-600 text-white text-center">
<p className="text-sm font-medium opacity-90 mb-3">Unlock Premium Insights to save an extra AED 450/mo</p>
<Button size="sm" className="bg-white text-indigo-600 hover:bg-white/90 w-full rounded-xl">
Upgrade to Pro
</Button>
</div>
</GlassCard>
</div>
{/* Summary Cards */}
<SavingsSummaryCards />
{/* Main Content Tabs */}
<Tabs defaultValue="overview" className="space-y-6 w-full">
<TabsList className="bg-white/60 p-1.5 rounded-2xl backdrop-blur-md border border-white/40 inline-flex w-auto flex-wrap lg:flex-nowrap shadow-sm h-auto">
<TabsTrigger value="overview" className="rounded-xl px-4 lg:px-6 py-2.5 text-sm font-medium data-[state=active]:bg-white data-[state=active]:text-slate-900 data-[state=active]:shadow-sm text-slate-500 transition-all">Overview</TabsTrigger>
<TabsTrigger value="goals" className="rounded-xl px-4 lg:px-6 py-2.5 text-sm font-medium data-[state=active]:bg-white data-[state=active]:text-slate-900 data-[state=active]:shadow-sm text-slate-500 transition-all">Goals</TabsTrigger>
<TabsTrigger value="accounts" className="rounded-xl px-4 lg:px-6 py-2.5 text-sm font-medium data-[state=active]:bg-white data-[state=active]:text-slate-900 data-[state=active]:shadow-sm text-slate-500 transition-all">Accounts</TabsTrigger>
<TabsTrigger value="calculator" className="rounded-xl px-4 lg:px-6 py-2.5 text-sm font-medium data-[state=active]:bg-white data-[state=active]:text-slate-900 data-[state=active]:shadow-sm text-slate-500 transition-all">Calculator</TabsTrigger>
<TabsTrigger value="add-goal" className="rounded-xl px-4 lg:px-6 py-2.5 text-sm font-medium data-[state=active]:bg-white data-[state=active]:text-slate-900 data-[state=active]:shadow-sm text-slate-500 transition-all">Add Goal</TabsTrigger>
</TabsList>
{/* Tab 1: Overview */}
<TabsContent value="overview" className="outline-none animate-in fade-in slide-in-from-bottom-2 duration-500">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 h-auto lg:h-[450px]">
<SavingsGoalsList />
<SavingsAccountsList />
</div>
</TabsContent>
{/* Tab 2: Goals */}
<TabsContent value="goals" className="outline-none animate-in fade-in slide-in-from-bottom-2 duration-500">
<SavingsGoalsGrid />
</TabsContent>
{/* Tab 3: Accounts */}
<TabsContent value="accounts" className="outline-none animate-in fade-in slide-in-from-bottom-2 duration-500">
<SavingsAccountsTable />
</TabsContent>
{/* Tab 4: Calculator */}
<TabsContent value="calculator" className="outline-none animate-in fade-in slide-in-from-bottom-2 duration-500">
<CompoundInterestCalculator />
</TabsContent>
{/* Tab 5: Add Goal */}
<TabsContent value="add-goal" className="outline-none animate-in fade-in slide-in-from-bottom-2 duration-500">
<AddGoalForm />
</TabsContent>
</Tabs>
</div>
);
}

View File

@@ -0,0 +1,67 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { formatAmount } from '@/lib/utils';
import { DirhamIcon } from '@/components/ui/custom-icons';
interface AssetAllocationProps {
holdings: Array<{
name: string;
symbol: string;
currentValue: number;
sector: string; // Keep for color logic if needed
shares?: number;
units?: number;
}>;
totalValue: number;
}
const AssetAllocation: React.FC<AssetAllocationProps> = ({ holdings, totalValue }) => {
// Sort holdings by value descending
const sortedHoldings = [...holdings].sort((a, b) => b.currentValue - a.currentValue);
// Helper to get color based on index
const getGradient = (index: number) => {
const gradients = [
'from-blue-500 to-blue-600',
'from-green-500 to-green-600',
'from-purple-500 to-purple-600',
'from-orange-500 to-orange-600',
'from-pink-500 to-pink-600',
'from-teal-500 to-teal-600',
];
return gradients[index % gradients.length];
};
return (
<GlassCard className="p-6 h-full">
<h3 className="text-lg font-semibold text-slate-800 mb-6">Asset Allocation</h3>
<div className="space-y-6">
{sortedHoldings.map((stock, index) => {
const percentage = ((stock.currentValue / totalValue) * 100).toFixed(1);
return (
<div key={index} className="space-y-2">
<div className="flex justify-between items-center text-sm">
<div className="flex items-center gap-2">
<span className="font-bold text-slate-700 w-12">{stock.symbol}</span>
<span className="text-slate-500 truncate max-w-[120px]">{stock.name}</span>
</div>
<div className="text-right">
<span className="font-bold text-slate-900">{percentage}%</span>
</div>
</div>
<div className="w-full bg-slate-100 rounded-full h-2 overflow-hidden">
<div
className={`h-full bg-gradient-to-r ${getGradient(index)} transition-all duration-1000`}
style={{ width: `${percentage}%` }}
></div>
</div>
</div>
);
})}
</div>
</GlassCard>
);
};
export default AssetAllocation;

View File

@@ -0,0 +1,90 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { formatAmount } from '@/lib/utils';
import { DirhamIcon } from '@/components/ui/custom-icons';
import { ArrowUpRight, ArrowDownRight, Plus, Minus } from 'lucide-react';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
interface HoldingsTableProps {
holdings: Array<{
name: string;
symbol: string;
shares?: number;
units?: number;
currentPrice: number;
currentValue: number;
gain: number;
gainPercent: number;
sector: string;
}>;
}
const HoldingsTable: React.FC<HoldingsTableProps> = ({ holdings }) => {
return (
<GlassCard className="overflow-hidden">
<Table>
<TableHeader className="bg-slate-50/50">
<TableRow>
<TableHead className="w-[200px]">Asset Name</TableHead>
<TableHead className="text-right">Price</TableHead>
<TableHead className="text-right">Shares</TableHead>
<TableHead className="text-right">Value</TableHead>
<TableHead className="text-right">Change</TableHead>
<TableHead className="text-center w-[100px]">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{holdings.map((holding) => (
<TableRow key={holding.symbol} className="hover:bg-slate-50/50 transition-colors">
<TableCell className="font-medium">
<div className="flex flex-col">
<span className="text-slate-900 font-bold">{holding.symbol}</span>
<span className="text-slate-500 text-xs">{holding.name}</span>
</div>
</TableCell>
<TableCell className="text-right">
<div className="flex items-center justify-end gap-1 font-medium">
<DirhamIcon className="w-3 h-3 text-slate-400" /> {formatAmount(holding.currentPrice)}
</div>
</TableCell>
<TableCell className="text-right text-slate-600">{holding.shares || holding.units}</TableCell>
<TableCell className="text-right font-bold text-slate-800">
<div className="flex items-center justify-end gap-1">
<DirhamIcon className="w-3 h-3 text-slate-400" /> {formatAmount(holding.currentValue)}
</div>
</TableCell>
<TableCell className="text-right">
<div className={`flex items-center justify-end gap-1 font-medium ${holding.gain >= 0 ? 'text-green-600' : 'text-red-600'}`}>
{holding.gain >= 0 ? <ArrowUpRight className="w-3 h-3" /> : <ArrowDownRight className="w-3 h-3" />}
{Math.abs(holding.gainPercent)}%
</div>
</TableCell>
<TableCell>
<div className="flex items-center justify-center gap-2">
<Button size="icon" variant="outline" className="h-7 w-7 rounded-lg hover:bg-slate-100 text-slate-600">
<Minus className="w-3 h-3" />
</Button>
<Button size="icon" variant="outline" className="h-7 w-7 rounded-lg hover:bg-slate-100 text-slate-600">
<Plus className="w-3 h-3" />
</Button>
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</GlassCard>
);
};
export default HoldingsTable;

View File

@@ -0,0 +1,57 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
interface Recommendation {
type: string;
title: string;
description: string;
priority: string;
action: React.ReactNode;
}
interface PortfolioAIInsightsProps {
recommendations: Recommendation[];
}
const PortfolioAIInsights: React.FC<PortfolioAIInsightsProps> = ({ recommendations }) => {
const getPriorityColor = (priority: string) => {
switch (priority) {
case 'high': return 'text-red-700 bg-red-50 border-red-200';
case 'medium': return 'text-yellow-700 bg-yellow-50 border-yellow-200';
case 'low': return 'text-green-700 bg-green-50 border-green-200';
default: return 'text-slate-700 bg-slate-50 border-slate-200';
}
};
return (
<div className="space-y-4 h-full">
{recommendations.map((rec, index) => (
<GlassCard key={index} className={`border-l-4 ${getPriorityColor(rec.priority)} p-5 hover:scale-[1.01] transition-transform`}>
<div className="flex items-start justify-between mb-3">
<div>
<h3 className="text-base font-bold text-slate-800">{rec.title}</h3>
<p className="text-sm text-slate-600 mt-1">{rec.description}</p>
</div>
<Badge variant="outline" className={`${getPriorityColor(rec.priority)} bg-white/50 backdrop-blur-sm shadow-sm`}>
{rec.priority.charAt(0).toUpperCase() + rec.priority.slice(1)}
</Badge>
</div>
<div className="flex items-center justify-between pt-3 border-t border-slate-100/50 mt-2">
<div className="text-sm font-semibold text-slate-700">
{rec.action}
</div>
<Button variant="ghost" size="sm" className="hover:bg-slate-100 text-xs h-8">
Act Now
</Button>
</div>
</GlassCard>
))}
</div>
);
};
export default PortfolioAIInsights;

View File

@@ -0,0 +1,97 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { formatAmount } from '@/lib/utils';
import { DirhamIcon } from '@/components/ui/custom-icons';
import {
TrendingUp,
BarChart3,
ArrowUpRight,
Coins,
Library
} from 'lucide-react';
interface PortfolioSummaryCardsProps {
overview: {
totalValue: number;
totalInvested: number;
totalGains: number;
gainPercentage: number;
dayChange: number;
dayChangePercent: number;
};
holdingsCount: number;
}
const PortfolioSummaryCards: React.FC<PortfolioSummaryCardsProps> = ({ overview, holdingsCount }) => {
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
{/* Total Value */}
<GlassCard className="p-6">
<div className="flex items-center justify-between mb-4">
<p className="text-sm font-medium text-slate-500">Total Value</p>
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-green-400 to-green-600 flex items-center justify-center shadow-lg shadow-green-200">
<BarChart3 className="w-5 h-5 text-white" />
</div>
</div>
<div className="flex items-center gap-1">
<DirhamIcon className="w-6 h-6 text-slate-900" />
<p className="text-2xl font-bold text-slate-900">{formatAmount(overview.totalValue)}</p>
</div>
<div className="flex items-center mt-2">
<TrendingUp className="w-4 h-4 text-green-500 mr-1" />
<span className="text-sm text-green-600 font-medium">+{overview.gainPercentage}%</span>
</div>
</GlassCard>
{/* Today's Change */}
<GlassCard className="p-6">
<div className="flex items-center justify-between mb-4">
<p className="text-sm font-medium text-slate-500">Today's Change</p>
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-cyan-400 to-cyan-600 flex items-center justify-center shadow-lg shadow-cyan-200">
<Coins className="w-5 h-5 text-white" />
</div>
</div>
<div className="flex items-center gap-1">
<DirhamIcon className="w-6 h-6 text-slate-900" />
<p className="text-2xl font-bold text-slate-900">{formatAmount(overview.dayChange)}</p>
</div>
<div className="flex items-center mt-2">
<ArrowUpRight className="w-4 h-4 text-green-500 mr-1" />
<span className="text-sm text-green-600 font-medium">+{overview.dayChangePercent}%</span>
</div>
</GlassCard>
{/* Holdings Count */}
<GlassCard className="p-6">
<div className="flex items-center justify-between mb-4">
<p className="text-sm font-medium text-slate-500">Holdings</p>
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-purple-400 to-purple-600 flex items-center justify-center shadow-lg shadow-purple-200">
<Library className="w-5 h-5 text-white" />
</div>
</div>
<p className="text-2xl font-bold text-slate-900">{holdingsCount}</p>
<p className="text-sm text-slate-500 mt-2">Active Positions</p>
</GlassCard>
{/* Performance / Total Gains */}
<GlassCard className="p-6">
<div className="flex items-center justify-between mb-4">
<p className="text-sm font-medium text-slate-500">Total Gains</p>
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-blue-400 to-blue-600 flex items-center justify-center shadow-lg shadow-blue-200">
<TrendingUp className="w-5 h-5 text-white" />
</div>
</div>
<div className="flex items-center gap-1">
<DirhamIcon className="w-6 h-6 text-slate-900" />
<p className="text-2xl font-bold text-slate-900">{formatAmount(overview.totalGains)}</p>
</div>
<div className="flex items-center gap-1 text-xs text-slate-400 mt-2">
vs <DirhamIcon className="w-3 h-3" /> {formatAmount(overview.totalInvested)} invested
</div>
</GlassCard>
</div>
);
};
export default PortfolioSummaryCards;

View File

@@ -0,0 +1,56 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { DirhamIcon } from '@/components/ui/custom-icons';
interface QuickInvestmentFormProps {
holdings: Array<{ symbol: string; name: string }>;
}
const QuickInvestmentForm: React.FC<QuickInvestmentFormProps> = ({ holdings }) => {
return (
<GlassCard className="p-6 h-full flex flex-col justify-center">
<h3 className="text-lg font-semibold text-slate-800 mb-6">Quick Investment</h3>
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="holding" className="text-slate-600">Select Holding</Label>
<Select>
<SelectTrigger id="holding" className="bg-white/50 border-slate-200">
<SelectValue placeholder="Choose asset..." />
</SelectTrigger>
<SelectContent>
{holdings.map((h) => (
<SelectItem key={h.symbol} value={h.symbol}>{h.symbol} - {h.name}</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="amount" className="text-slate-600">Amount (AED)</Label>
<div className="relative">
<div className="absolute inset-y-0 left-3 flex items-center pointer-events-none">
<DirhamIcon className="w-4 h-4 text-slate-400" />
</div>
<Input id="amount" placeholder="0.00" className="pl-9 bg-white/50 border-slate-200" />
</div>
</div>
<div className="grid grid-cols-2 gap-4 pt-4">
<Button variant="outline" className="text-green-600 border-green-200 hover:bg-green-50 hover:text-green-700 w-full">
Buy Asset
</Button>
<Button variant="outline" className="text-red-600 border-red-200 hover:bg-red-50 hover:text-red-700 w-full">
Sell Asset
</Button>
</div>
</div>
</GlassCard>
);
};
export default QuickInvestmentForm;

View File

@@ -0,0 +1,34 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { Target, Info } from 'lucide-react';
const RiskAnalysis: React.FC = () => {
return (
<GlassCard className="p-6 h-full flex flex-col justify-between">
<div>
<div className="flex items-center gap-2 mb-4">
<Target className="w-5 h-5 text-purple-600" />
<h3 className="text-lg font-semibold text-slate-800">Risk Analysis</h3>
</div>
<p className="text-slate-600 text-sm leading-relaxed mb-6">
Your portfolio currently aligns with a balanced growth strategy. Consider increasing diversification in global markets to hedge against local volatility.
</p>
</div>
<div className="space-y-4">
<div className="flex justify-between items-center p-3 bg-slate-50 rounded-xl border border-slate-100">
<span className="text-sm font-medium text-slate-600">Current Score</span>
<span className="text-2xl font-bold text-slate-900">7.2<span className="text-sm text-slate-400 font-normal">/10</span></span>
</div>
<div className="flex justify-center">
<span className="px-4 py-1.5 bg-yellow-100 text-yellow-700 rounded-full text-sm font-bold border border-yellow-200 shadow-sm">
Moderate Risk
</span>
</div>
</div>
</GlassCard>
);
};
export default RiskAnalysis;

View File

@@ -0,0 +1,72 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { DirhamIcon } from '@/components/ui/custom-icons';
import { Calendar } from 'lucide-react';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { format } from 'date-fns';
import { cn } from '@/lib/utils';
import { Calendar as CalendarComponent } from '@/components/ui/calendar'; // Assuming shadcn Calendar exists
const AddGoalForm: React.FC = () => {
const [date, setDate] = React.useState<Date>();
return (
<div className="flex justify-center items-center h-[450px]">
<GlassCard className="p-8 w-full max-w-md">
<h3 className="text-xl font-bold text-slate-800 mb-6 text-center">Create New Savings Goal</h3>
<div className="space-y-5">
<div className="space-y-2">
<Label htmlFor="goal-name" className="text-slate-600">Goal Name</Label>
<Input id="goal-name" placeholder="e.g. Dream Vacation" className="bg-white/50 border-slate-200" />
</div>
<div className="space-y-2">
<Label htmlFor="target-amount" className="text-slate-600">Target Amount (AED)</Label>
<div className="relative">
<div className="absolute inset-y-0 left-3 flex items-center pointer-events-none">
<DirhamIcon className="w-4 h-4 text-slate-400" />
</div>
<Input id="target-amount" placeholder="0.00" className="pl-9 bg-white/50 border-slate-200" />
</div>
</div>
<div className="space-y-2">
<Label className="text-slate-600">Target Date</Label>
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-full justify-start text-left font-normal bg-white/50 border-slate-200",
!date && "text-muted-foreground"
)}
>
<Calendar className="mr-2 h-4 w-4" />
{date ? format(date, "PPP") : <span>Pick a date</span>}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
{/* Placeholder for standard Calendar since exact import path might vary */}
<div className="p-4 bg-white border rounded shadow-md">
<p className="text-sm text-slate-500 mb-2">Select Date</p>
<Input type="date" className="w-full" onChange={(e) => setDate(new Date(e.target.value))} />
</div>
</PopoverContent>
</Popover>
</div>
<Button className="w-full mt-4 bg-slate-900 text-white hover:bg-slate-800 shadow-md">
Create Savings Goal
</Button>
</div>
</GlassCard>
</div>
);
};
export default AddGoalForm;

View File

@@ -0,0 +1,94 @@
import React, { useState } from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Slider } from '@/components/ui/slider';
import { DirhamIcon } from '@/components/ui/custom-icons';
import { formatAmount } from '@/lib/utils';
import { Calculator } from 'lucide-react';
const CompoundInterestCalculator: React.FC = () => {
const [initialAmount, setInitialAmount] = useState<number>(10000);
const [monthlyContribution, setMonthlyContribution] = useState<number>(2000);
const [years, setYears] = useState<number>(10);
const [rate, setRate] = useState<number>(5);
const calculateResult = () => {
// Simplified compound interest calculation
const r = rate / 100 / 12;
const n = years * 12;
const futureValue = initialAmount * Math.pow(1 + r, n) + (monthlyContribution * (Math.pow(1 + r, n) - 1)) / r;
return futureValue;
};
const finalAmount = calculateResult();
const totalInvested = initialAmount + (monthlyContribution * years * 12);
const totalInterest = finalAmount - totalInvested;
return (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 h-full">
{/* Input Form */}
<GlassCard className="p-6">
<h3 className="text-lg font-semibold text-slate-800 mb-6 flex items-center gap-2">
<Calculator className="w-5 h-5 text-slate-500" /> Parameters
</h3>
<div className="space-y-6">
<div className="space-y-2">
<Label className="text-slate-600">Initial Amount (AED)</Label>
<Input type="number" value={initialAmount} onChange={(e) => setInitialAmount(Number(e.target.value))} className="bg-white/50" />
</div>
<div className="space-y-2">
<Label className="text-slate-600">Monthly Contribution (AED)</Label>
<Input type="number" value={monthlyContribution} onChange={(e) => setMonthlyContribution(Number(e.target.value))} className="bg-white/50" />
</div>
<div className="space-y-4">
<div className="flex justify-between">
<Label className="text-slate-600">Time Period (Years)</Label>
<span className="text-sm font-bold text-slate-800">{years} Years</span>
</div>
<Slider value={[years]} onValueChange={(v) => setYears(v[0])} max={50} step={1} className="py-2" />
</div>
<div className="space-y-4">
<div className="flex justify-between">
<Label className="text-slate-600">Interest Rate (%)</Label>
<span className="text-sm font-bold text-slate-800">{rate}%</span>
</div>
<Slider value={[rate]} onValueChange={(v) => setRate(v[0])} max={15} step={0.1} className="py-2" />
</div>
</div>
</GlassCard>
{/* Results */}
<GlassCard className="p-8 flex flex-col justify-center bg-gradient-to-br from-white/60 to-indigo-50/50">
<h3 className="text-lg font-semibold text-slate-800 mb-8 text-center">Projected Savings</h3>
<div className="text-center mb-10">
<p className="text-sm text-slate-500 mb-2">In {years} years, you will have</p>
<div className="text-4xl md:text-5xl font-bold text-green-600 flex items-center justify-center gap-2">
<DirhamIcon className="w-8 h-8 md:w-10 md:h-10" />
{formatAmount(Math.round(finalAmount))}
</div>
</div>
<div className="grid grid-cols-2 gap-6 border-t border-slate-200 pt-8">
<div className="text-center">
<p className="text-xs text-slate-500 mb-1">Total Contributions</p>
<p className="text-lg font-bold text-slate-800 flex items-center justify-center gap-1">
<DirhamIcon className="w-4 h-4 text-slate-400" /> {formatAmount(Math.round(totalInvested))}
</p>
</div>
<div className="text-center">
<p className="text-xs text-slate-500 mb-1">Total Interest Earned</p>
<p className="text-lg font-bold text-indigo-600 flex items-center justify-center gap-1">
<DirhamIcon className="w-4 h-4 text-indigo-400" /> {formatAmount(Math.round(totalInterest))}
</p>
</div>
</div>
</GlassCard>
</div>
);
};
export default CompoundInterestCalculator;

View File

@@ -0,0 +1,50 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { formatAmount } from '@/lib/utils';
import { DirhamIcon } from '@/components/ui/custom-icons';
import { Building2, ArrowUpRight } from 'lucide-react';
const SavingsAccountsList: React.FC = () => {
const accounts = [
{ name: 'ADCB Active Saver', balance: 145000, growth: 423, apy: 3.5, type: 'High Yield' },
{ name: 'Emirates NBD Savings', balance: 85000, growth: 120, apy: 1.7, type: 'Standard' },
{ name: 'Wio Personal', balance: 25000, growth: 104, apy: 5.0, type: 'High Yield' },
];
return (
<GlassCard className="p-6 h-full flex flex-col">
<h3 className="text-lg font-semibold text-slate-800 mb-6">Savings Accounts</h3>
<div className="space-y-4 flex-1">
{accounts.map((acc, index) => (
<div key={index} className="flex items-center justify-between p-4 rounded-xl hover:bg-slate-50/80 transition-colors border border-transparent hover:border-slate-100 group">
<div className="flex items-center gap-4">
<div className="w-10 h-10 rounded-full bg-slate-100 flex items-center justify-center text-slate-600 group-hover:bg-indigo-50 group-hover:text-indigo-600 transition-colors">
<Building2 className="w-5 h-5" />
</div>
<div>
<p className="text-sm font-bold text-slate-900">{acc.name}</p>
<div className="flex items-center gap-2">
<span className="text-xs text-slate-500">{acc.type}</span>
<span className="text-xs text-slate-300"></span>
<span className="text-xs text-green-600 font-medium">{acc.apy}% APY</span>
</div>
</div>
</div>
<div className="text-right">
<div className="font-bold text-slate-900 flex items-center justify-end gap-1">
<DirhamIcon className="w-3.5 h-3.5" /> {formatAmount(acc.balance)}
</div>
<div className="text-xs text-green-600 font-medium flex items-center justify-end gap-0.5 mt-0.5">
+<DirhamIcon className="w-2.5 h-2.5" /> {acc.growth}/mo
</div>
</div>
</div>
))}
</div>
</GlassCard>
);
};
export default SavingsAccountsList;

View File

@@ -0,0 +1,73 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { formatAmount } from '@/lib/utils';
import { DirhamIcon } from '@/components/ui/custom-icons';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Settings } from 'lucide-react';
const SavingsAccountsTable: React.FC = () => {
// Mock Data
const accounts = [
{ name: 'ADCB Active Saver', type: 'High Yield', balance: 145000, rate: 3.5, growth: 423 },
{ name: 'Emirates NBD Savings', type: 'Standard', balance: 85000, rate: 1.7, growth: 120 },
{ name: 'Wio Personal', type: 'High Yield', balance: 25000, rate: 5.0, growth: 104 },
];
return (
<GlassCard className="overflow-hidden">
<Table>
<TableHeader className="bg-slate-50/50">
<TableRow>
<TableHead className="w-[200px]">Account Name</TableHead>
<TableHead>Type</TableHead>
<TableHead className="text-right">Balance (AED)</TableHead>
<TableHead className="text-right">Interest Rate</TableHead>
<TableHead className="text-right">Monthly Growth</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{accounts.map((acc, idx) => (
<TableRow key={idx} className="hover:bg-slate-50/50">
<TableCell className="font-semibold text-slate-800">{acc.name}</TableCell>
<TableCell>
<Badge variant="outline" className={acc.type === 'High Yield' ? 'bg-indigo-50 text-indigo-600 border-indigo-200' : 'bg-slate-50 text-slate-600 border-slate-200'}>
{acc.type}
</Badge>
</TableCell>
<TableCell className="text-right font-bold text-slate-900">
<span className="flex items-center justify-end gap-1">
<DirhamIcon className="w-3 h-3" /> {formatAmount(acc.balance)}
</span>
</TableCell>
<TableCell className="text-right font-medium text-slate-600">{acc.rate}%</TableCell>
<TableCell className="text-right font-bold text-green-600">
<span className="flex items-center justify-end gap-1">
+<DirhamIcon className="w-3 h-3" /> {acc.growth}
</span>
</TableCell>
<TableCell className="text-right">
<Button variant="ghost" size="sm" className="h-8 w-8 p-0">
<Settings className="w-4 h-4 text-slate-400" />
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</GlassCard>
);
};
export default SavingsAccountsTable;

View File

@@ -0,0 +1,69 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { formatAmount } from '@/lib/utils';
import { DirhamIcon } from '@/components/ui/custom-icons';
import { Button } from '@/components/ui/button';
import { Progress } from '@/components/ui/progress';
import { Clock, Plus } from 'lucide-react';
import { motion } from 'framer-motion';
const SavingsGoalsGrid: React.FC = () => {
const goals = [
{ name: 'Emergency Fund', saved: 25000, target: 50000, daysLeft: 120, color: 'bg-yellow-500' },
{ name: 'Vacation', saved: 5000, target: 15000, daysLeft: 65, color: 'bg-blue-500' },
{ name: 'New Car', saved: 85000, target: 120000, daysLeft: 411, color: 'bg-green-500' },
{ name: 'Wedding', saved: 10000, target: 80000, daysLeft: 500, color: 'bg-pink-500' },
{ name: 'Home Downpayment', saved: 125000, target: 200000, daysLeft: 730, color: 'bg-indigo-500' },
];
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{goals.map((goal, index) => {
const percentage = Math.round((goal.saved / goal.target) * 100);
return (
<GlassCard key={index} className="p-6 flex flex-col justify-between h-full" hoverEffect>
<div>
<div className="flex justify-between items-start mb-4">
<div>
<h3 className="text-lg font-bold text-slate-800">{goal.name}</h3>
<div className="flex items-center gap-1 text-slate-500 text-xs mt-1">
<Clock className="w-3 h-3" />
<span>{goal.daysLeft} days remaining</span>
</div>
</div>
<div className="px-3 py-1 rounded-full bg-slate-100 text-slate-600 text-xs font-bold border border-slate-200">
{percentage}%
</div>
</div>
<div className="space-y-2 mb-6">
<Progress value={percentage} className="h-2.5" indicatorClassName={goal.color} />
<div className="flex justify-between text-xs text-slate-500">
<span className="font-semibold text-slate-900 flex items-center gap-0.5"><DirhamIcon className="w-3 h-3" /> {formatAmount(goal.saved)}</span>
<span className="flex items-center gap-0.5"><DirhamIcon className="w-3 h-3" /> {formatAmount(goal.target)}</span>
</div>
</div>
</div>
<div className="grid grid-cols-3 gap-2">
<Button variant="outline" size="sm" className="h-8 text-xs border-slate-200 hover:bg-slate-50 hover:text-slate-900">
+1K
</Button>
<Button variant="outline" size="sm" className="h-8 text-xs border-slate-200 hover:bg-slate-50 hover:text-slate-900">
+5K
</Button>
<Button size="sm" className="h-8 text-xs bg-black text-white hover:bg-slate-800">
Custom
</Button>
</div>
</GlassCard>
);
})}
</div>
);
};
export default SavingsGoalsGrid;

View File

@@ -0,0 +1,58 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { formatAmount } from '@/lib/utils';
import { DirhamIcon } from '@/components/ui/custom-icons';
import { Badge } from '@/components/ui/badge';
import { motion } from 'framer-motion';
const SavingsGoalsList: React.FC = () => {
const goals = [
{ name: 'Emergency Fund', saved: 25000, target: 50000, status: 'Good Progress', color: 'bg-yellow-500', badgeColor: 'bg-yellow-100 text-yellow-700 border-yellow-200' },
{ name: 'Vacation', saved: 5000, target: 15000, status: 'Good Progress', color: 'bg-blue-500', badgeColor: 'bg-blue-100 text-blue-700 border-blue-200' },
{ name: 'New Car', saved: 85000, target: 120000, status: 'Good Progress', color: 'bg-green-500', badgeColor: 'bg-green-100 text-green-700 border-green-200' },
{ name: 'Wedding', saved: 10000, target: 80000, status: 'Getting Started', color: 'bg-slate-500', badgeColor: 'bg-slate-100 text-slate-600 border-slate-200' },
];
return (
<GlassCard className="p-6 h-full">
<h3 className="text-lg font-semibold text-slate-800 mb-6">Savings Goals</h3>
<div className="space-y-6">
{goals.map((goal, index) => {
const percentage = Math.min((goal.saved / goal.target) * 100, 100);
return (
<div key={index} className="space-y-2">
<div className="flex justify-between items-center text-sm">
<span className="font-medium text-slate-700">{goal.name}</span>
<Badge variant="outline" className={`${goal.badgeColor} text-[10px] px-2 py-0 border`}>
{goal.status}
</Badge>
</div>
<div className="flex justify-between text-xs text-slate-500 mb-1">
<div className="flex items-center gap-1">
<span className="font-semibold text-slate-900"><DirhamIcon className="w-3 h-3 inline" /> {formatAmount(goal.saved)}</span>
</div>
<div className="flex items-center gap-1">
of <DirhamIcon className="w-3 h-3 inline" /> {formatAmount(goal.target)}
</div>
</div>
<div className="w-full bg-slate-100 rounded-full h-2 overflow-hidden">
<motion.div
initial={{ width: 0 }}
animate={{ width: `${percentage}%` }}
transition={{ duration: 1, delay: index * 0.1 }}
className={`h-full ${goal.color} transition-all duration-1000`}
/>
</div>
</div>
);
})}
</div>
</GlassCard>
);
};
export default SavingsGoalsList;

View File

@@ -0,0 +1,86 @@
import React from 'react';
import { GlassCard } from '@/components/ui/GlassCard';
import { formatAmount } from '@/lib/utils';
import { DirhamIcon } from '@/components/ui/custom-icons';
import {
Wallet,
TrendingUp,
Target,
Percent
} from 'lucide-react';
const SavingsSummaryCards: React.FC = () => {
// Hardcoded design values
const data = {
totalSavings: 255000,
monthlyGrowth: 4500,
goalsProgress: 68,
annualYield: 3.5
};
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
{/* Total Savings */}
<GlassCard className="p-6">
<div className="flex items-center justify-between mb-4">
<p className="text-sm font-medium text-slate-500">Total Savings</p>
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-indigo-400 to-indigo-600 flex items-center justify-center shadow-lg shadow-indigo-200">
<Wallet className="w-5 h-5 text-white" />
</div>
</div>
<div className="flex items-center gap-1">
<DirhamIcon className="w-6 h-6 text-slate-900" />
<p className="text-2xl font-bold text-slate-900">{formatAmount(data.totalSavings)}</p>
</div>
<p className="text-xs text-slate-400 mt-2">Across all accounts</p>
</GlassCard>
{/* Monthly Growth */}
<GlassCard className="p-6">
<div className="flex items-center justify-between mb-4">
<p className="text-sm font-medium text-slate-500">Monthly Growth</p>
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-green-400 to-green-600 flex items-center justify-center shadow-lg shadow-green-200">
<TrendingUp className="w-5 h-5 text-white" />
</div>
</div>
<div className="flex items-center gap-1">
<DirhamIcon className="w-6 h-6 text-slate-900" />
<p className="text-2xl font-bold text-slate-900">{formatAmount(data.monthlyGrowth)}</p>
</div>
<div className="flex items-center mt-2">
<TrendingUp className="w-3 h-3 text-green-500 mr-1" />
<span className="text-xs text-green-600 font-medium">+1.8% vs last month</span>
</div>
</GlassCard>
{/* Goals Progress */}
<GlassCard className="p-6">
<div className="flex items-center justify-between mb-4">
<p className="text-sm font-medium text-slate-500">Goals Progress</p>
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-blue-400 to-blue-600 flex items-center justify-center shadow-lg shadow-blue-200">
<Target className="w-5 h-5 text-white" />
</div>
</div>
<p className="text-2xl font-bold text-slate-900">{data.goalsProgress}%</p>
<div className="w-full bg-slate-100 rounded-full h-1.5 mt-3 overflow-hidden">
<div className="h-full bg-blue-500" style={{ width: `${data.goalsProgress}%` }}></div>
</div>
</GlassCard>
{/* Annual Yield */}
<GlassCard className="p-6">
<div className="flex items-center justify-between mb-4">
<p className="text-sm font-medium text-slate-500">Annual Yield (APY)</p>
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-purple-400 to-purple-600 flex items-center justify-center shadow-lg shadow-purple-200">
<Percent className="w-5 h-5 text-white" />
</div>
</div>
<p className="text-2xl font-bold text-slate-900">{data.annualYield}%</p>
<p className="text-xs text-slate-400 mt-2">Average across liquid assets</p>
</GlassCard>
</div>
);
};
export default SavingsSummaryCards;

View File

@@ -1,5 +1,6 @@
import { cn } from "@/lib/utils";
import React from "react";
import { useTheme } from "@/context/ThemeContext";
interface GlassCardProps extends React.HTMLAttributes<HTMLDivElement> {
children: React.ReactNode;
@@ -13,11 +14,23 @@ export const GlassCard = ({
hoverEffect = false,
...props
}: GlassCardProps) => {
const { theme } = useTheme();
const getGlassStyle = () => {
if (theme === 'default') {
return "bg-white/40 border-white/40 shadow-sm backdrop-blur-xl";
}
// "Super Glass" for image backgrounds
// Increased transparency, higher blur, and adjusting border for contrast
return "bg-white/10 border-white/20 shadow-xl backdrop-blur-2xl text-current";
};
return (
<div
className={cn(
"glass-card bg-white/40 border border-white/40 shadow-sm backdrop-blur-xl",
hoverEffect && "hover:bg-white/50 transition-all duration-300 hover:scale-[1.02]",
"glass-card border",
getGlassStyle(),
hoverEffect && "hover:bg-white/20 transition-all duration-300 hover:scale-[1.02]",
className
)}
{...props}

View File

@@ -0,0 +1,17 @@
import React from 'react';
import { cn } from '@/lib/utils';
interface IconProps extends React.SVGProps<SVGSVGElement> {
className?: string;
}
export const DirhamIcon = ({ className, ...props }: IconProps) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 344.84 299.91"
className={cn("w-4 h-4 fill-current", className)}
{...props}
>
<path d="M342.14,140.96l2.7,2.54v-7.72c0-17-11.92-30.84-26.56-30.84h-23.41C278.49,36.7,222.69,0,139.68,0c-52.86,0-59.65,0-109.71,0,0,0,15.03,12.63,15.03,52.4v52.58h-27.68c-5.38,0-10.43-2.08-14.61-6.01l-2.7-2.54v7.72c0,17.01,11.92,30.84,26.56,30.84h18.44s0,29.99,0,29.99h-27.68c-5.38,0-10.43-2.07-14.61-6.01l-2.7-2.54v7.71c0,17,11.92,30.82,26.56,30.82h18.44s0,54.89,0,54.89c0,38.65-15.03,50.06-15.03,50.06h109.71c85.62,0,139.64-36.96,155.38-104.98h32.46c5.38,0,10.43,2.07,14.61,6l2.7,2.54v-7.71c0-17-11.92-30.83-26.56-30.83h-18.9c.32-4.88.49-9.87.49-15s-.18-10.11-.51-14.99h28.17c5.37,0,10.43,2.07,14.61,6.01ZM89.96,15.01h45.86c61.7,0,97.44,27.33,108.1,89.94l-153.96.02V15.01ZM136.21,284.93h-46.26v-89.98l153.87-.02c-9.97,56.66-42.07,88.38-107.61,90ZM247.34,149.96c0,5.13-.11,10.13-.34,14.99l-157.04.02v-29.99l157.05-.02c.22,4.84.33,9.83.33,15Z" />
</svg>
);

View File

@@ -0,0 +1,74 @@
import React, { createContext, useContext, useEffect, useState } from 'react';
type Theme = 'default' | 'dubai' | 'minimal' | 'nature';
interface ThemeContextType {
theme: Theme;
setTheme: (theme: Theme) => void;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [theme, setTheme] = useState<Theme>(() => {
if (typeof window !== 'undefined') {
const savedTheme = localStorage.getItem('app-theme');
return (savedTheme as Theme) || 'default';
}
return 'default';
});
useEffect(() => {
localStorage.setItem('app-theme', theme);
const root = window.document.documentElement;
// Remove old theme classes
root.classList.remove('theme-default', 'theme-dubai', 'theme-minimal', 'theme-nature');
// Add new theme class
root.classList.add(`theme-${theme}`);
// Check if we need to force dark mode for specific themes
if (theme === 'dubai') {
root.classList.add('dark');
} else {
root.classList.remove('dark');
}
}, [theme]);
// Background Image Map
const getBackgroundImage = () => {
switch (theme) {
case 'dubai': return "url('https://images.unsplash.com/photo-1512453979798-5ea9ba6a80f4?q=80&w=2000&auto=format&fit=crop')";
case 'minimal': return "url('https://images.unsplash.com/photo-1550684848-fac1c5b4e853?q=80&w=2000&auto=format&fit=crop')";
case 'nature': return "url('https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?q=80&w=2000&auto=format&fit=crop')";
default: return 'none'; // Gradient handles default
}
};
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{/* Background Layer managed here for global coverage */}
{theme !== 'default' && (
<div
className="fixed inset-0 z-[-1] transition-all duration-700 ease-in-out bg-cover bg-center bg-no-repeat bg-fixed"
style={{ backgroundImage: getBackgroundImage() }}
/>
)}
{/* Overlay for contrast if needed */}
{theme === 'dubai' && <div className="fixed inset-0 z-[-1] bg-black/40 pointer-events-none" />}
{children}
</ThemeContext.Provider>
);
};
export const useTheme = () => {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
};

View File

@@ -5,10 +5,9 @@ export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
export const formatCurrency = (amount: number) => {
export const formatAmount = (amount: number) => {
return new Intl.NumberFormat('en-AE', {
style: 'currency',
currency: 'AED',
style: 'decimal',
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(amount);