@flowselections/floriday-voorraad 1.0.27 → 1.0.29

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 (127) hide show
  1. package/dist-lib/components/voorraad/ArtikelWizard.d.ts +2 -1
  2. package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
  3. package/dist-lib/components/voorraad/ArtikelWizard.js +576 -324
  4. package/dist-lib/components/voorraad/EditArtikelPage.d.ts +2 -1
  5. package/dist-lib/components/voorraad/EditArtikelPage.d.ts.map +1 -1
  6. package/dist-lib/components/voorraad/EditArtikelPage.js +5 -4
  7. package/dist-lib/components/voorraad/FustenPage.d.ts +2 -0
  8. package/dist-lib/components/voorraad/FustenPage.d.ts.map +1 -0
  9. package/dist-lib/components/voorraad/FustenPage.js +97 -0
  10. package/dist-lib/components/voorraad/LogistiekTakenCard.d.ts +2 -0
  11. package/dist-lib/components/voorraad/LogistiekTakenCard.d.ts.map +1 -0
  12. package/dist-lib/components/voorraad/LogistiekTakenCard.js +54 -0
  13. package/dist-lib/components/voorraad/ProductCustomFieldsCard.d.ts +5 -0
  14. package/dist-lib/components/voorraad/ProductCustomFieldsCard.d.ts.map +1 -0
  15. package/dist-lib/components/voorraad/ProductCustomFieldsCard.js +65 -0
  16. package/dist-lib/components/voorraad/PublicatiesPage.d.ts +2 -0
  17. package/dist-lib/components/voorraad/PublicatiesPage.d.ts.map +1 -0
  18. package/dist-lib/components/voorraad/PublicatiesPage.js +60 -0
  19. package/dist-lib/components/voorraad/PublishProgressCard.d.ts +12 -0
  20. package/dist-lib/components/voorraad/PublishProgressCard.d.ts.map +1 -0
  21. package/dist-lib/components/voorraad/PublishProgressCard.js +30 -0
  22. package/dist-lib/components/voorraad/PublishReadinessCard.d.ts +10 -0
  23. package/dist-lib/components/voorraad/PublishReadinessCard.d.ts.map +1 -0
  24. package/dist-lib/components/voorraad/PublishReadinessCard.js +12 -0
  25. package/dist-lib/components/voorraad/StockBulkButtons.d.ts.map +1 -1
  26. package/dist-lib/components/voorraad/StockBulkButtons.js +65 -2
  27. package/dist-lib/components/voorraad/StockSyncStatus.d.ts +2 -0
  28. package/dist-lib/components/voorraad/StockSyncStatus.d.ts.map +1 -0
  29. package/dist-lib/components/voorraad/StockSyncStatus.js +23 -0
  30. package/dist-lib/components/voorraad/ValidationOverviewTable.d.ts.map +1 -1
  31. package/dist-lib/components/voorraad/ValidationOverviewTable.js +8 -2
  32. package/dist-lib/components/voorraad/VoorraadDashboard.d.ts.map +1 -1
  33. package/dist-lib/components/voorraad/VoorraadDashboard.js +4 -1
  34. package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
  35. package/dist-lib/components/voorraad/VoorraadPage.js +5 -1
  36. package/dist-lib/components/voorraad/VoorraadTable.d.ts.map +1 -1
  37. package/dist-lib/components/voorraad/VoorraadTable.js +10 -2
  38. package/dist-lib/components/voorraad/VoorraadVeldenCard.d.ts +2 -0
  39. package/dist-lib/components/voorraad/VoorraadVeldenCard.d.ts.map +1 -0
  40. package/dist-lib/components/voorraad/VoorraadVeldenCard.js +97 -0
  41. package/dist-lib/hooks/useFusten.d.ts +17 -0
  42. package/dist-lib/hooks/useFusten.d.ts.map +1 -0
  43. package/dist-lib/hooks/useFusten.js +81 -0
  44. package/dist-lib/hooks/useLocalTradeItems.d.ts +17 -7
  45. package/dist-lib/hooks/useLocalTradeItems.d.ts.map +1 -1
  46. package/dist-lib/hooks/useLocalTradeItems.js +139 -37
  47. package/dist-lib/hooks/useLogisticsTasks.d.ts +13 -0
  48. package/dist-lib/hooks/useLogisticsTasks.d.ts.map +1 -0
  49. package/dist-lib/hooks/useLogisticsTasks.js +155 -0
  50. package/dist-lib/hooks/usePublishJob.d.ts +13 -0
  51. package/dist-lib/hooks/usePublishJob.d.ts.map +1 -0
  52. package/dist-lib/hooks/usePublishJob.js +67 -0
  53. package/dist-lib/hooks/useRegisterModuleBaseUrl.d.ts +2 -0
  54. package/dist-lib/hooks/useRegisterModuleBaseUrl.d.ts.map +1 -0
  55. package/dist-lib/hooks/useRegisterModuleBaseUrl.js +31 -0
  56. package/dist-lib/hooks/useStockSyncHealth.d.ts +8 -0
  57. package/dist-lib/hooks/useStockSyncHealth.d.ts.map +1 -0
  58. package/dist-lib/hooks/useStockSyncHealth.js +42 -0
  59. package/dist-lib/hooks/useTradeItemStock.d.ts.map +1 -1
  60. package/dist-lib/hooks/useTradeItemStock.js +37 -1
  61. package/dist-lib/hooks/useVoorraadCustomFields.d.ts +9 -0
  62. package/dist-lib/hooks/useVoorraadCustomFields.d.ts.map +1 -0
  63. package/dist-lib/hooks/useVoorraadCustomFields.js +26 -0
  64. package/dist-lib/index.d.ts +2 -0
  65. package/dist-lib/index.d.ts.map +1 -1
  66. package/dist-lib/index.js +9 -2
  67. package/dist-lib/integrations/supabase/auth-middleware.d.ts +8467 -4487
  68. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  69. package/dist-lib/integrations/supabase/client.d.ts +8467 -4487
  70. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  71. package/dist-lib/integrations/supabase/client.server.d.ts +8467 -4487
  72. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  73. package/dist-lib/integrations/supabase/types.d.ts +8714 -4631
  74. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  75. package/dist-lib/integrations/supabase/types.js +49 -0
  76. package/dist-lib/lib/floricode-required-features.functions.d.ts +8467 -4487
  77. package/dist-lib/lib/floricode-required-features.functions.d.ts.map +1 -1
  78. package/dist-lib/lib/floriday-client.d.ts +1 -1
  79. package/dist-lib/lib/floriday-client.d.ts.map +1 -1
  80. package/dist-lib/lib/floriday-client.js +1 -2
  81. package/dist-lib/lib/floriday-customer-offer.functions.d.ts +8467 -4487
  82. package/dist-lib/lib/floriday-customer-offer.functions.d.ts.map +1 -1
  83. package/dist-lib/lib/floriday-customer-offer.functions.js +14 -19
  84. package/dist-lib/lib/floriday-env.functions.d.ts +35712 -0
  85. package/dist-lib/lib/floriday-env.functions.d.ts.map +1 -0
  86. package/dist-lib/lib/floriday-env.functions.js +16 -0
  87. package/dist-lib/lib/floriday-env.server.d.ts +12 -0
  88. package/dist-lib/lib/floriday-env.server.d.ts.map +1 -1
  89. package/dist-lib/lib/floriday-env.server.js +15 -2
  90. package/dist-lib/lib/floriday-gateway.functions.d.ts +77902 -40145
  91. package/dist-lib/lib/floriday-gateway.functions.d.ts.map +1 -1
  92. package/dist-lib/lib/floriday-gateway.functions.js +12 -3
  93. package/dist-lib/lib/floriday-readiness.server.d.ts +11 -0
  94. package/dist-lib/lib/floriday-readiness.server.d.ts.map +1 -0
  95. package/dist-lib/lib/floriday-readiness.server.js +43 -0
  96. package/dist-lib/lib/floriday-vbn-required.functions.d.ts +23115 -15155
  97. package/dist-lib/lib/floriday-vbn-required.functions.d.ts.map +1 -1
  98. package/dist-lib/lib/floriday-warehouse.functions.d.ts +31362 -19422
  99. package/dist-lib/lib/floriday-warehouse.functions.d.ts.map +1 -1
  100. package/dist-lib/lib/floriday-warehouse.functions.js +3 -0
  101. package/dist-lib/lib/floriday-writes.functions.d.ts +23191 -15231
  102. package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
  103. package/dist-lib/lib/fusten.d.ts +87 -0
  104. package/dist-lib/lib/fusten.d.ts.map +1 -0
  105. package/dist-lib/lib/fusten.js +389 -0
  106. package/dist-lib/lib/logistics-engine.d.ts +63 -0
  107. package/dist-lib/lib/logistics-engine.d.ts.map +1 -0
  108. package/dist-lib/lib/logistics-engine.js +57 -0
  109. package/dist-lib/lib/logistics-events.d.ts +18 -0
  110. package/dist-lib/lib/logistics-events.d.ts.map +1 -0
  111. package/dist-lib/lib/logistics-events.js +61 -0
  112. package/dist-lib/lib/module-runtime-config.functions.d.ts +14 -0
  113. package/dist-lib/lib/module-runtime-config.functions.d.ts.map +1 -0
  114. package/dist-lib/lib/module-runtime-config.functions.js +107 -0
  115. package/dist-lib/lib/publish-errors.d.ts +37 -0
  116. package/dist-lib/lib/publish-errors.d.ts.map +1 -0
  117. package/dist-lib/lib/publish-errors.js +183 -0
  118. package/dist-lib/lib/publish-pipeline.d.ts +91 -0
  119. package/dist-lib/lib/publish-pipeline.d.ts.map +1 -0
  120. package/dist-lib/lib/publish-pipeline.js +212 -0
  121. package/dist-lib/lib/publish-readiness.d.ts +42 -0
  122. package/dist-lib/lib/publish-readiness.d.ts.map +1 -0
  123. package/dist-lib/lib/publish-readiness.js +102 -0
  124. package/dist-lib/lib/voorraad-custom-fields.d.ts +22 -0
  125. package/dist-lib/lib/voorraad-custom-fields.d.ts.map +1 -0
  126. package/dist-lib/lib/voorraad-custom-fields.js +71 -0
  127. package/package.json +2 -2
