Commit Graph

1979 Commits

Author SHA1 Message Date
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 420b92bc7b fix(테이블타입): bug hunt 발견 6건 수정 (B1~B6)
review 4명 (debugger + security-reviewer + architect + code-reviewer) 발견:

- B1 [CRITICAL]: DdlService.convertToInputType 에 file/image/numbering case 추가
  - 사용자가 파일/이미지/채번 선택해도 silent text 저장되던 버그
  - 박창현 image 2 의 8개 중 3개가 운영에서 작동 안 함
- B2 [MAJOR]: TableManagementService.updateColumnSettings null check
  - settings 에 input_type 키만 있고 값 null 일 때 500 에러 방지
- B3 [MAJOR]: TableSettingModal.tsx 'direct' default → 'text'
  - 운영의 bom.status 같은 컬럼이 UI 에 'direct' 표시되던 원인 제거
- B4 [MINOR/UX]: TypeOverviewStrip 에 Legacy 합산 칩 추가
  - V0 의 legacy 1,209 row (category 888 외) 가 strip 에서 보이도록 amber 칩 + 도넛 호
- B5 [DRY]: USER_SELECTABLE_INPUT_TYPES 공통 상수 추출
  - TableManagementService:30 + DdlService:43 중복 → InputTypeConstants 신설
- B6 [type safety]: context flag enum 화
  - 'user-insert'/'user-update-type'/'user-update-other'/'system-normalize' string → InputTypeContext enum
  - typo silent fail 차단

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 18:07:19 +09:00
hjjeong f53307a72e Merge remote-tracking branch 'origin/main' into hjjeong 2026-05-13 17:20:59 +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
johngreen 5c0dca004b Merge fix(반응형): 페이지네이션 잘림 픽스
Build & Deploy to K8s / build-and-deploy (push) Failing after 31m59s
2026-05-13 09:41:44 +09:00
johngreen acbd61e25a fix(반응형): 페이지네이션 잘림 — viewport 하드코딩 → flex-1 min-h-0
기존: scrollContainer 모드의 max-h-[calc(100vh-280px)] 가 viewport 기준 하드코딩이라 페이지 헤더+툴바 합산이 280px 보다 클 때 카드 영역이 viewport 끝까지 차지 → 페이지네이션이 부모 overflow-hidden 에 잘림.

신: outer wrapper 에 flex flex-col min-h-0 flex-1, 내부 테이블/카드에 min-h-0 flex-1 overflow-y-auto. 부모 flex-col 의 남는 공간만 차지 → 페이지네이션 등 형제는 자기 자연 height 유지.

로딩 스켈레톤 동일 패턴 적용.
2026-05-13 09:41:42 +09:00
johngreen dc77c07cc4 Merge refactor(반응형): container query 전환
Build & Deploy to K8s / build-and-deploy (push) Failing after 28m28s
2026-05-13 08:43:16 +09:00
johngreen 6d5ca2f23a refactor(반응형): ResponsiveDataView 를 container query 기반으로 전환
기존: viewport 기준 (lg:hidden, sm:grid-cols-2) — 사이드바 펼친 상태에서 콘텐츠 영역의 실제 width 와 무관하게 동작 → 좁은 영역에 2열 카드가 들어가 카드가 잘려보이는 문제

신: @container 기반 — 컴포넌트가 자기 부모 컨테이너 width 에 반응
- 컨테이너 < 32rem (512px): 카드 1열
- 32~48rem (512~768px): 카드 2열
- ≥ 48rem (768px): 데스크톱 테이블

Tailwind v4 의 first-class container query 활용 (별도 플러그인 불필요). 데스크톱 테이블의 viewport 기준 max-height 스크롤은 유지.

근거: 2026 베스트프랙티스 — page layout=media query, 컴포넌트=container query (LogRocket / NN-Group / Tailwind v4 가이드).
2026-05-13 08:43:13 +09:00
johngreen 1ba310236c Merge fix(반응형): 모바일 width 카드/이력 모달 스크롤
Build & Deploy to K8s / build-and-deploy (push) Failing after 29m24s
2026-05-13 08:09:01 +09:00
johngreen 0d5d1fe10d fix(반응형): 모바일 width 에서 카드 뷰 + 이력 모달 스크롤 누락
- ResponsiveDataView: 모바일 카드 뷰 (lg 미만) + 카드 스켈레톤에 scrollContainer 모드의 max-h + overflow-y-auto 적용. 데스크톱 테이블만 적용돼 있던 누락 픽스
- TableHistoryModal: timeline/detail 탭의 ScrollArea 고정 h-[500px] → 모바일 h-[300px] sm:h-[500px] 적응형. DialogContent max-h-[90vh] 와 충돌 방지

