@flowselections/admin-installatietechniek 1.0.2 → 1.0.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"AdminForInstallatieTechniek.d.ts","sourceRoot":"","sources":["../../src/components/AdminForInstallatieTechniek.tsx"],"names":[],"mappings":"AAgEA,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gCAAgC;IAC/C,8EAA8E;IAC9E,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC7B,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAiDD,wBAAgB,2BAA2B,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,GAAE,gCAAqC,+BAy0BtG"}
1
+ {"version":3,"file":"AdminForInstallatieTechniek.d.ts","sourceRoot":"","sources":["../../src/components/AdminForInstallatieTechniek.tsx"],"names":[],"mappings":"AAoEA,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gCAAgC;IAC/C,8EAA8E;IAC9E,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC7B,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAiDD,wBAAgB,2BAA2B,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,GAAE,gCAAqC,+BAo+BtG"}
@@ -1,8 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useMemo, useState } from "react";
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
3
  import { useAuth, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, Button, Card, CardContent, CardHeader, CardTitle, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Badge, toast, } from "@flowselections/core";
4
- import { Copy, FlaskConical, Shield, Trash2, UserPlus, UserX } from "lucide-react";
5
- import { assignRole, createTestUser, deleteUser, getModulePermissions, getUserModulePermissions, getUsersWithRoles, inviteUser, removeRole, setModulePermission, setUserModulePermission, syncModuleRegistry, } from "../lib/admin.functions";
4
+ import { ArrowDown, ArrowUp, Copy, FlaskConical, Shield, Trash2, UserPlus, UserX } from "lucide-react";
5
+ import { assignRole, createTestUser, deleteUser, getModuleOrder, getModulePermissions, getUserModulePermissions, getUsersWithRoles, inviteUser, removeRole, setModuleOrder, setModulePermission, setUserModulePermission, syncModuleRegistry, } from "../lib/admin.functions";
6
+ import { notifyModuleOrderChanged } from "../hooks/useModuleOrder";
6
7
  import { useDiscoveredModules } from "../lib/moduleDiscovery";
7
8
  import { supabase } from "../integrations/supabase/client";
