feat: delete implementation
This commit is contained in:
@@ -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"] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ function RouteComponent() {
|
|||||||
<Box
|
<Box
|
||||||
component="form"
|
component="form"
|
||||||
enctype="multipart/form-data"
|
enctype="multipart/form-data"
|
||||||
action="/api/announcements/add"
|
action="/api/announcements"
|
||||||
method="POST"
|
method="POST"
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
|||||||
+18
-15
@@ -1,31 +1,21 @@
|
|||||||
import { Alert, Box, CircularProgress, Typography } from "@mui/material";
|
import { Alert, Box, CircularProgress, Typography } from "@mui/material";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { AnnouncementTable } from "../components/AnnouncementTable";
|
import { AnnouncementTable } from "../components/AnnouncementTable";
|
||||||
import { DeleteAnnouncementDialog } from "../components/DeleteAnnouncementDialog";
|
import { DeleteAnnouncementDialog } from "../components/DeleteAnnouncementDialog";
|
||||||
import { RouterButton } from "../components/RouterButton";
|
import { RouterButton } from "../components/RouterButton";
|
||||||
import { useBoolean } from "../components/use-boolean";
|
import { useBoolean } from "../components/use-boolean";
|
||||||
import { type Announcement, getAnnouncements } from "../services";
|
import { useAnnouncementsQuery, useDeleteAnnouncementMutation } from "../hooks";
|
||||||
|
import type { Announcement } from "../services";
|
||||||
|
|
||||||
export const Route = createFileRoute("/")({
|
export const Route = createFileRoute("/")({
|
||||||
component: RouteComponent,
|
component: RouteComponent,
|
||||||
});
|
});
|
||||||
|
|
||||||
function RouteComponent() {
|
function RouteComponent() {
|
||||||
// the query to retrieve the announcements
|
const announcementsQuery = useAnnouncementsQuery();
|
||||||
const announcementsQuery = useQuery({
|
|
||||||
queryKey: ["announcements"],
|
|
||||||
queryFn: getAnnouncements,
|
|
||||||
retry: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
// log error only if we get one
|
const deleteAnnouncementMutation = useDeleteAnnouncementMutation();
|
||||||
useEffect(() => {
|
|
||||||
if (announcementsQuery.error) {
|
|
||||||
console.log(announcementsQuery.error);
|
|
||||||
}
|
|
||||||
}, [announcementsQuery.error]);
|
|
||||||
|
|
||||||
// delete confirmation dialog status
|
// delete confirmation dialog status
|
||||||
const [
|
const [
|
||||||
@@ -40,6 +30,7 @@ function RouteComponent() {
|
|||||||
announcementForLatestDeleteAttempt,
|
announcementForLatestDeleteAttempt,
|
||||||
setAnnouncementForLatestDeleteAttempt,
|
setAnnouncementForLatestDeleteAttempt,
|
||||||
] = useState<Announcement | undefined>(undefined);
|
] = useState<Announcement | undefined>(undefined);
|
||||||
|
|
||||||
const openDeleteConfirmation = useCallback(
|
const openDeleteConfirmation = useCallback(
|
||||||
(a: Announcement) => {
|
(a: Announcement) => {
|
||||||
openDeleteConfirmationDialog();
|
openDeleteConfirmationDialog();
|
||||||
@@ -48,6 +39,18 @@ function RouteComponent() {
|
|||||||
[openDeleteConfirmationDialog],
|
[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
|
// handle loading / error / show values
|
||||||
return (
|
return (
|
||||||
<Box sx={{ flexDirection: "column", display: "flex", gap: "3em" }}>
|
<Box sx={{ flexDirection: "column", display: "flex", gap: "3em" }}>
|
||||||
@@ -72,7 +75,7 @@ function RouteComponent() {
|
|||||||
{announcementForLatestDeleteAttempt && (
|
{announcementForLatestDeleteAttempt && (
|
||||||
<DeleteAnnouncementDialog
|
<DeleteAnnouncementDialog
|
||||||
announcement={announcementForLatestDeleteAttempt}
|
announcement={announcementForLatestDeleteAttempt}
|
||||||
confirmDelete={closeDeleteConfirmation}
|
confirmDelete={confirmDelete}
|
||||||
denyDelete={closeDeleteConfirmation}
|
denyDelete={closeDeleteConfirmation}
|
||||||
isDeleteConfirmationOpen={isDeleteConfirmationOpen}
|
isDeleteConfirmationOpen={isDeleteConfirmationOpen}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -18,3 +18,10 @@ export async function getAnnouncements() {
|
|||||||
const announcements = Announcements.parse(data);
|
const announcements = Announcements.parse(data);
|
||||||
return announcements;
|
return announcements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function deleteAnnouncement(announcementId: number) {
|
||||||
|
const response = await fetch(`/api/announcements/${announcementId}`, {
|
||||||
|
method: "DELETE",
|
||||||
|
});
|
||||||
|
await response.text();
|
||||||
|
}
|
||||||
|
|||||||
+14
-1
@@ -21,11 +21,15 @@ const AddInput = z.object({
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const DeleteInput = z.object({
|
||||||
|
announcementId: z.coerce.number().positive(),
|
||||||
|
});
|
||||||
|
|
||||||
app.get("/hello", (_, res) => {
|
app.get("/hello", (_, res) => {
|
||||||
res.send("Hello Vite + React + TypeScript!");
|
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);
|
const addInput = AddInput.parse(req);
|
||||||
await db
|
await db
|
||||||
.insertInto("announcements")
|
.insertInto("announcements")
|
||||||
@@ -49,6 +53,15 @@ app.get("/api/announcements", async (_, res) => {
|
|||||||
res.json(announcements);
|
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, () =>
|
ViteExpress.listen(app, 3000, () =>
|
||||||
console.log("Server is listening on port 3000..."),
|
console.log("Server is listening on port 3000..."),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user