@nubitio/admin 0.5.22 → 0.5.23

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/index.cjs CHANGED
@@ -389,14 +389,14 @@ const AdminShell = ({ title, menuItems, headerActions, renderUserMenu, renderThe
389
389
  //#endregion
390
390
  //#region packages/admin/auth/SessionContext.tsx
391
391
  const SessionContext = (0, react.createContext)(null);
392
- function joinApiPath$2(apiBaseUrl, path) {
392
+ function joinApiPath$3(apiBaseUrl, path) {
393
393
  return `${apiBaseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
394
394
  }
395
395
  function SessionProvider({ apiBaseUrl = "/api/", mePath = "me", logoutPath = "auth/logout", children }) {
396
396
  const [session, setSession] = (0, react.useState)({ status: "loading" });
397
397
  const refresh = (0, react.useCallback)(async () => {
398
398
  try {
399
- const response = await fetch(joinApiPath$2(apiBaseUrl, mePath), { credentials: "include" });
399
+ const response = await fetch(joinApiPath$3(apiBaseUrl, mePath), { credentials: "include" });
400
400
  if (!response.ok) {
401
401
  setSession({ status: "anonymous" });
402
402
  return;
@@ -413,7 +413,7 @@ function SessionProvider({ apiBaseUrl = "/api/", mePath = "me", logoutPath = "au
413
413
  refresh();
414
414
  }, [refresh]);
415
415
  const logout = (0, react.useCallback)(async () => {
416
- await fetch(joinApiPath$2(apiBaseUrl, logoutPath), {
416
+ await fetch(joinApiPath$3(apiBaseUrl, logoutPath), {
417
417
  method: "POST",
418
418
  credentials: "include"
419
419
  });
@@ -482,7 +482,7 @@ function FeatureGate({ featureKey, ...props }) {
482
482
  }
483
483
  //#endregion
484
484
  //#region packages/admin/auth/LoginPage.tsx
485
- function joinApiPath$1(apiBaseUrl, path) {
485
+ function joinApiPath$2(apiBaseUrl, path) {
486
486
  return `${apiBaseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
487
487
  }
488
488
  function LoginPage({ onLoggedIn, apiBaseUrl = "/api/", loginPath = "auth/login", title = "Nubit Admin", hint, defaultUsername = "" }) {
@@ -495,7 +495,7 @@ function LoginPage({ onLoggedIn, apiBaseUrl = "/api/", loginPath = "auth/login",
495
495
  setBusy(true);
496
496
  setError(null);
497
497
  try {
498
- const response = await fetch(joinApiPath$1(apiBaseUrl, loginPath), {
498
+ const response = await fetch(joinApiPath$2(apiBaseUrl, loginPath), {
499
499
  method: "POST",
500
500
  headers: { "Content-Type": "application/json" },
501
501
  credentials: "include",
@@ -573,6 +573,125 @@ function LoginPage({ onLoggedIn, apiBaseUrl = "/api/", loginPath = "auth/login",
573
573
  });
574
574
  }
575
575
  //#endregion
576
+ //#region packages/admin/auth/RegisterPage.tsx
577
+ function joinApiPath$1(apiBaseUrl, path) {
578
+ return `${apiBaseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
579
+ }
580
+ function initialValues(fields) {
581
+ return Object.fromEntries(fields.map((field) => [field.name, field.defaultValue ?? ""]));
582
+ }
583
+ function RegisterPage({ fields, onRegistered, apiBaseUrl = "/api/", registerPath = "auth/register", title = "Create account", hint, submitLabel = "Create account", busyLabel = "Creating…", loginLink, loginPrompt = "Already have an account?" }) {
584
+ const [values, setValues] = (0, react.useState)((0, react.useMemo)(() => initialValues(fields), [fields]));
585
+ const [error, setError] = (0, react.useState)(null);
586
+ const [busy, setBusy] = (0, react.useState)(false);
587
+ const setValue = (name, value) => {
588
+ setValues((current) => ({
589
+ ...current,
590
+ [name]: value
591
+ }));
592
+ };
593
+ const submit = async (event) => {
594
+ event.preventDefault();
595
+ setBusy(true);
596
+ setError(null);
597
+ try {
598
+ const response = await fetch(joinApiPath$1(apiBaseUrl, registerPath), {
599
+ method: "POST",
600
+ headers: { "Content-Type": "application/json" },
601
+ credentials: "include",
602
+ body: JSON.stringify(values)
603
+ });
604
+ const body = await response.json().catch(() => null);
605
+ if (!response.ok) {
606
+ setError(body?.error ?? body?.message ?? "Registration failed");
607
+ return;
608
+ }
609
+ onRegistered();
610
+ } catch {
611
+ setError("Network error");
612
+ } finally {
613
+ setBusy(false);
614
+ }
615
+ };
616
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
617
+ style: {
618
+ display: "grid",
619
+ placeItems: "center",
620
+ minHeight: "100vh"
621
+ },
622
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_nubitio_ui.Card, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
623
+ onSubmit: submit,
624
+ style: {
625
+ display: "flex",
626
+ flexDirection: "column",
627
+ gap: 12,
628
+ width: 360,
629
+ padding: 8
630
+ },
631
+ children: [
632
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
633
+ style: { margin: 0 },
634
+ children: title
635
+ }),
636
+ hint && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
637
+ style: {
638
+ margin: 0,
639
+ color: "var(--text-secondary)"
640
+ },
641
+ children: hint
642
+ }),
643
+ fields.map((field) => {
644
+ const type = field.type ?? "text";
645
+ if (type === "select") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
646
+ value: values[field.name] ?? "",
647
+ onChange: (e) => setValue(field.name, e.target.value),
648
+ style: { width: "100%" },
649
+ "aria-label": field.placeholder ?? field.name,
650
+ children: (field.options ?? []).map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
651
+ value: option.value,
652
+ children: option.label
653
+ }, option.value))
654
+ }, field.name);
655
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_nubitio_ui.TextField, {
656
+ placeholder: field.placeholder,
657
+ type: type === "password" ? "password" : type === "email" ? "email" : "text",
658
+ value: values[field.name] ?? "",
659
+ autoComplete: field.autoComplete,
660
+ onChange: (e) => setValue(field.name, e.target.value)
661
+ }, field.name);
662
+ }),
663
+ error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
664
+ style: {
665
+ margin: 0,
666
+ color: "var(--error-color, #dc2626)"
667
+ },
668
+ children: error
669
+ }),
670
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_nubitio_ui.Button, {
671
+ variant: "primary",
672
+ type: "submit",
673
+ disabled: busy,
674
+ children: busy ? busyLabel : submitLabel
675
+ }),
676
+ loginLink && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
677
+ style: {
678
+ margin: 0,
679
+ color: "var(--text-secondary)"
680
+ },
681
+ children: [
682
+ loginPrompt,
683
+ " ",
684
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_router_dom.Link, {
685
+ to: loginLink.to,
686
+ children: loginLink.label
687
+ })
688
+ ]
689
+ })
690
+ ]
691
+ }) })
692
+ });
693
+ }
694
+ //#endregion
576
695
  //#region packages/admin/runtime/useAppRuntime.ts
577
696
  function useAppRuntime() {
578
697
  const [toasts, setToasts] = (0, react.useState)([]);
@@ -842,6 +961,7 @@ exports.AdminShell = AdminShell;
842
961
  exports.AdminSidebarMenu = AdminSidebarMenu;
843
962
  exports.FeatureGate = FeatureGate;
844
963
  exports.LoginPage = LoginPage;
964
+ exports.RegisterPage = RegisterPage;
845
965
  exports.SessionProvider = SessionProvider;
846
966
  exports.StaticSessionProvider = StaticSessionProvider;
847
967
  exports.ToastHost = ToastHost;
package/dist/index.d.cts CHANGED
@@ -186,6 +186,49 @@ declare function LoginPage({
186
186
  defaultUsername
187
187
  }: LoginPageProps): import("react").JSX.Element;
188
188
  //#endregion
189
+ //#region packages/admin/auth/RegisterPage.d.ts
190
+ type RegisterFieldType = 'text' | 'email' | 'password' | 'select';
191
+ interface RegisterSelectOption {
192
+ value: string;
193
+ label: string;
194
+ }
195
+ interface RegisterField {
196
+ /** JSON body key sent to the register endpoint. */
197
+ name: string;
198
+ placeholder?: string;
199
+ type?: RegisterFieldType;
200
+ defaultValue?: string;
201
+ autoComplete?: string;
202
+ options?: RegisterSelectOption[];
203
+ }
204
+ interface RegisterPageProps {
205
+ fields: RegisterField[];
206
+ onRegistered: () => void;
207
+ apiBaseUrl?: string;
208
+ registerPath?: string;
209
+ title?: string;
210
+ hint?: string;
211
+ submitLabel?: string;
212
+ busyLabel?: string;
213
+ loginLink?: {
214
+ to: string;
215
+ label: string;
216
+ };
217
+ loginPrompt?: string;
218
+ }
219
+ declare function RegisterPage({
220
+ fields,
221
+ onRegistered,
222
+ apiBaseUrl,
223
+ registerPath,
224
+ title,
225
+ hint,
226
+ submitLabel,
227
+ busyLabel,
228
+ loginLink,
229
+ loginPrompt
230
+ }: RegisterPageProps): import("react").JSX.Element;
231
+ //#endregion
189
232
  //#region packages/admin/runtime/useAppRuntime.d.ts
190
233
  type NotificationType = 'success' | 'error' | 'warning' | 'info';
191
234
  type ToastItem = {
@@ -307,4 +350,4 @@ declare function hasAnyRole(required: string | string[] | undefined, roles: stri
307
350
  */
308
351
  declare function filterMenuByRoles(items: NubitAppMenuItem[], roles: string[]): AdminMenuItem[];
309
352
  //#endregion
310
- export { AdminHeader, type AdminHeaderAction, type AdminHeaderProps, type AdminMenuItem, type AdminMenuSubItem, AdminShell, type AdminShellProps, AdminSidebarMenu, type AdminSidebarMenuProps, type AdminSidebarMenuSelectEvent, type AppProfile, type CreateNubitAppConfig, FeatureGate, type FeatureGateProps, LoginPage, type LoginPageProps, type NotificationType, type NubitApp, type NubitAppMenuContext, type NubitAppMenuItem, type NubitAppMenuSubItem, type NubitAppRoute, type NubitAppUserMenuContext, type RuntimeConfig, type RuntimeConfigState, type SessionContextValue, type SessionFeatureEntitlement, type SessionProfile, SessionProvider, type SessionProviderConfig, type SessionState, type SessionTenant, StaticSessionProvider, ToastHost, type ToastHostProps, type ToastItem, type UseRuntimeConfigOptions, createNubitApp, filterMenuByRoles, hasAnyRole, useAppRuntime, useFeature, useFeatureConfig, useRuntimeConfig, useScreenSize, useScreenSizeClass, useSession };
353
+ export { AdminHeader, type AdminHeaderAction, type AdminHeaderProps, type AdminMenuItem, type AdminMenuSubItem, AdminShell, type AdminShellProps, AdminSidebarMenu, type AdminSidebarMenuProps, type AdminSidebarMenuSelectEvent, type AppProfile, type CreateNubitAppConfig, FeatureGate, type FeatureGateProps, LoginPage, type LoginPageProps, type NotificationType, type NubitApp, type NubitAppMenuContext, type NubitAppMenuItem, type NubitAppMenuSubItem, type NubitAppRoute, type NubitAppUserMenuContext, type RegisterField, type RegisterFieldType, RegisterPage, type RegisterPageProps, type RegisterSelectOption, type RuntimeConfig, type RuntimeConfigState, type SessionContextValue, type SessionFeatureEntitlement, type SessionProfile, SessionProvider, type SessionProviderConfig, type SessionState, type SessionTenant, StaticSessionProvider, ToastHost, type ToastHostProps, type ToastItem, type UseRuntimeConfigOptions, createNubitApp, filterMenuByRoles, hasAnyRole, useAppRuntime, useFeature, useFeatureConfig, useRuntimeConfig, useScreenSize, useScreenSizeClass, useSession };
package/dist/index.d.mts CHANGED
@@ -186,6 +186,49 @@ declare function LoginPage({
186
186
  defaultUsername
187
187
  }: LoginPageProps): import("react").JSX.Element;
188
188
  //#endregion
189
+ //#region packages/admin/auth/RegisterPage.d.ts
190
+ type RegisterFieldType = 'text' | 'email' | 'password' | 'select';
191
+ interface RegisterSelectOption {
192
+ value: string;
193
+ label: string;
194
+ }
195
+ interface RegisterField {
196
+ /** JSON body key sent to the register endpoint. */
197
+ name: string;
198
+ placeholder?: string;
199
+ type?: RegisterFieldType;
200
+ defaultValue?: string;
201
+ autoComplete?: string;
202
+ options?: RegisterSelectOption[];
203
+ }
204
+ interface RegisterPageProps {
205
+ fields: RegisterField[];
206
+ onRegistered: () => void;
207
+ apiBaseUrl?: string;
208
+ registerPath?: string;
209
+ title?: string;
210
+ hint?: string;
211
+ submitLabel?: string;
212
+ busyLabel?: string;
213
+ loginLink?: {
214
+ to: string;
215
+ label: string;
216
+ };
217
+ loginPrompt?: string;
218
+ }
219
+ declare function RegisterPage({
220
+ fields,
221
+ onRegistered,
222
+ apiBaseUrl,
223
+ registerPath,
224
+ title,
225
+ hint,
226
+ submitLabel,
227
+ busyLabel,
228
+ loginLink,
229
+ loginPrompt
230
+ }: RegisterPageProps): import("react").JSX.Element;
231
+ //#endregion
189
232
  //#region packages/admin/runtime/useAppRuntime.d.ts
190
233
  type NotificationType = 'success' | 'error' | 'warning' | 'info';
191
234
  type ToastItem = {
@@ -307,4 +350,4 @@ declare function hasAnyRole(required: string | string[] | undefined, roles: stri
307
350
  */
308
351
  declare function filterMenuByRoles(items: NubitAppMenuItem[], roles: string[]): AdminMenuItem[];
309
352
  //#endregion
310
- export { AdminHeader, type AdminHeaderAction, type AdminHeaderProps, type AdminMenuItem, type AdminMenuSubItem, AdminShell, type AdminShellProps, AdminSidebarMenu, type AdminSidebarMenuProps, type AdminSidebarMenuSelectEvent, type AppProfile, type CreateNubitAppConfig, FeatureGate, type FeatureGateProps, LoginPage, type LoginPageProps, type NotificationType, type NubitApp, type NubitAppMenuContext, type NubitAppMenuItem, type NubitAppMenuSubItem, type NubitAppRoute, type NubitAppUserMenuContext, type RuntimeConfig, type RuntimeConfigState, type SessionContextValue, type SessionFeatureEntitlement, type SessionProfile, SessionProvider, type SessionProviderConfig, type SessionState, type SessionTenant, StaticSessionProvider, ToastHost, type ToastHostProps, type ToastItem, type UseRuntimeConfigOptions, createNubitApp, filterMenuByRoles, hasAnyRole, useAppRuntime, useFeature, useFeatureConfig, useRuntimeConfig, useScreenSize, useScreenSizeClass, useSession };
353
+ export { AdminHeader, type AdminHeaderAction, type AdminHeaderProps, type AdminMenuItem, type AdminMenuSubItem, AdminShell, type AdminShellProps, AdminSidebarMenu, type AdminSidebarMenuProps, type AdminSidebarMenuSelectEvent, type AppProfile, type CreateNubitAppConfig, FeatureGate, type FeatureGateProps, LoginPage, type LoginPageProps, type NotificationType, type NubitApp, type NubitAppMenuContext, type NubitAppMenuItem, type NubitAppMenuSubItem, type NubitAppRoute, type NubitAppUserMenuContext, type RegisterField, type RegisterFieldType, RegisterPage, type RegisterPageProps, type RegisterSelectOption, type RuntimeConfig, type RuntimeConfigState, type SessionContextValue, type SessionFeatureEntitlement, type SessionProfile, SessionProvider, type SessionProviderConfig, type SessionState, type SessionTenant, StaticSessionProvider, ToastHost, type ToastHostProps, type ToastItem, type UseRuntimeConfigOptions, createNubitApp, filterMenuByRoles, hasAnyRole, useAppRuntime, useFeature, useFeatureConfig, useRuntimeConfig, useScreenSize, useScreenSizeClass, useSession };
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
2
- import { BrowserRouter, Navigate, Route, Routes, useLocation, useNavigate } from "react-router-dom";
2
+ import { BrowserRouter, Link, Navigate, Route, Routes, useLocation, useNavigate } from "react-router-dom";
3
3
  import { Badge, Button, Card, FeatureGate as FeatureGate$1, IconButton, TextField, ThemeProvider, ThemeSwitcher, useFloatingPanel } from "@nubitio/ui";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
@@ -365,14 +365,14 @@ const AdminShell = ({ title, menuItems, headerActions, renderUserMenu, renderThe
365
365
  //#endregion
366
366
  //#region packages/admin/auth/SessionContext.tsx
367
367
  const SessionContext = createContext(null);
368
- function joinApiPath$2(apiBaseUrl, path) {
368
+ function joinApiPath$3(apiBaseUrl, path) {
369
369
  return `${apiBaseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
370
370
  }
371
371
  function SessionProvider({ apiBaseUrl = "/api/", mePath = "me", logoutPath = "auth/logout", children }) {
372
372
  const [session, setSession] = useState({ status: "loading" });
373
373
  const refresh = useCallback(async () => {
374
374
  try {
375
- const response = await fetch(joinApiPath$2(apiBaseUrl, mePath), { credentials: "include" });
375
+ const response = await fetch(joinApiPath$3(apiBaseUrl, mePath), { credentials: "include" });
376
376
  if (!response.ok) {
377
377
  setSession({ status: "anonymous" });
378
378
  return;
@@ -389,7 +389,7 @@ function SessionProvider({ apiBaseUrl = "/api/", mePath = "me", logoutPath = "au
389
389
  refresh();
390
390
  }, [refresh]);
391
391
  const logout = useCallback(async () => {
392
- await fetch(joinApiPath$2(apiBaseUrl, logoutPath), {
392
+ await fetch(joinApiPath$3(apiBaseUrl, logoutPath), {
393
393
  method: "POST",
394
394
  credentials: "include"
395
395
  });
@@ -458,7 +458,7 @@ function FeatureGate({ featureKey, ...props }) {
458
458
  }
459
459
  //#endregion
460
460
  //#region packages/admin/auth/LoginPage.tsx
461
- function joinApiPath$1(apiBaseUrl, path) {
461
+ function joinApiPath$2(apiBaseUrl, path) {
462
462
  return `${apiBaseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
463
463
  }
464
464
  function LoginPage({ onLoggedIn, apiBaseUrl = "/api/", loginPath = "auth/login", title = "Nubit Admin", hint, defaultUsername = "" }) {
@@ -471,7 +471,7 @@ function LoginPage({ onLoggedIn, apiBaseUrl = "/api/", loginPath = "auth/login",
471
471
  setBusy(true);
472
472
  setError(null);
473
473
  try {
474
- const response = await fetch(joinApiPath$1(apiBaseUrl, loginPath), {
474
+ const response = await fetch(joinApiPath$2(apiBaseUrl, loginPath), {
475
475
  method: "POST",
476
476
  headers: { "Content-Type": "application/json" },
477
477
  credentials: "include",
@@ -549,6 +549,125 @@ function LoginPage({ onLoggedIn, apiBaseUrl = "/api/", loginPath = "auth/login",
549
549
  });
550
550
  }
551
551
  //#endregion
552
+ //#region packages/admin/auth/RegisterPage.tsx
553
+ function joinApiPath$1(apiBaseUrl, path) {
554
+ return `${apiBaseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
555
+ }
556
+ function initialValues(fields) {
557
+ return Object.fromEntries(fields.map((field) => [field.name, field.defaultValue ?? ""]));
558
+ }
559
+ function RegisterPage({ fields, onRegistered, apiBaseUrl = "/api/", registerPath = "auth/register", title = "Create account", hint, submitLabel = "Create account", busyLabel = "Creating…", loginLink, loginPrompt = "Already have an account?" }) {
560
+ const [values, setValues] = useState(useMemo(() => initialValues(fields), [fields]));
561
+ const [error, setError] = useState(null);
562
+ const [busy, setBusy] = useState(false);
563
+ const setValue = (name, value) => {
564
+ setValues((current) => ({
565
+ ...current,
566
+ [name]: value
567
+ }));
568
+ };
569
+ const submit = async (event) => {
570
+ event.preventDefault();
571
+ setBusy(true);
572
+ setError(null);
573
+ try {
574
+ const response = await fetch(joinApiPath$1(apiBaseUrl, registerPath), {
575
+ method: "POST",
576
+ headers: { "Content-Type": "application/json" },
577
+ credentials: "include",
578
+ body: JSON.stringify(values)
579
+ });
580
+ const body = await response.json().catch(() => null);
581
+ if (!response.ok) {
582
+ setError(body?.error ?? body?.message ?? "Registration failed");
583
+ return;
584
+ }
585
+ onRegistered();
586
+ } catch {
587
+ setError("Network error");
588
+ } finally {
589
+ setBusy(false);
590
+ }
591
+ };
592
+ return /* @__PURE__ */ jsx("div", {
593
+ style: {
594
+ display: "grid",
595
+ placeItems: "center",
596
+ minHeight: "100vh"
597
+ },
598
+ children: /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs("form", {
599
+ onSubmit: submit,
600
+ style: {
601
+ display: "flex",
602
+ flexDirection: "column",
603
+ gap: 12,
604
+ width: 360,
605
+ padding: 8
606
+ },
607
+ children: [
608
+ /* @__PURE__ */ jsx("h2", {
609
+ style: { margin: 0 },
610
+ children: title
611
+ }),
612
+ hint && /* @__PURE__ */ jsx("p", {
613
+ style: {
614
+ margin: 0,
615
+ color: "var(--text-secondary)"
616
+ },
617
+ children: hint
618
+ }),
619
+ fields.map((field) => {
620
+ const type = field.type ?? "text";
621
+ if (type === "select") return /* @__PURE__ */ jsx("select", {
622
+ value: values[field.name] ?? "",
623
+ onChange: (e) => setValue(field.name, e.target.value),
624
+ style: { width: "100%" },
625
+ "aria-label": field.placeholder ?? field.name,
626
+ children: (field.options ?? []).map((option) => /* @__PURE__ */ jsx("option", {
627
+ value: option.value,
628
+ children: option.label
629
+ }, option.value))
630
+ }, field.name);
631
+ return /* @__PURE__ */ jsx(TextField, {
632
+ placeholder: field.placeholder,
633
+ type: type === "password" ? "password" : type === "email" ? "email" : "text",
634
+ value: values[field.name] ?? "",
635
+ autoComplete: field.autoComplete,
636
+ onChange: (e) => setValue(field.name, e.target.value)
637
+ }, field.name);
638
+ }),
639
+ error && /* @__PURE__ */ jsx("p", {
640
+ style: {
641
+ margin: 0,
642
+ color: "var(--error-color, #dc2626)"
643
+ },
644
+ children: error
645
+ }),
646
+ /* @__PURE__ */ jsx(Button, {
647
+ variant: "primary",
648
+ type: "submit",
649
+ disabled: busy,
650
+ children: busy ? busyLabel : submitLabel
651
+ }),
652
+ loginLink && /* @__PURE__ */ jsxs("p", {
653
+ style: {
654
+ margin: 0,
655
+ color: "var(--text-secondary)"
656
+ },
657
+ children: [
658
+ loginPrompt,
659
+ " ",
660
+ /* @__PURE__ */ jsx(Link, {
661
+ to: loginLink.to,
662
+ children: loginLink.label
663
+ })
664
+ ]
665
+ })
666
+ ]
667
+ }) })
668
+ });
669
+ }
670
+ //#endregion
552
671
  //#region packages/admin/runtime/useAppRuntime.ts
553
672
  function useAppRuntime() {
554
673
  const [toasts, setToasts] = useState([]);
@@ -813,4 +932,4 @@ function createNubitApp(config) {
813
932
  return { App };
814
933
  }
815
934
  //#endregion
816
- export { AdminHeader, AdminShell, AdminSidebarMenu, FeatureGate, LoginPage, SessionProvider, StaticSessionProvider, ToastHost, createNubitApp, filterMenuByRoles, hasAnyRole, useAppRuntime, useFeature, useFeatureConfig, useRuntimeConfig, useScreenSize, useScreenSizeClass, useSession };
935
+ export { AdminHeader, AdminShell, AdminSidebarMenu, FeatureGate, LoginPage, RegisterPage, SessionProvider, StaticSessionProvider, ToastHost, createNubitApp, filterMenuByRoles, hasAnyRole, useAppRuntime, useFeature, useFeatureConfig, useRuntimeConfig, useScreenSize, useScreenSizeClass, useSession };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nubitio/admin",
3
- "version": "0.5.22",
3
+ "version": "0.5.23",
4
4
  "type": "module",
5
5
  "description": "Admin shell layout components: responsive sidebar, header, and screen size utilities for Nubit apps.",
6
6
  "license": "MIT",
@@ -53,9 +53,9 @@
53
53
  "react": "^19.0.0",
54
54
  "react-dom": "^19.0.0",
55
55
  "react-router-dom": "^6.0.0",
56
- "@nubitio/core": "^0.5.22",
57
- "@nubitio/crud": "^0.5.22",
58
- "@nubitio/hydra": "^0.5.22",
59
- "@nubitio/ui": "^0.5.22"
56
+ "@nubitio/crud": "^0.5.23",
57
+ "@nubitio/core": "^0.5.23",
58
+ "@nubitio/hydra": "^0.5.23",
59
+ "@nubitio/ui": "^0.5.23"
60
60
  }
61
61
  }