Files
sam-react-prod/src/lib/utils/validation/common.ts
유병철 49d07914fd feat(WEB): CEO 대시보드 리팩토링, 캘린더 강화, validation 모듈 분리, Git Workflow 정립
- CEO 대시보드 전 섹션 공통 컴포넌트 기반 리팩토링 (SectionCard, StatItem 등)
- CalendarSection 일정 CRUD 기능 확장
- validation.ts → validation/ 모듈 분리 (item-schemas, form-schemas, common, utils)
- CLAUDE.md Git Workflow 섹션 추가 (develop/main 플로우 정의)
- Jenkinsfile CI/CD 파이프라인 정비 (Slack 알림 추가)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 21:55:15 +09:00

111 lines
3.0 KiB
TypeScript

/**
* 공통 Zod 검증 스키마
*
* 품목명, 품목유형, 날짜, 숫자, BOM 등 여러 스키마에서 공유하는 기본 블록
*/
import { z } from 'zod';
// ===== 내부 전용 스키마 =====
/**
* 품목 코드 검증
* 형식: {업체코드}-{품목유형}-{일련번호}
* 예: KD-FG-001
*
* 현재 사용하지 않음 (품목 코드 자동 생성)
*/
export const _itemCodeSchema = z.string()
.min(1, '품목 코드를 입력해주세요')
.regex(
/^[A-Z0-9]+-[A-Z]{2}-\d+$/,
'품목 코드 형식이 올바르지 않습니다 (예: KD-FG-001)'
);
// ===== 공통 필드 스키마 =====
/**
* 품목명 검증
*/
export const itemNameSchema = z.preprocess(
(val) => val === undefined || val === null ? "" : val,
z.string().min(1, '품목명을 입력해주세요').max(200, '품목명은 200자 이내로 입력해주세요')
);
/**
* 품목 유형 검증
*/
export const itemTypeSchema = z.enum(['FG', 'PT', 'SM', 'RM', 'CS'], {
message: '품목 유형을 선택해주세요',
});
/**
* 단위 검증
*
* 현재 사용하지 않음 (materialUnitSchema로 대체)
*/
export const _unitSchema = z.string()
.min(1, '단위를 입력해주세요')
.max(20, '단위는 20자 이내로 입력해주세요');
/**
* 양수 검증 (가격, 수량 등)
* undefined나 빈 문자열은 검증하지 않음
*/
export const positiveNumberSchema = z.union([
z.number().positive('0보다 큰 값을 입력해주세요'),
z.string().transform((val) => parseFloat(val)).pipe(z.number().positive('0보다 큰 값을 입력해주세요')),
z.undefined(),
z.null(),
z.literal('')
]).optional();
/**
* 날짜 검증 (YYYY-MM-DD)
* 빈 문자열이나 undefined는 검증하지 않음
*/
export const dateSchema = z.preprocess(
(val) => {
if (val === undefined || val === null || val === '') return undefined;
return val;
},
z.string()
.regex(/^\d{4}-\d{2}-\d{2}$/, '날짜 형식이 올바르지 않습니다 (YYYY-MM-DD)')
.optional()
);
// ===== BOM 라인 스키마 =====
/**
* 절곡품 전개도 상세 스키마
*/
export const bendingDetailSchema = z.object({
id: z.string(),
no: z.number().int().positive(),
input: z.number(),
elongation: z.number().default(-1),
calculated: z.number(),
sum: z.number(),
shaded: z.boolean().default(false),
aAngle: z.number().optional(),
});
/**
* BOM 라인 스키마
*/
export const bomLineSchema = z.object({
id: z.string(),
childItemCode: z.string().min(1, '하위 품목 코드를 입력해주세요'),
childItemName: z.string().min(1, '하위 품목명을 입력해주세요'),
quantity: z.number().positive('수량은 0보다 커야 합니다'),
unit: z.string().min(1, '단위를 입력해주세요'),
unitPrice: positiveNumberSchema,
quantityFormula: z.string().optional(),
note: z.string().max(500).optional(),
// 절곡품 관련
isBending: z.boolean().optional(),
bendingDiagram: z.string().url().optional(),
bendingDetails: z.array(bendingDetailSchema).optional(),
});