@flowselections/mailbox-orders 1.0.1
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/_core-safelist.d.ts +2 -0
- package/dist-lib/_core-safelist.d.ts.map +1 -0
- package/dist-lib/_core-safelist.js +15 -0
- 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 +61 -0
- package/dist-lib/components/MailboxProcessedPage.d.ts +2 -0
- package/dist-lib/components/MailboxProcessedPage.d.ts.map +1 -0
- package/dist-lib/components/MailboxProcessedPage.js +21 -0
- package/dist-lib/components/MailboxProposalsPage.d.ts +2 -0
- package/dist-lib/components/MailboxProposalsPage.d.ts.map +1 -0
- package/dist-lib/components/MailboxProposalsPage.js +198 -0
- package/dist-lib/components/SearchableSelect.d.ts +18 -0
- package/dist-lib/components/SearchableSelect.d.ts.map +1 -0
- package/dist-lib/components/SearchableSelect.js +20 -0
- 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/MailboxOrderTemplateCard.d.ts +2 -0
- package/dist-lib/components/settings/MailboxOrderTemplateCard.d.ts.map +1 -0
- package/dist-lib/components/settings/MailboxOrderTemplateCard.js +98 -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/index.d.ts +12 -0
- package/dist-lib/index.d.ts.map +1 -0
- package/dist-lib/index.js +34 -0
- package/dist-lib/integrations/supabase/auth-attacher.d.ts +2 -0
- package/dist-lib/integrations/supabase/auth-attacher.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/auth-attacher.js +15 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +2978 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/auth-middleware.js +52 -0
- package/dist-lib/integrations/supabase/client.d.ts +2974 -0
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/client.js +13 -0
- package/dist-lib/integrations/supabase/client.server.d.ts +2974 -0
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/client.server.js +30 -0
- package/dist-lib/integrations/supabase/types.d.ts +3119 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/types.js +8 -0
- 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-ai.server.d.ts +32 -0
- package/dist-lib/lib/mailbox-ai.server.d.ts.map +1 -0
- package/dist-lib/lib/mailbox-ai.server.js +107 -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-template.functions.d.ts +17913 -0
- package/dist-lib/lib/mailbox-template.functions.d.ts.map +1 -0
- package/dist-lib/lib/mailbox-template.functions.js +106 -0
- package/dist-lib/lib/mailbox.functions.d.ts +32888 -0
- package/dist-lib/lib/mailbox.functions.d.ts.map +1 -0
- package/dist-lib/lib/mailbox.functions.js +334 -0
- package/dist-lib/lib/utils.d.ts +3 -0
- package/dist-lib/lib/utils.d.ts.map +1 -0
- package/dist-lib/lib/utils.js +5 -0
- package/dist-lib/lib/validationSchemas.d.ts +15 -0
- package/dist-lib/lib/validationSchemas.d.ts.map +1 -0
- package/dist-lib/lib/validationSchemas.js +25 -0
- package/dist-lib/styles.css +1 -0
- package/package.json +96 -0
- package/public/flowselections-assets/template-module/README.md +15 -0
|
@@ -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";
|
|
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":"MailboxOrderTemplateCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/MailboxOrderTemplateCard.tsx"],"names":[],"mappings":"AA8CA,wBAAgB,wBAAwB,gCAgHvC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
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, Textarea, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, toast, } from "@flowselections/core";
|
|
4
|
+
import { Settings2, Plus, Trash2, ArrowUp, ArrowDown } from "lucide-react";
|
|
5
|
+
import { listTemplateFields, upsertTemplateField, deleteTemplateField, reorderTemplateFields, } from "../../lib/mailbox-template.functions";
|
|
6
|
+
const FIELD_TYPES = [
|
|
7
|
+
{ value: "text", label: "Tekst", defaultSource: "static" },
|
|
8
|
+
{ value: "number", label: "Nummer", defaultSource: "static" },
|
|
9
|
+
{ value: "date", label: "Datum", defaultSource: "static" },
|
|
10
|
+
{ value: "select", label: "Keuzelijst", defaultSource: "static" },
|
|
11
|
+
{ value: "multiselect", label: "Meerkeuzelijst", defaultSource: "static" },
|
|
12
|
+
{ value: "boolean", label: "Ja/Nee", defaultSource: "static" },
|
|
13
|
+
{ value: "customer", label: "Klant", defaultSource: "database", defaultTable: "customers" },
|
|
14
|
+
{ value: "product", label: "Product", defaultSource: "database", defaultTable: "products" },
|
|
15
|
+
{ value: "plant_type", label: "Plantsoort", defaultSource: "database", defaultTable: "products" },
|
|
16
|
+
{ value: "supplier", label: "Leverancier", defaultSource: "database", defaultTable: "suppliers" },
|
|
17
|
+
{ value: "note", label: "Opmerking", defaultSource: "static" },
|
|
18
|
+
];
|
|
19
|
+
export function MailboxOrderTemplateCard() {
|
|
20
|
+
const [items, setItems] = useState([]);
|
|
21
|
+
const [loading, setLoading] = useState(true);
|
|
22
|
+
const [editing, setEditing] = useState(null);
|
|
23
|
+
const load = async () => {
|
|
24
|
+
const res = await listTemplateFields();
|
|
25
|
+
const safe = Array.isArray(res?.items) ? res.items : [];
|
|
26
|
+
setItems(safe);
|
|
27
|
+
setLoading(false);
|
|
28
|
+
};
|
|
29
|
+
useEffect(() => { load(); }, []);
|
|
30
|
+
const handleSave = async (field) => {
|
|
31
|
+
try {
|
|
32
|
+
await upsertTemplateField({ data: field });
|
|
33
|
+
toast.success("Veld opgeslagen");
|
|
34
|
+
setEditing(null);
|
|
35
|
+
await load();
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
toast.error(e?.message ?? "Opslaan mislukt");
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const handleDelete = async (id) => {
|
|
42
|
+
if (!confirm("Veld verwijderen?"))
|
|
43
|
+
return;
|
|
44
|
+
try {
|
|
45
|
+
await deleteTemplateField({ data: { id } });
|
|
46
|
+
toast.success("Verwijderd");
|
|
47
|
+
await load();
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
toast.error(e?.message ?? "Verwijderen mislukt");
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const move = async (idx, dir) => {
|
|
54
|
+
const target = idx + dir;
|
|
55
|
+
if (target < 0 || target >= items.length)
|
|
56
|
+
return;
|
|
57
|
+
const next = [...items];
|
|
58
|
+
[next[idx], next[target]] = [next[target], next[idx]];
|
|
59
|
+
setItems(next);
|
|
60
|
+
await reorderTemplateFields({ data: { ids: next.map((f) => f.id).filter(Boolean) } });
|
|
61
|
+
};
|
|
62
|
+
const addNew = () => {
|
|
63
|
+
setEditing({
|
|
64
|
+
key: "",
|
|
65
|
+
label: "",
|
|
66
|
+
field_type: "text",
|
|
67
|
+
required: false,
|
|
68
|
+
visible: true,
|
|
69
|
+
sort_order: (items.length + 1) * 10,
|
|
70
|
+
default_value: null,
|
|
71
|
+
options: null,
|
|
72
|
+
source: "static",
|
|
73
|
+
source_table: null,
|
|
74
|
+
ai_enabled: true,
|
|
75
|
+
ai_hint: null,
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Settings2, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Ordertemplate" })] }), _jsx(CardDescription, { children: "Stel zelf samen welke velden de AI moet invullen bij binnenkomende bestel-e-mails." })] }), _jsx(CardContent, { className: "space-y-3", children: loading ? (_jsx("div", { className: "text-sm text-muted-foreground", children: "Laden..." })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "space-y-2", children: items.map((f, idx) => (_jsxs("div", { className: "flex items-center gap-2 border rounded-md p-2", children: [_jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "font-medium text-sm", children: [f.label, " ", _jsxs("span", { className: "text-muted-foreground", children: ["(", f.key, ")"] })] }), _jsxs("div", { className: "text-xs text-muted-foreground", children: [FIELD_TYPES.find((t) => t.value === f.field_type)?.label, f.required && " · verplicht", !f.visible && " · verborgen", f.ai_enabled ? " · AI aan" : " · AI uit"] })] }), _jsx(Button, { variant: "ghost", size: "icon", onClick: () => move(idx, -1), children: _jsx(ArrowUp, { className: "h-4 w-4" }) }), _jsx(Button, { variant: "ghost", size: "icon", onClick: () => move(idx, 1), children: _jsx(ArrowDown, { className: "h-4 w-4" }) }), _jsx(Button, { variant: "outline", size: "sm", onClick: () => setEditing(f), children: "Bewerken" }), _jsx(Button, { variant: "ghost", size: "icon", onClick: () => handleDelete(f.id), children: _jsx(Trash2, { className: "h-4 w-4" }) })] }, f.id))) }), _jsxs(Button, { variant: "outline", size: "sm", onClick: addNew, children: [_jsx(Plus, { className: "h-3 w-3 mr-1" }), " Veld toevoegen"] }), editing && (_jsx(FieldEditor, { field: editing, onCancel: () => setEditing(null), onSave: handleSave }))] })) })] }));
|
|
79
|
+
}
|
|
80
|
+
function FieldEditor({ field, onCancel, onSave, }) {
|
|
81
|
+
const [f, setF] = useState(field);
|
|
82
|
+
const typeMeta = FIELD_TYPES.find((t) => t.value === f.field_type);
|
|
83
|
+
return (_jsxs("div", { className: "border rounded-md p-3 space-y-3 bg-muted/30", children: [_jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Label" }), _jsx(Input, { value: f.label, onChange: (e) => setF({ ...f, label: e.target.value }) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Sleutel (key)" }), _jsx(Input, { value: f.key, onChange: (e) => setF({ ...f, key: e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, "_") }), placeholder: "bv. delivery_date" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Type" }), _jsxs(Select, { value: f.field_type, onValueChange: (v) => {
|
|
84
|
+
const meta = FIELD_TYPES.find((t) => t.value === v);
|
|
85
|
+
setF({
|
|
86
|
+
...f,
|
|
87
|
+
field_type: v,
|
|
88
|
+
source: meta?.defaultSource ?? "static",
|
|
89
|
+
source_table: meta?.defaultTable ?? null,
|
|
90
|
+
});
|
|
91
|
+
}, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: FIELD_TYPES.map((t) => _jsx(SelectItem, { value: t.value, children: t.label }, t.value)) })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Standaardwaarde" }), _jsx(Input, { value: f.default_value ?? "", onChange: (e) => setF({ ...f, default_value: e.target.value || null }) })] })] }), _jsxs("div", { className: "flex items-center gap-6", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { checked: f.required, onCheckedChange: (v) => setF({ ...f, required: v }) }), _jsx(Label, { children: "Verplicht" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { checked: f.visible, onCheckedChange: (v) => setF({ ...f, visible: v }) }), _jsx(Label, { children: "Zichtbaar" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { checked: f.ai_enabled, onCheckedChange: (v) => setF({ ...f, ai_enabled: v }) }), _jsx(Label, { children: "AI vult in" })] })] }), (f.field_type === "select" || f.field_type === "multiselect") && (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Opties (\u00E9\u00E9n per regel, formaat: waarde|label)" }), _jsx(Textarea, { rows: 4, value: (f.options ?? []).map((o) => `${o.value}|${o.label}`).join("\n"), onChange: (e) => {
|
|
92
|
+
const opts = e.target.value.split("\n").map((l) => l.trim()).filter(Boolean).map((line) => {
|
|
93
|
+
const [value, label] = line.split("|");
|
|
94
|
+
return { value: (value ?? "").trim(), label: (label ?? value ?? "").trim() };
|
|
95
|
+
});
|
|
96
|
+
setF({ ...f, options: opts });
|
|
97
|
+
} })] })), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "AI-instructie (optioneel)" }), _jsx(Textarea, { rows: 2, value: f.ai_hint ?? "", onChange: (e) => setF({ ...f, ai_hint: e.target.value || null }), placeholder: "Hint voor de AI om dit veld correct in te vullen." })] }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Bron: ", _jsx("strong", { children: f.source === "database" ? `Database (${f.source_table ?? typeMeta?.defaultTable ?? "n.v.t."})` : "Vrij invoer" }), f.source === "database" && " — alleen bestaande waarden uit de database mogen worden geselecteerd."] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { onClick: () => onSave(f), disabled: !f.key || !f.label, children: "Opslaan" }), _jsx(Button, { variant: "outline", onClick: onCancel, children: "Annuleren" })] })] }));
|
|
98
|
+
}
|
|
@@ -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";
|
|
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
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FlowModule } from "@flowselections/core";
|
|
2
|
+
export * from "./_core-safelist";
|
|
3
|
+
export { MailboxInboxPage } from "./components/MailboxInboxPage";
|
|
4
|
+
export { MailboxProposalsPage } from "./components/MailboxProposalsPage";
|
|
5
|
+
export { SearchableSelect } from "./components/SearchableSelect";
|
|
6
|
+
export { MailboxProcessedPage } from "./components/MailboxProcessedPage";
|
|
7
|
+
export { MailboxSettingsCard } from "./components/settings/MailboxSettingsCard";
|
|
8
|
+
export { MailboxOrderTemplateCard } from "./components/settings/MailboxOrderTemplateCard";
|
|
9
|
+
export { ImapAccountsCard } from "./components/settings/ImapAccountsCard";
|
|
10
|
+
export { ImapProfilesCard } from "./components/settings/ImapProfilesCard";
|
|
11
|
+
export declare const mailboxOrdersModule: FlowModule;
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAKvD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AAChF,OAAO,EAAE,wBAAwB,EAAE,MAAM,gDAAgD,CAAC;AAC1F,OAAO,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AAE1E,eAAO,MAAM,mBAAmB,EAAE,UAoBjC,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// src/index.ts — MODULE CONTRACT (Mailbox bestellingen)
|
|
3
|
+
// ============================================================================
|
|
4
|
+
import { Mail, Inbox, ClipboardCheck, CheckCircle2 } from "lucide-react";
|
|
5
|
+
import { ImapAccountsCard } from "./components/settings/ImapAccountsCard";
|
|
6
|
+
import { MailboxOrderTemplateCard } from "./components/settings/MailboxOrderTemplateCard";
|
|
7
|
+
export * from "./_core-safelist";
|
|
8
|
+
export { MailboxInboxPage } from "./components/MailboxInboxPage";
|
|
9
|
+
export { MailboxProposalsPage } from "./components/MailboxProposalsPage";
|
|
10
|
+
export { SearchableSelect } from "./components/SearchableSelect";
|
|
11
|
+
export { MailboxProcessedPage } from "./components/MailboxProcessedPage";
|
|
12
|
+
export { MailboxSettingsCard } from "./components/settings/MailboxSettingsCard";
|
|
13
|
+
export { MailboxOrderTemplateCard } from "./components/settings/MailboxOrderTemplateCard";
|
|
14
|
+
export { ImapAccountsCard } from "./components/settings/ImapAccountsCard";
|
|
15
|
+
export { ImapProfilesCard } from "./components/settings/ImapProfilesCard";
|
|
16
|
+
export const mailboxOrdersModule = {
|
|
17
|
+
id: "mailbox_orders",
|
|
18
|
+
name: "Mailbox bestellingen",
|
|
19
|
+
version: "1.1.0",
|
|
20
|
+
nav: {
|
|
21
|
+
label: "Mailbox",
|
|
22
|
+
href: "/mailbox-orders",
|
|
23
|
+
icon: Mail,
|
|
24
|
+
children: [
|
|
25
|
+
{ label: "Inbox", href: "/mailbox-orders", icon: Inbox },
|
|
26
|
+
{ label: "Voorstellen", href: "/mailbox-orders/voorstellen", icon: ClipboardCheck },
|
|
27
|
+
{ label: "Verwerkt", href: "/mailbox-orders/verwerkt", icon: CheckCircle2 },
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
settingsCards: [
|
|
31
|
+
{ component: ImapAccountsCard, order: 5 },
|
|
32
|
+
{ component: MailboxOrderTemplateCard, order: 20 },
|
|
33
|
+
],
|
|
34
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-attacher.d.ts","sourceRoot":"","sources":["../../../src/integrations/supabase/auth-attacher.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,kBAAkB,mHAa9B,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// This file is automatically generated. Do not edit it directly.
|
|
2
|
+
import { createMiddleware } from '@tanstack/react-start';
|
|
3
|
+
// Must be registered as a global `functionMiddleware` in `src/start.ts`; otherwise
|
|
4
|
+
// the browser never attaches the bearer token to serverFn RPCs.
|
|
5
|
+
export const attachSupabaseAuth = createMiddleware({ type: 'function' }).client(async ({ next }) => {
|
|
6
|
+
if (typeof window === 'undefined') {
|
|
7
|
+
return next();
|
|
8
|
+
}
|
|
9
|
+
const { supabase } = await import('./client');
|
|
10
|
+
const { data } = await supabase.auth.getSession();
|
|
11
|
+
const token = data.session?.access_token;
|
|
12
|
+
return next({
|
|
13
|
+
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
|
14
|
+
});
|
|
15
|
+
});
|