ownerRef($request); $visits = Visit::owned($owner) ->with(['visitor', 'host']) ->when($request->status, fn ($q, $s) => $q->where('status', $s)) ->latest() ->paginate(min((int) $request->query('per_page', 25), 100)); return response()->json($visits); } public function store(Request $request, VisitCheckInService $checkIn, VisitScheduleService $scheduler): JsonResponse { $owner = $this->ownerRef($request); $organization = Organization::owned($owner)->findOrFail($request->integer('organization_id')); $source = (string) $request->attributes->get('service_caller', 'api'); if ($request->filled('external_ref')) { $existing = Visit::owned($owner) ->where('organization_id', $organization->id) ->where('external_ref', $request->string('external_ref')) ->where('source', $source) ->with(['visitor', 'host']) ->first(); if ($existing) { return response()->json($existing); } } if ($request->boolean('schedule')) { $validated = $request->validate([ 'organization_id' => ['required', 'integer'], 'external_ref' => ['nullable', 'string', 'max:128'], 'visitor_id' => ['nullable', 'integer'], 'full_name' => ['required_without:visitor_id', 'string', 'max:255'], 'company' => ['nullable', 'string'], 'phone' => ['nullable', 'string'], 'email' => ['nullable', 'email'], 'host_id' => ['nullable', 'integer'], 'branch_id' => ['nullable', 'integer'], 'visitor_type' => ['required', 'string'], 'purpose' => ['nullable', 'string'], 'scheduled_at' => ['required', 'date'], 'notes' => ['nullable', 'string'], 'integration_metadata' => ['nullable', 'array'], ]); $visit = $scheduler->schedule($owner, $organization, [ ...$validated, 'source' => $source, ]); return response()->json($visit->load(['visitor', 'host']), 201); } $validated = $request->validate([ 'organization_id' => ['required', 'integer'], 'external_ref' => ['nullable', 'string', 'max:128'], 'full_name' => ['required', 'string', 'max:255'], 'company' => ['nullable', 'string'], 'phone' => ['nullable', 'string'], 'email' => ['nullable', 'email'], 'host_id' => ['nullable', 'integer'], 'visitor_type' => ['required', 'string'], 'purpose' => ['nullable', 'string'], 'integration_metadata' => ['nullable', 'array'], 'policies_accepted' => ['nullable', 'boolean'], ]); $visit = $checkIn->checkIn($owner, $organization, [ ...$validated, 'source' => $source, 'policies_accepted' => $validated['policies_accepted'] ?? true, ]); return response()->json($visit->load(['visitor', 'host']), 201); } public function show(Request $request, Visit $visit): JsonResponse { $this->authorizeOwner($request, $visit); return response()->json($visit->load(['visitor', 'host'])); } public function checkOut(Request $request, Visit $visit, VisitCheckOutService $checkOut): JsonResponse { $this->authorizeOwner($request, $visit); return response()->json($checkOut->checkOut($visit)); } public function activate(Request $request, Visit $visit, VisitLifecycleService $lifecycle): JsonResponse { $this->authorizeOwner($request, $visit); $visit = $lifecycle->checkInFromSchedule($visit); return response()->json($visit->load(['visitor', 'host'])); } public function approve(Request $request, Visit $visit, VisitLifecycleService $lifecycle): JsonResponse { $this->authorizeOwner($request, $visit); $visit = $lifecycle->approve($visit); return response()->json($visit->load(['visitor', 'host'])); } public function cancel(Request $request, Visit $visit, VisitLifecycleService $lifecycle): JsonResponse { $this->authorizeOwner($request, $visit); $validated = $request->validate([ 'reason' => ['nullable', 'string', 'max:500'], ]); return response()->json( $lifecycle->cancel($visit, null, $validated['reason'] ?? null)->load(['visitor', 'host']), ); } }