Compare commits
9 Commits
6475c49af0
...
4d7b96b5cc
| Author | SHA1 | Date | |
|---|---|---|---|
| 4d7b96b5cc | |||
| e5fd61aa49 | |||
| 4a2b5e8781 | |||
| 53396fce19 | |||
| dcd62cdbfa | |||
| 08774fc40b | |||
| bc8a86c025 | |||
| 3178840b02 | |||
| 161929f1c2 |
@ -9,7 +9,7 @@
|
|||||||
type ToastStore,
|
type ToastStore,
|
||||||
} from "@skeletonlabs/skeleton";
|
} from "@skeletonlabs/skeleton";
|
||||||
import { Button, Card, Dropdown } from "$lib/components";
|
import { Button, Card, Dropdown } from "$lib/components";
|
||||||
import type { Driver, Race, RaceResult } from "$lib/schema";
|
import type { Driver, Race, RaceResult, Substitution } 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 { pb } from "$lib/pocketbase";
|
import { pb } from "$lib/pocketbase";
|
||||||
@ -36,12 +36,16 @@
|
|||||||
|
|
||||||
const toastStore: ToastStore = getToastStore();
|
const toastStore: ToastStore = getToastStore();
|
||||||
|
|
||||||
|
// Await promises
|
||||||
let races: Race[] | undefined = $state(undefined);
|
let races: Race[] | undefined = $state(undefined);
|
||||||
data.races.then((r: Race[]) => (races = r));
|
data.races.then((r: Race[]) => (races = r));
|
||||||
|
|
||||||
let drivers: Driver[] | undefined = $state(undefined);
|
let drivers: Driver[] | undefined = $state(undefined);
|
||||||
data.drivers.then((d: Driver[]) => (drivers = d));
|
data.drivers.then((d: Driver[]) => (drivers = d));
|
||||||
|
|
||||||
|
let substitutions: Substitution[] | undefined = $state(undefined);
|
||||||
|
data.substitutions.then((s: Substitution[]) => (substitutions = s));
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
const labelwidth: string = "70px";
|
const labelwidth: string = "70px";
|
||||||
|
|
||||||
@ -69,10 +73,7 @@
|
|||||||
// Set the pxxs/dnfs states once the drivers are loaded
|
// Set the pxxs/dnfs states once the drivers are loaded
|
||||||
data.drivers.then(async (drivers: Driver[]) => {
|
data.drivers.then(async (drivers: Driver[]) => {
|
||||||
pxxs_chips =
|
pxxs_chips =
|
||||||
result?.pxxs.map(
|
result?.pxxs.map((id: string) => get_by_value(drivers, "id", id)?.code ?? "Invalid") ?? [];
|
||||||
(id: string, index: number) =>
|
|
||||||
`P${(currentrace?.pxx ?? -10) + index - 3}: ${get_by_value(drivers, "id", id)?.code ?? "Invalid"}`,
|
|
||||||
) ?? [];
|
|
||||||
|
|
||||||
dnfs_chips =
|
dnfs_chips =
|
||||||
result?.dnfs.map((id: string) => get_by_value(drivers, "id", id)?.code ?? "Invalid") ?? [];
|
result?.dnfs.map((id: string) => get_by_value(drivers, "id", id)?.code ?? "Invalid") ?? [];
|
||||||
@ -82,32 +83,37 @@
|
|||||||
let pxxs_ids: string[] = $state(result?.pxxs ?? []);
|
let pxxs_ids: string[] = $state(result?.pxxs ?? []);
|
||||||
let dnfs_ids: string[] = $state(result?.dnfs ?? []);
|
let dnfs_ids: string[] = $state(result?.dnfs ?? []);
|
||||||
|
|
||||||
let pxxs_options: AutocompleteOption<string>[] = $derived.by(() =>
|
let pxxs_options: AutocompleteOption<string>[] = $derived.by(() => {
|
||||||
(drivers ?? [])
|
if (!race_select_value) return [];
|
||||||
.filter((driver: Driver) => {
|
|
||||||
// Filter out all drivers that are already selected
|
let active_and_substitutes: Driver[] = (drivers ?? []).filter(
|
||||||
return !pxxs_ids.includes(driver.id);
|
(driver: Driver) => driver.active,
|
||||||
})
|
);
|
||||||
|
|
||||||
|
(substitutions ?? [])
|
||||||
|
.filter((substitution: Substitution) => substitution.race === race_select_value)
|
||||||
|
.forEach((substitution: Substitution) => {
|
||||||
|
const for_index = active_and_substitutes.findIndex(
|
||||||
|
(driver: Driver) => driver.id === substitution.for,
|
||||||
|
);
|
||||||
|
const sub_index = (drivers ?? []).findIndex(
|
||||||
|
(driver: Driver) => driver.id === substitution.substitute,
|
||||||
|
);
|
||||||
|
|
||||||
|
active_and_substitutes[for_index] = (drivers ?? [])[sub_index];
|
||||||
|
});
|
||||||
|
|
||||||
|
return active_and_substitutes
|
||||||
|
.sort((a: Driver, b: Driver) => a.firstname.localeCompare(b.firstname))
|
||||||
.map((driver: Driver) => {
|
.map((driver: Driver) => {
|
||||||
return {
|
return {
|
||||||
// NOTE: Because Skeleton displays the values inside the autocomplete input,
|
// NOTE: Because Skeleton displays the values inside the autocomplete input,
|
||||||
// we have to supply the driver code twice and manage a list of ids manually (ugh)
|
// we have to supply the driver code twice and manage a list of ids manually (ugh)
|
||||||
label: driver.code,
|
label: `${driver.firstname} ${driver.lastname}`,
|
||||||
value: driver.code,
|
value: driver.code,
|
||||||
};
|
};
|
||||||
}),
|
});
|
||||||
);
|
});
|
||||||
|
|
||||||
let dnfs_options: AutocompleteOption<string>[] = $derived.by(() =>
|
|
||||||
(drivers ?? []).map((driver: Driver) => {
|
|
||||||
return {
|
|
||||||
// NOTE: Because Skeleton displays the values inside the autocomplete input,
|
|
||||||
// we have to supply the driver code twice and manage a list of ids manually (ugh)
|
|
||||||
label: driver.code,
|
|
||||||
value: driver.code,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
let pxxs_whitelist: string[] = $derived.by(() =>
|
let pxxs_whitelist: string[] = $derived.by(() =>
|
||||||
(drivers ?? []).map((driver: Driver) => {
|
(drivers ?? []).map((driver: Driver) => {
|
||||||
@ -116,24 +122,17 @@
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Event handlers
|
// Event handlers
|
||||||
const on_race_select = (event: Event): void => {
|
|
||||||
pxxs_chips = pxxs_chips.map(
|
|
||||||
(label: string, index: number) =>
|
|
||||||
`P${(currentrace?.pxx ?? -10) + index - 3}: ${label.split(" ").pop()}`,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const on_pxxs_chip_select = (event: CustomEvent<AutocompleteOption<string>>): void => {
|
const on_pxxs_chip_select = (event: CustomEvent<AutocompleteOption<string>>): void => {
|
||||||
if (disabled || !currentrace || !drivers) return;
|
if (disabled || !drivers) return;
|
||||||
|
|
||||||
// Can only select 7 drivers
|
// Can only select 7 drivers
|
||||||
if (pxxs_chips.length >= 7) return;
|
if (pxxs_chips.length >= 7) return;
|
||||||
|
|
||||||
// Can only select a driver once (because we display the PXX, check for string suffixes)
|
// Can only select a driver once
|
||||||
if (pxxs_chips.some((label: string) => label.endsWith(event.detail.value))) return;
|
if (pxxs_chips.includes(event.detail.value)) return;
|
||||||
|
|
||||||
// Manage labels that are displayed
|
// Manage labels that are displayed
|
||||||
pxxs_chips.push(`P${currentrace.pxx + pxxs_chips.length - 3}: ${event.detail.value}`);
|
pxxs_chips.push(event.detail.value);
|
||||||
pxxs_input = "";
|
pxxs_input = "";
|
||||||
|
|
||||||
// Manage ids that are submitted via form
|
// Manage ids that are submitted via form
|
||||||
@ -145,11 +144,6 @@
|
|||||||
|
|
||||||
const on_pxxs_chip_remove = (event: CustomEvent): void => {
|
const on_pxxs_chip_remove = (event: CustomEvent): void => {
|
||||||
pxxs_ids.splice(event.detail.chipIndex, 1);
|
pxxs_ids.splice(event.detail.chipIndex, 1);
|
||||||
|
|
||||||
pxxs_chips = pxxs_chips.map(
|
|
||||||
(label: string, index: number) =>
|
|
||||||
`P${(currentrace?.pxx ?? -10) + index - 3}: ${label.split(" ").pop()}`,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const on_dnfs_chip_select = (event: CustomEvent<AutocompleteOption<string>>): void => {
|
const on_dnfs_chip_select = (event: CustomEvent<AutocompleteOption<string>>): void => {
|
||||||
@ -227,13 +221,13 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Card width="w-full sm:w-[512px]">
|
<Card width="w-full sm:w-[512px]">
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
<!-- Race select input -->
|
<!-- Race select input -->
|
||||||
{#await data.races then races}
|
{#await data.races then races}
|
||||||
<Dropdown
|
<Dropdown
|
||||||
name="race"
|
name="race"
|
||||||
bind:value={race_select_value}
|
bind:value={race_select_value}
|
||||||
options={race_dropdown_options(races)}
|
options={race_dropdown_options(races)}
|
||||||
onchange={on_race_select}
|
|
||||||
{labelwidth}
|
{labelwidth}
|
||||||
{disabled}
|
{disabled}
|
||||||
{required}
|
{required}
|
||||||
@ -242,7 +236,7 @@
|
|||||||
</Dropdown>
|
</Dropdown>
|
||||||
{/await}
|
{/await}
|
||||||
|
|
||||||
<div class="mt-2 flex flex-col gap-2">
|
{#if race_select_value}
|
||||||
<!-- PXXs autocomplete chips -->
|
<!-- PXXs autocomplete chips -->
|
||||||
<InputChip
|
<InputChip
|
||||||
bind:input={pxxs_input}
|
bind:input={pxxs_input}
|
||||||
@ -278,7 +272,7 @@
|
|||||||
<div class="card max-h-48 w-full overflow-y-auto p-2" tabindex="-1">
|
<div class="card max-h-48 w-full overflow-y-auto p-2" tabindex="-1">
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
bind:input={dnfs_input}
|
bind:input={dnfs_input}
|
||||||
options={dnfs_options}
|
options={pxxs_options}
|
||||||
denylist={dnfs_chips}
|
denylist={dnfs_chips}
|
||||||
on:selection={on_dnfs_chip_select}
|
on:selection={on_dnfs_chip_select}
|
||||||
/>
|
/>
|
||||||
@ -290,12 +284,15 @@
|
|||||||
<Button onclick={update_raceresult()} color="secondary" {disabled} width="w-1/2"
|
<Button onclick={update_raceresult()} color="secondary" {disabled} width="w-1/2"
|
||||||
>Save</Button
|
>Save</Button
|
||||||
>
|
>
|
||||||
<Button onclick={delete_raceresult} color="primary" {disabled} width="w-1/2">Delete</Button>
|
<Button onclick={delete_raceresult} color="primary" {disabled} width="w-1/2"
|
||||||
|
>Delete</Button
|
||||||
|
>
|
||||||
{:else}
|
{:else}
|
||||||
<Button onclick={update_raceresult(true)} color="tertiary" {disabled} width="w-full">
|
<Button onclick={update_raceresult(true)} color="tertiary" {disabled} width="w-full">
|
||||||
Create Result
|
Create Result
|
||||||
</Button>
|
</Button>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
399
src/lib/components/cards/SeasonPickCard.svelte
Normal file
399
src/lib/components/cards/SeasonPickCard.svelte
Normal file
@ -0,0 +1,399 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Card, Button, Dropdown, Input } from "$lib/components";
|
||||||
|
import type { Driver, SeasonPick, Team } from "$lib/schema";
|
||||||
|
import { get_by_value, get_driver_headshot_template } from "$lib/database";
|
||||||
|
import {
|
||||||
|
Autocomplete,
|
||||||
|
getModalStore,
|
||||||
|
getToastStore,
|
||||||
|
InputChip,
|
||||||
|
type AutocompleteOption,
|
||||||
|
type ModalStore,
|
||||||
|
type ToastStore,
|
||||||
|
} from "@skeletonlabs/skeleton";
|
||||||
|
import { DRIVER_HEADSHOT_HEIGHT, DRIVER_HEADSHOT_WIDTH } from "$lib/config";
|
||||||
|
import { driver_dropdown_options, team_dropdown_options } from "$lib/dropdown";
|
||||||
|
import { get_error_toast } from "$lib/toast";
|
||||||
|
import { pb } from "$lib/pocketbase";
|
||||||
|
import type { PageData } from "../../../routes/seasonpicks/$types";
|
||||||
|
|
||||||
|
interface SeasonPickCardProps {
|
||||||
|
/** Data passed from the page context */
|
||||||
|
data: PageData;
|
||||||
|
|
||||||
|
/** The [SeasonPick] object used to prefill values. */
|
||||||
|
seasonpick?: SeasonPick;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { data, seasonpick = undefined }: SeasonPickCardProps = $props();
|
||||||
|
|
||||||
|
const modalStore: ModalStore = getModalStore();
|
||||||
|
if ($modalStore[0].meta) {
|
||||||
|
const meta = $modalStore[0].meta;
|
||||||
|
|
||||||
|
data = meta.data;
|
||||||
|
seasonpick = meta.seasonpick;
|
||||||
|
}
|
||||||
|
|
||||||
|
const toastStore: ToastStore = getToastStore();
|
||||||
|
|
||||||
|
// Await promises
|
||||||
|
let drivers: Driver[] | undefined = $state(undefined);
|
||||||
|
data.drivers.then((d: Driver[]) => (drivers = d));
|
||||||
|
|
||||||
|
let teams: Team[] | undefined = $state(undefined);
|
||||||
|
data.teams.then((t: Team[]) => (teams = t));
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
const labelwidth: string = "150px";
|
||||||
|
|
||||||
|
// Reactive state
|
||||||
|
let required: boolean = $derived(!seasonpick);
|
||||||
|
let disabled: boolean = false; // TODO: Datelock
|
||||||
|
let hottake_value: string = $state(seasonpick?.hottake ?? "");
|
||||||
|
let wdc_value: string = $state(seasonpick?.wdcwinner ?? "");
|
||||||
|
let wcc_value: string = $state(seasonpick?.wccwinner ?? "");
|
||||||
|
let overtakes_value: string = $state(seasonpick?.mostovertakes ?? "");
|
||||||
|
let dnfs_value: string = $state(seasonpick?.mostdnfs ?? "");
|
||||||
|
let doohan_value: string = $state(seasonpick?.doohanstarts.toString() ?? "");
|
||||||
|
|
||||||
|
let teamwinners_input: string = $state("");
|
||||||
|
let teamwinners_chips: string[] = $state([]);
|
||||||
|
|
||||||
|
let podiums_input: string = $state("");
|
||||||
|
let podiums_chips: string[] = $state([]);
|
||||||
|
|
||||||
|
// Set the teamwinners/podiums states once the drivers are loaded
|
||||||
|
data.drivers.then(async (drivers: Driver[]) => {
|
||||||
|
teamwinners_chips =
|
||||||
|
seasonpick?.teamwinners.map(
|
||||||
|
(id: string) => get_by_value(drivers, "id", id)?.code ?? "Invalid",
|
||||||
|
) ?? [];
|
||||||
|
|
||||||
|
podiums_chips =
|
||||||
|
seasonpick?.podiums.map((id: string) => get_by_value(drivers, "id", id)?.code ?? "Invalid") ??
|
||||||
|
[];
|
||||||
|
});
|
||||||
|
|
||||||
|
// This is the actual data that gets sent through the form
|
||||||
|
let teamwinners_ids: string[] = $state(seasonpick?.teamwinners ?? []);
|
||||||
|
let podiums_ids: string[] = $state(seasonpick?.podiums ?? []);
|
||||||
|
|
||||||
|
let teamwinners_options: AutocompleteOption<string>[] = $derived.by(() =>
|
||||||
|
(drivers ?? [])
|
||||||
|
.filter((driver: Driver) => driver.active)
|
||||||
|
.map((driver: Driver) => {
|
||||||
|
const teamname: string = get_by_value(teams ?? [], "id", driver.team)?.name ?? "Invalid";
|
||||||
|
|
||||||
|
return {
|
||||||
|
firstname: driver.firstname,
|
||||||
|
lastname: driver.lastname,
|
||||||
|
code: driver.code,
|
||||||
|
teamname: teamname,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => a.teamname.localeCompare(b.teamname))
|
||||||
|
.map((driver) => {
|
||||||
|
return {
|
||||||
|
label: `${driver.teamname}: ${driver.firstname} ${driver.lastname}`,
|
||||||
|
value: driver.code,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
let teamwinners_whitelist: string[] = $derived.by(() =>
|
||||||
|
(drivers ?? []).map((driver: Driver) => driver.code),
|
||||||
|
);
|
||||||
|
|
||||||
|
let teamwinners_denylist: string[] = $derived.by(() => {
|
||||||
|
let denylist: string[] = [];
|
||||||
|
|
||||||
|
teamwinners_chips
|
||||||
|
.map((driver: string) => get_by_value(drivers ?? [], "code", driver))
|
||||||
|
.forEach((driver: Driver | undefined) => {
|
||||||
|
if (driver) {
|
||||||
|
(drivers ?? [])
|
||||||
|
.filter((d: Driver) => d.team === driver.team)
|
||||||
|
.forEach((d: Driver) => {
|
||||||
|
denylist.push(d.code);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return denylist;
|
||||||
|
});
|
||||||
|
|
||||||
|
let podiums_options: AutocompleteOption<string>[] = $derived.by(() =>
|
||||||
|
(drivers ?? [])
|
||||||
|
.filter((driver: Driver) => driver.active)
|
||||||
|
.sort((a: Driver, b: Driver) => a.firstname.localeCompare(b.firstname))
|
||||||
|
.map((driver: Driver) => {
|
||||||
|
return {
|
||||||
|
label: `${driver.firstname} ${driver.lastname}`,
|
||||||
|
value: driver.code,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Event handlers
|
||||||
|
const on_teamwinners_chip_select = (event: CustomEvent<AutocompleteOption<string>>): void => {
|
||||||
|
if (disabled || !drivers) return;
|
||||||
|
|
||||||
|
// Can only select 10 drivers
|
||||||
|
if (teamwinners_chips.length >= 10) return;
|
||||||
|
|
||||||
|
// Can only select a driver once
|
||||||
|
if (teamwinners_chips.includes(event.detail.value)) return;
|
||||||
|
|
||||||
|
// Manage labels that are displayed
|
||||||
|
teamwinners_chips.push(event.detail.value);
|
||||||
|
teamwinners_input = "";
|
||||||
|
|
||||||
|
// Manage ids that are submitted via form
|
||||||
|
const id: string = get_by_value(drivers, "code", event.detail.value)?.id ?? "Invalid";
|
||||||
|
if (!teamwinners_ids.includes(id)) {
|
||||||
|
teamwinners_ids.push(id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const on_teamwinners_chip_remove = (event: CustomEvent): void => {
|
||||||
|
teamwinners_ids.splice(event.detail.chipIndex, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const on_podiums_chip_select = (event: CustomEvent<AutocompleteOption<string>>): void => {
|
||||||
|
if (disabled || !drivers) return;
|
||||||
|
|
||||||
|
// Can only select a driver once
|
||||||
|
if (podiums_chips.includes(event.detail.value)) return;
|
||||||
|
|
||||||
|
// Manage labels that are displayed
|
||||||
|
podiums_chips.push(event.detail.value);
|
||||||
|
podiums_input = "";
|
||||||
|
|
||||||
|
// Manage ids that are submitted via form
|
||||||
|
const id: string = get_by_value(drivers, "code", event.detail.value)?.id ?? "Invalid";
|
||||||
|
if (!podiums_ids.includes(id)) {
|
||||||
|
podiums_ids.push(id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const on_podiums_chip_remove = (event: CustomEvent): void => {
|
||||||
|
podiums_ids.splice(event.detail.chipIndex, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Database actions
|
||||||
|
const update_seasonpick = (create?: boolean): (() => Promise<void>) => {
|
||||||
|
const handler = async (): Promise<void> => {
|
||||||
|
if (!data.user?.id || data.user.id === "") {
|
||||||
|
toastStore.trigger(get_error_toast("Invalid user id!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!hottake_value || hottake_value === "") {
|
||||||
|
toastStore.trigger(get_error_toast("Please enter a hottake!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!wdc_value || wdc_value === "") {
|
||||||
|
toastStore.trigger(get_error_toast("Please select a driver for WDC!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!wcc_value || wcc_value === "") {
|
||||||
|
toastStore.trigger(get_error_toast("Please select a team for WCC!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!overtakes_value || overtakes_value === "") {
|
||||||
|
toastStore.trigger(get_error_toast("Please select a driver for most overtakes!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!dnfs_value || dnfs_value === "") {
|
||||||
|
toastStore.trigger(get_error_toast("Please select a driver for most DNFs!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!doohan_value ||
|
||||||
|
doohan_value === "" ||
|
||||||
|
parseInt(doohan_value) <= 0 ||
|
||||||
|
parseInt(doohan_value) > 24
|
||||||
|
) {
|
||||||
|
toastStore.trigger(
|
||||||
|
get_error_toast("Please enter between 0 and 24 starts for Jack Doohan!"),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!teamwinners_ids || teamwinners_ids.length !== 10) {
|
||||||
|
toastStore.trigger(get_error_toast("Please select a winner for each team!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!podiums_ids || podiums_ids.length < 3) {
|
||||||
|
toastStore.trigger(get_error_toast("Please select at least 3 drivers with podiums!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const seasonpick_data = {
|
||||||
|
user: data.user.id,
|
||||||
|
hottake: hottake_value,
|
||||||
|
wdcwinner: wdc_value,
|
||||||
|
wccwinner: wcc_value,
|
||||||
|
mostovertakes: overtakes_value,
|
||||||
|
mostdnfs: dnfs_value,
|
||||||
|
doohanstarts: doohan_value,
|
||||||
|
teamwinners: teamwinners_ids,
|
||||||
|
podiums: podiums_ids,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (create) {
|
||||||
|
await pb.collection("seasonpicks").create(seasonpick_data);
|
||||||
|
} else {
|
||||||
|
if (!seasonpick?.id) {
|
||||||
|
toastStore.trigger(get_error_toast("Invalid seasonpick id!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await pb.collection("seasonpicks").update(seasonpick.id, seasonpick_data);
|
||||||
|
}
|
||||||
|
modalStore.close();
|
||||||
|
} catch (error) {
|
||||||
|
toastStore.trigger(get_error_toast("" + error));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return handler;
|
||||||
|
};
|
||||||
|
|
||||||
|
const delete_seasonpick = async (): Promise<void> => {
|
||||||
|
if (!seasonpick?.id) {
|
||||||
|
toastStore.trigger(get_error_toast("Invalid seasonpick id!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await pb.collection("seasonpicks").delete(seasonpick.id);
|
||||||
|
modalStore.close();
|
||||||
|
} catch (error) {
|
||||||
|
toastStore.trigger(get_error_toast("" + error));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#await Promise.all([data.graphics, data.drivers, data.teams]) then [graphics, drivers, teams]}
|
||||||
|
<Card width="w-full sm:w-[512px]">
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<!-- Hottake -->
|
||||||
|
<Input bind:value={hottake_value} {labelwidth} {disabled} {required}>Hottake</Input>
|
||||||
|
|
||||||
|
<!-- WDC select -->
|
||||||
|
<Dropdown
|
||||||
|
bind:value={wdc_value}
|
||||||
|
options={driver_dropdown_options(drivers.filter((driver: Driver) => driver.active))}
|
||||||
|
{labelwidth}
|
||||||
|
{disabled}
|
||||||
|
{required}
|
||||||
|
>
|
||||||
|
WDC
|
||||||
|
</Dropdown>
|
||||||
|
|
||||||
|
<!-- WCC select -->
|
||||||
|
<Dropdown
|
||||||
|
bind:value={wcc_value}
|
||||||
|
options={team_dropdown_options(teams)}
|
||||||
|
{labelwidth}
|
||||||
|
{disabled}
|
||||||
|
{required}
|
||||||
|
>
|
||||||
|
WCC
|
||||||
|
</Dropdown>
|
||||||
|
|
||||||
|
<!-- Overtakes select -->
|
||||||
|
<Dropdown
|
||||||
|
bind:value={overtakes_value}
|
||||||
|
options={driver_dropdown_options(drivers.filter((driver: Driver) => driver.active))}
|
||||||
|
{labelwidth}
|
||||||
|
{disabled}
|
||||||
|
{required}
|
||||||
|
>
|
||||||
|
Most Overtakes
|
||||||
|
</Dropdown>
|
||||||
|
|
||||||
|
<!-- DNFs select -->
|
||||||
|
<Dropdown
|
||||||
|
bind:value={dnfs_value}
|
||||||
|
options={driver_dropdown_options(drivers.filter((driver: Driver) => driver.active))}
|
||||||
|
{labelwidth}
|
||||||
|
{disabled}
|
||||||
|
{required}
|
||||||
|
>
|
||||||
|
Most DNFs
|
||||||
|
</Dropdown>
|
||||||
|
|
||||||
|
<!-- Doohan Starts -->
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
min={0}
|
||||||
|
max={24}
|
||||||
|
bind:value={doohan_value}
|
||||||
|
{labelwidth}
|
||||||
|
{disabled}
|
||||||
|
{required}
|
||||||
|
>
|
||||||
|
Doohan Starts
|
||||||
|
</Input>
|
||||||
|
|
||||||
|
<!-- Teamwinners autocomplete chips -->
|
||||||
|
<InputChip
|
||||||
|
bind:input={teamwinners_input}
|
||||||
|
bind:value={teamwinners_chips}
|
||||||
|
whitelist={teamwinners_whitelist}
|
||||||
|
allowUpperCase
|
||||||
|
placeholder="Select Teamwinners..."
|
||||||
|
name="teamwinners_codes"
|
||||||
|
{disabled}
|
||||||
|
{required}
|
||||||
|
on:remove={on_teamwinners_chip_remove}
|
||||||
|
/>
|
||||||
|
<div class="card max-h-48 w-full overflow-y-auto p-2" tabindex="-1">
|
||||||
|
<Autocomplete
|
||||||
|
bind:input={teamwinners_input}
|
||||||
|
options={teamwinners_options}
|
||||||
|
denylist={teamwinners_denylist}
|
||||||
|
on:selection={on_teamwinners_chip_select}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Podiums autocomplete chips -->
|
||||||
|
<InputChip
|
||||||
|
bind:input={podiums_input}
|
||||||
|
bind:value={podiums_chips}
|
||||||
|
whitelist={teamwinners_whitelist}
|
||||||
|
allowUpperCase
|
||||||
|
placeholder="Select Drivers with Podiums..."
|
||||||
|
name="podiums_codes"
|
||||||
|
{disabled}
|
||||||
|
{required}
|
||||||
|
on:remove={on_podiums_chip_remove}
|
||||||
|
/>
|
||||||
|
<div class="card max-h-48 w-full overflow-y-auto p-2" tabindex="-1">
|
||||||
|
<Autocomplete
|
||||||
|
bind:input={podiums_input}
|
||||||
|
options={podiums_options}
|
||||||
|
denylist={podiums_chips}
|
||||||
|
on:selection={on_podiums_chip_select}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Save/Delete buttons -->
|
||||||
|
<div class="flex justify-end gap-2">
|
||||||
|
{#if seasonpick}
|
||||||
|
<Button onclick={update_seasonpick()} color="secondary" {disabled} width="w-1/2">
|
||||||
|
Save Changes
|
||||||
|
</Button>
|
||||||
|
<Button onclick={delete_seasonpick} color="primary" {disabled} width="w-1/2">
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
{:else}
|
||||||
|
<Button onclick={update_seasonpick(true)} color="tertiary" {disabled} width="w-full">
|
||||||
|
Make Pick
|
||||||
|
</Button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
{/await}
|
||||||
@ -12,6 +12,7 @@ import DriverCard from "./cards/DriverCard.svelte";
|
|||||||
import RaceCard from "./cards/RaceCard.svelte";
|
import RaceCard from "./cards/RaceCard.svelte";
|
||||||
import RacePickCard from "./cards/RacePickCard.svelte";
|
import RacePickCard from "./cards/RacePickCard.svelte";
|
||||||
import RaceResultCard from "./cards/RaceResultCard.svelte";
|
import RaceResultCard from "./cards/RaceResultCard.svelte";
|
||||||
|
import SeasonPickCard from "./cards/SeasonPickCard.svelte";
|
||||||
import SubstitutionCard from "./cards/SubstitutionCard.svelte";
|
import SubstitutionCard from "./cards/SubstitutionCard.svelte";
|
||||||
import TeamCard from "./cards/TeamCard.svelte";
|
import TeamCard from "./cards/TeamCard.svelte";
|
||||||
|
|
||||||
@ -43,6 +44,7 @@ export {
|
|||||||
RaceCard,
|
RaceCard,
|
||||||
RacePickCard,
|
RacePickCard,
|
||||||
RaceResultCard,
|
RaceResultCard,
|
||||||
|
SeasonPickCard,
|
||||||
SubstitutionCard,
|
SubstitutionCard,
|
||||||
TeamCard,
|
TeamCard,
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,9 @@ import {
|
|||||||
* Generates a list of [DropdownOptions] for a <Dropdown> component.
|
* Generates a list of [DropdownOptions] for a <Dropdown> component.
|
||||||
*/
|
*/
|
||||||
export const team_dropdown_options = (teams: Team[]): DropdownOption[] =>
|
export const team_dropdown_options = (teams: Team[]): DropdownOption[] =>
|
||||||
teams.map((team: Team) => {
|
teams
|
||||||
|
.sort((a: Team, b: Team) => a.name.localeCompare(b.name))
|
||||||
|
.map((team: Team) => {
|
||||||
return {
|
return {
|
||||||
label: team.name,
|
label: team.name,
|
||||||
value: team.id,
|
value: team.id,
|
||||||
@ -27,9 +29,11 @@ export const team_dropdown_options = (teams: Team[]): DropdownOption[] =>
|
|||||||
* Generates a list of [DropdownOptions] for a <Dropdown> component.
|
* Generates a list of [DropdownOptions] for a <Dropdown> component.
|
||||||
*/
|
*/
|
||||||
export const driver_dropdown_options = (drivers: Driver[]): DropdownOption[] =>
|
export const driver_dropdown_options = (drivers: Driver[]): DropdownOption[] =>
|
||||||
drivers.map((driver: Driver) => {
|
drivers
|
||||||
|
.sort((a: Driver, b: Driver) => a.firstname.localeCompare(b.firstname))
|
||||||
|
.map((driver: Driver) => {
|
||||||
return {
|
return {
|
||||||
label: driver.code,
|
label: `${driver.firstname} ${driver.lastname}`,
|
||||||
value: driver.id,
|
value: driver.id,
|
||||||
icon_url: driver.headshot_url,
|
icon_url: driver.headshot_url,
|
||||||
icon_width: DRIVER_HEADSHOT_WIDTH,
|
icon_width: DRIVER_HEADSHOT_WIDTH,
|
||||||
@ -41,7 +45,9 @@ export const driver_dropdown_options = (drivers: Driver[]): DropdownOption[] =>
|
|||||||
* Generates a list of [DropdownOptions] for a <Dropdown> component.
|
* Generates a list of [DropdownOptions] for a <Dropdown> component.
|
||||||
*/
|
*/
|
||||||
export const race_dropdown_options = (races: Race[]): DropdownOption[] =>
|
export const race_dropdown_options = (races: Race[]): DropdownOption[] =>
|
||||||
races.map((race: Race) => {
|
races
|
||||||
|
.sort((a: Race, b: Race) => a.step - b.step)
|
||||||
|
.map((race: Race) => {
|
||||||
return {
|
return {
|
||||||
label: race.name,
|
label: race.name,
|
||||||
value: race.id,
|
value: race.id,
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
NameIcon,
|
NameIcon,
|
||||||
RacePickCard,
|
RacePickCard,
|
||||||
RaceResultCard,
|
RaceResultCard,
|
||||||
|
SeasonPickCard,
|
||||||
} from "$lib/components";
|
} from "$lib/components";
|
||||||
import { get_avatar_preview_event_handler } from "$lib/image";
|
import { get_avatar_preview_event_handler } from "$lib/image";
|
||||||
import {
|
import {
|
||||||
@ -58,6 +59,7 @@
|
|||||||
raceCard: { ref: RaceCard },
|
raceCard: { ref: RaceCard },
|
||||||
racePickCard: { ref: RacePickCard },
|
racePickCard: { ref: RacePickCard },
|
||||||
raceResultCard: { ref: RaceResultCard },
|
raceResultCard: { ref: RaceResultCard },
|
||||||
|
seasonPickCard: { ref: SeasonPickCard },
|
||||||
substitutionCard: { ref: SubstitutionCard },
|
substitutionCard: { ref: SubstitutionCard },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { fetch_drivers, fetch_raceresults, fetch_races } from "$lib/fetch";
|
import { fetch_drivers, fetch_raceresults, fetch_races, fetch_substitutions } from "$lib/fetch";
|
||||||
import type { PageLoad } from "../../$types";
|
import type { PageLoad } from "../../$types";
|
||||||
|
|
||||||
export const load: PageLoad = async ({ fetch, depends }) => {
|
export const load: PageLoad = async ({ fetch, depends }) => {
|
||||||
@ -8,5 +8,6 @@ export const load: PageLoad = async ({ fetch, depends }) => {
|
|||||||
drivers: fetch_drivers(fetch),
|
drivers: fetch_drivers(fetch),
|
||||||
races: fetch_races(fetch),
|
races: fetch_races(fetch),
|
||||||
raceresults: fetch_raceresults(fetch),
|
raceresults: fetch_raceresults(fetch),
|
||||||
|
substitutions: fetch_substitutions(fetch),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -29,7 +29,7 @@
|
|||||||
const seasonpick_handler = async (event: Event) => {
|
const seasonpick_handler = async (event: Event) => {
|
||||||
const modalSettings: ModalSettings = {
|
const modalSettings: ModalSettings = {
|
||||||
type: "component",
|
type: "component",
|
||||||
component: "seasonPickcard",
|
component: "seasonPickCard",
|
||||||
meta: {
|
meta: {
|
||||||
data,
|
data,
|
||||||
seasonpick: data.seasonpick,
|
seasonpick: data.seasonpick,
|
||||||
@ -85,9 +85,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="card w-full min-w-40 p-2 shadow">
|
<div class="card w-full min-w-40 p-2 shadow">
|
||||||
<h1 class="mb-2 text-nowrap font-bold">Doohan Starts:</h1>
|
<h1 class="mb-2 text-nowrap font-bold">Doohan Starts:</h1>
|
||||||
|
{#if data.seasonpick}
|
||||||
<span class="text-sm">
|
<span class="text-sm">
|
||||||
Jack Doohan startet {data.seasonpick?.doohanstarts} mal.
|
Jack Doohan startet {data.seasonpick?.doohanstarts} mal.
|
||||||
</span>
|
</span>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user