Feature: Creating Cart Service, Cart Events, Save For Later functionality, Adding Filament Views for viewing abandoned carts
This commit is contained in:
@@ -39,11 +39,13 @@ class CartResource extends Resource
|
||||
}
|
||||
|
||||
/**
|
||||
* Count only, not a fetch — no rows are loaded. Scoped to genuinely abandoned
|
||||
* carts specifically (mirrors ListCarts::getTabs()'s "Abandoned" query, not
|
||||
* "Ongoing"), since that's the number a staff member glancing at the sidebar
|
||||
* actually wants: how many carts might need following up on, not the total
|
||||
* including ones someone is actively shopping in right now.
|
||||
* 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
|
||||
{
|
||||
|
||||
@@ -17,19 +17,34 @@ class ListCarts extends ListRecords
|
||||
* "Completed" instead means the cart has an order with `placed_at` set (a
|
||||
* placed, not just drafted, order).
|
||||
*
|
||||
* "Ongoing" vs "Abandoned" both start from `Cart::scopeActive()` (not yet
|
||||
* converted to an order) and split on `updated_at` against
|
||||
* `CartResource::abandonedCutoff()` — Lunar has no time-based staleness signal
|
||||
* of its own, so recent activity is the only thing distinguishing a cart
|
||||
* someone is shopping in right now from one genuinely left behind.
|
||||
* `Cart::scopeActive()` (not yet converted to an order) actually mixes two
|
||||
* distinct states: no order started at all, vs. a draft order exists
|
||||
* (`placed_at IS NULL`) but was never placed — checkout was started, not
|
||||
* finished. That's a real difference in purchase intent (a cart with a
|
||||
* draft order is a much stronger signal than one with no order at all) and
|
||||
* in reachability (checkout usually captures an email even for a guest),
|
||||
* so they get separate tabs rather than one combined "no order yet"
|
||||
* bucket — same distinction Modules\Core\Recovery\Events\CartAbandoned /
|
||||
* Modules\Core\Recovery\Events\CheckoutAbandoned draw.
|
||||
*
|
||||
* "Ongoing" vs the two abandoned tabs all split on `updated_at` against
|
||||
* `CartResource::abandonedCutoff()` — Lunar has no time-based staleness
|
||||
* signal of its own, so recent activity is the only thing distinguishing a
|
||||
* cart someone is shopping in right now from one genuinely left behind.
|
||||
*/
|
||||
public function getTabs(): array
|
||||
{
|
||||
return [
|
||||
'ongoing' => Tab::make('Ongoing')
|
||||
->modifyQueryUsing(fn (Builder $query) => $query->active()->where('updated_at', '>', CartResource::abandonedCutoff())),
|
||||
'abandoned' => Tab::make('Abandoned')
|
||||
->modifyQueryUsing(fn (Builder $query) => $query->active()->where('updated_at', '<=', CartResource::abandonedCutoff())),
|
||||
'abandoned_cart' => Tab::make('Abandoned Cart')
|
||||
->modifyQueryUsing(fn (Builder $query) => $query
|
||||
->whereDoesntHave('orders')
|
||||
->where('updated_at', '<=', CartResource::abandonedCutoff())),
|
||||
'abandoned_checkout' => Tab::make('Abandoned Checkout')
|
||||
->modifyQueryUsing(fn (Builder $query) => $query
|
||||
->whereHas('orders', fn (Builder $query) => $query->whereNull('placed_at'))
|
||||
->where('updated_at', '<=', CartResource::abandonedCutoff())),
|
||||
'completed' => Tab::make('Completed')
|
||||
->modifyQueryUsing(fn (Builder $query) => $query->whereHas(
|
||||
'orders',
|
||||
|
||||
Reference in New Issue
Block a user