@deix/rossini-identity-management 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/lib/app/[locale]/groups/{page.jsx → page.js} +1 -1
  2. package/lib/app/[locale]/{layout.jsx → layout.js} +3 -4
  3. package/lib/app/[locale]/users/{page.jsx → page.js} +1 -1
  4. package/lib/app/{layout.jsx → layout.js} +3 -7
  5. package/lib/components/AddItemDialog.js +45 -0
  6. package/lib/components/DeleteConfirmationDialog.js +35 -0
  7. package/lib/components/SearchField.js +16 -0
  8. package/lib/components/groups/{CreateGroupDialog.jsx → CreateGroupDialog.js} +4 -6
  9. package/lib/components/groups/{GroupDetails.jsx → GroupDetails.js} +2 -3
  10. package/lib/components/groups/GroupForm.js +38 -0
  11. package/lib/components/groups/GroupTabs.js +29 -0
  12. package/lib/components/groups/GroupUsers.js +73 -0
  13. package/lib/components/groups/Groups.js +97 -0
  14. package/lib/components/groups/GroupsTable.js +43 -0
  15. package/lib/components/users/{CreateUserDialog.jsx → CreateUserDialog.js} +4 -6
  16. package/lib/components/users/{UserCredentials.jsx → UserCredentials.js} +2 -5
  17. package/lib/components/users/{UserDetails.jsx → UserDetails.js} +2 -3
  18. package/lib/components/users/UserForm.js +66 -0
  19. package/lib/components/users/UserGroups.js +63 -0
  20. package/lib/components/users/{Users.jsx → Users.js} +41 -54
  21. package/lib/components/users/UsersTable.js +64 -0
  22. package/lib/components/users/UsersTabs.js +32 -0
  23. package/lib/config/{index.jsx → index.js} +1 -3
  24. package/lib/tsconfig.tsbuildinfo +1 -1
  25. package/lib/utils/hooks/{useAPIClient.jsx → useAPIClient.js} +2 -4
  26. package/package.json +6 -5
  27. package/lib/components/AddItemDialog.jsx +0 -50
  28. package/lib/components/DeleteConfirmationDialog.jsx +0 -44
  29. package/lib/components/SearchField.jsx +0 -18
  30. package/lib/components/groups/GroupForm.jsx +0 -46
  31. package/lib/components/groups/GroupTabs.jsx +0 -33
  32. package/lib/components/groups/GroupUsers.jsx +0 -88
  33. package/lib/components/groups/Groups.jsx +0 -110
  34. package/lib/components/groups/GroupsTable.jsx +0 -55
  35. package/lib/components/users/UserForm.jsx +0 -79
  36. package/lib/components/users/UserGroups.jsx +0 -78
  37. package/lib/components/users/UsersTable.jsx +0 -78
  38. package/lib/components/users/UsersTabs.jsx +0 -36
@@ -4,6 +4,6 @@ import { useConfig } from '../../../config';
4
4
  import Groups from '../../../components/groups/Groups';
5
5
  const GroupsPage = () => {
6
6
  const config = useConfig();
7
- return <Groups apiUrl={config.generalProperties.apiURL}/>;
7
+ return React.createElement(Groups, { apiUrl: config.generalProperties.apiURL });
8
8
  };
9
9
  export default GroupsPage;
@@ -18,8 +18,7 @@ export default function RootLayout({ children, }) {
18
18
  },
19
19
  },
20
20
  });
21
- return (<QueryClientProvider client={queryClient}>
22
- <PageProvider>{children}</PageProvider>
23
- <ReactQueryDevtools initialIsOpen={false} buttonPosition='bottom-right'/>
24
- </QueryClientProvider>);
21
+ return (React.createElement(QueryClientProvider, { client: queryClient },
22
+ React.createElement(PageProvider, null, children),
23
+ React.createElement(ReactQueryDevtools, { initialIsOpen: false, buttonPosition: 'bottom-right' })));
25
24
  }
@@ -4,6 +4,6 @@ import { useConfig } from '../../../config';
4
4
  import Users from '../../../components/users/Users';
5
5
  const UsersPage = () => {
6
6
  const config = useConfig();
7
- return <Users apiUrl={config.generalProperties.apiURL} editUsername/>;
7
+ return React.createElement(Users, { apiUrl: config.generalProperties.apiURL, editUsername: true });
8
8
  };
9
9
  export default UsersPage;
@@ -14,11 +14,7 @@ export default function RootLayout({ children, }) {
14
14
  LOGOUT_URL: process.env.LOGOUT_URL || '',
15
15
  ACCOUNT_SETTINGS_URL: process.env.ACCOUNT_SETTINGS_URL || '',
16
16
  };
17
- return (<html lang='en'>
18
- <body>
19
- <ConfigProvider publicRuntimeEnv={publicRuntimeEnv}>
20
- {children}
21
- </ConfigProvider>
22
- </body>
23
- </html>);
17
+ return (React.createElement("html", { lang: 'en' },
18
+ React.createElement("body", null,
19
+ React.createElement(ConfigProvider, { publicRuntimeEnv: publicRuntimeEnv }, children))));
24
20
  }
