From 557accf0c2bdeb46ecd8113170694522084bdbb1 Mon Sep 17 00:00:00 2001 From: CycroftX Date: Mon, 16 Feb 2026 00:09:52 +0530 Subject: [PATCH] Refactor: Global Theme System and Finance Modules Redesign --- src/App.tsx | 19 +- src/components/BudgetRing.tsx | 15 +- src/components/Dashboard.tsx | 228 ++++---------- src/components/Layout.tsx | 19 +- src/components/budget/AddExpenseForm.tsx | 57 ++++ src/components/budget/BudgetCategoryGrid.tsx | 68 ++++ src/components/budget/BudgetOverviewList.tsx | 68 ++++ src/components/budget/BudgetSummaryCards.tsx | 92 ++++++ src/components/budget/FullExpensesTable.tsx | 69 +++++ .../budget/RecentTransactionsCard.tsx | 45 +++ src/components/budget/SetBudgetForm.tsx | 52 ++++ .../commitment/AddCommitmentForm.tsx | 45 +++ .../commitment/CommitmentAITips.tsx | 63 ++++ .../commitment/CommitmentCalendarTable.tsx | 85 +++++ .../commitment/UpcomingCommitmentsList.tsx | 90 ++++++ src/components/credit/AECBInfoCard.tsx | 49 +++ src/components/credit/CreditCardsList.tsx | 127 ++++++++ src/components/credit/CreditScoreProgress.tsx | 72 +++++ src/components/credit/CreditScoreTips.tsx | 58 ++++ src/components/credit/CreditSummaryCards.tsx | 80 +++++ .../credit/CreditUtilizationList.tsx | 48 +++ src/components/dashboard/AIInsights.tsx | 55 ++++ src/components/dashboard/GoalsList.tsx | 63 ++++ src/components/dashboard/HeroStats.tsx | 85 +++++ .../dashboard/InvestmentPerformance.tsx | 73 +++++ src/components/dashboard/QuickActions.tsx | 49 +++ src/components/dashboard/TransactionsList.tsx | 90 ++++++ src/components/features/BudgetManager.tsx | 252 ++++----------- src/components/features/CommitmentAdvisor.tsx | 214 +++---------- src/components/features/CreditManager.tsx | 220 ++++--------- src/components/features/GoalPlanning.tsx | 69 +++-- src/components/features/PortfolioManager.tsx | 256 +++++---------- src/components/features/Profile.tsx | 39 ++- src/components/features/SavingsBooster.tsx | 293 +++++------------- src/components/portfolio/AssetAllocation.tsx | 67 ++++ src/components/portfolio/HoldingsTable.tsx | 90 ++++++ .../portfolio/PortfolioAIInsights.tsx | 57 ++++ .../portfolio/PortfolioSummaryCards.tsx | 97 ++++++ .../portfolio/QuickInvestmentForm.tsx | 56 ++++ src/components/portfolio/RiskAnalysis.tsx | 34 ++ src/components/savings/AddGoalForm.tsx | 72 +++++ .../savings/CompoundInterestCalculator.tsx | 94 ++++++ .../savings/SavingsAccountsList.tsx | 50 +++ .../savings/SavingsAccountsTable.tsx | 73 +++++ src/components/savings/SavingsGoalsGrid.tsx | 69 +++++ src/components/savings/SavingsGoalsList.tsx | 58 ++++ .../savings/SavingsSummaryCards.tsx | 86 +++++ src/components/ui/GlassCard.tsx | 17 +- src/components/ui/custom-icons.tsx | 17 + src/context/ThemeContext.tsx | 74 +++++ src/lib/utils.ts | 5 +- 51 files changed, 3076 insertions(+), 1147 deletions(-) create mode 100644 src/components/budget/AddExpenseForm.tsx create mode 100644 src/components/budget/BudgetCategoryGrid.tsx create mode 100644 src/components/budget/BudgetOverviewList.tsx create mode 100644 src/components/budget/BudgetSummaryCards.tsx create mode 100644 src/components/budget/FullExpensesTable.tsx create mode 100644 src/components/budget/RecentTransactionsCard.tsx create mode 100644 src/components/budget/SetBudgetForm.tsx create mode 100644 src/components/commitment/AddCommitmentForm.tsx create mode 100644 src/components/commitment/CommitmentAITips.tsx create mode 100644 src/components/commitment/CommitmentCalendarTable.tsx create mode 100644 src/components/commitment/UpcomingCommitmentsList.tsx create mode 100644 src/components/credit/AECBInfoCard.tsx create mode 100644 src/components/credit/CreditCardsList.tsx create mode 100644 src/components/credit/CreditScoreProgress.tsx create mode 100644 src/components/credit/CreditScoreTips.tsx create mode 100644 src/components/credit/CreditSummaryCards.tsx create mode 100644 src/components/credit/CreditUtilizationList.tsx create mode 100644 src/components/dashboard/AIInsights.tsx create mode 100644 src/components/dashboard/GoalsList.tsx create mode 100644 src/components/dashboard/HeroStats.tsx create mode 100644 src/components/dashboard/InvestmentPerformance.tsx create mode 100644 src/components/dashboard/QuickActions.tsx create mode 100644 src/components/dashboard/TransactionsList.tsx create mode 100644 src/components/portfolio/AssetAllocation.tsx create mode 100644 src/components/portfolio/HoldingsTable.tsx create mode 100644 src/components/portfolio/PortfolioAIInsights.tsx create mode 100644 src/components/portfolio/PortfolioSummaryCards.tsx create mode 100644 src/components/portfolio/QuickInvestmentForm.tsx create mode 100644 src/components/portfolio/RiskAnalysis.tsx create mode 100644 src/components/savings/AddGoalForm.tsx create mode 100644 src/components/savings/CompoundInterestCalculator.tsx create mode 100644 src/components/savings/SavingsAccountsList.tsx create mode 100644 src/components/savings/SavingsAccountsTable.tsx create mode 100644 src/components/savings/SavingsGoalsGrid.tsx create mode 100644 src/components/savings/SavingsGoalsList.tsx create mode 100644 src/components/savings/SavingsSummaryCards.tsx create mode 100644 src/components/ui/custom-icons.tsx create mode 100644 src/context/ThemeContext.tsx diff --git a/src/App.tsx b/src/App.tsx index 67f6a6b..c831272 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -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 = () => ( - - - } /> - } /> - {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */} - } /> - - + + + + } /> + } /> + {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */} + } /> + + + diff --git a/src/components/BudgetRing.tsx b/src/components/BudgetRing.tsx index e03503b..2342561 100644 --- a/src/components/BudgetRing.tsx +++ b/src/components/BudgetRing.tsx @@ -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 =

