@nubitio/admin 0.5.30 → 0.6.1

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
@@ -1047,6 +1047,158 @@ function ToastHost({ toasts, onDismiss }) {
1047
1047
  });
1048
1048
  }
1049
1049
  //#endregion
1050
+ //#region packages/admin/devtools/NubitDevToolsPanel.tsx
1051
+ const PROVIDER_TREE = [
1052
+ "MercureProvider",
1053
+ "SchemaProvider",
1054
+ "HydraResourceSchemaProvider",
1055
+ "HydraResourceStoreProvider",
1056
+ "SmartCrudRolesProvider",
1057
+ "CoreProvider"
1058
+ ];
1059
+ function NubitDevToolsPanel() {
1060
+ const { enabled, resources, clearResources } = (0, _nubitio_crud.useDevTools)();
1061
+ const [open, setOpen] = (0, react.useState)(false);
1062
+ const [expandedUrl, setExpandedUrl] = (0, react.useState)(null);
1063
+ if (!enabled || !(0, _nubitio_crud.isDevEnvironment)()) return null;
1064
+ const active = expandedUrl ? resources.find((r) => r.apiUrl === expandedUrl) : resources[0];
1065
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1066
+ className: `nb-devtools ${open ? "nb-devtools--open" : ""}`,
1067
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1068
+ type: "button",
1069
+ className: "nb-devtools__toggle",
1070
+ onClick: () => setOpen((v) => !v),
1071
+ "aria-expanded": open,
1072
+ title: "Nubit DevTools",
1073
+ children: "Nubit"
1074
+ }), open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1075
+ className: "nb-devtools__panel",
1076
+ role: "complementary",
1077
+ "aria-label": "Nubit DevTools",
1078
+ children: [
1079
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
1080
+ className: "nb-devtools__header",
1081
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "Nubit DevTools" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1082
+ type: "button",
1083
+ className: "nb-devtools__clear",
1084
+ onClick: clearResources,
1085
+ children: "Clear"
1086
+ })]
1087
+ }),
1088
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1089
+ className: "nb-devtools__section",
1090
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", { children: "Provider tree" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
1091
+ className: "nb-devtools__providers",
1092
+ children: PROVIDER_TREE.map((name) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: name }, name))
1093
+ })]
1094
+ }),
1095
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1096
+ className: "nb-devtools__section",
1097
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h4", { children: [
1098
+ "Active resources (",
1099
+ resources.length,
1100
+ ")"
1101
+ ] }), resources.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1102
+ className: "nb-devtools__empty",
1103
+ children: "Open a SchemaCrudPage to inspect field resolution."
1104
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1105
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
1106
+ className: "nb-devtools__select",
1107
+ value: active?.apiUrl ?? "",
1108
+ onChange: (e) => setExpandedUrl(e.target.value),
1109
+ children: resources.map((r) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1110
+ value: r.apiUrl,
1111
+ children: r.apiUrl
1112
+ }, r.apiUrl))
1113
+ }),
1114
+ active && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", {
1115
+ className: "nb-devtools__meta",
1116
+ children: [
1117
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: "Source" }),
1118
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: active.source }),
1119
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: "Operations" }),
1120
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: active.supportedOperations.join(", ") || "—" }),
1121
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: "Permissions" }),
1122
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: active.permissionsSource ?? "—" })
1123
+ ]
1124
+ }),
1125
+ active?.formDetail && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
1126
+ className: "nb-devtools__subsection",
1127
+ children: "Form detail (lines)"
1128
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", {
1129
+ className: "nb-devtools__meta",
1130
+ children: [
1131
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: "Line fields" }),
1132
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: active.formDetail.fieldSource }),
1133
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: "Property" }),
1134
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: active.formDetail.propertyName ?? "—" }),
1135
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: "Reload URL" }),
1136
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", {
1137
+ className: "nb-devtools__mapping",
1138
+ children: active.formDetail.reloadUrl ?? "—"
1139
+ }),
1140
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: "Line class" }),
1141
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: active.formDetail.lineClass ?? "—" }),
1142
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: "x-embedded-lines" }),
1143
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: active.formDetail.embeddedLinesCount })
1144
+ ]
1145
+ })] }),
1146
+ active && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
1147
+ className: "nb-devtools__subsection",
1148
+ children: "Grid fields"
1149
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
1150
+ className: "nb-devtools__table",
1151
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
1152
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "Field" }),
1153
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "Type" }),
1154
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "Mapping" })
1155
+ ] }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: active.fields.filter((f) => !f.isIdentity).map((f) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
1156
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: f.name }),
1157
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: f.type }),
1158
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
1159
+ className: "nb-devtools__mapping",
1160
+ children: f.mappingReason ?? "—"
1161
+ })
1162
+ ] }, f.name)) })]
1163
+ })] }),
1164
+ active?.formDetail && active.formDetail.lineFieldCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h4", {
1165
+ className: "nb-devtools__subsection",
1166
+ children: [
1167
+ "Line fields (",
1168
+ active.formDetail.lineFieldCount,
1169
+ ")"
1170
+ ]
1171
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
1172
+ className: "nb-devtools__table",
1173
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
1174
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "Field" }),
1175
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "Type" }),
1176
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "Mapping" })
1177
+ ] }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: active.formDetail.lineFields.filter((f) => !f.isIdentity).map((f) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
1178
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: f.name }),
1179
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: f.type }),
1180
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
1181
+ className: "nb-devtools__mapping",
1182
+ children: f.mappingReason ?? "—"
1183
+ })
1184
+ ] }, f.name)) })]
1185
+ })] })
1186
+ ] })]
1187
+ }),
1188
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("footer", {
1189
+ className: "nb-devtools__footer",
1190
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
1191
+ href: "https://github.com/nubitio/nubit-react/blob/main/docs/architecture/README.md",
1192
+ target: "_blank",
1193
+ rel: "noreferrer",
1194
+ children: "Architecture docs"
1195
+ })
1196
+ })
1197
+ ]
1198
+ })]
1199
+ });
1200
+ }
1201
+ //#endregion
1050
1202
  //#region packages/admin/app/filterMenuByRoles.ts
