@flowselections/pick-schermen-kwekers 1.0.2 → 1.0.3

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.
Files changed (94) hide show
  1. package/dist-lib/components/PickPublicPage.d.ts +7 -0
  2. package/dist-lib/components/PickPublicPage.d.ts.map +1 -0
  3. package/dist-lib/components/PickPublicPage.js +19 -0
  4. package/dist-lib/components/PickstationsPage.d.ts.map +1 -1
  5. package/dist-lib/components/PickstationsPage.js +57 -14
  6. package/dist-lib/components/pick/LanguageTiles.d.ts +8 -0
  7. package/dist-lib/components/pick/LanguageTiles.d.ts.map +1 -0
  8. package/dist-lib/components/pick/LanguageTiles.js +11 -0
  9. package/dist-lib/components/pick/PickScherm.d.ts +6 -4
  10. package/dist-lib/components/pick/PickScherm.d.ts.map +1 -1
  11. package/dist-lib/components/pick/PickScherm.js +145 -61
  12. package/dist-lib/components/pick/QueueTable.d.ts +4 -1
  13. package/dist-lib/components/pick/QueueTable.d.ts.map +1 -1
  14. package/dist-lib/components/pick/QueueTable.js +11 -10
  15. package/dist-lib/hooks/usePickQueue.d.ts +7 -1
  16. package/dist-lib/hooks/usePickQueue.d.ts.map +1 -1
  17. package/dist-lib/hooks/usePickQueue.js +43 -6
  18. package/dist-lib/index.d.ts +5 -6
  19. package/dist-lib/index.d.ts.map +1 -1
  20. package/dist-lib/index.js +6 -12
  21. package/dist-lib/integrations/supabase/auth-middleware.d.ts +8929 -5189
  22. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  23. package/dist-lib/integrations/supabase/client.d.ts +8929 -5189
  24. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  25. package/dist-lib/integrations/supabase/client.server.d.ts +8929 -5189
  26. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  27. package/dist-lib/integrations/supabase/types.d.ts +9245 -5437
  28. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  29. package/dist-lib/lib/layoutApi.d.ts +23 -0
  30. package/dist-lib/lib/layoutApi.d.ts.map +1 -0
  31. package/dist-lib/lib/layoutApi.js +94 -0
  32. package/dist-lib/lib/layoutMaker/documentProfiles.d.ts +50 -0
  33. package/dist-lib/lib/layoutMaker/documentProfiles.d.ts.map +1 -0
  34. package/dist-lib/lib/layoutMaker/documentProfiles.js +458 -0
  35. package/dist-lib/lib/layoutMaker/documentTypes.d.ts +123 -0
  36. package/dist-lib/lib/layoutMaker/documentTypes.d.ts.map +1 -0
  37. package/dist-lib/lib/layoutMaker/documentTypes.js +74 -0
  38. package/dist-lib/lib/layoutMaker/logoLibrary.d.ts +13 -0
  39. package/dist-lib/lib/layoutMaker/logoLibrary.d.ts.map +1 -0
  40. package/dist-lib/lib/layoutMaker/logoLibrary.js +54 -0
  41. package/dist-lib/lib/layoutMaker/pakbonBuilder.d.ts +196 -0
  42. package/dist-lib/lib/layoutMaker/pakbonBuilder.d.ts.map +1 -0
  43. package/dist-lib/lib/layoutMaker/pakbonBuilder.js +813 -0
  44. package/dist-lib/lib/layoutMaker/pakbonHtml.d.ts +48 -0
  45. package/dist-lib/lib/layoutMaker/pakbonHtml.d.ts.map +1 -0
  46. package/dist-lib/lib/layoutMaker/pakbonHtml.js +703 -0
  47. package/dist-lib/lib/layoutRenderer.d.ts +7 -0
  48. package/dist-lib/lib/layoutRenderer.d.ts.map +1 -0
  49. package/dist-lib/lib/layoutRenderer.js +233 -0
  50. package/dist-lib/lib/layoutTypes.d.ts +84 -0
  51. package/dist-lib/lib/layoutTypes.d.ts.map +1 -0
  52. package/dist-lib/lib/layoutTypes.js +25 -0
  53. package/dist-lib/lib/logisticsApi.d.ts +54 -0
  54. package/dist-lib/lib/logisticsApi.d.ts.map +1 -0
  55. package/dist-lib/lib/logisticsApi.js +72 -0
  56. package/dist-lib/lib/logisticsTypes.d.ts +34 -0
  57. package/dist-lib/lib/logisticsTypes.d.ts.map +1 -0
  58. package/dist-lib/lib/logisticsTypes.js +36 -0
  59. package/dist-lib/lib/orderContext.d.ts +50 -0
  60. package/dist-lib/lib/orderContext.d.ts.map +1 -0
  61. package/dist-lib/lib/orderContext.js +162 -0
  62. package/dist-lib/lib/pakbonOrderInput.d.ts +8 -0
  63. package/dist-lib/lib/pakbonOrderInput.d.ts.map +1 -0
  64. package/dist-lib/lib/pakbonOrderInput.js +150 -0
  65. package/dist-lib/lib/pickApi.d.ts +24 -1
  66. package/dist-lib/lib/pickApi.d.ts.map +1 -1
  67. package/dist-lib/lib/pickApi.js +46 -5
  68. package/dist-lib/lib/pickI18n.d.ts +14 -0
  69. package/dist-lib/lib/pickI18n.d.ts.map +1 -0
  70. package/dist-lib/lib/pickI18n.js +500 -0
  71. package/dist-lib/lib/pickPrint.d.ts +18 -0
  72. package/dist-lib/lib/pickPrint.d.ts.map +1 -0
  73. package/dist-lib/lib/pickPrint.js +184 -0
  74. package/dist-lib/lib/pickTypes.d.ts +1 -0
  75. package/dist-lib/lib/pickTypes.d.ts.map +1 -1
  76. package/package.json +5 -20
  77. package/dist-lib/components/PickBeheerPage.d.ts +0 -2
  78. package/dist-lib/components/PickBeheerPage.d.ts.map +0 -1
  79. package/dist-lib/components/PickBeheerPage.js +0 -84
  80. package/dist-lib/components/PickDashboardPage.d.ts +0 -2
  81. package/dist-lib/components/PickDashboardPage.d.ts.map +0 -1
  82. package/dist-lib/components/PickDashboardPage.js +0 -54
  83. package/dist-lib/components/PickKioskPage.d.ts +0 -7
  84. package/dist-lib/components/PickKioskPage.d.ts.map +0 -1
  85. package/dist-lib/components/PickKioskPage.js +0 -18
  86. package/dist-lib/components/PickstationDetailPage.d.ts +0 -6
  87. package/dist-lib/components/PickstationDetailPage.d.ts.map +0 -1
  88. package/dist-lib/components/PickstationDetailPage.js +0 -25
  89. package/dist-lib/components/pick/ConfigCrud.d.ts +0 -27
  90. package/dist-lib/components/pick/ConfigCrud.d.ts.map +0 -1
  91. package/dist-lib/components/pick/ConfigCrud.js +0 -64
  92. package/dist-lib/components/pick/CountryTiles.d.ts +0 -9
  93. package/dist-lib/components/pick/CountryTiles.d.ts.map +0 -1
  94. package/dist-lib/components/pick/CountryTiles.js +0 -13
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ slug: string;
3
+ }
4
+ /** Openbaar pickscherm, bereikbaar via /pickstation/<slug>. */
5
+ export declare function PickPublicPage({ slug }: Props): import("react").JSX.Element;
6
+ export {};
7
+ //# sourceMappingURL=PickPublicPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PickPublicPage.d.ts","sourceRoot":"","sources":["../../src/components/PickPublicPage.tsx"],"names":[],"mappings":"AAKA,UAAU,KAAK;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,+DAA+D;AAC/D,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,EAAE,KAAK,+BA2B7C"}
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useQuery } from "@tanstack/react-query";
3
+ import { Loader2 } from "lucide-react";
4
+ import { pickApi } from "../lib/pickApi";
5
+ import { PickScherm } from "./pick/PickScherm";
6
+ /** Openbaar pickscherm, bereikbaar via /pickstation/<slug>. */
7
+ export function PickPublicPage({ slug }) {
8
+ const { data: station, isLoading } = useQuery({
9
+ queryKey: ["pick", "station", slug],
10
+ queryFn: () => pickApi.stationBySlug(slug),
11
+ });
12
+ if (isLoading) {
13
+ return (_jsx("div", { className: "flex min-h-screen items-center justify-center bg-background", children: _jsx(Loader2, { className: "h-8 w-8 animate-spin text-primary" }) }));
14
+ }
15
+ if (!station) {
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
+ }
18
+ return (_jsx("div", { className: "min-h-screen bg-background p-6", children: _jsx(PickScherm, { station: station }) }));
19
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"PickstationsPage.d.ts","sourceRoot":"","sources":["../../src/components/PickstationsPage.tsx"],"names":[],"mappings":"AAKA,wBAAgB,gBAAgB,gCA+E/B"}
1
+ {"version":3,"file":"PickstationsPage.d.ts","sourceRoot":"","sources":["../../src/components/PickstationsPage.tsx"],"names":[],"mappings":"AAmCA,wBAAgB,gBAAgB,gCAkL/B"}
@@ -1,18 +1,61 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Link } from "@tanstack/react-router";
3
- import { Badge, Button, Card, CardContent } from "@flowselections/core";
4
- import { Gauge, Loader2, Settings2 } from "lucide-react";
5
- import { usePickScreens, usePickStations, usePickTasks } from "../hooks/usePickConfig";
2
+ import { useState } from "react";
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";
6
+ import { pickApi } from "../lib/pickApi";
7
+ import { usePickStations } from "../hooks/usePickConfig";
8
+ const emptyDraft = { name: "", description: "", active: true };
9
+ function publicUrl(slug) {
10
+ const origin = typeof window !== "undefined" ? window.location.origin : "";
11
+ return `${origin}/pickstation/${slug ?? ""}`;
12
+ }
6
13
  export function PickstationsPage() {
7
14
  const { data: stations, isLoading } = usePickStations();
8
- const { data: screens } = usePickScreens();
9
- const { data: tasks } = usePickTasks();
10
- const safeStations = (Array.isArray(stations) ? stations : []).filter((s) => s.active);
11
- const safeScreens = Array.isArray(screens) ? screens : [];
12
- const safeTasks = Array.isArray(tasks) ? tasks : [];
13
- 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: "Kies een pickstation om te beginnen met picken en printen." })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { variant: "outline", asChild: true, children: _jsxs(Link, { to: "/pickstations/dashboard", children: [_jsx(Gauge, { className: "mr-2 h-4 w-4" }), " Live dashboard"] }) }), _jsx(Button, { variant: "outline", asChild: true, children: _jsxs(Link, { to: "/pickstations/beheer", children: [_jsx(Settings2, { className: "mr-2 h-4 w-4" }), " Beheer"] }) })] })] }), 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. Maak er een aan via Beheer." })) : (_jsx("div", { className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-3", children: safeStations.map((station) => {
14
- const stationScreens = safeScreens.filter((s) => s.station_id === station.id && s.active);
15
- const task = safeTasks.find((t) => t.id === station.task_id);
16
- return (_jsx(Link, { to: "/pickstations/$stationId", params: { stationId: station.id }, className: "block", children: _jsx(Card, { className: "h-full border-2 transition-all hover:border-primary hover:shadow-lg", children: _jsxs(CardContent, { className: "space-y-3 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: "h-10 w-10 shrink-0 rounded-lg", style: { backgroundColor: station.color ?? "#3b82f6" } })] }), _jsxs("div", { className: "flex flex-wrap gap-2 text-xs", children: [task && _jsx(Badge, { variant: "secondary", children: task.name }), station.location && _jsx(Badge, { variant: "outline", children: station.location }), station.department && _jsx(Badge, { variant: "outline", children: station.department }), _jsxs(Badge, { variant: "outline", children: [stationScreens.length, " schermen"] })] })] }) }) }, station.id));
17
- }) }))] }));
15
+ const queryClient = useQueryClient();
16
+ const [draft, setDraft] = useState(null);
17
+ const safeStations = Array.isArray(stations) ? stations : [];
18
+ const save = useMutation({
19
+ mutationFn: async (d) => {
20
+ await pickApi.upsert("pick_stations", {
21
+ ...(d.id ? { id: d.id } : {}),
22
+ name: d.name.trim(),
23
+ description: d.description.trim() || null,
24
+ active: d.active,
25
+ });
26
+ },
27
+ onSuccess: () => {
28
+ toast.success("Pickstation opgeslagen.");
29
+ setDraft(null);
30
+ queryClient.invalidateQueries({ queryKey: ["pick", "stations"] });
31
+ },
32
+ onError: (e) => toast.error(e instanceof Error ? e.message : "Opslaan mislukt."),
33
+ });
34
+ const remove = useMutation({
35
+ mutationFn: (id) => pickApi.remove("pick_stations", id),
36
+ onSuccess: () => {
37
+ toast.success("Pickstation verwijderd.");
38
+ queryClient.invalidateQueries({ queryKey: ["pick", "stations"] });
39
+ },
40
+ onError: (e) => toast.error(e instanceof Error ? e.message : "Verwijderen mislukt."),
41
+ });
42
+ const copyLink = async (station) => {
43
+ const url = publicUrl(station.slug);
44
+ try {
45
+ await navigator.clipboard.writeText(url);
46
+ toast.success("Link gekopieerd.");
47
+ }
48
+ catch {
49
+ toast.error(url);
50
+ }
51
+ };
52
+ 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
+ (station.active
54
+ ? "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({
56
+ id: station.id,
57
+ name: station.name,
58
+ description: station.description ?? "",
59
+ active: station.active,
60
+ }), 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: "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"] })] })] }))] }) })] }));
18
61
  }
