diff --git a/apps/client/src/page/archiving/index/component/TimeBlockBar/UploadedDocumentss/index.tsx b/apps/client/src/page/archiving/index/component/TimeBlockBar/UploadedDocumentss/index.tsx index 787c32f92..2c7efa9b1 100644 --- a/apps/client/src/page/archiving/index/component/TimeBlockBar/UploadedDocumentss/index.tsx +++ b/apps/client/src/page/archiving/index/component/TimeBlockBar/UploadedDocumentss/index.tsx @@ -5,6 +5,7 @@ import { listHeaderStyle } from '@/page/archiving/index/component/TimeBlockBar/T import FileItem from '@/page/archiving/index/component/TimeBlockBar/UploadedDocumentss/FileItem/FileItem'; import { useBlockDetailInfoQuery } from '@/page/archiving/index/hook/api/quries'; +import { useOpenModal } from '@/shared/store/modal'; import { useTimeBlockId } from '@/shared/store/timeBlockId'; interface UploadedDocumentsProps { @@ -14,6 +15,8 @@ interface UploadedDocumentsProps { const UploadedDocuments = ({ isEditable }: UploadedDocumentsProps) => { const timeBlockId = useTimeBlockId(); + const openModal = useOpenModal(); + const { data } = useBlockDetailInfoQuery(timeBlockId); return ( @@ -25,7 +28,7 @@ const UploadedDocuments = ({ isEditable }: UploadedDocumentsProps) => { {isEditable && ( - diff --git a/apps/client/src/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/FileHeader/BrowseFileHeader.style.ts b/apps/client/src/shared/component/BrowseFileHeader/BrowseFileHeader.style.ts similarity index 100% rename from apps/client/src/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/FileHeader/BrowseFileHeader.style.ts rename to apps/client/src/shared/component/BrowseFileHeader/BrowseFileHeader.style.ts diff --git a/apps/client/src/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/FileHeader/BrowseFileHeader.tsx b/apps/client/src/shared/component/BrowseFileHeader/BrowseFileHeader.tsx similarity index 68% rename from apps/client/src/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/FileHeader/BrowseFileHeader.tsx rename to apps/client/src/shared/component/BrowseFileHeader/BrowseFileHeader.tsx index 93394a68b..01760033e 100644 --- a/apps/client/src/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/FileHeader/BrowseFileHeader.tsx +++ b/apps/client/src/shared/component/BrowseFileHeader/BrowseFileHeader.tsx @@ -1,9 +1,6 @@ import { Text } from '@tiki/ui'; -import { - headerStyle, - rightSideRowStyle, -} from '@/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/FileHeader/BrowseFileHeader.style'; +import { headerStyle, rightSideRowStyle } from '@/shared/component/BrowseFileHeader/BrowseFileHeader.style'; const BrowseFileHeader = () => { return ( diff --git a/apps/client/src/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/BrowseFileItem.style.ts b/apps/client/src/shared/component/BrowseFileItem/BrowseFileItem.style.ts similarity index 75% rename from apps/client/src/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/BrowseFileItem.style.ts rename to apps/client/src/shared/component/BrowseFileItem/BrowseFileItem.style.ts index c0d9799ff..d68c1983a 100644 --- a/apps/client/src/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/BrowseFileItem.style.ts +++ b/apps/client/src/shared/component/BrowseFileItem/BrowseFileItem.style.ts @@ -6,7 +6,7 @@ import { ellipsisStyle } from '@/common/style/ellipsis'; export const containerStyle = (isSelected: boolean) => css({ display: 'grid', - gridTemplateColumns: '0.55fr 0.45fr', + gridTemplateColumns: '0.55fr 0.46fr', alignItems: 'center', width: '100%', @@ -39,7 +39,6 @@ export const timeStyle = css({ display: 'flex', alignItems: 'center', - justifyContent: 'center', gap: '2rem', @@ -48,7 +47,22 @@ export const timeStyle = css({ export const rightSideRowStyle = css({ display: 'grid', - gridTemplateColumns: '3fr 2fr 10fr', + + width: '100%', + + gridTemplateColumns: '10fr 22fr', alignItems: 'center', + gap: '0.2rem', }); + +export const iconStyle = (isSelected: boolean) => + css({ + display: 'flex', + + marginLeft: 'auto', + + flexShrink: '0', + + visibility: isSelected ? 'visible' : 'hidden', + }); diff --git a/apps/client/src/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/BrowseFileItem.tsx b/apps/client/src/shared/component/BrowseFileItem/BrowseFileItem.tsx similarity index 75% rename from apps/client/src/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/BrowseFileItem.tsx rename to apps/client/src/shared/component/BrowseFileItem/BrowseFileItem.tsx index 361108769..781feb448 100644 --- a/apps/client/src/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/BrowseFileItem.tsx +++ b/apps/client/src/shared/component/BrowseFileItem/BrowseFileItem.tsx @@ -6,11 +6,12 @@ import { KeyboardEvent } from 'react'; import { components } from '@/shared/__generated__/schema'; import { containerStyle, + iconStyle, rightSideRowStyle, timeStyle, -} from '@/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/BrowseFileItem.style'; +} from '@/shared/component/BrowseFileItem/BrowseFileItem.style'; import { getFormattedDate } from '@/shared/util/date'; -import { extractFileExtension, extractFileName } from '@/shared/util/file'; +import { extractFileName } from '@/shared/util/file'; type DocumentItem = components['schemas']['DocumentGetResponse']; @@ -33,17 +34,10 @@ const BrowseFileItem = ({ documentId, name, createdTime, url, isSelected, onSele {extractFileName(name)}
- {extractFileExtension(name)} {url?.split('.').at(-1)}
diff --git a/apps/client/src/shared/component/BrowseFileModal/BrowseFileModal.style.ts b/apps/client/src/shared/component/BrowseFileModal/BrowseFileModal.style.ts new file mode 100644 index 000000000..e69de29bb diff --git a/apps/client/src/shared/component/BrowseFileModal/BrowseFileModal.tsx b/apps/client/src/shared/component/BrowseFileModal/BrowseFileModal.tsx new file mode 100644 index 000000000..f298f55f5 --- /dev/null +++ b/apps/client/src/shared/component/BrowseFileModal/BrowseFileModal.tsx @@ -0,0 +1,101 @@ +import { IcSearch } from '@tiki/icon'; +import { Button, Flex, Input } from '@tiki/ui'; +import { useDebounce } from '@tiki/utils'; + +import { useState } from 'react'; + +import { components } from '@/shared/__generated__/schema'; +import BrowseFileHeader from '@/shared/component/BrowseFileHeader/BrowseFileHeader'; +import BrowseFileItem from '@/shared/component/BrowseFileItem/BrowseFileItem'; +import { Modal } from '@/shared/component/Modal'; +import { scrollContainerStyle } from '@/shared/component/TimeBlockModal/component/UploadModal/UploadModal.style'; +import { useFunnel } from '@/shared/hook/common/useFunnel'; +import { useCloseModal, useModalIsOpen } from '@/shared/store/modal'; + +type DocumentDetail = components['schemas']['DocumentInfoGetResponse']; + +interface BrowseFileProps { + files: DocumentDetail[]; + selectedFiles: DocumentDetail[]; + onShowBlockAdd: () => void; + onConfirmFile: (selectedFiles: DocumentDetail[]) => void; +} + +const BrowseFileModal = ({ files, onShowBlockAdd, onConfirmFile }: BrowseFileProps) => { + const [selectedFiles, setSelectedFiles] = useState([]); + const [searchFile, setSearchFile] = useState(''); + + const isOpen = useModalIsOpen(); + const closeModal = useCloseModal(); + const { nextStep } = useFunnel(); + + const isButtonActive = selectedFiles.length !== 0; + const filterKeyword = useDebounce(searchFile, 500); + + const filteredFiles = files.filter((file) => file.name.normalize('NFC').includes(filterKeyword.normalize('NFC'))); + + const handleFileSelect = (file: DocumentDetail) => { + setSelectedFiles((prevSelectedFiles) => { + const isSelected = prevSelectedFiles.some((selectedFile) => selectedFile.documentId === file.documentId); + + const updatedFiles = isSelected + ? prevSelectedFiles.filter((selectedFile) => selectedFile.documentId !== file.documentId) + : [...prevSelectedFiles, file]; + + onConfirmFile(updatedFiles); + return updatedFiles; + }); + }; + + const handleNext = () => { + nextStep(); + }; + + return ( + + + + + + } + onChange={(e) => setSearchFile(e.target.value)} + /> + + + +
+ +
    + {filteredFiles.map((file) => ( + selectedFile.documentId === file.documentId)} + onSelect={() => handleFileSelect(file)} + /> + ))} +