8
9
  const PERMISSION_LABELS = {
@@ -69,6 +70,11 @@ export function AdminForInstallatieTechniek({ modules, shellId } = {}) {
69
70
  const [userPermModules, setUserPermModules] = useState([]);
70
71
  const [userPermMap, setUserPermMap] = useState({});
71
72
  const [userPermLoading, setUserPermLoading] = useState(false);
73
+ const [orderModules, setOrderModules] = useState([]);
74
+ const [orderLoading, setOrderLoading] = useState(false);
75
+ const [orderSaving, setOrderSaving] = useState(false);
76
+ const [orderSavedAt, setOrderSavedAt] = useState(null);
77
+ const orderSaveTimer = useRef(null);
72
78
  const permKey = (role, moduleId) => `${role}::${moduleId}`;
73
79
  const discoveredModules = useDiscoveredModules();
74
80
  const shellModules = useMemo(() => {
@@ -144,6 +150,89 @@ export function AdminForInstallatieTechniek({ modules, shellId } = {}) {
144
150
  setUserPermLoading(false);
145
151
  }
146
152
  };
153
+ const loadModuleOrder = async () => {
154
+ setOrderLoading(true);
155
+ try {
156
+ const result = await getModuleOrder();
157
+ const saved = Array.isArray(result?.modules) ? result.modules : [];
158
+ const byId = new Map(saved.map((m) => [m.id, m]));
159
+ // Standaardvolgorde = de volgorde waarin de shell zijn modules levert.
160
+ // Opgeslagen posities krijgen voorrang; de rest houdt de bronvolgorde.
161
+ const base = [];
162
+ for (const m of shellModules) {
163
+ const match = byId.get(m.id);
164
+ base.push({ id: m.id, name: match?.name ?? m.name });
165
+ byId.delete(m.id);
166
+ }
167
+ for (const m of byId.values())
168
+ base.push({ id: m.id, name: m.name });
169
+ const positions = new Map();
170
+ for (const m of saved) {
171
+ if (typeof m.sortOrder === "number")
172
+ positions.set(m.id, m.sortOrder);
173
+ }
174
+ const ordered = base
175
+ .map((mod, index) => ({ mod, index }))
176
+ .sort((a, b) => {
177
+ const ao = positions.get(a.mod.id);
178
+ const bo = positions.get(b.mod.id);
179
+ if (ao !== undefined && bo !== undefined)
180
+ return ao - bo;
181
+ if (ao !== undefined)
182
+ return -1;
183
+ if (bo !== undefined)
184
+ return 1;
185
+ return a.index - b.index;
186
+ })
187
+ .map((e) => e.mod);
188
+ setOrderModules(ordered);
189
+ }
190
+ catch (error) {
191
+ toast.error(getErrorMessage(error, "Fout bij het laden van de zijbalk-volgorde"));
192
+ }
193
+ finally {
194
+ setOrderLoading(false);
195
+ }
196
+ };
197
+ const persistOrder = useCallback(async (list, previous) => {
198
+ setOrderSaving(true);
199
+ try {
200
+ await setModuleOrder({ data: { moduleIds: list.map((m) => m.id) } });
201
+ setOrderSavedAt(Date.now());
202
+ // Zijbalk van de shell direct laten herordenen.
203
+ notifyModuleOrderChanged();
204
+ }
205
+ catch (error) {
206
+ setOrderModules(previous);
207
+ toast.error(getErrorMessage(error, "Opslaan van de volgorde mislukt"));
208
+ }
209
+ finally {
210
+ setOrderSaving(false);
211
+ }
212
+ }, []);
213
+ const moveModule = (index, direction) => {
214
+ const previous = orderModules;
215
+ const next = [...previous];
216
+ const target = index + direction;
217
+ if (target < 0 || target >= next.length)
218
+ return;
219
+ const [item] = next.splice(index, 1);
220
+ next.splice(target, 0, item);
221
+ setOrderModules(next);
222
+ // Realtime opslaan: klikken worden kort gebundeld.
223
+ if (orderSaveTimer.current)
224
+ clearTimeout(orderSaveTimer.current);
225
+ orderSaveTimer.current = setTimeout(() => {
226
+ orderSaveTimer.current = null;
227
+ void persistOrder(next, previous);
228
+ }, 400);
229
+ };
230
+ useEffect(() => {
231
+ return () => {
232
+ if (orderSaveTimer.current)
233
+ clearTimeout(orderSaveTimer.current);
234
+ };
235
+ }, []);
147
236
  useEffect(() => {
148
237
  if (!canAccess)
149
238
  return;
@@ -164,6 +253,7 @@ export function AdminForInstallatieTechniek({ modules, shellId } = {}) {
164
253
  // Module-machtigingen zijn strikt admin-only.
165
254
  if (isAdmin) {
166
255
  loadPermissions();
256
+ loadModuleOrder();
167
257
  }
168
258
  else {
169
259
  setPermLoading(false);
@@ -345,14 +435,14 @@ export function AdminForInstallatieTechniek({ modules, shellId } = {}) {
345
435
  if (!canAccess) {
346
436
  return (_jsx("main", { className: "p-6", children: _jsx(Card, { children: _jsx(CardContent, { className: "pt-6", children: _jsx("p", { className: "text-muted-foreground", children: "Je hebt geen toegang tot deze pagina." }) }) }) }));
347
437
  }
348
- return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsxs(Tabs, { defaultValue: "users", children: [_jsxs(TabsList, { children: [_jsx(TabsTrigger, { value: "users", children: "Gebruikers & rollen" }), isAdmin && _jsx(TabsTrigger, { value: "permissions", children: "Module-machtigingen" }), _jsx(TabsTrigger, { value: "user-permissions", children: "Rechten per gebruiker" })] }), _jsx(TabsContent, { value: "users", className: "mt-4", children: _jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between gap-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Shield, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Gebruikersrollen beheren" })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Button, { variant: "outline", onClick: () => setTestOpen(true), size: "sm", children: [_jsx(FlaskConical, { className: "h-4 w-4 mr-2" }), "Testaccount aanmaken"] }), _jsxs(Button, { onClick: () => setInviteOpen(true), size: "sm", children: [_jsx(UserPlus, { className: "h-4 w-4 mr-2" }), "Account aanmaken"] })] })] }), _jsxs(CardContent, { className: "space-y-6", children: [_jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-end", children: [_jsxs("div", { className: "flex-1 space-y-2", children: [_jsx("label", { className: "text-sm font-medium", children: "Gebruiker" }), _jsxs(Select, { value: selectedUser, onValueChange: setSelectedUser, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Selecteer een gebruiker" }) }), _jsx(SelectContent, { children: users.map((user) => (_jsxs(SelectItem, { value: user.id, children: [user.displayName, " (", user.email, ")"] }, user.id))) })] })] }), _jsxs("div", { className: "w-full sm:w-48 space-y-2", children: [_jsx("label", { className: "text-sm font-medium", children: "Rol" }), _jsxs(Select, { value: selectedRole, onValueChange: (value) => setSelectedRole(value), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: assignableRoles.map((role) => (_jsx(SelectItem, { value: role, children: role }, role))) })] })] }), _jsx(Button, { onClick: handleAssign, disabled: !selectedUser || submitting || loading || selectedUserHasRole, children: submitting ? "Toekennen..." : selectedUserHasRole ? "Rol bestaat al" : "Rol toekennen" })] }), _jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-end", children: [_jsxs("div", { className: "flex-1 space-y-2", children: [_jsx(Label, { className: "text-sm font-medium", children: "Zoeken" }), _jsx(Input, { placeholder: "Zoek op naam of e-mail", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value) })] }), _jsxs("div", { className: "w-full sm:w-48 space-y-2", children: [_jsx(Label, { className: "text-sm font-medium", children: "Filter op rol" }), _jsxs(Select, { value: roleFilter, onValueChange: (value) => setRoleFilter(value), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "Alle rollen" }), availableRoles.map((role) => (_jsx(SelectItem, { value: role, children: role }, role))), _jsx(SelectItem, { value: "none", children: "Geen rollen" })] })] })] }), _jsxs("div", { className: "w-full sm:w-48 space-y-2", children: [_jsx(Label, { className: "text-sm font-medium", children: "Sorteren" }), _jsxs(Select, { value: sortBy, onValueChange: (value) => setSortBy(value), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "name-asc", children: "Naam A-Z" }), _jsx(SelectItem, { value: "name-desc", children: "Naam Z-A" }), _jsx(SelectItem, { value: "role-asc", children: "Rol A-Z" }), _jsx(SelectItem, { value: "role-desc", children: "Rol Z-A" })] })] })] })] }), loading ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Laden..." })) : filteredUsers.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: hasActiveFilters ? "Geen gebruikers gevonden met de huidige filters." : "Geen gebruikers gevonden." })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Gebruiker" }), _jsx(TableHead, { children: "E-mail" }), _jsx(TableHead, { children: "Rollen" }), _jsx(TableHead, { className: "w-24" })] }) }), _jsx(TableBody, { children: filteredUsers.map((user) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { children: user.displayName }), !user.emailConfirmed && (_jsx(Badge, { variant: "outline", className: "text-xs", children: "Wacht op verificatie" })), user.isTestAccount && (_jsx(Badge, { variant: "secondary", className: "text-xs", children: "Test" }))] }) }), _jsx(TableCell, { children: user.email }), _jsx(TableCell, { children: _jsx("div", { className: "flex flex-wrap gap-2", children: user.roles.length === 0 ? (_jsx("span", { className: "text-sm text-muted-foreground", children: "Geen rollen" })) : (user.roles.map((role) => (_jsxs(Badge, { variant: "secondary", className: "gap-1", children: [role, !(role === "admin" && !isAdmin) && (_jsx("button", { onClick: () => handleRemove(user.id, role), className: "ml-1 rounded-sm hover:text-destructive", "aria-label": `Verwijder rol ${role}`, children: _jsx(Trash2, { className: "h-3 w-3" }) }))] }, `${user.id}-${role}`)))) }) }), _jsx(TableCell, { className: "text-right", children: _jsx(Button, { variant: "ghost", size: "sm", className: "text-destructive hover:text-destructive hover:bg-destructive/10", disabled: user.id === currentUserId || !canManageUser(user), title: user.id === currentUserId
438
+ return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsxs(Tabs, { defaultValue: "users", children: [_jsxs(TabsList, { children: [_jsx(TabsTrigger, { value: "users", children: "Gebruikers & rollen" }), isAdmin && _jsx(TabsTrigger, { value: "permissions", children: "Module-machtigingen" }), _jsx(TabsTrigger, { value: "user-permissions", children: "Rechten per gebruiker" }), isAdmin && _jsx(TabsTrigger, { value: "order", children: "Zijbalk volgorde" })] }), _jsx(TabsContent, { value: "users", className: "mt-4", children: _jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between gap-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Shield, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Gebruikersrollen beheren" })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Button, { variant: "outline", onClick: () => setTestOpen(true), size: "sm", children: [_jsx(FlaskConical, { className: "h-4 w-4 mr-2" }), "Testaccount aanmaken"] }), _jsxs(Button, { onClick: () => setInviteOpen(true), size: "sm", children: [_jsx(UserPlus, { className: "h-4 w-4 mr-2" }), "Account aanmaken"] })] })] }), _jsxs(CardContent, { className: "space-y-6", children: [_jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-end", children: [_jsxs("div", { className: "flex-1 space-y-2", children: [_jsx("label", { className: "text-sm font-medium", children: "Gebruiker" }), _jsxs(Select, { value: selectedUser, onValueChange: setSelectedUser, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Selecteer een gebruiker" }) }), _jsx(SelectContent, { children: users.map((user) => (_jsxs(SelectItem, { value: user.id, children: [user.displayName, " (", user.email, ")"] }, user.id))) })] })] }), _jsxs("div", { className: "w-full sm:w-48 space-y-2", children: [_jsx("label", { className: "text-sm font-medium", children: "Rol" }), _jsxs(Select, { value: selectedRole, onValueChange: (value) => setSelectedRole(value), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: assignableRoles.map((role) => (_jsx(SelectItem, { value: role, children: role }, role))) })] })] }), _jsx(Button, { onClick: handleAssign, disabled: !selectedUser || submitting || loading || selectedUserHasRole, children: submitting ? "Toekennen..." : selectedUserHasRole ? "Rol bestaat al" : "Rol toekennen" })] }), _jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-end", children: [_jsxs("div", { className: "flex-1 space-y-2", children: [_jsx(Label, { className: "text-sm font-medium", children: "Zoeken" }), _jsx(Input, { placeholder: "Zoek op naam of e-mail", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value) })] }), _jsxs("div", { className: "w-full sm:w-48 space-y-2", children: [_jsx(Label, { className: "text-sm font-medium", children: "Filter op rol" }), _jsxs(Select, { value: roleFilter, onValueChange: (value) => setRoleFilter(value), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "Alle rollen" }), availableRoles.map((role) => (_jsx(SelectItem, { value: role, children: role }, role))), _jsx(SelectItem, { value: "none", children: "Geen rollen" })] })] })] }), _jsxs("div", { className: "w-full sm:w-48 space-y-2", children: [_jsx(Label, { className: "text-sm font-medium", children: "Sorteren" }), _jsxs(Select, { value: sortBy, onValueChange: (value) => setSortBy(value), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "name-asc", children: "Naam A-Z" }), _jsx(SelectItem, { value: "name-desc", children: "Naam Z-A" }), _jsx(SelectItem, { value: "role-asc", children: "Rol A-Z" }), _jsx(SelectItem, { value: "role-desc", children: "Rol Z-A" })] })] })] })] }), loading ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Laden..." })) : filteredUsers.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: hasActiveFilters ? "Geen gebruikers gevonden met de huidige filters." : "Geen gebruikers gevonden." })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Gebruiker" }), _jsx(TableHead, { children: "E-mail" }), _jsx(TableHead, { children: "Rollen" }), _jsx(TableHead, { className: "w-24" })] }) }), _jsx(TableBody, { children: filteredUsers.map((user) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { children: user.displayName }), !user.emailConfirmed && (_jsx(Badge, { variant: "outline", className: "text-xs", children: "Wacht op verificatie" })), user.isTestAccount && (_jsx(Badge, { variant: "secondary", className: "text-xs", children: "Test" }))] }) }), _jsx(TableCell, { children: user.email }), _jsx(TableCell, { children: _jsx("div", { className: "flex flex-wrap gap-2", children: user.roles.length === 0 ? (_jsx("span", { className: "text-sm text-muted-foreground", children: "Geen rollen" })) : (user.roles.map((role) => (_jsxs(Badge, { variant: "secondary", className: "gap-1", children: [role, !(role === "admin" && !isAdmin) && (_jsx("button", { onClick: () => handleRemove(user.id, role), className: "ml-1 rounded-sm hover:text-destructive", "aria-label": `Verwijder rol ${role}`, children: _jsx(Trash2, { className: "h-3 w-3" }) }))] }, `${user.id}-${role}`)))) }) }), _jsx(TableCell, { className: "text-right", children: _jsx(Button, { variant: "ghost", size: "sm", className: "text-destructive hover:text-destructive hover:bg-destructive/10", disabled: user.id === currentUserId || !canManageUser(user), title: user.id === currentUserId
349
439
  ? "Je kunt je eigen account niet verwijderen."
350
440
  : !canManageUser(user)
351
441
  ? "Alleen een admin kan een adminaccount verwijderen."
352
442
  : "Verwijder account", onClick: () => setDeleteTarget(user), "aria-label": `Verwijder account ${user.email}`, children: _jsx(UserX, { className: "h-4 w-4" }) }) })] }, user.id))) })] }))] })] }) }), isAdmin && (_jsx(TabsContent, { value: "permissions", className: "mt-4", children: _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Shield, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Module-machtigingen per rol" })] }) }), _jsx(CardContent, { children: permLoading ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Laden..." })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Module" }), permRoles.map((role) => (_jsx(TableHead, { className: "capitalize", children: role }, role)))] }) }), _jsx(TableBody, { children: permModules.map((mod) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: mod.name }), permRoles.map((role) => {
353
443
  const value = permMatrix[permKey(role, mod.id)] ?? "none";
354
444
  return (_jsx(TableCell, { children: _jsxs(Select, { value: value, onValueChange: (v) => handlePermChange(role, mod.id, v), children: [_jsx(SelectTrigger, { className: "w-40", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: Object.keys(PERMISSION_LABELS).map((p) => (_jsx(SelectItem, { value: p, children: PERMISSION_LABELS[p] }, p))) })] }) }, role));
355
- })] }, mod.id))) })] })) })] }) })), _jsx(TabsContent, { value: "user-permissions", className: "mt-4", children: _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Shield, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Rechten per gebruiker" })] }) }), _jsxs(CardContent, { className: "space-y-6", children: [_jsxs("div", { className: "w-full sm:w-96 space-y-2", children: [_jsx(Label, { className: "text-sm font-medium", children: "Gebruiker" }), _jsxs(Select, { value: permUserId, onValueChange: setPermUserId, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Selecteer een gebruiker" }) }), _jsx(SelectContent, { children: users.map((u) => (_jsxs(SelectItem, { value: u.id, children: [u.displayName, " (", u.email, ")"] }, u.id))) })] })] }), !permUserId ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Kies een gebruiker om persoonlijke uitzonderingen in te stellen. \"Volg rol\" gebruikt het recht dat via de rollen van deze gebruiker geldt." })) : userPermLoading ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Laden..." })) : userPermModules.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Er zijn nog geen modules geregistreerd voor deze shell." })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Module" }), _jsx(TableHead, { className: "w-56", children: "Recht" })] }) }), _jsx(TableBody, { children: userPermModules.map((mod) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: mod.name }), _jsx(TableCell, { children: _jsxs(Select, { value: userPermMap[mod.id] ?? "inherit", onValueChange: (v) => handleUserPermChange(mod.id, v), children: [_jsx(SelectTrigger, { className: "w-48", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: Object.keys(USER_PERMISSION_LABELS).map((p) => (_jsx(SelectItem, { value: p, children: USER_PERMISSION_LABELS[p] }, p))) })] }) })] }, mod.id))) })] }))] })] }) })] }), _jsx(Dialog, { open: inviteOpen, onOpenChange: setInviteOpen, children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Nieuw account aanmaken" }), _jsx(DialogDescription, { children: "De gebruiker ontvangt een uitnodigingsmail en kiest zelf een wachtwoord." })] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "invite-name", children: "Naam" }), _jsx(Input, { id: "invite-name", value: inviteName, onChange: (e) => setInviteName(e.target.value), placeholder: "Jan Jansen", disabled: inviting })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "invite-email", children: "E-mail" }), _jsx(Input, { id: "invite-email", type: "email", value: inviteEmail, onChange: (e) => setInviteEmail(e.target.value), placeholder: "jan@voorbeeld.nl", disabled: inviting })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Rol" }), _jsxs(Select, { value: inviteRole, onValueChange: (v) => setInviteRole(v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: assignableRoles.map((r) => (_jsx(SelectItem, { value: r, children: r }, r))) })] })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => setInviteOpen(false), disabled: inviting, children: "Annuleren" }), _jsx(Button, { onClick: handleInvite, disabled: inviting || !inviteEmail.trim() || !inviteName.trim(), children: inviting ? "Versturen..." : "Uitnodiging versturen" })] })] }) }), _jsx(Dialog, { open: testOpen, onOpenChange: (open) => !testCreating && setTestOpen(open), children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Testaccount aanmaken" }), _jsx(DialogDescription, { children: "Maakt direct een geverifieerd account met een automatisch wachtwoord \u2014 geen e-mail nodig." })] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "test-name", children: "Naam" }), _jsx(Input, { id: "test-name", value: testName, onChange: (e) => setTestName(e.target.value), placeholder: "Test Monteur", disabled: testCreating })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Rol" }), _jsxs(Select, { value: testRole, onValueChange: (v) => setTestRole(v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: assignableRoles.map((r) => (_jsx(SelectItem, { value: r, children: r }, r))) })] })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => setTestOpen(false), disabled: testCreating, children: "Annuleren" }), _jsx(Button, { onClick: handleCreateTestUser, disabled: testCreating || !testName.trim(), children: testCreating ? "Aanmaken..." : "Aanmaken" })] })] }) }), _jsx(Dialog, { open: testCredentials !== null, onOpenChange: (open) => !open && setTestCredentials(null), children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Testaccount aangemaakt" }), _jsx(DialogDescription, { children: "Kopieer de inloggegevens nu \u2014 het wachtwoord wordt maar \u00E9\u00E9n keer getoond." })] }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "E-mail" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { readOnly: true, value: testCredentials?.email ?? "" }), _jsx(Button, { variant: "outline", size: "icon", onClick: () => testCredentials && copyToClipboard(testCredentials.email, "E-mail"), "aria-label": "Kopieer e-mail", children: _jsx(Copy, { className: "h-4 w-4" }) })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Wachtwoord" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { readOnly: true, value: testCredentials?.password ?? "", className: "font-mono" }), _jsx(Button, { variant: "outline", size: "icon", onClick: () => testCredentials && copyToClipboard(testCredentials.password, "Wachtwoord"), "aria-label": "Kopieer wachtwoord", children: _jsx(Copy, { className: "h-4 w-4" }) })] })] })] }), _jsx(DialogFooter, { children: _jsx(Button, { onClick: () => setTestCredentials(null), children: "Sluiten" }) })] }) }), _jsx(AlertDialog, { open: deleteTarget !== null, onOpenChange: (open) => !open && !deleting && setDeleteTarget(null), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Account verwijderen?" }), _jsxs(AlertDialogDescription, { children: ["Weet je zeker dat je", " ", _jsx("strong", { children: deleteTarget?.displayName || deleteTarget?.email }), " ", "wilt verwijderen? Deze actie kan niet ongedaan worden gemaakt."] })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: deleting, children: "Annuleren" }), _jsx(AlertDialogAction, { onClick: (e) => {
445
+ })] }, mod.id))) })] })) })] }) })), _jsx(TabsContent, { value: "user-permissions", className: "mt-4", children: _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Shield, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Rechten per gebruiker" })] }) }), _jsxs(CardContent, { className: "space-y-6", children: [_jsxs("div", { className: "w-full sm:w-96 space-y-2", children: [_jsx(Label, { className: "text-sm font-medium", children: "Gebruiker" }), _jsxs(Select, { value: permUserId, onValueChange: setPermUserId, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Selecteer een gebruiker" }) }), _jsx(SelectContent, { children: users.map((u) => (_jsxs(SelectItem, { value: u.id, children: [u.displayName, " (", u.email, ")"] }, u.id))) })] })] }), !permUserId ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Kies een gebruiker om persoonlijke uitzonderingen in te stellen. \"Volg rol\" gebruikt het recht dat via de rollen van deze gebruiker geldt." })) : userPermLoading ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Laden..." })) : userPermModules.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Er zijn nog geen modules geregistreerd voor deze shell." })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Module" }), _jsx(TableHead, { className: "w-56", children: "Recht" })] }) }), _jsx(TableBody, { children: userPermModules.map((mod) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: mod.name }), _jsx(TableCell, { children: _jsxs(Select, { value: userPermMap[mod.id] ?? "inherit", onValueChange: (v) => handleUserPermChange(mod.id, v), children: [_jsx(SelectTrigger, { className: "w-48", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: Object.keys(USER_PERMISSION_LABELS).map((p) => (_jsx(SelectItem, { value: p, children: USER_PERMISSION_LABELS[p] }, p))) })] }) })] }, mod.id))) })] }))] })] }) }), isAdmin && (_jsx(TabsContent, { value: "order", className: "mt-4", children: _jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between gap-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Shield, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Volgorde van modules in de zijbalk" })] }), _jsx("span", { className: "text-sm text-muted-foreground", children: orderSaving ? "Opslaan..." : orderSavedAt ? "Opgeslagen" : "" })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Zet de modules in de gewenste volgorde. Wijzigingen worden direct opgeslagen en meteen toegepast in de zijbalk. De startvolgorde is de volgorde waarin deze shell zijn modules aanlevert; nieuwe modules komen daarin vanzelf op hun plek." }), orderLoading ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Laden..." })) : orderModules.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Er zijn nog geen modules geregistreerd voor deze shell." })) : (_jsx("ul", { className: "divide-y rounded-md border", children: orderModules.map((mod, index) => (_jsxs("li", { className: "flex items-center justify-between gap-4 px-4 py-3", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("span", { className: "w-6 text-sm text-muted-foreground", children: [index + 1, "."] }), _jsx("span", { className: "font-medium", children: mod.name })] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx(Button, { variant: "outline", size: "icon", "aria-label": `${mod.name} omhoog`, disabled: index === 0, onClick: () => moveModule(index, -1), children: _jsx(ArrowUp, { className: "h-4 w-4" }) }), _jsx(Button, { variant: "outline", size: "icon", "aria-label": `${mod.name} omlaag`, disabled: index === orderModules.length - 1, onClick: () => moveModule(index, 1), children: _jsx(ArrowDown, { className: "h-4 w-4" }) })] })] }, mod.id))) }))] })] }) }))] }), _jsx(Dialog, { open: inviteOpen, onOpenChange: setInviteOpen, children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Nieuw account aanmaken" }), _jsx(DialogDescription, { children: "De gebruiker ontvangt een uitnodigingsmail en kiest zelf een wachtwoord." })] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "invite-name", children: "Naam" }), _jsx(Input, { id: "invite-name", value: inviteName, onChange: (e) => setInviteName(e.target.value), placeholder: "Jan Jansen", disabled: inviting })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "invite-email", children: "E-mail" }), _jsx(Input, { id: "invite-email", type: "email", value: inviteEmail, onChange: (e) => setInviteEmail(e.target.value), placeholder: "jan@voorbeeld.nl", disabled: inviting })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Rol" }), _jsxs(Select, { value: inviteRole, onValueChange: (v) => setInviteRole(v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: assignableRoles.map((r) => (_jsx(SelectItem, { value: r, children: r }, r))) })] })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => setInviteOpen(false), disabled: inviting, children: "Annuleren" }), _jsx(Button, { onClick: handleInvite, disabled: inviting || !inviteEmail.trim() || !inviteName.trim(), children: inviting ? "Versturen..." : "Uitnodiging versturen" })] })] }) }), _jsx(Dialog, { open: testOpen, onOpenChange: (open) => !testCreating && setTestOpen(open), children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Testaccount aanmaken" }), _jsx(DialogDescription, { children: "Maakt direct een geverifieerd account met een automatisch wachtwoord \u2014 geen e-mail nodig." })] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "test-name", children: "Naam" }), _jsx(Input, { id: "test-name", value: testName, onChange: (e) => setTestName(e.target.value), placeholder: "Test Monteur", disabled: testCreating })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Rol" }), _jsxs(Select, { value: testRole, onValueChange: (v) => setTestRole(v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: assignableRoles.map((r) => (_jsx(SelectItem, { value: r, children: r }, r))) })] })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => setTestOpen(false), disabled: testCreating, children: "Annuleren" }), _jsx(Button, { onClick: handleCreateTestUser, disabled: testCreating || !testName.trim(), children: testCreating ? "Aanmaken..." : "Aanmaken" })] })] }) }), _jsx(Dialog, { open: testCredentials !== null, onOpenChange: (open) => !open && setTestCredentials(null), children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Testaccount aangemaakt" }), _jsx(DialogDescription, { children: "Kopieer de inloggegevens nu \u2014 het wachtwoord wordt maar \u00E9\u00E9n keer getoond." })] }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "E-mail" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { readOnly: true, value: testCredentials?.email ?? "" }), _jsx(Button, { variant: "outline", size: "icon", onClick: () => testCredentials && copyToClipboard(testCredentials.email, "E-mail"), "aria-label": "Kopieer e-mail", children: _jsx(Copy, { className: "h-4 w-4" }) })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Wachtwoord" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { readOnly: true, value: testCredentials?.password ?? "", className: "font-mono" }), _jsx(Button, { variant: "outline", size: "icon", onClick: () => testCredentials && copyToClipboard(testCredentials.password, "Wachtwoord"), "aria-label": "Kopieer wachtwoord", children: _jsx(Copy, { className: "h-4 w-4" }) })] })] })] }), _jsx(DialogFooter, { children: _jsx(Button, { onClick: () => setTestCredentials(null), children: "Sluiten" }) })] }) }), _jsx(AlertDialog, { open: deleteTarget !== null, onOpenChange: (open) => !open && !deleting && setDeleteTarget(null), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Account verwijderen?" }), _jsxs(AlertDialogDescription, { children: ["Weet je zeker dat je", " ", _jsx("strong", { children: deleteTarget?.displayName || deleteTarget?.email }), " ", "wilt verwijderen? Deze actie kan niet ongedaan worden gemaakt."] })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: deleting, children: "Annuleren" }), _jsx(AlertDialogAction, { onClick: (e) => {
356
446
  e.preventDefault();
357
447
  handleDelete();
358
448
  }, disabled: deleting, className: "bg-destructive text-destructive-foreground hover:bg-destructive/90", children: deleting ? "Verwijderen..." : "Verwijderen" })] })] }) })] }));
