Files
T
kjh2064 3f293d8aa8
deploy / deploy (push) Successful in 1m52s
deploy / notify (push) Successful in 1s
V13-FE-006: consolidate approved UI and contract hardening
2026-08-13 02:41:00 +09:00

30 lines
1.6 KiB
TypeScript

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
}