Skip to content
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

[가게 등록] 모바일 가게 등록 페이지 로직 및 API 구현 #37

Merged
merged 29 commits into from
Jan 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
7a625e8
fix: react-query 버전 업 에러 수정
dooohun Dec 21, 2023
8e6b9e4
feat: 카테고리 선택 로직 추가
dooohun Dec 21, 2023
3c5b982
feat: 모바일 사진 등록 로직 추가
dooohun Dec 22, 2023
c44b581
feat: 가게 정보를 전역 상태로 관리
dooohun Dec 22, 2023
93a2376
feat: 가게 등록 이미지를 전역으로 수정
dooohun Dec 22, 2023
e6ee1e4
feat: 가게 등록 이미지의 변수명 수정
dooohun Dec 22, 2023
3fa2df8
feat: 가게명, 주소를 전역 상태로 관리
dooohun Dec 22, 2023
4072ff2
feat: 모바일 세부 정보 입력 페이지 기능 추가
dooohun Dec 22, 2023
53fd0e6
fix: 배달 비용 타입을 숫자로 변경
dooohun Dec 25, 2023
152f33f
refactor: 카테고리 이름 및 아이디를 전역 변수로 수정
dooohun Dec 25, 2023
3f53304
feat: 입력 정보를 저장하는 기능 추가
dooohun Dec 25, 2023
290a8cc
feat: 가게 정보 확인 페이지 기능 구현
dooohun Dec 25, 2023
23d8253
fix: UI가 겹치지 않게 수정
dooohun Dec 25, 2023
b8518aa
refactor: 가게명 전역 상태를 name으로 통일
dooohun Dec 25, 2023
5ee2c4d
refactor: 모바일과 PC에서 입력한 값이 공유되도록 수정
dooohun Dec 25, 2023
3fd3f65
Merge branch 'develop' of https://github.com/BCSDLab/KOIN_OWNER_WEB i…
dooohun Dec 31, 2023
d800b9d
feat: 전화번호 입력 시 올바른 포맷으로 변경하는 기능 추가
dooohun Dec 31, 2023
3042e66
feat: image_urls 추가
dooohun Dec 31, 2023
7f5f43f
refactor: 배달 금액 초기값 수정
dooohun Dec 31, 2023
0d1b09a
refactor: 배달가격 초기값을 빈칸으로 수정
dooohun Dec 31, 2023
be6b147
feat: 가게 정보 미입력 시 에러 처리 기능 추가
dooohun Jan 4, 2024
03f24d5
feat: 메인 메뉴를 설명하는 alt값 추가
dooohun Jan 4, 2024
06e25fb
refactor: 이미지 업로드 훅이 shopRegistrationStore에 종속되지 않도록 수정
dooohun Jan 4, 2024
4c82225
refactor: 이미지를 비율과 크기에 맞도록 수정
dooohun Jan 4, 2024
9a45abf
refactor: imgUrl에서 imageFile로 변수명 수정
dooohun Jan 4, 2024
68fa091
refactor: 카테고리 이미지의 alt값 수정
dooohun Jan 4, 2024
bca13ec
refactor: 메인 메뉴 이미지의 alt값 수정
dooohun Jan 4, 2024
39da30a
refactor: alert 대신 주석으로 수정
dooohun Jan 4, 2024
2a96d20
Merge branch 'develop' into feature/#32
dooohun Jan 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/model/shopInfo/ownerShop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Shop } from './allShopInfo';
export const OwnerShop = Shop.omit({ id: true }).extend({
address: z.string(),
description: z.string(),
delivery_price: z.string(),
delivery_price: z.number(),
image_urls: z.array(z.string()),
});

Expand Down
36 changes: 26 additions & 10 deletions src/page/ShopRegistration/component/InputBox/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { UseFormRegister } from 'react-hook-form';
import { OwnerShop } from 'model/shopInfo/ownerShop';
import { HTMLInputTypeAttribute, useState } from 'react';
import { ChangeEvent, HTMLInputTypeAttribute } from 'react';
import useShopRegistrationStore from 'store/shopRegistration';
import styles from './InputBox.module.scss';

