@flowselections/mailbox-orders 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"MailboxInboxPage.d.ts","sourceRoot":"","sources":["../../src/components/MailboxInboxPage.tsx"],"names":[],"mappings":"AAmBA,wBAAgB,gBAAgB,gCAoH/B"}
1
+ {"version":3,"file":"MailboxInboxPage.d.ts","sourceRoot":"","sources":["../../src/components/MailboxInboxPage.tsx"],"names":[],"mappings":"AA6BA,wBAAgB,gBAAgB,gCAuO/B"}
@@ -1,9 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef } from "react";
2
3
  import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
3
4
  import { useServerFn } from "@tanstack/react-start";
4
5
  import { Button, Badge, Card, CardContent, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, toast, } from "@flowselections/core";
5
- import { Mail, RefreshCw, Sparkles } from "lucide-react";
6
- import { listMessages, pollMailbox, parseMessage } from "../lib/mailbox.functions";
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";
7
10
  const STATUS_VARIANT = {
8
11
  new: "secondary",
9
12
  parsing: "outline",
@@ -15,33 +18,88 @@ const STATUS_VARIANT = {
15
18
  export function MailboxInboxPage() {
16
19
  const qc = useQueryClient();
17
20
  const fetchList = useServerFn(listMessages);
18
- const fetchPoll = useServerFn(pollMailbox);
19
21
  const fetchParse = useServerFn(parseMessage);
22
+ const fetchActive = useServerFn(getActiveOrdersMailbox);
23
+ const fetchIngest = useServerFn(ingestEmailEvent);
24
+ const fetchBackfill = useServerFn(backfillRecentEvents);
20
25
  const { data, isLoading } = useQuery({
21
26
  queryKey: ["mailbox-messages"],
22
27
  queryFn: () => fetchList(),
23
28
  });
24
- const pollMut = useMutation({
25
- mutationFn: () => fetchPoll(),
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
+ },
26
84
  onSuccess: (res) => {
27
- if (res?.ok) {
28
- const ins = res.inserted ?? 0;
29
- const app = res.autoApproved ?? 0;
30
- if (ins === 0)
31
- toast.success("Geen nieuwe e-mails");
32
- else if (app > 0)
33
- toast.success(`${ins} nieuwe e-mail(s) — ${app} automatisch omgezet in bestelling`);
34
- else
35
- toast.success(`${ins} nieuwe e-mail(s) opgehaald`);
85
+ if (res?.ok && (res.inserted ?? 0) > 0) {
36
86
  qc.invalidateQueries({ queryKey: ["mailbox-messages"] });
37
87
  qc.invalidateQueries({ queryKey: ["mailbox-proposals"] });
38
88
  }
39
- else {
40
- toast.error(res?.error ?? "Ophalen mislukt");
41
- }
42
89
  },
43
- onError: (e) => toast.error(e?.message ?? "Ophalen mislukt"),
90
+ onError: () => {
91
+ /* stille fout — volgende interval probeert opnieuw */
92
+ },
44
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]);
45
103
  const parseMut = useMutation({
46
104
  mutationFn: (messageId) => fetchParse({ data: { messageId } }),
47
105
  onSuccess: (res) => {
@@ -57,5 +115,6 @@ export function MailboxInboxPage() {
57
115
  onError: (e) => toast.error(e?.message ?? "Parsen mislukt"),
58
116
  });
59
117
  const items = Array.isArray(data?.items) ? data.items : [];
60
- 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 uit de mailbox" })] }), _jsxs(Button, { onClick: () => pollMut.mutate(), disabled: pollMut.isPending, children: [_jsx(RefreshCw, { className: `h-4 w-4 mr-2 ${pollMut.isPending ? "animate-spin" : ""}` }), "Nu ophalen"] })] }), _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. Klik \"Nu ophalen\" om de mailbox te controleren." })) : (_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))) })] })) }) })] }));
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))) })] })) }) })] }));
61
120
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAKvD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAEzE,eAAO,MAAM,mBAAmB,EAAE,UAoBjC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGvD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAEzE,eAAO,MAAM,mBAAmB,EAAE,UAmBjC,CAAC"}
package/dist-lib/index.js CHANGED
@@ -2,7 +2,6 @@
2
2
  // src/index.ts — MODULE CONTRACT (Mailbox bestellingen)
3
3
  // ============================================================================
4
4
  import { Mail, Inbox, ClipboardCheck, CheckCircle2 } from "lucide-react";
5
- import { ImapAccountsCard } from "./components/settings/ImapAccountsCard";
6
5
  import { MailboxOrderTemplateCard } from "./components/settings/MailboxOrderTemplateCard";
7
6
  export * from "./_core-safelist";
8
7
  export { MailboxInboxPage } from "./components/MailboxInboxPage";
@@ -12,7 +11,7 @@ export { MailboxProcessedPage } from "./components/MailboxProcessedPage";
12
11
  export const mailboxOrdersModule = {
13
12
  id: "mailbox_orders",
14
13
  name: "Mailbox bestellingen",
15
- version: "1.1.0",
14
+ version: "1.2.0",
16
15
  nav: {
17
16
  label: "Mailbox",
18
17
  href: "/mailbox-orders",
@@ -24,7 +23,6 @@ export const mailboxOrdersModule = {
24
23
  ],
25
24
  },
26
25
  settingsCards: [
27
- { component: ImapAccountsCard, order: 5 },
28
26
  { component: MailboxOrderTemplateCard, order: 20 },
29
27
  ],
30
28
  };