@@ -0,0 +1,8 @@
1
+ interface Props {
2
+ onSelect: (code: string) => void;
3
+ selected?: string | null;
4
+ }
5
+ /** Taalkeuze — zelfde tegel-uitstraling en bediening als de oude landselectie. */
6
+ export declare function LanguageTiles({ onSelect, selected }: Props): import("react").JSX.Element;
7
+ export {};
8
+ //# sourceMappingURL=LanguageTiles.d.ts.map
@@ -0,0 +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,+BAuB1D"}
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Card } from "@flowselections/core";
3
+ import { PICK_LANGUAGES } from "../../lib/pickI18n";
4
+ /** Taalkeuze — zelfde tegel-uitstraling en bediening als de oude landselectie. */
5
+ export function LanguageTiles({ onSelect, selected }) {
6
+ return (_jsx("div", { className: "grid grid-cols-2 gap-4 sm:grid-cols-3 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
+ if (e.key === "Enter" || e.key === " ")
8
+ onSelect(l.code);
9
+ }, className: "cursor-pointer select-none border-2 p-6 text-center transition-all hover:border-primary hover:shadow-lg focus:border-primary focus:outline-none active:scale-[0.98] " +
10
+ (selected === l.code ? "border-primary bg-primary/5" : ""), children: [_jsx("div", { className: "text-5xl leading-none", children: l.flag }), _jsx("div", { className: "mt-3 text-lg font-semibold text-foreground", children: l.label })] }, l.code))) }));
11
+ }
@@ -1,10 +1,12 @@
1
- import type { PickScreen, PickStation } from "../../lib/pickTypes";
1
+ import type { PickStation } from "../../lib/pickTypes";
2
2
  interface Props {
3
3
  station: PickStation;
4
- screen: PickScreen;
5
4
  onBack?: () => void;
6
- kiosk?: boolean;
7
5
  }
