import ClearIcon from "@mui/icons-material/Clear"; import DeleteIcon from "@mui/icons-material/Delete"; import EditIcon from "@mui/icons-material/Edit"; import FileDownloadIcon from "@mui/icons-material/FileDownload"; import SaveIcon from "@mui/icons-material/Save"; import { Button, Grid, Stack } from "@mui/material"; import * as EmailValidator from "email-validator"; import React from "react"; import { useNavigate, useParams } from "react-router-dom"; import { Member, MemberApi } from "../../api/MemberApi"; import { ServerApi } from "../../api/ServerApi"; import { useAlert } from "../../hooks/context_providers/AlertDialogProvider"; import { useConfirm } from "../../hooks/context_providers/ConfirmDialogProvider"; import { useSnackbar } from "../../hooks/context_providers/SnackbarProvider"; import { AsyncWidget } from "../../widgets/AsyncWidget"; import { useFamily } from "../../widgets/BaseFamilyRoute"; import { ConfirmLeaveWithoutSaveDialog } from "../../widgets/ConfirmLeaveWithoutSaveDialog"; import { FamilyPageTitle } from "../../widgets/FamilyPageTitle"; import { MemberPhoto } from "../../widgets/MemberPhoto"; import { PropertiesBox } from "../../widgets/PropertiesBox"; import { RouterLink } from "../../widgets/RouterLink"; import { DateInput } from "../../widgets/forms/DateInput"; import { MemberInput } from "../../widgets/forms/MemberInput"; import { PropCheckbox } from "../../widgets/forms/PropCheckbox"; import { PropEdit } from "../../widgets/forms/PropEdit"; import { PropSelect } from "../../widgets/forms/SelectInput"; import { SexSelection } from "../../widgets/forms/SexSelection"; import { UploadPhotoButton } from "../../widgets/forms/UploadPhotoButton"; /** * Create a new member route */ export function FamilyCreateMemberRoute(): React.ReactElement { const alert = useAlert(); const snackbar = useSnackbar(); const [shouldQuit, setShouldQuit] = React.useState(false); const n = useNavigate(); const family = useFamily(); const create = async (m: Member) => { try { const r = await MemberApi.Create(m); await family.reloadMembersList(); setShouldQuit(true); n(family.family.URL(`member/${r.id}`)); snackbar(`La fiche pour ${r.fullName} a été créée avec succès !`); } catch (e) { console.error(e); alert("Echec de la création de la personne !"); } }; const cancel = () => { setShouldQuit(true); n(family.family.URL("members")); }; return ( ); } /** * Get existing member route */ export function FamilyMemberRoute(): React.ReactElement { const count = React.useRef(1); 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 forceReload = async () => { count.current += 1; setMember(undefined); await family.reloadMembersList(); }; 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")); await family.reloadMembersList(); } catch (e) { console.error(e); alert("Échec de la suppression du membre !"); } }; return ( ( n(family.family.URL(`member/${member!.id}/edit`)) } onForceReload={forceReload} /> )} /> ); } /** * Edit existing member route */ export function FamilyEditMemberRoute(): React.ReactElement { const n = useNavigate(); const alert = useAlert(); const snackbar = useSnackbar(); const [shouldQuit, setShouldQuit] = React.useState(false); const family = useFamily(); const { memberId } = useParams(); const [member, setMember] = React.useState(); const load = async () => { setMember(await MemberApi.GetSingle(family.familyId, Number(memberId))); }; const cancel = () => { setShouldQuit(true); n(family.family.URL(`member/${member!.id}`)); }; const save = async (m: Member) => { try { await MemberApi.Update(m); snackbar("Les informations du membre ont été mises à jour avec succès !"); await family.reloadMembersList(); setShouldQuit(true); n(family.family.URL(`member/${member!.id}`)); } catch (e) { console.error(e); alert("Échec de la mise à jour des informations du membre !"); } }; return ( ( )} /> ); } export function MemberPage(p: { member: Member; editing: boolean; creating: boolean; shouldAllowLeaving?: boolean; onCancel?: () => void; onSave?: (m: Member) => void; onRequestEdit?: () => void; onRequestDelete?: () => void; onForceReload?: () => void; }): React.ReactElement { const confirm = useConfirm(); const snackbar = useSnackbar(); const [changed, setChanged] = React.useState(false); const [member, setMember] = React.useState( new Member(structuredClone(p.member)) ); const updatedMember = () => { setChanged(true); setMember(new Member(structuredClone(member))); }; const save = () => { p.onSave!(member); }; const cancel = async () => { if ( changed && !(await confirm( "Voulez-vous vraiment retirer les modifications apportées ? Celles-ci seront perdues !" )) ) return; p.onCancel!(); }; const uploadNewPhoto = async (b: Blob) => { await MemberApi.SetMemberPhoto(member, b); snackbar("La photo du membre a été mise à jour avec succès !"); p.onForceReload?.(); }; const deletePhoto = async () => { try { if (!(await confirm("Voulez-vous supprimer cette photo ?"))) return; await MemberApi.RemoveMemberPhoto(member); snackbar("La photo du membre a été supprimée avec succès !"); p.onForceReload?.(); } catch (e) { console.error(e); alert("Échec de la suppresion de la photo !"); } }; return (
{/* Edit button */} {p.onRequestEdit && ( )} {/* Delete button */} {p.onRequestDelete && ( )} {/* Save button */} {p.editing && changed && ( )} {/* Cancel button */} {p.editing && ( )}
{/* General info */} {/* Sex */} { member.sex = v; updatedMember(); }} /> {/* First name */} { member.first_name = v; updatedMember(); }} size={ServerApi.Config.constraints.member_first_name} /> {/* Last name */} { member.last_name = v; updatedMember(); }} size={ServerApi.Config.constraints.member_last_name} /> {/* Birth last name */} { member.birth_last_name = v; updatedMember(); }} size={ServerApi.Config.constraints.member_birth_last_name} /> {/* Birth day */} { member.birth_year = d.year; member.birth_month = d.month; member.birth_day = d.day; updatedMember(); }} /> {/* Is dead */} { member.dead = v; updatedMember(); }} /> {/* Death day */} { member.death_year = d.year; member.death_month = d.month; member.death_day = d.day; updatedMember(); }} /> {/* Father */}
{ member.father = m; updatedMember(); }} filter={(m) => (m.sex === "M" || m.sex === undefined) && m.id !== member.id } current={member.father} /> {/* Mother */}
{ member.mother = m; updatedMember(); }} filter={(m) => (m.sex === "F" || m.sex === undefined) && m.id !== member.id } current={member.mother} />
{/* Photo */}

{p.editing ? (

Veuillez enregistrer / annuler les modifications apportées à la fiche avant de changer la photo du membre.

) : ( <> {" "} {member.hasPhoto && ( )}{" "} {member.hasPhoto && ( )} )}{" "}
{/* Contact */} {(p.editing || member.hasContactInfo) && ( {/* Email */} { member.email = v; updatedMember(); }} size={ServerApi.Config.constraints.member_email} checkValue={(e) => EmailValidator.validate(e)} /> {/* Phone number */} { member.phone = v; updatedMember(); }} size={ServerApi.Config.constraints.member_phone} /> {/* Country */} { member.country = o; updatedMember(); }} value={member.country} options={ServerApi.Config.countries.map((c) => { return { label: c.fr, value: c.code }; })} /> )} {/* Bio */} TODO {/* Spouse */} TODO {/* Children */} TODO
); }