diff --git a/backend/prisma/seed.ts b/backend/prisma/seed.ts index 58a9ecf..08211f5 100644 --- a/backend/prisma/seed.ts +++ b/backend/prisma/seed.ts @@ -42,7 +42,7 @@ const HUB_CONFIG = { ], - routineLabels: ['채용 운영', '학습 지원', '직원 소통', '자산·시설', '문서·행정'], + routineLabels: ['채용 운영', '학습 지원', '운영 지원', '자산·시설', '문서·행정'], }; diff --git a/backend/src/routes/hubConfig.ts b/backend/src/routes/hubConfig.ts index e7a09af..590fe10 100644 --- a/backend/src/routes/hubConfig.ts +++ b/backend/src/routes/hubConfig.ts @@ -13,11 +13,22 @@ export const DEFAULT_HUB_CONFIG = { { id: '2', date: '2026-05-15', text: '조직문화 진단·리더십 교육' }, { id: '3', date: '2026-06-20', text: '분기 성과 점검·평가' }, ], - routineLabels: ['채용 운영', '학습 지원', '직원 소통', '자산·시설', '문서·행정'], + routineLabels: ['채용 운영', '학습 지원', '운영 지원', '자산·시설', '문서·행정'], }; +function migrateRoutineLabels(labels: string[]): string[] { + return labels.map((label) => { + if (label === '교육 운영') return '학습 지원'; + if (label === '직원 소통') return '운영 지원'; + return label; + }); +} + function normalizeConfig(raw: Record) { const sloganTitle = (raw.sloganTitle as string) ?? DEFAULT_HUB_CONFIG.sloganTitle; + const routineLabels = Array.isArray(raw.routineLabels) + ? migrateRoutineLabels(raw.routineLabels as string[]) + : DEFAULT_HUB_CONFIG.routineLabels; return { sloganTitle: sloganTitle === '분기 슬로건' ? '분기 중점 과제' : sloganTitle, sloganLines: Array.isArray(raw.sloganLines) @@ -27,9 +38,7 @@ function normalizeConfig(raw: Record) { scheduleItems: Array.isArray(raw.scheduleItems) ? (raw.scheduleItems as typeof DEFAULT_HUB_CONFIG.scheduleItems) : DEFAULT_HUB_CONFIG.scheduleItems, - routineLabels: Array.isArray(raw.routineLabels) - ? (raw.routineLabels as string[]) - : DEFAULT_HUB_CONFIG.routineLabels, + routineLabels, }; } diff --git a/data/postgres/base/16384/1259 b/data/postgres/base/16384/1259 index 33ab7ec..fc0063d 100644 Binary files a/data/postgres/base/16384/1259 and b/data/postgres/base/16384/1259 differ diff --git a/data/postgres/base/16384/24615 b/data/postgres/base/16384/24615 index 1ee38ad..b0348b0 100644 Binary files a/data/postgres/base/16384/24615 and b/data/postgres/base/16384/24615 differ diff --git a/data/postgres/base/16384/24625 b/data/postgres/base/16384/24625 index 796fa39..7f0b7d0 100644 Binary files a/data/postgres/base/16384/24625 and b/data/postgres/base/16384/24625 differ diff --git a/data/postgres/base/16384/24633 b/data/postgres/base/16384/24633 index 9f5f54c..b72cda9 100644 Binary files a/data/postgres/base/16384/24633 and b/data/postgres/base/16384/24633 differ diff --git a/data/postgres/base/16384/24635 b/data/postgres/base/16384/24635 index 425c255..470c924 100644 Binary files a/data/postgres/base/16384/24635 and b/data/postgres/base/16384/24635 differ diff --git a/data/postgres/base/16384/24641 b/data/postgres/base/16384/24641 index 7e555bb..ac67af3 100644 Binary files a/data/postgres/base/16384/24641 and b/data/postgres/base/16384/24641 differ diff --git a/data/postgres/base/16384/24652 b/data/postgres/base/16384/24652 index 56f06c6..496628e 100644 Binary files a/data/postgres/base/16384/24652 and b/data/postgres/base/16384/24652 differ diff --git a/data/postgres/base/16384/24652_fsm b/data/postgres/base/16384/24652_fsm new file mode 100644 index 0000000..65e3765 Binary files /dev/null and b/data/postgres/base/16384/24652_fsm differ diff --git a/data/postgres/base/16384/24658 b/data/postgres/base/16384/24658 index a74362b..af6b19c 100644 Binary files a/data/postgres/base/16384/24658 and b/data/postgres/base/16384/24658 differ diff --git a/data/postgres/base/16384/24669 b/data/postgres/base/16384/24669 index d6fd97e..505ac31 100644 Binary files a/data/postgres/base/16384/24669 and b/data/postgres/base/16384/24669 differ diff --git a/data/postgres/base/16384/24670 b/data/postgres/base/16384/24670 index 52a0633..43fcedd 100644 Binary files a/data/postgres/base/16384/24670 and b/data/postgres/base/16384/24670 differ diff --git a/data/postgres/base/16384/24671 b/data/postgres/base/16384/24671 index 6e7b4b7..04f4585 100644 Binary files a/data/postgres/base/16384/24671 and b/data/postgres/base/16384/24671 differ diff --git a/data/postgres/base/16384/24672 b/data/postgres/base/16384/24672 index 06da28e..4938824 100644 Binary files a/data/postgres/base/16384/24672 and b/data/postgres/base/16384/24672 differ diff --git a/data/postgres/base/16384/24674 b/data/postgres/base/16384/24674 index 0ed379f..8f91fbd 100644 Binary files a/data/postgres/base/16384/24674 and b/data/postgres/base/16384/24674 differ diff --git a/data/postgres/base/16384/24718 b/data/postgres/base/16384/24718 index a7d40ee..76adec5 100644 Binary files a/data/postgres/base/16384/24718 and b/data/postgres/base/16384/24718 differ diff --git a/data/postgres/base/16384/24719 b/data/postgres/base/16384/24719 index 77f698f..edeca11 100644 Binary files a/data/postgres/base/16384/24719 and b/data/postgres/base/16384/24719 differ diff --git a/data/postgres/base/16384/24719_fsm b/data/postgres/base/16384/24719_fsm index 98aa91a..1bde6e0 100644 Binary files a/data/postgres/base/16384/24719_fsm and b/data/postgres/base/16384/24719_fsm differ diff --git a/data/postgres/base/16384/24727 b/data/postgres/base/16384/24727 index 51b96b1..2c9bd74 100644 Binary files a/data/postgres/base/16384/24727 and b/data/postgres/base/16384/24727 differ diff --git a/data/postgres/base/16384/24729 b/data/postgres/base/16384/24729 index bd9988b..c935754 100644 Binary files a/data/postgres/base/16384/24729 and b/data/postgres/base/16384/24729 differ diff --git a/data/postgres/base/16384/24735 b/data/postgres/base/16384/24735 index 71e493e..b31c52e 100644 Binary files a/data/postgres/base/16384/24735 and b/data/postgres/base/16384/24735 differ diff --git a/data/postgres/base/16384/24741 b/data/postgres/base/16384/24741 index 07184a1..7eb3782 100644 Binary files a/data/postgres/base/16384/24741 and b/data/postgres/base/16384/24741 differ diff --git a/data/postgres/base/16384/24754 b/data/postgres/base/16384/24754 index 721117f..7ee2d5b 100644 Binary files a/data/postgres/base/16384/24754 and b/data/postgres/base/16384/24754 differ diff --git a/data/postgres/base/16384/24764 b/data/postgres/base/16384/24764 index caa93dd..2130abf 100644 Binary files a/data/postgres/base/16384/24764 and b/data/postgres/base/16384/24764 differ diff --git a/data/postgres/base/16384/24769 b/data/postgres/base/16384/24769 index a7cd722..8a50cf9 100644 Binary files a/data/postgres/base/16384/24769 and b/data/postgres/base/16384/24769 differ diff --git a/data/postgres/base/16384/24771 b/data/postgres/base/16384/24771 index 1aaecc4..20b4fa3 100644 Binary files a/data/postgres/base/16384/24771 and b/data/postgres/base/16384/24771 differ diff --git a/data/postgres/base/16384/24778 b/data/postgres/base/16384/24778 index a35632b..0ed4139 100644 Binary files a/data/postgres/base/16384/24778 and b/data/postgres/base/16384/24778 differ diff --git a/data/postgres/base/16384/24788 b/data/postgres/base/16384/24788 index 8386148..7ea385b 100644 Binary files a/data/postgres/base/16384/24788 and b/data/postgres/base/16384/24788 differ diff --git a/data/postgres/base/16384/24789 b/data/postgres/base/16384/24789 index 377e6cc..ec16900 100644 Binary files a/data/postgres/base/16384/24789 and b/data/postgres/base/16384/24789 differ diff --git a/data/postgres/base/16384/2619 b/data/postgres/base/16384/2619 index 0e30754..cc4956d 100644 Binary files a/data/postgres/base/16384/2619 and b/data/postgres/base/16384/2619 differ diff --git a/data/postgres/base/16384/2619_fsm b/data/postgres/base/16384/2619_fsm index 0e4b285..bca3255 100644 Binary files a/data/postgres/base/16384/2619_fsm and b/data/postgres/base/16384/2619_fsm differ diff --git a/data/postgres/base/16384/2696 b/data/postgres/base/16384/2696 index 59fc1a3..df5c67d 100644 Binary files a/data/postgres/base/16384/2696 and b/data/postgres/base/16384/2696 differ diff --git a/data/postgres/base/16384/2840 b/data/postgres/base/16384/2840 index 0b5a2c2..77c05b7 100644 Binary files a/data/postgres/base/16384/2840 and b/data/postgres/base/16384/2840 differ diff --git a/data/postgres/base/16384/2840_fsm b/data/postgres/base/16384/2840_fsm index 846a9be..cfb7ce9 100644 Binary files a/data/postgres/base/16384/2840_fsm and b/data/postgres/base/16384/2840_fsm differ diff --git a/data/postgres/base/16384/2841 b/data/postgres/base/16384/2841 index 988464f..2451e35 100644 Binary files a/data/postgres/base/16384/2841 and b/data/postgres/base/16384/2841 differ diff --git a/data/postgres/base/16384/32768 b/data/postgres/base/16384/32768 index e69de29..fdf353b 100644 Binary files a/data/postgres/base/16384/32768 and b/data/postgres/base/16384/32768 differ diff --git a/data/postgres/base/16384/32768_fsm b/data/postgres/base/16384/32768_fsm new file mode 100644 index 0000000..a9d738c Binary files /dev/null and b/data/postgres/base/16384/32768_fsm differ diff --git a/data/postgres/base/16384/32768_vm b/data/postgres/base/16384/32768_vm new file mode 100644 index 0000000..7aef925 Binary files /dev/null and b/data/postgres/base/16384/32768_vm differ diff --git a/data/postgres/base/16384/32773 b/data/postgres/base/16384/32773 index d670de9..1d4ed65 100644 Binary files a/data/postgres/base/16384/32773 and b/data/postgres/base/16384/32773 differ diff --git a/data/postgres/base/16384/32775 b/data/postgres/base/16384/32775 index eee9901..d724edd 100644 Binary files a/data/postgres/base/16384/32775 and b/data/postgres/base/16384/32775 differ diff --git a/data/postgres/base/16384/32776 b/data/postgres/base/16384/32776 index 1d94919..8bb676f 100644 Binary files a/data/postgres/base/16384/32776 and b/data/postgres/base/16384/32776 differ diff --git a/data/postgres/global/pg_control b/data/postgres/global/pg_control index 0eebbcd..30cfcb8 100644 Binary files a/data/postgres/global/pg_control and b/data/postgres/global/pg_control differ diff --git a/data/postgres/pg_wal/000000010000000000000001 b/data/postgres/pg_wal/000000010000000000000001 index e58a140..53dadce 100644 Binary files a/data/postgres/pg_wal/000000010000000000000001 and b/data/postgres/pg_wal/000000010000000000000001 differ diff --git a/data/postgres/pg_xact/0000 b/data/postgres/pg_xact/0000 index 65c892a..a206506 100644 Binary files a/data/postgres/pg_xact/0000 and b/data/postgres/pg_xact/0000 differ diff --git a/frontend/public/quarter-dashboard-preview.html b/frontend/public/quarter-dashboard-preview.html index 89a18e5..f7514f9 100644 --- a/frontend/public/quarter-dashboard-preview.html +++ b/frontend/public/quarter-dashboard-preview.html @@ -1645,7 +1645,7 @@
- +
diff --git a/frontend/src/components/dashboard/QuarterStatusBoard.tsx b/frontend/src/components/dashboard/QuarterStatusBoard.tsx index e07d801..1dbb768 100644 --- a/frontend/src/components/dashboard/QuarterStatusBoard.tsx +++ b/frontend/src/components/dashboard/QuarterStatusBoard.tsx @@ -100,7 +100,7 @@ const DUMMY_DEPARTMENTS: DeptBlock[] = [ const HUB_MESSAGE = '인사·육성·문화·총무 개선과제 정상 추진'; -const ROUTINE_ITEMS = ['채용 운영', '교육 운영', '직원 소통', '자산·시설 관리', '문서·행정 지원']; +const ROUTINE_ITEMS = ['채용 운영', '교육 운영', '운영 지원', '자산·시설 관리', '문서·행정 지원']; const FOCUS_ITEMS = ['핵심직무 채용 완료', '복지제도 개선안 확정', '안전보안 점검 강화']; diff --git a/frontend/src/components/detail/MilestoneTimeline.tsx b/frontend/src/components/detail/MilestoneTimeline.tsx index 7d3619a..09a9779 100644 --- a/frontend/src/components/detail/MilestoneTimeline.tsx +++ b/frontend/src/components/detail/MilestoneTimeline.tsx @@ -46,7 +46,7 @@ const VARIANT_DEFAULTS = { portfolioTab: '분기 전체', }, routine: { - focusTitle: '업무명별 타임라인', + focusTitle: '업무별 타임라인', projectTitle: '상시 전체 일정', portfolioTitle: '분기 전체 상시업무', rowLabelHeader: '업무명', @@ -140,9 +140,45 @@ export function MilestoneTimeline({ const chartRef = useRef(null); const labelsScrollRef = useRef(null); const rowRefs = useRef>(new Map()); + const measureRef = useRef(null); const dragRef = useRef<{ startX: number; viewport: { start: Date; end: Date } } | null>(null); const didPanRef = useRef(false); const [isPanning, setIsPanning] = useState(false); + const [expandedBar, setExpandedBar] = useState<{ + id: string; + widthPx: number; + leftPx: number; + } | null>(null); + + const measureTitleWidth = (title: string) => { + const el = measureRef.current; + if (!el) return 0; + el.textContent = title; + return el.offsetWidth + 20; + }; + + const handleFocusBarEnter = ( + row: { id: string; leftPct: number; widthPct: number }, + title: string, + event: React.MouseEvent, + ) => { + const chart = chartRef.current; + if (!chart) return; + const btn = event.currentTarget; + const chartWidth = chart.clientWidth; + const leftPx = (row.leftPct / 100) * chartWidth; + const origWidthPx = btn.offsetWidth; + const neededWidthPx = measureTitleWidth(title); + if (neededWidthPx <= origWidthPx + 1) return; + + const widthPx = Math.min(chartWidth, neededWidthPx); + const extra = widthPx - origWidthPx; + let expandedLeftPx = leftPx - extra / 2; + if (expandedLeftPx < 0) expandedLeftPx = 0; + if (expandedLeftPx + widthPx > chartWidth) expandedLeftPx = chartWidth - widthPx; + + setExpandedBar({ id: row.id, widthPx, leftPx: expandedLeftPx }); + }; const rowGroups = useMemo(() => { if (!model) return []; @@ -286,6 +322,7 @@ export function MilestoneTimeline({ setIsPanning(false); dragRef.current = null; didPanRef.current = false; + setExpandedBar(null); }, [viewMode]); const handleRowSelect = (milestoneId: string, taskId: string) => { @@ -294,7 +331,7 @@ export function MilestoneTimeline({ onSelect?.(milestoneId); }; - const renderBar = ( + const renderGanttBar = ( group: { milestoneId: string; title: string; @@ -303,13 +340,12 @@ export function MilestoneTimeline({ }, isSelected: boolean, taskId: string, - showGanttBar: boolean, ) => group.segments.map((row) => ( )); + const renderFocusBar = ( + group: { + milestoneId: string; + title: string; + progress: number; + segments: Array<{ id: string; leftPct: number; widthPct: number; progress: number }>; + }, + isSelected: boolean, + taskId: string, + ) => + group.segments.map((row) => { + const isExpanded = expandedBar?.id === row.id; + return ( + + ); + }); + const renderTicks = () => ( @@ -478,11 +554,10 @@ export function MilestoneTimeline({ key={row.milestoneId} className={`milestone-timeline__row${row.isCurrentTask ? ' is-current-task' : ' is-other-task'}`} > - {renderBar( + {renderGanttBar( row, row.milestoneId === selectedId && row.isCurrentTask, row.taskId, - true, )} ), @@ -530,7 +605,7 @@ export function MilestoneTimeline({ else rowRefs.current.delete(group.milestoneId); }} > - {renderBar(group, group.milestoneId === selectedId, ownerTaskId ?? '', true)} + {renderGanttBar(group, group.milestoneId === selectedId, ownerTaskId ?? '')} ))} @@ -541,6 +616,7 @@ export function MilestoneTimeline({ ) : (
+