From da20e3552ffc0c2ac45a0ba4e39285f9901d245c 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:24:51 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20[org-chart]=20=EB=B6=80=EC=84=9C=20?= =?UTF-8?q?=EC=88=A8=EA=B8=B0=EA=B8=B0=20=EC=83=81=ED=83=9C=20DB=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - departments.options JSON 컬럼에 orgchart_hidden 플래그 저장 - 숨기기/복원 시 API 호출하여 영구 저장 - 페이지 로드 시 DB에서 숨김 상태 복원 --- app/Http/Controllers/RdController.php | 27 ++++++++++++++++++++++++++ app/Models/Tenants/Department.php | 1 + resources/views/rd/org-chart.blade.php | 16 ++++++++++++++- routes/web.php | 1 + 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/RdController.php b/app/Http/Controllers/RdController.php index 4efb4ad5..ebf0f29e 100644 --- a/app/Http/Controllers/RdController.php +++ b/app/Http/Controllers/RdController.php @@ -177,6 +177,33 @@ public function orgChartReorderDepts(Request $request): JsonResponse return response()->json(['success' => true]); } + /** + * 조직도 - 부서 숨기기/표시 토글 + */ + public function orgChartToggleHide(Request $request): JsonResponse + { + $request->validate([ + 'department_id' => 'required|integer', + 'hidden' => 'required|boolean', + ]); + + $tenantId = session('selected_tenant_id'); + $dept = Department::where('tenant_id', $tenantId) + ->where('id', $request->department_id) + ->first(); + + if (! $dept) { + return response()->json(['success' => false, 'message' => '부서를 찾을 수 없습니다.'], 404); + } + + $options = $dept->options ?? []; + $options['orgchart_hidden'] = $request->hidden; + $dept->options = $options; + $dept->save(); + + return response()->json(['success' => true]); + } + /** * 중대재해처벌법 실무 점검 */ diff --git a/app/Models/Tenants/Department.php b/app/Models/Tenants/Department.php index 73ca3061..8309e8c4 100644 --- a/app/Models/Tenants/Department.php +++ b/app/Models/Tenants/Department.php @@ -26,6 +26,7 @@ class Department extends Model 'parent_id' => 'int', 'is_active' => 'bool', 'sort_order' => 'int', + 'options' => 'array', ]; protected $hidden = [ diff --git a/resources/views/rd/org-chart.blade.php b/resources/views/rd/org-chart.blade.php index 0c9d6347..0622c3f4 100644 --- a/resources/views/rd/org-chart.blade.php +++ b/resources/views/rd/org-chart.blade.php @@ -127,7 +127,7 @@ function orgChart() { deptSortables: [], empSortables: [], unassignedSortable: null, - hiddenDepts: new Set(), + hiddenDepts: new Set(@json($departments->filter(fn($d) => ($d->options['orgchart_hidden'] ?? false))->pluck('id')->values())), dblClickDept: null, execTitles: ['대표이사', '사장', '부사장', '회장', '부회장'], @@ -414,11 +414,25 @@ function orgChart() { this.hiddenDepts.add(deptId); this.dblClickDept = null; this.renderTree(); + this.saveHideState(deptId, true); }, restoreDept(deptId) { this.hiddenDepts.delete(deptId); this.renderTree(); + this.saveHideState(deptId, false); + }, + + async saveHideState(deptId, hidden) { + this.saving = true; + try { + await fetch('{{ route("rd.org-chart.toggle-hide") }}', { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content, 'Accept': 'application/json' }, + body: JSON.stringify({ department_id: deptId, hidden }), + }); + } catch (e) { console.error('숨기기 저장 실패:', e); } + finally { this.saving = false; } }, get hiddenDeptList() { diff --git a/routes/web.php b/routes/web.php index 4db0a7d4..7ecbbace 100644 --- a/routes/web.php +++ b/routes/web.php @@ -393,6 +393,7 @@ Route::post('/org-chart/unassign', [RdController::class, 'orgChartUnassign'])->name('org-chart.unassign'); Route::post('/org-chart/reorder', [RdController::class, 'orgChartReorder'])->name('org-chart.reorder'); Route::post('/org-chart/reorder-depts', [RdController::class, 'orgChartReorderDepts'])->name('org-chart.reorder-depts'); + Route::post('/org-chart/toggle-hide', [RdController::class, 'orgChartToggleHide'])->name('org-chart.toggle-hide'); // 중대재해처벌법 실무 점검 Route::get('/safety-audit', [RdController::class, 'safetyAudit'])->name('safety-audit');