8
- export declare function PickScherm({ station, screen, onBack, kiosk }: Props): import("react").JSX.Element;
6
+ /**
7
+ * Pickscherm. De Logistiek Engine bepaalt welke documenten zichtbaar zijn en
8
+ * in welke volgorde; dit scherm toont uitsluitend wat de wachtrij teruggeeft.
9
+ */
10
+ export declare function PickScherm({ station, onBack }: Props): import("react").JSX.Element;
9
11
  export {};
10
12
  //# sourceMappingURL=PickScherm.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PickScherm.d.ts","sourceRoot":"","sources":["../../../src/components/pick/PickScherm.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAiB,UAAU,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAelF,UAAU,KAAK;IACb,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,KAAK,+BAgQnE"}
1
+ {"version":3,"file":"PickScherm.d.ts","sourceRoot":"","sources":["../../../src/components/pick/PickScherm.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAiB,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAUtE,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,+BAmVpD"}
@@ -1,114 +1,198 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useCallback, useMemo, useState } from "react";
3
- import { Badge, Button, Card, CardContent, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Switch, toast, } from "@flowselections/core";
4
- import { ArrowLeft, Eye, Loader2, Printer, RefreshCw, Search, WifiOff } from "lucide-react";
5
- import { CountryTiles } from "./CountryTiles";
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
+ import { Badge, Button, Card, CardContent, Input, toast } from "@flowselections/core";
4
+ import { ArrowLeft, Globe, Loader2, Printer, RefreshCw, Search, WifiOff } from "lucide-react";
5
+ import { LanguageTiles } from "./LanguageTiles";
6
6
  import { QueueTable } from "./QueueTable";
