diff --git a/src/Router.tsx b/src/Router.tsx index edfb751a..902e1c72 100644 --- a/src/Router.tsx +++ b/src/Router.tsx @@ -1,8 +1,8 @@ import { BrowserRouter, Route, Routes } from 'react-router-dom'; +import DetailPage from './page/DetailPage'; import ListPage from './page/ListPage'; -import MainPage from './page/MainPage'; import LoginPage from './page/Login/LoginPage'; -import DetailPage from './page/DetailPage'; +import MainPage from './page/MainPage'; import MyTattoo from './page/MyTattoo'; import MyTattooDetail from './page/MyTattooDetail'; import CompletePage from './page/Order/CompletePage'; @@ -10,20 +10,20 @@ import OrderPage from './page/Order/OrderPage'; import OnBoardingPage from './page/Custom/Common/OnBoardingPage'; -import SearchPage from './page/Search/SearchPage'; -import SearchResultPage from './page/Search/SearchResultPage'; -import ScrollToTop from './libs/hooks/ScrollTop'; import LoginCallback from './components/Login/LoginCallback'; -import SavePage from './page/SavePage'; -import NoDesignCustomPage from './page/Custom/NoDesign/NoDesignCustomPage'; -import HaveDesignCustomPage from './page/Custom/HaveDesign/HaveDesignCustomPage'; +import Interceptors from './libs/hooks/Interceptors'; +import ScrollToTop from './libs/hooks/ScrollTop'; +import CartPage from './page/CartPage'; import CommonCustomPage from './page/Custom/Common/CommonCustomPage'; +import HaveDesignCustomPage from './page/Custom/HaveDesign/HaveDesignCustomPage'; +import NoDesignCustomPage from './page/Custom/NoDesign/NoDesignCustomPage'; import ErrorPage from './page/Error/ErrorPage'; -import MagazinePage from './page/MagazinePage'; -import CartPage from './page/CartPage'; import ExpirationPage from './page/Expiration/ExpirationPage'; +import MagazinePage from './page/MagazinePage'; import OrderDepositPage from './page/Order/OrderDepositPage'; -import Interceptors from './libs/hooks/Interceptors'; +import SavePage from './page/SavePage'; +import SearchPage from './page/Search/SearchPage'; +import SearchResultPage from './page/Search/SearchResultPage'; const Router = () => { return ( diff --git a/src/components/Custom/Common/Select/SelectCustom.tsx b/src/components/Custom/Common/Select/SelectCustom.tsx index 429a3d4c..b26a57e1 100644 --- a/src/components/Custom/Common/Select/SelectCustom.tsx +++ b/src/components/Custom/Common/Select/SelectCustom.tsx @@ -1,4 +1,4 @@ -import React, { useState, useRef, useEffect } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { styled } from 'styled-components'; import SelectCustomBtn from './SelectCustomBtn'; @@ -10,14 +10,14 @@ interface SelectCustomProps { const SelectCustom = ({ setIsActiveNext, setHaveDesign }: SelectCustomProps) => { const CASE_BTN_DATA = [ { - id: 'noDesign', + id: 'haveDesign', firstTitle: '내 도안', secondTitle: '그대로 만들기', firstDetail: '이미지 파일', secondDetail: '그대로 제작해드려요', }, { - id: 'haveDesign', + id: 'noDesign', firstTitle: '타투어에게', secondTitle: '도안 의뢰하기', firstDetail: '참고 이미지, 간단 스케치를', diff --git a/src/components/Custom/Common/Select/SelectCustomFooter.tsx b/src/components/Custom/Common/Select/SelectCustomFooter.tsx index d5716a14..66161511 100644 --- a/src/components/Custom/Common/Select/SelectCustomFooter.tsx +++ b/src/components/Custom/Common/Select/SelectCustomFooter.tsx @@ -1,5 +1,6 @@ +import { useNavigate } from 'react-router-dom'; import { styled } from 'styled-components'; -import usePostCustomApply from '../../../../libs/hooks/usePostCustomApply'; +import { api } from '../../../../libs/api'; interface SelectCustomFooterProps { isActiveNext: boolean; @@ -14,15 +15,26 @@ const SelectCustomFooter = ({ setStep, setCustomId, }: SelectCustomFooterProps) => { - const { response } = usePostCustomApply(haveDesign); + const navigate = useNavigate(); + + // 선택된 커스텀 타투 플로우 케이스 값(haveDesign)이 있을 때, 통신을 해주기 위한 함수 + const fetchCustomApply = async () => { + try { + const { data } = await api.post('/custom/apply', { + haveDesign: haveDesign, + }); + setCustomId(data.data.customId); + } catch (err) { + navigate('/error'); + } + }; const handleClickFooter = () => { if (!isActiveNext) return; - if (response) { - setCustomId(response.customId); - setStep((prev) => prev + 1); - } + // 선택 된 값 있음 === isActiveNext일 때 custom/apply post 통신 + fetchCustomApply(); + setStep((prev) => prev + 1); }; return ( diff --git a/src/libs/hooks/usePostCustomApply.ts b/src/libs/hooks/usePostCustomApply.ts deleted file mode 100644 index 2cce0aab..00000000 --- a/src/libs/hooks/usePostCustomApply.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { AxiosError, isAxiosError } from 'axios'; -import { useEffect, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import { api } from '../api'; - -interface CustomApplyResponseData { - customId: number; -} - -// interface CustomApplyResponse { -// data: CustomApplyResponseData; -// code: number; -// message: string; -// } - -const usePostCustomApply = (haveDesign: boolean) => { - const navigate = useNavigate(); - - const [response, setResponse] = useState(); - const [error, setError] = useState(); - const [loading, setLoading] = useState(true); - - const fetchData = async () => { - try { - const { data } = await api.post('/custom/apply', { - haveDesign: haveDesign, - }); - setResponse(data.data); - } catch (err) { - if (isAxiosError(err)) { - setError(err); - navigate('/error'); - } - } finally { - setLoading(false); - } - }; - - useEffect(() => { - fetchData(); - }, []); - - return { response, error, loading }; -}; - -export default usePostCustomApply; diff --git a/src/page/Custom/Common/CommonCustomPage.tsx b/src/page/Custom/Common/CommonCustomPage.tsx index 2fab695e..9df5fac9 100644 --- a/src/page/Custom/Common/CommonCustomPage.tsx +++ b/src/page/Custom/Common/CommonCustomPage.tsx @@ -1,7 +1,7 @@ import { useState } from 'react'; +import { useLocation } from 'react-router-dom'; import SelectCustomLayout from '../../../components/Custom/Common/Select/SelectCustomLayout'; import CustomSizeLayout from '../../../components/Custom/Common/Size/CustomSizeLayout'; -import { useLocation } from 'react-router-dom'; const CommonCustomPage = () => { const location = useLocation(); @@ -10,7 +10,7 @@ const CommonCustomPage = () => { const [step, setStep] = useState(state ? state.step : 0); // step 0 : SelectPage - noDesign인지, haveDesign인지 상황 판단 플래그 state const [haveDesign, setHaveDesign] = useState( - state && state.haveDesign !== undefined ? state.haveDesign : false, + state && state.haveDesign !== undefined ? state.haveDesign : true, ); const [customId, setCustomId] = useState( state && state.customId !== undefined ? state.customId : 0, diff --git a/src/page/Custom/HaveDesign/HaveDesignCustomPage.tsx b/src/page/Custom/HaveDesign/HaveDesignCustomPage.tsx index 6a83c9fc..b24e0332 100644 --- a/src/page/Custom/HaveDesign/HaveDesignCustomPage.tsx +++ b/src/page/Custom/HaveDesign/HaveDesignCustomPage.tsx @@ -4,11 +4,8 @@ import CustomDirectDepositLayout from '../../../components/Custom/Common/DirectD import PriceLayout from '../../../components/Custom/Common/PriceLayout'; import ReceiptLayout from '../../../components/Custom/Common/Receipt/ReceiptLayout'; import CustomSizeLayout from '../../../components/Custom/Common/Size/CustomSizeLayout'; -import AdditionalRequestLayout from '../../../components/Custom/HaveDesign/AdditionalRequest/AdditionalRequestLayout'; -import CustomThemeLayout from '../../../components/Custom/HaveDesign/CustomTheme/CustomThemeLayout'; -import CustomReferenceLayout from '../../../components/Custom/HaveDesign/Reference/CustomReferenceLayout'; -import StylingColorLayout from '../../../components/Custom/HaveDesign/SelectColor/StylingColorLayout'; -import SelectKeywordLayout from '../../../components/Custom/HaveDesign/SelectKeyword/SelectKeywordLayout'; +import CustomImgLayout from '../../../components/Custom/NoDesign/Img/CustomImgLayout'; +import CustomRequestLayout from '../../../components/Custom/NoDesign/Request/CustomRequestLayout'; import { api } from '../../../libs/api'; import { resCustomInfoType } from '../../../types/customInfoType'; import LoadingPage from '../../LoadingPage'; @@ -17,93 +14,59 @@ const HaveDesignCustomPage = () => { const location = useLocation(); const navigate = useNavigate(); + //커스텀 신청서 플로우에 따른 각 단계별 컴포넌트 렌더링 플래그 const [step, setStep] = useState( location.state && location.state.step !== undefined ? location.state.step : 1, ); - //step 1: 이미지 첨부하기 관련 state - const [customImages, setCustomImages] = useState(); - const [handDrawingImage, setHandDrawingImage] = useState(null); - const [previewURL, setPreviewURL] = useState( - location.state && - location.state.mainImageUrl !== undefined && - location.state.images !== undefined - ? [location.state.mainImageUrl, ...location.state.images] - : [], - ); - const [drawingImageUrl, setDrawingImageUrl] = useState( - location.state && - location.state.handDrawingImageUrl !== undefined && - location.state.handDrawingImageUrl !== null - ? location.state.handDrawingImageUrl + //step 1: CustomImg - 그려둔 도안 이미지 state + const [customImages, setCustomImages] = useState(); + const [previewURL, setPreviewURL] = useState( + location.state && location.state.mainImageUrl !== undefined + ? location.state.mainImageUrl : null, ); - //step 2: 색상 선택 state - const [isColoredState, setIsColored] = useState( - location.state && location.state.isColored !== undefined ? location.state.isColored : false, - ); - const [selectedColorMode, setSelectedColorMode] = useState(''); - - //step 3: 키워드 선택 state - const [styles, setStyles] = useState([]); - const [themes, setThemes] = useState([]); - - const stylesKeyword = - location.state && location.state.styles !== undefined ? location.state.styles : []; - const themesKeyword = - location.state && location.state.themes !== undefined ? location.state.themes : []; - - //step 4: 타투 이름 입력 관련 state + //step 2: CustomRequest + //타투 이름 state const [name, setName] = useState( location.state && location.state.name !== undefined ? location.state.name : '', ); - const [description, setDescription] = useState( - location.state && location.state.description !== undefined ? location.state.description : '', - ); - //step 5: 추가 요구사항 관련 state + //요청사항 state const [demand, setDemand] = useState( location.state && location.state.demand !== undefined ? location.state.demand : '', ); - //step 6: 주문 관련 state + //step 3: 주문 관련 state -> 이 부분 나중에 같이 논의 필요합니다 const [count, setCount] = useState(1); const [isPublic, setIsPublic] = useState(false); - const [price, setPrice] = useState(0); + const [price, setPrice] = useState(0); //수정하면서 임의로 추가했습니다! - const handleTotalPriceChange = (newTotalPrice: number) => { - setPrice(newTotalPrice); - }; - - // 앞부분 임시 통합한 곳에서 state 불러오기. 최종 통합 때 제거 예정 + // 앞에 size + customId 통합 해놓은거에서 우선 navigate state로 관련 정보 불러옴 추후 통합시 제거 예정 const [size, setSize] = useState(location.state ? location.state.size : null); + // const size = location.state ? location.state.size : null; const customId = location.state ? location.state.customId : null; - const haveDesign = true; + const haveDesign = location.state ? location.state.haveDesign : null; //영수증 뷰 다르게 띄워주기 위해서 임시 추가했습니다 //patch에 보낼 정보들 객체로 모으기 const customInfo = { - customId: customId, - size: size, - isColored: isColoredState, - name: name === '' ? '임시 저장' : name, - description: description, - demand: demand, - viewCount: step, - themes: themes, - styles: styles, + customId: customId, //id + size: size, //타투 사이즈 + name: name === '' ? '임시저장' : name, //이름 + demand: demand, //요청사항 + viewCount: step, //뷰카운트(임시저장용) count: count, //수량 isPublic: isPublic, //도안 공개 여부 price: price, //최종 가격 - haveDesign: haveDesign, }; // patch 통신 response = receipt 뷰에 넘겨줘야 하는 정보들 const [receiptData, setReceiptData] = useState(); - // 이미지 용량 이슈로 receipt 페이지로 넘어갈 때, 데이터 통신 완료 이전 로딩 스피너 보여주기 위해 쓰이는 플래그 const [receiptLoading, setReceiptLoading] = useState(false); + // 무통장 입금에서 '송금했어요' 버튼 클릭시의 핸들러 const handleClickCustomDepositBtn = async () => { const formData = new FormData(); @@ -116,17 +79,12 @@ const HaveDesignCustomPage = () => { setReceiptLoading(true); try { - // 1. handDrawingImage(손 그림) append - if (handDrawingImage) { - formData.append('handDrawingImage', handDrawingImage); - } - - // 2. customInfo(커스텀 정보들) append + // 1. customInfo(커스텀 정보들) append const json = JSON.stringify(updatedCustomInfo); const blob = new Blob([json], { type: 'application/json' }); formData.append('customInfo', blob); - // 3. customImage(도안 이미지) append + // 2. customImage(도안 이미지) append if (customImages) { for (let i = 0; i < customImages.length; i++) { formData.append('customImages', customImages.item(i) as File); @@ -139,9 +97,9 @@ const HaveDesignCustomPage = () => { }, }); + setReceiptData(data.data); if (data) { setReceiptLoading(false); - setReceiptData(data.data); setStep((prev: number) => prev + 1); } } catch (err) { @@ -162,95 +120,43 @@ const HaveDesignCustomPage = () => { ); case 1: return ( - ); - case 2: return ( - - ); - - case 3: - return ( - - ); - - case 4: - return ( - - ); - - case 5: - return ( - ); - case 6: + case 3: return ( ); - case 7: + case 4: return receiptLoading ? ( ) : ( @@ -260,8 +166,8 @@ const HaveDesignCustomPage = () => { /> ); - case 8: - return ; + case 5: + return ; //haveDesign 임의로 추가해서 넘겨줬습니다 } }; diff --git a/src/page/Custom/NoDesign/NoDesignCustomPage.tsx b/src/page/Custom/NoDesign/NoDesignCustomPage.tsx index 885cc292..df81191b 100644 --- a/src/page/Custom/NoDesign/NoDesignCustomPage.tsx +++ b/src/page/Custom/NoDesign/NoDesignCustomPage.tsx @@ -4,8 +4,11 @@ import CustomDirectDepositLayout from '../../../components/Custom/Common/DirectD import PriceLayout from '../../../components/Custom/Common/PriceLayout'; import ReceiptLayout from '../../../components/Custom/Common/Receipt/ReceiptLayout'; import CustomSizeLayout from '../../../components/Custom/Common/Size/CustomSizeLayout'; -import CustomImgLayout from '../../../components/Custom/NoDesign/Img/CustomImgLayout'; -import CustomRequestLayout from '../../../components/Custom/NoDesign/Request/CustomRequestLayout'; +import AdditionalRequestLayout from '../../../components/Custom/HaveDesign/AdditionalRequest/AdditionalRequestLayout'; +import CustomThemeLayout from '../../../components/Custom/HaveDesign/CustomTheme/CustomThemeLayout'; +import CustomReferenceLayout from '../../../components/Custom/HaveDesign/Reference/CustomReferenceLayout'; +import StylingColorLayout from '../../../components/Custom/HaveDesign/SelectColor/StylingColorLayout'; +import SelectKeywordLayout from '../../../components/Custom/HaveDesign/SelectKeyword/SelectKeywordLayout'; import { api } from '../../../libs/api'; import { resCustomInfoType } from '../../../types/customInfoType'; import LoadingPage from '../../LoadingPage'; @@ -14,60 +17,92 @@ const NoDesignCustomPage = () => { const location = useLocation(); const navigate = useNavigate(); - //커스텀 신청서 플로우에 따른 각 단계별 컴포넌트 렌더링 플래그 const [step, setStep] = useState( location.state && location.state.step !== undefined ? location.state.step : 1, ); - //step 1: CustomImg - 그려둔 도안 이미지 state - const [customImages, setCustomImages] = useState(); - const [previewURL, setPreviewURL] = useState( - location.state && location.state.mainImageUrl !== undefined - ? location.state.mainImageUrl + //step 1: 이미지 첨부하기 관련 state + const [customImages, setCustomImages] = useState(); + const [handDrawingImage, setHandDrawingImage] = useState(null); + const [previewURL, setPreviewURL] = useState( + location.state && + location.state.mainImageUrl !== undefined && + location.state.images !== undefined + ? [location.state.mainImageUrl, ...location.state.images] + : [], + ); + const [drawingImageUrl, setDrawingImageUrl] = useState( + location.state && + location.state.handDrawingImageUrl !== undefined && + location.state.handDrawingImageUrl !== null + ? location.state.handDrawingImageUrl : null, ); - //step 2: CustomRequest - //타투 이름 state + //step 2: 색상 선택 state + const [isColoredState, setIsColored] = useState( + location.state && location.state.isColored !== undefined ? location.state.isColored : false, + ); + const [selectedColorMode, setSelectedColorMode] = useState(''); + + //step 3: 키워드 선택 state + const [styles, setStyles] = useState([]); + const [themes, setThemes] = useState([]); + + const stylesKeyword = + location.state && location.state.styles !== undefined ? location.state.styles : []; + const themesKeyword = + location.state && location.state.themes !== undefined ? location.state.themes : []; + + //step 4: 타투 이름 입력 관련 state const [name, setName] = useState( location.state && location.state.name !== undefined ? location.state.name : '', ); + const [description, setDescription] = useState( + location.state && location.state.description !== undefined ? location.state.description : '', + ); - //요청사항 state + //step 5: 추가 요구사항 관련 state const [demand, setDemand] = useState( location.state && location.state.demand !== undefined ? location.state.demand : '', ); - //step 3: 주문 관련 state -> 이 부분 나중에 같이 논의 필요합니다 + //step 6: 주문 관련 state const [count, setCount] = useState(1); const [isPublic, setIsPublic] = useState(false); - const [price, setPrice] = useState(0); //수정하면서 임의로 추가했습니다! + const [price, setPrice] = useState(0); - // 앞에 size + customId 통합 해놓은거에서 우선 navigate state로 관련 정보 불러옴 추후 통합시 제거 예정 + const handleTotalPriceChange = (newTotalPrice: number) => { + setPrice(newTotalPrice); + }; + + // 앞부분 임시 통합한 곳에서 state 불러오기. 최종 통합 때 제거 예정 const [size, setSize] = useState(location.state ? location.state.size : null); - // const size = location.state ? location.state.size : null; const customId = location.state ? location.state.customId : null; - const haveDesign = location.state ? location.state.haveDesign : null; //영수증 뷰 다르게 띄워주기 위해서 임시 추가했습니다 + const haveDesign = true; //patch에 보낼 정보들 객체로 모으기 const customInfo = { - customId: customId, //id - size: size, //타투 사이즈 - name: name === '' ? '임시저장' : name, //이름 - demand: demand, //요청사항 - viewCount: step, //뷰카운트(임시저장용) + customId: customId, + size: size, + isColored: isColoredState, + name: name === '' ? '임시 저장' : name, + description: description, + demand: demand, + viewCount: step, + themes: themes, + styles: styles, count: count, //수량 isPublic: isPublic, //도안 공개 여부 price: price, //최종 가격 - haveDesign: haveDesign, }; // patch 통신 response = receipt 뷰에 넘겨줘야 하는 정보들 const [receiptData, setReceiptData] = useState(); + // 이미지 용량 이슈로 receipt 페이지로 넘어갈 때, 데이터 통신 완료 이전 로딩 스피너 보여주기 위해 쓰이는 플래그 const [receiptLoading, setReceiptLoading] = useState(false); - // 무통장 입금에서 '송금했어요' 버튼 클릭시의 핸들러 const handleClickCustomDepositBtn = async () => { const formData = new FormData(); @@ -80,12 +115,17 @@ const NoDesignCustomPage = () => { setReceiptLoading(true); try { - // 1. customInfo(커스텀 정보들) append + // 1. handDrawingImage(손 그림) append + if (handDrawingImage) { + formData.append('handDrawingImage', handDrawingImage); + } + + // 2. customInfo(커스텀 정보들) append const json = JSON.stringify(updatedCustomInfo); const blob = new Blob([json], { type: 'application/json' }); formData.append('customInfo', blob); - // 2. customImage(도안 이미지) append + // 3. customImage(도안 이미지) append if (customImages) { for (let i = 0; i < customImages.length; i++) { formData.append('customImages', customImages.item(i) as File); @@ -98,9 +138,9 @@ const NoDesignCustomPage = () => { }, }); - setReceiptData(data.data); if (data) { setReceiptLoading(false); + setReceiptData(data.data); setStep((prev: number) => prev + 1); } } catch (err) { @@ -121,43 +161,95 @@ const NoDesignCustomPage = () => { ); case 1: return ( - ); + case 2: return ( - + ); + + case 3: + return ( + + ); + + case 4: + return ( + + ); + + case 5: + return ( + ); - case 3: + case 6: return ( ); - case 4: + case 7: return receiptLoading ? ( ) : ( @@ -167,8 +259,8 @@ const NoDesignCustomPage = () => { /> ); - case 5: - return ; //haveDesign 임의로 추가해서 넘겨줬습니다 + case 8: + return ; } };