@flowselections/mailbox-orders 1.0.29 → 1.0.32

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 (61) hide show
  1. package/dist-lib/components/MailboxProcessedPage.js +1 -1
  2. package/dist-lib/components/MailboxProposalsPage.d.ts.map +1 -1
  3. package/dist-lib/components/MailboxProposalsPage.js +202 -122
  4. package/dist-lib/components/MailboxSkippedPage.d.ts +2 -0
  5. package/dist-lib/components/MailboxSkippedPage.d.ts.map +1 -0
  6. package/dist-lib/components/MailboxSkippedPage.js +71 -0
  7. package/dist-lib/components/settings/MailboxFilterSettingsCard.d.ts +2 -0
  8. package/dist-lib/components/settings/MailboxFilterSettingsCard.d.ts.map +1 -0
  9. package/dist-lib/components/settings/MailboxFilterSettingsCard.js +78 -0
  10. package/dist-lib/components/settings/MailboxTestSetCard.d.ts +2 -0
  11. package/dist-lib/components/settings/MailboxTestSetCard.d.ts.map +1 -0
  12. package/dist-lib/components/settings/MailboxTestSetCard.js +69 -0
  13. package/dist-lib/index.d.ts +1 -0
  14. package/dist-lib/index.d.ts.map +1 -1
  15. package/dist-lib/index.js +8 -2
  16. package/dist-lib/integrations/supabase/client.server.js +1 -1
  17. package/dist-lib/integrations/supabase/types.d.ts +864 -1
  18. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  19. package/dist-lib/lib/mailbox-ai.server.d.ts +33 -12
  20. package/dist-lib/lib/mailbox-ai.server.d.ts.map +1 -1
  21. package/dist-lib/lib/mailbox-ai.server.js +47 -17
  22. package/dist-lib/lib/mailbox-filter-settings.functions.d.ts +143 -0
  23. package/dist-lib/lib/mailbox-filter-settings.functions.d.ts.map +1 -0
  24. package/dist-lib/lib/mailbox-filter-settings.functions.js +241 -0
  25. package/dist-lib/lib/mailbox-filter.d.ts +34 -0
  26. package/dist-lib/lib/mailbox-filter.d.ts.map +1 -0
  27. package/dist-lib/lib/mailbox-filter.js +121 -0
  28. package/dist-lib/lib/mailbox-ingest.server.d.ts +114 -17
  29. package/dist-lib/lib/mailbox-ingest.server.d.ts.map +1 -1
  30. package/dist-lib/lib/mailbox-ingest.server.js +594 -266
  31. package/dist-lib/lib/mailbox-match.d.ts +28 -0
  32. package/dist-lib/lib/mailbox-match.d.ts.map +1 -0
  33. package/dist-lib/lib/mailbox-match.js +74 -0
  34. package/dist-lib/lib/mailbox-units.d.ts +18 -0
  35. package/dist-lib/lib/mailbox-units.d.ts.map +1 -0
  36. package/dist-lib/lib/mailbox-units.js +80 -0
  37. package/dist-lib/lib/mailbox.functions.d.ts +75 -1
  38. package/dist-lib/lib/mailbox.functions.d.ts.map +1 -1
  39. package/dist-lib/lib/mailbox.functions.js +174 -29
  40. package/package.json +6 -1
  41. package/dist-lib/components/CartAssignmentWizard.d.ts +0 -6
  42. package/dist-lib/components/CartAssignmentWizard.d.ts.map +0 -1
  43. package/dist-lib/components/CartAssignmentWizard.js +0 -199
  44. package/dist-lib/components/MailboxInboxPage.d.ts +0 -2
  45. package/dist-lib/components/MailboxInboxPage.d.ts.map +0 -1
  46. package/dist-lib/components/MailboxInboxPage.js +0 -120
  47. package/dist-lib/components/settings/ImapAccountsCard.d.ts +0 -2
  48. package/dist-lib/components/settings/ImapAccountsCard.d.ts.map +0 -1
  49. package/dist-lib/components/settings/ImapAccountsCard.js +0 -136
  50. package/dist-lib/components/settings/ImapProfilesCard.d.ts +0 -2
  51. package/dist-lib/components/settings/ImapProfilesCard.d.ts.map +0 -1
  52. package/dist-lib/components/settings/ImapProfilesCard.js +0 -101
  53. package/dist-lib/components/settings/MailboxSettingsCard.d.ts +0 -2
  54. package/dist-lib/components/settings/MailboxSettingsCard.d.ts.map +0 -1
  55. package/dist-lib/components/settings/MailboxSettingsCard.js +0 -85
  56. package/dist-lib/lib/imap.functions.d.ts +0 -32852
  57. package/dist-lib/lib/imap.functions.d.ts.map +0 -1
  58. package/dist-lib/lib/imap.functions.js +0 -235
  59. package/dist-lib/lib/mailbox-auto.server.d.ts +0 -9
  60. package/dist-lib/lib/mailbox-auto.server.d.ts.map +0 -1
  61. package/dist-lib/lib/mailbox-auto.server.js +0 -198
