Commit Graph

599 Commits

Author SHA1 Message Date
johngreen acbab68a12 chore(테이블관리): 사소 3건 정리 (PR-D) — 디버그 로그 / dead code / 에러 메시지
1. 운영 console.log/warn 제거 (G15) — page.tsx 의 이모지 prefix 디버그 로그
   (🔍 🔄  🗑️ 📥 📊 📋) 일괄 제거. catch 블록의 console.error 는 추적용으로
   유지. CreateTableModal 의 컬럼 조회 디버그 로그도 정리.

2. useLogTable dead code 정리 (G16) — CreateTableModal 의 useLogTable state,
   handleCreateTable 분기, 주석 처리된 체크박스 UI 모두 제거. 시그니처 안 맞는
   createLogTable 호출 페이로드까지 같이 사라짐. Activity / Checkbox import 도
   필요 없어졌으므로 제거.

3. 에러 메시지 일관화 (G17) — DdlController 와 TableManagementController 의
   "최고 관리자 권한이 필요합니다." 메시지 모두 "최고 관리자(SUPER_ADMIN) 권한이
   필요합니다." 로 통일. 일반 ADMIN 권한 메시지는 그대로 유지.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 15:11:21 +09:00
hjjeong 8a9285f13e Merge pull request 'fix(배치관리): DB 커넥션 변경 시 테이블 목록이 안 바뀌는 버그' (#30) from hjjeong into main
Build & Deploy to K8s / build-and-deploy (push) Successful in 8m51s
Reviewed-on: #30
2026-05-22 05:57:24 +00:00
johngreen 33f0647c61 fix(테이블관리): 심각 5건 일괄 수정 (PR-B)
1. setTimeout 1초 reload 제거 — saveAllSettings / handleSaveColumn 의
   setTimeout(loadColumnTypes, 1000) 두 곳을 await 직접 호출로. 1초 깜빡임
   + race 해소.

2. typeFilter 자동 해제 시 input_type 변경 — 도넛 차트 필터가 켜진 상태에서
   상세 패널로 input_type 을 다른 타입으로 바꾸면 그 행이 그리드에서 갑자기
   사라지는 혼란. 변경된 타입이 필터와 다르면 자동으로 필터 해제.

3. composite PK 다이얼로그 — 매 PK 토글마다 확인 다이얼로그 뜨던 답답함.
   "이번 세션 다시 묻지 않기" 체크박스 추가. 체크 시 applyPkChange 즉시 호출.

4. 카테고리 매핑 병렬화 — for-of await 직렬 호출을 Promise.allSettled 로
   변경. 메뉴 수십 개 × 컬럼 수 = 수십 초 멈춤 → 한 번의 병렬 호출.

5. 좁은 화면 상세 패널 stuck 해소 — 패널이 열린 상태에서 column=null 이 되면
   X 버튼이 사라져 닫을 수단이 없던 문제. 빈 상태 UI 에도 X 버튼 유지 +
   ESC 키 핸들러 추가.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 14:50:12 +09:00
hjjeong 8606f0aaa3 Merge remote-tracking branch 'origin/main' into hjjeong 2026-05-22 14:49:50 +09:00
johngreen f530b3cf31 fix(테이블관리): 블로커 5건 일괄 수정 (PR-A)
종합 감사 결과 발견된 블로커 5건:

1. AddColumnModal / CreateTableModal prop 미스매치 — 호출부 tableName/sourceTableName(camelCase)
   vs 인터페이스 table_name/source_table_name(snake_case). 결과적으로 컬럼 추가 기능이 실제로
   동작하지 않았음 (/api/ddl/tables/undefined/columns). 호출부를 인터페이스에 맞춰 통일.

2. IS_NULLABLE 강제 'Y' 덮어쓰기 (backend mapper) — upsertColumnSettings 의 VALUES 절이
   모든 케이스에서 'Y' 리터럴을 박음. NN 토글 후 "컬럼 설정 저장" 누르면 필수 입력 제약이
   조용히 사라짐. COALESCE(#{is_nullable}, 'Y') 로 변경 + ON CONFLICT 절에 IS_NULLABLE
   COALESCE 추가. Service 도 settings 에서 is_nullable 을 읽어 'Y'/'N' 으로 정규화 후 전달.

3. 저장 안 한 편집 침묵 손실 — 우측 패널에서 편집 후 저장 안 누르고 다른 테이블로 이동 시
   변경 사항 소실. hasUnsavedChanges memo 추가 (columns vs originalColumns JSON 비교).
   handleTableSelect 에서 dirty 면 confirm 다이얼로그.

4. PK / NN / IDX / UQ 위치별 비대칭 저장 — 그리드 행 칩은 즉시 저장하는데 우측 상세 패널
   토글은 메모리만 변경하고 저장 버튼 필요. 두 위치 모두 즉시 저장으로 통일 (상세 패널의
   onColumnChange 가 is_nullable / is_unique 필드를 받으면 handleNullableToggle /
   handleUniqueToggle 호출하도록).

5. SUPER_ADMIN role 검증 누락 (backend 보안) — DdlController 의 isSuperAdmin 이 company_code
   == "*" 만 보고 role 클레임 무시. 토큰 변조 또는 "*" 회사 소속만으로 운영 DB DROP / ALTER
   가능. company_code "*" AND role "SUPER_ADMIN" 둘 다 충족 시에만 통과로 변경. 11개 엔드포인트
   에 @RequestAttribute("role") 추가.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 14:43:15 +09:00
johngreen 6233877029 style(테이블타입): 탭 스타일 Google 식 underline 으로 변경 — 가시성 ↑
기존 shadcn 기본 Tabs 가 둥근 알약 (segmented control) 스타일이라 탭이 아니라 토글처럼
보임. 사용자가 "탭이 눈에 잘 안 띈다" 지적.

Gmail/Drive/GitHub/Vercel 공통 패턴인 underline 스타일로 변경:
- TabsList: 전체 폭 + 하단 1px 구분선, 배경/padding 제거
- TabsTrigger: 평면 + 활성 시 2px primary 밑줄 + 글자 색 강조, 비활성은 muted
- 글자 / 아이콘 크기 한 단계 ↑ (text-xs→text-sm, h-3.5→h-4)
- 비활성에도 transparent border-b-2 줘서 layout shift 방지

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 14:03:52 +09:00
johngreen 7dbeccc182 feat(테이블타입): 와이드 모니터에서 우측 상세 패널 고정 표시 — 슬라이드 in 제거
기존엔 컬럼 행 클릭해야 우측 패널이 슬라이드로 들어옴. 와이드 모니터에선 우측에 큰 빈 공간이
남는데도 패널이 숨어있어 화면이 흔들려 보임 → 일관성 ↓

와이드(xl 이상)에선 패널을 일반 flex child 로 고정해서 항상 표시. 좁은 화면은 기존
오버레이 슬라이드 유지 (가운데 영역 좁아지는 것 방지).

- page.tsx: 우측 패널 컨테이너에 xl: 상태로 relative + 슬라이드 무력화
- ColumnDetailPanel: 컬럼 선택 안 한 상태일 때 "컬럼을 선택해주세요" 빈 상태 안내 UI 추가

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 13:09:08 +09:00
johngreen 2d39d17428 feat(테이블타입): 컬럼 그리드 DBeaver 식 탭 분리 — 컬럼 / 참조
ColumnGrid 의 "참조/설정" 컬럼이 두 가지 다른 역할 (entity/code/numbering 의 참조 대상 표시
vs 그 외 타입의 default_value 표시) 을 한 셀에 욱여넣고 있었음. text/number/date 행에선
대부분 — 빈 칸. 진단 결과는 architect 가 동의한 대로 컬럼 통째 제거가 답.

DBeaver 의 Columns / Foreign Keys 탭 분리 패턴 차용:
- 컬럼 탭 (기본 활성) — 컬럼 본연의 속성만 (라벨 / 타입 / PK NN IDX UQ / ⋯). 참조/설정 컬럼
  통째 제거, grid 6→5 컬럼으로 슬림화. 라벨 셀 폭 1fr 로 확보
- 참조 탭 — entity / code / category / numbering 컬럼만 모아 표 형태로. 종류별 그룹 헤더
  + (소스 컬럼 / 참조 종류 / 참조 대상) 3컬럼 그리드. code 행에서 누락되어있던 코드 그룹
  표시도 같이 채움

ReferenceListView 신규 컴포넌트로 분리. 사용자가 행 클릭하면 우측 상세 패널 슬라이드 in
하는 기존 동작은 양 탭 모두 동일.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 19:21:55 +09:00
hjjeong 067193efa9 fix(배치관리): 대시보드 NaN 제거 + 24시간 차트 더미데이터 → 실데이터
- 백엔드: getBatchManagementGlobalSparklineData 쿼리 추가 (generate_series 로
  24개 슬롯 고정, 회사 전체 배치 LEFT JOIN 집계)
- 백엔드: GET /api/batch-management/sparkline 엔드포인트 추가
- 프론트: BatchStats/SparklineData 타입을 백엔드 mapper 의 snake_case 응답키와
  일치시킴 (today_count, today_failed_count, hour_slot, success_count, ...).
  키 미스매치로 stats 카드가 NaN 으로 표시되던 버그 해소
- 프론트: GlobalSparkline 컴포넌트의 Math.random() 더미 막대를 실데이터 prop 으로
  교체. row-level Sparkline 도 동일 키 정렬로 정상 렌더되도록 수정

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 09:59:52 +09:00
DDD1542 318cac4f68 Merge remote-tracking branch 'origin/main' into gbpark-node
Build & Deploy to K8s / build-and-deploy (push) Successful in 8m44s
2026-05-19 21:31:11 +09:00
DDD1542 2f398ae0b3 chore: 제어모드 IDE 작업 + v2/legacy 레지스트리 컴포넌트 폐기
- 제어모드 IDE: ControlCardPanel, control/ide/* (Canvas/LeftRail/RightRail/PanZoomStage/V3RuleNode 등), schemas, lib/api/control
- 레지스트리 정리: aggregation-widget, status-count, section-card/paper, table-list(legacy/v2), tabs-widget 폐기 → table/_shared/ 로 통합
- InvLegacyButtonConfigPanel cp 마이그레이션
- canonical data view cleanup 후속 노트
2026-05-19 21:31:03 +09:00
johngreen 904fdd33e7 feat(테이블타입): 컬럼 단건 DROP + CreateTableModal flex 레이아웃 수정 (#22)
Build & Deploy to K8s / build-and-deploy (push) Successful in 13m20s
johngreen → main: 컬럼 단건 DROP 기능 + CreateTableModal 다이얼로그 flex 레이아웃 수정
2026-05-19 06:02:51 +00:00
johngreen f73e468f66 feat(테이블타입): 컬럼 단건 DROP 기능 — ColumnGrid ⋯ 메뉴에 "컬럼 삭제" 추가
- DdlService.dropColumn: ALTER TABLE ... DROP COLUMN (CASCADE 미사용 → FK 참조 시 Postgres 거부, DBeaver 동일)
- 시스템 테이블 / 예약 컬럼(id/created_date/updated_date/company_code/writer) 보호
- 같은 트랜잭션에서 table_type_columns / column_labels 메타 청소 + ddl_execution_log 기록
- DdlController: DELETE /api/ddl/tables/{table}/columns/{column} (SUPER_ADMIN 전용)
- ddlApi.dropColumn 헬퍼
- ColumnGrid: ... 버튼을 DropdownMenu 로 교체, "컬럼 삭제" destructive 메뉴 아이템
- page.tsx: 컬럼 삭제 확인 다이얼로그 + 핸들러, FK 거부 시 토스트로 안내

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 14:49:07 +09:00
hjjeong 8a10edd8e1 fix(배치관리): DB 커넥션 변경 시 테이블 목록이 안 바뀌는 버그
- 외부 DB id 비교를 strict === 에서 toString() 기반 string 비교로 변경 — number/string 어느 쪽으로 오든 매칭. find 실패로 toConnection=null 되면 auto-select useEffect 가 "내부 DB" 로 강제 복귀시키던 문제 해소
- 연결 변경 시 toTables/fromTables 즉시 초기화 — fetch 실패해도 직전 DB 의 테이블이 잔존하지 않도록
- 배치 파이프라인 / 외부커넥션 멀티 DB 작업 핸드오프 노트 함께 추가

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 13:23:24 +09:00
hjjeong 947b31eff5 Merge remote-tracking branch 'origin/main' into hjjeong 2026-05-19 11:10:04 +09:00
johngreen 75f6883497 fix(테이블타입): 헤더 인라인 편집을 명시적 연필 아이콘 진입으로 + 컬럼명 폰트 축소
Build & Deploy to K8s / build-and-deploy (push) Successful in 16m6s
1) 테이블 헤더 (표시명/설명) 편집 진입 방식 변경
   - 기존: 텍스트 div 자체가 role="button" + onClick 이라 무심코 클릭 시 input 으로 전환
   - 변경: 텍스트는 단순 span, 옆에 작은 Pencil 아이콘 버튼 추가. 그 버튼 클릭해야 편집 모드 진입.
   - 연필 아이콘은 평소 muted-foreground/50 톤, hover 시 진해짐 (group-hover 의존 X — Tailwind variant 캐시 회피).
   - 편집 모드 동작 (Enter / Esc / blur 커밋) 은 그대로.

2) ColumnGrid: 컬럼 라벨 text-sm → text-xs (14px → 12px)
   - 가운데 본문 컬럼 행이 너무 커보이던 문제. 좌측 list 폰트(이전 commit) 와 비례 맞춤.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 08:06:07 +09:00
johngreen d306ac2865 fix(테이블타입): dropdown key 중복 + hook 순서 + 탭바 outline + 좌측 list 폰트 사이즈
Build & Deploy to K8s / build-and-deploy (push) Successful in 6m43s
오늘 시리즈 후속 UX 다듬기 + 회귀 fix:

1) ColumnDetailPanel: dropdown key 중복 방어
   - codeInfoOptions 에 placeholder "none" + 데이터 "none" 중복 시 React 가 'two children
     with the same key, none' 으로 거부 → filter 로 사전 제거.
   - refTableOpts 도 referenceTableOptions/tables 어디서든 중복 들어오면 같은 증상 →
     Set 기반 dedupe.

2) ColumnDetailPanel: hook 순서 위반 수정
   - 기존 'if (!column) return null' 이 useMemo(refTableOpts) 앞에 있어서
     column null/존재 케이스마다 hook 호출 수가 달라짐 (Rules of Hooks 위반).
     overlay 패턴 도입 후 column null 케이스가 자주 들어오면서 드러남.
   - early return 을 모든 hook 뒤로 이동.

3) v5-layout.css 탭바: Chrome 식 outline 스타일
   - 비활성 탭도 각자 outline 보이게 (border:1px solid var(--v5-border))로 카드처럼 분리.
   - 활성 탭은 border + surface-hover 배경 + 위쪽 primary 1px inset 강조선.
   - 위 모서리 rounded, margin-bottom:-1px 로 탭바 하단 border 와 seamless 연결.

4) 좌측 테이블 list 폰트 사이즈 축소
   - 한글명 16px → 13px, 영문명 12px → 10.5px, 행 padding 7px → 6px.
   - 280px 좁은 패널에 맞는 컴팩트 비율로 v5 컨벤션 정렬.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 22:42:45 +09:00
johngreen 78c5e3e358 fix(테이블타입): 좌측 일괄선택 영역 layout shift + 우측 디테일 패널 overlay/slide-in 으로 전환
Build & Deploy to K8s / build-and-deploy (push) Successful in 6m53s
기존 UX 이슈 3가지:
- 좌측 테이블 목록에서 체크박스 첫 선택 시 삭제 버튼이 등장하면서
  헤더 영역 높이가 변해 아래 리스트가 살짝 밀림 (layout shift).
- 우측 디테일 패널이 conditional render 라 컬럼 클릭 시 가운데 본문이
  380px 만큼 좁아져 컬럼명/라벨이 truncate 되며 "찌그러진" 느낌.
- 닫는 방법이 X 버튼뿐이라 토글 직관성 부족.

변경:
- 좌측 헤더 영역에 min-h-9 고정 — 삭제 버튼 등장해도 높이 고정, 리스트 안 흔들림.
- 우측 디테일 패널을 overlay 로 전환: absolute right-0 z-20 + shadow-2xl.
  transition-transform + translate-x-{0|full} 로 300ms ease-out slide-in/out.
  pointer-events-none 으로 닫혀있을 때 클릭 차단.
- 가운데 본문 width 변동 0 — 컬럼 클릭해도 안 좁아짐.
- 컬럼 토글: 같은 컬럼 재클릭 시 디테일 패널 닫힘. X 버튼/외부 트리거도 그대로 동작.

invyone admin 다른 화면들과의 일관성보다 가운데 본문 공간 보존이 우선이라
overlay 패턴 채택. 다른 화면(screenMngList, deptMngList)은 detail 영역이
처음부터 펼쳐진 2-pane 구조라 별개.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 20:59:53 +09:00
DDD1542 34060d9534 Merge branch 'gbpark-node'
Build & Deploy to K8s / build-and-deploy (push) Successful in 9m32s
2026-05-15 16:55:31 +09:00
DDD1542 2348800e68 refactor(common-code): 마스터-디테일 재설계 — code_info(그룹) + code_detail(재귀 트리)
Build & Deploy to K8s / build-and-deploy (push) Successful in 9m22s
카테고리/캐스케이딩 시스템 (B/C/D) 전부 폐기:
- BE: mapper/Service/Controller 9세트 삭제 (cascading*, categoryTree, tableCategoryValue, categoryValueCascading, codeMerge)
- FE: 페이지 3 + API 8 + hooks 2 + 폐기 컴포넌트 6 삭제, 14곳 의존성 정리
- DB: 12 테이블 DROP, TABLE_TYPE_COLUMNS.CODE_CATEGORY → CODE_INFO rename

신설 commonCode 마스터-디테일:
- code_info: 1레벨 그룹 마스터
- code_detail: 2~∞ depth 재귀 트리 (parent_detail_id self-FK, depth 자동 계산)
- API: /api/common-codes/{info,detail}
- CodeCategoryFormModal/Panel → CodeInfoFormModal/Panel rename
- code_category 컬럼명 전부 code_info 로 치환 (mapper/Java/FE)
- 옛 commonCode API URL (/categories/...) → getCodeOptions 어댑터 + /detail?code_info=... 전환

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 16:50:50 +09:00
hjjeong d61777ab5f fix(admin): 외부커넥션 mapper varchar 캐스팅 + 외부커넥션/배치관리 UI 정돈
- mapper/externalDbConnection.xml: WHERE ID = #{id} 5곳 + ID != #{exclude_id} 1곳에 ::varchar 캐스팅 추가
  (EXTERNAL_DB_CONNECTIONS.ID 가 V001 마이그레이션으로 VARCHAR 인데 long 바인딩되어 character varying = bigint 비교 불가로 500 발생하던 것을 해결)
- exconList: 페이지 overflow-hidden + Tabs/TabsContent 가 flex 컨테이너, ResponsiveDataView scrollContainer 활성화로 테이블 안에서만 sticky header + 자체 스크롤
- exconList/RestApiConnectionList: text-3xl→text-lg/text-sm→text-xs/h-10→h-8 등 컴팩트 폰트로 통일 (배치관리/플로우관리와 톤 매칭)
- RestApiConnectionList: Table divClassName 으로 wrapper 자체에 스크롤 위임 + sticky TableHeader 적용
- ResponsiveDataView: compact 모드일 때 폰트/셀패딩/카드 폰트도 함께 축소, scrollContainer 모드에서 @3xl:block 이 flex 를 덮어쓰던 우선순위 충돌 해결, sticky header 알파 제거
- batchmngList: Pagination 컴포넌트 적용 (RPS batchmngList 참고, 페이지당 10/20/50/100 선택), 컨테이너를 h-full min-h-0 overflow-hidden + 리스트만 자체 스크롤로 변경

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 16:38:23 +09:00
johngreen 824a3100ce security(멀티테넌시): 관리 plane vs 테넌트 plane 격리 + 부서관리 후속
이번 PR 은 invyone 멀티테넌시 SaaS 의 "관리 plane vs 테넌트 plane" 격리를
4 영역(PR #A~D) 에서 강화하고, 별도로 진행 중이던 부서관리 후속 작업을 포함한다.

# 보안 (plane 격리)

PR #A — controller/CompanyManagementController 인증 누락 패치
  /api/company-management/* 가 JWT/role/host 체크 없이 외부에서 누구나 회사 삭제
  + 디스크 통계 호출 가능했던 critical 누수 막음. SuperAdminGuard.enforce() 적용.

PR #C — cross-tenant 컨트롤러 호스트 격리 + 감사 로그
  CrossTenantContext.requireManagementHost() 헬퍼 추가, 5 컨트롤러
  (CrossTenantContext/Controller/UserController/RoleController/DeptController) 모두
  테넌트 호스트에서 호출 시 403. CompanyAuditLogService 에 cross-tenant write 4종
  (USER_CREATE/DELETE, PW_RESET, ROLE_UPDATE) audit action 추가.
  SuperAdminGuard.isTenantHost 가시성 public static 으로 승격.

PR #B — 프론트 솔루션 전용 admin 페이지 가드
  admin/* 페이지 전수 분류 결과 솔루션 전용 3건 식별:
  subdomainList / companyList / audit-log. 각 페이지에 isManagementHost
  useEffect 가드 + redirect 추가. 사이드바도 같이 숨김.

PR #D — MENU_INFO.IS_SOLUTION_ONLY 컬럼 + DB-driven 메뉴 필터
  V023 마이그레이션으로 컬럼 추가 + 솔루션 메뉴 3개 마킹.
  admin.xml selectUserMenuList 에 호스트 기반 필터 추가, AdminController.getUserMenus
  가 Host 헤더로 is_management_host 결정. 프론트 MANAGEMENT_ONLY_MENU_URLS
  하드코딩 set 폐기 (DB 가 대신함). 페이지 자체 가드는 defense in depth 로 유지.
  StartupSchemaMigrator 에 V023 등록되어 모든 테넌트 DB 부팅 시 자동 적용.

# 부서관리 후속 (이전 PR #18/#19 follow-up)

DepartmentController/Service + frontend deptMngList/department.ts 의 추가 작업분.
이번 격리 작업과 무관하지만 같이 정리.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 10:59:15 +09:00
DDD1542 387a5c2bd7 Merge remote-tracking branch 'origin/main' into gbpark-node
Build & Deploy to K8s / build-and-deploy (push) Successful in 11m34s
2026-05-14 17:42:17 +09:00
DDD1542 3883031c0b feat(studio): Phase G — KPI stats / chart / cardList / groupedTable + canonical container tabs
INV Studio 데이터 뷰 시리즈. 솔루션 개발 단계라 backward-compat alias 없이 깔끔하게.

Backend:
- TableManagementController + Service: /aggregate, /aggregate-group, /select-rows endpoint 추가
  sanitize + hasColumn 검증 + buildAggregateWhere 공유 헬퍼

Frontend canonical view components (신규):
- stats: DB-first KPI editor (CPSegment 메타 chip, 컬럼 dropdown, 디자인 모드 debounce 350ms preview)
- chart: recharts (bar / horizontalBar / line / donut)
- card-list: title/subtitles/metrics 카드 카탈로그 (list / grid 레이아웃)
- grouped-table: 클라이언트 측 groupBy + 그룹 헤더 row

Canonical container (Phase G.2 / G.2.5 / G.2.6):
- containerType='tabs' 활성 탭만 mount, ChildSlot 으로 자식 렌더
- ScreenDesigner.handleComponentDrop 가 canonical container tabs 도 인식
- 우측 V2PropertiesPanel 4-way 분기: tab child / panel child / selected / empty
  nested path update + saveToHistory, delete handler 동기화

Shared utilities:
- useDbColumns hook (모듈 캐시), ColumnPicker (CPSelect 기반)
- OptionFilterRow 자연어 카드 형식 (컬럼 dropdown / 조건 select / 값 입력)
- _shared/use-table-rows.ts (cardList + groupedTable 공용 fetch)
- IconPicker: 한글 키워드 80+ alias, 휠 스크롤 fix, 360px 상한, 결과 80→300

stats DB-first UX (Phase G.4.x):
- DB / 정적 모드 이분법 제거 — 항상 dataSource 시작
- collapsed: 라벨 input + KpiMetaSegment chip (테이블 · 집계 · 컬럼 · 필터수)
- expanded: 데이터 / 필터 / 외형 / 고급 flat CP rows
- useSlideToggle hook 으로 펼침/닫힘 양방향 애니메이션
- 변화량 (delta) 수동 입력 UI 제거 — 향후 DB 자동 계산 영역
- 카드 fetch state 명시: loading / error / 대기 중 / 테이블 미설정

기타:
- ScreenDesigner.tsx → InvyoneStudio.tsx rename (활성 빌더 파일)
- 모든 hardcoded #6c5ce7 fallback 제거, hsl(var(--primary)) 토큰만 사용 (light/dark/테마 자동 적응)
- StatsDefinition default_config 도 DB-first placeholder (value: 0 박지 않음)

Docs:
- notes/gbpark/2026-05-14-studio-data-view-roadmap.md (G.0 ~ G.4.2 진행 기록)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 17:41:50 +09:00
johngreen 4f13d2e440 fix(부서관리): 보안 + 운영 데이터 버그 8건 (PR #18/#19 후속)
- security: syncManagers 가 user_id 의 회사 격리·실존 검증 (cross-tenant injection 차단)
- bug: base_date 필터에 START_DATE IS NULL 조건 추가 — 옛날 데이터가 기준일 켜자마자 사라지는 문제 해결
- bug: PUT partial update — 매니저 키 없으면 sync skip (단일 필드 수정 시 매니저 보존)
- bug: 페이지 로드 시 단일 컬럼 backfill — PR #19 이전 데이터가 chip UI 에서 사라지는 문제 해결
- validation: Controller base_date YYYY-MM-DD regex (잘못된 형식 시 400)
- validation: 프론트 handleSave start_date > end_date 체크
- robustness: parseManagersJson 64KB max + log.warn (catch silent swallow 제거)
- ops: StartupSchemaMigrator 실패 테넌트 DB 명단 부팅 종료 시 log.error 집계
2026-05-14 17:25:31 +09:00
johngreen c350ebe86a feat(부서관리): 다중 결재/부서 관리자 + 조직장 (DEPT_MANAGERS 매핑 테이블)
- 마이그레이션 V022/RUN_088: DEPT_MANAGERS 신규 (role: approval/dept/org_leader, PK 3-tuple, FK CASCADE)
- StartupSchemaMigrator 에 V022 idempotent CREATE 추가 → 테넌트 DB 자동 동기화
- mapper.xml: SELECT 에 3 json_agg ::TEXT 컬럼 추가, insertDeptManagers + deleteDeptManagersByDeptAndRole 신규
- service: parseManagersJson + syncManagers (delete-all + insert-all, 최대 10명, 역할 한글 메시지)
- frontend: types 3 필드, DeptDetailDraft 확장, ManagerChipsField (chip+UserSearchModal 재사용), 조직장 Row 신규

기존 DEPT_INFO.APPROVAL_MANAGER / DEPT_MANAGER 단일 컬럼은 호환을 위해 유지.
2026-05-14 15:19:50 +09:00
johngreen ecad2915ce feat(부서관리): 기준일 필터 + 시작일/종료일 UI 노출
- 백엔드: selectDepartments 에 base_date <if> 블록 추가 (start_date <= base_date AND (end_date IS NULL OR end_date >= base_date))
- 서비스/컨트롤러에 3-arg overload 와 @RequestParam("base_date") 추가
- 프론트: 사용기간 RadioGroup + 시작일/종료일 Row 의 {false &&} hide 제거
- loadDepartments 가 periodMode === "date" 일 때 baseDate 를 API 에 전달
2026-05-14 14:12:59 +09:00
johngreen ca241c017d feat(테이블타입): 헤더 표시명/설명 inline click-to-edit (Google Docs 패턴)
기존 "헤딩 + 옆에 input box 2개 + 저장 버튼" 구조의 UX 문제:
- 같은 값을 헤딩과 input 양쪽에서 중복 표시
- 항상 폼처럼 보여 컬럼 그리드 시선을 뺏음
- 라벨만 바꾸려는 의도가 "전체 설정 저장" 에 묶여 흐려짐

변경: 헤딩 텍스트 자체를 클릭하면 그 자리에서 input 으로 변신
(Google Docs 문서 제목 / Notion 패턴).
- blur 또는 Enter → PUT /label 즉시 저장
- Esc → 취소
- hover 시 muted/60 배경, cursor: text, title tooltip 으로 affordance
- 설명이 비어 있으면 "+ 설명 추가" 힌트 표시
- 표시명은 비울 수 없게 가드 (toast.error)

"전체 설정 저장" → "컬럼 설정 저장" 으로 책임 분리:
- 헤더 라벨/설명: inline 즉시 저장
- 컬럼 input_type/web_type/detail_settings 등 일괄: 버튼

키보드 접근성:
- Tab 으로 헤딩에 focus → Enter/Space 로 편집 모드
- Esc 로 취소, Enter 로 커밋 (blur 트리거)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 10:52:29 +09:00
johngreen 0365b743f5 fix(테이블타입): constraints API PgArray 디코딩 + primary_key key 매칭
테이블 타입 관리 페이지에서 PK/IDX 토글 후 UI 상태가 갱신되지 않던 버그.

1. Backend: TableManagementService.parseColumnArray
   - PostgreSQL ARRAY_AGG 가 java.sql.Array (PgArray) 로 오는데
     List/String 만 처리해서 항상 List.of() 반환 → columns 빈 배열
   - 조치: java.sql.Array 분기 추가, arr.getArray() → Object[] → List<String>

2. Frontend: loadConstraints
   - 백엔드는 result.put("primary_key", ...) 로 snake_case 반환
   - 프론트가 data.primaryKey 로 camelCase 로 읽어 undefined → 항상 빈 PK
   - 조치: data.primary_key 로 통일

이전 PR (#14) 의 IDX payload 수정과 합쳐, PK/IDX 토글이 API 호출/DB 적용/
UI 반영까지 한 사이클로 동작.

검증:
- gradle compileJava BUILD SUCCESSFUL
- solution.invyone.com 에서 IDX 토글 양방향 확인 예정

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 08:32:48 +09:00
johngreen 44f5b134a5 fix(테이블타입): IDX/label API payload key snake_case 로 통일
테이블 타입 관리 페이지에서 IDX 토글 / 테이블 라벨 저장이 400 에러로
조용히 실패하던 버그. 백엔드는 body.get("column_name") / get("index_type")
/ get("display_name") 등 snake_case 로 읽는데 프론트가 camelCase 로 보내고
있었음 (CLAUDE.md Map key snake_case 컨벤션 위반).

- POST /table-management/tables/:t/indexes
  { columnName, indexType, action } → { column_name, index_type, action }
- PUT /table-management/tables/:t/label
  { displayName } → { display_name }

PK 는 다이얼로그 확인 흐름, NN/UQ 는 key 가 맞아 영향 없음.
SUPER_ADMIN 으로 테스트 시 IDX 만 안 되던 증상 일치.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 08:18:09 +09:00
hjjeong f53307a72e Merge remote-tracking branch 'origin/main' into hjjeong 2026-05-13 17:20:59 +09:00
hjjeong cbf94dc90f feat(batch): TO DB 자동 선택 (internal) + Select 컴포넌트 controlled 화
새 배치 (REST API → DB) 진입 시 매번 사용자가 "데이터베이스 커넥션 선택" 셀렉트에서
"내부 DB" 를 직접 골라야 했음. 대부분의 배치가 internal 적재라 디폴트 채움이 자연스러움.

1) TO DB 자동 선택
   useEffect 로 batchType === "restapi-to-db" + connections 로드 + toConnection 비어있음
   조건 만족 시 handleToConnectionChange("internal") 자동 호출. 사용자가 외부 DB 로 변경하면
   toConnection != null 이 되어 더 이상 자동 동작 안 함.

2) Select controlled 화
   DB 커넥션/테이블 Select 가 value prop 없는 uncontrolled 상태였음.
   setToConnection/setToTable state 가 바뀌어도 Select UI 가 placeholder 그대로 →
   programmatic 자동 선택이 시각적으로 반영 안 됨.
   → value prop 추가:
     - DB 커넥션: toConnection.type === "internal" ? "internal" : String(toConnection.id)
     - 테이블: toTable

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 17:18:09 +09:00
hjjeong 54a8f97f78 fix(batch): 미리보기 → 매핑 카드 표시 흐름 정상화 + 매핑 카드 컴팩트화
배치 생성 흐름 검증 중 발견된 4가지 이슈 일괄 정정.

1) BatchManagementService.previewRestApiData — camelCase 키 명시 remap
   직전 커밋(b752de23)에서 convertCamelToSnake() 호출 추가했지만 그 함수의 실제 구현이
   batch_configs 전용 snake→snake remap 이라 사실상 no-op. 프론트의 apiUrl 등 camelCase
   가 변환되지 않아 isBlank(api_url)=true → 400.
   → previewRestApiData 진입부에 직접 remap (apiUrl/apiKey/requestBody/dataArrayPath/
     paramType/paramName/paramValue/paramSource/authServiceName 9개 키).

2) batchManagement.ts.previewRestApiData — 응답 totalCount 정규화
   백엔드는 total_count (snake_case) 로 응답하는데 프론트는 result.totalCount 로 읽음.
   토스트가 "2개 필드, undefined개 레코드" 로 표시됨.
   → 응답 normalize: total_count ?? totalCount ?? 0.

3) batch-management-new/page.tsx — root h-full overflow-y-auto
   페이지 root 가 overflow 처리가 없어 FROM/TO 카드 아래의 매핑 카드가 탭 컨테이너
   밖으로 잘려 사용자가 못 봄.
   → root div 에 h-full overflow-y-auto 추가.

4) RestApiToDbMappingCard — v5 컨벤션에 맞춘 컴팩트화
   다른 메뉴들과 톤 통일. CardHeader 패딩 축소, 폰트 size 일괄 다운,
   행 padding p-3 → p-2, Select/Input h-9 → h-7 text-xs, 순서 원형 h-6 → h-5,
   카드 내부 height 360 → 300px, 매핑 추가 버튼/삭제 버튼 컴팩트.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 16:32:41 +09:00
johngreen 8f92fb2368 refactor(테이블타입): 3-layer 분리 — DB 12개 유지, UI 8개 한정, widget variant
- input-type-mapping.ts: BaseInputType 10개 → UserSelectableInputType 8개 (박창현 image 2). vexplor_rps INPUT_TYPE_DETAIL_TYPES 포팅, select/checkbox/radio variant 를 code base 로 흡수
- input-types.ts: USER_SELECTABLE_INPUT_TYPE_ORDER/LABELS re-export (InputType 12개는 그대로)
- getDetailType.ts (신규): getWidgetVariants / getDefaultWidgetVariant helper
- 드롭다운 호출처 7개 8개 제한: ColumnDetailPanel, AddColumnModal, ColumnDefinitionTable, tableMngList/page.tsx, TableSettingModal, TypeOverviewStrip, types.ts
- ColumnDetailPanel: Legacy row 드롭다운 disabled + v5-glow-sm Alert 배너
- backward shim: BaseInputType / BASE_INPUT_TYPE_OPTIONS / getBaseInputType 등 V2/Properties/DetailSettingsPanel 호환

운영 DB 96.6% 가 이미 8개 안 (V0, 35,316 row). DB zero touch, mapper 5곳 보호.

spec: .omc/specs/deep-dive-table-type-storage-ui-separation.md (v3.2)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 14:43:26 +09:00
hjjeong 6fcb101f59 style(batch): API 파라미터 설정을 collapsible 로 변경 — 기본 접힘
이전: 페이지 진입 시 항상 펼쳐져 큰 공간 차지 → 매핑 영역이 화면 밖으로 밀림
신규: <details> + summary 로 기본 접힘. 클릭 시만 펼침. 토글 아이콘 함께.

특정 사용자/조건으로 API 조회할 때만 쓰는 옵션이라 기본 접힘이 자연스러움.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 14:34:26 +09:00
hjjeong d8f606ab00 style(batch): 기본 정보를 모드 토글과 한 행으로 통합 — 하단 매핑 영역 확보
사용자 보고: 기본 정보 카드가 별도 영역을 차지해서 한 화면에 매핑 영역(중요) 까지
스크롤 없이 안 들어옴. vexplor_rps 스크린샷처럼 상단을 한 줄로 컴팩트하게.

변경:
- 배치 타입 토글 grid + 기본 정보 카드를 단일 grid 로 통합
- xl(1280px+): grid-cols-[minmax(28rem,1.4fr)_1fr_1fr_1.5fr]
  → 모드토글그룹 + 배치명 + 스케줄 + 설명 한 줄
- xl 미만: grid-cols-1 stack
- 모드 토글 카드 컴팩트화: p-3 / h-9 아이콘 / text-[10px] description
- 설명: Textarea → Input (단일 행) 로 축소. 긴 설명은 어차피 메모 용도라 한 줄이면 충분.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 14:20:00 +09:00
hjjeong e8f517ed18 fix(batch): batch-management-new 도 풀폭 적용 — 이전 풀폭 커밋에서 누락된 파일
0bba1836 에서 batchmngList/page.tsx 와 edit/[id]/page.tsx 두 곳만 max-w 제거했고
batch-management-new 의 root mx-auto max-w-5xl 가 남아있어 가운데 1024px 컬럼으로
본문이 박혀있었음. 좌우 패널이 sparse 하게 보이는 원인.

mx-auto max-w-5xl → w-full

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 14:16:32 +09:00
hjjeong d02bc38f6c style(batch): FROM 카드 행 그룹화 + 컴팩트 폰트로 sparse 레이아웃 정리
이전: API 서버 URL / 인증 토큰 / 엔드포인트 / 메서드 / 데이터 배열 경로가 각각 단독 행
신규: 의미 단위로 그룹화 + 컴팩트화

- API 서버 URL (3fr) + HTTP 메서드 (1fr) 한 행
- 엔드포인트 + 데이터 배열 경로 50:50 한 행
- 인증 토큰: 라디오 → 세그먼티드 토글 버튼 그룹 + 입력을 한 행에 압축
- Label text-xs / Input h-9 text-sm 로 컴팩트 통일

vexplor_rps batch-management-new (L1417 부근) 의 컴팩트 레이아웃 패턴 참고.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 14:01:40 +09:00
hjjeong 0c9e22a679 feat(batch): 등록 REST API 연결 자동 호출 + 응답 필드 추출
vexplor_rps batch-management-new 의 applyRegisteredRestApi 핵심 흐름을 INVYONE 에
이식. 등록된 REST API 연결 선택 시:
  1. 폼(URL/엔드포인트/메서드/Body/인증 토큰) 자동 채움
  2. ExternalRestApiConnectionAPI.testConnectionById 로 자동 API 호출
  3. 응답 안에서 배열 자동 탐색 (depth ≤ 4)
  4. fromApiFields / fromApiData 채움 → 매핑 드롭다운에 필드 즉시 노출

UI: FROM 카드 최상단에 "🔗 등록된 연결" 셀렉터 추가.
로딩 중에는 셀렉터 라벨 옆에 스피너, 에러 시 destructive 톤 메시지.

vexplor_rps 와 다르게 제외한 부분:
  - Amaranth/Wehago 회사 전용 프리셋 (AMARANTH_TARGET_PRESETS) — 6종 ERP 동기화
    배치 전용 하드코딩이라 INVYONE 일반 사용자에겐 의미 없음. Phase 6 후속에서
    검토할 별도 영역.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 13:54:11 +09:00
hjjeong 570b3267ab feat(batch): batch-management-new 에 conditional 매핑 추가 (Phase 2 일관성)
vexplor_rps 와 비교 시 batch-management-new 에 조건 변환 모드가 빠져있어
batchmngList/edit/[id] 와 같은 패턴으로 정렬.

- MappingItem.sourceType 에 'conditional' 추가 + conditionalConfig 필드
- ConditionalConfig / ConditionalEditor / emptyConditionalConfig import
- Select 옵션에 "조건 변환" 추가
- conditional 일 때 ConditionalEditor 렌더
- validMappings 필터: conditional 은 평가 필드(apiField) + 룰 또는 default 필요
- apiMappings 변환: mapping_type='conditional' + mapping_config 객체 전송

note: 이 페이지는 신규 생성 전용이라 load(편집 복원) 로직은 없음.
편집은 batchmngList/edit/[id]/page.tsx 가 처리.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 12:29:21 +09:00
hjjeong 0bba1836fb fix(batch): 빈 화면 원인이던 openTab 키명 정정 + 본문 풀폭 적용
문제 1: 배치 수정/생성 탭이 빈 화면
- Tab type 정의는 admin_url (snake_case) 인데 호출자가 adminUrl (camelCase)
  로 전달 → TabPageRenderer 매칭 실패 → null 렌더 → 빈 화면
- 콘솔 경고: "[TabPageRenderer] 렌더링 불가 — 매칭 조건 없음" (TabContent.tsx:268)
- 11곳 (4파일) 의 adminUrl → admin_url 정정

문제 2: 리스트/편집 본문이 좁은 가운데 컬럼에 박혀있음
- batchmngList/page.tsx: max-w-[720px] → w-full
- batchmngList/edit/[id]/page.tsx: max-w-[640px] → w-full

해당 파일:
- batchmngList/page.tsx
- batchmngList/edit/[id]/page.tsx
- batchmngList/create/page.tsx
- batch-management-new/page.tsx

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 12:15:51 +09:00
hjjeong f31a7f852f feat(batch): Phase 2 — 프런트 ConditionalEditor + 조건 변환 매핑 UI
- batch.ts: ConditionalRule / ConditionalConfig 타입 추가,
  BatchMapping 에 mapping_type ('direct'|'fixed'|'conditional') + mapping_config 필드
- ConditionalEditor.tsx: 평가 필드 선택 + when/then 룰 add/remove + default 입력 컴포넌트.
  emptyConditionalConfig / normalizeConditionalConfig 헬퍼 동봉. vexplor_rps 1:1 포팅
- batchmngList/edit/[id]/page.tsx:
  · MappingItem.sourceType 에 'conditional' 추가 + conditionalConfig 필드
  · 소스타입 Select 에 "조건 변환" 옵션
  · Load: mapping_type=conditional 인 매핑은 mapping_config JSON 파싱 후 복원
  · Save: sourceType=conditional 매핑은 mapping_config 객체와 함께 전송

저장된 룰: {"rules":[{"when":"1","then":"Y"}],"default":"?"} 형태.
Phase 1 의 BatchService 직렬화 경로로 JSONB 에 저장된다.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 10:25:08 +09:00
hjjeong dce665caea feat(batch): Phase 0 — batch_mappings CRUD path + 실행 로그/관리 SQL 정리
- BatchService: insertBatch/updateBatch 가 body.mappings 받아 replace-all 동기화,
  getBatchInfo 가 batch_mappings 리스트 attach (지금까지는 silently drop)
- batch.xml: getBatchMappingsByConfigId / insertBatchMapping / deleteBatchMappingsByConfigId 신규
- batchExecutionLog.xml / batchManagement.xml: batch_config_id 에 ::varchar 캐스팅,
  오타 'batch_execution_log' → 'batch_execution_logs' 정정
- batchmngList/page.tsx: 같은 batch ID 가 회사 간 중복될 때 React key 충돌 방지
- notes: vexplor_rps → INVYONE 배치 파이프라인 이식 분석 노트

vexplor_rps → INVYONE 파이프라인 이식의 Phase 0.
구체 분해는 notes/hjjeong/2026-05-12-batch-pipeline-current-state.md 참조.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 10:24:36 +09:00
DDD1542 4a8413000b Consolidate canonical input migration
Build & Deploy to K8s / build-and-deploy (push) Failing after 11m17s
Remove legacy v2 input/select and file/media runtimes, add canonical option/file loaders, and document Codex handoff.
2026-05-12 18:36:43 +09:00
hjjeong 081feff51f style(rolesList): 다른 메뉴 톤에 맞춰 사이즈/글씨 축소
본문 텍스트 text-sm → text-xs, 헤더 보조 텍스트 text-xs →
text-[11px], 카드 헤더 p-3 → p-2.5, 좌측 권한 목록 그리드
260px → 220px. Input/Select h-8 → h-7 (메인) / h-7 → h-6 (서브),
메뉴 트리 row py-2 → py-1.5, 트리 들여쓰기 level*20+12 →
level*16+10, chevron h-3.5 → h-3. 4분할 직원 카드 영역
clamp(220, 32vh, 320) → clamp(200, 28vh, 280).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 14:17:39 +09:00
DDD1542 90035dd5c6 feat(numbering): 채번 관리 페이지 통짜 작업대 디자인 적용
Build & Deploy to K8s / build-and-deploy (push) Failing after 11m4s
채번 = 독립 자원 (컬럼과 N:M 연결) 모델로 페이지 통째 리뉴얼.
카드 박스 폐기, 좌측 sidebar + 우측 통짜 main + ⌘K 명령 팔레트 구조.

- frontend/styles/v5-layout.css: v5-nrm-* 섹션 추가 (메뉴관리 v5-mm-* 동일 패턴)
- numberingRuleList/page.tsx: PageHead 안 쓰고 메뉴관리 스타일 단순 헤더 +
  사이드바 (검색·필터·섹션·list) + Hero·파이프라인·2-col split·sticky save bar +
  ⌘K 팔레트 (검색·프리셋·기존 채번)
- 기존 NumberingRuleDesigner/SequenceManagementPanel/CreateDialog 의 로직을
  page.tsx 내부 sub-component (PipelineBlock·PartInspector·UsageList·CommandPalette) 로 재구성
- mockup HTML 산출물 notes/gbpark/ 보관

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 12:06:28 +09:00
hjjeong 7315603f0f Merge branch 'main' into hjjeong
# Conflicts:
#	backend-spring/src/main/java/com/erp/migration/StartupSchemaMigrator.java
2026-05-12 11:42:17 +09:00
hjjeong 8bdc9a958f feat(favorites): 사용자별 메뉴 즐겨찾기
사이드바 leaf 메뉴 옆 별표로 토글하고, 사이드바 최상단 '즐겨찾기'
섹션에 등록된 메뉴를 모아 보여준다. 테넌트 DB 별로 격리.

- V020 + StartupSchemaMigrator: USER_MENU_FAVORITES (USER_ID,
  MENU_OBJID UNIQUE) 메타·테넌트 DB 동기 멱등 생성
- 백엔드: GET/POST /api/favorites/menus, DELETE
  /api/favorites/menus/{menuObjid} — FavoritesController/Service
  + mapper/favorites.xml (MENU_INFO JOIN)
- 프론트: favoritesAPI 클라이언트 + FavoritesContext (낙관적
  toggle/롤백) + (main)/layout 에 Provider 마운트
- AppLayout: leaf/sub-item 옆 Star 토글 (등록 시 primary 컬러,
  미등록 시 0.35 dimmed) + 사이드바 최상단 favorites 섹션. uiMenus
  의 leaf 평탄화 결과를 isFavorite 으로 필터링해 권한 필터/메뉴
  클릭 핸들러를 그대로 재사용

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 11:29:18 +09:00
johngreen 6a9fc06f0e feat(대무자): 프론트엔드 UI — UserFormModal 대무자 섹션 + ProfileModal 조회 + 결재 뱃지
- frontend/lib/api/substitute.ts: 7개 API 함수 (Record<string, any> 컨벤션)
- components/admin/SubstituteSection.tsx (신규): 관리자용 대무자 지정 섹션
  · 활성/예정 대무 관계 테이블, 사전 겹침 검증
  · v5 토큰 (--v5-surface-solid, --v5-glow-sm) 사용, blur 금지
- components/admin/UserFormModal.tsx: 수정 모드일 때 SubstituteSection 노출
- components/layout/MySubstituteView.tsx (신규): ProfileModal 용 read-only 조회
  · 내 대무자 + 내가 대무 중인 사람 양방향, D-day 카운트다운
- components/layout/ProfileModal.tsx: MySubstituteView 삽입
- app/(main)/approval/page.tsx: 대기함 행에 "대무 ← {원본 결재자}" 뱃지
  · currentUser.user_id !== line.approver_id 비교 (별도 타입 필드 X)
2026-05-12 08:07:15 +09:00
gbpark baffd6affb Merge origin/main into gbpark-node
Build & Deploy to K8s / build-and-deploy (push) Successful in 9m45s
부서관리 V1 슬림 스코프 + UX 리디자인, 25개 버그 일괄 수정, admin/부서관리
탭 라벨 fallback, Windows dev HMR 복원 흡수.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 21:51:34 +09:00