@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.
- package/dist-lib/components/voorraad/ArtikelWizard.d.ts +2 -1
- package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ArtikelWizard.js +576 -324
- package/dist-lib/components/voorraad/EditArtikelPage.d.ts +2 -1
- package/dist-lib/components/voorraad/EditArtikelPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/EditArtikelPage.js +5 -4
- package/dist-lib/components/voorraad/FustenPage.d.ts +2 -0
- package/dist-lib/components/voorraad/FustenPage.d.ts.map +1 -0
- package/dist-lib/components/voorraad/FustenPage.js +97 -0
- package/dist-lib/components/voorraad/LogistiekTakenCard.d.ts +2 -0
- package/dist-lib/components/voorraad/LogistiekTakenCard.d.ts.map +1 -0
- package/dist-lib/components/voorraad/LogistiekTakenCard.js +54 -0
- package/dist-lib/components/voorraad/ProductCustomFieldsCard.d.ts +5 -0
- package/dist-lib/components/voorraad/ProductCustomFieldsCard.d.ts.map +1 -0
- package/dist-lib/components/voorraad/ProductCustomFieldsCard.js +65 -0
- package/dist-lib/components/voorraad/PublicatiesPage.d.ts +2 -0
- package/dist-lib/components/voorraad/PublicatiesPage.d.ts.map +1 -0
- package/dist-lib/components/voorraad/PublicatiesPage.js +60 -0
- package/dist-lib/components/voorraad/PublishProgressCard.d.ts +12 -0
- package/dist-lib/components/voorraad/PublishProgressCard.d.ts.map +1 -0
- package/dist-lib/components/voorraad/PublishProgressCard.js +30 -0
- package/dist-lib/components/voorraad/PublishReadinessCard.d.ts +10 -0
- package/dist-lib/components/voorraad/PublishReadinessCard.d.ts.map +1 -0
- package/dist-lib/components/voorraad/PublishReadinessCard.js +12 -0
- package/dist-lib/components/voorraad/StockBulkButtons.d.ts.map +1 -1
- package/dist-lib/components/voorraad/StockBulkButtons.js +65 -2
- package/dist-lib/components/voorraad/StockSyncStatus.d.ts +2 -0
- package/dist-lib/components/voorraad/StockSyncStatus.d.ts.map +1 -0
- package/dist-lib/components/voorraad/StockSyncStatus.js +23 -0
- package/dist-lib/components/voorraad/ValidationOverviewTable.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ValidationOverviewTable.js +8 -2
- package/dist-lib/components/voorraad/VoorraadDashboard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadDashboard.js +4 -1
- package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadPage.js +5 -1
- package/dist-lib/components/voorraad/VoorraadTable.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadTable.js +10 -2
- package/dist-lib/components/voorraad/VoorraadVeldenCard.d.ts +2 -0
- package/dist-lib/components/voorraad/VoorraadVeldenCard.d.ts.map +1 -0
- package/dist-lib/components/voorraad/VoorraadVeldenCard.js +97 -0
- package/dist-lib/hooks/useFusten.d.ts +17 -0
- package/dist-lib/hooks/useFusten.d.ts.map +1 -0
- package/dist-lib/hooks/useFusten.js +81 -0
- package/dist-lib/hooks/useLocalTradeItems.d.ts +17 -7
- package/dist-lib/hooks/useLocalTradeItems.d.ts.map +1 -1
- package/dist-lib/hooks/useLocalTradeItems.js +139 -37
- package/dist-lib/hooks/useLogisticsTasks.d.ts +13 -0
- package/dist-lib/hooks/useLogisticsTasks.d.ts.map +1 -0
- package/dist-lib/hooks/useLogisticsTasks.js +155 -0
- package/dist-lib/hooks/usePublishJob.d.ts +13 -0
- package/dist-lib/hooks/usePublishJob.d.ts.map +1 -0
- package/dist-lib/hooks/usePublishJob.js +67 -0
- package/dist-lib/hooks/useRegisterModuleBaseUrl.d.ts +2 -0
- package/dist-lib/hooks/useRegisterModuleBaseUrl.d.ts.map +1 -0
- package/dist-lib/hooks/useRegisterModuleBaseUrl.js +31 -0
- package/dist-lib/hooks/useStockSyncHealth.d.ts +8 -0
- package/dist-lib/hooks/useStockSyncHealth.d.ts.map +1 -0
- package/dist-lib/hooks/useStockSyncHealth.js +42 -0
- package/dist-lib/hooks/useTradeItemStock.d.ts.map +1 -1
- package/dist-lib/hooks/useTradeItemStock.js +37 -1
- package/dist-lib/hooks/useVoorraadCustomFields.d.ts +9 -0
- package/dist-lib/hooks/useVoorraadCustomFields.d.ts.map +1 -0
- package/dist-lib/hooks/useVoorraadCustomFields.js +26 -0
- package/dist-lib/index.d.ts +2 -0
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +9 -2
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +8467 -4487
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +8467 -4487
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +8467 -4487
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +8714 -4631
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +49 -0
- package/dist-lib/lib/floricode-required-features.functions.d.ts +8467 -4487
- package/dist-lib/lib/floricode-required-features.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-client.d.ts +1 -1
- package/dist-lib/lib/floriday-client.d.ts.map +1 -1
- package/dist-lib/lib/floriday-client.js +1 -2
- package/dist-lib/lib/floriday-customer-offer.functions.d.ts +8467 -4487
- package/dist-lib/lib/floriday-customer-offer.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-customer-offer.functions.js +14 -19
- package/dist-lib/lib/floriday-env.functions.d.ts +35712 -0
- package/dist-lib/lib/floriday-env.functions.d.ts.map +1 -0
- package/dist-lib/lib/floriday-env.functions.js +16 -0
- package/dist-lib/lib/floriday-env.server.d.ts +12 -0
- package/dist-lib/lib/floriday-env.server.d.ts.map +1 -1
- package/dist-lib/lib/floriday-env.server.js +15 -2
- package/dist-lib/lib/floriday-gateway.functions.d.ts +77902 -40145
- package/dist-lib/lib/floriday-gateway.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-gateway.functions.js +12 -3
- package/dist-lib/lib/floriday-readiness.server.d.ts +11 -0
- package/dist-lib/lib/floriday-readiness.server.d.ts.map +1 -0
- package/dist-lib/lib/floriday-readiness.server.js +43 -0
- package/dist-lib/lib/floriday-vbn-required.functions.d.ts +23115 -15155
- package/dist-lib/lib/floriday-vbn-required.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-warehouse.functions.d.ts +31362 -19422
- package/dist-lib/lib/floriday-warehouse.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-warehouse.functions.js +3 -0
- package/dist-lib/lib/floriday-writes.functions.d.ts +23191 -15231
- package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
- package/dist-lib/lib/fusten.d.ts +87 -0
- package/dist-lib/lib/fusten.d.ts.map +1 -0
- package/dist-lib/lib/fusten.js +389 -0
- package/dist-lib/lib/logistics-engine.d.ts +63 -0
- package/dist-lib/lib/logistics-engine.d.ts.map +1 -0
- package/dist-lib/lib/logistics-engine.js +57 -0
- package/dist-lib/lib/logistics-events.d.ts +18 -0
- package/dist-lib/lib/logistics-events.d.ts.map +1 -0
- package/dist-lib/lib/logistics-events.js +61 -0
- package/dist-lib/lib/module-runtime-config.functions.d.ts +14 -0
- package/dist-lib/lib/module-runtime-config.functions.d.ts.map +1 -0
- package/dist-lib/lib/module-runtime-config.functions.js +107 -0
- package/dist-lib/lib/publish-errors.d.ts +37 -0
- package/dist-lib/lib/publish-errors.d.ts.map +1 -0
- package/dist-lib/lib/publish-errors.js +183 -0
- package/dist-lib/lib/publish-pipeline.d.ts +91 -0
- package/dist-lib/lib/publish-pipeline.d.ts.map +1 -0
- package/dist-lib/lib/publish-pipeline.js +212 -0
- package/dist-lib/lib/publish-readiness.d.ts +42 -0
- package/dist-lib/lib/publish-readiness.d.ts.map +1 -0
- package/dist-lib/lib/publish-readiness.js +102 -0
- package/dist-lib/lib/voorraad-custom-fields.d.ts +22 -0
- package/dist-lib/lib/voorraad-custom-fields.d.ts.map +1 -0
- package/dist-lib/lib/voorraad-custom-fields.js +71 -0
- 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.
|
|
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.
|
|
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",
|