Enhance frontend-web with new features and dependencies
- Added new font support for 'Oxanium' and integrated Radix UI components including Dialog and Select. - Updated CSS to set the default font to 'Oxanium Variable' and adjusted HTML font size. - Introduced AddAccountDialog component for managing debt accounts, enhancing user experience. - Refactored DebtsPage to utilize the new AddAccountDialog and improved account management features. - Updated Redux store to support debt categories and accounts, including actions for adding, updating, and removing accounts. - Mock data added for clients and invoices to facilitate development and testing.
This commit is contained in:
@@ -1,10 +1,4 @@
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card';
|
||||
import {Card, CardContent, CardHeader, CardTitle} from '@/components/ui/card';
|
||||
import {Button} from '@/components/ui/button';
|
||||
import {useAppSelector} from '@/store';
|
||||
|
||||
@@ -14,47 +8,38 @@ export default function ClientsPage() {
|
||||
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 outstanding = clientInvoices.filter(i => i.status === 'sent' || i.status === 'overdue').reduce((sum, i) => sum + i.total, 0);
|
||||
return {totalBilled, outstanding, count: clientInvoices.length};
|
||||
};
|
||||
|
||||
const formatCurrency = (value: number) =>
|
||||
new Intl.NumberFormat('en-US', {style: 'currency', currency: 'USD'}).format(value);
|
||||
const fmt = (value: number) =>
|
||||
new Intl.NumberFormat('en-US', {style: 'currency', currency: 'USD', maximumFractionDigits: 0}).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 className="p-6">
|
||||
{/* Header */}
|
||||
<div className="mb-5 flex items-center justify-between">
|
||||
<h1 className="text-xl font-medium">Clients</h1>
|
||||
<Button size="sm">Add Client</Button>
|
||||
</div>
|
||||
|
||||
{/* Summary */}
|
||||
<div className="mb-8 grid gap-4 md:grid-cols-2">
|
||||
{/* Summary Cards */}
|
||||
<div className="mb-5 grid grid-cols-2 gap-4">
|
||||
<Card className="card-elevated">
|
||||
<CardHeader className="pb-2">
|
||||
<CardDescription>Total Clients</CardDescription>
|
||||
<CardHeader className="pb-1 pt-3 px-4">
|
||||
<p className="text-xs text-muted-foreground">Total Clients</p>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-2xl font-semibold">{clients.length}</p>
|
||||
<CardContent className="pb-3 px-4">
|
||||
<p className="text-xl font-semibold">{clients.length}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="card-elevated">
|
||||
<CardHeader className="pb-2">
|
||||
<CardDescription>Active This Month</CardDescription>
|
||||
<CardHeader className="pb-1 pt-3 px-4">
|
||||
<p className="text-xs text-muted-foreground">With Active Invoices</p>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-2xl font-semibold">
|
||||
{
|
||||
clients.filter(c => {
|
||||
const stats = getClientStats(c.id);
|
||||
return stats.invoiceCount > 0;
|
||||
}).length
|
||||
}
|
||||
<CardContent className="pb-3 px-4">
|
||||
<p className="text-xl font-semibold">
|
||||
{clients.filter(c => getClientStats(c.id).count > 0).length}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -62,46 +47,39 @@ export default function ClientsPage() {
|
||||
|
||||
{/* Clients List */}
|
||||
<Card className="card-elevated">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-lg font-medium">All Clients</CardTitle>
|
||||
<CardDescription>View and manage client information</CardDescription>
|
||||
<CardHeader className="pb-2 pt-3 px-4">
|
||||
<CardTitle className="text-sm font-medium">All Clients</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<CardContent className="px-4 pb-3">
|
||||
{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>
|
||||
<p className="text-muted-foreground">No clients yet</p>
|
||||
<Button size="sm" className="mt-3">Add Client</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
<div className="divide-y divide-border rounded-md border border-border">
|
||||
{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 key={client.id} className="flex items-center justify-between px-3 py-2.5">
|
||||
<div>
|
||||
<p className="font-medium">{client.name}</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{client.email}
|
||||
{client.company && ` · ${client.company}`}
|
||||
<p className="text-sm font-medium">{client.name}</p>
|
||||
<p className="text-xs 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 className="flex items-center gap-6 text-right">
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground">Billed</p>
|
||||
<p className="text-sm font-medium">{fmt(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>
|
||||
<p className="text-xs text-muted-foreground">Outstanding</p>
|
||||
<p className="text-sm font-medium">{fmt(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>
|
||||
<p className="text-xs text-muted-foreground">Invoices</p>
|
||||
<p className="text-sm font-medium">{stats.count}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -114,4 +92,3 @@ export default function ClientsPage() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user