authorizeAbility($request, 'reports.view'); $organization = $this->organization($request); $branches = Branch::owned($this->ownerRef($request)) ->where('organization_id', $organization->id) ->where('is_active', true) ->orderBy('name') ->get(); return view('meet.reports.index', [ 'organization' => $organization, 'branches' => $branches, 'reports' => config('meet.report_types'), ]); } public function show(Request $request, string $type): View { $this->authorizeReport($request, $type); $organization = $this->organization($request); $branchScope = app(OrganizationResolver::class)->branchScope($this->member($request)); $branchId = $request->input('branch_id') ? (int) $request->input('branch_id') : $branchScope; [$from, $to] = $this->dateRange($request); $data = $this->reportData($type, $this->ownerRef($request), $organization->id, $from, $to, $branchId); $branches = Branch::owned($this->ownerRef($request)) ->where('organization_id', $organization->id) ->orderBy('name') ->get(); return view('meet.reports.show', [ 'type' => $type, 'label' => config('meet.report_types')[$type] ?? $type, 'data' => $data, 'from' => $from->toDateString(), 'to' => $to->toDateString(), 'branchId' => $branchId, 'branches' => $branches, 'canExport' => app(MeetPermissions::class)->can($this->member($request), 'reports.export'), ]); } public function export(Request $request, string $type): StreamedResponse { $this->authorizeReport($request, $type); abort_unless(app(MeetPermissions::class)->can($this->member($request), 'reports.export'), 403); $organization = $this->organization($request); $branchScope = app(OrganizationResolver::class)->branchScope($this->member($request)); $branchId = $request->input('branch_id') ? (int) $request->input('branch_id') : $branchScope; [$from, $to] = $this->dateRange($request); $data = $this->reportData($type, $this->ownerRef($request), $organization->id, $from, $to, $branchId); $filename = 'meet-report-'.$type.'-'.now()->format('Y-m-d').'.csv'; return response()->streamDownload(function () use ($type, $data) { echo $this->reports->toCsv($type, $data); }, $filename, ['Content-Type' => 'text/csv']); } /** @return array */ protected function reportData(string $type, string $ownerRef, int $organizationId, Carbon $from, Carbon $to, ?int $branchId): array { return match ($type) { 'summary' => $this->reports->orgSummary($ownerRef, $organizationId, $from, $to, $branchId), 'meetings' => $this->reports->meetingsReport($ownerRef, $organizationId, $from, $to, $branchId), 'users' => $this->reports->usersReport($ownerRef, $organizationId, $from, $to, $branchId), 'invitations' => $this->reports->invitationsReport($ownerRef, $organizationId, $from, $to, $branchId), 'branches' => $this->reports->branchRollup($ownerRef, $organizationId, $from, $to), default => abort(404), }; } protected function authorizeReport(Request $request, string $type): void { $this->authorizeAbility($request, 'reports.view'); abort_unless(array_key_exists($type, config('meet.report_types', [])), 404); } /** @return array{0: Carbon, 1: Carbon} */ protected function dateRange(Request $request): array { $from = Carbon::parse($request->input('from', now()->subDays(30)->toDateString()))->startOfDay(); $to = Carbon::parse($request->input('to', now()->toDateString()))->endOfDay(); return [$from, $to]; } }