diff --git a/resources/views/filament/resources/product-resource/pages/list-products-footer.blade.php b/resources/views/filament/resources/product-resource/pages/list-products-footer.blade.php deleted file mode 100644 index d4be8f0..0000000 --- a/resources/views/filament/resources/product-resource/pages/list-products-footer.blade.php +++ /dev/null @@ -1,311 +0,0 @@ -
- -
- -
- - -
- -
-

-

-
-
- -
-
-
- - - - \ No newline at end of file diff --git a/src/Filament/Forms/Components/ImageManager.php b/src/Filament/Forms/Components/ImageManager.php index 8e32e67..6a862c7 100644 --- a/src/Filament/Forms/Components/ImageManager.php +++ b/src/Filament/Forms/Components/ImageManager.php @@ -11,6 +11,7 @@ use Filament\Forms\Components\TextInput; use Filament\Notifications\Notification; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Str; use Spatie\MediaLibrary\MediaCollections\Models\Media; class ImageManager extends Field @@ -84,7 +85,7 @@ protected function setUp(): void $tempPath = storage_path('app/public/'.$item['temp_file']); if (file_exists($tempPath)) { $record->addMedia($tempPath) - ->usingFileName($item['file_name'] ?? basename($tempPath)) + ->usingFileName($this->sanitizeFilename($item['file_name'] ?? basename($tempPath))) ->withCustomProperties([ 'name' => $item['name'] ?? [], 'description' => $item['description'] ?? [], @@ -98,7 +99,7 @@ protected function setUp(): void } elseif (isset($item['temp_url'])) { try { $record->addMediaFromUrl($item['temp_url']) - ->usingFileName($item['file_name'] ?? basename($item['temp_url'])) + ->usingFileName($this->sanitizeFilename($item['file_name'] ?? basename($item['temp_url']))) ->withCustomProperties([ 'name' => $item['name'] ?? [], 'description' => $item['description'] ?? [], @@ -213,7 +214,7 @@ public function getUploadAction(): Action if (file_exists($fullPath)) { $tempId = 'temp_'.uniqid(); - $fileName = basename($filePath); + $fileName = $this->sanitizeFilename(basename($filePath)); $currentState[] = [ 'id' => null, @@ -256,7 +257,7 @@ public function getUploadAction(): Action if (file_exists($fullPath)) { $record->addMedia($fullPath) - ->usingFileName(basename($filePath)) + ->usingFileName($this->sanitizeFilename(basename($filePath))) ->withCustomProperties([ 'name' => [], 'description' => [], @@ -331,7 +332,7 @@ public function getUrlUploadAction(): Action 'description' => [], 'is_cover' => count($currentState) === 0, 'position' => ++$maxPosition, - 'file_name' => basename($url), + 'file_name' => $this->sanitizeFilename(basename($url)), 'mime_type' => 'image/*', 'size' => 0, ]; @@ -694,6 +695,21 @@ protected function ensureSingleCoverImage(Model $record): void } } + protected function sanitizeFilename(string $filename): string + { + $pathInfo = pathinfo($filename); + $name = $pathInfo['filename'] ?? 'image'; + $extension = isset($pathInfo['extension']) ? '.'.$pathInfo['extension'] : ''; + + $sanitizedName = Str::slug($name, '-'); + + if (empty($sanitizedName)) { + $sanitizedName = 'image-'.time(); + } + + return $sanitizedName.$extension; + } + protected function cleanupOldTempFiles(): void { $tempDir = storage_path('app/public/temp-images'); diff --git a/src/Filament/Resources/ProductResource.php b/src/Filament/Resources/ProductResource.php index 6af21e6..39474d8 100644 --- a/src/Filament/Resources/ProductResource.php +++ b/src/Filament/Resources/ProductResource.php @@ -4,6 +4,7 @@ use BezhanSalleh\FilamentShield\Contracts\HasShieldPermissions; use Eclipse\Catalogue\Enums\PropertyInputType; +use Eclipse\Catalogue\Enums\PropertyType; use Eclipse\Catalogue\Filament\Filters\CustomPropertyConstraint; use Eclipse\Catalogue\Filament\Forms\Components\ImageManager; use Eclipse\Catalogue\Filament\Resources\ProductResource\Pages; @@ -16,8 +17,11 @@ use Eclipse\Catalogue\Models\Property; use Eclipse\Catalogue\Traits\HandlesTenantData; use Eclipse\Catalogue\Traits\HasTenantFields; +use Eclipse\Common\Filament\Tables\Columns\ImageColumn; +use Eclipse\Common\Helpers\MediaHelper; use Eclipse\World\Models\Country; use Eclipse\World\Models\TariffCode; +use Filament\Facades\Filament; use Filament\Forms\Components\CheckboxList; use Filament\Forms\Components\Placeholder; use Filament\Forms\Components\Radio; @@ -44,7 +48,6 @@ 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\QueryBuilder; use Filament\Tables\Filters\SelectFilter; @@ -263,7 +266,7 @@ public static function form(Form $form): Form 'name', function ($query) { $tenantFK = config('eclipse-catalogue.tenancy.foreign_key'); - $currentTenant = \Filament\Facades\Filament::getTenant(); + $currentTenant = Filament::getTenant(); if ($tenantFK && $currentTenant) { return $query->whereHas('productTypeData', function ($q) use ($tenantFK, $currentTenant) { @@ -569,40 +572,26 @@ public static function table(Table $table): Table ->columns([ TextColumn::make('id'), - ImageColumn::make('cover_image') + ImageColumn::make('images') + ->label('Images') + ->preview() + ->circular() ->stacked() - ->label('Image') - ->getStateUsing(function (Product $record) { - $url = $record->getFirstMediaUrl('images', 'thumb'); + ->getStateUsing(function (Product $record): array { + $images = $record->getMedia('images'); - return $url ?: null; - }) - ->circular() - ->defaultImageUrl(static::getPlaceholderImageUrl()) - ->extraImgAttributes(function (Product $record) { - $coverMedia = $record->getMedia('images') - ->filter(fn ($media) => $media->getCustomProperty('is_cover', false)) - ->first(); - - if (! $coverMedia) { - $coverMedia = $record->getMedia('images')->first(); + if ($images->isEmpty()) { + if (class_exists(MediaHelper::class)) { + return [MediaHelper::getPlaceholderImageUrl()]; + } + + return []; } - $fullImageUrl = $coverMedia ? $coverMedia->getUrl() : null; - $imageName = $coverMedia ? json_encode($coverMedia->getCustomProperty('name', [])) : '{}'; - $imageDescription = $coverMedia ? json_encode($coverMedia->getCustomProperty('description', [])) : '{}'; - - return [ - 'class' => 'cursor-pointer product-image-trigger', - 'data-url' => $fullImageUrl ?: static::getPlaceholderImageUrl(), - 'data-image-name' => htmlspecialchars($imageName, ENT_QUOTES, 'UTF-8'), - 'data-image-description' => htmlspecialchars($imageDescription, ENT_QUOTES, 'UTF-8'), - 'data-product-name' => htmlspecialchars(json_encode($record->getTranslations('name')), ENT_QUOTES, 'UTF-8'), - 'data-product-code' => $record->code ?: '', - 'data-filename' => $coverMedia ? $coverMedia->file_name : '', - 'onclick' => 'event.stopPropagation(); return false;', - ]; - }), + return $images->map(fn ($media) => $media->getUrl())->toArray(); + }) + ->title(fn (Product $record) => "{$record->name} - Product Images") + ->link(fn (Product $record) => ProductResource::getUrl('edit', ['record' => $record])), TextColumn::make('name') ->toggleable(false), @@ -612,7 +601,7 @@ public static function table(Table $table): Table ->badge() ->getStateUsing(function (Product $record) { $tenantFK = config('eclipse-catalogue.tenancy.foreign_key'); - $currentTenant = \Filament\Facades\Filament::getTenant(); + $currentTenant = Filament::getTenant(); $status = null; @@ -633,7 +622,7 @@ public static function table(Table $table): Table }) ->color(function (Product $record) { $tenantFK = config('eclipse-catalogue.tenancy.foreign_key'); - $currentTenant = \Filament\Facades\Filament::getTenant(); + $currentTenant = Filament::getTenant(); $status = null; if ($record->relationLoaded('productData')) { @@ -649,7 +638,7 @@ public static function table(Table $table): Table }) ->extraAttributes(function (Product $record) { $tenantFK = config('eclipse-catalogue.tenancy.foreign_key'); - $currentTenant = \Filament\Facades\Filament::getTenant(); + $currentTenant = Filament::getTenant(); $status = null; if ($record->relationLoaded('productData')) { @@ -687,7 +676,7 @@ public static function table(Table $table): Table ->limit(3) ->toggleable() ->getStateUsing(function (Product $record) { - $currentTenant = \Filament\Facades\Filament::getTenant(); + $currentTenant = Filament::getTenant(); $tenantFK = config('eclipse-catalogue.tenancy.foreign_key', 'site_id'); if ($currentTenant) { @@ -759,7 +748,7 @@ public static function table(Table $table): Table ->options(function () { $query = ProductStatus::query(); $tenantFK = config('eclipse-catalogue.tenancy.foreign_key'); - $currentTenant = \Filament\Facades\Filament::getTenant(); + $currentTenant = Filament::getTenant(); if ($tenantFK && $currentTenant) { $query->where($tenantFK, $currentTenant->id); } @@ -776,7 +765,7 @@ public static function table(Table $table): Table return; } $tenantFK = config('eclipse-catalogue.tenancy.foreign_key'); - $currentTenant = \Filament\Facades\Filament::getTenant(); + $currentTenant = Filament::getTenant(); $query->whereHas('productData', function ($q) use ($selected, $tenantFK, $currentTenant) { if ($tenantFK && $currentTenant) { $q->where($tenantFK, $currentTenant->id); @@ -794,7 +783,7 @@ public static function table(Table $table): Table return; } $tenantFK = config('eclipse-catalogue.tenancy.foreign_key'); - $currentTenant = \Filament\Facades\Filament::getTenant(); + $currentTenant = Filament::getTenant(); $query->whereHas('productData', function ($q) use ($selected, $tenantFK, $currentTenant) { if ($tenantFK && $currentTenant) { $q->where($tenantFK, $currentTenant->id); @@ -807,7 +796,7 @@ public static function table(Table $table): Table ->multiple() ->options(function () { $tenantFK = config('eclipse-catalogue.tenancy.foreign_key'); - $currentTenant = \Filament\Facades\Filament::getTenant(); + $currentTenant = Filament::getTenant(); $query = \Eclipse\Catalogue\Models\ProductType::query(); @@ -854,7 +843,7 @@ public static function table(Table $table): Table ->label('Groups') ->multiple() ->relationship('groups', 'name', function ($query) { - $currentTenant = \Filament\Facades\Filament::getTenant(); + $currentTenant = Filament::getTenant(); $tenantFK = config('eclipse-catalogue.tenancy.foreign_key', 'site_id'); if ($currentTenant) { return $query->where($tenantFK, $currentTenant->id); @@ -867,7 +856,7 @@ public static function table(Table $table): Table ->queries( true: function (Builder $query) { $tenantFK = config('eclipse-catalogue.tenancy.foreign_key'); - $currentTenant = \Filament\Facades\Filament::getTenant(); + $currentTenant = Filament::getTenant(); return $query->whereHas('productData', function ($q) use ($tenantFK, $currentTenant) { $q->where('is_active', true); @@ -878,7 +867,7 @@ public static function table(Table $table): Table }, false: function (Builder $query) { $tenantFK = config('eclipse-catalogue.tenancy.foreign_key'); - $currentTenant = \Filament\Facades\Filament::getTenant(); + $currentTenant = Filament::getTenant(); return $query->whereHas('productData', function ($q) use ($tenantFK, $currentTenant) { $q->where('is_active', false); @@ -987,7 +976,7 @@ public static function getEloquentQuery(): Builder ]); $tenantFK = config('eclipse-catalogue.tenancy.foreign_key'); - $currentTenant = \Filament\Facades\Filament::getTenant(); + $currentTenant = Filament::getTenant(); if ($tenantFK && $currentTenant) { $query->with(['productData' => function ($q) use ($tenantFK, $currentTenant) { @@ -1034,13 +1023,6 @@ public static function getGlobalSearchEloquentQuery(): Builder ->with(['customPropertyValues.property']); } - protected static function getPlaceholderImageUrl(): string - { - $svg = view('eclipse-catalogue::components.placeholder-image')->render(); - - return 'data:image/svg+xml;base64,'.base64_encode($svg); - } - public static function getPermissionPrefixes(): array { return [ @@ -1059,7 +1041,17 @@ public static function getPermissionPrefixes(): array protected static function getCustomPropertyColumns(): array { - $customProperties = Property::where('type', 'custom')->get(); + try { + // Check if the type column exists before querying + $customProperties = Property::where('type', PropertyType::CUSTOM->value)->get(); + } catch (\Illuminate\Database\QueryException $e) { + // If type column doesn't exist, return empty array + if (str_contains($e->getMessage(), "Unknown column 'type'")) { + return []; + } + throw $e; + } + $columns = []; foreach ($customProperties as $property) { @@ -1094,10 +1086,19 @@ protected static function getCustomPropertyColumns(): array protected static function getCustomPropertyConstraints(): array { $constraints = []; - $customProperties = Property::where('is_active', true) - ->where('type', 'custom') - ->where('input_type', '!=', 'file') - ->get(); + + try { + $customProperties = Property::where('is_active', true) + ->where('type', PropertyType::CUSTOM->value) + ->where('input_type', '!=', 'file') + ->get(); + } catch (\Illuminate\Database\QueryException $e) { + // If type column doesn't exist, return empty array + if (str_contains($e->getMessage(), "Unknown column 'type'")) { + return []; + } + throw $e; + } foreach ($customProperties as $property) { $constraints[] = CustomPropertyConstraint::forProperty($property); diff --git a/src/Filament/Resources/ProductResource/Pages/ListProducts.php b/src/Filament/Resources/ProductResource/Pages/ListProducts.php index 2b41b61..6378eca 100644 --- a/src/Filament/Resources/ProductResource/Pages/ListProducts.php +++ b/src/Filament/Resources/ProductResource/Pages/ListProducts.php @@ -8,7 +8,6 @@ use Filament\Resources\Pages\ListRecords; use Filament\Resources\Pages\ListRecords\Concerns\Translatable; use Filament\Support\Enums\MaxWidth; -use Illuminate\Contracts\View\View; class ListProducts extends ListRecords { @@ -25,9 +24,4 @@ protected function getHeaderActions(): array Actions\CreateAction::make(), ]; } - - public function getFooter(): ?View - { - return view('eclipse-catalogue::filament.resources.product-resource.pages.list-products-footer'); - } }