diff --git a/app/Enums/IconEnum.php b/app/Enums/IconEnum.php index 4674982..7923179 100644 --- a/app/Enums/IconEnum.php +++ b/app/Enums/IconEnum.php @@ -51,6 +51,7 @@ enum IconEnum: string case APPROVED = 'lucide-check-check'; case PRODUCTION = 'lucide-refresh-cw'; case SHIPPED = 'lucide-send'; + case INVOICING = 'lucide-calendar'; case INVOICED = 'lucide-credit-card'; // Shipping Types (THEY_SHIP => SHIPPING_ENTRY) diff --git a/app/Enums/OrderStatus.php b/app/Enums/OrderStatus.php index b598a37..decc509 100644 --- a/app/Enums/OrderStatus.php +++ b/app/Enums/OrderStatus.php @@ -8,11 +8,12 @@ enum OrderStatus: string implements HasColor, HasIcon, HasLabel { - case DRAFT = 'Draft'; - case APPROVED = 'Approved'; - case PRODUCTION = 'Production'; - case SHIPPED = 'Shipped'; - case INVOICED = 'Invoiced'; + case DRAFT = 'Draft'; + case APPROVED = 'Approved'; + case PRODUCTION = 'Production'; + case SHIPPED = 'Shipped'; + case READY_FOR_INVOICE = 'Ready for Invoice'; + case INVOICED = 'Invoiced'; public function getLabel(): ?string { @@ -22,22 +23,24 @@ public function getLabel(): ?string public function getColor(): string|array|null { return match ($this) { - self::DRAFT => 'gray', - self::APPROVED => 'success', - self::PRODUCTION => 'primary', - self::SHIPPED => 'warning', - self::INVOICED => 'invoiced', + self::DRAFT => 'gray', + self::APPROVED => 'success', + self::PRODUCTION => 'primary', + self::SHIPPED => 'warning', + self::READY_FOR_INVOICE => 'invoicing', + self::INVOICED => 'invoiced', }; } public function getIcon(): ?string { return match ($this) { - self::DRAFT => IconEnum::DRAFT->value, - self::APPROVED => IconEnum::APPROVED->value, - self::PRODUCTION => IconEnum::PRODUCTION->value, - self::SHIPPED => IconEnum::SHIPPED->value, - self::INVOICED => IconEnum::INVOICED->value, + self::DRAFT => IconEnum::DRAFT->value, + self::APPROVED => IconEnum::APPROVED->value, + self::PRODUCTION => IconEnum::PRODUCTION->value, + self::SHIPPED => IconEnum::SHIPPED->value, + self::READY_FOR_INVOICE => IconEnum::INVOICING->value, + self::INVOICED => IconEnum::INVOICED->value, }; } } diff --git a/app/Filament/Admin/Resources/InvoiceResource/Pages/ListInvoices.php b/app/Filament/Admin/Resources/InvoiceResource/Pages/ListInvoices.php index f918abe..172ccc8 100644 --- a/app/Filament/Admin/Resources/InvoiceResource/Pages/ListInvoices.php +++ b/app/Filament/Admin/Resources/InvoiceResource/Pages/ListInvoices.php @@ -23,6 +23,10 @@ public function getTabs(): array ->query(fn ($query) => $query->where('status', InvoiceStatus::UNPAID)) ->icon(InvoiceStatus::UNPAID->getIcon()), + 'partially_paid' => Tab::make('Partially Paid') + ->query(fn ($query) => $query->where('status', InvoiceStatus::PARTIALLY_PAID)) + ->icon(InvoiceStatus::PARTIALLY_PAID->getIcon()), + 'paid' => Tab::make('Paid') ->query(fn ($query) => $query->where('status', InvoiceStatus::PAID)) ->icon(InvoiceStatus::PAID->getIcon()), diff --git a/app/Filament/Admin/Resources/OrderResource.php b/app/Filament/Admin/Resources/OrderResource.php index 3d0b939..673bfd5 100644 --- a/app/Filament/Admin/Resources/OrderResource.php +++ b/app/Filament/Admin/Resources/OrderResource.php @@ -28,6 +28,8 @@ use Filament\Resources\Resource; use Filament\Support\Enums\MaxWidth; use Filament\Tables; +use Filament\Tables\Actions\BulkAction; +use Filament\Tables\Actions\BulkActionGroup; use Filament\Tables\Columns\IconColumn\IconColumnSize; use Filament\Tables\Columns\TextColumn; use Filament\Tables\Table; @@ -202,13 +204,13 @@ public static function form(Form $form): Form ->createOptionUsing(function (array $data): int { return ServiceType::create($data)->getKey(); }), - TextInput::make('placement') - ->datalist(ProductService::all()->unique('placement')->pluck('placement')->toArray()) - ->columnSpan(3), TextInput::make('serviceFileName') ->datalist(ServiceFile::all()->unique('name')->pluck('name')->toArray()) ->columnSpan(3) ->label('Logo Name'), + TextInput::make('placement') + ->datalist(ProductService::all()->unique('placement')->pluck('placement')->toArray()) + ->columnSpan(3), TextInput::make('serviceFileSetupNumber') ->label('Setup') ->columnSpan(1) @@ -343,7 +345,6 @@ public static function table(Table $table): Table Tables\Actions\EditAction::make(), ]) ->bulkActions([ - Tables\Actions\BulkAction::make('updateStatus') ->form([ Select::make('status') @@ -366,9 +367,18 @@ public static function table(Table $table): Table ->color('info') ->deselectRecordsAfterCompletion(), - Tables\Actions\BulkActionGroup::make([ + BulkActionGroup::make([ + BulkAction::make('Create individual invoices') + ->icon(IconEnum::INVOICE->value), + BulkAction::make('Add all to new invoice') + ->icon(IconEnum::REPEAT->value), + ]) + ->label('Invoicing') + ->hidden(fn () => ! auth()->user()->is_admin), + + BulkActionGroup::make([ Tables\Actions\DeleteBulkAction::make(), - ]), + ])->label('Other actions'), ]); } diff --git a/app/Filament/Admin/Resources/OrderResource/Pages/ListOrders.php b/app/Filament/Admin/Resources/OrderResource/Pages/ListOrders.php index c58bb77..0e525a5 100644 --- a/app/Filament/Admin/Resources/OrderResource/Pages/ListOrders.php +++ b/app/Filament/Admin/Resources/OrderResource/Pages/ListOrders.php @@ -15,6 +15,21 @@ class ListOrders extends ListRecords { protected static string $resource = OrderResource::class; + private function excludeStatuses($query): mixed + { + return $query + ->whereNot('status', OrderStatus::READY_FOR_INVOICE) + ->whereNot('status', OrderStatus::INVOICED) + ->whereNot('status', OrderStatus::SHIPPED); + } + + private function getBadgeCount(callable $queryCallback): ?int + { + $count = Order::query()->when(true, $queryCallback)->count(); + + return $count > 0 ? $count : null; + } + protected function getHeaderActions(): array { return [ @@ -26,67 +41,37 @@ protected function getHeaderActions(): array public function getTabs(): array { return [ + 'all' => Tab::make('All') + ->icon(IconEnum::TAB_ALL->value), + 'active' => Tab::make() - ->query(function ($query) { - return $query - ->whereNot('status', OrderStatus::INVOICED) - ->whereNot('status', ORderStatus::SHIPPED); - }) + ->query(fn ($query) => $this->excludeStatuses($query)) ->icon(OrderStatus::PRODUCTION->getIcon()) - ->badge(function () { - return Order::whereNot('status', OrderStatus::SHIPPED) - ->whereNot('status', OrderStatus::INVOICED) - ->count(); - }), + ->badge(fn () => $this->getBadgeCount(fn ($query) => $this->excludeStatuses($query))), 'unprinted' => Tab::make() - ->query(function ($query) { - return $query->where('printed', false); - }) + ->query(fn ($query) => $this->excludeStatuses($query)->where('printed', false)) ->icon(IconEnum::PRINT->value) - ->badge(function () { - $count = Order::where('printed', false)->count(); - - return $count > 0 ? $count : null; - }) + ->badge(fn () => $this->getBadgeCount(fn ($query) => $query->where('printed', false))) ->badgeColor('success'), 'overdue' => Tab::make() - ->query(function ($query) { - return $query->whereDate('due_date', '<=', today()) - ->whereNot('status', OrderStatus::INVOICED) - ->whereNot('status', ORderStatus::SHIPPED); - }) + ->query(fn ($query) => $this->excludeStatuses($query)->whereDate('due_date', '<=', today())) ->icon(IconEnum::TAB_OVERDUE->value) - ->badge(function () { - $count = Order::whereDate('due_date', '<=', today()) - ->whereNot('status', OrderStatus::INVOICED) - ->whereNot('status', ORderStatus::SHIPPED) - ->count(); - - return $count > 0 ? $count : null; - }) + ->badge(fn () => $this->getBadgeCount(fn ($query) => $query->whereDate('due_date', '<=', today()))) ->badgeColor('danger'), 'rush' => Tab::make() - ->query(function ($query) { - return $query->where('rush', true) - ->whereNot('status', OrderStatus::INVOICED) - ->whereNot('status', OrderStatus::SHIPPED); - }) + ->query(fn ($query) => $this->excludeStatuses($query)->where('rush', true)) ->icon(OrderAttributes::rush->getIcon()) - ->badge(function () { - $count = Order::where('rush', true) - ->whereNot('status', OrderStatus::INVOICED) - ->whereNot('status', OrderStatus::SHIPPED) - ->count(); - - return $count > 0 ? $count : null; - }) + ->badge(fn () => $this->getBadgeCount(fn ($query) => $query->where('rush', true))) ->badgeColor('warning'), - 'all' => Tab::make('All') - ->icon(IconEnum::TAB_ALL->value), + 'ready_for_invoice' => Tab::make() + ->query(fn ($query) => $query->where('status', OrderStatus::READY_FOR_INVOICE)) + ->icon(OrderStatus::READY_FOR_INVOICE->getIcon()) + ->badge(fn () => $this->getBadgeCount(fn ($query) => $query->where('status', OrderStatus::READY_FOR_INVOICE))) + ->badgeColor(OrderStatus::READY_FOR_INVOICE->getColor()), ]; } } diff --git a/app/Filament/Admin/Widgets/ActiveOrdersTable.php b/app/Filament/Admin/Widgets/ActiveOrdersTable.php index f5bed4d..eb86c61 100644 --- a/app/Filament/Admin/Widgets/ActiveOrdersTable.php +++ b/app/Filament/Admin/Widgets/ActiveOrdersTable.php @@ -12,6 +12,8 @@ class ActiveOrdersTable extends BaseWidget { protected static ?int $sort = 2; + protected string|int|array $columnSpan = 2; + public function table(Table $table): Table { return $table diff --git a/app/Filament/Admin/Widgets/RushOrdersTable.php b/app/Filament/Admin/Widgets/RushOrdersTable.php index 9ae3e62..c0b0ad3 100644 --- a/app/Filament/Admin/Widgets/RushOrdersTable.php +++ b/app/Filament/Admin/Widgets/RushOrdersTable.php @@ -10,6 +10,8 @@ class RushOrdersTable extends BaseWidget { + protected string|int|array $columnSpan = 2; + public function table(Table $table): Table { return $table diff --git a/app/Providers/Filament/AdminPanelProvider.php b/app/Providers/Filament/AdminPanelProvider.php index d481c82..3cb54b3 100644 --- a/app/Providers/Filament/AdminPanelProvider.php +++ b/app/Providers/Filament/AdminPanelProvider.php @@ -29,9 +29,10 @@ public function panel(Panel $panel): Panel ->path('admin') ->login(UsernameLogin::class) ->colors([ - 'primary' => Color::Blue, - 'code' => Color::hex('#d63384'), - 'invoiced' => Color::hex('#900090'), + 'primary' => Color::Blue, + 'code' => Color::hex('#d63384'), + 'invoicing' => Color::hex('#DD00DD'), + 'invoiced' => Color::hex('#900090'), ]) ->discoverResources(in: app_path('Filament/Admin/Resources/'), for: 'App\\Filament\\Admin\\Resources') ->discoverPages(in: app_path('Filament/Admin/Pages'), for: 'App\\Filament\\Admin\\Pages')