@flowselections/floriday-voorraad 1.0.152 → 1.0.155
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.map +1 -1
- package/dist-lib/components/fs-ui/DataTable.js +8 -8
- package/dist-lib/components/fs-ui/FilterChips.d.ts +1 -1
- package/dist-lib/components/fs-ui/FilterChips.d.ts.map +1 -1
- package/dist-lib/components/fs-ui/FilterChips.js +3 -3
- 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/KpiRow.d.ts +2 -2
- package/dist-lib/components/fs-ui/KpiRow.js +3 -3
- 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 +6 -5
- package/dist-lib/components/fs-ui/PageHeader.d.ts.map +1 -1
- package/dist-lib/components/fs-ui/PageHeader.js +4 -3
- 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 +3 -5
- package/dist-lib/components/fs-ui/PillTabs.d.ts.map +1 -1
- package/dist-lib/components/fs-ui/PillTabs.js +25 -15
- 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 +1 -4
- package/dist-lib/components/fs-ui/SearchInput.d.ts.map +1 -1
- package/dist-lib/components/fs-ui/SearchInput.js +23 -19
- 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/Toolbar.d.ts +7 -3
- package/dist-lib/components/fs-ui/Toolbar.d.ts.map +1 -1
- package/dist-lib/components/fs-ui/Toolbar.js +6 -3
- package/dist-lib/components/fs-ui/UpdatedAt.d.ts +6 -2
- package/dist-lib/components/fs-ui/UpdatedAt.d.ts.map +1 -1
- package/dist-lib/components/fs-ui/UpdatedAt.js +16 -8
- package/dist-lib/components/voorraad/FloridayConnectionNotice.d.ts +2 -1
- package/dist-lib/components/voorraad/FloridayConnectionNotice.d.ts.map +1 -1
- package/dist-lib/components/voorraad/FloridayConnectionNotice.js +9 -4
- package/dist-lib/components/voorraad/FustenPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/FustenPage.js +90 -26
- 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 +41 -36
- package/dist-lib/components/voorraad/Sneltoetsen.d.ts +1 -1
- package/dist-lib/components/voorraad/Sneltoetsen.d.ts.map +1 -1
- package/dist-lib/components/voorraad/Sneltoetsen.js +12 -13
- 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 +30 -6
- 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/ValidationOverviewTable.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ValidationOverviewTable.js +7 -38
- package/dist-lib/components/voorraad/ValidationPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ValidationPage.js +3 -3
- package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadPage.js +28 -10
- package/dist-lib/components/voorraad/artikel/ArtikelPagina.d.ts.map +1 -1
- package/dist-lib/components/voorraad/artikel/ArtikelPagina.js +16 -3
- 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.js +10 -10
- package/dist-lib/integrations/supabase/types.d.ts +102 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -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 +4 -1
- package/dist-lib/lib/voorraad-lijst.d.ts.map +1 -1
- package/dist-lib/lib/voorraad-lijst.js +16 -5
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from "react";
|
|
3
|
-
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@flowselections/core";
|
|
4
3
|
import { isTypen } from "../fs-ui/SearchInput.js";
|
|
4
|
+
import { ShortcutHelp } from "../fs-ui/ShortcutHelp.js";
|
|
5
5
|
/**
|
|
6
6
|
* Sneltoetsen zoals in Verkooporders: N = nieuw (alleen als er een maak-actie is),
|
|
7
7
|
* ? = overzicht. "/" (zoeken), J/K/Enter (rijen) en Esc zitten in SearchInput en DataTable.
|
|
@@ -28,18 +28,17 @@ export function Sneltoetsen({ onNieuw, nieuwLabel = "Nieuw artikel" }) {
|
|
|
28
28
|
window.addEventListener("keydown", h);
|
|
29
29
|
return () => window.removeEventListener("keydown", h);
|
|
30
30
|
}, [onNieuw]);
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
["
|
|
34
|
-
["J", "Volgende rij"],
|
|
35
|
-
["
|
|
36
|
-
["
|
|
37
|
-
["
|
|
38
|
-
["?", "Dit overzicht"],
|
|
31
|
+
const shortcuts = [
|
|
32
|
+
[["/"], "Zoeken"],
|
|
33
|
+
...(onNieuw ? [[["N"], nieuwLabel]] : []),
|
|
34
|
+
[["J", "K"], "Volgende / vorige rij"],
|
|
35
|
+
[["Enter"], "Geselecteerde rij openen"],
|
|
36
|
+
[["Esc"], "Zoeken wissen / sluiten"],
|
|
37
|
+
[["?"], "Dit overzicht"],
|
|
39
38
|
];
|
|
40
|
-
return
|
|
39
|
+
return _jsx(ShortcutHelp, { open: open, onOpenChange: setOpen, shortcuts: shortcuts });
|
|
41
40
|
}
|
|
42
|
-
/** Kleine sneltoets-hint in een knop
|
|
41
|
+
/** Kleine sneltoets-hint in een primaire knop, exact naar Verkooporders. */
|
|
43
42
|
export function Kbd({ children }) {
|
|
44
|
-
return _jsx("kbd", { className: "ml-2 hidden rounded
|
|
43
|
+
return _jsx("kbd", { "aria-hidden": true, className: "ml-2 hidden rounded border border-primary-foreground/30 bg-primary-foreground/15 px-1.5 font-mono text-[10px] leading-4 text-primary-foreground sm:inline-block", children: children });
|
|
45
44
|
}
|
|
@@ -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,11 +1,11 @@
|
|
|
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
11
|
/** Bron van een voorraadwijziging in gewone woorden; onbekende codes niet tonen. */
|
|
@@ -23,7 +23,8 @@ function label(it, names) {
|
|
|
23
23
|
return [code, name].filter(Boolean).join(" – ");
|
|
24
24
|
return "onbekend artikel";
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
/** Open voorraadbewakingsmeldingen, met namen en "Gezien". */
|
|
27
|
+
export function useStockIntegrityAlerts() {
|
|
27
28
|
const [alerts, setAlerts] = useState([]);
|
|
28
29
|
const [names, setNames] = useState(new Map());
|
|
29
30
|
const load = useCallback(async () => {
|
|
@@ -88,7 +89,30 @@ export function StockIntegrityAlert() {
|
|
|
88
89
|
}
|
|
89
90
|
void load();
|
|
90
91
|
};
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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 };
|
|
94
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
|
}
|
|
@@ -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
|
/**
|
|
@@ -52,51 +50,22 @@ export function ValidationOverviewTable({ items, connectionId, onChanged, }) {
|
|
|
52
50
|
}
|
|
53
51
|
return "Nog geen overeenkomend Floriday-artikel gevonden (streepjescode, artikelcode of naam)";
|
|
54
52
|
};
|
|
55
|
-
const publish = async (item) => {
|
|
56
|
-
const productId = item.productId ?? item.localId;
|
|
57
|
-
if (!productId || !connectionId)
|
|
58
|
-
return;
|
|
59
|
-
const payload = item.raw?.custom_fields?.floriday_payload ??
|
|
60
|
-
item.raw?.floriday_payload ??
|
|
61
|
-
item.raw?.data ??
|
|
62
|
-
{};
|
|
63
|
-
const v = validateDraftForPublish(conceptMetArtikel(payload, artikelVan(item)));
|
|
64
|
-
if (!v.ok) {
|
|
65
|
-
toast.error("Nog niet compleet: " + v.missing.join(", "));
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
try {
|
|
69
|
-
const { buildAddTradeItemPayload } = await import("../../lib/floriday-payload.js");
|
|
70
|
-
const built = buildAddTradeItemPayload(conceptMetArtikel(payload, artikelVan(item)));
|
|
71
|
-
const res = await floriday.createTradeItem(connectionId, built);
|
|
72
|
-
await updateDraftMeta(productId, {
|
|
73
|
-
floridayLinkStatus: "published",
|
|
74
|
-
floridayId: res?.tradeItemId ?? null,
|
|
75
|
-
floridayLinkError: null,
|
|
76
|
-
lastFloridaySyncAt: new Date().toISOString(),
|
|
77
|
-
});
|
|
78
|
-
toast.success("Artikel staat nu op Floriday");
|
|
79
|
-
await onChanged();
|
|
80
|
-
}
|
|
81
|
-
catch (e) {
|
|
82
|
-
console.warn(e);
|
|
83
|
-
toast.error("Op Floriday zetten is niet gelukt. Probeer het later opnieuw.");
|
|
84
|
-
await onChanged();
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
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"] }));
|
|
88
54
|
if (rows.length === 0) {
|
|
89
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 })] }));
|
|
90
56
|
}
|
|
91
|
-
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) => {
|
|
92
58
|
const payload = it.raw?.custom_fields?.floriday_payload ??
|
|
93
59
|
it.raw?.floriday_payload ??
|
|
94
60
|
it.raw?.data ??
|
|
95
61
|
{};
|
|
96
62
|
const v = validateDraftForPublish(conceptMetArtikel(payload, artikelVan(it)));
|
|
97
|
-
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: () => {
|
|
98
64
|
const target = it.productId ?? it.localId ?? it.id;
|
|
99
65
|
navigate({ to: "/voorraad/$id", params: { id: target } });
|
|
100
|
-
}, 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));
|
|
101
70
|
}) })] })] }));
|
|
102
71
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ValidationPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ValidationPage.tsx"],"names":[],"mappings":"AAiBA,oHAAoH;AACpH,wBAAgB,cAAc,
|
|
1
|
+
{"version":3,"file":"ValidationPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ValidationPage.tsx"],"names":[],"mappings":"AAiBA,oHAAoH;AACpH,wBAAgB,cAAc,gCAuC7B"}
|
|
@@ -8,10 +8,10 @@ import { supabase } from "../../integrations/supabase/client.js";
|
|
|
8
8
|
import { productToTradeItem } from "../../hooks/useLocalTradeItems.js";
|
|
9
9
|
import { dbFoutTekst } from "../../lib/db-error-message.js";
|
|
10
10
|
import { ValidationOverviewTable } from "./ValidationOverviewTable.js";
|
|
11
|
-
import {
|
|
11
|
+
import { Pagination } from "../fs-ui/Pagination.js";
|
|
12
|
+
import { NoticeBar } from "../fs-ui/NoticeBar.js";
|
|
12
13
|
import { PageHeader } from "../fs-ui/PageHeader.js";
|
|
13
14
|
import { UpdatedAt } from "../fs-ui/UpdatedAt.js";
|
|
14
|
-
import { InlineAlert } from "../fs-ui/InlineAlert.js";
|
|
15
15
|
const GROOTTE = 50;
|
|
16
16
|
/** Floriday-controle: artikelen die nog niet op Floriday staan, per pagina van de server (geen limiet van 1000). */
|
|
17
17
|
export function ValidationPage() {
|
|
@@ -37,5 +37,5 @@ export function ValidationPage() {
|
|
|
37
37
|
setBijgewerkt(new Date());
|
|
38
38
|
}, [pagina]);
|
|
39
39
|
useEffect(() => { void reload(); }, [reload]);
|
|
40
|
-
return (_jsxs("main", { className: "flex flex-col gap-4 p-4 pb-
|
|
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 })] }));
|
|
41
41
|
}
|
|
@@ -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,gCA+M3B"}
|
|
@@ -3,23 +3,22 @@ import { TwijfelKiezen } from "./artikel/TwijfelKiezen.js";
|
|
|
3
3
|
import { useCallback, useEffect, useMemo, useState, useSyncExternalStore } from "react";
|
|
4
4
|
import { useNavigate } from "@tanstack/react-router";
|
|
5
5
|
import { Button, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@flowselections/core";
|
|
6
|
-
import {
|
|
6
|
+
import { Archive, ArrowDownToLine, ClipboardCheck, Columns3, Euro, HelpCircle, Layers, Link2Off, ListChecks, MoreHorizontal, Package, PackageSearch, PackageX, Plus, Send, TrendingDown, } from "lucide-react";
|
|
7
7
|
import { supabase } from "../../integrations/supabase/client.js";
|
|
8
8
|
import { pageRequestGuard } from "../../lib/request-guard.js";
|
|
9
9
|
import { useFloridayConnection } from "../../hooks/useFloridayConnection.js";
|
|
10
10
|
import { useEnsureVoorraadDefaults } from "../../hooks/useEnsureVoorraadDefaults.js";
|
|
11
11
|
import { useVoorraadLijst } from "../../hooks/useVoorraadLijst.js";
|
|
12
|
-
import { aantal, FILTER_LABEL, meldingen, volgendeSortering, zichtbareFilters, } from "../../lib/voorraad-lijst.js";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
12
|
+
import { aantal, FILTER_LABEL, FILTER_HINT, meldingen, volgendeSortering, zichtbareFilters, } from "../../lib/voorraad-lijst.js";
|
|
13
|
+
import { floridayConnectionNotice } from "./FloridayConnectionNotice.js";
|
|
14
|
+
import { useStockIntegrityNotices } from "./StockIntegrityAlert.js";
|
|
15
15
|
import { TelmomentButton } from "./TelmomentButton.js";
|
|
16
|
-
import { VoorraadPagination } from "./VoorraadPagination.js";
|
|
17
16
|
import { InventorySettingsDrawer } from "./InventorySettingsDrawer.js";
|
|
18
17
|
import { OPTIONELE_KOLOMMEN, VoorraadLijstTabel } from "./lijst/VoorraadLijstTabel.js";
|
|
19
18
|
import { AanpassenDialog, AfboekenDialog } from "./lijst/SnelActieDialogen.js";
|
|
20
19
|
import { LijstExportKnop } from "./lijst/LijstExportKnop.js";
|
|
21
20
|
import { ImporteerKnop } from "./lijst/ImporteerKnop.js";
|
|
22
|
-
import {
|
|
21
|
+
import { Sneltoetsen } from "./Sneltoetsen.js";
|
|
23
22
|
import { PageHeader } from "../fs-ui/PageHeader.js";
|
|
24
23
|
import { UpdatedAt } from "../fs-ui/UpdatedAt.js";
|
|
25
24
|
import { KpiRow } from "../fs-ui/KpiRow.js";
|
|
@@ -27,7 +26,12 @@ import { KpiTile } from "../fs-ui/KpiTile.js";
|
|
|
27
26
|
import { PillTabs } from "../fs-ui/PillTabs.js";
|
|
28
27
|
import { Toolbar } from "../fs-ui/Toolbar.js";
|
|
29
28
|
import { SearchInput } from "../fs-ui/SearchInput.js";
|
|
30
|
-
import {
|
|
29
|
+
import { NoticeBar } from "../fs-ui/NoticeBar.js";
|
|
30
|
+
import { FiltersButton } from "../fs-ui/FiltersButton.js";
|
|
31
|
+
import { FilterChips } from "../fs-ui/FilterChips.js";
|
|
32
|
+
import { FilterPanel, FilterGroup } from "../fs-ui/FilterPanel.js";
|
|
33
|
+
import { Pagination } from "../fs-ui/Pagination.js";
|
|
34
|
+
import { HeaderPrimary } from "../fs-ui/HeaderButton.js";
|
|
31
35
|
import { EmptyState } from "../fs-ui/EmptyState.js";
|
|
32
36
|
import { MobilePrimaryAction } from "../fs-ui/MobilePrimaryAction.js";
|
|
33
37
|
import { DataTable } from "../fs-ui/DataTable.js";
|
|
@@ -98,6 +102,8 @@ export function VoorraadPage() {
|
|
|
98
102
|
const [selected, setSelected] = useState(new Set());
|
|
99
103
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
100
104
|
const [telmomentOpen, setTelmomentOpen] = useState(false);
|
|
105
|
+
const [filterOpen, setFilterOpen] = useState(false);
|
|
106
|
+
const integriteit = useStockIntegrityNotices();
|
|
101
107
|
const [aanpassen, setAanpassen] = useState(null);
|
|
102
108
|
const [afboeken, setAfboeken] = useState(null);
|
|
103
109
|
const { kolommen, toggle: toggleKolom } = useKolommen();
|
|
@@ -113,7 +119,7 @@ export function VoorraadPage() {
|
|
|
113
119
|
// Twijfelgevallen bestaan alleen bij een koppeling; tonen als er iets te kiezen is of als je er al staat.
|
|
114
120
|
const tabs = filters
|
|
115
121
|
.filter((f) => f !== "twijfel" || heeftKoppeling || (data?.tellers?.twijfel ?? 0) > 0 || filter === "twijfel")
|
|
116
|
-
.map((f) => ({ value: f, label: FILTER_LABEL[f], icon: FILTER_ICON[f], count: data?.tellers?.[f] ?? null }));
|
|
122
|
+
.map((f) => ({ value: f, label: FILTER_LABEL[f], icon: FILTER_ICON[f], hint: FILTER_HINT[f], count: data?.tellers?.[f] ?? null }));
|
|
117
123
|
const toggleSelect = (id) => setSelected((cur) => {
|
|
118
124
|
const next = new Set(cur);
|
|
119
125
|
if (next.has(id))
|
|
@@ -125,8 +131,20 @@ export function VoorraadPage() {
|
|
|
125
131
|
const toggleAll = (ids) => setSelected((cur) => (ids.length > 0 && ids.every((id) => cur.has(id)) ? new Set() : new Set(ids)));
|
|
126
132
|
const selectieBalk = selected.size > 0 && filter !== "twijfel";
|
|
127
133
|
const kpiLaden = kpi === null;
|
|
128
|
-
|
|
129
|
-
|
|
134
|
+
// Meldingen: één regel per melding, direct onder de kop.
|
|
135
|
+
const verbindingTekst = floridayConnectionNotice(conn.status, conn.error);
|
|
136
|
+
const noticeItems = [
|
|
137
|
+
...(guardTrips.length > 0 ? [_jsxs("span", { children: ["De pagina is gestopt met laden. ", _jsx("span", { role: "button", tabIndex: 0, className: "font-medium underline underline-offset-2", onClick: () => window.location.reload(), children: "Opnieuw laden" })] }, "guard")] : []),
|
|
138
|
+
...(error ? [_jsxs("span", { children: ["De voorraad laden lukt niet. ", _jsx("span", { role: "button", tabIndex: 0, className: "font-medium underline underline-offset-2", onClick: (e) => { e.stopPropagation(); void reload(); }, children: "Opnieuw proberen" })] }, "err")] : []),
|
|
139
|
+
...integriteit.items,
|
|
140
|
+
...(verbindingTekst ? [verbindingTekst] : []),
|
|
141
|
+
...melding.map((m) => m.actie === "telmoment" ? (_jsxs("span", { className: "inline-flex min-w-0 max-w-full items-center gap-2", children: [_jsx("span", { className: "truncate", children: m.tekst }), _jsx("span", { role: "button", tabIndex: 0, className: "shrink-0 font-medium underline underline-offset-2", onClick: (e) => { e.stopPropagation(); setTelmomentOpen(true); }, onKeyDown: (e) => { if (e.key === "Enter") {
|
|
142
|
+
e.stopPropagation();
|
|
143
|
+
setTelmomentOpen(true);
|
|
144
|
+
} }, children: "Telmoment instellen" })] }, m.tekst)) : m.filter ? (_jsx("span", { role: "button", tabIndex: 0, className: "underline-offset-2 hover:underline", onClick: (e) => { e.stopPropagation(); setFilter(m.filter); }, children: m.tekst }, m.tekst)) : m.tekst),
|
|
145
|
+
];
|
|
146
|
+
const leeg = zoek || filter !== "alles" ? (_jsx(EmptyState, { icon: _jsx(PackageSearch, { className: "h-6 w-6" }), title: "Niets gevonden", text: zoek ? `Geen artikelen voor “${zoek}”${filter !== "alles" ? ` in ${FILTER_HINT[filter].toLowerCase()}` : ""}.` : "Geen artikelen in deze weergave.", action: _jsx(Button, { variant: "outline", size: "sm", onClick: () => { setZoek(""); setFilter("alles"); }, children: "Filters wissen" }) })) : (_jsx(EmptyState, { icon: _jsx(Package, { className: "h-6 w-6" }), title: "Nog geen artikelen", text: "Maak je eerste artikel aan of importeer een lijst.", action: _jsxs(Button, { size: "sm", onClick: nieuw, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Nieuw artikel"] }) }));
|
|
147
|
+
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(Sneltoetsen, { onNieuw: nieuw }), _jsx(PageHeader, { title: "Voorraad", updated: _jsx(UpdatedAt, { at: data?.bijgewerkt ?? null }), actions: _jsxs(_Fragment, { children: [_jsx(ImporteerKnop, { connectionId: conn.activeId ?? null, onImported: reload }), _jsx(LijstExportKnop, { vraag: { zoek, filter, sort: sorteer.sort, oplopend: sorteer.oplopend } }), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "sm", className: "h-10 w-10 p-0 sm:h-9 sm:w-auto sm:px-3", "aria-label": "Meer acties", children: [_jsx(MoreHorizontal, { className: "h-4 w-4 sm:mr-2" }), _jsx("span", { className: "hidden sm:inline", children: "Meer" })] }) }), _jsxs(DropdownMenuContent, { align: "end", className: "w-56", children: [_jsxs(DropdownMenuItem, { onSelect: () => setTelmomentOpen(true), children: [_jsx(ClipboardCheck, { className: "mr-2 h-4 w-4" }), "Telmoment vastleggen"] }), heeftKoppeling && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { onSelect: () => navigate({ to: "/voorraad/validatie" }), children: [_jsx(ListChecks, { className: "mr-2 h-4 w-4" }), "Controle koppeling"] }), _jsxs(DropdownMenuItem, { onSelect: () => navigate({ to: "/voorraad/publicaties" }), children: [_jsx(Send, { className: "mr-2 h-4 w-4" }), "Verzonden naar Floriday"] })] }))] })] }), _jsx(HeaderPrimary, { icon: _jsx(Plus, {}), label: "Nieuw artikel", kbd: "N", onClick: nieuw })] }) }), _jsx(TelmomentButton, { verbergKnop: true, open: telmomentOpen, onOpenChange: setTelmomentOpen, onSaved: reload }), _jsx(NoticeBar, { items: noticeItems }), integriteit.dialog, _jsxs(KpiRow, { children: [_jsx(KpiTile, { title: "Artikelen", icon: _jsx(Package, {}), loading: kpiLaden, value: aantal(kpi?.artikelen ?? 0), sub: "Actief, zonder gearchiveerd", onClick: () => setFilter("alles"), active: false }), _jsx(KpiTile, { title: "Onder minimum", icon: _jsx(TrendingDown, {}), tone: (kpi?.onder_minimum ?? 0) > 0 ? "warning" : "default", loading: kpiLaden, value: aantal(kpi?.onder_minimum ?? 0), sub: (kpi?.onder_minimum ?? 0) > 0 ? _jsx("span", { className: "text-destructive", children: "Bijbestellen of bijmaken" }) : "Alles boven het minimum", onClick: () => setFilter("onder_minimum"), active: filter === "onder_minimum" }), _jsx(KpiTile, { title: "Vrij beschikbaar", icon: _jsx(Layers, {}), loading: kpiLaden, value: aantal(kpi?.vrij ?? 0), sub: "Stuks, losse artikelen" }), _jsx(KpiTile, { title: "Inkomend", icon: _jsx(ArrowDownToLine, {}), loading: kpiLaden, value: aantal(kpi?.inkomend ?? 0), sub: "Stuks verwacht" })] }), _jsx(PillTabs, { tabs: tabs, value: filter, onChange: setFilter, label: "Filter" }), _jsx(Toolbar, { filters: _jsx(FiltersButton, { count: filter !== "alles" ? 1 : 0, onClick: () => setFilterOpen(true) }), chips: _jsx(FilterChips, { chips: filter !== "alles" ? [{ key: "weergave", label: FILTER_HINT[filter], icon: FILTER_ICON[filter], onRemove: () => setFilter("alles") }] : [] }), right: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "sm", className: "h-9 px-3", "aria-label": "Kolommen kiezen", children: [_jsx(Columns3, { className: "h-4 w-4 sm:mr-2" }), _jsx("span", { className: "hidden sm:inline", children: "Kolommen" })] }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsx(DropdownMenuLabel, { children: "Kolommen kiezen" }), OPTIONELE_KOLOMMEN.map((c) => (_jsx(DropdownMenuCheckboxItem, { checked: kolommen.has(c.key), onCheckedChange: () => toggleKolom(c.key), children: c.label }, c.key)))] })] }), children: _jsx(SearchInput, { value: zoek, onChange: setZoek, placeholder: "Zoek op naam, artikelnummer of barcode" }) }), selectieBalk && (_jsxs("div", { className: "sticky top-2 z-20 flex flex-wrap items-center gap-2 rounded-lg border border-border bg-card px-4 py-2 text-sm shadow-lg", children: [_jsxs("span", { className: "font-medium tabular-nums", children: [selected.size, " gekozen"] }), _jsx(Button, { size: "sm", className: "h-8", onClick: () => setDrawerOpen(true), children: "Voorraadsturing instellen" }), _jsx(Button, { size: "sm", variant: "ghost", className: "h-8", onClick: () => setSelected(new Set()), children: "Keuze wissen" })] })), filter === "twijfel" ? (_jsx(TwijfelTabel, { rijen: data?.twijfel_rijen ?? [], loading: loading, onGekozen: () => void reload() })) : (_jsx(VoorraadLijstTabel, { rijen: data?.rijen ?? [], loading: loading, sort: sorteer.sort, oplopend: sorteer.oplopend, onSort: (k) => setSorteer((cur) => volgendeSortering(cur, k)), kolommen: kolommen, selected: selected, onToggleSelect: toggleSelect, onToggleAll: toggleAll, onAanpassen: setAanpassen, onAfboeken: setAfboeken, heeftKoppeling: heeftKoppeling, leeg: leeg })), _jsx(Pagination, { totalCount: data?.totaal ?? 0, page: pagina, pageSize: grootte, onPage: setPagina, onPageSize: setGrootte }), _jsx(FilterPanel, { open: filterOpen, onOpenChange: setFilterOpen, onClear: () => setFilter("alles"), children: _jsx(FilterGroup, { title: "Weergave", value: filter, onChange: setFilter, options: tabs.map((t) => ({ value: t.value, label: FILTER_HINT[t.value] })) }) }), _jsx(AanpassenDialog, { rij: aanpassen, onClose: () => setAanpassen(null), onDone: reload }), _jsx(AfboekenDialog, { rij: afboeken, connectionId: stockConnId, onClose: () => setAfboeken(null), onDone: reload }), _jsx(InventorySettingsDrawer, { open: drawerOpen, onOpenChange: setDrawerOpen, connectionId: conn.activeId ?? null, productIds: [...selected], onSaved: reload }), _jsx(MobilePrimaryAction, { onClick: nieuw, hidden: selectieBalk, icon: _jsx(Plus, {}), children: "Nieuw artikel" })] }));
|
|
130
148
|
}
|
|
131
149
|
function TwijfelTabel({ rijen, loading, onGekozen }) {
|
|
132
150
|
const [open, setOpen] = useState(null);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ArtikelPagina.d.ts","sourceRoot":"","sources":["../../../../src/components/voorraad/artikel/ArtikelPagina.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ArtikelPagina.d.ts","sourceRoot":"","sources":["../../../../src/components/voorraad/artikel/ArtikelPagina.tsx"],"names":[],"mappings":"AAmGA,wBAAgB,aAAa,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,cAAc,EAAE,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,+BA6enI"}
|