Make members information globally available
This commit is contained in:
		@@ -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>
 | 
			
		||||
        );
 | 
			
		||||
      }}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user