@flowselections/mailbox-orders 1.0.57 → 1.0.59

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 (36) hide show
  1. package/dist-lib/components/CartAssignmentWizard.d.ts +6 -0
  2. package/dist-lib/components/CartAssignmentWizard.d.ts.map +1 -0
  3. package/dist-lib/components/CartAssignmentWizard.js +199 -0
  4. package/dist-lib/components/EmailPreview.d.ts.map +1 -1
  5. package/dist-lib/components/EmailPreview.js +2 -1
  6. package/dist-lib/components/MailboxInboxPage.d.ts +2 -0
  7. package/dist-lib/components/MailboxInboxPage.d.ts.map +1 -0
  8. package/dist-lib/components/MailboxInboxPage.js +120 -0
  9. package/dist-lib/components/MailboxPageFrame.js +1 -1
  10. package/dist-lib/components/MailboxProposalsPage.js +3 -3
  11. package/dist-lib/components/MailboxSkippedPage.d.ts.map +1 -1
  12. package/dist-lib/components/MailboxSkippedPage.js +4 -4
  13. package/dist-lib/components/fs-ui/DataTable.js +3 -3
  14. package/dist-lib/components/settings/ImapAccountsCard.d.ts +2 -0
  15. package/dist-lib/components/settings/ImapAccountsCard.d.ts.map +1 -0
  16. package/dist-lib/components/settings/ImapAccountsCard.js +136 -0
  17. package/dist-lib/components/settings/ImapProfilesCard.d.ts +2 -0
  18. package/dist-lib/components/settings/ImapProfilesCard.d.ts.map +1 -0
  19. package/dist-lib/components/settings/ImapProfilesCard.js +101 -0
  20. package/dist-lib/components/settings/MailboxSettingsCard.d.ts +2 -0
  21. package/dist-lib/components/settings/MailboxSettingsCard.d.ts.map +1 -0
  22. package/dist-lib/components/settings/MailboxSettingsCard.js +85 -0
  23. package/dist-lib/integrations/supabase/client.server.js +1 -1
  24. package/dist-lib/integrations/supabase/types.d.ts +76 -0
  25. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  26. package/dist-lib/lib/imap.functions.d.ts +32852 -0
  27. package/dist-lib/lib/imap.functions.d.ts.map +1 -0
  28. package/dist-lib/lib/imap.functions.js +235 -0
  29. package/dist-lib/lib/mailbox-auto.server.d.ts +9 -0
  30. package/dist-lib/lib/mailbox-auto.server.d.ts.map +1 -0
  31. package/dist-lib/lib/mailbox-auto.server.js +198 -0
  32. package/dist-lib/lib/mailbox-filter-settings.functions.d.ts +2 -2
  33. package/dist-lib/lib/mailbox-filter-settings.functions.js +3 -3
  34. package/dist-lib/lib/mailbox.functions.d.ts +1 -1
  35. package/dist-lib/lib/mailbox.functions.js +6 -6
  36. package/package.json +2 -2