interface InputBoxProps {
Expand All @@ -12,21 +13,36 @@ interface InputBoxProps {

function formatPhoneNumber(inputNumber: string) {
const phoneNumber = inputNumber.replace(/\D/g, '');

const formattedPhoneNumber = phoneNumber.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3');

return formattedPhoneNumber;
}

export default function InputBox({
content, id, register, inputType,
}: InputBoxProps) {
const [formattedPhoneNumber, setFormattedPhoneNumber] = useState('');
const {
setAddress, setPhone, setDeliveryPrice, setDescription,
} = useShopRegistrationStore();

const {
address, phone, deliveryPrice, description,
} = useShopRegistrationStore();

const handleValueChange = (e: ChangeEvent<HTMLInputElement>) => {
const inputValue = e.target.value;
if (id === 'address') setAddress(inputValue);
if (id === 'delivery_price') setDeliveryPrice(inputValue);
if (id === 'description') setDescription(inputValue);
if (id === 'phone') {
setPhone(formatPhoneNumber(inputValue));
}
};

const handlePhoneChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const inputNumber = e.target.value;
const formattedNumber = formatPhoneNumber(inputNumber);
setFormattedPhoneNumber(formattedNumber);
const getValue = () => {
if (id === 'address') return address;
if (id === 'delivery_price') return deliveryPrice;
if (id === 'description') return description;
return phone;
};
return (
<label htmlFor={id} className={styles.form}>
Expand All @@ -36,8 +52,8 @@ export default function InputBox({
id={id}
className={styles.form__input}
{...register(id)}
onChange={inputType === 'tel' ? handlePhoneChange : undefined}
value={inputType === 'tel' ? formattedPhoneNumber : undefined}
onChange={handleValueChange}
value={getValue()}
/>
</label>
);
Expand Down
28 changes: 15 additions & 13 deletions src/page/ShopRegistration/component/Modal/Category/index.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,36 @@
import useShopCategory from 'query/shopCategory';
import { useState } from 'react';
import cn from 'utils/ts/className';
import useModalStore from 'store/modalStore';
import { Category as CategoryProps } from 'model/category/storeCategory';
import useShopRegistrationStore from 'store/shopRegistration';
import styles from './Category.module.scss';

export default function Category() {
const [selectedCategory, setSelectedCategory] = useState('');
const { categoryList } = useShopCategory();
const { setCategoryState } = useModalStore();
const { category, setCategory, setCategoryId } = useShopRegistrationStore();

const handleCategoryClick = (category: CategoryProps) => {
setSelectedCategory(category.name);
setCategoryState([category.name, category.id]);
const handleCategoryClick = (categoryInfo: CategoryProps) => {
setCategory(categoryInfo.name);
setCategoryId(categoryInfo.id);
};

return (
<div className={styles.category}>
{categoryList?.shop_categories.filter((_, index) => index > 0).map((category) => (
{categoryList?.shop_categories.filter((_, index) => index > 0).map((categoryInfo) => (
<button
className={cn({
[styles.category__menu]: true,
[styles['category__menu--selected']]: category.name === selectedCategory,
[styles['category__menu--selected']]: categoryInfo.name === category,
})}
type="button"
onClick={() => { handleCategoryClick(category); }}
key={category.id}
onClick={() => { handleCategoryClick(categoryInfo); }}
key={categoryInfo.id}
>
<img className={styles.category__image} src={category.image_url} alt="" />
<span className={styles.category__type}>{category.name}</span>
<img
className={styles.category__image}
src={categoryInfo.image_url}
alt={categoryInfo.name}
/>
<span className={styles.category__type}>{categoryInfo.name}</span>
</button>
))}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import useModalStore from 'store/modalStore';
import useShopRegistrationStore from 'store/shopRegistration';
import styles from './ConfirmShop.module.scss';

interface ShopInfo {
Expand All @@ -14,7 +14,7 @@ interface ConfirmShopProps {
}

export default function ConfirmShop({ open, onCancel, selectedShop }: ConfirmShopProps) {
const { setSearchShopState: setSearchStoreState, setSelectedShopId } = useModalStore();
const { setName } = useShopRegistrationStore();
if (!open) return null;
return (
<div className={styles.container}>
Expand All @@ -29,8 +29,7 @@ export default function ConfirmShop({ open, onCancel, selectedShop }: ConfirmSho
type="button"
className={styles['container__select-button']}
onClick={() => {
setSearchStoreState(selectedShop.name);
setSelectedShopId(selectedShop.id);
setName(selectedShop.name);
onCancel();
}}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
&__time-picker {
display: flex;
justify-content: center;

&--selected {
color: #c5c5c5;
}
}

&__checkbox {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import useStepStore from 'store/useStepStore';
import TimePicker from 'page/ShopRegistration/component/TimePicker';
import { WEEK } from 'utils/constant/week';
import { createPortal } from 'react-dom';
import useModalStore from 'store/modalStore';
import cn from 'utils/ts/className';
import styles from './OperateTimeMobile.module.scss';

interface OperateTimeMobileProps {
Expand All @@ -13,6 +15,17 @@ interface OperateTimeMobileProps {

export default function OperateTimeMobile({ isOpen, closeModal }: OperateTimeMobileProps) {
const step = useStepStore((state) => state.step);
const { shopClosedState } = useModalStore();

const handleShopClosedChange = (day: string) => {
useModalStore.setState((prev) => ({
...prev,
shopClosedState: {
...prev.shopClosedState,
[day]: !prev.shopClosedState[day],
},
}));
};

if (!isOpen) return null;
return createPortal(
Expand All @@ -36,12 +49,23 @@ export default function OperateTimeMobile({ isOpen, closeModal }: OperateTimeMob
{WEEK.map((day) => (
<tr className={styles.table__data} key={day}>
<td>{day}</td>
<td className={styles['table__time-picker']}>
<td className={cn({
[styles['table__time-picker']]: true,
[styles['table__time-picker--selected']]: shopClosedState[day],
})}
>
<TimePicker operatingDay={day} isOpenTimePicker />
{' ~ '}
<TimePicker operatingDay={day} isOpenTimePicker={false} />
</td>
<td><input type="checkbox" className={styles.table__checkbox} /></td>
<td>
<input
type="checkbox"
onChange={() => handleShopClosedChange(day)}
className={styles.table__checkbox}
checked={shopClosedState[day]}
/>
</td>
</tr>
))}
</tbody>
Expand Down
2 changes: 1 addition & 1 deletion src/page/ShopRegistration/hooks/useOperateTimeState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default function useOperateTimeState() {
useEffect(() => {
setOperateTimeState((prevOperateTimeState) => ({
...prevOperateTimeState,
holiday: `매주 ${WEEK.filter((day) => shopClosedState[day]).join('요일 ')}요일 정기 휴무`,
holiday: `매주 ${WEEK.filter((day) => shopClosedState[day]).join(' ')} 정기 휴무`,
time: `${openTimeState[openDay]} ~ ${closeTimeState[openDay]}`,
}));
}, [openTimeState, closeTimeState, shopClosedState, openDay]);
Expand Down
27 changes: 27 additions & 0 deletions src/page/ShopRegistration/view/Mobile/Main/Main.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,33 @@
}
}

&__image-upload {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
cursor: pointer;
width: 295px;
height: 200px;
border: 1px solid #858585;
margin-bottom: 32px;
}

&__upload-file {
display: none;
}

&__main-menu {
max-width: 295px;
max-height: 200px;
}

&__text {
margin-top: 8px;
font-size: 14px;
color: #858585;
}

&__label {
display: flex;
justify-content: space-between;
Expand Down
49 changes: 43 additions & 6 deletions src/page/ShopRegistration/view/Mobile/Main/index.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,60 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { ReactComponent as EmptyImgIcon } from 'assets/svg/shopRegistration/mobile-empty-img.svg';
import useStepStore from 'store/useStepStore';
import useImageUpload from 'utils/hooks/useImageUpload';
import useShopRegistrationStore from 'store/shopRegistration';
import { useEffect } from 'react';
import styles from './Main.module.scss';

export default function Main() {
const { increaseStep } = useStepStore();
const { imageFile, imgRef, saveImgFile } = useImageUpload();
const {
name, setName, address, setAddress, imageUrl, setImageUrl,
} = useShopRegistrationStore();

useEffect(() => {
if (imageFile !== '') setImageUrl(imageFile);
}, [imageFile]);
return (
<div className={styles.form}>
<div className={styles.form__img}>
<EmptyImgIcon />
<div>등록된 이미지가 없습니다.</div>
</div>
<label className={styles['form__image-upload']} htmlFor="mainMenuImage">
<input
type="file"
accept="image/*"
id="mainMenuImage"
className={styles['form__upload-file']}
onChange={saveImgFile}
ref={imgRef}
/>
{imageUrl
? <img src={imageUrl} className={styles['form__main-menu']} alt="메인 메뉴" />
: (
<>
<EmptyImgIcon />
<span className={styles.form__text}>등록된 이미지가 없습니다.</span>
</>
)}
</label>
<label htmlFor="name" className={styles.form__label}>
가게명
<input type="text" id="name" className={styles.form__input} />
<input
type="text"
id="name"
onChange={(e) => setName(e.target.value)}
value={name}
className={styles.form__input}
/>
</label>
<label htmlFor="address" className={styles.form__label}>
주소정보
<input type="text" id="address" className={styles.form__input} />
<input
type="text"
id="address"
onChange={(e) => setAddress(e.target.value)}
value={address}
className={styles.form__input}
/>
</label>
<div className={styles.form__button}>
<button type="button" onClick={increaseStep}>다음</button>
Expand Down
30 changes: 20 additions & 10 deletions src/page/ShopRegistration/view/Mobile/ShopCategory/index.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,42 @@
import { useState } from 'react';
import useStepStore from 'store/useStepStore';
import useShopCategory from 'query/shopCategory';
import cn from 'utils/ts/className';
import { Category as CategoryProps } from 'model/category/storeCategory';
import useShopRegistrationStore from 'store/shopRegistration';
import styles from './ShopCategory.module.scss';

type Category = string;

export default function ShopCategory() {
const { categoryList } = useShopCategory();
const { increaseStep } = useStepStore();
const [selectedCategory, setSelectedCategory] = useState<Category | null>(null);
const {
category, setCategory, setCategoryId,
} = useShopRegistrationStore();

const handleCategoryClick = (categoryInfo: CategoryProps) => {
setCategory(categoryInfo.name);
setCategoryId(categoryInfo.id);
};

return (
<div className={styles.category}>
<div className={styles.category__title}>카테고리를 골라주세요.</div>
<div className={styles.category__wrapper}>
{categoryList?.shop_categories.filter((_, index) => index > 0).map((category) => (
{categoryList?.shop_categories.filter((_, index) => index > 0).map((categoryInfo) => (
<button
className={cn({
[styles.category__menu]: true,
[styles['category__menu--selected']]: category.name === selectedCategory,
[styles['category__menu--selected']]: categoryInfo.name === category,
})}
type="button"
onClick={() => setSelectedCategory(category.name)}
key={category.id}
onClick={() => handleCategoryClick(categoryInfo)}
key={categoryInfo.id}
>
<img className={styles.category__image} src={category.image_url} alt="" />
<span className={styles.category__type}>{category.name}</span>
<img
className={styles.category__image}
src={categoryInfo.image_url}
alt={categoryInfo.name}
/>
<span className={styles.category__type}>{categoryInfo.name}</span>
</button>
))}
</div>
Expand Down
Loading
Loading