@flowselections/mailbox-orders 1.0.57 → 1.0.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/components/CartAssignmentWizard.d.ts +6 -0
- package/dist-lib/components/CartAssignmentWizard.d.ts.map +1 -0
- package/dist-lib/components/CartAssignmentWizard.js +199 -0
- package/dist-lib/components/EmailPreview.d.ts.map +1 -1
- package/dist-lib/components/EmailPreview.js +2 -1
- package/dist-lib/components/MailboxInboxPage.d.ts +2 -0
- package/dist-lib/components/MailboxInboxPage.d.ts.map +1 -0
- package/dist-lib/components/MailboxInboxPage.js +120 -0
- package/dist-lib/components/MailboxPageFrame.js +1 -1
- package/dist-lib/components/MailboxProposalsPage.js +3 -3
- package/dist-lib/components/MailboxSkippedPage.d.ts.map +1 -1
- package/dist-lib/components/MailboxSkippedPage.js +4 -4
- package/dist-lib/components/fs-ui/DataTable.js +3 -3
- package/dist-lib/components/settings/ImapAccountsCard.d.ts +2 -0
- package/dist-lib/components/settings/ImapAccountsCard.d.ts.map +1 -0
- package/dist-lib/components/settings/ImapAccountsCard.js +136 -0
- package/dist-lib/components/settings/ImapProfilesCard.d.ts +2 -0
- package/dist-lib/components/settings/ImapProfilesCard.d.ts.map +1 -0
- package/dist-lib/components/settings/ImapProfilesCard.js +101 -0
- package/dist-lib/components/settings/MailboxSettingsCard.d.ts +2 -0
- package/dist-lib/components/settings/MailboxSettingsCard.d.ts.map +1 -0
- package/dist-lib/components/settings/MailboxSettingsCard.js +85 -0
- package/dist-lib/integrations/supabase/client.server.js +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +76 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/imap.functions.d.ts +32852 -0
- package/dist-lib/lib/imap.functions.d.ts.map +1 -0
- package/dist-lib/lib/imap.functions.js +235 -0
- package/dist-lib/lib/mailbox-auto.server.d.ts +9 -0
- package/dist-lib/lib/mailbox-auto.server.d.ts.map +1 -0
- package/dist-lib/lib/mailbox-auto.server.js +198 -0
- package/dist-lib/lib/mailbox-filter-settings.functions.d.ts +2 -2
- package/dist-lib/lib/mailbox-filter-settings.functions.js +3 -3
- package/dist-lib/lib/mailbox.functions.d.ts +1 -1
- package/dist-lib/lib/mailbox.functions.js +6 -6
- package/package.json +2 -2
|
@@ -0,0 +1,136 @@
|
|
|
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.js";
|
|
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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImapProfilesCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/ImapProfilesCard.tsx"],"names":[],"mappings":"AAgCA,wBAAgB,gBAAgB,gCAgN/B"}
|
|
@@ -0,0 +1,101 @@
|
|
|
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.js";
|
|
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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MailboxSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/MailboxSettingsCard.tsx"],"names":[],"mappings":"AAWA,wBAAgB,mBAAmB,gCA8IlC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
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.js";
|
|
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
|
+
}
|
|
@@ -20,7 +20,7 @@ function createSupabaseAdminClient() {
|
|
|
20
20
|
let _supabaseAdmin;
|
|
21
21
|
// Server-side Supabase client with service role - bypasses RLS
|
|
22
22
|
// SECURITY: Only use this for trusted server-side operations, never expose to client code
|
|
23
|
-
// Import like: import { supabaseAdmin } from "
|
|
23
|
+
// Import like: import { supabaseAdmin } from "../../integrations/supabase/client.server.js";
|
|
24
24
|
export const supabaseAdmin = new Proxy({}, {
|
|
25
25
|
get(_, prop, receiver) {
|
|
26
26
|
if (!_supabaseAdmin)
|
|
@@ -33210,6 +33210,25 @@ export type Database = {
|
|
|
33210
33210
|
};
|
|
33211
33211
|
Returns: undefined;
|
|
33212
33212
|
};
|
|
33213
|
+
aanbiedingsmail_campagnes_lijst: {
|
|
33214
|
+
Args: {
|
|
33215
|
+
p_limit?: number;
|
|
33216
|
+
p_offset?: number;
|
|
33217
|
+
p_status?: string;
|
|
33218
|
+
p_tot?: string;
|
|
33219
|
+
p_van?: string;
|
|
33220
|
+
p_zoek?: string;
|
|
33221
|
+
};
|
|
33222
|
+
Returns: Json;
|
|
33223
|
+
};
|
|
33224
|
+
aanbiedingsmail_campagnes_tellers: {
|
|
33225
|
+
Args: {
|
|
33226
|
+
p_tot?: string;
|
|
33227
|
+
p_van?: string;
|
|
33228
|
+
p_zoek?: string;
|
|
33229
|
+
};
|
|
33230
|
+
Returns: Json;
|
|
33231
|
+
};
|
|
33213
33232
|
aanbiedingsmail_create_order: {
|
|
33214
33233
|
Args: {
|
|
33215
33234
|
p_offer_order_id: string;
|
|
@@ -33246,6 +33265,15 @@ export type Database = {
|
|
|
33246
33265
|
};
|
|
33247
33266
|
Returns: Json;
|
|
33248
33267
|
};
|
|
33268
|
+
aanbiedingsmail_ontvangers_lijst: {
|
|
33269
|
+
Args: {
|
|
33270
|
+
p_campaign_id: string;
|
|
33271
|
+
p_limit?: number;
|
|
33272
|
+
p_offset?: number;
|
|
33273
|
+
p_zoek?: string;
|
|
33274
|
+
};
|
|
33275
|
+
Returns: Json;
|
|
33276
|
+
};
|
|
33249
33277
|
aanbiedingsmail_place_offer_order: {
|
|
33250
33278
|
Args: {
|
|
33251
33279
|
p_code: string;
|
|
@@ -33265,6 +33293,12 @@ export type Database = {
|
|
|
33265
33293
|
};
|
|
33266
33294
|
Returns: string;
|
|
33267
33295
|
};
|
|
33296
|
+
aanbiedingsmail_status_sleutel: {
|
|
33297
|
+
Args: {
|
|
33298
|
+
p_status: string;
|
|
33299
|
+
};
|
|
33300
|
+
Returns: string;
|
|
33301
|
+
};
|
|
33268
33302
|
aanbiedingsmail_stock_key: {
|
|
33269
33303
|
Args: {
|
|
33270
33304
|
p_ref: string;
|
|
@@ -35882,6 +35916,19 @@ export type Database = {
|
|
|
35882
35916
|
};
|
|
35883
35917
|
Returns: Json;
|
|
35884
35918
|
};
|
|
35919
|
+
inkoop_artikelen_page: {
|
|
35920
|
+
Args: {
|
|
35921
|
+
_asc?: boolean;
|
|
35922
|
+
_limit?: number;
|
|
35923
|
+
_location?: string;
|
|
35924
|
+
_offset?: number;
|
|
35925
|
+
_search?: string;
|
|
35926
|
+
_sort?: string;
|
|
35927
|
+
_tab?: string;
|
|
35928
|
+
_unit?: string;
|
|
35929
|
+
};
|
|
35930
|
+
Returns: Json;
|
|
35931
|
+
};
|
|
35885
35932
|
inkoop_cancel_line: {
|
|
35886
35933
|
Args: {
|
|
35887
35934
|
_item_id: string;
|
|
@@ -35931,6 +35978,18 @@ export type Database = {
|
|
|
35931
35978
|
Args: never;
|
|
35932
35979
|
Returns: string;
|
|
35933
35980
|
};
|
|
35981
|
+
inkoop_orders_page: {
|
|
35982
|
+
Args: {
|
|
35983
|
+
_delivery_from?: string;
|
|
35984
|
+
_delivery_to?: string;
|
|
35985
|
+
_limit?: number;
|
|
35986
|
+
_offset?: number;
|
|
35987
|
+
_search?: string;
|
|
35988
|
+
_supplier_id?: string;
|
|
35989
|
+
_tab?: string;
|
|
35990
|
+
};
|
|
35991
|
+
Returns: Json;
|
|
35992
|
+
};
|
|
35934
35993
|
inkoop_process_request: {
|
|
35935
35994
|
Args: {
|
|
35936
35995
|
_request_id: string;
|
|
@@ -44758,6 +44817,12 @@ export type Database = {
|
|
|
44758
44817
|
};
|
|
44759
44818
|
Returns: Json;
|
|
44760
44819
|
};
|
|
44820
|
+
scan_kar_scans_lezen: {
|
|
44821
|
+
Args: {
|
|
44822
|
+
p_kar_id: string;
|
|
44823
|
+
};
|
|
44824
|
+
Returns: Json;
|
|
44825
|
+
};
|
|
44761
44826
|
scan_kar_voortgang: {
|
|
44762
44827
|
Args: {
|
|
44763
44828
|
p_cart_id: string;
|
|
@@ -44774,6 +44839,17 @@ export type Database = {
|
|
|
44774
44839
|
};
|
|
44775
44840
|
Returns: Json;
|
|
44776
44841
|
};
|
|
44842
|
+
scan_karren_overzicht: {
|
|
44843
|
+
Args: {
|
|
44844
|
+
p_limit?: number;
|
|
44845
|
+
p_offset?: number;
|
|
44846
|
+
p_status?: string;
|
|
44847
|
+
p_tot?: string;
|
|
44848
|
+
p_van?: string;
|
|
44849
|
+
p_zoek?: string;
|
|
44850
|
+
};
|
|
44851
|
+
Returns: Json;
|
|
44852
|
+
};
|
|
44777
44853
|
scan_koppelcode_maken: {
|
|
44778
44854
|
Args: {
|
|
44779
44855
|
p_scanner_id: string;
|