diff --git a/web/src/components/ProductDetail.tsx b/web/src/components/ProductDetail.tsx
index 19a4cc8..ef37061 100644
--- a/web/src/components/ProductDetail.tsx
+++ b/web/src/components/ProductDetail.tsx
@@ -180,7 +180,31 @@ export function ProductDetail({
>
{(
[
- ["Price", fmt.money(product.price)],
+ [
+ "Price",
+ product.kind === "discrete" && product.countOriginal > 0 ? (
+ <>
+ {fmt.money(product.price / product.countOriginal)}
+
+ /unit
+
+
+ {fmt.money(product.price)} total
+
+ >
+ ) : (
+ fmt.money(product.price)
+ ),
+ ],
[
product.kind === "discrete" ? "Quantity" : "Size",
product.kind === "discrete"
@@ -189,9 +213,9 @@ export function ProductDetail({
],
["THC", `${product.thc.toFixed(1)}%`],
["CBD", `${product.cbd.toFixed(1)}%`],
- ] as [string, string][]
- ).map(([l, v]) => (
-
+ ] as [string, React.ReactNode][]
+ ).map(([l, v], i) => (
+
diff --git a/web/src/components/modals/AddProductFlow.tsx b/web/src/components/modals/AddProductFlow.tsx
index f722ebb..e8a42c1 100644
--- a/web/src/components/modals/AddProductFlow.tsx
+++ b/web/src/components/modals/AddProductFlow.tsx
@@ -48,6 +48,8 @@ export function AddProductFlow({ data, onClose }: { data: Bootstrap; onClose: ()
const cfg = TYPES.find((t) => t.id === form.type);
const isDiscrete = cfg?.kind === "discrete";
+ // form.price is total for bulk, per-unit for discrete.
+ const totalPrice = isDiscrete ? form.price * form.countOriginal : form.price;
const cpg = !isDiscrete && form.weight > 0 ? form.price / form.weight : 0;
// Find an existing strain matching the current name + brand + type.
@@ -107,6 +109,7 @@ export function AddProductFlow({ data, onClose }: { data: Bootstrap; onClose: ()
shopId,
binId,
kind: isDiscrete ? "discrete" : "bulk",
+ price: totalPrice,
});
},
onSuccess: () => {
@@ -274,7 +277,7 @@ export function AddProductFlow({ data, onClose }: { data: Bootstrap; onClose: ()
/>
)}
-
+
{fmt.money(cpg)}
)}
+ {isDiscrete && form.price > 0 && form.countOriginal > 0 && (
+
+ Total:{" "}
+ {fmt.money(totalPrice)}
+
+ ({form.countOriginal} × {fmt.money(form.price)})
+
+
+ )}
Cannabinoid profile
diff --git a/web/src/components/modals/EditProductFlow.tsx b/web/src/components/modals/EditProductFlow.tsx
index eff36c9..c33b2eb 100644
--- a/web/src/components/modals/EditProductFlow.tsx
+++ b/web/src/components/modals/EditProductFlow.tsx
@@ -22,6 +22,13 @@ export function EditProductFlow({
}) {
const qc = useQueryClient();
+ const isDiscrete = product.kind === "discrete";
+ // form.price is total for bulk, per-unit for discrete. Convert at I/O boundaries.
+ const initialPrice =
+ isDiscrete && product.countOriginal > 0
+ ? product.price / product.countOriginal
+ : product.price;
+
const [form, setForm] = useState({
name: product.name,
brandId: product.brandId ?? NEW_BRAND,
@@ -30,7 +37,7 @@ export function EditProductFlow({
weight: product.weight,
countOriginal: product.countOriginal,
unitWeight: product.unitWeight,
- price: product.price,
+ price: initialPrice,
thc: product.thc,
cbd: product.cbd,
totalCannabinoids: product.totalCannabinoids,
@@ -49,7 +56,7 @@ export function EditProductFlow({
setForm((f) => ({ ...f, [k]: v }));
const cfg = TYPES.find((t) => t.id === product.type);
- const isDiscrete = product.kind === "discrete";
+ const totalPrice = isDiscrete ? form.price * form.countOriginal : form.price;
const cpg = !isDiscrete && form.weight > 0 ? form.price / form.weight : 0;
const save = useMutation({
@@ -86,7 +93,7 @@ export function EditProductFlow({
weight: isDiscrete ? undefined : form.weight,
countOriginal: isDiscrete ? form.countOriginal : undefined,
unitWeight: isDiscrete ? form.unitWeight : undefined,
- price: form.price,
+ price: totalPrice,
thc: form.thc,
cbd: form.cbd,
totalCannabinoids: form.totalCannabinoids,
@@ -262,7 +269,7 @@ export function EditProductFlow({
/>
)}
-
+
{fmt.money(cpg)}
)}
+ {isDiscrete && form.price > 0 && form.countOriginal > 0 && (
+
+ Total:{" "}
+ {fmt.money(totalPrice)}
+
+ ({form.countOriginal} × {fmt.money(form.price)})
+
+
+ )}
Cannabinoid profile