dark mode supoport and new design now with delet esupport

This commit is contained in:
Mark Gregory
2026-08-27 17:28:02 +01:00
parent 6de15d1398
commit c8408658c9
8 changed files with 365 additions and 59 deletions
@@ -0,0 +1,165 @@
<?php
declare(strict_types=1);
namespace Modules\Helpdesk\Livewire;
use App\Models\User;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Livewire\Attributes\Url;
use Livewire\Component;
use Livewire\WithPagination;
use Modules\Helpdesk\Models\Ticket;
final class ArchivedTicketIndex extends Component
{
use AuthorizesRequests;
use WithPagination;
#[Url]
public string $search = '';
public bool $deleteModalOpen = false;
public ?int $pendingDeleteTicketId = null;
public ?string $pendingDeleteTicketReference = null;
public ?string $pendingDeleteTicketSubject = null;
public function updatedSearch(): void
{
$this->resetPage();
}
public function restoreTicket(int $ticketId): void
{
$ticket = $this->archivedTicket($ticketId);
$this->authorize('delete', $ticket);
$reference = $ticket->reference;
$ticket->restore();
$ticket->forceFill(['archived_at' => null])->save();
$this->resetPage();
$this->dispatch(
'thredary:toast',
type: 'success',
title: 'Ticket restored',
message: "Ticket {$reference} was restored to the active ticket list.",
);
}
public function askToDeletePermanently(int $ticketId): void
{
$ticket = $this->archivedTicket($ticketId);
$this->authorize('delete', $ticket);
$this->pendingDeleteTicketId = $ticket->id;
$this->pendingDeleteTicketReference = $ticket->reference;
$this->pendingDeleteTicketSubject = $ticket->subject;
$this->deleteModalOpen = true;
}
public function cancelPermanentDelete(): void
{
$this->resetDeleteState();
}
public function deletePermanently(): void
{
if ($this->pendingDeleteTicketId === null) {
$this->resetDeleteState();
return;
}
$ticket = $this->archivedTicket($this->pendingDeleteTicketId);
$this->authorize('delete', $ticket);
$reference = $ticket->reference;
DB::transaction(static function () use ($ticket): void {
$attachments = $ticket->attachments()->get(['id', 'disk', 'path']);
foreach ($attachments as $attachment) {
if (filled($attachment->disk) && filled($attachment->path)) {
Storage::disk($attachment->disk)->delete($attachment->path);
}
}
$ticket->forceDelete();
});
$this->resetDeleteState();
$this->resetPage();
$this->dispatch(
'thredary:toast',
type: 'success',
title: 'Ticket permanently deleted',
message: "Ticket {$reference} and its archived data were permanently deleted.",
);
}
public function render(): View
{
return view('modules.helpdesk.livewire.archived-ticket-index', [
'tickets' => $this->tickets(),
])->layout('layouts.app');
}
private function archivedTicket(int $ticketId): Ticket
{
return Ticket::query()
->onlyTrashed()
->visibleTo(Auth::user())
->findOrFail($ticketId);
}
/** @return LengthAwarePaginator<int, Ticket> */
private function tickets(): LengthAwarePaginator
{
$user = Auth::user();
$search = trim($this->search);
return Ticket::query()
->onlyTrashed()
->without('user')
->select('helpdesk_tickets.*')
->addSelect([
'requester_name' => User::query()
->select('name')
->whereColumn('users.id', 'helpdesk_tickets.user_id')
->limit(1),
'agent_name' => User::query()
->select('name')
->whereColumn('users.id', 'helpdesk_tickets.agent_id')
->limit(1),
])
->with(['status', 'priority', 'category', 'project', 'team'])
->withCount('attachments')
->visibleTo($user)
->when($search !== '', static function (Builder $query) use ($search): Builder {
return $query->where(static function (Builder $subQuery) use ($search): void {
$subQuery->where('reference', 'like', '%'.$search.'%')
->orWhere('subject', 'like', '%'.$search.'%')
->orWhere('body', 'like', '%'.$search.'%');
});
})
->orderByDesc('archived_at')
->orderByDesc('deleted_at')
->paginate(15);
}
private function resetDeleteState(): void
{
$this->deleteModalOpen = false;
$this->pendingDeleteTicketId = null;
$this->pendingDeleteTicketReference = null;
$this->pendingDeleteTicketSubject = null;
}
}
+26 -25
View File
@@ -22,13 +22,13 @@ final class TicketIndex extends Component
use AuthorizesRequests;
use WithPagination;
public bool $deleteModalOpen = false;
public bool $archiveModalOpen = false;
public ?int $pendingDeleteTicketId = null;
public ?int $pendingArchiveTicketId = null;
public ?string $pendingDeleteTicketReference = null;
public ?string $pendingArchiveTicketReference = null;
public ?string $pendingDeleteTicketSubject = null;
public ?string $pendingArchiveTicketSubject = null;
#[Url]
public string $search = '';
@@ -48,54 +48,55 @@ final class TicketIndex extends Component
}
/**
* Open the delete confirmation modal for a visible ticket.
* Open the archive confirmation modal for a visible ticket.
*/
public function askToDeleteTicket(int $ticketId): void
public function askToArchiveTicket(int $ticketId): void
{
$ticket = Ticket::query()
->visibleTo(Auth::user())
->findOrFail($ticketId);
$this->pendingDeleteTicketId = $ticket->id;
$this->pendingDeleteTicketReference = $ticket->reference;
$this->pendingDeleteTicketSubject = $ticket->subject;
$this->deleteModalOpen = true;
$this->pendingArchiveTicketId = $ticket->id;
$this->pendingArchiveTicketReference = $ticket->reference;
$this->pendingArchiveTicketSubject = $ticket->subject;
$this->archiveModalOpen = true;
}
public function cancelTicketDelete(): void
public function cancelTicketArchive(): void
{
$this->resetTicketDeletionState();
$this->resetTicketArchiveState();
}
/**
* Delete the confirmed ticket.
* Archive the confirmed ticket using Laravel soft deletes.
*/
public function confirmTicketDelete(): void
public function confirmTicketArchive(): void
{
if ($this->pendingDeleteTicketId === null) {
$this->resetTicketDeletionState();
if ($this->pendingArchiveTicketId === null) {
$this->resetTicketArchiveState();
return;
}
$ticket = Ticket::query()
->visibleTo(Auth::user())
->findOrFail($this->pendingDeleteTicketId);
->findOrFail($this->pendingArchiveTicketId);
$this->authorize('delete', $ticket);
$reference = $ticket->reference;
$ticket->forceFill(['archived_at' => now()])->save();
$ticket->delete();
$this->resetTicketDeletionState();
$this->resetTicketArchiveState();
$this->resetPage();
$this->dispatch(
'thredary:toast',
type: 'success',
title: 'Ticket deleted',
message: "Ticket {$reference} was deleted successfully.",
title: 'Ticket archived',
message: "Ticket {$reference} was moved to the archive.",
);
}
@@ -108,12 +109,12 @@ final class TicketIndex extends Component
])->layout('layouts.app');
}
private function resetTicketDeletionState(): void
private function resetTicketArchiveState(): void
{
$this->deleteModalOpen = false;
$this->pendingDeleteTicketId = null;
$this->pendingDeleteTicketReference = null;
$this->pendingDeleteTicketSubject = null;
$this->archiveModalOpen = false;
$this->pendingArchiveTicketId = null;
$this->pendingArchiveTicketReference = null;
$this->pendingArchiveTicketSubject = null;
}
/**
+2
View File
@@ -13,6 +13,7 @@ use Modules\Helpdesk\Http\Controllers\TicketControlsController;
use Modules\Helpdesk\Http\Controllers\TicketStoreController;
use Modules\Helpdesk\Http\Controllers\TicketTimeLogController;
use Modules\Helpdesk\Http\Controllers\WorkflowOpenController;
use Modules\Helpdesk\Livewire\ArchivedTicketIndex;
use Modules\Helpdesk\Livewire\KanbanBoard;
use Modules\Helpdesk\Livewire\SettingsManager;
use Modules\Helpdesk\Livewire\TicketCreate;
@@ -28,6 +29,7 @@ Route::get('/settings', SettingsManager::class)->name('settings');
Route::get('/kanban', KanbanBoard::class)->name('kanban');
Route::get('/tickets', TicketIndex::class)->name('tickets.index');
Route::get('/tickets/archived', ArchivedTicketIndex::class)->name('tickets.archived');
Route::post('/tickets', TicketStoreController::class)->name('tickets.store');
Route::get('/tickets/create', TicketCreate::class)->name('tickets.create');
Route::get('/tickets/{ticket}/edit', TicketEdit::class)->name('tickets.edit');
@@ -138,7 +138,8 @@
</div>
<div class="hd-nav-section"><div class="hd-nav-heading">Helpdesk</div>
@if(Route::has('helpdesk.tickets.index'))<a class="hd-nav-link hd-nav-link--tickets {{ request()->routeIs('helpdesk.tickets.*') ? 'is-active' : '' }}" href="{{ route('helpdesk.tickets.index') }}" wire:navigate><i class="fa-solid fa-ticket"></i><span>Tickets</span></a>@endif
@if(Route::has('helpdesk.tickets.index'))<a class="hd-nav-link hd-nav-link--tickets {{ request()->routeIs('helpdesk.tickets.index', 'helpdesk.tickets.create', 'helpdesk.tickets.show', 'helpdesk.tickets.edit') ? 'is-active' : '' }}" href="{{ route('helpdesk.tickets.index') }}" wire:navigate><i class="fa-solid fa-ticket"></i><span>Tickets</span></a>@endif
@if(Route::has('helpdesk.tickets.archived'))<a class="hd-nav-link {{ request()->routeIs('helpdesk.tickets.archived') ? 'is-active' : '' }}" style="--hd-nav-accent:#f59e0b;--hd-nav-accent-rgb:245,158,11" href="{{ route('helpdesk.tickets.archived') }}" wire:navigate><i class="fa-solid fa-box-archive"></i><span>Archive</span></a>@endif
@if(Route::has('helpdesk.kanban'))<a class="hd-nav-link hd-nav-link--kanban {{ request()->routeIs('helpdesk.kanban') ? 'is-active' : '' }}" href="{{ route('helpdesk.kanban') }}" wire:navigate><i class="fa-solid fa-table-columns"></i><span>Kanban</span></a>@endif
@if(Route::has('helpdesk.settings'))<a class="hd-nav-link hd-nav-link--settings {{ request()->routeIs('helpdesk.settings*') ? 'is-active' : '' }}" href="{{ route('helpdesk.settings') }}" wire:navigate><i class="fa-solid fa-gear"></i><span>Settings</span></a>@endif
</div>
@@ -138,7 +138,8 @@
</div>
<div class="hd-nav-section"><div class="hd-nav-heading">Helpdesk</div>
@if(Route::has('helpdesk.tickets.index'))<a class="hd-nav-link hd-nav-link--tickets {{ request()->routeIs('helpdesk.tickets.*') ? 'is-active' : '' }}" href="{{ route('helpdesk.tickets.index') }}" wire:navigate><i class="fa-solid fa-ticket"></i><span>Tickets</span></a>@endif
@if(Route::has('helpdesk.tickets.index'))<a class="hd-nav-link hd-nav-link--tickets {{ request()->routeIs('helpdesk.tickets.index', 'helpdesk.tickets.create', 'helpdesk.tickets.show', 'helpdesk.tickets.edit') ? 'is-active' : '' }}" href="{{ route('helpdesk.tickets.index') }}" wire:navigate><i class="fa-solid fa-ticket"></i><span>Tickets</span></a>@endif
@if(Route::has('helpdesk.tickets.archived'))<a class="hd-nav-link {{ request()->routeIs('helpdesk.tickets.archived') ? 'is-active' : '' }}" style="--hd-nav-accent:#f59e0b;--hd-nav-accent-rgb:245,158,11" href="{{ route('helpdesk.tickets.archived') }}" wire:navigate><i class="fa-solid fa-box-archive"></i><span>Archive</span></a>@endif
@if(Route::has('helpdesk.kanban'))<a class="hd-nav-link hd-nav-link--kanban {{ request()->routeIs('helpdesk.kanban') ? 'is-active' : '' }}" href="{{ route('helpdesk.kanban') }}" wire:navigate><i class="fa-solid fa-table-columns"></i><span>Kanban</span></a>@endif
@if(Route::has('helpdesk.settings'))<a class="hd-nav-link hd-nav-link--settings {{ request()->routeIs('helpdesk.settings*') ? 'is-active' : '' }}" href="{{ route('helpdesk.settings') }}" wire:navigate><i class="fa-solid fa-gear"></i><span>Settings</span></a>@endif
</div>
@@ -138,7 +138,8 @@
</div>
<div class="hd-nav-section"><div class="hd-nav-heading">Helpdesk</div>
@if(Route::has('helpdesk.tickets.index'))<a class="hd-nav-link hd-nav-link--tickets {{ request()->routeIs('helpdesk.tickets.*') ? 'is-active' : '' }}" href="{{ route('helpdesk.tickets.index') }}" wire:navigate><i class="fa-solid fa-ticket"></i><span>Tickets</span></a>@endif
@if(Route::has('helpdesk.tickets.index'))<a class="hd-nav-link hd-nav-link--tickets {{ request()->routeIs('helpdesk.tickets.index', 'helpdesk.tickets.create', 'helpdesk.tickets.show', 'helpdesk.tickets.edit') ? 'is-active' : '' }}" href="{{ route('helpdesk.tickets.index') }}" wire:navigate><i class="fa-solid fa-ticket"></i><span>Tickets</span></a>@endif
@if(Route::has('helpdesk.tickets.archived'))<a class="hd-nav-link {{ request()->routeIs('helpdesk.tickets.archived') ? 'is-active' : '' }}" style="--hd-nav-accent:#f59e0b;--hd-nav-accent-rgb:245,158,11" href="{{ route('helpdesk.tickets.archived') }}" wire:navigate><i class="fa-solid fa-box-archive"></i><span>Archive</span></a>@endif
@if(Route::has('helpdesk.kanban'))<a class="hd-nav-link hd-nav-link--kanban {{ request()->routeIs('helpdesk.kanban') ? 'is-active' : '' }}" href="{{ route('helpdesk.kanban') }}" wire:navigate><i class="fa-solid fa-table-columns"></i><span>Kanban</span></a>@endif
@if(Route::has('helpdesk.settings'))<a class="hd-nav-link hd-nav-link--settings {{ request()->routeIs('helpdesk.settings*') ? 'is-active' : '' }}" href="{{ route('helpdesk.settings') }}" wire:navigate><i class="fa-solid fa-gear"></i><span>Settings</span></a>@endif
</div>
@@ -0,0 +1,131 @@
<div class="hd-helpdesk-surface hd-mobile-ready space-y-6" dusk="helpdesk-ticket-archive">
<section class="overflow-hidden rounded-3xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
<div class="bg-gradient-to-br from-slate-950 via-slate-900 to-amber-950 px-6 py-7 text-white">
<div class="flex flex-col gap-5 lg:flex-row lg:items-center lg:justify-between">
<div>
<p class="text-xs font-semibold uppercase tracking-[0.28em] text-amber-200">Ticket archive</p>
<h1 class="mt-2 text-3xl font-semibold tracking-tight">Archived tickets</h1>
<p class="mt-2 max-w-3xl text-sm leading-6 text-amber-100/85">
Restore tickets to active work or permanently remove tickets that are no longer required.
</p>
</div>
<a href="{{ route('helpdesk.tickets.index') }}" wire:navigate class="hd-btn hd-btn-blue">
<i class="fa-solid fa-arrow-left" aria-hidden="true"></i><span>Active tickets</span>
</a>
</div>
</div>
</section>
<section class="rounded-2xl border border-zinc-200 bg-white p-4 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
<div class="hd-ticket-search-wrap max-w-2xl">
<i class="fa-solid fa-magnifying-glass hd-ticket-search-icon" aria-hidden="true"></i>
<input type="search" class="hd-ticket-search" wire:model.live.debounce.350ms="search"
placeholder="Search archived reference, subject or body" aria-label="Search archived tickets">
</div>
</section>
<section class="overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-zinc-200 text-sm dark:divide-zinc-700">
<thead class="bg-zinc-50 text-left text-xs font-semibold uppercase tracking-wide text-zinc-500 dark:bg-zinc-800 dark:text-zinc-300">
<tr>
<th class="px-4 py-3">Reference</th><th class="px-4 py-3">Subject</th>
<th class="px-4 py-3">Status</th><th class="px-4 py-3">Requester</th>
<th class="px-4 py-3">Archived</th><th class="px-4 py-3 text-center">Files</th>
<th class="px-4 py-3 text-right">Actions</th>
</tr>
</thead>
<tbody class="divide-y divide-zinc-100 dark:divide-zinc-800">
@forelse ($tickets as $ticket)
<tr class="hover:bg-zinc-50 dark:hover:bg-zinc-800/70">
<td class="whitespace-nowrap px-4 py-3 font-mono text-xs font-semibold text-blue-700 dark:text-blue-300">{{ $ticket->reference }}</td>
<td class="min-w-[260px] px-4 py-3">
<div class="font-semibold text-zinc-900 dark:text-zinc-100">{{ $ticket->subject }}</div>
<div class="mt-1 text-xs text-zinc-500 dark:text-zinc-400">
{{ $ticket->category?->name ?? 'No category' }}
@if ($ticket->project) · {{ $ticket->project->title }} @endif
@if ($ticket->team) · {{ $ticket->team->name }} @endif
</div>
</td>
<td class="px-4 py-3">
<x-helpdesk.colour-badge :color="$ticket->status?->color ?? 'zinc'">
{{ $ticket->status?->name ?? 'No status' }}
</x-helpdesk.colour-badge>
</td>
<td class="px-4 py-3 text-zinc-600 dark:text-zinc-300">{{ $ticket->requester_name ?? 'Unknown' }}</td>
<td class="whitespace-nowrap px-4 py-3">
<div class="font-medium text-zinc-700 dark:text-zinc-200">{{ ($ticket->archived_at ?? $ticket->deleted_at)?->format('d M Y H:i') }}</div>
<div class="text-xs text-zinc-500">{{ ($ticket->archived_at ?? $ticket->deleted_at)?->diffForHumans() }}</div>
</td>
<td class="px-4 py-3 text-center">
<span class="inline-flex items-center gap-1 rounded-full border border-zinc-200 bg-zinc-50 px-2.5 py-1 text-xs font-bold text-zinc-600 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-300">
<i class="fa-solid fa-paperclip"></i>{{ $ticket->attachments_count ?? 0 }}
</span>
</td>
<td class="whitespace-nowrap px-4 py-3 text-right">
<div class="inline-flex flex-wrap justify-end gap-2">
<button type="button" wire:click="restoreTicket({{ $ticket->id }})"
wire:loading.attr="disabled" wire:target="restoreTicket({{ $ticket->id }})"
class="hd-btn hd-btn-sm hd-btn-green">
<i class="fa-solid fa-rotate-left"></i><span>Restore</span>
</button>
@can('delete', $ticket)
<button type="button" wire:click="askToDeletePermanently({{ $ticket->id }})"
class="hd-btn hd-btn-sm hd-btn-red">
<i class="fa-solid fa-trash"></i><span>Delete permanently</span>
</button>
@endcan
</div>
</td>
</tr>
@empty
<tr>
<td colspan="7" class="px-4 py-14 text-center">
<span class="mx-auto grid h-14 w-14 place-items-center rounded-2xl bg-amber-500/15 text-amber-500">
<i class="fa-solid fa-box-open text-xl"></i>
</span>
<h2 class="mt-4 text-lg font-bold text-zinc-900 dark:text-white">Archive is empty</h2>
<p class="mt-1 text-sm text-zinc-500 dark:text-zinc-400">Tickets that you archive will appear here and can be restored later.</p>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<div class="flex flex-col gap-3 border-t border-zinc-200 bg-zinc-50/70 px-4 py-4 dark:border-zinc-700 dark:bg-zinc-950/35 sm:flex-row sm:items-center sm:justify-between">
<div class="text-sm font-medium text-zinc-500 dark:text-zinc-400">{{ $tickets->total() }} archived ticket{{ $tickets->total() === 1 ? '' : 's' }}</div>
<div>@if ($tickets->hasPages()) {{ $tickets->onEachSide(1)->links() }} @endif</div>
</div>
</section>
@if ($deleteModalOpen)
<div class="fixed inset-0 z-[9998] flex items-center justify-center bg-black/75 px-4 py-6 backdrop-blur-sm" role="dialog" aria-modal="true">
<div class="w-full max-w-lg overflow-hidden rounded-3xl border border-red-500/35 bg-zinc-950 text-zinc-100 shadow-2xl">
<div class="border-b border-red-500/25 bg-red-950/45 px-6 py-5">
<h2 class="text-xl font-black text-white">Delete permanently?</h2>
<p class="mt-1 text-sm leading-6 text-red-100/90">
<span class="font-mono font-bold">{{ $pendingDeleteTicketReference }}</span> will be permanently removed and cannot be restored.
</p>
</div>
<div class="space-y-3 px-6 py-5">
@if ($pendingDeleteTicketSubject)
<div class="rounded-2xl border border-zinc-800 bg-zinc-900 px-4 py-3">
<p class="text-xs font-bold uppercase tracking-[0.2em] text-zinc-500">Ticket</p>
<p class="mt-1 font-semibold text-zinc-100">{{ $pendingDeleteTicketSubject }}</p>
</div>
@endif
<p class="text-sm leading-6 text-zinc-300">Stored attachment files linked to this ticket are also deleted.</p>
</div>
<div class="flex flex-col-reverse gap-3 border-t border-zinc-800 bg-zinc-900/80 px-6 py-4 sm:flex-row sm:justify-end">
<button type="button" wire:click="cancelPermanentDelete" class="hd-btn hd-btn-sm hd-btn-blue"><i class="fa-solid fa-xmark"></i><span>Cancel</span></button>
<button type="button" wire:click="deletePermanently" wire:loading.attr="disabled" wire:target="deletePermanently"
class="hd-btn hd-btn-sm hd-btn-red">
<i class="fa-solid fa-trash"></i>
<span wire:loading.remove wire:target="deletePermanently">Delete permanently</span>
<span wire:loading wire:target="deletePermanently">Deleting...</span>
</button>
</div>
</div>
</div>
@endif
</div>
@@ -18,6 +18,10 @@
<i class="fa-solid fa-plus" aria-hidden="true"></i>
<span>New Ticket</span>
</a>
<a href="{{ route('helpdesk.tickets.archived') }}" wire:navigate class="hd-btn hd-btn-amber">
<i class="fa-solid fa-box-archive" aria-hidden="true"></i>
<span>Archived</span>
</a>
</div>
</div>
</div>
@@ -144,12 +148,12 @@
</td>
<td class="whitespace-nowrap px-4 py-3 text-right">
@can('delete', $ticket)
<button type="button" wire:click="askToDeleteTicket({{ $ticket->id }})"
wire:loading.attr="disabled" wire:target="askToDeleteTicket({{ $ticket->id }})"
class="hd-btn hd-btn-sm hd-btn-red disabled:cursor-not-allowed disabled:opacity-60"
title="Delete {{ $ticket->reference }}" dusk="delete-ticket-{{ $ticket->id }}">
<i class="fa-solid fa-trash" aria-hidden="true"></i>
<span>Delete</span>
<button type="button" wire:click="askToArchiveTicket({{ $ticket->id }})"
wire:loading.attr="disabled" wire:target="askToArchiveTicket({{ $ticket->id }})"
class="hd-btn hd-btn-sm hd-btn-amber disabled:cursor-not-allowed disabled:opacity-60"
title="Archive {{ $ticket->reference }}" dusk="delete-ticket-{{ $ticket->id }}">
<i class="fa-solid fa-box-archive" aria-hidden="true"></i>
<span>Archive</span>
</button>
@endcan
</td>
@@ -184,62 +188,62 @@
</div>
</div>
@if ($deleteModalOpen)
@if ($archiveModalOpen)
<div class="fixed inset-0 z-[9998] flex items-center justify-center bg-black/75 px-4 py-6 backdrop-blur-sm"
role="dialog" aria-modal="true" aria-labelledby="delete-ticket-modal-title"
wire:key="ticket-delete-modal-{{ $pendingDeleteTicketId ?? 'none' }}"
dusk="delete-ticket-confirmation-modal">
role="dialog" aria-modal="true" aria-labelledby="archive-ticket-modal-title"
wire:key="ticket-archive-modal-{{ $pendingArchiveTicketId ?? 'none' }}"
dusk="archive-ticket-confirmation-modal">
<div
class="w-full max-w-lg overflow-hidden rounded-3xl border border-red-500/30 bg-zinc-950 text-zinc-100 shadow-2xl">
<div class="border-b border-red-500/20 bg-red-950/40 px-6 py-5">
class="w-full max-w-lg overflow-hidden rounded-3xl border border-amber-500/30 bg-zinc-950 text-zinc-100 shadow-2xl">
<div class="border-b border-amber-500/20 bg-amber-950/35 px-6 py-5">
<div class="flex items-start gap-4">
<span
class="grid h-12 w-12 shrink-0 place-items-center rounded-2xl bg-red-600 text-white shadow-lg shadow-red-950/40">
class="grid h-12 w-12 shrink-0 place-items-center rounded-2xl bg-amber-500 text-zinc-950 shadow-lg shadow-amber-950/30">
<i class="fa-solid fa-triangle-exclamation" aria-hidden="true"></i>
</span>
<div class="min-w-0">
<h2 id="delete-ticket-modal-title" class="text-xl font-black tracking-tight text-white">
Delete ticket?
<h2 id="archive-ticket-modal-title" class="text-xl font-black tracking-tight text-white">
Archive ticket?
</h2>
<p class="mt-1 text-sm leading-6 text-red-100/90">
This will delete <span
class="font-mono font-bold">{{ $pendingDeleteTicketReference }}</span>. This
action cannot be undone.
<p class="mt-1 text-sm leading-6 text-amber-100/90">
This will move <span
class="font-mono font-bold">{{ $pendingArchiveTicketReference }}</span> to the ticket archive.
It can be restored later.
</p>
</div>
</div>
</div>
<div class="space-y-3 px-6 py-5">
@if ($pendingDeleteTicketSubject)
@if ($pendingArchiveTicketSubject)
<div class="rounded-2xl border border-zinc-800 bg-zinc-900 px-4 py-3">
<p class="text-xs font-bold uppercase tracking-[0.22em] text-zinc-500">Ticket subject</p>
<p class="mt-1 text-sm font-semibold text-zinc-100">{{ $pendingDeleteTicketSubject }}</p>
<p class="mt-1 text-sm font-semibold text-zinc-100">{{ $pendingArchiveTicketSubject }}</p>
</div>
@endif
<p class="text-sm leading-6 text-zinc-300">
Confirm only if this ticket was created in error or is no longer required.
Archived tickets are removed from active ticket lists and the Kanban board until restored.
</p>
</div>
<div
class="flex flex-col-reverse gap-3 border-t border-zinc-800 bg-zinc-900/80 px-6 py-4 sm:flex-row sm:justify-end">
<button type="button" wire:click="cancelTicketDelete" class="hd-btn hd-btn-sm hd-btn-blue"
dusk="cancel-delete-ticket">
<button type="button" wire:click="cancelTicketArchive" class="hd-btn hd-btn-sm hd-btn-blue"
dusk="cancel-archive-ticket">
<i class="fa-solid fa-xmark" aria-hidden="true"></i>
<span>Cancel</span>
</button>
<button type="button" wire:click="confirmTicketDelete" wire:loading.attr="disabled"
wire:target="confirmTicketDelete"
class="hd-btn hd-btn-sm hd-btn-red disabled:cursor-not-allowed disabled:opacity-60"
dusk="confirm-delete-ticket">
<i class="fa-solid fa-trash" aria-hidden="true"></i>
<span wire:loading.remove wire:target="confirmTicketDelete">Delete ticket</span>
<span wire:loading wire:target="confirmTicketDelete">Deleting...</span>
<button type="button" wire:click="confirmTicketArchive" wire:loading.attr="disabled"
wire:target="confirmTicketArchive"
class="hd-btn hd-btn-sm hd-btn-amber disabled:cursor-not-allowed disabled:opacity-60"
dusk="confirm-archive-ticket">
<i class="fa-solid fa-box-archive" aria-hidden="true"></i>
<span wire:loading.remove wire:target="confirmTicketArchive">Archive ticket</span>
<span wire:loading wire:target="confirmTicketArchive">Archiving...</span>
</button>
</div>
</div>