@flowselections/logistiek-engine 1.0.21 → 1.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/components/PrintjobsCard.d.ts.map +1 -1
- package/dist-lib/components/PrintjobsCard.js +16 -10
- package/dist-lib/components/RuleEnginePage.d.ts.map +1 -1
- package/dist-lib/components/RuleEnginePage.js +1 -1
- package/dist-lib/components/rule-engine/RuleFieldInput.d.ts +3 -1
- package/dist-lib/components/rule-engine/RuleFieldInput.d.ts.map +1 -1
- package/dist-lib/components/rule-engine/RuleFieldInput.js +6 -2
- package/dist-lib/hooks/usePrintjobs.d.ts +19 -0
- package/dist-lib/hooks/usePrintjobs.d.ts.map +1 -1
- package/dist-lib/hooks/usePrintjobs.js +98 -24
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +37 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +37 -0
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/moduleClient.d.ts +37 -0
- package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +37 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/floridayDocuments.functions.d.ts +111 -0
- package/dist-lib/lib/floridayDocuments.functions.d.ts.map +1 -1
- package/dist-lib/lib/floridayPush.functions.d.ts +37 -0
- package/dist-lib/lib/floridayPush.functions.d.ts.map +1 -1
- package/dist-lib/lib/loadCarrierPlanning.functions.d.ts +185 -0
- package/dist-lib/lib/loadCarrierPlanning.functions.d.ts.map +1 -1
- package/dist-lib/lib/logisticsRecalcJob.functions.d.ts +74 -0
- package/dist-lib/lib/logisticsRecalcJob.functions.d.ts.map +1 -1
- package/dist-lib/lib/logisticsRecalculate.functions.d.ts +37 -0
- package/dist-lib/lib/logisticsRecalculate.functions.d.ts.map +1 -1
- package/dist-lib/lib/ruleEngineSchema.d.ts +4 -0
- package/dist-lib/lib/ruleEngineSchema.d.ts.map +1 -1
- package/dist-lib/lib/ruleEngineSchema.js +21 -3
- package/dist-lib/lib/ruleEngineWizard.d.ts.map +1 -1
- package/dist-lib/lib/ruleEngineWizard.js +3 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PrintjobsCard.d.ts","sourceRoot":"","sources":["../../src/components/PrintjobsCard.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PrintjobsCard.d.ts","sourceRoot":"","sources":["../../src/components/PrintjobsCard.tsx"],"names":[],"mappings":"AAsCA,wBAAgB,aAAa,gCA2G5B"}
|
|
@@ -4,8 +4,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
4
4
|
// de pickschermen zijn aangemaakt, met de vastgelopen opdrachten bovenaan.
|
|
5
5
|
// ============================================================================
|
|
6
6
|
import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Skeleton, } from "@flowselections/core";
|
|
7
|
-
import { Printer, RefreshCw } from "lucide-react";
|
|
8
|
-
import { PRINTJOB_STATUS_LABEL, usePrintjobs } from "../hooks/usePrintjobs";
|
|
7
|
+
import { AlertTriangle, Printer, RefreshCw } from "lucide-react";
|
|
8
|
+
import { PRINTJOB_STATUS_LABEL, PRINTJOB_STUCK_MINUTES, formatAge, usePrintjobs, } from "../hooks/usePrintjobs";
|
|
9
9
|
function tijd(value) {
|
|
10
10
|
try {
|
|
11
11
|
return new Date(value).toLocaleString("nl-NL", { dateStyle: "short", timeStyle: "short" });
|
|
@@ -24,14 +24,20 @@ function statusVariant(status) {
|
|
|
24
24
|
return "outline";
|
|
25
25
|
}
|
|
26
26
|
export function PrintjobsCard() {
|
|
27
|
-
const { jobs, loading, reload } = usePrintjobs();
|
|
27
|
+
const { jobs, totals, loading, reload } = usePrintjobs();
|
|
28
28
|
const safe = Array.isArray(jobs) ? jobs : [];
|
|
29
29
|
const vastgelopen = safe.filter((j) => j.status === "mislukt");
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
const stuck = safe
|
|
31
|
+
.filter((j) => j.stuck)
|
|
32
|
+
.sort((a, b) => b.ageMinutes - a.ageMinutes);
|
|
33
|
+
const overig = safe.filter((j) => !j.stuck && (j.status === "wachtend" || j.status === "bezig"));
|
|
34
|
+
const zichtbaar = [...stuck, ...vastgelopen, ...overig].slice(0, 25);
|
|
35
|
+
const nietGetoond = Math.max(0, totals.wachtend + totals.bezig + totals.mislukt - zichtbaar.length);
|
|
36
|
+
return (_jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(Printer, { className: "h-4 w-4 text-muted-foreground" }), "Printopdrachten"] }), _jsxs(CardDescription, { children: [totals.wachtend, " in de wachtrij, ", totals.bezig, " bezig, ", totals.mislukt, " mislukt.", nietGetoond > 0 ? ` ${nietGetoond} daarvan staan niet in deze lijst.` : ""] })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => void reload(), children: [_jsx(RefreshCw, { className: "mr-1 h-4 w-4" }), "Vernieuwen"] })] }), _jsxs(CardContent, { className: "space-y-3", children: [totals.wachtendZonderLayout > 0 && (_jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-destructive/40 bg-destructive/10 p-3", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0 text-destructive" }), _jsxs("div", { className: "text-sm", children: [_jsxs("p", { className: "font-semibold text-destructive", children: [totals.wachtendZonderLayout, " printopdracht", totals.wachtendZonderLayout === 1 ? "" : "en", " staat al langer dan", " ", PRINTJOB_STUCK_MINUTES, " minuten te wachten zonder ontwerp."] }), _jsx("p", { className: "text-muted-foreground", children: "Deze blijven staan tot er een layout is: maak in de Layout maker een layout voor de genoemde documentsoort aan, of koppel er hier \u00E9\u00E9n aan. Ze verlopen niet vanzelf." })] })] })), loading ? (_jsx(Skeleton, { className: "h-24 w-full" })) : zichtbaar.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Geen openstaande of vastgelopen printopdrachten." })) : (zichtbaar.map((j) => (_jsx("div", { className: j.stuck
|
|
37
|
+
? "rounded-lg border border-destructive/40 bg-destructive/5 p-3"
|
|
38
|
+
: "rounded-lg border border-border p-3", children: _jsxs("div", { className: "flex flex-wrap items-start justify-between gap-2", children: [_jsxs("div", { children: [_jsxs("p", { className: "text-sm font-semibold", children: [j.documentnaam ?? "Pickdocument", j.documenttype ? ` · ${j.documenttype}` : ""] }), _jsxs("p", { className: "text-xs text-muted-foreground", children: [j.printplekNaam
|
|
39
|
+
? `Printplek: ${j.printplekNaam}`
|
|
40
|
+
: "Nog geen printplek gekoppeld", j.printerNaam ? ` · Printer: ${j.printerNaam}` : "", j.layoutNaam ? ` · Ontwerp: ${j.layoutNaam}` : " · Nog geen ontwerp gekoppeld", " · ", tijd(j.createdAt)] }), j.foutmelding ? (_jsx("p", { className: "mt-1 text-xs text-destructive", children: j.foutmelding })) : null] }), _jsxs("div", { className: "flex flex-col items-end gap-1", children: [_jsx(Badge, { variant: statusVariant(j.status), children: PRINTJOB_STATUS_LABEL[j.status] ?? j.status }), _jsxs("span", { className: j.stuck
|
|
41
|
+
? "text-xs font-medium text-destructive"
|
|
42
|
+
: "text-xs text-muted-foreground", children: [formatAge(j.ageMinutes), " oud"] })] })] }) }, j.id))))] })] }));
|
|
37
43
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuleEnginePage.d.ts","sourceRoot":"","sources":["../../src/components/RuleEnginePage.tsx"],"names":[],"mappings":"AAmEA,MAAM,WAAW,mBAAmB;IAClC,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAQD,wBAAgB,cAAc,CAAC,EAC7B,UAAU,EAAE,cAAqB,EACjC,YAAY,EAAE,gBAAuB,GACtC,GAAE,mBAAwB,+
|
|
1
|
+
{"version":3,"file":"RuleEnginePage.d.ts","sourceRoot":"","sources":["../../src/components/RuleEnginePage.tsx"],"names":[],"mappings":"AAmEA,MAAM,WAAW,mBAAmB;IAClC,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAQD,wBAAgB,cAAc,CAAC,EAC7B,UAAU,EAAE,cAAqB,EACjC,YAAY,EAAE,gBAAuB,GACtC,GAAE,mBAAwB,+BAyZ1B"}
|
|
@@ -129,6 +129,6 @@ export function RuleEnginePage({ strategyId: strategyIdProp = null, strategyName
|
|
|
129
129
|
const basicFields = visibleFields.filter((f) => !isAdvancedField(f));
|
|
130
130
|
const advancedFields = visibleFields.filter((f) => isAdvancedField(f));
|
|
131
131
|
const isLastGroup = groupIndex === step.groups.length - 1;
|
|
132
|
-
return (_jsxs(CollapsibleGroup, { title: group.label, help: group.description, summary: groupSummary(group, stepValues), defaultOpen: groupIndex === 0, children: [basicFields.length > 0 && (_jsx("div", { className: "grid gap-4 md:grid-cols-2", children: basicFields.map((field) => (_jsx("div", { className: fieldClass(field), children: _jsx(RuleFieldInput, { field: field, value: stepValues[field.key], onChange: (value) => setFieldValue(field.key, value) }) }, field.key))) })), (advancedFields.length > 0 || (step.showConditions && isLastGroup)) && (_jsxs("details", { className: "rounded-lg border border-dashed border-border", children: [_jsx("summary", { className: "cursor-pointer px-3 py-2 text-sm font-medium text-muted-foreground", children: "Geavanceerd" }), _jsxs("div", { className: "space-y-4 border-t border-border px-3 py-4", children: [advancedFields.length > 0 && (_jsx("div", { className: "grid gap-4 md:grid-cols-2", children: advancedFields.map((field) => (_jsx("div", { className: fieldClass(field), children: _jsx(RuleFieldInput, { field: field, value: stepValues[field.key], onChange: (value) => setFieldValue(field.key, value) }) }, field.key))) })), step.showConditions && isLastGroup && (_jsx(ConditionsEditor, { conditions: stepConditions, onChange: setConditions }))] })] }))] }, group.id));
|
|
132
|
+
return (_jsxs(CollapsibleGroup, { title: group.label, help: group.description, summary: groupSummary(group, stepValues), defaultOpen: groupIndex === 0, children: [basicFields.length > 0 && (_jsx("div", { className: "grid gap-4 md:grid-cols-2", children: basicFields.map((field) => (_jsx("div", { className: fieldClass(field), children: _jsx(RuleFieldInput, { field: field, value: stepValues[field.key], stepValues: stepValues, onChange: (value) => setFieldValue(field.key, value) }) }, field.key))) })), (advancedFields.length > 0 || (step.showConditions && isLastGroup)) && (_jsxs("details", { className: "rounded-lg border border-dashed border-border", children: [_jsx("summary", { className: "cursor-pointer px-3 py-2 text-sm font-medium text-muted-foreground", children: "Geavanceerd" }), _jsxs("div", { className: "space-y-4 border-t border-border px-3 py-4", children: [advancedFields.length > 0 && (_jsx("div", { className: "grid gap-4 md:grid-cols-2", children: advancedFields.map((field) => (_jsx("div", { className: fieldClass(field), children: _jsx(RuleFieldInput, { field: field, value: stepValues[field.key], stepValues: stepValues, onChange: (value) => setFieldValue(field.key, value) }) }, field.key))) })), step.showConditions && isLastGroup && (_jsx(ConditionsEditor, { conditions: stepConditions, onChange: setConditions }))] })] }))] }, group.id));
|
|
133
133
|
}) })] }), _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { variant: "outline", onClick: () => setStepIndex((i) => Math.max(0, i - 1)), disabled: stepIndex === 0, children: [_jsx(ChevronLeft, { className: "h-4 w-4" }), "Vorige"] }), _jsxs(Button, { variant: "outline", onClick: () => setStepIndex((i) => Math.min(WIZARD_STEPS.length - 1, i + 1)), disabled: stepIndex === WIZARD_STEPS.length - 1, children: ["Volgende", _jsx(ChevronRight, { className: "h-4 w-4" })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Button, { variant: "outline", disabled: saving, onClick: () => saveConfig(activeConfig), children: [_jsx(Save, { className: "h-4 w-4" }), "Opslaan"] }), _jsxs(Button, { disabled: saving || activeConfig.is_active, onClick: () => activateConfig(activeConfig.id), children: [_jsx(CheckCircle2, { className: "h-4 w-4" }), activeConfig.is_active ? "Actief" : "Activeren"] }), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Meer acties", children: _jsx(MoreVertical, { className: "h-4 w-4" }) }) }), _jsx(DropdownMenuContent, { align: "end", children: _jsxs(DropdownMenuItem, { onClick: () => deleteConfig(activeConfig.id), children: [_jsx(Trash2, { className: "h-4 w-4" }), "Werkwijze verwijderen"] }) })] })] })] })] }), _jsx("div", { className: "lg:min-h-0 lg:overflow-y-auto lg:pr-1", children: _jsxs(Tabs, { defaultValue: "impact", children: [_jsxs(TabsList, { className: "w-full", children: [_jsx(TabsTrigger, { className: "flex-1", value: "impact", children: "Aandachtspunten" }), _jsx(TabsTrigger, { className: "flex-1", value: "versies", children: "Eerdere versies" })] }), _jsx(TabsContent, { value: "impact", className: "mt-3", children: _jsx(ImpactPanel, { settings: settings }) }), _jsx(TabsContent, { value: "versies", className: "mt-3", children: _jsx(VersionsPanel, { versions: versions, currentSettings: settings, onRestore: restoreVersion }) })] }) })] })), _jsx(Dialog, { open: createOpen, onOpenChange: setCreateOpen, children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Nieuwe werkwijze" }), _jsx(DialogDescription, { children: "Elke werkwijze beschrijft hoe orders bij jou verwerkt worden." })] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Naam" }), _jsx(Input, { value: newName, onChange: (e) => setNewName(e.target.value), placeholder: "Bijv. Standaard werkwijze" })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Omschrijving" }), _jsx(Textarea, { value: newDescription, onChange: (e) => setNewDescription(e.target.value), placeholder: "Waarvoor gebruik je deze werkwijze?" })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => setCreateOpen(false), children: "Annuleren" }), _jsx(Button, { onClick: handleCreate, disabled: !newName.trim(), children: "Aanmaken" })] })] }) })] }));
|
|
134
134
|
}
|
|
@@ -3,7 +3,9 @@ interface Props {
|
|
|
3
3
|
field: RuleField;
|
|
4
4
|
value: unknown;
|
|
5
5
|
onChange: (value: unknown) => void;
|
|
6
|
+
/** Alle waarden van deze stap, nodig om een terugval zichtbaar te maken. */
|
|
7
|
+
stepValues?: Record<string, unknown>;
|
|
6
8
|
}
|
|
7
|
-
export declare function RuleFieldInput({ field, value, onChange }: Props): import("react").JSX.Element | null;
|
|
9
|
+
export declare function RuleFieldInput({ field, value, onChange, stepValues }: Props): import("react").JSX.Element | null;
|
|
8
10
|
export {};
|
|
9
11
|
//# sourceMappingURL=RuleFieldInput.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuleFieldInput.d.ts","sourceRoot":"","sources":["../../../src/components/rule-engine/RuleFieldInput.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAM3E,UAAU,KAAK;IACb,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"RuleFieldInput.d.ts","sourceRoot":"","sources":["../../../src/components/rule-engine/RuleFieldInput.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAM3E,UAAU,KAAK;IACb,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,KAAK,sCAwT3E"}
|
|
@@ -10,7 +10,7 @@ import { fieldHelp } from "../../lib/ruleEngineWizard";
|
|
|
10
10
|
import { FieldHelp, FieldLabel } from "./FieldHelp";
|
|
11
11
|
import { usePickstations } from "../../hooks/usePickstations";
|
|
12
12
|
import { useDocumentLayouts } from "../../hooks/useDocumentLayouts";
|
|
13
|
-
export function RuleFieldInput({ field, value, onChange }) {
|
|
13
|
+
export function RuleFieldInput({ field, value, onChange, stepValues }) {
|
|
14
14
|
const { stations, types, loading } = usePickstations();
|
|
15
15
|
const { layouts: matching, allLayouts, autoLayout, loading: layoutsLoading, } = useDocumentLayouts(field.layoutTypes);
|
|
16
16
|
const help = fieldHelp(field);
|
|
@@ -24,7 +24,11 @@ export function RuleFieldInput({ field, value, onChange }) {
|
|
|
24
24
|
const others = allLayouts.filter((l) => !matching.some((m) => m.id === l.id));
|
|
25
25
|
const options = [...matching, ...others];
|
|
26
26
|
const isAuto = !value;
|
|
27
|
-
|
|
27
|
+
const fallbackValue = field.fallbackKey ? stepValues?.[field.fallbackKey] : undefined;
|
|
28
|
+
const fallbackId = typeof fallbackValue === "string" ? fallbackValue : "";
|
|
29
|
+
const fallbackLayout = fallbackId ? allLayouts.find((l) => l.id === fallbackId) : undefined;
|
|
30
|
+
const fallbackActive = Boolean(field.fallbackKey && isAuto && fallbackId);
|
|
31
|
+
return (_jsxs("div", { className: "space-y-2", children: [_jsx(FieldLabel, { label: field.label, help: help }), _jsxs(Select, { value: String(value ?? ""), onValueChange: (v) => onChange(v === "__none__" ? "" : v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: layoutsLoading ? "Laden…" : "Automatisch — de layout uit de Layout maker" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "__none__", children: "Automatisch \u2014 de layout uit de Layout maker" }), options.map((layout) => (_jsxs(SelectItem, { value: layout.id, children: [layout.name, " \u00B7 ", layout.documentType, " \u00B7 v", layout.version, layout.active ? "" : " (inactief)"] }, layout.id)))] })] }), !layoutsLoading && fallbackActive && (_jsxs("p", { className: "rounded-md border border-dashed border-border bg-muted/40 px-2 py-1.5 text-xs text-muted-foreground", children: ["Dit veld is leeg, dus dit document volgt nu de instelling voor", " ", field.fallbackLabel ?? "verzendbrieven", fallbackLayout ? `: “${fallbackLayout.name}”` : "", ". Kies hier een eigen layout om dat onderscheid w\u00E9l te maken."] })), !layoutsLoading && isAuto && !fallbackActive && autoLayout && (_jsxs("p", { className: "text-xs text-muted-foreground", children: ["Nu wordt \u201C", autoLayout.name, "\u201D gebruikt \u2014 de layout die in de Layout maker op actief staat."] })), !layoutsLoading && isAuto && !fallbackActive && !autoLayout && (_jsx("p", { className: "text-xs text-destructive", children: "Er staat nog geen layout op actief in de Layout maker. Maak daar een layout aan en zet die op actief, anders blijft het printen wachten." })), !layoutsLoading && options.length === 0 && (_jsx("p", { className: "text-xs text-muted-foreground", children: "Er staan nog geen layouts klaar om te kiezen." }))] }));
|
|
28
32
|
}
|
|
29
33
|
if (field.type === "pickstation") {
|
|
30
34
|
const options = field.type === "pickstation" ? stations : types;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** Vanaf hoeveel minuten een wachtende job zonder layout als vastgelopen telt. */
|
|
2
|
+
export declare const PRINTJOB_STUCK_MINUTES = 15;
|
|
1
3
|
export interface Printjob {
|
|
2
4
|
id: string;
|
|
3
5
|
documentnaam: string | null;
|
|
@@ -6,14 +8,31 @@ export interface Printjob {
|
|
|
6
8
|
foutmelding: string | null;
|
|
7
9
|
pogingen: number;
|
|
8
10
|
printerNaam: string | null;
|
|
11
|
+
layoutId: string | null;
|
|
9
12
|
layoutNaam: string | null;
|
|
10
13
|
printplekNaam: string | null;
|
|
11
14
|
externJobId: string | null;
|
|
12
15
|
createdAt: string;
|
|
16
|
+
/** Leeftijd in minuten op het moment van ophalen. */
|
|
17
|
+
ageMinutes: number;
|
|
18
|
+
/** Wachtend, geen layout gekoppeld en ouder dan de drempel. */
|
|
19
|
+
stuck: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface PrintjobTotals {
|
|
22
|
+
/** Alle jobs met status 'wachtend', over de hele tabel. */
|
|
23
|
+
wachtend: number;
|
|
24
|
+
/** Alle jobs met status 'bezig', over de hele tabel. */
|
|
25
|
+
bezig: number;
|
|
26
|
+
/** Alle jobs met status 'mislukt', over de hele tabel. */
|
|
27
|
+
mislukt: number;
|
|
28
|
+
/** Wachtend zonder layout_id en ouder dan PRINTJOB_STUCK_MINUTES. */
|
|
29
|
+
wachtendZonderLayout: number;
|
|
13
30
|
}
|
|
14
31
|
export declare const PRINTJOB_STATUS_LABEL: Record<string, string>;
|
|
32
|
+
export declare function formatAge(minutes: number): string;
|
|
15
33
|
export declare function usePrintjobs(limit?: number): {
|
|
16
34
|
jobs: Printjob[];
|
|
35
|
+
totals: PrintjobTotals;
|
|
17
36
|
loading: boolean;
|
|
18
37
|
reload: () => Promise<void>;
|
|
19
38
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePrintjobs.d.ts","sourceRoot":"","sources":["../../src/hooks/usePrintjobs.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"usePrintjobs.d.ts","sourceRoot":"","sources":["../../src/hooks/usePrintjobs.ts"],"names":[],"mappings":"AAeA,kFAAkF;AAClF,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,UAAU,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAMxD,CAAC;AAaF,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAOjD;AAED,wBAAgB,YAAY,CAAC,KAAK,SAAK;;;;;EA4HtC"}
|
|
@@ -2,9 +2,17 @@
|
|
|
2
2
|
// src/hooks/usePrintjobs.ts — status van printopdrachten die vanuit de
|
|
3
3
|
// pickschermen zijn aangemaakt. Alleen lezen; het printen zelf gebeurt in de
|
|
4
4
|
// Layout maker via de PrintNode API.
|
|
5
|
+
//
|
|
6
|
+
// De Layout maker heeft een vervalcontrole voor jobs op 'bezig'. Voor
|
|
7
|
+
// 'wachtend' bestaat die niet: een job zonder layout blijft daar staan tot
|
|
8
|
+
// iemand de layout aanmaakt. Daarom telt deze hook de wachtende jobs over de
|
|
9
|
+
// HELE tabel (niet over de opgehaalde rijen) en markeert hij de jobs die
|
|
10
|
+
// zonder layout te lang blijven staan.
|
|
5
11
|
// ============================================================================
|
|
6
12
|
import { useCallback, useEffect, useState } from "react";
|
|
7
13
|
import { supabase } from "@flowselections/core";
|
|
14
|
+
/** Vanaf hoeveel minuten een wachtende job zonder layout als vastgelopen telt. */
|
|
15
|
+
export const PRINTJOB_STUCK_MINUTES = 15;
|
|
8
16
|
export const PRINTJOB_STATUS_LABEL = {
|
|
9
17
|
wachtend: "In de wachtrij",
|
|
10
18
|
bezig: "Bezig",
|
|
@@ -13,19 +21,66 @@ export const PRINTJOB_STATUS_LABEL = {
|
|
|
13
21
|
geannuleerd: "Geannuleerd",
|
|
14
22
|
};
|
|
15
23
|
const db = () => supabase;
|
|
16
|
-
|
|
24
|
+
const SELECT = "id, documentnaam, documenttype, status, foutmelding, pogingen, layout_id, extern_job_id, created_at, payload, printers(naam), document_layouts(naam)";
|
|
25
|
+
function minutesSince(iso) {
|
|
26
|
+
const t = Date.parse(iso);
|
|
27
|
+
if (!Number.isFinite(t))
|
|
28
|
+
return 0;
|
|
29
|
+
return Math.max(0, Math.round((Date.now() - t) / 60000));
|
|
30
|
+
}
|
|
31
|
+
export function formatAge(minutes) {
|
|
32
|
+
if (minutes < 1)
|
|
33
|
+
return "zojuist";
|
|
34
|
+
if (minutes < 60)
|
|
35
|
+
return `${minutes} min`;
|
|
36
|
+
const uren = Math.floor(minutes / 60);
|
|
37
|
+
if (uren < 24)
|
|
38
|
+
return `${uren} uur`;
|
|
39
|
+
const dagen = Math.floor(uren / 24);
|
|
40
|
+
return `${dagen} dag${dagen === 1 ? "" : "en"}`;
|
|
41
|
+
}
|
|
42
|
+
export function usePrintjobs(limit = 50) {
|
|
17
43
|
const [jobs, setJobs] = useState([]);
|
|
44
|
+
const [totals, setTotals] = useState({
|
|
45
|
+
wachtend: 0,
|
|
46
|
+
bezig: 0,
|
|
47
|
+
mislukt: 0,
|
|
48
|
+
wachtendZonderLayout: 0,
|
|
49
|
+
});
|
|
18
50
|
const [loading, setLoading] = useState(true);
|
|
19
51
|
const reload = useCallback(async () => {
|
|
20
52
|
setLoading(true);
|
|
21
53
|
try {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
.limit(limit)
|
|
27
|
-
|
|
28
|
-
|
|
54
|
+
const stuckBefore = new Date(Date.now() - PRINTJOB_STUCK_MINUTES * 60000).toISOString();
|
|
55
|
+
// Recente rijen + de OUDSTE wachtende rijen, zodat een stapel de oudste
|
|
56
|
+
// job niet uit beeld duwt.
|
|
57
|
+
const [recentRes, oudsteRes, wachtendRes, bezigRes, misluktRes, zonderLayoutRes] = await Promise.all([
|
|
58
|
+
db().from("print_jobs").select(SELECT).order("created_at", { ascending: false }).limit(limit),
|
|
59
|
+
db()
|
|
60
|
+
.from("print_jobs")
|
|
61
|
+
.select(SELECT)
|
|
62
|
+
.in("status", ["wachtend", "bezig"])
|
|
63
|
+
.order("created_at", { ascending: true })
|
|
64
|
+
.limit(limit),
|
|
65
|
+
db().from("print_jobs").select("id", { count: "exact", head: true }).eq("status", "wachtend"),
|
|
66
|
+
db().from("print_jobs").select("id", { count: "exact", head: true }).eq("status", "bezig"),
|
|
67
|
+
db().from("print_jobs").select("id", { count: "exact", head: true }).eq("status", "mislukt"),
|
|
68
|
+
db()
|
|
69
|
+
.from("print_jobs")
|
|
70
|
+
.select("id", { count: "exact", head: true })
|
|
71
|
+
.eq("status", "wachtend")
|
|
72
|
+
.is("layout_id", null)
|
|
73
|
+
.lt("created_at", stuckBefore),
|
|
74
|
+
]);
|
|
75
|
+
const rows = [
|
|
76
|
+
...(Array.isArray(recentRes?.data) ? recentRes.data : []),
|
|
77
|
+
...(Array.isArray(oudsteRes?.data) ? oudsteRes.data : []),
|
|
78
|
+
];
|
|
79
|
+
const uniek = new Map();
|
|
80
|
+
for (const r of rows)
|
|
81
|
+
uniek.set(String(r.id), r);
|
|
82
|
+
const alle = Array.from(uniek.values());
|
|
83
|
+
const plekIds = Array.from(new Set(alle
|
|
29
84
|
.map((r) => r?.payload?.print_plek_id)
|
|
30
85
|
.filter((v) => typeof v === "string" && v.length > 0)));
|
|
31
86
|
const plekNamen = new Map();
|
|
@@ -38,21 +93,35 @@ export function usePrintjobs(limit = 25) {
|
|
|
38
93
|
plekNamen.set(String(p.id), String(p.naam ?? ""));
|
|
39
94
|
}
|
|
40
95
|
}
|
|
41
|
-
setJobs(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
: null,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
96
|
+
setJobs(alle.map((r) => {
|
|
97
|
+
const createdAt = String(r.created_at ?? "");
|
|
98
|
+
const ageMinutes = minutesSince(createdAt);
|
|
99
|
+
const status = String(r.status ?? "");
|
|
100
|
+
return {
|
|
101
|
+
id: String(r.id),
|
|
102
|
+
documentnaam: r.documentnaam ?? null,
|
|
103
|
+
documenttype: r.documenttype ?? null,
|
|
104
|
+
status,
|
|
105
|
+
foutmelding: r.foutmelding ?? null,
|
|
106
|
+
pogingen: Number(r.pogingen ?? 0),
|
|
107
|
+
printerNaam: r.printers?.naam ?? null,
|
|
108
|
+
layoutId: r.layout_id ?? null,
|
|
109
|
+
layoutNaam: r.document_layouts?.naam ?? null,
|
|
110
|
+
printplekNaam: typeof r?.payload?.print_plek_id === "string"
|
|
111
|
+
? (plekNamen.get(r.payload.print_plek_id) ?? null)
|
|
112
|
+
: null,
|
|
113
|
+
externJobId: r.extern_job_id ?? null,
|
|
114
|
+
createdAt,
|
|
115
|
+
ageMinutes,
|
|
116
|
+
stuck: status === "wachtend" && !r.layout_id && ageMinutes >= PRINTJOB_STUCK_MINUTES,
|
|
117
|
+
};
|
|
118
|
+
}));
|
|
119
|
+
setTotals({
|
|
120
|
+
wachtend: Number(wachtendRes?.count ?? 0),
|
|
121
|
+
bezig: Number(bezigRes?.count ?? 0),
|
|
122
|
+
mislukt: Number(misluktRes?.count ?? 0),
|
|
123
|
+
wachtendZonderLayout: Number(zonderLayoutRes?.count ?? 0),
|
|
124
|
+
});
|
|
56
125
|
}
|
|
57
126
|
finally {
|
|
58
127
|
setLoading(false);
|
|
@@ -61,6 +130,11 @@ export function usePrintjobs(limit = 25) {
|
|
|
61
130
|
useEffect(() => {
|
|
62
131
|
void reload();
|
|
63
132
|
}, [reload]);
|
|
133
|
+
// Leeftijden lopen door zonder dat er iets in de database verandert.
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
const timer = setInterval(() => void reload(), 60000);
|
|
136
|
+
return () => clearInterval(timer);
|
|
137
|
+
}, [reload]);
|
|
64
138
|
useEffect(() => {
|
|
65
139
|
const channel = supabase
|
|
66
140
|
.channel("logistiek-engine-printjobs")
|
|
@@ -72,5 +146,5 @@ export function usePrintjobs(limit = 25) {
|
|
|
72
146
|
supabase.removeChannel(channel);
|
|
73
147
|
};
|
|
74
148
|
}, [reload]);
|
|
75
|
-
return { jobs, loading, reload };
|
|
149
|
+
return { jobs, totals, loading, reload };
|
|
76
150
|
}
|
|
@@ -10736,6 +10736,24 @@ export declare const requireSupabaseAuth: import("@tanstack/start-client-core").
|
|
|
10736
10736
|
};
|
|
10737
10737
|
Relationships: [];
|
|
10738
10738
|
};
|
|
10739
|
+
maps_job_lease: {
|
|
10740
|
+
Row: {
|
|
10741
|
+
job: string;
|
|
10742
|
+
locked_until: string;
|
|
10743
|
+
updated_at: string;
|
|
10744
|
+
};
|
|
10745
|
+
Insert: {
|
|
10746
|
+
job: string;
|
|
10747
|
+
locked_until?: string;
|
|
10748
|
+
updated_at?: string;
|
|
10749
|
+
};
|
|
10750
|
+
Update: {
|
|
10751
|
+
job?: string;
|
|
10752
|
+
locked_until?: string;
|
|
10753
|
+
updated_at?: string;
|
|
10754
|
+
};
|
|
10755
|
+
Relationships: [];
|
|
10756
|
+
};
|
|
10739
10757
|
maps_request_queue: {
|
|
10740
10758
|
Row: {
|
|
10741
10759
|
attempts: number;
|
|
@@ -10797,6 +10815,7 @@ export declare const requireSupabaseAuth: import("@tanstack/start-client-core").
|
|
|
10797
10815
|
last_error: string | null;
|
|
10798
10816
|
last_error_at: string | null;
|
|
10799
10817
|
last_success_at: string | null;
|
|
10818
|
+
provider: string;
|
|
10800
10819
|
updated_at: string;
|
|
10801
10820
|
};
|
|
10802
10821
|
Insert: {
|
|
@@ -10808,6 +10827,7 @@ export declare const requireSupabaseAuth: import("@tanstack/start-client-core").
|
|
|
10808
10827
|
last_error?: string | null;
|
|
10809
10828
|
last_error_at?: string | null;
|
|
10810
10829
|
last_success_at?: string | null;
|
|
10830
|
+
provider?: string;
|
|
10811
10831
|
updated_at?: string;
|
|
10812
10832
|
};
|
|
10813
10833
|
Update: {
|
|
@@ -10819,6 +10839,7 @@ export declare const requireSupabaseAuth: import("@tanstack/start-client-core").
|
|
|
10819
10839
|
last_error?: string | null;
|
|
10820
10840
|
last_error_at?: string | null;
|
|
10821
10841
|
last_success_at?: string | null;
|
|
10842
|
+
provider?: string;
|
|
10822
10843
|
updated_at?: string;
|
|
10823
10844
|
};
|
|
10824
10845
|
Relationships: [];
|
|
@@ -13597,6 +13618,8 @@ export declare const requireSupabaseAuth: import("@tanstack/start-client-core").
|
|
|
13597
13618
|
foutmelding: string | null;
|
|
13598
13619
|
gebruiker_id: string | null;
|
|
13599
13620
|
gebruiker_naam: string;
|
|
13621
|
+
gebruikte_bron: string | null;
|
|
13622
|
+
gebruikte_layout_id: string | null;
|
|
13600
13623
|
gestart_op: string | null;
|
|
13601
13624
|
herkomst: string | null;
|
|
13602
13625
|
id: string;
|
|
@@ -13627,6 +13650,8 @@ export declare const requireSupabaseAuth: import("@tanstack/start-client-core").
|
|
|
13627
13650
|
foutmelding?: string | null;
|
|
13628
13651
|
gebruiker_id?: string | null;
|
|
13629
13652
|
gebruiker_naam?: string;
|
|
13653
|
+
gebruikte_bron?: string | null;
|
|
13654
|
+
gebruikte_layout_id?: string | null;
|
|
13630
13655
|
gestart_op?: string | null;
|
|
13631
13656
|
herkomst?: string | null;
|
|
13632
13657
|
id?: string;
|
|
@@ -13657,6 +13682,8 @@ export declare const requireSupabaseAuth: import("@tanstack/start-client-core").
|
|
|
13657
13682
|
foutmelding?: string | null;
|
|
13658
13683
|
gebruiker_id?: string | null;
|
|
13659
13684
|
gebruiker_naam?: string;
|
|
13685
|
+
gebruikte_bron?: string | null;
|
|
13686
|
+
gebruikte_layout_id?: string | null;
|
|
13660
13687
|
gestart_op?: string | null;
|
|
13661
13688
|
herkomst?: string | null;
|
|
13662
13689
|
id?: string;
|
|
@@ -20326,6 +20353,8 @@ export declare const requireSupabaseAuth: import("@tanstack/start-client-core").
|
|
|
20326
20353
|
foutmelding: string | null;
|
|
20327
20354
|
gebruiker_id: string | null;
|
|
20328
20355
|
gebruiker_naam: string;
|
|
20356
|
+
gebruikte_bron: string | null;
|
|
20357
|
+
gebruikte_layout_id: string | null;
|
|
20329
20358
|
gestart_op: string | null;
|
|
20330
20359
|
herkomst: string | null;
|
|
20331
20360
|
id: string;
|
|
@@ -20447,6 +20476,8 @@ export declare const requireSupabaseAuth: import("@tanstack/start-client-core").
|
|
|
20447
20476
|
foutmelding: string | null;
|
|
20448
20477
|
gebruiker_id: string | null;
|
|
20449
20478
|
gebruiker_naam: string;
|
|
20479
|
+
gebruikte_bron: string | null;
|
|
20480
|
+
gebruikte_layout_id: string | null;
|
|
20450
20481
|
gestart_op: string | null;
|
|
20451
20482
|
herkomst: string | null;
|
|
20452
20483
|
id: string;
|
|
@@ -21503,6 +21534,12 @@ export declare const requireSupabaseAuth: import("@tanstack/start-client-core").
|
|
|
21503
21534
|
};
|
|
21504
21535
|
Returns: string;
|
|
21505
21536
|
};
|
|
21537
|
+
logistics_document_type_map: {
|
|
21538
|
+
Args: {
|
|
21539
|
+
_document_code: string;
|
|
21540
|
+
};
|
|
21541
|
+
Returns: Database["public"]["Enums"]["doc_layout_type"][];
|
|
21542
|
+
};
|
|
21506
21543
|
logistics_emit_event: {
|
|
21507
21544
|
Args: {
|
|
21508
21545
|
p_event_type: string;
|