Commit Graph

479 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
johngreen ff95c1950e fix(테이블관리): 중요 4건 일괄 수정 (PR-C)
1. getColumnGroup metaCols 확장 — 시스템 자동 생성 컬럼이 5개만 인식되어
   사용자 컬럼에 섞여 보이던 문제. objid / tenant_id / creator / modifier /
   created_at / updated_at 추가 (총 11개).

2. updateColumnWebType 회사코드 격리 — 모든 호출이 company_code='*' 로 저장돼
   회사 관리자가 자기 회사 전용 web_type 변경 시 모든 회사 공통 설정을 건드림.
   컨트롤러에서 @RequestAttribute("company_code") 받아 service 에 전달.

3. validateUniqueConstraints N+1 해소 — hasColumn 이 루프 안에서 매번 호출되어
   UNIQUE 컬럼 N 개일 때 N 번의 information_schema 조회. 루프 밖으로 빼서 1 번.

4. sanitize 강화 — 빈 문자열 / 숫자 시작 / 63자 초과 / SQL 예약어 모두
   IllegalArgumentException 으로 거부. 빈 식별자가 동적 SQL 에 끼어들어 500
   에러 노출되던 패턴 방지.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 15:04:02 +09: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
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 cf5f7ef9af fix(테이블타입): 컬럼 탭 그룹 헤더 사용자/시스템 2그룹으로 정리 — 참조 탭과 중복 해소
PR #24 에서 컬럼/참조 탭은 분리했지만 ColumnGrid 의 기존 3그룹 (기본/참조/메타) 헤더가
그대로 남아있어 "참조 정보" 그룹이 컬럼 탭에서도 보이고 참조 탭에서도 보이는 시각적 중복
발생.

DBeaver 의 Columns 탭처럼 모든 컬럼은 컬럼 탭에서 보여야 함 (참조 컬럼 포함). 단,
"참조 정보" 그룹 헤더는 참조 탭이 같은 역할을 하므로 제거.

- "기본 정보" + "참조 정보" → "사용자 컬럼" 으로 합침
- "메타 정보" → "시스템 컬럼" 으로 이름만 평이하게
- 참조 컬럼은 평범한 한 행으로 표시 (타입 칩으로 종류는 식별)
- 미사용 Layers 아이콘 import 제거

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 12:31:59 +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
johngreen 30ebb14023 fix(테이블타입): 입력 타입 아이콘 lucide 로 통일 — letter/symbol/emoji 혼재 정리
기존엔 iconChar 문자열에 'T'(글자) / '#'(기호) / '{}'(코드) / '📎'(emoji) 식으로 일관성 없게
표시. 카드 그리드에서 시각 노이즈 큼.

- TypeColorConfig.iconChar(string) → Icon(LucideIcon) 으로 교체
- 13개 입력 타입 전부 lucide 아이콘 매핑 (Type/Hash/Calendar/Braces/Link2 등)
- FALLBACK_TYPE_CONFIG export 해서 TypeOverviewStrip 의 legacy/unknown 도 같은 인터페이스 따름

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 19:21:30 +09:00
johngreen 4c5b672f40 fix(테이블타입): ⋯ 드롭다운 클릭이 row 로 새서 상세 패널이 열리던 문제
Radix DropdownMenuTrigger 는 onPointerDown 으로 트리거되는데 기존엔 onClick
stopPropagation 만 있어서, 부모 row 의 onClick(=setSelectedColumn)이 같이
발화 → 상세 패널이 슬라이드 in → 중앙 ColumnGrid 가 오버레이에 가려져
squish 처럼 보이던 문제.

⋯ 버튼을 감싸는 div 에 onClick / onPointerDown / onMouseDown 세 가지에
모두 stopPropagation 추가.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 18:44:55 +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
johngreen b25a6324f8 fix(테이블타입): CreateTableModal 다이얼로그 flex 레이아웃 — 스크롤 가능한 본문 + 고정 푸터
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 14:48:47 +09:00
hjjeong 947b31eff5 Merge remote-tracking branch 'origin/main' into hjjeong 2026-05-19 11:10:04 +09:00
hjjeong 46707bd116 feat(admin): 외부 DB 커넥션 멀티 DB 테스트 + 프로비저닝 시퀀스 reset 보강
기존엔 PostgreSQL 만 테스트 가능했고, V001 (SERIAL→VARCHAR) 마이그레이션 이후 회사 프로비저닝 시 시퀀스가 max(id) 보다 작은 상태로 남아있어서 새 외부 커넥션 등록 시 duplicate key 가 재발하던 문제 해결.

backend
- build.gradle: MariaDB/MySQL/MSSQL/SQLite JDBC 드라이버 4종 runtimeOnly 추가
- ExternalDbConnectionService.executeConnectionTest: PostgreSQL-only 가드 제거, dbType 별 JDBC URL/props 분기 구현 (postgresql/mysql/mariadb/mssql/sqlite). defaultPort helper 추가
- mapper/externalDbConnection.xml: INSERT/UPDATE 의 port/connection_timeout/query_timeout/max_connections 에 ::varchar 캐스팅 추가 (V001 으로 VARCHAR 가 됐는데 클라가 숫자로 보내서 character varying = bigint 비교 불가로 500 나던 것)
- DataCopier.resetSequences: VARCHAR PK + 시퀀스 의존성이 남은 컬럼도 setval 대상에 포함. MAX(col::bigint) + col ~ '^[0-9]+$' 정규식으로 type mismatch 회피하면서 숫자형 VARCHAR PK 만 안전하게 reset

