feat(salas): reserva vira wizard de 4 passos em slide

Modal de reservar sala reorganizado num wizard (formato slide, translateX) com
stepper clicável p/ voltar:
1) Dados — reservar como (pessoal/unidade) + profissional + observações.
2) Calendário — modelo (cartões c/ preço) + calendário mensal + horário/turno.
3) Resumo — resumo detalhado (sala, modelo, período, total) + histórico de
   reservas da sala.
4) Pagamento — total, forma de pagamento (PIX/Dinheiro/Transferência/Cartão) e
   como funciona; a forma escolhida vai nas observações do pedido.
Navegação Voltar/Próximo (Próximo bloqueado no calendário até escolher o dia).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
VPS 4 Builder
2026-07-07 03:46:02 +02:00
parent dab74de207
commit f24a5a82e8
+222 -131
View File
@@ -337,6 +337,17 @@ const ReservaModal: React.FC<{ sala: Sala; onClose: () => void; onReserved: () =
const ocupadasDoDia = diaSel ? ocupadas.filter(o => new Date(o.inicio).toDateString() === diaSel.toDateString()) : [];
const rangeFim = diaSel ? (() => { const d = new Date(diaSel); d.setDate(d.getDate() + (f.modelo === 'mensal' ? 29 : 6)); return d; })() : null;
// ── Wizard (4 passos, formato slide) ──
const [step, setStep] = useState(0);
const [formaPagamento, setFormaPagamento] = useState('pix');
const STEPS = ['Dados', 'Calendário', 'Resumo', 'Pagamento'];
const FORMAS: Record<string, string> = { pix: 'PIX', dinheiro: 'Dinheiro', transferencia: 'Transferência', cartao: 'Cartão' };
const modeloLabel = MODELOS.find(m => m.id === f.modelo)?.label || '';
const total = (() => {
const m = MODELOS.find(x => x.id === f.modelo); const v = m ? Number(sala[m.valor]) : NaN;
return Number.isFinite(v) ? (f.modelo === 'hora' ? v * Math.max(1, qtd) : v) : 0;
})();
// Modelo padrão = 1º disponível.
useEffect(() => { if (modelosDisp.length && !modelosDisp.some(m => m.id === f.modelo)) setF(p => ({ ...p, modelo: modelosDisp[0].id })); /* eslint-disable-next-line */ }, []);
// Recalcula início/fim conforme dia + modelo + horário.
@@ -369,7 +380,8 @@ const ReservaModal: React.FC<{ sala: Sala; onClose: () => void; onReserved: () =
const res = await apiFetch(`/api/salas/${sala.id}/reservas`, {
method: 'POST',
body: JSON.stringify({
inicio: new Date(f.inicio).toISOString(), fim: new Date(f.fim).toISOString(), modelo: f.modelo, observacoes: f.observacoes || null,
inicio: new Date(f.inicio).toISOString(), fim: new Date(f.fim).toISOString(), modelo: f.modelo,
observacoes: [f.observacoes, `Forma de pagamento: ${FORMAS[formaPagamento] || formaPagamento}`].filter(Boolean).join(' · ') || null,
clinicaId: comoUnidade && ws?.id ? ws.id : null,
profissionalUsuarioId: comoUnidade && profissionalId ? profissionalId : null,
}),
@@ -385,152 +397,231 @@ const ReservaModal: React.FC<{ sala: Sala; onClose: () => void; onReserved: () =
return (
<div className="fixed inset-0 bg-black/60 z-50 flex items-center justify-center backdrop-blur-sm p-4">
<div className="bg-white rounded-2xl w-full max-w-lg shadow-2xl overflow-hidden max-h-[92vh] overflow-y-auto">
<div className="px-6 py-4 border-b border-gray-100 flex items-center justify-between">
<div>
<h3 className="text-sm font-black text-gray-900 uppercase">RESERVAR {sala.nome}</h3>
<p className="text-[10px] font-bold uppercase tracking-widest" style={{ color: COLOR }}>{[sala.cidade, sala.estado].filter(Boolean).join(' · ')}</p>
<div className="bg-white rounded-[1.5rem] w-full max-w-lg shadow-2xl overflow-hidden max-h-[92vh] flex flex-col">
{/* Header + stepper */}
<div className="px-6 pt-5 pb-4 border-b border-gray-100 shrink-0">
<div className="flex items-center justify-between">
<div>
<h3 className="text-sm font-black text-gray-900 uppercase">Reservar {sala.nome}</h3>
<p className="text-[10px] font-bold uppercase tracking-widest" style={{ color: COLOR }}>{[sala.cidade, sala.estado].filter(Boolean).join(' · ')}</p>
</div>
<button onClick={onClose} className="p-2 hover:bg-gray-100 rounded-xl transition-colors"><X size={18} /></button>
</div>
<div className="flex items-center gap-2 mt-4">
{STEPS.map((s, i) => (
<button key={s} type="button" onClick={() => i < step && setStep(i)} className="flex-1 flex flex-col items-center gap-1.5">
<div className={`w-full h-1.5 rounded-full transition-colors ${i <= step ? '' : 'bg-gray-100'}`} style={i <= step ? { backgroundColor: COLOR } : {}} />
<span className={`text-[8px] font-black uppercase tracking-wide ${i === step ? 'text-gray-700' : 'text-gray-300'}`}>{s}</span>
</button>
))}
</div>
<button onClick={onClose} className="p-2 hover:bg-gray-100 rounded-xl transition-colors"><X size={18} /></button>
</div>
<div className="p-6 space-y-4">
{ws?.id && (
<div>
<label className={labelCls}>Reservar como</label>
<div className="flex gap-2">
{[{ v: false, l: 'PESSOAL' }, { v: true, l: ws.nome?.toUpperCase() || 'UNIDADE' }].map(o => (
<button key={String(o.v)} type="button" onClick={() => setComoUnidade(o.v)}
className={`flex-1 py-2.5 rounded-xl text-[10px] font-black uppercase border transition-all truncate px-2 ${comoUnidade === o.v ? 'text-white border-transparent' : 'bg-gray-50 text-gray-500 border-gray-200 hover:bg-gray-100'}`}
style={comoUnidade === o.v ? { backgroundColor: COLOR } : {}}>
{o.l}
</button>
))}
{/* Slides */}
<div className="flex-1 overflow-hidden min-h-[340px]">
<div className="flex h-full transition-transform duration-300 ease-out" style={{ transform: `translateX(-${step * 100}%)` }}>
{/* PASSO 1 — Dados */}
<div className="w-full shrink-0 overflow-y-auto p-6 space-y-4">
{ws?.id && (
<div>
<label className={labelCls}>Reservar como</label>
<div className="flex gap-2">
{[{ v: false, l: 'PESSOAL' }, { v: true, l: ws.nome?.toUpperCase() || 'UNIDADE' }].map(o => (
<button key={String(o.v)} type="button" onClick={() => setComoUnidade(o.v)}
className={`flex-1 py-2.5 rounded-xl text-[10px] font-black uppercase border transition-all truncate px-2 ${comoUnidade === o.v ? 'text-white border-transparent' : 'bg-gray-50 text-gray-500 border-gray-200 hover:bg-gray-100'}`}
style={comoUnidade === o.v ? { backgroundColor: COLOR } : {}}>{o.l}</button>
))}
</div>
</div>
)}
{comoUnidade && (
<div>
<label className={labelCls}>Profissional que vai usar a sala (opcional)</label>
<select className={inputCls} value={profissionalId} onChange={e => setProfissionalId(e.target.value)}>
<option value="">EU MESMO ({HybridBackend.getCurrentUserName()?.toUpperCase() || 'SOLICITANTE'})</option>
{profissionais.map(p => <option key={p.usuario_id} value={p.usuario_id}>{p.nome?.toUpperCase()}</option>)}
</select>
<p className="text-[9px] text-gray-400 font-bold uppercase mt-1">O anti-conflito verifica a agenda do profissional selecionado</p>
</div>
)}
<div>
<label className={labelCls}>Observações</label>
<textarea className={inputCls} rows={4} value={f.observacoes} onChange={e => setF(p => ({ ...p, observacoes: e.target.value }))} placeholder="Alguma informação para o locador…" />
</div>
</div>
)}
{comoUnidade && (
<div>
<label className={labelCls}>Profissional que vai usar a sala (opcional)</label>
<select className={inputCls} value={profissionalId} onChange={e => setProfissionalId(e.target.value)}>
<option value="">EU MESMO ({HybridBackend.getCurrentUserName()?.toUpperCase() || 'SOLICITANTE'})</option>
{profissionais.map(p => <option key={p.usuario_id} value={p.usuario_id}>{p.nome?.toUpperCase()}</option>)}
</select>
<p className="text-[9px] text-gray-400 font-bold uppercase mt-1">O anti-conflito verifica a agenda do profissional selecionado</p>
</div>
)}
{/* Modelo de locação — cartões com preço */}
<div>
<label className={labelCls}>Modelo de locação</label>
<div className="grid grid-cols-3 sm:grid-cols-5 gap-2 mt-1">
{(modelosDisp.length ? modelosDisp : [MODELOS[0]]).map(m => {
const ativo = f.modelo === m.id;
const preco = fmtValor(sala[m.valor] as any) || (sala[m.consulta] ? 'sob consulta' : '—');
return (
<button key={m.id} type="button" onClick={() => setF(p => ({ ...p, modelo: m.id }))}
className={`p-2.5 rounded-xl border text-center transition-all ${ativo ? 'text-white border-transparent shadow-sm' : 'bg-gray-50 text-gray-600 border-gray-200 hover:bg-gray-100'}`}
style={ativo ? { backgroundColor: COLOR } : {}}>
<div className="text-[11px] font-black uppercase leading-none">{m.label}</div>
<div className={`text-[9px] font-bold mt-1 leading-none ${ativo ? 'text-white/80' : 'text-gray-400'}`}>{preco}</div>
</button>
);
})}
</div>
</div>
{/* Calendário mensal */}
<div className="rounded-2xl border border-gray-100 p-4">
<div className="flex items-center justify-between mb-3">
<button type="button" onClick={() => setMesRef(m => new Date(m.getFullYear(), m.getMonth() - 1, 1))} className="p-1.5 rounded-lg hover:bg-gray-100 text-gray-500"><ChevronLeft size={18} /></button>
<span className="text-xs font-black text-gray-800 uppercase tracking-wide">{MESES[mesRef.getMonth()]} {mesRef.getFullYear()}</span>
<button type="button" onClick={() => setMesRef(m => new Date(m.getFullYear(), m.getMonth() + 1, 1))} className="p-1.5 rounded-lg hover:bg-gray-100 text-gray-500"><ChevronRight size={18} /></button>
</div>
<div className="grid grid-cols-7 gap-1 mb-1">
{WEEKDAYS.map((w, i) => <div key={i} className="text-center text-[9px] font-black text-gray-300 uppercase py-1">{w}</div>)}
</div>
<div className="grid grid-cols-7 gap-1">
{monthGrid(mesRef).map((dia, i) => {
if (!dia) return <div key={i} />;
const passado = dia < hoje0;
const sel = !!(diaSel && mesmoDia(dia, diaSel));
const emRange = !!(diaSel && rangeFim && (f.modelo === 'semanal' || f.modelo === 'mensal') && dia > diaSel && dia <= rangeFim);
const ocupado = diasOcupados.has(dia.toDateString());
return (
<button key={i} type="button" disabled={passado} onClick={() => setDiaSel(dia)}
className={`relative aspect-square rounded-lg text-xs font-bold flex items-center justify-center transition-all ${passado ? 'text-gray-200 cursor-not-allowed' : sel ? 'text-white shadow' : emRange ? 'text-teal-700' : 'text-gray-600 hover:bg-gray-100'}`}
style={sel ? { backgroundColor: COLOR } : emRange ? { backgroundColor: `${COLOR}22` } : {}}>
{dia.getDate()}
{ocupado && !sel && <span className="absolute bottom-1 w-1 h-1 rounded-full bg-amber-400" />}
</button>
);
})}
</div>
</div>
{/* Controles de horário (conforme o modelo) */}
{diaSel && f.modelo === 'hora' && (
<div className="grid grid-cols-2 gap-3">
{/* PASSO 2 — Calendário */}
<div className="w-full shrink-0 overflow-y-auto p-6 space-y-4">
<div>
<label className={labelCls}>Início</label>
<select className={inputCls} value={hora} onChange={e => setHora(e.target.value)}>
{HORAS.map(h => <option key={h} value={h}>{h}</option>)}
</select>
</div>
<div>
<label className={labelCls}>Duração</label>
<div className="flex items-center gap-2">
<button type="button" onClick={() => setQtd(q => Math.max(1, q - 1))} className="w-9 h-9 rounded-lg border border-gray-200 text-gray-500 font-black text-lg leading-none"></button>
<span className="flex-1 text-center text-sm font-black text-gray-800">{qtd}h</span>
<button type="button" onClick={() => setQtd(q => Math.min(12, q + 1))} className="w-9 h-9 rounded-lg border border-gray-200 text-gray-500 font-black text-lg leading-none">+</button>
<label className={labelCls}>Modelo de locação</label>
<div className="grid grid-cols-3 sm:grid-cols-5 gap-2 mt-1">
{(modelosDisp.length ? modelosDisp : [MODELOS[0]]).map(m => {
const ativo = f.modelo === m.id;
const preco = fmtValor(sala[m.valor] as any) || (sala[m.consulta] ? 'sob consulta' : '—');
return (
<button key={m.id} type="button" onClick={() => setF(p => ({ ...p, modelo: m.id }))}
className={`p-2.5 rounded-xl border text-center transition-all ${ativo ? 'text-white border-transparent shadow-sm' : 'bg-gray-50 text-gray-600 border-gray-200 hover:bg-gray-100'}`}
style={ativo ? { backgroundColor: COLOR } : {}}>
<div className="text-[11px] font-black uppercase leading-none">{m.label}</div>
<div className={`text-[9px] font-bold mt-1 leading-none ${ativo ? 'text-white/80' : 'text-gray-400'}`}>{preco}</div>
</button>
);
})}
</div>
</div>
</div>
)}
{diaSel && f.modelo === 'turno' && (
<div>
<label className={labelCls}>Turno</label>
<div className="grid grid-cols-3 gap-2">
{Object.entries(TURNOS).map(([k, t]) => (
<button key={k} type="button" onClick={() => setTurno(k)}
className={`py-2 rounded-xl text-[11px] font-black uppercase border transition-all ${turno === k ? 'text-white border-transparent' : 'bg-gray-50 text-gray-500 border-gray-200 hover:bg-gray-100'}`}
style={turno === k ? { backgroundColor: COLOR } : {}}>
{t.label}<div className="text-[8px] font-bold opacity-70">{pad2(t.ini)}h{pad2(t.fim)}h</div>
</button>
))}
<div className="rounded-2xl border border-gray-100 p-4">
<div className="flex items-center justify-between mb-3">
<button type="button" onClick={() => setMesRef(m => new Date(m.getFullYear(), m.getMonth() - 1, 1))} className="p-1.5 rounded-lg hover:bg-gray-100 text-gray-500"><ChevronLeft size={18} /></button>
<span className="text-xs font-black text-gray-800 uppercase tracking-wide">{MESES[mesRef.getMonth()]} {mesRef.getFullYear()}</span>
<button type="button" onClick={() => setMesRef(m => new Date(m.getFullYear(), m.getMonth() + 1, 1))} className="p-1.5 rounded-lg hover:bg-gray-100 text-gray-500"><ChevronRight size={18} /></button>
</div>
<div className="grid grid-cols-7 gap-1 mb-1">
{WEEKDAYS.map((w, i) => <div key={i} className="text-center text-[9px] font-black text-gray-300 uppercase py-1">{w}</div>)}
</div>
<div className="grid grid-cols-7 gap-1">
{monthGrid(mesRef).map((dia, i) => {
if (!dia) return <div key={i} />;
const passado = dia < hoje0;
const sel = !!(diaSel && mesmoDia(dia, diaSel));
const emRange = !!(diaSel && rangeFim && (f.modelo === 'semanal' || f.modelo === 'mensal') && dia > diaSel && dia <= rangeFim);
const ocupado = diasOcupados.has(dia.toDateString());
return (
<button key={i} type="button" disabled={passado} onClick={() => setDiaSel(dia)}
className={`relative aspect-square rounded-lg text-xs font-bold flex items-center justify-center transition-all ${passado ? 'text-gray-200 cursor-not-allowed' : sel ? 'text-white shadow' : emRange ? 'text-teal-700' : 'text-gray-600 hover:bg-gray-100'}`}
style={sel ? { backgroundColor: COLOR } : emRange ? { backgroundColor: `${COLOR}22` } : {}}>
{dia.getDate()}
{ocupado && !sel && <span className="absolute bottom-1 w-1 h-1 rounded-full bg-amber-400" />}
</button>
);
})}
</div>
</div>
{diaSel && f.modelo === 'hora' && (
<div className="grid grid-cols-2 gap-3">
<div>
<label className={labelCls}>Início</label>
<select className={inputCls} value={hora} onChange={e => setHora(e.target.value)}>
{HORAS.map(h => <option key={h} value={h}>{h}</option>)}
</select>
</div>
<div>
<label className={labelCls}>Duração</label>
<div className="flex items-center gap-2">
<button type="button" onClick={() => setQtd(q => Math.max(1, q - 1))} className="w-9 h-9 rounded-lg border border-gray-200 text-gray-500 font-black text-lg leading-none"></button>
<span className="flex-1 text-center text-sm font-black text-gray-800">{qtd}h</span>
<button type="button" onClick={() => setQtd(q => Math.min(12, q + 1))} className="w-9 h-9 rounded-lg border border-gray-200 text-gray-500 font-black text-lg leading-none">+</button>
</div>
</div>
</div>
)}
{diaSel && f.modelo === 'turno' && (
<div>
<label className={labelCls}>Turno</label>
<div className="grid grid-cols-3 gap-2">
{Object.entries(TURNOS).map(([k, t]) => (
<button key={k} type="button" onClick={() => setTurno(k)}
className={`py-2 rounded-xl text-[11px] font-black uppercase border transition-all ${turno === k ? 'text-white border-transparent' : 'bg-gray-50 text-gray-500 border-gray-200 hover:bg-gray-100'}`}
style={turno === k ? { backgroundColor: COLOR } : {}}>
{t.label}<div className="text-[8px] font-bold opacity-70">{pad2(t.ini)}h{pad2(t.fim)}h</div>
</button>
))}
</div>
</div>
)}
{diaSel && ocupadasDoDia.length > 0 && (
<div className="p-3 rounded-xl bg-amber-50 border border-amber-100">
<p className="text-[9px] font-black text-amber-600 uppercase tracking-widest mb-1.5 flex items-center gap-1"><Clock size={11} /> ocupado neste dia</p>
<div className="space-y-1 max-h-24 overflow-y-auto">
{ocupadasDoDia.map(o => (
<p key={o.id} className="text-[10px] font-bold text-amber-700">
{new Date(o.inicio).toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' })}{new Date(o.fim).toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' })} ({o.status})
</p>
))}
</div>
</div>
)}
</div>
)}
{/* Resumo do período */}
{f.inicio && f.fim && (
<div className="rounded-2xl bg-teal-50 border border-teal-100 p-3.5 flex items-center gap-3">
<CalendarClock size={18} className="text-teal-600 shrink-0" />
<div className="text-xs font-bold text-teal-800 leading-snug">
{fmtData(new Date(f.inicio).toISOString())} <span className="text-teal-400"></span> {fmtData(new Date(f.fim).toISOString())}
</div>
</div>
)}
<div>
<label className={labelCls}>Observações</label>
<textarea className={inputCls} rows={2} value={f.observacoes} onChange={e => setF(p => ({ ...p, observacoes: e.target.value }))} />
</div>
{diaSel && ocupadasDoDia.length > 0 && (
<div className="p-3 rounded-xl bg-amber-50 border border-amber-100">
<p className="text-[9px] font-black text-amber-600 uppercase tracking-widest mb-1.5 flex items-center gap-1"><Clock size={11} /> ocupado neste dia</p>
<div className="space-y-1 max-h-28 overflow-y-auto">
{ocupadasDoDia.map(o => (
<p key={o.id} className="text-[10px] font-bold text-amber-700">
{new Date(o.inicio).toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' })}{new Date(o.fim).toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' })} ({o.status})
</p>
{/* PASSO 3 — Resumo detalhado + históricos */}
<div className="w-full shrink-0 overflow-y-auto p-6 space-y-4">
<div className="rounded-2xl border border-gray-100 overflow-hidden">
{([
['Sala', sala.nome],
['Local', [sala.cidade, sala.estado].filter(Boolean).join(' · ') || '—'],
['Modelo', modeloLabel],
['Início', f.inicio ? fmtData(new Date(f.inicio).toISOString()) : '—'],
['Fim', f.fim ? fmtData(new Date(f.fim).toISOString()) : '—'],
['Reservar como', comoUnidade ? (ws?.nome || 'Unidade') : 'Pessoal'],
] as Array<[string, string]>).map(([k, v]) => (
<div key={k} className="flex items-center justify-between px-4 py-2.5 border-b border-gray-50 last:border-0">
<span className="text-[10px] font-black text-gray-400 uppercase tracking-widest">{k}</span>
<span className="text-xs font-bold text-gray-800 text-right max-w-[62%] truncate">{v}</span>
</div>
))}
<div className="flex items-center justify-between px-4 py-3 bg-teal-50/60">
<span className="text-[10px] font-black text-teal-600 uppercase tracking-widest">Total estimado</span>
<span className="text-base font-black text-teal-700">{total > 0 ? fmtValor(String(total)) : 'Sob consulta'}</span>
</div>
</div>
<div>
<p className="text-[10px] font-black text-gray-400 uppercase tracking-widest mb-2 flex items-center gap-1"><Clock size={11} /> Histórico de reservas desta sala</p>
{ocupadas.length === 0 ? (
<p className="text-[11px] text-gray-300 font-bold uppercase py-2">Sem reservas registradas</p>
) : (
<div className="space-y-1.5 max-h-44 overflow-y-auto">
{ocupadas.slice().sort((a, b) => new Date(b.inicio).getTime() - new Date(a.inicio).getTime()).map(o => (
<div key={o.id} className="flex items-center justify-between text-[11px] bg-gray-50 rounded-lg px-3 py-1.5">
<span className="font-bold text-gray-600">{fmtData(o.inicio)} {new Date(o.fim).toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' })}</span>
<span className={`text-[9px] font-black uppercase px-1.5 py-0.5 rounded-full ${o.status === 'confirmada' ? 'bg-emerald-100 text-emerald-700' : o.status === 'pendente' ? 'bg-amber-100 text-amber-700' : 'bg-gray-200 text-gray-500'}`}>{o.status}</span>
</div>
))}
</div>
)}
</div>
</div>
)}
{/* PASSO 4 — Pagamento */}
<div className="w-full shrink-0 overflow-y-auto p-6 space-y-4">
<div className="rounded-2xl bg-gradient-to-br from-teal-50 to-white border border-teal-100 p-5 text-center">
<p className="text-[10px] font-black text-teal-600 uppercase tracking-widest">Total{modeloLabel ? ` · ${modeloLabel}` : ''}</p>
<p className="text-3xl font-black text-teal-700 mt-1">{total > 0 ? fmtValor(String(total)) : 'Sob consulta'}</p>
{f.modelo === 'hora' && total > 0 && <p className="text-[10px] text-teal-500 font-bold mt-1">{qtd}h × {fmtValor(String(Number(sala.valor_hora)))}</p>}
</div>
<div>
<label className={labelCls}>Forma de pagamento</label>
<div className="grid grid-cols-2 gap-2 mt-1">
{Object.entries(FORMAS).map(([k, l]) => (
<button key={k} type="button" onClick={() => setFormaPagamento(k)}
className={`py-2.5 rounded-xl text-[11px] font-black uppercase border transition-all ${formaPagamento === k ? 'text-white border-transparent' : 'bg-gray-50 text-gray-500 border-gray-200 hover:bg-gray-100'}`}
style={formaPagamento === k ? { backgroundColor: COLOR } : {}}>{l}</button>
))}
</div>
</div>
<div className="rounded-xl bg-gray-50 border border-gray-100 p-3.5 text-[11px] text-gray-500 leading-relaxed">
<span className="font-black text-gray-600 uppercase">Como funciona: </span>ao solicitar, o locador recebe seu pedido e confirma. O pagamento é combinado diretamente com ele após a confirmação sua preferência ({FORMAS[formaPagamento]}) vai junto no pedido.
</div>
</div>
</div>
</div>
<div className="px-6 pb-6 flex gap-3">
<button onClick={onClose} className="flex-1 py-3 border border-gray-200 rounded-xl text-xs font-black uppercase text-gray-500 hover:bg-gray-50 transition-colors">CANCELAR</button>
<button onClick={handleReserve} disabled={saving} className="flex-1 py-3 text-white rounded-xl text-xs font-black uppercase transition-colors flex items-center justify-center gap-2 disabled:opacity-60" style={{ backgroundColor: COLOR }}>
{saving ? <RefreshCw size={14} className="animate-spin" /> : <CalendarClock size={14} />} SOLICITAR RESERVA
{/* Footer nav */}
<div className="px-6 py-4 border-t border-gray-100 flex gap-3 shrink-0">
<button onClick={() => (step === 0 ? onClose() : setStep(s => s - 1))} className="flex-1 py-3 border border-gray-200 rounded-xl text-xs font-black uppercase text-gray-500 hover:bg-gray-50 transition-colors">
{step === 0 ? 'Cancelar' : 'Voltar'}
</button>
{step < 3 ? (
<button onClick={() => setStep(s => s + 1)} disabled={step === 1 && !f.inicio}
className="flex-1 py-3 text-white rounded-xl text-xs font-black uppercase transition-opacity hover:opacity-90 disabled:opacity-40 flex items-center justify-center gap-2" style={{ backgroundColor: COLOR }}>
Próximo <ChevronRight size={14} />
</button>
) : (
<button onClick={handleReserve} disabled={saving || !f.inicio}
className="flex-1 py-3 text-white rounded-xl text-xs font-black uppercase transition-opacity hover:opacity-90 disabled:opacity-50 flex items-center justify-center gap-2" style={{ backgroundColor: COLOR }}>
{saving ? <RefreshCw size={14} className="animate-spin" /> : <CalendarClock size={14} />} Solicitar Reserva
</button>
)}
</div>
</div>
</div>