diff --git a/geneit_app/package-lock.json b/geneit_app/package-lock.json
index 0d0bc66..f8a3741 100644
--- a/geneit_app/package-lock.json
+++ b/geneit_app/package-lock.json
@@ -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",
diff --git a/geneit_app/package.json b/geneit_app/package.json
index a59d199..2e44c01 100644
--- a/geneit_app/package.json
+++ b/geneit_app/package.json
@@ -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",
diff --git a/geneit_app/src/App.tsx b/geneit_app/src/App.tsx
index 779454d..2d5a9fd 100644
--- a/geneit_app/src/App.tsx
+++ b/geneit_app/src/App.tsx
@@ -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 {
} />
}>
} />
+ } />
} />
} />
diff --git a/geneit_app/src/api/ApiClient.ts b/geneit_app/src/api/ApiClient.ts
index 78bf5b4..4078ae8 100644
--- a/geneit_app/src/api/ApiClient.ts
+++ b/geneit_app/src/api/ApiClient.ts
@@ -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 {
diff --git a/geneit_app/src/api/FamilyApi.ts b/geneit_app/src/api/FamilyApi.ts
index c6ed444..dd541aa 100644
--- a/geneit_app/src/api/FamilyApi.ts
+++ b/geneit_app/src/api/FamilyApi.ts
@@ -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 {
+ return (
+ await APIClient.exec({
+ method: "GET",
+ uri: `/family/${id}/users`,
+ })
+ ).data;
+ }
+
+ /**
+ * Update a user of the family
+ */
+ static async UpdateUser(user: FamilyUser): Promise {
+ await APIClient.exec({
+ method: "PATCH",
+ uri: `/family/${user.family_id}/user/${user.user_id}`,
+ jsonData: {
+ is_admin: user.is_admin,
+ },
+ });
+ }
}
diff --git a/geneit_app/src/context_providers/DarkThemeProvider.tsx b/geneit_app/src/context_providers/DarkThemeProvider.tsx
index b410df5..1982dd9 100644
--- a/geneit_app/src/context_providers/DarkThemeProvider.tsx
+++ b/geneit_app/src/context_providers/DarkThemeProvider.tsx
@@ -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;
diff --git a/geneit_app/src/routes/family/FamilyUsersListRoute.tsx b/geneit_app/src/routes/family/FamilyUsersListRoute.tsx
new file mode 100644
index 0000000..8794d42
--- /dev/null
+++ b/geneit_app/src/routes/family/FamilyUsersListRoute.tsx
@@ -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(null);
+
+ const key = React.useRef(1);
+
+ const load = async () => {
+ setUsers(await FamilyApi.GetUsersList(family.family.family_id));
+ };
+
+ return (
+ (
+ <>
+
+
+ >
+ )}
+ />
+ );
+}
+
+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 (
+ 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 !");
+ }}
+ />
+ );
+}
diff --git a/geneit_app/src/widgets/BaseFamilyRoute.tsx b/geneit_app/src/widgets/BaseFamilyRoute.tsx
index d909a8d..be52588 100644
--- a/geneit_app/src/widgets/BaseFamilyRoute.tsx
+++ b/geneit_app/src/widgets/BaseFamilyRoute.tsx
@@ -184,6 +184,8 @@ export function BaseFamilyRoute(): React.ReactElement {
flexGrow: 1,
overflow: "auto",
padding: "20px",
+ display: "flex",
+ flexDirection: "column",
}}
>
diff --git a/geneit_app/src/widgets/FamilyPageTitle.tsx b/geneit_app/src/widgets/FamilyPageTitle.tsx
new file mode 100644
index 0000000..f7111e8
--- /dev/null
+++ b/geneit_app/src/widgets/FamilyPageTitle.tsx
@@ -0,0 +1,9 @@
+import { Typography } from "@mui/material";
+
+export function FamilyPageTitle(p: { title: string }): React.ReactElement {
+ return (
+
+ {p.title}
+
+ );
+}
diff --git a/geneit_backend/src/main.rs b/geneit_backend/src/main.rs
index 26f7f52..f937068 100644
--- a/geneit_backend/src/main.rs
+++ b/geneit_backend/src/main.rs
@@ -18,7 +18,7 @@ async fn main() -> std::io::Result<()> {
.wrap(
Cors::default()
.allowed_origin(&AppConfig::get().website_origin)
- .allowed_methods(vec!["GET", "POST"])
+ .allowed_methods(vec!["GET", "POST", "PATCH", "DELETE"])
.allowed_header("X-Auth-Token")
.allow_any_header()
.supports_credentials()
diff --git a/geneit_backend/src/schema.rs b/geneit_backend/src/schema.rs
index 9e5012b..82e09a7 100644
--- a/geneit_backend/src/schema.rs
+++ b/geneit_backend/src/schema.rs
@@ -44,8 +44,4 @@ diesel::table! {
diesel::joinable!(memberships -> families (family_id));
diesel::joinable!(memberships -> users (user_id));
-diesel::allow_tables_to_appear_in_same_query!(
- families,
- memberships,
- users,
-);
+diesel::allow_tables_to_appear_in_same_query!(families, memberships, users,);