@flowselections/actie-mail-kwekers-erp-mathot 1.0.8 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"AanbiedingsmailtjePage.d.ts","sourceRoot":"","sources":["../../src/components/AanbiedingsmailtjePage.tsx"],"names":[],"mappings":"AAuCA,wBAAgB,sBAAsB,gCA4jBrC"}
1
+ {"version":3,"file":"AanbiedingsmailtjePage.d.ts","sourceRoot":"","sources":["../../src/components/AanbiedingsmailtjePage.tsx"],"names":[],"mappings":"AA0CA,wBAAgB,sBAAsB,gCAylBrC"}
@@ -5,6 +5,7 @@ import { useServerFn } from "@tanstack/react-start";
5
5
  import { useNavigate } from "@tanstack/react-router";
6
6
  import { Mail, Send, Search, X, Package, Users, Percent, Tag, List, BarChart3 } from "lucide-react";
7
7
  import { createCampaign, sendCampaign } from "../lib/campaigns.functions";
8
+ import { createVoorraadStockMap, voorraadAvailableStock } from "../lib/voorraad-service";
8
9
  export function AanbiedingsmailtjePage() {
9
10
  const { user } = useAuth();
10
11
  const navigate = useNavigate();
@@ -69,13 +70,25 @@ export function AanbiedingsmailtjePage() {
69
70
  .order("updated_at", { ascending: false })
70
71
  .limit(1000)
71
72
  : Promise.resolve({ data: [] });
72
- const [{ data: cacheRows }, { data: draftRows }, { data: cs }, { data: extCs }, { data: settings }] = await Promise.all([
73
+ const stockPromise = activeId
74
+ ? sb.from("trade_item_stock")
75
+ .select("trade_item_key, physical_stock, reserved_stock, updated_at")
76
+ .eq("connection_id", activeId)
77
+ .limit(5000)
78
+ : Promise.resolve({ data: [] });
79
+ const [{ data: cacheRows }, { data: draftRows }, { data: stockRows }, { data: cs }, { data: extCs }, { data: settings }] = await Promise.all([
73
80
  cachePromise,
74
81
  draftPromise,
82
+ stockPromise,
75
83
  sb.from("customers").select("id, company_name, email, customer_class, country").not("email", "is", null).order("company_name").limit(1000),
76
84
  sb.from("external_customers_grouped").select("group_key, display_name, email, country, is_active").not("email", "is", null).eq("is_active", true).limit(2000),
77
85
  sb.from("offer_settings").select("default_subject, default_intro, default_sticker_cost, show_customer_classes").maybeSingle(),
78
86
  ]);
87
+ const stockMap = createVoorraadStockMap((Array.isArray(stockRows) ? stockRows : []));
88
+ const stockFor = (key) => {
89
+ const s = stockMap.get(key);
90
+ return voorraadAvailableStock(s);
91
+ };
79
92
  const pickName = (n) => {
80
93
  if (!n)
81
94
  return "";
@@ -94,28 +107,48 @@ export function AanbiedingsmailtjePage() {
94
107
  return d.imageUrls[0];
95
108
  return null;
96
109
  };
110
+ const pickPacking = (d) => {
111
+ const cfgs = Array.isArray(d?.packingConfigurations) ? d.packingConfigurations : [];
112
+ const primary = cfgs.find((c) => c?.primary) ?? cfgs[0];
113
+ if (!primary)
114
+ return { plants_per_crate: null, belading: null };
115
+ const ppc = Number(primary.piecesPerPackage);
116
+ const perLayer = Number(primary.packagesPerLayer);
117
+ const layers = Number(primary.layersPerLoadCarrier);
118
+ const parts = [ppc, perLayer, layers].filter((n) => Number.isFinite(n) && n > 0);
119
+ return {
120
+ plants_per_crate: Number.isFinite(ppc) && ppc > 0 ? ppc : null,
121
+ belading: parts.length === 3 ? `${ppc}x${perLayer}x${layers}` : null,
122
+ };
123
+ };
97
124
  const published = (cacheRows ?? []).map((r) => {
98
125
  const d = r.data ?? {};
126
+ const packing = pickPacking(d);
99
127
  return {
100
128
  id: r.floriday_id,
101
129
  product: pickName(d.tradeItemName) || pickName(d.name) || pickName(d.vbnName) || (d.botanicalNames?.[0] ?? "(naamloos)"),
102
130
  sale_price: null,
103
- quantity: Number(d.physicalStock ?? d.stock?.physical ?? 0),
131
+ quantity: stockFor(r.floriday_id),
104
132
  image_url: pickImage(d),
105
133
  barcode: d.supplierArticleCode ?? d.articleCode ?? d.gtin ?? d.articleGtin ?? d.tradeItemReference ?? "",
134
+ plants_per_crate: packing.plants_per_crate,
135
+ belading: packing.belading,
106
136
  is_draft: false,
107
137
  };
108
138
  });
109
139
  const drafts = (draftRows ?? []).map((r) => {
110
140
  const d = r.data ?? {};
111
141
  const firstImage = Array.isArray(d.images) && d.images[0] ? d.images[0] : null;
142
+ const packing = pickPacking(d);
112
143
  return {
113
144
  id: `local:${r.id}`,
114
145
  product: d.name || d.vbnName || "(naamloos)",
115
146
  sale_price: null,
116
- quantity: 0,
147
+ quantity: stockFor(`draft:${r.id}`),
117
148
  image_url: firstImage?.url ?? firstImage?.previewUrl ?? null,
118
149
  barcode: d.articleCode ?? d.gtin ?? "",
150
+ plants_per_crate: packing.plants_per_crate,
151
+ belading: packing.belading,
119
152
  is_draft: true,
120
153
  };
121
154
  });
@@ -168,8 +201,8 @@ export function AanbiedingsmailtjePage() {
168
201
  normal_price: 0,
169
202
  discount_price: 0,
170
203
  available_stock: p.quantity ?? 0,
171
- plants_per_crate: 10,
172
- belading: "",
204
+ plants_per_crate: p.plants_per_crate ?? 10,
205
+ belading: p.belading ?? "",
173
206
  barcode: p.barcode ?? "",
174
207
  crate_price: 0,
175
208
  sticker_cost: null,
@@ -306,7 +339,7 @@ export function AanbiedingsmailtjePage() {
306
339
  return (_jsxs("div", { className: "border rounded-md p-3 space-y-3 bg-muted/30", children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsxs("div", { children: [_jsx("div", { className: "font-medium text-sm", children: p.product_name }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Originele prijs: \u20AC ", p.normal_price.toFixed(2)] })] }), _jsx(Button, { variant: "ghost", size: "icon", onClick: () => removeProduct(p.product_id), children: _jsx(X, { className: "h-4 w-4" }) })] }), _jsxs("div", { className: "grid grid-cols-2 gap-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Label, { className: "text-xs w-24 shrink-0", children: "Planten/fust:" }), _jsx(Input, { type: "number", value: p.plants_per_crate, onChange: (e) => updateProd(p.product_id, { plants_per_crate: Number(e.target.value) }) })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Label, { className: "text-xs w-20 shrink-0", children: "Belading:" }), _jsx(Input, { placeholder: "AxBxC", value: p.belading, onChange: (e) => updateProd(p.product_id, { belading: e.target.value }) })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Label, { className: "text-xs w-24 shrink-0", children: "Barcode:" }), _jsx(Input, { value: p.barcode, onChange: (e) => updateProd(p.product_id, { barcode: e.target.value }) })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Label, { className: "text-xs w-20 shrink-0", children: "Fustprijs: \u20AC" }), _jsx(Input, { type: "number", step: "0.01", value: p.crate_price, onChange: (e) => updateProd(p.product_id, { crate_price: Number(e.target.value) }) })] })] }), _jsxs("div", { className: "flex items-center gap-2 border-t pt-2", children: [_jsx(Label, { className: "text-xs", children: "Stickeropdruk beschikbaar:" }), _jsx(Button, { type: "button", size: "sm", variant: p.sticker_available ? "default" : "outline", onClick: () => updateProd(p.product_id, { sticker_available: true }), children: "Ja" }), _jsx(Button, { type: "button", size: "sm", variant: !p.sticker_available ? "default" : "outline", onClick: () => updateProd(p.product_id, { sticker_available: false }), children: "Nee" })] }), _jsxs("div", { className: "space-y-2 border-t pt-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { className: "text-xs font-medium", children: "Prijs aftuin ex. Fust vanaf .." }), _jsx(Button, { variant: "outline", size: "sm", onClick: addTier, children: "+ Staffel" })] }), p.tiers.map((t, idx) => {
307
340
  const pct = p.normal_price > 0 ? Math.round((1 - t.price_per_crate / p.normal_price) * 100) : 0;
308
341
  return (_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx("span", { className: "text-xs text-muted-foreground", children: "Vanaf" }), _jsx(Input, { type: "number", className: "w-20", value: t.min_crates, onChange: (e) => updateTier(idx, { min_crates: Number(e.target.value) }) }), _jsx("span", { className: "text-xs text-muted-foreground", children: "fust(en): \u20AC" }), _jsx(Input, { type: "number", step: "0.01", className: "w-24", value: t.price_per_crate, onChange: (e) => updateTier(idx, { price_per_crate: Number(e.target.value) }) }), _jsxs("span", { className: `text-xs font-medium ${pct > 0 ? "text-destructive" : "text-muted-foreground"}`, children: ["(", pct, "%)"] }), p.tiers.length > 1 && (_jsx(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => removeTier(idx), children: _jsx(X, { className: "h-3 w-3" }) }))] }, idx));
309
- })] }), _jsxs("div", { className: "grid grid-cols-2 gap-2 border-t pt-2", children: [_jsxs("div", { children: [_jsx(Label, { className: "text-xs", children: "Actieprijs \u20AC" }), _jsx(Input, { type: "number", step: "0.01", value: p.discount_price, onChange: (e) => updateProd(p.product_id, { discount_price: Number(e.target.value) }) })] }), _jsxs("div", { children: [_jsx(Label, { className: "text-xs", children: "Voorraad" }), _jsx(Input, { type: "number", value: p.available_stock, onChange: (e) => updateProd(p.product_id, { available_stock: Number(e.target.value) }) })] })] }), _jsx(Textarea, { placeholder: "Omschrijving (optioneel)", rows: 2, value: p.description, onChange: (e) => updateProd(p.product_id, { description: e.target.value }) })] }, p.product_id));
342
+ })] }), _jsx(Textarea, { placeholder: "Omschrijving (optioneel)", rows: 2, value: p.description, onChange: (e) => updateProd(p.product_id, { description: e.target.value }) })] }, p.product_id));
310
343
  }) })] }))] })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Users, { className: "h-5 w-5" }), _jsx(CardTitle, { children: "Klanten Selecteren" })] }), _jsx(CardDescription, { children: "Kies klanten om de actie naar te versturen" })] }), _jsxs(CardContent, { className: "space-y-3", children: [_jsxs("div", { className: "relative", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { className: "pl-9", placeholder: "Zoek klant...", value: customerSearch, onChange: (e) => setCustomerSearch(e.target.value) })] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("label", { className: "flex items-center gap-2 text-sm cursor-pointer", children: [_jsx("input", { type: "checkbox", checked: allVisibleSelected, onChange: toggleAllVisible }), "Selecteer alle klanten met email"] }), _jsxs(Badge, { variant: "secondary", children: [selCustomers.size, " geselecteerd"] })] }), showCustomerClasses && (_jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "text-xs font-medium text-muted-foreground", children: "Selecteer op klantgroep" }), _jsx("div", { className: "flex flex-wrap gap-2", children: classGroups.map((g) => {
311
344
  const allSel = g.customers.length > 0 && g.customers.every((c) => selCustomers.has(c.id));
312
345
  return (_jsxs(Button, { type: "button", variant: allSel ? "default" : "outline", size: "sm", onClick: () => toggleClass(g.letter), className: "gap-2", children: ["Klasse ", g.letter, _jsx(Badge, { variant: "secondary", className: "px-1.5 py-0 text-xs", children: g.customers.length })] }, g.letter));
@@ -1 +1 @@
1
- {"version":3,"file":"ActiePage.d.ts","sourceRoot":"","sources":["../../src/components/ActiePage.tsx"],"names":[],"mappings":"AAqCA,wBAAgB,SAAS,gCAqbxB"}
1
+ {"version":3,"file":"ActiePage.d.ts","sourceRoot":"","sources":["../../src/components/ActiePage.tsx"],"names":[],"mappings":"AAqCA,wBAAgB,SAAS,gCAoexB"}
@@ -83,19 +83,46 @@ export function ActiePage() {
83
83
  }
84
84
  const safeProducts = Array.isArray(data?.products) ? data.products : [];
85
85
  const lineFor = (p) => {
86
- const stockPlants = Math.max(0, Number(p.live_stock ?? p.available_stock ?? 0));
86
+ const stockPlants = Math.max(0, Number(p.live_stock ?? 0));
87
87
  const ppc = Math.max(1, Math.floor(Number(p.plants_per_crate ?? 1) || 1));
88
88
  const maxCrates = Math.floor(stockPlants / ppc);
89
89
  const soldOut = maxCrates <= 0;
90
90
  const crates = Math.max(0, Math.floor(qty[p.id] ?? 0));
91
91
  const plants = crates * ppc;
92
- const unit = Number(p.discount_price ?? 0);
92
+ // Prijsprioriteit: (1) tier-prijs uit actie-e-mail, (2) discount_price uit e-mail,
93
+ // (3) standaardprijs uit artikelbeheer. De e-mailprijs is ALTIJD leidend.
94
+ const tiers = Array.isArray(p.tiers)
95
+ ? [...p.tiers].map((t) => ({ min_crates: Number(t.min_crates), price_per_crate: Number(t.price_per_crate) }))
96
+ .sort((a, b) => a.min_crates - b.min_crates)
97
+ : [];
98
+ let unit = 0;
99
+ let cratePrice = 0;
100
+ let priceSource = "normal";
101
+ if (tiers.length > 0) {
102
+ const applicable = [...tiers].reverse().find((t) => crates >= t.min_crates) ?? tiers[0];
103
+ cratePrice = applicable.price_per_crate;
104
+ unit = cratePrice / ppc;
105
+ priceSource = "tier";
106
+ }
107
+ else if (Number(p.discount_price) > 0) {
108
+ unit = Number(p.discount_price);
109
+ cratePrice = unit * ppc;
110
+ priceSource = "discount";
111
+ }
112
+ else {
113
+ unit = Number(p.normal_price ?? 0);
114
+ cratePrice = unit * ppc;
115
+ priceSource = "normal";
116
+ }
117
+ const priceSourceLabel = priceSource === "tier" ? "Prijs uit actie-e-mail"
118
+ : priceSource === "discount" ? "Actieprijs uit e-mail"
119
+ : "Standaardprijs artikelbeheer";
93
120
  const stickerCost = Number(p.sticker_cost ?? 0);
94
121
  const stickerAvail = p.sticker_available !== false;
95
122
  const stickerOn = stickerAvail && !!sticker[p.id];
96
123
  const productSubtotal = plants * unit;
97
124
  const stickerSubtotal = stickerOn ? plants * stickerCost : 0;
98
- return { q: crates, plants, ppc, unit, stickerCost, stickerAvail, stickerOn, productSubtotal, stickerSubtotal, total: productSubtotal + stickerSubtotal, stock: stockPlants, maxCrates, soldOut };
125
+ return { q: crates, plants, ppc, unit, cratePrice, priceSource, priceSourceLabel, tiers, stickerCost, stickerAvail, stickerOn, productSubtotal, stickerSubtotal, total: productSubtotal + stickerSubtotal, stock: stockPlants, maxCrates, soldOut };
99
126
  };
100
127
  const totalAmount = safeProducts.reduce((s, p) => s + lineFor(p).total, 0);
101
128
  const hasItems = safeProducts.some((p) => (qty[p.id] ?? 0) > 0);
@@ -108,7 +135,7 @@ export function ActiePage() {
108
135
  return {
109
136
  product_id: p.id,
110
137
  product_name: p.product_name,
111
- quantity: l.q,
138
+ quantity: l.plants,
112
139
  unit_price: l.unit,
113
140
  sticker: l.stickerOn,
114
141
  sticker_unit_price: l.stickerOn ? l.stickerCost : 0,
@@ -157,12 +184,14 @@ export function ActiePage() {
157
184
  safeProducts.forEach((p) => { next[p.id] = false; });
158
185
  setSticker(next);
159
186
  }, className: "text-xs font-semibold bg-white text-blue-700 border border-blue-300 rounded px-3 py-1.5 hover:bg-blue-50", children: "Alles uit" })] })] })), _jsx("div", { className: "space-y-4", children: safeProducts.map((p) => {
160
- const normal = Number(p.normal_price ?? 0);
161
- const price = Number(p.discount_price ?? 0);
162
- const showStrike = normal > price;
163
187
  const l = lineFor(p);
188
+ const normal = Number(p.normal_price ?? 0);
189
+ const price = l.cratePrice > 0 ? l.cratePrice : Number(p.discount_price ?? 0);
190
+ const showStrike = normal > 0 && normal > price && price > 0;
164
191
  const currentQty = l.q;
165
- return (_jsxs("div", { className: `bg-white rounded-xl border shadow-sm overflow-hidden ${l.soldOut ? "border-slate-200 opacity-75" : "border-slate-200"}`, children: [_jsxs("div", { className: "p-4 flex gap-4", children: [_jsxs("div", { className: "shrink-0 relative", children: [p.image_url ? (_jsx("img", { src: p.image_url, alt: p.product_name, className: `w-24 h-24 rounded-lg object-cover border border-slate-200 bg-slate-50 ${l.soldOut ? "grayscale" : ""}` })) : (_jsx("div", { className: `w-24 h-24 rounded-lg border border-slate-200 bg-green-50 grid place-items-center overflow-hidden ${l.soldOut ? "grayscale" : ""}`, children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 96 96", className: "w-full h-full", children: [_jsx("path", { d: "M48 60 Q46 75 44 86", stroke: "#16a34a", strokeWidth: "2.5", fill: "none", strokeLinecap: "round" }), _jsx("path", { d: "M48 72 Q40 70 36 64 Q42 66 48 72", fill: "#22c55e" }), _jsx("path", { d: "M48 78 Q56 76 60 70 Q54 72 48 78", fill: "#22c55e" }), _jsxs("g", { transform: "translate(48 40)", children: [_jsx("ellipse", { cx: "0", cy: "-14", rx: "9", ry: "14", fill: "#f9a8d4" }), _jsx("ellipse", { cx: "13.3", cy: "-4.3", rx: "9", ry: "14", fill: "#f472b6", transform: "rotate(72)" }), _jsx("ellipse", { cx: "8.2", cy: "11.3", rx: "9", ry: "14", fill: "#ec4899", transform: "rotate(144)" }), _jsx("ellipse", { cx: "-8.2", cy: "11.3", rx: "9", ry: "14", fill: "#f472b6", transform: "rotate(216)" }), _jsx("ellipse", { cx: "-13.3", cy: "-4.3", rx: "9", ry: "14", fill: "#f9a8d4", transform: "rotate(288)" }), _jsx("circle", { cx: "0", cy: "0", r: "6", fill: "#fbbf24" }), _jsx("circle", { cx: "0", cy: "0", r: "3", fill: "#f59e0b" })] })] }) })), l.soldOut && (_jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: _jsx("span", { className: "bg-red-600 text-white text-xs font-bold px-2 py-1 rounded rotate-[-8deg] shadow", children: "UITVERKOCHT" }) }))] }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "font-bold text-slate-900 truncate", children: p.product_name }), p.plants_per_crate ? (_jsxs("div", { className: "text-sm text-slate-500", children: [p.plants_per_crate, " planten/fust"] })) : null, _jsxs("div", { className: "mt-2 flex items-baseline gap-2 flex-wrap", children: [showStrike && (_jsx("span", { className: "text-slate-400 line-through text-sm", children: fmtEuro(normal) })), _jsx("span", { className: "text-green-700 font-bold text-lg", children: fmtEuro(price) })] }), _jsx("div", { className: "mt-2", children: l.soldOut ? (_jsx("span", { className: "inline-flex items-center gap-1 text-xs font-semibold bg-red-100 text-red-800 border border-red-200 rounded px-2 py-1", children: "\u25CF Uitverkocht" })) : l.stock <= 10 ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs font-semibold bg-amber-100 text-amber-800 border border-amber-200 rounded px-2 py-1", children: ["\u25CF Nog ", l.stock, " op voorraad"] })) : (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs font-semibold bg-green-100 text-green-800 border border-green-200 rounded px-2 py-1", children: ["\u25CF Op voorraad: ", l.stock, " stuks"] })) }), !l.soldOut && l.stickerAvail && (_jsxs("label", { className: "mt-2 flex items-start gap-2 text-xs text-blue-900 bg-blue-50 border-l-4 border-blue-400 rounded px-2 py-2 cursor-pointer select-none", children: [_jsx("input", { type: "checkbox", className: "mt-0.5", checked: l.stickerOn, onChange: (e) => setSticker({ ...sticker, [p.id]: e.target.checked }) }), _jsxs("span", { children: ["Stickeropdruk toevoegen", l.stickerCost > 0 ? _jsxs(_Fragment, { children: [" \u2014 ", _jsx("strong", { children: fmtEuro(l.stickerCost) }), " per stuk"] }) : _jsxs(_Fragment, { children: [" \u2014 ", _jsx("strong", { children: "geen meerprijs" })] })] })] })), !l.soldOut && !l.stickerAvail && (_jsx("div", { className: "mt-2 text-xs text-slate-500 bg-slate-50 border-l-4 border-slate-300 rounded px-2 py-2", children: "Stickeropdruk niet beschikbaar voor dit product" })), !l.soldOut && currentQty > 0 && (_jsxs("div", { className: "mt-3 text-xs text-slate-600 space-y-0.5 border-t border-slate-100 pt-2", children: [_jsxs("div", { className: "flex justify-between", children: [_jsxs("span", { children: ["Productprijs (", l.plants, " \u00D7 ", fmtEuro(l.unit), ")"] }), _jsx("span", { children: fmtEuro(l.productSubtotal) })] }), l.stickerOn && (_jsxs("div", { className: "flex justify-between", children: [_jsxs("span", { children: ["Stickeropdruk (", l.plants, " \u00D7 ", fmtEuro(l.stickerCost), ")"] }), _jsx("span", { children: fmtEuro(l.stickerSubtotal) })] })), _jsxs("div", { className: "flex justify-between font-bold text-slate-900 pt-0.5", children: [_jsx("span", { children: "Totaalprijs" }), _jsx("span", { className: "text-green-700", children: fmtEuro(l.total) })] })] }))] })] }), l.soldOut ? (_jsx("div", { className: "border-t border-slate-100 px-4 py-3 text-center text-sm text-red-700 bg-red-50 font-medium", children: "Dit product is niet meer beschikbaar" })) : (_jsxs("div", { className: "border-t border-slate-100 px-4 py-3 flex flex-wrap items-center justify-between gap-2", children: [_jsxs("span", { className: "text-sm text-slate-600", children: ["Aantal fusten:", currentQty > 0 && (_jsxs("span", { className: "ml-1 text-slate-500", children: ["= ", l.plants.toLocaleString("nl-NL"), " planten"] }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { type: "button", onClick: () => setQ(p.id, currentQty - 1, l.maxCrates), className: "h-9 w-9 rounded-md border border-slate-200 grid place-items-center text-slate-600 hover:bg-slate-50 disabled:opacity-40", disabled: currentQty <= 0, "aria-label": "Minder", children: _jsx(Minus, { className: "h-4 w-4" }) }), _jsx(Input, { type: "number", inputMode: "numeric", step: 1, min: 0, max: l.maxCrates, value: currentQty, onChange: (e) => setQ(p.id, parseInt(e.target.value, 10), l.maxCrates), onKeyDown: (e) => {
192
+ return (_jsxs("div", { className: `bg-white rounded-xl border shadow-sm overflow-hidden ${l.soldOut ? "border-slate-200 opacity-75" : "border-slate-200"}`, children: [_jsxs("div", { className: "p-4 flex gap-4", children: [_jsxs("div", { className: "shrink-0 relative", children: [p.image_url ? (_jsx("img", { src: p.image_url, alt: p.product_name, className: `w-24 h-24 rounded-lg object-cover border border-slate-200 bg-slate-50 ${l.soldOut ? "grayscale" : ""}` })) : (_jsx("div", { className: `w-24 h-24 rounded-lg border border-slate-200 bg-green-50 grid place-items-center overflow-hidden ${l.soldOut ? "grayscale" : ""}`, children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 96 96", className: "w-full h-full", children: [_jsx("path", { d: "M48 60 Q46 75 44 86", stroke: "#16a34a", strokeWidth: "2.5", fill: "none", strokeLinecap: "round" }), _jsx("path", { d: "M48 72 Q40 70 36 64 Q42 66 48 72", fill: "#22c55e" }), _jsx("path", { d: "M48 78 Q56 76 60 70 Q54 72 48 78", fill: "#22c55e" }), _jsxs("g", { transform: "translate(48 40)", children: [_jsx("ellipse", { cx: "0", cy: "-14", rx: "9", ry: "14", fill: "#f9a8d4" }), _jsx("ellipse", { cx: "13.3", cy: "-4.3", rx: "9", ry: "14", fill: "#f472b6", transform: "rotate(72)" }), _jsx("ellipse", { cx: "8.2", cy: "11.3", rx: "9", ry: "14", fill: "#ec4899", transform: "rotate(144)" }), _jsx("ellipse", { cx: "-8.2", cy: "11.3", rx: "9", ry: "14", fill: "#f472b6", transform: "rotate(216)" }), _jsx("ellipse", { cx: "-13.3", cy: "-4.3", rx: "9", ry: "14", fill: "#f9a8d4", transform: "rotate(288)" }), _jsx("circle", { cx: "0", cy: "0", r: "6", fill: "#fbbf24" }), _jsx("circle", { cx: "0", cy: "0", r: "3", fill: "#f59e0b" })] })] }) })), l.soldOut && (_jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: _jsx("span", { className: "bg-red-600 text-white text-xs font-bold px-2 py-1 rounded rotate-[-8deg] shadow", children: "UITVERKOCHT" }) }))] }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "font-bold text-slate-900 truncate", children: p.product_name }), p.plants_per_crate ? (_jsxs("div", { className: "text-sm text-slate-500", children: [p.plants_per_crate, " planten/fust"] })) : null, _jsxs("div", { className: "mt-2 flex items-baseline gap-2 flex-wrap", children: [showStrike && (_jsx("span", { className: "text-slate-400 line-through text-sm", children: fmtEuro(normal) })), _jsx("span", { className: "text-green-700 font-bold text-lg", children: fmtEuro(price) }), _jsx("span", { className: "text-xs text-slate-500", children: "per fust" })] }), l.tiers.length > 0 && (_jsx("div", { className: "mt-1 flex flex-wrap gap-1", children: l.tiers.map((t) => (_jsxs("span", { className: `text-[11px] px-2 py-0.5 rounded border ${l.cratePrice === t.price_per_crate && l.priceSource === "tier"
193
+ ? "bg-green-100 border-green-300 text-green-800 font-semibold"
194
+ : "bg-slate-50 border-slate-200 text-slate-600"}`, children: ["\u2265 ", t.min_crates, " fust: ", fmtEuro(t.price_per_crate)] }, t.min_crates))) })), _jsx("div", { className: "mt-2", children: l.soldOut ? (_jsx("span", { className: "inline-flex items-center gap-1 text-xs font-semibold bg-red-100 text-red-800 border border-red-200 rounded px-2 py-1", children: "\u25CF Uitverkocht" })) : l.stock <= 10 ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs font-semibold bg-amber-100 text-amber-800 border border-amber-200 rounded px-2 py-1", children: ["\u25CF Nog ", l.stock, " op voorraad"] })) : (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs font-semibold bg-green-100 text-green-800 border border-green-200 rounded px-2 py-1", children: ["\u25CF Op voorraad: ", l.stock, " stuks"] })) }), !l.soldOut && l.stickerAvail && (_jsxs("label", { className: "mt-2 flex items-start gap-2 text-xs text-blue-900 bg-blue-50 border-l-4 border-blue-400 rounded px-2 py-2 cursor-pointer select-none", children: [_jsx("input", { type: "checkbox", className: "mt-0.5", checked: l.stickerOn, onChange: (e) => setSticker({ ...sticker, [p.id]: e.target.checked }) }), _jsxs("span", { children: ["Stickeropdruk toevoegen", l.stickerCost > 0 ? _jsxs(_Fragment, { children: [" \u2014 ", _jsx("strong", { children: fmtEuro(l.stickerCost) }), " per stuk"] }) : _jsxs(_Fragment, { children: [" \u2014 ", _jsx("strong", { children: "geen meerprijs" })] })] })] })), !l.soldOut && !l.stickerAvail && (_jsx("div", { className: "mt-2 text-xs text-slate-500 bg-slate-50 border-l-4 border-slate-300 rounded px-2 py-2", children: "Stickeropdruk niet beschikbaar voor dit product" })), !l.soldOut && currentQty > 0 && (_jsxs("div", { className: "mt-3 text-xs text-slate-600 space-y-0.5 border-t border-slate-100 pt-2", children: [_jsxs("div", { className: "flex justify-between", children: [_jsxs("span", { children: ["Productprijs (", l.plants, " \u00D7 ", fmtEuro(l.unit), ")"] }), _jsx("span", { children: fmtEuro(l.productSubtotal) })] }), l.stickerOn && (_jsxs("div", { className: "flex justify-between", children: [_jsxs("span", { children: ["Stickeropdruk (", l.plants, " \u00D7 ", fmtEuro(l.stickerCost), ")"] }), _jsx("span", { children: fmtEuro(l.stickerSubtotal) })] })), _jsxs("div", { className: "flex justify-between font-bold text-slate-900 pt-0.5", children: [_jsx("span", { children: "Totaalprijs" }), _jsx("span", { className: "text-green-700", children: fmtEuro(l.total) })] }), _jsxs("div", { className: "pt-1 text-[11px] text-slate-500 italic", children: ["Herkomst prijs: ", l.priceSourceLabel] })] }))] })] }), l.soldOut ? (_jsx("div", { className: "border-t border-slate-100 px-4 py-3 text-center text-sm text-red-700 bg-red-50 font-medium", children: "Dit product is niet meer beschikbaar" })) : (_jsxs("div", { className: "border-t border-slate-100 px-4 py-3 flex flex-wrap items-center justify-between gap-2", children: [_jsxs("span", { className: "text-sm text-slate-600", children: ["Aantal fusten:", currentQty > 0 && (_jsxs("span", { className: "ml-1 text-slate-500", children: ["= ", l.plants.toLocaleString("nl-NL"), " planten"] }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { type: "button", onClick: () => setQ(p.id, currentQty - 1, l.maxCrates), className: "h-9 w-9 rounded-md border border-slate-200 grid place-items-center text-slate-600 hover:bg-slate-50 disabled:opacity-40", disabled: currentQty <= 0, "aria-label": "Minder", children: _jsx(Minus, { className: "h-4 w-4" }) }), _jsx(Input, { type: "number", inputMode: "numeric", step: 1, min: 0, max: l.maxCrates, value: currentQty, onChange: (e) => setQ(p.id, parseInt(e.target.value, 10), l.maxCrates), onKeyDown: (e) => {
166
195
  if (["e", "E", "+", "-", ".", ","].includes(e.key))
167
196
  e.preventDefault();
168
197
  }, className: "w-28 text-center" }), _jsx("button", { type: "button", onClick: () => setQ(p.id, currentQty + 1, l.maxCrates), className: "h-9 w-9 rounded-md border border-slate-200 grid place-items-center text-slate-600 hover:bg-slate-50 disabled:opacity-40", disabled: currentQty >= l.maxCrates, "aria-label": "Meer", children: _jsx(Plus, { className: "h-4 w-4" }) })] })] }))] }, p.id));
@@ -5,5 +5,6 @@ export { CampagneOverzicht } from "./components/CampagneOverzicht";
5
5
  export { CampagneDetail } from "./components/CampagneDetail";
6
6
  export { RapportagePage } from "./components/RapportagePage";
7
7
  export { ActiePage } from "./components/ActiePage";
8
+ export * from "./lib/voorraad-service";
8
9
  export declare const actiemailKwekersErpMathot: FlowModule;
9
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AASnD,eAAO,MAAM,yBAAyB,EAAE,UAgBvC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,cAAc,wBAAwB,CAAC;AASvC,eAAO,MAAM,yBAAyB,EAAE,UAgBvC,CAAC"}
package/dist-lib/index.js CHANGED
@@ -5,6 +5,7 @@ export { CampagneOverzicht } from "./components/CampagneOverzicht";
5
5
  export { CampagneDetail } from "./components/CampagneDetail";
6
6
  export { RapportagePage } from "./components/RapportagePage";
7
7
  export { ActiePage } from "./components/ActiePage";
8
+ export * from "./lib/voorraad-service";
8
9
  import { HuisstijlSettingsCard } from "./components/settings/HuisstijlSettingsCard";
9
10
  import { TrackingSettingsCard } from "./components/settings/TrackingSettingsCard";
10
11
  import { UitsluitingenSettingsCard } from "./components/settings/UitsluitingenSettingsCard";