diff --git a/geneit_app/src/App.tsx b/geneit_app/src/App.tsx index def7092..3f76344 100644 --- a/geneit_app/src/App.tsx +++ b/geneit_app/src/App.tsx @@ -19,6 +19,7 @@ import { FamilyUsersListRoute } from "./routes/family/FamilyUsersListRoute"; import { FamilySettingsRoute } from "./routes/family/FamilySettingsRoute"; import { FamilyCreateMemberRoute, + FamilyEditMemberRoute, FamilyMemberRoute, } from "./routes/family/FamilyMemberRoute"; @@ -56,6 +57,10 @@ export function App(): React.ReactElement { element={} /> } /> + } + /> } /> } /> } /> diff --git a/geneit_app/src/api/ApiClient.ts b/geneit_app/src/api/ApiClient.ts index 4078ae8..cc09599 100644 --- a/geneit_app/src/api/ApiClient.ts +++ b/geneit_app/src/api/ApiClient.ts @@ -26,7 +26,7 @@ export class APIClient { */ static async exec(args: { uri: string; - method: "GET" | "POST" | "DELETE" | "PATCH"; + method: "GET" | "POST" | "DELETE" | "PATCH" | "PUT"; allowFail?: boolean; jsonData?: any; }): Promise { diff --git a/geneit_app/src/api/MemberApi.ts b/geneit_app/src/api/MemberApi.ts index ac94a81..d3c7d13 100644 --- a/geneit_app/src/api/MemberApi.ts +++ b/geneit_app/src/api/MemberApi.ts @@ -121,4 +121,40 @@ export class MemberApi { return new Member(res.data); } + + /** + * Get the information about a single member + */ + static async GetSingle( + family_id: number, + member_id: number + ): Promise { + const res = await APIClient.exec({ + uri: `/family/${family_id}/member/${member_id}`, + method: "GET", + }); + + return new Member(res.data); + } + + /** + * Update a member information + */ + static async Update(m: Member): Promise { + await APIClient.exec({ + uri: `/family/${m.family_id}/member/${m.id}`, + method: "PUT", + jsonData: m, + }); + } + + /** + * Delete a family member + */ + static async Delete(m: Member): Promise { + await APIClient.exec({ + uri: `/family/${m.family_id}/member/${m.id}`, + method: "DELETE", + }); + } } diff --git a/geneit_app/src/routes/family/FamilyMemberRoute.tsx b/geneit_app/src/routes/family/FamilyMemberRoute.tsx index f12a3f2..070bd5e 100644 --- a/geneit_app/src/routes/family/FamilyMemberRoute.tsx +++ b/geneit_app/src/routes/family/FamilyMemberRoute.tsx @@ -2,7 +2,9 @@ import ClearIcon from "@mui/icons-material/Clear"; import SaveIcon from "@mui/icons-material/Save"; import { Button, Grid, Stack } from "@mui/material"; import React from "react"; -import { useNavigate } from "react-router-dom"; +import EditIcon from "@mui/icons-material/Edit"; +import DeleteIcon from "@mui/icons-material/Delete"; +import { useNavigate, useParams } from "react-router-dom"; import { Member, MemberApi } from "../../api/MemberApi"; import { ServerApi } from "../../api/ServerApi"; import { useAlert } from "../../context_providers/AlertDialogProvider"; @@ -13,6 +15,7 @@ import { PropEdit } from "../../widgets/PropEdit"; import { PropertiesBox } from "../../widgets/PropertiesBox"; import { SexSelection } from "../../widgets/SexSelection"; import { useSnackbar } from "../../context_providers/SnackbarProvider"; +import { AsyncWidget } from "../../widgets/AsyncWidget"; /** * Create a new member route @@ -50,19 +53,120 @@ export function FamilyCreateMemberRoute(): React.ReactElement { } /** - * Edit existing member route + * Get existing member route */ export function FamilyMemberRoute(): React.ReactElement { - return

TODO

; + const n = useNavigate(); + const alert = useAlert(); + const confirm = useConfirm(); + const snackbar = useSnackbar(); + + const family = useFamily(); + const { memberId } = useParams(); + + const [member, setMember] = React.useState(); + const load = async () => { + setMember(await MemberApi.GetSingle(family.familyId, Number(memberId))); + }; + + const deleteMember = async () => { + try { + if ( + !(await confirm( + "Voulez-vous vraiment supprimer cette fiche membre ? L'opération n'est pas réversible !" + )) + ) + return; + + await MemberApi.Delete(member!); + + snackbar("La fiche de membre a été supprimée avec succès !"); + n(family.family.URL("members")); + + // TODO : refresh cached members list + } catch (e) { + console.error(e); + alert("Échec de la suppression du membre !"); + } + }; + + return ( + ( + + n(family.family.URL(`member/${member!.id}/edit`)) + } + /> + )} + /> + ); +} + +/** + * Edit existing member route + */ +export function FamilyEditMemberRoute(): React.ReactElement { + const n = useNavigate(); + const alert = useAlert(); + const snackbar = useSnackbar(); + + const family = useFamily(); + const { memberId } = useParams(); + + const [member, setMember] = React.useState(); + const load = async () => { + setMember(await MemberApi.GetSingle(family.familyId, Number(memberId))); + }; + + const save = async (m: Member) => { + try { + await MemberApi.Update(m); + + snackbar("Les informations du membre ont été mises à jour avec succès !"); + + // TODO : update family hook info + + n(family.family.URL(`member/${member!.id}`)); + } catch (e) { + console.error(e); + alert("Échec de la mise à jour des informations du membre !"); + } + }; + + return ( + ( + n(family.family.URL(`member/${member!.id}`))} + onSave={save} + /> + )} + /> + ); } export function MemberPage(p: { member: Member; editing: boolean; creating: boolean; - onCancel: () => void; - onSave: (m: Member) => void; + onCancel?: () => void; + onSave?: (m: Member) => void; onRequestEdit?: () => void; + onRequestDelete?: () => void; }): React.ReactElement { const confirm = useConfirm(); @@ -77,7 +181,7 @@ export function MemberPage(p: { }; const save = () => { - p.onSave(member); + p.onSave!(member); }; const cancel = async () => { @@ -88,7 +192,7 @@ export function MemberPage(p: { ) return; - p.onCancel(); + p.onCancel!(); }; return ( @@ -102,10 +206,39 @@ export function MemberPage(p: { > + {/* Edit button */} + {p.onRequestEdit && ( + + )} + + {/* Delete button */} + {p.onRequestDelete && ( + + )} + {/* Save button */} {p.editing && (