feat: 3-section dashboard, reference dual-monitor layout, and detail dock
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
66
backend/scripts/migrate-sections.ts
Normal file
66
backend/scripts/migrate-sections.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* 전산관리·운영지원 → 운영관리 부문 통합 (1회 실행)
|
||||
* 사용: npm run db:migrate-sections
|
||||
*/
|
||||
import 'dotenv/config';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
const MERGE_INTO = '운영관리';
|
||||
const FROM = ['전산관리', '운영지원'] as const;
|
||||
|
||||
async function mergeCardOrder(intoKey: string, fromKeys: readonly string[]) {
|
||||
const target = await prisma.columnConfig.findUnique({ where: { key: intoKey } });
|
||||
const orders: string[] = [];
|
||||
if (target?.cardOrder) {
|
||||
try {
|
||||
orders.push(...JSON.parse(target.cardOrder));
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
for (const from of fromKeys) {
|
||||
const src = await prisma.columnConfig.findUnique({ where: { key: from } });
|
||||
if (!src?.cardOrder) continue;
|
||||
try {
|
||||
const ids = JSON.parse(src.cardOrder) as string[];
|
||||
for (const id of ids) {
|
||||
if (!orders.includes(id)) orders.push(id);
|
||||
}
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
if (orders.length === 0 && !target) return;
|
||||
await prisma.columnConfig.upsert({
|
||||
where: { key: intoKey },
|
||||
update: { cardOrder: JSON.stringify(orders) },
|
||||
create: {
|
||||
key: intoKey,
|
||||
title: '운영관리',
|
||||
titleEn: 'GA',
|
||||
subtitle: '',
|
||||
cardOrder: JSON.stringify(orders),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
for (const from of FROM) {
|
||||
const { count } = await prisma.task.updateMany({
|
||||
where: { section: from },
|
||||
data: { section: MERGE_INTO },
|
||||
});
|
||||
if (count > 0) console.log(` ✓ ${from} → ${MERGE_INTO}: ${count} tasks`);
|
||||
}
|
||||
await mergeCardOrder(MERGE_INTO, FROM);
|
||||
console.log('✅ Section migration done.');
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(() => prisma.$disconnect());
|
||||
@@ -9,7 +9,14 @@ import { PrismaClient, type Priority, type TaskStatus } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
const SOURCE = (process.env.SOURCE_API_URL || 'https://eene-dashboard-backend.onrender.com').replace(/\/$/, '');
|
||||
const SECTIONS = ['인사관리', '학습성장', '운영지원', '전산관리'];
|
||||
const SECTIONS = ['인사관리', '학습성장', '운영관리'];
|
||||
|
||||
function normalizeSection(section: string | null | undefined): string | null {
|
||||
if (!section) return null;
|
||||
if (section === '전산관리' || section === '운영지원') return '운영관리';
|
||||
if (section === '성장지원') return '학습성장';
|
||||
return section;
|
||||
}
|
||||
|
||||
type RemoteUser = { id: string; name: string; department?: string | null };
|
||||
type RemoteTask = {
|
||||
@@ -295,7 +302,7 @@ async function main() {
|
||||
priority: remote.priority,
|
||||
quarter: remote.quarter,
|
||||
category: remote.category ?? null,
|
||||
section: remote.section ?? null,
|
||||
section: normalizeSection(remote.section),
|
||||
tag: remote.tag ?? null,
|
||||
taskType: remote.taskType ?? null,
|
||||
progress: remote.progress ?? 0,
|
||||
|
||||
@@ -10,7 +10,7 @@ import { PrismaClient } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
const TARGET = (process.env.TARGET_API_URL || 'https://eene-dashboard-backend.onrender.com').replace(/\/$/, '');
|
||||
const SECTIONS = ['인사관리', '학습성장', '운영지원', '전산관리'];
|
||||
const SECTIONS = ['인사관리', '학습성장', '운영관리'];
|
||||
const PHOTOS_ONLY = process.argv.includes('--photos-only');
|
||||
const UPLOAD_DIR = path.resolve(process.env.UPLOAD_DIR || path.join(__dirname, '../../uploads'));
|
||||
const TEAM_DIR = path.join(UPLOAD_DIR, 'team');
|
||||
|
||||
Reference in New Issue
Block a user