Show family members list
This commit is contained in:
		@@ -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 {
 | 
			
		||||
            <Route path="profile" element={<ProfileRoute />} />
 | 
			
		||||
            <Route path="family/:familyId/*" element={<BaseFamilyRoute />}>
 | 
			
		||||
              <Route path="" element={<FamilyHomeRoute />} />
 | 
			
		||||
              <Route path="members" element={<FamilyMembersListRoute />} />
 | 
			
		||||
              <Route
 | 
			
		||||
                path="member/create"
 | 
			
		||||
                element={<FamilyCreateMemberRoute />}
 | 
			
		||||
 
 | 
			
		||||
@@ -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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -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<number, Member>;
 | 
			
		||||
@@ -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);
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -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) => {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										160
									
								
								geneit_app/src/routes/family/FamilyMembersListRoute.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								geneit_app/src/routes/family/FamilyMembersListRoute.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -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 (
 | 
			
		||||
    <>
 | 
			
		||||
      <div style={{ display: "flex", justifyContent: "space-between" }}>
 | 
			
		||||
        <FamilyPageTitle title="Membres de la famille" />
 | 
			
		||||
        <RouterLink to={family.family.URL("member/create")}>
 | 
			
		||||
          <Tooltip title="Créer la fiche d'un nouveau membre">
 | 
			
		||||
            <Button startIcon={<AddIcon />}>Nouveau</Button>
 | 
			
		||||
          </Tooltip>
 | 
			
		||||
        </RouterLink>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      {family.members.isEmpty ? (
 | 
			
		||||
        <p>
 | 
			
		||||
          Votre famille n'a aucun membre pour le moment ! Utilisez le bouton
 | 
			
		||||
          situé en haut à droite pour créer le premier !
 | 
			
		||||
        </p>
 | 
			
		||||
      ) : (
 | 
			
		||||
        <MembersTable
 | 
			
		||||
          members={family.members.fullList}
 | 
			
		||||
          onDelete={processDeletion}
 | 
			
		||||
        />
 | 
			
		||||
      )}
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function MembersTable(p: {
 | 
			
		||||
  members: Member[];
 | 
			
		||||
  onDelete: (m: Member) => void;
 | 
			
		||||
}): React.ReactElement {
 | 
			
		||||
  const family = useFamily();
 | 
			
		||||
  const n = useNavigate();
 | 
			
		||||
 | 
			
		||||
  const columns: GridColDef<Member>[] = [
 | 
			
		||||
    {
 | 
			
		||||
      field: "signed_photo_id",
 | 
			
		||||
      headerName: "",
 | 
			
		||||
      disableColumnMenu: true,
 | 
			
		||||
      sortable: false,
 | 
			
		||||
      width: 60,
 | 
			
		||||
      renderCell(params) {
 | 
			
		||||
        return <MemberPhoto member={params.row} />;
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      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 [
 | 
			
		||||
          <GridActionsCellItem
 | 
			
		||||
            icon={<VisibilityIcon />}
 | 
			
		||||
            label="Consulter le membre"
 | 
			
		||||
            className="textPrimary"
 | 
			
		||||
            onClick={() => n(family.family.memberURL(params.row))}
 | 
			
		||||
            color="inherit"
 | 
			
		||||
          />,
 | 
			
		||||
          <GridActionsCellItem
 | 
			
		||||
            icon={<EditIcon />}
 | 
			
		||||
            label="Modifier le membre"
 | 
			
		||||
            className="textPrimary"
 | 
			
		||||
            onClick={() => n(family.family.memberURL(params.row, true))}
 | 
			
		||||
            color="inherit"
 | 
			
		||||
          />,
 | 
			
		||||
          <GridActionsCellItem
 | 
			
		||||
            icon={<DeleteIcon />}
 | 
			
		||||
            label="Supprimer le membre"
 | 
			
		||||
            onClick={() => p.onDelete(params.row)}
 | 
			
		||||
            color="inherit"
 | 
			
		||||
          />,
 | 
			
		||||
        ];
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <DataGrid
 | 
			
		||||
      style={{ flex: "1" }}
 | 
			
		||||
      rows={p.members}
 | 
			
		||||
      columns={columns}
 | 
			
		||||
      autoPageSize
 | 
			
		||||
      getRowId={(c) => c.id}
 | 
			
		||||
      onRowDoubleClick={(p) => n(family.family.memberURL(p.row))}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user