Compare commits

...

3 Commits

Author SHA1 Message Date
ecee4ac5ab Skeleton: Use progressive form enhancement for register/login
All checks were successful
Build Formula11 Docker Image / pocketbase-docker (push) Successful in 27s
2025-02-04 23:02:05 +01:00
022054c1f1 Racepicks: Use progressive form enhancement for racepicks 2025-02-04 23:01:54 +01:00
887a9ea2ae Lib: Use progressive form enhancement for all cards 2025-02-04 23:01:35 +01:00
8 changed files with 48 additions and 16 deletions

View File

@ -10,6 +10,7 @@
import type { Driver, Team } from "$lib/schema"; import type { Driver, Team } from "$lib/schema";
import { DRIVER_HEADSHOT_HEIGHT, DRIVER_HEADSHOT_WIDTH } from "$lib/config"; import { DRIVER_HEADSHOT_HEIGHT, DRIVER_HEADSHOT_WIDTH } from "$lib/config";
import { team_dropdown_options } from "$lib/dropdown"; import { team_dropdown_options } from "$lib/dropdown";
import { enhance } from "$app/forms";
interface DriverCardProps { interface DriverCardProps {
/** The [Driver] object used to prefill values. */ /** The [Driver] object used to prefill values. */
@ -71,7 +72,7 @@
imgheight={DRIVER_HEADSHOT_HEIGHT} imgheight={DRIVER_HEADSHOT_HEIGHT}
imgonclick={(event: Event) => modalStore.close()} imgonclick={(event: Event) => modalStore.close()}
> >
<form method="POST" enctype="multipart/form-data"> <form method="POST" enctype="multipart/form-data" use:enhance>
<!-- This is also disabled, because the ID should only be --> <!-- This is also disabled, because the ID should only be -->
<!-- "leaked" to users that are allowed to use the inputs --> <!-- "leaked" to users that are allowed to use the inputs -->
{#if driver && !disable_inputs} {#if driver && !disable_inputs}
@ -158,6 +159,7 @@
disabled={disable_inputs} disabled={disable_inputs}
submit submit
width="w-1/2" width="w-1/2"
onclick={() => modalStore.close()}
> >
Save Save
</Button> </Button>
@ -167,6 +169,7 @@
disabled={disable_inputs} disabled={disable_inputs}
formaction="?/delete_driver" formaction="?/delete_driver"
width="w-1/2" width="w-1/2"
onclick={() => modalStore.close()}
> >
Delete Delete
</Button> </Button>
@ -177,6 +180,7 @@
submit submit
width="w-full" width="w-full"
disabled={disable_inputs} disabled={disable_inputs}
onclick={() => modalStore.close()}
> >
Create Driver Create Driver
</Button> </Button>

View File

@ -5,6 +5,7 @@
import type { Race } from "$lib/schema"; import type { Race } from "$lib/schema";
import { format } from "date-fns"; import { format } from "date-fns";
import { RACE_PICTOGRAM_HEIGHT, RACE_PICTOGRAM_WIDTH } from "$lib/config"; import { RACE_PICTOGRAM_HEIGHT, RACE_PICTOGRAM_WIDTH } from "$lib/config";
import { enhance } from "$app/forms";
interface RaceCardProps { interface RaceCardProps {
/** The [Race] object used to prefill values. */ /** The [Race] object used to prefill values. */
@ -76,7 +77,7 @@
imgheight={RACE_PICTOGRAM_HEIGHT} imgheight={RACE_PICTOGRAM_HEIGHT}
imgonclick={(event: Event) => modalStore.close()} imgonclick={(event: Event) => modalStore.close()}
> >
<form method="POST" enctype="multipart/form-data"> <form method="POST" enctype="multipart/form-data" use:enhance>
<!-- This is also disabled, because the ID should only be --> <!-- This is also disabled, because the ID should only be -->
<!-- "leaked" to users that are allowed to use the inputs --> <!-- "leaked" to users that are allowed to use the inputs -->
{#if race && !disable_inputs} {#if race && !disable_inputs}
@ -186,6 +187,7 @@
disabled={disable_inputs} disabled={disable_inputs}
submit submit
width="w-1/3" width="w-1/3"
onclick={() => modalStore.close()}
> >
Save Changes Save Changes
</Button> </Button>
@ -195,6 +197,7 @@
disabled={disable_inputs} disabled={disable_inputs}
formaction="?/delete_race" formaction="?/delete_race"
width="w-1/3" width="w-1/3"
onclick={() => modalStore.close()}
> >
Delete Delete
</Button> </Button>
@ -205,6 +208,7 @@
submit submit
width="w-1/2" width="w-1/2"
disabled={disable_inputs} disabled={disable_inputs}
onclick={() => modalStore.close()}
> >
Create Race Create Race
</Button> </Button>

View File

@ -6,6 +6,7 @@
import { getModalStore, type ModalStore } from "@skeletonlabs/skeleton"; import { getModalStore, type ModalStore } from "@skeletonlabs/skeleton";
import { DRIVER_HEADSHOT_HEIGHT, DRIVER_HEADSHOT_WIDTH } from "$lib/config"; import { DRIVER_HEADSHOT_HEIGHT, DRIVER_HEADSHOT_WIDTH } from "$lib/config";
import { driver_dropdown_options } from "$lib/dropdown"; import { driver_dropdown_options } from "$lib/dropdown";
import { enhance } from "$app/forms";
interface RacePickCardProps { interface RacePickCardProps {
/** The [RacePick] object used to prefill values. */ /** The [RacePick] object used to prefill values. */
@ -91,7 +92,7 @@
imgheight={DRIVER_HEADSHOT_HEIGHT} imgheight={DRIVER_HEADSHOT_HEIGHT}
imgonclick={(event: Event) => modalStore.close()} imgonclick={(event: Event) => modalStore.close()}
> >
<form method="POST" enctype="multipart/form-data"> <form method="POST" enctype="multipart/form-data" use:enhance>
<!-- This is also disabled, because the ID should only be --> <!-- This is also disabled, because the ID should only be -->
<!-- "leaked" to users that are allowed to use the inputs --> <!-- "leaked" to users that are allowed to use the inputs -->
{#if racepick && !disable_inputs} {#if racepick && !disable_inputs}
@ -134,6 +135,7 @@
disabled={disable_inputs} disabled={disable_inputs}
submit submit
width="w-1/2" width="w-1/2"
onclick={() => modalStore.close()}
> >
Save Changes Save Changes
</Button> </Button>
@ -143,11 +145,18 @@
disabled={disable_inputs} disabled={disable_inputs}
formaction="?/delete_racepick" formaction="?/delete_racepick"
width="w-1/2" width="w-1/2"
onclick={() => modalStore.close()}
> >
Delete Delete
</Button> </Button>
{:else} {:else}
<Button formaction="?/create_racepick" color="tertiary" submit width="w-full"> <Button
formaction="?/create_racepick"
color="tertiary"
submit
width="w-full"
onclick={() => modalStore.close()}
>
Make Pick Make Pick
</Button> </Button>
{/if} {/if}

View File

@ -10,6 +10,7 @@
import type { Driver, Race, RaceResult } from "$lib/schema"; import type { Driver, Race, RaceResult } from "$lib/schema";
import { get_by_value } from "$lib/database"; import { get_by_value } from "$lib/database";
import { race_dropdown_options } from "$lib/dropdown"; import { race_dropdown_options } from "$lib/dropdown";
import { enhance } from "$app/forms";
interface RaceResultCardProps { interface RaceResultCardProps {
/** The [RaceResult] object used to prefill values. */ /** The [RaceResult] object used to prefill values. */
@ -146,7 +147,7 @@
</script> </script>
<Card width="w-full sm:w-[512px]"> <Card width="w-full sm:w-[512px]">
<form method="POST" enctype="multipart/form-data"> <form method="POST" enctype="multipart/form-data" use:enhance>
<!-- This is also disabled, because the ID should only be --> <!-- This is also disabled, because the ID should only be -->
<!-- "leaked" to users that are allowed to use the inputs --> <!-- "leaked" to users that are allowed to use the inputs -->
{#if result2 && !disable_inputs2} {#if result2 && !disable_inputs2}
@ -224,6 +225,7 @@
disabled={disable_inputs2} disabled={disable_inputs2}
submit submit
width="w-1/2" width="w-1/2"
onclick={() => modalStore.close()}
> >
Save Save
</Button> </Button>
@ -233,6 +235,7 @@
disabled={disable_inputs2} disabled={disable_inputs2}
formaction="?/delete_raceresult" formaction="?/delete_raceresult"
width="w-1/2" width="w-1/2"
onclick={() => modalStore.close()}
> >
Delete Delete
</Button> </Button>
@ -243,6 +246,7 @@
submit submit
width="w-full" width="w-full"
disabled={disable_inputs2} disabled={disable_inputs2}
onclick={() => modalStore.close()}
> >
Create Result Create Result
</Button> </Button>

View File

@ -6,6 +6,7 @@
import { getModalStore, type ModalStore } from "@skeletonlabs/skeleton"; import { getModalStore, type ModalStore } from "@skeletonlabs/skeleton";
import { DRIVER_HEADSHOT_HEIGHT, DRIVER_HEADSHOT_WIDTH } from "$lib/config"; import { DRIVER_HEADSHOT_HEIGHT, DRIVER_HEADSHOT_WIDTH } from "$lib/config";
import { driver_dropdown_options, race_dropdown_options } from "$lib/dropdown"; import { driver_dropdown_options, race_dropdown_options } from "$lib/dropdown";
import { enhance } from "$app/forms";
interface SubstitutionCardProps { interface SubstitutionCardProps {
/** The [Substitution] object used to prefill values. */ /** The [Substitution] object used to prefill values. */
@ -104,7 +105,7 @@
imgheight={DRIVER_HEADSHOT_HEIGHT} imgheight={DRIVER_HEADSHOT_HEIGHT}
imgonclick={(event: Event) => modalStore.close()} imgonclick={(event: Event) => modalStore.close()}
> >
<form method="POST" enctype="multipart/form-data"> <form method="POST" enctype="multipart/form-data" use:enhance>
<!-- This is also disabled, because the ID should only be --> <!-- This is also disabled, because the ID should only be -->
<!-- "leaked" to users that are allowed to use the inputs --> <!-- "leaked" to users that are allowed to use the inputs -->
{#if substitution && !disable_inputs} {#if substitution && !disable_inputs}
@ -158,6 +159,7 @@
disabled={disable_inputs} disabled={disable_inputs}
submit submit
width="w-1/2" width="w-1/2"
onclick={() => modalStore.close()}
> >
Save Changes Save Changes
</Button> </Button>
@ -167,6 +169,7 @@
disabled={disable_inputs} disabled={disable_inputs}
formaction="?/delete_substitution" formaction="?/delete_substitution"
width="w-1/2" width="w-1/2"
onclick={() => modalStore.close()}
> >
Delete Delete
</Button> </Button>
@ -177,6 +180,7 @@
submit submit
width="w-full" width="w-full"
disabled={disable_inputs} disabled={disable_inputs}
onclick={() => modalStore.close()}
> >
Create Substitution Create Substitution
</Button> </Button>

View File

@ -4,6 +4,7 @@
import { Card, Button, Input, LazyImage } from "$lib/components"; import { Card, Button, Input, LazyImage } from "$lib/components";
import type { Team } from "$lib/schema"; import type { Team } from "$lib/schema";
import { TEAM_BANNER_HEIGHT, TEAM_BANNER_WIDTH } from "$lib/config"; import { TEAM_BANNER_HEIGHT, TEAM_BANNER_WIDTH } from "$lib/config";
import { enhance } from "$app/forms";
interface TeamCardProps { interface TeamCardProps {
/** The [Team] object used to prefill values. */ /** The [Team] object used to prefill values. */
@ -57,7 +58,7 @@
imgheight={TEAM_BANNER_HEIGHT} imgheight={TEAM_BANNER_HEIGHT}
imgonclick={(event: Event) => modalStore.close()} imgonclick={(event: Event) => modalStore.close()}
> >
<form method="POST" enctype="multipart/form-data"> <form method="POST" enctype="multipart/form-data" use:enhance>
<!-- This is also disabled, because the ID should only be --> <!-- This is also disabled, because the ID should only be -->
<!-- "leaked" to users that are allowed to use the inputs --> <!-- "leaked" to users that are allowed to use the inputs -->
{#if team && !disable_inputs} {#if team && !disable_inputs}
@ -147,6 +148,7 @@
disabled={disable_inputs} disabled={disable_inputs}
submit submit
width="w-1/2" width="w-1/2"
onclick={() => modalStore.close()}
> >
Save Save
</Button> </Button>
@ -156,6 +158,7 @@
disabled={disable_inputs} disabled={disable_inputs}
formaction="?/delete_team" formaction="?/delete_team"
width="w-1/2" width="w-1/2"
onclick={() => modalStore.close()}
> >
Delete Delete
</Button> </Button>
@ -166,6 +169,7 @@
submit submit
width="w-full" width="w-full"
disabled={disable_inputs} disabled={disable_inputs}
onclick={() => modalStore.close()}
> >
Create Team Create Team
</Button> </Button>

View File

@ -38,6 +38,7 @@
type ModalComponent, type ModalComponent,
} from "@skeletonlabs/skeleton"; } from "@skeletonlabs/skeleton";
import { computePosition, autoUpdate, offset, shift, flip, arrow } from "@floating-ui/dom"; import { computePosition, autoUpdate, offset, shift, flip, arrow } from "@floating-ui/dom";
import { enhance } from "$app/forms";
let { data, children }: { data: LayoutData; children: Snippet } = $props(); let { data, children }: { data: LayoutData; children: Snippet } = $props();
@ -180,7 +181,7 @@
<!-- Login Drawer --> <!-- Login Drawer -->
<div class="flex flex-col gap-2 p-2 pt-3"> <div class="flex flex-col gap-2 p-2 pt-3">
<h4 class="h4 select-none">Enter Username and Password</h4> <h4 class="h4 select-none">Enter Username and Password</h4>
<form method="POST" class="contents"> <form method="POST" class="contents" use:enhance>
<!-- Supply the pathname so the form can redirect to the current page. --> <!-- Supply the pathname so the form can redirect to the current page. -->
<input type="hidden" name="redirect_url" value={$page.url.pathname} /> <input type="hidden" name="redirect_url" value={$page.url.pathname} />
<Input name="username" placeholder="Username" autocomplete="username" required> <Input name="username" placeholder="Username" autocomplete="username" required>
@ -220,7 +221,7 @@
<!-- Profile Drawer --> <!-- Profile Drawer -->
<div class="flex flex-col gap-2 p-2 pt-3"> <div class="flex flex-col gap-2 p-2 pt-3">
<h4 class="h4 select-none">Edit Profile</h4> <h4 class="h4 select-none">Edit Profile</h4>
<form method="POST" enctype="multipart/form-data" class="contents"> <form method="POST" enctype="multipart/form-data" class="contents" use:enhance>
<!-- Supply the pathname so the form can redirect to the current page. --> <!-- Supply the pathname so the form can redirect to the current page. -->
<input type="hidden" name="redirect_url" value={$page.url.pathname} /> <input type="hidden" name="redirect_url" value={$page.url.pathname} />
<input type="hidden" name="id" value={data.user.id} /> <input type="hidden" name="id" value={data.user.id} />

View File

@ -25,24 +25,26 @@
let { data }: { data: PageData } = $props(); let { data }: { data: PageData } = $props();
const currentpick: RacePick | undefined = let currentpick = (): RacePick | undefined =>
data.racepicks.filter( data.racepicks.filter(
(racepick: RacePick) => (racepick: RacePick) =>
racepick.expand.user.username === data.user?.username && racepick.expand.user.username === data.user?.username &&
racepick.race === data.currentrace?.id, racepick.race === data.currentrace?.id,
)[0] ?? undefined; )[0] ?? undefined;
let pxx_select_value: string = $state(currentpick?.pxx ?? ""); let pxx_select_value: string = $state(currentpick()?.pxx ?? "");
let dnf_select_value: string = $state(currentpick?.dnf ?? ""); let dnf_select_value: string = $state(currentpick()?.dnf ?? "");
const active_drivers_and_substitutes = ( const active_drivers_and_substitutes = (
drivers: Driver[], drivers: Driver[],
substitutions: Substitution[], substitutions: Substitution[],
): Driver[] => { ): Driver[] => {
if (!data.currentrace) return [];
let active_and_substitutes: Driver[] = drivers.filter((driver: Driver) => driver.active); let active_and_substitutes: Driver[] = drivers.filter((driver: Driver) => driver.active);
substitutions substitutions
.filter((substitution: Substitution) => substitution.race === currentpick.race) .filter((substitution: Substitution) => substitution.race === data.currentrace?.id)
.forEach((substitution: Substitution) => { .forEach((substitution: Substitution) => {
const for_index = active_and_substitutes.findIndex( const for_index = active_and_substitutes.findIndex(
(driver: Driver) => driver.id === substitution.for, (driver: Driver) => driver.id === substitution.for,
@ -63,7 +65,7 @@
type: "component", type: "component",
component: "racePickCard", component: "racePickCard",
meta: { meta: {
racepick: currentpick, racepick: currentpick(),
currentrace: data.currentrace, currentrace: data.currentrace,
user: data.user, user: data.user,
drivers: active_drivers_and_substitutes(await data.drivers, await data.substitutions), drivers: active_drivers_and_substitutes(await data.drivers, await data.substitutions),
@ -156,7 +158,7 @@
{#await data.graphics then graphics} {#await data.graphics then graphics}
{#await data.drivers then drivers} {#await data.drivers then drivers}
<LazyImage <LazyImage
src={get_by_value(drivers, "id", currentpick?.pxx ?? "")?.headshot_url ?? src={get_by_value(drivers, "id", currentpick()?.pxx ?? "")?.headshot_url ??
get_driver_headshot_template(graphics)} get_driver_headshot_template(graphics)}
imgwidth={DRIVER_HEADSHOT_WIDTH} imgwidth={DRIVER_HEADSHOT_WIDTH}
imgheight={DRIVER_HEADSHOT_HEIGHT} imgheight={DRIVER_HEADSHOT_HEIGHT}
@ -173,7 +175,7 @@
{#await data.graphics then graphics} {#await data.graphics then graphics}
{#await data.drivers then drivers} {#await data.drivers then drivers}
<LazyImage <LazyImage
src={get_by_value(drivers, "id", currentpick?.dnf ?? "")?.headshot_url ?? src={get_by_value(drivers, "id", currentpick()?.dnf ?? "")?.headshot_url ??
get_driver_headshot_template(graphics)} get_driver_headshot_template(graphics)}
imgwidth={DRIVER_HEADSHOT_WIDTH} imgwidth={DRIVER_HEADSHOT_WIDTH}
imgheight={DRIVER_HEADSHOT_HEIGHT} imgheight={DRIVER_HEADSHOT_HEIGHT}