@@ -0,0 +1,45 @@
1
+ import React from 'react';
2
+ import { Autocomplete, Button, Dialog, DialogActions, DialogContent, DialogTitle, TextField, } from '@mui/material';
3
+ import { useLocale } from '@deix/rossini-core';
4
+ import { useEffect, useState } from 'react';
5
+ const translations = {
6
+ ok: {
7
+ en: 'Add',
8
+ it: 'Aggiungi',
9
+ },
10
+ cancel: {
11
+ en: 'Cancel',
12
+ it: 'Annulla',
13
+ },
14
+ search: {
15
+ en: 'Search...',
16
+ it: 'Cerca...',
17
+ },
18
+ };
19
+ const AddItemDialog = ({ title, options, open, onClose, onConfirm, }) => {
20
+ const locale = useLocale();
21
+ const [value, setValue] = useState(null);
22
+ // Reset the value when the dialog is closed
23
+ useEffect(() => {
24
+ if (!open) {
25
+ setValue(null);
26
+ }
27
+ }, [open]);
28
+ return (React.createElement(Dialog, { open: open, fullWidth: true, maxWidth: 'sm', onClose: onClose },
29
+ React.createElement(DialogTitle, null, title[locale]),
30
+ React.createElement(DialogContent, null,
31
+ React.createElement(Autocomplete
32
+ // multiple
33
+ , {
34
+ // multiple
35
+ options: options ?? [], getOptionLabel: (option) => option.label, getOptionKey: (option) => option.value, isOptionEqualToValue: (option, value) => option.value === value.value, renderInput: (params) => (React.createElement(TextField, { ...params, label: translations.search[locale], placeholder: '' })), onChange: (event, newValue) => {
36
+ setValue(newValue);
37
+ }, value: value })),
38
+ React.createElement(DialogActions, null,
39
+ React.createElement(Button, { size: 'small', variant: 'outlined', onClick: onClose }, translations.cancel[locale]),
40
+ React.createElement(Button, { variant: 'contained', onClick: () => {
41
+ onConfirm(value);
42
+ onClose();
43
+ } }, translations.ok[locale]))));
44
+ };
45
+ export default AddItemDialog;
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+ import { Button, Dialog, DialogActions, DialogContent, DialogTitle, Typography, } from '@mui/material';
3
+ import { useLocale } from '@deix/rossini-core';
4
+ const translations = {
5
+ title: {
6
+ en: 'Delete item?',
7
+ it: 'Cancellare elemento?',
8
+ },
9
+ message: {
10
+ en: 'This is a permanent action',
11
+ it: "Questa è un'azione permanente",
12
+ },
13
+ yes: {
14
+ en: 'Yes',
15
+ it: 'Si',
16
+ },
17
+ no: {
18
+ en: 'No',
19
+ it: 'No',
20
+ },
21
+ };
22
+ const DeleteConfirmationDialog = ({ open, onClose, onConfirm, }) => {
23
+ const locale = useLocale();
24
+ return (React.createElement(Dialog, { open: open, fullWidth: true, maxWidth: 'sm', onClose: onClose },
25
+ React.createElement(DialogTitle, null, translations.title[locale]),
26
+ React.createElement(DialogContent, null,
27
+ React.createElement(Typography, { variant: 'caption' }, translations.message[locale])),
28
+ React.createElement(DialogActions, null,
29
+ React.createElement(Button, { size: 'small', variant: 'outlined', onClick: onClose }, translations.no[locale]),
30
+ React.createElement(Button, { size: 'small', variant: 'contained', onClick: () => {
31
+ onConfirm();
32
+ onClose();
33
+ } }, translations.yes[locale]))));
34
+ };
35
+ export default DeleteConfirmationDialog;
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import SearchIcon from '@mui/icons-material/Search';
3
+ import { IconButton, TextField, } from '@mui/material';
4
+ import { useState } from 'react';
5
+ const SearchField = ({ placeholder = 'Search...', onSearch, ...props }) => {
6
+ const [value, setValue] = useState('');
7
+ return (React.createElement(React.Fragment, null,
8
+ React.createElement(TextField, { size: 'small', placeholder: placeholder, value: value, onChange: (evt) => {
9
+ setValue(evt.target.value);
10
+ onSearch(evt.target.value);
11
+ }, ...props, InputProps: {
12
+ endAdornment: (React.createElement(IconButton, { size: 'small', onClick: () => onSearch(value) },
13
+ React.createElement(SearchIcon, null))),
14
+ } })));
15
+ };
16
+ export default SearchField;
@@ -18,11 +18,9 @@ const CreateGroupDialog = ({ open, onClose, onGroupCreate, }) => {
18
18
  }
19
19
  onClose();
20
20
  };
