Shaheer Sarfaraz 3be0d25c87
Starting work on Dashboard! (#65)
* initial commit

* fix build issues and configurable time duration

* show in nav

* Positive response rate by posting freshness

* load today's jobs for charts

* fix infinite refetching with onboarding gate

* application to response rate

* refactor charts to their own directory

* bar hover color

* Duration selector embedded in navbar

* always load env

* remove warning about low conversion rate

* trend graph for applications per day

* better copy

* remove freshness response chart

* bottom line chart color and tooltip improved

* introduce check all command

* fix lint

* tests added and CI passing
2026-01-30 11:40:17 +00:00

178 lines
5.2 KiB
TypeScript

import * as React from "react";
import {
Tooltip as RechartsTooltip,
ResponsiveContainer,
type TooltipProps,
} from "recharts";
import { cn } from "@/lib/utils";
export type ChartConfig = Record<
string,
{
label?: React.ReactNode;
icon?: React.ComponentType<{ className?: string }>;
color?: string;
}
>;
const ChartConfigContext = React.createContext<ChartConfig | null>(null);
const useChartConfig = () => React.useContext(ChartConfigContext);
const ChartStyle: React.FC<{ id: string; config: ChartConfig }> = ({
id,
config,
}) => {
const entries = Object.entries(config).filter(([, value]) => value.color);
if (entries.length === 0) return null;
return (
<style>{`
[data-chart="${id}"] {
${entries
.map(([key, value]) => `--color-${key}: ${value.color};`)
.join("\n")}
}
`}</style>
);
};
export const ChartContainer = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & {
config: ChartConfig;
children?: React.ReactElement | null;
}
>(({ id, className, children, config, ...props }, ref) => {
const generatedId = React.useId();
const chartId = id ?? generatedId;
return (
<ChartConfigContext.Provider value={config}>
<div
ref={ref}
data-chart={chartId}
className={cn("flex aspect-video justify-center text-xs", className)}
{...props}
>
<ChartStyle id={chartId} config={config} />
{React.isValidElement(children) ? (
<ResponsiveContainer>{children}</ResponsiveContainer>
) : null}
</div>
</ChartConfigContext.Provider>
);
});
ChartContainer.displayName = "ChartContainer";
export const ChartTooltip = RechartsTooltip;
export type ChartTooltipContentProps = React.ComponentPropsWithoutRef<"div"> &
Pick<TooltipProps<number, string>, "active" | "payload" | "label"> & {
indicator?: "dot" | "line" | "dashed";
labelFormatter?: (value: unknown, payload: unknown[]) => React.ReactNode;
formatter?: (
value: unknown,
name: string,
item: unknown,
index: number,
) => React.ReactNode;
nameKey?: string;
};
export const ChartTooltipContent = React.forwardRef<
HTMLDivElement,
ChartTooltipContentProps
>(
(
{
active,
payload,
label,
className,
indicator = "dot",
labelFormatter,
formatter,
nameKey,
...props
},
ref,
) => {
const config = useChartConfig() ?? {};
if (!active || !payload?.length) return null;
const formattedLabel = labelFormatter
? labelFormatter(label, payload)
: label;
return (
<div
ref={ref}
className={cn(
"rounded-lg border border-border/60 bg-background px-3 py-2 text-xs shadow-sm",
className,
)}
{...props}
>
{formattedLabel ? (
<div className="mb-2 text-[11px] font-medium text-muted-foreground">
{formattedLabel}
</div>
) : null}
<div className="space-y-1">
{payload.map((item, index) => {
const dataKey = String(item.dataKey ?? item.name ?? "");
const configKey = nameKey ?? dataKey;
const entry = config[configKey] ?? config[dataKey];
const IndicatorIcon = entry?.icon;
const value = formatter
? formatter(item.value, dataKey, item, index)
: item.value;
const labelText = entry?.label ?? item.name ?? dataKey;
const indicatorColor =
entry?.color ?? item.color ?? item.fill ?? "currentColor";
return (
<div
key={`${dataKey}-${String(index)}`}
className="flex items-center justify-between gap-3"
>
<div className="flex items-center gap-2 text-muted-foreground">
{IndicatorIcon ? (
<IndicatorIcon className="h-3.5 w-3.5" />
) : (
<span
className={cn(
"inline-block",
indicator === "dot" && "h-2 w-2 rounded-full",
indicator === "line" && "h-0.5 w-3 rounded-full",
indicator === "dashed" &&
"h-0.5 w-3 rounded-full border border-dashed",
)}
style={{
backgroundColor:
indicator === "dot" || indicator === "line"
? indicatorColor
: "transparent",
borderColor:
indicator === "dashed" ? indicatorColor : undefined,
}}
/>
)}
<span>{labelText}</span>
</div>
<span className="font-semibold text-foreground">
{typeof value === "number"
? value.toLocaleString()
: (value as React.ReactNode)}
</span>
</div>
);
})}
</div>
</div>
);
},
);
ChartTooltipContent.displayName = "ChartTooltipContent";