From 961cc6e5aee6e837c9b445f9ee3dd8b79108e404 Mon Sep 17 00:00:00 2001 From: redglow Date: Sun, 21 Jun 2026 14:48:33 +0200 Subject: [PATCH] feat: delete implementation --- src/client/hooks.ts | 26 ++++++++++++++++++++++++++ src/client/query-client.ts | 10 ++++++++-- src/client/routes/add/index.tsx | 2 +- src/client/routes/index.tsx | 33 ++++++++++++++++++--------------- src/client/services.ts | 7 +++++++ src/server/main.ts | 15 ++++++++++++++- 6 files changed, 74 insertions(+), 19 deletions(-) create mode 100644 src/client/hooks.ts diff --git a/src/client/hooks.ts b/src/client/hooks.ts new file mode 100644 index 0000000..cde15d7 --- /dev/null +++ b/src/client/hooks.ts @@ -0,0 +1,26 @@ +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { deleteAnnouncement, getAnnouncements } from "./services"; + +const announcementsQueryKey = ["announcements"]; + +export function useAnnouncementsQuery() { + const announcementsQuery = useQuery({ + queryKey: announcementsQueryKey, + queryFn: getAnnouncements, + retry: false, + }); + + return announcementsQuery; +} + +export function useDeleteAnnouncementMutation() { + const queryClient = useQueryClient(); + return useMutation({ + async mutationFn(announcementId: number) { + await deleteAnnouncement(announcementId); + }, + async onSuccess() { + await queryClient.invalidateQueries({ queryKey: ["announcements"] }); + }, + }); +} diff --git a/src/client/query-client.ts b/src/client/query-client.ts index 6c7b9de..0d46fb1 100644 --- a/src/client/query-client.ts +++ b/src/client/query-client.ts @@ -1,3 +1,9 @@ -import { QueryClient } from "@tanstack/react-query"; +import { QueryCache, QueryClient } from "@tanstack/react-query"; -export const queryClient = new QueryClient(); +export const queryClient = new QueryClient({ + queryCache: new QueryCache({ + onError(error) { + console.error(error); + }, + }), +}); diff --git a/src/client/routes/add/index.tsx b/src/client/routes/add/index.tsx index 4dc1d37..2d700ad 100644 --- a/src/client/routes/add/index.tsx +++ b/src/client/routes/add/index.tsx @@ -32,7 +32,7 @@ function RouteComponent() { { - if (announcementsQuery.error) { - console.log(announcementsQuery.error); - } - }, [announcementsQuery.error]); + const deleteAnnouncementMutation = useDeleteAnnouncementMutation(); // delete confirmation dialog status const [ @@ -40,6 +30,7 @@ function RouteComponent() { announcementForLatestDeleteAttempt, setAnnouncementForLatestDeleteAttempt, ] = useState(undefined); + const openDeleteConfirmation = useCallback( (a: Announcement) => { openDeleteConfirmationDialog(); @@ -48,6 +39,18 @@ function RouteComponent() { [openDeleteConfirmationDialog], ); + const confirmDelete = useCallback(() => { + closeDeleteConfirmation(); + if (announcementForLatestDeleteAttempt === undefined) { + throw new Error("Cannot delete announcement without an attempt set."); + } + deleteAnnouncementMutation.mutate(announcementForLatestDeleteAttempt?.id); + }, [ + announcementForLatestDeleteAttempt, + closeDeleteConfirmation, + deleteAnnouncementMutation.mutate, + ]); + // handle loading / error / show values return ( @@ -72,7 +75,7 @@ function RouteComponent() { {announcementForLatestDeleteAttempt && ( diff --git a/src/client/services.ts b/src/client/services.ts index 3fcc13c..00e731a 100644 --- a/src/client/services.ts +++ b/src/client/services.ts @@ -18,3 +18,10 @@ export async function getAnnouncements() { const announcements = Announcements.parse(data); return announcements; } + +export async function deleteAnnouncement(announcementId: number) { + const response = await fetch(`/api/announcements/${announcementId}`, { + method: "DELETE", + }); + await response.text(); +} diff --git a/src/server/main.ts b/src/server/main.ts index 5c0a36c..f4e89f8 100644 --- a/src/server/main.ts +++ b/src/server/main.ts @@ -21,11 +21,15 @@ const AddInput = z.object({ }), }); +const DeleteInput = z.object({ + announcementId: z.coerce.number().positive(), +}); + app.get("/hello", (_, res) => { res.send("Hello Vite + React + TypeScript!"); }); -app.post("/api/announcements/add", upload.single("image"), async (req, res) => { +app.post("/api/announcements", upload.single("image"), async (req, res) => { const addInput = AddInput.parse(req); await db .insertInto("announcements") @@ -49,6 +53,15 @@ app.get("/api/announcements", async (_, res) => { res.json(announcements); }); +app.delete("/api/announcements/:announcementId", async (req, res) => { + const { announcementId } = DeleteInput.parse(req.params); + await db + .deleteFrom("announcements") + .where("id", "=", announcementId) + .execute(); + res.status(204).send(); +}); + ViteExpress.listen(app, 3000, () => console.log("Server is listening on port 3000..."), );