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,);