@flowselections/floriday-voorraad 1.0.32 → 1.0.34

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 (62) hide show
  1. package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
  2. package/dist-lib/components/voorraad/ArtikelWizard.js +11 -5
  3. package/dist-lib/components/voorraad/ModuleHealthCard.d.ts +2 -0
  4. package/dist-lib/components/voorraad/ModuleHealthCard.d.ts.map +1 -0
  5. package/dist-lib/components/voorraad/ModuleHealthCard.js +40 -0
  6. package/dist-lib/components/voorraad/StockBulkButtons.d.ts.map +1 -1
  7. package/dist-lib/components/voorraad/StockBulkButtons.js +5 -2
  8. package/dist-lib/components/voorraad/StockPushMonitor.d.ts +4 -0
  9. package/dist-lib/components/voorraad/StockPushMonitor.d.ts.map +1 -0
  10. package/dist-lib/components/voorraad/StockPushMonitor.js +110 -0
  11. package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
  12. package/dist-lib/components/voorraad/VoorraadPage.js +2 -1
  13. package/dist-lib/components/voorraad/VoorraadStockCard.d.ts.map +1 -1
  14. package/dist-lib/components/voorraad/VoorraadStockCard.js +10 -5
  15. package/dist-lib/hooks/useStockPushJobs.d.ts +23 -0
  16. package/dist-lib/hooks/useStockPushJobs.d.ts.map +1 -0
  17. package/dist-lib/hooks/useStockPushJobs.js +86 -0
  18. package/dist-lib/hooks/useTradeItemStock.d.ts +12 -0
  19. package/dist-lib/hooks/useTradeItemStock.d.ts.map +1 -1
  20. package/dist-lib/hooks/useTradeItemStock.js +22 -0
  21. package/dist-lib/hooks/useVoorraadData.d.ts.map +1 -1
  22. package/dist-lib/hooks/useVoorraadData.js +11 -3
  23. package/dist-lib/index.d.ts +1 -0
  24. package/dist-lib/index.d.ts.map +1 -1
  25. package/dist-lib/index.js +6 -1
  26. package/dist-lib/integrations/supabase/auth-middleware.d.ts +410 -4
  27. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  28. package/dist-lib/integrations/supabase/client.d.ts +410 -4
  29. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  30. package/dist-lib/integrations/supabase/client.server.d.ts +410 -4
  31. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  32. package/dist-lib/integrations/supabase/types.d.ts +411 -5
  33. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  34. package/dist-lib/integrations/supabase/types.js +12 -1
  35. package/dist-lib/lib/centralSecrets.server.js +1 -1
  36. package/dist-lib/lib/floricode-required-features.functions.d.ts +410 -4
  37. package/dist-lib/lib/floricode-required-features.functions.d.ts.map +1 -1
  38. package/dist-lib/lib/floriday-customer-offer.functions.d.ts +410 -4
  39. package/dist-lib/lib/floriday-customer-offer.functions.d.ts.map +1 -1
  40. package/dist-lib/lib/floriday-env.functions.d.ts +820 -8
  41. package/dist-lib/lib/floriday-env.functions.d.ts.map +1 -1
  42. package/dist-lib/lib/floriday-gateway.functions.d.ts +2460 -24
  43. package/dist-lib/lib/floriday-gateway.functions.d.ts.map +1 -1
  44. package/dist-lib/lib/floriday-gateway.server.d.ts +14 -0
  45. package/dist-lib/lib/floriday-gateway.server.d.ts.map +1 -1
  46. package/dist-lib/lib/floriday-gateway.server.js +62 -10
  47. package/dist-lib/lib/floriday-vbn-required.functions.d.ts +820 -8
  48. package/dist-lib/lib/floriday-vbn-required.functions.d.ts.map +1 -1
  49. package/dist-lib/lib/floriday-warehouse.functions.d.ts +1230 -12
  50. package/dist-lib/lib/floriday-warehouse.functions.d.ts.map +1 -1
  51. package/dist-lib/lib/floriday-writes.functions.d.ts +820 -8
  52. package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
  53. package/dist-lib/lib/module-health.functions.d.ts +41725 -0
  54. package/dist-lib/lib/module-health.functions.d.ts.map +1 -0
  55. package/dist-lib/lib/module-health.functions.js +173 -0
  56. package/dist-lib/lib/module-runtime.server.d.ts +8 -1
  57. package/dist-lib/lib/module-runtime.server.d.ts.map +1 -1
  58. package/dist-lib/lib/module-runtime.server.js +103 -12
  59. package/dist-lib/lib/stock-sync.functions.d.ts +21302 -31
  60. package/dist-lib/lib/stock-sync.functions.d.ts.map +1 -1
  61. package/dist-lib/lib/stock-sync.functions.js +46 -3
  62. package/package.json +2 -2
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Statusoverzicht van de module: klopt de configuratie, draaien de
3
+ * achtergrondtaken en loopt de voorraadsync?
4
+ *
5
+ * Bewust één server-functie die alles samenvat, zodat de instellingenpagina
6
+ * met één blik laat zien wat er in déze shell nog ontbreekt.
7
+ */
8
+ import { createServerFn } from "@tanstack/react-start";
9
+ import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware";
10
+ export const getModuleHealth = createServerFn({ method: "GET" })
11
+ .middleware([requireSupabaseAuth])
12
+ .handler(async () => {
13
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server");
14
+ const admin = supabaseAdmin;
15
+ const checks = [];
16
+ // --- Configuratie in de database -------------------------------------
17
+ const { data: cfgRows } = await admin
18
+ .from("module_runtime_config")
19
+ .select("key,value,updated_at");
20
+ const cfg = new Map((cfgRows ?? []).map((r) => [r.key, r]));
21
+ const baseUrl = cfg.get("voorraad_base_url")?.value ?? "";
22
+ checks.push({
23
+ id: "base_url",
24
+ label: "Adres van deze module",
25
+ status: baseUrl ? "ok" : "error",
26
+ detail: baseUrl || "Nog niet vastgelegd.",
27
+ hint: baseUrl
28
+ ? undefined
29
+ : "Open een pagina van de gepubliceerde module; het adres wordt dan automatisch vastgelegd.",
30
+ });
31
+ const hookKey = cfg.get("voorraad_hook_apikey")?.value ?? "";
32
+ checks.push({
33
+ id: "hook_apikey",
34
+ label: "Sleutel voor achtergrondtaken",
35
+ status: hookKey ? "ok" : "error",
36
+ detail: hookKey ? "Aanwezig" : "Ontbreekt",
37
+ hint: hookKey ? undefined : "Controleer of SUPABASE_PUBLISHABLE_KEY in deze omgeving is ingesteld.",
38
+ });
39
+ const processor = cfg.get("stock_queue_processor_base_url")?.value ?? "";
40
+ checks.push({
41
+ id: "processor",
42
+ label: "Verwerker van de voorraadwachtrij",
43
+ status: processor ? "ok" : "warn",
44
+ detail: processor || "Niet ingesteld; de module verwerkt de wachtrij dan zelf.",
45
+ });
46
+ // --- Koppeling met de Authenticatiemodule ------------------------------
47
+ let activeEnvironment = null;
48
+ try {
49
+ const { describeGatewayResolution } = await import("./floriday-gateway.server");
50
+ const info = await describeGatewayResolution();
51
+ const bron = info.addressSource === "override"
52
+ ? "handmatig ingesteld adres"
53
+ : info.addressSource === "central"
54
+ ? "automatisch via de centrale authenticatiemodule"
55
+ : "onbekend";
56
+ checks.push({
57
+ id: "gateway",
58
+ label: "Koppeling Authenticatiemodule",
59
+ status: info.addressSource === "onbekend" || info.secretSource === "onbekend"
60
+ ? "error"
61
+ : info.mismatch
62
+ ? "warn"
63
+ : "ok",
64
+ detail: info.addressSource === "onbekend"
65
+ ? "Geen adres gevonden; Floriday-acties zullen mislukken."
66
+ : `${info.address} (${bron})`,
67
+ hint: info.mismatch
68
+ ? "Let op: het handmatig ingestelde adres wijkt af van het adres dat de centrale " +
69
+ `module doorgeeft (${info.centralAddress}). Het handmatige adres krijgt voorrang. ` +
70
+ "Verwijder het handmatige adres zodra deze omgeving verhuist."
71
+ : (info.error ?? undefined),
72
+ });
73
+ }
74
+ catch (e) {
75
+ checks.push({
76
+ id: "gateway",
77
+ label: "Koppeling Authenticatiemodule",
78
+ status: "error",
79
+ detail: e?.message ?? "Kan de koppeling niet vaststellen.",
80
+ });
81
+ }
82
+ // --- Floriday-koppeling ----------------------------------------------
83
+ try {
84
+ const { data: conn } = await admin.rpc("get_active_floriday_connection_public");
85
+ const row = Array.isArray(conn) ? conn[0] : conn;
86
+ activeEnvironment = row?.environment ?? null;
87
+ checks.push({
88
+ id: "floriday",
89
+ label: "Floriday-koppeling",
90
+ status: row?.id ? "ok" : "error",
91
+ detail: row?.id
92
+ ? `Actief (${row.environment ?? "onbekende omgeving"})`
93
+ : "Geen actieve koppeling gevonden.",
94
+ hint: row?.id ? undefined : "Koppel Floriday in de authenticatiemodule.",
95
+ });
96
+ }
97
+ catch (e) {
98
+ checks.push({
99
+ id: "floriday",
100
+ label: "Floriday-koppeling",
101
+ status: "error",
102
+ detail: e?.message ?? "Kan de koppeling niet uitlezen.",
103
+ });
104
+ }
105
+ checks.push({
106
+ id: "environment",
107
+ label: "Actieve omgeving",
108
+ status: activeEnvironment ? "ok" : "warn",
109
+ detail: activeEnvironment
110
+ ? activeEnvironment === "live"
111
+ ? "Live — wijzigingen komen echt in Floriday."
112
+ : "Proefomgeving (staging)."
113
+ : "Nog geen omgeving gekozen.",
114
+ hint: activeEnvironment
115
+ ? "De authenticatiemodule moet voor deze omgeving inloggegevens hebben; anders " +
116
+ "melden voorraadwijzigingen dat er geen koppeling voor deze omgeving is."
117
+ : undefined,
118
+ });
119
+ // --- Voorraadwachtrij --------------------------------------------------
120
+ const { data: queueRows } = await admin
121
+ .from("floriday_continuous_stock_queue")
122
+ .select("status,created_at,last_error")
123
+ .in("status", ["pending", "processing", "failed"])
124
+ .order("created_at", { ascending: true })
125
+ .limit(1000);
126
+ const rows = (queueRows ?? []);
127
+ const failed = rows.filter((r) => r.status === "failed").length;
128
+ const pending = rows.filter((r) => r.status !== "failed").length;
129
+ const oldest = rows[0]?.created_at;
130
+ const stale = oldest && Date.now() - new Date(oldest).getTime() > 30 * 60000;
131
+ checks.push({
132
+ id: "queue",
133
+ label: "Voorraadwachtrij",
134
+ status: failed > 0 || stale ? "warn" : "ok",
135
+ detail: rows.length === 0
136
+ ? "Leeg — alles is verstuurd."
137
+ : `${pending} wachtend, ${failed} mislukt${oldest ? `; oudste van ${new Date(oldest).toLocaleString("nl-NL")}` : ""}.`,
138
+ hint: rows.find((r) => r.last_error)?.last_error ?? undefined,
139
+ });
140
+ // --- Geplande taken ----------------------------------------------------
141
+ try {
142
+ const { data: jobs } = await admin.rpc("get_floriday_cron_jobs");
143
+ const list = (jobs ?? []);
144
+ const active = list.filter((j) => j.active !== false).length;
145
+ checks.push({
146
+ id: "cron",
147
+ label: "Geplande achtergrondtaken",
148
+ status: active > 0 ? "ok" : "error",
149
+ detail: active > 0
150
+ ? `${active} taken actief.`
151
+ : "Geen actieve taken gevonden in de database.",
152
+ });
153
+ }
154
+ catch {
155
+ checks.push({
156
+ id: "cron",
157
+ label: "Geplande achtergrondtaken",
158
+ status: "warn",
159
+ detail: "Status van de geplande taken kon niet worden opgehaald.",
160
+ });
161
+ }
162
+ return { checkedAt: new Date().toISOString(), checks };
163
+ });
164
+ /** Geeft vastgelopen wachtrijregels handmatig vrij. */
165
+ export const recoverStockQueue = createServerFn({ method: "POST" })
166
+ .middleware([requireSupabaseAuth])
167
+ .handler(async () => {
168
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server");
169
+ const { data, error } = await supabaseAdmin.rpc("reset_stalled_floriday_stock_queue", { p_include_failed: true });
170
+ if (error)
171
+ throw new Error(error.message);
172
+ return { restored: Number(data ?? 0) };
173
+ });
@@ -23,7 +23,14 @@ export interface ModuleRegistrationResult {
23
23
  changed: boolean;
24
24
  reason?: string;
25
25
  }
