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 && (
+ }
+ onClick={p.onRequestEdit}
+ size="large"
+ >
+ Editer
+
+ )}
+
+ {/* Delete button */}
+ {p.onRequestDelete && (
+ }
+ onClick={p.onRequestDelete}
+ size="large"
+ color="error"
+ >
+ Supprimer
+
+ )}
+
{/* Save button */}
{p.editing && (