Display the list of family users
This commit is contained in:
		
							
								
								
									
										31
									
								
								geneit_app/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										31
									
								
								geneit_app/package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -16,6 +16,7 @@
 | 
			
		||||
        "@mdi/react": "^1.6.1",
 | 
			
		||||
        "@mui/icons-material": "^5.11.16",
 | 
			
		||||
        "@mui/material": "^5.13.4",
 | 
			
		||||
        "@mui/x-data-grid": "^6.9.2",
 | 
			
		||||
        "@testing-library/jest-dom": "^5.16.5",
 | 
			
		||||
        "@testing-library/react": "^13.4.0",
 | 
			
		||||
        "@testing-library/user-event": "^13.5.0",
 | 
			
		||||
@@ -3266,6 +3267,31 @@
 | 
			
		||||
        "react": "^17.0.0 || ^18.0.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@mui/x-data-grid": {
 | 
			
		||||
      "version": "6.9.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-6.9.2.tgz",
 | 
			
		||||
      "integrity": "sha512-hrjVq3FrbUpioi2GYSWJtU4NR3V4bPwLbXngw07+I21TGOWV1TKeTslkPI+FGVYU3gMjGSSJRFN8gehPlh5Evw==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@babel/runtime": "^7.22.5",
 | 
			
		||||
        "@mui/utils": "^5.13.6",
 | 
			
		||||
        "clsx": "^1.2.1",
 | 
			
		||||
        "prop-types": "^15.8.1",
 | 
			
		||||
        "reselect": "^4.1.8"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=14.0.0"
 | 
			
		||||
      },
 | 
			
		||||
      "funding": {
 | 
			
		||||
        "type": "opencollective",
 | 
			
		||||
        "url": "https://opencollective.com/mui"
 | 
			
		||||
      },
 | 
			
		||||
      "peerDependencies": {
 | 
			
		||||
        "@mui/material": "^5.4.1",
 | 
			
		||||
        "@mui/system": "^5.4.1",
 | 
			
		||||
        "react": "^17.0.0 || ^18.0.0",
 | 
			
		||||
        "react-dom": "^17.0.0 || ^18.0.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
 | 
			
		||||
      "version": "5.1.1-v1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
 | 
			
		||||
@@ -13217,6 +13243,11 @@
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/reselect": {
 | 
			
		||||
      "version": "4.1.8",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz",
 | 
			
		||||
      "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/resolve": {
 | 
			
		||||
      "version": "1.22.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@
 | 
			
		||||
    "@mdi/react": "^1.6.1",
 | 
			
		||||
    "@mui/icons-material": "^5.11.16",
 | 
			
		||||
    "@mui/material": "^5.13.4",
 | 
			
		||||
    "@mui/x-data-grid": "^6.9.2",
 | 
			
		||||
    "@testing-library/jest-dom": "^5.16.5",
 | 
			
		||||
    "@testing-library/react": "^13.4.0",
 | 
			
		||||
    "@testing-library/user-event": "^13.5.0",
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ import { FamilyHomeRoute } from "./routes/family/FamilyHomeRoute";
 | 
			
		||||
import { BaseAuthenticatedPage } from "./widgets/BaseAuthenticatedPage";
 | 
			
		||||
import { BaseFamilyRoute } from "./widgets/BaseFamilyRoute";
 | 
			
		||||
import { BaseLoginPage } from "./widgets/BaseLoginpage";
 | 
			
		||||
import { FamilyUsersListRoute } from "./routes/family/FamilyUsersListRoute";
 | 
			
		||||
 | 
			
		||||
interface AuthContext {
 | 
			
		||||
  signedIn: boolean;
 | 
			
		||||
@@ -45,6 +46,7 @@ export function App(): React.ReactElement {
 | 
			
		||||
            <Route path="profile" element={<ProfileRoute />} />
 | 
			
		||||
            <Route path="family/:familyId/*" element={<BaseFamilyRoute />}>
 | 
			
		||||
              <Route path="" element={<FamilyHomeRoute />} />
 | 
			
		||||
              <Route path="users" element={<FamilyUsersListRoute />} />
 | 
			
		||||
              <Route path="*" element={<NotFoundRoute />} />
 | 
			
		||||
            </Route>
 | 
			
		||||
            <Route path="*" element={<NotFoundRoute />} />
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@ export class APIClient {
 | 
			
		||||
   */
 | 
			
		||||
  static async exec(args: {
 | 
			
		||||
    uri: string;
 | 
			
		||||
    method: "GET" | "POST" | "DELETE";
 | 
			
		||||
    method: "GET" | "POST" | "DELETE" | "PATCH";
 | 
			
		||||
    allowFail?: boolean;
 | 
			
		||||
    jsonData?: any;
 | 
			
		||||
  }): Promise<APIResponse> {
 | 
			
		||||
 
 | 
			
		||||
@@ -62,6 +62,15 @@ export enum JoinFamilyResult {
 | 
			
		||||
  Success,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface FamilyUser {
 | 
			
		||||
  user_id: number;
 | 
			
		||||
  family_id: number;
 | 
			
		||||
  time_create: number;
 | 
			
		||||
  is_admin: boolean;
 | 
			
		||||
  user_name: string;
 | 
			
		||||
  user_mail: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class FamilyApi {
 | 
			
		||||
  /**
 | 
			
		||||
   * Create a new family
 | 
			
		||||
@@ -142,4 +151,29 @@ export class FamilyApi {
 | 
			
		||||
      uri: `/family/${id}/renew_invitation_code`,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get the users of a family
 | 
			
		||||
   */
 | 
			
		||||
  static async GetUsersList(id: number): Promise<FamilyUser[]> {
 | 
			
		||||
    return (
 | 
			
		||||
      await APIClient.exec({
 | 
			
		||||
        method: "GET",
 | 
			
		||||
        uri: `/family/${id}/users`,
 | 
			
		||||
      })
 | 
			
		||||
    ).data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Update a user of the family
 | 
			
		||||
   */
 | 
			
		||||
  static async UpdateUser(user: FamilyUser): Promise<void> {
 | 
			
		||||
    await APIClient.exec({
 | 
			
		||||
      method: "PATCH",
 | 
			
		||||
      uri: `/family/${user.family_id}/user/${user.user_id}`,
 | 
			
		||||
      jsonData: {
 | 
			
		||||
        is_admin: user.is_admin,
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,20 +1,27 @@
 | 
			
		||||
import { ThemeProvider, createTheme } from "@mui/material/styles";
 | 
			
		||||
import React from "react";
 | 
			
		||||
import { PropsWithChildren } from "react";
 | 
			
		||||
import { frFR as dataGridFr } from "@mui/x-data-grid";
 | 
			
		||||
 | 
			
		||||
const localStorageKey = "dark-theme";
 | 
			
		||||
 | 
			
		||||
const darkTheme = createTheme({
 | 
			
		||||
  palette: {
 | 
			
		||||
    mode: "dark",
 | 
			
		||||
const darkTheme = createTheme(
 | 
			
		||||
  {
 | 
			
		||||
    palette: {
 | 
			
		||||
      mode: "dark",
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
  dataGridFr
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const lightTheme = createTheme({
 | 
			
		||||
  palette: {
 | 
			
		||||
    mode: "light",
 | 
			
		||||
const lightTheme = createTheme(
 | 
			
		||||
  {
 | 
			
		||||
    palette: {
 | 
			
		||||
      mode: "light",
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
  dataGridFr
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
interface DarkThemeContext {
 | 
			
		||||
  enabled: boolean;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										74
									
								
								geneit_app/src/routes/family/FamilyUsersListRoute.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								geneit_app/src/routes/family/FamilyUsersListRoute.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,74 @@
 | 
			
		||||
import { DataGrid, GridColDef } from "@mui/x-data-grid";
 | 
			
		||||
import React from "react";
 | 
			
		||||
import { FamilyApi, FamilyUser } from "../../api/FamilyApi";
 | 
			
		||||
import { useAlert } from "../../context_providers/AlertDialogProvider";
 | 
			
		||||
import { AsyncWidget } from "../../widgets/AsyncWidget";
 | 
			
		||||
import { useUser } from "../../widgets/BaseAuthenticatedPage";
 | 
			
		||||
import { useFamily } from "../../widgets/BaseFamilyRoute";
 | 
			
		||||
import { FamilyPageTitle } from "../../widgets/FamilyPageTitle";
 | 
			
		||||
 | 
			
		||||
export function FamilyUsersListRoute(): React.ReactElement {
 | 
			
		||||
  const family = useFamily();
 | 
			
		||||
 | 
			
		||||
  const [users, setUsers] = React.useState<FamilyUser[] | null>(null);
 | 
			
		||||
 | 
			
		||||
  const key = React.useRef(1);
 | 
			
		||||
 | 
			
		||||
  const load = async () => {
 | 
			
		||||
    setUsers(await FamilyApi.GetUsersList(family.family.family_id));
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <AsyncWidget
 | 
			
		||||
      loadKey={`${family.family.family_id}-${key.current}`}
 | 
			
		||||
      errMsg="Echec du chargement de la liste des utilisateurs de la famille !"
 | 
			
		||||
      load={load}
 | 
			
		||||
      ready={users !== null}
 | 
			
		||||
      build={() => (
 | 
			
		||||
        <>
 | 
			
		||||
          <FamilyPageTitle title="Utilisateurs de la famille" />
 | 
			
		||||
          <UsersTable users={users!} />
 | 
			
		||||
        </>
 | 
			
		||||
      )}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function UsersTable(p: { users: FamilyUser[] }): React.ReactElement {
 | 
			
		||||
  const alert = useAlert();
 | 
			
		||||
  const user = useUser();
 | 
			
		||||
  const family = useFamily();
 | 
			
		||||
 | 
			
		||||
  const columns: GridColDef[] = [
 | 
			
		||||
    { field: "user_id", headerName: "#", flex: 1 },
 | 
			
		||||
    { field: "user_mail", headerName: "Adresse mail", flex: 5 },
 | 
			
		||||
    { field: "user_name", headerName: "Nom d'utilisateur", flex: 5 },
 | 
			
		||||
    {
 | 
			
		||||
      field: "is_admin",
 | 
			
		||||
      headerName: "Admin",
 | 
			
		||||
      flex: 2,
 | 
			
		||||
      type: "boolean",
 | 
			
		||||
      editable: family.family.is_admin,
 | 
			
		||||
    },
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <DataGrid
 | 
			
		||||
      style={{ flex: "1" }}
 | 
			
		||||
      rows={p.users}
 | 
			
		||||
      columns={columns}
 | 
			
		||||
      autoPageSize
 | 
			
		||||
      editMode="cell"
 | 
			
		||||
      getRowId={(c) => c.user_id}
 | 
			
		||||
      isCellEditable={(params) => params.row.user_id !== user.user.id}
 | 
			
		||||
      processRowUpdate={async (n: FamilyUser) => {
 | 
			
		||||
        await FamilyApi.UpdateUser(n);
 | 
			
		||||
        return n;
 | 
			
		||||
      }}
 | 
			
		||||
      onProcessRowUpdateError={(e) => {
 | 
			
		||||
        console.error(e);
 | 
			
		||||
        alert.alert("Échec de la mise à jour des informations utilisateurs !");
 | 
			
		||||
      }}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
@@ -184,6 +184,8 @@ export function BaseFamilyRoute(): React.ReactElement {
 | 
			
		||||
                flexGrow: 1,
 | 
			
		||||
                overflow: "auto",
 | 
			
		||||
                padding: "20px",
 | 
			
		||||
                display: "flex",
 | 
			
		||||
                flexDirection: "column",
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              <Outlet />
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								geneit_app/src/widgets/FamilyPageTitle.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								geneit_app/src/widgets/FamilyPageTitle.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
import { Typography } from "@mui/material";
 | 
			
		||||
 | 
			
		||||
export function FamilyPageTitle(p: { title: string }): React.ReactElement {
 | 
			
		||||
  return (
 | 
			
		||||
    <Typography variant="h4" style={{ margin: "20px" }}>
 | 
			
		||||
      {p.title}
 | 
			
		||||
    </Typography>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user