Make members information globally available

This commit is contained in:
Pierre HUBERT 2023-08-09 08:55:37 +02:00
parent 1128b5ebd4
commit 359dd2f9ee
3 changed files with 143 additions and 96 deletions

View File

@ -2,7 +2,7 @@ import { APIClient } from "./ApiClient";
export type Sex = "M" | "F";
export interface MemberApi {
export interface MemberDataApi {
id: number;
family_id: number;
first_name?: string;
@ -31,7 +31,7 @@ export interface MemberApi {
note?: string;
}
export class Member implements MemberApi {
export class Member implements MemberDataApi {
id: number;
family_id: number;
first_name?: string;
@ -59,7 +59,7 @@ export class Member implements MemberApi {
death_day?: number;
note?: string;
constructor(m: MemberApi) {
constructor(m: MemberDataApi) {
this.id = m.id;
this.family_id = m.family_id;
this.first_name = m.first_name;
@ -108,6 +108,20 @@ export class Member implements MemberApi {
}
}
export class MembersList {
private list: Member[];
private map: Map<number, Member>;
constructor(list: Member[]) {
this.list = list;
this.map = new Map();
for (const m of list) {
this.map.set(m.id, m);
}
}
}
export class MemberApi {
/**
* Create a new member
@ -137,6 +151,18 @@ export class MemberApi {
return new Member(res.data);
}
/**
* Get the entire list of family members of a family
*/
static async GetEntireList(family_id: number): Promise<MembersList> {
const res = await APIClient.exec({
uri: `/family/${family_id}/members`,
method: "GET",
});
return new MembersList(res.data.map((d: any) => new Member(d)));
}
/**
* Update a member information
*/

View File

@ -33,7 +33,7 @@ export function FamilyCreateMemberRoute(): React.ReactElement {
try {
const r = await MemberApi.Create(m);
// TODO : trigger update
await family.reloadMembersList();
setShouldQuit(true);
n(family.family.URL(`member/${r.id}`));
@ -92,7 +92,7 @@ export function FamilyMemberRoute(): React.ReactElement {
snackbar("La fiche de membre a été supprimée avec succès !");
n(family.family.URL("members"));
// TODO : refresh cached members list
await family.reloadMembersList();
} catch (e) {
console.error(e);
alert("Échec de la suppression du membre !");
@ -148,7 +148,7 @@ export function FamilyEditMemberRoute(): React.ReactElement {
snackbar("Les informations du membre ont été mises à jour avec succès !");
// TODO : update family hook info
await family.reloadMembersList();
setShouldQuit(true);
n(family.family.URL(`member/${member!.id}`));
@ -193,7 +193,6 @@ export function MemberPage(p: {
const [member, setMember] = React.useState(structuredClone(p.member));
const updatedMember = () => {
// TODO : add confirmation
setChanged(true);
setMember(structuredClone(member));
};

View File

@ -30,11 +30,14 @@ import { RouterLink } from "./RouterLink";
import { useSnackbar } from "../hooks/context_providers/SnackbarProvider";
import { useConfirm } from "../hooks/context_providers/ConfirmDialogProvider";
import { useAlert } from "../hooks/context_providers/AlertDialogProvider";
import { Member, MemberApi, MembersList } from "../api/MemberApi";
interface FamilyContext {
family: Family;
members: MembersList;
familyId: number;
reloadFamilyInfo: () => void;
reloadMembersList: () => Promise<void>;
}
const FamilyContextK = React.createContext<FamilyContext | null>(null);
@ -46,16 +49,26 @@ export function BaseFamilyRoute(): React.ReactElement {
const confirm = useConfirm();
const [family, setFamily] = React.useState<null | Family>(null);
const [members, setMembers] = React.useState<null | MembersList>(null);
const loadKey = React.useRef(1);
const loadPromise = React.useRef<() => void>();
const load = async () => {
setFamily(await FamilyApi.GetSingle(Number(familyId)));
const familyID = Number(familyId);
setFamily(await FamilyApi.GetSingle(familyID));
setMembers(await MemberApi.GetEntireList(familyID));
};
const onReload = () => {
const onReload = async () => {
loadKey.current += 1;
setFamily(null);
setMembers(null);
return new Promise<void>((res, _rej) => {
loadPromise.current = () => res();
});
};
const copyInvitationCode = async () => {
@ -85,113 +98,122 @@ export function BaseFamilyRoute(): React.ReactElement {
return (
<AsyncWidget
ready={family != null}
ready={family !== null && members !== null}
loadKey={`${familyId}-${loadKey.current}`}
load={load}
errMsg="Échec du chargement des informations de la famille !"
build={() => (
<FamilyContextK.Provider
value={{
family: family!,
familyId: family!.family_id,
reloadFamilyInfo: onReload,
}}
>
<Box
sx={{
display: "flex",
flex: "2",
build={() => {
if (loadPromise.current != null) {
loadPromise.current?.();
loadPromise.current = undefined;
}
return (
<FamilyContextK.Provider
value={{
family: family!,
members: members!,
familyId: family!.family_id,
reloadFamilyInfo: onReload,
reloadMembersList: onReload,
}}
>
<List
component="nav"
<Box
sx={{
minWidth: "280px",
backgroundColor: "background.paper",
display: "flex",
flex: "2",
}}
>
<FamilyLink icon={<HomeIcon />} label="Accueil" uri="" />
<List
component="nav"
sx={{
minWidth: "280px",
backgroundColor: "background.paper",
}}
>
<FamilyLink icon={<HomeIcon />} label="Accueil" uri="" />
<FamilyLink
icon={<Icon path={mdiCrowd} size={1} />}
label="Membres"
uri="members"
/>
<FamilyLink
icon={<Icon path={mdiCrowd} size={1} />}
label="Membres"
uri="members"
/>
<FamilyLink
icon={<Icon path={mdiHumanMaleFemale} size={1} />}
label="Couples"
uri="couples"
/>
<FamilyLink
icon={<Icon path={mdiHumanMaleFemale} size={1} />}
label="Couples"
uri="couples"
/>
<FamilyLink
icon={<Icon path={mdiFamilyTree} size={1} />}
label="Arbre"
uri="tree"
/>
<FamilyLink
icon={<Icon path={mdiFamilyTree} size={1} />}
label="Arbre"
uri="tree"
/>
<Divider sx={{ my: 1 }} />
<ListSubheader component="div">Administration</ListSubheader>
<Divider sx={{ my: 1 }} />
<ListSubheader component="div">Administration</ListSubheader>
<FamilyLink
icon={<Icon path={mdiAccountMultiple} size={1} />}
label="Utilisateurs"
uri="users"
/>
<FamilyLink
icon={<Icon path={mdiAccountMultiple} size={1} />}
label="Utilisateurs"
uri="users"
/>
<FamilyLink
icon={<Icon path={mdiCog} size={1} />}
label="Paramètres"
uri="settings"
/>
<FamilyLink
icon={<Icon path={mdiCog} size={1} />}
label="Paramètres"
uri="settings"
/>
{/* Invitation code */}
{/* Invitation code */}
<ListItem
dense
secondaryAction={
<span>
<Tooltip title="Copier le code d'invitation dans le presse papier">
<IconButton onClick={copyInvitationCode}>
<Icon path={mdiContentCopy} size={0.75} />
</IconButton>
</Tooltip>
{family!.is_admin && (
<Tooltip title="Changer le code d'activation">
<IconButton onClick={changeInvitationCode}>
<Icon path={mdiRefresh} size={0.75} />
<ListItem
dense
secondaryAction={
<span>
<Tooltip title="Copier le code d'invitation dans le presse papier">
<IconButton onClick={copyInvitationCode}>
<Icon path={mdiContentCopy} size={0.75} />
</IconButton>
</Tooltip>
)}
</span>
}
>
<ListItemIcon>
<Icon path={mdiLockCheck} size={1} />
</ListItemIcon>
<ListItemText
primary="Code d'invitation"
secondary={family?.invitation_code}
/>
</ListItem>
</List>
<Box
component="main"
sx={{
flexGrow: 1,
overflow: "auto",
padding: "20px",
display: "flex",
flexDirection: "column",
}}
>
<Outlet />
{family!.is_admin && (
<Tooltip title="Changer le code d'activation">
<IconButton onClick={changeInvitationCode}>
<Icon path={mdiRefresh} size={0.75} />
</IconButton>
</Tooltip>
)}
</span>
}
>
<ListItemIcon>
<Icon path={mdiLockCheck} size={1} />
</ListItemIcon>
<ListItemText
primary="Code d'invitation"
secondary={family?.invitation_code}
/>
</ListItem>
</List>
<Box
component="main"
sx={{
flexGrow: 1,
overflow: "auto",
padding: "20px",
display: "flex",
flexDirection: "column",
}}
>
<Outlet />
</Box>
</Box>
</Box>
</FamilyContextK.Provider>
)}
</FamilyContextK.Provider>
);
}}
/>
);
}