Replace native browser confirms with Ladill confirm dialogs.
Deploy Ladill Frontdesk / deploy (push) Successful in 43s

Visits, devices, and admin destructive actions now use the shared modal UI.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-07-12 17:51:22 +00:00
co-authored by Cursor
parent 8300cafc36
commit 8f555bf2bf
10 changed files with 227 additions and 61 deletions
@@ -0,0 +1,69 @@
@props([
'name',
'title',
'message' => null,
'action',
'method' => 'POST',
'confirmLabel' => 'Confirm',
'cancelLabel' => 'Cancel',
'variant' => 'danger',
])
@php
$confirmBtnClass = $variant === 'danger'
? 'bg-red-600 hover:bg-red-700'
: 'bg-indigo-600 hover:bg-indigo-700';
$iconWrapClass = $variant === 'danger'
? 'bg-red-100 text-red-600'
: 'bg-indigo-100 text-indigo-600';
@endphp
@if(isset($trigger))
<span @click.stop="$dispatch('open-modal', {{ Js::from($name) }})" class="inline-flex">
{{ $trigger }}
</span>
@endif
<x-modal :name="$name" maxWidth="md">
<div class="px-5 pb-6 pt-2 sm:px-6 sm:pb-6 sm:pt-4">
<div class="flex flex-col items-center text-center sm:items-start sm:text-left">
<div @class(['flex h-12 w-12 items-center justify-center rounded-full', $iconWrapClass])>
@if($variant === 'danger')
<svg class="h-6 w-6" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"/>
</svg>
@else
<svg class="h-6 w-6" fill="none" stroke="currentColor" stroke-width="1.75" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z"/>
</svg>
@endif
</div>
<h2 class="mt-4 text-lg font-semibold text-slate-900">{{ $title }}</h2>
@if($message)
<p class="mt-2 text-sm leading-relaxed text-slate-500">{{ $message }}</p>
@endif
@isset($details)
<div class="mt-3 w-full">{{ $details }}</div>
@endisset
</div>
<form method="post" action="{{ $action }}" class="mt-6 flex flex-col-reverse gap-2.5 sm:flex-row sm:justify-end">
@csrf
@if(strtoupper($method) !== 'POST')
@method($method)
@endif
@isset($fields)
{{ $fields }}
@endisset
<button type="button"
@click="$dispatch('close-modal', {{ Js::from($name) }})"
class="rounded-xl border border-slate-200 bg-white px-4 py-2.5 text-sm font-semibold text-slate-700 transition hover:bg-slate-50">
{{ $cancelLabel }}
</button>
<button type="submit"
@class(['rounded-xl px-4 py-2.5 text-sm font-semibold text-white transition', $confirmBtnClass])>
{{ $confirmLabel }}
</button>
</form>
</div>
</x-modal>
@@ -22,10 +22,18 @@
</div> </div>
<div class="btn-group"> <div class="btn-group">
<x-btn href="{{ route('frontdesk.desks.index', $building) }}" variant="link" size="sm" plain>Desks</x-btn> <x-btn href="{{ route('frontdesk.desks.index', $building) }}" variant="link" size="sm" plain>Desks</x-btn>
<form method="POST" action="{{ route('frontdesk.buildings.destroy', [$branch, $building]) }}" onsubmit="return confirm('Remove this building?')"> <x-confirm-dialog
@csrf @method('DELETE') :name="'remove-building-'.$building->id"
<x-btn type="submit" variant="danger" size="sm">Remove</x-btn> title="Remove this building?"
</form> message="Reception desks linked to this building will also be removed."
:action="route('frontdesk.buildings.destroy', [$branch, $building])"
method="DELETE"
confirm-label="Remove building"
>
<x-slot:trigger>
<x-btn type="button" variant="danger" size="sm">Remove</x-btn>
</x-slot:trigger>
</x-confirm-dialog>
</div> </div>
</div> </div>
@empty @empty
@@ -16,10 +16,17 @@
<p class="font-medium">{{ $desk->name }}</p> <p class="font-medium">{{ $desk->name }}</p>
<p class="text-xs text-slate-500">{{ $desk->location ?? '—' }}</p> <p class="text-xs text-slate-500">{{ $desk->location ?? '—' }}</p>
</div> </div>
<form method="POST" action="{{ route('frontdesk.desks.destroy', [$building, $desk]) }}" onsubmit="return confirm('Remove this desk?')"> <x-confirm-dialog
@csrf @method('DELETE') :name="'remove-desk-'.$desk->id"
<x-btn type="submit" variant="danger" size="sm">Remove</x-btn> title="Remove this desk?"
</form> :action="route('frontdesk.desks.destroy', [$building, $desk])"
method="DELETE"
confirm-label="Remove desk"
>
<x-slot:trigger>
<x-btn type="button" variant="danger" size="sm">Remove</x-btn>
</x-slot:trigger>
</x-confirm-dialog>
</div> </div>
@empty @empty
<p class="text-sm text-slate-500">No reception desks yet.</p> <p class="text-sm text-slate-500">No reception desks yet.</p>
@@ -38,10 +38,18 @@
<td class="px-4 py-3">{{ $member->branch?->name ?? 'All branches' }}</td> <td class="px-4 py-3">{{ $member->branch?->name ?? 'All branches' }}</td>
<td class="px-4 py-3 text-right"> <td class="px-4 py-3 text-right">
@if ($member->user_ref !== auth()->user()->public_id) @if ($member->user_ref !== auth()->user()->public_id)
<form method="POST" action="{{ route('frontdesk.members.destroy', $member) }}" class="inline" onsubmit="return confirm('Remove this member?')"> <x-confirm-dialog
@csrf @method('DELETE') :name="'remove-member-'.$member->id"
<x-btn type="submit" variant="danger" size="sm">Remove</x-btn> title="Remove this member?"
</form> message="They will lose access to this Frontdesk organization."
:action="route('frontdesk.members.destroy', $member)"
method="DELETE"
confirm-label="Remove member"
>
<x-slot:trigger>
<x-btn type="button" variant="danger" size="sm">Remove</x-btn>
</x-slot:trigger>
</x-confirm-dialog>
@endif @endif
</td> </td>
</tr> </tr>
@@ -67,26 +67,44 @@
@if ($device->device_token) @if ($device->device_token)
<x-danger-zone title="Device actions" class="mt-4"> <x-danger-zone title="Device actions" class="mt-4">
<form method="POST" action="{{ route('frontdesk.devices.regenerate-token', $device) }}"> <x-confirm-dialog
@csrf :name="'regenerate-device-token-'.$device->id"
<x-btn title="Regenerate token?"
type="submit" message="Existing kiosk sessions will stop working until the device is reprovisioned."
variant="warning" :action="route('frontdesk.devices.regenerate-token', $device)"
onclick="return confirm('Regenerate token? Existing kiosk sessions will stop working.')" confirm-label="Regenerate token"
>Regenerate token</x-btn> variant="primary"
</form> >
<form method="POST" action="{{ route('frontdesk.devices.destroy', $device) }}" onsubmit="return confirm('Remove this device?')"> <x-slot:trigger>
@csrf @method('DELETE') <x-btn type="button" variant="warning">Regenerate token</x-btn>
<x-btn type="submit" variant="danger">Remove device</x-btn> </x-slot:trigger>
</form> </x-confirm-dialog>
<x-confirm-dialog
:name="'remove-device-'.$device->id"
title="Remove this device?"
:action="route('frontdesk.devices.destroy', $device)"
method="DELETE"
confirm-label="Remove device"
>
<x-slot:trigger>
<x-btn type="button" variant="danger">Remove device</x-btn>
</x-slot:trigger>
</x-confirm-dialog>
</x-danger-zone> </x-danger-zone>
@else @else
<form method="POST" action="{{ route('frontdesk.devices.destroy', $device) }}" class="mt-4" onsubmit="return confirm('Remove this device?')"> <x-danger-zone title="Device actions" class="mt-4">
@csrf @method('DELETE') <x-confirm-dialog
<x-danger-zone title="Device actions"> :name="'remove-device-'.$device->id"
<x-btn type="submit" variant="danger">Remove device</x-btn> title="Remove this device?"
</x-danger-zone> :action="route('frontdesk.devices.destroy', $device)"
</form> method="DELETE"
confirm-label="Remove device"
>
<x-slot:trigger>
<x-btn type="button" variant="danger">Remove device</x-btn>
</x-slot:trigger>
</x-confirm-dialog>
</x-danger-zone>
@endif @endif
</div> </div>
</x-app-layout> </x-app-layout>
@@ -68,11 +68,19 @@
<x-btn type="submit" class="flex-1">Save</x-btn> <x-btn type="submit" class="flex-1">Save</x-btn>
</div> </div>
</form> </form>
<form method="POST" action="{{ route('frontdesk.employees.destroy', $employee) }}" class="mt-4" onsubmit="return confirm('Remove this employee?')"> <x-danger-zone title="Employee actions" class="mt-4">
@csrf @method('DELETE') <x-confirm-dialog
<x-danger-zone title="Employee actions"> :name="'remove-employee-'.$employee->id"
<x-btn type="submit" variant="danger">Remove employee</x-btn> title="Remove this employee?"
</x-danger-zone> message="They will no longer be able to use the kiosk or appear in host lists."
</form> :action="route('frontdesk.employees.destroy', $employee)"
method="DELETE"
confirm-label="Remove employee"
>
<x-slot:trigger>
<x-btn type="button" variant="danger">Remove employee</x-btn>
</x-slot:trigger>
</x-confirm-dialog>
</x-danger-zone>
</div> </div>
</x-app-layout> </x-app-layout>
@@ -57,10 +57,19 @@
<button type="submit" class="btn-primary">Validate &amp; save SMS</button> <button type="submit" class="btn-primary">Validate &amp; save SMS</button>
</form> </form>
@if ($credential->sms_api_key_encrypted || $credential->hasValidSms()) @if ($credential->sms_api_key_encrypted || $credential->hasValidSms())
<form method="POST" action="{{ route('frontdesk.integrations.sms.disconnect') }}" class="mt-3" onsubmit="return confirm('Disconnect Ladill SMS?')"> <div class="mt-3">
@csrf <x-confirm-dialog
<button type="submit" class="text-sm text-rose-700 hover:underline dark:text-rose-400">Disconnect SMS</button> name="disconnect-sms"
</form> title="Disconnect Ladill SMS?"
message="Host SMS alerts will stop working until you connect a new key."
:action="route('frontdesk.integrations.sms.disconnect')"
confirm-label="Disconnect SMS"
>
<x-slot:trigger>
<button type="button" class="text-sm text-rose-700 hover:underline dark:text-rose-400">Disconnect SMS</button>
</x-slot:trigger>
</x-confirm-dialog>
</div>
@endif @endif
@endif @endif
</section> </section>
@@ -112,10 +121,19 @@
<button type="submit" class="btn-primary">Validate &amp; save Bird</button> <button type="submit" class="btn-primary">Validate &amp; save Bird</button>
</form> </form>
@if ($credential->bird_api_key_encrypted || $credential->hasValidBird()) @if ($credential->bird_api_key_encrypted || $credential->hasValidBird())
<form method="POST" action="{{ route('frontdesk.integrations.bird.disconnect') }}" class="mt-3" onsubmit="return confirm('Disconnect Ladill Bird?')"> <div class="mt-3">
@csrf <x-confirm-dialog
<button type="submit" class="text-sm text-rose-700 hover:underline dark:text-rose-400">Disconnect Bird</button> name="disconnect-bird"
</form> title="Disconnect Ladill Bird?"
message="Host email alerts will stop working until you connect a new key."
:action="route('frontdesk.integrations.bird.disconnect')"
confirm-label="Disconnect Bird"
>
<x-slot:trigger>
<button type="button" class="text-sm text-rose-700 hover:underline dark:text-rose-400">Disconnect Bird</button>
</x-slot:trigger>
</x-confirm-dialog>
</div>
@endif @endif
@endif @endif
</section> </section>
@@ -7,10 +7,18 @@
@if ($canManage) @if ($canManage)
<div class="btn-group"> <div class="btn-group">
<x-btn href="{{ route('frontdesk.visitors.check-in', $visitor) }}">Quick check-in</x-btn> <x-btn href="{{ route('frontdesk.visitors.check-in', $visitor) }}">Quick check-in</x-btn>
<form method="POST" action="{{ route('frontdesk.visitors.destroy', $visitor) }}" onsubmit="return confirm('Archive this visitor?')"> <x-confirm-dialog
@csrf @method('DELETE') :name="'archive-visitor-'.$visitor->id"
<x-btn type="submit" variant="danger">Archive</x-btn> title="Archive this visitor?"
</form> message="Their profile and visit history will be archived."
:action="route('frontdesk.visitors.destroy', $visitor)"
method="DELETE"
confirm-label="Archive visitor"
>
<x-slot:trigger>
<x-btn type="button" variant="danger">Archive</x-btn>
</x-slot:trigger>
</x-confirm-dialog>
</div> </div>
@endif @endif
</div> </div>
@@ -28,10 +28,17 @@
<x-btn type="submit">Check in</x-btn> <x-btn type="submit">Check in</x-btn>
</form> </form>
@elseif ($canManage && ! $visit->isInside()) @elseif ($canManage && ! $visit->isInside())
<form method="POST" action="{{ route('frontdesk.visits.destroy', $visit) }}" onsubmit="return confirm('Archive this visit?')"> <x-confirm-dialog
@csrf @method('DELETE') :name="'archive-visit-'.$visit->id"
<x-btn type="submit" variant="danger" size="sm">Archive</x-btn> title="Archive this visit?"
</form> :action="route('frontdesk.visits.destroy', $visit)"
method="DELETE"
confirm-label="Archive visit"
>
<x-slot:trigger>
<x-btn type="button" variant="danger" size="sm">Archive</x-btn>
</x-slot:trigger>
</x-confirm-dialog>
@endif @endif
</div> </div>
</div> </div>
@@ -48,11 +55,19 @@
<x-btn type="submit" variant="secondary" size="sm">Mark waiting</x-btn> <x-btn type="submit" variant="secondary" size="sm">Mark waiting</x-btn>
</form> </form>
@endif @endif
<form method="POST" action="{{ route('frontdesk.visits.cancel', $visit) }}" onsubmit="return confirm('Cancel this visit?')"> <x-confirm-dialog
@csrf :name="'cancel-visit-'.$visit->id"
<input type="hidden" name="reason" value="Cancelled by reception"> title="Cancel this visit?"
<x-btn type="submit" variant="danger" size="sm">Cancel visit</x-btn> :action="route('frontdesk.visits.cancel', $visit)"
</form> confirm-label="Cancel visit"
>
<x-slot:trigger>
<x-btn type="button" variant="danger" size="sm">Cancel visit</x-btn>
</x-slot:trigger>
<x-slot:fields>
<input type="hidden" name="reason" value="Cancelled by reception">
</x-slot:fields>
</x-confirm-dialog>
</div> </div>
@endif @endif
@@ -62,10 +62,17 @@
<td class="px-4 py-3 text-slate-500">{{ Str::limit($entry->reason, 60) ?: '—' }}</td> <td class="px-4 py-3 text-slate-500">{{ Str::limit($entry->reason, 60) ?: '—' }}</td>
<td class="px-4 py-3"> <td class="px-4 py-3">
@if ($canManage) @if ($canManage)
<form method="POST" action="{{ route('frontdesk.watchlist.destroy', $entry) }}" onsubmit="return confirm('Remove this watchlist entry?')"> <x-confirm-dialog
@csrf @method('DELETE') :name="'remove-watchlist-'.$entry->id"
<x-btn type="submit" variant="danger" size="sm">Remove</x-btn> title="Remove this watchlist entry?"
</form> :action="route('frontdesk.watchlist.destroy', $entry)"
method="DELETE"
confirm-label="Remove entry"
>
<x-slot:trigger>
<x-btn type="button" variant="danger" size="sm">Remove</x-btn>
</x-slot:trigger>
</x-confirm-dialog>
@endif @endif
</td> </td>
</tr> </tr>