diff --git a/app/Http/Controllers/Sales/SalesManagerController.php b/app/Http/Controllers/Sales/SalesManagerController.php index 1d62eab7..5eb0bc9f 100644 --- a/app/Http/Controllers/Sales/SalesManagerController.php +++ b/app/Http/Controllers/Sales/SalesManagerController.php @@ -332,4 +332,81 @@ public function deleteDocument(int $id, int $documentId) return redirect()->back() ->with('success', '서류가 삭제되었습니다.'); } + + /** + * 영업파트너 승인 목록 (본사 관리자 전용) + */ + public function approvals(Request $request): View|Response + { + // 권한 체크: admin 역할만 접근 가능 + if (!auth()->user()->isAdmin()) { + abort(403, '접근 권한이 없습니다.'); + } + + if ($request->header('HX-Request')) { + return response('', 200)->header('HX-Redirect', route('sales.managers.approvals')); + } + + $filters = [ + 'search' => $request->get('search'), + 'approval_status' => 'pending', // 승인 대기만 + ]; + + $partners = $this->service->getSalesPartners($filters)->paginate(20); + $stats = $this->service->getApprovalStats(); + + return view('sales.managers.approvals', compact('partners', 'stats')); + } + + /** + * 승인 목록에서 승인 처리 + */ + public function approveFromList(Request $request, int $id) + { + // 권한 체크 + if (!auth()->user()->isAdmin()) { + abort(403, '접근 권한이 없습니다.'); + } + + $partner = User::findOrFail($id); + $this->service->approve($partner, auth()->id()); + + if ($request->header('HX-Request')) { + return response()->json([ + 'success' => true, + 'message' => "{$partner->name}님이 승인되었습니다.", + ]); + } + + return redirect()->route('sales.managers.approvals') + ->with('success', "{$partner->name}님이 승인되었습니다."); + } + + /** + * 승인 목록에서 반려 처리 + */ + public function rejectFromList(Request $request, int $id) + { + // 권한 체크 + if (!auth()->user()->isAdmin()) { + abort(403, '접근 권한이 없습니다.'); + } + + $validated = $request->validate([ + 'rejection_reason' => 'required|string|max:1000', + ]); + + $partner = User::findOrFail($id); + $this->service->reject($partner, auth()->id(), $validated['rejection_reason']); + + if ($request->header('HX-Request')) { + return response()->json([ + 'success' => true, + 'message' => "{$partner->name}님이 반려되었습니다.", + ]); + } + + return redirect()->route('sales.managers.approvals') + ->with('success', "{$partner->name}님이 반려되었습니다."); + } } diff --git a/app/Services/Sales/SalesManagerService.php b/app/Services/Sales/SalesManagerService.php index 1a25036b..270b9d6e 100644 --- a/app/Services/Sales/SalesManagerService.php +++ b/app/Services/Sales/SalesManagerService.php @@ -491,6 +491,34 @@ public function getStats(?int $parentId = null): array ]; } + /** + * 승인 대기 통계 조회 (본사 관리자용) + */ + public function getApprovalStats(): array + { + $tenantId = session('selected_tenant_id', 1); + + $baseQuery = User::query() + ->whereHas('userRoles', function ($q) use ($tenantId) { + $q->where('tenant_id', $tenantId) + ->whereHas('role', function ($rq) { + $rq->whereIn('name', self::SALES_ROLES); + }); + }); + + return [ + 'pending' => (clone $baseQuery)->where('approval_status', 'pending')->count(), + 'approved_today' => (clone $baseQuery) + ->where('approval_status', 'approved') + ->whereDate('approved_at', today()) + ->count(), + 'rejected_today' => (clone $baseQuery) + ->where('approval_status', 'rejected') + ->whereDate('approved_at', today()) + ->count(), + ]; + } + /** * 파트너의 계층 레벨 계산 */ diff --git a/resources/views/sales/managers/approvals.blade.php b/resources/views/sales/managers/approvals.blade.php new file mode 100644 index 00000000..d5a82c77 --- /dev/null +++ b/resources/views/sales/managers/approvals.blade.php @@ -0,0 +1,335 @@ +@extends('layouts.app') + +@section('title', '영업파트너 승인') + +@section('content') +
+ +
+
+

영업파트너 승인

+

영업파트너 가입 신청을 검토하고 승인/반려합니다

+
+
+ + +
+
+
승인 대기
+
{{ number_format($stats['pending']) }}명
+
+
+
오늘 승인
+
{{ number_format($stats['approved_today']) }}명
+
+
+
오늘 반려
+
{{ number_format($stats['rejected_today']) }}명
+
+
+ + +
+
+
+ +
+ +
+
+ + +
+
+ + + + + + + + + + + + + + @forelse($partners as $partner) + + + + + + + + + + @empty + + + + @endforelse + +
신청자아이디역할연락처추천인(유치자)신청일처리
+
{{ $partner->name }}
+ @if($partner->email) +
{{ $partner->email }}
+ @endif +
+ {{ $partner->user_id ?? '-' }} + +
+ @foreach($partner->userRoles as $userRole) + @php + $roleColor = match($userRole->role->name ?? '') { + 'sales' => 'bg-blue-100 text-blue-800', + 'manager' => 'bg-purple-100 text-purple-800', + 'recruiter' => 'bg-green-100 text-green-800', + default => 'bg-gray-100 text-gray-800', + }; + $roleLabel = match($userRole->role->name ?? '') { + 'sales' => '영업', + 'manager' => '매니저', + 'recruiter' => '유치담당', + default => $userRole->role->name ?? '-', + }; + @endphp + + {{ $roleLabel }} + + @endforeach +
+
+ {{ $partner->phone ?? '-' }} + + @if($partner->parent) + {{ $partner->parent->name }} + @else + - + @endif + + {{ $partner->created_at->format('Y-m-d H:i') }} + +
+ + + +
+
+ 승인 대기 중인 영업파트너가 없습니다. +
+
+ + + @if($partners->hasPages()) +
+ {{ $partners->withQueryString()->links() }} +
+ @endif +
+
+ + + + + + +@endsection + +@push('scripts') + +@endpush diff --git a/routes/web.php b/routes/web.php index 90a1984e..fb5210d8 100644 --- a/routes/web.php +++ b/routes/web.php @@ -868,6 +868,11 @@ Route::get('salesmanagement/dashboard/refresh', [\App\Http\Controllers\Sales\SalesDashboardController::class, 'refresh'])->name('salesmanagement.dashboard.refresh'); Route::get('salesmanagement/dashboard/tenants', [\App\Http\Controllers\Sales\SalesDashboardController::class, 'refreshTenantList'])->name('salesmanagement.dashboard.tenants'); + // 영업파트너 승인 (본사 관리자 전용) - resource 전에 정의해야 함 + Route::get('managers/approvals', [\App\Http\Controllers\Sales\SalesManagerController::class, 'approvals'])->name('managers.approvals'); + Route::post('managers/approvals/{id}/approve', [\App\Http\Controllers\Sales\SalesManagerController::class, 'approveFromList'])->name('managers.approvals.approve'); + Route::post('managers/approvals/{id}/reject', [\App\Http\Controllers\Sales\SalesManagerController::class, 'rejectFromList'])->name('managers.approvals.reject'); + // 영업 담당자 관리 Route::resource('managers', \App\Http\Controllers\Sales\SalesManagerController::class); Route::get('managers/{id}/modal-show', [\App\Http\Controllers\Sales\SalesManagerController::class, 'modalShow'])->name('managers.modal-show');