@flowselections/twinfield-authenticatie-module-15-06-2026 1.3.5 → 1.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/components/settings/TwinfieldAuthSettingsCard.d.ts.map +1 -1
- package/dist-lib/components/settings/TwinfieldAuthSettingsCard.js +34 -5
- package/dist-lib/integrations/supabase/auth-attacher.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/auth-attacher.js +9 -1
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +2015 -87
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +2015 -87
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +2015 -87
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/twinfield-admin.server.d.ts +2015 -87
- package/dist-lib/integrations/supabase/twinfield-admin.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +2033 -73
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/accounting/twinfield/actions.functions.d.ts +9715 -75
- package/dist-lib/lib/accounting/twinfield/actions.functions.d.ts.map +1 -1
- package/dist-lib/lib/accounting/twinfield/conformance.functions.d.ts +2015 -87
- package/dist-lib/lib/accounting/twinfield/conformance.functions.d.ts.map +1 -1
- package/dist-lib/lib/accounting/twinfield/cron-auth.server.d.ts +4 -0
- package/dist-lib/lib/accounting/twinfield/cron-auth.server.d.ts.map +1 -0
- package/dist-lib/lib/accounting/twinfield/cron-auth.server.js +56 -0
- package/dist-lib/lib/accounting/twinfield/writes.functions.d.ts +5829 -45
- package/dist-lib/lib/accounting/twinfield/writes.functions.d.ts.map +1 -1
- package/dist-lib/lib/central/central-accounts.server.d.ts.map +1 -1
- package/dist-lib/lib/central/central-accounts.server.js +2 -1
- package/dist-lib/lib/central/central-token.server.d.ts +12 -0
- package/dist-lib/lib/central/central-token.server.d.ts.map +1 -1
- package/dist-lib/lib/central/central-token.server.js +72 -14
- package/dist-lib/lib/twinfield-adapter.functions.d.ts +5233 -1377
- package/dist-lib/lib/twinfield-adapter.functions.d.ts.map +1 -1
- package/dist-lib/lib/twinfield-sync.functions.d.ts +9715 -75
- package/dist-lib/lib/twinfield-sync.functions.d.ts.map +1 -1
- package/dist-lib/lib/twinfield-sync.server.d.ts +24 -0
- package/dist-lib/lib/twinfield-sync.server.d.ts.map +1 -1
- package/dist-lib/lib/twinfield-sync.server.js +639 -154
- package/dist-lib/lib/twinfield-token.server.d.ts +20 -1
- package/dist-lib/lib/twinfield-token.server.d.ts.map +1 -1
- package/dist-lib/lib/twinfield-token.server.js +104 -29
- package/dist-lib/lib/twinfield.functions.d.ts +29151 -225
- package/dist-lib/lib/twinfield.functions.d.ts.map +1 -1
- package/dist-lib/lib/twinfield.functions.js +88 -18
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TwinfieldAuthSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/TwinfieldAuthSettingsCard.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TwinfieldAuthSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/TwinfieldAuthSettingsCard.tsx"],"names":[],"mappings":"AA0WA,wBAAgB,yBAAyB,CAAC,EACxC,eAAsB,GACvB,GAAE;IACD,eAAe,CAAC,EAAE,OAAO,CAAC;CACtB,+BA6UL"}
|
|
@@ -50,7 +50,13 @@ function ConnectionCard({ conn, onChanged, onDisconnected, }) {
|
|
|
50
50
|
const saveOffice = useServerFn(setTwinfieldConnectionOffice);
|
|
51
51
|
const disconnect = useServerFn(disconnectTwinfieldConnection);
|
|
52
52
|
const trigger = useServerFn(triggerTwinfieldSync);
|
|
53
|
+
const startOAuthForCard = useServerFn(startTwinfieldOAuth);
|
|
53
54
|
const [offices, setOffices] = useState([]);
|
|
55
|
+
const [officesStatus, setOfficesStatus] = useState("empty");
|
|
56
|
+
const [officesMessage, setOfficesMessage] = useState(null);
|
|
57
|
+
const [needsReauth, setNeedsReauth] = useState(false);
|
|
58
|
+
const [reauthorizing, setReauthorizing] = useState(false);
|
|
59
|
+
const [officesReload, setOfficesReload] = useState(0);
|
|
54
60
|
const [loadingOffices, setLoadingOffices] = useState(false);
|
|
55
61
|
const [savingOffice, setSavingOffice] = useState(false);
|
|
56
62
|
const [optimisticOffice, setOptimisticOffice] = useState(null);
|
|
@@ -65,10 +71,19 @@ function ConnectionCard({ conn, onChanged, onDisconnected, }) {
|
|
|
65
71
|
if (!cancelled) {
|
|
66
72
|
const list = Array.isArray(res?.offices) ? res.offices : [];
|
|
67
73
|
setOffices(list);
|
|
74
|
+
setOfficesStatus(res?.status ??
|
|
75
|
+
(list.length > 0 ? "ok" : "empty"));
|
|
76
|
+
setOfficesMessage(res?.message ?? null);
|
|
77
|
+
setNeedsReauth(Boolean(res?.needsReauth));
|
|
68
78
|
}
|
|
69
79
|
}
|
|
70
80
|
catch {
|
|
71
|
-
|
|
81
|
+
if (!cancelled) {
|
|
82
|
+
setOffices([]);
|
|
83
|
+
setOfficesStatus("error");
|
|
84
|
+
setOfficesMessage("Administraties konden niet opgehaald worden.");
|
|
85
|
+
setNeedsReauth(false);
|
|
86
|
+
}
|
|
72
87
|
}
|
|
73
88
|
finally {
|
|
74
89
|
if (!cancelled)
|
|
@@ -78,7 +93,20 @@ function ConnectionCard({ conn, onChanged, onDisconnected, }) {
|
|
|
78
93
|
return () => {
|
|
79
94
|
cancelled = true;
|
|
80
95
|
};
|
|
81
|
-
}, [conn.id, loadOffices]);
|
|
96
|
+
}, [conn.id, loadOffices, officesReload]);
|
|
97
|
+
const handleReauthorize = async () => {
|
|
98
|
+
setReauthorizing(true);
|
|
99
|
+
try {
|
|
100
|
+
const res = await startOAuthForCard({ data: undefined });
|
|
101
|
+
if (!res?.url)
|
|
102
|
+
throw new Error("Kon geen koppelingsverzoek starten.");
|
|
103
|
+
window.location.href = res.url;
|
|
104
|
+
}
|
|
105
|
+
catch (e) {
|
|
106
|
+
toast.error(e?.message ?? "Autoriseren niet gelukt.");
|
|
107
|
+
setReauthorizing(false);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
82
110
|
// Wanneer server-waarde overeenkomt met optimistische keuze: opruimen.
|
|
83
111
|
useEffect(() => {
|
|
84
112
|
if (optimisticOffice && conn.office_code === optimisticOffice.code) {
|
|
@@ -147,12 +175,13 @@ function ConnectionCard({ conn, onChanged, onDisconnected, }) {
|
|
|
147
175
|
const effectiveOfficeCode = conn.office_code ?? optimisticOffice?.code ?? null;
|
|
148
176
|
const effectiveOfficeName = conn.company_name ?? optimisticOffice?.name ?? null;
|
|
149
177
|
const displayName = effectiveOfficeName ?? effectiveOfficeCode ?? conn.company_code ?? "Nog geen administratie gekozen";
|
|
150
|
-
return (_jsxs("div", { className: "rounded-lg border p-4 space-y-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("span", { className: "font-semibold text-foreground truncate", children: displayName }), _jsx(Badge, { variant: conn.connected ? "default" : "secondary", className: "font-normal", children: conn.connected ? "Verbonden" : "Onvolledig" }), _jsx(Badge, { variant: "outline", className: "font-normal", children: conn.environment === "sandbox" ? "Test" : "Live" })] }), _jsxs("div", { className: "mt-1 text-xs text-muted-foreground", children: [effectiveOfficeCode ? (_jsx("span", { className: "font-mono", children: effectiveOfficeCode })) : (_jsx("span", { children: "Kies hieronder een administratie om te kunnen synchroniseren." })), conn.connected_at && (_jsxs("span", { className: "ml-2", children: ["\u00B7 gekoppeld ", relativeTime(conn.connected_at)] }))] })] }), _jsxs(Button, { size: "sm", variant: "ghost", onClick: handleDisconnect, disabled: disconnecting, className: "text-muted-foreground hover:text-destructive", children: [_jsx(Unlink, { className: "h-4 w-4 mr-1.5" }), disconnecting ? "Bezig…" : "Verbreken"] })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx("div", { className: "text-xs font-medium text-muted-foreground", children: "Administratie" }), offices.length > 0 ? (_jsxs(Select, { value: effectiveOfficeCode ?? undefined, onValueChange: handleOfficeChange, disabled: savingOffice, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Kies een administratie", children: (() => {
|
|
178
|
+
return (_jsxs("div", { className: "rounded-lg border p-4 space-y-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("span", { className: "font-semibold text-foreground truncate", children: displayName }), _jsx(Badge, { variant: needsReauth ? "destructive" : conn.connected ? "default" : "secondary", className: "font-normal", children: needsReauth ? "Autorisatie vereist" : conn.connected ? "Verbonden" : "Onvolledig" }), _jsx(Badge, { variant: "outline", className: "font-normal", children: conn.environment === "sandbox" ? "Test" : "Live" })] }), _jsxs("div", { className: "mt-1 text-xs text-muted-foreground", children: [effectiveOfficeCode ? (_jsx("span", { className: "font-mono", children: effectiveOfficeCode })) : (_jsx("span", { children: "Kies hieronder een administratie om te kunnen synchroniseren." })), conn.connected_at && (_jsxs("span", { className: "ml-2", children: ["\u00B7 gekoppeld ", relativeTime(conn.connected_at)] }))] })] }), _jsxs(Button, { size: "sm", variant: "ghost", onClick: handleDisconnect, disabled: disconnecting, className: "text-muted-foreground hover:text-destructive", children: [_jsx(Unlink, { className: "h-4 w-4 mr-1.5" }), disconnecting ? "Bezig…" : "Verbreken"] })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx("div", { className: "text-xs font-medium text-muted-foreground", children: "Administratie" }), offices.length > 0 ? (_jsxs(Select, { value: effectiveOfficeCode ?? undefined, onValueChange: handleOfficeChange, disabled: savingOffice, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Kies een administratie", children: (() => {
|
|
151
179
|
const selected = offices.find((o) => o.code === effectiveOfficeCode);
|
|
152
180
|
if (!selected)
|
|
153
181
|
return null;
|
|
154
182
|
return (_jsxs("span", { className: "flex items-baseline gap-2 truncate", children: [_jsx("span", { className: "truncate", children: selected.name ?? selected.code }), _jsx("span", { className: "text-[11px] text-muted-foreground font-mono", children: selected.code })] }));
|
|
155
|
-
})() }) }), _jsx(SelectContent, { children: offices.map((o) => (_jsx(SelectItem, { value: o.code, children: _jsxs("div", { className: "flex flex-col", children: [_jsx("span", { children: o.name ?? o.code }), _jsx("span", { className: "text-[11px] text-muted-foreground font-mono", children: o.code })] }) }, o.code))) })] })) : (_jsx("div", { className: "text-xs text-muted-foreground", children:
|
|
183
|
+
})() }) }), _jsx(SelectContent, { children: offices.map((o) => (_jsx(SelectItem, { value: o.code, children: _jsxs("div", { className: "flex flex-col", children: [_jsx("span", { children: o.name ?? o.code }), _jsx("span", { className: "text-[11px] text-muted-foreground font-mono", children: o.code })] }) }, o.code))) })] })) : loadingOffices ? (_jsx("div", { className: "text-xs text-muted-foreground", children: "Administraties ophalen\u2026" })) : officesStatus === "error" ? (_jsxs("div", { className: "space-y-1.5", children: [_jsx("div", { className: "text-xs text-destructive", children: officesMessage ?? "Administraties konden niet opgehaald worden." }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx(Button, { size: "sm", variant: "outline", onClick: () => setOfficesReload((n) => n + 1), children: "Opnieuw proberen" }), needsReauth && (_jsxs(Button, { size: "sm", onClick: handleReauthorize, disabled: reauthorizing, children: [_jsx(Link2, { className: "h-4 w-4 mr-1.5" }), reauthorizing ? "Bezig…" : "Opnieuw autoriseren"] }))] })] })) : (_jsx("div", { className: "text-xs text-muted-foreground", children: officesMessage ??
|
|
184
|
+
"Dit Twinfield-account heeft geen administraties. Koppel een account met toegang tot een administratie." }))] }), effectiveOfficeCode && (_jsxs("div", { className: "flex flex-wrap gap-2 pt-1", children: [_jsx(Button, { size: "sm", variant: "outline", onClick: () => runSync("customers"), disabled: busy === "customers", children: busy === "customers" ? "Debiteuren…" : "Debiteuren bijwerken" }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => runSync("invoices"), disabled: busy === "invoices", children: busy === "invoices" ? "Facturen…" : "Facturen bijwerken" })] }))] }));
|
|
156
185
|
}
|
|
157
186
|
export function TwinfieldAuthSettingsCard({ defaultExpanded = true, } = {}) {
|
|
158
187
|
const startOAuth = useServerFn(startTwinfieldOAuth);
|
|
@@ -193,7 +222,7 @@ export function TwinfieldAuthSettingsCard({ defaultExpanded = true, } = {}) {
|
|
|
193
222
|
}
|
|
194
223
|
}
|
|
195
224
|
const [connRes, stateRes, queueRes] = await Promise.all([
|
|
196
|
-
listConnections(),
|
|
225
|
+
listConnections().catch(() => null),
|
|
197
226
|
getSyncState().catch(() => null),
|
|
198
227
|
fetchPushQueue().catch(() => null),
|
|
199
228
|
]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-attacher.d.ts","sourceRoot":"","sources":["../../../src/integrations/supabase/auth-attacher.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,kBAAkB,
|
|
1
|
+
{"version":3,"file":"auth-attacher.d.ts","sourceRoot":"","sources":["../../../src/integrations/supabase/auth-attacher.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,kBAAkB,mHAgB9B,CAAA"}
|
|
@@ -5,7 +5,15 @@
|
|
|
5
5
|
import { createMiddleware } from '@tanstack/react-start';
|
|
6
6
|
export const attachSupabaseAuth = createMiddleware({ type: 'function' }).client(async ({ next }) => {
|
|
7
7
|
const { supabase } = await import('./client');
|
|
8
|
-
|
|
8
|
+
let { data } = await supabase.auth.getSession();
|
|
9
|
+
// Proactief verversen: een token dat binnen 60s verloopt geeft server-side
|
|
10
|
+
// een 401 Response, wat als "Error: [object Response]" op het scherm belandt.
|
|
11
|
+
const expiresAt = data.session?.expires_at ?? 0;
|
|
12
|
+
if (data.session && expiresAt * 1000 - Date.now() < 60000) {
|
|
13
|
+
const refreshed = await supabase.auth.refreshSession().catch(() => null);
|
|
14
|
+
if (refreshed?.data?.session)
|
|
15
|
+
data = refreshed.data;
|
|
16
|
+
}
|
|
9
17
|
const token = data.session?.access_token;
|
|
10
18
|
return next({
|
|
11
19
|
headers: token ? { Authorization: `Bearer ${token}` } : {},
|