schema([
Section::make('Donation Details')
->schema([
Grid::make(5)->schema([
Placeholder::make('status')
->label('Status')
->content(function (ScheduledGivingDonation $scheduledGivingDonation) {
if ($scheduledGivingDonation->is_active) {
return new HtmlString('Enabled');
} else {
return new HtmlString('Disabled');
}
}),
Placeholder::make('scheduledGivingCampaign.title')
->label('Campaign')
->content(fn (ScheduledGivingDonation $record): HtmlString => new HtmlString('' . $record->scheduledGivingCampaign->title . '')),
Placeholder::make('Amount')
->content(fn (ScheduledGivingDonation $record): HtmlString => new Htmlstring('' . Helpers::formatMoneyGlobal($record->total_amount) . '')),
Placeholder::make('admin_contribution')
->label('Admin Contribution')
->content(fn (ScheduledGivingDonation $record): HtmlString => new Htmlstring('' . Helpers::formatMoneyGlobal($record->amount_admin) . '')),
Placeholder::make('is_zakat')
->label('Zakat?')
->content(fn (ScheduledGivingDonation $record): HtmlString => new Htmlstring('' . ($record->is_zakat ? 'Yes' : 'No') . '')),
Placeholder::make('is_gift_aid')
->label('Gift Aid?')
->content(fn (ScheduledGivingDonation $record): HtmlString => new Htmlstring('' . ($record->is_gift_aid ? 'Yes' : 'No') . '')),
]),
Fieldset::make('Customer Details')
->columns(3)
->schema([
Placeholder::make('name')
->label('Name')
->content(fn (ScheduledGivingDonation $donation) => $donation->customer->name),
Placeholder::make('email')
->label('Email')
->content(fn (ScheduledGivingDonation $donation) => $donation->customer->email),
Placeholder::make('phone')
->label('Phone')
->content(fn (ScheduledGivingDonation $donation) => strlen(trim($phone = $donation->customer->phone)) > 0 ? $phone : new HtmlString('(Not given)')),
])
->visible(fn (ScheduledGivingDonation $donation) => (bool) $donation->customer),
Fieldset::make('Address')
->columns(3)
->schema([
Placeholder::make('house')
->label('House')
->content(fn (ScheduledGivingDonation $donation) => strlen(trim($v = $donation->address->house)) ? $v : new HtmlString('(Not given)')),
Placeholder::make('street')
->label('Street')
->content(fn (ScheduledGivingDonation $donation) => strlen(trim($v = $donation->address->street)) ? $v : new HtmlString('(Not given)')),
Placeholder::make('town')
->label('Town')
->content(fn (ScheduledGivingDonation $donation) => strlen(trim($v = $donation->address->town)) ? $v : new HtmlString('(Not given)')),
Placeholder::make('state')
->label('State')
->content(fn (ScheduledGivingDonation $donation) => strlen(trim($v = $donation->address->state)) ? $v : new HtmlString('(Not given)')),
Placeholder::make('postcode')
->label('Postcode')
->content(fn (ScheduledGivingDonation $donation) => strlen(trim($v = $donation->address->postcode)) ? $v : new HtmlString('(Not given)')),
Placeholder::make('country_code')
->label('Country')
->content(fn (ScheduledGivingDonation $donation) => strlen(trim($v = $donation->address->country_code)) ? $v : new HtmlString('(Not given)')),
])
->visible(fn (ScheduledGivingDonation $donation) => (bool) $donation->address),
Fieldset::make('Appeal')
->columns(2)
->schema([
Placeholder::make('appeal.name')
->label('Name')
->content(fn (ScheduledGivingDonation $donation) => $donation->appeal->name),
Placeholder::make('appeal.url')
->label('URL')
->content(fn (ScheduledGivingDonation $donation): HtmlString => new HtmlString('' . $donation->appeal->url() . '')),
])
->visible(fn (ScheduledGivingDonation $donation) => (bool) $donation->appeal),
Repeater::make('allocations')
->schema([
TextInput::make('type')
->label('Donation Type')
->required()
->disabled(),
TextInput::make('amount')
->label('Amount')
->numeric()
->required()
->disabled(),
])
->columns()
->formatStateUsing(function ($state) {
return collect($state)
->map(fn ($amount, $type) => ['type' => DonationType::find($type)?->display_name, 'amount' => $amount])
->values()
->toArray();
})
->addable(false)
->deletable(false),
]),
]);
}
public static function getEloquentQuery(): Builder
{
return parent::getEloquentQuery()
->withCount(['payments as paid_payments_count' => fn ($q) => $q->where('is_paid', true)->whereNull('deleted_at')]);
}
public static function table(Table $table): Table
{
return $table
->columns([
IconColumn::make('is_active')
->label('')
->boolean()
->trueIcon('heroicon-o-check-circle')
->falseIcon('heroicon-o-x-circle')
->trueColor('success')
->falseColor('danger')
->tooltip(fn (ScheduledGivingDonation $r) => $r->is_active ? 'Active' : 'Cancelled'),
TextColumn::make('customer.name')
->label('Donor')
->description(fn (ScheduledGivingDonation $d) => $d->customer?->email)
->sortable()
->searchable(query: function (Builder $query, string $search) {
$query->whereHas('customer', fn (Builder $q) => $q
->where('first_name', 'like', "%{$search}%")
->orWhere('last_name', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%"));
}),
TextColumn::make('scheduledGivingCampaign.title')
->label('Campaign')
->badge()
->color(fn ($state) => match ($state) {
'30 Nights of Giving' => 'primary',
'10 Days of Giving' => 'success',
'Night of Power' => 'warning',
default => 'gray',
}),
TextColumn::make('total_amount')
->label('Per Night')
->money('gbp', divideBy: 100)
->sortable(),
TextColumn::make('payments_count')
->label('Progress')
->counts('payments')
->formatStateUsing(function ($state, ScheduledGivingDonation $record) {
$total = (int) $state;
if ($total === 0) return '—';
$paid = (int) ($record->paid_payments_count ?? 0);
$pct = round($paid / $total * 100);
return "{$paid}/{$total} ({$pct}%)";
})
->color(function ($state, ScheduledGivingDonation $record) {
$total = (int) $state;
if ($total === 0) return 'gray';
$paid = (int) ($record->paid_payments_count ?? 0);
$pct = $paid / $total * 100;
return $pct >= 80 ? 'success' : ($pct >= 40 ? 'warning' : 'danger');
})
->badge(),
TextColumn::make('created_at')
->label('Signed Up')
->date('d M Y')
->description(fn (ScheduledGivingDonation $d) => $d->created_at?->diffForHumans())
->sortable(),
TextColumn::make('reference_code')
->label('Ref')
->searchable()
->fontFamily('mono')
->copyable()
->toggleable(isToggledHiddenByDefault: true),
])
->searchable()
->bulkActions(static::getBulkActions())
->actions(static::getTableRowActions())
->defaultSort('created_at', 'desc')
->filters([
Filter::make('confirmed')
->query(fn (Builder $query): Builder => $query->whereHas('donationConfirmation', fn ($q) => $q->whereNotNull('confirmed_at'))),
SelectFilter::make('scheduled_giving_campaign_id')
->options(ScheduledGivingCampaign::get()->pluck('title', 'id')),
]);
}
public static function getRelations(): array
{
return [
ScheduledGivingDonationPayments::class,
InternalNotesRelationManager::class,
];
}
public static function getPages(): array
{
return [
'index' => ListScheduledGivingDonations::route('/'),
'edit' => EditScheduledGivingDonation::route('/{record}/edit'),
];
}
private static function getTableRowActions(): array
{
return [
ViewAction::make(),
ActionGroup::make([
Action::make('view_customer')
->label('View Customer')
->icon('heroicon-s-eye')
->visible(fn () => (Auth::user()?->hasPermissionTo('view-customer') || Auth::user()?->hasRole('Superadmin')) || Auth::user()?->hasRole('Superadmin')),
]),
];
}
private static function getBulkActions()
{
return BulkActionGroup::make([
BulkAction::make('send_to_zapier')
->label('Send to Zapier')
->icon('heroicon-s-envelope')
->visible(fn (ScheduledGivingDonation $donation) => (Auth::user()?->hasPermissionTo('view-donation') || Auth::user()?->hasRole('Superadmin')))
->action(function ($records) {
foreach ($records as $donation) {
dispatch(new SendCustomer($donation->customer));
Notification::make()
->success()
->title($donation->reference_code . ' pushed to Zapier.')
->send();
}
}),
]);
}
}