Sales CRM UX — Design System & Consistency Guide
This document defines the user experience and UI consistency for building a Sales CRM inside PulseGate using the existing database schema. It ensures the CRM feels like a natural extension of the current dashboard: same data tables, side panels, filters, buttons, modals, and search patterns.
1. Purpose & scope
- Goal: Deliver a Sales CRM experience (pipeline, contacts, activities) that reuses the same UX patterns already built for Leads, Sessions, and analytics.
- Constraint: Use the current DB schema (Leads, LeadSubmissions, LeadCrmActivity, Sites, Forms, Integrations, etc.) — no schema changes required for this UX doc; extend only where migrations already support it (e.g.
LeadStatus,CrmLeadId,SeenAt). - Outcome: A single, consistent design language across Dashboard, Leads, and CRM so users learn once and apply everywhere.
1.1 Using this guide for the next CRM application
Yes — this document is the single source of truth for maintaining the same experience when you build the next CRM (whether it’s new pages in the same PulseGate dashboard or a separate CRM application).
| Scenario | How this doc helps |
|---|---|
| New CRM inside PulseGate (same repo, new routes/pages) | Use the Component inventory (Section 6) and page patterns (Section 3) as a checklist. Import the same components (Card, Pagination, QuickRangeFilter, BulkActionsBar, etc.) and follow the same layout (list + side panel, filters, search). Section 2 gives exact patterns (table classes, panel width, filter bar grid) so the new CRM looks and behaves like Leads/Sessions. |
| Separate CRM application (different repo or stack) | Use this doc as a UX spec: same structure (page title → saved views → quick range → search/filters → table → side panel), same behaviours (row click opens detail drawer, Escape closes panel, filters reset offset). Recreate the patterns in your stack (e.g. different component library) so users get the same mental model and flow. |
| Onboarding or handoff | New developers can read this doc first to understand how tables, side panels, filters, and modals work across the product. Section 5 (CRM-specific checklist) is a quick reference during implementation and code review. |
To keep the same experience:
- Before building — Skim Section 2 (design system) and Section 3 (page patterns); decide which pattern fits (list + side panel vs full detail vs kanban).
- While building — Use Section 5 (checklist) and Section 6 (component inventory); match table structure, filter bar, and side panel behaviour to the spec.
- When reviewing — Check that new CRM screens follow the same layout, components (or equivalent), and interaction rules so the product stays consistent.
Treat this guide as the contract for CRM UX. Any new CRM feature or app that follows it will feel like the same system to users.
For tokens, components, and layout (colors, Button/Card/Badge, theme, sidebar), see the Design system doc. This guide focuses on patterns and behaviour (tables, side panel, filters, search).
2. Design system — what to reuse
2.1 Layout & navigation
| Element | Current implementation | CRM usage |
|---|---|---|
| App shell | Layout: Sidebar + main content area; p-6 main padding | Same. CRM views live inside the same shell. |
| Sidebar | Sidebar: grouped nav (Overview, Leads & pipeline, Sessions, Settings, etc.), collapsible, theme + theme-color in footer | Add a “CRM” or “Pipeline” group (or keep under “Leads & pipeline”) so CRM entry point is clear. |
| Page title | Top of page: <h1 className="text-xl font-semibold">Leads</h1> + optional total count | Use same: e.g. “Pipeline” or “Leads” with “X total”. |
| Breadcrumb / back | “Back to leads” with ArrowLeft, navigate('/leads') | Use same for “Back to pipeline” or “Back to leads” from detail. |
2.2 Data tables
Use the same table pattern as Leads and Sessions:
- Container:
<Card>with<CardHeader>(title + optional actions) and<CardContent>. - Structure:
<table className="w-full text-sm">with<thead>(e.g. border-b, text-muted-foreground) and<tbody>. - Rows:
border-b border-border/50,hover:bg-secondary/30,transition-colors.- Clickable row:
cursor-pointer; click opens side panel or navigates to detail (same as Leads). - Selected row:
selectedLeadId === row.id→bg-primary/5(or equivalent).
- Row content: Use
Badgefor status/score/risk;text-muted-foregroundfor secondary text; truncate long text withtruncateand optionaltitle={fullText}. - Checkbox column: First column for bulk selection when applicable; use
Checkbox+useBulkSelection+BulkActionsBar(see below). - Pagination: Use
Paginationcomponent below the table withtotal,pageSize,offset,onOffsetChange,pageSizeOptions={DEFAULT_PAGE_SIZE_OPTIONS}(20, 50, 100),onPageSizeChange(and reset offset to 0). Show “Page X of Y (total)” and Previous/Next. - Empty state: “No leads found” / “No items” style message, centered,
text-muted-foreground text-sm. - Loading:
<Spinner />centered in table area; error state withtext-destructive text-sm.
Consistency rules for CRM tables:
- Same table wrapper (Card), same header pattern, same row hover/selected styles.
- If the CRM shows “contacts” or “leads,” reuse the same column semantics where they map (Name, Contact, Project, Score, Risk, Time, Status).
- Keep column order and density similar so switching between “Leads” and “CRM pipeline” doesn’t feel like a different product.
2.3 Side panel (detail drawer)
The fluid side panel used on the Leads page is the reference for “list + detail on the right”:
- Trigger: Click row in table (or card in kanban) → open panel; do not navigate away.
- Overlay: Fixed overlay
bg-black/30 backdrop-blur-[2px](or similar),pointer-events-autowhen open; click overlay to close. - Panel:
<aside>fixed right, full height,border-l bg-background shadow-xl,translate-x-0when open /translate-x-fullwhen closed, withtransition-[transform] duration-200 ease-out. - Width: Resizable (e.g. 320–900px or 90% viewport); drag handle on left edge; persist width in state (optional: localStorage).
- Header: Shrink-0, border-b,
px-4 py-3:- Close button (PanelRightClose icon), title (e.g. “Lead details”), optional badges (e.g. follow-up count).
- Actions: “Full details” (navigate to
/leads/:id), Previous/Next lead (ChevronLeft/Right).
- Body:
min-h-0 flex-1 overflow-y-auto p-4; content inspace-y-5sections. - Sections: Contact info grid (label
text-xs text-muted-foreground, valuetext-sm), Badges row, then blocks like “Follow-up history”, “Activity timeline”. - Escape: Close panel on Escape (existing
useEffectonkeydown). - Resize:
onMouseDownon drag handle →setIsResizing(true);mousemove/mouseupto update width and cleanup.
CRM consistency: Use the same side panel for “Contact/Lead detail” in CRM: same header layout, same resize behavior, same “Full details” + Prev/Next. Content can add CRM-specific blocks (e.g. “CRM activity”, “Next step”) but structure stays the same.
2.4 Filters
- Saved views (presets): Row of small buttons, e.g. “All”, “Hot”, “Very Hot”, “High Risk”, “Last 24h”, “Cold”. Active view:
variant="default", othersvariant="outline",size="sm",className="h-7 text-xs". Click applies a predefined filter set and resets offset. - Quick date range: Use
QuickRangeFilterwith options like Today, Yesterday, Past 7 days, Past 2 weeks, Past 30 days, All time. Same component as Leads/Sessions; samevalue/onChangepattern. - Search: Single search input with Search icon (
pl-9), placeholder e.g. “Search name, email, phone, project…”. “Search” button to apply (setsqandoffset=0). “Clear” button whenfilters.qis set. Submit on Enter. - Filter toggle: “Filters” button toggles visibility of the advanced filter bar; when active use
variant="default"so it’s clear filters are on. - Advanced filter bar: Shown when
showFiltersis true. Wrapped in<Card>with<CardContent>. Grid of controls (e.g.grid gap-3 sm:grid-cols-2 lg:grid-cols-4):- Dropdowns: Use
DropdownOptionSelectfor Project, Source (and any other option sets from DropdownOptions). Samekind,value,onChange,placeholder,labelpattern. - Numeric/date: Min Score, Max Risk, From Date, To Date — use
<Input type="number">/type="date"with small labels (text-xs text-muted-foreground). On change, reset offset to 0. - Selects: Seen (All / Not seen yet / Already seen), Engagement Label (All / Very Hot / Hot / Warm / Cold), Sort By — native
<select className="h-8 w-full rounded-md border ...">.
- Dropdowns: Use
- Reset: “Reset” button (X icon) when
hasActiveFilters; clears all filters and resets to default view.
CRM: Reuse the same filter bar pattern. Add or rename filters only where the CRM has different dimensions (e.g. “Stage” instead of “Engagement label” if you expose status as a filter). Keep layout and component choices consistent.
2.5 Buttons
- Variants:
default(primary),secondary,outline,ghost,destructive. Use for primary action, secondary action, tertiary, and destructive (e.g. “Block IP”, “Remove”). - Sizes:
sm(e.g.h-7,text-xs) for toolbar/table actions;defaultfor page-level actions. - Icons: Lucide icons; place with
className="mr-1 h-3.5 w-3.5"or similar; pair with text for clarity. Usesize="icon"for icon-only (e.g. close, refresh). - Loading: For async actions, show
<Spinner className="h-3.5 w-3.5" />and disable button; optional “Blocking…” / “Saving…” text.
CRM: Same variants and sizes. Use “View in CRM” as outline/primary link-style button with ExternalLink icon when crm_lead_url is present.
2.6 Modals
- Destructive confirmation: Use
ConfirmDestructiveModal:open,onClose,onConfirm,title,description(default “This action cannot be undone.”),confirmLabel,loading. Overlay + Card, Cancel + destructive Confirm button. Use for bulk delete, remove, or any irreversible action. - Non-destructive: Same overlay + Card pattern; primary action can be
variant="default". Keep max width (e.g.max-w-md) and padding consistent.
CRM: Use the same modal for “Delete contact”, “Remove from pipeline”, or “Mark as lost” confirmations.
2.7 Badges & status
- Engagement score:
BadgewithscoreBadgeVariant(score)→ cold / warm / hot / very-hot; show numeric score + label (e.g. “72 Hot”). - Risk/suspicion:
variant="destructive"when score ≥ 40, elsevariant="outline". - Pipeline status: Text or small Badge; capitalize (New, Contacted, Qualified, Proposal, Won, Lost). Use same
LEAD_STATUSES(or CRM stage list) across list and side panel. - Counts: e.g. “Follow-up” badge with count; “New” when unseen.
CRM: Keep status badges visually consistent with Leads (same colors and typography). Pipeline stages can reuse the same status field and styling.
2.8 Cards
- Section card:
<Card>with<CardHeader>(optionalCardTitle) and<CardContent>. Use for “Recent lead activity”, “Activity timeline”, or any block on detail page. - List/canvas: Table or kanban lives inside one main Card (e.g. “Lead Listing”).
- Emphasis: For “blocked” or highlighted state use
border-primary/40 bg-primary/5(theme-aware).
CRM: Use Cards for timeline, notes (future), and CRM activity blocks in the side panel or full detail page.
2.9 Search
- Position: Near the top of the list page, often left of “Filters” and “Reset”.
- Behavior: Controlled input; apply on button click or Enter; clear button when query is non-empty; reset
offsetto 0 when applying or clearing. - Placeholder: Descriptive, e.g. “Search name, email, phone, project…”.
CRM: Same pattern: one search control, apply/clear, offset reset.
2.10 Theme & accessibility
- Theme: Use existing
ThemeProvider(light/dark/system) and theme color (neutral, zinc, violet, etc.). No new tokens for CRM. - Sidebar: Collapsible; same active state (
bg-sidebar-accent). Role-based visibility: admin vs marketing (e.g. hide “Leads” for marketing); CRM entry can follow same role rules. - Focus & keyboard: Escape closes side panel; focus trap in modals; visible focus rings (
focus-visible:ring-2). Keep button and link semantics (e.g. “Full details” as navigation, “View in CRM” as external link).
3. Page patterns
3.1 List + side panel (e.g. Leads / Pipeline)
- Page title + total count.
- Saved views (preset filter buttons).
- Quick date range (QuickRangeFilter).
- Optional “Recent activity” or similar card.
- Search + Filters toggle + Reset.
- Advanced filter bar (conditional Card).
- Main Card: table (or kanban) with optional bulk bar; pagination below table when in table mode.
- Side panel: opens on row click; overlay + resizable panel; header with close, title, Full details, Prev/Next; body with contact info, badges, timeline, etc.
Use this for “Pipeline” or “Leads” CRM view: same flow, same components.
3.2 Full detail page (e.g. /leads/:id)
- Back button + breadcrumb (e.g. “Back to leads” + “/ Lead #123”).
- Primary summary Card (contact name, key fields, status, “View in CRM” if applicable).
- Grid of contact fields (Email, Phone, Project, Source, Location, IP, Referer, Pipeline status).
- Badges row (score, risk, returns, duration, etc.).
- Optional blocks: Suspicion reasons, Related projects.
- “All submissions” (or equivalent) as child cards in a grid.
- Activity timeline in a Card (submissions + sessions + CRM activity with same timeline component).
CRM: The same full-detail page can serve as “Contact detail” or “Lead detail”; only labels and optional sections (e.g. “CRM activity”) differ.
3.3 Kanban (optional)
- Columns by status (New, Contacted, Qualified, Proposal, Won, Lost). Each column: header with count, scrollable card list; cards draggable; drop to change status.
- Card content: same as table row summary (name, email, project, score, badges). Click card opens side panel (same as table row click).
- Reuse same
handleStatusChange, same API (updateStatus), same styling (borders, hover, blocked state).
4. Data & API alignment (current schema)
The CRM UX should align with existing APIs and schema so that the same components receive the same data shapes:
- Leads list:
GET /analytics/leadswithlimit,offset,q,project,source,min_score,max_score,min_risk,max_risk,date_from,date_to,label,sort,status,has_follow_ups,seen. Response:{ leads, total }. Use for both “Leads” and “Pipeline” views (filter/sort bystatuswhen needed). - Lead detail:
GET /analytics/leads/:id→ lead, submissions, sessions,crm_activity. Use for side panel and full detail page. - Status update:
PATCH /analytics/leads/:idwith{ status }for pipeline/kanban. - Dropdown options:
GET /analytics/dropdown-options?kind=project|source|...for Project, Source filters. UseDropdownOptionSelectwith samekind/value/onChange.
DB entities (reference): Leads (with LeadStatus, CrmLeadId, SeenAt, engagement/suspicion columns), LeadSubmissions, LeadCrmActivity, Sites, Forms, Integrations. No new tables required for this UX doc; CRM is a view and workflow on top of Leads + submissions + CRM activity.
5. CRM-specific UX checklist
When building CRM screens, ensure:
| Area | Rule |
|---|---|
| Tables | Same Card + table structure, row hover/selected, pagination (Pagination component, 20/50/100), optional checkbox + BulkActionsBar. |
| Side panel | Same overlay, resize, header (close, title, Full details, Prev/Next), body sections; reuse contact grid and timeline. |
| Filters | Saved views + QuickRangeFilter + search + Filters toggle + advanced bar (DropdownOptionSelect for project/source, same inputs for date/score/risk); Reset when any filter active. |
| Buttons | Same variants/sizes; “View in CRM” when crm_lead_url present; status change via select or kanban drop. |
| Modals | ConfirmDestructiveModal for destructive actions; same overlay/Card style for others. |
| Badges | Score (cold/warm/hot/very-hot), risk (destructive/outline), status (capitalized); same Badge component. |
| Search | Single search input, apply on Enter/button, clear, offset reset. |
| Theme/nav | Same ThemeProvider, sidebar, role-based nav; CRM entry in same sidebar group as Leads. |
6. Component inventory (dashboard)
Reuse these; do not reinvent for CRM:
| Component | Path | Use |
|---|---|---|
| Card, CardHeader, CardTitle, CardContent | @/components/ui/card | Sections, list container, timeline. |
| Button | @/components/ui/button | All actions. |
| Input | @/components/ui/input | Search, numeric/date filters. |
| Badge | @/components/ui/badge | Score, risk, status, counts. |
| Checkbox | @/components/ui/checkbox | Row selection. |
| Spinner | @/components/ui/spinner | Loading states. |
| Pagination | @/components/ui/pagination | Tables; DEFAULT_PAGE_SIZE_OPTIONS. |
| QuickRangeFilter | @/components/ui/quick-range-filter | Date presets. |
| BulkActionsBar | @/components/ui/bulk-actions-bar | When rows are selected. |
| ConfirmDestructiveModal | @/components/ui/confirm-destructive-modal | Destructive confirmations. |
| DropdownOptionSelect | @/components/dropdown-option-select | Project, Source, etc. |
| useApi | @/hooks/use-api | All API calls. |
| useBulkSelection | @/hooks/use-bulk-selection | Table selection. |
7. Summary
- Sales CRM in PulseGate should use the same UX as the existing Leads/Sessions experience: same data table pattern, same side panel, same filters (saved views, quick range, search, advanced bar), same buttons, modals, badges, and search behavior.
- Consistency is maintained by reusing the same layout, components, and page patterns (list + side panel, full detail page, optional kanban).
- Data comes from the current schema and APIs (Leads, LeadSubmissions, LeadCrmActivity, dropdown options); the CRM is a view and workflow layer on top of them.
- Use this document as the single reference when building or refining CRM screens so that the product feels one cohesive system.