Skeleton: Fetch static data (teams/drivers/races/substitutions) in global layout asynchronously
All checks were successful
Build Formula11 Docker Image / pocketbase-docker (push) Successful in 29s

This commit is contained in:
2025-02-03 22:34:07 +01:00
parent 3bb2e318b1
commit 346fdb3b75
12 changed files with 259 additions and 363 deletions

View File

@ -1,83 +0,0 @@
import type { Team, Driver, Race, Substitution, Graphic } from "$lib/schema";
import type { LayoutServerLoad } from "./$types";
// This "load" function runs serverside only, as it's located inside +layout.server.ts
export const load: LayoutServerLoad = async ({ fetch, locals }) => {
// TODO: Duplicated code from racepicks/+page.server.ts + users/+page.server.ts
const fetch_graphics = async (): Promise<Graphic[]> => {
const graphics: Graphic[] = await locals.pb
.collection("graphics")
.getFullList({ fetch: fetch });
graphics.map((graphic: Graphic) => {
graphic.file_url = locals.pb.files.getURL(graphic, graphic.file);
});
return graphics;
};
const fetch_teams = async (): Promise<Team[]> => {
const teams: Team[] = await locals.pb.collection("teams").getFullList({
sort: "+name",
fetch: fetch,
});
teams.map((team: Team) => {
team.banner_url = locals.pb.files.getURL(team, team.banner);
team.logo_url = locals.pb.files.getURL(team, team.logo);
});
return teams;
};
// TODO: Duplicated code from racepicks/+page.server.ts and data/raceresults/+page.server.ts
const fetch_drivers = async (): Promise<Driver[]> => {
const drivers: Driver[] = await locals.pb.collection("drivers").getFullList({
sort: "+code",
fetch: fetch,
});
drivers.map((driver: Driver) => {
driver.headshot_url = locals.pb.files.getURL(driver, driver.headshot);
});
return drivers;
};
// TODO: Duplicated code from racepicks/+page.server.ts and data/raceresults/+page.server.ts
const fetch_races = async (): Promise<Race[]> => {
const races: Race[] = await locals.pb.collection("races").getFullList({
sort: "+step",
fetch: fetch,
});
races.map((race: Race) => {
race.pictogram_url = locals.pb.files.getURL(race, race.pictogram);
});
return races;
};
const fetch_substitutions = async (): Promise<Substitution[]> => {
const substitutions: Substitution[] = await locals.pb.collection("substitutions").getFullList({
expand: "race",
fetch: fetch,
});
// Sort by race step (ascending)
substitutions.sort((a, b) => a.expand.race.step - b.expand.race.step);
return substitutions;
};
return {
// Graphics and teams are awaited, since those are visible on page load.
graphics: await fetch_graphics(),
teams: await fetch_teams(),
// The rest is streamed gradually, since the user has to switch pages to need them.
drivers: fetch_drivers(),
races: fetch_races(),
substitutions: fetch_substitutions(),
};
};

View File

