Design
llms.txt digitalcrew.tech

Analytics

FunnelChart

A vertical column funnel with a summary panel.

MaxAnalyticsDigitalCrew.FunnelChart
FunnelChart · live · 1100px wideOpen

Guidelines#

Each stage shows its count above a bar, a soft area joins the bar tops, numbered markers sit on a connector, and each label carries its conversion to the next stage. The panel shows the overall and per-step conversion.

Use it for acquisition and campaign funnels. In Max the funnel runs Leads enrolled → Engaged (opens + connections) → Replied (email + LinkedIn + WhatsApp) → Meetings booked. The grid adapts to any number of stages.

What you provide: title, optional subtitle, isLoading, and stages: AnalyticsFunnelStageDto[]. Each stage is { key, label, count, pct_of_first, pct_of_previous }, with percentages from 0 to 100. The chart prints them as ${value}%, so pass them already rounded; the server rounds to one decimal.

Anatomy: the panel is glass-panel border-glass rounded-3xl px-6 py-6 and the whole chart uses one hue, var(--chart-2). The bar zone is 176px tall. Bars are max-w-14 (sm:max-w-16) and rounded-t-md. A non-zero stage keeps at least 4% height; a true zero draws nothing. The joining area is filled at 10% opacity. Markers are 28px circles in border-glass bg-background. The summary aside is rounded-2xl bg-muted/40, lg:w-60, and sits beside the chart from lg.

States: while loading, a 4-column skeleton. When every count is 0: "No funnel activity in the selected period".

Don't count activity rows or pass non-monotonic stages. Count distinct leads and fold each stage into the one before it, so no conversion exceeds 100%.

Facts#

GroupAnalytics
ProductMax
Globalwindow.DigitalCrew.FunnelChart
Sourcemax-agent: src/features/analytics/ui/funnel-chart.tsx
Import in the appimport { FunnelChart } from "@/features/analytics/ui/funnel-chart";

API#

TypeScript declarations emitted from src/features/analytics/ui/funnel-chart.tsx.

/**
 * Vertical column funnel: one column per stage with the count above a
 * baseline-anchored bar, a soft area joining stage tops, numbered step
 * markers on a connector line, and stage-over-stage conversion beneath.
 * A summary panel carries the overall + per-step conversion rates.
 */
export declare function FunnelChart({ title, subtitle, stages, isLoading, }: {
    title: string;
    subtitle?: string;
    stages: AnalyticsFunnelStageDto[];
    isLoading: boolean;
}): JSX.Element;

Example#

The code of the preview above: plain React.createElement against window.DigitalCrew, with realistic data.

<div id="root" class="p-6"></div>
<script>
(function () {
  var h = React.createElement, DC = window.DigitalCrew, I = DC.Icons;
  // AnalyticsFunnelStageDto[]: distinct leads per stage, monotonic, with the
  // same rounding as core/acquisition-funnel.ts (one decimal place).
  function pct(n, d) { return d === 0 ? 0 : Math.round((n / d) * 1000) / 10; }
  var RAW = [
    { key: "contacted", label: "Leads enrolled", count: 1240 },
    { key: "engaged", label: "Engaged (opens + connections)", count: 684 },
    { key: "replied", label: "Replied (email + LinkedIn + WhatsApp)", count: 173 },
    { key: "meetings", label: "Meetings booked", count: 41 },
    { key: "deals", label: "Deals created", count: 15 }
  ];
  var STAGES = RAW.map(function (s, i) {
    return {
      key: s.key, label: s.label, count: s.count,
      pct_of_first: pct(s.count, RAW[0].count),
      pct_of_previous: pct(s.count, i === 0 ? RAW[0].count : RAW[i - 1].count)
    };
  });
  function App() {
    return h(DC.FunnelChart, {
      title: "Acquisition funnel",
      subtitle: "Q3 outbound · from first touch to deal created",
      stages: STAGES,
      isLoading: false
    });
  }
  ReactDOM.createRoot(document.getElementById("root")).render(h(DC.TooltipProvider, { delayDuration: 80 }, h(App)));
})();
</script>