{label}

-

₹{spent / 1000}k / ₹{total / 1000}k

+
+
+ + {formatAmount(spent)} +
+ / +
+ + {formatAmount(total)} +
+
); diff --git a/src/components/Dashboard.tsx b/src/components/Dashboard.tsx index 93d9873..3e2c85d 100644 --- a/src/components/Dashboard.tsx +++ b/src/components/Dashboard.tsx @@ -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 ( -
- {/* Hero Section */} +
+ {/* Header Section */}
+
{currentDate}

- {greeting}, Anjali + Good evening, Anjali!

-

Here's your financial snapshot for today.

+
+ Monthly Wealth Growth: +
+ {monthlyWealthGrowth} +
+
+ + {/* Search Bar */}
@@ -82,137 +54,45 @@ const Dashboard: React.FC = () => {
- {/* Key Metrics - Budget Rings */} + {/* Hero Stats (Top Row) */}
-
- {budgetData.categories.map((cat, idx) => ( - - - - ))} + +
+ + {/* Main Content Area (2 Columns) */} +
+ {/* Left Column: Recent Transactions */} +
+ +
+ + {/* Right Column: Financial Goals */} +
+
- {/* Portfolio & Net Worth - Sliding Cards */} -
-

Portfolio Highlights

- {/* Horizontal Scroll / Slider */} -
- {/* Total Net Worth Card */} - -
-
- -
- - +3.2% - -
-
-

Total Net Worth

-

{formatCurrency(portfolioData.totalValue)}

-
-
+ {/* Bottom Section (Full Width) - Performance & AI Insights */} +
+ {/* Investment Performance (Takes 2 columns) */} +
+ +
- {/* Savings Card */} - -
-
- -
- - 28% Rate - -
-
-

Total Savings

-

{formatCurrency(portfolioData.totalValue * 0.15)}

-
-
- - {/* Credit Score Card */} - -
-
- -
- - Excellent - -
-
-

Credit Score

-

{creditScore}

-
-
+ {/* AI Insights (Takes 1 column) */} +
+
- {/* AI Insights & Goals */} -
- -
- -

AI Recommendations

-
-
-
-
- -
-
-

Optimize Savings

-

Cancel unused streaming subscription to save {formatCurrency(150)}/year. You haven't used it in 3 months.

-
-
-
-
- -
-
-

Credit utilization

-

Pay down {formatCurrency(1200)} on ADCB card to keep utilization under 30%.

-
-
-
-
- - -
- -

Goal Progress

-
-
- {savingsGoals.map((goal, idx) => ( -
-
- {goal.name} - {formatCurrency(goal.current)} / {formatCurrency(goal.target)} -
-
- -
-
- ))} - -
-
+ {/* Footer Actions */} +
+

Quick Actions

+
); }; export default Dashboard; + diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx index 2aa868b..9825def 100644 --- a/src/components/Layout.tsx +++ b/src/components/Layout.tsx @@ -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 ( -
- {/* Ambient Background Mesh */} -
-
-
-
-
+
+ {/* Ambient Background Mesh - Only show for default theme */} + {theme === 'default' && ( +
+
+
+
+
+ )} {/* Main Content Area */}
diff --git a/src/components/budget/AddExpenseForm.tsx b/src/components/budget/AddExpenseForm.tsx new file mode 100644 index 0000000..48d77b5 --- /dev/null +++ b/src/components/budget/AddExpenseForm.tsx @@ -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 ( +
+ +

Add New Expense

+ +
+
+ +
+
+ +
+ +
+
+ +
+ + +
+ +
+ + +
+ + +
+
+
+ ); +}; + +export default AddExpenseForm; diff --git a/src/components/budget/BudgetCategoryGrid.tsx b/src/components/budget/BudgetCategoryGrid.tsx new file mode 100644 index 0000000..cf7bc4a --- /dev/null +++ b/src/components/budget/BudgetCategoryGrid.tsx @@ -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 ( +
+ {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 ( + +
+
+ +
+ + {isOverGroups ? "Over Budget" : "Active"} + +
+ +

{cat.name}

+
+ {remaining < 0 ? ( + Over by {formatAmount(Math.abs(remaining))} + ) : ( + {formatAmount(remaining)} left + )} +
+ +
+ +
+ +
+ {formatAmount(cat.spent)} + {formatAmount(cat.limit)} +
+
+ ); + })} +
+ ); +}; + +export default BudgetCategoryGrid; diff --git a/src/components/budget/BudgetOverviewList.tsx b/src/components/budget/BudgetOverviewList.tsx new file mode 100644 index 0000000..6fc1d20 --- /dev/null +++ b/src/components/budget/BudgetOverviewList.tsx @@ -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 ( + +

Budget Overview

+
+ {categories.map((cat, index) => { + const percentage = Math.min((cat.spent / cat.limit) * 100, 100); + return ( +
+
+ {cat.name} + + {cat.status} + +
+ +
+
+ {formatAmount(cat.spent)} +
+
+ of {formatAmount(cat.limit)} +
+
+ +
+ +
+
+ ); + })} +
+
+ ); +}; + +export default BudgetOverviewList; diff --git a/src/components/budget/BudgetSummaryCards.tsx b/src/components/budget/BudgetSummaryCards.tsx new file mode 100644 index 0000000..de3b11e --- /dev/null +++ b/src/components/budget/BudgetSummaryCards.tsx @@ -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 ( +
+ {/* Total Income */} + +
+

Total Income

+
+ +
+
+
+ +

{formatAmount(data.income)}

+
+

Monthly inflow

+
+ + {/* Total Spent */} + +
+

Total Spent

+
+ +
+
+
+ +

{formatAmount(data.spent)}

+
+

+ {(data.spent / data.income * 100).toFixed(1)}% of income +

+
+ + {/* Budget Remaining */} + +
+

Budget Remaining

+
+ +
+
+
+ +

{formatAmount(remaining)}

+
+
+
+
+
+ + {/* Savings Rate */} + +
+

Savings Rate

+
+ +
+
+

{data.savingsRate}%

+

+ +2% from last month +

+
+
+ ); +}; + +export default BudgetSummaryCards; diff --git a/src/components/budget/FullExpensesTable.tsx b/src/components/budget/FullExpensesTable.tsx new file mode 100644 index 0000000..46eb5a3 --- /dev/null +++ b/src/components/budget/FullExpensesTable.tsx @@ -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 ( + + + + + Date + Description + Category + Type + Amount + + + + {transactions.map((tx, idx) => ( + + {tx.date} + {tx.desc} + + + {tx.category} + + + + + {tx.type} + + + + + {tx.type === 'income' ? '+' : '-'} {formatAmount(Math.abs(tx.amount))} + + + + ))} + +
+
+ ); +}; + +export default FullExpensesTable; diff --git a/src/components/budget/RecentTransactionsCard.tsx b/src/components/budget/RecentTransactionsCard.tsx new file mode 100644 index 0000000..12767c4 --- /dev/null +++ b/src/components/budget/RecentTransactionsCard.tsx @@ -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 ( + +

Recent Transactions

+
+ {transactions.map((tx, index) => ( +
+
+
+ {tx.type === 'income' ? : } +
+
+

{tx.name}

+

{tx.date} • {tx.category}

+
+
+
+ + {tx.type === 'income' ? '+' : '-'} {formatAmount(Math.abs(tx.amount))} + +
+
+ ))} +
+
+ ); +}; + +export default RecentTransactionsCard; diff --git a/src/components/budget/SetBudgetForm.tsx b/src/components/budget/SetBudgetForm.tsx new file mode 100644 index 0000000..43400ae --- /dev/null +++ b/src/components/budget/SetBudgetForm.tsx @@ -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 ( +
+ +

Set Monthly Budget

+ +
+
+ + +
+ +
+ +
+
+ +
+ +
+
+ + +
+
+
+ ); +}; + +export default SetBudgetForm; diff --git a/src/components/commitment/AddCommitmentForm.tsx b/src/components/commitment/AddCommitmentForm.tsx new file mode 100644 index 0000000..f9228b9 --- /dev/null +++ b/src/components/commitment/AddCommitmentForm.tsx @@ -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 ( +
+ +

Add New Commitment

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+
+
+ ); +}; + +export default AddCommitmentForm; diff --git a/src/components/commitment/CommitmentAITips.tsx b/src/components/commitment/CommitmentAITips.tsx new file mode 100644 index 0000000..d343077 --- /dev/null +++ b/src/components/commitment/CommitmentAITips.tsx @@ -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 ( +
+ +
+ +

AI Financial Insights

+
+

Smart recommendations to optimize your cash flow.

+ +
+ {tips.map((tip, idx) => ( +
+

{tip.title}

+

{tip.desc}

+
+ ))} +
+
+ + +
+
+ +
+
+

Did you know?

+

Paying bills 2 days early improves credit score.

+
+
+ +
+
+ ); +}; + +export default CommitmentAITips; diff --git a/src/components/commitment/CommitmentCalendarTable.tsx b/src/components/commitment/CommitmentCalendarTable.tsx new file mode 100644 index 0000000..4a5e45f --- /dev/null +++ b/src/components/commitment/CommitmentCalendarTable.tsx @@ -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 Due; + case 'Paid': return Paid; + default: return Upcoming; + } + }; + + return ( + +
+

Commitment Calendar

+

View and manage all your scheduled payments.

+
+ + + + Commitment + Type + Amount (AED) + Due Date + Status + Actions + + + + {commitments.map((item, idx) => ( + + {item.name} + {item.type} + + + {formatAmount(item.amount)} + + + {item.date} + {getStatusBadge(item.status)} + +
+ {item.status !== 'Paid' && ( + + )} + +
+
+
+ ))} +
+
+
+ ); +}; + +export default CommitmentCalendarTable; diff --git a/src/components/commitment/UpcomingCommitmentsList.tsx b/src/components/commitment/UpcomingCommitmentsList.tsx new file mode 100644 index 0000000..736a247 --- /dev/null +++ b/src/components/commitment/UpcomingCommitmentsList.tsx @@ -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 ( + +

Upcoming Commitments

+
+ {commitments.map((item) => ( +
+
+
+ +
+
+

{item.title}

+

{item.type}

+
+
+ +
+
+

+ {formatAmount(item.amount)} +

+

Due {item.dueDate}

+
+ +
+ + +
+
+
+ ))} +
+
+
+

Total Due This Month

+

+ {formatAmount(9050)} +

+
+ +
+
+ ); +}; + +export default UpcomingCommitmentsList; diff --git a/src/components/credit/AECBInfoCard.tsx b/src/components/credit/AECBInfoCard.tsx new file mode 100644 index 0000000..7d0e298 --- /dev/null +++ b/src/components/credit/AECBInfoCard.tsx @@ -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 ( + +
+

Al Etihad Credit Bureau (AECB)

+

Understanding your score range.

+
+ + + + {ranges.map((range, idx) => ( + + {range.label} + {range.range} + + ))} + +
+ +
+

+ Your current score of 785 puts you in the top 15% of UAE residents! +

+
+
+ ); +}; + +export default AECBInfoCard; diff --git a/src/components/credit/CreditCardsList.tsx b/src/components/credit/CreditCardsList.tsx new file mode 100644 index 0000000..77350b0 --- /dev/null +++ b/src/components/credit/CreditCardsList.tsx @@ -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 ( +
+ {cards.map((card, idx) => { + const utilization = Math.round((card.balance / card.limit) * 100); + + return ( + + {/* Visual Card Representation */} +
+
+
+ {card.network} +
+
+

Current Balance

+

{formatAmount(card.balance)}

+
+
+ **** **** **** 4242 + EXP 12/28 +
+
+ + {/* Details & Actions */} +
+
+
+

{card.name}

+
+ {card.network} + Due Date: {card.dueDate} +
+
+ +
+ +
+
+

Credit Limit

+

{formatAmount(card.limit)}

+
+
+

Utilization

+

30 ? 'text-orange-600' : 'text-green-600'}`}>{utilization}%

+
+
+

Interest Rate

+

{card.rate}% / mo

+
+
+

Available Crdt.

+

{formatAmount(card.limit - card.balance)}

+
+
+ +
+
+

Min Payment Due

+

+ {formatAmount(card.minDue)} +

+
+
+ + +
+
+
+ + ); + })} +
+ ); +}; + +export default CreditCardsList; diff --git a/src/components/credit/CreditScoreProgress.tsx b/src/components/credit/CreditScoreProgress.tsx new file mode 100644 index 0000000..ea205ae --- /dev/null +++ b/src/components/credit/CreditScoreProgress.tsx @@ -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 ( + +
+

Credit Score Progress

+ +
+
+ Current Sore + {score} +
+ + {/* Custom Range Bar */} +
+
+ + +
+
+ 300 + 850 +
+
+
+ +
+

Score Factors

+ {factors.map((factor, idx) => ( +
+
+ + {factor.name} +
+ + {factor.status} + +
+ ))} +
+
+ ); +}; + +export default CreditScoreProgress; diff --git a/src/components/credit/CreditScoreTips.tsx b/src/components/credit/CreditScoreTips.tsx new file mode 100644 index 0000000..0359568 --- /dev/null +++ b/src/components/credit/CreditScoreTips.tsx @@ -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 ( +
+ {tips.map((tip, idx) => ( + +
+
+ +
+
+

{tip.title}

+

{tip.desc}

+
+
+
+ ))} +
+ ); +}; + +export default CreditScoreTips; diff --git a/src/components/credit/CreditSummaryCards.tsx b/src/components/credit/CreditSummaryCards.tsx new file mode 100644 index 0000000..dfa4463 --- /dev/null +++ b/src/components/credit/CreditSummaryCards.tsx @@ -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 ( +
+ {/* Credit Score */} + +
+

Credit Score

+
+ +
+
+

{data.score}

+

Excellent • Top 15%

+
+ + {/* Total Balance */} + +
+

Total Balance

+
+ +
+
+
+ +

{formatAmount(data.balance)}

+
+
+ + AED 1,200 from last month +
+
+ + {/* Utilization */} + +
+

Utilization

+
+ +
+
+

{data.utilization}%

+
+
+
+
+ + {/* Min Payment */} + +
+

Min Payment Due

+
+ +
+
+
+ +

{formatAmount(data.minPayment)}

+
+

Due in 3 days

+
+
+ ); +}; + +export default CreditSummaryCards; diff --git a/src/components/credit/CreditUtilizationList.tsx b/src/components/credit/CreditUtilizationList.tsx new file mode 100644 index 0000000..53553f4 --- /dev/null +++ b/src/components/credit/CreditUtilizationList.tsx @@ -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 ( + +

Credit Utilization

+
+ {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 ( +
+
+ {card.name} + + {utilization}% Utilized + +
+ +
+ {formatAmount(card.balance)} + Limit: {formatAmount(card.limit)} +
+
+ ) + })} +
+
+ ); +}; + +export default CreditUtilizationList; diff --git a/src/components/dashboard/AIInsights.tsx b/src/components/dashboard/AIInsights.tsx new file mode 100644 index 0000000..27a3e92 --- /dev/null +++ b/src/components/dashboard/AIInsights.tsx @@ -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: , + 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: , + 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: , + colors: 'bg-purple-50 border-purple-100 text-purple-700', + iconColor: 'bg-purple-100 text-purple-600' + } + ]; + + return ( + +

AI Insights

+
+ {insights.map((insight, idx) => ( +
+
+ {insight.icon} +
+
+

{insight.title}

+

{insight.description}

+
+
+ ))} +
+
+ ); +}; + +export default AIInsights; diff --git a/src/components/dashboard/GoalsList.tsx b/src/components/dashboard/GoalsList.tsx new file mode 100644 index 0000000..9654afc --- /dev/null +++ b/src/components/dashboard/GoalsList.tsx @@ -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 ( + +
+

+ Financial Goals +

+
+ +
+ {goals.map((goal, idx) => ( +
+
+ {goal.name} + {goal.amount} +
+
+
+
+

{goal.progress}% Completed

+
+ ))} +
+ + +
+ ); +}; + +export default GoalsList; diff --git a/src/components/dashboard/HeroStats.tsx b/src/components/dashboard/HeroStats.tsx new file mode 100644 index 0000000..23ca2c6 --- /dev/null +++ b/src/components/dashboard/HeroStats.tsx @@ -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: , + color: 'from-purple-500 to-indigo-600', + iconBg: 'bg-white/20' + }, + { + label: 'Monthly Contributions', + value: '12,470', + change: '+5.7%', + isPositive: true, + icon: , + color: 'from-blue-500 to-cyan-600', + iconBg: 'bg-white/20' + }, + { + label: 'Annual Returns', + value: '12.7%', + change: '+2.1%', + isPositive: true, + icon: , + 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: , + color: 'from-orange-500 to-red-600', + iconBg: 'bg-white/20', + isScore: true + } + ]; + + return ( +
+ {stats.map((stat, idx) => ( + +
+
+
+ {stat.icon} +
+ + {stat.change} + +
+
+

{stat.label}

+
+ {!stat.isPercentage && !stat.isScore && } +

{stat.value}

+
+
+
+ {/* Decorative background element */} +
+ + ))} +
+ ); +}; + +export default HeroStats; diff --git a/src/components/dashboard/InvestmentPerformance.tsx b/src/components/dashboard/InvestmentPerformance.tsx new file mode 100644 index 0000000..f957320 --- /dev/null +++ b/src/components/dashboard/InvestmentPerformance.tsx @@ -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 ( + +
+

Investment Performance

+ +
+ +
+
+

Total Portfolio

+
+ 2.85M +
+
+
+

Annual Return

+

+12.7%

+
+
+

Monthly Contrib.

+
+ 12.5K +
+
+
+

Risk Score

+

3.2/10

+
+
+ +
+ + + + + + + + + + + + + +
+
+ ); +}; + +export default InvestmentPerformance; diff --git a/src/components/dashboard/QuickActions.tsx b/src/components/dashboard/QuickActions.tsx new file mode 100644 index 0000000..cf6ceca --- /dev/null +++ b/src/components/dashboard/QuickActions.tsx @@ -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: , + color: 'hover:border-green-200 hover:bg-green-50/50 hover:text-green-700' + }, + { + label: 'Set Goals', + icon: , + color: 'hover:border-blue-200 hover:bg-blue-50/50 hover:text-blue-700' + }, + { + label: 'View Reports', + icon: , + color: 'hover:border-purple-200 hover:bg-purple-50/50 hover:text-purple-700' + }, + { + label: 'Savings Plan', + icon: , + color: 'hover:border-pink-200 hover:bg-pink-50/50 hover:text-pink-700' + } + ]; + + return ( +
+ {actions.map((action, idx) => ( + +
+ {action.icon} +
+ {action.label} +
+ ))} +
+ ); +}; + +export default QuickActions; diff --git a/src/components/dashboard/TransactionsList.tsx b/src/components/dashboard/TransactionsList.tsx new file mode 100644 index 0000000..6d4e6b1 --- /dev/null +++ b/src/components/dashboard/TransactionsList.tsx @@ -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 ( + +
+

Recent Transactions

+
+ +
+ {transactions.map((tx) => ( +
+
+
+ {tx.icon} +
+
+

{tx.name}

+
+ {tx.date} +
+
+
+
+
+ {tx.amount} +
+ + {tx.status} + +
+
+ ))} +
+ + +
+ ); +}; + +export default TransactionsList; diff --git a/src/components/features/BudgetManager.tsx b/src/components/features/BudgetManager.tsx index 9b63f2a..fe8716b 100644 --- a/src/components/features/BudgetManager.tsx +++ b/src/components/features/BudgetManager.tsx @@ -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 ( -
- {/* Header */} -
+
+ + {/* Header Section */} +
-

Monthly Budget & Goals

-

Track your spending and save for the future.

+

+ Budget Manager +

+

Track every dirham with precision.

- -
- -
- {/* Main Budget Ring */} - -
- {/* Background Circle */} - - - {/* Progress Circle */} - 90 ? "text-red-500" : percentage > 75 ? "text-orange-500" : "text-blue-500" - )} - /> - -
- Total Spent - - {formatCurrency(budgetData.spent)} - - - of {formatCurrency(budgetData.limit)} - -
-
- {percentage > 90 && ( - - - You've used {percentage.toFixed(0)}% of your budget! - - )} -
- - {/* Categories & Goals */} -
- {/* Categories Breakdown */} - -

- Category Breakdown -

-
- {budgetData.categories.map((cat, index) => ( - -
-
-
- -
- {cat.name} -
-
- {formatCurrency(cat.spent)} - / {formatCurrency(cat.limit)} -
-
-
- -
-
- ))} -
-
- - {/* Long-term Goals */} -
-

- Long-term Planning -

-
- {budgetData.goals.map((goal, index) => ( - -
-
-
-

{goal.name}

-
- On Track -
-
-
- -
-
- -
-
- Progress - {Math.round((goal.current / goal.target) * 100)}% -
- -
- {formatCurrency(goal.current)} - {formatCurrency(goal.target)} -
-
-
- - {/* Hover Effect Background */} -
- - ))} -
-
+
+ +
+ + {/* Summary Cards */} + + + {/* Main Content Tabs */} + + + Overview + Budgets + Expenses + Add Expense + Set Budget + + + {/* Tab 1: Overview */} + +
+ + +
+
+ + {/* Tab 2: Budgets */} + + + + + {/* Tab 3: Expenses */} + + + + + {/* Tab 4: Add Expense */} + + + + + {/* Tab 5: Set Budget */} + + + + +
); } \ No newline at end of file diff --git a/src/components/features/CommitmentAdvisor.tsx b/src/components/features/CommitmentAdvisor.tsx index a9e155f..3e5b9c0 100644 --- a/src/components/features/CommitmentAdvisor.tsx +++ b/src/components/features/CommitmentAdvisor.tsx @@ -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 ( -
-
-
-

- Commitment Advisor -

-

Stay on top of your bills and financial obligations.

-
+
-
- - + {/* Header Section */} +
+
+

+ + Commitment Advisor +

+

Stay on top of your bills and financial obligations.

+
+
+
-
- {/* Main Content */} -
- - {view === 'list' ? ( - - {commitments.map((item) => ( - -
-
- -
-
-

{item.title}

-
- Due {item.dueDate} -
-
-
+ {/* Main Content Tabs */} + + + Overview + Calendar + Add + -
-
-

AED {item.amount.toLocaleString()}

- - {item.status} ({item.daysLeft} days) - -
- -
-
- ))} -
- ) : ( - - - -

Calendar View

-

- Calendar visualization is currently in development. Please use the List view to manage your upcoming payments. -

-
-
- )} -
-
- - {/* Sidebar */} -
-
-
-

Total Due this Month

-

AED 9,050

-

3 payments remaining

-
-
-
+ {/* Tab 1: Overview */} + +
+
+
-
- +
+
+ -

- AI Insights -

+ {/* Tab 2: Calendar */} + + + - {aiTips.map((tip, idx) => ( - -

{tip.title}

-

{tip.desc}

- -
- ))} -
-
+ {/* Tab 3: Add */} + + + + +
); } \ No newline at end of file diff --git a/src/components/features/CreditManager.tsx b/src/components/features/CreditManager.tsx index 86742f9..e78111b 100644 --- a/src/components/features/CreditManager.tsx +++ b/src/components/features/CreditManager.tsx @@ -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 ( -
- - - - -
- {score} - Excellent -
-
- ); -}; +// 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 ( -
-
-

- AI Credit Slashing System -

-

Master your credit health and eliminate debt faster.

-
+
-
- {/* Credit Health Score */} - -

- Credit Health - - +13 pts - -

- -

- Your score is higher than 85% of users in UAE. Keep it up! -

-
- - {/* Strategy & Visualizer */} -
- {/* Strategy Card */} - -
-
- -
-
-

Strategy: {creditData.strategy.method} Method Recommended

-

{creditData.strategy.recommendation}

- -
-
-
- - {/* Debt Visualizer */} - -

- Debt Breakdown -

-
- - - - - - - - - - -
-
+ {/* Header Section */} +
+
+

+ + Credit Manager +

+

Master your credit health and eliminate debt.

+
+
+ +
- {/* Offers / Cards */} -
- -
- -
-
-

Total Credit Utilization

-

12%

-
- -
+ {/* Summary Cards */} + - -
-

Next Payment

-

Credit Card A • Due in 3 days

-
-
-

AED 800

- -
-
+ {/* Main Content Tabs */} + + + Overview + Cards + Score Tips + - -
- -
-

Action Required

-

Car Loan interest rate revised to 9.2%. Review refinance options.

+ {/* Tab 1: Overview */} + +
+ + +
+
+ + {/* Tab 2: Cards */} + + + + + {/* Tab 3: Score Tips */} + +
+
+

Tips to Improve

+
+
- -
+ + +
); } \ No newline at end of file diff --git a/src/components/features/GoalPlanning.tsx b/src/components/features/GoalPlanning.tsx index a8f2164..76e7f36 100644 --- a/src/components/features/GoalPlanning.tsx +++ b/src/components/features/GoalPlanning.tsx @@ -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 = () => {

Target Amount

-

{formatCurrency(progressInsights.totalTargetAmount)}

+
+ +

{formatAmount(progressInsights.totalTargetAmount)}

+

Total across goals

@@ -277,7 +281,10 @@ const GoalPlanning: React.FC = () => {

Monthly SIP

-

{formatCurrency(progressInsights.monthlyCommitment)}

+
+ +

{formatAmount(progressInsights.monthlyCommitment)}

+

Total commitment

@@ -334,7 +341,9 @@ const GoalPlanning: React.FC = () => {
Progress - {formatCurrency(goal.currentAmount)} / {formatCurrency(goal.targetAmount)} +
+ {formatAmount(goal.currentAmount)} / {formatAmount(goal.targetAmount)} +

@@ -345,7 +354,9 @@ const GoalPlanning: React.FC = () => {

Monthly SIP

-

{formatCurrency(goal.monthlyContribution)}

+
+ {formatAmount(goal.monthlyContribution)} +

Time Remaining

@@ -406,15 +417,21 @@ const GoalPlanning: React.FC = () => {

Current

-

{formatCurrency(goal.currentAmount)}

+
+ {formatAmount(goal.currentAmount)} +

Monthly SIP

-

{formatCurrency(goal.monthlyContribution)}

+
+ {formatAmount(goal.monthlyContribution)} +

Remaining

-

{formatCurrency(goal.targetAmount - goal.currentAmount)}

+
+ {formatAmount(goal.targetAmount - goal.currentAmount)} +
@@ -511,30 +528,26 @@ const GoalPlanning: React.FC = () => {
{goal.milestones.map((milestone, index) => (
-
+
{milestone.completed ? ( - + ) : ( - + )} -
-

{formatCurrency(milestone.amount)}

-

- Target: {new Date(milestone.date).toLocaleDateString()} -

+
+
+ {formatAmount(milestone.amount)} +
+ + {milestone.completed ? 'Completed' : 'Pending'} +
-
- {milestone.completed ? ( - - Completed - - ) : ( - - Pending - - )} -
))}
@@ -545,7 +558,7 @@ const GoalPlanning: React.FC = () => { -
+
); }; diff --git a/src/components/features/PortfolioManager.tsx b/src/components/features/PortfolioManager.tsx index bde6322..866b94b 100644 --- a/src/components/features/PortfolioManager.tsx +++ b/src/components/features/PortfolioManager.tsx @@ -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: ( + + Sell {formatAmount(5000)} worth of real estate stocks + + ) }, { 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: ( + + Consider investing {formatAmount(10000)} + + ) }, { 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 ( -
-
-

- Portfolio Manager -

-

- AI-powered insights for your investments. -

+
+ + {/* Header Section */} +
+
+

+ Portfolio Manager +

+

Smart insights for your investments.

+
+
+ + +
- {/* Portfolio Overview */} -
- -
-

Total Value

-
- -
-
-

{formatCurrency(portfolioOverview.totalValue)}

-
- - +{portfolioOverview.gainPercentage}% -
-
+ {/* Summary Cards */} + - -
-

Total Gains

-
- -
-
-

{formatCurrency(portfolioOverview.totalGains)}

-

vs {formatCurrency(portfolioOverview.totalInvested)} invested

-
- - -
-

Day Change

-
- -
-
-

{formatCurrency(portfolioOverview.dayChange)}

-
- - +{portfolioOverview.dayChangePercent}% -
-
- - -
-

Risk Score

-
- -
-
-

7.2

-

Moderate

-
-
- - - - Holdings - Allocation - AI Insights + {/* Main Content Tabs */} + + + Overview + Holdings + Analytics - -
- {holdings.map((holding, index) => ( - -
-
-
- {holding.symbol.substring(0, 4)} -
- {holding.sector} -
-

{holding.name}

-

{holding.shares} shares @ {formatCurrency(holding.currentPrice)}

-
- -
-
-
-

Current Value

-

{formatCurrency(holding.currentValue)}

-
-
0 ? 'text-green-600' : 'text-red-600'}`}> -

- {holding.gain > 0 ? : } - {holding.gainPercent}% -

-

- {holding.gain > 0 ? '+' : ''}{formatCurrency(holding.gain)} -

-
-
-
-
- ))} + {/* Tab 1: Overview */} + +
+ +
- - -
- {sectorAllocation.map((sector, index) => ( -
-
-
-
- {sector.sector} -
-
- {sector.percentage}% -

{formatCurrency(sector.value)}

-
-
-
-
-
-
- ))} + {/* Tab 2: Holdings */} + + + + + {/* Tab 3: Analytics */} + +
+
+ +
+
+
- - - - -
- {recommendations.map((rec, index) => ( - -
-
-

{rec.title}

-

{rec.description}

-
- - {rec.priority.charAt(0).toUpperCase() + rec.priority.slice(1)} - -
-
-

{rec.action}

- -
-
- ))}
+
); diff --git a/src/components/features/Profile.tsx b/src/components/features/Profile.tsx index 5d71acb..cff4aeb 100644 --- a/src/components/features/Profile.tsx +++ b/src/components/features/Profile.tsx @@ -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 = () => {
+ {/* Appearance & Themes */} + +
+
+ +
+

Appearance

+
+ +
+ {['default', 'dubai', 'minimal', 'nature'].map((themeName) => ( + + ))} +
+
+ {/* Data Privacy (UAE PDPL) */}
diff --git a/src/components/features/SavingsBooster.tsx b/src/components/features/SavingsBooster.tsx index 27b8d53..b9949ac 100644 --- a/src/components/features/SavingsBooster.tsx +++ b/src/components/features/SavingsBooster.tsx @@ -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 ( - - - - - ) -} - -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 ( -
- {/* Background Arc */} -
- - {/* Value Arc (Simulated with rotation) */} -
- -
- {value} - Score -
-
- ); -}; +// 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 ( -
-
-

