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(); } }), ]); } }