File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
export const NAME_MAX_LENGTH = 100;
export const BRAND_MAX_LENGTH = 100;
export const OWNER_MAX_LENGTH = 50;
export const ADDRESS_DETAIL_MAX_LENGTH = 200;
export const MEMO_MAX_LENGTH = 500;
export const ACCOUNT_NUMBER_MAX_LENGTH = 30;
export interface MerchantValues {
id: string;
/** 백엔드에 컬럼이 없어 저장되지 않는다 — 기획 시안 유지를 위해 화면에는 남긴다. */
brandName: string;
memo: string;
name: string;
categoryCode: string;
businessNumber: string;
ownerName: string;
phoneNumber: string;
address: string;
addressDetail: string;
postalCode: string;
latitude: string;
longitude: string;
/** 백엔드 등록 요청 VO(`MngrVocInsertReqVo`)의 예금주명·입금은행코드·입금계좌번호·업태·구분. */
bankHolderName: string;
bankCode: string;
accountNumber: string;
businessStatusCode: string;
divisionCode: string;
isVisible: boolean;
}
export type MerchantFormErrors = Partial<
Record<
'name' | 'categoryCode' | 'phoneNumber' | 'address' | 'postalCode' | 'businessNumber' | 'accountNumber',
string
>
>;
export type MerchantFormState =
| { status: 'idle' }
| { status: 'error'; message?: string; errors?: MerchantFormErrors }
| { status: 'success' };
export const INITIAL_MERCHANT_FORM_STATE: MerchantFormState = { status: 'idle' };
export const REQUIRED_MESSAGE = '필수입력 사항을 입력하세요.';
/** 사업자등록번호는 선택이지만, 넣었다면 형태를 지킨다(기획 예시 `000-00-00000`). */
const BUSINESS_NUMBER_PATTERN = /^\d{3}-?\d{2}-?\d{5}$/;
type ValidationResult =
| { ok: true; values: MerchantValues }
| { ok: false; errors: MerchantFormErrors };
export function validateMerchant(values: MerchantValues): ValidationResult {
const errors: MerchantFormErrors = {};
const name = values.name.trim();
if (!name) {
errors.name = REQUIRED_MESSAGE;
} else if (name.length > NAME_MAX_LENGTH) {
errors.name = `가맹점명은 ${NAME_MAX_LENGTH}자 이내로 입력해 주세요.`;
}
if (!values.categoryCode.trim()) {
errors.categoryCode = REQUIRED_MESSAGE;
}
const phoneNumber = values.phoneNumber.trim();
if (!phoneNumber) {
errors.phoneNumber = REQUIRED_MESSAGE;
} else if (!/^[\d-]+$/.test(phoneNumber)) {
errors.phoneNumber = '전화번호는 숫자와 -만 입력할 수 있습니다.';
}
if (!values.address.trim()) {
errors.address = '주소를 검색하거나 입력해 주세요.';
}
const postalCode = values.postalCode.trim();
if (postalCode && !/^\d{5}$/.test(postalCode)) {
errors.postalCode = '우편번호는 숫자 5자리로 입력해 주세요.';
}
const businessNumber = values.businessNumber.trim();
if (businessNumber && !BUSINESS_NUMBER_PATTERN.test(businessNumber)) {
errors.businessNumber = '사업자등록번호는 000-00-00000 형식으로 입력해 주세요.';
}
const accountNumber = values.accountNumber.trim();
if (accountNumber && !/^[\d-]+$/.test(accountNumber)) {
errors.accountNumber = '계좌번호는 숫자와 -만 입력할 수 있습니다.';
}
if (Object.keys(errors).length > 0) {
return { ok: false, errors };
}
return {
ok: true,
values: {
...values,
name,
brandName: values.brandName.trim().slice(0, BRAND_MAX_LENGTH),
memo: values.memo.trim().slice(0, MEMO_MAX_LENGTH),
ownerName: values.ownerName.trim().slice(0, OWNER_MAX_LENGTH),
phoneNumber,
businessNumber,
address: values.address.trim(),
addressDetail: values.addressDetail.trim().slice(0, ADDRESS_DETAIL_MAX_LENGTH),
postalCode,
bankHolderName: values.bankHolderName.trim().slice(0, OWNER_MAX_LENGTH),
bankCode: values.bankCode.trim(),
accountNumber: accountNumber.slice(0, ACCOUNT_NUMBER_MAX_LENGTH),
businessStatusCode: values.businessStatusCode.trim(),
divisionCode: values.divisionCode.trim(),
},
};
}