Make members information globally available
This commit is contained in:
parent
1128b5ebd4
commit
359dd2f9ee
@ -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
|
||||
*/
|
||||
|
@ -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));
|
||||
};
|
||||
|
@ -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>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user