@flowselections/dashboard-planner-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/README.md +24 -0
- 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 +30 -0
- package/dist-lib/components/DashboardInstallatieTechniekPage.d.ts +2 -0
- package/dist-lib/components/DashboardInstallatieTechniekPage.d.ts.map +1 -0
- package/dist-lib/components/DashboardInstallatieTechniekPage.js +129 -0
- package/dist-lib/components/InstallatieTechniekLogo.d.ts +6 -0
- package/dist-lib/components/InstallatieTechniekLogo.d.ts.map +1 -0
- package/dist-lib/components/InstallatieTechniekLogo.js +4 -0
- package/dist-lib/components/settings/MijnModuleSettingsCard.d.ts +2 -0
- package/dist-lib/components/settings/MijnModuleSettingsCard.d.ts.map +1 -0
- package/dist-lib/components/settings/MijnModuleSettingsCard.js +42 -0
- package/dist-lib/hooks/useMijnModuleData.d.ts +13 -0
- package/dist-lib/hooks/useMijnModuleData.d.ts.map +1 -0
- package/dist-lib/hooks/useMijnModuleData.js +49 -0
- package/dist-lib/index.d.ts +6 -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 +22018 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/auth-middleware.js +51 -0
- package/dist-lib/integrations/supabase/client.d.ts +22014 -0
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/client.js +13 -0
- package/dist-lib/integrations/supabase/client.server.d.ts +22014 -0
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/client.server.js +30 -0
- package/dist-lib/integrations/supabase/types.d.ts +22614 -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/dashboard-installatietechniek.functions.d.ts +22018 -0
- package/dist-lib/lib/dashboard-installatietechniek.functions.d.ts.map +1 -0
- package/dist-lib/lib/dashboard-installatietechniek.functions.js +15 -0
- package/dist-lib/lib/dashboard-installatietechniek.server.d.ts +7 -0
- package/dist-lib/lib/dashboard-installatietechniek.server.d.ts.map +1 -0
- package/dist-lib/lib/dashboard-installatietechniek.server.js +130 -0
- package/dist-lib/lib/dashboard-installatietechniek.types.d.ts +54 -0
- package/dist-lib/lib/dashboard-installatietechniek.types.d.ts.map +1 -0
- package/dist-lib/lib/dashboard-installatietechniek.types.js +7 -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
package/README.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Dashboard - Installatie Techniek
|
|
2
|
+
|
|
3
|
+
blank project start
|
|
4
|
+
|
|
5
|
+
This project was built with [Lovable](https://lovable.dev).
|
|
6
|
+
|
|
7
|
+
## Build with Lovable
|
|
8
|
+
|
|
9
|
+
Continue developing this project in the [Lovable editor](https://lovable.dev/projects/74cd71d5-b2ff-473a-8ed5-9a68ede2ca92).
|
|
10
|
+
|
|
11
|
+
- **Ship faster**: describe what you want to build and Lovable handles the code.
|
|
12
|
+
- **Stay in sync**: every change made in Lovable is committed straight to this repository.
|
|
13
|
+
- **Full ownership**: this code is yours. Push to `main` on GitHub and your changes sync back into Lovable, ready for your next prompt.
|
|
14
|
+
|
|
15
|
+
## Development
|
|
16
|
+
|
|
17
|
+
Prefer working locally? You need Node.js and npm — [install with nvm](https://github.com/nvm-sh/nvm#installing-and-updating).
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
git clone <this-repository-url>
|
|
21
|
+
cd <repository-name>
|
|
22
|
+
npm i
|
|
23
|
+
npm run dev
|
|
24
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_core-safelist.d.ts","sourceRoot":"","sources":["../src/_core-safelist.ts"],"names":[],"mappings":"AAgCA,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
// ── Dashboard-installatietechniek — dynamische status- en KPI-kleuren ──
|
|
15
|
+
// Statusbadges
|
|
16
|
+
'bg-blue-500/10', 'text-blue-600',
|
|
17
|
+
'bg-amber-500/10', 'text-amber-600',
|
|
18
|
+
'bg-purple-500/10', 'text-purple-600',
|
|
19
|
+
'bg-green-500/10', 'text-green-600',
|
|
20
|
+
'bg-indigo-500/10', 'text-indigo-600',
|
|
21
|
+
// KPI-icoonkachels (kleur+bg in één class)
|
|
22
|
+
'text-blue-600 bg-blue-500/10',
|
|
23
|
+
'text-amber-600 bg-amber-500/10',
|
|
24
|
+
'text-green-600 bg-green-500/10',
|
|
25
|
+
'text-indigo-600 bg-indigo-500/10',
|
|
26
|
+
'text-orange-600 bg-orange-500/10',
|
|
27
|
+
// Bus-voorraad alert-waarde
|
|
28
|
+
'text-red-600',
|
|
29
|
+
];
|
|
30
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DashboardInstallatieTechniekPage.d.ts","sourceRoot":"","sources":["../../src/components/DashboardInstallatieTechniekPage.tsx"],"names":[],"mappings":"AAwHA,wBAAgB,gCAAgC,gCAmQ/C"}
|
|
@@ -0,0 +1,129 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MijnModuleSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/MijnModuleSettingsCard.tsx"],"names":[],"mappings":"AAiCA,wBAAgB,sBAAsB,gCA+CrC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// src/components/settings/MijnModuleSettingsCard.tsx — EXAMPLE SETTINGS CARD
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// This is an example of a settings card for your module.
|
|
6
|
+
// It appears on the main settings page when registered in src/index.ts.
|
|
7
|
+
//
|
|
8
|
+
// RULES:
|
|
9
|
+
// - Must return a <Card> as its root element
|
|
10
|
+
// - Handles its own data fetching — no props are passed by the core
|
|
11
|
+
// - Use the same Card primitives from @flowselections/core
|
|
12
|
+
//
|
|
13
|
+
// WHAT TO DO:
|
|
14
|
+
// - Rename this file to match your card's purpose
|
|
15
|
+
// - Add your real settings UI
|
|
16
|
+
// - Register it in src/index.ts under settingsCards
|
|
17
|
+
// - Delete this file entirely if your module has no settings
|
|
18
|
+
// ============================================================================
|
|
19
|
+
import { useState } from "react";
|
|
20
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle, Button, Input, Label, toast, } from "@flowselections/core";
|
|
21
|
+
import { Settings2 } from "lucide-react";
|
|
22
|
+
export function MijnModuleSettingsCard() {
|
|
23
|
+
// Replace with your real settings state
|
|
24
|
+
const [setting, setSetting] = useState("");
|
|
25
|
+
const [saving, setSaving] = useState(false);
|
|
26
|
+
const handleSave = async () => {
|
|
27
|
+
setSaving(true);
|
|
28
|
+
try {
|
|
29
|
+
// Example: save to a table — replace with your real query
|
|
30
|
+
// const { error } = await supabase.from("my_settings").upsert({ ... });
|
|
31
|
+
// if (error) throw error;
|
|
32
|
+
toast.success("Instellingen opgeslagen");
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
toast.error("Fout bij het opslaan");
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
setSaving(false);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Settings2, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Module Instellingen" }), " "] }), _jsx(CardDescription, { children: "Beheer de instellingen voor deze module " })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "setting", children: "Instelling" }), _jsx(Input, { id: "setting", value: setting, onChange: (e) => setSetting(e.target.value), placeholder: "Waarde..." })] }), _jsx(Button, { onClick: handleSave, disabled: saving, children: saving ? "Opslaan..." : "Opslaan" })] })] }));
|
|
42
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface MijnItem {
|
|
2
|
+
id: string;
|
|
3
|
+
created_at: string;
|
|
4
|
+
[key: string]: unknown;
|
|
5
|
+
}
|
|
6
|
+
export declare function useMijnModuleData(): {
|
|
7
|
+
items: MijnItem[];
|
|
8
|
+
loading: boolean;
|
|
9
|
+
error: string | null;
|
|
10
|
+
reload: () => Promise<void>;
|
|
11
|
+
};
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=useMijnModuleData.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMijnModuleData.d.ts","sourceRoot":"","sources":["../../src/hooks/useMijnModuleData.ts"],"names":[],"mappings":"AAkBA,UAAU,QAAQ;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,wBAAgB,iBAAiB;;;;;EAkChC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// src/hooks/useMijnModuleData.ts — EXAMPLE DATA HOOK
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Put your data fetching logic in hooks like this, not directly in components.
|
|
5
|
+
// This keeps your pages clean and makes it easy to reuse data across pages.
|
|
6
|
+
//
|
|
7
|
+
// WHAT TO DO:
|
|
8
|
+
// - Rename to match your data (e.g. useProducts, useOrders, useSuppliers)
|
|
9
|
+
// - Replace the example query with your real Supabase query
|
|
10
|
+
// - Replace MijnItem with your actual table's row type
|
|
11
|
+
// - Import and use in your page components
|
|
12
|
+
// - Delete this file if you prefer a different pattern
|
|
13
|
+
// ============================================================================
|
|
14
|
+
import { useState, useEffect, useCallback } from "react";
|
|
15
|
+
import { supabase, toast } from "@flowselections/core";
|
|
16
|
+
export function useMijnModuleData() {
|
|
17
|
+
const [items, setItems] = useState([]);
|
|
18
|
+
const [loading, setLoading] = useState(true);
|
|
19
|
+
const [error, setError] = useState(null);
|
|
20
|
+
const load = useCallback(async () => {
|
|
21
|
+
setLoading(true);
|
|
22
|
+
setError(null);
|
|
23
|
+
try {
|
|
24
|
+
const { data, error } = await supabase
|
|
25
|
+
.from("products") // ← CHANGE THIS to your table
|
|
26
|
+
.select("*")
|
|
27
|
+
.order("created_at", { ascending: false });
|
|
28
|
+
if (error)
|
|
29
|
+
throw error;
|
|
30
|
+
setItems(data ?? []);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
setError("Fout bij het laden van gegevens");
|
|
34
|
+
toast.error("Fout bij het laden van gegevens");
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
setLoading(false);
|
|
38
|
+
}
|
|
39
|
+
}, []);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
load();
|
|
42
|
+
}, [load]);
|
|
43
|
+
return {
|
|
44
|
+
items,
|
|
45
|
+
loading,
|
|
46
|
+
error,
|
|
47
|
+
reload: load,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { FlowModule } from "@flowselections/core";
|
|
2
|
+
export * from "./_core-safelist";
|
|
3
|
+
export { DashboardInstallatieTechniekPage } from "./components/DashboardInstallatieTechniekPage";
|
|
4
|
+
export { InstallatieTechniekLogo } from "./components/InstallatieTechniekLogo";
|
|
5
|
+
export declare const dashboardInstallatieTechniekModule: FlowModule;
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,gCAAgC,EAAE,MAAM,+CAA+C,CAAC;AACjG,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAE/E,eAAO,MAAM,kCAAkC,EAAE,UAYhD,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// src/index.ts — MODULE CONTRACT
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// De shell leest dit bestand voor navigatie, instellingen en identiteit.
|
|
5
|
+
// ============================================================================
|
|
6
|
+
import { Gauge } from "lucide-react";
|
|
7
|
+
export * from "./_core-safelist";
|
|
8
|
+
export { DashboardInstallatieTechniekPage } from "./components/DashboardInstallatieTechniekPage";
|
|
9
|
+
export { InstallatieTechniekLogo } from "./components/InstallatieTechniekLogo";
|
|
10
|
+
export const dashboardInstallatieTechniekModule = {
|
|
11
|
+
id: "dashboard-installatietechniek",
|
|
12
|
+
name: "Installatietechniek",
|
|
13
|
+
version: "1.0.0",
|
|
14
|
+
nav: {
|
|
15
|
+
label: "Installatietechniek",
|
|
16
|
+
href: "/dashboard-installatietechniek",
|
|
17
|
+
icon: Gauge,
|
|
18
|
+
},
|
|
19
|
+
settingsCards: [],
|
|
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
|
+
});
|