diff --git a/src/App.tsx b/src/App.tsx index 667c3fc..fafcc58 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,7 +2,15 @@ import { Outlet, Route, Routes } from 'react-router-dom'; import React from 'react'; import { ToastContainer } from 'react-toastify'; -import { ApiListPage, ApiPage, DetailPage, MainPage, CheckPwPage } from './pages'; +import { + ApiListPage, + ApiPage, + DetailPage, + MainPage, + CheckPwPage, + ListPage, + DeletePage, +} from './pages'; import { DefaultLayout } from './components'; export const App: React.FC = () => { @@ -17,10 +25,12 @@ export const App: React.FC = () => { } > } /> - } /> + } /> } /> + } /> } /> } /> + } /> ); diff --git a/src/api/api.ts b/src/api/api.ts index 2182465..0420b66 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -1,7 +1,7 @@ import axios from 'axios'; export const API_SUFFIX = { - BASEURL: import.meta.env.DEV ? 'http://127.0.0.1:5050' : import.meta.env.VITE_BASEURL, + BASEURL: import.meta.env.DEV ? 'http://172.30.1.56:5050' : import.meta.env.VITE_BASEURL, UPLOAD: '/upload', FILE: '/file', FILES: '/files', diff --git a/src/api/file.ts b/src/api/file.ts index 679505b..976ed67 100644 --- a/src/api/file.ts +++ b/src/api/file.ts @@ -12,11 +12,13 @@ export interface FileUploadValues extends UploadOptions { file: FormData; } -export interface FileUploadResponse extends DataUploadResponse { +export interface FileResponse extends DataUploadResponse { filename: string; size: number; } +export type FileUploadResponse = FileResponse; + export type GetFileOptions = GetItemOptions; export interface GetFileResponse extends DataResponse { diff --git a/src/api/index.ts b/src/api/index.ts index d00fa3d..1ed1a7b 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -2,3 +2,4 @@ export * from './api'; export * from './file'; export * from './text'; export * from './checkPw'; +export * from './list'; diff --git a/src/api/list.ts b/src/api/list.ts new file mode 100644 index 0000000..5ca75cc --- /dev/null +++ b/src/api/list.ts @@ -0,0 +1,13 @@ +import { API_SUFFIX, instance } from './api'; +import { FileResponse } from './file'; +import { TextResponse } from './text'; + +export interface DataListResponse { + files: FileResponse[]; + texts: TextResponse[]; +} + +export const getList = async (): Promise => { + const { data } = await instance.get(API_SUFFIX.LIST); + return data; +}; diff --git a/src/api/text.ts b/src/api/text.ts index 0f4f7ea..e2b41ba 100644 --- a/src/api/text.ts +++ b/src/api/text.ts @@ -13,11 +13,13 @@ export interface UploadTextValues extends UploadOptions { textData: string; } -export interface UploadTextResponse extends DataUploadResponse { +export interface TextResponse extends DataUploadResponse { data: string; numberOfText: number; } +export type UploadTextResponse = TextResponse; + export interface GetTextResponse extends DataResponse { textData: string; } @@ -35,7 +37,7 @@ export const upLoadText = async ({ timeLimit, }: UploadTextValues) => { const { data } = await instance.post( - `${API_SUFFIX.TEXT}${API_SUFFIX.TEXT_UPLOAD}${password ? `?pw=${password}` : ''}`, + `${API_SUFFIX.TEXT_UPLOAD}${password ? `?pw=${password}` : ''}`, textData, { headers: { diff --git a/src/components/common/DataBox/index.tsx b/src/components/common/DataBox/index.tsx index 026941e..61da8b7 100644 --- a/src/components/common/DataBox/index.tsx +++ b/src/components/common/DataBox/index.tsx @@ -2,9 +2,10 @@ import React from 'react'; import * as S from './styled'; export interface DataBoxProps { + onClick?: () => void; children: React.ReactNode; } -export const DataBox: React.FC = ({ children }) => { - return {children}; +export const DataBox: React.FC = ({ children, onClick }) => { + return {children}; }; diff --git a/src/components/common/DataBox/styled.ts b/src/components/common/DataBox/styled.ts index dd079de..720825b 100644 --- a/src/components/common/DataBox/styled.ts +++ b/src/components/common/DataBox/styled.ts @@ -11,6 +11,7 @@ export const DataBoxElement = styled.div` flex-wrap: wrap; word-break: break-all; font-size: 1.3rem; + width: fit-content; max-width: 100%; max-height: 10rem; line-height: 1.8rem; diff --git a/src/components/common/FileDetail/index.tsx b/src/components/common/FileDetail/index.tsx new file mode 100644 index 0000000..7d6eb30 --- /dev/null +++ b/src/components/common/FileDetail/index.tsx @@ -0,0 +1,62 @@ +import React from 'react'; + +import { useGetWindowSize } from '@/hooks'; +import { FileResponse } from '@/api'; +import { calculateMaxDataLength, getShortData } from '@/utils'; + +export interface FileDetailProps { + uploadDate: { + year: number; + month: number; + day: number; + }; + fileData: FileResponse; + fileSize: string; + isDetailPage?: boolean; +} + +export const FileDetail: React.FC = ({ + uploadDate, + fileData, + fileSize, + isDetailPage, +}) => { + const { windowSize } = useGetWindowSize(); + + const fileName = fileData.filename; + const fileNameLength = fileName.length; + + const maxFilenameLength = calculateMaxDataLength(windowSize); + const shortFileName = getShortData(fileName, maxFilenameLength); + + let detailsText: React.ReactNode; + + if (windowSize < 550 && fileNameLength <= 18) { + detailsText = ( + <> + {!isDetailPage ? shortFileName : fileName} / {fileSize}
+ 업로드된 날짜: {uploadDate.year}-{uploadDate.month}-{uploadDate.day} + + ); + } else if ( + (windowSize > 550 && fileNameLength >= 18) || + (windowSize < 550 && fileNameLength > 30) + ) { + detailsText = ( + <> + {!isDetailPage ? shortFileName : fileName} +
+ {fileSize} / 업로드된 날짜: {uploadDate.year}-{uploadDate.month}-{uploadDate.day} + + ); + } else { + detailsText = ( + <> + {!isDetailPage ? shortFileName : fileName} / {fileSize} / 업로드된 날짜: {uploadDate.year}- + {uploadDate.month}-{uploadDate.day} + + ); + } + + return <>{detailsText}; +}; diff --git a/src/components/common/Navbar/styled.ts b/src/components/common/Navbar/styled.ts index 6e52930..cffb243 100644 --- a/src/components/common/Navbar/styled.ts +++ b/src/components/common/Navbar/styled.ts @@ -13,7 +13,7 @@ export const NavbarContainer = styled.div` justify-content: center; column-gap: 6rem; @media screen and (max-width: 500px) { - padding: 1.6rem 0; + padding-bottom: 1.6rem; } `; diff --git a/src/components/common/SkeletonUI/index.tsx b/src/components/common/SkeletonUI/index.tsx index 322a6cb..521649d 100644 --- a/src/components/common/SkeletonUI/index.tsx +++ b/src/components/common/SkeletonUI/index.tsx @@ -1,5 +1,8 @@ +/** @jsxImportSource @emotion/react */ import React from 'react'; +import { css } from '@emotion/react'; + import * as S from './styled'; export interface SkeletonUIProps { @@ -9,5 +12,13 @@ export interface SkeletonUIProps { } export const SkeletonUI: React.FC = ({ width, height, margin }) => { - return ; + return ( + + ); }; diff --git a/src/components/common/SkeletonUI/styled.ts b/src/components/common/SkeletonUI/styled.ts index ed23581..d5dabfa 100644 --- a/src/components/common/SkeletonUI/styled.ts +++ b/src/components/common/SkeletonUI/styled.ts @@ -2,15 +2,8 @@ import styled from '@emotion/styled'; import { colors } from '@/styles'; -export const SkeletonUIElement = styled.div<{ - width: string; - height: string; - margin: string; -}>` +export const SkeletonUIElement = styled.div` display: flex; - min-width: ${({ width }) => width}; - min-height: ${({ height }) => height}; - margin: ${({ margin }) => margin}; background: ${colors.file}; border-radius: 0.8rem; overflow: hidden; diff --git a/src/components/common/SkeletonUIBox/index.tsx b/src/components/common/SkeletonUIBox/index.tsx new file mode 100644 index 0000000..4ce4c93 --- /dev/null +++ b/src/components/common/SkeletonUIBox/index.tsx @@ -0,0 +1,19 @@ +/** @jsxImportSource @emotion/react */ +import React from 'react'; + +import { css } from '@emotion/react'; + +import * as S from './styled'; +export interface SkeletonUIBoxProps { + randomWidth: string; +} + +export const SkeletonUIBox: React.FC = ({ randomWidth }) => { + return ( + + ); +}; diff --git a/src/components/common/SkeletonUIBox/styled.ts b/src/components/common/SkeletonUIBox/styled.ts new file mode 100644 index 0000000..e9950e6 --- /dev/null +++ b/src/components/common/SkeletonUIBox/styled.ts @@ -0,0 +1,30 @@ +import styled from '@emotion/styled'; + +import { colors } from '@/styles'; + +export const SkeletonUIBoxElement = styled.div` + display: flex; + background-color: ${colors.file}; + border-radius: 0.6rem; + margin-bottom: 1.5rem; + min-height: 3rem; + overflow: hidden; + &::before { + content: ' '; + width: 100%; + height: auto; + animation: loading 2s infinite; + box-shadow: 0 0 3rem 3rem rgba(255, 255, 255, 0.3); + } + @keyframes loading { + 0% { + transform: translateX(-50%); + } + 50% { + transform: translateX(100%); + } + 100% { + transform: translate(200%); + } + } +`; diff --git a/src/components/common/index.ts b/src/components/common/index.ts index c4537ed..58cf359 100644 --- a/src/components/common/index.ts +++ b/src/components/common/index.ts @@ -4,3 +4,5 @@ export * from './SkeletonUI'; export * from './Button'; export * from './Password'; export * from './DataBox'; +export * from './FileDetail'; +export * from './SkeletonUIBox'; diff --git a/src/components/detail/FileDetails/index.tsx b/src/components/detail/FileDetails/index.tsx deleted file mode 100644 index d61cc8a..0000000 --- a/src/components/detail/FileDetails/index.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React from 'react'; - -import { useGetWindowSize } from '@/hooks'; -import { GetFileResponse } from '@/api'; - -export interface FileDetailsProps { - filenameLength: number; - uploadDate: { - year: number; - month: number; - day: number; - }; - fileData: GetFileResponse; - fileSize: string; -} - -export const FileDetails: React.FC = ({ - filenameLength, - uploadDate, - fileData, - fileSize, -}) => { - const { windowSize } = useGetWindowSize(); - - let detailsText: React.ReactNode; - - if (windowSize < 550 && filenameLength <= 18) { - detailsText = ( - <> - {fileData.filename} / {fileSize}
- 업로드된 날짜: {uploadDate.year}-{uploadDate.month}-{uploadDate.day} - - ); - } else if ( - (windowSize < 1180 && windowSize > 550 && filenameLength <= 46 && filenameLength >= 18) || - (windowSize < 550 && filenameLength > 30) - ) { - detailsText = ( - <> - {fileData.filename} -
- 크기: {fileSize} / 업로드된 날짜: {uploadDate.year}-{uploadDate.month}-{uploadDate.day} - - ); - } else { - detailsText = ( - <> - 파일 이름: {fileData.filename} / 크기: {fileSize} / 업로드된 날짜: {uploadDate.year}- - {uploadDate.month}-{uploadDate.day} - - ); - } - - return <>{detailsText}; -}; diff --git a/src/components/detail/index.ts b/src/components/detail/index.ts deleted file mode 100644 index 7c7d345..0000000 --- a/src/components/detail/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './FileDetails'; diff --git a/src/components/index.ts b/src/components/index.ts index cc5909f..34a499a 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1,5 +1,5 @@ export * from './common'; export * from './layouts'; export * from './main'; -export * from './detail'; export * from './apiList'; +export * from './list'; diff --git a/src/components/layouts/DefaultLayout/styled.ts b/src/components/layouts/DefaultLayout/styled.ts index bfe1080..3708f3c 100644 --- a/src/components/layouts/DefaultLayout/styled.ts +++ b/src/components/layouts/DefaultLayout/styled.ts @@ -13,11 +13,11 @@ export const DefaultLayoutWrapper = styled(DefaultLayoutContainer)` justify-content: flex-start; position: relative; height: 68%; - row-gap: 3rem; width: 100%; padding: 0 2rem; overflow: hidden; max-width: 1250px; + row-gap: 1rem; @media screen and (min-width: 1800px) { max-width: 1500px; } @@ -25,6 +25,7 @@ export const DefaultLayoutWrapper = styled(DefaultLayoutContainer)` height: 76%; } @media screen and (max-width: 500px) { + row-gap: 0; padding: 0 1rem; height: 80%; } @@ -33,4 +34,8 @@ export const DefaultLayoutWrapper = styled(DefaultLayoutContainer)` export const DefaultLayoutTitleContainer = styled.div` position: static; top: 0; + margin-bottom: 3rem; + @media screen and (max-width: 500px) { + margin-bottom: 3rem; + } `; diff --git a/src/components/list/DataList/index.tsx b/src/components/list/DataList/index.tsx new file mode 100644 index 0000000..ea11efa --- /dev/null +++ b/src/components/list/DataList/index.tsx @@ -0,0 +1,70 @@ +import React from 'react'; +import { useNavigate } from 'react-router-dom'; + +import { FileResponse, TextResponse } from '@/api'; +import { FileDetail, DataBox } from '@/components'; +import { getDate, getFileSize } from '@/utils'; +import { LockSVG } from '@/assets'; + +import { TextDetail } from '../TextDetail'; + +import * as S from './styled'; + +export interface DataListProps { + type: 'file' | 'text'; + dataList: TextResponse[] | FileResponse[]; +} + +export const DataList: React.FC = ({ dataList, type }) => { + const navigate = useNavigate(); + + const handleDataBoxClick = (id: string, type: string, isEncrypted: boolean) => { + const routePath = isEncrypted ? `/checkPw/${id}` : `/dl/${id}`; + navigate(`${routePath}?type=${type}`); + }; + + const renderFileItem = (file: FileResponse) => { + const uploadDate = getDate(file.uploadDate); + const fileSize = getFileSize(file.size); + + return ( + handleDataBoxClick(file.id, 'file', file.isEncrypted)} key={file.id}> + {file.isEncrypted && } + + + ); + }; + + const renderTextItem = (text: TextResponse) => { + const uploadDate = getDate(text.uploadDate); + + return ( + handleDataBoxClick(text.id, 'text', text.isEncrypted)} key={text.id}> + {text.isEncrypted ? ( + <> + + 비밀글입니다 + + ) : ( + + )} + + ); + }; + + const renderItems = () => { + if (type === 'file') { + const fileData = dataList as FileResponse[]; + return fileData.map(renderFileItem); + } + + if (type === 'text') { + const textData = dataList as TextResponse[]; + return textData.map(renderTextItem); + } + + return null; + }; + + return <>{renderItems()}; +}; diff --git a/src/components/list/DataList/styled.ts b/src/components/list/DataList/styled.ts new file mode 100644 index 0000000..073bb78 --- /dev/null +++ b/src/components/list/DataList/styled.ts @@ -0,0 +1,7 @@ +import styled from '@emotion/styled'; + +export const DataListLockIcon = styled.img` + width: 1.4rem; + height: 1.4rem; + margin-right: 0.4rem; +`; diff --git a/src/components/list/TextDetail/index.tsx b/src/components/list/TextDetail/index.tsx new file mode 100644 index 0000000..3c5b97e --- /dev/null +++ b/src/components/list/TextDetail/index.tsx @@ -0,0 +1,55 @@ +import React from 'react'; + +import { useGetWindowSize } from '@/hooks'; +import { TextResponse } from '@/api'; +import { calculateMaxDataLength, getShortData } from '@/utils'; + +export interface TextDetailProps { + uploadDate: { + year: number; + month: number; + day: number; + }; + textData: TextResponse; +} + +export const TextDetail: React.FC = ({ uploadDate, textData }) => { + const { windowSize } = useGetWindowSize(); + + const textName = textData.data; + const textNameLength = textName.length; + + const maxTextLength = calculateMaxDataLength(windowSize); + const shortText = getShortData(textName, maxTextLength); + + let detailsText: React.ReactNode; + + if (windowSize < 550 && textNameLength <= 22) { + detailsText = ( + <> + {shortText} / {textNameLength}글자
+ 업로드된 날짜: {uploadDate.year}-{uploadDate.month}-{uploadDate.day} + + ); + } else if ( + (windowSize > 550 && textNameLength >= 40) || + (windowSize < 550 && textNameLength > 22) + ) { + detailsText = ( + <> + {shortText} +
+ {textNameLength}글자 / 업로드된 날짜: {uploadDate.year}-{uploadDate.month}-{uploadDate.day} + + ); + } else { + detailsText = ( + <> + {shortText} / {textNameLength}글자 / 업로드된 날짜: + {uploadDate.year}-{uploadDate.month}-{uploadDate.day} + + ); + } + + return <>{detailsText}; +}; diff --git a/src/components/list/index.ts b/src/components/list/index.ts new file mode 100644 index 0000000..2d5f4cc --- /dev/null +++ b/src/components/list/index.ts @@ -0,0 +1 @@ +export * from './DataList'; diff --git a/src/hooks/query/checkPw/index.tsx b/src/hooks/query/checkPw/index.tsx index 3484fa3..c311dc9 100644 --- a/src/hooks/query/checkPw/index.tsx +++ b/src/hooks/query/checkPw/index.tsx @@ -13,21 +13,21 @@ export const useCheckPw = (): UseMutationResult< AxiosError, CheckPwValues > => { - const navigation = useNavigate(); + const navigate = useNavigate(); const setCheckPw = useSetRecoilState(checkPwState); return useMutation( 'useCheckPw', ({ id, password }) => { if (id === '' || id === undefined) { toastError('잘못된 접근입니다.'); - navigation('/'); + navigate('/'); } return checkPw({ id, password }); }, { onSuccess: ({ token }, variables) => { setCheckPw({ token: token, isEncrypt: true }); - navigation(`/dl/${variables.id}`); + navigate(`/dl/${variables.id}`); }, onError: () => { toastError('비밀번호가 틀렸어요.'); diff --git a/src/hooks/query/delete/index.tsx b/src/hooks/query/delete/index.tsx index 5654668..5b4aa4a 100644 --- a/src/hooks/query/delete/index.tsx +++ b/src/hooks/query/delete/index.tsx @@ -16,13 +16,13 @@ export const useDelete = (): UseMutationResult< AxiosError, DeleteValues > => { - const navigation = useNavigate(); + const navigate = useNavigate(); return useMutation( 'useDelete', ({ type, id }) => { if (id === '' || id === undefined) { toastError('잘못된 접근입니다.'); - navigation('/'); + navigate('/'); } if (type === 'file') { return deleteFile({ id }); @@ -32,7 +32,7 @@ export const useDelete = (): UseMutationResult< }, { onSuccess: () => { - navigation(`/`); + navigate(`/`); toastSuccess(`삭제에 성공했어요!`); }, onError: () => { diff --git a/src/hooks/query/getItem/index.tsx b/src/hooks/query/getItem/index.tsx index b7d311a..543400f 100644 --- a/src/hooks/query/getItem/index.tsx +++ b/src/hooks/query/getItem/index.tsx @@ -20,47 +20,49 @@ export interface GetItemValues { isCheckPwPage?: boolean; type: 'file' | 'text' | 'none'; options: GetItemOptions; + isEncrypt?: boolean; } export const useGetItem = ({ isCheckPwPage, type, options: { id }, + isEncrypt, }: GetItemValues): UseQueryResult< APIResponse, AxiosError > => { - const navigation = useNavigate(); + const navigate = useNavigate(); const checkPw = useRecoilValue(checkPwState); return useQuery( 'useGetItem', () => { if (id === '' || id === undefined) { toastError('잘못된 접근입니다.'); - navigation('/'); + navigate('/'); } else { if (type === 'file') { - return getFile({ id, token: checkPw.token, isEncrypted: checkPw.isEncrypt }); + return getFile({ id, token: checkPw.token, isEncrypted: checkPw.isEncrypt || isEncrypt }); } else if (type === 'none') { - return null; + return []; } else { - return getText({ id, token: checkPw.token, isEncrypted: checkPw.isEncrypt }); + return getText({ id, token: checkPw.token, isEncrypted: checkPw.isEncrypt || isEncrypt }); } } }, { - onSuccess: ({ data: { isEncrypted, provide_token } }) => { - if (!isCheckPwPage && isEncrypted && !provide_token) { + onSuccess: ({ data }) => { + if (!isCheckPwPage && data.isEncrypted && !data.provide_token) { toastError('비밀번호가 필요해요.'); - navigation(`/checkPw/${id}?type=${type}`); + navigate(`/checkPw/${id}?type=${type}`); } }, onError: (res) => { if (res.response?.status === 401) { toastError('비밀번호가 필요해요.'); - navigation(`/checkPw/${id}?type=${type}`); + navigate(`/checkPw/${id}?type=${type}`); } else { - navigation('/'); + navigate('/'); } }, retry: 0, diff --git a/src/hooks/query/index.ts b/src/hooks/query/index.ts index 33126df..7e06bcf 100644 --- a/src/hooks/query/index.ts +++ b/src/hooks/query/index.ts @@ -2,3 +2,4 @@ export * from './upload'; export * from './getItem'; export * from './delete'; export * from './checkPw'; +export * from './list'; diff --git a/src/hooks/query/list/index.tsx b/src/hooks/query/list/index.tsx new file mode 100644 index 0000000..0f176e3 --- /dev/null +++ b/src/hooks/query/list/index.tsx @@ -0,0 +1,17 @@ +import { UseQueryResult, useQuery } from 'react-query'; + +import { AxiosError } from 'axios'; + +import { APIErrorResponse, APIResponse, DataListResponse, getList } from '@/api'; + +export const useGetList = (): UseQueryResult< + APIResponse, + AxiosError +> => { + return useQuery('useGetItem', getList, { + onError: (error) => { + console.log(error, 'error'); + }, + retry: 0, + }); +}; diff --git a/src/hooks/query/upload/index.tsx b/src/hooks/query/upload/index.tsx index 6fc3755..69ab316 100644 --- a/src/hooks/query/upload/index.tsx +++ b/src/hooks/query/upload/index.tsx @@ -27,7 +27,7 @@ export const useUpload = (): UseMutationResult< AxiosError, UploadValues > => { - const navigation = useNavigate(); + const navigate = useNavigate(); const checkPw = useSetRecoilState(checkPwState); return useMutation( 'useUpload', @@ -41,7 +41,7 @@ export const useUpload = (): UseMutationResult< { onSuccess: ({ data }, variables) => { checkPw({ isEncrypt: data.isEncrypted, token: data.token }); - navigation(`/dl/${data.id}?type=${variables.type}`); + navigate(`/dl/${data.id}?type=${variables.type}`); toastSuccess(`업로드에 성공했어요!`); }, onError: () => { diff --git a/src/pages/apiList/styled.ts b/src/pages/apiList/styled.ts index 2dfccb3..3cb3b8d 100644 --- a/src/pages/apiList/styled.ts +++ b/src/pages/apiList/styled.ts @@ -59,6 +59,6 @@ export const ApiListPageBoxShadow = styled.div` width: 100%; height: 6rem; position: relative; - top: -2.5rem; //-25px + top: -3.2rem; background: linear-gradient(180deg, rgba(40, 42, 58, 0) 0%, #282a3a 45.31%); `; diff --git a/src/pages/checkPw/index.tsx b/src/pages/checkPw/index.tsx index 236868a..a9c00db 100644 --- a/src/pages/checkPw/index.tsx +++ b/src/pages/checkPw/index.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; -import { Button, DataBox, FileDetails, Password, SkeletonUI } from '@/components'; +import { Button, DataBox, FileDetail, Password, SkeletonUI } from '@/components'; import { useGetItem, useCheckPw } from '@/hooks'; import { LockSVG } from '@/assets'; import { GetFileResponse } from '@/api'; @@ -20,6 +20,7 @@ export const CheckPwPage: React.FC = () => { id: id ? id : '', }, isCheckPwPage: true, + isEncrypt: true, }); const { mutate } = useCheckPw(); @@ -36,10 +37,29 @@ export const CheckPwPage: React.FC = () => { ); } - const fileData = data?.data as GetFileResponse; + const fileRender = () => { + if (data?.data.uploadDate) { + const fileData = data.data as GetFileResponse; - const fileSize = data && getFileSize(fileData.size); - const uploadDate = data && getDate(fileData.uploadDate); + const fileSize = getFileSize(fileData.size); + const uploadDate = getDate(fileData.uploadDate); + return ( + + + + ); + } else { + return ( + <> + + + ); + } + }; const onPasswordSubmit = () => { mutate({ id: id ? id : '', password }); @@ -47,20 +67,13 @@ export const CheckPwPage: React.FC = () => { return ( - - {!data ? ( - <> - 텍스트를 확인하려면 비밀번호를 입력하세요{' '} - - ) : ( - - )} - + {!data?.data ? ( + + 텍스트를 확인하려면 비밀번호를 입력하세요 + + ) : ( + fileRender() + )} + + + + ); +}; diff --git a/src/pages/delete/styled.ts b/src/pages/delete/styled.ts new file mode 100644 index 0000000..6e76a47 --- /dev/null +++ b/src/pages/delete/styled.ts @@ -0,0 +1,28 @@ +import styled from '@emotion/styled'; + +export const DeletePageContainer = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + row-gap: 2rem; +`; + +export const DeletePageMainText = styled.h1` + font-size: 1.4rem; + font-weight: 700; + @media screen and (max-width: 500px) { + font-size: 1.2rem; + } +`; + +export const DeletePageButtonContainer = styled.div` + display: flex; + width: 100%; + align-items: center; + justify-content: center; + column-gap: 1.6rem; + & > button { + width: 8rem; + } +`; diff --git a/src/pages/detail/index.tsx b/src/pages/detail/index.tsx index 550e960..7f8c658 100644 --- a/src/pages/detail/index.tsx +++ b/src/pages/detail/index.tsx @@ -1,7 +1,8 @@ import React from 'react'; +import { useNavigate } from 'react-router-dom'; -import { useDelete, useGetInfo, useGetItem } from '@/hooks'; -import { Button, DataBox, FileDetails, SkeletonUI } from '@/components'; +import { useGetInfo, useGetItem } from '@/hooks'; +import { Button, DataBox, FileDetail, SkeletonUI } from '@/components'; import { getDate, getExpireTime, getFileSize, toastSuccess } from '@/utils'; import { GetFileResponse, GetTextResponse } from '@/api'; @@ -17,9 +18,9 @@ export const DetailPage: React.FC = () => { }, }); - const { mutate: deleteMutate } = useDelete(); + const navigate = useNavigate(); - if (isLoading || !data) { + if (isLoading || !data?.data.uploadDate) { return ( <> @@ -39,13 +40,13 @@ export const DetailPage: React.FC = () => { const { expireTime, downloadLimit } = data.data; const expireDate = getExpireTime(expireTime); - const uploadDate = getDate(data.data.uploadDate); + const uploadDate = getDate(fileData.uploadDate); const fileSize = getFileSize(fileData.size); const fileDownload = data.data.download_url; const onDeleteClick = () => { - deleteMutate({ type, id: id ? id : '' }); + navigate(`/del/${id}?type=${type}`); }; const onLinkCopy = () => { @@ -61,11 +62,11 @@ export const DetailPage: React.FC = () => { return ( - {type === 'file' ? ( - ) : ( @@ -78,7 +79,7 @@ export const DetailPage: React.FC = () => { diff --git a/src/pages/index.ts b/src/pages/index.ts index e818dbf..1a3f12d 100644 --- a/src/pages/index.ts +++ b/src/pages/index.ts @@ -4,3 +4,5 @@ export * from './apiList'; export * from './checkPw'; export * from './apiList'; export * from './api'; +export * from './list'; +export * from './delete'; diff --git a/src/pages/list/index.tsx b/src/pages/list/index.tsx new file mode 100644 index 0000000..a4489b9 --- /dev/null +++ b/src/pages/list/index.tsx @@ -0,0 +1,55 @@ +import React from 'react'; + +import { useGetList } from '@/hooks'; +import { Button, DataList, SkeletonUIBox } from '@/components'; + +import * as S from './styled'; + +export const ListPage: React.FC = () => { + const { data, isLoading } = useGetList(); + + const SkeletonUIRandomWidth = ['50', '55', '60', '65', '70', '75', '80']; + + const randomSkeletonWidths = Array.from( + { length: 7 }, + () => SkeletonUIRandomWidth[Math.floor(Math.random() * 6)], + ); + + if (isLoading || !data?.data || !data?.data.files || !data?.data.texts) { + return ( + + {randomSkeletonWidths.map((width, i) => ( + + ))} + + ); + } + + const fileData = data.data.files; + const textData = data.data.texts; + + return ( + <> + + {fileData.length !== 0 || textData.length !== 0 ? ( + + + + + ) : ( + <> + 업로드된 파일이나 텍스트가 없어요. + + + )} + + + + ); +}; diff --git a/src/pages/list/styled.ts b/src/pages/list/styled.ts new file mode 100644 index 0000000..aa31239 --- /dev/null +++ b/src/pages/list/styled.ts @@ -0,0 +1,43 @@ +import styled from '@emotion/styled'; +import { motion } from 'framer-motion'; + +export const ListPageContainer = styled(motion.div)` + display: flex; + flex-direction: column; + align-items: center; + overflow: scroll; + height: 100vh; + width: 100%; + &::-webkit-scrollbar { + display: none; + } + width: 100%; +`; + +export const ListPageListWrapper = styled.div` + display: flex; + flex-direction: column; + align-items: center; + row-gap: 1.4rem; +`; + +export const ListPageBoxShadow = styled.div` + width: 100%; + height: 5rem; + position: relative; + top: -2rem; + background: linear-gradient(180deg, rgba(40, 42, 58, 0) 0%, #282a3a 45.31%); + @media screen and (max-width: 500px) { + top: -1rem; + height: 4rem; + } +`; + +export const ListPageText = styled.h1` + font-size: 1.4rem; + font-weight: 700; + margin: 2rem 0; + @media screen and (max-width: 500px) { + font-size: 1.2rem; + } +`; diff --git a/src/pages/main/index.tsx b/src/pages/main/index.tsx index 2737a41..12432de 100644 --- a/src/pages/main/index.tsx +++ b/src/pages/main/index.tsx @@ -71,9 +71,9 @@ export const MainPage: React.FC = () => { const { mutate } = useUpload(); - const onOptionClick = (index: number) => { - const option = UPLOAD_OPTIONS_LIST[index]; - setActiveOption((prev) => ({ ...prev, [option]: !prev[option] })); + const onOptionClick = (option: string) => { + const optionName = option as UPLOAD_OPTIONS_LIST_TYPE; + setActiveOption((prev) => ({ ...prev, [optionName]: !prev[optionName] })); }; const onExpireTimeClick = (type: string, value: number) => { @@ -154,7 +154,7 @@ export const MainPage: React.FC = () => { {UPLOAD_OPTIONS_LIST.map((option, i) => ( - onOptionClick(i)} key={i}> + onOptionClick(option)} key={i}> { + switch (true) { + case windowSize > 1250: + return 80; + case windowSize > 1180: + return 60; + case windowSize > 768: + return 55; + case windowSize > 530: + return 42; + default: + return 24; + } +}; + +export const getShortData = (data: string, maxDataLength: number) => { + if (data.length > maxDataLength) { + return `${data.slice(0, maxDataLength)}...`; + } + + return data; +}; diff --git a/src/utils/index.ts b/src/utils/index.ts index cb01832..7f38c7c 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,3 +1,4 @@ export * from './getFileSize'; export * from './toast'; export * from './getDay'; +export * from './getShortData';