feat(07-02): add weight unit toggle and wire all formatWeight call sites

- Add segmented g/oz/lb/kg toggle to TotalsBar with settings persistence
- Pass unit parameter to all 8 formatWeight call sites across components and routes
- Import useWeightUnit hook in ItemCard, CandidateCard, CategoryHeader, SetupCard, ItemPicker, Dashboard, SetupDetail

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-16 12:23:19 +01:00
parent 1b0b4d0368
commit faa437896f
8 changed files with 66 additions and 20 deletions

View File

@@ -1,8 +1,12 @@
import { Link } from "@tanstack/react-router";
import { useUpdateSetting } from "../hooks/useSettings";
import { useTotals } from "../hooks/useTotals";
import { formatPrice, formatWeight } from "../lib/formatters";
import { useWeightUnit } from "../hooks/useWeightUnit";
import { formatPrice, formatWeight, type WeightUnit } from "../lib/formatters";
import { LucideIcon } from "../lib/iconData";
const UNITS: WeightUnit[] = ["g", "oz", "lb", "kg"];
interface TotalsBarProps {
title?: string;
stats?: Array<{ label: string; value: string }>;
@@ -15,6 +19,8 @@ export function TotalsBar({
linkTo,
}: TotalsBarProps) {
const { data } = useTotals();
const unit = useWeightUnit();
const updateSetting = useUpdateSetting();
// When no stats provided, use global totals (backward compatible)
const displayStats =
@@ -22,12 +28,15 @@ export function TotalsBar({
(data?.global
? [
{ label: "items", value: String(data.global.itemCount) },
{ label: "total", value: formatWeight(data.global.totalWeight) },
{
label: "total",
value: formatWeight(data.global.totalWeight, unit),
},
{ label: "spent", value: formatPrice(data.global.totalCost) },
]
: [
{ label: "items", value: "0" },
{ label: "total", value: formatWeight(null) },
{ label: "total", value: formatWeight(null, unit) },
{ label: "spent", value: formatPrice(null) },
]);
@@ -57,18 +66,41 @@ export function TotalsBar({
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-14">
{titleElement}
{showStats && (
<div className="flex items-center gap-6 text-sm text-gray-500">
{displayStats.map((stat) => (
<span key={stat.label}>
<span className="font-medium text-gray-700">
{stat.value}
</span>{" "}
{stat.label}
</span>
<div className="flex items-center gap-4">
<div className="flex items-center gap-1 bg-gray-100 rounded-full px-1 py-0.5">
{UNITS.map((u) => (
<button
key={u}
type="button"
onClick={() =>
updateSetting.mutate({
key: "weightUnit",
value: u,
})
}
className={`px-2 py-0.5 text-xs rounded-full transition-colors ${
unit === u
? "bg-white text-gray-700 shadow-sm font-medium"
: "text-gray-400 hover:text-gray-600"
}`}
>
{u}
</button>
))}
</div>
)}
{showStats && (
<div className="flex items-center gap-6 text-sm text-gray-500">
{displayStats.map((stat) => (
<span key={stat.label}>
<span className="font-medium text-gray-700">
{stat.value}
</span>{" "}
{stat.label}
</span>
))}
</div>
)}
</div>
</div>
</div>
</div>