From add04c2bbfd07a62b5d80f6a5d44edf88c84d57f Mon Sep 17 00:00:00 2001 From: bernardhanna Date: Mon, 30 Mar 2026 15:32:40 +0100 Subject: [PATCH] upload images to nova --- app/Nova/Actions/BulkUploadMediaFiles.php | 119 ++++++++++++++++++++++ app/Nova/MediaUpload.php | 18 ++++ app/School.php | 7 ++ 3 files changed, 144 insertions(+) create mode 100644 app/Nova/Actions/BulkUploadMediaFiles.php diff --git a/app/Nova/Actions/BulkUploadMediaFiles.php b/app/Nova/Actions/BulkUploadMediaFiles.php new file mode 100644 index 000000000..52afb53c5 --- /dev/null +++ b/app/Nova/Actions/BulkUploadMediaFiles.php @@ -0,0 +1,119 @@ +files ?? request()->file('files'); + if (!$uploadedFiles) { + return Action::danger('Please select one or more files.'); + } + + if (!is_array($uploadedFiles)) { + $uploadedFiles = [$uploadedFiles]; + } + + $allowedExtensions = [ + 'jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', + 'pdf', 'doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx', 'txt', + ]; + + $uploaded = 0; + $skipped = 0; + + foreach ($uploadedFiles as $file) { + if (!$file) { + $skipped++; + continue; + } + + $extension = strtolower((string) $file->getClientOriginalExtension()); + if (!in_array($extension, $allowedExtensions, true)) { + $skipped++; + continue; + } + + $baseName = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME); + $safeBaseName = Str::slug($baseName) ?: 'upload-file'; + $destination = 'nova/uploads/' . now()->format('Y/m'); + $storedPath = $file->storeAs( + $destination, + $safeBaseName . '-' . Str::random(8) . '.' . $extension, + 'resources' + ); + + if (!$storedPath) { + $skipped++; + continue; + } + + MediaUpload::create([ + 'title' => trim(str_replace(['-', '_'], ' ', $baseName)), + 'file_path' => $storedPath, + 'disk' => 'resources', + ]); + + $uploaded++; + } + + return Action::message("Bulk upload completed: {$uploaded} uploaded, {$skipped} skipped."); + } + + /** + * Get the fields available on the action. + * + * @param \Laravel\Nova\Http\Requests\NovaRequest $request + * @return array + */ + public function fields(NovaRequest $request): array + { + return [ + File::make('Files', 'files') + ->withMeta([ + 'extraAttributes' => [ + 'multiple' => true, + 'accept' => '.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.'), + ]; + } + + /** + * Indicate that this action can be run without any models. + */ + public function standalone() + { + return true; + } +} diff --git a/app/Nova/MediaUpload.php b/app/Nova/MediaUpload.php index 3cc7b83e3..c260d9a05 100644 --- a/app/Nova/MediaUpload.php +++ b/app/Nova/MediaUpload.php @@ -2,10 +2,12 @@ namespace App\Nova; +use App\Nova\Actions\BulkUploadMediaFiles; use Illuminate\Http\Request; use Laravel\Nova\Fields\File; use Laravel\Nova\Fields\ID; use Laravel\Nova\Fields\Text; +use Laravel\Nova\Http\Requests\NovaRequest; class MediaUpload extends Resource { @@ -40,6 +42,10 @@ public function fields(Request $request): array ->nullable() ->help('Optional label to help identify this file in Nova.'), + Text::make('File Name', function () { + return basename((string) $this->file_path); + })->onlyOnIndex(), + File::make('File', 'file_path') ->disk('resources') ->path('nova/uploads') @@ -65,4 +71,16 @@ public function fields(Request $request): array })->onlyOnIndex(), ]; } + + public static function indexQuery(NovaRequest $request, $query) + { + return $query->orderBy('created_at', 'desc'); + } + + public function actions(Request $request): array + { + return [ + new BulkUploadMediaFiles, + ]; + } } diff --git a/app/School.php b/app/School.php index 7d21ec019..9d83ec09c 100644 --- a/app/School.php +++ b/app/School.php @@ -4,6 +4,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; +use Spatie\Activitylog\LogOptions; use Spatie\Activitylog\Traits\LogsActivity; /** @@ -49,6 +50,12 @@ public function getDescriptionForEvent(string $eventName): string protected static $logFillable = true; + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->setDescriptionForEvent(fn (string $eventName) => "School {$this->id} has been {$eventName}"); + } + public function path() { return '/school/'.$this->id;