Compare commits
No commits in common. "ba5ee357cad7d0600f0a4a2bf3e652f277c169f5" and "ef32de3087eff783844e40db01e82475b2f1ca29" have entirely different histories.
ba5ee357ca
...
ef32de3087
|
|
@ -26,9 +26,7 @@ import {
|
||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
Zap,
|
Zap,
|
||||||
Copy,
|
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
|
||||||
import { importFromExcel, getExcelSheetNames } from "@/lib/utils/excelExport";
|
import { importFromExcel, getExcelSheetNames } from "@/lib/utils/excelExport";
|
||||||
import { DynamicFormApi } from "@/lib/api/dynamicForm";
|
import { DynamicFormApi } from "@/lib/api/dynamicForm";
|
||||||
import { getTableSchema, TableColumn } from "@/lib/api/tableSchema";
|
import { getTableSchema, TableColumn } from "@/lib/api/tableSchema";
|
||||||
|
|
@ -96,8 +94,6 @@ export interface ExcelUploadModalProps {
|
||||||
interface ColumnMapping {
|
interface ColumnMapping {
|
||||||
excelColumn: string;
|
excelColumn: string;
|
||||||
systemColumn: string | null;
|
systemColumn: string | null;
|
||||||
// 중복 체크 설정 (해당 컬럼을 중복 체크 키로 사용할지)
|
|
||||||
checkDuplicate?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ExcelUploadModal: React.FC<ExcelUploadModalProps> = ({
|
export const ExcelUploadModal: React.FC<ExcelUploadModalProps> = ({
|
||||||
|
|
@ -136,9 +132,6 @@ export const ExcelUploadModal: React.FC<ExcelUploadModalProps> = ({
|
||||||
const [systemColumns, setSystemColumns] = useState<TableColumn[]>([]);
|
const [systemColumns, setSystemColumns] = useState<TableColumn[]>([]);
|
||||||
const [columnMappings, setColumnMappings] = useState<ColumnMapping[]>([]);
|
const [columnMappings, setColumnMappings] = useState<ColumnMapping[]>([]);
|
||||||
|
|
||||||
// 중복 처리 방법 (전역 설정)
|
|
||||||
const [duplicateAction, setDuplicateAction] = useState<"overwrite" | "skip">("skip");
|
|
||||||
|
|
||||||
// 3단계: 확인
|
// 3단계: 확인
|
||||||
const [isUploading, setIsUploading] = useState(false);
|
const [isUploading, setIsUploading] = useState(false);
|
||||||
|
|
||||||
|
|
@ -551,20 +544,6 @@ export const ExcelUploadModal: React.FC<ExcelUploadModalProps> = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 중복 체크 설정 변경
|
|
||||||
const handleDuplicateCheckChange = (excelColumn: string, checkDuplicate: boolean) => {
|
|
||||||
setColumnMappings((prev) =>
|
|
||||||
prev.map((mapping) =>
|
|
||||||
mapping.excelColumn === excelColumn
|
|
||||||
? { ...mapping, checkDuplicate }
|
|
||||||
: mapping
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 중복 체크 설정된 컬럼 수
|
|
||||||
const duplicateCheckCount = columnMappings.filter((m) => m.checkDuplicate && m.systemColumn).length;
|
|
||||||
|
|
||||||
// 다음 단계
|
// 다음 단계
|
||||||
const handleNext = () => {
|
const handleNext = () => {
|
||||||
if (currentStep === 1 && !file) {
|
if (currentStep === 1 && !file) {
|
||||||
|
|
@ -728,96 +707,16 @@ export const ExcelUploadModal: React.FC<ExcelUploadModalProps> = ({
|
||||||
// 기존 단일 테이블 업로드 로직
|
// 기존 단일 테이블 업로드 로직
|
||||||
let successCount = 0;
|
let successCount = 0;
|
||||||
let failCount = 0;
|
let failCount = 0;
|
||||||
let skipCount = 0;
|
|
||||||
let overwriteCount = 0;
|
|
||||||
|
|
||||||
// 단일 테이블 채번 설정 확인
|
// 단일 테이블 채번 설정 확인
|
||||||
const hasNumbering = numberingRuleId && numberingTargetColumn;
|
const hasNumbering = numberingRuleId && numberingTargetColumn;
|
||||||
|
|
||||||
// 중복 체크 설정 확인
|
|
||||||
const duplicateCheckMappings = columnMappings.filter(
|
|
||||||
(m) => m.checkDuplicate && m.systemColumn
|
|
||||||
);
|
|
||||||
const hasDuplicateCheck = duplicateCheckMappings.length > 0;
|
|
||||||
|
|
||||||
// 중복 체크를 위한 기존 데이터 조회 (중복 체크가 설정된 경우에만)
|
|
||||||
let existingDataMap: Map<string, any> = new Map();
|
|
||||||
if (hasDuplicateCheck) {
|
|
||||||
try {
|
|
||||||
// 중복 체크할 컬럼들의 값 조회
|
|
||||||
const checkColumns = duplicateCheckMappings.map((m) => {
|
|
||||||
let colName = m.systemColumn!;
|
|
||||||
if (isMasterDetail && colName.includes(".")) {
|
|
||||||
colName = colName.split(".")[1];
|
|
||||||
}
|
|
||||||
return colName;
|
|
||||||
});
|
|
||||||
|
|
||||||
// DynamicFormApi.getTableData 사용
|
|
||||||
const existingResponse = await DynamicFormApi.getTableData(tableName, {
|
|
||||||
page: 1,
|
|
||||||
pageSize: 10000,
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("📊 중복 체크용 기존 데이터 조회 결과:", existingResponse);
|
|
||||||
|
|
||||||
// getTableData는 { success, data: [...] } 또는 { success, data: { rows: [...] } } 형식
|
|
||||||
const rows = existingResponse.data?.rows || existingResponse.data;
|
|
||||||
if (existingResponse.success && rows && Array.isArray(rows)) {
|
|
||||||
// 중복 체크 컬럼 값을 키로 하는 맵 생성
|
|
||||||
rows.forEach((row: any) => {
|
|
||||||
const keyParts = checkColumns.map((col) => String(row[col] || "").trim());
|
|
||||||
const key = keyParts.join("|||");
|
|
||||||
existingDataMap.set(key, row);
|
|
||||||
});
|
|
||||||
console.log(`📊 중복 체크용 기존 데이터 로드: ${existingDataMap.size}건`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("중복 체크 데이터 조회 오류:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const row of filteredData) {
|
for (const row of filteredData) {
|
||||||
try {
|
try {
|
||||||
let dataToSave = { ...row };
|
let dataToSave = { ...row };
|
||||||
let shouldSkip = false;
|
|
||||||
let shouldUpdate = false;
|
|
||||||
let existingRow: any = null;
|
|
||||||
|
|
||||||
// 중복 체크
|
// 채번 적용: 각 행마다 채번 API 호출
|
||||||
if (hasDuplicateCheck) {
|
if (hasNumbering && uploadMode === "insert") {
|
||||||
const checkColumns = duplicateCheckMappings.map((m) => {
|
|
||||||
let colName = m.systemColumn!;
|
|
||||||
if (isMasterDetail && colName.includes(".")) {
|
|
||||||
colName = colName.split(".")[1];
|
|
||||||
}
|
|
||||||
return colName;
|
|
||||||
});
|
|
||||||
|
|
||||||
const keyParts = checkColumns.map((col) => String(dataToSave[col] || "").trim());
|
|
||||||
const key = keyParts.join("|||");
|
|
||||||
|
|
||||||
if (existingDataMap.has(key)) {
|
|
||||||
existingRow = existingDataMap.get(key);
|
|
||||||
// 중복 발견 - 전역 설정에 따라 처리
|
|
||||||
if (duplicateAction === "skip") {
|
|
||||||
shouldSkip = true;
|
|
||||||
skipCount++;
|
|
||||||
console.log(`⏭️ 중복으로 건너뛰기: ${key}`);
|
|
||||||
} else {
|
|
||||||
shouldUpdate = true;
|
|
||||||
console.log(`🔄 중복으로 덮어쓰기: ${key}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 건너뛰기 처리
|
|
||||||
if (shouldSkip) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 채번 적용: 각 행마다 채번 API 호출 (신규 등록 시에만)
|
|
||||||
if (hasNumbering && uploadMode === "insert" && !shouldUpdate) {
|
|
||||||
try {
|
try {
|
||||||
const { apiClient } = await import("@/lib/api/client");
|
const { apiClient } = await import("@/lib/api/client");
|
||||||
const numberingResponse = await apiClient.post(`/numbering-rules/${numberingRuleId}/allocate`);
|
const numberingResponse = await apiClient.post(`/numbering-rules/${numberingRuleId}/allocate`);
|
||||||
|
|
@ -830,22 +729,7 @@ export const ExcelUploadModal: React.FC<ExcelUploadModalProps> = ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldUpdate && existingRow) {
|
if (uploadMode === "insert") {
|
||||||
// 덮어쓰기: 기존 데이터 업데이트
|
|
||||||
const formData = {
|
|
||||||
screenId: 0,
|
|
||||||
tableName,
|
|
||||||
data: dataToSave,
|
|
||||||
};
|
|
||||||
const result = await DynamicFormApi.updateFormData(existingRow.id, formData);
|
|
||||||
if (result.success) {
|
|
||||||
overwriteCount++;
|
|
||||||
successCount++;
|
|
||||||
} else {
|
|
||||||
failCount++;
|
|
||||||
}
|
|
||||||
} else if (uploadMode === "insert") {
|
|
||||||
// 신규 등록
|
|
||||||
const formData = { screenId: 0, tableName, data: dataToSave };
|
const formData = { screenId: 0, tableName, data: dataToSave };
|
||||||
const result = await DynamicFormApi.saveFormData(formData);
|
const result = await DynamicFormApi.saveFormData(formData);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
|
|
@ -859,7 +743,7 @@ export const ExcelUploadModal: React.FC<ExcelUploadModalProps> = ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 업로드 후 제어 실행
|
// 🆕 업로드 후 제어 실행
|
||||||
if (afterUploadFlows && afterUploadFlows.length > 0 && successCount > 0) {
|
if (afterUploadFlows && afterUploadFlows.length > 0 && successCount > 0) {
|
||||||
console.log("🔄 업로드 후 제어 실행:", afterUploadFlows);
|
console.log("🔄 업로드 후 제어 실행:", afterUploadFlows);
|
||||||
try {
|
try {
|
||||||
|
|
@ -877,24 +761,10 @@ export const ExcelUploadModal: React.FC<ExcelUploadModalProps> = ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (successCount > 0 || skipCount > 0) {
|
|
||||||
// 상세 결과 메시지 생성
|
|
||||||
let message = "";
|
|
||||||
if (successCount > 0) {
|
if (successCount > 0) {
|
||||||
message += `${successCount}개 행 업로드`;
|
toast.success(
|
||||||
if (overwriteCount > 0) {
|
`${successCount}개 행이 업로드되었습니다.${failCount > 0 ? ` (실패: ${failCount}개)` : ""}`
|
||||||
message += ` (덮어쓰기 ${overwriteCount}건)`;
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
if (skipCount > 0) {
|
|
||||||
message += message ? `, ` : "";
|
|
||||||
message += `중복 건너뛰기 ${skipCount}개`;
|
|
||||||
}
|
|
||||||
if (failCount > 0) {
|
|
||||||
message += ` (실패: ${failCount}개)`;
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.success(message);
|
|
||||||
|
|
||||||
// 매핑 템플릿 저장
|
// 매핑 템플릿 저장
|
||||||
await saveMappingTemplateInternal();
|
await saveMappingTemplateInternal();
|
||||||
|
|
@ -955,7 +825,6 @@ export const ExcelUploadModal: React.FC<ExcelUploadModalProps> = ({
|
||||||
setExcelColumns([]);
|
setExcelColumns([]);
|
||||||
setSystemColumns([]);
|
setSystemColumns([]);
|
||||||
setColumnMappings([]);
|
setColumnMappings([]);
|
||||||
setDuplicateAction("skip");
|
|
||||||
// 🆕 마스터-디테일 모드 초기화
|
// 🆕 마스터-디테일 모드 초기화
|
||||||
setMasterFieldValues({});
|
setMasterFieldValues({});
|
||||||
}
|
}
|
||||||
|
|
@ -1299,18 +1168,17 @@ export const ExcelUploadModal: React.FC<ExcelUploadModalProps> = ({
|
||||||
|
|
||||||
{/* 매핑 리스트 */}
|
{/* 매핑 리스트 */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="grid grid-cols-[1fr_auto_1fr_80px] gap-2 text-[10px] font-medium text-muted-foreground sm:text-xs">
|
<div className="grid grid-cols-[1fr_auto_1fr] gap-2 text-[10px] font-medium text-muted-foreground sm:text-xs">
|
||||||
<div>엑셀 컬럼</div>
|
<div>엑셀 컬럼</div>
|
||||||
<div></div>
|
<div></div>
|
||||||
<div>시스템 컬럼</div>
|
<div>시스템 컬럼</div>
|
||||||
<div className="text-center">중복 키</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="max-h-[300px] space-y-2 overflow-y-auto">
|
<div className="max-h-[350px] space-y-2 overflow-y-auto">
|
||||||
{columnMappings.map((mapping, index) => (
|
{columnMappings.map((mapping, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="grid grid-cols-[1fr_auto_1fr_80px] items-center gap-2"
|
className="grid grid-cols-[1fr_auto_1fr] items-center gap-2"
|
||||||
>
|
>
|
||||||
<div className="rounded-md border border-border bg-muted px-3 py-2 text-xs font-medium sm:text-sm">
|
<div className="rounded-md border border-border bg-muted px-3 py-2 text-xs font-medium sm:text-sm">
|
||||||
{mapping.excelColumn}
|
{mapping.excelColumn}
|
||||||
|
|
@ -1352,78 +1220,11 @@ export const ExcelUploadModal: React.FC<ExcelUploadModalProps> = ({
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
{/* 중복 체크 체크박스 */}
|
|
||||||
<div className="flex justify-center">
|
|
||||||
{mapping.systemColumn ? (
|
|
||||||
<Checkbox
|
|
||||||
checked={mapping.checkDuplicate || false}
|
|
||||||
onCheckedChange={(checked) =>
|
|
||||||
handleDuplicateCheckChange(mapping.excelColumn, checked as boolean)
|
|
||||||
}
|
|
||||||
className="h-4 w-4"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<span className="text-[10px] text-muted-foreground">-</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 중복 체크 안내 */}
|
|
||||||
{duplicateCheckCount > 0 ? (
|
|
||||||
<div className="rounded-md border border-blue-200 bg-blue-50 p-3">
|
|
||||||
<div className="flex items-start justify-between gap-4">
|
|
||||||
<div className="flex items-start gap-2">
|
|
||||||
<Copy className="mt-0.5 h-4 w-4 text-blue-600" />
|
|
||||||
<div className="text-[10px] text-blue-700 sm:text-xs">
|
|
||||||
<p className="font-medium">
|
|
||||||
중복 키: {columnMappings
|
|
||||||
.filter((m) => m.checkDuplicate && m.systemColumn)
|
|
||||||
.map((m) => {
|
|
||||||
const col = systemColumns.find((c) => c.name === m.systemColumn);
|
|
||||||
return col?.label || m.systemColumn;
|
|
||||||
})
|
|
||||||
.join(" + ")}
|
|
||||||
</p>
|
|
||||||
<p className="mt-1">
|
|
||||||
위 컬럼 값이 모두 일치하는 기존 데이터가 있으면 중복으로 처리합니다.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2 shrink-0">
|
|
||||||
<span className="text-[10px] text-blue-700 sm:text-xs">중복 시:</span>
|
|
||||||
<Select
|
|
||||||
value={duplicateAction}
|
|
||||||
onValueChange={(value) => setDuplicateAction(value as "overwrite" | "skip")}
|
|
||||||
>
|
|
||||||
<SelectTrigger className="h-7 w-[100px] text-[10px] sm:text-xs border-blue-300 bg-white">
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="skip" className="text-xs">건너뛰기</SelectItem>
|
|
||||||
<SelectItem value="overwrite" className="text-xs">덮어쓰기</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="rounded-md border border-muted bg-muted/30 p-3">
|
|
||||||
<div className="flex items-start gap-2">
|
|
||||||
<Copy className="mt-0.5 h-4 w-4 text-muted-foreground" />
|
|
||||||
<div className="text-[10px] text-muted-foreground sm:text-xs">
|
|
||||||
<p className="font-medium">중복 체크 (선택사항)</p>
|
|
||||||
<p className="mt-1">
|
|
||||||
"중복 키" 체크박스를 선택하면 해당 컬럼 값으로 기존 데이터와 비교합니다.
|
|
||||||
여러 컬럼을 선택하면 복합 키로 중복을 판단합니다.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 매핑 자동 저장 안내 */}
|
{/* 매핑 자동 저장 안내 */}
|
||||||
{isAutoMappingLoaded ? (
|
{isAutoMappingLoaded ? (
|
||||||
<div className="rounded-md border border-success bg-success/10 p-3">
|
<div className="rounded-md border border-success bg-success/10 p-3">
|
||||||
|
|
@ -1497,11 +1298,6 @@ export const ExcelUploadModal: React.FC<ExcelUploadModalProps> = ({
|
||||||
<p key={index}>
|
<p key={index}>
|
||||||
<span className="font-medium">{mapping.excelColumn}</span> →{" "}
|
<span className="font-medium">{mapping.excelColumn}</span> →{" "}
|
||||||
{col?.label || mapping.systemColumn}
|
{col?.label || mapping.systemColumn}
|
||||||
{mapping.checkDuplicate && (
|
|
||||||
<span className="ml-2 text-blue-600">
|
|
||||||
(중복 체크: {mapping.duplicateAction === "overwrite" ? "덮어쓰기" : "건너뛰기"})
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
@ -1511,29 +1307,6 @@ export const ExcelUploadModal: React.FC<ExcelUploadModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 중복 체크 요약 */}
|
|
||||||
{duplicateCheckCount > 0 && (
|
|
||||||
<div className="rounded-md border border-blue-200 bg-blue-50 p-4">
|
|
||||||
<h3 className="text-sm font-medium text-blue-800 sm:text-base">중복 체크 설정</h3>
|
|
||||||
<div className="mt-2 space-y-1 text-[10px] text-blue-700 sm:text-xs">
|
|
||||||
<p>
|
|
||||||
<span className="font-medium">중복 키:</span>{" "}
|
|
||||||
{columnMappings
|
|
||||||
.filter((m) => m.checkDuplicate && m.systemColumn)
|
|
||||||
.map((m) => {
|
|
||||||
const col = systemColumns.find((c) => c.name === m.systemColumn);
|
|
||||||
return col?.label || m.systemColumn;
|
|
||||||
})
|
|
||||||
.join(" + ")}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<span className="font-medium">중복 시 처리:</span>{" "}
|
|
||||||
{duplicateAction === "overwrite" ? "덮어쓰기 (기존 데이터 업데이트)" : "건너뛰기 (해당 행 무시)"}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="rounded-md border border-warning bg-warning/10 p-3">
|
<div className="rounded-md border border-warning bg-warning/10 p-3">
|
||||||
<div className="flex items-start gap-2">
|
<div className="flex items-start gap-2">
|
||||||
<AlertCircle className="mt-0.5 h-4 w-4 text-warning" />
|
<AlertCircle className="mt-0.5 h-4 w-4 text-warning" />
|
||||||
|
|
|
||||||
|
|
@ -84,20 +84,8 @@ export const ModernDatePicker: React.FC<ModernDatePickerProps> = ({ label, value
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleConfirm = () => {
|
const handleConfirm = () => {
|
||||||
// 날짜 순서 자동 정렬
|
|
||||||
let finalValue = { ...tempValue };
|
|
||||||
|
|
||||||
if (finalValue.from && finalValue.to) {
|
|
||||||
// from이 to보다 나중이면 swap
|
|
||||||
if (finalValue.from > finalValue.to) {
|
|
||||||
const temp = finalValue.from;
|
|
||||||
finalValue.from = finalValue.to;
|
|
||||||
finalValue.to = temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 확인 버튼을 눌렀을 때만 onChange 호출
|
// 확인 버튼을 눌렀을 때만 onChange 호출
|
||||||
onChange(finalValue);
|
onChange(tempValue);
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
setSelectingType("from");
|
setSelectingType("from");
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -380,16 +380,6 @@ export function UniversalFormModalComponent({
|
||||||
const handleBeforeFormSave = (event: Event) => {
|
const handleBeforeFormSave = (event: Event) => {
|
||||||
if (!(event instanceof CustomEvent) || !event.detail?.formData) return;
|
if (!(event instanceof CustomEvent) || !event.detail?.formData) return;
|
||||||
|
|
||||||
// 필수값 검증 실행
|
|
||||||
const validation = validateRequiredFields();
|
|
||||||
if (!validation.valid) {
|
|
||||||
event.detail.validationFailed = true;
|
|
||||||
event.detail.validationErrors = validation.missingFields;
|
|
||||||
toast.error(`필수 항목을 입력해주세요: ${validation.missingFields.join(", ")}`);
|
|
||||||
console.log("[UniversalFormModal] 필수값 검증 실패:", validation.missingFields);
|
|
||||||
return; // 검증 실패 시 데이터 병합 중단
|
|
||||||
}
|
|
||||||
|
|
||||||
// 설정에 정의된 필드 columnName 목록 수집
|
// 설정에 정의된 필드 columnName 목록 수집
|
||||||
const configuredFields = new Set<string>();
|
const configuredFields = new Set<string>();
|
||||||
config.sections.forEach((section) => {
|
config.sections.forEach((section) => {
|
||||||
|
|
|
||||||
|
|
@ -528,8 +528,6 @@ export class ButtonActionExecutor {
|
||||||
const beforeSaveEventDetail = {
|
const beforeSaveEventDetail = {
|
||||||
formData: context.formData,
|
formData: context.formData,
|
||||||
skipDefaultSave: false,
|
skipDefaultSave: false,
|
||||||
validationFailed: false,
|
|
||||||
validationErrors: [] as string[],
|
|
||||||
};
|
};
|
||||||
window.dispatchEvent(
|
window.dispatchEvent(
|
||||||
new CustomEvent("beforeFormSave", {
|
new CustomEvent("beforeFormSave", {
|
||||||
|
|
@ -542,12 +540,6 @@ export class ButtonActionExecutor {
|
||||||
|
|
||||||
console.log("📦 [handleSave] beforeFormSave 이벤트 후 formData keys:", Object.keys(context.formData || {}));
|
console.log("📦 [handleSave] beforeFormSave 이벤트 후 formData keys:", Object.keys(context.formData || {}));
|
||||||
|
|
||||||
// 검증 실패 시 저장 중단
|
|
||||||
if (beforeSaveEventDetail.validationFailed) {
|
|
||||||
console.log("❌ [handleSave] 검증 실패로 저장 중단:", beforeSaveEventDetail.validationErrors);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 🔧 skipDefaultSave 플래그 확인 - SelectedItemsDetailInput 등에서 자체 UPSERT 처리 시 기본 저장 건너뛰기
|
// 🔧 skipDefaultSave 플래그 확인 - SelectedItemsDetailInput 등에서 자체 UPSERT 처리 시 기본 저장 건너뛰기
|
||||||
if (beforeSaveEventDetail.skipDefaultSave) {
|
if (beforeSaveEventDetail.skipDefaultSave) {
|
||||||
console.log("🚫 [handleSave] skipDefaultSave=true - 기본 저장 로직 건너뛰기 (컴포넌트에서 자체 처리)");
|
console.log("🚫 [handleSave] skipDefaultSave=true - 기본 저장 로직 건너뛰기 (컴포넌트에서 자체 처리)");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue