From 90a1fd212e3abdd91c5c7a1d413e9b381775f1a3 Mon Sep 17 00:00:00 2001 From: bernardhanna Date: Wed, 1 Apr 2026 11:44:03 +0100 Subject: [PATCH] qc edits --- app/Nova/Actions/BulkUploadMediaFiles.php | 144 +++--------------- app/Nova/TrainingResource.php | 6 + app/TrainingResource.php | 2 + ...ton_fields_to_training_resources_table.php | 32 ++++ .../views/static/training/index.blade.php | 4 +- resources/views/training/show.blade.php | 31 +++- 6 files changed, 89 insertions(+), 130 deletions(-) create mode 100644 database/migrations/2026_03_19_160000_add_third_button_fields_to_training_resources_table.php diff --git a/app/Nova/Actions/BulkUploadMediaFiles.php b/app/Nova/Actions/BulkUploadMediaFiles.php index e4d3c6de9..2dd8cb8bd 100644 --- a/app/Nova/Actions/BulkUploadMediaFiles.php +++ b/app/Nova/Actions/BulkUploadMediaFiles.php @@ -3,18 +3,15 @@ namespace App\Nova\Actions; use App\MediaUpload; -use DigitalCreative\Filepond\Filepond; -use DigitalCreative\Filepond\Data\Data; use Illuminate\Bus\Queueable; -use Illuminate\Http\UploadedFile; +use Illuminate\Http\Request; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; -use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; use Laravel\Nova\Actions\Action; use Laravel\Nova\Fields\ActionFields; +use Laravel\Nova\Fields\File; use Laravel\Nova\Http\Requests\NovaRequest; class BulkUploadMediaFiles extends Action @@ -37,7 +34,7 @@ class BulkUploadMediaFiles extends Action */ public function handle(ActionFields $fields, Collection $models) { - $uploadedFiles = $this->collectUploadedFiles($fields); + $uploadedFiles = $this->collectUploadedFiles($fields, request()); if (empty($uploadedFiles)) { return Action::danger('Please select one or more files.'); } @@ -56,11 +53,7 @@ public function handle(ActionFields $fields, Collection $models) continue; } - $originalName = $this->extractOriginalName($file); - if ($originalName === null) { - $skipped++; - continue; - } + $originalName = $file->getClientOriginalName(); $extension = strtolower((string) pathinfo($originalName, PATHINFO_EXTENSION)); if (!in_array($extension, $allowedExtensions, true)) { $skipped++; @@ -71,7 +64,7 @@ public function handle(ActionFields $fields, Collection $models) $safeBaseName = Str::slug($baseName) ?: 'upload-file'; $destination = 'nova/uploads/' . now()->format('Y/m'); $finalFileName = $safeBaseName . '-' . Str::random(8) . '.' . $extension; - $storedPath = $this->storeFile($file, $destination, $finalFileName); + $storedPath = $file->storeAs($destination, $finalFileName, 'resources'); if (!$storedPath) { $skipped++; @@ -98,123 +91,34 @@ public function handle(ActionFields $fields, Collection $models) */ public function fields(NovaRequest $request): array { - return [ - Filepond::make('Files', 'files') - ->multiple() - ->limit(50) - ->acceptedTypes('.jpg,.jpeg,.png,.gif,.webp,.svg,.pdf,.doc,.docx,.ppt,.pptx,.xls,.xlsx,.txt') - ->rules('required') - ->help('Drag and drop multiple files or click to select. Supported: images, PDF, Office docs, and TXT.'), - ]; - } - - /** - * Collect uploaded files from Nova action fields + raw request payload. - * - * @return UploadedFile[] - */ - protected function collectUploadedFiles(ActionFields $fields): array - { - $candidates = []; - - if (isset($fields->files)) { - $candidates[] = $fields->files; - } + $fields = []; - $requestItems = request()->collect('files')->all(); - if (!empty($requestItems)) { - $candidates[] = $requestItems; + for ($i = 1; $i <= 20; $i++) { + $fields[] = File::make("File {$i}", "file_{$i}") + ->rules('nullable', 'max:51200', 'mimes:jpg,jpeg,png,gif,webp,svg,pdf,doc,docx,ppt,pptx,xls,xlsx,txt'); } - $requestFiles = request()->allFiles(); - if (!empty($requestFiles)) { - $candidates[] = $requestFiles; - } - - $flatten = function ($value) use (&$flatten): array { - if ($value instanceof UploadedFile) { - return [$value]; - } - - if (is_string($value)) { - return [$value]; - } - - if (!is_array($value)) { - return []; - } - - $result = []; - foreach ($value as $item) { - $result = array_merge($result, $flatten($item)); - } + $fields[0]->help('Upload up to 20 files in one run (mix of images/docs).'); - return $result; - }; - - $files = []; - foreach ($candidates as $candidate) { - $files = array_merge($files, $flatten($candidate)); - } - - return $files; - } - - protected function extractOriginalName(UploadedFile|string $file): ?string - { - if ($file instanceof UploadedFile) { - return $file->getClientOriginalName(); - } - - try { - $data = Data::fromEncrypted($file); - return $data->filename; - } catch (\Throwable $e) { - return null; - } + return $fields; } - protected function storeFile(UploadedFile|string $file, string $destination, string $finalFileName): ?string + /** + * Collect uploaded files from explicit action slots. + * + * @return array + */ + protected function collectUploadedFiles(ActionFields $fields, Request $request): array { - if ($file instanceof UploadedFile) { - return $file->storeAs($destination, $finalFileName, 'resources'); - } - - try { - $data = Data::fromEncrypted($file); - } catch (\Throwable $e) { - return null; - } - - $targetPath = $destination . '/' . $finalFileName; - $stream = Storage::disk($data->disk)->readStream($data->path); - - if (is_resource($stream)) { - try { - $stored = Storage::disk('resources')->writeStream($targetPath, $stream, ['visibility' => 'public']); - } finally { - fclose($stream); - } - } else { - // Some environments return null instead of a stream for temp files. - // Fallback to reading file contents directly. - $contents = Storage::disk($data->disk)->get($data->path); - if (!is_string($contents) || $contents === '') { - Log::warning('[BulkUploadMediaFiles] Unable to read temp file contents', [ - 'disk' => $data->disk, - 'path' => $data->path, - ]); - return null; + $files = []; + for ($i = 1; $i <= 20; $i++) { + $key = "file_{$i}"; + $file = $fields->{$key} ?? $request->file($key); + if ($file) { + $files[] = $file; } - - $stored = Storage::disk('resources')->put($targetPath, $contents, 'public'); } - // Clean only this temp file. Deleting the whole directory can remove - // sibling files from the same multi-upload batch. - $data->deleteFile(); - - return $stored ? $targetPath : null; + return $files; } - } diff --git a/app/Nova/TrainingResource.php b/app/Nova/TrainingResource.php index e54295723..1c5e35bce 100644 --- a/app/Nova/TrainingResource.php +++ b/app/Nova/TrainingResource.php @@ -138,6 +138,12 @@ public function fields(Request $request): array ->nullable() ->rules('nullable', 'url'), + Text::make('Third button text', 'third_button_text')->nullable(), + + Text::make('Third button URL', 'third_button_url') + ->nullable() + ->rules('nullable', 'url'), + Text::make('Meta title', 'meta_title') ->nullable() ->help('Optional HTML title override'), diff --git a/app/TrainingResource.php b/app/TrainingResource.php index dfa6640cb..1c98026a9 100644 --- a/app/TrainingResource.php +++ b/app/TrainingResource.php @@ -32,6 +32,8 @@ class TrainingResource extends Model 'button_url', 'secondary_button_text', 'secondary_button_url', + 'third_button_text', + 'third_button_url', 'meta_title', 'meta_description', 'position', diff --git a/database/migrations/2026_03_19_160000_add_third_button_fields_to_training_resources_table.php b/database/migrations/2026_03_19_160000_add_third_button_fields_to_training_resources_table.php new file mode 100644 index 000000000..f747f9f29 --- /dev/null +++ b/database/migrations/2026_03_19_160000_add_third_button_fields_to_training_resources_table.php @@ -0,0 +1,32 @@ +string('third_button_text')->nullable()->after('secondary_button_url'); + $table->string('third_button_url')->nullable()->after('third_button_text'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('training_resources', function (Blueprint $table) { + $table->dropColumn([ + 'third_button_text', + 'third_button_url', + ]); + }); + } +}; diff --git a/resources/views/static/training/index.blade.php b/resources/views/static/training/index.blade.php index f1a40f075..58fa4b634 100644 --- a/resources/views/static/training/index.blade.php +++ b/resources/views/static/training/index.blade.php @@ -289,7 +289,7 @@ class="absolute top-0 right-0 h-full max-w-[calc(70vw)] object-cover hidden md:b @foreach($dynamicResults as $result)
- +

@@ -306,7 +306,7 @@ class="absolute top-0 right-0 h-full max-w-[calc(70vw)] object-cover hidden md:b @foreach($results as $result)

- +

diff --git a/resources/views/training/show.blade.php b/resources/views/training/show.blade.php index 373bc6adc..9697aecf8 100644 --- a/resources/views/training/show.blade.php +++ b/resources/views/training/show.blade.php @@ -88,14 +88,29 @@ class="mb-12 w-full h-full max-h-[630px] object-contain" @endif @if(!empty($trainingResource->button_text) && !empty($trainingResource->button_url)) - - {{ $trainingResource->button_text }} - +

+ + {{ $trainingResource->button_text }} + + + @if(!empty($trainingResource->third_button_text) && !empty($trainingResource->third_button_url)) + + @endif +
@endif @if(!empty($trainingResource->contacts_section))