1051
1203
  function hasAnyRole(required, roles) {
1052
1204
  if (required === void 0) return true;
@@ -1167,24 +1319,32 @@ function NubitAuthenticatedApp({ config }) {
1167
1319
  element: route.element
1168
1320
  }, route.path))] })
1169
1321
  });
1170
- const authenticated = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_nubitio_core.CoreProvider, {
1171
- http: {
1172
- baseUrl: apiBaseUrl,
1173
- refreshPath: "auth/refresh",
1174
- loginPath: "auth/login"
1175
- },
1176
- runtime,
1177
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_nubitio_core.CoreConfigProvider, {
1178
- apiBaseUrl,
1179
- locale: config.locale ?? "en",
1180
- timezone: config.timezone ?? "UTC",
1181
- currency: config.currency ?? "USD",
1182
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_nubitio_crud.SmartCrudRolesProvider, {
1183
- roles: session.roles,
1184
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_router_dom.BrowserRouter, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Wrapper, { children: config.hydra === false ? shell : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_nubitio_core.MercureProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_nubitio_hydra.SchemaProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_nubitio_hydra.HydraResourceSchemaProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_nubitio_hydra.HydraResourceStoreProvider, { children: shell }) }) }) }) }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToastHost, {
1185
- toasts,
1186
- onDismiss: dismiss
1187
- })]
1322
+ const devToolsEnabled = config.devTools ?? (0, _nubitio_crud.isDevEnvironment)();
1323
+ const authenticated = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_nubitio_crud.DevToolsProvider, {
1324
+ enabled: devToolsEnabled,
1325
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_nubitio_core.CoreProvider, {
1326
+ http: {
1327
+ baseUrl: apiBaseUrl,
1328
+ refreshPath: "auth/refresh",
1329
+ loginPath: "auth/login"
1330
+ },
1331
+ runtime,
1332
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_nubitio_core.CoreConfigProvider, {
1333
+ apiBaseUrl,
1334
+ locale: config.locale ?? "en",
1335
+ timezone: config.timezone ?? "UTC",
1336
+ currency: config.currency ?? "USD",
1337
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_nubitio_crud.SmartCrudRolesProvider, {
1338
+ roles: session.roles,
1339
+ children: [
1340
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_router_dom.BrowserRouter, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Wrapper, { children: config.hydra === false ? shell : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_nubitio_core.MercureProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_nubitio_hydra.SchemaProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_nubitio_hydra.HydraResourceSchemaProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_nubitio_hydra.HydraResourceStoreProvider, { children: shell }) }) }) }) }) }),
1341
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToastHost, {
1342
+ toasts,
1343
+ onDismiss: dismiss
1344
+ }),
1345
+ devToolsEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NubitDevToolsPanel, {})
1346
+ ]
1347
+ })
1188
1348
  })