@@ -20,16 +20,30 @@ export interface ParsedFieldValue {
20
20
  needs_review: boolean;
21
21
  reason?: string;
22
22
  }
23
+ export interface ParsedLine {
24
+ raw_product_text: string;
25
+ code: string | null;
26
+ name_in_mail: string | null;
27
+ product_id: string | null;
28
+ plant_type: string | null;
29
+ quantity: number | null;
30
+ unit_text: string | null;
31
+ unit: "stuk" | "fust" | "laag" | "kar" | "onbekend" | null;
32
+ pieces_per_unit: number | null;
33
+ price: number | null;
34
+ currency: string | null;
35
+ price_per: "stuk" | "eenheid" | null;
36
+ match_confidence: number;
37
+ confidence: {
38
+ product: number;
39
+ quantity: number;
40
+ unit: number;
41
+ price: number;
42
+ };
43
+ }
23
44
  export interface ParsedOrder {
24
45
  fields: Record<string, ParsedFieldValue>;
25
- lines: Array<{
26
- raw_product_text: string;
27
- product_id: string | null;
28
- plant_type: string | null;
29
- quantity: number | null;
30
- unit: string | null;
31
- match_confidence: number;
32
- }>;
46
+ lines: ParsedLine[];
33
47
  validation: {
34
48
  missing: string[];
35
49
  uncertain: string[];
@@ -37,12 +51,20 @@ export interface ParsedOrder {
37
51
  };
38
52
  overall_confidence: number;
39
53
  }
54
+ export interface AiCandidate {
55
+ id: string;
56
+ name: string;
57
+ codes: string[];
58
+ belading: string | null;
59
+ }
40
60
  export interface AiParseMeta {
41
61
  providerSlug: string;
42
62
  modelSlug: string;
43
63
  modelName: string;
44
64
  durationMs: number;
45
65
  fingerprint: string;
66
+ inputTokens: number | null;
67
+ outputTokens: number | null;
46
68
  }
47
69
  /** Huidige vingerafdruk van de gekoppelde AI-agent, of null als AI niet beschikbaar is. */
48
70
  export declare function currentAiFingerprint(admin: AdminClient): Promise<string | null>;
@@ -66,10 +88,7 @@ export declare function parseOrderEmail(admin: AdminClient, args: {
66
88
  bodyText: string;
67
89
  subject: string;
68
90
  templateFields: TemplateFieldSpec[];
69
- productCatalog: Array<{
70
- id: string;
71
- name: string;
72
- }>;
91
+ productCatalog: AiCandidate[];
73
92
  deliveryLocations?: Array<{
74
93
  id: string;
75
94
  name: string;
@@ -77,6 +96,8 @@ export declare function parseOrderEmail(admin: AdminClient, args: {
77
96
  plantTypes?: string[];
78
97
  requiredFields?: string[];
79
98
  systemPrompt?: string | null;
99
+ now?: Date;
100
+ maxAttemptsPerModel?: number;
80
101
  }): Promise<{
81
102
  parsed: ParsedOrder;
82
103
  meta: AiParseMeta;
@@ -1 +1 @@
1
- {"version":3,"file":"mailbox-ai.server.d.ts","sourceRoot":"","sources":["../../src/lib/mailbox-ai.server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mailbox-ai.server.d.ts","sourceRoot":"","sources":["../../src/lib/mailbox-ai.server.ts"],"names":[],"mappings":"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"}
@@ -131,9 +131,19 @@ const OUTPUT_CONTRACT = [
131
131
  "Antwoord uitsluitend met JSON, zonder uitleg en zonder markdown.",
132
132
  "Structuur:",
133
133
  '{"fields":{"<key>":{"value":<any|null>,"confidence":0..1,"needs_review":true|false}},',
134
- '"lines":[{"raw_product_text":string,"product_id":string|null,"plant_type":string|null,"quantity":number|null,"unit":"fust"|"planten"|null,"match_confidence":0..1}],',
134
+ '"lines":[{"raw_text":string,"code":string|null,"name_in_mail":string|null,"product_id":string|null,"plant_type":string|null,',
135
+ '"quantity":number|null,"unit_text":string|null,"unit":"stuk"|"fust"|"laag"|"kar"|"onbekend","pieces_per_unit":number|null,',
136
+ '"price":number|null,"currency":string|null,"price_per":"stuk"|"eenheid"|null,',
137
+ '"confidence":{"product":0..1,"quantity":0..1,"unit":0..1,"price":0..1}}],',
135
138
  '"validation":{"missing":[string],"uncertain":[string],"notes":string|null},',
136
139
  '"overall_confidence":0..1}',
140
+ "Regels per bestelregel:",
141
+ "- raw_text: de letterlijke regel uit de mail. code: artikelcode/EAN/VBN precies zoals in de mail. name_in_mail: productnaam zoals in de mail.",
142
+ "- quantity en unit_text precies zoals in de mail. Reken NIET om naar planten. unit: tray/doos/kist/fust = fust; laag/plank = laag; kar/container = kar; stuks/planten = stuk; anders onbekend.",
143
+ "- pieces_per_unit alleen als de mail dat noemt (bijv. '20 stuks per fust').",
144
+ "- price, currency en price_per ('stuk' bij 'per stuk', 'eenheid' bij 'per fust/tray/kar'). Geen prijs in de mail: null.",
145
+ "- product_id alleen uit product_candidates; twijfel je, dan null.",
146
+ "- Het veld 'reference' is het order-, referentie- of PO-nummer van de klant; letterlijk overnemen.",
137
147
  "Datums altijd als YYYY-MM-DD.",
138
148
  ].join("\n");
139
149
  const DAY_NAMES = ["zondag", "maandag", "dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag"];
@@ -181,12 +191,13 @@ function buildPrompt(bodyText, subject, templateFields, productCatalog, extra) {
181
191
  const system = `${base}\n\n${OUTPUT_CONTRACT}`;
182
192
  const user = JSON.stringify({
183
193
  email: { subject, body: (bodyText ?? "").slice(0, 20000) },
194
+ // Relatieve datums ('morgen', 'woensdag') rekenen vanaf de ontvangstdatum van de mail.
184
195
  date_context: buildDateContext(extra.now ?? new Date()),
185
196
  required_fields: extra.requiredFields ?? [],
186
197
  fields: fieldSpec,
187
198
  delivery_locations: extra.deliveryLocations?.slice(0, 100) ?? [],
188
199
  plant_types: (extra.plantTypes ?? []).slice(0, 400),
189
- product_catalog: productCatalog.slice(0, 250),
200
+ product_candidates: productCatalog,
190
201
  });
191
202
  return { system, user };
192
203
  }
@@ -195,6 +206,8 @@ function strList(v) {
195
206
  ? v.map((x) => (typeof x === "string" ? x.slice(0, 120) : "")).filter(Boolean).slice(0, 50)
196
207
  : [];
197
208
  }
209
+ const str = (v, max = 200) => typeof v === "string" && v.trim() ? v.trim().slice(0, max) : null;
210
+ const UNIT_KINDS = new Set(["stuk", "fust", "laag", "kar", "onbekend"]);
198
211
  function normalize(raw, productCatalog) {
199
212
  const allowedProductIds = new Set(productCatalog.map((product) => product.id));
200
213
  const fields = {};
@@ -212,17 +225,32 @@ function normalize(raw, productCatalog) {
212
225
  }
213
226
  const rawLines = Array.isArray(raw?.lines) ? raw.lines : [];
214
227
  const lines = rawLines
215
- .map((l) => ({
216
- raw_product_text: typeof l?.raw_product_text === "string" ? l.raw_product_text.slice(0, 500) : "",
217
- product_id: typeof l?.product_id === "string" && allowedProductIds.has(l.product_id)
218
- ? l.product_id
219
- : null,
220
- plant_type: typeof l?.plant_type === "string" && l.plant_type.trim() ? l.plant_type.trim().slice(0, 200) : null,
221
- quantity: num(l?.quantity),
222
- unit: typeof l?.unit === "string" ? l.unit.slice(0, 40) : null,
223
- match_confidence: clamp01(l?.match_confidence),
224
- }))
225
- .filter((l) => l.raw_product_text || l.product_id);
228
+ .map((l) => {
229
+ const c = l?.confidence && typeof l.confidence === "object" ? l.confidence : {};
230
+ const unitRaw = typeof l?.unit === "string" ? l.unit.toLowerCase() : null;
231
+ return {
232
+ raw_product_text: str(l?.raw_text ?? l?.raw_product_text, 500) ?? "",
233
+ code: str(l?.code, 60),
234
+ name_in_mail: str(l?.name_in_mail, 200),
235
+ product_id: typeof l?.product_id === "string" && allowedProductIds.has(l.product_id) ? l.product_id : null,
236
+ plant_type: str(l?.plant_type),
237
+ quantity: num(l?.quantity),
238
+ unit_text: str(l?.unit_text, 60),
239
+ unit: unitRaw && UNIT_KINDS.has(unitRaw) ? unitRaw : null,
240
+ pieces_per_unit: num(l?.pieces_per_unit),
241
+ price: num(l?.price),
242
+ currency: str(l?.currency, 8),
243
+ price_per: l?.price_per === "stuk" || l?.price_per === "eenheid" ? l.price_per : null,
244
+ match_confidence: clamp01(c?.product ?? l?.match_confidence),
245
+ confidence: {
246
+ product: clamp01(c?.product ?? l?.match_confidence),
247
+ quantity: clamp01(c?.quantity),
248
+ unit: clamp01(c?.unit),
249
+ price: clamp01(c?.price),
250
+ },
251
+ };
252
+ })
253
+ .filter((l) => l.raw_product_text || l.product_id || l.code);
226
254
  const rv = raw?.validation ?? {};
227
255
  return {
228
256
  fields,
@@ -272,11 +300,11 @@ export async function parseOrderEmail(admin, args) {
272
300
  plantTypes: args.plantTypes,
273
301
  requiredFields: args.requiredFields,
274
302
  systemPrompt: args.systemPrompt,
303
+ now: args.now,
275
304
  });
276
- // Per model maximaal 3 pogingen met exponentiële backoff; daarna uitwijken
277
- // naar het volgende model in de keten. De vingerafdruk blijft die van het
278
- // standaardmodel, zodat een fallback geen eindeloze her-analyse veroorzaakt.
279
- const MAX_ATTEMPTS_PER_MODEL = 3;
305
+ // Per model een begrensd aantal pogingen (instelling) met exponentiële
306
+ // backoff; daarna uitwijken naar het volgende model in de keten.
307
+ const MAX_ATTEMPTS_PER_MODEL = Math.max(1, Math.min(5, Number(args.maxAttemptsPerModel ?? 3)));
280
308
  let attemptNo = 0;
281
309
  let lastMessage = "AI niet beschikbaar.";
282
310
  let lastTransient = false;
@@ -306,6 +334,8 @@ export async function parseOrderEmail(admin, args) {
306
334
  modelName: cfg.modelName,
307
335
  durationMs,
308
336
  fingerprint: primary.fingerprint,
337
+ inputTokens: completion.inputTokens,
338
+ outputTokens: completion.outputTokens,
309
339
  },
310
340
  };
311
341
  }
@@ -0,0 +1,143 @@
1
+ export declare const getMailboxFilterSettings: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
2
+ supabase: import("../integrations/supabase/auth-middleware.js").AuthedSupabaseClient;
3
+ userId: string;
4
+ claims: import("@supabase/auth-js").User;
5
+ }, undefined, undefined, undefined>], undefined, Promise<{
6
+ settings: any;
7
+ auto_analyze: boolean;
8
+ usage: {
9
+ month_analyses: number;
10
+ today_analyses: number;
11
+ test_analyses: number;
12
+ input_tokens: number;
13
+ output_tokens: number;
14
+ ai_calls_month: any;
15
+ waiting: any;
16
+ };
17
+ }>>;
18
+ export declare const saveMailboxFilterSettings: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
19
+ supabase: import("../integrations/supabase/auth-middleware.js").AuthedSupabaseClient;
20
+ userId: string;
21
+ claims: import("@supabase/auth-js").User;
22
+ }, undefined, undefined, undefined>], (input: unknown) => {
23
+ domain_match: boolean;
24
+ analyze_unknown_with_signals: boolean;
25
+ min_signals_unknown: number;
26
+ order_keywords: string[];
27
+ order_patterns: string[];
28
+ negative_keywords: string[];
29
+ skip_senders: string[];
30
+ skip_domains: string[];
31
+ max_ai_per_day: number;
32
+ max_ai_per_month: number;
33
+ max_candidates: number;
34
+ max_retry_attempts: number;
35
+ max_retries_per_run: number;
36
+ max_agent_reparse: number;
37
+ conf_green: number;
38
+ conf_orange: number;
39
+ duplicate_days: number;
40
+ auto_analyze: boolean;
41
+ }, Promise<{
42
+ ok: true;
43
+ }>>;
44
+ /** Telling van openstaande voorstellen die het nieuwe filter als "geen bestelling" zou zien. Wijzigt niets. */
45
+ export declare const countLikelyFakeProposals: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
46
+ supabase: import("../integrations/supabase/auth-middleware.js").AuthedSupabaseClient;
47
+ userId: string;
48
+ claims: import("@supabase/auth-js").User;
49
+ }, undefined, undefined, undefined>], undefined, Promise<{
50
+ total: number;
51
+ known: number;
52
+ wouldSkip: number;
53
+ withSignals: number;
54
+ }>>;
55
+ export declare const listTestCases: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
56
+ supabase: import("../integrations/supabase/auth-middleware.js").AuthedSupabaseClient;
57
+ userId: string;
58
+ claims: import("@supabase/auth-js").User;
59
+ }, undefined, undefined, undefined>], undefined, Promise<{
60
+ items: any[];
61
+ }>>;
62
+ export declare const saveTestCase: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
63
+ supabase: import("../integrations/supabase/auth-middleware.js").AuthedSupabaseClient;
64
+ userId: string;
65
+ claims: import("@supabase/auth-js").User;
66
+ }, undefined, undefined, undefined>], (input: unknown) => {
67
+ name: string;
68
+ subject: string;
69
+ from_email: string;
70
+ body_text: string;
71
+ has_attachment: boolean;
72
+ expected: Record<string, any>;
73
+ id?: string | undefined;
74
+ }, Promise<{
75
+ ok: true;
76
+ }>>;
77
+ export declare const deleteTestCase: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
78
+ supabase: import("../integrations/supabase/auth-middleware.js").AuthedSupabaseClient;
79
+ userId: string;
80
+ claims: import("@supabase/auth-js").User;
81
+ }, undefined, undefined, undefined>], (input: unknown) => {
82
+ id: string;
83
+ }, Promise<{
84
+ ok: true;
85
+ }>>;
86
+ type Check = {
87
+ field: string;
88
+ expected: any;
89
+ actual: any;
90
+ ok: boolean;
91
+ };
92
+ /** Draait één voorbeeld: filter + extractie, maakt geen voorstel of order aan. */
93
+ export declare const runTestCase: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
94
+ supabase: import("../integrations/supabase/auth-middleware.js").AuthedSupabaseClient;
95
+ userId: string;
96
+ claims: import("@supabase/auth-js").User;
97
+ }, undefined, undefined, undefined>], (input: unknown) => {
98
+ id: string;
99
+ }, Promise<{
100
+ checks: Check[];
101
+ passed: boolean;
102
+ filter: import("./mailbox-filter.js").FilterResult;
103
+ sender: string;
104
+ forwarded: boolean;
105
+ analyzed: boolean;
106
+ tokens: {
107
+ input: number;
108
+ output: number;
109
+ };
110
+ result: null;
111
+ } | {
112
+ checks: Check[];
113
+ passed: boolean;
114
+ filter: import("./mailbox-filter.js").FilterResult;
115
+ sender: string;
116
+ forwarded: boolean;
117
+ analyzed: boolean;
118
+ tokens: {
119
+ input: number;
120
+ output: number;
121
+ };
122
+ result: {
123
+ error: string | null;
124
+ customer_known: boolean;
125
+ delivery_date: any;
126
+ reference: any;
127
+ attachment_unread: boolean;
128
+ lines: {
129
+ text: string;
130
+ code: string | null;
131
+ product_id: string | null;
132
+ product_known: boolean;
133
+ match_source: "code" | "naam" | "ai" | null;
134
+ quantity: number | null;
135
+ unit: import("./mailbox-units.js").UnitKind;
136
+ plants: number | null;
137
+ unit_price: number | null;
138
+ warnings: string[];
139
+ }[];
140
+ };
141
+ }>>;
142
+ export {};
143
+ //# sourceMappingURL=mailbox-filter-settings.functions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mailbox-filter-settings.functions.d.ts","sourceRoot":"","sources":["../../src/lib/mailbox-filter-settings.functions.ts"],"names":[],"mappings":"AA4BA,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;GA0CjC,CAAC;AAEL,eAAO,MAAM,yBAAyB;;;;8CAEZ,OAAO;;;;;;;;;;;;;;;;;;;;;GAuB7B,CAAC;AAEL,+GAA+G;AAC/G,eAAO,MAAM,wBAAwB;;;;;;;;;GA+BjC,CAAC;AAIL,eAAO,MAAM,aAAa;;;;;;GAUtB,CAAC;AAYL,eAAO,MAAM,YAAY;;;;8CAEC,OAAO;;;;;;;;;;GAW7B,CAAC;AAEL,eAAO,MAAM,cAAc;;;;8CAED,OAAO;;;;GAM7B,CAAC;AAEL,KAAK,KAAK,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,GAAG,CAAC;IAAC,EAAE,EAAE,OAAO,CAAA;CAAE,CAAC;AA4BxE,kFAAkF;AAClF,eAAO,MAAM,WAAW;;;;8CAEE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqB7B,CAAC"}
@@ -0,0 +1,241 @@
1
+ // Instellingen voor het voorfilter, de kostengrenzen en de testset.
2
+ import { createServerFn } from "@tanstack/react-start";
3
+ import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware.js";
4
+ import { z } from "zod";
5
+ const list = z.array(z.string().trim().min(1).max(200)).max(500);
6
+ const SettingsInput = z.object({
7
+ domain_match: z.boolean(),
8
+ analyze_unknown_with_signals: z.boolean(),
9
+ min_signals_unknown: z.number().int().min(1).max(20),
10
+ order_keywords: list,
11
+ order_patterns: list,
12
+ negative_keywords: list,
13
+ skip_senders: list,
14
+ skip_domains: list,
15
+ max_ai_per_day: z.number().int().min(0).max(100000),
16
+ max_ai_per_month: z.number().int().min(0).max(1000000),
17
+ max_candidates: z.number().int().min(1).max(250),
18
+ max_retry_attempts: z.number().int().min(1).max(5),
19
+ max_retries_per_run: z.number().int().min(0).max(50),
20
+ max_agent_reparse: z.number().int().min(0).max(200),
21
+ conf_green: z.number().min(0).max(1),
22
+ conf_orange: z.number().min(0).max(1),
23
+ duplicate_days: z.number().int().min(1).max(365),
24
+ auto_analyze: z.boolean(),
25
+ });
26
+ export const getMailboxFilterSettings = createServerFn({ method: "GET" })
27
+ .middleware([requireSupabaseAuth])
28
+ .handler(async () => {
29
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
30
+ const admin = supabaseAdmin;
31
+ const { data: s, error } = await admin.from("mailbox_filter_settings").select("*").eq("singleton", true).maybeSingle();
32
+ if (error)
33
+ throw new Error(error.message);
34
+ const { data: ai } = await admin.from("mailbox_ai_settings").select("enabled").limit(1).maybeSingle();
35
+ const now = new Date();
36
+ const monthStart = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1)).toISOString();
37
+ const dayStart = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())).toISOString();
38
+ const { data: runs } = await admin
39
+ .from("mailbox_ai_runs")
40
+ .select("started_at, input_tokens, output_tokens, is_test")
41
+ .gte("started_at", monthStart)
42
+ .range(0, 9999);
43
+ const rs = Array.isArray(runs) ? runs : [];
44
+ const real = rs.filter((r) => !r.is_test);
45
+ const { count: calls } = await admin
46
+ .from("ai_call_log")
47
+ .select("id", { count: "exact", head: true })
48
+ .eq("consumer_module_id", "mailbox-bestellingen")
49
+ .gte("created_at", monthStart);
50
+ const { count: waitingCount } = await admin
51
+ .from("mailbox_messages")
52
+ .select("id", { count: "exact", head: true })
53
+ .eq("analysis_state", "wacht");
54
+ return {
55
+ settings: s ?? null,
56
+ auto_analyze: ai?.enabled !== false,
57
+ usage: {
58
+ month_analyses: real.length,
59
+ today_analyses: real.filter((r) => r.started_at >= dayStart).length,
60
+ test_analyses: rs.length - real.length,
61
+ input_tokens: rs.reduce((a, r) => a + Number(r.input_tokens ?? 0), 0),
62
+ output_tokens: rs.reduce((a, r) => a + Number(r.output_tokens ?? 0), 0),
63
+ ai_calls_month: calls ?? 0,
64
+ waiting: waitingCount ?? 0,
65
+ },
66
+ };
67
+ });
68
+ export const saveMailboxFilterSettings = createServerFn({ method: "POST" })
69
+ .middleware([requireSupabaseAuth])
70
+ .inputValidator((input) => SettingsInput.parse(input))
71
+ .handler(async ({ data }) => {
72
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
73
+ const admin = supabaseAdmin;
74
+ for (const p of data.order_patterns) {
75
+ try {
76
+ new RegExp(p.replace(/\\m|\\M/g, "\\b"));
77
+ }
78
+ catch {
79
+ throw new Error(`Ongeldig patroon: ${p}`);
80
+ }
81
+ }
82
+ const { auto_analyze, ...rest } = data;
83
+ const { error } = await admin
84
+ .from("mailbox_filter_settings")
85
+ .update({ ...rest, updated_at: new Date().toISOString() })
86
+ .eq("singleton", true);
87
+ if (error)
88
+ throw new Error(error.message);
89
+ const { data: ai } = await admin.from("mailbox_ai_settings").select("id").limit(1).maybeSingle();
90
+ if (ai?.id) {
91
+ const { error: e2 } = await admin.from("mailbox_ai_settings").update({ enabled: auto_analyze }).eq("id", ai.id);
92
+ if (e2)
93
+ throw new Error(e2.message);
94
+ }
95
+ return { ok: true };
96
+ });
97
+ /** Telling van openstaande voorstellen die het nieuwe filter als "geen bestelling" zou zien. Wijzigt niets. */
98
+ export const countLikelyFakeProposals = createServerFn({ method: "GET" })
99
+ .middleware([requireSupabaseAuth])
100
+ .handler(async () => {
101
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
102
+ const admin = supabaseAdmin;
103
+ const { loadFilterSettings } = await import("./mailbox-ingest.server.js");
104
+ const { classifyMessage } = await import("./mailbox-filter.js");
105
+ const s = await loadFilterSettings(admin);
106
+ let total = 0, known = 0, wouldSkip = 0, withSignals = 0;
107
+ for (let from = 0; from < 20000; from += 1000) {
108
+ const { data } = await admin
109
+ .from("mailbox_order_proposals")
110
+ .select("matched_customer_id, mailbox_messages(from_email, subject, preview)")
111
+ .eq("status", "pending")
112
+ .range(from, from + 999);
113
+ const rows = Array.isArray(data) ? data : [];
114
+ for (const r of rows) {
115
+ total++;
116
+ const m = r.mailbox_messages ?? {};
117
+ const isKnown = !!r.matched_customer_id;
118
+ if (isKnown)
119
+ known++;
120
+ const c = classifyMessage({ fromEmail: String(m.from_email ?? ""), subject: String(m.subject ?? ""), body: String(m.preview ?? ""), knownSender: isKnown }, s);
121
+ if (c.outcome === "geen_bestelling")
122
+ wouldSkip++;
123
+ else if (c.outcome === "onbekend_met_signalen")
124
+ withSignals++;
125
+ }
126
+ if (rows.length < 1000)
127
+ break;
128
+ }
129
+ return { total, known, wouldSkip, withSignals };
130
+ });
131
+ // ─── Testset ────────────────────────────────────────────────────────────────
132
+ export const listTestCases = createServerFn({ method: "GET" })
133
+ .middleware([requireSupabaseAuth])
134
+ .handler(async () => {
135
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
136
+ const { data, error } = await supabaseAdmin
137
+ .from("mailbox_test_cases")
138
+ .select("*")
139
+ .order("sort_order", { ascending: true });
140
+ if (error)
141
+ throw new Error(error.message);
142
+ return { items: Array.isArray(data) ? data : [] };
143
+ });
144
+ const CaseInput = z.object({
145
+ id: z.string().uuid().optional(),
146
+ name: z.string().trim().min(1).max(120),
147
+ subject: z.string().max(500),
148
+ from_email: z.string().max(320),
149
+ body_text: z.string().max(20000),
150
+ has_attachment: z.boolean(),
151
+ expected: z.record(z.string(), z.any()),
152
+ });
153
+ export const saveTestCase = createServerFn({ method: "POST" })
154
+ .middleware([requireSupabaseAuth])
155
+ .inputValidator((input) => CaseInput.parse(input))
156
+ .handler(async ({ data }) => {
157
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
158
+ const admin = supabaseAdmin;
159
+ const row = { ...data, updated_at: new Date().toISOString() };
160
+ const q = data.id
161
+ ? admin.from("mailbox_test_cases").update(row).eq("id", data.id)
162
+ : admin.from("mailbox_test_cases").insert(row);
163
+ const { error } = await q;
164
+ if (error)
165
+ throw new Error(error.message);
166
+ return { ok: true };
167
+ });
168
+ export const deleteTestCase = createServerFn({ method: "POST" })
169
+ .middleware([requireSupabaseAuth])
170
+ .inputValidator((input) => z.object({ id: z.string().uuid() }).parse(input))
171
+ .handler(async ({ data }) => {
172
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
173
+ const { error } = await supabaseAdmin.from("mailbox_test_cases").delete().eq("id", data.id);
174
+ if (error)
175
+ throw new Error(error.message);
176
+ return { ok: true };
177
+ });
178
+ function compare(expected, run) {
179
+ const checks = [];
180
+ const add = (field, exp, act, ok = exp === act) => checks.push({ field, expected: exp, actual: act, ok });
181
+ const isOrder = run.analyzed && run.result && !run.result.error;
182
+ if (typeof expected.is_order === "boolean")
183
+ add("Bestelling", expected.is_order ? "ja" : "nee", run.analyzed ? "ja" : "nee", expected.is_order === run.analyzed);
184
+ if (!isOrder)
185
+ return checks;
186
+ const r = run.result;
187
+ if (expected.delivery_date)
188
+ add("Leverdatum", expected.delivery_date, r.delivery_date);
189
+ if (expected.reference)
190
+ add("Referentie", expected.reference, r.reference);
191
+ if (expected.forwarded_from)
192
+ add("Doorgestuurd van", expected.forwarded_from, run.sender);
193
+ if (expected.attachment_warning)
194
+ add("Bijlage-melding", true, r.attachment_unread);
195
+ const el = Array.isArray(expected.lines) ? expected.lines : [];
196
+ if (el.length)
197
+ add("Aantal regels", el.length, r.lines.length);
198
+ el.forEach((e, i) => {
199
+ const a = r.lines[i] ?? {};
200
+ const n = `Regel ${i + 1}`;
201
+ if (e.quantity !== undefined)
202
+ add(`${n} aantal`, e.quantity, a.quantity ?? null);
203
+ if (e.unit !== undefined)
204
+ add(`${n} eenheid`, e.unit, a.unit ?? null);
205
+ if (e.plants !== undefined)
206
+ add(`${n} planten`, e.plants, a.plants ?? null);
207
+ if (e.unit_price !== undefined)
208
+ add(`${n} prijs`, e.unit_price, a.unit_price ?? null);
209
+ if (e.product_known !== undefined)
210
+ add(`${n} artikel bekend`, e.product_known, !!a.product_known);
211
+ if (e.code !== undefined)
212
+ add(`${n} gekoppeld op code`, true, a.match_source === "code");
213
+ });
214
+ return checks;
215
+ }
216
+ /** Draait één voorbeeld: filter + extractie, maakt geen voorstel of order aan. */
217
+ export const runTestCase = createServerFn({ method: "POST" })
218
+ .middleware([requireSupabaseAuth])
219
+ .inputValidator((input) => z.object({ id: z.string().uuid() }).parse(input))
220
+ .handler(async ({ data }) => {
221
+ const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
222
+ const admin = supabaseAdmin;
223
+ const { data: tc, error } = await admin.from("mailbox_test_cases").select("*").eq("id", data.id).maybeSingle();
224
+ if (error || !tc)
225
+ throw new Error("Voorbeeld niet gevonden.");
226
+ const { dryRunAnalysis } = await import("./mailbox-ingest.server.js");
227
+ const run = await dryRunAnalysis(admin, {
228
+ subject: tc.subject ?? "",
229
+ body: tc.body_text ?? "",
230
+ fromEmail: tc.from_email ?? "",
231
+ hasAttachment: tc.has_attachment === true,
232
+ simulateKnownSender: tc.expected?.known_sender === true,
233
+ });
234
+ const checks = compare(tc.expected ?? {}, run);
235
+ const result = { ...run, checks, passed: checks.every((c) => c.ok) };
236
+ await admin
237
+ .from("mailbox_test_cases")
238
+ .update({ last_result: result, last_run_at: new Date().toISOString() })
239
+ .eq("id", tc.id);
240
+ return result;
241
+ });
@@ -0,0 +1,34 @@
1
+ export interface FilterSettings {
2
+ domain_match: boolean;
3
+ analyze_unknown_with_signals: boolean;
4
+ min_signals_unknown: number;
5
+ order_keywords: string[];
6
+ order_patterns: string[];
7
+ negative_keywords: string[];
8
+ skip_senders: string[];
9
+ skip_domains: string[];
10
+ }
11
+ export type FilterOutcome = "analyseren" | "onbekend_met_signalen" | "geen_bestelling";
12
+ export interface FilterResult {
13
+ outcome: FilterOutcome;
14
+ reason: string;
15
+ signals: string[];
16
+ }
17
+ /** Postgres-woordgrenzen (\m, \M) omzetten naar JS (\b). */
18
+ export declare function toJsRegex(pattern: string): RegExp | null;
19
+ export declare function classifyMessage(input: {
20
+ fromEmail: string;
21
+ subject: string;
22
+ body: string;
23
+ knownSender: boolean;
24
+ }, s: FilterSettings): FilterResult;
25
+ /**
26
+ * Alleen het nieuwste deel van de mail: geciteerde tekst en handtekening weg.
27
+ * Bij een doorgestuurde mail is het doorgestuurde bericht leidend en wordt de
28
+ * oorspronkelijke afzender teruggegeven.
29
+ */
30
+ export declare function extractLatestPart(body: string): {
31
+ text: string;
32
+ forwardedFrom: string | null;
33
+ };
34
+ //# sourceMappingURL=mailbox-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mailbox-filter.d.ts","sourceRoot":"","sources":["../../src/lib/mailbox-filter.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,OAAO,CAAC;IACtB,4BAA4B,EAAE,OAAO,CAAC;IACtC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,uBAAuB,GAAG,iBAAiB,CAAC;AAEvF,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,aAAa,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAID,4DAA4D;AAC5D,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMxD;AAoBD,wBAAgB,eAAe,CAC7B,KAAK,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,EACjF,CAAC,EAAE,cAAc,GAChB,YAAY,CAoDd;AAqBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAgB9F"}