Analytics
ActivityChart
A day-bucketed Recharts chart in Max's glass panel: each series is drawn as an area, a line or a bar, with a dot legend and a popover tooltip.
Guidelines#
Use it for activity over a period on the analytics page, a campaign's performance tab and detail sheets.
What you provide: title (a 12px uppercase overline), data (one point per day, date as YYYY-MM-DD plus numeric keys; the default point type is AnalyticsTimeseriesPointDto), series ({ key, label, color, kind: "area" | "line" | "bar" }) and isLoading. Take the presets from chart-series.ts: VOLUME_SERIES, ENGAGEMENT_SERIES, DEALS_SERIES, with colours C1–C4 = var(--chart-1…4).
Anatomy: glass-panel glass-hover border border-glass rounded-3xl px-6 py-6. The plot is 280px tall. The grid is dashed 3 3, horizontal only, drawn in var(--border) at 50% opacity. Axis ticks are 11px muted-foreground with no axis or tick lines. The X axis reads MMM d with a 32px minimum tick gap. The Y axis is 48px wide and shows integers only. Areas are monotone with a 2px stroke and 12% fill. Lines are 2px with no dots and a 4px active dot. Bars have [4,4,0,0] radii and a maximum width of 24px. The tooltip is rounded-xl border-glass bg-popover shadow-md.
States: while isLoading, a 280px Skeleton (rounded-2xl) replaces the chart. When no series has a value above 0, a dashed box reads "No activity in this period".
Don't hard-code hex colours; pass the --chart-N variables so the chart follows the theme. Don't expect stacking: areas overlap.
Facts#
| Group | Analytics |
|---|---|
| Product | Max |
| Global | window.DigitalCrew.ActivityChart |
| Source | max-agent: src/features/analytics/ui/activity-chart.tsx |
| Import in the app | import { ActivityChart } from "@/features/analytics/ui/activity-chart"; |
API#
TypeScript declarations emitted from src/features/analytics/ui/activity-chart.tsx.
/** A day-bucketed point: the date key plus numeric series values. */
export type ChartPoint = {
date: string;
};
export interface ChartSeries<Point extends ChartPoint = AnalyticsTimeseriesPointDto> {
key: Exclude<keyof Point, "date"> & string;
label: string;
/** CSS color (use the theme's --chart-N variables) */
color: string;
kind: "area" | "line" | "bar";
}
export declare function ActivityChart<Point extends ChartPoint = AnalyticsTimeseriesPointDto>({ title, data, series, isLoading, }: {
title: string;
data: Point[];
series: ChartSeries<Point>[];
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;
// VOLUME_SERIES from src/features/analytics/ui/chart-series.ts
var SERIES = [
{ key: "email_sent", label: "Emails sent", color: "var(--chart-1)", kind: "area" },
{ key: "linkedin_invitations_sent", label: "LinkedIn invitations", color: "var(--chart-2)", kind: "area" },
{ key: "linkedin_messages_sent", label: "LinkedIn messages", color: "var(--chart-3)", kind: "area" }
];
// Sat 12 Sep to Fri 25 Sep 2026: quiet weekends, busy weekdays.
var SENT = [18, 12, 212, 236, 248, 231, 204, 22, 15, 244, 262, 275, 258, 221];
var INV = [4, 2, 58, 64, 61, 66, 52, 5, 3, 67, 72, 70, 74, 60];
var MSG = [6, 3, 41, 47, 52, 44, 38, 7, 4, 49, 55, 58, 53, 46];
// One AnalyticsTimeseriesPointDto per day (every field of the DTO is present).
var DATA = SENT.map(function (sent, i) {
return {
date: new Date(Date.UTC(2026, 8, 12 + i)).toISOString().slice(0, 10),
email_sent: sent,
email_opened: Math.round(sent * 0.52),
email_replied: Math.round(sent * 0.041),
linkedin_invitations_sent: INV[i],
linkedin_connections: Math.round(INV[i] * 0.38),
linkedin_messages_sent: MSG[i],
linkedin_replies: Math.round(MSG[i] * 0.12),
whatsapp_messages_sent: 0,
whatsapp_replies: 0,
meetings_booked: i % 3 === 0 ? 1 : 2,
deals_created: 0,
deals_won: 0,
prospect_lists_created: 0,
signals_detected: 0
};
});
function App() {
return h(DC.ActivityChart, { title: "Outreach volume", data: DATA, series: SERIES, isLoading: false });
}
ReactDOM.createRoot(document.getElementById("root")).render(h(DC.TooltipProvider, { delayDuration: 80 }, h(App)));
})();
</script>