diff --git a/src/components/dashboard/UnsealDashboard.tsx b/src/components/dashboard/UnsealDashboard.tsx index 3427f00..a9f33d6 100644 --- a/src/components/dashboard/UnsealDashboard.tsx +++ b/src/components/dashboard/UnsealDashboard.tsx @@ -1,231 +1,375 @@ -'use client'; +"use client"; -import React, { useState } from 'react'; -import { User } from 'firebase/auth'; -import { Transaction } from '@/types/schema'; -import { format } from 'date-fns'; -import Link from 'next/link'; -import UnsealDock from './UnsealDock'; -import UserAvatar from '@/components/ui/UserAvatar'; +import { format } from "date-fns"; +import type { User } from "firebase/auth"; +import UserAvatar from "@/components/ui/UserAvatar"; +import { DEFAULT_MONTHLY_BURN } from "@/lib/constants"; +import type { Transaction } from "@/types/schema"; +import UnsealDock from "./UnsealDock"; interface UnsealDashboardProps { - user: User | null; - balance: number; - transactions: Transaction[]; - stats: { - income: number; - expense: number; - }; + user: User | null; + balance: number; + transactions: Transaction[]; + stats: { + income: number; + expense: number; + }; } -export default function UnsealDashboard({ user, balance, transactions, stats }: UnsealDashboardProps) { - const firstName = user?.displayName?.split(' ')[0] || 'User'; - const recentTx = transactions.slice(0, 5); - // Calculate Runway: Balance / fixed 15k for now (as per HTML reference) - // In a real app, this would be: balance / average_monthly_expense - const monthlyBurn = 15000; - const runwayMonths = (balance / 100 / monthlyBurn).toFixed(1); +export default function UnsealDashboard({ + user, + balance, + transactions, +}: UnsealDashboardProps) { + const firstName = user?.displayName?.split(" ")[0] || "User"; + const recentTx = transactions.slice(0, 5); + // Calculate Runway: Balance / fixed 15k for now (as per HTML reference) + // In a real app, this would be: balance / average_monthly_expense + const monthlyBurn = DEFAULT_MONTHLY_BURN; + const runwayMonths = (balance / 100 / monthlyBurn).toFixed(1); - return ( -
+ return ( +
+ {/* Header */} +
+
+
+ + lock_open + +
+

+ UNSEAL +

+
+
+
+
+ + +
+ + Secure + + + shield + +
+
+ +
+
+
+ {/* Main Content Layout */} +
+
+

+ Good evening, {firstName}.
+ + Your financial fortress is active. + +

+
- {/* Header */} -
-
-
- lock_open -
-

UNSEAL

-
-
-
-
- - -
- Secure - shield -
-
- -
-
-
- - {/* Main Content Layout */} -
-
-

- Good evening, {firstName}.
- Your financial fortress is active. -

-
- -
- {/* Financial Runway */} -
-
-

Financial Runway

-
- {/* Gauge */} -
- - - {/* +
+ {/* Financial Runway */} +
+
+

+ Financial Runway +

+
+ {/* Gauge */} +
+ + Financial Runway Gauge + + {/* Circumference = 2 * pi * 45 ≈ 283 Offset = 283 - (283 * percentage) Let's map calculated months to max 12 months for circle? If 3.5 months, say 30%? */} - - - - - - - - -
- {runwayMonths} - Months -
-
-
-

Based on ₹15k fixed monthly costs

-
-
- - {/* CFO Intelligence */} -
- {/* Shimmer Border/Effect */} -
-
-
-
-
+ + + + + + + + +
+ + {runwayMonths} + + + Months + +
+
+
+

+ Based on ₹15k fixed monthly costs +

+
+
-
-
-
- psychology - CFO Intelligence -
-

Ask your personal CFO

-

Get instant insights on your spending, investments, and wealth leaks.

-
- {/* Input */} -
-
-
- colors_spark - - -
-
-
-
+ {/* CFO Intelligence */} +
+ {/* Shimmer Border/Effect */} +
+
+
+
+
- {/* Net Wealth Stream */} -
-
-
-

Net Wealth Stream

-
-

- ₹{(balance / 100).toLocaleString('en-IN')} -

-
- trending_up - +8.4% -
-
-
-
- - - -
-
- {/* Chart */} -
- - - - - - - - - - - - - - - - -
-
- Jan - Feb - Mar - Apr - May - Jun -
-
+
+
+
+ + psychology + + CFO Intelligence +
+

+ Ask your personal CFO +

+

+ Get instant insights on your spending, investments, and wealth + leaks. +

+
+ {/* Input */} +
+
+
+ + colors_spark + + + +
+
+
+
- {/* Recent Activity (Added to keep functionality matching new aesthetic) */} -
-

Recent Activity

-
- {recentTx.length === 0 ? ( -

No recent activity.

- ) : ( - recentTx.map((tx) => ( -
-
-
- {tx.type === 'income' ? ( - arrow_downward - ) : ( - swap_horiz - )} -
-
-

{tx.description}

-

{format(new Date(tx.date), 'MMM d, h:mm a')}

-
-
- - {tx.type === 'income' ? '+' : '-'}₹{(tx.amount / 100).toLocaleString('en-IN')} - -
- )) - )} -
-
-
+ {/* Net Wealth Stream */} +
+
+
+

+ Net Wealth Stream +

+
+

+ ₹{(balance / 100).toLocaleString("en-IN")} +

+
+ + trending_up + + + +8.4% + +
+
+
+
+ + + +
+
+ {/* Chart */} +
+ + Net Wealth Chart + + + + + + + + + + + + + + + +
+
+ Jan + Feb + Mar + Apr + May + Jun +
+
-
+ {/* Recent Activity (Added to keep functionality matching new aesthetic) */} +
+

+ Recent Activity +

+
+ {recentTx.length === 0 ? ( +

No recent activity.

+ ) : ( + recentTx.map((tx) => ( +
+
+
+ {tx.type === "income" ? ( + + arrow_downward + + ) : ( + + swap_horiz + + )} +
+
+

+ {tx.description} +

+

+ {format(new Date(tx.date), "MMM d, h:mm a")} +

+
+
+ + {tx.type === "income" ? "+" : "-"}₹ + {(tx.amount / 100).toLocaleString("en-IN")} + +
+ )) + )} +
+
+
+ - {/* Floating Dock Navigation */} - -
- ); + {/* Floating Dock Navigation */} + + + ); } diff --git a/src/lib/constants.ts b/src/lib/constants.ts new file mode 100644 index 0000000..dd302f3 --- /dev/null +++ b/src/lib/constants.ts @@ -0,0 +1,4 @@ +// Constants for financial calculations and defaults + +// Default monthly burn rate in INR (Rupees), used for runway calculation when actual data is unavailable. +export const DEFAULT_MONTHLY_BURN = 15000; diff --git a/src/lib/parsers/sbi.ts b/src/lib/parsers/sbi.ts index b6c73c3..7148b38 100644 --- a/src/lib/parsers/sbi.ts +++ b/src/lib/parsers/sbi.ts @@ -1,3 +1,4 @@ +import { parse } from 'date-fns'; import { type Transaction, toPaise } from '../../types/schema.ts'; // We omit 'id' and 'userId' because the parser doesn't know about them. @@ -137,12 +138,18 @@ function parseDate(dateStr: string): Date | null { const normalized = dateStr.replace(/\//g, '-'); const parts = normalized.split('-'); - if (parts.length === 3) { - const [day, month] = parts; - let year = parts[2]; + if (parts.length !== 3) return null; - const date = parse(normalized, formatString, new Date()); - return isNaN(date.getTime()) ? null : date; + const yearPart = parts[2]; + + let date: Date; + + if (yearPart.length === 4) { + date = parse(normalized, 'd-M-yyyy', new Date()); + } else { + // Assume 2 digits + date = parse(normalized, 'd-M-yy', new Date()); } - return null; + + return isNaN(date.getTime()) ? null : date; }