Compare commits

...

8 Commits

5 changed files with 57 additions and 28 deletions

View File

@ -11,7 +11,7 @@
import type { Race } from "$lib/schema"; import type { Race } from "$lib/schema";
import { RACE_PICTOGRAM_HEIGHT, RACE_PICTOGRAM_WIDTH } from "$lib/config"; import { RACE_PICTOGRAM_HEIGHT, RACE_PICTOGRAM_WIDTH } from "$lib/config";
import { get_race_pictogram_template } from "$lib/database"; import { get_race_pictogram_template } from "$lib/database";
import { format_date } from "$lib/date"; import { format_date, isodatetimeformat } from "$lib/date";
import { get_error_toast } from "$lib/toast"; import { get_error_toast } from "$lib/toast";
import { pb } from "$lib/pocketbase"; import { pb } from "$lib/pocketbase";
import { error } from "@sveltejs/kit"; import { error } from "@sveltejs/kit";
@ -38,12 +38,14 @@
const toastStore: ToastStore = getToastStore(); const toastStore: ToastStore = getToastStore();
// Constants // Constants
const labelwidth = "80px"; const labelwidth = "85px";
const dateformat: string = "yyyy-MM-dd'T'HH:mm";
const clear_sprint = () => { const clear_sprint = () => {
(document.getElementById("sqdate") as HTMLInputElement).value = ""; (document.getElementById("sqdate") as HTMLInputElement).value = "";
(document.getElementById("sdate") as HTMLInputElement).value = ""; (document.getElementById("sdate") as HTMLInputElement).value = "";
sprintqualidate_value = "";
sprintdate_value = "";
}; };
// Reactive state // Reactive state
@ -58,14 +60,16 @@
let racedate_value: string = $state(""); let racedate_value: string = $state("");
let pictogram_value: FileList | undefined = $state(); let pictogram_value: FileList | undefined = $state();
// Set the initial values if we've clicked on an existing race
// PocketBase stores in UTC, so resulting values will be offset by 1h here
if (race) { if (race) {
if (race.sprintqualidate && race.sprintdate) { if (race.sprintqualidate && race.sprintdate) {
sprintqualidate_value = format_date(race.sprintqualidate, dateformat); sprintqualidate_value = format_date(race.sprintqualidate, isodatetimeformat);
sprintdate_value = format_date(race.sprintdate, dateformat); sprintdate_value = format_date(race.sprintdate, isodatetimeformat);
} }
qualidate_value = format_date(race.qualidate, dateformat); qualidate_value = format_date(race.qualidate, isodatetimeformat);
racedate_value = format_date(race.racedate, dateformat); racedate_value = format_date(race.racedate, isodatetimeformat);
} }
// Database actions // Database actions
@ -119,6 +123,7 @@
} }
} }
// Use toISOString here, as we want to convert from localtime to UTC, which PocketBase uses
const race_data = { const race_data = {
name: name_value, name: name_value,
step: step_value, step: step_value,
@ -126,11 +131,11 @@
sprintqualidate: sprintqualidate:
sprintqualidate_value && sprintqualidate_value !== "" sprintqualidate_value && sprintqualidate_value !== ""
? new Date(sprintqualidate_value).toISOString() ? new Date(sprintqualidate_value).toISOString()
: undefined, : null,
sprintdate: sprintdate:
sprintdate_value && sprintdate_value != "" sprintdate_value && sprintdate_value != ""
? new Date(sprintdate_value).toISOString() ? new Date(sprintdate_value).toISOString()
: undefined, : null,
qualidate: new Date(qualidate_value).toISOString(), qualidate: new Date(qualidate_value).toISOString(),
racedate: new Date(racedate_value).toISOString(), racedate: new Date(racedate_value).toISOString(),
pictogram: pictogram_avif, pictogram: pictogram_avif,
@ -214,9 +219,9 @@
<!-- NOTE: Input datetime-local accepts YYYY-mm-ddTHH:MM format --> <!-- NOTE: Input datetime-local accepts YYYY-mm-ddTHH:MM format -->
<Input <Input
id="sqdate" id="sqdate"
type="datetime-local"
bind:value={sprintqualidate_value} bind:value={sprintqualidate_value}
autocomplete="off" autocomplete="off"
type="datetime-local"
{labelwidth} {labelwidth}
{disabled} {disabled}
> >
@ -224,18 +229,18 @@
</Input> </Input>
<Input <Input
id="sdate" id="sdate"
type="datetime-local"
bind:value={sprintdate_value} bind:value={sprintdate_value}
autocomplete="off" autocomplete="off"
type="datetime-local"
{labelwidth} {labelwidth}
{disabled} {disabled}
> >
SRace SRace
</Input> </Input>
<Input <Input
type="datetime-local"
bind:value={qualidate_value} bind:value={qualidate_value}
autocomplete="off" autocomplete="off"
type="datetime-local"
{labelwidth} {labelwidth}
{disabled} {disabled}
{required} {required}
@ -243,9 +248,9 @@
Quali Quali
</Input> </Input>
<Input <Input
type="datetime-local"
bind:value={racedate_value} bind:value={racedate_value}
autocomplete="off" autocomplete="off"
type="datetime-local"
{labelwidth} {labelwidth}
{disabled} {disabled}
{required} {required}

View File

@ -26,7 +26,7 @@
<div class="input-group input-group-divider grid-cols-[auto_1fr_auto]"> <div class="input-group input-group-divider grid-cols-[auto_1fr_auto]">
<div <div
class="input-group-shim select-none text-nowrap text-neutral-900" class="input-group-shim select-none text-nowrap border-r text-neutral-900"
style="width: {labelwidth};" style="width: {labelwidth};"
> >
{@render children()} {@render children()}

View File

@ -1,4 +1,28 @@
import { format } from "date-fns"; import { format } from "date-fns";
export const format_date = (date: string, formatstring: string): string => /**
* 2025-03-28T17:35
*/
export const isodatetimeformat: string = "yyyy-MM-dd'T'HH:mm";
/**
* 28.03. 17:35
*/
export const shortdatetimeformat: string = "dd.MM.' 'HH:mm";
/**
* 2025-03-28
*/
export const isodateformat: string = "yyyy-MM-dd";
/**
* 17:35
*/
export const timeformat: string = "HH:mm";
/**
* Format a [Date] object using a [date-fns] formatstring.
* This function uses localtime instead of UTC.
*/
export const format_date = <T extends Date | string>(date: T, formatstring: string): string =>
format(new Date(date), formatstring); format(new Date(date), formatstring);

View File

@ -4,6 +4,7 @@
import type { PageData } from "./$types"; import type { PageData } from "./$types";
import { get_by_value } from "$lib/database"; import { get_by_value } from "$lib/database";
import type { Race } from "$lib/schema"; import type { Race } from "$lib/schema";
import { format_date, shortdatetimeformat } from "$lib/date";
let { data }: { data: PageData } = $props(); let { data }: { data: PageData } = $props();
@ -26,7 +27,7 @@
modalStore.trigger(modalSettings); modalStore.trigger(modalSettings);
}; };
// TODO: Displayed dates differ from actual dates by 1 hour // The date value functions convert UTC from PocketBase to localtime
const races_columns: TableColumn[] = $derived([ const races_columns: TableColumn[] = $derived([
{ {
data_value_name: "name", data_value_name: "name",
@ -38,22 +39,22 @@
{ {
data_value_name: "sprintqualidate", data_value_name: "sprintqualidate",
label: "Sprint Quali", label: "Sprint Quali",
valuefun: async (value: string): Promise<string> => value.slice(0, -5), // Cutoff the "Z" from the ISO datetime valuefun: async (value: string): Promise<string> => format_date(value, shortdatetimeformat),
}, },
{ {
data_value_name: "sprintdate", data_value_name: "sprintdate",
label: "Sprint Race", label: "Sprint Race",
valuefun: async (value: string): Promise<string> => value.slice(0, -5), valuefun: async (value: string): Promise<string> => format_date(value, shortdatetimeformat),
}, },
{ {
data_value_name: "qualidate", data_value_name: "qualidate",
label: "Quali", label: "Quali",
valuefun: async (value: string): Promise<string> => value.slice(0, -5), valuefun: async (value: string): Promise<string> => format_date(value, shortdatetimeformat),
}, },
{ {
data_value_name: "racedate", data_value_name: "racedate",
label: "Race", label: "Race",
valuefun: async (value: string): Promise<string> => value.slice(0, -5), valuefun: async (value: string): Promise<string> => format_date(value, shortdatetimeformat),
}, },
]); ]);
</script> </script>

View File

@ -19,7 +19,7 @@
RACE_PICTOGRAM_HEIGHT, RACE_PICTOGRAM_HEIGHT,
RACE_PICTOGRAM_WIDTH, RACE_PICTOGRAM_WIDTH,
} from "$lib/config"; } from "$lib/config";
import { format_date } from "$lib/date"; import { format_date, shortdatetimeformat } from "$lib/date";
import type { CurrentPickedUser, RacePick } from "$lib/schema"; import type { CurrentPickedUser, RacePick } from "$lib/schema";
import { get_by_value, get_driver_headshot_template } from "$lib/database"; import { get_by_value, get_driver_headshot_template } from "$lib/database";
@ -53,8 +53,6 @@
), ),
); );
const dateformat: string = "dd.MM' 'HH:mm";
const race_popupsettings = (target: string): PopupSettings => { const race_popupsettings = (target: string): PopupSettings => {
return { return {
event: "click", event: "click",
@ -87,20 +85,21 @@
{#if data.currentrace.sprintdate} {#if data.currentrace.sprintdate}
<div class="flex gap-2"> <div class="flex gap-2">
<span class="w-12">SQuali:</span> <span class="w-12">SQuali:</span>
<span>{format_date(data.currentrace.sprintqualidate, dateformat)}</span> <span>{format_date(data.currentrace.sprintqualidate, shortdatetimeformat)}</span
>
</div> </div>
<div class="flex gap-2"> <div class="flex gap-2">
<span class="w-12">SRace:</span> <span class="w-12">SRace:</span>
<span>{format_date(data.currentrace.sprintdate, dateformat)}</span> <span>{format_date(data.currentrace.sprintdate, shortdatetimeformat)}</span>
</div> </div>
{/if} {/if}
<div class="flex gap-2"> <div class="flex gap-2">
<span class="w-12">Quali:</span> <span class="w-12">Quali:</span>
<span>{format_date(data.currentrace.qualidate, dateformat)}</span> <span>{format_date(data.currentrace.qualidate, shortdatetimeformat)}</span>
</div> </div>
<div class="flex gap-2"> <div class="flex gap-2">
<span class="w-12">Race:</span> <span class="w-12">Race:</span>
<span>{format_date(data.currentrace.racedate, dateformat)}</span> <span>{format_date(data.currentrace.racedate, shortdatetimeformat)}</span>
</div> </div>
<div class="m-auto flex"> <div class="m-auto flex">
<div class="mr-1 mt-1"> <div class="mr-1 mt-1">
@ -255,7 +254,7 @@
{race?.step}: {race?.name} {race?.step}: {race?.name}
</span> </span>
<span class="hidden text-sm lg:block"> <span class="hidden text-sm lg:block">
Date: {format_date(race?.racedate ?? "", dateformat)} Date: {format_date(race?.racedate ?? "", shortdatetimeformat)}
</span> </span>
<span class="hidden text-sm lg:block">Guessed: P{race?.pxx}</span> <span class="hidden text-sm lg:block">Guessed: P{race?.pxx}</span>