Add hero section to Watchlist index with entry stats and CTAs.
Deploy Ladill Frontdesk / deploy (push) Successful in 31s

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-07-05 20:58:20 +00:00
co-authored by Cursor
parent 0d74330e44
commit db7f0e04c7
2 changed files with 85 additions and 59 deletions
@@ -35,10 +35,20 @@ class WatchlistController extends Controller
->paginate(25) ->paginate(25)
->withQueryString(); ->withQueryString();
$statsQuery = WatchlistEntry::owned($this->ownerRef($request))
->where('organization_id', $organization->id);
$heroStats = [
'total' => (clone $statsQuery)->count(),
'requires_approval' => (clone $statsQuery)->where('status', Visitor::WATCHLIST_REQUIRES_APPROVAL)->count(),
'blacklisted' => (clone $statsQuery)->where('status', Visitor::WATCHLIST_BLACKLISTED)->count(),
];
return view('frontdesk.watchlist.index', [ return view('frontdesk.watchlist.index', [
'entries' => $entries, 'entries' => $entries,
'organization' => $organization, 'organization' => $organization,
'statuses' => config('frontdesk.watchlist_statuses'), 'statuses' => config('frontdesk.watchlist_statuses'),
'heroStats' => $heroStats,
'canManage' => app(\App\Services\Frontdesk\FrontdeskPermissions::class) 'canManage' => app(\App\Services\Frontdesk\FrontdeskPermissions::class)
->can($this->member($request), 'watchlist.manage'), ->can($this->member($request), 'watchlist.manage'),
]); ]);
@@ -1,66 +1,82 @@
<x-app-layout title="Watchlist"> <x-app-layout title="Watchlist">
<div class="flex items-center justify-between"> <div class="space-y-6">
<div> <x-frontdesk.page-hero
<h1 class="text-xl font-semibold text-slate-900">Watchlist</h1> badge="Security · Approvals · Blocked visitors"
<p class="text-sm text-slate-500">Flagged and blocked visitors</p> title="Watchlist"
</div> description="Flag visitors who need approval before entry or block them from checking in at your reception desks and kiosks."
@if ($canManage) :stats="[
<a href="{{ route('frontdesk.watchlist.create') }}" class="btn-primary">Add entry</a> ['value' => number_format($heroStats['total']), 'label' => 'Entries'],
@endif ['value' => number_format($heroStats['requires_approval']), 'label' => 'Needs approval'],
</div> ['value' => number_format($heroStats['blacklisted']), 'label' => 'Blacklisted'],
]">
<form method="GET" class="mt-4 flex flex-wrap gap-2"> <x-slot name="actions">
<input type="search" name="q" value="{{ request('q') }}" placeholder="Search name or company…" class="rounded-lg border-slate-300 text-sm"> <a href="{{ route('frontdesk.visitors.index') }}" class="btn-secondary">Visitor database</a>
<select name="status" class="rounded-lg border-slate-300 text-sm"> @if ($canManage)
<option value="">All statuses</option> <a href="{{ route('frontdesk.watchlist.create') }}" class="btn-primary">
@foreach ($statuses as $key => $label) <svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"/></svg>
@if ($key !== 'allowed') Add entry
<option value="{{ $key }}" @selected(request('status') === $key)>{{ $label }}</option> </a>
@endif @endif
@endforeach </x-slot>
</select> </x-frontdesk.page-hero>
<button type="submit" class="rounded-lg border border-slate-200 px-4 py-2 text-sm">Filter</button>
</form>
<div class="mt-4 overflow-hidden rounded-2xl border border-slate-200 bg-white"> <form method="GET" class="flex flex-wrap gap-2">
<table class="min-w-full divide-y divide-slate-100 text-sm"> <input type="search" name="q" value="{{ request('q') }}" placeholder="Search name or company…" class="rounded-lg border-slate-300 text-sm">
<thead class="bg-slate-50 text-left text-xs uppercase text-slate-500"> <select name="status" class="rounded-lg border-slate-300 text-sm">
<tr> <option value="">All statuses</option>
<th class="px-4 py-3">Name</th> @foreach ($statuses as $key => $label)
<th class="px-4 py-3">Company</th> @if ($key !== 'allowed')
<th class="px-4 py-3">Status</th> <option value="{{ $key }}" @selected(request('status') === $key)>{{ $label }}</option>
<th class="px-4 py-3">Reason</th> @endif
<th class="px-4 py-3"></th> @endforeach
</tr> </select>
</thead> <button type="submit" class="rounded-lg border border-slate-200 px-4 py-2 text-sm">Filter</button>
<tbody class="divide-y divide-slate-50"> </form>
@forelse ($entries as $entry)
<div class="overflow-hidden rounded-2xl border border-slate-200 bg-white">
<div class="border-b border-slate-100 px-6 py-4">
<h2 class="text-sm font-semibold text-slate-900">Watchlist entries</h2>
</div>
<table class="min-w-full divide-y divide-slate-100 text-sm">
<thead class="bg-slate-50 text-left text-xs uppercase text-slate-500">
<tr> <tr>
<td class="px-4 py-3 font-medium"> <th class="px-4 py-3">Name</th>
@if ($entry->visitor) <th class="px-4 py-3">Company</th>
<a href="{{ route('frontdesk.visitors.show', $entry->visitor) }}" class="text-indigo-700">{{ $entry->full_name }}</a> <th class="px-4 py-3">Status</th>
@else <th class="px-4 py-3">Reason</th>
{{ $entry->full_name }} <th class="px-4 py-3"></th>
@endif
</td>
<td class="px-4 py-3 text-slate-600">{{ $entry->company ?? '—' }}</td>
<td class="px-4 py-3 capitalize">{{ str_replace('_', ' ', $entry->status) }}</td>
<td class="px-4 py-3 text-slate-500">{{ Str::limit($entry->reason, 60) ?: '—' }}</td>
<td class="px-4 py-3">
@if ($canManage)
<form method="POST" action="{{ route('frontdesk.watchlist.destroy', $entry) }}" onsubmit="return confirm('Remove this watchlist entry?')">
@csrf @method('DELETE')
<button type="submit" class="text-sm text-red-600 hover:text-red-800">Remove</button>
</form>
@endif
</td>
</tr> </tr>
@empty </thead>
<tr><td colspan="5" class="px-4 py-8 text-center text-slate-400">No watchlist entries.</td></tr> <tbody class="divide-y divide-slate-50">
@endforelse @forelse ($entries as $entry)
</tbody> <tr>
</table> <td class="px-4 py-3 font-medium">
@if ($entry->visitor)
<a href="{{ route('frontdesk.visitors.show', $entry->visitor) }}" class="text-indigo-700">{{ $entry->full_name }}</a>
@else
{{ $entry->full_name }}
@endif
</td>
<td class="px-4 py-3 text-slate-600">{{ $entry->company ?? '—' }}</td>
<td class="px-4 py-3 capitalize">{{ str_replace('_', ' ', $entry->status) }}</td>
<td class="px-4 py-3 text-slate-500">{{ Str::limit($entry->reason, 60) ?: '—' }}</td>
<td class="px-4 py-3">
@if ($canManage)
<form method="POST" action="{{ route('frontdesk.watchlist.destroy', $entry) }}" onsubmit="return confirm('Remove this watchlist entry?')">
@csrf @method('DELETE')
<button type="submit" class="text-sm text-red-600 hover:text-red-800">Remove</button>
</form>
@endif
</td>
</tr>
@empty
<tr><td colspan="5" class="px-4 py-8 text-center text-slate-400">No watchlist entries.</td></tr>
@endforelse
</tbody>
</table>
@if ($entries->hasPages())
<div class="border-t border-slate-100 px-5 py-3">{{ $entries->links() }}</div>
@endif
</div>
</div> </div>
<div class="mt-4">{{ $entries->links() }}</div>
</x-app-layout> </x-app-layout>