diff --git a/app/Console/Commands/MoveMediaToPublic.php b/app/Console/Commands/MoveMediaToPublic.php
new file mode 100644
index 0000000..bce2727
--- /dev/null
+++ b/app/Console/Commands/MoveMediaToPublic.php
@@ -0,0 +1,112 @@
+option('dry-run');
+
+ if ($dryRun) {
+ $this->warn('DRY RUN MODE - No files will actually be moved');
+ }
+
+ // Get all media records using local disk
+ $mediaRecords = Media::where('disk', 'local')->get();
+
+ if ($mediaRecords->isEmpty()) {
+ $this->info('No media records found using local disk.');
+ return self::SUCCESS;
+ }
+
+ $this->info("Found {$mediaRecords->count()} media records to migrate.");
+
+ $progressBar = $this->output->createProgressBar($mediaRecords->count());
+ $progressBar->start();
+
+ $moved = 0;
+ $errors = 0;
+
+ foreach ($mediaRecords as $media) {
+ // Use relative path: {id}/{filename}
+ $relativePath = $media->id . '/' . $media->file_name;
+
+ // Check if source file exists
+ if (!Storage::disk('local')->exists($relativePath)) {
+ $this->newLine();
+ $this->error("Source file not found: {$relativePath}");
+ $errors++;
+ $progressBar->advance();
+ continue;
+ }
+
+ try {
+ if (!$dryRun) {
+ // Copy file from local to public disk
+ $fileContent = Storage::disk('local')->get($relativePath);
+ Storage::disk('public')->put($relativePath, $fileContent);
+
+ // Verify the file was copied successfully
+ if (Storage::disk('public')->exists($relativePath)) {
+ // Update the database record
+ $media->update([
+ 'disk' => 'public',
+ 'conversions_disk' => 'public',
+ ]);
+
+ // Delete the old file from local disk
+ Storage::disk('local')->delete($relativePath);
+
+ $moved++;
+ } else {
+ throw new \Exception("Failed to copy file to public disk");
+ }
+ } else {
+ $this->newLine();
+ $this->line("Would move: local:{$relativePath} -> public:{$relativePath}");
+ $moved++;
+ }
+ } catch (\Exception $e) {
+ $this->newLine();
+ $this->error("Error moving {$relativePath}: {$e->getMessage()}");
+ $errors++;
+ }
+
+ $progressBar->advance();
+ }
+
+ $progressBar->finish();
+ $this->newLine(2);
+
+ if ($dryRun) {
+ $this->info("DRY RUN: Would move {$moved} files, {$errors} errors encountered.");
+ $this->info("Run without --dry-run to actually perform the migration.");
+ } else {
+ $this->info("Successfully moved {$moved} files, {$errors} errors encountered.");
+ }
+
+ return self::SUCCESS;
+ }
+}
diff --git a/app/Filament/Resources/Entries/Schemas/EntryForm.php b/app/Filament/Resources/Entries/Schemas/EntryForm.php
index 82e163d..6c029f6 100644
--- a/app/Filament/Resources/Entries/Schemas/EntryForm.php
+++ b/app/Filament/Resources/Entries/Schemas/EntryForm.php
@@ -2,13 +2,18 @@
namespace App\Filament\Resources\Entries\Schemas;
+use Filament\Actions\Action;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\RichEditor;
-use Filament\Forms\Components\TextInput;
+use Filament\Forms\Components\Select;
+use Filament\Forms\Components\SpatieMediaLibraryFileUpload;
use Filament\Forms\Components\Textarea;
+use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Schemas\Schema;
+use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
+use Spatie\MediaLibrary\MediaCollections\Models\Media;
class EntryForm
{
@@ -18,22 +23,183 @@ class EntryForm
->components([
TextInput::make('title')
->required()
- ->reactive()
+ ->live(onBlur: true)
->afterStateUpdated(function ($state, $set): void {
$set('slug', Str::slug((string) $state));
}),
TextInput::make('slug')
->required()
- ->disabled(),
+ ->dehydrated()
+ ->readOnly(),
Textarea::make('description')
->columnSpanFull(),
+ SpatieMediaLibraryFileUpload::make('featured_image')
+ ->collection('featured-image')
+ ->image()
+ ->imageEditor()
+ ->disk('public')
+ ->visibility('public')
+ ->columnSpanFull()
+ ->dehydrated(false)
+ ->hintAction(
+ Action::make('featured_picker')
+ ->label('Pick from Gallery')
+ ->icon('heroicon-m-photo')
+ ->schema([
+ Select::make('image_id')
+ ->label('Select an existing image')
+ ->allowHtml()
+ ->options(function () {
+ return Media::where('model_type', 'temp')
+ ->where('model_id', 0)
+ ->where('disk', 'public')
+ ->latest()
+ ->limit(30)
+ ->get(['id', 'file_name', 'name', 'disk'])
+ ->mapWithKeys(function (Media $item) {
+ try {
+ $url = $item->getUrl();
+ $fileName = e($item->file_name);
+ $name = e($item->name ?? '');
+
+ $html = "
" .
+ "

" .
+ "
" .
+ "{$name}" .
+ "{$fileName}" .
+ "
";
+
+ return [$item->id => $html];
+ } catch (\Exception $e) {
+ return [];
+ }
+ })->toArray();
+ })
+ ->searchable()
+ ->preload()
+ ->required(),
+ ])
+ ->action(function (array $data, SpatieMediaLibraryFileUpload $component): void {
+ $record = $component->getRecord();
+
+ if (!$record) {
+ \Filament\Notifications\Notification::make()
+ ->warning()
+ ->title('Save the entry first')
+ ->send();
+ return;
+ }
+
+ if (!$data['image_id']) {
+ return;
+ }
+
+ $sourceMedia = Media::find($data['image_id']);
+ if (!$sourceMedia || !file_exists($sourceMedia->getPath())) {
+ \Filament\Notifications\Notification::make()
+ ->danger()
+ ->title('Image file not found')
+ ->send();
+ return;
+ }
+
+ $sourceFile = $sourceMedia->getPath();
+ $tempCopy = sys_get_temp_dir() . '/' . uniqid() . '_' . $sourceMedia->file_name;
+ copy($sourceFile, $tempCopy);
+
+ try {
+ // Verify record has ID
+ if (!$record->id) {
+ \Filament\Notifications\Notification::make()
+ ->danger()
+ ->title('Entry must be saved first')
+ ->send();
+ return;
+ }
+
+ // Add the copy to the entry's featured-image collection
+ $newMedia = $record->addMedia($tempCopy)
+ ->usingName($sourceMedia->name ?: pathinfo($sourceMedia->file_name, PATHINFO_FILENAME))
+ ->usingFileName($sourceMedia->file_name)
+ ->toMediaCollection('featured-image', 'public');
+
+ // Dispatch event for app.js to handle
+ $component->getLivewire()->dispatch('featured-image-added', ['mediaId' => $newMedia->id]);
+
+ \Filament\Notifications\Notification::make()
+ ->success()
+ ->title('Image added to featured image')
+ ->send();
+ } catch (\Exception $e) {
+ \Filament\Notifications\Notification::make()
+ ->danger()
+ ->title('Error: ' . $e->getMessage())
+ ->send();
+ } finally {
+ if (file_exists($tempCopy)) {
+ unlink($tempCopy);
+ }
+ }
+ })
+ ),
Toggle::make('is_published')
->required(),
Toggle::make('is_featured')
->required(),
DatePicker::make('published_at'),
RichEditor::make('content')
- ->columnSpanFull(),
+ ->columnSpanFull()
+ ->hintAction(
+ Action::make('picker')
+ ->label('Gallery Picker')
+ ->icon('heroicon-m-photo')
+ ->schema([
+ Select::make('image_url')
+ ->label('Select an existing image')
+ ->allowHtml()
+ ->options(function () {
+ return Media::latest()
+ ->limit(30) // Limit to 30 most recent items for performance
+ ->get(['id', 'file_name', 'name', 'uuid', 'collection_name', 'model_type', 'model_id', 'disk'])
+ ->filter(function (Media $item) {
+ // Only include media items that have a valid disk
+ return $item->disk !== null;
+ })
+ ->mapWithKeys(function (Media $item) {
+ try {
+ $url = $item->getUrl();
+ } catch (\Exception $e) {
+ // Skip items that can't generate URLs
+ return [];
+ }
+
+ $fileName = e($item->file_name);
+ $name = e($item->name ?? '');
+
+ // Smaller image preview for better performance
+ $html = "" .
+ "

" .
+ "
" .
+ "{$name}" .
+ "{$fileName}" .
+ "
";
+
+ return [$url => $html];
+ })->toArray();
+ })
+ ->searchable()
+ ->preload()
+ ->required(),
+ ])
+ ->action(function (array $data, RichEditor $component) {
+ // We dispatch the URL to the browser to be inserted into TipTap
+ $component->getLivewire()->dispatch('insert-editor-content', [
+ 'statePath' => $component->getStatePath(),
+ 'html' => "
",
+ ]);
+ })
+ ),
+
]);
}
}
diff --git a/app/Filament/Resources/Entries/Schemas/EntryInfolist.php b/app/Filament/Resources/Entries/Schemas/EntryInfolist.php
index 6a5d87d..be741bc 100644
--- a/app/Filament/Resources/Entries/Schemas/EntryInfolist.php
+++ b/app/Filament/Resources/Entries/Schemas/EntryInfolist.php
@@ -3,6 +3,7 @@
namespace App\Filament\Resources\Entries\Schemas;
use Filament\Infolists\Components\IconEntry;
+use Filament\Infolists\Components\SpatieMediaLibraryImageEntry;
use Filament\Infolists\Components\TextEntry;
use Filament\Schemas\Schema;
@@ -17,6 +18,9 @@ class EntryInfolist
TextEntry::make('description')
->placeholder('-')
->columnSpanFull(),
+ SpatieMediaLibraryImageEntry::make('featured_image')
+ ->collection('featured-image')
+ ->columnSpanFull(),
IconEntry::make('is_published')
->boolean(),
IconEntry::make('is_featured')
diff --git a/app/Filament/Resources/Entries/Tables/EntriesTable.php b/app/Filament/Resources/Entries/Tables/EntriesTable.php
index 57035c0..eb89fd4 100644
--- a/app/Filament/Resources/Entries/Tables/EntriesTable.php
+++ b/app/Filament/Resources/Entries/Tables/EntriesTable.php
@@ -7,6 +7,7 @@ use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Actions\ViewAction;
use Filament\Tables\Columns\IconColumn;
+use Filament\Tables\Columns\SpatieMediaLibraryImageColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
@@ -16,6 +17,11 @@ class EntriesTable
{
return $table
->columns([
+ SpatieMediaLibraryImageColumn::make('featured_image')
+ ->collection('featured-image')
+ ->circular()
+ ->stacked()
+ ->limit(3),
TextColumn::make('title')
->searchable(),
TextColumn::make('slug')
diff --git a/app/Filament/Resources/Media/MediaResource.php b/app/Filament/Resources/Media/MediaResource.php
new file mode 100644
index 0000000..2ac19fe
--- /dev/null
+++ b/app/Filament/Resources/Media/MediaResource.php
@@ -0,0 +1,58 @@
+ ListMedia::route('/'),
+ 'create' => CreateMedia::route('/create'),
+ 'view' => ViewMedia::route('/{record}'),
+ 'edit' => EditMedia::route('/{record}/edit'),
+ ];
+ }
+}
diff --git a/app/Filament/Resources/Media/Pages/CreateMedia.php b/app/Filament/Resources/Media/Pages/CreateMedia.php
new file mode 100644
index 0000000..c652a96
--- /dev/null
+++ b/app/Filament/Resources/Media/Pages/CreateMedia.php
@@ -0,0 +1,61 @@
+uploadedFile = $file;
+
+ // Set required fields for Media model
+ $data['model_type'] = $data['model_type'] ?? 'temp';
+ $data['model_id'] = $data['model_id'] ?? 0;
+ $data['collection_name'] = $data['collection_name'] ?? 'default';
+ $data['disk'] = $data['disk'] ?? 'public';
+ $data['file_name'] = $file ? basename($file) : '';
+ $data['mime_type'] = $file && Storage::disk('public')->exists($file)
+ ? Storage::disk('public')->mimeType($file)
+ : 'application/octet-stream';
+ $data['size'] = $file && Storage::disk('public')->exists($file)
+ ? Storage::disk('public')->size($file)
+ : 0;
+ $data['manipulations'] = [];
+ $data['custom_properties'] = [];
+ $data['generated_conversions'] = [];
+ $data['responsive_images'] = [];
+
+ return $data;
+ }
+
+ protected function afterCreate(): void
+ {
+ if ($this->uploadedFile && $this->record) {
+ $disk = Storage::disk('public');
+
+ // Create the directory for this media ID (Spatie structure: {id}/{filename})
+ $mediaDirectory = (string) $this->record->id;
+ $disk->makeDirectory($mediaDirectory);
+
+ // Move file from temporary upload location to Spatie's expected location
+ if ($disk->exists($this->uploadedFile)) {
+ $newPath = $mediaDirectory.'/'.$this->record->file_name;
+ $disk->move($this->uploadedFile, $newPath);
+ }
+ }
+ }
+}
diff --git a/app/Filament/Resources/Media/Pages/EditMedia.php b/app/Filament/Resources/Media/Pages/EditMedia.php
new file mode 100644
index 0000000..b6f4cdb
--- /dev/null
+++ b/app/Filament/Resources/Media/Pages/EditMedia.php
@@ -0,0 +1,72 @@
+record->getPathRelativeToRoot()) {
+ $this->uploadedFile = $file;
+
+ // Keep the original file_name to prevent breaking existing references
+ // $data['file_name'] is not updated - we preserve the original filename
+ $data['mime_type'] = Storage::disk('public')->exists($file)
+ ? Storage::disk('public')->mimeType($file)
+ : 'application/octet-stream';
+ $data['size'] = Storage::disk('public')->exists($file)
+ ? Storage::disk('public')->size($file)
+ : 0;
+ }
+
+ return $data;
+ }
+
+ protected function afterSave(): void
+ {
+ if ($this->uploadedFile && $this->record) {
+ $disk = Storage::disk('public');
+ $mediaDirectory = (string) $this->record->id;
+
+ // Delete old file if it exists
+ $oldPath = $mediaDirectory.'/'.$this->record->getOriginal('file_name');
+ if ($disk->exists($oldPath)) {
+ $disk->delete($oldPath);
+ }
+
+ // Move new file to Spatie's expected location using the original filename
+ if ($disk->exists($this->uploadedFile)) {
+ $disk->makeDirectory($mediaDirectory);
+ // Use the original file_name to preserve existing references
+ $newPath = $mediaDirectory.'/'.$this->record->file_name;
+ $disk->move($this->uploadedFile, $newPath);
+ }
+
+ // Redirect to the same page to refresh the form state
+ $this->redirect(static::getUrl(['record' => $this->record]), navigate: true);
+ }
+ }
+}
diff --git a/app/Filament/Resources/Media/Pages/ListMedia.php b/app/Filament/Resources/Media/Pages/ListMedia.php
new file mode 100644
index 0000000..2e6b63d
--- /dev/null
+++ b/app/Filament/Resources/Media/Pages/ListMedia.php
@@ -0,0 +1,19 @@
+components([
+ TextInput::make('name')
+ ->required()
+ ->maxLength(255),
+ TextInput::make('collection_name')
+ ->default('default')
+ ->required()
+ ->maxLength(255),
+ Hidden::make('disk')
+ ->default('public'),
+ FileUpload::make('file')
+ ->label('File')
+ ->imageEditor()
+ ->imageEditorAspectRatios([
+ '16:9',
+ '4:3',
+ '1:1',
+ ])
+ ->columnSpanFull()
+ ->disk('public')
+ ->directory('media')
+ ->visibility('public')
+ ->acceptedFileTypes(['image/*', 'application/pdf'])
+ ->maxSize(10240)
+ ->required(fn ($context) => $context === 'create')
+ ->afterStateHydrated(function (FileUpload $component, $state, $record): void {
+ if (! $record) {
+ return;
+ }
+
+ $media = $record;
+
+ if (! $media instanceof SpatieMedia) {
+ return;
+ }
+
+ // Construct the correct path: {media_id}/{filename}
+ $path = $media->id.'/'.$media->file_name;
+
+ $component->state($path);
+ }),
+ ]);
+ }
+}
diff --git a/app/Filament/Resources/Media/Schemas/MediaInfolist.php b/app/Filament/Resources/Media/Schemas/MediaInfolist.php
new file mode 100644
index 0000000..dfe4801
--- /dev/null
+++ b/app/Filament/Resources/Media/Schemas/MediaInfolist.php
@@ -0,0 +1,36 @@
+components([
+ ImageEntry::make('file_name')
+ ->label('Preview')
+ ->getStateUsing(fn ($record) => $record->getUrl())
+ ->visible(fn ($record) => $record->mime_type && str_starts_with($record->mime_type, 'image/')),
+ TextEntry::make('name'),
+ TextEntry::make('file_name'),
+ TextEntry::make('mime_type'),
+ TextEntry::make('collection_name'),
+ TextEntry::make('size')
+ ->formatStateUsing(fn ($state) => number_format($state / 1024, 2).' KB'),
+ TextEntry::make('model_type')
+ ->label('Attached to Model'),
+ TextEntry::make('model_id'),
+ TextEntry::make('custom_properties')
+ ->formatStateUsing(fn ($state) => json_encode($state, JSON_PRETTY_PRINT)),
+ TextEntry::make('created_at')
+ ->dateTime(),
+ TextEntry::make('updated_at')
+ ->dateTime(),
+ ]);
+ }
+}
diff --git a/app/Filament/Resources/Media/Tables/MediaTable.php b/app/Filament/Resources/Media/Tables/MediaTable.php
new file mode 100644
index 0000000..dba76b0
--- /dev/null
+++ b/app/Filament/Resources/Media/Tables/MediaTable.php
@@ -0,0 +1,86 @@
+modifyQueryUsing(fn ($query) => $query->where('collection_name', '!=', 'avatars'))
+ ->columns([
+ ImageColumn::make('url')
+ ->label('Preview')
+ ->getStateUsing(fn ($record) =>
+ // Prefer the stored path produced by Filament's FileUpload (saved in custom_properties),
+ // fall back to Spatie's getUrl() when no stored_path exists.
+ ($record->getCustomProperty('stored_path'))
+ ? Storage::url($record->getCustomProperty('stored_path'))
+ : $record->getUrl()
+ )
+ ->height(40)
+ ->width(40),
+ TextColumn::make('name')
+ ->searchable(),
+ TextColumn::make('file_name')
+ ->searchable(),
+ TextColumn::make('collection_name')
+ ->badge(),
+ TextColumn::make('mime_type'),
+ TextColumn::make('size')
+ ->formatStateUsing(fn ($state) => number_format($state / 1024, 2).' KB'),
+ TextColumn::make('created_at')
+ ->dateTime(),
+ ])
+ ->filters([
+ SelectFilter::make('collection_name')
+ ->options([
+ 'images' => 'Images',
+ 'documents' => 'Documents',
+ ]),
+ ])
+ ->recordActions([
+ EditAction::make(),
+ DeleteAction::make()
+ ->action(function (Media $record) {
+ // Delete the actual stored file path if we saved one, otherwise fall back to the Spatie path.
+ $stored = $record->getCustomProperty('stored_path');
+ if ($stored) {
+ Storage::disk($record->disk)->delete($stored);
+ } else {
+ Storage::disk($record->disk)->delete($record->getPath());
+ }
+
+ $record->delete();
+ }),
+ ])
+ ->toolbarActions([
+ BulkActionGroup::make([
+ DeleteBulkAction::make()
+ ->action(function (Collection $records) {
+ $records->each(function (Media $record) {
+ $stored = $record->getCustomProperty('stored_path');
+ if ($stored) {
+ Storage::disk($record->disk)->delete($stored);
+ } else {
+ Storage::disk($record->disk)->delete($record->getPath());
+ }
+ $record->delete();
+ });
+ }),
+ ]),
+ ]);
+ }
+}
diff --git a/app/Livewire/GalleryPicker.php b/app/Livewire/GalleryPicker.php
new file mode 100644
index 0000000..c1da485
--- /dev/null
+++ b/app/Livewire/GalleryPicker.php
@@ -0,0 +1,108 @@
+entryId = $entryId;
+ $this->loadMediaItems();
+ $this->showModal = true;
+ }
+
+ public function loadMediaItems(): void
+ {
+ $this->mediaItems = Media::where('model_type', 'temp')
+ ->where('model_id', 0)
+ ->where('disk', 'public')
+ ->latest()
+ ->limit(30)
+ ->get(['id', 'file_name', 'name', 'disk'])
+ ->toArray();
+ }
+
+ public function selectMedia($mediaId): void
+ {
+ $this->selectedMediaId = $mediaId;
+ }
+
+ public function copyToEntry(): void
+ {
+ if (!$this->selectedMediaId || !$this->entryId) {
+ $this->dispatch('notify-error', ['message' => 'Please select an image']);
+ return;
+ }
+
+ $sourceMedia = Media::find($this->selectedMediaId);
+ if (!$sourceMedia) {
+ $this->dispatch('notify-error', ['message' => 'Media not found']);
+ return;
+ }
+
+ try {
+ // Get the entry
+ $entry = \App\Models\Entry::find($this->entryId);
+ if (!$entry) {
+ $this->dispatch('notify-error', ['message' => 'Entry not found']);
+ return;
+ }
+
+ // Get source file
+ $sourceFile = $sourceMedia->getPath();
+ if (!file_exists($sourceFile)) {
+ $this->dispatch('notify-error', ['message' => 'Source file not found']);
+ return;
+ }
+
+ // Create temp copy
+ $tempCopy = sys_get_temp_dir() . '/' . uniqid() . '_' . $sourceMedia->file_name;
+ copy($sourceFile, $tempCopy);
+
+ try {
+ // Clear existing featured image
+ $entry->clearMediaCollection('featured-image');
+
+ // Add to entry
+ $newMedia = $entry->addMedia($tempCopy)
+ ->usingName($sourceMedia->name ?: pathinfo($sourceMedia->file_name, PATHINFO_FILENAME))
+ ->usingFileName($sourceMedia->file_name)
+ ->toMediaCollection('featured-image', 'public');
+
+ // Close modal and notify
+ $this->showModal = false;
+ $this->selectedMediaId = null;
+ $this->dispatch('media-selected', ['mediaId' => $newMedia->id, 'fileName' => $newMedia->file_name]);
+ $this->dispatch('notify-success', ['message' => 'Image added to entry']);
+ } finally {
+ if (file_exists($tempCopy)) {
+ unlink($tempCopy);
+ }
+ }
+ } catch (\Exception $e) {
+ $this->dispatch('notify-error', ['message' => 'Error: ' . $e->getMessage()]);
+ }
+ }
+
+ public function closePicker(): void
+ {
+ $this->showModal = false;
+ $this->selectedMediaId = null;
+ }
+
+ public function render()
+ {
+ return view('livewire.gallery-picker');
+ }
+}
diff --git a/app/Providers/Filament/AdminPanelProvider.php b/app/Providers/Filament/AdminPanelProvider.php
index 373a4be..81aa43d 100644
--- a/app/Providers/Filament/AdminPanelProvider.php
+++ b/app/Providers/Filament/AdminPanelProvider.php
@@ -10,6 +10,8 @@ use Filament\Pages\Dashboard;
use Filament\Panel;
use Filament\PanelProvider;
use Filament\Support\Colors\Color;
+use Filament\Support\Facades\FilamentView;
+use Filament\View\PanelsRenderHook;
use Filament\Widgets\AccountWidget;
use Filament\Widgets\FilamentInfoWidget;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
@@ -56,4 +58,12 @@ class AdminPanelProvider extends PanelProvider
Authenticate::class,
]);
}
+
+ public function boot(): void
+ {
+ FilamentView::registerRenderHook(
+ PanelsRenderHook::BODY_END,
+ fn (): string => \Illuminate\Support\Facades\Blade::render('@vite("resources/js/app.js")'),
+ );
+ }
}
diff --git a/composer.json b/composer.json
index 4745dfb..c67e0d1 100644
--- a/composer.json
+++ b/composer.json
@@ -8,7 +8,6 @@
"require": {
"php": "^8.2",
"filament/filament": "^4.0",
- "filament/spatie-laravel-media-library-plugin": "^4.0",
"laravel/fortify": "^1.30",
"laravel/framework": "^12.0",
"laravel/tinker": "^2.10.1",
diff --git a/resources/js/app.js b/resources/js/app.js
index b677733..22e1c9c 100644
--- a/resources/js/app.js
+++ b/resources/js/app.js
@@ -1 +1,48 @@
console.log('App.js is loaded');
+
+document.addEventListener('livewire:init', () => {
+ Livewire.on('insert-editor-content', (data) => {
+ console.log('Received insert-editor-content data:', data);
+ // Handle if data is an array
+ const payload = Array.isArray(data) ? data[0] : data;
+ const statePath = payload.statePath;
+ const html = payload.html;
+ console.log('Extracted statePath:', statePath, 'html:', html);
+ // 1. Find the editor by its statePath
+ const container = document.querySelector(`[wire\\:model="${statePath}"]`) || document.querySelector(`[statepath="${statePath}"]`);
+ console.log('Container found:', container);
+ const editorElement = container ? container.querySelector('.tiptap') : null;
+ console.log('Editor element found:', editorElement);
+
+ if (editorElement && editorElement.editor) {
+ console.log('Inserting content:', html);
+ // 2. Insert the HTML (the
tag) into the editor
+ setTimeout(() => {
+ editorElement.editor.chain().focus().insertContent(html).run();
+ }, 500);
+ } else {
+ console.log('Editor not found or not initialized');
+ // Fallback: try to find any .tiptap on the page
+ const anyTiptap = document.querySelector('.tiptap');
+ console.log('Any tiptap found:', anyTiptap);
+ if (anyTiptap && anyTiptap.editor) {
+ console.log('Inserting to any tiptap');
+ setTimeout(() => {
+ anyTiptap.editor.chain().focus().insertContent(html).run();
+ }, 500);
+ }
+ }
+ });
+
+ Livewire.on('featured-image-added', (data) => {
+ console.log('Received featured-image-added event:', data);
+ const payload = Array.isArray(data) ? data[0] : data;
+
+ // Reload the page to show the updated featured image
+ setTimeout(() => {
+ window.location.reload();
+ }, 500);
+ });
+});
+
+console.log('Testing if app.js is still running');
\ No newline at end of file
diff --git a/resources/views/components/featured-image-display.blade.php b/resources/views/components/featured-image-display.blade.php
new file mode 100644
index 0000000..437552f
--- /dev/null
+++ b/resources/views/components/featured-image-display.blade.php
@@ -0,0 +1,32 @@
+
+ @php
+ $record = $this->data;
+ if ($record && isset($record['id'])) {
+ $entry = \App\Models\Entry::find($record['id']);
+ $media = $entry ? $entry->getMedia('featured-image') : [];
+ } else {
+ $media = [];
+ }
+ @endphp
+
+
+
+ @if(count($media) > 0)
+
+ @foreach($media as $item)
+
+
+
 }})
+
+
{{ $item->name }}
+
{{ $item->file_name }}
+
+
+
+ @endforeach
+
+ @else
+
No featured image selected
+ @endif
+
+
diff --git a/resources/views/livewire/gallery-picker.blade.php b/resources/views/livewire/gallery-picker.blade.php
new file mode 100644
index 0000000..103b66b
--- /dev/null
+++ b/resources/views/livewire/gallery-picker.blade.php
@@ -0,0 +1,72 @@
+
+ @if($showModal ?? false)
+
+
+
+
+
+
+
+
+
+
+ Select Image from Gallery
+
+
+
+
+ @if(empty($mediaItems ?? []))
+
No images in gallery
+ @else
+
+ @foreach($mediaItems ?? [] as $item)
+
+ @endforeach
+
+ @endif
+
+
+
+
+
+
+
+
+
+ @endif
+