Compare commits

..

4 Commits

7 changed files with 254 additions and 70 deletions

153
package-lock.json generated
View File

@ -19,6 +19,7 @@
"@skeletonlabs/skeleton": "^2.10.3", "@skeletonlabs/skeleton": "^2.10.3",
"@skeletonlabs/tw-plugin": "^0.4.0", "@skeletonlabs/tw-plugin": "^0.4.0",
"@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/adapter-node": "^5.2.10",
"@sveltejs/kit": "^2.9.0", "@sveltejs/kit": "^2.9.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0", "@sveltejs/vite-plugin-svelte": "^5.0.0",
"@tailwindcss/forms": "^0.5.9", "@tailwindcss/forms": "^0.5.9",
@ -1094,6 +1095,112 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@rollup/plugin-commonjs": {
"version": "28.0.2",
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.2.tgz",
"integrity": "sha512-BEFI2EDqzl+vA1rl97IDRZ61AIwGH093d9nz8+dThxJNH8oSoB7MjWvPCX3dkaK1/RCJ/1v/R1XB15FuSs0fQw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
"commondir": "^1.0.1",
"estree-walker": "^2.0.2",
"fdir": "^6.2.0",
"is-reference": "1.2.1",
"magic-string": "^0.30.3",
"picomatch": "^4.0.2"
},
"engines": {
"node": ">=16.0.0 || 14 >= 14.17"
},
"peerDependencies": {
"rollup": "^2.68.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/plugin-commonjs/node_modules/is-reference": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
"integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "*"
}
},
"node_modules/@rollup/plugin-json": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz",
"integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rollup/pluginutils": "^5.1.0"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/plugin-node-resolve": {
"version": "15.3.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.1.tgz",
"integrity": "sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
"@types/resolve": "1.20.2",
"deepmerge": "^4.2.2",
"is-module": "^1.0.0",
"resolve": "^1.22.1"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^2.78.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/pluginutils": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz",
"integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.0",
"estree-walker": "^2.0.2",
"picomatch": "^4.0.2"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/rollup-android-arm-eabi": { "node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.28.1", "version": "4.28.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.28.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.28.1.tgz",
@ -1403,6 +1510,22 @@
"@sveltejs/kit": "^2.0.0" "@sveltejs/kit": "^2.0.0"
} }
}, },
"node_modules/@sveltejs/adapter-node": {
"version": "5.2.10",
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.2.10.tgz",
"integrity": "sha512-U0SCdULhHbSYCDgvvrHRtKUykl9GZkM/b3NyeIUtaxM39upQFd5059pWmXgTNaNTU1HMdj4xx0xvNAvQcIzmXQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rollup/plugin-commonjs": "^28.0.1",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.3.0",
"rollup": "^4.9.5"
},
"peerDependencies": {
"@sveltejs/kit": "^2.4.0"
}
},
"node_modules/@sveltejs/kit": { "node_modules/@sveltejs/kit": {
"version": "2.9.0", "version": "2.9.0",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.9.0.tgz", "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.9.0.tgz",
@ -1582,6 +1705,13 @@
"undici-types": "~6.20.0" "undici-types": "~6.20.0"
} }
}, },
"node_modules/@types/resolve": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/unist": { "node_modules/@types/unist": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
@ -2031,6 +2161,13 @@
"node": ">= 12.0.0" "node": ">= 12.0.0"
} }
}, },
"node_modules/commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
"dev": true,
"license": "MIT"
},
"node_modules/cookie": { "node_modules/cookie": {
"version": "0.6.0", "version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
@ -2315,6 +2452,13 @@
"@types/estree": "^1.0.1" "@types/estree": "^1.0.1"
} }
}, },
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"dev": true,
"license": "MIT"
},
"node_modules/fast-glob": { "node_modules/fast-glob": {
"version": "3.3.2", "version": "3.3.2",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
@ -2589,6 +2733,13 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/is-module": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
"integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
"dev": true,
"license": "MIT"
},
"node_modules/is-number": { "node_modules/is-number": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@ -3485,8 +3636,6 @@
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"optional": true,
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },

View File

@ -14,6 +14,7 @@
"@skeletonlabs/skeleton": "^2.10.3", "@skeletonlabs/skeleton": "^2.10.3",
"@skeletonlabs/tw-plugin": "^0.4.0", "@skeletonlabs/tw-plugin": "^0.4.0",
"@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/adapter-node": "^5.2.10",
"@sveltejs/kit": "^2.9.0", "@sveltejs/kit": "^2.9.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0", "@sveltejs/vite-plugin-svelte": "^5.0.0",
"@tailwindcss/forms": "^0.5.9", "@tailwindcss/forms": "^0.5.9",

View File

@ -8,6 +8,8 @@ import PocketBase from "pocketbase";
// most recent authentication data. // most recent authentication data.
// The authenticated PocketBase client will be available in all *.server.ts files. // The authenticated PocketBase client will be available in all *.server.ts files.
export const handle: Handle = async ({ event, resolve }) => { export const handle: Handle = async ({ event, resolve }) => {
const requestStartTime: number = Date.now();
event.locals.pb = new PocketBase("http://192.168.86.50:8090"); event.locals.pb = new PocketBase("http://192.168.86.50:8090");
// Load the most recent authentication data from a cookie (is updated below) // Load the most recent authentication data from a cookie (is updated below)
@ -45,6 +47,15 @@ export const handle: Handle = async ({ event, resolve }) => {
// Resolve the request. This is what happens by default. // Resolve the request. This is what happens by default.
const response = await resolve(event); const response = await resolve(event);
console.log(
"=====\n",
`Request Date: ${new Date(requestStartTime).toISOString()}\n`,
`Method: ${event.request.method}\n`,
`Path: ${event.url.pathname}\n`,
`Duration: ${Date.now() - requestStartTime}ms\n`,
`Status: ${response.status}`,
);
// Store the current authentication data to a cookie, so it can be loaded above. // Store the current authentication data to a cookie, so it can be loaded above.
response.headers.set("set-cookie", event.locals.pb.authStore.exportToCookie({ secure: false })); response.headers.set("set-cookie", event.locals.pb.authStore.exportToCookie({ secure: false }));

View File

@ -36,7 +36,7 @@
{#await loadImage(src)} {#await loadImage(src)}
<!-- Loading... --> <!-- Loading... -->
{:then data} {:then data}
<img src={data} {...restProps} /> <img src={data} style="width: 100%" {...restProps} />
{/await} {/await}
{/if} {/if}
</div> </div>

View File

@ -1,4 +1,4 @@
import type { Actions, PageServerLoad } from "./$types"; import type { ActionData, Actions, PageServerLoad } from "./$types";
import { import {
form_data_clean, form_data_clean,
form_data_ensure_keys, form_data_ensure_keys,
@ -286,10 +286,13 @@ export const load: PageServerLoad = async ({ fetch, locals }) => {
}; };
return { return {
// Graphics and teams are awaited, since those are visible on page load.
graphics: await fetch_graphics(), graphics: await fetch_graphics(),
teams: await fetch_teams(), teams: await fetch_teams(),
drivers: await fetch_drivers(),
races: await fetch_races(), // The rest is streamed gradually, since the user has to switch tabs to need them.
substitutions: await fetch_substitutions(), drivers: fetch_drivers(),
races: fetch_races(),
substitutions: fetch_substitutions(),
}; };
}; };

View File

@ -17,47 +17,57 @@
} }
// Values for driver cards // Values for driver cards
// Maps driver to team: <driver.id, team.id> let update_driver_team_select_values: { [key: string]: string } = $state({}); // <driver.id, team.id>
let update_driver_team_select_values: { [key: string]: string } = $state({});
let update_driver_active_values: { [key: string]: boolean } = $state({}); let update_driver_active_values: { [key: string]: boolean } = $state({});
data.drivers.forEach((driver: Driver) => { data.drivers.then((drivers: Driver[]) =>
update_driver_team_select_values[driver.id] = driver.team; drivers.forEach((driver: Driver) => {
update_driver_active_values[driver.id] = driver.active; update_driver_team_select_values[driver.id] = driver.team;
}); update_driver_active_values[driver.id] = driver.active;
}),
);
update_driver_team_select_values["create"] = ""; update_driver_team_select_values["create"] = "";
update_driver_active_values["create"] = true; update_driver_active_values["create"] = true;
// Values for substitution cards // Values for substitution cards
let update_substitution_substitute_select_values: { [key: string]: string } = $state({}); const update_substitution_substitute_select_values: { [key: string]: string } = $state({});
let update_substitution_for_select_values: { [key: string]: string } = $state({}); const update_substitution_for_select_values: { [key: string]: string } = $state({});
let update_substitution_race_select_values: { [key: string]: string } = $state({}); const update_substitution_race_select_values: { [key: string]: string } = $state({});
data.substitutions.forEach((substitution: Substitution) => { data.substitutions.then((substitutions: Substitution[]) =>
update_substitution_substitute_select_values[substitution.id] = substitution.substitute; substitutions.forEach((substitution: Substitution) => {
update_substitution_for_select_values[substitution.id] = substitution.for; update_substitution_substitute_select_values[substitution.id] = substitution.substitute;
update_substitution_race_select_values[substitution.id] = substitution.race; update_substitution_for_select_values[substitution.id] = substitution.for;
}); update_substitution_race_select_values[substitution.id] = substitution.race;
}),
);
update_substitution_substitute_select_values["create"] = ""; update_substitution_substitute_select_values["create"] = "";
update_substitution_for_select_values["create"] = ""; update_substitution_for_select_values["create"] = "";
update_substitution_race_select_values["create"] = ""; update_substitution_race_select_values["create"] = "";
// All options to create a <Dropdown> component for the teams // All options to create a <Dropdown> component for the teams
const team_dropdown_options: DropdownOption[] = data.teams.map((team: Team) => { const team_dropdown_options: DropdownOption[] = [];
return { label: team.name, value: team.id, icon_url: team.logo_url } as DropdownOption; data.teams.forEach((team: Team) => {
team_dropdown_options.push({ label: team.name, value: team.id, icon_url: team.logo_url });
}); });
// All options to create a <Dropdown> component for the drivers // All options to create a <Dropdown> component for the drivers
const driver_dropdown_options: DropdownOption[] = data.drivers.map((driver: Driver) => { const driver_dropdown_options: DropdownOption[] = [];
return { data.drivers.then((drivers: Driver[]) =>
label: driver.code, drivers.forEach((driver: Driver) => {
value: driver.id, driver_dropdown_options.push({
icon_url: driver.headshot_url, label: driver.code,
} as DropdownOption; value: driver.id,
}); icon_url: driver.headshot_url,
});
}),
);
// All options to create a <Dropdown> component for the races // All options to create a <Dropdown> component for the races
const race_dropdown_options: DropdownOption[] = data.races.map((race: Race) => { const race_dropdown_options: DropdownOption[] = [];
return { label: race.name, value: race.id } as DropdownOption; data.races.then((races: Race[]) =>
}); races.forEach((race: Race) => {
race_dropdown_options.push({ label: race.name, value: race.id });
}),
);
</script> </script>
<svelte:head> <svelte:head>
@ -116,15 +126,17 @@
{/if} {/if}
<!-- List all drivers inside the database --> <!-- List all drivers inside the database -->
{#each data.drivers as driver} {#await data.drivers then drivers}
<DriverCard {#each drivers as driver}
{driver} <DriverCard
disable_inputs={!data.admin} {driver}
team_select_value={update_driver_team_select_values[driver.id]} disable_inputs={!data.admin}
team_select_options={team_dropdown_options} team_select_value={update_driver_team_select_values[driver.id]}
active_value={update_driver_active_values[driver.id]} team_select_options={team_dropdown_options}
/> active_value={update_driver_active_values[driver.id]}
{/each} />
{/each}
{/await}
</div> </div>
{:else if current_tab === 2} {:else if current_tab === 2}
<!-- Races Tab --> <!-- Races Tab -->
@ -138,40 +150,48 @@
/> />
{/if} {/if}
{#each data.races as race} {#await data.races then races}
<RaceCard {race} disable_inputs={!data.admin} /> {#each races as race}
{/each} <RaceCard {race} disable_inputs={!data.admin} />
{/each}
{/await}
</div> </div>
{:else if current_tab === 3} {:else if current_tab === 3}
<!-- Substitutions Tab --> <!-- Substitutions Tab -->
<!-- Substitutions Tab --> <!-- Substitutions Tab -->
<!-- Substitutions Tab --> <!-- Substitutions Tab -->
<div class="mt-2 grid grid-cols-1 gap-2 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6"> <div class="mt-2 grid grid-cols-1 gap-2 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6">
{#if data.admin} {#await data.drivers then drivers}
<SubstitutionCard {#if data.admin}
drivers={data.drivers} <SubstitutionCard
substitute_select_value={update_substitution_substitute_select_values["create"]} {drivers}
driver_select_value={update_substitution_for_select_values["create"]} substitute_select_value={update_substitution_substitute_select_values["create"]}
race_select_value={update_substitution_race_select_values["create"]} driver_select_value={update_substitution_for_select_values["create"]}
driver_select_options={driver_dropdown_options} race_select_value={update_substitution_race_select_values["create"]}
race_select_options={race_dropdown_options} driver_select_options={driver_dropdown_options}
headshot_template={get_by_value(data.graphics, "name", "driver_template")?.file_url} race_select_options={race_dropdown_options}
require_inputs headshot_template={get_by_value(data.graphics, "name", "driver_template")?.file_url}
/> require_inputs
{/if} />
{/if}
{#each data.substitutions as substitution} {#await data.substitutions then substitutions}
<SubstitutionCard {#each substitutions as substitution}
{substitution} <SubstitutionCard
drivers={data.drivers} {substitution}
substitute_select_value={update_substitution_substitute_select_values[substitution.id]} {drivers}
driver_select_value={update_substitution_for_select_values[substitution.id]} substitute_select_value={update_substitution_substitute_select_values[
race_select_value={update_substitution_race_select_values[substitution.id]} substitution.id
driver_select_options={driver_dropdown_options} ]}
race_select_options={race_dropdown_options} driver_select_value={update_substitution_for_select_values[substitution.id]}
disable_inputs={!data.admin} race_select_value={update_substitution_race_select_values[substitution.id]}
/> driver_select_options={driver_dropdown_options}
{/each} race_select_options={race_dropdown_options}
disable_inputs={!data.admin}
/>
{/each}
{/await}
{/await}
</div> </div>
{/if} {/if}
</svelte:fragment> </svelte:fragment>

View File

@ -1,4 +1,4 @@
import adapter from "@sveltejs/adapter-auto"; import adapter from "@sveltejs/adapter-node";
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
/** @type {import('@sveltejs/kit').Config} */ /** @type {import('@sveltejs/kit').Config} */