feat: [rd] 조직도 관리 화면 추가
- SortableJS 기반 drag & drop 부서 배치 UI - 미배치 직원 패널 + 부서 트리 (3단계 계층 지원) - 직원 배치/해제 API 엔드포인트 - 실시간 저장 및 인원수 표시
This commit is contained in:
@@ -2,8 +2,11 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\HR\Employee;
|
||||
use App\Models\Rd\AiQuotation;
|
||||
use App\Models\Tenants\Department;
|
||||
use App\Services\Rd\AiQuotationService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
@@ -28,6 +31,114 @@ public function index(Request $request): View|\Illuminate\Http\Response
|
||||
return view('rd.index', compact('dashboard', 'statuses'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 조직도 관리
|
||||
*/
|
||||
public function orgChart(Request $request): View|\Illuminate\Http\Response
|
||||
{
|
||||
if ($request->header('HX-Request')) {
|
||||
return response('', 200)->header('HX-Redirect', route('rd.org-chart'));
|
||||
}
|
||||
|
||||
$tenantId = session('selected_tenant_id');
|
||||
|
||||
// 부서 트리 (parent_id=null이 최상위)
|
||||
$departments = Department::where('tenant_id', $tenantId)
|
||||
->where('is_active', true)
|
||||
->orderBy('sort_order')
|
||||
->orderBy('name')
|
||||
->get();
|
||||
|
||||
// 전체 직원 (활성 상태)
|
||||
$employees = Employee::withoutGlobalScopes()
|
||||
->where('tenant_id', $tenantId)
|
||||
->where('employee_status', 'active')
|
||||
->with(['user', 'department'])
|
||||
->orderBy('display_name')
|
||||
->get();
|
||||
|
||||
// 미배치 직원 (department_id가 null)
|
||||
$unassigned = $employees->whereNull('department_id');
|
||||
// 배치된 직원을 부서별로 그룹핑
|
||||
$assignedByDept = $employees->whereNotNull('department_id')->groupBy('department_id');
|
||||
|
||||
return view('rd.org-chart', compact('departments', 'employees', 'unassigned', 'assignedByDept'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 조직도 - 직원 부서 배치
|
||||
*/
|
||||
public function orgChartAssign(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'employee_id' => 'required|integer',
|
||||
'department_id' => 'required|integer',
|
||||
]);
|
||||
|
||||
$tenantId = session('selected_tenant_id');
|
||||
$employee = Employee::withoutGlobalScopes()
|
||||
->where('tenant_id', $tenantId)
|
||||
->where('id', $request->employee_id)
|
||||
->first();
|
||||
|
||||
if (! $employee) {
|
||||
return response()->json(['success' => false, 'message' => '직원을 찾을 수 없습니다.'], 404);
|
||||
}
|
||||
|
||||
$employee->department_id = $request->department_id;
|
||||
$employee->save();
|
||||
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 조직도 - 직원 부서 해제 (미배치로 이동)
|
||||
*/
|
||||
public function orgChartUnassign(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'employee_id' => 'required|integer',
|
||||
]);
|
||||
|
||||
$tenantId = session('selected_tenant_id');
|
||||
$employee = Employee::withoutGlobalScopes()
|
||||
->where('tenant_id', $tenantId)
|
||||
->where('id', $request->employee_id)
|
||||
->first();
|
||||
|
||||
if (! $employee) {
|
||||
return response()->json(['success' => false, 'message' => '직원을 찾을 수 없습니다.'], 404);
|
||||
}
|
||||
|
||||
$employee->department_id = null;
|
||||
$employee->save();
|
||||
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 조직도 - 부서 내 직원 순서/이동 일괄 처리
|
||||
*/
|
||||
public function orgChartReorder(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'moves' => 'required|array',
|
||||
'moves.*.employee_id' => 'required|integer',
|
||||
'moves.*.department_id' => 'nullable|integer',
|
||||
]);
|
||||
|
||||
$tenantId = session('selected_tenant_id');
|
||||
|
||||
foreach ($request->moves as $move) {
|
||||
Employee::withoutGlobalScopes()
|
||||
->where('tenant_id', $tenantId)
|
||||
->where('id', $move['employee_id'])
|
||||
->update(['department_id' => $move['department_id']]);
|
||||
}
|
||||
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 중대재해처벌법 실무 점검
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user