Overview
The dashboard is the landing page after login. It gives you a quick snapshot of your workspace: how many contacts you have, import success rates, recent activity, and a 30-day growth chart. Everything loads fast thanks to server-side prefetching with SWR revalidation.Stats Cards
The top of the dashboard shows four stat cards:| Stat | What it shows |
|---|---|
| Total Contacts | Count of all contacts in the workspace |
| Total Imports | Number of CSV imports performed |
| Success Rate | Percentage of rows successfully processed across all imports (SUM(processed_rows) / SUM(total_rows)) |
| Contacts This Month | New contacts added in the current calendar month |
Growth Chart
A 30-day line chart showing daily contact creation counts. The data comes from adate_trunc('day', created_at) aggregate query on the contacts table, backed by a dedicated contacts_created_at_idx index for performance.
The chart uses the same 5-minute cache as the stats cards.
Recent Activity
The dashboard shows three recent activity sections:Recent Contacts
The 5 most recently created contacts, showing name (or email as fallback), avatar, and relative timestamp. Clicking a contact navigates to the contacts page.Recent Imports
Recent CSV imports with status badges (pending, processing, completed, failed) and progress information. Completed imports show “X added, Y updated” when updates were detected during upsert processing.
Recent Exports
Recent exports with status badges and row counts.Onboarding Flow
For new workspaces with no data, the dashboard shows a guided onboarding timeline instead of empty charts:- Upload your first CSV — links to the import page
- Create your first contact — opens the create contact drawer
- Create your first tag — links to the tags page
Data Fetching Pattern
The dashboard uses a specific pattern for fast initial loads:- The RSC (React Server Component) page prefetches all data server-side via
apiServer - Data is passed as
initialDatato the client component - The client component wraps children in
<SWRConfig value={{ fallback }}>for instant render - SWR revalidates in the background, so the data stays fresh without a loading spinner