diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..1588f65 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,24 @@ +--- +name: ๐Ÿ› Bug Report +about: ๋ฒ„๊ทธ๋ฅผ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค +title: "[Bug] " +labels: bug +assignees: '' +--- + +## ๐Ÿž ๋ฒ„๊ทธ ์„ค๋ช… +- ๋ฒ„๊ทธ์— ๋Œ€ํ•ด ๊ฐ„๋‹จํžˆ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”. + +## โœ… ์žฌํ˜„ ๋ฐฉ๋ฒ• +- ๋ฒ„๊ทธ๋ฅผ ์žฌํ˜„ํ•˜๋ ค๋ฉด ๋‹ค์Œ ๋‹จ๊ณ„๋ฅผ ๋”ฐ๋ฅด์„ธ์š”: + 1. [์˜ˆ: ํŽ˜์ด์ง€ ์ด๋™ ๊ฒฝ๋กœ] + 2. [์˜ˆ: ํŠน์ • ๋ฒ„ํŠผ ํด๋ฆญ] + 3. [์˜ˆ: ๊ธฐ๋Œ€๋˜๋Š” ๊ฒฐ๊ณผ] + +## ๐Ÿ–ฅ ํ™˜๊ฒฝ ์ •๋ณด +- OS: [Windows, macOS, Linux ๋“ฑ] +- Browser: [Chrome, Firefox ๋“ฑ] +- Version: [์•ฑ/๋ธŒ๋ผ์šฐ์ € ๋ฒ„์ „] + +## ๐Ÿ“ธ ์Šคํฌ๋ฆฐ์ƒท +- ๋ฒ„๊ทธ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ์Šคํฌ๋ฆฐ์ƒท์„ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š” (ํ•„์š”ํ•œ ๊ฒฝ์šฐ). diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..9880a67 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,16 @@ +--- +name: โœจ Feature Request +about: ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค +title: "[Feature] " +labels: enhancement +assignees: '' +--- + +## ๐ŸŒŸ ๊ธฐ๋Šฅ ์„ค๋ช… +- ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์€ ๊ธฐ๋Šฅ์ด๋‚˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊ฐ„๋žตํžˆ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”. + +## ๐Ÿค” ์ด์œ  +- ์ด ๊ธฐ๋Šฅ์ด ์™œ ํ•„์š”ํ•œ์ง€, ์–ด๋–ค ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š”์ง€ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”. + +## ๐Ÿ“‹ ์ถ”๊ฐ€ ์ •๋ณด +- ๊ธฐ๋Šฅ๊ณผ ๊ด€๋ จ๋œ ์ฐธ๊ณ  ์ž๋ฃŒ๋‚˜ ์ถ”๊ฐ€ ์ •๋ณด๋ฅผ ํฌํ•จํ•ด์ฃผ์„ธ์š”. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..3d75a26 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,34 @@ +## ๐Ÿ“‹ ์š”์•ฝ +- ์ด Pull Request์˜ ๋ชฉ์ ๊ณผ ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊ฐ„๋žตํžˆ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”. + +## ๐Ÿ›  ๋ณ€๊ฒฝ ์‚ฌํ•ญ +- ์ด๋ฒˆ Pull Request์—์„œ ์ž‘์—…ํ•œ ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: + - [x] ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ๋˜๋Š” ๋ฒ„๊ทธ ์ˆ˜์ • + - [x] ์ฝ”๋“œ ๋ฆฌํŒฉํ† ๋ง ๋˜๋Š” ์ตœ์ ํ™” + - [x] ๋ฌธ์„œ ์—…๋ฐ์ดํŠธ + +## ๐Ÿ”— ๊ด€๋ จ ์ด์Šˆ +- ์ด PR๋กœ ํ•ด๊ฒฐ๋˜๋Š” ์ด์Šˆ: #[์ด์Šˆ ๋ฒˆํ˜ธ] +- ๊ด€๋ จ๋œ ์ด์Šˆ: #[์ด์Šˆ ๋ฒˆํ˜ธ] + +## ๐Ÿ“ธ ์Šคํฌ๋ฆฐ์ƒท ๋˜๋Š” GIF (ํ•ด๋‹น๋˜๋Š” ๊ฒฝ์šฐ) +- UI ๋ณ€๊ฒฝ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋ณด์—ฌ์ฃผ๋Š” ์Šคํฌ๋ฆฐ์ƒท์ด๋‚˜ GIF๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”. + +## โœ… ์ฒดํฌ๋ฆฌ์ŠคํŠธ +- [ ] ์ฝ”๋“œ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ…Œ์ŠคํŠธํ–ˆ์Šต๋‹ˆ๋‹ค. +- [ ] ๊ด€๋ จ ๋ฌธ์„œ๋ฅผ ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜ ์—…๋ฐ์ดํŠธํ–ˆ์Šต๋‹ˆ๋‹ค. (ํ•ด๋‹น๋˜๋Š” ๊ฒฝ์šฐ) +- [ ] ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋ฐ˜์˜ํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. +- [ ] ๋ชจ๋“  ํ…Œ์ŠคํŠธ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ํ†ต๊ณผํ–ˆ์Šต๋‹ˆ๋‹ค. + +## ๐Ÿ›ก ํ…Œ์ŠคํŠธ ๋ฐฉ๋ฒ• +- ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•œ ํ…Œ์ŠคํŠธ ๋ฐฉ๋ฒ•์„ ๋‹จ๊ณ„๋ณ„๋กœ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”: + 1. ๋ธŒ๋žœ์น˜๋ฅผ ๋กœ์ปฌ๋กœ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. + 2. ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋˜๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค: + ``` + # ์˜ˆ์‹œ ๋ช…๋ น์–ด + ./run_tests.sh + ``` + 3. ์˜ˆ์ƒ ๋™์ž‘์ด๋‚˜ ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. + +## ๐Ÿ“š ์ถ”๊ฐ€ ์ฐธ๊ณ  ์‚ฌํ•ญ +- ์ด PR๊ณผ ๊ด€๋ จํ•ด ๋ฆฌ๋ทฐ์–ด๊ฐ€ ์•Œ์•„์•ผ ํ•  ์ถ”๊ฐ€ ๋‚ด์šฉ์ด ์žˆ๋‹ค๋ฉด ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”. diff --git a/src/App.jsx b/src/App.jsx index 90d4bac..0bfc956 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,21 +1,27 @@ import { BrowserRouter, Routes, Route } from "react-router-dom" import Layout from "./pages/layouts/Layout" import Ingredient from "./pages/ingredient/Ingregdient" +import Recipe from "./pages/recipe/Recipe" +import {AddRecipe} from "./pages/recipe/AddRecipe" import UsersIngredientItem from "./components/ingredient/UsersIngredientItem" function App() { - return ( - - - }> - - }/> - {/* } /> */} - - - - - ) -} + return ( + + + }> + + }/> + {/* } /> */} + + + } /> + } /> + + -export default App + + + ) +} +export default App \ No newline at end of file diff --git a/src/assets/image/noimage.jpg b/src/assets/image/noimage.jpg new file mode 100644 index 0000000..110d770 Binary files /dev/null and b/src/assets/image/noimage.jpg differ diff --git a/src/components/recipe/default.jsx b/src/components/recipe/default.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/pages/recipe/Recipe.jsx b/src/pages/recipe/Recipe.jsx new file mode 100644 index 0000000..b965de2 --- /dev/null +++ b/src/pages/recipe/Recipe.jsx @@ -0,0 +1,40 @@ +import { useEffect, useState,useCallback } from "react" +import { getRecipeList } from "../../sources/api/recipeAPI.jsx"; +import {Route, useNavigate} from "react-router-dom"; +import { AddRecipe} from "./addRecipe.jsx"; + +function Recipe() { + const [recipeList, setRecipeList] = useState([]) + const navigate = useNavigate(); + + useEffect(() => { + const fetchRecipes = async () => { + try { + const data = await getRecipeList(); + setRecipeList(data); // ์ƒํƒœ ์—…๋ฐ์ดํŠธ + console.log(recipeList); // ๊ฐ€์ ธ์˜จ ๋ฐ์ดํ„ฐ ํ™•์ธ + } catch (err) { + console.error(err); + } + }; + + fetchRecipes(); // ๋น„๋™๊ธฐ ์ž‘์—… ํ˜ธ์ถœ + }, []); + + const handleAdd = useCallback(() => { + navigate("/recipe/create"); + },[navigate]) + + return ( + <> +

