@flowselections/floriday-voorraad 1.0.72 → 1.0.75

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.
Files changed (24) hide show
  1. package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
  2. package/dist-lib/components/voorraad/ArtikelWizard.js +1 -1
  3. package/dist-lib/components/voorraad/FloridaySentCard.d.ts.map +1 -1
  4. package/dist-lib/components/voorraad/FloridaySentCard.js +15 -2
  5. package/dist-lib/components/voorraad/NieuwArtikelPage.d.ts.map +1 -1
  6. package/dist-lib/components/voorraad/NieuwArtikelPage.js +1 -0
  7. package/dist-lib/components/voorraad/PriceStatusBadge.d.ts +3 -1
  8. package/dist-lib/components/voorraad/PriceStatusBadge.d.ts.map +1 -1
  9. package/dist-lib/components/voorraad/PriceStatusBadge.js +64 -14
  10. package/dist-lib/components/voorraad/ProductStructureCard.js +1 -1
  11. package/dist-lib/components/voorraad/StockMutationHistoryCard.d.ts.map +1 -1
  12. package/dist-lib/components/voorraad/StockMutationHistoryCard.js +1 -0
  13. package/dist-lib/components/voorraad/StockPushMonitor.d.ts.map +1 -1
  14. package/dist-lib/components/voorraad/StockPushMonitor.js +8 -2
  15. package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
  16. package/dist-lib/components/voorraad/VoorraadPage.js +3 -2
  17. package/dist-lib/components/voorraad/VoorraadStockCard.d.ts.map +1 -1
  18. package/dist-lib/components/voorraad/VoorraadStockCard.js +1 -1
  19. package/dist-lib/components/voorraad/VoorraadTable.d.ts.map +1 -1
  20. package/dist-lib/components/voorraad/VoorraadTable.js +2 -1
  21. package/dist-lib/integrations/supabase/types.d.ts +1127 -2
  22. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  23. package/dist-lib/integrations/supabase/types.js +1 -0
  24. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"ArtikelWizard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ArtikelWizard.tsx"],"names":[],"mappings":"AA6MA,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EAAE,cAAc,EACvB,WAAW,EACX,SAAS,EACT,WAAW,GACZ,GAAE;IACD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACjB,+BAssEL"}
1
+ {"version":3,"file":"ArtikelWizard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ArtikelWizard.tsx"],"names":[],"mappings":"AA6MA,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EAAE,cAAc,EACvB,WAAW,EACX,SAAS,EACT,WAAW,GACZ,GAAE;IACD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACjB,+BAusEL"}
@@ -1724,7 +1724,7 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
1724
1724
  setDeleteOpen(o);
1725
1725
  if (!o)
1726
1726
  setDeleteAck(false);
