@flowselections/floriday-afhandeling-module 1.0.63 → 1.0.66

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 (41) hide show
  1. package/dist-lib/components/afhandeling/DagDetailDialog.d.ts +4 -0
  2. package/dist-lib/components/afhandeling/DagDetailDialog.d.ts.map +1 -1
  3. package/dist-lib/components/afhandeling/DagDetailDialog.js +27 -11
  4. package/dist-lib/components/afhandeling/LeveringenPage.d.ts.map +1 -1
  5. package/dist-lib/components/afhandeling/LeveringenPage.js +24 -2
  6. package/dist-lib/components/afhandeling/SignupMailMoments.d.ts +8 -0
  7. package/dist-lib/components/afhandeling/SignupMailMoments.d.ts.map +1 -0
  8. package/dist-lib/components/afhandeling/SignupMailMoments.js +75 -0
  9. package/dist-lib/components/afhandeling/VervoerderForm.d.ts.map +1 -1
  10. package/dist-lib/components/afhandeling/VervoerderForm.js +14 -7
  11. package/dist-lib/components/afhandeling/WeekAgendaView.d.ts.map +1 -1
  12. package/dist-lib/components/afhandeling/WeekAgendaView.js +2 -1
  13. package/dist-lib/components/settings/AfhandelingSettingsCard.d.ts.map +1 -1
  14. package/dist-lib/components/settings/AfhandelingSettingsCard.js +47 -2
  15. package/dist-lib/hooks/useOrderPlanning.d.ts.map +1 -1
  16. package/dist-lib/hooks/useOrderPlanning.js +20 -5
  17. package/dist-lib/integrations/supabase/types.d.ts +359 -0
  18. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  19. package/dist-lib/lib/carrierPlanning.d.ts +4 -0
  20. package/dist-lib/lib/carrierPlanning.d.ts.map +1 -1
  21. package/dist-lib/lib/carrierPlanning.js +14 -8
  22. package/dist-lib/lib/carrierSignup.functions.d.ts +102 -0
  23. package/dist-lib/lib/carrierSignup.functions.d.ts.map +1 -0
  24. package/dist-lib/lib/carrierSignup.functions.js +119 -0
  25. package/dist-lib/lib/carriers.functions.d.ts.map +1 -1
  26. package/dist-lib/lib/carriers.functions.js +13 -6
  27. package/dist-lib/lib/pickWorkdays.d.ts +24 -0
  28. package/dist-lib/lib/pickWorkdays.d.ts.map +1 -0
  29. package/dist-lib/lib/pickWorkdays.js +30 -0
  30. package/dist-lib/lib/pickstationOrders.server.d.ts.map +1 -1
  31. package/dist-lib/lib/pickstationOrders.server.js +9 -3
  32. package/dist-lib/lib/planning.d.ts +19 -0
  33. package/dist-lib/lib/planning.d.ts.map +1 -1
  34. package/dist-lib/lib/planning.js +58 -11
  35. package/dist-lib/lib/serverRoutes/dispatchCarrierSignups.handler.d.ts +1 -1
  36. package/dist-lib/lib/serverRoutes/dispatchCarrierSignups.handler.d.ts.map +1 -1
  37. package/dist-lib/lib/serverRoutes/dispatchCarrierSignups.handler.js +2 -325
  38. package/dist-lib/lib/signupMailText.d.ts +4 -0
  39. package/dist-lib/lib/signupMailText.d.ts.map +1 -0
  40. package/dist-lib/lib/signupMailText.js +37 -0
  41. package/package.json +1 -1
@@ -18,6 +18,10 @@ export interface CarrierOrderTarget {
18
18
  city?: string | null;
19
19
  }
20
20
  export declare function sanitizeWeekdays(input: unknown): number[];
21
+ /**
22
+ * Ophaalmomenten van een vervoerder. Tijden worden nooit aangevuld: een moment
23
+ * zonder aanmeld-, ophaal- of bezorgtijd valt weg en telt niet mee.
24
+ */
21
25
  export declare function normalizePickupMoments(raw: unknown): PickupMoment[];
22
26
  export declare function carrierLeadDaysFromMoments(raw: unknown): number;
23
27
  export declare function normalizePlanningCarriers<T extends CarrierPlanningInfo>(carriers: T[]): T[];
@@ -1 +1 @@
1
- {"version":3,"file":"carrierPlanning.d.ts","sourceRoot":"","sources":["../../src/lib/carrierPlanning.ts"],"names":[],"mappings":"AAEA,OAAO,EAAyB,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAItE,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACpC,iBAAiB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACpC,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACjC,cAAc,CAAC,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC;IAC1C,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAClC,iBAAiB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAUzD;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,YAAY,EAAE,CAanE;AAED,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAK/D;AAED,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,mBAAmB,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAU3F;AAED,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,mBAAmB,EAClE,QAAQ,EAAE,CAAC,EAAE,EACb,MAAM,EAAE,kBAAkB,GACzB,CAAC,GAAG,IAAI,CAiCV"}
1
+ {"version":3,"file":"carrierPlanning.d.ts","sourceRoot":"","sources":["../../src/lib/carrierPlanning.ts"],"names":[],"mappings":"AAEA,OAAO,EAA2C,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAIxF,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACpC,iBAAiB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACpC,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACjC,cAAc,CAAC,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC;IAC1C,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAClC,iBAAiB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAUzD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,YAAY,EAAE,CAenE;AAED,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAK/D;AAED,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,mBAAmB,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAU3F;AAED,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,mBAAmB,EAClE,QAAQ,EAAE,CAAC,EAAE,EACb,MAAM,EAAE,kBAAkB,GACzB,CAAC,GAAG,IAAI,CAiCV"}
@@ -1,6 +1,6 @@
1
1
  import { matchesServiceArea } from "./geo/euServiceAreas";
