@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.
Files changed (36) hide show
  1. package/dist-lib/components/CartAssignmentWizard.d.ts +6 -0
  2. package/dist-lib/components/CartAssignmentWizard.d.ts.map +1 -0
  3. package/dist-lib/components/CartAssignmentWizard.js +199 -0
  4. package/dist-lib/components/EmailPreview.d.ts.map +1 -1
  5. package/dist-lib/components/EmailPreview.js +2 -1
  6. package/dist-lib/components/MailboxInboxPage.d.ts +2 -0
  7. package/dist-lib/components/MailboxInboxPage.d.ts.map +1 -0
  8. package/dist-lib/components/MailboxInboxPage.js +120 -0
  9. package/dist-lib/components/MailboxPageFrame.js +1 -1
  10. package/dist-lib/components/MailboxProposalsPage.js +3 -3
  11. package/dist-lib/components/MailboxSkippedPage.d.ts.map +1 -1
  12. package/dist-lib/components/MailboxSkippedPage.js +4 -4
  13. package/dist-lib/components/fs-ui/DataTable.js +3 -3
  14. package/dist-lib/components/settings/ImapAccountsCard.d.ts +2 -0
  15. package/dist-lib/components/settings/ImapAccountsCard.d.ts.map +1 -0
  16. package/dist-lib/components/settings/ImapAccountsCard.js +136 -0
  17. package/dist-lib/components/settings/ImapProfilesCard.d.ts +2 -0
  18. package/dist-lib/components/settings/ImapProfilesCard.d.ts.map +1 -0
  19. package/dist-lib/components/settings/ImapProfilesCard.js +101 -0
  20. package/dist-lib/components/settings/MailboxSettingsCard.d.ts +2 -0
  21. package/dist-lib/components/settings/MailboxSettingsCard.d.ts.map +1 -0
  22. package/dist-lib/components/settings/MailboxSettingsCard.js +85 -0
  23. package/dist-lib/integrations/supabase/client.server.js +1 -1
  24. package/dist-lib/integrations/supabase/types.d.ts +76 -0
  25. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  26. package/dist-lib/lib/imap.functions.d.ts +32852 -0
  27. package/dist-lib/lib/imap.functions.d.ts.map +1 -0
  28. package/dist-lib/lib/imap.functions.js +235 -0
  29. package/dist-lib/lib/mailbox-auto.server.d.ts +9 -0
  30. package/dist-lib/lib/mailbox-auto.server.d.ts.map +1 -0
  31. package/dist-lib/lib/mailbox-auto.server.js +198 -0
  32. package/dist-lib/lib/mailbox-filter-settings.functions.d.ts +2 -2
  33. package/dist-lib/lib/mailbox-filter-settings.functions.js +3 -3
  34. package/dist-lib/lib/mailbox.functions.d.ts +1 -1
  35. package/dist-lib/lib/mailbox.functions.js +6 -6
  36. 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,2 @@
1
+ export declare function ImapProfilesCard(): import("react").JSX.Element;
2
+ //# sourceMappingURL=ImapProfilesCard.d.ts.map
@@ -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,2 @@
1
+ export declare function MailboxSettingsCard(): import("react").JSX.Element;
2
+ //# sourceMappingURL=MailboxSettingsCard.d.ts.map
@@ -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 "./client.server.js";
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;