Files
CMT/frontend/app/page.tsx
T
2025-08-16 14:41:12 +02:00

562 lines
22 KiB
TypeScript

"use client"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import {
DollarSign,
Package,
Users,
AlertTriangle,
ShoppingCart,
CreditCard,
BarChart3,
Plus,
Building2,
} from "lucide-react"
import { useBusinessData } from "@/hooks/use-business-data"
import { useProducts } from "@/hooks/use-products"
import { useClients } from "@/hooks/use-clients"
import { useSuppliers } from "@/hooks/use-suppliers"
import { ProductForm } from "@/components/products/product-form"
import { ProductsTable } from "@/components/products/products-table"
import { ClientForm } from "@/components/clients/client-form"
import { ClientsTable } from "@/components/clients/clients-table"
import { PaymentForm } from "@/components/clients/payment-form"
import { SupplierForm } from "@/components/suppliers/supplier-form"
import { SuppliersTable } from "@/components/suppliers/suppliers-table"
import { PurchaseForm } from "@/components/suppliers/purchase-form"
import { SupplierPaymentForm } from "@/components/suppliers/supplier-payment-form"
import { CreditOverview } from "@/components/credit/credit-overview"
import { TransactionHistory } from "@/components/credit/transaction-history"
import { useState } from "react"
import type { Product, Client, Supplier } from "@/types/business"
export default function Dashboard() {
const { kpis, recentTransactions, topProducts, clientsWithCredit, suppliersWithCredit, loading } = useBusinessData()
const {
products,
loading: productsLoading,
addProduct,
updateProduct,
deleteProduct,
getLowStockProducts,
getOutOfStockProducts,
} = useProducts()
const {
clients,
loading: clientsLoading,
addClient,
updateClient,
deleteClient,
addPayment,
getClientById,
getClientsWithOutstandingCredit,
getClientsOverCreditLimit,
getTotalOutstandingAmount,
} = useClients()
const {
suppliers,
loading: suppliersLoading,
addSupplier,
updateSupplier,
deleteSupplier,
addPurchase,
recordPayment,
getSupplierById,
getSuppliersWithOutstandingBalance,
getTotalAmountOwed,
} = useSuppliers()
const [showProductForm, setShowProductForm] = useState(false)
const [editingProduct, setEditingProduct] = useState<Product | undefined>()
const [showClientForm, setShowClientForm] = useState(false)
const [editingClient, setEditingClient] = useState<Client | undefined>()
const [showPaymentForm, setShowPaymentForm] = useState(false)
const [paymentClientId, setPaymentClientId] = useState<string>("")
const [showSupplierForm, setShowSupplierForm] = useState(false)
const [editingSupplier, setEditingSupplier] = useState<Supplier | undefined>()
const [showPurchaseForm, setShowPurchaseForm] = useState(false)
const [purchaseSupplierId, setPurchaseSupplierId] = useState<string>("")
const [showSupplierPaymentForm, setShowSupplierPaymentForm] = useState(false)
const [supplierPaymentId, setSupplierPaymentId] = useState<string>("")
const handleEditProduct = (product: Product) => {
setEditingProduct(product)
setShowProductForm(true)
}
const handleProductSubmit = (productData: Omit<Product, "id">) => {
if (editingProduct) {
updateProduct(editingProduct.id, productData)
setEditingProduct(undefined)
} else {
addProduct(productData)
}
}
const handleCloseProductForm = () => {
setShowProductForm(false)
setEditingProduct(undefined)
}
const handleEditClient = (client: Client) => {
setEditingClient(client)
setShowClientForm(true)
}
const handleClientSubmit = (clientData: Omit<Client, "id">) => {
if (editingClient) {
updateClient(editingClient.id, clientData)
setEditingClient(undefined)
} else {
addClient(clientData)
}
}
const handleCloseClientForm = () => {
setShowClientForm(false)
setEditingClient(undefined)
}
const handleAddPayment = (clientId: string) => {
setPaymentClientId(clientId)
setShowPaymentForm(true)
}
const handlePaymentSubmit = (payment: { amount: number; notes: string; date: string }) => {
addPayment(paymentClientId, payment.amount, payment.notes, payment.date)
setShowPaymentForm(false)
setPaymentClientId("")
}
const handleEditSupplier = (supplier: Supplier) => {
setEditingSupplier(supplier)
setShowSupplierForm(true)
}
const handleSupplierSubmit = (supplierData: Omit<Supplier, "id">) => {
if (editingSupplier) {
updateSupplier(editingSupplier.id, supplierData)
setEditingSupplier(undefined)
} else {
addSupplier(supplierData)
}
}
const handleCloseSupplierForm = () => {
setShowSupplierForm(false)
setEditingSupplier(undefined)
}
const handleAddPurchase = (supplierId: string) => {
setPurchaseSupplierId(supplierId)
setShowPurchaseForm(true)
}
const handlePurchaseSubmit = (purchase: {
amount: number
description: string
date: string
invoiceNumber: string
}) => {
addPurchase(purchaseSupplierId, purchase.amount, purchase.description, purchase.date, purchase.invoiceNumber)
setShowPurchaseForm(false)
setPurchaseSupplierId("")
}
const handleRecordPayment = (supplierId: string) => {
setSupplierPaymentId(supplierId)
setShowSupplierPaymentForm(true)
}
const handleSupplierPaymentSubmit = (payment: {
amount: number
notes: string
date: string
paymentMethod: string
}) => {
recordPayment(supplierPaymentId, payment.amount, payment.notes, payment.date, payment.paymentMethod)
setShowSupplierPaymentForm(false)
setSupplierPaymentId("")
}
if (loading || productsLoading || clientsLoading || suppliersLoading) {
return (
<div className="min-h-screen bg-background flex items-center justify-center">
<div className="text-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto mb-4"></div>
<p className="text-muted-foreground">Loading dashboard...</p>
</div>
</div>
)
}
const lowStockProducts = getLowStockProducts()
const outOfStockProducts = getOutOfStockProducts()
const clientsWithOutstanding = getClientsWithOutstandingCredit()
const clientsOverLimit = getClientsOverCreditLimit()
const totalOutstanding = getTotalOutstandingAmount()
const suppliersWithBalance = getSuppliersWithOutstandingBalance()
const totalOwed = getTotalAmountOwed()
const paymentClient = getClientById(paymentClientId)
const purchaseSupplier = getSupplierById(purchaseSupplierId)
const paymentSupplier = getSupplierById(supplierPaymentId)
return (
<div className="min-h-screen bg-background">
{/* Header */}
<header className="border-b bg-card">
<div className="container mx-auto px-4 py-4">
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold text-foreground">Wholesale Manager</h1>
<p className="text-muted-foreground">Business Dashboard</p>
</div>
<Button variant="outline" size="sm">
<BarChart3 className="h-4 w-4 mr-2" />
Export Report
</Button>
</div>
</div>
</header>
<main className="container mx-auto px-4 py-6 space-y-6">
{/* KPI Cards */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Revenue</CardTitle>
<DollarSign className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">${kpis.totalRevenue.toLocaleString()}</div>
<p className="text-xs text-muted-foreground">
<span className="text-green-600">+{kpis.revenueGrowth}%</span> from last month
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Active Products</CardTitle>
<Package className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{products.length}</div>
<p className="text-xs text-muted-foreground">
{lowStockProducts.length} low stock, {outOfStockProducts.length} out of stock
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Active Clients</CardTitle>
<Users className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{clients.length}</div>
<p className="text-xs text-muted-foreground">${totalOutstanding.toLocaleString()} outstanding</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Suppliers</CardTitle>
<Building2 className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{suppliers.length}</div>
<p className="text-xs text-muted-foreground">${totalOwed.toLocaleString()} owed</p>
</CardContent>
</Card>
</div>
{/* Main Content Tabs */}
<Tabs defaultValue="overview" className="space-y-4">
<TabsList className="grid w-full grid-cols-5">
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="products">Products</TabsTrigger>
<TabsTrigger value="clients">Clients</TabsTrigger>
<TabsTrigger value="suppliers">Suppliers</TabsTrigger>
<TabsTrigger value="credit">Credit Tracking</TabsTrigger>
</TabsList>
<TabsContent value="overview" className="space-y-4">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Recent Transactions */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<ShoppingCart className="h-5 w-5" />
Recent Transactions
</CardTitle>
<CardDescription>Latest business activities</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-3">
{recentTransactions.slice(0, 5).map((transaction) => (
<div key={transaction.id} className="flex items-center justify-between p-3 border rounded-lg">
<div className="flex-1">
<p className="font-medium text-sm">{transaction.client}</p>
<p className="text-xs text-muted-foreground">{transaction.product}</p>
</div>
<div className="text-right">
<p className="font-medium text-sm">${transaction.amount}</p>
<Badge variant={transaction.status === "paid" ? "default" : "secondary"} className="text-xs">
{transaction.status}
</Badge>
</div>
</div>
))}
</div>
</CardContent>
</Card>
{/* Top Products */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Package className="h-5 w-5" />
Top Selling Products
</CardTitle>
<CardDescription>Best performers this month</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-3">
{topProducts.map((product, index) => (
<div key={product.id} className="flex items-center justify-between p-3 border rounded-lg">
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-primary/10 rounded-full flex items-center justify-center">
<span className="text-sm font-bold text-primary">#{index + 1}</span>
</div>
<div>
<p className="font-medium text-sm">{product.name}</p>
<p className="text-xs text-muted-foreground">{product.category}</p>
</div>
</div>
<div className="text-right">
<p className="font-medium text-sm">{product.unitsSold} sold</p>
<p className="text-xs text-muted-foreground">${product.revenue}</p>
</div>
</div>
))}
</div>
</CardContent>
</Card>
</div>
</TabsContent>
<TabsContent value="products" className="space-y-4">
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle className="flex items-center gap-2">
<Package className="h-5 w-5" />
Products Management
</CardTitle>
<CardDescription>Manage your product inventory and pricing</CardDescription>
</div>
<Button onClick={() => setShowProductForm(true)}>
<Plus className="h-4 w-4 mr-2" />
Add Product
</Button>
</div>
</CardHeader>
<CardContent>
{/* Stock Alerts */}
{(lowStockProducts.length > 0 || outOfStockProducts.length > 0) && (
<div className="mb-6 p-4 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg">
<div className="flex items-center gap-2 mb-2">
<AlertTriangle className="h-4 w-4 text-yellow-600" />
<h4 className="font-medium text-yellow-800 dark:text-yellow-200">Stock Alerts</h4>
</div>
{outOfStockProducts.length > 0 && (
<p className="text-sm text-yellow-700 dark:text-yellow-300 mb-1">
<strong>{outOfStockProducts.length}</strong> products are out of stock
</p>
)}
{lowStockProducts.length > 0 && (
<p className="text-sm text-yellow-700 dark:text-yellow-300">
<strong>{lowStockProducts.length}</strong> products are running low
</p>
)}
</div>
)}
<ProductsTable products={products} onEdit={handleEditProduct} onDelete={deleteProduct} />
</CardContent>
</Card>
</TabsContent>
<TabsContent value="clients" className="space-y-4">
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle className="flex items-center gap-2">
<Users className="h-5 w-5" />
Clients Management
</CardTitle>
<CardDescription>Manage your clients and track credit status</CardDescription>
</div>
<Button onClick={() => setShowClientForm(true)}>
<Plus className="h-4 w-4 mr-2" />
Add Client
</Button>
</div>
</CardHeader>
<CardContent>
{/* Credit Alerts */}
{(clientsWithOutstanding.length > 0 || clientsOverLimit.length > 0) && (
<div className="mb-6 p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg">
<div className="flex items-center gap-2 mb-2">
<CreditCard className="h-4 w-4 text-red-600" />
<h4 className="font-medium text-red-800 dark:text-red-200">Credit Alerts</h4>
</div>
{clientsOverLimit.length > 0 && (
<p className="text-sm text-red-700 dark:text-red-300 mb-1">
<strong>{clientsOverLimit.length}</strong> clients are over their credit limit
</p>
)}
<p className="text-sm text-red-700 dark:text-red-300">
Total outstanding: <strong>${totalOutstanding.toLocaleString()}</strong>
</p>
</div>
)}
<ClientsTable
clients={clients}
onEdit={handleEditClient}
onDelete={deleteClient}
onAddPayment={handleAddPayment}
/>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="suppliers" className="space-y-4">
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle className="flex items-center gap-2">
<Building2 className="h-5 w-5" />
Suppliers Management
</CardTitle>
<CardDescription>Manage your suppliers and track payment obligations</CardDescription>
</div>
<Button onClick={() => setShowSupplierForm(true)}>
<Plus className="h-4 w-4 mr-2" />
Add Supplier
</Button>
</div>
</CardHeader>
<CardContent>
{/* Payment Alerts */}
{suppliersWithBalance.length > 0 && (
<div className="mb-6 p-4 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg">
<div className="flex items-center gap-2 mb-2">
<AlertTriangle className="h-4 w-4 text-blue-600" />
<h4 className="font-medium text-blue-800 dark:text-blue-200">Payment Obligations</h4>
</div>
<p className="text-sm text-blue-700 dark:text-blue-300 mb-1">
<strong>{suppliersWithBalance.length}</strong> suppliers have outstanding balances
</p>
<p className="text-sm text-blue-700 dark:text-blue-300">
Total amount owed: <strong>${totalOwed.toLocaleString()}</strong>
</p>
</div>
)}
<SuppliersTable
suppliers={suppliers}
onEdit={handleEditSupplier}
onDelete={deleteSupplier}
onAddPurchase={handleAddPurchase}
onRecordPayment={handleRecordPayment}
/>
</CardContent>
</Card>
</TabsContent>
{/* Added new credit tracking tab */}
<TabsContent value="credit" className="space-y-4">
<CreditOverview clients={clients} suppliers={suppliers} />
<TransactionHistory />
</TabsContent>
</Tabs>
</main>
{/* Product Form Dialog */}
<ProductForm
product={editingProduct}
open={showProductForm}
onOpenChange={handleCloseProductForm}
onSubmit={handleProductSubmit}
/>
{/* Client Form Dialog */}
<ClientForm
client={editingClient}
open={showClientForm}
onOpenChange={handleCloseClientForm}
onSubmit={handleClientSubmit}
/>
{/* Payment Form Dialog */}
{paymentClient && (
<PaymentForm
clientName={paymentClient.name}
outstandingAmount={paymentClient.outstandingAmount}
open={showPaymentForm}
onOpenChange={(open) => {
setShowPaymentForm(open)
if (!open) setPaymentClientId("")
}}
onSubmit={handlePaymentSubmit}
/>
)}
{/* Supplier Form Dialog */}
<SupplierForm
supplier={editingSupplier}
open={showSupplierForm}
onOpenChange={handleCloseSupplierForm}
onSubmit={handleSupplierSubmit}
/>
{/* Purchase Form Dialog */}
{purchaseSupplier && (
<PurchaseForm
supplierName={purchaseSupplier.name}
open={showPurchaseForm}
onOpenChange={(open) => {
setShowPurchaseForm(open)
if (!open) setPurchaseSupplierId("")
}}
onSubmit={handlePurchaseSubmit}
/>
)}
{/* Supplier Payment Form Dialog */}
{paymentSupplier && (
<SupplierPaymentForm
supplierName={paymentSupplier.name}
amountOwed={paymentSupplier.amountOwed}
open={showSupplierPaymentForm}
onOpenChange={(open) => {
setShowSupplierPaymentForm(open)
if (!open) setSupplierPaymentId("")
}}
onSubmit={handleSupplierPaymentSubmit}
/>
)}
</div>
)
}