metric.tsx 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. "use client";
  2. import { type ComponentRenderProps } from "@json-render/react";
  3. import { useData } from "@json-render/react";
  4. import { getByPath } from "@json-render/core";
  5. export function Metric({ element }: ComponentRenderProps) {
  6. const { label, valuePath, format, trend, trendValue } = element.props as {
  7. label: string;
  8. valuePath: string;
  9. format?: string | null;
  10. trend?: string | null;
  11. trendValue?: string | null;
  12. };
  13. const { data } = useData();
  14. const rawValue = getByPath(data, valuePath);
  15. let displayValue = String(rawValue ?? "-");
  16. if (format === "currency" && typeof rawValue === "number") {
  17. displayValue = new Intl.NumberFormat("en-US", {
  18. style: "currency",
  19. currency: "USD",
  20. }).format(rawValue);
  21. } else if (format === "percent" && typeof rawValue === "number") {
  22. displayValue = new Intl.NumberFormat("en-US", {
  23. style: "percent",
  24. minimumFractionDigits: 1,
  25. }).format(rawValue);
  26. } else if (format === "number" && typeof rawValue === "number") {
  27. displayValue = new Intl.NumberFormat("en-US").format(rawValue);
  28. }
  29. return (
  30. <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
  31. <span style={{ fontSize: 14, color: "var(--muted)" }}>{label}</span>
  32. <span style={{ fontSize: 32, fontWeight: 600 }}>{displayValue}</span>
  33. {(trend || trendValue) && (
  34. <span
  35. style={{
  36. fontSize: 14,
  37. color:
  38. trend === "up"
  39. ? "#22c55e"
  40. : trend === "down"
  41. ? "#ef4444"
  42. : "var(--muted)",
  43. }}
  44. >
  45. {trend === "up" ? "+" : trend === "down" ? "-" : ""}
  46. {trendValue}
  47. </span>
  48. )}
  49. </div>
  50. );
  51. }