correção

This commit is contained in:
2026-03-12 17:23:09 +00:00
parent 8842ff08b4
commit 7c8ecb7563

View File

@@ -29,16 +29,24 @@ export default function Booking() {
const [barberId, setBarber] = useState('');
const [date, setDate] = useState('');
const [slot, setSlot] = useState('');
const [step, setStep] = useState(searchParams.get('service') ? 2 : 1);
// Sincroniza o serviceId se o parâmetro mudar (ex: navegação interna)
useEffect(() => {
const s = searchParams.get('service');
if (s) setService(s);
if (s) {
setService(s);
setStep(2);
}
}, [searchParams]);
const selectedService = shop?.services.find((s) => s.id === serviceId);
const selectedBarber = shop?.barbers.find((b) => b.id === barberId);
// Avanço automático por efeito colateral (opcional, mas vamos fazer por clique para ser mais explícito)
const nextStep = () => setStep((s) => Math.min(s + 1, 4));
const prevStep = () => setStep((s) => Math.max(s - 1, 1));
/**
* Função para gerar horários padrão se não houver horários específicos predefinidos
* pelo barbeiro na Base de Dados.
@@ -111,173 +119,234 @@ export default function Booking() {
};
const steps = [
{ id: 1, label: 'Serviço', icon: Scissors, completed: !!serviceId },
{ id: 2, label: 'Barbeiro', icon: User, completed: !!barberId },
{ id: 3, label: 'Data', icon: Calendar, completed: !!date },
{ id: 4, label: 'Horário', icon: Clock, completed: !!slot },
{ id: 1, label: 'Serviço', icon: Scissors, completed: !!serviceId, active: step === 1 },
{ id: 2, label: 'Barbeiro', icon: User, completed: !!barberId, active: step === 2 },
{ id: 3, label: 'Data', icon: Calendar, completed: !!date, active: step === 3 },
{ id: 4, label: 'Horário', icon: Clock, completed: !!slot, active: step === 4 },
];
return (
<div className="space-y-6">
<div>
<h1 className="text-2xl font-bold text-slate-900 mb-2">Agendar em {shop.name}</h1>
<p className="text-sm text-slate-600">{shop.address}</p>
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold text-slate-900 mb-1">Agendar em {shop.name}</h1>
<p className="text-sm text-slate-600">{shop.address}</p>
</div>
{step > 1 && (
<Button variant="outline" size="sm" onClick={prevStep}>
Voltar
</Button>
)}
</div>
{/* Progress Steps */}
<div className="flex items-center justify-between max-w-2xl">
{steps.map((step, idx) => (
<div key={step.id} className="flex items-center flex-1">
<div className="flex items-center justify-between max-w-2xl bg-white p-4 rounded-xl shadow-sm border border-slate-100">
{steps.map((s, idx) => (
<div key={s.id} className="flex items-center flex-1 last:flex-none">
<div className="flex flex-col items-center flex-1">
<div
className={`w-10 h-10 rounded-full flex items-center justify-center border-2 transition-all ${step.completed
? 'bg-gradient-to-br from-amber-500 to-amber-600 border-amber-600 text-white shadow-md'
: 'bg-white border-slate-300 text-slate-400'
<button
onClick={() => {
if (s.completed || s.id < step) setStep(s.id);
}}
disabled={!s.completed && s.id > step}
className={`w-10 h-10 rounded-full flex items-center justify-center border-2 transition-all ${s.active
? 'bg-amber-600 border-amber-600 text-white shadow-lg ring-4 ring-amber-100'
: s.completed
? 'bg-amber-500 border-amber-500 text-white'
: 'bg-white border-slate-200 text-slate-400'
}`}
>
{step.completed ? <CheckCircle2 size={18} /> : <step.icon size={18} />}
</div>
<span className={`text-xs mt-2 font-medium ${step.completed ? 'text-amber-700' : 'text-slate-500'}`}>
{step.label}
{s.completed && !s.active ? <CheckCircle2 size={18} /> : <s.icon size={18} />}
</button>
<span className={`text-[10px] mt-2 font-bold uppercase tracking-wider ${s.active ? 'text-amber-700' : 'text-slate-500'}`}>
{s.label}
</span>
</div>
{idx < steps.length - 1 && (
<div className={`h-0.5 flex-1 mx-2 ${step.completed ? 'bg-amber-500' : 'bg-slate-200'}`} />
<div className={`h-1 flex-1 mx-2 rounded-full ${s.completed ? 'bg-amber-500' : 'bg-slate-100'}`} />
)}
</div>
))}
</div>
<Card className="p-6 space-y-6">
{/* Step 1: Service */}
<div className="space-y-3">
<div className="flex items-center gap-2">
<Scissors size={18} className="text-amber-600" />
<h3 className="text-base font-bold text-slate-900">1. Escolha o serviço</h3>
</div>
<div className="grid md:grid-cols-2 gap-3">
{shop.services.map((s) => (
<button
key={s.id}
onClick={() => setService(s.id)}
className={`p-4 rounded-xl border-2 text-left transition-all ${serviceId === s.id
? 'border-amber-500 bg-gradient-to-br from-amber-50 to-amber-100/50 shadow-md scale-[1.02]'
: 'border-slate-200 hover:border-amber-300 hover:bg-amber-50/50'
}`}
>
<div className="flex items-center justify-between mb-1">
<div className="font-bold text-slate-900">{s.name}</div>
<div className="text-sm font-bold text-amber-600">{currency(s.price)}</div>
</div>
<div className="text-xs text-slate-500">Duração: {s.duration} min</div>
</button>
))}
</div>
</div>
{/* Step 2: Barber */}
<div className="space-y-3">
<div className="flex items-center gap-2">
<User size={18} className="text-amber-600" />
<h3 className="text-base font-bold text-slate-900">2. Escolha o barbeiro</h3>
</div>
<div className="flex gap-2 flex-wrap">
{shop.barbers.map((b) => (
<button
key={b.id}
onClick={() => setBarber(b.id)}
className={`px-4 py-2.5 rounded-full border-2 text-sm font-medium transition-all ${barberId === b.id
? 'border-amber-500 bg-gradient-to-r from-amber-500 to-amber-600 text-white shadow-md'
: 'border-slate-200 text-slate-700 hover:border-amber-300 hover:bg-amber-50'
}`}
>
{b.name}
{b.specialties.length > 0 && (
<span className="ml-2 text-xs opacity-80">· {b.specialties[0]}</span>
)}
</button>
))}
</div>
</div>
{/* Step 3 & 4: Date & Time */}
<div className="grid md:grid-cols-2 gap-6">
<div className="space-y-3">
<div className="flex items-center gap-2">
<Calendar size={18} className="text-amber-600" />
<h3 className="text-base font-bold text-slate-900">3. Escolha a data</h3>
<Card className="p-6">
{/* Step Info Summary (Show what was already selected) */}
{step > 1 && selectedService && (
<div className="mb-6 p-3 bg-amber-50 border border-amber-100 rounded-lg flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-white rounded-full flex items-center justify-center text-amber-600 shadow-sm">
<Scissors size={20} />
</div>
<div>
<p className="text-xs text-amber-600 font-bold uppercase">Serviço Selecionado</p>
<p className="text-sm font-bold text-slate-900">{selectedService.name}</p>
</div>
</div>
<Input
type="date"
value={date}
onChange={(e) => setDate(e.target.value)}
min={new Date().toISOString().split('T')[0]}
/>
</div>
<div className="space-y-3">
<div className="flex items-center gap-2">
<Clock size={18} className="text-amber-600" />
<h3 className="text-base font-bold text-slate-900">4. Escolha o horário</h3>
</div>
<div className="flex gap-2 flex-wrap">
{!barberId || !date ? (
<p className="text-sm text-slate-500 py-2">Escolha primeiro o barbeiro e a data.</p>
) : availableSlots.length > 0 ? (
availableSlots.map((h) => (
<button
key={h}
onClick={() => setSlot(h)}
className={`px-4 py-2 rounded-lg border-2 text-sm font-medium transition-all ${slot === h
? 'border-amber-500 bg-gradient-to-r from-amber-500 to-amber-600 text-white shadow-md'
: 'border-slate-200 text-slate-700 hover:border-amber-300 hover:bg-amber-50'
}`}
>
{h}
</button>
))
) : (
<p className="text-sm text-amber-600 py-2 font-medium">Nenhum horário disponível para esta data.</p>
)}
</div>
</div>
</div>
{/* Summary */}
{canSubmit && selectedService && (
<div className="pt-4 border-t border-slate-200 space-y-3">
<h4 className="text-sm font-bold text-slate-900">Resumo do agendamento</h4>
<div className="bg-slate-50 rounded-lg p-4 space-y-2 text-sm">
<div className="flex justify-between">
<span className="text-slate-600">Serviço:</span>
<span className="font-semibold text-slate-900">{selectedService.name}</span>
</div>
<div className="flex justify-between">
<span className="text-slate-600">Barbeiro:</span>
<span className="font-semibold text-slate-900">{selectedBarber?.name}</span>
</div>
<div className="flex justify-between">
<span className="text-slate-600">Data e hora:</span>
<span className="font-semibold text-slate-900">
{new Date(date).toLocaleDateString('pt-BR')} às {slot}
</span>
</div>
<div className="flex justify-between pt-2 border-t border-slate-200">
<span className="font-bold text-slate-900">Total:</span>
<span className="font-bold text-lg text-amber-600">{currency(selectedService.price)}</span>
</div>
<div className="text-right">
<p className="text-lg font-bold text-amber-600">{currency(selectedService.price)}</p>
</div>
</div>
)}
<Button onClick={submit} disabled={!canSubmit} size="lg" className="w-full">
{user ? 'Confirmar agendamento' : 'Entrar para agendar'}
</Button>
{step > 2 && selectedBarber && (
<div className="mb-6 p-3 bg-indigo-50 border border-indigo-100 rounded-lg flex items-center gap-3">
<div className="w-10 h-10 bg-white rounded-full flex items-center justify-center text-indigo-600 shadow-sm">
<User size={20} />
</div>
<div>
<p className="text-xs text-indigo-600 font-bold uppercase">Barbeiro</p>
<p className="text-sm font-bold text-slate-900">{selectedBarber.name}</p>
</div>
</div>
)}
{/* Dynamic Step Content */}
<div className="space-y-6">
{step === 1 && (
<div className="space-y-4 animate-in fade-in slide-in-from-bottom-2 duration-300">
<div className="flex items-center gap-2 mb-2">
<div className="w-8 h-8 bg-amber-600 text-white rounded-lg flex items-center justify-center text-sm font-bold">1</div>
<h3 className="text-lg font-bold text-slate-900">Escolha o serviço</h3>
</div>
<div className="grid md:grid-cols-2 gap-3">
{shop.services.map((s) => (
<button
key={s.id}
onClick={() => {
setService(s.id);
setStep(2);
}}
className={`p-4 rounded-xl border-2 text-left transition-all ${serviceId === s.id
? 'border-amber-500 bg-amber-50/50 shadow-md ring-2 ring-amber-200'
: 'border-slate-100 hover:border-amber-300 hover:bg-amber-50/30'
}`}
>
<div className="flex items-center justify-between mb-1">
<div className="font-bold text-slate-900">{s.name}</div>
<div className="text-sm font-bold text-amber-600">{currency(s.price)}</div>
</div>
<div className="text-xs text-slate-500">Duração: {s.duration} min</div>
</button>
))}
</div>
</div>
)}
{step === 2 && (
<div className="space-y-4 animate-in fade-in slide-in-from-bottom-2 duration-300">
<div className="flex items-center gap-2 mb-2">
<div className="w-8 h-8 bg-amber-600 text-white rounded-lg flex items-center justify-center text-sm font-bold">2</div>
<h3 className="text-lg font-bold text-slate-900">Escolha o barbeiro</h3>
</div>
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
{shop.barbers.map((b) => (
<button
key={b.id}
onClick={() => {
setBarber(b.id);
setStep(3);
}}
className={`p-4 rounded-xl border-2 text-center transition-all flex flex-col items-center gap-3 ${barberId === b.id
? 'border-amber-500 bg-amber-50/50 shadow-md ring-2 ring-amber-200'
: 'border-slate-100 hover:border-amber-300 hover:bg-amber-50/30'
}`}
>
<div className="w-16 h-16 rounded-full overflow-hidden border-2 border-slate-200 bg-slate-50">
{b.imageUrl ? (
<img src={b.imageUrl} alt={b.name} className="w-full h-full object-cover" />
) : (
<div className="w-full h-full flex items-center justify-center text-slate-400">
<User size={32} />
</div>
)}
</div>
<div>
<p className="font-bold text-slate-900">{b.name}</p>
{b.specialties.length > 0 && (
<p className="text-xs text-slate-500 mt-1">{b.specialties[0]}</p>
)}
</div>
</button>
))}
</div>
</div>
)}
{step === 3 && (
<div className="space-y-4 animate-in fade-in slide-in-from-bottom-2 duration-300">
<div className="flex items-center gap-2 mb-2">
<div className="w-8 h-8 bg-amber-600 text-white rounded-lg flex items-center justify-center text-sm font-bold">3</div>
<h3 className="text-lg font-bold text-slate-900">Escolha a data</h3>
</div>
<div className="max-w-md mx-auto">
<Input
type="date"
value={date}
onChange={(e) => {
setDate(e.target.value);
if (e.target.value) setStep(4);
}}
min={new Date().toISOString().split('T')[0]}
className="text-lg py-6"
/>
</div>
</div>
)}
{step === 4 && (
<div className="space-y-6 animate-in fade-in slide-in-from-bottom-2 duration-300">
<div className="flex items-center gap-2 mb-2">
<div className="w-8 h-8 bg-amber-600 text-white rounded-lg flex items-center justify-center text-sm font-bold">4</div>
<h3 className="text-lg font-bold text-slate-900">Escolha o horário</h3>
</div>
<div className="p-4 bg-slate-50 rounded-xl mb-4 flex items-center justify-between border border-slate-100">
<div className="flex items-center gap-2 text-slate-700">
<Calendar size={18} className="text-amber-600" />
<span className="font-bold">{new Date(date).toLocaleDateString('pt-PT', { day: 'numeric', month: 'long', year: 'numeric' })}</span>
</div>
<Button variant="ghost" size="sm" onClick={() => setStep(3)}>Alterar</Button>
</div>
<div className="grid grid-cols-3 md:grid-cols-4 gap-2">
{availableSlots.length > 0 ? (
availableSlots.map((h) => (
<button
key={h}
onClick={() => setSlot(h)}
className={`py-3 rounded-lg border-2 text-sm font-bold transition-all ${slot === h
? 'border-amber-600 bg-amber-600 text-white shadow-md'
: 'border-slate-200 text-slate-700 hover:border-amber-400 hover:bg-amber-50'
}`}
>
{h}
</button>
))
) : (
<div className="col-span-full py-8 text-center bg-rose-50 rounded-xl border border-rose-100">
<p className="text-sm text-rose-600 font-bold">Infelizmente não horários livres para este dia.</p>
</div>
)}
</div>
{/* Final Summary & Confirmation */}
{canSubmit && (
<div className="pt-6 border-t border-slate-200 mt-8 space-y-4">
<div className="flex justify-between items-end">
<div>
<p className="text-xs text-slate-500 font-bold uppercase mb-1">Total a pagar</p>
<p className="text-2xl font-black text-slate-900">{currency(selectedService?.price || 0)}</p>
</div>
<Button onClick={submit} size="lg" className="px-10 bg-indigo-600 hover:bg-indigo-700 shadow-lg shadow-indigo-100">
Confirmar Marcação
</Button>
</div>
</div>
)}
</div>
)}
</div>
</Card>
</div>
);
}