@flowselections/logistiek-engine 1.0.28 → 1.0.30
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/README.md +24 -24
- package/dist-lib/components/GeblokkeerdeOrdersCard.d.ts +2 -0
- package/dist-lib/components/GeblokkeerdeOrdersCard.d.ts.map +1 -0
- package/dist-lib/components/GeblokkeerdeOrdersCard.js +44 -0
- package/dist-lib/components/LogistiekEnginePage.d.ts.map +1 -1
- package/dist-lib/components/LogistiekEnginePage.js +2 -1
- package/dist-lib/components/RegiePage.d.ts.map +1 -1
- package/dist-lib/components/RegiePage.js +2 -1
- package/dist-lib/components/WerkwijzeHerindelingCard.d.ts +2 -0
- package/dist-lib/components/WerkwijzeHerindelingCard.d.ts.map +1 -0
- package/dist-lib/components/WerkwijzeHerindelingCard.js +36 -0
- package/dist-lib/hooks/useGeblokkeerdeOrders.d.ts +28 -0
- package/dist-lib/hooks/useGeblokkeerdeOrders.d.ts.map +1 -0
- package/dist-lib/hooks/useGeblokkeerdeOrders.js +94 -0
- package/dist-lib/hooks/useRuleEngine.d.ts.map +1 -1
- package/dist-lib/hooks/useRuleEngine.js +7 -4
- package/dist-lib/hooks/useWerkwijzeStatus.d.ts +17 -0
- package/dist-lib/hooks/useWerkwijzeStatus.d.ts.map +1 -0
- package/dist-lib/hooks/useWerkwijzeStatus.js +58 -0
- package/dist-lib/index.d.ts +3 -0
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +3 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +206 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +1516 -159
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +206 -0
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/moduleClient.d.ts +206 -0
- package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +206 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/floridayDocuments.functions.d.ts +618 -0
- package/dist-lib/lib/floridayDocuments.functions.d.ts.map +1 -1
- package/dist-lib/lib/floridayDocuments.server.d.ts +7 -1
- package/dist-lib/lib/floridayDocuments.server.d.ts.map +1 -1
- package/dist-lib/lib/floridayDocuments.server.js +25 -1
- package/dist-lib/lib/floridayPush.functions.d.ts +206 -0
- package/dist-lib/lib/floridayPush.functions.d.ts.map +1 -1
- package/dist-lib/lib/floridayPush.server.d.ts.map +1 -1
- package/dist-lib/lib/floridayPush.server.js +28 -0
- package/dist-lib/lib/handlers/logistics-tick.server.d.ts.map +1 -1
- package/dist-lib/lib/handlers/logistics-tick.server.js +21 -0
- package/dist-lib/lib/loadCarrierPlanning.functions.d.ts +1030 -0
- package/dist-lib/lib/loadCarrierPlanning.functions.d.ts.map +1 -1
- package/dist-lib/lib/loadCarrierPlanning.server.d.ts.map +1 -1
- package/dist-lib/lib/loadCarrierPlanning.server.js +23 -3
- package/dist-lib/lib/logisticsRecalcJob.functions.d.ts +412 -0
- package/dist-lib/lib/logisticsRecalcJob.functions.d.ts.map +1 -1
- package/dist-lib/lib/logisticsRecalculate.functions.d.ts +206 -0
- package/dist-lib/lib/logisticsRecalculate.functions.d.ts.map +1 -1
- package/dist-lib/lib/modulePrompts.d.ts.map +1 -1
- package/dist-lib/lib/modulePrompts.js +2613 -2406
- package/dist-lib/lib/ruleEngineSchema.d.ts.map +1 -1
- package/dist-lib/lib/ruleEngineSchema.js +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
# logistiek Engine
|
|
2
|
-
|
|
3
|
-
blank project start
|
|
4
|
-
|
|
5
|
-
This project was built with [Lovable](https://lovable.dev).
|
|
6
|
-
|
|
7
|
-
## Build with Lovable
|
|
8
|
-
|
|
9
|
-
Continue developing this project in the [Lovable editor](https://lovable.dev/projects/316ec9c5-0f01-408a-b7ec-3d4dccf9b65d).
|
|
10
|
-
|
|
11
|
-
- **Ship faster**: describe what you want to build and Lovable handles the code.
|
|
12
|
-
- **Stay in sync**: every change made in Lovable is committed straight to this repository.
|
|
13
|
-
- **Full ownership**: this code is yours. Push to `main` on GitHub and your changes sync back into Lovable, ready for your next prompt.
|
|
14
|
-
|
|
15
|
-
## Development
|
|
16
|
-
|
|
17
|
-
Prefer working locally? You need Node.js and npm — [install with nvm](https://github.com/nvm-sh/nvm#installing-and-updating).
|
|
18
|
-
|
|
19
|
-
```sh
|
|
20
|
-
git clone <this-repository-url>
|
|
21
|
-
cd <repository-name>
|
|
22
|
-
npm i
|
|
23
|
-
npm run dev
|
|
24
|
-
```
|
|
1
|
+
# logistiek Engine
|
|
2
|
+
|
|
3
|
+
blank project start
|
|
4
|
+
|
|
5
|
+
This project was built with [Lovable](https://lovable.dev).
|
|
6
|
+
|
|
7
|
+
## Build with Lovable
|
|
8
|
+
|
|
9
|
+
Continue developing this project in the [Lovable editor](https://lovable.dev/projects/316ec9c5-0f01-408a-b7ec-3d4dccf9b65d).
|
|
10
|
+
|
|
11
|
+
- **Ship faster**: describe what you want to build and Lovable handles the code.
|
|
12
|
+
- **Stay in sync**: every change made in Lovable is committed straight to this repository.
|
|
13
|
+
- **Full ownership**: this code is yours. Push to `main` on GitHub and your changes sync back into Lovable, ready for your next prompt.
|
|
14
|
+
|
|
15
|
+
## Development
|
|
16
|
+
|
|
17
|
+
Prefer working locally? You need Node.js and npm — [install with nvm](https://github.com/nvm-sh/nvm#installing-and-updating).
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
git clone <this-repository-url>
|
|
21
|
+
cd <repository-name>
|
|
22
|
+
npm i
|
|
23
|
+
npm run dev
|
|
24
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GeblokkeerdeOrdersCard.d.ts","sourceRoot":"","sources":["../../src/components/GeblokkeerdeOrdersCard.tsx"],"names":[],"mappings":"AA6BA,wBAAgB,sBAAsB,gCAsIrC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// src/components/GeblokkeerdeOrdersCard.tsx — orders die vastlopen
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// Een blokkade is een zichtbare eindtoestand: de order blijft staan tot er
|
|
6
|
+
// iets aan de order verandert of iemand hier op opnieuw proberen klikt.
|
|
7
|
+
// Alles komt uit de Engine-functies; hier wordt niets zelf berekend.
|
|
8
|
+
// GEEN <Header /> hier — die levert de shell.
|
|
9
|
+
// ============================================================================
|
|
10
|
+
import { useMemo, useState } from "react";
|
|
11
|
+
import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Skeleton, } from "@flowselections/core";
|
|
12
|
+
import { AlertTriangle, RefreshCw, RotateCcw } from "lucide-react";
|
|
13
|
+
import { useGeblokkeerdeOrders, redenLabel } from "../hooks/useGeblokkeerdeOrders";
|
|
14
|
+
function datum(waarde) {
|
|
15
|
+
if (!waarde)
|
|
16
|
+
return "—";
|
|
17
|
+
const d = new Date(waarde);
|
|
18
|
+
return Number.isNaN(d.getTime()) ? "—" : d.toLocaleDateString("nl-NL");
|
|
19
|
+
}
|
|
20
|
+
export function GeblokkeerdeOrdersCard() {
|
|
21
|
+
const { rows, overzicht, loading, error, busy, refresh, retry } = useGeblokkeerdeOrders();
|
|
22
|
+
const [filter, setFilter] = useState("");
|
|
23
|
+
const [melding, setMelding] = useState(null);
|
|
24
|
+
const safeRows = Array.isArray(rows) ? rows : [];
|
|
25
|
+
const codes = useMemo(() => {
|
|
26
|
+
const set = new Map();
|
|
27
|
+
for (const r of safeRows)
|
|
28
|
+
set.set(r.reasonCode, (set.get(r.reasonCode) ?? 0) + 1);
|
|
29
|
+
return Array.from(set.entries()).sort((a, b) => b[1] - a[1]);
|
|
30
|
+
}, [safeRows]);
|
|
31
|
+
const zichtbaar = filter ? safeRows.filter((r) => r.reasonCode === filter) : safeRows;
|
|
32
|
+
const opnieuw = async (ids) => {
|
|
33
|
+
try {
|
|
34
|
+
const n = await retry(ids);
|
|
35
|
+
setMelding(n === 0
|
|
36
|
+
? "Er is niets opnieuw aangeboden."
|
|
37
|
+
: `${n} order(s) opnieuw aangeboden. Ze verdwijnen zodra de verwerking ze heeft opgepakt.`);
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
setMelding(e?.message ? String(e.message) : "Opnieuw proberen is niet gelukt.");
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
return (_jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-start justify-between gap-4", children: [_jsxs("div", { children: [_jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(AlertTriangle, { className: "h-4 w-4 text-destructive" }), "Vastgelopen orders"] }), _jsx(CardDescription, { children: "Deze orders krijgen geen karrenindeling tot er iets aan de order verandert of je ze hier opnieuw aanbiedt." })] }), _jsxs("div", { className: "flex items-center gap-2", children: [zichtbaar.length > 0 && (_jsxs(Button, { variant: "outline", size: "sm", disabled: busy, onClick: () => void opnieuw(zichtbaar.map((r) => r.orderId)), children: [_jsx(RotateCcw, { className: "mr-2 h-4 w-4" }), "Alles opnieuw proberen"] })), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => void refresh(), disabled: loading, children: _jsx(RefreshCw, { className: `h-4 w-4 ${loading ? "animate-spin" : ""}` }) })] })] }), _jsxs(CardContent, { className: "space-y-4", children: [overzicht?.tickStale && (_jsx("p", { className: "rounded-md bg-muted p-3 text-sm text-muted-foreground", children: "De verwerking op de achtergrond heeft zich langer dan tien minuten niet gemeld. Opnieuw proberen zet de order wel klaar, maar hij wordt pas opgepakt zodra de verwerking weer draait." })), error && _jsx("p", { className: "text-sm text-destructive", children: error }), melding && _jsx("p", { className: "text-sm text-muted-foreground", children: melding }), loading && safeRows.length === 0 ? (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-6 w-full" }), _jsx(Skeleton, { className: "h-6 w-full" })] })) : safeRows.length === 0 ? (_jsxs("p", { className: "text-sm text-muted-foreground", children: ["Geen vastgelopen orders. Alles is ingedeeld of staat nog in de wachtrij", overzicht ? ` (${overzicht.pending} wachtend)` : "", "."] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsxs(Badge, { variant: filter === "" ? "default" : "outline", className: "cursor-pointer", onClick: () => setFilter(""), children: ["Alles (", safeRows.length, ")"] }), codes.map(([code, aantal]) => (_jsxs(Badge, { variant: filter === code ? "default" : "outline", className: "cursor-pointer", onClick: () => setFilter(code), children: [redenLabel(code), " (", aantal, ")"] }, code)))] }), _jsx("div", { className: "divide-y rounded-md border", children: zichtbaar.map((r) => (_jsxs("div", { className: "flex items-start justify-between gap-4 p-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "font-medium", children: r.orderRef }), _jsx(Badge, { variant: "secondary", children: redenLabel(r.reasonCode) }), _jsxs("span", { className: "text-xs text-muted-foreground", children: ["Leverdatum ", datum(r.deliveryDate), " \u00B7 sinds ", datum(r.blockedSince)] })] }), r.reason && (_jsx("p", { className: "mt-1 line-clamp-2 text-sm text-muted-foreground", children: r.reason }))] }), _jsx(Button, { variant: "outline", size: "sm", disabled: busy, onClick: () => void opnieuw([r.orderId]), children: "Opnieuw" })] }, r.queueId))) })] }))] })] }));
|
|
44
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LogistiekEnginePage.d.ts","sourceRoot":"","sources":["../../src/components/LogistiekEnginePage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"LogistiekEnginePage.d.ts","sourceRoot":"","sources":["../../src/components/LogistiekEnginePage.tsx"],"names":[],"mappings":"AAsEA,wBAAgB,mBAAmB,gCA+OlC"}
|
|
@@ -10,6 +10,7 @@ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent,
|
|
|
10
10
|
import { Container, Pencil, Plus, Puzzle, SlidersHorizontal, Trash2, Workflow } from "lucide-react";
|
|
11
11
|
import { Link } from "@tanstack/react-router";
|
|
12
12
|
import { StrategieDialog } from "./StrategieDialog";
|
|
13
|
+
import { WerkwijzeHerindelingCard } from "./WerkwijzeHerindelingCard";
|
|
13
14
|
import { useLogistiekStrategieen, } from "../hooks/useLogistiekStrategieen";
|
|
14
15
|
const SNELKOPPELINGEN = [
|
|
15
16
|
{
|
|
@@ -62,7 +63,7 @@ export function LogistiekEnginePage() {
|
|
|
62
63
|
setEditing(item);
|
|
63
64
|
setDialogOpen(true);
|
|
64
65
|
};
|
|
65
|
-
return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Hier bepaal je hoe orders in je bedrijf logistiek worden afgehandeld." }), _jsx("div", { className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-4", children: SNELKOPPELINGEN.map(({ to, icon: Icon, titel, uitleg }) => (_jsx(Link, { to: to, className: "group", children: _jsx(Card, { className: "h-full transition-colors group-hover:border-primary", children: _jsxs(CardHeader, { className: "p-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary", children: _jsx(Icon, { className: "h-4 w-4" }) }), _jsx(CardTitle, { className: "text-sm font-semibold leading-tight", children: titel })] }), _jsx(CardDescription, { className: "pt-1 text-xs leading-relaxed", children: uitleg })] }) }) }, to))) }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx(CardTitle, { children: "Werkwijzen" }), _jsx(CardDescription, { children: "Alleen een werkwijze die aan staat wordt op je orders toegepast." })] }), _jsxs(Button, { size: "sm", onClick: openNew, children: [_jsx(Plus, { className: "h-4 w-4" }), "Nieuwe werkwijze"] })] }) }), _jsx(CardContent, { children: loading ? (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-8 w-full" }), _jsx(Skeleton, { className: "h-8 w-full" }), _jsx(Skeleton, { className: "h-8 w-full" })] })) : error ? (_jsx("p", { className: "text-sm text-destructive", children: "Kon de gegevens niet ophalen. Probeer het over een moment opnieuw." })) : safeItems.length === 0 ? (_jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Je hebt nog geen werkwijze ingesteld." }), _jsxs(Button, { variant: "outline", size: "sm", onClick: openNew, children: [_jsx(Plus, { className: "h-4 w-4" }), "Eerste werkwijze aanmaken"] })] })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Naam" }), _jsx(TableHead, { children: "Status" }), _jsx(TableHead, { className: "w-48 text-right", children: "Acties" })] }) }), _jsx(TableBody, { children: safeItems.map((item) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-3 w-3 shrink-0 rounded-full border border-border", style: { backgroundColor: item.color ?? "#3b82f6" } }), _jsxs("div", { children: [_jsx("p", { className: "font-medium", children: item.name ?? "—" }), item.description && (_jsx("p", { className: "text-xs text-muted-foreground", children: item.description }))] })] }) }), _jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx("input", { type: "radio", name: "actieve-werkwijze", className: "h-4 w-4 accent-primary", checked: !!item.is_active, disabled: saving, onChange: () => kiesWerkwijze(item), "aria-label": `${item.name ?? "Werkwijze"} als actieve werkwijze kiezen` }), _jsx("span", { className: "sr-only", children: "Actief" })] }), _jsx(Badge, { variant: item.is_active ? "default" : "secondary", children: item.is_active ? "Actief" : "Uit" }), item.is_active && (_jsx(Button, { size: "sm", variant: "ghost", disabled: saving, onClick: () => void clearActive(), children: "Uitzetten" }))] }) }), _jsx(TableCell, { className: "text-right", children: _jsxs("div", { className: "flex justify-end gap-1", children: [_jsx(Button, { size: "sm", variant: "outline", asChild: true, children: _jsxs(Link, { to: "/logistiek-engine/rule-engine", search: { strategy: String(item.id), naam: item.name ?? "" }, children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Instellen"] }) }), _jsx(Button, { size: "icon", variant: "ghost", onClick: () => openEdit(item), "aria-label": "Bewerken", children: _jsx(Pencil, { className: "h-4 w-4" }) }), _jsx(Button, { size: "icon", variant: "ghost", onClick: () => setDeleting(item), "aria-label": "Verwijderen", children: _jsx(Trash2, { className: "h-4 w-4" }) })] }) })] }, String(item.id)))) })] })) })] }), _jsx(StrategieDialog, { open: dialogOpen, onOpenChange: setDialogOpen, strategie: editing, saving: saving, onSubmit: (input) => (editing ? update(editing.id, input) : create(input)) }), _jsx(AlertDialog, { open: !!deleting, onOpenChange: (open) => !open && setDeleting(null), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Werkwijze verwijderen?" }), _jsxs(AlertDialogDescription, { children: ["\"", deleting?.name, "\" wordt definitief verwijderd. Dit kan niet ongedaan worden gemaakt."] })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Annuleren" }), _jsx(AlertDialogAction, { onClick: async () => {
|
|
66
|
+
return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Hier bepaal je hoe orders in je bedrijf logistiek worden afgehandeld." }), _jsx("div", { className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-4", children: SNELKOPPELINGEN.map(({ to, icon: Icon, titel, uitleg }) => (_jsx(Link, { to: to, className: "group", children: _jsx(Card, { className: "h-full transition-colors group-hover:border-primary", children: _jsxs(CardHeader, { className: "p-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary", children: _jsx(Icon, { className: "h-4 w-4" }) }), _jsx(CardTitle, { className: "text-sm font-semibold leading-tight", children: titel })] }), _jsx(CardDescription, { className: "pt-1 text-xs leading-relaxed", children: uitleg })] }) }) }, to))) }), _jsx(WerkwijzeHerindelingCard, {}), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx(CardTitle, { children: "Werkwijzen" }), _jsx(CardDescription, { children: "Alleen een werkwijze die aan staat wordt op je orders toegepast." })] }), _jsxs(Button, { size: "sm", onClick: openNew, children: [_jsx(Plus, { className: "h-4 w-4" }), "Nieuwe werkwijze"] })] }) }), _jsx(CardContent, { children: loading ? (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-8 w-full" }), _jsx(Skeleton, { className: "h-8 w-full" }), _jsx(Skeleton, { className: "h-8 w-full" })] })) : error ? (_jsx("p", { className: "text-sm text-destructive", children: "Kon de gegevens niet ophalen. Probeer het over een moment opnieuw." })) : safeItems.length === 0 ? (_jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Je hebt nog geen werkwijze ingesteld." }), _jsxs(Button, { variant: "outline", size: "sm", onClick: openNew, children: [_jsx(Plus, { className: "h-4 w-4" }), "Eerste werkwijze aanmaken"] })] })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Naam" }), _jsx(TableHead, { children: "Status" }), _jsx(TableHead, { className: "w-48 text-right", children: "Acties" })] }) }), _jsx(TableBody, { children: safeItems.map((item) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-3 w-3 shrink-0 rounded-full border border-border", style: { backgroundColor: item.color ?? "#3b82f6" } }), _jsxs("div", { children: [_jsx("p", { className: "font-medium", children: item.name ?? "—" }), item.description && (_jsx("p", { className: "text-xs text-muted-foreground", children: item.description }))] })] }) }), _jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx("input", { type: "radio", name: "actieve-werkwijze", className: "h-4 w-4 accent-primary", checked: !!item.is_active, disabled: saving, onChange: () => kiesWerkwijze(item), "aria-label": `${item.name ?? "Werkwijze"} als actieve werkwijze kiezen` }), _jsx("span", { className: "sr-only", children: "Actief" })] }), _jsx(Badge, { variant: item.is_active ? "default" : "secondary", children: item.is_active ? "Actief" : "Uit" }), item.is_active && (_jsx(Button, { size: "sm", variant: "ghost", disabled: saving, onClick: () => void clearActive(), children: "Uitzetten" }))] }) }), _jsx(TableCell, { className: "text-right", children: _jsxs("div", { className: "flex justify-end gap-1", children: [_jsx(Button, { size: "sm", variant: "outline", asChild: true, children: _jsxs(Link, { to: "/logistiek-engine/rule-engine", search: { strategy: String(item.id), naam: item.name ?? "" }, children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Instellen"] }) }), _jsx(Button, { size: "icon", variant: "ghost", onClick: () => openEdit(item), "aria-label": "Bewerken", children: _jsx(Pencil, { className: "h-4 w-4" }) }), _jsx(Button, { size: "icon", variant: "ghost", onClick: () => setDeleting(item), "aria-label": "Verwijderen", children: _jsx(Trash2, { className: "h-4 w-4" }) })] }) })] }, String(item.id)))) })] })) })] }), _jsx(StrategieDialog, { open: dialogOpen, onOpenChange: setDialogOpen, strategie: editing, saving: saving, onSubmit: (input) => (editing ? update(editing.id, input) : create(input)) }), _jsx(AlertDialog, { open: !!deleting, onOpenChange: (open) => !open && setDeleting(null), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Werkwijze verwijderen?" }), _jsxs(AlertDialogDescription, { children: ["\"", deleting?.name, "\" wordt definitief verwijderd. Dit kan niet ongedaan worden gemaakt."] })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Annuleren" }), _jsx(AlertDialogAction, { onClick: async () => {
|
|
66
67
|
if (deleting)
|
|
67
68
|
await remove(deleting.id);
|
|
68
69
|
setDeleting(null);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RegiePage.d.ts","sourceRoot":"","sources":["../../src/components/RegiePage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"RegiePage.d.ts","sourceRoot":"","sources":["../../src/components/RegiePage.tsx"],"names":[],"mappings":"AA6EA,wBAAgB,SAAS,gCAmYxB"}
|
|
@@ -19,6 +19,7 @@ import { FloridayPushCard } from "./FloridayPushCard";
|
|
|
19
19
|
import { PrintjobsCard } from "./PrintjobsCard";
|
|
20
20
|
import { ConsistentieCard } from "./ConsistentieCard";
|
|
21
21
|
import { KarplanStatusCard } from "./KarplanStatusCard";
|
|
22
|
+
import { GeblokkeerdeOrdersCard } from "./GeblokkeerdeOrdersCard";
|
|
22
23
|
import { AfmetingenBronnenCard } from "./AfmetingenBronnenCard";
|
|
23
24
|
import { LOGISTICS_MODULES, TASK_LABEL, TASK_STATUS_LABEL, } from "../lib/logisticsTasks";
|
|
24
25
|
function tijd(value) {
|
|
@@ -76,7 +77,7 @@ export function RegiePage() {
|
|
|
76
77
|
}
|
|
77
78
|
return counts;
|
|
78
79
|
}, [safeTaken]);
|
|
79
|
-
return (_jsxs("div", { className: "space-y-6 p-6", children: [_jsxs("div", { className: "flex flex-wrap items-start justify-between gap-4", children: [_jsxs("div", { children: [_jsxs("h1", { className: "flex items-center gap-2 text-2xl font-semibold tracking-tight", children: [_jsx(Workflow, { className: "h-6 w-6 text-primary" }), "Overzicht & meldingen"] }), _jsx("p", { className: "text-muted-foreground text-sm", children: "Wat er vandaag klaarstaat en waar aandacht voor nodig is." })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "outline", asChild: true, children: _jsx(Link, { to: "/logistiek-engine", children: "Terug naar overzicht" }) }), _jsxs(Button, { variant: "outline", onClick: () => void laden(), disabled: loading, children: [_jsx(RefreshCw, { className: "mr-2 h-4 w-4" }), "Vernieuwen"] })] })] }), _jsx(ConsistentieCard, {}), _jsx(KarplanStatusCard, {}), _jsx(AfmetingenBronnenCard, {}), _jsx("div", { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3", children: Object.keys(LOGISTICS_MODULES).map((key) => (_jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-2", children: [_jsx(CardDescription, { children: LOGISTICS_MODULES[key] }), _jsx(CardTitle, { className: "text-2xl", children: perModule[key] ?? 0 })] }), _jsx(CardContent, { className: "text-muted-foreground text-xs", children: "openstaande en geplande taken" })] }, key))) }), _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { children: "Taken per order" }), _jsx(CardDescription, { children: "Elke order krijgt bij binnenkomst meteen de volledige keten aan taken toegewezen." }), _jsxs("div", { className: "pt-2", children: [_jsx(Label, { htmlFor: "regie-filter", className: "sr-only", children: "Zoeken" }), _jsx(Input, { id: "regie-filter", placeholder: "Zoek op ordernummer, taaktype of module\u2026", value: filter, onChange: (e) => setFilter(e.target.value), className: "max-w-sm" })] })] }), _jsx(CardContent, { className: "space-y-6", children: loading ? (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-10 w-full" }), _jsx(Skeleton, { className: "h-10 w-full" }), _jsx(Skeleton, { className: "h-10 w-full" })] })) : perOrder.length === 0 ? (_jsxs("p", { className: "text-muted-foreground py-8 text-center text-sm", children: ["Nog geen taken. Zodra een module een melding wegschrijft in", " ", _jsx("code", { className: "bg-muted rounded px-1", children: "logistics_events" }), " maakt de database de keten meteen aan."] })) : (perOrder.map(([orderRef, orderTaken]) => {
|
|
80
|
+
return (_jsxs("div", { className: "space-y-6 p-6", children: [_jsxs("div", { className: "flex flex-wrap items-start justify-between gap-4", children: [_jsxs("div", { children: [_jsxs("h1", { className: "flex items-center gap-2 text-2xl font-semibold tracking-tight", children: [_jsx(Workflow, { className: "h-6 w-6 text-primary" }), "Overzicht & meldingen"] }), _jsx("p", { className: "text-muted-foreground text-sm", children: "Wat er vandaag klaarstaat en waar aandacht voor nodig is." })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "outline", asChild: true, children: _jsx(Link, { to: "/logistiek-engine", children: "Terug naar overzicht" }) }), _jsxs(Button, { variant: "outline", onClick: () => void laden(), disabled: loading, children: [_jsx(RefreshCw, { className: "mr-2 h-4 w-4" }), "Vernieuwen"] })] })] }), _jsx(ConsistentieCard, {}), _jsx(KarplanStatusCard, {}), _jsx(GeblokkeerdeOrdersCard, {}), _jsx(AfmetingenBronnenCard, {}), _jsx("div", { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3", children: Object.keys(LOGISTICS_MODULES).map((key) => (_jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-2", children: [_jsx(CardDescription, { children: LOGISTICS_MODULES[key] }), _jsx(CardTitle, { className: "text-2xl", children: perModule[key] ?? 0 })] }), _jsx(CardContent, { className: "text-muted-foreground text-xs", children: "openstaande en geplande taken" })] }, key))) }), _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { children: "Taken per order" }), _jsx(CardDescription, { children: "Elke order krijgt bij binnenkomst meteen de volledige keten aan taken toegewezen." }), _jsxs("div", { className: "pt-2", children: [_jsx(Label, { htmlFor: "regie-filter", className: "sr-only", children: "Zoeken" }), _jsx(Input, { id: "regie-filter", placeholder: "Zoek op ordernummer, taaktype of module\u2026", value: filter, onChange: (e) => setFilter(e.target.value), className: "max-w-sm" })] })] }), _jsx(CardContent, { className: "space-y-6", children: loading ? (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-10 w-full" }), _jsx(Skeleton, { className: "h-10 w-full" }), _jsx(Skeleton, { className: "h-10 w-full" })] })) : perOrder.length === 0 ? (_jsxs("p", { className: "text-muted-foreground py-8 text-center text-sm", children: ["Nog geen taken. Zodra een module een melding wegschrijft in", " ", _jsx("code", { className: "bg-muted rounded px-1", children: "logistics_events" }), " maakt de database de keten meteen aan."] })) : (perOrder.map(([orderRef, orderTaken]) => {
|
|
80
81
|
const spoed = orderTaken.some((t) => (t.priority ?? 0) > 0);
|
|
81
82
|
const actief = orderTaken.some((t) => t.status !== "cancelled" && t.status !== "done" && t.status !== "superseded");
|
|
82
83
|
return (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs("h2", { className: "flex items-center gap-2 font-medium", children: ["Order ", orderRef, _jsxs("span", { className: "text-muted-foreground text-xs font-normal", children: [orderTaken.length, " taken"] }), spoed && (_jsx(Badge, { variant: "destructive", className: "text-[10px]", children: "spoed" }))] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: () => void zetPrioriteit(orderRef, spoed ? 0 : 100), children: [_jsx(ArrowUpCircle, { className: "mr-1 h-4 w-4" }), spoed ? "Normaal" : "Spoed"] }), actief && (_jsxs(Button, { variant: "outline", size: "sm", onClick: () => void annuleerOrder(orderRef), children: [_jsx(Ban, { className: "mr-1 h-4 w-4" }), "Annuleren"] })), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => void verwijderOrder(orderRef), children: _jsx(Trash2, { className: "h-4 w-4" }) })] })] }), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { className: "w-12", children: "#" }), _jsx(TableHead, { children: "Taak" }), _jsx(TableHead, { children: "Module" }), _jsx(TableHead, { children: "Start" }), _jsx(TableHead, { children: "Gereed" }), _jsx(TableHead, { children: "Status" }), _jsx(TableHead, { className: "text-right", children: "Actie" })] }) }), _jsx(TableBody, { children: orderTaken.map((t) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "text-muted-foreground", children: t.sequence + 1 }), _jsxs(TableCell, { className: "font-medium", children: [TASK_LABEL[t.task_type] ?? t.task_type, t.shadow && (_jsx(Badge, { variant: "outline", className: "ml-2 text-[10px]", children: "schaduw" }))] }), _jsx(TableCell, { className: "text-sm", children: LOGISTICS_MODULES[t.owner_module] ?? t.owner_module }), _jsxs(TableCell, { className: "text-sm", children: [tijd(t.planned_start), t.payload?.["pickWindowShifted"] === true && (_jsx(Badge, { variant: "outline", className: "ml-2 text-[10px]", title: "Het berekende moment lag in het verleden; deze taak is naar nu verschoven.", children: "te laat aangemeld" }))] }), _jsx(TableCell, { className: "text-sm", children: tijd(t.planned_end) }), _jsxs(TableCell, { children: [_jsx(Badge, { variant: statusVariant(t.status), children: TASK_STATUS_LABEL[t.status] ?? t.status }), t.claimed_by && (t.status === "claimed" || t.status === "in_progress") && (_jsxs("span", { className: "text-muted-foreground ml-2 text-xs", children: ["door ", t.claimed_by] })), t.error && _jsx("span", { className: "text-destructive ml-2 text-xs", children: t.error })] }), _jsxs(TableCell, { className: "space-x-1 text-right", children: [t.status === "planned" && (_jsx(Button, { variant: "outline", size: "sm", onClick: () => void claimTaak(t.id), children: "Claimen" })), (t.status === "claimed" || t.status === "in_progress") && (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", size: "sm", onClick: () => void rondTaakAf(t.id), children: "Afronden" }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => void meldTaakMislukt(t.id, "Handmatig gemeld vanuit regieoverzicht"), children: "Mislukt" })] }))] })] }, t.id))) })] })] }, orderRef));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WerkwijzeHerindelingCard.d.ts","sourceRoot":"","sources":["../../src/components/WerkwijzeHerindelingCard.tsx"],"names":[],"mappings":"AA4BA,wBAAgB,wBAAwB,gCAoGvC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// src/components/WerkwijzeHerindelingCard.tsx — welke methode geldt er echt?
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// Toont de methode van de actieve werkwijze, hoeveel karren nog volgens een
|
|
6
|
+
// oudere methode zijn berekend en de stand van de verwerking. Alles komt uit de
|
|
7
|
+
// Engine-functies; hier wordt niets zelf berekend.
|
|
8
|
+
// GEEN <Header /> hier — die levert de shell.
|
|
9
|
+
// ============================================================================
|
|
10
|
+
import { useState } from "react";
|
|
11
|
+
import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Skeleton, } from "@flowselections/core";
|
|
12
|
+
import { RefreshCw, RotateCcw, Ruler, Boxes } from "lucide-react";
|
|
13
|
+
import { useWerkwijzeStatus } from "../hooks/useWerkwijzeStatus";
|
|
14
|
+
const METHODE_LABEL = {
|
|
15
|
+
belading: "Indelen op basis van belading",
|
|
16
|
+
afmetingen: "Indelen op basis van afmetingen",
|
|
17
|
+
};
|
|
18
|
+
export function WerkwijzeHerindelingCard() {
|
|
19
|
+
const { status, loading, busy, error, refresh, herindelen } = useWerkwijzeStatus();
|
|
20
|
+
const [melding, setMelding] = useState(null);
|
|
21
|
+
const wachtrij = status?.wachtrij ?? {};
|
|
22
|
+
const pending = Number(wachtrij["pending"] ?? 0);
|
|
23
|
+
const blocked = Number(wachtrij["geblokkeerd"] ?? 0);
|
|
24
|
+
const opnieuw = async () => {
|
|
25
|
+
try {
|
|
26
|
+
const n = await herindelen();
|
|
27
|
+
setMelding(n === 0
|
|
28
|
+
? "Er stonden geen orders open om opnieuw in te delen."
|
|
29
|
+
: `${n} order(s) opnieuw aangemeld. De indeling wordt op de achtergrond bijgewerkt.`);
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
setMelding(e?.message ? String(e.message) : "Opnieuw indelen is niet gelukt.");
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
return (_jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-start justify-between gap-4", children: [_jsxs("div", { children: [_jsxs(CardTitle, { className: "flex items-center gap-2", children: [status?.methode === "afmetingen" ? (_jsx(Ruler, { className: "h-4 w-4" })) : (_jsx(Boxes, { className: "h-4 w-4" })), "Werkwijze en herindeling"] }), _jsx(CardDescription, { children: "Zo worden karren op dit moment samengesteld. Verandert de methode, dan worden alle openstaande orders automatisch opnieuw ingedeeld." })] }), _jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: () => void refresh(), disabled: loading, children: [_jsx(RefreshCw, { className: "h-4 w-4" }), "Verversen"] }), _jsxs(Button, { size: "sm", onClick: () => void opnieuw(), disabled: busy, children: [_jsx(RotateCcw, { className: "h-4 w-4" }), "Alles opnieuw indelen"] })] })] }), _jsx(CardContent, { className: "space-y-3", children: loading ? (_jsx(Skeleton, { className: "h-16 w-full" })) : error ? (_jsx("p", { className: "text-sm text-destructive", children: error })) : status ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Badge, { children: METHODE_LABEL[status.methode] ?? status.methode }), status.actieveWerkwijze ? (_jsxs("span", { className: "text-sm text-muted-foreground", children: ["volgens werkwijze \u201C", status.actieveWerkwijze, "\u201D"] })) : (_jsx("span", { className: "text-sm text-muted-foreground", children: "Er staat geen werkwijze aan; er wordt op belading ingedeeld." }))] }), _jsxs("div", { className: "grid gap-3 sm:grid-cols-3", children: [_jsxs("div", { className: "rounded-md border p-3", children: [_jsx("p", { className: "text-xs text-muted-foreground", children: "Karren volgens deze methode" }), _jsx("p", { className: "text-lg font-semibold", children: status.karrenVolgensMethode })] }), _jsxs("div", { className: "rounded-md border p-3", children: [_jsx("p", { className: "text-xs text-muted-foreground", children: "Karren van een oudere methode" }), _jsx("p", { className: "text-lg font-semibold", children: status.karrenOudeMethode })] }), _jsxs("div", { className: "rounded-md border p-3", children: [_jsx("p", { className: "text-xs text-muted-foreground", children: "Wacht op indelen" }), _jsx("p", { className: "text-lg font-semibold", children: pending }), blocked > 0 ? (_jsxs("p", { className: "text-xs text-destructive", children: [blocked, " geblokkeerd"] })) : null] })] }), status.karrenOudeMethode > 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Deze karren zijn nog met een eerdere methode berekend. Ze worden vervangen zodra de order opnieuw is ingedeeld; al geprinte of gecontroleerde karren blijven staan." })) : null, melding ? _jsx("p", { className: "text-sm", children: melding }) : null] })) : null })] }));
|
|
36
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface GeblokkeerdeOrder {
|
|
2
|
+
queueId: string;
|
|
3
|
+
orderId: string;
|
|
4
|
+
orderRef: string;
|
|
5
|
+
deliveryDate: string | null;
|
|
6
|
+
reason: string | null;
|
|
7
|
+
reasonCode: string;
|
|
8
|
+
attempts: number;
|
|
9
|
+
blockedSince: string | null;
|
|
10
|
+
}
|
|
11
|
+
export interface BlokkadeOverzicht {
|
|
12
|
+
blocked: number;
|
|
13
|
+
pending: number;
|
|
14
|
+
lastTickAt: string | null;
|
|
15
|
+
tickStale: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare const REDEN_LABEL: Record<string, string>;
|
|
18
|
+
export declare function redenLabel(code: string | null | undefined): string;
|
|
19
|
+
export declare function useGeblokkeerdeOrders(): {
|
|
20
|
+
rows: GeblokkeerdeOrder[];
|
|
21
|
+
overzicht: BlokkadeOverzicht | null;
|
|
22
|
+
loading: boolean;
|
|
23
|
+
error: string | null;
|
|
24
|
+
busy: boolean;
|
|
25
|
+
refresh: () => Promise<void>;
|
|
26
|
+
retry: (orderIds: string[]) => Promise<number>;
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=useGeblokkeerdeOrders.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useGeblokkeerdeOrders.d.ts","sourceRoot":"","sources":["../../src/hooks/useGeblokkeerdeOrders.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAY9C,CAAC;AAEF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,UAGzD;AAED,wBAAgB,qBAAqB;;;;;;;sBAqDhB,MAAM,EAAE;EAmB5B"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// src/hooks/useGeblokkeerdeOrders.ts — geblokkeerde orders ophalen en opnieuw
|
|
3
|
+
// aanbieden. De Engine is de enige bron: alles komt uit de database-functies
|
|
4
|
+
// logistics_blocked_orders / logistics_blocked_overview / logistics_plan_retry.
|
|
5
|
+
// ============================================================================
|
|
6
|
+
import { useCallback, useEffect, useState } from "react";
|
|
7
|
+
import { supabase } from "../integrations/supabase/moduleClient";
|
|
8
|
+
export const REDEN_LABEL = {
|
|
9
|
+
belading_onbekend: "Belading onbekend",
|
|
10
|
+
belading_onvolledig: "Belading onvolledig",
|
|
11
|
+
beladingsprofiel_ontbreekt: "Beladingsprofiel ontbreekt",
|
|
12
|
+
afmetingen_onbekend: "Afmetingen onbekend",
|
|
13
|
+
afmetingen_plan_ongeldig: "Afmetingenplan ongeldig",
|
|
14
|
+
kargrens_overschreden: "Te veel karren",
|
|
15
|
+
geen_karren: "Geen karren berekend",
|
|
16
|
+
verificatie_mislukt: "Controle mislukt",
|
|
17
|
+
bufferdekking_onvolledig: "Buffer niet volledig gedekt",
|
|
18
|
+
karindeling_geblokkeerd: "Karindeling geblokkeerd",
|
|
19
|
+
other: "Overige reden",
|
|
20
|
+
};
|
|
21
|
+
export function redenLabel(code) {
|
|
22
|
+
if (!code)
|
|
23
|
+
return REDEN_LABEL.other;
|
|
24
|
+
return REDEN_LABEL[code] ?? code;
|
|
25
|
+
}
|
|
26
|
+
export function useGeblokkeerdeOrders() {
|
|
27
|
+
const [rows, setRows] = useState([]);
|
|
28
|
+
const [overzicht, setOverzicht] = useState(null);
|
|
29
|
+
const [loading, setLoading] = useState(true);
|
|
30
|
+
const [error, setError] = useState(null);
|
|
31
|
+
const [busy, setBusy] = useState(false);
|
|
32
|
+
const load = useCallback(async () => {
|
|
33
|
+
setLoading(true);
|
|
34
|
+
setError(null);
|
|
35
|
+
try {
|
|
36
|
+
const [list, summary] = await Promise.all([
|
|
37
|
+
supabase.rpc("logistics_blocked_orders"),
|
|
38
|
+
supabase.rpc("logistics_blocked_overview"),
|
|
39
|
+
]);
|
|
40
|
+
if (list.error)
|
|
41
|
+
throw new Error(list.error.message);
|
|
42
|
+
if (summary.error)
|
|
43
|
+
throw new Error(summary.error.message);
|
|
44
|
+
const safe = Array.isArray(list.data) ? list.data : [];
|
|
45
|
+
setRows(safe.map((r) => ({
|
|
46
|
+
queueId: String(r.queue_id),
|
|
47
|
+
orderId: String(r.order_id),
|
|
48
|
+
orderRef: String(r.order_ref ?? r.order_id),
|
|
49
|
+
deliveryDate: r.delivery_date ?? null,
|
|
50
|
+
reason: r.reason ?? null,
|
|
51
|
+
reasonCode: String(r.reason_code ?? "other"),
|
|
52
|
+
attempts: Number(r.attempts ?? 0),
|
|
53
|
+
blockedSince: r.blocked_since ?? null,
|
|
54
|
+
})));
|
|
55
|
+
const o = (summary.data ?? {});
|
|
56
|
+
setOverzicht({
|
|
57
|
+
blocked: Number(o.blocked ?? 0),
|
|
58
|
+
pending: Number(o.pending ?? 0),
|
|
59
|
+
lastTickAt: o.lastTickAt ?? null,
|
|
60
|
+
tickStale: Boolean(o.tickStale ?? true),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
setError(e?.message ? String(e.message) : "Kon de geblokkeerde orders niet ophalen.");
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
setLoading(false);
|
|
68
|
+
}
|
|
69
|
+
}, []);
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
void load();
|
|
72
|
+
const timer = setInterval(() => void load(), 30000);
|
|
73
|
+
return () => clearInterval(timer);
|
|
74
|
+
}, [load]);
|
|
75
|
+
const retry = useCallback(async (orderIds) => {
|
|
76
|
+
const ids = Array.isArray(orderIds) ? orderIds.filter(Boolean) : [];
|
|
77
|
+
if (ids.length === 0)
|
|
78
|
+
return 0;
|
|
79
|
+
setBusy(true);
|
|
80
|
+
try {
|
|
81
|
+
const { data, error: rpcError } = await supabase.rpc("logistics_plan_retry", {
|
|
82
|
+
p_order_ids: ids,
|
|
83
|
+
});
|
|
84
|
+
if (rpcError)
|
|
85
|
+
throw new Error(rpcError.message);
|
|
86
|
+
await load();
|
|
87
|
+
return Number(data ?? 0);
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
setBusy(false);
|
|
91
|
+
}
|
|
92
|
+
}, [load]);
|
|
93
|
+
return { rows, overzicht, loading, error, busy, refresh: load, retry };
|
|
94
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useRuleEngine.d.ts","sourceRoot":"","sources":["../../src/hooks/useRuleEngine.ts"],"names":[],"mappings":"AAMA,OAAO,EAAsC,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAEtG,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;CACpB;AAiBD,MAAM,WAAW,oBAAoB;IACnC,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE,oBAAoB;;;;;;;;;yBAwF3C,MAAM,gBAAgB,MAAM;8BA2BE,MAAM,YAAY,kBAAkB;yBAUhE,UAAU,YAAY;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;+BA4CtD,MAAM;
|
|
1
|
+
{"version":3,"file":"useRuleEngine.d.ts","sourceRoot":"","sources":["../../src/hooks/useRuleEngine.ts"],"names":[],"mappings":"AAMA,OAAO,EAAsC,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAEtG,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;CACpB;AAiBD,MAAM,WAAW,oBAAoB;IACnC,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE,oBAAoB;;;;;;;;;yBAwF3C,MAAM,gBAAgB,MAAM;8BA2BE,MAAM,YAAY,kBAAkB;yBAUhE,UAAU,YAAY;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;+BA4CtD,MAAM;8BAmCP,WAAW;6BAYqB,MAAM;;EA4BzD"}
|
|
@@ -164,10 +164,13 @@ export function useRuleEngine(options) {
|
|
|
164
164
|
}
|
|
165
165
|
}, [loadVersions]);
|
|
166
166
|
const activateConfig = useCallback(async (configId) => {
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
167
|
+
// Er mag in de hele module maar één werkwijze actief zijn: anders leest de
|
|
168
|
+
// planner een andere werkwijze dan het scherm toont.
|
|
169
|
+
const { error: resetError } = await db()
|
|
170
|
+
.from("logistics_rule_configs")
|
|
171
|
+
.update({ is_active: false })
|
|
172
|
+
.neq("id", configId)
|
|
173
|
+
.eq("is_active", true);
|
|
171
174
|
if (resetError) {
|
|
172
175
|
toast.error("Activeren mislukt");
|
|
173
176
|
return;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface WerkwijzeStatus {
|
|
2
|
+
methode: "belading" | "afmetingen";
|
|
3
|
+
actieveWerkwijze: string | null;
|
|
4
|
+
actieveWerkwijzeId: string | null;
|
|
5
|
+
karrenVolgensMethode: number;
|
|
6
|
+
karrenOudeMethode: number;
|
|
7
|
+
wachtrij: Record<string, number>;
|
|
8
|
+
}
|
|
9
|
+
export declare function useWerkwijzeStatus(): {
|
|
10
|
+
status: WerkwijzeStatus | null;
|
|
11
|
+
loading: boolean;
|
|
12
|
+
busy: boolean;
|
|
13
|
+
error: string | null;
|
|
14
|
+
refresh: () => Promise<void>;
|
|
15
|
+
herindelen: () => Promise<number>;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=useWerkwijzeStatus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useWerkwijzeStatus.d.ts","sourceRoot":"","sources":["../../src/hooks/useWerkwijzeStatus.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,UAAU,GAAG,YAAY,CAAC;IACnC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,wBAAgB,kBAAkB;;;;;;sBAiCS,OAAO,CAAC,MAAM,CAAC;EAezD"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// src/hooks/useWerkwijzeStatus.ts — welke indelingsmethode geldt er echt?
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// De Engine is de enige bron: alles komt uit public.logistics_layout_method_status
|
|
5
|
+
// en public.logistics_replan_all. Hier wordt niets zelf berekend.
|
|
6
|
+
// ============================================================================
|
|
7
|
+
import { useCallback, useEffect, useState } from "react";
|
|
8
|
+
import { supabase } from "../integrations/supabase/moduleClient";
|
|
9
|
+
export function useWerkwijzeStatus() {
|
|
10
|
+
const [status, setStatus] = useState(null);
|
|
11
|
+
const [loading, setLoading] = useState(true);
|
|
12
|
+
const [busy, setBusy] = useState(false);
|
|
13
|
+
const [error, setError] = useState(null);
|
|
14
|
+
const load = useCallback(async () => {
|
|
15
|
+
setLoading(true);
|
|
16
|
+
setError(null);
|
|
17
|
+
try {
|
|
18
|
+
const { data, error: rpcError } = await supabase.rpc("logistics_layout_method_status");
|
|
19
|
+
if (rpcError)
|
|
20
|
+
throw rpcError;
|
|
21
|
+
const raw = (data ?? {});
|
|
22
|
+
setStatus({
|
|
23
|
+
methode: raw["methode"] === "afmetingen" ? "afmetingen" : "belading",
|
|
24
|
+
actieveWerkwijze: raw["actieveWerkwijze"] ?? null,
|
|
25
|
+
actieveWerkwijzeId: raw["actieveWerkwijzeId"] ?? null,
|
|
26
|
+
karrenVolgensMethode: Number(raw["karrenVolgensMethode"] ?? 0),
|
|
27
|
+
karrenOudeMethode: Number(raw["karrenOudeMethode"] ?? 0),
|
|
28
|
+
wachtrij: raw["wachtrij"] ?? {},
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
setError(e?.message ? String(e.message) : "Status kon niet worden opgehaald.");
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
setLoading(false);
|
|
36
|
+
}
|
|
37
|
+
}, []);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
void load();
|
|
40
|
+
}, [load]);
|
|
41
|
+
/** Alle open orders opnieuw laten indelen volgens de actieve methode. */
|
|
42
|
+
const herindelen = useCallback(async () => {
|
|
43
|
+
setBusy(true);
|
|
44
|
+
try {
|
|
45
|
+
const { data, error: rpcError } = await supabase.rpc("logistics_replan_all", {
|
|
46
|
+
p_reason: "handmatig_herindelen",
|
|
47
|
+
});
|
|
48
|
+
if (rpcError)
|
|
49
|
+
throw rpcError;
|
|
50
|
+
await load();
|
|
51
|
+
return Number(data?.aangemeld ?? 0);
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
setBusy(false);
|
|
55
|
+
}
|
|
56
|
+
}, [load]);
|
|
57
|
+
return { status, loading, busy, error, refresh: load, herindelen };
|
|
58
|
+
}
|
package/dist-lib/index.d.ts
CHANGED
|
@@ -11,7 +11,10 @@ export { FloridayPushCard } from './components/FloridayPushCard';
|
|
|
11
11
|
export { PrintjobsCard } from './components/PrintjobsCard';
|
|
12
12
|
export { ConsistentieCard } from './components/ConsistentieCard';
|
|
13
13
|
export { KarplanStatusCard } from './components/KarplanStatusCard';
|
|
14
|
+
export { GeblokkeerdeOrdersCard } from './components/GeblokkeerdeOrdersCard';
|
|
14
15
|
export { AfmetingenBronnenCard } from './components/AfmetingenBronnenCard';
|
|
16
|
+
export { WerkwijzeHerindelingCard } from './components/WerkwijzeHerindelingCard';
|
|
17
|
+
export * from './hooks/useWerkwijzeStatus';
|
|
15
18
|
export * from './hooks/usePrintjobs';
|
|
16
19
|
export * from './components/VertragingMeldingenCard';
|
|
17
20
|
export * from './components/CapaciteitMeldingenCard';
|
package/dist-lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,cAAc,kBAAkB,CAAC;AACjC,cAAc,kCAAkC,CAAC;AACjD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,mCAAmC,CAAC;AAClD,cAAc,oCAAoC,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,cAAc,kBAAkB,CAAC;AACjC,cAAc,kCAAkC,CAAC;AACjD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,mCAAmC,CAAC;AAClD,cAAc,oCAAoC,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAE7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACjF,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sBAAsB,CAAC;AAGrC,cAAc,sCAAsC,CAAC;AACrD,cAAc,sCAAsC,CAAC;AACrD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AAGrC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAEnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,2BAA2B,EAAE,MAAM,mDAAmD,CAAC;AAChG,cAAc,iCAAiC,CAAC;AAChD,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qCAAqC,CAAC;AAMpD,eAAO,MAAM,qBAAqB,EAAE,UAcnC,CAAC"}
|
package/dist-lib/index.js
CHANGED
|
@@ -14,7 +14,10 @@ export { FloridayPushCard } from './components/FloridayPushCard';
|
|
|
14
14
|
export { PrintjobsCard } from './components/PrintjobsCard';
|
|
15
15
|
export { ConsistentieCard } from './components/ConsistentieCard';
|
|
16
16
|
export { KarplanStatusCard } from './components/KarplanStatusCard';
|
|
17
|
+
export { GeblokkeerdeOrdersCard } from './components/GeblokkeerdeOrdersCard';
|
|
17
18
|
export { AfmetingenBronnenCard } from './components/AfmetingenBronnenCard';
|
|
19
|
+
export { WerkwijzeHerindelingCard } from './components/WerkwijzeHerindelingCard';
|
|
20
|
+
export * from './hooks/useWerkwijzeStatus';
|
|
18
21
|
export * from './hooks/usePrintjobs';
|
|
19
22
|
export * from './components/VertragingMeldingenCard';
|
|
20
23
|
export * from './components/CapaciteitMeldingenCard';
|