Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions database/factories/ProductDataFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace Eclipse\Catalogue\Factories;

use Eclipse\Catalogue\Models\Product;
use Eclipse\Catalogue\Models\ProductData;
use Illuminate\Database\Eloquent\Factories\Factory;

/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\Eclipse\Catalogue\Models\ProductData>
*/
class ProductDataFactory extends Factory
{
protected $model = ProductData::class;

public function definition(): array
{
$definition = [
'product_id' => Product::factory(),
'sorting_label' => $this->faker->optional()->word(),
'is_active' => $this->faker->boolean(90),
'available_from_date' => $this->faker->optional()->dateTimeBetween('now', '+3 months'),
'has_free_delivery' => $this->faker->boolean(20),
];

if (config('eclipse-catalogue.tenancy.foreign_key')) {
$tenantModel = config('eclipse-catalogue.tenancy.model');
if (class_exists($tenantModel)) {
$definition[config('eclipse-catalogue.tenancy.foreign_key')] = $tenantModel::factory();
}
}

return $definition;
}

public function active(): static
{
return $this->state(fn (array $attributes) => [
'is_active' => true,
]);
}

public function inactive(): static
{
return $this->state(fn (array $attributes) => [
'is_active' => false,
]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
public function up(): void
{
Schema::create('catalogue_product_data', function (Blueprint $table) {
$table->id();
$table->foreignId('product_id');

// Add foreign key for tenant if it's configured in the catalogue config
if (config('eclipse-catalogue.tenancy.model')) {
$tenantClass = config('eclipse-catalogue.tenancy.model');
/** @var \Illuminate\Database\Eloquent\Model $tenant */
$tenant = new $tenantClass;
$table->foreignId(config('eclipse-catalogue.tenancy.foreign_key'))
->constrained($tenant->getTable(), $tenant->getKeyName())
->cascadeOnUpdate()
->cascadeOnDelete();
}

$table->string('sorting_label')->nullable();
$table->boolean('is_active')->default(false);
$table->dateTime('available_from_date')->nullable();
$table->boolean('has_free_delivery')->default(false);
});
}

public function down(): void
{
Schema::dropIfExists('catalogue_product_data');
}
};
36 changes: 36 additions & 0 deletions database/migrations/2025_08_16_121654_add_product_attributes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
public function up(): void
{
Schema::table('catalogue_products', function (Blueprint $table) {
$table->string('origin_country_id', 2)->nullable();
$table->foreign('origin_country_id')
->references('id')
->on('world_countries')
->cascadeOnUpdate()
->restrictOnDelete();
$table->text('meta_description')
->nullable();
$table->string('meta_title')
->nullable();
});
}

public function down(): void
{
Schema::table('catalogue_products', function (Blueprint $table) {
$table->dropForeign(['origin_country_id']);
$table->dropColumn([
'origin_country_id',
'meta_description',
'meta_title',
]);
});
}
};
32 changes: 32 additions & 0 deletions resources/lang/en/product.php
Original file line number Diff line number Diff line change
@@ -1,21 +1,53 @@
<?php

return [
'singular' => 'Product',
'plural' => 'Products',

'fields' => [
'product_type' => 'Product Type',
'origin_country_id' => 'Country of Origin',
'meta_title' => 'Meta Title',
'meta_description' => 'Meta Description',
'is_active' => 'Active',
'has_free_delivery' => 'Free Delivery',
'available_from_date' => 'Available From',
'sorting_label' => 'Sorting Label',
],

'placeholders' => [
'product_type' => 'Select product type (optional)',
'origin_country_id' => 'Select country of origin',
'meta_title' => 'SEO meta title',
'meta_description' => 'SEO meta description',
],

'table' => [
'columns' => [
'type' => 'Type',
'is_active' => 'Active',
],
],

'filters' => [
'product_type' => 'Product Types',
],

'sections' => [
'tenant_settings' => 'Tenant Settings',
'tenant_settings_description' => 'Configure product settings per tenant',
'tenant_specific' => 'Tenant Specific Settings',
'seo' => 'SEO',
'seo_description' => 'Search engine optimization fields',
'additional' => 'Additional Information',
],

'help_text' => [
'is_active' => 'Enable this product',
'is_active_tenant' => 'Enable this product for :tenant',
'has_free_delivery' => 'Mark product as free delivery',
'has_free_delivery_tenant' => 'Mark product as free delivery for :tenant',
'available_from_date' => 'Date/time when the product becomes available',
'sorting_label' => 'Optional label used to influence sorting within lists',
],
];
32 changes: 32 additions & 0 deletions resources/lang/sl/product.php
Original file line number Diff line number Diff line change
@@ -1,21 +1,53 @@
<?php

return [
'singular' => 'Izdelek',
'plural' => 'Izdelki',

'fields' => [
'product_type' => 'Tip proizvoda',
'origin_country_id' => 'Država izvora',
'meta_title' => 'Meta naslov',
'meta_description' => 'Meta opis',
'is_active' => 'Aktiven',
'has_free_delivery' => 'Brezplačna dostava',
'available_from_date' => 'Na voljo od',
'sorting_label' => 'Oznaka za razvrščanje',
],

'placeholders' => [
'product_type' => 'Izberi tip proizvoda (neobvezno)',
'origin_country_id' => 'Izberi državo izvora',
'meta_title' => 'SEO meta naslov',
'meta_description' => 'SEO meta opis',
],

'table' => [
'columns' => [
'type' => 'Tip',
'is_active' => 'Aktiven',
],
],

'filters' => [
'product_type' => 'Tipi proizvodov',
],

'sections' => [
'tenant_settings' => 'Nastavitve najemnikov',
'tenant_settings_description' => 'Nastavi parametre izdelka za posamezne najemnike',
'tenant_specific' => 'Nastavitve za najemnika',
'seo' => 'SEO',
'seo_description' => 'Polja za optimizacijo iskalnikov',
'additional' => 'Dodatne informacije',
],

'help_text' => [
'is_active' => 'Omogoči ta izdelek',
'is_active_tenant' => 'Omogoči ta izdelek za :tenant',
'has_free_delivery' => 'Označi izdelek kot brezplačna dostava',
'has_free_delivery_tenant' => 'Označi izdelek kot brezplačna dostava za :tenant',
'available_from_date' => 'Datum/čas, ko bo izdelek na voljo',
'sorting_label' => 'Neobvezna oznaka, ki vpliva na razvrščanje v seznamih',
],
];
98 changes: 87 additions & 11 deletions src/Filament/Resources/ProductResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
use BezhanSalleh\FilamentShield\Contracts\HasShieldPermissions;
use Eclipse\Catalogue\Filament\Forms\Components\ImageManager;
use Eclipse\Catalogue\Filament\Resources\ProductResource\Pages;
use Eclipse\Catalogue\Forms\Components\GenericTenantFieldsComponent;
use Eclipse\Catalogue\Models\Category;
use Eclipse\Catalogue\Models\Product;
use Filament\Forms\Components\Placeholder;
use Eclipse\Catalogue\Traits\HandlesTenantData;
use Eclipse\Catalogue\Traits\HasTenantFields;
use Eclipse\World\Models\Country;
use Filament\Forms\Components\RichEditor;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\Select;
Expand All @@ -25,9 +28,11 @@
use Filament\Tables\Actions\ForceDeleteBulkAction;
use Filament\Tables\Actions\RestoreAction;
use Filament\Tables\Actions\RestoreBulkAction;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Filters\TernaryFilter;
use Filament\Tables\Filters\TrashedFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
Expand All @@ -36,7 +41,7 @@

class ProductResource extends Resource implements HasShieldPermissions
{
use Translatable;
use HandlesTenantData, HasTenantFields, Translatable;

protected static ?string $model = Product::class;

Expand Down Expand Up @@ -130,18 +135,52 @@ function ($query) {
->columnSpanFull(),
]),

Section::make('Timestamps')
Section::make(__('eclipse-catalogue::product.sections.additional'))
->schema([
Placeholder::make('created_at')
->label('Created Date')
->content(fn (?Product $record): string => $record?->created_at?->diffForHumans() ?? '-'),
Select::make('origin_country_id')
->label(__('eclipse-catalogue::product.fields.origin_country_id'))
->relationship('originCountry', 'name')
->getOptionLabelFromRecordUsing(fn ($record) => "{$record->id} - {$record->name}")
->searchable(['id', 'name'])
->preload()
->placeholder(__('eclipse-catalogue::product.placeholders.origin_country_id')),
])
->collapsible()
->persistCollapsed(),

Placeholder::make('updated_at')
->label('Last Modified Date')
->content(fn (?Product $record): string => $record?->updated_at?->diffForHumans() ?? '-'),
Section::make(__('eclipse-catalogue::product.sections.seo'))
->description(__('eclipse-catalogue::product.sections.seo_description'))
->schema([
TextInput::make('meta_title')
->label(__('eclipse-catalogue::product.fields.meta_title'))
->maxLength(255)
->placeholder(__('eclipse-catalogue::product.placeholders.meta_title')),

TextInput::make('meta_description')
->label(__('eclipse-catalogue::product.fields.meta_description'))
->maxLength(255)
->placeholder(__('eclipse-catalogue::product.placeholders.meta_description')),
])
->columns(2)
->hidden(fn (?Product $record) => $record === null),
->collapsible()
->persistCollapsed(),

// Tenant settings (embedded in General tab)
GenericTenantFieldsComponent::make(
tenantFlags: ['is_active', 'has_free_delivery'],
mutuallyExclusiveFlagSets: [],
translationPrefix: 'eclipse-catalogue::product',
extraFieldsBuilder: function (int $tenantId, string $tenantName) {
return [
TextInput::make("tenant_data.{$tenantId}.sorting_label")
->label(__('eclipse-catalogue::product.fields.sorting_label'))
->maxLength(255),
\Filament\Forms\Components\DateTimePicker::make("tenant_data.{$tenantId}.available_from_date")
->label(__('eclipse-catalogue::product.fields.available_from_date')),
];
},
sectionTitle: __('eclipse-catalogue::product.sections.tenant_settings'),
sectionDescription: __('eclipse-catalogue::product.sections.tenant_settings_description'),
),
]),

