import type { CommonCode } from '@/lib/domain/common-code'; import { MAX_PRODUCT_BADGES, type MerchantProductBadge } from '@/lib/domain/merchant-product'; /** 기획 BO-MAL-004 ② — 필수는 메뉴명·가격·노출 여부 셋. */ export const PRODUCT_NAME_MAX_LENGTH = 100; /** 기획 BO-MAL-004 ⑤ [의사결정 필요] 잠정값 — 설명 최대 글자수. */ export const PRODUCT_DESCRIPTION_MAX_LENGTH = 120; export const PRODUCT_PRICE_MAX = 99_999_999; export const PRODUCT_SORT_ORDER_MAX = 9999; export type MerchantProductValues = { frcsNo: string; name: string; price: number | null; description: string; badges: MerchantProductBadge[]; isVisible: boolean; sortOrder: number | null; imageFileId: string; }; export type MerchantProductFormErrors = Partial< Record<'name' | 'price' | 'description' | 'badges' | 'sortOrder' | 'image', string> >; export type MerchantProductFormState = | { status: 'idle' } | { status: 'error'; message?: string; errors?: MerchantProductFormErrors } | { status: 'success' }; export const INITIAL_MERCHANT_PRODUCT_FORM_STATE: MerchantProductFormState = { status: 'idle', }; type ValidationResult = | { ok: true; values: MerchantProductValues } | { ok: false; errors: MerchantProductFormErrors }; export function validateMerchantProduct( values: MerchantProductValues, badgeCodes: readonly CommonCode[] ): ValidationResult { const errors: MerchantProductFormErrors = {}; const name = values.name.trim(); if (!name) { errors.name = '메뉴명을 입력해 주세요.'; } else if (name.length > PRODUCT_NAME_MAX_LENGTH) { errors.name = `메뉴명은 ${PRODUCT_NAME_MAX_LENGTH}자 이내로 입력해 주세요.`; } if ( values.price === null || !Number.isInteger(values.price) || values.price < 0 || values.price > PRODUCT_PRICE_MAX ) { errors.price = '가격은 숫자만 입력해 주세요.'; } const description = values.description.trim(); if (description.length > PRODUCT_DESCRIPTION_MAX_LENGTH) { errors.description = `설명은 ${PRODUCT_DESCRIPTION_MAX_LENGTH}자 이내로 입력해 주세요.`; } const badges = values.badges.filter((badge) => badgeCodes.some((code) => code.code === badge)); if (badges.length > MAX_PRODUCT_BADGES) { errors.badges = `배지는 최대 ${MAX_PRODUCT_BADGES}개까지 고를 수 있습니다.`; } if ( values.sortOrder !== null && (!Number.isInteger(values.sortOrder) || values.sortOrder < 0 || values.sortOrder > PRODUCT_SORT_ORDER_MAX) ) { errors.sortOrder = `노출 순서는 0 이상 ${PRODUCT_SORT_ORDER_MAX} 이하의 숫자로 입력해 주세요.`; } if (Object.keys(errors).length > 0) { return { ok: false, errors }; } return { ok: true, values: { ...values, name, description, badges } }; }