diff --git a/app/Livewire/AdminCommentSearch.php b/app/Livewire/AdminCommentSearch.php index eac49d1..bef600d 100644 --- a/app/Livewire/AdminCommentSearch.php +++ b/app/Livewire/AdminCommentSearch.php @@ -2,7 +2,10 @@ namespace App\Livewire; +use App\Enums\UserRole; use App\Models\Comment; +use App\Models\User; +use Livewire\Attributes\Url; use Livewire\Component; use Livewire\WithPagination; @@ -10,37 +13,147 @@ class AdminCommentSearch extends Component { use WithPagination; + #[Url(history: true)] public $search = ''; + #[Url(history: true)] public $userSearch = ''; - public function updatingSearch(): void + #[Url(history: true)] + public $sortField = 'created_at'; + + #[Url(history: true)] + public $sortDirection = 'desc'; + + #[Url(history: true)] + public $perPage = 20; + + public $selected = []; + + public $selectPage = false; + + protected $queryString = [ + 'search' => ['except' => ''], + 'userSearch' => ['except' => ''], + 'sortField' => ['except' => 'created_at'], + 'sortDirection' => ['except' => 'desc'], + 'perPage' => ['except' => 20], + ]; + + protected $allowedSortFields = ['id', 'body', 'created_at', 'user_id']; + + protected $allowedPerPages = [10, 20, 50, 100]; + + public function updatedPerPage(): void { $this->resetPage(); } - public function updatingUserSearch(): void + public function updatedPage(): void { + $this->selectPage = false; + $this->selected = []; + } + + public function updatedSelectPage($value): void + { + if ($value) { + $this->selected = $this->comments->pluck('id')->map(fn ($id) => (string) $id)->toArray(); + } else { + $this->selected = []; + } + } + + public function sortBy(string $field): void + { + if (! in_array($field, $this->allowedSortFields)) { + return; + } + + if ($this->sortField === $field) { + $this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc'; + } else { + $this->sortField = $field; + $this->sortDirection = 'asc'; + } + } + + public function clearFilters(): void + { + $this->search = ''; + $this->userSearch = ''; + $this->sortField = 'created_at'; + $this->sortDirection = 'desc'; + $this->perPage = 20; $this->resetPage(); } - public function deleteComment($commentId) + public function deleteComment(int $commentId): void { - $comment = Comment::where('id', (int) $commentId)->firstOrFail(); + $comment = Comment::findOrFail($commentId); $comment->delete(); + cache()->flush(); + $this->dispatch('notify', type: 'success', message: 'Comment deleted successfully.'); + } + + public function bulkDelete(): void + { + $count = Comment::whereIn('id', array_map('intval', $this->selected))->delete(); + cache()->flush(); + $this->selected = []; + $this->selectPage = false; + $this->dispatch('notify', type: 'success', message: "{$count} comment(s) deleted."); + } + + public function banCommentAuthor(int $commentId): void + { + $comment = Comment::findOrFail($commentId); + $user = $comment->user; + + if ($user && ! $user->hasRole(UserRole::BANNED)) { + $user->addRole(UserRole::BANNED); + cache()->flush(); + $this->dispatch('notify', type: 'success', message: "{$user->name} has been banned."); + } else { + $this->dispatch('notify', type: 'error', message: 'User is already banned or not found.'); + } + } + + public function bulkBanAuthors(): void + { + $count = 0; + $userIds = Comment::whereIn('id', array_map('intval', $this->selected)) + ->pluck('user_id') + ->unique(); + + foreach ($userIds as $userId) { + $user = User::find($userId); + if ($user && ! $user->hasRole(UserRole::BANNED)) { + $user->addRole(UserRole::BANNED); + $count++; + } + } cache()->flush(); + $this->selected = []; + $this->selectPage = false; + $this->dispatch('notify', type: 'success', message: "{$count} comment author(s) banned."); + } + + public function getCommentsProperty() + { + return Comment::query() + ->with('user') + ->when($this->search !== '', fn ($query) => $query->where('body', 'LIKE', "%{$this->search}%")) + ->when($this->userSearch !== '', fn ($query) => $query->whereHas('user', fn ($q) => $q->where('name', 'LIKE', "%{$this->userSearch}%"))) + ->orderBy($this->sortField, $this->sortDirection) + ->paginate((int) $this->perPage); } public function render() { - $comments = Comment::when($this->search !== '', fn ($query) => $query->where('body', 'LIKE', "%$this->search%")) - ->when($this->userSearch !== '', fn ($query) => $query->whereHas('user', fn ($query) => $query->where('name', 'LIKE', "%{$this->userSearch}%"))) - ->orderBy('created_at', 'DESC') - ->paginate(12); - return view('livewire.admin-comment-search', [ - 'comments' => $comments, + 'comments' => $this->comments, ]); } -} +} \ No newline at end of file diff --git a/app/Livewire/AdminUserSearch.php b/app/Livewire/AdminUserSearch.php index e8ffae2..4893e9e 100644 --- a/app/Livewire/AdminUserSearch.php +++ b/app/Livewire/AdminUserSearch.php @@ -20,32 +20,239 @@ class AdminUserSearch extends Component public $discordId = ''; #[Url(history: true)] - public $patreon = []; + public $email = ''; #[Url(history: true)] - public $banned = []; + public $roleFilter = []; - public function deleteUserComments(int $userID) + #[Url(history: true)] + public $sortField = 'created_at'; + + #[Url(history: true)] + public $sortDirection = 'desc'; + + #[Url(history: true)] + public $perPage = 20; + + public $selected = []; + + public $selectAll = false; + + public $selectPage = false; + + // Modal state + public $showUserModal = false; + + public $modalUser = null; + + public $modalUserComments = []; + + protected $queryString = [ + 'search' => ['except' => ''], + 'discordId' => ['except' => ''], + 'email' => ['except' => ''], + 'roleFilter' => ['except' => []], + 'sortField' => ['except' => 'created_at'], + 'sortDirection' => ['except' => 'desc'], + 'perPage' => ['except' => 20], + ]; + + protected $allowedSortFields = ['id', 'name', 'email', 'discord_id', 'created_at', 'updated_at']; + + protected $allowedPerPages = [10, 20, 50, 100]; + + public function updatedPerPage(): void { - $user = User::where('id', $userID) - ->firstOrFail(); + $this->resetPage(); + } - Comment::where('user_id', $user->id) - ->delete(); + public function updatedPage(): void + { + $this->selectPage = false; + $this->selected = []; + } + public function updatedSelectPage($value): void + { + if ($value) { + $this->selected = $this->users->pluck('id')->map(fn ($id) => (string) $id)->toArray(); + } else { + $this->selected = []; + } + } + + public function sortBy(string $field): void + { + if (! in_array($field, $this->allowedSortFields)) { + return; + } + + if ($this->sortField === $field) { + $this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc'; + } else { + $this->sortField = $field; + $this->sortDirection = 'asc'; + } + } + + public function clearFilters(): void + { + $this->search = ''; + $this->discordId = ''; + $this->email = ''; + $this->roleFilter = []; + $this->sortField = 'created_at'; + $this->sortDirection = 'desc'; + $this->perPage = 20; + $this->resetPage(); + } + + public function viewUser(int $userId): void + { + $this->modalUser = User::find($userId); + + if ($this->modalUser) { + $this->modalUserComments = $this->modalUser->comments() + ->orderBy('created_at', 'desc') + ->limit(10) + ->get(); + $this->showUserModal = true; + } + } + + public function closeModal(): void + { + $this->showUserModal = false; + $this->modalUser = null; + $this->modalUserComments = []; + } + + public function banUser(int $userId): void + { + $user = User::findOrFail($userId); + $user->addRole(UserRole::BANNED); cache()->flush(); + $this->dispatch('notify', type: 'success', message: "{$user->name} has been banned."); + + if ($this->showUserModal && $this->modalUser?->id === $userId) { + $this->modalUser->refresh(); + } + } + + public function unbanUser(int $userId): void + { + $user = User::findOrFail($userId); + $user->removeRole(UserRole::BANNED); + cache()->flush(); + $this->dispatch('notify', type: 'success', message: "{$user->name} has been unbanned."); + + if ($this->showUserModal && $this->modalUser?->id === $userId) { + $this->modalUser->refresh(); + } + } + + public function grantModerator(int $userId): void + { + $user = User::findOrFail($userId); + $user->addRole(UserRole::MODERATOR); + cache()->flush(); + $this->dispatch('notify', type: 'success', message: "{$user->name} has been granted Moderator role."); + + if ($this->showUserModal && $this->modalUser?->id === $userId) { + $this->modalUser->refresh(); + } + } + + public function revokeModerator(int $userId): void + { + $user = User::findOrFail($userId); + $user->removeRole(UserRole::MODERATOR); + cache()->flush(); + $this->dispatch('notify', type: 'success', message: "Moderator role revoked from {$user->name}."); + + if ($this->showUserModal && $this->modalUser?->id === $userId) { + $this->modalUser->refresh(); + } + } + + public function deleteUserComments(int $userId): void + { + $user = User::findOrFail($userId); + Comment::where('user_id', $user->id)->delete(); + cache()->flush(); + $this->dispatch('notify', type: 'success', message: "All comments from {$user->name} have been deleted."); + + if ($this->showUserModal && $this->modalUser?->id === $userId) { + $this->modalUserComments = collect(); + } + } + + public function bulkBan(): void + { + $count = 0; + foreach ($this->selected as $userId) { + $user = User::find($userId); + if ($user && ! $user->hasRole(UserRole::BANNED)) { + $user->addRole(UserRole::BANNED); + $count++; + } + } + cache()->flush(); + $this->selected = []; + $this->selectPage = false; + $this->dispatch('notify', type: 'success', message: "{$count} user(s) have been banned."); + } + + public function bulkUnban(): void + { + $count = 0; + foreach ($this->selected as $userId) { + $user = User::find($userId); + if ($user && $user->hasRole(UserRole::BANNED)) { + $user->removeRole(UserRole::BANNED); + $count++; + } + } + cache()->flush(); + $this->selected = []; + $this->selectPage = false; + $this->dispatch('notify', type: 'success', message: "{$count} user(s) have been unbanned."); + } + + public function bulkDeleteComments(): void + { + $count = 0; + foreach ($this->selected as $userId) { + $deleted = Comment::where('user_id', (int) $userId)->delete(); + if ($deleted > 0) { + $count++; + } + } + cache()->flush(); + $this->selected = []; + $this->selectPage = false; + $this->dispatch('notify', type: 'success', message: "Deleted comments from {$count} user(s)."); + } + + public function getUsersProperty() + { + return User::query() + ->when($this->search !== '', fn ($query) => $query->where('name', 'like', '%'.$this->search.'%')) + ->when($this->discordId !== '', fn ($query) => $query->where('discord_id', '=', $this->discordId)) + ->when($this->email !== '', fn ($query) => $query->where('email', 'like', '%'.$this->email.'%')) + ->when(! empty($this->roleFilter), function ($query) { + foreach ($this->roleFilter as $role) { + $query->whereJsonContains('roles', $role); + } + }) + ->orderBy($this->sortField, $this->sortDirection) + ->paginate((int) $this->perPage); } public function render() { - $users = User::when($this->patreon !== [], fn ($query) => $query->whereJsonContains('roles', UserRole::SUPPORTER->value)) - ->when($this->banned !== [], fn ($query) => $query->whereJsonContains('roles', UserRole::BANNED->value)) - ->when($this->search !== '', fn ($query) => $query->where('name', 'like', '%'.$this->search.'%')) - ->when($this->discordId !== '', fn ($query) => $query->where('discord_id', '=', $this->discordId)) - ->paginate(20); - return view('livewire.admin-user-search', [ - 'users' => $users, + 'users' => $this->users, ]); } -} +} \ No newline at end of file diff --git a/resources/views/livewire/admin-comment-search.blade.php b/resources/views/livewire/admin-comment-search.blade.php index ad9ad46..7d9101d 100644 --- a/resources/views/livewire/admin-comment-search.blade.php +++ b/resources/views/livewire/admin-comment-search.blade.php @@ -1,60 +1,291 @@ -
| - User - + | + | -- Comment - + |
+
+ Author
+ @if($sortField === 'user_id')
+
+ @endif
+
|
- + |
+
+ Comment
+ @if($sortField === 'body')
+
+ @endif
+
|
- - Actions + |
+
+ Date
+ @if($sortField === 'created_at')
+
+ @endif
+
|
+ Actions |
|---|---|---|---|---|---|---|---|---|
| - {{ $comment->user->name }} + @forelse($comments as $comment) + | ||||||||
| + | -- {{ $comment->body }} - | -- {{ $comment->created_at }} - | -- + |
+
+ @if($comment->user)
+
+
+ {{ $comment->user->name }}
+ @if($comment->user->hasRole(\App\Enums\UserRole::BANNED))
+ Banned
+ @endif
+
+ @else
+ Unknown
+ @endif
+ |
+
+
+
+ + {{ $comment->body }} + ++ {{ $comment->body }} + + @if(strlen($comment->body) > 150) + + @endif + |
+ + + {{ $comment->created_at->diffForHumans() }} + + | +
+
+
+ @if($comment->user && !$comment->user->hasRole(\App\Enums\UserRole::BANNED))
+
+ @endif
+
|
|
|
+
+ No comments found matching your filters. + |
+ ||||||||
{{ $comment->body }}
+ {{ $comment->created_at->diffForHumans() }} +| - ID - | -- Discord ID - - | -- Username - - | -- Patreon - - | -- Banned - - | -- Created at - | -- Updated at - | -- Actions + | + | + @foreach([ + 'id' => 'ID', + 'discord_id' => 'Discord ID', + 'name' => 'Username', + 'email' => 'Email', + 'created_at' => 'Registered', + 'updated_at' => 'Updated', + ] as $field => $label) +
+
+ {{ $label }}
+ @if($sortField === $field)
+
+ @endif
+
+ |
+ @endforeach
+ Roles | +Actions |
|---|---|---|---|---|---|---|---|---|---|---|---|
| - {{ $user->id }} - | -- {{ $user->discord_id ?? 'n/a' }} + @forelse($users as $user) + | ||||||||||
| + | -- {{ $user->name }} - | -- {{ $user->hasRole(\App\Enums\UserRole::SUPPORTER) ? 'Yes' : 'No' }} - | -- {{ $user->hasRole(\App\Enums\UserRole::BANNED) ? 'Yes' : 'No' }} - | -- {{ $user->created_at->format('Y-m-d') }} - | -- {{ $user->updated_at->format('Y-m-d') }} - | -- - | |||||
|
+
+ No users found matching your filters. + |
+ |||||||||||