@flowselections/floriday-voorraad 1.0.22 → 1.0.23

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 (33) hide show
  1. package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
  2. package/dist-lib/components/voorraad/ArtikelWizard.js +31 -1
  3. package/dist-lib/components/voorraad/FloridayInfoCard.d.ts +12 -0
  4. package/dist-lib/components/voorraad/FloridayInfoCard.d.ts.map +1 -0
  5. package/dist-lib/components/voorraad/FloridayInfoCard.js +85 -0
  6. package/dist-lib/components/voorraad/VoorraadTable.d.ts.map +1 -1
  7. package/dist-lib/components/voorraad/VoorraadTable.js +15 -1
  8. package/dist-lib/hooks/useVoorraadData.d.ts.map +1 -1
  9. package/dist-lib/hooks/useVoorraadData.js +36 -5
  10. package/dist-lib/integrations/supabase/auth-middleware.d.ts +26 -0
  11. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  12. package/dist-lib/integrations/supabase/client.d.ts +26 -0
  13. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  14. package/dist-lib/integrations/supabase/client.server.d.ts +26 -0
  15. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  16. package/dist-lib/integrations/supabase/types.d.ts +26 -0
  17. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  18. package/dist-lib/lib/floricode-required-features.functions.d.ts +26 -0
  19. package/dist-lib/lib/floricode-required-features.functions.d.ts.map +1 -1
  20. package/dist-lib/lib/floriday-client.d.ts +4 -0
  21. package/dist-lib/lib/floriday-client.d.ts.map +1 -1
  22. package/dist-lib/lib/floriday-customer-offer.functions.d.ts +26 -0
  23. package/dist-lib/lib/floriday-customer-offer.functions.d.ts.map +1 -1
  24. package/dist-lib/lib/floriday-gateway.functions.d.ts +130 -0
  25. package/dist-lib/lib/floriday-gateway.functions.d.ts.map +1 -1
  26. package/dist-lib/lib/floriday-info.d.ts +54 -0
  27. package/dist-lib/lib/floriday-info.d.ts.map +1 -0
  28. package/dist-lib/lib/floriday-info.js +114 -0
  29. package/dist-lib/lib/floriday-warehouse.functions.d.ts +78 -0
  30. package/dist-lib/lib/floriday-warehouse.functions.d.ts.map +1 -1
  31. package/dist-lib/lib/floriday-writes.functions.d.ts +52 -0
  32. package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
  33. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ArtikelWizard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ArtikelWizard.tsx"],"names":[],"mappings":"AAoJA,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EAAE,cAAc,EACvB,WAAW,EACX,SAAS,GACV,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,2CA8lCrE"}
1
+ {"version":3,"file":"ArtikelWizard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ArtikelWizard.tsx"],"names":[],"mappings":"AAqJA,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EAAE,cAAc,EACvB,WAAW,EACX,SAAS,GACV,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,2CAkrCrE"}
@@ -22,6 +22,7 @@ import { ProductLinkCard } from "../../components/voorraad/ProductLinkCard";
22
22
  import { PriceStatusBadge } from "../../components/voorraad/PriceStatusBadge";
23
23
  import { OrderHistoryCard } from "../../components/voorraad/OrderHistoryCard";
24
24
  import { ArtikelCompositionCard } from "../../components/voorraad/ArtikelCompositionCard";
25
+ import { FloridayInfoCard } from "../../components/voorraad/FloridayInfoCard";
25
26
  import { stockKeyFor } from "../../hooks/useTradeItemStock";
26
27
  const empty = emptyWizardData;
27
28
  const LOAD_CARRIER_LABELS = {
@@ -85,6 +86,9 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
85
86
  const [productLoading, setProductLoading] = useState(Boolean(productId));
86
87
  const [syncState, setSyncState] = useState("idle");
87
88
  const [syncError, setSyncError] = useState(null);
89
+ const [productMeta, setProductMeta] = useState({ floridayActive: false, floridayTradeItemId: null, physicalStock: 0 });
90
+ const [deleteAck, setDeleteAck] = useState(false);
91
+ const [deleteOpen, setDeleteOpen] = useState(false);
88
92
  const isEditingDraft = Boolean(initialDraftId);
89
93
  const isEditingPublished = Boolean(tradeItemId);
90
94
  const isEditingProduct = Boolean(productId);
@@ -219,6 +223,24 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
219
223
  });
