138 lines
5.9 KiB
TypeScript
138 lines
5.9 KiB
TypeScript
"use client";
|
|
|
|
import { useReservas } from "@/hooks/useReservas";
|
|
import { useMesas } from "@/hooks/useMesas";
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Button } from "@/components/ui/button";
|
|
import { CalendarDays, Check, X, Clock, User } from "lucide-react";
|
|
import { Reserva } from "@/types/reserva";
|
|
import { Mesa } from "@/types/mesa";
|
|
import { useToast } from "@/components/ui/toast";
|
|
|
|
export default function ReservasPage() {
|
|
const { reservas, loading, updateReservaEstado, concluirReserva, confirmarComMesa } = useReservas();
|
|
const { mesas } = useMesas();
|
|
const { toast } = useToast();
|
|
|
|
const handleConcluir = async (reserva: Reserva) => {
|
|
// Tentar encontrar a mesa se o estado for "Confirmada (Mesa X)"
|
|
let mesaId: string | undefined;
|
|
if (reserva.estado.includes("Mesa")) {
|
|
const match = reserva.estado.match(/Mesa (\d+)/);
|
|
if (match) {
|
|
const num = parseInt(match[1]);
|
|
const mesa = mesas.find(m => m.numero === num);
|
|
mesaId = mesa?.id;
|
|
}
|
|
}
|
|
const res = await concluirReserva(reserva.id, mesaId);
|
|
if (res.success) {
|
|
toast("Reserva concluída e mesa libertada.", "success");
|
|
}
|
|
};
|
|
|
|
const handleUpdate = async (id: string, estado: any) => {
|
|
const res = await updateReservaEstado(id, estado);
|
|
if (res.success) {
|
|
toast(`Reserva marcada como ${estado}`, "info");
|
|
}
|
|
};
|
|
|
|
const getStatusColor = (estado: string) => {
|
|
if (estado.startsWith("Confirmada")) return "bg-green-500/10 text-green-500 border-green-500/20";
|
|
switch (estado) {
|
|
case "Pendente": return "bg-amber-500/10 text-amber-500 border-amber-500/20";
|
|
case "Recusada":
|
|
case "Cancelada": return "bg-destructive/10 text-destructive border-destructive/20";
|
|
default: return "bg-muted text-muted-foreground border-border";
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<div className="flex items-center justify-between">
|
|
<h1 className="text-3xl font-display font-bold text-foreground">Gestão de Reservas</h1>
|
|
<div className="flex items-center gap-2 text-sm text-muted-foreground bg-card px-3 py-1.5 rounded-full border border-border">
|
|
<CalendarDays className="h-4 w-4 text-primary" />
|
|
<span>{new Date().toLocaleDateString('pt-PT')}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid gap-4">
|
|
{loading ? (
|
|
<div className="flex items-center justify-center py-20">
|
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
|
|
</div>
|
|
) : reservas.length > 0 ? (
|
|
reservas.map((reserva) => (
|
|
<Card key={reserva.id} className="overflow-hidden border-border/50 hover:border-primary/30 transition-all duration-200 shadow-sm">
|
|
<CardContent className="p-0">
|
|
<div className="flex flex-col md:flex-row md:items-center p-6 gap-6">
|
|
<div className="flex-1 space-y-2">
|
|
<div className="flex items-center gap-3">
|
|
<div className={`px-2.5 py-0.5 rounded-full text-[11px] font-bold uppercase border ${getStatusColor(reserva.estado)}`}>
|
|
{reserva.estado}
|
|
</div>
|
|
<span className="text-sm text-muted-foreground flex items-center gap-1">
|
|
<Clock className="h-3 w-3" /> {reserva.data} às {reserva.hora}
|
|
</span>
|
|
</div>
|
|
<h3 className="text-xl font-bold">{reserva.clienteEmail}</h3>
|
|
<div className="flex items-center gap-4 text-sm text-muted-foreground">
|
|
<span className="flex items-center gap-1">
|
|
<User className="h-4 w-4" /> {reserva.pessoas} pessoas
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-2">
|
|
{reserva.estado === "Pendente" && (
|
|
<>
|
|
<Button
|
|
onClick={() => handleUpdate(reserva.id, "Confirmada")}
|
|
className="bg-green-600 hover:bg-green-700 text-white"
|
|
size="sm"
|
|
>
|
|
<Check className="h-4 w-4 mr-2" /> Aceitar
|
|
</Button>
|
|
<Button
|
|
size="sm"
|
|
variant="ghost"
|
|
className="text-destructive hover:bg-destructive/10"
|
|
onClick={() => handleUpdate(reserva.id, "Recusada")}
|
|
>
|
|
<X className="h-4 w-4 mr-1" /> Recusar
|
|
</Button>
|
|
</>
|
|
)}
|
|
{reserva.estado.startsWith("Confirmada") && (
|
|
<Button
|
|
onClick={() => handleConcluir(reserva)}
|
|
variant="outline"
|
|
size="sm"
|
|
className="hover:bg-primary/10 hover:text-primary hover:border-primary/30"
|
|
>
|
|
<Check className="h-4 w-4 mr-2" /> Marcar como Concluída
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
))
|
|
) : (
|
|
<Card className="border-dashed border-2">
|
|
<CardContent className="flex flex-col items-center justify-center py-20 text-center">
|
|
<CalendarDays className="h-16 w-16 text-muted-foreground/20 mb-4" />
|
|
<h3 className="text-xl font-medium">Sem reservas registadas</h3>
|
|
<p className="text-muted-foreground max-w-xs mx-auto">
|
|
As reservas feitas pelos clientes através da App Android aparecerão aqui em tempo real.
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|