diff --git a/src/apis/authAxois.ts b/src/apis/authAxois.ts index 1c3692b..d027a11 100644 --- a/src/apis/authAxois.ts +++ b/src/apis/authAxois.ts @@ -54,11 +54,12 @@ export const getAuthAxios = (token: string | null) => { return response; }, async (error) => { - alert("토큰이 만료되었습니다. 재로그인 후 시도해주세요!"); - LocalStorage.removeItem("access"); - LocalStorage.removeItem("refresh"); - window.location.href = "/login"; - return Promise.reject(error); // 에러 전달 + throw error; + // alert("토큰이 만료되었습니다. 재로그인 후 시도해주세요!"); + // LocalStorage.removeItem("access"); + // LocalStorage.removeItem("refresh"); + // window.location.href = "/login"; + // return Promise.reject(error); // 에러 전달 } ); diff --git a/src/apis/createTales.ts b/src/apis/createTales.ts index ea7b74b..d6ff0a2 100644 --- a/src/apis/createTales.ts +++ b/src/apis/createTales.ts @@ -1,21 +1,41 @@ import LocalStorage from "@utils/localStorage"; import { getAuthAxios } from "./authAxois"; +import { CreateTaleData } from "@type/createTale"; +// import { Server } from "./settings"; const baseURL = import.meta.env.VITE_PUBLIC_SERVER_URL; -export const createWithImg = async (body: FormData): Promise => { +export const createKeyword = async (body: FormData): Promise => { try { - const access = LocalStorage.getItem('access'); + const access = LocalStorage.getItem("access"); const authAxios = getAuthAxios(access); const response = await authAxios.post(`${baseURL}/tales/keyword`, body, { headers: { "Content-Type": "multipart/form-data", }, }); - console.log(response.data); + + // response에서 keyword만 추출하여 배열에 담아 return + const keywords: string[] = response.data.result.map( + (item: { keyword: string }) => item.keyword + ); + + return keywords; + } catch (error) { + alert( + "이미지 화질이 안좋거나, 추출된 키워드가 없습니다. 다시 이미지를 업로드하세요!" + ); + throw error; + } +}; + +export const createTale = async (body: CreateTaleData) => { + try { + const access = LocalStorage.getItem("access"); + const authAxios = getAuthAxios(access); + const response = await authAxios.post(`${baseURL}/tales/`, body); return response.data; } catch (error) { - alert("동화 생성 중 문제가 발생했습니다. 잠시 후 다시 시도해주세요."); throw error; } }; diff --git a/src/components/tales/createMain/CreateMain.tsx b/src/components/tales/createMain/CreateMain.tsx index e58406f..e9d3c08 100644 --- a/src/components/tales/createMain/CreateMain.tsx +++ b/src/components/tales/createMain/CreateMain.tsx @@ -3,23 +3,37 @@ import * as S from "./CreateMain.styled"; import SelectOption from "./SelectOption/SelectOption"; import TabBar from "@components/common/tabBar/TabBar"; import { CommonTitle } from "@components/common/common.styled"; +import { useState } from "react"; +import LoadingSpinner from "@components/common/spinner/LoadingSpinner"; const CreateMain = () => { + const [isLoading, setIsLoading] = useState(false); + return ( <> -
- - - - 동화를 만들어볼까요? - - - - - - - - + {isLoading ? ( + + ) : ( + <> +
+ + + + 동화를 만들어볼까요? + + + + + + + + + + )} ); }; diff --git a/src/components/tales/createMain/SelectOption/InputImg.tsx b/src/components/tales/createMain/SelectOption/InputImg.tsx index 83b1dab..21b839d 100644 --- a/src/components/tales/createMain/SelectOption/InputImg.tsx +++ b/src/components/tales/createMain/SelectOption/InputImg.tsx @@ -1,19 +1,20 @@ import { ChangeEvent } from "react"; import * as S from "./SelectOption.styled"; -import { createWithImg } from "../../../../apis/createTales"; +import { createKeyword } from "../../../../apis/createTales"; +import { useNavigate } from "react-router-dom"; +import { InputImgProps } from "@type/selectOption"; + +const InputImg = ({ setIsLoading }: InputImgProps) => { + const navigate = useNavigate(); -const InputImg = () => { const insertImg = async (e: ChangeEvent) => { const file = e.target.files?.[0]; if (file) { const formData = new FormData(); formData.append("file", file); - try { - const result = await createWithImg(formData); - console.log(result); - } catch (error) { - console.error("파일 업로드 중 오류 발생:", error); - } + if (setIsLoading) setIsLoading(true); + const keywords: string[] = await createKeyword(formData); + navigate("/selectKeyword", { state: { keywords } }); } }; return ( diff --git a/src/components/tales/createMain/SelectOption/SelectOption.tsx b/src/components/tales/createMain/SelectOption/SelectOption.tsx index 8b24708..8ba7978 100644 --- a/src/components/tales/createMain/SelectOption/SelectOption.tsx +++ b/src/components/tales/createMain/SelectOption/SelectOption.tsx @@ -2,12 +2,12 @@ import { SelectOptionProps } from "@type/selectOption"; import * as S from "./SelectOption.styled"; import InputImg from "./InputImg"; -const SelectOption = ({ text, imgURL }: SelectOptionProps) => { +const SelectOption = ({ text, imgURL, setIsLoading }: SelectOptionProps) => { if (text.includes("사진")) { return ( - + diff --git a/src/components/tales/readTale/ReadTale.styled.ts b/src/components/tales/readTale/ReadTale.styled.ts new file mode 100644 index 0000000..9c2a398 --- /dev/null +++ b/src/components/tales/readTale/ReadTale.styled.ts @@ -0,0 +1,49 @@ +import styled from "styled-components"; + +export const Wrapper = styled.div` + width: 95%; + height: 90%; + overflow-y: hidden; + display: flex; + flex-direction: column; + justify-content: space-between; +`; + +export const ReadTaleHead = styled.div` + width: 100%; + display: flex; + justify-content: space-between; + padding: 2.8rem 0; +`; + +export const TitleWrapper = styled.div` + display: flex; + flex-direction: column; + gap: 1rem; +`; + +export const Title = styled.div` + font-size: 2.5rem; + font-weight: 800; +`; + +export const Complete = styled.div` + font-size: 1.7rem; + font-weight: 500; + color: #f7a300; +`; + +export const TaleWrapper = styled.div` + width: 100%; + height: 75%; + max-height: 75%; + overflow-y: scroll; + font-size: 1.8rem; + margin-bottom: 1rem; +`; + +export const BtnWrapper = styled.div` + width: 100%; + display: flex; + justify-content: space-between; +`; diff --git a/src/components/tales/readTale/ReadTale.tsx b/src/components/tales/readTale/ReadTale.tsx new file mode 100644 index 0000000..d345b52 --- /dev/null +++ b/src/components/tales/readTale/ReadTale.tsx @@ -0,0 +1,74 @@ +import Header from "@components/common/header/Header"; +import * as S from "./ReadTale.styled"; +import Dropdown from "@components/common/dropDown/Dropdown"; +import { nationElements } from "@pages/OnboardingPage"; +import { useEffect, useState } from "react"; +import NextBtn from "@components/common/NextBtn"; +import LoadingScreen from "@components/common/spinner/LoadingScreen"; +import { createTale } from "@apis/createTales"; +import { useLocation } from "react-router-dom"; + +const ReadTale = () => { + const location = useLocation(); + const { requestData } = location.state || {}; + + const [result, setResult] = useState(null); + const [data, setData] = useState(null); + + const onClick = () => { + console.log(result); + }; + + useEffect(() => { + const getTale = async () => { + if (requestData) { + const response = await createTale(requestData); + setData(response); + console.log(response); + } + }; + getTale(); + }, []); + + return ( + <> +
+ + {data ? ( + <> + + + 동화가 완성되었어요! + 제목: 사과나무와 작은 동물들 + + + + + + + + + + ) : ( + + )} + + + ); +}; + +export default ReadTale; diff --git a/src/components/tales/selectKeyword/SelectKeyword.tsx b/src/components/tales/selectKeyword/SelectKeyword.tsx index 6fc77e4..c0125ad 100644 --- a/src/components/tales/selectKeyword/SelectKeyword.tsx +++ b/src/components/tales/selectKeyword/SelectKeyword.tsx @@ -1,12 +1,15 @@ import Header from "@components/common/header/Header"; import * as S from "./SelectKeyword.styeld"; -import keywordData from "./data.json"; import NextBtn from "@components/common/NextBtn"; import { useEffect, useState } from "react"; import { CommonTitle } from "@components/common/common.styled"; +import { useLocation, useNavigate } from "react-router-dom"; const SelectKeyword = () => { - const result = keywordData.result.map((i) => i.keyword); + const location = useLocation(); + const navigate = useNavigate(); + + const { keywords } = location.state || {}; const [selectedKeywordIndices, setSelectedKeywordIndices] = useState< number[] @@ -14,7 +17,14 @@ const SelectKeyword = () => { const [isActive, setIsActive] = useState(false); const [btnText, setBtnText] = useState("단어를 선택해주세요"); - const handleClick = (index: number) => { + useEffect(() => { + if (selectedKeywordIndices.length > 0) { + setIsActive(true); + setBtnText("다음"); + } + }, [selectedKeywordIndices]); + + const handleComponents = (index: number) => { if (selectedKeywordIndices.includes(index)) { setSelectedKeywordIndices( selectedKeywordIndices.filter( @@ -28,13 +38,12 @@ const SelectKeyword = () => { } }; - useEffect(() => { - if (selectedKeywordIndices.length > 0) { - setIsActive(true); - setBtnText("다음"); - } - }, [selectedKeywordIndices]); - + const handleNextBtn = () => { + const selectKeywords = selectedKeywordIndices.map( + (index) => keywords[index] + ); + navigate("/taleDetail", { state: { selectKeywords } }); + }; return ( <>
@@ -44,27 +53,22 @@ const SelectKeyword = () => { 원하는 단어를 골라주세요 - {result.map((item, idx) => ( - handleClick(idx)} - > - {item} - - ))} + {keywords && + keywords.map((item: string, idx: number) => ( + handleComponents(idx)} + > + {item} + + ))} { - const selectedKeywords = selectedKeywordIndices.map( - (index) => result[index] - ); - console.log(selectedKeywords); - console.log(selectedKeywordIndices); - }} + handleBtn={handleNextBtn} /> diff --git a/src/components/tales/taleDetail/TaleDetail.tsx b/src/components/tales/taleDetail/TaleDetail.tsx index 9341323..114835b 100644 --- a/src/components/tales/taleDetail/TaleDetail.tsx +++ b/src/components/tales/taleDetail/TaleDetail.tsx @@ -4,8 +4,15 @@ import Dropdown from "@components/common/dropDown/Dropdown"; import { useEffect, useState } from "react"; import NextBtn from "@components/common/NextBtn"; import { nationElements } from "@utils/defaultData"; +import { useLocation, useNavigate } from "react-router-dom"; + const TaleDetail = () => { + const location = useLocation(); + const { selectKeywords } = location.state || {}; + + const navigate = useNavigate(); + const [mood, setMood] = useState(null); const [characters, setCharacters] = useState(null); const [contents, setContents] = useState(null); @@ -13,7 +20,8 @@ const TaleDetail = () => { const [btnText, setBtnText] = useState("단어를 선택해주세요"); const result: (string | number | null)[] = [mood, characters, contents]; - + const detail = "구체적"; + const chr = ["최재영"]; const isFormValid = () => result.every((value) => value !== null); useEffect(() => { @@ -28,16 +36,13 @@ const TaleDetail = () => { const handleBtn = async () => { const requestData = { + detail, + keywords: selectKeywords, mood, - characters, + characters: chr, contents, }; - - try { - console.log(requestData); - } catch (error) { - throw error; - } + navigate("/readTale", { state: { requestData } }); }; return ( diff --git a/src/router.tsx b/src/router.tsx index ee9b189..aad4de8 100644 --- a/src/router.tsx +++ b/src/router.tsx @@ -8,6 +8,7 @@ import KakaoRedirect from "@pages/KakaoRedirect"; import LearningPage from "@pages/LearningPage"; import SelectKeywordPage from "@pages/SelectKeywordPage"; import TaleDetailPage from "@pages/TaleDetailPage"; +import ReadTale from "@components/tales/readTale/ReadTale"; const router = createBrowserRouter([ { @@ -46,6 +47,10 @@ const router = createBrowserRouter([ path: "/taleDetail", element: , }, + { + path: "/readTale", + element: , + }, ]); export default router; diff --git a/src/type/createTale.d.ts b/src/type/createTale.d.ts new file mode 100644 index 0000000..c5e6138 --- /dev/null +++ b/src/type/createTale.d.ts @@ -0,0 +1,7 @@ +export interface CreateTaleData { + detail: string; + keywords?: string[]; + mood: string | number | null; + characters: string | number | null[]; + contents: string | number | null; +}