diff --git a/app/DataTransferObjects/NewsFeedItem.php b/app/DataTransferObjects/NewsFeedItem.php new file mode 100644 index 0000000..26865e4 --- /dev/null +++ b/app/DataTransferObjects/NewsFeedItem.php @@ -0,0 +1,41 @@ + $this->id, + 'title' => $this->title, + 'content' => $this->content, + 'publishedAt' => $this->publishedAt, + 'author' => $this->author, + 'embeds' => $this->embeds, + 'media' => $this->media, + ]; + } + + public static function fromLivewire($value): static + { + return new static(...$value); + } +} diff --git a/app/DataTransferObjects/NewsFeedItemAuthor.php b/app/DataTransferObjects/NewsFeedItemAuthor.php new file mode 100644 index 0000000..0d7c8bb --- /dev/null +++ b/app/DataTransferObjects/NewsFeedItemAuthor.php @@ -0,0 +1,30 @@ + $this->name, + 'avatar' => $this->avatar, + ]; + } + + public static function fromLivewire($value): static + { + return new static(...$value); + } +} diff --git a/app/DataTransferObjects/NewsFeedItemEmbed.php b/app/DataTransferObjects/NewsFeedItemEmbed.php new file mode 100644 index 0000000..d83361a --- /dev/null +++ b/app/DataTransferObjects/NewsFeedItemEmbed.php @@ -0,0 +1,28 @@ + $this->html, + ]; + } + + public static function fromLivewire($value): static + { + return new static(...$value); + } +} diff --git a/app/DataTransferObjects/NewsFeedItemMedia.php b/app/DataTransferObjects/NewsFeedItemMedia.php new file mode 100644 index 0000000..8cc51db --- /dev/null +++ b/app/DataTransferObjects/NewsFeedItemMedia.php @@ -0,0 +1,32 @@ + $this->name, + 'url' => $this->url, + 'thumbs' => $this->thumb, + ]; + } + + public static function fromLivewire($value): static + { + return new static(...$value); + } +} diff --git a/app/Filament/Resources/ElectionDayResource.php b/app/Filament/Resources/ElectionDayResource.php index 76f835e..1fc6e36 100644 --- a/app/Filament/Resources/ElectionDayResource.php +++ b/app/Filament/Resources/ElectionDayResource.php @@ -22,7 +22,7 @@ class ElectionDayResource extends Resource public static function getNavigationGroup(): ?string { - return __('navigation.group.manage'); + return __('admin.navigation.newsfeed'); } public static function form(Form $form): Form diff --git a/app/Filament/Resources/PostResource.php b/app/Filament/Resources/PostResource.php index b3f0eb0..779be20 100644 --- a/app/Filament/Resources/PostResource.php +++ b/app/Filament/Resources/PostResource.php @@ -32,7 +32,7 @@ class PostResource extends Resource public static function getNavigationGroup(): ?string { - return __('navigation.group.manage'); + return __('admin.navigation.newsfeed'); } public static function form(Form $form): Form @@ -55,6 +55,13 @@ public static function form(Form $form): Form ->required() ->preload(), + Select::make('election_day_id') + ->relationship('electionDay', 'date') + ->getOptionLabelFromRecordUsing(fn (ElectionDay $record) => $record->date->toDateString()) + // ->formatStateUsing(fn (Post $record) => dd($record) && $record->date?->toDateString()) + ->required() + ->preload(), + DateTimePicker::make('published_at') ->nullable(), diff --git a/app/Livewire/NewsFeed.php b/app/Livewire/NewsFeed.php index 33641a1..4539d96 100644 --- a/app/Livewire/NewsFeed.php +++ b/app/Livewire/NewsFeed.php @@ -13,7 +13,8 @@ use Filament\Forms\Contracts\HasForms; use Filament\Forms\Form; use Illuminate\Contracts\Database\Eloquent\Builder; -use Illuminate\Pagination\LengthAwarePaginator; +use Illuminate\Support\Collection; +use Livewire\Attributes\Computed; use Livewire\Component; use Livewire\WithPagination; @@ -22,10 +23,18 @@ class NewsFeed extends Component implements HasForms use InteractsWithForms; use WithPagination; + private int $perPage = 10; + + public Collection $posts; + public ?array $filters = []; public function mount(): void { + $this->posts = collect(); + + $this->loadPosts(); + $this->form->fill(); } @@ -64,24 +73,45 @@ public function form(Form $form): Form public function render() { - return view('livewire.news-feed', [ - 'posts' => $this->getPosts(), - ]); + return view('livewire.news-feed'); + } + + private function loadPosts(bool $more = false): void + { + $this->query() + ->limit($this->perPage) + ->when($more, fn (Builder $query) => $query->offset($this->posts->count())) + ->get() + ->each(fn (Post $post) => $this->posts->push( + $post->toNewsFeedItem() + )); } public function loadMore(): void { + $this->loadPosts(true); + } + + #[Computed] + public function total(): int + { + return $this->query()->count(); + } + + #[Computed] + public function hasMore(): bool + { + return $this->posts->count() < $this->total; } - protected function getPosts(): LengthAwarePaginator + private function query(): Builder { return Post::query() - ->with('author.media', 'electionDay') + ->with('author.media', 'electionDay', 'media') ->when(data_get($this->filters, 'country'), fn (Builder $query, array $countries) => $query->whereIn('country', $countries)) ->when(data_get($this->filters, 'author'), fn (Builder $query, array $authors) => $query->whereIn('author_id', $authors)) ->when(data_get($this->filters, 'day'), fn (Builder $query, array $days) => $query->whereIn('election_day_id', $days)) ->onlyPublished() - ->orderByDesc('published_at') - ->paginate(); + ->orderByDesc('published_at'); } } diff --git a/app/Models/Media.php b/app/Models/Media.php new file mode 100644 index 0000000..d7be52c --- /dev/null +++ b/app/Models/Media.php @@ -0,0 +1,12 @@ +belongsTo(ElectionDay::class); } + + public function toNewsFeedItem(): NewsFeedItem + { + return new NewsFeedItem( + id: $this->id, + title: $this->title, + content: $this->content, + publishedAt: $this->published_at, + author: new NewsFeedItemAuthor( + name: $this->author->name, + avatar: $this->author->getFilamentAvatarUrl(), + ), + embeds: $this->embeds + ->map(fn (array $embed) => new NewsFeedItemEmbed(html: $embed['html'])) + ->all(), + media: $this->getMedia() + ->map(fn (Media $media) => new NewsFeedItemMedia( + name: $media->name, + url: $media->getUrl(), + thumb: $media->getUrl('thumb'), + )) + ->all(), + ); + } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 2ce8d21..61ee1a1 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -4,6 +4,12 @@ namespace App\Providers; +use App\Models\ElectionDay; +use App\Models\Media; +use App\Models\Post; +use App\Models\User; +use Illuminate\Database\Eloquent\Relations\Relation; +use Illuminate\Support\Facades\Gate; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Str; @@ -27,7 +33,9 @@ public function register(): void */ public function boot(): void { - // + $this->enforceMorphMap(); + + Gate::define('viewPulse', fn (User $user) => $user->isAdmin()); } protected function registerCountries(): void @@ -201,4 +209,14 @@ protected function registerLanguages(): void ], ]); } + + protected function enforceMorphMap(): void + { + Relation::enforceMorphMap([ + 'electionDay' => ElectionDay::class, + 'media' => Media::class, + 'post' => Post::class, + 'user' => User::class, + ]); + } } diff --git a/config/media-library.php b/config/media-library.php index 4200946..556c6b0 100644 --- a/config/media-library.php +++ b/config/media-library.php @@ -36,7 +36,7 @@ /* * The fully qualified class name of the media model. */ - 'media_model' => Spatie\MediaLibrary\MediaCollections\Models\Media::class, + 'media_model' => App\Models\Media::class, /* * When enabled, media collections will be serialised using the default diff --git a/lang/en/admin.php b/lang/en/admin.php new file mode 100644 index 0000000..a266567 --- /dev/null +++ b/lang/en/admin.php @@ -0,0 +1,12 @@ + [ + 'newsfeed' => 'Newsfeed', + 'counters' => 'Counters', + ], + +]; diff --git a/resources/views/components/news-item.blade.php b/resources/views/components/news-feed-item.blade.php similarity index 73% rename from resources/views/components/news-item.blade.php rename to resources/views/components/news-feed-item.blade.php index a6ee7e7..f654aec 100644 --- a/resources/views/components/news-item.blade.php +++ b/resources/views/components/news-feed-item.blade.php @@ -1,13 +1,12 @@ @props(['post'])
merge([ - 'x-data' => '{ more: false }', - 'class' => 'overflow-hidden bg-white rounded-lg shadow', - ]) }}> + wire:key="{{ $post->id }}" + x-data="{ more: false }" + class="overflow-hidden bg-white rounded-lg shadow">
-
@@ -37,9 +36,11 @@ class="prose prose-headings:text-base prose-headings:font-medium max-w-none">
- @foreach ($post->getMedia() as $media) - - media as $media) + + {{ $media->name }} diff --git a/resources/views/livewire/news-feed.blade.php b/resources/views/livewire/news-feed.blade.php index 77c8148..5dcd852 100644 --- a/resources/views/livewire/news-feed.blade.php +++ b/resources/views/livewire/news-feed.blade.php @@ -15,16 +15,44 @@ class="prose md:prose-lg lg:prose-xl max-w-none prose-headings:font-semibold pro {{ $this->form }} -
- {{ $posts->links(data: ['scrollTo' => '#newsfeed']) }} - +
@forelse ($posts as $post) - + @empty

No Posts Found

@endforelse - {{ $posts->links(data: ['scrollTo' => '#newsfeed']) }} +
+
Loading...
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + @if ($this->hasMore) +
+ @endif
diff --git a/tailwind.config.js b/tailwind.config.js index 0508f8c..60d4e34 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,4 +1,5 @@ export default { + darkMode: 'selector', content: [ //, './app/Filament/**/*.php',