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 ( + + + + + {member + ? "Mis à jour du membre" + : "Créer un nouveau membre"} + + +
+ + {( + [ + "userId", + "firstname", + "lastname", + "email", + "password", + "phone", + "role", + ] as const + ).map((field) => ( + ( + + + {field.name + .charAt(0) + .toUpperCase() + + field.name.slice(1)} + + + + + + + )} + /> + ))} + + + + + +
+
+ ); +} 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} - - - - - - ))} + {member.lastname} + {member.email} + {member.phone} + {member.role} + + + + + + ))}
- 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} />
);