Deploy Ladill Events / deploy (push) Successful in 55s
Make Ladill Events free for all accounts, remove plan upgrade UI, force Ladill Pay only for tickets and contributions, and reinstate standard platform fees.
184 lines
13 KiB
PHP
184 lines
13 KiB
PHP
<x-user-layout>
|
||
<x-slot name="title">Payments & Payouts</x-slot>
|
||
@php
|
||
$fmt = fn ($m) => 'GHS '.number_format($m / 100, 2);
|
||
$pa = is_array($payoutAccount ?? null) ? $payoutAccount : null;
|
||
$hasPayoutErrors = $errors->hasAny(['account_type', 'account_name', 'account_number', 'bank_code', 'bank_name']);
|
||
@endphp
|
||
<div class="mx-auto max-w-5xl space-y-6">
|
||
@foreach(['success', 'error'] as $flash)
|
||
@if(session($flash))
|
||
<div class="rounded-xl border px-4 py-3 text-sm {{ $flash === 'success' ? 'border-emerald-200 bg-emerald-50 text-emerald-700' : 'border-red-200 bg-red-50 text-red-700' }}">
|
||
{{ session($flash) }}
|
||
</div>
|
||
@endif
|
||
@endforeach
|
||
|
||
<div>
|
||
<h1 class="text-xl font-semibold text-slate-900">Payments & Payouts</h1>
|
||
<p class="mt-1 text-sm text-slate-500">Paid tickets and contributions settle via Ladill Pay (standard platform fee). Net proceeds land in your Ladill wallet for withdrawal.</p>
|
||
</div>
|
||
|
||
<div class="grid gap-4 sm:grid-cols-2">
|
||
<div class="rounded-2xl border border-slate-200 bg-white p-6">
|
||
<p class="text-xs font-medium uppercase tracking-wide text-slate-400">Total event revenue (net)</p>
|
||
<p class="mt-2 text-3xl font-semibold text-slate-900">{{ $fmt($revenueMinor) }}</p>
|
||
</div>
|
||
<div class="rounded-2xl border border-indigo-100 bg-indigo-50/60 p-6">
|
||
<p class="text-xs font-medium uppercase tracking-wide text-indigo-600">Available in wallet</p>
|
||
<p class="mt-2 text-3xl font-semibold text-slate-900">{{ $fmt($balanceMinor) }}</p>
|
||
<a href="{{ route('account.wallet') }}" class="mt-4 inline-block text-sm font-medium text-indigo-600 hover:text-indigo-800">Full wallet →</a>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- Withdraw --}}
|
||
<div class="overflow-hidden rounded-2xl border border-slate-200 bg-white" x-data="{ open: {{ $pa ? 'true' : 'false' }} }">
|
||
<button type="button" @click="open = !open" class="flex w-full items-center justify-between gap-3 px-5 py-4 text-left">
|
||
<div>
|
||
<h2 class="text-sm font-semibold text-slate-900">Withdraw to bank or MoMo</h2>
|
||
<p class="mt-0.5 text-xs text-slate-500">
|
||
@if($pa)
|
||
To {{ $pa['bank_name'] }} · {{ $pa['account_number'] }}
|
||
@else
|
||
Add a payout account to withdraw from your wallet.
|
||
@endif
|
||
</p>
|
||
</div>
|
||
<svg class="h-4 w-4 shrink-0 text-slate-400 transition" :class="open && 'rotate-180'" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"/></svg>
|
||
</button>
|
||
|
||
<div x-show="open" x-cloak class="space-y-4 border-t border-slate-100 px-5 py-4">
|
||
@if($pa)
|
||
<div class="flex items-start gap-3 rounded-xl border border-slate-100 bg-slate-50/80 p-4">
|
||
<div class="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-indigo-100">
|
||
<svg class="h-5 w-5 text-indigo-600" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 8.25h19.5M2.25 9h19.5m-16.5 5.25h6m-6 2.25h3m-3.75 3h15a2.25 2.25 0 0 0 2.25-2.25V6.75A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25v10.5A2.25 2.25 0 0 0 4.5 19.5Z"/></svg>
|
||
</div>
|
||
<div class="min-w-0 flex-1">
|
||
<p class="text-[11px] font-semibold uppercase tracking-widest text-slate-400">{{ ($pa['account_type'] ?? '') === 'mobile_money' ? 'Mobile Money' : 'Bank Account' }}</p>
|
||
<p class="text-sm font-semibold text-slate-900">{{ $pa['account_name'] }}</p>
|
||
<p class="text-xs text-slate-500">{{ $pa['bank_name'] }} · {{ $pa['account_number'] }}</p>
|
||
</div>
|
||
<button type="button" @click="$dispatch('open-modal', 'events-payout-account')" class="shrink-0 text-xs font-semibold text-indigo-600 hover:text-indigo-800">Change</button>
|
||
</div>
|
||
|
||
<form action="{{ route('events.payouts.withdraw') }}" method="POST" class="space-y-3">
|
||
@csrf
|
||
<div>
|
||
<label class="block text-xs font-semibold text-slate-600">Amount (GHS)</label>
|
||
<div class="relative mt-1.5">
|
||
<span class="absolute inset-y-0 left-0 flex items-center pl-3.5 text-sm text-slate-400">GHS</span>
|
||
<input type="number" name="amount" min="1" max="{{ $balanceGhs }}" step="0.01" value="{{ old('amount') }}" placeholder="10.00"
|
||
class="block w-full rounded-xl border border-slate-200 py-2.5 pl-12 pr-4 text-sm text-slate-900 placeholder-slate-400 focus:border-indigo-400 focus:outline-none focus:ring-1 focus:ring-indigo-400/30" required>
|
||
</div>
|
||
@error('amount') <p class="mt-1 text-xs text-red-600">{{ $message }}</p> @enderror
|
||
</div>
|
||
<button type="submit" class="rounded-xl bg-slate-900 px-4 py-2 text-sm font-semibold text-white shadow-sm transition hover:bg-slate-800">Request withdrawal</button>
|
||
</form>
|
||
@else
|
||
<div class="flex flex-col items-center gap-3 py-2 text-center">
|
||
<p class="text-sm text-slate-600">Add a payout account to withdraw from your wallet.</p>
|
||
<button type="button" @click="$dispatch('open-modal', 'events-payout-account')" class="btn-primary">Add payout account</button>
|
||
</div>
|
||
@endif
|
||
</div>
|
||
</div>
|
||
|
||
<x-modal name="events-payout-account" :show="$hasPayoutErrors" maxWidth="md" focusable>
|
||
<div class="border-b border-slate-100 px-5 py-4 pr-12 sm:px-6 sm:pr-14">
|
||
<h3 class="text-base font-semibold text-slate-900">Payout account</h3>
|
||
<p class="mt-1 text-sm text-slate-500">Mobile money or bank account for withdrawals.</p>
|
||
</div>
|
||
<div class="px-5 py-4 sm:px-6 sm:py-5">
|
||
@include('events.partials.payout-account-form', ['payoutAccount' => $pa])
|
||
</div>
|
||
</x-modal>
|
||
|
||
{{-- Event payments --}}
|
||
<div class="rounded-2xl border border-slate-200 bg-white">
|
||
<div class="border-b border-slate-100 px-5 py-4">
|
||
<h2 class="text-sm font-semibold text-slate-900">Event payments</h2>
|
||
<p class="mt-0.5 text-xs text-slate-500">Ticket and contribution net proceeds after the Ladill Pay platform fee.</p>
|
||
</div>
|
||
|
||
@if($transactions->isEmpty())
|
||
<div class="px-6 py-10 text-center">
|
||
<svg class="mx-auto h-10 w-10 text-slate-300" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 18.75a60.07 60.07 0 0 1 15.797 2.101c.727.198 1.453-.342 1.453-1.096V18.75M3.75 4.5v.75A.75.75 0 0 1 3 6h-.75m0 0v-.375c0-.621.504-1.125 1.125-1.125H20.25M2.25 6v9m18-10.5v.75c0 .414.336.75.75.75h.75m-1.5-1.5h.375c.621 0 1.125.504 1.125 1.125v9.75c0 .621-.504 1.125-1.125 1.125h-.375m1.5-1.5H21a.75.75 0 0 0-.75.75v.75m0 0H3.75m0 0h-.375a1.125 1.125 0 0 1-1.125-1.125V15m1.5 1.5v-.75A.75.75 0 0 0 3 15h-.75M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm3 0h.008v.008H18V10.5Zm-12 0h.008v.008H6V10.5Z"/></svg>
|
||
<h3 class="mt-3 text-sm font-semibold text-slate-900">No payments yet</h3>
|
||
<p class="mt-1 text-sm text-slate-500">Paid registrations will appear here once attendees complete checkout.</p>
|
||
</div>
|
||
@else
|
||
<div class="overflow-x-auto">
|
||
<table class="min-w-full text-left text-sm">
|
||
<thead class="border-b border-slate-100 bg-slate-50/80 text-xs uppercase tracking-wide text-slate-500">
|
||
<tr>
|
||
<th class="px-5 py-3 font-medium">Event</th>
|
||
<th class="px-5 py-3 font-medium">Attendee</th>
|
||
<th class="px-5 py-3 font-medium">Type</th>
|
||
<th class="px-5 py-3 font-medium text-right">Gross</th>
|
||
<th class="px-5 py-3 font-medium text-right">Fee</th>
|
||
<th class="px-5 py-3 font-medium text-right">Net</th>
|
||
<th class="px-5 py-3 font-medium">Paid</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody class="divide-y divide-slate-100">
|
||
@foreach($transactions as $tx)
|
||
@php
|
||
$eventName = $tx->qrCode?->content()['name'] ?? $tx->qrCode?->label ?? 'Event';
|
||
$isContribution = ($tx->qrCode?->content()['mode'] ?? 'ticketing') === 'contributions';
|
||
@endphp
|
||
<tr class="hover:bg-slate-50/50">
|
||
<td class="px-5 py-3 font-medium text-slate-900">{{ $eventName }}</td>
|
||
<td class="px-5 py-3 text-slate-600">{{ $tx->attendee_name }}</td>
|
||
<td class="px-5 py-3 text-slate-600">{{ $tx->tier_name }}</td>
|
||
<td class="px-5 py-3 text-right text-slate-600">{{ $fmt($tx->amount_minor) }}</td>
|
||
<td class="px-5 py-3 text-right text-slate-400">−{{ $fmt($tx->platformFeeMinor()) }}</td>
|
||
<td class="px-5 py-3 text-right font-medium text-slate-900">{{ $fmt($tx->netAmountMinor()) }}</td>
|
||
<td class="px-5 py-3 text-slate-500">
|
||
{{ $tx->paid_at?->format('M j, Y g:i A') ?? '—' }}
|
||
@if($tx->payment_reference)
|
||
<span class="block text-[11px] text-slate-400">{{ $tx->payment_reference }}</span>
|
||
@endif
|
||
</td>
|
||
</tr>
|
||
@endforeach
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
@endif
|
||
</div>
|
||
|
||
{{-- Withdrawal history --}}
|
||
@if(!empty($withdrawals))
|
||
<div class="rounded-2xl border border-slate-200 bg-white">
|
||
<div class="border-b border-slate-100 px-5 py-4">
|
||
<h2 class="text-sm font-semibold text-slate-900">Withdrawal history</h2>
|
||
<p class="mt-0.5 text-xs text-slate-500">Recent requests to your bank or mobile money account.</p>
|
||
</div>
|
||
<div class="divide-y divide-slate-100">
|
||
@foreach($withdrawals as $withdrawal)
|
||
@php
|
||
$status = (string) ($withdrawal['status'] ?? 'pending');
|
||
$statusClass = match ($status) {
|
||
'completed', 'success' => 'text-emerald-700 bg-emerald-50',
|
||
'failed' => 'text-red-700 bg-red-50',
|
||
default => 'text-amber-700 bg-amber-50',
|
||
};
|
||
@endphp
|
||
<div class="flex flex-wrap items-center justify-between gap-3 px-5 py-3">
|
||
<div>
|
||
<p class="text-sm font-medium text-slate-900">GHS {{ number_format((float) ($withdrawal['amount_ghs'] ?? 0), 2) }}</p>
|
||
<p class="text-xs text-slate-500">
|
||
@if(!empty($withdrawal['created_at']))
|
||
{{ \Illuminate\Support\Carbon::parse($withdrawal['created_at'])->format('M j, Y g:i A') }}
|
||
@endif
|
||
</p>
|
||
</div>
|
||
<span class="rounded-full px-2.5 py-0.5 text-xs font-medium capitalize {{ $statusClass }}">{{ $status }}</span>
|
||
</div>
|
||
@endforeach
|
||
</div>
|
||
</div>
|
||
@endif
|
||
</div>
|
||
</x-user-layout>
|