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

[Refactor] 모달 띄우는 로직, 데이터 상태 로직 리팩토링.. #197

Open
wants to merge 19 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
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
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Outlet, useNavigate } from 'react-router-dom';
import { AxiosError } from 'axios';

import ErrorBoundary from '@/common/component/ErrorBoundary/ErrorBoundary';
import ModalContainer from '@/common/component/Modal/ModalContainer';
import { theme } from '@/common/style/theme/theme';

import LeftSidebar from '@/shared/component/LeftSidebar/LeftSidebar';
Expand Down Expand Up @@ -44,6 +45,7 @@ const App = () => {
<ErrorBoundary fallback={ErrorPage} onReset={handleResetError}>
<Login>
<div css={containerStyle}>
<ModalContainer />
<LeftSidebar />
<main css={layoutStyle}>
<Outlet />
Expand Down
4 changes: 2 additions & 2 deletions src/common/component/Modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */

/* eslint-disable jsx-a11y/click-events-have-key-events */
import { ReactElement, useCallback, useEffect } from 'react';
import { ReactNode, useCallback, useEffect } from 'react';
import { createPortal } from 'react-dom';

import { backgroundStyle, dialogStyle } from '@/common/component/Modal/Modal.style';

interface ModalProps {
isOpen: boolean;
children?: ReactElement;
children: ReactNode;
onClose?: () => void;
}

Expand Down
23 changes: 23 additions & 0 deletions src/common/component/Modal/ModalContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Modal from '@/common/component/Modal/Modal';

import { BlockProvider } from '@/shared/hook/common/useBlockContext';
import { WorkSpaceProvider } from '@/shared/hook/common/useWorkSpaceContext';
import { useCloseModal, useModalContent, useModalIsOpen } from '@/shared/store/modal';

const ModalContainer = () => {
const isOpen = useModalIsOpen();
const content = useModalContent();
const closeModal = useCloseModal();

if (!isOpen || !content) return null;

return (
<Modal isOpen={isOpen} onClose={closeModal}>
<WorkSpaceProvider>
<BlockProvider>{content}</BlockProvider>
</WorkSpaceProvider>
</Modal>
);
};

export default ModalContainer;
43 changes: 22 additions & 21 deletions src/page/archiving/createTimeBlock/component/Block/BlockModal.tsx
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

추가적으로 폴더명이 동사 + 목적어 형태의 문장이 되어버리니까 좀 어색한 느낌이 드는 것 같아요.

createTimeBlock이나 createWorkSpaceModal 둘다 timeBlockModal 형태로 바꾸는 것은 어떤가요 ?

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import BlockDate from '@/page/archiving/createTimeBlock/component/Block/Date/Blo
import BlockIcon from '@/page/archiving/createTimeBlock/component/Block/Icon/BlockIcon';
import BlockBox from '@/page/archiving/createTimeBlock/component/Box/BlockBox';
import { BLOCK_ICON } from '@/page/archiving/createTimeBlock/constant/iconBlock';
import { BlockData } from '@/page/archiving/createTimeBlock/type/blockType';

import { useState } from 'react';

Expand All @@ -12,35 +11,37 @@ import Flex from '@/common/component/Flex/Flex';
import Input from '@/common/component/Input/Input';
import Text from '@/common/component/Text/Text';

import WorkSapceInfo from '@/shared/component/createWorkSpace/info/WorkSpaceInfo';
import WorkSapceInfo from '@/shared/component/createWorkSpaceModal/info/WorkSpaceInfo';
import { useBlockContext } from '@/shared/hook/common/useBlockContext';

interface BlockModalProps {
onNext: (blockData: BlockData) => void;
}

const BlockModal = ({ onNext }: BlockModalProps) => {
const [blockName, setBlockName] = useState('');
const BlockModal = () => {
const [selectedIcon, setSelectedIcon] = useState<number>(-1);
const [dates, setDates] = useState({ startDate: '', endDate: '' });
const [isDateRangeValid, setIsDateRangeValid] = useState(false);

const { formData, setFormData, nextStep } = useBlockContext();

const handleBlockNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.value.length <= 25) {
setBlockName(e.target.value);
setFormData({ blockName: e.target.value });
}
};

const isButtonActive =
blockName.trim() !== '' &&
formData.blockName.trim() !== '' &&
selectedIcon !== -1 &&
dates.startDate.length === 10 &&
dates.endDate.length === 10 &&
formData.startDate.length === 10 &&
formData.endDate.length === 10 &&
isDateRangeValid;

const handleNext = () => {
if (isButtonActive) {
const blockType = BLOCK_ICON[selectedIcon].name;
onNext({ blockName, dates, blockType });
const blockIconType = BLOCK_ICON[selectedIcon].name;
setFormData({
blockType: blockIconType,
startDate: formData.startDate,
endDate: formData.endDate,
});
nextStep();
}
};

Expand Down Expand Up @@ -74,22 +75,22 @@ const BlockModal = ({ onNext }: BlockModalProps) => {
size="large"
placeholder="활동,행사명 등"
css={{ width: '100%' }}
value={blockName}
value={formData.blockName}
onChange={handleBlockNameChange}
/>
<Text tag="body7" css={textStyle}>
{blockName.length} / 25
{formData.blockName.length} / 25
</Text>
</Flex>
</BlockBox>

<BlockBox title="기간">
<Flex styles={{ align: 'flex-start', direction: 'column', padding: '0', width: '100%' }}>
<BlockDate
startDate={dates.startDate}
endDate={dates.endDate}
onSetStartDate={(date) => setDates((prev) => ({ ...prev, startDate: date as string }))}
onSetEndDate={(date) => setDates((prev) => ({ ...prev, endDate: date as string }))}
startDate={formData.startDate}
endDate={formData.endDate}
onSetStartDate={(date) => setFormData({ startDate: date })}
onSetEndDate={(date) => setFormData({ endDate: date })}
onSetIsDateRangeValid={setIsDateRangeValid}
/>
</Flex>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ import SupportingText from '@/common/component/SupportingText/SupportingText';
interface BlockDateProps {
startDate: string;
endDate: string;
onSetStartDate: (date: string | ((prev: string) => string)) => void;
onSetEndDate: (date: string | ((prev: string) => string)) => void;
onSetStartDate: (date: string) => void;
onSetEndDate: (date: string) => void;
onSetIsDateRangeValid: (isValid: boolean) => void;
}

const BlockDate = ({ startDate, endDate, onSetStartDate, onSetEndDate, onSetIsDateRangeValid }: BlockDateProps) => {
const { dates, validation, handleChange } = useDateRange(
startDate,
endDate,
onSetStartDate,
onSetEndDate,
(date: string) => onSetStartDate(date),
(date: string) => onSetEndDate(date),
onSetIsDateRangeValid
);

Expand Down
31 changes: 16 additions & 15 deletions src/page/archiving/createTimeBlock/component/Upload/UploadModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import BlockItem from '@/page/archiving/createTimeBlock/component/Upload/File/Li
import { flexStyle, scrollStyle } from '@/page/archiving/createTimeBlock/component/Upload/UploadModal.style';
import { useDeleteFileMutation } from '@/page/archiving/createTimeBlock/hook/api/useDeleteFileMutation';
import { usePostTimeBlockMutation } from '@/page/archiving/createTimeBlock/hook/api/usePostTimeBlockMutation';
import { BlockData } from '@/page/archiving/createTimeBlock/type/blockType';
import { formatDatePost } from '@/page/archiving/createTimeBlock/util/date';
import { getRandomColor } from '@/page/archiving/index/util/color';

Expand All @@ -13,23 +12,24 @@ import Button from '@/common/component/Button/Button';
import Flex from '@/common/component/Flex/Flex';

import { Files } from '@/shared/api/time-blocks/team/time-block/type';
import WorkSapceInfo from '@/shared/component/createWorkSpace/info/WorkSpaceInfo';
import WorkSapceInfo from '@/shared/component/createWorkSpaceModal/info/WorkSpaceInfo';
import { useBlockContext } from '@/shared/hook/common/useBlockContext';
import { useModalStore } from '@/shared/store/modal';
import { useTeamStore } from '@/shared/store/team';
import { useToastStore } from '@/shared/store/toast';

interface UploadModalProps {
onClose: () => void;
teamId: number;
type: string;
blockData: BlockData;
}
const UploadModal = () => {
const { teamId } = useTeamStore();

const { formData, reset } = useBlockContext();
const { closeModal } = useModalStore();

const UploadModal = ({ onClose, teamId, type, blockData }: UploadModalProps) => {
const [files, setFiles] = useState<File[]>([]);
const [fileUrls, setFileUrls] = useState<Files>({});
const [uploadStatus, setUploadStatus] = useState<{ [key: string]: boolean }>({});
const [isAllUploaded, setIsAllUploaded] = useState(true);

const { mutate: timeBlockMutate } = usePostTimeBlockMutation(teamId, type);
const { mutate: timeBlockMutate } = usePostTimeBlockMutation(+teamId, 'executive');
const { mutate: fileDeleteMutate } = useDeleteFileMutation();
const { createToast } = useToastStore();

Expand Down Expand Up @@ -77,19 +77,20 @@ const UploadModal = ({ onClose, teamId, type, blockData }: UploadModalProps) =>
};

const data = {
name: blockData.blockName,
name: formData.blockName,
color: getRandomColor(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요기 서버에서 컬러색상코드를 전달해주니까 그 값을 넘겨주는 방향으로 수정 부탁드립니다 ㅎㅎ

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수정 완료 했습니다!

startDate: formatDatePost(blockData.dates.startDate),
endDate: formatDatePost(blockData.dates.endDate),
blockType: blockData.blockType,
startDate: formatDatePost(formData.startDate),
endDate: formatDatePost(formData.endDate),
blockType: formData.blockType,
files: fileUrls,
};

const handleSave = () => {
timeBlockMutate(data, {
onSuccess: () => {
onClose();
createToast('활동 블록이 생성되었습니다', 'success');
closeModal();
reset();
},
});
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { useState } from 'react';
const useDateRange = (
initialStartDate = '',
initialEndDate = '',
onSetStartDate: (date: string | ((prev: string) => string)) => void,
onSetEndDate: (date: string | ((prev: string) => string)) => void,
onSetStartDate: (date: string) => void,
onSetEndDate: (date: string) => void,
onSetIsDateRangeValid: (isValid: boolean) => void
) => {
const [dates, setDates] = useState({ startDate: initialStartDate, endDate: initialEndDate });
Expand Down
26 changes: 8 additions & 18 deletions src/page/archiving/index/ArchivingPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import BlockModal from '@/page/archiving/createTimeBlock/component/Block/BlockModal';
import UploadModal from '@/page/archiving/createTimeBlock/component/Upload/UploadModal';
import { buttonStyle, contentStyle, daySectionStyle, timelineStyle } from '@/page/archiving/index/ArchivingPage.style';
import DaySection from '@/page/archiving/index/component/DaySection/DaySection';
import DocumentBar from '@/page/archiving/index/component/DocumentBar/DocumentBar';
Expand All @@ -16,10 +14,11 @@ import { useState } from 'react';
import AddIc from '@/common/asset/svg/add_btn.svg?react';
import Button from '@/common/component/Button/Button';
import Flex from '@/common/component/Flex/Flex';
import Modal from '@/common/component/Modal/Modal';
import { useModal, useOutsideClick } from '@/common/hook';
import { useOutsideClick } from '@/common/hook';
import { theme } from '@/common/style/theme/theme';

import { BlockFlow } from '@/shared/component/ModalFlow/BlockFlow';
import { useOpenModal } from '@/shared/store/modal';
import { useTeamStore } from '@/shared/store/team';

const ArchivingPage = () => {
Expand Down Expand Up @@ -66,15 +65,10 @@ const ArchivingPage = () => {
const blockFloors = alignBlocks(timeBlocks, endDay, selectedMonth, currentYear);

// 블록 생성 모달 관련 코드
const { isOpen, openModal, closeModal, setCurrentContent, currentContent } = useModal();

const handleNext = (blockData: {
blockName: string;
blockType: string;
dates: { startDate: string; endDate: string };
}) => {
const type = 'executive';
setCurrentContent(<UploadModal onClose={closeModal} teamId={+teamId} type={type} blockData={blockData} />);
const openModal = useOpenModal();

const handleOpenBlockModal = () => {
openModal(<BlockFlow />);
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기서 든 생각은, 제가 처음에 얘기했다시피 전역 상태로 content에 관한 컴포넌트를 가지고 있으면 더 편할 것 같다.. 라고 생각이 들긴 했습니다.

만약 그렇게 된다면, 호출하는 컴포넌트에서는 모달의 content 타입에 맞는 string 값을 호출하기만 하면 되기 때문이라고 생각해요.
뭔가 지금은 직접 openModal을 통해 BlockFlow라는 컴포넌트를 import 해와서 호출하고 있으니 결합도가 높다는 느낌을 받았어요.

openModal('create-block');

과 같이 컨텐츠 타입에 관한 특정 인자만 호출해준다면, 전역 상태의 content가 수정되고 이에 따라 ModalContainer 에서의 렌더링되는 플로우 컴포넌트가 바뀌는 과정으로 구현 된다면 더 좋을 것 같다고 생각이 드네요 ..


return (
Expand Down Expand Up @@ -148,17 +142,13 @@ const ArchivingPage = () => {
</div>
</Flex>
<Flex css={{ zIndex: theme.zIndex.overlayTop, marginLeft: 'auto' }}>
<Button
variant="action"
css={buttonStyle(selectedBlock)}
onClick={() => openModal(<BlockModal onNext={handleNext} />)}>
<Button variant="action" css={buttonStyle(selectedBlock)} onClick={handleOpenBlockModal}>
<AddIc width={24} height={24} />
블록 생성
</Button>
</Flex>
</section>

<Modal isOpen={isOpen} children={currentContent} onClose={closeModal} />
<DocumentBar
selectedBlock={selectedBlock}
ref={sideBarRef}
Expand Down
46 changes: 22 additions & 24 deletions src/page/archiving/index/component/DocumentItem/DocumentItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ import { ReactNode } from 'react';
import Download from '@/common/asset/svg/download.svg?react';
import TrashBox from '@/common/asset/svg/trash_box.svg?react';
import Flex from '@/common/component/Flex/Flex';
import Modal from '@/common/component/Modal/Modal';
import Text from '@/common/component/Text/Text';
import { useModal } from '@/common/hook';

import DeleteModal from '@/shared/component/DeleteModal/DeleteModal';
import { useOpenModal } from '@/shared/store/modal';
import { useTeamStore } from '@/shared/store/team';

interface DocumentItemProps {
Expand All @@ -28,12 +27,12 @@ interface DocumentItemProps {
}

const DocumentItem = ({ documentId, children, selectedId, blockName, fileUrl, color }: DocumentItemProps) => {
const { isOpen, openModal, closeModal, currentContent } = useModal();

const fileName = children?.toString();

const { teamId } = useTeamStore();

const openModal = useOpenModal();

//문서 클릭시 띄워주는 함수
const onClickDocumentItem = () => {
window.open(fileUrl);
Expand All @@ -46,30 +45,29 @@ const DocumentItem = ({ documentId, children, selectedId, blockName, fileUrl, co

const handleTrashClick = (e: React.MouseEvent<SVGSVGElement, MouseEvent>) => {
e.stopPropagation();
openModal(<DeleteModal title="docs" detail="docs" onClose={closeModal} teamId={+teamId} id={documentId} />);
// 모달 띄우기
const modalContent = <DeleteModal title="docs" detail="docs" teamId={+teamId} id={documentId} />;
openModal(modalContent);
};

return (
<>
{/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}
<li css={containerStyle(selectedId)} onClick={onClickDocumentItem}>
{color && (
<div>
<Text tag="body8" css={blockNameTextStyle(color)}>
{blockName}
</Text>
</div>
)}
<Flex>
<Text tag="body6" css={fileNameStyle}>
{fileName}
/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */
<li css={containerStyle(selectedId)} onClick={onClickDocumentItem}>
{color && (
<div>
<Text tag="body8" css={blockNameTextStyle(color)}>
{blockName}
</Text>
<Download width={20} height={20} css={{ cursor: 'pointer' }} onClick={handleDownloadClick} />
<TrashBox width={20} height={20} onClick={(e) => handleTrashClick(e)} css={{ cursor: 'pointer' }} />
</Flex>
</li>
<Modal isOpen={isOpen} children={currentContent} onClose={closeModal} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 요 modal 컴포넌트가 굉장히 애매했는데 이제 없앨수 있군요!
그러면 지금

  • 태그와 태그를 감싸고 있던 빈 <> 태그를 지워도 되겠네요~~!!

  • Copy link
    Contributor Author

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    맞아요! 빈태그도 지워줬습니다!!

    </>
    </div>
    )}
    <Flex>
    <Text tag="body6" css={fileNameStyle}>
    {fileName}
    </Text>
    <Download width={20} height={20} css={{ cursor: 'pointer' }} onClick={handleDownloadClick} />
    <TrashBox width={20} height={20} onClick={(e) => handleTrashClick(e)} css={{ cursor: 'pointer' }} />
    </Flex>
    </li>
    );
    };
    export default DocumentItem;
    Loading
    Loading