Initial Ladill Give extraction — online giving pages at give.ladill.com.
Donation checkout via Ladill Pay (9% fee), church/giving QR pages, SSO, and platform scan forwarding. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
<x-user-layout>
|
||||
<x-slot name="title">Overview</x-slot>
|
||||
@php $fmt = fn ($m) => 'GHS '.number_format($m / 100, 2); @endphp
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<h1 class="text-xl font-semibold text-slate-900">Overview</h1>
|
||||
<p class="mt-1 text-sm text-slate-500">Total raised today, active giving pages, and recent donations.</p>
|
||||
</div>
|
||||
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<div class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||
<p class="text-xs font-medium uppercase tracking-wide text-slate-400">Raised today</p>
|
||||
<p class="mt-1.5 text-2xl font-semibold text-slate-900">{{ $fmt($todayMinor) }}</p>
|
||||
</div>
|
||||
<div class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||
<p class="text-xs font-medium uppercase tracking-wide text-slate-400">Donations today</p>
|
||||
<p class="mt-1.5 text-2xl font-semibold text-slate-900">{{ number_format($todayCount) }}</p>
|
||||
</div>
|
||||
<div class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||
<p class="text-xs font-medium uppercase tracking-wide text-slate-400">Giving pages</p>
|
||||
<p class="mt-1.5 text-2xl font-semibold text-slate-900">{{ $givingPageCount }}</p>
|
||||
</div>
|
||||
<div class="rounded-2xl border border-indigo-100 bg-indigo-50/60 p-5">
|
||||
<p class="text-xs font-medium uppercase tracking-wide text-indigo-600">Wallet balance</p>
|
||||
<p class="mt-1.5 text-2xl font-semibold text-slate-900">{{ $fmt($balanceMinor) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid gap-4 lg:grid-cols-2">
|
||||
<div class="rounded-2xl border border-slate-200 bg-white">
|
||||
<div class="flex items-center justify-between border-b border-slate-100 px-6 py-4">
|
||||
<h2 class="font-semibold text-slate-900">Recent donations</h2>
|
||||
<a href="{{ route('give.donations.index') }}" class="text-sm font-medium text-indigo-600 hover:text-indigo-800">View all</a>
|
||||
</div>
|
||||
@if($recentDonations->isEmpty())
|
||||
<p class="px-6 py-8 text-sm text-slate-500">No donations yet. Create a giving page and share your QR.</p>
|
||||
@else
|
||||
<div class="divide-y divide-slate-100">
|
||||
@foreach($recentDonations as $donation)
|
||||
<div class="flex items-center justify-between px-6 py-4">
|
||||
<div>
|
||||
<p class="font-medium text-slate-900">{{ $donation->payer_name ?: 'Anonymous donor' }}</p>
|
||||
<p class="text-xs text-slate-500">{{ $donation->collection_type ?: 'Donation' }} · {{ $donation->qrCode?->label }} · {{ $donation->paid_at?->diffForHumans() }}</p>
|
||||
</div>
|
||||
<span class="text-sm font-semibold text-emerald-700">{{ $fmt($donation->merchant_amount_minor) }}</span>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
<div class="rounded-2xl border border-slate-200 bg-white p-6">
|
||||
<h2 class="font-semibold text-slate-900">Quick links</h2>
|
||||
<div class="mt-4 space-y-3">
|
||||
<a href="{{ route('give.giving-pages.create') }}" class="flex items-center justify-between rounded-xl border border-slate-100 px-4 py-3 text-sm hover:bg-slate-50">
|
||||
<span class="font-medium text-slate-700">Create giving page</span>
|
||||
<span class="text-slate-400">→</span>
|
||||
</a>
|
||||
<a href="{{ route('give.giving-pages.index') }}" class="flex items-center justify-between rounded-xl border border-slate-100 px-4 py-3 text-sm hover:bg-slate-50">
|
||||
<span class="font-medium text-slate-700">All giving pages</span>
|
||||
<span class="text-slate-400">→</span>
|
||||
</a>
|
||||
<a href="{{ route('give.payouts') }}" class="flex items-center justify-between rounded-xl border border-slate-100 px-4 py-3 text-sm hover:bg-slate-50">
|
||||
<span class="font-medium text-slate-700">Payouts & withdrawals</span>
|
||||
<span class="text-slate-400">→</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-user-layout>
|
||||
@@ -0,0 +1,58 @@
|
||||
<x-user-layout>
|
||||
<x-slot name="title">Donations</x-slot>
|
||||
@php $fmt = fn ($m) => 'GHS '.number_format($m / 100, 2); @endphp
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<h1 class="text-xl font-semibold text-slate-900">Donations</h1>
|
||||
<p class="mt-1 text-sm text-slate-500">Incoming donations across all your giving pages.</p>
|
||||
</div>
|
||||
<form method="get" class="max-w-md">
|
||||
<input type="search" name="q" value="{{ $search }}" placeholder="Search donor, cause, reference…"
|
||||
class="w-full rounded-xl border-slate-200 text-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
</form>
|
||||
<div class="overflow-hidden rounded-2xl border border-slate-200 bg-white">
|
||||
@if($donations->isEmpty())
|
||||
<p class="px-6 py-10 text-sm text-slate-500">No donations yet.</p>
|
||||
@else
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-slate-100 text-sm">
|
||||
<thead class="bg-slate-50 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">
|
||||
<tr>
|
||||
<th class="px-6 py-3">When</th>
|
||||
<th class="px-6 py-3">Donor</th>
|
||||
<th class="px-6 py-3">Cause</th>
|
||||
<th class="px-6 py-3">Giving page</th>
|
||||
<th class="px-6 py-3">Amount</th>
|
||||
<th class="px-6 py-3">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
@foreach($donations as $donation)
|
||||
<tr>
|
||||
<td class="px-6 py-4 text-slate-600">{{ $donation->paid_at?->format('M j, g:i A') ?? $donation->created_at->format('M j, g:i A') }}</td>
|
||||
<td class="px-6 py-4">
|
||||
<p class="font-medium text-slate-900">{{ $donation->payer_name ?: 'Anonymous' }}</p>
|
||||
@if($donation->payer_email)
|
||||
<p class="text-xs text-slate-500">{{ $donation->payer_email }}</p>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-6 py-4 text-slate-600">{{ $donation->collection_type ?: 'Donation' }}</td>
|
||||
<td class="px-6 py-4 text-slate-600">{{ $donation->qrCode?->label }}</td>
|
||||
<td class="px-6 py-4 font-semibold text-slate-900">{{ $fmt($donation->status === \App\Models\GiveDonation::STATUS_PAID ? $donation->merchant_amount_minor : $donation->amount_minor) }}</td>
|
||||
<td class="px-6 py-4">
|
||||
<span class="inline-flex rounded-full px-2.5 py-0.5 text-xs font-medium {{ $donation->status === \App\Models\GiveDonation::STATUS_PAID ? 'bg-emerald-50 text-emerald-700' : ($donation->status === \App\Models\GiveDonation::STATUS_FAILED ? 'bg-red-50 text-red-700' : 'bg-amber-50 text-amber-700') }}">
|
||||
{{ ucfirst($donation->status) }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@if($donations->hasPages())
|
||||
<div class="border-t border-slate-100 px-6 py-4">{{ $donations->links() }}</div>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</x-user-layout>
|
||||
@@ -0,0 +1,106 @@
|
||||
<x-user-layout>
|
||||
<x-slot name="title">Create Giving Page</x-slot>
|
||||
@php
|
||||
$giveOldTypes = old('collection_types', ['Offering','Tithe','Donation','Harvest']);
|
||||
$giveOldOrg = old('org_type', 'church');
|
||||
@endphp
|
||||
<div class="mx-auto max-w-2xl space-y-6" x-data="{
|
||||
orgType: @js($giveOldOrg),
|
||||
orgMeta: {
|
||||
church: { namePh: 'Church name *', descriptorLabel: 'Denomination', descriptorPh: 'e.g. Presbyterian', timesLabel: 'Service times', timesPh: 'e.g. Sundays 9am', presets: ['Offering','Tithe','Donation','Harvest'] },
|
||||
school: { namePh: 'School name *', descriptorLabel: 'Motto', descriptorPh: 'School motto', timesLabel: 'School hours', timesPh: 'Mon–Fri 7:30am', presets: ['School Fees','PTA Levy','Development Fund'] },
|
||||
mosque: { namePh: 'Mosque name *', descriptorLabel: 'Community', descriptorPh: 'Jama\'ah', timesLabel: 'Prayer times', timesPh: 'Jumu\'ah 1pm', presets: ['Sadaqah','Zakat','Waqf'] },
|
||||
ngo: { namePh: 'Organisation name *', descriptorLabel: 'Mission', descriptorPh: 'Mission statement', timesLabel: 'Office hours', timesPh: 'Mon–Fri 9am', presets: ['General Donation','Project Fund','Emergency Relief'] },
|
||||
club: { namePh: 'Club name *', descriptorLabel: 'Tagline', descriptorPh: 'Club tagline', timesLabel: 'Meeting times', timesPh: 'Fridays 5pm', presets: ['Membership Dues','Event Fund','Donation'] },
|
||||
},
|
||||
get cfg() { return this.orgMeta[this.orgType]; },
|
||||
types: @js($giveOldTypes),
|
||||
addInput: '',
|
||||
setOrg(t) { this.orgType = t; this.types = [...this.orgMeta[t].presets]; },
|
||||
addType() { const v = this.addInput.trim(); if (v && !this.types.includes(v)) this.types.push(v); this.addInput = ''; },
|
||||
removeType(i) { this.types.splice(i, 1); }
|
||||
}">
|
||||
<div>
|
||||
<a href="{{ route('give.giving-pages.index') }}" class="text-sm text-slate-500 hover:text-slate-700">← All giving pages</a>
|
||||
<h1 class="mt-2 text-xl font-semibold text-slate-900">Create giving page</h1>
|
||||
<p class="mt-1 text-sm text-slate-500">Set up your organisation, causes, and online giving QR.</p>
|
||||
</div>
|
||||
@if(session('error'))
|
||||
<div class="rounded-xl border border-red-100 bg-red-50 px-4 py-3 text-sm text-red-700">{{ session('error') }}</div>
|
||||
@endif
|
||||
<form method="post" action="{{ route('give.giving-pages.store') }}" enctype="multipart/form-data" class="space-y-5 rounded-2xl border border-slate-200 bg-white p-6">
|
||||
@csrf
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700">Dashboard label</label>
|
||||
<input type="text" name="label" value="{{ old('label') }}" required maxlength="120" placeholder="e.g. Main church giving"
|
||||
class="mt-1 w-full rounded-xl border-slate-200 text-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
</div>
|
||||
<div>
|
||||
<p class="mb-2 text-xs font-semibold text-slate-600">Organisation type</p>
|
||||
<div class="grid grid-cols-3 gap-2 sm:grid-cols-5">
|
||||
@foreach(['church'=>'Church','school'=>'School','mosque'=>'Mosque','ngo'=>'NGO','club'=>'Club'] as $key => $label)
|
||||
<button type="button" @click="setOrg('{{ $key }}')"
|
||||
:class="orgType === '{{ $key }}' ? 'border-indigo-500 bg-indigo-50 text-indigo-700' : 'border-slate-200 bg-white text-slate-500'"
|
||||
class="rounded-xl border-2 px-2 py-2 text-[11px] font-semibold">{{ $label }}</button>
|
||||
@endforeach
|
||||
</div>
|
||||
<input type="hidden" name="org_type" :value="orgType">
|
||||
</div>
|
||||
<input type="text" name="name" value="{{ old('name') }}" required :placeholder="cfg.namePh"
|
||||
class="w-full rounded-xl border-slate-200 text-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-500" x-text="cfg.descriptorLabel">Descriptor</label>
|
||||
<input type="text" name="denomination" value="{{ old('denomination') }}" :placeholder="cfg.descriptorPh"
|
||||
class="mt-1 w-full rounded-xl border-slate-200 text-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
</div>
|
||||
<textarea name="description" rows="2" placeholder="Short description"
|
||||
class="w-full rounded-xl border-slate-200 text-sm focus:border-indigo-500 focus:ring-indigo-500">{{ old('description') }}</textarea>
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<input type="text" name="phone" value="{{ old('phone') }}" placeholder="Phone" class="rounded-xl border-slate-200 text-sm">
|
||||
<input type="email" name="email" value="{{ old('email') }}" placeholder="Email" class="rounded-xl border-slate-200 text-sm">
|
||||
</div>
|
||||
<input type="text" name="service_times" value="{{ old('service_times') }}" :placeholder="cfg.timesPh"
|
||||
class="w-full rounded-xl border-slate-200 text-sm">
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-500">Logo</label>
|
||||
<input type="file" name="church_logo" accept="image/*" class="mt-1 block w-full text-xs">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-500">Cover image</label>
|
||||
<input type="file" name="church_cover" accept="image/*" class="mt-1 block w-full text-xs">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-500">Brand color</label>
|
||||
<input type="color" name="brand_color" value="{{ old('brand_color', '#1a3a5c') }}" class="mt-1 h-10 w-full rounded-xl border border-slate-200">
|
||||
</div>
|
||||
<label class="flex cursor-pointer items-center justify-between rounded-xl border border-slate-200 px-4 py-3">
|
||||
<div>
|
||||
<p class="text-sm font-semibold text-slate-900">Accept online giving</p>
|
||||
<p class="text-xs text-slate-500">Supporters can give online (9% platform fee).</p>
|
||||
</div>
|
||||
<input type="checkbox" name="accepts_payment" value="1" @checked(old('accepts_payment', true)) class="h-5 w-5 rounded text-indigo-600">
|
||||
</label>
|
||||
<div>
|
||||
<p class="mb-2 text-xs font-semibold text-slate-600">Giving categories</p>
|
||||
<template x-for="t in types" :key="t">
|
||||
<input type="hidden" name="collection_types[]" :value="t">
|
||||
</template>
|
||||
<div class="flex flex-wrap gap-2 mb-2">
|
||||
<template x-for="(t, i) in types" :key="t">
|
||||
<span class="inline-flex items-center gap-1 rounded-full border border-slate-200 px-3 py-1 text-xs font-semibold">
|
||||
<span x-text="t"></span>
|
||||
<button type="button" @click="removeType(i)" class="text-slate-400 hover:text-red-500">×</button>
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<input type="text" x-model="addInput" @keydown.enter.prevent="addType()" placeholder="Add category…" class="flex-1 rounded-xl border-slate-200 text-sm">
|
||||
<button type="button" @click="addType()" class="rounded-xl bg-indigo-50 px-3 py-2 text-xs font-semibold text-indigo-600">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary w-full">Create giving page</button>
|
||||
</form>
|
||||
</div>
|
||||
</x-user-layout>
|
||||
@@ -0,0 +1,35 @@
|
||||
<x-user-layout>
|
||||
<x-slot name="title">Giving Pages</x-slot>
|
||||
<div class="space-y-6">
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<div>
|
||||
<h1 class="text-xl font-semibold text-slate-900">Giving pages</h1>
|
||||
<p class="mt-1 text-sm text-slate-500">Branded pages with a printable QR for online giving.</p>
|
||||
</div>
|
||||
<x-btn.create :href="route('give.giving-pages.create')">New giving page</x-btn.create>
|
||||
</div>
|
||||
@if($qrCodes->isEmpty())
|
||||
<div class="rounded-2xl border border-dashed border-slate-200 bg-white px-6 py-12 text-center">
|
||||
<p class="text-sm text-slate-500">No giving pages yet.</p>
|
||||
<a href="{{ route('give.giving-pages.create') }}" class="mt-3 inline-block text-sm font-semibold text-indigo-600 hover:text-indigo-800">Create your first giving page</a>
|
||||
</div>
|
||||
@else
|
||||
<div class="grid gap-4 sm:grid-cols-2 xl:grid-cols-3">
|
||||
@foreach($qrCodes as $qr)
|
||||
<a href="{{ route('give.giving-pages.show', $qr) }}" class="rounded-2xl border border-slate-200 bg-white p-5 transition hover:border-indigo-200 hover:shadow-sm">
|
||||
<div class="flex items-start gap-4">
|
||||
@if(!empty($previewDataUris[$qr->id]))
|
||||
<img src="{{ $previewDataUris[$qr->id] }}" alt="" class="h-16 w-16 rounded-lg border border-slate-100 bg-white p-1">
|
||||
@endif
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="truncate font-semibold text-slate-900">{{ $qr->label }}</p>
|
||||
<p class="mt-0.5 truncate text-sm text-slate-500">{{ $qr->content()['name'] ?? 'Organisation' }}</p>
|
||||
<p class="mt-2 text-xs text-slate-400">{{ $qr->is_active ? 'Active' : 'Inactive' }} · /q/{{ $qr->short_code }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-user-layout>
|
||||
@@ -0,0 +1,38 @@
|
||||
@php
|
||||
$businessName = $qrCode->content()['business_name'] ?? $qrCode->label;
|
||||
@endphp
|
||||
|
||||
<x-modal name="delete-payment-qr" maxWidth="md">
|
||||
<div class="px-5 pb-6 pt-2 sm:px-6 sm:pb-6 sm:pt-4">
|
||||
<div class="flex flex-col items-center text-center sm:items-start sm:text-left">
|
||||
<div class="flex h-12 w-12 items-center justify-center rounded-full bg-red-100 text-red-600">
|
||||
<svg class="h-6 w-6" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h2 class="mt-4 text-lg font-semibold text-slate-900">Delete giving page?</h2>
|
||||
<p class="mt-2 text-sm leading-relaxed text-slate-500">
|
||||
<span class="font-medium text-slate-700">{{ $qrCode->label }}</span>
|
||||
@if($businessName !== $qrCode->label)
|
||||
<span class="text-slate-400">· {{ $businessName }}</span>
|
||||
@endif
|
||||
will be removed permanently. The payment link will stop working and printed codes for this till will no longer accept payments.
|
||||
</p>
|
||||
<p class="mt-3 break-all rounded-xl bg-slate-50 px-3 py-2 text-xs text-slate-500">{{ $qrCode->publicUrl() }}</p>
|
||||
</div>
|
||||
|
||||
<form method="post" action="{{ route('give.giving-pages.destroy', $qrCode) }}" class="mt-6 flex flex-col-reverse gap-2.5 sm:flex-row sm:justify-end">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="button"
|
||||
@click="$dispatch('close-modal', 'delete-payment-qr')"
|
||||
class="rounded-xl border border-slate-200 bg-white px-4 py-2.5 text-sm font-semibold text-slate-700 transition hover:bg-slate-50">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit"
|
||||
class="rounded-xl bg-red-600 px-4 py-2.5 text-sm font-semibold text-white transition hover:bg-red-700">
|
||||
Delete giving page
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</x-modal>
|
||||
@@ -0,0 +1,37 @@
|
||||
@php
|
||||
$shareUrl = $qrCode->publicUrl();
|
||||
$shareEnc = urlencode($shareUrl);
|
||||
$shareText = urlencode($qrCode->label . ' — pay with QR');
|
||||
@endphp
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<a href="{{ route('give.giving-pages.download', [$qrCode, 'png']) }}"
|
||||
class="rounded-lg border border-slate-200 px-3 py-1.5 text-sm font-medium text-slate-700 hover:bg-slate-50">PNG</a>
|
||||
<a href="{{ route('give.giving-pages.download', [$qrCode, 'svg']) }}"
|
||||
class="rounded-lg border border-slate-200 px-3 py-1.5 text-sm font-medium text-slate-700 hover:bg-slate-50">SVG</a>
|
||||
<a href="{{ route('give.giving-pages.download', [$qrCode, 'pdf']) }}"
|
||||
class="rounded-lg border border-slate-200 px-3 py-1.5 text-sm font-medium text-slate-700 hover:bg-slate-50">PDF</a>
|
||||
<div class="relative" x-data="{ open: false, copied: false }" @click.outside="open = false">
|
||||
<button type="button"
|
||||
@click="if(navigator.share){navigator.share({title:@js($qrCode->label),url:@js($shareUrl)}).catch(()=>{open=!open})}else{open=!open}"
|
||||
class="rounded-lg border border-slate-200 px-3 py-1.5 text-sm font-medium text-slate-700 hover:bg-slate-50">
|
||||
Share
|
||||
</button>
|
||||
<div x-show="open" x-cloak
|
||||
x-transition:enter="transition ease-out duration-100"
|
||||
x-transition:enter-start="opacity-0 scale-95"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
class="absolute right-0 z-50 mt-2 w-52 origin-top-right rounded-xl border border-slate-200 bg-white p-1.5 shadow-xl">
|
||||
<a href="https://wa.me/?text={{ $shareText }}%20{{ $shareEnc }}" target="_blank" rel="noopener"
|
||||
@click="open = false"
|
||||
class="flex items-center gap-2.5 rounded-lg px-3 py-2 text-sm text-slate-700 transition hover:bg-slate-50">
|
||||
WhatsApp
|
||||
</a>
|
||||
<button type="button"
|
||||
@click="navigator.clipboard.writeText(@js($shareUrl)).then(() => { copied = true; setTimeout(() => { copied = false; open = false }, 1500) })"
|
||||
class="flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm transition hover:bg-slate-50"
|
||||
:class="copied ? 'text-emerald-600' : 'text-slate-700'">
|
||||
<span x-text="copied ? 'Copied!' : 'Copy link'">Copy link</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,35 @@
|
||||
@php
|
||||
$showDownloads = $showDownloads ?? true;
|
||||
@endphp
|
||||
|
||||
<div class="overflow-hidden rounded-2xl border border-slate-200/80 bg-white shadow-sm">
|
||||
<div class="relative flex items-center justify-center bg-gradient-to-br from-indigo-50/60 via-white to-violet-50/40 px-8 py-10">
|
||||
<div class="w-full max-w-xs">
|
||||
<div class="overflow-hidden rounded-2xl bg-white p-4 shadow-2xl ring-1 ring-black/5">
|
||||
<img src="{{ $previewDataUri }}"
|
||||
alt="Payment QR code for {{ $qrCode->label }}"
|
||||
class="aspect-square w-full object-contain">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($showDownloads)
|
||||
<div class="border-t border-slate-100 bg-slate-50/50 px-6 py-5">
|
||||
<p class="mb-3 text-[10px] font-semibold uppercase tracking-[0.1em] text-slate-400">Download</p>
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
<a href="{{ route('give.giving-pages.download', [$qrCode, 'png']) }}"
|
||||
class="group flex flex-col items-center gap-1.5 rounded-xl border border-slate-200 bg-white py-3.5 text-center transition hover:border-indigo-300 hover:bg-indigo-50">
|
||||
<span class="text-xs font-semibold text-slate-600 transition group-hover:text-indigo-700">PNG</span>
|
||||
</a>
|
||||
<a href="{{ route('give.giving-pages.download', [$qrCode, 'svg']) }}"
|
||||
class="group flex flex-col items-center gap-1.5 rounded-xl border border-slate-200 bg-white py-3.5 text-center transition hover:border-violet-300 hover:bg-violet-50">
|
||||
<span class="text-xs font-semibold text-slate-600 transition group-hover:text-violet-700">SVG</span>
|
||||
</a>
|
||||
<a href="{{ route('give.giving-pages.download', [$qrCode, 'pdf']) }}"
|
||||
class="group flex flex-col items-center gap-1.5 rounded-xl border border-slate-200 bg-white py-3.5 text-center transition hover:border-rose-300 hover:bg-rose-50">
|
||||
<span class="text-xs font-semibold text-slate-600 transition group-hover:text-rose-700">PDF</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@@ -0,0 +1,86 @@
|
||||
<x-user-layout>
|
||||
<x-slot name="title">{{ $qrCode->label }}</x-slot>
|
||||
@php $c = $qrCode->content(); @endphp
|
||||
<div class="mx-auto max-w-6xl space-y-6">
|
||||
<div class="flex flex-wrap items-start justify-between gap-4">
|
||||
<div class="min-w-0 flex-1">
|
||||
<a href="{{ route('give.giving-pages.index') }}" class="text-sm text-slate-500 hover:text-slate-700">← All giving pages</a>
|
||||
<h1 class="mt-2 text-xl font-semibold text-slate-900">{{ $qrCode->label }}</h1>
|
||||
<p class="mt-1 text-sm text-slate-500">{{ $c['name'] ?? '' }}@if(!empty($c['denomination'])) · {{ $c['denomination'] }}@endif</p>
|
||||
</div>
|
||||
@include('give.giving-pages.partials.header-actions', ['qrCode' => $qrCode])
|
||||
</div>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="rounded-xl border border-emerald-100 bg-emerald-50 px-4 py-3 text-sm text-emerald-800">{{ session('success') }}</div>
|
||||
@endif
|
||||
|
||||
<div class="grid gap-6 lg:grid-cols-[380px_1fr]">
|
||||
<div class="lg:sticky lg:top-6 lg:self-start">
|
||||
@include('give.giving-pages.partials.preview-card', [
|
||||
'qrCode' => $qrCode,
|
||||
'previewDataUri' => $previewDataUri,
|
||||
'showDownloads' => false,
|
||||
])
|
||||
<p class="mt-4 break-all text-center text-sm text-indigo-600">{{ $qrCode->publicUrl() }}</p>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<form method="post" action="{{ route('give.giving-pages.update', $qrCode) }}" enctype="multipart/form-data" class="space-y-4 rounded-2xl border border-slate-200 bg-white p-6">
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
<h2 class="font-semibold text-slate-900">Settings</h2>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700">Dashboard label</label>
|
||||
<input type="text" name="label" value="{{ old('label', $qrCode->label) }}" class="mt-1 w-full rounded-xl border-slate-200 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700">Organisation name</label>
|
||||
<input type="text" name="name" value="{{ old('name', $c['name'] ?? '') }}" class="mt-1 w-full rounded-xl border-slate-200 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700">Description / denomination</label>
|
||||
<input type="text" name="denomination" value="{{ old('denomination', $c['denomination'] ?? '') }}" class="mt-1 w-full rounded-xl border-slate-200 text-sm">
|
||||
</div>
|
||||
<textarea name="description" rows="2" class="w-full rounded-xl border-slate-200 text-sm">{{ old('description', $c['description'] ?? '') }}</textarea>
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<input type="text" name="phone" value="{{ old('phone', $c['phone'] ?? '') }}" placeholder="Phone" class="rounded-xl border-slate-200 text-sm">
|
||||
<input type="email" name="email" value="{{ old('email', $c['email'] ?? '') }}" placeholder="Email" class="rounded-xl border-slate-200 text-sm">
|
||||
</div>
|
||||
<label class="flex items-center gap-2 text-sm text-slate-700">
|
||||
<input type="checkbox" name="accepts_payment" value="1" @checked(old('accepts_payment', $c['accepts_payment'] ?? false)) class="rounded border-slate-300 text-indigo-600">
|
||||
Accept online giving
|
||||
</label>
|
||||
<label class="flex items-center gap-2 text-sm text-slate-700">
|
||||
<input type="checkbox" name="is_active" value="1" @checked($qrCode->is_active) class="rounded border-slate-300 text-indigo-600">
|
||||
Page is active
|
||||
</label>
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<div>
|
||||
<label class="block text-xs text-slate-500">Replace logo</label>
|
||||
<input type="file" name="church_logo" accept="image/*" class="mt-1 block w-full text-xs">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-slate-500">Replace cover</label>
|
||||
<input type="file" name="church_cover" accept="image/*" class="mt-1 block w-full text-xs">
|
||||
</div>
|
||||
</div>
|
||||
@foreach($c['collection_types'] ?? ['Offering','Tithe','Donation'] as $i => $type)
|
||||
<input type="hidden" name="collection_types[]" value="{{ $type }}">
|
||||
@endforeach
|
||||
<button type="submit" class="btn-primary">Save changes</button>
|
||||
</form>
|
||||
|
||||
<div class="rounded-2xl border border-red-100 bg-red-50/40 p-6">
|
||||
<h2 class="font-semibold text-red-700">Danger zone</h2>
|
||||
<p class="mt-1 text-sm text-slate-500">Remove this giving page and deactivate its QR.</p>
|
||||
<button type="button" @click="$dispatch('open-modal', 'delete-giving-page')"
|
||||
class="mt-4 rounded-xl border border-red-200 bg-white px-4 py-2 text-sm font-semibold text-red-700 hover:bg-red-50">
|
||||
Delete giving page
|
||||
</button>
|
||||
</div>
|
||||
@include('give.giving-pages.partials.delete-modal', ['qrCode' => $qrCode])
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-user-layout>
|
||||
@@ -0,0 +1,27 @@
|
||||
<x-user-layout>
|
||||
<x-slot name="title">Payouts</x-slot>
|
||||
@php $fmt = fn ($m) => 'GHS '.number_format($m / 100, 2); @endphp
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<h1 class="text-xl font-semibold text-slate-900">Payouts</h1>
|
||||
<p class="mt-1 text-sm text-slate-500">Takings settle into your Ladill wallet (5% fee), then withdraw to bank or MoMo.</p>
|
||||
</div>
|
||||
<div class="grid gap-4 sm:grid-cols-2">
|
||||
<div class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||
<p class="text-xs font-medium uppercase tracking-wide text-slate-400">Total received (net)</p>
|
||||
<p class="mt-1.5 text-2xl font-semibold text-slate-900">{{ $fmt($revenueMinor) }}</p>
|
||||
</div>
|
||||
<div class="rounded-2xl border border-indigo-100 bg-indigo-50/60 p-5">
|
||||
<p class="text-xs font-medium uppercase tracking-wide text-indigo-600">Available in wallet</p>
|
||||
<p class="mt-1.5 text-2xl font-semibold text-slate-900">{{ $fmt($balanceMinor) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-2xl border border-amber-100 bg-amber-50/60 px-6 py-4 text-sm text-amber-800">
|
||||
Withdraw to bank or MoMo from your Ladill account wallet. Ladill Pay integration will add in-app withdrawal history here.
|
||||
</div>
|
||||
<a href="{{ $accountWalletUrl }}" class="btn-primary">
|
||||
Open wallet & withdraw
|
||||
<span aria-hidden="true">→</span>
|
||||
</a>
|
||||
</div>
|
||||
</x-user-layout>
|
||||
@@ -0,0 +1,69 @@
|
||||
<x-user-layout>
|
||||
<x-slot name="title">Settings</x-slot>
|
||||
|
||||
<div class="mx-auto max-w-2xl">
|
||||
<h1 class="text-xl font-semibold tracking-tight text-slate-900">Settings</h1>
|
||||
<p class="mt-0.5 text-sm text-slate-500">Notification preferences for your payment QRs.</p>
|
||||
|
||||
@if (session('success'))
|
||||
<div class="mt-4 rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-800">{{ session('success') }}</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('account.settings.update') }}" class="mt-6 space-y-4">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||
<h2 class="text-sm font-semibold text-slate-900">Notifications</h2>
|
||||
<p class="mt-1 text-xs text-slate-500">Choose what we email you about payments and your account.</p>
|
||||
|
||||
<div class="mt-4">
|
||||
<label for="notify_email" class="block text-[11px] font-medium text-slate-500">Notifications email</label>
|
||||
<input type="email" id="notify_email" name="notify_email"
|
||||
value="{{ old('notify_email', $settings->notify_email ?? $account->email) }}"
|
||||
class="mt-1 w-full rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-sm focus:border-indigo-300 focus:bg-white focus:outline-none focus:ring-2 focus:ring-indigo-100">
|
||||
@error('notify_email') <p class="mt-1 text-xs text-rose-600">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<label class="mt-4 flex items-center justify-between gap-4">
|
||||
<span>
|
||||
<span class="block text-sm font-medium text-slate-800">Incoming payments</span>
|
||||
<span class="block text-xs text-slate-400">Email when a customer pays via your payment QR.</span>
|
||||
</span>
|
||||
<input type="checkbox" name="notify_registrations" value="1"
|
||||
@checked(old('notify_registrations', $settings->notify_registrations ?? true))
|
||||
class="h-5 w-5 rounded border-slate-300 text-indigo-600 focus:ring-indigo-500">
|
||||
</label>
|
||||
|
||||
<label class="mt-4 flex items-center justify-between gap-4">
|
||||
<span>
|
||||
<span class="block text-sm font-medium text-slate-800">Payouts & settlements</span>
|
||||
<span class="block text-xs text-slate-400">Email when takings settle into your wallet.</span>
|
||||
</span>
|
||||
<input type="checkbox" name="notify_payouts" value="1"
|
||||
@checked(old('notify_payouts', $settings->notify_payouts ?? true))
|
||||
class="h-5 w-5 rounded border-slate-300 text-indigo-600 focus:ring-indigo-500">
|
||||
</label>
|
||||
|
||||
<label class="mt-4 flex items-center justify-between gap-4">
|
||||
<span>
|
||||
<span class="block text-sm font-medium text-slate-800">Ladill Give updates</span>
|
||||
<span class="block text-xs text-slate-400">Occasional emails about new features and improvements.</span>
|
||||
</span>
|
||||
<input type="checkbox" name="product_updates" value="1"
|
||||
@checked(old('product_updates', $settings->product_updates ?? true))
|
||||
class="h-5 w-5 rounded border-slate-300 text-indigo-600 focus:ring-indigo-500">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-primary">
|
||||
Save settings
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<p class="mt-6 text-xs text-slate-400">
|
||||
Profile, password, and security are managed on
|
||||
<a href="{{ ladill_account_url('account-settings') }}" class="font-medium text-indigo-600 hover:text-indigo-800">account.ladill.com</a>.
|
||||
</p>
|
||||
</div>
|
||||
</x-user-layout>
|
||||
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Signed out — Ladill Give</title>
|
||||
@include('partials.favicon')
|
||||
</head>
|
||||
<body class="flex min-h-screen items-center justify-center bg-slate-100 font-sans antialiased">
|
||||
<div class="text-center">
|
||||
<p class="text-slate-600">You have been signed out of Ladill Give.</p>
|
||||
<a href="{{ route('sso.connect') }}" class="mt-4 inline-block text-sm font-medium text-indigo-600 hover:text-indigo-800">Sign in again</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user