21
- return (<Dialog open={open} fullWidth maxWidth='sm' onClose={onClose}>
22
- <DialogTitle>{groupsTranslations.create[locale]}</DialogTitle>
23
- <DialogContent>
24
- <GroupForm mode='create' onSave={handleSave} onCancel={onClose}/>
25
- </DialogContent>
26
- </Dialog>);
21
+ return (React.createElement(Dialog, { open: open, fullWidth: true, maxWidth: 'sm', onClose: onClose },
22
+ React.createElement(DialogTitle, null, groupsTranslations.create[locale]),
23
+ React.createElement(DialogContent, null,
24
+ React.createElement(GroupForm, { mode: 'create', onSave: handleSave, onCancel: onClose }))));
27
25
  };
28
26
  export default CreateGroupDialog;
@@ -13,8 +13,7 @@ const GroupDetails = ({ group, onGroupUpdate, editName, }) => {
13
13
  onGroupUpdate(updatedGroup, group);
14
14
  }
15
15
  };
16
- return (<>
17
- <GroupForm mode='modify' group={group} onSave={handleUpdate} onCancel={() => { }} editName={editName}/>
18
- </>);
16
+ return (React.createElement(React.Fragment, null,
17
+ React.createElement(GroupForm, { mode: 'modify', group: group, onSave: handleUpdate, onCancel: () => { }, editName: editName })));
19
18
  };
20
19
  export default GroupDetails;