1189
1349
  })
1190
1350
  });
@@ -1221,6 +1381,7 @@ exports.AdminSidebarMenu = AdminSidebarMenu;
1221
1381
  exports.FeatureGate = FeatureGate;
1222
1382
  exports.FeatureHubLayout = FeatureHubLayout;
1223
1383
  exports.LoginPage = LoginPage;
1384
+ exports.NubitDevToolsPanel = NubitDevToolsPanel;
1224
1385
  exports.PlanPanel = PlanPanel;
1225
1386
  exports.QuotaUsageBanner = QuotaUsageBanner;
1226
1387
  exports.RegisterPage = RegisterPage;
package/dist/index.d.cts CHANGED
@@ -470,6 +470,11 @@ type CreateNubitAppConfig = {
470
470
  }>;
471
471
  queryClient?: QueryClient; /** Register Mercure + Hydra schema/store providers. Default true. */
472
472
  hydra?: boolean;
473
+ /**
474
+ * Show the Nubit DevTools panel (field mapping, provider tree).
475
+ * Default true on localhost; set false to disable.
476
+ */
477
+ devTools?: boolean;
473
478
  };
474
479
  type NubitApp = {
475
480
  App: React.ComponentType;
@@ -478,6 +483,9 @@ type NubitApp = {
478
483
  //#region packages/admin/app/createNubitApp.d.ts
479
484
  declare function createNubitApp(config: CreateNubitAppConfig): NubitApp;
480
485
  //#endregion
486
+ //#region packages/admin/devtools/NubitDevToolsPanel.d.ts
487
+ declare function NubitDevToolsPanel(): React$1.JSX.Element | null;
488
+ //#endregion
481
489
  //#region packages/admin/app/filterMenuByRoles.d.ts
482
490
  declare function hasAnyRole(required: string | string[] | undefined, roles: string[]): boolean;
483
491
  /**
@@ -486,4 +494,4 @@ declare function hasAnyRole(required: string | string[] | undefined, roles: stri
486
494
  */
487
495
  declare function filterMenuByRoles(items: NubitAppMenuItem[], roles: string[]): AdminMenuItem[];
488
496
  //#endregion
489
- 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, type FeatureHubBanner, type FeatureHubDensity, FeatureHubLayout, type FeatureHubLayoutProps, type FeatureHubTab, LoginPage, type LoginPageProps, type NotificationType, type NubitApp, type NubitAppMenuContext, type NubitAppMenuItem, type NubitAppMenuSubItem, type NubitAppRoute, type NubitAppUserMenuContext, type ParsedQuotaError, type PlanDefinition, PlanPanel, type PlanPanelLabels, type PlanPanelProps, QuotaUsageBanner, type QuotaUsageBannerProps, 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 UseQuotaUsageOptions, type UseRuntimeConfigOptions, createNubitApp, filterMenuByRoles, hasAnyRole, parseQuotaError, quotaErrorToastMessage, quotaUsageAboveGrid, useAppRuntime, useFeature, useFeatureConfig, useQuotaUsage, useRuntimeConfig, useScreenSize, useScreenSizeClass, useSession };
497
+ 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, type FeatureHubBanner, type FeatureHubDensity, FeatureHubLayout, type FeatureHubLayoutProps, type FeatureHubTab, LoginPage, type LoginPageProps, type NotificationType, type NubitApp, type NubitAppMenuContext, type NubitAppMenuItem, type NubitAppMenuSubItem, type NubitAppRoute, type NubitAppUserMenuContext, NubitDevToolsPanel, type ParsedQuotaError, type PlanDefinition, PlanPanel, type PlanPanelLabels, type PlanPanelProps, QuotaUsageBanner, type QuotaUsageBannerProps, 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 UseQuotaUsageOptions, type UseRuntimeConfigOptions, createNubitApp, filterMenuByRoles, hasAnyRole, parseQuotaError, quotaErrorToastMessage, quotaUsageAboveGrid, useAppRuntime, useFeature, useFeatureConfig, useQuotaUsage, useRuntimeConfig, useScreenSize, useScreenSizeClass, useSession };
package/dist/index.d.mts CHANGED
@@ -470,6 +470,11 @@ type CreateNubitAppConfig = {
470
470
  }>;
471
471
  queryClient?: QueryClient; /** Register Mercure + Hydra schema/store providers. Default true. */
472
472
  hydra?: boolean;
