Claude Code से डेटा विज़ुअलाइज़ेशन रेवेन्यू डैशबोर्ड बनाएं
Claude Code से data visualization बनाएं: Recharts, D3, CSV aggregation, accessibility और Playwright checks।
पहले decision तय करें, chart बाद में बनाएं
Claude Code को सिर्फ “data visualization बना दो” कहने से अक्सर सुंदर demo मिल जाता है, लेकिन business decision में मदद नहीं मिलती। अच्छे dashboard की शुरुआत chart type से नहीं, बल्कि इस सवाल से होती है: कौन यह data देखेगा और क्या फैसला करेगा? ClaudeCodeLab जैसे content और product site में pageviews अकेले काफी नहीं हैं। असली सवाल हैं: readers article पूरा पढ़ते हैं या नहीं, CTA पर click करते हैं या नहीं, products देखते हैं या नहीं, training consultation form भरते हैं या नहीं, और free checklist से paid template तक जाते हैं या नहीं।
इस guide में हम React, TypeScript, Recharts, छोटा D3 helper, CSV aggregation, accessible labels, responsive layout और Playwright screenshot checks के साथ content analytics और monetization dashboard बनाएंगे। Recharts को React components वाली chart library समझें। D3 को lower-level toolbox समझें, जिससे scale, transformation और custom calculation किए जाते हैं। Practical काम में पहले Recharts से dashboard बनाएं, फिर जहां जरूरी हो वहां D3 जोड़ें।
Data contract का मतलब है data source और UI के बीच साफ वादा: date ISO date होगा, sessions number होगा, revenue numeric USD होगा, और channel fixed list से आएगा। Aggregation का मतलब raw rows को channel revenue या conversion rate जैसे summary numbers में बदलना है। Accessibility का मतलब chart सिर्फ color और mouse पर निर्भर न रहे; heading, table, contrast और keyboard-friendly controls से भी समझ आए।
Related ClaudeCodeLab guides: analytics implementation, dashboard development और accessibility। Official references: Claude Code docs, Recharts getting started, D3 getting started, Playwright screenshots, WCAG non-text contrast।
Practical use cases
| Use case | Metrics | Chart | Action |
|---|---|---|---|
| Article improvement | Read completion, CTA clicks, organic traffic | Line और bar | Intro, internal links और CTA position बदलें |
| Product revenue | Product clicks, revenue, channel mix | Bar और share chart | Product card, pricing copy, comparison table सुधारें |
| Training consulting | Lead form, downloads, B2B traffic | KPI cards और funnel | Consultation path साफ करें |
| AdSense quality | Read depth, scroll, ad-adjacent exits | Annotated trend | Ads बढ़ाने से पहले reading experience बचाएं |
Chart selection simple रखें। Time trend के लिए line, category comparison के लिए bar, few categories के लिए share chart, और तुरंत पढ़े जाने वाले numbers के लिए KPI cards। Beginner dashboard में dual-axis chart avoid करें, क्योंकि revenue और conversion rate गलत तरीके से related लग सकते हैं।
flowchart LR
Raw["CSV / analytics events"]
Contract["data contract"]
Aggregate["aggregation"]
Chart["Recharts dashboard"]
Review["accessibility and Playwright check"]
CTA["training / products / consultation CTA"]
Raw --> Contract --> Aggregate --> Chart --> Review --> CTA
Claude Code prompt
Build a React + TypeScript content analytics dashboard for ClaudeCodeLab.
Use Recharts for the main charts and a small D3 helper only for scale calculations.
Input data must follow a documented data contract: date, channel, sessions, signups, revenue, readRate.
Include sample data, CSV parsing, channel aggregation, loading state, error state, empty state, accessible labels, a table fallback, and responsive layout.
Avoid misleading charts: no truncated bar axis, no dual-axis chart, no color-only meaning.
Add Playwright screenshot checks for mobile and desktop.
Return copy-pasteable code and explain the failure modes to review.
यह prompt Claude Code को सिर्फ UI नहीं, बल्कि data shape, failure states, accessibility और visual evidence भी बनाने के लिए मजबूर करता है।
Install
npm i recharts d3
npm i -D @types/d3 @playwright/test
Data contract और CSV aggregation
Chart से पहले data contract बनाएं। इससे Claude Code field names guess नहीं करेगा, revenue को string नहीं मानेगा, और NaN tooltip तक नहीं पहुंचेगा।
// dashboard-data.ts
export type Channel = "organic" | "email" | "social" | "referral" | "paid";
export type TrafficRow = {
date: string;
channel: Channel;
sessions: number;
signups: number;
revenue: number;
readRate: number;
};
export type ChannelSummary = {
channel: Channel;
sessions: number;
signups: number;
revenue: number;
conversionRate: number;
arps: number;
};
const channels: Channel[] = ["organic", "email", "social", "referral", "paid"];
export const sampleRows: TrafficRow[] = [
{ date: "2026-05-01", channel: "organic", sessions: 1280, signups: 42, revenue: 840, readRate: 0.61 },
{ date: "2026-05-01", channel: "email", sessions: 420, signups: 31, revenue: 1240, readRate: 0.74 },
{ date: "2026-05-01", channel: "social", sessions: 680, signups: 18, revenue: 260, readRate: 0.49 },
{ date: "2026-05-02", channel: "organic", sessions: 1360, signups: 48, revenue: 980, readRate: 0.64 },
{ date: "2026-05-02", channel: "referral", sessions: 310, signups: 17, revenue: 510, readRate: 0.58 },
{ date: "2026-05-02", channel: "paid", sessions: 540, signups: 22, revenue: 730, readRate: 0.52 },
];
function toNumber(value: string | undefined, fallback = 0) {
const parsed = Number(value);
return Number.isFinite(parsed) ? parsed : fallback;
}
function toChannel(value: string | undefined): Channel {
return channels.includes(value as Channel) ? (value as Channel) : "referral";
}
export function parseAnalyticsCsv(csv: string): TrafficRow[] {
const [headerLine, ...lines] = csv.trim().split(/\r?\n/);
if (!headerLine) return [];
const headers = headerLine.split(",").map((header) => header.trim());
return lines
.filter(Boolean)
.map((line) => {
const columns = line.split(",").map((column) => column.trim());
const get = (name: string) => columns[headers.indexOf(name)];
return {
date: get("date") ?? "",
channel: toChannel(get("channel")),
sessions: toNumber(get("sessions")),
signups: toNumber(get("signups")),
revenue: toNumber(get("revenue")),
readRate: toNumber(get("readRate")),
};
})
.filter((row) => row.date && !Number.isNaN(Date.parse(row.date)));
}
export function aggregateByChannel(rows: TrafficRow[]): ChannelSummary[] {
const map = new Map<Channel, ChannelSummary>();
for (const row of rows) {
const current =
map.get(row.channel) ??
{ channel: row.channel, sessions: 0, signups: 0, revenue: 0, conversionRate: 0, arps: 0 };
current.sessions += row.sessions;
current.signups += row.signups;
current.revenue += row.revenue;
map.set(row.channel, current);
}
return [...map.values()]
.map((row) => ({
...row,
conversionRate: row.sessions === 0 ? 0 : row.signups / row.sessions,
arps: row.sessions === 0 ? 0 : row.revenue / row.sessions,
}))
.sort((a, b) => b.revenue - a.revenue);
}
यह parser simple CSV export के लिए है। अगर CSV में quoted fields, field के अंदर comma, local decimal format या timezone issue है, तो Claude Code से dedicated CSV parser और real fixtures जोड़ने को कहें।
React dashboard component
इस component में KPI cards, chart tabs, loading/error/empty states, responsive chart और table fallback है। Table जरूरी है, क्योंकि exact numbers review किए जा सकते हैं और screen readers को भी structured data मिलता है।
// ContentAnalyticsDashboard.tsx
"use client";
import { useMemo, useState } from "react";
import {
Bar,
BarChart,
CartesianGrid,
Cell,
Legend,
Pie,
PieChart,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts";
import { aggregateByChannel, sampleRows, type Channel, type TrafficRow } from "./dashboard-data";
type DashboardProps = { rows?: TrafficRow[]; isLoading?: boolean; error?: string | null };
type ViewMode = "revenue" | "conversion" | "mix";
const colors: Record<Channel, string> = {
organic: "#2563eb",
email: "#16a34a",
social: "#f97316",
referral: "#7c3aed",
paid: "#dc2626",
};
const money = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 0 });
const percent = new Intl.NumberFormat("en-US", { style: "percent", maximumFractionDigits: 1 });
export function ContentAnalyticsDashboard({ rows = sampleRows, isLoading = false, error = null }: DashboardProps) {
const [view, setView] = useState<ViewMode>("revenue");
const summary = useMemo(() => aggregateByChannel(rows), [rows]);
const totals = useMemo(
() =>
summary.reduce(
(total, row) => ({
sessions: total.sessions + row.sessions,
signups: total.signups + row.signups,
revenue: total.revenue + row.revenue,
}),
{ sessions: 0, signups: 0, revenue: 0 },
),
[summary],
);
if (isLoading) return <p role="status">Loading dashboard data...</p>;
if (error) return <p role="alert">Dashboard data could not be loaded: {error}</p>;
if (summary.length === 0) return <p role="status">No data matches this date range. Try widening the filters.</p>;
const totalConversion = totals.sessions === 0 ? 0 : totals.signups / totals.sessions;
return (
<section aria-labelledby="content-analytics-title" style={{ display: "grid", gap: 24 }}>
<header>
<p style={{ margin: 0, color: "#64748b" }}>ClaudeCodeLab revenue dashboard</p>
<h2 id="content-analytics-title" style={{ margin: "4px 0 0" }}>Content analytics dashboard</h2>
</header>
<div style={{ display: "grid", gap: 16, gridTemplateColumns: "repeat(auto-fit, minmax(160px, 1fr))" }}>
<MetricCard label="Sessions" value={totals.sessions.toLocaleString("en-US")} />
<MetricCard label="Signups" value={totals.signups.toLocaleString("en-US")} />
<MetricCard label="Revenue" value={money.format(totals.revenue)} />
<MetricCard label="Conversion" value={percent.format(totalConversion)} />
</div>
<div role="tablist" aria-label="Chart view" style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
{[
["revenue", "Revenue by channel"],
["conversion", "Conversion rate"],
["mix", "Revenue mix"],
].map(([key, label]) => (
<button key={key} type="button" role="tab" aria-selected={view === key} onClick={() => setView(key as ViewMode)}>
{label}
</button>
))}
</div>
<figure aria-labelledby="dashboard-chart-title" style={{ margin: 0 }}>
<h3 id="dashboard-chart-title">Dashboard chart</h3>
<ResponsiveContainer width="100%" height={320}>
{view === "revenue" ? (
<BarChart data={summary}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="channel" />
<YAxis tickFormatter={(value) => money.format(Number(value))} domain={[0, "dataMax"]} />
<Tooltip formatter={(value) => money.format(Number(value))} />
<Bar dataKey="revenue" name="Revenue">
{summary.map((row) => <Cell key={row.channel} fill={colors[row.channel]} />)}
</Bar>
</BarChart>
) : view === "conversion" ? (
<BarChart data={summary}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="channel" />
<YAxis tickFormatter={(value) => percent.format(Number(value))} domain={[0, "dataMax"]} />
<Tooltip formatter={(value) => percent.format(Number(value))} />
<Bar dataKey="conversionRate" name="Conversion rate">
{summary.map((row) => <Cell key={row.channel} fill={colors[row.channel]} />)}
</Bar>
</BarChart>
) : (
<PieChart>
<Pie data={summary} dataKey="revenue" nameKey="channel" outerRadius={110} label>
{summary.map((row) => <Cell key={row.channel} fill={colors[row.channel]} />)}
</Pie>
<Tooltip formatter={(value) => money.format(Number(value))} />
<Legend />
</PieChart>
)}
</ResponsiveContainer>
<figcaption>Bars start at zero, values are available in the table, and color is never the only label.</figcaption>
</figure>
<table aria-label="Channel summary">
<thead>
<tr>{["Channel", "Sessions", "Signups", "Revenue", "CVR", "Revenue/session"].map((heading) => <th key={heading} scope="col">{heading}</th>)}</tr>
</thead>
<tbody>
{summary.map((row) => (
<tr key={row.channel}>
<th scope="row">{row.channel}</th>
<td>{row.sessions.toLocaleString("en-US")}</td>
<td>{row.signups.toLocaleString("en-US")}</td>
<td>{money.format(row.revenue)}</td>
<td>{percent.format(row.conversionRate)}</td>
<td>{money.format(row.arps)}</td>
</tr>
))}
</tbody>
</table>
</section>
);
}
function MetricCard({ label, value }: { label: string; value: string }) {
return (
<div>
<p>{label}</p>
<strong>{value}</strong>
</div>
);
}
D3 को helper की तरह इस्तेमाल करें
// d3-scales.ts
import { extent, scaleLinear, scaleTime } from "d3";
export function buildRevenueScales(rows: Array<{ date: string; revenue: number }>, width: number, height: number) {
const dates = rows.map((row) => new Date(row.date)).filter((date) => !Number.isNaN(date.valueOf()));
const revenues = rows.map((row) => row.revenue).filter((value) => Number.isFinite(value));
const dateExtent = extent(dates);
const revenueExtent = extent(revenues);
const minDate = dateExtent[0] ?? new Date("2026-01-01");
const maxDate = dateExtent[1] ?? minDate;
const maxRevenue = Math.max(revenueExtent[1] ?? 0, 1);
return {
x: scaleTime().domain([minDate, maxDate]).range([0, width]),
y: scaleLinear().domain([0, maxRevenue]).nice().range([height, 0]),
};
}
Common failure modes
पहली गलती bar chart का Y-axis zero से शुरू न करना है। इससे छोटा difference बहुत बड़ा दिख सकता है। दूसरी गलती dirty CSV है: empty cells, invalid dates और unknown channels पहले साफ होने चाहिए। तीसरी गलती color-only meaning है। Legend, label और table भी चाहिए। चौथी गलती loading, error और empty states छोड़ना है। पांचवीं गलती mobile width, खासकर 390px, पर screenshot न देखना है।
Playwright screenshot check
// tests/content-analytics-dashboard.spec.ts
import { expect, test } from "@playwright/test";
test.describe("content analytics dashboard", () => {
for (const viewport of [
{ width: 390, height: 844 },
{ width: 1280, height: 900 },
]) {
test(`renders at ${viewport.width}px`, async ({ page }) => {
await page.setViewportSize(viewport);
await page.goto("/analytics-demo");
await expect(page.getByRole("heading", { name: /Content analytics dashboard/i })).toBeVisible();
await expect(page.getByRole("figure", { name: /Dashboard chart/i })).toBeVisible();
await expect(page.getByRole("table", { name: /Channel summary/i })).toBeVisible();
await expect(page).toHaveScreenshot(`content-analytics-${viewport.width}.png`, {
fullPage: true,
animations: "disabled",
});
});
}
});
npx playwright test tests/content-analytics-dashboard.spec.ts
Screenshot math prove नहीं करता, इसलिए aggregation के लिए unit tests रखें। लेकिन blank chart, overlapping labels, horizontal overflow और missing table जल्दी पकड़ में आ जाते हैं।
Revenue path से जोड़ना
Data visualization decoration नहीं है। अगर reader article पूरा पढ़कर भी products पर नहीं जाता, CTA कमजोर है। अगर consulting article traffic ला रहा है लेकिन leads नहीं, तो training and consultation page पर offer साफ करना होगा। Beginners के लिए free checklist natural next step हो सकती है।
इस pattern को test करने पर सबसे अच्छा result तब मिला जब data contract chart से पहले तय किया गया। Prompt में empty state, error state, table fallback, mobile screenshot और misleading chart review जोड़ने से Claude Code production के करीब output देता है। फिर भी real CSV में quotes, timezone और provider-specific fields को actual sample से verify करना जरूरी है।
मुफ़्त PDF: Claude Code cheatsheet
Email डालें और commands, review habits तथा safe workflow वाली एक-page PDF पाएँ.
हम आपका data सुरक्षित रखते हैं और spam नहीं भेजते.
लेखक के बारे में
Masa
Claude Code workflow और team adoption पर काम करने वाला engineer.
संबंधित लेख
Claude Code Obsidian to CLAUDE.md workflow: context बार-बार न समझाएं
Obsidian notes को CLAUDE.md operating notes में बदलकर Claude Code sessions को resume करना आसान बनाएं.
Claude Code Revenue CTA Routing: article से PDF, Gumroad और consultation तक
Reader intent के आधार पर free PDF, Gumroad products और consultation तक CTA route करने वाला workflow.
Claude Code टीम हैंडऑफ नियम: review proof, permissions, rollback और revenue path
Claude Code टीम काम के लिए evidence, permission rules, rollback, free PDF, Gumroad और consultation path वाला handoff.