Skip to content

Commit

Permalink
Merge pull request #32 from JieunYume/develop/3rd-notification
Browse files Browse the repository at this point in the history
공지사항, 서비스 문의
  • Loading branch information
cla6shade authored Jan 25, 2024
2 parents 3966ea2 + fc2079e commit f72bf8a
Show file tree
Hide file tree
Showing 14 changed files with 314 additions and 40 deletions.
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon

.idea/*

Expand Down
5 changes: 5 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import RegisterCompletionPage from '@pages/register/RegisterCompletionPage.tsx';
import {clearRegisterInfo} from '@modules/registerReducer.ts';
import FindPasswordPage from '@pages/findPassword/FindPasswordPage.tsx';
import ResetPasswordPage from '@pages/findPassword/ResetPasswordPage.tsx';
import NoticePage from '@pages/notification/NotificationPage.tsx';
import NoticeDetailPage from '@pages/notification/NoticeDetailPage.tsx';

const App = () => {
const dispatch = useDispatch();
Expand Down Expand Up @@ -58,6 +60,9 @@ const App = () => {
<Route path='/find-password' element={<FindPasswordPage />}></Route>
<Route path='/reset-password' element={<ResetPasswordPage />}></Route>
<Route path='/register-complete' element={<RegisterCompletionPage />}></Route>
<Route path='/notification' element={<NoticePage />}></Route>
<Route path="/notice/:id" element={<NoticeDetailPage />}></Route>
{/* <Route path="/inquiry/:id" element={<InquiryDetailPage />}></Route> */}
</Routes>;
};

Expand Down
13 changes: 0 additions & 13 deletions src/assets/icons/Frame 427319472.svg

This file was deleted.

6 changes: 0 additions & 6 deletions src/assets/icons/Frame 427319473.svg

This file was deleted.

3 changes: 0 additions & 3 deletions src/assets/icons/Frame 427319474.svg

This file was deleted.

29 changes: 24 additions & 5 deletions src/components/footer/FooterBar.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
import MenuIcon from '@components/icon/MenuIcon.tsx';
import Constants from '@/constants';

type FooterBarProps = {
currentMenu: number
currentMenu: number,
setCurrentMenu: (menuNumber: number) => void,
}