473
+ /**
474
+ * Show the Nubit DevTools panel (field mapping, provider tree).
475
+ * Default true on localhost; set false to disable.
476
+ */
477
+ devTools?: boolean;
473
478
  };
474
479
  type NubitApp = {
475
480
  App: React.ComponentType;
@@ -478,6 +483,9 @@ type NubitApp = {
478
483
  //#region packages/admin/app/createNubitApp.d.ts
479
484
  declare function createNubitApp(config: CreateNubitAppConfig): NubitApp;
480
485
  //#endregion
486
+ //#region packages/admin/devtools/NubitDevToolsPanel.d.ts
487
+ declare function NubitDevToolsPanel(): React$1.JSX.Element | null;
488
+ //#endregion
481
489
  //#region packages/admin/app/filterMenuByRoles.d.ts
482
490
  declare function hasAnyRole(required: string | string[] | undefined, roles: string[]): boolean;
483
491
  /**
@@ -486,4 +494,4 @@ declare function hasAnyRole(required: string | string[] | undefined, roles: stri
486
494
  */
487
495
  declare function filterMenuByRoles(items: NubitAppMenuItem[], roles: string[]): AdminMenuItem[];
488
496
  //#endregion
489
- 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, type FeatureHubBanner, type FeatureHubDensity, FeatureHubLayout, type FeatureHubLayoutProps, type FeatureHubTab, LoginPage, type LoginPageProps, type NotificationType, type NubitApp, type NubitAppMenuContext, type NubitAppMenuItem, type NubitAppMenuSubItem, type NubitAppRoute, type NubitAppUserMenuContext, type ParsedQuotaError, type PlanDefinition, PlanPanel, type PlanPanelLabels, type PlanPanelProps, QuotaUsageBanner, type QuotaUsageBannerProps, 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 UseQuotaUsageOptions, type UseRuntimeConfigOptions, createNubitApp, filterMenuByRoles, hasAnyRole, parseQuotaError, quotaErrorToastMessage, quotaUsageAboveGrid, useAppRuntime, useFeature, useFeatureConfig, useQuotaUsage, useRuntimeConfig, useScreenSize, useScreenSizeClass, useSession };
497
+ 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, type FeatureHubBanner, type FeatureHubDensity, FeatureHubLayout, type FeatureHubLayoutProps, type FeatureHubTab, LoginPage, type LoginPageProps, type NotificationType, type NubitApp, type NubitAppMenuContext, type NubitAppMenuItem, type NubitAppMenuSubItem, type NubitAppRoute, type NubitAppUserMenuContext, NubitDevToolsPanel, type ParsedQuotaError, type PlanDefinition, PlanPanel, type PlanPanelLabels, type PlanPanelProps, QuotaUsageBanner, type QuotaUsageBannerProps, 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 UseQuotaUsageOptions, type UseRuntimeConfigOptions, createNubitApp, filterMenuByRoles, hasAnyRole, parseQuotaError, quotaErrorToastMessage, quotaUsageAboveGrid, useAppRuntime, useFeature, useFeatureConfig, useQuotaUsage, useRuntimeConfig, useScreenSize, useScreenSizeClass, useSession };
package/dist/index.mjs CHANGED
@@ -4,7 +4,7 @@ import React, { createContext, useCallback, useContext, useEffect, useMemo, useS
4
4
  import { Badge, Button, Card, FeatureGate as FeatureGate$1, IconButton, TextField, ThemeProvider, ThemeSwitcher, useFloatingPanel } from "@nubitio/ui";
5
5
  import { QueryClient, QueryClientProvider, useQuery } from "@tanstack/react-query";
6
6
  import { CoreConfigProvider, CoreProvider, MercureProvider, useCoreHttpClient } from "@nubitio/core";
7
- import { SmartCrudRolesProvider } from "@nubitio/crud";
7
+ import { DevToolsProvider, SmartCrudRolesProvider, isDevEnvironment, useDevTools } from "@nubitio/crud";
8
8
  import { HydraResourceSchemaProvider, HydraResourceStoreProvider, SchemaProvider } from "@nubitio/hydra";
9
9
  //#region packages/admin/FeatureHubLayout.tsx
10
10
  function normalizeIcon(icon) {
@@ -1023,6 +1023,158 @@ function ToastHost({ toasts, onDismiss }) {
1023
1023
  });
1024
1024
  }
1025
1025
  //#endregion
