• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

atlp-rwanda / hackers-ec-Fe / #1299

26 Jul 2024 09:24AM UTC coverage: 89.758% (-1.5%) from 91.283%
#1299

push

web-flow
Merge pull request #54 from atlp-rwanda/feat-admin-stat-fe

feat(admin):can view statistics of whole commetcer

832 of 1022 branches covered (81.41%)

Branch coverage included in aggregate %.

33 of 215 new or added lines in 3 files covered. (15.35%)

9 existing lines in 3 files now uncovered.

9386 of 10362 relevant lines covered (90.58%)

15.61 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.27
/src/components/notification/Notification.tsx
1
import { BellRing } from 'lucide-react';
1✔
2
import { useEffect, useRef, useState } from 'react';
1✔
3
import { FaCheckDouble } from 'react-icons/fa6';
1✔
4
import { io } from 'socket.io-client';
1✔
5
import { toast } from 'sonner';
1✔
6
import { NotificationTypes } from '../../@types/notification';
1✔
7
import { UserInfoTypes } from '../../@types/userType';
1✔
8
import useToken from '../../hooks/useToken';
1✔
9
import {
1✔
10
        addNotification,
1✔
11
        markAllRead,
1✔
12
        userNotification,
1✔
13
} from '../../redux/features/notificationSlice';
1✔
14
import { useAppDispatch, useAppSelector } from '../../redux/hooks/hooks';
1✔
15
import { notificationSound } from '../../utils/images';
1✔
16
import fetchInfo from '../../utils/userDetails';
1✔
17
import NotificationItem from '../cards/NotificationItem';
1✔
18
import { DynamicData } from '../../@types/DynamicData';
1✔
19
import NotificationSkeleton from '../cards/NotificationSkeleton';
1✔
20

