@flowselections/mailbox-orders 1.0.18 → 1.0.20
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 -21649
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.js +6 -12
- package/dist-lib/integrations/supabase/client.server.d.ts +18128 -9567
- 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/previewAuthStorage.d.ts +6 -0
- package/dist-lib/integrations/supabase/previewAuthStorage.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/previewAuthStorage.js +92 -0
- package/dist-lib/integrations/supabase/types.d.ts +18505 -9833
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +4 -0
- 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 +5 -3
- package/dist-lib/lib/mailbox.functions.d.ts.map +1 -1
- package/dist-lib/lib/mailbox.functions.js +28 -28
- 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,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"}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useState } from "react";
|
|
3
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle, Button, Input, Label, Switch, Badge, Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, toast, } from "@flowselections/core";
|
|
4
|
-
import { Server, Plus, Pencil, Trash2, TestTube2, RefreshCw, CheckCircle2, PlayCircle } from "lucide-react";
|
|
5
|
-
import { listImapAccounts, upsertImapAccount, deleteImapAccount, testImapAccount, listImapProfiles, activateImapAccount, pollImapProfile, } from "../../lib/imap.functions";
|
|
6
|
-
const emptyAccount = {
|
|
7
|
-
id: undefined,
|
|
8
|
-
name: "",
|
|
9
|
-
email: "",
|
|
10
|
-
host: "",
|
|
11
|
-
port: 993,
|
|
12
|
-
username: "",
|
|
13
|
-
use_tls: true,
|
|
14
|
-
password: "",
|
|
15
|
-
is_active: true,
|
|
16
|
-
};
|
|
17
|
-
export function ImapAccountsCard() {
|
|
18
|
-
const [items, setItems] = useState([]);
|
|
19
|
-
const [activeAccountId, setActiveAccountId] = useState(null);
|
|
20
|
-
const [activeProfileId, setActiveProfileId] = useState(null);
|
|
21
|
-
const [loading, setLoading] = useState(true);
|
|
22
|
-
const [editing, setEditing] = useState(null);
|
|
23
|
-
const [saving, setSaving] = useState(false);
|
|
24
|
-
const [testingId, setTestingId] = useState(null);
|
|
25
|
-
const [pollingId, setPollingId] = useState(null);
|
|
26
|
-
const reload = async () => {
|
|
27
|
-
setLoading(true);
|
|
28
|
-
try {
|
|
29
|
-
const [accRes, profRes] = await Promise.all([listImapAccounts(), listImapProfiles()]);
|
|
30
|
-
setItems(Array.isArray(accRes?.items) ? accRes.items : []);
|
|
31
|
-
const profs = Array.isArray(profRes?.items) ? profRes.items : [];
|
|
32
|
-
const orders = profs.find((p) => p.profile_key === "mailbox_orders") ?? profs[0] ?? null;
|
|
33
|
-
setActiveAccountId(orders?.account_id ?? null);
|
|
34
|
-
setActiveProfileId(orders?.id ?? null);
|
|
35
|
-
}
|
|
36
|
-
finally {
|
|
37
|
-
setLoading(false);
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
useEffect(() => { reload(); }, []);
|
|
41
|
-
const handleSave = async () => {
|
|
42
|
-
if (!editing)
|
|
43
|
-
return;
|
|
44
|
-
setSaving(true);
|
|
45
|
-
try {
|
|
46
|
-
const saved = await upsertImapAccount({
|
|
47
|
-
data: {
|
|
48
|
-
id: editing.id,
|
|
49
|
-
name: editing.name,
|
|
50
|
-
email: editing.email || null,
|
|
51
|
-
host: editing.host,
|
|
52
|
-
port: Number(editing.port),
|
|
53
|
-
username: editing.username,
|
|
54
|
-
use_tls: editing.use_tls,
|
|
55
|
-
password: editing.password || undefined,
|
|
56
|
-
is_active: editing.is_active,
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
toast.success("Account opgeslagen");
|
|
60
|
-
// Nieuw account direct als actief markeren als er nog geen is
|
|
61
|
-
if (!editing.id && saved?.id && !activeAccountId) {
|
|
62
|
-
await activateImapAccount({ data: { account_id: saved.id } });
|
|
63
|
-
}
|
|
64
|
-
setEditing(null);
|
|
65
|
-
reload();
|
|
66
|
-
}
|
|
67
|
-
catch (e) {
|
|
68
|
-
toast.error(e?.message ?? "Opslaan mislukt");
|
|
69
|
-
}
|
|
70
|
-
finally {
|
|
71
|
-
setSaving(false);
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
const handleDelete = async (id) => {
|
|
75
|
-
if (!confirm("Account verwijderen?"))
|
|
76
|
-
return;
|
|
77
|
-
try {
|
|
78
|
-
await deleteImapAccount({ data: { id } });
|
|
79
|
-
toast.success("Verwijderd");
|
|
80
|
-
reload();
|
|
81
|
-
}
|
|
82
|
-
catch (e) {
|
|
83
|
-
toast.error(e?.message ?? "Verwijderen mislukt");
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
const handleTest = async (id) => {
|
|
87
|
-
setTestingId(id);
|
|
88
|
-
try {
|
|
89
|
-
const res = await testImapAccount({ data: { id } });
|
|
90
|
-
if (res?.ok)
|
|
91
|
-
toast.success("Verbinding OK");
|
|
92
|
-
else
|
|
93
|
-
toast.error(res?.error ?? "Verbinding mislukt");
|
|
94
|
-
reload();
|
|
95
|
-
}
|
|
96
|
-
finally {
|
|
97
|
-
setTestingId(null);
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
const handleActivate = async (id) => {
|
|
101
|
-
try {
|
|
102
|
-
await activateImapAccount({ data: { account_id: id } });
|
|
103
|
-
toast.success("Account geactiveerd");
|
|
104
|
-
reload();
|
|
105
|
-
}
|
|
106
|
-
catch (e) {
|
|
107
|
-
toast.error(e?.message ?? "Activeren mislukt");
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
const handlePoll = async () => {
|
|
111
|
-
if (!activeProfileId)
|
|
112
|
-
return;
|
|
113
|
-
setPollingId(activeProfileId);
|
|
114
|
-
try {
|
|
115
|
-
const res = await pollImapProfile({ data: { profile_id: activeProfileId } });
|
|
116
|
-
if (res?.ok)
|
|
117
|
-
toast.success(`${res.inserted ?? 0} nieuwe e-mail(s) opgehaald`);
|
|
118
|
-
else
|
|
119
|
-
toast.error(res?.error ?? "Ophalen mislukt");
|
|
120
|
-
}
|
|
121
|
-
finally {
|
|
122
|
-
setPollingId(null);
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
return (_jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Server, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "IMAP Account" })] }), _jsx(CardDescription, { children: "Beheer je e-mailaccounts en kies eenvoudig welke nu actief is voor de mailbox-bestellingen." })] }), _jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: handlePoll, disabled: !activeProfileId || !!pollingId, children: [_jsx(PlayCircle, { className: `h-4 w-4 mr-2 ${pollingId ? "animate-pulse" : ""}` }), "Nu ophalen"] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: reload, disabled: loading, children: [_jsx(RefreshCw, { className: `h-4 w-4 mr-2 ${loading ? "animate-spin" : ""}` }), "Vernieuwen"] }), _jsxs(Button, { size: "sm", onClick: () => setEditing({ ...emptyAccount }), children: [_jsx(Plus, { className: "h-4 w-4 mr-2" }), "Account toevoegen"] })] })] }), _jsx(CardContent, { children: loading ? (_jsx("div", { className: "text-sm text-muted-foreground", children: "Laden..." })) : items.length === 0 ? (_jsx("div", { className: "text-sm text-muted-foreground", children: "Nog geen accounts. Klik op \"Account toevoegen\"." })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { className: "w-[80px]", children: "Actief" }), _jsx(TableHead, { children: "Naam" }), _jsx(TableHead, { children: "E-mail" }), _jsx(TableHead, { children: "Server" }), _jsx(TableHead, { children: "Laatste test" }), _jsx(TableHead, { className: "text-right", children: "Acties" })] }) }), _jsx(TableBody, { children: items.map((a) => {
|
|
126
|
-
const isActive = a.id === activeAccountId;
|
|
127
|
-
return (_jsxs(TableRow, { className: isActive ? "bg-primary/5" : "", children: [_jsx(TableCell, { children: isActive ? (_jsxs(Badge, { variant: "default", className: "gap-1", children: [_jsx(CheckCircle2, { className: "h-3 w-3" }), "Actief"] })) : (_jsx(Button, { variant: "outline", size: "sm", onClick: () => handleActivate(a.id), children: "Activeer" })) }), _jsx(TableCell, { className: "font-medium", children: a.name }), _jsx(TableCell, { className: "text-muted-foreground", children: a.email ?? a.username }), _jsxs(TableCell, { className: "text-muted-foreground", children: [a.host, ":", a.port, a.use_tls ? " · TLS" : ""] }), _jsx(TableCell, { children: a.last_tested_at ? (a.last_test_ok
|
|
128
|
-
? _jsx(Badge, { variant: "default", children: "OK" })
|
|
129
|
-
: _jsx(Badge, { variant: "destructive", title: a.last_test_error ?? "", children: "Fout" })) : _jsx("span", { className: "text-xs text-muted-foreground", children: "\u2014" }) }), _jsx(TableCell, { className: "text-right", children: _jsxs("div", { className: "flex gap-1 justify-end", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => handleTest(a.id), disabled: testingId === a.id, title: "Test verbinding", children: _jsx(TestTube2, { className: "h-4 w-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setEditing({
|
|
130
|
-
id: a.id, name: a.name, email: a.email ?? "", host: a.host, port: a.port,
|
|
131
|
-
username: a.username, use_tls: a.use_tls,
|
|
132
|
-
password: "",
|
|
133
|
-
is_active: a.is_active,
|
|
134
|
-
}), title: "Bewerken", children: _jsx(Pencil, { className: "h-4 w-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => handleDelete(a.id), title: "Verwijderen", children: _jsx(Trash2, { className: "h-4 w-4 text-destructive" }) })] }) })] }, a.id));
|
|
135
|
-
}) })] })) }), _jsx(Dialog, { open: !!editing, onOpenChange: (o) => !o && setEditing(null), children: _jsxs(DialogContent, { className: "max-w-lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: editing?.id ? "Account bewerken" : "Nieuw account" }) }), editing && (_jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { className: "space-y-1 col-span-2", children: [_jsx(Label, { children: "Naam" }), _jsx(Input, { value: editing.name, onChange: (e) => setEditing({ ...editing, name: e.target.value }), placeholder: "bv. Orders Mailbox" })] }), _jsxs("div", { className: "space-y-1 col-span-2", children: [_jsx(Label, { children: "E-mailadres" }), _jsx(Input, { type: "email", value: editing.email, onChange: (e) => setEditing({ ...editing, email: e.target.value }), placeholder: "bestellingen@example.com" })] }), _jsxs("div", { className: "space-y-1 col-span-2", children: [_jsx(Label, { children: "IMAP-server" }), _jsx(Input, { value: editing.host, onChange: (e) => setEditing({ ...editing, host: e.target.value }), placeholder: "imap.example.com" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Poort" }), _jsx(Input, { type: "number", value: editing.port, onChange: (e) => setEditing({ ...editing, port: Number(e.target.value) }) })] }), _jsxs("div", { className: "space-y-1 flex flex-col", children: [_jsx(Label, { children: "TLS / SSL" }), _jsx(Switch, { checked: editing.use_tls, onCheckedChange: (v) => setEditing({ ...editing, use_tls: v }) })] }), _jsxs("div", { className: "space-y-1 col-span-2", children: [_jsx(Label, { children: "Gebruikersnaam" }), _jsx(Input, { value: editing.username, onChange: (e) => setEditing({ ...editing, username: e.target.value }) })] }), _jsxs("div", { className: "space-y-1 col-span-2", children: [_jsx(Label, { children: "Wachtwoord" }), _jsx(Input, { type: "password", value: editing.password, onChange: (e) => setEditing({ ...editing, password: e.target.value }), placeholder: editing.id ? "Laat leeg om huidig wachtwoord te behouden" : "Voer wachtwoord in", autoComplete: "new-password" }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Wachtwoord wordt versleuteld opgeslagen en alleen gebruikt voor de IMAP-verbinding." })] }), _jsxs("div", { className: "col-span-2 flex items-center justify-between", children: [_jsx(Label, { children: "Actief" }), _jsx(Switch, { checked: editing.is_active, onCheckedChange: (v) => setEditing({ ...editing, is_active: v }) })] })] })), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => setEditing(null), children: "Annuleren" }), _jsx(Button, { onClick: handleSave, disabled: saving, children: saving ? "Opslaan..." : "Opslaan" })] })] }) })] }));
|
|
136
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ImapProfilesCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/ImapProfilesCard.tsx"],"names":[],"mappings":"AAgCA,wBAAgB,gBAAgB,gCAgN/B"}
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useState } from "react";
|
|
3
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle, Button, Input, Label, Switch, Badge, Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, toast, } from "@flowselections/core";
|
|
4
|
-
import { Inbox, Plus, Pencil, Trash2, RefreshCw, PlayCircle } from "lucide-react";
|
|
5
|
-
import { listImapAccounts, listImapProfiles, upsertImapProfile, deleteImapProfile, setProfileAccount, pollImapProfile, } from "../../lib/imap.functions";
|
|
6
|
-
const emptyProfile = {
|
|
7
|
-
id: undefined,
|
|
8
|
-
profile_key: "",
|
|
9
|
-
name: "",
|
|
10
|
-
account_id: null,
|
|
11
|
-
folder: "INBOX",
|
|
12
|
-
polling_enabled: false,
|
|
13
|
-
};
|
|
14
|
-
export function ImapProfilesCard() {
|
|
15
|
-
const [profiles, setProfiles] = useState([]);
|
|
16
|
-
const [accounts, setAccounts] = useState([]);
|
|
17
|
-
const [loading, setLoading] = useState(true);
|
|
18
|
-
const [editing, setEditing] = useState(null);
|
|
19
|
-
const [saving, setSaving] = useState(false);
|
|
20
|
-
const [pollingId, setPollingId] = useState(null);
|
|
21
|
-
const reload = async () => {
|
|
22
|
-
setLoading(true);
|
|
23
|
-
try {
|
|
24
|
-
const [p, a] = await Promise.all([listImapProfiles(), listImapAccounts()]);
|
|
25
|
-
setProfiles(Array.isArray(p?.items) ? p.items : []);
|
|
26
|
-
setAccounts(Array.isArray(a?.items) ? a.items : []);
|
|
27
|
-
}
|
|
28
|
-
finally {
|
|
29
|
-
setLoading(false);
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
useEffect(() => { reload(); }, []);
|
|
33
|
-
const handleSave = async () => {
|
|
34
|
-
if (!editing)
|
|
35
|
-
return;
|
|
36
|
-
setSaving(true);
|
|
37
|
-
try {
|
|
38
|
-
await upsertImapProfile({ data: { ...editing } });
|
|
39
|
-
toast.success("Profiel opgeslagen");
|
|
40
|
-
setEditing(null);
|
|
41
|
-
reload();
|
|
42
|
-
}
|
|
43
|
-
catch (e) {
|
|
44
|
-
toast.error(e?.message ?? "Opslaan mislukt");
|
|
45
|
-
}
|
|
46
|
-
finally {
|
|
47
|
-
setSaving(false);
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
const handleDelete = async (id) => {
|
|
51
|
-
if (!confirm("Profiel verwijderen?"))
|
|
52
|
-
return;
|
|
53
|
-
try {
|
|
54
|
-
await deleteImapProfile({ data: { id } });
|
|
55
|
-
toast.success("Verwijderd");
|
|
56
|
-
reload();
|
|
57
|
-
}
|
|
58
|
-
catch (e) {
|
|
59
|
-
toast.error(e?.message ?? "Verwijderen mislukt");
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
const handleChangeAccount = async (profile_id, account_id) => {
|
|
63
|
-
try {
|
|
64
|
-
await setProfileAccount({ data: { profile_id, account_id: account_id === "__none__" ? null : account_id } });
|
|
65
|
-
reload();
|
|
66
|
-
}
|
|
67
|
-
catch (e) {
|
|
68
|
-
toast.error(e?.message ?? "Wijzigen mislukt");
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
const handlePoll = async (id) => {
|
|
72
|
-
setPollingId(id);
|
|
73
|
-
try {
|
|
74
|
-
const res = await pollImapProfile({ data: { profile_id: id } });
|
|
75
|
-
if (res?.ok)
|
|
76
|
-
toast.success(`${res.inserted ?? 0} nieuwe e-mail(s) opgehaald`);
|
|
77
|
-
else
|
|
78
|
-
toast.error(res?.error ?? "Ophalen mislukt");
|
|
79
|
-
reload();
|
|
80
|
-
}
|
|
81
|
-
finally {
|
|
82
|
-
setPollingId(null);
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
const handleTogglePolling = async (p, v) => {
|
|
86
|
-
try {
|
|
87
|
-
await upsertImapProfile({ data: {
|
|
88
|
-
id: p.id, profile_key: p.profile_key, name: p.name,
|
|
89
|
-
account_id: p.account_id, folder: p.folder, polling_enabled: v,
|
|
90
|
-
} });
|
|
91
|
-
reload();
|
|
92
|
-
}
|
|
93
|
-
catch (e) {
|
|
94
|
-
toast.error(e?.message ?? "Wijzigen mislukt");
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
return (_jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Inbox, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "IMAP Profielen" })] }), _jsx(CardDescription, { children: "Profielen zijn de logische \"rol\" waar modules aan koppelen (bv. Orders, Offertes, Support). Wissel hier het achterliggende account zonder dat modules iets merken." })] }), _jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: reload, disabled: loading, children: [_jsx(RefreshCw, { className: `h-4 w-4 mr-2 ${loading ? "animate-spin" : ""}` }), "Vernieuwen"] }), _jsxs(Button, { size: "sm", onClick: () => setEditing({ ...emptyProfile }), children: [_jsx(Plus, { className: "h-4 w-4 mr-2" }), "Profiel toevoegen"] })] })] }), _jsx(CardContent, { children: loading ? (_jsx("div", { className: "text-sm text-muted-foreground", children: "Laden..." })) : profiles.length === 0 ? (_jsx("div", { className: "text-sm text-muted-foreground", children: "Nog geen profielen." })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Profiel" }), _jsx(TableHead, { children: "Sleutel" }), _jsx(TableHead, { children: "Account" }), _jsx(TableHead, { children: "Map" }), _jsx(TableHead, { children: "Auto" }), _jsx(TableHead, { children: "Laatste sync" }), _jsx(TableHead, { className: "text-right", children: "Acties" })] }) }), _jsx(TableBody, { children: profiles.map((p) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: p.name }), _jsx(TableCell, { children: _jsx("code", { className: "text-xs", children: p.profile_key }) }), _jsx(TableCell, { children: _jsxs(Select, { value: p.account_id ?? "__none__", onValueChange: (v) => handleChangeAccount(p.id, v), children: [_jsx(SelectTrigger, { className: "h-8 w-[220px]", children: _jsx(SelectValue, { placeholder: "Geen account" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "__none__", children: "\u2014 Geen \u2014" }), accounts.map((a) => (_jsx(SelectItem, { value: a.id, children: a.name }, a.id)))] })] }) }), _jsx(TableCell, { className: "text-muted-foreground text-xs", children: p.folder }), _jsx(TableCell, { children: _jsx(Switch, { checked: p.polling_enabled, onCheckedChange: (v) => handleTogglePolling(p, v) }) }), _jsxs(TableCell, { className: "text-xs", children: [p.last_polled_at ? new Date(p.last_polled_at).toLocaleString("nl-NL") : "—", p.last_error ? _jsx(Badge, { variant: "destructive", className: "ml-2", title: p.last_error, children: "Fout" }) : null] }), _jsx(TableCell, { className: "text-right", children: _jsxs("div", { className: "flex gap-1 justify-end", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => handlePoll(p.id), disabled: pollingId === p.id || !p.account_id, children: _jsx(PlayCircle, { className: `h-4 w-4 ${pollingId === p.id ? "animate-pulse" : ""}` }) }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setEditing({
|
|
98
|
-
id: p.id, profile_key: p.profile_key, name: p.name,
|
|
99
|
-
account_id: p.account_id, folder: p.folder, polling_enabled: p.polling_enabled,
|
|
100
|
-
}), children: _jsx(Pencil, { className: "h-4 w-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => handleDelete(p.id), children: _jsx(Trash2, { className: "h-4 w-4 text-destructive" }) })] }) })] }, p.id))) })] })) }), _jsx(Dialog, { open: !!editing, onOpenChange: (o) => !o && setEditing(null), children: _jsxs(DialogContent, { className: "max-w-lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: editing?.id ? "Profiel bewerken" : "Nieuw profiel" }) }), editing && (_jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { className: "space-y-1 col-span-2", children: [_jsx(Label, { children: "Naam" }), _jsx(Input, { value: editing.name, onChange: (e) => setEditing({ ...editing, name: e.target.value }), placeholder: "bv. Mailbox Orders" })] }), _jsxs("div", { className: "space-y-1 col-span-2", children: [_jsx(Label, { children: "Profielsleutel (uniek)" }), _jsx(Input, { value: editing.profile_key, onChange: (e) => setEditing({ ...editing, profile_key: e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, "_") }), placeholder: "mailbox_orders", disabled: !!editing.id }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Modules verwijzen naar deze sleutel." })] }), _jsxs("div", { className: "space-y-1 col-span-2", children: [_jsx(Label, { children: "Gekoppeld account" }), _jsxs(Select, { value: editing.account_id ?? "__none__", onValueChange: (v) => setEditing({ ...editing, account_id: v === "__none__" ? null : v }), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "__none__", children: "\u2014 Geen \u2014" }), accounts.map((a) => (_jsx(SelectItem, { value: a.id, children: a.name }, a.id)))] })] })] }), _jsxs("div", { className: "space-y-1 col-span-2", children: [_jsx(Label, { children: "Map" }), _jsx(Input, { value: editing.folder, onChange: (e) => setEditing({ ...editing, folder: e.target.value }) })] }), _jsxs("div", { className: "col-span-2 flex items-center justify-between", children: [_jsx(Label, { children: "Automatisch ophalen (elke 5 min)" }), _jsx(Switch, { checked: editing.polling_enabled, onCheckedChange: (v) => setEditing({ ...editing, polling_enabled: v }) })] })] })), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => setEditing(null), children: "Annuleren" }), _jsx(Button, { onClick: handleSave, disabled: saving, children: saving ? "Opslaan..." : "Opslaan" })] })] }) })] }));
|
|
101
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"MailboxSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/MailboxSettingsCard.tsx"],"names":[],"mappings":"AAWA,wBAAgB,mBAAmB,gCA8IlC"}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useState } from "react";
|
|
3
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle, Button, Input, Label, Switch, toast, } from "@flowselections/core";
|
|
4
|
-
import { Mail, RefreshCw } from "lucide-react";
|
|
5
|
-
import { getMailboxSettings, saveMailboxSettings, testMailboxConnection, pollMailbox, } from "../../lib/mailbox.functions";
|
|
6
|
-
export function MailboxSettingsCard() {
|
|
7
|
-
const [form, setForm] = useState({
|
|
8
|
-
imap_host: "",
|
|
9
|
-
imap_port: 993,
|
|
10
|
-
imap_username: "",
|
|
11
|
-
imap_use_tls: true,
|
|
12
|
-
folder: "INBOX",
|
|
13
|
-
polling_enabled: false,
|
|
14
|
-
});
|
|
15
|
-
const [loading, setLoading] = useState(true);
|
|
16
|
-
const [saving, setSaving] = useState(false);
|
|
17
|
-
const [testing, setTesting] = useState(false);
|
|
18
|
-
const [polling, setPolling] = useState(false);
|
|
19
|
-
useEffect(() => {
|
|
20
|
-
(async () => {
|
|
21
|
-
try {
|
|
22
|
-
const res = await getMailboxSettings();
|
|
23
|
-
if (res?.settings) {
|
|
24
|
-
setForm({
|
|
25
|
-
imap_host: res.settings.imap_host ?? "",
|
|
26
|
-
imap_port: res.settings.imap_port ?? 993,
|
|
27
|
-
imap_username: res.settings.imap_username ?? "",
|
|
28
|
-
imap_use_tls: res.settings.imap_use_tls ?? true,
|
|
29
|
-
folder: res.settings.folder ?? "INBOX",
|
|
30
|
-
polling_enabled: res.settings.polling_enabled ?? false,
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
finally {
|
|
35
|
-
setLoading(false);
|
|
36
|
-
}
|
|
37
|
-
})();
|
|
38
|
-
}, []);
|
|
39
|
-
const handleSave = async () => {
|
|
40
|
-
setSaving(true);
|
|
41
|
-
try {
|
|
42
|
-
await saveMailboxSettings({ data: form });
|
|
43
|
-
toast.success("Instellingen opgeslagen");
|
|
44
|
-
}
|
|
45
|
-
catch (e) {
|
|
46
|
-
toast.error(e?.message ?? "Opslaan mislukt");
|
|
47
|
-
}
|
|
48
|
-
finally {
|
|
49
|
-
setSaving(false);
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
const handleTest = async () => {
|
|
53
|
-
setTesting(true);
|
|
54
|
-
try {
|
|
55
|
-
const res = await testMailboxConnection();
|
|
56
|
-
if (res?.ok)
|
|
57
|
-
toast.success("Verbinding OK");
|
|
58
|
-
else
|
|
59
|
-
toast.error(res?.error ?? "Verbinding mislukt");
|
|
60
|
-
}
|
|
61
|
-
catch (e) {
|
|
62
|
-
toast.error(e?.message ?? "Verbinding mislukt");
|
|
63
|
-
}
|
|
64
|
-
finally {
|
|
65
|
-
setTesting(false);
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
const handlePoll = async () => {
|
|
69
|
-
setPolling(true);
|
|
70
|
-
try {
|
|
71
|
-
const res = await pollMailbox();
|
|
72
|
-
if (res?.ok)
|
|
73
|
-
toast.success(`${res.inserted ?? 0} nieuwe e-mail(s) opgehaald`);
|
|
74
|
-
else
|
|
75
|
-
toast.error(res?.error ?? "Ophalen mislukt");
|
|
76
|
-
}
|
|
77
|
-
catch (e) {
|
|
78
|
-
toast.error(e?.message ?? "Ophalen mislukt");
|
|
79
|
-
}
|
|
80
|
-
finally {
|
|
81
|
-
setPolling(false);
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Mail, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Mailbox (IMAP)" })] }), _jsx(CardDescription, { children: "IMAP-mailbox waaruit bestel-e-mails worden opgehaald. Het wachtwoord staat als geheime sleutel opgeslagen." })] }), _jsx(CardContent, { className: "space-y-4", children: loading ? (_jsx("div", { className: "text-sm text-muted-foreground", children: "Laden..." })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { className: "space-y-2 col-span-2", children: [_jsx(Label, { htmlFor: "host", children: "IMAP host" }), _jsx(Input, { id: "host", value: form.imap_host, onChange: (e) => setForm({ ...form, imap_host: e.target.value }), placeholder: "imap.example.com" })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "port", children: "Poort" }), _jsx(Input, { id: "port", type: "number", value: form.imap_port, onChange: (e) => setForm({ ...form, imap_port: Number(e.target.value) }) })] }), _jsxs("div", { className: "space-y-2 flex flex-col", children: [_jsx(Label, { htmlFor: "tls", children: "TLS" }), _jsx(Switch, { id: "tls", checked: form.imap_use_tls, onCheckedChange: (v) => setForm({ ...form, imap_use_tls: v }) })] }), _jsxs("div", { className: "space-y-2 col-span-2", children: [_jsx(Label, { htmlFor: "user", children: "Gebruikersnaam" }), _jsx(Input, { id: "user", value: form.imap_username, onChange: (e) => setForm({ ...form, imap_username: e.target.value }), placeholder: "bestellingen@example.com" })] }), _jsxs("div", { className: "space-y-2 col-span-2", children: [_jsx(Label, { htmlFor: "folder", children: "Map" }), _jsx(Input, { id: "folder", value: form.folder, onChange: (e) => setForm({ ...form, folder: e.target.value }) })] }), _jsxs("div", { className: "space-y-2 col-span-2 flex items-center justify-between", children: [_jsx(Label, { htmlFor: "poll", children: "Automatisch ophalen (elke 5 min)" }), _jsx(Switch, { id: "poll", checked: form.polling_enabled, onCheckedChange: (v) => setForm({ ...form, polling_enabled: v }) })] })] }), _jsxs("div", { className: "flex gap-2 flex-wrap", children: [_jsx(Button, { onClick: handleSave, disabled: saving, children: saving ? "Opslaan..." : "Opslaan" }), _jsx(Button, { variant: "outline", onClick: handleTest, disabled: testing, children: testing ? "Testen..." : "Test verbinding" }), _jsxs(Button, { variant: "outline", onClick: handlePoll, disabled: polling, children: [_jsx(RefreshCw, { className: `h-4 w-4 mr-2 ${polling ? "animate-spin" : ""}` }), "Nu ophalen"] })] }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Het IMAP-wachtwoord is opgeslagen als geheime sleutel ", _jsx("code", { children: "MAILBOX_IMAP_PASSWORD" }), "."] })] })) })] }));
|
|
85
|
-
}
|