- 모델: BarobillSubscription, BarobillBillingRecord, BarobillMonthlySummary - 서비스: BarobillBillingService (구독/과금 처리 로직) - API 컨트롤러: BarobillBillingController (구독/과금 CRUD) - 뷰: 과금 현황 탭, 구독 관리 탭, 통계 카드, 상세 모달 - 라우트: 웹/API 라우트 추가 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
138 lines
7.7 KiB
PHP
138 lines
7.7 KiB
PHP
@if($subscriptions->isEmpty())
|
|
<div class="p-12 text-center">
|
|
<svg class="mx-auto h-12 w-12 text-gray-400" 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>
|
|
<h3 class="mt-2 text-sm font-medium text-gray-900">등록된 구독이 없습니다</h3>
|
|
<p class="mt-1 text-sm text-gray-500">회원사에 월정액 서비스 구독을 등록해주세요.</p>
|
|
</div>
|
|
@else
|
|
<div class="flex-1 overflow-auto">
|
|
<table class="min-w-full divide-y divide-gray-200">
|
|
<thead class="bg-gray-50 sticky top-0 z-10">
|
|
<tr>
|
|
@if($allTenants ?? false)
|
|
<th scope="col" class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-16">
|
|
T-ID
|
|
</th>
|
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
테넌트
|
|
</th>
|
|
@endif
|
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
회원사
|
|
</th>
|
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
서비스
|
|
</th>
|
|
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
월정액
|
|
</th>
|
|
<th scope="col" class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
시작일
|
|
</th>
|
|
<th scope="col" class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
종료일
|
|
</th>
|
|
<th scope="col" class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
상태
|
|
</th>
|
|
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider w-20">
|
|
관리
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="bg-white divide-y divide-gray-200">
|
|
@foreach($subscriptions as $subscription)
|
|
<tr class="hover:bg-gray-50 transition-colors group">
|
|
@if($allTenants ?? false)
|
|
<td class="px-4 py-4 whitespace-nowrap text-center">
|
|
<span class="inline-flex items-center justify-center w-8 h-8 rounded-full text-xs font-bold bg-indigo-100 text-indigo-700">
|
|
{{ $subscription->member->tenant_id ?? '-' }}
|
|
</span>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-indigo-100 text-indigo-800">
|
|
{{ $subscription->member->tenant->company_name ?? '-' }}
|
|
</span>
|
|
</td>
|
|
@endif
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
<div class="text-sm font-medium text-gray-900">{{ $subscription->member->corp_name ?? '-' }}</div>
|
|
<div class="text-xs text-gray-400">{{ $subscription->member->formatted_biz_no ?? '' }}</div>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
@php
|
|
$serviceColors = [
|
|
'bank_account' => 'bg-green-100 text-green-800',
|
|
'card' => 'bg-blue-100 text-blue-800',
|
|
'hometax' => 'bg-orange-100 text-orange-800',
|
|
];
|
|
@endphp
|
|
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium {{ $serviceColors[$subscription->service_type] ?? 'bg-gray-100 text-gray-800' }}">
|
|
{{ $subscription->service_type_label }}
|
|
</span>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-right">
|
|
<span class="text-sm font-medium text-gray-900">{{ number_format($subscription->monthly_fee) }}원</span>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-center">
|
|
<span class="text-sm text-gray-600">{{ $subscription->started_at?->format('Y-m-d') ?? '-' }}</span>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-center">
|
|
<span class="text-sm text-gray-600">{{ $subscription->ended_at?->format('Y-m-d') ?? '-' }}</span>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-center">
|
|
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full {{ $subscription->status_color }}">
|
|
{{ $subscription->status_label }}
|
|
</span>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-right">
|
|
<div class="flex justify-end gap-1 opacity-30 group-hover:opacity-100 transition-opacity">
|
|
@if($subscription->is_active && !$subscription->ended_at)
|
|
<button
|
|
type="button"
|
|
onclick="cancelSubscription({{ $subscription->id }}, '{{ addslashes($subscription->member->corp_name ?? '') }}', '{{ $subscription->service_type_label }}')"
|
|
class="p-2 text-red-600 hover:bg-red-50 rounded-lg"
|
|
title="구독 해지"
|
|
>
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
|
</svg>
|
|
</button>
|
|
@endif
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
@endif
|
|
|
|
<script>
|
|
async function cancelSubscription(id, memberName, serviceName) {
|
|
if (!confirm(`${memberName}의 ${serviceName} 구독을 해지하시겠습니까?`)) return;
|
|
|
|
try {
|
|
const res = await fetch(`/api/admin/barobill/billing/subscriptions/${id}`, {
|
|
method: 'DELETE',
|
|
headers: {
|
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
|
|
},
|
|
});
|
|
|
|
const result = await res.json();
|
|
|
|
if (result.success) {
|
|
showToast(result.message, 'success');
|
|
htmx.trigger(document.body, 'subscriptionUpdated');
|
|
} else {
|
|
showToast(result.message || '처리 실패', 'error');
|
|
}
|
|
} catch (error) {
|
|
showToast('오류가 발생했습니다.', 'error');
|
|
}
|
|
}
|
|
</script>
|