@flowselections/core 1.0.20 → 1.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/redirect.d.ts +23 -0
- package/dist/auth/redirect.d.ts.map +1 -0
- package/dist/auth/redirect.js +47 -0
- package/dist/components/layout/AuthenticatedLayout.d.ts +20 -0
- package/dist/components/layout/AuthenticatedLayout.d.ts.map +1 -0
- package/dist/components/layout/AuthenticatedLayout.js +15 -0
- package/dist/components/layout/HandoverPage.d.ts.map +1 -1
- package/dist/components/layout/HandoverPage.js +4 -9
- package/dist/components/layout/Header.js +1 -1
- package/dist/components/layout/InstellingenPage.js +1 -1
- package/dist/components/layout/LoginPage.d.ts.map +1 -1
- package/dist/components/layout/LoginPage.js +6 -6
- package/dist/components/layout/Sidebar.d.ts +13 -3
- package/dist/components/layout/Sidebar.d.ts.map +1 -1
- package/dist/components/layout/Sidebar.js +28 -11
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/package.json +2 -2
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accepteert alleen een same-site pad: begint met "/", niet met "//",
|
|
3
|
+
* geen backslash, geen schema en geen stuurtekens. Anders `fallback`.
|
|
4
|
+
*/
|
|
5
|
+
export declare function safeRedirectPath(value: unknown, fallback: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Leest `?redirect=` direct uit de adresbalk, zodat het ook werkt als de
|
|
8
|
+
* login-route van de shell geen validateSearch heeft.
|
|
9
|
+
*/
|
|
10
|
+
export declare function readRedirectParam(fallback: string): string;
|
|
11
|
+
export interface RequireAuthArgs {
|
|
12
|
+
context: unknown;
|
|
13
|
+
location: {
|
|
14
|
+
href: string;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Guard voor de `_authenticated`-route (gebruik in beforeLoad, met ssr: false).
|
|
19
|
+
* Zolang auth laadt: geen redirect; de root invalideert de router zodra auth
|
|
20
|
+
* geladen is, waarna deze guard opnieuw draait.
|
|
21
|
+
*/
|
|
22
|
+
export declare function requireAuth({ context, location }: RequireAuthArgs, loginPath?: string): void;
|
|
23
|
+
//# sourceMappingURL=redirect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redirect.d.ts","sourceRoot":"","sources":["../../src/core/auth/redirect.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAezE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,eAAe,EAAE,SAAS,SAAW,GAAG,IAAI,CAI9F"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { redirect } from "@tanstack/react-router";
|
|
2
|
+
/**
|
|
3
|
+
* Accepteert alleen een same-site pad: begint met "/", niet met "//",
|
|
4
|
+
* geen backslash, geen schema en geen stuurtekens. Anders `fallback`.
|
|
5
|
+
*/
|
|
6
|
+
export function safeRedirectPath(value, fallback) {
|
|
7
|
+
if (typeof value !== "string" || value.length === 0)
|
|
8
|
+
return fallback;
|
|
9
|
+
if (!value.startsWith("/") || value.startsWith("//"))
|
|
10
|
+
return fallback;
|
|
11
|
+
if (value.includes("\\"))
|
|
12
|
+
return fallback;
|
|
13
|
+
// eslint-disable-next-line no-control-regex
|
|
14
|
+
if (/[\u0000-\u001f\u007f]/.test(value))
|
|
15
|
+
return fallback;
|
|
16
|
+
// Schema vóór het eerste "/", "?" of "#" (bijv. "/javascript:..." is ok, "javascript:" niet;
|
|
17
|
+
// dat laatste valt al af op de "/"-regel, dit vangt varianten als "/\t//").
|
|
18
|
+
try {
|
|
19
|
+
const url = new URL(value, "http://same.invalid");
|
|
20
|
+
if (url.origin !== "http://same.invalid")
|
|
21
|
+
return fallback;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return fallback;
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Leest `?redirect=` direct uit de adresbalk, zodat het ook werkt als de
|
|
30
|
+
* login-route van de shell geen validateSearch heeft.
|
|
31
|
+
*/
|
|
32
|
+
export function readRedirectParam(fallback) {
|
|
33
|
+
if (typeof window === "undefined")
|
|
34
|
+
return fallback;
|
|
35
|
+
return safeRedirectPath(new URLSearchParams(window.location.search).get("redirect"), fallback);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Guard voor de `_authenticated`-route (gebruik in beforeLoad, met ssr: false).
|
|
39
|
+
* Zolang auth laadt: geen redirect; de root invalideert de router zodra auth
|
|
40
|
+
* geladen is, waarna deze guard opnieuw draait.
|
|
41
|
+
*/
|
|
42
|
+
export function requireAuth({ context, location }, loginPath = "/login") {
|
|
43
|
+
const auth = context?.auth;
|
|
44
|
+
if (!auth || auth.loading || auth.isAuthenticated)
|
|
45
|
+
return;
|
|
46
|
+
throw redirect({ to: loginPath, search: { redirect: location.href } });
|
|
47
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { SidebarProps } from "./Sidebar";
|
|
3
|
+
/**
|
|
4
|
+
* Alle Sidebar-props (auth, modules, shellId, ...) plus optionele children.
|
|
5
|
+
* Zo verliest een shell bij migratie naar AuthenticatedLayout geen enkele
|
|
6
|
+
* prop die hij voorheen rechtstreeks aan <Sidebar /> gaf.
|
|
7
|
+
*/
|
|
8
|
+
export type AuthenticatedLayoutProps = SidebarProps & {
|
|
9
|
+
/** Optioneel; standaard rendert de layout <Outlet />. */
|
|
10
|
+
children?: ReactNode;
|
|
11
|
+
/** Toon de gedeelde paginakop. Standaard ingeschakeld. */
|
|
12
|
+
header?: boolean;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Zijbalk + content-offset + mobiele topbalk. Desktop: ml-64 (gelijk aan de
|
|
16
|
+
* oude shell-layout). Mobiel: geen marge, pt-14 onder de topbalk; de sticky
|
|
17
|
+
* Header schuift via --core-topbar-h onder de topbalk.
|
|
18
|
+
*/
|
|
19
|
+
export declare function AuthenticatedLayout({ children, header, ...sidebarProps }: AuthenticatedLayoutProps): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
//# sourceMappingURL=AuthenticatedLayout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthenticatedLayout.d.ts","sourceRoot":"","sources":["../../../src/core/components/layout/AuthenticatedLayout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAG9C;;;;GAIG;AACH,MAAM,MAAM,wBAAwB,GAAG,YAAY,GAAG;IACpD,yDAAyD;IACzD,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,QAAQ,EAAE,MAAa,EAAE,GAAG,YAAY,EAAE,EAAE,wBAAwB,2CAkBzG"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Outlet, useMatches } from "@tanstack/react-router";
|
|
3
|
+
import { Sidebar } from "./Sidebar";
|
|
4
|
+
import { Header } from "./Header";
|
|
5
|
+
/**
|
|
6
|
+
* Zijbalk + content-offset + mobiele topbalk. Desktop: ml-64 (gelijk aan de
|
|
7
|
+
* oude shell-layout). Mobiel: geen marge, pt-14 onder de topbalk; de sticky
|
|
8
|
+
* Header schuift via --core-topbar-h onder de topbalk.
|
|
9
|
+
*/
|
|
10
|
+
export function AuthenticatedLayout({ children, header = true, ...sidebarProps }) {
|
|
11
|
+
const matches = useMatches();
|
|
12
|
+
const titleMatch = [...matches].reverse().find((match) => typeof match.staticData?.title === "string");
|
|
13
|
+
const title = titleMatch?.staticData?.title ?? "";
|
|
14
|
+
return (_jsxs("div", { className: "min-h-screen bg-background [--core-topbar-h:3.5rem] md:[--core-topbar-h:0px]", children: [_jsx(Sidebar, { ...sidebarProps }), _jsxs("div", { className: "flex min-h-dvh flex-col pt-14 md:ml-64 md:pt-0", children: [header && _jsx(Header, { title: title }), _jsx("main", { className: "flex-1", children: children ?? _jsx(Outlet, {}) })] })] }));
|
|
15
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HandoverPage.d.ts","sourceRoot":"","sources":["../../../src/core/components/layout/HandoverPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"HandoverPage.d.ts","sourceRoot":"","sources":["../../../src/core/components/layout/HandoverPage.tsx"],"names":[],"mappings":"AAQA,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAID,wBAAgB,YAAY,CAAC,EAAE,UAAgB,EAAE,SAAoB,EAAE,EAAE,iBAAiB,2CA0FzF"}
|
|
@@ -4,6 +4,7 @@ import { useRouter } from "@tanstack/react-router";
|
|
|
4
4
|
import { Button } from "../ui/button";
|
|
5
5
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "../ui/card";
|
|
6
6
|
import { supabase } from "../../supabase/client";
|
|
7
|
+
import { readRedirectParam } from "../../auth/redirect";
|
|
7
8
|
import { exchangeHandover } from "../../auth/handover.functions.js";
|
|
8
9
|
const FOUTMELDING = "Inloggen is niet gelukt. Probeer het opnieuw vanaf de inlogpagina.";
|
|
9
10
|
export function HandoverPage({ redirectTo = "/", loginPath = "/login" }) {
|
|
@@ -27,6 +28,8 @@ export function HandoverPage({ redirectTo = "/", loginPath = "/login" }) {
|
|
|
27
28
|
started.current = true;
|
|
28
29
|
(async () => {
|
|
29
30
|
const code = new URLSearchParams(window.location.search).get("code");
|
|
31
|
+
// Doel vóór het opschonen van de URL lezen; alleen same-site paden.
|
|
32
|
+
const target = readRedirectParam(redirectTo);
|
|
30
33
|
if (!code) {
|
|
31
34
|
setFailed(true);
|
|
32
35
|
return;
|
|
@@ -36,14 +39,6 @@ export function HandoverPage({ redirectTo = "/", loginPath = "/login" }) {
|
|
|
36
39
|
const cleanUrl = window.location.pathname + window.location.hash;
|
|
37
40
|
window.history.replaceState(null, "", cleanUrl);
|
|
38
41
|
try {
|
|
39
|
-
// Eerst een eventuele bestaande sessie beëindigen, anders belandt
|
|
40
|
-
// iemand in de omgeving van de vorige gebruiker. Scope 'local':
|
|
41
|
-
// alleen de lokale opslag wissen, geen server-side sessies intrekken.
|
|
42
|
-
// De standaardscope ('global') zou óók de net aangemaakte
|
|
43
|
-
// handover-sessie server-side ongeldig maken (session_not_found).
|
|
44
|
-
const { data } = await supabase.auth.getSession();
|
|
45
|
-
if (data.session)
|
|
46
|
-
await supabase.auth.signOut({ scope: "local" });
|
|
47
42
|
const result = await exchangeHandover({ data: { code } });
|
|
48
43
|
const { error } = await supabase.auth.setSession({
|
|
49
44
|
access_token: result.accessToken,
|
|
@@ -56,7 +51,7 @@ export function HandoverPage({ redirectTo = "/", loginPath = "/login" }) {
|
|
|
56
51
|
await waitForAuthContext();
|
|
57
52
|
// Nooit window.location.href: een volledige pagina-load laat de
|
|
58
53
|
// server (zonder sessie) eerst naar /login redirecten.
|
|
59
|
-
await router.navigate({
|
|
54
|
+
await router.navigate({ href: target, replace: true });
|
|
60
55
|
}
|
|
61
56
|
catch {
|
|
62
57
|
setFailed(true);
|
|
@@ -7,5 +7,5 @@ import { BugsDialog } from "./BugsDialog";
|
|
|
7
7
|
import { NotificationBell } from "./NotificationBell";
|
|
8
8
|
export function Header({ title }) {
|
|
9
9
|
const [bugsDialogOpen, setBugsDialogOpen] = useState(false);
|
|
10
|
-
return (_jsxs(_Fragment, { children: [_jsxs("header", { className: "sticky top-0 z-30 flex h-16 items-center justify-between border-b border-border bg-card px-6", children: [_jsx("h1", { className: "text-xl font-semibold text-foreground", children: title }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsxs("div", { className: "relative hidden md:block", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { placeholder: "Zoeken...", className: "w-64 pl-9" })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setBugsDialogOpen(true), children: [_jsx(Bug, { className: "mr-2 h-4 w-4" }), "Bugs melden"] }), _jsx(NotificationBell, {})] })] }), _jsx(BugsDialog, { open: bugsDialogOpen, onOpenChange: setBugsDialogOpen })] }));
|
|
10
|
+
return (_jsxs(_Fragment, { children: [_jsxs("header", { className: "sticky top-[var(--core-topbar-h,0px)] md:top-0 z-30 flex h-16 items-center justify-between border-b border-border bg-card px-6", children: [_jsx("h1", { className: "text-xl font-semibold text-foreground", children: title }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsxs("div", { className: "relative hidden md:block", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { placeholder: "Zoeken...", className: "w-64 pl-9" })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setBugsDialogOpen(true), children: [_jsx(Bug, { className: "mr-2 h-4 w-4" }), "Bugs melden"] }), _jsx(NotificationBell, {})] })] }), _jsx(BugsDialog, { open: bugsDialogOpen, onOpenChange: setBugsDialogOpen })] }));
|
|
11
11
|
}
|
|
@@ -74,5 +74,5 @@ const coreCards = [
|
|
|
74
74
|
export function InstellingenPage({ modules }) {
|
|
75
75
|
const moduleCards = modules.flatMap((m) => m.settingsCards);
|
|
76
76
|
const allCards = [...coreCards, ...moduleCards].sort((a, b) => a.order - b.order);
|
|
77
|
-
return (_jsx("
|
|
77
|
+
return (_jsx("div", { className: "p-6", children: _jsx("div", { className: "grid gap-6 lg:grid-cols-2", children: allCards.map((card, i) => _jsx(card.component, {}, i)) }) }));
|
|
78
78
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LoginPage.d.ts","sourceRoot":"","sources":["../../../src/core/components/layout/LoginPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"LoginPage.d.ts","sourceRoot":"","sources":["../../../src/core/components/layout/LoginPage.tsx"],"names":[],"mappings":"AAWA,UAAU,cAAc;IACtB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,SAAS,CAAC,EAAE,UAA4B,EAAE,EAAE,cAAc,2CA+HzE"}
|
|
@@ -8,6 +8,7 @@ import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle }
|
|
|
8
8
|
import { toast } from "sonner";
|
|
9
9
|
import { loginSchema } from "../../lib/validationSchemas";
|
|
10
10
|
import { supabase } from "../../supabase/client";
|
|
11
|
+
import { readRedirectParam } from "../../auth/redirect";
|
|
11
12
|
export function LoginPage({ redirectTo = "/instellingen" }) {
|
|
12
13
|
const router = useRouter();
|
|
13
14
|
const [email, setEmail] = useState("");
|
|
@@ -42,12 +43,11 @@ export function LoginPage({ redirectTo = "/instellingen" }) {
|
|
|
42
43
|
}
|
|
43
44
|
// Client-side navigatie zonder volledige herlaad: dat voorkomt de flits
|
|
44
45
|
// waarbij de server (zonder sessie) eerst naar /login terugstuurt.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
await router.navigate({ to: redirectTo, replace: true });
|
|
46
|
+
// Geen window.location.href-fallback: een volledige load laat de guard
|
|
47
|
+
// opnieuw beslissen; de root-invalidate pakt de sessie alsnog op.
|
|
48
|
+
await waitForAuthContext();
|
|
49
|
+
const target = readRedirectParam(redirectTo);
|
|
50
|
+
await router.navigate({ href: target, replace: true });
|
|
51
51
|
setIsLoading(false);
|
|
52
52
|
};
|
|
53
53
|
const handleForgotPassword = async (e) => {
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import type { AuthContext } from "../../auth/useAuth";
|
|
2
2
|
import type { FlowModule } from "../../types/module.js";
|
|
3
|
-
interface SidebarProps {
|
|
3
|
+
export interface SidebarProps {
|
|
4
4
|
auth: AuthContext;
|
|
5
5
|
modules: FlowModule[];
|
|
6
|
+
/**
|
|
7
|
+
* Identificeert de shell (bijv. "floriday-voorraad"). De core gebruikt hem
|
|
8
|
+
* niet voor logica; hij komt als data-attribuut op de zijbalk, zodat
|
|
9
|
+
* extensies en shells per-shell zijbalkvolgorde of styling kunnen koppelen.
|
|
10
|
+
*/
|
|
11
|
+
shellId?: string;
|
|
6
12
|
}
|
|
7
|
-
|
|
8
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Desktop (md+): vaste zijbalk links, w-64 — ongewijzigd t.o.v. eerdere versies.
|
|
15
|
+
* Mobiel (<md): vaste topbalk (h-14) met menuknop die de zijbalk als Sheet opent.
|
|
16
|
+
* Shells die AuthenticatedLayout gebruiken krijgen de juiste content-offset.
|
|
17
|
+
*/
|
|
18
|
+
export declare function Sidebar({ auth, modules, shellId }: SidebarProps): import("react/jsx-runtime").JSX.Element;
|
|
9
19
|
//# sourceMappingURL=Sidebar.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sidebar.d.ts","sourceRoot":"","sources":["../../../src/core/components/layout/Sidebar.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Sidebar.d.ts","sourceRoot":"","sources":["../../../src/core/components/layout/Sidebar.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAW,MAAM,uBAAuB,CAAC;AAMjE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA6ID;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,YAAY,2CA8C/D"}
|
|
@@ -1,32 +1,49 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
3
|
import { Link, useLocation } from "@tanstack/react-router";
|
|
4
|
-
import { Settings, LogOut, ChevronDown } from "lucide-react";
|
|
4
|
+
import { Settings, LogOut, ChevronDown, Menu } from "lucide-react";
|
|
5
|
+
import { Sheet, SheetContent, SheetTitle } from "../ui/sheet";
|
|
5
6
|
import { cn } from "../../lib/utils";
|
|
6
7
|
import { useModuleListTransform } from "../../extensions/ShellExtensions.js";
|
|
7
8
|
// ─── Single flat nav link ─────────────────────────────────────────────────────
|
|
8
|
-
function SidebarLink({ item, pathname, indent = false, }) {
|
|
9
|
+
function SidebarLink({ item, pathname, indent = false, onNavigate, }) {
|
|
9
10
|
const isActive = pathname === item.href;
|
|
10
|
-
return (_jsxs(Link, { to: item.href, className: cn("flex items-center gap-3 rounded-lg px-3 text-sm font-medium transition-colors", indent ? "py-2" : "py-2.5", isActive
|
|
11
|
+
return (_jsxs(Link, { to: item.href, onClick: onNavigate, className: cn("flex items-center gap-3 rounded-lg px-3 text-sm font-medium transition-colors", indent ? "py-2" : "py-2.5", isActive
|
|
11
12
|
? "bg-sidebar-accent text-sidebar-accent-foreground"
|
|
12
13
|
: "text-sidebar-muted hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"), children: [_jsx(item.icon, { className: cn(indent ? "h-4 w-4" : "h-5 w-5") }), item.label] }));
|
|
13
14
|
}
|
|
14
15
|
// ─── Collapsible group (for modules with children) ────────────────────────────
|
|
15
|
-
function SidebarGroup({ item, pathname, }) {
|
|
16
|
+
function SidebarGroup({ item, pathname, onNavigate, }) {
|
|
16
17
|
const groupActive = item.children?.some((child) => pathname === child.href || pathname.startsWith(child.href + "/")) ?? false;
|
|
17
18
|
const [open, setOpen] = useState(groupActive);
|
|
18
19
|
return (_jsxs(_Fragment, { children: [_jsxs("button", { onClick: () => setOpen(!open), className: cn("flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-colors cursor-pointer", groupActive
|
|
19
20
|
? "text-sidebar-accent-foreground"
|
|
20
|
-
: "text-sidebar-muted hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"), children: [_jsx(item.icon, { className: "h-5 w-5" }), _jsx("span", { className: "flex-1 text-left", children: item.label }), _jsx(ChevronDown, { className: cn("h-4 w-4 transition-transform", open && "rotate-180") })] }), open && (_jsx("div", { className: "ml-3 space-y-1", children: item.children.map((child) => (_jsx(SidebarLink, { item: child, pathname: pathname, indent: true }, child.href))) }))] }));
|
|
21
|
+
: "text-sidebar-muted hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"), children: [_jsx(item.icon, { className: "h-5 w-5" }), _jsx("span", { className: "flex-1 text-left", children: item.label }), _jsx(ChevronDown, { className: cn("h-4 w-4 transition-transform", open && "rotate-180") })] }), open && (_jsx("div", { className: "ml-3 space-y-1", children: item.children.map((child) => (_jsx(SidebarLink, { item: child, pathname: pathname, indent: true, onNavigate: onNavigate }, child.href))) }))] }));
|
|
21
22
|
}
|
|
22
|
-
// ───
|
|
23
|
-
|
|
24
|
-
const location = useLocation();
|
|
25
|
-
const pathname = location.pathname;
|
|
23
|
+
// ─── Shared content (desktop aside + mobile sheet) ────────────────────────────
|
|
24
|
+
function SidebarContent({ auth, modules, pathname, onNavigate, }) {
|
|
26
25
|
// Zonder ShellExtensionsProvider komt de lijst ongewijzigd terug (code-volgorde).
|
|
27
26
|
const visibleModules = useModuleListTransform(modules);
|
|
28
27
|
const handleSignOut = async () => {
|
|
28
|
+
onNavigate?.();
|
|
29
29
|
await auth.signOut();
|
|
30
30
|
};
|
|
31
|
-
return (
|
|
31
|
+
return (_jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsx("div", { className: "flex shrink-0 items-center justify-center px-12 py-0 border-b border-sidebar-border", children: _jsx("img", { src: "/logo.png", alt: "Logo", className: "max-h-24 w-auto max-w-full object-contain" }) }), _jsx("nav", { className: "min-h-0 flex-1 space-y-1 px-3 py-4 overflow-y-auto sidebar-scroll", children: visibleModules.map((mod) => mod.nav.children?.length ? (_jsx(SidebarGroup, { item: mod.nav, pathname: pathname, onNavigate: onNavigate }, mod.id)) : (_jsx(SidebarLink, { item: mod.nav, pathname: pathname, onNavigate: onNavigate }, mod.id))) }), _jsxs("div", { className: "shrink-0 border-t border-sidebar-border px-3 py-4", children: [_jsx(SidebarLink, { item: { label: "Instellingen", href: "/instellingen", icon: Settings }, pathname: pathname, onNavigate: onNavigate }), _jsxs("button", { onClick: handleSignOut, className: "flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-colors text-sidebar-muted hover:bg-sidebar-accent hover:text-sidebar-accent-foreground mt-1 cursor-pointer", children: [_jsx(LogOut, { className: "h-5 w-5" }), "Uitloggen"] })] })] }));
|
|
32
|
+
}
|
|
33
|
+
// ─── Main Sidebar ─────────────────────────────────────────────────────────────
|
|
34
|
+
/**
|
|
35
|
+
* Desktop (md+): vaste zijbalk links, w-64 — ongewijzigd t.o.v. eerdere versies.
|
|
36
|
+
* Mobiel (<md): vaste topbalk (h-14) met menuknop die de zijbalk als Sheet opent.
|
|
37
|
+
* Shells die AuthenticatedLayout gebruiken krijgen de juiste content-offset.
|
|
38
|
+
*/
|
|
39
|
+
export function Sidebar({ auth, modules, shellId }) {
|
|
40
|
+
const location = useLocation();
|
|
41
|
+
const pathname = location.pathname;
|
|
42
|
+
const [mobileOpen, setMobileOpen] = useState(false);
|
|
43
|
+
// Sluit het mobiele menu bij elke routewijziging.
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
setMobileOpen(false);
|
|
46
|
+
}, [pathname]);
|
|
47
|
+
const close = () => setMobileOpen(false);
|
|
48
|
+
return (_jsxs(_Fragment, { children: [_jsx("aside", { "data-shell": shellId, className: "fixed left-0 top-0 z-40 hidden h-screen h-dvh w-64 bg-sidebar md:block", children: _jsx(SidebarContent, { auth: auth, modules: modules, pathname: pathname }) }), _jsxs("div", { className: "fixed inset-x-0 top-0 z-40 flex h-14 items-center gap-3 border-b border-sidebar-border bg-sidebar px-3 md:hidden", children: [_jsx("button", { type: "button", onClick: () => setMobileOpen(true), "aria-label": "Menu openen", className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-lg text-sidebar-muted hover:bg-sidebar-accent hover:text-sidebar-accent-foreground", children: _jsx(Menu, { className: "h-5 w-5" }) }), _jsx("img", { src: "/logo.png", alt: "Logo", className: "h-8 w-auto max-w-[10rem] object-contain" })] }), _jsx(Sheet, { open: mobileOpen, onOpenChange: setMobileOpen, children: _jsxs(SheetContent, { side: "left", "data-shell": shellId, className: "w-64 max-w-[85vw] border-sidebar-border bg-sidebar p-0 md:hidden", children: [_jsx(SheetTitle, { className: "sr-only", children: "Navigatie" }), _jsx(SidebarContent, { auth: auth, modules: modules, pathname: pathname, onNavigate: close })] }) })] }));
|
|
32
49
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,12 +3,17 @@ export type { AuthContext, AuthState, AuthActions, AppRole } from "./auth/useAut
|
|
|
3
3
|
export { supabase } from "./supabase/client";
|
|
4
4
|
export type { Database } from "./supabase/types";
|
|
5
5
|
export { LoginPage } from "./components/layout/LoginPage";
|
|
6
|
+
export { requireAuth, safeRedirectPath, readRedirectParam } from "./auth/redirect";
|
|
7
|
+
export type { RequireAuthArgs } from "./auth/redirect";
|
|
6
8
|
export { HandoverPage } from "./components/layout/HandoverPage";
|
|
7
9
|
export type { HandoverPageProps } from "./components/layout/HandoverPage";
|
|
8
10
|
export { ResetWachtwoordPage } from "./components/layout/ResetWachtwoordPage";
|
|
9
11
|
export { Header } from "./components/layout/Header";
|
|
10
12
|
export { InstellingenPage } from "./components/layout/InstellingenPage";
|
|
11
13
|
export { Sidebar } from "./components/layout/Sidebar";
|
|
14
|
+
export type { SidebarProps } from "./components/layout/Sidebar";
|
|
15
|
+
export { AuthenticatedLayout } from "./components/layout/AuthenticatedLayout";
|
|
16
|
+
export type { AuthenticatedLayoutProps } from "./components/layout/AuthenticatedLayout";
|
|
12
17
|
export { Toaster } from "./components/ui/sonner";
|
|
13
18
|
export { Button } from "./components/ui/button";
|
|
14
19
|
export { Input } from "./components/ui/input";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/core/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/core/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -4,11 +4,13 @@ export { useAuth, useAuthState } from "./auth/useAuth";
|
|
|
4
4
|
export { supabase } from "./supabase/client";
|
|
5
5
|
// Layout
|
|
6
6
|
export { LoginPage } from "./components/layout/LoginPage";
|
|
7
|
+
export { requireAuth, safeRedirectPath, readRedirectParam } from "./auth/redirect";
|
|
7
8
|
export { HandoverPage } from "./components/layout/HandoverPage";
|
|
8
9
|
export { ResetWachtwoordPage } from "./components/layout/ResetWachtwoordPage";
|
|
9
10
|
export { Header } from "./components/layout/Header";
|
|
10
11
|
export { InstellingenPage } from "./components/layout/InstellingenPage";
|
|
11
12
|
export { Sidebar } from "./components/layout/Sidebar";
|
|
13
|
+
export { AuthenticatedLayout } from "./components/layout/AuthenticatedLayout";
|
|
12
14
|
export { Toaster } from "./components/ui/sonner";
|
|
13
15
|
// UI components
|
|
14
16
|
export { Button } from "./components/ui/button";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flowselections/core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.22",
|
|
4
4
|
"private": false,
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"type": "module",
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
},
|
|
92
92
|
"devDependencies": {
|
|
93
93
|
"@eslint/js": "^9.32.0",
|
|
94
|
-
"@lovable.dev/vite-tanstack-config": "2.
|
|
94
|
+
"@lovable.dev/vite-tanstack-config": "2.23.1",
|
|
95
95
|
"@types/node": "^22.16.5",
|
|
96
96
|
"@types/react": "^19.2.0",
|
|
97
97
|
"@types/react-dom": "^19.2.0",
|