major helpdesk updates

This commit is contained in:
Mark Gregory
2026-06-15 13:47:58 +01:00
parent 330d6922a4
commit b668116b11
47 changed files with 2350 additions and 23 deletions
@@ -59,6 +59,20 @@
<form wire:submit="addComment" class="rounded-2xl border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
<h2 class="mb-4 text-base font-semibold text-zinc-900 dark:text-white">Add reply</h2>
@if ($cannedResponses->isNotEmpty())
<div class="mb-4 grid gap-3 md:grid-cols-[1fr_auto]">
<flux:select wire:model="canned_response_id" label="Canned response">
<option value="">Choose a saved response...</option>
@foreach ($cannedResponses as $response)
<option value="{{ $response->id }}">{{ $response->name }}</option>
@endforeach
</flux:select>
<div class="flex items-end">
<flux:button type="button" wire:click="applyCannedResponse" variant="ghost" icon="document-duplicate">Insert response</flux:button>
</div>
</div>
@endif
<x-helpdesk::html-editor dusk="ticket-comment-editor" wire:model="commentBody" label="Comment" rows="5" placeholder="Write a professional response..." />
@can('update', $ticket)
@@ -88,12 +102,67 @@
<dl class="space-y-3 text-sm">
<div class="flex justify-between gap-4"><dt class="text-zinc-500">Category</dt><dd>{{ $ticket->category?->name ?? 'None' }}</dd></div>
<div class="flex justify-between gap-4"><dt class="text-zinc-500">Project</dt><dd>{{ $ticket->project?->title ?? 'None' }}</dd></div>
<div class="flex justify-between gap-4"><dt class="text-zinc-500">Product/service</dt><dd>{{ $ticket->product?->name ?? 'None' }}</dd></div>
<div class="flex justify-between gap-4"><dt class="text-zinc-500">Team</dt><dd>{{ $ticket->team?->name ?? 'None' }}</dd></div>
<div class="flex justify-between gap-4"><dt class="text-zinc-500">Created</dt><dd>{{ $ticket->created_at->format('d M Y H:i') }}</dd></div>
<div class="flex justify-between gap-4"><dt class="text-zinc-500">Due</dt><dd>{{ $ticket->due_at?->format('d M Y') ?? 'No due date' }}</dd></div>
</dl>
</div>
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
<h2 class="mb-4 text-base font-semibold text-zinc-900 dark:text-white">SLA metrics</h2>
<div class="grid gap-3 text-sm">
<div class="rounded-xl border {{ $ticket->sla_first_response_breached ? 'border-red-300 bg-red-50 text-red-900 dark:border-red-900 dark:bg-red-950 dark:text-red-100' : 'border-emerald-200 bg-emerald-50 text-emerald-900 dark:border-emerald-900 dark:bg-emerald-950 dark:text-emerald-100' }} p-3">
<div class="text-xs font-semibold uppercase tracking-wide">First response</div>
<div class="mt-1">{{ $ticket->first_response_at?->format('d M Y H:i') ?? 'Awaiting response' }}</div>
<div class="mt-1 text-xs opacity-75">Due {{ $ticket->sla_first_response_due_at?->format('d M Y H:i') ?? 'Not set' }}</div>
</div>
<div class="rounded-xl border {{ $ticket->sla_resolution_breached ? 'border-red-300 bg-red-50 text-red-900 dark:border-red-900 dark:bg-red-950 dark:text-red-100' : 'border-blue-200 bg-blue-50 text-blue-900 dark:border-blue-900 dark:bg-blue-950 dark:text-blue-100' }} p-3">
<div class="text-xs font-semibold uppercase tracking-wide">Resolution</div>
<div class="mt-1">{{ $ticket->completed_at?->format('d M Y H:i') ?? 'Open' }}</div>
<div class="mt-1 text-xs opacity-75">Due {{ $ticket->sla_resolution_due_at?->format('d M Y H:i') ?? 'Not set' }}</div>
</div>
<div class="grid grid-cols-2 gap-2 text-center">
<div class="rounded-xl border border-zinc-200 p-3 dark:border-zinc-700"><div class="text-xl font-semibold">{{ $ticket->public_replies_count }}</div><div class="text-xs text-zinc-500">Public replies</div></div>
<div class="rounded-xl border border-zinc-200 p-3 dark:border-zinc-700"><div class="text-xl font-semibold">{{ $ticket->time_spent_minutes }}</div><div class="text-xs text-zinc-500">Minutes logged</div></div>
</div>
</div>
</div>
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
<h2 class="mb-4 text-base font-semibold text-zinc-900 dark:text-white">Requester profile</h2>
<dl class="space-y-3 text-sm">
<div class="flex justify-between gap-4"><dt class="text-zinc-500">Name</dt><dd>{{ $ticket->owner?->name ?? 'Unknown' }}</dd></div>
<div class="flex justify-between gap-4"><dt class="text-zinc-500">Email</dt><dd class="truncate">{{ $ticket->owner?->email ?? 'None' }}</dd></div>
<div class="flex justify-between gap-4"><dt class="text-zinc-500">Company</dt><dd>{{ $ticket->owner?->company?->name ?? 'Current company' }}</dd></div>
</dl>
@if ($ticket->tags->isNotEmpty())
<div class="mt-4 flex flex-wrap gap-2">
@foreach ($ticket->tags as $tag)
<span class="rounded-full px-2 py-1 text-xs font-semibold text-white" style="background-color: {{ \Modules\Helpdesk\Support\Color::css($tag->color) }}">{{ $tag->name }}</span>
@endforeach
</div>
@endif
</div>
@if ($ticket->checklistItems->isNotEmpty())
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
@php($completedChecks = $ticket->checklistItems->where('is_completed', true)->count())
<div class="mb-4 flex items-center justify-between gap-3">
<h2 class="text-base font-semibold text-zinc-900 dark:text-white">Checklist</h2>
<flux:badge color="blue">{{ $completedChecks }}/{{ $ticket->checklistItems->count() }}</flux:badge>
</div>
<div class="space-y-2">
@foreach ($ticket->checklistItems as $item)
<label class="flex items-start gap-3 rounded-xl border border-zinc-200 p-3 text-sm dark:border-zinc-700">
<input type="checkbox" wire:click="toggleChecklistItem({{ $item->id }})" @checked($item->is_completed) class="mt-1 rounded border-zinc-300">
<span class="{{ $item->is_completed ? 'text-zinc-500 line-through' : 'text-zinc-800 dark:text-zinc-100' }}">{{ $item->title }}</span>
</label>
@endforeach
</div>
</div>
@endif
<div class="rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
<h2 class="mb-4 text-base font-semibold text-zinc-900 dark:text-white">Git commits</h2>
<div class="space-y-2">