An interactive demonstration of field service performance metrics. Explore metric tiles, radial gauges, and comparative bar grids.
KPI Scorecard Laboratory
Track and analyze key business metrics using interactive tiles, radial/gauge dashboards, and comparative bar grids.
Field service performance at a glance
Jobs Completed
+12%
18
Target: 24
First-Time Fix Rate
2%
87%
Target: 92%
Avg Job Duration
stable
2.3h
Target: 2h
Revenue
+8%
$12.4k
Target: $15k
Customer Satisfaction
+3%
4.6
Target: 4.8
Callback Rate
1%
8%
Target: 5%
Utilization Rate
+5%
78%
Target: 85%
At/above target
Near target
Below target
Variant1_MetricTiles.tsx (Widget Implementation)
import { useState } from "react";
import {
Paper,
Group,
Stack,
Text,
Title,
SimpleGrid,
SegmentedControl,
Badge,
} from "@mantine/core";
import { Sparkline } from "@mantine/charts";
import { HiArrowTrendingUp, HiArrowTrendingDown, HiMinus } from "react-icons/hi2";
import type { KpiMetric } from "./types";
import { sampleMetrics } from "./sampleData";
// Color constants
const GREEN = "#40c057";
const RED = "#fa5252";
const YELLOW = "#fab005";
function isAtTarget(metric: KpiMetric): boolean {
if (metric.lowerIsBetter) {
return metric.value <= metric.target;
}
return metric.value >= metric.target;
}
function isNearTarget(metric: KpiMetric): boolean {
if (metric.lowerIsBetter) {
const pct = metric.value / metric.target;
return pct <= 1.2 && pct > 1.0;
}
const pct = metric.value / metric.target;
return pct >= 0.85 && pct < 1.0;
}
function getValueColor(metric: KpiMetric): string {
if (isAtTarget(metric)) return GREEN;
if (isNearTarget(metric)) return YELLOW;
return RED;
}
function formatValue(metric: KpiMetric): string {
if (metric.unit === "$") {
return `$${metric.value >= 1000 ? (metric.value / 1000).toFixed(1) + "k" : metric.value}`;
}
if (metric.unit === "%") return `${metric.value}%`;
if (metric.unit === "score") return metric.value.toFixed(1);
if (metric.unit === "hrs") return `${metric.value}h`;
return `${metric.value}`;
}
function formatTarget(metric: KpiMetric): string {
if (metric.unit === "$") {
return `$${metric.target >= 1000 ? (metric.target / 1000).toFixed(0) + "k" : metric.target}`;
}
if (metric.unit === "%") return `${metric.target}%`;
if (metric.unit === "score") return metric.target.toFixed(1);
if (metric.unit === "hrs") return `${metric.target}h`;
return `${metric.target}`;
}
function TrendIndicator({ metric }: { metric: KpiMetric }) {
const { trend, trendValue } = metric;
if (trend === "stable") {
return (
<Group gap={4} align="center">
<HiMinus size={14} color="#868e96" />
<Text size="xs" c="dimmed">
stable
</Text>
</Group>
);
}
// For "lower is better" metrics, down is good and up is bad
const isPositive = metric.lowerIsBetter ? trend === "down" : trend === "up";
const color = isPositive ? GREEN : RED;
const absVal = Math.abs(trendValue);
const sign = trendValue > 0 ? "+" : "";
return (
<Group gap={4} align="center">
{trend === "up" ? (
<HiArrowTrendingUp size={14} color={color} />
) : (
<HiArrowTrendingDown size={14} color={color} />
)}
<Text size="xs" fw={600} style={{ color }}>
{sign}
{absVal}%
</Text>
</Group>
);
}
function MetricTile({ metric }: { metric: KpiMetric }) {
const valueColor = getValueColor(metric);
const sparklineValues = metric.sparklineData.map((d) => d.value);
// Sparkline color based on trend direction for the metric
const sparkColor = isAtTarget(metric) ? "teal" : isNearTarget(metric) ? "yellow" : "red";
return (
<Paper
withBorder
shadow="xs"
radius="md"
p="md"
style={{ position: "relative", overflow: "hidden" }}
>
<Stack gap={4}>
{/* Label + trend badge row */}
<Group justify="space-between" align="flex-start" wrap="nowrap">
<Text size="sm" c="dimmed" fw={500} style={{ lineHeight: 1.3 }}>
{metric.label}
</Text>
<TrendIndicator metric={metric} />
</Group>
{/* Large value */}
<Text size="xl" fw={800} style={{ color: valueColor, fontSize: "1.75rem", lineHeight: 1 }}>
{formatValue(metric)}
</Text>
{/* Target line */}
<Text size="xs" c="dimmed">
Target: {formatTarget(metric)}
{isAtTarget(metric) && (
<Badge
size="xs"
color="green"
variant="light"
ml={6}
style={{ verticalAlign: "middle" }}
>
On track
</Badge>
)}
</Text>
{/* Sparkline */}
<Sparkline
h={40}
data={sparklineValues}
color={sparkColor}
fillOpacity={0.15}
strokeWidth={2}
curveType="natural"
mt={4}
/>
</Stack>
</Paper>
);
}
interface Variant1Props {
metrics?: KpiMetric[];
}
export function Variant1_MetricTiles({ metrics = sampleMetrics }: Variant1Props) {
const [period, setPeriod] = useState("today");
return (
<Paper withBorder shadow="sm" radius="md" p="md">
{/* Header */}
<Group justify="space-between" mb="md" align="center">
<div>
<Title order={4} fw={700}>
KPI Scorecard
</Title>
<Text size="xs" c="dimmed">
Field service performance at a glance
</Text>
</div>
<SegmentedControl
size="xs"
value={period}
onChange={setPeriod}
data={[
{ label: "Today", value: "today" },
{ label: "This Week", value: "this-week" },
{ label: "This Month", value: "this-month" },
]}
/>
</Group>
{/* Metric grid */}
<SimpleGrid cols={{ base: 1, xs: 2, sm: 2, md: 2 }} spacing="sm">
{metrics.map((metric) => (
<MetricTile key={metric.id} metric={metric} />
))}
</SimpleGrid>
{/* Legend */}
<Group gap="lg" mt="md" justify="flex-end">
<Group gap={4}>
<span
style={{
width: 10,
height: 10,
borderRadius: "50%",
background: GREEN,
display: "inline-block",
}}
/>
<Text size="xs" c="dimmed">
At/above target
</Text>
</Group>
<Group gap={4}>
<span
style={{
width: 10,
height: 10,
borderRadius: "50%",
background: YELLOW,
display: "inline-block",
}}
/>
<Text size="xs" c="dimmed">
Near target
</Text>
</Group>
<Group gap={4}>
<span
style={{
width: 10,
height: 10,
borderRadius: "50%",
background: RED,
display: "inline-block",
}}
/>
<Text size="xs" c="dimmed">
Below target
</Text>
</Group>
</Group>
</Paper>
);
}