@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.
- package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ArtikelWizard.js +31 -1
- package/dist-lib/components/voorraad/FloridayInfoCard.d.ts +12 -0
- package/dist-lib/components/voorraad/FloridayInfoCard.d.ts.map +1 -0
- package/dist-lib/components/voorraad/FloridayInfoCard.js +85 -0
- package/dist-lib/components/voorraad/VoorraadTable.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadTable.js +15 -1
- package/dist-lib/hooks/useVoorraadData.d.ts.map +1 -1
- package/dist-lib/hooks/useVoorraadData.js +36 -5
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +26 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +26 -0
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +26 -0
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +26 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/floricode-required-features.functions.d.ts +26 -0
- package/dist-lib/lib/floricode-required-features.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-client.d.ts +4 -0
- package/dist-lib/lib/floriday-client.d.ts.map +1 -1
- package/dist-lib/lib/floriday-customer-offer.functions.d.ts +26 -0
- package/dist-lib/lib/floriday-customer-offer.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-gateway.functions.d.ts +130 -0
- package/dist-lib/lib/floriday-gateway.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-info.d.ts +54 -0
- package/dist-lib/lib/floriday-info.d.ts.map +1 -0
- package/dist-lib/lib/floriday-info.js +114 -0
- package/dist-lib/lib/floriday-warehouse.functions.d.ts +78 -0
- package/dist-lib/lib/floriday-warehouse.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-writes.functions.d.ts +52 -0
- package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ArtikelWizard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ArtikelWizard.tsx"],"names":[],"mappings":"
|
|
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, {
|
|
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;
|
|
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:
|
|
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;
|
|
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
|
-
//
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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;
|