@@ -0,0 +1,235 @@
1
+ // Server functions voor centraal IMAP-beheer (accounts + profielen).
2
+ // Admins beheren accounts & profielen; modules pollen via profile_key.
3
+ import { createServerFn } from "@tanstack/react-start";
4
+ import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware.js";
5
+ import { z } from "zod";
6
+ async function assertAdmin(supabase, userId) {
7
+ const { data } = await supabase.rpc("has_role", { _user_id: userId, _role: "admin" });
8
+ if (!data)
9
+ throw new Error("Alleen admins mogen IMAP-instellingen wijzigen");
10
+ }
11
+ // ─── Accounts ────────────────────────────────────────────────────────────────
12
+ export const listImapAccounts = createServerFn({ method: "GET" })
13
+ .middleware([requireSupabaseAuth])
14
+ .handler(async ({ context }) => {
15
+ const { data, error } = await context.supabase
16
+ .from("imap_accounts")
17
+ .select("id, name, email, host, port, username, use_tls, password_secret_name, is_active, last_tested_at, last_test_ok, last_test_error, created_at, updated_at")
18
+ .order("name");
19
+ if (error)
20
+ throw new Error(error.message);
21
+ return { items: Array.isArray(data) ? data : [] };
22
+ });
23
+ const AccountInput = z.object({
24
+ id: z.string().uuid().optional(),
25
+ name: z.string().min(1).max(255),
26
+ email: z.string().email().max(255).nullable().optional(),
27
+ host: z.string().min(1).max(255),
28
+ port: z.number().int().min(1).max(65535),
29
+ username: z.string().min(1).max(255),
30
+ use_tls: z.boolean(),
31
+ password: z.string().max(1024).optional(), // leeg = bestaand wachtwoord behouden
32
+ password_secret_name: z.string().min(1).max(255).regex(/^[A-Za-z_][A-Za-z0-9_]*$/).nullable().optional(),
33
+ is_active: z.boolean().optional(),
34
+ });
35
+ export const upsertImapAccount = createServerFn({ method: "POST" })
36
+ .middleware([requireSupabaseAuth])
37
+ .inputValidator((input) => AccountInput.parse(input))
38
+ .handler(async ({ data, context }) => {
39
+ await assertAdmin(context.supabase, context.userId);
40
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
41
+ const payload = {
42
+ name: data.name,
43
+ email: data.email ?? null,
44
+ host: data.host,
45
+ port: data.port,
46
+ username: data.username,
47
+ use_tls: data.use_tls,
48
+ password_secret_name: data.password_secret_name ?? null,
49
+ is_active: data.is_active ?? true,
50
+ };
51
+ if (typeof data.password === "string" && data.password.length > 0) {
52
+ payload.password = data.password;
53
+ }
54
+ if (data.id) {
55
+ const { error } = await supabaseAdmin.from("imap_accounts").update(payload).eq("id", data.id);
56
+ if (error)
57
+ throw new Error(error.message);
58
+ return { ok: true, id: data.id };
59
+ }
60
+ const { data: row, error } = await supabaseAdmin.from("imap_accounts").insert(payload).select("id").single();
61
+ if (error)
62
+ throw new Error(error.message);
63
+ return { ok: true, id: row.id };
64
+ });
65
+ // Markeer 1 account als de actieve voor het mailbox_orders profiel
66
+ export const activateImapAccount = createServerFn({ method: "POST" })
67
+ .middleware([requireSupabaseAuth])
68
+ .inputValidator((input) => z.object({ account_id: z.string().uuid() }).parse(input))
69
+ .handler(async ({ data, context }) => {
70
+ await assertAdmin(context.supabase, context.userId);
71
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
72
+ // Zorg dat het standaardprofiel bestaat
73
+ const { data: prof } = await supabaseAdmin
74
+ .from("imap_profiles")
75
+ .upsert({ profile_key: "mailbox_orders", name: "Mailbox Orders", folder: "INBOX", polling_enabled: true, account_id: data.account_id }, { onConflict: "profile_key" })
76
+ .select("id")
77
+ .single();
78
+ // Update voor zekerheid (upsert zou ook account_id moeten zetten, maar PostgREST upsert kan default-merge gedrag hebben)
79
+ if (prof?.id) {
80
+ await supabaseAdmin.from("imap_profiles").update({ account_id: data.account_id, polling_enabled: true }).eq("id", prof.id);
81
+ }
82
+ return { ok: true };
83
+ });
84
+ export const deleteImapAccount = createServerFn({ method: "POST" })
85
+ .middleware([requireSupabaseAuth])
86
+ .inputValidator((input) => z.object({ id: z.string().uuid() }).parse(input))
87
+ .handler(async ({ data, context }) => {
88
+ await assertAdmin(context.supabase, context.userId);
89
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
90
+ const { error } = await supabaseAdmin.from("imap_accounts").delete().eq("id", data.id);
91
+ if (error)
92
+ throw new Error(error.message);
93
+ return { ok: true };
94
+ });
95
+ export const testImapAccount = createServerFn({ method: "POST" })
96
+ .middleware([requireSupabaseAuth])
97
+ .inputValidator((input) => z.object({
98
+ id: z.string().uuid().optional(),
99
+ host: z.string().max(255).optional(),
100
+ port: z.number().int().min(1).max(65535).optional(),
101
+ username: z.string().max(255).optional(),
102
+ use_tls: z.boolean().optional(),
103
+ password: z.string().max(1024).optional(),
104
+ password_secret_name: z.string().max(255).optional(),
105
+ }).parse(input ?? {}))
106
+ .handler(async ({ data, context }) => {
107
+ await assertAdmin(context.supabase, context.userId);
108
+ const body = { action: "test" };
109
+ if (data.id)
110
+ body.account_id = data.id;
111
+ else {
112
+ body.host = data.host;
113
+ body.port = data.port;
114
+ body.username = data.username;
115
+ body.use_tls = data.use_tls;
116
+ body.password = data.password;
117
+ body.password_secret_name = data.password_secret_name;
118
+ }
119
+ try {
120
+ const { data: res, error } = await context.supabase.functions.invoke("mailbox-imap", { body });
121
+ if (error)
122
+ return { ok: false, error: error.message };
123
+ return res;
124
+ }
125
+ catch (e) {
126
+ return { ok: false, error: e?.message ?? "Onbekende fout" };
127
+ }
128
+ });
129
+ // ─── Profielen ───────────────────────────────────────────────────────────────
130
+ export const listImapProfiles = createServerFn({ method: "GET" })
131
+ .middleware([requireSupabaseAuth])
132
+ .handler(async ({ context }) => {
133
+ const { data, error } = await context.supabase
134
+ .from("imap_profiles")
135
+ .select("id, profile_key, name, account_id, folder, polling_enabled, last_polled_at, last_uid_seen, last_error, created_at, updated_at, imap_accounts(id, name, email, host)")
136
+ .order("name");
137
+ if (error)
138
+ throw new Error(error.message);
139
+ return { items: Array.isArray(data) ? data : [] };
140
+ });
141
+ const ProfileInput = z.object({
142
+ id: z.string().uuid().optional(),
143
+ profile_key: z.string().min(1).max(64).regex(/^[a-z0-9_]+$/),
144
+ name: z.string().min(1).max(255),
145
+ account_id: z.string().uuid().nullable(),
146
+ folder: z.string().min(1).max(255),
147
+ polling_enabled: z.boolean(),
148
+ });
149
+ export const upsertImapProfile = createServerFn({ method: "POST" })
150
+ .middleware([requireSupabaseAuth])
151
+ .inputValidator((input) => ProfileInput.parse(input))
152
+ .handler(async ({ data, context }) => {
153
+ await assertAdmin(context.supabase, context.userId);
154
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
155
+ const payload = {
156
+ profile_key: data.profile_key,
157
+ name: data.name,
158
+ account_id: data.account_id,
159
+ folder: data.folder,
160
+ polling_enabled: data.polling_enabled,
161
+ };
162
+ if (data.id) {
163
+ const { error } = await supabaseAdmin.from("imap_profiles").update(payload).eq("id", data.id);
164
+ if (error)
165
+ throw new Error(error.message);
166
+ return { ok: true, id: data.id };
167
+ }
168
+ const { data: row, error } = await supabaseAdmin
169
+ .from("imap_profiles")
170
+ .upsert(payload, { onConflict: "profile_key" })
171
+ .select("id").single();
172
+ if (error)
173
+ throw new Error(error.message);
174
+ return { ok: true, id: row.id };
175
+ });
176
+ export const deleteImapProfile = createServerFn({ method: "POST" })
177
+ .middleware([requireSupabaseAuth])
178
+ .inputValidator((input) => z.object({ id: z.string().uuid() }).parse(input))
179
+ .handler(async ({ data, context }) => {
180
+ await assertAdmin(context.supabase, context.userId);
181
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
182
+ const { error } = await supabaseAdmin.from("imap_profiles").delete().eq("id", data.id);
183
+ if (error)
184
+ throw new Error(error.message);
185
+ return { ok: true };
186
+ });
187
+ export const setProfileAccount = createServerFn({ method: "POST" })
188
+ .middleware([requireSupabaseAuth])
189
+ .inputValidator((input) => z.object({ profile_id: z.string().uuid(), account_id: z.string().uuid().nullable() }).parse(input))
190
+ .handler(async ({ data, context }) => {
191
+ await assertAdmin(context.supabase, context.userId);
192
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
193
+ const { error } = await supabaseAdmin.from("imap_profiles").update({ account_id: data.account_id }).eq("id", data.profile_id);
194
+ if (error)
195
+ throw new Error(error.message);
196
+ return { ok: true };
197
+ });
198
+ export const pollImapProfile = createServerFn({ method: "POST" })
199
+ .middleware([requireSupabaseAuth])
200
+ .inputValidator((input) => z.object({ profile_id: z.string().uuid() }).parse(input))
201
+ .handler(async ({ data, context }) => {
202
+ try {
203
+ const { data: res, error } = await context.supabase.functions.invoke("mailbox-imap", {
204
+ body: { action: "poll", profile_id: data.profile_id },
205
+ });
206
+ if (error)
207
+ return { ok: false, error: error.message, inserted: 0 };
208
+ const r = res;
209
+ // Auto-verwerk binnenkomende mails (alleen mailbox_orders profiel heeft AI-flow)
210
+ if (r.ok && (r.inserted ?? 0) > 0) {
211
+ const { autoProcessNewMessagesInternal } = await import("./mailbox-auto.server.js");
212
+ await autoProcessNewMessagesInternal();
213
+ }
214
+ return r;
215
+ }
216
+ catch (e) {
217
+ return { ok: false, error: e?.message ?? "Onbekende fout", inserted: 0 };
218
+ }
219
+ });
220
+ export const listImapSyncLog = createServerFn({ method: "GET" })
221
+ .middleware([requireSupabaseAuth])
222
+ .inputValidator((input) => z.object({ profile_id: z.string().uuid().optional() }).parse(input ?? {}))
223
+ .handler(async ({ data, context }) => {
224
+ let q = context.supabase
225
+ .from("imap_sync_log")
226
+ .select("id, profile_id, account_id, started_at, finished_at, inserted, ok, error")
227
+ .order("started_at", { ascending: false })
228
+ .limit(50);
229
+ if (data.profile_id)
230
+ q = q.eq("profile_id", data.profile_id);
231
+ const { data: rows, error } = await q;
232
+ if (error)
233
+ throw new Error(error.message);
234
+ return { items: Array.isArray(rows) ? rows : [] };
235
+ });
@@ -0,0 +1,9 @@
1
+ interface ProcessResult {
2
+ processed: number;
3
+ approved: number;
4
+ pending: number;
5
+ failed: number;
6
+ }
7
+ export declare function autoProcessNewMessagesInternal(): Promise<ProcessResult>;
8
+ export {};
9
+ //# sourceMappingURL=mailbox-auto.server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mailbox-auto.server.d.ts","sourceRoot":"","sources":["../../src/lib/mailbox-auto.server.ts"],"names":[],"mappings":"AAIA,UAAU,aAAa;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AA0LD,wBAAsB,8BAA8B,IAAI,OAAO,CAAC,aAAa,CAAC,CA2C7E"}
@@ -0,0 +1,198 @@
1
+ async function parseOneMessage(messageId) {
2
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
3
+ const { data: msg, error: msgErr } = await supabaseAdmin
4
+ .from("mailbox_messages").select("*").eq("id", messageId).single();
5
+ if (msgErr || !msg)
6
+ return { ok: false, error: "Bericht niet gevonden" };
7
+ await supabaseAdmin.from("mailbox_messages")
8
+ .update({ status: "parsing", error_message: null }).eq("id", msg.id);
9
+ try {
10
+ const { data: fields } = await supabaseAdmin
11
+ .from("mailbox_order_template_fields")
12
+ .select("*")
13
+ .order("sort_order", { ascending: true });
14
+ const templateFields = (fields ?? []).filter((f) => f.ai_enabled);
15
+ const [{ data: products }, { data: customers }, { data: suppliers }] = await Promise.all([
16
+ supabaseAdmin.from("products").select("id, product, product_type").order("product").range(0, 1999),
17
+ supabaseAdmin.from("customers").select("id, company_name, email").eq("is_active", true).order("company_name").range(0, 4999),
18
+ supabaseAdmin.from("suppliers").select("id, name").order("name").range(0, 999),
19
+ ]);
20
+ const catalog = (products ?? []).map((p) => ({ id: p.id, name: p.product }));
21
+ const plantTypeSet = new Set();
22
+ (products ?? []).forEach((p) => { if (p.product_type)
23
+ plantTypeSet.add(p.product_type); });
24
+ const plantTypes = Array.from(plantTypeSet).sort().map((v) => ({ id: v, name: v }));
25
+ const spec = templateFields.map((f) => {
26
+ let allowed;
27
+ if (f.source === "database") {
28
+ switch (f.field_type) {
29
+ case "customer":
30
+ allowed = (customers ?? []).map((c) => ({ id: c.id, name: c.company_name }));
31
+ break;
32
+ case "product":
33
+ allowed = catalog;
34
+ break;
35
+ case "plant_type":
36
+ allowed = plantTypes;
37
+ break;
38
+ case "supplier":
39
+ allowed = (suppliers ?? []).map((s) => ({ id: s.id, name: s.name }));
40
+ break;
41
+ }
42
+ }
43
+ return {
44
+ key: f.key,
45
+ label: f.label,
46
+ field_type: f.field_type,
47
+ ai_hint: f.ai_hint,
48
+ allowed,
49
+ };
50
+ });
51
+ const { parseOrderEmail } = await import("./mailbox-ai.server.js");
52
+ const parsed = await parseOrderEmail(msg.body_text ?? msg.body_html ?? "", msg.subject ?? "", spec, catalog);
53
+ const customerId = parsed.fields["customer"]?.value;
54
+ const deliveryDate = parsed.fields["delivery_date"]?.value;
55
+ const notesValue = parsed.fields["notes"]?.value;
56
+ const matchedCustomerId = typeof customerId === "string" && customerId.length === 36 ? customerId : null;
57
+ let finalCustomerId = matchedCustomerId;
58
+ if (!finalCustomerId && msg.from_email) {
59
+ const { data: customer } = await supabaseAdmin
60
+ .from("customers").select("id").eq("email", msg.from_email).maybeSingle();
61
+ finalCustomerId = customer?.id ?? null;
62
+ }
63
+ const { data: proposal, error: propErr } = await supabaseAdmin
64
+ .from("mailbox_order_proposals").insert({
65
+ message_id: msg.id,
66
+ matched_customer_id: finalCustomerId,
67
+ confidence: parsed.overall_confidence,
68
+ delivery_date: typeof deliveryDate === "string" ? deliveryDate : null,
69
+ notes: typeof notesValue === "string" ? notesValue : null,
70
+ parsed_payload: parsed,
71
+ status: "pending",
72
+ }).select("id").single();
73
+ if (propErr)
74
+ throw new Error(propErr.message);
75
+ const fieldRows = Object.entries(parsed.fields).map(([key, v]) => ({
76
+ proposal_id: proposal.id,
77
+ field_key: key,
78
+ value: v.value === undefined ? null : v.value,
79
+ ai_confidence: v.confidence,
80
+ ai_filled: v.value !== null && v.value !== undefined,
81
+ needs_review: v.needs_review,
82
+ }));
83
+ if (fieldRows.length > 0) {
84
+ await supabaseAdmin.from("mailbox_proposal_field_values").insert(fieldRows);
85
+ }
86
+ if (parsed.lines.length > 0) {
87
+ const linesToInsert = parsed.lines.map((l, idx) => ({
88
+ proposal_id: proposal.id,
89
+ product_id: l.product_id,
90
+ raw_product_text: l.raw_product_text,
91
+ quantity: l.quantity,
92
+ unit: l.unit,
93
+ match_confidence: l.match_confidence,
94
+ sort_order: idx,
95
+ }));
96
+ await supabaseAdmin.from("mailbox_proposal_lines").insert(linesToInsert);
97
+ }
98
+ await supabaseAdmin.from("mailbox_messages")
99
+ .update({ status: "parsed" }).eq("id", msg.id);
100
+ return {
101
+ ok: true,
102
+ proposalId: proposal.id,
103
+ parsed,
104
+ customerId: finalCustomerId,
105
+ deliveryDate: typeof deliveryDate === "string" ? deliveryDate : null,
106
+ notes: typeof notesValue === "string" ? notesValue : null,
107
+ };
108
+ }
109
+ catch (e) {
110
+ await supabaseAdmin.from("mailbox_messages")
111
+ .update({ status: "failed", error_message: e?.message ?? "Parsefout" }).eq("id", msg.id);
112
+ return { ok: false, error: e?.message ?? "Parsefout" };
113
+ }
114
+ }
115
+ async function autoApproveProposal(args) {
116
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
117
+ const customFields = {};
118
+ for (const [k, v] of Object.entries(args.parsed.fields)) {
119
+ customFields[k] = v.value ?? null;
120
+ }
121
+ const orderNumber = `MB-${Date.now()}`;
122
+ const { data: order, error: orderErr } = await supabaseAdmin
123
+ .from("orders").insert({
124
+ order_number: orderNumber,
125
+ customer_name: args.customerName,
126
+ customer_id: args.customerId,
127
+ order_date: new Date().toISOString().slice(0, 10),
128
+ delivery_date: args.deliveryDate,
129
+ status: "nieuw",
130
+ total: "0",
131
+ notes: args.notes,
132
+ custom_fields: { ...customFields, source: "mailbox", auto_created: true },
133
+ }).select("id").single();
134
+ if (orderErr || !order)
135
+ return false;
136
+ const items = args.parsed.lines.map((l) => ({
137
+ order_id: order.id,
138
+ product_id: l.product_id,
139
+ product_name: l.raw_product_text || "Onbekend product",
140
+ quantity: Math.max(1, Math.round(l.quantity ?? 1)),
141
+ unit: l.unit ?? "stuks",
142
+ }));
143
+ if (items.length > 0) {
144
+ const { error: itemsErr } = await supabaseAdmin.from("order_items").insert(items);
145
+ if (itemsErr)
146
+ return false;
147
+ }
148
+ await supabaseAdmin.from("mailbox_order_proposals").update({
149
+ status: "approved",
150
+ reviewed_at: new Date().toISOString(),
151
+ created_order_id: order.id,
152
+ }).eq("id", args.proposalId);
153
+ await supabaseAdmin.from("mailbox_messages")
154
+ .update({ status: "converted" }).eq("id", args.messageId);
155
+ return true;
156
+ }
157
+ export async function autoProcessNewMessagesInternal() {
158
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
159
+ const { data: newMsgs } = await supabaseAdmin
160
+ .from("mailbox_messages")
161
+ .select("id")
162
+ .eq("status", "new")
163
+ .order("received_at", { ascending: true })
164
+ .limit(20);
165
+ const result = { processed: 0, approved: 0, pending: 0, failed: 0 };
166
+ for (const m of newMsgs ?? []) {
167
+ result.processed++;
168
+ const r = await parseOneMessage(m.id);
169
+ if (!r.ok || !r.proposalId || !r.parsed) {
170
+ result.failed++;
171
+ continue;
172
+ }
173
+ const hasCustomer = !!r.customerId;
174
+ const hasDelivery = !!r.deliveryDate;
175
+ const hasLines = r.parsed.lines.length > 0;
176
+ if (!hasCustomer || !hasDelivery || !hasLines) {
177
+ result.pending++;
178
+ continue;
179
+ }
180
+ // Haal klantnaam op
181
+ const { data: cust } = await supabaseAdmin
182
+ .from("customers").select("company_name").eq("id", r.customerId).maybeSingle();
183
+ const ok = await autoApproveProposal({
184
+ proposalId: r.proposalId,
185
+ messageId: m.id,
186
+ customerId: r.customerId,
187
+ customerName: cust?.company_name ?? "Onbekende klant",
188
+ deliveryDate: r.deliveryDate,
189
+ notes: r.notes ?? null,
190
+ parsed: r.parsed,
191
+ });
192
+ if (ok)
193
+ result.approved++;
194
+ else
195
+ result.pending++;
196
+ }
197
+ return result;
198
+ }
@@ -107,7 +107,7 @@ export declare const runTestCase: import("@tanstack/start-client-core").Optional
107
107
  }, Promise<{
108
108
  checks: Check[];
109
109
  passed: boolean;
110
- filter: import("./mailbox-filter.js").FilterResult;
110
+ filter: import("../lib/mailbox-filter.js").FilterResult;
111
111
  sender: string;
112
112
  forwarded: boolean;
113
113
  analyzed: boolean;
@@ -119,7 +119,7 @@ export declare const runTestCase: import("@tanstack/start-client-core").Optional
119
119
  } | {
120
120
  checks: Check[];
121
121
  passed: boolean;
122
- filter: import("./mailbox-filter.js").FilterResult;
122
+ filter: import("../lib/mailbox-filter.js").FilterResult;
123
123
  sender: string;
124
124
  forwarded: boolean;
125
125
  analyzed: boolean;
@@ -113,8 +113,8 @@ export const countLikelyFakeProposals = createServerFn({ method: "GET" })
113
113
  .handler(async () => {
114
114
  const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
115
115
  const admin = supabaseAdmin;
116
- const { loadFilterSettings } = await import("./mailbox-ingest.server.js");
117
- const { classifyMessage } = await import("./mailbox-filter.js");
116
+ const { loadFilterSettings } = await import("../lib/mailbox-ingest.server.js");
117
+ const { classifyMessage } = await import("../lib/mailbox-filter.js");
118
118
  const s = await loadFilterSettings(admin);
119
119
  let total = 0, known = 0, wouldSkip = 0, withSignals = 0;
120
120
  for (let from = 0; from < 20000; from += 1000) {
@@ -236,7 +236,7 @@ export const runTestCase = createServerFn({ method: "POST" })
236
236
  const { data: tc, error } = await admin.from("mailbox_test_cases").select("*").eq("id", data.id).maybeSingle();
237
237
  if (error || !tc)
238
238
  throw new Error("Voorbeeld niet gevonden.");
239
- const { dryRunAnalysis } = await import("./mailbox-ingest.server.js");
239
+ const { dryRunAnalysis } = await import("../lib/mailbox-ingest.server.js");
240
240
  const run = await dryRunAnalysis(admin, {
241
241
  subject: tc.subject ?? "",
242
242
  body: tc.body_text ?? "",
@@ -2,7 +2,7 @@ import { type ActiveMailboxStatus } from "./mailbox-active.js";
2
2
  export declare const getActiveOrdersMailbox: import("@tanstack/start-client-core").OptionalFetcher<undefined, undefined, Promise<ActiveMailboxStatus>>;
3
3
  export declare const syncMailboxNow: import("@tanstack/start-client-core").OptionalFetcher<undefined, (input: unknown) => {
4
4
  trigger: "realtime" | "manual";
5
- }, Promise<import("./mailbox-ingest.server.js").IngestRunResult | {
5
+ }, Promise<import("../lib/mailbox-ingest.server.js").IngestRunResult | {
6
6
  ok: false;
7
7
  error: string;
8
8
  processed: 0;
@@ -64,7 +64,7 @@ export const syncMailboxNow = createServerFn({ method: "POST" })
64
64
  if (userErr || !userData?.user) {
65
65
  return { ok: false, error: "unauthorized", processed: 0, inserted: 0, errors: 1 };
66
66
  }
67
- const { runIngest } = await import("./mailbox-ingest.server.js");
67
+ const { runIngest } = await import("../lib/mailbox-ingest.server.js");
68
68
  return await runIngest(data.trigger);
69
69
  }
70
70
  catch (e) {
@@ -95,7 +95,7 @@ export const reparseProposalNow = createServerFn({ method: "POST" })
95
95
  .inputValidator((input) => z.object({ proposalId: z.string().uuid() }).parse(input))
96
96
  .handler(async ({ data }) => {
97
97
  const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
98
- const { reparseProposal } = await import("./mailbox-ingest.server.js");
98
+ const { reparseProposal } = await import("../lib/mailbox-ingest.server.js");
99
99
  return reparseProposal(supabaseAdmin, data.proposalId);
100
100
  });
101
101
  /** Detecteert een gewijzigde AI-agent en herverwerkt openstaande voorstellen. */
@@ -103,7 +103,7 @@ export const refreshProposalsForAgentChange = createServerFn({ method: "POST" })
103
103
  .middleware([requireSupabaseAuth])
104
104
  .handler(async () => {
105
105
  const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
106
- const { reparseChangedAgentProposals } = await import("./mailbox-ingest.server.js");
106
+ const { reparseChangedAgentProposals } = await import("../lib/mailbox-ingest.server.js");
107
107
  return reparseChangedAgentProposals(supabaseAdmin);
108
108
  });
109
109
  // ─── Messages & Proposals ────────────────────────────────────────────────────
@@ -125,7 +125,7 @@ export const parseMessage = createServerFn({ method: "POST" })
125
125
  .inputValidator((input) => z.object({ messageId: z.string().uuid() }).parse(input))
126
126
  .handler(async ({ data }) => {
127
127
  const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
128
- const { createStubProposal } = await import("./mailbox-ingest.server.js");
128
+ const { createStubProposal } = await import("../lib/mailbox-ingest.server.js");
129
129
  const { data: msg, error: msgErr } = await supabaseAdmin
130
130
  .from("mailbox_messages").select("*").eq("id", data.messageId).single();
131
131
  if (msgErr || !msg)
@@ -1189,7 +1189,7 @@ export const analyzeMessageNow = createServerFn({ method: "POST" })
1189
1189
  .inputValidator((input) => z.object({ messageId: z.string().uuid() }).parse(input))
1190
1190
  .handler(async ({ data }) => {
1191
1191
  const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
1192
- const { createStubProposal } = await import("./mailbox-ingest.server.js");
1192
+ const { createStubProposal } = await import("../lib/mailbox-ingest.server.js");
1193
1193
  try {
1194
1194
  const res = await createStubProposal(supabaseAdmin, data.messageId, null, { force: true, trigger: "handmatig" });
1195
1195
  if (res.status === "busy")
@@ -1205,7 +1205,7 @@ export const countOutdatedProposals = createServerFn({ method: "GET" })
1205
1205
  .middleware([requireSupabaseAuth])
1206
1206
  .handler(async () => {
1207
1207
  const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
1208
- const { countOutdatedAgentProposals } = await import("./mailbox-ingest.server.js");
1208
+ const { countOutdatedAgentProposals } = await import("../lib/mailbox-ingest.server.js");
1209
1209
  return { count: await countOutdatedAgentProposals(supabaseAdmin) };
1210
1210
  });
1211
1211
  // ─── Stickervinkje: beschikbaarheid + standaardwaarden ─────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/mailbox-orders",
3
- "version": "1.0.57",
3
+ "version": "1.0.59",
4
4
  "private": false,
5
5
  "sideEffects": false,
6
6
  "type": "module",
@@ -50,7 +50,7 @@
50
50
  "devDependencies": {
51
51
  "@cloudflare/vite-plugin": "^1.25.5",
52
52
  "@eslint/js": "^9.32.0",
53
- "@flowselections/core": "1.0.35",
53
+ "@flowselections/core": "1.0.37",
54
54
  "@flowselections/e-mail-authenticatie-module": "^1.0.8",
55
55
  "@lovable.dev/vite-tanstack-config": "2.26.5",
56
56
  "@rsbuild/core": "^1.0.0",