navigate('/notification')}>
+
+ navigate('/notification', {
+ state: {
+ returnBackgroundTo: variant === 'Home' ? '/' : '/interest',
+ },
+ })
+ }
+ >
{headerText}
diff --git a/src/components/blocks/home/notification/NotificationItem.tsx b/src/components/blocks/home/notification/NotificationItem.tsx
new file mode 100644
index 00000000..b83517ca
--- /dev/null
+++ b/src/components/blocks/home/notification/NotificationItem.tsx
@@ -0,0 +1,84 @@
+import styled from 'styled-components';
+import { INotification } from '@lib/apis/notification/notificationDTO';
+import { ReactComponent as AlertDongil } from '@assets/icons/alertDongil.svg';
+import { ReactComponent as AlertNotice } from '@assets/icons/alertNotice.svg';
+import { ReactComponent as AlertLevel } from '@assets/icons/alertLevel.svg';
+import { ReactComponent as AlertFamily } from '@assets/icons/alertFamily.svg';
+import getTimeForToday from '@lib/utils/getTimeForToday';
+
+const icon = {
+ CHALLENGE:
,
+ NOTICE:
,
+ LEVEL:
,
+ FAMILY:
,
+};
+const category = {
+ CHALLENGE: '돈길',
+ NOTICE: '공지',
+ LEVEL: '레벨',
+ FAMILY: '가족',
+};
+
+export interface NotificationItemProps {
+ notification: INotification;
+ isRead: boolean;
+ handleListItemClick: () => void;
+}
+
+const NotificationItem = ({
+ notification,
+ isRead,
+ handleListItemClick,
+}: NotificationItemProps) => {
+ const { id, notificationCategory, createdAt, title, message } = notification;
+ return (
+
+ {icon[notificationCategory]}
+
+
+
{category[notificationCategory]}
+
{getTimeForToday(createdAt)}
+
+
+
+
+ );
+};
+
+export default NotificationItem;
+
+const Wrapper = styled.div<{ isRead: boolean }>`
+ display: grid;
+ grid-template-columns: 48px auto;
+ grid-gap: 16px;
+ padding: 16px 18px 20px 18px;
+ background-color: ${({ isRead, theme }) =>
+ isRead ? 'none' : theme.palette.main.yellow100};
+ cursor: pointer;
+ .sub {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 8px;
+ & > p:first-child {
+ ${({ theme }) => theme.typo.text.T_12_EB}
+ color: ${({ theme }) => theme.palette.greyScale.grey500};
+ }
+ & > p:last-child {
+ ${({ theme }) => theme.typo.text.Alarm_T_12_R}
+ color: ${({ theme }) => theme.palette.greyScale.grey500};
+ }
+ }
+
+ .main {
+ ${({ theme }) => theme.typo.text.Alarm_T_14_R}
+ line-height: 150%;
+ color: ${({ theme }) => theme.palette.greyScale.black};
+ }
+
+ svg {
+ margin: auto 0px;
+ }
+`;
diff --git a/src/components/blocks/home/notification/NotificationList.tsx b/src/components/blocks/home/notification/NotificationList.tsx
new file mode 100644
index 00000000..0434e64a
--- /dev/null
+++ b/src/components/blocks/home/notification/NotificationList.tsx
@@ -0,0 +1,63 @@
+import { useCallback, useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import NotificationItem, { NotificationItemProps } from './NotificationItem';
+import { INotification } from '@lib/apis/notification/notificationDTO';
+import notificationAPI from '@lib/apis/notification/notificationAPI';
+
+interface NotificationListProps {
+ notifications: INotification[];
+}
+
+const NotificationList = ({ notifications }: NotificationListProps) => {
+ return (
+ <>
+ {notifications.map((notification) => (
+
+ {({ notification, handleListItemClick, isRead }) => (
+
+ )}
+
+ ))}
+ >
+ );
+};
+
+export default NotificationList;
+
+interface NotificationListHeadlessProps {
+ notification: INotification;
+ children: (args: NotificationItemProps) => JSX.Element;
+}
+
+const NotificationListHeadless = ({
+ notification,
+ children,
+}: NotificationListHeadlessProps) => {
+ const { isRead, id, linkUrl } = notification;
+ const [isReadClient, setIsReadClient] = useState
(isRead);
+ const navigate = useNavigate();
+
+ const onNotificationItemClick = useCallback(
+ async (id: number, url: string) => {
+ if (!isReadClient) {
+ await notificationAPI.patchNotificationIsRead(id);
+ setIsReadClient(true);
+ }
+ url && navigate(url);
+ },
+ [isReadClient],
+ );
+
+ return children({
+ notification: notification,
+ handleListItemClick: () => onNotificationItemClick(id, linkUrl),
+ isRead: isReadClient,
+ });
+};
diff --git a/src/components/pages/Home/Create.tsx b/src/components/pages/Home/Create.tsx
index 6604c7e1..139eb190 100644
--- a/src/components/pages/Home/Create.tsx
+++ b/src/components/pages/Home/Create.tsx
@@ -118,16 +118,16 @@ function Create() {
step={step}
skipSelectParents={status === 'success' && isAlone ? true : false}
/>
-
-
-
- {status === 'success' && isAlone
- ? title[step]
- : title[step - 1]}
- {renderContent(step)}
-
-
-
+ {/* */}
+
+
+ {status === 'success' && isAlone
+ ? title[step]
+ : title[step - 1]}
+ {renderContent(step)}
+
+
+ {/* */}
>
)}
diff --git a/src/components/pages/Home/Notification.tsx b/src/components/pages/Home/Notification.tsx
index 7f96ab5b..d10692bd 100644
--- a/src/components/pages/Home/Notification.tsx
+++ b/src/components/pages/Home/Notification.tsx
@@ -2,7 +2,7 @@ import styled from 'styled-components';
import ForegroundTemplate from '@components/atoms/layout/ForegroundTemplate';
import { ReactComponent as Banki } from '@assets/icons/giveUpExceeded.svg';
import useInfiniteNotificationQuery from '@lib/hooks/queries/useInfiniteNotificationQuery';
-import NotificationList from '@components/blocks/home/NotificationList';
+import NotificationList from '@components/blocks/home/notification/NotificationList';
import LoadingSpinner from '@components/atoms/loaders/LoadingSpinner';
const Notification = () => {
diff --git a/src/lib/hooks/globalErrorHandler/useAPIError.tsx b/src/lib/hooks/globalErrorHandler/useAPIError.tsx
index 0275149d..dbdd6e42 100644
--- a/src/lib/hooks/globalErrorHandler/useAPIError.tsx
+++ b/src/lib/hooks/globalErrorHandler/useAPIError.tsx
@@ -58,7 +58,7 @@ const handle401default = () => {
};
const handle500default = () => {
toast.error(
- '예상치 못한 서버 오류가 발생했어요. 오류가 반복되는 경우 앱을 종료하고 다시 실행해주세요.',
+ '예상치 못한 서버 오류가 발생했어요. 오류가 반복되면 앱을 종료하고 다시 실행해주세요.',
);
};
diff --git a/src/lib/utils/getTimeForToday.ts b/src/lib/utils/getTimeForToday.ts
index a9bbf706..15c8aa91 100644
--- a/src/lib/utils/getTimeForToday.ts
+++ b/src/lib/utils/getTimeForToday.ts
@@ -4,6 +4,7 @@ const getTimeForToday = (date: string) => {
const betweenTime = Math.floor(
(today.getTime() - timeValue.getTime()) / 1000 / 60,
);
+ console.log(date, timeValue.getTime());
if (betweenTime < 1) return '방금 전';
if (betweenTime < 60) {
diff --git a/yarn.lock b/yarn.lock
index a8e297f6..c4f77144 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3708,6 +3708,11 @@
dependencies:
"@types/jest" "*"
+"@types/throttle-debounce@^5.0.0":
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/@types/throttle-debounce/-/throttle-debounce-5.0.0.tgz#8208087f0af85107bcc681c50fa837fc9505483e"
+ integrity sha512-Pb7k35iCGFcGPECoNE4DYp3Oyf2xcTd3FbFQxXUI9hEYKUl6YX+KLf7HrBmgVcD05nl50LIH6i+80js4iYmWbw==
+
"@types/trusted-types@^2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756"
@@ -14487,6 +14492,11 @@ throat@^6.0.1:
resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375"
integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==
+throttle-debounce@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-5.0.0.tgz#a17a4039e82a2ed38a5e7268e4132d6960d41933"
+ integrity sha512-2iQTSgkkc1Zyk0MeVrt/3BvuOXYPl/R8Z0U2xxo9rjwNciaHDG3R+Lm6dh4EeUci49DanvBnuqI6jshoQQRGEg==
+
through2@^2.0.0:
version "2.0.5"
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"