@flowselections/floriday-afhandeling-module 1.0.43 → 1.0.45

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 (68) hide show
  1. package/dist-lib/components/afhandeling/ControlePage.d.ts.map +1 -1
  2. package/dist-lib/components/afhandeling/ControlePage.js +92 -54
  3. package/dist-lib/components/afhandeling/WeekAgendaView.d.ts.map +1 -1
  4. package/dist-lib/components/afhandeling/WeekAgendaView.js +24 -1
  5. package/dist-lib/hooks/useCartIndex.d.ts.map +1 -1
  6. package/dist-lib/hooks/useCartIndex.js +16 -7
  7. package/dist-lib/hooks/useEngineBufferEvents.d.ts +12 -0
  8. package/dist-lib/hooks/useEngineBufferEvents.d.ts.map +1 -0
  9. package/dist-lib/hooks/useEngineBufferEvents.js +52 -0
  10. package/dist-lib/hooks/useEngineTasks.d.ts.map +1 -1
  11. package/dist-lib/hooks/useEngineTasks.js +8 -18
  12. package/dist-lib/hooks/useKnownOrders.d.ts +1 -0
  13. package/dist-lib/hooks/useKnownOrders.d.ts.map +1 -1
  14. package/dist-lib/hooks/useKnownOrders.js +28 -2
  15. package/dist-lib/hooks/useOrderPickDates.d.ts +1 -1
  16. package/dist-lib/hooks/useOrderPickDates.d.ts.map +1 -1
  17. package/dist-lib/hooks/useOrderPickDates.js +5 -3
  18. package/dist-lib/hooks/usePickAgenda.d.ts +4 -0
  19. package/dist-lib/hooks/usePickAgenda.d.ts.map +1 -1
  20. package/dist-lib/hooks/usePickAgenda.js +23 -16
  21. package/dist-lib/hooks/usePickMode.d.ts +8 -2
  22. package/dist-lib/hooks/usePickMode.d.ts.map +1 -1
  23. package/dist-lib/hooks/usePickMode.js +17 -9
  24. package/dist-lib/index.d.ts +1 -2
  25. package/dist-lib/index.d.ts.map +1 -1
  26. package/dist-lib/index.js +1 -33
  27. package/dist-lib/integrations/supabase/auth-middleware.d.ts +3256 -191
  28. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  29. package/dist-lib/integrations/supabase/client.server.d.ts +3256 -191
  30. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  31. package/dist-lib/integrations/supabase/moduleClient.d.ts +3257 -190
  32. package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -1
  33. package/dist-lib/integrations/supabase/types.d.ts +3354 -261
  34. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  35. package/dist-lib/lib/afhandelingSettings.functions.d.ts +6144 -14
  36. package/dist-lib/lib/afhandelingSettings.functions.d.ts.map +1 -1
  37. package/dist-lib/lib/carriers.functions.d.ts +15375 -40
  38. package/dist-lib/lib/carriers.functions.d.ts.map +1 -1
  39. package/dist-lib/lib/cartAssignments.functions.d.ts +15360 -35
  40. package/dist-lib/lib/cartAssignments.functions.d.ts.map +1 -1
  41. package/dist-lib/lib/cartControl.functions.d.ts +33902 -104
  42. package/dist-lib/lib/cartControl.functions.d.ts.map +1 -1
  43. package/dist-lib/lib/cartControl.functions.js +389 -259
  44. package/dist-lib/lib/deliveryNote.functions.d.ts +3256 -191
  45. package/dist-lib/lib/deliveryNote.functions.d.ts.map +1 -1
  46. package/dist-lib/lib/engineLayouts.functions.d.ts +3256 -191
  47. package/dist-lib/lib/engineLayouts.functions.d.ts.map +1 -1
  48. package/dist-lib/lib/floriday.functions.d.ts +6146 -14
  49. package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
  50. package/dist-lib/lib/floriday.functions.js +1 -0
  51. package/dist-lib/lib/orderFulfilment.functions.d.ts +3256 -191
  52. package/dist-lib/lib/orderFulfilment.functions.d.ts.map +1 -1
  53. package/dist-lib/lib/pickDates.functions.d.ts +3260 -190
  54. package/dist-lib/lib/pickDates.functions.d.ts.map +1 -1
  55. package/dist-lib/lib/pickDates.functions.js +30 -3
  56. package/dist-lib/lib/pickMode.d.ts +20 -2
  57. package/dist-lib/lib/pickMode.d.ts.map +1 -1
  58. package/dist-lib/lib/pickMode.js +65 -16
  59. package/dist-lib/lib/realtimeBus.d.ts +20 -0
  60. package/dist-lib/lib/realtimeBus.d.ts.map +1 -0
  61. package/dist-lib/lib/realtimeBus.js +56 -0
  62. package/dist-lib/lib/serverFnError.d.ts +12 -0
  63. package/dist-lib/lib/serverFnError.d.ts.map +1 -1
  64. package/dist-lib/lib/serverFnError.js +48 -1
  65. package/dist-lib/moduleDefinition.d.ts +3 -0
  66. package/dist-lib/moduleDefinition.d.ts.map +1 -0
  67. package/dist-lib/moduleDefinition.js +36 -0
  68. package/package.json +2 -2
