-
Notifications
You must be signed in to change notification settings - Fork 4
✨ Feat: 가게 정보/등록페이지 초안 #68
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
Changes from 3 commits
e46f19d
f95d38c
662a4f9
99cfeb6
82a6523
f1733a5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| import axios from '@/lib/axios'; | ||
| import RegisterFormData from '@/types/myShop'; | ||
| import { default as originAxios } from 'axios'; | ||
|
|
||
| export async function postShop(body: RegisterFormData) { | ||
| const { address1, address2, category, description, name, originalHourlyPay, image } = body; | ||
|
|
||
| const imageUrl = image | ||
| ? `https://bootcamp-project-api.s3.ap-northeast-2.amazonaws.com/${image.name}` | ||
| : ''; | ||
|
|
||
| const tmpBody = { | ||
| address1, | ||
| address2, | ||
| category, | ||
| description, | ||
| name, | ||
| originalHourlyPay, | ||
| imageUrl, | ||
| }; | ||
| const { data } = await axios.post('/shops', tmpBody); | ||
| } | ||
|
|
||
| export async function postPresignedUrl(imageName: string) { | ||
| const { data } = await axios.post('/images', { name: imageName }); | ||
| console.log(data); | ||
| return data.item.url; | ||
| } | ||
|
|
||
| export async function uploadImage(presignedUrl: string, file: File) { | ||
| const result = await originAxios.put(presignedUrl, file); | ||
| } | ||
|
|
||
| export async function getPresignedUrl(presignedUrl: string) { | ||
| // 1. URL 객체 생성 | ||
| const url = new URL(presignedUrl); | ||
|
|
||
| // 2. 쿼리 파라미터를 제거 (URL 객체의 search 속성을 비움) | ||
| url.search = ''; | ||
|
|
||
| // 3. 쿼리 파라미터가 제거된 새 URL 문자열을 얻습니다. | ||
| const baseUrl = url.toString(); | ||
|
|
||
| const result = await originAxios.get(baseUrl); | ||
| } | ||
|
|
||
| export async function getShop(shopId: string) { | ||
| const { data } = await axios.get(`/shops/${shopId}`); | ||
| return data; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| import { Dropdown, Input } from '@/components/ui'; | ||
| import { ADDRESS_CODE } from '@/constants/dropdown'; | ||
| import RegisterFormData from '@/types/myShop'; | ||
|
|
||
| interface Props { | ||
| formData: RegisterFormData; | ||
| handleChange: (key: keyof RegisterFormData, value: string) => void; | ||
| } | ||
|
|
||
| const RegisterAddress = ({ formData, handleChange }: Props) => { | ||
| return ( | ||
| <> | ||
| <div className='flex flex-col gap-6 tablet:flex-row'> | ||
| <div className='flex flex-col gap-2 tablet:w-1/2'> | ||
| <div> | ||
| <span>주소</span> | ||
| <span className='ml-0.5 text-red-500'>*</span> | ||
| </div> | ||
| <Dropdown | ||
| name='staus' | ||
| ariaLabel='어드레스' | ||
| placeholder='선택' | ||
| values={ADDRESS_CODE} | ||
| selected={formData.address1} | ||
| onChange={val => handleChange('address1', val)} | ||
| className='w-full' | ||
| /> | ||
| </div> | ||
| <div className='flex flex-col gap-2 tablet:w-1/2'> | ||
| <div> | ||
| <span>상세 주소</span> | ||
| <span className='ml-0.5 text-red-500'>*</span> | ||
| </div> | ||
| <Input | ||
| value={formData.address2} | ||
| onChange={e => handleChange('address2', e.target.value)} | ||
| placeholder='입력' | ||
| /> | ||
| </div> | ||
| </div> | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| export default RegisterAddress; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import RegisterFormData from '@/types/myShop'; | ||
|
|
||
| interface Props { | ||
| formData: RegisterFormData; | ||
| handleChange: (key: keyof RegisterFormData, value: string) => void; | ||
| } | ||
|
|
||
| const RegisterDescription = ({ formData, handleChange }: Props) => { | ||
| return ( | ||
| <> | ||
| <div className='flex flex-col gap-2'> | ||
| <span>가게 설명</span> | ||
| <textarea | ||
| value={formData.description} | ||
| placeholder='입력' | ||
| className='h-[153px] resize-none rounded-md border border-gray-300 px-5 py-4' | ||
|
||
| onChange={e => handleChange('description', e.target.value)} | ||
| ></textarea> | ||
| </div> | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| export default RegisterDescription; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| import { Icon } from '@/components/ui'; | ||
| import Image from 'next/image'; | ||
| import { ChangeEvent } from 'react'; | ||
|
|
||
| interface Props { | ||
| preview: string | null; | ||
| handleImageChange: (e: ChangeEvent<HTMLInputElement>) => void; | ||
| } | ||
|
|
||
| const RegisterImage = ({ preview, handleImageChange }: Props) => { | ||
| return ( | ||
| <> | ||
| <div className='flex flex-col gap-2'> | ||
| <span>가게 이미지</span> | ||
| <label className='relative flex h-[200px] w-full cursor-pointer flex-col items-center justify-center overflow-hidden rounded-xl border border-gray-300 tablet:h-[276px] tablet:w-[483px]'> | ||
| {preview ? ( | ||
| <Image src={preview} alt='미리보기' fill /> | ||
| ) : ( | ||
| <> | ||
| <Icon | ||
| iconName='camera' | ||
| iconSize='lg' | ||
| ariaLabel='카메라 아이콘' | ||
| className='bg-gray-400' | ||
| /> | ||
| <p className='text-gray-400'>이미지 추가하기</p> | ||
| </> | ||
| )} | ||
| <input type='file' accept='image/*' className='hidden' onChange={handleImageChange} /> | ||
| </label> | ||
| </div> | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| export default RegisterImage; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| import { Modal } from '@/components/ui'; | ||
| import { useRouter } from 'next/router'; | ||
|
|
||
| interface Props { | ||
| openWarning: boolean; | ||
| setOpenWarning: (value: boolean) => void; | ||
| openCancel: boolean; | ||
| setOpenCancel: (value: boolean) => void; | ||
| openConfirm: boolean; | ||
| setOepnConfirm: (value: boolean) => void; | ||
| } | ||
|
|
||
| const RegisterModal = ({ | ||
| openWarning, | ||
| setOpenWarning, | ||
| openCancel, | ||
| setOpenCancel, | ||
| openConfirm, | ||
| setOepnConfirm, | ||
| }: Props) => { | ||
| const router = useRouter(); | ||
| return ( | ||
| <> | ||
| <Modal | ||
| open={openWarning} | ||
| onClose={() => setOpenWarning(false)} | ||
| variant='warning' | ||
| title='필수 항목을 작성해주세요.' | ||
| primaryText='확인' | ||
| onPrimary={() => setOpenWarning(false)} | ||
| /> | ||
| <Modal | ||
| open={openCancel} | ||
| onClose={() => setOpenCancel(false)} | ||
| variant='warning' | ||
| title='취소하시겠습니까?' | ||
| primaryText='아니요' | ||
| secondaryText='예' | ||
| onSecondary={() => { | ||
| setOpenCancel(false); | ||
| router.push('/my-shop'); | ||
| }} | ||
| onPrimary={() => setOpenCancel(false)} | ||
| /> | ||
| <Modal | ||
| open={openConfirm} | ||
| onClose={() => setOepnConfirm(false)} | ||
| variant='success' | ||
| title='등록이 완료되었습니다.' | ||
| primaryText='확인' | ||
| onPrimary={() => router.push('/my-shop')} | ||
| /> | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| export default RegisterModal; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| import { Dropdown, Input } from '@/components/ui'; | ||
| import { CATEGORY_CODE } from '@/constants/dropdown'; | ||
| import RegisterFormData from '@/types/myShop'; | ||
|
|
||
| interface Props { | ||
| formData: RegisterFormData; | ||
| handleChange: (key: keyof RegisterFormData, value: string) => void; | ||
| } | ||
|
|
||
| const RegisterName = ({ formData, handleChange }: Props) => { | ||
| return ( | ||
| <> | ||
| <div className='flex flex-col gap-6 tablet:flex-row'> | ||
| <div className='flex flex-col gap-2 tablet:w-1/2'> | ||
| <div> | ||
| <span>가게 이름</span> | ||
| <span className='ml-0.5 text-red-500'>*</span> | ||
| </div> | ||
| <Input | ||
| value={formData.name} | ||
| onChange={e => handleChange('name', e.target.value)} | ||
| placeholder='입력' | ||
| /> | ||
| </div> | ||
| <div className='flex flex-col gap-2 tablet:w-1/2'> | ||
| <div> | ||
| <span>분류</span> | ||
| <span className='ml-0.5 text-red-500'>*</span> | ||
| </div> | ||
| <Dropdown | ||
| name='staus' | ||
| ariaLabel='카테고리' | ||
| placeholder='선택' | ||
| values={CATEGORY_CODE} | ||
| selected={formData.category} | ||
| onChange={val => handleChange('category', val)} | ||
| className='w-full' | ||
| /> | ||
| </div> | ||
| </div> | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| export default RegisterName; |
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.
dropdown 은 label 이 없지만 기본 input 은 label과 requiredMark 옵션이 있기에 활용해도 좋을것 같습니다 (
이외 다른 인풋도 동일하게 활용한다면 코드 작성량이 줄어들것 같습니다!
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.
작업 초반에 label 부분 font-size 가 달라서 저렇게 작업했는데 지금 다시 해보니 잘되네요 😂 다시 수정했습니다.
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.
아 저도 그부분 font-size 가 달랐어서 제가 저번에 수정하고 PR 올렸습니다!