For the complete documentation index, see llms.txt. Markdown variants are available by appending .md to documentation URLs. Homepage markdown is available at /llms.md/content.md. An agent skill is available at /.well-known/agent-skills/site-skill.md.

Marketing Dashboard 01

A responsive marketing overview with interactive charts and campaigns.

Installation

Set up the @cobalt registry first, as described in Installation, then add the block:

$ pnpm dlx shadcn@latest add @cobalt/marketing-dashboard-01

The dashboard uses local sample data. Pass popupClassName to scope the theme on chart menus rendered outside the dashboard.

Source

"use client";

import {
  RiCursorLine,
  RiEyeLine,
  RiGlobalLine,
  RiGroupLine,
  RiLayoutLine,
  RiMailLine,
  RiMoneyDollarCircleLine,
  RiSearchLine,
  RiShareForwardLine,
  RiWindowLine,
  RiMegaphoneLine,
  RiFileTextLine,
} from "@remixicon/react";
import { cn } from "cn";
import * as React from "react";

import { AreaChartCard } from "@/components/blocks/area-chart-card-01/components/area-chart-card";
import { BarListCard } from "@/components/blocks/bar-list-card-01/components/bar-list-card";
import { ComboChartCard } from "@/components/blocks/combo-chart-card-01/components/combo-chart-card";
import { FunnelChartCard } from "@/components/blocks/funnel-chart-card-01/components/funnel-chart-card";
import { CampaignsTable } from "@/components/blocks/marketing-dashboard-01/components/marketing-dashboard-campaigns-table";
import {
  investment,
  listTabs,
  monthly,
  stages,
} from "@/components/blocks/marketing-dashboard-01/components/marketing-dashboard-data";
import { RadialChartCard } from "@/components/blocks/radial-chart-card-01/components/radial-chart-card";
import { StatCards } from "@/components/blocks/stat-cards-01/components/stat-cards";
import type { Stat } from "@/components/blocks/stat-cards-01/components/stat-cards";
import { Checkbox } from "@/components/ui/checkbox";
import {
  Collapsible,
  CollapsibleContent,
} from "@/components/ui/collapsible";
import { Label } from "@/components/ui/label";
import { Reveal } from "@/components/ui/reveal";

const stats: Stat[] = [
  {
    icon: RiMoneyDollarCircleLine,
    label: "Investment",
    value: `$${investment.reduce((sum, channel) => sum + channel.value, 0).toLocaleString("en-US")}`,
    delta: "+8.4%",
    deltaColor: "lime",
  },
  {
    icon: RiEyeLine,
    label: "Views",
    value: "1.94M",
    delta: "+12.6%",
    deltaColor: "lime",
  },
  {
    icon: RiGroupLine,
    label: "Sign-ups",
    value: "1,286",
    delta: "+5.2%",
    deltaColor: "lime",
  },
  {
    icon: RiCursorLine,
    label: "Cost per visit",
    value: "$1.24",
    delta: "-3.1%",
    deltaColor: "rose",
  },
];

const channelIcon = [
  RiSearchLine,
  RiShareForwardLine,
  RiGroupLine,
  RiGlobalLine,
  RiMailLine,
  RiWindowLine,
];
const withIcons = (
  items: { label: string; value: number }[],
  icons: React.ElementType[]
) =>
  items.map((item, index) => {
    const Icon = icons[index % icons.length];
    return { ...item, icon: <Icon aria-hidden /> };
  });

// The chart cards draw in with a left-to-right wipe on their charts and bars.
const charts = "marketing-dashboard-01-draw";
// Inline panels fade in from just above, so a toggle never pops open.
const banner = "marketing-dashboard-01-banner";

/**
 * The body of a marketing overview: four KPI cards, a funnel, channel spend,
 * a ranked list, two charts and a campaign table. It carries no navigation or
 * heading, so it drops into an app shell or any page. A page can open the
 * filters panel and the new-campaign dialog from its own header with
 * `filtersOpen` and `newCampaignOpen`. Pass `popupClassName` (a theme scope)
 * so menus and tooltips rendered outside the block pick up the same tokens.
 */
