diff --git a/geneit_app/src/App.tsx b/geneit_app/src/App.tsx index f502a3f..a75fe33 100644 --- a/geneit_app/src/App.tsx +++ b/geneit_app/src/App.tsx @@ -27,6 +27,7 @@ import { FamilyUsersListRoute } from "./routes/family/FamilyUsersListRoute"; import { BaseAuthenticatedPage } from "./widgets/BaseAuthenticatedPage"; import { BaseFamilyRoute } from "./widgets/BaseFamilyRoute"; import { BaseLoginPage } from "./widgets/BaseLoginpage"; +import { FamilyMembersListRoute } from "./routes/family/FamilyMembersListRoute"; interface AuthContext { signedIn: boolean; @@ -57,6 +58,7 @@ export function App(): React.ReactElement { } /> }> } /> + } /> } diff --git a/geneit_app/src/api/FamilyApi.ts b/geneit_app/src/api/FamilyApi.ts index e1aeaa0..2a2129a 100644 --- a/geneit_app/src/api/FamilyApi.ts +++ b/geneit_app/src/api/FamilyApi.ts @@ -1,4 +1,5 @@ import { APIClient } from "./ApiClient"; +import { Member } from "./MemberApi"; interface FamilyAPI { user_id: number; @@ -52,6 +53,15 @@ export class Family implements FamilyAPI { get BaseURL(): string { return this.URL(""); } + + /** + * Get application URL for member page + */ + memberURL(member: Member, edit?: boolean): string { + return ( + `/family/${this.family_id}/member/${member.id}` + (edit ? "/edit" : "") + ); + } } export enum JoinFamilyResult { diff --git a/geneit_app/src/api/MemberApi.ts b/geneit_app/src/api/MemberApi.ts index b9a873f..74502e1 100644 --- a/geneit_app/src/api/MemberApi.ts +++ b/geneit_app/src/api/MemberApi.ts @@ -171,6 +171,15 @@ export function fmtDate(d?: DateValue): string { }/${d?.year?.toString().padStart(4, "0") ?? "__"}`; } +const OLD_TIME = -58991812735; +export function dateTimestamp(d?: DateValue): number { + if (!d) return OLD_TIME; + + const date = new Date(); + date.setFullYear(d.year ?? 1010, (d.month ?? 1) - 1, d.day ?? 1); + return date.getTime() / 1000; +} + export class MembersList { private list: Member[]; private map: Map; @@ -184,6 +193,14 @@ export class MembersList { } } + public get isEmpty(): boolean { + return this.list.length === 0; + } + + public get fullList(): Member[] { + return this.list; + } + filter(predicate: (m: Member) => boolean): Member[] { return this.list.filter(predicate); } diff --git a/geneit_app/src/routes/family/FamilyMemberRoute.tsx b/geneit_app/src/routes/family/FamilyMemberRoute.tsx index e5d792d..67fa631 100644 --- a/geneit_app/src/routes/family/FamilyMemberRoute.tsx +++ b/geneit_app/src/routes/family/FamilyMemberRoute.tsx @@ -163,7 +163,8 @@ export function FamilyEditMemberRoute(): React.ReactElement { const cancel = () => { setShouldQuit(true); - n(family.family.URL(`member/${member!.id}`)); + //n(family.family.URL(`member/${member!.id}`)); + n(-1); }; const save = async (m: Member) => { diff --git a/geneit_app/src/routes/family/FamilyMembersListRoute.tsx b/geneit_app/src/routes/family/FamilyMembersListRoute.tsx new file mode 100644 index 0000000..97187a8 --- /dev/null +++ b/geneit_app/src/routes/family/FamilyMembersListRoute.tsx @@ -0,0 +1,160 @@ +import { useNavigate } from "react-router-dom"; +import { useFamily } from "../../widgets/BaseFamilyRoute"; +import { FamilyPageTitle } from "../../widgets/FamilyPageTitle"; +import { Button, Tooltip } from "@mui/material"; +import AddIcon from "@mui/icons-material/Add"; +import { RouterLink } from "../../widgets/RouterLink"; +import { Member, dateTimestamp, fmtDate } from "../../api/MemberApi"; +import { DataGrid, GridActionsCellItem, GridColDef } from "@mui/x-data-grid"; +import { MemberPhoto } from "../../widgets/MemberPhoto"; +import DeleteIcon from "@mui/icons-material/DeleteOutlined"; +import EditIcon from "@mui/icons-material/Edit"; +import VisibilityIcon from "@mui/icons-material/Visibility"; + +export function FamilyMembersListRoute(): React.ReactElement { + const family = useFamily(); + + const processDeletion = async (m: Member) => {}; + + return ( + <> +
+ + + + + + +
+ + {family.members.isEmpty ? ( +

+ Votre famille n'a aucun membre pour le moment ! Utilisez le bouton + situé en haut à droite pour créer le premier ! +

+ ) : ( + + )} + + ); +} + +function MembersTable(p: { + members: Member[]; + onDelete: (m: Member) => void; +}): React.ReactElement { + const family = useFamily(); + const n = useNavigate(); + + const columns: GridColDef[] = [ + { + field: "signed_photo_id", + headerName: "", + disableColumnMenu: true, + sortable: false, + width: 60, + renderCell(params) { + return ; + }, + }, + { + field: "last_name", + headerName: "Nom", + flex: 3, + }, + { + field: "first_name", + headerName: "Prénom", + flex: 3, + }, + { + field: "dateOfBirth", + headerName: "Date de naissance", + flex: 3, + sortComparator(v1, v2) { + const d1 = dateTimestamp(v1); + const d2 = dateTimestamp(v2); + return d1 - d2; + }, + renderCell(params) { + return fmtDate(params.row.dateOfBirth); + }, + }, + { + field: "dateOfDeath", + headerName: "Date de décès", + flex: 3, + sortComparator(v1, v2) { + const d1 = dateTimestamp(v1); + const d2 = dateTimestamp(v2); + return d1 - d2; + }, + renderCell(params) { + return fmtDate(params.row.dateOfDeath); + }, + }, + { + field: "father", + headerName: "Père", + flex: 5, + renderCell(params) { + if (!params.row.father) return <>; + return family.members.get(params.row.father)!.fullName; + }, + }, + { + field: "mother", + headerName: "Mère", + flex: 5, + renderCell(params) { + if (!params.row.mother) return <>; + return family.members.get(params.row.mother)!.fullName; + }, + }, + { + field: "actions", + type: "actions", + sortable: false, + width: 120, + disableColumnMenu: true, + getActions(params) { + return [ + } + label="Consulter le membre" + className="textPrimary" + onClick={() => n(family.family.memberURL(params.row))} + color="inherit" + />, + } + label="Modifier le membre" + className="textPrimary" + onClick={() => n(family.family.memberURL(params.row, true))} + color="inherit" + />, + } + label="Supprimer le membre" + onClick={() => p.onDelete(params.row)} + color="inherit" + />, + ]; + }, + }, + ]; + + return ( + c.id} + onRowDoubleClick={(p) => n(family.family.memberURL(p.row))} + /> + ); +}