1727
- }, children: [_jsx(AlertDialogTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "sm", className: "text-destructive hover:text-destructive", children: [_jsx(Trash2, { className: "mr-2 h-4 w-4" }), "Verwijderen"] }) }), _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Product verwijderen?" }), _jsx(AlertDialogDescription, { asChild: true, children: _jsxs("div", { className: "space-y-3 text-sm", children: [_jsxs("div", { children: ["Je staat op het punt om ", _jsx("strong", { children: data.name || "dit artikel" }), data.articleCode ? _jsxs(_Fragment, { children: [" (artikelnummer ", _jsx("code", { children: data.articleCode }), ")"] }) : null, " ", "lokaal uit de voorraad te verwijderen."] }), _jsxs("div", { className: "rounded-md border p-3 bg-muted/40 space-y-1", children: [_jsxs("div", { children: ["Huidige voorraad: ", _jsx("strong", { children: productMeta.physicalStock }), " stuks"] }), _jsxs("div", { children: ["Floriday-koppeling:", " ", productMeta.floridayActive || productMeta.floridayTradeItemId ? (_jsx("span", { className: "text-emerald-700 font-medium", children: "actief" })) : (_jsx("span", { className: "text-muted-foreground", children: "geen" }))] })] }), (productMeta.floridayActive || productMeta.floridayTradeItemId) && (_jsxs("div", { className: "rounded-md border border-amber-300 bg-amber-50 p-3 text-amber-900", children: [_jsx("strong", { children: "Let op:" }), " dit artikel is gekoppeld met Floriday. Als je alleen de lokale kant verwijdert, blijft het artikel op Floriday bestaan en verschijnt het bij de volgende sync weer terug in de lijst. Trek het artikel daarna zelf in via het Floriday-portaal als je het echt weg wilt."] })), productMeta.physicalStock > 0 && (_jsxs("div", { className: "rounded-md border border-rose-300 bg-rose-50 p-3 text-rose-900", children: ["Er staat nog voorraad (", productMeta.physicalStock, " stuks) op dit product. Deze voorraad wordt \u00F3\u00F3k verwijderd en kan niet automatisch worden hersteld."] })), _jsxs("label", { className: "flex items-start gap-2 pt-1", children: [_jsx(Checkbox, { checked: deleteAck, onCheckedChange: (v) => setDeleteAck(Boolean(v)) }), _jsx("span", { children: "Ik weet zeker dat ik dit product wil verwijderen en begrijp de gevolgen." })] })] }) })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Annuleren" }), _jsx(AlertDialogAction, { disabled: !deleteAck, onClick: handleDeleteDraft, className: "bg-destructive text-destructive-foreground hover:bg-destructive/90", children: "Definitief verwijderen" })] })] })] }))] }), isEditing && (data.name || data.vbnName) && (_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "text-xs uppercase tracking-wide text-muted-foreground", children: "Artikel" }), _jsxs("div", { className: "flex items-center gap-3 flex-wrap", children: [_jsx("h1", { className: "text-2xl font-semibold leading-tight", children: data.name || data.vbnName }), (isEditingPublished || draftFloridayId) ? (syncState === "saving" ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full bg-muted text-muted-foreground", children: [_jsx(Loader2, { className: "h-3 w-3 animate-spin" }), " Synchroniseren\u2026"] })) : syncState === "saved" ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full bg-emerald-100 text-emerald-700", children: [_jsx(Check, { className: "h-3 w-3" }), " Gesynchroniseerd"] })) : syncState === "error" ? (_jsx("span", { className: "inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full bg-red-100 text-red-700", title: syncError ?? undefined, children: "Sync mislukt \u2014 klik Opslaan om opnieuw te proberen" })) : null) : (_jsx("span", { className: "inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full bg-muted text-muted-foreground", children: "Nog niet gepubliceerd naar Floriday" })), _jsx(PriceStatusBadge, { tradeItemId: tradeItemId ?? draftFloridayId ?? null, offerForSale: data.offerForSale })] }), data.name && data.vbnName && data.name !== data.vbnName && (_jsx("div", { className: "text-sm text-muted-foreground", children: data.vbnName }))] })), pendingSync && (_jsxs(Card, { className: "p-4 border-amber-300 bg-amber-50 text-amber-900 space-y-2", children: [_jsx("div", { className: "font-medium text-sm", children: "Je wijzigingen zijn opgeslagen, maar nog niet doorgegeven aan Floriday" }), _jsxs("div", { className: "text-xs", children: ["Alles wat je hier hebt ingevuld blijft bewaard in dit systeem. Zodra het doorgeven lukt, verdwijnt deze melding vanzelf.", pendingSync.error ? ` Reden: ${pendingSync.error}` : ""] }), _jsx(FloridayStockReasonLine, { productId: linkedProductId ?? productId ?? null }), _jsx(Button, { type: "button", variant: "outline", size: "sm", disabled: retryingSync, onClick: retrySync, children: retryingSync ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-3 w-3 animate-spin mr-1" }), " Bezig\u2026"] })) : ("Opnieuw proberen") })] })), isEditing && (_jsx(Card, { className: "p-4 bg-muted/30 flex items-center gap-3 text-sm", children: isEditingPublished ? (_jsxs(_Fragment, { children: [_jsx("img", { src: floridayLogo, alt: "Floriday", className: "h-6 w-6 shrink-0" }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "font-medium", children: "Gepubliceerd artikel bewerken" }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Wijzigingen worden direct naar Floriday verstuurd. Je kunt vrij tussen alle secties navigeren." })] }), _jsxs(Button, { onClick: () => void submit(), disabled: submitting || invalidStepCount > 0, children: [submitting ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(Check, { className: "mr-2 h-4 w-4" })), "Wijzigingen opslaan"] })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "font-medium", children: "Lokaal artikel bewerken" }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Niet gepubliceerd. Je kunt vrij tussen alle secties navigeren." })] }), _jsxs(Button, { onClick: () => void submit(), disabled: submitting || invalidStepCount > 0, children: [submitting ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(Check, { className: "mr-2 h-4 w-4" })), publishToFloriday ? "Publiceren naar Floriday" : "Wijzigingen opslaan"] })] })) })), isEditing && (_jsxs(_Fragment, { children: [_jsx(ProductStructureCard, { productId: linkedProductId ?? productId ?? null }), _jsx(ArtikelCompositionCard, { productId: linkedProductId ?? productId ?? null, floridayId: tradeItemId ?? draftFloridayId ?? null }), _jsx(FloridayInfoCard, { floridayTradeItemId: tradeItemId ?? draftFloridayId ?? null, local: {
1727
+ }, children: [_jsx(AlertDialogTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "sm", className: "text-destructive hover:text-destructive", children: [_jsx(Trash2, { className: "mr-2 h-4 w-4" }), "Verwijderen"] }) }), _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Product verwijderen?" }), _jsx(AlertDialogDescription, { asChild: true, children: _jsxs("div", { className: "space-y-3 text-sm", children: [_jsxs("div", { children: ["Je staat op het punt om ", _jsx("strong", { children: data.name || "dit artikel" }), data.articleCode ? _jsxs(_Fragment, { children: [" (artikelnummer ", _jsx("code", { children: data.articleCode }), ")"] }) : null, " ", "lokaal uit de voorraad te verwijderen."] }), _jsxs("div", { className: "rounded-md border p-3 bg-muted/40 space-y-1", children: [_jsxs("div", { children: ["Huidige voorraad: ", _jsx("strong", { children: productMeta.physicalStock }), " stuks"] }), _jsxs("div", { children: ["Floriday-koppeling:", " ", productMeta.floridayActive || productMeta.floridayTradeItemId ? (_jsx("span", { className: "text-emerald-700 font-medium", children: "actief" })) : (_jsx("span", { className: "text-muted-foreground", children: "geen" }))] })] }), (productMeta.floridayActive || productMeta.floridayTradeItemId) && (_jsxs("div", { className: "rounded-md border border-amber-300 bg-amber-50 p-3 text-amber-900", children: [_jsx("strong", { children: "Let op:" }), " dit artikel is gekoppeld met Floriday. Als je alleen de lokale kant verwijdert, blijft het artikel op Floriday bestaan en verschijnt het bij de volgende sync weer terug in de lijst. Trek het artikel daarna zelf in via het Floriday-portaal als je het echt weg wilt."] })), productMeta.physicalStock > 0 && (_jsxs("div", { className: "rounded-md border border-rose-300 bg-rose-50 p-3 text-rose-900", children: ["Er staat nog voorraad (", productMeta.physicalStock, " stuks) op dit product. Deze voorraad wordt \u00F3\u00F3k verwijderd en kan niet automatisch worden hersteld."] })), _jsxs("label", { className: "flex items-start gap-2 pt-1", children: [_jsx(Checkbox, { checked: deleteAck, onCheckedChange: (v) => setDeleteAck(Boolean(v)) }), _jsx("span", { children: "Ik weet zeker dat ik dit product wil verwijderen en begrijp de gevolgen." })] })] }) })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Annuleren" }), _jsx(AlertDialogAction, { disabled: !deleteAck, onClick: handleDeleteDraft, className: "bg-destructive text-destructive-foreground hover:bg-destructive/90", children: "Definitief verwijderen" })] })] })] }))] }), isEditing && (data.name || data.vbnName) && (_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "text-xs uppercase tracking-wide text-muted-foreground", children: "Artikel" }), _jsxs("div", { className: "flex items-center gap-3 flex-wrap", children: [_jsx("h1", { className: "text-2xl font-semibold leading-tight", children: data.name || data.vbnName }), (isEditingPublished || draftFloridayId) ? (syncState === "saving" ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full bg-muted text-muted-foreground", children: [_jsx(Loader2, { className: "h-3 w-3 animate-spin" }), " Synchroniseren\u2026"] })) : syncState === "saved" ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full bg-emerald-100 text-emerald-700", children: [_jsx(Check, { className: "h-3 w-3" }), " Gesynchroniseerd"] })) : syncState === "error" ? (_jsx("span", { className: "inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full bg-red-100 text-red-700", title: syncError ?? undefined, children: "Sync mislukt \u2014 klik Opslaan om opnieuw te proberen" })) : null) : (_jsx("span", { className: "inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full bg-muted text-muted-foreground", children: "Nog niet gepubliceerd naar Floriday" })), _jsx(PriceStatusBadge, { tradeItemId: tradeItemId ?? draftFloridayId ?? null, productId: productId ?? null, offerForSale: data.offerForSale })] }), data.name && data.vbnName && data.name !== data.vbnName && (_jsx("div", { className: "text-sm text-muted-foreground", children: data.vbnName }))] })), pendingSync && (_jsxs(Card, { className: "p-4 border-amber-300 bg-amber-50 text-amber-900 space-y-2", children: [_jsx("div", { className: "font-medium text-sm", children: "Je wijzigingen zijn opgeslagen, maar nog niet doorgegeven aan Floriday" }), _jsxs("div", { className: "text-xs", children: ["Alles wat je hier hebt ingevuld blijft bewaard in dit systeem. Zodra het doorgeven lukt, verdwijnt deze melding vanzelf.", pendingSync.error ? ` Reden: ${pendingSync.error}` : ""] }), _jsx(FloridayStockReasonLine, { productId: linkedProductId ?? productId ?? null }), _jsx(Button, { type: "button", variant: "outline", size: "sm", disabled: retryingSync, onClick: retrySync, children: retryingSync ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-3 w-3 animate-spin mr-1" }), " Bezig\u2026"] })) : ("Opnieuw proberen") })] })), isEditing && (_jsx(Card, { className: "p-4 bg-muted/30 flex items-center gap-3 text-sm", children: isEditingPublished ? (_jsxs(_Fragment, { children: [_jsx("img", { src: floridayLogo, alt: "Floriday", className: "h-6 w-6 shrink-0" }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "font-medium", children: "Gepubliceerd artikel bewerken" }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Wijzigingen worden direct naar Floriday verstuurd. Je kunt vrij tussen alle secties navigeren." })] }), _jsxs(Button, { onClick: () => void submit(), disabled: submitting || invalidStepCount > 0, children: [submitting ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(Check, { className: "mr-2 h-4 w-4" })), "Wijzigingen opslaan"] })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "font-medium", children: "Lokaal artikel bewerken" }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Niet gepubliceerd. Je kunt vrij tussen alle secties navigeren." })] }), _jsxs(Button, { onClick: () => void submit(), disabled: submitting || invalidStepCount > 0, children: [submitting ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(Check, { className: "mr-2 h-4 w-4" })), publishToFloriday ? "Publiceren naar Floriday" : "Wijzigingen opslaan"] })] })) })), isEditing && (_jsxs(_Fragment, { children: [_jsx(ProductStructureCard, { productId: linkedProductId ?? productId ?? null }), _jsx(ArtikelCompositionCard, { productId: linkedProductId ?? productId ?? null, floridayId: tradeItemId ?? draftFloridayId ?? null }), _jsx(FloridayInfoCard, { floridayTradeItemId: tradeItemId ?? draftFloridayId ?? null, local: {
1728
1728
  name: data.name || data.vbnName,
1729
1729
  articleNumber: data.articleCode,
1730
1730
  gtin: data.gtin,
