refactor: admin edit and approval modals
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { SvelteDate } from 'svelte/reactivity';
|
||||||
import { authStore } from '$lib/stores/auth.svelte';
|
import { authStore } from '$lib/stores/auth.svelte';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import * as Modal from '$lib/components/ui/dialog';
|
import * as Modal from '$lib/components/ui/dialog';
|
||||||
@@ -64,7 +65,7 @@
|
|||||||
|
|
||||||
function getBookingDateTime(): string {
|
function getBookingDateTime(): string {
|
||||||
if (!booking?.start_time) return '';
|
if (!booking?.start_time) return '';
|
||||||
const d = new Date(booking.start_time);
|
const d = new SvelteDate(booking.start_time);
|
||||||
return d.toLocaleDateString('en-GB', {
|
return d.toLocaleDateString('en-GB', {
|
||||||
weekday: 'long',
|
weekday: 'long',
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
@@ -147,12 +148,12 @@
|
|||||||
function findOldestBooking(): { id: string; isCurrent: boolean } | null {
|
function findOldestBooking(): { id: string; isCurrent: boolean } | null {
|
||||||
if (overlappingBookings.length === 0) return null;
|
if (overlappingBookings.length === 0) return null;
|
||||||
|
|
||||||
const currentCreatedAt = new Date(booking.created_at).getTime();
|
const currentCreatedAt = new SvelteDate(booking.created_at).getTime();
|
||||||
let oldestId = booking.id;
|
let oldestId = booking.id;
|
||||||
let oldestTime = currentCreatedAt;
|
let oldestTime = currentCreatedAt;
|
||||||
|
|
||||||
for (const ob of overlappingBookings) {
|
for (const ob of overlappingBookings) {
|
||||||
const obTime = new Date(ob.created_at).getTime();
|
const obTime = new SvelteDate(ob.created_at).getTime();
|
||||||
if (obTime < oldestTime) {
|
if (obTime < oldestTime) {
|
||||||
oldestTime = obTime;
|
oldestTime = obTime;
|
||||||
oldestId = ob.id;
|
oldestId = ob.id;
|
||||||
@@ -402,7 +403,7 @@
|
|||||||
{ob.user?.full_name || 'Unknown'}
|
{ob.user?.full_name || 'Unknown'}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs text-gray-500">
|
<div class="text-xs text-gray-500">
|
||||||
{new Date(ob.start_time).toLocaleDateString('en-GB', {
|
{new SvelteDate(ob.start_time).toLocaleDateString('en-GB', {
|
||||||
weekday: 'short',
|
weekday: 'short',
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
month: 'short',
|
month: 'short',
|
||||||
@@ -430,7 +431,7 @@
|
|||||||
{ob.status}
|
{ob.status}
|
||||||
</span>
|
</span>
|
||||||
<div class="mt-1 text-xs text-gray-400">
|
<div class="mt-1 text-xs text-gray-400">
|
||||||
{new Date(ob.created_at).toLocaleDateString('en-GB', {
|
{new SvelteDate(ob.created_at).toLocaleDateString('en-GB', {
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
month: 'short',
|
month: 'short',
|
||||||
hour: 'numeric',
|
hour: 'numeric',
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { authStore } from '$lib/stores/auth.svelte';
|
import { authStore } from '$lib/stores/auth.svelte';
|
||||||
import { SvelteDate } from 'svelte/reactivity';
|
import { SvelteDate } from 'svelte/reactivity';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
|
import { formatDuration } from '$lib/utils/format';
|
||||||
import * as Modal from '$lib/components/ui/dialog';
|
import * as Modal from '$lib/components/ui/dialog';
|
||||||
import * as AlertDialog from '$lib/components/ui/alert-dialog';
|
import * as AlertDialog from '$lib/components/ui/alert-dialog';
|
||||||
import { Button } from '$lib/components/ui/button';
|
import { Button } from '$lib/components/ui/button';
|
||||||
@@ -274,10 +275,10 @@
|
|||||||
let maxAvailableDuration = $derived.by(() => {
|
let maxAvailableDuration = $derived.by(() => {
|
||||||
if (!booking?.start_time || !nextAppointmentStart) return null;
|
if (!booking?.start_time || !nextAppointmentStart) return null;
|
||||||
|
|
||||||
const bookingStart = new Date(booking.start_time).getTime();
|
const bookingStart = new SvelteDate(booking.start_time).getTime();
|
||||||
const currentDurationMs = totalDuration * 60 * 1000;
|
const currentDurationMs = totalDuration * 60 * 1000;
|
||||||
const bookingEnd = bookingStart + currentDurationMs;
|
const bookingEnd = bookingStart + currentDurationMs;
|
||||||
const nextStart = new Date(nextAppointmentStart).getTime();
|
const nextStart = new SvelteDate(nextAppointmentStart).getTime();
|
||||||
|
|
||||||
const availableMs = nextStart - bookingEnd;
|
const availableMs = nextStart - bookingEnd;
|
||||||
return Math.max(0, Math.floor(availableMs / (60 * 1000)));
|
return Math.max(0, Math.floor(availableMs / (60 * 1000)));
|
||||||
@@ -474,7 +475,7 @@
|
|||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<div class="font-medium">{service.service_name}</div>
|
<div class="font-medium">{service.service_name}</div>
|
||||||
<div class="mt-1 text-sm text-gray-600">
|
<div class="mt-1 text-sm text-gray-600">
|
||||||
{service.override_duration_minutes ?? service.duration_minutes} min • £{(service.override_price ?? service.price ?? 0).toFixed(2)}
|
{formatDuration(service.override_duration_minutes ?? service.duration_minutes ?? 0)} • £{(service.override_price ?? service.price ?? 0).toFixed(2)}
|
||||||
</div>
|
</div>
|
||||||
{#if service.override_price !== undefined || service.override_duration_minutes !== undefined}
|
{#if service.override_price !== undefined || service.override_duration_minutes !== undefined}
|
||||||
<div class="text-xs text-amber-600">Modified</div>
|
<div class="text-xs text-amber-600">Modified</div>
|
||||||
@@ -523,7 +524,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-3 flex items-center justify-between border-t border-gray-200 pt-3 text-sm">
|
<div class="mt-3 flex items-center justify-between border-t border-gray-200 pt-3 text-sm">
|
||||||
<span class="text-gray-600">Total: {totalDuration} min</span>
|
<span class="text-gray-600">Total: {formatDuration(totalDuration)}</span>
|
||||||
<span class="font-semibold">£{totalPrice.toFixed(2)}</span>
|
<span class="font-semibold">£{totalPrice.toFixed(2)}</span>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -536,7 +537,7 @@
|
|||||||
Payments ({booking.payments.length})
|
Payments ({booking.payments.length})
|
||||||
</h3>
|
</h3>
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
{#each booking.payments as payment (payment.id)}
|
{#each booking.payments as payment, index (index)}
|
||||||
<div class="flex items-center justify-between rounded-md border border-gray-300 bg-white p-3">
|
<div class="flex items-center justify-between rounded-md border border-gray-300 bg-white p-3">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<div class="font-medium">
|
<div class="font-medium">
|
||||||
@@ -561,7 +562,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-1 text-xs text-gray-400">
|
<div class="mt-1 text-xs text-gray-400">
|
||||||
{new Date(payment.created_at).toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' })}
|
{new SvelteDate(payment.created_at).toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' })}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if payment.status === 'completed'}
|
{#if payment.status === 'completed'}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { SvelteDate } from 'svelte/reactivity';
|
||||||
import { authStore } from '$lib/stores/auth.svelte';
|
import { authStore } from '$lib/stores/auth.svelte';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import * as Modal from '$lib/components/ui/dialog';
|
import * as Modal from '$lib/components/ui/dialog';
|
||||||
@@ -51,7 +52,7 @@
|
|||||||
let showDenyConfirm = $state(false);
|
let showDenyConfirm = $state(false);
|
||||||
|
|
||||||
function formatDateLine1(dateTimeString: string): string {
|
function formatDateLine1(dateTimeString: string): string {
|
||||||
const d = new Date(dateTimeString);
|
const d = new SvelteDate(dateTimeString);
|
||||||
const dateStr = d.toLocaleDateString('en-GB', {
|
const dateStr = d.toLocaleDateString('en-GB', {
|
||||||
weekday: 'long',
|
weekday: 'long',
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
@@ -67,7 +68,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function formatDateLine2(dateTimeString: string, durationMinutes: number): string {
|
function formatDateLine2(dateTimeString: string, durationMinutes: number): string {
|
||||||
const d = new Date(dateTimeString);
|
const d = new SvelteDate(dateTimeString);
|
||||||
const endMinutes = d.getHours() * 60 + d.getMinutes() + durationMinutes;
|
const endMinutes = d.getHours() * 60 + d.getMinutes() + durationMinutes;
|
||||||
const endH = Math.floor(endMinutes / 60);
|
const endH = Math.floor(endMinutes / 60);
|
||||||
const endM = endMinutes % 60;
|
const endM = endMinutes % 60;
|
||||||
@@ -248,7 +249,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="mb-1 text-xs text-gray-500">Original</div>
|
<div class="mb-1 text-xs text-gray-500">Original</div>
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
{#each editRequest.original.services as service}
|
{#each editRequest.original.services as service (service.id)}
|
||||||
{#if serviceDiff.removed.some((s) => s.id === service.id)}
|
{#if serviceDiff.removed.some((s) => s.id === service.id)}
|
||||||
<div class="flex items-start gap-2 rounded border border-red-200 bg-red-50 p-2">
|
<div class="flex items-start gap-2 rounded border border-red-200 bg-red-50 p-2">
|
||||||
<span class="mt-0.5 text-red-600 font-mono text-sm">−</span>
|
<span class="mt-0.5 text-red-600 font-mono text-sm">−</span>
|
||||||
@@ -278,7 +279,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="mb-1 text-xs text-gray-500">Proposed</div>
|
<div class="mb-1 text-xs text-gray-500">Proposed</div>
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
{#each editRequest.proposed.services as service}
|
{#each editRequest.proposed.services as service (service.id)}
|
||||||
{#if serviceDiff.added.some((s) => s.id === service.id)}
|
{#if serviceDiff.added.some((s) => s.id === service.id)}
|
||||||
<div class="flex items-start gap-2 rounded border border-emerald-200 bg-emerald-50 p-2">
|
<div class="flex items-start gap-2 rounded border border-emerald-200 bg-emerald-50 p-2">
|
||||||
<span class="mt-0.5 text-emerald-600 font-mono text-sm">+</span>
|
<span class="mt-0.5 text-emerald-600 font-mono text-sm">+</span>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
let saving = $state(false);
|
let saving = $state(false);
|
||||||
let placeholder = $state<CalendarDate>(
|
let placeholder = $state<CalendarDate>(
|
||||||
new CalendarDate(new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate())
|
new CalendarDate(new SvelteDate().getFullYear(), new SvelteDate().getMonth() + 1, new SvelteDate().getDate())
|
||||||
);
|
);
|
||||||
let selectedDate = $state<CalendarDate | undefined>(undefined);
|
let selectedDate = $state<CalendarDate | undefined>(undefined);
|
||||||
let selectedTime = $state<string | null>(null);
|
let selectedTime = $state<string | null>(null);
|
||||||
@@ -237,7 +237,7 @@
|
|||||||
loadingMonths = {};
|
loadingMonths = {};
|
||||||
loadingMonthKeys = new Set();
|
loadingMonthKeys = new Set();
|
||||||
rescheduleAutoSelectDone = false;
|
rescheduleAutoSelectDone = false;
|
||||||
placeholder = new CalendarDate(new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate());
|
placeholder = new CalendarDate(new SvelteDate().getFullYear(), new SvelteDate().getMonth() + 1, new SvelteDate().getDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
async function reschedule() {
|
async function reschedule() {
|
||||||
|
|||||||
Reference in New Issue
Block a user