Skip to content

Instantly share code, notes, and snippets.

@bigmabigma
Last active May 5, 2025 22:39
Show Gist options
  • Save bigmabigma/c787ab7200162312963ba2fca76dfe88 to your computer and use it in GitHub Desktop.
Save bigmabigma/c787ab7200162312963ba2fca76dfe88 to your computer and use it in GitHub Desktop.
import { PieChart, Pie, Cell, BarChart, Bar, XAxis, YAxis, Tooltip, Legend, ResponsiveContainer, LineChart, Line, AreaChart, Area } from "recharts";
import { MetricsRow } from "./MetricsRow";
// Expenses data
const expensesData = [
{ name: "Rental Cost", value: 24374, color: "#9b87f5" },
{ name: "Wages", value: 19500, color: "#F97316" },
{ name: "Medical Equipment", value: 12847, color: "#0EA5E9" },
{ name: "Supplies", value: 16538, color: "#10b981" },
{ name: "Other", value: 12589, color: "#D946EF" },
];
// Custom Multi-Section Gauge Chart Component
const GaugeChart = ({ value, total, data }) => {
// Calculate total percentage (0-100)
const percentage = Math.min(100, Math.max(0, (value / total) * 100));
// For the gauge arc - we use a semi-circle (180 degrees)
const startAngle = 180;
const endAngle = 0;
// Calculate total sum for the data
const sum = data.reduce((acc, item) => acc + item.value, 0);
// Create gauge sections based on the data proportions
const sections = [];
let currentAngle = startAngle;
data.forEach(item => {
const sectionPercentage = (item.value / sum) * 100;
const sectionAngle = (sectionPercentage / 100) * (startAngle - endAngle);
const nextAngle = currentAngle - sectionAngle;
sections.push({
startAngle: currentAngle,
endAngle: nextAngle,
value: sectionPercentage,
color: item.color,
name: item.name
});
currentAngle = nextAngle;
});
return (
<div className="w-full h-full flex flex-col items-center justify-center">
{/* Legend at the top */}
<div className="mb-2 text-center">
<span className="text-green-500 text-sm font-medium flex items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 10l7-7m0 0l7 7m-7-7v18" />
</svg>
5 pts
</span>
<div className="text-5xl font-bold mt-1">{Math.floor(percentage)}</div>
<div className="text-lg font-semibold mt-1">Excellent</div>
<div className="text-xs text-gray-400 mt-1">PERFORMANCE</div>
</div>
<ResponsiveContainer width="100%" height="75%">
<PieChart>
{/* Background arc - light gray */}
<Pie
data={[{ value: 100 }]}
cx="50%"
cy="60%"
startAngle={startAngle}
endAngle={endAngle}
innerRadius={80}
outerRadius={100}
fill="#e5e7eb"
stroke="none"
dataKey="value"
isAnimationActive={false}
/>
{/* Colored arc sections with gaps */}
{sections.map((section, index) => {
// Add gap by reducing the endAngle slightly
const gapSize = 3; // Angle in degrees for the gap
const adjustedEndAngle = section.endAngle + gapSize;
return (
<Pie
key={`section-${index}`}
data={[{ value: section.value, name: section.name }]}
cx="50%"
cy="60%"
startAngle={section.startAngle}
endAngle={adjustedEndAngle}
innerRadius={80}
outerRadius={100}
cornerRadius={5} // Rounded edges
stroke="none"
fill={section.color}
dataKey="value"
/>
);
})}
{/* The dot at the end of the last section */}
<Pie
data={[{ value: 1 }]}
cx="50%"
cy="60%"
startAngle={endAngle + 3} // Small offset
endAngle={endAngle - 3}
innerRadius={95}
outerRadius={105}
fill="#fff" // White dot
stroke={sections[sections.length-1]?.color || "#10b981"}
strokeWidth={2}
dataKey="value"
/>
</PieChart>
</ResponsiveContainer>
</div>
);
};
// Revenue data
const revenueData = [
{ month: "Jan", value: 4000, color: "bg-purple-500" },
{ month: "Feb", value: 3000, color: "bg-purple-500" },
{ month: "Mar", value: 5000, color: "bg-purple-500" },
{ month: "Apr", value: 7000, color: "bg-purple-500" },
{ month: "May", value: 6000, color: "bg-purple-500" },
{ month: "Jun", value: 8000, color: "bg-purple-500" },
];
// Patient data
const patientData = [
{ month: "Jan", newPatients: 45, returningPatients: 120, color: "bg-blue-500" },
{ month: "Feb", newPatients: 50, returningPatients: 130, color: "bg-blue-500" },
{ month: "Mar", newPatients: 35, returningPatients: 125, color: "bg-blue-500" },
{ month: "Apr", newPatients: 60, returningPatients: 140, color: "bg-blue-500" },
{ month: "May", newPatients: 75, returningPatients: 150, color: "bg-blue-500" },
{ month: "Jun", newPatients: 90, returningPatients: 160, color: "bg-blue-500" },
];
// Growth data
const growthData = [
{ month: "Jan", value: 10, color: "bg-green-500" },
{ month: "Feb", value: 15, color: "bg-green-500" },
{ month: "Mar", value: 13, color: "bg-green-500" },
{ month: "Apr", value: 20, color: "bg-green-500" },
{ month: "May", value: 25, color: "bg-green-500" },
{ month: "Jun", value: 30, color: "bg-green-500" },
];
export function Dashboard() {
// Calculate total expenses
const totalExpenses = expensesData.reduce((sum, item) => sum + item.value, 0);
// Calculate total revenue
const totalRevenue = revenueData.reduce((sum, item) => sum + item.value, 0);
// Calculate total patients
const totalNewPatients = patientData.reduce((sum, item) => sum + item.newPatients, 0);
const totalReturningPatients = patientData.reduce((sum, item) => sum + item.returningPatients, 0);
// Calculate average growth
const averageGrowth = growthData.reduce((sum, item) => sum + item.value, 0) / growthData.length;
return (
<div className="container mx-auto py-8 px-4">
<h1 className="text-3xl font-bold mb-6">Business Analytics Dashboard</h1>
{/* Expenses Section with Gauge Chart */}
<MetricsRow
title="Expense Breakdown"
description="This chart shows the distribution of expenses across different categories. Rental costs and wages make up the largest portions of our expenses."
totalValue={`$${totalExpenses.toLocaleString()}`}
totalLabel="Total Expenses"
chart={
<GaugeChart
value={78}
total={100}
data={expensesData}
/>
}
metrics={expensesData.map(item => ({
title: item.name,
value: `$${item.value.toLocaleString()}`,
color: item.color,
}))}
/>
{/* Revenue Section */}
<MetricsRow
title="Monthly Revenue"
description="Our revenue has shown a steady increase over the past six months, with a notable spike in April and June. This suggests our new services are gaining traction."
totalValue={`$${totalRevenue.toLocaleString()}`}
totalLabel="Total Revenue"
chart={
<ResponsiveContainer width="100%" height="100%">
<BarChart data={revenueData}>
<Legend layout="horizontal" verticalAlign="top" align="center" />
<XAxis dataKey="month" />
<YAxis />
<Tooltip formatter={(value) => [`$${value.toLocaleString()}`, 'Revenue']} />
<Bar dataKey="value" fill="#8b5cf6" />
</BarChart>
</ResponsiveContainer>
}
metrics={[
{ title: "Highest Month", value: `$${Math.max(...revenueData.map(d => d.value)).toLocaleString()}`, color: "bg-purple-500" },
{ title: "Average Monthly", value: `$${(totalRevenue / revenueData.length).toLocaleString()}`, color: "bg-purple-400" },
{ title: "Q2 Revenue", value: `$${revenueData.slice(3, 6).reduce((sum, item) => sum + item.value, 0).toLocaleString()}`, color: "bg-purple-600" },
{ title: "Year Projection", value: `$${(totalRevenue * 2).toLocaleString()}`, color: "bg-purple-700" },
]}
/>
{/* Patient Section */}
<MetricsRow
title="Patient Statistics"
description="Our patient base continues to grow with both new and returning patients increasing. The ratio of returning to new patients indicates strong patient loyalty and satisfaction."
totalValue={`${(totalNewPatients + totalReturningPatients).toLocaleString()}`}
totalLabel="Total Patients"
chart={
<ResponsiveContainer width="100%" height="100%">
<AreaChart data={patientData}>
<Legend layout="horizontal" verticalAlign="top" align="center" />
<XAxis dataKey="month" />
<YAxis />
<Tooltip />
<Area type="monotone" dataKey="newPatients" stackId="1" stroke="#3b82f6" fill="#93c5fd" />
<Area type="monotone" dataKey="returningPatients" stackId="1" stroke="#1d4ed8" fill="#60a5fa" />
</AreaChart>
</ResponsiveContainer>
}
metrics={[
{ title: "New Patients", value: totalNewPatients.toString(), color: "bg-blue-400" },
{ title: "Returning Patients", value: totalReturningPatients.toString(), color: "bg-blue-600" },
{ title: "Patient Retention", value: "87%", color: "bg-blue-500" },
{ title: "Avg. Visit Duration", value: "42 min", color: "bg-blue-300" },
]}
/>
{/* Growth Section */}
<MetricsRow
title="Business Growth"
description="Our month-over-month growth continues to trend upward, showing the effectiveness of our expansion strategy and marketing efforts."
totalValue={`${averageGrowth.toFixed(1)}%`}
totalLabel="Average Growth"
chart={
<ResponsiveContainer width="100%" height="100%">
<LineChart data={growthData}>
<Legend layout="horizontal" verticalAlign="top" align="center" />
<XAxis dataKey="month" />
<YAxis />
<Tooltip formatter={(value) => [`${value}%`, 'Growth']} />
<Line type="monotone" dataKey="value" stroke="#22c55e" strokeWidth={2} />
</LineChart>
</ResponsiveContainer>
}
metrics={[
{ title: "MoM Growth", value: `${growthData[growthData.length - 1].value}%`, color: "bg-green-500" },
{ title: "YoY Growth", value: "32%", color: "bg-green-600" },
{ title: "New Service Growth", value: "45%", color: "bg-green-400" },
{ title: "Market Share", value: "18%", color: "bg-green-700" },
]}
/>
</div>
);
}
import { cn } from "@/lib/utils";
interface MetricCardProps {
title: string;
value: string;
color: string;
}
export function MetricCard({ title, value, color }: MetricCardProps) {
// Determine if we're dealing with a Tailwind class or a hex color
const isTailwindClass = color.startsWith('bg-');
return (
<div className="bg-white p-4 rounded-lg shadow-sm hover:shadow-md transition-shadow">
<div className="flex items-center gap-2 mb-1">
{isTailwindClass ? (
<div className={cn("w-3 h-3 rounded-full", color)} />
) : (
<div
className="w-3 h-3 rounded-full"
style={{ backgroundColor: color }}
/>
)}
<h3 className="text-gray-500 font-medium">{title}</h3>
</div>
<p className="text-2xl font-bold">{value}</p>
</div>
);
}
import React from "react";
import { Card } from "@/components/ui/card";
import { MetricCard } from "./MetricCard";
interface Metric {
title: string;
value: string;
color: string;
}
interface MetricsRowProps {
title: string;
description: string;
totalValue: string;
totalLabel: string;
chart: React.ReactNode;
metrics: Metric[];
}
export function MetricsRow({
title,
description,
totalValue,
totalLabel,
chart,
metrics,
}: MetricsRowProps) {
return (
<Card className="p-6 mb-6">
<h2 className="text-xl font-bold mb-4">{title}</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Left section - Chart */}
<div className="w-full h-64 md:h-80 relative flex flex-col">
{chart}
</div>
{/* Right section - Content and Metrics */}
<div className="flex flex-col">
{/* Description at the top */}
<div className="mb-4">
<p className="text-gray-600">{description}</p>
</div>
{/* Metrics at the bottom */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 mt-auto">
{metrics.map((metric, index) => (
<MetricCard
key={index}
title={metric.title}
value={metric.value}
color={metric.color}
/>
))}
</div>
</div>
</div>
</Card>
);
}
{/* Expenses Section with Gauge Chart */}
<MetricsRow
title="Expense Breakdown"
description="This chart shows the distribution of expenses across different categories. Rental costs and wages make up the largest portions of our expenses."
totalValue={`$${totalExpenses.toLocaleString()}`}
totalLabel="Total Expenses"
chart={
<GaugeChart
value={78}
total={100}
data={expensesData}
/>
}
metrics={expensesData.map(item => ({
title: item.name,
value: `$${item.value.toLocaleString()}`,
color: item.color,
}))}
/>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment