Docs
Dashboard Flow
Dashboard Flow
End-to-end explanation of the project dashboard experience and supporting layout
Overview
The dashboard flow renders the authenticated workspace for a selected project. It resolves the current project, validates membership, and streams independent widgets (statistics, charts, members, upgrade prompt) using React Server Components with Suspense skeletons. The protected layout also exposes the global search command, navigation, project switcher, and account controls.
Main Libraries/Services:
- Next.js App Router – Protected routing, dynamic segments, and streaming via React Server Components.
lib/session.ts– CachedgetCurrentUser()/getCurrentUserId()helpers shared across layout, API routes, and widgets.- Project services (
services/projects/*) – Fetch project metadata, member roles, invitation statistics, and enforce permissions. - Kysely repositories (
repositories/projects/*) – Sanitized SQL for project, member, stats, and invitation queries (aliases quoted for PostgreSQL). - Shadcn UI + Radix primitives – Layout shell (
DashboardSidebar,MobileSheetSidebar),SearchCommand, cards, charts, and skeleton loaders. components/dashboard/project-not-found.tsx– Fallback screen when the requested project is missing or inaccessible.components/dashboard/invitation-accepted-toast.tsx– Suspense-wrapped client toast acknowledging invitation acceptance.
File Map
app/(protected)/layout.tsx– Auth guard; renders sidebar, search command, and account controls, filtering navigation by role.app/(protected)/dashboard/page.tsx– Redirects authenticated users to their first project or/projectswhen they have none.app/(protected)/dashboard/[projectId]/page.tsx– Main dashboard entry; validates project access, rendersProjectNotFoundfallback, and streams widgets inside Suspense.components/dashboard/sections/*.tsx– Server components (stats, members, charts, upgrade card, projects list) fetching data per widget.components/dashboard/skeletons/*.tsx– Matching skeleton placeholders for each widget (stats cards, charts, list, upgrade card, projects list).components/dashboard/project-stats-card.tsx– Shared card used by the stats section.components/dashboard/project-not-found.tsx– CTA to redirect the user to their first available project or/dashboard.components/dashboard/invitation-accepted-toast.tsx– Client component that reads URL params and shows a success toast.components/dashboard/search-command.tsx– Command palette aware of project context; disables project-scoped links when no project is available.config/dashboard.ts&types/index.d.ts– Defines navigation structure andINavItemmetadata (requiresProjectId,fallbackHref).
Step-by-Step Flow
Access & Layout Bootstrapping
- Requests under
/app/(protected)loadlayout.tsx, which callsgetCurrentUser(). Missing sessions triggerredirect("/login"). - The layout determines whether the user is a platform admin, filters
sidebarLinkswithfilterNavigationItems, and renders:DashboardSidebar(desktop) andMobileSheetSidebar(mobile) with navigation sections.SearchCommandclient component bound to⌘/Ctrl + K, aware of the current/fallback project.UserAccountNavwith avatar, settings link (/settings), and sign-out action.
Project Resolution & Authorization
/dashboard/page.tsxredirects authenticated users to/dashboard/{projectId}using their first available project. If none exist, it goes to/projects./dashboard/[projectId]/page.tsxunwraps the dynamicprojectId, validates it, and fetches the current user again (cached).projectService.getProjectById(projectId)loads project metadata. Missing projects renderProjectNotFound, showing a CTA to the first project.memberService.getUserRole(projectId, user.id)ensures the user is a member and returns their role. Lack of membership also shows the fallback screen.- The page exports
dynamic = "force-dynamic"andrevalidate = 0to avoid sharing cached project data between users.
Streaming Widgets with Suspense
- Once access is confirmed, the page renders
InvitationAcceptedToast(client),DashboardHeader, and a grid of widgets. - Each widget is an isolated server component wrapped by
<Suspense fallback={<MatchingSkeleton />}>:ProjectStatsSection– Aggregated counts, invitations, team leaders, and 30-day growth.MembersByRoleSection,MemberGrowthSection,InvitationsChartSection– Chart data feeding shadcn chart components.ProjectMembersSection– Member list with role badges and management controls, respecting permission checks.ProjectsListSection– User-centric project overview (mirrors/projectspage cards).UpgradeCardSection– Surface upgrade prompts for admins/owners.
- Data fetching relies on repository functions (
getProjectMemberCount,getProjectMemberGrowth, etc.) that enforce camelCase aliases with quotes (AS "memberCount") per@next-js.mdc. ProjectStatsCardand corresponding skeleton mirror the same padding, typography, and hover states to avoid layout shifts.
Invitation Acceptance Feedback
- When a user accepts an invitation, the API returns
redirect("/dashboard/{projectId}?invitation=accepted"). InvitationAcceptedToastreads theinvitationquery param, raises a Sonner toast, and cleans the URL viarouter.replaceto prevent repeated toasts.
Navigation, Search, and Project Switching
NavigationSections(client) parses the path to detect the current project ID and fetches a fallback project via/api/projectswhen none is in context.- Items with
requiresProjectIddynamically replace[projectId]inhref. If no project is available, they usefallbackHref(e.g.,/projects) and render as disabled inSearchCommand. - The project switcher popover components (
components/dashboard/project/switcher/*) listen for the customprojects:refreshevent to stay in sync with create/edit/delete actions.
Data Flow Diagram
flowchart TD
A[Authenticated request /dashboard/:projectId] --> B[(protected)/layout.tsx]
B --> C[getCurrentUser()]
C --> D[Navigation & SearchCommand]
B --> E[/dashboard/:projectId/page.tsx]
E --> F[projectService.getProjectById]
E --> G[memberService.getUserRole]
F & G --> H{Has access?}
H -- No --> I[ProjectNotFound]
H -- Yes --> J[Render Suspense widgets]
J --> K[ProjectStatsSection -> repositories/projects/statistics]
J --> L[ProjectMembersSection -> memberService.getProjectMembers]
J --> M[Chart sections -> Kysely stats queries]
J --> N[ProjectsListSection -> projectService.getUserProjects]
J --> O[UpgradeCardSection -> permissions service]
N --> P[(PostgreSQL)]Dependencies & Contracts
getCurrentUser()/getCurrentUserId()– Cached session helpers; throw if auth fails.projectService.getProjectById(id)– Returns project metadata ornull; consumed before rendering widgets.memberService.getUserRole(projectId, userId)– Validates membership and exposes role for permission checks.getProjectMemberCount,getProjectMemberGrowth,getProjectInvitationStats,getProjectMembersByRole– Repository functions returning camelCase fields via double-quoted aliases.ProjectStatsSection({ projectId })– Server component returning four stats cards with optional trend data.NavigationSections/SearchCommand– Client utilities ensuring dynamic links resolve safely and remain accessible without a current project.projects:refreshevent – Broadcast mechanism to refresh project lists after mutations.
Known Limitations
- Statistics queries aggregate by day; there is no pagination or filtering beyond the last 30 days.
- Invitation and member queries are recalculated on every request (
revalidate = 0); consider memoization or background jobs if data grows large. SearchCommandfetches the fallback project client-side; failure gracefully disables project-scoped commands but does not show an inline error.- Upgrade card currently relies on admin detection; future plan tiers may require more granular feature gating.
- Project fallback selection assumes the first available project; no notion of “last opened project” persisted yet.
Notes & TODOs
- ✅ Enforced double-quoted SQL aliases and sanitized Kysely queries in all repositories.
- ✅ Implemented Suspense + skeletons for each widget to improve perceived loading time.
- ✅ Added
ProjectNotFoundfallback and invitation acceptance toast. - 🔄 Record “last visited project” to improve redirect accuracy.
- 🔄 Cache heavy statistics (e.g., growth charts) using tag-based revalidation once frequency is known.
- 🔄 Extend dashboard widgets with billing usage and notifications summary.
- 🔄 Add owner/admin shortcuts (e.g., quick invite, create member) surfaced directly on the dashboard.