Customization

Last updated on 2026-04-05

The Sales Dashboard Kit is designed to be customized for your sales team or SaaS product. All components use design tokens and Tailwind CSS utilities, making brand adaptation straightforward.

Changing Colors

Update CSS custom properties in app/globals.css. The default theme uses oklch hue 250. Change the hue to rebrand the entire kit:

:root {
  /* Change from blue (hue 250) to emerald (hue 160) */
  --primary: oklch(0.50 0.18 160);
  --primary-foreground: oklch(0.98 0 0);
  --accent: oklch(0.94 0.02 160);
  --accent-foreground: oklch(0.35 0.15 160);
  --ring: oklch(0.55 0.18 160);
}

.dark {
  --primary: oklch(0.70 0.18 160);
  --primary-foreground: oklch(0.15 0.02 160);
}

All 9 screens automatically inherit the new colors -- buttons, badges, links, charts, sidebar accents, pipeline stages, progress bars, and focus rings all update at once.

Changing Typography

Configure fonts in app/layout.tsx:

import { Inter, DM_Sans, JetBrains_Mono } from "next/font/google";

const inter = Inter({ subsets: ["latin"], variable: "--font-inter" });
const dmSans = DM_Sans({ subsets: ["latin"], variable: "--font-dm-sans" });
const jetbrainsMono = JetBrains_Mono({ subsets: ["latin"], variable: "--font-jetbrains" });

Swap these for any Google Font or local font files. The CSS variables --font-sans, --font-heading, and --font-mono control body text, headings, and code respectively.

Adding Sales Stages

Edit the pipeline stages in data/seed.ts and the corresponding type in types/index.ts:

// data/seed.ts
export const pipelineStages: PipelineStage[] = [
  { id: "prospecting", name: "Prospecting", color: "gray", order: 0 },
  { id: "qualification", name: "Qualification", color: "blue", order: 1 },
  { id: "discovery", name: "Discovery", color: "cyan", order: 2 },    // New stage
  { id: "proposal", name: "Proposal", color: "indigo", order: 3 },
  { id: "negotiation", name: "Negotiation", color: "violet", order: 4 },
  { id: "contract", name: "Contract Review", color: "amber", order: 5 }, // New stage
  { id: "closed-won", name: "Closed Won", color: "green", order: 6 },
  { id: "closed-lost", name: "Closed Lost", color: "red", order: 7 },
]

The pipeline board, deal list stage badges, conversion funnel, and reports all dynamically render from this stages array, so new stages appear everywhere automatically.

Modifying Deal Fields

Extend the Deal interface in types/index.ts to add custom fields:

// types/index.ts
export interface Deal {
  id: string
  name: string
  company: string
  value: number
  stage: string
  owner: string
  closeDate: string
  priority: "hot" | "warm" | "cold"
  // Add custom fields
  productLine: string           // e.g., "Enterprise", "SMB", "Startup"
  competitorInDeal: string      // e.g., "Salesforce", "HubSpot", "None"
  nextStepDate: string          // Next scheduled action date
  decisionMaker: string         // Key contact name
  lossReason?: string           // Reason for lost deals
}

Then update the DealRow and PipelineCard components to display the new fields, and add corresponding mock data to data/seed.ts.

Connecting a CRM API

Replace the seed data imports in your page components with API calls to your CRM platform.

Salesforce REST API

// app/api/salesforce/deals/route.ts
export async function GET() {
  const tokenRes = await fetch("https://login.salesforce.com/services/oauth2/token", {
    method: "POST",
    body: new URLSearchParams({
      grant_type: "client_credentials",
      client_id: process.env.SALESFORCE_CLIENT_ID!,
      client_secret: process.env.SALESFORCE_CLIENT_SECRET!,
    }),
  })
  const { access_token, instance_url } = await tokenRes.json()

  const res = await fetch(
    `${instance_url}/services/data/v60.0/query?q=SELECT+Id,Name,Amount,StageName,CloseDate+FROM+Opportunity`,
    { headers: { Authorization: `Bearer ${access_token}` } }
  )
  const data = await res.json()
  return Response.json(data.records)
}

HubSpot API

// app/api/hubspot/deals/route.ts
export async function GET() {
  const res = await fetch(
    "https://api.hubapi.com/crm/v3/objects/deals?limit=100&properties=dealname,amount,dealstage,closedate",
    { headers: { Authorization: `Bearer ${process.env.HUBSPOT_ACCESS_TOKEN}` } }
  )
  const data = await res.json()
  return Response.json(data.results)
}

Supabase

import { createClient } from "@supabase/supabase-js"

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)

const { data: deals } = await supabase
  .from("deals")
  .select("*, owner:team_members(*), company:companies(*)")
  .order("created_at", { ascending: false })

Building Custom Reports

Add a new report section by creating a chart component and wiring it to your data:

// components/charts/revenue-by-region-chart.tsx
"use client"

import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer } from "recharts"

interface RevenueByRegionProps {
  data: { region: string; revenue: number }[]
}

export function RevenueByRegionChart({ data }: RevenueByRegionProps) {
  return (
    <ResponsiveContainer width="100%" height={300}>
      <BarChart data={data}>
        <XAxis dataKey="region" />
        <YAxis />
        <Tooltip />
        <Bar dataKey="revenue" fill="var(--chart-1)" radius={[4, 4, 0, 0]} />
      </BarChart>
    </ResponsiveContainer>
  )
}

Then add it to the reports page:

// app/(dashboard)/reports/page.tsx
import { RevenueByRegionChart } from "@/components/charts/revenue-by-region-chart"
import { regionRevenueData } from "@/data/seed"

<section aria-label="Revenue by Region">
  <h2 className="font-heading text-xl font-bold">Revenue by Region</h2>
  <RevenueByRegionChart data={regionRevenueData} />
</section>

Customizing the Sidebar

Edit components/layout/app-sidebar.tsx to add or remove navigation items:

const navItems = [
  { label: "Dashboard", href: "/", icon: LayoutDashboard },
  { label: "Pipeline", href: "/pipeline", icon: Kanban },
  { label: "Deals", href: "/deals", icon: Handshake },
  { label: "Targets", href: "/targets", icon: Target },
  { label: "Leaderboard", href: "/leaderboard", icon: Trophy },
  { label: "Reports", href: "/reports", icon: BarChart3 },
  // Add custom pages
  { label: "Forecasting", href: "/forecasting", icon: TrendingUp },
  // Remove items you don't need
]

Each item takes a label, href, and a Lucide icon component. Active state highlighting is handled automatically based on the current pathname.

Replacing Seed Data

All mock data lives in data/seed.ts. Replace it with your data source:

API calls

import { getDeals } from "@/lib/api"

export default async function DealsPage() {
  const deals = await getDeals()
  return <DealsList deals={deals} />
}

Database (Prisma, Drizzle)

import { db } from "@/lib/db"

const deals = await db.deal.findMany({
  orderBy: { updatedAt: "desc" },
  include: { owner: true, company: true },
})

Removing Unused Features

Delete directories you don't need:

  • Don't need leaderboard? Delete app/(dashboard)/leaderboard/ and components/sales/rep-rank-card.tsx
  • Don't need targets? Delete app/(dashboard)/targets/ and components/sales/target-card.tsx, components/sales/quota-progress.tsx
  • Don't need reports? Delete app/(dashboard)/reports/ and the chart components you no longer reference
  • Run pnpm build to verify no broken imports

Using shadcn/ui CLI

Add new components alongside the kit:

npx shadcn@latest add <component-name>

Components install to components/ui/ and integrate seamlessly with the existing design tokens. The kit uses the new-york style with CSS variables enabled.