1665 Commits

Author SHA1 Message Date
hjjeong 8606f0aaa3 Merge remote-tracking branch 'origin/main' into hjjeong 2026-05-22 14:49:50 +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 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
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
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 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
hjjeong f53307a72e Merge remote-tracking branch 'origin/main' into hjjeong 2026-05-13 17:20:59 +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 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
gbpark 7bd08dcf9d refactor(components): consolidate canonical input cleanup 2026-05-13 02:38:29 +09:00
johngreen 3dbc2107d8 Merge fix(비번초기화): 키 불일치 + 입력값 무시 픽스
Build & Deploy to K8s / build-and-deploy (push) Failing after 11m15s
2026-05-12 19:28:02 +09:00
johngreen 3eeb0764bf fix(비번초기화): 키 불일치 + 입력값 무시 픽스
- Frontend: body 키를 snake_case (user_id/new_password) 로 변환
- Controller: new_password 도 추출해서 service 에 전달
- Service: 2-arg 오버로드 추가, newPassword 입력값 사용 (blank 일 때만 Welcome1! fallback), userId null/blank 시 IllegalArgumentException

증상: 사용자관리에서 비밀번호 초기화 modal 입력 → backend 가 user_id=null 로 SQL 실행 (0행) + newPassword 무시 후 항상 Welcome1! 로 덮어쓰기.
2026-05-12 19:27:44 +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 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
gbpark a5bbd1eb7c refactor(numbering-rule): NumberingRule → Input canonical 흡수 + 채번 관리 페이지 분리
- 옛 registry/numbering-rule, registry/v2-numbering-rule, V2NumberingRuleConfigPanel,
  NumberingRuleTemplate 폐기 — InvFieldConfigPanel + InputComponent 로 통합
- input 에 numbering-picker / select-pickers 추가, autonum 타입 흡수
- 채번 관리 전용 admin 페이지(systemMng/numberingRuleList) + CreateDialog +
  SequenceManagementPanel 신설
- backend NumberingRule controller/service/mapper 갱신 (시퀀스 관리 엔드포인트)
- input canonical 진행 노트 + 채번 관리 mockup 추가

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 21:42:13 +09:00
hjjeong 74ddc4936f Merge branch 'main' into hjjeong 2026-05-08 14:59:12 +09:00
johngreen 0e895a90fa feat(부서관리): V1 슬림 스코프 + 트리 컨텍스트 메뉴 UX 리디자인
백엔드:
- V018 soft-delete (deleted_at 컬럼) + 휴지통/복구 흐름
- V019 미사용 컬럼 cleanup (V1 슬림 스코프)
- DepartmentService.updateDepartment 에 parent_dept_code 사이클 가드
  (자기 자신/자손을 부모로 지정 시도 차단)
- DepartmentController, mapper 갱신

프론트:
- 부서관리 페이지(deptMngList) UX 리디자인
  - 트리 노드 ⋮ 컨텍스트 메뉴 (하위 추가, 다른 부서 아래로 이동, 정렬 4단계, 삭제)
  - 헤더 breadcrumb 으로 부서 위치 상시 표시
  - 폼의 상위부서 row 제거 (트리 ⋮ 로 진입점 일원화)
  - 빈 상태 placeholder + X 닫기 동작
  - 토글 버튼 토스 스타일 (아이콘 + 툴팁, 일정한 위치)
  - 부서유형 row 좁은 화면 가로 오버플로 fix
- DepartmentPicker 신규 재사용 컴포넌트 (자손 자동 exclude, 사이클 차단)
- 회사관리/프로비저닝 폼 개선 (Step1Basic, fields, CompanyTable, AdminPageRenderer)
- companyList/[companyCode]/departments 구버전 페이지 삭제

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 08:34:23 +09:00
DDD1542 59f5cf22f0 Merge remote-tracking branch 'origin/gbpark-node' into gbpark-node
Build & Deploy to K8s / build-and-deploy (push) Successful in 4m4s
2026-05-07 17:09:57 +09:00
DDD1542 c4631efbd2 중간저장 2026-05-07 17:06:26 +09:00
hjjeong b782bb298f merge: origin/gbpark-node → hjjeong (60 commits, 5 conflicts resolved)
충돌 해결 5개 파일:
- .gitignore: .envrc/.direnv (hjjeong direnv 셋업) + .omc/ (gbpark) 양쪽 보존
- docs/MULTI_TENANCY_ARCHITECTURE.md: *.localhost dev 분기 + *.invyone.com/solution.invyone.com 통합
- frontend/lib/api/client.ts: 1-b *.localhost:8081 dev + 1-c DEV_TENANT_HOST(nip.io):8083 + invyone.com 신 도메인
- frontend/lib/tenant/subdomain.ts: IPv4 차단 + *.invyone.com + DEV_TENANT_HOST + *.localhost 모두 처리
- frontend/app/(auth)/login/page.tsx: B안 채택 — buttons 항상 렌더, className 만 mounted 가드 (next-themes 표준 패턴)