220
224
  setDraftId(product.id);
221
225
  setDraftFloridayId((product.floriday_trade_item_id ?? null));
226
+ const fid = (product.floriday_trade_item_id ?? null);
227
+ const fActive = Boolean(product.floriday_active);
228
+ // Voorraad ophalen voor deze product-key
229
+ let physical = 0;
230
+ try {
231
+ const { data: stockRow } = await supabase
232
+ .from("trade_item_stock")
233
+ .select("physical_stock")
234
+ .eq("trade_item_key", `product:${product.id}`)
235
+ .maybeSingle();
236
+ physical = Number(stockRow?.physical_stock ?? 0);
237
+ }
238
+ catch { /* stock niet gevonden = 0 */ }
239
+ setProductMeta({
240
+ floridayActive: fActive,
241
+ floridayTradeItemId: fid,
242
+ physicalStock: physical,
243
+ });
222
244
  }
223
245
  catch (e) {
224
246
  if (!cancelled)
@@ -808,7 +830,15 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
808
830
  if (!activeConnection) {
809
831
  return (_jsx("main", { className: "p-6", children: _jsx(Card, { className: "p-6", children: "Geen actieve Floriday-connectie gevonden. Voeg er eerst een toe in Instellingen." }) }));
810
832
  }
811
- return (_jsxs("main", { className: "mx-auto w-full max-w-3xl space-y-6 p-6", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Button, { variant: "ghost", size: "sm", asChild: true, children: _jsxs(Link, { to: "/voorraad", children: [_jsx(ArrowLeft, { className: "mr-2 h-4 w-4" }), "Terug naar voorraad"] }) }), !isEditingPublished && (_jsxs(Button, { variant: "outline", size: "sm", className: "ml-auto", onClick: saveDraft, disabled: submitting || (!draftId && !vbnValid), children: [submitting ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(ClipboardList, { className: "mr-2 h-4 w-4" })), "Product opslaan"] })), (isEditingDraft || isEditingProduct) && (_jsxs(AlertDialog, { 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, { children: "Dit product wordt permanent uit de voorraad verwijderd. Als het product actief op Floriday staat moet je het daar apart deactiveren." })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Annuleren" }), _jsx(AlertDialogAction, { onClick: handleDeleteDraft, children: "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 }))] })), 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: 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: 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(ArtikelCompositionCard, { productId: linkedProductId, floridayId: tradeItemId ?? draftFloridayId ?? null }), _jsx(VoorraadStockCard, { connectionId: activeId, tradeItemId: tradeItemId ?? null, draftId: draftId ?? null, productId: linkedProductId }), _jsx(OrderHistoryCard, { tradeItemKey: localTradeItemKey, productName: data.name || data.vbnName || null, productId: linkedProductId ?? null }), _jsx(ProductLinkCard, { tradeItemKey: localTradeItemKey, productName: data.vbnName || null })] })), _jsx("ol", { className: "space-y-3", children: steps.map((s, i) => {
833
+ return (_jsxs("main", { className: "mx-auto w-full max-w-3xl space-y-6 p-6", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Button, { variant: "ghost", size: "sm", asChild: true, children: _jsxs(Link, { to: "/voorraad", children: [_jsx(ArrowLeft, { className: "mr-2 h-4 w-4" }), "Terug naar voorraad"] }) }), !isEditingPublished && (_jsxs(Button, { variant: "outline", size: "sm", className: "ml-auto", onClick: saveDraft, disabled: submitting || (!draftId && !vbnValid), children: [submitting ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(ClipboardList, { className: "mr-2 h-4 w-4" })), "Product opslaan"] })), (isEditingDraft || isEditingProduct) && (_jsxs(AlertDialog, { open: deleteOpen, onOpenChange: (o) => {
834
+ setDeleteOpen(o);
835
+ if (!o)
836
+ setDeleteAck(false);
837
+ }, 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 }))] })), 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: 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: 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(ArtikelCompositionCard, { productId: linkedProductId, floridayId: tradeItemId ?? draftFloridayId ?? null }), _jsx(FloridayInfoCard, { floridayTradeItemId: tradeItemId ?? draftFloridayId ?? null, local: {
838
+ name: data.name || data.vbnName,
839
+ articleNumber: data.articleCode,
840
+ gtin: data.gtin,
841
+ } }), _jsx(VoorraadStockCard, { connectionId: activeId, tradeItemId: tradeItemId ?? null, draftId: draftId ?? null, productId: linkedProductId }), _jsx(OrderHistoryCard, { tradeItemKey: localTradeItemKey, productName: data.name || data.vbnName || null, productId: linkedProductId ?? null }), _jsx(ProductLinkCard, { tradeItemKey: localTradeItemKey, productName: data.vbnName || null })] })), _jsx("ol", { className: "space-y-3", children: steps.map((s, i) => {
812
842
  const isOpen = i === step;
813
843
  const isValid = stepValidity[i];
814
844
  const isVisited = i < step || isEditing;
@@ -0,0 +1,12 @@
1
+ interface Props {
2
+ floridayTradeItemId: string | null;
3
+ /** Lokale productwaarden om ‘lokaal wijkt af van Floriday’-hints te tonen. */
4
+ local?: {
5
+ name?: string | null;
6
+ articleNumber?: string | null;
7
+ gtin?: string | null;
8
+ };
9
+ }
10
+ export declare function FloridayInfoCard({ floridayTradeItemId, local }: Props): import("react/jsx-runtime").JSX.Element | null;
11
+ export {};
12
+ //# sourceMappingURL=FloridayInfoCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FloridayInfoCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FloridayInfoCard.tsx"],"names":[],"mappings":"AAYA,UAAU,KAAK;IACb,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,8EAA8E;IAC9E,KAAK,CAAC,EAAE;QACN,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACtB,CAAC;CACH;AA6DD,wBAAgB,gBAAgB,CAAC,EAAE,mBAAmB,EAAE,KAAK,EAAE,EAAE,KAAK,kDAqRrE"}
@@ -0,0 +1,85 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { Card, CardContent, Badge } from "@flowselections/core";
4
+ import { AlertTriangle, ChevronDown, ChevronRight, ExternalLink } from "lucide-react";
5
+ import { supabase } from "../../integrations/supabase/client";
6
+ import { floridayInfoFromCache, LOAD_CARRIER_LABELS, } from "../../lib/floriday-info";
7
+ const floridayLogo = "/flowselections-assets/Voorraad/floriday-logo.png";
8
+ function formatDate(iso) {
9
+ if (!iso)
10
+ return "—";
11
+ try {
12
+ return new Date(iso).toLocaleString("nl-NL", {
13
+ dateStyle: "medium",
14
+ timeStyle: "short",
15
+ });
16
+ }
17
+ catch {
18
+ return iso;
19
+ }
20
+ }
21
+ function DiffField({ label, floridayValue, localValue, }) {
22
+ const fv = floridayValue == null || floridayValue === "" ? "—" : String(floridayValue);
23
+ const lv = (localValue ?? "").trim();
24
+ const differs = lv.length > 0 && fv !== "—" && lv !== fv;
25
+ return (_jsxs("div", { className: "grid grid-cols-[140px_1fr] gap-2 text-sm py-1", children: [_jsx("div", { className: "text-muted-foreground", children: label }), _jsxs("div", { children: [_jsx("div", { className: "font-medium", children: fv }), differs && (_jsxs("div", { className: "mt-0.5 inline-flex items-center gap-1 text-xs text-amber-700", title: "De lokale waarde wijkt af van Floriday. Puur informatief \u2014 er wordt niets automatisch overschreven.", children: [_jsx(AlertTriangle, { className: "h-3 w-3" }), "lokaal: ", _jsx("span", { className: "font-mono", children: lv })] }))] })] }));
26
+ }
27
+ function Field({ label, value, }) {
28
+ const v = value == null || value === "" ? "—" : String(value);
29
+ return (_jsxs("div", { className: "grid grid-cols-[140px_1fr] gap-2 text-sm py-1", children: [_jsx("div", { className: "text-muted-foreground", children: label }), _jsx("div", { className: "font-medium break-words", children: v })] }));
30
+ }
31
+ export function FloridayInfoCard({ floridayTradeItemId, local }) {
32
+ const [info, setInfo] = useState(null);
33
+ const [loading, setLoading] = useState(false);
34
+ const [notFound, setNotFound] = useState(false);
35
+ const [rawOpen, setRawOpen] = useState(false);
36
+ const [activePhoto, setActivePhoto] = useState(0);
37
+ useEffect(() => {
38
+ if (!floridayTradeItemId) {
39
+ setInfo(null);
40
+ setNotFound(false);
41
+ return;
42
+ }
43
+ let cancelled = false;
44
+ setLoading(true);
45
+ setNotFound(false);
46
+ (async () => {
47
+ try {
48
+ const { data, error } = await supabase
49
+ .from("floriday_trade_items_cache")
50
+ .select("data,floriday_id")
51
+ .eq("floriday_id", floridayTradeItemId)
52
+ .maybeSingle();
53
+ if (cancelled)
54
+ return;
55
+ if (error || !data) {
56
+ setInfo(null);
57
+ setNotFound(true);
58
+ return;
59
+ }
60
+ const mapped = floridayInfoFromCache({
61
+ ...data.data,
62
+ id: data.floriday_id,
63
+ });
64
+ setInfo(mapped);
65
+ setActivePhoto(0);
66
+ }
67
+ finally {
68
+ if (!cancelled)
69
+ setLoading(false);
70
+ }
71
+ })();
72
+ return () => {
73
+ cancelled = true;
74
+ };
75
+ }, [floridayTradeItemId]);
76
+ if (!floridayTradeItemId)
77
+ return null;
78
+ return (_jsx(Card, { className: "overflow-hidden", children: _jsxs(CardContent, { className: "p-4 space-y-4", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("img", { src: floridayLogo, alt: "Floriday", className: "h-6 w-6 shrink-0" }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "font-semibold", children: "Floriday-informatie" }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Rechtstreeks uit Floriday geladen \u2014 controle-informatie, wordt niet lokaal overschreven." })] }), info?.tradeItemVersion != null && (_jsxs(Badge, { variant: "secondary", className: "text-xs", children: ["v", info.tradeItemVersion] }))] }), loading && (_jsx("div", { className: "text-sm text-muted-foreground", children: "Floriday-info laden\u2026" })), !loading && notFound && (_jsxs("div", { className: "rounded-md border border-amber-300 bg-amber-50 p-3 text-sm text-amber-900", children: ["Geen Floriday-record gevonden voor id", " ", _jsx("code", { className: "font-mono", children: floridayTradeItemId }), ". Wacht op de volgende sync of controleer de connectie."] })), info && (_jsxs(_Fragment, { children: [info.photos.length > 0 && (_jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "rounded-md border overflow-hidden bg-muted/30", children: _jsx("img", { src: info.photos[activePhoto]?.url ?? info.photos[0].url, alt: info.name, className: "w-full max-h-80 object-contain" }) }), info.photos.length > 1 && (_jsx("div", { className: "flex gap-2 flex-wrap", children: info.photos.map((p, i) => (_jsx("button", { type: "button", onClick: () => setActivePhoto(i), className: `h-14 w-14 rounded border overflow-hidden ${i === activePhoto
79
+ ? "ring-2 ring-primary"
80
+ : "opacity-70 hover:opacity-100"}`, "aria-label": `Foto ${i + 1}`, children: _jsx("img", { src: p.url, alt: "", className: "h-full w-full object-cover" }) }, p.url))) }))] })), _jsxs("div", { className: "grid gap-x-8 gap-y-1 md:grid-cols-2", children: [_jsxs("div", { children: [_jsx(DiffField, { label: "Naam", floridayValue: info.name, localValue: local?.name ?? null }), _jsx(DiffField, { label: "Artikelnr", floridayValue: info.supplierArticleCode, localValue: local?.articleNumber ?? null }), _jsx(DiffField, { label: "GTIN", floridayValue: info.articleGtin, localValue: local?.gtin ?? null }), _jsx(Field, { label: "VBN-code", value: info.vbnProductCode }), _jsx(Field, { label: "Referentie", value: info.supplierReference })] }), _jsxs("div", { children: [_jsx(Field, { label: "Botanische naam", value: info.botanicalNames.join(", ") || null }), _jsx(Field, { label: "Land van herkomst", value: info.countryOfOrigin.join(", ") || null }), _jsx(Field, { label: "Seizoensperiode", value: info.seasonalPeriods.length > 0
81
+ ? info.seasonalPeriods
82
+ .map((s) => `wk ${s.startWeek}–${s.endWeek}`)
83
+ .join(", ")
84
+ : null }), _jsx(Field, { label: "Verborgen in catalogus", value: info.isHiddenInCatalog ? "Ja" : "Nee" }), _jsx(Field, { label: "Laatst gewijzigd", value: formatDate(info.lastModifiedDateTime) })] })] }), info.characteristics.length > 0 && (_jsxs("div", { children: [_jsx("div", { className: "text-xs uppercase tracking-wide text-muted-foreground mb-2", children: "Kenmerken" }), _jsx("div", { className: "rounded-md border overflow-hidden", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-muted/50 text-left", children: _jsxs("tr", { children: [_jsx("th", { className: "p-2 font-medium", children: "VBN-code" }), _jsx("th", { className: "p-2 font-medium", children: "Waarde" })] }) }), _jsx("tbody", { children: info.characteristics.map((c) => (_jsxs("tr", { className: "border-t", children: [_jsx("td", { className: "p-2 font-mono text-xs", children: c.vbnCode }), _jsx("td", { className: "p-2 font-mono text-xs", children: c.vbnValueCode })] }, c.vbnCode))) })] }) })] })), info.packingConfigurations.length > 0 && (_jsxs("div", { children: [_jsx("div", { className: "text-xs uppercase tracking-wide text-muted-foreground mb-2", children: "Beladingen (Floriday)" }), _jsx("div", { className: "rounded-md border overflow-x-auto", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-muted/50 text-left", children: _jsxs("tr", { children: [_jsx("th", { className: "p-2 font-medium", children: "Ladingdrager" }), _jsx("th", { className: "p-2 font-medium", children: "Verpakking" }), _jsx("th", { className: "p-2 text-right font-medium", children: "Stuks/verp." }), _jsx("th", { className: "p-2 text-right font-medium", children: "Verp./laag" }), _jsx("th", { className: "p-2 text-right font-medium", children: "Lagen" }), _jsx("th", { className: "p-2 font-medium", children: "Primair" })] }) }), _jsx("tbody", { children: info.packingConfigurations.map((p, i) => (_jsxs("tr", { className: "border-t", children: [_jsx("td", { className: "p-2", children: LOAD_CARRIER_LABELS[p.loadCarrier] ?? p.loadCarrier }), _jsx("td", { className: "p-2 font-mono text-xs", children: p.packagingCode ?? p.customPackageId ?? "—" }), _jsx("td", { className: "p-2 text-right tabular-nums", children: p.piecesPerPackage }), _jsx("td", { className: "p-2 text-right tabular-nums", children: p.packagesPerLayer }), _jsx("td", { className: "p-2 text-right tabular-nums", children: p.layersPerLoadCarrier }), _jsx("td", { className: "p-2", children: p.primary ? (_jsx(Badge, { className: "bg-emerald-500/15 text-emerald-700 border-emerald-500/30", children: "Ja" })) : (_jsx("span", { className: "text-muted-foreground text-xs", children: "nee" })) })] }, i))) })] }) })] })), _jsxs("div", { children: [_jsxs("button", { type: "button", onClick: () => setRawOpen((o) => !o), className: "inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground", children: [rawOpen ? (_jsx(ChevronDown, { className: "h-3 w-3" })) : (_jsx(ChevronRight, { className: "h-3 w-3" })), "Alles wat Floriday teruggeeft (raw)"] }), rawOpen && (_jsx("pre", { className: "mt-2 max-h-96 overflow-auto rounded-md border bg-muted/30 p-3 text-[11px] leading-tight font-mono", children: JSON.stringify(info.raw, null, 2) }))] }), _jsxs("div", { className: "text-xs text-muted-foreground flex items-center gap-1", children: [_jsx(ExternalLink, { className: "h-3 w-3" }), "Floriday trade item ID:", " ", _jsx("code", { className: "font-mono", children: info.id })] })] }))] }) }));
85
+ }
@@ -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;AAY3D,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,OAAO,GACR,EAAE;IACD,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB,2CAmHA"}
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;AAW3D,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,OAAO,GACR,EAAE;IACD,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB,2CA0IA"}
@@ -27,6 +27,20 @@ export function VoorraadTable({ items, loading, }) {
27
27
  if (linkId)
28
28
  navigate({ to: "/voorraad/$id", params: { id: linkId } });
29
29
  };
30
+ const floriday = item.floriday ?? null;
31
+ const thumbUrl = floriday?.photoUrl || item.imageUrl;
32
+ const floridayTooltip = floriday
33
+ ? [
34
+ `Naam (Floriday): ${floriday.name || "—"}`,
35
+ `Artikelnr (Floriday): ${floriday.supplierArticleCode || "—"}`,
36
+ `GTIN: ${floriday.articleGtin || "—"}`,
37
+ floriday.vbnProductCode
38
+ ? `VBN-code: ${floriday.vbnProductCode}`
39
+ : null,
40
+ ]
41
+ .filter(Boolean)
42
+ .join("\n")
43
+ : null;
30
44
  return (_jsxs("tr", { className: `border-t hover:bg-muted/30 ${clickable ? "cursor-pointer" : ""}`, onClick: clickable ? open : undefined, role: clickable ? "button" : undefined, tabIndex: clickable ? 0 : undefined, onKeyDown: clickable
31
45
  ? (e) => {
32
46
  if (e.key === "Enter" || e.key === " ") {
@@ -34,6 +48,6 @@ export function VoorraadTable({ items, loading, }) {
34
48
  open();
35
49
  }
36
50
  }
37
- : undefined, "aria-label": clickable ? `Bewerk ${item.name}` : undefined, children: [_jsx("td", { className: "p-2", children: item.imageUrl ? (_jsx("img", { src: item.imageUrl, alt: item.name, className: "h-10 w-10 rounded object-cover", loading: "lazy" })) : (_jsx("div", { className: "h-10 w-10 rounded bg-muted flex items-center justify-center", children: _jsx(Package, { className: "h-4 w-4 text-muted-foreground" }) })) }), _jsxs("td", { className: "p-3", children: [_jsx("div", { className: "flex items-center gap-2 flex-wrap", children: _jsx("span", { className: "font-medium text-foreground", children: item.name }) }), low && (_jsx("div", { className: "text-xs text-destructive", children: "onder minimumvoorraad" }))] }), _jsx("td", { className: "p-3", children: item.itemType === "productgroep" ? (_jsx(Badge, { className: "bg-indigo-500/15 text-indigo-700 border-indigo-500/30", children: "Productgroep" })) : item.itemType === "samenstelling" ? (_jsx(Badge, { className: "bg-emerald-500/15 text-emerald-700 border-emerald-500/30", children: "Samenstelling" })) : (_jsx(Badge, { variant: "secondary", children: "Artikel" })) }), _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)))] }, item.id));
51
+ : undefined, "aria-label": clickable ? `Bewerk ${item.name}` : undefined, children: [_jsx("td", { className: "p-2", children: thumbUrl ? (_jsx("img", { src: thumbUrl, alt: item.name, className: "h-10 w-10 rounded object-cover", loading: "lazy" })) : (_jsx("div", { className: "h-10 w-10 rounded bg-muted flex items-center justify-center", children: _jsx(Package, { className: "h-4 w-4 text-muted-foreground" }) })) }), _jsxs("td", { className: "p-3", children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("span", { className: "font-medium text-foreground", children: item.name }), floriday && (_jsx("span", { className: "inline-flex items-center justify-center h-4 min-w-4 px-1 rounded-full bg-emerald-500/15 text-emerald-700 border border-emerald-500/30 text-[10px] font-semibold leading-none", title: floridayTooltip ?? "Gekoppeld met Floriday", "aria-label": "Gekoppeld met Floriday", children: "F" }))] }), low && (_jsx("div", { className: "text-xs text-destructive", children: "onder minimumvoorraad" }))] }), _jsx("td", { className: "p-3", children: item.itemType === "productgroep" ? (_jsx(Badge, { className: "bg-indigo-500/15 text-indigo-700 border-indigo-500/30", children: "Productgroep" })) : item.itemType === "samenstelling" ? (_jsx(Badge, { className: "bg-emerald-500/15 text-emerald-700 border-emerald-500/30", children: "Samenstelling" })) : (_jsx(Badge, { variant: "secondary", children: "Artikel" })) }), _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)))] }, item.id));
38
52
  }) })] }) }));
39
53
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useVoorraadData.d.ts","sourceRoot":"","sources":["../../src/hooks/useVoorraadData.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAqB5E;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;;;;;EAuS1D"}
1
+ {"version":3,"file":"useVoorraadData.d.ts","sourceRoot":"","sources":["../../src/hooks/useVoorraadData.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAsB5E;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;;;;;EA+T1D"}
@@ -1,6 +1,7 @@
1
1
  import { useCallback, useEffect, useState } from "react";
2
2
  import { supabase } from "../integrations/supabase/client";
3
3
  import { normaliseTradeItem } from "../lib/floriday-client";
4
+ import { floridayInfoFromCache } from "../lib/floriday-info";
4
5
  import { useLocalTradeItems } from "./useLocalTradeItems";
5
6
  import { fetchStockMap } from "./useTradeItemStock";
6
7
  import { useFloridayContinuousStockMap } from "./useFloridayContinuousStock";
@@ -29,6 +30,7 @@ function applyStock(item, s) {
29
30
  */
30
31
  export function useVoorraadData(connectionId) {
31
32
  const [publishedItems, setPublishedItems] = useState([]);
33
+ const [floridayInfoMap, setFloridayInfoMap] = useState(new Map());
32
34
  const [stockMap, setStockMap] = useState(new Map());
33
35
  const [typeSets, setTypeSets] = useState({
34
36
  groupProdIds: new Set(),
@@ -72,6 +74,13 @@ export function useVoorraadData(connectionId) {
72
74
  ]);
73
75
  if (pubRes.error)
74
76
  throw pubRes.error;
77
+ const infoMap = new Map();
78
+ for (const row of pubRes.data ?? []) {
79
+ const info = floridayInfoFromCache({ ...row.data, id: row.floriday_id });
80
+ if (info)
81
+ infoMap.set(row.floriday_id, info);
82
+ }
83
+ setFloridayInfoMap(infoMap);
75
84
  const normalised = (pubRes.data ?? [])
76
85
  .map((row) => normaliseTradeItem({ ...row.data, id: row.floriday_id }))
77
86
  .filter((i) => i.id)
@@ -81,6 +90,7 @@ export function useVoorraadData(connectionId) {
81
90
  floridayActive: true,
82
91
  source: "published",
83
92
  tradeItemKey: i.id,
93
+ floriday: infoMap.get(i.id) ?? null,
84
94
  }));
85
95
  setPublishedItems(normalised);
86
96
  setStockMap(stock);
@@ -247,12 +257,33 @@ export function useVoorraadData(connectionId) {
247
257
  };
248
258
  };
249
259
  // Voorkom dubbele rijen: Floriday-cache-items waar we al een lokaal product voor hebben
250
- // (op `floriday_trade_item_id`) tonen we niet apart — het lokale product wint.
251
- const localFloridayIds = new Set(localItems
252
- .map((i) => i.floridayId)
253
- .filter((id) => Boolean(id)));
260
+ // tonen we niet apart — het lokale product wint. We matchen breed zodat ook half-gelegde
261
+ // koppelingen (lege `floriday_trade_item_id` maar zelfde id/trade_item_key als de cache)
262
+ // niet dubbel verschijnen.
263
+ const localFloridayIds = new Set();
264
+ for (const i of localItems) {
265
+ if (i.floridayId)
266
+ localFloridayIds.add(i.floridayId);
267
+ // Zelfde UUID op products.id als op cache.floriday_id → legacy koppeling.
268
+ if (i.productId)
269
+ localFloridayIds.add(i.productId);
270
+ // Rauwe trade_item_key die een floriday-id-vorm heeft.
271
+ const key = i.tradeItemKey ?? "";
272
+ if (key && !key.startsWith("product:") && !key.startsWith("draft:")) {
273
+ localFloridayIds.add(key);
274
+ }
275
+ }
276
+ const attachFloriday = (i) => {
277
+ const fid = i.floridayId ?? null;
278
+ if (!fid)
279
+ return i;
280
+ const info = floridayInfoMap.get(fid);
281
+ if (!info)
282
+ return i;
283
+ return { ...i, floriday: info };
284
+ };
254
285
  const merged = [
255
- ...localItems.map((i) => applyProductGroupReservation(classify(applyStock(i, stockMap.get(i.tradeItemKey ?? ""))))),
286
+ ...localItems.map((i) => applyProductGroupReservation(classify(attachFloriday(applyStock(i, stockMap.get(i.tradeItemKey ?? "")))))),
256
287
  ...publishedItems
257
288
  .filter((i) => !i.id || !localFloridayIds.has(i.id))
258
289
  .map((i) => applyProductGroupReservation(classify(applyFloridayOverride(applyStock(i, stockMap.get(i.id)))))),
@@ -4701,6 +4701,8 @@ export declare const requireSupabaseAuth: import("@tanstack/start-client-core").
4701
4701
  Row: {
4702
4702
  buyer_organization_id: string | null;
4703
4703
  connection_id: string;
4704
+ controlled_at: string | null;
4705
+ controlled_by: string | null;
4704
4706
  created_at: string;
4705
4707
  data: import("./types").Json;
4706
4708
  floriday_id: string;
@@ -4712,6 +4714,8 @@ export declare const requireSupabaseAuth: import("@tanstack/start-client-core").
4712
4714
  Insert: {
4713
4715
  buyer_organization_id?: string | null;
4714
4716
  connection_id: string;
4717
+ controlled_at?: string | null;
4718
+ controlled_by?: string | null;
4715
4719
  created_at?: string;
4716
4720
  data: import("./types").Json;
4717
4721
  floriday_id: string;
@@ -4723,6 +4727,8 @@ export declare const requireSupabaseAuth: import("@tanstack/start-client-core").
4723
4727
  Update: {
4724
4728
  buyer_organization_id?: string | null;
4725
4729
  connection_id?: string;
4730
+ controlled_at?: string | null;
4731
+ controlled_by?: string | null;
4726
4732
  created_at?: string;
4727
4733
  data?: import("./types").Json;
4728
4734
  floriday_id?: string;
@@ -7741,6 +7747,7 @@ export declare const requireSupabaseAuth: import("@tanstack/start-client-core").
7741
7747
  id: string;
7742
7748
  image_url: string | null;
7743
7749
  incoming_quantity: number;
7750
+ is_layer: boolean;
7744
7751
  layers_per_cart: number;
7745
7752
  location: string;
7746
7753
  min_quantity: number;
@@ -7768,6 +7775,7 @@ export declare const requireSupabaseAuth: import("@tanstack/start-client-core").
7768
7775
  id?: string;
7769
7776
  image_url?: string | null;
7770
7777
  incoming_quantity?: number;
7778
+ is_layer?: boolean;
7771
7779
  layers_per_cart?: number;
7772
7780
  location: string;
7773
7781
  min_quantity?: number;
@@ -7795,6 +7803,7 @@ export declare const requireSupabaseAuth: import("@tanstack/start-client-core").
7795
7803
  id?: string;
7796
7804
  image_url?: string | null;
7797
7805
  incoming_quantity?: number;
7806
+ is_layer?: boolean;
7798
7807
  layers_per_cart?: number;
7799
7808
  location?: string;
7800
7809
  min_quantity?: number;
@@ -11309,6 +11318,13 @@ export declare const requireSupabaseAuth: import("@tanstack/start-client-core").
11309
11318
  };
11310
11319
  Returns: undefined;
11311
11320
  };
11321
+ mark_salesorder_controlled: {
11322
+ Args: {
11323
+ p_controlled: boolean;
11324
+ p_floriday_id: string;
11325
+ };
11326
+ Returns: undefined;
11327
+ };
11312
11328
  mark_shipment_shipped: {
11313
11329
  Args: {
11314
11330
  p_shipment_id: string;
@@ -11765,6 +11781,10 @@ export declare const requireSupabaseAuth: import("@tanstack/start-client-core").
11765
11781
  };
11766
11782
  Returns: number;
11767
11783
  };
11784
+ self_heal_floriday_links: {
11785
+ Args: never;
11786
+ Returns: number;
11787
+ };
11768
11788
  show_limit: {
11769
11789
  Args: never;
11770
11790
  Returns: number;
@@ -11787,6 +11807,12 @@ export declare const requireSupabaseAuth: import("@tanstack/start-client-core").
11787
11807
  };
11788
11808
  Returns: undefined;
11789
11809
  };
11810
+ sync_product_group_belading: {
11811
+ Args: {
11812
+ _group_id: string;
11813
+ };
11814
+ Returns: undefined;
11815
+ };
11790
11816
  sync_product_packing_from_floriday_cache: {
11791
11817
  Args: {
11792
11818
  p_floriday_id: string;