From 913a4477a7328428c9e0933f993c5665906ab8f2 Mon Sep 17 00:00:00 2001 From: Nisse Lommerde Date: Wed, 30 Oct 2024 11:55:21 -0400 Subject: [PATCH] Replace icons with lucide and work on quote --- app/Enums/OrderAttributes.php | 20 ++---- app/Enums/OrderStatus.php | 10 +-- app/Filament/Resources/InvoiceResource.php | 4 +- app/Filament/Resources/OrderResource.php | 7 +- .../OrderResource/Pages/EditOrder.php | 8 ++- .../Resources/PackingSlipResource.php | 4 +- app/Filament/Resources/QuoteResource.php | 36 ++++++++-- app/Filament/Widgets/OrderStats.php | 40 +++++++++-- app/Models/Order.php | 6 ++ app/Models/Quote.php | 11 ++++ composer.json | 1 + composer.lock | 66 ++++++++++++++++++- database/factories/OrderFactory.php | 10 ++- database/factories/QuoteFactory.php | 21 ++++++ .../2024_10_21_145025_create_quotes_table.php | 5 ++ database/seeders/DatabaseSeeder.php | 1 + database/seeders/OrderSeeder.php | 2 +- database/seeders/QuoteSeeder.php | 22 +++++++ 18 files changed, 232 insertions(+), 42 deletions(-) create mode 100644 database/factories/QuoteFactory.php create mode 100644 database/seeders/QuoteSeeder.php diff --git a/app/Enums/OrderAttributes.php b/app/Enums/OrderAttributes.php index c738283..050e63d 100644 --- a/app/Enums/OrderAttributes.php +++ b/app/Enums/OrderAttributes.php @@ -20,22 +20,16 @@ enum OrderAttributes: string implements HasIcon, HasLabel return $this->value; } - // public function getColor(): string|array|null - // { - // return match ($this) { - // }; - // } - public function getIcon(): ?string { return match ($this) { - self::new_art => 'heroicon-o-paint-brush', - self::repeat => 'heroicon-o-clipboard-document', - self::rush => 'heroicon-o-clock', - self::event => 'heroicon-o-users', - self::digitizing => 'heroicon-o-computer-desktop', - self::garments => 'heroicon-o-shopping-bag', - self::supplied_file => 'heroicon-o-arrow-down-tray', + self::new_art => 'lucide-brush', + self::repeat => 'lucide-files', + self::rush => 'lucide-bell-ring', + self::event => 'lucide-calendar-range', + self::digitizing => 'lucide-computer', + self::garments => 'lucide-shirt', + self::supplied_file => 'lucide-file-check', }; } } diff --git a/app/Enums/OrderStatus.php b/app/Enums/OrderStatus.php index 1503bed..a583e5b 100644 --- a/app/Enums/OrderStatus.php +++ b/app/Enums/OrderStatus.php @@ -33,11 +33,11 @@ enum OrderStatus: string implements HasColor, HasIcon, HasLabel public function getIcon(): ?string { return match ($this) { - self::DRAFT => 'heroicon-o-pencil', - self::APPROVED => 'heroicon-o-check', - self::PRODUCTION => 'heroicon-o-arrow-path', - self::SHIPPED => 'heroicon-o-paper-airplane', - self::INVOICED => 'heroicon-o-credit-card', + self::DRAFT => 'lucide-pencil', + self::APPROVED => 'lucide-check-check', + self::PRODUCTION => 'lucide-iteration-ccw', + self::SHIPPED => 'lucide-send', + self::INVOICED => 'lucide-credit-card', }; } } diff --git a/app/Filament/Resources/InvoiceResource.php b/app/Filament/Resources/InvoiceResource.php index cf12f93..0583046 100644 --- a/app/Filament/Resources/InvoiceResource.php +++ b/app/Filament/Resources/InvoiceResource.php @@ -13,9 +13,9 @@ class InvoiceResource extends Resource { protected static ?string $model = Invoice::class; - protected static ?string $navigationIcon = 'heroicon-o-clipboard-document-list'; + protected static ?string $navigationIcon = 'lucide-receipt-text'; - protected static ?string $navigationGroup = 'Billing'; + protected static ?string $navigationGroup = 'Production'; protected static ?int $navigationSort = 2; diff --git a/app/Filament/Resources/OrderResource.php b/app/Filament/Resources/OrderResource.php index 81d00a8..30c3410 100644 --- a/app/Filament/Resources/OrderResource.php +++ b/app/Filament/Resources/OrderResource.php @@ -30,7 +30,7 @@ class OrderResource extends Resource { protected static ?string $model = Order::class; - protected static ?string $navigationIcon = 'heroicon-o-shopping-bag'; + protected static ?string $navigationIcon = 'lucide-shopping-cart'; protected static ?string $navigationGroup = 'Production'; @@ -55,7 +55,10 @@ class OrderResource extends Resource Select::make('contact_id') ->label('Contact') - ->options(fn ($get): array => Contact::where('customer_id', $get('customer_id') ?? null)->get()->pluck('full_name', 'id')->toArray()) + ->options(fn ($get): array => Contact::where('customer_id', $get('customer_id') ?? null) + ->get() + ->pluck('full_name', 'id') + ->toArray()) ->searchable(), ]), diff --git a/app/Filament/Resources/OrderResource/Pages/EditOrder.php b/app/Filament/Resources/OrderResource/Pages/EditOrder.php index 423068f..181c5ef 100644 --- a/app/Filament/Resources/OrderResource/Pages/EditOrder.php +++ b/app/Filament/Resources/OrderResource/Pages/EditOrder.php @@ -143,12 +143,16 @@ class EditOrder extends EditRecord protected function getHeaderActions(): array { return [ + Action::make('save') + ->label('Save changes') + ->action('save') + ->icon('lucide-save'), Action::make('print') - ->icon('heroicon-s-printer') + ->icon('lucide-printer') ->url(fn (Order $record) => route('orders.pdf', $record)) ->openUrlInNewTab(), Actions\DeleteAction::make() - ->icon('heroicon-s-trash'), + ->icon('lucide-trash-2'), ]; } } diff --git a/app/Filament/Resources/PackingSlipResource.php b/app/Filament/Resources/PackingSlipResource.php index 812c863..ee2f911 100644 --- a/app/Filament/Resources/PackingSlipResource.php +++ b/app/Filament/Resources/PackingSlipResource.php @@ -20,9 +20,9 @@ class PackingSlipResource extends Resource { protected static ?string $model = PackingSlip::class; - protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack'; + protected static ?string $navigationIcon = 'lucide-package'; - protected static ?string $navigationGroup = 'Production'; + protected static ?string $navigationGroup = 'Management'; public static function form(Form $form): Form { diff --git a/app/Filament/Resources/QuoteResource.php b/app/Filament/Resources/QuoteResource.php index 74225bf..80b7985 100644 --- a/app/Filament/Resources/QuoteResource.php +++ b/app/Filament/Resources/QuoteResource.php @@ -3,7 +3,12 @@ namespace App\Filament\Resources; use App\Filament\Resources\QuoteResource\Pages; +use App\Models\Customer; +use App\Models\Order; use App\Models\Quote; +use Filament\Forms\Components\Select; +use Filament\Forms\Components\Split; +use Filament\Forms\Components\Textarea; use Filament\Forms\Form; use Filament\Resources\Resource; use Filament\Tables; @@ -13,9 +18,9 @@ class QuoteResource extends Resource { protected static ?string $model = Quote::class; - protected static ?string $navigationIcon = 'heroicon-o-chat-bubble-bottom-center-text'; + protected static ?string $navigationIcon = 'lucide-quote'; - protected static ?string $navigationGroup = 'Billing'; + protected static ?string $navigationGroup = 'Production'; protected static ?int $navigationSort = 1; @@ -23,15 +28,36 @@ class QuoteResource extends Resource { return $form ->schema([ - // - ]); + Split::make([ + Select::make('customer_id') + ->required() + ->label('Customer') + ->options(Customer::all()->pluck('company_name', 'id')) + ->reactive() + ->searchable(), + + Select::make('order_id') + ->label('Order') + ->options(fn ($get): array => Order::where('customer_id', $get('customer_id') ?? null) + ->get() + ->pluck('customer_po', 'id') + ->toArray()) + ->searchable(), + ])->columnSpan(2), + Textarea::make('body') + ->columnSpan(2) + ->rows(8), + ])->columns(3); } public static function table(Table $table): Table { return $table ->columns([ - // + Tables\Columns\TextColumn::make('order.customer.company_name'), + Tables\Columns\TextColumn::make('order.customer_po'), + Tables\Columns\TextColumn::make('body') + ->limit(100), ]) ->filters([ // diff --git a/app/Filament/Widgets/OrderStats.php b/app/Filament/Widgets/OrderStats.php index 18f5b48..6d3bba2 100644 --- a/app/Filament/Widgets/OrderStats.php +++ b/app/Filament/Widgets/OrderStats.php @@ -14,14 +14,20 @@ class OrderStats extends BaseWidget protected function getStats(): array { return [ + Stat::make('This Month', $this->getOrdersPast30Days()) + ->icon('heroicon-s-calendar') + ->chartColor('success') + ->chart($this->getOrdersInPast30DaysChart()) + ->description('New orders in the past 30 days'), + Stat::make('Active Orders', $this->getActiveOrders()) ->icon('heroicon-o-arrow-path') ->description('Orders that have yet to be completed'), - Stat::make('This Month', $this->getOrdersCurrentMonth()) - ->icon('heroicon-s-calendar') - ->description('New orders since the beginning of the month'), + Stat::make('Due Today', $this->getDueOrders()) ->icon('heroicon-o-clock') + ->chartColor('info') + ->chart($this->getDueOrdersChart()) ->description('Orders that are scheduled to be due today'), ]; } @@ -34,7 +40,7 @@ class OrderStats extends BaseWidget ->count(); } - private function getOrdersCurrentMonth(): string + private function getOrdersPast30Days(): string { return Order::all() ->where('order_status', '!=', OrderStatus::SHIPPED) @@ -43,6 +49,19 @@ class OrderStats extends BaseWidget ->count(); } + private function getOrdersInPast30DaysChart(): array + { + $chart = []; + $points = 30; + $startDate = today()->subDays(31); + + for ($i = 0; $i < $points; $i++) { + $chart[$i] = Order::where('order_date', $startDate->addDay())->count(); + } + + return $chart; + } + private function getDueOrders(): string { return Order::all() @@ -51,4 +70,17 @@ class OrderStats extends BaseWidget ->where('due_date', '<=', now()) ->count(); } + + private function getDueOrdersChart(): array + { + $chart = []; + $points = 30; + $startDate = today()->subDays(31); + + for ($i = 0; $i < $points; $i++) { + $chart[$i] = Order::where('due_date', $startDate->addDay())->count(); + } + + return $chart; + } } diff --git a/app/Models/Order.php b/app/Models/Order.php index f4a0b01..148a1af 100644 --- a/app/Models/Order.php +++ b/app/Models/Order.php @@ -10,6 +10,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Carbon; use Spatie\Browsershot\Browsershot; @@ -192,6 +193,11 @@ class Order extends Model return $this->hasMany(PackingSlip::class); } + public function quote(): HasOne + { + return $this->hasOne(Quote::class); + } + protected function serializeDate(DateTimeInterface $date): string { return $date->format('Y-m-d'); diff --git a/app/Models/Quote.php b/app/Models/Quote.php index f8d8793..e86a67f 100644 --- a/app/Models/Quote.php +++ b/app/Models/Quote.php @@ -4,8 +4,19 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; class Quote extends Model { use HasFactory; + + protected $fillable = [ + 'body', + 'order_id', + ]; + + public function order(): BelongsTo + { + return $this->belongsTo(Order::class); + } } diff --git a/composer.json b/composer.json index fb624bb..d58dfa5 100644 --- a/composer.json +++ b/composer.json @@ -13,6 +13,7 @@ "laravel/framework": "^11.9", "laravel/tinker": "^2.9", "livewire/livewire": "^3.5", + "mallardduck/blade-lucide-icons": "^1.23", "spatie/laravel-pdf": "^1.5" }, "require-dev": { diff --git a/composer.lock b/composer.lock index ccdb662..5a1c5e6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "86c43bbee5882b7329eaa389aa089339", + "content-hash": "ba64e848b8657779cbac9c8911ad1479", "packages": [ { "name": "anourvalar/eloquent-serialize", @@ -3349,6 +3349,66 @@ ], "time": "2024-08-19T11:52:18+00:00" }, + { + "name": "mallardduck/blade-lucide-icons", + "version": "1.23.0", + "source": { + "type": "git", + "url": "https://github.com/mallardduck/blade-lucide-icons.git", + "reference": "19081819bb527e15eed22f6528b9d059c1a8df8b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mallardduck/blade-lucide-icons/zipball/19081819bb527e15eed22f6528b9d059c1a8df8b", + "reference": "19081819bb527e15eed22f6528b9d059c1a8df8b", + "shasum": "" + }, + "require": { + "blade-ui-kit/blade-icons": "^1.6", + "php": "^8.0" + }, + "require-dev": { + "ext-dom": "*", + "ext-fileinfo": "*", + "orchestra/testbench": "^6.0|^7.0|^8.0|^9.0", + "phpunit/phpunit": "^9.0|^10.5|^11.0", + "spatie/phpunit-snapshot-assertions": "^4.2.14" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "MallardDuck\\LucideIcons\\BladeLucideIconsServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "MallardDuck\\LucideIcons\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dan Pock" + } + ], + "description": "A package to easily make use of Lucide icons in your Laravel Blade views.", + "homepage": "https://github.com/mallardduck/blade-lucide-icons", + "keywords": [ + "LucideIcons", + "blade", + "laravel" + ], + "support": { + "issues": "https://github.com/mallardduck/blade-lucide-icons/issues", + "source": "https://github.com/mallardduck/blade-lucide-icons/tree/1.23.0" + }, + "time": "2024-07-22T15:40:39+00:00" + }, { "name": "masterminds/html5", "version": "2.9.0", @@ -10338,12 +10398,12 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": [], "prefer-stable": true, "prefer-lowest": false, "platform": { "php": "^8.2" }, - "platform-dev": {}, + "platform-dev": [], "plugin-api-version": "2.6.0" } diff --git a/database/factories/OrderFactory.php b/database/factories/OrderFactory.php index 0e94284..610f710 100644 --- a/database/factories/OrderFactory.php +++ b/database/factories/OrderFactory.php @@ -14,8 +14,12 @@ class OrderFactory extends Factory public function definition(): array { - $order_date = Carbon::today()->subDays(rand(0, 30)); - $due_date = $order_date->copy()->addDays(rand(9, 15)); + $order_date = Carbon::today()->subDays(rand(0, 60)); + $due_date = $order_date->copy()->addDays(rand(7, 12)); + + $status = $due_date < today() ? + OrderStatus::INVOICED->value : + $this->faker->randomElement(OrderStatus::cases())->value; return [ 'created_at' => $order_date, @@ -24,7 +28,7 @@ class OrderFactory extends Factory 'order_type' => $this->faker->randomElement(OrderType::cases())->value, 'order_date' => $order_date, 'due_date' => $due_date, - 'status' => $this->faker->randomElement(OrderStatus::cases())->value, + 'status' => $status, 'notes' => $this->faker->words(10, true), 'rush' => $this->faker->boolean(20), diff --git a/database/factories/QuoteFactory.php b/database/factories/QuoteFactory.php new file mode 100644 index 0000000..581b50d --- /dev/null +++ b/database/factories/QuoteFactory.php @@ -0,0 +1,21 @@ + $this->faker->realText(300), + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + ]; + } +} diff --git a/database/migrations/2024_10_21_145025_create_quotes_table.php b/database/migrations/2024_10_21_145025_create_quotes_table.php index fe99787..ea4193a 100644 --- a/database/migrations/2024_10_21_145025_create_quotes_table.php +++ b/database/migrations/2024_10_21_145025_create_quotes_table.php @@ -13,6 +13,11 @@ return new class extends Migration { Schema::create('quotes', function (Blueprint $table) { $table->id(); + + $table->foreignId('order_id')->nullable()->constrained(); + + $table->longText('body')->nullable(); + $table->timestamps(); }); } diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index c7a1939..5e1b812 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -23,6 +23,7 @@ class DatabaseSeeder extends Seeder ProductSizeSeeder::class, ProductServiceSeeder::class, ServiceFileSeeder::class, + QuoteSeeder::class, ]); User::factory()->create([ diff --git a/database/seeders/OrderSeeder.php b/database/seeders/OrderSeeder.php index 510a59b..9bc5503 100644 --- a/database/seeders/OrderSeeder.php +++ b/database/seeders/OrderSeeder.php @@ -14,7 +14,7 @@ class OrderSeeder extends Seeder public function run(): void { foreach (Customer::all() as $customer) { - Order::factory(rand(2, 10), ['customer_id' => $customer])->create(); + Order::factory(rand(2, 50), ['customer_id' => $customer])->create(); } } } diff --git a/database/seeders/QuoteSeeder.php b/database/seeders/QuoteSeeder.php new file mode 100644 index 0000000..0b49fdf --- /dev/null +++ b/database/seeders/QuoteSeeder.php @@ -0,0 +1,22 @@ += 1) { + Quote::factory()->for($order)->create(); + } + } + } +}