Build Laravel 13 ticket assistant with Docker, Livewire admin, and helpdesk scraper command

This commit is contained in:
SitiWeb
2026-04-29 13:11:39 +02:00
parent 141a1a3c9b
commit 3c4572bb12
58 changed files with 9377 additions and 455 deletions

View File

@@ -0,0 +1,3 @@
<x-layouts.admin title="Articles">
<livewire:admin.article-manager />
</x-layouts.admin>

View File

@@ -0,0 +1,3 @@
<x-layouts.admin title="Dashboard">
<livewire:admin.dashboard-overview />
</x-layouts.admin>

View File

@@ -0,0 +1,3 @@
<x-layouts.admin title="Tickets">
<livewire:admin.ticket-monitor />
</x-layouts.admin>

View File

@@ -0,0 +1,29 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ $title ?? 'Admin' }} - Ticket Assistant</title>
<script src="https://cdn.tailwindcss.com"></script>
@livewireStyles
</head>
<body class="bg-slate-100 text-slate-900">
<div class="min-h-screen">
<header class="bg-slate-900 text-white">
<div class="mx-auto max-w-7xl px-6 py-4 flex items-center justify-between">
<h1 class="text-lg font-semibold">Ticket Assistant Admin</h1>
<nav class="flex gap-3 text-sm">
<a href="{{ route('admin.dashboard') }}" class="hover:underline">Dashboard</a>
<a href="{{ route('admin.articles') }}" class="hover:underline">Articles</a>
<a href="{{ route('admin.tickets') }}" class="hover:underline">Tickets</a>
</nav>
</div>
</header>
<main class="mx-auto max-w-7xl px-6 py-6">
{{ $slot }}
</main>
</div>
@livewireScripts
</body>
</html>

View File

@@ -0,0 +1,28 @@
<div class="space-y-6">
<div class="bg-white rounded-xl p-4 shadow">
<h2 class="font-semibold mb-3">Nieuw Artikel</h2>
@if (session('success'))
<div class="mb-3 text-green-700 bg-green-100 p-2 rounded">{{ session('success') }}</div>
@endif
<form wire:submit="save" class="space-y-3">
<input wire:model="title" type="text" class="w-full border rounded p-2" placeholder="Titel">
@error('title') <p class="text-red-600 text-sm">{{ $message }}</p> @enderror
<textarea wire:model="content" class="w-full border rounded p-2 min-h-40" placeholder="Content"></textarea>
@error('content') <p class="text-red-600 text-sm">{{ $message }}</p> @enderror
<button class="bg-slate-900 text-white px-4 py-2 rounded" type="submit">Opslaan</button>
</form>
</div>
<div class="bg-white rounded-xl p-4 shadow">
<h2 class="font-semibold mb-3">Artikelen</h2>
<div class="space-y-3">
@foreach($articles as $article)
<div class="border rounded p-3">
<div class="font-medium">#{{ $article->id }} {{ $article->title }}</div>
<div class="text-sm text-slate-600">{{ \Illuminate\Support\Str::limit($article->content, 140) }}</div>
</div>
@endforeach
</div>
<div class="mt-4">{{ $articles->links() }}</div>
</div>
</div>

View File

@@ -0,0 +1,35 @@
<div class="space-y-6">
<div class="grid gap-4 md:grid-cols-4">
<div class="bg-white rounded-xl p-4 shadow">Articles<br><span class="text-2xl font-bold">{{ $stats['articles_count'] ?? 0 }}</span></div>
<div class="bg-white rounded-xl p-4 shadow">Tickets<br><span class="text-2xl font-bold">{{ $stats['tickets_count'] ?? 0 }}</span></div>
<div class="bg-white rounded-xl p-4 shadow">AI Decisions<br><span class="text-2xl font-bold">{{ $stats['decisions_count'] ?? 0 }}</span></div>
<div class="bg-white rounded-xl p-4 shadow">Feedback Accuracy<br><span class="text-2xl font-bold">{{ isset($stats['feedback_accuracy']) ? $stats['feedback_accuracy'].'%' : 'N/A' }}</span></div>
</div>
<div class="grid gap-6 lg:grid-cols-2">
<div class="bg-white rounded-xl p-4 shadow">
<h2 class="font-semibold mb-3">Recent Tickets</h2>
<ul class="space-y-2 text-sm">
@forelse($recentTickets as $ticket)
<li class="border-b pb-2">#{{ $ticket->id }} - {{ \Illuminate\Support\Str::limit($ticket->message, 100) }}</li>
@empty
<li>Geen tickets.</li>
@endforelse
</ul>
</div>
<div class="bg-white rounded-xl p-4 shadow">
<h2 class="font-semibold mb-3">Recent AI Decisions</h2>
<ul class="space-y-2 text-sm">
@forelse($recentDecisions as $decision)
<li class="border-b pb-2">
Ticket #{{ $decision->ticket_id }} -> Article #{{ $decision->article_id ?? 'N/A' }}
<span class="text-slate-500">({{ number_format($decision->confidence, 2) }})</span>
</li>
@empty
<li>Geen beslissingen.</li>
@endforelse
</ul>
</div>
</div>
</div>

View File

@@ -0,0 +1,19 @@
<div class="bg-white rounded-xl p-4 shadow">
<h2 class="font-semibold mb-3">Tickets + AI Decisions</h2>
<div class="space-y-3">
@foreach($tickets as $ticket)
<div class="border rounded p-3">
<div class="font-medium">Ticket #{{ $ticket->id }}</div>
<div class="text-sm text-slate-700 mb-2">{{ $ticket->message }}</div>
@php($decision = $ticket->decisions->first())
@if($decision)
<div class="text-sm">Article: #{{ $decision->article_id ?? 'N/A' }} | Confidence: {{ number_format($decision->confidence, 2) }}</div>
<div class="text-xs text-slate-500">{{ $decision->explanation }}</div>
@else
<div class="text-sm text-slate-500">Nog geen AI beslissing.</div>
@endif
</div>
@endforeach
</div>
<div class="mt-4">{{ $tickets->links() }}</div>
</div>