@@ -1 +1 @@
1
- {"version":3,"file":"FloridaySentCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FloridaySentCard.tsx"],"names":[],"mappings":"AAWA,KAAK,GAAG,GAAG;IACT,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AAUF,wBAAgB,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,EAAE,QAAQ,GAAG,cAAc,GAAG,aAAa,GAAG,YAAY,GAAG,YAAY,GAAG,aAAa,CAAC,GAAG,MAAM,CA0BxI;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,YAAY,EACZ,SAAS,EACT,WAAW,EACX,WAAW,GACZ,EAAE;IACD,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,+BA+IA"}
1
+ {"version":3,"file":"FloridaySentCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FloridaySentCard.tsx"],"names":[],"mappings":"AAWA,KAAK,GAAG,GAAG;IACT,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AAUF,wBAAgB,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,EAAE,QAAQ,GAAG,cAAc,GAAG,aAAa,GAAG,YAAY,GAAG,YAAY,GAAG,aAAa,CAAC,GAAG,MAAM,CA0BxI;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,YAAY,EACZ,SAAS,EACT,WAAW,EACX,WAAW,GACZ,EAAE;IACD,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,+BA2JA"}
@@ -50,14 +50,27 @@ export function FloridaySentCard({ connectionId, productId, tradeItemId, product
50
50
  const [weekPrice, setWeekPrice] = useState({});
51
51
  const [weekChange, setWeekChange] = useState({});
52
52
  const [reason, setReason] = useState(null);
53
+ // Het Floriday-id komt uit het artikel zelf (canonieke bron), niet alleen
54
+ // uit de meegegeven eigenschap: zo klopt de kop ook als de aanroeper het
55
+ // id niet heeft meegegeven.
56
+ const [resolvedTradeItemId, setResolvedTradeItemId] = useState(tradeItemId ?? null);
53
57
  useEffect(() => {
54
58
  if (!connectionId || (!productId && !tradeItemId))
55
59
  return;
56
60
  let cancelled = false;
57
61
  (async () => {
62
+ let effectiveTradeItemId = tradeItemId ?? null;
63
+ if (!effectiveTradeItemId && productId) {
64
+ const { data: prod } = await supabase
65
+ .from("products").select("floriday_trade_item_id").eq("id", productId).maybeSingle();
66
+ effectiveTradeItemId = prod?.floriday_trade_item_id ?? null;
67
+ }
68
+ if (cancelled)
69
+ return;
70
+ setResolvedTradeItemId(effectiveTradeItemId);
58
71
  const ors = [
59
72
  productId ? `product_id.eq.${productId}` : null,
60
- tradeItemId ? `trade_item_id.eq.${tradeItemId}` : null,
73
+ effectiveTradeItemId ? `trade_item_id.eq.${effectiveTradeItemId}` : null,
61
74
  ].filter(Boolean).join(",");
62
75
  const { data } = await supabase
63
76
  .from("floriday_continuous_stock_queue")
@@ -119,7 +132,7 @@ export function FloridaySentCard({ connectionId, productId, tradeItemId, product
119
132
  cancelled = true;
120
133
  };
121
134
  }, [connectionId, productId, tradeItemId]);
122
- return (_jsx(Card, { children: _jsxs(CardContent, { className: "space-y-3 p-4", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Send, { className: "h-4 w-4 text-muted-foreground" }), _jsx("p", { className: "text-sm font-medium", children: "Laatst naar Floriday" })] }), _jsxs("p", { className: "text-xs text-muted-foreground break-all", children: ["Floriday-id: ", tradeItemId ? _jsx("span", { className: "font-mono", children: tradeItemId }) : "nog niet gekoppeld"] }), (productType === "GROUP" || productType === "SET" || productType === "COMPOSITION") && (_jsx("p", { className: "text-xs text-muted-foreground", children: "Productgroep (ERP): leden met eigen voorraad; alleen het totaal gaat naar Floriday. Set (Floriday): \u00E9\u00E9n artikel samengesteld uit onderdelen; de voorraad gaat pas naar Floriday na akkoord." })), rows.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog niets naar Floriday gestuurd." })) : (_jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full text-xs", children: [_jsx("thead", { className: "text-left text-muted-foreground", children: _jsxs("tr", { children: [_jsx("th", { className: "py-1 pr-3", children: "Week" }), _jsx("th", { className: "py-1 pr-3 text-right", children: "Aantal" }), _jsx("th", { className: "py-1 pr-3 text-right", children: "Prijs (excl. btw)" }), _jsx("th", { className: "py-1 pr-3", children: "Modus" }), _jsx("th", { className: "py-1 pr-3", children: "Tijd" }), _jsx("th", { className: "py-1", children: "Resultaat" })] }) }), _jsx("tbody", { children: rows.map((r) => (_jsxs("tr", { className: "border-t align-top", children: [_jsx("td", { className: "py-1 pr-3 whitespace-nowrap", children: r.target_week ? `${r.target_year}-${r.target_week}` : "—" }), _jsx("td", { className: "py-1 pr-3 text-right tabular-nums", children: r.number_of_pieces ?? "—" }), _jsx("td", { className: "py-1 pr-3 text-right tabular-nums", children: (() => {
135
+ return (_jsx(Card, { children: _jsxs(CardContent, { className: "space-y-3 p-4", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Send, { className: "h-4 w-4 text-muted-foreground" }), _jsx("p", { className: "text-sm font-medium", children: "Laatst naar Floriday" })] }), _jsxs("p", { className: "text-xs text-muted-foreground break-all", children: ["Floriday-id: ", resolvedTradeItemId ? _jsx("span", { className: "font-mono", children: resolvedTradeItemId }) : "nog niet gekoppeld"] }), (productType === "GROUP" || productType === "SET" || productType === "COMPOSITION") && (_jsx("p", { className: "text-xs text-muted-foreground", children: "Productgroep (ERP): leden met eigen voorraad; alleen het totaal gaat naar Floriday. Set (Floriday): \u00E9\u00E9n artikel samengesteld uit onderdelen; de voorraad gaat pas naar Floriday na akkoord." })), rows.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog niets naar Floriday gestuurd." })) : (_jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full text-xs", children: [_jsx("thead", { className: "text-left text-muted-foreground", children: _jsxs("tr", { children: [_jsx("th", { className: "py-1 pr-3", children: "Week" }), _jsx("th", { className: "py-1 pr-3 text-right", children: "Aantal" }), _jsx("th", { className: "py-1 pr-3 text-right", children: "Prijs (excl. btw)" }), _jsx("th", { className: "py-1 pr-3", children: "Modus" }), _jsx("th", { className: "py-1 pr-3", children: "Tijd" }), _jsx("th", { className: "py-1", children: "Resultaat" })] }) }), _jsx("tbody", { children: rows.map((r) => (_jsxs("tr", { className: "border-t align-top", children: [_jsx("td", { className: "py-1 pr-3 whitespace-nowrap", children: r.target_week ? `${r.target_year}-${r.target_week}` : "—" }), _jsx("td", { className: "py-1 pr-3 text-right tabular-nums", children: r.number_of_pieces ?? "—" }), _jsx("td", { className: "py-1 pr-3 text-right tabular-nums", children: (() => {
123
136
  const p = r.price_sent ?? weekPrice[`${r.target_year ?? ""}-${r.target_week ?? ""}`];
124
137
  return p != null ? `€ ${Number(p).toFixed(2)}` : "—";
125
138
  })() }), _jsx("td", { className: "py-1 pr-3 whitespace-nowrap", children: r.stock_mode ? MODE_LABEL[r.stock_mode] ?? r.stock_mode : "—" }), _jsx("td", { className: "py-1 pr-3 whitespace-nowrap", children: new Date(r.sent_at ?? r.updated_at).toLocaleString("nl-NL") }), _jsxs("td", { className: "py-1", children: [r.status === "skipped_no_price" && reason ? reason : resultText(r), weekChange[`${r.target_year ?? ""}-${r.target_week ?? ""}`] &&
@@ -1 +1 @@
1
- {"version":3,"file":"NieuwArtikelPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/NieuwArtikelPage.tsx"],"names":[],"mappings":"AAgBA,wBAAgB,gBAAgB,gCAiM/B"}
1
+ {"version":3,"file":"NieuwArtikelPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/NieuwArtikelPage.tsx"],"names":[],"mappings":"AAgBA,wBAAgB,gBAAgB,gCAkM/B"}
@@ -63,6 +63,7 @@ export function NieuwArtikelPage() {
63
63
  p_product_id: id,
64
64
  p_physical_stock: Math.trunc(Number(stock) || 0),
65
65
  p_notes: "Nieuw artikel",
66
+ p_change_source: "NEW_ARTICLE",
66
67
  });
67
68
  if (stockErr) {
68
69
  throw new Error(`Artikel is aangemaakt, maar de voorraad is niet opgeslagen: ${stockErr.message}. Klik opnieuw op "Artikel opslaan" om alleen de voorraad nog eens te proberen.`);
@@ -1,7 +1,9 @@
1
1
  type Props = {
2
2
  tradeItemId: string | null | undefined;
3
+ /** Artikel-id; wordt gebruikt om het Floriday-id en de verzendgeschiedenis op te zoeken. */
4
+ productId?: string | null;
3
5
  offerForSale: boolean;
4
6
  };
5
- export declare function PriceStatusBadge({ tradeItemId, offerForSale }: Props): import("react").JSX.Element | null;
7
+ export declare function PriceStatusBadge({ tradeItemId, productId, offerForSale }: Props): import("react").JSX.Element | null;
6
8
  export {};
7
9
  //# sourceMappingURL=PriceStatusBadge.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PriceStatusBadge.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/PriceStatusBadge.tsx"],"names":[],"mappings":"AAmCA,KAAK,KAAK,GAAG;IACX,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACvC,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AAeF,wBAAgB,gBAAgB,CAAC,EAAE,WAAW,EAAE,YAAY,EAAE,EAAE,KAAK,sCA8EpE"}
1
+ {"version":3,"file":"PriceStatusBadge.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/PriceStatusBadge.tsx"],"names":[],"mappings":"AAuFA,KAAK,KAAK,GAAG;IACX,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACvC,4FAA4F;IAC5F,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AAeF,wBAAgB,gBAAgB,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,EAAE,KAAK,sCAmG/E"}
@@ -3,24 +3,37 @@ import { CircleDollarSign, Clock, MinusCircle, Loader2 } from "lucide-react";
3
3
  import { useEffect, useState } from "react";
4
4
  import { useTradeItemSupplyLine } from "../../hooks/useTradeItemSupplyLine.js";
5
5
  import { supabase } from "../../integrations/supabase/client.js";
6
- /** M26: laatst naar Floriday verzonden weekprijs (weekaanbod), uit de wachtrij. */
7
- function useLastSentPrice(tradeItemId) {
6
+ /**
7
+ * M26: laatst naar Floriday verzonden weekprijs (weekaanbod), uit de wachtrij.
8
+ * M28: dezelfde bron als de verzendgeschiedenis ("Laatst naar Floriday"):
9
+ * de wachtrij wordt op artikel-id én Floriday-id doorzocht, zodat de kop nooit
10
+ * afwijkt van de geschiedenis.
11
+ */
12
+ function useLastSentPrice(tradeItemId, productId) {
8
13
  const [v, setV] = useState(null);
9
14
  useEffect(() => {
10
- if (!tradeItemId)
15
+ if (!tradeItemId && !productId)
11
16
  return;
12
17
  let cancelled = false;
13
18
  const load = async () => {
19
+ const ors = [
20
+ tradeItemId ? `trade_item_id.eq.${tradeItemId}` : null,
21
+ productId ? `product_id.eq.${productId}` : null,
22
+ ]
23
+ .filter(Boolean)
24
+ .join(",");
14
25
  const { data } = await supabase
15
26
  .from("floriday_continuous_stock_queue")
16
27
  .select("price_sent,target_week,target_year,updated_at")
17
- .eq("trade_item_id", tradeItemId)
28
+ .or(ors)
18
29
  .not("price_sent", "is", null)
19
30
  .order("updated_at", { ascending: false })
20
31
  .limit(1)
21
32
  .maybeSingle();
22
33
  if (!cancelled)
23
- setV(data ? { price: Number(data.price_sent), week: data.target_week, year: data.target_year, at: data.updated_at } : null);
34
+ setV(data
35
+ ? { price: Number(data.price_sent), week: data.target_week, year: data.target_year, at: data.updated_at }
36
+ : null);
24
37
  };
25
38
  void load();
26
39
  const t = setInterval(load, 30000);
@@ -28,9 +41,40 @@ function useLastSentPrice(tradeItemId) {
28
41
  cancelled = true;
29
42
  clearInterval(t);
30
43
  };
31
- }, [tradeItemId]);
44
+ }, [tradeItemId, productId]);
32
45
  return v;
33
46
  }
47
+ /**
48
+ * Het Floriday-id van het artikel zelf (canonieke bron: products.floriday_trade_item_id).
49
+ * Zo klopt de prijsstatus ook als de aanroeper het id niet heeft meegegeven.
50
+ */
51
+ function useResolvedTradeItemId(tradeItemId, productId) {
52
+ const [resolved, setResolved] = useState(tradeItemId ?? null);
53
+ useEffect(() => {
54
+ if (tradeItemId) {
55
+ setResolved(tradeItemId);
56
+ return;
57
+ }
58
+ if (!productId) {
59
+ setResolved(null);
60
+ return;
61
+ }
62
+ let cancelled = false;
63
+ void supabase
64
+ .from("products")
65
+ .select("floriday_trade_item_id")
66
+ .eq("id", productId)
67
+ .maybeSingle()
68
+ .then(({ data }) => {
69
+ if (!cancelled)
70
+ setResolved(data?.floriday_trade_item_id ?? null);
71
+ });
72
+ return () => {
73
+ cancelled = true;
74
+ };
75
+ }, [tradeItemId, productId]);
76
+ return resolved;
77
+ }
34
78
  function formatDate(iso) {
35
79
  if (!iso)
36
80
  return "—";
@@ -45,14 +89,23 @@ function formatDate(iso) {
45
89
  return iso;
46
90
  }
47
91
  }
48
- export function PriceStatusBadge({ tradeItemId, offerForSale }) {
49
- const { data: supplyLine, isLoading } = useTradeItemSupplyLine(tradeItemId);
50
- const sent = useLastSentPrice(tradeItemId);
51
- if (!tradeItemId)
92
+ export function PriceStatusBadge({ tradeItemId, productId, offerForSale }) {
93
+ const resolvedId = useResolvedTradeItemId(tradeItemId, productId);
94
+ const { data: supplyLine, isLoading } = useTradeItemSupplyLine(resolvedId);
95
+ const sent = useLastSentPrice(resolvedId, productId);
96
+ if (!resolvedId && !productId)
52
97
  return null;
53
98
  if (isLoading) {
54
99
  return (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full bg-muted text-muted-foreground", children: [_jsx(Loader2, { className: "h-3 w-3 animate-spin" }), " Prijsstatus laden\u2026"] }));
55
100
  }
101
+ // "Te koop aanbieden" staat uit: nooit "Prijs live" tonen. Wel de laatst
102
+ // verzonden prijs melden als die er is (zelfde bron als de geschiedenis).
103
+ if (!offerForSale) {
104
+ if (sent) {
105
+ return (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full cursor-help bg-amber-100 text-amber-800", title: `Laatst verzonden op ${new Date(sent.at).toLocaleString("nl-NL")} · week ${sent.week ?? "?"}.\n"Te koop aanbieden" staat uit: dit artikel is niet te koop in Floriday.`, children: [_jsx(CircleDollarSign, { className: "h-3 w-3" }), " Laatst verzonden: \u20AC ", sent.price.toFixed(2), " \u2013 niet te koop in Floriday"] }));
106
+ }
107
+ return (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full cursor-help bg-muted text-muted-foreground", title: '"Te koop aanbieden" staat uit.', children: [_jsx(MinusCircle, { className: "h-3 w-3" }), " Geen prijs actief"] }));
108
+ }
56
109
  if (!supplyLine && sent) {
57
110
  return (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full cursor-help bg-emerald-100 text-emerald-700", title: `Weekaanbod verzonden op ${new Date(sent.at).toLocaleString("nl-NL")}`, children: [_jsx(CircleDollarSign, { className: "h-3 w-3" }), " Prijs live in Floriday \u00B7 week ", sent.week ?? "?", " \u00B7 \u20AC ", sent.price.toFixed(2)] }));
58
111
  }
@@ -65,7 +118,7 @@ export function PriceStatusBadge({ tradeItemId, offerForSale }) {
65
118
  let Icon = CircleDollarSign;
66
119
  let cls = "";
67
120
  let variant;
68
- if (!supplyLine || !offerForSale) {
121
+ if (!supplyLine) {
69
122
  variant = "none";
70
123
  label = "Geen prijs actief";
71
124
  Icon = MinusCircle;
@@ -90,9 +143,6 @@ export function PriceStatusBadge({ tradeItemId, offerForSale }) {
90
143
  : "—"}`,
91
144
  `Beschikbaar: ${qty}`,
92
145
  `Periode: ${formatDate(supplyLine.period_start)} → ${formatDate(supplyLine.period_end)}`,
93
- variant === "none" && !offerForSale
94
- ? '"Te koop aanbieden" staat uit.'
95
- : null,
96
146
  ]
97
147
  .filter(Boolean)
98
148
  .join("\n")
@@ -104,7 +104,7 @@ export function ProductStructureCard({ productId }) {
104
104
  return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx(CardTitle, { children: "Producttype" }), _jsx(Badge, { className: PRODUCT_TYPE_CLASSES[type], title: PRODUCT_TYPE_HELP[type], children: PRODUCT_TYPE_LABELS[type] }), _jsx("span", { title: PRODUCT_TYPE_HELP[type], "aria-label": PRODUCT_TYPE_HELP[type], children: _jsx(Info, { className: "h-4 w-4 text-muted-foreground" }) })] }), _jsx(CardDescription, { children: PRODUCT_TYPE_HELP[type] })] }), _jsxs(CardContent, { className: "space-y-3 text-sm", children: [structureReason && (_jsxs("div", { className: "rounded border border-destructive/30 bg-destructive/10 px-3 py-2 text-destructive", children: ["Actie nodig: ", structureReason] })), type === "GROUP" && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "font-medium", children: [publishedLabel(desired, setting?.packing_size), " \u00B7 som van ", parts.length, " ", parts.length === 1 ? "lid" : "leden", " (", totals?.sum_members ?? 0, " beschikbaar)"] }), _jsx("p", { className: "text-muted-foreground", children: "De leden staan niet los in Floriday; alleen deze groep wordt gepubliceerd. Volgorde = voorraadprioriteit (te wijzigen in de kaart Productgroep hieronder)." }), _jsxs("details", { className: "rounded border", children: [_jsxs("summary", { className: "cursor-pointer px-3 py-2", children: ["Leden tonen (", parts.length, ")"] }), _jsxs("table", { className: "w-full text-xs", children: [_jsx("thead", { className: "text-left text-muted-foreground", children: _jsxs("tr", { children: [_jsx("th", { className: "p-2", children: "#" }), _jsx("th", { className: "p-2", children: "Lid" }), _jsx("th", { className: "p-2 text-right", children: "Voorraad" }), _jsx("th", { className: "p-2 text-right", children: "Gereserveerd" }), _jsx("th", { className: "p-2 text-right", children: "Beschikbaar" })] }) }), _jsx("tbody", { children: parts.map((p, i) => (_jsxs("tr", { className: "border-t", children: [_jsx("td", { className: "p-2", children: i + 1 }), _jsxs("td", { className: "p-2", children: [p.product_id ? (_jsx(Link, { to: "/voorraad/$id", params: { id: p.product_id }, className: "underline", children: name(p.product_id) })) : ("—"), p.reason && _jsxs("span", { className: "ml-1 text-destructive", children: ["(", ACTION_REASON_LABELS[p.reason] ?? p.reason, ")"] })] }), _jsx("td", { className: "p-2 text-right tabular-nums", children: p.physical ?? 0 }), _jsx("td", { className: "p-2 text-right tabular-nums", children: p.reserved ?? 0 }), _jsx("td", { className: "p-2 text-right tabular-nums", children: p.available ?? 0 })] }, p.product_id ?? i))) })] })] })] })), type === "SET" && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "font-medium", children: ["Berekend: ", totals?.computed ?? 0, " sets", totals?.limiting_product_id && (_jsxs(_Fragment, { children: [" ", "\u00B7 beperkt door: ", name(totals.limiting_product_id), ", ", totals?.computed ?? 0, " sets"] }))] }), _jsx("div", { className: "rounded border border-amber-300 bg-amber-50 px-3 py-2 text-amber-900", children: "Deze set gaat nog niet naar Floriday (\"geen aanbod\") totdat de Logistieke engine bij een bestelling de onderdelen reserveert en afboekt." }), _jsxs("table", { className: "w-full text-xs", children: [_jsx("thead", { className: "text-left text-muted-foreground", children: _jsxs("tr", { children: [_jsx("th", { className: "p-2", children: "Onderdeel" }), _jsx("th", { className: "p-2 text-right", children: "Per set" }), _jsx("th", { className: "p-2 text-right", children: "Beschikbaar" }), _jsx("th", { className: "p-2 text-right", children: "Genoeg voor" })] }) }), _jsx("tbody", { children: parts.map((p, i) => {
105
105
  const limiting = p.product_id && p.product_id === totals?.limiting_product_id;
106
106
  return (_jsxs("tr", { className: `border-t ${limiting ? "font-medium" : ""}`, children: [_jsxs("td", { className: "p-2", children: [p.product_id ? (_jsx(Link, { to: "/voorraad/$id", params: { id: p.product_id }, className: "underline", children: name(p.product_id) })) : ("—"), limiting && _jsx("span", { className: "ml-1 text-amber-700", children: "(bepaalt het maximum)" })] }), _jsx("td", { className: "p-2 text-right tabular-nums", children: p.quantity ?? 1 }), _jsx("td", { className: "p-2 text-right tabular-nums", children: p.available ?? 0 }), _jsxs("td", { className: "p-2 text-right tabular-nums", children: [p.possible ?? 0, " sets"] })] }, (p.product_id ?? "") + i));
107
- }) })] }), _jsx("p", { className: "text-muted-foreground", children: "De onderdelen blijven ook los te koop in Floriday." })] })), groupHeads.length > 0 && (_jsx("div", { className: "rounded border px-3 py-2", children: groupHeads.map((g) => (_jsxs("div", { children: ["Lid van groep", " ", g.headId ? (_jsx(Link, { to: "/voorraad/$id", params: { id: g.headId }, className: "underline font-medium", children: g.name })) : (_jsx("span", { className: "font-medium", children: g.name })), " ", "\u2014 staat niet los in Floriday; de groep wordt gepubliceerd."] }, g.id))) })), (totals?.component_of_sets?.length ?? 0) > 0 && (_jsxs("div", { className: "rounded border px-3 py-2", children: ["Onderdeel van:", " ", totals.component_of_sets.map((id, i) => (_jsxs("span", { children: [i > 0 && ", ", _jsx(Link, { to: "/voorraad/$id", params: { id }, className: "underline", children: name(id) })] }, id))), ". Een verkoop van dit artikel verlaagt ook de berekende stand van die sets."] })), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Floriday: ", reason ?? "doorgegeven"] })] })] }));
107
+ }) })] }), _jsx("p", { className: "text-muted-foreground", children: "De onderdelen blijven ook los te koop in Floriday." })] })), groupHeads.length > 0 && (_jsx("div", { className: "rounded border px-3 py-2", children: groupHeads.map((g) => (_jsxs("div", { children: ["Lid van groep", " ", g.headId ? (_jsx(Link, { to: "/voorraad/$id", params: { id: g.headId }, className: "underline font-medium", children: g.name })) : (_jsx("span", { className: "font-medium", children: g.name })), " ", "\u2014 staat niet los in Floriday; de groep wordt gepubliceerd."] }, g.id))) })), (totals?.component_of_sets?.length ?? 0) > 0 && (_jsxs("div", { className: "rounded border px-3 py-2", children: ["Onderdeel van:", " ", totals.component_of_sets.map((id, i) => (_jsxs("span", { children: [i > 0 && ", ", _jsx(Link, { to: "/voorraad/$id", params: { id }, className: "underline", children: name(id) })] }, id))), ". Een verkoop van dit artikel verlaagt ook de berekende stand van die sets."] })), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Floriday: ", reason ?? (queue?.status === "sent" ? "doorgegeven" : "Nog niet gepubliceerd")] })] })] }));
108
108
  }
109
109
  /** Eén regel met de echte reden waarom de voorraad (nog) niet in Floriday staat. */
110
110
  export function FloridayStockReasonLine({ productId }) {
@@ -1 +1 @@
1
- {"version":3,"file":"StockMutationHistoryCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/StockMutationHistoryCard.tsx"],"names":[],"mappings":"AAWA,UAAU,KAAK;IACb,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AA8CD,wBAAgB,wBAAwB,CAAC,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE,KAAK,+BAyN1E"}
1
+ {"version":3,"file":"StockMutationHistoryCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/StockMutationHistoryCard.tsx"],"names":[],"mappings":"AAWA,UAAU,KAAK;IACb,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AA+CD,wBAAgB,wBAAwB,CAAC,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE,KAAK,+BAyN1E"}
@@ -21,6 +21,7 @@ const TYPE_LABEL = {
21
21
  import: { label: "Import", tone: "bg-slate-100 text-slate-700 border-slate-200" },
22
22
  production: { label: "Productie", tone: "bg-emerald-100 text-emerald-800 border-emerald-200" },
23
23
  stock_update: { label: "Voorraad bijgewerkt", tone: "bg-slate-100 text-slate-700 border-slate-200" },
24
+ new_article: { label: "Nieuw artikel", tone: "bg-emerald-100 text-emerald-800 border-emerald-200" },
24
25
  };
25
26
  function formatMoment(v) {
26
27
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"StockPushMonitor.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/StockPushMonitor.tsx"],"names":[],"mappings":"AA4NA,wBAAgB,gBAAgB,CAAC,EAAE,YAAY,EAAE,EAAE;IAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,+BA2MjF"}
1
+ {"version":3,"file":"StockPushMonitor.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/StockPushMonitor.tsx"],"names":[],"mappings":"AA4NA,wBAAgB,gBAAgB,CAAC,EAAE,YAAY,EAAE,EAAE;IAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,+BAiNjF"}
@@ -226,14 +226,20 @@ export function StockPushMonitor({ connectionId }) {
226
226
  onError: (e) => toast.error(e?.message ?? "Testpush inplannen is niet gelukt."),
227
227
  });
228
228
  const rows = jobs.data ?? [];
229
+ const jobsError = jobs.error;
229
230
  const summary = useMemo(() => {
231
+ if (jobs.isError) {
232
+ return `Pushes konden niet worden geladen${jobsError?.code ? ` (fout ${jobsError.code})` : ""}`;
233
+ }
230
234
  if (rows.length === 0)
231
235
  return "nog geen pushes";
232
236
  const last = rows[0];
233
237
  return `laatste: ${displayStatus(last).label} om ${timeLabel(last.createdAt)}`;
234
- }, [rows]);
238
+ }, [rows, jobs.isError, jobsError]);
235
239
  const stuckCount = rows.filter((r) => displayStatus(r).label === "Vastgelopen").length;
236
- return (_jsxs("section", { className: "rounded-lg border bg-card", children: [_jsxs("button", { type: "button", className: "flex w-full items-center gap-2 px-4 py-3 text-left text-sm font-medium", onClick: () => setOpen((v) => !v), children: [open ? (_jsx(ChevronDown, { className: "h-4 w-4 text-muted-foreground" })) : (_jsx(ChevronRight, { className: "h-4 w-4 text-muted-foreground" })), "Voorraadsync naar Floriday testen", _jsx("span", { className: "ml-auto text-xs font-normal text-muted-foreground", children: summary })] }), open && (_jsxs("div", { className: "space-y-4 border-t px-4 py-4 text-sm", children: [_jsx("p", { className: "text-muted-foreground", children: "Elke voorraadwijziging van een gekoppeld artikel zet automatisch opdrachten in de wachtrij. De Floriday-Authenticatiemodule verwerkt die elke minuut. Hieronder plan je een testopdracht in met de huidige voorraadstand, zonder iets te wijzigen." }), _jsxs("p", { className: "rounded-md border bg-muted/40 px-3 py-2 text-muted-foreground", children: ["E\u00E9n wijziging levert bewust meerdere berichten op: \u00E9\u00E9n met de", " ", _jsx("span", { className: "font-medium text-foreground", children: "actuele voorraad" }), " en daarnaast \u00E9\u00E9n per week met de ", _jsx("span", { className: "font-medium text-foreground", children: "verwachte beschikbaarheid" }), " voor de komende weken. Ze lijken op elkaar, maar gaan elk over een andere periode \u2014 zie de kolom \u201CGeldt voor\u201D. Plan je opnieuw een wijziging in, dan wordt een nog wachtende opdracht voor dezelfde week vervangen."] }), blockade.data?.blocked && (_jsxs("div", { className: "rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-destructive", children: [_jsx("span", { className: "font-medium", children: "Actie nodig: " }), "de Floriday-Authenticatiemodule krijgt geen geldig token, dus elke push mislukt meteen. Dit lost zichzelf niet op \u2014 controleer daar het adres van Central Auth en de actieve inloggegevens voor deze omgeving.", blockade.data.message && (_jsxs("span", { className: "block opacity-80", children: ["Melding: ", blockade.data.message] }))] })), stuckCount > 0 && (_jsxs("div", { className: "rounded-md border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-amber-800", children: [stuckCount === 1 ? "Eén opdracht is" : `${stuckCount} opdrachten zijn`, " door de verwerker opgepakt maar nooit afgerond. Ze worden na vijf minuten automatisch opnieuw opgepakt."] })), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Select, { value: selected, onValueChange: setSelected, disabled: !connectionId || items.isLoading || itemList.length === 0, children: [_jsx(SelectTrigger, { className: "h-9 w-[22rem]", children: _jsx(SelectValue, { placeholder: items.isLoading ? "Artikelen laden…" : "Geen gekoppelde artikelen gevonden" }) }), _jsx(SelectContent, { className: "max-h-72", children: itemList.map((i) => (_jsx(SelectItem, { value: i.id, children: _jsxs("span", { className: "flex w-full items-center justify-between gap-3", children: [_jsx("span", { className: "truncate", children: i.name }), _jsxs("span", { className: "tabular-nums text-muted-foreground", children: [i.stock, " stuks"] })] }) }, i.id))) })] }), _jsxs(Button, { type: "button", disabled: !selected || mutation.isPending, onClick: () => selected && mutation.mutate(selected), children: [_jsx(Send, { className: "mr-2 h-4 w-4" }), "Testpush uitvoeren"] }), _jsxs(Button, { type: "button", variant: "outline", disabled: jobs.isFetching, onClick: () => void jobs.refetch(), children: [_jsx(RefreshCw, { className: `mr-2 h-4 w-4 ${jobs.isFetching ? "animate-spin" : ""}` }), "Ververs"] })] }), _jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full text-left text-xs", children: [_jsx("thead", { className: "text-muted-foreground", children: _jsxs("tr", { children: [_jsx("th", { className: "py-1 pr-3 font-medium", children: "Artikel" }), _jsx("th", { className: "py-1 pr-3 font-medium", children: "Geldt voor" }), _jsx("th", { className: "py-1 pr-3 font-medium", children: "Stuks" }), _jsx("th", { className: "py-1 pr-3 font-medium", children: "Status" }), _jsx("th", { className: "py-1 pr-3 font-medium", children: "Pogingen" }), _jsx("th", { className: "py-1 pr-3 font-medium", children: "Aangemaakt" }), _jsx("th", { className: "py-1 pr-3 font-medium", children: "Bijgewerkt" }), _jsx("th", { className: "py-1 font-medium", children: "Melding" })] }) }), _jsxs("tbody", { children: [rows.length === 0 && (_jsx("tr", { children: _jsx("td", { colSpan: 8, className: "py-3 text-muted-foreground", children: "Er staan nog geen push-opdrachten in de wachtrij." }) })), rows.map((r) => {
240
+ return (_jsxs("section", { className: "rounded-lg border bg-card", children: [_jsxs("button", { type: "button", className: "flex w-full items-center gap-2 px-4 py-3 text-left text-sm font-medium", onClick: () => setOpen((v) => !v), children: [open ? (_jsx(ChevronDown, { className: "h-4 w-4 text-muted-foreground" })) : (_jsx(ChevronRight, { className: "h-4 w-4 text-muted-foreground" })), "Voorraadsync naar Floriday testen", _jsx("span", { className: "ml-auto text-xs font-normal text-muted-foreground", children: summary })] }), open && (_jsxs("div", { className: "space-y-4 border-t px-4 py-4 text-sm", children: [_jsx("p", { className: "text-muted-foreground", children: "Elke voorraadwijziging van een gekoppeld artikel zet automatisch opdrachten in de wachtrij. De Floriday-Authenticatiemodule verwerkt die elke minuut. Hieronder plan je een testopdracht in met de huidige voorraadstand, zonder iets te wijzigen." }), _jsxs("p", { className: "rounded-md border bg-muted/40 px-3 py-2 text-muted-foreground", children: ["E\u00E9n wijziging levert bewust meerdere berichten op: \u00E9\u00E9n met de", " ", _jsx("span", { className: "font-medium text-foreground", children: "actuele voorraad" }), " en daarnaast \u00E9\u00E9n per week met de ", _jsx("span", { className: "font-medium text-foreground", children: "verwachte beschikbaarheid" }), " voor de komende weken. Ze lijken op elkaar, maar gaan elk over een andere periode \u2014 zie de kolom \u201CGeldt voor\u201D. Plan je opnieuw een wijziging in, dan wordt een nog wachtende opdracht voor dezelfde week vervangen."] }), blockade.data?.blocked && (_jsxs("div", { className: "rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-destructive", children: [_jsx("span", { className: "font-medium", children: "Actie nodig: " }), "de Floriday-Authenticatiemodule krijgt geen geldig token, dus elke push mislukt meteen. Dit lost zichzelf niet op \u2014 controleer daar het adres van Central Auth en de actieve inloggegevens voor deze omgeving.", blockade.data.message && (_jsxs("span", { className: "block opacity-80", children: ["Melding: ", blockade.data.message] }))] })), stuckCount > 0 && (_jsxs("div", { className: "rounded-md border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-amber-800", children: [stuckCount === 1 ? "Eén opdracht is" : `${stuckCount} opdrachten zijn`, " door de verwerker opgepakt maar nooit afgerond. Ze worden na vijf minuten automatisch opnieuw opgepakt."] })), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Select, { value: selected, onValueChange: setSelected, disabled: !connectionId || items.isLoading || itemList.length === 0, children: [_jsx(SelectTrigger, { className: "h-9 w-[22rem]", children: _jsx(SelectValue, { placeholder: items.isLoading ? "Artikelen laden…" : "Geen gekoppelde artikelen gevonden" }) }), _jsx(SelectContent, { className: "max-h-72", children: itemList.map((i) => (_jsx(SelectItem, { value: i.id, children: _jsxs("span", { className: "flex w-full items-center justify-between gap-3", children: [_jsx("span", { className: "truncate", children: i.name }), _jsxs("span", { className: "tabular-nums text-muted-foreground", children: [i.stock, " stuks"] })] }) }, i.id))) })] }), _jsxs(Button, { type: "button", disabled: !selected || mutation.isPending, onClick: () => selected && mutation.mutate(selected), children: [_jsx(Send, { className: "mr-2 h-4 w-4" }), "Testpush uitvoeren"] }), _jsxs(Button, { type: "button", variant: "outline", disabled: jobs.isFetching, onClick: () => void jobs.refetch(), children: [_jsx(RefreshCw, { className: `mr-2 h-4 w-4 ${jobs.isFetching ? "animate-spin" : ""}` }), "Ververs"] })] }), _jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full text-left text-xs", children: [_jsx("thead", { className: "text-muted-foreground", children: _jsxs("tr", { children: [_jsx("th", { className: "py-1 pr-3 font-medium", children: "Artikel" }), _jsx("th", { className: "py-1 pr-3 font-medium", children: "Geldt voor" }), _jsx("th", { className: "py-1 pr-3 font-medium", children: "Stuks" }), _jsx("th", { className: "py-1 pr-3 font-medium", children: "Status" }), _jsx("th", { className: "py-1 pr-3 font-medium", children: "Pogingen" }), _jsx("th", { className: "py-1 pr-3 font-medium", children: "Aangemaakt" }), _jsx("th", { className: "py-1 pr-3 font-medium", children: "Bijgewerkt" }), _jsx("th", { className: "py-1 font-medium", children: "Melding" })] }) }), _jsxs("tbody", { children: [rows.length === 0 && (_jsx("tr", { children: _jsx("td", { colSpan: 8, className: "py-3 text-muted-foreground", children: jobs.isError
241
+ ? `Pushes konden niet worden geladen${jobsError?.code ? ` (fout ${jobsError.code})` : ""}: ${jobsError?.message ?? "onbekende fout"}`
242
+ : "Er staan nog geen push-opdrachten in de wachtrij." }) })), rows.map((r) => {
237
243
  const status = displayStatus(r);
238
244
  const detail = explainError(r.lastError);
239
245
  const scope = scopeLabel(r);
@@ -1 +1 @@
1
- {"version":3,"file":"VoorraadPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadPage.tsx"],"names":[],"mappings":"AAwGA,wBAAgB,YAAY,gCA8V3B"}
1
+ {"version":3,"file":"VoorraadPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadPage.tsx"],"names":[],"mappings":"AAyGA,wBAAgB,YAAY,gCA+V3B"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useMemo, useState } from "react";
3
3
  import { Link } from "@tanstack/react-router";
4
- import { Button, Card, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@flowselections/core";
4
+ import { Button, Card, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, useAuthState, } from "@flowselections/core";
5
5
  import { AlertTriangle, CheckCircle2, FileText, PackageX, Plus, Search, } from "lucide-react";
6
6
  import { useFloridayConnection } from "../../hooks/useFloridayConnection";
7
7
  import { supabase } from "../../integrations/supabase/client";
@@ -41,6 +41,7 @@ function availableOf(i) {
41
41
  return i.availableQuantity ?? i.quantity ?? 0;
42
42
  }
43
43
  export function VoorraadPage() {
44
+ const auth = useAuthState();
44
45
  const { activeId, error: connError } = useFloridayConnection();
45
46
  const { items, loading, error, reload } = useVoorraadData(activeId);
46
47
  const { byKey: settingsByKey, reload: reloadSettings } = useInventorySettings(activeId);
@@ -145,7 +146,7 @@ export function VoorraadPage() {
145
146
  { id: "out_of_stock", label: "Niet op voorraad", count: counts.outOfStock },
146
147
  { id: "below_min", label: "Onder minimum", count: counts.belowMin },
147
148
  ];
148
- return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsxs("header", { className: "flex flex-wrap items-start justify-between gap-4", children: [_jsxs("div", { className: "min-w-[12rem] flex-1", children: [_jsx("h1", { className: "text-2xl font-bold tracking-tight", children: "Voorraad" }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Beheer je artikelen en voorraadniveaus." })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsx(Link, { to: "/voorraad/validatie", children: "Validatie" }) }), _jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsx(Link, { to: "/voorraad/publicaties", children: "Publicaties" }) }), _jsx(Button, { asChild: true, disabled: !activeId, size: "sm", children: _jsxs(Link, { to: "/voorraad/nieuw", children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Artikel toevoegen"] }) })] })] }), _jsx(StockIntegrityAlert, {}), _jsx(SupplyReadScopeAlert, {}), _jsx(StockSyncStatus, {}), noPriceIds.size > 0 && (_jsxs("div", { className: "flex flex-wrap items-start gap-2 rounded-md border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-sm text-amber-800", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsxs("div", { className: "flex-1 min-w-[12rem]", children: [_jsxs("span", { className: "font-medium", children: [noPriceIds.size, " artikel", noPriceIds.size === 1 ? "" : "en", " staan niet in Floriday omdat de prijs ontbreekt."] }), " ", "Kies \"Toon deze artikelen\", open een artikel en vul bij Artikel regulier de verkoopprijs per stuk (excl. btw) in. De voorraadsturing van elk artikel heeft ook een link \"Prijs invullen\"."] }), _jsx(Button, { variant: "outline", size: "sm", onClick: () => setNoPriceOnly((v) => !v), children: noPriceOnly ? "Alle artikelen tonen" : "Toon deze artikelen" })] })), _jsx(StockPushMonitor, { connectionId: activeId ?? null }), connError && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 text-destructive p-3 text-sm", children: ["Floriday-connectie laden mislukt: ", connError] })), error && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 text-destructive p-3 text-sm", children: ["Voorraad laden mislukt: ", error] })), _jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4", children: [_jsx(KpiCard, { icon: _jsx(FileText, { className: "h-4 w-4" }), tone: "primary", label: "Artikelen", count: counts.total, active: stockFilter === "all", onClick: () => toggle("all") }), _jsx(KpiCard, { icon: _jsx(CheckCircle2, { className: "h-4 w-4" }), tone: "emerald", label: "Op voorraad", count: counts.inStock, active: stockFilter === "in_stock", onClick: () => toggle("in_stock") }), _jsx(KpiCard, { icon: _jsx(PackageX, { className: "h-4 w-4" }), tone: "rose", label: "Niet op voorraad", count: counts.outOfStock, active: stockFilter === "out_of_stock", onClick: () => toggle("out_of_stock") }), _jsx(KpiCard, { icon: _jsx(AlertTriangle, { className: "h-4 w-4" }), tone: "amber", label: "Onder minimum", count: counts.belowMin, active: stockFilter === "below_min", onClick: () => toggle("below_min") })] }), _jsxs(Card, { className: "overflow-hidden p-0", children: [_jsx("div", { className: "flex flex-wrap items-center gap-1 border-b px-4", children: TABS.map((t) => {
149
+ return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsxs("header", { className: "flex flex-wrap items-start justify-between gap-4", children: [_jsxs("div", { className: "min-w-[12rem] flex-1", children: [_jsx("h1", { className: "text-2xl font-bold tracking-tight", children: "Voorraad" }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Beheer je artikelen en voorraadniveaus." })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsx(Link, { to: "/voorraad/validatie", children: "Validatie" }) }), _jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsx(Link, { to: "/voorraad/publicaties", children: "Publicaties" }) }), _jsx(Button, { asChild: true, disabled: !activeId, size: "sm", children: _jsxs(Link, { to: "/voorraad/nieuw", children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Artikel toevoegen"] }) })] })] }), _jsx(StockIntegrityAlert, {}), _jsx(SupplyReadScopeAlert, {}), _jsx(StockSyncStatus, {}), noPriceIds.size > 0 && (_jsxs("div", { className: "flex flex-wrap items-start gap-2 rounded-md border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-sm text-amber-800", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsxs("div", { className: "flex-1 min-w-[12rem]", children: [_jsxs("span", { className: "font-medium", children: [noPriceIds.size, " artikel", noPriceIds.size === 1 ? "" : "en", " staan niet in Floriday omdat de prijs ontbreekt."] }), " ", "Kies \"Toon deze artikelen\", open een artikel en vul bij Artikel regulier de verkoopprijs per stuk (excl. btw) in. De voorraadsturing van elk artikel heeft ook een link \"Prijs invullen\"."] }), _jsx(Button, { variant: "outline", size: "sm", onClick: () => setNoPriceOnly((v) => !v), children: noPriceOnly ? "Alle artikelen tonen" : "Toon deze artikelen" })] })), auth.isAdmin && _jsx(StockPushMonitor, { connectionId: activeId ?? null }), connError && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 text-destructive p-3 text-sm", children: ["Floriday-connectie laden mislukt: ", connError] })), error && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 text-destructive p-3 text-sm", children: ["Voorraad laden mislukt: ", error] })), _jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4", children: [_jsx(KpiCard, { icon: _jsx(FileText, { className: "h-4 w-4" }), tone: "primary", label: "Artikelen", count: counts.total, active: stockFilter === "all", onClick: () => toggle("all") }), _jsx(KpiCard, { icon: _jsx(CheckCircle2, { className: "h-4 w-4" }), tone: "emerald", label: "Op voorraad", count: counts.inStock, active: stockFilter === "in_stock", onClick: () => toggle("in_stock") }), _jsx(KpiCard, { icon: _jsx(PackageX, { className: "h-4 w-4" }), tone: "rose", label: "Niet op voorraad", count: counts.outOfStock, active: stockFilter === "out_of_stock", onClick: () => toggle("out_of_stock") }), _jsx(KpiCard, { icon: _jsx(AlertTriangle, { className: "h-4 w-4" }), tone: "amber", label: "Onder minimum", count: counts.belowMin, active: stockFilter === "below_min", onClick: () => toggle("below_min") })] }), _jsxs(Card, { className: "overflow-hidden p-0", children: [_jsx("div", { className: "flex flex-wrap items-center gap-1 border-b px-4", children: TABS.map((t) => {
149
150
  const active = stockFilter === t.id;
150
151
  return (_jsxs("button", { type: "button", onClick: () => setStockFilter(t.id), className: `relative py-3 px-3 text-sm font-medium whitespace-nowrap transition-colors ${active
151
152
  ? "text-foreground"
@@ -1 +1 @@
1
- {"version":3,"file":"VoorraadStockCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadStockCard.tsx"],"names":[],"mappings":"AAeA,UAAU,KAAK;IACb,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,wBAAgB,iBAAiB,CAAC,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,KAAK,sCA+LzF"}
1
+ {"version":3,"file":"VoorraadStockCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadStockCard.tsx"],"names":[],"mappings":"AAeA,UAAU,KAAK;IACb,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,wBAAgB,iBAAiB,CAAC,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,KAAK,sCAoMzF"}
@@ -105,5 +105,5 @@ export function VoorraadStockCard({ connectionId, tradeItemId, draftId, productI
105
105
  setTouched(true);
106
106
  setValues((v) => ({ ...v, [id]: Number(e.target.value) || 0 }));
107
107
  } })] }));
108
- return (_jsxs("div", { className: "space-y-4", children: [_jsxs(Card, { children: [_jsxs("div", { className: "grid grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-3 px-6 py-4", children: [_jsx("span", { className: "flex h-7 w-7 items-center justify-center rounded-full border border-primary/40 bg-primary/10 text-primary", children: _jsx(Package, { className: "h-4 w-4" }) }), _jsx("span", { className: "font-medium", children: "Voorraad" }), _jsxs("span", { className: "text-xs text-muted-foreground", children: ["Beschikbaar: ", _jsx("span", { className: "tabular-nums font-medium text-foreground", children: available })] })] }), _jsx(CardContent, { className: "pt-0 space-y-4", children: loading ? (_jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), " Voorraad laden\u2026"] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "grid grid-cols-2 gap-4 sm:grid-cols-4", children: [numField("physical_stock", "Liggend"), numField("minimum_stock", "Minimum"), numField("reserved_stock", "Gereserveerd"), numField("incoming_stock", "Inkomend")] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "stock-notes", children: "Notitie (optioneel)" }), _jsx(Textarea, { id: "stock-notes", rows: 2, value: values.notes ?? "", onChange: (e) => setValues((v) => ({ ...v, notes: e.target.value })), placeholder: "Bijv. locatie of opmerking" })] }), _jsx("div", { className: "flex justify-end", children: _jsxs(Button, { onClick: handleSave, disabled: saving, children: [saving ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(Check, { className: "mr-2 h-4 w-4" })), "Voorraad opslaan"] }) })] })) })] }), _jsx(FloridaySentCard, { connectionId: connectionId, productId: productId, tradeItemId: tradeItemId, productType: isGroupHead ? "GROUP" : hasBom ? "SET" : null })] }));
108
+ return (_jsxs("div", { className: "space-y-4", children: [_jsxs(Card, { children: [_jsxs("div", { className: "grid grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-3 px-6 py-4", children: [_jsx("span", { className: "flex h-7 w-7 items-center justify-center rounded-full border border-primary/40 bg-primary/10 text-primary", children: _jsx(Package, { className: "h-4 w-4" }) }), _jsx("span", { className: "font-medium", children: "Voorraad" }), _jsxs("span", { className: "text-xs text-muted-foreground", children: ["Beschikbaar: ", _jsx("span", { className: "tabular-nums font-medium text-foreground", children: available })] })] }), _jsx(CardContent, { className: "pt-0 space-y-4", children: loading ? (_jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), " Voorraad laden\u2026"] })) : (_jsxs(_Fragment, { children: [Number(values.reserved_stock) > Number(values.physical_stock) && (_jsxs("div", { className: "rounded-md border border-amber-300 bg-amber-50 px-3 py-2 text-xs text-amber-800", children: ["Let op: er is meer gereserveerd (", Number(values.reserved_stock), ") dan er ligt (", Number(values.physical_stock), "). Controleer de open orders van dit artikel."] })), _jsxs("div", { className: "grid grid-cols-2 gap-4 sm:grid-cols-4", children: [numField("physical_stock", "Liggend"), numField("minimum_stock", "Minimum"), numField("reserved_stock", "Gereserveerd"), numField("incoming_stock", "Inkomend")] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "stock-notes", children: "Notitie (optioneel)" }), _jsx(Textarea, { id: "stock-notes", rows: 2, value: values.notes ?? "", onChange: (e) => setValues((v) => ({ ...v, notes: e.target.value })), placeholder: "Bijv. locatie of opmerking" })] }), _jsx("div", { className: "flex justify-end", children: _jsxs(Button, { onClick: handleSave, disabled: saving, children: [saving ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(Check, { className: "mr-2 h-4 w-4" })), "Voorraad opslaan"] }) })] })) })] }), _jsx(FloridaySentCard, { connectionId: connectionId, productId: productId, tradeItemId: tradeItemId, productType: isGroupHead ? "GROUP" : hasBom ? "SET" : null })] }));
109
109
  }
@@ -1 +1 @@
1
- {"version":3,"file":"VoorraadTable.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadTable.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAI3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAkBzE,oFAAoF;AACpF,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CAE3D;AAED,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,OAAO,EACP,aAAa,EACb,QAAQ,EACR,cAAc,EACd,WAAW,EACX,cAAc,GACf,EAAE;IACD,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC9C,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC7C,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C,+BA0OA"}
1
+ {"version":3,"file":"VoorraadTable.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadTable.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAI3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAkBzE,oFAAoF;AACpF,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CAE3D;AAED,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,OAAO,EACP,aAAa,EACb,QAAQ,EACR,cAAc,EACd,WAAW,EACX,cAAc,GACf,EAAE;IACD,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC9C,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC7C,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C,+BAoPA"}
@@ -39,6 +39,7 @@ export function VoorraadTable({ items, loading, settingsByKey, selected, onToggl
39
39
  return (_jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-muted/50 text-left", children: _jsxs("tr", { children: [onToggleSelect && (_jsx("th", { className: "p-3 w-10", children: _jsx(Checkbox, { checked: allSelected, onCheckedChange: () => onToggleAll?.(selectableIds), "aria-label": "Alles selecteren" }) })), _jsx("th", { className: "p-3 w-14" }), _jsx("th", { className: "p-3", children: "Product" }), _jsx("th", { className: "p-3", children: "Floriday-sturing" }), _jsx("th", { className: "p-3", children: "Type" }), _jsx("th", { className: "p-3", children: "Artikelnummer" }), columns.map((c) => (_jsx("th", { className: "p-3 text-right whitespace-nowrap", children: c.label }, c.key))), extraColumns.map((f) => (_jsx("th", { className: "p-3 whitespace-nowrap", children: f.field_label }, f.id)))] }) }), _jsx("tbody", { children: items.map((item) => {
40
40
  const low = item.minQuantity > 0 &&
41
41
  item.availableQuantity < item.minQuantity;
42
+ const overReserved = Number(item.reservedQuantity ?? 0) > Number(item.quantity ?? 0);
42
43
  const linkId = item.productId ?? item.localId ?? item.id;
43
44
  const clickable = Boolean(linkId);
44
45
  const open = () => {
@@ -61,7 +62,7 @@ export function VoorraadTable({ items, loading, settingsByKey, selected, onToggl
61
62
  : "Niet online in Floriday", title: floriday
62
63
  ? "Online in Floriday"
63
64
  : "Niet online in Floriday", className: "inline-flex items-center gap-1.5 rounded-full border border-border bg-background px-2 py-0.5 text-[11px] font-medium leading-none text-muted-foreground", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full " +
64
- (floriday ? "bg-emerald-500" : "bg-red-500"), "aria-hidden": true }), _jsx("span", { children: "Floriday" })] })] }), Boolean(customValue(item, "floriday_sync")?.dirty) && (_jsx("div", { className: "mt-1 inline-flex items-center rounded-full bg-amber-100 px-2 py-0.5 text-[11px] font-medium leading-none text-amber-800", title: "Opgeslagen in dit systeem, wacht nog op doorgifte naar Floriday", children: "Wacht op Floriday" })), low && (_jsx("div", { className: "text-xs text-destructive", children: "onder minimumvoorraad" }))] }), _jsx("td", { className: "p-3", onClick: (e) => e.stopPropagation(), children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(InventoryStatusBadge, { setting: setting }), settingProductId && onOpenSettings && (_jsx(Button, { variant: "ghost", size: "sm", className: "h-7 w-7 p-0", onClick: () => onOpenSettings(settingProductId), "aria-label": `Voorraadsturing van ${item.name}`, children: _jsx(Settings2, { className: "h-4 w-4" }) }))] }) }), _jsx("td", { className: "p-3", children: (() => {
65
+ (floriday ? "bg-emerald-500" : "bg-red-500"), "aria-hidden": true }), _jsx("span", { children: "Floriday" })] })] }), Boolean(customValue(item, "floriday_sync")?.dirty) && (_jsx("div", { className: "mt-1 inline-flex items-center rounded-full bg-amber-100 px-2 py-0.5 text-[11px] font-medium leading-none text-amber-800", title: "Opgeslagen in dit systeem, wacht nog op doorgifte naar Floriday", children: "Wacht op Floriday" })), low && (_jsx("div", { className: "text-xs text-destructive", children: "onder minimumvoorraad" })), overReserved && (_jsx("div", { className: "text-xs text-amber-700", title: "Er is meer gereserveerd voor orders dan er ligt. Controleer open orders.", children: "gereserveerd groter dan liggend" }))] }), _jsx("td", { className: "p-3", onClick: (e) => e.stopPropagation(), children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(InventoryStatusBadge, { setting: setting }), settingProductId && onOpenSettings && (_jsx(Button, { variant: "ghost", size: "sm", className: "h-7 w-7 p-0", onClick: () => onOpenSettings(settingProductId), "aria-label": `Voorraadsturing van ${item.name}`, children: _jsx(Settings2, { className: "h-4 w-4" }) }))] }) }), _jsx("td", { className: "p-3", children: (() => {
65
66
  const t = toStructureType(setting?.product_type, item.itemType);
66
67
  return (_jsxs("div", { className: "flex flex-col items-start gap-1", children: [_jsx(Badge, { variant: t === "NORMAL" ? "secondary" : undefined, className: PRODUCT_TYPE_CLASSES[t], title: PRODUCT_TYPE_HELP[t], children: PRODUCT_TYPE_LABELS[t] }), setting?.action_reason === "GROUP_MEMBER_OFF" && (_jsx("span", { className: "text-[11px] text-muted-foreground", title: "Dit artikel is lid van een actieve productgroep; alleen de groep staat in Floriday.", children: "Lid van groep \u00B7 niet los in Floriday" })), setting?.action_reason === "SET_AWAITS_LOGISTICS" && (item.floridayId ? (_jsx("span", { className: "text-[11px] text-amber-700", title: "Set: dit artikel staat in Floriday. De voorraad (berekend uit de onderdelen) gaat pas naar Floriday na akkoord, als de Logistieke engine onderdelen reserveert.", children: "In Floriday \u00B7 voorraad nog niet doorgezet (set)" })) : (_jsx("span", { className: "text-[11px] text-amber-700", title: "Berekend uit de onderdelen; nog niet gekoppeld aan een Floriday-artikel.", children: "Berekend \u00B7 nog niet gekoppeld aan Floriday" })))] }));
67
68
  })() }), _jsx("td", { className: "p-3 font-mono text-xs", children: item.articleNumber || "—" }), columns.map((c) => (_jsx("td", { className: "p-3 text-right tabular-nums text-muted-foreground", children: Number(item[c.key] ?? 0) }, c.key))), extraColumns.map((f) => (_jsx("td", { className: "p-3 text-muted-foreground whitespace-nowrap", children: formatCustomValue(customValue(item, f.field_key)) }, f.id)))] }, item.id));