264 lines
17 KiB
PHP
Executable File
264 lines
17 KiB
PHP
Executable File
@extends("layouts.app")
|
|
|
|
@section("title", __("Billing Management"))
|
|
|
|
@section("header")
|
|
<div class="flex justify-between items-center">
|
|
<h2 class="font-semibold text-xl text-gray-800 leading-tight">{{ __("Billing Management") }}</h2>
|
|
</div>
|
|
@endsection
|
|
|
|
@section("content")
|
|
<div class="py-6">
|
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
|
@if(session("success"))
|
|
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded-lg mb-6">{{ session("success") }}</div>
|
|
@endif
|
|
|
|
<!-- Stats -->
|
|
<div class="grid grid-cols-1 md:grid-cols-5 gap-4 mb-6">
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-4">
|
|
<p class="text-xs font-medium text-gray-500">{{ __("Active Plans") }}</p>
|
|
<p class="text-2xl font-bold text-gray-900">{{ $stats["active_plans"] }}</p>
|
|
</div>
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-4">
|
|
<p class="text-xs font-medium text-gray-500">{{ __("Active Subscriptions") }}</p>
|
|
<p class="text-2xl font-bold text-green-600">{{ $stats["active_subscriptions"] }}</p>
|
|
</div>
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-4">
|
|
<p class="text-xs font-medium text-gray-500">{{ __("Total Revenue") }}</p>
|
|
<p class="text-2xl font-bold text-indigo-600">R$ {{ number_format($stats["total_revenue"], 2, ',', '.') }}</p>
|
|
</div>
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-4">
|
|
<p class="text-xs font-medium text-gray-500">{{ __("Pending Invoices") }}</p>
|
|
<p class="text-2xl font-bold text-yellow-600">{{ $stats["pending_invoices"] }}</p>
|
|
</div>
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-4">
|
|
<p class="text-xs font-medium text-gray-500">{{ __("Expiring Soon") }}</p>
|
|
<p class="text-2xl font-bold text-red-600">{{ $stats["expiring_soon"] }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Plans -->
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6 mb-6">
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h3 class="text-lg font-semibold">{{ __("Subscription Plans") }}</h3>
|
|
<button onclick="document.getElementById('createPlanModal').classList.remove('hidden')"
|
|
class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 text-sm font-medium">
|
|
+ {{ __("New Plan") }}
|
|
</button>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
@foreach($plans as $plan)
|
|
<div class="border rounded-lg p-4 {{ $plan->is_active ? 'border-indigo-200' : 'border-gray-200 opacity-75' }}">
|
|
<div class="flex justify-between items-start mb-2">
|
|
<h4 class="font-semibold text-gray-900">{{ $plan->name }}</h4>
|
|
@if($plan->is_active)
|
|
<span class="text-xs bg-green-100 text-green-800 px-2 py-0.5 rounded-full">{{ __("Active") }}</span>
|
|
@else
|
|
<span class="text-xs bg-gray-100 text-gray-800 px-2 py-0.5 rounded-full">{{ __("Inactive") }}</span>
|
|
@endif
|
|
</div>
|
|
<p class="text-2xl font-bold text-indigo-600 mb-2">
|
|
R$ {{ number_format($plan->price, 2, ',', '.') }}
|
|
<span class="text-sm font-normal text-gray-500">/{{ $plan->duration_days }}d</span>
|
|
</p>
|
|
<p class="text-sm text-gray-600 mb-3">{{ $plan->description }}</p>
|
|
<div class="text-xs text-gray-500 space-y-1 mb-3">
|
|
<p>📅 {{ $plan->duration_days }} {{ __("days") }}</p>
|
|
<p>🔤 {{ $plan->token_limit ? number_format($plan->token_limit) . ' tokens' : __('Unlimited tokens') }}</p>
|
|
<p>👥 {{ $plan->user_subscriptions_count ?? 0 }} {{ __("subscribers") }}</p>
|
|
</div>
|
|
<button onclick="openEditPlanModal({{ $plan->id }}, '{{ addslashes($plan->name) }}', '{{ addslashes($plan->description ?? '') }}', {{ $plan->price }}, '{{ $plan->currency }}', {{ $plan->duration_days }}, {{ $plan->token_limit ?? 'null' }}, {{ $plan->is_active ? 'true' : 'false' }})"
|
|
class="w-full text-sm px-3 py-1.5 bg-indigo-100 text-indigo-700 rounded-lg hover:bg-indigo-200 transition font-medium">
|
|
{{ __("Edit") }}
|
|
</button>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recent Subscriptions -->
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
|
|
<h3 class="text-lg font-semibold mb-4">{{ __("Recent Subscriptions") }}</h3>
|
|
<div class="overflow-x-auto">
|
|
<table class="min-w-full divide-y divide-gray-200">
|
|
<thead class="bg-gray-50">
|
|
<tr>
|
|
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">{{ __("User") }}</th>
|
|
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">{{ __("Plan") }}</th>
|
|
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">{{ __("Status") }}</th>
|
|
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">{{ __("Payment") }}</th>
|
|
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">{{ __("Period") }}</th>
|
|
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">{{ __("Actions") }}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-gray-200">
|
|
@foreach($subscriptions as $sub)
|
|
<tr class="hover:bg-gray-50">
|
|
<td class="px-4 py-3 text-sm">{{ $sub->user->name }}</td>
|
|
<td class="px-4 py-3 text-sm">{{ $sub->plan->name }}</td>
|
|
<td class="px-4 py-3">
|
|
<span class="px-2 py-0.5 text-xs rounded-full {{ $sub->status === 'active' ? 'bg-green-100 text-green-800' : ($sub->status === 'cancelled' ? 'bg-red-100 text-red-800' : 'bg-gray-100 text-gray-800') }}">
|
|
{{ ucfirst($sub->status) }}
|
|
</span>
|
|
</td>
|
|
<td class="px-4 py-3">
|
|
@if($sub->payment_approved)
|
|
<span class="text-xs text-green-600">✅ {{ __("Approved") }}</span>
|
|
@else
|
|
<span class="text-xs text-yellow-600">⏳ {{ __("Pending") }}</span>
|
|
@endif
|
|
</td>
|
|
<td class="px-4 py-3 text-xs text-gray-500">
|
|
{{ $sub->start_date->format('d/m/Y') }} - {{ $sub->end_date->format('d/m/Y') }}
|
|
</td>
|
|
<td class="px-4 py-3">
|
|
<div class="flex gap-1">
|
|
@if(!$sub->payment_approved)
|
|
<form action="{{ route("superadmin.billing.approve", $sub) }}" method="POST">
|
|
@csrf
|
|
<button type="submit" class="px-2 py-1 bg-green-600 text-white text-xs rounded hover:bg-green-700">{{ __("Approve") }}</button>
|
|
</form>
|
|
@endif
|
|
@if($sub->status === 'active')
|
|
<form action="{{ route("superadmin.billing.cancel", $sub) }}" method="POST" onsubmit="return confirm('{{ __("Cancel this subscription?") }}')">
|
|
@csrf
|
|
<button type="submit" class="px-2 py-1 bg-red-600 text-white text-xs rounded hover:bg-red-700">{{ __("Cancel") }}</button>
|
|
</form>
|
|
@endif
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
@if(method_exists($subscriptions, 'links'))
|
|
<div class="mt-4">{{ $subscriptions->links() }}</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Create Plan Modal -->
|
|
<div id="createPlanModal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 z-50">
|
|
<div class="relative top-20 mx-auto p-5 border w-full max-w-md shadow-lg rounded-xl bg-white">
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h3 class="text-lg font-medium">{{ __("Create Plan") }}</h3>
|
|
<button onclick="document.getElementById('createPlanModal').classList.add('hidden')" class="text-gray-400 hover:text-gray-600">×</button>
|
|
</div>
|
|
<form action="{{ route("superadmin.billing.plans.store") }}" method="POST">
|
|
@csrf
|
|
<div class="mb-3">
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">{{ __("Name") }}</label>
|
|
<input type="text" name="name" required class="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-indigo-500">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">{{ __("Description") }}</label>
|
|
<textarea name="description" rows="2" class="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-indigo-500"></textarea>
|
|
</div>
|
|
<div class="grid grid-cols-2 gap-3 mb-3">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">{{ __("Price") }}</label>
|
|
<input type="number" name="price" step="0.01" min="0" required class="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-indigo-500">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">{{ __("Currency") }}</label>
|
|
<select name="currency" class="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-indigo-500">
|
|
<option value="BRL">BRL (R$)</option>
|
|
<option value="USD">USD ($)</option>
|
|
<option value="EUR">EUR (€)</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-2 gap-3 mb-3">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">{{ __("Duration (days)") }}</label>
|
|
<input type="number" name="duration_days" min="1" value="30" required class="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-indigo-500">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">{{ __("Token Limit") }}</label>
|
|
<input type="number" name="token_limit" min="0" placeholder="{{ __("Unlimited") }}" class="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-indigo-500">
|
|
</div>
|
|
</div>
|
|
<div class="flex justify-end gap-2 mt-4">
|
|
<button type="button" onclick="document.getElementById('createPlanModal').classList.add('hidden')" class="px-4 py-2 bg-gray-100 rounded-lg hover:bg-gray-200">{{ __("Cancel") }}</button>
|
|
<button type="submit" class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700">{{ __("Create") }}</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Edit Plan Modal -->
|
|
<div id="editPlanModal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 z-50">
|
|
<div class="relative top-20 mx-auto p-5 border w-full max-w-md shadow-lg rounded-xl bg-white">
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h3 class="text-lg font-medium">{{ __("Edit Plan") }}</h3>
|
|
<button onclick="document.getElementById('editPlanModal').classList.add('hidden')" class="text-gray-400 hover:text-gray-600">×</button>
|
|
</div>
|
|
<form id="editPlanForm" method="POST">
|
|
@csrf
|
|
@method("PUT")
|
|
<div class="mb-3">
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">{{ __("Name") }}</label>
|
|
<input type="text" name="name" id="edit_name" required class="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-indigo-500">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">{{ __("Description") }}</label>
|
|
<textarea name="description" id="edit_description" rows="2" class="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-indigo-500"></textarea>
|
|
</div>
|
|
<div class="grid grid-cols-2 gap-3 mb-3">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">{{ __("Price") }}</label>
|
|
<input type="number" name="price" id="edit_price" step="0.01" min="0" required class="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-indigo-500">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">{{ __("Currency") }}</label>
|
|
<select name="currency" id="edit_currency" class="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-indigo-500">
|
|
<option value="BRL">BRL (R$)</option>
|
|
<option value="USD">USD ($)</option>
|
|
<option value="EUR">EUR (€)</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-2 gap-3 mb-3">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">{{ __("Duration (days)") }}</label>
|
|
<input type="number" name="duration_days" id="edit_duration" min="1" required class="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-indigo-500">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">{{ __("Token Limit") }}</label>
|
|
<input type="number" name="token_limit" id="edit_token_limit" min="0" placeholder="{{ __("Unlimited") }}" class="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-indigo-500">
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="flex items-center">
|
|
<input type="checkbox" name="is_active" id="edit_is_active" value="1" class="rounded border-gray-300 text-indigo-600">
|
|
<span class="ml-2 text-sm text-gray-700">{{ __("Active") }}</span>
|
|
</label>
|
|
</div>
|
|
<div class="flex justify-end gap-2 mt-4">
|
|
<button type="button" onclick="document.getElementById('editPlanModal').classList.add('hidden')" class="px-4 py-2 bg-gray-100 rounded-lg hover:bg-gray-200">{{ __("Cancel") }}</button>
|
|
<button type="submit" class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700">{{ __("Update") }}</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
@endsection
|
|
|
|
@push("scripts")
|
|
<script>
|
|
function openEditPlanModal(id, name, description, price, currency, duration, tokenLimit, isActive) {
|
|
document.getElementById('edit_name').value = name;
|
|
document.getElementById('edit_description').value = description;
|
|
document.getElementById('edit_price').value = price;
|
|
document.getElementById('edit_currency').value = currency;
|
|
document.getElementById('edit_duration').value = duration;
|
|
document.getElementById('edit_token_limit').value = tokenLimit || '';
|
|
document.getElementById('edit_is_active').checked = isActive;
|
|
document.getElementById('editPlanForm').action = '{{ url("superadmin/billing/plans") }}/' + id;
|
|
document.getElementById('editPlanModal').classList.remove('hidden');
|
|
}
|
|
</script>
|
|
@endpush |