Clarify device registration with type-specific helper text.
Deploy Ladill Frontdesk / deploy (push) Successful in 30s

Replace the static kiosk/printer token message with dynamic hints per device type so reception computers are not misread as getting unattended access.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-06-28 01:32:01 +00:00
co-authored by Cursor
parent c0bcc3dbaf
commit 8c79c312cf
6 changed files with 71 additions and 18 deletions
@@ -61,7 +61,7 @@ class DeviceController extends Controller
'reception_desk_id' => ['nullable', 'integer'],
]);
$token = in_array($validated['type'], ['kiosk', 'badge_printer', 'qr_scanner'], true)
$token = in_array($validated['type'], config('frontdesk.device_token_types', []), true)
? $devices->generateToken()
: null;
+18
View File
@@ -87,6 +87,24 @@ return [
'signature_pad' => 'Signature Pad',
],
/*
| Device types that receive a server-generated token for unattended API/kiosk access.
*/
'device_token_types' => ['kiosk', 'badge_printer', 'qr_scanner'],
/*
| Contextual copy for the register / edit device forms (keyed by device type).
*/
'device_registration_hints' => [
'reception_computer' => 'Tracked for your team only. Staff sign in on this machine — no device token or automatic connection is created.',
'kiosk' => 'A device token is created after registration. Open the kiosk URL on your tablet to run self-service check-in without staff login.',
'tablet' => 'Tracked for inventory. Use Visitor Kiosk if this tablet should run unattended check-in.',
'badge_printer' => 'A device token is created for unattended printing. The printer connects when your print agent sends requests with that token.',
'qr_scanner' => 'A device token is created for unattended scanning. The scanner connects when it calls Frontdesk with that token.',
'camera' => 'Tracked for inventory at this reception desk or branch — no device token is issued.',
'signature_pad' => 'Tracked for inventory at this reception desk or branch — no device token is issued.',
],
'notification_channels' => [
'email' => 'Email',
'sms' => 'SMS',
@@ -0,0 +1,25 @@
@props([
'deviceTypes',
'selected' => 'reception_computer',
])
@php
$hints = config('frontdesk.device_registration_hints', []);
@endphp
<div
{{ $attributes->merge(['class' => '']) }}
x-data="{
type: @js(old('type', $selected)),
hints: @js($hints),
hint() { return this.hints[this.type] ?? ''; },
}"
>
<label class="block text-sm font-medium">Type</label>
<select name="type" required x-model="type" class="mt-1 w-full rounded-lg border-slate-300 text-sm">
@foreach ($deviceTypes as $key => $label)
<option value="{{ $key }}">{{ $label }}</option>
@endforeach
</select>
<p x-show="hint()" x-text="hint()" class="mt-2 text-xs text-slate-500"></p>
</div>
@@ -7,14 +7,7 @@
<label class="block text-sm font-medium">Name</label>
<input type="text" name="name" value="{{ old('name') }}" required placeholder="Lobby kiosk" class="mt-1 w-full rounded-lg border-slate-300 text-sm">
</div>
<div>
<label class="block text-sm font-medium">Type</label>
<select name="type" required class="mt-1 w-full rounded-lg border-slate-300 text-sm">
@foreach ($deviceTypes as $key => $label)
<option value="{{ $key }}" @selected(old('type') === $key)>{{ $label }}</option>
@endforeach
</select>
</div>
<x-frontdesk.device-type-field :device-types="$deviceTypes" selected="reception_computer" />
<div>
<label class="block text-sm font-medium">Branch</label>
<select name="branch_id" class="mt-1 w-full rounded-lg border-slate-300 text-sm">
@@ -33,7 +26,6 @@
@endforeach
</select>
</div>
<p class="text-xs text-slate-500">Kiosks and printers receive a device token for unattended access.</p>
<button type="submit" class="btn-primary w-full">Register device</button>
</form>
</div>
@@ -7,14 +7,7 @@
<label class="block text-sm font-medium">Name</label>
<input type="text" name="name" value="{{ old('name', $device->name) }}" required class="mt-1 w-full rounded-lg border-slate-300 text-sm">
</div>
<div>
<label class="block text-sm font-medium">Type</label>
<select name="type" required class="mt-1 w-full rounded-lg border-slate-300 text-sm">
@foreach ($deviceTypes as $key => $label)
<option value="{{ $key }}" @selected(old('type', $device->type) === $key)>{{ $label }}</option>
@endforeach
</select>
</div>
<x-frontdesk.device-type-field :device-types="$deviceTypes" :selected="$device->type" />
<div>
<label class="block text-sm font-medium">Status</label>
<select name="status" class="mt-1 w-full rounded-lg border-slate-300 text-sm">
+25
View File
@@ -82,6 +82,31 @@ class FrontdeskPhase7Test extends TestCase
$this->assertNotEmpty($device->device_token);
}
public function test_device_create_page_shows_type_specific_registration_hints(): void
{
$response = $this->actingAs($this->user)->get(route('frontdesk.devices.create'));
$response->assertOk();
$response->assertSee('Tracked for your team only', false);
$response->assertSee('Open the kiosk URL on your tablet', false);
$response->assertSee('x-model="type"', false);
$response->assertDontSee('Kiosks and printers receive a device token for unattended access.', false);
}
public function test_reception_computer_registration_does_not_issue_token(): void
{
$this->actingAs($this->user)
->post(route('frontdesk.devices.store'), [
'name' => 'Front desk PC',
'type' => 'reception_computer',
])
->assertRedirect(route('frontdesk.devices.index'));
$device = Device::first();
$this->assertSame('reception_computer', $device->type);
$this->assertNull($device->device_token);
}
public function test_kiosk_device_page_works_without_auth(): void
{
$device = $this->createKioskDevice();