들어가기
Owner의 restaurant page에서 식당의 menu를
추가하는 page 및 component
menu의 option받는거 떄문에 복잡해짐.
집중할것!!
import { gql, useMutation } from '@apollo/client'
import React, { useState } from 'react'
import { useForm } from 'react-hook-form'
import { useNavigate, useParams } from 'react-router-dom'
import Button from '../../components/button'
import {
CreateDishMutation,
CreateDishMutationVariables,
} from '../../graphql/__generated__'
import { MY_RESTAURANT_QUERY } from './my-restaurant'
const CREATE_DISH_MUTATION = gql`
mutation createDish($input: CreateDishInput!) {
createDish(input: $input) {
ok
error
}
}
`
///server의 createDish API를 호출함..
interface IParams {
restaurantId: string
}
///createDish의 input의 type을 설정해줌.
interface IForm {
name: string
price: string
description: string
[key: string]: string //그외 싸그라니
}
///createDish의 variables들의 type을 설정해줌.
///option부분은 [key:string]로 해줌.
const AddDish = () => {
const { restaurantId } = useParams() as unknown as IParams
///path가 path: '/restaurants/:restaurantId/add-dish'
///임으로 restaurnatId를 useParam를 이용해서 받아옴.
const navigate = useNavigate()
const [createDishMutation, { loading }] = useMutation<
CreateDishMutation,
CreateDishMutationVariables
>(CREATE_DISH_MUTATION, {
refetchQueries: [
{
query: MY_RESTAURANT_QUERY,
variables: {
input: {
id: +restaurantId,
},
},
},
],
})
///createDish mutation을 실행 후, my restaurantDetail
///page를 refetch해줌.
///만들어진 menu가 화면에 바로 나타날 수 있게~
const { register, handleSubmit, getValues, setValue } = useForm<IForm>({
mode: 'onChange',
})
///react-hook-form!!!
const onSubmit = () => {
const { name, price, description, ...rest } = getValues()
///option부분은 ...rest로 받음.
console.log(rest)
///rest를 찍어보면 무엇이 나오는지 알 수 있음.
const optionsObject = optionsNumber.map((id) => ({
name: rest[`${id}-optionName`],
extra: +rest[`${id}-optionExtra`],
}))
///option부분은 optionsObject로 만들어줌.
///반드시 console을 찍어서 모양을 확인할 것.
console.log(optionsObject)
///createDishMutation에 variables들을 넣어줌.
///options 부분은 optionsObject를 만들어서 넣어줌.
///restaurantId는 usePaprams로 따옴.
createDishMutation({
variables: {
input: {
name,
price: +price,
description,
restaurantId: +restaurantId,
options: optionsObject,
},
},
})
navigate(-1)
///createDish후 page를 myRestaurant page로 되돌려놓음
///생성항 dish확인 가능함
}
///여기서 부터 엄청중요...
///dish생성방법.
///options의 갯수를 배열(숫자로 된)로 지정해서 useState로 만든다.
///default값은 빈배열로!!
const [optionsNumber, setOptionsNumber] = useState<number[]>([])
const onAddOptionClick = () => {
setOptionsNumber((current) => [Date.now(), ...current])
}
///option 추가 버튼을 누를경우, 실행되는 함수.
///현재의 배열에 Date.now()을 숫자로 된 것이
///배열에 추가된다.
const onDeleteClick = (idToDelete: number) => {
setOptionsNumber((currnet) => currnet.filter((id) => id !== idToDelete))
setValue(`${idToDelete}-optionName`, '')
setValue(`${idToDelete}-optionExtra`, '')
}
///추가할려던 option을 제거하는 함수.
///옵션Delete를 클릭했을때, 클릭한 option의 id를 idToDelete로
///받아와서 setOptionsNumber에 들어있는 option들 중,
///id가 다른것은 없애는 작업.
///setValue부분은 delete한 option의 값을 비워준다.
return (
<div className="max-w-screen-sm mx-auto flex flex-col items-center mt-52">
<h4 className="font-semibold text-2xl mb-3">음식 추가</h4>
<form
onSubmit={handleSubmit(onSubmit)}
className="grid gap-3 mt-3 mb-5 w-full"
>
<input
{...register('name', {
required: 'name is required',
maxLength: { value: 30, message: 'max char is 30' },
})}
required
maxLength={30}
placeholder="name"
className="input"
/>
///menu name input react-hook-form
<input
{...register('price', { required: 'price is required' })}
name="price"
placeholder="price"
className="input"
type="number"
min={0}
/>
///menu price input react-hook-form
<input
{...register('description', { required: 'description is required' })}
name="description"
placeholder="description"
className="input"
type="text"
/>
///menu description input react-hook-form
<div className="my-10">
<h4 className="font-medium mb-3 text-lg">음식 추가 옵션</h4>
<span
className="cursor-pointer text-white bg-gray-400 hover:bg-gray-600 py-1 px-2 mt-5 rounded-md"
onClick={onAddOptionClick}
>
///위에서 만든 option 추가 버튼
///optionsNumber가 추가됨.
Add Option
</span>
///위에서 useState로 만든 optionsNumer를
///map으로 뿌려줌.
{optionsNumber.length !== 0 &&
optionsNumber.map((id) => (
<div key={id} className="mt-5">
<input
{...register(`${id}-optionName`)}
className="ml-5 py-2 px-4 focus:outline-none mr-3 focus:border-gray-600 border-2"
placeholder="Option Name"
type="text"
/>
///option의 name을 입력하는 부분.
///register되는 name을 집중해서 봐둘것.
///`${id}-optionName`으로 option name이
///설정됨.
<input
{...register(`${id}-optionExtra`)}
className="ml-5 py-2 px-4 focus:outline-none focus:border-gray-600 border-2"
placeholder="Option Extra"
min={0}
type="number"
defaultValue={0}
/>
///option의 extra가 입력하는 부분.
///register되는 name을 집중해서 봐둘것.
///`${id}-optionExtra`으로 option name이
///설정됨.
///extra이기 떄문에 defaultValue는 0으로
///설정함.
<span
className="cursor-pointer ml-3 text-white bg-red-400 hover:bg-red-600 py-1 px-2 mt-5 rounded-md"
onClick={() => onDeleteClick(id)}
>
Delete Option
</span>
///option옆의 Delete 버튼을 누르면, option이
///사라지게 하는 버튼.
///클릭시 그 옵션의 id를 담아서
///onDeleteClick 함수로(id => idToDelete)
///보낸다.
///id를 담아서 보낼때에는 ()=> 의
///함수 형식을 취한다.
</div>
))}
</div>
<Button loading={loading} actionText="Make Dish" />
</form>
</div>
)
}
export default AddDish
매우 어려운부분 ㅠㅠㅠ
마르고 닳도록 볼것!!