레이아웃 추가기능
This commit is contained in:
105
backend-node/scripts/add-missing-columns.js
Normal file
105
backend-node/scripts/add-missing-columns.js
Normal file
@@ -0,0 +1,105 @@
|
||||
const { PrismaClient } = require("@prisma/client");
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function addMissingColumns() {
|
||||
try {
|
||||
console.log("🔄 누락된 컬럼들을 screen_layouts 테이블에 추가 중...");
|
||||
|
||||
// layout_type 컬럼 추가
|
||||
try {
|
||||
await prisma.$executeRaw`
|
||||
ALTER TABLE screen_layouts
|
||||
ADD COLUMN IF NOT EXISTS layout_type VARCHAR(50);
|
||||
`;
|
||||
console.log("✅ layout_type 컬럼 추가 완료");
|
||||
} catch (error) {
|
||||
console.log(
|
||||
"ℹ️ layout_type 컬럼이 이미 존재하거나 추가 중 오류:",
|
||||
error.message
|
||||
);
|
||||
}
|
||||
|
||||
// layout_config 컬럼 추가
|
||||
try {
|
||||
await prisma.$executeRaw`
|
||||
ALTER TABLE screen_layouts
|
||||
ADD COLUMN IF NOT EXISTS layout_config JSONB;
|
||||
`;
|
||||
console.log("✅ layout_config 컬럼 추가 완료");
|
||||
} catch (error) {
|
||||
console.log(
|
||||
"ℹ️ layout_config 컬럼이 이미 존재하거나 추가 중 오류:",
|
||||
error.message
|
||||
);
|
||||
}
|
||||
|
||||
// zones_config 컬럼 추가
|
||||
try {
|
||||
await prisma.$executeRaw`
|
||||
ALTER TABLE screen_layouts
|
||||
ADD COLUMN IF NOT EXISTS zones_config JSONB;
|
||||
`;
|
||||
console.log("✅ zones_config 컬럼 추가 완료");
|
||||
} catch (error) {
|
||||
console.log(
|
||||
"ℹ️ zones_config 컬럼이 이미 존재하거나 추가 중 오류:",
|
||||
error.message
|
||||
);
|
||||
}
|
||||
|
||||
// zone_id 컬럼 추가
|
||||
try {
|
||||
await prisma.$executeRaw`
|
||||
ALTER TABLE screen_layouts
|
||||
ADD COLUMN IF NOT EXISTS zone_id VARCHAR(100);
|
||||
`;
|
||||
console.log("✅ zone_id 컬럼 추가 완료");
|
||||
} catch (error) {
|
||||
console.log(
|
||||
"ℹ️ zone_id 컬럼이 이미 존재하거나 추가 중 오류:",
|
||||
error.message
|
||||
);
|
||||
}
|
||||
|
||||
// 인덱스 생성 (성능 향상)
|
||||
try {
|
||||
await prisma.$executeRaw`
|
||||
CREATE INDEX IF NOT EXISTS idx_screen_layouts_layout_type
|
||||
ON screen_layouts(layout_type);
|
||||
`;
|
||||
console.log("✅ layout_type 인덱스 생성 완료");
|
||||
} catch (error) {
|
||||
console.log("ℹ️ layout_type 인덱스 생성 중 오류:", error.message);
|
||||
}
|
||||
|
||||
try {
|
||||
await prisma.$executeRaw`
|
||||
CREATE INDEX IF NOT EXISTS idx_screen_layouts_zone_id
|
||||
ON screen_layouts(zone_id);
|
||||
`;
|
||||
console.log("✅ zone_id 인덱스 생성 완료");
|
||||
} catch (error) {
|
||||
console.log("ℹ️ zone_id 인덱스 생성 중 오류:", error.message);
|
||||
}
|
||||
|
||||
// 최종 테이블 구조 확인
|
||||
const columns = await prisma.$queryRaw`
|
||||
SELECT column_name, data_type, is_nullable, column_default
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'screen_layouts'
|
||||
ORDER BY ordinal_position
|
||||
`;
|
||||
|
||||
console.log("\n📋 screen_layouts 테이블 최종 구조:");
|
||||
console.table(columns);
|
||||
|
||||
console.log("\n🎉 모든 누락된 컬럼 추가 작업이 완료되었습니다!");
|
||||
} catch (error) {
|
||||
console.error("❌ 컬럼 추가 중 오류 발생:", error);
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
addMissingColumns();
|
||||
309
backend-node/scripts/init-layout-standards.js
Normal file
309
backend-node/scripts/init-layout-standards.js
Normal file
@@ -0,0 +1,309 @@
|
||||
/**
|
||||
* 레이아웃 표준 데이터 초기화 스크립트
|
||||
* 기본 레이아웃들을 layout_standards 테이블에 삽입합니다.
|
||||
*/
|
||||
|
||||
const { PrismaClient } = require("@prisma/client");
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
// 기본 레이아웃 데이터
|
||||
const PREDEFINED_LAYOUTS = [
|
||||
{
|
||||
layout_code: "GRID_2X2_001",
|
||||
layout_name: "2x2 그리드",
|
||||
layout_name_eng: "2x2 Grid",
|
||||
description: "2행 2열의 균등한 그리드 레이아웃입니다.",
|
||||
layout_type: "grid",
|
||||
category: "basic",
|
||||
icon_name: "grid",
|
||||
default_size: { width: 800, height: 600 },
|
||||
layout_config: {
|
||||
grid: { rows: 2, columns: 2, gap: 16 },
|
||||
},
|
||||
zones_config: [
|
||||
{
|
||||
id: "zone1",
|
||||
name: "상단 좌측",
|
||||
position: { row: 0, column: 0 },
|
||||
size: { width: "50%", height: "50%" },
|
||||
},
|
||||
{
|
||||
id: "zone2",
|
||||
name: "상단 우측",
|
||||
position: { row: 0, column: 1 },
|
||||
size: { width: "50%", height: "50%" },
|
||||
},
|
||||
{
|
||||
id: "zone3",
|
||||
name: "하단 좌측",
|
||||
position: { row: 1, column: 0 },
|
||||
size: { width: "50%", height: "50%" },
|
||||
},
|
||||
{
|
||||
id: "zone4",
|
||||
name: "하단 우측",
|
||||
position: { row: 1, column: 1 },
|
||||
size: { width: "50%", height: "50%" },
|
||||
},
|
||||
],
|
||||
sort_order: 1,
|
||||
is_active: "Y",
|
||||
is_public: "Y",
|
||||
company_code: "DEFAULT",
|
||||
},
|
||||
{
|
||||
layout_code: "FORM_TWO_COLUMN_001",
|
||||
layout_name: "2단 폼 레이아웃",
|
||||
layout_name_eng: "Two Column Form",
|
||||
description: "좌우 2단으로 구성된 폼 레이아웃입니다.",
|
||||
layout_type: "grid",
|
||||
category: "form",
|
||||
icon_name: "columns",
|
||||
default_size: { width: 800, height: 400 },
|
||||
layout_config: {
|
||||
grid: { rows: 1, columns: 2, gap: 24 },
|
||||
},
|
||||
zones_config: [
|
||||
{
|
||||
id: "left",
|
||||
name: "좌측 입력 영역",
|
||||
position: { row: 0, column: 0 },
|
||||
size: { width: "50%", height: "100%" },
|
||||
},
|
||||
{
|
||||
id: "right",
|
||||
name: "우측 입력 영역",
|
||||
position: { row: 0, column: 1 },
|
||||
size: { width: "50%", height: "100%" },
|
||||
},
|
||||
],
|
||||
sort_order: 2,
|
||||
is_active: "Y",
|
||||
is_public: "Y",
|
||||
company_code: "DEFAULT",
|
||||
},
|
||||
{
|
||||
layout_code: "FLEXBOX_ROW_001",
|
||||
layout_name: "가로 플렉스박스",
|
||||
layout_name_eng: "Horizontal Flexbox",
|
||||
description: "가로 방향으로 배치되는 플렉스박스 레이아웃입니다.",
|
||||
layout_type: "flexbox",
|
||||
category: "basic",
|
||||
icon_name: "flex",
|
||||
default_size: { width: 800, height: 300 },
|
||||
layout_config: {
|
||||
flexbox: {
|
||||
direction: "row",
|
||||
justify: "flex-start",
|
||||
align: "stretch",
|
||||
wrap: "nowrap",
|
||||
gap: 16,
|
||||
},
|
||||
},
|
||||
zones_config: [
|
||||
{
|
||||
id: "left",
|
||||
name: "좌측 영역",
|
||||
position: {},
|
||||
size: { width: "50%", height: "100%" },
|
||||
},
|
||||
{
|
||||
id: "right",
|
||||
name: "우측 영역",
|
||||
position: {},
|
||||
size: { width: "50%", height: "100%" },
|
||||
},
|
||||
],
|
||||
sort_order: 3,
|
||||
is_active: "Y",
|
||||
is_public: "Y",
|
||||
company_code: "DEFAULT",
|
||||
},
|
||||
{
|
||||
layout_code: "SPLIT_HORIZONTAL_001",
|
||||
layout_name: "수평 분할",
|
||||
layout_name_eng: "Horizontal Split",
|
||||
description: "크기 조절이 가능한 수평 분할 레이아웃입니다.",
|
||||
layout_type: "split",
|
||||
category: "basic",
|
||||
icon_name: "separator-horizontal",
|
||||
default_size: { width: 800, height: 400 },
|
||||
layout_config: {
|
||||
split: {
|
||||
direction: "horizontal",
|
||||
ratio: [50, 50],
|
||||
minSize: [200, 200],
|
||||
resizable: true,
|
||||
splitterSize: 4,
|
||||
},
|
||||
},
|
||||
zones_config: [
|
||||
{
|
||||
id: "left",
|
||||
name: "좌측 패널",
|
||||
position: {},
|
||||
size: { width: "50%", height: "100%" },
|
||||
isResizable: true,
|
||||
},
|
||||
{
|
||||
id: "right",
|
||||
name: "우측 패널",
|
||||
position: {},
|
||||
size: { width: "50%", height: "100%" },
|
||||
isResizable: true,
|
||||
},
|
||||
],
|
||||
sort_order: 4,
|
||||
is_active: "Y",
|
||||
is_public: "Y",
|
||||
company_code: "DEFAULT",
|
||||
},
|
||||
{
|
||||
layout_code: "TABS_HORIZONTAL_001",
|
||||
layout_name: "수평 탭",
|
||||
layout_name_eng: "Horizontal Tabs",
|
||||
description: "상단에 탭이 있는 탭 레이아웃입니다.",
|
||||
layout_type: "tabs",
|
||||
category: "navigation",
|
||||
icon_name: "tabs",
|
||||
default_size: { width: 800, height: 500 },
|
||||
layout_config: {
|
||||
tabs: {
|
||||
position: "top",
|
||||
variant: "default",
|
||||
size: "md",
|
||||
defaultTab: "tab1",
|
||||
closable: false,
|
||||
},
|
||||
},
|
||||
zones_config: [
|
||||
{
|
||||
id: "tab1",
|
||||
name: "첫 번째 탭",
|
||||
position: {},
|
||||
size: { width: "100%", height: "100%" },
|
||||
},
|
||||
{
|
||||
id: "tab2",
|
||||
name: "두 번째 탭",
|
||||
position: {},
|
||||
size: { width: "100%", height: "100%" },
|
||||
},
|
||||
{
|
||||
id: "tab3",
|
||||
name: "세 번째 탭",
|
||||
position: {},
|
||||
size: { width: "100%", height: "100%" },
|
||||
},
|
||||
],
|
||||
sort_order: 5,
|
||||
is_active: "Y",
|
||||
is_public: "Y",
|
||||
company_code: "DEFAULT",
|
||||
},
|
||||
{
|
||||
layout_code: "TABLE_WITH_FILTERS_001",
|
||||
layout_name: "필터가 있는 테이블",
|
||||
layout_name_eng: "Table with Filters",
|
||||
description: "상단에 필터가 있고 하단에 테이블이 있는 레이아웃입니다.",
|
||||
layout_type: "flexbox",
|
||||
category: "table",
|
||||
icon_name: "table",
|
||||
default_size: { width: 1000, height: 600 },
|
||||
layout_config: {
|
||||
flexbox: {
|
||||
direction: "column",
|
||||
justify: "flex-start",
|
||||
align: "stretch",
|
||||
wrap: "nowrap",
|
||||
gap: 16,
|
||||
},
|
||||
},
|
||||
zones_config: [
|
||||
{
|
||||
id: "filters",
|
||||
name: "검색 필터",
|
||||
position: {},
|
||||
size: { width: "100%", height: "auto" },
|
||||
},
|
||||
{
|
||||
id: "table",
|
||||
name: "데이터 테이블",
|
||||
position: {},
|
||||
size: { width: "100%", height: "1fr" },
|
||||
},
|
||||
],
|
||||
sort_order: 6,
|
||||
is_active: "Y",
|
||||
is_public: "Y",
|
||||
company_code: "DEFAULT",
|
||||
},
|
||||
];
|
||||
|
||||
async function initializeLayoutStandards() {
|
||||
try {
|
||||
console.log("🏗️ 레이아웃 표준 데이터 초기화 시작...");
|
||||
|
||||
// 기존 데이터 확인
|
||||
const existingLayouts = await prisma.layout_standards.count();
|
||||
if (existingLayouts > 0) {
|
||||
console.log(`⚠️ 이미 ${existingLayouts}개의 레이아웃이 존재합니다.`);
|
||||
console.log(
|
||||
"기존 데이터를 삭제하고 새로 생성하시겠습니까? (기본값: 건너뛰기)"
|
||||
);
|
||||
|
||||
// 기존 데이터가 있으면 건너뛰기 (안전을 위해)
|
||||
console.log("💡 기존 데이터를 유지하고 건너뜁니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 데이터 삽입
|
||||
let insertedCount = 0;
|
||||
|
||||
for (const layoutData of PREDEFINED_LAYOUTS) {
|
||||
try {
|
||||
await prisma.layout_standards.create({
|
||||
data: {
|
||||
...layoutData,
|
||||
created_date: new Date(),
|
||||
updated_date: new Date(),
|
||||
created_by: "SYSTEM",
|
||||
updated_by: "SYSTEM",
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`✅ ${layoutData.layout_name} 생성 완료`);
|
||||
insertedCount++;
|
||||
} catch (error) {
|
||||
console.error(`❌ ${layoutData.layout_name} 생성 실패:`, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(
|
||||
`🎉 레이아웃 표준 데이터 초기화 완료! (${insertedCount}/${PREDEFINED_LAYOUTS.length})`
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("❌ 레이아웃 표준 데이터 초기화 실패:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 스크립트 실행
|
||||
if (require.main === module) {
|
||||
initializeLayoutStandards()
|
||||
.then(() => {
|
||||
console.log("✨ 스크립트 실행 완료");
|
||||
process.exit(0);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("💥 스크립트 실행 실패:", error);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { initializeLayoutStandards };
|
||||
|
||||
Reference in New Issue
Block a user