From 579805812517671af5f1a2eb42a97a8e4ff9f3ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Fri, 6 Mar 2026 20:34:06 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20[projects]=20=EC=A1=B0=EC=A7=81?= =?UTF-8?q?=EB=8F=84=20=EA=B4=80=EB=A6=AC=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20?= =?UTF-8?q?=EA=B8=B0=EC=88=A0=EB=AC=B8=EC=84=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - projects/org-chart/README.md: 아키텍처, API, DB, 프론트엔드 상세 - index_projects.md: 조직도 프로젝트 등록 - INDEX.md: 조직도 문서 링크 추가 --- sam/docs/INDEX.md | 5 +- sam/docs/projects/index_projects.md | 277 ++++++++++++++++++++++ sam/docs/projects/org-chart/README.md | 317 ++++++++++++++++++++++++++ 3 files changed, 598 insertions(+), 1 deletion(-) create mode 100644 sam/docs/projects/index_projects.md create mode 100644 sam/docs/projects/org-chart/README.md diff --git a/sam/docs/INDEX.md b/sam/docs/INDEX.md index 7b457e9..8da0fce 100644 --- a/sam/docs/INDEX.md +++ b/sam/docs/INDEX.md @@ -1,7 +1,7 @@ # SAM 프로젝트 문서 인덱스 > **Claude Code 작업 전 필수 확인** — 작업 유형에 맞는 문서를 먼저 읽고 시작하세요. -> **최종 갱신**: 2026-02-28 +> **최종 갱신**: 2026-03-06 --- @@ -208,6 +208,7 @@ docs/ | **Legacy** | [draw-module.md](projects/legacy-5130/draw-module.md) | 레거시 드로우 모듈 | | **견적** | [quotation/](projects/quotation/) | 견적 프로젝트 자료 | | **전자서명** | [e-sign/](projects/e-sign/) | 전자서명 프로젝트 자료 | +| **조직도** | [org-chart/README.md](projects/org-chart/README.md) | 조직도 관리 (트리형, 드래그앤드롭, 숨기기) | --- @@ -226,6 +227,7 @@ docs/ | 문서 | 설명 | |------|------| | [20260304_eaccount_infinite_loop_fix.md](changes/20260304_eaccount_infinite_loop_fix.md) | 계좌 입출금내역 부분 월 조회 시 무한루프 크래시 수정 | +| [20260306_purchase_request_payment_method.md](changes/20260306_purchase_request_payment_method.md) | 품의서 지급방법 UI 개선 (구매품의서 추가, 비용정산품의서 행별 변경) | --- @@ -287,6 +289,7 @@ docs/ | **MES** | [README.md](projects/mes/README.md) | MES 프로젝트 개요 | | **MES** | [MES_PROJECT_ROADMAP.md](projects/mes/MES_PROJECT_ROADMAP.md) | 개발 로드맵 | | **Legacy** | [draw-module.md](projects/legacy-5130/draw-module.md) | 레거시 드로우 모듈 | +| **조직도** | [README.md](projects/org-chart/README.md) | 조직도 관리 (Alpine.js + SortableJS) | ### history/ - 히스토리 diff --git a/sam/docs/projects/index_projects.md b/sam/docs/projects/index_projects.md new file mode 100644 index 0000000..844f0f6 --- /dev/null +++ b/sam/docs/projects/index_projects.md @@ -0,0 +1,277 @@ +# 프로젝트 문서 인덱스 + +> SAM 시스템 개발 프로젝트별 문서 모음 +> **최종 업데이트**: 2026-03-06 + +--- + +## 프로젝트 현황 요약 + +| 프로젝트 | 상태 | 설명 | +|---------|------|------| +| [mes](#mes---meserp-프로젝트) | 🟡 진행중 | 차세대 MES/ERP 기능 개발 | +| [quotation](#quotation---견적-기능) | 🟢 Phase 3 완료 | 5130 견적 → SAM 이관 | +| [api-integration](#api-integration---react--api-연동) | 🟡 진행중 | React ↔ API 연동 | +| [5130-migration](#5130-migration---품목-마이그레이션) | 🟡 Phase 1 진행중 | 5130 품목 데이터 마이그레이션 | +| [legacy-5130](#legacy-5130---레거시-분석) | 📚 참조용 | 5130 레거시 모듈 분석 | +| [mng-mobile-responsive](#mng-mobile-responsive---모바일-반응형) | 🟡 진행중 | mng 모바일 반응형 개선 | +| [auto-login](#auto-login---자동-로그인) | ⚪ 대기 | 자동 로그인 기능 | +| [migration-5130-mng](#migration-5130-mng---5130--mng-마이그레이션) | 🟡 진행중 | 5130 → mng 통합 마이그레이션 | +| [e-sign](#e-sign---전자계약-서명) | 🟢 v1.0 구현 완료 | 전자계약 서명 솔루션 (SAM E-Sign) | +| [org-chart](#org-chart---조직도-관리) | 🟢 v1.0 구현 완료 | 트리형 조직도 관리 (드래그앤드롭, 숨기기) | + +--- + +## 프로젝트 상세 + +### mes - MES/ERP 프로젝트 + +**경로**: `docs/projects/mes/` +**상태**: 🟡 Phase 0 (베이스라인 분석) 30% 완료 +**목표**: SAM 시스템의 차세대 MES/ERP 기능 개발 + +**핵심 문서**: +- [README.md](./mes/README.md) - 프로젝트 개요 및 문서 안내 +- [MES_PROGRESS_TRACKER.md](./mes/MES_PROGRESS_TRACKER.md) - 진행 상황 추적 +- [MES_PROJECT_ROADMAP.md](./mes/MES_PROJECT_ROADMAP.md) - 전체 로드맵 + +**분석 결과**: +- `00_baseline/` - Phase 0 분석 결과 + - [PHASE_0_FINAL_REPORT.md](./mes/00_baseline/PHASE_0_FINAL_REPORT.md) + - [BACKEND_DEVELOPMENT_ROADMAP_V2.md](./mes/00_baseline/BACKEND_DEVELOPMENT_ROADMAP_V2.md) + - `docs_breakdown/` - 문서 분석 (7개) + +**v2 분석**: +- `v2-analysis/` - MES v2 화면 분석 + - `quote-analysis/` - 견적 분석 + - `order-analysis/` - 주문 분석 + - `production-analysis/` - 생산 분석 + - `customer-analysis/` - 거래처 분석 + - `site-analysis/` - 현장 분석 + - `price-analysis/` - 단가 분석 + - `master-data-analysis/` - 기준정보 분석 + - `production-userflow/` - 생산 유저플로우 + +--- + +### quotation - 견적 기능 + +**경로**: `docs/projects/quotation/` +**상태**: 🟢 Phase 3 완료 (2025-12-19) +**목표**: 5130 레거시 견적 기능을 SAM 시스템으로 이관 + +**핵심 문서**: +- [MASTER_PLAN.md](./quotation/MASTER_PLAN.md) - 마스터 플랜 +- [PROGRESS.md](./quotation/PROGRESS.md) - 진행 현황 + +**Phase 문서**: +| Phase | 상태 | 경로 | +|-------|------|------| +| 1. 5130 분석 | ✅ 완료 | `phase-1-5130-analysis/` | +| 2. mng 분석 | ✅ 완료 | `phase-2-mng-analysis/` | +| 3. 구현 | ✅ 완료 | `phase-3-implementation/` | +| 4. API 개발 | ⚪ 대기 | `phase-4-api/` | + +**참조 자료**: +- `screenshots/` - MES 프로토타입 화면 캡쳐 (7개) + +--- + +### api-integration - React ↔ API 연동 + +**경로**: `docs/projects/api-integration/` +**상태**: 🟡 Phase 4 진행중 +**목표**: React(dev.sam.kr)와 API(api.sam.kr) 완벽 연동 + +**핵심 문서**: +- [MASTER_PLAN.md](./api-integration/MASTER_PLAN.md) - 마스터 플랜 +- [PROGRESS.md](./api-integration/PROGRESS.md) - 진행 현황 +- [WORKFLOW.md](./api-integration/WORKFLOW.md) - 작업 프로세스 + +**Phase 문서**: +| Phase | 상태 | 경로 | +|-------|------|------| +| 1. 테이블 통합 | 🟢 완료(스킵) | `phase-1-table-migration/` | +| 2. 메뉴 추출 | 🟡 진행중 | `phase-2-menu-extraction/` | +| 3. API 매핑 | 🟡 진행중 | `phase-3-api-mapping/` | +| 4. 연동+검증 | 🟡 진행중 | `phase-4-integration/` | + +**TC 파일**: `phase-4-integration/tc/` - 기능별 테스트 케이스 JSON (17개) + +--- + +### 5130-migration - 품목 마이그레이션 + +**경로**: `docs/projects/5130-migration/` +**상태**: 🟡 Phase 1 진행중 +**목표**: 5130 품목(부품, 자재, BOM) 데이터를 SAM DB로 이전 + +**핵심 문서**: +- [MASTER_PLAN.md](./5130-migration/MASTER_PLAN.md) - 마스터 플랜 +- [PROGRESS.md](./5130-migration/PROGRESS.md) - 진행 현황 + +**Phase 문서**: +| Phase | 상태 | 경로 | +|-------|------|------| +| 1. 소스 분석 | 🟡 진행중 | `phase-1-source-analysis/` | +| 2. 타겟 분석 | ⚪ 대기 | `phase-2-target-analysis/` | +| 3. 매핑 설계 | ⚪ 대기 | `phase-3-mapping/` | + +--- + +### legacy-5130 - 레거시 분석 + +**경로**: `docs/projects/legacy-5130/` +**상태**: 📚 참조용 문서 +**용도**: 5130 레거시 시스템 모듈별 분석 + +**모듈별 분석 문서**: +| 문서 | 내용 | +|------|------| +| [00_OVERVIEW.md](./legacy-5130/00_OVERVIEW.md) | 시스템 개요 | +| [01_MATERIAL.md](./legacy-5130/01_MATERIAL.md) | 자재 관리 | +| [02_PRODUCT.md](./legacy-5130/02_PRODUCT.md) | 제품 관리 | +| [03_ESTIMATE.md](./legacy-5130/03_ESTIMATE.md) | 견적 관리 | +| [04_PRODUCTION.md](./legacy-5130/04_PRODUCTION.md) | 생산 관리 | +| [05_SHIPPING.md](./legacy-5130/05_SHIPPING.md) | 출하 관리 | +| [06_QUALITY.md](./legacy-5130/06_QUALITY.md) | 품질 관리 | +| [07_ACCOUNTING.md](./legacy-5130/07_ACCOUNTING.md) | 회계 관리 | +| [08_SAM_COMPARISON.md](./legacy-5130/08_SAM_COMPARISON.md) | SAM 비교 분석 | +| [draw-module.md](./legacy-5130/draw-module.md) | 도면 모듈 | + +--- + +### mng-mobile-responsive - 모바일 반응형 + +**경로**: `docs/projects/mng-mobile-responsive/` +**상태**: 🟡 진행중 +**목표**: mng 관리자 패널 모바일 반응형 개선 + +**문서**: +- [01-analysis.md](./mng-mobile-responsive/01-analysis.md) - 분석 +- [02-implementation-plan.md](./mng-mobile-responsive/02-implementation-plan.md) - 구현 계획 +- [06-excluded-menus.md](./mng-mobile-responsive/06-excluded-menus.md) - 제외 메뉴 +- [PROGRESS.md](./mng-mobile-responsive/PROGRESS.md) - 진행 현황 + +--- + +### auto-login - 자동 로그인 + +**경로**: `docs/projects/auto-login/` +**상태**: ⚪ 대기 +**목표**: 자동 로그인 기능 구현 + +**문서**: +- [PROGRESS.md](./auto-login/PROGRESS.md) - 진행 현황 + +--- + +### migration-5130-mng - 5130 → mng 마이그레이션 + +**경로**: `docs/projects/migration-5130-mng/` +**상태**: 🟡 진행중 +**목표**: 5130 기능을 mng로 통합 마이그레이션 + +**문서**: +- [MIGRATION_TRACKER.md](./migration-5130-mng/MIGRATION_TRACKER.md) - 마이그레이션 추적 + +--- + +### e-sign - 전자계약 서명 + +**경로**: `docs/projects/e-sign/` +**상태**: 🟢 v1.0 구현 완료 (2026-02-12) +**목표**: 모두싸인과 유사한 간편 전자계약 서명 솔루션 자체 구축 + +**핵심 문서**: +- [technical-design.md](./e-sign/technical-design.md) - 기술 설계 문서 +- [implementation-guide.md](./e-sign/implementation-guide.md) - 구현 가이드 + +**구현 범위**: +| 영역 | 수량 | +|------|------| +| DB 마이그레이션 | 4개 (esign_contracts, esign_signers, esign_sign_fields, esign_audit_logs) | +| API 모델 | 4개 | +| API 서비스 | 4개 | +| API 컨트롤러 | 2개 (16 엔드포인트) | +| MNG 컨트롤러 | 2개 (8 화면) | +| MNG 뷰 | 8개 (React 하이브리드) | + +**기술 스택**: Laravel 11 + React 18 + SignaturePad + PDF.js + +**참고 자료**: +- `esign-storyboard.pptx` - 화면 스토리보드 +- `storyboard-config.json` - 스토리보드 설정 + +--- + +### org-chart - 조직도 관리 + +**경로**: `docs/projects/org-chart/` +**상태**: 🟢 v1.0 구현 완료 (2026-03-06) +**목표**: 테넌트별 조직 구조를 시각적으로 관리하는 트리형 조직도 + +**핵심 문서**: +- [README.md](./org-chart/README.md) - 기술 문서 (아키텍처, API, DB, 프론트엔드 상세) + +**구현 범위**: + +| 영역 | 수량 | +|------|------| +| MNG 컨트롤러 메서드 | 7개 (RdController) | +| API 엔드포인트 | 6개 (조회, 배치, 해제, 순서변경, 숨기기) | +| DB 마이그레이션 | 1개 (departments.options JSON 추가) | +| 뷰 | 1개 (Alpine.js + SortableJS) | + +**기술 스택**: Alpine.js + SortableJS + 수동 DOM 렌더링 (x-for 미사용) + +--- + +## 디렉토리 구조 + +``` +docs/projects/ +├── INDEX.md # 이 파일 +├── mes/ # MES/ERP 프로젝트 (핵심) +│ ├── 00_baseline/ # Phase 0 분석 +│ ├── v1-analysis/ # v1 분석 +│ ├── v2-analysis/ # v2 화면 분석 +│ └── phases/ # Phase별 진행 +├── quotation/ # 견적 기능 +│ ├── phase-1-5130-analysis/ +│ ├── phase-2-mng-analysis/ +│ ├── phase-3-implementation/ +│ └── screenshots/ +├── api-integration/ # React ↔ API 연동 +│ ├── phase-1-table-migration/ +│ ├── phase-2-menu-extraction/ +│ ├── phase-3-api-mapping/ +│ └── phase-4-integration/tc/ +├── 5130-migration/ # 품목 마이그레이션 +│ ├── phase-1-source-analysis/ +│ ├── phase-2-target-analysis/ +│ └── phase-3-mapping/ +├── legacy-5130/ # 레거시 분석 (참조용) +├── mng-mobile-responsive/ # 모바일 반응형 +├── auto-login/ # 자동 로그인 +├── migration-5130-mng/ # 5130→mng 마이그레이션 +├── e-sign/ # 전자계약 서명 (SAM E-Sign) +└── org-chart/ # 조직도 관리 +``` + +--- + +## 관련 문서 + +- [docs/INDEX.md](../INDEX.md) - 전체 문서 인덱스 +- [docs/plans/index_plans.md](../plans/index_plans.md) - 기획 문서 인덱스 +- [docs/guides/PROJECT_DEVELOPMENT_POLICY.md](../guides/PROJECT_DEVELOPMENT_POLICY.md) - 공통 개발 정책 +- [CURRENT_WORKS.md](../../CURRENT_WORKS.md) - 현재 작업 + +--- + +**범례**: +- 🟢 완료 +- 🟡 진행중 +- ⚪ 대기 +- 📚 참조용 \ No newline at end of file diff --git a/sam/docs/projects/org-chart/README.md b/sam/docs/projects/org-chart/README.md new file mode 100644 index 0000000..238ce52 --- /dev/null +++ b/sam/docs/projects/org-chart/README.md @@ -0,0 +1,317 @@ +# 조직도 관리 시스템 + +> **작성일**: 2026-03-06 +> **상태**: 🟢 v1.0 구현 완료 +> **프로젝트**: MNG 전용 (Blade + Alpine.js + SortableJS) + +--- + +## 1. 개요 + +### 1.1 목적 + +테넌트별 조직 구조를 시각적으로 관리하는 트리형 조직도 시스템. +부서 계층 구조와 직원 배치를 드래그 앤 드롭으로 관리한다. + +### 1.2 주요 기능 + +| 기능 | 설명 | +|------|------| +| 트리형 조직도 | 회사 → 부서 → 하위부서 (무한 depth) 계층 표시 | +| 직원 배치 | 드래그 앤 드롭으로 직원을 부서에 배치/해제 | +| 부서 순서 변경 | 같은 레벨 내 부서 순서 드래그로 변경 | +| 부서 계층 이동 | 부서를 다른 부서 아래로 드래그하여 parent 변경 | +| 부서 숨기기 | 더블클릭 → 숨기기 버튼 → DB 저장 (영구) | +| 임원 필터링 | 대표이사/사장 등은 미배치 목록에서 제외 | + +--- + +## 2. 기술 스택 + +| 구분 | 기술 | +|------|------| +| 백엔드 | Laravel (MNG 프로젝트) | +| 프론트엔드 | Alpine.js + 수동 DOM 렌더링 | +| 드래그 앤 드롭 | SortableJS | +| 스타일 | Tailwind CSS + inline style | +| 데이터 저장 | MySQL `departments`, `employees` 테이블 | + +--- + +## 3. 아키텍처 + +### 3.1 렌더링 방식 + +> **핵심**: Alpine.js `x-for` 대신 수동 `innerHTML` 렌더링을 사용한다. + +SortableJS와 Alpine.js `x-for` 템플릿이 동시에 DOM을 조작하면 **이중 업데이트 버그**가 발생한다. +이를 해결하기 위해 부서 트리는 JavaScript로 HTML 문자열을 생성하고 `innerHTML`로 삽입한다. + +``` +Alpine.js 데이터 변경 + ↓ +renderTree() 호출 + ↓ +기존 SortableJS 인스턴스 destroy + ↓ +buildChildrenHtml(null, 0) → 재귀적 HTML 생성 + ↓ +$refs.deptTree.innerHTML = html + ↓ +$nextTick → initDeptSortables() + initEmpSortables() +``` + +### 3.2 이벤트 처리 + +수동 렌더링된 HTML에는 Alpine 디렉티브가 없으므로 **이벤트 위임(Event Delegation)** 패턴을 사용한다. + +``` +루트 div @click="handleClick($event)" + @dblclick="handleDblClick($event)" + ↓ +e.target.closest('[data-action]') 으로 액션 식별 + ↓ +data-action 값에 따라 분기: + - "unassign" → 직원 미배치 + - "hide-dept" → 부서 숨기기 + - "restore-dept" → 부서 복원 + - "dept-dblclick" → 더블클릭 시 숨기기 버튼 토글 +``` + +### 3.3 순환 참조 방지 + +부서를 자신의 하위로 드래그하면 무한 루프가 발생한다. +`isDescendant(ancestorId, targetId)` 재귀 함수로 이를 차단한다. + +```javascript +// 드래그 대상(dragId)의 자손인 곳으로는 이동 불가 +onMove: (evt) => { + const dragId = parseInt(evt.dragged.dataset.deptId); + const toPid = evt.to.dataset.parentId ? parseInt(evt.to.dataset.parentId) : null; + if (toPid === dragId || this.isDescendant(dragId, toPid)) return false; +} +``` + +--- + +## 4. 파일 구조 + +### 4.1 MNG 프로젝트 + +| 파일 | 역할 | +|------|------| +| `app/Http/Controllers/RdController.php` | 컨트롤러 (7개 메서드) | +| `app/Models/Tenants/Department.php` | 부서 모델 (`options` JSON cast) | +| `resources/views/rd/org-chart.blade.php` | 뷰 (Alpine.js + SortableJS) | +| `routes/web.php` | 라우트 (6개 엔드포인트) | + +### 4.2 API 프로젝트 + +| 파일 | 역할 | +|------|------| +| `database/migrations/2026_03_06_201500_add_options_to_departments_table.php` | `options` JSON 컬럼 추가 | + +--- + +## 5. API 엔드포인트 + +> 모든 엔드포인트는 `rd.` 네임 프리픽스 하위에 위치한다. + +| Method | Route | 컨트롤러 메서드 | 설명 | +|--------|-------|---------------|------| +| GET | `/rd/org-chart` | `orgChart` | 조직도 페이지 | +| POST | `/rd/org-chart/assign` | `orgChartAssign` | 직원 부서 배치 | +| POST | `/rd/org-chart/unassign` | `orgChartUnassign` | 직원 부서 해제 | +| POST | `/rd/org-chart/reorder` | `orgChartReorder` | 직원 일괄 이동 | +| POST | `/rd/org-chart/reorder-depts` | `orgChartReorderDepts` | 부서 순서/계층 변경 | +| POST | `/rd/org-chart/toggle-hide` | `orgChartToggleHide` | 부서 숨기기/표시 토글 | + +### 5.1 요청/응답 형식 + +**부서 배치** (`POST /rd/org-chart/assign`): +```json +{ "employee_id": 1, "department_id": 5 } +→ { "success": true } +``` + +**부서 순서 변경** (`POST /rd/org-chart/reorder-depts`): +```json +{ + "orders": [ + { "id": 1, "parent_id": null, "sort_order": 1 }, + { "id": 2, "parent_id": null, "sort_order": 2 }, + { "id": 3, "parent_id": 1, "sort_order": 1 } + ] +} +→ { "success": true } +``` + +**부서 숨기기** (`POST /rd/org-chart/toggle-hide`): +```json +{ "department_id": 5, "hidden": true } +→ { "success": true } +``` + +--- + +## 6. DB 구조 + +### 6.1 departments 테이블 (관련 컬럼) + +| 컬럼 | 타입 | 설명 | +|------|------|------| +| `id` | int | PK | +| `tenant_id` | int | 테넌트 FK | +| `parent_id` | int (nullable) | 상위 부서 (null = 최상위) | +| `name` | varchar | 부서명 | +| `code` | varchar | 부서 코드 | +| `is_active` | bool | 활성 여부 | +| `sort_order` | int | 정렬 순서 | +| `options` | json (nullable) | 확장 속성 | + +**`options` 키**: + +| 키 | 타입 | 설명 | +|----|------|------| +| `orgchart_hidden` | boolean | 조직도에서 숨김 여부 | + +### 6.2 employees 테이블 (관련 컬럼) + +| 컬럼 | 타입 | 설명 | +|------|------|------| +| `id` | int | PK | +| `tenant_id` | int | 테넌트 FK | +| `department_id` | int (nullable) | 소속 부서 (null = 미배치) | +| `display_name` | varchar | 표시 이름 | +| `position_label` | varchar | 직책/직급 | +| `employee_status` | enum | `active`, `leave`, `resigned` | + +--- + +## 7. 프론트엔드 구현 상세 + +### 7.1 Alpine.js 컴포넌트 (`orgChart()`) + +**데이터**: + +| 속성 | 타입 | 설명 | +|------|------|------| +| `departments` | Array | 전체 부서 목록 (서버에서 전달) | +| `employees` | Array | 전체 직원 목록 (서버에서 전달) | +| `hiddenDepts` | Set | 숨긴 부서 ID (DB에서 초기화) | +| `dblClickDept` | int/null | 더블클릭된 부서 ID (숨기기 버튼 표시용) | +| `execTitles` | Array | 임원 직책 목록 (`['대표이사', '사장', '부사장', '회장', '부회장']`) | + +**핵심 메서드**: + +| 메서드 | 설명 | +|--------|------| +| `renderTree()` | SortableJS 파괴 → HTML 재생성 → SortableJS 재초기화 | +| `buildChildrenHtml(parentId, level)` | 재귀적 자식 부서 HTML 생성 | +| `buildNodeHtml(dept, level)` | 단일 부서 카드 HTML (level별 스타일 차등) | +| `buildEmpHtml(emp, isLarge)` | 직원 카드 HTML | +| `isDeptHidden(deptId)` | 부서 또는 상위 부서가 숨김인지 재귀 체크 | +| `isDescendant(ancestorId, targetId)` | 순환 참조 방지 | +| `isExecutive(emp)` | 임원 여부 판별 | + +### 7.2 SortableJS 그룹 + +| 그룹 | 대상 | 핸들 | 기능 | +|------|------|------|------| +| `departments` | `.org-children`, `.org-drop-target` | `.dept-drag-handle` | 부서 순서/계층 변경 | +| `employees` | `.emp-zone`, `#unassigned-zone` | (전체) | 직원 배치/해제 | + +### 7.3 CSS 연결선 + +부서 간 연결선은 CSS `::before`/`::after` 의사 요소로 구현한다. + +``` + 부모 노드 + │ (vertical: div 1px × 24px) + ┌───────┼───────┐ (horizontal: ::before + ::after) + │ │ │ (vertical: div 1px × 24px) + 자식1 자식2 자식3 +``` + +| 선택자 | 역할 | +|--------|------| +| `.org-node-wrap` 내부 div (1px × 24px) | 세로 연결선 | +| `.org-node-wrap:not(:first-child)::before` | 왼쪽 가로선 (left:0 ~ right:50%) | +| `.org-node-wrap:not(:last-child)::after` | 오른쪽 가로선 (left:50% ~ right:0) | +| `:only-child` | 단일 자식이면 가로선 숨김 | + +### 7.4 부서 숨기기 UX 흐름 + +``` +① 부서 헤더 더블클릭 + ↓ +② dblClickDept = dept.id → renderTree() + ↓ +③ 헤더에 빨간 "숨기기" 버튼 표시 + ↓ +④ "숨기기" 클릭 + ↓ +⑤ hiddenDepts.add(id) → renderTree() → POST /toggle-hide (DB 저장) + ↓ +⑥ 해당 부서 + 하위 부서가 트리에서 제거 +⑦ "숨겨진 부서" 패널에 표시 + ↓ +⑧ 패널에서 👁 아이콘 클릭 → hiddenDepts.delete(id) → POST /toggle-hide +``` + +### 7.5 부서 레벨별 스타일 + +| Level | 색상 테마 | 너비 | 아이콘 | +|-------|---------|------|--------| +| 0 (최상위) | 보라 (`#7C3AED`) | 200px | `ri-building-2-line` | +| 1 (중간) | 인디고 (`#6366F1`) | 180px | `ri-git-branch-line` | +| 2+ (하위) | 회색 (`#6B7280`) | 160px | `ri-subtract-line` | + +--- + +## 8. 비즈니스 규칙 + +### 8.1 임원 필터링 + +미배치 직원 목록에서 다음 조건에 해당하면 제외: + +- `position_label`이 `['대표이사', '사장', '부사장', '회장', '부회장']` 중 하나 +- `display_name`이 테넌트의 `ceo_name`과 일치 + +> 이유: 조직도 최상단에 "대표이사 OOO"이 이미 표시되므로 중복 방지 + +### 8.2 부서 숨기기 + +- `departments.options` JSON의 `orgchart_hidden` 키로 저장 +- 숨긴 부서의 **하위 부서도 자동으로 숨겨짐** (`isDeptHidden` 재귀 체크) +- 숨겨진 부서 패널에는 **직접 숨긴 부서만** 표시 (자식은 부모 복원 시 같이 복원) +- 숨기기는 **조직도 표시 전용** — `is_active`와 무관하며, 부서 데이터에 영향 없음 + +### 8.3 직원 표시 형식 + +- 직책이 있으면: `{직책} {이름}` (예: "과장 전진선") +- 직책이 없으면: `{이름}` (예: "김보곤") + +--- + +## 9. 개발 이력 + +| 날짜 | 커밋 | 내용 | +|------|------|------| +| 2026-03-06 | `a12ee886` | CSS 연결선 수정 + 빈 드롭 타겟 숨김 | +| 2026-03-06 | `9fd72e49` | 부서 숨기기 기능 추가 (프론트 전용) | +| 2026-03-06 | `8c8fd5f6` | 대표이사 미배치 제외 + 숨긴 부서 연결선 제거 | +| 2026-03-06 | `81157a15` | 부서 숨기기 상태 DB 저장 (`options.orgchart_hidden`) | + +--- + +## 관련 문서 + +- [rules/department-tree-api.md](../../rules/department-tree-api.md) — 부서 트리 API 규칙 +- [rules/employee-api.md](../../rules/employee-api.md) — 직원 API 규칙 +- [system/database/hr.md](../../system/database/hr.md) — HR 테이블 스키마 +- [standards/options-column-policy.md](../../standards/options-column-policy.md) — options JSON 컬럼 정책 + +--- + +**최종 업데이트**: 2026-03-06