Move Devices under Settings like Branches and Team.
Deploy Ladill POS / deploy (push) Successful in 29s
Deploy Ladill POS / deploy (push) Successful in 29s
Drop Devices from the main sidebar, surface a Devices card on the Settings page, and nest device routes under /settings/devices with legacy redirects.
This commit is contained in:
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Pos;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Controllers\Pos\Concerns\ScopesToAccount;
|
||||
use App\Models\PosDevice;
|
||||
use App\Models\PosLocation;
|
||||
use App\Models\PosMember;
|
||||
use App\Models\PaymentGatewaySetting;
|
||||
@@ -41,9 +42,14 @@ class SettingsController extends Controller
|
||||
? PosLocation::owned($owner)->orderBy('name')->get()
|
||||
: PosLocation::owned($owner)->whereKey($scope)->orderBy('name')->get();
|
||||
|
||||
$devicesQuery = PosDevice::owned($owner)->with('location')->orderBy('name');
|
||||
$this->scopeToLocation($request, $devicesQuery);
|
||||
|
||||
return view('pos.settings', [
|
||||
'location' => $location,
|
||||
'branches' => $branches,
|
||||
'devices' => $devicesQuery->limit(10)->get(),
|
||||
'deviceTypes' => config('pos.device_types', []),
|
||||
'members' => PosMember::owned($owner)->with('location')->orderBy('created_at')->get(),
|
||||
'roles' => config('pos.roles', []),
|
||||
'hasMultiLocation' => $this->subscriptions->canUseMultiLocation($account),
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="m21 7.5-9-5.25L3 7.5m18 0-9 5.25m9-5.25v9l-9 5.25M3 7.5l9 5.25M3 7.5v9l9 5.25m0-9v9" />'],
|
||||
['name' => 'Sales', 'route' => route('pos.sales.index'), 'active' => request()->routeIs('pos.sales.*'),
|
||||
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v12m-3-2.818.879.659c1.171.879 3.07.879 4.242 0 1.172-.879 1.172-2.303 0-3.182C13.536 12.219 12.768 12 12 12c-.725 0-1.45-.22-2.003-.659-1.106-.879-1.106-2.303 0-3.182s2.9-.879 4.006 0l.415.33M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />'],
|
||||
['name' => 'Devices', 'route' => route('pos.devices.index'), 'active' => request()->routeIs('pos.devices*'),
|
||||
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M9 17.25v1.007a3 3 0 0 1-.879 2.122L7.5 21h9l-.621-.621A3 3 0 0 1 15 18.257V17.25m6-12V15a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 15V5.25A2.25 2.25 0 0 1 5.25 3h13.5A2.25 2.25 0 0 1 21 5.25Z" />'],
|
||||
];
|
||||
|
||||
// Restaurant mode adds the floor + kitchen display, right after Register.
|
||||
@@ -40,9 +38,12 @@
|
||||
</nav>
|
||||
|
||||
<div class="shrink-0 border-t border-slate-100 px-3 py-3">
|
||||
@php
|
||||
$settingsActive = request()->routeIs('pos.settings*', 'pos.devices.*', 'pos.branches.*', 'pos.team.*');
|
||||
@endphp
|
||||
<a href="{{ route('pos.settings') }}"
|
||||
class="group flex items-center gap-3 rounded-lg px-3 py-2 text-[13px] transition {{ request()->routeIs('pos.settings*') ? 'bg-indigo-50 text-indigo-700 font-semibold' : 'text-slate-600 hover:bg-slate-50 hover:text-slate-900' }}">
|
||||
<svg class="h-[18px] w-[18px] shrink-0 {{ request()->routeIs('pos.settings*') ? 'text-indigo-600' : 'text-slate-400' }}" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
||||
class="group flex items-center gap-3 rounded-lg px-3 py-2 text-[13px] transition {{ $settingsActive ? 'bg-indigo-50 text-indigo-700 font-semibold' : 'text-slate-600 hover:bg-slate-50 hover:text-slate-900' }}">
|
||||
<svg class="h-[18px] w-[18px] shrink-0 {{ $settingsActive ? 'text-indigo-600' : 'text-slate-400' }}" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" /><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
|
||||
</svg>
|
||||
<span class="flex-1 truncate">Settings</span>
|
||||
|
||||
@@ -1,51 +1,58 @@
|
||||
<x-app-layout title="Add device">
|
||||
<div class="mx-auto max-w-lg space-y-6">
|
||||
<div>
|
||||
<a href="{{ route('pos.devices.index') }}" class="text-sm font-medium text-slate-500 hover:text-slate-800">← Devices</a>
|
||||
<h1 class="mt-2 text-2xl font-semibold text-slate-900">Register device</h1>
|
||||
<p class="mt-1 text-sm text-slate-500">Name the hardware and link it to a branch when needed.</p>
|
||||
<x-settings.page description="Name the hardware and link it to a branch when needed.">
|
||||
<div class="mb-4 flex flex-wrap items-center gap-1 text-sm text-slate-500">
|
||||
<a href="{{ route('pos.settings') }}" class="hover:text-slate-800">Settings</a>
|
||||
<span class="px-1">/</span>
|
||||
<a href="{{ route('pos.devices.index') }}" class="hover:text-slate-800">Devices</a>
|
||||
<span class="px-1">/</span>
|
||||
<span class="font-medium text-slate-900">Add</span>
|
||||
</div>
|
||||
|
||||
@if (session('error'))
|
||||
<div class="rounded-xl border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-800">{{ session('error') }}</div>
|
||||
<div class="mb-4 rounded-xl border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-800">{{ session('error') }}</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('pos.devices.store') }}"
|
||||
class="space-y-4 rounded-2xl border border-slate-200 bg-white p-6"
|
||||
x-data="{ type: @js(old('type', 'register')), hints: @js($hints) }">
|
||||
@csrf
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700">Name</label>
|
||||
<input type="text" name="name" value="{{ old('name') }}" required placeholder="Front till · Customer screen"
|
||||
class="mt-1.5 w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
@error('name') <p class="mt-1 text-xs text-rose-600">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700">Type</label>
|
||||
<select name="type" required x-model="type"
|
||||
class="mt-1.5 w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
@foreach ($deviceTypes as $key => $label)
|
||||
<option value="{{ $key }}">{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-2 text-xs text-slate-500" x-text="hints[type] || ''"></p>
|
||||
@error('type') <p class="mt-1 text-xs text-rose-600">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700">Branch</label>
|
||||
<select name="location_id"
|
||||
class="mt-1.5 w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
<option value="">— Optional / all branches —</option>
|
||||
@foreach ($locations as $location)
|
||||
<option value="{{ $location->id }}" @selected((string) old('location_id', $defaultLocationId) === (string) $location->id)>
|
||||
{{ $location->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-xs text-slate-400">Required for customer and kitchen displays.</p>
|
||||
@error('location_id') <p class="mt-1 text-xs text-rose-600">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
<button type="submit" class="btn-primary w-full">Register device</button>
|
||||
</form>
|
||||
</div>
|
||||
<x-settings.card title="Register device">
|
||||
<form method="POST" action="{{ route('pos.devices.store') }}"
|
||||
class="space-y-4"
|
||||
x-data="{ type: @js(old('type', 'register')), hints: @js($hints) }">
|
||||
@csrf
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700">Name</label>
|
||||
<input type="text" name="name" value="{{ old('name') }}" required placeholder="Front till · Customer screen"
|
||||
class="mt-1.5 w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
@error('name') <p class="mt-1 text-xs text-rose-600">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700">Type</label>
|
||||
<select name="type" required x-model="type"
|
||||
class="mt-1.5 w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
@foreach ($deviceTypes as $key => $label)
|
||||
<option value="{{ $key }}">{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-2 text-xs text-slate-500" x-text="hints[type] || ''"></p>
|
||||
@error('type') <p class="mt-1 text-xs text-rose-600">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700">Branch</label>
|
||||
<select name="location_id"
|
||||
class="mt-1.5 w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
<option value="">— Optional / all branches —</option>
|
||||
@foreach ($locations as $location)
|
||||
<option value="{{ $location->id }}" @selected((string) old('location_id', $defaultLocationId) === (string) $location->id)>
|
||||
{{ $location->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-xs text-slate-400">Required for customer and kitchen displays.</p>
|
||||
@error('location_id') <p class="mt-1 text-xs text-rose-600">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-3 pt-1">
|
||||
<a href="{{ route('pos.settings') }}#devices" class="inline-flex items-center justify-center rounded-xl border border-slate-200 px-4 py-2.5 text-sm font-medium text-slate-700 hover:bg-slate-50">Cancel</a>
|
||||
<button type="submit" class="btn-primary">Register device</button>
|
||||
</div>
|
||||
</form>
|
||||
</x-settings.card>
|
||||
</x-settings.page>
|
||||
</x-app-layout>
|
||||
|
||||
@@ -1,96 +1,101 @@
|
||||
<x-app-layout title="Edit device">
|
||||
<div class="mx-auto max-w-lg space-y-6">
|
||||
<div>
|
||||
<a href="{{ route('pos.devices.index') }}" class="text-sm font-medium text-slate-500 hover:text-slate-800">← Devices</a>
|
||||
<h1 class="mt-2 text-2xl font-semibold text-slate-900">Edit device</h1>
|
||||
<p class="mt-1 text-sm text-slate-500">{{ $deviceTypes[$device->type] ?? $device->type }} · {{ $device->location?->name ?? 'No branch' }}</p>
|
||||
<x-settings.page description="{{ ($deviceTypes[$device->type] ?? $device->type) }} · {{ $device->location?->name ?? 'No branch' }}">
|
||||
<div class="mb-4 flex flex-wrap items-center gap-1 text-sm text-slate-500">
|
||||
<a href="{{ route('pos.settings') }}" class="hover:text-slate-800">Settings</a>
|
||||
<span class="px-1">/</span>
|
||||
<a href="{{ route('pos.devices.index') }}" class="hover:text-slate-800">Devices</a>
|
||||
<span class="px-1">/</span>
|
||||
<span class="font-medium text-slate-900">{{ $device->name }}</span>
|
||||
</div>
|
||||
|
||||
@if (session('success'))
|
||||
<div class="rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-800">{{ session('success') }}</div>
|
||||
<div class="mb-4 rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-800">{{ session('success') }}</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('pos.devices.update', $device) }}"
|
||||
class="space-y-4 rounded-2xl border border-slate-200 bg-white p-6"
|
||||
x-data="{ type: @js(old('type', $device->type)), hints: @js($hints) }">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700">Name</label>
|
||||
<input type="text" name="name" value="{{ old('name', $device->name) }}" required
|
||||
class="mt-1.5 w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700">Type</label>
|
||||
<select name="type" required x-model="type"
|
||||
class="mt-1.5 w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
@foreach ($deviceTypes as $key => $label)
|
||||
<option value="{{ $key }}">{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-2 text-xs text-slate-500" x-text="hints[type] || ''"></p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700">Status</label>
|
||||
<select name="status"
|
||||
class="mt-1.5 w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
@foreach (['online', 'offline', 'maintenance'] as $status)
|
||||
<option value="{{ $status }}" @selected(old('status', $device->status) === $status)>{{ ucfirst($status) }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700">Branch</label>
|
||||
<select name="location_id"
|
||||
class="mt-1.5 w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
<option value="">— Optional / all branches —</option>
|
||||
@foreach ($locations as $location)
|
||||
<option value="{{ $location->id }}" @selected((string) old('location_id', $device->location_id) === (string) $location->id)>
|
||||
{{ $location->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@if ($device->device_token)
|
||||
<div class="rounded-xl border border-slate-100 bg-slate-50 p-4">
|
||||
<p class="text-xs font-semibold uppercase tracking-wide text-slate-500">Device token</p>
|
||||
<code class="mt-2 block break-all text-xs text-slate-700">{{ $device->device_token }}</code>
|
||||
@if ($openUrl)
|
||||
<a href="{{ $openUrl }}" target="_blank" rel="noopener"
|
||||
class="mt-3 inline-flex text-sm font-semibold text-indigo-600 hover:text-indigo-800">
|
||||
Open device URL →
|
||||
</a>
|
||||
@endif
|
||||
<p class="mt-2 text-xs text-slate-500">Last online: {{ $device->last_online_at?->diffForHumans() ?? 'Never' }}</p>
|
||||
<x-settings.card title="Edit {{ $device->name }}">
|
||||
<form method="POST" action="{{ route('pos.devices.update', $device) }}"
|
||||
class="space-y-4"
|
||||
x-data="{ type: @js(old('type', $device->type)), hints: @js($hints) }">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700">Name</label>
|
||||
<input type="text" name="name" value="{{ old('name', $device->name) }}" required
|
||||
class="mt-1.5 w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700">Type</label>
|
||||
<select name="type" required x-model="type"
|
||||
class="mt-1.5 w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
@foreach ($deviceTypes as $key => $label)
|
||||
<option value="{{ $key }}">{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-2 text-xs text-slate-500" x-text="hints[type] || ''"></p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700">Status</label>
|
||||
<select name="status"
|
||||
class="mt-1.5 w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
@foreach (['online', 'offline', 'maintenance'] as $status)
|
||||
<option value="{{ $status }}" @selected(old('status', $device->status) === $status)>{{ ucfirst($status) }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700">Branch</label>
|
||||
<select name="location_id"
|
||||
class="mt-1.5 w-full rounded-xl border-slate-300 text-sm shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
<option value="">— Optional / all branches —</option>
|
||||
@foreach ($locations as $location)
|
||||
<option value="{{ $location->id }}" @selected((string) old('location_id', $device->location_id) === (string) $location->id)>
|
||||
{{ $location->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="flex gap-3 pt-1">
|
||||
<a href="{{ route('pos.devices.index') }}" class="inline-flex flex-1 items-center justify-center rounded-xl border border-slate-200 px-4 py-2.5 text-sm font-medium text-slate-700 hover:bg-slate-50">Back</a>
|
||||
<button type="submit" class="btn-primary flex-1">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
@if ($device->device_token)
|
||||
<div class="rounded-xl border border-slate-100 bg-slate-50 p-4">
|
||||
<p class="text-xs font-semibold uppercase tracking-wide text-slate-500">Device token</p>
|
||||
<code class="mt-2 block break-all text-xs text-slate-700">{{ $device->device_token }}</code>
|
||||
@if ($openUrl)
|
||||
<a href="{{ $openUrl }}" target="_blank" rel="noopener"
|
||||
class="mt-3 inline-flex text-sm font-semibold text-indigo-600 hover:text-indigo-800">
|
||||
Open device URL →
|
||||
</a>
|
||||
@endif
|
||||
<p class="mt-2 text-xs text-slate-500">Last online: {{ $device->last_online_at?->diffForHumans() ?? 'Never' }}</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="space-y-3 rounded-2xl border border-rose-100 bg-white p-5">
|
||||
<p class="text-sm font-semibold text-slate-900">Device actions</p>
|
||||
@if ($device->device_token)
|
||||
<form method="POST" action="{{ route('pos.devices.regenerate-token', $device) }}"
|
||||
onsubmit="return confirm('Regenerate token? Existing display sessions will stop until reopened.');">
|
||||
<div class="flex flex-wrap gap-3 pt-1">
|
||||
<a href="{{ route('pos.settings') }}#devices" class="inline-flex items-center justify-center rounded-xl border border-slate-200 px-4 py-2.5 text-sm font-medium text-slate-700 hover:bg-slate-50">Back</a>
|
||||
<button type="submit" class="btn-primary">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</x-settings.card>
|
||||
|
||||
<x-settings.card title="Device actions" class="mt-6">
|
||||
<div class="space-y-3">
|
||||
@if ($device->device_token)
|
||||
<form method="POST" action="{{ route('pos.devices.regenerate-token', $device) }}"
|
||||
onsubmit="return confirm('Regenerate token? Existing display sessions will stop until reopened.');">
|
||||
@csrf
|
||||
<button type="submit" class="w-full rounded-xl border border-amber-200 bg-amber-50 px-4 py-2.5 text-sm font-medium text-amber-900 hover:bg-amber-100 sm:w-auto">
|
||||
Regenerate token
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
<form method="POST" action="{{ route('pos.devices.destroy', $device) }}"
|
||||
onsubmit="return confirm('Remove this device?');">
|
||||
@csrf
|
||||
<button type="submit" class="w-full rounded-xl border border-amber-200 bg-amber-50 px-4 py-2.5 text-sm font-medium text-amber-900 hover:bg-amber-100">
|
||||
Regenerate token
|
||||
@method('DELETE')
|
||||
<button type="submit" class="w-full rounded-xl border border-rose-200 bg-rose-50 px-4 py-2.5 text-sm font-medium text-rose-800 hover:bg-rose-100 sm:w-auto">
|
||||
Remove device
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
<form method="POST" action="{{ route('pos.devices.destroy', $device) }}"
|
||||
onsubmit="return confirm('Remove this device?');">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="w-full rounded-xl border border-rose-200 bg-rose-50 px-4 py-2.5 text-sm font-medium text-rose-800 hover:bg-rose-100">
|
||||
Remove device
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-settings.card>
|
||||
</x-settings.page>
|
||||
</x-app-layout>
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<x-app-layout title="Devices">
|
||||
<div class="mx-auto max-w-5xl space-y-6">
|
||||
<div class="flex flex-wrap items-end justify-between gap-4">
|
||||
<div>
|
||||
<p class="text-xs font-semibold uppercase tracking-wider text-indigo-600">Hardware</p>
|
||||
<h1 class="mt-1 text-2xl font-semibold text-slate-900">Devices</h1>
|
||||
<p class="mt-1 text-sm text-slate-500">Register tills, customer screens, kitchen displays, and printers for each branch.</p>
|
||||
<x-settings.page description="Register tills, customer screens, kitchen displays, and printers for each branch.">
|
||||
<div class="mb-4 flex flex-wrap items-center justify-between gap-3 text-sm">
|
||||
<div class="flex flex-wrap items-center gap-1 text-slate-500">
|
||||
<a href="{{ route('pos.settings') }}" class="hover:text-slate-800">Settings</a>
|
||||
<span class="px-1">/</span>
|
||||
<span class="font-medium text-slate-900">Devices</span>
|
||||
</div>
|
||||
<a href="{{ route('pos.devices.create') }}" class="btn-primary">Add device</a>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-3 sm:grid-cols-3">
|
||||
<div class="mb-6 grid gap-3 sm:grid-cols-3">
|
||||
<div class="rounded-2xl border border-slate-200 bg-white px-5 py-4">
|
||||
<p class="text-2xl font-semibold tabular-nums text-slate-900">{{ number_format($heroStats['total']) }}</p>
|
||||
<p class="mt-0.5 text-xs font-medium uppercase tracking-wide text-slate-500">Devices</p>
|
||||
@@ -25,16 +25,13 @@
|
||||
</div>
|
||||
|
||||
@if (session('success'))
|
||||
<div class="rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-800">{{ session('success') }}</div>
|
||||
<div class="mb-4 rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-800">{{ session('success') }}</div>
|
||||
@endif
|
||||
@if (session('error'))
|
||||
<div class="rounded-xl border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-800">{{ session('error') }}</div>
|
||||
<div class="mb-4 rounded-xl border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-800">{{ session('error') }}</div>
|
||||
@endif
|
||||
|
||||
<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 devices</h2>
|
||||
</div>
|
||||
<x-settings.card title="All devices" description="Hardware registered to this POS account.">
|
||||
<div class="overflow-x-auto">
|
||||
<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">
|
||||
@@ -75,8 +72,8 @@
|
||||
</table>
|
||||
</div>
|
||||
@if ($devices->hasPages())
|
||||
<div class="border-t border-slate-100 px-5 py-3">{{ $devices->links() }}</div>
|
||||
<div class="border-t border-slate-100 px-1 pt-3">{{ $devices->links() }}</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</x-settings.card>
|
||||
</x-settings.page>
|
||||
</x-app-layout>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<x-app-layout title="Settings">
|
||||
<x-settings.page description="Configure this branch, manage locations, and invite team members.">
|
||||
<x-settings.page description="Configure this branch, manage locations, devices, and invite team members.">
|
||||
<x-settings.card title="This branch" description="Register name, currency, service style, and receipt options for {{ $location->name }}.">
|
||||
<form method="POST" action="{{ route('pos.settings.update') }}" enctype="multipart/form-data" class="space-y-4">
|
||||
@csrf
|
||||
@@ -179,6 +179,8 @@
|
||||
<div class="mt-6 space-y-6">
|
||||
@include('pos.settings._branches-section')
|
||||
|
||||
@include('pos.settings._devices-section')
|
||||
|
||||
@if (! empty($canManageTeam))
|
||||
@include('pos.settings._team-section')
|
||||
@endif
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<x-settings.card id="devices" title="Devices" description="Register tills, customer screens, kitchen displays, and printers for each branch.">
|
||||
@if ($devices->isNotEmpty())
|
||||
<ul class="divide-y divide-slate-100">
|
||||
@foreach ($devices as $device)
|
||||
<li class="flex items-center justify-between gap-4 py-3 first:pt-0 last:pb-0">
|
||||
<div class="min-w-0">
|
||||
<p class="font-medium text-slate-900">
|
||||
{{ $device->name }}
|
||||
<span class="ml-2 inline-flex rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase {{ $device->isOnline() ? 'bg-emerald-50 text-emerald-700' : 'bg-slate-100 text-slate-500' }}">
|
||||
{{ $device->isOnline() ? 'Online' : ucfirst($device->status) }}
|
||||
</span>
|
||||
</p>
|
||||
<p class="mt-0.5 text-xs text-slate-500">
|
||||
{{ $deviceTypes[$device->type] ?? $device->type }}
|
||||
· {{ $device->location?->name ?? 'All branches' }}
|
||||
@if ($device->last_online_at)
|
||||
· last online {{ $device->last_online_at->diffForHumans() }}
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
<a href="{{ route('pos.devices.edit', $device) }}" class="shrink-0 text-sm font-medium text-indigo-600 hover:text-indigo-800">Manage</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@else
|
||||
<p class="text-sm text-slate-500">No devices yet. Add a register, customer display, kitchen screen, or printer.</p>
|
||||
@endif
|
||||
|
||||
<div class="mt-4 flex flex-wrap items-center gap-3 border-t border-slate-100 pt-4">
|
||||
<a href="{{ route('pos.devices.create') }}" class="btn-primary">Add device</a>
|
||||
@if ($devices->isNotEmpty())
|
||||
<a href="{{ route('pos.devices.index') }}" class="text-sm font-medium text-slate-600 hover:text-slate-900">View all devices</a>
|
||||
@endif
|
||||
</div>
|
||||
</x-settings.card>
|
||||
+11
-7
@@ -153,13 +153,17 @@ Route::middleware(['auth', 'platform.session'])->group(function () {
|
||||
Route::get('/settings', [SettingsController::class, 'index'])->name('pos.settings');
|
||||
Route::put('/settings', [SettingsController::class, 'update'])->name('pos.settings.update');
|
||||
|
||||
Route::get('/devices', [DeviceController::class, 'index'])->name('pos.devices.index');
|
||||
Route::get('/devices/create', [DeviceController::class, 'create'])->name('pos.devices.create');
|
||||
Route::post('/devices', [DeviceController::class, 'store'])->name('pos.devices.store');
|
||||
Route::get('/devices/{device}/edit', [DeviceController::class, 'edit'])->name('pos.devices.edit');
|
||||
Route::put('/devices/{device}', [DeviceController::class, 'update'])->name('pos.devices.update');
|
||||
Route::delete('/devices/{device}', [DeviceController::class, 'destroy'])->name('pos.devices.destroy');
|
||||
Route::post('/devices/{device}/regenerate-token', [DeviceController::class, 'regenerateToken'])->name('pos.devices.regenerate-token');
|
||||
Route::get('/settings/devices', [DeviceController::class, 'index'])->name('pos.devices.index');
|
||||
Route::get('/settings/devices/create', [DeviceController::class, 'create'])->name('pos.devices.create');
|
||||
Route::post('/settings/devices', [DeviceController::class, 'store'])->name('pos.devices.store');
|
||||
Route::get('/settings/devices/{device}/edit', [DeviceController::class, 'edit'])->name('pos.devices.edit');
|
||||
Route::put('/settings/devices/{device}', [DeviceController::class, 'update'])->name('pos.devices.update');
|
||||
Route::delete('/settings/devices/{device}', [DeviceController::class, 'destroy'])->name('pos.devices.destroy');
|
||||
Route::post('/settings/devices/{device}/regenerate-token', [DeviceController::class, 'regenerateToken'])->name('pos.devices.regenerate-token');
|
||||
// Legacy /devices URLs → settings
|
||||
Route::redirect('/devices', '/settings/devices');
|
||||
Route::redirect('/devices/create', '/settings/devices/create');
|
||||
Route::get('/devices/{device}/edit', fn (string $device) => redirect("/settings/devices/{$device}/edit"));
|
||||
|
||||
Route::get('/settings/branches', [LocationController::class, 'index'])->name('pos.branches.index');
|
||||
Route::get('/settings/branches/create', [LocationController::class, 'create'])->name('pos.branches.create');
|
||||
|
||||
Reference in New Issue
Block a user