@notionx/core 0.1.0
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/admin/index.d.ts +137 -0
- package/dist/admin/index.js +206 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/index.d.ts +324 -0
- package/dist/admin/pages/index.js +827 -0
- package/dist/admin/pages/index.js.map +1 -0
- package/dist/auth/auth-pages/forgot-password.d.ts +20 -0
- package/dist/auth/auth-pages/forgot-password.js +70 -0
- package/dist/auth/auth-pages/forgot-password.js.map +1 -0
- package/dist/auth/auth-pages/index.d.ts +6 -0
- package/dist/auth/auth-pages/index.js +342 -0
- package/dist/auth/auth-pages/index.js.map +1 -0
- package/dist/auth/auth-pages/login.d.ts +30 -0
- package/dist/auth/auth-pages/login.js +125 -0
- package/dist/auth/auth-pages/login.js.map +1 -0
- package/dist/auth/auth-pages/register.d.ts +17 -0
- package/dist/auth/auth-pages/register.js +81 -0
- package/dist/auth/auth-pages/register.js.map +1 -0
- package/dist/auth/auth-pages/reset-password.d.ts +18 -0
- package/dist/auth/auth-pages/reset-password.js +72 -0
- package/dist/auth/auth-pages/reset-password.js.map +1 -0
- package/dist/auth/index.d.ts +72 -0
- package/dist/auth/index.js +1011 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/passwords.d.ts +6 -0
- package/dist/auth/passwords.js +79 -0
- package/dist/auth/passwords.js.map +1 -0
- package/dist/auth/rate-limit.d.ts +28 -0
- package/dist/auth/rate-limit.js +245 -0
- package/dist/auth/rate-limit.js.map +1 -0
- package/dist/auth/routes/google-callback.d.ts +6 -0
- package/dist/auth/routes/google-callback.js +404 -0
- package/dist/auth/routes/google-callback.js.map +1 -0
- package/dist/auth/routes/google.d.ts +6 -0
- package/dist/auth/routes/google.js +250 -0
- package/dist/auth/routes/google.js.map +1 -0
- package/dist/auth/routes/index.d.ts +22 -0
- package/dist/auth/routes/index.js +619 -0
- package/dist/auth/routes/index.js.map +1 -0
- package/dist/auth/routes/verify-email.d.ts +6 -0
- package/dist/auth/routes/verify-email.js +317 -0
- package/dist/auth/routes/verify-email.js.map +1 -0
- package/dist/auth/routes/viewer.d.ts +6 -0
- package/dist/auth/routes/viewer.js +372 -0
- package/dist/auth/routes/viewer.js.map +1 -0
- package/dist/auth/session.d.ts +9 -0
- package/dist/auth/session.js +1 -0
- package/dist/auth/session.js.map +1 -0
- package/dist/auth/turnstile.d.ts +20 -0
- package/dist/auth/turnstile.js +301 -0
- package/dist/auth/turnstile.js.map +1 -0
- package/dist/auth/user-session.d.ts +42 -0
- package/dist/auth/user-session.js +419 -0
- package/dist/auth/user-session.js.map +1 -0
- package/dist/auth/users.d.ts +112 -0
- package/dist/auth/users.js +558 -0
- package/dist/auth/users.js.map +1 -0
- package/dist/bootstrap-CN2g76M6.d.ts +67 -0
- package/dist/cache/index.d.ts +6 -0
- package/dist/cache/index.js +47 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/content/admin-summary.d.ts +24 -0
- package/dist/content/admin-summary.js +36 -0
- package/dist/content/admin-summary.js.map +1 -0
- package/dist/content/index.d.ts +9 -0
- package/dist/content/index.js +473 -0
- package/dist/content/index.js.map +1 -0
- package/dist/content/models.d.ts +69 -0
- package/dist/content/models.js +24 -0
- package/dist/content/models.js.map +1 -0
- package/dist/content/prewarm.d.ts +28 -0
- package/dist/content/prewarm.js +56 -0
- package/dist/content/prewarm.js.map +1 -0
- package/dist/content/revalidate.d.ts +37 -0
- package/dist/content/revalidate.js +170 -0
- package/dist/content/revalidate.js.map +1 -0
- package/dist/content/search-index.d.ts +54 -0
- package/dist/content/search-index.js +172 -0
- package/dist/content/search-index.js.map +1 -0
- package/dist/content/search.d.ts +8 -0
- package/dist/content/search.js +57 -0
- package/dist/content/search.js.map +1 -0
- package/dist/doctor/cli.d.ts +1 -0
- package/dist/doctor/cli.js +360 -0
- package/dist/doctor/cli.js.map +1 -0
- package/dist/doctor/index.d.ts +139 -0
- package/dist/doctor/index.js +289 -0
- package/dist/doctor/index.js.map +1 -0
- package/dist/email/index.d.ts +38 -0
- package/dist/email/index.js +126 -0
- package/dist/email/index.js.map +1 -0
- package/dist/env-C5qu-0R-.d.ts +35 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/i18n/index.d.ts +26 -0
- package/dist/i18n/index.js +73 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +1281 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/admin/index.d.ts +75 -0
- package/dist/internal/admin/index.js +365 -0
- package/dist/internal/admin/index.js.map +1 -0
- package/dist/media/index.d.ts +24 -0
- package/dist/media/index.js +86 -0
- package/dist/media/index.js.map +1 -0
- package/dist/media/routes/index.d.ts +1 -0
- package/dist/media/routes/index.js +585 -0
- package/dist/media/routes/index.js.map +1 -0
- package/dist/media/routes/notion-media.d.ts +19 -0
- package/dist/media/routes/notion-media.js +588 -0
- package/dist/media/routes/notion-media.js.map +1 -0
- package/dist/middleware.d.ts +95 -0
- package/dist/middleware.js +79 -0
- package/dist/middleware.js.map +1 -0
- package/dist/notion/block-text.d.ts +5 -0
- package/dist/notion/block-text.js +37 -0
- package/dist/notion/block-text.js.map +1 -0
- package/dist/notion/blocks.d.ts +24 -0
- package/dist/notion/blocks.js +46 -0
- package/dist/notion/blocks.js.map +1 -0
- package/dist/notion/client.d.ts +7 -0
- package/dist/notion/client.js +13 -0
- package/dist/notion/client.js.map +1 -0
- package/dist/notion/config.d.ts +25 -0
- package/dist/notion/config.js +147 -0
- package/dist/notion/config.js.map +1 -0
- package/dist/notion/content-cache.d.ts +45 -0
- package/dist/notion/content-cache.js +166 -0
- package/dist/notion/content-cache.js.map +1 -0
- package/dist/notion/generic-source.d.ts +61 -0
- package/dist/notion/generic-source.js +408 -0
- package/dist/notion/generic-source.js.map +1 -0
- package/dist/notion/index.d.ts +13 -0
- package/dist/notion/index.js +1278 -0
- package/dist/notion/index.js.map +1 -0
- package/dist/notion/mappers.d.ts +1 -0
- package/dist/notion/mappers.js +152 -0
- package/dist/notion/mappers.js.map +1 -0
- package/dist/notion/media.d.ts +22 -0
- package/dist/notion/media.js +209 -0
- package/dist/notion/media.js.map +1 -0
- package/dist/notion/property-mappers.d.ts +24 -0
- package/dist/notion/property-mappers.js +152 -0
- package/dist/notion/property-mappers.js.map +1 -0
- package/dist/notion/routes/index.d.ts +8 -0
- package/dist/notion/routes/index.js +428 -0
- package/dist/notion/routes/index.js.map +1 -0
- package/dist/notion/routes/webhook.d.ts +98 -0
- package/dist/notion/routes/webhook.js +428 -0
- package/dist/notion/routes/webhook.js.map +1 -0
- package/dist/notion/types.d.ts +152 -0
- package/dist/notion/types.js +1 -0
- package/dist/notion/types.js.map +1 -0
- package/dist/notion/webhook.d.ts +83 -0
- package/dist/notion/webhook.js +490 -0
- package/dist/notion/webhook.js.map +1 -0
- package/dist/platform/capabilities.d.ts +34 -0
- package/dist/platform/capabilities.js +42 -0
- package/dist/platform/capabilities.js.map +1 -0
- package/dist/platform/current.d.ts +13 -0
- package/dist/platform/current.js +181 -0
- package/dist/platform/current.js.map +1 -0
- package/dist/platform/index.d.ts +5 -0
- package/dist/platform/index.js +269 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/platform/runtime.d.ts +118 -0
- package/dist/platform/runtime.js +160 -0
- package/dist/platform/runtime.js.map +1 -0
- package/dist/platform/selection.d.ts +10 -0
- package/dist/platform/selection.js +22 -0
- package/dist/platform/selection.js.map +1 -0
- package/dist/storage/index.d.ts +17 -0
- package/dist/storage/index.js +218 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/routes/cdn.d.ts +19 -0
- package/dist/storage/routes/cdn.js +289 -0
- package/dist/storage/routes/cdn.js.map +1 -0
- package/dist/storage/routes/files.d.ts +27 -0
- package/dist/storage/routes/files.js +216 -0
- package/dist/storage/routes/files.js.map +1 -0
- package/dist/storage/routes/index.d.ts +2 -0
- package/dist/storage/routes/index.js +352 -0
- package/dist/storage/routes/index.js.map +1 -0
- package/dist/types-BsAcZSNX.d.ts +94 -0
- package/dist/types.d.ts +78 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/dist/util/index.d.ts +18 -0
- package/dist/util/index.js +48 -0
- package/dist/util/index.js.map +1 -0
- package/dist/worker/index.d.ts +6 -0
- package/dist/worker/index.js +1026 -0
- package/dist/worker/index.js.map +1 -0
- package/dist/worker/routes/content-prewarm.d.ts +34 -0
- package/dist/worker/routes/content-prewarm.js +38 -0
- package/dist/worker/routes/content-prewarm.js.map +1 -0
- package/dist/worker/routes/content-revalidate.d.ts +81 -0
- package/dist/worker/routes/content-revalidate.js +64 -0
- package/dist/worker/routes/content-revalidate.js.map +1 -0
- package/dist/worker/routes/health.d.ts +14 -0
- package/dist/worker/routes/health.js +278 -0
- package/dist/worker/routes/health.js.map +1 -0
- package/dist/worker/routes/index.d.ts +6 -0
- package/dist/worker/routes/index.js +373 -0
- package/dist/worker/routes/index.js.map +1 -0
- package/package.json +124 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { AdminNavItem } from '../types.js';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import { ReactNode } from 'react';
|
|
4
|
+
import '../content/models.js';
|
|
5
|
+
import '../notion/types.js';
|
|
6
|
+
|
|
7
|
+
interface AdminNavOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Roles the current viewer has. When supplied, any item whose
|
|
10
|
+
* `requireRole` is set and not in this list is dropped. When omitted,
|
|
11
|
+
* no role filtering is applied.
|
|
12
|
+
*/
|
|
13
|
+
roles?: string[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Build the visible admin nav for a viewer. The returned array is a
|
|
17
|
+
* fresh copy; mutating it does not affect the caller's input.
|
|
18
|
+
*/
|
|
19
|
+
declare function createAdminNav(items: AdminNavItem[], options?: AdminNavOptions): AdminNavItem[];
|
|
20
|
+
|
|
21
|
+
interface AdminHeaderProps {
|
|
22
|
+
viewer: AdminShellViewer;
|
|
23
|
+
brandLabel: string;
|
|
24
|
+
brandHref: string;
|
|
25
|
+
headerLinks?: ReactNode;
|
|
26
|
+
headerActions?: ReactNode;
|
|
27
|
+
ui?: AdminShellUI;
|
|
28
|
+
}
|
|
29
|
+
declare function AdminHeader({ viewer, brandLabel, brandHref, headerLinks, headerActions, ui, }: AdminHeaderProps): react.JSX.Element;
|
|
30
|
+
|
|
31
|
+
interface AdminSidebarLinkRenderProps {
|
|
32
|
+
href: string;
|
|
33
|
+
active: boolean;
|
|
34
|
+
external?: boolean;
|
|
35
|
+
children: ReactNode;
|
|
36
|
+
}
|
|
37
|
+
type AdminSidebarLinkRenderer = (props: AdminSidebarLinkRenderProps) => ReactNode;
|
|
38
|
+
interface AdminSidebarProps {
|
|
39
|
+
items: AdminNavItem[];
|
|
40
|
+
activeHref: string;
|
|
41
|
+
renderLink: AdminSidebarLinkRenderer;
|
|
42
|
+
}
|
|
43
|
+
declare function AdminSidebar({ items, activeHref, renderLink, }: AdminSidebarProps): react.JSX.Element | null;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Minimal viewer shape the shell needs to render the header. The
|
|
47
|
+
* consumer's full `AdminViewer` may carry more fields; only the
|
|
48
|
+
* fields the shell actually reads are required here.
|
|
49
|
+
*/
|
|
50
|
+
interface AdminShellViewer {
|
|
51
|
+
email: string;
|
|
52
|
+
name?: string | null;
|
|
53
|
+
picture?: string | null;
|
|
54
|
+
isAdmin?: boolean;
|
|
55
|
+
role?: string | null;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Slots the consumer can fill with their own design-system primitives.
|
|
59
|
+
* Every slot is optional — the shell falls back to plain HTML when a
|
|
60
|
+
* slot is not provided so projects without shadcn still render.
|
|
61
|
+
*/
|
|
62
|
+
interface AdminShellUI {
|
|
63
|
+
/** Render a brand link (e.g. the "vinext Admin" mark). */
|
|
64
|
+
BrandLink?: (props: {
|
|
65
|
+
href: string;
|
|
66
|
+
children: ReactNode;
|
|
67
|
+
}) => ReactNode;
|
|
68
|
+
/** Render a single sidebar entry. */
|
|
69
|
+
SidebarLink?: (props: {
|
|
70
|
+
href: string;
|
|
71
|
+
active: boolean;
|
|
72
|
+
external?: boolean;
|
|
73
|
+
children: ReactNode;
|
|
74
|
+
}) => ReactNode;
|
|
75
|
+
/** Render a single header link/button (e.g. theme toggle, logout). */
|
|
76
|
+
HeaderAction?: (props: {
|
|
77
|
+
children: ReactNode;
|
|
78
|
+
}) => ReactNode;
|
|
79
|
+
}
|
|
80
|
+
interface AdminShellProps {
|
|
81
|
+
/** Raw nav items. The shell sorts and role-filters them. */
|
|
82
|
+
nav: AdminNavItem[];
|
|
83
|
+
/** Currently authenticated viewer. */
|
|
84
|
+
viewer: AdminShellViewer;
|
|
85
|
+
/** Current pathname; used to mark the active sidebar entry. */
|
|
86
|
+
pathname?: string;
|
|
87
|
+
/** Brand label rendered in the header. Defaults to "Admin". */
|
|
88
|
+
brandLabel?: string;
|
|
89
|
+
/** Brand link target. Defaults to "/admin". */
|
|
90
|
+
brandHref?: string;
|
|
91
|
+
/** Extra links rendered in the header (e.g. "view site"). */
|
|
92
|
+
headerLinks?: ReactNode;
|
|
93
|
+
/** Extra actions rendered in the header right cluster (logout, etc.). */
|
|
94
|
+
headerActions?: ReactNode;
|
|
95
|
+
/** Slots for design-system primitives. */
|
|
96
|
+
ui?: AdminShellUI;
|
|
97
|
+
/** Roles the viewer has; controls role-gated nav items. */
|
|
98
|
+
viewerRoles?: string[];
|
|
99
|
+
children: ReactNode;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Top-level admin chrome. Re-exports the sidebar/header pieces so
|
|
103
|
+
* projects that want to rearrange the layout (e.g. put the sidebar
|
|
104
|
+
* on the right) can swap them in.
|
|
105
|
+
*/
|
|
106
|
+
declare function AdminShell({ nav, viewer, pathname, brandLabel, brandHref, headerLinks, headerActions, ui, viewerRoles, children, }: AdminShellProps): react.JSX.Element;
|
|
107
|
+
|
|
108
|
+
interface AdminLayoutProps {
|
|
109
|
+
nav: AdminNavItem[];
|
|
110
|
+
/** Resolves the currently authenticated viewer. Return `null` for guests. */
|
|
111
|
+
getViewer: () => Promise<AdminShellViewer | null>;
|
|
112
|
+
/** Path the user is redirected to when `getViewer` returns `null`. */
|
|
113
|
+
loginPath?: string;
|
|
114
|
+
/** Current pathname; used to mark the active sidebar entry. */
|
|
115
|
+
pathname?: string;
|
|
116
|
+
brandLabel?: string;
|
|
117
|
+
brandHref?: string;
|
|
118
|
+
headerLinks?: ReactNode;
|
|
119
|
+
headerActions?: ReactNode;
|
|
120
|
+
ui?: AdminShellUI;
|
|
121
|
+
/**
|
|
122
|
+
* Roles the viewer has. Pass `viewer.isAdmin ? ["admin"] : ["user"]`
|
|
123
|
+
* (or your role catalog) to enable role-based nav filtering.
|
|
124
|
+
*/
|
|
125
|
+
viewerRoles?: string[];
|
|
126
|
+
children: ReactNode;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Async Next.js layout. If the viewer is missing, renders a minimal
|
|
130
|
+
* "Redirecting…" placeholder and the consumer's `_redirect` mechanism
|
|
131
|
+
* (set `loginPath` and let the consumer wrap in a `redirect()` from
|
|
132
|
+
* `next/navigation`). For simpler use, pass a `getViewer` that throws
|
|
133
|
+
* a redirect.
|
|
134
|
+
*/
|
|
135
|
+
declare function AdminLayout({ nav, getViewer, loginPath, pathname, brandLabel, brandHref, headerLinks, headerActions, ui, viewerRoles, children, }: AdminLayoutProps): Promise<react.JSX.Element>;
|
|
136
|
+
|
|
137
|
+
export { AdminHeader, type AdminHeaderProps, AdminLayout, type AdminLayoutProps, AdminNavItem, type AdminNavOptions, AdminShell, type AdminShellProps, type AdminShellUI, type AdminShellViewer, AdminSidebar, type AdminSidebarLinkRenderProps, type AdminSidebarLinkRenderer, type AdminSidebarProps, createAdminNav };
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
// src/admin/nav.ts
|
|
2
|
+
function createAdminNav(items, options = {}) {
|
|
3
|
+
const visible = options.roles ? items.filter(
|
|
4
|
+
(i) => !i.requireRole || options.roles.includes(i.requireRole)
|
|
5
|
+
) : items.slice();
|
|
6
|
+
return visible.sort((a, b) => {
|
|
7
|
+
const orderDiff = (a.order ?? 100) - (b.order ?? 100);
|
|
8
|
+
return orderDiff !== 0 ? orderDiff : a.labelKey.localeCompare(b.labelKey);
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// src/admin/header.tsx
|
|
13
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
+
function defaultBrandLink({
|
|
15
|
+
href,
|
|
16
|
+
children
|
|
17
|
+
}) {
|
|
18
|
+
return /* @__PURE__ */ jsx(
|
|
19
|
+
"a",
|
|
20
|
+
{
|
|
21
|
+
href,
|
|
22
|
+
className: "inline-flex items-center gap-2 text-sm font-semibold",
|
|
23
|
+
children
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
function AdminHeader({
|
|
28
|
+
viewer,
|
|
29
|
+
brandLabel,
|
|
30
|
+
brandHref,
|
|
31
|
+
headerLinks,
|
|
32
|
+
headerActions,
|
|
33
|
+
ui
|
|
34
|
+
}) {
|
|
35
|
+
const BrandLink = ui?.BrandLink ?? defaultBrandLink;
|
|
36
|
+
return /* @__PURE__ */ jsx("header", { className: "border-b bg-muted/30", children: /* @__PURE__ */ jsxs("div", { className: "container mx-auto flex h-14 max-w-6xl items-center justify-between gap-4 px-4", children: [
|
|
37
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
|
|
38
|
+
/* @__PURE__ */ jsxs(BrandLink, { href: brandHref, children: [
|
|
39
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u25CF" }),
|
|
40
|
+
brandLabel
|
|
41
|
+
] }),
|
|
42
|
+
headerLinks
|
|
43
|
+
] }),
|
|
44
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
45
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-md bg-muted px-2.5 py-1 text-xs", children: [
|
|
46
|
+
viewer.picture && /* @__PURE__ */ jsx(
|
|
47
|
+
"img",
|
|
48
|
+
{
|
|
49
|
+
src: viewer.picture,
|
|
50
|
+
alt: "",
|
|
51
|
+
className: "h-5 w-5 rounded-full"
|
|
52
|
+
}
|
|
53
|
+
),
|
|
54
|
+
/* @__PURE__ */ jsx("span", { className: "hidden font-medium sm:inline", children: viewer.name || viewer.email }),
|
|
55
|
+
viewer.isAdmin && /* @__PURE__ */ jsx("span", { className: "rounded bg-primary/10 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-primary", children: "Admin" })
|
|
56
|
+
] }),
|
|
57
|
+
headerActions
|
|
58
|
+
] })
|
|
59
|
+
] }) });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/admin/sidebar.tsx
|
|
63
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
64
|
+
function isActive(itemHref, currentHref) {
|
|
65
|
+
if (itemHref === currentHref) return true;
|
|
66
|
+
if (itemHref === "/admin") return currentHref === "/admin";
|
|
67
|
+
return currentHref === itemHref || currentHref.startsWith(itemHref + "/");
|
|
68
|
+
}
|
|
69
|
+
function AdminSidebar({
|
|
70
|
+
items,
|
|
71
|
+
activeHref,
|
|
72
|
+
renderLink
|
|
73
|
+
}) {
|
|
74
|
+
if (items.length === 0) return null;
|
|
75
|
+
return /* @__PURE__ */ jsx2(
|
|
76
|
+
"nav",
|
|
77
|
+
{
|
|
78
|
+
"aria-label": "Admin",
|
|
79
|
+
className: "hidden w-56 shrink-0 md:block",
|
|
80
|
+
children: /* @__PURE__ */ jsx2("ul", { className: "space-y-1", children: items.map((item) => /* @__PURE__ */ jsx2("li", { children: renderLink({
|
|
81
|
+
href: item.href,
|
|
82
|
+
active: isActive(item.href, activeHref),
|
|
83
|
+
external: item.external,
|
|
84
|
+
children: item.labelKey
|
|
85
|
+
}) }, item.href)) })
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/admin/shell.tsx
|
|
91
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
92
|
+
function defaultSidebarLink({
|
|
93
|
+
href,
|
|
94
|
+
active,
|
|
95
|
+
external,
|
|
96
|
+
children
|
|
97
|
+
}) {
|
|
98
|
+
if (external) {
|
|
99
|
+
return /* @__PURE__ */ jsx3(
|
|
100
|
+
"a",
|
|
101
|
+
{
|
|
102
|
+
href,
|
|
103
|
+
target: "_blank",
|
|
104
|
+
rel: "noreferrer noopener",
|
|
105
|
+
className: "block rounded-md px-3 py-2 text-sm font-medium " + (active ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/60 hover:text-foreground"),
|
|
106
|
+
children
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
return /* @__PURE__ */ jsx3(
|
|
111
|
+
"a",
|
|
112
|
+
{
|
|
113
|
+
href,
|
|
114
|
+
className: "block rounded-md px-3 py-2 text-sm font-medium " + (active ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/60 hover:text-foreground"),
|
|
115
|
+
children
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
function AdminShell({
|
|
120
|
+
nav,
|
|
121
|
+
viewer,
|
|
122
|
+
pathname,
|
|
123
|
+
brandLabel = "Admin",
|
|
124
|
+
brandHref = "/admin",
|
|
125
|
+
headerLinks,
|
|
126
|
+
headerActions,
|
|
127
|
+
ui,
|
|
128
|
+
viewerRoles,
|
|
129
|
+
children
|
|
130
|
+
}) {
|
|
131
|
+
const items = createAdminNav(nav, { roles: viewerRoles });
|
|
132
|
+
const activeHref = pathname ?? "/admin";
|
|
133
|
+
return /* @__PURE__ */ jsxs2("div", { className: "min-h-screen bg-background", children: [
|
|
134
|
+
/* @__PURE__ */ jsx3(
|
|
135
|
+
AdminHeader,
|
|
136
|
+
{
|
|
137
|
+
viewer,
|
|
138
|
+
brandLabel,
|
|
139
|
+
brandHref,
|
|
140
|
+
headerLinks,
|
|
141
|
+
headerActions,
|
|
142
|
+
ui
|
|
143
|
+
}
|
|
144
|
+
),
|
|
145
|
+
/* @__PURE__ */ jsxs2("div", { className: "container mx-auto flex max-w-6xl gap-8 px-4 py-8", children: [
|
|
146
|
+
/* @__PURE__ */ jsx3(
|
|
147
|
+
AdminSidebar,
|
|
148
|
+
{
|
|
149
|
+
items,
|
|
150
|
+
activeHref,
|
|
151
|
+
renderLink: ui?.SidebarLink ?? defaultSidebarLink
|
|
152
|
+
}
|
|
153
|
+
),
|
|
154
|
+
/* @__PURE__ */ jsx3("main", { className: "min-w-0 flex-1", children })
|
|
155
|
+
] })
|
|
156
|
+
] });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// src/admin/layout.tsx
|
|
160
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
161
|
+
async function AdminLayout({
|
|
162
|
+
nav,
|
|
163
|
+
getViewer,
|
|
164
|
+
loginPath = "/login",
|
|
165
|
+
pathname,
|
|
166
|
+
brandLabel,
|
|
167
|
+
brandHref,
|
|
168
|
+
headerLinks,
|
|
169
|
+
headerActions,
|
|
170
|
+
ui,
|
|
171
|
+
viewerRoles,
|
|
172
|
+
children
|
|
173
|
+
}) {
|
|
174
|
+
const viewer = await getViewer();
|
|
175
|
+
if (!viewer) {
|
|
176
|
+
return /* @__PURE__ */ jsxs3("div", { className: "flex min-h-screen items-center justify-center text-sm text-muted-foreground", children: [
|
|
177
|
+
"Redirecting to",
|
|
178
|
+
" ",
|
|
179
|
+
/* @__PURE__ */ jsx4("a", { className: "ml-1 underline", href: loginPath, children: loginPath }),
|
|
180
|
+
"\u2026"
|
|
181
|
+
] });
|
|
182
|
+
}
|
|
183
|
+
return /* @__PURE__ */ jsx4(
|
|
184
|
+
AdminShell,
|
|
185
|
+
{
|
|
186
|
+
nav,
|
|
187
|
+
viewer,
|
|
188
|
+
pathname,
|
|
189
|
+
brandLabel,
|
|
190
|
+
brandHref,
|
|
191
|
+
headerLinks,
|
|
192
|
+
headerActions,
|
|
193
|
+
ui,
|
|
194
|
+
viewerRoles,
|
|
195
|
+
children
|
|
196
|
+
}
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
export {
|
|
200
|
+
AdminHeader,
|
|
201
|
+
AdminLayout,
|
|
202
|
+
AdminShell,
|
|
203
|
+
AdminSidebar,
|
|
204
|
+
createAdminNav
|
|
205
|
+
};
|
|
206
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/admin/nav.ts","../../src/admin/header.tsx","../../src/admin/sidebar.tsx","../../src/admin/shell.tsx","../../src/admin/layout.tsx"],"sourcesContent":["// Admin sidebar nav factory. Sorts items by `order` (ascending) and\n// then by `labelKey` (locale-aware ascending) as a stable tiebreaker.\n// When the caller passes `roles`, items whose `requireRole` is set and\n// not in the supplied role list are filtered out — this is how the\n// admin shell hides admin-only entries for plain logged-in viewers.\n\nimport type { AdminNavItem } from \"../types\";\n\nexport interface AdminNavOptions {\n /**\n * Roles the current viewer has. When supplied, any item whose\n * `requireRole` is set and not in this list is dropped. When omitted,\n * no role filtering is applied.\n */\n roles?: string[];\n}\n\n/**\n * Build the visible admin nav for a viewer. The returned array is a\n * fresh copy; mutating it does not affect the caller's input.\n */\nexport function createAdminNav(\n items: AdminNavItem[],\n options: AdminNavOptions = {}\n): AdminNavItem[] {\n const visible = options.roles\n ? items.filter(\n (i) => !i.requireRole || options.roles!.includes(i.requireRole)\n )\n : items.slice();\n return visible.sort((a, b) => {\n const orderDiff = (a.order ?? 100) - (b.order ?? 100);\n return orderDiff !== 0 ? orderDiff : a.labelKey.localeCompare(b.labelKey);\n });\n}\n","// admin/header.tsx\n//\n// Top bar for the admin shell. Renders the brand mark, any extra\n// header links supplied by the consumer, and a user-info cluster\n// (avatar + name + admin badge). The consumer can drop additional\n// actions into the right cluster via the `headerActions` slot.\n\nimport type { ReactNode } from \"react\";\nimport type { AdminShellViewer, AdminShellUI } from \"./shell\";\n\nexport interface AdminHeaderProps {\n viewer: AdminShellViewer;\n brandLabel: string;\n brandHref: string;\n headerLinks?: ReactNode;\n headerActions?: ReactNode;\n ui?: AdminShellUI;\n}\n\nfunction defaultBrandLink({\n href,\n children,\n}: {\n href: string;\n children: ReactNode;\n}) {\n return (\n <a\n href={href}\n className=\"inline-flex items-center gap-2 text-sm font-semibold\"\n >\n {children}\n </a>\n );\n}\n\nexport function AdminHeader({\n viewer,\n brandLabel,\n brandHref,\n headerLinks,\n headerActions,\n ui,\n}: AdminHeaderProps) {\n const BrandLink = ui?.BrandLink ?? defaultBrandLink;\n return (\n <header className=\"border-b bg-muted/30\">\n <div className=\"container mx-auto flex h-14 max-w-6xl items-center justify-between gap-4 px-4\">\n <div className=\"flex items-center gap-4\">\n <BrandLink href={brandHref}>\n <span aria-hidden=\"true\">●</span>\n {brandLabel}\n </BrandLink>\n {headerLinks}\n </div>\n <div className=\"flex items-center gap-2\">\n <div className=\"flex items-center gap-2 rounded-md bg-muted px-2.5 py-1 text-xs\">\n {viewer.picture && (\n <img\n src={viewer.picture}\n alt=\"\"\n className=\"h-5 w-5 rounded-full\"\n />\n )}\n <span className=\"hidden font-medium sm:inline\">\n {viewer.name || viewer.email}\n </span>\n {viewer.isAdmin && (\n <span className=\"rounded bg-primary/10 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-primary\">\n Admin\n </span>\n )}\n </div>\n {headerActions}\n </div>\n </div>\n </header>\n );\n}\n","// admin/sidebar.tsx\n//\n// Vertical sidebar that renders the sorted, role-filtered nav. The\n// `renderLink` prop is supplied by `AdminShell` — it defaults to a\n// plain `<a>` and consumers can override it with their design-system\n// link primitive.\n\nimport type { ReactNode } from \"react\";\nimport type { AdminNavItem } from \"../types\";\n\nexport interface AdminSidebarLinkRenderProps {\n href: string;\n active: boolean;\n external?: boolean;\n children: ReactNode;\n}\n\nexport type AdminSidebarLinkRenderer = (\n props: AdminSidebarLinkRenderProps\n) => ReactNode;\n\nexport interface AdminSidebarProps {\n items: AdminNavItem[];\n activeHref: string;\n renderLink: AdminSidebarLinkRenderer;\n}\n\nfunction isActive(itemHref: string, currentHref: string): boolean {\n if (itemHref === currentHref) return true;\n // Treat the dashboard (\"/admin\") as active only when the current\n // path is exactly \"/admin\" — don't let it greedily match every\n // nested admin page.\n if (itemHref === \"/admin\") return currentHref === \"/admin\";\n return currentHref === itemHref || currentHref.startsWith(itemHref + \"/\");\n}\n\nexport function AdminSidebar({\n items,\n activeHref,\n renderLink,\n}: AdminSidebarProps) {\n if (items.length === 0) return null;\n return (\n <nav\n aria-label=\"Admin\"\n className=\"hidden w-56 shrink-0 md:block\"\n >\n <ul className=\"space-y-1\">\n {items.map((item) => (\n <li key={item.href}>\n {renderLink({\n href: item.href,\n active: isActive(item.href, activeHref),\n external: item.external,\n children: item.labelKey,\n })}\n </li>\n ))}\n </ul>\n </nav>\n );\n}\n","// admin/shell.tsx\n//\n// The `AdminShell` is the top-level wrapper for every page rendered\n// under `/admin`. It composes a header (brand + user info + slot for\n// the consumer's secondary links), a sidebar (built from the supplied\n// `nav` prop, sorted and role-filtered by `createAdminNav`), and a\n// main content area.\n//\n// The package stays unopinionated about the design system by taking\n// a `ui` prop with render slots for the few interactive bits (the\n// brand link, sidebar links, the logout form). Consumers wire their\n// own shadcn/radix/Tailwind components in. The defaults are plain\n// HTML elements that work without any setup.\n\nimport type { ReactNode } from \"react\";\nimport type { AdminNavItem } from \"../types\";\nimport { createAdminNav } from \"./nav\";\nimport { AdminHeader } from \"./header\";\nimport { AdminSidebar } from \"./sidebar\";\n\n/**\n * Minimal viewer shape the shell needs to render the header. The\n * consumer's full `AdminViewer` may carry more fields; only the\n * fields the shell actually reads are required here.\n */\nexport interface AdminShellViewer {\n email: string;\n name?: string | null;\n picture?: string | null;\n isAdmin?: boolean;\n role?: string | null;\n}\n\n/**\n * Slots the consumer can fill with their own design-system primitives.\n * Every slot is optional — the shell falls back to plain HTML when a\n * slot is not provided so projects without shadcn still render.\n */\nexport interface AdminShellUI {\n /** Render a brand link (e.g. the \"vinext Admin\" mark). */\n BrandLink?: (props: { href: string; children: ReactNode }) => ReactNode;\n /** Render a single sidebar entry. */\n SidebarLink?: (props: {\n href: string;\n active: boolean;\n external?: boolean;\n children: ReactNode;\n }) => ReactNode;\n /** Render a single header link/button (e.g. theme toggle, logout). */\n HeaderAction?: (props: { children: ReactNode }) => ReactNode;\n}\n\nexport interface AdminShellProps {\n /** Raw nav items. The shell sorts and role-filters them. */\n nav: AdminNavItem[];\n /** Currently authenticated viewer. */\n viewer: AdminShellViewer;\n /** Current pathname; used to mark the active sidebar entry. */\n pathname?: string;\n /** Brand label rendered in the header. Defaults to \"Admin\". */\n brandLabel?: string;\n /** Brand link target. Defaults to \"/admin\". */\n brandHref?: string;\n /** Extra links rendered in the header (e.g. \"view site\"). */\n headerLinks?: ReactNode;\n /** Extra actions rendered in the header right cluster (logout, etc.). */\n headerActions?: ReactNode;\n /** Slots for design-system primitives. */\n ui?: AdminShellUI;\n /** Roles the viewer has; controls role-gated nav items. */\n viewerRoles?: string[];\n children: ReactNode;\n}\n\n/**\n * Default `<a>` for sidebar links. Renders an external `target` when\n * the nav item is marked external.\n */\nfunction defaultSidebarLink({\n href,\n active,\n external,\n children,\n}: {\n href: string;\n active: boolean;\n external?: boolean;\n children: ReactNode;\n}) {\n if (external) {\n return (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className={\n \"block rounded-md px-3 py-2 text-sm font-medium \" +\n (active\n ? \"bg-muted text-foreground\"\n : \"text-muted-foreground hover:bg-muted/60 hover:text-foreground\")\n }\n >\n {children}\n </a>\n );\n }\n return (\n <a\n href={href}\n className={\n \"block rounded-md px-3 py-2 text-sm font-medium \" +\n (active\n ? \"bg-muted text-foreground\"\n : \"text-muted-foreground hover:bg-muted/60 hover:text-foreground\")\n }\n >\n {children}\n </a>\n );\n}\n\n/**\n * Top-level admin chrome. Re-exports the sidebar/header pieces so\n * projects that want to rearrange the layout (e.g. put the sidebar\n * on the right) can swap them in.\n */\nexport function AdminShell({\n nav,\n viewer,\n pathname,\n brandLabel = \"Admin\",\n brandHref = \"/admin\",\n headerLinks,\n headerActions,\n ui,\n viewerRoles,\n children,\n}: AdminShellProps) {\n const items = createAdminNav(nav, { roles: viewerRoles });\n const activeHref = pathname ?? \"/admin\";\n return (\n <div className=\"min-h-screen bg-background\">\n <AdminHeader\n viewer={viewer}\n brandLabel={brandLabel}\n brandHref={brandHref}\n headerLinks={headerLinks}\n headerActions={headerActions}\n ui={ui}\n />\n <div className=\"container mx-auto flex max-w-6xl gap-8 px-4 py-8\">\n <AdminSidebar\n items={items}\n activeHref={activeHref}\n renderLink={ui?.SidebarLink ?? defaultSidebarLink}\n />\n <main className=\"min-w-0 flex-1\">{children}</main>\n </div>\n </div>\n );\n}\n\nexport type { AdminNavItem };\nexport { AdminHeader } from \"./header\";\nexport { AdminSidebar } from \"./sidebar\";\n","// admin/layout.tsx\n//\n// Convenience Next.js layout that fetches the current viewer through\n// a caller-supplied async function and renders the `AdminShell`.\n// Projects that need extra wrapping (e.g. a custom redirect on\n// unauthenticated viewers) can keep using `AdminShell` directly and\n// skip this helper.\n\nimport type { ReactNode } from \"react\";\nimport { AdminShell, type AdminShellViewer, type AdminShellUI } from \"./shell\";\nimport type { AdminNavItem } from \"../types\";\n\nexport interface AdminLayoutProps {\n nav: AdminNavItem[];\n /** Resolves the currently authenticated viewer. Return `null` for guests. */\n getViewer: () => Promise<AdminShellViewer | null>;\n /** Path the user is redirected to when `getViewer` returns `null`. */\n loginPath?: string;\n /** Current pathname; used to mark the active sidebar entry. */\n pathname?: string;\n brandLabel?: string;\n brandHref?: string;\n headerLinks?: ReactNode;\n headerActions?: ReactNode;\n ui?: AdminShellUI;\n /**\n * Roles the viewer has. Pass `viewer.isAdmin ? [\"admin\"] : [\"user\"]`\n * (or your role catalog) to enable role-based nav filtering.\n */\n viewerRoles?: string[];\n children: ReactNode;\n}\n\n/**\n * Async Next.js layout. If the viewer is missing, renders a minimal\n * \"Redirecting…\" placeholder and the consumer's `_redirect` mechanism\n * (set `loginPath` and let the consumer wrap in a `redirect()` from\n * `next/navigation`). For simpler use, pass a `getViewer` that throws\n * a redirect.\n */\nexport async function AdminLayout({\n nav,\n getViewer,\n loginPath = \"/login\",\n pathname,\n brandLabel,\n brandHref,\n headerLinks,\n headerActions,\n ui,\n viewerRoles,\n children,\n}: AdminLayoutProps) {\n const viewer = await getViewer();\n if (!viewer) {\n // The consumer should pass a `getViewer` that throws/redirects\n // when the user is unauthenticated. This branch is a safety net\n // for callers that prefer a non-throwing getter.\n return (\n <div className=\"flex min-h-screen items-center justify-center text-sm text-muted-foreground\">\n Redirecting to{\" \"}\n <a className=\"ml-1 underline\" href={loginPath}>\n {loginPath}\n </a>\n …\n </div>\n );\n }\n return (\n <AdminShell\n nav={nav}\n viewer={viewer}\n pathname={pathname}\n brandLabel={brandLabel}\n brandHref={brandHref}\n headerLinks={headerLinks}\n headerActions={headerActions}\n ui={ui}\n viewerRoles={viewerRoles}\n >\n {children}\n </AdminShell>\n );\n}\n"],"mappings":"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","names":["jsx","jsx","jsxs","jsx","jsxs"]}
|