@flowselections/mailbox-orders 1.0.19 → 1.0.21
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/MailboxProcessedPage.js +1 -1
- package/dist-lib/components/MailboxProposalsPage.js +5 -5
- package/dist-lib/components/settings/MailboxAiPromptCard.js +2 -2
- package/dist-lib/components/settings/MailboxOrderTemplateCard.js +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +2 -28697
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.js +6 -13
- package/dist-lib/integrations/supabase/client.server.d.ts +1576 -63
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.js +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +1581 -55
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/ai-config.server.d.ts +1 -1
- package/dist-lib/lib/mailbox-ai-settings.functions.js +1 -1
- package/dist-lib/lib/mailbox-ai.server.d.ts +1 -1
- package/dist-lib/lib/mailbox-ingest.server.d.ts +1 -1
- package/dist-lib/lib/mailbox-ingest.server.js +1 -1
- package/dist-lib/lib/mailbox-template.functions.js +1 -1
- package/dist-lib/lib/mailbox.functions.d.ts +3 -3
- package/dist-lib/lib/mailbox.functions.js +22 -22
- package/package.json +3 -3
- package/dist-lib/components/CartAssignmentWizard.d.ts +0 -6
- package/dist-lib/components/CartAssignmentWizard.d.ts.map +0 -1
- package/dist-lib/components/CartAssignmentWizard.js +0 -199
- package/dist-lib/components/MailboxInboxPage.d.ts +0 -2
- package/dist-lib/components/MailboxInboxPage.d.ts.map +0 -1
- package/dist-lib/components/MailboxInboxPage.js +0 -120
- package/dist-lib/components/settings/ImapAccountsCard.d.ts +0 -2
- package/dist-lib/components/settings/ImapAccountsCard.d.ts.map +0 -1
- package/dist-lib/components/settings/ImapAccountsCard.js +0 -136
- package/dist-lib/components/settings/ImapProfilesCard.d.ts +0 -2
- package/dist-lib/components/settings/ImapProfilesCard.d.ts.map +0 -1
- package/dist-lib/components/settings/ImapProfilesCard.js +0 -101
- package/dist-lib/components/settings/MailboxSettingsCard.d.ts +0 -2
- package/dist-lib/components/settings/MailboxSettingsCard.d.ts.map +0 -1
- package/dist-lib/components/settings/MailboxSettingsCard.js +0 -85
- package/dist-lib/lib/imap.functions.d.ts +0 -32852
- package/dist-lib/lib/imap.functions.d.ts.map +0 -1
- package/dist-lib/lib/imap.functions.js +0 -235
- package/dist-lib/lib/mailbox-auto.server.d.ts +0 -9
- package/dist-lib/lib/mailbox-auto.server.d.ts.map +0 -1
- package/dist-lib/lib/mailbox-auto.server.js +0 -198
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
-
import type { Database } from "../integrations/supabase/types";
|
|
2
|
+
import type { Database } from "../integrations/supabase/types.js";
|
|
3
3
|
type AdminClient = SupabaseClient<Database>;
|
|
4
4
|
export interface ModuleAiConfig {
|
|
5
5
|
moduleKey: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Beheer van de centrale systeemprompt en verplichte velden voor de mailbox-AI.
|
|
2
2
|
import { createServerFn } from "@tanstack/react-start";
|
|
3
|
-
import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware";
|
|
3
|
+
import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { DEFAULT_MAILBOX_SYSTEM_PROMPT, DEFAULT_REQUIRED_FIELDS } from "./mailbox-ai-prompt";
|
|
6
6
|
export const getMailboxAiSettings = createServerFn({ method: "GET" })
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
-
import type { Database } from "../integrations/supabase/types";
|
|
2
|
+
import type { Database } from "../integrations/supabase/types.js";
|
|
3
3
|
import { DEFAULT_MAILBOX_SYSTEM_PROMPT } from "./mailbox-ai-prompt";
|
|
4
4
|
export { DEFAULT_MAILBOX_SYSTEM_PROMPT };
|
|
5
5
|
type AdminClient = SupabaseClient<Database>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
-
import type { Database } from "../integrations/supabase/types";
|
|
2
|
+
import type { Database } from "../integrations/supabase/types.js";
|
|
3
3
|
type AdminClient = SupabaseClient<Database>;
|
|
4
4
|
/** Centrale AI-instellingen van deze module (aanpasbaar via Instellingen). */
|
|
5
5
|
export declare function loadMailboxAiSettings(admin: AdminClient): Promise<{
|
|
@@ -799,7 +799,7 @@ async function ingestSingleEvent(admin, connectionId, ev) {
|
|
|
799
799
|
}
|
|
800
800
|
// De volledige pipeline. Alle triggers roepen dit aan.
|
|
801
801
|
export async function runIngest(trigger) {
|
|
802
|
-
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
802
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
803
803
|
// 1. Actieve orders/receive connectie ophalen (service_role → geen bearer nodig).
|
|
804
804
|
const { data: active, error: activeErr } = await supabaseAdmin.rpc("email_auth_for_purpose", {
|
|
805
805
|
_purpose: "orders",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// CRUD voor de configureerbare ordertemplate.
|
|
2
2
|
import { createServerFn } from "@tanstack/react-start";
|
|
3
|
-
import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware";
|
|
3
|
+
import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
const FieldType = z.enum([
|
|
6
6
|
"text", "number", "date", "select", "multiselect",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Database } from "../integrations/supabase/types";
|
|
1
|
+
import type { Database } from "../integrations/supabase/types.js";
|
|
2
2
|
export declare const getActiveOrdersMailbox: import("@tanstack/start-client-core").OptionalFetcher<undefined, undefined, Promise<{
|
|
3
3
|
connected: false;
|
|
4
4
|
error: string;
|
|
@@ -35,7 +35,7 @@ export declare const getActiveOrdersMailbox: import("@tanstack/start-client-core
|
|
|
35
35
|
}>>;
|
|
36
36
|
export declare const syncMailboxNow: import("@tanstack/start-client-core").OptionalFetcher<undefined, (input: unknown) => {
|
|
37
37
|
trigger: "realtime" | "manual";
|
|
38
|
-
}, Promise<import("
|
|
38
|
+
}, Promise<import("./mailbox-ingest.server.js").IngestRunResult | {
|
|
39
39
|
ok: false;
|
|
40
40
|
error: string;
|
|
41
41
|
processed: 0;
|
|
@@ -358,7 +358,7 @@ export declare const listCarriersForPicker: import("@tanstack/start-client-core"
|
|
|
358
358
|
delivery_days: number | null;
|
|
359
359
|
pickup_weekdays: number[];
|
|
360
360
|
delivery_weekdays: number[];
|
|
361
|
-
pickup_moments: import("../integrations/supabase/types").Json;
|
|
361
|
+
pickup_moments: import("../integrations/supabase/types.js").Json;
|
|
362
362
|
}[];
|
|
363
363
|
}>>;
|
|
364
364
|
export declare const getOrderPickDays: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// Server functions voor de Mailbox-bestellingen module
|
|
2
2
|
import { createServerFn } from "@tanstack/react-start";
|
|
3
|
-
import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware";
|
|
3
|
+
import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware.js";
|
|
4
4
|
import { createClient } from "@supabase/supabase-js";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
-
import { computeEarliestDelivery } from "
|
|
6
|
+
import { computeEarliestDelivery } from "./delivery-date.js";
|
|
7
7
|
import { emailBodyText, inlineCidImages, normalizeAddressList, normalizeAttachments, sanitizeEmailHtml, stripInvisible, } from "./mailbox-body";
|
|
8
8
|
import { EMPTY_LOAD_CARRIERS, isLoadCarrierAllowed, loadCarrierLabel, normalizeAllowedLoadCarriers, } from "./load-carriers";
|
|
9
9
|
// ─── Actieve mailbox (uit gedeelde e-mail-auth module) ──────────────────────
|
|
@@ -66,7 +66,7 @@ export const syncMailboxNow = createServerFn({ method: "POST" })
|
|
|
66
66
|
if (userErr || !userData?.user) {
|
|
67
67
|
return { ok: false, error: "unauthorized", processed: 0, inserted: 0, errors: 1 };
|
|
68
68
|
}
|
|
69
|
-
const { runIngest } = await import("
|
|
69
|
+
const { runIngest } = await import("./mailbox-ingest.server.js");
|
|
70
70
|
return await runIngest(data.trigger);
|
|
71
71
|
}
|
|
72
72
|
catch (e) {
|
|
@@ -82,7 +82,7 @@ export const syncMailboxNow = createServerFn({ method: "POST" })
|
|
|
82
82
|
export const getLastIngestRun = createServerFn({ method: "GET" })
|
|
83
83
|
.middleware([requireSupabaseAuth])
|
|
84
84
|
.handler(async () => {
|
|
85
|
-
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
85
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
86
86
|
const { data } = await supabaseAdmin
|
|
87
87
|
.from("mailbox_ingest_runs")
|
|
88
88
|
.select("id, trigger, started_at, finished_at, processed, inserted, errors, error_sample")
|
|
@@ -96,23 +96,23 @@ export const reparseProposalNow = createServerFn({ method: "POST" })
|
|
|
96
96
|
.middleware([requireSupabaseAuth])
|
|
97
97
|
.inputValidator((input) => z.object({ proposalId: z.string().uuid() }).parse(input))
|
|
98
98
|
.handler(async ({ data }) => {
|
|
99
|
-
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
100
|
-
const { reparseProposal } = await import("
|
|
99
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
100
|
+
const { reparseProposal } = await import("./mailbox-ingest.server.js");
|
|
101
101
|
return reparseProposal(supabaseAdmin, data.proposalId);
|
|
102
102
|
});
|
|
103
103
|
/** Detecteert een gewijzigde AI-agent en herverwerkt openstaande voorstellen. */
|
|
104
104
|
export const refreshProposalsForAgentChange = createServerFn({ method: "POST" })
|
|
105
105
|
.middleware([requireSupabaseAuth])
|
|
106
106
|
.handler(async () => {
|
|
107
|
-
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
108
|
-
const { reparseChangedAgentProposals } = await import("
|
|
107
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
108
|
+
const { reparseChangedAgentProposals } = await import("./mailbox-ingest.server.js");
|
|
109
109
|
return reparseChangedAgentProposals(supabaseAdmin);
|
|
110
110
|
});
|
|
111
111
|
// ─── Messages & Proposals ────────────────────────────────────────────────────
|
|
112
112
|
export const listMessages = createServerFn({ method: "GET" })
|
|
113
113
|
.middleware([requireSupabaseAuth])
|
|
114
114
|
.handler(async () => {
|
|
115
|
-
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
115
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
116
116
|
const { data, error } = await supabaseAdmin
|
|
117
117
|
.from("mailbox_messages")
|
|
118
118
|
.select("id, uid, from_email, from_name, subject, received_at, status, error_message, created_at")
|
|
@@ -126,8 +126,8 @@ export const parseMessage = createServerFn({ method: "POST" })
|
|
|
126
126
|
.middleware([requireSupabaseAuth])
|
|
127
127
|
.inputValidator((input) => z.object({ messageId: z.string().uuid() }).parse(input))
|
|
128
128
|
.handler(async ({ data }) => {
|
|
129
|
-
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
130
|
-
const { createStubProposal } = await import("
|
|
129
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
130
|
+
const { createStubProposal } = await import("./mailbox-ingest.server.js");
|
|
131
131
|
const { data: msg, error: msgErr } = await supabaseAdmin
|
|
132
132
|
.from("mailbox_messages").select("*").eq("id", data.messageId).single();
|
|
133
133
|
if (msgErr || !msg)
|
|
@@ -142,7 +142,7 @@ export const listProposals = createServerFn({ method: "GET" })
|
|
|
142
142
|
.middleware([requireSupabaseAuth])
|
|
143
143
|
.inputValidator((input) => z.object({ status: z.enum(["pending", "approved", "rejected"]).optional() }).parse(input ?? {}))
|
|
144
144
|
.handler(async ({ data }) => {
|
|
145
|
-
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
145
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
146
146
|
// Alleen de kolommen die de lijstweergave toont. De mailtekst (body_text /
|
|
147
147
|
// body_html) en de bestelregels zijn bewust NIET opgenomen: die worden pas
|
|
148
148
|
// opgehaald zodra een voorstel geopend wordt (`getProposalDetail`).
|
|
@@ -178,7 +178,7 @@ export const getProposalDetail = createServerFn({ method: "GET" })
|
|
|
178
178
|
.middleware([requireSupabaseAuth])
|
|
179
179
|
.inputValidator((input) => z.object({ proposalId: z.string().uuid() }).parse(input))
|
|
180
180
|
.handler(async ({ data }) => {
|
|
181
|
-
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
181
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
182
182
|
const sel = (s) => s;
|
|
183
183
|
const { data: row, error } = await supabaseAdmin
|
|
184
184
|
.from("mailbox_order_proposals")
|
|
@@ -252,7 +252,7 @@ export const approveProposal = createServerFn({ method: "POST" })
|
|
|
252
252
|
fieldValues: z.array(FieldValueInput).max(100).optional(),
|
|
253
253
|
}).parse(input))
|
|
254
254
|
.handler(async ({ data, context }) => {
|
|
255
|
-
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
255
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
256
256
|
const customFields = {};
|
|
257
257
|
(data.fieldValues ?? []).forEach((fv) => {
|
|
258
258
|
customFields[fv.field_key] = fv.value;
|
|
@@ -384,7 +384,7 @@ export const rejectProposal = createServerFn({ method: "POST" })
|
|
|
384
384
|
.middleware([requireSupabaseAuth])
|
|
385
385
|
.inputValidator((input) => z.object({ proposalId: z.string().uuid(), reason: z.string().max(500).optional() }).parse(input))
|
|
386
386
|
.handler(async ({ data, context }) => {
|
|
387
|
-
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
387
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
388
388
|
await supabaseAdmin.from("mailbox_order_proposals").update({
|
|
389
389
|
status: "rejected",
|
|
390
390
|
reviewed_by: context.userId,
|
|
@@ -398,7 +398,7 @@ export const reopenProposal = createServerFn({ method: "POST" })
|
|
|
398
398
|
.middleware([requireSupabaseAuth])
|
|
399
399
|
.inputValidator((input) => z.object({ proposalId: z.string().uuid() }).parse(input))
|
|
400
400
|
.handler(async ({ data }) => {
|
|
401
|
-
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
401
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
402
402
|
await supabaseAdmin.from("mailbox_order_proposals").update({
|
|
403
403
|
status: "pending",
|
|
404
404
|
reviewed_by: null,
|
|
@@ -413,7 +413,7 @@ export const reopenProposal = createServerFn({ method: "POST" })
|
|
|
413
413
|
export const listCustomersForPicker = createServerFn({ method: "GET" })
|
|
414
414
|
.middleware([requireSupabaseAuth])
|
|
415
415
|
.handler(async () => {
|
|
416
|
-
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
416
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
417
417
|
const sb = supabaseAdmin;
|
|
418
418
|
const { data: grouped } = await sb
|
|
419
419
|
.from("external_customers_grouped")
|
|
@@ -474,7 +474,7 @@ export const createCustomerFromGroup = createServerFn({ method: "POST" })
|
|
|
474
474
|
phone: z.string().max(50).nullable().optional(),
|
|
475
475
|
}).parse(input))
|
|
476
476
|
.handler(async ({ data }) => {
|
|
477
|
-
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
477
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
478
478
|
const sb = supabaseAdmin;
|
|
479
479
|
const email = (data.email ?? "").trim().toLowerCase() || null;
|
|
480
480
|
const name = data.companyName.trim();
|
|
@@ -512,7 +512,7 @@ export const createCustomerFromGroup = createServerFn({ method: "POST" })
|
|
|
512
512
|
export const listProductsForPicker = createServerFn({ method: "GET" })
|
|
513
513
|
.middleware([requireSupabaseAuth])
|
|
514
514
|
.handler(async ({ context }) => {
|
|
515
|
-
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
515
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
516
516
|
// ── 1. Voorraad verzamelen ──────────────────────────────────────────────
|
|
517
517
|
const { data: stockRows } = await supabaseAdmin
|
|
518
518
|
.from("trade_item_stock")
|
|
@@ -633,7 +633,7 @@ export const planAndSaveAutoCarts = createServerFn({ method: "POST" })
|
|
|
633
633
|
.eq("order_id", data.orderId);
|
|
634
634
|
if (itErr)
|
|
635
635
|
throw new Error(itErr.message);
|
|
636
|
-
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
636
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
637
637
|
// Reset eventuele eerdere auto-karren (idempotent)
|
|
638
638
|
await supabaseAdmin.from("order_carts").delete().eq("order_id", data.orderId).eq("is_auto", true);
|
|
639
639
|
let cartNumber = 1;
|
|
@@ -743,7 +743,7 @@ export const saveManualCarts = createServerFn({ method: "POST" })
|
|
|
743
743
|
.middleware([requireSupabaseAuth])
|
|
744
744
|
.inputValidator((input) => z.object({ orderId: z.string().uuid(), carts: z.array(ManualCart) }).parse(input))
|
|
745
745
|
.handler(async ({ data }) => {
|
|
746
|
-
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
746
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
747
747
|
// Verwijder eerdere handmatige karren (replace strategy)
|
|
748
748
|
await supabaseAdmin.from("order_carts").delete()
|
|
749
749
|
.eq("order_id", data.orderId).eq("is_auto", false);
|
|
@@ -875,7 +875,7 @@ export const listBeladingOptionsForProducts = createServerFn({ method: "POST" })
|
|
|
875
875
|
.middleware([requireSupabaseAuth])
|
|
876
876
|
.inputValidator((input) => z.object({ productIds: z.array(z.string()).max(200) }).parse(input))
|
|
877
877
|
.handler(async ({ data }) => {
|
|
878
|
-
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
878
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
879
879
|
const ids = Array.from(new Set((data.productIds ?? []).filter(Boolean)));
|
|
880
880
|
const byProduct = {};
|
|
881
881
|
function sortCombos(list) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flowselections/mailbox-orders",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.21",
|
|
4
4
|
"private": false,
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"type": "module",
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@cloudflare/vite-plugin": "^1.25.5",
|
|
48
48
|
"@eslint/js": "^9.32.0",
|
|
49
|
-
"@flowselections/core": "1.0.
|
|
50
|
-
"@lovable.dev/vite-tanstack-config": "2.
|
|
49
|
+
"@flowselections/core": "1.0.28",
|
|
50
|
+
"@lovable.dev/vite-tanstack-config": "2.23.1",
|
|
51
51
|
"@rsbuild/core": "^1.0.0",
|
|
52
52
|
"@tailwindcss/vite": "^4.2.1",
|
|
53
53
|
"@tanstack/react-query": "^5.83.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CartAssignmentWizard.d.ts","sourceRoot":"","sources":["../../src/components/CartAssignmentWizard.tsx"],"names":[],"mappings":"AA2CA,wBAAgB,oBAAoB,CAAC,EACnC,OAAO,EAAE,IAAI,EAAE,OAAO,GACvB,EAAE;IAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,+BAsUhE"}
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useMemo, useState } from "react";
|
|
3
|
-
import { useQuery } from "@tanstack/react-query";
|
|
4
|
-
import { useServerFn } from "@tanstack/react-start";
|
|
5
|
-
import { Button, Badge, Card, CardContent, CardHeader, CardTitle, Input, Label, Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, toast, } from "@flowselections/core";
|
|
6
|
-
import { Plus, Trash2, AlertTriangle, CheckCircle2 } from "lucide-react";
|
|
7
|
-
import { planAndSaveAutoCarts, saveManualCarts } from "../lib/mailbox.functions";
|
|
8
|
-
export function CartAssignmentWizard({ orderId, open, onClose, }) {
|
|
9
|
-
const fetchPlan = useServerFn(planAndSaveAutoCarts);
|
|
10
|
-
const fetchSave = useServerFn(saveManualCarts);
|
|
11
|
-
const { data, isLoading, refetch } = useQuery({
|
|
12
|
-
queryKey: ["cart-plan", orderId],
|
|
13
|
-
enabled: !!orderId && open,
|
|
14
|
-
queryFn: async () => {
|
|
15
|
-
try {
|
|
16
|
-
return await fetchPlan({ data: { orderId: orderId } });
|
|
17
|
-
}
|
|
18
|
-
catch (e) {
|
|
19
|
-
toast.error(e?.message ?? "Indelen mislukt");
|
|
20
|
-
return { autoCarts: [], remainders: [], existingManualCarts: [], nextCartNumber: 1 };
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
});
|
|
24
|
-
const autoCarts = Array.isArray(data?.autoCarts) ? data.autoCarts : [];
|
|
25
|
-
const remainders = Array.isArray(data?.remainders) ? data.remainders : [];
|
|
26
|
-
const existingManualCarts = Array.isArray(data?.existingManualCarts) ? data.existingManualCarts : [];
|
|
27
|
-
// Pool met nog in te delen fusten per orderregel
|
|
28
|
-
const [pool, setPool] = useState({});
|
|
29
|
-
const [manual, setManual] = useState([]);
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
const p = {};
|
|
32
|
-
remainders.forEach((r) => { p[r.order_item_id] = r.crates_remaining; });
|
|
33
|
-
setPool(p);
|
|
34
|
-
// Bestaande handmatige karren herstellen — capaciteitsmeta uit remainders aanvullen.
|
|
35
|
-
const remMeta = {};
|
|
36
|
-
remainders.forEach((r) => { remMeta[r.order_item_id] = r; });
|
|
37
|
-
setManual(existingManualCarts.map((c) => ({
|
|
38
|
-
id: crypto.randomUUID(),
|
|
39
|
-
items: c.items.map((i) => {
|
|
40
|
-
const meta = i.order_item_id ? remMeta[i.order_item_id] : null;
|
|
41
|
-
return {
|
|
42
|
-
...i,
|
|
43
|
-
crates_per_layer: meta?.crates_per_layer ?? i.crates_per_layer ?? 1,
|
|
44
|
-
layers_per_cart: meta?.layers_per_cart ?? i.layers_per_cart ?? 1,
|
|
45
|
-
};
|
|
46
|
-
}),
|
|
47
|
-
})));
|
|
48
|
-
}, [data]);
|
|
49
|
-
const remByItem = useMemo(() => {
|
|
50
|
-
const m = {};
|
|
51
|
-
remainders.forEach((r) => { m[r.order_item_id] = r; });
|
|
52
|
-
return m;
|
|
53
|
-
}, [remainders]);
|
|
54
|
-
const addCart = () => setManual((m) => [...m, { id: crypto.randomUUID(), items: [] }]);
|
|
55
|
-
const addItemToCart = (cartId, orderItemId, crates) => {
|
|
56
|
-
const rem = remByItem[orderItemId];
|
|
57
|
-
if (!rem)
|
|
58
|
-
return;
|
|
59
|
-
const available = pool[orderItemId] ?? 0;
|
|
60
|
-
const take = Math.max(0, Math.min(crates, available));
|
|
61
|
-
if (take === 0)
|
|
62
|
-
return;
|
|
63
|
-
setPool((p) => ({ ...p, [orderItemId]: available - take }));
|
|
64
|
-
setManual((carts) => carts.map((c) => {
|
|
65
|
-
if (c.id !== cartId)
|
|
66
|
-
return c;
|
|
67
|
-
// merge in plaats van duplicate regel
|
|
68
|
-
const existingIdx = c.items.findIndex((i) => i.order_item_id === orderItemId);
|
|
69
|
-
if (existingIdx >= 0) {
|
|
70
|
-
const next = [...c.items];
|
|
71
|
-
next[existingIdx] = { ...next[existingIdx], crates: next[existingIdx].crates + take };
|
|
72
|
-
return { ...c, items: next };
|
|
73
|
-
}
|
|
74
|
-
return {
|
|
75
|
-
...c,
|
|
76
|
-
items: [...c.items, {
|
|
77
|
-
order_item_id: orderItemId,
|
|
78
|
-
product_id: rem.product_id,
|
|
79
|
-
product_name: rem.product_name,
|
|
80
|
-
crates: take,
|
|
81
|
-
plants_per_crate: rem.plants_per_crate,
|
|
82
|
-
crates_per_layer: rem.crates_per_layer,
|
|
83
|
-
layers_per_cart: rem.layers_per_cart,
|
|
84
|
-
}],
|
|
85
|
-
};
|
|
86
|
-
}));
|
|
87
|
-
};
|
|
88
|
-
const removeItem = (cartId, orderItemId) => {
|
|
89
|
-
setManual((carts) => carts.map((c) => {
|
|
90
|
-
if (c.id !== cartId)
|
|
91
|
-
return c;
|
|
92
|
-
const item = c.items.find((i) => i.order_item_id === orderItemId);
|
|
93
|
-
if (!item)
|
|
94
|
-
return c;
|
|
95
|
-
setPool((p) => ({ ...p, [orderItemId]: (p[orderItemId] ?? 0) + item.crates }));
|
|
96
|
-
return { ...c, items: c.items.filter((i) => i.order_item_id !== orderItemId) };
|
|
97
|
-
}));
|
|
98
|
-
};
|
|
99
|
-
const removeCart = (cartId) => {
|
|
100
|
-
setManual((carts) => {
|
|
101
|
-
const c = carts.find((x) => x.id === cartId);
|
|
102
|
-
if (c) {
|
|
103
|
-
setPool((p) => {
|
|
104
|
-
const np = { ...p };
|
|
105
|
-
c.items.forEach((i) => {
|
|
106
|
-
if (!i.order_item_id)
|
|
107
|
-
return;
|
|
108
|
-
np[i.order_item_id] = (np[i.order_item_id] ?? 0) + i.crates;
|
|
109
|
-
});
|
|
110
|
-
return np;
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
return carts.filter((x) => x.id !== cartId);
|
|
114
|
-
});
|
|
115
|
-
};
|
|
116
|
-
// Capaciteit per kar = max van betrokken artikelen (gemengde karren toegestaan,
|
|
117
|
-
// pakken we de hoogste cratesPerCart als bovengrens; waarschuwing toont per item).
|
|
118
|
-
const cartCapacity = (cart) => cart.items.length === 0
|
|
119
|
-
? 0
|
|
120
|
-
: Math.max(...cart.items.map((i) => i.crates_per_layer * i.layers_per_cart));
|
|
121
|
-
const cartUsed = (cart) => cart.items.reduce((s, i) => s + i.crates, 0);
|
|
122
|
-
const totalRemainingInPool = Object.values(pool).reduce((s, n) => s + n, 0);
|
|
123
|
-
const handleSave = async () => {
|
|
124
|
-
if (totalRemainingInPool > 0) {
|
|
125
|
-
toast.error(`Nog ${totalRemainingInPool} fust niet ingedeeld — verdeel alles over een kar voordat je opslaat`);
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
if (manual.some((c) => c.items.length === 0)) {
|
|
129
|
-
toast.error("Lege karren niet toegestaan");
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
const overfull = manual.find((c) => cartUsed(c) > cartCapacity(c));
|
|
133
|
-
if (overfull) {
|
|
134
|
-
toast.error("Een kar overschrijdt de capaciteit");
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
try {
|
|
138
|
-
await fetchSave({
|
|
139
|
-
data: {
|
|
140
|
-
orderId: orderId,
|
|
141
|
-
carts: manual.map((c) => ({
|
|
142
|
-
is_complete: cartUsed(c) >= cartCapacity(c),
|
|
143
|
-
items: c.items.map((i) => ({
|
|
144
|
-
order_item_id: i.order_item_id,
|
|
145
|
-
product_id: i.product_id,
|
|
146
|
-
product_name: i.product_name,
|
|
147
|
-
crates: Math.max(1, Math.trunc(i.crates)),
|
|
148
|
-
plants_per_crate: Math.max(1, Math.trunc(i.plants_per_crate || 1)),
|
|
149
|
-
crates_per_layer: Math.max(1, Math.trunc(i.crates_per_layer || 1)),
|
|
150
|
-
})),
|
|
151
|
-
})),
|
|
152
|
-
},
|
|
153
|
-
});
|
|
154
|
-
toast.success("Karindeling opgeslagen");
|
|
155
|
-
onClose();
|
|
156
|
-
}
|
|
157
|
-
catch (e) {
|
|
158
|
-
// requireSupabaseAuth en input-validatie gooien een Response; toString
|
|
159
|
-
// daarvan is "[object Response]" — zet om in leesbare melding zodat
|
|
160
|
-
// de React error-overlay niet wordt getriggerd.
|
|
161
|
-
let msg = "Opslaan mislukt";
|
|
162
|
-
if (e instanceof Response) {
|
|
163
|
-
try {
|
|
164
|
-
msg = (await e.text()) || `HTTP ${e.status}`;
|
|
165
|
-
}
|
|
166
|
-
catch {
|
|
167
|
-
msg = `HTTP ${e.status}`;
|
|
168
|
-
}
|
|
169
|
-
if (e.status === 401)
|
|
170
|
-
msg = "Sessie verlopen — log opnieuw in";
|
|
171
|
-
}
|
|
172
|
-
else if (e?.message) {
|
|
173
|
-
msg = e.message;
|
|
174
|
-
}
|
|
175
|
-
toast.error(msg);
|
|
176
|
-
console.error("saveManualCarts failed:", e);
|
|
177
|
-
}
|
|
178
|
-
};
|
|
179
|
-
return (_jsx(Dialog, { open: open, onOpenChange: (o) => { if (!o)
|
|
180
|
-
onClose(); }, children: _jsxs(DialogContent, { className: "max-w-[95vw] w-[95vw] h-[92vh] max-h-[92vh] overflow-y-auto p-6", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "Karindeling" }) }), isLoading ? (_jsx("div", { className: "text-muted-foreground", children: "Karren worden berekend..." })) : (_jsxs("div", { className: "space-y-6", children: [_jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsx(CardTitle, { className: "text-base", children: "Automatisch ingedeelde karren" }), _jsxs(Badge, { variant: "secondary", children: [autoCarts.length, " kar(ren)"] })] }), _jsx(CardContent, { children: autoCarts.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Geen volledige karren mogelijk." })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Kar #" }), _jsx(TableHead, { children: "Artikel" }), _jsx(TableHead, { className: "text-right", children: "Fusten" }), _jsx(TableHead, { className: "text-right", children: "Planten" }), _jsx(TableHead, { className: "text-right", children: "Bezetting" }), _jsx(TableHead, { children: "Status" })] }) }), _jsx(TableBody, { children: autoCarts.map((c) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-mono", children: c.cart_number }), _jsx(TableCell, { children: c.product_name }), _jsx(TableCell, { className: "text-right", children: c.crates }), _jsx(TableCell, { className: "text-right", children: c.plants }), _jsx(TableCell, { className: "text-right", children: "100%" }), _jsx(TableCell, { children: _jsxs(Badge, { className: "gap-1", children: [_jsx(CheckCircle2, { className: "h-3 w-3" }), " Volledig"] }) })] }, c.cart_number))) })] })) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Handmatig in te delen fusten" }) }), _jsx(CardContent, { className: "space-y-3", children: remainders.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Geen restant \u2014 alles past volledig op karren." })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Artikel" }), _jsx(TableHead, { className: "text-right", children: "Beschikbaar (fusten)" }), _jsx(TableHead, { className: "text-right", children: "Planten/fust" }), _jsx(TableHead, { className: "text-right", children: "Capaciteit/kar" })] }) }), _jsx(TableBody, { children: remainders.map((r) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: r.product_name }), _jsxs(TableCell, { className: "text-right font-mono", children: [pool[r.order_item_id] ?? 0, " / ", r.crates_remaining] }), _jsx(TableCell, { className: "text-right", children: r.plants_per_crate }), _jsx(TableCell, { className: "text-right", children: r.crates_per_layer * r.layers_per_cart })] }, r.order_item_id))) })] })) })] }), remainders.length > 0 && (_jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsx(CardTitle, { className: "text-base", children: "Restant-karren samenstellen" }), _jsxs(Button, { size: "sm", onClick: addCart, children: [_jsx(Plus, { className: "h-3 w-3 mr-1" }), " Kar toevoegen"] })] }), _jsxs(CardContent, { className: "space-y-3", children: [manual.length === 0 && (_jsx("p", { className: "text-sm text-muted-foreground", children: "Voeg een kar toe om restant-fusten in te delen." })), manual.map((cart, idx) => {
|
|
181
|
-
const used = cartUsed(cart);
|
|
182
|
-
const cap = cartCapacity(cart);
|
|
183
|
-
const pct = cap > 0 ? Math.round((used / cap) * 100) : 0;
|
|
184
|
-
const over = cap > 0 && used > cap;
|
|
185
|
-
return (_jsxs(Card, { className: over ? "border-destructive" : "", children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between py-3", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("span", { className: "font-medium", children: ["Restant-kar ", idx + 1] }), _jsxs(Badge, { variant: over ? "destructive" : "secondary", children: [used, " / ", cap || "?", " fusten (", pct, "%)"] }), over && (_jsxs("span", { className: "text-xs text-destructive flex items-center gap-1", children: [_jsx(AlertTriangle, { className: "h-3 w-3" }), " Capaciteit overschreden"] }))] }), _jsx(Button, { variant: "ghost", size: "icon", onClick: () => removeCart(cart.id), children: _jsx(Trash2, { className: "h-4 w-4" }) })] }), _jsxs(CardContent, { className: "space-y-2", children: [cart.items.map((i) => (_jsxs("div", { className: "flex items-center justify-between text-sm", children: [_jsxs("div", { children: [_jsx("span", { className: "font-medium", children: i.product_name }), _jsxs("span", { className: "text-muted-foreground", children: [" \u00B7 ", i.crates, " fust (", i.crates * i.plants_per_crate, " planten)"] })] }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => removeItem(cart.id, i.order_item_id), children: _jsx(Trash2, { className: "h-3 w-3" }) })] }, i.order_item_id))), _jsx(AddItemRow, { remainders: remainders, pool: pool, onAdd: (itemId, crates) => addItemToCart(cart.id, itemId, crates) })] })] }, cart.id));
|
|
186
|
-
})] })] }))] })), _jsxs(DialogFooter, { className: "gap-2", children: [_jsx(Button, { variant: "outline", onClick: onClose, children: "Annuleren" }), _jsxs(Button, { onClick: handleSave, disabled: isLoading || totalRemainingInPool > 0, children: ["Karindeling opslaan", totalRemainingInPool > 0 && (_jsxs(Badge, { variant: "destructive", className: "ml-2", children: ["Nog ", totalRemainingInPool, " fust in te delen"] }))] })] })] }) }));
|
|
187
|
-
}
|
|
188
|
-
function AddItemRow({ remainders, pool, onAdd, }) {
|
|
189
|
-
const available = remainders.filter((r) => (pool[r.order_item_id] ?? 0) > 0);
|
|
190
|
-
const [sel, setSel] = useState("");
|
|
191
|
-
const [qty, setQty] = useState(1);
|
|
192
|
-
if (available.length === 0)
|
|
193
|
-
return null;
|
|
194
|
-
return (_jsxs("div", { className: "flex items-end gap-2 pt-2 border-t", children: [_jsxs("div", { className: "flex-1", children: [_jsx(Label, { className: "text-xs", children: "Artikel" }), _jsxs(Select, { value: sel, onValueChange: setSel, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Kies restant-artikel..." }) }), _jsx(SelectContent, { children: available.map((r) => (_jsxs(SelectItem, { value: r.order_item_id, children: [r.product_name, " (", pool[r.order_item_id], " fust beschikbaar)"] }, r.order_item_id))) })] })] }), _jsxs("div", { className: "w-24", children: [_jsx(Label, { className: "text-xs", children: "Fusten" }), _jsx(Input, { type: "number", min: 1, value: qty, onChange: (e) => setQty(Math.max(1, Number(e.target.value) || 1)) })] }), _jsxs(Button, { size: "sm", disabled: !sel, onClick: () => { if (sel) {
|
|
195
|
-
onAdd(sel, qty);
|
|
196
|
-
setSel("");
|
|
197
|
-
setQty(1);
|
|
198
|
-
} }, children: [_jsx(Plus, { className: "h-3 w-3 mr-1" }), " Toevoegen"] })] }));
|
|
199
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"MailboxInboxPage.d.ts","sourceRoot":"","sources":["../../src/components/MailboxInboxPage.tsx"],"names":[],"mappings":"AA6BA,wBAAgB,gBAAgB,gCAuO/B"}
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef } from "react";
|
|
3
|
-
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
4
|
-
import { useServerFn } from "@tanstack/react-start";
|
|
5
|
-
import { Button, Badge, Card, CardContent, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, toast, } from "@flowselections/core";
|
|
6
|
-
import { Mail, Sparkles, AlertCircle, ExternalLink, RefreshCw } from "lucide-react";
|
|
7
|
-
import { listMessages, parseMessage, getActiveOrdersMailbox, ingestEmailEvent, backfillRecentEvents, } from "../lib/mailbox.functions";
|
|
8
|
-
import { supabase } from "../integrations/supabase/client";
|
|
9
|
-
const AUTH_MODULE_URL = "https://auth.flowselections.com/e-mail-authenticatie";
|
|
10
|
-
const STATUS_VARIANT = {
|
|
11
|
-
new: "secondary",
|
|
12
|
-
parsing: "outline",
|
|
13
|
-
parsed: "default",
|
|
14
|
-
failed: "destructive",
|
|
15
|
-
ignored: "outline",
|
|
16
|
-
converted: "default",
|
|
17
|
-
};
|
|
18
|
-
export function MailboxInboxPage() {
|
|
19
|
-
const qc = useQueryClient();
|
|
20
|
-
const fetchList = useServerFn(listMessages);
|
|
21
|
-
const fetchParse = useServerFn(parseMessage);
|
|
22
|
-
const fetchActive = useServerFn(getActiveOrdersMailbox);
|
|
23
|
-
const fetchIngest = useServerFn(ingestEmailEvent);
|
|
24
|
-
const fetchBackfill = useServerFn(backfillRecentEvents);
|
|
25
|
-
const { data, isLoading } = useQuery({
|
|
26
|
-
queryKey: ["mailbox-messages"],
|
|
27
|
-
queryFn: () => fetchList(),
|
|
28
|
-
});
|
|
29
|
-
const { data: active, isSuccess: activeLoaded } = useQuery({
|
|
30
|
-
queryKey: ["mailbox-active-account"],
|
|
31
|
-
queryFn: () => fetchActive(),
|
|
32
|
-
refetchInterval: 60000,
|
|
33
|
-
retry: 1,
|
|
34
|
-
});
|
|
35
|
-
// Realtime: nieuwe events uit gedeelde authenticatiemodule
|
|
36
|
-
const activeId = active?.connected ? active.id : null;
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
if (!activeId)
|
|
39
|
-
return;
|
|
40
|
-
const channel = supabase
|
|
41
|
-
.channel(`email_auth_events:${activeId}`)
|
|
42
|
-
.on("postgres_changes", {
|
|
43
|
-
event: "INSERT",
|
|
44
|
-
schema: "public",
|
|
45
|
-
table: "email_auth_events",
|
|
46
|
-
filter: `connection_id=eq.${activeId}`,
|
|
47
|
-
}, async (payload) => {
|
|
48
|
-
const evId = payload?.new?.id;
|
|
49
|
-
if (!evId)
|
|
50
|
-
return;
|
|
51
|
-
try {
|
|
52
|
-
const res = await fetchIngest({ data: { eventId: evId } });
|
|
53
|
-
if (res?.ok && (res.inserted ?? 0) > 0) {
|
|
54
|
-
const app = res.autoApproved ?? 0;
|
|
55
|
-
if (app > 0)
|
|
56
|
-
toast.success(`Nieuwe e-mail — ${app} automatisch omgezet`);
|
|
57
|
-
else
|
|
58
|
-
toast.success("Nieuwe e-mail ontvangen");
|
|
59
|
-
qc.invalidateQueries({ queryKey: ["mailbox-messages"] });
|
|
60
|
-
qc.invalidateQueries({ queryKey: ["mailbox-proposals"] });
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
/* stille fout — backfill vangt het later op */
|
|
65
|
-
}
|
|
66
|
-
})
|
|
67
|
-
.subscribe();
|
|
68
|
-
return () => {
|
|
69
|
-
void supabase.removeChannel(channel);
|
|
70
|
-
};
|
|
71
|
-
}, [activeId, fetchIngest, qc]);
|
|
72
|
-
// Backfill bij eerste mount + iedere 60s (vangt gemiste events op)
|
|
73
|
-
const didInitialBackfill = useRef(false);
|
|
74
|
-
const backfillMut = useMutation({
|
|
75
|
-
mutationFn: async () => {
|
|
76
|
-
try {
|
|
77
|
-
return await fetchBackfill();
|
|
78
|
-
}
|
|
79
|
-
catch {
|
|
80
|
-
// 401's (token verlopen) en tijdelijke fouten stil opvangen
|
|
81
|
-
return { ok: false, inserted: 0 };
|
|
82
|
-
}
|
|
83
|
-
},
|
|
84
|
-
onSuccess: (res) => {
|
|
85
|
-
if (res?.ok && (res.inserted ?? 0) > 0) {
|
|
86
|
-
qc.invalidateQueries({ queryKey: ["mailbox-messages"] });
|
|
87
|
-
qc.invalidateQueries({ queryKey: ["mailbox-proposals"] });
|
|
88
|
-
}
|
|
89
|
-
},
|
|
90
|
-
onError: () => {
|
|
91
|
-
/* stille fout — volgende interval probeert opnieuw */
|
|
92
|
-
},
|
|
93
|
-
});
|
|
94
|
-
useEffect(() => {
|
|
95
|
-
if (!activeId || didInitialBackfill.current)
|
|
96
|
-
return;
|
|
97
|
-
didInitialBackfill.current = true;
|
|
98
|
-
backfillMut.mutate();
|
|
99
|
-
const t = setInterval(() => backfillMut.mutate(), 60000);
|
|
100
|
-
return () => clearInterval(t);
|
|
101
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
102
|
-
}, [activeId]);
|
|
103
|
-
const parseMut = useMutation({
|
|
104
|
-
mutationFn: (messageId) => fetchParse({ data: { messageId } }),
|
|
105
|
-
onSuccess: (res) => {
|
|
106
|
-
if (res?.ok) {
|
|
107
|
-
toast.success("E-mail geparsed naar voorstel");
|
|
108
|
-
qc.invalidateQueries({ queryKey: ["mailbox-messages"] });
|
|
109
|
-
qc.invalidateQueries({ queryKey: ["mailbox-proposals"] });
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
toast.error(res?.error ?? "Parsen mislukt");
|
|
113
|
-
}
|
|
114
|
-
},
|
|
115
|
-
onError: (e) => toast.error(e?.message ?? "Parsen mislukt"),
|
|
116
|
-
});
|
|
117
|
-
const items = Array.isArray(data?.items) ? data.items : [];
|
|
118
|
-
const needsReauth = active?.connected && active.needs_reauth;
|
|
119
|
-
return (_jsxs("div", { className: "p-6 space-y-6", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsxs("h1", { className: "text-2xl font-semibold flex items-center gap-2", children: [_jsx(Mail, { className: "h-6 w-6 text-primary" }), " Inbox"] }), _jsx("p", { className: "text-muted-foreground text-sm mt-1", children: "Binnenkomende bestel-e-mails \u2014 geleverd door de E-mail Authenticatie module" })] }), _jsxs(Button, { variant: "outline", onClick: () => backfillMut.mutate(), disabled: backfillMut.isPending || !active?.connected, title: "Haal achterstallige events op", children: [_jsx(RefreshCw, { className: `h-4 w-4 mr-2 ${backfillMut.isPending ? "animate-spin" : ""}` }), "Synchroniseer"] })] }), activeLoaded && !active?.connected ? (_jsx(Card, { children: _jsxs(CardContent, { className: "p-4 flex items-start gap-3 text-sm", children: [_jsx(AlertCircle, { className: "h-4 w-4 text-destructive mt-0.5" }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "font-medium", children: "Nog geen e-mailadres gekoppeld" }), _jsxs("div", { className: "text-muted-foreground", children: ["Koppel in de module ", _jsx("strong", { children: "E-mail Authenticatie" }), " een adres met doeleinde", " ", _jsx("code", { children: "orders" }), " en richting ", _jsx("code", { children: "receive" }), "."] })] }), _jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsxs("a", { href: AUTH_MODULE_URL, target: "_blank", rel: "noreferrer", children: ["Open module ", _jsx(ExternalLink, { className: "h-3 w-3 ml-1" })] }) })] }) })) : needsReauth ? (_jsx(Card, { className: "border-destructive", children: _jsxs(CardContent, { className: "p-4 flex items-start gap-3 text-sm", children: [_jsx(AlertCircle, { className: "h-4 w-4 text-destructive mt-0.5" }), _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "font-medium", children: ["Heraanmelding vereist voor ", active.account_email] }), _jsx("div", { className: "text-muted-foreground", children: active.last_error ?? "De koppeling is verlopen. Log opnieuw in via de authenticatiemodule." })] }), _jsx(Button, { asChild: true, variant: "destructive", size: "sm", children: _jsxs("a", { href: `${AUTH_MODULE_URL}?focus=${active.id}`, target: "_blank", rel: "noreferrer", children: ["Opnieuw verbinden ", _jsx(ExternalLink, { className: "h-3 w-3 ml-1" })] }) })] }) })) : active?.connected ? (_jsx(Card, { children: _jsxs(CardContent, { className: "p-4 flex items-center gap-3 text-sm", children: [_jsx(Mail, { className: "h-4 w-4 text-primary" }), _jsx("span", { className: "text-muted-foreground", children: "Actieve mailbox:" }), _jsx("span", { className: "font-medium", children: active.account_email }), _jsx(Badge, { variant: "outline", className: "ml-2", children: active.provider }), active.status && active.status !== "ok" && active.status !== "verified" && (_jsx(Badge, { variant: "secondary", className: "ml-2", children: active.status }))] }) })) : null, _jsx(Card, { children: _jsx(CardContent, { className: "p-0", children: isLoading ? (_jsx("div", { className: "p-8 text-center text-muted-foreground", children: "Laden..." })) : items.length === 0 ? (_jsx("div", { className: "p-8 text-center text-muted-foreground", children: "Nog geen e-mails ontvangen. Nieuwe berichten verschijnen automatisch." })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Status" }), _jsx(TableHead, { children: "Afzender" }), _jsx(TableHead, { children: "Onderwerp" }), _jsx(TableHead, { children: "Ontvangen" }), _jsx(TableHead, { className: "text-right", children: "Actie" })] }) }), _jsx(TableBody, { children: items.map((m) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsx(Badge, { variant: STATUS_VARIANT[m.status] ?? "secondary", children: m.status }) }), _jsxs(TableCell, { children: [_jsx("div", { className: "text-sm font-medium", children: m.from_name || m.from_email }), m.from_name && _jsx("div", { className: "text-xs text-muted-foreground", children: m.from_email })] }), _jsx(TableCell, { className: "max-w-md truncate", children: m.subject || "(geen onderwerp)" }), _jsx(TableCell, { className: "text-sm text-muted-foreground", children: m.received_at ? new Date(m.received_at).toLocaleString("nl-NL") : "-" }), _jsx(TableCell, { className: "text-right", children: m.status !== "converted" && (_jsxs(Button, { size: "sm", variant: "outline", disabled: parseMut.isPending, onClick: () => parseMut.mutate(m.id), children: [_jsx(Sparkles, { className: "h-3 w-3 mr-1" }), m.status === "parsed" ? "Opnieuw parsen" : "Parse"] })) })] }, m.id))) })] })) }) })] }));
|
|
120
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ImapAccountsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/ImapAccountsCard.tsx"],"names":[],"mappings":"AAyCA,wBAAgB,gBAAgB,gCAwP/B"}
|