import type { KbxImportDefinition, KbxImportMapping } from '@kbx/contracts' export type KbxImportMappingIssueCode = 'required-missing' | 'duplicate-target' export interface KbxImportMappingIssue { code: KbxImportMappingIssueCode fieldKey: string message: string } export function validateKbxImportMappings(definition:KbxImportDefinition,mappings:readonly KbxImportMapping[]):KbxImportMappingIssue[]{ const fields=definition.fields.filter(field=>field.importable!==false) const targets=mappings.map(mapping=>mapping.targetField).filter((value):value is string=>Boolean(value)) const issues:KbxImportMappingIssue[]=[] for(const target of [...new Set(targets.filter((value,index)=>targets.indexOf(value)!==index))]){ const field=fields.find(item=>item.key===target) issues.push({code:'duplicate-target',fieldKey:target,message:`${field?.label ?? target} 필드에 Excel 열이 두 개 이상 연결되어 있습니다.`}) } for(const field of fields.filter(item=>item.required)){ if(!targets.includes(field.key))issues.push({code:'required-missing',fieldKey:field.key,message:`${field.label} 필수 필드를 매핑하세요.`}) } return issues } export function validateKbxImportFileCandidate(definition:KbxImportDefinition,file:{name:string;size:number}):string|null{ if(!file.name.toLocaleLowerCase().endsWith('.xlsx'))return 'Excel .xlsx 파일만 업로드할 수 있습니다.' const maxBytes=definition.maxFileSizeBytes??10*1024*1024 if(file.size>maxBytes)return `파일 크기는 ${Math.round(maxBytes/1048576)}MB 이하여야 합니다.` return null }