26
- /** Schrijft basis-URL + hook-apikey weg (idempotent, schrijft alleen bij wijziging). */
26
+ /**
27
+ * Schrijft basis-URL + hook-apikey weg (idempotent, schrijft alleen bij wijziging).
28
+ *
29
+ * Zelfherstellend: de reeds geregistreerde host wordt eerst gecontroleerd.
30
+ * Werkt die nog én is hij minstens zo stabiel als de nieuwe kandidaat, dan
31
+ * blijft hij staan. Is hij stuk (verlopen preview, andere shell), dan neemt de
32
+ * nieuwe kandidaat het over.
33
+ */
27
34
  export declare function registerModuleRuntime(request?: Request | null): Promise<ModuleRegistrationResult>;
28
35
  /**
29
36
  * Fire-and-forget variant met throttle; veilig om bij élk request aan te roepen.
@@ -1 +1 @@
1
- {"version":3,"file":"module-runtime.server.d.ts","sourceRoot":"","sources":["../../src/lib/module-runtime.server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AA2FH;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,GACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMxB;AAkCD,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wFAAwF;AACxF,wBAAsB,qBAAqB,CACzC,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,GACvB,OAAO,CAAC,wBAAwB,CAAC,CAyBnC;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI,CAsB5E"}
1
+ {"version":3,"file":"module-runtime.server.d.ts","sourceRoot":"","sources":["../../src/lib/module-runtime.server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAqJH;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,GACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMxB;AAkCD,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,GACvB,OAAO,CAAC,wBAAwB,CAAC,CAuDnC;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI,CAsB5E"}
@@ -76,15 +76,69 @@ async function servesModule(baseUrl) {
76
76
  return false;
77
77
  }
78
78
  }
79
- /** Kandidaten, in volgorde: PUBLIC_BASE_URL → request-origin → centrale shell-host. */
79
+ /**
80
+ * Voorkeursscore per host. Lager = beter.
81
+ *
82
+ * Tijdelijke preview-hosts (`…lovableproject.com`, `id-preview--…`) verdwijnen
83
+ * of gaan stuk zodra er opnieuw gepubliceerd wordt. Ze mogen alleen als laatste
84
+ * redmiddel worden vastgelegd, anders blijft de cron maandenlang naar een dood
85
+ * adres posten.
86
+ */
87
+ function hostRank(origin) {
88
+ let host;
89
+ try {
90
+ host = new URL(origin).hostname.toLowerCase();
91
+ }
92
+ catch {
93
+ return 99;
94
+ }
95
+ if (!host.endsWith(".lovable.app") && !host.endsWith(".lovableproject.com")) {
96
+ return 0; // eigen domein: stabielst
97
+ }
98
+ if (host.startsWith("project--") && host.endsWith(".lovable.app")) {
99
+ return host.includes("-dev.lovable.app") ? 2 : 1; // stabiele project-URL's
100
+ }
101
+ if (host.endsWith(".lovableproject.com") || host.startsWith("id-preview--")) {
102
+ return 4; // vluchtige preview
103
+ }
104
+ return 3;
105
+ }
106
+ /** Leidt de stabiele project-URL's af uit een preview-host. */
107
+ function stableVariants(origin) {
108
+ if (!origin)
109
+ return [];
110
+ let host;
111
+ try {
112
+ host = new URL(origin).hostname.toLowerCase();
113
+ }
114
+ catch {
115
+ return [];
116
+ }
117
+ const uuid = host.match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/)?.[1] ?? null;
118
+ if (!uuid)
119
+ return [];
120
+ return [
121
+ `https://project--${uuid}.lovable.app`,
122
+ `https://project--${uuid}-dev.lovable.app`,
123
+ ];
124
+ }
125
+ /**
126
+ * Kandidaten: expliciete instelling → stabiele varianten van de request-host →
127
+ * request-origin zelf → centrale shell-host. Daarna gesorteerd op stabiliteit,
128
+ * zodat een gepubliceerde host altijd wint van een preview-host.
129
+ */
80
130
  async function baseUrlCandidates(request) {
81
131
  const out = [];
82
132
  const push = (v) => {
83
133
  if (v && !out.includes(v))
84
134
  out.push(v);
85
135
  };
86
- push(normalizeOrigin(process.env.PUBLIC_BASE_URL));
87
- push(originFromRequest(request));
136
+ const explicit = normalizeOrigin(process.env.PUBLIC_BASE_URL);
137
+ const fromRequest = originFromRequest(request);
138
+ push(explicit);
139
+ for (const v of stableVariants(fromRequest))
140
+ push(v);
141
+ push(fromRequest);
88
142
  try {
89
143
  const { getShellHost } = await import("./centralSecrets.server");
90
144
  push(normalizeOrigin(await getShellHost()));
@@ -92,7 +146,10 @@ async function baseUrlCandidates(request) {
92
146
  catch {
93
147
  // centrale module niet bereikbaar
94
148
  }
95
- return out;
149
+ // Expliciet ingestelde waarde blijft altijd voorop; de rest op stabiliteit.
150
+ const rest = explicit ? out.filter((o) => o !== explicit) : out.slice();
151
+ rest.sort((a, b) => hostRank(a) - hostRank(b));
152
+ return explicit ? [explicit, ...rest] : rest;
96
153
  }
97
154
  /**
98
155
  * Resolutie met verificatie: de eerste kandidaat die de module daadwerkelijk
@@ -129,18 +186,52 @@ async function upsertConfig(supabaseAdmin, key, value) {
129
186
  throw new Error(error.message);
130
187
  return true;
131
188
  }
132
- /** Schrijft basis-URL + hook-apikey weg (idempotent, schrijft alleen bij wijziging). */
189
+ /**
190
+ * Schrijft basis-URL + hook-apikey weg (idempotent, schrijft alleen bij wijziging).
191
+ *
192
+ * Zelfherstellend: de reeds geregistreerde host wordt eerst gecontroleerd.
193
+ * Werkt die nog én is hij minstens zo stabiel als de nieuwe kandidaat, dan
194
+ * blijft hij staan. Is hij stuk (verlopen preview, andere shell), dan neemt de
195
+ * nieuwe kandidaat het over.
196
+ */
133
197
  export async function registerModuleRuntime(request) {
134
- const baseUrl = await resolveModuleBaseUrl(request);
135
- if (!baseUrl) {
136
- return { ok: false, baseUrl: null, changed: false, reason: "geen origin resolvbaar" };
198
+ let admin;
199
+ try {
200
+ const mod = await import("../integrations/supabase/client.server");
201
+ admin = mod.supabaseAdmin;
202
+ }
203
+ catch (e) {
204
+ return { ok: false, baseUrl: null, changed: false, reason: e?.message ?? String(e) };
137
205
  }
138
206
  try {
139
- const { supabaseAdmin } = await import("../integrations/supabase/client.server");
140
- let changed = await upsertConfig(supabaseAdmin, BASE_URL_KEY, baseUrl);
207
+ const { data: current } = await admin
208
+ .from("module_runtime_config")
209
+ .select("value")
210
+ .eq("key", BASE_URL_KEY)
211
+ .maybeSingle();
212
+ const currentUrl = normalizeOrigin(current?.value ?? null);
213
+ const explicit = normalizeOrigin(process.env.PUBLIC_BASE_URL);
214
+ const candidate = await resolveModuleBaseUrl(request);
215
+ let baseUrl = null;
216
+ if (explicit) {
217
+ baseUrl = candidate ?? explicit;
218
+ }
219
+ else if (currentUrl &&
220
+ candidate &&
221
+ hostRank(currentUrl) <= hostRank(candidate) &&
222
+ (currentUrl === candidate || (await servesModule(currentUrl)))) {
223
+ baseUrl = currentUrl; // bestaande host is gezond en minstens zo stabiel
224
+ }
225
+ else {
226
+ baseUrl = candidate ?? (currentUrl && (await servesModule(currentUrl)) ? currentUrl : null);
227
+ }
228
+ if (!baseUrl) {
229
+ return { ok: false, baseUrl: null, changed: false, reason: "geen bereikbare host gevonden" };
230
+ }
231
+ let changed = await upsertConfig(admin, BASE_URL_KEY, baseUrl);
141
232
  const apikey = hookApiKey();
142
233
  if (apikey) {
143
- const apikeyChanged = await upsertConfig(supabaseAdmin, APIKEY_KEY, apikey);
234
+ const apikeyChanged = await upsertConfig(admin, APIKEY_KEY, apikey);
144
235
  changed = changed || apikeyChanged;
145
236
  }
146
237
  return { ok: true, baseUrl, changed };
@@ -148,7 +239,7 @@ export async function registerModuleRuntime(request) {
148
239
  catch (e) {
149
240
  return {
150
241
  ok: false,
151
- baseUrl,
242
+ baseUrl: null,
152
243
  changed: false,
153
244
  reason: e?.message ?? String(e),
154
245
  };