jskim-node #410
|
|
@ -501,7 +501,7 @@ export function TabBar() {
|
||||||
touchAction: "none",
|
touchAction: "none",
|
||||||
...animStyle,
|
...animStyle,
|
||||||
...(hiddenByGhost ? { opacity: 0 } : {}),
|
...(hiddenByGhost ? { opacity: 0 } : {}),
|
||||||
...(isActive ? { boxShadow: "0 -1px 28px rgba(0,0,0,0.9)" } : {}),
|
...(isActive ? {} : {}),
|
||||||
}}
|
}}
|
||||||
title={tab.title}
|
title={tab.title}
|
||||||
>
|
>
|
||||||
|
|
@ -542,13 +542,13 @@ export function TabBar() {
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
className="border-border bg-muted/30 relative flex h-[33px] shrink-0 items-end gap-[2px] overflow-hidden px-1.5"
|
className="border-border bg-background relative flex h-[33px] shrink-0 items-end gap-[2px] overflow-hidden px-1.5"
|
||||||
onDragOver={handleBarDragOver}
|
onDragOver={handleBarDragOver}
|
||||||
onDragLeave={handleBarDragLeave}
|
onDragLeave={handleBarDragLeave}
|
||||||
onDrop={handleBarDrop}
|
onDrop={handleBarDrop}
|
||||||
>
|
>
|
||||||
<div className="border-border pointer-events-none absolute inset-x-0 bottom-0 z-0 border-b" />
|
<div className="border-border pointer-events-none absolute inset-x-0 bottom-0 z-0 border-b" />
|
||||||
<div className="pointer-events-none absolute inset-0 z-5 bg-black/15" />
|
<div className="pointer-events-none absolute inset-0 z-5" />
|
||||||
{displayVisible.map((tab, i) => renderTab(tab, i))}
|
{displayVisible.map((tab, i) => renderTab(tab, i))}
|
||||||
|
|
||||||
{hasOverflow && (
|
{hasOverflow && (
|
||||||
|
|
@ -587,7 +587,7 @@ export function TabBar() {
|
||||||
{ghostStyle && draggedTab && (
|
{ghostStyle && draggedTab && (
|
||||||
<div
|
<div
|
||||||
style={ghostStyle}
|
style={ghostStyle}
|
||||||
className="border-primary/50 bg-background rounded-t-md border border-b-0 px-3 shadow-lg"
|
className="border-primary/50 bg-background rounded-t-md border border-b-0 px-3"
|
||||||
>
|
>
|
||||||
<div className="flex h-full items-center">
|
<div className="flex h-full items-center">
|
||||||
<span className="truncate text-[11px] font-medium">{draggedTab.title}</span>
|
<span className="truncate text-[11px] font-medium">{draggedTab.title}</span>
|
||||||
|
|
|
||||||
|
|
@ -335,92 +335,6 @@ export const EditModal: React.FC<EditModalProps> = ({ className }) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* 디테일 행의 FK를 통해 마스터 테이블 데이터를 자동 조회
|
|
||||||
* - entity join 설정에서 FK 관계 탐지
|
|
||||||
* - FK 값으로 마스터 테이블 전체 row 조회
|
|
||||||
* - editData에 없는 필드만 병합 (디테일 데이터를 덮어쓰지 않음)
|
|
||||||
*/
|
|
||||||
const loadMasterDataForDetailRow = async (
|
|
||||||
editData: Record<string, any>,
|
|
||||||
targetScreenId: number,
|
|
||||||
eventTableName?: string,
|
|
||||||
): Promise<Record<string, any>> => {
|
|
||||||
try {
|
|
||||||
let detailTableName = eventTableName;
|
|
||||||
if (!detailTableName) {
|
|
||||||
const screenInfo = await screenApi.getScreen(targetScreenId);
|
|
||||||
detailTableName = screenInfo?.tableName;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!detailTableName) {
|
|
||||||
console.log("[EditModal:MasterLoad] 테이블명을 알 수 없음 - 스킵");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("[EditModal:MasterLoad] 시작:", { detailTableName, editDataKeys: Object.keys(editData) });
|
|
||||||
|
|
||||||
const entityJoinRes = await entityJoinApi.getEntityJoinConfigs(detailTableName);
|
|
||||||
const joinConfigs = entityJoinRes?.joinConfigs || [];
|
|
||||||
|
|
||||||
if (joinConfigs.length === 0) {
|
|
||||||
console.log("[EditModal:MasterLoad] entity join 없음 - 스킵");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
"[EditModal:MasterLoad] entity join:",
|
|
||||||
joinConfigs.map((c) => `${c.sourceColumn} → ${c.referenceTable}`),
|
|
||||||
);
|
|
||||||
|
|
||||||
const masterDataResult: Record<string, any> = {};
|
|
||||||
const processedTables = new Set<string>();
|
|
||||||
const { apiClient } = await import("@/lib/api/client");
|
|
||||||
|
|
||||||
for (const joinConfig of joinConfigs) {
|
|
||||||
const { sourceColumn, referenceTable, referenceColumn } = joinConfig;
|
|
||||||
if (processedTables.has(referenceTable)) continue;
|
|
||||||
|
|
||||||
const fkValue = editData[sourceColumn];
|
|
||||||
if (!fkValue) continue;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await apiClient.post(`/table-management/tables/${referenceTable}/data`, {
|
|
||||||
search: { [referenceColumn || "id"]: fkValue },
|
|
||||||
size: 1,
|
|
||||||
page: 1,
|
|
||||||
autoFilter: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const rows = response.data?.data?.data || response.data?.data?.rows || [];
|
|
||||||
if (rows.length > 0) {
|
|
||||||
const masterRow = rows[0];
|
|
||||||
for (const [col, val] of Object.entries(masterRow)) {
|
|
||||||
if (val !== undefined && val !== null && editData[col] === undefined) {
|
|
||||||
masterDataResult[col] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log("[EditModal:MasterLoad] 조회 성공:", {
|
|
||||||
table: referenceTable,
|
|
||||||
fk: `${sourceColumn}=${fkValue}`,
|
|
||||||
loadedFields: Object.keys(masterDataResult),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (queryError) {
|
|
||||||
console.warn("[EditModal:MasterLoad] 조회 실패:", referenceTable, queryError);
|
|
||||||
}
|
|
||||||
|
|
||||||
processedTables.add(referenceTable);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("[EditModal:MasterLoad] 최종 결과:", Object.keys(masterDataResult));
|
|
||||||
return masterDataResult;
|
|
||||||
} catch (error) {
|
|
||||||
console.warn("[EditModal:MasterLoad] 전체 오류:", error);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 전역 모달 이벤트 리스너 (활성 탭에서만 처리)
|
// 전역 모달 이벤트 리스너 (활성 탭에서만 처리)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleOpenEditModal = async (event: CustomEvent) => {
|
const handleOpenEditModal = async (event: CustomEvent) => {
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,13 @@ export const SaveModal: React.FC<SaveModalProps> = ({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ 필수 항목 검증
|
||||||
|
const validation = validateRequiredFields();
|
||||||
|
if (!validation.isValid) {
|
||||||
|
toast.error(`필수 항목을 입력해주세요: ${validation.missingFields.join(", ")}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setIsSaving(true);
|
setIsSaving(true);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue