feat:영업관리 대시보드 HTMX 부분 새로고침 구현
- 기간별 조회 및 실적 새로고침 시 전체 페이지가 아닌 데이터 영역만 갱신 - partial 뷰 분리 (stats, commission-by-role, tenant-stats, no-data) - 컨트롤러에 refresh 메서드 추가 - 로딩 인디케이터 추가 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,26 @@ class SalesDashboardController extends Controller
|
||||
* 대시보드 화면
|
||||
*/
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$data = $this->getDashboardData($request);
|
||||
|
||||
return view('sales.dashboard.index', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* HTMX 부분 새로고침용 데이터 반환
|
||||
*/
|
||||
public function refresh(Request $request): View
|
||||
{
|
||||
$data = $this->getDashboardData($request);
|
||||
|
||||
return view('sales.dashboard.partials.data-container', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 대시보드 데이터 조회
|
||||
*/
|
||||
private function getDashboardData(Request $request): array
|
||||
{
|
||||
// 기간 설정
|
||||
$period = $request->input('period', 'month'); // month or custom
|
||||
@@ -73,7 +93,7 @@ public function index(Request $request): View
|
||||
'confirmed_commission' => 0, // 확정 가입비 수당
|
||||
];
|
||||
|
||||
return view('sales.dashboard.index', compact(
|
||||
return compact(
|
||||
'stats',
|
||||
'commissionByRole',
|
||||
'totalCommissionRatio',
|
||||
@@ -83,6 +103,6 @@ public function index(Request $request): View
|
||||
'month',
|
||||
'startDate',
|
||||
'endDate'
|
||||
));
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,20 @@
|
||||
|
||||
@section('title', '영업관리 대시보드')
|
||||
|
||||
@push('styles')
|
||||
<style>
|
||||
.htmx-indicator {
|
||||
display: none;
|
||||
}
|
||||
.htmx-request .htmx-indicator {
|
||||
display: inline-flex;
|
||||
}
|
||||
.htmx-request.htmx-indicator {
|
||||
display: inline-flex;
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<!-- 페이지 헤더 -->
|
||||
@@ -24,349 +38,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 전체 누적 실적 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-6">
|
||||
<h2 class="text-lg font-bold text-gray-800 mb-4">전체 누적 실적</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-5 gap-4">
|
||||
<!-- 총 가입비 -->
|
||||
<div class="bg-white border border-gray-200 rounded-xl p-4 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-start justify-between">
|
||||
<span class="text-sm text-gray-500">총 가입비</span>
|
||||
<div class="p-2 bg-gray-100 rounded-lg">
|
||||
<svg class="w-5 h-5 text-gray-600" 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>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-2xl font-bold text-gray-900 mt-2">₩{{ number_format($stats['total_membership_fee']) }}</p>
|
||||
<p class="text-xs text-gray-400 mt-1">전체 누적 가입비</p>
|
||||
</div>
|
||||
|
||||
<!-- 총 수당 -->
|
||||
<div class="bg-blue-50 border border-blue-200 rounded-xl p-4 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-start justify-between">
|
||||
<span class="text-sm text-blue-600">총 수당</span>
|
||||
<div class="p-2 bg-blue-100 rounded-lg">
|
||||
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 9V7a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2m2 4h10a2 2 0 002-2v-6a2 2 0 00-2-2H9a2 2 0 00-2 2v6a2 2 0 002 2zm7-5a2 2 0 11-4 0 2 2 0 014 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-2xl font-bold text-blue-700 mt-2">₩{{ number_format($stats['total_commission']) }}</p>
|
||||
<p class="text-xs text-blue-500 mt-1">지급 승인 완료 기준 ({{ $stats['commission_rate'] }}%)</p>
|
||||
</div>
|
||||
|
||||
<!-- 전체 건수 -->
|
||||
<div class="bg-white border border-gray-200 rounded-xl p-4 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-start justify-between">
|
||||
<span class="text-sm text-gray-500">전체 건수</span>
|
||||
<div class="p-2 bg-gray-100 rounded-lg">
|
||||
<svg class="w-5 h-5 text-gray-600" 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>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-2xl font-bold text-gray-900 mt-2">{{ number_format($stats['total_contracts']) }}건</p>
|
||||
<p class="text-xs text-gray-400 mt-1">전체 계약 건수</p>
|
||||
</div>
|
||||
|
||||
<!-- 가입 승인 대기 -->
|
||||
<div class="bg-white border border-gray-200 rounded-xl p-4 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-start justify-between">
|
||||
<span class="text-sm text-gray-500">가입 승인 대기</span>
|
||||
<div class="p-2 bg-gray-100 rounded-lg">
|
||||
<svg class="w-5 h-5 text-gray-600" 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>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-2xl font-bold text-gray-900 mt-2">{{ number_format($stats['pending_membership_approval']) }}건</p>
|
||||
<p class="text-xs text-gray-400 mt-1">조직 내 가입 승인 대기</p>
|
||||
</div>
|
||||
|
||||
<!-- 지급 승인 대기 -->
|
||||
<div class="bg-white border border-gray-200 rounded-xl p-4 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-start justify-between">
|
||||
<span class="text-sm text-gray-500">지급 승인 대기</span>
|
||||
<div class="p-2 bg-gray-100 rounded-lg">
|
||||
<svg class="w-5 h-5 text-gray-600" 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-6 9l2 2 4-4" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-2xl font-bold text-gray-900 mt-2">{{ number_format($stats['pending_payment_approval']) }}건</p>
|
||||
<p class="text-xs text-gray-400 mt-1">조직 내 지급 승인 대기</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 기간별 조회 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-6">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<svg class="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||
</svg>
|
||||
<h2 class="text-lg font-bold text-gray-800">기간별 조회</h2>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<div class="inline-flex rounded-lg border border-gray-200 p-1">
|
||||
<button type="button"
|
||||
id="btn-month"
|
||||
class="px-4 py-2 text-sm font-medium rounded-md transition-colors {{ $period === 'month' ? 'bg-blue-600 text-white' : 'text-gray-600 hover:bg-gray-100' }}"
|
||||
onclick="togglePeriodMode('month')">
|
||||
당월
|
||||
</button>
|
||||
<button type="button"
|
||||
id="btn-custom"
|
||||
class="px-4 py-2 text-sm font-medium rounded-md transition-colors {{ $period === 'custom' ? 'bg-blue-600 text-white' : 'text-gray-600 hover:bg-gray-100' }}"
|
||||
onclick="togglePeriodMode('custom')">
|
||||
기간 설정
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 당월 표시 -->
|
||||
<div id="month-display" class="{{ $period === 'custom' ? 'hidden' : '' }}">
|
||||
<span class="text-sm text-gray-600">{{ $year }}년 {{ $month }}월</span>
|
||||
</div>
|
||||
|
||||
<!-- 기간 설정 입력 -->
|
||||
<div id="custom-period" class="flex items-center gap-2 {{ $period === 'month' ? 'hidden' : '' }}">
|
||||
<input type="date"
|
||||
id="start-date"
|
||||
value="{{ $startDate ?? now()->startOfMonth()->format('Y-m-d') }}"
|
||||
class="px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
||||
<span class="text-gray-500">~</span>
|
||||
<input type="date"
|
||||
id="end-date"
|
||||
value="{{ $endDate ?? now()->format('Y-m-d') }}"
|
||||
class="px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
||||
<button type="button"
|
||||
onclick="applyCustomPeriod()"
|
||||
class="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700 transition-colors">
|
||||
조회
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 역할별 수당 상세 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-6">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<svg class="w-5 h-5 text-gray-600" 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>
|
||||
<h2 class="text-lg font-bold text-gray-800">역할별 수당 상세</h2>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
|
||||
@foreach($commissionByRole as $role)
|
||||
<div class="rounded-xl p-4 border
|
||||
@if($role['color'] === 'green') bg-green-50 border-green-200
|
||||
@elseif($role['color'] === 'blue') bg-blue-50 border-blue-200
|
||||
@elseif($role['color'] === 'red') bg-red-50 border-red-200
|
||||
@else bg-gray-50 border-gray-200
|
||||
@endif">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<svg class="w-5 h-5
|
||||
@if($role['color'] === 'green') text-green-600
|
||||
@elseif($role['color'] === 'blue') text-blue-600
|
||||
@elseif($role['color'] === 'red') text-red-600
|
||||
@else text-gray-600
|
||||
@endif" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||
</svg>
|
||||
<span class="text-sm font-medium
|
||||
@if($role['color'] === 'green') text-green-800
|
||||
@elseif($role['color'] === 'blue') text-blue-800
|
||||
@elseif($role['color'] === 'red') text-red-800
|
||||
@else text-gray-800
|
||||
@endif">{{ $role['name'] }}</span>
|
||||
</div>
|
||||
@if($role['rate'] !== null)
|
||||
<span class="text-sm font-semibold
|
||||
@if($role['color'] === 'green') text-green-600
|
||||
@elseif($role['color'] === 'blue') text-blue-600
|
||||
@else text-gray-600
|
||||
@endif">{{ $role['rate'] }}%</span>
|
||||
@else
|
||||
<span class="px-2 py-0.5 text-xs font-medium bg-red-100 text-red-700 rounded">별도</span>
|
||||
@endif
|
||||
</div>
|
||||
@if($role['amount'] !== null)
|
||||
<p class="text-2xl font-bold
|
||||
@if($role['color'] === 'green') text-green-700
|
||||
@elseif($role['color'] === 'blue') text-blue-700
|
||||
@else text-gray-700
|
||||
@endif">₩{{ number_format($role['amount']) }}</p>
|
||||
@else
|
||||
<p class="text-xl font-bold text-red-600">운영팀 산정</p>
|
||||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<!-- 총 가입비 대비 수당 -->
|
||||
<div class="bg-gray-50 rounded-xl p-4 border border-gray-200">
|
||||
<div class="flex items-center justify-end gap-4">
|
||||
<div class="text-right">
|
||||
<p class="text-2xl font-bold text-gray-900">₩{{ number_format($totalCommissionRatio) }}</p>
|
||||
<p class="text-sm text-gray-500">총 가입비 대비 수당</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 실적 데이터 없음 안내 (데이터가 없을 때만 표시) -->
|
||||
@if($stats['total_contracts'] == 0)
|
||||
<div class="bg-white rounded-xl shadow-sm p-12">
|
||||
<div class="flex flex-col items-center justify-center text-center">
|
||||
<div class="w-20 h-20 bg-blue-50 rounded-full flex items-center justify-center mb-6">
|
||||
<svg class="w-10 h-10 text-blue-400" 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 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold text-gray-800 mb-2">실적 데이터가 존재하지 않습니다</h3>
|
||||
<p class="text-gray-500 mb-2">선택한 기간 내에 등록된 계약 정보나 조직 구성 데이터가 없습니다.</p>
|
||||
<p class="text-gray-500 mb-6">아직 실적이 발생하지 않았거나, 시스템 동기화 중일 수 있습니다.</p>
|
||||
<button type="button"
|
||||
onclick="location.reload()"
|
||||
class="inline-flex items-center gap-2 px-6 py-3 bg-blue-600 text-white font-medium rounded-lg hover:bg-blue-700 transition-colors">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||
</svg>
|
||||
실적 데이터 새로고침
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- 수익 및 테넌트 관리 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-6">
|
||||
<div class="flex items-center gap-3 mb-6">
|
||||
<div class="p-2 bg-blue-100 rounded-lg">
|
||||
<svg class="w-6 h-6 text-blue-600" 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>
|
||||
</div>
|
||||
<h2 class="text-xl font-bold text-gray-800">수익 및 테넌트 관리</h2>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<!-- 관리 테넌트 -->
|
||||
<div class="bg-white border border-gray-200 rounded-xl p-5 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<span class="text-sm text-gray-500">관리 테넌트</span>
|
||||
<div class="p-2 bg-blue-50 rounded-lg">
|
||||
<svg class="w-5 h-5 text-blue-600" 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>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-3xl font-bold text-gray-900">{{ number_format($tenantStats['total_tenants'] ?? 0) }}개</p>
|
||||
<p class="text-xs text-gray-400 mt-1">등록된 총 업체 수</p>
|
||||
</div>
|
||||
|
||||
<!-- 총 가입비 실적 -->
|
||||
<div class="bg-white border border-gray-200 rounded-xl p-5 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<span class="text-sm text-gray-500">총 가입비 실적</span>
|
||||
<div class="p-2 bg-blue-50 rounded-lg">
|
||||
<svg class="w-5 h-5 text-blue-600" 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>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-3xl font-bold text-gray-900">₩{{ number_format($tenantStats['total_membership_revenue'] ?? 0) }}</p>
|
||||
<p class="text-xs text-gray-400 mt-1">전체 가입비 합계</p>
|
||||
</div>
|
||||
|
||||
<!-- 누적 가입비 수당 -->
|
||||
<div class="bg-white border border-gray-200 rounded-xl p-5 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<span class="text-sm text-gray-500">누적 가입비 수당</span>
|
||||
<div class="p-2 bg-blue-50 rounded-lg">
|
||||
<svg class="w-5 h-5 text-blue-600" 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>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-3xl font-bold text-gray-900">₩{{ number_format($tenantStats['total_commission_accumulated'] ?? 0) }}</p>
|
||||
<p class="text-xs text-gray-400 mt-1">전체 가입비 수당 합계</p>
|
||||
</div>
|
||||
|
||||
<!-- 확정 가입비 수당 (지급대상) -->
|
||||
<div class="bg-green-50 border border-green-200 rounded-xl p-5 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<span class="text-sm text-green-700">확정 가입비 수당 (지급대상)</span>
|
||||
<div class="p-2 bg-green-100 rounded-lg">
|
||||
<svg class="w-5 h-5 text-green-600" 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>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-3xl font-bold text-green-700">₩{{ number_format($tenantStats['confirmed_commission'] ?? 0) }}</p>
|
||||
<p class="text-xs text-green-600 mt-1">운영팀 승인 완료된 금액 (지급: 계약 익월 말일)</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 대시보드 데이터 컨테이너 (HTMX로 새로고침되는 영역) -->
|
||||
<div id="dashboard-data" class="space-y-6">
|
||||
@include('sales.dashboard.partials.data-container')
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
function togglePeriodMode(mode) {
|
||||
const btnMonth = document.getElementById('btn-month');
|
||||
const btnCustom = document.getElementById('btn-custom');
|
||||
const monthDisplay = document.getElementById('month-display');
|
||||
const customPeriod = document.getElementById('custom-period');
|
||||
|
||||
if (mode === 'month') {
|
||||
// 당월 모드
|
||||
btnMonth.classList.add('bg-blue-600', 'text-white');
|
||||
btnMonth.classList.remove('text-gray-600', 'hover:bg-gray-100');
|
||||
btnCustom.classList.remove('bg-blue-600', 'text-white');
|
||||
btnCustom.classList.add('text-gray-600', 'hover:bg-gray-100');
|
||||
monthDisplay.classList.remove('hidden');
|
||||
customPeriod.classList.add('hidden');
|
||||
|
||||
// 당월로 이동
|
||||
const url = new URL(window.location);
|
||||
url.searchParams.set('period', 'month');
|
||||
url.searchParams.delete('start_date');
|
||||
url.searchParams.delete('end_date');
|
||||
window.location = url;
|
||||
} else {
|
||||
// 기간 설정 모드
|
||||
btnCustom.classList.add('bg-blue-600', 'text-white');
|
||||
btnCustom.classList.remove('text-gray-600', 'hover:bg-gray-100');
|
||||
btnMonth.classList.remove('bg-blue-600', 'text-white');
|
||||
btnMonth.classList.add('text-gray-600', 'hover:bg-gray-100');
|
||||
customPeriod.classList.remove('hidden');
|
||||
monthDisplay.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function applyCustomPeriod() {
|
||||
const startDate = document.getElementById('start-date').value;
|
||||
const endDate = document.getElementById('end-date').value;
|
||||
|
||||
if (!startDate || !endDate) {
|
||||
alert('시작일과 종료일을 모두 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (startDate > endDate) {
|
||||
alert('시작일은 종료일보다 이전이어야 합니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
const url = new URL(window.location);
|
||||
url.searchParams.set('period', 'custom');
|
||||
url.searchParams.set('start_date', startDate);
|
||||
url.searchParams.set('end_date', endDate);
|
||||
window.location = url;
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
@endsection
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
{{-- 역할별 수당 상세 --}}
|
||||
<div class="bg-white rounded-xl shadow-sm p-6">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<svg class="w-5 h-5 text-gray-600" 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>
|
||||
<h2 class="text-lg font-bold text-gray-800">역할별 수당 상세</h2>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
|
||||
@foreach($commissionByRole as $role)
|
||||
<div class="rounded-xl p-4 border
|
||||
@if($role['color'] === 'green') bg-green-50 border-green-200
|
||||
@elseif($role['color'] === 'blue') bg-blue-50 border-blue-200
|
||||
@elseif($role['color'] === 'red') bg-red-50 border-red-200
|
||||
@else bg-gray-50 border-gray-200
|
||||
@endif">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<svg class="w-5 h-5
|
||||
@if($role['color'] === 'green') text-green-600
|
||||
@elseif($role['color'] === 'blue') text-blue-600
|
||||
@elseif($role['color'] === 'red') text-red-600
|
||||
@else text-gray-600
|
||||
@endif" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||
</svg>
|
||||
<span class="text-sm font-medium
|
||||
@if($role['color'] === 'green') text-green-800
|
||||
@elseif($role['color'] === 'blue') text-blue-800
|
||||
@elseif($role['color'] === 'red') text-red-800
|
||||
@else text-gray-800
|
||||
@endif">{{ $role['name'] }}</span>
|
||||
</div>
|
||||
@if($role['rate'] !== null)
|
||||
<span class="text-sm font-semibold
|
||||
@if($role['color'] === 'green') text-green-600
|
||||
@elseif($role['color'] === 'blue') text-blue-600
|
||||
@else text-gray-600
|
||||
@endif">{{ $role['rate'] }}%</span>
|
||||
@else
|
||||
<span class="px-2 py-0.5 text-xs font-medium bg-red-100 text-red-700 rounded">별도</span>
|
||||
@endif
|
||||
</div>
|
||||
@if($role['amount'] !== null)
|
||||
<p class="text-2xl font-bold
|
||||
@if($role['color'] === 'green') text-green-700
|
||||
@elseif($role['color'] === 'blue') text-blue-700
|
||||
@else text-gray-700
|
||||
@endif">₩{{ number_format($role['amount']) }}</p>
|
||||
@else
|
||||
<p class="text-xl font-bold text-red-600">운영팀 산정</p>
|
||||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<!-- 총 가입비 대비 수당 -->
|
||||
<div class="bg-gray-50 rounded-xl p-4 border border-gray-200">
|
||||
<div class="flex items-center justify-end gap-4">
|
||||
<div class="text-right">
|
||||
<p class="text-2xl font-bold text-gray-900">₩{{ number_format($totalCommissionRatio) }}</p>
|
||||
<p class="text-sm text-gray-500">총 가입비 대비 수당</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,98 @@
|
||||
{{-- 대시보드 데이터 컨테이너 (HTMX로 새로고침되는 영역) --}}
|
||||
|
||||
{{-- 전체 누적 실적 --}}
|
||||
@include('sales.dashboard.partials.stats')
|
||||
|
||||
{{-- 기간별 조회 --}}
|
||||
<div class="bg-white rounded-xl shadow-sm p-6">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<svg class="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||
</svg>
|
||||
<h2 class="text-lg font-bold text-gray-800">기간별 조회</h2>
|
||||
</div>
|
||||
|
||||
<form id="period-form" class="flex flex-wrap items-center gap-3">
|
||||
<div class="inline-flex rounded-lg border border-gray-200 p-1">
|
||||
<button type="button"
|
||||
id="btn-month"
|
||||
hx-get="{{ route('sales.salesmanagement.dashboard.refresh') }}"
|
||||
hx-target="#dashboard-data"
|
||||
hx-swap="innerHTML"
|
||||
hx-vals='{"period": "month"}'
|
||||
hx-indicator="#loading-indicator"
|
||||
class="px-4 py-2 text-sm font-medium rounded-md transition-colors {{ $period === 'month' ? 'bg-blue-600 text-white' : 'text-gray-600 hover:bg-gray-100' }}">
|
||||
당월
|
||||
</button>
|
||||
<button type="button"
|
||||
id="btn-custom"
|
||||
class="px-4 py-2 text-sm font-medium rounded-md transition-colors {{ $period === 'custom' ? 'bg-blue-600 text-white' : 'text-gray-600 hover:bg-gray-100' }}"
|
||||
onclick="toggleCustomPeriod()">
|
||||
기간 설정
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 당월 표시 -->
|
||||
<div id="month-display" class="{{ $period === 'custom' ? 'hidden' : '' }}">
|
||||
<span class="text-sm text-gray-600">{{ $year }}년 {{ $month }}월</span>
|
||||
</div>
|
||||
|
||||
<!-- 기간 설정 입력 -->
|
||||
<div id="custom-period" class="flex items-center gap-2 {{ $period === 'month' ? 'hidden' : '' }}">
|
||||
<input type="date"
|
||||
name="start_date"
|
||||
id="start-date"
|
||||
value="{{ $startDate ?? now()->startOfMonth()->format('Y-m-d') }}"
|
||||
class="px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
||||
<span class="text-gray-500">~</span>
|
||||
<input type="date"
|
||||
name="end_date"
|
||||
id="end-date"
|
||||
value="{{ $endDate ?? now()->format('Y-m-d') }}"
|
||||
class="px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
||||
<button type="button"
|
||||
hx-get="{{ route('sales.salesmanagement.dashboard.refresh') }}"
|
||||
hx-target="#dashboard-data"
|
||||
hx-swap="innerHTML"
|
||||
hx-include="#start-date, #end-date"
|
||||
hx-vals='{"period": "custom"}'
|
||||
hx-indicator="#loading-indicator"
|
||||
class="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700 transition-colors">
|
||||
조회
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 로딩 인디케이터 -->
|
||||
<div id="loading-indicator" class="htmx-indicator">
|
||||
<svg class="w-5 h-5 animate-spin text-blue-600" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{{-- 역할별 수당 상세 --}}
|
||||
@include('sales.dashboard.partials.commission-by-role')
|
||||
|
||||
{{-- 실적 데이터 없음 안내 --}}
|
||||
@include('sales.dashboard.partials.no-data')
|
||||
|
||||
{{-- 수익 및 테넌트 관리 --}}
|
||||
@include('sales.dashboard.partials.tenant-stats')
|
||||
|
||||
<script>
|
||||
function toggleCustomPeriod() {
|
||||
const btnMonth = document.getElementById('btn-month');
|
||||
const btnCustom = document.getElementById('btn-custom');
|
||||
const monthDisplay = document.getElementById('month-display');
|
||||
const customPeriod = document.getElementById('custom-period');
|
||||
|
||||
btnCustom.classList.add('bg-blue-600', 'text-white');
|
||||
btnCustom.classList.remove('text-gray-600', 'hover:bg-gray-100');
|
||||
btnMonth.classList.remove('bg-blue-600', 'text-white');
|
||||
btnMonth.classList.add('text-gray-600', 'hover:bg-gray-100');
|
||||
customPeriod.classList.remove('hidden');
|
||||
monthDisplay.classList.add('hidden');
|
||||
}
|
||||
</script>
|
||||
26
resources/views/sales/dashboard/partials/no-data.blade.php
Normal file
26
resources/views/sales/dashboard/partials/no-data.blade.php
Normal file
@@ -0,0 +1,26 @@
|
||||
{{-- 실적 데이터 없음 안내 --}}
|
||||
@if($stats['total_contracts'] == 0)
|
||||
<div class="bg-white rounded-xl shadow-sm p-12">
|
||||
<div class="flex flex-col items-center justify-center text-center">
|
||||
<div class="w-20 h-20 bg-blue-50 rounded-full flex items-center justify-center mb-6">
|
||||
<svg class="w-10 h-10 text-blue-400" 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 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold text-gray-800 mb-2">실적 데이터가 존재하지 않습니다</h3>
|
||||
<p class="text-gray-500 mb-2">선택한 기간 내에 등록된 계약 정보나 조직 구성 데이터가 없습니다.</p>
|
||||
<p class="text-gray-500 mb-6">아직 실적이 발생하지 않았거나, 시스템 동기화 중일 수 있습니다.</p>
|
||||
<button type="button"
|
||||
hx-get="{{ route('sales.salesmanagement.dashboard.refresh') }}"
|
||||
hx-target="#dashboard-data"
|
||||
hx-swap="innerHTML"
|
||||
hx-indicator="#refresh-indicator"
|
||||
class="inline-flex items-center gap-2 px-6 py-3 bg-blue-600 text-white font-medium rounded-lg hover:bg-blue-700 transition-colors">
|
||||
<svg class="w-5 h-5 htmx-indicator" id="refresh-indicator" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||
</svg>
|
||||
실적 데이터 새로고침
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
75
resources/views/sales/dashboard/partials/stats.blade.php
Normal file
75
resources/views/sales/dashboard/partials/stats.blade.php
Normal file
@@ -0,0 +1,75 @@
|
||||
{{-- 전체 누적 실적 --}}
|
||||
<div class="bg-white rounded-xl shadow-sm p-6">
|
||||
<h2 class="text-lg font-bold text-gray-800 mb-4">전체 누적 실적</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-5 gap-4">
|
||||
<!-- 총 가입비 -->
|
||||
<div class="bg-white border border-gray-200 rounded-xl p-4 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-start justify-between">
|
||||
<span class="text-sm text-gray-500">총 가입비</span>
|
||||
<div class="p-2 bg-gray-100 rounded-lg">
|
||||
<svg class="w-5 h-5 text-gray-600" 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>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-2xl font-bold text-gray-900 mt-2">₩{{ number_format($stats['total_membership_fee']) }}</p>
|
||||
<p class="text-xs text-gray-400 mt-1">전체 누적 가입비</p>
|
||||
</div>
|
||||
|
||||
<!-- 총 수당 -->
|
||||
<div class="bg-blue-50 border border-blue-200 rounded-xl p-4 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-start justify-between">
|
||||
<span class="text-sm text-blue-600">총 수당</span>
|
||||
<div class="p-2 bg-blue-100 rounded-lg">
|
||||
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 9V7a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2m2 4h10a2 2 0 002-2v-6a2 2 0 00-2-2H9a2 2 0 00-2 2v6a2 2 0 002 2zm7-5a2 2 0 11-4 0 2 2 0 014 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-2xl font-bold text-blue-700 mt-2">₩{{ number_format($stats['total_commission']) }}</p>
|
||||
<p class="text-xs text-blue-500 mt-1">지급 승인 완료 기준 ({{ $stats['commission_rate'] }}%)</p>
|
||||
</div>
|
||||
|
||||
<!-- 전체 건수 -->
|
||||
<div class="bg-white border border-gray-200 rounded-xl p-4 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-start justify-between">
|
||||
<span class="text-sm text-gray-500">전체 건수</span>
|
||||
<div class="p-2 bg-gray-100 rounded-lg">
|
||||
<svg class="w-5 h-5 text-gray-600" 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>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-2xl font-bold text-gray-900 mt-2">{{ number_format($stats['total_contracts']) }}건</p>
|
||||
<p class="text-xs text-gray-400 mt-1">전체 계약 건수</p>
|
||||
</div>
|
||||
|
||||
<!-- 가입 승인 대기 -->
|
||||
<div class="bg-white border border-gray-200 rounded-xl p-4 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-start justify-between">
|
||||
<span class="text-sm text-gray-500">가입 승인 대기</span>
|
||||
<div class="p-2 bg-gray-100 rounded-lg">
|
||||
<svg class="w-5 h-5 text-gray-600" 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>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-2xl font-bold text-gray-900 mt-2">{{ number_format($stats['pending_membership_approval']) }}건</p>
|
||||
<p class="text-xs text-gray-400 mt-1">조직 내 가입 승인 대기</p>
|
||||
</div>
|
||||
|
||||
<!-- 지급 승인 대기 -->
|
||||
<div class="bg-white border border-gray-200 rounded-xl p-4 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-start justify-between">
|
||||
<span class="text-sm text-gray-500">지급 승인 대기</span>
|
||||
<div class="p-2 bg-gray-100 rounded-lg">
|
||||
<svg class="w-5 h-5 text-gray-600" 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-6 9l2 2 4-4" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-2xl font-bold text-gray-900 mt-2">{{ number_format($stats['pending_payment_approval']) }}건</p>
|
||||
<p class="text-xs text-gray-400 mt-1">조직 내 지급 승인 대기</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,69 @@
|
||||
{{-- 수익 및 테넌트 관리 --}}
|
||||
<div class="bg-white rounded-xl shadow-sm p-6">
|
||||
<div class="flex items-center gap-3 mb-6">
|
||||
<div class="p-2 bg-blue-100 rounded-lg">
|
||||
<svg class="w-6 h-6 text-blue-600" 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>
|
||||
</div>
|
||||
<h2 class="text-xl font-bold text-gray-800">수익 및 테넌트 관리</h2>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<!-- 관리 테넌트 -->
|
||||
<div class="bg-white border border-gray-200 rounded-xl p-5 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<span class="text-sm text-gray-500">관리 테넌트</span>
|
||||
<div class="p-2 bg-blue-50 rounded-lg">
|
||||
<svg class="w-5 h-5 text-blue-600" 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>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-3xl font-bold text-gray-900">{{ number_format($tenantStats['total_tenants'] ?? 0) }}개</p>
|
||||
<p class="text-xs text-gray-400 mt-1">등록된 총 업체 수</p>
|
||||
</div>
|
||||
|
||||
<!-- 총 가입비 실적 -->
|
||||
<div class="bg-white border border-gray-200 rounded-xl p-5 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<span class="text-sm text-gray-500">총 가입비 실적</span>
|
||||
<div class="p-2 bg-blue-50 rounded-lg">
|
||||
<svg class="w-5 h-5 text-blue-600" 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>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-3xl font-bold text-gray-900">₩{{ number_format($tenantStats['total_membership_revenue'] ?? 0) }}</p>
|
||||
<p class="text-xs text-gray-400 mt-1">전체 가입비 합계</p>
|
||||
</div>
|
||||
|
||||
<!-- 누적 가입비 수당 -->
|
||||
<div class="bg-white border border-gray-200 rounded-xl p-5 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<span class="text-sm text-gray-500">누적 가입비 수당</span>
|
||||
<div class="p-2 bg-blue-50 rounded-lg">
|
||||
<svg class="w-5 h-5 text-blue-600" 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>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-3xl font-bold text-gray-900">₩{{ number_format($tenantStats['total_commission_accumulated'] ?? 0) }}</p>
|
||||
<p class="text-xs text-gray-400 mt-1">전체 가입비 수당 합계</p>
|
||||
</div>
|
||||
|
||||
<!-- 확정 가입비 수당 (지급대상) -->
|
||||
<div class="bg-green-50 border border-green-200 rounded-xl p-5 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<span class="text-sm text-green-700">확정 가입비 수당 (지급대상)</span>
|
||||
<div class="p-2 bg-green-100 rounded-lg">
|
||||
<svg class="w-5 h-5 text-green-600" 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>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-3xl font-bold text-green-700">₩{{ number_format($tenantStats['confirmed_commission'] ?? 0) }}</p>
|
||||
<p class="text-xs text-green-600 mt-1">운영팀 승인 완료된 금액 (지급: 계약 익월 말일)</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -790,6 +790,7 @@
|
||||
Route::middleware(['auth', 'hq.member'])->prefix('sales')->name('sales.')->group(function () {
|
||||
// 영업관리 대시보드
|
||||
Route::get('salesmanagement/dashboard', [\App\Http\Controllers\Sales\SalesDashboardController::class, 'index'])->name('salesmanagement.dashboard');
|
||||
Route::get('salesmanagement/dashboard/refresh', [\App\Http\Controllers\Sales\SalesDashboardController::class, 'refresh'])->name('salesmanagement.dashboard.refresh');
|
||||
|
||||
// 영업 담당자 관리
|
||||
Route::resource('managers', \App\Http\Controllers\Sales\SalesManagerController::class);
|
||||
|
||||
Reference in New Issue
Block a user