Align Branches and Team settings UI with Care layout.
Deploy Ladill Frontdesk / deploy (push) Successful in 1m5s

Use the shared settings page and card pattern for branch, team,
buildings, and desks screens, with Settings breadcrumbs and a
Branches & team hub card on the organization settings page.
This commit is contained in:
isaacclad
2026-07-16 08:25:44 +00:00
parent fc072e71be
commit 4ce45f4989
9 changed files with 361 additions and 276 deletions
@@ -1,14 +1,37 @@
<x-app-layout title="Add branch"> <x-app-layout title="Add branch">
<div class="mx-auto max-w-lg"> <x-settings.page title="Add branch" description="Create a new location for visitors, devices, and staff.">
<a href="{{ route('frontdesk.branches.index') }}" class="text-sm text-slate-500 hover:text-slate-800">&larr; Branches</a> <div class="mb-4 flex flex-wrap items-center gap-2 text-sm">
<h1 class="mt-2 text-xl font-semibold text-slate-900">Add branch</h1> <a href="{{ route('frontdesk.settings') }}" class="text-slate-500 hover:text-slate-800">Settings</a>
<form method="POST" action="{{ route('frontdesk.branches.store') }}" class="mt-6 space-y-4 rounded-2xl border border-slate-200 bg-white p-6"> <span class="text-slate-300">/</span>
@csrf <a href="{{ route('frontdesk.branches.index') }}" class="text-slate-500 hover:text-slate-800">Branches</a>
<div><label class="block text-sm font-medium">Name</label><input type="text" name="name" required class="mt-1 w-full rounded-lg border-slate-300 text-sm"></div> <span class="text-slate-300">/</span>
<div><label class="block text-sm font-medium">Code</label><input type="text" name="code" class="mt-1 w-full rounded-lg border-slate-300 text-sm"></div> <span class="font-medium text-slate-900">Add</span>
<div><label class="block text-sm font-medium">Address</label><input type="text" name="address" class="mt-1 w-full rounded-lg border-slate-300 text-sm"></div> </div>
<div><label class="block text-sm font-medium">Phone</label><input type="tel" name="phone" class="mt-1 w-full rounded-lg border-slate-300 text-sm"></div>
<button type="submit" class="btn-primary w-full">Save</button> <x-settings.card title="Branch details">
</form> <form method="POST" action="{{ route('frontdesk.branches.store') }}" class="space-y-4">
</div> @csrf
<div>
<label class="block text-sm font-medium text-slate-700">Name</label>
<input type="text" name="name" value="{{ old('name') }}" required class="mt-1 w-full rounded-lg border-slate-300 text-sm">
</div>
<div>
<label class="block text-sm font-medium text-slate-700">Code</label>
<input type="text" name="code" value="{{ old('code') }}" class="mt-1 w-full rounded-lg border-slate-300 text-sm">
</div>
<div>
<label class="block text-sm font-medium text-slate-700">Address</label>
<input type="text" name="address" value="{{ old('address') }}" class="mt-1 w-full rounded-lg border-slate-300 text-sm">
</div>
<div>
<label class="block text-sm font-medium text-slate-700">Phone</label>
<input type="tel" name="phone" value="{{ old('phone') }}" class="mt-1 w-full rounded-lg border-slate-300 text-sm">
</div>
<div class="flex justify-end gap-3">
<a href="{{ route('frontdesk.branches.index') }}" class="rounded-xl border border-slate-200 px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50">Cancel</a>
<button type="submit" class="btn-primary">Save</button>
</div>
</form>
</x-settings.card>
</x-settings.page>
</x-app-layout> </x-app-layout>
@@ -1,15 +1,41 @@
<x-app-layout title="Edit branch"> <x-app-layout title="Edit branch">
<div class="mx-auto max-w-lg"> <x-settings.page title="Edit branch" description="Update location details for {{ $branch->name }}.">
<a href="{{ route('frontdesk.branches.index') }}" class="text-sm text-slate-500 hover:text-slate-800">&larr; Branches</a> <div class="mb-4 flex flex-wrap items-center gap-2 text-sm">
<h1 class="mt-2 text-xl font-semibold text-slate-900">Edit branch</h1> <a href="{{ route('frontdesk.settings') }}" class="text-slate-500 hover:text-slate-800">Settings</a>
<form method="POST" action="{{ route('frontdesk.branches.update', $branch) }}" class="mt-6 space-y-4 rounded-2xl border border-slate-200 bg-white p-6"> <span class="text-slate-300">/</span>
@csrf @method('PUT') <a href="{{ route('frontdesk.branches.index') }}" class="text-slate-500 hover:text-slate-800">Branches</a>
<div><label class="block text-sm font-medium">Name</label><input type="text" name="name" value="{{ old('name', $branch->name) }}" required class="mt-1 w-full rounded-lg border-slate-300 text-sm"></div> <span class="text-slate-300">/</span>
<div><label class="block text-sm font-medium">Code</label><input type="text" name="code" value="{{ old('code', $branch->code) }}" class="mt-1 w-full rounded-lg border-slate-300 text-sm"></div> <span class="font-medium text-slate-900">{{ $branch->name }}</span>
<div><label class="block text-sm font-medium">Address</label><input type="text" name="address" value="{{ old('address', $branch->address) }}" class="mt-1 w-full rounded-lg border-slate-300 text-sm"></div> </div>
<div><label class="block text-sm font-medium">Phone</label><input type="tel" name="phone" value="{{ old('phone', $branch->phone) }}" class="mt-1 w-full rounded-lg border-slate-300 text-sm"></div>
<label class="flex items-center gap-2 text-sm"><input type="checkbox" name="is_active" value="1" @checked(old('is_active', $branch->is_active))> Active</label> <x-settings.card title="Branch details">
<button type="submit" class="btn-primary w-full">Update</button> <form method="POST" action="{{ route('frontdesk.branches.update', $branch) }}" class="space-y-4">
</form> @csrf @method('PUT')
</div> <div>
<label class="block text-sm font-medium text-slate-700">Name</label>
<input type="text" name="name" value="{{ old('name', $branch->name) }}" required class="mt-1 w-full rounded-lg border-slate-300 text-sm">
</div>
<div>
<label class="block text-sm font-medium text-slate-700">Code</label>
<input type="text" name="code" value="{{ old('code', $branch->code) }}" class="mt-1 w-full rounded-lg border-slate-300 text-sm">
</div>
<div>
<label class="block text-sm font-medium text-slate-700">Address</label>
<input type="text" name="address" value="{{ old('address', $branch->address) }}" class="mt-1 w-full rounded-lg border-slate-300 text-sm">
</div>
<div>
<label class="block text-sm font-medium text-slate-700">Phone</label>
<input type="tel" name="phone" value="{{ old('phone', $branch->phone) }}" class="mt-1 w-full rounded-lg border-slate-300 text-sm">
</div>
<label class="flex items-center gap-2 text-sm text-slate-700">
<input type="checkbox" name="is_active" value="1" @checked(old('is_active', $branch->is_active))>
Active
</label>
<div class="flex justify-end gap-3">
<a href="{{ route('frontdesk.branches.index') }}" class="rounded-xl border border-slate-200 px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50">Cancel</a>
<button type="submit" class="btn-primary">Save changes</button>
</div>
</form>
</x-settings.card>
</x-settings.page>
</x-app-layout> </x-app-layout>
@@ -6,45 +6,41 @@
@endphp @endphp
<x-app-layout title="Branches"> <x-app-layout title="Branches">
<div class="space-y-6"> <x-settings.page title="Branches" description="Organize locations and sites so visits, devices, and staff stay scoped to the right branch.">
<a href="{{ route('frontdesk.settings') }}" class="inline-flex items-center gap-1 text-sm text-slate-500 hover:text-slate-800">&larr; Settings</a> <div class="mb-4 flex flex-wrap items-center justify-between gap-3">
<x-frontdesk.page-hero <div class="flex flex-wrap items-center gap-2 text-sm">
badge="Settings · Locations · Buildings" <a href="{{ route('frontdesk.settings') }}" class="text-slate-500 hover:text-slate-800">Settings</a>
title="Branches" <span class="text-slate-300">/</span>
description="Organize your sites, buildings, and reception desks so visits and devices stay scoped to the right location." <span class="font-medium text-slate-900">Branches</span>
:stats="[ @if (app(\App\Services\Frontdesk\FrontdeskPermissions::class)->can(
['value' => number_format($heroStats['total']), 'label' => 'Branches'], auth()->user() ? app(\App\Services\Frontdesk\OrganizationResolver::class)->memberFor(auth()->user(), $organization) : null,
['value' => number_format($heroStats['active']), 'label' => 'Active'], 'admin.members.view'
['value' => number_format($heroStats['buildings']), 'label' => 'Buildings'], ))
]"> <a href="{{ route('frontdesk.members.index') }}" class="ml-3 text-indigo-600 hover:text-indigo-800">Team</a>
@if ($canManageBranches) @endif
<x-slot name="actions">
<a href="{{ route('frontdesk.branches.create') }}" class="btn-primary">
<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>
Add branch
</a>
</x-slot>
@endif
</x-frontdesk.page-hero>
<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">All branches</h2>
</div> </div>
@if ($canManageBranches)
<a href="{{ route('frontdesk.branches.create') }}" class="btn-primary">Add branch</a>
@endif
</div>
<x-settings.card title="All branches" description="{{ number_format($heroStats['total']) }} total · {{ number_format($heroStats['active']) }} active · {{ number_format($heroStats['buildings']) }} building(s)">
@forelse ($branches as $branch) @forelse ($branches as $branch)
<div class="flex items-center justify-between border-b border-slate-50 px-6 py-4 last:border-b-0"> <div class="flex items-center justify-between gap-4 border-b border-slate-50 py-3 last:border-b-0">
<div> <div>
<p class="font-medium text-slate-900">{{ $branch->name }}</p> <p class="font-medium text-slate-900">{{ $branch->name }}</p>
<p class="text-sm text-slate-500">{{ $branch->address ?? 'No address' }} · {{ $branch->buildings_count }} building(s)</p> <p class="text-sm text-slate-500">{{ $branch->address ?? 'No address' }} · {{ $branch->buildings_count }} building(s)</p>
</div> </div>
<div class="flex gap-2"> <div class="flex items-center gap-3">
<x-btn href="{{ route('frontdesk.buildings.index', $branch) }}" variant="link" size="sm" plain>Buildings</x-btn> <a href="{{ route('frontdesk.buildings.index', $branch) }}" class="text-sm font-medium text-indigo-600 hover:text-indigo-800">Buildings</a>
<a href="{{ route('frontdesk.branches.edit', $branch) }}" class="text-sm text-slate-600 hover:text-slate-800">Edit</a> @if ($canManageBranches)
<a href="{{ route('frontdesk.branches.edit', $branch) }}" class="text-sm font-medium text-slate-600 hover:text-slate-900">Edit</a>
@endif
</div> </div>
</div> </div>
@empty @empty
<p class="px-6 py-10 text-center text-sm text-slate-500">No branches yet.</p> <p class="text-sm text-slate-500">No branches yet.</p>
@endforelse @endforelse
</div> </x-settings.card>
</div> </x-settings.page>
</x-app-layout> </x-app-layout>
@@ -1,43 +1,48 @@
<x-app-layout :title="$branch->name.' buildings'"> <x-app-layout :title="$branch->name.' buildings'">
<div class="flex items-center justify-between"> <x-settings.page title="{{ $branch->name }}" description="Buildings and reception desks for this branch.">
<div> <div class="mb-4 flex flex-wrap items-center gap-2 text-sm">
<x-btn href="{{ route('frontdesk.branches.index') }}" variant="link" size="sm" plain> Branches</x-btn> <a href="{{ route('frontdesk.settings') }}" class="text-slate-500 hover:text-slate-800">Settings</a>
<h1 class="mt-1 text-xl font-semibold text-slate-900">{{ $branch->name }}</h1> <span class="text-slate-300">/</span>
<a href="{{ route('frontdesk.branches.index') }}" class="text-slate-500 hover:text-slate-800">Branches</a>
<span class="text-slate-300">/</span>
<span class="font-medium text-slate-900">Buildings</span>
</div> </div>
</div>
<form method="POST" action="{{ route('frontdesk.buildings.store', $branch) }}" class="mt-6 flex gap-2 rounded-2xl border border-slate-200 bg-white p-4"> <x-settings.card title="Add building">
@csrf <form method="POST" action="{{ route('frontdesk.buildings.store', $branch) }}" class="flex flex-col gap-3 sm:flex-row">
<input type="text" name="name" placeholder="Building name" required class="flex-1 rounded-lg border-slate-300 text-sm"> @csrf
<input type="text" name="floor_count" placeholder="Floors" class="w-24 rounded-lg border-slate-300 text-sm"> <input type="text" name="name" placeholder="Building name" required class="flex-1 rounded-lg border-slate-300 text-sm">
<button type="submit" class="btn-primary">Add</button> <input type="text" name="floor_count" placeholder="Floors" class="w-full rounded-lg border-slate-300 text-sm sm:w-24">
</form> <button type="submit" class="btn-primary shrink-0">Add</button>
</form>
</x-settings.card>
<div class="mt-4 space-y-2"> <x-settings.card title="Buildings">
@forelse ($buildings as $building) @forelse ($buildings as $building)
<div class="flex items-center justify-between rounded-xl border border-slate-200 bg-white px-4 py-3"> <div class="flex items-center justify-between gap-4 border-b border-slate-50 py-3 last:border-b-0">
<div> <div>
<p class="font-medium">{{ $building->name }}</p> <p class="font-medium text-slate-900">{{ $building->name }}</p>
<p class="text-xs text-slate-500">{{ $building->reception_desks_count }} desk(s)</p> <p class="text-xs text-slate-500">{{ $building->reception_desks_count }} desk(s)</p>
</div>
<div class="flex items-center gap-3">
<a href="{{ route('frontdesk.desks.index', $building) }}" class="text-sm font-medium text-indigo-600 hover:text-indigo-800">Desks</a>
<x-confirm-dialog
:name="'remove-building-'.$building->id"
title="Remove this building?"
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>
<button type="button" class="text-sm font-medium text-red-600 hover:text-red-800">Remove</button>
</x-slot:trigger>
</x-confirm-dialog>
</div>
</div> </div>
<div class="btn-group"> @empty
<x-btn href="{{ route('frontdesk.desks.index', $building) }}" variant="link" size="sm" plain>Desks</x-btn> <p class="text-sm text-slate-500">No buildings yet.</p>
<x-confirm-dialog @endforelse
:name="'remove-building-'.$building->id" </x-settings.card>
title="Remove this building?" </x-settings.page>
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>
@empty
<p class="text-sm text-slate-500">No buildings yet.</p>
@endforelse
</div>
</x-app-layout> </x-app-layout>
@@ -1,35 +1,46 @@
<x-app-layout :title="$building->name.' desks'"> <x-app-layout :title="$building->name.' desks'">
<x-btn href="{{ route('frontdesk.buildings.index', $building->branch) }}" variant="link" size="sm" plain class="mb-1"> Buildings</x-btn> <x-settings.page title="{{ $building->name }}" description="Reception desks in this building.">
<h1 class="mt-1 text-xl font-semibold text-slate-900">{{ $building->name }}</h1> <div class="mb-4 flex flex-wrap items-center gap-2 text-sm">
<a href="{{ route('frontdesk.settings') }}" class="text-slate-500 hover:text-slate-800">Settings</a>
<span class="text-slate-300">/</span>
<a href="{{ route('frontdesk.branches.index') }}" class="text-slate-500 hover:text-slate-800">Branches</a>
<span class="text-slate-300">/</span>
<a href="{{ route('frontdesk.buildings.index', $building->branch) }}" class="text-slate-500 hover:text-slate-800">Buildings</a>
<span class="text-slate-300">/</span>
<span class="font-medium text-slate-900">Desks</span>
</div>
<form method="POST" action="{{ route('frontdesk.desks.store', $building) }}" class="mt-6 flex gap-2 rounded-2xl border border-slate-200 bg-white p-4"> <x-settings.card title="Add desk">
@csrf <form method="POST" action="{{ route('frontdesk.desks.store', $building) }}" class="flex flex-col gap-3 sm:flex-row">
<input type="text" name="name" placeholder="Desk name" required class="flex-1 rounded-lg border-slate-300 text-sm"> @csrf
<input type="text" name="location" placeholder="Location" class="flex-1 rounded-lg border-slate-300 text-sm"> <input type="text" name="name" placeholder="Desk name" required class="flex-1 rounded-lg border-slate-300 text-sm">
<button type="submit" class="btn-primary">Add</button> <input type="text" name="location" placeholder="Location" class="flex-1 rounded-lg border-slate-300 text-sm">
</form> <button type="submit" class="btn-primary shrink-0">Add</button>
</form>
</x-settings.card>
<div class="mt-4 space-y-2"> <x-settings.card title="Reception desks">
@forelse ($desks as $desk) @forelse ($desks as $desk)
<div class="flex items-center justify-between rounded-xl border border-slate-200 bg-white px-4 py-3"> <div class="flex items-center justify-between gap-4 border-b border-slate-50 py-3 last:border-b-0">
<div> <div>
<p class="font-medium">{{ $desk->name }}</p> <p class="font-medium text-slate-900">{{ $desk->name }}</p>
<p class="text-xs text-slate-500">{{ $desk->location ?? '—' }}</p> <p class="text-xs text-slate-500">{{ $desk->location ?? '—' }}</p>
</div>
<x-confirm-dialog
:name="'remove-desk-'.$desk->id"
title="Remove this desk?"
:action="route('frontdesk.desks.destroy', [$building, $desk])"
method="DELETE"
confirm-label="Remove desk"
>
<x-slot:trigger>
<button type="button" class="text-sm font-medium text-red-600 hover:text-red-800">Remove</button>
</x-slot:trigger>
</x-confirm-dialog>
</div> </div>
<x-confirm-dialog @empty
:name="'remove-desk-'.$desk->id" <p class="text-sm text-slate-500">No reception desks yet.</p>
title="Remove this desk?" @endforelse
:action="route('frontdesk.desks.destroy', [$building, $desk])" </x-settings.card>
method="DELETE" </x-settings.page>
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>
@empty
<p class="text-sm text-slate-500">No reception desks yet.</p>
@endforelse
</div>
</x-app-layout> </x-app-layout>
@@ -1,53 +1,66 @@
<x-app-layout title="Add member"> <x-app-layout title="Add member">
<div class="mx-auto max-w-lg"> <x-settings.page title="Invite team member" description="They get access only to Ladill Frontdesk (plus Mail if they already have a Ladill mailbox).">
<a href="{{ route('frontdesk.members.index') }}" class="text-sm text-slate-500 hover:text-slate-800">&larr; Team</a> <div class="mb-4 flex flex-wrap items-center gap-2 text-sm">
<h1 class="mt-2 text-xl font-semibold text-slate-900">Invite team member</h1> <a href="{{ route('frontdesk.settings') }}" class="text-slate-500 hover:text-slate-800">Settings</a>
<span class="text-slate-300">/</span>
<a href="{{ route('frontdesk.members.index') }}" class="text-slate-500 hover:text-slate-800">Team</a>
<span class="text-slate-300">/</span>
<span class="font-medium text-slate-900">Invite</span>
</div>
<form method="POST" x-data action="{{ route('frontdesk.members.store') }}" class="mt-6 space-y-4 rounded-2xl border border-slate-200 bg-white p-6"> <x-settings.card title="Invitation">
@csrf <form method="POST" action="{{ route('frontdesk.members.store') }}" class="space-y-4" x-data>
@csrf
@if (! empty($mailboxOptions))
<div>
<label class="block text-sm font-medium text-slate-700">From a Ladill mailbox</label>
<select x-on:change="if ($event.target.value) { $refs.email.value = $event.target.value }"
class="mt-1 w-full rounded-lg border-slate-300 text-sm">
<option value="">Choose a mailbox…</option>
@foreach ($mailboxOptions as $address)
<option value="{{ $address }}">{{ $address }}</option>
@endforeach
</select>
<p class="mt-1 text-xs text-slate-500">Or type any email below.</p>
</div>
@endif
@if (! empty($mailboxOptions))
<div> <div>
<label class="block text-sm font-medium text-slate-700">From a Ladill mailbox</label> <label class="block text-sm font-medium text-slate-700">Email address</label>
<select x-on:change="if ($event.target.value) { $refs.email.value = $event.target.value }" <input type="email" name="email" x-ref="email" value="{{ old('email') }}" required
class="mt-1 w-full rounded-lg border-slate-300 text-sm"> class="mt-1 w-full rounded-lg border-slate-300 text-sm"
<option value="">Choose a mailbox…</option> placeholder="colleague@company.com">
@foreach ($mailboxOptions as $address) <p class="mt-1 text-xs text-slate-500">They will receive an email to accept and join your Frontdesk organization.</p>
<option value="{{ $address }}">{{ $address }}</option> @error('email')
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<div>
<label class="block text-sm font-medium text-slate-700">Role</label>
<select name="role" class="mt-1 w-full rounded-lg border-slate-300 text-sm">
@foreach ($roles as $key => $label)
<option value="{{ $key }}" @selected(old('role') === $key)>{{ $label }}</option>
@endforeach @endforeach
</select> </select>
<p class="mt-1 text-xs text-slate-500">Or type any email below.</p>
</div> </div>
@endif
<div> <div>
<label class="block text-sm font-medium text-slate-700">Email address</label> <label class="block text-sm font-medium text-slate-700">Branch (optional)</label>
<input type="email" name="email" x-ref="email" value="{{ old('email') }}" required <select name="branch_id" class="mt-1 w-full rounded-lg border-slate-300 text-sm">
class="mt-1 w-full rounded-lg border-slate-300 text-sm" <option value="">All branches</option>
placeholder="colleague@company.com"> @foreach ($branches as $branch)
<p class="mt-1 text-xs text-slate-500">They will receive an email to accept and join your Frontdesk organization.</p> <option value="{{ $branch->id }}" @selected(old('branch_id') == $branch->id)>{{ $branch->name }}</option>
</div> @endforeach
</select>
</div>
<div> <div class="flex justify-end gap-3">
<label class="block text-sm font-medium text-slate-700">Role</label> <a href="{{ route('frontdesk.members.index') }}" class="rounded-xl border border-slate-200 px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50">Cancel</a>
<select name="role" class="mt-1 w-full rounded-lg border-slate-300 text-sm"> <button type="submit" class="btn-primary">Send invitation</button>
@foreach ($roles as $key => $label) </div>
<option value="{{ $key }}" @selected(old('role') === $key)>{{ $label }}</option> </form>
@endforeach </x-settings.card>
</select> </x-settings.page>
</div>
<div>
<label class="block text-sm font-medium text-slate-700">Branch (optional)</label>
<select name="branch_id" class="mt-1 w-full rounded-lg border-slate-300 text-sm">
<option value="">All branches</option>
@foreach ($branches as $branch)
<option value="{{ $branch->id }}" @selected(old('branch_id') == $branch->id)>{{ $branch->name }}</option>
@endforeach
</select>
</div>
<button type="submit" class="btn-primary">Send invitation</button>
</form>
</div>
</x-app-layout> </x-app-layout>
@@ -1,62 +1,76 @@
<x-app-layout title="Team"> @php
<div class="space-y-6"> $canManageMembers = app(\App\Services\Frontdesk\FrontdeskPermissions::class)->can(
<a href="{{ route('frontdesk.settings') }}" class="inline-flex items-center gap-1 text-sm text-slate-500 hover:text-slate-800">&larr; Settings</a> auth()->user() ? app(\App\Services\Frontdesk\OrganizationResolver::class)->memberFor(auth()->user(), $organization) : null,
<x-frontdesk.page-hero 'admin.members.manage'
badge="Settings · Roles · Access" );
title="Team" @endphp
description="Invite colleagues, assign Frontdesk roles, and limit branch access for reception and security staff."
:stats="[
['value' => number_format($heroStats['total']), 'label' => 'Members'],
['value' => number_format($heroStats['admins']), 'label' => 'Administrators'],
['value' => number_format($heroStats['branch_scoped']), 'label' => 'Branch-scoped'],
]">
<x-slot name="actions">
<a href="{{ route('frontdesk.members.create') }}" class="btn-primary">
<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>
Add member
</a>
</x-slot>
</x-frontdesk.page-hero>
<div class="overflow-hidden rounded-2xl border border-slate-200 bg-white"> <x-app-layout title="Team">
<div class="border-b border-slate-100 px-6 py-4"> <x-settings.page title="Team" description="Manage who can access Frontdesk, their roles, and which branch each member belongs to.">
<h2 class="text-sm font-semibold text-slate-900">Team members</h2> <div class="mb-4 flex flex-wrap items-center justify-between gap-3">
<div class="flex flex-wrap items-center gap-2 text-sm">
<a href="{{ route('frontdesk.settings') }}" class="text-slate-500 hover:text-slate-800">Settings</a>
<span class="text-slate-300">/</span>
<span class="font-medium text-slate-900">Team</span>
@if (app(\App\Services\Frontdesk\FrontdeskPermissions::class)->can(
auth()->user() ? app(\App\Services\Frontdesk\OrganizationResolver::class)->memberFor(auth()->user(), $organization) : null,
'admin.branches.view'
))
<a href="{{ route('frontdesk.branches.index') }}" class="ml-3 text-indigo-600 hover:text-indigo-800">Branches</a>
@endif
</div> </div>
<table class="min-w-full text-sm"> @if ($canManageMembers)
<thead class="bg-slate-50 text-left text-xs uppercase text-slate-500"> <a href="{{ route('frontdesk.members.create') }}" class="btn-primary">Add member</a>
<tr><th class="px-4 py-3">Member</th><th class="px-4 py-3">Role</th><th class="px-4 py-3">Branch</th><th class="px-4 py-3"></th></tr> @endif
</thead>
<tbody class="divide-y divide-slate-50">
@foreach ($members as $member)
@php
$display = str_contains($member->user_ref, '@')
? $member->user_ref
: (\App\Models\User::where('public_id', $member->user_ref)->value('email') ?? $member->user_ref);
@endphp
<tr>
<td class="px-4 py-3 text-sm">{{ $display }}</td>
<td class="px-4 py-3">{{ $roles[$member->role] ?? $member->role }}</td>
<td class="px-4 py-3">{{ $member->branch?->name ?? 'All branches' }}</td>
<td class="px-4 py-3 text-right">
@if ($member->user_ref !== auth()->user()->public_id)
<x-confirm-dialog
:name="'remove-member-'.$member->id"
title="Remove this member?"
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
</td>
</tr>
@endforeach
</tbody>
</table>
</div> </div>
</div>
<x-settings.card title="Team members" description="{{ number_format($heroStats['total']) }} members · {{ number_format($heroStats['admins']) }} administrators · {{ number_format($heroStats['branch_scoped']) }} branch-scoped">
@if ($members->isEmpty())
<p class="text-sm text-slate-500">No team members yet.</p>
@else
<div class="overflow-x-auto">
<table class="min-w-full text-sm">
<thead class="text-left text-xs uppercase text-slate-500">
<tr>
<th class="pb-2 pr-4">Member</th>
<th class="pb-2 pr-4">Role</th>
<th class="pb-2 pr-4">Branch</th>
<th class="pb-2"></th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100">
@foreach ($members as $member)
@php
$display = str_contains($member->user_ref, '@')
? $member->user_ref
: (\App\Models\User::where('public_id', $member->user_ref)->value('email') ?? $member->user_ref);
@endphp
<tr>
<td class="py-3 pr-4">{{ $display }}</td>
<td class="py-3 pr-4">{{ $roles[$member->role] ?? $member->role }}</td>
<td class="py-3 pr-4">{{ $member->branch?->name ?? 'All branches' }}</td>
<td class="py-3 text-right">
@if ($canManageMembers && $member->user_ref !== auth()->user()->public_id)
<x-confirm-dialog
:name="'remove-member-'.$member->id"
title="Remove this member?"
message="They will lose access to this Frontdesk organization."
:action="route('frontdesk.members.destroy', $member)"
method="DELETE"
confirm-label="Remove member"
>
<x-slot:trigger>
<button type="button" class="text-sm font-medium text-red-600 hover:text-red-800">Remove</button>
</x-slot:trigger>
</x-confirm-dialog>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
</x-settings.card>
</x-settings.page>
</x-app-layout> </x-app-layout>
@@ -3,7 +3,7 @@
$settings = $organization->settings ?? []; $settings = $organization->settings ?? [];
@endphp @endphp
<x-settings.page description="Configure branding, reception workflows, host alerts, and integrations for {{ $organization->name }}."> <x-settings.page title="Organization settings" description="Configure branding, reception workflows, host alerts, and access for {{ $organization->name }}.">
@if ($canManage) @if ($canManage)
<x-settings.card title="Plan & usage" description="Subscription limits and notification billing for {{ $organization->name }}."> <x-settings.card title="Plan & usage" description="Subscription limits and notification billing for {{ $organization->name }}.">
<div class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between"> <div class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
@@ -48,42 +48,40 @@
@endif @endif
@if ($canViewBranches || $canViewTeam) @if ($canViewBranches || $canViewTeam)
<div class="grid gap-4 sm:grid-cols-2"> <x-settings.card title="Branches & team" description="Locations and staff access for this organization. Multi-branch and team require Frontdesk Pro.">
@if ($canViewBranches) <ul class="space-y-3">
<a href="{{ route('frontdesk.branches.index') }}" @if ($canViewBranches)
class="group rounded-2xl border border-slate-200 bg-white p-5 shadow-sm transition hover:border-indigo-200 hover:shadow-md"> <li>
<div class="flex items-start justify-between gap-3"> <a href="{{ route('frontdesk.branches.index') }}"
<div> class="flex items-center justify-between rounded-xl border border-slate-100 bg-slate-50 px-4 py-3 text-sm text-slate-700 hover:text-indigo-700">
<p class="text-sm font-semibold text-slate-900 group-hover:text-indigo-700">Branches</p> <span>
<p class="mt-1 text-sm text-slate-500">Locations, buildings, and reception desks.</p> Branches
</div> @if (! $hasBranchesFeature)
@if (! $hasBranchesFeature) <span class="ml-2 rounded-full bg-amber-50 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-amber-700">Pro</span>
<span class="shrink-0 rounded-full bg-amber-50 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-amber-700">Pro</span> @endif
@endif <span class="mt-0.5 block text-xs text-slate-500">Locations, buildings, and reception desks</span>
</div> </span>
<p class="mt-4 text-xs font-medium text-indigo-600"> <span class="text-slate-400">&rarr;</span>
{{ $hasBranchesFeature ? 'Manage branches →' : 'Upgrade to unlock →' }} </a>
</p> </li>
</a> @endif
@endif @if ($canViewTeam)
@if ($canViewTeam) <li>
<a href="{{ route('frontdesk.members.index') }}" <a href="{{ route('frontdesk.members.index') }}"
class="group rounded-2xl border border-slate-200 bg-white p-5 shadow-sm transition hover:border-indigo-200 hover:shadow-md"> class="flex items-center justify-between rounded-xl border border-slate-100 bg-slate-50 px-4 py-3 text-sm text-slate-700 hover:text-indigo-700">
<div class="flex items-start justify-between gap-3"> <span>
<div> Team
<p class="text-sm font-semibold text-slate-900 group-hover:text-indigo-700">Team</p> @if (! $hasTeamFeature)
<p class="mt-1 text-sm text-slate-500">Invite colleagues and assign Frontdesk roles.</p> <span class="ml-2 rounded-full bg-amber-50 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-amber-700">Pro</span>
</div> @endif
@if (! $hasTeamFeature) <span class="mt-0.5 block text-xs text-slate-500">Invite colleagues and assign Frontdesk roles</span>
<span class="shrink-0 rounded-full bg-amber-50 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-amber-700">Pro</span> </span>
@endif <span class="text-slate-400">&rarr;</span>
</div> </a>
<p class="mt-4 text-xs font-medium text-indigo-600"> </li>
{{ $hasTeamFeature ? 'Manage team →' : 'Upgrade to unlock →' }} @endif
</p> </ul>
</a> </x-settings.card>
@endif
</div>
@endif @endif
<form method="POST" action="{{ route('frontdesk.settings.update') }}" enctype="multipart/form-data" class="space-y-6"> <form method="POST" action="{{ route('frontdesk.settings.update') }}" enctype="multipart/form-data" class="space-y-6">
@@ -1,21 +1,20 @@
<x-app-layout :title="$title"> <x-app-layout :title="$title">
<div class="mx-auto max-w-lg"> <x-settings.page :title="$title" :description="$description">
<p class="text-xs font-semibold uppercase tracking-wide text-slate-400">Settings</p> <div class="mb-4 flex flex-wrap items-center gap-2 text-sm">
<h1 class="mt-1 text-xl font-semibold text-slate-900">{{ $title }}</h1> <a href="{{ route('frontdesk.settings') }}" class="text-slate-500 hover:text-slate-800">Settings</a>
<p class="mt-2 text-sm text-slate-600">{{ $description }}</p> <span class="text-slate-300">/</span>
<span class="font-medium text-slate-900">{{ $title }}</span>
</div>
<div class="mt-6 rounded-2xl border border-indigo-200 bg-indigo-50 p-6"> <x-settings.card title="Upgrade to Pro">
<h2 class="font-semibold text-indigo-900">Upgrade to Pro</h2> <p class="text-sm text-slate-600">
<p class="mt-2 text-sm text-indigo-800">
GHS {{ number_format($proPriceMinor / 100, 0) }} / branch / month from your Ladill wallet GHS {{ number_format($proPriceMinor / 100, 0) }} / branch / month from your Ladill wallet
provided visitor kiosks, multi-branch locations, team roles, integrations, and more. provided visitor kiosks, multi-branch locations, team roles, integrations, and more.
</p> </p>
<a href="{{ route('frontdesk.pro.index') }}" class="btn-primary mt-4 inline-flex">View Pro plans</a> <div class="mt-4 flex flex-wrap items-center gap-3">
<p class="mt-3 text-xs text-indigo-700"> <a href="{{ route('frontdesk.pro.index') }}" class="btn-primary">View Pro plans</a>
<a href="{{ route('frontdesk.wallet') }}" class="underline">Top up your wallet</a> if you need more balance. <a href="{{ route('frontdesk.wallet') }}" class="text-sm font-medium text-indigo-600 hover:text-indigo-800">Top up wallet</a>
</p> </div>
</div> </x-settings.card>
</x-settings.page>
<a href="{{ route('frontdesk.settings') }}" class="mt-6 inline-block text-sm text-slate-600 hover:text-slate-900">&larr; Back to settings</a>
</div>
</x-app-layout> </x-app-layout>