@@ -0,0 +1,38 @@
1
+ import React from 'react';
2
+ import { useLocale } from '@deix/rossini-core';
3
+ import { Button, Grid, TextField } from '@mui/material';
4
+ import { Controller, useForm } from 'react-hook-form';
5
+ import { useEffect } from 'react';
6
+ import groupsTranslations from '../../lang/groups';
7
+ const GroupForm = ({ group, onCancel, onSave, mode, editName, }) => {
8
+ const locale = useLocale();
9
+ const { control, handleSubmit, setValue } = useForm({
10
+ defaultValues: {
11
+ id: group?.id,
12
+ name: group?.name || '',
13
+ },
14
+ });
15
+ useEffect(() => {
16
+ if (group) {
17
+ setValue('id', group?.id);
18
+ setValue('name', group?.name);
19
+ }
20
+ }, [group, setValue]);
21
+ const onSubmit = (data) => {
22
+ onSave(data);
23
+ };
24
+ return (React.createElement("form", { onSubmit: (event) => void handleSubmit(onSubmit)(event) },
25
+ React.createElement(Grid, { container: true, spacing: 2, sx: { mt: 2, maxWidth: 'sm' }, justifyContent: 'stretch' },
26
+ React.createElement(Grid, { size: { xs: 12 } },
27
+ React.createElement(Controller, { name: 'name', control: control, render: ({ field }) => (React.createElement(TextField, { size: 'small', fullWidth: true, label: groupsTranslations.name[locale], disabled: mode === 'modify' && !editName, sx: { flexShrink: 0 }, ...field })) })),
28
+ React.createElement(Grid, { sx: {
29
+ mt: 2,
30
+ display: 'flex',
31
+ justifyContent: 'flex-end',
32
+ gap: 2,
33
+ width: '100%',
34
+ } },
35
+ mode === 'create' && (React.createElement(Button, { variant: 'outlined', onClick: onCancel }, groupsTranslations.cancel[locale])),
36
+ React.createElement(Button, { variant: 'contained', type: 'submit' }, groupsTranslations.save[locale])))));
37
+ };
38
+ export default GroupForm;
@@ -0,0 +1,29 @@
1
+ 'use client';
2
+ import React, { useState } from 'react';
3
+ import { Box, Tab, Tabs } from '@mui/material';
4
+ import { useLocale } from '@deix/rossini-core';
5
+ import GroupDetails from './GroupDetails';
6
+ import groupsTranslations from '../../lang/groups';
7
+ import GroupUsers from './GroupUsers';
8
+ const GroupTabs = ({ group, additionalTabs = [], onGroupUpdate, editName, }) => {
9
+ const locale = useLocale();
10
+ const [selectedTab, setSelectedTab] = useState('DETAILS');
11
+ return (React.createElement(Box, { sx: {
12
+ flexGrow: 1,
13
+ maxWidth: '100%',
14
+ overflowX: 'hidden',
15
+ alignSelf: 'stretch',
16
+ } },
17
+ React.createElement(Box, { sx: { borderBottom: 1, borderColor: 'divider' } },
18
+ React.createElement(Tabs, { value: selectedTab, onChange: (_evt, newValue) => {
19
+ setSelectedTab(newValue);
20
+ }, "aria-label": 'basic tabs example' },
21
+ React.createElement(Tab, { label: groupsTranslations.details[locale], value: 'DETAILS' }),
22
+ React.createElement(Tab, { label: groupsTranslations.users[locale], value: 'USERS' }),
23
+ additionalTabs.map((tab, index) => (React.createElement(Tab, { key: `tab${index}`, label: tab.title[locale], value: `ADDITIONAL_TAB_${index}` }))))),
24
+ React.createElement(Box, { sx: { p: 1 } },
25
+ selectedTab === 'DETAILS' && (React.createElement(GroupDetails, { group: group, onGroupUpdate: onGroupUpdate, editName: editName })),
26
+ selectedTab === 'USERS' && React.createElement(GroupUsers, { group: group }),
27
+ additionalTabs.map((tab, index) => selectedTab === `ADDITIONAL_TAB_${index}` && (React.createElement(tab.component, { key: `tab${index}`, group: group }))))));
28
+ };
29
+ export default GroupTabs;
@@ -0,0 +1,73 @@
1
+ import React from 'react';
2
+ import { Box, Button, IconButton, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, } from '@mui/material';
3
+ import AddIcon from '@mui/icons-material/Add';
4
+ import DeleteIcon from '@mui/icons-material/Delete';
5
+ import { isStringTranslation, useLocale } from '@deix/rossini-core';
6
+ import { useAddUserToGroup, useRemoveUserFromGroup, useUsers, } from '../../utils/hooks/users';
7
+ import { useState } from 'react';
8
+ import DeleteConfirmationDialog from '../DeleteConfirmationDialog';
9
+ import { useQueryClient } from '@tanstack/react-query';
10
+ import AddItemDialog from '../AddItemDialog';
11
+ import { useGroupUsers } from '../../utils/hooks/groups';
12
+ import groupsTranslations from '../../lang/groups';
13
+ import { useAPIClient } from '../../utils/hooks/useAPIClient';
14
+ const groupUsersTableColumns = [
15
+ {
16
+ id: 'username',
17
+ label: { en: 'Username', it: 'Username' },
18
+ },
19
+ {
20
+ id: 'firstName',
21
+ label: { en: 'First name', it: 'Nome' },
22
+ },
23
+ {
24
+ id: 'lastName',
25
+ label: { en: 'Last name', it: 'Cognome' },
26
+ },
27
+ ];
28
+ const GroupUsers = ({ group }) => {
29
+ const locale = useLocale();
30
+ const apiClient = useAPIClient();
31
+ const queryClient = useQueryClient();
32
+ const { data: users } = useUsers(apiClient);
33
+ const { data: groupUsers } = useGroupUsers(group?.id, apiClient);
34
+ const { mutate: addUserToGroup } = useAddUserToGroup(queryClient, apiClient);
35
+ const { mutate: removeUserFromGroup } = useRemoveUserFromGroup(queryClient, apiClient);
36
+ const [openAddUserDialog, setOpenAddUserDialog] = useState(false);
37
+ const [openConfirmationDialog, setOpenConfirmationDialog] = useState(false);
38
+ const [deleteId, setDeleteId] = useState();
39
+ return (React.createElement(React.Fragment, null,
40
+ groupUsers && groupUsers.length > 0 && (React.createElement(Box, { maxWidth: 'md' },
41
+ React.createElement(TableContainer, null,
42
+ React.createElement(Table, { size: 'small' },
43
+ React.createElement(TableHead, null,
44
+ React.createElement(TableRow, null,
45
+ groupUsersTableColumns.map((col) => (React.createElement(TableCell, { key: col.id, align: col.align || 'inherit' }, isStringTranslation(col.label)
46
+ ? col.label[locale]
47
+ : col.label))),
48
+ React.createElement(TableCell, { padding: 'none' }, ''))),
49
+ React.createElement(TableBody, null, groupUsers?.map((user, rowIdx) => (React.createElement(TableRow, { key: rowIdx },
50
+ React.createElement(TableCell, null, user.username),
51
+ React.createElement(TableCell, null, user.firstName),
52
+ React.createElement(TableCell, null, user.lastName),
53
+ React.createElement(TableCell, { padding: 'none', align: 'right' },
54
+ React.createElement(IconButton, { size: 'small', onClick: (evt) => {
55
+ evt.stopPropagation();
56
+ setOpenConfirmationDialog(true);
57
+ setDeleteId(user.id);
58
+ } },
59
+ React.createElement(DeleteIcon, null))))))))))),
60
+ React.createElement(Button, { variant: 'outlined', startIcon: React.createElement(AddIcon, null), onClick: () => setOpenAddUserDialog(true), sx: { marginTop: 2 } }, groupsTranslations.addUser[locale]),
61
+ React.createElement(AddItemDialog, { open: openAddUserDialog, onClose: () => setOpenAddUserDialog(false), onConfirm: (value) => {
62
+ if (value) {
63
+ addUserToGroup({ userId: value?.value, groupId: group.id });
64
+ }
65
+ }, title: groupsTranslations.addUser, options: users?.map((user) => ({
66
+ value: user.id,
67
+ label: user.username || '',
68
+ })) }),
69
+ React.createElement(DeleteConfirmationDialog, { open: openConfirmationDialog, onClose: () => setOpenConfirmationDialog(false), onConfirm: () => deleteId
70
+ ? removeUserFromGroup({ userId: deleteId, groupId: group.id })
71
+ : {} })));
72
+ };
73
+ export default GroupUsers;
@@ -0,0 +1,97 @@
1
+ 'use client';
2
+ import React, { useEffect, useState } from 'react';
3
+ import GroupAddIcon from '@mui/icons-material/GroupAdd';
4
+ import { Box, Button, Grid, IconButton, Link } from '@mui/material';
5
+ import ArrowBackIcon from '@mui/icons-material/ArrowBack';
6
+ import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
7
+ import { useAPI, useLocale } from '@deix/rossini-core';
8
+ import { SearchField } from '..';
9
+ import CreateGroupDialog from './CreateGroupDialog';
10
+ import GroupsTable from './GroupsTable';
11
+ import groupsTranslations from '../../lang/groups';
12
+ import { useDeleteGroup, useGroups } from '../../utils/hooks/groups';
13
+ import GroupTabs from './GroupTabs';
14
+ import { useQueryClient } from '@tanstack/react-query';
15
+ import { APIClientProvider } from '../../utils/hooks/useAPIClient';
16
+ const Groups = ({ apiUrl, additionalTabs = [], onGroupUpdate, onGroupCreate, onGroupDelete, editName, }) => {
17
+ const locale = useLocale();
18
+ const apiClient = useAPI(apiUrl);
19
+ const queryClient = useQueryClient();
20
+ const { data: groups } = useGroups(apiClient);
21
+ const { mutate: deleteGroup } = useDeleteGroup(queryClient, apiClient);
22
+ const [searchString, setSearchString] = useState('');
23
+ const [filteredGroups, setFilteredGroups] = useState();
24
+ const [selectedGroup, setSelectedGroup] = useState();
25
+ const [openGroupDialog, setOpenGroupDialog] = useState(false);
26
+ // Update the filtered groups using the search string
27
+ useEffect(() => {
28
+ if (groups && searchString === '') {
29
+ setFilteredGroups(groups);
30
+ }
31
+ else if (groups && searchString !== '') {
32
+ setFilteredGroups(groups.filter((group) => group.name.includes(searchString)));
33
+ }
34
+ }, [searchString, groups, setFilteredGroups]);
35
+ // If the groups change updated the selected group to reflect the chages
36
+ useEffect(() => {
37
+ if (selectedGroup) {
38
+ const updatedSelectedGroup = groups?.find((group) => group.id === selectedGroup.id);
39
+ if (updatedSelectedGroup) {
40
+ setSelectedGroup(updatedSelectedGroup);
41
+ }
42
+ else {
43
+ // deselect the group if it doesn't exist anymore
44
+ setSelectedGroup(undefined);
45
+ }
46
+ }
47
+ }, [groups, selectedGroup, setSelectedGroup]);
48
+ const handleDelete = (group) => {
49
+ deleteGroup(group.id);
50
+ if (typeof onGroupDelete === 'function') {
51
+ onGroupDelete(group);
52
+ }
53
+ setSelectedGroup(undefined);
54
+ };
55
+ return (React.createElement(APIClientProvider, { baseURL: apiUrl },
56
+ React.createElement(Box, { sx: {
57
+ flexGrow: 1,
58
+ maxWidth: '100%',
59
+ overflowX: 'hidden',
60
+ paddingTop: 3,
61
+ paddingLeft: 3,
62
+ paddingRight: 3,
63
+ } },
64
+ !selectedGroup && (React.createElement(React.Fragment, null,
65
+ React.createElement(Grid, { container: true, spacing: 2, sx: { marginBottom: 2 } },
66
+ React.createElement(Grid, null,
67
+ React.createElement(SearchField, { placeholder: groupsTranslations.search[locale], onSearch: (value) => setSearchString(value) })),
68
+ React.createElement(Grid, null,
69
+ React.createElement(Button, { variant: 'contained', startIcon: React.createElement(GroupAddIcon, null), onClick: () => {
70
+ setOpenGroupDialog(true);
71
+ setSelectedGroup(undefined);
72
+ } }, groupsTranslations.new[locale]))),
73
+ React.createElement(GroupsTable, { groups: filteredGroups, selectedGroup: selectedGroup, setSelectedGroup: (group) => {
74
+ setSelectedGroup(group);
75
+ }, onDelete: handleDelete }))),
76
+ selectedGroup && (React.createElement(Box, { sx: {
77
+ display: 'flex',
78
+ flexDirection: 'column',
79
+ alignItems: 'flex-start',
80
+ width: '100%',
81
+ } },
82
+ React.createElement(Box, { sx: {
83
+ display: 'flex',
84
+ flexDirection: 'row',
85
+ alignItems: 'center',
86
+ } },
87
+ React.createElement(IconButton, { "aria-label": 'delete', color: 'primary', onClick: () => setSelectedGroup(undefined), sx: { mr: 2 } },
88
+ React.createElement(ArrowBackIcon, null)),
89
+ React.createElement(Link, { underline: 'none', href: '#', onClick: () => setSelectedGroup(undefined) }, groupsTranslations.title[locale]),
90
+ React.createElement(KeyboardArrowRightIcon, null),
91
+ selectedGroup.name),
92
+ React.createElement(GroupTabs, { group: selectedGroup, additionalTabs: additionalTabs, onGroupUpdate: onGroupUpdate, editName: editName }))),
93
+ React.createElement(CreateGroupDialog, { open: openGroupDialog, onClose: () => {
94
+ setOpenGroupDialog(false);
95
+ }, onGroupCreate: onGroupCreate }))));
96
+ };
97
+ export default Groups;
@@ -0,0 +1,43 @@
1
+ import React from 'react';
2
+ import { isStringTranslation, useLocale } from '@deix/rossini-core';
3
+ import DeleteIcon from '@mui/icons-material/Delete';
4
+ import { IconButton, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, useTheme, } from '@mui/material';
5
+ import { useState } from 'react';
6
+ import DeleteConfirmationDialog from '../DeleteConfirmationDialog';
7
+ const groupsTableColumns = [
8
+ {
9
+ id: 'name',
10
+ label: { en: 'Name', it: 'Nome' },
11
+ },
12
+ ];
13
+ const GroupsTable = ({ groups = [], selectedGroup, setSelectedGroup, onDelete, }) => {
14
+ const locale = useLocale();
15
+ const muiTheme = useTheme();
16
+ const [openConfirmationDialog, setOpenConfirmationDialog] = useState(false);
17
+ const [deleteGroup, setDeleteGroup] = useState();
18
+ return (React.createElement(React.Fragment, null,
19
+ React.createElement(TableContainer, null,
20
+ React.createElement(Table, { size: 'small' },
21
+ React.createElement(TableHead, null,
22
+ React.createElement(TableRow, null,
23
+ groupsTableColumns.map((col) => (React.createElement(TableCell, { key: col.id, align: col.align || 'inherit' }, isStringTranslation(col.label)
24
+ ? col.label[locale]
25
+ : col.label))),
26
+ React.createElement(TableCell, { padding: 'none' }, ''))),
27
+ React.createElement(TableBody, null, groups.map((group, rowIdx) => (React.createElement(TableRow, { key: rowIdx, onClick: () => setSelectedGroup(group), sx: {
28
+ cursor: 'pointer',
29
+ background: selectedGroup && group.id === selectedGroup.id
30
+ ? muiTheme.palette.primary.light
31
+ : undefined,
32
+ } },
33
+ React.createElement(TableCell, null, group.name),
34
+ React.createElement(TableCell, { padding: 'none', align: 'right' },
35
+ React.createElement(IconButton, { size: 'small', onClick: (evt) => {
36
+ evt.stopPropagation();
37
+ setOpenConfirmationDialog(true);
38
+ setDeleteGroup(group);
39
+ } },
40
+ React.createElement(DeleteIcon, null))))))))),
41
+ React.createElement(DeleteConfirmationDialog, { open: openConfirmationDialog, onClose: () => setOpenConfirmationDialog(false), onConfirm: () => (deleteGroup ? onDelete(deleteGroup) : {}) })));
42
+ };
43
+ export default GroupsTable;
@@ -31,11 +31,9 @@ const CreateUserDialog = ({ open, onClose, onUserCreate, filteredGroup, emailAsU
31
31
  });
