feat: 실험실 메뉴 Tab + Flyout 방식 구현

- 실험실 그룹 (S: Strategy, A: AI/Automation, M: Management) 추가
- 확장 상태: S | A | M 수평 탭, 클릭 시 해당 패널 표시
- 축소 상태: 아이콘 호버 시 우측 플라이아웃 팝업
- localStorage 탭 상태 저장/복원

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-13 15:10:39 +09:00
parent 01ec419a13
commit 8bb0318836
4 changed files with 885 additions and 776 deletions

View File

@@ -1,125 +1,59 @@
# Workflow 진행 상태
**작업명:** Archive & Restore Feature (아카이브 복원 기능)
**시작일:** 2025-11-30
**분석 문서:** `claudedocs/archive-restore-feature-analysis.md`
## 현재 단계: 완료 ✅
---
## 현재 단계: Phase 2 완료 ✅
---
## 작업 내용
실험실 메뉴 Tab + Flyout 구현 (3단계 메뉴 구조 개선)
## 전체 작업 목록
- [x] 1단계: 분석 완료
- [x] 2단계: Phase 1 순차 수정 (8/8 완료)
- [x] 3단계: Phase 1 검증 완료 (Pint)
- [x] 4단계: Phase 2 테넌트 필터링 (5/5 완료)
- [x] 5단계: Phase 2 검증 완료 (Pint)
- [ ] 6단계: 커밋 대기
- [x] 2단계: 순차 수정 (3/3 완료) - Tab + Flyout 방식
- [x] 3단계: 검증 완료
- [x] 4단계: 문서 정리 완료
- [x] 5단계: 커밋 완료 (39f8d58)
---
## 구현 방식 변경 이력
1. **초기 방식**: 중첩 서브그룹 (padding 들여쓰기)
- 문제: 256px 사이드바에서 텍스트가 세로로 표시됨
2. **최종 방식**: Tab + Flyout
- 확장 상태: S | A | M 수평 탭, 클릭 시 해당 패널 표시
- 축소 상태: 아이콘 호버 시 우측에 플라이아웃 팝업
## Phase 1 - 아카이브 복원 기능 ✅ 완료
## 순차 수정 상세 (2단계) - 완료
- [x] 수정 1/3: HTML 구조 변경 (탭 + 플라이아웃)
- [x] 수정 2/3: CSS 스타일 추가 (탭 버튼, 플라이아웃 애니메이션)
- [x] 수정 3/3: JavaScript 추가 (switchLabTab, switchLabFlyoutTab)
| # | 작업 | 파일 | 상태 |
|---|------|------|------|
| 1 | 마이그레이션: record_type enum→varchar | `api/database/migrations/2025_11_30_*_modify_archived_records_record_type_to_varchar.php` | ✅ 완료 |
| 2 | ArchiveService 생성 | `mng/app/Services/ArchiveService.php` | ✅ 완료 |
| 3 | RestoreService 생성 | `mng/app/Services/RestoreService.php` | ✅ 완료 |
| 4 | TenantService에 아카이브 로직 적용 | `mng/app/Services/TenantService.php` | ✅ 완료 |
| 5 | UserService에 아카이브 로직 적용 | `mng/app/Services/UserService.php` | ✅ 완료 |
| 6 | ArchivedRecordController에 restore 추가 | `mng/app/Http/Controllers/ArchivedRecordController.php` | ✅ 완료 |
| 7 | 라우트 추가 | `mng/routes/web.php` | ✅ 완료 |
| 8 | UI: 복원 버튼 추가 | `mng/resources/views/archived-records/show.blade.php`, `restore-check.blade.php` | ✅ 완료 |
## 검증 결과 (3단계) - 완료
- [x] PHP 문법 검사: 오류 없음
- [x] Blade 뷰 캐시 빌드: 성공
---
## 도구 사용 기록
## Phase 2 - 테넌트 필터링 기능 ✅ 완료
### 1단계 (분석) - 완료
**네이티브 도구:**
- Read: 4회 (myheader.php x2, INDEX.md, CURRENT_WORKS.md)
- Bash: 2회 (프로젝트 탐색)
| # | 저장소 | 파일 | 작업 | 상태 |
|---|--------|------|------|------|
| 1 | **api/** | `database/migrations/2025_12_01_002115_add_tenant_id_to_archived_records.php` | 신규 - DB 마이그레이션 | ✅ 완료 |
| 2 | mng/ | `app/Services/ArchiveService.php` | 수정 - tenant_id 저장 로직 | ✅ 완료 |
| 3 | mng/ | `app/Services/ArchivedRecordService.php` | 수정 - 테넌트 필터링 | ✅ 완료 |
| 4 | mng/ | `app/Models/Archives/ArchivedRecord.php` | 수정 - fillable, 관계 추가 | ✅ 완료 |
| 5 | mng/ | `resources/views/archived-records/partials/table.blade.php` | 수정 - 대상 테넌트 표시 | ✅ 완료 |
### 2단계 (순차 수정) - 완료
**네이티브 도구:**
- Read: 3회 (sidebar.blade.php, WORKFLOW_STATE.md, header.blade.php)
- Edit: 3회 (HTML 구조 변경, CSS 추가, JS 추가)
---
### 3단계 (검증) - 완료
**네이티브 도구:**
- Bash: 2회 (php -l, view:cache)
## 생성/수정된 파일 목록 (전체)
### api/ 저장소
- `database/migrations/2025_12_01_002115_add_tenant_id_to_archived_records.php` (신규)
### mng/ 저장소
#### 신규 생성
- `app/Services/ArchiveService.php` (326줄)
- `app/Services/RestoreService.php` (319줄)
- `resources/views/archived-records/restore-check.blade.php`
- `database/migrations/2025_11_30_144617_modify_archived_records_record_type_to_varchar.php`
#### 수정
- `app/Services/TenantService.php` (ArchiveService DI, forceDeleteTenant 수정)
- `app/Services/UserService.php` (ArchiveService DI, forceDeleteUser 수정)
- `app/Services/ArchivedRecordService.php` (tenant_id 필드 추가, 필터링)
- `app/Models/Archives/ArchivedRecord.php` (tenant_id fillable, tenant 관계)
- `app/Http/Controllers/ArchivedRecordController.php` (restore, checkRestore 메서드)
- `routes/web.php` (restore-check, restore 라우트)
- `resources/views/archived-records/show.blade.php` (복원 버튼, 알림 메시지)
- `resources/views/archived-records/index.blade.php` (알림 메시지)
- `resources/views/archived-records/partials/table.blade.php` (대상 테넌트 컬럼)
---
## Phase 2 주요 변경 내용
### 1. tenant_id 컬럼 추가
- archived_records 테이블에 tenant_id 컬럼 추가
- FK 제약조건 없음 (삭제된 테넌트 참조 가능)
- 인덱스 추가
### 2. tenant_id 결정 로직
- **테넌트 삭제**: tenant_id = 삭제되는 테넌트의 ID (자기 자신)
- **사용자 삭제**: tenant_id = session('selected_tenant_id')
- **부서/메뉴/역할 삭제**: 해당 레코드의 tenant_id
### 3. UI 변경
- 목록에 "대상 테넌트" 컬럼 추가
- 존재하는 테넌트: 인디고 뱃지로 표시
- 삭제된 테넌트: 회색 뱃지로 "(삭제됨 ID: N)" 표시
- 테넌트 없음: "-" 표시
### 4. 필터링
- ArchivedRecordService에 tenant_id 필터 추가
- filters['tenant_id']로 테넌트별 필터링 가능
---
## 남은 작업
### 6단계: 커밋 (대기)
- api/ 저장소: 마이그레이션 커밋
- mng/ 저장소: 모든 변경사항 커밋
- 사용자 승인 후 진행
---
## 중단 시 재개 가이드
1. 이 파일(`WORKFLOW_STATE.md`) 확인
2. 현재 상태: Phase 2 완료, 커밋 대기
3. 커밋 요청 시 변경 사항 요약 제공 후 커밋
---
## 참고 파일
- 분석 문서: `claudedocs/archive-restore-feature-analysis.md`
- ArchivedRecord 모델: `app/Models/Archives/ArchivedRecord.php`
- ArchivedRecordRelation 모델: `app/Models/Archives/ArchivedRecordRelation.php`
- ArchivedRecordService: `app/Services/ArchivedRecordService.php`
- ArchiveService: `app/Services/ArchiveService.php`
- RestoreService: `app/Services/RestoreService.php`
## 변경 파일 목록
- `resources/views/partials/sidebar.blade.php`
- HTML: 실험실 탭 기반 메뉴 구조 (~200줄)
- 확장 뷰: 헤더 + S|A|M 탭 + 각 탭 패널
- 축소 뷰: 아이콘 + 플라이아웃 팝업
- CSS: 탭 + 플라이아웃 스타일 (~95줄)
- 탭 버튼 active 상태
- 플라이아웃 애니메이션 (flyoutFadeIn)
- 확장/축소 상태별 뷰 전환
- JS: 탭 전환 함수 (~75줄)
- switchLabTab(): 확장 상태 탭 전환
- switchLabFlyoutTab(): 플라이아웃 탭 전환
- localStorage 상태 저장/복원

732
composer.lock generated

File diff suppressed because it is too large Load Diff

298
package-lock.json generated
View File

@@ -1302,18 +1302,6 @@
"node": ">=0.4.0"
}
},
"node_modules/detect-libc": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"dev": true,
"license": "Apache-2.0",
"optional": true,
"peer": true,
"engines": {
"node": ">=8"
}
},
"node_modules/didyoumean": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
@@ -1811,18 +1799,6 @@
"node": ">=0.12.0"
}
},
"node_modules/jiti": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
"integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"bin": {
"jiti": "lib/jiti-cli.mjs"
}
},
"node_modules/laravel-vite-plugin": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-2.0.1.tgz",
@@ -1843,280 +1819,6 @@
"vite": "^7.0.0"
}
},
"node_modules/lightningcss": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
"integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==",
"dev": true,
"license": "MPL-2.0",
"optional": true,
"peer": true,
"dependencies": {
"detect-libc": "^2.0.3"
},
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"lightningcss-android-arm64": "1.30.2",
"lightningcss-darwin-arm64": "1.30.2",
"lightningcss-darwin-x64": "1.30.2",
"lightningcss-freebsd-x64": "1.30.2",
"lightningcss-linux-arm-gnueabihf": "1.30.2",
"lightningcss-linux-arm64-gnu": "1.30.2",
"lightningcss-linux-arm64-musl": "1.30.2",
"lightningcss-linux-x64-gnu": "1.30.2",
"lightningcss-linux-x64-musl": "1.30.2",
"lightningcss-win32-arm64-msvc": "1.30.2",
"lightningcss-win32-x64-msvc": "1.30.2"
}
},
"node_modules/lightningcss-android-arm64": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz",
"integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"android"
],
"peer": true,
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-darwin-arm64": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz",
"integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-darwin-x64": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz",
"integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-freebsd-x64": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz",
"integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"freebsd"
],
"peer": true,
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm-gnueabihf": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz",
"integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm64-gnu": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz",
"integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm64-musl": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz",
"integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-x64-gnu": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz",
"integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-x64-musl": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz",
"integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-win32-arm64-msvc": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz",
"integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-win32-x64-msvc": {
"version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz",
"integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lilconfig": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",

View File

@@ -369,6 +369,189 @@ class="flex items-center gap-2 pr-3 py-2 rounded-lg text-sm text-gray-700 hover:
</ul>
</li>
<!-- 실험실 그룹 ( 스타일 + 플라이아웃) -->
<li class="lab-menu-container pt-4 pb-1 border-t border-gray-200 mt-2" id="lab-menu-container">
<!-- 확장 상태: 헤더 + + 메뉴 -->
<div class="lab-expanded-view">
<button onclick="toggleGroup('lab-group')" class="sidebar-group-header lab-group-header w-full flex items-center justify-between px-3 py-2 text-xs font-bold text-gray-700 uppercase tracking-wider rounded">
<span class="flex items-center gap-2">
<svg class="w-4 h-4 text-amber-600 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z" />
</svg>
<span class="sidebar-text">실험실</span>
</span>
<svg id="lab-group-icon" class="w-3 h-3 transition-transform sidebar-text" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</button>
<!-- + 메뉴 콘텐츠 -->
<div id="lab-group" class="mt-2">
<!-- S | A | M 버튼 -->
<div class="lab-tabs flex mx-2 mb-2 bg-gray-100 rounded-lg p-1">
<button type="button" onclick="switchLabTab('s')" id="lab-tab-s" class="lab-tab active flex-1 py-1.5 text-xs font-bold rounded-md transition-all text-blue-600">
S
</button>
<button type="button" onclick="switchLabTab('a')" id="lab-tab-a" class="lab-tab flex-1 py-1.5 text-xs font-bold rounded-md transition-all text-purple-600">
A
</button>
<button type="button" onclick="switchLabTab('m')" id="lab-tab-m" class="lab-tab flex-1 py-1.5 text-xs font-bold rounded-md transition-all text-green-600">
M
</button>
</div>
<!-- S. Strategy 메뉴 (16) -->
<ul id="lab-panel-s" class="lab-panel space-y-1">
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="세무 전략 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 7h6m0 10v-3m-3 3h.01M9 17h.01M9 14h.01M12 14h.01M15 11h.01M12 11h.01M9 11h.01M7 21h10a2 2 0 002-2V5a2 2 0 00-2-2H7a2 2 0 00-2 2v14a2 2 0 002 2z" /></svg><span class="font-medium sidebar-text">세무 전략</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="노무 전략 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z" /></svg><span class="font-medium sidebar-text">노무 전략</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="채권추심 전략 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg><span class="font-medium sidebar-text">채권추심 전략</span></span></li>
<li class="border-t border-gray-100 pt-1 mt-1"></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="스테이블코인 보고서 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /></svg><span class="font-medium sidebar-text">스테이블코인 보고서</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="MRP 해외사례 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg><span class="font-medium sidebar-text">MRP 해외사례</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="상담용 챗봇 전략 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" /></svg><span class="font-medium sidebar-text">상담용 챗봇 전략</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="KoDATA vs NICE API 비교 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" /></svg><span class="font-medium sidebar-text">KoDATA vs NICE API</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="바로빌 vs 팝빌 API (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" /></svg><span class="font-medium sidebar-text">바로빌 vs 팝빌 API</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="사내 지식 검색 시스템 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /></svg><span class="font-medium sidebar-text">사내 지식 검색 시스템</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="챗봇 솔루션 비교 분석 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01" /></svg><span class="font-medium sidebar-text">챗봇 솔루션 비교 분석</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="RAG 스타트업 현황 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg><span class="font-medium sidebar-text">RAG 스타트업 현황</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="더존비즈온 분석 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" /></svg><span class="font-medium sidebar-text">더존비즈온 분석</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="Confluence vs Notion (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7v8a2 2 0 002 2h6M8 7V5a2 2 0 012-2h4.586a1 1 0 01.707.293l4.414 4.414a1 1 0 01.293.707V15a2 2 0 01-2 2h-2M8 7H6a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2v-2" /></svg><span class="font-medium sidebar-text">Confluence vs Notion</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="차세대 QA 솔루션 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg><span class="font-medium sidebar-text">차세대 QA 솔루션</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="SAM 영업전략 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" /></svg><span class="font-medium sidebar-text">SAM 영업전략</span></span></li>
</ul>
<!-- A. AI/Automation 메뉴 (12) -->
<ul id="lab-panel-a" class="lab-panel space-y-1 hidden">
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="사업자등록증 OCR (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /></svg><span class="font-medium sidebar-text">사업자등록증 OCR</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="웹 녹음 AI 요약 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z" /></svg><span class="font-medium sidebar-text"> 녹음 AI 요약</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="회의록 AI 요약 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" /></svg><span class="font-medium sidebar-text">회의록 AI 요약</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="업무협의록 AI 요약 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" /></svg><span class="font-medium sidebar-text">업무협의록 AI 요약</span></span></li>
<li class="border-t border-gray-100 pt-1 mt-1"></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="운영자용 챗봇 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" /></svg><span class="font-medium sidebar-text">운영자용 챗봇</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="Vertex RAG 챗봇 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" /></svg><span class="font-medium sidebar-text">Vertex RAG 챗봇</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="테넌트 지식 업로드 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" /></svg><span class="font-medium sidebar-text">테넌트 지식 업로드</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="테넌트 챗봇 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z" /></svg><span class="font-medium sidebar-text">테넌트 챗봇</span></span></li>
<li class="border-t border-gray-100 pt-1 mt-1"></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="SAM AI 메뉴 이동 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg><span class="font-medium sidebar-text">SAM AI 메뉴 이동</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="SAM AI 알람음 제작 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.536 8.464a5 5 0 010 7.072m2.828-9.9a9 9 0 010 12.728M5.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z" /></svg><span class="font-medium sidebar-text">SAM AI 알람음 제작</span></span></li>
<li class="border-t border-gray-100 pt-1 mt-1"></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="GPS 출퇴근 관리 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" /><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" /></svg><span class="font-medium sidebar-text">GPS 출퇴근 관리</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="기업개황 조회 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" /></svg><span class="font-medium sidebar-text">기업개황 조회</span></span></li>
</ul>
<!-- M. Management 메뉴 (13) -->
<ul id="lab-panel-m" class="lab-panel space-y-1 hidden">
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="바로빌 테넌트 관리 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" /></svg><span class="font-medium sidebar-text">바로빌 테넌트 관리</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="전자세금계산서 전략 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 14l6-6m-5.5.5h.01m4.99 5h.01M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16l3.5-2 3.5 2 3.5-2 3.5 2z" /></svg><span class="font-medium sidebar-text">전자세금계산서 전략</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="전자세금계산서 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 14l6-6m-5.5.5h.01m4.99 5h.01M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16l3.5-2 3.5 2 3.5-2 3.5 2zM10 8.5a.5.5 0 11-1 0 .5.5 0 011 0zm5 5a.5.5 0 11-1 0 .5.5 0 011 0z" /></svg><span class="font-medium sidebar-text">전자세금계산서</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="사업자등록번호 진위 확인 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" /></svg><span class="font-medium sidebar-text">사업자등록번호 진위 확인</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="영업관리 & 매니저 미팅관리 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" /></svg><span class="font-medium sidebar-text">영업관리 & 매니저 미팅관리</span></span></li>
<li class="border-t border-gray-100 pt-1 mt-1"></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="카드 세무항목 매칭 전략 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" /></svg><span class="font-medium sidebar-text">카드 세무항목 매칭 전략</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="한국 카드사 API 보고서 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /></svg><span class="font-medium sidebar-text">한국 카드사 API 보고서</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="카드 사용내역 수집 후 매칭 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4" /></svg><span class="font-medium sidebar-text">카드 사용내역 수집 매칭</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="계좌입출금 내역 조회 API (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 6l3 1m0 0l-3 9a5.002 5.002 0 006.001 0M6 7l3 9M6 7l6-2m6 2l3-1m-3 1l-3 9a5.002 5.002 0 006.001 0M18 7l3 9m-3-9l-6-2m0-2v2m0 16V5m0 16H9m3 0h3" /></svg><span class="font-medium sidebar-text">계좌입출금 내역 조회 API</span></span></li>
<li class="border-t border-gray-100 pt-1 mt-1"></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="영업관리 시나리오 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /></svg><span class="font-medium sidebar-text">영업관리 시나리오</span></span></li>
<li><span class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-400 cursor-not-allowed" title="매니저 시나리오 (준비중)"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01" /></svg><span class="font-medium sidebar-text">매니저 시나리오</span></span></li>
</ul>
</div>
</div>
<!-- 축소 상태: 아이콘 + 플라이아웃 -->
<div class="lab-collapsed-view hidden">
<div class="lab-flyout-trigger relative">
<button type="button" class="flex items-center justify-center w-full p-2 rounded-lg hover:bg-amber-50 transition-colors" title="실험실">
<svg class="w-5 h-5 text-amber-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z" />
</svg>
</button>
<!-- 플라이아웃 팝업 -->
<div class="lab-flyout hidden absolute left-full top-0 ml-2 w-56 bg-white rounded-lg shadow-xl border border-gray-200 z-[1000]">
<!-- 화살표 -->
<div class="absolute -left-2 top-3 w-0 h-0 border-t-8 border-b-8 border-r-8 border-transparent border-r-white"></div>
<div class="absolute -left-[9px] top-3 w-0 h-0 border-t-8 border-b-8 border-r-8 border-transparent border-r-gray-200"></div>
<!-- 헤더 -->
<div class="px-3 py-2 bg-gradient-to-r from-amber-50 to-amber-100 rounded-t-lg border-b border-amber-200">
<span class="text-xs font-bold text-amber-800 uppercase tracking-wider">실험실</span>
</div>
<!-- -->
<div class="flex p-2 bg-gray-50 border-b border-gray-100">
<button type="button" onclick="switchLabFlyoutTab('s')" id="lab-flyout-tab-s" class="lab-flyout-tab active flex-1 py-1.5 text-xs font-bold rounded transition-all text-blue-600">
S
</button>
<button type="button" onclick="switchLabFlyoutTab('a')" id="lab-flyout-tab-a" class="lab-flyout-tab flex-1 py-1.5 text-xs font-bold rounded transition-all text-purple-600">
A
</button>
<button type="button" onclick="switchLabFlyoutTab('m')" id="lab-flyout-tab-m" class="lab-flyout-tab flex-1 py-1.5 text-xs font-bold rounded transition-all text-green-600">
M
</button>
</div>
<!-- 메뉴 패널들 -->
<div class="p-2 max-h-64 overflow-y-auto">
<!-- S. Strategy (16) -->
<ul id="lab-flyout-panel-s" class="lab-flyout-panel space-y-0.5">
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">세무 전략</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">노무 전략</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">채권추심 전략</span></li>
<li class="border-t border-gray-100 my-1"></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">스테이블코인 보고서</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">MRP 해외사례</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">상담용 챗봇 전략</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">KoDATA vs NICE API</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">바로빌 vs 팝빌 API</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">사내 지식 검색 시스템</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">챗봇 솔루션 비교 분석</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">RAG 스타트업 현황</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">더존비즈온 분석</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">Confluence vs Notion</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">차세대 QA 솔루션</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">SAM 영업전략</span></li>
</ul>
<!-- A. AI/Automation (12) -->
<ul id="lab-flyout-panel-a" class="lab-flyout-panel space-y-0.5 hidden">
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">사업자등록증 OCR</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50"> 녹음 AI 요약</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">회의록 AI 요약</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">업무협의록 AI 요약</span></li>
<li class="border-t border-gray-100 my-1"></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">운영자용 챗봇</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">Vertex RAG 챗봇</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">테넌트 지식 업로드</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">테넌트 챗봇</span></li>
<li class="border-t border-gray-100 my-1"></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">SAM AI 메뉴 이동</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">SAM AI 알람음 제작</span></li>
<li class="border-t border-gray-100 my-1"></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">GPS 출퇴근 관리</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">기업개황 조회</span></li>
</ul>
<!-- M. Management (13) -->
<ul id="lab-flyout-panel-m" class="lab-flyout-panel space-y-0.5 hidden">
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">바로빌 테넌트 관리</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">전자세금계산서 전략</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">전자세금계산서</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">사업자등록번호 진위 확인</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">영업관리 & 매니저 미팅</span></li>
<li class="border-t border-gray-100 my-1"></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">카드 세무항목 매칭 전략</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">한국 카드사 API 보고서</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">카드 사용내역 수집 매칭</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">계좌입출금 내역 조회 API</span></li>
<li class="border-t border-gray-100 my-1"></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">영업관리 시나리오</span></li>
<li><span class="block px-2 py-1 text-xs text-gray-400 rounded hover:bg-gray-50">매니저 시나리오</span></li>
</ul>
</div>
</div>
</div>
</div>
</li>
</ul>
</nav>
@@ -584,6 +767,179 @@ class="flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-gray-700 hover:
opacity: 1;
visibility: visible;
}
/* ========== 3단계 메뉴 스타일 ========== */
/* 서브그룹 헤더 (2단계) */
.sidebar-subgroup-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.5rem 0.75rem;
margin-left: 1.5rem;
font-size: 0.75rem;
font-weight: 600;
color: #6b7280;
text-transform: uppercase;
letter-spacing: 0.025em;
cursor: pointer;
border-radius: 0.375rem;
transition: background-color 0.15s;
}
.sidebar-subgroup-header:hover {
background-color: #f3f4f6;
}
/* 서브그룹 컨테이너 */
.sidebar-subgroup-items {
margin-left: 0.5rem;
border-left: 2px solid #e5e7eb;
padding-left: 0.5rem;
}
/* 3단계 메뉴 아이템 들여쓰기 */
.sidebar-subgroup-items a,
.sidebar-subgroup-items span {
padding-left: 2.5rem !important;
}
/* 서브그룹 아이콘 회전 */
.sidebar-subgroup-header .subgroup-icon {
transition: transform 0.2s;
}
.sidebar-subgroup-header.collapsed .subgroup-icon {
transform: rotate(-90deg);
}
/* 접힌 상태에서 서브그룹 헤더 숨김 */
html.sidebar-is-collapsed #sidebar .sidebar-subgroup-header,
.sidebar.sidebar-collapsed .sidebar-subgroup-header {
display: none;
}
/* 접힌 상태에서 서브그룹 아이템 border 숨김 */
html.sidebar-is-collapsed #sidebar .sidebar-subgroup-items,
.sidebar.sidebar-collapsed .sidebar-subgroup-items {
margin-left: 0;
border-left: none;
padding-left: 0;
}
/* 접힌 상태에서 3단계 메뉴 아이템 패딩 리셋 */
html.sidebar-is-collapsed #sidebar .sidebar-subgroup-items a,
html.sidebar-is-collapsed #sidebar .sidebar-subgroup-items span,
.sidebar.sidebar-collapsed .sidebar-subgroup-items a,
.sidebar.sidebar-collapsed .sidebar-subgroup-items span {
padding-left: 0.75rem !important;
}
/* 실험실 그룹 특별 스타일 */
.lab-group-header {
background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
border: 1px solid #f59e0b;
}
.lab-group-header:hover {
background: linear-gradient(135deg, #fde68a 0%, #fcd34d 100%);
}
/* ========== 실험실 탭 + 플라이아웃 스타일 ========== */
/* 탭 버튼 스타일 */
.lab-tabs .lab-tab {
background: transparent;
border: none;
cursor: pointer;
}
.lab-tabs .lab-tab.active {
background: white;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.lab-tabs .lab-tab:not(.active):hover {
background: rgba(255, 255, 255, 0.5);
}
/* 확장 상태에서 축소 뷰 숨김 */
.lab-expanded-view {
display: block;
}
.lab-collapsed-view {
display: none !important;
}
/* 축소 상태에서 확장 뷰 숨김, 축소 뷰 표시 */
html.sidebar-is-collapsed #sidebar .lab-expanded-view,
.sidebar.sidebar-collapsed .lab-expanded-view {
display: none !important;
}
html.sidebar-is-collapsed #sidebar .lab-collapsed-view,
.sidebar.sidebar-collapsed .lab-collapsed-view {
display: block !important;
}
/* 플라이아웃 트리거 */
.lab-flyout-trigger {
position: relative;
}
/* 플라이아웃 표시 (호버 시) */
.lab-flyout-trigger:hover .lab-flyout {
display: block !important;
animation: flyoutFadeIn 0.15s ease-out;
}
@keyframes flyoutFadeIn {
from {
opacity: 0;
transform: translateX(-8px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
/* 플라이아웃 탭 스타일 */
.lab-flyout-tab {
background: transparent;
border: none;
cursor: pointer;
}
.lab-flyout-tab.active {
background: white;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
.lab-flyout-tab:not(.active):hover {
background: rgba(255, 255, 255, 0.7);
}
/* 접힌 상태에서 실험실 메뉴 아이템 스타일 리셋 */
html.sidebar-is-collapsed #sidebar .lab-panel span,
.sidebar.sidebar-collapsed .lab-panel span {
display: none;
}
/* 접힌 상태에서 탭 숨김 */
html.sidebar-is-collapsed #sidebar .lab-tabs,
.sidebar.sidebar-collapsed .lab-tabs {
display: none;
}
/* 접힌 상태에서 실험실 그룹 border-t 유지 */
html.sidebar-is-collapsed #sidebar .lab-menu-container,
.sidebar.sidebar-collapsed .lab-menu-container {
border-top: 1px solid #e5e7eb;
padding-top: 1rem;
margin-top: 0.5rem;
}
</style>
<script>
@@ -608,18 +964,54 @@ function toggleGroup(groupId) {
}
}
// 서브그룹 토글 함수 (3단계 메뉴용)
function toggleSubgroup(subgroupId) {
const sidebar = document.getElementById('sidebar');
// 사이드바가 접힌 상태면 서브그룹 토글 무시
if (sidebar.classList.contains('sidebar-collapsed') || document.documentElement.classList.contains('sidebar-is-collapsed')) {
return;
}
const subgroup = document.getElementById(subgroupId);
const icon = document.getElementById(subgroupId + '-icon');
const header = icon ? icon.closest('.sidebar-subgroup-header') : null;
if (subgroup.style.display === 'none') {
subgroup.style.display = 'block';
if (header) header.classList.remove('collapsed');
localStorage.setItem('sidebar-' + subgroupId, 'open');
} else {
subgroup.style.display = 'none';
if (header) header.classList.add('collapsed');
localStorage.setItem('sidebar-' + subgroupId, 'closed');
}
}
// 페이지 로드 시 저장된 상태 복원
document.addEventListener('DOMContentLoaded', function() {
['system-group', 'permission-group', 'production-group', 'content-group', 'system-settings-group', 'pm-group', 'dev-tools-group'].forEach(function(groupId) {
// 1단계 그룹 상태 복원
['system-group', 'permission-group', 'production-group', 'content-group', 'system-settings-group', 'pm-group', 'dev-tools-group', 'lab-group'].forEach(function(groupId) {
const state = localStorage.getItem('sidebar-' + groupId);
if (state === 'closed') {
const group = document.getElementById(groupId);
const icon = document.getElementById(groupId + '-icon');
group.style.display = 'none';
icon.style.transform = 'rotate(-90deg)';
if (group) group.style.display = 'none';
if (icon) icon.style.transform = 'rotate(-90deg)';
}
});
// 서브그룹 (2단계) 상태 복원
['lab-s-group', 'lab-a-group', 'lab-m-group'].forEach(function(subgroupId) {
const state = localStorage.getItem('sidebar-' + subgroupId);
if (state === 'closed') {
const subgroup = document.getElementById(subgroupId);
const icon = document.getElementById(subgroupId + '-icon');
const header = icon ? icon.closest('.sidebar-subgroup-header') : null;
if (subgroup) subgroup.style.display = 'none';
if (header) header.classList.add('collapsed');
}
});
// 사이드바 접힌 상태에서 메뉴 아이템 호버 시 툴팁 표시
initSidebarTooltips();
});
@@ -702,4 +1094,79 @@ function initSidebarTooltips() {
}, 150);
});
}
// ========== 실험실 탭 전환 함수 ==========
// 확장 상태: 탭 전환
function switchLabTab(tabKey) {
const tabs = ['s', 'a', 'm'];
// 모든 탭 버튼에서 active 제거
tabs.forEach(function(key) {
const tab = document.getElementById('lab-tab-' + key);
const panel = document.getElementById('lab-panel-' + key);
if (tab) {
tab.classList.remove('active');
}
if (panel) {
panel.classList.add('hidden');
}
});
// 선택된 탭 활성화
const activeTab = document.getElementById('lab-tab-' + tabKey);
const activePanel = document.getElementById('lab-panel-' + tabKey);
if (activeTab) {
activeTab.classList.add('active');
}
if (activePanel) {
activePanel.classList.remove('hidden');
}
// 상태 저장
localStorage.setItem('lab-active-tab', tabKey);
}
// 축소 상태 (플라이아웃): 탭 전환
function switchLabFlyoutTab(tabKey) {
const tabs = ['s', 'a', 'm'];
// 모든 탭 버튼에서 active 제거
tabs.forEach(function(key) {
const tab = document.getElementById('lab-flyout-tab-' + key);
const panel = document.getElementById('lab-flyout-panel-' + key);
if (tab) {
tab.classList.remove('active');
}
if (panel) {
panel.classList.add('hidden');
}
});
// 선택된 탭 활성화
const activeTab = document.getElementById('lab-flyout-tab-' + tabKey);
const activePanel = document.getElementById('lab-flyout-panel-' + tabKey);
if (activeTab) {
activeTab.classList.add('active');
}
if (activePanel) {
activePanel.classList.remove('hidden');
}
// 상태 저장 (확장 탭과 동기화)
localStorage.setItem('lab-active-tab', tabKey);
}
// 페이지 로드 시 저장된 탭 상태 복원
document.addEventListener('DOMContentLoaded', function() {
const savedTab = localStorage.getItem('lab-active-tab');
if (savedTab && ['s', 'a', 'm'].includes(savedTab)) {
switchLabTab(savedTab);
switchLabFlyoutTab(savedTab);
}
});
</script>