mirror of
https://github.com/Dvorinka/Bookra.git
synced 2026-10-28 07:41:16 +00:00
A waitlisted customer saw only a bare status — worse, the badge fell back to "Pending" for both waitlisted and cancelled states. The view now carries a 1-based waitlistPosition (earlier-created waitlisted bookings on the session + 1) and the page renders a warning-tone notice with the queue spot and auto-promotion explanation, in CS/EN. Also fixed the badge's nonexistent --danger-soft/--warning-soft tokens. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
483 lines
22 KiB
TypeScript
483 lines
22 KiB
TypeScript
import { useParams, useSearchParams } from "@solidjs/router";
|
|
import { createSignal, createResource, Show } from "solid-js";
|
|
import { apiClient } from "../lib/api-client";
|
|
import { useI18n } from "../providers/i18n-provider";
|
|
import { BookraCharacter } from "../components/bookra-character";
|
|
import { Button, Card, CardHeader, CardTitle, CardDescription, CardContent, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Input, Textarea } from "../components/ui";
|
|
|
|
export function BookingManageRoute() {
|
|
const params = useParams();
|
|
const [searchParams] = useSearchParams();
|
|
const i18n = useI18n();
|
|
const [isRescheduling, setIsRescheduling] = createSignal(false);
|
|
const [isCancelling, setIsCancelling] = createSignal(false);
|
|
const [newDate, setNewDate] = createSignal("");
|
|
const [newTime, setNewTime] = createSignal("");
|
|
const [message, setMessage] = createSignal("");
|
|
const [actionSuccess, setActionSuccess] = createSignal<string | null>(null);
|
|
const [actionError, setActionError] = createSignal<string | null>(null);
|
|
|
|
const token = () => searchParams.token || "";
|
|
const reference = () => params.reference || "";
|
|
|
|
const [booking] = createResource(token, async (t) => {
|
|
if (!t) return null;
|
|
const ref = reference();
|
|
if (!ref) return null;
|
|
|
|
// Fetch from real API
|
|
const response = await (apiClient as any).GET(`/v1/public/bookings/${ref}`, {
|
|
params: { query: { token: t } }
|
|
});
|
|
|
|
if (response.error) {
|
|
return null;
|
|
}
|
|
|
|
return response.data ?? {
|
|
reference: ref || "BK-001",
|
|
customerName: "Alice Johnson",
|
|
customerEmail: "[email protected]",
|
|
service: "Yoga Flow Class",
|
|
businessName: "Serenity Wellness Studio",
|
|
businessEmail: "[email protected]",
|
|
startsAt: new Date(Date.now() + 86400000).toISOString(),
|
|
endsAt: new Date(Date.now() + 86400000 + 3600000).toISOString(),
|
|
location: "Main Studio, 123 Wellness Street",
|
|
status: "confirmed",
|
|
notes: "",
|
|
};
|
|
});
|
|
|
|
const handleReschedule = async () => {
|
|
setActionSuccess(null);
|
|
setActionError(null);
|
|
|
|
if (!newDate() || !newTime()) {
|
|
setActionError(i18n.locale() === 'cs' ? 'Vyberte prosím nové datum a čas' : 'Please select a new date and time');
|
|
return;
|
|
}
|
|
|
|
const ref = reference();
|
|
const t = token();
|
|
if (!ref || !t) {
|
|
setActionError(i18n.locale() === 'cs' ? 'Chybí referenční číslo nebo token' : 'Missing reference or token');
|
|
return;
|
|
}
|
|
|
|
// Calculate new start and end times
|
|
const newStart = new Date(`${newDate()}T${newTime()}`);
|
|
const newEnd = new Date(newStart.getTime() + 60 * 60 * 1000); // Default 1 hour duration
|
|
|
|
try {
|
|
const response = await (apiClient as any).POST(`/v1/public/bookings/${ref}/reschedule`, {
|
|
params: { query: { token: t } },
|
|
body: {
|
|
newStartsAt: newStart.toISOString(),
|
|
newEndsAt: newEnd.toISOString(),
|
|
reason: message()
|
|
}
|
|
});
|
|
|
|
if (response.error) {
|
|
setActionError(i18n.locale() === 'cs' ? 'Změna termínu selhala' : 'Reschedule failed');
|
|
return;
|
|
}
|
|
|
|
setActionSuccess(i18n.locale() === 'cs' ? 'Rezervace byla přeplánována' : 'Booking rescheduled successfully');
|
|
setIsRescheduling(false);
|
|
setNewDate("");
|
|
setNewTime("");
|
|
setMessage("");
|
|
} catch (err) {
|
|
setActionError(i18n.locale() === 'cs' ? 'Změna termínu selhala' : 'Reschedule failed');
|
|
}
|
|
};
|
|
|
|
const handleCancel = async () => {
|
|
setActionSuccess(null);
|
|
setActionError(null);
|
|
|
|
const ref = reference();
|
|
const t = token();
|
|
if (!ref || !t) {
|
|
setActionError(i18n.locale() === 'cs' ? 'Chybí referenční číslo nebo token' : 'Missing reference or token');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await (apiClient as any).POST(`/v1/public/bookings/${ref}/cancel`, {
|
|
params: { query: { token: t } },
|
|
body: { reason: message() }
|
|
});
|
|
|
|
if (response.error) {
|
|
setActionError(i18n.locale() === 'cs' ? 'Zrušení rezervace selhalo' : 'Cancellation failed');
|
|
return;
|
|
}
|
|
|
|
setActionSuccess(i18n.locale() === 'cs' ? 'Rezervace byla zrušena' : 'Booking cancelled successfully');
|
|
setIsCancelling(false);
|
|
setMessage("");
|
|
} catch (err) {
|
|
setActionError(i18n.locale() === 'cs' ? 'Zrušení rezervace selhalo' : 'Cancellation failed');
|
|
}
|
|
};
|
|
|
|
const formatDate = (dateString: string) => {
|
|
const date = new Date(dateString);
|
|
return new Intl.DateTimeFormat(i18n.locale() === 'cs' ? 'cs-CZ' : 'en-US', {
|
|
weekday: 'long',
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric',
|
|
}).format(date);
|
|
};
|
|
|
|
const formatTime = (dateString: string) => {
|
|
const date = new Date(dateString);
|
|
return new Intl.DateTimeFormat(i18n.locale() === 'cs' ? 'cs-CZ' : 'en-US', {
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
}).format(date);
|
|
};
|
|
|
|
return (
|
|
<section class="section-container py-12 min-h-screen">
|
|
{/* Header */}
|
|
<div class="max-w-2xl mx-auto text-center mb-10">
|
|
<BookraCharacter pose="hello" size="lg" animate={true} class="mx-auto mb-6" />
|
|
<h1 class="text-display-lg font-semibold text-ink mb-4">
|
|
{i18n.locale() === 'cs' ? 'Správa rezervace' : 'Manage Your Booking'}
|
|
</h1>
|
|
<p class="text-lg text-ink-muted">
|
|
{i18n.locale() === 'cs'
|
|
? 'Zde můžete zobrazit, změnit nebo zrušit svou rezervaci'
|
|
: 'View, modify or cancel your booking here'}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Success/Error Messages */}
|
|
<Show when={actionSuccess()}>
|
|
<div class="max-w-2xl mx-auto mb-6 p-4 rounded-xl bg-[hsl(var(--success-soft))] border border-[hsl(var(--success))/20] text-[hsl(var(--success))] animate-fade-in">
|
|
<div class="flex items-center gap-3">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
|
|
<polyline points="22 4 12 14.01 9 11.01"/>
|
|
</svg>
|
|
<span class="font-medium">{actionSuccess()}</span>
|
|
</div>
|
|
</div>
|
|
</Show>
|
|
|
|
<Show when={actionError()}>
|
|
<div class="max-w-2xl mx-auto mb-6 p-4 rounded-xl bg-[hsl(var(--error-soft))] border border-[hsl(var(--error))/20] text-[hsl(var(--error))] animate-fade-in">
|
|
<div class="flex items-center gap-3">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<circle cx="12" cy="12" r="10"/>
|
|
<line x1="12" x2="12" y1="8" y2="12"/>
|
|
<line x1="12" x2="12.01" y1="16" y2="16"/>
|
|
</svg>
|
|
<span class="font-medium">{actionError()}</span>
|
|
</div>
|
|
</div>
|
|
</Show>
|
|
|
|
{/* Booking Details Card */}
|
|
<Show when={booking()}>
|
|
{(b) => (
|
|
<div class="max-w-2xl mx-auto space-y-6">
|
|
<Card class="surface-elevated">
|
|
<CardHeader>
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<CardTitle class="font-display text-2xl">{b().service}</CardTitle>
|
|
<CardDescription class="mt-1">{b().businessName}</CardDescription>
|
|
</div>
|
|
<span class={`inline-flex items-center px-3 py-1 rounded-full text-sm font-semibold ${
|
|
b().status === 'confirmed'
|
|
? 'bg-[hsl(var(--success-soft))] text-[hsl(var(--success))]'
|
|
: b().status === 'cancelled'
|
|
? 'bg-[hsl(var(--error-subtle))] text-[hsl(var(--error))]'
|
|
: 'bg-[hsl(var(--warning-subtle))] text-[hsl(var(--warning))]'
|
|
}`}>
|
|
{b().status === 'confirmed'
|
|
? (i18n.locale() === 'cs' ? 'Potvrzeno' : 'Confirmed')
|
|
: b().status === 'waitlisted'
|
|
? (i18n.locale() === 'cs' ? 'Čekací listina' : 'Waitlisted')
|
|
: b().status === 'cancelled'
|
|
? (i18n.locale() === 'cs' ? 'Zrušeno' : 'Cancelled')
|
|
: (i18n.locale() === 'cs' ? 'Čeká se' : 'Pending')
|
|
}
|
|
</span>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent class="space-y-6">
|
|
{/* Waitlist position */}
|
|
<Show when={b().status === 'waitlisted'}>
|
|
<div class="flex items-center gap-3 p-4 bg-[hsl(var(--warning-subtle))] rounded-xl border border-[hsl(var(--warning))]/20">
|
|
<div class="w-10 h-10 rounded-lg bg-[hsl(var(--warning))]/10 flex items-center justify-center text-[hsl(var(--warning))]">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<circle cx="12" cy="12" r="10"/>
|
|
<polyline points="12 6 12 12 16 14"/>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<p class="font-medium text-ink">
|
|
{b().waitlistPosition > 0
|
|
? (i18n.locale() === 'cs' ? `Jste ${b().waitlistPosition}. v pořadí` : `You are #${b().waitlistPosition} in the queue`)
|
|
: (i18n.locale() === 'cs' ? 'Jste na čekací listině' : 'You are on the waitlist')}
|
|
</p>
|
|
<p class="text-sm text-ink-muted">
|
|
{i18n.locale() === 'cs'
|
|
? 'Po uvolnění místa vám rezervaci automaticky potvrdíme e-mailem.'
|
|
: 'When a seat frees up we will confirm your booking by email automatically.'}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</Show>
|
|
|
|
{/* Reference */}
|
|
<div class="flex items-center gap-3 p-3 bg-canvas-subtle rounded-xl">
|
|
<div class="w-10 h-10 rounded-lg bg-[hsl(var(--info-subtle))] flex items-center justify-center text-[hsl(var(--info))]">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<path d="M4 7V4h16v3"/>
|
|
<path d="M9 20h6"/>
|
|
<path d="M12 4v16"/>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<p class="text-sm text-ink-muted">{i18n.locale() === 'cs' ? 'Referenční číslo' : 'Reference'}</p>
|
|
<p class="font-mono font-medium text-ink">{b().reference}</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Date & Time */}
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<div class="flex items-center gap-3 p-4 bg-canvas-subtle rounded-xl">
|
|
<div class="w-10 h-10 rounded-lg bg-accent-subtle flex items-center justify-center text-accent">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"/>
|
|
<line x1="16" y1="2" x2="16" y2="6"/>
|
|
<line x1="8" y1="2" x2="8" y2="6"/>
|
|
<line x1="3" y1="10" x2="21" y2="10"/>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<p class="text-sm text-ink-muted">{i18n.locale() === 'cs' ? 'Datum' : 'Date'}</p>
|
|
<p class="font-medium text-ink">{formatDate(b().startsAt)}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-3 p-4 bg-canvas-subtle rounded-xl">
|
|
<div class="w-10 h-10 rounded-lg bg-accent-subtle flex items-center justify-center text-accent">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<circle cx="12" cy="12" r="10"/>
|
|
<polyline points="12 6 12 12 16 14"/>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<p class="text-sm text-ink-muted">{i18n.locale() === 'cs' ? 'Čas' : 'Time'}</p>
|
|
<p class="font-medium text-ink">{formatTime(b().startsAt)} - {formatTime(b().endsAt)}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Location */}
|
|
<div class="flex items-center gap-3 p-4 bg-canvas-subtle rounded-xl">
|
|
<div class="w-10 h-10 rounded-lg bg-[hsl(var(--info-subtle))] flex items-center justify-center text-[hsl(var(--info))]">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/>
|
|
<circle cx="12" cy="10" r="3"/>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<p class="text-sm text-ink-muted">{i18n.locale() === 'cs' ? 'Místo' : 'Location'}</p>
|
|
<p class="font-medium text-ink">{b().location}</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Customer Info */}
|
|
<div class="p-4 bg-canvas-subtle rounded-xl">
|
|
<p class="text-sm text-ink-muted mb-3">{i18n.locale() === 'cs' ? 'Vaše údaje' : 'Your Details'}</p>
|
|
<div class="space-y-2">
|
|
<p class="font-medium text-ink">{b().customerName}</p>
|
|
<p class="text-sm text-ink-muted">{b().customerEmail}</p>
|
|
</div>
|
|
<Show when={b().notes}>
|
|
<div class="mt-3 pt-3 border-t border-border">
|
|
<p class="text-sm text-ink-muted">{i18n.locale() === 'cs' ? 'Poznámky' : 'Notes'}</p>
|
|
<p class="text-sm text-ink mt-1">{b().notes}</p>
|
|
</div>
|
|
</Show>
|
|
</div>
|
|
|
|
{/* Action Buttons */}
|
|
<div class="flex flex-col sm:flex-row gap-3 pt-4">
|
|
<Button
|
|
variant="secondary"
|
|
class="flex-1"
|
|
onClick={() => setIsRescheduling(true)}
|
|
>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="mr-2">
|
|
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"/>
|
|
<line x1="16" y1="2" x2="16" y2="6"/>
|
|
<line x1="8" y1="2" x2="8" y2="6"/>
|
|
<line x1="3" y1="10" x2="21" y2="10"/>
|
|
</svg>
|
|
{i18n.locale() === 'cs' ? 'Změnit termín' : 'Reschedule'}
|
|
</Button>
|
|
<Button
|
|
variant="secondary"
|
|
class="flex-1 text-[hsl(var(--error))] hover:bg-[hsl(var(--error-soft))]"
|
|
onClick={() => setIsCancelling(true)}
|
|
>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="mr-2">
|
|
<circle cx="12" cy="12" r="10"/>
|
|
<line x1="15" y1="9" x2="9" y2="15"/>
|
|
<line x1="9" y1="9" x2="15" y2="15"/>
|
|
</svg>
|
|
{i18n.locale() === 'cs' ? 'Zrušit rezervaci' : 'Cancel Booking'}
|
|
</Button>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Contact Business Card */}
|
|
<Card class="surface-card">
|
|
<CardContent class="p-6">
|
|
<div class="flex items-start gap-4">
|
|
<div class="w-12 h-12 rounded-full bg-accent-subtle flex items-center justify-center text-accent shrink-0">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<h3 class="font-display font-semibold text-ink mb-1">
|
|
{i18n.locale() === 'cs' ? 'Potřebujete pomoci?' : 'Need help?'}
|
|
</h3>
|
|
<p class="text-sm text-ink-muted mb-3">
|
|
{i18n.locale() === 'cs'
|
|
? 'Máte dotazy nebo potřebujete speciální úpravy? Kontaktujte přímo podnik.'
|
|
: 'Have questions or need special arrangements? Contact the business directly.'}
|
|
</p>
|
|
<a
|
|
href={`mailto:${b().businessEmail || '[email protected]'}?subject=Booking ${b().reference}`}
|
|
class="text-accent hover:text-accent-hover font-medium text-sm inline-flex items-center gap-2"
|
|
>
|
|
{i18n.locale() === 'cs' ? 'Poslat zprávu' : 'Send message'}
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<path d="M5 12h14"/>
|
|
<path d="m12 5 7 7-7 7"/>
|
|
</svg>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
)}
|
|
</Show>
|
|
|
|
{/* Reschedule Dialog */}
|
|
<Dialog open={isRescheduling()} onClose={() => setIsRescheduling(false)}>
|
|
<DialogContent>
|
|
<DialogHeader>
|
|
<DialogTitle>
|
|
{i18n.locale() === 'cs' ? 'Změnit termín rezervace' : 'Reschedule Booking'}
|
|
</DialogTitle>
|
|
<DialogDescription>
|
|
{i18n.locale() === 'cs'
|
|
? 'Vyberte nové datum a čas pro vaši rezervaci'
|
|
: 'Select a new date and time for your booking'}
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
<div class="space-y-4 mt-4">
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-ink-muted mb-1.5">
|
|
{i18n.locale() === 'cs' ? 'Nové datum' : 'New Date'}
|
|
</label>
|
|
<Input
|
|
type="date"
|
|
value={newDate()}
|
|
onInput={(e) => setNewDate(e.currentTarget.value)}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-ink-muted mb-1.5">
|
|
{i18n.locale() === 'cs' ? 'Nový čas' : 'New Time'}
|
|
</label>
|
|
<Input
|
|
type="time"
|
|
value={newTime()}
|
|
onInput={(e) => setNewTime(e.currentTarget.value)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-ink-muted mb-1.5">
|
|
{i18n.locale() === 'cs' ? 'Zpráva pro podnik (volitelné)' : 'Message to business (optional)'}
|
|
</label>
|
|
<Textarea
|
|
rows={3}
|
|
value={message()}
|
|
onInput={(e) => setMessage(e.currentTarget.value)}
|
|
placeholder={i18n.locale() === 'cs' ? 'Důvod změny termínu...' : 'Reason for rescheduling...'}
|
|
/>
|
|
</div>
|
|
<div class="flex gap-3 pt-4">
|
|
<Button variant="secondary" class="flex-1" onClick={() => setIsRescheduling(false)}>
|
|
{i18n.locale() === 'cs' ? 'Zrušit' : 'Cancel'}
|
|
</Button>
|
|
<Button class="flex-1" onClick={handleReschedule}>
|
|
{i18n.locale() === 'cs' ? 'Potvrdit změnu' : 'Confirm Change'}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
|
|
{/* Cancel Dialog */}
|
|
<Dialog open={isCancelling()} onClose={() => setIsCancelling(false)}>
|
|
<DialogContent>
|
|
<DialogHeader>
|
|
<DialogTitle>
|
|
{i18n.locale() === 'cs' ? 'Zrušit rezervaci' : 'Cancel Booking'}
|
|
</DialogTitle>
|
|
<DialogDescription>
|
|
{i18n.locale() === 'cs'
|
|
? 'Opravdu chcete zrušit tuto rezervaci? Tuto akci nelze vrátit zpět.'
|
|
: 'Are you sure you want to cancel this booking? This action cannot be undone.'}
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
<div class="space-y-4 mt-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-ink-muted mb-1.5">
|
|
{i18n.locale() === 'cs' ? 'Důvod zrušení (volitelné)' : 'Cancellation reason (optional)'}
|
|
</label>
|
|
<Textarea
|
|
rows={3}
|
|
value={message()}
|
|
onInput={(e) => setMessage(e.currentTarget.value)}
|
|
placeholder={i18n.locale() === 'cs' ? 'Proč rušíte rezervaci...' : 'Why are you cancelling...'}
|
|
/>
|
|
</div>
|
|
<div class="flex gap-3 pt-4">
|
|
<Button variant="secondary" class="flex-1" onClick={() => setIsCancelling(false)}>
|
|
{i18n.locale() === 'cs' ? 'Nemazat' : 'Keep Booking'}
|
|
</Button>
|
|
<Button
|
|
class="flex-1 bg-[hsl(var(--error))] hover:bg-[hsl(var(--error-hover))]"
|
|
onClick={handleCancel}
|
|
>
|
|
{i18n.locale() === 'cs' ? 'Zrušit rezervaci' : 'Cancel Booking'}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</section>
|
|
);
|
|
}
|