State Management & Interactive Flow
In a data-heavy application like PlugZero Analytics, state management is split between Navigation State, Local UI State, and Backend Synchronization. We avoid giant global stores in favor of scoped, predictable state containers.
🛤️ Tier 1: URL-Driven State (The Source of Truth)
We treat the browser’s address bar as our primary “Global Store.”
- Dynamic Navigation: We use Next.js
useParamsanduseSearchParamsto drive the UI. - Persistence: Filters, active tabs in the Workbench, and the current Project ID are all stored in the URL.
- Benefit: If you find an interesting chart, you can copy the URL and send it to a teammate. They will see the exact same page, filters, and chart type instantly.
🔐 Tier 2: Authentication & User Context (AuthContext)
The src/context/AuthContext.tsx handles the foundational security state.
- Session Guard: It manages the
Userobject and theisAuthenticatedstatus. - Privilege Gate: It tracks the Active Team and the user’s role within that team (
OWNER,ADMIN, etc.). - Automatic Refresh: It intercepts 401 errors from the API and attempts to refresh the session or redirects the user to the login gateway.
📡 Tier 3: Server Interaction & Sync (apiFetch)
We do not use an external library like Redux for backend data. Instead, we use a custom, high-performance fetch wrapper found in src/lib/api/apiFetch.ts.
- Interceptors: Every request automatically injects the
Authorization: Bearer <token>header. - Multi-Tenancy Header: The fetcher ensures the current
TeamIDis sent with every request to maintain strict data isolation. - Error Normalization: It converts raw backend errors into user-friendly messages that the
use-toast.tshook can display.
🔄 Tier 4: Scoped State & Hooks
For complex interactive zones, we move state closer to where it’s used.
The Project Pulse
We use specialized hooks to track project-level data without re-fetching everything.
useActiveProject: Pulls the project metadata from the route and makes it available to the entire dashboard.- Domain Hooks: Small, focused hooks (e.g.,
useSurveyResponses) that handle the lifecycle of specific intelligence data.
Ephemeral UI State
For things that don’t need to persist (like “is this dropdown open?”), we use standard useState or Zustand for cross-component communication (e.g., toggling the global sidebar from a deep sub-page).
🛠️ Data Flow Rulebook
1. The “Push, Don’t Pull” Rule
If a user changes a filter, Push that change to the URL. Let the page re-render based on the new URL parameters. This ensures the “Back” button works exactly as expected.
2. Server Action Priority
For all writing operations (Creating a project, deleting a file), use Server Actions. This keeps the “Write” logic on the server and reduces the client-side JavaScript bundle.
3. Loading Boundaries
Always wrap heavy data components in a <Suspense> boundary with a skeleton loader. This ensures the rest of the dashboard remains interactive while the math engine is working.