frontend
- ExternalDbConnectionModal: DialogContent 를 flex 컬럼으로, 본문에 자체 스크롤 + Footer shrink-0 → 길어진 폼에서도 취소/생성 버튼이 항상 보이도록

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 11:01:14 +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
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 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 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 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
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
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
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 e16fb16987 어드민 cross-tenant 집계 (SUPER_ADMIN) + 사용자관리 자체 스크롤
SUPER_ADMIN 토큰(company_code=*)이면 등록 회사들 DB 를 순회해 결과를
집계해 돌려주는 CrossTenantAggregator/Controller 추가. 사용자/권한그룹/
배치/다국어 키 4개 도메인의 list API 가 cross-tenant 모드 지원.

UserTable + ResponsiveDataView 에 compact/scrollContainer prop 추가.
페이지 헤더/툴바/페이지네이션은 고정, 테이블만 자체 스크롤.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 17:52:30 +09:00
gbpark 1761b5d599 추가수정분
Build & Deploy to K8s / build-and-deploy (push) Successful in 7s
2026-04-25 00:41:37 +09:00
gbpark 68f85f3736 회사 관리 기능 확장 + 테넌트/비번 보안 하드닝
- 첫 로그인 비번 강제 변경 (RUN_082): FORCE_PASSWORD_CHANGE 컬럼,
  ForcePasswordChangeGuardFilter, /auth/change-password API + 페이지
- 테넌트 일관성 가드: TenantConsistencyGuardFilter 로 JWT.company_code
  ↔ 서브도메인 company_code 대조, CompanyResolver 가 (db_name, company_code)
  동시 반환
- 회사 관리 확장 (RUN_083 audit log, RUN_084 lifecycle 컬럼):
  CompanyAdmin/Members/Templates/Lifecycle/AuditLog 서비스 +
  CompanyMgmtController + SuperAdminGuard
- 회사 관리 UI: CompanyAccordionRow 탭화 + 모달 4종
  (AdminInfo/Deactivate/Delete/RecopyTemplates) + AuditLogDrawer + csvExport
- 프로비저닝 마법사: force_password_change 토글 반영
- 프론트 인증: storage 이벤트 멀티탭 동기화, 403 errorCode
  (PASSWORD_CHANGE_REQUIRED / CROSS_TENANT_REJECTED / TENANT_NOT_RESOLVED)
  전역 리다이렉트
- 기타: StartupSchemaMigrator, OS별 도커 기동 스크립트, CLAUDE.md 트래킹

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 00:36:05 +09:00
gbpark 76f43cea9b DB 이름 vexplor → invyone 전환
Build & Deploy to K8s / build-and-deploy (push) Successful in 4m30s
- application.yml, k8s configmap, docker-compose 4종: SPRING_DATASOURCE_URL path
- provisioning 코드: {prefix}_vexplor → {prefix}_invyone 테넌트 DB 네이밍 규칙
- 프론트 마법사: Step1Basic, Step4Run 미리보기 라벨
- CompanyAccordionRow: 기본 DB 이름 포맷
- 마이그레이션/멀티테넌시 문서 동기화
- Traefik 와일드카드 설정 산출물 보관 (notes/)

비밀번호(vexplor0909!!) 및 역사 기록 문서(INVYONE_CONCEPT,
DDD1542, test-output, dashboard-runtime-fixes) 는 의도적으로 미변경.
2026-04-24 19:15:06 +09:00
gbpark 8c861144dc 서브도메인 배포 작업
Build & Deploy to K8s / build-and-deploy (push) Successful in 6s
2026-04-24 17:49:16 +09:00
gbpark 8be7e16e56 서브도메인설정
Build & Deploy to K8s / build-and-deploy (push) Successful in 4m28s
2026-04-24 04:56:40 +09:00
DDD1542 3eda684787 사용자 대시보드 기능강화 및 인비온 스튜디오 메뉴관리 자잘한수정
Build & Deploy to K8s / build-and-deploy (push) Successful in 4m22s
2026-04-22 18:27:06 +09:00
chpark 2c57dc8cda 부서 권한 그룹 권한 관리
Build & Deploy to K8s / build-and-deploy (push) Successful in 7s
2026-04-22 03:16:38 +09:00
gbpark de7ab9b7e3 중간세이브 - 메뉴수정 - INVYONE 스튜디오 작업 2026-04-16 00:32:19 +09:00
gbpark 06ab990dbd 진행중 2026-04-08 04:55:12 +09:00
gbpark de24fb09d5 123 2026-04-06 15:54:31 +09:00
DDD1542 87498b9940 Refactor code structure for improved readability and maintainability 2026-03-31 09:34:54 +09:00
DDD1542 b9e3f68fce [agent-pipeline] pipe-20260330021427-v9fb round-1 2026-03-30 11:25:52 +09:00
DDD1542 7529c3ff9e [agent-pipeline] pipe-20260329160157-3bqb round-1 2026-03-30 02:26:40 +09:00
DDD1542 a5f4cd5ba9 [agent-pipeline] pipe-20260329112709-ncml round-1 2026-03-29 22:56:00 +09:00
DDD1542 0fc2101331 [agent-pipeline] pipe-20260329080516-zyud round-1 2026-03-29 17:48:45 +09:00
DDD1542 9277c93ddc [agent-pipeline] pipe-20260329072859-n5mz round-1 2026-03-29 16:57:32 +09:00
DDD1542 882f63e744 [agent-pipeline] pipe-20260329071056-9n90 round-1 2026-03-29 16:18:59 +09:00
DDD1542 f01fdfc57c [agent-pipeline] pipe-20260329052843-hdtq round-1 2026-03-29 14:49:52 +09:00
DDD1542 18d237c95e [agent-pipeline] pipe-20260329010534-qgv9 round-2 2026-03-29 13:32:26 +09:00
DDD1542 7418e712cd [agent-pipeline] pipe-20260329010534-qgv9 round-1 2026-03-29 12:59:58 +09:00