fix: match date/time picker style, fix two-row footer layout
- Replace Modal.Footer with plain div to avoid bits-ui class merging (sm:flex-row override) - DatePicker wrapped in flex items-center justify-center matching BookingCreateModal - Time picker uses Button elements with fuchsia selection (hover:bg-fuchsia-50, bg-fuchsia-100) - Generate 30-min time slots from 08:00-18:00 matching salon hours - Reschedule form container uses bg-gray-50 matching other sections - Footer: two rows with flex-1 buttons for equal width
This commit is contained in:
@@ -146,6 +146,30 @@
|
||||
return jsDate < todayStart;
|
||||
}
|
||||
|
||||
function formatTime(time: string): string {
|
||||
const parts = time.split(':').map(Number);
|
||||
const hours = parts[0];
|
||||
const minutes = parts.length > 1 ? parts[1] : 0;
|
||||
if (hours === 12 && minutes === 0) return 'Noon';
|
||||
if (hours === 0 && minutes === 0) return 'Midnight';
|
||||
const period = hours >= 12 ? 'PM' : 'AM';
|
||||
const displayHours = hours % 12 || 12;
|
||||
return `${displayHours}:${minutes.toString().padStart(2, '0')} ${period}`;
|
||||
}
|
||||
|
||||
function generateTimeSlots(): string[] {
|
||||
const slots: string[] = [];
|
||||
for (let h = 8; h <= 18; h++) {
|
||||
for (let m = 0; m < 60; m += 30) {
|
||||
if (h === 18 && m > 0) break;
|
||||
slots.push(`${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`);
|
||||
}
|
||||
}
|
||||
return slots;
|
||||
}
|
||||
|
||||
const timeSlots = generateTimeSlots();
|
||||
|
||||
async function submitReschedule() {
|
||||
if (!selectedBooking || !rescheduleDate || !rescheduleTime) return;
|
||||
rescheduleSubmitting = true;
|
||||
@@ -427,70 +451,79 @@
|
||||
{/if}
|
||||
|
||||
{#if showRescheduleForm}
|
||||
<div class="rounded-lg border border-amber-200 bg-amber-50 p-4">
|
||||
<h3 class="mb-3 text-sm font-semibold tracking-wide text-amber-700 uppercase">
|
||||
<div class="rounded-lg border border-gray-200 bg-gray-50 p-4">
|
||||
<h3 class="mb-3 text-sm font-semibold tracking-wide text-gray-600 uppercase">
|
||||
Request Reschedule
|
||||
</h3>
|
||||
<div class="space-y-4">
|
||||
|
||||
<div class="flex items-center justify-center">
|
||||
<DatePicker
|
||||
date={rescheduleDate}
|
||||
placeholder={reschedulePlaceholder}
|
||||
minValue={minDate}
|
||||
maxValue={maxCalendarDate}
|
||||
isDateUnavailable={isDateUnavailable}
|
||||
onchange={(d) => { rescheduleDate = d; }}
|
||||
onchange={(d) => { rescheduleDate = d; rescheduleTime = ''; }}
|
||||
onPlaceholderChange={(p) => { reschedulePlaceholder = p; }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label.Root for="reschedule-time">Preferred Time *</Label.Root>
|
||||
<input
|
||||
id="reschedule-time"
|
||||
type="time"
|
||||
bind:value={rescheduleTime}
|
||||
class="flex h-9 w-full rounded-md border border-gray-300 bg-transparent px-3 py-1 text-sm shadow-sm"
|
||||
/>
|
||||
<p class="text-xs text-gray-500">We'll confirm the exact time based on availability</p>
|
||||
{#if rescheduleDate}
|
||||
<div class="no-scrollbar mt-2 flex max-h-40 w-full scroll-pb-6 flex-col gap-4 overflow-y-auto border-t pt-4">
|
||||
<div class="grid justify-center gap-2 text-sm text-gray-600">
|
||||
{rescheduleDate.toDate(getLocalTimeZone()).toLocaleDateString('en-GB', { weekday: 'long', day: 'numeric', month: 'short' })}
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
{#each timeSlots as slot (slot)}
|
||||
<Button
|
||||
variant="outline"
|
||||
onclick={() => { rescheduleTime = slot; }}
|
||||
class="w-full hover:bg-fuchsia-50 {rescheduleTime === slot ? 'bg-fuchsia-100' : ''}"
|
||||
>
|
||||
{formatTime(slot)}
|
||||
</Button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label.Root for="reschedule-notes">Reason (optional)</Label.Root>
|
||||
<Textarea.Root
|
||||
id="reschedule-notes"
|
||||
bind:value={rescheduleNotes}
|
||||
placeholder="Tell us why you need to reschedule"
|
||||
rows={2}
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-4 space-y-2">
|
||||
<Label.Root for="reschedule-notes">Reason (optional)</Label.Root>
|
||||
<Textarea.Root
|
||||
id="reschedule-notes"
|
||||
bind:value={rescheduleNotes}
|
||||
placeholder="Tell us why you need to reschedule"
|
||||
rows={2}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
onclick={submitReschedule}
|
||||
disabled={!isRescheduleValid || rescheduleSubmitting}
|
||||
>
|
||||
{rescheduleSubmitting ? 'Submitting...' : 'Submit Request'}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onclick={() => {
|
||||
showRescheduleForm = false;
|
||||
rescheduleDate = undefined;
|
||||
rescheduleTime = '';
|
||||
rescheduleNotes = '';
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
<div class="mt-4 flex gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
onclick={submitReschedule}
|
||||
disabled={!isRescheduleValid || rescheduleSubmitting}
|
||||
>
|
||||
{rescheduleSubmitting ? 'Submitting...' : 'Submit Request'}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onclick={() => {
|
||||
showRescheduleForm = false;
|
||||
rescheduleDate = undefined;
|
||||
rescheduleTime = '';
|
||||
rescheduleNotes = '';
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<Modal.Footer class="flex flex-col gap-2">
|
||||
<div class="flex flex-col gap-2 border-t px-4 py-3">
|
||||
<div class="flex gap-2">
|
||||
{#if isCancellable}
|
||||
<Button
|
||||
@@ -535,7 +568,7 @@
|
||||
{/if}
|
||||
<Button size="sm" class="flex-1" onclick={() => (open = false)}>Close</Button>
|
||||
</div>
|
||||
</Modal.Footer>
|
||||
</div>
|
||||
</Modal.Content>
|
||||
</Modal.Root>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user