7
7
  import { getSessionId, pickApi } from "../../lib/pickApi";
8
- import { defaultFilters, usePickCountryCounts, usePickHeartbeat, usePickQueue, } from "../../hooks/usePickQueue";
9
- import { usePickCountries, usePickDocumentTypes, usePickPrinters, usePickStatuses, } from "../../hooks/usePickConfig";
10
- export function PickScherm({ station, screen, onBack, kiosk }) {
8
+ import { completeLogisticsTaskForItem, documentTypeOf, EmptyLayoutError, MissingLayoutError, printQueueItem, } from "../../lib/pickPrint";
9
+ import { getPickTranslator } from "../../lib/pickI18n";
10
+ import { defaultFilters, usePickHeartbeat, usePickQueue, } from "../../hooks/usePickQueue";
11
+ import { usePickDocumentTypes, usePickStatuses } from "../../hooks/usePickConfig";
12
+ const LANG_STORAGE_KEY = "pick.language";
13
+ /**
14
+ * Pickscherm. De Logistiek Engine bepaalt welke documenten zichtbaar zijn en
15
+ * in welke volgorde; dit scherm toont uitsluitend wat de wachtrij teruggeeft.
16
+ */
17
+ export function PickScherm({ station, onBack }) {
11
18
  const sessionId = getSessionId();
12
- const [country, setCountry] = useState(station.require_country_selection ? null : "");
19
+ const [lang, setLang] = useState(null);
13
20
  const [filters, setFilters] = useState(defaultFilters);
14
21
  const [busyId, setBusyId] = useState(null);
15
- const [preview, setPreview] = useState(false);
16
- const { data: countries } = usePickCountries();
22
+ const [pending, setPending] = useState(null);
23
+ const autoFetched = useRef(false);
17
24
  const { data: statuses } = usePickStatuses();
18
25
  const { data: documentTypes } = usePickDocumentTypes();
19
- const { data: printers } = usePickPrinters();
20
- const { data: counts } = usePickCountryCounts(screen.id);
21
- const { online } = usePickHeartbeat(station.id, screen.id, sessionId);
22
- const effectiveFilters = useMemo(() => ({
23
- ...filters,
24
- country: country || null,
25
- sortRules: (screen.sort_rules?.length ? screen.sort_rules : station.sort_rules) ?? [],
26
- }), [filters, country, screen.sort_rules, station.sort_rules]);
27
- const { items, total, loading, lockedIds, refresh } = usePickQueue({
26
+ const { online } = usePickHeartbeat(station.id, null, sessionId);
27
+ const t = useMemo(() => getPickTranslator(lang ?? "nl"), [lang]);
28
+ const layoutMissingMessage = useCallback((documentType) => t.lang === "nl"
29
+ ? `Geen layout ingesteld voor ${documentType}. Stel deze in in de Layout maker (pakbonnen, verzendbrieven, extra).`
30
+ : `No layout configured for ${documentType}. Set it up in the Layout maker.`, [t]);
31
+ useEffect(() => {
32
+ if (typeof window === "undefined")
33
+ return;
34
+ const stored = window.localStorage.getItem(LANG_STORAGE_KEY);
35
+ if (stored)
36
+ setLang(stored);
37
+ }, []);
38
+ const chooseLang = useCallback((code) => {
39
+ setLang(code);
40
+ if (typeof window !== "undefined")
41
+ window.localStorage.setItem(LANG_STORAGE_KEY, code);
42
+ }, []);
43
+ const effectiveFilters = useMemo(() => ({ ...filters, country: null, sortRules: [] }), [filters]);
44
+ const { items, total, loading, lockedIds, refresh, pendingCount, printedCount } = usePickQueue({
28
45
  stationId: station.id,
29
- screenId: screen.id,
30
46
  filters: effectiveFilters,
31
- enabled: country !== null,
47
+ enabled: lang !== null,
32
48
  });
33
- const printerId = screen.printer_id ?? station.default_printer_id ?? null;
34
- const printer = (Array.isArray(printers) ? printers : []).find((p) => p.id === printerId);
35
49
  const runPrint = useCallback(async (item) => {
36
- if (preview) {
37
- toast.info(`Voorbeeldmodus: ${item.document_number} zou nu geprint worden.`);
50
+ const printed = await printQueueItem(item, t.locale);
51
+ await pickApi.completePrint(item.id, null, sessionId);
52
+ await completeLogisticsTaskForItem(item, printed);
53
+ toast.success(`${item.document_number ?? t("document")} ${t("sentToPrinter")}`);
54
+ refresh();
55
+ }, [sessionId, refresh, t]);
56
+ /** Zet een printfout om in een begrijpelijke melding, zonder ooit zelf op te maken. */
57
+ const showPrintError = useCallback((e, item) => {
58
+ if (e instanceof EmptyLayoutError) {
59
+ toast.error(t.lang === "nl"
60
+ ? `Layout heeft nog geen inhoud (${e.layoutName}).`
61
+ : `Layout has no content yet (${e.layoutName}).`);
38
62
  return;
39
63
  }
40
- if (printer && printer.status !== "online" && printer.status !== "unknown") {
41
- toast.error(`Printer ${printer.name} is niet beschikbaar (${printer.status}).`);
42
- await pickApi.releaseLock(item.id);
64
+ if (e instanceof MissingLayoutError) {
65
+ toast.error(layoutMissingMessage(e.documentType || documentTypeOf(item)));
43
66
  return;
44
67
  }
45
- await pickApi.completePrint(item.id, printerId, sessionId);
46
- toast.success(`${item.document_number} verstuurd naar ${printer?.name ?? "printer"}.`);
47
- refresh();
48
- }, [preview, printer, printerId, sessionId, refresh]);
68
+ toast.error(e instanceof Error ? e.message : t("notAvailable"));
69
+ }, [layoutMissingMessage, t]);
49
70
  const handlePick = useCallback(async (item) => {
50
71
  setBusyId(item.id);
51
72
  try {
52
73
  const claimed = await pickApi.claim({
53
74
  itemId: item.id,
54
75
  stationId: station.id,
55
- screenId: screen.id,
76
+ screenId: null,
56
77
  sessionId,
57
- timeout: station.lock_timeout_seconds,
78
+ timeout: station.lock_timeout_seconds ?? 120,
58
79
  });
59
80
  await runPrint(claimed ?? item);
60
81
  }
61
82
  catch (e) {
62
- const message = e instanceof Error ? e.message : "Dit document is zojuist verwerkt.";
63
- toast.error(message);
64
- // Als claim wel is gelukt maar runPrint faalde, geef de lock vrij zodat
65
- // het document niet minutenlang geblokkeerd blijft voor andere schermen.
83
+ showPrintError(e, item);
66
84
  try {
67
85
  await pickApi.releaseLock(item.id);
68
86
  }
69
87
  catch {
70
- // Lock is mogelijk al opgeruimd of van een andere sessie; negeren.
88
+ // lock al opgeruimd of van een andere sessie
71
89
  }
72
90
  refresh();
73
91
  }
74
92
  finally {
75
93
  setBusyId(null);
76
94
  }
77
- }, [station.id, station.lock_timeout_seconds, screen.id, sessionId, runPrint, refresh]);
78
- const handleNext = useCallback(async () => {
95
+ }, [station.id, station.lock_timeout_seconds, sessionId, runPrint, refresh, showPrintError]);
96
+ /** Vraagt de Logistiek Engine om de volgende opdracht en laat deze ter bevestiging zien. */
97
+ /** Print een al geprint document opnieuw, zonder de wachtrijstatus te wijzigen. */
98
+ const handleReprint = useCallback(async (item) => {
99
+ setBusyId(item.id);
100
+ try {
101
+ await printQueueItem(item, t.locale);
102
+ toast.success(`${item.document_number ?? t("document")} ${t("sentToPrinter")}`);
103
+ }
104
+ catch (e) {
105
+ showPrintError(e, item);
106
+ }
107
+ finally {
108
+ setBusyId(null);
109
+ }
110
+ }, [t, showPrintError]);
111
+ const fetchNext = useCallback(async () => {
79
112
  setBusyId("next");
80
- let claimed = null;
81
113
  try {
82
- claimed = await pickApi.claimNext({
114
+ const claimed = await pickApi.claimNext({
83
115
  stationId: station.id,
84
- screenId: screen.id,
85
- country: country || null,
116
+ screenId: null,
117
+ country: null,
86
118
  sessionId,
87
- timeout: station.lock_timeout_seconds,
119
+ timeout: station.lock_timeout_seconds ?? 120,
88
120
  });
89
- await runPrint(claimed);
121
+ if (!claimed)
122
+ throw new Error(t("noDocument"));
123
+ setPending(claimed);
124
+ return claimed;
90
125
  }
91
126
  catch (e) {
92
- const message = e instanceof Error ? e.message : "Geen document beschikbaar.";
127
+ const message = e instanceof Error ? e.message : t("noDocument");
93
128
  toast.error(message);
94
- if (claimed?.id) {
95
- try {
96
- await pickApi.releaseLock(claimed.id);
97
- }
98
- catch {
99
- // Negeren; lock wordt toch door heartbeat opgeruimd.
100
- }
101
- }
129
+ setPending(null);
102
130
  refresh();
131
+ return null;
103
132
  }
104
133
  finally {
105
134
  setBusyId(null);
106
135
  }
107
- }, [station.id, station.lock_timeout_seconds, screen.id, country, sessionId, runPrint, refresh]);
108
- // Stap 1 — landselectie
109
- if (country === null) {
110
- return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-2xl font-bold text-foreground", children: screen.name }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Kies eerst een land om verder te gaan." })] }), onBack && !kiosk && (_jsxs(Button, { variant: "outline", onClick: onBack, children: [_jsx(ArrowLeft, { className: "mr-2 h-4 w-4" }), " Terug"] }))] }), _jsx(CountryTiles, { countries: Array.isArray(countries) ? countries : [], counts: counts ?? {}, onSelect: setCountry })] }));
136
+ }, [station.id, station.lock_timeout_seconds, sessionId, refresh, t]);
137
+ /** Bevestigt de opdracht, print deze en haalt direct de volgende op. */
138
+ const confirmPending = useCallback(async () => {
139
+ const item = pending;
140
+ if (!item)
141
+ return;
142
+ setBusyId("confirm");
143
+ try {
144
+ await runPrint(item);
145
+ setPending(null);
146
+ setBusyId(null);
147
+ await fetchNext();
148
+ }
149
+ catch (e) {
150
+ showPrintError(e, item);
151
+ try {
152
+ await pickApi.releaseLock(item.id);
153
+ }
154
+ catch {
155
+ // heartbeat ruimt de lock alsnog op
156
+ }
157
+ setPending(null);
158
+ refresh();
159
+ setBusyId(null);
160
+ }
161
+ }, [pending, runPrint, fetchNext, refresh, showPrintError]);
162
+ const cancelPending = useCallback(async () => {
163
+ const item = pending;
164
+ setPending(null);
165
+ if (item) {
166
+ try {
167
+ await pickApi.releaseLock(item.id);
168
+ }
169
+ catch {
170
+ // lock verloopt automatisch
171
+ }
172
+ }
173
+ refresh();
174
+ }, [pending, refresh]);
175
+ /** Zet automatisch de volgende opdracht klaar, zodat één klik op printen volstaat. */
176
+ useEffect(() => {
177
+ if (lang === null || loading)
178
+ return;
179
+ if (pending !== null || busyId !== null)
180
+ return;
181
+ if (autoFetched.current)
182
+ return;
183
+ if (items.length === 0)
184
+ return;
185
+ autoFetched.current = true;
186
+ void fetchNext();
187
+ }, [lang, loading, pending, busyId, items.length, fetchNext]);
188
+ /** Na een lege of mislukte poging opnieuw proberen zodra de wachtrij wijzigt. */
189
+ useEffect(() => {
190
+ autoFetched.current = false;
191
+ }, [items.length]);
192
+ // Stap 1 — taalselectie
193
+ if (lang === null) {
194
+ return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-2xl font-bold text-foreground", children: station.name }), _jsx("p", { className: "text-sm text-muted-foreground", children: t("chooseLanguageHint") })] }), onBack && (_jsxs(Button, { variant: "outline", onClick: onBack, children: [_jsx(ArrowLeft, { className: "mr-2 h-4 w-4" }), " ", t("back")] }))] }), _jsx(LanguageTiles, { onSelect: chooseLang })] }));
111
195
  }
