Skip to content

Commit

Permalink
Merge branch 'develop' into feat/#8
Browse files Browse the repository at this point in the history
  • Loading branch information
gominzip authored Aug 24, 2024
2 parents 690b2e4 + 1381dbc commit 814e941
Show file tree
Hide file tree
Showing 11 changed files with 246 additions and 66 deletions.
11 changes: 6 additions & 5 deletions src/apis/authAxois.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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); // 에러 전달
}
);

Expand Down
28 changes: 24 additions & 4 deletions src/apis/createTales.ts
Original file line number Diff line number Diff line change
@@ -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<string[]> => {
export const createKeyword = async (body: FormData): Promise<string[]> => {
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;
}
};
40 changes: 27 additions & 13 deletions src/components/tales/createMain/CreateMain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<>
<Header text="동화 만들기" />
<S.Wrapper>
<img src="/topGraphic.png" id="topGraphic" />
<S.SelectContainer>
<CommonTitle>동화를 만들어볼까요?</CommonTitle>
<S.OptionContainer>
<SelectOption text="사진으로" imgURL="/picture.png" />
<SelectOption text="글로" imgURL="/text.png" />
</S.OptionContainer>
</S.SelectContainer>
<img src="/btmGraphic.png" id="btmGraphic" />
</S.Wrapper>
<TabBar />
{isLoading ? (
<LoadingSpinner />
) : (
<>
<Header text="동화 만들기" />
<S.Wrapper>
<img src="/topGraphic.png" id="topGraphic" />
<S.SelectContainer>
<CommonTitle>동화를 만들어볼까요?</CommonTitle>
<S.OptionContainer>
<SelectOption
text="사진으로"
imgURL="/picture.png"
setIsLoading={setIsLoading}
/>
<SelectOption text="글로" imgURL="/text.png" />
</S.OptionContainer>
</S.SelectContainer>
<img src="/btmGraphic.png" id="btmGraphic" />
</S.Wrapper>
<TabBar />
</>
)}
</>
);
};
Expand Down
17 changes: 9 additions & 8 deletions src/components/tales/createMain/SelectOption/InputImg.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLInputElement>) => {
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 (
Expand Down
4 changes: 2 additions & 2 deletions src/components/tales/createMain/SelectOption/SelectOption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<S.ImgLabel htmlFor="imageInput">
<S.Wrapper>
<InputImg />
<InputImg setIsLoading={setIsLoading} />
<S.ImgBox>
<img src={imgURL} />
</S.ImgBox>
Expand Down
49 changes: 49 additions & 0 deletions src/components/tales/readTale/ReadTale.styled.ts
Original file line number Diff line number Diff line change
@@ -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;
`;
74 changes: 74 additions & 0 deletions src/components/tales/readTale/ReadTale.tsx
Original file line number Diff line number Diff line change
@@ -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<string | number | null>(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 (
<>
<Header text="동화 읽기" />
<S.Wrapper>
{data ? (
<>
<S.ReadTaleHead>
<S.TitleWrapper>
<S.Complete>동화가 완성되었어요!</S.Complete>
<S.Title>제목: 사과나무와 작은 동물들</S.Title>
</S.TitleWrapper>
<Dropdown
selectList={nationElements}
setter={setResult}
width="30%"
/>
</S.ReadTaleHead>
<S.TaleWrapper></S.TaleWrapper>
<S.BtnWrapper>
<NextBtn
width="48%"
isActive={true}
text="음성으로 듣기"
handleBtn={onClick}
/>
<NextBtn
width="48%"
isActive={true}
text="학습하기"
handleBtn={onClick}
/>
</S.BtnWrapper>
</>
) : (
<LoadingScreen text="동화" />
)}
</S.Wrapper>
</>
);
};

export default ReadTale;
56 changes: 30 additions & 26 deletions src/components/tales/selectKeyword/SelectKeyword.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
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[]
>([]);
const [isActive, setIsActive] = useState(false);
const [btnText, setBtnText] = useState<string>("단어를 선택해주세요");

const handleClick = (index: number) => {
useEffect(() => {
if (selectedKeywordIndices.length > 0) {
setIsActive(true);
setBtnText("다음");
}
}, [selectedKeywordIndices]);

const handleComponents = (index: number) => {
if (selectedKeywordIndices.includes(index)) {
setSelectedKeywordIndices(
selectedKeywordIndices.filter(
Expand All @@ -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 (
<>
<Header text="동화 만들기" />
Expand All @@ -44,27 +53,22 @@ const SelectKeyword = () => {
<CommonTitle>원하는 단어를 골라주세요</CommonTitle>
</S.TitleWrapper>
<S.KeywordWrapper>
{result.map((item, idx) => (
<S.Keyword
key={idx}
isSelected={selectedKeywordIndices.includes(idx)}
onClick={() => handleClick(idx)}
>
{item}
</S.Keyword>
))}
{keywords &&
keywords.map((item: string, idx: number) => (
<S.Keyword
key={idx}
isSelected={selectedKeywordIndices.includes(idx)}
onClick={() => handleComponents(idx)}
>
{item}
</S.Keyword>
))}
</S.KeywordWrapper>
<NextBtn
width="85%"
isActive={isActive}
text={btnText}
handleBtn={() => {
const selectedKeywords = selectedKeywordIndices.map(
(index) => result[index]
);
console.log(selectedKeywords);
console.log(selectedKeywordIndices);
}}
handleBtn={handleNextBtn}
/>
</S.Wrapper>
</>
Expand Down
Loading

0 comments on commit 814e941

Please sign in to comment.