diff --git a/src/client/components/AnnouncementTable.tsx b/src/client/components/AnnouncementTable.tsx
new file mode 100644
index 0000000..13a17b4
--- /dev/null
+++ b/src/client/components/AnnouncementTable.tsx
@@ -0,0 +1,45 @@
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+} from "@mui/material";
+import type { Announcement } from "../services";
+import { AnnouncementTableRow } from "./AnnouncementTableRow";
+
+export interface AnnouncementTableProps {
+ announcements: Announcement[];
+ openDeleteConfirmation(announcement: Announcement): void;
+}
+
+export function AnnouncementTable({
+ announcements,
+ openDeleteConfirmation,
+}: AnnouncementTableProps) {
+ return (
+
+
+
+
+
+ Title
+ Text
+ Publication Date
+
+
+
+
+ {announcements.map((a) => (
+
+ ))}
+
+
+
+ );
+}
diff --git a/src/client/components/AnnouncementTableRow.tsx b/src/client/components/AnnouncementTableRow.tsx
new file mode 100644
index 0000000..fef33f0
--- /dev/null
+++ b/src/client/components/AnnouncementTableRow.tsx
@@ -0,0 +1,30 @@
+import DeleteIcon from "@mui/icons-material/Delete";
+import { IconButton, TableCell, TableRow } from "@mui/material";
+import type { Announcement } from "../services";
+
+export interface AnnouncementTableRowProps {
+ announcement: Announcement;
+ openDeleteConfirmation(announcement: Announcement): void;
+}
+
+export function AnnouncementTableRow({
+ openDeleteConfirmation,
+ announcement,
+}: AnnouncementTableRowProps) {
+ return (
+
+ {announcement.id}
+ {announcement.title}
+
+ {announcement.text.substring(0, 50) +
+ (announcement.text.length > 50 ? "..." : "")}
+
+ {announcement.publication_date.toString()}
+
+ openDeleteConfirmation(announcement)}>
+
+
+
+
+ );
+}
diff --git a/src/client/components/DeleteAnnouncementDialog.tsx b/src/client/components/DeleteAnnouncementDialog.tsx
new file mode 100644
index 0000000..881e60f
--- /dev/null
+++ b/src/client/components/DeleteAnnouncementDialog.tsx
@@ -0,0 +1,45 @@
+import {
+ Button,
+ Dialog,
+ DialogActions,
+ DialogContent,
+ DialogContentText,
+ DialogTitle,
+} from "@mui/material";
+import type { Announcement } from "../services";
+
+export interface DeleteAnnouncementDialogProps {
+ isDeleteConfirmationOpen: boolean;
+ denyDelete(): void;
+ confirmDelete(): void;
+ announcement: Announcement;
+}
+
+export function DeleteAnnouncementDialog({
+ announcement,
+ confirmDelete,
+ denyDelete,
+ isDeleteConfirmationOpen,
+}: DeleteAnnouncementDialogProps) {
+ return (
+
+ );
+}
diff --git a/src/client/routes/index.tsx b/src/client/routes/index.tsx
index d9bb51b..5e9512f 100644
--- a/src/client/routes/index.tsx
+++ b/src/client/routes/index.tsx
@@ -1,41 +1,12 @@
-import DeleteIcon from "@mui/icons-material/Delete";
-import {
- Alert,
- Box,
- Button,
- CircularProgress,
- Dialog,
- DialogActions,
- DialogContent,
- DialogContentText,
- DialogTitle,
- IconButton,
- Table,
- TableBody,
- TableCell,
- TableContainer,
- TableHead,
- TableRow,
- 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 { useCallback, useEffect, useState } from "react";
-import z from "zod";
+import { AnnouncementTable } from "../components/AnnouncementTable";
+import { DeleteAnnouncementDialog } from "../components/DeleteAnnouncementDialog";
import { RouterButton } from "../components/RouterButton";
import { useBoolean } from "../components/use-boolean";
-
-const Announcements = z.array(
- z.object({
- id: z.number(),
- title: z.string(),
- text: z.string(),
- publication_date: z.iso.datetime().transform((arg) => new Date(arg)),
- image: z.string(),
- }),
-);
-
-type Announcement = z.infer[0];
+import { type Announcement, getAnnouncements } from "../services";
export const Route = createFileRoute("/")({
component: RouteComponent,
@@ -45,13 +16,7 @@ function RouteComponent() {
// the query to retrieve the announcements
const announcementsQuery = useQuery({
queryKey: ["announcements"],
- queryFn: async () => {
- const response = await fetch("/api/announcements");
- const data = await response.json();
- const announcements = Announcements.parse(data);
- console.log(announcements);
- return announcements;
- },
+ queryFn: getAnnouncements,
retry: false,
});
@@ -100,56 +65,18 @@ function RouteComponent() {
Error (see console)
) : (
<>
-
-
-
-
-
- Title
- Text
- Publication Date
-
-
-
-
- {announcementsQuery.data.map((a) => (
-
- {a.id}
- {a.title}
-
- {a.text.substring(0, 50) +
- (a.text.length > 50 ? "..." : "")}
-
- {a.publication_date.toString()}
-
- openDeleteConfirmation(a)}>
-
-
-
-
- ))}
-
-
-
-
+
+ {announcementForLatestDeleteAttempt && (
+
+ )}
>
)}
diff --git a/src/client/services.ts b/src/client/services.ts
new file mode 100644
index 0000000..3fcc13c
--- /dev/null
+++ b/src/client/services.ts
@@ -0,0 +1,20 @@
+import z from "zod";
+
+const Announcements = z.array(
+ z.object({
+ id: z.number(),
+ title: z.string(),
+ text: z.string(),
+ publication_date: z.iso.datetime().transform((arg) => new Date(arg)),
+ image: z.string(),
+ }),
+);
+
+export type Announcement = z.infer[0];
+
+export async function getAnnouncements() {
+ const response = await fetch("/api/announcements");
+ const data = await response.json();
+ const announcements = Announcements.parse(data);
+ return announcements;
+}