@flowselections/mailbox-orders 1.0.1 → 1.0.3
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/MailboxInboxPage.d.ts.map +1 -1
- package/dist-lib/components/MailboxInboxPage.js +78 -19
- package/dist-lib/index.d.ts +0 -4
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +1 -7
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +4935 -1303
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +4935 -1303
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +4935 -1303
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +4989 -1258
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +18 -1
- package/dist-lib/lib/mailbox-ai.server.d.ts +1 -1
- package/dist-lib/lib/mailbox-ai.server.d.ts.map +1 -1
- package/dist-lib/lib/mailbox-ai.server.js +6 -106
- package/dist-lib/lib/mailbox-template.functions.d.ts +32826 -11034
- package/dist-lib/lib/mailbox-template.functions.d.ts.map +1 -1
- package/dist-lib/lib/mailbox.functions.d.ts +42670 -15922
- package/dist-lib/lib/mailbox.functions.d.ts.map +1 -1
- package/dist-lib/lib/mailbox.functions.js +162 -173
- package/package.json +7 -6
- package/public/flowselections-assets/template-module/README.md +0 -15
|
@@ -1,85 +1,169 @@
|
|
|
1
1
|
// Server functions voor de Mailbox-bestellingen module
|
|
2
2
|
import { createServerFn } from "@tanstack/react-start";
|
|
3
3
|
import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware";
|
|
4
|
+
import { createClient } from "@supabase/supabase-js";
|
|
4
5
|
import { z } from "zod";
|
|
5
|
-
// ───
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
// ─── Actieve mailbox (uit gedeelde e-mail-auth module) ──────────────────────
|
|
7
|
+
// Geen auth-middleware: de RPC is SECURITY DEFINER en EXECUTE staat ook aan
|
|
8
|
+
// `anon`. Zo blijft de status-banner werken als de bearer-token tijdelijk
|
|
9
|
+
// ontbreekt of verlopen is — anders crasht de UI met een 401.
|
|
10
|
+
export const getActiveOrdersMailbox = createServerFn({ method: "GET" })
|
|
11
|
+
.handler(async () => {
|
|
12
|
+
const url = process.env.SUPABASE_URL;
|
|
13
|
+
const key = process.env.SUPABASE_PUBLISHABLE_KEY;
|
|
14
|
+
if (!url || !key)
|
|
15
|
+
return { connected: false, error: "missing_env" };
|
|
16
|
+
const sb = createClient(url, key, {
|
|
17
|
+
auth: { storage: undefined, persistSession: false, autoRefreshToken: false },
|
|
18
|
+
});
|
|
19
|
+
const { data, error } = await sb.rpc("email_auth_for_purpose", {
|
|
20
|
+
_purpose: "orders",
|
|
21
|
+
_direction: "receive",
|
|
22
|
+
});
|
|
14
23
|
if (error)
|
|
15
|
-
|
|
16
|
-
|
|
24
|
+
return { connected: false, error: error.message };
|
|
25
|
+
const row = Array.isArray(data) ? data[0] : null;
|
|
26
|
+
if (!row)
|
|
27
|
+
return { connected: false };
|
|
28
|
+
return {
|
|
29
|
+
connected: true,
|
|
30
|
+
id: row.id,
|
|
31
|
+
provider: row.provider,
|
|
32
|
+
account_email: row.account_email,
|
|
33
|
+
display_name: (row.display_name ?? null),
|
|
34
|
+
status: (row.status ?? null),
|
|
35
|
+
last_error: (row.last_error ?? null),
|
|
36
|
+
last_verified_at: (row.last_verified_at ?? null),
|
|
37
|
+
needs_reauth: !!row.needs_reauth,
|
|
38
|
+
};
|
|
17
39
|
});
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
polling_enabled: z.boolean(),
|
|
25
|
-
});
|
|
26
|
-
export const saveMailboxSettings = createServerFn({ method: "POST" })
|
|
27
|
-
.middleware([requireSupabaseAuth])
|
|
28
|
-
.inputValidator((input) => SettingsInput.parse(input))
|
|
29
|
-
.handler(async ({ data, context }) => {
|
|
30
|
-
const { data: existing } = await context.supabase
|
|
31
|
-
.from("mailbox_settings").select("id").limit(1).maybeSingle();
|
|
32
|
-
if (existing?.id) {
|
|
33
|
-
const { error } = await context.supabase
|
|
34
|
-
.from("mailbox_settings").update(data).eq("id", existing.id);
|
|
35
|
-
if (error)
|
|
36
|
-
throw new Error(error.message);
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
const { error } = await context.supabase
|
|
40
|
-
.from("mailbox_settings").insert({ ...data, singleton: true });
|
|
41
|
-
if (error)
|
|
42
|
-
throw new Error(error.message);
|
|
40
|
+
// ─── Event ingest helpers ────────────────────────────────────────────────────
|
|
41
|
+
function pickStr(obj, ...keys) {
|
|
42
|
+
for (const k of keys) {
|
|
43
|
+
const v = k.split(".").reduce((a, p) => (a == null ? a : a[p]), obj);
|
|
44
|
+
if (typeof v === "string" && v.length > 0)
|
|
45
|
+
return v;
|
|
43
46
|
}
|
|
44
|
-
return
|
|
45
|
-
});
|
|
46
|
-
async function invokeMailboxImap(supabase, action) {
|
|
47
|
-
const { data, error } = await supabase.functions.invoke("mailbox-imap", {
|
|
48
|
-
body: { action },
|
|
49
|
-
});
|
|
50
|
-
if (error)
|
|
51
|
-
throw new Error(error.message ?? "Edge function aanroep mislukt");
|
|
52
|
-
return data;
|
|
47
|
+
return null;
|
|
53
48
|
}
|
|
54
|
-
|
|
55
|
-
.
|
|
56
|
-
.
|
|
57
|
-
|
|
58
|
-
|
|
49
|
+
async function ingestSingleEvent(supabaseAdmin, connectionId, ev) {
|
|
50
|
+
const p = ev.payload ?? {};
|
|
51
|
+
const externalId = ev.external_id ?? pickStr(p, "message_id", "messageId", "id") ?? ev.id;
|
|
52
|
+
// Skip als deze external_id al bestaat
|
|
53
|
+
if (externalId) {
|
|
54
|
+
const { data: existing } = await supabaseAdmin
|
|
55
|
+
.from("mailbox_messages")
|
|
56
|
+
.select("id")
|
|
57
|
+
.eq("external_id", externalId)
|
|
58
|
+
.maybeSingle();
|
|
59
|
+
if (existing?.id) {
|
|
60
|
+
return { inserted: false, messageId: existing.id };
|
|
61
|
+
}
|
|
59
62
|
}
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
const fromEmail = pickStr(p, "from_email", "fromEmail", "from.address", "from.email", "sender.address") ?? "";
|
|
64
|
+
const fromName = pickStr(p, "from_name", "fromName", "from.name", "sender.name");
|
|
65
|
+
const subject = pickStr(p, "subject", "title") ?? "";
|
|
66
|
+
const bodyText = pickStr(p, "body_text", "bodyText", "text", "body.text", "snippet");
|
|
67
|
+
const bodyHtml = pickStr(p, "body_html", "bodyHtml", "html", "body.html");
|
|
68
|
+
const receivedAt = pickStr(p, "received_at", "receivedAt", "date", "internalDate") ?? ev.received_at ?? new Date().toISOString();
|
|
69
|
+
const { data: inserted, error: insErr } = await supabaseAdmin
|
|
70
|
+
.from("mailbox_messages")
|
|
71
|
+
.insert({
|
|
72
|
+
external_id: externalId,
|
|
73
|
+
message_id: pickStr(p, "message_id", "messageId") ?? externalId,
|
|
74
|
+
from_email: fromEmail,
|
|
75
|
+
from_name: fromName,
|
|
76
|
+
subject,
|
|
77
|
+
body_text: bodyText,
|
|
78
|
+
body_html: bodyHtml,
|
|
79
|
+
received_at: receivedAt,
|
|
80
|
+
status: "new",
|
|
81
|
+
})
|
|
82
|
+
.select("id")
|
|
83
|
+
.single();
|
|
84
|
+
if (insErr) {
|
|
85
|
+
// Duplicate race → markeer alsnog als consumed
|
|
86
|
+
if (insErr.code === "23505") {
|
|
87
|
+
return { inserted: false, messageId: null };
|
|
88
|
+
}
|
|
89
|
+
throw new Error(insErr.message);
|
|
62
90
|
}
|
|
91
|
+
return { inserted: true, messageId: inserted.id };
|
|
92
|
+
}
|
|
93
|
+
// ─── Ingest één specifiek event (vanuit realtime subscription) ───────────────
|
|
94
|
+
export const ingestEmailEvent = createServerFn({ method: "POST" })
|
|
95
|
+
.middleware([requireSupabaseAuth])
|
|
96
|
+
.inputValidator((input) => z.object({ eventId: z.string().uuid() }).parse(input))
|
|
97
|
+
.handler(async ({ data }) => {
|
|
98
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
99
|
+
const { data: ev, error: evErr } = await supabaseAdmin
|
|
100
|
+
.from("email_auth_events")
|
|
101
|
+
.select("id, connection_id, external_id, payload, received_at, consumed_at")
|
|
102
|
+
.eq("id", data.eventId)
|
|
103
|
+
.maybeSingle();
|
|
104
|
+
if (evErr)
|
|
105
|
+
throw new Error(evErr.message);
|
|
106
|
+
if (!ev)
|
|
107
|
+
return { ok: false, error: "Event niet gevonden" };
|
|
108
|
+
if (ev.consumed_at)
|
|
109
|
+
return { ok: true, inserted: 0, autoProcessed: 0, autoApproved: 0 };
|
|
110
|
+
const res = await ingestSingleEvent(supabaseAdmin, ev.connection_id, ev);
|
|
111
|
+
await supabaseAdmin
|
|
112
|
+
.from("email_auth_events")
|
|
113
|
+
.update({ consumed_at: new Date().toISOString() })
|
|
114
|
+
.eq("id", ev.id);
|
|
115
|
+
// AI-verwerking uitgeschakeld — wacht op externe model-koppeling
|
|
116
|
+
const autoProcessed = 0;
|
|
117
|
+
const autoApproved = 0;
|
|
118
|
+
return { ok: true, inserted: res.inserted ? 1 : 0, autoProcessed, autoApproved };
|
|
63
119
|
});
|
|
64
|
-
// ───
|
|
65
|
-
export const
|
|
120
|
+
// ─── Backfill: haal alle nog niet-consumed events op voor 'orders/receive' ───
|
|
121
|
+
export const backfillRecentEvents = createServerFn({ method: "POST" })
|
|
66
122
|
.middleware([requireSupabaseAuth])
|
|
67
123
|
.handler(async ({ context }) => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
124
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
125
|
+
// Welke connection is gekoppeld aan orders/receive?
|
|
126
|
+
const { data: active, error: actErr } = await context.supabase.rpc("email_auth_for_purpose", {
|
|
127
|
+
_purpose: "orders",
|
|
128
|
+
_direction: "receive",
|
|
129
|
+
});
|
|
130
|
+
if (actErr)
|
|
131
|
+
return { ok: false, error: actErr.message };
|
|
132
|
+
const row = Array.isArray(active) ? active[0] : null;
|
|
133
|
+
if (!row?.id)
|
|
134
|
+
return { ok: false, error: "Geen actieve koppeling voor orders/receive" };
|
|
135
|
+
const { data: events, error: evErr } = await supabaseAdmin
|
|
136
|
+
.from("email_auth_events")
|
|
137
|
+
.select("id, connection_id, external_id, payload, received_at")
|
|
138
|
+
.eq("connection_id", row.id)
|
|
139
|
+
.is("consumed_at", null)
|
|
140
|
+
.order("received_at", { ascending: true })
|
|
141
|
+
.limit(200);
|
|
142
|
+
if (evErr)
|
|
143
|
+
return { ok: false, error: evErr.message };
|
|
144
|
+
let inserted = 0;
|
|
145
|
+
const consumedIds = [];
|
|
146
|
+
for (const ev of events ?? []) {
|
|
147
|
+
try {
|
|
148
|
+
const res = await ingestSingleEvent(supabaseAdmin, ev.connection_id, ev);
|
|
149
|
+
if (res.inserted)
|
|
150
|
+
inserted++;
|
|
151
|
+
consumedIds.push(ev.id);
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
// Fout → niet markeren als consumed, volgende ronde opnieuw proberen
|
|
77
155
|
}
|
|
78
|
-
return { ok: !!res.ok, inserted: res.inserted ?? 0, autoProcessed, autoApproved, error: res.error };
|
|
79
156
|
}
|
|
80
|
-
|
|
81
|
-
|
|
157
|
+
if (consumedIds.length > 0) {
|
|
158
|
+
await supabaseAdmin
|
|
159
|
+
.from("email_auth_events")
|
|
160
|
+
.update({ consumed_at: new Date().toISOString() })
|
|
161
|
+
.in("id", consumedIds);
|
|
82
162
|
}
|
|
163
|
+
// AI-verwerking uitgeschakeld — wacht op externe model-koppeling
|
|
164
|
+
const autoProcessed = 0;
|
|
165
|
+
const autoApproved = 0;
|
|
166
|
+
return { ok: true, inserted, autoProcessed, autoApproved };
|
|
83
167
|
});
|
|
84
168
|
// ─── Messages & Proposals ────────────────────────────────────────────────────
|
|
85
169
|
export const listMessages = createServerFn({ method: "GET" })
|
|
@@ -103,112 +187,19 @@ export const parseMessage = createServerFn({ method: "POST" })
|
|
|
103
187
|
.from("mailbox_messages").select("*").eq("id", data.messageId).single();
|
|
104
188
|
if (msgErr || !msg)
|
|
105
189
|
throw new Error("Bericht niet gevonden");
|
|
190
|
+
// AI-parsing is uitgeschakeld tot de externe model-koppeling klaar is.
|
|
191
|
+
// Het bericht blijft bewaard; status gaat naar "failed" met een duidelijke melding.
|
|
192
|
+
const msg2 = msg;
|
|
106
193
|
await supabaseAdmin.from("mailbox_messages")
|
|
107
|
-
.update({
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const [{ data: products }, { data: customers }, { data: suppliers }] = await Promise.all([
|
|
117
|
-
supabaseAdmin.from("products").select("id, product, product_type").order("product").range(0, 1999),
|
|
118
|
-
supabaseAdmin.from("customers").select("id, company_name, email").eq("is_active", true).order("company_name").range(0, 4999),
|
|
119
|
-
supabaseAdmin.from("suppliers").select("id, name").order("name").range(0, 999),
|
|
120
|
-
]);
|
|
121
|
-
const catalog = (products ?? []).map((p) => ({ id: p.id, name: p.product }));
|
|
122
|
-
const plantTypeSet = new Set();
|
|
123
|
-
(products ?? []).forEach((p) => { if (p.product_type)
|
|
124
|
-
plantTypeSet.add(p.product_type); });
|
|
125
|
-
const plantTypes = Array.from(plantTypeSet).sort().map((v) => ({ id: v, name: v }));
|
|
126
|
-
// Bouw spec met allowed values per database-veld
|
|
127
|
-
const spec = templateFields.map((f) => {
|
|
128
|
-
let allowed;
|
|
129
|
-
if (f.source === "database") {
|
|
130
|
-
switch (f.field_type) {
|
|
131
|
-
case "customer":
|
|
132
|
-
allowed = (customers ?? []).map((c) => ({ id: c.id, name: c.company_name }));
|
|
133
|
-
break;
|
|
134
|
-
case "product":
|
|
135
|
-
allowed = catalog;
|
|
136
|
-
break;
|
|
137
|
-
case "plant_type":
|
|
138
|
-
allowed = plantTypes;
|
|
139
|
-
break;
|
|
140
|
-
case "supplier":
|
|
141
|
-
allowed = (suppliers ?? []).map((s) => ({ id: s.id, name: s.name }));
|
|
142
|
-
break;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
return {
|
|
146
|
-
key: f.key,
|
|
147
|
-
label: f.label,
|
|
148
|
-
field_type: f.field_type,
|
|
149
|
-
ai_hint: f.ai_hint,
|
|
150
|
-
allowed,
|
|
151
|
-
};
|
|
152
|
-
});
|
|
153
|
-
const { parseOrderEmail } = await import("./mailbox-ai.server");
|
|
154
|
-
const parsed = await parseOrderEmail(msg.body_text ?? msg.body_html ?? "", msg.subject ?? "", spec, catalog);
|
|
155
|
-
// Backwards-compat: leid klant/leverdatum/notes af voor de bestaande kolommen
|
|
156
|
-
const customerId = parsed.fields["customer"]?.value;
|
|
157
|
-
const deliveryDate = parsed.fields["delivery_date"]?.value;
|
|
158
|
-
const notesValue = parsed.fields["notes"]?.value;
|
|
159
|
-
const matchedCustomerId = typeof customerId === "string" && customerId.length === 36 ? customerId : null;
|
|
160
|
-
// Fallback: match op e-mail als AI niets vond
|
|
161
|
-
let finalCustomerId = matchedCustomerId;
|
|
162
|
-
if (!finalCustomerId && msg.from_email) {
|
|
163
|
-
const { data: customer } = await supabaseAdmin
|
|
164
|
-
.from("customers").select("id").eq("email", msg.from_email).maybeSingle();
|
|
165
|
-
finalCustomerId = customer?.id ?? null;
|
|
166
|
-
}
|
|
167
|
-
const { data: proposal, error: propErr } = await supabaseAdmin
|
|
168
|
-
.from("mailbox_order_proposals").insert({
|
|
169
|
-
message_id: msg.id,
|
|
170
|
-
matched_customer_id: finalCustomerId,
|
|
171
|
-
confidence: parsed.overall_confidence,
|
|
172
|
-
delivery_date: typeof deliveryDate === "string" ? deliveryDate : null,
|
|
173
|
-
notes: typeof notesValue === "string" ? notesValue : null,
|
|
174
|
-
parsed_payload: parsed,
|
|
175
|
-
status: "pending",
|
|
176
|
-
}).select("id").single();
|
|
177
|
-
if (propErr)
|
|
178
|
-
throw new Error(propErr.message);
|
|
179
|
-
// Sla alle veldwaarden op
|
|
180
|
-
const fieldRows = Object.entries(parsed.fields).map(([key, v]) => ({
|
|
181
|
-
proposal_id: proposal.id,
|
|
182
|
-
field_key: key,
|
|
183
|
-
value: v.value === undefined ? null : v.value,
|
|
184
|
-
ai_confidence: v.confidence,
|
|
185
|
-
ai_filled: v.value !== null && v.value !== undefined,
|
|
186
|
-
needs_review: v.needs_review,
|
|
187
|
-
}));
|
|
188
|
-
if (fieldRows.length > 0) {
|
|
189
|
-
await supabaseAdmin.from("mailbox_proposal_field_values").insert(fieldRows);
|
|
190
|
-
}
|
|
191
|
-
if (parsed.lines.length > 0) {
|
|
192
|
-
const linesToInsert = parsed.lines.map((l, idx) => ({
|
|
193
|
-
proposal_id: proposal.id,
|
|
194
|
-
product_id: l.product_id,
|
|
195
|
-
raw_product_text: l.raw_product_text,
|
|
196
|
-
quantity: l.quantity,
|
|
197
|
-
unit: l.unit,
|
|
198
|
-
match_confidence: l.match_confidence,
|
|
199
|
-
sort_order: idx,
|
|
200
|
-
}));
|
|
201
|
-
await supabaseAdmin.from("mailbox_proposal_lines").insert(linesToInsert);
|
|
202
|
-
}
|
|
203
|
-
await supabaseAdmin.from("mailbox_messages")
|
|
204
|
-
.update({ status: "parsed" }).eq("id", msg.id);
|
|
205
|
-
return { ok: true, proposalId: proposal.id };
|
|
206
|
-
}
|
|
207
|
-
catch (e) {
|
|
208
|
-
await supabaseAdmin.from("mailbox_messages")
|
|
209
|
-
.update({ status: "failed", error_message: e?.message ?? "Parsefout" }).eq("id", msg.id);
|
|
210
|
-
return { ok: false, error: e?.message ?? "Parsefout" };
|
|
211
|
-
}
|
|
194
|
+
.update({
|
|
195
|
+
status: "failed",
|
|
196
|
+
error_message: "AI-parsing is tijdelijk uitgeschakeld — wacht op externe model-koppeling.",
|
|
197
|
+
})
|
|
198
|
+
.eq("id", msg2.id);
|
|
199
|
+
return {
|
|
200
|
+
ok: false,
|
|
201
|
+
error: "AI-parsing is tijdelijk uitgeschakeld — wacht op externe model-koppeling.",
|
|
202
|
+
};
|
|
212
203
|
});
|
|
213
204
|
export const listProposals = createServerFn({ method: "GET" })
|
|
214
205
|
.middleware([requireSupabaseAuth])
|
|
@@ -250,7 +241,6 @@ export const approveProposal = createServerFn({ method: "POST" })
|
|
|
250
241
|
}).parse(input))
|
|
251
242
|
.handler(async ({ data, context }) => {
|
|
252
243
|
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
253
|
-
// Bouw custom_fields map vanuit alle templatevelden
|
|
254
244
|
const customFields = {};
|
|
255
245
|
(data.fieldValues ?? []).forEach((fv) => {
|
|
256
246
|
customFields[fv.field_key] = fv.value;
|
|
@@ -280,7 +270,6 @@ export const approveProposal = createServerFn({ method: "POST" })
|
|
|
280
270
|
const { error: itemsErr } = await supabaseAdmin.from("order_items").insert(items);
|
|
281
271
|
if (itemsErr)
|
|
282
272
|
throw new Error(itemsErr.message);
|
|
283
|
-
// Sla finale veldwaarden op (overschrijft AI-waarden)
|
|
284
273
|
if (data.fieldValues && data.fieldValues.length > 0) {
|
|
285
274
|
for (const fv of data.fieldValues) {
|
|
286
275
|
await supabaseAdmin.from("mailbox_proposal_field_values").upsert({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flowselections/mailbox-orders",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"type": "module",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"@cloudflare/vite-plugin": "^1.25.5",
|
|
47
47
|
"@eslint/js": "^9.32.0",
|
|
48
48
|
"@flowselections/core": "^1.0.11",
|
|
49
|
-
"@lovable.dev/vite-tanstack-config": "
|
|
49
|
+
"@lovable.dev/vite-tanstack-config": "2.5.3",
|
|
50
50
|
"@rsbuild/core": "^1.0.0",
|
|
51
51
|
"@tailwindcss/vite": "^4.2.1",
|
|
52
52
|
"@tanstack/react-query": "^5.83.0",
|
|
@@ -71,24 +71,25 @@
|
|
|
71
71
|
"typescript": "^5.8.3",
|
|
72
72
|
"typescript-eslint": "^8.56.1",
|
|
73
73
|
"vite": "^7.3.1",
|
|
74
|
-
"vite-tsconfig-paths": "^6.0.2"
|
|
74
|
+
"vite-tsconfig-paths": "^6.0.2",
|
|
75
|
+
"nitro": "3.0.260603-beta"
|
|
75
76
|
},
|
|
76
77
|
"flowselections": {
|
|
77
78
|
"moduleId": "Mailbox",
|
|
78
79
|
"pages": [
|
|
79
80
|
{
|
|
80
81
|
"export": "MailboxInboxPage",
|
|
81
|
-
"route": "
|
|
82
|
+
"route": "mailbox-orders",
|
|
82
83
|
"title": "Inbox"
|
|
83
84
|
},
|
|
84
85
|
{
|
|
85
86
|
"export": "MailboxProposalsPage",
|
|
86
|
-
"route": "
|
|
87
|
+
"route": "mailbox-orders/voorstellen",
|
|
87
88
|
"title": "Voorstellen"
|
|
88
89
|
},
|
|
89
90
|
{
|
|
90
91
|
"export": "MailboxProcessedPage",
|
|
91
|
-
"route": "
|
|
92
|
+
"route": "mailbox-orders/verwerkt",
|
|
92
93
|
"title": "Verwerkt"
|
|
93
94
|
}
|
|
94
95
|
]
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# Module assets
|
|
2
|
-
|
|
3
|
-
Raster-afbeeldingen (PNG, JPEG) die deze module nodig heeft, worden hier geplaatst.
|
|
4
|
-
|
|
5
|
-
De setup CLI kopieert de inhoud van `public/` naar:
|
|
6
|
-
`shell/public/flowselections-assets/<module-id>/`
|
|
7
|
-
|
|
8
|
-
In de module-code verwijs je naar de afbeelding via een pad-string (GEEN import):
|
|
9
|
-
```ts
|
|
10
|
-
const MODULE_LOGO = '/flowselections-assets/<module-id>/logo.png'
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
Vervang `<module-id>` door de id die je in `src/index.ts` als moduleConfig-id hebt opgegeven.
|
|
14
|
-
|
|
15
|
-
Zie ook: AGENTS.md sectie "Afbeeldingen"
|