const FooterBar = ({/* currentMenu */}: FooterBarProps) => {
return <div className='w-full h-[85px]'>
<div className='mt-[31px] w-full h-[54px] flex justify-between items-center'>

const FooterBar = ({currentMenu, setCurrentMenu}: FooterBarProps) => {
const menuNums = Constants.mainpage.menu_nums;
return <div className='w-full h-[85px] sticky'>
<div className='mt-[28px] mb-[3px] w-full h-[54px] grid grid-cols-3 cursor-pointer'>
<div className='w-full h-full flex justify-center items-center'
onClick={() => setCurrentMenu(menuNums.MENU_STYLING)}>
<MenuIcon menuType={menuNums.MENU_STYLING}
selected={currentMenu === menuNums.MENU_STYLING} />
</div>
<div className='w-full h-full flex justify-center items-center cursor-pointer'
onClick={() => setCurrentMenu(menuNums.MENU_HOME)}>
<MenuIcon menuType={menuNums.MENU_HOME}
selected={currentMenu === menuNums.MENU_HOME} />
</div>
<div className='w-full h-full flex justify-center items-center cursor-pointer'
onClick={() => setCurrentMenu(menuNums.MENU_MYPAGE)}>
<MenuIcon menuType={menuNums.MENU_MYPAGE}
selected={currentMenu === menuNums.MENU_MYPAGE} />
</div>
</div>
</div>;
};
Expand Down
55 changes: 55 additions & 0 deletions src/components/icon/MenuIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import Constants from '@/constants';
import {useEffect, useState} from 'react';

type MenuIconProps = {
menuType: number
selected: boolean,
}
const MenuIcon = ({menuType, selected}: MenuIconProps) => {
const [fillColor, setFillColor] = useState('');
useEffect(() => {
setFillColor(selected ? 'rgba(28, 28, 30, 1)' : 'rgba(229, 229, 234, 1)');
}, [selected]);

const menuNums = Constants.mainpage.menu_nums;
const getIcon = () => {
switch (menuType) {
case menuNums.MENU_HOME:
return (<svg width="31" height="30" viewBox="0 0 31 30" fill="none"
xmlns="http://www.w3.org/2000/svg">
{/* eslint-disable-next-line max-len */}
<path d="M8 23.75H11.75V17.5C11.75 17.1458 11.8698 16.849 12.1094 16.6094C12.349 16.3698 12.6458 16.25 13 16.25H18C18.3542 16.25 18.651 16.3698 18.8906 16.6094C19.1302 16.849 19.25 17.1458 19.25 17.5V23.75H23V12.5L15.5 6.875L8 12.5V23.75ZM5.5 23.75V12.5C5.5 12.1042 5.58854 11.7292 5.76562 11.375C5.94271 11.0208 6.1875 10.7292 6.5 10.5L14 4.875C14.4375 4.54167 14.9375 4.375 15.5 4.375C16.0625 4.375 16.5625 4.54167 17 4.875L24.5 10.5C24.8125 10.7292 25.0573 11.0208 25.2344 11.375C25.4115 11.7292 25.5 12.1042 25.5 12.5V23.75C25.5 24.4375 25.2552 25.026 24.7656 25.5156C24.276 26.0052 23.6875 26.25 23 26.25H18C17.6458 26.25 17.349 26.1302 17.1094 25.8906C16.8698 25.651 16.75 25.3542 16.75 25V18.75H14.25V25C14.25 25.3542 14.1302 25.651 13.8906 25.8906C13.651 26.1302 13.3542 26.25 13 26.25H8C7.3125 26.25 6.72396 26.0052 6.23438 25.5156C5.74479 25.026 5.5 24.4375 5.5 23.75Z"
fill={fillColor} />
</svg>);
case menuNums.MENU_STYLING:
return (<svg width="30" height="30" viewBox="0 0 30 30" fill="none"
xmlns="http://www.w3.org/2000/svg">
{/* eslint-disable-next-line max-len */}
<path d="M8.75 27.5V20H6.75C5.91667 20 5.20833 19.7083 4.625 19.125C4.04167 18.5417 3.75 17.8333 3.75 17C3.75 16.3958 3.91667 15.8385 4.25 15.3281C4.58333 14.8177 5.02083 14.4375 5.5625 14.1875L13.75 10.5625V9.75C13 9.47917 12.3958 9.02604 11.9375 8.39062C11.4792 7.75521 11.25 7.04167 11.25 6.25C11.25 5.20833 11.6146 4.32292 12.3438 3.59375C13.0729 2.86458 13.9583 2.5 15 2.5C16.0417 2.5 16.9271 2.86458 17.6562 3.59375C18.3854 4.32292 18.75 5.20833 18.75 6.25H16.25C16.25 5.89583 16.1302 5.59896 15.8906 5.35938C15.651 5.11979 15.3542 5 15 5C14.6458 5 14.349 5.11979 14.1094 5.35938C13.8698 5.59896 13.75 5.89583 13.75 6.25C13.75 6.60417 13.8698 6.90104 14.1094 7.14062C14.349 7.38021 14.6458 7.5 15 7.5C15.3542 7.5 15.651 7.61979 15.8906 7.85938C16.1302 8.09896 16.25 8.39583 16.25 8.75V10.5625L24.4375 14.1875C24.9792 14.4375 25.4167 14.8177 25.75 15.3281C26.0833 15.8385 26.25 16.3958 26.25 17C26.25 17.8333 25.9583 18.5417 25.375 19.125C24.7917 19.7083 24.0833 20 23.25 20H21.25V27.5H8.75ZM6.75 17.5H8.75V16.25H21.25V17.5H23.25C23.3958 17.5 23.5156 17.4479 23.6094 17.3438C23.7031 17.2396 23.75 17.1042 23.75 16.9375C23.75 16.8333 23.724 16.7448 23.6719 16.6719C23.6198 16.599 23.5417 16.5417 23.4375 16.5L15 12.75L6.5625 16.5C6.45833 16.5417 6.38021 16.599 6.32812 16.6719C6.27604 16.7448 6.25 16.8333 6.25 16.9375C6.25 17.1042 6.29688 17.2396 6.39062 17.3438C6.48438 17.4479 6.60417 17.5 6.75 17.5ZM11.25 25H18.75V18.75H11.25V25Z"
fill={fillColor} />
</svg>);
case menuNums.MENU_MYPAGE:
return (<svg width="30" height="30" viewBox="0 0 30 30" fill="none"
xmlns="http://www.w3.org/2000/svg">
<g clipPath="url(#clip0_1452_2539)">
{/* eslint-disable-next-line max-len */}
<path d="M15 7.375C16.45 7.375 17.625 8.55 17.625 10C17.625 11.45 16.45 12.625 15 12.625C13.55 12.625 12.375 11.45 12.375 10C12.375 8.55 13.55 7.375 15 7.375ZM15 18.625C18.7125 18.625 22.625 20.45 22.625 21.25V22.625H7.375V21.25C7.375 20.45 11.2875 18.625 15 18.625ZM15 5C12.2375 5 10 7.2375 10 10C10 12.7625 12.2375 15 15 15C17.7625 15 20 12.7625 20 10C20 7.2375 17.7625 5 15 5ZM15 16.25C11.6625 16.25 5 17.925 5 21.25V23.75C5 24.4375 5.5625 25 6.25 25H23.75C24.4375 25 25 24.4375 25 23.75V21.25C25 17.925 18.3375 16.25 15 16.25Z"
fill={fillColor} />
</g>
<defs>
<clipPath id="clip0_1452_2539">
<rect width="30" height="30" fill={fillColor} />
</clipPath>
</defs>
</svg>);
default:
return null;
}
};

return <>
{getIcon()}
</>;
};

export default MenuIcon;
7 changes: 3 additions & 4 deletions src/pages/MainPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,19 @@ import ResponsiveContainer from '@components/container/ResponsiveContainer.tsx';

const MainPage = () => {
const menuNums = Constants.mainpage.menu_nums;
const menuTitles = Constants.mainpage.menu_titles;
const [currentMenu/* , setCurrentMenu */] =
const [currentMenu, setCurrentMenu] =
useState(menuNums.MENU_HOME);

return (
<PageContainer>
<Navbar title={menuTitles[currentMenu]} hasNotificationButton={true}
<Navbar title={'MARICO'} hasNotificationButton={true}
containsHeadline={true}/>
<ScrollableContainer>
<ResponsiveContainer>
<div className='flex grow'/>
</ResponsiveContainer>
</ScrollableContainer>
<FooterBar currentMenu={currentMenu} />
<FooterBar currentMenu={currentMenu} setCurrentMenu={setCurrentMenu}/>
</PageContainer>
);
};
Expand Down
3 changes: 3 additions & 0 deletions src/pages/mypage/member/MemberMyPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const MemberMyPage = () => {

};
3 changes: 3 additions & 0 deletions src/pages/mypage/stylist/StylistMyPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const StylistMyPage = () => {

};
60 changes: 60 additions & 0 deletions src/pages/notification/NoticeDetailPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React, {useEffect, useState} from 'react';
import {useParams} from 'react-router-dom';
import AxiosInstance from '@utils/AxiosInstance.ts';
import Navbar from '@components/navbar/Navbar.tsx';
import ResponsiveContainer from '@components/container/ResponsiveContainer.tsx';
import ScrollableContainer from '@components/container/ScrollableContainer.tsx';
import PageContainer from '@components/container/PageContainer.tsx';

interface Notice {
id: number;
title: string;
content: string;
createDate: string;
}
interface NoticesResponse {
data: Notice;
}

const NoticeDetailPage = () => {
const {id} = useParams<{id: string}>();
const [notice, setNotice] = useState<Notice | null>(null);

useEffect(() => {
const fetchNotice = async () => {
try {
const response = await AxiosInstance
.get<NoticesResponse>(`http://43.201.242.123:8080/api/notification/notice/${id}`);
setNotice(response.data.data);
console.log(notice);
} catch (error) {
console.error('Error fetching notice:', error);
}
};
fetchNotice();
}, [id]);

return (
<PageContainer>

<Navbar title="공지사항" hasBackwardButton={true} />
<ResponsiveContainer>
<ScrollableContainer>
<div className="w-full max-w-[500px]">
{notice ? (
<div>
<h1 className='font-bold text-2xl mt-8'>{notice.title}</h1>
<p className='text-sm mt-2'>{new Date(notice.createDate).toLocaleString()}</p>
<div className='mt-10 whitespace-pre-wrap'>{notice.content}</div>
</div>
) : (
<p>Loading...</p>
)}
</div>
</ScrollableContainer>
</ResponsiveContainer>
</PageContainer>
);
};

export default NoticeDetailPage;
153 changes: 153 additions & 0 deletions src/pages/notification/NotificationPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import React, {useState, useEffect} from 'react';
import {ReactNode} from 'react';
import {Link} from 'react-router-dom';
import PageContainer from '@components/container/PageContainer.tsx';
import Navbar from '@components/navbar/Navbar.tsx';
import ScrollableContainer from '@components/container/ScrollableContainer.tsx';
import ResponsiveContainer from '@components/container/ResponsiveContainer.tsx';
import AxiosInstance from '@utils/AxiosInstance.ts';

interface Notice {
id: number;
title: string;
content: string;
createDate: Date;
isUnread: boolean;
}

interface NoticesResponse {
noticeDtoList: Notice[];
readNotice: number[];
}

interface Inquiry {
id: number;
title: string;
content: string;
createDate: string;
}

// InquiryResponse 인터페이스를 추가합니다.
interface InquiryPreviewDtoList {
inquiryPreviewDtoList: Inquiry[];
}

interface InquiryResponse {
data: InquiryPreviewDtoList
}

const NotificationPage = (): ReactNode => {
const [notices, setNotices] = useState<Notice[]>([]);
const [inquiries, setInquiries] = useState<Inquiry[]>([]);
const [tab, setTab] = useState('notice');

// 읽지 않은 공지사항 수를 상태로 관리합니다.
const [unreadNoticesCount, setUnreadNoticesCount] = useState(0);
const [unreadInquiryCount, setunreadInquiryCount] = useState(0);
const [unreadEtcCount] = useState(0);


useEffect(() => {
// 공지사항
AxiosInstance.get<NoticesResponse>('http://43.201.242.123:8080/api/notification/notice')
.then((response) => {
// 서버로부터 받은 데이터에서 'noticeDtoList'를 추출합니다.
const fetchedNotices = response.data.noticeDtoList.map((notice) => {
return {
id: notice.id,
title: notice.title,
content: notice.content,
// 서버로부터 받은 'createDate' 문자열을 `Date` 객체로 변환합니다.
createDate: new Date(notice.createDate),
isUnread: !response.data.readNotice.includes(notice.id),
} as Notice;
});
setNotices(fetchedNotices);

// 읽지 않은 공지사항의 개수를 업데이트합니다.
const unreadNotices = response.data.noticeDtoList.filter(
(notice) => !response.data.readNotice.includes(notice.id),
);
setUnreadNoticesCount(unreadNotices.length);
setNotices(fetchedNotices);
})
.catch((error) => {
// 오류 발생시 콘솔에 출력합니다.
console.error('Error fetching notices:', error);
});

// 서비스 문의
AxiosInstance.get<InquiryResponse>('http://43.201.242.123:8080/api/service/inquiry')
.then((response) => {
const fetchedInquiries = response.data.data.inquiryPreviewDtoList.map((inquiry) => {
return {
id: inquiry.id,
title: inquiry.title,
content: inquiry.content,
createDate: new Date(inquiry.createDate),
} as unknown as Inquiry;
});
setInquiries(fetchedInquiries);
})
.catch((error) => {
console.error('Error fetching inquiries:', error);
});
}, []);
return (
<PageContainer>

<Navbar title="알림" hasBackwardButton={true} />
<ResponsiveContainer>
<ScrollableContainer>
<div className="flex w-full mx-auto">
<div
className={`flex-auto text-center py-4 font-semibold
${tab === 'notice' ? 'border-b-2 border-black text-black' : 'text-gray-500'}`}
onClick={() => setTab('notice')}>
공지사항({unreadNoticesCount})</div>

<div
className={`flex-auto text-center py-4 font-semibold
${tab === 'inquiry' ? 'border-b-2 border-black text-black' : 'text-gray-500'}`}
onClick={() => setTab('inquiry')}>서비스 문의({unreadInquiryCount})</div>

<div
className={`flex-auto text-center py-4 font-semibold
${tab === 'others' ? 'border-b-2 border-black text-black' : 'text-gray-500'}`}
onClick={() => setTab('others')}>기타({unreadEtcCount})</div>
</div>

{tab === 'notice' && (
<div className="mt-8 flex flex-col w-full max-w-[500px]">
{notices.map((notice, index) => (
<div className={`py-2 ${index < notices.length - 1 ? 'border-b-2' : ''}
${notice.isUnread ? 'bg-blue-50' : ''}`} key={notice.id}>
<Link to={`/notice/${notice.id}`}>
<h2 className="font-bold text-xl mt-3">{notice.title}</h2>
<p className="text-sm mb-3">{notice.createDate.toLocaleString()}</p>
</Link>
</div>
))}
</div>
)}
{tab === 'inquiry' && (
<div className={'mt-8 flex flex-col w-full max-w-[500px]'}>
{inquiries.map((inquiry) => (
<div key={inquiry.id}>
<Link to={`/inquiry/${inquiry.id}`}>
<h2 className="font-bold text-xl mt-3">{inquiry.title}</h2>
<p>{inquiry.content}</p>
<p className="text-sm mb-3">{new Date(inquiry.createDate)
.toLocaleDateString()}</p>
</Link>
</div>
))}
</div>
)}
</ScrollableContainer>
</ResponsiveContainer>
</PageContainer>
);
};

export default NotificationPage;
Loading

0 comments on commit f72bf8a

Please sign in to comment.