@ -1,6 +1,6 @@
<script lang="ts">
import { Button, type TableColumn, Table } from "$lib/components";
import { get_by_value } from "$lib/database";
import { get_by_value, get_driver_headshot_template } from "$lib/database";
import type { Driver, Team } from "$lib/schema";
import { getModalStore, type ModalSettings, type ModalStore } from "@skeletonlabs/skeleton";
import type { PageData } from "./$types";
@ -31,7 +31,7 @@
data_value_name: "team",
label: "Team",
valuefun: async (value: string): Promise<string> => {
const team: Team | undefined = get_by_value(data.teams, "id", value);
const team: Team | undefined = get_by_value(await data.teams, "id", value);
return team
? `<span class='badge border mr-2' style='color: ${team.color}; background: ${team.color};'>C</span>${team.name}`
: "<span class='badge variant-filled-primary'>Invalid</span>";
@ -57,7 +57,7 @@
component: "driverCard",
meta: {
driver: driver,
teams: data.teams,
teams: await data.teams,
team_select_value: update_driver_team_select_values[driver.id],
active_value: update_driver_active_values[driver.id],
disable_inputs: !data.admin,
@ -72,13 +72,12 @@
type: "component",
component: "driverCard",
meta: {
teams: data.teams,
teams: await data.teams,
team_select_value: update_driver_team_select_values["create"],
active_value: update_driver_active_values["create"],
disable_inputs: !data.admin,
require_inputs: true,
headshot_template:
get_by_value(data.graphics, "name", "driver_headshot_template")?.file_url ?? "Invalid",
headshot_template: get_driver_headshot_template(await data.graphics),
},
};

View File

@ -2,7 +2,7 @@
import { Button, Table, type TableColumn } from "$lib/components";
import { getModalStore, type ModalSettings, type ModalStore } from "@skeletonlabs/skeleton";
import type { PageData } from "./$types";
import { get_by_value } from "$lib/database";
import { get_by_value, get_race_pictogram_template } from "$lib/database";
import type { Race } from "$lib/schema";
let { data }: { data: PageData } = $props();
@ -62,8 +62,7 @@
meta: {
disable_inputs: !data.admin,
require_inputs: true,
pictogram_template:
get_by_value(data.graphics, "name", "race_pictogram_template")?.file_url ?? "Invalid",
pictogram_template: get_race_pictogram_template(await data.graphics),
},
};

View File

@ -1,12 +1,13 @@
<script lang="ts">
import { get_by_value } from "$lib/database";
import { get_by_value, get_driver_headshot_template } from "$lib/database";
import { getModalStore, type ModalSettings, type ModalStore } from "@skeletonlabs/skeleton";
import type { PageData } from "./$types";
import type { Race, Substitution } from "$lib/schema";
import { Button, Table, type DropdownOption, type TableColumn } from "$lib/components";
import { Button, Table, type TableColumn } from "$lib/components";
let { data }: { data: PageData } = $props();
// TODO: Cleanup
const update_substitution_substitute_select_values: { [key: string]: string } = $state({});
const update_substitution_for_select_values: { [key: string]: string } = $state({});
const update_substitution_race_select_values: { [key: string]: string } = $state({});
@ -85,8 +86,7 @@
disable_inputs: !data.admin,
race_select_value: update_substitution_race_select_values["create"],
require_inputs: true,
headshot_template:
get_by_value(data.graphics, "name", "driver_headshot_template")?.file_url ?? "Invalid",
headshot_template: get_driver_headshot_template(await data.graphics),
},
};

View File

@ -3,7 +3,7 @@
import type { Team } from "$lib/schema";
import { getModalStore, type ModalSettings, type ModalStore } from "@skeletonlabs/skeleton";
import type { PageData } from "./$types";
import { get_by_value } from "$lib/database";
import { get_by_value, get_team_banner_template, get_team_logo_template } from "$lib/database";
let { data }: { data: PageData } = $props();
@ -25,7 +25,7 @@
const modalStore: ModalStore = getModalStore();
const teams_handler = async (event: Event, id: string) => {
const team: Team | undefined = get_by_value(data.teams, "id", id);
const team: Team | undefined = get_by_value(await data.teams, "id", id);
if (!team) return;
const modalSettings: ModalSettings = {
@ -40,15 +40,13 @@
modalStore.trigger(modalSettings);
};
const create_team_handler = (event: Event) => {
const create_team_handler = async (event: Event) => {
const modalSettings: ModalSettings = {
type: "component",
component: "teamCard",
meta: {
banner_template:
get_by_value(data.graphics, "name", "team_banner_template")?.file_url ?? "Invalid",
logo_template:
get_by_value(data.graphics, "name", "team_logo_template")?.file_url ?? "Invalid",
banner_template: get_team_banner_template(await data.graphics),
logo_template: get_team_logo_template(await data.graphics),
require_inputs: true,
disable_inputs: !data.admin,
},
@ -63,4 +61,6 @@
<span class="font-bold">Create New Team</span>
</Button>
</div>
<Table data={data.teams} columns={teams_columns} handler={teams_handler} />
{#await data.teams then teams}
<Table data={teams} columns={teams_columns} handler={teams_handler} />
{/await}