-
Notifications
You must be signed in to change notification settings - Fork 2
design: 알림페이지 퍼블리싱 #10
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
Changes from all commits
aefbdb9
1a7be8d
54f3de3
e72db2c
b283980
d5763b2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,8 @@ | ||
| import AlarmIcon from './alarm.svg?react'; | ||
| import ArrowLeftIcon from './arrow-left.svg?react'; | ||
| import BoardIcon from './board.svg?react'; | ||
| import EnvelopeIcon from './envelope.svg?react'; | ||
| import PersonIcon from './person.svg?react'; | ||
| import SirenIcon from './siren.svg?react'; | ||
|
|
||
| export { AlarmIcon, PersonIcon, ArrowLeftIcon }; | ||
| export { AlarmIcon, PersonIcon, ArrowLeftIcon, SirenIcon, EnvelopeIcon, BoardIcon }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import { Outlet } from 'react-router'; | ||
|
|
||
| import Header from './Header'; | ||
|
|
||
| const Layout = () => { | ||
| return ( | ||
| <> | ||
| <Header /> | ||
| <Outlet /> | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| export default Layout; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| import { twMerge } from 'tailwind-merge'; | ||
|
|
||
| import { NOTIFICATION_ICON } from '../constants'; | ||
|
|
||
| interface NotificationItemProps { | ||
| type: string; | ||
| message: string; | ||
| isRead: boolean; | ||
| onClick: () => void; | ||
| } | ||
|
|
||
| const NotificationItem = ({ type, message, isRead, onClick }: NotificationItemProps) => { | ||
| const Icon = NOTIFICATION_ICON[type]; | ||
|
|
||
| const handleClick = (e: React.MouseEvent<HTMLElement>) => { | ||
| e.stopPropagation(); | ||
| onClick(); | ||
| }; | ||
|
|
||
| return ( | ||
| <article | ||
| className={twMerge( | ||
| 'relative flex cursor-pointer items-center gap-3 overflow-hidden rounded-sm p-4', | ||
| type === 'warning' ? 'alarm-warning-bg' : 'alarm-default-bg', | ||
| )} | ||
| onClick={handleClick} | ||
| > | ||
| {isRead && <div className="absolute inset-0 z-10 bg-white/60" />} | ||
| <div className="absolute inset-0 bg-white/50 blur-xl" /> | ||
| <Icon className="z-0 h-6 w-6 text-white" /> | ||
| <p className="body-m text-gray-80 z-0">{message}</p> | ||
| </article> | ||
| ); | ||
| }; | ||
|
|
||
| export default NotificationItem; |
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. handleoutside 함수도 utils로 빼면 좋을 것 같아용 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| import { useEffect, useRef } from 'react'; | ||
| import { twMerge } from 'tailwind-merge'; | ||
|
|
||
| import ModalOverlay from '@/components/ModalOverlay'; | ||
|
|
||
| interface WarningModalProps { | ||
| isOpen: boolean; | ||
| onClose: () => void; | ||
| } | ||
|
|
||
| const WarningModal = ({ isOpen, onClose }: WarningModalProps) => { | ||
| const ref = useRef<HTMLElement>(null); | ||
|
|
||
| useEffect(() => { | ||
| const handleOutsideClick = (e: MouseEvent) => { | ||
| if (ref.current && !ref.current.contains(e.target as Node)) onClose(); | ||
| }; | ||
|
|
||
| if (isOpen) document.addEventListener('click', handleOutsideClick); | ||
|
|
||
| return () => { | ||
| document.removeEventListener('click', handleOutsideClick); | ||
| }; | ||
| }, [isOpen, onClose]); | ||
|
|
||
| if (!isOpen) return null; | ||
|
|
||
| return ( | ||
| <ModalOverlay> | ||
| <article | ||
| ref={ref} | ||
| className={twMerge( | ||
| 'relative w-77 overflow-hidden rounded-sm p-6', | ||
| 'bg-accent-1 bg-[url("/src/assets/images/background-overlay.png")] bg-repeat bg-blend-overlay', | ||
| )} | ||
| > | ||
| <div className="absolute inset-0 h-full w-full bg-white/90 blur-[25px]" /> | ||
| <div className="relative"> | ||
| <h2 className="body-sb mb-1.5 text-gray-100">경고 안내</h2> | ||
| <p className="caption-r mb-5 text-black"> | ||
| 따사로운 서비스 이용을 위해, 부적절하다고 판단되는 편지는 반려하고 있어요. 서로를 | ||
| 존중하는 따뜻한 공간을 만들기 위해 협조 부탁드립니다. | ||
| </p> | ||
| <h2 className="body-sb mb-1.5 text-gray-100">경고 규칙</h2> | ||
| <p className="caption-r text-black"> | ||
| 1회 경고: 주의 안내 | ||
| <br /> | ||
| 2회 경고: 7일 동안 서비스 이용 제한 | ||
| <br /> | ||
| 3회 경고: 서비스 이용 불가능 | ||
| </p> | ||
| </div> | ||
| </article> | ||
| </ModalOverlay> | ||
| ); | ||
| }; | ||
|
|
||
| export default WarningModal; |
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 우와 이런식으로도 매핑이 가능하군요! |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import { BoardIcon, EnvelopeIcon, SirenIcon } from '@/assets/icons'; | ||
|
|
||
| export const NOTIFICATION_ICON: Record< | ||
| string, | ||
| React.ComponentType<React.SVGProps<SVGSVGElement>> | ||
| > = { | ||
| letter: EnvelopeIcon, | ||
| warning: SirenIcon, | ||
| board: BoardIcon, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,55 @@ | ||
| import { useState } from 'react'; | ||
|
|
||
| import NotificationItem from './components/NotificationItem'; | ||
| import WarningModal from './components/WarningModal'; | ||
|
|
||
| const DUMMY_NOTI = [ | ||
| { id: 1, type: 'letter', message: '12E31님이 편지를 보냈습니다.', isRead: false }, | ||
| { id: 2, type: 'warning', message: '따숨님, 욕설로 인해 경고를 받으셨어요.', isRead: false }, | ||
| { id: 3, type: 'letter', message: '12E31님이 편지를 보냈습니다.', isRead: false }, | ||
| { id: 4, type: 'letter', message: '12E31님이 편지를 보냈습니다.', isRead: true }, | ||
| { id: 5, type: 'letter', message: '12E31님이 편지를 보냈습니다.', isRead: false }, | ||
| { id: 6, type: 'board', message: '12E31님과의 대화가 게시판에 공유되었어요.', isRead: false }, | ||
| { | ||
| id: 7, | ||
| type: 'board', | ||
| message: '12E31님과의 게시글에 대한 공유요청을 보냈어요.', | ||
| isRead: false, | ||
| }, | ||
| ]; | ||
|
|
||
| const NotificationsPage = () => { | ||
| return <div>NotificationsPage</div>; | ||
| const [isOpenWarningModal, setIsOpenWarningModal] = useState(false); | ||
|
|
||
| const handleClickItem = (type: string) => { | ||
| if (type === 'warning') { | ||
| setIsOpenWarningModal(true); | ||
| } | ||
| }; | ||
|
|
||
| return ( | ||
| <> | ||
| <WarningModal isOpen={isOpenWarningModal} onClose={() => setIsOpenWarningModal(false)} /> | ||
| <main className="flex grow flex-col items-center px-5 pt-4 pb-9"> | ||
| <h1 className="text-gray-60 body-b mb-10 w-fit rounded-full bg-white px-6 py-4">알림</h1> | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 제가 이거 공통 스타일로 만들어둘께용 |
||
| <button type="button" className="body-sb text-gray-60 place-self-end"> | ||
| 모두 읽음 | ||
| </button> | ||
| <ul className="mt-2 flex h-full w-full flex-col gap-2 pb-10"> | ||
| {DUMMY_NOTI.map((notification) => ( | ||
| <li key={notification.id}> | ||
| <NotificationItem | ||
| type={notification.type} | ||
| message={notification.message} | ||
| isRead={notification.isRead} | ||
| onClick={() => handleClickItem(notification.type)} | ||
| /> | ||
| </li> | ||
| ))} | ||
| </ul> | ||
| </main> | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| export default NotificationsPage; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오 이렇게 레이아웃 나눈 거 좋아요!