feat: add quantity support to totals, UI, and thread resolution

- totals.service: multiply weight/cost sums by quantity in category and global totals
- setup.service: multiply by quantity in getAllSetups SQL subqueries; expose quantity in getSetupWithItems item list
- thread.service: explicitly pass quantity: 1 when inserting resolved item
- ItemForm: add Quantity number input (min=1, default=1) after price field
- ItemCard: show ×N badge next to item name when quantity > 1
- CollectionView: pass quantity prop to ItemCard in both filtered and grouped views
- $setupId.tsx: pass quantity to ItemCard; multiply by quantity in client-side per-setup totals
- WeightSummaryCard: multiply by quantity in all chart and legend weight calculations
- useItems / useSetups: add quantity to ItemWithCategory / SetupItemWithCategory interfaces

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-03 18:04:27 +02:00
parent 923a0f66b0
commit 1a5e6a303e
10 changed files with 79 additions and 20 deletions

View File

@@ -55,9 +55,15 @@ function buildCategoryChartData(items: SetupItemWithCategory[]): ChartDatum[] {
const groups = new Map<string, number>();
for (const item of items) {
const current = groups.get(item.categoryName) ?? 0;
groups.set(item.categoryName, current + (item.weightGrams ?? 0));
groups.set(
item.categoryName,
current + (item.weightGrams ?? 0) * (item.quantity ?? 1),
);
}
const total = items.reduce((sum, i) => sum + (i.weightGrams ?? 0), 0);
const total = items.reduce(
(sum, i) => sum + (i.weightGrams ?? 0) * (i.quantity ?? 1),
0,
);
return Array.from(groups.entries())
.filter(([, weight]) => weight > 0)
.map(([name, weight]) => ({
@@ -76,7 +82,8 @@ function buildClassificationChartData(
consumable: 0,
};
for (const item of items) {
groups[item.classification] += item.weightGrams ?? 0;
groups[item.classification] +=
(item.weightGrams ?? 0) * (item.quantity ?? 1);
}
const total = Object.values(groups).reduce((a, b) => a + b, 0);
return Object.entries(groups)
@@ -148,17 +155,23 @@ export function WeightSummaryCard({ items }: WeightSummaryCardProps) {
const baseWeight = items.reduce(
(sum, i) =>
i.classification === "base" ? sum + (i.weightGrams ?? 0) : sum,
i.classification === "base"
? sum + (i.weightGrams ?? 0) * (i.quantity ?? 1)
: sum,
0,
);
const wornWeight = items.reduce(
(sum, i) =>
i.classification === "worn" ? sum + (i.weightGrams ?? 0) : sum,
i.classification === "worn"
? sum + (i.weightGrams ?? 0) * (i.quantity ?? 1)
: sum,
0,
);
const consumableWeight = items.reduce(
(sum, i) =>
i.classification === "consumable" ? sum + (i.weightGrams ?? 0) : sum,
i.classification === "consumable"
? sum + (i.weightGrams ?? 0) * (i.quantity ?? 1)
: sum,
0,
);
const totalWeight = baseWeight + wornWeight + consumableWeight;