@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
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Publicatie-pijplijn: één publicatie = één opdracht met vaste deelstappen.
3
+ *
4
+ * Elke deelstap wordt apart vastgelegd (status, pogingen, begrijpelijke fout).
5
+ * Geslaagde stappen worden bij een nieuwe poging overgeslagen, tijdelijke
6
+ * storingen worden automatisch opnieuw geprobeerd.
7
+ */
8
+ import { supabase } from "../integrations/supabase/client";
9
+ import { translatePublishError } from "./publish-errors";
10
+ export const PUBLISH_STEPS = [
11
+ { key: "trade-item", label: "Productgegevens" },
12
+ { key: "stock", label: "Voorraad" },
13
+ { key: "offer", label: "Aanbod" },
14
+ { key: "supply-line", label: "Prijs online" },
15
+ { key: "prices", label: "Prijsafspraken" },
16
+ { key: "verify", label: "Controle" },
17
+ ];
18
+ const jobs = () => supabase.from("voorraad_publish_jobs");
19
+ const steps = () => supabase.from("voorraad_publish_steps");
20
+ export async function createPublishJob(input) {
21
+ const userRes = await supabase.auth.getUser();
22
+ const userId = userRes.data.user?.id;
23
+ if (!userId)
24
+ return null;
25
+ const { data, error } = await jobs()
26
+ .insert({
27
+ user_id: userId,
28
+ connection_id: input.connectionId,
29
+ environment: input.environment,
30
+ intent: input.intent,
31
+ title: input.title,
32
+ product_id: input.productId ?? null,
33
+ trade_item_id: input.tradeItemId ?? null,
34
+ trade_item_key: input.tradeItemKey ?? null,
35
+ snapshot: (input.snapshot ?? {}),
36
+ })
37
+ .select("*")
38
+ .single();
39
+ if (error || !data)
40
+ return null;
41
+ const job = data;
42
+ const active = input.activeSteps ?? PUBLISH_STEPS.map((s) => s.key);
43
+ const rows = PUBLISH_STEPS.map((s, i) => ({
44
+ job_id: job.id,
45
+ user_id: userId,
46
+ step_key: s.key,
47
+ label: s.label,
48
+ position: i,
49
+ status: active.includes(s.key) ? "pending" : "skipped",
50
+ }));
51
+ await steps().insert(rows);
52
+ return job;
53
+ }
54
+ export async function loadPublishJob(jobId) {
55
+ const [{ data: j }, { data: s }] = await Promise.all([
56
+ jobs().select("*").eq("id", jobId).maybeSingle(),
57
+ steps().select("*").eq("job_id", jobId).order("position"),
58
+ ]);
59
+ return {
60
+ job: j ?? null,
61
+ steps: (s ?? []),
62
+ };
63
+ }
64
+ async function patchStep(jobId, key, patch) {
65
+ await steps().update(patch).eq("job_id", jobId).eq("step_key", key);
66
+ }
67
+ export async function markStepSkipped(jobId, key) {
68
+ await patchStep(jobId, key, { status: "skipped", finished_at: new Date().toISOString() });
69
+ }
70
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
71
+ /**
72
+ * Voert één deelstap uit. Al geslaagde stappen worden overgeslagen zodat
73
+ * "opnieuw proberen" nooit dubbel publiceert. Tijdelijke storingen worden
74
+ * automatisch tot 3× herhaald met oplopende wachttijd.
75
+ */
76
+ export async function runPublishStep(jobId, key, fn, opts) {
77
+ // Zonder job (bv. offline logboek) draaien we de stap gewoon uit.
78
+ if (jobId) {
79
+ const { data } = await steps()
80
+ .select("status")
81
+ .eq("job_id", jobId)
82
+ .eq("step_key", key)
83
+ .maybeSingle();
84
+ const status = data?.status;
85
+ if (status === "done")
86
+ return { ok: true, skipped: true };
87
+ await patchStep(jobId, key, {
88
+ status: "running",
89
+ started_at: new Date().toISOString(),
90
+ error_code: null,
91
+ error_message: null,
92
+ error_detail: null,
93
+ });
94
+ }
95
+ const maxAttempts = opts?.maxAttempts ?? 3;
96
+ let last;
97
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
98
+ try {
99
+ const value = await fn();
100
+ if (jobId) {
101
+ await patchStep(jobId, key, {
102
+ status: "done",
103
+ attempts: attempt,
104
+ finished_at: new Date().toISOString(),
105
+ result: sanitizeResult(value),
106
+ });
107
+ }
108
+ return { ok: true, value };
109
+ }
110
+ catch (e) {
111
+ last = translatePublishError(e, {
112
+ characteristicLabels: opts?.characteristicLabels,
113
+ });
114
+ if (!last.retryable || attempt === maxAttempts)
115
+ break;
116
+ await sleep(attempt * 1500);
117
+ }
118
+ }
119
+ if (jobId && last) {
120
+ await patchStep(jobId, key, {
121
+ status: "failed",
122
+ attempts: maxAttempts,
123
+ finished_at: new Date().toISOString(),
124
+ error_code: last.code,
125
+ error_message: last.message,
126
+ error_detail: last.detail?.slice(0, 4000) ?? null,
127
+ });
128
+ }
129
+ return { ok: false, error: last };
130
+ }
131
+ function sanitizeResult(value) {
132
+ if (value == null)
133
+ return null;
134
+ if (typeof value === "object") {
135
+ try {
136
+ const json = JSON.stringify(value);
137
+ return json.length > 4000 ? { truncated: true } : JSON.parse(json);
138
+ }
139
+ catch {
140
+ return null;
141
+ }
142
+ }
143
+ return { value: String(value) };
144
+ }
145
+ export async function finishPublishJob(jobId, patch) {
146
+ const { data } = await steps().select("status, step_key").eq("job_id", jobId);
147
+ const rows = (data ?? []);
148
+ const failed = rows.filter((r) => r.status === "failed");
149
+ const coreFailed = failed.some((r) => r.step_key === "trade-item");
150
+ const status = coreFailed
151
+ ? "failed"
152
+ : failed.length > 0
153
+ ? "succeeded_with_warnings"
154
+ : "succeeded";
155
+ await jobs()
156
+ .update({
157
+ status,
158
+ finished_at: new Date().toISOString(),
159
+ ...(patch?.tradeItemId ? { trade_item_id: patch.tradeItemId } : {}),
160
+ ...(patch?.summary ? { summary: patch.summary } : {}),
161
+ })
162
+ .eq("id", jobId);
163
+ return status;
164
+ }
165
+ /**
166
+ * Onthoudt welke gegevens door de handelsomgeving zijn afgekeurd, zodat een
167
+ * volgend product hier vóór het publiceren al op gecontroleerd wordt.
168
+ */
169
+ export async function rememberPublishRejection(input) {
170
+ if (!input.vbnCode || input.keys.length === 0)
171
+ return;
172
+ try {
173
+ for (const key of input.keys) {
174
+ const { data } = await supabase
175
+ .from("voorraad_publish_learnings")
176
+ .select("id, hits")
177
+ .eq("vbn_code", input.vbnCode)
178
+ .eq("scope", input.scope)
179
+ .eq("field_key", key)
180
+ .maybeSingle();
181
+ if (data?.id) {
182
+ await supabase
183
+ .from("voorraad_publish_learnings")
184
+ .update({
185
+ hits: (data.hits ?? 0) + 1,
186
+ last_error: input.lastError?.slice(0, 500) ?? null,
187
+ })
188
+ .eq("id", data.id);
189
+ }
190
+ else {
191
+ await supabase.from("voorraad_publish_learnings").insert({
192
+ vbn_code: input.vbnCode,
193
+ scope: input.scope,
194
+ field_key: key,
195
+ last_error: input.lastError?.slice(0, 500) ?? null,
196
+ });
197
+ }
198
+ }
199
+ }
200
+ catch {
201
+ // Leren mag de publicatie nooit blokkeren.
202
+ }
203
+ }
204
+ export async function listPublishLearnings(vbnCode) {
205
+ if (!vbnCode)
206
+ return [];
207
+ const { data } = await supabase
208
+ .from("voorraad_publish_learnings")
209
+ .select("scope, field_key")
210
+ .eq("vbn_code", vbnCode);
211
+ return (data ?? []);
212
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Controle vooraf: kan dit product veilig gepubliceerd worden?
3
+ *
4
+ * Levert een korte checklist op in gewone taal. Elke regel verwijst naar de
5
+ * stap in de wizard waar het opgelost kan worden.
6
+ */
7
+ import type { WizardData } from "./floriday-payload";
8
+ import type { TradeItemSpec } from "../hooks/useTradeItemSpec";
9
+ export interface ReadinessItem {
10
+ key: string;
11
+ label: string;
12
+ ok: boolean;
13
+ /** Wizard-stap waar dit ingevuld wordt. */
14
+ stepKey?: string;
15
+ /** Blokkeert publiceren volledig. */
16
+ blocking: boolean;
17
+ hint?: string;
18
+ }
19
+ export interface ReadinessReport {
20
+ ok: boolean;
21
+ items: ReadinessItem[];
22
+ blockers: ReadinessItem[];
23
+ }
24
+ export interface ReadinessInput {
25
+ data: WizardData;
26
+ spec?: TradeItemSpec | null;
27
+ /** Verbinding actief en omgeving bekend. */
28
+ connectionOk: boolean;
29
+ connectionLoading?: boolean;
30
+ environment?: string | null;
31
+ connectionMessage?: string | null;
32
+ /** Productgegevens uit de handelsomgeving zijn geladen. */
33
+ schemaReady: boolean;
34
+ /** Eerder afgekeurde eigenschapscodes voor deze productsoort. */
35
+ learnedCharacteristicCodes?: string[];
36
+ /** Labels bij eigenschapscodes voor leesbare meldingen. */
37
+ characteristicLabels?: Record<string, string>;
38
+ /** Wordt het product ook echt gepubliceerd (of alleen lokaal bewaard)? */
39
+ wantsPublish: boolean;
40
+ }
41
+ export declare function buildReadinessReport(input: ReadinessInput): ReadinessReport;
42
+ //# sourceMappingURL=publish-readiness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish-readiness.d.ts","sourceRoot":"","sources":["../../src/lib/publish-readiness.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,OAAO,CAAC;IACZ,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IAC5B,4CAA4C;IAC5C,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,2DAA2D;IAC3D,WAAW,EAAE,OAAO,CAAC;IACrB,iEAAiE;IACjE,0BAA0B,CAAC,EAAE,MAAM,EAAE,CAAC;IACtC,2DAA2D;IAC3D,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,0EAA0E;IAC1E,YAAY,EAAE,OAAO,CAAC;CACvB;AAOD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,CAuG3E"}
@@ -0,0 +1,102 @@
1
+ function labelFor(code, labels) {
2
+ const l = labels?.[code];
3
+ return l && l !== code ? `${l}` : code;
4
+ }
5
+ export function buildReadinessReport(input) {
6
+ const d = input.data;
7
+ const items = [];
8
+ if (input.wantsPublish) {
9
+ items.push({
10
+ key: "connection",
11
+ label: input.connectionLoading
12
+ ? "Verbinding met de handelsomgeving controleren…"
13
+ : input.connectionOk
14
+ ? `De ${input.environment === "live" ? "liveomgeving" : "testomgeving"} is klaar voor publiceren`
15
+ : input.connectionMessage ?? "De verbinding is nog niet klaar voor publiceren",
16
+ ok: Boolean(input.connectionOk),
17
+ blocking: true,
18
+ hint: input.connectionOk ? undefined : "Controleer de verbinding bij Instellingen.",
19
+ });
20
+ items.push({
21
+ key: "schema",
22
+ label: input.schemaReady
23
+ ? "Productregels opgehaald"
24
+ : "Productregels nog niet opgehaald",
25
+ ok: input.schemaReady,
26
+ blocking: true,
27
+ hint: input.schemaReady ? undefined : "Wacht heel even en probeer opnieuw.",
28
+ });
29
+ }
30
+ items.push({
31
+ key: "vbn",
32
+ label: "Productsoort gekozen",
33
+ ok: Boolean(d.vbnCode?.trim()),
34
+ stepKey: "vbn",
35
+ blocking: true,
36
+ });
37
+ items.push({
38
+ key: "name",
39
+ label: "Artikelnaam ingevuld",
40
+ ok: Boolean(d.name?.trim() && d.name.trim().length >= 2),
41
+ stepKey: "artikel",
42
+ blocking: true,
43
+ });
44
+ items.push({
45
+ key: "articleCode",
46
+ label: "Artikelcode aanwezig",
47
+ ok: Boolean(d.articleCode?.trim()),
48
+ stepKey: "artikel",
49
+ blocking: true,
50
+ });
51
+ items.push({
52
+ key: "packing",
53
+ label: "Belading ingesteld",
54
+ ok: Array.isArray(d.packingConfigurations) && d.packingConfigurations.length > 0,
55
+ stepKey: "belading",
56
+ blocking: true,
57
+ });
58
+ items.push({
59
+ key: "photos",
60
+ label: "Minimaal één foto toegevoegd",
61
+ ok: Array.isArray(d.images) && d.images.length > 0,
62
+ stepKey: "fotos",
63
+ blocking: false,
64
+ hint: "Zonder foto is het product minder goed vindbaar.",
65
+ });
66
+ // Verplichte eigenschappen uit de productregels + eerder afgekeurde velden.
67
+ const required = new Set([
68
+ ...Array.from(input.spec?.requiredCharacteristicCodes ?? []),
69
+ ...(input.learnedCharacteristicCodes ?? []),
70
+ ]);
71
+ const filled = d.features ?? {};
72
+ const missing = Array.from(required).filter((code) => {
73
+ const v = filled[code];
74
+ return v == null || String(v).trim() === "";
75
+ });
76
+ if (required.size > 0) {
77
+ items.push({
78
+ key: "characteristics",
79
+ label: missing.length === 0
80
+ ? "Alle verplichte eigenschappen ingevuld"
81
+ : `Nog invullen: ${missing
82
+ .slice(0, 4)
83
+ .map((c) => labelFor(c, input.characteristicLabels))
84
+ .join(", ")}${missing.length > 4 ? ` en ${missing.length - 4} meer` : ""}`,
85
+ ok: missing.length === 0,
86
+ stepKey: "eigenschappen",
87
+ blocking: true,
88
+ });
89
+ }
90
+ if (d.offerForSale) {
91
+ items.push({
92
+ key: "price",
93
+ label: "Verkoopprijs ingevuld",
94
+ ok: Number(d.salePricePerPiece) > 0,
95
+ stepKey: "aanbieden",
96
+ blocking: true,
97
+ hint: "Zonder prijs kan het product niet worden aangeboden.",
98
+ });
99
+ }
100
+ const blockers = items.filter((i) => i.blocking && !i.ok);
101
+ return { ok: blockers.length === 0, items, blockers };
102
+ }
@@ -0,0 +1,22 @@
1
+ export type VoorraadFieldType = "text" | "number" | "date" | "select" | "boolean";
2
+ export interface VoorraadCustomField {
3
+ id: string;
4
+ field_key: string;
5
+ field_label: string;
6
+ field_type: VoorraadFieldType;
7
+ options: string[];
8
+ show_in_template: boolean;
9
+ show_in_columns: boolean;
10
+ show_in_detail: boolean;
11
+ sort_order: number;
12
+ }
13
+ export declare function slugifyFieldKey(label: string): string;
14
+ export declare function fetchVoorraadCustomFields(): Promise<VoorraadCustomField[]>;
15
+ export declare function createVoorraadCustomField(input: Omit<VoorraadCustomField, "id">): Promise<void>;
16
+ export declare function updateVoorraadCustomField(id: string, patch: Partial<Omit<VoorraadCustomField, "id">>): Promise<void>;
17
+ export declare function deleteVoorraadCustomField(id: string): Promise<void>;
18
+ /** Waarden van eigen velden staan in `products.custom_fields` (jsonb). */
19
+ export declare function fetchProductCustomValues(productId: string): Promise<Record<string, unknown>>;
20
+ export declare function saveProductCustomValues(productId: string, values: Record<string, unknown>): Promise<void>;
21
+ export declare function formatCustomValue(value: unknown): string;
22
+ //# sourceMappingURL=voorraad-custom-fields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voorraad-custom-fields.d.ts","sourceRoot":"","sources":["../../src/lib/voorraad-custom-fields.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;AAElF,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,iBAAiB,CAAC;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,eAAe,EAAE,OAAO,CAAC;IACzB,cAAc,EAAE,OAAO,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAOD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQrD;AAED,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAWhF;AAED,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAC,GACrC,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,yBAAyB,CAC7C,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAC,CAAC,GAC9C,OAAO,CAAC,IAAI,CAAC,CAMf;AAED,wBAAsB,yBAAyB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGzE;AAED,0EAA0E;AAC1E,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CASlC;AAED,wBAAsB,uBAAuB,CAC3C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAIxD"}
@@ -0,0 +1,71 @@
1
+ import { supabase } from "../integrations/supabase/client";
2
+ const TABLE = "voorraad_custom_fields";
3
+ const SELECT = "id,field_key,field_label,field_type,options,show_in_template,show_in_columns,show_in_detail,sort_order";
4
+ export function slugifyFieldKey(label) {
5
+ return label
6
+ .toLowerCase()
7
+ .normalize("NFD")
8
+ .replace(/[\u0300-\u036f]/g, "")
9
+ .replace(/[^a-z0-9]+/g, "_")
10
+ .replace(/^_+|_+$/g, "")
11
+ .slice(0, 48);
12
+ }
13
+ export async function fetchVoorraadCustomFields() {
14
+ const { data, error } = await supabase
15
+ .from(TABLE)
16
+ .select(SELECT)
17
+ .order("sort_order", { ascending: true })
18
+ .order("created_at", { ascending: true });
19
+ if (error)
20
+ throw new Error(error.message);
21
+ return (data ?? []).map((f) => ({
22
+ ...f,
23
+ options: Array.isArray(f.options) ? f.options : [],
24
+ }));
25
+ }
26
+ export async function createVoorraadCustomField(input) {
27
+ const { error } = await supabase.from(TABLE).insert(input);
28
+ if (error)
29
+ throw new Error(error.message);
30
+ }
31
+ export async function updateVoorraadCustomField(id, patch) {
32
+ const { error } = await supabase
33
+ .from(TABLE)
34
+ .update(patch)
35
+ .eq("id", id);
36
+ if (error)
37
+ throw new Error(error.message);
38
+ }
39
+ export async function deleteVoorraadCustomField(id) {
40
+ const { error } = await supabase.from(TABLE).delete().eq("id", id);
41
+ if (error)
42
+ throw new Error(error.message);
43
+ }
44
+ /** Waarden van eigen velden staan in `products.custom_fields` (jsonb). */
45
+ export async function fetchProductCustomValues(productId) {
46
+ const { data, error } = await supabase
47
+ .from("products")
48
+ .select("custom_fields")
49
+ .eq("id", productId)
50
+ .maybeSingle();
51
+ if (error)
52
+ throw new Error(error.message);
53
+ const cf = data?.custom_fields;
54
+ return cf && typeof cf === "object" && !Array.isArray(cf) ? cf : {};
55
+ }
56
+ export async function saveProductCustomValues(productId, values) {
57
+ const current = await fetchProductCustomValues(productId);
58
+ const { error } = await supabase
59
+ .from("products")
60
+ .update({ custom_fields: { ...current, ...values } })
61
+ .eq("id", productId);
62
+ if (error)
63
+ throw new Error(error.message);
64
+ }
65
+ export function formatCustomValue(value) {
66
+ if (value === null || value === undefined || value === "")
67
+ return "—";
68
+ if (typeof value === "boolean")
69
+ return value ? "Ja" : "Nee";
70
+ return String(value);
71
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/floriday-voorraad",
3
- "version": "1.0.27",
3
+ "version": "1.0.29",
4
4
  "sideEffects": false,
5
5
  "type": "module",
6
6
  "main": "./dist-lib/index.js",
@@ -45,7 +45,7 @@
45
45
  "@cloudflare/vite-plugin": "^1.25.5",
46
46
  "@eslint/js": "^9.32.0",
47
47
  "@flowselections/core": "1.0.16",
48
- "@lovable.dev/vite-tanstack-config": "2.7.7",
48
+ "@lovable.dev/vite-tanstack-config": "2.8.5",
49
49
  "@rsbuild/core": "^1.0.0",
50
50
  "@tailwindcss/vite": "^4.2.1",
51
51
  "@tanstack/router-plugin": "^1.167.10",