@@ -0,0 +1,30 @@
1
+ export interface ModuleOrderEntry {
2
+ id: string;
3
+ name: string;
4
+ sortOrder: number | null;
5
+ }
6
+ interface OrderableModule {
7
+ id: string;
8
+ name?: string;
9
+ }
10
+ /** Laat elke actieve `useModuleOrder()` de volgorde direct opnieuw ophalen. */
11
+ export declare function notifyModuleOrderChanged(): void;
12
+ /**
13
+ * Sorteert een modulelijst volgens de opgeslagen volgorde.
14
+ * Modules zonder opgeslagen positie behouden hun oorspronkelijke plek
15
+ * (de bronvolgorde van de shell) — geen alfabetische herschikking.
16
+ */
17
+ export declare function sortModulesByOrder<T extends OrderableModule>(modules: T[], order: ModuleOrderEntry[]): T[];
18
+ /**
19
+ * Haalt de opgeslagen zijbalk-volgorde op en levert een sorteerfunctie
20
+ * die shells op hun modulelijst kunnen toepassen. Werkt realtime bij
21
+ * zodra de adminpagina een nieuwe volgorde opslaat.
22
+ */
23
+ export declare function useModuleOrder(): {
24
+ order: ModuleOrderEntry[];
25
+ loading: boolean;
26
+ sortModules: <T extends OrderableModule>(modules: T[]) => T[];
27
+ reload: () => Promise<void>;
28
+ };
29
+ export {};
30
+ //# sourceMappingURL=useModuleOrder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useModuleOrder.d.ts","sourceRoot":"","sources":["../../src/hooks/useModuleOrder.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,UAAU,eAAe;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAID,+EAA+E;AAC/E,wBAAgB,wBAAwB,SAGvC;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,eAAe,EAC1D,OAAO,EAAE,CAAC,EAAE,EACZ,KAAK,EAAE,gBAAgB,EAAE,GACxB,CAAC,EAAE,CAyBL;AAED;;;;GAIG;AACH,wBAAgB,cAAc;;;kBA6CzB,CAAC,SAAS,eAAe,WAAW,CAAC,EAAE;;EAK3C"}
@@ -0,0 +1,99 @@
1
+ import { useCallback, useEffect, useState } from "react";
2
+ import { getModuleOrder } from "../lib/admin.functions";
3
+ const MODULE_ORDER_EVENT = "flowselections:module-order-changed";
4
+ /** Laat elke actieve `useModuleOrder()` de volgorde direct opnieuw ophalen. */
5
+ export function notifyModuleOrderChanged() {
6
+ if (typeof window === "undefined")
7
+ return;
8
+ window.dispatchEvent(new CustomEvent(MODULE_ORDER_EVENT));
9
+ }
10
+ /**
11
+ * Sorteert een modulelijst volgens de opgeslagen volgorde.
12
+ * Modules zonder opgeslagen positie behouden hun oorspronkelijke plek
13
+ * (de bronvolgorde van de shell) — geen alfabetische herschikking.
14
+ */
15
+ export function sortModulesByOrder(modules, order) {
16
+ const safeModules = Array.isArray(modules) ? modules : [];
17
+ const safeOrder = Array.isArray(order) ? order : [];
18
+ if (safeOrder.length === 0)
19
+ return safeModules;
20
+ const positions = new Map();
21
+ safeOrder.forEach((entry) => {
22
+ if (entry?.id && typeof entry.sortOrder === "number") {
23
+ positions.set(entry.id, entry.sortOrder);
24
+ }
25
+ });
26
+ if (positions.size === 0)
27
+ return safeModules;
28
+ return safeModules
29
+ .map((mod, index) => ({ mod, index }))
30
+ .sort((a, b) => {
31
+ const ao = positions.get(a.mod.id);
32
+ const bo = positions.get(b.mod.id);
33
+ if (ao !== undefined && bo !== undefined)
34
+ return ao - bo;
35
+ // Zonder positie: originele volgorde behouden, ná gepositioneerde modules.
36
+ if (ao !== undefined)
37
+ return -1;
38
+ if (bo !== undefined)
39
+ return 1;
40
+ return a.index - b.index;
41
+ })
42
+ .map((entry) => entry.mod);
43
+ }
44
+ /**
45
+ * Haalt de opgeslagen zijbalk-volgorde op en levert een sorteerfunctie
46
+ * die shells op hun modulelijst kunnen toepassen. Werkt realtime bij
47
+ * zodra de adminpagina een nieuwe volgorde opslaat.
48
+ */
49
+ export function useModuleOrder() {
50
+ const [order, setOrder] = useState([]);
51
+ const [loading, setLoading] = useState(true);
52
+ const reload = useCallback(async () => {
53
+ try {
54
+ const result = await getModuleOrder();
55
+ setOrder(Array.isArray(result?.modules) ? result.modules : []);
56
+ }
57
+ catch {
58
+ setOrder([]);
59
+ }
60
+ finally {
61
+ setLoading(false);
62
+ }
63
+ }, []);
64
+ useEffect(() => {
65
+ let cancelled = false;
66
+ const load = async () => {
67
+ try {
68
+ const result = await getModuleOrder();
69
+ if (cancelled)
70
+ return;
71
+ setOrder(Array.isArray(result?.modules) ? result.modules : []);
72
+ }
73
+ catch {
74
+ if (!cancelled)
75
+ setOrder([]);
76
+ }
77
+ finally {
78
+ if (!cancelled)
79
+ setLoading(false);
80
+ }
81
+ };
82
+ load();
83
+ const onChanged = () => {
84
+ if (!cancelled)
85
+ load();
86
+ };
87
+ if (typeof window !== "undefined") {
88
+ window.addEventListener(MODULE_ORDER_EVENT, onChanged);
89
+ }
90
+ return () => {
91
+ cancelled = true;
92
+ if (typeof window !== "undefined") {
93
+ window.removeEventListener(MODULE_ORDER_EVENT, onChanged);
94
+ }
95
+ };
96
+ }, []);
97
+ const sortModules = useCallback((modules) => sortModulesByOrder(modules, order), [order]);
98
+ return { order, loading, sortModules, reload };
99
+ }
@@ -6,5 +6,7 @@ export { useModulePermission, useMyModulePermissions } from './hooks/useModulePe
6
6
  export type { ModulePermission } from './hooks/useModulePermission';
