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">
|
||||
import { SvelteDate } from 'svelte/reactivity';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import * as Modal from '$lib/components/ui/dialog';
|
||||
@@ -64,7 +65,7 @@
|
||||
|
||||
function getBookingDateTime(): string {
|
||||
if (!booking?.start_time) return '';
|
||||
const d = new Date(booking.start_time);
|
||||
const d = new SvelteDate(booking.start_time);
|
||||
return d.toLocaleDateString('en-GB', {
|
||||
weekday: 'long',
|
||||
day: 'numeric',
|
||||
@@ -147,12 +148,12 @@
|
||||
function findOldestBooking(): { id: string; isCurrent: boolean } | 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 oldestTime = currentCreatedAt;
|
||||
|
||||
for (const ob of overlappingBookings) {
|
||||
const obTime = new Date(ob.created_at).getTime();
|
||||
const obTime = new SvelteDate(ob.created_at).getTime();
|
||||
if (obTime < oldestTime) {
|
||||
oldestTime = obTime;
|
||||
oldestId = ob.id;
|
||||
@@ -402,7 +403,7 @@
|
||||
{ob.user?.full_name || 'Unknown'}
|
||||
</div>
|
||||
<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',
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
@@ -430,7 +431,7 @@
|
||||
{ob.status}
|
||||
</span>
|
||||
<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',
|
||||
month: 'short',
|
||||
hour: 'numeric',
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { SvelteDate } from 'svelte/reactivity';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { SvelteDate } from 'svelte/reactivity';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { formatDuration } from '$lib/utils/format';
|
||||
import * as Modal from '$lib/components/ui/dialog';
|
||||
import * as AlertDialog from '$lib/components/ui/alert-dialog';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
@@ -274,10 +275,10 @@
|
||||
let maxAvailableDuration = $derived.by(() => {
|
||||
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 bookingEnd = bookingStart + currentDurationMs;
|
||||
const nextStart = new Date(nextAppointmentStart).getTime();
|
||||
const nextStart = new SvelteDate(nextAppointmentStart).getTime();
|
||||
|
||||
const availableMs = nextStart - bookingEnd;
|
||||
return Math.max(0, Math.floor(availableMs / (60 * 1000)));
|
||||
@@ -474,7 +475,7 @@
|
||||
<div class="flex-1">
|
||||
<div class="font-medium">{service.service_name}</div>
|
||||
<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>
|
||||
{#if service.override_price !== undefined || service.override_duration_minutes !== undefined}
|
||||
<div class="text-xs text-amber-600">Modified</div>
|
||||
@@ -523,7 +524,7 @@
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -536,7 +537,7 @@
|
||||
Payments ({booking.payments.length})
|
||||
</h3>
|
||||
<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-1">
|
||||
<div class="font-medium">
|
||||
@@ -561,7 +562,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
<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>
|
||||
{#if payment.status === 'completed'}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { SvelteDate } from 'svelte/reactivity';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import * as Modal from '$lib/components/ui/dialog';
|
||||
@@ -51,7 +52,7 @@
|
||||
let showDenyConfirm = $state(false);
|
||||
|
||||
function formatDateLine1(dateTimeString: string): string {
|
||||
const d = new Date(dateTimeString);
|
||||
const d = new SvelteDate(dateTimeString);
|
||||
const dateStr = d.toLocaleDateString('en-GB', {
|
||||
weekday: 'long',
|
||||
day: 'numeric',
|
||||
@@ -67,7 +68,7 @@
|
||||
}
|
||||
|
||||
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 endH = Math.floor(endMinutes / 60);
|
||||
const endM = endMinutes % 60;
|
||||
@@ -248,7 +249,7 @@
|
||||
<div>
|
||||
<div class="mb-1 text-xs text-gray-500">Original</div>
|
||||
<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)}
|
||||
<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>
|
||||
@@ -278,7 +279,7 @@
|
||||
<div>
|
||||
<div class="mb-1 text-xs text-gray-500">Proposed</div>
|
||||
<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)}
|
||||
<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>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
let saving = $state(false);
|
||||
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 selectedTime = $state<string | null>(null);
|
||||
@@ -237,7 +237,7 @@
|
||||
loadingMonths = {};
|
||||
loadingMonthKeys = new Set();
|
||||
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() {
|
||||
|
||||
Reference in New Issue
Block a user