๋ ˆ์‹œํ”ผ ํŽ˜์ด์ง€

+ + + ) +} + +export default Recipe \ No newline at end of file diff --git a/src/pages/recipe/addRecipe.jsx b/src/pages/recipe/addRecipe.jsx new file mode 100644 index 0000000..f04c1e3 --- /dev/null +++ b/src/pages/recipe/addRecipe.jsx @@ -0,0 +1,317 @@ + +import { useState } from 'react'; +import { createRecipe } from "../../sources/api/recipeAPI.jsx"; + +const initialState = { + recipeName: '', + recipeCookingTime: 0, + recipeDifficulty: 0, + recipeContent: '', + recipeSteps: [], + recipeCategoryPk: 0, + userPk: 0, +}; + +export const AddRecipe = () => { + + const [request, setRequest] = useState({ ...initialState }); + + const [recipeSources, setRecipeSources] = useState([]); + + const [recipeStepSources, setRecipeStepSources] = useState([]); + + const [result, setResult] = useState(null); + + const defaultUrl = 'https://picsum.photos/200/300'; + const handleClickAdd = () => { + + const formData = new FormData(); + + // request ๊ธฐ๋ณธ ๊ธ€์€ ๊ทธ๋ƒฅ ์ถ”๊ฐ€ + formData.append("request",new Blob([JSON.stringify(request)],{type:"application/json"})); + + // recipeSources ํŒŒ์ผ ๋ฐฐ์—ด ์ถ”๊ฐ€ + recipeSources.forEach(file => { + formData.append(`recipeSources`, file); // ๊ฐ™์€ ์ด๋ฆ„์œผ๋กœ ์„œ๋ฒ„์— ์ „๋‹ฌ + }); + + // recipeStepSources ์ถ”๊ฐ€ + recipeStepSources.forEach((files, stepIndex) => { + if(files) { + files.forEach(file => { + formData.append(`recipeStepSources`, file); + }) + } + }); + + createRecipe(formData) + .then(res => { + console.log(res); + setResult(res); + + // ์‚ฌ์šฉ ํ›„ ์ดˆ๊ธฐํ™” + setRequest({ ...initialState }); + setRecipeSources([]); + setRecipeStepSources([]); + }) + .catch(err => { + console.log(err); + }); + }; + + // ํŒŒ์ผ ์—…๋กœ๋“œ ์ฒ˜๋ฆฌ + const handleRecipeSourcesChange = (e) => { + setRecipeSources(Array.from(e.target.files)); // ์—ฌ๋Ÿฌ ํŒŒ์ผ์„ ๋ฐฐ์—ด๋กœ ์ถ”๊ฐ€ + }; + + + const handleInputChange = (e) => { + const { name, value } = e.target; + setRequest(prevState => ({ + ...prevState, + [name]: value, + })); + }; + + const addStep = (index) => { + setRequest((prevState) => { + const updatedSteps = [...prevState.recipeSteps]; + const newStep = { + recipeStepOrder: index + 2, + recipeStepContent: '', + }; + + updatedSteps.splice(index + 1, 0, newStep); // ํด๋ฆญ๋œ ์Šคํ… ์•„๋ž˜ ์‚ฝ์ž… + + // ์ „์ฒด ์ˆœ์„œ ์—…๋ฐ์ดํŠธ + const updatedOrderedSteps = updatedSteps.map((step, idx) => ({ + ...step, + recipeStepOrder: idx + 1, // ์ˆœ์„œ ์žฌ์ •๋ ฌ + })); + + return { + ...prevState, + recipeSteps: updatedOrderedSteps, + }; + }); + + // recipeStepSources์—๋„ ๋นˆ ๊ฐ’ ์ถ”๊ฐ€ + setRecipeStepSources((prevSources) => { + const updatedSources = [...prevSources]; + updatedSources.splice(index + 1, 0, null); // ํด๋ฆญ๋œ ์Šคํ… ์•„๋ž˜์— ๋นˆ ๊ฐ’ ์‚ฝ์ž… + return updatedSources; + }); + + }; + + const removeStep = (index) => { + setRequest((prevState) => { + const updatedSteps = [...prevState.recipeSteps]; + updatedSteps.splice(index, 1); // ํ•ด๋‹น ์ธ๋ฑ์Šค ์Šคํ… ์‚ญ์ œ + + // ์ˆœ์„œ ์žฌ์ •๋ ฌ + const updatedOrderedSteps = updatedSteps.map((step, idx) => ({ + ...step, + recipeStepOrder: idx + 1, + })); + + return { + ...prevState, + recipeSteps: updatedOrderedSteps, + }; + }); + + // recipeStepSources์—์„œ๋„ ํ•ด๋‹น ์ธ๋ฑ์Šค์˜ ์ด๋ฏธ์ง€ ์‚ญ์ œ + setRecipeStepSources((prevSources) => { + const updatedSources = [...prevSources]; + updatedSources.splice(index, 1); // ํ•ด๋‹น ์ธ๋ฑ์Šค ์‚ญ์ œ + return updatedSources; + }); + }; + + const handleStepChange = (e, index) => { + const { name, value } = e.target; + setRequest(prevState => { + const updatedSteps = [...prevState.recipeSteps]; + updatedSteps[index] = { ...updatedSteps[index], [name]: value }; + return { + ...prevState, + recipeSteps: updatedSteps, + }; + }); + }; + + const handleRecipeStepSourcesChange = (e, index) => { + const files = Array.from(e.target.files||[]); // ์—…๋กœ๋“œ๋œ ํŒŒ์ผ ๋ฆฌ์ŠคํŠธ + + setRecipeStepSources((prevSources) => { + const updatedSources = [...prevSources]; + updatedSources[index] = files.length > 0 ? files : null; // ํŒŒ์ผ์ด ์žˆ์œผ๋ฉด ์—…๋ฐ์ดํŠธ, ์—†์œผ๋ฉด null + return updatedSources; + }); + }; + + const printRecipeStepSources = () => { + console.log(recipeStepSources); + } + return ( +
+
+

๋ ˆ์‹œํ”ผ ์‚ฌ์ง„ : + + + +

+
+
+

๋ ˆ์‹œํ”ผ ์ด๋ฆ„ : + +

+
+ + +
+

์กฐ๋ฆฌ ์‹œ๊ฐ„ : + +

+
+ +
+

๋ ˆ์‹œํ”ผ ๊ฐ„๋‹จํ•œ ๋‚ด์šฉ : + +

+
+
+

์นดํ…Œ๊ณ ๋ฆฌ : + +

+
+
+

์œ ์ € ๋ฒˆํ˜ธ: + +

+
+ + + {/* ๋‹จ๊ณ„๋ณ„ ์ž…๋ ฅ */} + + + {request.recipeSteps.map((step, index) => ( +
+

์Šคํ… {index + 1} : + handleStepChange(e, index)} + placeholder={`์Šคํ… ${index + 1}`} + /> +

+ {/* ์ˆจ๊ฒจ์ง„ ํŒŒ์ผ ์„ ํƒ ์ž…๋ ฅ */} + handleRecipeStepSourcesChange(e, index)} + style={{display: 'none'}} // ์ˆจ๊ธฐ๊ธฐ + /> + + + + + {/* ๋ผ๋ฒจ์„ ํด๋ฆญํ•˜๋ฉด ํŒŒ์ผ ์ฐฝ ์—ด๊ธฐ */} + + +
+

+ +
+ ))} + + + + +
+ ); +}; \ No newline at end of file diff --git a/src/sources/api/recipeAPI.jsx b/src/sources/api/recipeAPI.jsx new file mode 100644 index 0000000..21404ce --- /dev/null +++ b/src/sources/api/recipeAPI.jsx @@ -0,0 +1,18 @@ +import axios from "axios"; + +export const API_URL_HOST = "http://localhost:8080"; + +const prefix = `${API_URL_HOST}/recipe`; + +export const getRecipeList = async () => { + const url = `${prefix}`; + const res = await axios.get(url); + return res.data; +} + +export const createRecipe = async (recipe) => { + console.log(recipe); + const url = `${prefix}`; + const res = await axios.post(url, recipe); + return res.data; +} \ No newline at end of file