Skip to content

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).

ScenarioHow 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 handoffNew 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:

  1. Before building — Skim Section 2 (design system) and Section 3 (page patterns); decide which pattern fits (list + side panel vs full detail vs kanban).
  2. While building — Use Section 5 (checklist) and Section 6 (component inventory); match table structure, filter bar, and side panel behaviour to the spec.
  3. 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

ElementCurrent implementationCRM usage
App shellLayout: Sidebar + main content area; p-6 main paddingSame. CRM views live inside the same shell.
SidebarSidebar: grouped nav (Overview, Leads & pipeline, Sessions, Settings, etc.), collapsible, theme + theme-color in footerAdd a “CRM” or “Pipeline” group (or keep under “Leads & pipeline”) so CRM entry point is clear.
Page titleTop of page: <h1 className="text-xl font-semibold">Leads</h1> + optional total countUse 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.idbg-primary/5 (or equivalent).
  • Row content: Use Badge for status/score/risk; text-muted-foreground for secondary text; truncate long text with truncate and optional title={fullText}.
  • Checkbox column: First column for bulk selection when applicable; use Checkbox + useBulkSelection + BulkActionsBar (see below).
  • Pagination: Use Pagination component below the table with total, 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 with text-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-auto when open; click overlay to close.
  • Panel: <aside> fixed right, full height, border-l bg-background shadow-xl, translate-x-0 when open / translate-x-full when closed, with transition-[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 in space-y-5 sections.
  • Sections: Contact info grid (label text-xs text-muted-foreground, value text-sm), Badges row, then blocks like “Follow-up history”, “Activity timeline”.
  • Escape: Close panel on Escape (existing useEffect on keydown).
  • Resize: onMouseDown on drag handle → setIsResizing(true); mousemove/mouseup to 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", others variant="outline", size="sm", className="h-7 text-xs". Click applies a predefined filter set and resets offset.
  • Quick date range: Use QuickRangeFilter with options like Today, Yesterday, Past 7 days, Past 2 weeks, Past 30 days, All time. Same component as Leads/Sessions; same value/onChange pattern.
  • Search: Single search input with Search icon (pl-9), placeholder e.g. “Search name, email, phone, project…”. “Search” button to apply (sets q and offset=0). “Clear” button when filters.q is 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 showFilters is true. Wrapped in <Card> with <CardContent>. Grid of controls (e.g. grid gap-3 sm:grid-cols-2 lg:grid-cols-4):
    • Dropdowns: Use DropdownOptionSelect for Project, Source (and any other option sets from DropdownOptions). Same kind, value, onChange, placeholder, label pattern.
    • 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 ...">.
  • 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; default for page-level actions.
  • Icons: Lucide icons; place with className="mr-1 h-3.5 w-3.5" or similar; pair with text for clarity. Use size="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: Badge with scoreBadgeVariant(score) → cold / warm / hot / very-hot; show numeric score + label (e.g. “72 Hot”).
  • Risk/suspicion: variant="destructive" when score ≥ 40, else variant="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> (optional CardTitle) 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.

  • 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 offset to 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)

  1. Page title + total count.
  2. Saved views (preset filter buttons).
  3. Quick date range (QuickRangeFilter).
  4. Optional “Recent activity” or similar card.
  5. Search + Filters toggle + Reset.
  6. Advanced filter bar (conditional Card).
  7. Main Card: table (or kanban) with optional bulk bar; pagination below table when in table mode.
  8. 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)

  1. Back button + breadcrumb (e.g. “Back to leads” + “/ Lead #123”).
  2. Primary summary Card (contact name, key fields, status, “View in CRM” if applicable).
  3. Grid of contact fields (Email, Phone, Project, Source, Location, IP, Referer, Pipeline status).
  4. Badges row (score, risk, returns, duration, etc.).
  5. Optional blocks: Suspicion reasons, Related projects.
  6. “All submissions” (or equivalent) as child cards in a grid.
  7. 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/leads with limit, 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 by status when 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/:id with { status } for pipeline/kanban.
  • Dropdown options: GET /analytics/dropdown-options?kind=project|source|... for Project, Source filters. Use DropdownOptionSelect with same kind/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:

AreaRule
TablesSame Card + table structure, row hover/selected, pagination (Pagination component, 20/50/100), optional checkbox + BulkActionsBar.
Side panelSame overlay, resize, header (close, title, Full details, Prev/Next), body sections; reuse contact grid and timeline.
FiltersSaved views + QuickRangeFilter + search + Filters toggle + advanced bar (DropdownOptionSelect for project/source, same inputs for date/score/risk); Reset when any filter active.
ButtonsSame variants/sizes; “View in CRM” when crm_lead_url present; status change via select or kanban drop.
ModalsConfirmDestructiveModal for destructive actions; same overlay/Card style for others.
BadgesScore (cold/warm/hot/very-hot), risk (destructive/outline), status (capitalized); same Badge component.
SearchSingle search input, apply on Enter/button, clear, offset reset.
Theme/navSame ThemeProvider, sidebar, role-based nav; CRM entry in same sidebar group as Leads.

6. Component inventory (dashboard)

Reuse these; do not reinvent for CRM:

ComponentPathUse
Card, CardHeader, CardTitle, CardContent@/components/ui/cardSections, list container, timeline.
Button@/components/ui/buttonAll actions.
Input@/components/ui/inputSearch, numeric/date filters.
Badge@/components/ui/badgeScore, risk, status, counts.
Checkbox@/components/ui/checkboxRow selection.
Spinner@/components/ui/spinnerLoading states.
Pagination@/components/ui/paginationTables; DEFAULT_PAGE_SIZE_OPTIONS.
QuickRangeFilter@/components/ui/quick-range-filterDate presets.
BulkActionsBar@/components/ui/bulk-actions-barWhen rows are selected.
ConfirmDestructiveModal@/components/ui/confirm-destructive-modalDestructive confirmations.
DropdownOptionSelect@/components/dropdown-option-selectProject, Source, etc.
useApi@/hooks/use-apiAll API calls.
useBulkSelection@/hooks/use-bulk-selectionTable 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.