Files
karibeo_backend_admin/src/components/restaurant/BillManagement.tsx
2025-10-10 23:20:14 +00:00

275 lines
10 KiB
TypeScript

import { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Separator } from '@/components/ui/separator';
import { Receipt, Users, CreditCard, Percent } from 'lucide-react';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
import { toast } from 'sonner';
interface Bill {
id: string;
tableNumber: number;
items: BillItem[];
subtotal: number;
tip: number;
total: number;
status: 'open' | 'split' | 'paid';
splits?: number;
}
interface BillItem {
name: string;
quantity: number;
price: number;
}
const BillManagement = () => {
const [bills, setBills] = useState<Bill[]>([
{
id: '1',
tableNumber: 5,
items: [
{ name: 'Paella Valenciana', quantity: 2, price: 18.50 },
{ name: 'Gazpacho', quantity: 2, price: 6.50 },
{ name: 'Vino Tinto', quantity: 1, price: 12.00 }
],
subtotal: 62.00,
tip: 6.20,
total: 68.20,
status: 'open'
},
{
id: '2',
tableNumber: 3,
items: [
{ name: 'Pulpo a la Gallega', quantity: 1, price: 16.00 }
],
subtotal: 16.00,
tip: 0,
total: 16.00,
status: 'open'
}
]);
const [selectedBill, setSelectedBill] = useState<Bill | null>(null);
const [tipPercentage, setTipPercentage] = useState('10');
const [splitCount, setSplitCount] = useState('2');
const calculateTip = (subtotal: number, percentage: string) => {
const percent = parseFloat(percentage) || 0;
return (subtotal * percent) / 100;
};
const applySplitBill = () => {
if (!selectedBill) return;
const splits = parseInt(splitCount) || 2;
const amountPerPerson = selectedBill.total / splits;
setBills(bills.map(bill =>
bill.id === selectedBill.id
? { ...bill, status: 'split', splits }
: bill
));
toast.success(`Cuenta dividida en ${splits} partes: €${amountPerPerson.toFixed(2)} por persona`);
};
const applyTip = (billId: string) => {
setBills(bills.map(bill => {
if (bill.id === billId) {
const tip = calculateTip(bill.subtotal, tipPercentage);
return {
...bill,
tip,
total: bill.subtotal + tip
};
}
return bill;
}));
toast.success(`Propina aplicada: €${calculateTip(selectedBill?.subtotal || 0, tipPercentage).toFixed(2)}`);
};
const closeBill = (billId: string) => {
setBills(bills.map(bill =>
bill.id === billId ? { ...bill, status: 'paid' } : bill
));
toast.success('Cuenta cerrada correctamente');
};
return (
<div className="space-y-6">
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{bills.filter(b => b.status !== 'paid').map((bill) => (
<Card key={bill.id}>
<CardHeader>
<div className="flex justify-between items-start">
<CardTitle className="text-lg">Mesa {bill.tableNumber}</CardTitle>
<Badge variant={bill.status === 'split' ? 'default' : 'secondary'}>
{bill.status === 'split' ? `Dividida (${bill.splits})` : 'Abierta'}
</Badge>
</div>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
{bill.items.map((item, idx) => (
<div key={idx} className="flex justify-between text-sm">
<span>{item.quantity}x {item.name}</span>
<span>{(item.quantity * item.price).toFixed(2)}</span>
</div>
))}
</div>
<Separator />
<div className="space-y-1 text-sm">
<div className="flex justify-between">
<span>Subtotal:</span>
<span>{bill.subtotal.toFixed(2)}</span>
</div>
<div className="flex justify-between text-muted-foreground">
<span>Propina:</span>
<span>{bill.tip.toFixed(2)}</span>
</div>
<div className="flex justify-between font-bold text-base pt-1">
<span>Total:</span>
<span className="text-primary">{bill.total.toFixed(2)}</span>
</div>
</div>
{bill.status === 'split' && (
<div className="bg-primary/10 p-2 rounded text-sm">
<div className="font-medium">Por persona:</div>
<div className="text-lg font-bold text-primary">
{(bill.total / (bill.splits || 1)).toFixed(2)}
</div>
</div>
)}
<div className="flex gap-2 pt-2">
<Dialog>
<DialogTrigger asChild>
<Button
variant="outline"
size="sm"
className="flex-1 gap-2"
onClick={() => setSelectedBill(bill)}
>
<Users className="h-4 w-4" />
Dividir
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Dividir Cuenta - Mesa {bill.tableNumber}</DialogTitle>
</DialogHeader>
<div className="space-y-4">
<div>
<Label htmlFor="split-count">Número de Personas</Label>
<Input
id="split-count"
type="number"
min="2"
value={splitCount}
onChange={(e) => setSplitCount(e.target.value)}
/>
</div>
<div className="p-4 bg-muted rounded-lg">
<div className="text-sm text-muted-foreground">Monto por persona:</div>
<div className="text-2xl font-bold text-primary">
{(bill.total / (parseInt(splitCount) || 2)).toFixed(2)}
</div>
</div>
<Button className="w-full" onClick={applySplitBill}>
Aplicar División
</Button>
</div>
</DialogContent>
</Dialog>
<Dialog>
<DialogTrigger asChild>
<Button
variant="outline"
size="sm"
className="flex-1 gap-2"
onClick={() => setSelectedBill(bill)}
>
<Percent className="h-4 w-4" />
Propina
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Agregar Propina - Mesa {bill.tableNumber}</DialogTitle>
</DialogHeader>
<div className="space-y-4">
<div>
<Label htmlFor="tip-percentage">Porcentaje de Propina</Label>
<Input
id="tip-percentage"
type="number"
min="0"
max="100"
value={tipPercentage}
onChange={(e) => setTipPercentage(e.target.value)}
/>
</div>
<div className="grid grid-cols-3 gap-2">
<Button variant="outline" onClick={() => setTipPercentage('5')}>5%</Button>
<Button variant="outline" onClick={() => setTipPercentage('10')}>10%</Button>
<Button variant="outline" onClick={() => setTipPercentage('15')}>15%</Button>
</div>
<div className="p-4 bg-muted rounded-lg">
<div className="flex justify-between text-sm mb-2">
<span>Subtotal:</span>
<span>{bill.subtotal.toFixed(2)}</span>
</div>
<div className="flex justify-between text-sm mb-2">
<span>Propina ({tipPercentage}%):</span>
<span>{calculateTip(bill.subtotal, tipPercentage).toFixed(2)}</span>
</div>
<Separator className="my-2" />
<div className="flex justify-between">
<span className="font-bold">Total:</span>
<span className="text-xl font-bold text-primary">
{(bill.subtotal + calculateTip(bill.subtotal, tipPercentage)).toFixed(2)}
</span>
</div>
</div>
<Button className="w-full" onClick={() => applyTip(bill.id)}>
Aplicar Propina
</Button>
</div>
</DialogContent>
</Dialog>
</div>
<Button
className="w-full gap-2"
onClick={() => closeBill(bill.id)}
>
<CreditCard className="h-4 w-4" />
Cerrar Cuenta
</Button>
</CardContent>
</Card>
))}
</div>
{bills.filter(b => b.status !== 'paid').length === 0 && (
<Card>
<CardContent className="py-12 text-center text-muted-foreground">
<Receipt className="h-12 w-12 mx-auto mb-4 opacity-50" />
<p>No hay cuentas abiertas</p>
</CardContent>
</Card>
)}
</div>
);
};
export default BillManagement;