Mobile optimise

This commit is contained in:
2025-10-13 23:30:45 +01:00
parent b474b8a41c
commit 8b3d08ce84
+104 -140
View File
@@ -44,6 +44,7 @@
fd.append('file', file);
try {
// Note: API call is mocked here, replace with your actual endpoint
// Mock success/fail
await new Promise((r) => setTimeout(r, 500)); // Simulate network delay
if (file.name.toLowerCase().includes('fail')) {
@@ -142,7 +143,6 @@
let editingException = $state<ExceptionGroup | null>(null);
let editingExceptionIndex = $state<number | null>(null);
let showExceptionModal = $state(false);
let showUploadModal = $state(false); // Kept in case you want to toggle visibility
const dayNames = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
@@ -463,34 +463,34 @@
</Card.Content>
</Card.Root>
<!-- Default Hours Card -->
<Card.Root>
<Card.Header>
<Card.Title>Working Hours</Card.Title>
<Card.Description>
View default hours for each weekday, and manage exceptions.
</Card.Description>
<Card.Description>View and manage your standard weekly schedule.</Card.Description>
</Card.Header>
<Card.Content class="space-y-4">
<div class="flex items-center justify-between">
<h3 class="font-semibold">Default Hours</h3>
<h3 class="font-semibold">Weekly Schedule</h3>
<Button onclick={prepareDefaultHoursEdit} disabled={loadingHours || savingHours}>
Edit Defaults
</Button>
</div>
<div class="overflow-x-auto">
<div class="w-full overflow-x-auto">
<table class="w-full table-auto">
<thead>
<tr class="text-left text-xs text-gray-500">
<th class="py-2">Day</th>
<th class="py-2">Status</th> <th class="py-2">Start</th>
<th class="py-2">Status</th>
<th class="py-2">Start</th>
<th class="py-2">End</th>
</tr>
</thead>
<tbody>
{#each defaultHours as row, i}
{#each defaultHours as row}
<tr class="border-t">
<td class="py-2">{weekdayLabel(row.weekday)}</td>
<td class="py-2 text-sm">{weekdayLabel(row.weekday)}</td>
<td class="py-2">
<span
class="text-sm font-medium {row.is_open ? 'text-green-600' : 'text-red-600'}"
@@ -498,34 +498,31 @@
{row.is_open ? 'Open' : 'Closed'}
</span>
</td>
<td class="py-2">
<input
class="rounded border bg-gray-100 px-2 py-1"
type="time"
value={row.start_time}
disabled
/>
<td class="py-2 text-sm">
{row.is_open ? row.start_time : '—'}
</td>
<td class="py-2">
<input
class="rounded border bg-gray-100 px-2 py-1"
type="time"
value={row.end_time}
disabled
/>
<td class="py-2 text-sm">
{row.is_open ? row.end_time : '—'}
</td>
</tr>
{/each}
</tbody>
</table>
</div>
<Separator />
</Card.Content>
</Card.Root>
<!-- Exception Groups Card -->
<Card.Root>
<Card.Header>
<Card.Title>Exception Groups</Card.Title>
<Card.Description>
Manage temporary schedules for holidays, closures, and special events.
</Card.Description>
</Card.Header>
<Card.Content class="space-y-4">
<div class="flex items-center justify-between">
<div>
<h3 class="font-semibold">Exception Groups</h3>
<p class="text-sm text-gray-500">Temporary schedules such as 'Christmas' or 'Holiday'</p>
</div>
<Button variant="outline" onclick={createNewException}>New Exception Group</Button>
</div>
@@ -572,66 +569,67 @@
<Card.Root>
<Card.Header>
<Card.Title>Users & Bookings</Card.Title>
<Card.Description>
Search users or bookings. Click an item to view details and history.
</Card.Description>
<Card.Title>Users</Card.Title>
<Card.Description>Search and manage user details.</Card.Description>
</Card.Header>
<Card.Content class="space-y-4">
<div class="grid gap-4 md:grid-cols-2">
<div>
<Label>Search Users</Label>
<div class="flex gap-2">
<Input
placeholder="Name, email or phone"
bind:value={userQuery}
onkeyup={(e) => {
if ((e as KeyboardEvent).key === 'Enter') searchUsers();
}}
/>
<Button onclick={searchUsers} disabled={loadingSearch}>Search</Button>
</div>
<div class="mt-3 max-h-60 space-y-2 overflow-y-auto">
{#each users as u}
<div class="flex items-center justify-between rounded bg-gray-50 p-2">
<div>
<div class="font-medium">{u.fn || `${u.n_first_name} ${u.n_last_name}`}</div>
<div class="text-xs text-gray-500">{u.email} • {u.phone}</div>
</div>
<Button variant="outline" onclick={() => openUserModal(u.id)}>View</Button>
</div>
{/each}
</div>
<div>
<div class="flex gap-2">
<Input
placeholder="Name, email or phone"
bind:value={userQuery}
onkeyup={(e) => {
if ((e as KeyboardEvent).key === 'Enter') searchUsers();
}}
/>
<Button onclick={searchUsers} disabled={loadingSearch}>Search</Button>
</div>
<div>
<Label>Search Bookings</Label>
<div class="flex gap-2">
<Input
placeholder="Booking ID or user"
bind:value={bookingQuery}
onkeyup={(e) => {
if ((e as KeyboardEvent).key === 'Enter') searchBookings();
}}
/>
<Button onclick={searchBookings} disabled={loadingSearch}>Search</Button>
</div>
<div class="mt-3 max-h-60 space-y-2 overflow-y-auto">
{#each bookings as b}
<div class="flex items-center justify-between rounded bg-gray-50 p-2">
<div>
<div class="font-medium">{new Date(b.start_time).toLocaleString()}</div>
<div class="text-xs text-gray-500">
{b.status} • {b.services?.map((s) => s.name).join(', ')}
</div>
</div>
<Button variant="outline" onclick={() => openBookingModal(b.id)}>View</Button>
<div class="mt-3 max-h-60 space-y-2 overflow-y-auto">
{#each users as u}
<div class="flex items-center justify-between rounded bg-gray-50 p-2">
<div>
<div class="font-medium">{u.fn || `${u.n_first_name} ${u.n_last_name}`}</div>
<div class="text-xs text-gray-500">{u.email} • {u.phone}</div>
</div>
{/each}
</div>
<Button variant="outline" onclick={() => openUserModal(u.id)}>View</Button>
</div>
{/each}
</div>
</div>
</Card.Content>
</Card.Root>
<Card.Root>
<Card.Header>
<Card.Title>Bookings</Card.Title>
<Card.Description>Search and manage booking history.</Card.Description>
</Card.Header>
<Card.Content class="space-y-4">
<div>
<div class="flex gap-2">
<Input
placeholder="Booking ID or user"
bind:value={bookingQuery}
onkeyup={(e) => {
if ((e as KeyboardEvent).key === 'Enter') searchBookings();
}}
/>
<Button onclick={searchBookings} disabled={loadingSearch}>Search</Button>
</div>
<div class="mt-3 max-h-60 space-y-2 overflow-y-auto">
{#each bookings as b}
<div class="flex items-center justify-between rounded bg-gray-50 p-2">
<div>
<div class="font-medium">{new Date(b.start_time).toLocaleString()}</div>
<div class="text-xs text-gray-500">
{b.status} • {b.services?.map((s) => s.name).join(', ')}
</div>
</div>
<Button variant="outline" onclick={() => openBookingModal(b.id)}>View</Button>
</div>
{/each}
</div>
</div>
</Card.Content>
@@ -639,7 +637,7 @@
</div>
<Modal.Root bind:open={showDefaultHoursModal}>
<Modal.Content>
<Modal.Content class="max-h-[90vh] max-w-sm overflow-y-auto md:max-w-lg">
<Modal.Header>
<Modal.Title class="text-lg font-semibold">Edit Default Working Hours</Modal.Title>
<Modal.Description>
@@ -648,7 +646,7 @@
</Modal.Header>
<div class="px-4 pb-4">
<div class="overflow-x-auto">
<div class="w-full overflow-x-auto">
<table class="w-full table-auto">
<thead>
<tr class="text-left text-xs text-gray-500">
@@ -661,7 +659,7 @@
<tbody>
{#each defaultHoursDraft as row}
<tr class="border-t">
<td class="py-2">{weekdayLabel(row.weekday)}</td>
<td class="py-2 text-sm">{weekdayLabel(row.weekday)}</td>
<td class="py-2">
<input
type="checkbox"
@@ -674,7 +672,7 @@
type="time"
bind:value={row.start_time}
disabled={!row.is_open}
class="max-w-24"
class="max-w-[70px] text-sm"
/>
</td>
<td class="py-2">
@@ -682,7 +680,7 @@
type="time"
bind:value={row.end_time}
disabled={!row.is_open}
class="max-w-24"
class="max-w-[70px] text-sm"
/>
</td>
</tr>
@@ -709,15 +707,15 @@
</Modal.Root>
<Modal.Root bind:open={showExceptionModal}>
<Modal.Content>
<Modal.Header>
<Modal.Content class="max-h-[90vh] max-w-sm overflow-y-auto p-4 md:max-w-lg">
<Modal.Header class="mb-4 p-0">
<Modal.Title class="text-lg font-semibold">
{editingExceptionIndex === null ? 'New Exception Group' : 'Edit Exception Group'}
</Modal.Title>
</Modal.Header>
{#if editingException}
<div class="space-y-4 px-4 pb-4">
<div class="space-y-4 pb-4">
<div>
<Label>Name</Label>
<Input bind:value={editingException.name} placeholder="e.g. Christmas" />
@@ -727,16 +725,17 @@
<Textarea
rows={3}
bind:value={editingException.description}
placeholder="A short description of the exception"
placeholder="e.g. Extended weekend hours and shortened weekday hours for the holiday period"
/>
</div>
<div>
<Label>Week range to apply</Label>
<div class="flex gap-2">
<Input type="date" id="exception_from" />
<Input type="date" id="exception_to" />
<div class="flex flex-wrap gap-2">
<Input type="date" id="exception_from" class="min-w-[120px] flex-1" />
<Input type="date" id="exception_to" class="min-w-[120px] flex-1" />
<Button
class="w-full sm:w-auto"
onclick={() => {
const fromEl = document.getElementById('exception_from') as HTMLInputElement;
const toEl = document.getElementById('exception_to') as HTMLInputElement;
@@ -792,9 +791,14 @@
type="time"
bind:value={r.start_time}
disabled={!r.is_open}
class="max-w-24"
class="max-w-[70px] text-sm"
/>
<Input
type="time"
bind:value={r.end_time}
disabled={!r.is_open}
class="max-w-[70px] text-sm"
/>
<Input type="time" bind:value={r.end_time} disabled={!r.is_open} class="max-w-24" />
</div>
{/each}
</div>
@@ -802,7 +806,7 @@
</div>
{/if}
<Modal.Footer class="flex items-center justify-end gap-2">
<Modal.Footer class="flex items-center justify-end gap-2 p-0 pt-4">
<Button
variant="outline"
onclick={() => {
@@ -819,49 +823,9 @@
</Modal.Content>
</Modal.Root>
<Modal.Root bind:open={showUploadModal}>
<Modal.Content>
<Modal.Header>
<Modal.Title class="text-lg font-semibold">Upload Images</Modal.Title>
</Modal.Header>
<div class="px-4 pb-4">
<FileDropZone onfiles={handleFilesDropped} accept="image/*" multiple>
<div class="p-6 text-center">
<p class="text-sm text-gray-500">Drop files here, or click to open the file picker</p>
</div>
</FileDropZone>
<div class="mt-4">
<div class="text-sm text-gray-600">Selected files</div>
<div class="mt-2 space-y-2">
{#each uploadFiles as f}
<div class="flex items-center justify-between rounded bg-gray-50 p-2">
<div>{f.name} • {Math.round(f.size / 1024)}KB</div>
<button
class="text-red-500"
onclick={() => (uploadFiles = uploadFiles.filter((x) => x !== f))}
>
Remove
</button>
</div>
{/each}
</div>
</div>
</div>
<Modal.Footer class="flex items-center justify-end gap-2">
<Button variant="outline" onclick={() => (showUploadModal = false)}>Close</Button>
<Button onclick={uploadOneOrMany} disabled={!uploadFiles.length || uploading}>
{uploading ? `Uploading (${uploadProgress}%)` : 'Upload'}
</Button>
</Modal.Footer>
</Modal.Content>
</Modal.Root>
{#if selectedUser}
<Modal.Root bind:open={showUserModal}>
<Modal.Content>
<Modal.Content class="max-h-[90vh] max-w-sm overflow-y-auto md:max-w-lg">
<Modal.Header>
<Modal.Title class="text-lg font-semibold">
User: {selectedUser.fn || `${selectedUser.n_first_name} ${selectedUser.n_last_name}`}
@@ -936,7 +900,7 @@
{#if selectedBooking}
<Modal.Root bind:open={showBookingModal}>
<Modal.Content>
<Modal.Content class="max-h-[90vh] max-w-sm overflow-y-auto md:max-w-lg">
<Modal.Header>
<Modal.Title class="text-lg font-semibold">Booking {selectedBooking.id}</Modal.Title>
</Modal.Header>