112
196
  const totalPages = Math.max(1, Math.ceil(total / filters.pageSize));
113
- return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsxs(Button, { variant: "outline", size: "lg", onClick: () => setCountry(null), children: [_jsx(ArrowLeft, { className: "mr-2 h-5 w-5" }), " Land wijzigen"] }), _jsxs("div", { children: [_jsxs("h2", { className: "text-xl font-bold text-foreground", children: [screen.name, " \u2014 ", country || "alle landen"] }), _jsxs("p", { className: "text-sm text-muted-foreground", children: [total, " document", total === 1 ? "" : "en", " in wachtrij \u00B7 printer: ", printer?.name ?? "geen", printer && printer.status !== "online" ? ` (${printer.status})` : ""] })] })] }), _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" }), " Offline"] })), _jsxs("div", { className: "flex items-center gap-2 rounded-md border px-3 py-2", children: [_jsx(Eye, { className: "h-4 w-4 text-muted-foreground" }), _jsx("span", { className: "text-sm", children: "Voorbeeld" }), _jsx(Switch, { checked: preview, onCheckedChange: setPreview })] }), _jsx(Button, { variant: "outline", size: "icon", onClick: refresh, "aria-label": "Vernieuwen", children: _jsx(RefreshCw, { className: "h-4 w-4" }) }), _jsxs(Button, { size: "lg", className: "h-14 px-8 text-lg", onClick: handleNext, disabled: busyId !== null, children: [busyId === "next" ? (_jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" })) : (_jsx(Printer, { className: "mr-2 h-5 w-5" })), "Volgende uitprinten"] })] })] }), _jsx(Card, { children: _jsxs(CardContent, { className: "flex flex-wrap items-center gap-3 p-4", children: [_jsxs("div", { className: "relative min-w-64 flex-1", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { className: "pl-9", placeholder: "Zoek op order, document, klant, referentie of barcode", value: filters.search, onChange: (e) => setFilters((f) => ({ ...f, search: e.target.value, page: 0 })) })] }), _jsxs(Select, { value: filters.documentTypeId ?? "all", onValueChange: (v) => setFilters((f) => ({ ...f, documentTypeId: v === "all" ? null : v, page: 0 })), children: [_jsx(SelectTrigger, { className: "w-56", children: _jsx(SelectValue, { placeholder: "Documenttype" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "Alle documenttypes" }), (Array.isArray(documentTypes) ? documentTypes : []).map((d) => (_jsx(SelectItem, { value: d.id, children: d.name }, d.id)))] })] }), _jsxs("div", { className: "flex items-center gap-2 rounded-md border px-3 py-2", children: [_jsx("span", { className: "text-sm", children: "Alleen spoed" }), _jsx(Switch, { checked: filters.urgentOnly, onCheckedChange: (v) => setFilters((f) => ({ ...f, urgentOnly: v, page: 0 })) })] })] }) }), 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" }), " Wachtrij laden..."] })) : (_jsx(QueueTable, { items: items, lockedIds: lockedIds, statuses: Array.isArray(statuses) ? statuses : [], documentTypes: Array.isArray(documentTypes) ? documentTypes : [], busyId: busyId, onPick: handlePick })), _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("span", { className: "text-sm text-muted-foreground", children: ["Pagina ", filters.page + 1, " van ", totalPages] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { variant: "outline", disabled: filters.page === 0, onClick: () => setFilters((f) => ({ ...f, page: Math.max(0, f.page - 1) })), children: "Vorige" }), _jsx(Button, { variant: "outline", disabled: filters.page + 1 >= totalPages, onClick: () => setFilters((f) => ({ ...f, page: f.page + 1 })), children: "Volgende" })] })] })] }));
197
+ return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsxs(Button, { variant: "outline", size: "lg", onClick: () => setLang(null), children: [_jsx(Globe, { className: "mr-2 h-5 w-5" }), " ", t("changeLanguage")] }), _jsxs("div", { children: [_jsx("h2", { className: "text-xl font-bold text-foreground", children: station.name }), _jsxs("p", { className: "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", onClick: refresh, "aria-label": t("refresh"), children: _jsx(RefreshCw, { className: "h-4 w-4" }) }), _jsxs(Button, { size: "lg", className: "h-14 px-8 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" })), t("printNext"), pending?.document_number ? ` · ${pending.document_number}` : ""] })] })] }), pending && (_jsx(Card, { className: "border-2 border-primary", children: _jsxs(CardContent, { className: "flex flex-wrap items-center justify-between gap-4 p-5", children: [_jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-muted-foreground", children: t("confirmPrintTitle") }), _jsx("p", { className: "text-2xl font-bold text-foreground", children: pending.document_number ?? t("document") }), _jsx("p", { className: "text-sm text-muted-foreground", children: [pending.order_number, pending.customer_name].filter(Boolean).join(" · ") })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { variant: "outline", size: "lg", onClick: cancelPending, disabled: busyId !== null, children: t("cancel") }), _jsxs(Button, { size: "lg", className: "h-14 px-8 text-lg", 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")] })] })] }) })), _jsx(Card, { children: _jsx(CardContent, { className: "flex flex-wrap items-center gap-3 p-4", children: _jsxs("div", { className: "relative min-w-64 flex-1", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { className: "pl-9", placeholder: t("search"), value: filters.search, onChange: (e) => setFilters((f) => ({ ...f, search: e.target.value, page: 0 })) })] }) }) }), 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 items-center justify-between", children: [_jsxs("span", { className: "text-sm text-muted-foreground", children: [t("page"), " ", filters.page + 1, " ", t("of"), " ", totalPages] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { variant: "outline", disabled: filters.page === 0, onClick: () => setFilters((f) => ({ ...f, page: Math.max(0, f.page - 1) })), children: t("previous") }), _jsx(Button, { variant: "outline", disabled: filters.page + 1 >= totalPages, onClick: () => setFilters((f) => ({ ...f, page: f.page + 1 })), children: t("next") })] })] })] }));
114
198
  }
