From 46b84eadeaa823e443d90d0e08629011dbf596cf Mon Sep 17 00:00:00 2001 From: Alec Ritson Date: Fri, 2 Feb 2024 14:48:15 +0000 Subject: [PATCH 01/11] Discounts WIP --- packages/admin/resources/lang/en/discount.php | 198 ++++++++++++++++++ .../Filament/Resources/DiscountResource.php | 125 ++++++++++- .../DiscountResource/Pages/EditDiscount.php | 19 ++ .../Pages/ManageDiscountLimitations.php | 53 +++++ .../BrandLimitationRelationManager.php | 60 ++++++ .../CollectionLimitationRelationManager.php | 64 ++++++ .../ProductLimitationRelationManager.php | 78 +++++++ ...roductVariantLimitationRelationManager.php | 68 ++++++ packages/admin/src/LunarPanelManager.php | 1 + packages/core/src/Models/Brand.php | 7 + packages/core/src/Models/Discount.php | 22 ++ 11 files changed, 692 insertions(+), 3 deletions(-) create mode 100644 packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php create mode 100644 packages/admin/src/Filament/Resources/DiscountResource/Pages/ManageDiscountLimitations.php create mode 100644 packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/BrandLimitationRelationManager.php create mode 100644 packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/CollectionLimitationRelationManager.php create mode 100644 packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductLimitationRelationManager.php create mode 100644 packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductVariantLimitationRelationManager.php diff --git a/packages/admin/resources/lang/en/discount.php b/packages/admin/resources/lang/en/discount.php index 57ad128850..d3f019d8c4 100644 --- a/packages/admin/resources/lang/en/discount.php +++ b/packages/admin/resources/lang/en/discount.php @@ -7,10 +7,208 @@ 'name' => [ 'label' => 'Name', ], + 'handle' => [ + 'label' => 'Handle', + ], + 'starts_at' => [ + 'label' => 'Start Date', + ], + 'ends_at' => [ + 'label' => 'End Date', + ], + 'priority' => [ + 'label' => 'Priority', + 'helper_text' => 'Discounts with higher priority will be applied first.', + 'options' => [ + 'low' => [ + 'label' => 'Low', + ], + 'medium' => [ + 'label' => 'Low', + ], + 'high' => [ + 'label' => 'High', + ], + ], + ], + 'stop' => [ + 'label' => 'Stop other discounts applying after this one', + ], ], 'table' => [ 'name' => [ 'label' => 'Name', ], + 'status' => [ + 'label' => 'Status', + \Lunar\Models\Discount::ACTIVE => [ + 'label' => 'Active', + ], + \Lunar\Models\Discount::PENDING => [ + 'label' => 'Pending', + ], + \Lunar\Models\Discount::EXPIRED => [ + 'label' => 'Expired', + ], + \Lunar\Models\Discount::SCHEDULED => [ + 'label' => 'Scheduled', + ], + ], + 'type' => [ + 'label' => 'Type', + ], + 'starts_at' => [ + 'label' => 'Start Date', + ], + 'ends_at' => [ + 'label' => 'End Date', + ], + ], + 'pages' => [ + 'limitations' => [ + 'label' => 'Limitations', + ], + ], + 'relationmanagers' => [ + 'collections' => [ + 'title' => 'Collections', + 'description' => 'Select which collections this discount should be limited to.', + 'actions' => [ + 'attach' => [ + 'label' => 'Attach Collection', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Name', + ], + 'type' => [ + 'label' => 'Type', + 'limitation' => [ + 'label' => 'Limitation', + ], + 'exclusion' => [ + 'label' => 'Exclusion', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Limitation', + ], + 'exclusion' => [ + 'label' => 'Exclusion', + ], + ], + ], + ], + ], + 'brands' => [ + 'title' => 'Brands', + 'description' => 'Select which brands this discount should be limited to.', + 'actions' => [ + 'attach' => [ + 'label' => 'Attach Brand', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Name', + ], + 'type' => [ + 'label' => 'Type', + 'limitation' => [ + 'label' => 'Limitation', + ], + 'exclusion' => [ + 'label' => 'Exclusion', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Limitation', + ], + 'exclusion' => [ + 'label' => 'Exclusion', + ], + ], + ], + ], + ], + 'products' => [ + 'title' => 'Products', + 'description' => 'Select which products this discount should be limited to.', + 'actions' => [ + 'attach' => [ + 'label' => 'Add Product', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Name', + ], + 'type' => [ + 'label' => 'Type', + 'limitation' => [ + 'label' => 'Limitation', + ], + 'exclusion' => [ + 'label' => 'Exclusion', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Limitation', + ], + 'exclusion' => [ + 'label' => 'Exclusion', + ], + ], + ], + ], + ], + 'productvariants' => [ + 'title' => 'Product Variants', + 'description' => 'Select which product variants this discount should be limited to.', + 'actions' => [ + 'attach' => [ + 'label' => 'Add Product Variant', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Name', + ], + 'type' => [ + 'label' => 'Type', + 'limitation' => [ + 'label' => 'Limitation', + ], + 'exclusion' => [ + 'label' => 'Exclusion', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Limitation', + ], + 'exclusion' => [ + 'label' => 'Exclusion', + ], + ], + ], + ], + ], ], ]; diff --git a/packages/admin/src/Filament/Resources/DiscountResource.php b/packages/admin/src/Filament/Resources/DiscountResource.php index efadd2e4f4..31bb0e457d 100644 --- a/packages/admin/src/Filament/Resources/DiscountResource.php +++ b/packages/admin/src/Filament/Resources/DiscountResource.php @@ -4,11 +4,18 @@ use Filament\Forms; use Filament\Forms\Components\Component; +use Filament\Forms\Form; +use Filament\Pages\Page; use Filament\Pages\SubNavigationPosition; use Filament\Support\Facades\FilamentIcon; use Filament\Tables; use Filament\Tables\Table; +use Illuminate\Support\Str; use Lunar\Admin\Filament\Resources\DiscountResource\Pages; +use Lunar\Admin\Filament\Resources\DiscountResource\RelationManagers\BrandLimitationRelationManager; +use Lunar\Admin\Filament\Resources\DiscountResource\RelationManagers\CollectionLimitationRelationManager; +use Lunar\Admin\Filament\Resources\DiscountResource\RelationManagers\ProductLimitationRelationManager; +use Lunar\Admin\Filament\Resources\DiscountResource\RelationManagers\ProductVariantLimitationRelationManager; use Lunar\Admin\Support\Resources\BaseResource; use Lunar\Models\Discount; @@ -42,10 +49,30 @@ public static function getNavigationGroup(): ?string return 'Sales'; } + public static function getDefaultForm(Form $form): Form + { + return $form->schema([ + Forms\Components\Section::make('')->schema( + static::getMainFormComponents() + ), + ]); + } + protected static function getMainFormComponents(): array { return [ - static::getNameFormComponent(), + Forms\Components\Group::make([ + static::getNameFormComponent(), + static::getHandleFormComponent(), + ])->columns(2), + Forms\Components\Group::make([ + static::getStartsAtFormComponent(), + static::getEndsAtFormComponent(), + ])->columns(2), + Forms\Components\Group::make([ + static::getPriorityFormComponent(), + static::getStopFormComponent(), + ])->columns(2), ]; } @@ -53,11 +80,67 @@ protected static function getNameFormComponent(): Component { return Forms\Components\TextInput::make('name') ->label(__('lunarpanel::discount.form.name.label')) + ->live(onBlur: true) + ->afterStateUpdated(function (string $operation, $state, Forms\Set $set) { + if ($operation !== 'create') { + return; + } + $set('handle', Str::slug($state)); + }) + ->required() + ->maxLength(255) + ->autofocus(); + } + + protected static function getHandleFormComponent(): Component + { + return Forms\Components\TextInput::make('handle') + ->label(__('lunarpanel::discount.form.handle.label')) ->required() + ->unique(ignoreRecord: true) ->maxLength(255) ->autofocus(); } + protected static function getStartsAtFormComponent(): Component + { + return Forms\Components\DateTimePicker::make('starts_at') + ->label(__('lunarpanel::discount.form.starts_at.label')) + ->before(function (Forms\Get $get) { + return $get('ends_at'); + }); + } + + protected static function getEndsAtFormComponent(): Component + { + return Forms\Components\DateTimePicker::make('ends_at') + ->label(__('lunarpanel::discount.form.ends_at.label')); + } + + protected static function getPriorityFormComponent(): Component + { + return Forms\Components\Select::make('priority') + ->label(__('lunarpanel::discount.form.priority.label')) + ->helperText( + __('lunarpanel::discount.form.priority.helper_text') + ) + ->options(function () { + return [ + 1 => __('lunarpanel::discount.form.priority.options.low.label'), + 5 => __('lunarpanel::discount.form.priority.options.medium.label'), + 10 => __('lunarpanel::discount.form.priority.options.high.label'), + ]; + }); + } + + protected static function getStopFormComponent(): Component + { + return Forms\Components\Toggle::make('stop') + ->label( + __('lunarpanel::discount.form.stop.label') + ); + } + public static function getDefaultTable(Table $table): Table { return $table @@ -78,15 +161,49 @@ public static function getDefaultTable(Table $table): Table protected static function getTableColumns(): array { return [ + Tables\Columns\TextColumn::make('status') + ->formatStateUsing(function ($state) { + return __("lunarpanel::discount.table.status.{$state}.label"); + }) + ->label(__('lunarpanel::discount.table.status.label')) + ->badge() + ->color(fn (string $state): string => match ($state) { + Discount::ACTIVE => 'success', + Discount::EXPIRED => 'danger', + Discount::PENDING => 'gray', + Discount::SCHEDULED => 'info', + }), Tables\Columns\TextColumn::make('name') ->label(__('lunarpanel::discount.table.name.label')), + Tables\Columns\TextColumn::make('type') + ->formatStateUsing(function ($state) { + return (new $state)->getName(); + }) + ->label(__('lunarpanel::discount.table.type.label')), + Tables\Columns\TextColumn::make('starts_at') + ->label(__('lunarpanel::discount.table.starts_at.label')) + ->date(), + Tables\Columns\TextColumn::make('ends_at') + ->label(__('lunarpanel::discount.table.ends_at.label')) + ->date(), ]; } - public static function getRelations(): array + public static function getRecordSubNavigation(Page $page): array + { + return $page->generateNavigationItems([ + Pages\EditDiscount::class, + Pages\ManageDiscountLimitations::class, + ]); + } + + protected static function getDefaultRelations(): array { return [ - // + CollectionLimitationRelationManager::class, + BrandLimitationRelationManager::class, + ProductLimitationRelationManager::class, + ProductVariantLimitationRelationManager::class, ]; } @@ -94,6 +211,8 @@ public static function getPages(): array { return [ 'index' => Pages\ListDiscounts::route('/'), + 'edit' => Pages\EditDiscount::route('/{record}'), + 'limitations' => Pages\ManageDiscountLimitations::route('/{record}/limitations'), ]; } } diff --git a/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php b/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php new file mode 100644 index 0000000000..ae20bd00c7 --- /dev/null +++ b/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php @@ -0,0 +1,19 @@ +schema([]); + } + + protected function getFormActions(): array + { + return []; + } + + public function getRelationManagers(): array + { + return [ + RelationGroup::make('Limitations', [ + DiscountResource\RelationManagers\CollectionLimitationRelationManager::class, + DiscountResource\RelationManagers\BrandLimitationRelationManager::class, + DiscountResource\RelationManagers\ProductLimitationRelationManager::class, + DiscountResource\RelationManagers\ProductVariantLimitationRelationManager::class, + ]), + + ]; + } +} diff --git a/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/BrandLimitationRelationManager.php b/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/BrandLimitationRelationManager.php new file mode 100644 index 0000000000..187bf395d1 --- /dev/null +++ b/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/BrandLimitationRelationManager.php @@ -0,0 +1,60 @@ +description( + __('lunarpanel::discount.relationmanagers.brands.description') + ) + ->paginated(false) + ->headerActions([ + Tables\Actions\AttachAction::make()->form(fn (Tables\Actions\AttachAction $action): array => [ + $action->getRecordSelect(), + Select::make('type') + ->options( + fn () => [ + 'limitation' => __('lunarpanel::discount.relationmanagers.brands.form.type.options.limitation.label'), + 'exclusion' => __('lunarpanel::discount.relationmanagers.brands.form.type.options.exclusion.label'), + ] + )->default('limitation'), + ])->recordTitle(function ($record) { + return $record->name; + })->preloadRecordSelect() + ->label( + __('lunarpanel::discount.relationmanagers.brands.actions.attach.label') + ), + ])->columns([ + Tables\Columns\TextColumn::make('name') + ->label( + __('lunarpanel::discount.relationmanagers.brands.table.name.label') + ), + Tables\Columns\TextColumn::make('pivot.type') + ->label( + __('lunarpanel::discount.relationmanagers.brands.table.type.label') + )->formatStateUsing( + fn (string $state) => __("lunarpanel::discount.relationmanagers.brands.table.type.{$state}.label") + ), + ])->actions([ + Tables\Actions\DetachAction::make(), + ]); + } +} diff --git a/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/CollectionLimitationRelationManager.php b/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/CollectionLimitationRelationManager.php new file mode 100644 index 0000000000..9e0c5d54ed --- /dev/null +++ b/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/CollectionLimitationRelationManager.php @@ -0,0 +1,64 @@ +description( + __('lunarpanel::discount.relationmanagers.collections.description') + ) + ->paginated(false) + ->headerActions([ + Tables\Actions\AttachAction::make()->form(fn (Tables\Actions\AttachAction $action): array => [ + $action->getRecordSelect(), + Select::make('type') + ->options( + fn () => [ + 'limitation' => __('lunarpanel::discount.relationmanagers.collections.form.type.options.limitation.label'), + 'exclusion' => __('lunarpanel::discount.relationmanagers.collections.form.type.options.exclusion.label'), + ] + )->default('limitation'), + ])->recordTitle(function ($record) { + return $record->attr('name'); + })->preloadRecordSelect() + ->label( + __('lunarpanel::discount.relationmanagers.collections.actions.attach.label') + ), + ])->columns([ + Tables\Columns\TextColumn::make('attribute_data.name') + ->label( + __('lunarpanel::discount.relationmanagers.collections.table.name.label') + ) + ->formatStateUsing( + fn (Model $record) => $record->attr('name') + ), + Tables\Columns\TextColumn::make('pivot.type') + ->label( + __('lunarpanel::discount.relationmanagers.collections.table.type.label') + )->formatStateUsing( + fn (string $state) => __("lunarpanel::discount.relationmanagers.collections.table.type.{$state}.label") + ), + ])->actions([ + Tables\Actions\DetachAction::make(), + ]); + } +} diff --git a/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductLimitationRelationManager.php b/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductLimitationRelationManager.php new file mode 100644 index 0000000000..f7430351b9 --- /dev/null +++ b/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductLimitationRelationManager.php @@ -0,0 +1,78 @@ +heading( + __('lunarpanel::discount.relationmanagers.products.title') + ) + ->description( + __('lunarpanel::discount.relationmanagers.products.description') + ) + ->paginated(false) + ->modifyQueryUsing( + fn ($query) => $query->whereIn('type', ['limitation', 'exclusion']) + ->wherePurchasableType(Product::class) + ->whereHas('purchasable') + ) + ->headerActions([ + Tables\Actions\CreateAction::make()->form([ + Forms\Components\MorphToSelect::make('purchasable') + ->searchable(true) + ->types([ + Forms\Components\MorphToSelect\Type::make(Product::class) + ->titleAttribute('name.en') + ->getSearchResultsUsing(static function (Forms\Components\Select $component, string $search): array { + return Product::search($search) + ->get() + ->mapWithKeys(fn (Product $record): array => [$record->getKey() => $record->attr('name')]) + ->all(); + }), + ]), + ])->label( + __('lunarpanel::discount.relationmanagers.products.actions.attach.label') + )->mutateFormDataUsing(function (array $data) { + $data['type'] = 'limitation'; + + return $data; + }), + ])->columns([ + Tables\Columns\SpatieMediaLibraryImageColumn::make('purchasable.thumbnail') + ->collection('images') + ->conversion('small') + ->limit(1) + ->square() + ->label(''), + Tables\Columns\TextColumn::make('purchasable.attribute_data.name') + ->label( + __('lunarpanel::discount.relationmanagers.products.table.name.label') + ) + ->formatStateUsing( + fn (Model $record) => $record->purchasable->attr('name') + ), + ])->actions([ + Tables\Actions\DeleteAction::make(), + ]); + } +} diff --git a/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductVariantLimitationRelationManager.php b/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductVariantLimitationRelationManager.php new file mode 100644 index 0000000000..65a200e340 --- /dev/null +++ b/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductVariantLimitationRelationManager.php @@ -0,0 +1,68 @@ +heading( + __('lunarpanel::discount.relationmanagers.productvariants.title') + ) + ->description( + __('lunarpanel::discount.relationmanagers.productvariants.description') + ) + ->paginated(false) + ->modifyQueryUsing( + fn ($query) => $query->whereIn('type', ['limitation', 'exclusion']) + ->wherePurchasableType(ProductVariant::class) + ->whereHas('purchasable') + ) + ->headerActions([ + Tables\Actions\CreateAction::make()->form([ + Forms\Components\MorphToSelect::make('purchasable') + ->searchable(true) + ->types([ + Forms\Components\MorphToSelect\Type::make(ProductVariant::class) + ->titleAttribute('sku') + ->getSearchResultsUsing(static function (Forms\Components\Select $component, string $search): array { + $products = Product::search($search) + ->get(); + + return ProductVariant::whereIn('product_id', $products->pluck('id')) + ->get() + ->mapWithKeys(fn (ProductVariant $record): array => [$record->getKey() => $record->product->attr('name').' - '.$record->sku]) + ->all(); + }), + ]), + ])->label( + __('lunarpanel::discount.relationmanagers.productvariants.actions.attach.label') + )->mutateFormDataUsing(function (array $data) { + $data['type'] = 'limitation'; + + return $data; + }), + ])->columns([ + ])->actions([ + Tables\Actions\DeleteAction::make(), + ]); + } +} diff --git a/packages/admin/src/LunarPanelManager.php b/packages/admin/src/LunarPanelManager.php index 5e31915baf..8441fcb7fd 100644 --- a/packages/admin/src/LunarPanelManager.php +++ b/packages/admin/src/LunarPanelManager.php @@ -113,6 +113,7 @@ public function register(): self 'lunar::customer-groups' => 'lucide-users', 'lunar::dashboard' => 'lucide-bar-chart-big', 'lunar::discounts' => 'lucide-percent-circle', + 'lunar::discount-limitations' => 'lucide-list-x', 'lunar::languages' => 'lucide-languages', 'lunar::media' => 'lucide-image', 'lunar::orders' => 'lucide-inbox', diff --git a/packages/core/src/Models/Brand.php b/packages/core/src/Models/Brand.php index 3c74434e60..416ac5626b 100644 --- a/packages/core/src/Models/Brand.php +++ b/packages/core/src/Models/Brand.php @@ -76,4 +76,11 @@ public function products(): HasMany { return $this->hasMany(Product::class); } + + public function discounts() + { + $prefix = config('lunar.database.table_prefix'); + + return $this->belongsToMany(Discount::class, "{$prefix}brand_discount"); + } } diff --git a/packages/core/src/Models/Discount.php b/packages/core/src/Models/Discount.php index 88471e58e7..4a95b01e39 100644 --- a/packages/core/src/Models/Discount.php +++ b/packages/core/src/Models/Discount.php @@ -36,6 +36,11 @@ class Discount extends BaseModel protected $guarded = []; + const ACTIVE = 'active'; + const PENDING = 'pending'; + const EXPIRED = 'expired'; + const SCHEDULED = 'scheduled'; + /** * Define which attributes should be cast. * @@ -55,6 +60,23 @@ protected static function newFactory(): DiscountFactory return DiscountFactory::new(); } + public function getStatusAttribute() + { + $active = $this->starts_at?->isPast() && ! $this->ends_at?->isPast(); + $expired = $this->ends_at?->isPast(); + $future = $this->starts_at?->isFuture(); + + if ($expired) { + return static::EXPIRED; + } + + if ($future) { + return static::SCHEDULED; + } + + return $active ? static::ACTIVE : static::PENDING; + } + public function users(): BelongsToMany { $prefix = config('lunar.database.table_prefix'); From 5ca9aa4a71709b5dce05b181c7e42ecc0fb3081f Mon Sep 17 00:00:00 2001 From: Alec Ritson Date: Thu, 8 Feb 2024 10:00:16 +0000 Subject: [PATCH 02/11] Updates --- packages/admin/resources/lang/en/discount.php | 31 +++-- .../Filament/Resources/DiscountResource.php | 129 +++++++++++++++++- .../DiscountResource/Pages/EditDiscount.php | 37 +++++ ...roductVariantLimitationRelationManager.php | 20 +++ 4 files changed, 208 insertions(+), 9 deletions(-) diff --git a/packages/admin/resources/lang/en/discount.php b/packages/admin/resources/lang/en/discount.php index d3f019d8c4..488c9aff1c 100644 --- a/packages/admin/resources/lang/en/discount.php +++ b/packages/admin/resources/lang/en/discount.php @@ -4,6 +4,9 @@ 'plural_label' => 'Discounts', 'label' => 'Discount', 'form' => [ + 'conditions' => [ + 'heading' => 'Conditions', + ], 'name' => [ 'label' => 'Name', ], @@ -34,6 +37,21 @@ 'stop' => [ 'label' => 'Stop other discounts applying after this one', ], + 'coupon' => [ + 'label' => 'Coupon', + 'helper_text' => 'Enter the coupon required for the discount to apply, if left blank it will apply automatically.', + ], + 'max_uses' => [ + 'label' => 'Max uses', + 'helper_text' => 'Leave blank for unlimited uses.', + ], + 'max_uses_per_user' => [ + 'label' => 'Max uses per user', + 'helper_text' => 'Leave blank for unlimited uses.', + ], + 'minimum_cart_amount' => [ + 'label' => 'Minimum Cart Amount', + ], ], 'table' => [ 'name' => [ @@ -187,14 +205,11 @@ 'name' => [ 'label' => 'Name', ], - 'type' => [ - 'label' => 'Type', - 'limitation' => [ - 'label' => 'Limitation', - ], - 'exclusion' => [ - 'label' => 'Exclusion', - ], + 'sku' => [ + 'label' => 'SKU', + ], + 'values' => [ + 'label' => 'Option(s)', ], ], 'form' => [ diff --git a/packages/admin/src/Filament/Resources/DiscountResource.php b/packages/admin/src/Filament/Resources/DiscountResource.php index 31bb0e457d..05996a5576 100644 --- a/packages/admin/src/Filament/Resources/DiscountResource.php +++ b/packages/admin/src/Filament/Resources/DiscountResource.php @@ -17,6 +17,9 @@ use Lunar\Admin\Filament\Resources\DiscountResource\RelationManagers\ProductLimitationRelationManager; use Lunar\Admin\Filament\Resources\DiscountResource\RelationManagers\ProductVariantLimitationRelationManager; use Lunar\Admin\Support\Resources\BaseResource; +use Lunar\DiscountTypes\AmountOff; +use Lunar\Facades\Discounts; +use Lunar\Models\Currency; use Lunar\Models\Discount; class DiscountResource extends BaseResource @@ -55,6 +58,19 @@ public static function getDefaultForm(Form $form): Form Forms\Components\Section::make('')->schema( static::getMainFormComponents() ), + Forms\Components\Section::make('amount_off') + ->heading('Amount Off') + ->visible( + fn (Forms\Get $get) => $get('type') == AmountOff::class + )->schema( + static::getAmountOffFormComponents() + ), + + Forms\Components\Section::make('conditions')->schema( + static::getConditionsFormComponents() + )->heading( + __('lunarpanel::discount.form.conditions.heading') + ), ]); } @@ -71,8 +87,25 @@ protected static function getMainFormComponents(): array ])->columns(2), Forms\Components\Group::make([ static::getPriorityFormComponent(), - static::getStopFormComponent(), + static::getDiscountTypeFormComponent(), ])->columns(2), + static::getStopFormComponent(), + ]; + } + + protected static function getConditionsFormComponents(): array + { + return [ + Forms\Components\Group::make([ + static::getCouponFormComponent(), + static::getMaxUsesFormComponent(), + static::getMaxUsesPerUserFormComponent(), + ])->columns(3), + Forms\Components\Fieldset::make()->schema( + static::getMinimumCartAmountsFormComponents() + )->label( + __('lunarpanel::discount.form.minimum_cart_amount.label') + ), ]; } @@ -141,6 +174,100 @@ protected static function getStopFormComponent(): Component ); } + protected static function getCouponFormComponent(): Component + { + return Forms\Components\TextInput::make('coupon') + ->label( + __('lunarpanel::discount.form.coupon.label') + )->helperText( + __('lunarpanel::discount.form.coupon.helper_text') + ); + } + + protected static function getMaxUsesFormComponent(): Component + { + return Forms\Components\TextInput::make('max_uses') + ->label( + __('lunarpanel::discount.form.max_uses.label') + )->helperText( + __('lunarpanel::discount.form.max_uses.helper_text') + ); + } + + protected static function getMaxUsesPerUserFormComponent(): Component + { + return Forms\Components\TextInput::make('max_uses_per_user') + ->label( + __('lunarpanel::discount.form.max_uses_per_user.label') + )->helperText( + __('lunarpanel::discount.form.max_uses_per_user.helper_text') + ); + } + + protected static function getMinimumCartAmountsFormComponents(): array + { + $currencies = Currency::get(); + $inputs = []; + + foreach ($currencies as $currency) { + $inputs[] = Forms\Components\TextInput::make('data.min_prices.'.$currency->code)->label( + $currency->code + )->afterStateHydrated(function (Forms\Components\TextInput $component, $state) { + $currencyCode = last(explode('.', $component->getStatePath())); + $currency = Currency::whereCode($currencyCode)->first(); + + if ($currency) { + $component->state($state / $currency->factor); + } + }); + } + + return $inputs; + } + + protected static function getDiscountTypeFormComponent(): Component + { + return Forms\Components\Select::make('type')->options( + Discounts::getTypes()->mapWithKeys( + fn ($type) => [get_class($type) => $type->getName()] + ) + )->live(); + } + + protected static function getAmountOffFormComponents(): array + { + $currencies = Currency::get(); + + $currencyInputs = []; + + foreach ($currencies as $currency) { + $currencyInputs[] = Forms\Components\TextInput::make( + 'data.fixed_values.'.$currency->code + )->label($currency->name)->afterStateHydrated(function (Forms\Components\TextInput $component, $state) use ($currencies) { + $currencyCode = last(explode('.', $component->getStatePath())); + $currency = $currencies->first( + fn ($currency) => $currency->code == $currencyCode + ); + + if ($currency) { + $component->state($state / $currency->factor); + } + }); + } + + return [ + Forms\Components\Toggle::make('data.fixed_value')->live(), + Forms\Components\TextInput::make('data.percentage')->visible( + fn (Forms\Get $get) => ! $get('data.fixed_value') + ), + Forms\Components\Group::make( + $currencyInputs + )->visible( + fn (Forms\Get $get) => (bool) $get('data.fixed_value') + )->columns(3), + ]; + } + public static function getDefaultTable(Table $table): Table { return $table diff --git a/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php b/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php index ae20bd00c7..026b8be348 100644 --- a/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php +++ b/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php @@ -5,6 +5,7 @@ use Filament\Actions; use Lunar\Admin\Filament\Resources\DiscountResource; use Lunar\Admin\Support\Pages\BaseEditRecord; +use Lunar\Models\Currency; class EditDiscount extends BaseEditRecord { @@ -16,4 +17,40 @@ protected function getDefaultHeaderActions(): array Actions\DeleteAction::make(), ]; } + + protected function mutateFormDataBeforeSave(array $data): array + { + $minPrices = $data['data']['min_prices'] ?? []; + $fixedPrices = $data['data']['fixed_values'] ?? []; + $currencies = Currency::get(); + + foreach ($minPrices as $currencyCode => $value) { + $currency = $currencies->first( + fn ($currency) => $currency->code == $currencyCode + ); + + if (! $currency) { + continue; + } + $data['data']['min_prices'][$currencyCode] = (int) round($value * $currency->factor); + } + + foreach ($fixedPrices as $currencyCode => $fixedPrice) { + $currency = $currencies->first( + fn ($currency) => $currency->code == $currencyCode + ); + + if (! $currency) { + continue; + } + $data['data']['fixed_values'][$currencyCode] = (int) round($fixedPrice * $currency->factor); + } + + return parent::mutateFormDataBeforeSave($data); + } + + public function getRelationManagers(): array + { + return []; + } } diff --git a/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductVariantLimitationRelationManager.php b/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductVariantLimitationRelationManager.php index 65a200e340..8c3802067a 100644 --- a/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductVariantLimitationRelationManager.php +++ b/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductVariantLimitationRelationManager.php @@ -6,6 +6,7 @@ use Filament\Resources\RelationManagers\RelationManager; use Filament\Tables; use Filament\Tables\Table; +use Illuminate\Database\Eloquent\Model; use Lunar\Models\Product; use Lunar\Models\ProductVariant; @@ -61,6 +62,25 @@ public function table(Table $table): Table return $data; }), ])->columns([ + Tables\Columns\TextColumn::make('purchasable') + ->formatStateUsing( + fn (Model $model) => $model->purchasable->getDescription() + ) + ->label( + __('lunarpanel::discount.relationmanagers.productvariants.table.name.label') + ), + Tables\Columns\TextColumn::make('purchasable.sku') + ->label( + __('lunarpanel::discount.relationmanagers.productvariants.table.sku.label') + ), + Tables\Columns\TextColumn::make('purchasable.values') + ->formatStateUsing(function (Model $record) { + return $record->purchasable->values->map( + fn ($value) => $value->translate('name') + )->join(', '); + })->label( + __('lunarpanel::discount.relationmanagers.productvariants.table.values.label') + ), ])->actions([ Tables\Actions\DeleteAction::make(), ]); From 0837397c32712ffcfd46fe472489a87841dd9fd3 Mon Sep 17 00:00:00 2001 From: alecritson Date: Thu, 8 Feb 2024 10:02:30 +0000 Subject: [PATCH 03/11] chore: fix code style --- packages/core/src/Models/Discount.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/core/src/Models/Discount.php b/packages/core/src/Models/Discount.php index 4a95b01e39..2592fdc3a0 100644 --- a/packages/core/src/Models/Discount.php +++ b/packages/core/src/Models/Discount.php @@ -37,8 +37,11 @@ class Discount extends BaseModel protected $guarded = []; const ACTIVE = 'active'; + const PENDING = 'pending'; + const EXPIRED = 'expired'; + const SCHEDULED = 'scheduled'; /** From 4fdc10a0dabce71938857d600092dfd3913c963d Mon Sep 17 00:00:00 2001 From: Alec Ritson Date: Thu, 8 Feb 2024 11:44:59 +0000 Subject: [PATCH 04/11] WIP --- packages/admin/resources/lang/en/discount.php | 85 +++++++++++++++++++ .../Filament/Resources/DiscountResource.php | 53 ++++++++++-- .../DiscountResource/Pages/EditDiscount.php | 10 ++- .../Pages/ManageBuyXGetYDiscount.php | 53 ++++++++++++ .../Pages/ManageDiscountAvailability.php | 47 ++++++++++ .../ProductConditionRelationManager.php | 83 ++++++++++++++++++ .../ProductRewardRelationManager.php | 83 ++++++++++++++++++ 7 files changed, 406 insertions(+), 8 deletions(-) create mode 100644 packages/admin/src/Filament/Resources/DiscountResource/Pages/ManageBuyXGetYDiscount.php create mode 100644 packages/admin/src/Filament/Resources/DiscountResource/Pages/ManageDiscountAvailability.php create mode 100644 packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductConditionRelationManager.php create mode 100644 packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductRewardRelationManager.php diff --git a/packages/admin/resources/lang/en/discount.php b/packages/admin/resources/lang/en/discount.php index 488c9aff1c..a513e7f59b 100644 --- a/packages/admin/resources/lang/en/discount.php +++ b/packages/admin/resources/lang/en/discount.php @@ -52,6 +52,18 @@ 'minimum_cart_amount' => [ 'label' => 'Minimum Cart Amount', ], + 'min_qty' => [ + 'label' => 'Product Quantity', + 'helper_text' => 'Set how many qualifying products are required for the discount to apply.', + ], + 'reward_qty' => [ + 'label' => 'No. of free items', + 'helper_text' => 'How many of each item are discounted.', + ], + 'max_reward_qty' => [ + 'label' => 'Maximum reward quantity', + 'helper_text' => 'The maximum amount of products which can be discounted, regardless of criteria.', + ], ], 'table' => [ 'name' => [ @@ -83,6 +95,9 @@ ], ], 'pages' => [ + 'availability' => [ + 'label' => 'Availability', + ], 'limitations' => [ 'label' => 'Limitations', ], @@ -193,6 +208,76 @@ ], ], ], + 'rewards' => [ + 'title' => 'Product Rewards', + 'description' => 'Select which products will be discounted if they exist in the cart and the above conditions are met.', + 'actions' => [ + 'attach' => [ + 'label' => 'Add Product', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Name', + ], + 'type' => [ + 'label' => 'Type', + 'limitation' => [ + 'label' => 'Limitation', + ], + 'exclusion' => [ + 'label' => 'Exclusion', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Limitation', + ], + 'exclusion' => [ + 'label' => 'Exclusion', + ], + ], + ], + ], + ], + 'conditions' => [ + 'title' => 'Product Conditions', + 'description' => 'Select the products required for the discount to apply.', + 'actions' => [ + 'attach' => [ + 'label' => 'Add Product', + ], + ], + 'table' => [ + 'name' => [ + 'label' => 'Name', + ], + 'type' => [ + 'label' => 'Type', + 'limitation' => [ + 'label' => 'Limitation', + ], + 'exclusion' => [ + 'label' => 'Exclusion', + ], + ], + ], + 'form' => [ + 'type' => [ + 'options' => [ + 'limitation' => [ + 'label' => 'Limitation', + ], + 'exclusion' => [ + 'label' => 'Exclusion', + ], + ], + ], + ], + ], 'productvariants' => [ 'title' => 'Product Variants', 'description' => 'Select which product variants this discount should be limited to.', diff --git a/packages/admin/src/Filament/Resources/DiscountResource.php b/packages/admin/src/Filament/Resources/DiscountResource.php index 05996a5576..38a2060dc5 100644 --- a/packages/admin/src/Filament/Resources/DiscountResource.php +++ b/packages/admin/src/Filament/Resources/DiscountResource.php @@ -14,10 +14,13 @@ use Lunar\Admin\Filament\Resources\DiscountResource\Pages; use Lunar\Admin\Filament\Resources\DiscountResource\RelationManagers\BrandLimitationRelationManager; use Lunar\Admin\Filament\Resources\DiscountResource\RelationManagers\CollectionLimitationRelationManager; +use Lunar\Admin\Filament\Resources\DiscountResource\RelationManagers\ProductConditionRelationManager; use Lunar\Admin\Filament\Resources\DiscountResource\RelationManagers\ProductLimitationRelationManager; +use Lunar\Admin\Filament\Resources\DiscountResource\RelationManagers\ProductRewardRelationManager; use Lunar\Admin\Filament\Resources\DiscountResource\RelationManagers\ProductVariantLimitationRelationManager; use Lunar\Admin\Support\Resources\BaseResource; use Lunar\DiscountTypes\AmountOff; +use Lunar\DiscountTypes\BuyXGetY; use Lunar\Facades\Discounts; use Lunar\Models\Currency; use Lunar\Models\Discount; @@ -58,6 +61,18 @@ public static function getDefaultForm(Form $form): Form Forms\Components\Section::make('')->schema( static::getMainFormComponents() ), + Forms\Components\Section::make('conditions')->schema( + static::getConditionsFormComponents() + )->heading( + __('lunarpanel::discount.form.conditions.heading') + ), + Forms\Components\Section::make('buy_x_get_y') + ->heading('Buy X Get Y') + ->visible( + fn (Forms\Get $get) => $get('type') == BuyXGetY::class + )->schema( + static::getBuyXGetYFormComponents() + ), Forms\Components\Section::make('amount_off') ->heading('Amount Off') ->visible( @@ -65,12 +80,6 @@ public static function getDefaultForm(Form $form): Form )->schema( static::getAmountOffFormComponents() ), - - Forms\Components\Section::make('conditions')->schema( - static::getConditionsFormComponents() - )->heading( - __('lunarpanel::discount.form.conditions.heading') - ), ]); } @@ -259,7 +268,7 @@ protected static function getAmountOffFormComponents(): array Forms\Components\Toggle::make('data.fixed_value')->live(), Forms\Components\TextInput::make('data.percentage')->visible( fn (Forms\Get $get) => ! $get('data.fixed_value') - ), + )->numeric(), Forms\Components\Group::make( $currencyInputs )->visible( @@ -268,6 +277,32 @@ protected static function getAmountOffFormComponents(): array ]; } + public static function getBuyXGetYFormComponents(): array + { + return [ + Forms\Components\TextInput::make('data.min_qty') + ->label( + __('lunarpanel::discount.form.min_qty.label') + )->helperText( + __('lunarpanel::discount.form.min_qty.helper_text') + )->numeric(), + Forms\Components\Group::make([ + Forms\Components\TextInput::make('data.reward_qty') + ->label( + __('lunarpanel::discount.form.reward_qty.label') + )->helperText( + __('lunarpanel::discount.form.reward_qty.helper_text') + )->numeric(), + Forms\Components\TextInput::make('data.max_reward_qty') + ->label( + __('lunarpanel::discount.form.max_reward_qty.label') + )->helperText( + __('lunarpanel::discount.form.max_reward_qty.helper_text') + )->numeric(), + ])->columns(2), + ]; + } + public static function getDefaultTable(Table $table): Table { return $table @@ -320,6 +355,7 @@ public static function getRecordSubNavigation(Page $page): array { return $page->generateNavigationItems([ Pages\EditDiscount::class, + Pages\ManageDiscountAvailability::class, Pages\ManageDiscountLimitations::class, ]); } @@ -331,6 +367,8 @@ protected static function getDefaultRelations(): array BrandLimitationRelationManager::class, ProductLimitationRelationManager::class, ProductVariantLimitationRelationManager::class, + ProductRewardRelationManager::class, + ProductConditionRelationManager::class, ]; } @@ -340,6 +378,7 @@ public static function getPages(): array 'index' => Pages\ListDiscounts::route('/'), 'edit' => Pages\EditDiscount::route('/{record}'), 'limitations' => Pages\ManageDiscountLimitations::route('/{record}/limitations'), + 'availability' => Pages\ManageDiscountAvailability::route('/{record}/availability'), ]; } } diff --git a/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php b/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php index 026b8be348..52c264c9ac 100644 --- a/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php +++ b/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php @@ -5,6 +5,7 @@ use Filament\Actions; use Lunar\Admin\Filament\Resources\DiscountResource; use Lunar\Admin\Support\Pages\BaseEditRecord; +use Lunar\DiscountTypes\BuyXGetY; use Lunar\Models\Currency; class EditDiscount extends BaseEditRecord @@ -51,6 +52,13 @@ protected function mutateFormDataBeforeSave(array $data): array public function getRelationManagers(): array { - return []; + $managers = []; + + if ($this->form->getState()['type'] == BuyXGetY::class) { + $managers[] = DiscountResource\RelationManagers\ProductRewardRelationManager::class; + $managers[] = DiscountResource\RelationManagers\ProductConditionRelationManager::class; + } + + return $managers; } } diff --git a/packages/admin/src/Filament/Resources/DiscountResource/Pages/ManageBuyXGetYDiscount.php b/packages/admin/src/Filament/Resources/DiscountResource/Pages/ManageBuyXGetYDiscount.php new file mode 100644 index 0000000000..78c097e387 --- /dev/null +++ b/packages/admin/src/Filament/Resources/DiscountResource/Pages/ManageBuyXGetYDiscount.php @@ -0,0 +1,53 @@ +schema([]); + } + + protected function getFormActions(): array + { + return []; + } + + public function getRelationManagers(): array + { + return [ + RelationGroup::make('Limitations', [ + DiscountResource\RelationManagers\CollectionLimitationRelationManager::class, + DiscountResource\RelationManagers\BrandLimitationRelationManager::class, + DiscountResource\RelationManagers\ProductLimitationRelationManager::class, + DiscountResource\RelationManagers\ProductVariantLimitationRelationManager::class, + ]), + + ]; + } +} diff --git a/packages/admin/src/Filament/Resources/DiscountResource/Pages/ManageDiscountAvailability.php b/packages/admin/src/Filament/Resources/DiscountResource/Pages/ManageDiscountAvailability.php new file mode 100644 index 0000000000..476d107b4f --- /dev/null +++ b/packages/admin/src/Filament/Resources/DiscountResource/Pages/ManageDiscountAvailability.php @@ -0,0 +1,47 @@ + [ + 'enabled', + 'visible', + ], + ]), + ]), + ]; + } +} diff --git a/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductConditionRelationManager.php b/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductConditionRelationManager.php new file mode 100644 index 0000000000..d642946cc4 --- /dev/null +++ b/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductConditionRelationManager.php @@ -0,0 +1,83 @@ +heading( + __('lunarpanel::discount.relationmanagers.conditions.title') + ) + ->description( + __('lunarpanel::discount.relationmanagers.conditions.description') + ) + ->paginated(false) + ->modifyQueryUsing( + fn ($query) => $query->whereIn('type', ['condition']) + ->wherePurchasableType(Product::class) + ->whereHas('purchasable') + ) + ->headerActions([ + Tables\Actions\CreateAction::make()->form([ + Forms\Components\MorphToSelect::make('purchasable') + ->searchable(true) + ->types([ + Forms\Components\MorphToSelect\Type::make(Product::class) + ->titleAttribute('name.en') + ->getSearchResultsUsing(static function (Forms\Components\Select $component, string $search): array { + return Product::search($search) + ->get() + ->mapWithKeys(fn (Product $record): array => [$record->getKey() => $record->attr('name')]) + ->all(); + }), + ]), + ])->label( + __('lunarpanel::discount.relationmanagers.conditions.actions.attach.label') + )->mutateFormDataUsing(function (array $data) { + $data['type'] = 'condition'; + + return $data; + }), + ])->columns([ + Tables\Columns\SpatieMediaLibraryImageColumn::make('purchasable.thumbnail') + ->collection('images') + ->conversion('small') + ->limit(1) + ->square() + ->label(''), + Tables\Columns\TextColumn::make('purchasable.attribute_data.name') + ->label( + __('lunarpanel::discount.relationmanagers.conditions.table.name.label') + ) + ->formatStateUsing( + fn (Model $record) => $record->purchasable->attr('name') + ), + ])->actions([ + Tables\Actions\DeleteAction::make(), + ]); + } +} diff --git a/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductRewardRelationManager.php b/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductRewardRelationManager.php new file mode 100644 index 0000000000..e9d0de67aa --- /dev/null +++ b/packages/admin/src/Filament/Resources/DiscountResource/RelationManagers/ProductRewardRelationManager.php @@ -0,0 +1,83 @@ +heading( + __('lunarpanel::discount.relationmanagers.rewards.title') + ) + ->description( + __('lunarpanel::discount.relationmanagers.rewards.description') + ) + ->paginated(false) + ->modifyQueryUsing( + fn ($query) => $query->whereIn('type', ['reward']) + ->wherePurchasableType(Product::class) + ->whereHas('purchasable') + ) + ->headerActions([ + Tables\Actions\CreateAction::make()->form([ + Forms\Components\MorphToSelect::make('purchasable') + ->searchable(true) + ->types([ + Forms\Components\MorphToSelect\Type::make(Product::class) + ->titleAttribute('name.en') + ->getSearchResultsUsing(static function (Forms\Components\Select $component, string $search): array { + return Product::search($search) + ->get() + ->mapWithKeys(fn (Product $record): array => [$record->getKey() => $record->attr('name')]) + ->all(); + }), + ]), + ])->label( + __('lunarpanel::discount.relationmanagers.rewards.actions.attach.label') + )->mutateFormDataUsing(function (array $data) { + $data['type'] = 'reward'; + + return $data; + }), + ])->columns([ + Tables\Columns\SpatieMediaLibraryImageColumn::make('purchasable.thumbnail') + ->collection('images') + ->conversion('small') + ->limit(1) + ->square() + ->label(''), + Tables\Columns\TextColumn::make('purchasable.attribute_data.name') + ->label( + __('lunarpanel::discount.relationmanagers.rewards.table.name.label') + ) + ->formatStateUsing( + fn (Model $record) => $record->purchasable->attr('name') + ), + ])->actions([ + Tables\Actions\DeleteAction::make(), + ]); + } +} From b93d52f6cb393df0febd5ee141e40055e24d80ed Mon Sep 17 00:00:00 2001 From: Alec Ritson Date: Thu, 8 Feb 2024 11:55:56 +0000 Subject: [PATCH 05/11] Update EditDiscount.php --- .../Filament/Resources/DiscountResource/Pages/EditDiscount.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php b/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php index 52c264c9ac..41ea18d62d 100644 --- a/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php +++ b/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php @@ -55,8 +55,8 @@ public function getRelationManagers(): array $managers = []; if ($this->form->getState()['type'] == BuyXGetY::class) { - $managers[] = DiscountResource\RelationManagers\ProductRewardRelationManager::class; $managers[] = DiscountResource\RelationManagers\ProductConditionRelationManager::class; + $managers[] = DiscountResource\RelationManagers\ProductRewardRelationManager::class; } return $managers; From 90bc98dcb61194bf79e53a5beb9c48300b68d100 Mon Sep 17 00:00:00 2001 From: Alec Ritson Date: Thu, 8 Feb 2024 13:50:28 +0000 Subject: [PATCH 06/11] WIP --- .../Filament/Resources/DiscountResource.php | 5 +- .../DiscountResource/Pages/EditDiscount.php | 4 +- .../Pages/EditDiscountTest.php | 47 +++++++++++++++++++ .../Pages/ManageDiscountAvailabilityTest.php | 20 ++++++++ .../Pages/ManageDiscountLimitationsTest.php | 20 ++++++++ 5 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 tests/admin/Feature/Filament/Resources/DiscountResource/Pages/EditDiscountTest.php create mode 100644 tests/admin/Feature/Filament/Resources/DiscountResource/Pages/ManageDiscountAvailabilityTest.php create mode 100644 tests/admin/Feature/Filament/Resources/DiscountResource/Pages/ManageDiscountLimitationsTest.php diff --git a/packages/admin/src/Filament/Resources/DiscountResource.php b/packages/admin/src/Filament/Resources/DiscountResource.php index 38a2060dc5..1ac1eab20a 100644 --- a/packages/admin/src/Filament/Resources/DiscountResource.php +++ b/packages/admin/src/Filament/Resources/DiscountResource.php @@ -19,8 +19,6 @@ use Lunar\Admin\Filament\Resources\DiscountResource\RelationManagers\ProductRewardRelationManager; use Lunar\Admin\Filament\Resources\DiscountResource\RelationManagers\ProductVariantLimitationRelationManager; use Lunar\Admin\Support\Resources\BaseResource; -use Lunar\DiscountTypes\AmountOff; -use Lunar\DiscountTypes\BuyXGetY; use Lunar\Facades\Discounts; use Lunar\Models\Currency; use Lunar\Models\Discount; @@ -148,6 +146,7 @@ protected static function getStartsAtFormComponent(): Component { return Forms\Components\DateTimePicker::make('starts_at') ->label(__('lunarpanel::discount.form.starts_at.label')) + ->required() ->before(function (Forms\Get $get) { return $get('ends_at'); }); @@ -369,6 +368,8 @@ protected static function getDefaultRelations(): array ProductVariantLimitationRelationManager::class, ProductRewardRelationManager::class, ProductConditionRelationManager::class, + ProductRewardRelationManager::class, + ProductConditionRelationManager::class, ]; } diff --git a/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php b/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php index 41ea18d62d..f719f6e9bd 100644 --- a/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php +++ b/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php @@ -47,14 +47,14 @@ protected function mutateFormDataBeforeSave(array $data): array $data['data']['fixed_values'][$currencyCode] = (int) round($fixedPrice * $currency->factor); } - return parent::mutateFormDataBeforeSave($data); + return $data; } public function getRelationManagers(): array { $managers = []; - if ($this->form->getState()['type'] == BuyXGetY::class) { + if ($this->record->type == BuyXGetY::class) { $managers[] = DiscountResource\RelationManagers\ProductConditionRelationManager::class; $managers[] = DiscountResource\RelationManagers\ProductRewardRelationManager::class; } diff --git a/tests/admin/Feature/Filament/Resources/DiscountResource/Pages/EditDiscountTest.php b/tests/admin/Feature/Filament/Resources/DiscountResource/Pages/EditDiscountTest.php new file mode 100644 index 0000000000..b185c850bb --- /dev/null +++ b/tests/admin/Feature/Filament/Resources/DiscountResource/Pages/EditDiscountTest.php @@ -0,0 +1,47 @@ +group('resource.discount'); + +beforeEach(function () { + $this->asStaff(); +}); + +it('can render discount edit page', function () { + get( + \Lunar\Admin\Filament\Resources\DiscountResource::getUrl( + 'edit', + ['record' => \Lunar\Models\Discount::factory()->create()] + ) + )->assertSuccessful(); +}); + +it('can edit discount', function () { + $discount = \Lunar\Models\Discount::factory()->create(); + \Livewire\Livewire::test(\Lunar\Admin\Filament\Resources\DiscountResource\Pages\EditDiscount::class, + ['record' => $discount->getKey()] + )->fillForm([ + 'name' => 'Updated Name', + 'handle' => 'updated_name', + ])->call('save')->assertHasNoErrors(); + + assertDatabaseHas(\Lunar\Models\Discount::class, [ + 'name' => 'Updated Name', + 'handle' => 'updated_name', + ]); +}); + +it('can validate start and end date', function () { + $discount = \Lunar\Models\Discount::factory()->create(); + \Livewire\Livewire::test(\Lunar\Admin\Filament\Resources\DiscountResource\Pages\EditDiscount::class, + ['record' => $discount->getKey()] + )->fillForm([ + 'starts_at' => now(), + 'ends_at' => now()->subWeek(), + ])->call('save')->assertHasFormErrors([ + 'starts_at' => 'before', + ]); +}); diff --git a/tests/admin/Feature/Filament/Resources/DiscountResource/Pages/ManageDiscountAvailabilityTest.php b/tests/admin/Feature/Filament/Resources/DiscountResource/Pages/ManageDiscountAvailabilityTest.php new file mode 100644 index 0000000000..b41af193f7 --- /dev/null +++ b/tests/admin/Feature/Filament/Resources/DiscountResource/Pages/ManageDiscountAvailabilityTest.php @@ -0,0 +1,20 @@ +group('resource.discount'); + +beforeEach(function () { + $this->asStaff(); +}); + +it('can render discount availability page', function () { + $record = \Lunar\Models\Discount::factory()->create(); + + \Lunar\Models\Channel::factory()->create(['default' => true]); + + get(\Lunar\Admin\Filament\Resources\DiscountResource::getUrl('availability', [ + 'record' => $record, + ]))->assertSuccessful(); +}); diff --git a/tests/admin/Feature/Filament/Resources/DiscountResource/Pages/ManageDiscountLimitationsTest.php b/tests/admin/Feature/Filament/Resources/DiscountResource/Pages/ManageDiscountLimitationsTest.php new file mode 100644 index 0000000000..229ba2c13d --- /dev/null +++ b/tests/admin/Feature/Filament/Resources/DiscountResource/Pages/ManageDiscountLimitationsTest.php @@ -0,0 +1,20 @@ +group('resource.discount'); + +beforeEach(function () { + $this->asStaff(); +}); + +it('can render discount limitations page', function () { + $record = \Lunar\Models\Discount::factory()->create(); + + \Lunar\Models\Channel::factory()->create(['default' => true]); + + get(\Lunar\Admin\Filament\Resources\DiscountResource::getUrl('limitations', [ + 'record' => $record, + ]))->assertSuccessful(); +}); From 26847a0a782fdd75c9a9e4d1f7ed1f5a781583ed Mon Sep 17 00:00:00 2001 From: Alec Ritson Date: Thu, 8 Feb 2024 13:51:08 +0000 Subject: [PATCH 07/11] Update DiscountResource.php --- packages/admin/src/Filament/Resources/DiscountResource.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/admin/src/Filament/Resources/DiscountResource.php b/packages/admin/src/Filament/Resources/DiscountResource.php index 1ac1eab20a..c0a591eb7a 100644 --- a/packages/admin/src/Filament/Resources/DiscountResource.php +++ b/packages/admin/src/Filament/Resources/DiscountResource.php @@ -19,6 +19,8 @@ use Lunar\Admin\Filament\Resources\DiscountResource\RelationManagers\ProductRewardRelationManager; use Lunar\Admin\Filament\Resources\DiscountResource\RelationManagers\ProductVariantLimitationRelationManager; use Lunar\Admin\Support\Resources\BaseResource; +use Lunar\DiscountTypes\AmountOff; +use Lunar\DiscountTypes\BuyXGetY; use Lunar\Facades\Discounts; use Lunar\Models\Currency; use Lunar\Models\Discount; From 03fb247d99088eea461e8c5379cd53743798f94d Mon Sep 17 00:00:00 2001 From: Alec Ritson Date: Thu, 8 Feb 2024 14:42:27 +0000 Subject: [PATCH 08/11] Tweak creation --- .../Filament/Resources/DiscountResource.php | 12 ++++---- .../DiscountResource/Pages/ListDiscounts.php | 13 ++++++++- .../Pages/ListDiscountsTest.php | 28 +++++++++++++++++++ 3 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 tests/admin/Feature/Filament/Resources/DiscountResource/Pages/ListDiscountsTest.php diff --git a/packages/admin/src/Filament/Resources/DiscountResource.php b/packages/admin/src/Filament/Resources/DiscountResource.php index c0a591eb7a..a1c24b3d9b 100644 --- a/packages/admin/src/Filament/Resources/DiscountResource.php +++ b/packages/admin/src/Filament/Resources/DiscountResource.php @@ -118,7 +118,7 @@ protected static function getConditionsFormComponents(): array ]; } - protected static function getNameFormComponent(): Component + public static function getNameFormComponent(): Component { return Forms\Components\TextInput::make('name') ->label(__('lunarpanel::discount.form.name.label')) @@ -134,7 +134,7 @@ protected static function getNameFormComponent(): Component ->autofocus(); } - protected static function getHandleFormComponent(): Component + public static function getHandleFormComponent(): Component { return Forms\Components\TextInput::make('handle') ->label(__('lunarpanel::discount.form.handle.label')) @@ -144,7 +144,7 @@ protected static function getHandleFormComponent(): Component ->autofocus(); } - protected static function getStartsAtFormComponent(): Component + public static function getStartsAtFormComponent(): Component { return Forms\Components\DateTimePicker::make('starts_at') ->label(__('lunarpanel::discount.form.starts_at.label')) @@ -154,7 +154,7 @@ protected static function getStartsAtFormComponent(): Component }); } - protected static function getEndsAtFormComponent(): Component + public static function getEndsAtFormComponent(): Component { return Forms\Components\DateTimePicker::make('ends_at') ->label(__('lunarpanel::discount.form.ends_at.label')); @@ -235,13 +235,13 @@ protected static function getMinimumCartAmountsFormComponents(): array return $inputs; } - protected static function getDiscountTypeFormComponent(): Component + public static function getDiscountTypeFormComponent(): Component { return Forms\Components\Select::make('type')->options( Discounts::getTypes()->mapWithKeys( fn ($type) => [get_class($type) => $type->getName()] ) - )->live(); + )->required()->live(); } protected static function getAmountOffFormComponents(): array diff --git a/packages/admin/src/Filament/Resources/DiscountResource/Pages/ListDiscounts.php b/packages/admin/src/Filament/Resources/DiscountResource/Pages/ListDiscounts.php index e6ca818968..a19d12b06e 100644 --- a/packages/admin/src/Filament/Resources/DiscountResource/Pages/ListDiscounts.php +++ b/packages/admin/src/Filament/Resources/DiscountResource/Pages/ListDiscounts.php @@ -3,6 +3,7 @@ namespace Lunar\Admin\Filament\Resources\DiscountResource\Pages; use Filament\Actions; +use Filament\Forms; use Lunar\Admin\Filament\Resources\DiscountResource; use Lunar\Admin\Support\Pages\BaseListRecords; @@ -13,7 +14,17 @@ class ListDiscounts extends BaseListRecords protected function getDefaultHeaderActions(): array { return [ - Actions\CreateAction::make(), + Actions\CreateAction::make()->form([ + Forms\Components\Group::make([ + DiscountResource::getNameFormComponent(), + DiscountResource::getHandleFormComponent(), + ])->columns(2), + Forms\Components\Group::make([ + DiscountResource::getStartsAtFormComponent(), + DiscountResource::getEndsAtFormComponent(), + ])->columns(2), + DiscountResource::getDiscountTypeFormComponent(), + ]), ]; } } diff --git a/tests/admin/Feature/Filament/Resources/DiscountResource/Pages/ListDiscountsTest.php b/tests/admin/Feature/Filament/Resources/DiscountResource/Pages/ListDiscountsTest.php new file mode 100644 index 0000000000..275a04344a --- /dev/null +++ b/tests/admin/Feature/Filament/Resources/DiscountResource/Pages/ListDiscountsTest.php @@ -0,0 +1,28 @@ +group('resource.discount'); + +beforeEach(function () { + $this->asStaff(); +}); + +it('can list discounts', function () { + get( + \Lunar\Admin\Filament\Resources\DiscountResource::getUrl('index') + )->assertSuccessful(); +}); + +it('can create a discount', function () { + $discount = \Lunar\Models\Discount::factory()->create(); + \Livewire\Livewire::test( + \Lunar\Admin\Filament\Resources\DiscountResource\Pages\ListDiscounts::class + )->callAction('create', [ + 'name' => 'Discount A', + 'handle' => 'discount_a', + 'starts_at' => now(), + 'type' => \Lunar\DiscountTypes\BuyXGetY::class, + ])->assertHasNoErrors(); +}); From 5646969b63e89429b5a20378affe0c14c2bdbeb9 Mon Sep 17 00:00:00 2001 From: Alec Ritson Date: Fri, 9 Feb 2024 08:10:46 +0000 Subject: [PATCH 09/11] Translations --- packages/admin/resources/lang/en/discount.php | 6 ++++++ .../admin/src/Filament/Resources/DiscountResource.php | 8 ++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/admin/resources/lang/en/discount.php b/packages/admin/resources/lang/en/discount.php index a513e7f59b..089b30b67c 100644 --- a/packages/admin/resources/lang/en/discount.php +++ b/packages/admin/resources/lang/en/discount.php @@ -7,6 +7,12 @@ 'conditions' => [ 'heading' => 'Conditions', ], + 'buy_x_get_y' => [ + 'heading' => 'Buy X Get Y', + ], + 'amount_off' => [ + 'heading' => 'Amount Off', + ], 'name' => [ 'label' => 'Name', ], diff --git a/packages/admin/src/Filament/Resources/DiscountResource.php b/packages/admin/src/Filament/Resources/DiscountResource.php index a1c24b3d9b..ee4ac9b6f6 100644 --- a/packages/admin/src/Filament/Resources/DiscountResource.php +++ b/packages/admin/src/Filament/Resources/DiscountResource.php @@ -67,14 +67,18 @@ public static function getDefaultForm(Form $form): Form __('lunarpanel::discount.form.conditions.heading') ), Forms\Components\Section::make('buy_x_get_y') - ->heading('Buy X Get Y') + ->heading( + __('lunarpanel::discount.form.buy_x_get_y.heading') + ) ->visible( fn (Forms\Get $get) => $get('type') == BuyXGetY::class )->schema( static::getBuyXGetYFormComponents() ), Forms\Components\Section::make('amount_off') - ->heading('Amount Off') + ->heading( + __('lunarpanel::discount.form.amount_off.heading') + ) ->visible( fn (Forms\Get $get) => $get('type') == AmountOff::class )->schema( From 72b09d36436ea7411c8d3a5da70a01001423fcf6 Mon Sep 17 00:00:00 2001 From: Alec Ritson Date: Thu, 15 Feb 2024 08:57:40 +0000 Subject: [PATCH 10/11] Only show enabled --- packages/admin/src/Filament/Resources/DiscountResource.php | 2 +- .../Resources/DiscountResource/Pages/EditDiscount.php | 4 ++-- packages/core/src/Models/Currency.php | 5 +++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/admin/src/Filament/Resources/DiscountResource.php b/packages/admin/src/Filament/Resources/DiscountResource.php index ee4ac9b6f6..e537486909 100644 --- a/packages/admin/src/Filament/Resources/DiscountResource.php +++ b/packages/admin/src/Filament/Resources/DiscountResource.php @@ -220,7 +220,7 @@ protected static function getMaxUsesPerUserFormComponent(): Component protected static function getMinimumCartAmountsFormComponents(): array { - $currencies = Currency::get(); + $currencies = Currency::enabled()->get(); $inputs = []; foreach ($currencies as $currency) { diff --git a/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php b/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php index f719f6e9bd..b01698ecfe 100644 --- a/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php +++ b/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php @@ -23,8 +23,8 @@ protected function mutateFormDataBeforeSave(array $data): array { $minPrices = $data['data']['min_prices'] ?? []; $fixedPrices = $data['data']['fixed_values'] ?? []; - $currencies = Currency::get(); - + $currencies = Currency::enabled()->get(); + foreach ($minPrices as $currencyCode => $value) { $currency = $currencies->first( fn ($currency) => $currency->code == $currencyCode diff --git a/packages/core/src/Models/Currency.php b/packages/core/src/Models/Currency.php index 416bd511ba..51bb4702be 100644 --- a/packages/core/src/Models/Currency.php +++ b/packages/core/src/Models/Currency.php @@ -44,6 +44,11 @@ protected static function newFactory(): CurrencyFactory return CurrencyFactory::new(); } + public function scopeEnabled($query, $enabled = true) + { + return $query->whereEnabled($enabled); + } + /** * Return the prices relationship */ From 1bc657c65bc5a6de94589d80f51b0b515637735d Mon Sep 17 00:00:00 2001 From: alecritson Date: Thu, 15 Feb 2024 09:00:02 +0000 Subject: [PATCH 11/11] chore: fix code style --- .../Filament/Resources/DiscountResource/Pages/EditDiscount.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php b/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php index b01698ecfe..754c8fe12b 100644 --- a/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php +++ b/packages/admin/src/Filament/Resources/DiscountResource/Pages/EditDiscount.php @@ -24,7 +24,7 @@ protected function mutateFormDataBeforeSave(array $data): array $minPrices = $data['data']['min_prices'] ?? []; $fixedPrices = $data['data']['fixed_values'] ?? []; $currencies = Currency::enabled()->get(); - + foreach ($minPrices as $currencyCode => $value) { $currency = $currencies->first( fn ($currency) => $currency->code == $currencyCode