diff --git a/client/public/icons.svg b/client/public/icons.svg index 6b4a3cf..1eb082d 100644 --- a/client/public/icons.svg +++ b/client/public/icons.svg @@ -64,4 +64,10 @@ + + + + + \ No newline at end of file diff --git a/client/src/Icon.tsx b/client/src/Icon.tsx index 4dcab55..29510af 100644 --- a/client/src/Icon.tsx +++ b/client/src/Icon.tsx @@ -15,6 +15,7 @@ export const ICON_ID = { ARROW_BACK: "arrow-back", OPEN_IN_NEW_WINDOW: "open-in-new-window", SAVE: "save", + DELETE: "delete", } as const; export type IconId = (typeof ICON_ID)[keyof typeof ICON_ID]; diff --git a/client/src/routes/_app/times/index.tsx b/client/src/routes/_app/times/index.tsx index 41b2873..a178f79 100644 --- a/client/src/routes/_app/times/index.tsx +++ b/client/src/routes/_app/times/index.tsx @@ -3,7 +3,7 @@ import { createFileRoute, Link } from "@tanstack/solid-router"; import { createSignal, For, type VoidProps } from "solid-js"; import Icon from "@/Icon"; -import { query, type Time, type TimeId } from "@/time"; +import { deleteTimes, query, updateTimes, type Time, type TimeId } from "@/time"; import { Title } from "@/Title"; import { idQuery, type UserId } from "@/user"; @@ -76,23 +76,23 @@ function Day(properties: VoidProps) { const dayId = properties.day.toString(); const editFormId = "edit-" + dayId; - type UpdateParameters = { - userId: UserId; - times: Time[]; - }; - // TODO optimistic update const updateMutation = useMutation(() => ({ - async mutationFn({ userId, times }: UpdateParameters) { - const response = await fetch(`/api/users/${userId}/times`, { - method: "PUT", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(times), - }); - - if (!response.ok) throw new Error("Error updating times"); + async mutationFn({ + userId, + times, + idsToDelete, + }: { + userId: UserId; + times: Time[]; + idsToDelete: TimeId[]; + }) { + console.debug("Updating times", { userId, times, idsToDelete }); + // Not very elegant but does the job for now + const updatePromise = times.length > 0 ? updateTimes({ userId, times }) : Promise.resolve(); + const deletePromise = + idsToDelete.length > 0 ? deleteTimes({ userId, timeIds: idsToDelete }) : Promise.resolve(); + await Promise.all([updatePromise, deletePromise]); }, onSettled(_data, _error, variables, _result, context) { setIsEdit(false); @@ -104,13 +104,23 @@ function Day(properties: VoidProps) { function handleEditSubmit(event: SubmitEvent & { currentTarget: HTMLFormElement }) { event.preventDefault(); + const idsToDelete: TimeId[] = []; const times: Time[] = []; for (const element of event.currentTarget.elements) { if (!(element instanceof HTMLFieldSetElement)) continue; const timeIdInput = element.elements.namedItem("time-id") as HTMLInputElement; - const timeId = timeIdInput.value; + const timeId = timeIdInput.value as TimeId; + + console.debug("ELEMENTS", element.elements); + const deleteInput = element.elements.namedItem(`delete-${timeId}`) as HTMLInputElement; + + if (deleteInput.checked) { + idsToDelete.push(timeId); + continue; + } + const startInput = element.elements.namedItem(`start-${timeId}`) as HTMLInputElement; const startTime = Temporal.PlainTime.from(startInput.value); const startInstant = properties.day @@ -124,15 +134,18 @@ function Day(properties: VoidProps) { .toInstant(); times.push({ - id: timeId as TimeId, + id: timeId, start: startInstant, end: endInstant, }); } + if (idsToDelete.length === 0 && times.length === 0) return; + updateMutation.mutate({ userId: properties.userId, times, + idsToDelete, }); } @@ -227,7 +240,7 @@ function Day(properties: VoidProps) {
); }} diff --git a/client/src/time.ts b/client/src/time.ts index 07d7063..79a0c84 100644 --- a/client/src/time.ts +++ b/client/src/time.ts @@ -1,5 +1,7 @@ import { queryOptions } from "@tanstack/solid-query"; +import type { UserId } from "./user"; + export type TimeId = string & { __brand: "TimeId" }; export type Time = { @@ -30,3 +32,32 @@ export const query = (userId: string | undefined, signal?: AbortSignal) => }, enabled: Boolean(userId), }); + +export type UpdateParameters = { + userId: UserId; + times: Time[]; +}; + +export async function updateTimes({ userId, times }: UpdateParameters) { + const response = await fetch(`/api/users/${userId}/times`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(times), + }); + + if (!response.ok) throw new Error("Error updating times"); +} + +export async function deleteTimes({ userId, timeIds }: { userId: UserId; timeIds: TimeId[] }) { + const response = await fetch(`/api/users/${userId}/times`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(timeIds), + }); + + if (!response.ok) throw new Error("Error deleting times"); +}