@asaleh37/ui-base 1.2.19 → 1.2.21
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/index.d.ts +2 -1
- package/dist/index.js +11 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +11 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/administration/dev/NotificationGrid.tsx +432 -0
- package/src/components/administration/dev/NotificationQueueGrid.tsx +222 -0
- package/src/components/common/Home.tsx +22 -24
- package/src/components/common/MyNotificationsPanel.tsx +104 -0
- package/src/components/common/NotificationItem.tsx +138 -0
- package/src/components/templates/workflow/WorkflowRouteComponent.tsx +14 -0
- package/src/hooks/useAxios.tsx +4 -1
- package/src/hooks/useInterval.tsx +23 -0
- package/src/layout/Layout.tsx +7 -1
- package/src/layout/NotificationButton.tsx +207 -0
- package/src/layout/TopBar.tsx +2 -0
- package/src/locales/arabic/devLocalsAr.json +28 -3
- package/src/locales/english/devLocalsEn.json +26 -1
- package/src/redux/features/administration/AdministrationStoresMetaData.ts +13 -0
- package/src/routes/administration/devRoutes.tsx +15 -0
- package/src/util/AppUtils.ts +30 -0
- package/src/assets/logo.png +0 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
2
|
+
import { Box, Button } from "@mui/material";
|
|
3
|
+
import { useSelector } from "react-redux";
|
|
4
|
+
import { useAxios, useSession } from "../../hooks";
|
|
5
|
+
import { useState } from "react";
|
|
6
|
+
import useInterval from "../../hooks/useInterval";
|
|
7
|
+
import { notificationsCheckIntervalSeconds } from "../../layout/NotificationButton";
|
|
8
|
+
import NotificationItem from "./NotificationItem";
|
|
9
|
+
|
|
10
|
+
const MyNotificationsPanel: React.FC = () => {
|
|
11
|
+
const { UserInfo } = useSession();
|
|
12
|
+
const AppLayout = useSelector((state: any) => state.AppLayout);
|
|
13
|
+
const { handleGetRequest, handlePostRequest } = useAxios();
|
|
14
|
+
const [notifications, setNotifications] = useState<Array<Notification>>([]);
|
|
15
|
+
|
|
16
|
+
const acknowledgeAllCurrentNotifications = async () => {
|
|
17
|
+
await handlePostRequest({
|
|
18
|
+
endPointURI: "api/v1/public/notification/all/notified",
|
|
19
|
+
showMask: true,
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const archiveAllCurrentNotifications = async () => {
|
|
24
|
+
await handlePostRequest({
|
|
25
|
+
endPointURI: "api/v1/public/notification/all/archive",
|
|
26
|
+
showMask: true,
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const loadUserNotifications = () => {
|
|
31
|
+
if (UserInfo.isAuthenticated === true) {
|
|
32
|
+
handleGetRequest({
|
|
33
|
+
endPointURI: "api/v1/public/notifications",
|
|
34
|
+
showMask: false,
|
|
35
|
+
successCallBkFn: (response: any) => {
|
|
36
|
+
setNotifications(response.data);
|
|
37
|
+
console.log(response.data);
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
useInterval(loadUserNotifications, notificationsCheckIntervalSeconds * 1000);
|
|
43
|
+
return (
|
|
44
|
+
<>
|
|
45
|
+
<Box
|
|
46
|
+
sx={{ display: "flex", alignItems: "center", justifyContent: "center" }}
|
|
47
|
+
>
|
|
48
|
+
<Box sx={{ flex: 1 }}></Box>
|
|
49
|
+
<h3>
|
|
50
|
+
<FontAwesomeIcon
|
|
51
|
+
icon="bell"
|
|
52
|
+
style={{ marginRight: 10, marginLeft: 10 }}
|
|
53
|
+
/>
|
|
54
|
+
{AppLayout.appDirection === "ltr" ? "Notifications" : "الاشعارات"}
|
|
55
|
+
</h3>
|
|
56
|
+
<Box sx={{ flex: 1 }}></Box>
|
|
57
|
+
<Button
|
|
58
|
+
sx={{ textTransform: "none" }}
|
|
59
|
+
onClick={acknowledgeAllCurrentNotifications}
|
|
60
|
+
>
|
|
61
|
+
Mark All Read
|
|
62
|
+
</Button>
|
|
63
|
+
<Button
|
|
64
|
+
color="error"
|
|
65
|
+
sx={{ textTransform: "none" }}
|
|
66
|
+
onClick={archiveAllCurrentNotifications}
|
|
67
|
+
>
|
|
68
|
+
Delete All
|
|
69
|
+
</Button>
|
|
70
|
+
</Box>
|
|
71
|
+
<Box
|
|
72
|
+
sx={{
|
|
73
|
+
flex: 1,
|
|
74
|
+
width: "100%",
|
|
75
|
+
display: "flex",
|
|
76
|
+
overflowY: "auto",
|
|
77
|
+
}}
|
|
78
|
+
>
|
|
79
|
+
<Box
|
|
80
|
+
sx={{
|
|
81
|
+
flexGrow: 1,
|
|
82
|
+
width: "fit-content",
|
|
83
|
+
display: "flex",
|
|
84
|
+
flexDirection: "column",
|
|
85
|
+
overflowY: "auto",
|
|
86
|
+
alignItems: "center",
|
|
87
|
+
justifyContent: "flex-start",
|
|
88
|
+
}}
|
|
89
|
+
>
|
|
90
|
+
{notifications.map((notification: Notification, index) => {
|
|
91
|
+
return (
|
|
92
|
+
<NotificationItem
|
|
93
|
+
{...notification}
|
|
94
|
+
showDivider={index < notifications.length - 1}
|
|
95
|
+
/>
|
|
96
|
+
);
|
|
97
|
+
})}
|
|
98
|
+
</Box>
|
|
99
|
+
</Box>
|
|
100
|
+
</>
|
|
101
|
+
);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export default MyNotificationsPanel;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
|
2
|
+
import { Box, Button, Divider, keyframes, Typography } from "@mui/material";
|
|
3
|
+
import { useSelector } from "react-redux";
|
|
4
|
+
import { useNavigate } from "react-router-dom";
|
|
5
|
+
import { useAxios, useSession } from "../../hooks";
|
|
6
|
+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
7
|
+
import { timeAgo } from "../../util";
|
|
8
|
+
|
|
9
|
+
const beatFade = keyframes`
|
|
10
|
+
0% { transform: scale(1); opacity: 1; }
|
|
11
|
+
25% { transform: scale(1); opacity: 0.6; }
|
|
12
|
+
50% { transform: scale(1); opacity: 1; }
|
|
13
|
+
75% { transform: scale(1); opacity: 0.6; }
|
|
14
|
+
100% { transform: scale(1); opacity: 1; }
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
export type Notification = {
|
|
18
|
+
id: number;
|
|
19
|
+
notificationIcon?: IconProp;
|
|
20
|
+
notificationIconColor?: string;
|
|
21
|
+
notificationEnText: string;
|
|
22
|
+
notificationArText: string;
|
|
23
|
+
notificationTextColor?: string;
|
|
24
|
+
notificationAction?: "NAVIGATION";
|
|
25
|
+
notificationActionPayload?: string;
|
|
26
|
+
isNotified?: boolean;
|
|
27
|
+
createTime: string;
|
|
28
|
+
setOpen?: (state: boolean) => void;
|
|
29
|
+
showDivider?: boolean;
|
|
30
|
+
};
|
|
31
|
+
const NotificationItem: React.FC<Notification> = (notification) => {
|
|
32
|
+
const navigate = useNavigate();
|
|
33
|
+
const AppLayout = useSelector((state: any) => state.AppLayout);
|
|
34
|
+
const { handlePostRequest } = useAxios();
|
|
35
|
+
const acknowledgeNotification = (notificationId) => {
|
|
36
|
+
handlePostRequest({
|
|
37
|
+
endPointURI: "api/v1/public/notification/notified",
|
|
38
|
+
showMask: false,
|
|
39
|
+
parameters: { notificationQueueId: notificationId },
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
return (
|
|
43
|
+
<>
|
|
44
|
+
<Button
|
|
45
|
+
fullWidth
|
|
46
|
+
onClick={() => {
|
|
47
|
+
if (
|
|
48
|
+
notification?.notificationAction === "NAVIGATION" &&
|
|
49
|
+
notification?.notificationActionPayload
|
|
50
|
+
) {
|
|
51
|
+
navigate(notification.notificationActionPayload);
|
|
52
|
+
}
|
|
53
|
+
if (!notification?.isNotified) {
|
|
54
|
+
acknowledgeNotification(notification.id);
|
|
55
|
+
}
|
|
56
|
+
if (notification?.setOpen) {
|
|
57
|
+
notification.setOpen(false);
|
|
58
|
+
}
|
|
59
|
+
}}
|
|
60
|
+
sx={{
|
|
61
|
+
marginBottom: 1,
|
|
62
|
+
justifyContent: "flex-start",
|
|
63
|
+
alignItems: "flex-start",
|
|
64
|
+
color: notification?.notificationTextColor,
|
|
65
|
+
display: "flex",
|
|
66
|
+
flexDirection: "column",
|
|
67
|
+
cursor: "pointer",
|
|
68
|
+
}}
|
|
69
|
+
>
|
|
70
|
+
<Box
|
|
71
|
+
sx={{
|
|
72
|
+
cursor: "pointer",
|
|
73
|
+
display: "flex",
|
|
74
|
+
alignItems: "center",
|
|
75
|
+
justifyContent: "flex-start",
|
|
76
|
+
width: "100%",
|
|
77
|
+
}}
|
|
78
|
+
>
|
|
79
|
+
{notification?.notificationIcon ? (
|
|
80
|
+
<FontAwesomeIcon
|
|
81
|
+
icon={notification.notificationIcon}
|
|
82
|
+
style={{
|
|
83
|
+
marginRight: 10,
|
|
84
|
+
marginLeft: 10,
|
|
85
|
+
color: notification?.notificationIconColor,
|
|
86
|
+
}}
|
|
87
|
+
/>
|
|
88
|
+
) : (
|
|
89
|
+
<></>
|
|
90
|
+
)}
|
|
91
|
+
{AppLayout.appDirection === "ltr"
|
|
92
|
+
? notification.notificationEnText
|
|
93
|
+
: notification.notificationArText}
|
|
94
|
+
<Box sx={{ flex: 1 }}></Box>
|
|
95
|
+
{!notification?.isNotified ? (
|
|
96
|
+
<Typography
|
|
97
|
+
color="warning"
|
|
98
|
+
sx={{
|
|
99
|
+
animation: `${beatFade} 2s infinite ease-in-out`,
|
|
100
|
+
textTransform: "none",
|
|
101
|
+
display: "inline-block",
|
|
102
|
+
}}
|
|
103
|
+
>
|
|
104
|
+
<FontAwesomeIcon
|
|
105
|
+
icon="bell"
|
|
106
|
+
style={{ marginRight: 5, marginLeft: 5 }}
|
|
107
|
+
/>
|
|
108
|
+
{AppLayout.appDirection === "ltr" ? "New" : "جديد"}
|
|
109
|
+
</Typography>
|
|
110
|
+
) : (
|
|
111
|
+
<></>
|
|
112
|
+
)}
|
|
113
|
+
</Box>
|
|
114
|
+
<Box
|
|
115
|
+
sx={{
|
|
116
|
+
width: "100%",
|
|
117
|
+
fontSize: 12,
|
|
118
|
+
display: "flex",
|
|
119
|
+
justifyContent: "flex-end",
|
|
120
|
+
alignItems: "center",
|
|
121
|
+
cursor: "pointer",
|
|
122
|
+
}}
|
|
123
|
+
>
|
|
124
|
+
<Box sx={{ marginRight: 1, marginLeft: 1 }}>
|
|
125
|
+
{timeAgo(notification.createTime, AppLayout.appDirection)}
|
|
126
|
+
</Box>
|
|
127
|
+
<Box sx={{ flex: 1 }}></Box>
|
|
128
|
+
<Box sx={{ marginRight: 1, marginLeft: 1 }}>
|
|
129
|
+
{notification.createTime}
|
|
130
|
+
</Box>
|
|
131
|
+
</Box>
|
|
132
|
+
</Button>
|
|
133
|
+
{notification?.showDivider ? <Divider flexItem /> : <></>}
|
|
134
|
+
</>
|
|
135
|
+
);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export default NotificationItem;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useParams } from "react-router-dom";
|
|
2
|
+
import WorkflowDocumentPanel from "./WorkflowDocumentPanel";
|
|
3
|
+
|
|
4
|
+
const WorkflowRouteComponent: React.FC = () => {
|
|
5
|
+
const { workflowDocumentCode, refDocumentId } = useParams();
|
|
6
|
+
return (
|
|
7
|
+
<WorkflowDocumentPanel
|
|
8
|
+
workFlowDocumentCode={workflowDocumentCode}
|
|
9
|
+
refDocumentId={refDocumentId}
|
|
10
|
+
/>
|
|
11
|
+
);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default WorkflowRouteComponent;
|
package/src/hooks/useAxios.tsx
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { toast } from "react-toastify";
|
|
2
2
|
import axios, { ResponseType } from "axios";
|
|
3
|
-
import { useSelector } from "react-redux";
|
|
3
|
+
import { useDispatch, useSelector } from "react-redux";
|
|
4
4
|
import useLoadingMask from "./useLoadingMask";
|
|
5
|
+
import { UserSessionActions } from "../redux/features/common/UserSessionSlice";
|
|
5
6
|
|
|
6
7
|
export interface APIRequest {
|
|
7
8
|
endPointURI: string;
|
|
@@ -27,6 +28,7 @@ export interface UploadAttachmentRequest {
|
|
|
27
28
|
|
|
28
29
|
const useAxios = () => {
|
|
29
30
|
const mask = useLoadingMask();
|
|
31
|
+
const dispatch = useDispatch();
|
|
30
32
|
const apiBaseUrl = useSelector(
|
|
31
33
|
(state: any) => state.AppInfo.value.apiBaseUrl
|
|
32
34
|
);
|
|
@@ -51,6 +53,7 @@ const useAxios = () => {
|
|
|
51
53
|
: false
|
|
52
54
|
: false
|
|
53
55
|
) {
|
|
56
|
+
dispatch(UserSessionActions.setUnAuthenticated());
|
|
54
57
|
toast.error("your session is now expired, you need to login again", {
|
|
55
58
|
autoClose: false,
|
|
56
59
|
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
|
|
3
|
+
export default function useInterval(
|
|
4
|
+
callback: () => void,
|
|
5
|
+
delay: number | null
|
|
6
|
+
) {
|
|
7
|
+
const savedCallback = useRef<() => void>(callback);
|
|
8
|
+
|
|
9
|
+
// Remember the latest callback
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
savedCallback.current = callback;
|
|
12
|
+
}, [callback]);
|
|
13
|
+
|
|
14
|
+
// Set up the interval
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (delay === null) return;
|
|
17
|
+
|
|
18
|
+
const tick = () => savedCallback.current?.();
|
|
19
|
+
|
|
20
|
+
const id = setInterval(tick, delay);
|
|
21
|
+
return () => clearInterval(id);
|
|
22
|
+
}, [delay]);
|
|
23
|
+
}
|
package/src/layout/Layout.tsx
CHANGED
|
@@ -63,9 +63,15 @@ export default function Layout() {
|
|
|
63
63
|
const UserSession: UserSessionProps = useSelector(
|
|
64
64
|
(state: any) => state.UserSession
|
|
65
65
|
);
|
|
66
|
+
const AppLayout = useSelector((state: any) => state.AppLayout);
|
|
67
|
+
|
|
66
68
|
return (
|
|
67
69
|
<BrowserRouter>
|
|
68
|
-
<ToastContainer
|
|
70
|
+
<ToastContainer
|
|
71
|
+
rtl={AppLayout.appDirection === "rtl"}
|
|
72
|
+
draggable={true}
|
|
73
|
+
position="bottom-center"
|
|
74
|
+
/>
|
|
69
75
|
<LoadingMask />
|
|
70
76
|
{UserSession.value.isAuthenticated === true ? (
|
|
71
77
|
<Main open={SideBarState.isOpened}>
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
|
2
|
+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
3
|
+
import {
|
|
4
|
+
Badge,
|
|
5
|
+
Box,
|
|
6
|
+
Button,
|
|
7
|
+
ClickAwayListener,
|
|
8
|
+
Divider,
|
|
9
|
+
Fade,
|
|
10
|
+
IconButton,
|
|
11
|
+
keyframes,
|
|
12
|
+
Paper,
|
|
13
|
+
Popper,
|
|
14
|
+
PopperPlacementType,
|
|
15
|
+
Typography,
|
|
16
|
+
} from "@mui/material";
|
|
17
|
+
import { useEffect, useState } from "react";
|
|
18
|
+
import { useTranslation } from "react-i18next";
|
|
19
|
+
import { useSelector } from "react-redux";
|
|
20
|
+
import { useNavigate } from "react-router-dom";
|
|
21
|
+
import { timeAgo } from "../util";
|
|
22
|
+
import useInterval from "../hooks/useInterval";
|
|
23
|
+
import { useAxios, useSession } from "../hooks";
|
|
24
|
+
import { toast } from "react-toastify";
|
|
25
|
+
import NotificationItem, {
|
|
26
|
+
Notification,
|
|
27
|
+
} from "../components/common/NotificationItem";
|
|
28
|
+
|
|
29
|
+
export const notificationsCheckIntervalSeconds = 1;
|
|
30
|
+
|
|
31
|
+
const NotificationButton: React.FC = () => {
|
|
32
|
+
const { UserInfo } = useSession();
|
|
33
|
+
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
|
|
34
|
+
const { handleGetRequest, handlePostRequest } = useAxios();
|
|
35
|
+
const navigate = useNavigate();
|
|
36
|
+
const AppLayout = useSelector((state: any) => state.AppLayout);
|
|
37
|
+
const [open, setOpen] = useState(false);
|
|
38
|
+
const [notifications, setNotifications] = useState<Array<Notification>>([]);
|
|
39
|
+
const loadUseNotifications = () => {
|
|
40
|
+
if (UserInfo.isAuthenticated === true) {
|
|
41
|
+
handleGetRequest({
|
|
42
|
+
endPointURI: "api/v1/public/notifications/new",
|
|
43
|
+
showMask: false,
|
|
44
|
+
successCallBkFn: (response: any) => {
|
|
45
|
+
if (response.data.length > notifications.length) {
|
|
46
|
+
toast.info(
|
|
47
|
+
AppLayout.appDirection === "ltr"
|
|
48
|
+
? `You have new notifications`
|
|
49
|
+
: `لديك اشعارات جديدة`,
|
|
50
|
+
{
|
|
51
|
+
autoClose: false,
|
|
52
|
+
position:
|
|
53
|
+
AppLayout.appDirection === "ltr"
|
|
54
|
+
? "bottom-right"
|
|
55
|
+
: "bottom-left",
|
|
56
|
+
closeOnClick: true,
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
setNotifications(response.data);
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
useInterval(() => {
|
|
67
|
+
loadUseNotifications();
|
|
68
|
+
}, notificationsCheckIntervalSeconds * 1000);
|
|
69
|
+
|
|
70
|
+
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
71
|
+
setAnchorEl(event.currentTarget);
|
|
72
|
+
setOpen(!open);
|
|
73
|
+
};
|
|
74
|
+
return (
|
|
75
|
+
<ClickAwayListener
|
|
76
|
+
onClickAway={() => {
|
|
77
|
+
setOpen(false);
|
|
78
|
+
}}
|
|
79
|
+
>
|
|
80
|
+
<div style={{ cursor: "pointer" }}>
|
|
81
|
+
<IconButton
|
|
82
|
+
color="inherit"
|
|
83
|
+
onClick={handleClick}
|
|
84
|
+
sx={{ marginRight: 1, marginLeft: 1 }}
|
|
85
|
+
>
|
|
86
|
+
<Badge
|
|
87
|
+
anchorOrigin={{
|
|
88
|
+
vertical: "top",
|
|
89
|
+
horizontal: AppLayout.appDirection === "ltr" ? "right" : "left",
|
|
90
|
+
}}
|
|
91
|
+
badgeContent={
|
|
92
|
+
notifications.filter(
|
|
93
|
+
(notificaiton) =>
|
|
94
|
+
notificaiton.isNotified === undefined ||
|
|
95
|
+
notificaiton.isNotified === null
|
|
96
|
+
).length
|
|
97
|
+
}
|
|
98
|
+
color="error"
|
|
99
|
+
>
|
|
100
|
+
<FontAwesomeIcon icon="bell" />
|
|
101
|
+
</Badge>
|
|
102
|
+
</IconButton>
|
|
103
|
+
|
|
104
|
+
<Popper
|
|
105
|
+
// Note: The following zIndex style is specifically for documentation purposes and may not be necessary in your application.
|
|
106
|
+
sx={{
|
|
107
|
+
zIndex: 1200,
|
|
108
|
+
width: "fit-content",
|
|
109
|
+
minWidth: 350,
|
|
110
|
+
display: "flex",
|
|
111
|
+
flexDirection: "column",
|
|
112
|
+
}}
|
|
113
|
+
open={open}
|
|
114
|
+
anchorEl={anchorEl}
|
|
115
|
+
dir={AppLayout.appDirection}
|
|
116
|
+
placement={"bottom"}
|
|
117
|
+
transition
|
|
118
|
+
>
|
|
119
|
+
{({ TransitionProps }) => (
|
|
120
|
+
<Fade {...TransitionProps} timeout={350}>
|
|
121
|
+
<Paper
|
|
122
|
+
sx={{
|
|
123
|
+
padding: 1,
|
|
124
|
+
m: 1,
|
|
125
|
+
maxHeight: 500,
|
|
126
|
+
overflow: "hidden",
|
|
127
|
+
display: "flex",
|
|
128
|
+
flexDirection: "column",
|
|
129
|
+
}}
|
|
130
|
+
>
|
|
131
|
+
<Divider variant="fullWidth" flexItem>
|
|
132
|
+
<Box
|
|
133
|
+
sx={{
|
|
134
|
+
display: "flex",
|
|
135
|
+
alignItems: "center",
|
|
136
|
+
justifyContent: "center",
|
|
137
|
+
marginTop: 2,
|
|
138
|
+
marginBottom: 1,
|
|
139
|
+
}}
|
|
140
|
+
>
|
|
141
|
+
<FontAwesomeIcon
|
|
142
|
+
icon="bell"
|
|
143
|
+
style={{ marginRight: 10, marginLeft: 10 }}
|
|
144
|
+
/>
|
|
145
|
+
{AppLayout.appDirection === "ltr"
|
|
146
|
+
? "Notifications"
|
|
147
|
+
: "الاشعارات"}
|
|
148
|
+
</Box>
|
|
149
|
+
</Divider>
|
|
150
|
+
{notifications.length == 0 ? (
|
|
151
|
+
<Box
|
|
152
|
+
sx={{
|
|
153
|
+
display: "flex",
|
|
154
|
+
alignItems: "center",
|
|
155
|
+
flex: 1,
|
|
156
|
+
justifyContent: "center",
|
|
157
|
+
}}
|
|
158
|
+
>
|
|
159
|
+
<Box>
|
|
160
|
+
{AppLayout.appDirection === "ltr"
|
|
161
|
+
? "You don't have any notifications"
|
|
162
|
+
: "لا يوجد اشعارات"}
|
|
163
|
+
</Box>
|
|
164
|
+
</Box>
|
|
165
|
+
) : (
|
|
166
|
+
<></>
|
|
167
|
+
)}
|
|
168
|
+
<Box
|
|
169
|
+
sx={{
|
|
170
|
+
flex: 1,
|
|
171
|
+
display: "flex",
|
|
172
|
+
flexDirection: "column",
|
|
173
|
+
overflowY: "auto",
|
|
174
|
+
}}
|
|
175
|
+
>
|
|
176
|
+
{notifications.map((notification, index) => {
|
|
177
|
+
return (
|
|
178
|
+
<NotificationItem
|
|
179
|
+
{...notification}
|
|
180
|
+
setOpen={setOpen}
|
|
181
|
+
showDivider={index < notifications.length - 1}
|
|
182
|
+
/>
|
|
183
|
+
);
|
|
184
|
+
})}
|
|
185
|
+
</Box>
|
|
186
|
+
<Divider variant="fullWidth" flexItem>
|
|
187
|
+
<Button
|
|
188
|
+
sx={{ textTransform: "none" }}
|
|
189
|
+
onClick={() => {
|
|
190
|
+
navigate("myNotifications");
|
|
191
|
+
}}
|
|
192
|
+
>
|
|
193
|
+
{AppLayout.appDirection === "ltr"
|
|
194
|
+
? "Show Old Notifications"
|
|
195
|
+
: "عرض الاشعارات القديمة"}
|
|
196
|
+
</Button>
|
|
197
|
+
</Divider>
|
|
198
|
+
</Paper>
|
|
199
|
+
</Fade>
|
|
200
|
+
)}
|
|
201
|
+
</Popper>
|
|
202
|
+
</div>
|
|
203
|
+
</ClickAwayListener>
|
|
204
|
+
);
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
export default NotificationButton;
|
package/src/layout/TopBar.tsx
CHANGED
|
@@ -22,6 +22,7 @@ import { useSession, useWindow } from "../hooks";
|
|
|
22
22
|
import ChangeOrgForm from "../components/common/ChangeOrgForm";
|
|
23
23
|
import { toggleSideBarState } from "../redux/features/common/SideBarSlice";
|
|
24
24
|
import AttachmentImageViewer from "../components/templates/attachment/AttachmentImageViewer";
|
|
25
|
+
import NotificationButton from "./NotificationButton";
|
|
25
26
|
|
|
26
27
|
interface AppBarProps extends MuiAppBarProps {
|
|
27
28
|
open?: boolean;
|
|
@@ -192,6 +193,7 @@ const TopBar: React.FC = () => {
|
|
|
192
193
|
<></>
|
|
193
194
|
)}
|
|
194
195
|
|
|
196
|
+
<NotificationButton />
|
|
195
197
|
<AttachmentImageViewer
|
|
196
198
|
showAsAvatar={true}
|
|
197
199
|
attachmentCode="PERSON_IMG"
|
|
@@ -276,8 +276,8 @@
|
|
|
276
276
|
"WORKFLOW_DOCUMENT_STATUS_IS_ZERO_STATE": "Is zero state",
|
|
277
277
|
"WORKFLOW_DOCUMENT_STATUS_NEXT_ACTION_TAKERS_QUERY_ID": "Next action takers query id",
|
|
278
278
|
"WORKFLOW_DOCUMENT_STATUS_NEXT_ACTIONS_QUERY_ID": "Next actions query id",
|
|
279
|
-
"WORKFLOW_DOCUMENT_STATUS_WORKFLOW_DOCUMENT_ID": "Workflow document id",
|
|
280
|
-
"ATTACHMENT_ATTACHMENT_CONFIG_ID": "Attachment config id",
|
|
279
|
+
"WORKFLOW_DOCUMENT_STATUS_WORKFLOW_DOCUMENT_ID": "Workflow document id",
|
|
280
|
+
"ATTACHMENT_ATTACHMENT_CONFIG_ID": "Attachment config id",
|
|
281
281
|
"ATTACHMENT_CONFIG_SINGULAR": "Attachment Configuration",
|
|
282
282
|
"ATTACHMENT_CONFIG_PLURAL": "Attachment Configuration",
|
|
283
283
|
"ATTACHMENT_CONFIG_ALLOWED_FILE_TYPES": "Allowed file types",
|
|
@@ -288,5 +288,30 @@
|
|
|
288
288
|
"ATTACHMENT_CONFIG_RELATED_DATABASE_TABLE": "Related database table",
|
|
289
289
|
"ATTACHMENT_CONFIG_STORAGE_TYPE": "Storage type",
|
|
290
290
|
"ATTACHMENT_CONFIG_UPLOAD_AUTHORITY_KEY": "Upload authority key",
|
|
291
|
-
"ATTACHMENT_CONFIG_UPLOAD_DIRECTORY": "Upload directory"
|
|
291
|
+
"ATTACHMENT_CONFIG_UPLOAD_DIRECTORY": "Upload directory",
|
|
292
|
+
"NOTIFICATION_SINGULAR": "notification",
|
|
293
|
+
"NOTIFICATION_PLURAL": "notifications",
|
|
294
|
+
"NOTIFICATION_IS_ACTIVE": "Is active",
|
|
295
|
+
"NOTIFICATION_NOTIFICATION_ACTION_PAYLOAD": "Notification action payload",
|
|
296
|
+
"NOTIFICATION_NOTIFICATION_CODE": "Notification code",
|
|
297
|
+
"NOTIFICATION_NOTIFICATION_ICON": "Notification icon",
|
|
298
|
+
"NOTIFICATION_NOTIFICATION_QUERY_ID": "Notification query id",
|
|
299
|
+
"NOTIFICATION_SYSTEM_APPLICATION_ID": "System application id",
|
|
300
|
+
"NOTIFICATION_USERS_TO_NOTIFY_QUERY_ID": "Users to notify query id",
|
|
301
|
+
"NOTIFICATION_AUTHORITY_TO_NOTIFY": "Authority to notify",
|
|
302
|
+
"NOTIFICATION_NOTIFICATION_ACTION": "Notification action",
|
|
303
|
+
"NOTIFICATION_NOTIFICATION_AR_TEXT": "Notification ar text",
|
|
304
|
+
"NOTIFICATION_NOTIFICATION_EN_TEXT": "Notification en text",
|
|
305
|
+
"NOTIFICATION_NOTIFICATION_ICON_COLOR": "Notification icon color",
|
|
306
|
+
"NOTIFICATION_NOTIFICATION_TEXT_COLOR": "Notification text color",
|
|
307
|
+
"NOTIFICATION_ORGANIZATION_ID_FIELD": "Organization id field",
|
|
308
|
+
"NOTIFICATION_QUEUE_SYSTEM_APPLICATION_ID": "System application id",
|
|
309
|
+
"NOTIFICATION_QUEUE_NOTIFICATION_ACTION": "Notification action",
|
|
310
|
+
"NOTIFICATION_QUEUE_NOTIFICATION_ACTION_PAYLOAD": "Notification action payload",
|
|
311
|
+
"NOTIFICATION_QUEUE_NOTIFICATION_AR_TEXT": "Notification ar text",
|
|
312
|
+
"NOTIFICATION_QUEUE_NOTIFICATION_EN_TEXT": "Notification en text",
|
|
313
|
+
"NOTIFICATION_QUEUE_NOTIFICATION_ICON": "Notification icon",
|
|
314
|
+
"NOTIFICATION_QUEUE_NOTIFICATION_ICON_COLOR": "Notification icon color",
|
|
315
|
+
"NOTIFICATION_QUEUE_NOTIFICATION_ID": "Notification id",
|
|
316
|
+
"NOTIFICATION_QUEUE_NOTIFICATION_TEXT_COLOR": "Notification text color"
|
|
292
317
|
}
|
|
@@ -289,5 +289,30 @@
|
|
|
289
289
|
"ATTACHMENT_CONFIG_RELATED_DATABASE_TABLE": "Related database table",
|
|
290
290
|
"ATTACHMENT_CONFIG_STORAGE_TYPE": "Storage type",
|
|
291
291
|
"ATTACHMENT_CONFIG_UPLOAD_AUTHORITY_KEY": "Upload authority key",
|
|
292
|
-
"ATTACHMENT_CONFIG_UPLOAD_DIRECTORY": "Upload directory"
|
|
292
|
+
"ATTACHMENT_CONFIG_UPLOAD_DIRECTORY": "Upload directory",
|
|
293
|
+
"NOTIFICATION_SINGULAR": "notification",
|
|
294
|
+
"NOTIFICATION_PLURAL": "notifications",
|
|
295
|
+
"NOTIFICATION_IS_ACTIVE": "Is active",
|
|
296
|
+
"NOTIFICATION_NOTIFICATION_ACTION_PAYLOAD": "Notification action payload",
|
|
297
|
+
"NOTIFICATION_NOTIFICATION_CODE": "Notification code",
|
|
298
|
+
"NOTIFICATION_NOTIFICATION_ICON": "Notification icon",
|
|
299
|
+
"NOTIFICATION_NOTIFICATION_QUERY_ID": "Notification query id",
|
|
300
|
+
"NOTIFICATION_SYSTEM_APPLICATION_ID": "System application id",
|
|
301
|
+
"NOTIFICATION_USERS_TO_NOTIFY_QUERY_ID": "Users to notify query id",
|
|
302
|
+
"NOTIFICATION_AUTHORITY_TO_NOTIFY": "Authority to notify",
|
|
303
|
+
"NOTIFICATION_NOTIFICATION_ACTION": "Notification action",
|
|
304
|
+
"NOTIFICATION_NOTIFICATION_AR_TEXT": "Notification ar text",
|
|
305
|
+
"NOTIFICATION_NOTIFICATION_EN_TEXT": "Notification en text",
|
|
306
|
+
"NOTIFICATION_NOTIFICATION_ICON_COLOR": "Notification icon color",
|
|
307
|
+
"NOTIFICATION_NOTIFICATION_TEXT_COLOR": "Notification text color",
|
|
308
|
+
"NOTIFICATION_ORGANIZATION_ID_FIELD": "Organization id field",
|
|
309
|
+
"NOTIFICATION_QUEUE_SYSTEM_APPLICATION_ID": "System application id",
|
|
310
|
+
"NOTIFICATION_QUEUE_NOTIFICATION_ACTION": "Notification action",
|
|
311
|
+
"NOTIFICATION_QUEUE_NOTIFICATION_ACTION_PAYLOAD": "Notification action payload",
|
|
312
|
+
"NOTIFICATION_QUEUE_NOTIFICATION_AR_TEXT": "Notification ar text",
|
|
313
|
+
"NOTIFICATION_QUEUE_NOTIFICATION_EN_TEXT": "Notification en text",
|
|
314
|
+
"NOTIFICATION_QUEUE_NOTIFICATION_ICON": "Notification icon",
|
|
315
|
+
"NOTIFICATION_QUEUE_NOTIFICATION_ICON_COLOR": "Notification icon color",
|
|
316
|
+
"NOTIFICATION_QUEUE_NOTIFICATION_ID": "Notification id",
|
|
317
|
+
"NOTIFICATION_QUEUE_NOTIFICATION_TEXT_COLOR": "Notification text color"
|
|
293
318
|
}
|
|
@@ -37,6 +37,19 @@ export const ADMINISTRATION_STORES: CommonStores = {
|
|
|
37
37
|
{ name: "LinearProgress" },
|
|
38
38
|
],
|
|
39
39
|
},
|
|
40
|
+
SystemTimeIntervals: {
|
|
41
|
+
autoLoad: false,
|
|
42
|
+
url: "",
|
|
43
|
+
data: [
|
|
44
|
+
{ value: "second" },
|
|
45
|
+
{ value: "minute" },
|
|
46
|
+
{ value: "hour" },
|
|
47
|
+
{ value: "day" },
|
|
48
|
+
{ value: "week" },
|
|
49
|
+
{ value: "month" },
|
|
50
|
+
{ value: "year" },
|
|
51
|
+
],
|
|
52
|
+
},
|
|
40
53
|
SystemMailRecipientTypes: {
|
|
41
54
|
autoLoad: false,
|
|
42
55
|
url: "",
|
|
@@ -19,6 +19,9 @@ import SystemApplicationAuthorityGrid from "../../components/administration/admi
|
|
|
19
19
|
import SystemApplicationRoleGrid from "../../components/administration/admin/SystemApplicationRoleGrid";
|
|
20
20
|
import SystemApplicationModuleGrid from "../../components/administration/admin/SystemApplicationModuleGrid";
|
|
21
21
|
import AttachmentConfigGrid from "../../components/administration/dev/AttachmentConfigGrid";
|
|
22
|
+
import NotificationGrid from "../../components/administration/dev/NotificationGrid";
|
|
23
|
+
import MyNotificationsPanel from "../../components/common/MyNotificationsPanel";
|
|
24
|
+
import WorkflowRouteComponent from "../../components/templates/workflow/WorkflowRouteComponent";
|
|
22
25
|
|
|
23
26
|
export const devRoutes: Array<SystemRoute> = [
|
|
24
27
|
{
|
|
@@ -108,6 +111,18 @@ export const devRoutes: Array<SystemRoute> = [
|
|
|
108
111
|
path: "dev/attachmentconfigs",
|
|
109
112
|
component: AttachmentConfigGrid,
|
|
110
113
|
},
|
|
114
|
+
{
|
|
115
|
+
path: "dev/notifications",
|
|
116
|
+
component: NotificationGrid,
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
path: "myNotifications",
|
|
120
|
+
component: MyNotificationsPanel,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
path: "workflow/:workflowDocumentCode/:refDocumentId",
|
|
124
|
+
component: WorkflowRouteComponent,
|
|
125
|
+
},
|
|
111
126
|
];
|
|
112
127
|
|
|
113
128
|
// import { devRoutes } from "./devRoutes";
|