feat: forçar configurações de agendamento na pagina publica (limites de antecedência e bloqueio online)
Build and Deploy (Gitea Actions) / build-and-deploy (push) Successful in 2m41s Details

This commit is contained in:
Sidney 2026-07-19 22:06:33 -03:00
parent 944f7930d9
commit 2e22c112d3
2 changed files with 33 additions and 3 deletions

View File

@ -26,6 +26,7 @@ export default function PublicBookingPage({ params }: { params: Promise<{ slug:
const [establishment, setEstablishment] = useState({ const [establishment, setEstablishment] = useState({
name: '', type: 'barbearia', description: '', phone: '', name: '', type: 'barbearia', description: '', phone: '',
address: '', rating: 5.0, reviews: 0, address: '', rating: 5.0, reviews: 0,
allowOnlineBooking: true, bookingAdvanceMin: 60, bookingAdvanceMaxDays: 30
}); });
// Algoritmo dinâmico de fatiamento de grade // Algoritmo dinâmico de fatiamento de grade
@ -68,6 +69,9 @@ export default function PublicBookingPage({ params }: { params: Promise<{ slug:
address: fullAddress || '', address: fullAddress || '',
rating: 5.0, rating: 5.0,
reviews: 0, reviews: 0,
allowOnlineBooking: profile.allowOnlineBooking !== false,
bookingAdvanceMin: profile.bookingAdvanceMin || 60,
bookingAdvanceMaxDays: profile.bookingAdvanceMaxDays || 30,
}); });
// Gerar slots com horários do tenant // Gerar slots com horários do tenant
@ -105,7 +109,8 @@ export default function PublicBookingPage({ params }: { params: Promise<{ slug:
}, [slug]); }, [slug]);
const today = new Date(); const today = new Date();
const dates = Array.from({ length: 14 }, (_, i) => { const maxDays = establishment.bookingAdvanceMaxDays || 30;
const dates = Array.from({ length: maxDays }, (_, i) => {
const d = new Date(today); const d = new Date(today);
d.setDate(d.getDate() + i); d.setDate(d.getDate() + i);
return d; return d;
@ -195,6 +200,20 @@ export default function PublicBookingPage({ params }: { params: Promise<{ slug:
); );
} }
if (!establishment.allowOnlineBooking && !booked) {
return (
<div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'var(--bg-primary)', color: 'white' }}>
<div style={{ maxWidth: '440px', textAlign: 'center', padding: '40px', background: 'var(--bg-card)', borderRadius: 'var(--radius-lg)', border: '1px solid var(--border-color)', boxShadow: 'var(--shadow-lg)' }}>
<div style={{ width: 80, height: 80, borderRadius: '50%', background: 'var(--danger-bg)', color: 'var(--danger)', display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 24px' }}>
<Calendar size={40} />
</div>
<h2 style={{ fontSize: '24px', fontWeight: 800, marginBottom: '16px', color: 'var(--text-primary)' }}>Agendamento Indisponível</h2>
<p style={{ color: 'var(--text-secondary)' }}>O agendamento online está temporariamente desativado para este estabelecimento. Por favor, entre em contato diretamente pelo WhatsApp ou telefone.</p>
</div>
</div>
);
}
return ( return (
<div style={{ minHeight: '100vh', background: 'var(--bg-primary)' }}> <div style={{ minHeight: '100vh', background: 'var(--bg-primary)' }}>
{/* HEADER */} {/* HEADER */}
@ -334,7 +353,16 @@ export default function PublicBookingPage({ params }: { params: Promise<{ slug:
<Clock size={14} style={{ display: 'inline', marginRight: '6px' }} />Horários disponíveis <Clock size={14} style={{ display: 'inline', marginRight: '6px' }} />Horários disponíveis
</h3> </h3>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(90px, 1fr))', gap: '8px' }}> <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(90px, 1fr))', gap: '8px' }}>
{(slots.length > 0 ? slots : ['09:00', '09:30', '10:00', '10:30', '11:00', '11:30', '14:00', '14:30', '15:00', '15:30', '16:00', '16:30', '17:00', '17:30', '18:00']).map(slot => ( {(slots.length > 0 ? slots : ['09:00', '09:30', '10:00', '10:30', '11:00', '11:30', '14:00', '14:30', '15:00', '15:30', '16:00', '16:30', '17:00', '17:30', '18:00']).map(slot => {
const isToday = selectedDate === today.toISOString().split('T')[0];
const [sh, sm] = slot.split(':').map(Number);
const slotMinutes = sh * 60 + sm;
const currentMinutes = today.getHours() * 60 + today.getMinutes();
const isPastOrTooSoon = isToday && (slotMinutes < currentMinutes + (establishment.bookingAdvanceMin || 60));
if (isPastOrTooSoon) return null;
return (
<div key={slot} onClick={() => setSelectedTime(slot)} style={{ <div key={slot} onClick={() => setSelectedTime(slot)} style={{
padding: '12px', borderRadius: 'var(--radius-sm)', cursor: 'pointer', textAlign: 'center', padding: '12px', borderRadius: 'var(--radius-sm)', cursor: 'pointer', textAlign: 'center',
fontWeight: 600, fontSize: '14px', transition: 'all 0.2s', fontWeight: 600, fontSize: '14px', transition: 'all 0.2s',
@ -344,7 +372,7 @@ export default function PublicBookingPage({ params }: { params: Promise<{ slug:
}}> }}>
{slot} {slot}
</div> </div>
))} )})}
</div> </div>
</div> </div>
)} )}

View File

@ -45,6 +45,8 @@ export async function GET(req: NextRequest) {
interval: settings.interval || 30, interval: settings.interval || 30,
workingDays: settings.working_days || [1, 2, 3, 4, 5, 6], workingDays: settings.working_days || [1, 2, 3, 4, 5, 6],
allowOnlineBooking: settings.allow_online_booking !== false, allowOnlineBooking: settings.allow_online_booking !== false,
bookingAdvanceMin: settings.booking_advance_min || 60,
bookingAdvanceMaxDays: settings.booking_advance_max_days || 30,
}); });
} catch (error: any) { } catch (error: any) {
console.error('GET Profile error:', error); console.error('GET Profile error:', error);