증상: 브라우저 width 좁아질 때 카드 그리드/이력 타임라인이 viewport 너머로 잘리고 스크롤 안 됨.
2026-05-13 08:08:58 +09:00
gbpark 7bd08dcf9d refactor(components): consolidate canonical input cleanup 2026-05-13 02:38:29 +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
johngreen a7683d4d0e Merge fix(대무자): Select candidates data shape
Build & Deploy to K8s / build-and-deploy (push) Failing after 18m41s
2026-05-12 18:10:09 +09:00
johngreen c3e5d7fc1b fix(대무자): Select 후보 목록 - getUserList 응답 shape 호환
SubstituteSection 의 loadCandidates 가 res.data.list 를 가정했지만
getUserList(/api/admin/users) 응답은 { data: [...] } 형태 (data 가 list 자체).
결과로 모든 select 가 '지정 가능한 사용자가 없습니다' 로 표시됐음.

Array.isArray(res.data) 와 res.data.list 둘 다 fallback 으로 처리.
2026-05-12 18:10:07 +09:00
johngreen 33a245e4e8 Merge pull request 'fix(대무자): COMPANY_ADMIN + SQL + UI select' from johngreen into main
Build & Deploy to K8s / build-and-deploy (push) Failing after 3m24s
2026-05-12 17:02:44 +09:00
johngreen c4a62b7e35 fix(대무자): COMPANY_ADMIN 권한 허용 + 결재함 SQL 컬럼 오타 fix + UI 셀렉트 개선
운영 QA 에서 발견된 3가지 결함을 한 번에 수정.

1. SubstituteController.java:56 / SubstituteService.java:242 (requireAdmin)
   - role 비교에서 "COMPANY_ADMIN" 누락 → 운영 admin 이 대무자 지정 시 항상 403.
   - 운영 회사 admin 의 user_type 은 COMPANY_ADMIN 이 표준 (AdminAccountCreator 가 그렇게 생성).
   - "ADMIN" / "SUPER_ADMIN" 외 "COMPANY_ADMIN" 도 허용.

2. mapper/approval.xml (selectMyRequests, selectMyPendingLines)
   - ORDER BY / SELECT 의 R.CREATED_DATE 가 잘못된 컬럼명 (APPROVAL_REQUESTS 실제: created_at).
   - 결재함 /api/approval/my-pending, /api/approval/requests 가 항상 500.
   - 3군데 R.CREATED_DATE → R.CREATED_AT.

3. SubstituteSection.tsx
   - 대무자 ID 를 직접 타이핑하던 input 을 Select 로 교체.
   - getUserList 로 같은 회사 활성 사용자 목록 로드, 본인 + SUPER_ADMIN + 비활성 자동 제외.
   - 다이얼로그 열 때 한 번만 load (openDialog 시 loadCandidates).
   - 빈 결과/로딩 placeholder 처리.
2026-05-12 17:02:15 +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
johngreen 1b9604f66e fix(layout): 부서관리 탭 라벨 hardcoded 한글 fallback 추가
SUPER_ADMIN cross-tenant 모드에서 menu API (/api/admin/menus) 가 500 응답을
내어 uiMenus 가 비어있고, 그 결과 우리 effect 가 매칭할 데이터가 없어
sessionStorage 의 영어 fallback title (deptMngList) 이 갱신되지 않던 문제.

AppLayout 의 fallback 두 곳에 ADMIN_PATH_LABELS 맵 추가:
1. URL 직접 진입 시 첫 openTab 의 fallback title
2. uiMenus 매칭 실패 시 한글 라벨 보강

근본 원인 (menu API 500) 은 별도 backend 이슈 — 본 fix 는 우회.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 13:03:05 +09:00
johngreen 36d93d91cf fix(layout): admin 탭 영어 fallback title 을 메뉴 한글명으로 갱신
URL 직접 진입 / sessionStorage 복원 시 AppLayout 의 fallback
(pathname.split('/').pop()) 이 path segment 를 그대로 탭 title 로
사용해서 '부서관리' 대신 'deptMngList' 같은 영어가 표시되던 문제.

