@applica-software-guru/react-admin 1.3.151 → 1.3.153
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.
- package/dist/components/Layout/Header/Buttons/HeaderButton.d.ts +6 -0
- package/dist/components/Layout/Header/Buttons/HeaderButton.d.ts.map +1 -0
- package/dist/components/Layout/Header/Buttons/HeaderIconButton.d.ts +6 -0
- package/dist/components/Layout/Header/Buttons/HeaderIconButton.d.ts.map +1 -0
- package/dist/components/Layout/Header/Buttons/HeaderToggleButton.d.ts +6 -0
- package/dist/components/Layout/Header/Buttons/HeaderToggleButton.d.ts.map +1 -0
- package/dist/components/Layout/Header/Buttons/index.d.ts +4 -0
- package/dist/components/Layout/Header/Buttons/index.d.ts.map +1 -0
- package/dist/components/Layout/Header/DrawerToggle.d.ts.map +1 -1
- package/dist/components/Layout/Header/Notification/Notification.d.ts.map +1 -1
- package/dist/components/Layout/Header/Profile/Profile.d.ts +1 -1
- package/dist/components/Layout/Header/Profile/Profile.d.ts.map +1 -1
- package/dist/components/Layout/Header/ResponsiveSection.d.ts.map +1 -1
- package/dist/components/Layout/Header/index.d.ts +1 -0
- package/dist/components/Layout/Header/index.d.ts.map +1 -1
- package/dist/hooks/index.d.ts +13 -12
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/mui.d.ts +4 -1
- package/dist/hooks/mui.d.ts.map +1 -1
- package/dist/hooks/useLocalizedValue.d.ts +9 -0
- package/dist/hooks/useLocalizedValue.d.ts.map +1 -0
- package/dist/hooks/usePopoverState.d.ts +10 -0
- package/dist/hooks/usePopoverState.d.ts.map +1 -0
- package/dist/react-admin.cjs.js +57 -57
- package/dist/react-admin.cjs.js.map +1 -1
- package/dist/react-admin.es.js +7315 -7304
- package/dist/react-admin.es.js.map +1 -1
- package/dist/react-admin.umd.js +58 -58
- package/dist/react-admin.umd.js.map +1 -1
- package/dist/utils/localizedValue.d.ts +12 -5
- package/dist/utils/localizedValue.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/Layout/Header/Buttons/HeaderButton.tsx +13 -0
- package/src/components/Layout/Header/Buttons/HeaderIconButton.tsx +22 -0
- package/src/components/Layout/Header/Buttons/HeaderToggleButton.tsx +23 -0
- package/src/components/Layout/Header/Buttons/index.ts +3 -0
- package/src/components/Layout/Header/DrawerToggle.tsx +9 -18
- package/src/components/Layout/Header/Notification/Notification.tsx +31 -53
- package/src/components/Layout/Header/Profile/{Profile.jsx → Profile.tsx} +34 -92
- package/src/components/Layout/Header/ResponsiveSection.tsx +17 -34
- package/src/components/Layout/Header/index.ts +1 -0
- package/src/hooks/index.jsx +13 -22
- package/src/hooks/mui.ts +16 -6
- package/src/hooks/useLocalizedValue.tsx +34 -0
- package/src/hooks/usePopoverState.tsx +28 -0
- package/src/utils/localizedValue.ts +37 -2
|
@@ -1,11 +1,18 @@
|
|
|
1
|
+
type ILocale = {
|
|
2
|
+
name: string;
|
|
3
|
+
locale: string;
|
|
4
|
+
};
|
|
1
5
|
type ILocalizedValue<T> = {
|
|
2
6
|
[key: string]: T;
|
|
3
7
|
};
|
|
4
|
-
declare function localizedValueHasAllLocales<T>(value?: ILocalizedValue<T>, locales?: Array<{
|
|
5
|
-
name: string;
|
|
6
|
-
locale: string;
|
|
7
|
-
}>, options?: {
|
|
8
|
+
declare function localizedValueHasAllLocales<T>(value?: ILocalizedValue<T>, locales?: Array<ILocale>, options?: {
|
|
8
9
|
isEmpty?: (v: T) => boolean;
|
|
9
10
|
}): boolean;
|
|
10
|
-
|
|
11
|
+
type IGetLocalizedValueOptions = {
|
|
12
|
+
fallback?: boolean;
|
|
13
|
+
fallbackLocales?: Array<string>;
|
|
14
|
+
};
|
|
15
|
+
declare function getLocalizedValue<T>(value: ILocalizedValue<T>, locale: string, options?: IGetLocalizedValueOptions): T | undefined;
|
|
16
|
+
export type { ILocalizedValue, IGetLocalizedValueOptions };
|
|
17
|
+
export { localizedValueHasAllLocales, getLocalizedValue };
|
|
11
18
|
//# sourceMappingURL=localizedValue.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localizedValue.d.ts","sourceRoot":"","sources":["../../../src/utils/localizedValue.ts"],"names":[],"mappings":"AACA,KAAK,eAAe,CAAC,CAAC,IAAI;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,CAAA;CAAE,CAAC;AAE/C,iBAAS,2BAA2B,CAAC,CAAC,EACpC,KAAK,GAAE,eAAe,CAAC,CAAC,CAAM,EAC9B,OAAO,GAAE,KAAK,CAAC;IAAE,
|
|
1
|
+
{"version":3,"file":"localizedValue.d.ts","sourceRoot":"","sources":["../../../src/utils/localizedValue.ts"],"names":[],"mappings":"AACA,KAAK,OAAO,GAAG;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AACF,KAAK,eAAe,CAAC,CAAC,IAAI;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,CAAA;CAAE,CAAC;AAE/C,iBAAS,2BAA2B,CAAC,CAAC,EACpC,KAAK,GAAE,eAAe,CAAC,CAAC,CAAM,EAC9B,OAAO,GAAE,KAAK,CAAC,OAAO,CAAM,EAC5B,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,CAAA;CAAO,GAC5C,OAAO,CAIT;AAED,KAAK,yBAAyB,GAAG;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,eAAe,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACjC,CAAC;AACF,iBAAS,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,yBAAyB,GAAG,CAAC,GAAG,SAAS,CAwB3H;AAED,YAAY,EAAE,eAAe,EAAE,yBAAyB,EAAE,CAAC;AAC3D,OAAO,EAAE,2BAA2B,EAAE,iBAAiB,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Button, ButtonProps } from '@mui/material';
|
|
2
|
+
import { useSx } from '../../../../hooks';
|
|
3
|
+
import { ForwardedRef, forwardRef } from 'react';
|
|
4
|
+
|
|
5
|
+
type IHeaderButtonProps = Omit<ButtonProps, 'color' | 'variant' | 'size'>;
|
|
6
|
+
|
|
7
|
+
const HeaderButton = forwardRef(function HeaderButton(props: IHeaderButtonProps, ref: ForwardedRef<HTMLButtonElement> | null) {
|
|
8
|
+
const sx = useSx(props, { color: 'text.primary' }, { defaults: true });
|
|
9
|
+
|
|
10
|
+
return <Button ref={ref} {...props} color="secondary" variant="text" sx={sx} />;
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export { HeaderButton };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ButtonProps, IconButtonProps } from '@mui/material';
|
|
2
|
+
import { IconButton } from '../../../@extended';
|
|
3
|
+
import { useSx } from '../../../../hooks';
|
|
4
|
+
import { ForwardedRef, forwardRef } from 'react';
|
|
5
|
+
|
|
6
|
+
type IHeaderIconButtonProps = Omit<IconButtonProps & ButtonProps, 'color' | 'variant' | 'size'>;
|
|
7
|
+
|
|
8
|
+
const HeaderIconButton = forwardRef(function HeaderIconButton(props: IHeaderIconButtonProps, ref: ForwardedRef<HTMLButtonElement> | null) {
|
|
9
|
+
const sx = useSx(
|
|
10
|
+
props,
|
|
11
|
+
{
|
|
12
|
+
color: 'text.primary'
|
|
13
|
+
},
|
|
14
|
+
{ defaults: true }
|
|
15
|
+
);
|
|
16
|
+
return (
|
|
17
|
+
// @ts-ignore
|
|
18
|
+
<IconButton ref={ref} {...props} color="secondary" variant="text" sx={sx} />
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export { HeaderIconButton };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ToggleButton, ToggleButtonProps } from '@mui/material';
|
|
2
|
+
import { ForwardedRef, forwardRef } from 'react';
|
|
3
|
+
import { useSx } from '../../../../hooks';
|
|
4
|
+
|
|
5
|
+
type IHeaderToggleButtonProps = Omit<ToggleButtonProps, 'color' | 'size'>;
|
|
6
|
+
|
|
7
|
+
const HeaderToggleButton = forwardRef(function HeaderToggleButton(props: IHeaderToggleButtonProps, ref: ForwardedRef<HTMLButtonElement>) {
|
|
8
|
+
const sx = useSx(
|
|
9
|
+
props,
|
|
10
|
+
{
|
|
11
|
+
color: 'text.primary',
|
|
12
|
+
borderStyle: 'none',
|
|
13
|
+
padding: 1,
|
|
14
|
+
fontSize: '1.5rem',
|
|
15
|
+
width: 36,
|
|
16
|
+
height: 36
|
|
17
|
+
},
|
|
18
|
+
{ defaults: true }
|
|
19
|
+
);
|
|
20
|
+
return <ToggleButton ref={ref} {...props} color="secondary" sx={sx} />;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export { HeaderToggleButton };
|
|
@@ -1,27 +1,18 @@
|
|
|
1
1
|
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { useLayoutDrawerState } from '../Provider';
|
|
3
|
+
import { HeaderToggleButton } from './Buttons';
|
|
4
|
+
|
|
5
|
+
const buttonSx = {
|
|
6
|
+
ml: { xs: 0, lg: -2 }
|
|
7
|
+
};
|
|
4
8
|
|
|
5
9
|
function DrawerToggle() {
|
|
6
|
-
const {
|
|
7
|
-
{ open, handleDrawerToggle } = useLayoutDrawerState();
|
|
10
|
+
const { open, handleDrawerToggle } = useLayoutDrawerState();
|
|
8
11
|
|
|
9
12
|
return (
|
|
10
|
-
|
|
11
|
-
<IconButton
|
|
12
|
-
aria-label="open drawer"
|
|
13
|
-
onClick={handleDrawerToggle}
|
|
14
|
-
edge="start"
|
|
15
|
-
color="secondary"
|
|
16
|
-
variant="light"
|
|
17
|
-
sx={{
|
|
18
|
-
color: 'text.primary',
|
|
19
|
-
bgcolor: open ? iconColorOpen : iconColor,
|
|
20
|
-
ml: { xs: 0, lg: -2 }
|
|
21
|
-
}}
|
|
22
|
-
>
|
|
13
|
+
<HeaderToggleButton value="drawer" aria-label="open drawer" sx={buttonSx} onChange={handleDrawerToggle} selected={open}>
|
|
23
14
|
{!open ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
|
24
|
-
</
|
|
15
|
+
</HeaderToggleButton>
|
|
25
16
|
);
|
|
26
17
|
}
|
|
27
18
|
|
|
@@ -1,74 +1,62 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BellOutlined, CheckCircleOutlined } from '@ant-design/icons';
|
|
2
|
+
import { useLayoutMediaState, useLayoutNotificationsState } from '../../Provider';
|
|
3
|
+
import { HeaderToggleButton } from '../Buttons';
|
|
4
|
+
import { useGetList, useTranslate } from 'ra-core';
|
|
5
|
+
import { HeaderNotificationItem, INotification } from './NotificationItem';
|
|
6
|
+
import { useEffect, useState } from 'react';
|
|
2
7
|
import {
|
|
3
8
|
Badge,
|
|
4
|
-
Box,
|
|
5
9
|
ClickAwayListener,
|
|
6
|
-
Divider,
|
|
7
|
-
List,
|
|
8
|
-
ListItemButton,
|
|
9
|
-
ListItemText,
|
|
10
10
|
Paper,
|
|
11
11
|
Popper,
|
|
12
|
+
useTheme,
|
|
12
13
|
Tooltip,
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
List,
|
|
15
|
+
Divider,
|
|
16
|
+
ListItemButton,
|
|
17
|
+
ListItemText,
|
|
18
|
+
Typography
|
|
15
19
|
} from '@mui/material';
|
|
16
|
-
import {
|
|
20
|
+
import { usePopoverState } from '../../../../hooks';
|
|
17
21
|
import { IconButton, Transitions } from '../../../@extended';
|
|
18
|
-
import { BellOutlined, CheckCircleOutlined } from '@ant-design/icons';
|
|
19
|
-
import { useGetList, useTranslate } from 'ra-core';
|
|
20
22
|
import MainCard from '../../../MainCard';
|
|
21
|
-
import { HeaderNotificationItem, INotification } from './NotificationItem';
|
|
22
23
|
import { Link } from 'react-router-dom';
|
|
23
24
|
|
|
24
|
-
const
|
|
25
|
+
const avatarSx = {
|
|
25
26
|
width: 36,
|
|
26
27
|
height: 36,
|
|
27
28
|
fontSize: '1rem'
|
|
28
29
|
};
|
|
29
30
|
|
|
30
|
-
const
|
|
31
|
+
const actionSx = {
|
|
31
32
|
mt: '6px',
|
|
32
33
|
ml: 1,
|
|
33
34
|
top: 'auto',
|
|
34
35
|
right: 'auto',
|
|
35
36
|
alignSelf: 'flex-start',
|
|
36
|
-
|
|
37
37
|
transform: 'none'
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
function HeaderNotification() {
|
|
41
41
|
const { enable } = useLayoutNotificationsState();
|
|
42
|
-
|
|
43
42
|
return enable ? <HeaderNotificationButton /> : null;
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
function HeaderNotificationButton() {
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
translate = useTranslate(),
|
|
50
|
-
{ resource } = useLayoutNotificationsState(),
|
|
46
|
+
const translate = useTranslate(),
|
|
47
|
+
theme = useTheme(),
|
|
51
48
|
{ downMd } = useLayoutMediaState(),
|
|
52
|
-
{
|
|
53
|
-
[notificationOpen, setNotificationOpen] = useState(false),
|
|
49
|
+
{ resource } = useLayoutNotificationsState(),
|
|
54
50
|
[notifications, setNotifications] = useState<Array<INotification>>([]),
|
|
55
|
-
read = notifications.filter((item) => item.readed === null).length,
|
|
56
|
-
handleToggle = useCallback(() => setNotificationOpen(!notificationOpen), [notificationOpen]),
|
|
57
|
-
handleClose = useCallback(() => {
|
|
58
|
-
//@ts-ignore
|
|
59
|
-
if (anchorRef.current && anchorRef.current.contains(event.target)) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
setNotificationOpen(false);
|
|
64
|
-
}, [anchorRef.current, setNotificationOpen]),
|
|
65
51
|
{ data, isLoading } = useGetList<INotification>(resource, {
|
|
66
52
|
pagination: { page: 1, perPage: 25 },
|
|
67
53
|
sort: { field: 'created', order: 'DESC' },
|
|
68
54
|
filter: {
|
|
69
55
|
readed: false
|
|
70
56
|
}
|
|
71
|
-
})
|
|
57
|
+
}),
|
|
58
|
+
read = notifications.filter((item) => item.readed === null).length,
|
|
59
|
+
{ open, anchorEl, handleClose, handleToggle } = usePopoverState();
|
|
72
60
|
|
|
73
61
|
useEffect(() => {
|
|
74
62
|
if (!isLoading && data) {
|
|
@@ -77,26 +65,16 @@ function HeaderNotificationButton() {
|
|
|
77
65
|
}, [data, isLoading]);
|
|
78
66
|
|
|
79
67
|
return (
|
|
80
|
-
|
|
81
|
-
{
|
|
82
|
-
<IconButton
|
|
83
|
-
color="secondary"
|
|
84
|
-
variant="light"
|
|
85
|
-
sx={{ color: 'text.primary', bgcolor: notificationOpen ? iconColorOpen : iconColor }}
|
|
86
|
-
aria-label="open profile"
|
|
87
|
-
ref={anchorRef}
|
|
88
|
-
aria-controls={notificationOpen ? 'profile-grow' : undefined}
|
|
89
|
-
aria-haspopup="true"
|
|
90
|
-
onClick={handleToggle}
|
|
91
|
-
>
|
|
68
|
+
<>
|
|
69
|
+
<HeaderToggleButton value="notifications" selected={open} onChange={handleToggle}>
|
|
92
70
|
<Badge badgeContent={read} color="primary">
|
|
93
71
|
<BellOutlined />
|
|
94
72
|
</Badge>
|
|
95
|
-
</
|
|
73
|
+
</HeaderToggleButton>
|
|
96
74
|
<Popper
|
|
97
75
|
placement={downMd ? 'bottom' : 'bottom-end'}
|
|
98
|
-
open={
|
|
99
|
-
anchorEl={
|
|
76
|
+
open={open}
|
|
77
|
+
anchorEl={anchorEl}
|
|
100
78
|
role={undefined}
|
|
101
79
|
transition
|
|
102
80
|
disablePortal
|
|
@@ -152,8 +130,8 @@ function HeaderNotificationButton() {
|
|
|
152
130
|
'& .MuiListItemButton-root': {
|
|
153
131
|
py: 0.5,
|
|
154
132
|
'&.Mui-selected': { bgcolor: 'grey.50', color: 'text.primary' },
|
|
155
|
-
'& .MuiAvatar-root':
|
|
156
|
-
'& .MuiListItemSecondaryAction-root': { ...
|
|
133
|
+
'& .MuiAvatar-root': avatarSx,
|
|
134
|
+
'& .MuiListItemSecondaryAction-root': { ...actionSx, position: 'relative' }
|
|
157
135
|
},
|
|
158
136
|
maxHeight: 'calc(100vh - 240px)',
|
|
159
137
|
overflow: 'auto'
|
|
@@ -164,12 +142,11 @@ function HeaderNotificationButton() {
|
|
|
164
142
|
key={item.id}
|
|
165
143
|
selected={read > 0}
|
|
166
144
|
notification={item}
|
|
167
|
-
onClick={
|
|
145
|
+
onClick={handleClose}
|
|
168
146
|
divider={index < notifications.length - 1}
|
|
169
147
|
/>
|
|
170
148
|
))}
|
|
171
149
|
</List>
|
|
172
|
-
<Divider />
|
|
173
150
|
{/*@ts-ignore*/}
|
|
174
151
|
<ListItemButton
|
|
175
152
|
sx={{ textAlign: 'center', py: `${12}px !important` }}
|
|
@@ -185,13 +162,14 @@ function HeaderNotificationButton() {
|
|
|
185
162
|
}
|
|
186
163
|
/>
|
|
187
164
|
</ListItemButton>
|
|
165
|
+
<Divider />
|
|
188
166
|
</MainCard>
|
|
189
167
|
</ClickAwayListener>
|
|
190
168
|
</Paper>
|
|
191
169
|
</Transitions>
|
|
192
170
|
)}
|
|
193
171
|
</Popper>
|
|
194
|
-
|
|
172
|
+
</>
|
|
195
173
|
);
|
|
196
174
|
}
|
|
197
175
|
|
|
@@ -1,108 +1,46 @@
|
|
|
1
|
+
import { CardContent, ClickAwayListener, Grid, List, Paper, Stack, Tooltip, Typography, useTheme } from '@mui/material';
|
|
2
|
+
import { HeaderToggleButton } from '../Buttons';
|
|
1
3
|
import { Avatar, IconButton, Transitions } from '../../../@extended';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
ClickAwayListener,
|
|
7
|
-
Grid,
|
|
8
|
-
List,
|
|
9
|
-
Paper,
|
|
10
|
-
Popper,
|
|
11
|
-
Stack,
|
|
12
|
-
Tooltip,
|
|
13
|
-
Typography,
|
|
14
|
-
useTheme
|
|
15
|
-
} from '@mui/material';
|
|
16
|
-
import { ChangePasswordButton, LogoutButton, StopImpersonateButton } from './buttons';
|
|
17
|
-
import { useAuthProvider, useDataProvider, useGetIdentity, useLogout } from 'react-admin';
|
|
18
|
-
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
19
|
-
|
|
20
|
-
import { LogoutOutlined } from '@ant-design/icons';
|
|
4
|
+
import { useAuthProvider, useDataProvider, useGetIdentity, useLogout } from 'ra-core';
|
|
5
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
6
|
+
import { usePopoverState } from '../../../../hooks';
|
|
7
|
+
import { Popper } from '@mui/base';
|
|
21
8
|
import MainCard from '../../../MainCard';
|
|
22
|
-
import
|
|
23
|
-
|
|
24
|
-
function TabPanel({ children, value, index, ...other }) {
|
|
25
|
-
return (
|
|
26
|
-
<div role="tabpanel" hidden={value !== index} id={`profile-tabpanel-${index}`} aria-labelledby={`profile-tab-${index}`} {...other}>
|
|
27
|
-
{value === index && children}
|
|
28
|
-
</div>
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
TabPanel.propTypes = {
|
|
33
|
-
children: PropTypes.node,
|
|
34
|
-
index: PropTypes.any.isRequired,
|
|
35
|
-
value: PropTypes.any.isRequired
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const Profile = () => {
|
|
39
|
-
const theme = useTheme();
|
|
40
|
-
|
|
41
|
-
const { identity } = useGetIdentity();
|
|
42
|
-
const hasProfilePic = identity !== undefined && identity.image && identity.image !== null && identity.image !== '';
|
|
43
|
-
const anchorRef = useRef(null);
|
|
44
|
-
const [open, setOpen] = useState(false);
|
|
45
|
-
const handleToggle = () => {
|
|
46
|
-
setOpen((prevOpen) => !prevOpen);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const handleClose = (event) => {
|
|
50
|
-
if (anchorRef.current && anchorRef.current.contains(event.target)) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
setOpen(false);
|
|
54
|
-
};
|
|
9
|
+
import { LogoutOutlined } from '@ant-design/icons';
|
|
10
|
+
import { ChangePasswordButton, LogoutButton, StopImpersonateButton } from './buttons';
|
|
55
11
|
|
|
56
|
-
|
|
57
|
-
const
|
|
12
|
+
function Profile() {
|
|
13
|
+
const theme = useTheme(),
|
|
14
|
+
dataProvider = useDataProvider(),
|
|
15
|
+
authProvider = useAuthProvider(),
|
|
16
|
+
{ identity } = useGetIdentity(),
|
|
17
|
+
hasProfilePic = identity !== undefined && identity.image && identity.image !== null && identity.image !== '',
|
|
18
|
+
[profileImage, setProfileImage] = useState<string | null>(null),
|
|
19
|
+
{ open, anchorEl, handleToggle, handleClose } = usePopoverState(),
|
|
20
|
+
logout = useLogout(),
|
|
21
|
+
handleLogout = useCallback(() => {
|
|
22
|
+
logout();
|
|
23
|
+
}, [authProvider, logout]);
|
|
58
24
|
|
|
59
|
-
const dataProvider = useDataProvider();
|
|
60
|
-
const authProvider = useAuthProvider();
|
|
61
|
-
const handleLogout = useCallback(() => {
|
|
62
|
-
logout();
|
|
63
|
-
}, [authProvider, logout]);
|
|
64
|
-
const [profileImage, setProfileImage] = useState(null);
|
|
65
25
|
useEffect(() => {
|
|
66
|
-
if (
|
|
67
|
-
|
|
26
|
+
if (hasProfilePic) {
|
|
27
|
+
dataProvider.getFile('/profile/me/image').then((image: string) => setProfileImage(image));
|
|
68
28
|
}
|
|
69
|
-
async function fetchProfileImage() {
|
|
70
|
-
var image = await dataProvider.getFile('/profile/me/image');
|
|
71
|
-
setProfileImage(image);
|
|
72
|
-
}
|
|
73
|
-
fetchProfileImage();
|
|
74
29
|
}, [dataProvider, hasProfilePic]);
|
|
75
30
|
|
|
76
31
|
return (
|
|
77
|
-
|
|
78
|
-
<
|
|
79
|
-
sx={{
|
|
80
|
-
p: 0.25,
|
|
81
|
-
bgcolor: open ? iconBackColorOpen : 'transparent',
|
|
82
|
-
borderRadius: 1,
|
|
83
|
-
'&:hover': {
|
|
84
|
-
bgcolor: theme.palette.mode === 'dark' ? 'secondary.light' : 'secondary.lighter'
|
|
85
|
-
},
|
|
86
|
-
'&:focus-visible': {
|
|
87
|
-
outline: `2px solid ${theme.palette.secondary.dark}`,
|
|
88
|
-
outlineOffset: 2
|
|
89
|
-
}
|
|
90
|
-
}}
|
|
91
|
-
aria-label="open profile"
|
|
92
|
-
ref={anchorRef}
|
|
93
|
-
aria-controls={open ? 'profile-grow' : undefined}
|
|
94
|
-
aria-haspopup="true"
|
|
95
|
-
onClick={handleToggle}
|
|
96
|
-
>
|
|
32
|
+
<>
|
|
33
|
+
<HeaderToggleButton sx={{ p: 0.25, width: 'auto' }} onClick={handleToggle} value="profile" selected={open}>
|
|
97
34
|
<Stack direction="row" spacing={2} alignItems="center" sx={{ p: 0.5 }}>
|
|
35
|
+
{/* @ts-ignore */}
|
|
98
36
|
<Avatar alt={identity?.name?.toUpperCase()} src={profileImage} size="xs" />
|
|
99
37
|
<Typography variant="subtitle1">{identity?.name}</Typography>
|
|
100
38
|
</Stack>
|
|
101
|
-
</
|
|
39
|
+
</HeaderToggleButton>
|
|
102
40
|
<Popper
|
|
103
41
|
placement="bottom-end"
|
|
104
42
|
open={open}
|
|
105
|
-
anchorEl={
|
|
43
|
+
anchorEl={anchorEl}
|
|
106
44
|
role={undefined}
|
|
107
45
|
transition
|
|
108
46
|
disablePortal
|
|
@@ -118,9 +56,11 @@ const Profile = () => {
|
|
|
118
56
|
}}
|
|
119
57
|
>
|
|
120
58
|
{({ TransitionProps }) => (
|
|
59
|
+
/* @ts-ignore */
|
|
121
60
|
<Transitions type="grow" position="top-right" in={open} {...TransitionProps}>
|
|
122
61
|
<Paper
|
|
123
62
|
sx={{
|
|
63
|
+
// @ts-ignore
|
|
124
64
|
boxShadow: theme.customShadows.z1,
|
|
125
65
|
width: 290,
|
|
126
66
|
minWidth: 240,
|
|
@@ -136,17 +76,19 @@ const Profile = () => {
|
|
|
136
76
|
<Grid container justifyContent="space-between" alignItems="center">
|
|
137
77
|
<Grid item>
|
|
138
78
|
<Stack direction="row" spacing={1.25} alignItems="center">
|
|
79
|
+
{/* @ts-ignore */}
|
|
139
80
|
<Avatar alt="profile user" src={profileImage} sx={{ width: 32, height: 32 }} />
|
|
140
81
|
<Stack>
|
|
141
82
|
<Typography variant="h6">{identity?.name}</Typography>
|
|
142
83
|
<Typography variant="body2" color="textSecondary">
|
|
143
|
-
{identity?.roles?.map((r) => r.name.replace('ROLE_', '')).join(',')}
|
|
84
|
+
{identity?.roles?.map((r: { name: string }) => r.name.replace('ROLE_', '')).join(',')}
|
|
144
85
|
</Typography>
|
|
145
86
|
</Stack>
|
|
146
87
|
</Stack>
|
|
147
88
|
</Grid>
|
|
148
89
|
<Grid item>
|
|
149
90
|
<Tooltip title="Logout">
|
|
91
|
+
{/* @ts-ignore */}
|
|
150
92
|
<IconButton size="large" sx={{ color: 'text.primary' }} onClick={handleLogout}>
|
|
151
93
|
<LogoutOutlined />
|
|
152
94
|
</IconButton>
|
|
@@ -169,8 +111,8 @@ const Profile = () => {
|
|
|
169
111
|
</Transitions>
|
|
170
112
|
)}
|
|
171
113
|
</Popper>
|
|
172
|
-
|
|
114
|
+
</>
|
|
173
115
|
);
|
|
174
|
-
}
|
|
116
|
+
}
|
|
175
117
|
|
|
176
118
|
export default Profile;
|
|
@@ -1,53 +1,36 @@
|
|
|
1
|
-
import { AppBar,
|
|
2
|
-
import {
|
|
3
|
-
import { useRef, useState, useCallback } from 'react';
|
|
1
|
+
import { AppBar, ClickAwayListener, Paper, Popper, Toolbar } from '@mui/material';
|
|
2
|
+
import { Transitions } from '../../@extended';
|
|
4
3
|
import { MoreOutlined } from '@ant-design/icons';
|
|
5
4
|
import { useTheme } from '@mui/material/styles';
|
|
6
|
-
import { useLayoutMediaState
|
|
5
|
+
import { useLayoutMediaState } from '../Provider';
|
|
6
|
+
import { HeaderToggleButton } from './Buttons';
|
|
7
|
+
import { usePopoverState } from '../../../hooks';
|
|
7
8
|
|
|
8
9
|
type IResponsiveSectionProps = React.PropsWithChildren;
|
|
9
10
|
|
|
10
11
|
const ResponsiveSection = (props: IResponsiveSectionProps) => {
|
|
11
12
|
const theme = useTheme(),
|
|
12
13
|
{ downMd } = useLayoutMediaState(),
|
|
13
|
-
{
|
|
14
|
-
[open, setOpen] = useState(false),
|
|
15
|
-
anchorRef = useRef(null),
|
|
16
|
-
handleToggle = useCallback(() => {
|
|
17
|
-
setOpen(!open);
|
|
18
|
-
}, [open]),
|
|
19
|
-
//@ts-ignore
|
|
20
|
-
handleClose = useCallback((event) => {
|
|
21
|
-
//@ts-ignore
|
|
22
|
-
if (anchorRef.current && anchorRef.current.contains(event.target)) {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
setOpen(false);
|
|
26
|
-
}, []);
|
|
14
|
+
{ open, anchorEl, handleToggle, handleClose } = usePopoverState();
|
|
27
15
|
|
|
28
16
|
return !downMd ? (
|
|
29
17
|
<>{props.children}</>
|
|
30
18
|
) : (
|
|
31
19
|
<>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
variant="light"
|
|
43
|
-
>
|
|
44
|
-
<MoreOutlined />
|
|
45
|
-
</IconButton>
|
|
46
|
-
</Box>
|
|
20
|
+
{/*@ts-ignore*/}
|
|
21
|
+
<HeaderToggleButton
|
|
22
|
+
aria-label="open more menu"
|
|
23
|
+
aria-controls={open ? 'menu-list-grow' : undefined}
|
|
24
|
+
aria-haspopup="true"
|
|
25
|
+
onClick={handleToggle}
|
|
26
|
+
selected={open}
|
|
27
|
+
>
|
|
28
|
+
<MoreOutlined />
|
|
29
|
+
</HeaderToggleButton>
|
|
47
30
|
<Popper
|
|
48
31
|
placement="bottom-end"
|
|
49
32
|
open={open}
|
|
50
|
-
anchorEl={
|
|
33
|
+
anchorEl={anchorEl}
|
|
51
34
|
role={undefined}
|
|
52
35
|
transition
|
|
53
36
|
disablePortal
|
package/src/hooks/index.jsx
CHANGED
|
@@ -1,23 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import useMenuConfig from './useMenuConfig';
|
|
6
|
-
import useResourceTitle from './useResourceTitle';
|
|
7
|
-
import useThemeConfig from './useThemeConfig';
|
|
8
|
-
import useTableFormIterator from './useTableFormIterator';
|
|
9
|
-
import useTableFormIteratorItem from './useTableFormIteratorItem';
|
|
10
|
-
export {
|
|
11
|
-
useAppConfig,
|
|
12
|
-
useMenu,
|
|
13
|
-
useBreadcrumbs,
|
|
14
|
-
useLocalStorage,
|
|
15
|
-
useThemeConfig,
|
|
16
|
-
useResourceTitle,
|
|
17
|
-
useMenuConfig,
|
|
18
|
-
useTableFormIterator,
|
|
19
|
-
useTableFormIteratorItem
|
|
20
|
-
};
|
|
21
|
-
export { useSx } from './mui';
|
|
22
|
-
export { useRefDimensions } from './useRefDimensions';
|
|
1
|
+
export { default as useAppConfig } from './useAppConfig';
|
|
2
|
+
export { default as useBreadcrumbs } from './useBreadcrumbs';
|
|
3
|
+
export { useLocalizedValue, useGetLocalizedValue } from './useLocalizedValue';
|
|
4
|
+
export { default as useLocalStorage } from './useLocalStorage';
|
|
23
5
|
export { useMemoizedObject } from './useMemoizedObject';
|
|
6
|
+
export { default as useMenu } from './useMenu';
|
|
7
|
+
export { default as useMenuConfig } from './useMenuConfig';
|
|
8
|
+
export { usePopoverState } from './usePopoverState';
|
|
9
|
+
export { useRefDimensions } from './useRefDimensions';
|
|
10
|
+
export { default as useResourceTitle } from './useResourceTitle';
|
|
11
|
+
export { useSx } from './mui';
|
|
12
|
+
export { default as useTableFormIterator } from './useTableFormIterator';
|
|
13
|
+
export { default as useTableFormIteratorItem } from './useTableFormIteratorItem';
|
|
14
|
+
export { default as useThemeConfig } from './useThemeConfig';
|
package/src/hooks/mui.ts
CHANGED
|
@@ -2,12 +2,22 @@ import _ from 'lodash';
|
|
|
2
2
|
import { SxProps } from '@mui/material';
|
|
3
3
|
import { ComponentProps, useState, useEffect } from 'react';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
type IUseSxOptions = {
|
|
6
|
+
defaults?: boolean;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
function useSx(props: ComponentProps<{ sx?: SxProps } & any>, sx: SxProps, options?: IUseSxOptions): SxProps {
|
|
10
|
+
const _sx = _.chain(props?.sx ?? {})
|
|
11
|
+
.cloneDeepWith((v) => (_.isFunction(v) ? v : undefined))
|
|
12
|
+
.value();
|
|
13
|
+
|
|
14
|
+
if (options?.defaults) {
|
|
15
|
+
_.defaultsDeep(_sx, sx);
|
|
16
|
+
} else {
|
|
17
|
+
_.merge(_sx, sx);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const [value, setValue] = useState(_sx);
|
|
11
21
|
|
|
12
22
|
useEffect(() => {
|
|
13
23
|
if (!_.isEqual(value, _sx)) {
|