columns([ TextColumn::make('order_number') ->label('Number') ->searchable() ->sortable(), TextColumn::make('customer_name') ->label('Customer') ->searchable() ->sortable(), TextColumn::make('status') ->label('Status') ->badge() ->state(function (Order $record): array { $badges = []; $badges[] = $record->status; if ($record->is_exchange || $record->childOrders->isNotEmpty()) { $badges[] = 'exchange'; } return $badges; }) ->color(fn (string $state): string => match ($state) { 'pending' => 'info', 'processing' => 'warning', 'shipped' => 'info', 'delivered' => 'success', 'cancelled' => 'danger', 'returned' => 'purple', 'exchange' => 'success', default => 'gray', }) ->icon(fn (string $state): ?string => match ($state) { 'pending' => 'heroicon-m-sparkles', 'processing' => 'heroicon-m-arrow-path', 'shipped' => 'heroicon-m-truck', 'delivered' => 'heroicon-m-check-circle', 'cancelled' => 'heroicon-m-x-circle', 'returned' => 'heroicon-m-arrow-uturn-left', 'exchange' => 'heroicon-m-arrow-path-rounded-square', default => null, }) ->formatStateUsing(fn (string $state): string => match ($state) { 'pending' => 'New', 'returned' => 'Returned', 'exchange' => 'Exchange', default => ucfirst($state), }) ->sortable(), TextColumn::make('grand_total') ->label('Total price') ->money('BDT') ->alignEnd() ->sortable(), TextColumn::make('shipping_cost') ->label('Shipping cost') ->money('BDT') ->alignEnd() ->sortable() ->toggleable(isToggledHiddenByDefault: true), TextColumn::make('created_at') ->label('Order date') ->date('M j, Y') ->sortable() ->toggleable(), ]) ->defaultSort('created_at', 'desc') ->filters([ // Filter by Order Status SelectFilter::make('status') ->options([ 'pending' => 'Pending', 'processing' => 'Processing', 'shipped' => 'Shipped', 'delivered' => 'Delivered', 'cancelled' => 'Cancelled', 'returned' => 'Returned', ]), // Filter by Payment Status SelectFilter::make('payment_status') ->options([ 'pending' => 'Pending', 'paid' => 'Paid', 'failed' => 'Failed', 'refunded' => 'Refunded', ]), // Filter by Payment Method SelectFilter::make('payment_method') ->options([ 'cod' => 'Cash On Delivery (COD)', 'sslcommerz' => 'SSLCommerz', 'bkash' => 'bKash', 'nagad' => 'Nagad', ]), // Filter by Courier Booking State TernaryFilter::make('has_courier_booking') ->label('Has Courier Booking') ->queries( true: fn (Builder $query) => $query->whereHas('courierBooking'), false: fn (Builder $query) => $query->whereDoesntHave('courierBooking'), ), // Filter by Ordered Date Range Filter::make('created_at') ->form([ DatePicker::make('created_from') ->label('Ordered From'), DatePicker::make('created_until') ->label('Ordered Until'), ]) ->query(function (Builder $query, array $data): Builder { return $query ->when( $data['created_from'], fn (Builder $query, $date): Builder => $query->whereDate('created_at', '>=', $date), ) ->when( $data['created_until'], fn (Builder $query, $date): Builder => $query->whereDate('created_at', '<=', $date), ); }) ]) ->actions([ ActionGroup::make([ EditAction::make(), Action::make('update_status') ->label('Update Status') ->icon('heroicon-o-check-circle') ->color('primary') ->modalWidth('sm') ->form([ Select::make('status') ->label('Status') ->options([ 'pending' => 'Pending', 'processing' => 'Processing', 'shipped' => 'Shipped', 'delivered' => 'Delivered', 'cancelled' => 'Cancelled', 'returned' => 'Returned', ]) ->default(fn (Order $record) => $record->status) ->required(), ]) ->action(function (Order $record, array $data): void { $oldStatus = $record->status; if ($oldStatus === $data['status']) { return; } $record->update(['status' => $data['status']]); $record->histories()->create([ 'user_id' => auth()->id(), 'action' => 'status_changed', 'from_status' => $oldStatus, 'to_status' => $data['status'], 'notes' => 'Status updated to ' . $data['status'] . ' via quick action.', ]); try { \App\Services\NotificationDispatcher::dispatchForOrder($record, $data['status']); } catch (\Exception $e) { // Silence notifications error } Notification::make() ->title('Order Status Updated') ->success() ->send(); }), Action::make('one_click_ship') ->label('One-Click Ship') ->icon('heroicon-o-truck') ->color('primary') ->modalWidth('sm') ->visible(fn (Order $record): bool => in_array($record->status, ['pending', 'processing']) && !$record->courierBooking()->exists()) ->mountUsing(function ($action, Order $record) { $defaultCourier = Setting::getValue('default_courier'); $isDefaultActive = false; if ($defaultCourier === 'steadfast') { $isDefaultActive = Setting::getValue('steadfast_enabled') === 'on' || Setting::getValue('steadfast_enabled') === true || Setting::getValue('steadfast_enabled') === '1' || Setting::getValue('steadfast_enabled') === 1; } elseif ($defaultCourier === 'pathao') { $isDefaultActive = Setting::getValue('pathao_enabled') === 'on' || Setting::getValue('pathao_enabled') === true || Setting::getValue('pathao_enabled') === '1' || Setting::getValue('pathao_enabled') === 1; } if ($isDefaultActive) { $result = CourierService::bookConsignment($record, $defaultCourier); if ($result['success']) { Notification::make() ->title('Shipment Booked') ->body($result['message']) ->success() ->send(); } else { Notification::make() ->title('Booking Failed') ->body($result['message']) ->danger() ->send(); } $action->cancel(); } }) ->form([ Select::make('courier') ->label('Select Courier') ->options(function () { $options = []; if (Setting::getValue('steadfast_enabled') === 'on' || Setting::getValue('steadfast_enabled') === true || Setting::getValue('steadfast_enabled') === '1' || Setting::getValue('steadfast_enabled') === 1) { $options['steadfast'] = 'Steadfast Courier'; } if (Setting::getValue('pathao_enabled') === 'on' || Setting::getValue('pathao_enabled') === true || Setting::getValue('pathao_enabled') === '1' || Setting::getValue('pathao_enabled') === 1) { $options['pathao'] = 'Pathao Courier'; } return $options; }) ->default(fn () => Setting::getValue('default_courier', 'steadfast')) ->required(), ]) ->action(function (Order $record, array $data) { $result = CourierService::bookConsignment($record, $data['courier']); if ($result['success']) { Notification::make() ->title('Shipment Booked') ->body($result['message']) ->success() ->send(); } else { Notification::make() ->title('Booking Failed') ->body($result['message']) ->danger() ->send(); } }), Action::make('download_invoice') ->label('Invoice') ->icon('heroicon-o-arrow-down-tray') ->color('info') ->url(fn (Order $record): string => route('orders.invoice', ['order' => $record->id])) ->openUrlInNewTab(), DeleteAction::make(), ]) ->color('info'), ]) ->bulkActions([ BulkActionGroup::make([ // Bulk Status Updates BulkAction::make('bulk_status_update') ->label('Update Status') ->icon('heroicon-o-check-circle') ->color('primary') ->form([ Select::make('status') ->label('Select Status') ->options([ 'pending' => 'Pending', 'processing' => 'Processing', 'shipped' => 'Shipped', 'delivered' => 'Delivered', 'cancelled' => 'Cancelled', 'returned' => 'Returned', ]) ->required(), ]) ->action(function (\Illuminate\Support\Collection $records, array $data): void { foreach ($records as $record) { $oldStatus = $record->status; $record->update(['status' => $data['status']]); $record->histories()->create([ 'user_id' => auth()->id(), 'action' => 'status_changed', 'from_status' => $oldStatus, 'to_status' => $data['status'], 'notes' => 'Status bulk-updated to ' . $data['status'] . ' by administrator.', ]); try { \App\Services\NotificationDispatcher::dispatchForOrder($record, $data['status']); } catch (\Exception $e) { // Fallback if notifications fail } } Notification::make() ->title('Orders Updated') ->body('Status updated successfully for ' . $records->count() . ' orders.') ->success() ->send(); }), // Bulk Print Invoices BulkAction::make('bulk_print_invoices') ->label('Print Invoices') ->icon('heroicon-o-printer') ->color('info') ->action(function (\Illuminate\Support\Collection $records) { $ids = $records->pluck('id')->implode(','); return redirect()->away(route('orders.invoice.bulk', ['ids' => $ids])); }), // Bulk One-Click Ship BulkAction::make('bulk_one_click_ship') ->label('One-Click Ship') ->icon('heroicon-o-truck') ->color('primary') ->modalWidth('sm') ->mountUsing(function ($action, \Illuminate\Support\Collection $records) { $defaultCourier = Setting::getValue('default_courier'); $isDefaultActive = false; if ($defaultCourier === 'steadfast') { $isDefaultActive = Setting::getValue('steadfast_enabled') === 'on' || Setting::getValue('steadfast_enabled') === true || Setting::getValue('steadfast_enabled') === '1' || Setting::getValue('steadfast_enabled') === 1; } elseif ($defaultCourier === 'pathao') { $isDefaultActive = Setting::getValue('pathao_enabled') === 'on' || Setting::getValue('pathao_enabled') === true || Setting::getValue('pathao_enabled') === '1' || Setting::getValue('pathao_enabled') === 1; } if ($isDefaultActive) { $successCount = 0; $failCount = 0; $messages = []; foreach ($records as $record) { if (!in_array($record->status, ['pending', 'processing']) || $record->courierBooking()->exists()) { $failCount++; continue; } $result = CourierService::bookConsignment($record, $defaultCourier); if ($result['success']) { $successCount++; } else { $failCount++; $messages[] = "Order #{$record->order_number}: {$result['message']}"; } } if ($successCount > 0) { Notification::make() ->title('Bulk Shipment Booked') ->body("Successfully booked shipment for {$successCount} order(s) via " . ($defaultCourier === 'steadfast' ? 'Steadfast' : 'Pathao') . ".") ->success() ->send(); } if ($failCount > 0) { $failBody = "Failed to book shipment for {$failCount} order(s)."; if (count($messages) > 0) { $failBody .= " Errors: " . implode('; ', array_slice($messages, 0, 3)); } Notification::make() ->title('Some Bookings Failed') ->body($failBody) ->danger() ->send(); } $action->cancel(); } }) ->form([ Select::make('courier') ->label('Select Courier') ->options(function () { $options = []; if (Setting::getValue('steadfast_enabled') === 'on' || Setting::getValue('steadfast_enabled') === true || Setting::getValue('steadfast_enabled') === '1' || Setting::getValue('steadfast_enabled') === 1) { $options['steadfast'] = 'Steadfast Courier'; } if (Setting::getValue('pathao_enabled') === 'on' || Setting::getValue('pathao_enabled') === true || Setting::getValue('pathao_enabled') === '1' || Setting::getValue('pathao_enabled') === 1) { $options['pathao'] = 'Pathao Courier'; } return $options; }) ->default(fn () => Setting::getValue('default_courier', 'steadfast')) ->required(), ]) ->action(function (\Illuminate\Support\Collection $records, array $data): void { $courier = $data['courier']; $successCount = 0; $failCount = 0; $messages = []; foreach ($records as $record) { if (!in_array($record->status, ['pending', 'processing']) || $record->courierBooking()->exists()) { $failCount++; continue; } $result = CourierService::bookConsignment($record, $courier); if ($result['success']) { $successCount++; } else { $failCount++; $messages[] = "Order #{$record->order_number}: {$result['message']}"; } } if ($successCount > 0) { Notification::make() ->title('Bulk Shipment Booked') ->body("Successfully booked shipment for {$successCount} order(s) via " . ($courier === 'steadfast' ? 'Steadfast' : 'Pathao') . ".") ->success() ->send(); } if ($failCount > 0) { $failBody = "Failed to book shipment for {$failCount} order(s)."; if (count($messages) > 0) { $failBody .= " Errors: " . implode('; ', array_slice($messages, 0, 3)); } Notification::make() ->title('Some Bookings Failed') ->body($failBody) ->danger() ->send(); } }), DeleteBulkAction::make(), ]), ]); } }