@@ -1,4 +1,5 @@
1
1
  import type { PickDocumentType, PickQueueItem, PickStatus } from "../../lib/pickTypes";
2
+ import { type PickTranslator } from "../../lib/pickI18n";
2
3
  interface Props {
3
4
  items: PickQueueItem[];
4
5
  lockedIds: Set<string>;
@@ -6,8 +7,10 @@ interface Props {
6
7
  documentTypes: PickDocumentType[];
7
8
  busyId: string | null;
8
9
  onPick: (item: PickQueueItem) => void;
10
+ onReprint?: (item: PickQueueItem) => void;
9
11
  readOnly?: boolean;
12
+ t?: PickTranslator;
10
13
  }
11
- export declare function QueueTable({ items, lockedIds, statuses, documentTypes, busyId, onPick, readOnly, }: Props): import("react").JSX.Element;
14
+ export declare function QueueTable({ items, lockedIds, statuses, documentTypes, busyId, onPick, onReprint, readOnly, t: translator, }: Props): import("react").JSX.Element;
12
15
  export {};
13
16
  //# sourceMappingURL=QueueTable.d.ts.map
@@ -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;AAGvF,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,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AASD,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,SAAS,EACT,QAAQ,EACR,aAAa,EACb,MAAM,EACN,MAAM,EACN,QAAQ,GACT,EAAE,KAAK,+BAsGP"}
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,+BA8GP"}
@@ -1,7 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Badge, Button, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@flowselections/core";
3
- import { AlertTriangle, Lock, Printer } from "lucide-react";
3
+ import { AlertTriangle, Lock, Printer, RotateCcw } from "lucide-react";
4
4
  import { sinceLabel } from "../../lib/pickTypes";
5
+ import { getPickTranslator } from "../../lib/pickI18n";
5
6
  function priorityTone(priority, urgent) {
6
7
  if (urgent)
7
8
  return "bg-destructive text-destructive-foreground";
@@ -11,19 +12,19 @@ function priorityTone(priority, urgent) {
11
12
  return "bg-secondary text-secondary-foreground";
12
13
  return "bg-muted text-muted-foreground";
13
14
  }
14
- export function QueueTable({ items, lockedIds, statuses, documentTypes, busyId, onPick, readOnly, }) {
15
+ export function QueueTable({ items, lockedIds, statuses, documentTypes, busyId, onPick, onReprint, readOnly, t: translator, }) {
16
+ const t = translator ?? getPickTranslator("nl");
15
17
  const safeItems = Array.isArray(items) ? items : [];
16
- const statusById = new Map((Array.isArray(statuses) ? statuses : []).map((s) => [s.id, s]));
17
18
  const docById = new Map((Array.isArray(documentTypes) ? documentTypes : []).map((d) => [d.id, d]));
18
- return (_jsx("div", { className: "overflow-x-auto rounded-lg border", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Document" }), _jsx(TableHead, { children: "Order" }), _jsx(TableHead, { children: "Klant" }), _jsx(TableHead, { className: "hidden lg:table-cell", children: "Afleveradres" }), _jsx(TableHead, { className: "text-right", children: "Regels" }), _jsx(TableHead, { className: "text-right", children: "Karren" }), _jsx(TableHead, { children: "Status" }), _jsx(TableHead, { children: "Prioriteit" }), _jsx(TableHead, { children: "Aangemeld" }), _jsx(TableHead, { children: "Wachttijd" }), _jsx(TableHead, { className: "text-right", children: "Actie" })] }) }), _jsxs(TableBody, { children: [safeItems.map((item) => {
19
- const locked = lockedIds.has(item.id);
20
- const status = item.status_id ? statusById.get(item.status_id) : undefined;
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
+ const printed = !!item.printed_at;
21
+ const locked = !printed && lockedIds.has(item.id);
21
22
  const doc = item.document_type_id ? docById.get(item.document_type_id) : undefined;
22
23
  const warnings = Array.isArray(item.warnings) ? item.warnings : [];
23
- return (_jsxs(TableRow, { className: locked ? "opacity-50" : 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": "Waarschuwing" }))] }), _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, { style: status?.color ? { backgroundColor: status.color, color: "#fff" } : undefined, children: status?.name ?? "-" }) }), _jsx(TableCell, { children: _jsx("span", { className: "rounded px-2 py-1 text-xs font-semibold " +
24
- priorityTone(item.priority, item.is_urgent), children: item.is_urgent ? "SPOED" : item.priority }) }), _jsx(TableCell, { className: "whitespace-nowrap text-sm", children: new Date(item.announced_at).toLocaleTimeString("nl-NL", {
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, {
25
26
  hour: "2-digit",
26
27
  minute: "2-digit",
27
- }) }), _jsx(TableCell, { className: "whitespace-nowrap text-sm", children: sinceLabel(item.announced_at) }), _jsx(TableCell, { className: "text-right", children: 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" }), " In behandeling"] })) : (_jsxs(Button, { size: "sm", variant: "outline", disabled: readOnly || busyId === item.id, onClick: () => onPick(item), children: [_jsx(Printer, { className: "mr-1 h-4 w-4" }), "Printen"] })) })] }, item.id));
28
- }), safeItems.length === 0 && (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 11, className: "py-10 text-center text-muted-foreground", children: "Geen documenten in de wachtrij." }) }))] })] }) }));
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") }) }))] })] }) }));
29
30
  }
