active()->where('updated_at', '<=', static::abandonedCutoff())->count(); } public static function lifecycle(): CartLifecycleService { return app(CartLifecycleService::class); } /** * `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 static::lifecycle()->abandonedCutoff(); } 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; } }