@mistertemp/libs-front-shared 3.0.3 → 4.0.0-alpha.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.
- package/CHANGELOG.md +7 -0
- package/package.json +10 -1
- package/src/components/Notifications/CustomerNotificationCenterClient.ts +195 -0
- package/src/components/Notifications/EmptyNotificationsList.tsx +33 -0
- package/src/components/Notifications/Notification.tsx +105 -0
- package/src/components/Notifications/NotificationContent.tsx +153 -0
- package/src/components/Notifications/Notifications.module.scss +345 -0
- package/src/components/Notifications/NotificationsBell.tsx +242 -0
- package/src/components/Notifications/NotificationsCenterContext.tsx +578 -0
- package/src/components/Notifications/NotificationsList.tsx +293 -0
- package/src/components/Notifications/PopupNotifications.module.scss +73 -0
- package/src/components/Notifications/PopupNotifications.tsx +42 -0
- package/src/components/Notifications/dateISOToString.ts +32 -0
- package/src/components/Notifications/index.ts +7 -0
- package/src/components/Notifications/types.ts +39 -0
- package/src/components/Notifications/useLiveNotifications.ts +122 -0
- package/src/components/TextFilter/TextFilter.tsx +1 -8
- package/src/components/index.ts +1 -0
- package/src/locales/es/date-picker.json +11 -0
- package/src/locales/es/notification-error.json +12 -0
- package/src/locales/es/notification-utils.json +7 -0
- package/src/locales/es/notification.json +17 -0
- package/src/locales/fr/notification-error.json +12 -0
- package/src/locales/fr/notification-utils.json +7 -0
- package/src/locales/fr/notification.json +17 -0
- package/src/locales/it/notification-error.json +12 -0
- package/src/locales/it/notification-utils.json +7 -0
- package/src/locales/it/notification.json +17 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Badge,
|
|
3
|
+
Button,
|
|
4
|
+
ChevronDownSvg,
|
|
5
|
+
ChevronUpSvg,
|
|
6
|
+
DatePicker,
|
|
7
|
+
Dropdown,
|
|
8
|
+
Loader,
|
|
9
|
+
Tabs,
|
|
10
|
+
Tag,
|
|
11
|
+
} from '@mistertemp/design-system';
|
|
12
|
+
import { MixedDatePickerSelection } from '@mistertemp/design-system/src/components/design-system/DatePicker/DatePicker';
|
|
13
|
+
import classNames from 'classnames';
|
|
14
|
+
import { fr, it } from 'date-fns/locale';
|
|
15
|
+
import { TFunction } from 'i18next';
|
|
16
|
+
import React, { FC, useContext, useMemo, useState } from 'react';
|
|
17
|
+
import { DateRange } from 'react-day-picker';
|
|
18
|
+
|
|
19
|
+
import { useBundledTranslation } from '../../hooks';
|
|
20
|
+
import datePickerEs from '../../locales/es/date-picker.json';
|
|
21
|
+
import datePickerFr from '../../locales/fr/date-picker.json';
|
|
22
|
+
import datePickerIt from '../../locales/it/date-picker.json';
|
|
23
|
+
import notificationEs from '../../locales/es/notification.json';
|
|
24
|
+
import notificationFr from '../../locales/fr/notification.json';
|
|
25
|
+
import notificationIt from '../../locales/it/notification.json';
|
|
26
|
+
import EmptyNotificationsList from './EmptyNotificationsList';
|
|
27
|
+
import { Notification } from './Notification';
|
|
28
|
+
import { NotificationsCenterContext } from './NotificationsCenterContext';
|
|
29
|
+
import styles from './Notifications.module.scss';
|
|
30
|
+
|
|
31
|
+
const dataTestId = 'notificationsList';
|
|
32
|
+
const translationNs = 'notification';
|
|
33
|
+
const notificationTranslations = { fr: notificationFr, it: notificationIt, es: notificationEs };
|
|
34
|
+
|
|
35
|
+
interface NotificationsListProps {
|
|
36
|
+
/**
|
|
37
|
+
* Locale used for the date picker component.
|
|
38
|
+
* @default fr (French)
|
|
39
|
+
*/
|
|
40
|
+
locale?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const NotificationsList: FC<NotificationsListProps> = ({ locale: localeStr }) => {
|
|
44
|
+
const { t } = useBundledTranslation(translationNs, notificationTranslations);
|
|
45
|
+
const { t: tDate } = useBundledTranslation('date-picker', {
|
|
46
|
+
fr: datePickerFr,
|
|
47
|
+
it: datePickerIt,
|
|
48
|
+
es: datePickerEs,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const {
|
|
52
|
+
unreadNotifications,
|
|
53
|
+
notifications,
|
|
54
|
+
isFullPageMode,
|
|
55
|
+
startDate,
|
|
56
|
+
setStartDate,
|
|
57
|
+
endDate,
|
|
58
|
+
setEndDate,
|
|
59
|
+
setIsDisplayReadNotification,
|
|
60
|
+
markAllAsRead,
|
|
61
|
+
setCategories,
|
|
62
|
+
categories,
|
|
63
|
+
notificationsHasBeenLoaded,
|
|
64
|
+
} = useContext(NotificationsCenterContext);
|
|
65
|
+
|
|
66
|
+
const [isDatePickerOpen, setIsDatePickerOpen] = useState(false);
|
|
67
|
+
|
|
68
|
+
const dateLocale = useMemo(
|
|
69
|
+
() => (localeStr === 'it-IT' ? it : fr),
|
|
70
|
+
[localeStr],
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const tabs = useMemo(() => {
|
|
74
|
+
const tabs = [
|
|
75
|
+
{
|
|
76
|
+
label: t('notification:notificationsPopover.all'),
|
|
77
|
+
path: 'tab_all',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
label: t('notification:notificationsPopover.unread'),
|
|
81
|
+
path: 'tab_unread',
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
if (isFullPageMode) {
|
|
86
|
+
tabs.push({
|
|
87
|
+
label: t('notification:notificationsPopover.read'),
|
|
88
|
+
path: 'tab_read',
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return tabs;
|
|
93
|
+
}, [isFullPageMode, t]);
|
|
94
|
+
|
|
95
|
+
const [selectedDate, setSelectedDate] = useState<MixedDatePickerSelection>([new Date()]);
|
|
96
|
+
|
|
97
|
+
const handleOnChangeTab = (value: string) => {
|
|
98
|
+
switch (value) {
|
|
99
|
+
case 'tab_all':
|
|
100
|
+
setIsDisplayReadNotification(undefined);
|
|
101
|
+
break;
|
|
102
|
+
case 'tab_read':
|
|
103
|
+
setIsDisplayReadNotification(true);
|
|
104
|
+
break;
|
|
105
|
+
case 'tab_unread':
|
|
106
|
+
setIsDisplayReadNotification(false);
|
|
107
|
+
break;
|
|
108
|
+
default:
|
|
109
|
+
setIsDisplayReadNotification(undefined);
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const handleAllMarkAsRead = () => {
|
|
115
|
+
markAllAsRead();
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const handleReinitFilters = () => {
|
|
119
|
+
setStartDate(undefined);
|
|
120
|
+
setEndDate(undefined);
|
|
121
|
+
setCategories([]);
|
|
122
|
+
setSelectedDate(new Date());
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const handleOpenChange = (isDatePickerOpen: boolean) => {
|
|
126
|
+
setIsDatePickerOpen(isDatePickerOpen);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const handleOnCancel = () => {
|
|
130
|
+
setIsDatePickerOpen(false);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const handleOnSelect = (date: Date | Date[] | DateRange | undefined) => {
|
|
134
|
+
setSelectedDate(date);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const handleOnSubmit = (value: Date | Array<Date> | DateRange) => {
|
|
138
|
+
if (value && typeof value === 'object' && 'from' in value && 'to' in value) {
|
|
139
|
+
if (value.from) {
|
|
140
|
+
const startDate = new Date(
|
|
141
|
+
Date.UTC(
|
|
142
|
+
value.from.getFullYear(),
|
|
143
|
+
value.from.getMonth(),
|
|
144
|
+
value.from.getDate(),
|
|
145
|
+
0,
|
|
146
|
+
0,
|
|
147
|
+
0,
|
|
148
|
+
0,
|
|
149
|
+
),
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
setStartDate(startDate);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (value.to) {
|
|
156
|
+
const endDate = new Date(
|
|
157
|
+
Date.UTC(
|
|
158
|
+
value.to.getFullYear(),
|
|
159
|
+
value.to.getMonth(),
|
|
160
|
+
value.to.getDate(),
|
|
161
|
+
23,
|
|
162
|
+
59,
|
|
163
|
+
59,
|
|
164
|
+
999,
|
|
165
|
+
),
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
setEndDate(endDate);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
setIsDatePickerOpen(false);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const iconName = isDatePickerOpen ? ChevronUpSvg : ChevronDownSvg;
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<div data-testid={`${dataTestId}`} className={styles.notificationsListBody}>
|
|
178
|
+
<div className={styles.notiticationsListFilters}>
|
|
179
|
+
<Tabs
|
|
180
|
+
data-testid={`${dataTestId}+tabs`}
|
|
181
|
+
onChange={handleOnChangeTab}
|
|
182
|
+
size="sm"
|
|
183
|
+
tabs={tabs}
|
|
184
|
+
value={
|
|
185
|
+
isFullPageMode ? 'tab_all' : 'tab_unread'
|
|
186
|
+
}
|
|
187
|
+
variant={isFullPageMode ? 'buttons' : 'underline'}
|
|
188
|
+
className={styles.tabs}
|
|
189
|
+
/>
|
|
190
|
+
{unreadNotifications > 0 && !isFullPageMode && (
|
|
191
|
+
<Badge
|
|
192
|
+
data-testid={`${dataTestId}+unreadBadge`}
|
|
193
|
+
className={styles.notificationsListUnreadBadge}
|
|
194
|
+
text={unreadNotifications}
|
|
195
|
+
color="primary"
|
|
196
|
+
size="sm"
|
|
197
|
+
/>
|
|
198
|
+
)}
|
|
199
|
+
{isFullPageMode && (
|
|
200
|
+
<Button
|
|
201
|
+
data-testid={`${dataTestId}+markAllAsReadButton`}
|
|
202
|
+
color="secondary-gray"
|
|
203
|
+
size="small"
|
|
204
|
+
text={t('notification:notificationsPopover.markAllAsRead')}
|
|
205
|
+
onClick={handleAllMarkAsRead}
|
|
206
|
+
disabled={unreadNotifications === 0}
|
|
207
|
+
className={styles.notificationsBtnMarkAllRead}
|
|
208
|
+
/>
|
|
209
|
+
)}
|
|
210
|
+
{isFullPageMode && (
|
|
211
|
+
<Dropdown
|
|
212
|
+
isOpen={isDatePickerOpen}
|
|
213
|
+
onOpenChange={handleOpenChange}
|
|
214
|
+
onDropdownCTAClick={() => {}}
|
|
215
|
+
data-testid={`${dataTestId}+dropDownDate`}
|
|
216
|
+
trigger={
|
|
217
|
+
<Tag
|
|
218
|
+
data-testid={`${dataTestId}+dropDownDate-Tag`}
|
|
219
|
+
label={t('notification:notificationsExtended.date')}
|
|
220
|
+
rightIcon={iconName}
|
|
221
|
+
size="lg"
|
|
222
|
+
/>
|
|
223
|
+
}
|
|
224
|
+
className={styles.notificationsDropDownDate}>
|
|
225
|
+
<DatePicker
|
|
226
|
+
t={tDate as TFunction}
|
|
227
|
+
locale={dateLocale}
|
|
228
|
+
selected={selectedDate}
|
|
229
|
+
data-testid={`${dataTestId}+date-picker`}
|
|
230
|
+
mode={'range'}
|
|
231
|
+
onSelect={handleOnSelect}
|
|
232
|
+
onSubmit={handleOnSubmit}
|
|
233
|
+
onCancel={handleOnCancel}
|
|
234
|
+
/>
|
|
235
|
+
</Dropdown>
|
|
236
|
+
)}
|
|
237
|
+
{isFullPageMode && (
|
|
238
|
+
<Button
|
|
239
|
+
data-testid={`${dataTestId}+ReinitFilterButton`}
|
|
240
|
+
color="tertiary-color"
|
|
241
|
+
size="small"
|
|
242
|
+
text={t('notification:notificationsExtended.reinitFilters')}
|
|
243
|
+
onClick={handleReinitFilters}
|
|
244
|
+
disabled={
|
|
245
|
+
startDate == undefined && endDate == undefined && categories.length == 0
|
|
246
|
+
}
|
|
247
|
+
/>
|
|
248
|
+
)}
|
|
249
|
+
</div>
|
|
250
|
+
<div
|
|
251
|
+
className={classNames(
|
|
252
|
+
isFullPageMode
|
|
253
|
+
? styles.notificationsListFullPageMode
|
|
254
|
+
: styles.notificationsList,
|
|
255
|
+
)}
|
|
256
|
+
data-testid={`${dataTestId}+list-container`}>
|
|
257
|
+
<div
|
|
258
|
+
className={classNames(
|
|
259
|
+
isFullPageMode && styles.notificationListRoundedContainer,
|
|
260
|
+
)}>
|
|
261
|
+
{!notificationsHasBeenLoaded ? (
|
|
262
|
+
<div
|
|
263
|
+
data-testid="notificationsList+Loader"
|
|
264
|
+
className={styles.notificationsListLoader}>
|
|
265
|
+
<Loader />
|
|
266
|
+
</div>
|
|
267
|
+
) : notifications.length > 0 ? (
|
|
268
|
+
notifications.map((notification, rank) => (
|
|
269
|
+
<Notification
|
|
270
|
+
data-testid={`${dataTestId}+`}
|
|
271
|
+
key={notification.notificationId}
|
|
272
|
+
id={notification.notificationId}
|
|
273
|
+
title={notification.title}
|
|
274
|
+
createdAt={notification.createdAt}
|
|
275
|
+
content={notification.message}
|
|
276
|
+
link={notification.link}
|
|
277
|
+
isUnread={notification.readAt === undefined}
|
|
278
|
+
notificationType={notification.notificationType}
|
|
279
|
+
rank={rank}
|
|
280
|
+
aggregatedSubtitle={notification.aggregatedSubtitle}
|
|
281
|
+
aggregatedNotifications={notification.aggregatedNotifications}
|
|
282
|
+
/>
|
|
283
|
+
))
|
|
284
|
+
) : (
|
|
285
|
+
<EmptyNotificationsList />
|
|
286
|
+
)}
|
|
287
|
+
</div>
|
|
288
|
+
</div>
|
|
289
|
+
</div>
|
|
290
|
+
);
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
export default NotificationsList;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
.popupNotificationsContainer {
|
|
2
|
+
position: fixed;
|
|
3
|
+
top: 6.4rem;
|
|
4
|
+
right: -84rem;
|
|
5
|
+
display: flex;
|
|
6
|
+
width: 80rem;
|
|
7
|
+
height: 1000px;
|
|
8
|
+
flex-direction: column;
|
|
9
|
+
padding-right: 0.5rem;
|
|
10
|
+
padding-left: 0.5rem;
|
|
11
|
+
margin-top: 0.8rem;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.popupNotifications {
|
|
15
|
+
width: 36rem;
|
|
16
|
+
padding-left: 0.8rem;
|
|
17
|
+
border-radius: 0.8rem;
|
|
18
|
+
margin-top: 0.5rem;
|
|
19
|
+
margin-left: 40rem;
|
|
20
|
+
animation: bounce-in-left 1.1s both;
|
|
21
|
+
background-color: white;
|
|
22
|
+
box-shadow: 0 4px 8px -2px #1018281a;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.displayed {
|
|
26
|
+
margin-left: 0;
|
|
27
|
+
opacity: 1;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.hidden {
|
|
31
|
+
animation: bounce-out-left 1.1s both;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@keyframes bounce-in-left {
|
|
35
|
+
0% {
|
|
36
|
+
animation-timing-function: ease-in;
|
|
37
|
+
opacity: 0;
|
|
38
|
+
transform: translateX(0);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
60% {
|
|
42
|
+
animation-timing-function: ease-out;
|
|
43
|
+
opacity: 1;
|
|
44
|
+
transform: translateX(-44rem);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
80% {
|
|
48
|
+
animation-timing-function: ease-in;
|
|
49
|
+
transform: translateX(-40.4rem);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
100% {
|
|
53
|
+
animation-timing-function: ease-out;
|
|
54
|
+
transform: translateX(-44rem);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@keyframes bounce-out-left {
|
|
59
|
+
0% {
|
|
60
|
+
animation-timing-function: ease-in;
|
|
61
|
+
transform: translateX(-44rem);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
60% {
|
|
65
|
+
animation-timing-function: ease-in;
|
|
66
|
+
transform: translateX(0);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
100% {
|
|
70
|
+
animation-timing-function: ease-in;
|
|
71
|
+
transform: translateX(0);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import classnames from 'classnames';
|
|
2
|
+
import React, { FC, useContext } from 'react';
|
|
3
|
+
|
|
4
|
+
import { NotificationsCenterContext } from './NotificationsCenterContext';
|
|
5
|
+
import { Notification } from './Notification';
|
|
6
|
+
import styles from './PopupNotifications.module.scss';
|
|
7
|
+
|
|
8
|
+
export const PopupNotifications: FC = () => {
|
|
9
|
+
const { popupNotifications, closePopupNotification } = useContext(NotificationsCenterContext);
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<div className={styles.popupNotificationsContainer}>
|
|
13
|
+
{[...popupNotifications].map((notification, rank) => (
|
|
14
|
+
<div
|
|
15
|
+
key={notification.notificationId}
|
|
16
|
+
className={classnames(
|
|
17
|
+
styles.popupNotifications,
|
|
18
|
+
notification.displayed ? styles.displayed : '',
|
|
19
|
+
notification.hidden ? styles.hidden : '',
|
|
20
|
+
)}>
|
|
21
|
+
<Notification
|
|
22
|
+
data-testid={`popupNotifications+${notification.notificationId}`}
|
|
23
|
+
key={notification.notificationId}
|
|
24
|
+
id={notification.notificationId}
|
|
25
|
+
title={notification.title}
|
|
26
|
+
createdAt={notification.createdAt}
|
|
27
|
+
content={notification.message}
|
|
28
|
+
onClose={() => closePopupNotification(notification)}
|
|
29
|
+
link={notification.link}
|
|
30
|
+
isUnread={false}
|
|
31
|
+
notificationType={notification.notificationType}
|
|
32
|
+
rank={rank}
|
|
33
|
+
aggregatedSubtitle={notification.aggregatedSubtitle}
|
|
34
|
+
aggregatedNotifications={notification.aggregatedNotifications}
|
|
35
|
+
/>
|
|
36
|
+
</div>
|
|
37
|
+
))}
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default PopupNotifications;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const dateISOToString = (
|
|
2
|
+
isoDate: string,
|
|
3
|
+
): {
|
|
4
|
+
value: string;
|
|
5
|
+
i18n: 'minutesAgo' | 'hoursAgo' | 'onDate';
|
|
6
|
+
} => {
|
|
7
|
+
const date = new Date(isoDate);
|
|
8
|
+
const time = Date.now() - date.getTime();
|
|
9
|
+
|
|
10
|
+
if (time < 1000 * 60 * 60) {
|
|
11
|
+
// Between one minute and one hour
|
|
12
|
+
return { value: `${Math.floor(time / (1000 * 60))}`, i18n: 'minutesAgo' };
|
|
13
|
+
} else if (time < 1000 * 60 * 60 * 24) {
|
|
14
|
+
// Between one hour and one day
|
|
15
|
+
return { value: `${Math.floor(time / (1000 * 60 * 60))}`, i18n: 'hoursAgo' };
|
|
16
|
+
} else {
|
|
17
|
+
// More than one day
|
|
18
|
+
let day = date.getDate().toString();
|
|
19
|
+
let month = (date.getMonth() + 1).toString();
|
|
20
|
+
const year = date.getFullYear().toString();
|
|
21
|
+
|
|
22
|
+
if (day.length < 2) {
|
|
23
|
+
day = '0' + day;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (month.length < 2) {
|
|
27
|
+
month = '0' + month;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return { value: `${day}/${month}/${year}`, i18n: 'onDate' };
|
|
31
|
+
}
|
|
32
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './NotificationsBell';
|
|
2
|
+
export * from './NotificationsList';
|
|
3
|
+
export * from './PopupNotifications';
|
|
4
|
+
export * from './NotificationsCenterContext';
|
|
5
|
+
export * from './useLiveNotifications';
|
|
6
|
+
export * from './CustomerNotificationCenterClient';
|
|
7
|
+
export type { NotificationCenterClientInterface } from './types';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { NotificationTypes, PublishedNotification } from '@mistertemp/types-agency-notification-center';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Common interface for notification center clients.
|
|
5
|
+
* Both the agency client (match) and customer client implement this contract.
|
|
6
|
+
*/
|
|
7
|
+
export interface NotificationCenterClientInterface {
|
|
8
|
+
getNotificationList(params: {
|
|
9
|
+
userCorrelationId: string;
|
|
10
|
+
agencyId: string;
|
|
11
|
+
token: string;
|
|
12
|
+
queryString?: Record<string, unknown>;
|
|
13
|
+
}): Promise<PublishedNotification<NotificationTypes>[]>;
|
|
14
|
+
|
|
15
|
+
getNotificationStatus(params: {
|
|
16
|
+
userCorrelationId: string;
|
|
17
|
+
agencyId: string;
|
|
18
|
+
token: string;
|
|
19
|
+
}): Promise<{ countUnread: number; countUnseen: number }>;
|
|
20
|
+
|
|
21
|
+
markNotificationAsRead(params: {
|
|
22
|
+
userCorrelationId: string;
|
|
23
|
+
agencyId: string;
|
|
24
|
+
notificationId: string;
|
|
25
|
+
token: string;
|
|
26
|
+
}): Promise<unknown>;
|
|
27
|
+
|
|
28
|
+
markAllNotificationAsRead(params: {
|
|
29
|
+
userCorrelationId: string;
|
|
30
|
+
agencyId: string;
|
|
31
|
+
token: string;
|
|
32
|
+
}): Promise<unknown>;
|
|
33
|
+
|
|
34
|
+
markAlertAsOpen(params: {
|
|
35
|
+
userCorrelationId: string;
|
|
36
|
+
agencyId: string;
|
|
37
|
+
token: string;
|
|
38
|
+
}): Promise<unknown>;
|
|
39
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { IOTMessageHandler, OAuthContext, useIOTSubscriptions } from '@mistertemp/front-core';
|
|
2
|
+
import {
|
|
3
|
+
NotificationTypes,
|
|
4
|
+
PublishedNotification,
|
|
5
|
+
} from '@mistertemp/types-agency-notification-center';
|
|
6
|
+
import { useCallback, useContext } from 'react';
|
|
7
|
+
|
|
8
|
+
import { NotificationsCenterContext } from './NotificationsCenterContext';
|
|
9
|
+
|
|
10
|
+
interface UseLiveNotificationsOptions {
|
|
11
|
+
/**
|
|
12
|
+
* The path to the notifications page.
|
|
13
|
+
* When the user is on this page, popup notifications are not shown.
|
|
14
|
+
*/
|
|
15
|
+
notificationsPagePath?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Hook that subscribes to real-time notifications via IOT.
|
|
20
|
+
*
|
|
21
|
+
* This hook handles:
|
|
22
|
+
* - Subscribing to user-specific notification topics
|
|
23
|
+
* - Processing incoming notifications
|
|
24
|
+
* - Updating notification counters (unread, unseen)
|
|
25
|
+
* - Adding new notifications to the notification center
|
|
26
|
+
* - Showing popup notifications for urgent ones
|
|
27
|
+
*/
|
|
28
|
+
export function useLiveNotifications(options: UseLiveNotificationsOptions = {}) {
|
|
29
|
+
const { notificationsPagePath } = options;
|
|
30
|
+
|
|
31
|
+
const {
|
|
32
|
+
addNotification,
|
|
33
|
+
addPopupNotification,
|
|
34
|
+
isPopoverOpen,
|
|
35
|
+
setUnseenNotifications,
|
|
36
|
+
setUnreadNotifications,
|
|
37
|
+
notificationsHasBeenLoaded,
|
|
38
|
+
agencyId,
|
|
39
|
+
notifications,
|
|
40
|
+
} = useContext(NotificationsCenterContext);
|
|
41
|
+
|
|
42
|
+
const { decodedIdToken } = useContext(OAuthContext);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Counts notifications that match a specific condition
|
|
46
|
+
*/
|
|
47
|
+
function countNotifications(
|
|
48
|
+
notifications: PublishedNotification<NotificationTypes>[],
|
|
49
|
+
condition: (notification: PublishedNotification<NotificationTypes>) => boolean,
|
|
50
|
+
): number {
|
|
51
|
+
return notifications.reduce(
|
|
52
|
+
(acc, notification) => (condition(notification) ? acc + 1 : acc),
|
|
53
|
+
0,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Handler for incoming notifications
|
|
59
|
+
*/
|
|
60
|
+
const onNotificationReceived: IOTMessageHandler<PublishedNotification<NotificationTypes>> =
|
|
61
|
+
useCallback(
|
|
62
|
+
({ detail: { new: notification } }) => {
|
|
63
|
+
if (notification.agencyId !== agencyId) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Show popup for urgent notifications when not on the notifications page
|
|
68
|
+
if (notification.isUrgent) {
|
|
69
|
+
const isOnNotificationsPage =
|
|
70
|
+
notificationsPagePath &&
|
|
71
|
+
window.location.pathname === notificationsPagePath;
|
|
72
|
+
|
|
73
|
+
if (!isOnNotificationsPage) {
|
|
74
|
+
addPopupNotification(notification);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const newNotifications = [
|
|
79
|
+
notification,
|
|
80
|
+
...notifications.filter(
|
|
81
|
+
(n) => n.notificationId !== notification.notificationId,
|
|
82
|
+
),
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
// Append notifications only when the initial load is done
|
|
86
|
+
if (notificationsHasBeenLoaded) {
|
|
87
|
+
addNotification(notification);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Increment unread notifications
|
|
91
|
+
setUnreadNotifications(
|
|
92
|
+
countNotifications(newNotifications, (notif) => !notif.readAt),
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// Increment unseen notifications if the popover is closed
|
|
96
|
+
if (!isPopoverOpen) {
|
|
97
|
+
setUnseenNotifications(
|
|
98
|
+
countNotifications(newNotifications, (notif) => !notif.seenAt),
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
[
|
|
103
|
+
addNotification,
|
|
104
|
+
addPopupNotification,
|
|
105
|
+
agencyId,
|
|
106
|
+
isPopoverOpen,
|
|
107
|
+
notificationsHasBeenLoaded,
|
|
108
|
+
setUnreadNotifications,
|
|
109
|
+
setUnseenNotifications,
|
|
110
|
+
notifications,
|
|
111
|
+
notificationsPagePath,
|
|
112
|
+
],
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
// Create topic string for user-specific notifications
|
|
116
|
+
const topic = `userCorrelationId/${decodedIdToken?.['custom:userCorrelationId']}/notifications`;
|
|
117
|
+
|
|
118
|
+
// Subscribe to the notification topic
|
|
119
|
+
useIOTSubscriptions({
|
|
120
|
+
[topic]: onNotificationReceived,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
@@ -43,14 +43,7 @@ export type TextFilterProps = Omit<
|
|
|
43
43
|
>;
|
|
44
44
|
inputProps?: Omit<
|
|
45
45
|
InputTextProps,
|
|
46
|
-
| '
|
|
47
|
-
| 'data-testid'
|
|
48
|
-
| 'value'
|
|
49
|
-
| 'size'
|
|
50
|
-
| 'type'
|
|
51
|
-
| 'onSubmit'
|
|
52
|
-
| 'onChange'
|
|
53
|
-
| 'inputType'
|
|
46
|
+
'data-testid' | 'value' | 'size' | 'type' | 'onSubmit' | 'onChange' | 'inputType'
|
|
54
47
|
>;
|
|
55
48
|
};
|
|
56
49
|
|
package/src/components/index.ts
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"common": {
|
|
3
|
+
"userNotAuthenticated": "No has iniciado sesión."
|
|
4
|
+
},
|
|
5
|
+
"notifications": {
|
|
6
|
+
"markNotificationAsRead": "Se ha producido un error al marcar tu notificación como leída.",
|
|
7
|
+
"markAllNotificationAsRead": "Se ha producido un error al marcar tus notificaciones como leídas.",
|
|
8
|
+
"getNotificationList": "Se ha producido un error al recuperar tus notificaciones.",
|
|
9
|
+
"getNotificationStatus": "Se ha producido un error al recuperar el estado de tus notificaciones.",
|
|
10
|
+
"markAlertAsOpen": "Se ha producido un error al marcar tus notificaciones como abiertas."
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"notificationsPopover": {
|
|
3
|
+
"title": "Notificaciones",
|
|
4
|
+
"all": "Todas",
|
|
5
|
+
"unread": "No leídas",
|
|
6
|
+
"read": "Leídas",
|
|
7
|
+
"markAllAsRead": "Marcar todo como leído",
|
|
8
|
+
"noNotifications": "No hay notificaciones disponibles.",
|
|
9
|
+
"lastActivityWillDisplayHere": "Las últimas actividades de tus candidatos y clientes aparecerán aquí.",
|
|
10
|
+
"showMore": "Ver más",
|
|
11
|
+
"showLess": "Ver menos"
|
|
12
|
+
},
|
|
13
|
+
"notificationsExtended": {
|
|
14
|
+
"date": "Fecha",
|
|
15
|
+
"reinitFilters": "Restablecer"
|
|
16
|
+
}
|
|
17
|
+
}
|