@@ -21,6 +21,9 @@ export declare function usePickQueue(args: {
21
21
  }): {
22
22
  items: PickQueueItem[];
23
23
  total: number;
24
+ pickDay: string | null;
25
+ pendingCount: number;
26
+ printedCount: number;
24
27
  loading: boolean;
25
28
  error: string | null;
26
29
  lockedIds: Set<string>;
@@ -30,5 +33,8 @@ export declare function usePickQueue(args: {
30
33
  export declare function usePickHeartbeat(stationId?: string, screenId?: string | null, sessionId?: string): {
31
34
  online: boolean;
32
35
  };
33
- export declare function usePickCountryCounts(screenId?: string): import("@tanstack/react-query").UseQueryResult<NoInfer<Record<string, number>>, Error>;
36
+ export declare function usePickCountryCounts(args: {
37
+ stationId?: string;
38
+ screenId?: string;
39
+ }): import("@tanstack/react-query").UseQueryResult<NoInfer<Record<string, number>>, Error>;
34
40
  //# sourceMappingURL=usePickQueue.d.ts.map
@@ -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,YAQ5B,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;;;;;;;EAkEA;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,QAAQ,CAAC,EAAE,MAAM,0FAOrD"}
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,YAQ5B,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;;;;;;;;;;EA0GA;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"}