- Personal Savings Booster -

-

AI-driven optimization to maximize your wealth.

-
+
-
- {/* Hero Comparison */} -
-
- - Optimization Score - - - - - Monthly Potential -
- {formatCurrency(savingsData.potential)} - /mo -
-
- - + {formatCurrency(savingsData.potential - savingsData.current)} vs current -
-
- -
-
- - -

Quick Actions

- -
-
- - {/* Projection Chart */} - -

Yearly Projection: Current vs Optimized

-
- - - - - - - - - - - - - - - -
-
+ {/* Header Section */} +
+
+

+ + Savings Booster +

+

Accelerate your wealth with smart goals.

+
+
+ +
- - {/* AI Opportunities Sidebar */} - -
-

AI Opportunities

- 3 New -
- -
- {savingsData.opportunities.map((opp, index) => ( - -
-
- -
-
-

{opp.title}

- {opp.type} -
-
- -
- - {opp.impact} - - -
-
- ))} -
- -
-

Unlock Premium Insights to save an extra AED 450/mo

- -
-
+ + {/* Summary Cards */} + + + {/* Main Content Tabs */} + + + Overview + Goals + Accounts + Calculator + Add Goal + + + {/* Tab 1: Overview */} + +
+ + +
+
+ + {/* Tab 2: Goals */} + + + + + {/* Tab 3: Accounts */} + + + + + {/* Tab 4: Calculator */} + + + + + {/* Tab 5: Add Goal */} + + + + +
); } \ No newline at end of file diff --git a/src/components/portfolio/AssetAllocation.tsx b/src/components/portfolio/AssetAllocation.tsx new file mode 100644 index 0000000..3fa5d3f --- /dev/null +++ b/src/components/portfolio/AssetAllocation.tsx @@ -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 = ({ 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 ( + +

Asset Allocation

+
+ {sortedHoldings.map((stock, index) => { + const percentage = ((stock.currentValue / totalValue) * 100).toFixed(1); + return ( +
+
+
+ {stock.symbol} + {stock.name} +
+
+ {percentage}% +
+
+
+
+
+
+ ); + })} +
+
+ ); +}; + +export default AssetAllocation; diff --git a/src/components/portfolio/HoldingsTable.tsx b/src/components/portfolio/HoldingsTable.tsx new file mode 100644 index 0000000..44afcef --- /dev/null +++ b/src/components/portfolio/HoldingsTable.tsx @@ -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 = ({ holdings }) => { + return ( + + + + + Asset Name + Price + Shares + Value + Change + Actions + + + + {holdings.map((holding) => ( + + +
+ {holding.symbol} + {holding.name} +
+
+ +
+ {formatAmount(holding.currentPrice)} +
+
+ {holding.shares || holding.units} + +
+ {formatAmount(holding.currentValue)} +
+
+ +
= 0 ? 'text-green-600' : 'text-red-600'}`}> + {holding.gain >= 0 ? : } + {Math.abs(holding.gainPercent)}% +
+
+ +
+ + +
+
+
+ ))} +
+
+
+ ); +}; + +export default HoldingsTable; diff --git a/src/components/portfolio/PortfolioAIInsights.tsx b/src/components/portfolio/PortfolioAIInsights.tsx new file mode 100644 index 0000000..9353dbf --- /dev/null +++ b/src/components/portfolio/PortfolioAIInsights.tsx @@ -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 = ({ 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 ( +
+ {recommendations.map((rec, index) => ( + +
+
+

{rec.title}

+

{rec.description}

+
+ + {rec.priority.charAt(0).toUpperCase() + rec.priority.slice(1)} + +
+
+
+ {rec.action} +
+ +
+
+ ))} +
+ ); +}; + +export default PortfolioAIInsights; diff --git a/src/components/portfolio/PortfolioSummaryCards.tsx b/src/components/portfolio/PortfolioSummaryCards.tsx new file mode 100644 index 0000000..b4cf946 --- /dev/null +++ b/src/components/portfolio/PortfolioSummaryCards.tsx @@ -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 = ({ overview, holdingsCount }) => { + return ( +
+ {/* Total Value */} + +
+

Total Value

+
+ +
+
+
+ +

{formatAmount(overview.totalValue)}

+
+
+ + +{overview.gainPercentage}% +
+
+ + {/* Today's Change */} + +
+

Today's Change

+
+ +
+
+
+ +

{formatAmount(overview.dayChange)}

+
+
+ + +{overview.dayChangePercent}% +
+
+ + {/* Holdings Count */} + +
+

Holdings

+
+ +
+
+

{holdingsCount}

+

Active Positions

+
+ + {/* Performance / Total Gains */} + +
+

Total Gains

+
+ +
+
+
+ +

{formatAmount(overview.totalGains)}

+
+
+ vs {formatAmount(overview.totalInvested)} invested +
+
+
+ ); +}; + +export default PortfolioSummaryCards; diff --git a/src/components/portfolio/QuickInvestmentForm.tsx b/src/components/portfolio/QuickInvestmentForm.tsx new file mode 100644 index 0000000..fef3053 --- /dev/null +++ b/src/components/portfolio/QuickInvestmentForm.tsx @@ -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 = ({ holdings }) => { + return ( + +

Quick Investment

+
+
+ + +
+ +
+ +
+
+ +
+ +
+
+ +
+ + +
+
+
+ ); +}; + +export default QuickInvestmentForm; diff --git a/src/components/portfolio/RiskAnalysis.tsx b/src/components/portfolio/RiskAnalysis.tsx new file mode 100644 index 0000000..149d010 --- /dev/null +++ b/src/components/portfolio/RiskAnalysis.tsx @@ -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 ( + +
+
+ +

Risk Analysis

+
+

+ Your portfolio currently aligns with a balanced growth strategy. Consider increasing diversification in global markets to hedge against local volatility. +

+
+ +
+
+ Current Score + 7.2/10 +
+
+ + Moderate Risk + +
+
+
+ ); +}; + +export default RiskAnalysis; diff --git a/src/components/savings/AddGoalForm.tsx b/src/components/savings/AddGoalForm.tsx new file mode 100644 index 0000000..d7dbefa --- /dev/null +++ b/src/components/savings/AddGoalForm.tsx @@ -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(); + + return ( +
+ +

Create New Savings Goal

+ +
+
+ + +
+ +
+ +
+
+ +
+ +
+
+ +
+ + + + + + + {/* Placeholder for standard Calendar since exact import path might vary */} +
+

Select Date

+ setDate(new Date(e.target.value))} /> +
+
+
+
+ + +
+
+
+ ); +}; + +export default AddGoalForm; diff --git a/src/components/savings/CompoundInterestCalculator.tsx b/src/components/savings/CompoundInterestCalculator.tsx new file mode 100644 index 0000000..c7166b2 --- /dev/null +++ b/src/components/savings/CompoundInterestCalculator.tsx @@ -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(10000); + const [monthlyContribution, setMonthlyContribution] = useState(2000); + const [years, setYears] = useState(10); + const [rate, setRate] = useState(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 ( +
+ {/* Input Form */} + +

+ Parameters +

+
+
+ + setInitialAmount(Number(e.target.value))} className="bg-white/50" /> +
+
+ + setMonthlyContribution(Number(e.target.value))} className="bg-white/50" /> +
+
+
+ + {years} Years +
+ setYears(v[0])} max={50} step={1} className="py-2" /> +
+
+
+ + {rate}% +
+ setRate(v[0])} max={15} step={0.1} className="py-2" /> +
+
+
+ + {/* Results */} + +

Projected Savings

+ +
+

In {years} years, you will have

+
+ + {formatAmount(Math.round(finalAmount))} +
+
+ +
+
+

Total Contributions

+

+ {formatAmount(Math.round(totalInvested))} +

+
+
+

Total Interest Earned

+

+ {formatAmount(Math.round(totalInterest))} +

+
+
+
+
+ ); +}; + +export default CompoundInterestCalculator; diff --git a/src/components/savings/SavingsAccountsList.tsx b/src/components/savings/SavingsAccountsList.tsx new file mode 100644 index 0000000..b26c5da --- /dev/null +++ b/src/components/savings/SavingsAccountsList.tsx @@ -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 ( + +

Savings Accounts

+
+ {accounts.map((acc, index) => ( +
+
+
+ +
+
+

{acc.name}

+
+ {acc.type} + + {acc.apy}% APY +
+
+
+
+
+ {formatAmount(acc.balance)} +
+
+ + {acc.growth}/mo +
+
+
+ ))} +
+
+ ); +}; + +export default SavingsAccountsList; diff --git a/src/components/savings/SavingsAccountsTable.tsx b/src/components/savings/SavingsAccountsTable.tsx new file mode 100644 index 0000000..c699765 --- /dev/null +++ b/src/components/savings/SavingsAccountsTable.tsx @@ -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 ( + + + + + Account Name + Type + Balance (AED) + Interest Rate + Monthly Growth + Actions + + + + {accounts.map((acc, idx) => ( + + {acc.name} + + + {acc.type} + + + + + {formatAmount(acc.balance)} + + + {acc.rate}% + + + + {acc.growth} + + + + + + + ))} + +
+
+ ); +}; + +export default SavingsAccountsTable; diff --git a/src/components/savings/SavingsGoalsGrid.tsx b/src/components/savings/SavingsGoalsGrid.tsx new file mode 100644 index 0000000..26a658d --- /dev/null +++ b/src/components/savings/SavingsGoalsGrid.tsx @@ -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 ( +
+ {goals.map((goal, index) => { + const percentage = Math.round((goal.saved / goal.target) * 100); + + return ( + +
+
+
+

{goal.name}

+
+ + {goal.daysLeft} days remaining +
+
+
+ {percentage}% +
+
+ +
+ +
+ {formatAmount(goal.saved)} + {formatAmount(goal.target)} +
+
+
+ +
+ + + +
+
+ ); + })} +
+ ); +}; + +export default SavingsGoalsGrid; diff --git a/src/components/savings/SavingsGoalsList.tsx b/src/components/savings/SavingsGoalsList.tsx new file mode 100644 index 0000000..15dbf49 --- /dev/null +++ b/src/components/savings/SavingsGoalsList.tsx @@ -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 ( + +

Savings Goals

+
+ {goals.map((goal, index) => { + const percentage = Math.min((goal.saved / goal.target) * 100, 100); + return ( +
+
+ {goal.name} + + {goal.status} + +
+ +
+
+ {formatAmount(goal.saved)} +
+
+ of {formatAmount(goal.target)} +
+
+ +
+ +
+
+ ); + })} +
+
+ ); +}; + +export default SavingsGoalsList; diff --git a/src/components/savings/SavingsSummaryCards.tsx b/src/components/savings/SavingsSummaryCards.tsx new file mode 100644 index 0000000..dd8c519 --- /dev/null +++ b/src/components/savings/SavingsSummaryCards.tsx @@ -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 ( +
+ {/* Total Savings */} + +
+

Total Savings

+
+ +
+
+
+ +

{formatAmount(data.totalSavings)}

+
+

Across all accounts

+
+ + {/* Monthly Growth */} + +
+

Monthly Growth

+
+ +
+
+
+ +

{formatAmount(data.monthlyGrowth)}

+
+
+ + +1.8% vs last month +
+
+ + {/* Goals Progress */} + +
+

Goals Progress

+
+ +
+
+

{data.goalsProgress}%

+
+
+
+
+ + {/* Annual Yield */} + +
+

Annual Yield (APY)

+
+ +
+
+

{data.annualYield}%

+

Average across liquid assets

+
+
+ ); +}; + +export default SavingsSummaryCards; diff --git a/src/components/ui/GlassCard.tsx b/src/components/ui/GlassCard.tsx index be10d34..3a228b1 100644 --- a/src/components/ui/GlassCard.tsx +++ b/src/components/ui/GlassCard.tsx @@ -1,5 +1,6 @@ import { cn } from "@/lib/utils"; import React from "react"; +import { useTheme } from "@/context/ThemeContext"; interface GlassCardProps extends React.HTMLAttributes { 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 (
{ + className?: string; +} + +export const DirhamIcon = ({ className, ...props }: IconProps) => ( + + + +); diff --git a/src/context/ThemeContext.tsx b/src/context/ThemeContext.tsx new file mode 100644 index 0000000..58da95e --- /dev/null +++ b/src/context/ThemeContext.tsx @@ -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(undefined); + +export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { + const [theme, setTheme] = useState(() => { + 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 ( + + {/* Background Layer managed here for global coverage */} + {theme !== 'default' && ( +
+ )} + {/* Overlay for contrast if needed */} + {theme === 'dubai' &&
} + + {children} + + ); +}; + +export const useTheme = () => { + const context = useContext(ThemeContext); + if (context === undefined) { + throw new Error('useTheme must be used within a ThemeProvider'); + } + return context; +}; diff --git a/src/lib/utils.ts b/src/lib/utils.ts index c0ef83f..7d6ca63 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -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);