205 lines
6.7 KiB
TypeScript
205 lines
6.7 KiB
TypeScript
/**
|
|
* INVYONE 스튜디오 ↔ templates 테이블 어댑터
|
|
*
|
|
* ScreenDesigner 의 layout 객체는 screens 테이블(layout-v2 JSON)을 기준으로 설계되어 있음.
|
|
* INVYONE 스튜디오는 templates 테이블(fields/views/connections 3-jsonb 분리) 을 사용하므로
|
|
* 두 포맷 사이 왕복 변환을 이 어댑터가 담당한다.
|
|
*
|
|
* - saveTemplate: 현재 layout + 3뷰 캐시 → templates.views JSON 으로 저장
|
|
* - loadTemplateAsLayout: templates.views JSON → ScreenDesigner 가 기대하는 layout + 3뷰 캐시
|
|
*
|
|
* 규칙: Record<string, any> 원칙 유지 (invyone-component.ts 의 확정 타입 외 금지).
|
|
*/
|
|
|
|
import {
|
|
getTemplateInfo,
|
|
insertTemplate,
|
|
updateTemplate,
|
|
} from "@/lib/api/template";
|
|
import { convertLegacyToV2, convertV2ToLegacy } from "./layoutV2Converter";
|
|
|
|
export interface TemplateSavePayload {
|
|
templateId: string;
|
|
name: string;
|
|
category?: string;
|
|
description?: string;
|
|
primaryTable?: string;
|
|
/** 현재 list 뷰 layout (components, gridSettings, screenResolution 포함) */
|
|
layout: Record<string, any>;
|
|
/** v2 포맷 컴포넌트 배열 — 등록/수정 뷰 */
|
|
v2Views?: {
|
|
create?: Record<string, any>[];
|
|
edit?: Record<string, any>[];
|
|
};
|
|
/** 뷰별 해상도 */
|
|
viewScreenResolutions?: {
|
|
list?: Record<string, any>;
|
|
create?: Record<string, any>;
|
|
edit?: Record<string, any>;
|
|
};
|
|
/** 템플릿 수준 필드 규격 (아직 미사용, 확장용) */
|
|
fields?: Record<string, any>[];
|
|
/** DataPort 연결 (아직 미사용, 확장용) */
|
|
connections?: Record<string, any>[];
|
|
}
|
|
|
|
/**
|
|
* 현재 layout + 3뷰 캐시를 templates.views JSON 으로 직렬화해서 updateTemplate 호출.
|
|
* 등록/수정/수정용 뷰는 v2 포맷으로, list 뷰는 convertLegacyToV2 를 거친 후 함께 묶어서 저장.
|
|
*/
|
|
export async function saveTemplate(p: TemplateSavePayload): Promise<void> {
|
|
const v2Layout = convertLegacyToV2(p.layout as any);
|
|
|
|
// screenResolutions 병합을 key-level 폴백으로 강화.
|
|
// 기존: `p.viewScreenResolutions ?? 전체폴백` → {} 나 일부 키 누락 시 정보 유실
|
|
// 개선: 항상 3뷰 모두 값이 보장되게 key 별로 폴백 처리
|
|
const fallbackSr = p.layout.screenResolution;
|
|
const vsrIn = (p.viewScreenResolutions ?? {}) as Record<string, any>;
|
|
const screenResolutions = {
|
|
list: vsrIn.list ?? fallbackSr,
|
|
create: vsrIn.create ?? fallbackSr,
|
|
edit: vsrIn.edit ?? fallbackSr,
|
|
};
|
|
|
|
const viewsJson = {
|
|
list: (v2Layout as any)?.components ?? [],
|
|
create: p.v2Views?.create ?? [],
|
|
edit: p.v2Views?.edit ?? [],
|
|
gridSettings: p.layout.gridSettings,
|
|
screenResolution: fallbackSr,
|
|
screenResolutions,
|
|
mainTableName: p.primaryTable,
|
|
};
|
|
|
|
// 진단 로그 — 저장 payload 확인용. 문제 재현 시 여기서 확인 가능.
|
|
/* eslint-disable no-console */
|
|
console.log("[saveTemplate] payload", {
|
|
templateId: p.templateId,
|
|
viewsKeys: Object.keys(viewsJson),
|
|
screenResolutions,
|
|
listCount: viewsJson.list.length,
|
|
createCount: viewsJson.create.length,
|
|
editCount: viewsJson.edit.length,
|
|
});
|
|
/* eslint-enable no-console */
|
|
|
|
const payload: Record<string, any> = {
|
|
name: p.name,
|
|
category: p.category ?? "custom",
|
|
description: p.description ?? "",
|
|
primary_table: p.primaryTable ?? "",
|
|
fields: JSON.stringify(p.fields ?? []),
|
|
views: JSON.stringify(viewsJson),
|
|
connections: JSON.stringify(p.connections ?? []),
|
|
};
|
|
await updateTemplate(p.templateId, payload);
|
|
}
|
|
|
|
/**
|
|
* 신규 template 레코드 생성 — 빈 views/fields/connections 로 시작.
|
|
* 반환된 template_id 는 URL 쿼리 등에 사용.
|
|
*/
|
|
export async function createTemplate(data: {
|
|
name: string;
|
|
category?: string;
|
|
description?: string;
|
|
primaryTable?: string;
|
|
}): Promise<Record<string, any>> {
|
|
const payload: Record<string, any> = {
|
|
name: data.name,
|
|
category: data.category ?? "custom",
|
|
description: data.description ?? "",
|
|
primary_table: data.primaryTable ?? "",
|
|
fields: JSON.stringify([]),
|
|
views: JSON.stringify({ list: [], create: [], edit: [] }),
|
|
connections: JSON.stringify([]),
|
|
};
|
|
const res = await insertTemplate(payload);
|
|
return res ?? {};
|
|
}
|
|
|
|
export interface LoadedTemplate {
|
|
templateInfo: Record<string, any>;
|
|
/** ScreenDesigner 의 layout 상태에 그대로 setLayout 으로 주입 가능 */
|
|
layout: Record<string, any>;
|
|
/** viewLayoutsRef.current.create / edit 에 주입할 legacy 컴포넌트 배열 */
|
|
viewLayouts: {
|
|
create: any[];
|
|
edit: any[];
|
|
};
|
|
primaryTable: string;
|
|
screenResolution?: Record<string, any>;
|
|
viewScreenResolutions?: {
|
|
list?: Record<string, any>;
|
|
create?: Record<string, any>;
|
|
edit?: Record<string, any>;
|
|
};
|
|
}
|
|
|
|
function parseJsonMaybe(raw: any): any {
|
|
if (raw == null) return null;
|
|
if (typeof raw === "object") return raw;
|
|
if (typeof raw === "string") {
|
|
try {
|
|
return JSON.parse(raw);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* templates.views JSON → ScreenDesigner layout + 3뷰 캐시로 역직렬화.
|
|
* 목록/등록/수정 3뷰가 모두 저장되어 있으면 각각 Legacy 포맷으로 변환.
|
|
*/
|
|
export async function loadTemplateAsLayout(
|
|
templateId: string,
|
|
): Promise<LoadedTemplate | null> {
|
|
const template = await getTemplateInfo(templateId);
|
|
if (!template) return null;
|
|
|
|
const viewsObj =
|
|
parseJsonMaybe(template.views) ?? parseJsonMaybe(template.VIEWS) ?? {};
|
|
|
|
const listV2 = Array.isArray(viewsObj.list) ? viewsObj.list : [];
|
|
const createV2 = Array.isArray(viewsObj.create) ? viewsObj.create : [];
|
|
const editV2 = Array.isArray(viewsObj.edit) ? viewsObj.edit : [];
|
|
const viewScreenResolutions = viewsObj.screenResolutions ?? {
|
|
list: viewsObj.screenResolution,
|
|
create: viewsObj.screenResolution,
|
|
edit: viewsObj.screenResolution,
|
|
};
|
|
|
|
const legacyListLayout = convertV2ToLegacy({
|
|
components: listV2,
|
|
gridSettings: viewsObj.gridSettings,
|
|
screenResolution: viewsObj.screenResolution,
|
|
} as any) ?? { components: [] };
|
|
|
|
const createLegacy = createV2.length
|
|
? convertV2ToLegacy({ components: createV2 } as any)?.components ?? []
|
|
: [];
|
|
const editLegacy = editV2.length
|
|
? convertV2ToLegacy({ components: editV2 } as any)?.components ?? []
|
|
: [];
|
|
|
|
return {
|
|
templateInfo: template,
|
|
layout: {
|
|
...legacyListLayout,
|
|
gridSettings: viewsObj.gridSettings ?? (legacyListLayout as any).gridSettings,
|
|
screenResolution:
|
|
viewsObj.screenResolution ?? (legacyListLayout as any).screenResolution,
|
|
},
|
|
viewLayouts: {
|
|
create: createLegacy,
|
|
edit: editLegacy,
|
|
},
|
|
primaryTable:
|
|
template.primary_table ?? template.PRIMARY_TABLE ?? viewsObj.mainTableName ?? "",
|
|
screenResolution: viewsObj.screenResolution,
|
|
viewScreenResolutions,
|
|
};
|
|
}
|