+
+
+
+ +
+ ); +}; + +export default BrowseFileModal; diff --git a/apps/client/src/shared/component/Modal/Footer/ModalFooter.tsx b/apps/client/src/shared/component/Modal/Footer/ModalFooter.tsx index 05b496b7d..ada485d30 100644 --- a/apps/client/src/shared/component/Modal/Footer/ModalFooter.tsx +++ b/apps/client/src/shared/component/Modal/Footer/ModalFooter.tsx @@ -96,6 +96,11 @@ const ModalFooterButtons = ( createButton('취소', closeModal, 'outline'), createButton('연동', buttonClick, 'primary', !isButtonActive), ]; + case 'timeblock-file': + return [ + createButton('취소', closeModal, 'outline'), + createButton('업로드', buttonClick, 'primary', !isButtonActive), + ]; case 'caution': return [createButton('취소', closeModal, 'outline'), createButton('삭제', buttonClick, 'primary')]; case 'caution-modify': diff --git a/apps/client/src/shared/component/Modal/ModalFunnel.tsx b/apps/client/src/shared/component/Modal/ModalFunnel.tsx index 808d92d9a..19280b13e 100644 --- a/apps/client/src/shared/component/Modal/ModalFunnel.tsx +++ b/apps/client/src/shared/component/Modal/ModalFunnel.tsx @@ -13,6 +13,7 @@ import { BlockFlow } from '@/shared/component/TimeBlockModal'; import { WorkSpaceFlow } from '@/shared/component/WorkSpaceModal/index'; import { BlockProvider } from '@/shared/hook/common/useBlockContext'; import { FunnelProvider } from '@/shared/hook/common/useFunnel'; +import { TimeBlockFileUploadFlow } from '@/shared/hook/common/useTimeBlockUploadFlow'; import { WorkSpaceProvider } from '@/shared/hook/common/useWorkSpaceContext'; import { isCautionModalData, @@ -48,6 +49,16 @@ const ModalFunnel = () => { ); } + if (contentType === 'timeblock-file') { + return ( + + + + + + ); + } + const renderContent = () => { switch (contentType) { case 'create-workspace': diff --git a/apps/client/src/shared/component/NewFileImportModal/FileUploadContainer/FileUploadContainer.tsx b/apps/client/src/shared/component/NewFileImportModal/FileUploadContainer/FileUploadContainer.tsx new file mode 100644 index 000000000..13f375587 --- /dev/null +++ b/apps/client/src/shared/component/NewFileImportModal/FileUploadContainer/FileUploadContainer.tsx @@ -0,0 +1,59 @@ +import { IcFileUpload } from '@tiki/icon'; +import { Button, Flex, Text } from '@tiki/ui'; + +import React, { Dispatch, SetStateAction } from 'react'; + +import { Files } from '@/shared/api/time-blocks/team/time-block/type'; +import { FileWithDocumentId } from '@/shared/component/NewFileImportModal/NewFileImportModal'; +import { boxStyle, uploadBoxStyle } from '@/shared/component/NewFileImportModal/NewFileImportModal.style'; +import useFile from '@/shared/component/TimeBlockModal/hook/common/useFile'; + +interface FileUploadContainerProps { + size: 'medium' | 'large'; + files: FileWithDocumentId[]; + setFiles: Dispatch>; + setFileUrls: React.Dispatch>; + setUploadStatus: React.Dispatch>; +} + +const FileUploadContainer = ({ size, files, setFiles, setFileUrls, setUploadStatus }: FileUploadContainerProps) => { + const { fileInputRef, handleFileChange, handleDragOver, handleDrop } = useFile({ + files, + onFilesChange: (newFiles) => { + setFiles((prevFiles) => { + const uniqueFiles = newFiles.filter( + (newFile) => !prevFiles.some((file) => file.name === newFile.name && file.size === newFile.size) + ); + + return [...prevFiles, ...uniqueFiles]; + }); + }, + setFileUrls, + setUploadStatus, + }); + + return ( + handleDrop(event)}> + + + + + + 업로드할 파일을 끌어다 놓으세요. + + + JPEG, PNG, PDF, Word 형식의 파일을 업로드할 수 있습니다. + + + 최대 파일 크기는 50MB입니다. + + + + + + ); +}; + +export default FileUploadContainer; diff --git a/apps/client/src/shared/component/NewFileImportModal/NewFileImportModal.tsx b/apps/client/src/shared/component/NewFileImportModal/NewFileImportModal.tsx index 52b617c20..6c4854b88 100644 --- a/apps/client/src/shared/component/NewFileImportModal/NewFileImportModal.tsx +++ b/apps/client/src/shared/component/NewFileImportModal/NewFileImportModal.tsx @@ -1,6 +1,5 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import { IcFileUpload } from '@tiki/icon'; -import { Button, Flex, Text, scrollStyle, useToastAction } from '@tiki/ui'; +import { Flex, scrollStyle, useToastAction } from '@tiki/ui'; import { useEffect, useState } from 'react'; @@ -9,14 +8,10 @@ import { useQueryClient } from '@tanstack/react-query'; import { $api } from '@/shared/api/client'; import { Files } from '@/shared/api/time-blocks/team/time-block/type'; import { Modal } from '@/shared/component/Modal'; -import { - boxStyle, - scrollBoxStyle, - uploadBoxStyle, -} from '@/shared/component/NewFileImportModal/NewFileImportModal.style'; +import FileUploadContainer from '@/shared/component/NewFileImportModal/FileUploadContainer/FileUploadContainer'; +import { scrollBoxStyle } from '@/shared/component/NewFileImportModal/NewFileImportModal.style'; import { DocumentDetail } from '@/shared/component/TimeBlockModal'; import { flexStyle } from '@/shared/component/TimeBlockModal/component/UploadModal/UploadModal.style'; -import useFile from '@/shared/component/TimeBlockModal/hook/common/useFile'; import UploadedFileItem from '@/shared/component/UploadedFileItem/UploadedFileItem'; import { useInitializeTeamId } from '@/shared/hook/common/useInitializeTeamId'; import { useCloseModal, useModalIsOpen } from '@/shared/store/modal'; @@ -27,9 +22,11 @@ interface NewFileImportModalProps { selectedFiles?: DocumentDetail[]; onUploadFile?: (files: DocumentDetail[]) => void; onNext?: () => void; + contentType?: 'new-file' | 'timeblock-file'; + onPrev?: () => void; } -interface FileWithDocumentId extends File { +export interface FileWithDocumentId extends File { documentId?: number; } @@ -38,6 +35,8 @@ const NewFileImportModal = ({ selectedFiles = [], onUploadFile = () => {}, size = 'medium', + contentType = 'new-file', + onPrev, }: NewFileImportModalProps) => { const [files, setFiles] = useState([]); const [uploadStatus, setUploadStatus] = useState<{ [key: string]: boolean }>({}); @@ -55,21 +54,6 @@ const NewFileImportModal = ({ const { mutate: deleteDocumentMutation } = $api.useMutation('delete', '/api/v1/teams/{teamId}/documents'); const queryClient = useQueryClient(); - const { fileInputRef, handleFileChange, handleDragOver, handleDrop } = useFile({ - files, - onFilesChange: (newFiles) => { - setFiles((prevFiles) => { - const uniqueFiles = newFiles.filter( - (newFile) => !prevFiles.some((file) => file.name === newFile.name && file.size === newFile.size) - ); - - return [...prevFiles, ...uniqueFiles]; - }); - }, - setFileUrls, - setUploadStatus, - }); - const handleUploadFile = (validFiles: FileWithDocumentId[]) => { postDocumentMutation( { @@ -171,35 +155,13 @@ const NewFileImportModal = ({ - handleDrop(event)}> - - - - - - 업로드할 파일을 끌어다 놓으세요. - - - JPEG, PNG, PDF, Word 형식의 파일을 업로드할 수 있습니다. - - - 최대 파일 크기는 50MB입니다. - - - - - +
{files.map((file) => ( diff --git a/apps/client/src/shared/component/SelectedFilesModal/SelectedFilesModal.style.ts b/apps/client/src/shared/component/SelectedFilesModal/SelectedFilesModal.style.ts new file mode 100644 index 000000000..1725ccfa3 --- /dev/null +++ b/apps/client/src/shared/component/SelectedFilesModal/SelectedFilesModal.style.ts @@ -0,0 +1,15 @@ +import { css } from '@emotion/react'; +import { scrollStyle } from '@tiki/ui'; + +export const fileListWrapperStyle = css({ + width: '100%', + maxHeight: '30rem', + + paddingTop: '2rem', + + overflowY: 'scroll', + + scrollBehavior: 'smooth', + + ...scrollStyle, +}); diff --git a/apps/client/src/shared/component/SelectedFilesModal/SelectedFilesModal.tsx b/apps/client/src/shared/component/SelectedFilesModal/SelectedFilesModal.tsx new file mode 100644 index 000000000..8620f13e4 --- /dev/null +++ b/apps/client/src/shared/component/SelectedFilesModal/SelectedFilesModal.tsx @@ -0,0 +1,34 @@ +import { Modal } from '@/shared/component/Modal'; +import { DocumentDetail } from '@/shared/component/TimeBlockModal'; +import FileItem from '@/shared/component/TimeBlockModal/component/SelectedFileModal/FileItem/FileItem'; +import { fileListWrapperStyle } from '@/shared/component/TimeBlockModal/component/SelectedFileModal/SelectedFileModal.style'; +import { useFunnel } from '@/shared/hook/common/useFunnel'; +import { useCloseModal, useModalIsOpen } from '@/shared/store/modal'; + +interface SelectedFileModalProps { + selectedFiles: DocumentDetail[]; + onComplete: () => void; +} + +const SelectedFilesModal = ({ selectedFiles, onComplete }: SelectedFileModalProps) => { + const { prevStep } = useFunnel(); + + const isOpen = useModalIsOpen(); + const closeModal = useCloseModal(); + + return ( + + + +
    + {selectedFiles.map((file) => ( + + ))} +
+
+ +
+ ); +}; + +export default SelectedFilesModal; diff --git a/apps/client/src/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/BrowseFile.tsx b/apps/client/src/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/BrowseFile.tsx deleted file mode 100644 index 8f3f2bacf..000000000 --- a/apps/client/src/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/BrowseFile.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { IcSearch } from '@tiki/icon'; -import { Button, Flex, Input } from '@tiki/ui'; -import { useDebounce } from '@tiki/utils'; - -import { useMemo, useState } from 'react'; - -import { components } from '@/shared/__generated__/schema'; -import BrowseFileItem from '@/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/BrowseFileItem'; -import BrowseFileHeader from '@/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/FileHeader/BrowseFileHeader'; -import { scrollContainerStyle } from '@/shared/component/TimeBlockModal/component/UploadModal/UploadModal.style'; - -type DocumentDetail = components['schemas']['DocumentInfoGetResponse']; - -interface BrowseFileProps { - files: DocumentDetail[]; - selectedFiles: DocumentDetail[]; - onUpdateSelection: (selectedFiles: DocumentDetail[]) => void; - onShowBlockAdd: () => void; -} - -const BrowseFile = ({ files, selectedFiles, onUpdateSelection, onShowBlockAdd }: BrowseFileProps) => { - const [searchFile, setSearchFile] = useState(''); - - const filterKeyword = useDebounce(searchFile, 500); - - const filteredFiles = useMemo( - () => files.filter((file) => file.name.normalize('NFC').includes(filterKeyword.normalize('NFC'))), - [files, filterKeyword] - ); - - const handleFileSelect = (file: DocumentDetail) => { - const isSelected = selectedFiles.some((selectedFile) => selectedFile.documentId === file.documentId); - - const updatedSelection = isSelected - ? selectedFiles.filter((selectedFile) => selectedFile.documentId !== file.documentId) - : [...selectedFiles, file]; - - onUpdateSelection(updatedSelection); - }; - - return ( - - - } - onChange={(e) => setSearchFile(e.target.value)} - /> - - - -
- <> - -
    - {filteredFiles.map((file) => ( - selectedFile.documentId === file.documentId)} - onSelect={() => handleFileSelect(file)} - /> - ))} -
- -
-
- ); -}; - -export default BrowseFile; diff --git a/apps/client/src/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/BrowseFileModal.tsx b/apps/client/src/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/BrowseFileModal.tsx new file mode 100644 index 000000000..1875615f0 --- /dev/null +++ b/apps/client/src/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/BrowseFileModal.tsx @@ -0,0 +1,89 @@ +import { IcSearch } from '@tiki/icon'; +import { Button, Flex, Input } from '@tiki/ui'; +import { useDebounce } from '@tiki/utils'; + +import { useMemo, useState } from 'react'; + +import { components } from '@/shared/__generated__/schema'; +import BrowseFileHeader from '@/shared/component/BrowseFileHeader/BrowseFileHeader'; +import BrowseFileItem from '@/shared/component/BrowseFileItem/BrowseFileItem'; +import { Modal } from '@/shared/component/Modal'; +import { scrollContainerStyle } from '@/shared/component/TimeBlockModal/component/UploadModal/UploadModal.style'; +import { useCloseModal, useModalIsOpen } from '@/shared/store/modal'; + +type DocumentDetail = components['schemas']['DocumentInfoGetResponse']; + +interface BrowseFileProps { + files: DocumentDetail[]; + selectedFiles: DocumentDetail[]; + onUpdateSelection: (selectedFiles: DocumentDetail[]) => void; + onShowBlockAdd: () => void; +} + +const BrowseFileModal = ({ files, selectedFiles, onUpdateSelection, onShowBlockAdd }: BrowseFileProps) => { + const [searchFile, setSearchFile] = useState(''); + + const isOpen = useModalIsOpen(); + const closeModal = useCloseModal(); + + const isButtonActive = files.length !== 0; + + const filterKeyword = useDebounce(searchFile, 500); + + const filteredFiles = useMemo( + () => files.filter((file) => file.name.normalize('NFC').includes(filterKeyword.normalize('NFC'))), + [files, filterKeyword] + ); + + const handleFileSelect = (file: DocumentDetail) => { + const isSelected = selectedFiles.some((selectedFile) => selectedFile.documentId === file.documentId); + + const updatedSelection = isSelected + ? selectedFiles.filter((selectedFile) => selectedFile.documentId !== file.documentId) + : [...selectedFiles, file]; + + onUpdateSelection(updatedSelection); + }; + + return ( + + + + + + } + onChange={(e) => setSearchFile(e.target.value)} + /> + + + +
+ +
    + {filteredFiles.map((file) => ( + selectedFile.documentId === file.documentId)} + onSelect={() => handleFileSelect(file)} + /> + ))} +
+
+
+
+ +
+ ); +}; + +export default BrowseFileModal; diff --git a/apps/client/src/shared/component/TimeBlockModal/component/UploadModal/UploadModal.tsx b/apps/client/src/shared/component/TimeBlockModal/component/UploadModal/UploadModal.tsx index ed454a4b9..a5fa66d9e 100644 --- a/apps/client/src/shared/component/TimeBlockModal/component/UploadModal/UploadModal.tsx +++ b/apps/client/src/shared/component/TimeBlockModal/component/UploadModal/UploadModal.tsx @@ -6,7 +6,7 @@ import { components } from '@/shared/__generated__/schema'; import { $api } from '@/shared/api/client'; import { Modal } from '@/shared/component/Modal'; import NewFileImportModal from '@/shared/component/NewFileImportModal/NewFileImportModal'; -import BrowseFile from '@/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/BrowseFile'; +import BrowseFileModal from '@/shared/component/TimeBlockModal/component/UploadModal/BrowseFile/BrowseFileModal'; import { flexStyle } from '@/shared/component/TimeBlockModal/component/UploadModal/UploadModal.style'; import { useBlockContext } from '@/shared/hook/common/useBlockContext'; import { useFunnel } from '@/shared/hook/common/useFunnel'; @@ -83,7 +83,7 @@ const UploadModal = ({ onConfirmFile }: UploadModalProps) => { size="large" /> ) : ( - setIsAddingFiles(true)} diff --git a/apps/client/src/shared/constant/modal.tsx b/apps/client/src/shared/constant/modal.tsx index 273f220b2..d42703c94 100644 --- a/apps/client/src/shared/constant/modal.tsx +++ b/apps/client/src/shared/constant/modal.tsx @@ -7,6 +7,7 @@ type ModalContentType = | 'activity-tag' | 'new-file' | 'file' + | 'timeblock-file' | 'caution' | 'image'; @@ -208,6 +209,27 @@ export const MODAL_CONTENTS: Record = { ], ], }, + 'timeblock-file': { + steps: 2, + headers: [ + { + icon: , + title: '파일 업로드', + infoText: '타임블록에 추가할 파일을 선택해주세요', + }, + { + icon: , + title: '타임 블록 생성', + infoText: '추가한 파일을 확인해주세요', + }, + ], + buttons: [ + [ + { text: '취소', variant: 'outline' }, + { text: '완료', variant: 'primary' }, + ], + ], + }, image: { steps: 3, headers: [ diff --git a/apps/client/src/shared/hook/common/useTimeBlockUploadFlow.tsx b/apps/client/src/shared/hook/common/useTimeBlockUploadFlow.tsx new file mode 100644 index 000000000..87ee3e224 --- /dev/null +++ b/apps/client/src/shared/hook/common/useTimeBlockUploadFlow.tsx @@ -0,0 +1,74 @@ +import { useEffect, useState } from 'react'; + +import { components } from '@/shared/__generated__/schema'; +import { $api } from '@/shared/api/client'; +import BrowseFileModal from '@/shared/component/BrowseFileModal/BrowseFileModal'; +import NewFileImportModal from '@/shared/component/NewFileImportModal/NewFileImportModal'; +import SelectedFilesModal from '@/shared/component/SelectedFilesModal/SelectedFilesModal'; +import { useFunnel } from '@/shared/hook/common/useFunnel'; +import { useInitializeTeamId } from '@/shared/hook/common/useInitializeTeamId'; +import { useCloseModal } from '@/shared/store/modal'; +import { FunnelStep } from '@/shared/util/funnelStep'; + +export type DocumentDetail = components['schemas']['DocumentInfoGetResponse']; + +export const TimeBlockFileUploadFlow = () => { + const [files, setFiles] = useState([]); + const [isAddingFiles, setIsAddingFiles] = useState(false); + + const teamId = useInitializeTeamId(); + + const { setTotalSteps, nextStep } = useFunnel(); + const closeModal = useCloseModal(); + + const { data: fileData } = $api.useQuery('get', '/api/v1/documents/team/{teamId}/timeline', { + params: { + query: { type: 'executive' }, + path: { teamId }, + }, + }); + + const handleFilesConfirmed = (selectedFiles: DocumentDetail[]) => { + setFiles((prevFiles) => { + const combinedFiles = [...prevFiles, ...selectedFiles]; + return Array.from(new Map(combinedFiles.map((file) => [file.documentId, file])).values()); + }); + }; + + const handleComplete = () => { + closeModal(); + }; + + useEffect(() => { + setTotalSteps(2); + }, [setTotalSteps]); + + return ( + <> + + {isAddingFiles ? ( + { + setIsAddingFiles(false); + }} + onNext={() => { + nextStep(); + }} + /> + ) : ( + setIsAddingFiles(true)} + onConfirmFile={handleFilesConfirmed} + /> + )} + + + + + + ); +}; diff --git a/apps/client/src/shared/store/modal.tsx b/apps/client/src/shared/store/modal.tsx index b34f3eadb..68c7d0080 100644 --- a/apps/client/src/shared/store/modal.tsx +++ b/apps/client/src/shared/store/modal.tsx @@ -71,6 +71,7 @@ type ModalContentType = | 'new-file' | 'file' | 'caution' + | 'timeblock-file' | 'image'; const useModalStore = create((set) => ({