@flowselections/floriday-voorraad 1.0.152 → 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.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 +25 -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 +26 -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 +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 +1 -1
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
/**
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Werkbalk exact naar Verkooporders: zoekveld, direct rechts "Filters", dan de
|
|
4
|
+
* actieve chips op dezelfde regel; uiterst rechts alleen "Kolommen".
|
|
5
|
+
*/
|
|
6
|
+
export function Toolbar({ children, filters, chips, right }) {
|
|
7
|
+
return (_jsxs("div", { className: "shrink-0 flex flex-wrap items-center gap-2", children: [_jsx("div", { className: "min-w-0 flex-1 sm:flex-none", children: children }), filters, chips, right && _jsx("div", { className: "ml-auto hidden items-center gap-2 md:flex", children: right })] }));
|
|
5
8
|
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
/** "Bijgewerkt hh:mm" met klokicoon (12 px tekst); extra uitleg alleen in de infoballon. */
|
|
3
|
+
export declare function UpdatedAt({ at, prefix, info, stale, }: {
|
|
3
4
|
at: string | Date | null | undefined;
|
|
4
5
|
prefix?: string;
|
|
6
|
+
/** Extra informatie, alleen zichtbaar in de infoballon. */
|
|
7
|
+
info?: ReactNode;
|
|
8
|
+
stale?: boolean;
|
|
5
9
|
}): import("react").JSX.Element | null;
|
|
6
10
|
//# sourceMappingURL=UpdatedAt.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UpdatedAt.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/UpdatedAt.tsx"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"UpdatedAt.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/UpdatedAt.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,4FAA4F;AAC5F,wBAAgB,SAAS,CAAC,EACxB,EAAE,EACF,MAAqB,EACrB,IAAI,EACJ,KAAK,GACN,EAAE;IACD,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,sCAgBA"}
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
if (
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
import { Clock } from "lucide-react";
|
|
3
|
+
import { HoverTip } from "./HoverTip.js";
|
|
4
|
+
/** "Bijgewerkt hh:mm" met klokicoon (12 px tekst); extra uitleg alleen in de infoballon. */
|
|
5
|
+
export function UpdatedAt({ at, prefix = "Bijgewerkt", info, stale, }) {
|
|
6
|
+
let tekst = "Laden…";
|
|
7
|
+
if (at) {
|
|
8
|
+
const d = typeof at === "string" ? new Date(at) : at;
|
|
9
|
+
if (Number.isNaN(d.getTime()))
|
|
10
|
+
return null;
|
|
11
|
+
const t = d.toLocaleTimeString("nl-NL", { hour: "2-digit", minute: "2-digit" });
|
|
12
|
+
tekst = stale ? `Niet bijgewerkt sinds ${t}` : `${prefix} ${t}`;
|
|
13
|
+
}
|
|
14
|
+
const inhoud = (_jsxs("span", { className: "inline-flex items-center gap-1 tabular-nums " + (stale ? "text-warning" : ""), children: [_jsx(Clock, { className: "h-3.5 w-3.5", "aria-hidden": true }), tekst] }));
|
|
15
|
+
if (!info)
|
|
16
|
+
return inhoud;
|
|
17
|
+
return _jsx(HoverTip, { label: tekst, content: _jsx("div", { className: "max-w-xs text-xs", children: info }), align: "start", children: inhoud });
|
|
10
18
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { ConnectionStatus } from "../../lib/stock-scope.js";
|
|
2
2
|
/**
|
|
3
3
|
* Werkpagina's tonen geen uitnodiging om een koppeling in te stellen (dat hoort bij Instellingen).
|
|
4
|
-
* Alleen als een bestaande koppeling niet geladen kan worden: één rustige
|
|
4
|
+
* Alleen als een bestaande koppeling niet geladen kan worden: één rustige meldingsregel.
|
|
5
5
|
* De technische reden gaat naar de console.
|
|
6
6
|
*/
|
|
7
|
+
export declare function floridayConnectionNotice(status: ConnectionStatus, error: string | null): string | null;
|
|
7
8
|
export declare function FloridayConnectionNotice({ status, error, }: {
|
|
8
9
|
status: ConnectionStatus;
|
|
9
10
|
environment?: string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FloridayConnectionNotice.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FloridayConnectionNotice.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"FloridayConnectionNotice.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FloridayConnectionNotice.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAItG;AAED,wBAAgB,wBAAwB,CAAC,EACvC,MAAM,EACN,KAAK,GACN,EAAE;IACD,MAAM,EAAE,gBAAgB,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,sCAMA"}
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { InlineAlert } from "../fs-ui/InlineAlert.js";
|
|
3
2
|
/**
|
|
4
3
|
* Werkpagina's tonen geen uitnodiging om een koppeling in te stellen (dat hoort bij Instellingen).
|
|
5
|
-
* Alleen als een bestaande koppeling niet geladen kan worden: één rustige
|
|
4
|
+
* Alleen als een bestaande koppeling niet geladen kan worden: één rustige meldingsregel.
|
|
6
5
|
* De technische reden gaat naar de console.
|
|
7
6
|
*/
|
|
8
|
-
export function
|
|
7
|
+
export function floridayConnectionNotice(status, error) {
|
|
9
8
|
if (status !== "fout")
|
|
10
9
|
return null;
|
|
11
10
|
if (error)
|
|
12
11
|
console.warn("Floriday-koppeling laden mislukt:", error);
|
|
13
|
-
return
|
|
12
|
+
return "De koppeling met Floriday kon niet geladen worden. Je eigen artikelen werken gewoon; probeer het later opnieuw.";
|
|
13
|
+
}
|
|
14
|
+
export function FloridayConnectionNotice({ status, error, }) {
|
|
15
|
+
const t = floridayConnectionNotice(status, error);
|
|
16
|
+
if (!t)
|
|
17
|
+
return null;
|
|
18
|
+
return (_jsx("div", { role: "status", className: "shrink-0 rounded-lg border border-border bg-warning-soft px-3 py-2 text-sm text-warning-soft-foreground", children: t }));
|
|
14
19
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FustenPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FustenPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FustenPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FustenPage.tsx"],"names":[],"mappings":"AAmMA,wBAAgB,UAAU,gCAiNzB"}
|
|
@@ -1,14 +1,33 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useMemo, useState } from "react";
|
|
3
|
-
import { Boxes, Minus, Plus, RefreshCw,
|
|
4
|
-
import { Button,
|
|
3
|
+
import { AlertTriangle, Boxes, CalendarClock, Layers, Minus, Plus, RefreshCw, TrendingDown, PackageX, CircleSlash } from "lucide-react";
|
|
4
|
+
import { Button, Dialog, DialogContent, DialogHeader, DialogTitle, Input, toast } from "@flowselections/core";
|
|
5
|
+
import { fustReden } from "../../lib/labels.js";
|
|
6
|
+
import { datum, getal } from "../../lib/notatie.js";
|
|
7
|
+
import { UpdatedAt } from "../fs-ui/UpdatedAt.js";
|
|
8
|
+
import { NoticeBar } from "../fs-ui/NoticeBar.js";
|
|
9
|
+
import { KpiRow } from "../fs-ui/KpiRow.js";
|
|
10
|
+
import { KpiTile } from "../fs-ui/KpiTile.js";
|
|
11
|
+
import { PillTabs } from "../fs-ui/PillTabs.js";
|
|
12
|
+
import { Toolbar } from "../fs-ui/Toolbar.js";
|
|
13
|
+
import { SearchInput } from "../fs-ui/SearchInput.js";
|
|
14
|
+
import { FiltersButton } from "../fs-ui/FiltersButton.js";
|
|
15
|
+
import { FilterChips } from "../fs-ui/FilterChips.js";
|
|
16
|
+
import { FilterPanel, FilterGroup } from "../fs-ui/FilterPanel.js";
|
|
17
|
+
import { DataTable } from "../fs-ui/DataTable.js";
|
|
18
|
+
import { EmptyState } from "../fs-ui/EmptyState.js";
|
|
19
|
+
import { Pagination } from "../fs-ui/Pagination.js";
|
|
20
|
+
import { HoverTip } from "../fs-ui/HoverTip.js";
|
|
21
|
+
import { RowMenu } from "../fs-ui/RowMenu.js";
|
|
22
|
+
import { HeaderButton } from "../fs-ui/HeaderButton.js";
|
|
23
|
+
import { Sneltoetsen } from "./Sneltoetsen.js";
|
|
24
|
+
const BRON_LABEL = { alles: "Alle bronnen", vbn: "Standaard fust", leverancier: "Leverancier" };
|
|
5
25
|
import { FustStartmomentCard, FustInstellingenCell } from "./FustAdministratie.js";
|
|
6
26
|
import { fustNaam, fustTekorten, saldoBijhouden } from "../../lib/fust-administratie.js";
|
|
7
27
|
import { useFloridayConnection } from "../../hooks/useFloridayConnection.js";
|
|
8
28
|
import { useFusten } from "../../hooks/useFusten.js";
|
|
9
29
|
import { fetchFloricodeFustMap, fetchFloridayCustomPackageMap, fetchFustMutations, formatFustDimensions, resolveFustDimensions, } from "../../lib/fusten.js";
|
|
10
30
|
import { PageHeader } from "../fs-ui/PageHeader.js";
|
|
11
|
-
import { InlineAlert } from "../fs-ui/InlineAlert.js";
|
|
12
31
|
/** Afmetingen tonen en handmatig aanvullen wanneer de catalogus ze niet kent. */
|
|
13
32
|
function DimensionCell({ fust, floricode, customPackages, onSave, }) {
|
|
14
33
|
const [editing, setEditing] = useState(false);
|
|
@@ -58,20 +77,20 @@ function MutationHistory({ fust }) {
|
|
|
58
77
|
}
|
|
59
78
|
};
|
|
60
79
|
return (_jsxs("div", { children: [_jsx("button", { type: "button", onClick: toggle, className: "text-xs text-primary hover:underline", children: open ? "Historie verbergen" : "Historie" }), open && (_jsxs("div", { className: "mt-2 rounded-md border border-border bg-muted/30 p-2 text-xs", children: [rows === null && _jsx("p", { className: "text-muted-foreground", children: "Laden\u2026" }), rows !== null && rows.length === 0 && (_jsx("p", { className: "text-muted-foreground", children: "Nog geen mutaties." })), rows !== null &&
|
|
61
|
-
rows.map((m) => (_jsxs("div", { className: "flex items-center justify-between gap-3 py-0.5", children: [_jsx("span", { className: "text-muted-foreground", children:
|
|
62
|
-
? "Verkoop"
|
|
63
|
-
: m.reason === "sale_cancelled"
|
|
64
|
-
? "Order geannuleerd"
|
|
65
|
-
: m.reason === "manual_sale"
|
|
66
|
-
? "Handmatige verkoop"
|
|
67
|
-
: m.reason === "startmoment"
|
|
68
|
-
? "Startmoment / beginstand"
|
|
69
|
-
: "Handmatig", m.note ? ` — ${m.note}` : ""] }), _jsx("span", { className: `tabular-nums font-medium ${m.delta < 0 ? "text-rose-600" : "text-emerald-600"}`, children: m.delta > 0 ? `+${m.delta}` : m.delta })] }, m.id)))] }))] }));
|
|
80
|
+
rows.map((m) => (_jsxs("div", { className: "flex items-center justify-between gap-3 py-0.5", children: [_jsx("span", { className: "text-muted-foreground", children: datum(m.created_at, true) }), _jsxs("span", { className: "flex-1 truncate", children: [fustReden(m.reason), m.note ? ` — ${m.note}` : ""] }), _jsx("span", { className: `tabular-nums font-medium ${m.delta < 0 ? "text-destructive" : "text-primary"}`, children: m.delta > 0 ? `+${m.delta}` : m.delta })] }, m.id)))] }))] }));
|
|
70
81
|
}
|
|
71
82
|
export function FustenPage() {
|
|
72
83
|
const { activeId } = useFloridayConnection();
|
|
73
84
|
const { items, loading, syncing, error, sync, adjust, patch } = useFusten(activeId);
|
|
74
85
|
const [search, setSearch] = useState("");
|
|
86
|
+
const [tab, setTab] = useState("alles");
|
|
87
|
+
const [bron, setBron] = useState("alles");
|
|
88
|
+
const [filterOpen, setFilterOpen] = useState(false);
|
|
89
|
+
const [startOpen, setStartOpen] = useState(false);
|
|
90
|
+
const [detailId, setDetailId] = useState(null);
|
|
91
|
+
const [pagina, setPagina] = useState(1);
|
|
92
|
+
const [grootte, setGrootte] = useState(25);
|
|
93
|
+
const [bijgewerkt, setBijgewerkt] = useState(null);
|
|
75
94
|
const [floricode, setFloricode] = useState(null);
|
|
76
95
|
const [floricodeError, setFloricodeError] = useState(null);
|
|
77
96
|
useEffect(() => {
|
|
@@ -86,14 +105,29 @@ export function FustenPage() {
|
|
|
86
105
|
const [busyId, setBusyId] = useState(null);
|
|
87
106
|
const [amounts, setAmounts] = useState({});
|
|
88
107
|
const negative = useMemo(() => fustTekorten(items), [items]);
|
|
108
|
+
const onderMin = (f) => f.minimum_stock > 0 && f.stock < f.minimum_stock;
|
|
109
|
+
const tekort = (f) => saldoBijhouden(f) && f.stock < 0;
|
|
89
110
|
const filtered = useMemo(() => {
|
|
90
111
|
const q = search.trim().toLowerCase();
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
112
|
+
return items.filter((f) => {
|
|
113
|
+
if (tab === "onder_minimum" && !onderMin(f))
|
|
114
|
+
return false;
|
|
115
|
+
if (tab === "tekort" && !tekort(f))
|
|
116
|
+
return false;
|
|
117
|
+
if (tab === "niet_in_gebruik" && f.in_use)
|
|
118
|
+
return false;
|
|
119
|
+
if (bron === "leverancier" && f.source !== "SUPPLIER")
|
|
120
|
+
return false;
|
|
121
|
+
if (bron === "vbn" && f.source === "SUPPLIER")
|
|
122
|
+
return false;
|
|
123
|
+
if (!q)
|
|
124
|
+
return true;
|
|
125
|
+
return fustNaam(f).toLowerCase().includes(q) || f.name.toLowerCase().includes(q) || String(f.vbn_code ?? "").includes(q) || f.fust_key.toLowerCase().includes(q);
|
|
126
|
+
});
|
|
127
|
+
}, [items, search, tab, bron]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
128
|
+
useEffect(() => setPagina(1), [search, tab, bron, grootte]);
|
|
129
|
+
useEffect(() => { if (!loading)
|
|
130
|
+
setBijgewerkt(new Date()); }, [loading, items]);
|
|
97
131
|
const totals = useMemo(() => ({
|
|
98
132
|
types: items.length,
|
|
99
133
|
stock: items.reduce((sum, f) => sum + f.stock, 0),
|
|
@@ -116,11 +150,41 @@ export function FustenPage() {
|
|
|
116
150
|
setBusyId(null);
|
|
117
151
|
}
|
|
118
152
|
};
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
153
|
+
const tabs = [
|
|
154
|
+
{ value: "alles", label: "Alles", icon: _jsx(Layers, {}), count: items.length },
|
|
155
|
+
{ value: "onder_minimum", label: "Onder minimum", icon: _jsx(TrendingDown, {}), count: totals.belowMin },
|
|
156
|
+
{ value: "tekort", label: "Tekort", icon: _jsx(AlertTriangle, {}), count: negative.length },
|
|
157
|
+
{ value: "niet_in_gebruik", label: "Niet in gebruik", icon: _jsx(CircleSlash, {}), count: items.filter((f) => !f.in_use).length },
|
|
158
|
+
];
|
|
159
|
+
const TAB_ICON = { alles: _jsx(Layers, {}), onder_minimum: _jsx(TrendingDown, {}), tekort: _jsx(AlertTriangle, {}), niet_in_gebruik: _jsx(CircleSlash, {}) };
|
|
160
|
+
const notices = [
|
|
161
|
+
...(error ? ["Fusten laden is niet gelukt. Probeer het opnieuw."] : []),
|
|
162
|
+
...(floricodeError ? ["De standaardmaten van fusten konden niet geladen worden. Alleen zelf ingevoerde maten worden getoond."] : []),
|
|
163
|
+
...(negative.length > 0 ? [
|
|
164
|
+
_jsxs("span", { role: "button", tabIndex: 0, className: "hover:underline", onClick: (e) => { e.stopPropagation(); setTab("tekort"); }, children: [negative.length, " fusttype", negative.length === 1 ? " heeft" : "n hebben", " een tekort sinds het startmoment"] }, "tekort"),
|
|
165
|
+
] : []),
|
|
166
|
+
];
|
|
167
|
+
const detail = items.find((f) => f.id === detailId) ?? null;
|
|
168
|
+
const zichtbaar = filtered.slice((pagina - 1) * grootte, pagina * grootte);
|
|
169
|
+
const chips = [
|
|
170
|
+
...(tab !== "alles" ? [{ key: "tab", label: tabs.find((t) => t.value === tab).label, icon: TAB_ICON[tab], onRemove: () => setTab("alles") }] : []),
|
|
171
|
+
...(bron !== "alles" ? [{ key: "bron", label: BRON_LABEL[bron], onRemove: () => setBron("alles") }] : []),
|
|
172
|
+
];
|
|
173
|
+
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, {}), _jsx(PageHeader, { title: "Fusten", updated: _jsx(UpdatedAt, { at: bijgewerkt, info: "Fusttypen komen vanzelf uit de beladingen van je artikelen." }), actions: _jsxs(_Fragment, { children: [_jsx(HeaderButton, { icon: _jsx(CalendarClock, {}), label: "Startmoment", onClick: () => setStartOpen(true) }), _jsx(HeaderButton, { icon: _jsx(RefreshCw, { className: syncing ? "animate-spin motion-reduce:animate-none" : "" }), label: "Bijwerken", onClick: () => void sync(), disabled: syncing })] }) }), _jsx(NoticeBar, { items: notices }), _jsxs(KpiRow, { children: [_jsx(KpiTile, { title: "Fusttypen", icon: _jsx(Boxes, {}), loading: loading, value: getal(totals.types), sub: "Uit beladingen", onClick: () => setTab("alles"), active: false }), _jsx(KpiTile, { title: "Totale fustvoorraad", icon: _jsx(Layers, {}), loading: loading, value: getal(totals.stock), sub: "Fust" }), _jsx(KpiTile, { title: "Onder minimum", icon: _jsx(TrendingDown, {}), loading: loading, tone: totals.belowMin > 0 ? "warning" : "default", value: getal(totals.belowMin), sub: "Fusttypen", onClick: () => setTab("onder_minimum"), active: tab === "onder_minimum" }), _jsx(KpiTile, { title: "Tekort", icon: _jsx(PackageX, {}), loading: loading, tone: negative.length > 0 ? "warning" : "default", value: getal(negative.length), sub: "Sinds het startmoment", onClick: () => setTab("tekort"), active: tab === "tekort" })] }), _jsx(PillTabs, { tabs: tabs, value: tab, onChange: setTab, label: "Fusten filteren" }), _jsx(Toolbar, { filters: _jsx(FiltersButton, { count: bron !== "alles" ? 1 : 0, onClick: () => setFilterOpen(true) }), chips: _jsx(FilterChips, { chips: chips, onClearAll: () => { setTab("alles"); setBron("alles"); } }), children: _jsx(SearchInput, { value: search, onChange: setSearch, placeholder: "Zoek fust op naam of code" }) }), _jsx(DataTable, { rows: zichtbaar, getId: (f) => f.id, loading: loading, onOpen: (f) => setDetailId(f.id), openLabel: "Fust openen", empty: _jsx(EmptyState, { icon: _jsx(Boxes, { className: "h-6 w-6" }), title: items.length === 0 ? "Nog geen fusttypen" : "Niets gevonden", text: items.length === 0 ? "Voeg een belading toe aan een artikel; het fust verschijnt dan vanzelf." : "Geen fusten in deze weergave." }), rowActions: (f) => (_jsx(RowMenu, { label: fustNaam(f), items: [
|
|
174
|
+
{ label: "Details en instellingen", onClick: () => setDetailId(f.id) },
|
|
175
|
+
] })), columns: [
|
|
176
|
+
{ key: "fust", header: "Fust", width: "240px", render: (f) => (_jsxs("div", { className: "min-w-0", children: [_jsx(HoverTip, { label: fustNaam(f), content: _jsxs("div", { className: "text-xs", children: [fustNaam(f), f.eigen_naam?.trim() ? ` (${f.name})` : "", f.vbn_code ? ` · code ${f.vbn_code}` : ""] }), align: "start", className: "block max-w-full truncate text-left", children: _jsx("span", { className: "block truncate font-medium", children: fustNaam(f) }) }), _jsxs("div", { className: "truncate text-xs text-muted-foreground", children: [f.source === "SUPPLIER" ? "Leverancier" : "Standaard fust", f.in_use ? "" : " · niet meer in gebruik"] })] })) },
|
|
177
|
+
{ key: "afmeting", header: "Afmeting", width: "160px", render: (f) => {
|
|
178
|
+
const r = resolveFustDimensions(f, floricode, customPackages);
|
|
179
|
+
const l = formatFustDimensions(r);
|
|
180
|
+
return (_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate " + (l ? "tabular-nums" : "text-muted-foreground"), children: l ?? "Onbekend" }), r.source !== "onbekend" && _jsx("div", { className: "truncate text-xs text-muted-foreground", children: r.source === "floricode" ? "Standaardmaat" : r.source === "floriday" ? "Eigen fust uit Floriday" : "Handmatig" })] }));
|
|
181
|
+
} },
|
|
182
|
+
{ key: "gebruikt", header: "Gebruikt door", width: "128px", render: (f) => _jsxs("span", { className: "tabular-nums", children: [getal(f.used_by_count), " ", _jsx("span", { className: "text-xs text-muted-foreground", children: "artikelen" })] }) },
|
|
183
|
+
{ key: "voorraad", header: "Voorraad", width: "152px", render: (f) => (_jsxs("div", { className: "min-w-0", children: [tekort(f)
|
|
184
|
+
? _jsxs("div", { className: "truncate font-semibold tabular-nums text-destructive", children: ["Tekort ", getal(Math.abs(f.stock)), " fust"] })
|
|
185
|
+
: _jsxs("div", { className: "truncate font-semibold tabular-nums " + (onderMin(f) ? "text-destructive" : ""), children: [getal(f.stock), " ", _jsx("span", { className: "text-xs font-normal text-muted-foreground", children: "fust" })] }), !saldoBijhouden(f) && _jsx("div", { className: "truncate text-xs text-muted-foreground", children: "Saldo niet bijgehouden" })] })) },
|
|
186
|
+
{ key: "minimum", header: "Minimum", width: "120px", render: (f) => (_jsx(Input, { type: "number", "aria-label": `Minimum ${fustNaam(f)}`, className: "h-8 w-24", defaultValue: f.minimum_stock, onClick: (e) => e.stopPropagation(), onBlur: (e) => { const v = Math.max(0, Math.trunc(Number(e.target.value) || 0)); if (v !== f.minimum_stock)
|
|
187
|
+
void patch(f.id, { minimum_stock: v }); } })) },
|
|
188
|
+
{ key: "aanpassen", header: "Aanpassen", width: "184px", render: (f) => (_jsxs("div", { className: "flex items-center gap-1", onClick: (e) => e.stopPropagation(), children: [_jsx(Button, { variant: "outline", size: "icon", className: "h-8 w-8", "aria-label": "Afboeken", disabled: busyId === f.id || !(Number(amounts[f.id]) > 0), onClick: () => void mutate(f, -1), children: _jsx(Minus, { className: "h-4 w-4" }) }), _jsx(Input, { className: "h-8 w-20 text-center", type: "number", placeholder: "Aantal", "aria-label": `Aantal ${fustNaam(f)}`, value: amounts[f.id] ?? "", onChange: (e) => setAmounts((a) => ({ ...a, [f.id]: e.target.value })) }), _jsx(Button, { variant: "outline", size: "icon", className: "h-8 w-8", "aria-label": "Bijboeken", disabled: busyId === f.id || !(Number(amounts[f.id]) > 0), onClick: () => void mutate(f, 1), children: _jsx(Plus, { className: "h-4 w-4" }) })] })) },
|
|
189
|
+
] }), _jsx(Pagination, { page: pagina, pageSize: grootte, totalCount: filtered.length, onPage: setPagina, onPageSize: setGrootte }), _jsx(FilterPanel, { open: filterOpen, onOpenChange: setFilterOpen, onClear: () => setBron("alles"), children: _jsx(FilterGroup, { title: "Bron", value: bron, onChange: setBron, options: Object.keys(BRON_LABEL).map((b) => ({ value: b, label: BRON_LABEL[b] })) }) }), _jsx(Dialog, { open: startOpen, onOpenChange: setStartOpen, children: _jsxs(DialogContent, { className: "max-w-2xl", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "Startmoment" }) }), _jsx(FustStartmomentCard, { items: items, onSaved: () => void sync() })] }) }), _jsx(Dialog, { open: !!detail, onOpenChange: (o) => !o && setDetailId(null), children: _jsxs(DialogContent, { className: "left-auto right-0 top-0 h-[100dvh] max-w-md translate-x-0 translate-y-0 overflow-auto rounded-none sm:rounded-none", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: detail ? fustNaam(detail) : "" }) }), detail && (_jsxs("div", { className: "space-y-5 text-sm", children: [_jsxs("section", { className: "space-y-1", children: [_jsx("h3", { className: "text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: "Naam en saldo" }), _jsx(FustInstellingenCell, { fust: detail, onSaved: () => void sync() })] }), _jsxs("section", { className: "space-y-1", children: [_jsx("h3", { className: "text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: "Afmeting" }), _jsx(DimensionCell, { fust: detail, floricode: floricode, customPackages: customPackages, onSave: (v) => patch(detail.id, v) })] }), _jsxs("section", { className: "space-y-1", children: [_jsx("h3", { className: "text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: "Boekingen" }), _jsx(MutationHistory, { fust: detail })] })] }))] }) })] }));
|
|
126
190
|
}
|
|
@@ -4,7 +4,9 @@ interface Props {
|
|
|
4
4
|
productId?: string | null;
|
|
5
5
|
/** Artikelen met dezelfde voorraadsleutel, uit voorraad_artikel_detail. */
|
|
6
6
|
productIds?: string[];
|
|
7
|
+
/** Stuks per fust van de hoofdverpakking (voor omrekenen naar stuks). */
|
|
8
|
+
stuksPerFust?: number | null;
|
|
7
9
|
}
|
|
8
|
-
export declare function OrderHistoryCard({ tradeItemKey, productName, productId, productIds: bekendeIds }: Props): import("react").JSX.Element;
|
|
10
|
+
export declare function OrderHistoryCard({ tradeItemKey, productName, productId, productIds: bekendeIds, stuksPerFust }: Props): import("react").JSX.Element;
|
|
9
11
|
export {};
|
|
10
12
|
//# sourceMappingURL=OrderHistoryCard.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OrderHistoryCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/OrderHistoryCard.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"OrderHistoryCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/OrderHistoryCard.tsx"],"names":[],"mappings":"AAmBA,UAAU,KAAK;IACb,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAgBD,wBAAgB,gBAAgB,CAAC,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,EAAE,KAAK,+BA6JrH"}
|
|
@@ -1,49 +1,22 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from "react";
|
|
3
|
-
import { Card, CardContent
|
|
4
|
-
import { Loader2, ClipboardList, ExternalLink } from "lucide-react";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (!s)
|
|
18
|
-
return "Nog te picken";
|
|
19
|
-
if (["verzonden", "geleverd", "shipped", "delivered"].includes(s))
|
|
20
|
-
return "Verzonden";
|
|
21
|
-
if (["nieuw", "open", "new", "pending"].includes(s))
|
|
22
|
-
return "Nog te picken";
|
|
23
|
-
if (["ready", "klaar", "klaar voor verzending"].includes(s))
|
|
24
|
-
return "Klaar voor verzending";
|
|
25
|
-
if (["geannuleerd", "cancelled", "canceled"].includes(s))
|
|
26
|
-
return "geannuleerd";
|
|
27
|
-
if (s === "concept")
|
|
28
|
-
return "concept";
|
|
29
|
-
if (s === "nog te picken")
|
|
30
|
-
return "Nog te picken";
|
|
31
|
-
return raw ?? "Nog te picken";
|
|
32
|
-
}
|
|
33
|
-
function formatDate(v) {
|
|
34
|
-
if (!v)
|
|
35
|
-
return "—";
|
|
36
|
-
try {
|
|
37
|
-
const d = new Date(v);
|
|
38
|
-
if (Number.isNaN(d.getTime()))
|
|
39
|
-
return v;
|
|
40
|
-
return d.toLocaleDateString("nl-NL", { day: "2-digit", month: "short", year: "numeric" });
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
return v;
|
|
44
|
-
}
|
|
3
|
+
import { Card, CardContent } from "@flowselections/core";
|
|
4
|
+
import { Loader2, ClipboardList, ExternalLink, Circle, CircleDot, CheckCircle2, XCircle, CircleDashed } from "lucide-react";
|
|
5
|
+
import { orderStatus, EENHEID } from "../../lib/labels.js";
|
|
6
|
+
import { datum, getal } from "../../lib/notatie.js";
|
|
7
|
+
import { StatusIcon } from "../fs-ui/StatusIcon.js";
|
|
8
|
+
const TONE = { open: "open", bezig: "bezig", klaar: "klaar", fout: "fout", concept: "open" };
|
|
9
|
+
const ICON = { open: _jsx(Circle, {}), bezig: _jsx(CircleDot, {}), klaar: _jsx(CheckCircle2, {}), fout: _jsx(XCircle, {}), concept: _jsx(CircleDashed, {}) };
|
|
10
|
+
/** Aantal altijd in stuks; fust als tweede regel wanneer de verpakking bekend is. */
|
|
11
|
+
function stuksEnFust(qty, unit, stuksPerFust) {
|
|
12
|
+
const e = EENHEID[(unit ?? "stuks").trim().toLowerCase()] ?? "stuks";
|
|
13
|
+
const spf = stuksPerFust && stuksPerFust > 0 ? stuksPerFust : null;
|
|
14
|
+
if (e === "fust")
|
|
15
|
+
return { stuks: spf ? qty * spf : null, fust: qty };
|
|
16
|
+
return { stuks: qty, fust: spf ? Math.round((qty / spf) * 100) / 100 : null };
|
|
45
17
|
}
|
|
46
|
-
|
|
18
|
+
import { supabase } from "../../integrations/supabase/client.js";
|
|
19
|
+
export function OrderHistoryCard({ tradeItemKey, productName, productId, productIds: bekendeIds, stuksPerFust }) {
|
|
47
20
|
const [rows, setRows] = useState(null);
|
|
48
21
|
const [error, setError] = useState(null);
|
|
49
22
|
useEffect(() => {
|
|
@@ -86,7 +59,7 @@ export function OrderHistoryCard({ tradeItemKey, productName, productId, product
|
|
|
86
59
|
}
|
|
87
60
|
const { data, error } = await supabase
|
|
88
61
|
.from("order_items")
|
|
89
|
-
.select("order_id,quantity,unit,orders!inner(id,order_number,customer_name,order_date,created_at,status,order_source,deleted_at)")
|
|
62
|
+
.select("order_id,quantity,unit,plants_per_crate,orders!inner(id,order_number,customer_name,order_date,created_at,status,order_source,deleted_at)")
|
|
90
63
|
.or(orFilters.join(","))
|
|
91
64
|
.is("orders.deleted_at", null)
|
|
92
65
|
.order("created_at", { ascending: false, foreignTable: "orders" })
|
|
@@ -102,6 +75,7 @@ export function OrderHistoryCard({ tradeItemKey, productName, productId, product
|
|
|
102
75
|
customer_name: r.orders?.customer_name ?? null,
|
|
103
76
|
quantity: r.quantity ?? 0,
|
|
104
77
|
unit: r.unit ?? null,
|
|
78
|
+
plants_per_crate: r.plants_per_crate == null ? null : Number(r.plants_per_crate),
|
|
105
79
|
order_date: r.orders?.order_date ?? null,
|
|
106
80
|
created_at: r.orders?.created_at ?? "",
|
|
107
81
|
status: r.orders?.status ?? null,
|
|
@@ -111,8 +85,10 @@ export function OrderHistoryCard({ tradeItemKey, productName, productId, product
|
|
|
111
85
|
setRows(mapped);
|
|
112
86
|
}
|
|
113
87
|
catch (e) {
|
|
114
|
-
if (!cancelled)
|
|
88
|
+
if (!cancelled) {
|
|
89
|
+
console.warn("Bestelgeschiedenis laden mislukt:", e);
|
|
115
90
|
setError(e.message);
|
|
91
|
+
}
|
|
116
92
|
}
|
|
117
93
|
};
|
|
118
94
|
load();
|
|
@@ -127,11 +103,11 @@ export function OrderHistoryCard({ tradeItemKey, productName, productId, product
|
|
|
127
103
|
supabase.removeChannel(channel);
|
|
128
104
|
};
|
|
129
105
|
}, [tradeItemKey, productName, productId, bekendeIds?.join("|")]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
130
|
-
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(ClipboardList, { className: "h-4 w-4" }) }), _jsx("span", { className: "font-medium", children: "Bestelgeschiedenis" }), _jsx("span", { className: "text-xs text-muted-foreground tabular-nums", children: rows ? `${rows.length} bestelling${rows.length === 1 ? "" : "en"}` : "" })] }), _jsxs(CardContent, { className: "pt-0", children: [error && (
|
|
131
|
-
const
|
|
132
|
-
const
|
|
133
|
-
"bg-slate-100 text-slate-700 border-slate-200";
|
|
106
|
+
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(ClipboardList, { className: "h-4 w-4" }) }), _jsx("span", { className: "font-medium", children: "Bestelgeschiedenis" }), _jsx("span", { className: "text-xs text-muted-foreground tabular-nums", children: rows ? `${rows.length} bestelling${rows.length === 1 ? "" : "en"}` : "" })] }), _jsxs(CardContent, { className: "pt-0", children: [error && (_jsx("div", { className: "text-sm text-destructive", children: "Bestelgeschiedenis laden lukt niet. Probeer opnieuw." })), !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" }), " Bestellingen laden\u2026"] })), rows && rows.length === 0 && !error && (_jsx("div", { className: "text-sm text-muted-foreground", children: "Nog geen bestellingen voor dit artikel." })), rows && rows.length > 0 && (_jsx("div", { className: "overflow-x-auto rounded-md border border-border", children: _jsxs("table", { className: "w-full min-w-[640px] 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: "140px" } }), _jsx("col", {}), _jsx("col", { style: { width: "132px" } }), _jsx("col", { style: { width: "120px" } }), _jsx("col", { style: { width: "184px" } }), _jsx("col", { style: { width: "44px" } })] }), _jsx("thead", { children: _jsxs("tr", { className: "border-b border-border", children: [_jsx("th", { children: "Ordernummer" }), _jsx("th", { children: "Klant" }), _jsx("th", { children: "Aantal" }), _jsx("th", { children: "Besteldatum" }), _jsx("th", { children: "Status" }), _jsx("th", { children: _jsx("span", { className: "sr-only", children: "Openen" }) })] }) }), _jsx("tbody", { children: rows.map((r, i) => {
|
|
107
|
+
const st = orderStatus(r.status);
|
|
108
|
+
const q = stuksEnFust(Number(r.quantity ?? 0), r.unit, r.plants_per_crate ?? stuksPerFust);
|
|
134
109
|
const href = `/floriday-verkoop/verkooporders/${r.order_id}`;
|
|
135
|
-
|
|
110
|
+
const cel = "truncate px-2 py-0 align-middle";
|
|
111
|
+
return (_jsxs("tr", { className: "h-[60px] border-b border-border last:border-0 hover:bg-muted/40", children: [_jsx("td", { className: cel, children: _jsx("a", { href: href, className: "font-medium text-primary hover:underline", children: r.order_number }) }), _jsx("td", { className: cel, children: r.customer_name ?? "—" }), _jsxs("td", { className: cel + " tabular-nums", children: [_jsx("div", { className: "truncate", children: q.stuks == null ? "—" : `${getal(q.stuks)} stuks` }), q.fust != null && _jsxs("div", { className: "truncate text-xs text-muted-foreground", children: [getal(q.fust), " fust"] })] }), _jsx("td", { className: cel + " tabular-nums", children: datum(r.order_date ?? r.created_at) }), _jsx("td", { className: cel, children: _jsxs("span", { className: "inline-flex min-w-0 items-center gap-1.5", children: [_jsx(StatusIcon, { tone: TONE[st.tone], icon: ICON[st.tone], label: st.label }), _jsx("span", { className: "truncate", children: st.label })] }) }), _jsx("td", { className: cel + " text-muted-foreground", children: _jsx("a", { href: href, "aria-label": "Order openen", className: "inline-flex h-8 w-8 items-center justify-center rounded-md hover:bg-muted", children: _jsx(ExternalLink, { className: "h-3.5 w-3.5" }) }) })] }, `${r.order_id}-${i}`));
|
|
136
112
|
}) })] }) }))] })] }));
|
|
137
113
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PublicatiesPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/PublicatiesPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PublicatiesPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/PublicatiesPage.tsx"],"names":[],"mappings":"AAoBA,wBAAgB,eAAe,gCA0F9B"}
|
|
@@ -1,27 +1,21 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Link } from "@tanstack/react-router";
|
|
3
|
-
import {
|
|
4
|
-
import { ArrowLeft,
|
|
3
|
+
import { Button } from "@flowselections/core";
|
|
4
|
+
import { ArrowLeft, CheckCircle2, CircleDot, XCircle, CircleDashed, RefreshCw, Send } from "lucide-react";
|
|
5
5
|
import { useEffect, useState } from "react";
|
|
6
6
|
import { usePublishJobs } from "../../hooks/usePublishJob.js";
|
|
7
7
|
import { supabase } from "../../integrations/supabase/client.js";
|
|
8
8
|
import { PageHeader } from "../fs-ui/PageHeader.js";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
? "bg-destructive/10 text-destructive"
|
|
20
|
-
: status === "running"
|
|
21
|
-
? "bg-muted text-muted-foreground"
|
|
22
|
-
: "bg-amber-100 text-amber-800";
|
|
23
|
-
return (_jsx("span", { className: `rounded-full px-2 py-0.5 text-xs font-medium ${tone}`, children: STATUS_LABEL[status] ?? status }));
|
|
24
|
-
}
|
|
9
|
+
import { UpdatedAt } from "../fs-ui/UpdatedAt.js";
|
|
10
|
+
import { HeaderButton } from "../fs-ui/HeaderButton.js";
|
|
11
|
+
import { DataTable } from "../fs-ui/DataTable.js";
|
|
12
|
+
import { EmptyState } from "../fs-ui/EmptyState.js";
|
|
13
|
+
import { Pagination } from "../fs-ui/Pagination.js";
|
|
14
|
+
import { StatusIcon } from "../fs-ui/StatusIcon.js";
|
|
15
|
+
import { publicatieStatus } from "../../lib/labels.js";
|
|
16
|
+
import { datum } from "../../lib/notatie.js";
|
|
17
|
+
const TONE = { open: "open", bezig: "bezig", klaar: "klaar", fout: "fout", concept: "open" };
|
|
18
|
+
const ICON = { open: _jsx(CircleDashed, {}), bezig: _jsx(CircleDot, {}), klaar: _jsx(CheckCircle2, {}), fout: _jsx(XCircle, {}), concept: _jsx(CircleDashed, {}) };
|
|
25
19
|
export function PublicatiesPage() {
|
|
26
20
|
const { jobs, loading, reload } = usePublishJobs(50);
|
|
27
21
|
const [stepsByJob, setStepsByJob] = useState({});
|
|
@@ -48,22 +42,33 @@ export function PublicatiesPage() {
|
|
|
48
42
|
cancelled = true;
|
|
49
43
|
};
|
|
50
44
|
}, [jobs]);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
45
|
+
const [pagina, setPagina] = useState(1);
|
|
46
|
+
const [grootte, setGrootte] = useState(25);
|
|
47
|
+
const [bijgewerkt, setBijgewerkt] = useState(null);
|
|
48
|
+
useEffect(() => { if (!loading)
|
|
49
|
+
setBijgewerkt(new Date()); }, [loading, jobs]);
|
|
50
|
+
const rijen = jobs.map((job) => {
|
|
51
|
+
const target = (job.product_id ?? job.trade_item_id);
|
|
52
|
+
// Oude "Actie nodig" sluit vanzelf zodra een latere publicatie van hetzelfde artikel (op id) wel gelukt is.
|
|
53
|
+
const repaired = job.status === "failed" &&
|
|
54
|
+
jobs.some((o) => o.id !== job.id && (o.product_id ?? o.trade_item_id) === target && target != null &&
|
|
55
|
+
new Date(o.created_at) > new Date(job.created_at) && (o.status === "succeeded" || o.status === "succeeded_with_warnings"));
|
|
56
|
+
const steps = (stepsByJob[job.id] ?? []).filter((s) => s.status !== "skipped");
|
|
57
|
+
const failed = repaired ? [] : steps.filter((s) => s.status === "failed");
|
|
58
|
+
for (const f of failed)
|
|
59
|
+
if (f.error_message)
|
|
60
|
+
console.warn("Publicatiestap mislukt:", f.label, f.error_message);
|
|
61
|
+
return { job, target, repaired, steps, failed };
|
|
62
|
+
});
|
|
63
|
+
const zichtbaar = rijen.slice((pagina - 1) * grootte, pagina * grootte);
|
|
64
|
+
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: "Naar Floriday gestuurd", updated: _jsx(UpdatedAt, { at: bijgewerkt, info: "Artikelen die je naar Floriday hebt gestuurd, met wat er eventueel nog moet gebeuren." }), actions: _jsx(HeaderButton, { icon: _jsx(RefreshCw, {}), label: "Vernieuwen", onClick: () => void reload() }) }), _jsx(DataTable, { rows: zichtbaar, getId: (r) => r.job.id, loading: loading, keyboard: false, empty: _jsx(EmptyState, { icon: _jsx(Send, { className: "h-6 w-6" }), title: "Nog niets verstuurd", text: "Er zijn nog geen artikelen naar Floriday gestuurd." }), columns: [
|
|
65
|
+
{ key: "artikel", header: "Artikel", width: "260px", render: (r) => (_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate font-medium", children: r.job.title || "Artikel" }), r.job.environment !== "live" && _jsx("div", { className: "truncate text-xs text-muted-foreground", children: "Testomgeving" })] })) },
|
|
66
|
+
{ key: "moment", header: "Verstuurd", width: "152px", render: (r) => _jsx("span", { className: "tabular-nums", children: datum(r.job.created_at, true) }) },
|
|
67
|
+
{ key: "stappen", header: "Stappen", width: "260px", render: (r) => (_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "truncate tabular-nums", children: [r.steps.filter((s) => s.status === "done").length, " van ", r.steps.length, " klaar"] }), r.failed.length > 0 && _jsx("div", { className: "truncate text-xs text-destructive", children: r.failed.map((s) => s.label).join(", ") })] })) },
|
|
68
|
+
{ key: "status", header: "Status", width: "176px", render: (r) => {
|
|
69
|
+
const st = publicatieStatus(r.repaired ? "repaired" : r.job.status);
|
|
70
|
+
return _jsxs("span", { className: "inline-flex min-w-0 items-center gap-1.5", children: [_jsx(StatusIcon, { tone: TONE[st.tone], icon: ICON[st.tone], label: st.label }), _jsx("span", { className: "truncate", children: st.label })] });
|
|
71
|
+
} },
|
|
72
|
+
{ key: "actie", header: _jsx("span", { className: "sr-only", children: "Actie" }), width: "120px", render: (r) => r.target && r.failed.length > 0 ? (_jsx(Button, { asChild: true, size: "sm", variant: "outline", children: _jsx(Link, { to: "/voorraad/$id", params: { id: String(r.target) }, search: { hervat: r.job.id }, children: "Afronden" }) })) : null },
|
|
73
|
+
] }), _jsx(Pagination, { page: pagina, pageSize: grootte, totalCount: rijen.length, onPage: setPagina, onPageSize: (n) => { setGrootte(n); setPagina(1); } })] }));
|
|
69
74
|
}
|
|
@@ -7,7 +7,7 @@ export declare function Sneltoetsen({ onNieuw, nieuwLabel }: {
|
|
|
7
7
|
onNieuw?: () => void;
|
|
8
8
|
nieuwLabel?: string;
|
|
9
9
|
}): import("react").JSX.Element;
|
|
10
|
-
/** Kleine sneltoets-hint in een knop
|
|
10
|
+
/** Kleine sneltoets-hint in een primaire knop, exact naar Verkooporders. */
|
|
11
11
|
export declare function Kbd({ children }: {
|
|
12
12
|
children: string;
|
|
13
13
|
}): import("react").JSX.Element;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sneltoetsen.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/Sneltoetsen.tsx"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,UAA4B,EAAE,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,+
|
|
1
|
+
{"version":3,"file":"Sneltoetsen.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/Sneltoetsen.tsx"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,UAA4B,EAAE,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,+BAsBnH;AAED,4EAA4E;AAC5E,wBAAgB,GAAG,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,+BAErD"}
|