ui updates for ticket layout

This commit is contained in:
Mark Gregory
2026-06-17 13:15:55 +01:00
parent 0b4c821187
commit a87c9762c9
8 changed files with 202 additions and 84 deletions
@@ -55,9 +55,12 @@ final class KanbanBoard extends Component
Auth::user() Auth::user()
); );
$this->board_id = $board?->id;
$message = 'Ticket '.$ticket->reference.' moved from '.$oldStatusName.' to '.$status->name.'.'; $message = 'Ticket '.$ticket->reference.' moved from '.$oldStatusName.' to '.$status->name.'.';
session()->flash('success', $message); session()->flash('success', $message);
$this->dispatch('wms:toast', type: 'success', title: 'Ticket moved', message: $message); $this->dispatch('wms:toast', type: 'success', title: 'Ticket moved', message: $message);
$this->dispatch('helpdesk-kanban-ticket-moved', ticketId: (int) $ticket->id, statusId: (int) $status->id);
} }
/** /**
@@ -95,6 +98,7 @@ final class KanbanBoard extends Component
$message = 'Ticket '.$fresh->reference.' moved from '.$oldBoardName.' to '.$board->name.'.'; $message = 'Ticket '.$fresh->reference.' moved from '.$oldBoardName.' to '.$board->name.'.';
session()->flash('success', $message); session()->flash('success', $message);
$this->dispatch('wms:toast', type: 'success', title: 'Board changed', message: $message); $this->dispatch('wms:toast', type: 'success', title: 'Board changed', message: $message);
$this->dispatch('helpdesk-kanban-ticket-moved', ticketId: (int) $fresh->id, statusId: (int) $fresh->status_id);
} }
/** /**
+34
View File
@@ -24,6 +24,7 @@ use Modules\Helpdesk\Models\Priority;
use Modules\Helpdesk\Models\Status; use Modules\Helpdesk\Models\Status;
use Modules\Helpdesk\Models\Ticket; use Modules\Helpdesk\Models\Ticket;
use Modules\Helpdesk\Services\CannedResponseRenderer; use Modules\Helpdesk\Services\CannedResponseRenderer;
use Modules\Helpdesk\Services\AttachmentService;
use Modules\Helpdesk\Services\ChecklistService; use Modules\Helpdesk\Services\ChecklistService;
use Modules\Helpdesk\Services\SlaService; use Modules\Helpdesk\Services\SlaService;
use Modules\Helpdesk\Services\TicketService; use Modules\Helpdesk\Services\TicketService;
@@ -43,6 +44,9 @@ final class TicketShow extends Component
/** @var array<int, TemporaryUploadedFile> */ /** @var array<int, TemporaryUploadedFile> */
public array $attachments = []; public array $attachments = [];
/** @var array<int, TemporaryUploadedFile> */
public array $supportingFiles = [];
public ?int $status_id = null; public ?int $status_id = null;
public ?int $priority_id = null; public ?int $priority_id = null;
@@ -259,6 +263,36 @@ final class TicketShow extends Component
$this->dispatch('helpdesk-agent-updated', agentId: $this->agent_id); $this->dispatch('helpdesk-agent-updated', agentId: $this->agent_id);
} }
/**
* Persist one or more supporting files directly against an existing ticket.
*
* This is intentionally separate from comment attachments so users can add
* evidence to an already-created ticket without posting another response.
*/
public function saveSupportingFiles(AttachmentService $attachments): void
{
$this->authorize('comment', $this->ticket);
$maxUploadKilobytes = (int) config('helpdesk.max_upload_kb', 10240);
$allowedMimes = implode(',', config('helpdesk.allowed_mimes', ['jpg', 'jpeg', 'png', 'pdf', 'txt', 'log', 'zip']));
$this->validate([
'supportingFiles' => ['required', 'array', 'min:1'],
'supportingFiles.*' => ['file', 'max:'.$maxUploadKilobytes, 'mimes:'.$allowedMimes],
]);
$user = Auth::user();
abort_if($user === null, 403);
$attachments->storeMany($this->ticket, $this->supportingFiles, (int) $user->id);
$this->reset('supportingFiles');
$this->ticket = $this->freshTicket($this->ticket);
session()->flash('ticket-supporting-files-added', 'Supporting files attached.');
$this->dispatch('helpdesk-supporting-files-added');
}
public function render(): View public function render(): View
{ {
return view('helpdesk::livewire.ticket-show', [ return view('helpdesk::livewire.ticket-show', [
@@ -254,11 +254,11 @@
This board is being initialised with the default workflow statuses. Refresh the page if the columns do not appear immediately. This board is being initialised with the default workflow statuses. Refresh the page if the columns do not appear immediately.
</div> </div>
@else @else
<div class="flex gap-4 overflow-x-auto pb-4" data-helpdesk-kanban-board> <div class="flex gap-4 overflow-x-auto pb-4" data-helpdesk-kanban-board wire:key="helpdesk-kanban-board-{{ $board?->id ?? 'none' }}">
@foreach ($statuses as $status) @foreach ($statuses as $status)
@php($tickets = $ticketsByStatus[$status->id] ?? collect()) @php($tickets = $ticketsByStatus[$status->id] ?? collect())
<section class="flex min-h-[640px] w-[330px] shrink-0 flex-col overflow-hidden rounded-2xl border border-zinc-200 bg-zinc-100 shadow-sm dark:border-zinc-700 dark:bg-zinc-950" dusk="kanban-column-{{ $status->slug }}"> <section wire:key="helpdesk-kanban-column-{{ $board?->id }}-{{ $status->id }}" class="flex min-h-[640px] w-[330px] shrink-0 flex-col overflow-hidden rounded-2xl border border-zinc-200 bg-zinc-100 shadow-sm dark:border-zinc-700 dark:bg-zinc-950" dusk="kanban-column-{{ $status->slug }}">
<header class="border-b border-zinc-200 bg-white px-4 py-4 dark:border-zinc-800 dark:bg-zinc-900"> <header class="border-b border-zinc-200 bg-white px-4 py-4 dark:border-zinc-800 dark:bg-zinc-900">
<div class="flex items-start justify-between gap-3"> <div class="flex items-start justify-between gap-3">
<div> <div>
@@ -281,6 +281,7 @@
data-helpdesk-kanban-ticket data-helpdesk-kanban-ticket
data-ticket-id="{{ $ticket->id }}" data-ticket-id="{{ $ticket->id }}"
dusk="kanban-ticket-{{ $ticket->id }}" dusk="kanban-ticket-{{ $ticket->id }}"
wire:key="helpdesk-kanban-ticket-{{ $ticket->id }}-{{ $ticket->status_id }}-{{ optional($ticket->updated_at)->timestamp ?? 0 }}"
> >
<div class="flex items-start justify-between gap-3"> <div class="flex items-start justify-between gap-3">
<div class="min-w-0"> <div class="min-w-0">
@@ -409,7 +410,9 @@
if (!ticketId || !statusId) return; if (!ticketId || !statusId) return;
$wire.moveTicket(ticketId, statusId); $wire.moveTicket(ticketId, statusId).then(function () {
initialiseHelpdeskKanban();
});
}); });
}); });
}; };
@@ -417,6 +420,8 @@
initialiseHelpdeskKanban(); initialiseHelpdeskKanban();
document.addEventListener('livewire:navigated', initialiseHelpdeskKanban); document.addEventListener('livewire:navigated', initialiseHelpdeskKanban);
document.addEventListener('livewire:updated', initialiseHelpdeskKanban); document.addEventListener('livewire:updated', initialiseHelpdeskKanban);
document.addEventListener('livewire:update', initialiseHelpdeskKanban);
document.addEventListener('helpdesk-kanban-ticket-moved', initialiseHelpdeskKanban);
</script> </script>
@endscript @endscript
</div> </div>
@@ -307,42 +307,114 @@
</div> </div>
</article> </article>
@if ($ticket->attachments->isNotEmpty()) <section class="overflow-hidden rounded-[2rem] border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900" data-helpdesk-supporting-files-card>
<details class="overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900"> <div class="bg-gradient-to-r from-slate-950 via-blue-950 to-slate-900 px-7 py-7 text-white sm:px-9">
<summary class="flex cursor-pointer list-none items-center justify-between gap-5 bg-gradient-to-r from-slate-950 via-blue-950 to-slate-900 px-7 py-7 text-white marker:hidden sm:px-9"> <div class="flex flex-col gap-5 xl:flex-row xl:items-center xl:justify-between">
<div class="flex min-w-0 items-start gap-5"> <div class="flex min-w-0 items-start gap-5">
<span class="inline-flex h-14 w-14 shrink-0 items-center justify-center rounded-2xl bg-blue-500/25 text-white ring-1 ring-white/20 shadow-lg shadow-black/20"> <span class="inline-flex h-14 w-14 shrink-0 items-center justify-center rounded-2xl bg-blue-500/25 text-white ring-1 ring-white/20 shadow-lg shadow-black/20">
<i class="fa-solid fa-paperclip text-xl" aria-hidden="true"></i> <i class="fa-solid fa-paperclip text-xl" aria-hidden="true"></i>
</span> </span>
<div class="min-w-0 space-y-2"> <div class="min-w-0 space-y-2">
<p class="text-xs font-black uppercase tracking-[0.22em] text-blue-200">Ticket attachments</p> <p class="text-xs font-black uppercase tracking-[0.22em] text-blue-200">Ticket attachments</p>
<h2 class="text-2xl font-extrabold tracking-tight sm:text-3xl">Supporting files</h2> <h2 class="text-2xl font-extrabold tracking-tight sm:text-3xl">Supporting files</h2>
<p class="text-base font-medium leading-7 text-blue-50/85">Screenshots, documents and logs linked to this ticket.</p> <p class="text-base font-medium leading-7 text-blue-50/85">Attach multiple screenshots, documents, logs and evidence directly to this existing ticket.</p>
</div>
</div> </div>
<span class="inline-flex w-fit shrink-0 items-center gap-3 rounded-2xl bg-white/15 px-5 py-3 text-base font-extrabold ring-1 ring-white/20 shadow-sm">
<i class="fa-solid fa-paperclip text-sm" aria-hidden="true"></i>
{{ $ticket->attachments->count() }} file{{ $ticket->attachments->count() === 1 ? '' : 's' }}
</span>
</div> </div>
<span class="inline-flex shrink-0 items-center gap-3 rounded-2xl bg-white/15 px-5 py-3 text-base font-extrabold ring-1 ring-white/20 shadow-sm">
<i class="fa-solid fa-paperclip text-sm" aria-hidden="true"></i>
{{ $ticket->attachments->count() }} file{{ $ticket->attachments->count() === 1 ? '' : 's' }}
<i class="fa-solid fa-chevron-right transition" aria-hidden="true"></i>
</span>
</summary>
<div class="grid gap-3 p-6 sm:grid-cols-2">
@foreach ($ticket->attachments as $attachment)
<a href="{{ route('helpdesk.attachments.download', $attachment->id) }}" target="_blank" class="group flex items-center justify-between gap-4 rounded-xl border border-zinc-200 p-4 text-sm transition hover:bg-zinc-50 dark:border-zinc-700 dark:hover:bg-zinc-800">
<span class="min-w-0">
<span class="block truncate font-bold text-zinc-900 dark:text-zinc-100">{{ $attachment->original_name ?? $attachment->original_filename ?? $attachment->filename ?? $attachment->file_name ?? basename($attachment->path ?? $attachment->file_path ?? '') }}</span>
<span class="block text-xs text-zinc-500">{{ Number::fileSize($attachment->size ?? $attachment->size_bytes ?? 0) }}</span>
</span>
<span class="inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-zinc-100 text-zinc-700 transition group-hover:bg-blue-600 group-hover:text-white dark:bg-zinc-800 dark:text-zinc-200">
<i class="fa-solid fa-download" aria-hidden="true"></i>
</span>
</a>
@endforeach
</div> </div>
</details>
@endif <div class="space-y-5 p-6 sm:p-7">
@if (session('ticket-supporting-files-added'))
<div class="rounded-2xl border border-emerald-300 bg-emerald-50 px-4 py-3 text-sm font-bold text-emerald-800 dark:border-emerald-800 dark:bg-emerald-950 dark:text-emerald-100">
{{ session('ticket-supporting-files-added') }}
</div>
@endif
@can('comment', $ticket)
<div class="rounded-[1.5rem] border border-blue-200 bg-blue-50/80 p-5 shadow-sm dark:border-blue-900/60 dark:bg-blue-950/30" data-helpdesk-attachment-shell>
<div class="flex min-h-[5.25rem] flex-col gap-4 sm:flex-row sm:items-center sm:justify-between" data-helpdesk-attachment-picker-row>
<div class="flex min-w-0 items-center gap-4">
<span class="inline-flex h-12 w-12 shrink-0 items-center justify-center rounded-2xl border border-blue-200 bg-white text-blue-700 shadow-sm dark:border-blue-800 dark:bg-zinc-950 dark:text-blue-200">
<i class="fa-solid fa-file-arrow-up text-base" aria-hidden="true"></i>
</span>
<span class="min-w-0">
<span class="block text-sm font-black text-zinc-900 dark:text-zinc-100">Attach supporting evidence</span>
<span class="block truncate text-xs font-semibold text-zinc-600 dark:text-zinc-300" data-helpdesk-supporting-file-label>
{{ count($supportingFiles) === 0 ? 'No files selected' : count($supportingFiles).' file'.(count($supportingFiles) === 1 ? '' : 's').' selected' }}
</span>
</span>
</div>
<div class="flex flex-wrap items-center justify-end gap-3">
<label data-helpdesk-file-button>
<i class="fa-solid fa-paperclip" aria-hidden="true"></i>
<span>Choose files</span>
<input id="helpdesk-supporting-files" type="file" multiple class="sr-only" wire:model="supportingFiles" data-helpdesk-supporting-file-input>
</label>
<button
type="button"
wire:click="saveSupportingFiles"
wire:loading.attr="disabled"
wire:target="supportingFiles,saveSupportingFiles"
@disabled(count($supportingFiles) === 0)
class="inline-flex min-h-12 items-center justify-center gap-2 rounded-2xl bg-emerald-600 px-5 py-3 text-sm font-black text-white shadow-sm transition hover:bg-emerald-700 disabled:cursor-not-allowed disabled:opacity-50"
>
<i class="fa-solid fa-cloud-arrow-up" aria-hidden="true"></i>
Save files
</button>
</div>
</div>
<div wire:loading wire:target="supportingFiles" class="mt-4 rounded-xl border border-blue-200 bg-white px-4 py-3 text-sm font-bold text-blue-800 dark:border-blue-800 dark:bg-zinc-950 dark:text-blue-100">
Preparing selected files…
</div>
@if (count($supportingFiles) > 0)
<div class="mt-4 rounded-2xl border border-zinc-200 bg-white p-4 dark:border-zinc-800 dark:bg-zinc-950">
<p class="mb-3 text-xs font-black uppercase tracking-[0.2em] text-zinc-500 dark:text-zinc-400">Ready to attach</p>
<ul class="space-y-2">
@foreach ($supportingFiles as $file)
<li class="flex items-center justify-between gap-3 rounded-xl border border-zinc-200 bg-zinc-50 px-3 py-2 text-sm font-semibold text-zinc-800 dark:border-zinc-800 dark:bg-zinc-900 dark:text-zinc-100">
<span class="truncate">{{ $file->getClientOriginalName() }}</span>
<span class="shrink-0 text-xs text-zinc-500">{{ Number::fileSize((int) $file->getSize()) }}</span>
</li>
@endforeach
</ul>
</div>
@endif
@error('supportingFiles') <div class="mt-2 text-sm font-bold text-red-600">{{ $message }}</div> @enderror
@error('supportingFiles.*') <div class="mt-2 text-sm font-bold text-red-600">{{ $message }}</div> @enderror
</div>
@endcan
@if ($ticket->attachments->isEmpty())
<div class="rounded-2xl border border-dashed border-zinc-300 bg-zinc-50 px-5 py-8 text-center text-sm font-semibold text-zinc-600 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-300">
No supporting files have been attached yet.
</div>
@else
<div class="grid gap-3 sm:grid-cols-2">
@foreach ($ticket->attachments as $attachment)
<a href="{{ route('helpdesk.attachments.download', $attachment->id) }}" target="_blank" class="group flex items-center justify-between gap-4 rounded-2xl border border-zinc-200 bg-white p-4 text-sm shadow-sm transition hover:-translate-y-0.5 hover:bg-zinc-50 hover:shadow-md dark:border-zinc-700 dark:bg-zinc-950 dark:hover:bg-zinc-900">
<span class="min-w-0">
<span class="block truncate font-black text-zinc-900 dark:text-zinc-100">{{ $attachment->original_name ?? $attachment->original_filename ?? $attachment->filename ?? $attachment->file_name ?? basename($attachment->path ?? $attachment->file_path ?? '') }}</span>
<span class="block text-xs font-semibold text-zinc-500">{{ Number::fileSize($attachment->size ?? $attachment->size_bytes ?? 0) }}</span>
</span>
<span class="inline-flex h-11 w-11 shrink-0 items-center justify-center rounded-2xl bg-blue-600 text-white shadow-sm transition group-hover:bg-blue-700">
<i class="fa-solid fa-download" aria-hidden="true"></i>
</span>
</a>
@endforeach
</div>
@endif
</div>
</section>
<section class="space-y-4"> <section class="space-y-4">
<details class="overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900" data-helpdesk-conversation-section> <details class="overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900" data-helpdesk-conversation-section>
@@ -505,50 +577,6 @@
@endif @endif
@endcan @endcan
<details class="mt-5 overflow-hidden rounded-[1.5rem] border border-zinc-200 bg-white/80 shadow-sm dark:border-zinc-800 dark:bg-zinc-950/50">
<summary class="group flex cursor-pointer list-none items-center justify-between gap-4 bg-gradient-to-r from-slate-950 via-blue-950 to-slate-900 px-6 py-5 text-white transition hover:from-slate-900 hover:via-blue-900 hover:to-slate-900 [&::-webkit-details-marker]:hidden">
<span class="flex min-w-0 items-center gap-4">
<span class="inline-flex h-11 w-11 shrink-0 items-center justify-center rounded-2xl border border-blue-400/30 bg-blue-500/15 text-blue-200 shadow-inner">
<i class="fa-solid fa-paperclip text-sm" aria-hidden="true"></i>
</span>
<span class="min-w-0">
<span class="block text-xs font-black uppercase tracking-[0.28em] text-blue-200">Attachments</span>
<span class="block text-xl font-black tracking-tight text-white">Supporting files</span>
<span class="block truncate text-sm font-semibold text-zinc-300">Pin screenshots, logs and supporting documents.</span>
</span>
</span>
<span class="flex shrink-0 items-center gap-3">
<span class="rounded-full border border-white/10 bg-white/10 px-3 py-1 text-xs font-black text-white" data-helpdesk-file-count>0 files</span>
<i class="fa-solid fa-chevron-down text-xs text-blue-200 transition group-open:rotate-180" aria-hidden="true"></i>
</span>
</summary>
<div class="border-t border-zinc-200 p-6 dark:border-zinc-800">
<div class="rounded-2xl border border-zinc-200 bg-zinc-50/80 p-5 shadow-sm dark:border-zinc-800 dark:bg-zinc-950/70">
<div class="flex min-h-[5.25rem] flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div class="flex min-w-0 items-center gap-4">
<span class="inline-flex h-11 w-11 shrink-0 items-center justify-center rounded-xl border border-zinc-300 bg-white text-zinc-700 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-200">
<i class="fa-solid fa-file-arrow-up text-sm" aria-hidden="true"></i>
</span>
<span class="min-w-0">
<span class="block text-sm font-black text-zinc-900 dark:text-zinc-100">Attach supporting evidence</span>
<span class="block truncate text-xs font-semibold text-zinc-500 dark:text-zinc-400" data-helpdesk-file-label>No files attached</span>
</span>
</div>
<div class="flex shrink-0 items-center justify-center sm:min-w-[190px]">
<label data-helpdesk-file-button>
<i class="fa-solid fa-paperclip" aria-hidden="true"></i>
<span>Attach files</span>
<input name="attachments[]" type="file" multiple class="sr-only" data-helpdesk-file-input>
</label>
</div>
</div>
</div>
@error('attachments.*') <div class="mt-2 text-sm text-red-600">{{ $message }}</div> @enderror
</div>
</details>
<div class="mt-6 flex justify-end"> <div class="mt-6 flex justify-end">
<button dusk="post-comment" type="submit" class="inline-flex items-center justify-center rounded-xl bg-emerald-600 px-5 py-3 text-sm font-bold text-white shadow-sm transition hover:bg-emerald-700 focus:outline-none focus:ring-2 focus:ring-emerald-400/70"> <button dusk="post-comment" type="submit" class="inline-flex items-center justify-center rounded-xl bg-emerald-600 px-5 py-3 text-sm font-bold text-white shadow-sm transition hover:bg-emerald-700 focus:outline-none focus:ring-2 focus:ring-emerald-400/70">
<i class="fa-solid fa-comments mr-2" aria-hidden="true"></i> <i class="fa-solid fa-comments mr-2" aria-hidden="true"></i>
@@ -677,11 +705,11 @@
}); });
} }
const fileInput = event.target.closest('[data-helpdesk-file-input]'); const fileInput = event.target.closest('[data-helpdesk-file-input], [data-helpdesk-supporting-file-input]');
if (fileInput) { if (fileInput) {
const panel = fileInput.closest('details') || fileInput.closest('div'); const panel = fileInput.closest('details') || fileInput.closest('div');
const label = panel?.querySelector('[data-helpdesk-file-label]'); const label = panel?.querySelector('[data-helpdesk-file-label], [data-helpdesk-supporting-file-label]');
const badge = panel?.querySelector('[data-helpdesk-file-count]'); const badge = panel?.querySelector('[data-helpdesk-file-count]');
const count = fileInput.files?.length || 0; const count = fileInput.files?.length || 0;
const text = count === 0 ? 'No files attached' : `${count} file${count === 1 ? '' : 's'} selected`; const text = count === 0 ? 'No files attached' : `${count} file${count === 1 ? '' : 's'} selected`;
@@ -56,7 +56,9 @@ final class KanbanBoardTest extends TestCase
Livewire::test(KanbanBoard::class) Livewire::test(KanbanBoard::class)
->call('moveTicket', $ticket->id, $lookups['closed']->id) ->call('moveTicket', $ticket->id, $lookups['closed']->id)
->assertHasNoErrors(); ->assertHasNoErrors()
->assertDispatched('helpdesk-kanban-ticket-moved')
->assertSee($ticket->reference);
$ticket->refresh(); $ticket->refresh();
@@ -116,6 +116,40 @@ final class TicketConversationTest extends TestCase
} }
public function test_existing_ticket_accepts_multiple_supporting_file_attachments(): void
{
Storage::fake((string) config('helpdesk.disk', 'public'));
$fixtures = $this->seedLookupData();
$owner = $this->user();
$ticket = $this->ticket(['owner' => $owner, 'status_id' => $fixtures['open']->id, 'priority_id' => $fixtures['normal']->id]);
Livewire::actingAs($owner)
->test(TicketShow::class, ['ticket' => $ticket])
->set('supportingFiles', [
UploadedFile::fake()->create('existing-ticket-log.txt', 8, 'text/plain'),
UploadedFile::fake()->create('existing-ticket-proof.pdf', 12, 'application/pdf'),
])
->call('saveSupportingFiles')
->assertHasNoErrors()
->assertDispatched('helpdesk-supporting-files-added');
self::assertSame(2, Attachment::query()->where('ticket_id', $ticket->id)->whereNull('comment_id')->count());
$this->assertDatabaseHas('helpdesk_attachments', [
'ticket_id' => $ticket->id,
'comment_id' => null,
'original_name' => 'existing-ticket-log.txt',
]);
$this->assertDatabaseHas('helpdesk_attachments', [
'ticket_id' => $ticket->id,
'comment_id' => null,
'original_name' => 'existing-ticket-proof.pdf',
]);
}
public function test_post_endpoint_accepts_multiple_comment_attachments(): void public function test_post_endpoint_accepts_multiple_comment_attachments(): void
{ {
Storage::fake((string) config('helpdesk.disk', 'public')); Storage::fake((string) config('helpdesk.disk', 'public'));
+11
View File
@@ -344,3 +344,14 @@ versions:
- Removed direct Helpdesk Vite manifest loading from ticket create when the module build manifest is not present. - Removed direct Helpdesk Vite manifest loading from ticket create when the module build manifest is not present.
- Updated Helpdesk conversation feature coverage for multiple attachments and super-admin response deletion. - Updated Helpdesk conversation feature coverage for multiple attachments and super-admin response deletion.
- Kept module.json version in sync with changelog.yaml. - Kept module.json version in sync with changelog.yaml.
- version: 1.9.20
date: 2026-06-17
changes:
- Moved existing-ticket attachment upload into the Supporting files card instead of the response form.
- Added Livewire-backed multi-file uploads directly against existing tickets without requiring a new response.
- Preserved normal response posting while removing the misplaced response attachment chooser from the ticket detail form.
- Improved the Supporting files attachment button styling and selected-file status display.
- Added Kanban board re-render keys and moved-ticket browser events so drag/drop status moves refresh reliably.
- Added feature coverage for existing-ticket multiple supporting file uploads and Kanban moved-ticket dispatch.
- Kept module.json version in sync with changelog.yaml.
+1 -1
View File
@@ -22,5 +22,5 @@
"Modules\\Helpdesk\\Providers\\HelpdeskServiceProvider" "Modules\\Helpdesk\\Providers\\HelpdeskServiceProvider"
], ],
"files": [], "files": [],
"version": "1.9.19" "version": "1.9.20"
} }