Skip to content

Commit 46496bd

Browse files
committed
feat: implement comments functionality with Comment model, factory, and seeder; enhance dashboard for admin view
1 parent ead9fe3 commit 46496bd

File tree

7 files changed

+166
-5
lines changed

7 files changed

+166
-5
lines changed

app/Models/Comment.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22

33
namespace App\Models;
44

5+
use Illuminate\Database\Eloquent\Factories\HasFactory;
56
use Illuminate\Database\Eloquent\Model;
67

78
class Comment extends Model
89
{
10+
use HasFactory;
11+
912
protected $fillable = [
1013
'user_id',
1114
'ticket_id',
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace Database\Factories;
4+
5+
use App\Models\Ticket;
6+
use App\Models\User;
7+
use Illuminate\Database\Eloquent\Factories\Factory;
8+
9+
/**
10+
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Comment>
11+
*/
12+
class CommentFactory extends Factory
13+
{
14+
public function definition(): array
15+
{
16+
return [
17+
'body' => $this->faker->sentence(),
18+
'user_id' => User::inRandomOrder()->first()?->id ?? User::factory(),
19+
'ticket_id' => Ticket::inRandomOrder()->first()?->id ?? Ticket::factory(),
20+
];
21+
}
22+
}

database/seeders/CommentSeeder.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace Database\Seeders;
4+
5+
use App\Models\Comment;
6+
use App\Models\Ticket;
7+
use App\Models\User;
8+
use Illuminate\Database\Seeder;
9+
10+
class CommentSeeder extends Seeder
11+
{
12+
public function run(): void
13+
{
14+
$users = User::all();
15+
$tickets = Ticket::all();
16+
17+
if ($users->isEmpty() || $tickets->isEmpty()) {
18+
$this->command->warn('Geen users of tickets gevonden. Seed die eerst.');
19+
return;
20+
}
21+
22+
foreach ($tickets as $ticket) {
23+
// Elke ticket krijgt 1–4 willekeurige reacties
24+
$count = rand(1, 4);
25+
for ($i = 0; $i < $count; $i++) {
26+
Comment::create([
27+
'ticket_id' => $ticket->id,
28+
'user_id' => $users->random()->id,
29+
'body' => fake()->sentence(),
30+
]);
31+
}
32+
}
33+
34+
$this->command->info('Comments succesvol geseeded!');
35+
}
36+
}

database/seeders/DatabaseSeeder.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ class DatabaseSeeder extends Seeder
1616
*/
1717
public function run(): void
1818
{
19-
User::factory(10)->create();
19+
User::factory(10)->create();
2020

2121
User::factory()->create([
2222
'name' => 'Test User',
2323
'email' => 'test@example.com',
2424
]);
2525

26-
Ticket::factory(20)->create();
26+
Ticket::factory(20)->create();
27+
28+
$this->call(CommentSeeder::class);
2729
}
2830
}

resources/css/app.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
@tailwind base;
22
@tailwind components;
33
@tailwind utilities;
4+
5+
[x-cloak] { display: none !important; }

resources/views/dashboard.blade.php

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<x-app-layout>
22
<x-slot name="header">
33
<div class="flex justify-between items-center">
4-
<h2 class="text-2xl font-bold text-gray-900">Mijn Dashboard</h2>
4+
<h2 class="text-2xl font-bold text-gray-900">{{ auth()->user()->isAdmin() ? 'Admin Dashboard' : 'Mijn Dashboard' }}</h2>
55
<button onclick="window.location='{{ route('tickets.index') }}'" class="bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded">Mijn tickets</button>
66
</div>
77
</x-slot>
@@ -39,6 +39,93 @@
3939
</div>
4040
</div>
4141
</div>
42+
43+
@if(auth()->user()->isAdmin())
44+
<div class="py-8">
45+
<div class="max-w-6xl mx-auto sm:px-6 lg:px-8">
46+
<h3 class="text-xl font-bold text-gray-900 mb-6">Alle tickets</h3>
47+
48+
@forelse($allTickets ?? collect() as $ticket)
49+
<div class="bg-white border border-gray-200 rounded-lg shadow-sm mb-4" x-data="{ open: false }">
50+
<div class="p-5 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
51+
<div class="flex-1 min-w-0">
52+
<div class="flex flex-wrap items-center gap-2 mb-1">
53+
<span class="text-xs font-semibold px-2 py-0.5 rounded-full
54+
{{ $ticket->status === 'open' ? 'bg-yellow-100 text-yellow-800' : '' }}
55+
{{ $ticket->status === 'in_progress' ? 'bg-blue-100 text-blue-800' : '' }}
56+
{{ $ticket->status === 'closed' ? 'bg-green-100 text-green-800' : '' }}
57+
{{ !in_array($ticket->status, ['open','in_progress','closed']) ? 'bg-gray-100 text-gray-600' : '' }}
58+
">
59+
{{ ucfirst(str_replace('_', ' ', $ticket->status ?? 'open')) }}
60+
</span>
61+
62+
@if($ticket->priority)
63+
<span class="text-xs px-2 py-0.5 rounded-full
64+
{{ $ticket->priority === 'high' ? 'bg-red-100 text-red-700' : '' }}
65+
{{ $ticket->priority === 'medium' ? 'bg-orange-100 text-orange-700' : '' }}
66+
{{ $ticket->priority === 'low' ? 'bg-gray-100 text-gray-600' : '' }}
67+
">{{ ucfirst($ticket->priority) }}</span>
68+
@endif
69+
70+
@if($ticket->labels)
71+
@foreach(explode(',', $ticket->labels) as $label)
72+
<span class="text-xs bg-indigo-100 text-indigo-700 px-2 py-0.5 rounded-full">{{ trim($label) }}</span>
73+
@endforeach
74+
@endif
75+
</div>
76+
77+
<a href="{{ route('tickets.show', $ticket) }}" class="font-semibold text-gray-900 hover:text-blue-600 truncate block">
78+
#{{ $ticket->id }}{{ $ticket->title }}
79+
</a>
80+
<p class="text-xs text-gray-400 mt-0.5">Door {{ $ticket->user->name }} &middot; {{ $ticket->created_at->diffForHumans() }}</p>
81+
</div>
82+
83+
<button @click="open = !open"
84+
class="flex items-center gap-1 text-sm text-blue-600 hover:text-blue-800 font-medium shrink-0">
85+
<span x-text="open ? 'Verberg' : 'Reacties'"></span>
86+
<span class="text-xs bg-blue-100 text-blue-700 rounded-full px-2 py-0.5">{{ $ticket->comments->count() }}</span>
87+
<svg class="w-4 h-4 transition-transform" :class="open ? 'rotate-180' : ''" fill="none" stroke="currentColor" viewBox="0 0 24 24">
88+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
89+
</svg>
90+
</button>
91+
</div>
92+
93+
<div x-show="open" x-cloak class="border-t border-gray-100 px-5 py-4 space-y-3 bg-gray-50 rounded-b-lg">
94+
@forelse($ticket->comments as $comment)
95+
<div class="flex gap-3">
96+
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-blue-600 text-white flex items-center justify-center text-xs font-bold">
97+
{{ strtoupper(substr($comment->user->name, 0, 1)) }}
98+
</div>
99+
<div class="flex-1">
100+
<p class="text-xs font-semibold text-gray-700">{{ $comment->user->name }}
101+
<span class="font-normal text-gray-400 ml-1">{{ $comment->created_at->diffForHumans() }}</span>
102+
</p>
103+
<p class="text-sm text-gray-800 mt-0.5">{{ $comment->body }}</p>
104+
</div>
105+
</div>
106+
@empty
107+
<p class="text-sm text-gray-400">Nog geen reacties.</p>
108+
@endforelse
109+
110+
<form action="{{ route('comments.store', $ticket) }}" method="POST" class="pt-2">
111+
@csrf
112+
<div class="flex gap-2">
113+
<input type="text" name="body" required placeholder="Schrijf een reactie..."
114+
class="flex-1 text-sm border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500">
115+
<button type="submit"
116+
class="bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium px-4 py-2 rounded">
117+
Stuur
118+
</button>
119+
</div>
120+
</form>
121+
</div>
122+
</div>
123+
@empty
124+
<p class="text-gray-500">Er zijn nog geen tickets.</p>
125+
@endforelse
126+
</div>
127+
</div>
128+
@endif
42129
</x-app-layout>
43130

44131
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

routes/web.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,21 @@
4343
})->name('faq');
4444

4545
Route::get('/dashboard', function () {
46-
$stats = Ticket::where('user_id', auth()->id())
46+
$user = auth()->user();
47+
48+
$stats = Ticket::where('user_id', $user->id)
4749
->selectRaw('status, COUNT(*) as total')
4850
->groupBy('status')
4951
->pluck('total', 'status');
5052

51-
return view('dashboard', compact('stats'));
53+
$recentTickets = Ticket::where('user_id', $user->id)->latest()->take(3)->get();
54+
55+
if ($user->isAdmin()) {
56+
$allTickets = Ticket::with(['user', 'comments.user'])->latest()->get();
57+
return view('dashboard', compact('stats', 'recentTickets', 'allTickets'));
58+
}
59+
60+
return view('dashboard', compact('stats', 'recentTickets'));
5261
})->middleware(['auth', 'verified'])->name('dashboard');
5362

5463
Route::middleware('auth')->group(function () {

0 commit comments

Comments
 (0)