@@ -23,13 +23,40 @@ function keyOf(value) {
23
23
  return null;
24
24
  return normalizeOrderNumber(trimmed) ?? trimmed.toUpperCase();
25
25
  }
26
+ /** Bovengrens per opvraging: liever een nette melding dan een vastlopend scherm. */
27
+ const MAX_ORDERS = 3000;
28
+ const MAX_TASKS = 6000;
29
+ function defaultWindow() {
30
+ const today = new Date();
31
+ const shift = (delta) => {
32
+ const d = new Date(today);
33
+ d.setUTCDate(d.getUTCDate() + delta);
34
+ return d.toISOString().slice(0, 10);
35
+ };
36
+ return { from: shift(-60), to: shift(60) };
37
+ }
26
38
  export const listOrderPickDates = createServerFn({ method: "GET" })
27
39
  .middleware([requireSupabaseAuth])
28
- .handler(async ({ context }) => {
40
+ .inputValidator((input) => input ?? {})
41
+ .handler(async ({ context, data }) => {
29
42
  const supabase = context.supabase;
43
+ const fallback = defaultWindow();
44
+ const from = data.from ?? fallback.from;
45
+ const to = data.to ?? fallback.to;
30
46
  const [ordersRes, tasksRes] = await Promise.all([
31
- supabase.from("orders").select("id, order_number, delivery_date").is("deleted_at", null).limit(5000),
32
- supabase.from("logistics_tasks").select("order_ref, planned_start").limit(20000),
47
+ supabase
48
+ .from("orders")
49
+ .select("id, order_number, delivery_date")
50
+ .is("deleted_at", null)
51
+ .gte("delivery_date", from)
52
+ .lte("delivery_date", to)
53
+ .limit(MAX_ORDERS),
54
+ supabase
55
+ .from("logistics_tasks")
56
+ .select("order_ref, planned_start")
57
+ .gte("planned_start", `${from}T00:00:00Z`)
58
+ .lte("planned_start", `${to}T23:59:59Z`)
59
+ .limit(MAX_TASKS),
33
60
  ]);
34
61
  if (ordersRes.error)
35
62
  return { items: [], error: ordersRes.error.message };
@@ -1,14 +1,32 @@
1
1
  export type PickMode = "order" | "cart" | "smallest" | "buffer" | null;
2
+ /**
3
+ * `status` maakt het verschil tussen "de Engine zegt dat er niet per kar wordt
4
+ * gepickt" (ok) en "we konden het niet ophalen" (unknown). Een storing mag
5
+ * karcontrole niet stilzwijgend uitzetten.
6
+ */
2
7
  export interface PickModeInfo {
3
8
  pickMode: PickMode;
4
9
  showCartControl: boolean;
10
+ status: "unknown" | "ok";
11
+ lastError?: string;
5
12
  }
6
- /** Huidige (gecachete) waarde; standaard verborgen. */
13
+ /** Huidige (gecachete) waarde; standaard onbekend. */
7
14
  export declare function getPickMode(): PickModeInfo;
8
15
  export declare function subscribePickMode(cb: () => void): () => void;
16
+ /**
17
+ * Haalt de werkwijze op bij de Engine. Gooit de fout door bij een storing; de
18
+ * laatst succesvol opgehaalde werkwijze blijft dan gewoon gelden.
19
+ */
9
20
  export declare function fetchPickMode(): Promise<PickModeInfo>;
10
21
  export declare function startPickModeWatch(): void;
22
+ /**
23
+ * Veilige variant voor plekken die niet met een fout om kunnen gaan (zoals de
24
+ * zijbalk): faalt stil en geeft de laatst bekende waarde terug.
25
+ */
11
26
  export declare function ensurePickMode(): Promise<PickModeInfo>;
12
- /** Forceert een verse ophaalactie (na het wijzigen van de werkwijze). */
27
+ /**
28
+ * Forceert een verse ophaalactie (na het wijzigen van de werkwijze). Gooit de
29
+ * fout door, zodat de aanroeper "storing" kan onderscheiden van "uit".
30
+ */
13
31
  export declare function refreshPickMode(): Promise<PickModeInfo>;
14
32
  //# sourceMappingURL=pickMode.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pickMode.d.ts","sourceRoot":"","sources":["../../src/lib/pickMode.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,GAAG,IAAI,CAAC;AAEvE,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,QAAQ,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAwBD,uDAAuD;AACvD,wBAAgB,WAAW,IAAI,YAAY,CAE1C;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAG5D;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,YAAY,CAAC,CAe3D;AAeD,wBAAgB,kBAAkB,IAAI,IAAI,CA0BzC;AAMD,wBAAgB,cAAc,IAAI,OAAO,CAAC,YAAY,CAAC,CAStD;AAED,yEAAyE;AACzE,wBAAgB,eAAe,IAAI,OAAO,CAAC,YAAY,CAAC,CAIvD"}
1
+ {"version":3,"file":"pickMode.d.ts","sourceRoot":"","sources":["../../src/lib/pickMode.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,GAAG,IAAI,CAAC;AAEvE;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,QAAQ,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;IACzB,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA0BD,sDAAsD;AACtD,wBAAgB,WAAW,IAAI,YAAY,CAE1C;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAG5D;AAaD;;;GAGG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,YAAY,CAAC,CA4B3D;AAyBD,wBAAgB,kBAAkB,IAAI,IAAI,CA0BzC;AAMD;;;GAGG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAAC,YAAY,CAAC,CAYtD;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAAC,YAAY,CAAC,CAMvD"}
@@ -1,13 +1,14 @@
1
1
  import { supabase } from "../integrations/supabase/moduleClient";
2
- /** Fail closed: zolang de Engine niets zegt, tonen we karcontrole niet. */
3
- const HIDDEN = { pickMode: null, showCartControl: false };
4
- let cached = HIDDEN;
2
+ /** Nog nooit met succes opgehaald: werkwijze onbekend. */
3
+ const UNKNOWN = { pickMode: null, showCartControl: false, status: "unknown" };
4
+ const PICK_MODE_TIMEOUT_MS = 8000;
5
+ let cached = UNKNOWN;
5
6
  let inflight = null;
6
7
  const listeners = new Set();
7
8
  function normalize(raw) {
8
9
  const row = Array.isArray(raw) ? raw[0] : raw;
9
10
  if (!row || typeof row !== "object")
10
- return HIDDEN;
11
+ return { ...UNKNOWN };
11
12
  const obj = row;
12
13
  const mode = (obj.pickMode ?? obj.pick_mode);
13
14
  const show = obj.showCartControl ?? obj.show_cart_control;
@@ -16,9 +17,10 @@ function normalize(raw) {
16
17
  pickMode,
17
18
  // Karcontrole uitsluitend bij picken per kar.
18
19
  showCartControl: show === true && pickMode === "cart",
20
+ status: "ok",
19
21
  };
20
22
  }
21
- /** Huidige (gecachete) waarde; standaard verborgen. */
23
+ /** Huidige (gecachete) waarde; standaard onbekend. */
22
24
  export function getPickMode() {
23
25
  return cached;
24
26
  }
@@ -26,24 +28,58 @@ export function subscribePickMode(cb) {
26
28
  listeners.add(cb);
27
29
  return () => listeners.delete(cb);
28
30
  }
31
+ /** Oplopende wachttijd na een mislukte poging, zodat we niet blijven stormen. */
32
+ const RETRY_BACKOFF_MS = [2000, 5000, 15000, 30000];
33
+ let failures = 0;
34
+ let lastFailure = 0;
35
+ function backoffActive() {
36
+ if (!failures)
37
+ return false;
38
+ const wait = RETRY_BACKOFF_MS[Math.min(failures - 1, RETRY_BACKOFF_MS.length - 1)];
39
+ return Date.now() - lastFailure < wait;
40
+ }
41
+ /**
42
+ * Haalt de werkwijze op bij de Engine. Gooit de fout door bij een storing; de
43
+ * laatst succesvol opgehaalde werkwijze blijft dan gewoon gelden.
44
+ */
29
45
  export async function fetchPickMode() {
30
46
  const previous = cached;
31
47
  try {
32
- const { data, error } = await supabase.rpc("logistics_pick_mode");
48
+ let timeoutId;
49
+ const timeout = new Promise((_, reject) => {
50
+ timeoutId = setTimeout(() => reject(new Error("De planning antwoordt niet binnen 8 seconden")), PICK_MODE_TIMEOUT_MS);
51
+ });
52
+ const request = supabase.rpc("logistics_pick_mode");
53
+ const { data, error } = await Promise.race([request, timeout]);
54
+ if (timeoutId)
55
+ clearTimeout(timeoutId);
33
56
  if (error)
34
57
  throw new Error(error.message);
35
58
  cached = normalize(data);
59
+ failures = 0;
60
+ lastFetch = Date.now();
36
61
  }
37
- catch {
38
- cached = HIDDEN;
62
+ catch (err) {
63
+ failures += 1;
64
+ lastFailure = Date.now();
65
+ const message = err instanceof Error ? err.message : "onbekende fout";
66
+ // Laatst bekende werkwijze behouden; alleen de foutmelding aanvullen.
67
+ cached = { ...previous, lastError: message };
68
+ notifyListeners(previous);
69
+ throw err instanceof Error ? err : new Error(message);
39
70
  }
40
- lastFetch = Date.now();
41
- if (previous.pickMode !== cached.pickMode || previous.showCartControl !== cached.showCartControl) {
42
- notifyChanged();
43
- }
44
- listeners.forEach((cb) => cb());
71
+ notifyListeners(previous);
45
72
  return cached;
46
73
  }
74
+ function notifyListeners(previous) {
75
+ const changed = previous.pickMode !== cached.pickMode ||
76
+ previous.showCartControl !== cached.showCartControl ||
77
+ previous.status !== cached.status;
78
+ if (!changed)
79
+ return;
80
+ notifyChanged();
81
+ listeners.forEach((cb) => cb());
82
+ }
47
83
  function notifyChanged() {
48
84
  if (typeof window !== "undefined") {
49
85
  // Zodat de zijbalk van de shell direct meeloopt met de nieuwe werkwijze.
@@ -84,20 +120,33 @@ export function startPickModeWatch() {
84
120
  /** Haalt één keer op en hergebruikt de lopende aanvraag. */
85
121
  let lastFetch = 0;
86
122
  const TTL_MS = 10000;
123
+ /**
124
+ * Veilige variant voor plekken die niet met een fout om kunnen gaan (zoals de
125
+ * zijbalk): faalt stil en geeft de laatst bekende waarde terug.
126
+ */
87
127
  export function ensurePickMode() {
88
128
  startPickModeWatch();
89
129
  if (!inflight && lastFetch > 0 && Date.now() - lastFetch < TTL_MS)
90
130
  return Promise.resolve(cached);
131
+ if (!inflight && backoffActive())
132
+ return Promise.resolve(cached);
91
133
  if (!inflight) {
92
- inflight = fetchPickMode().finally(() => {
134
+ inflight = fetchPickMode()
135
+ .catch(() => cached)
136
+ .finally(() => {
93
137
  inflight = null;
94
138
  });
95
139
  }
96
140
  return inflight;
97
141
  }
98
- /** Forceert een verse ophaalactie (na het wijzigen van de werkwijze). */
142
+ /**
143
+ * Forceert een verse ophaalactie (na het wijzigen van de werkwijze). Gooit de
144
+ * fout door, zodat de aanroeper "storing" kan onderscheiden van "uit".
145
+ */
99
146
  export function refreshPickMode() {
100
147
  lastFetch = 0;
148
+ failures = 0;
101
149
  inflight = null;
102
- return ensurePickMode();
150
+ startPickModeWatch();
151
+ return fetchPickMode();
103
152
  }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Eén gedeelde realtime-verbinding per tabel, met samenvoegen van seintjes.
3
+ *
4
+ * Zonder dit maakte elk scherm-onderdeel een eigen kanaal met een willekeurige
5
+ * naam, en leidde elke wijziging van de Engine direct tot opnieuw ophalen. Bij
6
+ * een reeks wijzigingen (de Engine schrijft tientallen taken achter elkaar)
7
+ * stapelden die opvragingen op en blokkeerde het scherm.
8
+ */
9
+ type Listener = (row: Record<string, unknown>) => void;
10
+ /** Abonneer op wijzigingen van één tabel. Geeft een opzegfunctie terug. */
11
+ export declare function subscribeTable(table: string, listener: Listener): () => void;
12
+ /**
13
+ * Voegt snel opeenvolgende aanroepen samen tot één aanroep. Zonder dit leidt
14
+ * elke afzonderlijke wijziging van de Engine tot een eigen opvraging.
15
+ */
16
+ export declare function debounce(fn: () => void, waitMs?: number): (() => void) & {
17
+ cancel: () => void;
18
+ };
19
+ export {};
20
+ //# sourceMappingURL=realtimeBus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtimeBus.d.ts","sourceRoot":"","sources":["../../src/lib/realtimeBus.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,KAAK,QAAQ,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;AAiCvD,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI,CAO5E;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,MAAM,SAAM,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG;IAAE,MAAM,EAAE,MAAM,IAAI,CAAA;CAAE,CAc5F"}
@@ -0,0 +1,56 @@
1
+ import { supabase } from "../integrations/supabase/moduleClient";
2
+ const entries = new Map();
3
+ function ensureEntry(table) {
4
+ const existing = entries.get(table);
5
+ if (existing)
6
+ return existing;
7
+ const listeners = new Set();
8
+ const channel = supabase
9
+ .channel(`shared-${table}`)
10
+ .on("postgres_changes", { event: "*", schema: "public", table }, (payload) => {
11
+ const row = (payload.new ?? payload.old ?? {});
12
+ for (const listener of listeners)
13
+ listener(row);
14
+ })
15
+ .subscribe();
16
+ const entry = {
17
+ listeners,
18
+ unsubscribe: () => {
19
+ supabase.removeChannel(channel);
20
+ entries.delete(table);
21
+ },
22
+ };
23
+ entries.set(table, entry);
24
+ return entry;
25
+ }
26
+ /** Abonneer op wijzigingen van één tabel. Geeft een opzegfunctie terug. */
27
+ export function subscribeTable(table, listener) {
28
+ const entry = ensureEntry(table);
29
+ entry.listeners.add(listener);
30
+ return () => {
31
+ entry.listeners.delete(listener);
32
+ if (entry.listeners.size === 0)
33
+ entry.unsubscribe();
34
+ };
35
+ }
36
+ /**
37
+ * Voegt snel opeenvolgende aanroepen samen tot één aanroep. Zonder dit leidt
38
+ * elke afzonderlijke wijziging van de Engine tot een eigen opvraging.
39
+ */
40
+ export function debounce(fn, waitMs = 750) {
41
+ let timer = null;
42
+ const run = () => {
43
+ if (timer)
44
+ clearTimeout(timer);
45
+ timer = setTimeout(() => {
46
+ timer = null;
47
+ fn();
48
+ }, waitMs);
49
+ };
50
+ run.cancel = () => {
51
+ if (timer)
52
+ clearTimeout(timer);
53
+ timer = null;
54
+ };
55
+ return run;
56
+ }
@@ -5,6 +5,18 @@
5
5
  * een leeg scherm op. Deze helpers zetten hem altijd om naar een echte Error.
6
6
  */
7
7
  export declare function toServerFnError(e: unknown): Promise<Error>;
8
+ /** Tijdslimiet per aanroep: zonder limiet blijft het scherm eindeloos laden. */
9
+ export declare const SERVER_CALL_TIMEOUT_MS = 15000;
10
+ export interface ServerFnCallOptions {
11
+ timeoutMs?: number;
12
+ maxAttempts?: number;
13
+ }
14
+ /**
15
+ * Roept een server function aan met tijdslimiet en beperkte herpoging, en
16
+ * logt de duur. Een hangende aanroep gaf eerder een scherm dat eeuwig
17
+ * "Laden..." bleef tonen.
18
+ */
19
+ export declare function callServerFn<T>(call: () => Promise<T>, context?: string, options?: ServerFnCallOptions): Promise<T>;
8
20
  /** Roept een server function aan en geeft bij elke fout de fallback terug. */
9
21
  export declare function safeServerCall<T>(call: () => Promise<T>, fallback: T, context?: string): Promise<T>;
10
22
  //# sourceMappingURL=serverFnError.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"serverFnError.d.ts","sourceRoot":"","sources":["../../src/lib/serverFnError.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAUhE;AAED,8EAA8E;AAC9E,wBAAsB,cAAc,CAAC,CAAC,EACpC,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACtB,QAAQ,EAAE,CAAC,EACX,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,CAAC,CAAC,CAQZ"}
1
+ {"version":3,"file":"serverFnError.d.ts","sourceRoot":"","sources":["../../src/lib/serverFnError.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAUhE;AAED,gFAAgF;AAChF,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAK7C,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAUD;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACtB,OAAO,SAAa,EACpB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,CAAC,CAAC,CA+BZ;AAED,8EAA8E;AAC9E,wBAAsB,cAAc,CAAC,CAAC,EACpC,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACtB,QAAQ,EAAE,CAAC,EACX,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,CAAC,CAAC,CAQZ"}
@@ -16,10 +16,57 @@ export async function toServerFnError(e) {
16
16
  return e;
17
17
  return new Error(String(e));
18
18
  }
19
+ /** Tijdslimiet per aanroep: zonder limiet blijft het scherm eindeloos laden. */
20
+ export const SERVER_CALL_TIMEOUT_MS = 15000;
21
+ /** Aantal herpogingen bij tijdelijke fouten (niet bij 401/403). */
22
+ const MAX_ATTEMPTS = 3;
23
+ function isAuthFailure(err) {
24
+ return /^(401|403)\b/.test(err.message) || /Unauthorized|Forbidden/i.test(err.message);
25
+ }
26
+ function sleep(ms) {
27
+ return new Promise((resolve) => setTimeout(resolve, ms));
28
+ }
29
+ /**
30
+ * Roept een server function aan met tijdslimiet en beperkte herpoging, en
31
+ * logt de duur. Een hangende aanroep gaf eerder een scherm dat eeuwig
32
+ * "Laden..." bleef tonen.
33
+ */
34
+ export async function callServerFn(call, context = "onbekend", options = {}) {
35
+ let lastError = new Error("Aanroep mislukt");
36
+ const timeoutMs = options.timeoutMs ?? SERVER_CALL_TIMEOUT_MS;
37
+ const maxAttempts = options.maxAttempts ?? MAX_ATTEMPTS;
38
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
39
+ const startedAt = Date.now();
40
+ let timeoutId;
41
+ try {
42
+ const timeout = new Promise((_, reject) => { timeoutId = setTimeout(() => reject(new Error(`Time-out na ${timeoutMs / 1000} seconden`)), timeoutMs); });
43
+ const result = await Promise.race([call(), timeout]);
44
+ if (timeoutId)
45
+ clearTimeout(timeoutId);
46
+ console.info("[serverFn] call done", { context, attempt, durationMs: Date.now() - startedAt });
47
+ return result;
48
+ }
49
+ catch (e) {
50
+ if (timeoutId)
51
+ clearTimeout(timeoutId);
52
+ lastError = await toServerFnError(e);
53
+ console.warn("[serverFn] call attempt failed", {
54
+ context,
55
+ attempt,
56
+ durationMs: Date.now() - startedAt,
57
+ message: lastError.message,
58
+ });
59
+ if (isAuthFailure(lastError) || attempt === maxAttempts)
60
+ break;
61
+ await sleep(attempt * 500);
62
+ }
63
+ }
64
+ throw lastError;
65
+ }
19
66
  /** Roept een server function aan en geeft bij elke fout de fallback terug. */
20
67
  export async function safeServerCall(call, fallback, context) {
21
68
  try {
22
- return await call();
69
+ return await callServerFn(call, context ?? "onbekend");
23
70
  }
24
71
  catch (e) {
25
72
  const err = await toServerFnError(e);
@@ -0,0 +1,3 @@
1
+ import type { FlowModule } from "@flowselections/core";
2
+ export declare const floridayAfhandelingModule: FlowModule;
3
+ //# sourceMappingURL=moduleDefinition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"moduleDefinition.d.ts","sourceRoot":"","sources":["../src/moduleDefinition.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAgBvD,eAAO,MAAM,yBAAyB,EAAE,UAqBvC,CAAC"}
@@ -0,0 +1,36 @@
1
+ import { CalendarCheck, CalendarRange, ClipboardCheck, PackageCheck } from "lucide-react";
2
+ import { AfhandelingSettingsCard } from "./components/settings/AfhandelingSettingsCard";
3
+ import { VervoerdersSettingsCard } from "./components/settings/VervoerdersSettingsCard";
4
+ import { getPickMode } from "./lib/pickMode";
5
+ const baseChildren = [
6
+ { label: "Pick Agenda", href: "/afhandeling/picking", icon: CalendarCheck },
7
+ { label: "Planning en leveringen", href: "/afhandeling/leveringen", icon: CalendarRange },
8
+ ];
9
+ const cartControlChild = {
10
+ label: "Controle karren",
11
+ href: "/afhandeling/controle",
12
+ icon: ClipboardCheck,
13
+ };
14
+ export const floridayAfhandelingModule = {
15
+ id: "afhandeling",
16
+ name: "Afhandeling",
17
+ version: "1.4.0",
18
+ nav: {
19
+ label: "Afhandeling",
20
+ href: "/afhandeling/picking",
21
+ icon: PackageCheck,
22
+ get children() {
23
+ const info = getPickMode();
24
+ if (info.showCartControl)
25
+ return [...baseChildren, cartControlChild];
26
+ if (info.status !== "ok") {
27
+ return [...baseChildren, { ...cartControlChild, label: "Controle karren (onbekend)" }];
28
+ }
29
+ return baseChildren;
30
+ },
31
+ },
32
+ settingsCards: [
33
+ { component: VervoerdersSettingsCard, order: 80 },
34
+ { component: AfhandelingSettingsCard, order: 100 },
35
+ ],
36
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/floriday-afhandeling-module",
3
- "version": "1.0.43",
3
+ "version": "1.0.45",
4
4
  "private": false,
5
5
  "sideEffects": false,
6
6
  "type": "module",
@@ -48,7 +48,7 @@
48
48
  "devDependencies": {
49
49
  "@cloudflare/vite-plugin": "^1.25.5",
50
50
  "@eslint/js": "^9.32.0",
51
- "@flowselections/core": "1.0.18",
51
+ "@flowselections/core": "1.0.21",
52
52
  "@lovable.dev/vite-tanstack-config": "2.13.1",
53
53
  "@rsbuild/core": "^1.0.0",
54
54
  "@tailwindcss/vite": "^4.2.1",