DISABLE SSR AND TRANSITION TO SPA
This commit is contained in:
@ -3,15 +3,19 @@
|
||||
import {
|
||||
FileDropzone,
|
||||
getModalStore,
|
||||
getToastStore,
|
||||
SlideToggle,
|
||||
type ModalStore,
|
||||
type ToastStore,
|
||||
} from "@skeletonlabs/skeleton";
|
||||
import { Button, Input, Card, Dropdown } from "$lib/components";
|
||||
import type { Driver, SkeletonData } from "$lib/schema";
|
||||
import { DRIVER_HEADSHOT_HEIGHT, DRIVER_HEADSHOT_WIDTH } from "$lib/config";
|
||||
import { team_dropdown_options } from "$lib/dropdown";
|
||||
import { enhance } from "$app/forms";
|
||||
import { get_driver_headshot_template } from "$lib/database";
|
||||
import { get_error_toast } from "$lib/toast";
|
||||
import { pb } from "$lib/pocketbase";
|
||||
import { invalidateAll } from "$app/navigation";
|
||||
|
||||
interface DriverCardProps {
|
||||
/** Data passed from the page context */
|
||||
@ -31,14 +35,92 @@
|
||||
driver = meta.driver;
|
||||
}
|
||||
|
||||
const toastStore: ToastStore = getToastStore();
|
||||
|
||||
// Constants
|
||||
const labelwidth: string = "120px";
|
||||
|
||||
// Reactive state
|
||||
let required: boolean = $derived(!driver);
|
||||
let disabled: boolean = $derived(!data.admin);
|
||||
let firstname_input_value: string = $state(driver?.firstname ?? "");
|
||||
let lastname_input_value: string = $state(driver?.lastname ?? "");
|
||||
let code_input_value: string = $state(driver?.code ?? "");
|
||||
let team_select_value: string = $state(driver?.team ?? "");
|
||||
let headshot_file_value: FileList | undefined = $state();
|
||||
let active_value: boolean = $state(driver?.active ?? true);
|
||||
|
||||
// Database actions
|
||||
// TODO: Headshot compression
|
||||
const update_driver = (create?: boolean): (() => Promise<void>) => {
|
||||
const handler = async (): Promise<void> => {
|
||||
if (!firstname_input_value || firstname_input_value === "") {
|
||||
toastStore.trigger(get_error_toast("Please enter a first name!"));
|
||||
return;
|
||||
}
|
||||
if (!lastname_input_value || lastname_input_value === "") {
|
||||
toastStore.trigger(get_error_toast("Please enter a last name!"));
|
||||
return;
|
||||
}
|
||||
if (!code_input_value || code_input_value === "") {
|
||||
toastStore.trigger(get_error_toast("Please enter a driver code!"));
|
||||
return;
|
||||
}
|
||||
if (!team_select_value || team_select_value === "") {
|
||||
toastStore.trigger(get_error_toast("Please select a team!"));
|
||||
return;
|
||||
}
|
||||
|
||||
const driver_data = {
|
||||
firstname: firstname_input_value,
|
||||
lastname: lastname_input_value,
|
||||
code: code_input_value,
|
||||
team: team_select_value,
|
||||
active: active_value,
|
||||
headshot:
|
||||
headshot_file_value && headshot_file_value.length === 1
|
||||
? headshot_file_value[0]
|
||||
: undefined,
|
||||
};
|
||||
|
||||
try {
|
||||
if (create) {
|
||||
if (!headshot_file_value || headshot_file_value.length !== 1) {
|
||||
toastStore.trigger(get_error_toast("Please upload a single driver headshot!"));
|
||||
return;
|
||||
}
|
||||
await pb.collection("drivers").create(driver_data);
|
||||
} else {
|
||||
if (!driver?.id) {
|
||||
toastStore.trigger(get_error_toast("Invalid driver id!"));
|
||||
return;
|
||||
}
|
||||
await pb.collection("drivers").update(driver.id, driver_data);
|
||||
}
|
||||
invalidateAll();
|
||||
modalStore.close();
|
||||
} catch (error) {
|
||||
toastStore.trigger(get_error_toast("" + error));
|
||||
}
|
||||
};
|
||||
|
||||
return handler;
|
||||
};
|
||||
|
||||
const delete_driver = async (): Promise<void> => {
|
||||
if (!driver?.id) {
|
||||
toastStore.trigger(get_error_toast("Invalid driver id!"));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await pb.collection("drivers").delete(driver.id);
|
||||
invalidateAll();
|
||||
modalStore.close();
|
||||
} catch (error) {
|
||||
toastStore.trigger(get_error_toast("" + error));
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
{#await data.graphics then graphics}
|
||||
@ -50,97 +132,84 @@
|
||||
imgheight={DRIVER_HEADSHOT_HEIGHT}
|
||||
imgonclick={(event: Event) => modalStore.close()}
|
||||
>
|
||||
<form
|
||||
method="POST"
|
||||
enctype="multipart/form-data"
|
||||
use:enhance
|
||||
onsubmit={() => modalStore.close()}
|
||||
>
|
||||
<!-- This is also disabled, because the ID should only be -->
|
||||
<!-- "leaked" to users that are allowed to use the inputs -->
|
||||
{#if driver && !disabled}
|
||||
<input name="id" type="hidden" value={driver.id} />
|
||||
{/if}
|
||||
<div class="flex flex-col gap-2">
|
||||
<!-- Driver name input -->
|
||||
<Input
|
||||
bind:value={firstname_input_value}
|
||||
autocomplete="off"
|
||||
{labelwidth}
|
||||
{disabled}
|
||||
{required}
|
||||
>
|
||||
First Name
|
||||
</Input>
|
||||
<Input
|
||||
bind:value={lastname_input_value}
|
||||
autocomplete="off"
|
||||
{labelwidth}
|
||||
{disabled}
|
||||
{required}
|
||||
>
|
||||
Last Name
|
||||
</Input>
|
||||
<Input
|
||||
bind:value={code_input_value}
|
||||
autocomplete="off"
|
||||
minlength={3}
|
||||
maxlength={3}
|
||||
{labelwidth}
|
||||
{disabled}
|
||||
{required}
|
||||
>
|
||||
Driver Code
|
||||
</Input>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<!-- Driver name input -->
|
||||
<Input name="firstname" value={driver?.firstname ?? ""} {labelwidth} {disabled} {required}>
|
||||
First Name
|
||||
</Input>
|
||||
<Input
|
||||
name="lastname"
|
||||
value={driver?.lastname ?? ""}
|
||||
autocomplete="off"
|
||||
<!-- Driver team input -->
|
||||
{#await data.teams then teams}
|
||||
<Dropdown
|
||||
bind:value={team_select_value}
|
||||
options={team_dropdown_options(teams)}
|
||||
{labelwidth}
|
||||
{disabled}
|
||||
{required}
|
||||
>
|
||||
Last Name
|
||||
</Input>
|
||||
<Input
|
||||
name="code"
|
||||
value={driver?.code ?? ""}
|
||||
autocomplete="off"
|
||||
minlength={3}
|
||||
maxlength={3}
|
||||
{labelwidth}
|
||||
{disabled}
|
||||
{required}
|
||||
>
|
||||
Driver Code
|
||||
</Input>
|
||||
Team
|
||||
</Dropdown>
|
||||
{/await}
|
||||
|
||||
<!-- Driver team input -->
|
||||
{#await data.teams then teams}
|
||||
<Dropdown
|
||||
name="team"
|
||||
bind:value={team_select_value}
|
||||
options={team_dropdown_options(teams)}
|
||||
{labelwidth}
|
||||
<!-- Headshot upload -->
|
||||
<FileDropzone
|
||||
name="headshot"
|
||||
bind:files={headshot_file_value}
|
||||
onchange={get_image_preview_event_handler("headshot_preview")}
|
||||
{disabled}
|
||||
{required}
|
||||
>
|
||||
<svelte:fragment slot="message">
|
||||
<span class="font-bold">Upload Headshot</span>
|
||||
</svelte:fragment>
|
||||
</FileDropzone>
|
||||
|
||||
<!-- Save/Delete buttons -->
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
<div class="mr-auto">
|
||||
<SlideToggle
|
||||
name="active"
|
||||
background="bg-primary-500"
|
||||
active="bg-tertiary-500"
|
||||
bind:checked={active_value}
|
||||
{disabled}
|
||||
{required}
|
||||
>
|
||||
Team
|
||||
</Dropdown>
|
||||
{/await}
|
||||
|
||||
<!-- Headshot upload -->
|
||||
<FileDropzone
|
||||
name="headshot"
|
||||
onchange={get_image_preview_event_handler("headshot_preview")}
|
||||
{disabled}
|
||||
{required}
|
||||
>
|
||||
<svelte:fragment slot="message">
|
||||
<span class="font-bold">Upload Headshot</span>
|
||||
</svelte:fragment>
|
||||
</FileDropzone>
|
||||
|
||||
<!-- Save/Delete buttons -->
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
<div class="mr-auto">
|
||||
<SlideToggle
|
||||
name="active"
|
||||
background="bg-primary-500"
|
||||
active="bg-tertiary-500"
|
||||
bind:checked={active_value}
|
||||
{disabled}
|
||||
/>
|
||||
</div>
|
||||
{#if driver}
|
||||
<Button formaction="?/update_driver" color="secondary" {disabled} submit width="w-1/2">
|
||||
Save
|
||||
</Button>
|
||||
<Button formaction="?/delete_driver" color="primary" {disabled} submit width="w-1/2">
|
||||
Delete
|
||||
</Button>
|
||||
{:else}
|
||||
<Button formaction="?/create_driver" color="tertiary" {disabled} submit width="w-full">
|
||||
Create Driver
|
||||
</Button>
|
||||
{/if}
|
||||
/>
|
||||
</div>
|
||||
{#if driver}
|
||||
<Button onclick={update_driver()} color="secondary" {disabled} width="w-1/2">Save</Button>
|
||||
<Button onclick={delete_driver} color="primary" {disabled} width="w-1/2">Delete</Button>
|
||||
{:else}
|
||||
<Button onclick={update_driver(true)} color="tertiary" {disabled} width="w-full">
|
||||
Create Driver
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</Card>
|
||||
{/await}
|
||||
|
||||
Reference in New Issue
Block a user