'required|integer', 'name' => 'required|min_length[2]|max_length[200]', 'slug' => 'required|min_length[1]|max_length[220]', 'theme' => 'permit_empty|in_list[light,dark,system]', ]; public function forWorkspace(int $workspaceId): array { return $this->select('dashboards.*') ->select('(SELECT COUNT(*) FROM dashboard_widgets WHERE dashboard_widgets.dashboard_id = dashboards.id) AS widget_count', false) ->where('dashboards.workspace_id', $workspaceId) ->orderBy('dashboards.is_pinned', 'DESC') ->orderBy('dashboards.updated_at', 'DESC') ->findAll(); } public function findForWorkspace(int $id, int $workspaceId): ?array { $row = $this->where('workspace_id', $workspaceId)->find($id); return $row ?: null; } public function generateUniqueSlug(int $workspaceId, string $name): string { $slug = url_title($name, '-', true); if ($slug === '') { $slug = 'dashboard'; } $base = $slug; $n = 2; while ($this->where('workspace_id', $workspaceId)->where('slug', $slug)->first()) { $slug = $base . '-' . $n; $n++; } return $slug; } /** * URL-safe random token for public sharing (future use). */ public function generatePublicToken(): string { return bin2hex(random_bytes(24)); } }