32
32
  onClose();
33
33
  };
34
- return (<Dialog open={open} fullWidth maxWidth='sm' onClose={onClose}>
35
- <DialogTitle>{usersTranslations.create[locale]}</DialogTitle>
36
- <DialogContent>
37
- <UserForm mode='create' onSave={handleSave} onCancel={onClose} emailAsUsername={emailAsUsername}/>
38
- </DialogContent>
39
- </Dialog>);
34
+ return (React.createElement(Dialog, { open: open, fullWidth: true, maxWidth: 'sm', onClose: onClose },
35
+ React.createElement(DialogTitle, null, usersTranslations.create[locale]),
36
+ React.createElement(DialogContent, null,
37
+ React.createElement(UserForm, { mode: 'create', onSave: handleSave, onCancel: onClose, emailAsUsername: emailAsUsername }))));
40
38
  };
41
39
  export default CreateUserDialog;
@@ -10,10 +10,7 @@ const UserCredentials = ({ user }) => {
10
10
  const apiClient = useAPIClient();
11
11
  const queryClient = useQueryClient();
12
12
  const { mutate: sendResetPasswordEmail } = useSendUpdatePasswordEmail(queryClient, apiClient);
13
- return (<Box sx={{ mt: 2 }}>
14
- <Button variant='contained' onClick={() => sendResetPasswordEmail(user.id)}>
15
- {usersTranslations.sendResetPasswordEmail[locale]}
16
- </Button>
17
- </Box>);
13
+ return (React.createElement(Box, { sx: { mt: 2 } },
14
+ React.createElement(Button, { variant: 'contained', onClick: () => sendResetPasswordEmail(user.id) }, usersTranslations.sendResetPasswordEmail[locale])));
18
15
  };
