@flowselections/dashboard-planner-installatie-techniek 1.0.38 → 1.0.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. package/dist-lib/components/DashboardPlannerInstallatieTechniekPage.js +1 -1
  2. package/dist-lib/components/WerkbonnenFunnel.js +1 -1
  3. package/dist-lib/components/WerkurenGrafiek.js +1 -1
  4. package/dist-lib/integrations/supabase/client.js +1 -1
  5. package/dist-lib/integrations/supabase/client.server.js +1 -1
  6. package/dist-lib/integrations/supabase/types.d.ts +106 -0
  7. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  8. package/package.json +2 -2
  9. package/dist-lib/components/DashboardInstallatieTechniekPage.d.ts +0 -2
  10. package/dist-lib/components/DashboardInstallatieTechniekPage.d.ts.map +0 -1
  11. package/dist-lib/components/DashboardInstallatieTechniekPage.js +0 -129
  12. package/dist-lib/components/InstallatieTechniekLogo.d.ts +0 -6
  13. package/dist-lib/components/InstallatieTechniekLogo.d.ts.map +0 -1
  14. package/dist-lib/components/InstallatieTechniekLogo.js +0 -4
  15. package/dist-lib/lib/dashboard-installatietechniek.functions.d.ts +0 -22018
  16. package/dist-lib/lib/dashboard-installatietechniek.functions.d.ts.map +0 -1
  17. package/dist-lib/lib/dashboard-installatietechniek.functions.js +0 -15
  18. package/dist-lib/lib/dashboard-installatietechniek.server.d.ts +0 -7
  19. package/dist-lib/lib/dashboard-installatietechniek.server.d.ts.map +0 -1
  20. package/dist-lib/lib/dashboard-installatietechniek.server.js +0 -130
  21. package/dist-lib/lib/dashboard-installatietechniek.types.d.ts +0 -54
  22. package/dist-lib/lib/dashboard-installatietechniek.types.d.ts.map +0 -1
  23. package/dist-lib/lib/dashboard-installatietechniek.types.js +0 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/dashboard-planner-installatie-techniek",
