feat: 3-section dashboard, reference dual-monitor layout, and detail dock

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
EENE Dashboard
2026-06-08 11:13:40 +09:00
parent 5f16515dab
commit 525a4fc1f2
13 changed files with 1205 additions and 386 deletions

View 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());

View File

@@ -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,

View File

@@ -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');