[agent-pipeline] pipe-20260329010534-qgv9 round-2
This commit is contained in:
@@ -61,8 +61,8 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) {
|
||||
|
||||
// 코드 맵 생성
|
||||
codes.forEach((code) => {
|
||||
const codeValue = code.codeValue || code.code_value || "";
|
||||
const parentValue = code.parentCodeValue || code.parent_code_value;
|
||||
const codeValue = code.code_value || "";
|
||||
const parentValue = code.parent_code_value;
|
||||
codeMap.set(codeValue, code);
|
||||
|
||||
if (parentValue) {
|
||||
@@ -77,14 +77,14 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) {
|
||||
const traverse = (parentValue: string | null, depth: number) => {
|
||||
const children = parentValue
|
||||
? childrenMap.get(parentValue) || []
|
||||
: codes.filter((c) => !c.parentCodeValue && !c.parent_code_value);
|
||||
: codes.filter((c) => !c.parent_code_value);
|
||||
|
||||
// 정렬 순서로 정렬
|
||||
children
|
||||
.sort((a, b) => (a.sortOrder || a.sort_order || 0) - (b.sortOrder || b.sort_order || 0))
|
||||
.sort((a, b) => (a.sort_order || 0) - (b.sort_order || 0))
|
||||
.forEach((code) => {
|
||||
result.push(code);
|
||||
const codeValue = code.codeValue || code.code_value || "";
|
||||
const codeValue = code.code_value || "";
|
||||
traverse(codeValue, depth + 1);
|
||||
});
|
||||
};
|
||||
@@ -118,7 +118,7 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) {
|
||||
const childrenMap = useMemo(() => {
|
||||
const map = new Map<string, CodeInfo[]>();
|
||||
codes.forEach((code) => {
|
||||
const parentValue = code.parentCodeValue || code.parent_code_value;
|
||||
const parentValue = code.parent_code_value;
|
||||
if (parentValue) {
|
||||
if (!map.has(parentValue)) {
|
||||
map.set(parentValue, []);
|
||||
@@ -144,14 +144,14 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) {
|
||||
|
||||
// 특정 코드가 표시되어야 하는지 확인 (부모가 접혀있으면 숨김)
|
||||
const isCodeVisible = (code: CodeInfo): boolean => {
|
||||
const parentValue = code.parentCodeValue || code.parent_code_value;
|
||||
const parentValue = code.parent_code_value;
|
||||
if (!parentValue) return true; // 최상위 코드는 항상 표시
|
||||
|
||||
// 부모가 접혀있으면 숨김
|
||||
if (collapsedCodes.has(parentValue)) return false;
|
||||
|
||||
// 부모의 부모도 확인 (재귀적으로)
|
||||
const parentCode = codes.find((c) => (c.codeValue || c.code_value) === parentValue);
|
||||
const parentCode = codes.find((c) => c.code_value === parentValue);
|
||||
if (parentCode) {
|
||||
return isCodeVisible(parentCode);
|
||||
}
|
||||
@@ -176,7 +176,7 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) {
|
||||
})),
|
||||
});
|
||||
},
|
||||
getItemId: (code: CodeInfo) => code.codeValue || code.code_value,
|
||||
getItemId: (code: CodeInfo) => code.code_value,
|
||||
});
|
||||
|
||||
// 새 코드 생성
|
||||
@@ -196,7 +196,7 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) {
|
||||
// 하위 코드 추가
|
||||
const handleAddChild = (parentCode: CodeInfo) => {
|
||||
setEditingCode(null);
|
||||
setDefaultParentCode(parentCode.codeValue || parentCode.code_value || "");
|
||||
setDefaultParentCode(parentCode.code_value || "");
|
||||
setShowFormModal(true);
|
||||
};
|
||||
|
||||
@@ -213,7 +213,7 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) {
|
||||
try {
|
||||
await deleteCodeMutation.mutateAsync({
|
||||
categoryCode,
|
||||
codeValue: deletingCode.codeValue || deletingCode.code_value,
|
||||
codeValue: deletingCode.code_value,
|
||||
});
|
||||
|
||||
setShowDeleteModal(false);
|
||||
@@ -298,11 +298,11 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) {
|
||||
<>
|
||||
<DndContext {...dragAndDrop.dndContextProps}>
|
||||
<SortableContext
|
||||
items={visibleCodes.map((code) => code.codeValue || code.code_value)}
|
||||
items={visibleCodes.map((code) => code.code_value)}
|
||||
strategy={verticalListSortingStrategy}
|
||||
>
|
||||
{visibleCodes.map((code, index) => {
|
||||
const codeValue = code.codeValue || code.code_value || "";
|
||||
const codeValue = code.code_value || "";
|
||||
const children = childrenMap.get(codeValue) || [];
|
||||
const hasChildren = children.length > 0;
|
||||
const isExpanded = !collapsedCodes.has(codeValue);
|
||||
@@ -334,17 +334,17 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) {
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<h4 className="text-sm font-semibold">{activeCode.codeName || activeCode.code_name}</h4>
|
||||
<h4 className="text-sm font-semibold">{activeCode.code_name}</h4>
|
||||
<Badge
|
||||
variant={
|
||||
activeCode.isActive === "Y" || activeCode.is_active === "Y" ? "default" : "secondary"
|
||||
activeCode.is_active === "Y" ? "default" : "secondary"
|
||||
}
|
||||
>
|
||||
{activeCode.isActive === "Y" || activeCode.is_active === "Y" ? "활성" : "비활성"}
|
||||
{activeCode.is_active === "Y" ? "활성" : "비활성"}
|
||||
</Badge>
|
||||
</div>
|
||||
<p className="text-muted-foreground mt-1 text-xs">
|
||||
{activeCode.codeValue || activeCode.code_value}
|
||||
{activeCode.code_value}
|
||||
</p>
|
||||
{activeCode.description && (
|
||||
<p className="text-muted-foreground mt-1 text-xs">{activeCode.description}</p>
|
||||
|
||||
@@ -1272,7 +1272,7 @@ export const ExcelUploadModal: React.FC<ExcelUploadModalProps> = ({
|
||||
}
|
||||
} else if (uploadMode === "insert" || uploadMode === "upsert") {
|
||||
// 신규 등록 (insert, upsert 모드)
|
||||
const formData = { screenId: 0, tableName, data: dataToSave };
|
||||
const formData = { screen_id: 0, table_name: tableName, data: dataToSave };
|
||||
console.log(`📝 [행 ${rowIdx + 1}] 신규 등록 시도 (mode: ${uploadMode}):`, dataToSave);
|
||||
const result = await DynamicFormApi.saveFormData(formData);
|
||||
if (result.success) {
|
||||
|
||||
@@ -167,14 +167,14 @@ export default function CopyScreenModal({
|
||||
|
||||
if (isOpen && mode === "screen" && sourceScreen) {
|
||||
// 단일 화면 복제 모드
|
||||
setScreenName(`${sourceScreen.screen_name ?? sourceScreen.screenName} (복사본)`);
|
||||
setScreenName(`${sourceScreen.screen_name} (복사본)`);
|
||||
setDescription(sourceScreen.description || "");
|
||||
|
||||
// 대상 회사 코드 설정
|
||||
if (isSuperAdmin) {
|
||||
setTargetCompanyCode(sourceScreen.company_code ?? sourceScreen.companyCode);
|
||||
setTargetCompanyCode(sourceScreen.company_code);
|
||||
} else {
|
||||
setTargetCompanyCode(sourceScreen.company_code ?? sourceScreen.companyCode);
|
||||
setTargetCompanyCode(sourceScreen.company_code);
|
||||
}
|
||||
|
||||
// 대상 그룹 초기화 (전달받은 값 또는 null)
|
||||
@@ -230,7 +230,7 @@ export default function CopyScreenModal({
|
||||
|
||||
if (useBulkRename) {
|
||||
// 일괄 수정 사용 시: (복사본) 텍스트 제거
|
||||
const newMainName = applyBulkRename(sourceScreen.screen_name ?? sourceScreen.screenName);
|
||||
const newMainName = applyBulkRename(sourceScreen.screen_name);
|
||||
setScreenName(newMainName);
|
||||
|
||||
// 모달 화면명 업데이트
|
||||
@@ -242,8 +242,8 @@ export default function CopyScreenModal({
|
||||
);
|
||||
} else {
|
||||
// 일괄 수정 미사용 시: (복사본) 텍스트 추가
|
||||
setScreenName(`${sourceScreen.screen_name ?? sourceScreen.screenName} (복사본)`);
|
||||
|
||||
setScreenName(`${sourceScreen.screen_name} (복사본)`);
|
||||
|
||||
setLinkedScreens((prev) =>
|
||||
prev.map((screen) => ({
|
||||
...screen,
|
||||
@@ -259,7 +259,7 @@ export default function CopyScreenModal({
|
||||
|
||||
const sourceCompanyCode = mode === "group"
|
||||
? sourceGroup?.company_code
|
||||
: sourceScreen?.company_code ?? sourceScreen?.companyCode;
|
||||
: sourceScreen?.company_code;
|
||||
|
||||
// 원본 회사와 같은 회사가 선택되어 있으면 다른 회사로 변경
|
||||
if (sourceCompanyCode && targetCompanyCode === sourceCompanyCode) {
|
||||
@@ -317,8 +317,8 @@ export default function CopyScreenModal({
|
||||
const data = response.data.data || response.data || [];
|
||||
console.log("📋 회사 목록 데이터:", data);
|
||||
const mappedCompanies = data.map((c: any) => ({
|
||||
companyCode: c.company_code || c.companyCode,
|
||||
companyName: c.company_name || c.companyName,
|
||||
companyCode: c.company_code,
|
||||
companyName: c.company_name,
|
||||
}));
|
||||
console.log("📋 매핑된 회사 목록:", mappedCompanies);
|
||||
setCompanies(mappedCompanies);
|
||||
@@ -336,8 +336,8 @@ export default function CopyScreenModal({
|
||||
|
||||
try {
|
||||
setLoadingLinkedScreens(true);
|
||||
console.log("📡 API 호출: detectLinkedModals", sourceScreen.screen_id ?? sourceScreen.screenId);
|
||||
const linked = await screenApi.detectLinkedModals(sourceScreen.screen_id ?? sourceScreen.screenId);
|
||||
console.log("📡 API 호출: detectLinkedModals", sourceScreen.screen_id);
|
||||
const linked = await screenApi.detectLinkedModals(sourceScreen.screen_id);
|
||||
console.log("✅ 연결된 모달 화면 감지 결과:", linked);
|
||||
|
||||
// 초기 newScreenName 설정
|
||||
@@ -444,8 +444,8 @@ export default function CopyScreenModal({
|
||||
|
||||
return {
|
||||
main: {
|
||||
original: sourceScreen.screen_name ?? sourceScreen.screenName,
|
||||
preview: applyBulkRename(sourceScreen.screen_name ?? sourceScreen.screenName), // (복사본) 없음
|
||||
original: sourceScreen.screen_name,
|
||||
preview: applyBulkRename(sourceScreen.screen_name), // (복사본) 없음
|
||||
},
|
||||
modals: linkedScreens.map((screen) => ({
|
||||
original: screen.screenName,
|
||||
@@ -561,7 +561,7 @@ export default function CopyScreenModal({
|
||||
setIsCopying(true);
|
||||
|
||||
// 화면명 중복 체크
|
||||
const companyCode = targetCompanyCode || sourceScreen.company_code || sourceScreen.companyCode;
|
||||
const companyCode = targetCompanyCode || sourceScreen.company_code;
|
||||
|
||||
// 메인 화면명 중복 체크
|
||||
const isMainDuplicate = await screenApi.checkDuplicateScreenName(
|
||||
@@ -591,7 +591,7 @@ export default function CopyScreenModal({
|
||||
}
|
||||
|
||||
// 메인 화면 + 모달 화면들 일괄 복사
|
||||
const result = await screenApi.copyScreenWithModals(sourceScreen.screen_id ?? sourceScreen.screenId, {
|
||||
const result = await screenApi.copyScreenWithModals(sourceScreen.screen_id, {
|
||||
targetCompanyCode: targetCompanyCode || undefined, // 최고 관리자: 대상 회사 전달
|
||||
mainScreen: {
|
||||
screenName: screenName.trim(),
|
||||
@@ -608,14 +608,14 @@ export default function CopyScreenModal({
|
||||
console.log("✅ 복사 완료:", result);
|
||||
|
||||
// 대상 그룹이 선택된 경우 복제된 메인 화면을 그룹에 추가
|
||||
if (selectedTargetGroupId && (result.mainScreen?.screen_id ?? result.mainScreen?.screenId)) {
|
||||
if (selectedTargetGroupId && result.mainScreen?.screen_id) {
|
||||
try {
|
||||
await addScreenToGroup({
|
||||
group_id: selectedTargetGroupId,
|
||||
screen_id: result.mainScreen.screen_id ?? result.mainScreen.screenId,
|
||||
screen_id: result.mainScreen.screen_id,
|
||||
screen_role: "MAIN",
|
||||
display_order: 1,
|
||||
target_company_code: targetCompanyCode || sourceScreen.company_code || sourceScreen.companyCode, // 대상 회사 코드 전달
|
||||
target_company_code: targetCompanyCode || sourceScreen.company_code, // 대상 회사 코드 전달
|
||||
});
|
||||
console.log(`✅ 복제된 화면을 그룹(${selectedTargetGroupId})에 추가 완료`);
|
||||
} catch (groupError) {
|
||||
@@ -625,7 +625,7 @@ export default function CopyScreenModal({
|
||||
}
|
||||
|
||||
// 추가 복사 옵션 처리 (단일 화면 복제용)
|
||||
const sourceCompanyCode = sourceScreen.company_code ?? sourceScreen.companyCode;
|
||||
const sourceCompanyCode = sourceScreen.company_code;
|
||||
const copyTargetCompanyCode = targetCompanyCode || sourceCompanyCode;
|
||||
let additionalCopyMessages: string[] = [];
|
||||
|
||||
@@ -634,8 +634,8 @@ export default function CopyScreenModal({
|
||||
try {
|
||||
console.log("📋 단일 화면: 채번규칙 복제 시작...");
|
||||
const numberingResult = await apiClient.post("/api/screen-management/copy-numbering-rules", {
|
||||
sourceCompanyCode,
|
||||
targetCompanyCode: copyTargetCompanyCode
|
||||
source_company_code: sourceCompanyCode,
|
||||
target_company_code: copyTargetCompanyCode
|
||||
});
|
||||
if (numberingResult.data.success) {
|
||||
additionalCopyMessages.push(`채번규칙 ${numberingResult.data.copiedCount || 0}개`);
|
||||
@@ -651,8 +651,8 @@ export default function CopyScreenModal({
|
||||
try {
|
||||
console.log("📋 단일 화면: 카테고리 값 복제 시작...");
|
||||
const categoryResult = await apiClient.post("/api/screen-management/copy-category-mapping", {
|
||||
sourceCompanyCode,
|
||||
targetCompanyCode: copyTargetCompanyCode
|
||||
source_company_code: sourceCompanyCode,
|
||||
target_company_code: copyTargetCompanyCode
|
||||
});
|
||||
if (categoryResult.data.success) {
|
||||
additionalCopyMessages.push(`카테고리 값 ${categoryResult.data.copiedValues || 0}개`);
|
||||
@@ -668,8 +668,8 @@ export default function CopyScreenModal({
|
||||
try {
|
||||
console.log("📋 단일 화면: 테이블 타입 컬럼 복제 시작...");
|
||||
const tableTypeResult = await apiClient.post("/api/screen-management/copy-table-type-columns", {
|
||||
sourceCompanyCode,
|
||||
targetCompanyCode: copyTargetCompanyCode
|
||||
source_company_code: sourceCompanyCode,
|
||||
target_company_code: copyTargetCompanyCode
|
||||
});
|
||||
if (tableTypeResult.data.success) {
|
||||
additionalCopyMessages.push(`테이블 타입 컬럼 ${tableTypeResult.data.copiedCount || 0}개`);
|
||||
@@ -771,7 +771,7 @@ export default function CopyScreenModal({
|
||||
const tableName = typeof s === 'object' ? s.table_name : '';
|
||||
|
||||
// allScreens에서 먼저 찾고, 없으면 그룹의 screens 정보로 대체
|
||||
let screenData = allScreens.find((sc) => (sc.screen_id ?? sc.screenId) === screenId);
|
||||
let screenData = allScreens.find((sc) => sc.screen_id === screenId);
|
||||
if (!screenData && screenId && screenName) {
|
||||
// allScreens에 없는 경우 (다른 회사 화면) - 그룹의 screens 정보로 최소한의 데이터 구성
|
||||
screenData = {
|
||||
@@ -799,13 +799,13 @@ export default function CopyScreenModal({
|
||||
setCopyProgress({
|
||||
current: stats.screens + 1,
|
||||
total: totalScreenCount,
|
||||
message: `화면 복제 중: ${screen.screen_name ?? screen.screenName}`
|
||||
message: `화면 복제 중: ${screen.screen_name}`
|
||||
});
|
||||
|
||||
const transformedScreenName = transformName(screen.screen_name ?? screen.screenName, false, sourceGroupData.company_code);
|
||||
console.log(` 📄 화면 복제: ${screen.screen_name ?? screen.screenName} → ${transformedScreenName}`);
|
||||
const transformedScreenName = transformName(screen.screen_name, false, sourceGroupData.company_code);
|
||||
console.log(` 📄 화면 복제: ${screen.screen_name} → ${transformedScreenName}`);
|
||||
|
||||
const result = await screenApi.copyScreenWithModals(screen.screen_id ?? screen.screenId, {
|
||||
const result = await screenApi.copyScreenWithModals(screen.screen_id, {
|
||||
targetCompanyCode: targetCompany,
|
||||
mainScreen: {
|
||||
screenName: transformedScreenName, // 일괄 이름 변경 적용
|
||||
@@ -815,22 +815,22 @@ export default function CopyScreenModal({
|
||||
modalScreens: [],
|
||||
});
|
||||
|
||||
if (result.mainScreen?.screen_id ?? result.mainScreen?.screenId) {
|
||||
if (result.mainScreen?.screen_id) {
|
||||
// 원본 화면 ID -> 새 화면 ID 매핑 기록
|
||||
screenIdMap[screen.screen_id ?? screen.screenId] = result.mainScreen.screen_id ?? result.mainScreen.screenId;
|
||||
screenIdMap[screen.screen_id] = result.mainScreen.screen_id;
|
||||
|
||||
await addScreenToGroup({
|
||||
group_id: newGroup.id,
|
||||
screen_id: result.mainScreen.screen_id ?? result.mainScreen.screenId,
|
||||
screen_id: result.mainScreen.screen_id,
|
||||
screen_role: screenRole || "MAIN",
|
||||
display_order: displayOrder, // 원본 정렬순서 유지
|
||||
target_company_code: targetCompany, // 대상 회사 코드 전달
|
||||
});
|
||||
stats.screens++;
|
||||
console.log(` ✅ 화면 복제 완료: ${result.mainScreen.screen_name ?? result.mainScreen.screenName} (${screen.screen_id ?? screen.screenId} → ${result.mainScreen.screen_id ?? result.mainScreen.screenId})`);
|
||||
console.log(` ✅ 화면 복제 완료: ${result.mainScreen.screen_name} (${screen.screen_id} → ${result.mainScreen.screen_id})`);
|
||||
}
|
||||
} catch (screenError) {
|
||||
console.error(` ❌ 화면 복제 실패 (${screen.screen_code ?? screen.screenCode}):`, screenError);
|
||||
console.error(` ❌ 화면 복제 실패 (${screen.screen_code}):`, screenError);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -951,7 +951,7 @@ export default function CopyScreenModal({
|
||||
const tableName = typeof s === 'object' ? s.table_name : '';
|
||||
|
||||
// allScreens에서 먼저 찾고, 없으면 그룹의 screens 정보로 대체
|
||||
let screenData = allScreens.find((sc) => (sc.screen_id ?? sc.screenId) === screenId);
|
||||
let screenData = allScreens.find((sc) => sc.screen_id === screenId);
|
||||
const foundInAllScreens = !!screenData;
|
||||
|
||||
if (!screenData && screenId && screenName) {
|
||||
@@ -966,7 +966,7 @@ export default function CopyScreenModal({
|
||||
companyCode: sourceGroup.company_code || '',
|
||||
} as any;
|
||||
} else if (screenData) {
|
||||
console.log(` ✅ allScreens에서 찾음: ${screenId} - ${screenData.screen_name ?? screenData.screenName}`);
|
||||
console.log(` ✅ allScreens에서 찾음: ${screenId} - ${screenData.screen_name}`);
|
||||
} else {
|
||||
console.log(` ❌ 화면 정보 없음: screenId=${screenId}, screenName=${screenName}`);
|
||||
}
|
||||
@@ -974,7 +974,7 @@ export default function CopyScreenModal({
|
||||
}).filter(item => item.screenData && item.screenId); // screenId가 유효한 것만
|
||||
|
||||
console.log(`🔍 매핑 완료: ${screensWithOrder.length}개 화면 복사 예정`);
|
||||
screensWithOrder.forEach(item => console.log(` - ${item.screenId}: ${item.screenData?.screenName}`));
|
||||
screensWithOrder.forEach(item => console.log(` - ${item.screenId}: ${item.screenData?.screen_name}`));
|
||||
|
||||
// display_order 순으로 정렬
|
||||
screensWithOrder.sort((a, b) => (a.displayOrder || 0) - (b.displayOrder || 0));
|
||||
@@ -989,13 +989,13 @@ export default function CopyScreenModal({
|
||||
setCopyProgress({
|
||||
current: stats.screens + 1,
|
||||
total: totalScreenCount,
|
||||
message: `화면 복제 중: ${screen.screen_name ?? screen.screenName}`
|
||||
message: `화면 복제 중: ${screen.screen_name}`
|
||||
});
|
||||
|
||||
const transformedScreenName = transformName(screen.screen_name ?? screen.screenName, false, sourceGroup.company_code);
|
||||
console.log(`📄 화면 복제: ${screen.screen_name ?? screen.screenName} → ${transformedScreenName}`);
|
||||
const transformedScreenName = transformName(screen.screen_name, false, sourceGroup.company_code);
|
||||
console.log(`📄 화면 복제: ${screen.screen_name} → ${transformedScreenName}`);
|
||||
|
||||
const result = await screenApi.copyScreenWithModals(screen.screen_id ?? screen.screenId, {
|
||||
const result = await screenApi.copyScreenWithModals(screen.screen_id, {
|
||||
targetCompanyCode: finalCompanyCode,
|
||||
mainScreen: {
|
||||
screenName: transformedScreenName, // 일괄 이름 변경 적용
|
||||
@@ -1005,22 +1005,22 @@ export default function CopyScreenModal({
|
||||
modalScreens: [],
|
||||
});
|
||||
|
||||
if (result.mainScreen?.screen_id ?? result.mainScreen?.screenId) {
|
||||
if (result.mainScreen?.screen_id) {
|
||||
// 원본 화면 ID -> 새 화면 ID 매핑 기록
|
||||
screenIdMap[screen.screen_id ?? screen.screenId] = result.mainScreen.screen_id ?? result.mainScreen.screenId;
|
||||
screenIdMap[screen.screen_id] = result.mainScreen.screen_id;
|
||||
|
||||
await addScreenToGroup({
|
||||
group_id: newRootGroup.id,
|
||||
screen_id: result.mainScreen.screen_id ?? result.mainScreen.screenId,
|
||||
screen_id: result.mainScreen.screen_id,
|
||||
screen_role: screenRole || "MAIN",
|
||||
display_order: displayOrder, // 원본 정렬순서 유지
|
||||
target_company_code: finalCompanyCode, // 대상 회사 코드 전달
|
||||
});
|
||||
stats.screens++;
|
||||
console.log(`✅ 화면 복제 완료: ${result.mainScreen.screen_name ?? result.mainScreen.screenName} (${screen.screen_id ?? screen.screenId} → ${result.mainScreen.screen_id ?? result.mainScreen.screenId})`);
|
||||
console.log(`✅ 화면 복제 완료: ${result.mainScreen.screen_name} (${screen.screen_id} → ${result.mainScreen.screen_id})`);
|
||||
}
|
||||
} catch (screenError) {
|
||||
console.error(`화면 복제 실패 (${screen.screen_code ?? screen.screenCode}):`, screenError);
|
||||
console.error(`화면 복제 실패 (${screen.screen_code}):`, screenError);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1068,7 +1068,7 @@ export default function CopyScreenModal({
|
||||
|
||||
// 7-1. 메뉴 동기화 (화면 그룹 → 메뉴) - 항상 실행
|
||||
const syncResponse = await apiClient.post("/screen-groups/sync/screen-to-menu", {
|
||||
targetCompanyCode: finalCompanyCode,
|
||||
target_company_code: finalCompanyCode,
|
||||
});
|
||||
|
||||
if (syncResponse.data?.success) {
|
||||
@@ -1079,9 +1079,9 @@ export default function CopyScreenModal({
|
||||
console.log("📋 화면-메뉴 할당 복제 시작...");
|
||||
|
||||
const menuAssignResponse = await apiClient.post("/screen-management/copy-menu-assignments", {
|
||||
sourceCompanyCode: sourceGroup.company_code,
|
||||
targetCompanyCode: finalCompanyCode,
|
||||
screenIdMap,
|
||||
source_company_code: sourceGroup.company_code,
|
||||
target_company_code: finalCompanyCode,
|
||||
screen_id_map: screenIdMap,
|
||||
});
|
||||
|
||||
if (menuAssignResponse.data?.success) {
|
||||
@@ -1096,8 +1096,8 @@ export default function CopyScreenModal({
|
||||
console.log("📋 채번규칙 복제 시작...");
|
||||
|
||||
const numberingResponse = await apiClient.post("/numbering-rules/copy-for-company", {
|
||||
sourceCompanyCode: sourceGroup.company_code,
|
||||
targetCompanyCode: finalCompanyCode,
|
||||
source_company_code: sourceGroup.company_code,
|
||||
target_company_code: finalCompanyCode,
|
||||
});
|
||||
|
||||
if (numberingResponse.data?.success) {
|
||||
@@ -1124,8 +1124,8 @@ export default function CopyScreenModal({
|
||||
console.log("📋 카테고리 값 복제 시작...");
|
||||
|
||||
const response = await apiClient.post("/screen-management/copy-category-mapping", {
|
||||
sourceCompanyCode: sourceGroup.company_code,
|
||||
targetCompanyCode: finalCompanyCode,
|
||||
source_company_code: sourceGroup.company_code,
|
||||
target_company_code: finalCompanyCode,
|
||||
});
|
||||
|
||||
if (response.data?.success) {
|
||||
@@ -1148,8 +1148,8 @@ export default function CopyScreenModal({
|
||||
console.log("📋 테이블 타입 컬럼 복제 시작...");
|
||||
|
||||
const response = await apiClient.post("/screen-management/copy-table-type-columns", {
|
||||
sourceCompanyCode: sourceGroup.company_code,
|
||||
targetCompanyCode: finalCompanyCode,
|
||||
source_company_code: sourceGroup.company_code,
|
||||
target_company_code: finalCompanyCode,
|
||||
});
|
||||
|
||||
if (response.data?.success) {
|
||||
@@ -1169,9 +1169,9 @@ export default function CopyScreenModal({
|
||||
try {
|
||||
await apiClient.post("/audit-log", {
|
||||
action: "COPY",
|
||||
resourceType: "SCREEN",
|
||||
resourceId: String(sourceGroup.id),
|
||||
resourceName: sourceGroup.group_name,
|
||||
resource_type: "SCREEN",
|
||||
resource_id: String(sourceGroup.id),
|
||||
resource_name: sourceGroup.group_name,
|
||||
summary: `그룹 "${sourceGroup.group_name}" → "${rootGroupName}" 복제 (그룹 ${stats.groups}개, 화면 ${stats.screens}개)${finalCompanyCode !== sourceGroup.company_code ? ` [${sourceGroup.company_code} → ${finalCompanyCode}]` : ""}`,
|
||||
changes: {
|
||||
after: {
|
||||
@@ -1663,7 +1663,7 @@ export default function CopyScreenModal({
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-base sm:text-lg">화면 복제</DialogTitle>
|
||||
<DialogDescription className="text-xs sm:text-sm">
|
||||
"{sourceScreen?.screen_name ?? sourceScreen?.screenName}" 화면을 복제합니다.
|
||||
"{sourceScreen?.screen_name}" 화면을 복제합니다.
|
||||
{linkedScreens.length > 0 && ` (모달 ${linkedScreens.length}개 포함)`}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
@@ -1758,7 +1758,7 @@ export default function CopyScreenModal({
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{companies
|
||||
.filter((company) => company.companyCode !== (sourceScreen?.company_code ?? sourceScreen?.companyCode))
|
||||
.filter((company) => company.companyCode !== sourceScreen?.company_code)
|
||||
.map((company) => (
|
||||
<SelectItem key={company.companyCode} value={company.companyCode}>
|
||||
{company.companyName}
|
||||
@@ -1768,7 +1768,7 @@ export default function CopyScreenModal({
|
||||
</Select>
|
||||
{sourceScreen && (
|
||||
<p className="mt-1 text-[10px] text-amber-600">
|
||||
* 원본 회사({sourceScreen.company_code ?? sourceScreen.companyCode})로는 복제할 수 없습니다
|
||||
* 원본 회사({sourceScreen.company_code})로는 복제할 수 없습니다
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -948,8 +948,8 @@ export const EditModal: React.FC<EditModalProps> = ({ className }) => {
|
||||
|
||||
try {
|
||||
const response = await dynamicFormApi.saveFormData({
|
||||
screenId: modalState.screenId || 0,
|
||||
tableName: screenData.screenInfo.table_name,
|
||||
screen_id: modalState.screenId || 0,
|
||||
table_name: screenData.screenInfo.table_name,
|
||||
data: insertData,
|
||||
});
|
||||
|
||||
@@ -1332,8 +1332,8 @@ export const EditModal: React.FC<EditModalProps> = ({ className }) => {
|
||||
console.log("[EditModal] 최종 저장 데이터:", masterDataToSave);
|
||||
|
||||
const response = await dynamicFormApi.saveFormData({
|
||||
screenId: modalState.screenId!,
|
||||
tableName: screenData.screenInfo.table_name,
|
||||
screen_id: modalState.screenId!,
|
||||
table_name: screenData.screenInfo.table_name,
|
||||
data: masterDataToSave,
|
||||
});
|
||||
|
||||
@@ -1470,7 +1470,7 @@ export const EditModal: React.FC<EditModalProps> = ({ className }) => {
|
||||
});
|
||||
|
||||
const response = await dynamicFormApi.updateFormData(recordId, {
|
||||
tableName: screenData.screenInfo.table_name,
|
||||
table_name: screenData.screenInfo.table_name,
|
||||
data: dataToSave,
|
||||
});
|
||||
|
||||
|
||||
@@ -464,7 +464,7 @@ export const EnhancedInteractiveScreenViewer: React.FC<EnhancedInteractiveScreen
|
||||
<CardContent className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="outline">테이블</Badge>
|
||||
<span className="text-sm">{screenInfo.tableName}</span>
|
||||
<span className="text-sm">{screenInfo.table_name}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="outline">필드</Badge>
|
||||
|
||||
@@ -162,24 +162,24 @@ export const FileAttachmentDetailModal: React.FC<FileAttachmentDetailModalProps>
|
||||
if (response.success && response.data) {
|
||||
const newFiles: FileInfo[] = response.data.map((file: any) => ({
|
||||
objid: file.objid || `temp_${Date.now()}_${Math.random()}`,
|
||||
savedFileName: file.saved_file_name || file.savedFileName,
|
||||
realFileName: file.real_file_name || file.realFileName,
|
||||
fileSize: file.file_size || file.fileSize,
|
||||
fileExt: file.file_ext || file.fileExt,
|
||||
filePath: file.file_path || file.filePath,
|
||||
savedFileName: file.saved_file_name,
|
||||
realFileName: file.real_file_name,
|
||||
fileSize: file.file_size,
|
||||
fileExt: file.file_ext,
|
||||
filePath: file.file_path,
|
||||
docType: fileConfig.docType,
|
||||
docTypeName: fileConfig.docTypeName,
|
||||
targetObjid: file.target_objid || file.targetObjid || recordId || screenId || '',
|
||||
parentTargetObjid: file.parent_target_objid || file.parentTargetObjid,
|
||||
companyCode: file.company_code || file.companyCode || 'DEFAULT',
|
||||
targetObjid: file.target_objid || recordId || screenId || '',
|
||||
parentTargetObjid: file.parent_target_objid,
|
||||
companyCode: file.company_code || 'DEFAULT',
|
||||
writer: file.writer || 'user',
|
||||
regdate: file.regdate || new Date().toISOString(),
|
||||
status: file.status || 'ACTIVE',
|
||||
// 호환성 속성들
|
||||
path: file.file_path || file.filePath,
|
||||
name: file.real_file_name || file.realFileName,
|
||||
path: file.file_path,
|
||||
name: file.real_file_name,
|
||||
id: file.objid,
|
||||
size: file.file_size || file.fileSize,
|
||||
size: file.file_size,
|
||||
type: fileConfig.docType,
|
||||
uploadedAt: file.regdate || new Date().toISOString(),
|
||||
}));
|
||||
|
||||
@@ -879,16 +879,16 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
|
||||
}
|
||||
|
||||
// 없으면 테이블 타입 관리에서 설정된 값 찾기
|
||||
const tableColumn = tableColumns.find((col) => col.columnName === columnName);
|
||||
const tableColumn = tableColumns.find((col) => col.column_name === columnName);
|
||||
|
||||
// input_type 우선 사용 (category 등)
|
||||
const inputType = (tableColumn as any)?.input_type || (tableColumn as any)?.inputType;
|
||||
const inputType = tableColumn?.input_type;
|
||||
if (inputType) {
|
||||
return inputType;
|
||||
}
|
||||
|
||||
// 없으면 webType 사용
|
||||
return tableColumn?.webType || "text";
|
||||
// 없으면 web_type 사용
|
||||
return tableColumn?.web_type || "text";
|
||||
},
|
||||
[component.columns, tableColumns],
|
||||
);
|
||||
@@ -903,9 +903,9 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
|
||||
}
|
||||
|
||||
// 없으면 테이블 타입 관리에서 설정된 값 찾기
|
||||
const tableColumn = tableColumns.find((col) => col.columnName === columnName);
|
||||
const tableColumn = tableColumns.find((col) => col.column_name === columnName);
|
||||
try {
|
||||
return tableColumn?.detailSettings ? JSON.parse(tableColumn.detailSettings) : {};
|
||||
return tableColumn?.detail_settings ? JSON.parse(tableColumn.detail_settings) : {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
@@ -1009,13 +1009,13 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
|
||||
setTableColumns(columns);
|
||||
|
||||
// 🆕 전체 컬럼 목록 설정
|
||||
const columnNames = columns.map((col) => col.columnName);
|
||||
const columnNames = columns.map((col) => col.column_name);
|
||||
setAllAvailableColumns(columnNames);
|
||||
|
||||
// 🆕 컬럼명 -> 라벨 매핑 생성
|
||||
const labels: Record<string, string> = {};
|
||||
columns.forEach((col) => {
|
||||
labels[col.columnName] = col.display_name || col.columnName;
|
||||
labels[col.column_name] = col.display_name || col.column_name;
|
||||
});
|
||||
setColumnLabels(labels);
|
||||
|
||||
|
||||
@@ -410,17 +410,17 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
console.log("📊 팝업 화면 로드 완료:", {
|
||||
componentsCount: layout.components?.length || 0,
|
||||
screenInfo: {
|
||||
screenId: screen.screenId,
|
||||
tableName: screen.tableName
|
||||
screenId: screen.screen_id,
|
||||
tableName: screen.table_name
|
||||
},
|
||||
popupFormData: {}
|
||||
});
|
||||
|
||||
|
||||
setPopupLayout(layout.components || []);
|
||||
setPopupScreenResolution(layout.screenResolution || null);
|
||||
setPopupScreenResolution(layout.screen_resolution || null);
|
||||
setPopupScreenInfo({
|
||||
id: popupScreen.screenId,
|
||||
tableName: screen.tableName
|
||||
tableName: screen.table_name
|
||||
});
|
||||
|
||||
// 팝업 formData 초기화
|
||||
@@ -1778,8 +1778,8 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||
});
|
||||
|
||||
const saveData: DynamicFormData = {
|
||||
screenId: screenInfo.id,
|
||||
tableName: tableName,
|
||||
screen_id: screenInfo.id,
|
||||
table_name: tableName,
|
||||
data: masterDataWithUserInfo,
|
||||
};
|
||||
|
||||
|
||||
@@ -320,12 +320,12 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
|
||||
const screenData = response.data;
|
||||
setPopupLayout(screenData.components || []);
|
||||
setPopupScreenResolution({
|
||||
width: screenData.screenResolution?.width || 1200,
|
||||
height: screenData.screenResolution?.height || 800,
|
||||
width: screenData.screen_resolution?.width || 1200,
|
||||
height: screenData.screen_resolution?.height || 800,
|
||||
});
|
||||
setPopupScreenInfo({
|
||||
id: screenData.id,
|
||||
tableName: screenData.tableName,
|
||||
tableName: screenData.table_name,
|
||||
});
|
||||
} else {
|
||||
toast.error("팝업 화면을 불러올 수 없습니다.");
|
||||
@@ -646,7 +646,7 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
|
||||
});
|
||||
|
||||
const saveData: DynamicFormData = {
|
||||
tableName: screenInfo.tableName,
|
||||
table_name: screenInfo.tableName,
|
||||
data: masterFormData,
|
||||
};
|
||||
|
||||
@@ -750,7 +750,7 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
|
||||
);
|
||||
if (columnsResponse.data?.success && columnsResponse.data?.data) {
|
||||
const columnsData = columnsResponse.data.data.columns || columnsResponse.data.data;
|
||||
targetTableColumns = columnsData.map((col: any) => col.columnName || col.column_name || col.name);
|
||||
targetTableColumns = columnsData.map((col: any) => col.column_name || col.name);
|
||||
console.log("📍 대상 테이블 컬럼 목록:", targetTableColumns);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -1033,7 +1033,7 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
|
||||
isInteractive={true}
|
||||
isDesignMode={false}
|
||||
formData={{
|
||||
screenId, // 🎯 화면 ID 전달
|
||||
screen_id: screenId, // 🎯 화면 ID 전달
|
||||
// 🎯 백엔드 API가 기대하는 정확한 형식으로 설정
|
||||
autoLink: true, // 자동 연결 활성화
|
||||
linkedTable: "screen_files", // 연결 테이블
|
||||
|
||||
@@ -240,8 +240,8 @@ export const SaveModal: React.FC<SaveModalProps> = ({
|
||||
const tableName = screenData.table_name || components.find((c) => c.columnName)?.table_name || "dynamic_form_data";
|
||||
|
||||
const saveData: DynamicFormData = {
|
||||
screenId: screenId,
|
||||
tableName: tableName,
|
||||
screen_id: screenId,
|
||||
table_name: tableName,
|
||||
data: masterDataWithUserInfo,
|
||||
};
|
||||
|
||||
|
||||
@@ -368,8 +368,8 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
|
||||
if (response.success && response.data) {
|
||||
// tableName과 displayName 매핑 (백엔드에서 displayName으로 라벨을 반환함)
|
||||
const tableList = response.data.map((table: any) => ({
|
||||
tableName: table.tableName,
|
||||
tableLabel: table.displayName || table.tableName,
|
||||
tableName: table.table_name,
|
||||
tableLabel: table.display_name || table.table_name,
|
||||
}));
|
||||
setTables(tableList);
|
||||
}
|
||||
@@ -679,7 +679,7 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
// 화면 레이아웃 로드
|
||||
const layoutData = await screenApi.getLayout(screen.screenId);
|
||||
const layoutData = await screenApi.getLayout(screen.screen_id);
|
||||
console.log("📊 미리보기 레이아웃 로드:", layoutData);
|
||||
setPreviewLayout(layoutData);
|
||||
} catch (error) {
|
||||
@@ -1923,8 +1923,8 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
|
||||
isSelected={false}
|
||||
isDesignMode={false}
|
||||
onClick={() => {}}
|
||||
screenId={screenToPreview!.screenId}
|
||||
tableName={screenToPreview?.tableName}
|
||||
screenId={screenToPreview!.screen_id}
|
||||
tableName={screenToPreview?.table_name}
|
||||
formData={previewFormData}
|
||||
onFormDataChange={(fieldName, value) => {
|
||||
setPreviewFormData((prev) => ({
|
||||
|
||||
@@ -1365,10 +1365,10 @@ function TableColumnAccordion({
|
||||
조인
|
||||
</Badge>
|
||||
<span className={`text-[10px] font-medium ${hasJoinSetting ? "text-orange-700" : "text-muted-foreground"}`}>
|
||||
{editingJoin && editingJoin.columnName === (selectedColumn?.columnName || editingField) ? "연결 편집" : (hasJoinSetting ? "연결 정보" : "연결 설정")}
|
||||
{editingJoin && editingJoin.columnName === (selectedColumn?.column_name || editingField) ? "연결 편집" : (hasJoinSetting ? "연결 정보" : "연결 설정")}
|
||||
</span>
|
||||
</div>
|
||||
{editingJoin && editingJoin.columnName === (selectedColumn?.columnName || editingField) ? (
|
||||
{editingJoin && editingJoin.columnName === (selectedColumn?.column_name || editingField) ? (
|
||||
<div className="flex gap-1">
|
||||
<Button variant="ghost" size="sm" className="h-5 px-1.5 text-[10px] text-muted-foreground hover:text-foreground" onClick={() => setEditingJoin(null)}>
|
||||
취소
|
||||
@@ -1383,10 +1383,10 @@ function TableColumnAccordion({
|
||||
size="sm"
|
||||
className={`h-5 px-1 text-[10px] ${hasJoinSetting ? "text-amber-600 hover:text-orange-800" : "text-muted-foreground hover:text-foreground"}`}
|
||||
onClick={() => startEditingJoin(
|
||||
selectedColumn?.columnName || editingField,
|
||||
isJoinKey && joinRef ? joinRef.refTable : (selectedColumn?.referenceTable || ""),
|
||||
isJoinKey && joinRef ? joinRef.refColumn : (selectedColumn?.referenceColumn || ""),
|
||||
selectedColumn?.displayColumn || ""
|
||||
selectedColumn?.column_name || editingField,
|
||||
isJoinKey && joinRef ? joinRef.refTable : (selectedColumn?.reference_table || ""),
|
||||
isJoinKey && joinRef ? joinRef.refColumn : (selectedColumn?.reference_column || ""),
|
||||
selectedColumn?.display_column || ""
|
||||
)}
|
||||
>
|
||||
<Settings2 className="h-3 w-3 mr-0.5" />
|
||||
@@ -1395,7 +1395,7 @@ function TableColumnAccordion({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{editingJoin && editingJoin.columnName === (selectedColumn?.columnName || editingField) ? (
|
||||
{editingJoin && editingJoin.columnName === (selectedColumn?.column_name || editingField) ? (
|
||||
<JoinSettingEditor
|
||||
editingJoin={editingJoin}
|
||||
setEditingJoin={setEditingJoin}
|
||||
@@ -1410,11 +1410,11 @@ function TableColumnAccordion({
|
||||
<div className="space-y-1.5 text-xs">
|
||||
<div>
|
||||
<span className="text-muted-foreground">대상 테이블: </span>
|
||||
<span className="font-mono text-orange-800">{isJoinKey && joinRef ? joinRef.refTable : selectedColumn?.referenceTable}</span>
|
||||
<span className="font-mono text-orange-800">{isJoinKey && joinRef ? joinRef.refTable : selectedColumn?.reference_table}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-muted-foreground">연결 컬럼: </span>
|
||||
<span className="font-mono text-orange-800">{isJoinKey && joinRef ? joinRef.refColumn : selectedColumn?.referenceColumn}</span>
|
||||
<span className="font-mono text-orange-800">{isJoinKey && joinRef ? joinRef.refColumn : selectedColumn?.reference_column}</span>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
@@ -1499,9 +1499,9 @@ function JoinSettingEditor({
|
||||
const [refColSearchOpen, setRefColSearchOpen] = useState(false);
|
||||
const [displayColSearchOpen, setDisplayColSearchOpen] = useState(false);
|
||||
|
||||
const selectedTable = allTables.find(t => t.tableName === editingJoin.referenceTable);
|
||||
const selectedRefCol = refTableColumns.find(c => c.columnName === editingJoin.referenceColumn);
|
||||
const selectedDisplayCol = refTableColumns.find(c => c.columnName === editingJoin.displayColumn);
|
||||
const selectedTable = allTables.find(t => t.table_name === editingJoin.referenceTable);
|
||||
const selectedRefCol = refTableColumns.find(c => c.column_name === editingJoin.referenceColumn);
|
||||
const selectedDisplayCol = refTableColumns.find(c => c.column_name === editingJoin.displayColumn);
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
@@ -1515,7 +1515,7 @@ function JoinSettingEditor({
|
||||
size="sm"
|
||||
className="w-full justify-between h-7 text-xs font-normal"
|
||||
>
|
||||
{selectedTable?.displayName || editingJoin.referenceTable || "테이블 선택"}
|
||||
{selectedTable?.display_name || editingJoin.referenceTable || "테이블 선택"}
|
||||
<ChevronsUpDown className="h-3 w-3 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
@@ -1527,11 +1527,11 @@ function JoinSettingEditor({
|
||||
<CommandGroup>
|
||||
{allTables.map((t, idx) => (
|
||||
<CommandItem
|
||||
key={`${t.tableName}-${idx}`}
|
||||
value={t.displayName || t.tableName}
|
||||
key={`${t.table_name}-${idx}`}
|
||||
value={t.display_name || t.table_name}
|
||||
onSelect={() => {
|
||||
setEditingJoin({ ...editingJoin, referenceTable: t.tableName, referenceColumn: "", displayColumn: "" });
|
||||
loadRefTableColumns(t.tableName);
|
||||
setEditingJoin({ ...editingJoin, referenceTable: t.table_name, referenceColumn: "", displayColumn: "" });
|
||||
loadRefTableColumns(t.table_name);
|
||||
setTableSearchOpen(false);
|
||||
}}
|
||||
className="text-xs"
|
||||
@@ -1539,10 +1539,10 @@ function JoinSettingEditor({
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-3 w-3",
|
||||
editingJoin.referenceTable === t.tableName ? "opacity-100" : "opacity-0"
|
||||
editingJoin.referenceTable === t.table_name ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
{t.displayName || t.tableName}
|
||||
{t.display_name || t.table_name}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
@@ -1563,7 +1563,7 @@ function JoinSettingEditor({
|
||||
className="w-full justify-between h-7 text-xs font-normal"
|
||||
disabled={!editingJoin.referenceTable || loadingRefColumns}
|
||||
>
|
||||
{loadingRefColumns ? "로딩중..." : (selectedRefCol?.displayName || editingJoin.referenceColumn || "컬럼 선택")}
|
||||
{loadingRefColumns ? "로딩중..." : (selectedRefCol?.display_name || editingJoin.referenceColumn || "컬럼 선택")}
|
||||
<ChevronsUpDown className="h-3 w-3 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
@@ -1575,10 +1575,10 @@ function JoinSettingEditor({
|
||||
<CommandGroup>
|
||||
{refTableColumns.map(c => (
|
||||
<CommandItem
|
||||
key={c.columnName}
|
||||
value={c.displayName || c.columnName}
|
||||
key={c.column_name}
|
||||
value={c.display_name || c.column_name}
|
||||
onSelect={() => {
|
||||
setEditingJoin({ ...editingJoin, referenceColumn: c.columnName });
|
||||
setEditingJoin({ ...editingJoin, referenceColumn: c.column_name });
|
||||
setRefColSearchOpen(false);
|
||||
}}
|
||||
className="text-xs"
|
||||
@@ -1586,10 +1586,10 @@ function JoinSettingEditor({
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-3 w-3",
|
||||
editingJoin.referenceColumn === c.columnName ? "opacity-100" : "opacity-0"
|
||||
editingJoin.referenceColumn === c.column_name ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
{c.displayName || c.columnName}
|
||||
{c.display_name || c.column_name}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
@@ -1610,7 +1610,7 @@ function JoinSettingEditor({
|
||||
className="w-full justify-between h-7 text-xs font-normal"
|
||||
disabled={!editingJoin.referenceTable || loadingRefColumns}
|
||||
>
|
||||
{selectedDisplayCol?.displayName || editingJoin.displayColumn || "컬럼 선택"}
|
||||
{selectedDisplayCol?.display_name || editingJoin.displayColumn || "컬럼 선택"}
|
||||
<ChevronsUpDown className="h-3 w-3 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
@@ -1622,10 +1622,10 @@ function JoinSettingEditor({
|
||||
<CommandGroup>
|
||||
{refTableColumns.map(c => (
|
||||
<CommandItem
|
||||
key={c.columnName}
|
||||
value={c.displayName || c.columnName}
|
||||
key={c.column_name}
|
||||
value={c.display_name || c.column_name}
|
||||
onSelect={() => {
|
||||
setEditingJoin({ ...editingJoin, displayColumn: c.columnName });
|
||||
setEditingJoin({ ...editingJoin, displayColumn: c.column_name });
|
||||
setDisplayColSearchOpen(false);
|
||||
}}
|
||||
className="text-xs"
|
||||
@@ -1633,10 +1633,10 @@ function JoinSettingEditor({
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-3 w-3",
|
||||
editingJoin.displayColumn === c.columnName ? "opacity-100" : "opacity-0"
|
||||
editingJoin.displayColumn === c.column_name ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
{c.displayName || c.columnName}
|
||||
{c.display_name || c.column_name}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
@@ -2627,7 +2627,7 @@ function FieldMappingTab({
|
||||
const columnDisplayMap = useMemo(() => {
|
||||
const map: Record<string, string> = {};
|
||||
tableColumns.forEach((tc) => {
|
||||
map[tc.columnName] = tc.displayName || tc.columnName;
|
||||
map[tc.column_name] = tc.display_name || tc.column_name;
|
||||
});
|
||||
return map;
|
||||
}, [tableColumns]);
|
||||
@@ -2806,8 +2806,8 @@ function FieldMappingTab({
|
||||
) : (
|
||||
tableColumns.map((tableCol) => (
|
||||
<CommandItem
|
||||
key={tableCol.columnName}
|
||||
value={tableCol.columnName}
|
||||
key={tableCol.column_name}
|
||||
value={tableCol.column_name}
|
||||
onSelect={(value) => {
|
||||
toast.info(`컬럼 변경: ${col} → ${value}`, {
|
||||
description: "저장 기능은 아직 구현 중입니다."
|
||||
@@ -2820,17 +2820,17 @@ function FieldMappingTab({
|
||||
<div className="flex flex-col">
|
||||
<span className={cn(
|
||||
"font-medium",
|
||||
tableCol.columnName === col && "text-primary"
|
||||
tableCol.column_name === col && "text-primary"
|
||||
)}>
|
||||
{tableCol.displayName || tableCol.columnName}
|
||||
{tableCol.display_name || tableCol.column_name}
|
||||
</span>
|
||||
{tableCol.displayName && tableCol.displayName !== tableCol.columnName && (
|
||||
{tableCol.display_name && tableCol.display_name !== tableCol.column_name && (
|
||||
<span className="text-[10px] text-muted-foreground font-mono">
|
||||
{tableCol.columnName}
|
||||
{tableCol.column_name}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{tableCol.columnName === col && (
|
||||
{tableCol.column_name === col && (
|
||||
<Check className="ml-auto h-3 w-3 text-primary" />
|
||||
)}
|
||||
</CommandItem>
|
||||
|
||||
@@ -40,7 +40,7 @@ export const dataApi = {
|
||||
const page = raw.page ?? params?.page ?? 1;
|
||||
const size = raw.size ?? params?.size ?? items.length;
|
||||
const total = raw.total ?? items.length;
|
||||
const totalPages = raw.total_pages ?? raw.totalPages ?? Math.max(1, Math.ceil(total / (size || 1)));
|
||||
const totalPages = raw.total_pages ?? Math.max(1, Math.ceil(total / (size || 1)));
|
||||
|
||||
return { data: items, total, page, size, totalPages };
|
||||
},
|
||||
|
||||
@@ -85,11 +85,11 @@ export const loadDataflowRelationship = async (diagramId: number) => {
|
||||
// 기존 구조를 redesigned 구조로 변환
|
||||
relationshipsData = {
|
||||
description: firstRelation.note || "",
|
||||
connectionType: firstRelation.connectionType || "data_save",
|
||||
connectionType: firstRelation.connection_type || firstRelation.connectionType || "data_save",
|
||||
fromConnection: { id: 0, name: "메인 데이터베이스 (현재 시스템)" }, // 기본값
|
||||
toConnection: { id: 0, name: "메인 데이터베이스 (현재 시스템)" }, // 기본값
|
||||
fromTable: firstRelation.fromTable,
|
||||
toTable: firstRelation.toTable,
|
||||
fromTable: firstRelation.from_table || firstRelation.fromTable,
|
||||
toTable: firstRelation.to_table || firstRelation.toTable,
|
||||
actionType: "insert", // 기본값
|
||||
controlConditions: [],
|
||||
actionConditions: [],
|
||||
@@ -108,9 +108,9 @@ export const loadDataflowRelationship = async (diagramId: number) => {
|
||||
const plan = diagram.plan.find((p) => p.id === firstRelation.id);
|
||||
if (plan && plan.actions && plan.actions.length > 0) {
|
||||
const firstAction = plan.actions[0];
|
||||
relationshipsData.actionType = firstAction.actionType || "insert";
|
||||
relationshipsData.fieldMappings = firstAction.fieldMappings || [];
|
||||
relationshipsData.actionConditions = firstAction.conditions || [];
|
||||
relationshipsData.actionType = firstAction.action_type || firstAction.actionType || "insert";
|
||||
relationshipsData.fieldMappings = firstAction.field_mappings || firstAction.fieldMappings || [];
|
||||
relationshipsData.actionConditions = firstAction.action_conditions || firstAction.conditions || [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ import { apiClient, ApiResponse } from "./client";
|
||||
|
||||
// 동적 폼 데이터 타입
|
||||
export interface DynamicFormData {
|
||||
screenId: number;
|
||||
tableName: string;
|
||||
screen_id?: number;
|
||||
table_name: string;
|
||||
data: Record<string, any>;
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ export class DynamicFormApi {
|
||||
});
|
||||
|
||||
const response = await apiClient.patch(`/dynamic-form/${id}/partial`, {
|
||||
tableName,
|
||||
table_name: tableName,
|
||||
originalData,
|
||||
newData,
|
||||
});
|
||||
@@ -375,7 +375,7 @@ export class DynamicFormApi {
|
||||
console.log("✅ 폼 데이터 검증 요청:", { tableName, data });
|
||||
|
||||
const response = await apiClient.post(`/dynamic-form/validate`, {
|
||||
tableName,
|
||||
table_name: tableName,
|
||||
data,
|
||||
});
|
||||
|
||||
@@ -440,8 +440,8 @@ export class DynamicFormApi {
|
||||
filters?: Record<string, any>;
|
||||
autoFilter?: {
|
||||
enabled: boolean;
|
||||
filterColumn?: string;
|
||||
userField?: string;
|
||||
filter_column?: string;
|
||||
user_field?: string;
|
||||
};
|
||||
},
|
||||
): Promise<ApiResponse<any[]>> {
|
||||
@@ -455,8 +455,8 @@ export class DynamicFormApi {
|
||||
size: params?.pageSize || params?.size || 100, // 기본값 100
|
||||
autoFilter: params?.autoFilter ?? {
|
||||
enabled: true,
|
||||
filterColumn: "company_code",
|
||||
userField: "company_code",
|
||||
filter_column: "company_code",
|
||||
user_field: "company_code",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -675,7 +675,7 @@ export class DynamicFormApi {
|
||||
masterFieldValues: Record<string, any>,
|
||||
numberingRuleId?: string,
|
||||
afterUploadFlowId?: string,
|
||||
afterUploadFlows?: Array<{ flowId: string; order: number }>
|
||||
afterUploadFlows?: Array<{ flow_id: string; order: number }>
|
||||
): Promise<ApiResponse<MasterDetailSimpleUploadResult>> {
|
||||
try {
|
||||
console.log("📤 마스터-디테일 간단 모드 업로드:", {
|
||||
@@ -688,11 +688,11 @@ export class DynamicFormApi {
|
||||
|
||||
const response = await apiClient.post(`/data/master-detail/upload-simple`, {
|
||||
screen_id: screenId,
|
||||
detailData,
|
||||
masterFieldValues,
|
||||
numberingRuleId,
|
||||
afterUploadFlowId,
|
||||
afterUploadFlows,
|
||||
detail_data: detailData,
|
||||
master_field_values: masterFieldValues,
|
||||
numbering_rule_id: numberingRuleId,
|
||||
after_upload_flow_id: afterUploadFlowId,
|
||||
after_upload_flows: afterUploadFlows,
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
+15
-15
@@ -55,20 +55,20 @@ export const uploadFiles = async (params: {
|
||||
|
||||
// 추가 파라미터들 추가
|
||||
if (params.tableName) formData.append("table_name", params.tableName);
|
||||
if (params.fieldName) formData.append("fieldName", params.fieldName);
|
||||
if (params.recordId) formData.append("recordId", String(params.recordId));
|
||||
if (params.docType) formData.append("docType", params.docType);
|
||||
if (params.docTypeName) formData.append("docTypeName", params.docTypeName);
|
||||
if (params.targetObjid) formData.append("targetObjid", params.targetObjid);
|
||||
if (params.parentTargetObjid) formData.append("parentTargetObjid", params.parentTargetObjid);
|
||||
if (params.linkedTable) formData.append("linkedTable", params.linkedTable);
|
||||
if (params.linkedField) formData.append("linkedField", params.linkedField);
|
||||
if (params.autoLink !== undefined) formData.append("autoLink", params.autoLink.toString());
|
||||
if (params.columnName) formData.append("columnName", params.columnName);
|
||||
if (params.isVirtualFileColumn !== undefined) formData.append("isVirtualFileColumn", params.isVirtualFileColumn.toString());
|
||||
if (params.fieldName) formData.append("field_name", params.fieldName);
|
||||
if (params.recordId) formData.append("record_id", String(params.recordId));
|
||||
if (params.docType) formData.append("doc_type", params.docType);
|
||||
if (params.docTypeName) formData.append("doc_type_name", params.docTypeName);
|
||||
if (params.targetObjid) formData.append("target_objid", params.targetObjid);
|
||||
if (params.parentTargetObjid) formData.append("parent_target_objid", params.parentTargetObjid);
|
||||
if (params.linkedTable) formData.append("linked_table", params.linkedTable);
|
||||
if (params.linkedField) formData.append("linked_field", params.linkedField);
|
||||
if (params.autoLink !== undefined) formData.append("auto_link", params.autoLink.toString());
|
||||
if (params.columnName) formData.append("column_name", params.columnName);
|
||||
if (params.isVirtualFileColumn !== undefined) formData.append("is_virtual_file_column", params.isVirtualFileColumn.toString());
|
||||
if (params.companyCode) formData.append("company_code", params.companyCode); // 🔒 멀티테넌시
|
||||
// 🆕 레코드 모드 플래그 추가 (백엔드에서 attachments 컬럼 자동 업데이트용)
|
||||
if (params.isRecordMode !== undefined) formData.append("isRecordMode", params.isRecordMode.toString());
|
||||
if (params.isRecordMode !== undefined) formData.append("is_record_mode", params.isRecordMode.toString());
|
||||
|
||||
const response = await apiClient.post("/files/upload", formData, {
|
||||
headers: {
|
||||
@@ -145,10 +145,10 @@ export const getFileInfo = async (fileId: string, serverFilename: string) => {
|
||||
*/
|
||||
export const getComponentFiles = async (params: {
|
||||
screen_id: number;
|
||||
componentId: string;
|
||||
component_id: string;
|
||||
table_name?: string;
|
||||
recordId?: string;
|
||||
columnName?: string;
|
||||
record_id?: string;
|
||||
column_name?: string;
|
||||
}): Promise<{
|
||||
success: boolean;
|
||||
templateFiles: FileInfo[];
|
||||
|
||||
+12
-13
@@ -12,13 +12,13 @@ export interface GetLayoutsParams {
|
||||
page?: number;
|
||||
size?: number;
|
||||
category?: LayoutCategory;
|
||||
layoutType?: LayoutType;
|
||||
searchTerm?: string;
|
||||
includePublic?: boolean;
|
||||
layout_type?: LayoutType;
|
||||
search_term?: string;
|
||||
include_public?: boolean;
|
||||
}
|
||||
|
||||
export interface DuplicateLayoutRequest {
|
||||
newName: string;
|
||||
new_name: string;
|
||||
}
|
||||
|
||||
class LayoutApiService {
|
||||
@@ -84,9 +84,9 @@ class LayoutApiService {
|
||||
*/
|
||||
async searchLayouts(
|
||||
searchTerm: string,
|
||||
params: Omit<GetLayoutsParams, "searchTerm"> = {},
|
||||
params: Omit<GetLayoutsParams, "search_term"> = {},
|
||||
): Promise<GetLayoutsResponse> {
|
||||
return this.getLayouts({ ...params, searchTerm });
|
||||
return this.getLayouts({ ...params, search_term: searchTerm });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,23 +104,23 @@ class LayoutApiService {
|
||||
*/
|
||||
async getLayoutsByType(
|
||||
layoutType: LayoutType,
|
||||
params: Omit<GetLayoutsParams, "layoutType"> = {},
|
||||
params: Omit<GetLayoutsParams, "layout_type"> = {},
|
||||
): Promise<GetLayoutsResponse> {
|
||||
return this.getLayouts({ ...params, layoutType });
|
||||
return this.getLayouts({ ...params, layout_type: layoutType });
|
||||
}
|
||||
|
||||
/**
|
||||
* 공개 레이아웃만 조회
|
||||
*/
|
||||
async getPublicLayouts(params: Omit<GetLayoutsParams, "includePublic"> = {}): Promise<GetLayoutsResponse> {
|
||||
return this.getLayouts({ ...params, includePublic: true });
|
||||
async getPublicLayouts(params: Omit<GetLayoutsParams, "include_public"> = {}): Promise<GetLayoutsResponse> {
|
||||
return this.getLayouts({ ...params, include_public: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* 개인 레이아웃만 조회
|
||||
*/
|
||||
async getPrivateLayouts(params: Omit<GetLayoutsParams, "includePublic"> = {}): Promise<GetLayoutsResponse> {
|
||||
return this.getLayouts({ ...params, includePublic: false });
|
||||
async getPrivateLayouts(params: Omit<GetLayoutsParams, "include_public"> = {}): Promise<GetLayoutsResponse> {
|
||||
return this.getLayouts({ ...params, include_public: false });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,4 +129,3 @@ export const layoutApi = new LayoutApiService();
|
||||
|
||||
// 기본 내보내기
|
||||
export default layoutApi;
|
||||
|
||||
|
||||
@@ -347,7 +347,7 @@ export async function previewMailTemplate(
|
||||
): Promise<{ html: string }> {
|
||||
return fetchApi<{ html: string }>(`/mail/templates-file/${id}/preview`, {
|
||||
method: 'POST',
|
||||
data: { sampleData },
|
||||
data: { sample_data: sampleData },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -316,10 +316,10 @@ export async function getSecondLevelMenus() {
|
||||
const response = await apiClient.get<{
|
||||
success: boolean;
|
||||
data: Array<{
|
||||
menuObjid: number;
|
||||
menuName: string;
|
||||
parentMenuName: string;
|
||||
screenCode?: string;
|
||||
menu_objid: number;
|
||||
menu_name: string;
|
||||
parent_menu_name: string;
|
||||
screen_code?: string;
|
||||
}>;
|
||||
}>("/table-categories/second-level-menus");
|
||||
return response.data;
|
||||
|
||||
+10
-10
@@ -58,7 +58,7 @@ export function ConditionalContainerConfigPanel({
|
||||
const [screensLoading, setScreensLoading] = useState(false);
|
||||
|
||||
// 🆕 메뉴 기반 카테고리 관련 상태
|
||||
const [availableMenus, setAvailableMenus] = useState<Array<{ menuObjid: number; menuName: string; parentMenuName: string; screenCode?: string }>>([]);
|
||||
const [availableMenus, setAvailableMenus] = useState<Array<{ menu_objid: number; menu_name: string; parent_menu_name: string; screen_code?: string }>>([]);
|
||||
const [menusLoading, setMenusLoading] = useState(false);
|
||||
const [selectedMenuObjid, setSelectedMenuObjid] = useState<number | null>(null);
|
||||
const [menuPopoverOpen, setMenuPopoverOpen] = useState(false);
|
||||
@@ -309,8 +309,8 @@ export function ConditionalContainerConfigPanel({
|
||||
</>
|
||||
) : selectedMenuObjid ? (
|
||||
(() => {
|
||||
const menu = availableMenus.find((m) => m.menuObjid === selectedMenuObjid);
|
||||
return menu ? `${menu.parentMenuName} > ${menu.menuName}` : `메뉴 ${selectedMenuObjid}`;
|
||||
const menu = availableMenus.find((m) => m.menu_objid === selectedMenuObjid);
|
||||
return menu ? `${menu.parent_menu_name} > ${menu.menu_name}` : `메뉴 ${selectedMenuObjid}`;
|
||||
})()
|
||||
) : (
|
||||
"메뉴 선택..."
|
||||
@@ -326,10 +326,10 @@ export function ConditionalContainerConfigPanel({
|
||||
<CommandGroup>
|
||||
{availableMenus.map((menu) => (
|
||||
<CommandItem
|
||||
key={menu.menuObjid}
|
||||
value={`${menu.parentMenuName} ${menu.menuName}`}
|
||||
key={menu.menu_objid}
|
||||
value={`${menu.parent_menu_name} ${menu.menu_name}`}
|
||||
onSelect={() => {
|
||||
setSelectedMenuObjid(menu.menuObjid);
|
||||
setSelectedMenuObjid(menu.menu_objid);
|
||||
setSelectedCategoryColumn("");
|
||||
setSelectedCategoryTableName("");
|
||||
setMenuPopoverOpen(false);
|
||||
@@ -339,14 +339,14 @@ export function ConditionalContainerConfigPanel({
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-3 w-3",
|
||||
selectedMenuObjid === menu.menuObjid ? "opacity-100" : "opacity-0"
|
||||
selectedMenuObjid === menu.menu_objid ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<span>{menu.parentMenuName} > {menu.menuName}</span>
|
||||
{menu.screenCode && (
|
||||
<span>{menu.parent_menu_name} > {menu.menu_name}</span>
|
||||
{menu.screen_code && (
|
||||
<span className="text-[10px] text-muted-foreground">
|
||||
{menu.screenCode}
|
||||
{menu.screen_code}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
+3
-3
@@ -1674,9 +1674,9 @@ function RowNumberingConfigSection({
|
||||
const response = await getNumberingRules();
|
||||
if (response.success && response.data) {
|
||||
setNumberingRules(response.data.map((rule: any, index: number) => ({
|
||||
id: String(rule.ruleId || rule.id || `rule-${index}`),
|
||||
name: rule.ruleName || rule.name || "이름 없음",
|
||||
code: rule.ruleId || rule.code || "",
|
||||
id: String(rule.rule_id || `rule-${index}`),
|
||||
name: rule.rule_name || "이름 없음",
|
||||
code: rule.rule_id || "",
|
||||
})));
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -1347,8 +1347,8 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||
const response = await apiClient.get(`/table-categories/${leftTableName}/${columnName}/values`);
|
||||
if (response.data.success && response.data.data && response.data.data.length > 0) {
|
||||
return response.data.data.map((item: any) => ({
|
||||
value: item.valueCode,
|
||||
label: item.valueLabel,
|
||||
value: item.value_code,
|
||||
label: item.value_label,
|
||||
}));
|
||||
}
|
||||
} catch {
|
||||
|
||||
+19
-19
@@ -198,7 +198,7 @@ interface AdditionalTabConfigPanelProps {
|
||||
tabIndex: number;
|
||||
config: SplitPanelLayoutConfig;
|
||||
updateRightPanel: (updates: Partial<SplitPanelLayoutConfig["rightPanel"]>) => void;
|
||||
availableRightTables: TableInfo[];
|
||||
availableRightTables: any[];
|
||||
leftTableColumns: ColumnInfo[];
|
||||
menuObjid?: number;
|
||||
// 공유 컬럼 로드 상태
|
||||
@@ -323,17 +323,17 @@ const AdditionalTabConfigPanel: React.FC<AdditionalTabConfigPanelProps> = ({
|
||||
<CommandGroup className="max-h-[200px] overflow-auto">
|
||||
{availableRightTables.map((table) => (
|
||||
<CommandItem
|
||||
key={table.tableName}
|
||||
value={`${table.displayName || ""} ${table.tableName}`}
|
||||
onSelect={() => updateTab({ tableName: table.tableName, columns: [] })}
|
||||
key={table.table_name}
|
||||
value={`${table.display_name || ""} ${table.table_name}`}
|
||||
onSelect={() => updateTab({ tableName: table.table_name, columns: [] })}
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
tab.tableName === table.tableName ? "opacity-100" : "opacity-0"
|
||||
tab.tableName === table.table_name ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
{table.displayName || table.tableName}
|
||||
{table.display_name || table.table_name}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
@@ -1431,8 +1431,8 @@ export const SplitPanelLayoutConfigPanel: React.FC<SplitPanelLayoutConfigPanelPr
|
||||
<Database className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
||||
<span className="truncate">
|
||||
{config.leftPanel?.tableName
|
||||
? allTables.find((t) => (t.tableName || t.table_name) === config.leftPanel?.tableName)?.tableLabel ||
|
||||
allTables.find((t) => (t.tableName || t.table_name) === config.leftPanel?.tableName)?.displayName ||
|
||||
? allTables.find((t) => t.table_name === config.leftPanel?.tableName)?.table_label ||
|
||||
allTables.find((t) => t.table_name === config.leftPanel?.tableName)?.display_name ||
|
||||
config.leftPanel?.tableName
|
||||
: "테이블을 선택하세요"}
|
||||
</span>
|
||||
@@ -1466,8 +1466,8 @@ export const SplitPanelLayoutConfigPanel: React.FC<SplitPanelLayoutConfigPanelPr
|
||||
)}
|
||||
/>
|
||||
<Database className="mr-2 h-3.5 w-3.5 text-primary" />
|
||||
{allTables.find((t) => (t.tableName || t.table_name) === screenTableName)?.tableLabel ||
|
||||
allTables.find((t) => (t.tableName || t.table_name) === screenTableName)?.displayName ||
|
||||
{allTables.find((t) => t.table_name === screenTableName)?.table_label ||
|
||||
allTables.find((t) => t.table_name === screenTableName)?.display_name ||
|
||||
screenTableName}
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
@@ -1476,10 +1476,10 @@ export const SplitPanelLayoutConfigPanel: React.FC<SplitPanelLayoutConfigPanelPr
|
||||
{/* 전체 테이블 */}
|
||||
<CommandGroup heading="전체 테이블">
|
||||
{allTables
|
||||
.filter((t) => (t.tableName || t.table_name) !== screenTableName)
|
||||
.filter((t) => t.table_name !== screenTableName)
|
||||
.map((table) => {
|
||||
const tableName = table.tableName || table.table_name;
|
||||
const displayName = table.tableLabel || table.displayName || tableName;
|
||||
const tableName = table.table_name;
|
||||
const displayName = table.table_label || table.display_name || tableName;
|
||||
return (
|
||||
<CommandItem
|
||||
key={tableName}
|
||||
@@ -1823,21 +1823,21 @@ export const SplitPanelLayoutConfigPanel: React.FC<SplitPanelLayoutConfigPanelPr
|
||||
<CommandGroup className="max-h-[200px] overflow-auto">
|
||||
{availableRightTables.map((table) => (
|
||||
<CommandItem
|
||||
key={table.tableName}
|
||||
value={`${table.displayName || ""} ${table.tableName}`}
|
||||
key={table.table_name}
|
||||
value={`${table.display_name || ""} ${table.table_name}`}
|
||||
onSelect={() => {
|
||||
updateRightPanel({ tableName: table.tableName });
|
||||
updateRightPanel({ tableName: table.table_name });
|
||||
setRightTableOpen(false);
|
||||
}}
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
config.rightPanel?.tableName === table.tableName ? "opacity-100" : "opacity-0",
|
||||
config.rightPanel?.tableName === table.table_name ? "opacity-100" : "opacity-0",
|
||||
)}
|
||||
/>
|
||||
{table.displayName || table.tableName}
|
||||
{table.displayName && <span className="ml-2 text-xs text-muted-foreground">({table.tableName})</span>}
|
||||
{table.display_name || table.table_name}
|
||||
{table.display_name && <span className="ml-2 text-xs text-muted-foreground">({table.table_name})</span>}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
|
||||
@@ -672,11 +672,11 @@ const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
|
||||
const fileData = response.files || (response as any).data || [];
|
||||
const chunkFiles = fileData.map((file: any) => ({
|
||||
objid: file.objid || file.id,
|
||||
savedFileName: file.saved_file_name || file.savedFileName,
|
||||
realFileName: file.real_file_name || file.realFileName || file.name,
|
||||
fileSize: file.file_size || file.fileSize || file.size,
|
||||
fileExt: file.file_ext || file.fileExt || file.extension,
|
||||
filePath: file.file_path || file.filePath || file.path,
|
||||
savedFileName: file.saved_file_name,
|
||||
realFileName: file.real_file_name || file.name,
|
||||
fileSize: file.file_size || file.size,
|
||||
fileExt: file.file_ext || file.extension,
|
||||
filePath: file.file_path || file.path,
|
||||
docType: file.doc_type,
|
||||
docTypeName: file.doc_type_name,
|
||||
targetObjid: file.target_objid,
|
||||
|
||||
+18
-18
@@ -2012,8 +2012,8 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||
// 컬럼명을 라벨로 변환하는 함수
|
||||
const getColumnLabel = useCallback(
|
||||
(columnName: string) => {
|
||||
const column = rightTableColumns.find((col) => col.columnName === columnName || col.column_name === columnName);
|
||||
return column?.columnLabel || column?.column_label || column?.displayName || columnName;
|
||||
const column = rightTableColumns.find((col) => col.column_name === columnName);
|
||||
return column?.column_label || column?.display_name || columnName;
|
||||
},
|
||||
[rightTableColumns],
|
||||
);
|
||||
@@ -2030,8 +2030,8 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||
const response = await apiClient.get(`/table-categories/${leftTableName}/${columnName}/values`);
|
||||
if (response.data.success && response.data.data && response.data.data.length > 0) {
|
||||
return response.data.data.map((item: any) => ({
|
||||
value: item.valueCode,
|
||||
label: item.valueLabel,
|
||||
value: item.value_code,
|
||||
label: item.value_label,
|
||||
}));
|
||||
}
|
||||
} catch {
|
||||
@@ -2203,8 +2203,8 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||
const columnsResponse = await tableTypeApi.getColumns(leftTableName);
|
||||
const labels: Record<string, string> = {};
|
||||
columnsResponse.forEach((col: any) => {
|
||||
const columnName = col.columnName || col.column_name;
|
||||
const label = col.columnLabel || col.column_label || col.displayName || columnName;
|
||||
const columnName = col.column_name;
|
||||
const label = col.column_label || col.display_name || columnName;
|
||||
if (columnName) {
|
||||
labels[columnName] = label;
|
||||
}
|
||||
@@ -2231,8 +2231,8 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||
// 우측 컬럼 라벨도 함께 로드
|
||||
const labels: Record<string, string> = {};
|
||||
columnsResponse.forEach((col: any) => {
|
||||
const columnName = col.columnName || col.column_name;
|
||||
const label = col.columnLabel || col.column_label || col.displayName || columnName;
|
||||
const columnName = col.column_name;
|
||||
const label = col.column_label || col.display_name || columnName;
|
||||
if (columnName) {
|
||||
labels[columnName] = label;
|
||||
}
|
||||
@@ -2251,9 +2251,9 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||
try {
|
||||
const inputTypesResponse = await tableTypeApi.getColumnInputTypes(tbl);
|
||||
inputTypesResponse.forEach((col: any) => {
|
||||
const colName = col.columnName || col.column_name;
|
||||
const colName = col.column_name;
|
||||
if (colName) {
|
||||
inputTypes[colName] = col.inputType || "text";
|
||||
inputTypes[colName] = col.input_type || "text";
|
||||
}
|
||||
});
|
||||
} catch {
|
||||
@@ -2293,10 +2293,10 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||
for (const tableName of tablesToLoad) {
|
||||
try {
|
||||
const columnsResponse = await tableTypeApi.getColumns(tableName);
|
||||
const categoryColumns = columnsResponse.filter((col: any) => col.inputType === "category");
|
||||
const categoryColumns = columnsResponse.filter((col: any) => col.input_type === "category");
|
||||
|
||||
for (const col of categoryColumns) {
|
||||
const columnName = col.columnName || col.column_name;
|
||||
const columnName = col.column_name;
|
||||
try {
|
||||
const response = await apiClient.get(
|
||||
`/table-categories/${tableName}/${columnName}/values?includeInactive=true`,
|
||||
@@ -2305,8 +2305,8 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||
if (response.data.success && response.data.data) {
|
||||
const valueMap: Record<string, { label: string; color?: string }> = {};
|
||||
response.data.data.forEach((item: any) => {
|
||||
valueMap[item.value_code || item.valueCode] = {
|
||||
label: item.value_label || item.valueLabel,
|
||||
valueMap[item.value_code] = {
|
||||
label: item.value_label,
|
||||
color: item.color,
|
||||
};
|
||||
});
|
||||
@@ -2379,11 +2379,11 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||
try {
|
||||
// 1. 컬럼 메타 정보 조회
|
||||
const columnsResponse = await tableTypeApi.getColumns(tableName);
|
||||
const categoryColumns = columnsResponse.filter((col: any) => col.inputType === "category");
|
||||
const categoryColumns = columnsResponse.filter((col: any) => col.input_type === "category");
|
||||
|
||||
// 2. 각 카테고리 컬럼에 대한 값 조회
|
||||
for (const col of categoryColumns) {
|
||||
const columnName = col.columnName || col.column_name;
|
||||
const columnName = col.column_name;
|
||||
try {
|
||||
const response = await apiClient.get(
|
||||
`/table-categories/${tableName}/${columnName}/values?includeInactive=true`,
|
||||
@@ -2392,8 +2392,8 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||
if (response.data.success && response.data.data) {
|
||||
const valueMap: Record<string, { label: string; color?: string }> = {};
|
||||
response.data.data.forEach((item: any) => {
|
||||
valueMap[item.value_code || item.valueCode] = {
|
||||
label: item.value_label || item.valueLabel,
|
||||
valueMap[item.value_code] = {
|
||||
label: item.value_label,
|
||||
color: item.color,
|
||||
};
|
||||
});
|
||||
|
||||
+61
-61
@@ -312,15 +312,15 @@ const GroupByColumnsSelector: React.FC<{
|
||||
) : (
|
||||
<div className="max-h-[200px] space-y-1 overflow-y-auto rounded-md border p-3">
|
||||
{columns.map((col) => (
|
||||
<div key={col.columnName} className="flex items-center gap-2">
|
||||
<div key={col.column_name} className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
id={`groupby-${col.columnName}`}
|
||||
checked={selectedColumns.includes(col.columnName)}
|
||||
onCheckedChange={() => toggleColumn(col.columnName)}
|
||||
id={`groupby-${col.column_name}`}
|
||||
checked={selectedColumns.includes(col.column_name)}
|
||||
onCheckedChange={() => toggleColumn(col.column_name)}
|
||||
/>
|
||||
<label htmlFor={`groupby-${col.columnName}`} className="flex-1 cursor-pointer text-xs">
|
||||
{col.columnLabel || col.columnName}
|
||||
<span className="text-muted-foreground ml-1">({col.columnName})</span>
|
||||
<label htmlFor={`groupby-${col.column_name}`} className="flex-1 cursor-pointer text-xs">
|
||||
{col.column_label || col.column_name}
|
||||
<span className="text-muted-foreground ml-1">({col.column_name})</span>
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
@@ -343,7 +343,7 @@ const ScreenSelector: React.FC<{
|
||||
onChange: (screenId?: number) => void;
|
||||
}> = ({ value, onChange }) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [screens, setScreens] = useState<Array<{ screenId: number; screenName: string; screenCode: string }>>([]);
|
||||
const [screens, setScreens] = useState<Array<{ screen_id: number; screen_name: string; screen_code: string }>>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -353,7 +353,7 @@ const ScreenSelector: React.FC<{
|
||||
const { screenApi } = await import("@/lib/api/screen");
|
||||
const response = await screenApi.getScreens({ page: 1, size: 1000 });
|
||||
setScreens(
|
||||
response.data.map((s) => ({ screenId: s.screenId, screenName: s.screenName, screenCode: s.screenCode })),
|
||||
response.data.map((s) => ({ screen_id: s.screen_id, screen_name: s.screen_name, screen_code: s.screen_code })),
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("화면 목록 로드 실패:", error);
|
||||
@@ -364,7 +364,7 @@ const ScreenSelector: React.FC<{
|
||||
loadScreens();
|
||||
}, []);
|
||||
|
||||
const selectedScreen = screens.find((s) => s.screenId === value);
|
||||
const selectedScreen = screens.find((s) => s.screen_id === value);
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
@@ -376,7 +376,7 @@ const ScreenSelector: React.FC<{
|
||||
className="h-8 w-full justify-between text-xs"
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? "로딩 중..." : selectedScreen ? selectedScreen.screenName : "화면 선택"}
|
||||
{loading ? "로딩 중..." : selectedScreen ? selectedScreen.screen_name : "화면 선택"}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
@@ -388,18 +388,18 @@ const ScreenSelector: React.FC<{
|
||||
<CommandGroup className="max-h-[300px] overflow-auto">
|
||||
{screens.map((screen) => (
|
||||
<CommandItem
|
||||
key={screen.screenId}
|
||||
value={`${screen.screenName.toLowerCase()} ${screen.screenCode.toLowerCase()} ${screen.screenId}`}
|
||||
key={screen.screen_id}
|
||||
value={`${screen.screen_name.toLowerCase()} ${screen.screen_code.toLowerCase()} ${screen.screen_id}`}
|
||||
onSelect={() => {
|
||||
onChange(screen.screenId === value ? undefined : screen.screenId);
|
||||
onChange(screen.screen_id === value ? undefined : screen.screen_id);
|
||||
setOpen(false);
|
||||
}}
|
||||
className="text-xs"
|
||||
>
|
||||
<Check className={cn("mr-2 h-4 w-4", value === screen.screenId ? "opacity-100" : "opacity-0")} />
|
||||
<Check className={cn("mr-2 h-4 w-4", value === screen.screen_id ? "opacity-100" : "opacity-0")} />
|
||||
<div className="flex flex-col">
|
||||
<span className="font-medium">{screen.screenName}</span>
|
||||
<span className="text-muted-foreground text-[10px]">{screen.screenCode}</span>
|
||||
<span className="font-medium">{screen.screen_name}</span>
|
||||
<span className="text-muted-foreground text-[10px]">{screen.screen_code}</span>
|
||||
</div>
|
||||
</CommandItem>
|
||||
))}
|
||||
@@ -546,16 +546,16 @@ const AdditionalTabConfigPanel: React.FC<AdditionalTabConfigPanelProps> = ({
|
||||
<CommandGroup className="max-h-[200px] overflow-auto">
|
||||
{availableRightTables.map((table) => (
|
||||
<CommandItem
|
||||
key={table.tableName}
|
||||
value={`${table.displayName || ""} ${table.tableName}`}
|
||||
onSelect={() => updateTab({ tableName: table.tableName, columns: [] })}
|
||||
key={table.table_name}
|
||||
value={`${table.display_name || ""} ${table.table_name}`}
|
||||
onSelect={() => updateTab({ tableName: table.table_name, columns: [] })}
|
||||
>
|
||||
<Check
|
||||
className={cn("mr-2 h-4 w-4", tab.tableName === table.tableName ? "opacity-100" : "opacity-0")}
|
||||
className={cn("mr-2 h-4 w-4", tab.tableName === table.table_name ? "opacity-100" : "opacity-0")}
|
||||
/>
|
||||
{table.displayName || table.tableName}
|
||||
{table.displayName && (
|
||||
<span className="text-muted-foreground ml-2 text-xs">({table.tableName})</span>
|
||||
{table.display_name || table.table_name}
|
||||
{table.display_name && (
|
||||
<span className="text-muted-foreground ml-2 text-xs">({table.table_name})</span>
|
||||
)}
|
||||
</CommandItem>
|
||||
))}
|
||||
@@ -1645,23 +1645,23 @@ export const SplitPanelLayoutConfigPanel: React.FC<SplitPanelLayoutConfigPanelPr
|
||||
console.log(`📊 테이블 ${tableName} 컬럼 응답:`, columnsResponse);
|
||||
|
||||
const columns: ColumnInfo[] = (columnsResponse || []).map((col: any) => ({
|
||||
tableName: col.tableName || tableName,
|
||||
columnName: col.columnName || col.column_name,
|
||||
columnLabel: col.displayName || col.columnLabel || col.column_label || col.columnName || col.column_name,
|
||||
dataType: col.dataType || col.data_type || col.dbType,
|
||||
webType: col.webType || col.web_type,
|
||||
input_type: col.inputType || col.input_type,
|
||||
widgetType: col.widgetType || col.widget_type || col.webType || col.web_type,
|
||||
isNullable: col.isNullable || col.is_nullable,
|
||||
required: col.required !== undefined ? col.required : col.isNullable === "NO" || col.is_nullable === "NO",
|
||||
columnDefault: col.columnDefault || col.column_default,
|
||||
characterMaximumLength: col.characterMaximumLength || col.character_maximum_length,
|
||||
tableName: col.table_name || tableName,
|
||||
columnName: col.column_name,
|
||||
columnLabel: col.display_name || col.column_label || col.column_name,
|
||||
dataType: col.data_type,
|
||||
webType: col.web_type,
|
||||
input_type: col.input_type,
|
||||
widgetType: col.widget_type || col.web_type,
|
||||
isNullable: col.is_nullable,
|
||||
required: col.required !== undefined ? col.required : col.is_nullable === "NO",
|
||||
columnDefault: col.column_default,
|
||||
characterMaximumLength: col.character_maximum_length,
|
||||
isPrimaryKey: col.isPrimaryKey || false, // PK 여부 추가
|
||||
codeCategory: col.codeCategory || col.code_category,
|
||||
codeValue: col.codeValue || col.code_value,
|
||||
referenceTable: col.referenceTable || col.reference_table, // 🆕 참조 테이블
|
||||
referenceColumn: col.referenceColumn || col.reference_column, // 🆕 참조 컬럼
|
||||
displayColumn: col.displayColumn || col.display_column, // 🆕 표시 컬럼
|
||||
codeCategory: col.code_category,
|
||||
codeValue: col.code_value,
|
||||
referenceTable: col.reference_table, // 🆕 참조 테이블
|
||||
referenceColumn: col.reference_column, // 🆕 참조 컬럼
|
||||
displayColumn: col.display_column, // 🆕 표시 컬럼
|
||||
}));
|
||||
|
||||
console.log(`✅ 테이블 ${tableName} 컬럼 ${columns.length}개 로드됨:`, columns);
|
||||
@@ -1733,11 +1733,11 @@ export const SplitPanelLayoutConfigPanel: React.FC<SplitPanelLayoutConfigPanelPr
|
||||
try {
|
||||
const refColumnsResponse = await tableTypeApi.getColumns(refTableName);
|
||||
const refColumns: ColumnInfo[] = (refColumnsResponse || []).map((col: any) => ({
|
||||
tableName: col.tableName || refTableName,
|
||||
columnName: col.columnName || col.column_name,
|
||||
columnLabel: col.displayName || col.columnLabel || col.column_label || col.columnName || col.column_name,
|
||||
dataType: col.dataType || col.data_type || col.dbType,
|
||||
input_type: col.inputType || col.input_type,
|
||||
tableName: col.table_name || refTableName,
|
||||
columnName: col.column_name,
|
||||
columnLabel: col.display_name || col.column_label || col.column_name,
|
||||
dataType: col.data_type,
|
||||
input_type: col.input_type,
|
||||
}));
|
||||
|
||||
referenceTableData.push({ tableName: refTableName, columns: refColumns });
|
||||
@@ -2061,10 +2061,10 @@ export const SplitPanelLayoutConfigPanel: React.FC<SplitPanelLayoutConfigPanelPr
|
||||
<Database className="text-muted-foreground h-3.5 w-3.5 shrink-0" />
|
||||
<span className="truncate">
|
||||
{config.leftPanel?.tableName
|
||||
? allTables.find((t) => (t.tableName || t.table_name) === config.leftPanel?.tableName)
|
||||
?.tableLabel ||
|
||||
allTables.find((t) => (t.tableName || t.table_name) === config.leftPanel?.tableName)
|
||||
?.displayName ||
|
||||
? allTables.find((t) => t.table_name === config.leftPanel?.tableName)
|
||||
?.table_label ||
|
||||
allTables.find((t) => t.table_name === config.leftPanel?.tableName)
|
||||
?.display_name ||
|
||||
config.leftPanel?.tableName
|
||||
: "테이블을 선택하세요"}
|
||||
</span>
|
||||
@@ -2098,8 +2098,8 @@ export const SplitPanelLayoutConfigPanel: React.FC<SplitPanelLayoutConfigPanelPr
|
||||
)}
|
||||
/>
|
||||
<Database className="text-primary mr-2 h-3.5 w-3.5" />
|
||||
{allTables.find((t) => (t.tableName || t.table_name) === screenTableName)?.tableLabel ||
|
||||
allTables.find((t) => (t.tableName || t.table_name) === screenTableName)?.displayName ||
|
||||
{allTables.find((t) => t.table_name === screenTableName)?.table_label ||
|
||||
allTables.find((t) => t.table_name === screenTableName)?.display_name ||
|
||||
screenTableName}
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
@@ -2108,10 +2108,10 @@ export const SplitPanelLayoutConfigPanel: React.FC<SplitPanelLayoutConfigPanelPr
|
||||
{/* 전체 테이블 */}
|
||||
<CommandGroup heading="전체 테이블">
|
||||
{allTables
|
||||
.filter((t) => (t.tableName || t.table_name) !== screenTableName)
|
||||
.filter((t) => t.table_name !== screenTableName)
|
||||
.map((table) => {
|
||||
const tableName = table.tableName || table.table_name;
|
||||
const displayName = table.tableLabel || table.displayName || tableName;
|
||||
const tableName = table.table_name;
|
||||
const displayName = table.table_label || table.display_name || tableName;
|
||||
return (
|
||||
<CommandItem
|
||||
key={tableName}
|
||||
@@ -2784,22 +2784,22 @@ export const SplitPanelLayoutConfigPanel: React.FC<SplitPanelLayoutConfigPanelPr
|
||||
<CommandGroup className="max-h-[200px] overflow-auto">
|
||||
{availableRightTables.map((table) => (
|
||||
<CommandItem
|
||||
key={table.tableName}
|
||||
value={`${table.displayName || ""} ${table.tableName}`}
|
||||
key={table.table_name}
|
||||
value={`${table.display_name || ""} ${table.table_name}`}
|
||||
onSelect={() => {
|
||||
updateRightPanel({ tableName: table.tableName });
|
||||
updateRightPanel({ tableName: table.table_name });
|
||||
setRightTableOpen(false);
|
||||
}}
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
config.rightPanel?.tableName === table.tableName ? "opacity-100" : "opacity-0",
|
||||
config.rightPanel?.tableName === table.table_name ? "opacity-100" : "opacity-0",
|
||||
)}
|
||||
/>
|
||||
{table.displayName || table.tableName}
|
||||
{table.displayName && (
|
||||
<span className="text-muted-foreground ml-2 text-xs">({table.tableName})</span>
|
||||
{table.display_name || table.table_name}
|
||||
{table.display_name && (
|
||||
<span className="text-muted-foreground ml-2 text-xs">({table.table_name})</span>
|
||||
)}
|
||||
</CommandItem>
|
||||
))}
|
||||
|
||||
+4
-4
@@ -75,8 +75,8 @@ export const LeftPanelConfigTab: React.FC<LeftPanelConfigTabProps> = ({
|
||||
<Database className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
||||
<span className="truncate">
|
||||
{config.leftPanel?.tableName
|
||||
? allTables.find((t) => (t.tableName || t.table_name) === config.leftPanel?.tableName)?.tableLabel ||
|
||||
allTables.find((t) => (t.tableName || t.table_name) === config.leftPanel?.tableName)?.displayName ||
|
||||
? allTables.find((t) => t.table_name === config.leftPanel?.tableName)?.table_label ||
|
||||
allTables.find((t) => t.table_name === config.leftPanel?.tableName)?.display_name ||
|
||||
config.leftPanel?.tableName
|
||||
: "테이블을 선택하세요"}
|
||||
</span>
|
||||
@@ -108,8 +108,8 @@ export const LeftPanelConfigTab: React.FC<LeftPanelConfigTabProps> = ({
|
||||
)}
|
||||
/>
|
||||
<Database className="mr-2 h-3.5 w-3.5 text-primary" />
|
||||
{allTables.find((t) => (t.tableName || t.table_name) === screenTableName)?.tableLabel ||
|
||||
allTables.find((t) => (t.tableName || t.table_name) === screenTableName)?.displayName ||
|
||||
{allTables.find((t) => t.table_name === screenTableName)?.table_label ||
|
||||
allTables.find((t) => t.table_name === screenTableName)?.display_name ||
|
||||
screenTableName}
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
|
||||
+4
-4
@@ -99,11 +99,11 @@ export const RightPanelConfigTab: React.FC<RightPanelConfigTabProps> = ({
|
||||
<CommandEmpty>테이블을 찾을 수 없습니다.</CommandEmpty>
|
||||
<CommandGroup className="max-h-[200px] overflow-auto">
|
||||
{availableRightTables.map((table) => {
|
||||
const tableName = (table as any).tableName || (table as any).table_name;
|
||||
const tableName = (table as any).table_name;
|
||||
return (
|
||||
<CommandItem
|
||||
key={tableName}
|
||||
value={`${(table as any).displayName || ""} ${tableName}`}
|
||||
value={`${(table as any).display_name || ""} ${tableName}`}
|
||||
onSelect={() => {
|
||||
updateRightPanel({ tableName });
|
||||
setRightTableOpen(false);
|
||||
@@ -115,8 +115,8 @@ export const RightPanelConfigTab: React.FC<RightPanelConfigTabProps> = ({
|
||||
config.rightPanel?.tableName === tableName ? "opacity-100" : "opacity-0",
|
||||
)}
|
||||
/>
|
||||
{(table as any).displayName || tableName}
|
||||
{(table as any).displayName && <span className="ml-2 text-xs text-muted-foreground">({tableName})</span>}
|
||||
{(table as any).display_name || tableName}
|
||||
{(table as any).display_name && <span className="ml-2 text-xs text-muted-foreground">({tableName})</span>}
|
||||
</CommandItem>
|
||||
);
|
||||
})}
|
||||
|
||||
+10
-10
@@ -201,7 +201,7 @@ export const ScreenSelector: React.FC<{
|
||||
onChange: (screenId?: number) => void;
|
||||
}> = ({ value, onChange }) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [screens, setScreens] = useState<Array<{ screenId: number; screenName: string; screenCode: string }>>([]);
|
||||
const [screens, setScreens] = useState<Array<{ screen_id: number; screen_name: string; screen_code: string }>>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -211,7 +211,7 @@ export const ScreenSelector: React.FC<{
|
||||
const { screenApi } = await import("@/lib/api/screen");
|
||||
const response = await screenApi.getScreens({ page: 1, size: 1000 });
|
||||
setScreens(
|
||||
response.data.map((s) => ({ screenId: s.screen_id, screenName: s.screen_name, screenCode: s.screen_code })),
|
||||
response.data.map((s) => ({ screen_id: s.screen_id, screen_name: s.screen_name, screen_code: s.screen_code })),
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("화면 목록 로드 실패:", error);
|
||||
@@ -222,7 +222,7 @@ export const ScreenSelector: React.FC<{
|
||||
loadScreens();
|
||||
}, []);
|
||||
|
||||
const selectedScreen = screens.find((s) => s.screenId === value);
|
||||
const selectedScreen = screens.find((s) => s.screen_id === value);
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
@@ -234,7 +234,7 @@ export const ScreenSelector: React.FC<{
|
||||
className="h-8 w-full justify-between text-xs"
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? "로딩 중..." : selectedScreen ? selectedScreen.screenName : "화면 선택"}
|
||||
{loading ? "로딩 중..." : selectedScreen ? selectedScreen.screen_name : "화면 선택"}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
@@ -246,18 +246,18 @@ export const ScreenSelector: React.FC<{
|
||||
<CommandGroup className="max-h-[300px] overflow-auto">
|
||||
{screens.map((screen) => (
|
||||
<CommandItem
|
||||
key={screen.screenId}
|
||||
value={`${screen.screenName.toLowerCase()} ${screen.screenCode.toLowerCase()} ${screen.screenId}`}
|
||||
key={screen.screen_id}
|
||||
value={`${screen.screen_name.toLowerCase()} ${screen.screen_code.toLowerCase()} ${screen.screen_id}`}
|
||||
onSelect={() => {
|
||||
onChange(screen.screenId === value ? undefined : screen.screenId);
|
||||
onChange(screen.screen_id === value ? undefined : screen.screen_id);
|
||||
setOpen(false);
|
||||
}}
|
||||
className="text-xs"
|
||||
>
|
||||
<Check className={cn("mr-2 h-4 w-4", value === screen.screenId ? "opacity-100" : "opacity-0")} />
|
||||
<Check className={cn("mr-2 h-4 w-4", value === screen.screen_id ? "opacity-100" : "opacity-0")} />
|
||||
<div className="flex flex-col">
|
||||
<span className="font-medium">{screen.screenName}</span>
|
||||
<span className="text-muted-foreground text-[10px]">{screen.screenCode}</span>
|
||||
<span className="font-medium">{screen.screen_name}</span>
|
||||
<span className="text-muted-foreground text-[10px]">{screen.screen_code}</span>
|
||||
</div>
|
||||
</CommandItem>
|
||||
))}
|
||||
|
||||
@@ -1515,7 +1515,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
try {
|
||||
// 🔥 FIX: 캐시 키에 회사 코드 포함 (멀티테넌시 지원)
|
||||
const currentUser = JSON.parse(localStorage.getItem("currentUser") || "{}");
|
||||
const companyCode = currentUser.companyCode || "UNKNOWN";
|
||||
const companyCode = currentUser.company_code || "UNKNOWN";
|
||||
const cacheKey = `columns_${tableConfig.selectedTable}_${companyCode}`;
|
||||
const cached = tableColumnCache.get(cacheKey);
|
||||
if (cached && Date.now() - cached.timestamp < TABLE_CACHE_TTL) {
|
||||
|
||||
@@ -853,13 +853,13 @@ export function PopButtonComponent({ config, label, isDesignMode, screenId, comp
|
||||
action: "inbound-confirm",
|
||||
data: {
|
||||
items: selectedItems,
|
||||
fieldValues,
|
||||
field_values: fieldValues,
|
||||
},
|
||||
mappings: {
|
||||
cardList: cardListMapping,
|
||||
card_list: cardListMapping,
|
||||
field: fieldMapping,
|
||||
},
|
||||
statusChanges: statusChangeRules,
|
||||
status_changes: statusChangeRules,
|
||||
});
|
||||
|
||||
if (result.data?.success) {
|
||||
|
||||
@@ -518,7 +518,7 @@ export function PopCardListV2Component({
|
||||
|
||||
const result = await apiClient.post("/pop/execute-action", {
|
||||
tasks,
|
||||
data: { items: [{ ...row, id: targetId }], fieldValues: {} },
|
||||
data: { items: [{ ...row, id: targetId }], field_values: {} },
|
||||
mappings: {},
|
||||
});
|
||||
if (result.data?.success) successCount++;
|
||||
@@ -1284,7 +1284,7 @@ function CardV2({
|
||||
try {
|
||||
const result = await apiClient.post("/pop/execute-action", {
|
||||
tasks,
|
||||
data: { items: [targetRow], fieldValues: {} },
|
||||
data: { items: [targetRow], field_values: {} },
|
||||
mappings: {},
|
||||
});
|
||||
if (result.data?.success) {
|
||||
@@ -1546,7 +1546,7 @@ function CardV2({
|
||||
: processId ? { ...actionRow, id: processId } : actionRow;
|
||||
const result = await apiClient.post("/pop/execute-action", {
|
||||
tasks,
|
||||
data: { items: [targetRow], fieldValues: {} },
|
||||
data: { items: [targetRow], field_values: {} },
|
||||
mappings: {},
|
||||
});
|
||||
if (result.data?.success) {
|
||||
|
||||
@@ -331,7 +331,7 @@ function TabData({
|
||||
const ds = cfg.dataSource;
|
||||
|
||||
const selectedDisplay = ds.tableName
|
||||
? tables.find((t) => t.tableName === ds.tableName)?.displayName || ds.tableName
|
||||
? tables.find((t) => t.table_name === ds.tableName)?.display_name || ds.tableName
|
||||
: "";
|
||||
|
||||
const toggleColumn = (colName: string) => {
|
||||
@@ -383,16 +383,16 @@ function TabData({
|
||||
</CommandItem>
|
||||
{tables.map((t) => (
|
||||
<CommandItem
|
||||
key={t.tableName}
|
||||
value={`${t.tableName} ${t.displayName || ""}`}
|
||||
onSelect={() => { onTableChange(t.tableName); setTableOpen(false); }}
|
||||
key={t.table_name}
|
||||
value={`${t.table_name} ${t.display_name || ""}`}
|
||||
onSelect={() => { onTableChange(t.table_name); setTableOpen(false); }}
|
||||
className="text-xs"
|
||||
>
|
||||
<Check className={cn("mr-2 h-3 w-3", ds.tableName === t.tableName ? "opacity-100" : "opacity-0")} />
|
||||
<Check className={cn("mr-2 h-3 w-3", ds.tableName === t.table_name ? "opacity-100" : "opacity-0")} />
|
||||
<div className="flex flex-col">
|
||||
<span>{t.displayName || t.tableName}</span>
|
||||
{t.displayName && t.displayName !== t.tableName && (
|
||||
<span className="text-[10px] text-muted-foreground">{t.tableName}</span>
|
||||
<span>{t.display_name || t.table_name}</span>
|
||||
{t.display_name && t.display_name !== t.table_name && (
|
||||
<span className="text-[10px] text-muted-foreground">{t.table_name}</span>
|
||||
)}
|
||||
</div>
|
||||
</CommandItem>
|
||||
@@ -596,7 +596,7 @@ function JoinItemV2({
|
||||
targetColumns.some((tc) => tc.name === mc.name && tc.type === mc.type)
|
||||
);
|
||||
|
||||
const selectableTables = tables.filter((t) => t.tableName !== mainTableName);
|
||||
const selectableTables = tables.filter((t) => t.table_name !== mainTableName);
|
||||
const hasJoinCondition = join.sourceColumn !== "" && join.targetColumn !== "";
|
||||
const selectedTargetCols = join.selectedTargetColumns || [];
|
||||
const pickableTargetCols = targetColumns.filter((tc) => tc.name !== join.targetColumn);
|
||||
@@ -633,16 +633,16 @@ function JoinItemV2({
|
||||
<CommandGroup>
|
||||
{selectableTables.map((t) => (
|
||||
<CommandItem
|
||||
key={t.tableName}
|
||||
value={`${t.tableName} ${t.displayName || ""}`}
|
||||
key={t.table_name}
|
||||
value={`${t.table_name} ${t.display_name || ""}`}
|
||||
onSelect={() => {
|
||||
onUpdate({ targetTable: t.tableName, sourceColumn: "", targetColumn: "", selectedTargetColumns: [] });
|
||||
onUpdate({ targetTable: t.table_name, sourceColumn: "", targetColumn: "", selectedTargetColumns: [] });
|
||||
setTableOpen(false);
|
||||
}}
|
||||
className="text-[10px]"
|
||||
>
|
||||
<Check className={cn("mr-1 h-3 w-3", join.targetTable === t.tableName ? "opacity-100" : "opacity-0")} />
|
||||
{t.tableName}
|
||||
<Check className={cn("mr-1 h-3 w-3", join.targetTable === t.table_name ? "opacity-100" : "opacity-0")} />
|
||||
{t.table_name}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
@@ -1635,16 +1635,16 @@ function TimelineConfigEditor({
|
||||
<CommandGroup>
|
||||
{tables.map((t) => (
|
||||
<CommandItem
|
||||
key={t.tableName}
|
||||
value={`${t.tableName} ${t.displayName || ""}`}
|
||||
key={t.table_name}
|
||||
value={`${t.table_name} ${t.display_name || ""}`}
|
||||
onSelect={() => {
|
||||
updateSource({ processTable: t.tableName, foreignKey: "", seqColumn: "", nameColumn: "", statusColumn: "" });
|
||||
updateSource({ processTable: t.table_name, foreignKey: "", seqColumn: "", nameColumn: "", statusColumn: "" });
|
||||
setTableOpen(false);
|
||||
}}
|
||||
className="text-[10px]"
|
||||
>
|
||||
<Check className={cn("mr-1 h-3 w-3", src.processTable === t.tableName ? "opacity-100" : "opacity-0")} />
|
||||
{t.displayName || t.tableName}
|
||||
<Check className={cn("mr-1 h-3 w-3", src.processTable === t.table_name ? "opacity-100" : "opacity-0")} />
|
||||
{t.display_name || t.table_name}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
@@ -2726,15 +2726,15 @@ function DbTableCombobox({
|
||||
const q = search.toLowerCase();
|
||||
return tables.filter(
|
||||
(t) =>
|
||||
t.tableName.toLowerCase().includes(q) ||
|
||||
(t.tableComment || "").toLowerCase().includes(q),
|
||||
t.table_name.toLowerCase().includes(q) ||
|
||||
(t.description || "").toLowerCase().includes(q),
|
||||
);
|
||||
}, [tables, search]);
|
||||
|
||||
const selectedLabel = useMemo(() => {
|
||||
if (!value) return "DB 테이블 검색...";
|
||||
const found = tables.find((t) => t.tableName === value);
|
||||
return found ? `${found.tableName}${found.tableComment ? ` (${found.tableComment})` : ""}` : value;
|
||||
const found = tables.find((t) => t.table_name === value);
|
||||
return found ? `${found.table_name}${found.description ? ` (${found.description})` : ""}` : value;
|
||||
}, [value, tables]);
|
||||
|
||||
return (
|
||||
@@ -2769,19 +2769,19 @@ function DbTableCombobox({
|
||||
<CommandGroup>
|
||||
{filtered.map((t) => (
|
||||
<CommandItem
|
||||
key={t.tableName}
|
||||
value={t.tableName}
|
||||
key={t.table_name}
|
||||
value={t.table_name}
|
||||
onSelect={() => {
|
||||
onSelect(t.tableName);
|
||||
onSelect(t.table_name);
|
||||
setOpen(false);
|
||||
setSearch("");
|
||||
}}
|
||||
className="text-[10px]"
|
||||
>
|
||||
<Check className={cn("mr-1.5 h-3 w-3", value === t.tableName ? "opacity-100" : "opacity-0")} />
|
||||
<span className="font-medium">{t.tableName}</span>
|
||||
{t.tableComment && (
|
||||
<span className="ml-1 text-muted-foreground">({t.tableComment})</span>
|
||||
<Check className={cn("mr-1.5 h-3 w-3", value === t.table_name ? "opacity-100" : "opacity-0")} />
|
||||
<span className="font-medium">{t.table_name}</span>
|
||||
{t.description && (
|
||||
<span className="ml-1 text-muted-foreground">({t.description})</span>
|
||||
)}
|
||||
</CommandItem>
|
||||
))}
|
||||
|
||||
@@ -385,8 +385,8 @@ export class EnhancedFormService {
|
||||
): Promise<{ success: boolean; message?: string; data?: any }> {
|
||||
try {
|
||||
const result = await dynamicFormApi.saveData({
|
||||
screenId,
|
||||
tableName,
|
||||
screen_id: screenId,
|
||||
table_name: tableName,
|
||||
data: formData,
|
||||
});
|
||||
|
||||
|
||||
@@ -667,13 +667,8 @@ export class ButtonActionExecutor {
|
||||
firstItem.location_name &&
|
||||
firstItem.row_num !== undefined &&
|
||||
firstItem.level_num !== undefined;
|
||||
const isOldFormat =
|
||||
firstItem.locationCode &&
|
||||
firstItem.locationName &&
|
||||
firstItem.rowNum !== undefined &&
|
||||
firstItem.levelNum !== undefined;
|
||||
|
||||
if (isNewFormat || isOldFormat) {
|
||||
if (isNewFormat) {
|
||||
rackStructureLocations = value;
|
||||
rackStructureFieldKey = key;
|
||||
break;
|
||||
@@ -937,8 +932,8 @@ export class ButtonActionExecutor {
|
||||
delete dataWithMeta.id;
|
||||
|
||||
const insertResult = await DynamicFormApi.saveFormData({
|
||||
screenId: context.screenId || 0,
|
||||
tableName: repeaterTargetTable,
|
||||
screen_id: context.screenId || 0,
|
||||
table_name: repeaterTargetTable,
|
||||
data: dataWithMeta as Record<string, any>,
|
||||
});
|
||||
} else if (item.id && _existingRecord === true) {
|
||||
@@ -1075,7 +1070,7 @@ export class ButtonActionExecutor {
|
||||
// 전체 업데이트 (originalData 없이 id로 UPDATE 판단된 경우)
|
||||
|
||||
saveResult = await DynamicFormApi.updateFormData(primaryKeyValue, {
|
||||
tableName,
|
||||
table_name: tableName,
|
||||
data: formData,
|
||||
});
|
||||
}
|
||||
@@ -1533,8 +1528,8 @@ export class ButtonActionExecutor {
|
||||
saveResult = { success: true, message: "RepeaterFieldGroup/RepeatScreenModal/V2Repeater에서 처리" };
|
||||
} else {
|
||||
saveResult = await DynamicFormApi.saveFormData({
|
||||
screenId,
|
||||
tableName,
|
||||
screen_id: screenId,
|
||||
table_name: tableName,
|
||||
data: dataWithUserInfo,
|
||||
});
|
||||
}
|
||||
@@ -2095,7 +2090,7 @@ export class ButtonActionExecutor {
|
||||
|
||||
// 저장 전 중복 체크
|
||||
const firstLocation = locations[0];
|
||||
const warehouseCode = firstLocation.warehouse_code || firstLocation.warehouse_id || firstLocation.warehouseCode;
|
||||
const warehouseCode = firstLocation.warehouse_code || firstLocation.warehouse_id;
|
||||
const floor = firstLocation.floor;
|
||||
const zone = firstLocation.zone;
|
||||
|
||||
@@ -2124,14 +2119,14 @@ export class ButtonActionExecutor {
|
||||
const existingSet = new Set(existingData.map((loc: any) => `${loc.row_num}-${loc.level_num}`));
|
||||
|
||||
const duplicates = locations.filter((loc) => {
|
||||
const key = `${loc.row_num || loc.rowNum}-${loc.level_num || loc.levelNum}`;
|
||||
const key = `${loc.row_num}-${loc.level_num}`;
|
||||
return existingSet.has(key);
|
||||
});
|
||||
|
||||
if (duplicates.length > 0) {
|
||||
const duplicateInfo = duplicates
|
||||
.slice(0, 5)
|
||||
.map((d) => `${d.row_num || d.rowNum}열 ${d.level_num || d.levelNum}단`)
|
||||
.map((d) => `${d.row_num}열 ${d.level_num}단`)
|
||||
.join(", ");
|
||||
|
||||
const moreCount = duplicates.length > 5 ? ` 외 ${duplicates.length - 5}개` : "";
|
||||
@@ -2153,17 +2148,17 @@ export class ButtonActionExecutor {
|
||||
// 새로운 형식(스네이크 케이스)과 기존 형식(카멜 케이스) 모두 지원
|
||||
const record: Record<string, any> = {
|
||||
// 렉 구조에서 생성된 필드 (이미 테이블 컬럼명과 동일)
|
||||
location_code: loc.location_code || loc.locationCode,
|
||||
location_name: loc.location_name || loc.locationName,
|
||||
row_num: loc.row_num || String(loc.rowNum),
|
||||
level_num: loc.level_num || String(loc.levelNum),
|
||||
location_code: loc.location_code,
|
||||
location_name: loc.location_name,
|
||||
row_num: loc.row_num,
|
||||
level_num: loc.level_num,
|
||||
// 창고 정보 (렉 구조 컴포넌트에서 전달) - DB 컬럼명은 warehouse_code
|
||||
warehouse_code: loc.warehouse_code || loc.warehouse_id || loc.warehouseCode,
|
||||
warehouse_name: loc.warehouse_name || loc.warehouseName,
|
||||
warehouse_code: loc.warehouse_code || loc.warehouse_id,
|
||||
warehouse_name: loc.warehouse_name,
|
||||
// 위치 정보 (렉 구조 컴포넌트에서 전달)
|
||||
floor: loc.floor,
|
||||
zone: loc.zone,
|
||||
location_type: loc.location_type || loc.locationType,
|
||||
location_type: loc.location_type,
|
||||
status: loc.status || "사용",
|
||||
// 사용자 정보 추가
|
||||
writer: userId,
|
||||
@@ -2183,8 +2178,8 @@ export class ButtonActionExecutor {
|
||||
const record = recordsToInsert[i];
|
||||
try {
|
||||
const result = await DynamicFormApi.saveFormData({
|
||||
screenId,
|
||||
tableName,
|
||||
screen_id: screenId,
|
||||
table_name: tableName,
|
||||
data: record,
|
||||
});
|
||||
|
||||
@@ -2474,15 +2469,15 @@ export class ButtonActionExecutor {
|
||||
|
||||
if (isMainUpdate) {
|
||||
mainSaveResult = await DynamicFormApi.updateFormData(existingMainId, {
|
||||
tableName: tableName!,
|
||||
table_name: tableName!,
|
||||
data: mainRowToSave,
|
||||
});
|
||||
mainRecordId = existingMainId;
|
||||
} else {
|
||||
console.log("➕ [handleUniversalFormModalTableSectionSave] 메인 테이블 INSERT 실행");
|
||||
mainSaveResult = await DynamicFormApi.saveFormData({
|
||||
screenId: screenId!,
|
||||
tableName: tableName!,
|
||||
screen_id: screenId!,
|
||||
table_name: tableName!,
|
||||
data: mainRowToSave,
|
||||
});
|
||||
mainRecordId = mainSaveResult.data?.id || null;
|
||||
@@ -2546,8 +2541,8 @@ export class ButtonActionExecutor {
|
||||
console.log("➕ [INSERT] 신규 품목:", { tableName: saveTableName, data: rowToSave });
|
||||
|
||||
const saveResult = await DynamicFormApi.saveFormData({
|
||||
screenId: screenId!,
|
||||
tableName: saveTableName,
|
||||
screen_id: screenId!,
|
||||
table_name: saveTableName,
|
||||
data: rowToSave,
|
||||
});
|
||||
|
||||
@@ -2583,7 +2578,7 @@ export class ButtonActionExecutor {
|
||||
});
|
||||
|
||||
const updateResult = await DynamicFormApi.updateFormData(item.id, {
|
||||
tableName: saveTableName,
|
||||
table_name: saveTableName,
|
||||
data: rowToUpdate,
|
||||
});
|
||||
|
||||
@@ -2937,8 +2932,8 @@ export class ButtonActionExecutor {
|
||||
// INSERT 실행
|
||||
const { DynamicFormApi } = await import("@/lib/api/dynamicForm");
|
||||
const saveResult = await DynamicFormApi.saveFormData({
|
||||
screenId,
|
||||
tableName,
|
||||
screen_id: screenId,
|
||||
table_name: tableName,
|
||||
data: dataWithUserInfo,
|
||||
});
|
||||
|
||||
@@ -4725,8 +4720,8 @@ export class ButtonActionExecutor {
|
||||
}
|
||||
|
||||
const result = await DynamicFormApi.saveFormData({
|
||||
screenId: 0, // 임시값
|
||||
tableName: context.tableName,
|
||||
screen_id: 0, // 임시값
|
||||
table_name: context.tableName,
|
||||
data: saveData,
|
||||
});
|
||||
|
||||
@@ -4817,7 +4812,7 @@ export class ButtonActionExecutor {
|
||||
}
|
||||
|
||||
const result = await DynamicFormApi.updateFormData(updateId, {
|
||||
tableName: context.tableName,
|
||||
table_name: context.tableName,
|
||||
data: updateData,
|
||||
});
|
||||
|
||||
@@ -4955,8 +4950,8 @@ export class ButtonActionExecutor {
|
||||
const targetTable = action.fieldMappings?.[0]?.targetTable || action.targetTable || "test_project_info";
|
||||
|
||||
const formDataPayload = {
|
||||
screenId: 0, // 제어 관리에서는 screenId가 없으므로 0 사용
|
||||
tableName: targetTable, // 필드 매핑에서 대상 테이블명 가져오기
|
||||
screen_id: 0, // 제어 관리에서는 screen_id가 없으므로 0 사용
|
||||
table_name: targetTable, // 필드 매핑에서 대상 테이블명 가져오기
|
||||
data: insertData,
|
||||
};
|
||||
|
||||
@@ -5445,8 +5440,8 @@ export class ButtonActionExecutor {
|
||||
// valueCode → valueLabel 매핑
|
||||
categoryMap[columnName] = {};
|
||||
valuesResponse.data.forEach((catValue: any) => {
|
||||
const code = catValue.valueCode || catValue.category_value_id;
|
||||
const label = catValue.valueLabel || catValue.label || code;
|
||||
const code = catValue.value_code || catValue.category_value_id;
|
||||
const label = catValue.value_label || catValue.label || code;
|
||||
if (code) {
|
||||
categoryMap[columnName][code] = label;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user