@flowselections/pick-schermen-kwekers 1.0.10 → 1.0.12
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/PickPublicPage.js +1 -1
- package/dist-lib/components/PickstationsPage.d.ts.map +1 -1
- package/dist-lib/components/PickstationsPage.js +23 -8
- package/dist-lib/components/TemplateModulePage.d.ts +2 -0
- package/dist-lib/components/TemplateModulePage.d.ts.map +1 -0
- package/dist-lib/components/TemplateModulePage.js +44 -0
- package/dist-lib/components/pick/LanguageTiles.d.ts.map +1 -1
- package/dist-lib/components/pick/LanguageTiles.js +3 -3
- package/dist-lib/components/pick/PickScherm.d.ts.map +1 -1
- package/dist-lib/components/pick/PickScherm.js +73 -11
- package/dist-lib/components/pick/QueueTable.d.ts.map +1 -1
- package/dist-lib/components/pick/QueueTable.js +22 -12
- package/dist-lib/hooks/usePickConfig.d.ts +6 -0
- package/dist-lib/hooks/usePickConfig.d.ts.map +1 -1
- package/dist-lib/hooks/usePickConfig.js +19 -0
- package/dist-lib/hooks/usePickQueue.d.ts +4 -0
- package/dist-lib/hooks/usePickQueue.d.ts.map +1 -1
- package/dist-lib/hooks/usePickQueue.js +38 -7
- package/dist-lib/hooks/usePrintJob.d.ts.map +1 -1
- package/dist-lib/hooks/usePrintJob.js +13 -2
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +2084 -310
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +2084 -310
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.js +2 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +2084 -310
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/previewAuthStorage.d.ts +6 -0
- package/dist-lib/integrations/supabase/previewAuthStorage.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/previewAuthStorage.js +92 -0
- package/dist-lib/integrations/supabase/types.d.ts +2168 -351
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +3 -0
- package/dist-lib/lib/layoutMaker/pakbonHtml.js +49 -49
- package/dist-lib/lib/layoutRenderer.js +17 -17
- package/dist-lib/lib/pickApi.d.ts +20 -5
- package/dist-lib/lib/pickApi.d.ts.map +1 -1
- package/dist-lib/lib/pickApi.js +76 -17
- package/dist-lib/lib/pickTypes.d.ts +15 -0
- package/dist-lib/lib/pickTypes.d.ts.map +1 -1
- package/dist-lib/lib/pickUiText.d.ts +29 -0
- package/dist-lib/lib/pickUiText.d.ts.map +1 -0
- package/dist-lib/lib/pickUiText.js +71 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
# pick schermen kwekers
|
|
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/f7462658-1d60-42f1-8e22-dbefe36f44ef).
|
|
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
|
+
# pick schermen kwekers
|
|
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/f7462658-1d60-42f1-8e22-dbefe36f44ef).
|
|
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
|
+
```
|
|
@@ -15,5 +15,5 @@ export function PickPublicPage({ slug }) {
|
|
|
15
15
|
if (!station) {
|
|
16
16
|
return (_jsx("div", { className: "flex min-h-screen items-center justify-center bg-background", children: _jsx("p", { className: "text-muted-foreground", children: "Pickstation niet gevonden of niet actief." }) }));
|
|
17
17
|
}
|
|
18
|
-
return (_jsx("div", { className: "min-h-screen bg-background p-6", children: _jsx(PickScherm, { station: station }) }));
|
|
18
|
+
return (_jsx("div", { className: "min-h-screen bg-background p-3 sm:p-6", children: _jsx(PickScherm, { station: station }) }));
|
|
19
19
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PickstationsPage.d.ts","sourceRoot":"","sources":["../../src/components/PickstationsPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PickstationsPage.d.ts","sourceRoot":"","sources":["../../src/components/PickstationsPage.tsx"],"names":[],"mappings":"AAwDA,wBAAgB,gBAAgB,gCA2O/B"}
|
|
@@ -1,41 +1,55 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from "react";
|
|
3
3
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
4
|
-
import { Button, Card, CardContent, Dialog, DialogContent, DialogHeader, DialogTitle, Input, Label, Switch, Textarea, toast, } from "@flowselections/core";
|
|
5
|
-
import { Copy, ExternalLink, Loader2, Pencil, Plus, Trash2 } from "lucide-react";
|
|
4
|
+
import { Button, Card, CardContent, Dialog, DialogContent, DialogHeader, DialogTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Switch, Textarea, toast, } from "@flowselections/core";
|
|
5
|
+
import { AlertTriangle, Copy, ExternalLink, Loader2, Pencil, Plus, Printer, Trash2, } from "lucide-react";
|
|
6
6
|
import { pickApi } from "../lib/pickApi";
|
|
7
|
-
import { usePickStations } from "../hooks/usePickConfig";
|
|
8
|
-
const
|
|
7
|
+
import { usePickPrintPlekken, usePickStations, useStationPrintPlekken, } from "../hooks/usePickConfig";
|
|
8
|
+
const NO_PLEK = "__geen__";
|
|
9
|
+
const emptyDraft = { name: "", description: "", active: true, printPlekId: null };
|
|
9
10
|
function publicUrl(slug) {
|
|
10
11
|
const origin = typeof window !== "undefined" ? window.location.origin : "";
|
|
11
12
|
return `${origin}/pickstation/${slug ?? ""}`;
|
|
12
13
|
}
|
|
13
14
|
export function PickstationsPage() {
|
|
14
15
|
const { data: stations, isLoading } = usePickStations();
|
|
16
|
+
const { data: printPlekken } = usePickPrintPlekken();
|
|
17
|
+
const { data: koppelingen } = useStationPrintPlekken();
|
|
15
18
|
const queryClient = useQueryClient();
|
|
16
19
|
const [draft, setDraft] = useState(null);
|
|
17
20
|
const safeStations = Array.isArray(stations) ? stations : [];
|
|
21
|
+
const safePlekken = Array.isArray(printPlekken) ? printPlekken : [];
|
|
22
|
+
const safeKoppelingen = Array.isArray(koppelingen) ? koppelingen : [];
|
|
23
|
+
const plekOf = (stationId) => safeKoppelingen.find((k) => k.station_id === stationId) ?? null;
|
|
18
24
|
const save = useMutation({
|
|
19
25
|
mutationFn: async (d) => {
|
|
20
|
-
await pickApi.upsert("pick_stations", {
|
|
26
|
+
const row = await pickApi.upsert("pick_stations", {
|
|
21
27
|
...(d.id ? { id: d.id } : {}),
|
|
22
28
|
name: d.name.trim(),
|
|
23
29
|
description: d.description.trim() || null,
|
|
24
30
|
active: d.active,
|
|
25
31
|
});
|
|
32
|
+
const stationId = d.id ?? row?.["id"] ?? null;
|
|
33
|
+
if (stationId)
|
|
34
|
+
await pickApi.setStationPrintPlek(stationId, d.printPlekId);
|
|
26
35
|
},
|
|
27
36
|
onSuccess: () => {
|
|
28
37
|
toast.success("Pickstation opgeslagen.");
|
|
29
38
|
setDraft(null);
|
|
30
39
|
queryClient.invalidateQueries({ queryKey: ["pick", "stations"] });
|
|
40
|
+
queryClient.invalidateQueries({ queryKey: ["pick", "stationPrintPlekken"] });
|
|
31
41
|
},
|
|
32
42
|
onError: (e) => toast.error(e instanceof Error ? e.message : "Opslaan mislukt."),
|
|
33
43
|
});
|
|
34
44
|
const remove = useMutation({
|
|
35
|
-
mutationFn: (id) =>
|
|
45
|
+
mutationFn: async (id) => {
|
|
46
|
+
await pickApi.setStationPrintPlek(id, null);
|
|
47
|
+
await pickApi.remove("pick_stations", id);
|
|
48
|
+
},
|
|
36
49
|
onSuccess: () => {
|
|
37
50
|
toast.success("Pickstation verwijderd.");
|
|
38
51
|
queryClient.invalidateQueries({ queryKey: ["pick", "stations"] });
|
|
52
|
+
queryClient.invalidateQueries({ queryKey: ["pick", "stationPrintPlekken"] });
|
|
39
53
|
},
|
|
40
54
|
onError: (e) => toast.error(e instanceof Error ? e.message : "Verwijderen mislukt."),
|
|
41
55
|
});
|
|
@@ -52,10 +66,11 @@ export function PickstationsPage() {
|
|
|
52
66
|
return (_jsxs("main", { className: "space-y-6 p-6", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-2xl font-bold text-foreground", children: "Pickstations" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Beheer je pickstations en hun openbare link. Alle logica komt uit de Logistiek Engine." })] }), _jsxs(Button, { onClick: () => setDraft({ ...emptyDraft }), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Nieuw pickstation"] })] }), isLoading ? (_jsxs("div", { className: "flex items-center gap-2 text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), " Laden..."] })) : safeStations.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen pickstations." })) : (_jsx("div", { className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-3", children: safeStations.map((station) => (_jsx(Card, { className: "border-2", children: _jsxs(CardContent, { className: "space-y-4 p-6", children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-lg font-semibold text-foreground", children: station.name }), _jsx("p", { className: "text-sm text-muted-foreground", children: station.description })] }), _jsx("span", { className: "rounded-full px-2 py-1 text-xs font-medium " +
|
|
53
67
|
(station.active
|
|
54
68
|
? "bg-primary/10 text-primary"
|
|
55
|
-
: "bg-muted text-muted-foreground"), children: station.active ? "Actief" : "Inactief" })] }), _jsx("code", { className: "block truncate rounded bg-muted px-2 py-1 text-xs text-muted-foreground", children: publicUrl(station.slug) }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx(Button, { className: "flex-1", asChild: true, children: _jsxs("a", { href: publicUrl(station.slug), target: "_blank", rel: "noreferrer", children: [_jsx(ExternalLink, { className: "mr-2 h-4 w-4" }), " Open pickstation"] }) }), _jsxs(Button, { variant: "outline", onClick: () => copyLink(station), children: [_jsx(Copy, { className: "mr-2 h-4 w-4" }), " Link kopi\u00EBren"] })] }), _jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { variant: "ghost", size: "sm", onClick: () => setDraft({
|
|
69
|
+
: "bg-muted text-muted-foreground"), children: station.active ? "Actief" : "Inactief" })] }), _jsx("code", { className: "block truncate rounded bg-muted px-2 py-1 text-xs text-muted-foreground", children: publicUrl(station.slug) }), plekOf(station.id) ? (_jsxs("p", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(Printer, { className: "h-4 w-4 shrink-0" }), _jsxs("span", { className: "truncate", children: ["Printplek: ", plekOf(station.id)?.naam] })] })) : (_jsxs("p", { className: "flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/5 px-2 py-1.5 text-sm text-destructive", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), "Nog geen printplek gekoppeld \u2014 de opdracht blijft in de wachtrij staan."] })), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx(Button, { className: "flex-1", asChild: true, children: _jsxs("a", { href: publicUrl(station.slug), target: "_blank", rel: "noreferrer", children: [_jsx(ExternalLink, { className: "mr-2 h-4 w-4" }), " Open pickstation"] }) }), _jsxs(Button, { variant: "outline", onClick: () => copyLink(station), children: [_jsx(Copy, { className: "mr-2 h-4 w-4" }), " Link kopi\u00EBren"] })] }), _jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { variant: "ghost", size: "sm", onClick: () => setDraft({
|
|
56
70
|
id: station.id,
|
|
57
71
|
name: station.name,
|
|
58
72
|
description: station.description ?? "",
|
|
59
73
|
active: station.active,
|
|
60
|
-
|
|
74
|
+
printPlekId: plekOf(station.id)?.print_plek_id ?? null,
|
|
75
|
+
}), children: [_jsx(Pencil, { className: "mr-2 h-4 w-4" }), " Wijzigen"] }), _jsxs(Button, { variant: "ghost", size: "sm", className: "text-destructive", onClick: () => remove.mutate(station.id), children: [_jsx(Trash2, { className: "mr-2 h-4 w-4" }), " Verwijderen"] })] })] }) }, station.id))) })), _jsx(Dialog, { open: draft !== null, onOpenChange: (open) => !open && setDraft(null), children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: draft?.id ? "Pickstation wijzigen" : "Nieuw pickstation" }) }), draft && (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "station-name", children: "Naam" }), _jsx(Input, { id: "station-name", value: draft.name, onChange: (e) => setDraft({ ...draft, name: e.target.value }) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "station-desc", children: "Omschrijving (optioneel)" }), _jsx(Textarea, { id: "station-desc", value: draft.description, onChange: (e) => setDraft({ ...draft, description: e.target.value }) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "station-plek", children: "Printplek" }), _jsxs(Select, { value: draft.printPlekId ?? NO_PLEK, onValueChange: (v) => setDraft({ ...draft, printPlekId: v === NO_PLEK ? null : v }), children: [_jsx(SelectTrigger, { id: "station-plek", children: _jsx(SelectValue, { placeholder: "Kies een printplek" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: NO_PLEK, children: "Geen printplek" }), safePlekken.map((plek) => (_jsxs(SelectItem, { value: plek.id, children: [plek.naam, plek.omschrijving ? ` — ${plek.omschrijving}` : ""] }, plek.id)))] })] }), !draft.printPlekId && (_jsx("p", { className: "text-sm text-destructive", children: "Nog geen printplek gekoppeld \u2014 de opdracht blijft in de wachtrij staan." }))] }), _jsxs("div", { className: "flex items-center justify-between rounded-md border px-3 py-2", children: [_jsx(Label, { htmlFor: "station-active", children: "Actief" }), _jsx(Switch, { id: "station-active", checked: draft.active, onCheckedChange: (v) => setDraft({ ...draft, active: v }) })] }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(Button, { variant: "outline", onClick: () => setDraft(null), children: "Annuleren" }), _jsxs(Button, { disabled: !draft.name.trim() || save.isPending, onClick: () => save.mutate(draft), children: [save.isPending && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), " Opslaan"] })] })] }))] }) })] }));
|
|
61
76
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TemplateModulePage.d.ts","sourceRoot":"","sources":["../../src/components/TemplateModulePage.tsx"],"names":[],"mappings":"AAsBA,wBAAgB,kBAAkB,gCAmFjC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// src/components/TemplateModulePage.tsx — EXAMPLE PAGE COMPONENT
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// Pagina-componenten horen in src/components/, NIET alleen in src/routes/.
|
|
6
|
+
// src/routes/ wordt NIET naar npm gepubliceerd (zie tsconfig.build.json exclude).
|
|
7
|
+
//
|
|
8
|
+
// GEEN <Header /> hier — die zit in _authenticated.tsx via staticData.title
|
|
9
|
+
// ============================================================================
|
|
10
|
+
import { useState, useEffect } from "react";
|
|
11
|
+
import { useAuth, supabase, Button, Card, CardContent, CardHeader, CardTitle, toast, } from "@flowselections/core";
|
|
12
|
+
import { TemplateModuleLogo } from './TemplateModuleLogo';
|
|
13
|
+
export function TemplateModulePage() {
|
|
14
|
+
// Auth is guaranteed — this user is already logged in
|
|
15
|
+
const { user, isAdmin } = useAuth();
|
|
16
|
+
// Example state — replace with your own
|
|
17
|
+
const [items, setItems] = useState([]);
|
|
18
|
+
const [loading, setLoading] = useState(true);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
loadData();
|
|
21
|
+
}, []);
|
|
22
|
+
const loadData = async () => {
|
|
23
|
+
setLoading(true);
|
|
24
|
+
try {
|
|
25
|
+
// Example query — replace "products" with your table
|
|
26
|
+
const { data, error } = await supabase
|
|
27
|
+
.from("products")
|
|
28
|
+
.select("*")
|
|
29
|
+
.limit(10);
|
|
30
|
+
if (error)
|
|
31
|
+
throw error;
|
|
32
|
+
// ✅ Altijd Array.isArray bewaken op server-function data
|
|
33
|
+
// const safeItems = Array.isArray(data?.items) ? data.items : [];
|
|
34
|
+
setItems(Array.isArray(data) ? data : []);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
toast.error("Fout bij het laden van gegevens");
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
setLoading(false);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
return (_jsxs("main", { className: "p-6 space-y-6", children: [isAdmin && (_jsxs("div", { className: "text-sm text-muted-foreground", children: ["Ingelogd als admin: ", user?.email] })), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: "Overzicht" }) }), _jsxs(CardContent, { children: [loading ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Laden..." })) : items.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Geen gegevens gevonden." })) : (_jsxs("p", { className: "text-sm", children: [items.length, " items geladen."] })), _jsx(Button, { onClick: loadData, className: "mt-4", disabled: loading, children: "Vernieuwen" })] })] }), _jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(TemplateModuleLogo, { className: "w-6 h-6 text-primary" }), _jsx("span", { children: "SVG-component (voorkeur voor vector-logo's)" })] })] }));
|
|
44
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LanguageTiles.d.ts","sourceRoot":"","sources":["../../../src/components/pick/LanguageTiles.tsx"],"names":[],"mappings":"AAGA,UAAU,KAAK;IACb,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,KAAK,+
|
|
1
|
+
{"version":3,"file":"LanguageTiles.d.ts","sourceRoot":"","sources":["../../../src/components/pick/LanguageTiles.tsx"],"names":[],"mappings":"AAGA,UAAU,KAAK;IACb,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,KAAK,+BAyB1D"}
|
|
@@ -3,9 +3,9 @@ import { Card } from "@flowselections/core";
|
|
|
3
3
|
import { PICK_LANGUAGES } from "../../lib/pickI18n";
|
|
4
4
|
/** Taalkeuze — zelfde tegel-uitstraling en bediening als de oude landselectie. */
|
|
5
5
|
export function LanguageTiles({ onSelect, selected }) {
|
|
6
|
-
return (_jsx("div", { className: "grid grid-cols-2 gap-
|
|
6
|
+
return (_jsx("div", { className: "grid grid-cols-2 gap-3 sm:grid-cols-3 sm:gap-4 lg:grid-cols-4 xl:grid-cols-5", children: PICK_LANGUAGES.map((l) => (_jsxs(Card, { role: "button", tabIndex: 0, onClick: () => onSelect(l.code), onKeyDown: (e) => {
|
|
7
7
|
if (e.key === "Enter" || e.key === " ")
|
|
8
8
|
onSelect(l.code);
|
|
9
|
-
}, className: "cursor-pointer select-none border-2 p-
|
|
10
|
-
(selected === l.code ? "border-primary bg-primary/5" : ""), children: [_jsx("div", { className: "text-
|
|
9
|
+
}, className: "flex min-h-22 cursor-pointer select-none flex-col items-center justify-center border-2 p-4 text-center transition-all hover:border-primary hover:shadow-lg focus:border-primary focus:outline-none active:scale-[0.98] sm:p-6 " +
|
|
10
|
+
(selected === l.code ? "border-primary bg-primary/5" : ""), children: [_jsx("div", { className: "text-4xl leading-none sm:text-5xl", children: l.flag }), _jsx("div", { className: "mt-2 text-base font-semibold text-foreground sm:mt-3 sm:text-lg", children: l.label })] }, l.code))) }));
|
|
11
11
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PickScherm.d.ts","sourceRoot":"","sources":["../../../src/components/pick/PickScherm.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PickScherm.d.ts","sourceRoot":"","sources":["../../../src/components/pick/PickScherm.tsx"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAiB,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAkBtE,UAAU,KAAK;IACb,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAID;;;GAGG;AACH,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,KAAK,+BAgmBpD"}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
-
import { Badge, Button, Card, CardContent, Input, toast, } from "@flowselections/core";
|
|
4
|
-
import { AlertTriangle, ArrowLeft, CheckCircle2, Globe, Loader2, Printer, RefreshCw, Search, WifiOff, } from "lucide-react";
|
|
3
|
+
import { Badge, Button, Card, CardContent, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, toast, } from "@flowselections/core";
|
|
4
|
+
import { AlertTriangle, ArrowLeft, CheckCircle2, Globe, Loader2, Printer, RefreshCw, ScanLine, Search, WifiOff, Zap, } from "lucide-react";
|
|
5
5
|
import { LanguageTiles } from "./LanguageTiles";
|
|
6
6
|
import { QueueTable } from "./QueueTable";
|
|
7
7
|
import { getSessionId, pickApi } from "../../lib/pickApi";
|
|
8
8
|
import { completeLogisticsTaskForItem } from "../../lib/pickPrint";
|
|
9
9
|
import { getPickTranslator } from "../../lib/pickI18n";
|
|
10
|
+
import { friendlyPickError, getPickUiTexts } from "../../lib/pickUiText";
|
|
10
11
|
import { getPrintJobTexts, printJobStatusLabel } from "../../lib/pickPrintJobText";
|
|
11
12
|
import { defaultFilters, usePickHeartbeat, usePickQueue, } from "../../hooks/usePickQueue";
|
|
12
|
-
import { usePickDocumentTypes, usePickStatuses } from "../../hooks/usePickConfig";
|
|
13
|
+
import { usePickDocumentTypes, usePickStatuses, useStationPrintPlek, } from "../../hooks/usePickConfig";
|
|
13
14
|
import { usePrintJob } from "../../hooks/usePrintJob";
|
|
14
15
|
const LANG_STORAGE_KEY = "pick.language";
|
|
15
16
|
/**
|
|
@@ -20,18 +21,23 @@ export function PickScherm({ station, onBack }) {
|
|
|
20
21
|
const sessionId = getSessionId();
|
|
21
22
|
const [lang, setLang] = useState(null);
|
|
22
23
|
const [filters, setFilters] = useState(defaultFilters);
|
|
24
|
+
const [searchInput, setSearchInput] = useState("");
|
|
25
|
+
const [scanInput, setScanInput] = useState("");
|
|
23
26
|
const [busyId, setBusyId] = useState(null);
|
|
24
27
|
const [pending, setPending] = useState(null);
|
|
25
28
|
const [lastPrinted, setLastPrinted] = useState(null);
|
|
26
29
|
const [emptyNotice, setEmptyNotice] = useState(null);
|
|
27
30
|
const resetDone = useRef(null);
|
|
28
31
|
const autoFetched = useRef(false);
|
|
32
|
+
const retryAfterExpiry = useRef(false);
|
|
29
33
|
const { data: statuses } = usePickStatuses();
|
|
30
34
|
const { data: documentTypes } = usePickDocumentTypes();
|
|
31
35
|
const { online } = usePickHeartbeat(station.id, null, sessionId);
|
|
36
|
+
const { data: printPlek, isLoading: plekLoading } = useStationPrintPlek(station.id);
|
|
32
37
|
const { tracked, track, clear: clearJob } = usePrintJob();
|
|
33
38
|
const t = useMemo(() => getPickTranslator(lang ?? "nl"), [lang]);
|
|
34
39
|
const jobTexts = useMemo(() => getPrintJobTexts(t.lang), [t]);
|
|
40
|
+
const ui = useMemo(() => getPickUiTexts(t.lang), [t]);
|
|
35
41
|
useEffect(() => {
|
|
36
42
|
if (typeof window === "undefined")
|
|
37
43
|
return;
|
|
@@ -44,8 +50,15 @@ export function PickScherm({ station, onBack }) {
|
|
|
44
50
|
if (typeof window !== "undefined")
|
|
45
51
|
window.localStorage.setItem(LANG_STORAGE_KEY, code);
|
|
46
52
|
}, []);
|
|
53
|
+
/** Zoeken pas na een korte pauze, zodat typen geen query per toets afvuurt. */
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
const timer = setTimeout(() => {
|
|
56
|
+
setFilters((f) => (f.search === searchInput ? f : { ...f, search: searchInput, page: 0 }));
|
|
57
|
+
}, 300);
|
|
58
|
+
return () => clearTimeout(timer);
|
|
59
|
+
}, [searchInput]);
|
|
47
60
|
const effectiveFilters = useMemo(() => ({ ...filters, country: null, sortRules: [] }), [filters]);
|
|
48
|
-
const { items, total, loading, lockedIds, refresh, pendingCount, printedCount } = usePickQueue({
|
|
61
|
+
const { items, total, loading, lockedIds, refresh, pendingCount, printedCount, urgentCount, realtimeOk, } = usePickQueue({
|
|
49
62
|
stationId: station.id,
|
|
50
63
|
filters: effectiveFilters,
|
|
51
64
|
enabled: lang !== null,
|
|
@@ -89,9 +102,14 @@ export function PickScherm({ station, onBack }) {
|
|
|
89
102
|
}
|
|
90
103
|
})();
|
|
91
104
|
}, [tracked, sessionId, refresh, jobTexts]);
|
|
92
|
-
/**
|
|
105
|
+
/**
|
|
106
|
+
* Zet een printfout om in een begrijpelijke melding en meldt of de claim
|
|
107
|
+
* verlopen was — dan kan het scherm zelf opnieuw ophalen.
|
|
108
|
+
*/
|
|
93
109
|
const showPrintError = useCallback((e) => {
|
|
94
|
-
|
|
110
|
+
const { message, detail, expiredClaim } = friendlyPickError(e, t.lang);
|
|
111
|
+
toast.error(message, detail && detail !== message ? { description: detail } : undefined);
|
|
112
|
+
return expiredClaim;
|
|
95
113
|
}, [t]);
|
|
96
114
|
const handlePick = useCallback(async (item) => {
|
|
97
115
|
setBusyId(item.id);
|
|
@@ -162,7 +180,7 @@ export function PickScherm({ station, onBack }) {
|
|
|
162
180
|
return claimed;
|
|
163
181
|
}
|
|
164
182
|
catch (e) {
|
|
165
|
-
const message = e
|
|
183
|
+
const { message } = friendlyPickError(e, t.lang);
|
|
166
184
|
if (silent)
|
|
167
185
|
setEmptyNotice(message);
|
|
168
186
|
else
|
|
@@ -189,7 +207,7 @@ export function PickScherm({ station, onBack }) {
|
|
|
189
207
|
await fetchNext();
|
|
190
208
|
}
|
|
191
209
|
catch (e) {
|
|
192
|
-
showPrintError(e);
|
|
210
|
+
const expired = showPrintError(e);
|
|
193
211
|
try {
|
|
194
212
|
await pickApi.releaseLock(item.id);
|
|
195
213
|
}
|
|
@@ -199,8 +217,47 @@ export function PickScherm({ station, onBack }) {
|
|
|
199
217
|
setPending(null);
|
|
200
218
|
refresh();
|
|
201
219
|
setBusyId(null);
|
|
220
|
+
// Verlopen claim: zelf een verse opdracht ophalen in plaats van blijven hangen.
|
|
221
|
+
if (expired && !retryAfterExpiry.current) {
|
|
222
|
+
retryAfterExpiry.current = true;
|
|
223
|
+
try {
|
|
224
|
+
await fetchNext(true);
|
|
225
|
+
}
|
|
226
|
+
finally {
|
|
227
|
+
retryAfterExpiry.current = false;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
202
230
|
}
|
|
203
231
|
}, [pending, runPrint, fetchNext, refresh, showPrintError]);
|
|
232
|
+
/** Scanner: barcode, documentnummer of ordernummer selecteert direct de regel. */
|
|
233
|
+
const handleScan = useCallback(async (raw) => {
|
|
234
|
+
const code = raw.trim();
|
|
235
|
+
if (!code)
|
|
236
|
+
return;
|
|
237
|
+
setBusyId("scan");
|
|
238
|
+
try {
|
|
239
|
+
const found = await pickApi.findByCode(code, station.id);
|
|
240
|
+
if (!found) {
|
|
241
|
+
toast.error(ui.scanNotFound);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
setScanInput("");
|
|
245
|
+
if (found.printed_at) {
|
|
246
|
+
toast.error(ui.scanAlreadyPrinted);
|
|
247
|
+
setFilters((f) => ({ ...f, search: code, page: 0 }));
|
|
248
|
+
setSearchInput(code);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
setBusyId(null);
|
|
252
|
+
await handlePick(found);
|
|
253
|
+
}
|
|
254
|
+
catch (e) {
|
|
255
|
+
showPrintError(e);
|
|
256
|
+
}
|
|
257
|
+
finally {
|
|
258
|
+
setBusyId(null);
|
|
259
|
+
}
|
|
260
|
+
}, [station.id, ui, handlePick, showPrintError]);
|
|
204
261
|
const cancelPending = useCallback(async () => {
|
|
205
262
|
const item = pending;
|
|
206
263
|
setPending(null);
|
|
@@ -234,13 +291,18 @@ export function PickScherm({ station, onBack }) {
|
|
|
234
291
|
}, [pendingCount]);
|
|
235
292
|
// Stap 1 — taalselectie
|
|
236
293
|
if (lang === null) {
|
|
237
|
-
return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "
|
|
294
|
+
return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "grid grid-cols-[minmax(0,1fr)_auto] items-center gap-3 sm:flex sm:flex-wrap sm:justify-between", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h2", { className: "truncate text-xl font-bold text-foreground sm:text-2xl", children: station.name }), _jsx("p", { className: "text-sm text-muted-foreground", children: t("chooseLanguageHint") })] }), onBack && (_jsxs(Button, { variant: "outline", className: "shrink-0", onClick: onBack, children: [_jsx(ArrowLeft, { className: "mr-2 h-4 w-4" }), " ", t("back")] }))] }), _jsx(LanguageTiles, { onSelect: chooseLang })] }));
|
|
238
295
|
}
|
|
239
296
|
const totalPages = Math.max(1, Math.ceil(total / filters.pageSize));
|
|
240
297
|
const jobStatus = tracked?.job?.status ?? null;
|
|
241
298
|
const jobFailed = jobStatus === "mislukt" || jobStatus === "geannuleerd";
|
|
242
|
-
return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between
|
|
299
|
+
return (_jsxs("div", { className: "space-y-4 pb-24 md:pb-0", children: [_jsxs("div", { className: "flex flex-col gap-3 md:flex-row md:flex-wrap md:items-center md:justify-between", children: [_jsxs("div", { className: "grid grid-cols-[auto_minmax(0,1fr)] items-center gap-3", children: [_jsxs(Button, { variant: "outline", size: "lg", className: "h-12 shrink-0 px-3 sm:px-4", onClick: () => setLang(null), "aria-label": t("changeLanguage"), children: [_jsx(Globe, { className: "h-5 w-5 sm:mr-2" }), _jsx("span", { className: "hidden sm:inline", children: t("changeLanguage") })] }), _jsxs("div", { className: "min-w-0", children: [_jsx("h2", { className: "truncate text-lg font-bold text-foreground sm:text-xl", children: station.name }), _jsxs("p", { className: "truncate text-sm text-muted-foreground", children: [pendingCount, " ", t("statusToPrint"), " \u00B7 ", printedCount, " ", t("statusPrinted")] })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [!online && (_jsxs(Badge, { className: "bg-destructive text-destructive-foreground", children: [_jsx(WifiOff, { className: "mr-1 h-3.5 w-3.5" }), " ", t("offline")] })), _jsx(Button, { variant: "outline", size: "icon", className: "h-12 w-12 shrink-0", onClick: refresh, "aria-label": t("refresh"), children: _jsx(RefreshCw, { className: "h-4 w-4" }) }), _jsxs(Button, { size: "lg", className: "hidden h-14 px-8 text-lg md:inline-flex", onClick: () => (pending ? confirmPending() : fetchNext()), disabled: busyId !== null, children: [busyId !== null ? (_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" })) : (_jsx(Printer, { className: "mr-2 h-5 w-5" })), t("printNext"), pending?.document_number ? ` · ${pending.document_number}` : ""] })] })] }), !plekLoading && !printPlek && (_jsxs("div", { className: "flex items-start gap-2 rounded-md border-2 border-destructive/50 bg-destructive/5 p-3 text-sm text-destructive", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsx("span", { children: "Nog geen printplek gekoppeld \u2014 de opdracht blijft in de wachtrij staan." })] })), !pending && emptyNotice && (_jsx("p", { className: "text-sm text-muted-foreground", children: emptyNotice })), pending && (_jsx(Card, { className: "border-2 border-primary", children: _jsxs(CardContent, { className: "flex flex-col gap-4 p-4 sm:flex-row sm:flex-wrap sm:items-center sm:justify-between sm:p-5", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-sm font-medium text-muted-foreground", children: t("confirmPrintTitle") }), _jsx("p", { className: "truncate text-xl font-bold text-foreground sm:text-2xl", children: pending.document_number ?? t("document") }), _jsx("p", { className: "truncate text-sm text-muted-foreground", children: [pending.order_number, pending.customer_name].filter(Boolean).join(" · ") })] }), _jsxs("div", { className: "flex flex-col gap-2 sm:flex-row", children: [_jsx(Button, { variant: "outline", size: "lg", className: "h-12 w-full sm:w-auto", onClick: cancelPending, disabled: busyId !== null, children: t("cancel") }), _jsxs(Button, { size: "lg", className: "h-14 w-full px-8 text-lg sm:w-auto", onClick: confirmPending, disabled: busyId !== null, children: [busyId === "confirm" ? (_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" })) : (_jsx(Printer, { className: "mr-2 h-5 w-5" })), t("print")] })] })] }) })), tracked && (_jsx(Card, { className: jobFailed ? "border-2 border-destructive" : "border-2 border-primary/40", children: _jsxs(CardContent, { className: "flex flex-col gap-4 p-4 sm:flex-row sm:flex-wrap sm:items-center sm:justify-between", children: [_jsxs("div", { className: "flex min-w-0 items-start gap-3", children: [jobFailed ? (_jsx(AlertTriangle, { className: "h-6 w-6 shrink-0 text-destructive" })) : jobStatus === "voltooid" ? (_jsx(CheckCircle2, { className: "h-6 w-6 shrink-0 text-primary" })) : (_jsx(Loader2, { className: "h-6 w-6 shrink-0 animate-spin text-muted-foreground" })), _jsxs("div", { className: "min-w-0", children: [_jsxs("p", { className: "truncate text-sm font-medium text-muted-foreground", children: [jobTexts.jobTitle, " ", tracked.documentNumber ? `· ${tracked.documentNumber}` : ""] }), _jsx("p", { className: "text-lg font-semibold text-foreground", children: printJobStatusLabel(jobStatus, t.lang) }), tracked.job?.printer_naam && (_jsxs("p", { className: "truncate text-sm text-muted-foreground", children: [jobTexts.printerLabel, ": ", tracked.job.printer_naam] })), jobFailed && (_jsxs("p", { className: "text-sm text-destructive", children: [tracked.job?.foutmelding ?? jobTexts.unknown, " \u00B7 ", jobTexts.requeued] }))] })] }), _jsxs("div", { className: "flex flex-col gap-2 sm:flex-row", children: [jobFailed && lastPrinted && (_jsxs(Button, { size: "lg", className: "h-12 w-full sm:w-auto", onClick: () => {
|
|
243
300
|
clearJob();
|
|
244
301
|
void handlePick(lastPrinted);
|
|
245
|
-
}, disabled: busyId !== null, children: [_jsx(Printer, { className: "mr-2 h-5 w-5" }), " ", jobTexts.retry] })), _jsx(Button, { variant: "outline", size: "lg", onClick: clearJob, children: jobTexts.close })] })] }) })), _jsx(Card, { children:
|
|
302
|
+
}, disabled: busyId !== null, children: [_jsx(Printer, { className: "mr-2 h-5 w-5" }), " ", jobTexts.retry] })), _jsx(Button, { variant: "outline", size: "lg", className: "h-12 w-full sm:w-auto", onClick: clearJob, children: jobTexts.close })] })] }) })), _jsx(Card, { children: _jsxs(CardContent, { className: "flex flex-wrap items-center gap-3 p-3 sm:p-4", children: [_jsxs("div", { className: "relative w-full min-w-0 flex-1 sm:min-w-64", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { className: "h-12 pl-9 text-base", placeholder: t("search"), value: searchInput, onChange: (e) => setSearchInput(e.target.value) })] }), _jsxs("div", { className: "relative w-full min-w-0 sm:w-56", children: [_jsx(ScanLine, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { className: "h-12 pl-9 text-base", placeholder: ui.scanPlaceholder, value: scanInput, onChange: (e) => setScanInput(e.target.value), onKeyDown: (e) => {
|
|
303
|
+
if (e.key !== "Enter")
|
|
304
|
+
return;
|
|
305
|
+
e.preventDefault();
|
|
306
|
+
void handleScan(scanInput);
|
|
307
|
+
} })] }), _jsxs(Select, { value: filters.documentTypeId ?? "all", onValueChange: (v) => setFilters((f) => ({ ...f, documentTypeId: v === "all" ? null : v, page: 0 })), children: [_jsx(SelectTrigger, { className: "h-12 w-full sm:w-52", children: _jsx(SelectValue, { placeholder: ui.allDocumentTypes }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: ui.allDocumentTypes }), (Array.isArray(documentTypes) ? documentTypes : []).map((dt) => (_jsx(SelectItem, { value: dt.id, children: dt.name }, dt.id)))] })] }), _jsx(Button, { variant: filters.onlyPending ? "default" : "outline", className: "h-12", onClick: () => setFilters((f) => ({ ...f, onlyPending: !f.onlyPending, page: 0 })), children: ui.onlyPending }), _jsxs(Button, { variant: filters.urgentOnly ? "default" : "outline", className: "h-12", onClick: () => setFilters((f) => ({ ...f, urgentOnly: !f.urgentOnly, page: 0 })), children: [_jsx(Zap, { className: "mr-2 h-4 w-4" }), ui.onlyUrgent, urgentCount > 0 ? ` (${urgentCount})` : ""] })] }) }), !realtimeOk && (_jsx("p", { className: "text-sm text-muted-foreground", children: ui.connectionLost })), loading ? (_jsxs("div", { className: "flex items-center justify-center py-16 text-muted-foreground", children: [_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" }), " ", t("loadingQueue")] })) : (_jsx(QueueTable, { items: items, lockedIds: lockedIds, statuses: Array.isArray(statuses) ? statuses : [], documentTypes: Array.isArray(documentTypes) ? documentTypes : [], busyId: busyId, onPick: handlePick, onReprint: handleReprint, t: t })), _jsxs("div", { className: "flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("span", { className: "text-center text-sm text-muted-foreground sm:text-left", children: [t("page"), " ", filters.page + 1, " ", t("of"), " ", totalPages] }), _jsxs("div", { className: "grid grid-cols-2 gap-2 sm:flex", children: [_jsx(Button, { variant: "outline", className: "h-12 w-full sm:w-auto", disabled: filters.page === 0, onClick: () => setFilters((f) => ({ ...f, page: Math.max(0, f.page - 1) })), children: t("previous") }), _jsx(Button, { variant: "outline", className: "h-12 w-full sm:w-auto", disabled: filters.page + 1 >= totalPages, onClick: () => setFilters((f) => ({ ...f, page: f.page + 1 })), children: t("next") })] })] }), _jsx("div", { className: "fixed inset-x-0 bottom-0 z-40 border-t bg-background/95 p-3 pb-[max(0.75rem,env(safe-area-inset-bottom))] backdrop-blur md:hidden", children: _jsxs(Button, { size: "lg", className: "h-14 w-full text-lg", onClick: () => (pending ? confirmPending() : fetchNext()), disabled: busyId !== null, children: [busyId !== null ? (_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" })) : (_jsx(Printer, { className: "mr-2 h-5 w-5" })), _jsxs("span", { className: "truncate", children: [t("printNext"), pending?.document_number ? ` · ${pending.document_number}` : ""] })] }) })] }));
|
|
246
308
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueueTable.d.ts","sourceRoot":"","sources":["../../../src/components/pick/QueueTable.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEvF,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE5E,UAAU,KAAK;IACb,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,aAAa,EAAE,gBAAgB,EAAE,CAAC;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IACtC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,CAAC,CAAC,EAAE,cAAc,CAAC;CACpB;AASD,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,SAAS,EACT,QAAQ,EACR,aAAa,EACb,MAAM,EACN,MAAM,EACN,SAAS,EACT,QAAQ,EACR,CAAC,EAAE,UAAU,GACd,EAAE,KAAK,+
|
|
1
|
+
{"version":3,"file":"QueueTable.d.ts","sourceRoot":"","sources":["../../../src/components/pick/QueueTable.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEvF,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE5E,UAAU,KAAK;IACb,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,aAAa,EAAE,gBAAgB,EAAE,CAAC;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IACtC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,CAAC,CAAC,EAAE,cAAc,CAAC;CACpB;AASD,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,SAAS,EACT,QAAQ,EACR,aAAa,EACb,MAAM,EACN,MAAM,EACN,SAAS,EACT,QAAQ,EACR,CAAC,EAAE,UAAU,GACd,EAAE,KAAK,+BA4NP"}
|
|
@@ -1,4 +1,4 @@
|
|
|
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 { Badge, Button, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@flowselections/core";
|
|
3
3
|
import { AlertTriangle, Lock, Printer, RotateCcw } from "lucide-react";
|
|
4
4
|
import { sinceLabel } from "../../lib/pickTypes";
|
|
@@ -16,15 +16,25 @@ export function QueueTable({ items, lockedIds, statuses, documentTypes, busyId,
|
|
|
16
16
|
const t = translator ?? getPickTranslator("nl");
|
|
17
17
|
const safeItems = Array.isArray(items) ? items : [];
|
|
18
18
|
const docById = new Map((Array.isArray(documentTypes) ? documentTypes : []).map((d) => [d.id, d]));
|
|
19
|
-
return (_jsx("div", { className: "overflow-x-auto rounded-lg border", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: t("document") }), _jsx(TableHead, { children: t("order") }), _jsx(TableHead, { children: t("customer") }), _jsx(TableHead, { className: "hidden lg:table-cell", children: t("deliveryAddress") }), _jsx(TableHead, { className: "text-right", children: t("lines") }), _jsx(TableHead, { className: "text-right", children: t("carts") }), _jsx(TableHead, { children: t("status") }), _jsx(TableHead, { children: t("priority") }), _jsx(TableHead, { children: t("announced") }), _jsx(TableHead, { children: t("waiting") }), _jsx(TableHead, { className: "text-right", children: t("action") })] }) }), _jsxs(TableBody, { children: [safeItems.map((item) => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "hidden overflow-x-auto rounded-lg border md:block", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: t("document") }), _jsx(TableHead, { children: t("order") }), _jsx(TableHead, { children: t("customer") }), _jsx(TableHead, { className: "hidden lg:table-cell", children: t("deliveryAddress") }), _jsx(TableHead, { className: "text-right", children: t("lines") }), _jsx(TableHead, { className: "text-right", children: t("carts") }), _jsx(TableHead, { children: t("status") }), _jsx(TableHead, { children: t("priority") }), _jsx(TableHead, { children: t("announced") }), _jsx(TableHead, { children: t("waiting") }), _jsx(TableHead, { className: "text-right", children: t("action") })] }) }), _jsxs(TableBody, { children: [safeItems.map((item) => {
|
|
20
|
+
const printed = !!item.printed_at;
|
|
21
|
+
const locked = !printed && lockedIds.has(item.id);
|
|
22
|
+
const doc = item.document_type_id ? docById.get(item.document_type_id) : undefined;
|
|
23
|
+
const warnings = Array.isArray(item.warnings) ? item.warnings : [];
|
|
24
|
+
return (_jsxs(TableRow, { className: locked || printed ? "opacity-60" : undefined, children: [_jsxs(TableCell, { className: "font-medium", children: [_jsxs("div", { className: "flex items-center gap-2", children: [item.document_number, warnings.length > 0 && (_jsx(AlertTriangle, { className: "h-4 w-4 text-destructive", "aria-label": t("warning") }))] }), _jsx("div", { className: "text-xs text-muted-foreground", children: doc?.name ?? "-" })] }), _jsx(TableCell, { children: item.order_number }), _jsx(TableCell, { children: item.customer_name }), _jsx(TableCell, { className: "hidden max-w-56 truncate lg:table-cell", children: item.delivery_address }), _jsx(TableCell, { className: "text-right", children: item.line_count }), _jsx(TableCell, { className: "text-right", children: item.cart_count }), _jsx(TableCell, { children: _jsx(Badge, { variant: printed ? "secondary" : "default", children: printed ? t("statusPrinted") : t("statusToPrint") }) }), _jsx(TableCell, { children: _jsx("span", { className: "rounded px-2 py-1 text-xs font-semibold " +
|
|
25
|
+
priorityTone(item.priority, item.is_urgent), children: item.is_urgent ? t("urgent") : item.priority }) }), _jsx(TableCell, { className: "whitespace-nowrap text-sm", children: new Date(item.announced_at).toLocaleTimeString(t.locale, {
|
|
26
|
+
hour: "2-digit",
|
|
27
|
+
minute: "2-digit",
|
|
28
|
+
}) }), _jsx(TableCell, { className: "whitespace-nowrap text-sm", children: sinceLabel(item.announced_at) }), _jsx(TableCell, { className: "text-right", children: printed ? (_jsxs(Button, { size: "sm", variant: "ghost", disabled: readOnly || busyId === item.id, onClick: () => (onReprint ?? onPick)(item), children: [_jsx(RotateCcw, { className: "mr-1 h-4 w-4" }), t("print")] })) : locked ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(Lock, { className: "h-3.5 w-3.5" }), " ", t("inProgress")] })) : (_jsxs(Button, { size: "sm", variant: "outline", disabled: readOnly || busyId === item.id, onClick: () => onPick(item), children: [_jsx(Printer, { className: "mr-1 h-4 w-4" }), t("print")] })) })] }, item.id));
|
|
29
|
+
}), safeItems.length === 0 && (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 11, className: "py-10 text-center text-muted-foreground", children: t("emptyQueue") }) }))] })] }) }), _jsxs("div", { className: "space-y-3 md:hidden", children: [safeItems.map((item) => {
|
|
30
|
+
const printed = !!item.printed_at;
|
|
31
|
+
const locked = !printed && lockedIds.has(item.id);
|
|
32
|
+
const doc = item.document_type_id ? docById.get(item.document_type_id) : undefined;
|
|
33
|
+
const warnings = Array.isArray(item.warnings) ? item.warnings : [];
|
|
34
|
+
return (_jsxs("div", { className: "rounded-lg border bg-card p-4 " + (locked || printed ? "opacity-60" : ""), children: [_jsxs("div", { className: "grid grid-cols-[minmax(0,1fr)_auto] items-start gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx("span", { className: "truncate text-lg font-bold text-foreground", children: item.document_number }), warnings.length > 0 && (_jsx(AlertTriangle, { className: "h-4 w-4 shrink-0 text-destructive", "aria-label": t("warning") }))] }), _jsx("p", { className: "truncate text-xs text-muted-foreground", children: doc?.name ?? "-" })] }), _jsxs("div", { className: "flex shrink-0 flex-col items-end gap-1", children: [_jsx(Badge, { variant: printed ? "secondary" : "default", children: printed ? t("statusPrinted") : t("statusToPrint") }), _jsx("span", { className: "rounded px-2 py-0.5 text-xs font-semibold " +
|
|
35
|
+
priorityTone(item.priority, item.is_urgent), children: item.is_urgent ? t("urgent") : item.priority })] })] }), _jsxs("div", { className: "mt-2 min-w-0 space-y-0.5 text-sm", children: [_jsx("p", { className: "truncate text-foreground", children: item.order_number }), _jsx("p", { className: "truncate text-muted-foreground", children: item.customer_name }), item.delivery_address && (_jsx("p", { className: "truncate text-xs text-muted-foreground", children: item.delivery_address }))] }), _jsxs("div", { className: "mt-2 flex flex-wrap gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [_jsxs("span", { children: [t("lines"), ": ", item.line_count] }), _jsxs("span", { children: [t("carts"), ": ", item.cart_count] }), _jsxs("span", { children: [t("announced"), ":", " ", new Date(item.announced_at).toLocaleTimeString(t.locale, {
|
|
36
|
+
hour: "2-digit",
|
|
37
|
+
minute: "2-digit",
|
|
38
|
+
})] }), _jsxs("span", { children: [t("waiting"), ": ", sinceLabel(item.announced_at)] })] }), _jsx("div", { className: "mt-3", children: printed ? (_jsxs(Button, { size: "lg", variant: "outline", className: "h-12 w-full", disabled: readOnly || busyId === item.id, onClick: () => (onReprint ?? onPick)(item), children: [_jsx(RotateCcw, { className: "mr-2 h-4 w-4" }), t("print")] })) : locked ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(Lock, { className: "h-3.5 w-3.5" }), " ", t("inProgress")] })) : (_jsxs(Button, { size: "lg", className: "h-12 w-full", disabled: readOnly || busyId === item.id, onClick: () => onPick(item), children: [_jsx(Printer, { className: "mr-2 h-4 w-4" }), t("print")] })) })] }, item.id));
|
|
39
|
+
}), safeItems.length === 0 && (_jsx("div", { className: "rounded-lg border py-10 text-center text-muted-foreground", children: t("emptyQueue") }))] })] }));
|
|
30
40
|
}
|
|
@@ -5,5 +5,11 @@ export declare function usePickDocumentTypes(): import("@tanstack/react-query").
|
|
|
5
5
|
export declare function usePickStationTypes(): import("@tanstack/react-query").UseQueryResult<NoInfer<import("..").PickStationType[]>, Error>;
|
|
6
6
|
export declare function usePickTasks(): import("@tanstack/react-query").UseQueryResult<NoInfer<import("..").PickTask[]>, Error>;
|
|
7
7
|
export declare function usePickStations(): import("@tanstack/react-query").UseQueryResult<NoInfer<import("..").PickStation[]>, Error>;
|
|
8
|
+
/** Actieve printplekken uit de Layout maker. */
|
|
9
|
+
export declare function usePickPrintPlekken(): import("@tanstack/react-query").UseQueryResult<NoInfer<import("..").PickPrintPlek[]>, Error>;
|
|
10
|
+
/** Koppelingen pickstation → printplek (beheerpagina). */
|
|
11
|
+
export declare function useStationPrintPlekken(): import("@tanstack/react-query").UseQueryResult<NoInfer<import("..").PickStationPrintPlekRow[]>, Error>;
|
|
12
|
+
/** De printplek van één pickstation (ook in openbare pickschermen). */
|
|
13
|
+
export declare function useStationPrintPlek(stationId?: string): import("@tanstack/react-query").UseQueryResult<NoInfer<import("..").PickStationPrintPlek | null>, Error>;
|
|
8
14
|
export declare function usePickScreens(stationId?: string): import("@tanstack/react-query").UseQueryResult<NoInfer<import("..").PickScreen[]>, Error>;
|
|
9
15
|
//# sourceMappingURL=usePickConfig.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePickConfig.d.ts","sourceRoot":"","sources":["../../src/hooks/usePickConfig.ts"],"names":[],"mappings":"AAGA,wBAAgB,gBAAgB,+FAE/B;AACD,wBAAgB,eAAe,8FAE9B;AACD,wBAAgB,eAAe,+FAE9B;AACD,wBAAgB,oBAAoB,oGAEnC;AACD,wBAAgB,mBAAmB,mGAElC;AACD,wBAAgB,YAAY,4FAE3B;AACD,wBAAgB,eAAe,+FAE9B;AACD,wBAAgB,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,6FAKhD"}
|
|
1
|
+
{"version":3,"file":"usePickConfig.d.ts","sourceRoot":"","sources":["../../src/hooks/usePickConfig.ts"],"names":[],"mappings":"AAGA,wBAAgB,gBAAgB,+FAE/B;AACD,wBAAgB,eAAe,8FAE9B;AACD,wBAAgB,eAAe,+FAE9B;AACD,wBAAgB,oBAAoB,oGAEnC;AACD,wBAAgB,mBAAmB,mGAElC;AACD,wBAAgB,YAAY,4FAE3B;AACD,wBAAgB,eAAe,+FAE9B;AACD,gDAAgD;AAChD,wBAAgB,mBAAmB,iGAElC;AACD,0DAA0D;AAC1D,wBAAgB,sBAAsB,2GAKrC;AACD,uEAAuE;AACvE,wBAAgB,mBAAmB,CAAC,SAAS,CAAC,EAAE,MAAM,4GAMrD;AAED,wBAAgB,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,6FAKhD"}
|
|
@@ -21,6 +21,25 @@ export function usePickTasks() {
|
|
|
21
21
|
export function usePickStations() {
|
|
22
22
|
return useQuery({ queryKey: ["pick", "stations"], queryFn: pickApi.stations });
|
|
23
23
|
}
|
|
24
|
+
/** Actieve printplekken uit de Layout maker. */
|
|
25
|
+
export function usePickPrintPlekken() {
|
|
26
|
+
return useQuery({ queryKey: ["pick", "printPlekken"], queryFn: pickApi.printPlekken });
|
|
27
|
+
}
|
|
28
|
+
/** Koppelingen pickstation → printplek (beheerpagina). */
|
|
29
|
+
export function useStationPrintPlekken() {
|
|
30
|
+
return useQuery({
|
|
31
|
+
queryKey: ["pick", "stationPrintPlekken"],
|
|
32
|
+
queryFn: pickApi.stationPrintPlekken,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/** De printplek van één pickstation (ook in openbare pickschermen). */
|
|
36
|
+
export function useStationPrintPlek(stationId) {
|
|
37
|
+
return useQuery({
|
|
38
|
+
queryKey: ["pick", "stationPrintPlek", stationId ?? "none"],
|
|
39
|
+
queryFn: () => (stationId ? pickApi.stationPrintPlek(stationId) : null),
|
|
40
|
+
enabled: Boolean(stationId),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
24
43
|
export function usePickScreens(stationId) {
|
|
25
44
|
return useQuery({
|
|
26
45
|
queryKey: ["pick", "screens", stationId ?? "all"],
|
|
@@ -4,6 +4,8 @@ export interface QueueFilters {
|
|
|
4
4
|
search: string;
|
|
5
5
|
documentTypeId: string | null;
|
|
6
6
|
urgentOnly: boolean;
|
|
7
|
+
/** Alleen regels die nog geprint moeten worden. */
|
|
8
|
+
onlyPending: boolean;
|
|
7
9
|
page: number;
|
|
8
10
|
pageSize: number;
|
|
9
11
|
sortRules: PickSortRule[];
|
|
@@ -24,9 +26,11 @@ export declare function usePickQueue(args: {
|
|
|
24
26
|
pickDay: string | null;
|
|
25
27
|
pendingCount: number;
|
|
26
28
|
printedCount: number;
|
|
29
|
+
urgentCount: number;
|
|
27
30
|
loading: boolean;
|
|
28
31
|
error: string | null;
|
|
29
32
|
lockedIds: Set<string>;
|
|
33
|
+
realtimeOk: boolean;
|
|
30
34
|
refresh: () => void;
|
|
31
35
|
};
|
|
32
36
|
/** Houdt bij dat dit scherm online is en ruimt verlopen locks op. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePickQueue.d.ts","sourceRoot":"","sources":["../../src/hooks/usePickQueue.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAY,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAG9E,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,UAAU,EAAE,OAAO,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED,eAAO,MAAM,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"usePickQueue.d.ts","sourceRoot":"","sources":["../../src/hooks/usePickQueue.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAY,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAG9E,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,UAAU,EAAE,OAAO,CAAC;IACpB,mDAAmD;IACnD,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED,eAAO,MAAM,cAAc,EAAE,YAS5B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;;;;;;;;;;;;EA6IA;AAED,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,CAAC,EAAE,MAAM;;EAuBhG;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,0FAOnF"}
|