3
- "version": "1.0.38",
3
+ "version": "1.0.40",
4
4
  "flowselections": {
5
5
  "moduleId": "dashboard-planner-installatie-techniek",
6
6
  "pages": [
@@ -118,7 +118,7 @@
118
118
  "devDependencies": {
119
119
  "@cloudflare/vite-plugin": "^1.25.5",
120
120
  "@eslint/js": "^9.32.0",
121
- "@flowselections/core": "1.0.29",
121
+ "@flowselections/core": "1.0.30",
122
122
  "@lovable.dev/vite-tanstack-config": "2.26.5",
123
123
  "@rsbuild/core": "^1.0.0",
124
124
  "@tailwindcss/vite": "^4.2.1",
@@ -1,2 +0,0 @@
1
- export declare function DashboardInstallatieTechniekPage(): import("react").JSX.Element;
2
- //# sourceMappingURL=DashboardInstallatieTechniekPage.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"DashboardInstallatieTechniekPage.d.ts","sourceRoot":"","sources":["../../src/components/DashboardInstallatieTechniekPage.tsx"],"names":[],"mappings":"AAwHA,wBAAgB,gCAAgC,gCAmQ/C"}
@@ -1,129 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- // ============================================================================
3
- // src/components/DashboardInstallatieTechniekPage.tsx
4
- // ============================================================================
5
- // Dashboard voor de installatietechniek — alle cijfers komen uit de database
6
- // via de getDashboardData server-function. Blokken zonder bruikbare bron
7
- // blijven bewust leeg met een duidelijke melding.
8
- //
9
- // GEEN <Header /> hier — de shell levert de Header via staticData.title.
10
- // ============================================================================
11
- import { useQuery, useQueryClient } from "@tanstack/react-query";
12
- import { useServerFn } from "@tanstack/react-start";
13
- import { AlertTriangle, CalendarCheck, CheckCircle2, ClipboardList, Eye, LayoutGrid, Package, RefreshCw, Truck, Wrench, } from "lucide-react";
14
- import { useState } from "react";
15
- import { Badge, Button, Card, CardContent, CardHeader, CardTitle, Skeleton, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, toast, useAuth, } from "@flowselections/core";
16
- import { getDashboardData } from "../lib/dashboard-installatietechniek.functions";
17
- // ── KPI-tegels (volgorde = referentie) ────────────────────────────────────────
18
- const KPI_TILES = [
19
- { key: "totaleVoorraad", label: "Totale Voorraad", Icon: Package, kleur: "text-blue-600 bg-blue-500/10" },
20
- { key: "openBestellingen", label: "Open Bestellingen", Icon: ClipboardList, kleur: "text-blue-600 bg-blue-500/10" },
21
- { key: "verzendingenVandaag", label: "Verzendingen Vandaag", Icon: Truck, kleur: "text-green-600 bg-green-500/10" },
22
- { key: "voorraadAlerts", label: "Voorraad Alerts", Icon: AlertTriangle, kleur: "text-blue-600 bg-blue-500/10" },
23
- { key: "openWerkbonnen", label: "Open Werkbonnen", Icon: Wrench, kleur: "text-blue-600 bg-blue-500/10" },
24
- { key: "werkbonnenVandaag", label: "Werkbonnen Vandaag", Icon: CalendarCheck, kleur: "text-green-600 bg-green-500/10" },
25
- { key: "teControleren", label: "Te Controleren", Icon: Eye, kleur: "text-orange-600 bg-orange-500/10" },
26
- ];
27
- const EMPTY = {
28
- kpis: {
29
- totaleVoorraad: 0,
30
- openBestellingen: 0,
31
- verzendingenVandaag: 0,
32
- voorraadAlerts: 0,
33
- openWerkbonnen: 0,
34
- werkbonnenVandaag: 0,
35
- teControleren: 0,
36
- },
37
- recenteBestellingen: [],
38
- voorraadWaarschuwingenMagazijn: [],
39
- voorraadWaarschuwingenBus: [],
40
- verzendingenVandaag: [],
41
- garanties: [],
42
- };
43
- const STATUS_LABEL = {
44
- open: "Open",
45
- in_progress: "In uitvoering",
46
- invoicing: "Te factureren",
47
- planned_hidden: "Gepland",
48
- planned_visible: "Gepland",
49
- unassigned: "Niet toegewezen",
50
- completed: "Afgerond",
51
- geannuleerd: "Geannuleerd",
52
- concept: "Concept",
53
- verzonden: "Verzonden",
54
- afgerond: "Afgerond",
55
- goedgekeurd: "Goedgekeurd",
56
- };
57
- function statusLabel(status) {
58
- return STATUS_LABEL[status] ?? status ?? "—";
59
- }
60
- /**
61
- * Server-function data is nooit blind te vertrouwen: bij een auth-fout of een
62
- * onverwachte respons kan `data` een object zijn dat géén DashboardData is.
63
- * Deze helper normaliseert altijd naar een volledig geldig DashboardData-object.
64
- */
65
- function normaliseerDashboard(data) {
66
- const d = (data ?? {});
67
- const kpis = (d.kpis && typeof d.kpis === "object" ? d.kpis : {});
68
- return {
69
- kpis: { ...EMPTY.kpis, ...kpis },
70
- recenteBestellingen: Array.isArray(d.recenteBestellingen) ? d.recenteBestellingen : [],
71
- voorraadWaarschuwingenMagazijn: Array.isArray(d.voorraadWaarschuwingenMagazijn)
72
- ? d.voorraadWaarschuwingenMagazijn
73
- : [],
74
- voorraadWaarschuwingenBus: Array.isArray(d.voorraadWaarschuwingenBus)
75
- ? d.voorraadWaarschuwingenBus
76
- : [],
77
- verzendingenVandaag: Array.isArray(d.verzendingenVandaag) ? d.verzendingenVandaag : [],
78
- garanties: Array.isArray(d.garanties) ? d.garanties : [],
79
- };
80
- }
81
- export function DashboardInstallatieTechniekPage() {
82
- const { user } = useAuth();
83
- const queryClient = useQueryClient();
84
- const fetchDashboard = useServerFn(getDashboardData);
85
- const { data, isLoading, isFetching, isError, refetch } = useQuery({
86
- queryKey: ["dashboard-installatietechniek"],
87
- queryFn: async () => {
88
- try {
89
- return await fetchDashboard();
90
- }
91
- catch (err) {
92
- // Een 401/500 komt terug als een ruwe Response; die mag nooit als
93
- // "Error: [object Response]" naar de router-errorboundary lekken.
94
- if (err instanceof Response) {
95
- const tekst = await err.text().catch(() => "");
96
- throw new Error(`Dashboard laden mislukt (${err.status}) ${tekst}`.trim());
97
- }
98
- throw err instanceof Error ? err : new Error(String(err));
99
- }
100
- },
101
- staleTime: 30000,
102
- retry: false,
103
- });
104
- const dashboard = normaliseerDashboard(data);
105
- const [voorraadTab, setVoorraadTab] = useState("magazijn");
106
- const handleVernieuwen = () => {
107
- queryClient.invalidateQueries({ queryKey: ["dashboard-installatietechniek"] });
108
- refetch();
109
- toast.success("Dashboard ververst");
110
- };
111
- const handleWidgetsBeheren = () => {
112
- toast.info("Widgets beheren — binnenkort beschikbaar");
113
- };
114
- return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-2xl font-semibold tracking-tight", children: "Installatietechniek" }), _jsxs("p", { className: "text-sm text-muted-foreground", children: ["Welkom terug", user?.email ? `, ${user.email}` : "", " \u2014 hier is het actuele overzicht."] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { onClick: handleWidgetsBeheren, variant: "outline", className: "gap-2", children: [_jsx(LayoutGrid, { className: "w-4 h-4" }), "Widgets beheren"] }), _jsxs(Button, { onClick: handleVernieuwen, variant: "outline", className: "gap-2", disabled: isFetching, children: [_jsx(RefreshCw, { className: `w-4 h-4 ${isFetching ? "animate-spin" : ""}` }), "Vernieuwen"] })] })] }), isError && (_jsxs("div", { className: "flex items-center gap-3 rounded-md border border-destructive/30 bg-destructive/5 p-4 text-sm", children: [_jsx(AlertTriangle, { className: "h-4 w-4 text-destructive" }), _jsx("span", { className: "text-muted-foreground", children: "Kon dashboardgegevens niet laden \u2014 probeer het opnieuw met Vernieuwen." })] })), _jsx("div", { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-4", children: KPI_TILES.map((tile) => {
115
- const { Icon } = tile;
116
- const waarde = dashboard.kpis[tile.key];
117
- return (_jsx(Card, { children: _jsx(CardContent, { className: "p-5", children: _jsxs("div", { className: "flex items-start justify-between", children: [_jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-muted-foreground", children: tile.label }), isLoading ? (_jsx(Skeleton, { className: "mt-2 h-9 w-16" })) : (_jsx("p", { className: "mt-2 text-3xl font-semibold tracking-tight", children: waarde }))] }), _jsx("div", { className: `flex items-center justify-center w-10 h-10 rounded-full ${tile.kleur}`, children: _jsx(Icon, { className: "w-5 h-5" }) })] }) }) }, tile.key));
118
- }) }), _jsxs("div", { className: "grid gap-4 lg:grid-cols-2", children: [_jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsx(CardTitle, { children: "Recente Bestellingen" }), _jsx(Badge, { variant: "secondary", children: dashboard.recenteBestellingen.length })] }), _jsx(CardContent, { children: isLoading ? (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-8 w-full" }), _jsx(Skeleton, { className: "h-8 w-full" }), _jsx(Skeleton, { className: "h-8 w-full" })] })) : dashboard.recenteBestellingen.length === 0 ? (_jsx(EmptyState, { tekst: "Geen recente bestellingen" })) : (_jsx("div", { className: "rounded-md border", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Order" }), _jsx(TableHead, { children: "Klant" }), _jsx(TableHead, { children: "Status" }), _jsx(TableHead, { className: "text-right", children: "Bedrag" })] }) }), _jsx(TableBody, { children: dashboard.recenteBestellingen.map((b) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: b.orderNumber || "—" }), _jsx(TableCell, { className: "text-muted-foreground", children: b.customerName || "—" }), _jsx(TableCell, { children: _jsx(Badge, { variant: "outline", children: statusLabel(b.status) }) }), _jsx(TableCell, { className: "text-right text-muted-foreground", children: b.total || "—" })] }, b.orderNumber || b.orderDate))) })] }) })) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { className: "flex flex-row items-center justify-between", children: _jsx(CardTitle, { children: "Voorraadwaarschuwingen" }) }), _jsx(CardContent, { children: _jsxs(Tabs, { value: voorraadTab, onValueChange: (v) => setVoorraadTab(v), children: [_jsxs(TabsList, { children: [_jsx(TabsTrigger, { value: "magazijn", children: "Magazijn" }), _jsx(TabsTrigger, { value: "bus", children: "Bus-voorraad" })] }), _jsx(TabsContent, { value: "magazijn", className: "mt-4", children: isLoading ? (_jsx(Skeleton, { className: "h-8 w-full" })) : dashboard.voorraadWaarschuwingenMagazijn.length === 0 ? (_jsx(EmptyState, { tekst: "Geen voorraadwaarschuwingen" })) : (_jsx(VoorraadListMagazijn, { items: dashboard.voorraadWaarschuwingenMagazijn })) }), _jsx(TabsContent, { value: "bus", className: "mt-4", children: isLoading ? (_jsx(Skeleton, { className: "h-8 w-full" })) : dashboard.voorraadWaarschuwingenBus.length === 0 ? (_jsx(EmptyState, { tekst: "Geen voorraadwaarschuwingen" })) : (_jsx(VoorraadListBus, { items: dashboard.voorraadWaarschuwingenBus })) })] }) })] })] }), _jsxs("div", { className: "grid gap-4 lg:grid-cols-2", children: [_jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsx(CardTitle, { children: "Verzendingen Vandaag" }), _jsx(Badge, { variant: "secondary", children: dashboard.verzendingenVandaag.length })] }), _jsx(CardContent, { children: isLoading ? (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-8 w-full" }), _jsx(Skeleton, { className: "h-8 w-full" })] })) : dashboard.verzendingenVandaag.length === 0 ? (_jsx(EmptyState, { tekst: "Geen verzendingen vandaag" })) : (_jsx("div", { className: "rounded-md border", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Verzending" }), _jsx(TableHead, { children: "Order" }), _jsx(TableHead, { children: "Klant" }), _jsx(TableHead, { children: "Status" })] }) }), _jsx(TableBody, { children: dashboard.verzendingenVandaag.map((v) => (_jsxs(TableRow, { children: [_jsxs(TableCell, { className: "font-medium", children: ["#", v.shipmentNumber] }), _jsx(TableCell, { children: v.orderNumber || "—" }), _jsx(TableCell, { className: "text-muted-foreground", children: v.customerName || "—" }), _jsx(TableCell, { children: _jsx(Badge, { variant: "outline", children: statusLabel(v.status) }) })] }, `${v.orderNumber}-${v.shipmentNumber}`))) })] }) })) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(CheckCircle2, { className: "w-5 h-5 text-green-600" }), "Garanties"] }) }), _jsx(CardContent, { children: _jsx(EmptyState, { tekst: "Geen garanties die binnenkort verlopen" }) })] })] })] }));
119
- }
120
- // ── Subcomponenten ───────────────────────────────────────────────────────────
121
- function EmptyState({ tekst }) {
122
- return (_jsx("div", { className: "flex min-h-[6rem] items-center justify-center py-8 text-center text-sm text-muted-foreground", children: tekst }));
123
- }
124
- function VoorraadListMagazijn({ items, }) {
125
- return (_jsx("div", { className: "rounded-md border", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Artikel" }), _jsx(TableHead, { children: "Artikelnr." }), _jsx(TableHead, { className: "text-right", children: "Voorraad" })] }) }), _jsx(TableBody, { children: items.map((m) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: m.description || "—" }), _jsx(TableCell, { className: "text-muted-foreground", children: m.articleNumber || "—" }), _jsxs(TableCell, { className: "text-right text-muted-foreground", children: [m.quantity, " ", m.unit] })] }, m.articleNumber || m.description))) })] }) }));
126
- }
127
- function VoorraadListBus({ items, }) {
128
- return (_jsx("div", { className: "rounded-md border", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Product" }), _jsx(TableHead, { className: "text-right", children: "Huidig" }), _jsx(TableHead, { className: "text-right", children: "Minimum" })] }) }), _jsx(TableBody, { children: items.map((m) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: m.productName || "—" }), _jsxs(TableCell, { className: "text-right text-red-600", children: [m.quantityCurrent, " ", m.unit] }), _jsxs(TableCell, { className: "text-right text-muted-foreground", children: [m.quantityMin, " ", m.unit] })] }, m.barcode || m.productName))) })] }) }));
129
- }
@@ -1,6 +0,0 @@
1
- interface InstallatieTechniekLogoProps {
2
- className?: string;
3
- }
4
- export declare function InstallatieTechniekLogo({ className }: InstallatieTechniekLogoProps): import("react").JSX.Element;
5
- export {};
6
- //# sourceMappingURL=InstallatieTechniekLogo.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"InstallatieTechniekLogo.d.ts","sourceRoot":"","sources":["../../src/components/InstallatieTechniekLogo.tsx"],"names":[],"mappings":"AAKA,UAAU,4BAA4B;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,uBAAuB,CAAC,EAAE,SAAS,EAAE,EAAE,4BAA4B,+BAsBlF"}
@@ -1,4 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- export function InstallatieTechniekLogo({ 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: "M12 28 L20 20 M24 16 a4 4 0 1 0 -2.8 -1.2 L19 17 a1.4 1.4 0 0 0 0 2 L22 22 a1.4 1.4 0 0 0 2 0 l2.2 -2.2 A4 4 0 0 0 24 16 Z", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", fill: "currentColor", fillOpacity: "0.15" })] }));
4
- }