@flowselections/google-maps-api-installatie-techniek 1.0.2
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/MapsBeheerPage.d.ts +2 -0
- package/dist-lib/components/MapsBeheerPage.d.ts.map +1 -0
- package/dist-lib/components/MapsBeheerPage.js +68 -0
- package/dist-lib/components/MapsInstallatieLogo.d.ts +6 -0
- package/dist-lib/components/MapsInstallatieLogo.d.ts.map +1 -0
- package/dist-lib/components/MapsInstallatieLogo.js +4 -0
- package/dist-lib/components/maps/MapsDiagnosticsPanel.d.ts +2 -0
- package/dist-lib/components/maps/MapsDiagnosticsPanel.d.ts.map +1 -0
- package/dist-lib/components/maps/MapsDiagnosticsPanel.js +81 -0
- package/dist-lib/components/maps/MapsStatusPanel.d.ts +5 -0
- package/dist-lib/components/maps/MapsStatusPanel.d.ts.map +1 -0
- package/dist-lib/components/maps/MapsStatusPanel.js +74 -0
- package/dist-lib/components/settings/MapsSettingsCard.d.ts +2 -0
- package/dist-lib/components/settings/MapsSettingsCard.d.ts.map +1 -0
- package/dist-lib/components/settings/MapsSettingsCard.js +15 -0
- package/dist-lib/index.d.ts +7 -0
- package/dist-lib/index.d.ts.map +1 -0
- package/dist-lib/index.js +20 -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 +12 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +23365 -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 +23361 -0
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/client.js +29 -0
- package/dist-lib/integrations/supabase/client.server.d.ts +23361 -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/previewAuthStorage.d.ts +6 -0
- package/dist-lib/integrations/supabase/previewAuthStorage.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/previewAuthStorage.js +92 -0
- package/dist-lib/integrations/supabase/types.d.ts +23990 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/types.js +92 -0
- package/dist-lib/lib/central-auth.server.d.ts +60 -0
- package/dist-lib/lib/central-auth.server.d.ts.map +1 -0
- package/dist-lib/lib/central-auth.server.js +439 -0
- package/dist-lib/lib/google-maps.server.d.ts +22 -0
- package/dist-lib/lib/google-maps.server.d.ts.map +1 -0
- package/dist-lib/lib/google-maps.server.js +65 -0
- package/dist-lib/lib/maps-admin.server.d.ts +17 -0
- package/dist-lib/lib/maps-admin.server.d.ts.map +1 -0
- package/dist-lib/lib/maps-admin.server.js +121 -0
- package/dist-lib/lib/maps-diagnostics.server.d.ts +4 -0
- package/dist-lib/lib/maps-diagnostics.server.d.ts.map +1 -0
- package/dist-lib/lib/maps-diagnostics.server.js +137 -0
- package/dist-lib/lib/maps-processor.server.d.ts +16 -0
- package/dist-lib/lib/maps-processor.server.d.ts.map +1 -0
- package/dist-lib/lib/maps-processor.server.js +315 -0
- package/dist-lib/lib/maps.functions.d.ts +186950 -0
- package/dist-lib/lib/maps.functions.d.ts.map +1 -0
- package/dist-lib/lib/maps.functions.js +63 -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 +87 -0
- package/public/logo.png +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_core-safelist.d.ts","sourceRoot":"","sources":["../src/_core-safelist.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Forceert Tailwind classes die uit @flowselections/core komen maar niet
|
|
2
|
+
// voorkomen in de eigen broncode van deze module.
|
|
3
|
+
// Zonder dit bestand worden deze classes weggeoptimaliseerd op de
|
|
4
|
+
// Lovable productie build.
|
|
5
|
+
const _safelist = [
|
|
6
|
+
// Layout — Sidebar en shell structuur
|
|
7
|
+
'h-full', 'flex-col', 'flex-1', 'min-h-screen', 'ml-64', 'pt-16',
|
|
8
|
+
'items-start', 'items-center', 'justify-between', 'border-t',
|
|
9
|
+
'w-64', 'h-screen', 'h-20', 'overflow-y-auto',
|
|
10
|
+
// Grid — InstellingenPage
|
|
11
|
+
'grid', 'lg:grid-cols-2', 'gap-6', 'p-6', 'space-y-6',
|
|
12
|
+
// Toggle/Switch — aan/uit zichtbaarheid
|
|
13
|
+
'data-[state=checked]:bg-primary', 'data-[state=unchecked]:bg-input',
|
|
14
|
+
];
|
|
15
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MapsBeheerPage.d.ts","sourceRoot":"","sources":["../../src/components/MapsBeheerPage.tsx"],"names":[],"mappings":"AAyCA,wBAAgB,cAAc,gCAgN7B"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
4
|
+
import { useServerFn } from "@tanstack/react-start";
|
|
5
|
+
import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Input, Label, Switch, Tabs, TabsContent, TabsList, TabsTrigger, toast, } from "@flowselections/core";
|
|
6
|
+
import { MapPin, RefreshCw, Trash2, Timer } from "lucide-react";
|
|
7
|
+
import { clearMapsCache, getMapsOverview, getMapsStatus, processMapsQueue, purgeExpiredTravelTimes, updateMapsSettings, } from "../lib/maps.functions";
|
|
8
|
+
import { MapsStatusPanel } from "./maps/MapsStatusPanel";
|
|
9
|
+
import { MapsDiagnosticsPanel } from "./maps/MapsDiagnosticsPanel";
|
|
10
|
+
function Stat({ label, value }) {
|
|
11
|
+
return (_jsxs("div", { className: "rounded-lg border border-border bg-card p-4", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: label }), _jsx("p", { className: "mt-1 text-2xl font-semibold text-foreground", children: value })] }));
|
|
12
|
+
}
|
|
13
|
+
export function MapsBeheerPage() {
|
|
14
|
+
const queryClient = useQueryClient();
|
|
15
|
+
const fetchOverview = useServerFn(getMapsOverview);
|
|
16
|
+
const fetchStatus = useServerFn(getMapsStatus);
|
|
17
|
+
const runQueue = useServerFn(processMapsQueue);
|
|
18
|
+
const saveSettings = useServerFn(updateMapsSettings);
|
|
19
|
+
const clearCache = useServerFn(clearMapsCache);
|
|
20
|
+
const purgeExpired = useServerFn(purgeExpiredTravelTimes);
|
|
21
|
+
const { data, isLoading } = useQuery({
|
|
22
|
+
queryKey: ["maps-overview"],
|
|
23
|
+
queryFn: () => fetchOverview(),
|
|
24
|
+
});
|
|
25
|
+
const { data: status } = useQuery({
|
|
26
|
+
queryKey: ["maps-status"],
|
|
27
|
+
queryFn: () => fetchStatus(),
|
|
28
|
+
});
|
|
29
|
+
const [ttl, setTtl] = useState("");
|
|
30
|
+
const [limit, setLimit] = useState("");
|
|
31
|
+
const [fallback, setFallback] = useState(null);
|
|
32
|
+
const [provider, setProvider] = useState(null);
|
|
33
|
+
const settings = data?.settings ?? null;
|
|
34
|
+
const ttlValue = ttl !== "" ? ttl : String(settings?.cacheTtlDays ?? 30);
|
|
35
|
+
const limitValue = limit !== "" ? limit : String(settings?.dailyCallLimit ?? 2000);
|
|
36
|
+
const fallbackValue = fallback ?? settings?.allowEstimateFallback ?? true;
|
|
37
|
+
const providerValue = provider ?? settings?.provider ?? "free";
|
|
38
|
+
const invalidate = () => {
|
|
39
|
+
queryClient.invalidateQueries({ queryKey: ["maps-overview"] });
|
|
40
|
+
queryClient.invalidateQueries({ queryKey: ["maps-status"] });
|
|
41
|
+
};
|
|
42
|
+
const mutation = useMutation({
|
|
43
|
+
mutationFn: async (action) => {
|
|
44
|
+
if (action === "queue")
|
|
45
|
+
return runQueue();
|
|
46
|
+
if (action === "clear")
|
|
47
|
+
return clearCache();
|
|
48
|
+
if (action === "purge")
|
|
49
|
+
return purgeExpired();
|
|
50
|
+
return saveSettings({
|
|
51
|
+
data: {
|
|
52
|
+
cacheTtlDays: Number(ttlValue),
|
|
53
|
+
dailyCallLimit: Number(limitValue),
|
|
54
|
+
allowEstimateFallback: fallbackValue,
|
|
55
|
+
provider: providerValue,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
onSuccess: () => {
|
|
60
|
+
toast.success("Actie uitgevoerd");
|
|
61
|
+
invalidate();
|
|
62
|
+
},
|
|
63
|
+
onError: (error) => {
|
|
64
|
+
toast.error(error instanceof Error ? error.message : "Er ging iets mis");
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
return (_jsx("main", { className: "flex-1 p-6", children: _jsxs(Tabs, { defaultValue: "status", className: "w-full", children: [_jsxs(TabsList, { children: [_jsx(TabsTrigger, { value: "status", children: "Status & diagnose" }), _jsx(TabsTrigger, { value: "beheer", children: "Beheer" })] }), _jsxs(TabsContent, { value: "status", className: "space-y-4", children: [_jsx(MapsStatusPanel, { status: status ?? null }), _jsx(MapsDiagnosticsPanel, {})] }), _jsxs(TabsContent, { value: "beheer", className: "space-y-6", children: [_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(MapPin, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Google-koppeling" })] }), data ? (_jsx(Badge, { variant: data.connected ? "default" : "destructive", children: data.connected ? "Verbonden" : "Niet beschikbaar" })) : null] }), _jsx(CardDescription, { children: "Route- en adresgegevens worden opgehaald via de centrale auth-module." })] }), _jsxs(CardContent, { className: "space-y-2 text-sm text-muted-foreground", children: [isLoading ? _jsx("p", { children: "Laden..." }) : null, data && !data.connected ? _jsx("p", { children: data.connectionReason }) : null, settings?.lastSuccessAt ? (_jsxs("p", { children: ["Laatste geslaagde aanroep: ", new Date(settings.lastSuccessAt).toLocaleString("nl-NL")] })) : null, settings?.lastError ? (_jsxs("p", { className: "text-destructive", children: ["Laatste fout: ", settings.lastError] })) : null] })] }), _jsxs("div", { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-4", children: [_jsx(Stat, { label: "Aanroepen vandaag", value: data?.elementsToday ?? 0 }), _jsx(Stat, { label: "Aanroepen deze maand", value: data?.elementsMonth ?? 0 }), data?.estimatedCostMonth != null ? (_jsx(Stat, { label: "Geschatte kosten (USD)", value: data.estimatedCostMonth })) : null, _jsx(Stat, { label: "Mislukte aanroepen", value: data?.errorsMonth ?? 0 }), _jsx(Stat, { label: "Terugvallen deze maand", value: data?.fallbacksMonth ?? 0 }), _jsx(Stat, { label: "Adressen in cache", value: data?.addressCount ?? 0 }), _jsx(Stat, { label: "Reistijden in cache", value: data?.travelTimeCount ?? 0 }), _jsx(Stat, { label: "Verlopen reistijden", value: data?.expiredCount ?? 0 }), _jsx(Stat, { label: "Wachtrij (open / mislukt)", value: `${data?.queuePending ?? 0} / ${data?.queueFailed ?? 0}` })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { children: "Instellingen" }), _jsx(CardDescription, { children: "Aanbieder, houdbaarheid, daglimiet en terugval op schatting." })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "ttl", children: "Houdbaarheid cache (dagen)" }), _jsx(Input, { id: "ttl", type: "number", min: 1, value: ttlValue, onChange: (event) => setTtl(event.target.value) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "limit", children: "Daglimiet (elementen)" }), _jsx(Input, { id: "limit", type: "number", min: 0, value: limitValue, onChange: (event) => setLimit(event.target.value) })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "provider", children: "Aanbieder" }), _jsxs("select", { id: "provider", className: "h-10 w-full rounded-md border border-input bg-background px-3 text-sm sm:w-64", value: providerValue, onChange: (event) => setProvider(event.target.value === "google" ? "google" : "free"), children: [_jsx("option", { value: "free", children: "Gratis (geen kosten)" }), _jsx("option", { value: "google", children: "Google (met billing)" })] }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Kostenschatting wordt alleen getoond bij Google." })] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Switch, { id: "fallback", checked: fallbackValue, onCheckedChange: (checked) => setFallback(checked) }), _jsx(Label, { htmlFor: "fallback", children: "Terugvallen op postcodeschatting toestaan" })] }), _jsx(Button, { onClick: () => mutation.mutate("save"), disabled: mutation.isPending, children: "Instellingen opslaan" })] })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { children: "Acties" }), _jsx(CardDescription, { children: "Onderhoud van de wachtrij en de cache." })] }), _jsxs(CardContent, { className: "flex flex-wrap gap-3", children: [_jsxs(Button, { onClick: () => mutation.mutate("queue"), disabled: mutation.isPending, children: [_jsx(RefreshCw, { className: "mr-2 h-4 w-4" }), " Wachtrij nu verwerken"] }), _jsxs(Button, { variant: "outline", onClick: () => mutation.mutate("purge"), disabled: mutation.isPending, children: [_jsx(Timer, { className: "mr-2 h-4 w-4" }), " Verlopen rijen opruimen"] }), _jsxs(Button, { variant: "destructive", onClick: () => mutation.mutate("clear"), disabled: mutation.isPending, children: [_jsx(Trash2, { className: "mr-2 h-4 w-4" }), " Cache legen"] })] })] })] })] }) }));
|
|
68
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MapsInstallatieLogo.d.ts","sourceRoot":"","sources":["../../src/components/MapsInstallatieLogo.tsx"],"names":[],"mappings":"AAAA,UAAU,wBAAwB;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,SAAS,EAAE,EAAE,wBAAwB,+BAgB1E"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export function MapsInstallatieLogo({ className }) {
|
|
3
|
+
return (_jsxs("svg", { viewBox: "0 0 40 40", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: className, "aria-hidden": "true", children: [_jsx("rect", { width: "40", height: "40", rx: "8", fill: "currentColor", opacity: "0.1" }), _jsx("path", { d: "M20 9c-4 0-7.2 3.2-7.2 7.2 0 5.4 7.2 14.3 7.2 14.3s7.2-8.9 7.2-14.3C27.2 12.2 24 9 20 9Zm0 9.8a2.6 2.6 0 1 1 0-5.2 2.6 2.6 0 0 1 0 5.2Z", fill: "currentColor" })] }));
|
|
4
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MapsDiagnosticsPanel.d.ts","sourceRoot":"","sources":["../../../src/components/maps/MapsDiagnosticsPanel.tsx"],"names":[],"mappings":"AA6DA,wBAAgB,oBAAoB,gCAmJnC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
// Diagnosekaart voor de statustab van de Kaarten-module.
|
|
3
|
+
// Toont stap-voor-stap diagnose met kopieerknop.
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { useServerFn } from "@tanstack/react-start";
|
|
6
|
+
import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@flowselections/core";
|
|
7
|
+
import { AlertTriangle, CheckCircle2, ClipboardCopy, Loader2, XCircle, } from "lucide-react";
|
|
8
|
+
import { getMapsDiagnostics } from "../../lib/maps.functions";
|
|
9
|
+
function formatDateTime(value) {
|
|
10
|
+
const parsed = Date.parse(value);
|
|
11
|
+
if (Number.isNaN(parsed))
|
|
12
|
+
return value;
|
|
13
|
+
return new Date(parsed).toLocaleString("nl-NL");
|
|
14
|
+
}
|
|
15
|
+
function reportToText(report) {
|
|
16
|
+
const lines = [];
|
|
17
|
+
lines.push(`# Kaarten diagnose`);
|
|
18
|
+
lines.push(`Gemeten op: ${formatDateTime(report.measuredAt)}`);
|
|
19
|
+
lines.push(`Central host: ${report.centralHost ?? "—"}`);
|
|
20
|
+
lines.push(`Secret aanwezig: ${report.secretPresent ? "ja" : "nee"}`);
|
|
21
|
+
lines.push("");
|
|
22
|
+
lines.push("## Stappen");
|
|
23
|
+
for (const step of report.steps) {
|
|
24
|
+
lines.push(`### ${step.name}`);
|
|
25
|
+
lines.push(`- ok: ${step.ok ? "ja" : "nee"}`);
|
|
26
|
+
lines.push(`- target: ${step.target}`);
|
|
27
|
+
if (step.httpStatus)
|
|
28
|
+
lines.push(`- status: ${step.httpStatus}`);
|
|
29
|
+
lines.push(`- summary: ${step.summary}`);
|
|
30
|
+
if (step.code)
|
|
31
|
+
lines.push(`- code: ${step.code}`);
|
|
32
|
+
if (step.hint)
|
|
33
|
+
lines.push(`- hint: ${step.hint}`);
|
|
34
|
+
if (step.body)
|
|
35
|
+
lines.push(`- body: ${step.body}`);
|
|
36
|
+
lines.push("");
|
|
37
|
+
}
|
|
38
|
+
if (report.hints.length > 0) {
|
|
39
|
+
lines.push("## Hints");
|
|
40
|
+
for (const h of report.hints)
|
|
41
|
+
lines.push(`- ${h}`);
|
|
42
|
+
lines.push("");
|
|
43
|
+
}
|
|
44
|
+
if (report.checklist.length > 0) {
|
|
45
|
+
lines.push("## Checklist");
|
|
46
|
+
for (const c of report.checklist)
|
|
47
|
+
lines.push(`- [ ] ${c}`);
|
|
48
|
+
}
|
|
49
|
+
return lines.join("\n");
|
|
50
|
+
}
|
|
51
|
+
export function MapsDiagnosticsPanel() {
|
|
52
|
+
const fetchDiagnostics = useServerFn(getMapsDiagnostics);
|
|
53
|
+
const [report, setReport] = useState(null);
|
|
54
|
+
const [loading, setLoading] = useState(false);
|
|
55
|
+
const [copied, setCopied] = useState(false);
|
|
56
|
+
const runDiagnostics = async () => {
|
|
57
|
+
setLoading(true);
|
|
58
|
+
try {
|
|
59
|
+
const result = await fetchDiagnostics();
|
|
60
|
+
setReport(result);
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
setLoading(false);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const handleCopy = async () => {
|
|
67
|
+
if (!report)
|
|
68
|
+
return;
|
|
69
|
+
try {
|
|
70
|
+
await navigator.clipboard.writeText(reportToText(report));
|
|
71
|
+
setCopied(true);
|
|
72
|
+
setTimeout(() => setCopied(false), 2000);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// ignore
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(AlertTriangle, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Diagnose" })] }), _jsx(CardDescription, { children: "Stap-voor-stap controle van Central Auth, Google-API's en Supabase." })] }), _jsxs(CardContent, { className: "space-y-4", children: [!report && (_jsxs("div", { className: "flex flex-col items-center gap-3 py-8 text-center", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Er is nog geen diagnose uitgevoerd." }), _jsx(Button, { onClick: runDiagnostics, disabled: loading, children: loading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Diagnose draait..."] })) : ("Diagnose uitvoeren") })] })), report && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsxs("div", { className: "text-sm text-muted-foreground", children: ["Gemeten op ", formatDateTime(report.measuredAt)] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { variant: "outline", size: "sm", onClick: runDiagnostics, disabled: loading, children: loading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Bezig..."] })) : ("Opnieuw uitvoeren") }), _jsxs(Button, { variant: "outline", size: "sm", onClick: handleCopy, children: [_jsx(ClipboardCopy, { className: "mr-2 h-4 w-4" }), copied ? "Gekopieerd" : "Kopieer diagnose"] })] })] }), report.hints.length > 0 && (_jsx("div", { className: "rounded-md bg-amber-500/10 p-3 text-sm text-amber-700", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsxs("div", { children: [_jsx("div", { className: "font-medium", children: "Actiepunten" }), _jsx("ul", { className: "mt-1 list-inside list-disc space-y-0.5", children: report.hints.map((hint, i) => (_jsx("li", { children: hint }, i))) })] })] }) })), _jsx("ol", { className: "space-y-2", children: report.steps.map((step, i) => (_jsxs("li", { className: "flex flex-col gap-1 rounded-md border p-3 sm:flex-row sm:items-start sm:gap-3", children: [_jsx("div", { className: "shrink-0", children: step.ok ? (_jsx(CheckCircle2, { className: "h-4 w-4 text-emerald-600" })) : (_jsx(XCircle, { className: "h-4 w-4 text-red-600" })) }), _jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "font-medium", children: step.name }), _jsx(Badge, { className: step.ok
|
|
79
|
+
? "bg-emerald-500/10 text-emerald-700"
|
|
80
|
+
: "bg-red-500/10 text-red-700", children: step.ok ? "ok" : step.code ?? "fout" }), step.httpStatus && (_jsxs("span", { className: "text-xs text-muted-foreground", children: ["HTTP ", step.httpStatus] }))] }), _jsx("div", { className: "text-sm text-muted-foreground", children: step.summary }), _jsx("div", { className: "break-all text-xs text-muted-foreground", children: step.target }), step.hint && (_jsxs("div", { className: "text-xs text-amber-700", children: ["\u21B3 ", step.hint] })), step.body && (_jsx("pre", { className: "mt-1 max-h-40 overflow-auto rounded bg-muted p-2 text-xs", children: step.body }))] })] }, i))) })] }))] })] }));
|
|
81
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MapsStatusPanel.d.ts","sourceRoot":"","sources":["../../../src/components/maps/MapsStatusPanel.tsx"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAwBtE,wBAAgB,eAAe,CAAC,EAC9B,MAAM,GACP,EAAE;IACD,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC;CACrC,+BAwIA"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
// Statuskaart voor de statustab van de Kaarten-module.
|
|
3
|
+
// Toont koppelingsstatus met Central Auth en Google, met een testknop.
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { useServerFn } from "@tanstack/react-start";
|
|
6
|
+
import { useQuery } from "@tanstack/react-query";
|
|
7
|
+
import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, toast, } from "@flowselections/core";
|
|
8
|
+
import { AlertTriangle, CheckCircle2, RefreshCw, ShieldCheck, XCircle, } from "lucide-react";
|
|
9
|
+
import { getMapsOverview, testMapsConnection } from "../../lib/maps.functions";
|
|
10
|
+
function formatDateTime(value) {
|
|
11
|
+
if (!value)
|
|
12
|
+
return "onbekend";
|
|
13
|
+
const parsed = Date.parse(value);
|
|
14
|
+
if (Number.isNaN(parsed))
|
|
15
|
+
return value;
|
|
16
|
+
return new Date(parsed).toLocaleString("nl-NL");
|
|
17
|
+
}
|
|
18
|
+
function remaining(value) {
|
|
19
|
+
if (!value)
|
|
20
|
+
return "";
|
|
21
|
+
const parsed = Date.parse(value);
|
|
22
|
+
if (Number.isNaN(parsed))
|
|
23
|
+
return "";
|
|
24
|
+
const diff = parsed - Date.now();
|
|
25
|
+
if (diff <= 0)
|
|
26
|
+
return "verlopen";
|
|
27
|
+
const minutes = Math.round(diff / 60000);
|
|
28
|
+
if (minutes < 60)
|
|
29
|
+
return `over ${minutes} min`;
|
|
30
|
+
const hours = Math.round(minutes / 60);
|
|
31
|
+
if (hours < 48)
|
|
32
|
+
return `over ${hours} uur`;
|
|
33
|
+
return `over ${Math.round(hours / 24)} dagen`;
|
|
34
|
+
}
|
|
35
|
+
export function MapsStatusPanel({ status, }) {
|
|
36
|
+
const fetchOverview = useServerFn(getMapsOverview);
|
|
37
|
+
const runTest = useServerFn(testMapsConnection);
|
|
38
|
+
const [testing, setTesting] = useState(false);
|
|
39
|
+
const [lastTest, setLastTest] = useState(null);
|
|
40
|
+
const { refetch, isFetching } = useQuery({
|
|
41
|
+
queryKey: ["maps-overview"],
|
|
42
|
+
queryFn: () => fetchOverview(),
|
|
43
|
+
});
|
|
44
|
+
const handleTest = async () => {
|
|
45
|
+
setTesting(true);
|
|
46
|
+
try {
|
|
47
|
+
const result = await runTest();
|
|
48
|
+
setLastTest(result);
|
|
49
|
+
if (result.ok)
|
|
50
|
+
toast.success(result.message);
|
|
51
|
+
else
|
|
52
|
+
toast.error(result.message);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
setLastTest({ ok: false, message: "Verbinding testen is niet gelukt.", detail: null });
|
|
56
|
+
toast.error("Verbinding testen is niet gelukt.");
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
setTesting(false);
|
|
60
|
+
refetch();
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
const connected = status?.connected === true;
|
|
64
|
+
const accountPresent = status?.accountPresent === true;
|
|
65
|
+
const testedOk = connected && lastTest?.ok === true;
|
|
66
|
+
const statusLabel = testedOk
|
|
67
|
+
? "Koppeling actief en getest"
|
|
68
|
+
: connected
|
|
69
|
+
? "Koppeling aanwezig, verbinding nog niet getest"
|
|
70
|
+
: accountPresent
|
|
71
|
+
? "Koppeling aanwezig, verbinding faalt"
|
|
72
|
+
: "Nog geen koppeling";
|
|
73
|
+
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(ShieldCheck, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Google Maps-koppeling (provider google_maps)" })] }), _jsx(CardDescription, { children: "De Maps API-key wordt centraal beheerd in Central Auth. Deze module bewaart geen Google-credentials." })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsx("div", { className: "flex items-center gap-2", children: testedOk ? (_jsxs(_Fragment, { children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-emerald-600" }), _jsx(Badge, { className: "bg-emerald-500/10 text-emerald-700", children: statusLabel })] })) : accountPresent ? (_jsxs(_Fragment, { children: [_jsx(AlertTriangle, { className: "h-4 w-4 text-amber-600" }), _jsx(Badge, { className: "bg-amber-500/10 text-amber-700", children: statusLabel })] })) : (_jsxs(_Fragment, { children: [_jsx(XCircle, { className: "h-4 w-4 text-red-600" }), _jsx(Badge, { className: "bg-red-500/10 text-red-700", children: statusLabel })] })) }), _jsxs("dl", { className: "grid gap-3 text-sm sm:grid-cols-2", children: [_jsxs("div", { children: [_jsx("dt", { className: "text-muted-foreground", children: "Central host" }), _jsx("dd", { className: "font-medium", children: status?.centralHost ?? "—" })] }), _jsxs("div", { children: [_jsx("dt", { className: "text-muted-foreground", children: "Central secret" }), _jsx("dd", { className: "font-medium", children: status?.secretPresent ? "aanwezig" : "ontbreekt" })] }), _jsxs("div", { children: [_jsx("dt", { className: "text-muted-foreground", children: "Account" }), _jsx("dd", { className: "font-medium", children: status?.account ?? "—" })] }), _jsxs("div", { children: [_jsx("dt", { className: "text-muted-foreground", children: "Accountstatus" }), _jsx("dd", { className: "font-medium", children: status?.accountStatus ?? "—" })] }), _jsxs("div", { children: [_jsx("dt", { className: "text-muted-foreground", children: "Token geldig tot" }), _jsx("dd", { className: "font-medium", children: formatDateTime(status?.expiresAt ?? null) })] }), _jsxs("div", { children: [_jsx("dt", { className: "text-muted-foreground", children: "Resterend" }), _jsx("dd", { className: "font-medium", children: remaining(status?.expiresAt ?? null) || "—" })] })] }), status?.reason && !connected && (_jsx("div", { className: "rounded-md bg-amber-500/10 p-3 text-sm text-amber-700", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsx("span", { children: status.reason })] }) })), lastTest && !lastTest.ok && (_jsxs("div", { className: "rounded-md bg-amber-500/10 p-3 text-sm text-amber-700", children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsx("span", { children: lastTest.message })] }), lastTest.detail && (_jsxs("p", { className: "mt-1 break-all pl-6 text-xs text-muted-foreground", children: ["Technisch: ", lastTest.detail] }))] })), _jsx("p", { className: "text-xs text-muted-foreground", children: "De test doet een kleine Google Geocoding-aanroep met een vast testadres." }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { onClick: handleTest, disabled: testing, children: testing ? "Testen..." : "Verbinding testen" }), _jsxs(Button, { variant: "outline", onClick: () => refetch(), disabled: isFetching, children: [_jsx(RefreshCw, { className: "mr-2 h-4 w-4" }), "Vernieuwen"] })] })] })] }));
|
|
74
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MapsSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/MapsSettingsCard.tsx"],"names":[],"mappings":"AAeA,wBAAgB,gBAAgB,gCAiC/B"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useQuery } from "@tanstack/react-query";
|
|
3
|
+
import { useServerFn } from "@tanstack/react-start";
|
|
4
|
+
import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@flowselections/core";
|
|
5
|
+
import { Link } from "@tanstack/react-router";
|
|
6
|
+
import { MapPin } from "lucide-react";
|
|
7
|
+
import { getMapsOverview } from "../../lib/maps.functions";
|
|
8
|
+
export function MapsSettingsCard() {
|
|
9
|
+
const fetchOverview = useServerFn(getMapsOverview);
|
|
10
|
+
const { data } = useQuery({
|
|
11
|
+
queryKey: ["maps-overview"],
|
|
12
|
+
queryFn: () => fetchOverview(),
|
|
13
|
+
});
|
|
14
|
+
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(MapPin, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Kaarten" })] }), data ? (_jsx(Badge, { variant: data.connected ? "default" : "destructive", children: data.connected ? "Verbonden" : "Niet beschikbaar" })) : null] }), _jsx(CardDescription, { children: "Google-adressen en reistijden voor de planning." })] }), _jsxs(CardContent, { className: "space-y-3 text-sm text-muted-foreground", children: [_jsxs("p", { children: [data?.elementsMonth ?? 0, " elementen deze maand \u00B7 ", data?.queuePending ?? 0, " in de wachtrij"] }), _jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsx(Link, { to: "/maps-installatie", children: "Naar beheerscherm" }) })] })] }));
|
|
15
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FlowModule } from "@flowselections/core";
|
|
2
|
+
export * from "./_core-safelist";
|
|
3
|
+
export { MapsBeheerPage } from "./components/MapsBeheerPage";
|
|
4
|
+
export { MapsSettingsCard } from "./components/settings/MapsSettingsCard";
|
|
5
|
+
export { MapsInstallatieLogo } from "./components/MapsInstallatieLogo";
|
|
6
|
+
export declare const mapsInstallatieModule: FlowModule;
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAIvD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAEvE,eAAO,MAAM,qBAAqB,EAAE,UAUnC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// src/index.ts — MODULE CONTRACT (kaarten / maps-installatie)
|
|
3
|
+
// ============================================================================
|
|
4
|
+
import { Map } from "lucide-react";
|
|
5
|
+
import { MapsSettingsCard } from "./components/settings/MapsSettingsCard";
|
|
6
|
+
export * from "./_core-safelist";
|
|
7
|
+
export { MapsBeheerPage } from "./components/MapsBeheerPage";
|
|
8
|
+
export { MapsSettingsCard } from "./components/settings/MapsSettingsCard";
|
|
9
|
+
export { MapsInstallatieLogo } from "./components/MapsInstallatieLogo";
|
|
10
|
+
export const mapsInstallatieModule = {
|
|
11
|
+
id: "maps_installatie",
|
|
12
|
+
name: "Kaarten",
|
|
13
|
+
version: "1.0.0",
|
|
14
|
+
nav: {
|
|
15
|
+
label: "Kaarten",
|
|
16
|
+
href: "/maps-installatie",
|
|
17
|
+
icon: Map,
|
|
18
|
+
},
|
|
19
|
+
settingsCards: [{ component: MapsSettingsCard, order: 10 }],
|
|
20
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-attacher.d.ts","sourceRoot":"","sources":["../../../src/integrations/supabase/auth-attacher.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,kBAAkB,mHAQ9B,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// This file is automatically generated. Do not edit it directly.
|
|
2
|
+
import { createMiddleware } from '@tanstack/react-start';
|
|
3
|
+
import { supabase } from './client';
|
|
4
|
+
// Must be registered as a global `functionMiddleware` in `src/start.ts`; otherwise
|
|
5
|
+
// the browser never attaches the bearer token to serverFn RPCs.
|
|
6
|
+
export const attachSupabaseAuth = createMiddleware({ type: 'function' }).client(async ({ next }) => {
|
|
7
|
+
const { data } = await supabase.auth.getSession();
|
|
8
|
+
const token = data.session?.access_token;
|
|
9
|
+
return next({
|
|
10
|
+
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
|
11
|
+
});
|
|
12
|
+
});
|