2
2
  import { countryCode } from "./countries";
3
- import { DEFAULT_PICKUP_MOMENT } from "./planning";
3
+ import { DEFAULT_PICKUP_MOMENT, isCompleteMoment } from "./planning";
4
4
  const DEFAULT_WEEKDAYS = [1, 2, 3, 4, 5];
5
5
  export function sanitizeWeekdays(input) {
6
6
  if (!Array.isArray(input))
@@ -10,20 +10,26 @@ export function sanitizeWeekdays(input) {
10
10
  .filter((n) => Number.isFinite(n) && n >= 1 && n <= 7))).sort((a, b) => a - b);
11
11
  return clean.length > 0 ? clean : DEFAULT_WEEKDAYS;
12
12
  }
13
+ /**
14
+ * Ophaalmomenten van een vervoerder. Tijden worden nooit aangevuld: een moment
15
+ * zonder aanmeld-, ophaal- of bezorgtijd valt weg en telt niet mee.
16
+ */
13
17
  export function normalizePickupMoments(raw) {
14
- if (!Array.isArray(raw) || raw.length === 0)
15
- return [DEFAULT_PICKUP_MOMENT];
16
- return raw.map((m) => {
18
+ if (!Array.isArray(raw))
19
+ return [];
20
+ return raw
21
+ .map((m) => {
17
22
  const r = (m ?? {});
18
23
  return {
19
24
  signup_days: Math.max(0, Math.floor(Number(r.signup_days ?? DEFAULT_PICKUP_MOMENT.signup_days) || 0)),
20
- signup_time: String(r.signup_time ?? DEFAULT_PICKUP_MOMENT.signup_time),
25
+ signup_time: typeof r.signup_time === "string" ? r.signup_time.trim() : "",
21
26
  pickup_days: Math.max(0, Math.floor(Number(r.pickup_days ?? DEFAULT_PICKUP_MOMENT.pickup_days) || 0)),
22
- pickup_time: String(r.pickup_time ?? DEFAULT_PICKUP_MOMENT.pickup_time),
27
+ pickup_time: typeof r.pickup_time === "string" ? r.pickup_time.trim() : "",
23
28
  delivery_days: Math.max(0, Math.floor(Number(r.delivery_days ?? DEFAULT_PICKUP_MOMENT.delivery_days) || 0)),
24
- delivery_time: String(r.delivery_time ?? DEFAULT_PICKUP_MOMENT.delivery_time),
29
+ delivery_time: typeof r.delivery_time === "string" ? r.delivery_time.trim() : "",
25
30
  };
26
- });
31
+ })
32
+ .filter(isCompleteMoment);
27
33
  }
