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

[Style] 친구 모달 및 모달 내 컴포넌트 구현 #217

Merged
merged 16 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 29 additions & 11 deletions src/pages/HomePage/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { useQueryClient } from '@tanstack/react-query';

import ModalWrapper, { ModalWrapperRef } from '@/shared/components/ModalWrapper';

import useClickOutside from '@/shared/hooks/useClickOutside';

import {
useDeleteCategory,
useGetAllCategoryTask,
Expand All @@ -35,7 +37,7 @@ import BoxTodayTodo from './components/BoxTodayTodo';
import ButtonMoreFriends from './components/ButtonMoreFriends';
import ButtonUserProfile from './components/ButtonUserProfile';
import DatePicker from './components/DatePicker';
import ModalAddCategory from './components/ModalAddCategory';
import { ModalContentsCategory, ModalContentsFriends } from './components/ModalContents';
import SideBarHome from './components/SideBarHome';
import StatusDefaultHome from './components/StatusDefaultHome';

Expand All @@ -47,7 +49,10 @@ const HomePage = () => {
const todayDate = dayjs().tz('Asia/Seoul');
const formattedTodayDate = todayDate.format('YYYY-MM-DD');

const modalRef = useRef<ModalWrapperRef>(null);
const categoryModalRef = useRef<ModalWrapperRef>(null);
const friendsModalRef = useRef<ModalWrapperRef>(null);
// NOTE: backdrop이 있으면 전체영역이 modal로 잡혀서 바깥영역을 클릭해도 modal이 닫히지 않음, 따라서 아래 모달로 모달 닫기를 구현
const friendModalContentRef = useRef<HTMLDivElement>(null);

const [selectedDate, setSelectedDate] = useState(todayDate);
const { startDate, endDate } = getThisWeekRange(selectedDate);
Expand Down Expand Up @@ -82,14 +87,23 @@ const HomePage = () => {

const { targetTime } = targetTimeData?.data || 0;

const handleOpenModal = () => {
modalRef.current?.open();
const handleOpenCategoryModal = () => {
categoryModalRef.current?.open();
};

const handleOpenFriendsModal = () => {
friendsModalRef.current?.open();
};
const handleCloseFriendsModal = () => {
friendsModalRef.current?.close();
};

const queryClient = useQueryClient();

useClickOutside(friendModalContentRef, handleCloseFriendsModal);

const handleCloseModal = () => {
modalRef.current?.close();
categoryModalRef.current?.close();
queryClient.invalidateQueries({ queryKey: ['categories'] });
queryClient.invalidateQueries({ queryKey: ['msets'] });
};
Expand Down Expand Up @@ -160,7 +174,7 @@ const HomePage = () => {
</div>

<div className={`absolute right-[4.4rem] top-[5.4rem] flex gap-[0.8rem] ${addTodayTodoOverlayStyle}`}>
<button>
<button onClick={handleOpenFriendsModal}>
<FriendSettingIcon className="rounded-[1.6rem] hover:bg-gray-bg-04 active:bg-gray-bg-05" />
</button>
<button>
Expand Down Expand Up @@ -215,19 +229,19 @@ const HomePage = () => {
})}
{dailyCategoryTask.length <= 2 && (
<div className="flex flex-col">
<ButtonSVG className="flex-shrink-0" onClick={handleOpenModal}>
<ButtonSVG className="flex-shrink-0" onClick={handleOpenCategoryModal}>
<LargePlusIcon className="rounded-full bg-gray-bg-03 hover:bg-gray-bg-05" />
</ButtonSVG>
</div>
)}
</>
) : (
<StatusDefaultHome onClick={handleOpenModal} />
<StatusDefaultHome onClick={handleOpenCategoryModal} />
)}
</article>
{dailyCategoryTask.length > 2 && (
<div className="ml-[2.2rem] flex flex-col">
<ButtonSVG className="flex-shrink-0" onClick={handleOpenModal}>
<ButtonSVG className="flex-shrink-0" onClick={handleOpenCategoryModal}>
<LargePlusIcon className="rounded-full bg-gray-bg-03 hover:bg-gray-bg-05" />
</ButtonSVG>
</div>
Expand All @@ -253,8 +267,12 @@ const HomePage = () => {
</div>
</section>
</div>
<ModalWrapper ref={modalRef} backdrop={true}>
<ModalAddCategory handleCloseModal={handleCloseModal} />
<ModalWrapper ref={categoryModalRef} backdrop={true}>
<ModalContentsCategory handleCloseModal={handleCloseModal} />
</ModalWrapper>

<ModalWrapper ref={friendsModalRef} backdrop={true}>
<ModalContentsFriends ref={friendModalContentRef} />
</ModalWrapper>
</HomePageWrapper>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ interface UrlInfo {
favicon: string;
}

interface ModalAddCategoryProps {
interface ModalContentsCategoryProps {
handleCloseModal: () => void;
}

const ModalAddCategory = ({ handleCloseModal }: ModalAddCategoryProps) => {
const ModalContentsCategory = ({ handleCloseModal }: ModalContentsCategoryProps) => {
const [totalUrlInfos, setTotalUrlInfos] = useState<UrlInfo[]>([]);
const [rightModalUrlInfos, setRightModalUrlInfos] = useState<UrlInfo[]>([]);
const [isFirstUrlValidated, setIsFirstUrlValidated] = useState<boolean | null>(null);
Expand Down Expand Up @@ -331,4 +331,4 @@ const ModalAddCategory = ({ handleCloseModal }: ModalAddCategoryProps) => {
);
};

export default ModalAddCategory;
export default ModalContentsCategory;
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { forwardRef, useState } from 'react';

import FriendsList from './components/FriendsList/FriendsList';
import FriendsRequest from './components/FriendsRequest/FriendsRequest';

const ModalContentsFriends = forwardRef<HTMLDivElement>((_, ref) => {
const [activeTab, setActiveTab] = useState<string>('친구목록');

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

return (
<div
ref={ref}
className="h-[800px] w-[1300px] rounded-[14px] bg-gray-bg-03 p-[4rem] shadow-[0_3px_30px_0px_rgba(0,0,0,0.4)]"
>
<h1 className="title-bold-32 mb-[1rem] text-white">친구</h1>

<button
className={`subhead-bold-22 mr-[0.5rem] p-[1rem] ${activeTab === '친구목록' ? 'text-white' : 'text-gray-03'}`}
onClick={() => handleTabChange('친구목록')}
>
{'친구목록'}
</button>

<button
className={`subhead-bold-22 mr-[0.5rem] p-[1rem] ${activeTab === '친구요청' ? 'text-white' : 'text-gray-03'}`}
onClick={() => handleTabChange('친구요청')}
>
{'친구요청'}
</button>

{activeTab === '친구목록' ? <FriendsList /> : <FriendsRequest />}
</div>
);
});

ModalContentsFriends.displayName = 'ModalContentsFriends';

export default ModalContentsFriends;
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { userFriendData } from '@/shared/mocks/userFriendData';

import FriendInfo from './components/FriendInfo';

const FriendsList = () => {
return (
<div>
{userFriendData.length > 0 ? (
<div className="mt-[3rem] flex w-full flex-col">
<div className="body-reg-16 flex p-[1rem] text-gray-05">
<div className="w-[40rem]">사용자</div>
<div className="w-[30rem]">현재 상태</div>
<div className="w-[46rem]">오늘 몰입 시간</div>
</div>
<ul className="h-[57.5rem] overflow-scroll">
<FriendInfo friendsData={userFriendData} />
</ul>
</div>
) : (
<div className="flex h-[57rem] flex-col items-center justify-center">
<p className="title-med-32 mb-[2rem] text-gray-05">함께 몰입할 친구를 추가해보세요!</p>
<button className="subhead-bold-20 flex flex-shrink-0 items-center justify-center rounded-[0.8rem] bg-main-gra-01 px-[6.2rem] py-[2rem] text-gray-01 hover:bg-main-gra-hover active:bg-main-gra-press">
친구 추가하기
</button>
</div>
)}
</div>
);
};

export default FriendsList;
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import Dropdown from '@/shared/components/Dropdown';
import FriendUserProfile from '@/shared/components/ModalContents/FriendUserProfile';

import { formatSecondsForFriendsList } from '@/shared/utils/time';

import DeleteBtn from '@/shared/assets/svgs/friend_delBtn.svg?react';

interface FriendsData {
id: number;
name: string;
image: string;
email: string;
isPlaying: boolean;
time: number;
categoryName: string;
}

type FriendsInfoProp = {
friendsData: FriendsData[];
};

const FriendInfo = ({ friendsData }: FriendsInfoProp) => {
return (
<>
{friendsData.map((friend: FriendsData) => {
return (
<li key={friend.id} className="flex items-center border-t-[1px] border-gray-bg-06 py-[1.5rem]">
<div className="flex w-[40rem]">
<FriendUserProfile isConnecting={friend.isPlaying} imgSrc={friend.image} />
<div className="ml-[2rem] flex flex-col justify-center">
<p className="subhead-bold-20 text-white">{friend.name}</p>
<p className="body-reg-16 text-gray-04">{friend.email}</p>
</div>
</div>
<p className="subhead-med-18 flex w-[30rem] self-center p-[0.8rem] text-white">
{friend.isPlaying ? '온라인' : '오프라인'}
</p>
<p className="subhead-med-18 flex w-[46rem] self-center p-[0.8rem] text-white">
{formatSecondsForFriendsList(friend?.time)}
</p>

<Dropdown.Root>
<Dropdown.Trigger>
<button className="flex h-[24px] w-[24px] flex-col items-center justify-center">
<DeleteBtn />
</button>
</Dropdown.Trigger>
<Dropdown.Content boxShadow="shadow-none" className="right-0 top-[26px]">
<Dropdown.Item label="친구삭제" textColor="red" />
</Dropdown.Content>
</Dropdown.Root>
</li>
);
})}
</>
);
};

export default FriendInfo;
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { userFriendData } from '@/shared/mocks/userFriendData';

import ButtonRequestAction from './components/ButtonRequestAction';
import FriendsListRequested from './components/FriendsListRequested';
import InputSendRequest from './components/InputSendRequest';

const FriendsRequest = () => {
return (
<div>
<div className="relative mb-[4rem] mt-[2rem]">
<InputSendRequest />
</div>
<div className="h-[52.5rem] overflow-scroll">
<h2 className="subhead-bold-22 px-[1rem] py-[2rem] text-white">받은 요청</h2>
<FriendsListRequested friendsData={userFriendData}>
<div className="flex w-full items-center justify-end gap-[1rem]">
<ButtonRequestAction variant="positive">수락</ButtonRequestAction>
<ButtonRequestAction variant="negative">거절</ButtonRequestAction>
</div>
</FriendsListRequested>

<h2 className="subhead-bold-22 mt-[4rem] px-[1rem] py-[2rem] text-white">보낸 요청</h2>
<FriendsListRequested friendsData={userFriendData}>
<div className="flex w-full items-center justify-end">
<ButtonRequestAction variant="negative">요청 취소</ButtonRequestAction>
</div>
</FriendsListRequested>
</div>
</div>
);
};

export default FriendsRequest;
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ButtonHTMLAttributes, ReactNode } from 'react';

interface CategoryBtnProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'positive' | 'negative';
children: ReactNode;
}

const ButtonRequestAction = ({ variant, children, ...props }: CategoryBtnProps) => {
const btnVariant = {
positive: 'text-gray-bg-01 bg-mint-02 hover:bg-mint-02-hover active:bg-mint-02-press',
negative: 'text-white bg-gray-bg-06 hover:bg-gray-bg-04 active:bg-gray-bg-05',
};

const commonStyle = ' px-[2.2rem] py-[1rem] rounded-[5px] subhead-semibold-18 ';

const styledBtn = variant ? btnVariant[variant] : '';

return (
<button className={`${styledBtn} ${commonStyle} flex-grow-0`} {...props}>
{children}
</button>
);
};

export default ButtonRequestAction;
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { ReactNode } from 'react';

import FriendUserProfile from '@/shared/components/ModalContents/FriendUserProfile';

interface FriendsData {
id: number;
name: string;
image: string;
email: string;
isPlaying: boolean;
time: number;
categoryName: string;
}

type FriendsInfoProp = {
friendsData: FriendsData[];
children: ReactNode;
};

const FriendsListRequested = ({ children, friendsData }: FriendsInfoProp) => {
return (
<>
{friendsData.map((friend) => {
return (
<li key={friend.id} className="flex w-full border-t-[1px] border-gray-bg-06 px-[1rem] py-[2rem]">
<div className="flex w-[40rem]">
<FriendUserProfile imgSrc={friend.image} />
<div className="ml-[2rem] flex flex-col justify-center">
<p className="subhead-bold-20 text-white">{friend.name}</p>
<p className="body-reg-16 text-gray-04">{friend.email}</p>
</div>
</div>
{children}
</li>
);
})}
</>
);
};

export default FriendsListRequested;
Loading