검증:
- backend: ./gradlew compileJava 성공 (Java 21)
- frontend: 머지된 4개 파일 관련 타입 에러 0개

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 16:51:06 +09:00
gbpark e70267f738 feat: SCADA 데모 음성 인식 + 경고 버튼 디자인 통일
Build & Deploy to K8s / build-and-deploy (push) Failing after 1m14s
- 음성 인식 (scada-demo/js/voice.js) — 한국어 발화 → 키워드 매핑 → INVYONE_UI.select()
  · 사이드바 마이크 버튼 + transcript 라벨, 매칭 시 청록 펄스
  · Chrome/Edge HTTPS 환경 (운영 siflex.invyone.com OK)
- 경고시스템/다중경고 버튼을 음성 인식과 동일 톤
  · 🚨 emoji → SVG 삼각형 아이콘, voice-btn 패턴 (다크 솔리드 + 컬러 액센트)
  · 정적 (반짝 펄스 애니메이션 제거)
- client.ts stash pop conflict 정리 (DEV_TENANT_HOST + 도메인 정리 통합)
- ui.js 다중 경고 시연 wiring + scada 작업 노트 2건
- 기타 syncthing 보류분 batch (대시보드/레이아웃/로그인 layout 정리)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 05:39:43 +09:00
gbpark 3a0ab10ee6 Merge branch 'gbpark-node' of https://git.junggomoa.com/gbpark/invyone into gbpark-node
Build & Deploy to K8s / build-and-deploy (push) Successful in 4m51s
2026-05-03 01:29:07 +09:00
johngreen b999b425cb Merge pull request 'Sync main → gbpark-node: AI 모듈 JSONB 파싱 + audit-log fix' (#1) from main into gbpark-node
Build & Deploy to K8s / build-and-deploy (push) Successful in 4m31s
Reviewed-on: #1
2026-05-02 10:20:35 +00:00
johngreen 04cea72f33 fix(ai-modules): JSONB ::text 응답 자동 파싱 + workspace 카드 깨짐 수정
백엔드:
- 6개 AI Service (group/apiKey/provider/conversation/agent/scheduler) 가 응답 메서드에서
  `parseJsonField` 헬퍼로 JSONB(::text) 컬럼 (connectors / config / permissions /
  metadata / tool_calls / notification / tools) 을 String → Object 자동 변환.
- 모범 패턴 (`AuditLogService.processChanges`, `BusinessRuleService.parseJsonField`,
  `DataflowDiagramService.parseJsonbFields`) 동일하게 적용.
- model 의 String getter 는 그대로 유지 — `MultiAgentExecutionEngine` 등
  내부 LLM 호출 chain 영향 없음 (`getEntityById` 분리).
- 컨트롤러 시그니처 generic 만 변경 (return type Map).

프론트엔드:
- `safeArray<T>` / `safeObject<T>` 헬퍼 (`lib/utils.ts`) — 백엔드가 미파싱 String 으로
  올 때 graceful fallback. 빈 배열/객체 반환.
- `workspace/page.tsx` 멤버 카드:
  - `safeArray(member.connectors)` 적용 → `.map()` 폭발 차단.
  - 좁은 viewport 에서 한글 텍스트 한 글자씩 세로로 깨지던 문제 해결
    (`flex-wrap` + `truncate` + `whitespace-nowrap` + `max-w` + `title`).

그렘린 1000마리 폭격 + architect 자문으로 발견. workspace `Application error`,
`memberConnectors.map is not a function` 모두 해결.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 16:11:50 +09:00
johngreen 53f2638b82 fix(audit-log): URL 단/복수 + DB 컬럼명 + 응답 키 case 일괄 정정
- frontend `/audit-log` → 백엔드 `/audit-logs` (단/복수 mismatch)
- mapper auditLog.xml: `CREATED_DATE` → `CREATED_AT` (PostgreSQL 실제 컬럼명)
- AuditLogStats interface camelCase → snake_case (백엔드 응답 키 일치)

그렘린 카오스 테스트 + fetch hook 으로 발견. Application error / 500 / 404 모두 해결.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 16:11:26 +09:00
hjjeong 7d9ec39b5d feat(cross-tenant): SUPER_ADMIN 의 회사별 권한관리 WRITE (Phase 2)
Phase 1(사용자관리) 패턴을 권한관리에 동일 적용. 권한 그룹 CRUD,
멤버 토글, 메뉴 권한 토글 모두 회사 컨텍스트 임시 전환 후 처리.

신규 백엔드
- crosstenant/CrossTenantRoleController.java
  /api/admin/cross-tenant/roles/** — 8개 endpoint
  · POST       — 권한 그룹 생성 (body.company_code 필수)
  · PUT  /{id} — 권한 그룹 수정 (body.company_code 필수)
  · DELETE /{id}?company_code= — 삭제
  · GET  /{id}/workspace?company_code= — 그룹 + 멤버 + 메뉴 통합 로드
  · GET  /menus/all?company_code= — 회사 메뉴 트리 (권한 설정용)
  · POST   /{id}/members/{userId}?company_code= — 멤버 1명 추가
  · DELETE /{id}/members/{userId}?company_code= — 멤버 1명 제거
  · PATCH  /{id}/menu-permissions/{menuObjid} — 토글
  CrossTenantExecutor 재사용. 기존 RoleController 무수정 (회귀 0).

  중요: @RequestAttribute("user_id") 가 토큰 없을 때 missing 에러로 500
  떨어지는 문제 — required=false 로 가드까지 안전하게 도달하도록.

프론트
- lib/api/role.ts — 7개 메서드(create/update/delete/getWorkspace/
  getAllMenus/addSingleMember/removeSingleMember/toggleMenuPermission)에
  isCrossTenantMode() 분기 + companyCode 인자 추가
- RoleFormModal — update 시 editingRole.company_code 같이 전달
- RoleDeleteModal — delete 시 role.company_code 같이 전달
- rolesList/page.tsx — loadWorkspace / addSingleMember / removeSingleMember /
  toggleMenuPermission 호출 시 selectedRole.company_code 전달

검증 (curl, SUPER_ADMIN 토큰):
- 토큰 없음 → 403 super_admin_required
- POST 권한 그룹 (TEST02) → 201, /roles fan-out 에 by={TEST01:1, TEST02:1}
- DELETE → 200, fan-out by={TEST01:1} 로 복귀

미구현 (Phase 2 후속, 별도 작업):
- 일괄 멤버 추가/제거/diff (PUT/POST /members)
- 메뉴 권한 일괄 설정 (PUT /menu-permissions)
- 사용자별 권한 그룹 조회

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 18:45:55 +09:00
hjjeong 4d19c31440 feat(cross-tenant): 부서 endpoint + UserFormModal 회사-우선 reorder
직전 Phase 1 의 후속 폴리시.

신규 백엔드
- crosstenant/CrossTenantDeptController.java
  GET /api/admin/cross-tenant/departments?company_code=TEST02
  단일 모드 GET /admin/departments 와 응답 형태 동일. company_code query param
  으로 명시된 회사 DB 컨텍스트로 임시 전환해서 부서 트리 반환.
  버그 수정: 메타 DB DEPT_INFO 시드 (qnc/COMPANY_7 등 다른 회사 부서) 가
  TEST02 선택 시에도 dropdown 에 섞여 보이던 문제 해결.

프론트
- lib/api/user.ts — getDepartmentList(companyCode) 가 isCrossTenantMode() 면
  /admin/cross-tenant/departments?company_code= 호출.
  cross-tenant 모드 + companyCode 미지정 → 빈 배열 반환 (회사 안 골랐는데
  메타 부서 보여주는 것 방지).

UserFormModal
- 회사 dropdown 을 폼 가장 위로 이동 — 사용자 ID 중복확인·부서 선택이
  모두 회사에 의존하므로 자연스러운 입력 순서
- SUPER_ADMIN 인데 회사 미선택 상태에선 사용자 ID input + 중복확인 버튼
  disable + placeholder "회사 먼저 선택"
- checkUserIdDuplicate 가드: 회사 미선택이면 "회사를 먼저 선택해주세요"
  (백엔드의 400 "company_code 가 비어있음" 보다 친절)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 18:38:30 +09:00
hjjeong a41f99c579 feat(cross-tenant): SUPER_ADMIN 의 회사별 사용자 WRITE (Phase 1)
지금까지 cross-tenant 는 READ 전용. admin 도메인에서 사용자 등록하면
JWT.company_code='*' 가 그대로 박혀 메타 DB 에 INSERT 되던 버그 해결.
이제 SUPER_ADMIN 이 폼의 "회사" 드롭다운에서 TEST01/TEST02 등 선택하면
그 회사 DB 에 정확히 INSERT.

신규 백엔드
- crosstenant/CrossTenantExecutor.java  — 회사 컨텍스트 임시 전환 헬퍼
  (company_code → db_name → ensureTenantPool → set → run → restore)
- crosstenant/CrossTenantUserController.java  — /api/admin/cross-tenant/users
  9개 endpoint (POST/PUT/DELETE/PATCH/with-dept/check-duplicate/단건/이력)
- mapper/provisioning.xml — resolveDbNameByCompanyCode (active 회사만)

기존 단일 회사 모드 (POST /admin/users 등) 무수정 — 회사 도메인
컨텍스트에서 회귀 0.

프론트
- lib/api/user.ts — createUser/updateUser/updateUserStatus/checkDuplicateUserId/
  saveUserWithDept 가 isCrossTenantMode() 면 새 endpoint + body.company_code 로 분기
- UserFormModal — checkDuplicateId 호출 시 formData.company_code 같이 전달
- useUserManagement — status toggle 시 row 의 company_code 같이 전달

검증 (curl, SUPER_ADMIN 토큰):
- 토큰 없음 → 403 super_admin_required
- company_code 없음 → 400 "company_code 가 비어있음"
- 잘못된 company_code → 400 "등록되지 않았거나 비활성 회사"
- check-duplicate: TEST01.test02_admin → not_dup, TEST02.test02_admin → dup ✓
- POST 사용자 → TEST02 USER_INFO +1, TEST01·메타 격리 ✓
- /users fan-out: by={'*':8, 'TEST01':1, 'TEST02':2}, hjtest_ct_001 in TEST02만 ✓
- DELETE → status=inactive (soft) ✓

미구현 (Phase 1 후속):
- 부서 dropdown (cross-tenant department endpoint 별도 필요)
- 비밀번호 초기화 모달의 cross-tenant 분기 (UserPasswordResetModal)
- Phase 2 권한관리 (별도 커밋)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 17:22:52 +09:00
hjjeong cdc55dfd48 feat(cross-tenant): truncated/failed 안내 배너 (READ 트랙 마무리)
SUPER_ADMIN cross-tenant 모드에서 회사당 cap 200 에 걸리거나 한 회사
조회 실패 시 화면 상단에 안내 배너 노출. 아무 메타 없으면 자리 안 잡음.

신규
- components/common/CrossTenantBanner.tsx — amber(truncated) + red(failed)
  v5 토큰 (surface-solid + glow-sm) 기반 솔리드 배너. blur 안 씀

API 클라이언트 4개에 cross_tenant_meta 노출
- lib/api/user.ts        — userAPI.getList 응답에 cross_tenant_meta 추가
- lib/api/role.ts        — roleAPI.getList 동일
- lib/api/batch.ts       — BatchAPI.getBatchConfigs 동일
- lib/api/multilang.ts   — getLangKeys 동일 (i18nList 페이지는 아직 직접
  호출 패턴이라 자동 적용 X — 후속에서 페이지를 getLangKeys 로 통일하면 동작)

페이지 마운트 (3개)
- userMng/userMngList — useUserManagement hook 에 crossTenantMeta state 추가
- userMng/rolesList   — loadRoleGroups 에서 메타 set
- automaticMng/batchmngList — loadBatchConfigs 에서 메타 set
- systemMng/i18nList — 스킵 (cross-tenant aggregation 미적용 상태, 별도 작업)

설계서 §11 검증 (직전 §11.2 부분 실패 시뮬) 결과: failed 배너가
header X-CrossTenant-Failed 와 동일 정보로 화면에 노출됨.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 17:14:48 +09:00
DDD1542 517c42b5cb fix: 피벗 column fields 필터 아이콘 가로 정렬 (세로 일렬 → flex-row + wrap)
PivotView line 1694 의 column header 좌측 셀 안 column fields 필터 아이콘
4개가 'flex flex-col' 로 세로 일렬로 표시되던 문제. 옛 PivotGridComponent
디자인을 통째 흡수한 결과 (T3b) — 가로로 변경.

flex flex-col gap-0.5 → flex flex-row flex-wrap items-center justify-center
gap-1

column fields 가 많아지면 자동 wrap, 한 줄에 모이지 않으면 다음 줄.
세로 길게 늘어지는 회귀 차단.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 16:58:09 +09:00
DDD1542 2d11d222db fix: 피벗 ConfigPanel — FeatureChipGrid 로 교체 + PivotView 빈 그리드 회피
이전 commit (3ed53a670, 57ffbcbbc) 의 두 잘못 수정.

1. ConfigPanel — CPSwitch 8개 → FeatureChipGrid 1개

   CP 시스템에 다중 boolean 토글 묶음용 FeatureChipGrid 가 이미 있는데
   (CPExtras.tsx:208, InvLegacyDivider/Button/Text/InvRepeater 가 사용),
   CPRow + CPSwitch 8개로 따로 만든 게 잘못. cp 시스템 본래 패턴 따름.

   - 8 토글 (chartEnabled / fieldChooserEnabled / rowGrandTotals /
     columnGrandTotals / mergeCells / alternateRowColors / exportExcel /
     exportPdf) 을 평면 key 로 정의
   - source = nested config 에서 평면 boolean 객체 변환
   - onToggle = 평면 key 받아 nested patch (switch 분기)
   - 각 chip 에 desc 추가 (hover tooltip, FeatureChipGrid 가 portal 로 표시)

2. PivotView — data 영역 0개면 안내 (빈 0 그리드 회피)

   hasActiveFields 분기를 강화. 기존: row/column/data 중 하나만 있어도
   true → row 영역에만 컬럼이 들어간 옛 잘못된 매핑이 빈 0 그리드를 표시
   하는 회귀 (Image #6).

   변경: data 영역 컬럼 ≥1 이어야 의미있는 피벗. data 0개면 "필드를
   배치하세요" 안내 + FieldChooser 버튼 fallback.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 16:54:30 +09:00
DDD1542 57ffbcbbc7 fix: 피벗 ConfigPanel default 값 모두 false (안일한 true → 빈 그리드 회귀 방지)
직전 commit (3ed53a670) 의 메타 토글 4종이 default true 로 잡혀 있었음.
빈 row/column 영역에서 행/열 총계만 활성화되어 사용자가 새 피벗 배치 시
무의미한 0 그리드가 보이는 회귀 발생.

default true → false 변경:
- 필드 선택기 (pivotFieldChooser.enabled)
- 행 총계 (pivotTotals.showRowGrandTotals)
- 열 총계 (pivotTotals.showColumnGrandTotals)
- 행 교대 색 (pivotStyle.alternateRowColors)

PivotView 본체 (line 1048~1085) 가 활성 필드 0 일 때 "필드를 배치하세요"
안내 + FieldChooser 버튼을 자연스럽게 표시함. 신규 피벗 배치 시 사용자가
명시적으로 옵션을 켜야 효과 발현 (솔루션 정의 단계 패턴).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 16:44:54 +09:00
DDD1542 3ed53a6708 fix: 피벗 ConfigPanel — 컬럼별 area dropdown 폐기, 메타 옵션 8종으로 교체
T4 (3e6bce70d) 의 ④ 피벗 설정이 빌더에서 row/column/data/filter 를 컬럼별
dropdown 으로 미리 정해버려 피벗의 본질 (사용자 자유 분석) 을 흐림. 본체
(FieldPanel / FieldChooser) 가 영역 배치 담당하도록 일관성 정리 (Excel 피벗
패턴).

삭제 — "사번 [row▼] / 사용자ID [row▼] ..." 컬럼별 area dropdown 매핑 67줄

신규 ④ 피벗 설정 — 메타 토글 8종 (CPRow + CPSwitch)
- 차트 표시 (pivotChart.enabled)
- 필드 선택기 (pivotFieldChooser.enabled)
- 행 총계 (pivotTotals.showRowGrandTotals)
- 열 총계 (pivotTotals.showColumnGrandTotals)
- 셀 병합 (pivotStyle.mergeCells)
- 행 교대 색 (pivotStyle.alternateRowColors)
- 엑셀 내보내기 (pivotExportConfig.excel)
- PDF 내보내기 (pivotExportConfig.pdf)

빌더 = "피벗에 어떤 도구/표시를 켤지" 만 결정.
본체 = row/column/data/filter 영역 배치 + drag-and-drop + drilldown + filter
+ chart 등 분석 인터랙션 담당 (이미 T3b 통째 흡수됨).

다른 viewMode (grouped: groupBy / card: cardColumnMapping) 는 본체 분석 UI
가 없으므로 ConfigPanel 이 핵심 — pivot 만 메타 패턴으로 가는 게 맞다
(Codex GO 판정).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 16:33:13 +09:00
DDD1542 3003a056d9 refactor: v2-card-display CSS rule + 'card-display' alias 제거 (T5 잔여)
Codex 재검증에서 발견된 2건 정리.

withContainerQuery.css: '@container v2-card-display' 미디어 쿼리 룰 (10줄)
삭제. v2-card-display 컴포넌트 폐기 (e44ba2953) 시 빠진 css 정리.

templateMigrate.ts: 'card-display': 'stats' 매핑 라인 삭제. v2-card-display
의 v2 prefix 없는 legacy alias. 폐기 컴포넌트의 마이그 매핑이라 dead.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 14:43:09 +09:00
DDD1542 e44ba2953a refactor: v2-table-grouped/v2-pivot-grid/v2-card-display 일괄 폐기 (T5)
T3 (본체) + T4 (ConfigPanel) 흡수 완료에 따라 옛 별도 컴포넌트 폐기.
v2-split-panel-layout 은 별도 컨테이너로 유지 (Codex 권고).

폴더 5개 삭제
- v2-table-grouped/ (537 + ConfigPanel)
- v2-pivot-grid/ (1963 + utils + components + hooks + ConfigPanel)
- v2-card-display/ (1314 + ConfigPanel)
- pivot-grid/ (legacy)
- card-display/ (legacy)

ConfigPanel 3개 삭제
- V2TableGroupedConfigPanel.tsx
- V2PivotGridConfigPanel.tsx
- V2CardDisplayConfigPanel.tsx

레지스트리 / alias / hidden 정리
- lib/registry/components/index.ts: 5개 import 라인 제거 (renderer 자동 등록 폐기)
- ComponentsPanel.tsx: hidden 목록의 v2-card-display ("→ stats" 잘못된 매핑) /
  v2-table-grouped / v2-pivot-grid / pivot-grid / card-display 모두 제거
- DynamicComponentRenderer.tsx LEGACY_TO_UNIFIED: v2-card-display "→stats"
  (잘못) / v2-table-grouped / v2-pivot-grid / card-display alias 제거
- getComponentConfigPanel.tsx: 4개 dynamic import + alias 제거
- templateMigrate.ts: 3 매핑 제거
- componentConfig.ts: v2PivotGridOverridesSchema, v2CardDisplayOverridesSchema
  + 등록 + defaults 제거

이벤트 dead code
- types/component-events.ts: RefreshCardDisplayDetail / REFRESH_CARD_DISPLAY
  이벤트 (사용처 0건 — v2-card-display 전용) 제거

ScreenDesigner
- 4058 의 isCardDisplay 분기 (66.67% 그리드 특수 처리) 제거
- 4098 의 gridColumnsRatioMap 의 "card-display" 항목 제거

검증
- npx tsc --noEmit 우리 작업 파일 새 에러 0
- v2-table-grouped/v2-pivot-grid/v2-card-display/RefreshCardDisplay 잔존
  grep — 의도적 주석 5곳만 (폐기 흔적 설명)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 14:38:05 +09:00
DDD1542 3e6bce70d1 feat: InvTableConfigPanel pivot/card 옵션 UI (T4)
displayMode 분기 옵션 채움. T3b 에서 흡수한 PivotView + CardView 가
실제로 동작하도록 ConfigPanel UI 노출. cp 프리미티브만 사용.

pivot 분기 (placeholder 제거)
- columns 별 영역 매핑 (none/row/column/data/filter) — CPSelect
- data 영역인 경우 집계 함수 (sum/count/avg/min/max/countDistinct) — CPSelect
- pivotFields[] 배열로 자동 변환 (area="none" 선택 시 항목 제거)
- 컬럼 미로드 시 안내 Hint

card 분기 (신규)
- 그리드: cardsPerRow (1~10), cardSpacing (0~64px) — CPNumber
- 표시 영역 (CPGroup defaultOpen): 제목/부제/설명/이미지 표시 토글 +
  이미지 위치 (top/left/right) + 이미지 크기 (small/medium/large)
- 컬럼 매핑 (CPGroup defaultOpen): titleColumn/subtitleColumn/
  descriptionColumn/imageColumn — CPSelect from columns
- 액션 버튼 (CPGroup defaultOpen=false): showActions 토글 + showView/Edit/
  DeleteButton 조건부 노출

cp-panel-standard 룰 준수 (CPSection > CPGroup > CPRow > CPSelect/Switch
/Segment/Number, 3-depth 미만, 카드형 외곽 X).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 14:31:45 +09:00
DDD1542 49b4cdf562 feat: 테이블 pivot 모드 본체 통째 흡수 (T3b)
5 viewMode 통합 세번째 단계 — Codex 권고 (Pivot 통째 흡수, 회귀 위험
차단) 그대로 적용. v2-pivot-grid 의 본체 + utils + components + hooks +
보조 타입을 모두 table/ 하위로 이전. 리팩토링 X (V2Date picker 패턴 동일).

table/types.ts 보조 타입 흡수
- PivotResult, PivotHeaderNode, PivotFlatRow, PivotFlatColumn, PivotCellValue,
  PivotCellData, PivotGridState, PivotGridProps
- PivotDataSourceType, PivotFilterCondition, PivotJoinConfig,
  PivotDataSourceConfig, PivotTotalsConfig, PivotFieldChooserConfig,
  PivotChartConfig, PivotConditionalFormatRule, PivotStyleConfig,
  PivotExportConfig
- PivotFieldConfig 에 filterValues / filterType / isCalculated /
  calculateFormula 누락 속성 추가
- TableConfig 에 pivot 보조 키 (pivotTotals / pivotStyle /
  pivotFieldChooser / pivotChart / pivotExportConfig)

table/utils/pivot/ 4개 파일 이전 (1505줄)
- pivotEngine.ts (812) — processPivotData / pathToKey / 헤더 트리 / 매트릭스 /
  10종 displayMode (runningTotal, percentDifferenceFromPrevious 등)
- aggregation.ts (180) — sum/count/avg/min/max/countDistinct + 포맷
- conditionalFormat.ts (311) — colorScale/dataBar/iconSet/cellValue 4 종
- exportExcel.ts (202) — Excel 내보내기 (xlsx)
- 옛 prefix(AggregationType 등) → Pivot prefix 일괄 정리

table/internals/pivot/components/ 7개 파일 이전 (2347줄)
- ContextMenu / DrillDownModal / FieldChooser / FieldPanel / FilterPopup /
  PivotChart / index

table/internals/pivot/hooks/ 3개 파일 이전 (570줄)
- usePivotState / useVirtualScroll / index

table/views/PivotView.tsx 신규 (PivotGridComponent.tsx 1963줄 통째 흡수)
- import 경로 일괄 정정 (../../types → ../types, ./utils → ../utils/pivot,
  ./components → ../internals/pivot/components, ./hooks →
  ../internals/pivot/hooks)
- 컴포넌트 이름 PivotGridComponent → PivotView
- 본체 로직 그대로 (리팩토링 X)

TableComponent.switch
- pivot 분기 placeholder 제거 → PivotView 호출
- DOM filter 에 pivotTotals/pivotStyle/pivotFieldChooser/pivotChart/
  pivotExportConfig 추가

13 files, +5400+ insertions. v2-pivot-grid/ 폴더 자체는 Phase T5 dead code
일괄 삭제에서 정리 예정.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 14:23:39 +09:00
DDD1542 a74dff4fa2 feat: 테이블 grouped/card 모드 본체 통합 (T3a)
5 viewMode 통합 두번째 단계 — TableComponent.switch 에 grouped/card/pivot
case 분기 추가. 별도 v2-* 컴포넌트 호출 X. table/views/* 분리.

GroupedView (신규, table/views/GroupedView.tsx)
- config.groupBy 기준으로 데이터를 그룹화해 펼침/접힘 단위로 렌더
- 그룹 헤더에 그룹 키 + 행 개수 표시. ChevronRight/Down 토글
- groupBy 미설정 시 안내 메시지

CardView (신규, table/views/CardView.tsx)
- config.cardsPerRow 으로 그리드, cardSpacing 으로 간격
- cardColumnMapping (titleColumn / subtitleColumn / descriptionColumn /
  imageColumn / displayColumns / actionColumns) 으로 데이터 → 카드 매핑
- cardStyle (showTitle/Subtitle/Description/Image, imagePosition,
  imageSize, showActions, showView/Edit/DeleteButton)

PivotView (placeholder, T3b 에서 통째 흡수 예정)
- v2-pivot-grid/PivotGridComponent (1963) + utils/pivotEngine.ts (700) +
  보조 타입 통째 흡수가 다음 단계
- 현재는 설정된 필드/행 수만 표시하는 placeholder

TableComponent.switch
- case "grouped" / "card" / "pivot" 신규 분기. case "split"/"table"/default 유지
- DOM filter 에 cardsPerRow/cardSpacing/cardStyle/cardColumnMapping/pivotFields 추가

빌드 OK. grouped/card 모드 사용자 선택 시 정상 렌더 (config 옵션 없으면 안내).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 13:49:32 +09:00
DDD1542 442f641305 feat: 테이블 card 모드 옵션 + pivot 풍부한 필드 타입 추가 (T2 인프라)
5 viewMode 통합 (table/split/grouped/pivot/card) 의 첫 단계. types.ts 만.
다음 단계에서 본체 흡수 + ConfigPanel UI + dead code 정리.

card 모드 옵션 (TableConfig 신규)
- cardsPerRow, cardSpacing
- cardStyle: showTitle/Subtitle/Description/Image/imagePosition/Size/showActions
- cardColumnMapping: titleColumn/subtitleColumn/descriptionColumn/imageColumn/
  displayColumns/actionColumns

pivot 모드 풍부한 필드 (TableConfig 의 pivotFields, PivotFieldConfig 신규)
- area: row/column/data/filter
- summaryType (sum/count/avg/min/max/countDistinct)
- summaryDisplayMode 10종 (absoluteValue / percentOfColumnTotal / runningTotal 등)
- groupInterval (year/quarter/month/week/day)
- 기존 단순 pivotRows/pivotColumns/pivotValues 도 호환 유지

Codex 솔루션 정의 단계 GO + 단계별 commit 권고.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 13:44:39 +09:00
DDD1542 5f945363b2 refactor: V2Date 일괄 폐기 + InvField type=date 통일
V2DateConfigPanel(262줄) + V2Date 본체(1046줄) + V2DateConfig 타입
+ lib/registry/components/v2-date/ + DynamicComponentRenderer 의
LEGACY alias 모두 삭제. 솔루션 정의 단계 정확한 1안.

데이터 모델
- FieldType / InputFieldType union 에 time, daterange 2종 추가하여 12종 확장
- canonical 키: dateFormat / minDate / maxDate / showToday / weekStart /
  dateDefault / rangePresets / maxRangeDays
- 옛 v2-date 의 snake_case (min_date / max_date / show_today) 와 dateType /
  type / range / format 키 충돌 종결

런타임 (InputComponent + pickers.tsx)
- V2Date 본체에 있던 SingleDatePicker / DateTimePicker / TimePicker /
  RangeDatePicker 4 picker 를 input/pickers.tsx 로 통합
- InputComponent 의 case "date"/"datetime" 의 native input 분기를 datepicker
  로 교체하고 case "time"/"daterange" 신규 추가
- type 결정 로직에 inputType prop 인식 (DB input_type 매핑) → date/time
  입력이 text 로 fallback 되던 silent breakage 해결

FC 계층
- FieldRenderer / CellRenderer / FcSearch 에 time / daterange 분기 추가
- TimeField, DateRangeField 신규 컴포넌트
- adapters.normalizeType allowed 배열 확장

ConfigPanel
- InvFieldConfigPanel.DateOptions 에 showToday CPRow + CPSwitch 신규
- 옛 호환 코드 (showSeconds:ss 보정 / dateType-format 격상 등) 모두 제거
- InvInputConfigPanel.TYPE_OPTIONS / 날짜 옵션 분기에 time/daterange 추가

dead code 삭제 14곳 + 잔존 정리
- V2Date / V2DateConfigPanel / V2DateConfig / lib/registry/v2-date/ 폴더
- LEGACY_TO_UNIFIED / CONFIG_PANEL_MAP / CONFIG_PANEL_ALIAS / register /
  V2PropertiesPanel hardcoded require / config-panels barrel / hidden 목록
- componentConfig 스키마, templateMigrate, webTypeMapping, DynamicConfigPanel
- withContainerQuery.css 의 v2-date 컨테이너 룰
- db/migrations/so_modal_layout(_kr).json 의 v2-date → input + type=date

37 files, +287 / -854.

Codex GO 판정 기준 (2회 NO-GO 후 FC 계층 / inputType prop / FieldType union /
잔존 v2-date / console.log 모두 처리 후 GO).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 13:22:55 +09:00
DDD1542 e347a75953 refactor: ConfigPanel hook/helper 추출 + IconPicker cp+Portal
Build & Deploy to K8s / build-and-deploy (push) Successful in 5m32s
- useDbTables: search/table/stats 의 DB 테이블 로드 hook (3 패널 중복 제거)
- TableConnectSection + AutoLoadButton: search/table 의 테이블 연결 섹션 + 자동 로드 버튼
- row-helpers: RowNumberBadge / RowExpandChevron / RowDeleteBtn (4 패널 dense list helper)
- IconPicker: shadcn 톤 -> cp 톤 (28px 트리거, focus glow, cp 변수)
- IconPicker popover: React Portal + position:fixed (부모 overflow:hidden 우회)
- input X버튼은 hoverBg={false} 로 silent visual change 원복

Codex (GPT-5.5) 와 매 단계 교차검증 후 진행
미완 후속 사항 (auto-flip / 외부 클릭 닫기 / z-index 표준화 등) 노트에 기록

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 11:25:06 +09:00