From b723479ec83aa3ba9feb4b383a449dc775ee94b4 Mon Sep 17 00:00:00 2001
From: cdricms <36056008+cdricms@users.noreply.github.com>
Date: Tue, 28 Jan 2025 13:29:33 +0100
Subject: [PATCH] Handling CRUD for members in frontend
---
backend/api/get_users.go | 7 +-
.../app/(auth)/dashboard/members/page.tsx | 10 +
frontend/components/app-sidebar.tsx | 4 +
frontend/components/member-dialog.tsx | 152 ++++++++++
frontend/components/members-table.tsx | 277 +++++++-----------
5 files changed, 280 insertions(+), 170 deletions(-)
create mode 100644 frontend/app/(auth)/dashboard/members/page.tsx
create mode 100644 frontend/components/member-dialog.tsx
diff --git a/backend/api/get_users.go b/backend/api/get_users.go
index 707807e..f577cba 100644
--- a/backend/api/get_users.go
+++ b/backend/api/get_users.go
@@ -20,9 +20,9 @@ func HandleGetUsers(w http.ResponseWriter, r *http.Request) {
})
if count == 0 {
- core.JSONError{
- Status: core.Error,
- Message: "Not users.",
+ core.JSONSuccess{
+ Status: core.Success,
+ Message: "No users.",
}.Respond(w, http.StatusNotFound)
return
}
@@ -35,7 +35,6 @@ func HandleGetUsers(w http.ResponseWriter, r *http.Request) {
return
}
- // TODO : Remove password
core.JSONSuccess{
Status: core.Success,
Message: "Users found.",
diff --git a/frontend/app/(auth)/dashboard/members/page.tsx b/frontend/app/(auth)/dashboard/members/page.tsx
new file mode 100644
index 0000000..771ed9d
--- /dev/null
+++ b/frontend/app/(auth)/dashboard/members/page.tsx
@@ -0,0 +1,10 @@
+"use server";
+import MembersTable from "@/components/members-table";
+
+export default async function Page({}) {
+ return (
+
+
+
+ );
+}
diff --git a/frontend/components/app-sidebar.tsx b/frontend/components/app-sidebar.tsx
index dd6266a..3efcdfb 100644
--- a/frontend/components/app-sidebar.tsx
+++ b/frontend/components/app-sidebar.tsx
@@ -54,6 +54,10 @@ const data = {
icon: Users,
isActive: true,
items: [
+ {
+ title: "Liste des membres",
+ url: "/dashboard/members",
+ },
{
title: "Création d'un membre",
url: "/dashboard/members/new",
diff --git a/frontend/components/member-dialog.tsx b/frontend/components/member-dialog.tsx
new file mode 100644
index 0000000..9539c55
--- /dev/null
+++ b/frontend/components/member-dialog.tsx
@@ -0,0 +1,152 @@
+import { useEffect } from "react";
+import { useForm } from "react-hook-form";
+import { zodResolver } from "@hookform/resolvers/zod";
+import * as z from "zod";
+import {
+ Dialog,
+ DialogContent,
+ DialogHeader,
+ DialogTitle,
+ DialogFooter,
+} from "@/components/ui/dialog";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import {
+ Form,
+ FormControl,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from "@/components/ui/form";
+
+const memberSchema = z.object({
+ userId: z.string().optional(),
+ firstname: z.string().min(1, "Prénom est requis."),
+ lastname: z.string().min(1, "Nom de famille est requis."),
+ email: z.string().email("Adresse email invalide."),
+ password: z
+ .string()
+ .min(6, "Le mot de passe doit avoir au moins 6 caractères.")
+ .optional(),
+ phone: z.string().regex(/^\d{10}$/, "Le numéro doit avoir 10 chiffres."),
+ role: z.string().min(1, "Le rôle est requis."),
+});
+
+const updateMemberSchema = memberSchema.partial();
+
+export type Member = z.infer;
+
+interface MemberDialogProps {
+ isOpen: boolean;
+ onClose: () => void;
+ member: Member | null;
+ onSave: (member: Member) => void;
+}
+
+export default function MemberDialog({
+ isOpen,
+ onClose,
+ member,
+ onSave,
+}: MemberDialogProps) {
+ const schema = member?.userId ? updateMemberSchema : memberSchema;
+ const form = useForm({
+ resolver: zodResolver(schema),
+ defaultValues: member?.userId
+ ? member
+ : {
+ userId: "",
+ firstname: "",
+ lastname: "",
+ email: "",
+ password: "",
+ phone: "",
+ role: "",
+ },
+ });
+
+ useEffect(() => {
+ if (member) {
+ form.reset(member);
+ } else {
+ form.reset({
+ userId: "",
+ firstname: "",
+ lastname: "",
+ email: "",
+ password: "",
+ phone: "",
+ role: "",
+ });
+ }
+ }, [member, form]);
+
+ const onSubmit = (data: Member) => {
+ onSave(data);
+ onClose();
+ };
+
+ return (
+
+ );
+}
diff --git a/frontend/components/members-table.tsx b/frontend/components/members-table.tsx
index 399ec59..01d334b 100644
--- a/frontend/components/members-table.tsx
+++ b/frontend/components/members-table.tsx
@@ -12,93 +12,28 @@ import {
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { ScrollArea } from "@/components/ui/scroll-area";
-import { UpdateMemberDialog } from "./UpdateMemberDialog";
-import { AddMemberDialog } from "./AddMemberDialog";
+import MemberDialog, { Member } from "./member-dialog";
+import * as z from "zod";
+import { request, useApi } from "@/hooks/use-api";
+import {
+ CircleX,
+ Loader2,
+ Trash2,
+ UserRoundPen,
+ UserRoundPlus,
+} from "lucide-react";
-interface Member {
- user_id: string;
- firstname: string;
- lastname: string;
- email: string;
- password: string;
- phone: string;
- role: string;
-}
-
-const initialMembers: Member[] = [
- // Add some sample data here
- {
- user_id: "1",
- firstname: "John",
- lastname: "Doe",
- email: "john@example.com",
- password: "********",
- phone: "1234567890",
- role: "User",
- },
- {
- user_id: "1",
- firstname: "John",
- lastname: "Doe",
- email: "john@example.com",
- password: "********",
- phone: "1234567890",
- role: "User",
- },
- {
- user_id: "1",
- firstname: "John",
- lastname: "Doe",
- email: "john@example.com",
- password: "********",
- phone: "1234567890",
- role: "User",
- },
- {
- user_id: "1",
- firstname: "John",
- lastname: "Doe",
- email: "john@example.com",
- password: "********",
- phone: "1234567890",
- role: "User",
- },
- {
- user_id: "1",
- firstname: "John",
- lastname: "Doe",
- email: "john@example.com",
- password: "********",
- phone: "1234567890",
- role: "User",
- },
- {
- user_id: "1",
- firstname: "John",
- lastname: "Doe",
- email: "john@example.com",
- password: "********",
- phone: "1234567890",
- role: "User",
- },
- {
- user_id: "1",
- firstname: "John",
- lastname: "Doe",
- email: "john@example.com",
- password: "********",
- phone: "1234567890",
- role: "User",
- },
- // Add more sample members...
-];
-
-export function MembersTable() {
- const [members, setMembers] = useState(initialMembers);
+export default function MembersTable() {
+ const {
+ data: members,
+ error,
+ mutate,
+ success,
+ isLoading,
+ } = useApi("/users", undefined, true, false);
const [selectMode, setSelectMode] = useState(false);
const [selectedMembers, setSelectedMembers] = useState([]);
- const [updateDialogOpen, setUpdateDialogOpen] = useState(false);
- const [addDialogOpen, setAddDialogOpen] = useState(false);
+ const [dialogOpen, setDialogOpen] = useState(false);
const [currentMember, setCurrentMember] = useState(null);
const toggleSelectMode = () => {
@@ -114,124 +49,134 @@ export function MembersTable() {
);
};
- const handleUpdate = (member: Member) => {
+ const handleOpenDialog = (member: Member | null) => {
setCurrentMember(member);
- setUpdateDialogOpen(true);
+ setDialogOpen(true);
};
- const handleDelete = (userId: string) => {
- setMembers((prev) =>
- prev.filter((member) => member.user_id !== userId),
- );
+ const handleSaveMember = async (savedMember: Member) => {
+ if (savedMember.userId) {
+ const res = await request(
+ `/users/${savedMember.userId}/update`,
+ {
+ body: savedMember,
+ requiresAuth: true,
+ method: "PATCH",
+ },
+ );
+ if (res.status === "Success") mutate();
+ // Update existing member
+ // setMembers((prev) =>
+ // prev.map((m) =>
+ // m.user_id === savedMember.user_id ? savedMember : m,
+ // ),
+ // );
+ } else {
+ const res = await request("/users/new", {
+ body: savedMember,
+ method: "POST",
+ requiresAuth: true,
+ });
+ if (res.status === "Success") mutate();
+ }
};
- const handleAdd = (newMember: Member) => {
- setMembers((prev) => [
- ...prev,
- { ...newMember, user_id: String(prev.length + 1) },
- ]);
+ const handleDelete = async (userId: string) => {
+ const res = await request(`/users/${userId}/delete`, {
+ method: "DELETE",
+ requiresAuth: true,
+ });
+ if (res.status === "Success") mutate();
};
return (
-
-
+
{selectMode && (
- Select
+ Selectionner
)}
- User ID
- First Name
- Last Name
+ Prénom
+ Nom
Email
- Password
- Phone
- Role
+ Téléphone
+ Rôle
Actions
- {members.map((member) => (
-
- {selectMode && (
+ {isLoading && }
+ {members &&
+ members.map((member) => (
+
+ {selectMode && (
+
+
+ toggleMemberSelection(
+ member.userId!,
+ )
+ }
+ />
+
+ )}
-
- toggleMemberSelection(
- member.user_id,
- )
- }
- />
+ {member.firstname}
- )}
- {member.user_id}
- {member.firstname}
- {member.lastname}
- {member.email}
- {member.password}
- {member.phone}
- {member.role}
-
- handleUpdate(member)}
- >
- Modify
-
-
- handleDelete(member.user_id)
- }
- >
- Delete
-
-
-
- ))}
+ {member.lastname}
+ {member.email}
+ {member.phone}
+ {member.role}
+
+
+ handleOpenDialog(member)
+ }
+ >
+
+
+
+ handleDelete(member.userId!)
+ }
+ >
+
+
+
+
+ ))}
-
setUpdateDialogOpen(false)}
+ setDialogOpen(false)}
member={currentMember}
- onUpdate={(updatedMember) => {
- setMembers((prev) =>
- prev.map((m) =>
- m.user_id === updatedMember.user_id
- ? updatedMember
- : m,
- ),
- );
- setUpdateDialogOpen(false);
- }}
- />
- setAddDialogOpen(false)}
- onAdd={handleAdd}
+ onSave={handleSaveMember}
/>
);