import { foxValidationMessage, foxValidators, type FoxValidationMessages, } from '@fox/core/validation'; export type AdminMenuValues = { name: string; sortOrder: number; roleId: string; isTopActive: boolean; url: string; /** 이슈: 백엔드에 대응 컬럼이 없어 저장되지 않는다. 기획대로 화면에만 둔다. */ opensNewWindow: boolean; description: string; isActive: boolean; }; export type AdminMenuFormErrors = Partial>; export type AdminMenuFormState = | { status: 'idle' } | { status: 'error'; message?: string; errors?: AdminMenuFormErrors } | { status: 'success' }; export const INITIAL_ADMIN_MENU_FORM_STATE: AdminMenuFormState = { status: 'idle', }; const NAME_MAX_LENGTH = 100; const URL_MAX_LENGTH = 300; const DESCRIPTION_MAX_LENGTH = 500; const NAME_VALIDATORS = [ foxValidators.required, foxValidators.maxLength(NAME_MAX_LENGTH), ]; const URL_VALIDATORS = [ foxValidators.required, foxValidators.maxLength(URL_MAX_LENGTH), ]; const DESCRIPTION_VALIDATORS = [ foxValidators.required, foxValidators.maxLength(DESCRIPTION_MAX_LENGTH), ]; export const ADMIN_MENU_NAME_MESSAGES: FoxValidationMessages = { required: '메뉴명을 입력해 주세요.', maxlength: `메뉴명은 ${NAME_MAX_LENGTH}자 이내로 입력해 주세요.`, }; export const ADMIN_MENU_URL_MESSAGES: FoxValidationMessages = { required: '메뉴링크를 입력해 주세요.', maxlength: `메뉴링크는 ${URL_MAX_LENGTH}자 이내로 입력해 주세요.`, }; export const ADMIN_MENU_DESCRIPTION_MESSAGES: FoxValidationMessages = { required: '메뉴설명을 입력해 주세요.', maxlength: `메뉴설명은 ${DESCRIPTION_MAX_LENGTH}자 이내로 입력해 주세요.`, }; export { NAME_VALIDATORS, URL_VALIDATORS, DESCRIPTION_VALIDATORS }; function check( value: string, validators: typeof NAME_VALIDATORS, messages: FoxValidationMessages ): string | undefined { return foxValidationMessage( foxValidators.compose(validators)(value.trim()), messages ); } export function validateAdminMenu( values: AdminMenuValues, allowedRoleIds: string[] ): | { ok: true; values: AdminMenuValues } | { ok: false; errors: AdminMenuFormErrors } { const errors: AdminMenuFormErrors = {}; const name = values.name.trim(); const url = values.url.trim(); const description = values.description.trim(); errors.name = check(name, NAME_VALIDATORS, ADMIN_MENU_NAME_MESSAGES); errors.url = check(url, URL_VALIDATORS, ADMIN_MENU_URL_MESSAGES); errors.description = check( description, DESCRIPTION_VALIDATORS, ADMIN_MENU_DESCRIPTION_MESSAGES ); if (!Number.isInteger(values.sortOrder) || values.sortOrder < 1) { errors.sortOrder = '메뉴순번은 1 이상의 숫자로 입력해 주세요.'; } if (!allowedRoleIds.includes(values.roleId)) { errors.roleId = '메뉴권한을 선택해 주세요.'; } for (const key of Object.keys(errors) as (keyof AdminMenuFormErrors)[]) { if (errors[key] === undefined) { delete errors[key]; } } if (Object.keys(errors).length > 0) { return { ok: false, errors }; } return { ok: true, values: { ...values, name, url, description } }; }