19
16
  export default UserCredentials;
@@ -16,8 +16,7 @@ const UserDetails = ({ user, onUserUpdate, editUsername, emailAsUsername = false
16
16
  onUserUpdate(updatedUser, user);
17
17
  }
18
18
  };
19
- return (<>
20
- <UserForm mode='modify' user={user} onSave={handleUpdate} onCancel={() => { }} editUsername={editUsername} emailAsUsername={emailAsUsername}/>
21
- </>);
19
+ return (React.createElement(React.Fragment, null,
20
+ React.createElement(UserForm, { mode: 'modify', user: user, onSave: handleUpdate, onCancel: () => { }, editUsername: editUsername, emailAsUsername: emailAsUsername })));
22
21
  };
23
22
  export default UserDetails;
@@ -0,0 +1,66 @@
1
+ import React from 'react';
2
+ import { useLocale } from '@deix/rossini-core';
3
+ import { Button, FormControlLabel, Grid, Switch, TextField, } from '@mui/material';
4
+ import { Controller, useForm } from 'react-hook-form';
5
+ import { useEffect } from 'react';
6
+ import usersTranslations from '../../lang/users';
7
+ const UserForm = ({ user, onSave, onCancel, mode = 'create', editUsername, emailAsUsername = false, }) => {
8
+ const locale = useLocale();
9
+ const { control, formState: { errors }, handleSubmit, setValue, } = useForm({
10
+ defaultValues: {
11
+ id: user?.id,
12
+ username: user?.username || '',
13
+ firstName: user?.firstName || '',
14
+ lastName: user?.lastName || '',
15
+ email: user?.email || '',
16
+ emailVerified: user?.emailVerified || true,
17
+ enabled: user?.enabled || true,
18
+ sendEmailOnCreation: true,
19
+ },
20
+ });
21
+ useEffect(() => {
22
+ if (user) {
23
+ setValue('id', user?.id);
24
+ if (!emailAsUsername) {
25
+ setValue('username', user?.username);
26
+ }
27
+ setValue('firstName', user?.firstName);
28
+ setValue('lastName', user?.lastName);
29
+ setValue('email', user?.email);
30
+ setValue('emailVerified', user?.emailVerified);
31
+ setValue('enabled', user?.enabled);
32
+ setValue('groups', user?.groups);
33
+ // The user already exists, we don't want to send an email
34
+ setValue('sendEmailOnCreation', false);
35
+ }
36
+ }, [user, setValue]);
37
+ const onSubmit = (data) => {
38
+ onSave(data);
39
+ };
40
+ return (React.createElement("form", { onSubmit: (event) => void handleSubmit(onSubmit)(event) },
41
+ React.createElement(Grid, { container: true, spacing: 2, sx: { mt: 2, maxWidth: 'sm' }, justifyContent: 'stretch' },
42
+ !emailAsUsername && (React.createElement(Grid, { size: { xs: 12 } },
43
+ React.createElement(Controller, { name: 'username', control: control, render: ({ field }) => (React.createElement(TextField, { size: 'small', fullWidth: true, label: usersTranslations.username[locale], disabled: mode === 'modify' && !editUsername, sx: { flexShrink: 0 }, ...field })) }))),
44
+ React.createElement(Grid, { size: { xs: 12 } },
45
+ React.createElement(Controller, { name: 'firstName', control: control, render: ({ field }) => (React.createElement(TextField, { size: 'small', fullWidth: true, label: usersTranslations.firstName[locale], sx: { flexShrink: 0 }, ...field })) })),
46
+ React.createElement(Grid, { size: { xs: 12 } },
47
+ React.createElement(Controller, { name: 'lastName', control: control, render: ({ field }) => (React.createElement(TextField, { size: 'small', fullWidth: true, label: usersTranslations.lastName[locale], sx: { flexShrink: 0 }, ...field })) })),
48
+ React.createElement(Grid, { size: { xs: 12 } },
49
+ React.createElement(Controller, { name: 'email', control: control, rules: { required: true }, render: ({ field }) => (React.createElement(TextField, { size: 'small', fullWidth: true, error: !!errors.email, helperText: errors.email
50
+ ? usersTranslations.requiredField[locale]
51
+ : undefined, label: usersTranslations.email[locale], sx: { flexShrink: 0 }, ...field })) })),
52
+ React.createElement(Grid, { size: { xs: 12 } },
53
+ React.createElement(Controller, { name: 'enabled', control: control, render: ({ field }) => (React.createElement(FormControlLabel, { sx: { ml: 1 }, control: React.createElement(Switch, { checked: field.value, size: 'small', ...field }), label: usersTranslations.enabled[locale] })) })),
54
+ mode === 'create' && (React.createElement(Grid, { size: { xs: 12 } },
55
+ React.createElement(Controller, { name: 'sendEmailOnCreation', control: control, render: ({ field }) => (React.createElement(FormControlLabel, { sx: { ml: 1 }, control: React.createElement(Switch, { checked: field.value, size: 'small', ...field }), label: usersTranslations.sendResetPasswordEmail[locale] })) }))),
56
+ React.createElement(Grid, { sx: {
57
+ mt: 2,
58
+ display: 'flex',
59
+ justifyContent: 'flex-end',
60
+ gap: 2,
61
+ width: '100%',
62
+ } },
63
+ mode === 'create' && (React.createElement(Button, { variant: 'outlined', onClick: onCancel }, usersTranslations.cancel[locale])),
64
+ React.createElement(Button, { variant: 'contained', type: 'submit' }, usersTranslations.save[locale])))));
65
+ };
66
+ export default UserForm;
@@ -0,0 +1,63 @@
1
+ import React from 'react';
2
+ import { Box, Button, IconButton, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, } from '@mui/material';
3
+ import AddIcon from '@mui/icons-material/Add';
4
+ import DeleteIcon from '@mui/icons-material/Delete';
5
+ import { isStringTranslation, useLocale } from '@deix/rossini-core';
6
+ import { useAddUserToGroup, useRemoveUserFromGroup, useUserGroups, } from '../../utils/hooks/users';
7
+ import { useState } from 'react';
8
+ import DeleteConfirmationDialog from '../DeleteConfirmationDialog';
9
+ import { useQueryClient } from '@tanstack/react-query';
10
+ import usersTranslations from '../../lang/users';
11
+ import AddItemDialog from '../AddItemDialog';
12
+ import { useGroups } from '../../utils/hooks/groups';
13
+ import { useAPIClient } from '../../utils/hooks/useAPIClient';
14
+ const userGroupsTableColumns = [
15
+ {
16
+ id: 'name',
17
+ label: { en: 'Group', it: 'Gruppo' },
18
+ },
19
+ ];
20
+ const UserGroups = ({ user }) => {
21
+ const locale = useLocale();
22
+ const apiClient = useAPIClient();
23
+ const queryClient = useQueryClient();
24
+ const { data: groups } = useGroups(apiClient);
25
+ const { data: userGroups } = useUserGroups(user?.id, apiClient);
26
+ const { mutate: addUserToGroup } = useAddUserToGroup(queryClient, apiClient);
27
+ const { mutate: removeUserFromGroup } = useRemoveUserFromGroup(queryClient, apiClient);
28
+ const [openAddGroupDialog, setOpenAddGroupDialog] = useState(false);
29
+ const [openConfirmationDialog, setOpenConfirmationDialog] = useState(false);
30
+ const [deleteId, setDeleteId] = useState();
31
+ return (React.createElement(React.Fragment, null,
32
+ userGroups && userGroups.length > 0 && (React.createElement(Box, { maxWidth: 'sm' },
33
+ React.createElement(TableContainer, null,
34
+ React.createElement(Table, { size: 'small' },
35
+ React.createElement(TableHead, null,
36
+ React.createElement(TableRow, null,
37
+ userGroupsTableColumns.map((col) => (React.createElement(TableCell, { key: col.id, align: col.align || 'inherit' }, isStringTranslation(col.label)
38
+ ? col.label[locale]
39
+ : col.label))),
40
+ React.createElement(TableCell, { padding: 'none' }, ''))),
41
+ React.createElement(TableBody, null, userGroups?.map((group, rowIdx) => (React.createElement(TableRow, { key: rowIdx },
42
+ React.createElement(TableCell, null, group.name),
43
+ React.createElement(TableCell, { padding: 'none', align: 'right' },
44
+ React.createElement(IconButton, { size: 'small', onClick: (evt) => {
45
+ evt.stopPropagation();
46
+ setOpenConfirmationDialog(true);
47
+ setDeleteId(group.id);
48
+ } },
49
+ React.createElement(DeleteIcon, null))))))))))),
50
+ React.createElement(Button, { variant: 'outlined', startIcon: React.createElement(AddIcon, null), onClick: () => setOpenAddGroupDialog(true), sx: { marginTop: 2 } }, usersTranslations.addGroup[locale]),
51
+ React.createElement(AddItemDialog, { open: openAddGroupDialog, onClose: () => setOpenAddGroupDialog(false), onConfirm: (value) => {
52
+ if (value) {
53
+ addUserToGroup({ userId: user.id, groupId: value?.value });
54
+ }
55
+ }, title: usersTranslations.addGroup, options: groups?.map((group) => ({
56
+ value: group.id,
57
+ label: group.name,
58
+ })) }),
59
+ React.createElement(DeleteConfirmationDialog, { open: openConfirmationDialog, onClose: () => setOpenConfirmationDialog(false), onConfirm: () => deleteId
60
+ ? removeUserFromGroup({ userId: user.id, groupId: deleteId })
61
+ : {} })));
62
+ };
63
+ export default UserGroups;