path; } public static function canCreate(): bool { // Files arrive by being uploaded on a record, not from here. return false; } public static function table(Table $table): Table { return $table ->defaultSort('created_at', 'desc') ->columns([ ImageColumn::make('path') ->label('') ->disk(fn (Media $record) => $record->disk) ->height(48), TextColumn::make('path') ->label(__('panel.media.fields.file')) ->fontFamily('mono') ->size('xs') ->wrap() ->searchable() ->description(fn (Media $record) => $record->mime), TextColumn::make('width') ->label(__('panel.media.fields.size')) ->state(fn (Media $record) => $record->width ? "{$record->width}×{$record->height}" : __('panel.media.pending')) ->badge() ->color(fn (Media $record) => $record->width ? 'gray' : 'warning'), TextColumn::make('variants') ->label(__('panel.media.fields.versions')) ->state(fn (Media $record) => count($record->variants ?? [])) ->badge() ->color(fn (int $state) => $state > 0 ? 'success' : 'gray'), // Original plus every derived version: the number that // explains where the disk went. TextColumn::make('size') ->label(__('panel.media.fields.weight')) ->state(fn (Media $record) => round($record->totalSize() / 1024).' KB') ->sortable(), TextColumn::make('used') ->label(__('panel.media.fields.used')) ->state(fn (Media $record) => $record->isReferenced() ? __('panel.media.in_use') : __('panel.media.orphan')) ->badge() ->color(fn (Media $record) => $record->isReferenced() ? 'success' : 'warning'), TextColumn::make('error') ->label(__('panel.media.fields.error')) ->wrap() ->color('danger') ->placeholder('—') ->toggleable(isToggledHiddenByDefault: true), ]) ->filters([ Filter::make('pending') ->label(__('panel.media.filters.pending')) ->query(fn (Builder $query) => $query->pending()), Filter::make('failed') ->label(__('panel.media.filters.failed')) ->query(fn (Builder $query) => $query->whereNotNull('error')), // Everything the prune command would take away. Filter::make('orphans') ->label(__('panel.media.filters.orphans')) ->query(fn (Builder $query) => $query ->whereNotIn('path', Media::referencedPaths())), Filter::make('missing') ->label(__('panel.media.filters.missing')) ->query(fn (Builder $query) => $query ->whereIn('id', Media::query()->get() ->reject(fn (Media $m) => $m->exists()) ->pluck('id'))), ]) ->recordActions([ Action::make('open') ->label(__('panel.media.actions.open')) ->icon('heroicon-o-arrow-top-right-on-square') ->url(fn (Media $record) => $record->url()) ->openUrlInNewTab(), Action::make('reprocess') ->label(__('panel.media.actions.reprocess')) ->icon('heroicon-o-arrow-path') ->color('gray') ->action(function (Media $record): void { ProcessMedia::dispatch($record); Notification::make() ->title(__('panel.media.actions.reprocess_queued')) ->success() ->send(); }), // Only offered for files nothing points at: deleting one in // use would put a broken image on the public site. DeleteAction::make() ->visible(fn (Media $record) => ! $record->isReferenced()) ->before(function (Media $record): void { $record->deleteDerived(); Storage::disk($record->disk)->delete($record->path); }), ]) ->emptyStateHeading(__('panel.media.empty.heading')) ->emptyStateDescription(__('panel.media.empty.description')) ->emptyStateIcon('heroicon-o-photo'); } /** * @return array */ public static function getGloballySearchableAttributes(): array { return ['path']; } public static function getPages(): array { return [ 'index' => ListMedia::route('/'), ]; } public static function getNavigationBadge(): ?string { $pending = static::getModel()::query()->pending()->count(); return $pending > 0 ? (string) $pending : null; } public static function getNavigationBadgeColor(): ?string { return 'warning'; } }