7
7
  export { useDiscoveredModules } from './lib/moduleDiscovery';
8
8
  export type { DiscoveredModule } from './lib/moduleDiscovery';
9
+ export { useModuleOrder, sortModulesByOrder, notifyModuleOrderChanged } from './hooks/useModuleOrder';
10
+ export type { ModuleOrderEntry } from './hooks/useModuleOrder';
9
11
  export declare const adminInstallatietechniekModule: FlowModule;
10
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,2BAA2B,EAAE,MAAM,0CAA0C,CAAC;AACvF,YAAY,EAAE,gCAAgC,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AACnH,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAC1F,YAAY,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAG9D,eAAO,MAAM,8BAA8B,EAAE,UAY5C,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,2BAA2B,EAAE,MAAM,0CAA0C,CAAC;AACvF,YAAY,EAAE,gCAAgC,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AACnH,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAC1F,YAAY,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AACtG,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAI/D,eAAO,MAAM,8BAA8B,EAAE,UAY5C,CAAC"}
package/dist-lib/index.js CHANGED
@@ -6,6 +6,7 @@ export * from './_core-safelist';
6
6
  export { AdminForInstallatieTechniek } from './components/AdminForInstallatieTechniek';
7
7
  export { useModulePermission, useMyModulePermissions } from './hooks/useModulePermission';
