-
Notifications
You must be signed in to change notification settings - Fork 1
✨ feat: 내 가게 정보 등록/편집 (사장님) 페이지 구현 #119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/pages/store/StoreEdit.tsx
Outdated
| const [edit, setEdit] = useState<StoreEditForm>( | ||
| initialData | ||
| ? { | ||
| name: initialData.name, | ||
| category: initialData.category, | ||
| address1: initialData.address1, | ||
| address2: initialData.address2, | ||
| description: initialData.description, | ||
| originalHourlyPay: initialData.originalHourlyPay, | ||
| imageUrl: initialData.imageUrl, | ||
| } | ||
| : { | ||
| name: '', | ||
| category: '', | ||
| address1: '', | ||
| address2: '', | ||
| description: '', | ||
| originalHourlyPay: 0, | ||
| imageUrl: '', | ||
| }, | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| const [edit, setEdit] = useState<StoreEditForm>( | |
| initialData | |
| ? { | |
| name: initialData.name, | |
| category: initialData.category, | |
| address1: initialData.address1, | |
| address2: initialData.address2, | |
| description: initialData.description, | |
| originalHourlyPay: initialData.originalHourlyPay, | |
| imageUrl: initialData.imageUrl, | |
| } | |
| : { | |
| name: '', | |
| category: '', | |
| address1: '', | |
| address2: '', | |
| description: '', | |
| originalHourlyPay: 0, | |
| imageUrl: '', | |
| }, | |
| ); | |
| const [edit, setEdit] = useState<StoreEditForm>({ | |
| name: initialData?.name ?? '', | |
| category: initialData?.category ?? '', | |
| address1: initialData?.address1 ?? '', | |
| address2: initialData?.address2 ?? '', | |
| description: initialData?.description ?? '', | |
| originalHourlyPay: initialData?.originalHourlyPay ?? 0, | |
| imageUrl: initialData?.imageUrl ?? '', | |
| }); |
이게 좀더 보기 쉬울것 같습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오 그렇네요 감사합니다! 변경할게요
src/components/common/Dropdown.tsx
Outdated
| interface DropdownProps<T extends string> { | ||
| options: readonly T[]; | ||
| selected: T; | ||
| selected: T | ''; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
에고 그 주영님께서 null로 사용하자는 피드백 주셔서 제가 null로 수정했는데 ㅠㅠ 혹시 드롭다운 요소 초기값을 null로 변경 가능하실까요?
좀 복잡하면 그냥 제가 ''로 되돌리겠습니다! 크게 상관은 없는것 같아서!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
엇.. 그렇군요 ㅠ
src/pages/store/StoreEdit.tsx
Outdated
| <textarea | ||
| id="description" | ||
| value={edit.description} | ||
| onChange={handleChange('description')} | ||
| placeholder="입력" | ||
| className="h-153 w-full rounded-[5px] border border-gray-30 bg-white px-20 py-16 text-body1/26 font-regular text-black" | ||
| /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| <textarea | |
| id="description" | |
| value={edit.description} | |
| onChange={handleChange('description')} | |
| placeholder="입력" | |
| className="h-153 w-full rounded-[5px] border border-gray-30 bg-white px-20 py-16 text-body1/26 font-regular text-black" | |
| /> | |
| <textarea | |
| id="description" | |
| value={edit.description} | |
| onChange={handleChange('description')} | |
| placeholder="입력" | |
| className="h-153 w-full resize-none rounded-[5px] border border-gray-30 bg-white px-20 py-16 text-body1/26 font-regular text-black" | |
| /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
앗 까먹고 있었네요 감사합니다!
src/pages/store/StoreEdit.tsx
Outdated
| </div> | ||
| </div> | ||
|
|
||
| <div className="mb-20 flex flex-col gap-20 md:mb-24 md:gap-24"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
보통 input을 포함하고 해당 값들을 전달하는 버튼이 필요한 부분은 form으로 감싸주는게 좋습니다!
또한 form의 onSubmit 속성을 활용하면 enter를 눌러서 등록버튼을 클릭할 수도 있습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 알겠습니다! 변경해보겠습니다!
src/pages/store/StoreEdit.tsx
Outdated
|
|
||
| <div className="mb-20 flex flex-col gap-20 md:mb-24 md:gap-24"> | ||
| <div className="flex flex-col gap-20 md:flex-row md:gap-20"> | ||
| <div className="flex flex-col gap-8 md:max-w-472 md:basis-1/2"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이부분 저도 고민했던 부분인데 grid로 하니까 편하더라구요! 이건 그냥 참고만 하세요! 방법은 여러가지니 ㅎㅎ
<div className="grid grid-cols-1 md:grid-cols-2">There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
앗 그리드도 좋은 방법이네요 ㅎㅎ
ghost
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
구현하시느라 고생하셨습니다!
Moon-ju-young
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
페이지에 너무 중첩된 div 태그들이 많아요...!! 그리고 통일성도 없는 것 같아요 (어디는 간격이 gap이고 어디는 margin 이고...) flexbox로도 가능은 하지만 grid로 작성하는 편이 훨씬 깔끔하고 div 태그를 줄일 수 있을 것 같습니다
src/pages/store/StoreEdit.tsx
Outdated
| import { useLocation, useNavigate } from 'react-router-dom'; | ||
| import Input from '@/components/common/Input'; | ||
| import Dropdown from '@/components/common/Dropdown'; | ||
| import { ADDRESS_OPTIONS, CATEGORY_OPTIONS } from '@/constants/dropdownOptions'; | ||
| import Close from '@/assets/icons/close.svg'; | ||
| import { useCallback, useContext, useEffect, useMemo, useState } from 'react'; | ||
| import { | ||
| postShop, | ||
| putShop, | ||
| type ShopItem, | ||
| type ShopRequest, | ||
| } from '@/api/shopApi'; | ||
| import ImageInput from '@/components/common/ImageInput'; | ||
| import Button from '@/components/common/Button'; | ||
| import Modal from '@/components/common/Modal'; | ||
| import { getPresignedUrl, uploadImageToS3 } from '@/api/imageApi'; | ||
| import { AuthContext } from '@/context/AuthContext'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💬 import 문을 비슷한 요소끼리 정렬해 놓으면 좀 더 이해하기 편할 것 같습니다~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 정리 해두겠습니다!
src/pages/store/StoreEdit.tsx
Outdated
| const location = useLocation(); | ||
| const initialData = location.state as InitialData; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💬 아마 location.state를 통해 구현하신 걸 보면 navigate에서 데이터를 넘겨주는 것이지 않나 싶은데 이렇게 될 경우엔 주소 직접 치고 들어올 때는 데이터가 있는데 없다고 인식하는 경우가 있지 않을까 싶네요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 그래서 api에서 가져오는 방식으로 수정했습니다!
src/pages/store/StoreEdit.tsx
Outdated
| const [edit, setEdit] = useState<StoreEditForm>( | ||
| initialData | ||
| ? { | ||
| name: initialData.name, | ||
| category: initialData.category, | ||
| address1: initialData.address1, | ||
| address2: initialData.address2, | ||
| description: initialData.description, | ||
| originalHourlyPay: initialData.originalHourlyPay, | ||
| imageUrl: initialData.imageUrl, | ||
| } | ||
| : { | ||
| name: '', | ||
| category: '', | ||
| address1: '', | ||
| address2: '', | ||
| description: '', | ||
| originalHourlyPay: 0, | ||
| imageUrl: '', | ||
| }, | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| const [edit, setEdit] = useState<StoreEditForm>( | |
| initialData | |
| ? { | |
| name: initialData.name, | |
| category: initialData.category, | |
| address1: initialData.address1, | |
| address2: initialData.address2, | |
| description: initialData.description, | |
| originalHourlyPay: initialData.originalHourlyPay, | |
| imageUrl: initialData.imageUrl, | |
| } | |
| : { | |
| name: '', | |
| category: '', | |
| address1: '', | |
| address2: '', | |
| description: '', | |
| originalHourlyPay: 0, | |
| imageUrl: '', | |
| }, | |
| ); | |
| const [edit, setEdit] = useState<StoreEditForm>( | |
| initialData ?? { | |
| name: '', | |
| category: '', | |
| address1: '', | |
| address2: '', | |
| description: '', | |
| originalHourlyPay: 0, | |
| imageUrl: '', | |
| }, | |
| ); |
💬 이런 방식도 가능할 것 같습니다~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오 좋습니다! 변경할게요!
src/pages/store/StoreEdit.tsx
Outdated
| const formatNumber = useMemo( | ||
| () => (value: string) => { | ||
| return Number(value).toLocaleString(); | ||
| }, | ||
| [], | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💬 여기서 useCallback이 아니라 useMemo를 쓰신 이유가 있으실까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
앗 제가 따로 따로 작업하다보니 다른 부분이랑 통일을 못했네요! 변경해두겠습니다!
src/pages/store/StoreEdit.tsx
Outdated
| { key: 'address2', label: '상세 주소' }, | ||
| { key: 'originalHourlyPay', label: '기본 시급' }, | ||
| { key: 'imageUrl', label: '가게 이미지' }, | ||
| { key: 'description', label: '가게 설명' }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ 가게 설명도 필수인가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
필수 부분은 아닌걸로 아는데 있는게 좋을 것 같아 넣었습니다! 요건 빼도록 하겠습니다!
src/pages/store/StoreEdit.tsx
Outdated
| case 'warning': | ||
| default: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| case 'warning': | |
| default: | |
| default: |
💬 간단하게 이렇게 작성해도 될 듯 합니다~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
변경했습니다!
src/pages/store/StoreEdit.tsx
Outdated
| <div className="mb-20 flex flex-col gap-20 md:mb-24 md:flex-row md:gap-20"> | ||
| <div className="md:max-w-472 md:basis-1/2"> | ||
| <Input | ||
| label="가게 이름*" | ||
| value={edit.name} | ||
| onChange={handleChange('name')} | ||
| /> | ||
| </div> | ||
| <div className="flex flex-col gap-8 md:max-w-472 md:basis-1/2"> | ||
| <label className="text-body1/26 font-regular text-black"> | ||
| 분류* | ||
| </label> | ||
| <Dropdown<Category> | ||
| options={CATEGORY_OPTIONS} | ||
| selected={edit.category} | ||
| setSelect={(value) => | ||
| setEdit((prev) => ({ ...prev, category: value as Category })) | ||
| } | ||
| variant="form" | ||
| /> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div className="mb-20 flex flex-col gap-20 md:mb-24 md:gap-24"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ input 에 대해서 굳이 div 태그를 두 부분으로 분리하신 이유가 있으실까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
드롭다운 부분에 따로 라벨 props가 없어서 div 감싸는 걸 먼저 작업 후에 태블릿,데스크탑 사이즈를 작업하다보니 두 개로 나눈 것 같습니다! 요건 수정하겠습니다
src/pages/store/StoreEdit.tsx
Outdated
| unit="원" | ||
| /> | ||
| </div> | ||
| <div className="hidden md:block md:max-w-472 md:basis-1/2"></div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ 이 요소는 왜 있는 걸까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
확인했습니다~ grid를 활용한다면 빈 div 태그 없이 처리 할 수도 있을 것 같네요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 그리드로 바꿔도 좋을 것 같습니다!
src/pages/store/StoreEdit.tsx
Outdated
| value={ | ||
| edit.originalHourlyPay === 0 | ||
| ? '' | ||
| : formatNumber(String(edit.originalHourlyPay)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💬 formatNumber 가 한 번 밖에 쓰이지 않는 것 같아 굳이 함수로 분리하지 않아도 될 것 같네요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 알겠습니다!
src/pages/store/StoreEdit.tsx
Outdated
|
|
||
| const isEditMode = | ||
| initialData !== undefined && typeof initialData?.id === 'string'; | ||
| const shopId = localStorage.getItem('shopId'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ 저희 로컬스토리지에 shopId가 있나요??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아 이건 제가 편집 모드 들어가기 위해서 조회 부분에서 생성해서 가져오는 식으로 해서 있었습니다.
src/pages/store/StoreEdit.tsx
Outdated
| setEdit({ | ||
| name: shopInfo.item.name ?? '', | ||
| category: shopInfo.item.category ?? null, | ||
| address1: shopInfo.item.address1 ?? null, | ||
| address2: shopInfo.item.address2 ?? '', | ||
| description: shopInfo.item.description ?? '', | ||
| originalHourlyPay: shopInfo.item.originalHourlyPay ?? 0, | ||
| imageUrl: shopInfo.item.imageUrl ?? '', | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| setEdit({ | |
| name: shopInfo.item.name ?? '', | |
| category: shopInfo.item.category ?? null, | |
| address1: shopInfo.item.address1 ?? null, | |
| address2: shopInfo.item.address2 ?? '', | |
| description: shopInfo.item.description ?? '', | |
| originalHourlyPay: shopInfo.item.originalHourlyPay ?? 0, | |
| imageUrl: shopInfo.item.imageUrl ?? '', | |
| }); | |
| setEdit(shopInfo.item); |
💬 이렇게 줄일 수 있을 것 같아요~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오 좋네요! 바꾸겠습니다
src/pages/store/StoreEdit.tsx
Outdated
| return; | ||
| } | ||
|
|
||
| if (isEditMode && !shopId) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ isEditMode 가 shopId에 의해 결정되는데 둘을 같이 체크하는 이유를 모르겠습니다! 이렇게 된다면 항상 거짓이 아닌가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
에러 메세지 끄려고 만들었는데 중복이였네요! 삭제하겠습니다!
src/pages/store/StoreEdit.tsx
Outdated
| edit.originalHourlyPay === 0 | ||
| ? '' | ||
| : formatNumber(String(edit.originalHourlyPay)) | ||
| : Number(edit.originalHourlyPay).toLocaleString() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| : Number(edit.originalHourlyPay).toLocaleString() | |
| : edit.originalHourlyPay.toLocaleString() |
💬 originalHourlyPay가 원래 number형이라 이대로 써도 될 것 같습니다~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 알겠습니다!
src/pages/store/StoreEdit.tsx
Outdated
| <div className="mb-24 flex items-center justify-between md:mb-32"> | ||
| <div className="flex flex-col gap-24 px-12 pt-40 pb-80 md:gap-32 md:px-32 md:pb-60 lg:mx-auto lg:max-w-964 lg:px-0"> | ||
| <div className="flex items-center justify-between"> | ||
| <h2 className="text-h3/25 font-bold text-black md:text-h1"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| <h2 className="text-h3/25 font-bold text-black md:text-h1"> | |
| <h2 className="text-h3/25 font-bold text-black md:text-h1/24"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
h1/24 이부분은 28인것 같습니다! 100%로 나온 부분이라
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오우 오타가 났네요 24가 아니라 34나 35가 맞을 것 같습니다!
src/pages/store/StoreEdit.tsx
Outdated
| }; | ||
|
|
||
| return ( | ||
| <div className="min-h-screen bg-gray-5"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❗ 이 부분은 진우님께도 피드백 드린 부분인데 Nav 컴포넌트가 페이지 바깥에 있어서 h-screen 같은 스타일을 사용하면 실제 화면 크기보다 높이가 더 높아져 불필요한 스크롤이 생겨버립니다! 수정 부탁드려요~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 수정했습니다!



📌 변경 사항 개요
내 가게 정보 등록/편집 (사장님)(
/owner/store/edit) 구현 완료📝 상세 내용
초기 데이터 유무에 따른 상태 분기
아무 값이 없으면 등록 / 값이 있으면 수정 페이지를 보여줍니다.
예외 상황 처리
'로그인이 필요합니다.'라는 모달을 보여준 뒤 로그인 페이지로 이동시킵니다.
각 태그 이름을 보여주고 '내용을 추가해 주세요' 라는 모달을 보여줍니다.
모달 버튼 타입
success는 가게 상세 페이지 이동, auth는 로그인 페이지 이동, warning은 단순 확인으로 동작합니다.
imageApi에 백엔드 에러메세지가 없어서 수정 / S3 업로드에 api로 잘못 요청한 부분 axios로 변경했습니다.
🔗 관련 이슈
🖼️ 스크린샷(선택사항)
반응형


빈 값일 때 모달

전체 입력

등록 완료

수정 페이지

수정 완료

수정된 모습

💡 참고 사항