Contacts & Companies

Last updated on 2026-03-22

The contacts and companies modules form the core of the CRM's data model. Contacts represent individual people, companies represent organizations, and the two are linked through the companyId field. All screens share the (dashboard) route group layout.

Contacts List

Route: /contacts

The primary contacts view with a feature-rich data table supporting search, filtering, and view toggling.

  • Data table -- columns for avatar, full name (linked to detail), email, phone, company (linked to company detail), total deal value, last contacted date, tags (as colored badges), and a row actions dropdown
  • Search -- text input filtering contacts by name, email, or phone number
  • Filters -- dropdown filters for company and tag, with a "Clear filters" button when active
  • View toggle -- switch between table view and card grid view using ToggleGroup
  • Card view -- responsive grid of contact cards showing avatar, name, company, email, deal value, and tags
  • Pagination -- page-based navigation with configurable page size (10, 25, 50)
  • Row actions -- dropdown menu with View, Edit, and Delete options; delete triggers an AlertDialog confirmation
import { ContactsTable } from "@/components/contacts/contacts-table"
import { ContactCard } from "@/components/contacts/contact-card"
import { ContactFilters } from "@/components/contacts/contact-filters"

Data Sources

Data Source Location
Contacts contacts data/seed.ts
Companies (for filter) companies data/seed.ts
Tags tags data/seed.ts

Contact Detail

Route: /contacts/[id]

A detailed view of an individual contact with a split layout: main content area with tabs and a right sidebar with summary information.

  • Hero section -- avatar, full name, title, company link, email (mailto), phone (tel), and social links
  • Tabbed content -- three tabs for different data views:
    • Overview -- contact information card, recent activity timeline, and notes section
    • Deals -- data table of all deals associated with this contact, showing deal name, stage badge, value, probability, and close date
    • Activities -- filtered activity feed showing only activities related to this contact
  • Right sidebar -- deal summary (total value, open deals count, won deals count), assigned owner with avatar, tags as editable badges, lead source badge, and quick action buttons (Log Call, Send Email, Add Note)
import { ContactHero } from "@/components/contacts/contact-hero"
import { ContactSidebar } from "@/components/contacts/contact-sidebar"
import { ContactTabs } from "@/components/contacts/contact-tabs"

Contact Detail Layout

┌───────────────────────────────┬─────────────┐
│ Avatar  Name  Title           │ Deal Summary│
│ Company  Email  Phone         │ Owner       │
├───────────────────────────────┤ Tags        │
│ [Overview] [Deals] [Activity] │ Lead Source │
│                               │ Quick       │
│ Tab content area              │ Actions     │
│                               │             │
└───────────────────────────────┴─────────────┘

Contact Create / Edit

Route: /contacts/new and /contacts/[id]/edit

A Zod-validated form for creating or editing contacts. The form is organized into logical sections with inline validation and error messages.

  • Personal Info -- first name, last name, email (validated), phone, job title
  • Company -- company select dropdown (searchable) or "Add new company" inline
  • Address -- street, city, state, zip, country select
  • Social -- LinkedIn URL, Twitter handle, website URL
  • Tags -- multi-select tag picker with the ability to create new tags
  • Lead Source -- select dropdown (Website, Referral, LinkedIn, Cold Call, Event, Other)
  • Owner -- team member select dropdown with avatar previews
  • Notes -- freeform textarea for additional context
import { ContactForm } from "@/components/contacts/contact-form"
import { contactSchema } from "@/lib/schemas"

Validation Schema

const contactSchema = z.object({
  firstName: z.string().min(1, "First name is required"),
  lastName: z.string().min(1, "Last name is required"),
  email: z.string().email("Invalid email address"),
  phone: z.string().optional(),
  jobTitle: z.string().optional(),
  companyId: z.string().optional(),
  address: addressSchema.optional(),
  social: socialSchema.optional(),
  tags: z.array(z.string()),
  leadSource: z.enum(["website", "referral", "linkedin", "cold_call", "event", "other"]),
  ownerId: z.string().min(1, "Owner is required"),
  notes: z.string().optional(),
})

Contact Import

Route: /contacts/import

A 3-step wizard for importing contacts from a CSV file. Each step validates before allowing progression to the next.

Step 1: Upload CSV

  • Drag-and-drop zone -- file upload area accepting .csv files up to 5MB
  • File preview -- shows filename, size, and row count after upload
  • Template download -- link to download a sample CSV template with the expected column headers
  • Validation -- checks file type, size, and basic CSV structure

Step 2: Column Mapping

  • Column mapper -- side-by-side mapping interface pairing CSV columns to CRM contact fields
  • Auto-mapping -- automatically maps columns with matching header names (e.g., "First Name" maps to firstName)
  • Preview row -- shows first data row values next to each mapped field for verification
  • Required fields -- marks firstName, lastName, and email as required; prevents advancing if unmapped

Step 3: Preview & Confirm

  • Data preview table -- paginated table showing all rows as they will be imported, with mapped field values
  • Duplicate detection -- highlights rows where the email matches an existing contact, with options to skip or update
  • Error rows -- rows failing validation are highlighted in red with error descriptions
  • Summary -- total rows, valid rows, duplicate rows, and error rows counts
  • Import button -- triggers the import with a progress bar and success toast
import { ImportWizard } from "@/components/contacts/import-wizard"
import { ColumnMapper } from "@/components/contacts/column-mapper"
import { ImportPreview } from "@/components/contacts/import-preview"

Companies List

Route: /companies

A data table of all companies tracked in the CRM with essential business information.

  • Data table -- columns for logo (or initials fallback), company name (linked to detail), industry badge, employee count, annual revenue, deal count, location (city, country), and row actions dropdown
  • Search -- text input filtering by company name or industry
  • Filter -- dropdown filter by industry
  • Sorting -- column header click to sort by name, revenue, deal count, or employees
  • Row actions -- dropdown with View, Edit, and Delete options
import { CompaniesTable } from "@/components/companies/companies-table"

Data Sources

Data Source Location
Companies companies data/seed.ts
Industries industries data/seed.ts

Company Detail

Route: /companies/[id]

A detailed view of a company with hero, tabbed content, and sidebar -- mirroring the contact detail layout.

  • Hero section -- company logo (or initials), name, industry badge, website link, location, employee count, and annual revenue
  • Tabbed content -- four tabs:
    • Overview -- company description, key metrics cards (total deal value, active deals, contacts), and recent activity timeline
    • Contacts -- data table of all contacts at this company with name, title, email, phone, and last contacted
    • Deals -- data table of all deals with this company showing deal name, stage, value, owner, and close date
    • Activities -- filtered activity feed for this company
  • Sidebar -- industry, founded year, company size, website, social links, assigned account owner, and tags
import { CompanyHero } from "@/components/companies/company-hero"
import { CompanySidebar } from "@/components/companies/company-sidebar"
import { CompanyTabs } from "@/components/companies/company-tabs"

Company Create / Edit

Route: /companies/new and /companies/[id]/edit

A Zod-validated form for creating or editing companies.

  • Company Info -- name, industry select, website URL, phone, description textarea
  • Location -- street, city, state, zip, country select
  • Details -- employee count (number input), annual revenue (currency input), founded year
  • Social -- LinkedIn company URL, Twitter handle
  • Account Owner -- team member select with avatar previews
  • Tags -- multi-select tag picker
import { CompanyForm } from "@/components/companies/company-form"
import { companySchema } from "@/lib/schemas"

Next Steps