Update frontend-web dependencies and implement routing structure

- Added new dependencies: @fontsource-variable/geist, @radix-ui/react-separator, @radix-ui/react-tooltip, date-fns, react-router-dom, and recharts.
- Updated index.html to set the HTML class to "dark" for dark mode support.
- Refactored App component to implement routing with React Router, replacing the previous UI structure with a layout and multiple pages (NetWorth, Debts, Invoices, Clients).
- Enhanced CSS for dark mode and added depth utilities for improved UI aesthetics.
- Expanded Redux store to include net worth, debts, and invoices slices for comprehensive state management.
This commit is contained in:
2025-12-07 10:49:43 -05:00
parent f1f0032bca
commit bf00261e1d
19 changed files with 1283 additions and 55 deletions

View File

@@ -0,0 +1,117 @@
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import {Button} from '@/components/ui/button';
import {useAppSelector} from '@/store';
export default function ClientsPage() {
const {clients, invoices} = useAppSelector(state => state.invoices);
const getClientStats = (clientId: string) => {
const clientInvoices = invoices.filter(i => i.clientId === clientId);
const totalBilled = clientInvoices.reduce((sum, i) => sum + i.total, 0);
const outstanding = clientInvoices
.filter(i => i.status === 'sent' || i.status === 'overdue')
.reduce((sum, i) => sum + i.total, 0);
return {totalBilled, outstanding, invoiceCount: clientInvoices.length};
};
const formatCurrency = (value: number) =>
new Intl.NumberFormat('en-US', {style: 'currency', currency: 'USD'}).format(value);
return (
<div className="p-8">
<div className="mb-8 flex items-center justify-between">
<div>
<h1 className="text-2xl font-medium tracking-tight">Clients</h1>
<p className="text-muted-foreground">Manage your customers and clients</p>
</div>
<Button>Add Client</Button>
</div>
{/* Summary */}
<div className="mb-8 grid gap-4 md:grid-cols-2">
<Card className="card-elevated">
<CardHeader className="pb-2">
<CardDescription>Total Clients</CardDescription>
</CardHeader>
<CardContent>
<p className="text-2xl font-semibold">{clients.length}</p>
</CardContent>
</Card>
<Card className="card-elevated">
<CardHeader className="pb-2">
<CardDescription>Active This Month</CardDescription>
</CardHeader>
<CardContent>
<p className="text-2xl font-semibold">
{
clients.filter(c => {
const stats = getClientStats(c.id);
return stats.invoiceCount > 0;
}).length
}
</p>
</CardContent>
</Card>
</div>
{/* Clients List */}
<Card className="card-elevated">
<CardHeader>
<CardTitle className="text-lg font-medium">All Clients</CardTitle>
<CardDescription>View and manage client information</CardDescription>
</CardHeader>
<CardContent>
{clients.length === 0 ? (
<div className="py-8 text-center">
<p className="text-muted-foreground">No clients added yet</p>
<p className="mt-1 text-sm text-muted-foreground">
Add your first client to start creating invoices
</p>
</div>
) : (
<div className="space-y-2">
{clients.map(client => {
const stats = getClientStats(client.id);
return (
<div
key={client.id}
className="flex items-center justify-between rounded-lg border border-border bg-secondary/30 p-4"
>
<div>
<p className="font-medium">{client.name}</p>
<p className="text-sm text-muted-foreground">
{client.email}
{client.company && ` · ${client.company}`}
</p>
</div>
<div className="flex items-center gap-8">
<div className="text-right">
<p className="text-sm text-muted-foreground">Total Billed</p>
<p className="font-medium">{formatCurrency(stats.totalBilled)}</p>
</div>
<div className="text-right">
<p className="text-sm text-muted-foreground">Outstanding</p>
<p className="font-medium">{formatCurrency(stats.outstanding)}</p>
</div>
<div className="text-right">
<p className="text-sm text-muted-foreground">Invoices</p>
<p className="font-medium">{stats.invoiceCount}</p>
</div>
</div>
</div>
);
})}
</div>
)}
</CardContent>
</Card>
</div>
);
}