Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ToniGrbic/474 home notifications #485

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
3 changes: 3 additions & 0 deletions apps/app/src/assets/icons/arrow-left.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions apps/app/src/assets/icons/icon-bell.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 9 additions & 2 deletions apps/app/src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,27 @@ import styles from './Header.module.scss';

import { useDeviceType } from '../../hooks/UseDeviceType';
import { NotificationBell } from './NotificationBell';
import { NotificationsModal } from '../NotificationsModal';
import { HeaderCardsWrapper } from './HeaderCardsWrapper';
import { useState } from 'react';

export const Header = () => {
const { isMobile } = useDeviceType({});

const [isOpenNotifications, setOpenNotifications] = useState(false);
return (
<div className={styles.header}>
<div className={styles.headerGreeting}>
<h1>Hello, {isMobile && <br />} Mihaela! 👋🏻</h1>

{isMobile && <NotificationBell />}
{isMobile && (
<NotificationBell setOpenNotifications={setOpenNotifications} />
)}
</div>

<HeaderCardsWrapper />
{isOpenNotifications && (
<NotificationsModal setOpenNotifications={setOpenNotifications} />
)}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import sprite from './../../../assets/sprite.svg';
import sprite from '@/assets/sprite.svg';
import styles from './NotificationBell.module.scss';

export const NotificationBell = () => {
interface NotificationBellProps {
setOpenNotifications: (open: boolean) => void;
}

export const NotificationBell: React.FC<NotificationBellProps> = ({
setOpenNotifications,
}) => {
return (
<div className={styles.notificationWrapper}>
<div
className={styles.notificationWrapper}
onClick={() => setOpenNotifications(true)}>
<svg className={styles.notificationIcon} width={32} height={32}>
<use href={`${sprite}#notification-bell-icon`} />
</svg>
Expand Down
26 changes: 11 additions & 15 deletions apps/app/src/components/Notification/Notification.module.scss
Original file line number Diff line number Diff line change
@@ -1,44 +1,40 @@
.title {
color: #000;
font-style: normal;
font-weight: 500;
line-height: 20px;
@include heading-3;
font-size: 14px;
color: $black-100;
@include heading-4;
font-weight: 500;
text-transform: uppercase;
margin-bottom: 6px;
margin-top: 6px;
}

.content {
color: #868685;
font-size: 14px;
font-style: normal;
line-height: 20px;
color: $black-80;
opacity: 0.78;
margin-top: 0px;
margin-bottom: 0px;
@include paragraph-14;
text-align: left;
}

.time {
color: #b3b3b2;
color: $black-100;
opacity: 0.5;
@include label-medium;
font-size: 12px;
font-style: normal;
font-weight: 500;
line-height: 16px;
text-transform: uppercase;
margin-bottom: 8px;
margin-top: 12px;
text-align: left;
}

.flex {
display: flex;
}

.expandButton {
color: #ff482f;
text-align: right;
color: $primary-highlight;
@include label-medium;
font-size: 12px;
font-style: normal;
Expand All @@ -57,7 +53,7 @@
width: 6px;
height: 6px;
border-radius: 50%;
background-color: #ff3737;
background-color: $error-dark;
align-self: center;
}

Expand Down
4 changes: 3 additions & 1 deletion apps/app/src/components/Notification/Notification.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type NotificationProps = {
time: Date;
expandedNotificationId: number | null;
setExpandedNotificationId: (id: number | null) => void;
notificationsLength: number;
};

//TODO: function that returns time string ex: 1 h ago or 10 min ago
Expand All @@ -20,6 +21,7 @@ const Notification: React.FC<NotificationProps> = ({
time,
expandedNotificationId,
setExpandedNotificationId,
notificationsLength,
}) => {
const [showExpandButton, setShowExpandButton] = useState(false);
const isExpanded = expandedNotificationId === id;
Expand Down Expand Up @@ -70,7 +72,7 @@ const Notification: React.FC<NotificationProps> = ({
)}
</div>
<p className={c.time}>{getPassedTime(time)}</p>
<div className={c.dottedBreak}></div>
{notificationsLength !== id && <div className={c.dottedBreak}></div>}
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
.modalHeader {
display: flex;
justify-content: center;
margin-bottom: 28px;
}

.backButton {
position: absolute;
left: 1.5rem;
top: 1rem;
width: 2rem;
height: 2rem;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}

.title {
@include heading-2;
font-weight: 500;
color: black;
}

.modalWrapper {
z-index: 4;
background: rgba(0, 0, 0, 0.9);
position: fixed;
inset: 0;
}

.modalContent {
background: white;
border-top-right-radius: 12px;
border-top-left-radius: 12px;
padding: 20px;
position: absolute;
top: 64px;
bottom: 0;
width: 100%;
z-index: 5;
overflow-y: auto;
padding-bottom: 40px;
}

.notificationsContainer {
display: flex;
flex-direction: column;
gap: 2rem;
margin-top: 35px;
}

.noNotificationsContainer {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 85%;
margin: 0 30px;
}

.noNotificationsTitle {
@include heading-3;
color: $black-50;
margin-top: 1.5rem;
}

.noNotificationsLabel {
@include paragraph-14;
color: $black-50;
margin-top: 6px;
}
92 changes: 92 additions & 0 deletions apps/app/src/components/NotificationsModal/NotificationsModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { useEffect, useMemo, useState } from 'react';
import styles from './NotificationsModal.module.scss';
import ArrowLeft from '@/assets/icons/arrow-left.svg';
import IconBell from '@/assets/icons/icon-bell.svg';
import TabGroup from '../TabGroup';
import Tab from '../Tab';
import Notification from '../Notification/Notification';
import { notifications } from './notifications.const';

enum Tabs {
Sve,
Nepročitano,
}

interface NotificationsModalProps {
setOpenNotifications: (open: boolean) => void;
}

export const NotificationsModal: React.FC<NotificationsModalProps> = ({
setOpenNotifications,
}) => {
const [expandedNotificationId, setExpandedNotificationId] = useState<
number | null
>(null);
const [notificationsTab, setNotificationsTab] = useState<string | number>(
Tabs.Sve,
);
const [displayedNotifications, setDisplayedNotifications] =
useState(notifications);

const handleTabChange = (tab: string) => {
setNotificationsTab(tab);
};

const allNotifications = useMemo(() => notifications, []);
const unreadNotifications = useMemo(() => notifications.slice(0, 2), []);

useEffect(() => {
if (notificationsTab === Tabs.Sve) {
setDisplayedNotifications(allNotifications);
} else if (notificationsTab === Tabs.Nepročitano) {
setDisplayedNotifications(unreadNotifications);
}
}, [notificationsTab, allNotifications, unreadNotifications]);

return (
<div className={styles.modalWrapper}>
<div className={styles.modalContent}>
<div className={styles.modalHeader}>
<div
className={styles.backButton}
onClick={() => setOpenNotifications(false)}>
<img src={ArrowLeft} alt='back' />
</div>
<h2 className={styles.title}>NOTIFIKACIJE</h2>
</div>
{notifications.length !== 0 ? (
<>
<TabGroup setter={handleTabChange}>
<Tab id={Tabs.Sve}>Sve</Tab>
<Tab id={Tabs.Nepročitano}>Nepročitano</Tab>
</TabGroup>
<div className={styles.notificationsContainer}>
{displayedNotifications.map((notification) => (
<Notification
key={notification.id}
id={notification.id}
title={notification.title}
content={notification.content}
time={notification.time}
expandedNotificationId={expandedNotificationId}
setExpandedNotificationId={setExpandedNotificationId}
notificationsLength={displayedNotifications.length}
/>
))}
</div>
</>
) : (
<div className={styles.noNotificationsContainer}>
<img src={IconBell} alt='bell' />
<p className={styles.noNotificationsTitle}>NEMA NOTIFIKACIJA</p>
<p className={styles.noNotificationsLabel}>
Nažalost nemamo novosti za tebe, možda uskoro te zatrebamo
</p>
</div>
)}
</div>
</div>
);
};

export default NotificationsModal;
2 changes: 2 additions & 0 deletions apps/app/src/components/NotificationsModal/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import NotificationsModal from './NotificationsModal';
export { NotificationsModal };
40 changes: 40 additions & 0 deletions apps/app/src/components/NotificationsModal/notifications.const.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
type Notification = {
id: number;
title: string;
content: string;
time: Date;
};

export const notifications: Array<Notification> = [
{
id: 1,
title: 'Notification 1',
content:
'This is the first notification and it is a very long notification that will be cut off after a few lines sdaasds dadsad asdasd asd das dasd asdasdas sadasdasd',
time: new Date('2025-1-28-12:00'),
},
{
id: 2,
title: 'Notification 2',
content: 'This is the second notification',
time: new Date('2025-1-28-12:00'),
},
{
id: 3,
title: 'Notification 3',
content: 'This is the third notification',
time: new Date('2025-1-28-12:00'),
},
{
id: 4,
title: 'Notification 4',
content: 'This is the fourth notification',
time: new Date('2025-1-28-12:00'),
},
{
id: 5,
title: 'Notification 4',
content: 'This is the fourth notification',
time: new Date('2025-1-28-12:00'),
},
];
1 change: 1 addition & 0 deletions apps/app/src/styles/_colors.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ $primary-muted-orange: #e0553f;
// Grayscale
$black-100: #171615;
$black-90: #2d2c2c;
$black-80: #363636;
$black-70: #5a5959;
$black-50: #868685;
$black-30: #b3b3b2;
Expand Down