Skip to content

Commit f5ae0f6

Browse files
committed
fix(notification): refactors and fixes user notifications feature
1 parent 64af47e commit f5ae0f6

File tree

5 files changed

+139
-66
lines changed

5 files changed

+139
-66
lines changed

src/Mutations/notificationMutation.tsx

+18
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,24 @@ export const NotificationSubscription = gql`
4343
}
4444
}
4545
`;
46+
export const PUSH_NOTIFICATION_SUB = gql`
47+
subscription PushNotificationSub($receiverId: String!) {
48+
pushNotification(receiverId: $receiverId) {
49+
sender {
50+
profile {
51+
firstName
52+
lastName
53+
avatar
54+
}
55+
}
56+
createdAt
57+
message
58+
read
59+
receiver
60+
id
61+
}
62+
}
63+
`;
4664
export const deleteNotification = gql`
4765
mutation Mutation($deleteNotificationsId: ID!) {
4866
deleteNotifications(id: $deleteNotificationsId)

src/components/DashHeader.tsx

+9
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { UserContext } from '../hook/useAuth';
1212
import {
1313
getAllNotification,
1414
NotificationSubscription,
15+
PUSH_NOTIFICATION_SUB,
1516
} from '../Mutations/notificationMutation';
1617
import { MenuContext } from '../hook/menuProvider';
1718
import ToggleThemeButton from './TogglethemeIcon';
@@ -74,6 +75,14 @@ function DashHeader() {
7475
receiver: user?.userId,
7576
},
7677
});
78+
useSubscription(PUSH_NOTIFICATION_SUB, {
79+
onData: (data) => {
80+
setNotificationData([data.data.data.pushNotification, ...notifications]);
81+
},
82+
variables: {
83+
receiverId: user?.userId,
84+
},
85+
});
7786

7887
/* istanbul ignore next */
7988
const handleShowProfileDropdown = () =>

src/components/Docs/users.tsx

+71-36
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,76 @@
1-
import React from 'react'
2-
import DocsMain from './DocsMain'
1+
import React from 'react';
2+
import DocsMain from './DocsMain';
33

4-
const UsersDocs = () => {
5-
return (
6-
<div>
7-
<DocsMain
8-
content={
9-
<div className="flex items-start box-border">
10-
<div className="w-full">
11-
<div className=" w-full sm:px-10 mb-10 text-gray-600 dark:text-slate-300 text-lg ml-0 pt-4">
12-
<h2 className="mb-4 mt-4 text-4xl font-[800] text-primary">
13-
Getting started
14-
</h2>
15-
<div className="mt-5 w-[100%] sm:w-full mb-4">
16-
Devpulse is a semi-open platform i.e. using it, requires a certain level of approval from the owners/managers. If you browse the homepage, you might wonder why there is no signup button, this is because to sign up you need an invitation.
17-
<h3 className="m-2 mb-4 text-2xl font-bold">
18-
Terminology to be familiar with:</h3>
19-
<ul className=" list-disc ml-12">
4+
function UsersDocs() {
5+
return (
6+
<div>
7+
<DocsMain
8+
content={
9+
<div className="flex items-start box-border">
10+
<div className="w-full">
11+
<div className=" w-full sm:px-10 mb-10 text-gray-600 dark:text-slate-300 text-lg ml-0 pt-4">
12+
<h2 className="mb-4 mt-4 text-4xl font-[800] text-primary">
13+
Getting started
14+
</h2>
15+
<div className="mt-5 w-[100%] sm:w-full mb-4">
16+
Devpulse is a semi-open platform i.e. using it, requires a
17+
certain level of approval from the owners/managers. If you
18+
browse the homepage, you might wonder why there is no signup
19+
button, this is because to sign up you need an invitation.
20+
<h3 className="m-2 mb-4 text-2xl font-bold">
21+
Terminology to be familiar with:
22+
</h3>
23+
<ul className=" list-disc ml-12">
24+
<li>
25+
<b>Organizations</b>: each Devpulse user belongs to an
26+
organization. The default organization is Andela. The
27+
admin of a given organisation has the highest privilege;
28+
they are the one in charge of managing the rest of the
29+
users and different administrative tasks
30+
</li>
2031

21-
<li><b>Organizations</b>: each Devpulse user belongs to an organization. The default organization is Andela. The admin of a given organisation has the highest privilege; they are the one in charge of managing the rest of the users and different administrative tasks</li>
22-
23-
<li><b>Programs</b>: each organization should have a program that it is running.</li>
24-
<li><b>Managers:</b> each program should have managers with different access levels. (coordinators, technical team leads, managers)</li>
25-
<li><b>Trainees:</b> an ordinary user of the app will fall under the role of a trainee. As a trainee, you belong to a team, which in turn belongs to a cohort, which in turn belongs to a program</li>
26-
</ul>
27-
28-
<h3 className="m-2 mb-4 text-2xl font-bold">Signing up as a user</h3>
29-
<p>To sign up, <b><u>your org admin must send you an invite</u></b>. The invitation email will contain the details of how to sign up</p>
30-
<h3 className="m-2 mb-4 text-2xl font-bold">Signing in as a user</h3>
31-
<p>To sign in, go to the signin page, provide the correct name of your organization, on the next screen enter username and password.</p>
32-
</div>
33-
</div>
32+
<li>
33+
<b>Programs</b>: each organization should have a program
34+
that it is running.
35+
</li>
36+
<li>
37+
<b>Managers:</b> each program should have managers with
38+
different access levels. (coordinators, technical team
39+
leads, managers)
40+
</li>
41+
<li>
42+
<b>Trainees:</b> an ordinary user of the app will fall
43+
under the role of a trainee. As a trainee, you belong to a
44+
team, which in turn belongs to a cohort, which in turn
45+
belongs to a program
46+
</li>
47+
</ul>
48+
<h3 className="m-2 mb-4 text-2xl font-bold">
49+
Signing up as a user
50+
</h3>
51+
<p>
52+
To sign up,{' '}
53+
<b>
54+
<u>your org admin must send you an invite</u>
55+
</b>
56+
. The invitation email will contain the details of how to
57+
sign up
58+
</p>
59+
<h3 className="m-2 mb-4 text-2xl font-bold">
60+
Signing in as a user
61+
</h3>
62+
<p>
63+
To sign in, go to the signin page, provide the correct name
64+
of your organization, on the next screen enter username and
65+
password.
66+
</p>
3467
</div>
68+
</div>
3569
</div>
36-
}
37-
/>
38-
</div>
39-
)
70+
</div>
71+
}
72+
/>
73+
</div>
74+
);
4075
}
41-
export default UsersDocs
76+
export default UsersDocs;

src/components/Notification.tsx

+40-30
Original file line numberDiff line numberDiff line change
@@ -28,40 +28,40 @@ function Notification({
2828
const notifications = user?.notifications;
2929
const { t } = useTranslation();
3030
/* istanbul ignore next */
31-
/* istanbul ignore next */
31+
/* istanbul ignore next */
3232
function removeNotification(id: number): void {
33-
/* istanbul ignore next */
33+
/* istanbul ignore next */
3434
setNotificationData(
3535
notifications.filter((notification: any) => notification.id !== id),
3636
);
37-
/* istanbul ignore next */
37+
/* istanbul ignore next */
3838
delNotification({
3939
variables: {
4040
deleteNotificationsId: id,
4141
},
4242
});
4343
}
4444
/* istanbul ignore next */
45-
// eslint-disable-next-line no-nested-ternary
46-
/* istanbul ignore next */
45+
// eslint-disable-next-line no-nested-ternary
46+
/* istanbul ignore next */
4747
function markRead(id: number): void {
4848
// eslint-disable-next-line no-nested-ternary
4949
setNotificationData(
50-
/* istanbul ignore next */
50+
/* istanbul ignore next */
5151
notifications.map((notification: any) => {
5252
// eslint-disable-next-line no-nested-ternary
53-
/* istanbul ignore next */
53+
/* istanbul ignore next */
5454
if (notification.id === id) {
5555
return {
5656
...notification,
5757
read: true,
5858
};
5959
}
60-
/* istanbul ignore next */
60+
/* istanbul ignore next */
6161
return notification;
6262
}),
6363
);
64-
/* istanbul ignore next */
64+
/* istanbul ignore next */
6565
readNotification({
6666
variables: {
6767
markAsReadId: id,
@@ -71,18 +71,18 @@ function Notification({
7171

7272
/* istanbul ignore next */
7373
function markAllRead(): void {
74-
/* istanbul ignore next */
74+
/* istanbul ignore next */
7575
setNotificationData(
76-
/* istanbul ignore next */
76+
/* istanbul ignore next */
7777
notifications?.map((notification: any) => {
7878
// eslint-disable-next-line no-nested-ternary
79-
/* istanbul ignore next */
80-
if (notification.read !== true) {
79+
/* istanbul ignore next */
80+
if (!notification.read) {
8181
// eslint-disable-next-line no-nested-ternary
82-
/* istanbul ignore next */
82+
/* istanbul ignore next */
8383
return { ...notification, read: true };
8484
}
85-
/* istanbul ignore next */
85+
/* istanbul ignore next */
8686
return notification;
8787
}),
8888
);
@@ -112,7 +112,7 @@ function Notification({
112112
/>
113113
</div>
114114
<div
115-
/* istanbul ignore next */
115+
/* istanbul ignore next */
116116
className="flex flex-col w-full overflow-auto"
117117
data-testid="notificationsContainer"
118118
>
@@ -123,7 +123,7 @@ function Notification({
123123
>
124124
<div
125125
className={`flex flex-row justify-between align-center gap-x-[10px] ${
126-
notification.read === 'false'
126+
!notification.read
127127
? 'bg-[#E5EAFF] font-bold dark:bg-dark-tertiary'
128128
: 'border-border-dark dark:border-white dark:bg-dark-tertiary opacity-30 hover:bg-[#E5EAFF] hover:opacity-100 dark:hover:bg-dark-tertiary'
129129
}`}
@@ -141,9 +141,9 @@ function Notification({
141141

142142
<div
143143
className="flex flex-col w-full gap-[5px] cursor-pointer"
144-
/* istanbul ignore next */
144+
/* istanbul ignore next */
145145
onClick={() => {
146-
/* istanbul ignore next */
146+
/* istanbul ignore next */
147147
markRead(notification.id);
148148
/* istanbul ignore next */
149149
if (
@@ -155,13 +155,13 @@ function Notification({
155155
)
156156
) {
157157
const ticketId: string =
158-
// eslint-disable-next-line no-nested-ternary
159-
/* istanbul ignore next */
158+
// eslint-disable-next-line no-nested-ternary
159+
/* istanbul ignore next */
160160
notification.message.split(' ')[
161161
// eslint-disable-next-line no-nested-ternary
162162
notification.message.split(' ').length - 1
163163
];
164-
/* istanbul ignore next */
164+
/* istanbul ignore next */
165165
handleShowNotification();
166166
return navigate(`/tickets/${ticketId}`);
167167
}
@@ -171,7 +171,7 @@ function Notification({
171171
} else {
172172
navigate('/ratings');
173173
}
174-
/* istanbul ignore next */
174+
/* istanbul ignore next */
175175
return handleShowNotification();
176176
}}
177177
data-testid={index === 0 && 'read'}
@@ -182,18 +182,28 @@ function Notification({
182182
? notification.sender.profile.name
183183
: notification.sender.email}
184184
</p>
185-
<p className="text-[#111827] dark:text-white text-[12px]">
186-
{notification.message}
187-
</p>
185+
<p
186+
className="text-[#111827] dark:text-white text-[12px]"
187+
// eslint-disable-next-line react/no-danger
188+
dangerouslySetInnerHTML={{
189+
__html: notification.message.replace(
190+
/"([^"]+)"/g,
191+
'<b>$1</b>',
192+
),
193+
}}
194+
/>
188195
<p className="text-[12px] dark:text-white">
189-
{format(new Date(notification.createdAt), 'MMMM dd, p')}
196+
{format(
197+
new Date(Number(notification.createdAt)),
198+
'MMMM dd, p',
199+
)}
190200
</p>
191201
</div>
192202

193203
<div className="flex flex-col items-center transition-all">
194204
<div
195205
className={`h-[15px] w-[15px] rounded-full ${
196-
notification.read === 'false'
206+
!notification.read
197207
? 'bg-[#148FB6]'
198208
: 'border-border-dark dark:border-white border-[1px]'
199209
} mt-[7px] mb-[10px]`}
@@ -203,7 +213,7 @@ function Notification({
203213
className="border-border-dark dark:fill-white h-[20px] w-[20px] cursor-pointer"
204214
onClick={() => {
205215
// eslint-disable-next-line no-nested-ternary
206-
/* istanbul ignore next */
216+
/* istanbul ignore next */
207217
removeNotification(notification.id);
208218
}}
209219
data-testid={index === 0 && 'delete'}
@@ -237,4 +247,4 @@ function Notification({
237247
);
238248
}
239249

240-
export default Notification;
250+
export default Notification;

src/pages/AdminTraineeDashboard.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ function AdminTraineeDashboard() {
403403
},
404404
fetchPolicy: 'network-only',
405405
onError: (error) => {
406+
console.log(error.message);
406407
toast.error(error.message);
407408
},
408409
});

0 commit comments

Comments
 (0)