1✔
21
const Notification = () => {
1✔
22
        const dispatch = useAppDispatch();
33✔
23
        const { accessToken } = useToken();
33✔
24
        const notificationPlayer = useRef<HTMLAudioElement>(null);
33✔
25
        const [notificationActive, setNotificationActive] = useState(false);
33✔
26
        const { notifications, value, isLoading } = useAppSelector(
33✔
27
                (state) => state.notifications,
33✔
28
        );
33✔
29

33✔
30
        const playAudio = () => {
33✔
31
                notificationPlayer.current?.play();
×
32
        };
×
33

33✔
34
        useEffect(() => {
33✔
35
                if (accessToken) {
18✔
36
                        const { id } = fetchInfo() as UserInfoTypes;
4✔
37
                        const socket = io(`${import.meta.env.VITE_API_APP_ROOT_URL}`, {
4✔
38
                                transports: ['websocket'],
4✔
39
                                auth: {
4✔
40
                                        token: accessToken,
4✔
41
                                },
4✔
42
                        });
4✔
43

4✔
44
                        socket.on('notifications', (data) => {
4✔
45
                                dispatch(userNotification(data));
×
46
                        });
4✔
47
                        socket.on(`notification-${id}`, (notification) => {
4✔
48
                                if (notification) {
×
49
                                        dispatch(addNotification(notification));
×
50
                                        toast.success('You have new notification 🔔');
×
51
                                        playAudio();
×
52
                                }
×
53
                        });
4✔
54

4✔
55
                        return () => {
4✔
56
                                socket.disconnect();
4✔
57
                        };
4✔
58
                }
4✔
59
        }, [accessToken, dispatch]);
33✔
60

33✔
61
        const sortedNotification = sortNotificationsByDate(
33✔
62
                notifications as unknown as NotificationTypes[],
33✔
63
        );
33✔
64

33✔
65
        const readAllNotification = async () => {
33✔
66
                try {
2✔
67
                        await dispatch(markAllRead()).unwrap();
2!
68
                } catch (e) {
2✔
69
                        const err = e as DynamicData;
1✔
70
                        toast.error(
1✔
71
                                err?.data?.message ||
1!
72
                                        err?.message ||
1!
UNCOV
73
                                        'Unknown error occurred! Please try again!',
×
74
                        );
1✔
75
                }
1✔
76
        };
2✔
77

33✔
78
        return (
33✔
79
                <div className="relative flex-center transition-colors border border-neutral-grey p-2 h-max rounded-lg cursor-pointer hover:bg-neutral-grey/20">
33✔
80
                        <BellRing
33✔
81
                                size={18}
33✔
82
                                onClick={() => setNotificationActive((prev) => !prev)}
33✔
83
                                className="cursor-pointer"
33✔
84
                                role="img"
33✔
85
                                aria-label="bell-image"
33✔
86
                        />
33✔
87
                        <audio ref={notificationPlayer} src={notificationSound} />
33✔
88
                        {value > 0 && (
33✔
89
                                <div
14✔
90
                                        className="absolute -top-4 -right-4 bg-action-error w-8 text-xs h-8 text-neutral-white rounded-full flex-center"
14✔
91
                                        aria-label="notification-number"
14✔
92
                                >
14✔
93
                                        {value <= 99 ? value : '99+'}
14!
94
                                </div>
14✔
95
                        )}
33✔
96
                        {notificationActive && (
33✔
97
                                <div
8✔
98
                                        className="absolute top-full -right-[100px] ipad:-right-[50px] mt-5 p-2 bg-neutral-white shadow-custom-heavy rounded-xl w-[80vw] ipad:w-[350px] h-max max-h-[80vh] md:max-h-[70vh] overflow-y-scroll no-scrollbar z-50"
8✔
99
                                        aria-label="notification-tab"
8✔
100
                                >
8✔
101
                                        <div className="w-full h-full overflow-hidden">
8✔
102
                                                <h2 className="text-md ipad:text-lg font-semibold text-start pl-4 pt-4">
8✔
103
                                                        Notifications
8✔
104
                                                </h2>
8✔
105
                                                <div className="flex items-center justify-end p-5">
8✔
106
                                                        <div
8✔
107
                                                                onClick={readAllNotification}
8✔
108
                                                                aria-label="mark-button"
8✔
109
                                                                className={`flex items-center gap-2 text-[11px] text-neutral-black bg-primary-lightblue/20 px-3 py-1 rounded-full hover:text-neutral-black/50 ${notifications?.filter((not) => not.unread === true).length === 0 && 'hidden'}`}
8✔
110
                                                        >
8✔
111
                                                                Mark all as read
8✔
112
                                                                <FaCheckDouble fill={'black'} />
8✔
113
                                                        </div>
8✔
114
                                                </div>
8✔
115
                                                <div className="w-full flex-1 h-[90%] no-scrollbar px-2 pb-10">
8✔
116
                                                        {isLoading ? (
8✔
117
                                                                Array.from({ length: 12 }).map((_, i) => (
3✔
118
                                                                        <NotificationSkeleton key={i} />
36✔
119
                                                                ))
3✔
120
                                                        ) : sortedNotification && sortedNotification.length > 0 ? (
5✔
121
                                                                sortedNotification.map((item) => (
4✔
122
                                                                        <NotificationItem
5✔
123
                                                                                key={item.id}
5✔
124
                                                                                id={item.id}
5✔
125
                                                                                unread={item.unread}
5✔
126
                                                                                text={item.message}
5✔
127
                                                                                date={`${item.createdAt.getHours()}:${item.createdAt.getMinutes() < 10 ? `0${item.createdAt.getMinutes()}` : item.createdAt.getMinutes()} - ${item.createdAt.getDate()}/${item.createdAt.getMonth()}`}
5!
128
                                                                        />
5✔
129
                                                                ))
4✔
130
                                                        ) : (
1✔
131
                                                                <div className="text-xs p-4 text-center">
1✔
132
                                                                        No new notification available!
1✔
133
                                                                </div>
1✔
134
                                                        )}
8✔
135
                                                </div>
8✔
136
                                        </div>
8✔
137
                                </div>
8✔
138
                        )}
33✔
139
                </div>
33✔
140
        );
33✔
141
};
33✔
142

1✔
143
function sortNotificationsByDate(
33✔
144
        noties: NotificationTypes[],
33✔
145
): NotificationTypes[] {
33✔
146
        return noties
33✔
147
                .map((notification) => ({
33✔
148
                        ...notification,
18✔
149
                        createdAt: new Date(notification.createdAt),
18✔
150
                }))
33✔
151
                .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
33✔
152
}
33✔
153

1✔
154
export default Notification;
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc