where(fn (Builder $query) => $query->whereNotNull('user_id')->orWhereNotNull('customer_id')); } /** * Count only, not a fetch — no rows are loaded. Combines BOTH abandoned * states (`active()` already covers "no order at all" and "draft order, * never placed" together — see ListCarts::getTabs()'s "Abandoned Cart" / * "Abandoned Checkout" tabs for where they're split apart), not "Ongoing" * — the badge is meant to answer "how many carts might need following up * on," not the total including ones someone is actively shopping in right * now. */ public static function getNavigationBadge(): ?string { return (string) static::getEloquentQuery()->active()->where('updated_at', '<=', static::abandonedCutoff())->count(); } /** * `Cart::scopeActive()` (not-yet-converted-to-an-order carts) mixes two very * different things together: a cart someone is actively shopping in right now, * and one that's genuinely been left behind. Lunar tracks no time-based * staleness signal of its own — `Cart::updated_at` plus a configurable * threshold (`config('core.cart.abandoned_after')`, default 1 hour) is what * this resource uses to tell them apart. A cart with no recent activity is * "Abandoned"; anything more recent is "Ongoing". */ public static function abandonedCutoff(): Carbon { return now()->sub(config('core.cart.abandoned_after', '1 hour')); } public static function table(Table $table): Table { return $table ->columns([ TextColumn::make('id') ->label('Cart') ->sortable(), TextColumn::make('customer.full_name') ->label('Customer') ->placeholder('—') ->searchable() ->url(fn (Cart $record) => $record->customer_id !== null ? CustomerResource::getUrl('view', ['record' => $record->customer_id]) : null), TextColumn::make('user.email') ->label('User') ->placeholder('—') ->searchable(), TextColumn::make('lines_count') ->label('Lines') ->counts('lines') ->sortable(), TextColumn::make('lines_sum_quantity') ->label('Items') ->sum('lines', 'quantity') ->sortable(), TextColumn::make('currency.code') ->label('Currency'), TextColumn::make('updated_at') ->label('Last activity') ->dateTime() ->sortable(), ]) ->recordActions([ ViewAction::make(), ]) ->defaultSort('updated_at', 'desc'); } public static function getPages(): array { return [ 'index' => ListCarts::route('/'), 'view' => ViewCart::route('/{record}'), ]; } public static function canCreate(): bool { return false; } }