8
8
  export { useDiscoveredModules } from './lib/moduleDiscovery';
9
+ export { useModuleOrder, sortModulesByOrder, notifyModuleOrderChanged } from './hooks/useModuleOrder';
9
10
  export const adminInstallatietechniekModule = {
10
11
  id: "admin-installatietechniek",
11
12
  name: "Admin Installatietechniek",
@@ -9886,6 +9886,7 @@ export declare const requireSupabaseAuth: import("@tanstack/start-client-core").
9886
9886
  module_id: string;
9887
9887
  name: string;
9888
9888
  shell_id: string | null;
9889
+ sort_order: number | null;
9889
9890
  };
9890
9891
  Insert: {
9891
9892
  created_at?: string;
@@ -9894,6 +9895,7 @@ export declare const requireSupabaseAuth: import("@tanstack/start-client-core").
9894
9895
  module_id: string;
9895
9896
  name: string;
9896
9897
  shell_id?: string | null;
9898
+ sort_order?: number | null;
9897
9899
  };
9898
9900
  Update: {
9899
9901
  created_at?: string;
@@ -9902,6 +9904,7 @@ export declare const requireSupabaseAuth: import("@tanstack/start-client-core").
9902
9904
  module_id?: string;
9903
9905
  name?: string;
9904
9906
  shell_id?: string | null;
9907
+ sort_order?: number | null;
9905
9908
  };
9906
9909
  Relationships: [];
9907
9910
  };