1026
+ //#region packages/admin/devtools/NubitDevToolsPanel.tsx
1027
+ const PROVIDER_TREE = [
1028
+ "MercureProvider",
1029
+ "SchemaProvider",
1030
+ "HydraResourceSchemaProvider",
1031
+ "HydraResourceStoreProvider",
1032
+ "SmartCrudRolesProvider",
1033
+ "CoreProvider"
1034
+ ];
1035
+ function NubitDevToolsPanel() {
1036
+ const { enabled, resources, clearResources } = useDevTools();
1037
+ const [open, setOpen] = useState(false);
1038
+ const [expandedUrl, setExpandedUrl] = useState(null);
1039
+ if (!enabled || !isDevEnvironment()) return null;
1040
+ const active = expandedUrl ? resources.find((r) => r.apiUrl === expandedUrl) : resources[0];
1041
+ return /* @__PURE__ */ jsxs("div", {
1042
+ className: `nb-devtools ${open ? "nb-devtools--open" : ""}`,
1043
+ children: [/* @__PURE__ */ jsx("button", {
1044
+ type: "button",
1045
+ className: "nb-devtools__toggle",
1046
+ onClick: () => setOpen((v) => !v),
1047
+ "aria-expanded": open,
1048
+ title: "Nubit DevTools",
1049
+ children: "Nubit"
1050
+ }), open && /* @__PURE__ */ jsxs("div", {
1051
+ className: "nb-devtools__panel",
1052
+ role: "complementary",
1053
+ "aria-label": "Nubit DevTools",
1054
+ children: [
1055
+ /* @__PURE__ */ jsxs("header", {
1056
+ className: "nb-devtools__header",
1057
+ children: [/* @__PURE__ */ jsx("strong", { children: "Nubit DevTools" }), /* @__PURE__ */ jsx("button", {
1058
+ type: "button",
1059
+ className: "nb-devtools__clear",
1060
+ onClick: clearResources,
1061
+ children: "Clear"
1062
+ })]
1063
+ }),
1064
+ /* @__PURE__ */ jsxs("section", {
1065
+ className: "nb-devtools__section",
1066
+ children: [/* @__PURE__ */ jsx("h4", { children: "Provider tree" }), /* @__PURE__ */ jsx("ul", {
1067
+ className: "nb-devtools__providers",
1068
+ children: PROVIDER_TREE.map((name) => /* @__PURE__ */ jsx("li", { children: name }, name))
1069
+ })]
1070
+ }),
1071
+ /* @__PURE__ */ jsxs("section", {
1072
+ className: "nb-devtools__section",
1073
+ children: [/* @__PURE__ */ jsxs("h4", { children: [
1074
+ "Active resources (",
1075
+ resources.length,
1076
+ ")"
1077
+ ] }), resources.length === 0 ? /* @__PURE__ */ jsx("p", {
1078
+ className: "nb-devtools__empty",
1079
+ children: "Open a SchemaCrudPage to inspect field resolution."
1080
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1081
+ /* @__PURE__ */ jsx("select", {
1082
+ className: "nb-devtools__select",
1083
+ value: active?.apiUrl ?? "",
1084
+ onChange: (e) => setExpandedUrl(e.target.value),
1085
+ children: resources.map((r) => /* @__PURE__ */ jsx("option", {
1086
+ value: r.apiUrl,
1087
+ children: r.apiUrl
1088
+ }, r.apiUrl))
1089
+ }),
1090
+ active && /* @__PURE__ */ jsxs("dl", {
1091
+ className: "nb-devtools__meta",
1092
+ children: [
1093
+ /* @__PURE__ */ jsx("dt", { children: "Source" }),
1094
+ /* @__PURE__ */ jsx("dd", { children: active.source }),
1095
+ /* @__PURE__ */ jsx("dt", { children: "Operations" }),
1096
+ /* @__PURE__ */ jsx("dd", { children: active.supportedOperations.join(", ") || "—" }),
1097
+ /* @__PURE__ */ jsx("dt", { children: "Permissions" }),
1098
+ /* @__PURE__ */ jsx("dd", { children: active.permissionsSource ?? "—" })
1099
+ ]
1100
+ }),
1101
+ active?.formDetail && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("h4", {
1102
+ className: "nb-devtools__subsection",
1103
+ children: "Form detail (lines)"
1104
+ }), /* @__PURE__ */ jsxs("dl", {
1105
+ className: "nb-devtools__meta",
1106
+ children: [
1107
+ /* @__PURE__ */ jsx("dt", { children: "Line fields" }),
1108
+ /* @__PURE__ */ jsx("dd", { children: active.formDetail.fieldSource }),
1109
+ /* @__PURE__ */ jsx("dt", { children: "Property" }),
1110
+ /* @__PURE__ */ jsx("dd", { children: active.formDetail.propertyName ?? "—" }),
1111
+ /* @__PURE__ */ jsx("dt", { children: "Reload URL" }),
1112
+ /* @__PURE__ */ jsx("dd", {
1113
+ className: "nb-devtools__mapping",
1114
+ children: active.formDetail.reloadUrl ?? "—"
1115
+ }),
1116
+ /* @__PURE__ */ jsx("dt", { children: "Line class" }),
1117
+ /* @__PURE__ */ jsx("dd", { children: active.formDetail.lineClass ?? "—" }),
1118
+ /* @__PURE__ */ jsx("dt", { children: "x-embedded-lines" }),
1119
+ /* @__PURE__ */ jsx("dd", { children: active.formDetail.embeddedLinesCount })
1120
+ ]
1121
+ })] }),
1122
+ active && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("h4", {
1123
+ className: "nb-devtools__subsection",
1124
+ children: "Grid fields"
1125
+ }), /* @__PURE__ */ jsxs("table", {
1126
+ className: "nb-devtools__table",
1127
+ children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
1128
+ /* @__PURE__ */ jsx("th", { children: "Field" }),
1129
+ /* @__PURE__ */ jsx("th", { children: "Type" }),
1130
+ /* @__PURE__ */ jsx("th", { children: "Mapping" })
1131
+ ] }) }), /* @__PURE__ */ jsx("tbody", { children: active.fields.filter((f) => !f.isIdentity).map((f) => /* @__PURE__ */ jsxs("tr", { children: [
1132
+ /* @__PURE__ */ jsx("td", { children: f.name }),
1133
+ /* @__PURE__ */ jsx("td", { children: f.type }),
1134
+ /* @__PURE__ */ jsx("td", {
1135
+ className: "nb-devtools__mapping",
1136
+ children: f.mappingReason ?? "—"
1137
+ })
1138
+ ] }, f.name)) })]
1139
+ })] }),
1140
+ active?.formDetail && active.formDetail.lineFieldCount > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("h4", {
1141
+ className: "nb-devtools__subsection",
1142
+ children: [
1143
+ "Line fields (",
1144
+ active.formDetail.lineFieldCount,
1145
+ ")"
1146
+ ]
1147
+ }), /* @__PURE__ */ jsxs("table", {
1148
+ className: "nb-devtools__table",
1149
+ children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
1150
+ /* @__PURE__ */ jsx("th", { children: "Field" }),
1151
+ /* @__PURE__ */ jsx("th", { children: "Type" }),
1152
+ /* @__PURE__ */ jsx("th", { children: "Mapping" })
1153
+ ] }) }), /* @__PURE__ */ jsx("tbody", { children: active.formDetail.lineFields.filter((f) => !f.isIdentity).map((f) => /* @__PURE__ */ jsxs("tr", { children: [
1154
+ /* @__PURE__ */ jsx("td", { children: f.name }),
1155
+ /* @__PURE__ */ jsx("td", { children: f.type }),
1156
+ /* @__PURE__ */ jsx("td", {
1157
+ className: "nb-devtools__mapping",
1158
+ children: f.mappingReason ?? "—"
1159
+ })
1160
+ ] }, f.name)) })]
1161
+ })] })
1162
+ ] })]
1163
+ }),
1164
+ /* @__PURE__ */ jsx("footer", {
1165
+ className: "nb-devtools__footer",
1166
+ children: /* @__PURE__ */ jsx("a", {
1167
+ href: "https://github.com/nubitio/nubit-react/blob/main/docs/architecture/README.md",
1168
+ target: "_blank",
1169
+ rel: "noreferrer",
1170
+ children: "Architecture docs"
1171
+ })
1172
+ })
1173
+ ]
1174
+ })]
1175
+ });
1176
+ }
1177
+ //#endregion
1026
1178
  //#region packages/admin/app/filterMenuByRoles.ts
