@flowselections/floriday-voorraad 1.0.150 → 1.0.154
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/fs-ui/DataTable.d.ts +42 -0
- package/dist-lib/components/fs-ui/DataTable.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/DataTable.js +68 -0
- package/dist-lib/components/fs-ui/EmptyState.d.ts +9 -0
- package/dist-lib/components/fs-ui/EmptyState.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/EmptyState.js +5 -0
- package/dist-lib/components/fs-ui/FilterChips.d.ts +13 -0
- package/dist-lib/components/fs-ui/FilterChips.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/FilterChips.js +8 -0
- package/dist-lib/components/fs-ui/FilterPanel.d.ts +19 -0
- package/dist-lib/components/fs-ui/FilterPanel.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/FilterPanel.js +10 -0
- package/dist-lib/components/fs-ui/FiltersButton.d.ts +6 -0
- package/dist-lib/components/fs-ui/FiltersButton.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/FiltersButton.js +7 -0
- package/dist-lib/components/fs-ui/HeaderButton.d.ts +16 -0
- package/dist-lib/components/fs-ui/HeaderButton.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/HeaderButton.js +10 -0
- package/dist-lib/components/fs-ui/HoverTip.d.ts +15 -0
- package/dist-lib/components/fs-ui/HoverTip.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/HoverTip.js +24 -0
- package/dist-lib/components/fs-ui/InlineAlert.d.ts +12 -0
- package/dist-lib/components/fs-ui/InlineAlert.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/InlineAlert.js +15 -0
- package/dist-lib/components/fs-ui/KpiRow.d.ts +10 -0
- package/dist-lib/components/fs-ui/KpiRow.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/KpiRow.js +8 -0
- package/dist-lib/components/fs-ui/KpiTile.d.ts +21 -0
- package/dist-lib/components/fs-ui/KpiTile.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/KpiTile.js +40 -0
- package/dist-lib/components/fs-ui/MobilePrimaryAction.d.ts +13 -0
- package/dist-lib/components/fs-ui/MobilePrimaryAction.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/MobilePrimaryAction.js +10 -0
- package/dist-lib/components/fs-ui/NoticeBar.d.ts +10 -0
- package/dist-lib/components/fs-ui/NoticeBar.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/NoticeBar.js +16 -0
- package/dist-lib/components/fs-ui/PageHeader.d.ts +19 -0
- package/dist-lib/components/fs-ui/PageHeader.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/PageHeader.js +9 -0
- package/dist-lib/components/fs-ui/Pagination.d.ts +10 -0
- package/dist-lib/components/fs-ui/Pagination.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/Pagination.js +10 -0
- package/dist-lib/components/fs-ui/PillTabs.d.ts +17 -0
- package/dist-lib/components/fs-ui/PillTabs.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/PillTabs.js +28 -0
- package/dist-lib/components/fs-ui/RowMenu.d.ts +13 -0
- package/dist-lib/components/fs-ui/RowMenu.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/RowMenu.js +7 -0
- package/dist-lib/components/fs-ui/SearchInput.d.ts +11 -0
- package/dist-lib/components/fs-ui/SearchInput.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/SearchInput.js +55 -0
- package/dist-lib/components/fs-ui/ShortcutHelp.d.ts +8 -0
- package/dist-lib/components/fs-ui/ShortcutHelp.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/ShortcutHelp.js +6 -0
- package/dist-lib/components/fs-ui/StatusIcon.d.ts +13 -0
- package/dist-lib/components/fs-ui/StatusIcon.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/StatusIcon.js +15 -0
- package/dist-lib/components/fs-ui/TableSkeleton.d.ts +7 -0
- package/dist-lib/components/fs-ui/TableSkeleton.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/TableSkeleton.js +6 -0
- package/dist-lib/components/fs-ui/Toolbar.d.ts +12 -0
- package/dist-lib/components/fs-ui/Toolbar.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/Toolbar.js +8 -0
- package/dist-lib/components/fs-ui/UpdatedAt.d.ts +10 -0
- package/dist-lib/components/fs-ui/UpdatedAt.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/UpdatedAt.js +18 -0
- package/dist-lib/components/voorraad/ArtikelCompositionCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ArtikelCompositionCard.js +46 -15
- package/dist-lib/components/voorraad/ArtikelWizard.js +4 -4
- package/dist-lib/components/voorraad/FloridayConnectionNotice.d.ts +7 -6
- package/dist-lib/components/voorraad/FloridayConnectionNotice.d.ts.map +1 -1
- package/dist-lib/components/voorraad/FloridayConnectionNotice.js +15 -12
- package/dist-lib/components/voorraad/FustenPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/FustenPage.js +92 -26
- package/dist-lib/components/voorraad/NieuwArtikelPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/NieuwArtikelPage.js +2 -1
- package/dist-lib/components/voorraad/OrderHistoryCard.d.ts +3 -1
- package/dist-lib/components/voorraad/OrderHistoryCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/OrderHistoryCard.js +26 -50
- package/dist-lib/components/voorraad/PublicatiesPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/PublicatiesPage.js +42 -36
- package/dist-lib/components/voorraad/Sneltoetsen.d.ts +14 -0
- package/dist-lib/components/voorraad/Sneltoetsen.d.ts.map +1 -0
- package/dist-lib/components/voorraad/Sneltoetsen.js +44 -0
- package/dist-lib/components/voorraad/StockIntegrityAlert.d.ts +42 -1
- package/dist-lib/components/voorraad/StockIntegrityAlert.d.ts.map +1 -1
- package/dist-lib/components/voorraad/StockIntegrityAlert.js +38 -7
- package/dist-lib/components/voorraad/StockMutationHistoryCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/StockMutationHistoryCard.js +10 -46
- package/dist-lib/components/voorraad/StockPushMonitor.js +2 -2
- package/dist-lib/components/voorraad/TelmomentButton.d.ts +5 -1
- package/dist-lib/components/voorraad/TelmomentButton.d.ts.map +1 -1
- package/dist-lib/components/voorraad/TelmomentButton.js +13 -6
- package/dist-lib/components/voorraad/ValidationOverviewTable.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ValidationOverviewTable.js +11 -40
- package/dist-lib/components/voorraad/ValidationPage.d.ts +1 -0
- package/dist-lib/components/voorraad/ValidationPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ValidationPage.js +33 -5
- package/dist-lib/components/voorraad/VoorraadBeheerCard.d.ts +4 -1
- package/dist-lib/components/voorraad/VoorraadBeheerCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadBeheerCard.js +26 -5
- package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadPage.js +67 -38
- package/dist-lib/components/voorraad/VoorraadSturingCard.js +1 -1
- package/dist-lib/components/voorraad/artikel/ArtikelPagina.d.ts.map +1 -1
- package/dist-lib/components/voorraad/artikel/ArtikelPagina.js +42 -19
- package/dist-lib/components/voorraad/lijst/ImporteerKnop.d.ts.map +1 -1
- package/dist-lib/components/voorraad/lijst/ImporteerKnop.js +1 -1
- package/dist-lib/components/voorraad/lijst/LijstExportKnop.d.ts.map +1 -1
- package/dist-lib/components/voorraad/lijst/LijstExportKnop.js +1 -1
- package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.d.ts +8 -2
- package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.d.ts.map +1 -1
- package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.js +62 -27
- package/dist-lib/hooks/useLocalTradeItems.d.ts +14 -0
- package/dist-lib/hooks/useLocalTradeItems.d.ts.map +1 -1
- package/dist-lib/hooks/useLocalTradeItems.js +5 -3
- package/dist-lib/hooks/useTradeItemStock.d.ts.map +1 -1
- package/dist-lib/hooks/useTradeItemStock.js +6 -3
- package/dist-lib/hooks/useVoorraadData.d.ts.map +1 -1
- package/dist-lib/hooks/useVoorraadData.js +16 -8
- package/dist-lib/hooks/useVoorraadLijst.d.ts +10 -0
- package/dist-lib/hooks/useVoorraadLijst.d.ts.map +1 -1
- package/dist-lib/hooks/useVoorraadLijst.js +22 -4
- package/dist-lib/integrations/supabase/types.d.ts +85 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/alle-rijen.d.ts +15 -0
- package/dist-lib/lib/alle-rijen.d.ts.map +1 -0
- package/dist-lib/lib/alle-rijen.js +17 -0
- package/dist-lib/lib/db-error-message.d.ts.map +1 -1
- package/dist-lib/lib/db-error-message.js +3 -1
- package/dist-lib/lib/labels.d.ts +42 -0
- package/dist-lib/lib/labels.d.ts.map +1 -0
- package/dist-lib/lib/labels.js +152 -0
- package/dist-lib/lib/notatie.d.ts +4 -0
- package/dist-lib/lib/notatie.d.ts.map +1 -0
- package/dist-lib/lib/notatie.js +18 -0
- package/dist-lib/lib/voorraad-lijst.d.ts +2 -0
- package/dist-lib/lib/voorraad-lijst.d.ts.map +1 -1
- package/dist-lib/lib/voorraad-lijst.js +14 -3
- package/package.json +2 -1
- package/sql/floriday-voorraad-schema.sql +315 -39
|
@@ -1,2 +1,43 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Rode melding wanneer in één transactie twee of meer artikelen van een
|
|
3
|
+
* positieve fysieke voorraad naar 0 zijn gegaan (bewaking in de database:
|
|
4
|
+
* `stock_integrity_alerts`). Blijft staan tot iemand op "Gezien" klikt.
|
|
5
|
+
*/
|
|
6
|
+
import { type ReactNode } from "react";
|
|
7
|
+
type AlertItem = {
|
|
8
|
+
trade_item_key: string;
|
|
9
|
+
physical_before: number;
|
|
10
|
+
change_source: string | null;
|
|
11
|
+
product_id?: string | null;
|
|
12
|
+
article_code?: string | null;
|
|
13
|
+
name?: string | null;
|
|
14
|
+
};
|
|
15
|
+
type IntegrityAlert = {
|
|
16
|
+
id: string;
|
|
17
|
+
created_at: string;
|
|
18
|
+
item_count: number;
|
|
19
|
+
alert_kind?: string | null;
|
|
20
|
+
items: (AlertItem & {
|
|
21
|
+
qty?: number | null;
|
|
22
|
+
order_number?: string | null;
|
|
23
|
+
})[];
|
|
24
|
+
};
|
|
25
|
+
/** Open voorraadbewakingsmeldingen, met namen en "Gezien". */
|
|
26
|
+
export declare function useStockIntegrityAlerts(): {
|
|
27
|
+
alerts: IntegrityAlert[];
|
|
28
|
+
names: Map<string, {
|
|
29
|
+
code: string | null;
|
|
30
|
+
name: string | null;
|
|
31
|
+
}>;
|
|
32
|
+
acknowledge: (id: string) => Promise<void>;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Meldingen als losse regels voor de NoticeBar: één regel per melding met
|
|
36
|
+
* "Details" (lijst in een venster, nooit in de melding zelf) en "Gezien".
|
|
37
|
+
*/
|
|
38
|
+
export declare function useStockIntegrityNotices(): {
|
|
39
|
+
items: ReactNode[];
|
|
40
|
+
dialog: ReactNode;
|
|
41
|
+
};
|
|
42
|
+
export {};
|
|
2
43
|
//# sourceMappingURL=StockIntegrityAlert.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StockIntegrityAlert.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/StockIntegrityAlert.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"StockIntegrityAlert.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/StockIntegrityAlert.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAoC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAKzE,KAAK,SAAS,GAAG;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,CAAC;AAmBF,KAAK,cAAc,GAAG;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,CAAC,SAAS,GAAG;QAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,EAAE,CAAC;CAC9E,CAAC;AAEF,8DAA8D;AAC9D,wBAAgB,uBAAuB;;;cAEkB,MAAM,GAAG,IAAI;cAAQ,MAAM,GAAG,IAAI;;sBAsD1D,MAAM;EAiBtC;AAUD;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI;IAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,CAyCpF"}
|
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* Rode melding wanneer in één transactie twee of meer artikelen van een
|
|
4
4
|
* positieve fysieke voorraad naar 0 zijn gegaan (bewaking in de database:
|
|
5
5
|
* `stock_integrity_alerts`). Blijft staan tot iemand op "Gezien" klikt.
|
|
6
6
|
*/
|
|
7
7
|
import { useCallback, useEffect, useState } from "react";
|
|
8
|
-
import {
|
|
8
|
+
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@flowselections/core";
|
|
9
9
|
import { toast } from "sonner";
|
|
10
10
|
import { supabase } from "../../integrations/supabase/client.js";
|
|
11
|
+
/** Bron van een voorraadwijziging in gewone woorden; onbekende codes niet tonen. */
|
|
12
|
+
const BRON = {
|
|
13
|
+
MANUAL_STOCK: "handmatig aangepast", IMPORT: "import", ORDER: "order", CORRECTIE: "correctie",
|
|
14
|
+
FLORIDAY: "Floriday", PRODUCTION: "productie", unknown: "onbekend",
|
|
15
|
+
};
|
|
16
|
+
const bronTekst = (b) => (b ? (BRON[b] ? ` (${BRON[b]})` : "") : "");
|
|
11
17
|
const keyToProductId = (k) => k.replace(/^product:/, "");
|
|
12
18
|
function label(it, names) {
|
|
13
19
|
const found = names.get(it.trade_item_key);
|
|
@@ -17,7 +23,8 @@ function label(it, names) {
|
|
|
17
23
|
return [code, name].filter(Boolean).join(" – ");
|
|
18
24
|
return "onbekend artikel";
|
|
19
25
|
}
|
|
20
|
-
|
|
26
|
+
/** Open voorraadbewakingsmeldingen, met namen en "Gezien". */
|
|
27
|
+
export function useStockIntegrityAlerts() {
|
|
21
28
|
const [alerts, setAlerts] = useState([]);
|
|
22
29
|
const [names, setNames] = useState(new Map());
|
|
23
30
|
const load = useCallback(async () => {
|
|
@@ -76,12 +83,36 @@ export function StockIntegrityAlert() {
|
|
|
76
83
|
})
|
|
77
84
|
.eq("id", id);
|
|
78
85
|
if (error) {
|
|
79
|
-
|
|
86
|
+
console.warn(error);
|
|
87
|
+
toast.error("Markeren als gezien lukt niet. Probeer opnieuw.");
|
|
80
88
|
return;
|
|
81
89
|
}
|
|
82
90
|
void load();
|
|
83
91
|
};
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
return { alerts, names, acknowledge };
|
|
93
|
+
}
|
|
94
|
+
const tijd = (iso) => new Date(iso).toLocaleString("nl-NL", { weekday: "short", day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" }).replace(/\./g, "");
|
|
95
|
+
function samenvatting(a) {
|
|
96
|
+
return a.alert_kind === "FLORIDAY_NOT_UPDATED"
|
|
97
|
+
? `Verkoop niet in Floriday verwerkt (${tijd(a.created_at)}): pas de Floriday-voorraad aan`
|
|
98
|
+
: `${a.item_count} artikelen gingen tegelijk naar 0 (${tijd(a.created_at)}): controleer of dit klopt`;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Meldingen als losse regels voor de NoticeBar: één regel per melding met
|
|
102
|
+
* "Details" (lijst in een venster, nooit in de melding zelf) en "Gezien".
|
|
103
|
+
*/
|
|
104
|
+
export function useStockIntegrityNotices() {
|
|
105
|
+
const { alerts, names, acknowledge } = useStockIntegrityAlerts();
|
|
106
|
+
const [open, setOpen] = useState(null);
|
|
107
|
+
const items = alerts.map((a) => (_jsxs("span", { className: "inline-flex min-w-0 max-w-full items-center gap-2", children: [_jsx("span", { className: "truncate", children: samenvatting(a) }), _jsx("span", { role: "button", tabIndex: 0, className: "shrink-0 font-medium underline underline-offset-2", onClick: (e) => { e.stopPropagation(); setOpen(a); }, onKeyDown: (e) => { if (e.key === "Enter") {
|
|
108
|
+
e.stopPropagation();
|
|
109
|
+
setOpen(a);
|
|
110
|
+
} }, children: "Details" }), _jsx("span", { role: "button", tabIndex: 0, className: "shrink-0 font-medium underline underline-offset-2", onClick: (e) => { e.stopPropagation(); void acknowledge(a.id); }, onKeyDown: (e) => { if (e.key === "Enter") {
|
|
111
|
+
e.stopPropagation();
|
|
112
|
+
void acknowledge(a.id);
|
|
113
|
+
} }, children: "Gezien" })] }, a.id)));
|
|
114
|
+
const dialog = (_jsx(Dialog, { open: !!open, onOpenChange: (o) => !o && setOpen(null), children: _jsxs(DialogContent, { className: "max-w-lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "Voorraadmelding" }) }), open && (_jsxs("div", { className: "space-y-3 text-sm", children: [_jsxs("p", { children: [samenvatting(open), "."] }), open.alert_kind === "FLORIDAY_NOT_UPDATED" && (_jsx("p", { className: "text-muted-foreground", children: "Floriday leidt deze artikelen. Deze verkoop kwam niet via Floriday, dus Floriday weet er niets van." })), _jsx("ul", { className: "max-h-80 divide-y divide-border overflow-auto rounded-lg border border-border", children: (Array.isArray(open.items) ? open.items : []).map((it, i) => (_jsxs("li", { className: "flex justify-between gap-3 px-3 py-2", children: [_jsx("span", { className: "min-w-0 truncate", children: label(it, names) }), _jsx("span", { className: "shrink-0 tabular-nums text-muted-foreground", children: open.alert_kind === "FLORIDAY_NOT_UPDATED"
|
|
115
|
+
? `${it.qty ?? "?"} stuks${it.order_number ? ` · order ${it.order_number}` : ""}`
|
|
116
|
+
: `was ${it.physical_before} stuks${bronTekst(it.change_source)}` })] }, `${it.trade_item_key}-${i}`))) }), _jsx("div", { className: "flex justify-end", children: _jsx("button", { type: "button", className: "h-8 rounded-md border border-border bg-card px-3 text-sm font-medium hover:bg-muted", onClick: () => { void acknowledge(open.id); setOpen(null); }, children: "Gezien" }) })] }))] }) }));
|
|
117
|
+
return { items, dialog };
|
|
87
118
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StockMutationHistoryCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/StockMutationHistoryCard.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"StockMutationHistoryCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/StockMutationHistoryCard.tsx"],"names":[],"mappings":"AAcA,UAAU,KAAK;IACb,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,wFAAwF;IACxF,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAiBD,wBAAgB,wBAAwB,CAAC,EAAE,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,KAAK,+BA2MpF"}
|
|
@@ -6,42 +6,12 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
6
6
|
*/
|
|
7
7
|
import { useEffect, useState } from "react";
|
|
8
8
|
import { dbFoutTekst } from "../../lib/db-error-message.js";
|
|
9
|
-
import { Card, CardContent,
|
|
9
|
+
import { Card, CardContent, Button } from "@flowselections/core";
|
|
10
|
+
import { mutatieSoort, redenTekst } from "../../lib/labels.js";
|
|
11
|
+
import { datum, getal } from "../../lib/notatie.js";
|
|
12
|
+
import { HoverTip } from "../fs-ui/HoverTip.js";
|
|
10
13
|
import { ChevronDown, ChevronUp, Loader2, History } from "lucide-react";
|
|
11
14
|
import { supabase } from "../../integrations/supabase/client.js";
|
|
12
|
-
import { CollapsibleText } from "./CollapsibleText.js";
|
|
13
|
-
const TYPE_LABEL = {
|
|
14
|
-
consumption: { label: "Afboeking (verkoop)", tone: "bg-rose-100 text-rose-800 border-rose-200" },
|
|
15
|
-
consumption_blocked: { label: "Afboeking geblokkeerd", tone: "bg-amber-100 text-amber-800 border-amber-200" },
|
|
16
|
-
consumption_skipped: { label: "Afboeking overgeslagen", tone: "bg-amber-100 text-amber-800 border-amber-200" },
|
|
17
|
-
reservation: { label: "Reservering", tone: "bg-blue-100 text-blue-800 border-blue-200" },
|
|
18
|
-
reservation_release: { label: "Reservering vrijgegeven", tone: "bg-blue-100 text-blue-800 border-blue-200" },
|
|
19
|
-
reservation_skipped: { label: "Reservering overgeslagen", tone: "bg-amber-100 text-amber-800 border-amber-200" },
|
|
20
|
-
manual_writeoff: { label: "Afgeboekt (met reden)", tone: "bg-rose-100 text-rose-800 border-rose-200" },
|
|
21
|
-
manual_correction: { label: "Handmatige correctie", tone: "bg-slate-100 text-slate-700 border-slate-200" },
|
|
22
|
-
order_sync: { label: "Verwerking verkooporders", tone: "bg-indigo-100 text-indigo-800 border-indigo-200" },
|
|
23
|
-
import: { label: "Import", tone: "bg-slate-100 text-slate-700 border-slate-200" },
|
|
24
|
-
production: { label: "Productie", tone: "bg-emerald-100 text-emerald-800 border-emerald-200" },
|
|
25
|
-
stock_update: { label: "Voorraad bijgewerkt", tone: "bg-slate-100 text-slate-700 border-slate-200" },
|
|
26
|
-
new_article: { label: "Nieuw artikel", tone: "bg-emerald-100 text-emerald-800 border-emerald-200" },
|
|
27
|
-
};
|
|
28
|
-
function formatMoment(v) {
|
|
29
|
-
try {
|
|
30
|
-
const d = new Date(v);
|
|
31
|
-
if (Number.isNaN(d.getTime()))
|
|
32
|
-
return v;
|
|
33
|
-
return d.toLocaleString("nl-NL", {
|
|
34
|
-
day: "2-digit",
|
|
35
|
-
month: "short",
|
|
36
|
-
year: "numeric",
|
|
37
|
-
hour: "2-digit",
|
|
38
|
-
minute: "2-digit",
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
catch {
|
|
42
|
-
return v;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
15
|
export function StockMutationHistoryCard({ tradeItemKey, productId, sleutels }) {
|
|
46
16
|
const [rows, setRows] = useState(null);
|
|
47
17
|
const [error, setError] = useState(null);
|
|
@@ -142,17 +112,11 @@ export function StockMutationHistoryCard({ tradeItemKey, productId, sleutels })
|
|
|
142
112
|
supabase.removeChannel(channel);
|
|
143
113
|
};
|
|
144
114
|
}, [tradeItemKey, productId, sleutels?.join("|")]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
145
|
-
return (_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(History, { className: "h-4 w-4" }) }), _jsx("span", { className: "font-medium", children: "Afboekgeschiedenis" }), _jsx("span", { className: "text-xs text-muted-foreground tabular-nums", children: rows ? `${rows.length}
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
r.physical_after !== null &&
|
|
152
|
-
r.physical_before !== r.physical_after;
|
|
153
|
-
const resChanged = r.reserved_before !== null &&
|
|
154
|
-
r.reserved_after !== null &&
|
|
155
|
-
r.reserved_before !== r.reserved_after;
|
|
156
|
-
return (_jsxs("tr", { className: "border-t align-top", children: [_jsx("td", { className: "whitespace-nowrap px-3 py-2", children: formatMoment(r.created_at) }), _jsx("td", { className: "px-3 py-2", children: _jsx(Badge, { variant: "outline", className: `border ${meta.tone}`, children: meta.label }) }), _jsxs("td", { className: `px-3 py-2 text-right tabular-nums font-medium ${r.qty_delta < 0 ? "text-rose-700" : r.qty_delta > 0 ? "text-emerald-700" : ""}`, children: [r.qty_delta > 0 ? "+" : "", r.qty_delta] }), _jsxs("td", { className: "whitespace-nowrap px-3 py-2 tabular-nums text-muted-foreground", children: [physChanged && (_jsxs("div", { children: ["Liggend ", r.physical_before, " \u2192 ", r.physical_after] })), resChanged && (_jsxs("div", { children: ["Gereserveerd ", r.reserved_before, " \u2192 ", r.reserved_after] })), !physChanged && !resChanged && _jsx("span", { children: "\u2014" })] }), _jsx("td", { className: "whitespace-nowrap px-3 py-2", children: r.order_id ? (_jsx("a", { href: `/floriday-verkoop/verkooporders/${r.order_id}`, className: "text-primary hover:underline", children: r.order_number ?? "Order" })) : (_jsx("span", { className: "text-muted-foreground", children: "\u2014" })) }), _jsx("td", { className: "whitespace-nowrap px-3 py-2 text-muted-foreground", children: r.created_by_name ?? "Systeem" }), _jsx("td", { className: "px-3 py-2 text-muted-foreground", children: _jsx(CollapsibleText, { text: r.reason }) })] }, r.id));
|
|
115
|
+
return (_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(History, { className: "h-4 w-4" }) }), _jsx("span", { className: "font-medium", children: "Afboekgeschiedenis" }), _jsx("span", { className: "text-xs text-muted-foreground tabular-nums", children: rows ? `${rows.length} wijziging${rows.length === 1 ? "" : "en"}` : "" })] }), _jsxs(CardContent, { className: "pt-0", children: [error && (_jsx("div", { className: "text-sm text-destructive", children: error })), !rows && !error && (_jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), " Mutaties laden\u2026"] })), rows && rows.length === 0 && !error && (_jsx("div", { className: "text-sm text-muted-foreground", children: "Nog geen voorraadmutaties voor dit artikel." })), rows && rows.length > 0 && (_jsxs("div", { className: "overflow-x-auto rounded-md border border-border", children: [_jsxs("table", { className: "w-full min-w-[900px] table-fixed text-sm [&_thead_th]:h-10 [&_thead_th]:px-2 [&_thead_th]:text-left [&_thead_th]:align-middle [&_thead_th]:text-[11px] [&_thead_th]:font-semibold [&_thead_th]:uppercase [&_thead_th]:tracking-wide [&_thead_th]:text-muted-foreground", children: [_jsxs("colgroup", { children: [_jsx("col", { style: { width: "132px" } }), _jsx("col", { style: { width: "184px" } }), _jsx("col", { style: { width: "104px" } }), _jsx("col", { style: { width: "184px" } }), _jsx("col", { style: { width: "112px" } }), _jsx("col", { style: { width: "132px" } }), _jsx("col", {})] }), _jsx("thead", { children: _jsxs("tr", { className: "border-b border-border", children: [_jsx("th", { children: "Wanneer" }), _jsx("th", { children: "Soort" }), _jsx("th", { children: "Aantal" }), _jsx("th", { children: "Van \u2192 naar" }), _jsx("th", { children: "Order" }), _jsx("th", { children: "Door" }), _jsx("th", { children: "Reden" })] }) }), _jsx("tbody", { children: (expanded ? rows : rows.slice(0, 5)).map((r) => {
|
|
116
|
+
const physChanged = r.physical_before !== null && r.physical_after !== null && r.physical_before !== r.physical_after;
|
|
117
|
+
const resChanged = r.reserved_before !== null && r.reserved_after !== null && r.reserved_before !== r.reserved_after;
|
|
118
|
+
const reden = redenTekst(r.reason);
|
|
119
|
+
const cel = "truncate px-2 py-0 align-middle";
|
|
120
|
+
return (_jsxs("tr", { className: "h-[60px] border-b border-border last:border-0", children: [_jsx("td", { className: cel + " tabular-nums", children: datum(r.created_at, true) }), _jsx("td", { className: cel, children: mutatieSoort(r.mutation_type) }), _jsxs("td", { className: cel + " tabular-nums " + (r.qty_delta < 0 ? "text-destructive" : r.qty_delta > 0 ? "text-primary" : "text-muted-foreground"), children: [r.qty_delta > 0 ? "+" : "", getal(r.qty_delta), " ", _jsx("span", { className: "text-xs text-muted-foreground", children: "stuks" })] }), _jsxs("td", { className: cel + " tabular-nums", children: [physChanged && _jsxs("div", { className: "truncate", children: ["Liggend ", getal(r.physical_before), " \u2192 ", getal(r.physical_after)] }), resChanged && _jsxs("div", { className: "truncate " + (physChanged ? "text-xs text-muted-foreground" : ""), children: ["Gereserveerd ", getal(r.reserved_before), " \u2192 ", getal(r.reserved_after)] }), !physChanged && !resChanged && _jsx("span", { className: "text-muted-foreground", children: "\u2014" })] }), _jsx("td", { className: cel, children: r.order_id ? (_jsx("a", { href: `/floriday-verkoop/verkooporders/${r.order_id}`, className: "text-primary hover:underline", children: r.order_number ?? "Order" })) : _jsx("span", { className: "text-muted-foreground", children: "\u2014" }) }), _jsx("td", { className: cel + " text-muted-foreground", children: r.created_by_name ?? "Systeem" }), _jsx("td", { className: cel + " text-muted-foreground", children: reden ? _jsx(HoverTip, { label: reden, content: _jsx("div", { className: "max-w-xs text-xs", children: reden }), align: "start", className: "block max-w-full truncate text-left", children: _jsx("span", { className: "truncate", children: reden }) }) : "—" })] }, r.id));
|
|
157
121
|
}) })] }), rows.length > 5 && (_jsx("div", { className: "flex justify-center border-t bg-muted/20 px-3 py-2", children: _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setExpanded((current) => !current), className: "gap-1.5", children: expanded ? (_jsxs(_Fragment, { children: [_jsx(ChevronUp, { className: "h-4 w-4" }), " Inklappen"] })) : (_jsxs(_Fragment, { children: [_jsx(ChevronDown, { className: "h-4 w-4" }), " Meer tonen (", rows.length - 5, ")"] })) }) }))] }))] })] }));
|
|
158
122
|
}
|
|
@@ -20,7 +20,7 @@ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
|
|
|
20
20
|
/** Een verwerker houdt een regel maximaal 5 minuten vast. */
|
|
21
21
|
const LOCK_STALE_MS = 5 * 60000;
|
|
22
22
|
const STATUS_LABEL = {
|
|
23
|
-
pending: "
|
|
23
|
+
pending: "Wacht op versturen",
|
|
24
24
|
processing: "Bezig",
|
|
25
25
|
sent: "Verstuurd",
|
|
26
26
|
completed: "Verstuurd",
|
|
@@ -74,7 +74,7 @@ function explainError(raw) {
|
|
|
74
74
|
return { text: raw, action: "Vul een prijs in bij dit artikel en probeer opnieuw." };
|
|
75
75
|
}
|
|
76
76
|
if (m.includes("cache")) {
|
|
77
|
-
return { text: raw, action: "
|
|
77
|
+
return { text: raw, action: "Werk de Floriday-artikelen bij en probeer opnieuw." };
|
|
78
78
|
}
|
|
79
79
|
if (m.includes("429") || m.includes("rate limit") || m.includes("snelheidslimiet")) {
|
|
80
80
|
return { text: "Floriday liet even geen nieuwe berichten toe.", action: "Dit gaat vanzelf; de volgende ronde pakt het op." };
|
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
* Telmoment vastleggen: orders vóór de teldag en orders die vóór het telmoment vertrokken
|
|
3
3
|
* zijn tellen niet mee; orders van de teldag die al weg zijn vink je aan.
|
|
4
4
|
*/
|
|
5
|
-
export declare function TelmomentButton({ onSaved }: {
|
|
5
|
+
export declare function TelmomentButton({ onSaved, open: openVan, onOpenChange, verbergKnop }: {
|
|
6
6
|
onSaved?: () => void;
|
|
7
|
+
/** Optioneel van buitenaf openen (bijv. vanuit het menu "Meer"). */
|
|
8
|
+
open?: boolean;
|
|
9
|
+
onOpenChange?: (o: boolean) => void;
|
|
10
|
+
verbergKnop?: boolean;
|
|
7
11
|
}): import("react").JSX.Element;
|
|
8
12
|
//# sourceMappingURL=TelmomentButton.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TelmomentButton.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/TelmomentButton.tsx"],"names":[],"mappings":"AA6BA;;;GAGG;AACH,wBAAgB,eAAe,CAAC,EAAE,OAAO,EAAE,EAAE;
|
|
1
|
+
{"version":3,"file":"TelmomentButton.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/TelmomentButton.tsx"],"names":[],"mappings":"AA6BA;;;GAGG;AACH,wBAAgB,eAAe,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,EAAE;IACrF,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,oEAAoE;IACpE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACpC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,+BAyGA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
3
|
import { ClipboardCheck } from "lucide-react";
|
|
4
4
|
import { Button, Dialog, DialogContent, DialogHeader, DialogTitle, Input, toast, } from "@flowselections/core";
|
|
5
5
|
import { supabase } from "../../integrations/supabase/client.js";
|
|
@@ -12,8 +12,10 @@ function lokaleInvoer(d) {
|
|
|
12
12
|
* Telmoment vastleggen: orders vóór de teldag en orders die vóór het telmoment vertrokken
|
|
13
13
|
* zijn tellen niet mee; orders van de teldag die al weg zijn vink je aan.
|
|
14
14
|
*/
|
|
15
|
-
export function TelmomentButton({ onSaved }) {
|
|
16
|
-
const [
|
|
15
|
+
export function TelmomentButton({ onSaved, open: openVan, onOpenChange, verbergKnop }) {
|
|
16
|
+
const [openEigen, setOpenEigen] = useState(false);
|
|
17
|
+
const open = openVan ?? openEigen;
|
|
18
|
+
const setOpen = (o) => { setOpenEigen(o); onOpenChange?.(o); };
|
|
17
19
|
const [moment, setMoment] = useState(() => lokaleInvoer(new Date()));
|
|
18
20
|
const [regels, setRegels] = useState(null);
|
|
19
21
|
const [alWeg, setAlWeg] = useState(new Set());
|
|
@@ -22,7 +24,8 @@ export function TelmomentButton({ onSaved }) {
|
|
|
22
24
|
setRegels(null);
|
|
23
25
|
const { data, error } = await supabase.rpc("voorraad_telmoment_lijst", { p_telmoment: new Date(m).toISOString(), p_keys: null });
|
|
24
26
|
if (error) {
|
|
25
|
-
|
|
27
|
+
console.warn(error);
|
|
28
|
+
toast.error("De lijst kon niet geladen worden. Probeer opnieuw.");
|
|
26
29
|
setRegels([]);
|
|
27
30
|
return;
|
|
28
31
|
}
|
|
@@ -42,14 +45,18 @@ export function TelmomentButton({ onSaved }) {
|
|
|
42
45
|
onSaved?.();
|
|
43
46
|
}
|
|
44
47
|
catch (e) {
|
|
45
|
-
|
|
48
|
+
console.warn(e);
|
|
49
|
+
toast.error("Telmoment opslaan lukt niet. Probeer opnieuw.");
|
|
46
50
|
}
|
|
47
51
|
finally {
|
|
48
52
|
setBusy(false);
|
|
49
53
|
}
|
|
50
54
|
};
|
|
55
|
+
// Van buitenaf geopend: lijst laden.
|
|
56
|
+
useEffect(() => { if (openVan)
|
|
57
|
+
void laden(); }, [openVan]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
51
58
|
const groepen = ["teldag_open", "vertrokken", "voor_teldag"];
|
|
52
|
-
return (_jsxs(_Fragment, { children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: () => { setOpen(true); void laden(); }, children: [_jsx(ClipboardCheck, { className: "mr-2 h-4 w-4" }), "Telmoment"] }), _jsx(Dialog, { open: open, onOpenChange: setOpen, children: _jsxs(DialogContent, { className: "max-w-3xl", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "Telmoment vastleggen" }) }), _jsxs("div", { className: "flex items-end gap-2", children: [_jsxs("label", { className: "text-xs text-muted-foreground space-y-1", children: [_jsx("span", { className: "block", children: "Moment van tellen" }), _jsx(Input, { type: "datetime-local", className: "h-9 w-56", value: moment, onChange: (e) => setMoment(e.target.value) })] }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => void laden(), children: "Ververs lijst" })] }), _jsxs("div", { className: "max-h-[50vh] overflow-y-auto space-y-4 text-sm", children: [regels === null && _jsx("p", { className: "text-muted-foreground", children: "Laden\u2026" }), regels !== null && regels.length === 0 && (_jsx("p", { className: "text-muted-foreground", children: "Geen openstaande orderregels tot en met de teldag." })), regels !== null && groepen.map((g) => {
|
|
59
|
+
return (_jsxs(_Fragment, { children: [!verbergKnop && (_jsxs(Button, { variant: "outline", size: "sm", onClick: () => { setOpen(true); void laden(); }, children: [_jsx(ClipboardCheck, { className: "mr-2 h-4 w-4" }), "Telmoment"] })), _jsx(Dialog, { open: open, onOpenChange: setOpen, children: _jsxs(DialogContent, { className: "max-w-3xl", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "Telmoment vastleggen" }) }), _jsxs("div", { className: "flex items-end gap-2", children: [_jsxs("label", { className: "text-xs text-muted-foreground space-y-1", children: [_jsx("span", { className: "block", children: "Moment van tellen" }), _jsx(Input, { type: "datetime-local", className: "h-9 w-56", value: moment, onChange: (e) => setMoment(e.target.value) })] }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => void laden(), children: "Ververs lijst" })] }), _jsxs("div", { className: "max-h-[50vh] overflow-y-auto space-y-4 text-sm", children: [regels === null && _jsx("p", { className: "text-muted-foreground", children: "Laden\u2026" }), regels !== null && regels.length === 0 && (_jsx("p", { className: "text-muted-foreground", children: "Geen openstaande orderregels tot en met de teldag." })), regels !== null && groepen.map((g) => {
|
|
53
60
|
const lijst = regels.filter((r) => r.categorie === g);
|
|
54
61
|
if (lijst.length === 0)
|
|
55
62
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ValidationOverviewTable.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ValidationOverviewTable.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"ValidationOverviewTable.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ValidationOverviewTable.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAkB3D,wBAAgB,uBAAuB,CAAC,EACtC,KAAK,EACL,YAAY,EACZ,SAAS,GACV,EAAE;IACD,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC,+BA6IA"}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo, useState } from "react";
|
|
3
3
|
import { useNavigate } from "@tanstack/react-router";
|
|
4
|
-
import {
|
|
4
|
+
import { Button } from "@flowselections/core";
|
|
5
5
|
import { Link2, Loader2, Pencil, Send } from "lucide-react";
|
|
6
6
|
import { conceptMetArtikel, validateDraftForPublish } from "../../lib/floriday-import-validator.js";
|
|
7
|
-
import { updateDraftMeta } from "../../hooks/useLocalTradeItems.js";
|
|
8
|
-
import { floriday } from "../../lib/floriday-client.js";
|
|
9
7
|
import { relinkProducts } from "../../lib/floriday-relink.js";
|
|
10
8
|
import { toast } from "@flowselections/core";
|
|
11
9
|
/**
|
|
@@ -33,13 +31,14 @@ export function ValidationOverviewTable({ items, connectionId, onChanged, }) {
|
|
|
33
31
|
else {
|
|
34
32
|
const total = (res.linked ?? 0) + (res.repaired ?? 0);
|
|
35
33
|
toast.success(total > 0
|
|
36
|
-
? `${total}
|
|
34
|
+
? `${total} artikel(en) opnieuw gekoppeld aan Floriday`
|
|
37
35
|
: "Geen extra koppelingen gevonden");
|
|
38
36
|
}
|
|
39
37
|
await onChanged();
|
|
40
38
|
}
|
|
41
39
|
catch (e) {
|
|
42
|
-
|
|
40
|
+
console.warn(e);
|
|
41
|
+
toast.error("Automatisch koppelen is niet gelukt. Probeer het later opnieuw.");
|
|
43
42
|
}
|
|
44
43
|
finally {
|
|
45
44
|
setRelinking(false);
|
|
@@ -51,50 +50,22 @@ export function ValidationOverviewTable({ items, connectionId, onChanged, }) {
|
|
|
51
50
|
}
|
|
52
51
|
return "Nog geen overeenkomend Floriday-artikel gevonden (streepjescode, artikelcode of naam)";
|
|
53
52
|
};
|
|
54
|
-
const publish = async (item) => {
|
|
55
|
-
const productId = item.productId ?? item.localId;
|
|
56
|
-
if (!productId || !connectionId)
|
|
57
|
-
return;
|
|
58
|
-
const payload = item.raw?.custom_fields?.floriday_payload ??
|
|
59
|
-
item.raw?.floriday_payload ??
|
|
60
|
-
item.raw?.data ??
|
|
61
|
-
{};
|
|
62
|
-
const v = validateDraftForPublish(conceptMetArtikel(payload, artikelVan(item)));
|
|
63
|
-
if (!v.ok) {
|
|
64
|
-
toast.error("Nog niet compleet: " + v.missing.join(", "));
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
try {
|
|
68
|
-
const { buildAddTradeItemPayload } = await import("../../lib/floriday-payload.js");
|
|
69
|
-
const built = buildAddTradeItemPayload(conceptMetArtikel(payload, artikelVan(item)));
|
|
70
|
-
const res = await floriday.createTradeItem(connectionId, built);
|
|
71
|
-
await updateDraftMeta(productId, {
|
|
72
|
-
floridayLinkStatus: "published",
|
|
73
|
-
floridayId: res?.tradeItemId ?? null,
|
|
74
|
-
floridayLinkError: null,
|
|
75
|
-
lastFloridaySyncAt: new Date().toISOString(),
|
|
76
|
-
});
|
|
77
|
-
toast.success("Product staat nu actief op Floriday");
|
|
78
|
-
await onChanged();
|
|
79
|
-
}
|
|
80
|
-
catch (e) {
|
|
81
|
-
toast.error(e?.message ?? "Activeren op Floriday mislukt");
|
|
82
|
-
await onChanged();
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
53
|
const relinkButton = (_jsxs(Button, { size: "sm", variant: "outline", onClick: runRelink, disabled: relinking, children: [relinking ? (_jsx(Loader2, { className: "h-3 w-3 mr-1 animate-spin" })) : (_jsx(Link2, { className: "h-3 w-3 mr-1" })), "Nu opnieuw koppelen"] }));
|
|
86
54
|
if (rows.length === 0) {
|
|
87
|
-
return (_jsxs("div", { className: "p-8 text-center text-sm text-muted-foreground space-y-3", children: [_jsx("div", { children: "Alle
|
|
55
|
+
return (_jsxs("div", { className: "p-8 text-center text-sm text-muted-foreground space-y-3", children: [_jsx("div", { children: "Alle artikelen staan op Floriday." }), _jsx("div", { className: "flex justify-center", children: relinkButton })] }));
|
|
88
56
|
}
|
|
89
|
-
return (_jsxs("div", { className: "overflow-x-auto", children: [_jsxs("div", { className: "flex items-center justify-between gap-2
|
|
57
|
+
return (_jsxs("div", { className: "overflow-x-auto", children: [_jsxs("div", { className: "flex items-center justify-between gap-2 border-b border-border p-3", children: [_jsx("span", { className: "text-xs text-muted-foreground", children: "Koppelen gebeurt automatisch zodra Floriday-artikelen binnenkomen en elk uur op de achtergrond." }), relinkButton] }), _jsxs("table", { className: "w-full min-w-[900px] table-fixed text-sm [&_thead_th]:h-10 [&_thead_th]:px-2 [&_thead_th]:text-left [&_thead_th]:align-middle [&_thead_th]:text-[11px] [&_thead_th]:font-semibold [&_thead_th]:uppercase [&_thead_th]:tracking-wide [&_thead_th]:text-muted-foreground [&_tbody_td]:h-[60px] [&_tbody_td]:truncate [&_tbody_td]:px-2 [&_tbody_td]:py-0 [&_tbody_td]:align-middle", children: [_jsxs("colgroup", { children: [_jsx("col", { style: { width: "120px" } }), _jsx("col", { style: { width: "220px" } }), _jsx("col", { style: { width: "160px" } }), _jsx("col", {}), _jsx("col", { style: { width: "220px" } }), _jsx("col", { style: { width: "260px" } })] }), _jsx("thead", { children: _jsxs("tr", { className: "border-b border-border", children: [_jsx("th", { children: "Artikelnummer" }), _jsx("th", { children: "Artikel" }), _jsx("th", { children: "Floriday" }), _jsx("th", { children: "Reden" }), _jsx("th", { children: "Nog nodig" }), _jsx("th", { children: _jsx("span", { className: "sr-only", children: "Acties" }) })] }) }), _jsx("tbody", { children: rows.map((it) => {
|
|
90
58
|
const payload = it.raw?.custom_fields?.floriday_payload ??
|
|
91
59
|
it.raw?.floriday_payload ??
|
|
92
60
|
it.raw?.data ??
|
|
93
61
|
{};
|
|
94
62
|
const v = validateDraftForPublish(conceptMetArtikel(payload, artikelVan(it)));
|
|
95
|
-
return (_jsxs("tr", { className: "border-
|
|
63
|
+
return (_jsxs("tr", { className: "border-b border-border last:border-0 hover:bg-muted/40", children: [_jsx("td", { children: it.articleNumber || "—" }), _jsx("td", { children: it.name }), _jsx("td", { children: _jsx("span", { className: "text-muted-foreground", children: "Niet actief op Floriday" }) }), _jsx("td", { children: reasonFor(it) }), _jsx("td", { children: v.missing.length === 0 ? (_jsx("span", { className: "text-primary", children: "Klaar om te activeren" })) : (_jsxs("span", { className: "text-muted-foreground", title: v.missing.join(", "), children: [v.missing.slice(0, 2).join(", ").toLowerCase(), v.missing.length > 2 ? ` +${v.missing.length - 2}` : ""] })) }), _jsx("td", { children: _jsxs("div", { className: "flex justify-end gap-1", children: [_jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
|
|
96
64
|
const target = it.productId ?? it.localId ?? it.id;
|
|
97
65
|
navigate({ to: "/voorraad/$id", params: { id: target } });
|
|
98
|
-
}, children: [_jsx(Pencil, { className: "h-3 w-3
|
|
66
|
+
}, children: [_jsx(Pencil, { className: "mr-1 h-3.5 w-3.5" }), "Bewerken"] }), _jsxs(Button, { size: "sm", disabled: !v.ok, onClick: () => {
|
|
67
|
+
const target = it.productId ?? it.localId ?? it.id;
|
|
68
|
+
navigate({ to: "/voorraad/$id", params: { id: target }, state: { stuurOverzicht: true } });
|
|
69
|
+
}, children: [_jsx(Send, { className: "mr-1 h-3.5 w-3.5" }), "Activeer op Floriday"] })] }) })] }, it.id));
|
|
99
70
|
}) })] })] }));
|
|
100
71
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ValidationPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ValidationPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ValidationPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ValidationPage.tsx"],"names":[],"mappings":"AAiBA,oHAAoH;AACpH,wBAAgB,cAAc,gCAuC7B"}
|
|
@@ -1,13 +1,41 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useState } from "react";
|
|
2
3
|
import { Link } from "@tanstack/react-router";
|
|
3
|
-
import { Card } from "@flowselections/core";
|
|
4
|
+
import { Card, Skeleton } from "@flowselections/core";
|
|
4
5
|
import { ArrowLeft } from "lucide-react";
|
|
5
6
|
import { useFloridayConnection } from "../../hooks/useFloridayConnection.js";
|
|
6
|
-
import {
|
|
7
|
+
import { supabase } from "../../integrations/supabase/client.js";
|
|
8
|
+
import { productToTradeItem } from "../../hooks/useLocalTradeItems.js";
|
|
9
|
+
import { dbFoutTekst } from "../../lib/db-error-message.js";
|
|
7
10
|
import { ValidationOverviewTable } from "./ValidationOverviewTable.js";
|
|
8
|
-
import {
|
|
11
|
+
import { Pagination } from "../fs-ui/Pagination.js";
|
|
12
|
+
import { NoticeBar } from "../fs-ui/NoticeBar.js";
|
|
13
|
+
import { PageHeader } from "../fs-ui/PageHeader.js";
|
|
14
|
+
import { UpdatedAt } from "../fs-ui/UpdatedAt.js";
|
|
15
|
+
const GROOTTE = 50;
|
|
16
|
+
/** Floriday-controle: artikelen die nog niet op Floriday staan, per pagina van de server (geen limiet van 1000). */
|
|
9
17
|
export function ValidationPage() {
|
|
10
18
|
const { activeId } = useFloridayConnection();
|
|
11
|
-
const
|
|
12
|
-
|
|
19
|
+
const [pagina, setPagina] = useState(1);
|
|
20
|
+
const [items, setItems] = useState([]);
|
|
21
|
+
const [totaal, setTotaal] = useState(0);
|
|
22
|
+
const [loading, setLoading] = useState(true);
|
|
23
|
+
const [fout, setFout] = useState(null);
|
|
24
|
+
const [bijgewerkt, setBijgewerkt] = useState(null);
|
|
25
|
+
const reload = useCallback(async () => {
|
|
26
|
+
const { data, error } = await supabase.rpc("voorraad_validatie_pagina", { p_pagina: pagina, p_grootte: GROOTTE });
|
|
27
|
+
if (error) {
|
|
28
|
+
setFout(dbFoutTekst(error));
|
|
29
|
+
setLoading(false);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const r = data;
|
|
33
|
+
setItems((r.rijen ?? []).map(productToTradeItem));
|
|
34
|
+
setTotaal(Number(r.totaal ?? 0));
|
|
35
|
+
setFout(null);
|
|
36
|
+
setLoading(false);
|
|
37
|
+
setBijgewerkt(new Date());
|
|
38
|
+
}, [pagina]);
|
|
39
|
+
useEffect(() => { void reload(); }, [reload]);
|
|
40
|
+
return (_jsxs("main", { className: "flex w-full min-w-0 flex-col gap-4 overflow-x-clip p-4 pb-[calc(7rem+env(safe-area-inset-bottom))] sm:p-6 sm:pb-24", children: [_jsx(PageHeader, { crumb: _jsxs(Link, { to: "/voorraad", className: "inline-flex items-center gap-1 hover:text-foreground", children: [_jsx(ArrowLeft, { className: "h-4 w-4" }), " Voorraad"] }), title: "Voorraadcontrole", updated: _jsx(UpdatedAt, { at: bijgewerkt, info: "Artikelen die nog niet op Floriday staan. Koppelen gebeurt automatisch zodra Floriday-artikelen binnenkomen." }) }), _jsx(NoticeBar, { items: fout ? [fout] : [] }), _jsx(Card, { className: "overflow-hidden rounded-xl shadow-sm", children: loading ? (_jsx("div", { className: "space-y-2 p-4", "aria-busy": "true", children: Array.from({ length: 8 }).map((_, i) => _jsx(Skeleton, { className: "h-10 w-full rounded-lg" }, i)) })) : (_jsx(ValidationOverviewTable, { items: items, connectionId: activeId, onChanged: reload })) }), _jsx(Pagination, { page: pagina, pageSize: GROOTTE, totalCount: totaal, onPage: setPagina })] }));
|
|
13
41
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* "Voorraad — Controle Floriday-artikelen": alleen voor admin en superadmin
|
|
3
|
+
* (`voorraad_is_beheerder` in de database). Inklapbaar; ververst na de knop en zolang een controle loopt.
|
|
4
|
+
*/
|
|
2
5
|
export declare function VoorraadBeheerCard(): import("react").JSX.Element | null;
|
|
3
6
|
//# sourceMappingURL=VoorraadBeheerCard.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VoorraadBeheerCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadBeheerCard.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"VoorraadBeheerCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadBeheerCard.tsx"],"names":[],"mappings":"AA8BA;;;GAGG;AACH,wBAAgB,kBAAkB,uCAoFjC"}
|
|
@@ -1,28 +1,48 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useEffect, useState } from "react";
|
|
3
|
-
import { Button, Card,
|
|
2
|
+
import { lazy, Suspense, useCallback, useEffect, useState } from "react";
|
|
3
|
+
import { Button, Card, Skeleton, toast } from "@flowselections/core";
|
|
4
|
+
import { ChevronDown, RefreshCw } from "lucide-react";
|
|
4
5
|
import { supabase } from "../../integrations/supabase/client.js";
|
|
5
6
|
import { dbFoutTekst } from "../../lib/db-error-message.js";
|
|
6
7
|
import { toonVersieBeheer } from "../../lib/floriday-versie-ronde.js";
|
|
7
8
|
import { floridayVersiesControleren } from "../../lib/floriday-versie-ronde.functions.js";
|
|
9
|
+
import { useFloridayConnection } from "../../hooks/useFloridayConnection.js";
|
|
10
|
+
// Zware beheeronderdelen laden pas als je het blok openklapt.
|
|
11
|
+
const KoppelingStatusPanel = lazy(() => import("./KoppelingStatusPanel.js").then((m) => ({ default: m.KoppelingStatusPanel })));
|
|
12
|
+
const StockPushMonitor = lazy(() => import("./StockPushMonitor.js").then((m) => ({ default: m.StockPushMonitor })));
|
|
13
|
+
const FloridayTakeoverPanel = lazy(() => import("./FloridayTakeoverPanel.js").then((m) => ({ default: m.FloridayTakeoverPanel })));
|
|
8
14
|
const STATUS_TEKST = {
|
|
9
15
|
geslaagd: "geslaagd",
|
|
10
16
|
onvolledig: "nog niet compleet (wordt automatisch opnieuw geprobeerd)",
|
|
11
17
|
mislukt: "mislukt (wordt automatisch opnieuw geprobeerd)",
|
|
12
18
|
};
|
|
13
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* "Voorraad — Controle Floriday-artikelen": alleen voor admin en superadmin
|
|
21
|
+
* (`voorraad_is_beheerder` in de database). Inklapbaar; ververst na de knop en zolang een controle loopt.
|
|
22
|
+
*/
|
|
14
23
|
export function VoorraadBeheerCard() {
|
|
15
24
|
const [status, setStatus] = useState(null);
|
|
16
25
|
const [bezig, setBezig] = useState(false);
|
|
26
|
+
const [open, setOpen] = useState(false);
|
|
27
|
+
const [versie, setVersie] = useState(0);
|
|
28
|
+
const conn = useFloridayConnection();
|
|
17
29
|
const laad = useCallback(async () => {
|
|
18
30
|
const { data, error } = await supabase.rpc("voorraad_floriday_versieronde_status");
|
|
19
31
|
if (error) {
|
|
32
|
+
console.warn("Beheerstatus voorraad niet geladen", error);
|
|
20
33
|
setStatus(null);
|
|
21
34
|
return;
|
|
22
35
|
}
|
|
23
36
|
setStatus(data);
|
|
24
37
|
}, []);
|
|
25
38
|
useEffect(() => { void laad(); }, [laad]);
|
|
39
|
+
// Stil verversen zolang een controle loopt.
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!status?.bezig)
|
|
42
|
+
return;
|
|
43
|
+
const t = setInterval(() => void laad(), 5000);
|
|
44
|
+
return () => clearInterval(t);
|
|
45
|
+
}, [status?.bezig, laad]);
|
|
26
46
|
if (!toonVersieBeheer(status) || !status?.heeft_koppeling)
|
|
27
47
|
return null;
|
|
28
48
|
const start = async () => {
|
|
@@ -30,15 +50,16 @@ export function VoorraadBeheerCard() {
|
|
|
30
50
|
try {
|
|
31
51
|
await floridayVersiesControleren();
|
|
32
52
|
toast.success("Controle gestart. Dit loopt op de achtergrond.");
|
|
33
|
-
await laad();
|
|
34
53
|
}
|
|
35
54
|
catch (e) {
|
|
36
55
|
toast.error(dbFoutTekst(e));
|
|
37
56
|
}
|
|
38
57
|
finally {
|
|
39
58
|
setBezig(false);
|
|
59
|
+
setVersie((v) => v + 1);
|
|
60
|
+
await laad();
|
|
40
61
|
}
|
|
41
62
|
};
|
|
42
63
|
const l = status.laatste;
|
|
43
|
-
return (
|
|
64
|
+
return (_jsxs(Card, { className: "overflow-hidden rounded-xl shadow-sm", children: [_jsxs("button", { type: "button", onClick: () => setOpen((o) => !o), "aria-expanded": open, className: "flex w-full items-center justify-between gap-3 px-5 py-4 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: [_jsxs("div", { children: [_jsx("div", { className: "text-base font-semibold", children: "Voorraad \u2014 Controle Floriday-artikelen" }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Alleen voor beheerders \u00B7 Laatste controle ", l ? new Date(l.uitgevoerd_at).toLocaleString("nl-NL") : "nog niet uitgevoerd"] })] }), _jsx(ChevronDown, { className: "h-4 w-4 shrink-0 transition-transform motion-reduce:transition-none " + (open ? "rotate-180" : ""), "aria-hidden": true })] }), open && (_jsxs("div", { className: "space-y-4 border-t border-border px-5 py-4 text-sm", children: [_jsxs("section", { className: "space-y-2", children: [_jsx("p", { className: "text-muted-foreground", children: "Loopt vanzelf elke nacht. Oude versies van Floriday-artikelen worden geen zichtbaar artikel." }), _jsxs("p", { children: ["Laatste controle:", " ", l ? `${new Date(l.uitgevoerd_at).toLocaleString("nl-NL")} — ${STATUS_TEKST[l.status] ?? "onbekend"}` : "nog niet uitgevoerd", status.bezig && " · nu bezig"] }), l?.melding && l.status !== "geslaagd" && _jsx("p", { className: "text-muted-foreground", children: l.melding }), _jsxs("p", { className: "tabular-nums", children: ["Vervangen in Floriday: ", status.vervangen ?? 0, " \u00B7 Vervallen: ", status.vervallen ?? 0, " \u00B7 Apart gezet (geen antwoord van Floriday): ", status.onbekend ?? 0] }), _jsxs(Button, { size: "sm", variant: "outline", className: "h-8", onClick: () => void start(), disabled: bezig || status.bezig, children: [_jsx(RefreshCw, { className: "mr-2 h-4 w-4 " + (bezig || status.bezig ? "animate-spin motion-reduce:animate-none" : "") }), bezig ? "Bezig…" : "Nu opnieuw controleren"] })] }), _jsx(Suspense, { fallback: _jsx(Skeleton, { className: "h-32 w-full rounded-xl" }), children: _jsxs("div", { className: "space-y-4", children: [_jsx(KoppelingStatusPanel, { connectionId: conn.activeId ?? null, onChanged: () => void laad() }), _jsx(StockPushMonitor, { connectionId: conn.activeId ?? null }), _jsx(FloridayTakeoverPanel, { connectionId: conn.activeId ?? null })] }, versie) })] }))] }));
|
|
44
65
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VoorraadPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"VoorraadPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadPage.tsx"],"names":[],"mappings":"AA2EA,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAsDnD,wBAAgB,YAAY,gCA0M3B"}
|