- tabStore: updateTabTitle(tabId, title) 추가
- AppLayout: uiMenus 로드 후 admin 탭들의 admin_url 매칭하여
  menu_name_kor (tabTitle/label/name) 로 갱신

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 11:10:01 +09:00
johngreen f0781022de fix(�μ�����): 25�� ���� �ϰ� ���� + ������ ���Ἲ ��ȭ (#4)
Build & Deploy to K8s / build-and-deploy (push) Successful in 4m31s
johngreen �귣ġ�� �μ����� ���� + �м� ��Ʈ�� main ���� ����. �ڵ� ���� Ʈ����.
2026-05-08 09:48:19 +00:00
johngreen 68c1cb5b14 fix(부서관리): 25개 버그 일괄 수정 + 데이터 무결성 강화
CRITICAL:
- searchUsers 회사/role 격리 가드 추가 (멀티테넌시 침해 차단)
- setPrimaryDept 멤버십 검증 추가 (주부서 데이터 손상 방지)
- parent_dept_code cross-tenant 검증 (validateParent 헬퍼)

HIGH:
- updateDepartment SQL WHERE 에 DELETED_AT IS NULL 추가 (silent corruption 방지)
- update/restore 부서명 중복 검증 추가
- 글로벌 부서 (*) write 작업 SUPER_ADMIN 전용 가드
- 부서코드 자동 생성으로 강제 (사용자 입력 받지 않음)
- 회사 변경 시 상세 패널 초기화
- handleMove 부분 실패 시 화면 동기화
- 검색 시 부모 체인 자동 포함 (broken tree 수정)
- start_date 기본값 today 강제 제거

MEDIUM:
- 멤버 fetch cancellation flag
- 삭제 다이얼로그 dept_code 클로저 캡처
- isDirty 시 X 버튼 폼 초기화 경고
- 변경이력 버튼 disabled (백엔드 API 미구현)
- 일괄등록 실패 상세 모달 (라인 + 사유)
- LIKE 와일드카드 ESCAPE 적용
- nullIfBlank 에 trim 통합

LOW + 새 기능:
- 부서원 추가/제거 UI 신규 구현 (UserSearchModal)
- selectDeptMembers LEFT JOIN 으로 변경
- DepartmentPicker allowRoot 옵션 (최상위로 이동)
- expandAll 전체 departments 사용
- dead code 정리

DB:
- RUN_085 마이그레이션: DEPT_INFO partial UNIQUE + USER_DEPT UNIQUE
- 모든 active 테넌트 DB (siflex/test01/test02_invyone) 적용 완료

Breaking changes:
- 일괄등록 CSV 4컬럼 → 3컬럼 (부서명,상위부서,유형)
- 부서코드 입력란 제거 (자동 부여 DEPT_n)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 17:08:03 +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 80cd2b2d07 fix: e70267f73 에서 누락된 untracked 파일 추가
Build & Deploy to K8s / build-and-deploy (push) Failing after 3m8s
- frontend/components/layout/MenuItemActions.tsx — AppLayout/TopNavBar 가 import 하는데 빠져서 webpack 빌드 fail
- backend-spring db migration V001 (varchar_migration) + V002 (create_missing_tables) 같이 누락분 정리

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 06:22:02 +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 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
johngreen 9755869754 feat(template-thumb): 라이브 미니 프리뷰 (실제 TemplateRenderer scale-down)
Build & Deploy to K8s / build-and-deploy (push) Successful in 4m5s
기존 와이어프레임 박스(테두리 + 투명 fill) 대신 실제 TemplateRenderer
를 mock empty context 로 띄워 transform: scale 로 축소 → 사용자가
빌더에서 그린 그대로의 레이아웃이 카드에서 그대로 읽힘.

- TemplateMiniPreview 컴포넌트 신설:
  - DEFAULT empty context (data:[], callbacks no-op) 로 데이터 fetch 0회
  - BASE_WIDTH=1200, 16:10 stage → ResizeObserver 로 카드 폭 변화 자동 추종
  - pointer-events: none / user-select: none / overflow: hidden
  - views 가 비어있으면 기존 TemplateThumbnail (와이어프레임) 폴백
- TemplateLibraryModal 카드 아이콘 자리 교체
- dashboard.css 에 .dash-lib-card-thumb--live / -stage 추가

향후 템플릿 50+ 로 늘어 모달 첫 오픈이 무거워지면 lazy mount(
intersection observer) 또는 background 스크린샷 캐싱으로 전환.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 08:31:34 +09:00
johngreen b7ebc69755 style(template-thumb): solid 컬러 → 와이어프레임 (테두리+투명 fill)
Build & Deploy to K8s / build-and-deploy (push) Successful in 4m6s
기존: 컴포넌트마다 .55 alpha 솔리드 박스 → 화면 스크린샷처럼 보임
변경: 5px inset 패딩 + 각 블록은 .55 테두리 + .14 fill 의 와이어프레임 톤

- KIND_COLOR (rgba 문자열) → KIND_RGB (RGB 트리플 문자열) 로 변경
  → 같은 색을 테두리/배경 다른 알파로 동시 사용 가능
- thumb 안에 dash-lib-card-thumb-canvas wrapper 추가해 padding 적용
- block 은 border-box + border 1px transparent base, 색상은 inline 으로

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 08:16:37 +09:00
johngreen bad1010621 fix(template-thumb): 실제 스튜디오 저장 포맷(list:Array + screenResolution) 지원
Build & Deploy to K8s / build-and-deploy (push) Successful in 4m7s
기존 fromV2 / fromV1 은 BlockV2.xPct 또는 TemplateComponent.order/row
가정이었는데, 실 운영 빌더(templateAdapter.saveTemplate) 는
list 가 컴포넌트 배열이고 각 요소가 position{x,y} + size{w,h} 절대 px,
공통 screenResolution 으로 정규화되는 구조였음.

fromStudio 추가:
- list 를 배열 / v2.1 layers / { components } 어느 모양이든 평탄화
- screenResolution 우선, 없으면 컴포넌트 bounding box 폴백
- url(v2-table-list 등)/componentType/widgetType 어느 키든 inferKind 로 분류

호출 순서: fromStudio → fromV2 → fromV1 (안전망).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 08:09:14 +09:00
johngreen df9a539017 feat(template): 라이브러리 카드에 wireframe 썸네일 표시
Build & Deploy to K8s / build-and-deploy (push) Successful in 4m26s
템플릿 목록 카드의 정적 📋 아이콘을 실제 view 구조 기반의
미니 와이어프레임으로 교체. 사용자가 카드만 보고도 템플릿이
어떤 화면인지(테이블 위주 / 폼 위주 / 단순 버튼 등) 파악 가능.

- backend: getTemplateList SQL 에 VIEWS 컬럼 추가, list 응답 각
  row 의 views jsonb 를 객체로 파싱
- frontend: TemplateThumbnail 컴포넌트 신설 — v2(BlockV2.xPct/yPct
  /wPct/hPct) 정규화 좌표 우선, v1(order/row) 폴백, 컴포넌트
  종류별 색상(table=primary, form=cyan, button=pink)
- TemplateLibraryModal 카드 아이콘 자리 교체
- dashboard.css 에 .dash-lib-card-thumb / -block 스타일 추가
  (v5 토큰 준수 — solid + glow, blur 없음)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 08:00:10 +09:00
johngreen 59f230187d feat(layout): Tweaks 버튼 토글 동작 — 다시 누르면 닫힘
setSettingsOpen(true) → setSettingsOpen(v => !v) 로 변경.
열려 있을 때 한 번 더 누르면 SettingsModal 이 닫히고,
v5-hdr-icon.on 활성 상태도 자동 토글됨.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 07:59:51 +09:00
johngreen 9d11616761 fix(sidebar): 두 메뉴가 동시에 active 표시되던 버그 해결
Build & Deploy to K8s / build-and-deploy (push) Successful in 4m5s
증상: AI 어시스턴트 → '에이전트 관리' 클릭 후 '멀티에이전트 워크스페이스'
이동하면 사이드바에 두 메뉴가 동시에 보라색 active 로 표시됨.

원인: isMenuActive 가 pathname 매칭과 activeTab 매칭을 OR 결합해서
이전 URL 메뉴 + 현재 활성 탭 메뉴 둘 다 true 반환.

수정: activeTab 이 있을 때는 그 탭 기준으로만 매칭. activeTab 이 없는
경우(/main 첫 진입 등)에만 pathname 으로 fallback.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 07:30:00 +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 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 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 a8ded6455d refactor: ConfigPanel Inv 네이밍 통합 + legacy 패널 분리 + input cp 마이그
11 패널 일괄 Inv* prefix 통일:
- 통합 (lib/registry/components/X/): button / container / divider / search /
  stats / table / title / input → Inv*ConfigPanel
- frontend/components/v2/config-panels/V2FieldConfigPanel → InvFieldConfigPanel
- 옛 v2-* hidden 호환 → InvLegacy{Divider,Text,Button}ConfigPanel

input 통합 컴포넌트 cp 톤 신규 작성 (InvInputConfigPanel):
- 277줄 옛 디자인 → CPVisualGrid 10칸 type 카드 + 타입별 옵션 + FeatureChipGrid

getComponentConfigPanel.tsx 버그 수정 (Codex 검토):
- "stats" key 중복 제거 (옛 StatsCardConfigPanel 이 통합 stats 덮던 silent bug)
- ALIAS 에서 v2-button-primary/v2-divider-line/v2-text-display 제외
  (옵션 B 일관성 — 옛 hidden 컴포넌트는 InvLegacy 패널 사용)
- MAP 의 해당 키를 InvLegacy* 로 직접 매핑

호출처 일괄 갱신:
- 각 통합 컴포넌트의 index.ts 7개 (import / config_panel / re-export)
- v2-input/v2-select/v2-divider-line/v2-text-display/v2-button-primary
  의 index.ts (config_panel 매핑)
- V2PropertiesPanel.tsx 의 require pattern (v2-input/v2-select)

검증: tsc 우리 영역 0건 / V2FieldConfigPanel 잔재 0건 / 기존 path 잔재 0건

다음 세션: useDbTables hook 추출 + 잔여 V2* cp 마이그 + dead code 정리

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 17:57:57 +09:00