28
34
  export function carrierLeadDaysFromMoments(raw) {
29
35
  return normalizePickupMoments(raw).reduce((max, moment) => Math.max(max, moment.signup_days, moment.pickup_days, moment.delivery_days), 0);
@@ -0,0 +1,102 @@
1
+ export interface SignupCronStatus {
2
+ cronAvailable: boolean;
3
+ active: boolean;
4
+ enabled: boolean;
5
+ reason: string | null;
6
+ timezone: string | null;
7
+ lastRun: {
8
+ started_at: string;
9
+ ok: boolean;
10
+ sent: number;
11
+ reason: string | null;
12
+ error: string | null;
13
+ } | null;
14
+ sentToday: number;
15
+ senderEmail: string | null;
16
+ purpose: string | null;
17
+ healed: boolean;
18
+ syncCode: string | null;
19
+ }
20
+ /** Leest de status; herstelt de taak (alleen beheerder) als hij ontbreekt terwijl de mail aan staat. */
21
+ export declare const getSignupCronStatus: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
22
+ supabase: import("../integrations/supabase/auth-middleware").AuthedSupabaseClient;
23
+ userId: string;
24
+ claims: import("@supabase/auth-js").JwtPayload;
25
+ }, undefined, undefined, undefined>], (d: {
26
+ sync?: boolean;
27
+ } | undefined) => {
28
+ sync: boolean;
29
+ }, Promise<SignupCronStatus>>;
30
+ export interface SignupGroup {
31
+ carrier_id: string;
32
+ carrier_name: string;
33
+ pickup_date: string;
34
+ pickup_time: string;
35
+ signup_time: string;
36
+ pickup_moment_key: string;
37
+ signup_deadline: string;
38
+ pickup_at: string;
39
+ send_at: string;
40
+ order_count: number;
41
+ cart_count: number;
42
+ last: {
43
+ sent_at: string;
44
+ cart_count: number;
45
+ version: number;
46
+ fingerprint_match: boolean;
47
+ } | null;
48
+ }
49
+ export interface SignupOverview {
50
+ ok: boolean;
51
+ enabled: boolean;
52
+ reason: string | null;
53
+ timezone: string | null;
54
+ groups: SignupGroup[];
55
+ unassigned: {
56
+ orderId: string;
57
+ orderNumber: string | null;
58
+ deliveryDate: string;
59
+ }[];
60
+ }
61
+ export declare const getSignupOverview: import("@tanstack/start-client-core").RequiredFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
62
+ supabase: import("../integrations/supabase/auth-middleware").AuthedSupabaseClient;
63
+ userId: string;
64
+ claims: import("@supabase/auth-js").JwtPayload;
65
+ }, undefined, undefined, undefined>], (d: {
66
+ from: string;
67
+ to: string;
68
+ }) => {
69
+ from: string;
70
+ to: string;
71
+ }, Promise<SignupOverview>>;
72
+ interface GroupKey {
73
+ carrierId: string;
74
+ pickupDate: string;
75
+ momentKey: string;
76
+ }
77
+ export interface SignupPreview {
78
+ ok: boolean;
79
+ reason: string | null;
80
+ to: string | null;
81
+ subject: string;
82
+ text: string;
83
+ html: string;
84
+ cart_count: number;
85
+ already_sent: boolean;
86
+ kind: "initial" | "change";
87
+ }
88
+ export declare const previewSignupMail: import("@tanstack/start-client-core").RequiredFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
89
+ supabase: import("../integrations/supabase/auth-middleware").AuthedSupabaseClient;
90
+ userId: string;
91
+ claims: import("@supabase/auth-js").JwtPayload;
92
+ }, undefined, undefined, undefined>], (d: GroupKey) => GroupKey, Promise<SignupPreview>>;
93
+ export declare const sendSignupMailNow: import("@tanstack/start-client-core").RequiredFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
94
+ supabase: import("../integrations/supabase/auth-middleware").AuthedSupabaseClient;
95
+ userId: string;
96
+ claims: import("@supabase/auth-js").JwtPayload;
97
+ }, undefined, undefined, undefined>], (d: GroupKey) => GroupKey, Promise<{
98
+ ok: boolean;
99
+ code: string;
100
+ }>>;
101
+ export {};
102
+ //# sourceMappingURL=carrierSignup.functions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"carrierSignup.functions.d.ts","sourceRoot":"","sources":["../../src/lib/carrierSignup.functions.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;IAC/G,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAoCD,wGAAwG;AACxG,eAAO,MAAM,mBAAmB;;;;0CAEV;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS;;6BAchD,CAAC;AAEL,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;CACnG;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,UAAU,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACrF;AAED,eAAO,MAAM,iBAAiB;;;;0CAER;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE;UAApB,MAAM;QAAM,MAAM;2BAkB5C,CAAC;AAEL,UAAU,QAAQ;IAAG,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE;AAQ/E,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;IACtB,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAC;CAC5B;AAED,eAAO,MAAM,iBAAiB;;;;0CAnBN,QAAQ,sCAwC5B,CAAC;AAEL,eAAO,MAAM,iBAAiB;;;;0CA1CN,QAAQ;QA6CoB,OAAO;UAAQ,MAAM;GAQrE,CAAC"}
@@ -0,0 +1,119 @@
1
+ import { createServerFn } from "@tanstack/react-start";
2
+ import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware";
3
+ async function isBeheerder(supabase, userId) {
4
+ for (const role of ["admin", "superadmin"]) {
5
+ const { data } = await supabase.rpc("has_role", { _user_id: userId, _role: role });
6
+ if (data === true)
7
+ return true;
8
+ }
9
+ return false;
10
+ }
11
+ async function runSync() {
12
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server");
13
+ const admin = supabaseAdmin;
14
+ const { data, error } = await admin.rpc("afhandeling_sync_signup_cron");
15
+ if (error)
16
+ throw new Error(`Automatische controle kon niet worden ingesteld: ${error.message}`);
17
+ const r = (data ?? {});
18
+ return { ok: r.ok === true, code: String(r.code ?? "unknown") };
19
+ }
20
+ async function readStatus(supabase) {
21
+ const { data, error } = await supabase.rpc("afhandeling_signup_cron_status");
22
+ if (error)
23
+ throw new Error(error.message);
24
+ const r = (data ?? {});
25
+ return {
26
+ cronAvailable: r.cron_available === true,
27
+ active: r.active === true,
28
+ enabled: r.enabled === true,
29
+ reason: r.reason ?? null,
30
+ timezone: r.timezone ?? null,
31
+ lastRun: r.last_run ?? null,
32
+ sentToday: Number(r.sent_today ?? 0),
33
+ senderEmail: r.sender_email ?? null,
34
+ purpose: r.purpose ?? null,
35
+ };
36
+ }
37
+ /** Leest de status; herstelt de taak (alleen beheerder) als hij ontbreekt terwijl de mail aan staat. */
38
+ export const getSignupCronStatus = createServerFn({ method: "POST" })
39
+ .middleware([requireSupabaseAuth])
40
+ .inputValidator((d) => ({ sync: Boolean(d?.sync) }))
41
+ .handler(async ({ data, context }) => {
42
+ const supabase = context.supabase;
43
+ let status = await readStatus(supabase);
44
+ let healed = false;
45
+ let syncCode = null;
46
+ const needsSync = data.sync || (status.cronAvailable && status.enabled !== status.active);
47
+ if (needsSync && status.cronAvailable && (await isBeheerder(supabase, context.userId))) {
48
+ const r = await runSync();
49
+ syncCode = r.code;
50
+ healed = !data.sync && r.code === "created";
51
+ status = await readStatus(supabase);
52
+ }
53
+ return { ...status, healed, syncCode };
54
+ });
55
+ export const getSignupOverview = createServerFn({ method: "POST" })
56
+ .middleware([requireSupabaseAuth])
57
+ .inputValidator((d) => {
58
+ const re = /^\d{4}-\d{2}-\d{2}$/;
59
+ if (!re.test(d.from) || !re.test(d.to))
60
+ throw new Error("Ongeldige datum");
61
+ return d;
62
+ })
63
+ .handler(async ({ data, context }) => {
64
+ const supabase = context.supabase;
65
+ const { data: r, error } = await supabase.rpc("carrier_signup_overview", { p_from: data.from, p_to: data.to });
66
+ if (error)
67
+ throw new Error(error.message);
68
+ const o = (r ?? {});
69
+ return {
70
+ ok: o.ok === true,
71
+ enabled: o.enabled === true,
72
+ reason: o.reason ?? null,
73
+ timezone: o.timezone ?? null,
74
+ groups: Array.isArray(o.groups) ? o.groups : [],
75
+ unassigned: Array.isArray(o.unassigned) ? o.unassigned : [],
76
+ };
77
+ });
78
+ const validateKey = (d) => {
79
+ if (!/^[0-9a-f-]{36}$/i.test(d.carrierId) || !/^\d{4}-\d{2}-\d{2}$/.test(d.pickupDate) || !d.momentKey || d.momentKey.length > 80) {
80
+ throw new Error("Ongeldig ophaalmoment");
81
+ }
82
+ return d;
83
+ };
84
+ export const previewSignupMail = createServerFn({ method: "POST" })
85
+ .middleware([requireSupabaseAuth])
86
+ .inputValidator(validateKey)
87
+ .handler(async ({ data, context }) => {
88
+ const supabase = context.supabase;
89
+ const { data: r, error } = await supabase.rpc("carrier_signup_preview", {
90
+ p_carrier_id: data.carrierId, p_pickup_date: data.pickupDate, p_moment_key: data.momentKey,
91
+ });
92
+ if (error)
93
+ throw new Error(error.message);
94
+ const o = (r ?? {});
95
+ return {
96
+ ok: o.ok === true,
97
+ reason: o.reason ?? null,
98
+ to: o.to ?? null,
99
+ subject: String(o.subject ?? ""),
100
+ text: String(o.text ?? ""),
101
+ html: String(o.html ?? ""),
102
+ cart_count: Number(o.cart_count ?? 0),
103
+ already_sent: o.already_sent === true,
104
+ kind: o.kind === "change" ? "change" : "initial",
105
+ };
106
+ });
107
+ export const sendSignupMailNow = createServerFn({ method: "POST" })
108
+ .middleware([requireSupabaseAuth])
109
+ .inputValidator(validateKey)
110
+ .handler(async ({ data, context }) => {
111
+ const supabase = context.supabase;
112
+ const { data: r, error } = await supabase.rpc("carrier_signup_send_now", {
113
+ p_carrier_id: data.carrierId, p_pickup_date: data.pickupDate, p_moment_key: data.momentKey,
114
+ });
115
+ if (error)
116
+ throw new Error(error.message);
117
+ const o = (r ?? {});
118
+ return { ok: o.ok === true, code: String(o.code ?? "unknown") };
119
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"carriers.functions.d.ts","sourceRoot":"","sources":["../../src/lib/carriers.functions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"carriers.functions.d.ts","sourceRoot":"","sources":["../../src/lib/carriers.functions.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { createServerFn } from "@tanstack/react-start";
2
2
  import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware";
3
- import { DEFAULT_PICKUP_MOMENT } from "./planning";
3
+ import { DEFAULT_PICKUP_MOMENT, isCompleteMoment } from "./planning";
4
4
  import { carrierLeadDaysFromMoments } from "./carrierPlanning";
5
5
  function clean(obj) {
6
6
  const out = {};
@@ -21,18 +21,20 @@ function sanitizeWeekdays(input) {
21
21
  .filter((n) => Number.isFinite(n) && n >= 1 && n <= 7))).sort((a, b) => a - b);
22
22
  return clean.length > 0 ? clean : DEFAULT_WEEKDAYS_ARR;
23
23
  }
24
+ /** Leest ophaalmomenten zonder tijden aan te vullen; lege tijden blijven leeg. */
24
25
  function normalizeMoments(raw) {
25
- if (!Array.isArray(raw) || raw.length === 0)
26
- return [DEFAULT_PICKUP_MOMENT];
26
+ if (!Array.isArray(raw))
27
+ return [];
27
28
  return raw.map((m) => {
28
29
  const r = (m ?? {});
30
+ const t = (v) => (typeof v === "string" ? v.trim() : "");
29
31
  return {
30
32
  signup_days: Number(r.signup_days ?? DEFAULT_PICKUP_MOMENT.signup_days),
31
- signup_time: String(r.signup_time ?? DEFAULT_PICKUP_MOMENT.signup_time),
33
+ signup_time: t(r.signup_time),
32
34
  pickup_days: Number(r.pickup_days ?? DEFAULT_PICKUP_MOMENT.pickup_days),
33
- pickup_time: String(r.pickup_time ?? DEFAULT_PICKUP_MOMENT.pickup_time),
35
+ pickup_time: t(r.pickup_time),
34
36
  delivery_days: Number(r.delivery_days ?? DEFAULT_PICKUP_MOMENT.delivery_days),
35
- delivery_time: String(r.delivery_time ?? DEFAULT_PICKUP_MOMENT.delivery_time),
37
+ delivery_time: t(r.delivery_time),
36
38
  };
37
39
  });
38
40
  }
@@ -109,6 +111,11 @@ export const upsertCarrier = createServerFn({ method: "POST" })
109
111
  throw new Error("Bedrijfsnaam is verplicht");
110
112
  }
111
113
  const moments = normalizeMoments(carrier.pickup_moments);
114
+ if (moments.length === 0)
115
+ throw new Error("Voeg minimaal één ophaalmoment toe.");
116
+ if (!moments.every(isCompleteMoment)) {
117
+ throw new Error("Vul bij elk ophaalmoment alle tijden in.");
118
+ }
112
119
  const leadDays = carrierLeadDaysFromMoments(moments);
113
120
  const signupDays = moments.reduce((max, moment) => Math.max(max, moment.signup_days), 0);
114
121
  const payload = clean({
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Laadt de pickwerkdagen uit de Werktijden (afh_pick_workdays →
3
+ * logistics_is_pick_workday, inclusief gesloten afwijkende dagen) en zet ze
4
+ * als enige werkdagkalender voor de planning. Ontbreekt de bron, dan blijft de
5
+ * kalender leeg en wordt er niet gepland (fail closed).
6
+ */
7
+ type RpcClient = {
8
+ rpc: (fn: string, args: Record<string, unknown>) => PromiseLike<{
9
+ data: unknown;
10
+ error: {
11
+ message: string;
12
+ } | null;
13
+ }>;
14
+ };
15
+ export declare function pickWorkdayRange(now?: Date): {
16
+ from: string;
17
+ to: string;
18
+ };
19
+ export declare function loadPickWorkdays(client: RpcClient): Promise<{
20
+ available: boolean;
21
+ count: number;
22
+ }>;
23
+ export {};
24
+ //# sourceMappingURL=pickWorkdays.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pickWorkdays.d.ts","sourceRoot":"","sources":["../../src/lib/pickWorkdays.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,KAAK,SAAS,GAAG;IACf,GAAG,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,WAAW,CAAC;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;CACvH,CAAC;AASF,wBAAgB,gBAAgB,CAAC,GAAG,OAAa,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAM/E;AAED,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAexG"}
@@ -0,0 +1,30 @@
1
+ import { setPickWorkdayCalendar } from "./planning";
2
+ function iso(d) {
3
+ const y = d.getFullYear();
4
+ const m = String(d.getMonth() + 1).padStart(2, "0");
5
+ const day = String(d.getDate()).padStart(2, "0");
6
+ return `${y}-${m}-${day}`;
7
+ }
8
+ export function pickWorkdayRange(now = new Date()) {
9
+ const from = new Date(now);
10
+ from.setDate(from.getDate() - 120);
11
+ const to = new Date(now);
12
+ to.setDate(to.getDate() + 270);
13
+ return { from: iso(from), to: iso(to) };
14
+ }
15
+ export async function loadPickWorkdays(client) {
16
+ const { from, to } = pickWorkdayRange();
17
+ const { data, error } = await client.rpc("afh_pick_workdays", { p_from: from, p_to: to });
18
+ if (error) {
19
+ setPickWorkdayCalendar(null);
20
+ throw new Error(error.message);
21
+ }
22
+ const res = (data ?? {});
23
+ if (!res.available) {
24
+ setPickWorkdayCalendar(null);
25
+ return { available: false, count: 0 };
26
+ }
27
+ const workdays = Array.isArray(res.workdays) ? res.workdays : [];
28
+ setPickWorkdayCalendar({ workdays, from, to });
29
+ return { available: true, count: workdays.length };
30
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"pickstationOrders.server.d.ts","sourceRoot":"","sources":["../../src/lib/pickstationOrders.server.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAgBrD,OAAO,KAAK,EAAmB,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AA8P5E,wBAAsB,2BAA2B,CAC/C,MAAM,CAAC,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,GACvC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAyW7B;AAED,wBAAsB,6BAA6B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7E,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,IAAI,CAAC,CAER;AAED,kFAAkF;AAClF,wBAAsB,4BAA4B,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC;IAChF,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,IAAI,CAAC,CAGR;AAED,wBAAsB,aAAa,CACjC,WAAW,EAAE,UAAU,GAAG,QAAQ,EAClC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,WAAW,EACjE,MAAM,CAAC,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,GACvC,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,SAAS,EAAE,MAAM,EAAE,EACnB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,MAAM,CAAC,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,GACvC,OAAO,CAAC,IAAI,CAAC,CA4Bf"}
1
+ {"version":3,"file":"pickstationOrders.server.d.ts","sourceRoot":"","sources":["../../src/lib/pickstationOrders.server.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAgBrD,OAAO,KAAK,EAAmB,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AA8P5E,wBAAsB,2BAA2B,CAC/C,MAAM,CAAC,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,GACvC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA8W7B;AAED,wBAAsB,6BAA6B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7E,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,IAAI,CAAC,CAER;AAED,kFAAkF;AAClF,wBAAsB,4BAA4B,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC;IAChF,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,IAAI,CAAC,CAGR;AAED,wBAAsB,aAAa,CACjC,WAAW,EAAE,UAAU,GAAG,QAAQ,EAClC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,WAAW,EACjE,MAAM,CAAC,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,GACvC,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,SAAS,EAAE,MAAM,EAAE,EACnB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,MAAM,CAAC,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,GACvC,OAAO,CAAC,IAAI,CAAC,CA4Bf"}
@@ -2,8 +2,9 @@
2
2
  // Uses the publishable-key Supabase client + SECURITY DEFINER RPCs so the
3
3
  // public Pickstation works without a service-role key.
4
4
  import { createClient } from "@supabase/supabase-js";
5
- import { calculateOrderPlanning, distributePickDates, DEFAULT_PICKUP_MOMENT, } from "./planning";
5
+ import { calculateOrderPlanning, distributePickDates, } from "./planning";
6
6
  import { normalizePickupMoments, normalizePlanningCarriers, sanitizeWeekdays, selectCarrierForTarget, } from "./carrierPlanning";
7
+ import { loadPickWorkdays } from "./pickWorkdays";
7
8
  const LOG_SCOPE = "[pickstation]";
8
9
  function debug(action, details) {
9
10
  console.info(`${LOG_SCOPE} ${action}`, {
@@ -164,6 +165,11 @@ export async function loadTodaysPickstationOrders(client) {
164
165
  const [settingsRes, ordersRes] = await Promise.all([
165
166
  supabase.rpc("pickstation_get_planning_context"),
166
167
  supabase.rpc("pickstation_get_orders_raw"),
168
+ // Werkdagen uitsluitend uit de Werktijden; mislukt dit, dan wordt er niet gepland.
169
+ loadPickWorkdays(supabase).catch((e) => {
170
+ warn("pick workdays load failed", { message: e instanceof Error ? e.message : String(e) });
171
+ return null;
172
+ }),
167
173
  ]);
168
174
  if (settingsRes.error) {
169
175
  errorLog("settings rpc failed", new Error(settingsRes.error.message), {
@@ -236,7 +242,7 @@ export async function loadTodaysPickstationOrders(client) {
236
242
  province: pickProvince(data),
237
243
  city: pickCity(data),
238
244
  });
239
- const pickupMoments = carrier ? normalizePickupMoments(carrier.pickup_moments) : [DEFAULT_PICKUP_MOMENT];
245
+ const pickupMoments = carrier ? normalizePickupMoments(carrier.pickup_moments) : [];
240
246
  const planning = calculateOrderPlanning(deliveryDate, {
241
247
  pickupMoments,
242
248
  pickDurationWorkdays: pickDays,
@@ -286,7 +292,7 @@ export async function loadTodaysPickstationOrders(client) {
286
292
  province: target.province,
287
293
  city: target.city,
288
294
  });
289
- const pickupMoments = carrier ? normalizePickupMoments(carrier.pickup_moments) : [DEFAULT_PICKUP_MOMENT];
295
+ const pickupMoments = carrier ? normalizePickupMoments(carrier.pickup_moments) : [];
290
296
  const planning = calculateOrderPlanning(row.delivery_date, {
291
297
  pickupMoments,
292
298
  pickDurationWorkdays: pickDays,
@@ -20,7 +20,21 @@ export interface PickupMoment {
20
20
  /** Bezorgtijd ("HH:MM"). */
21
21
  delivery_time: string;
22
22
  }
23
+ /**
24
+ * Leeg sjabloon voor een nieuw ophaalmoment: werkdagen ingevuld, tijden leeg.
25
+ * Er wordt nooit stil een tijd aangenomen; een moment zonder tijden telt niet mee.
26
+ */
23
27
  export declare const DEFAULT_PICKUP_MOMENT: PickupMoment;
28
+ export declare function isValidTime(t: string | null | undefined): boolean;
29
+ /** Een ophaalmoment telt pas mee als aanmeld-, ophaal- en bezorgtijd zijn gekozen. */
30
+ export declare function isCompleteMoment(m: PickupMoment): boolean;
31
+ /** Zet de pickwerkdagen (JJJJ-MM-DD) voor een periode; null wist de kalender. */
32
+ export declare function setPickWorkdayCalendar(cal: {
33
+ workdays: string[];
34
+ from: string;
35
+ to: string;
36
+ } | null): void;
37
+ export declare function isPickWorkdayCalendarReady(): boolean;
24
38
  export interface PlanningRules {
25
39
  /** Beschikbare ophaalmomenten — minimaal één. */
26
40
  pickupMoments: PickupMoment[];
@@ -87,6 +101,11 @@ export declare function normalizeWeekdays(days: number[] | undefined | null): nu
87
101
  * of naar de toekomst opschuiven.
88
102
  */
89
103
  export declare function snapToAllowedDay(date: Date, allowedWeekdays: number[] | undefined, direction: "backward" | "forward", holidays?: Date[]): Date;
104
+ /**
105
+ * Werkdag volgens de Werktijden (inclusief gesloten afwijkende dagen).
106
+ * Geen kalender of datum buiten de geladen periode → geen werkdag (fail closed).
107
+ * `holidays` blijft alleen voor oude aanroepers; niemand geeft hem nog mee.
108
+ */
90
109
  export declare function isWorkday(d: Date, holidays?: Date[]): boolean;
91
110
  export declare function subtractWorkdays(date: Date, n: number, holidays?: Date[]): Date;
92
111
  export declare function addWorkdays(date: Date, n: number, holidays?: Date[]): Date;
@@ -1 +1 @@
1
- {"version":3,"file":"planning.d.ts","sourceRoot":"","sources":["../../src/lib/planning.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"planning.d.ts","sourceRoot":"","sources":["../../src/lib/planning.ts"],"names":[],"mappings":"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"}
@@ -6,14 +6,44 @@
6
6
  * ophalen, bezorgen) hoeveel werkdagen vóór de bezorgdag de stap plaatsvindt
7
7
  * en op welk tijdstip.
8
8
  */
9
+ /**
10
+ * Leeg sjabloon voor een nieuw ophaalmoment: werkdagen ingevuld, tijden leeg.
11
+ * Er wordt nooit stil een tijd aangenomen; een moment zonder tijden telt niet mee.
12
+ */
9
13
  export const DEFAULT_PICKUP_MOMENT = {
10
14
  signup_days: 2,
11
- signup_time: "16:00",
15
+ signup_time: "",
12
16
  pickup_days: 1,
13
- pickup_time: "17:30",
17
+ pickup_time: "",
14
18
  delivery_days: 0,
15
- delivery_time: "08:00",
19
+ delivery_time: "",
16
20
  };
21
+ const TIME_RE = /^\d{1,2}:\d{2}$/;
22
+ export function isValidTime(t) {
23
+ return typeof t === "string" && TIME_RE.test(t.trim());
24
+ }
25
+ /** Een ophaalmoment telt pas mee als aanmeld-, ophaal- en bezorgtijd zijn gekozen. */
26
+ export function isCompleteMoment(m) {
27
+ return isValidTime(m.signup_time) && isValidTime(m.pickup_time) && isValidTime(m.delivery_time);
28
+ }
29
+ // ---------------------------------------------------------------------------
30
+ // Werkdagen: één bron, de Werktijden (logistics_is_pick_workday via
31
+ // afh_pick_workdays). Zonder geladen kalender wordt er niet gepland.
32
+ // ---------------------------------------------------------------------------
33
+ let pickWorkdayCalendar = null;
34
+ function dateKey(d) {
35
+ const y = d.getFullYear();
36
+ const m = String(d.getMonth() + 1).padStart(2, "0");
37
+ const day = String(d.getDate()).padStart(2, "0");
38
+ return `${y}-${m}-${day}`;
39
+ }
40
+ /** Zet de pickwerkdagen (JJJJ-MM-DD) voor een periode; null wist de kalender. */
41
+ export function setPickWorkdayCalendar(cal) {
42
+ pickWorkdayCalendar = cal ? { days: new Set(cal.workdays), from: cal.from, to: cal.to } : null;
43
+ }
44
+ export function isPickWorkdayCalendarReady() {
45
+ return pickWorkdayCalendar !== null;
46
+ }
17
47
  /** ISO-dagnummer 1=ma … 7=zo van een Date. */
18
48
  export function isoWeekday(d) {
19
49
  const g = d.getDay();
@@ -51,9 +81,18 @@ function sameDay(a, b) {
51
81
  a.getMonth() === b.getMonth() &&
52
82
  a.getDate() === b.getDate());
53
83
  }
84
+ /**
85
+ * Werkdag volgens de Werktijden (inclusief gesloten afwijkende dagen).
86
+ * Geen kalender of datum buiten de geladen periode → geen werkdag (fail closed).
87
+ * `holidays` blijft alleen voor oude aanroepers; niemand geeft hem nog mee.
88
+ */
54
89
  export function isWorkday(d, holidays) {
55
- const day = d.getDay();
56
- if (day === 0 || day === 6)
90
+ if (!pickWorkdayCalendar)
91
+ return false;
92
+ const key = dateKey(d);
93
+ if (key < pickWorkdayCalendar.from || key > pickWorkdayCalendar.to)
94
+ return false;
95
+ if (!pickWorkdayCalendar.days.has(key))
57
96
  return false;
58
97
  if (holidays && holidays.some((h) => sameDay(h, d)))
59
98
  return false;
@@ -67,7 +106,8 @@ function normalize(d) {
67
106
  export function subtractWorkdays(date, n, holidays) {
68
107
  const out = normalize(date);
69
108
  let remaining = Math.max(0, Math.floor(n));
70
- while (remaining > 0) {
109
+ let guard = 0;
110
+ while (remaining > 0 && guard++ < 60) {
71
111
  out.setDate(out.getDate() - 1);
72
112
  if (isWorkday(out, holidays))
73
113
  remaining--;
@@ -77,7 +117,8 @@ export function subtractWorkdays(date, n, holidays) {
77
117
  export function addWorkdays(date, n, holidays) {
78
118
  const out = normalize(date);
79
119
  let remaining = Math.max(0, Math.floor(n));
80
- while (remaining > 0) {
120
+ let guard = 0;
121
+ while (remaining > 0 && guard++ < 60) {
81
122
  out.setDate(out.getDate() + 1);
82
123
  if (isWorkday(out, holidays))
83
124
  remaining--;
@@ -137,7 +178,7 @@ export function getSignupDeadline(deliveryDate, moment, holidays) {
137
178
  */
138
179
  export function selectPickupMoment(deliveryDate, orderCreatedAt, moments, holidays) {
139
180
  if (moments.length === 0)
140
- return DEFAULT_PICKUP_MOMENT;
181
+ return DEFAULT_PICKUP_MOMENT; // aanroepers controleren vooraf
141
182
  if (moments.length === 1)
142
183
  return moments[0];
143
184
  const now = orderCreatedAt ?? new Date();
@@ -160,7 +201,7 @@ export function calculateEarliestDeliveryDate(rules) {
160
201
  ? rules.orderCreatedAt
161
202
  : new Date(rules.orderCreatedAt);
162
203
  const base = Number.isNaN(orderCreated.getTime()) ? new Date() : orderCreated;
163
- const moments = rules.pickupMoments.length > 0 ? rules.pickupMoments : [DEFAULT_PICKUP_MOMENT];
204
+ const moments = rules.pickupMoments.filter(isCompleteMoment);
164
205
  const leadDays = moments.reduce((max, moment) => Math.max(max, moment.signup_days, moment.pickup_days, moment.delivery_days), 0);
165
206
  const workdaysToAdd = Math.max(0, Math.floor(rules.pickDurationWorkdays)) + leadDays;
166
207
  const raw = addWorkdays(base, workdaysToAdd, rules.holidays);
@@ -185,8 +226,14 @@ export function calculateOrderPlanning(deliveryDate, rules) {
185
226
  const d = typeof deliveryDate === "string" ? new Date(deliveryDate) : deliveryDate;
186
227
  if (Number.isNaN(d.getTime()))
187
228
  return null;
229
+ // Zonder werkdagen uit de Werktijden of zonder volledig ingevuld ophaalmoment
230
+ // wordt er niet gepland: nooit stil terugvallen op ma–vr of een standaardtijd.
231
+ if (!isPickWorkdayCalendarReady())
232
+ return null;
188
233
  const requested = normalize(d);
189
- const moments = rules.pickupMoments.length > 0 ? rules.pickupMoments : [DEFAULT_PICKUP_MOMENT];
234
+ const moments = rules.pickupMoments.filter(isCompleteMoment);
235
+ if (moments.length === 0)
236
+ return null;
190
237
  const orderCreated = rules.orderCreatedAt == null
191
238
  ? null
192
239
  : rules.orderCreatedAt instanceof Date
@@ -299,7 +346,7 @@ export function distributePickDates(items, options) {
299
346
  const carts = getCarts(item);
300
347
  // Kandidaten in chronologische volgorde — alleen toegestane pickdagen.
301
348
  const allowedSet = new Set(normalizeWeekdays(options.pickWeekdays));
302
- const isAllowed = (d) => allowedSet.has(isoWeekday(d)) && !(options.holidays?.some((h) => sameDay(h, d)));
349
+ const isAllowed = (d) => allowedSet.has(isoWeekday(d)) && isWorkday(d, options.holidays);
303
350
  const candidates = [];
304
351
  if (distribute) {
305
352
  let cursor = deadline;
@@ -1,3 +1,3 @@
1
1
  import type { SupabaseClient } from "@supabase/supabase-js";
2
- export declare function handleDispatchCarrierSignups(request: Request, injected?: SupabaseClient): Promise<Response>;
2
+ export declare function handleDispatchCarrierSignups(_request: Request, _injected?: SupabaseClient): Promise<Response>;
3
3
  //# sourceMappingURL=dispatchCarrierSignups.handler.d.ts.map