Tabs\Tab::make('Images')
Expand Down Expand Up @@ -206,6 +245,13 @@ public static function table(Table $table): Table
TextColumn::make('type.name')
->label(__('eclipse-catalogue::product.table.columns.type')),

IconColumn::make('is_active')
->label(__('eclipse-catalogue::product.table.columns.is_active'))
->boolean(),

TextColumn::make('originCountry.name')
->label(__('eclipse-catalogue::product.fields.origin_country_id')),

TextColumn::make('short_description')
->words(5),

Expand Down Expand Up @@ -255,6 +301,36 @@ public static function table(Table $table): Table

return $query->pluck('name', 'id')->toArray();
}),
SelectFilter::make('origin_country_id')
->label(__('eclipse-catalogue::product.fields.origin_country_id'))
->multiple()
->options(fn () => Country::query()->orderBy('name')->pluck('name', 'id')->toArray()),
TernaryFilter::make('is_active')
->label(__('eclipse-catalogue::product.table.columns.is_active'))
->queries(
true: function (Builder $query) {
$tenantFK = config('eclipse-catalogue.tenancy.foreign_key');
$currentTenant = \Filament\Facades\Filament::getTenant();

return $query->whereHas('productData', function ($q) use ($tenantFK, $currentTenant) {
$q->where('is_active', true);
if ($tenantFK && $currentTenant) {
$q->where($tenantFK, $currentTenant->id);
}
});
},
false: function (Builder $query) {
$tenantFK = config('eclipse-catalogue.tenancy.foreign_key');
$currentTenant = \Filament\Facades\Filament::getTenant();

return $query->whereHas('productData', function ($q) use ($tenantFK, $currentTenant) {
$q->where('is_active', false);
if ($tenantFK && $currentTenant) {
$q->where($tenantFK, $currentTenant->id);
}
});
},
),
])
->actions([
ActionGroup::make([
Expand Down
Loading