@@ -1,8 +1,19 @@
1
1
  import { useCallback, useEffect, useState } from "react";
2
2
  import { supabase } from "../integrations/supabase/client";
3
3
  import { derivePrimaryPacking } from "../lib/floriday-client";
4
+ function asRecord(value) {
5
+ return value && typeof value === "object" && !Array.isArray(value)
6
+ ? value
7
+ : {};
8
+ }
4
9
  function productToTradeItem(row) {
5
- const payload = (row.floriday_payload ?? {});
10
+ const cf = asRecord(row.custom_fields);
11
+ const payload = asRecord(cf.floriday_payload);
12
+ const floridayId = row.floriday_trade_item_id ||
13
+ (typeof cf.floriday_id === "string" ? cf.floriday_id : null) ||
14
+ (row.trade_item_key && !row.trade_item_key.startsWith("product:")
15
+ ? row.trade_item_key
16
+ : null);
6
17
  const firstImage = Array.isArray(payload.images) && payload.images.length > 0 ? payload.images[0] : null;
7
18
  const payloadImage = (firstImage && (firstImage.url || firstImage.previewUrl)) ?? null;
8
19
  const packingConfigurations = Array.isArray(payload.packingConfigurations)
@@ -14,7 +25,7 @@ function productToTradeItem(row) {
14
25
  id: row.id,
15
26
  productId: row.id,
16
27
  localId: row.id,
17
- tradeItemKey: `product:${row.id}`,
28
+ tradeItemKey: row.trade_item_key || `product:${row.id}`,
18
29
  source: "product",
19
30
  name,
20
31
  articleNumber,
@@ -26,18 +37,18 @@ function productToTradeItem(row) {
26
37
  incomingQuantity: 0,
27
38
  economicQuantity: 0,
28
39
  raw: row,
29
- floridayActive: row.floriday_active,
30
- published: row.floriday_active,
31
- linkStatus: row.floriday_active ? "published" : "linked",
40
+ floridayActive: Boolean(floridayId),
41
+ published: Boolean(floridayId),
42
+ linkStatus: floridayId ? "published" : "linked",
32
43
  missingFields: [],
33
44
  linkError: null,
34
45
  lastSyncAt: null,
35
- floridayId: row.floriday_trade_item_id,
46
+ floridayId,
36
47
  packingConfigurations,
37
48
  primaryPacking: derivePrimaryPacking(packingConfigurations),
38
49
  };
39
50
  }
40
- const SELECT_COLS = "id,product,article_number,barcode,image_url,floriday_active,floriday_trade_item_id,floriday_payload,updated_at,created_at";
51
+ const SELECT_COLS = "id,product,article_number,barcode,image_url,trade_item_key,floriday_trade_item_id,custom_fields,updated_at,created_at";
41
52
  /**
42
53
  * `connectionId` wordt vandaag genegeerd: producten zijn niet meer per Floriday-connectie gescoped.
43
54
  * De parameter blijft in de signatuur zodat bestaande callers zonder wijziging werken.
@@ -83,21 +94,54 @@ export async function getLocalTradeItem(id) {
83
94
  return {
84
95
  id: p.id,
85
96
  connection_id: null,
86
- data: (p.floriday_payload ?? {}),
97
+ data: (asRecord(p.custom_fields).floriday_payload ?? {
98
+ ...{},
99
+ name: p.product ?? "",
100
+ articleCode: p.article_number ?? "",
101
+ gtin: p.barcode ?? "",
102
+ }),
87
103
  updated_at: p.updated_at,
88
104
  created_at: p.created_at,
89
- floriday_id: p.floriday_trade_item_id,
90
- floriday_link_status: p.floriday_active ? "published" : "linked",
105
+ floriday_id: p.floriday_trade_item_id ?? asRecord(p.custom_fields).floriday_id ?? null,
106
+ floriday_link_status: p.floriday_trade_item_id || asRecord(p.custom_fields).floriday_id ? "published" : "linked",
91
107
  missing_required_fields: [],
92
108
  last_floriday_sync_at: null,
93
109
  floriday_link_error: null,
94
110
  source: "product",
95
111
  };
96
112
  }
113
+ export function readLocalSyncStatus(customFields) {
114
+ const cf = asRecord(customFields);
115
+ const raw = cf.floriday_sync;
116
+ if (!raw || typeof raw !== "object")
117
+ return null;
118
+ const s = raw;
119
+ return {
120
+ dirty: Boolean(s.dirty),
121
+ last_attempt_at: s.last_attempt_at ?? null,
122
+ last_error: s.last_error ?? null,
123
+ last_synced_at: s.last_synced_at ?? null,
124
+ };
125
+ }
97
126
  /**
98
- * Slaat Floriday-velden op in `products.floriday_payload`. Als `id` ontbreekt maken we een
127
+ * Slaat Floriday-velden op in `products.custom_fields.floriday_payload`. Als `id` ontbreekt maken we een
99
128
  * nieuw product aan (met minimale ERP-velden uit de payload). Nooit een concept-rij.
129
+ *
130
+ * `sync` legt vast of de gegevens al door Floriday zijn bevestigd. Zolang `dirty` waar is,
131
+ * is de lokale payload leidend bij het opnieuw openen van het artikel.
100
132
  */
133
+ /** Nieuw fusttype in een belading meteen in de fustenvoorraad registreren. */
134
+ function scheduleFustSync(connectionId) {
135
+ void (async () => {
136
+ try {
137
+ const { syncFustTypes } = await import("../lib/fusten");
138
+ await syncFustTypes(connectionId);
139
+ }
140
+ catch {
141
+ // Fustenvoorraad is secundair; nooit het opslaan blokkeren.
142
+ }
143
+ })();
144
+ }
101
145
  export async function upsertLocalTradeItem(params) {
102
146
  const userRes = await supabase.auth.getUser();
103
147
  const userId = userRes.data.user?.id;
@@ -107,16 +151,34 @@ export async function upsertLocalTradeItem(params) {
107
151
  const linkedProductId = typeof payload.linkedProductId === "string" && payload.linkedProductId
108
152
  ? String(payload.linkedProductId)
109
153
  : null;
110
- const targetId = params.id && params.id.length > 0 ? params.id : linkedProductId;
154
+ let targetId = params.id && params.id.length > 0 ? params.id : linkedProductId;
155
+ // Bestaat er al een lokale rij voor dit Floriday-artikel? Dan daarop schrijven
156
+ // in plaats van een duplicaat aanmaken.
157
+ const floridayTradeItemId = params.floridayTradeItemId ?? params.meta?.floridayId ?? null;
158
+ if (!targetId && floridayTradeItemId) {
159
+ const { data: byFloriday } = await supabase
160
+ .from("products")
161
+ .select("id")
162
+ .eq("floriday_trade_item_id", floridayTradeItemId)
163
+ .maybeSingle();
164
+ targetId = byFloriday?.id ?? null;
165
+ }
166
+ const syncPatch = params.sync
167
+ ? {
168
+ dirty: params.sync.dirty,
169
+ last_attempt_at: params.sync.last_attempt_at ?? new Date().toISOString(),
170
+ last_error: params.sync.last_error ?? null,
171
+ last_synced_at: params.sync.last_synced_at ?? null,
172
+ }
173
+ : null;
111
174
  const productPatch = {
112
- floriday_payload: payload,
113
175
  updated_at: new Date().toISOString(),
114
176
  };
115
177
  if (params.meta?.floridayId !== undefined) {
116
178
  productPatch.floriday_trade_item_id = params.meta.floridayId;
117
179
  }
118
- if (params.meta?.floridayLinkStatus === "published") {
119
- productPatch.floriday_active = true;
180
+ else if (floridayTradeItemId) {
181
+ productPatch.floriday_trade_item_id = floridayTradeItemId;
120
182
  }
121
183
  if (payload.name)
122
184
  productPatch.product = payload.name;
@@ -128,21 +190,29 @@ export async function upsertLocalTradeItem(params) {
128
190
  const firstImageUrl = firstImage?.url || firstImage?.previewUrl;
129
191
  if (firstImageUrl)
130
192
  productPatch.image_url = firstImageUrl;
131
- if (payload.vatRate !== undefined) {
132
- const v = payload.vatRate;
133
- productPatch.vat_rate = v === null || v === "" || Number.isNaN(Number(v)) ? null : Number(v);
134
- }
135
- if (payload.vatCode !== undefined) {
136
- const c = payload.vatCode;
137
- productPatch.vat_code = c && String(c).length > 0 ? String(c) : null;
138
- }
139
193
  if (targetId) {
194
+ const { data: existing } = await supabase
195
+ .from("products")
196
+ .select("custom_fields")
197
+ .eq("id", targetId)
198
+ .maybeSingle();
199
+ const currentCustomFields = asRecord(existing?.custom_fields);
200
+ productPatch.custom_fields = {
201
+ ...currentCustomFields,
202
+ articleCode: payload.articleCode || currentCustomFields.articleCode,
203
+ floriday_id: params.meta?.floridayId !== undefined
204
+ ? params.meta.floridayId
205
+ : (currentCustomFields.floriday_id ?? floridayTradeItemId ?? null),
206
+ floriday_payload: payload,
207
+ ...(syncPatch ? { floriday_sync: syncPatch } : {}),
208
+ };
140
209
  const { error } = await supabase
141
210
  .from("products")
142
211
  .update(productPatch)
143
212
  .eq("id", targetId);
144
213
  if (error)
145
214
  throw new Error(error.message);
215
+ scheduleFustSync(params.connectionId ?? null);
146
216
  return targetId;
147
217
  }
148
218
  const generatedId = crypto.randomUUID();
@@ -157,33 +227,65 @@ export async function upsertLocalTradeItem(params) {
157
227
  barcode: payload.gtin || null,
158
228
  image_url: firstImageUrl || null,
159
229
  product_type: "levend",
160
- trade_item_key: `product:${generatedId}`,
161
- floriday_payload: payload,
162
- floriday_trade_item_id: params.meta?.floridayId ?? null,
163
- floriday_active: params.meta?.floridayLinkStatus === "published",
164
- vat_rate: payload.vatRate !== null && payload.vatRate !== undefined && payload.vatRate !== ""
165
- ? Number(payload.vatRate)
166
- : null,
167
- vat_code: payload.vatCode && String(payload.vatCode).length > 0
168
- ? String(payload.vatCode)
169
- : null,
230
+ trade_item_key: floridayTradeItemId ?? `product:${generatedId}`,
231
+ floriday_trade_item_id: params.meta?.floridayId ?? floridayTradeItemId ?? null,
232
+ custom_fields: {
233
+ articleCode: payload.articleCode || null,
234
+ floriday_id: params.meta?.floridayId ?? floridayTradeItemId ?? null,
235
+ floriday_payload: payload,
236
+ ...(syncPatch ? { floriday_sync: syncPatch } : {}),
237
+ },
170
238
  };
171
239
  const { error } = await supabase.from("products").insert(insertRow);
172
240
  if (error)
173
241
  throw new Error(error.message);
242
+ scheduleFustSync(params.connectionId ?? null);
174
243
  return generatedId;
175
244
  }
245
+ /** Zet alleen de sync-status bij, zonder de payload te herschrijven. */
246
+ export async function markLocalSyncStatus(productRowId, status) {
247
+ const { data } = await supabase
248
+ .from("products")
249
+ .select("custom_fields")
250
+ .eq("id", productRowId)
251
+ .maybeSingle();
252
+ const current = asRecord(data?.custom_fields);
253
+ const next = {
254
+ ...current,
255
+ floriday_sync: {
256
+ dirty: status.dirty,
257
+ last_attempt_at: status.last_attempt_at ?? new Date().toISOString(),
258
+ last_error: status.last_error ?? null,
259
+ last_synced_at: status.last_synced_at ?? null,
260
+ },
261
+ };
262
+ const { error } = await supabase
263
+ .from("products")
264
+ .update({ custom_fields: next })
265
+ .eq("id", productRowId);
266
+ if (error)
267
+ throw new Error(error.message);
268
+ }
176
269
  /**
177
- * Legacy API. Alleen meta-velden die vandaag nog op `products` bestaan (`floriday_active`,
178
- * `floriday_trade_item_id`) worden gepersisteerd; overige velden worden stilzwijgend genegeerd
270
+ * Legacy API. Alleen meta-velden die vandaag nog op `products` bestaan (`floriday_trade_item_id`
271
+ * en `custom_fields.floriday_id`) worden gepersisteerd; overige velden worden stilzwijgend genegeerd
179
272
  * omdat het draft-model verdwenen is.
180
273
  */
181
274
  export async function updateDraftMeta(id, meta) {
182
275
  const patch = {};
183
276
  if (meta.floridayId !== undefined)
184
277
  patch.floriday_trade_item_id = meta.floridayId;
185
- if (meta.floridayLinkStatus === "published")
186
- patch.floriday_active = true;
278
+ if (meta.floridayId !== undefined) {
279
+ const { data } = await supabase
280
+ .from("products")
281
+ .select("custom_fields")
282
+ .eq("id", id)
283
+ .maybeSingle();
284
+ patch.custom_fields = {
285
+ ...asRecord(data?.custom_fields),
286
+ floriday_id: meta.floridayId,
287
+ };
288
+ }
187
289
  if (Object.keys(patch).length === 0)
188
290
  return;
189
291
  const { error } = await supabase.from("products").update(patch).eq("id", id);
@@ -0,0 +1,13 @@
1
+ import { type LogisticsTaskRow, type TaskExecutionResult } from "../lib/logistics-engine";
2
+ export declare function useLogisticsTasks(statuses?: string[]): {
3
+ tasks: LogisticsTaskRow[];
4
+ loading: boolean;
5
+ error: string | null;
6
+ refetch: () => Promise<void>;
7
+ };
8
+ /**
9
+ * Voert één `stock.reserve` taak uit. De Engine bepaalt WAT er gereserveerd
10
+ * wordt; deze module rekent niets zelf uit.
11
+ */
12
+ export declare function runStockReserveTask(task: LogisticsTaskRow, connectionId: string | null): Promise<TaskExecutionResult>;
13
+ //# sourceMappingURL=useLogisticsTasks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLogisticsTasks.d.ts","sourceRoot":"","sources":["../../src/hooks/useLogisticsTasks.ts"],"names":[],"mappings":"AAUA,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACzB,MAAM,yBAAyB,CAAC;AAKjC,wBAAgB,iBAAiB,CAAC,QAAQ,GAAE,MAAM,EAAyB;;;;;EAuD1E;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,gBAAgB,EACtB,YAAY,EAAE,MAAM,GAAG,IAAI,GAC1B,OAAO,CAAC,mBAAmB,CAAC,CAoG9B"}
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Taken van de Logistics Engine voor deze module, rechtstreeks uit de
3
+ * gedeelde Supabase-database (public.logistics_tasks).
4
+ *
5
+ * De Engine plant; wij tonen, voeren `stock.reserve` uit en melden af.
6
+ * In schaduwmodus wordt alleen vergeleken en NIETS geschreven aan voorraad;
7
+ * de taak wordt dan ook niet afgemeld.
8
+ */
9
+ import { useCallback, useEffect, useState } from "react";
10
+ import { supabase } from "../integrations/supabase/client";
11
+ import { OWNER_MODULE, describeLoadCarriers, parseStockReservePlan, } from "../lib/logistics-engine";
12
+ const TASK_COLUMNS = "id,order_ref,buffer_ref,task_type,sequence,depends_on,planned_start,planned_end,due_at,status,shadow,payload,result,rule_version";
13
+ export function useLogisticsTasks(statuses = ["planned", "ready"]) {
14
+ const key = statuses.join(",");
15
+ const [tasks, setTasks] = useState([]);
16
+ const [loading, setLoading] = useState(true);
17
+ const [error, setError] = useState(null);
18
+ const refetch = useCallback(async () => {
19
+ const { data, error } = await supabase
20
+ .from("logistics_tasks")
21
+ .select(TASK_COLUMNS)
22
+ .eq("owner_module", OWNER_MODULE)
23
+ .in("status", key.split(","))
24
+ .order("planned_start", { ascending: true });
25
+ if (error) {
26
+ setError(error.message);
27
+ return;
28
+ }
29
+ setError(null);
30
+ setTasks((data ?? []));
31
+ }, [key]);
32
+ useEffect(() => {
33
+ let cancelled = false;
34
+ setLoading(true);
35
+ (async () => {
36
+ await refetch();
37
+ if (!cancelled)
38
+ setLoading(false);
39
+ })();
40
+ return () => {
41
+ cancelled = true;
42
+ };
43
+ }, [refetch]);
44
+ useEffect(() => {
45
+ const channel = supabase
46
+ .channel(`logistics-tasks-${OWNER_MODULE}`)
47
+ .on("postgres_changes", {
48
+ event: "*",
49
+ schema: "public",
50
+ table: "logistics_tasks",
51
+ filter: `owner_module=eq.${OWNER_MODULE}`,
52
+ }, () => {
53
+ refetch();
54
+ })
55
+ .subscribe();
56
+ return () => {
57
+ supabase.removeChannel(channel);
58
+ };
59
+ }, [refetch]);
60
+ return { tasks, loading, error, refetch };
61
+ }
62
+ /**
63
+ * Voert één `stock.reserve` taak uit. De Engine bepaalt WAT er gereserveerd
64
+ * wordt; deze module rekent niets zelf uit.
65
+ */
66
+ export async function runStockReserveTask(task, connectionId) {
67
+ const plan = parseStockReservePlan(task.payload);
68
+ const shadow = task.shadow;
69
+ if (plan.lines.length === 0) {
70
+ return {
71
+ taskId: task.id,
72
+ applied: false,
73
+ shadow,
74
+ diff: [],
75
+ message: `Taak zonder regels (${describeLoadCarriers(plan.loadCarriers)}).`,
76
+ };
77
+ }
78
+ const keys = plan.lines.map((l) => l.tradeItemKey);
79
+ let query = supabase
80
+ .from("trade_item_stock")
81
+ .select("trade_item_key,physical_stock,reserved_stock,connection_id")
82
+ .in("trade_item_key", keys);
83
+ if (connectionId)
84
+ query = query.eq("connection_id", connectionId);
85
+ const { data: rows, error: readError } = await query;
86
+ if (readError)
87
+ throw new Error(`voorraad lezen: ${readError.message}`);
88
+ const stock = (rows ?? []);
89
+ const diff = plan.lines.map((line) => {
90
+ const row = stock.find((r) => r.trade_item_key === line.tradeItemKey);
91
+ const currentReserved = row?.reserved_stock ?? 0;
92
+ return {
93
+ tradeItemKey: line.tradeItemKey,
94
+ currentReserved,
95
+ engineReserved: line.quantity,
96
+ delta: line.quantity - currentReserved,
97
+ };
98
+ });
99
+ if (shadow) {
100
+ return {
101
+ taskId: task.id,
102
+ applied: false,
103
+ shadow: true,
104
+ diff,
105
+ message: `Schaduwmodus: ${diff.length} regel(s) vergeleken, niets geschreven (${describeLoadCarriers(plan.loadCarriers)}).`,
106
+ };
107
+ }
108
+ await supabase
109
+ .from("logistics_tasks")
110
+ .update({ status: "in_progress", started_at: new Date().toISOString() })
111
+ .eq("id", task.id);
112
+ try {
113
+ for (const line of plan.lines) {
114
+ const row = stock.find((r) => r.trade_item_key === line.tradeItemKey);
115
+ if (!row)
116
+ throw new Error(`Geen voorraadrij voor ${line.tradeItemKey}`);
117
+ const { error } = await supabase
118
+ .from("trade_item_stock")
119
+ .update({ reserved_stock: line.quantity })
120
+ .eq("connection_id", row.connection_id)
121
+ .eq("trade_item_key", line.tradeItemKey);
122
+ if (error) {
123
+ throw new Error(`reservering ${line.tradeItemKey}: ${error.message}`);
124
+ }
125
+ }
126
+ }
127
+ catch (e) {
128
+ const message = e?.message ?? String(e);
129
+ await supabase
130
+ .from("logistics_tasks")
131
+ .update({
132
+ status: "failed",
133
+ result: { message, diff },
134
+ completed_at: new Date().toISOString(),
135
+ })
136
+ .eq("id", task.id);
137
+ throw new Error(message);
138
+ }
139
+ const result = {
140
+ taskId: task.id,
141
+ applied: true,
142
+ shadow: false,
143
+ diff,
144
+ message: `${plan.lines.length} regel(s) gereserveerd (${describeLoadCarriers(plan.loadCarriers)}).`,
145
+ };
146
+ await supabase
147
+ .from("logistics_tasks")
148
+ .update({
149
+ status: "done",
150
+ result: result,
151
+ completed_at: new Date().toISOString(),
152
+ })
153
+ .eq("id", task.id);
154
+ return result;
155
+ }
@@ -0,0 +1,13 @@
1
+ import { type PublishJobRow, type PublishStepRow } from "../lib/publish-pipeline";
2
+ export declare function usePublishJob(jobId: string | null): {
3
+ job: PublishJobRow | null;
4
+ steps: PublishStepRow[];
5
+ loading: boolean;
6
+ reload: () => Promise<void>;
7
+ };
8
+ export declare function usePublishJobs(limit?: number): {
9
+ jobs: PublishJobRow[];
10
+ loading: boolean;
11
+ reload: () => Promise<void>;
12
+ };
13
+ //# sourceMappingURL=usePublishJob.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePublishJob.d.ts","sourceRoot":"","sources":["../../src/hooks/usePublishJob.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,cAAc,EACpB,MAAM,yBAAyB,CAAC;AAEjC,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;;;;;EAqDjD;AAED,wBAAgB,cAAc,CAAC,KAAK,SAAK;;;;EAoBxC"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Volgt een lopende publicatie live: opdracht + deelstappen.
3
+ */
4
+ import { useEffect, useState, useCallback } from "react";
5
+ import { supabase } from "../integrations/supabase/client";
6
+ import { loadPublishJob, } from "../lib/publish-pipeline";
7
+ export function usePublishJob(jobId) {
8
+ const [job, setJob] = useState(null);
9
+ const [steps, setSteps] = useState([]);
10
+ const [loading, setLoading] = useState(false);
11
+ const reload = useCallback(async () => {
12
+ if (!jobId) {
13
+ setJob(null);
14
+ setSteps([]);
15
+ return;
16
+ }
17
+ setLoading(true);
18
+ const res = await loadPublishJob(jobId);
19
+ setJob(res.job);
20
+ setSteps(res.steps);
21
+ setLoading(false);
22
+ }, [jobId]);
23
+ useEffect(() => {
24
+ void reload();
25
+ }, [reload]);
26
+ useEffect(() => {
27
+ if (!jobId)
28
+ return;
29
+ const channel = supabase
30
+ .channel(`publish-job-${jobId}`)
31
+ .on("postgres_changes", {
32
+ event: "*",
33
+ schema: "public",
34
+ table: "voorraad_publish_steps",
35
+ filter: `job_id=eq.${jobId}`,
36
+ }, () => void reload())
37
+ .on("postgres_changes", {
38
+ event: "*",
39
+ schema: "public",
40
+ table: "voorraad_publish_jobs",
41
+ filter: `id=eq.${jobId}`,
42
+ }, () => void reload())
43
+ .subscribe();
44
+ return () => {
45
+ void supabase.removeChannel(channel);
46
+ };
47
+ }, [jobId, reload]);
48
+ return { job, steps, loading, reload };
49
+ }
50
+ export function usePublishJobs(limit = 50) {
51
+ const [jobs, setJobs] = useState([]);
52
+ const [loading, setLoading] = useState(true);
53
+ const reload = useCallback(async () => {
54
+ setLoading(true);
55
+ const { data } = await supabase
56
+ .from("voorraad_publish_jobs")
57
+ .select("*")
58
+ .order("created_at", { ascending: false })
59
+ .limit(limit);
60
+ setJobs((data ?? []));
61
+ setLoading(false);
62
+ }, [limit]);
63
+ useEffect(() => {
64
+ void reload();
65
+ }, [reload]);
66
+ return { jobs, loading, reload };
67
+ }
@@ -0,0 +1,2 @@
1
+ export declare function useRegisterModuleBaseUrl(): void;
2
+ //# sourceMappingURL=useRegisterModuleBaseUrl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useRegisterModuleBaseUrl.d.ts","sourceRoot":"","sources":["../../src/hooks/useRegisterModuleBaseUrl.ts"],"names":[],"mappings":"AAUA,wBAAgB,wBAAwB,SA0BvC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Registreert eenmalig per sessie de basis-URL van de shell waarin deze
3
+ * module draait, zodat de Postgres-cron de voorraadsync-hooks kan aanroepen
4
+ * zonder hardgecodeerde host.
5
+ */
6
+ import { useEffect, useRef } from "react";
7
+ import { registerModuleBaseUrl } from "../lib/module-runtime-config.functions";
8
+ let alreadyRegistered = false;
9
+ export function useRegisterModuleBaseUrl() {
10
+ const started = useRef(false);
11
+ useEffect(() => {
12
+ if (alreadyRegistered || started.current)
13
+ return;
14
+ started.current = true;
15
+ alreadyRegistered = true;
16
+ registerModuleBaseUrl()
17
+ .then((res) => {
18
+ if (res?.ok) {
19
+ console.info(`[voorraad] basis-URL geregistreerd: ${res.baseUrl}${res.changed ? " (bijgewerkt)" : ""}`);
20
+ }
21
+ else {
22
+ alreadyRegistered = false;
23
+ console.warn(`[voorraad] basis-URL niet geregistreerd: ${res?.reason ?? "onbekende reden"}`);
24
+ }
25
+ })
26
+ .catch((e) => {
27
+ alreadyRegistered = false;
28
+ console.warn("[voorraad] kon basis-URL niet registreren:", e?.message ?? e);
29
+ });
30
+ }, []);
31
+ }
@@ -0,0 +1,8 @@
1
+ export interface StockSyncHealth {
2
+ failed: number;
3
+ pending: number;
4
+ stalePending: number;
5
+ oldestPendingAt: string | null;
6
+ }
7
+ export declare function useStockSyncHealth(): import("@tanstack/react-query").UseQueryResult<NoInfer<StockSyncHealth>, Error>;
8
+ //# sourceMappingURL=useStockSyncHealth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useStockSyncHealth.d.ts","sourceRoot":"","sources":["../../src/hooks/useStockSyncHealth.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,wBAAgB,kBAAkB,oFAmCjC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Gezondheid van de voorraad-push-wachtrij richting Floriday.
3
+ * Signaleert vastgelopen (`failed`) of te lang wachtende (`pending`) regels.
4
+ */
5
+ import { useQuery } from "@tanstack/react-query";
6
+ import { supabase } from "../integrations/supabase/client";
7
+ const STALE_MINUTES = 10;
8
+ export function useStockSyncHealth() {
9
+ return useQuery({
10
+ queryKey: ["stock-sync-health"],
11
+ refetchInterval: 60000,
12
+ queryFn: async () => {
13
+ const { data, error } = await supabase
14
+ .from("floriday_continuous_stock_queue")
15
+ .select("status, created_at")
16
+ .in("status", ["pending", "failed"])
17
+ .limit(1000);
18
+ if (error)
19
+ throw error;
20
+ const rows = (data ?? []);
21
+ const cutoff = Date.now() - STALE_MINUTES * 60000;
22
+ let failed = 0;
23
+ let pending = 0;
24
+ let stalePending = 0;
25
+ let oldestPendingAt = null;
26
+ for (const r of rows) {
27
+ if (r.status === "failed") {
28
+ failed++;
29
+ continue;
30
+ }
31
+ pending++;
32
+ const ts = Date.parse(r.created_at);
33
+ if (Number.isFinite(ts) && ts < cutoff)
34
+ stalePending++;
35
+ if (!oldestPendingAt || r.created_at < oldestPendingAt) {
36
+ oldestPendingAt = r.created_at;
37
+ }
38
+ }
39
+ return { failed, pending, stalePending, oldestPendingAt };
40
+ },
41
+ });
42
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"useTradeItemStock.d.ts","sourceRoot":"","sources":["../../src/hooks/useTradeItemStock.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAUD,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,GAAG,IAAI,CAKhB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,EAAE,CAMX;AAED,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;;;mBA4ElB,iBAAiB;EA6BnC;AAED,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAUzC"}
1
+ {"version":3,"file":"useTradeItemStock.d.ts","sourceRoot":"","sources":["../../src/hooks/useTradeItemStock.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAUD,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,GAAG,IAAI,CAKhB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,EAAE,CAMX;AAED,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;;;mBA4ElB,iBAAiB;EAmEnC;AAED,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAUzC"}