authorizeAbility($request, 'dashboard.view'); $owner = $this->ownerRef($request); $organization = $this->organization($request); $visitQuery = Visit::owned($owner)->where('organization_id', $organization->id); $this->scopeToBranch($request, $visitQuery); $branchScope = app(OrganizationResolver::class)->branchScope($this->member($request)); $cacheKey = "fd:dashboard:{$owner}:{$organization->id}:".($branchScope ?? 'all'); $stats = Cache::remember($cacheKey, 60, function () use ($visitQuery) { $today = (clone $visitQuery)->where(function ($q) { $q->whereDate('checked_in_at', today()) ->orWhereDate('scheduled_at', today()); }); return [ 'visitors_today' => (clone $today)->count(), 'currently_inside' => (clone $visitQuery)->currentlyInside()->count(), 'expected_arrivals' => (clone $visitQuery)->whereIn('status', [ Visit::STATUS_EXPECTED, Visit::STATUS_SCHEDULED, Visit::STATUS_OVERDUE, ])->whereDate('scheduled_at', today())->count(), 'waiting' => (clone $visitQuery)->where('status', Visit::STATUS_WAITING)->count(), 'overdue' => (clone $visitQuery)->where('status', Visit::STATUS_OVERDUE)->count(), 'checked_out_today' => (clone $visitQuery)->where('status', Visit::STATUS_CHECKED_OUT) ->whereDate('checked_out_at', today())->count(), 'deliveries_today' => (clone $visitQuery)->where('visitor_type', 'delivery') ->whereDate('checked_in_at', today())->count(), 'contractors_today' => (clone $visitQuery)->where('visitor_type', 'contractor') ->whereDate('checked_in_at', today())->count(), 'pending_approvals' => (clone $visitQuery)->where('status', Visit::STATUS_WAITING) ->whereNull('checked_in_at') ->where(function ($q) { $q->whereJsonContains('contractor_details', ['_awaiting_approval' => true]) ->orWhereJsonContains('delivery_details', ['_awaiting_approval' => true]); })->count(), ]; }); $stats = array_merge($stats, $this->staffPresenceStats($request, $owner, $organization)); $currentVisitors = (clone $visitQuery)->currentlyInside() ->with(['visitor', 'host']) ->latest('checked_in_at') ->limit(10) ->get(); $expectedVisitors = (clone $visitQuery) ->whereIn('status', [Visit::STATUS_EXPECTED, Visit::STATUS_SCHEDULED, Visit::STATUS_WAITING, Visit::STATUS_OVERDUE]) ->whereDate('scheduled_at', today()) ->with(['visitor', 'host']) ->orderBy('scheduled_at') ->limit(10) ->get(); $pendingApprovals = (clone $visitQuery) ->where('status', Visit::STATUS_WAITING) ->whereNull('checked_in_at') ->where(function ($q) { $q->whereJsonContains('contractor_details', ['_awaiting_approval' => true]) ->orWhereJsonContains('delivery_details', ['_awaiting_approval' => true]); }) ->with(['visitor', 'host']) ->latest() ->limit(10) ->get(); $staffSteppedOut = EmployeePresence::query() ->where('status', EmployeePresence::STATUS_STEPPED_OUT) ->whereHas('employee', function ($q) use ($owner, $organization, $request) { $q->owned($owner)->where('organization_id', $organization->id)->where('active', true); $this->scopeToBranch($request, $q, 'branch_id'); }) ->with('employee') ->orderByDesc('last_event_at') ->limit(10) ->get(); return view('frontdesk.dashboard', compact( 'stats', 'currentVisitors', 'expectedVisitors', 'pendingApprovals', 'staffSteppedOut', 'organization', )); } /** @return array{staff_on_site: int, staff_stepped_out: int} */ protected function staffPresenceStats(Request $request, string $owner, Organization $organization): array { $query = EmployeePresence::query() ->whereHas('employee', function ($q) use ($owner, $organization, $request) { $q->owned($owner) ->where('organization_id', $organization->id) ->where('active', true); $this->scopeToBranch($request, $q, 'branch_id'); }); return [ 'staff_on_site' => (clone $query)->where('status', EmployeePresence::STATUS_ON_SITE)->count(), 'staff_stepped_out' => (clone $query)->where('status', EmployeePresence::STATUS_STEPPED_OUT)->count(), ]; } }