Add employee presence with kiosk sign-in, QR badges, and mobile step-out.
Deploy Ladill Frontdesk / deploy (push) Failing after 26s
Deploy Ladill Frontdesk / deploy (push) Failing after 26s
Enables staff roster, PIN/code kiosk flow, attendance reports, evacuation staff roll call, webhooks, branch mismatch warnings, and a linked-user My presence portal. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -6,11 +6,13 @@ use App\Http\Controllers\Controller;
|
||||
use App\Models\Device;
|
||||
use App\Models\Host;
|
||||
use App\Models\Organization;
|
||||
use App\Services\Frontdesk\EmployeePresenceService;
|
||||
use App\Services\Frontdesk\VisitCheckInService;
|
||||
use App\Services\Frontdesk\VisitorTypeService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class KioskDeviceController extends Controller
|
||||
{
|
||||
@@ -32,6 +34,8 @@ class KioskDeviceController extends Controller
|
||||
|
||||
$hosts = $hosts->orderBy('name')->get();
|
||||
|
||||
$employeeKioskEnabled = (bool) ($settings['employee_kiosk_enabled'] ?? true);
|
||||
|
||||
return view('frontdesk.kiosk.index', [
|
||||
'organization' => $organization,
|
||||
'hosts' => $hosts,
|
||||
@@ -40,9 +44,76 @@ class KioskDeviceController extends Controller
|
||||
'resetSeconds' => (int) ($settings['kiosk_reset_seconds'] ?? config('frontdesk.kiosk.inactivity_reset_seconds', 120)),
|
||||
'visitorPolicy' => $settings['visitor_policy'] ?? null,
|
||||
'checkInUrl' => route('frontdesk.kiosk.device.check-in', $device->device_token),
|
||||
'employeeKioskEnabled' => $employeeKioskEnabled,
|
||||
'stepOutReasons' => collect(config('frontdesk.employee_step_out_reasons'))
|
||||
->map(fn ($label, $key) => ['key' => $key, 'label' => $label])
|
||||
->values(),
|
||||
'staffActionUrl' => route('frontdesk.kiosk.device.staff.action', $device->device_token),
|
||||
]);
|
||||
}
|
||||
|
||||
public function staffAction(Request $request, EmployeePresenceService $presence): JsonResponse
|
||||
{
|
||||
$device = $this->device($request);
|
||||
$organization = Organization::findOrFail($device->organization_id);
|
||||
|
||||
$validated = $request->validate([
|
||||
'employee_code' => ['nullable', 'string', 'max:32'],
|
||||
'qr_token' => ['nullable', 'string', 'max:500'],
|
||||
'pin' => ['required', 'string', 'regex:/^\d{4,6}$/'],
|
||||
'action' => ['required', 'string', 'in:identify,sign_in,sign_out,step_out,step_in'],
|
||||
'step_out_reason' => ['nullable', 'string', 'in:'.implode(',', array_keys(config('frontdesk.employee_step_out_reasons')))],
|
||||
'destination' => ['nullable', 'string', 'max:255'],
|
||||
'notes' => ['nullable', 'string', 'max:500'],
|
||||
'expected_return_at' => ['nullable', 'date'],
|
||||
'confirm_branch_mismatch' => ['boolean'],
|
||||
]);
|
||||
|
||||
if (empty($validated['employee_code']) && empty($validated['qr_token'])) {
|
||||
throw ValidationException::withMessages([
|
||||
'credentials' => 'Enter your employee code or scan your badge.',
|
||||
]);
|
||||
}
|
||||
|
||||
$employee = $presence->resolveEmployee($organization, $validated);
|
||||
|
||||
if ($validated['action'] === 'identify') {
|
||||
return response()->json([
|
||||
'employee' => $presence->employeePayload($employee),
|
||||
]);
|
||||
}
|
||||
|
||||
match ($validated['action']) {
|
||||
'sign_in' => $result = $presence->signIn($employee, $device, (bool) ($validated['confirm_branch_mismatch'] ?? false)),
|
||||
'sign_out' => $presence->signOut($employee, $device),
|
||||
'step_out' => $presence->stepOut($employee, [
|
||||
'step_out_reason' => $validated['step_out_reason'] ?? 'other',
|
||||
'destination' => $validated['destination'] ?? null,
|
||||
'notes' => $validated['notes'] ?? null,
|
||||
'expected_return_at' => $validated['expected_return_at'] ?? null,
|
||||
], $device),
|
||||
'step_in' => $presence->stepIn($employee, $device),
|
||||
default => null,
|
||||
};
|
||||
|
||||
$response = [
|
||||
'employee' => $presence->employeePayload($employee->fresh()),
|
||||
'message' => match ($validated['action']) {
|
||||
'sign_in' => 'Signed in successfully.',
|
||||
'sign_out' => 'Signed out for the day.',
|
||||
'step_out' => 'Stepped out recorded.',
|
||||
'step_in' => 'Welcome back!',
|
||||
default => 'Done.',
|
||||
},
|
||||
];
|
||||
|
||||
if ($validated['action'] === 'sign_in' && isset($result['warning']) && $result['warning']) {
|
||||
$response['warning'] = $result['warning'];
|
||||
}
|
||||
|
||||
return response()->json($response);
|
||||
}
|
||||
|
||||
public function checkIn(Request $request, VisitCheckInService $checkIn, VisitorTypeService $visitorTypes): JsonResponse
|
||||
{
|
||||
$device = $this->device($request);
|
||||
|
||||
Reference in New Issue
Block a user