1027
1179
  function hasAnyRole(required, roles) {
1028
1180
  if (required === void 0) return true;
@@ -1143,24 +1295,32 @@ function NubitAuthenticatedApp({ config }) {
1143
1295
  element: route.element
1144
1296
  }, route.path))] })
1145
1297
  });
1146
- const authenticated = /* @__PURE__ */ jsx(CoreProvider, {
1147
- http: {
1148
- baseUrl: apiBaseUrl,
1149
- refreshPath: "auth/refresh",
1150
- loginPath: "auth/login"
1151
- },
1152
- runtime,
1153
- children: /* @__PURE__ */ jsx(CoreConfigProvider, {
1154
- apiBaseUrl,
1155
- locale: config.locale ?? "en",
1156
- timezone: config.timezone ?? "UTC",
1157
- currency: config.currency ?? "USD",
1158
- children: /* @__PURE__ */ jsxs(SmartCrudRolesProvider, {
1159
- roles: session.roles,
1160
- children: [/* @__PURE__ */ jsx(BrowserRouter, { children: /* @__PURE__ */ jsx(Wrapper, { children: config.hydra === false ? shell : /* @__PURE__ */ jsx(MercureProvider, { children: /* @__PURE__ */ jsx(SchemaProvider, { children: /* @__PURE__ */ jsx(HydraResourceSchemaProvider, { children: /* @__PURE__ */ jsx(HydraResourceStoreProvider, { children: shell }) }) }) }) }) }), /* @__PURE__ */ jsx(ToastHost, {
1161
- toasts,
1162
- onDismiss: dismiss
1163
- })]
1298
+ const devToolsEnabled = config.devTools ?? isDevEnvironment();
1299
+ const authenticated = /* @__PURE__ */ jsx(DevToolsProvider, {
1300
+ enabled: devToolsEnabled,
1301
+ children: /* @__PURE__ */ jsx(CoreProvider, {
1302
+ http: {
1303
+ baseUrl: apiBaseUrl,
1304
+ refreshPath: "auth/refresh",
1305
+ loginPath: "auth/login"
1306
+ },
1307
+ runtime,
1308
+ children: /* @__PURE__ */ jsx(CoreConfigProvider, {
1309
+ apiBaseUrl,
1310
+ locale: config.locale ?? "en",
1311
+ timezone: config.timezone ?? "UTC",
1312
+ currency: config.currency ?? "USD",
1313
+ children: /* @__PURE__ */ jsxs(SmartCrudRolesProvider, {
1314
+ roles: session.roles,
1315
+ children: [
1316
+ /* @__PURE__ */ jsx(BrowserRouter, { children: /* @__PURE__ */ jsx(Wrapper, { children: config.hydra === false ? shell : /* @__PURE__ */ jsx(MercureProvider, { children: /* @__PURE__ */ jsx(SchemaProvider, { children: /* @__PURE__ */ jsx(HydraResourceSchemaProvider, { children: /* @__PURE__ */ jsx(HydraResourceStoreProvider, { children: shell }) }) }) }) }) }),
1317
+ /* @__PURE__ */ jsx(ToastHost, {
1318
+ toasts,
1319
+ onDismiss: dismiss
1320
+ }),
1321
+ devToolsEnabled && /* @__PURE__ */ jsx(NubitDevToolsPanel, {})
1322
+ ]
1323
+ })
1164
1324
  })
1165
1325
  })
1166
1326
  });
@@ -1191,4 +1351,4 @@ function createNubitApp(config) {
1191
1351
  return { App };
1192
1352
  }
1193
1353
  //#endregion
1194
- export { AdminHeader, AdminShell, AdminSidebarMenu, FeatureGate, FeatureHubLayout, LoginPage, PlanPanel, QuotaUsageBanner, RegisterPage, SessionProvider, StaticSessionProvider, ToastHost, createNubitApp, filterMenuByRoles, hasAnyRole, parseQuotaError, quotaErrorToastMessage, quotaUsageAboveGrid, useAppRuntime, useFeature, useFeatureConfig, useQuotaUsage, useRuntimeConfig, useScreenSize, useScreenSizeClass, useSession };
1354
+ export { AdminHeader, AdminShell, AdminSidebarMenu, FeatureGate, FeatureHubLayout, LoginPage, NubitDevToolsPanel, PlanPanel, QuotaUsageBanner, RegisterPage, SessionProvider, StaticSessionProvider, ToastHost, createNubitApp, filterMenuByRoles, hasAnyRole, parseQuotaError, quotaErrorToastMessage, quotaUsageAboveGrid, useAppRuntime, useFeature, useFeatureConfig, useQuotaUsage, useRuntimeConfig, useScreenSize, useScreenSizeClass, useSession };
package/dist/style.css CHANGED
@@ -847,3 +847,135 @@ html[data-density=comfortable] {
847
847
  line-height: 1;
848
848
  padding: 0;
849
849
  }
850
+ .nb-devtools {
851
+ position: fixed;
852
+ bottom: 12px;
853
+ right: 12px;
854
+ z-index: 9999;
855
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
856
+ font-size: 12px;
857
+ }
858
+
859
+ .nb-devtools__toggle {
860
+ background: #1e293b;
861
+ color: #f8fafc;
862
+ border: 1px solid #334155;
863
+ border-radius: 6px;
864
+ padding: 6px 10px;
865
+ cursor: pointer;
866
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
867
+ }
868
+
869
+ .nb-devtools__panel {
870
+ position: absolute;
871
+ bottom: 40px;
872
+ right: 0;
873
+ width: min(420px, 100vw - 24px);
874
+ max-height: 70vh;
875
+ overflow: auto;
876
+ background: #0f172a;
877
+ color: #e2e8f0;
878
+ border: 1px solid #334155;
879
+ border-radius: 8px;
880
+ padding: 12px;
881
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
882
+ }
883
+
884
+ .nb-devtools__header {
885
+ display: flex;
886
+ justify-content: space-between;
887
+ align-items: center;
888
+ margin-bottom: 8px;
889
+ }
890
+
891
+ .nb-devtools__clear {
892
+ background: transparent;
893
+ color: #94a3b8;
894
+ border: none;
895
+ cursor: pointer;
896
+ font-size: 11px;
897
+ }
898
+
899
+ .nb-devtools__section {
900
+ margin-bottom: 12px;
901
+ }
902
+ .nb-devtools__section h4 {
903
+ margin: 0 0 6px;
904
+ font-size: 11px;
905
+ text-transform: uppercase;
906
+ letter-spacing: 0.04em;
907
+ color: #94a3b8;
908
+ }
909
+
910
+ .nb-devtools__subsection {
911
+ margin: 10px 0 6px;
912
+ font-size: 11px;
913
+ text-transform: uppercase;
914
+ letter-spacing: 0.04em;
915
+ color: #94a3b8;
916
+ }
917
+
918
+ .nb-devtools__providers {
919
+ margin: 0;
920
+ padding-left: 16px;
921
+ color: #cbd5e1;
922
+ }
923
+
924
+ .nb-devtools__empty {
925
+ margin: 0;
926
+ color: #94a3b8;
927
+ }
928
+
929
+ .nb-devtools__select {
930
+ width: 100%;
931
+ margin-bottom: 8px;
932
+ background: #1e293b;
933
+ color: #f8fafc;
934
+ border: 1px solid #334155;
935
+ border-radius: 4px;
936
+ padding: 4px 6px;
937
+ }
938
+
939
+ .nb-devtools__meta {
940
+ display: grid;
941
+ grid-template-columns: auto 1fr;
942
+ gap: 2px 8px;
943
+ margin: 0 0 8px;
944
+ }
945
+ .nb-devtools__meta dt {
946
+ color: #94a3b8;
947
+ }
948
+ .nb-devtools__meta dd {
949
+ margin: 0;
950
+ }
951
+
952
+ .nb-devtools__table {
953
+ width: 100%;
954
+ border-collapse: collapse;
955
+ }
956
+ .nb-devtools__table th,
957
+ .nb-devtools__table td {
958
+ text-align: left;
959
+ padding: 3px 4px;
960
+ border-bottom: 1px solid #1e293b;
961
+ vertical-align: top;
962
+ }
963
+ .nb-devtools__table th {
964
+ color: #94a3b8;
965
+ font-weight: 600;
966
+ }
967
+
968
+ .nb-devtools__mapping {
969
+ color: #7dd3fc;
970
+ word-break: break-word;
971
+ }
972
+
973
+ .nb-devtools__footer {
974
+ margin-top: 8px;
975
+ padding-top: 8px;
976
+ border-top: 1px solid #1e293b;
977
+ }
978
+ .nb-devtools__footer a {
979
+ color: #38bdf8;
980
+ text-decoration: none;
981
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nubitio/admin",
3
- "version": "0.5.30",
3
+ "version": "0.6.1",
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.30",
57
- "@nubitio/crud": "^0.5.30",
58
- "@nubitio/hydra": "^0.5.30",
59
- "@nubitio/ui": "^0.5.30"
56
+ "@nubitio/core": "^0.6.1",
57
+ "@nubitio/hydra": "^0.6.1",
58
+ "@nubitio/crud": "^0.6.1",
59
+ "@nubitio/ui": "^0.6.1"
60
60
  }
61
61
  }