@flowselections/mailbox-orders 1.0.29 → 1.0.30
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/integrations/supabase/client.server.js +1 -1
- package/dist-lib/lib/mailbox.functions.d.ts +1 -1
- package/dist-lib/lib/mailbox.functions.js +5 -5
- package/package.json +1 -1
- package/dist-lib/components/CartAssignmentWizard.d.ts +0 -6
- package/dist-lib/components/CartAssignmentWizard.d.ts.map +0 -1
- package/dist-lib/components/CartAssignmentWizard.js +0 -199
- package/dist-lib/components/MailboxInboxPage.d.ts +0 -2
- package/dist-lib/components/MailboxInboxPage.d.ts.map +0 -1
- package/dist-lib/components/MailboxInboxPage.js +0 -120
- package/dist-lib/components/settings/ImapAccountsCard.d.ts +0 -2
- package/dist-lib/components/settings/ImapAccountsCard.d.ts.map +0 -1
- package/dist-lib/components/settings/ImapAccountsCard.js +0 -136
- package/dist-lib/components/settings/ImapProfilesCard.d.ts +0 -2
- package/dist-lib/components/settings/ImapProfilesCard.d.ts.map +0 -1
- package/dist-lib/components/settings/ImapProfilesCard.js +0 -101
- package/dist-lib/components/settings/MailboxSettingsCard.d.ts +0 -2
- package/dist-lib/components/settings/MailboxSettingsCard.d.ts.map +0 -1
- package/dist-lib/components/settings/MailboxSettingsCard.js +0 -85
- package/dist-lib/lib/imap.functions.d.ts +0 -32852
- package/dist-lib/lib/imap.functions.d.ts.map +0 -1
- package/dist-lib/lib/imap.functions.js +0 -235
- package/dist-lib/lib/mailbox-auto.server.d.ts +0 -9
- package/dist-lib/lib/mailbox-auto.server.d.ts.map +0 -1
- package/dist-lib/lib/mailbox-auto.server.js +0 -198
|
@@ -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 "./client.server.js";
|
|
24
24
|
export const supabaseAdmin = new Proxy({}, {
|
|
25
25
|
get(_, prop, receiver) {
|
|
26
26
|
if (!_supabaseAdmin)
|
|
@@ -34,7 +34,7 @@ export declare const getActiveOrdersMailbox: import("@tanstack/start-client-core
|
|
|
34
34
|
}>>;
|
|
35
35
|
export declare const syncMailboxNow: import("@tanstack/start-client-core").OptionalFetcher<undefined, (input: unknown) => {
|
|
36
36
|
trigger: "realtime" | "manual";
|
|
37
|
-
}, Promise<import("
|
|
37
|
+
}, Promise<import("./mailbox-ingest.server.js").IngestRunResult | {
|
|
38
38
|
ok: false;
|
|
39
39
|
error: string;
|
|
40
40
|
processed: 0;
|
|
@@ -3,7 +3,7 @@ import { createServerFn } from "@tanstack/react-start";
|
|
|
3
3
|
import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware.js";
|
|
4
4
|
import { createClient } from "@supabase/supabase-js";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
-
import { computeEarliestDelivery } from "
|
|
6
|
+
import { computeEarliestDelivery } from "./delivery-date.js";
|
|
7
7
|
import { emailBodyText, inlineCidImages, normalizeAddressList, normalizeAttachments, sanitizeEmailHtml, stripInvisible, } from "./mailbox-body.js";
|
|
8
8
|
import { EMPTY_LOAD_CARRIERS, isLoadCarrierAllowed, loadCarrierLabel, normalizeAllowedLoadCarriers, } from "./load-carriers.js";
|
|
9
9
|
// ─── Actieve mailbox (uit gedeelde e-mail-auth module) ──────────────────────
|
|
@@ -66,7 +66,7 @@ export const syncMailboxNow = createServerFn({ method: "POST" })
|
|
|
66
66
|
if (userErr || !userData?.user) {
|
|
67
67
|
return { ok: false, error: "unauthorized", processed: 0, inserted: 0, errors: 1 };
|
|
68
68
|
}
|
|
69
|
-
const { runIngest } = await import("
|
|
69
|
+
const { runIngest } = await import("./mailbox-ingest.server.js");
|
|
70
70
|
return await runIngest(data.trigger);
|
|
71
71
|
}
|
|
72
72
|
catch (e) {
|
|
@@ -97,7 +97,7 @@ export const reparseProposalNow = createServerFn({ method: "POST" })
|
|
|
97
97
|
.inputValidator((input) => z.object({ proposalId: z.string().uuid() }).parse(input))
|
|
98
98
|
.handler(async ({ data }) => {
|
|
99
99
|
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
100
|
-
const { reparseProposal } = await import("
|
|
100
|
+
const { reparseProposal } = await import("./mailbox-ingest.server.js");
|
|
101
101
|
return reparseProposal(supabaseAdmin, data.proposalId);
|
|
102
102
|
});
|
|
103
103
|
/** Detecteert een gewijzigde AI-agent en herverwerkt openstaande voorstellen. */
|
|
@@ -105,7 +105,7 @@ export const refreshProposalsForAgentChange = createServerFn({ method: "POST" })
|
|
|
105
105
|
.middleware([requireSupabaseAuth])
|
|
106
106
|
.handler(async () => {
|
|
107
107
|
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
108
|
-
const { reparseChangedAgentProposals } = await import("
|
|
108
|
+
const { reparseChangedAgentProposals } = await import("./mailbox-ingest.server.js");
|
|
109
109
|
return reparseChangedAgentProposals(supabaseAdmin);
|
|
110
110
|
});
|
|
111
111
|
// ─── Messages & Proposals ────────────────────────────────────────────────────
|
|
@@ -127,7 +127,7 @@ export const parseMessage = createServerFn({ method: "POST" })
|
|
|
127
127
|
.inputValidator((input) => z.object({ messageId: z.string().uuid() }).parse(input))
|
|
128
128
|
.handler(async ({ data }) => {
|
|
129
129
|
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
130
|
-
const { createStubProposal } = await import("
|
|
130
|
+
const { createStubProposal } = await import("./mailbox-ingest.server.js");
|
|
131
131
|
const { data: msg, error: msgErr } = await supabaseAdmin
|
|
132
132
|
.from("mailbox_messages").select("*").eq("id", data.messageId).single();
|
|
133
133
|
if (msgErr || !msg)
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CartAssignmentWizard.d.ts","sourceRoot":"","sources":["../../src/components/CartAssignmentWizard.tsx"],"names":[],"mappings":"AA2CA,wBAAgB,oBAAoB,CAAC,EACnC,OAAO,EAAE,IAAI,EAAE,OAAO,GACvB,EAAE;IAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,+BAsUhE"}
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useMemo, useState } from "react";
|
|
3
|
-
import { useQuery } from "@tanstack/react-query";
|
|
4
|
-
import { useServerFn } from "@tanstack/react-start";
|
|
5
|
-
import { Button, Badge, Card, CardContent, CardHeader, CardTitle, Input, Label, Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, toast, } from "@flowselections/core";
|
|
6
|
-
import { Plus, Trash2, AlertTriangle, CheckCircle2 } from "lucide-react";
|
|
7
|
-
import { planAndSaveAutoCarts, saveManualCarts } from "../lib/mailbox.functions.js";
|
|
8
|
-
export function CartAssignmentWizard({ orderId, open, onClose, }) {
|
|
9
|
-
const fetchPlan = useServerFn(planAndSaveAutoCarts);
|
|
10
|
-
const fetchSave = useServerFn(saveManualCarts);
|
|
11
|
-
const { data, isLoading, refetch } = useQuery({
|
|
12
|
-
queryKey: ["cart-plan", orderId],
|
|
13
|
-
enabled: !!orderId && open,
|
|
14
|
-
queryFn: async () => {
|
|
15
|
-
try {
|
|
16
|
-
return await fetchPlan({ data: { orderId: orderId } });
|
|
17
|
-
}
|
|
18
|
-
catch (e) {
|
|
19
|
-
toast.error(e?.message ?? "Indelen mislukt");
|
|
20
|
-
return { autoCarts: [], remainders: [], existingManualCarts: [], nextCartNumber: 1 };
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
});
|
|
24
|
-
const autoCarts = Array.isArray(data?.autoCarts) ? data.autoCarts : [];
|
|
25
|
-
const remainders = Array.isArray(data?.remainders) ? data.remainders : [];
|
|
26
|
-
const existingManualCarts = Array.isArray(data?.existingManualCarts) ? data.existingManualCarts : [];
|
|
27
|
-
// Pool met nog in te delen fusten per orderregel
|
|
28
|
-
const [pool, setPool] = useState({});
|
|
29
|
-
const [manual, setManual] = useState([]);
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
const p = {};
|
|
32
|
-
remainders.forEach((r) => { p[r.order_item_id] = r.crates_remaining; });
|
|
33
|
-
setPool(p);
|
|
34
|
-
// Bestaande handmatige karren herstellen — capaciteitsmeta uit remainders aanvullen.
|
|
35
|
-
const remMeta = {};
|
|
36
|
-
remainders.forEach((r) => { remMeta[r.order_item_id] = r; });
|
|
37
|
-
setManual(existingManualCarts.map((c) => ({
|
|
38
|
-
id: crypto.randomUUID(),
|
|
39
|
-
items: c.items.map((i) => {
|
|
40
|
-
const meta = i.order_item_id ? remMeta[i.order_item_id] : null;
|
|
41
|
-
return {
|
|
42
|
-
...i,
|
|
43
|
-
crates_per_layer: meta?.crates_per_layer ?? i.crates_per_layer ?? 1,
|
|
44
|
-
layers_per_cart: meta?.layers_per_cart ?? i.layers_per_cart ?? 1,
|
|
45
|
-
};
|
|
46
|
-
}),
|
|
47
|
-
})));
|
|
48
|
-
}, [data]);
|
|
49
|
-
const remByItem = useMemo(() => {
|
|
50
|
-
const m = {};
|
|
51
|
-
remainders.forEach((r) => { m[r.order_item_id] = r; });
|
|
52
|
-
return m;
|
|
53
|
-
}, [remainders]);
|
|
54
|
-
const addCart = () => setManual((m) => [...m, { id: crypto.randomUUID(), items: [] }]);
|
|
55
|
-
const addItemToCart = (cartId, orderItemId, crates) => {
|
|
56
|
-
const rem = remByItem[orderItemId];
|
|
57
|
-
if (!rem)
|
|
58
|
-
return;
|
|
59
|
-
const available = pool[orderItemId] ?? 0;
|
|
60
|
-
const take = Math.max(0, Math.min(crates, available));
|
|
61
|
-
if (take === 0)
|
|
62
|
-
return;
|
|
63
|
-
setPool((p) => ({ ...p, [orderItemId]: available - take }));
|
|
64
|
-
setManual((carts) => carts.map((c) => {
|
|
65
|
-
if (c.id !== cartId)
|
|
66
|
-
return c;
|
|
67
|
-
// merge in plaats van duplicate regel
|
|
68
|
-
const existingIdx = c.items.findIndex((i) => i.order_item_id === orderItemId);
|
|
69
|
-
if (existingIdx >= 0) {
|
|
70
|
-
const next = [...c.items];
|
|
71
|
-
next[existingIdx] = { ...next[existingIdx], crates: next[existingIdx].crates + take };
|
|
72
|
-
return { ...c, items: next };
|
|
73
|
-
}
|
|
74
|
-
return {
|
|
75
|
-
...c,
|
|
76
|
-
items: [...c.items, {
|
|
77
|
-
order_item_id: orderItemId,
|
|
78
|
-
product_id: rem.product_id,
|
|
79
|
-
product_name: rem.product_name,
|
|
80
|
-
crates: take,
|
|
81
|
-
plants_per_crate: rem.plants_per_crate,
|
|
82
|
-
crates_per_layer: rem.crates_per_layer,
|
|
83
|
-
layers_per_cart: rem.layers_per_cart,
|
|
84
|
-
}],
|
|
85
|
-
};
|
|
86
|
-
}));
|
|
87
|
-
};
|
|
88
|
-
const removeItem = (cartId, orderItemId) => {
|
|
89
|
-
setManual((carts) => carts.map((c) => {
|
|
90
|
-
if (c.id !== cartId)
|
|
91
|
-
return c;
|
|
92
|
-
const item = c.items.find((i) => i.order_item_id === orderItemId);
|
|
93
|
-
if (!item)
|
|
94
|
-
return c;
|
|
95
|
-
setPool((p) => ({ ...p, [orderItemId]: (p[orderItemId] ?? 0) + item.crates }));
|
|
96
|
-
return { ...c, items: c.items.filter((i) => i.order_item_id !== orderItemId) };
|
|
97
|
-
}));
|
|
98
|
-
};
|
|
99
|
-
const removeCart = (cartId) => {
|
|
100
|
-
setManual((carts) => {
|
|
101
|
-
const c = carts.find((x) => x.id === cartId);
|
|
102
|
-
if (c) {
|
|
103
|
-
setPool((p) => {
|
|
104
|
-
const np = { ...p };
|
|
105
|
-
c.items.forEach((i) => {
|
|
106
|
-
if (!i.order_item_id)
|
|
107
|
-
return;
|
|
108
|
-
np[i.order_item_id] = (np[i.order_item_id] ?? 0) + i.crates;
|
|
109
|
-
});
|
|
110
|
-
return np;
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
return carts.filter((x) => x.id !== cartId);
|
|
114
|
-
});
|
|
115
|
-
};
|
|
116
|
-
// Capaciteit per kar = max van betrokken artikelen (gemengde karren toegestaan,
|
|
117
|
-
// pakken we de hoogste cratesPerCart als bovengrens; waarschuwing toont per item).
|
|
118
|
-
const cartCapacity = (cart) => cart.items.length === 0
|
|
119
|
-
? 0
|
|
120
|
-
: Math.max(...cart.items.map((i) => i.crates_per_layer * i.layers_per_cart));
|
|
121
|
-
const cartUsed = (cart) => cart.items.reduce((s, i) => s + i.crates, 0);
|
|
122
|
-
const totalRemainingInPool = Object.values(pool).reduce((s, n) => s + n, 0);
|
|
123
|
-
const handleSave = async () => {
|
|
124
|
-
if (totalRemainingInPool > 0) {
|
|
125
|
-
toast.error(`Nog ${totalRemainingInPool} fust niet ingedeeld — verdeel alles over een kar voordat je opslaat`);
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
if (manual.some((c) => c.items.length === 0)) {
|
|
129
|
-
toast.error("Lege karren niet toegestaan");
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
const overfull = manual.find((c) => cartUsed(c) > cartCapacity(c));
|
|
133
|
-
if (overfull) {
|
|
134
|
-
toast.error("Een kar overschrijdt de capaciteit");
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
try {
|
|
138
|
-
await fetchSave({
|
|
139
|
-
data: {
|
|
140
|
-
orderId: orderId,
|
|
141
|
-
carts: manual.map((c) => ({
|
|
142
|
-
is_complete: cartUsed(c) >= cartCapacity(c),
|
|
143
|
-
items: c.items.map((i) => ({
|
|
144
|
-
order_item_id: i.order_item_id,
|
|
145
|
-
product_id: i.product_id,
|
|
146
|
-
product_name: i.product_name,
|
|
147
|
-
crates: Math.max(1, Math.trunc(i.crates)),
|
|
148
|
-
plants_per_crate: Math.max(1, Math.trunc(i.plants_per_crate || 1)),
|
|
149
|
-
crates_per_layer: Math.max(1, Math.trunc(i.crates_per_layer || 1)),
|
|
150
|
-
})),
|
|
151
|
-
})),
|
|
152
|
-
},
|
|
153
|
-
});
|
|
154
|
-
toast.success("Karindeling opgeslagen");
|
|
155
|
-
onClose();
|
|
156
|
-
}
|
|
157
|
-
catch (e) {
|
|
158
|
-
// requireSupabaseAuth en input-validatie gooien een Response; toString
|
|
159
|
-
// daarvan is "[object Response]" — zet om in leesbare melding zodat
|
|
160
|
-
// de React error-overlay niet wordt getriggerd.
|
|
161
|
-
let msg = "Opslaan mislukt";
|
|
162
|
-
if (e instanceof Response) {
|
|
163
|
-
try {
|
|
164
|
-
msg = (await e.text()) || `HTTP ${e.status}`;
|
|
165
|
-
}
|
|
166
|
-
catch {
|
|
167
|
-
msg = `HTTP ${e.status}`;
|
|
168
|
-
}
|
|
169
|
-
if (e.status === 401)
|
|
170
|
-
msg = "Sessie verlopen — log opnieuw in";
|
|
171
|
-
}
|
|
172
|
-
else if (e?.message) {
|
|
173
|
-
msg = e.message;
|
|
174
|
-
}
|
|
175
|
-
toast.error(msg);
|
|
176
|
-
console.error("saveManualCarts failed:", e);
|
|
177
|
-
}
|
|
178
|
-
};
|
|
179
|
-
return (_jsx(Dialog, { open: open, onOpenChange: (o) => { if (!o)
|
|
180
|
-
onClose(); }, children: _jsxs(DialogContent, { className: "max-w-[95vw] w-[95vw] h-[92vh] max-h-[92vh] overflow-y-auto p-6", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "Karindeling" }) }), isLoading ? (_jsx("div", { className: "text-muted-foreground", children: "Karren worden berekend..." })) : (_jsxs("div", { className: "space-y-6", children: [_jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsx(CardTitle, { className: "text-base", children: "Automatisch ingedeelde karren" }), _jsxs(Badge, { variant: "secondary", children: [autoCarts.length, " kar(ren)"] })] }), _jsx(CardContent, { children: autoCarts.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Geen volledige karren mogelijk." })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Kar #" }), _jsx(TableHead, { children: "Artikel" }), _jsx(TableHead, { className: "text-right", children: "Fusten" }), _jsx(TableHead, { className: "text-right", children: "Planten" }), _jsx(TableHead, { className: "text-right", children: "Bezetting" }), _jsx(TableHead, { children: "Status" })] }) }), _jsx(TableBody, { children: autoCarts.map((c) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-mono", children: c.cart_number }), _jsx(TableCell, { children: c.product_name }), _jsx(TableCell, { className: "text-right", children: c.crates }), _jsx(TableCell, { className: "text-right", children: c.plants }), _jsx(TableCell, { className: "text-right", children: "100%" }), _jsx(TableCell, { children: _jsxs(Badge, { className: "gap-1", children: [_jsx(CheckCircle2, { className: "h-3 w-3" }), " Volledig"] }) })] }, c.cart_number))) })] })) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Handmatig in te delen fusten" }) }), _jsx(CardContent, { className: "space-y-3", children: remainders.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Geen restant \u2014 alles past volledig op karren." })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Artikel" }), _jsx(TableHead, { className: "text-right", children: "Beschikbaar (fusten)" }), _jsx(TableHead, { className: "text-right", children: "Planten/fust" }), _jsx(TableHead, { className: "text-right", children: "Capaciteit/kar" })] }) }), _jsx(TableBody, { children: remainders.map((r) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: r.product_name }), _jsxs(TableCell, { className: "text-right font-mono", children: [pool[r.order_item_id] ?? 0, " / ", r.crates_remaining] }), _jsx(TableCell, { className: "text-right", children: r.plants_per_crate }), _jsx(TableCell, { className: "text-right", children: r.crates_per_layer * r.layers_per_cart })] }, r.order_item_id))) })] })) })] }), remainders.length > 0 && (_jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsx(CardTitle, { className: "text-base", children: "Restant-karren samenstellen" }), _jsxs(Button, { size: "sm", onClick: addCart, children: [_jsx(Plus, { className: "h-3 w-3 mr-1" }), " Kar toevoegen"] })] }), _jsxs(CardContent, { className: "space-y-3", children: [manual.length === 0 && (_jsx("p", { className: "text-sm text-muted-foreground", children: "Voeg een kar toe om restant-fusten in te delen." })), manual.map((cart, idx) => {
|
|
181
|
-
const used = cartUsed(cart);
|
|
182
|
-
const cap = cartCapacity(cart);
|
|
183
|
-
const pct = cap > 0 ? Math.round((used / cap) * 100) : 0;
|
|
184
|
-
const over = cap > 0 && used > cap;
|
|
185
|
-
return (_jsxs(Card, { className: over ? "border-destructive" : "", children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between py-3", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("span", { className: "font-medium", children: ["Restant-kar ", idx + 1] }), _jsxs(Badge, { variant: over ? "destructive" : "secondary", children: [used, " / ", cap || "?", " fusten (", pct, "%)"] }), over && (_jsxs("span", { className: "text-xs text-destructive flex items-center gap-1", children: [_jsx(AlertTriangle, { className: "h-3 w-3" }), " Capaciteit overschreden"] }))] }), _jsx(Button, { variant: "ghost", size: "icon", onClick: () => removeCart(cart.id), children: _jsx(Trash2, { className: "h-4 w-4" }) })] }), _jsxs(CardContent, { className: "space-y-2", children: [cart.items.map((i) => (_jsxs("div", { className: "flex items-center justify-between text-sm", children: [_jsxs("div", { children: [_jsx("span", { className: "font-medium", children: i.product_name }), _jsxs("span", { className: "text-muted-foreground", children: [" \u00B7 ", i.crates, " fust (", i.crates * i.plants_per_crate, " planten)"] })] }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => removeItem(cart.id, i.order_item_id), children: _jsx(Trash2, { className: "h-3 w-3" }) })] }, i.order_item_id))), _jsx(AddItemRow, { remainders: remainders, pool: pool, onAdd: (itemId, crates) => addItemToCart(cart.id, itemId, crates) })] })] }, cart.id));
|
|
186
|
-
})] })] }))] })), _jsxs(DialogFooter, { className: "gap-2", children: [_jsx(Button, { variant: "outline", onClick: onClose, children: "Annuleren" }), _jsxs(Button, { onClick: handleSave, disabled: isLoading || totalRemainingInPool > 0, children: ["Karindeling opslaan", totalRemainingInPool > 0 && (_jsxs(Badge, { variant: "destructive", className: "ml-2", children: ["Nog ", totalRemainingInPool, " fust in te delen"] }))] })] })] }) }));
|
|
187
|
-
}
|
|
188
|
-
function AddItemRow({ remainders, pool, onAdd, }) {
|
|
189
|
-
const available = remainders.filter((r) => (pool[r.order_item_id] ?? 0) > 0);
|
|
190
|
-
const [sel, setSel] = useState("");
|
|
191
|
-
const [qty, setQty] = useState(1);
|
|
192
|
-
if (available.length === 0)
|
|
193
|
-
return null;
|
|
194
|
-
return (_jsxs("div", { className: "flex items-end gap-2 pt-2 border-t", children: [_jsxs("div", { className: "flex-1", children: [_jsx(Label, { className: "text-xs", children: "Artikel" }), _jsxs(Select, { value: sel, onValueChange: setSel, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Kies restant-artikel..." }) }), _jsx(SelectContent, { children: available.map((r) => (_jsxs(SelectItem, { value: r.order_item_id, children: [r.product_name, " (", pool[r.order_item_id], " fust beschikbaar)"] }, r.order_item_id))) })] })] }), _jsxs("div", { className: "w-24", children: [_jsx(Label, { className: "text-xs", children: "Fusten" }), _jsx(Input, { type: "number", min: 1, value: qty, onChange: (e) => setQty(Math.max(1, Number(e.target.value) || 1)) })] }), _jsxs(Button, { size: "sm", disabled: !sel, onClick: () => { if (sel) {
|
|
195
|
-
onAdd(sel, qty);
|
|
196
|
-
setSel("");
|
|
197
|
-
setQty(1);
|
|
198
|
-
} }, children: [_jsx(Plus, { className: "h-3 w-3 mr-1" }), " Toevoegen"] })] }));
|
|
199
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"MailboxInboxPage.d.ts","sourceRoot":"","sources":["../../src/components/MailboxInboxPage.tsx"],"names":[],"mappings":"AA6BA,wBAAgB,gBAAgB,gCAuO/B"}
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef } from "react";
|
|
3
|
-
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
4
|
-
import { useServerFn } from "@tanstack/react-start";
|
|
5
|
-
import { Button, Badge, Card, CardContent, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, toast, } from "@flowselections/core";
|
|
6
|
-
import { Mail, Sparkles, AlertCircle, ExternalLink, RefreshCw } from "lucide-react";
|
|
7
|
-
import { listMessages, parseMessage, getActiveOrdersMailbox, ingestEmailEvent, backfillRecentEvents, } from "../lib/mailbox.functions.js";
|
|
8
|
-
import { supabase } from "../integrations/supabase/client.js";
|
|
9
|
-
const AUTH_MODULE_URL = "https://auth.flowselections.com/e-mail-authenticatie";
|
|
10
|
-
const STATUS_VARIANT = {
|
|
11
|
-
new: "secondary",
|
|
12
|
-
parsing: "outline",
|
|
13
|
-
parsed: "default",
|
|
14
|
-
failed: "destructive",
|
|
15
|
-
ignored: "outline",
|
|
16
|
-
converted: "default",
|
|
17
|
-
};
|
|
18
|
-
export function MailboxInboxPage() {
|
|
19
|
-
const qc = useQueryClient();
|
|
20
|
-
const fetchList = useServerFn(listMessages);
|
|
21
|
-
const fetchParse = useServerFn(parseMessage);
|
|
22
|
-
const fetchActive = useServerFn(getActiveOrdersMailbox);
|
|
23
|
-
const fetchIngest = useServerFn(ingestEmailEvent);
|
|
24
|
-
const fetchBackfill = useServerFn(backfillRecentEvents);
|
|
25
|
-
const { data, isLoading } = useQuery({
|
|
26
|
-
queryKey: ["mailbox-messages"],
|
|
27
|
-
queryFn: () => fetchList(),
|
|
28
|
-
});
|
|
29
|
-
const { data: active, isSuccess: activeLoaded } = useQuery({
|
|
30
|
-
queryKey: ["mailbox-active-account"],
|
|
31
|
-
queryFn: () => fetchActive(),
|
|
32
|
-
refetchInterval: 60000,
|
|
33
|
-
retry: 1,
|
|
34
|
-
});
|
|
35
|
-
// Realtime: nieuwe events uit gedeelde authenticatiemodule
|
|
36
|
-
const activeId = active?.connected ? active.id : null;
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
if (!activeId)
|
|
39
|
-
return;
|
|
40
|
-
const channel = supabase
|
|
41
|
-
.channel(`email_auth_events:${activeId}`)
|
|
42
|
-
.on("postgres_changes", {
|
|
43
|
-
event: "INSERT",
|
|
44
|
-
schema: "public",
|
|
45
|
-
table: "email_auth_events",
|
|
46
|
-
filter: `connection_id=eq.${activeId}`,
|
|
47
|
-
}, async (payload) => {
|
|
48
|
-
const evId = payload?.new?.id;
|
|
49
|
-
if (!evId)
|
|
50
|
-
return;
|
|
51
|
-
try {
|
|
52
|
-
const res = await fetchIngest({ data: { eventId: evId } });
|
|
53
|
-
if (res?.ok && (res.inserted ?? 0) > 0) {
|
|
54
|
-
const app = res.autoApproved ?? 0;
|
|
55
|
-
if (app > 0)
|
|
56
|
-
toast.success(`Nieuwe e-mail — ${app} automatisch omgezet`);
|
|
57
|
-
else
|
|
58
|
-
toast.success("Nieuwe e-mail ontvangen");
|
|
59
|
-
qc.invalidateQueries({ queryKey: ["mailbox-messages"] });
|
|
60
|
-
qc.invalidateQueries({ queryKey: ["mailbox-proposals"] });
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
/* stille fout — backfill vangt het later op */
|
|
65
|
-
}
|
|
66
|
-
})
|
|
67
|
-
.subscribe();
|
|
68
|
-
return () => {
|
|
69
|
-
void supabase.removeChannel(channel);
|
|
70
|
-
};
|
|
71
|
-
}, [activeId, fetchIngest, qc]);
|
|
72
|
-
// Backfill bij eerste mount + iedere 60s (vangt gemiste events op)
|
|
73
|
-
const didInitialBackfill = useRef(false);
|
|
74
|
-
const backfillMut = useMutation({
|
|
75
|
-
mutationFn: async () => {
|
|
76
|
-
try {
|
|
77
|
-
return await fetchBackfill();
|
|
78
|
-
}
|
|
79
|
-
catch {
|
|
80
|
-
// 401's (token verlopen) en tijdelijke fouten stil opvangen
|
|
81
|
-
return { ok: false, inserted: 0 };
|
|
82
|
-
}
|
|
83
|
-
},
|
|
84
|
-
onSuccess: (res) => {
|
|
85
|
-
if (res?.ok && (res.inserted ?? 0) > 0) {
|
|
86
|
-
qc.invalidateQueries({ queryKey: ["mailbox-messages"] });
|
|
87
|
-
qc.invalidateQueries({ queryKey: ["mailbox-proposals"] });
|
|
88
|
-
}
|
|
89
|
-
},
|
|
90
|
-
onError: () => {
|
|
91
|
-
/* stille fout — volgende interval probeert opnieuw */
|
|
92
|
-
},
|
|
93
|
-
});
|
|
94
|
-
useEffect(() => {
|
|
95
|
-
if (!activeId || didInitialBackfill.current)
|
|
96
|
-
return;
|
|
97
|
-
didInitialBackfill.current = true;
|
|
98
|
-
backfillMut.mutate();
|
|
99
|
-
const t = setInterval(() => backfillMut.mutate(), 60000);
|
|
100
|
-
return () => clearInterval(t);
|
|
101
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
102
|
-
}, [activeId]);
|
|
103
|
-
const parseMut = useMutation({
|
|
104
|
-
mutationFn: (messageId) => fetchParse({ data: { messageId } }),
|
|
105
|
-
onSuccess: (res) => {
|
|
106
|
-
if (res?.ok) {
|
|
107
|
-
toast.success("E-mail geparsed naar voorstel");
|
|
108
|
-
qc.invalidateQueries({ queryKey: ["mailbox-messages"] });
|
|
109
|
-
qc.invalidateQueries({ queryKey: ["mailbox-proposals"] });
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
toast.error(res?.error ?? "Parsen mislukt");
|
|
113
|
-
}
|
|
114
|
-
},
|
|
115
|
-
onError: (e) => toast.error(e?.message ?? "Parsen mislukt"),
|
|
116
|
-
});
|
|
117
|
-
const items = Array.isArray(data?.items) ? data.items : [];
|
|
118
|
-
const needsReauth = active?.connected && active.needs_reauth;
|
|
119
|
-
return (_jsxs("div", { className: "p-6 space-y-6", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsxs("h1", { className: "text-2xl font-semibold flex items-center gap-2", children: [_jsx(Mail, { className: "h-6 w-6 text-primary" }), " Inbox"] }), _jsx("p", { className: "text-muted-foreground text-sm mt-1", children: "Binnenkomende bestel-e-mails \u2014 geleverd door de E-mail Authenticatie module" })] }), _jsxs(Button, { variant: "outline", onClick: () => backfillMut.mutate(), disabled: backfillMut.isPending || !active?.connected, title: "Haal achterstallige events op", children: [_jsx(RefreshCw, { className: `h-4 w-4 mr-2 ${backfillMut.isPending ? "animate-spin" : ""}` }), "Synchroniseer"] })] }), activeLoaded && !active?.connected ? (_jsx(Card, { children: _jsxs(CardContent, { className: "p-4 flex items-start gap-3 text-sm", children: [_jsx(AlertCircle, { className: "h-4 w-4 text-destructive mt-0.5" }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "font-medium", children: "Nog geen e-mailadres gekoppeld" }), _jsxs("div", { className: "text-muted-foreground", children: ["Koppel in de module ", _jsx("strong", { children: "E-mail Authenticatie" }), " een adres met doeleinde", " ", _jsx("code", { children: "orders" }), " en richting ", _jsx("code", { children: "receive" }), "."] })] }), _jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsxs("a", { href: AUTH_MODULE_URL, target: "_blank", rel: "noreferrer", children: ["Open module ", _jsx(ExternalLink, { className: "h-3 w-3 ml-1" })] }) })] }) })) : needsReauth ? (_jsx(Card, { className: "border-destructive", children: _jsxs(CardContent, { className: "p-4 flex items-start gap-3 text-sm", children: [_jsx(AlertCircle, { className: "h-4 w-4 text-destructive mt-0.5" }), _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "font-medium", children: ["Heraanmelding vereist voor ", active.account_email] }), _jsx("div", { className: "text-muted-foreground", children: active.last_error ?? "De koppeling is verlopen. Log opnieuw in via de authenticatiemodule." })] }), _jsx(Button, { asChild: true, variant: "destructive", size: "sm", children: _jsxs("a", { href: `${AUTH_MODULE_URL}?focus=${active.id}`, target: "_blank", rel: "noreferrer", children: ["Opnieuw verbinden ", _jsx(ExternalLink, { className: "h-3 w-3 ml-1" })] }) })] }) })) : active?.connected ? (_jsx(Card, { children: _jsxs(CardContent, { className: "p-4 flex items-center gap-3 text-sm", children: [_jsx(Mail, { className: "h-4 w-4 text-primary" }), _jsx("span", { className: "text-muted-foreground", children: "Actieve mailbox:" }), _jsx("span", { className: "font-medium", children: active.account_email }), _jsx(Badge, { variant: "outline", className: "ml-2", children: active.provider }), active.status && active.status !== "ok" && active.status !== "verified" && (_jsx(Badge, { variant: "secondary", className: "ml-2", children: active.status }))] }) })) : null, _jsx(Card, { children: _jsx(CardContent, { className: "p-0", children: isLoading ? (_jsx("div", { className: "p-8 text-center text-muted-foreground", children: "Laden..." })) : items.length === 0 ? (_jsx("div", { className: "p-8 text-center text-muted-foreground", children: "Nog geen e-mails ontvangen. Nieuwe berichten verschijnen automatisch." })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Status" }), _jsx(TableHead, { children: "Afzender" }), _jsx(TableHead, { children: "Onderwerp" }), _jsx(TableHead, { children: "Ontvangen" }), _jsx(TableHead, { className: "text-right", children: "Actie" })] }) }), _jsx(TableBody, { children: items.map((m) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsx(Badge, { variant: STATUS_VARIANT[m.status] ?? "secondary", children: m.status }) }), _jsxs(TableCell, { children: [_jsx("div", { className: "text-sm font-medium", children: m.from_name || m.from_email }), m.from_name && _jsx("div", { className: "text-xs text-muted-foreground", children: m.from_email })] }), _jsx(TableCell, { className: "max-w-md truncate", children: m.subject || "(geen onderwerp)" }), _jsx(TableCell, { className: "text-sm text-muted-foreground", children: m.received_at ? new Date(m.received_at).toLocaleString("nl-NL") : "-" }), _jsx(TableCell, { className: "text-right", children: m.status !== "converted" && (_jsxs(Button, { size: "sm", variant: "outline", disabled: parseMut.isPending, onClick: () => parseMut.mutate(m.id), children: [_jsx(Sparkles, { className: "h-3 w-3 mr-1" }), m.status === "parsed" ? "Opnieuw parsen" : "Parse"] })) })] }, m.id))) })] })) }) })] }));
|
|
120
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ImapAccountsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/ImapAccountsCard.tsx"],"names":[],"mappings":"AAyCA,wBAAgB,gBAAgB,gCAwP/B"}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useState } from "react";
|
|
3
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle, Button, Input, Label, Switch, Badge, Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, toast, } from "@flowselections/core";
|
|
4
|
-
import { Server, Plus, Pencil, Trash2, TestTube2, RefreshCw, CheckCircle2, PlayCircle } from "lucide-react";
|
|
5
|
-
import { listImapAccounts, upsertImapAccount, deleteImapAccount, testImapAccount, listImapProfiles, activateImapAccount, pollImapProfile, } from "../../lib/imap.functions.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
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ImapProfilesCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/ImapProfilesCard.tsx"],"names":[],"mappings":"AAgCA,wBAAgB,gBAAgB,gCAgN/B"}
|