TypeScript 97.5%
SQL 1.4%
Python 0.8%
1"use client";2import * as React from "react";3import { Bar, BarChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";4import { formatCompact, formatNumber } from "@/lib/format";5import { AXIS_TICK, CHART_COLORS, CHART_MARGIN, formatBucketLabel, formatTick } from "./chart-theme";6import { ChartTooltip } from "./chart-tooltip";7import { ChartLegend } from "./chart-frame";89export interface RequestsBarPoint {10 t: string;11 success: number;12 failed: number;13}1415/** Stacked success/failed bars per bucket. Legend + 1px surface gap between segments keep the two readable without color. */16export function RequestsBarChart({ data, bucket, height = 240 }: { data: RequestsBarPoint[]; bucket: "hour" | "day"; height?: number }) {17 const totalSuccess = data.reduce((a, d) => a + d.success, 0);18 const totalFailed = data.reduce((a, d) => a + d.failed, 0);19 const tickEvery = Math.max(1, Math.ceil(data.length / (bucket === "hour" ? 8 : 10)));20 return (21 <div>22 <ChartLegend23 className="mb-3"24 items={[25 { label: "Successful", color: CHART_COLORS.success, value: formatNumber(totalSuccess) },26 { label: "Failed", color: CHART_COLORS.danger, value: formatNumber(totalFailed) },27 ]}28 />29 <ResponsiveContainer width="100%" height={height}>30 <BarChart data={data} margin={CHART_MARGIN} barCategoryGap="30%">31 <CartesianGrid vertical={false} stroke={CHART_COLORS.grid} />32 <XAxis dataKey="t" tickFormatter={(v: string) => formatTick(v, bucket)} tick={AXIS_TICK} axisLine={false} tickLine={false} interval={tickEvery - 1} minTickGap={16} />33 <YAxis tick={AXIS_TICK} axisLine={false} tickLine={false} width={40} allowDecimals={false} tickFormatter={(v: number) => formatCompact(v)} />34 <Tooltip35 cursor={{ fill: CHART_COLORS.cursor, opacity: 0.6 }}36 content={37 <ChartTooltip38 labelFormatter={(l) => formatBucketLabel(String(l), bucket)}39 names={{ success: "Successful", failed: "Failed" }}40 footer={(p) => {41 const total = p.reduce((a, e) => a + Number(e.value ?? 0), 0);42 return (43 <span className="flex justify-between">44 <span>Total</span>45 <span className="font-mono tabular text-fg">{formatNumber(total)}</span>46 </span>47 );48 }}49 />50 }51 />52 <Bar dataKey="success" stackId="r" fill={CHART_COLORS.success} stroke={CHART_COLORS.surface} strokeWidth={1} maxBarSize={28} isAnimationActive={false} />53 <Bar dataKey="failed" stackId="r" fill={CHART_COLORS.danger} stroke={CHART_COLORS.surface} strokeWidth={1} radius={[3, 3, 0, 0]} maxBarSize={28} isAnimationActive={false} />54 </BarChart>55 </ResponsiveContainer>56 </div>57 );58}59