export function MarketingDashboard({
  className,
  popupClassName,
  filtersOpen = false,
  newCampaignOpen,
  onNewCampaignOpenChange,
}: {
  className?: string;
  popupClassName?: string;
  filtersOpen?: boolean;
  newCampaignOpen?: boolean;
  onNewCampaignOpenChange?: (open: boolean) => void;
}) {
  const [activeOnly, setActiveOnly] = React.useState(false);
  const lists = [
    {
      id: "channels",
      label: "Channels",
      items: withIcons(listTabs.channels, channelIcon),
    },
    {
      id: "campaigns",
      label: "Campaigns",
      items: withIcons(listTabs.campaigns, [RiMegaphoneLine]),
    },
    {
      id: "pages",
      label: "Landing pages",
      items: withIcons(listTabs.pages, [RiFileTextLine, RiLayoutLine]),
    },
  ];
  return (
    <div className={cn("@container flex min-w-0 flex-col gap-4", className)}>
      <Collapsible open={filtersOpen} className="contents">
        <CollapsibleContent>
          <div
            className={cn(
              "flex w-fit items-center gap-2 rounded-[8px] bg-muted px-3 py-2.5",
              banner
            )}
          >
            <Label variant="inline">
              <Checkbox
                checked={activeOnly}
                onCheckedChange={(checked) => setActiveOnly(checked)}
              />
              Active campaigns only
            </Label>
          </div>
        </CollapsibleContent>
      </Collapsible>
      <StatCards stats={stats} popupClassName={popupClassName} />
      <Reveal delay={160} blur>
        <div className={cn(charts, "grid grid-cols-1 gap-4 @5xl:grid-cols-3")}>
          <FunnelChartCard
            title="Audience journey"
            stages={stages}
            delta={0.058}
            ranges={[
              { label: "Last 30 days", stages, delta: 0.058 },
              {
                label: "Last 90 days",
                stages: stages.map((s) => ({ ...s, value: s.value * 3 })),
                delta: 0.081,
              },
            ]}
            popupClassName={popupClassName}
          />
          <RadialChartCard
            title="Channel investment"
            data={investment}
            variant="stacked"
            delta="+8.4%"
            ranges={[
              { label: "Last 30 days", data: investment, delta: "+8.4%" },
              {
                label: "Last 90 days",
                data: investment.map((c) => ({ ...c, value: c.value * 3 })),
                delta: "+9.1%",
              },
            ]}
            popupClassName={popupClassName}
            className="max-w-none"
          />
          <BarListCard
            title="Traffic sources"
            metricLabel="Sessions"
            tabs={lists}
          />
        </div>
      </Reveal>
      <Reveal delay={200} blur>
        <div className={cn(charts, "grid grid-cols-1 gap-4 @4xl:grid-cols-2")}>
          <ComboChartCard
            title="Investment"
            tiles
            data={monthly}
            bar={{
              key: "spend",
              label: "Investment",
              format: (n) => `$${(n / 1000).toFixed(1)}K`,
            }}
            line={{
              key: "return",
              label: "Return",
              color: "var(--funnel-chart-2, var(--primary))",
              format: (n) => `${n.toFixed(1)}x`,
            }}
            delta={0.094}
            ranges={[
              { id: "year", label: "This year", data: monthly },
              { id: "half", label: "Last 6 months", data: monthly.slice(6) },
            ]}
            popupClassName={popupClassName}
          />
          <AreaChartCard
            title="Visitors"
            tiles
            data={monthly}
            series={[
              {
                key: "search",
                label: "Search",
                color: "var(--funnel-chart-1, var(--chart-1))",
              },
              {
                key: "social",
                label: "Social",
                color: "var(--funnel-chart-2, var(--chart-4))",
              },
              {
                key: "email",
                label: "Email",
                color: "var(--funnel-chart-3, var(--chart-5))",
              },
            ]}
            delta={0.088}
            ranges={[
              { id: "year", label: "This year", data: monthly },
              { id: "half", label: "Last 6 months", data: monthly.slice(6) },
            ]}
            popupClassName={popupClassName}
          />
        </div>
      </Reveal>
      <Reveal delay={240} blur>
        <CampaignsTable
          popupClassName={popupClassName}
          activeOnly={activeOnly}
          newOpen={newCampaignOpen}
          onNewOpenChange={onNewCampaignOpenChange}
        />
      </Reveal>
    </div>
  );
}