@nubitio/crud 0.5.23 → 0.5.24

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
@@ -2731,6 +2731,10 @@ const NativeDataGridView = (0, react.forwardRef)((options, ref) => {
2731
2731
  })
2732
2732
  ]
2733
2733
  }),
2734
+ options.aboveGrid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2735
+ className: "nb-datagrid__above-grid",
2736
+ children: options.aboveGrid
2737
+ }) : null,
2734
2738
  isMobile && quickSearchField && (options.filterRow ?? true) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2735
2739
  className: "nb-datagrid__quick-search",
2736
2740
  children: [
@@ -5300,7 +5304,11 @@ function usePermissions(resource, supportedOperations = []) {
5300
5304
  canExport: resolve(p?.canExport, false),
5301
5305
  canBulkDelete: resolve(p?.canBulkDelete, false)
5302
5306
  };
5303
- }, [resource.id, opsKey]);
5307
+ }, [
5308
+ resource.id,
5309
+ opsKey,
5310
+ resource.permissions
5311
+ ]);
5304
5312
  }
5305
5313
  //#endregion
5306
5314
  //#region packages/crud/crud/useSelectionState.ts
@@ -6149,7 +6157,7 @@ function buildRoutingFilterRules(fields, initialFilters) {
6149
6157
  * Public export — wraps CrudPageInner in its own DialogStoreProvider so that
6150
6158
  * existing pages do not need to add a provider themselves.
6151
6159
  */
6152
- const CrudPage = ({ resource, onFormDataChange, initialRecordId, initialIsNew, computedValues, initialFilters, onFiltersChange, formRef, onSelectionChanged, editDisabled, deleteDisabled, gridRef, onOperationChange }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DialogStoreProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CrudPageInner, {
6160
+ const CrudPage = ({ resource, onFormDataChange, initialRecordId, initialIsNew, computedValues, initialFilters, onFiltersChange, formRef, onSelectionChanged, editDisabled, deleteDisabled, gridRef, aboveGrid, onOperationChange }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DialogStoreProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CrudPageInner, {
6153
6161
  resource,
6154
6162
  onFormDataChange,
6155
6163
  initialRecordId,
@@ -6162,9 +6170,10 @@ const CrudPage = ({ resource, onFormDataChange, initialRecordId, initialIsNew, c
6162
6170
  editDisabled,
6163
6171
  deleteDisabled,
6164
6172
  gridRef,
6173
+ aboveGrid,
6165
6174
  onOperationChange
6166
6175
  }) });
6167
- const CrudPageInner = ({ resource, onFormDataChange, initialRecordId = null, initialIsNew = false, computedValues = EMPTY_COMPUTED_VALUES, initialFilters = EMPTY_INITIAL_FILTERS, onFiltersChange, formRef: externalFormRef, onSelectionChanged: externalOnSelectionChanged, editDisabled, deleteDisabled, gridRef: externalGridRef, onOperationChange }) => {
6176
+ const CrudPageInner = ({ resource, onFormDataChange, initialRecordId = null, initialIsNew = false, computedValues = EMPTY_COMPUTED_VALUES, initialFilters = EMPTY_INITIAL_FILTERS, onFiltersChange, formRef: externalFormRef, onSelectionChanged: externalOnSelectionChanged, editDisabled, deleteDisabled, gridRef: externalGridRef, aboveGrid: aboveGridOverride, onOperationChange }) => {
6168
6177
  const { t } = (0, _nubitio_core.useCoreTranslation)();
6169
6178
  const { events, resource: resolvedResource, fields, formFields, formRef, permissions, selectionState, presetState } = useCrudPage((0, react.useMemo)(() => resolveCrudResource(resource), [resource]), externalFormRef);
6170
6179
  const datagridFields = (0, react.useMemo)(() => fields.filter((field) => field.isIdentity || field.visible !== false), [fields]);
@@ -6427,6 +6436,16 @@ const CrudPageInner = ({ resource, onFormDataChange, initialRecordId = null, ini
6427
6436
  resolvedResource.rowActions,
6428
6437
  t
6429
6438
  ]);
6439
+ const aboveGridSlot = aboveGridOverride ?? resolvedResource.aboveGrid;
6440
+ const aboveGridContent = (() => {
6441
+ if (!aboveGridSlot) return;
6442
+ if (typeof aboveGridSlot === "function") return aboveGridSlot({
6443
+ resource: resolvedResource,
6444
+ gridRef,
6445
+ refresh: () => gridRef.current?.refresh()
6446
+ });
6447
+ return aboveGridSlot;
6448
+ })();
6430
6449
  /** Preset selector rendered as a toolbar slot via `beforeToolbar`. */
6431
6450
  const renderPresetSelector = hasPresets ? () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ColumnPresetSelector, {
6432
6451
  resourceId: resolvedResource.id,
@@ -6489,6 +6508,7 @@ const CrudPageInner = ({ resource, onFormDataChange, initialRecordId = null, ini
6489
6508
  editMode: resolvedResource.editMode,
6490
6509
  visibleColumns: presetState.visibleColumns,
6491
6510
  beforeToolbar: renderPresetSelector,
6511
+ aboveGrid: aboveGridContent,
6492
6512
  detailUrl: gridDetail?.url,
6493
6513
  detailFields: gridDetail?.fields,
6494
6514
  onFilterChange: onFiltersChange,
@@ -7610,7 +7630,7 @@ function formatRuntimeErrorMessage(error) {
7610
7630
  *
7611
7631
  * URL deep-linking is wired via `initialRecordId` / `initialIsNew` props on CrudPage.
7612
7632
  */
7613
- function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged, editDisabled, deleteDisabled, gridRef }) {
7633
+ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged, editDisabled, deleteDisabled, gridRef, aboveGrid }) {
7614
7634
  const queryClient = (0, _tanstack_react_query.useQueryClient)();
7615
7635
  const internalGridRef = (0, react.useRef)(null);
7616
7636
  const effectiveGridRef = gridRef ?? internalGridRef;
@@ -7695,6 +7715,7 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7695
7715
  editDisabled,
7696
7716
  deleteDisabled,
7697
7717
  gridRef: effectiveGridRef,
7718
+ aboveGrid: aboveGrid ?? resource.aboveGrid,
7698
7719
  onOperationChange: (operation) => {
7699
7720
  if (operation === "create") {
7700
7721
  startCreate();
package/dist/index.d.cts CHANGED
@@ -3,6 +3,28 @@ import { CoreHttpClient, DataGridEventNames, DataRecord, DataRecord as DataRecor
3
3
  import { AppDropdownOption } from "@nubitio/ui";
4
4
  import { WorkflowSchema } from "@nubitio/hydra";
5
5
 
6
+ //#region packages/crud/field/FilterRule.d.ts
7
+ interface FilterRule {
8
+ field: string;
9
+ operator: string;
10
+ value: string | number | boolean;
11
+ }
12
+ //#endregion
13
+ //#region packages/crud/datagrid/GridHandle.d.ts
14
+ interface GridHandle {
15
+ showLoading: (message?: string) => void;
16
+ hideLoading: () => void;
17
+ refresh: () => void;
18
+ reset: () => void;
19
+ loadData: () => Promise<unknown>;
20
+ getSelectedRowKey: () => string | number | undefined;
21
+ getSelectedRow: () => DataRecord$1 | undefined;
22
+ getSelectedRowKeys: () => unknown[];
23
+ getSelectedRows: () => DataRecord$1[];
24
+ getFilter: () => unknown[];
25
+ filter: (filterRule: FilterRule | null) => void;
26
+ }
27
+ //#endregion
6
28
  //#region packages/crud/field/FieldType.d.ts
7
29
  declare enum FieldType {
8
30
  NONE = "none",
@@ -24,13 +46,6 @@ declare enum FieldType {
24
46
  HTML = "html"
25
47
  }
26
48
  //#endregion
27
- //#region packages/crud/field/FilterRule.d.ts
28
- interface FilterRule {
29
- field: string;
30
- operator: string;
31
- value: string | number | boolean;
32
- }
33
- //#endregion
34
49
  //#region packages/crud/field/ValidationRule.d.ts
35
50
  type RequiredRule = {
36
51
  type: 'required';
@@ -442,21 +457,6 @@ interface FormHandle {
442
457
  setIsEdit: (value: boolean) => void;
443
458
  }
444
459
  //#endregion
445
- //#region packages/crud/datagrid/GridHandle.d.ts
446
- interface GridHandle {
447
- showLoading: (message?: string) => void;
448
- hideLoading: () => void;
449
- refresh: () => void;
450
- reset: () => void;
451
- loadData: () => Promise<unknown>;
452
- getSelectedRowKey: () => string | number | undefined;
453
- getSelectedRow: () => DataRecord$1 | undefined;
454
- getSelectedRowKeys: () => unknown[];
455
- getSelectedRows: () => DataRecord$1[];
456
- getFilter: () => unknown[];
457
- filter: (filterRule: FilterRule | null) => void;
458
- }
459
- //#endregion
460
460
  //#region packages/crud/form/FormLayout.d.ts
461
461
  interface FormTab {
462
462
  label: string;
@@ -824,6 +824,12 @@ interface ResourceToolbarContext<T extends DataRecord$1 = DataRecord$1> {
824
824
  events: FormEventNames;
825
825
  emit: <P>(name: string, payload?: P) => void;
826
826
  }
827
+ /** Context passed to `aboveGrid` slot renderers on SmartCrudPage / CrudPage. */
828
+ interface CrudGridSlotContext<T extends DataRecord$1 = DataRecord$1> {
829
+ resource: ResourceConfig<T>;
830
+ gridRef: RefObject<GridHandle | null>;
831
+ refresh: () => void;
832
+ }
827
833
  type ResourceToolbar<T extends DataRecord$1 = DataRecord$1> = ResourceToolbarItems | ((context: ResourceToolbarContext<T>) => ResourceToolbarItems);
828
834
  interface ResourceGridDetail {
829
835
  url: string;
@@ -944,6 +950,11 @@ interface ResourceConfig<T extends DataRecord$1 = DataRecord$1> {
944
950
  viewMode?: CrudViewMode | CrudViewModeConfig;
945
951
  format?: 'json' | 'multipart';
946
952
  toolbar?: ResourceToolbar<T>;
953
+ /**
954
+ * Custom UI between the grid toolbar and the table — KPI cards, quota banners,
955
+ * contextual filters. Static node or render function with grid helpers.
956
+ */
957
+ aboveGrid?: ReactNode | ((context: CrudGridSlotContext<T>) => ReactNode);
947
958
  rowActions?: ResourceRowActions<T>;
948
959
  onSaveSuccess?: (response: T) => void;
949
960
  onSaveError?: (error?: unknown) => void;
@@ -1065,6 +1076,8 @@ interface CrudPageProps<T extends DataRecord$1 = DataRecord$1> {
1065
1076
  /** External ref forwarded to the DataGridView — allows the parent to call
1066
1077
  * showLoading / hideLoading / refresh / getSelectedRow imperatively. */
1067
1078
  gridRef?: React.RefObject<GridHandle | null>;
1079
+ /** Overrides `resource.aboveGrid` — KPI cards, banners, etc. */
1080
+ aboveGrid?: ResourceConfig<T>['aboveGrid'];
1068
1081
  onOperationChange?: (operation: SmartCrudOperation | null) => void;
1069
1082
  }
1070
1083
  /**
@@ -1084,6 +1097,7 @@ declare const CrudPage: <T extends DataRecord$1 = DataRecord$1>({
1084
1097
  editDisabled,
1085
1098
  deleteDisabled,
1086
1099
  gridRef,
1100
+ aboveGrid,
1087
1101
  onOperationChange
1088
1102
  }: CrudPageProps<T>) => React.JSX.Element;
1089
1103
  //#endregion
@@ -1135,6 +1149,8 @@ interface SmartCrudPageProps<T extends DataRecord$1 = DataRecord$1> {
1135
1149
  /** External ref forwarded to the DataGridView — allows the parent to call
1136
1150
  * showLoading / hideLoading / refresh / getSelectedRow imperatively. */
1137
1151
  gridRef?: RefObject<GridHandle | null>;
1152
+ /** Custom UI above the grid table — KPI cards, banners. Overrides `resource.aboveGrid`. */
1153
+ aboveGrid?: ResourceConfig<T>['aboveGrid'];
1138
1154
  }
1139
1155
  /**
1140
1156
  * SmartCrudPage — wraps CrudPage with Hydra auto-discovery and declarative rules.
@@ -1164,7 +1180,8 @@ declare function SmartCrudPage<T extends DataRecord$1 = DataRecord$1>({
1164
1180
  onSelectionChanged,
1165
1181
  editDisabled,
1166
1182
  deleteDisabled,
1167
- gridRef
1183
+ gridRef,
1184
+ aboveGrid
1168
1185
  }: SmartCrudPageProps<T>): React.JSX.Element;
1169
1186
  //#endregion
1170
1187
  //#region packages/crud/crud/SmartCrudRolesContext.d.ts
@@ -1578,6 +1595,8 @@ interface DataGridViewOptions {
1578
1595
  headerFilter?: boolean;
1579
1596
  manualLoad?: boolean;
1580
1597
  beforeToolbar?: () => ReactNode;
1598
+ /** Custom content between the toolbar and the table (KPI cards, banners, filters). */
1599
+ aboveGrid?: ReactNode;
1581
1600
  errorMessage?: string | null;
1582
1601
  onDismissError?: () => void;
1583
1602
  editMode?: 'popup' | 'row' | 'cell' | 'batch';
@@ -2016,4 +2035,4 @@ interface RestQueryDialect {
2016
2035
  */
2017
2036
  declare function createRestResourceStore(dialect?: RestQueryDialect): ResourceStoreFactory;
2018
2037
  //#endregion
2019
- export { type AuditEntry, type AuditFieldLabelResolver, type AuditTrailConfig, AuditTrailPanel, type AuditTrailPanelProps, type BackendAdapter, type BulkAction, type ColSpan, type ColumnPreset, ColumnPresetSelector, type ColumnPresetState, CrudDialogShell, CrudDrawerShell, type CrudDrawerSize, type CrudDrawerViewEvents, type CrudDrawerViewOptions, CrudFormShell, type CrudFormShellProps, CrudPage, CrudPageShell, type CrudPageViewEvents, type CrudPageViewOptions, type CrudViewMode, type CrudViewModeConfig, DATA_GRID_EVENTS, DEFAULT_DRAWER_SIZE, DEFAULT_DRAWER_WIDTH, DRAWER_WIDTHS, type DataGridSelectionChangedEvent, type DataGridSummaryItem, NativeDataGridView as DataGridView, type DataGridViewOptions, type DataRecord, type DetailSummaryOptions, CrudDialogView as DialogView, type DrawerSize, CrudDrawerView as DrawerView, type EnumOption, FORM_EVENTS, type Field, FieldBuilder, type FieldColSpanContext, type FieldDef, type FieldInput, type FieldOverride, FieldType, type FilterRule, type FormHandle, type FormLayout, type FormLayoutHint, type FormOnChangeFn, type FormPresentationContext, type FormPresentationMode, type FormSection, type FormTab, NativeFormView as FormView, type FormViewOptions, type FormatterFn, type GridCellContext, type GridData, type GridHandle, type GridOnChangeFn, HydraAdapter, type ItemFormatterFn, type LoadOption, type OnChangeFn, CrudPageView as PageView, type ResolvedViewMode, type ResourceConfig, type ResourceEmptyState, type ResourceFilterDescriptor, type ResourceFilterRule, type ResourceFormDetail, type ResourceGridDetail, type ResourceLoadOption, type ResourceLoadOptions, type ResourcePermissions, type ResourceRouting, type ResourceRowActions, ResourceSchemaProvider, type ResourceSchemaProviderProps, type ResourceSchemaResolution, type ResourceSchemaResolver, type ResourceSortDescriptor, type ResourceStore, type ResourceStoreFactory, type ResourceStoreOptions, ResourceStoreProvider, type ResourceStoreProviderProps, type ResourceToolbar, type ResourceToolbarAction, type ResourceToolbarActionVariant, type ResourceToolbarContext, type ResourceToolbarItems, RestAdapter, type RestQueryDialect, type SmartCrudFieldContract, type SmartCrudFieldOperation, type SmartCrudFieldPatch, type SmartCrudHydraFieldContract, type SmartCrudHydraFieldDirective, type SmartCrudManualField, type SmartCrudManualFieldContract, type SmartCrudOperation, SmartCrudPage, SmartCrudRolesProvider, type SummaryCalculateContext, type SummaryFormat, type SummaryItem, type SummaryTextContext, type SummaryType, ToolbarSelect, type ToolbarSelectOption, type ToolbarSelectProps, type ValidationRule, buildFieldColSpanContext, buildFields, buildWorkflowRowActions, checkboxField, computeSummaryValue, createCrudEvents, createRestResourceStore, crudRoute, currencyField, dateField, datetimeField, defineFieldContract, defineFields, defineResource, embeddedLinesUrl, entityField, enumField, fileField, formatSummaryValue, identityField, imageField, isLongTextField, isShortField, noneField, numberField, parseDrawerWidthPx, passwordField, resolveDrawerLayoutBucket, resolveDrawerSize, resolveDrawerWidth, resolveFieldColSpan, resolveFieldsColSpans, resolveSummaryText, resolveViewMode, selectField, switchField, textField, textareaField, useColumnPreset, useResourceStoreFactory, useSmartCrudRoles, validateFieldContract };
2038
+ export { type AuditEntry, type AuditFieldLabelResolver, type AuditTrailConfig, AuditTrailPanel, type AuditTrailPanelProps, type BackendAdapter, type BulkAction, type ColSpan, type ColumnPreset, ColumnPresetSelector, type ColumnPresetState, CrudDialogShell, CrudDrawerShell, type CrudDrawerSize, type CrudDrawerViewEvents, type CrudDrawerViewOptions, CrudFormShell, type CrudFormShellProps, type CrudGridSlotContext, CrudPage, CrudPageShell, type CrudPageViewEvents, type CrudPageViewOptions, type CrudViewMode, type CrudViewModeConfig, DATA_GRID_EVENTS, DEFAULT_DRAWER_SIZE, DEFAULT_DRAWER_WIDTH, DRAWER_WIDTHS, type DataGridSelectionChangedEvent, type DataGridSummaryItem, NativeDataGridView as DataGridView, type DataGridViewOptions, type DataRecord, type DetailSummaryOptions, CrudDialogView as DialogView, type DrawerSize, CrudDrawerView as DrawerView, type EnumOption, FORM_EVENTS, type Field, FieldBuilder, type FieldColSpanContext, type FieldDef, type FieldInput, type FieldOverride, FieldType, type FilterRule, type FormHandle, type FormLayout, type FormLayoutHint, type FormOnChangeFn, type FormPresentationContext, type FormPresentationMode, type FormSection, type FormTab, NativeFormView as FormView, type FormViewOptions, type FormatterFn, type GridCellContext, type GridData, type GridHandle, type GridOnChangeFn, HydraAdapter, type ItemFormatterFn, type LoadOption, type OnChangeFn, CrudPageView as PageView, type ResolvedViewMode, type ResourceConfig, type ResourceEmptyState, type ResourceFilterDescriptor, type ResourceFilterRule, type ResourceFormDetail, type ResourceGridDetail, type ResourceLoadOption, type ResourceLoadOptions, type ResourcePermissions, type ResourceRouting, type ResourceRowActions, ResourceSchemaProvider, type ResourceSchemaProviderProps, type ResourceSchemaResolution, type ResourceSchemaResolver, type ResourceSortDescriptor, type ResourceStore, type ResourceStoreFactory, type ResourceStoreOptions, ResourceStoreProvider, type ResourceStoreProviderProps, type ResourceToolbar, type ResourceToolbarAction, type ResourceToolbarActionVariant, type ResourceToolbarContext, type ResourceToolbarItems, RestAdapter, type RestQueryDialect, type SmartCrudFieldContract, type SmartCrudFieldOperation, type SmartCrudFieldPatch, type SmartCrudHydraFieldContract, type SmartCrudHydraFieldDirective, type SmartCrudManualField, type SmartCrudManualFieldContract, type SmartCrudOperation, SmartCrudPage, SmartCrudRolesProvider, type SummaryCalculateContext, type SummaryFormat, type SummaryItem, type SummaryTextContext, type SummaryType, ToolbarSelect, type ToolbarSelectOption, type ToolbarSelectProps, type ValidationRule, buildFieldColSpanContext, buildFields, buildWorkflowRowActions, checkboxField, computeSummaryValue, createCrudEvents, createRestResourceStore, crudRoute, currencyField, dateField, datetimeField, defineFieldContract, defineFields, defineResource, embeddedLinesUrl, entityField, enumField, fileField, formatSummaryValue, identityField, imageField, isLongTextField, isShortField, noneField, numberField, parseDrawerWidthPx, passwordField, resolveDrawerLayoutBucket, resolveDrawerSize, resolveDrawerWidth, resolveFieldColSpan, resolveFieldsColSpans, resolveSummaryText, resolveViewMode, selectField, switchField, textField, textareaField, useColumnPreset, useResourceStoreFactory, useSmartCrudRoles, validateFieldContract };
package/dist/index.d.mts CHANGED
@@ -3,6 +3,28 @@ import { AppDropdownOption } from "@nubitio/ui";
3
3
  import { CoreHttpClient, DataGridEventNames, DataRecord, DataRecord as DataRecord$1, DialogEventNames, FormEventNames, GridData, GridData as GridData$1, createCrudEvents } from "@nubitio/core";
4
4
  import { WorkflowSchema } from "@nubitio/hydra";
5
5
 
6
+ //#region packages/crud/field/FilterRule.d.ts
7
+ interface FilterRule {
8
+ field: string;
9
+ operator: string;
10
+ value: string | number | boolean;
11
+ }
12
+ //#endregion
13
+ //#region packages/crud/datagrid/GridHandle.d.ts
14
+ interface GridHandle {
15
+ showLoading: (message?: string) => void;
16
+ hideLoading: () => void;
17
+ refresh: () => void;
18
+ reset: () => void;
19
+ loadData: () => Promise<unknown>;
20
+ getSelectedRowKey: () => string | number | undefined;
21
+ getSelectedRow: () => DataRecord$1 | undefined;
22
+ getSelectedRowKeys: () => unknown[];
23
+ getSelectedRows: () => DataRecord$1[];
24
+ getFilter: () => unknown[];
25
+ filter: (filterRule: FilterRule | null) => void;
26
+ }
27
+ //#endregion
6
28
  //#region packages/crud/field/FieldType.d.ts
7
29
  declare enum FieldType {
8
30
  NONE = "none",
@@ -24,13 +46,6 @@ declare enum FieldType {
24
46
  HTML = "html"
25
47
  }
26
48
  //#endregion
27
- //#region packages/crud/field/FilterRule.d.ts
28
- interface FilterRule {
29
- field: string;
30
- operator: string;
31
- value: string | number | boolean;
32
- }
33
- //#endregion
34
49
  //#region packages/crud/field/ValidationRule.d.ts
35
50
  type RequiredRule = {
36
51
  type: 'required';
@@ -442,21 +457,6 @@ interface FormHandle {
442
457
  setIsEdit: (value: boolean) => void;
443
458
  }
444
459
  //#endregion
445
- //#region packages/crud/datagrid/GridHandle.d.ts
446
- interface GridHandle {
447
- showLoading: (message?: string) => void;
448
- hideLoading: () => void;
449
- refresh: () => void;
450
- reset: () => void;
451
- loadData: () => Promise<unknown>;
452
- getSelectedRowKey: () => string | number | undefined;
453
- getSelectedRow: () => DataRecord$1 | undefined;
454
- getSelectedRowKeys: () => unknown[];
455
- getSelectedRows: () => DataRecord$1[];
456
- getFilter: () => unknown[];
457
- filter: (filterRule: FilterRule | null) => void;
458
- }
459
- //#endregion
460
460
  //#region packages/crud/form/FormLayout.d.ts
461
461
  interface FormTab {
462
462
  label: string;
@@ -824,6 +824,12 @@ interface ResourceToolbarContext<T extends DataRecord$1 = DataRecord$1> {
824
824
  events: FormEventNames;
825
825
  emit: <P>(name: string, payload?: P) => void;
826
826
  }
827
+ /** Context passed to `aboveGrid` slot renderers on SmartCrudPage / CrudPage. */
828
+ interface CrudGridSlotContext<T extends DataRecord$1 = DataRecord$1> {
829
+ resource: ResourceConfig<T>;
830
+ gridRef: RefObject<GridHandle | null>;
831
+ refresh: () => void;
832
+ }
827
833
  type ResourceToolbar<T extends DataRecord$1 = DataRecord$1> = ResourceToolbarItems | ((context: ResourceToolbarContext<T>) => ResourceToolbarItems);
828
834
  interface ResourceGridDetail {
829
835
  url: string;
@@ -944,6 +950,11 @@ interface ResourceConfig<T extends DataRecord$1 = DataRecord$1> {
944
950
  viewMode?: CrudViewMode | CrudViewModeConfig;
945
951
  format?: 'json' | 'multipart';
946
952
  toolbar?: ResourceToolbar<T>;
953
+ /**
954
+ * Custom UI between the grid toolbar and the table — KPI cards, quota banners,
955
+ * contextual filters. Static node or render function with grid helpers.
956
+ */
957
+ aboveGrid?: ReactNode | ((context: CrudGridSlotContext<T>) => ReactNode);
947
958
  rowActions?: ResourceRowActions<T>;
948
959
  onSaveSuccess?: (response: T) => void;
949
960
  onSaveError?: (error?: unknown) => void;
@@ -1065,6 +1076,8 @@ interface CrudPageProps<T extends DataRecord$1 = DataRecord$1> {
1065
1076
  /** External ref forwarded to the DataGridView — allows the parent to call
1066
1077
  * showLoading / hideLoading / refresh / getSelectedRow imperatively. */
1067
1078
  gridRef?: React.RefObject<GridHandle | null>;
1079
+ /** Overrides `resource.aboveGrid` — KPI cards, banners, etc. */
1080
+ aboveGrid?: ResourceConfig<T>['aboveGrid'];
1068
1081
  onOperationChange?: (operation: SmartCrudOperation | null) => void;
1069
1082
  }
1070
1083
  /**
@@ -1084,6 +1097,7 @@ declare const CrudPage: <T extends DataRecord$1 = DataRecord$1>({
1084
1097
  editDisabled,
1085
1098
  deleteDisabled,
1086
1099
  gridRef,
1100
+ aboveGrid,
1087
1101
  onOperationChange
1088
1102
  }: CrudPageProps<T>) => React.JSX.Element;
1089
1103
  //#endregion
@@ -1135,6 +1149,8 @@ interface SmartCrudPageProps<T extends DataRecord$1 = DataRecord$1> {
1135
1149
  /** External ref forwarded to the DataGridView — allows the parent to call
1136
1150
  * showLoading / hideLoading / refresh / getSelectedRow imperatively. */
1137
1151
  gridRef?: RefObject<GridHandle | null>;
1152
+ /** Custom UI above the grid table — KPI cards, banners. Overrides `resource.aboveGrid`. */
1153
+ aboveGrid?: ResourceConfig<T>['aboveGrid'];
1138
1154
  }
1139
1155
  /**
1140
1156
  * SmartCrudPage — wraps CrudPage with Hydra auto-discovery and declarative rules.
@@ -1164,7 +1180,8 @@ declare function SmartCrudPage<T extends DataRecord$1 = DataRecord$1>({
1164
1180
  onSelectionChanged,
1165
1181
  editDisabled,
1166
1182
  deleteDisabled,
1167
- gridRef
1183
+ gridRef,
1184
+ aboveGrid
1168
1185
  }: SmartCrudPageProps<T>): React.JSX.Element;
1169
1186
  //#endregion
1170
1187
  //#region packages/crud/crud/SmartCrudRolesContext.d.ts
@@ -1578,6 +1595,8 @@ interface DataGridViewOptions {
1578
1595
  headerFilter?: boolean;
1579
1596
  manualLoad?: boolean;
1580
1597
  beforeToolbar?: () => ReactNode;
1598
+ /** Custom content between the toolbar and the table (KPI cards, banners, filters). */
1599
+ aboveGrid?: ReactNode;
1581
1600
  errorMessage?: string | null;
1582
1601
  onDismissError?: () => void;
1583
1602
  editMode?: 'popup' | 'row' | 'cell' | 'batch';
@@ -2016,4 +2035,4 @@ interface RestQueryDialect {
2016
2035
  */
2017
2036
  declare function createRestResourceStore(dialect?: RestQueryDialect): ResourceStoreFactory;
2018
2037
  //#endregion
2019
- export { type AuditEntry, type AuditFieldLabelResolver, type AuditTrailConfig, AuditTrailPanel, type AuditTrailPanelProps, type BackendAdapter, type BulkAction, type ColSpan, type ColumnPreset, ColumnPresetSelector, type ColumnPresetState, CrudDialogShell, CrudDrawerShell, type CrudDrawerSize, type CrudDrawerViewEvents, type CrudDrawerViewOptions, CrudFormShell, type CrudFormShellProps, CrudPage, CrudPageShell, type CrudPageViewEvents, type CrudPageViewOptions, type CrudViewMode, type CrudViewModeConfig, DATA_GRID_EVENTS, DEFAULT_DRAWER_SIZE, DEFAULT_DRAWER_WIDTH, DRAWER_WIDTHS, type DataGridSelectionChangedEvent, type DataGridSummaryItem, NativeDataGridView as DataGridView, type DataGridViewOptions, type DataRecord, type DetailSummaryOptions, CrudDialogView as DialogView, type DrawerSize, CrudDrawerView as DrawerView, type EnumOption, FORM_EVENTS, type Field, FieldBuilder, type FieldColSpanContext, type FieldDef, type FieldInput, type FieldOverride, FieldType, type FilterRule, type FormHandle, type FormLayout, type FormLayoutHint, type FormOnChangeFn, type FormPresentationContext, type FormPresentationMode, type FormSection, type FormTab, NativeFormView as FormView, type FormViewOptions, type FormatterFn, type GridCellContext, type GridData, type GridHandle, type GridOnChangeFn, HydraAdapter, type ItemFormatterFn, type LoadOption, type OnChangeFn, CrudPageView as PageView, type ResolvedViewMode, type ResourceConfig, type ResourceEmptyState, type ResourceFilterDescriptor, type ResourceFilterRule, type ResourceFormDetail, type ResourceGridDetail, type ResourceLoadOption, type ResourceLoadOptions, type ResourcePermissions, type ResourceRouting, type ResourceRowActions, ResourceSchemaProvider, type ResourceSchemaProviderProps, type ResourceSchemaResolution, type ResourceSchemaResolver, type ResourceSortDescriptor, type ResourceStore, type ResourceStoreFactory, type ResourceStoreOptions, ResourceStoreProvider, type ResourceStoreProviderProps, type ResourceToolbar, type ResourceToolbarAction, type ResourceToolbarActionVariant, type ResourceToolbarContext, type ResourceToolbarItems, RestAdapter, type RestQueryDialect, type SmartCrudFieldContract, type SmartCrudFieldOperation, type SmartCrudFieldPatch, type SmartCrudHydraFieldContract, type SmartCrudHydraFieldDirective, type SmartCrudManualField, type SmartCrudManualFieldContract, type SmartCrudOperation, SmartCrudPage, SmartCrudRolesProvider, type SummaryCalculateContext, type SummaryFormat, type SummaryItem, type SummaryTextContext, type SummaryType, ToolbarSelect, type ToolbarSelectOption, type ToolbarSelectProps, type ValidationRule, buildFieldColSpanContext, buildFields, buildWorkflowRowActions, checkboxField, computeSummaryValue, createCrudEvents, createRestResourceStore, crudRoute, currencyField, dateField, datetimeField, defineFieldContract, defineFields, defineResource, embeddedLinesUrl, entityField, enumField, fileField, formatSummaryValue, identityField, imageField, isLongTextField, isShortField, noneField, numberField, parseDrawerWidthPx, passwordField, resolveDrawerLayoutBucket, resolveDrawerSize, resolveDrawerWidth, resolveFieldColSpan, resolveFieldsColSpans, resolveSummaryText, resolveViewMode, selectField, switchField, textField, textareaField, useColumnPreset, useResourceStoreFactory, useSmartCrudRoles, validateFieldContract };
2038
+ export { type AuditEntry, type AuditFieldLabelResolver, type AuditTrailConfig, AuditTrailPanel, type AuditTrailPanelProps, type BackendAdapter, type BulkAction, type ColSpan, type ColumnPreset, ColumnPresetSelector, type ColumnPresetState, CrudDialogShell, CrudDrawerShell, type CrudDrawerSize, type CrudDrawerViewEvents, type CrudDrawerViewOptions, CrudFormShell, type CrudFormShellProps, type CrudGridSlotContext, CrudPage, CrudPageShell, type CrudPageViewEvents, type CrudPageViewOptions, type CrudViewMode, type CrudViewModeConfig, DATA_GRID_EVENTS, DEFAULT_DRAWER_SIZE, DEFAULT_DRAWER_WIDTH, DRAWER_WIDTHS, type DataGridSelectionChangedEvent, type DataGridSummaryItem, NativeDataGridView as DataGridView, type DataGridViewOptions, type DataRecord, type DetailSummaryOptions, CrudDialogView as DialogView, type DrawerSize, CrudDrawerView as DrawerView, type EnumOption, FORM_EVENTS, type Field, FieldBuilder, type FieldColSpanContext, type FieldDef, type FieldInput, type FieldOverride, FieldType, type FilterRule, type FormHandle, type FormLayout, type FormLayoutHint, type FormOnChangeFn, type FormPresentationContext, type FormPresentationMode, type FormSection, type FormTab, NativeFormView as FormView, type FormViewOptions, type FormatterFn, type GridCellContext, type GridData, type GridHandle, type GridOnChangeFn, HydraAdapter, type ItemFormatterFn, type LoadOption, type OnChangeFn, CrudPageView as PageView, type ResolvedViewMode, type ResourceConfig, type ResourceEmptyState, type ResourceFilterDescriptor, type ResourceFilterRule, type ResourceFormDetail, type ResourceGridDetail, type ResourceLoadOption, type ResourceLoadOptions, type ResourcePermissions, type ResourceRouting, type ResourceRowActions, ResourceSchemaProvider, type ResourceSchemaProviderProps, type ResourceSchemaResolution, type ResourceSchemaResolver, type ResourceSortDescriptor, type ResourceStore, type ResourceStoreFactory, type ResourceStoreOptions, ResourceStoreProvider, type ResourceStoreProviderProps, type ResourceToolbar, type ResourceToolbarAction, type ResourceToolbarActionVariant, type ResourceToolbarContext, type ResourceToolbarItems, RestAdapter, type RestQueryDialect, type SmartCrudFieldContract, type SmartCrudFieldOperation, type SmartCrudFieldPatch, type SmartCrudHydraFieldContract, type SmartCrudHydraFieldDirective, type SmartCrudManualField, type SmartCrudManualFieldContract, type SmartCrudOperation, SmartCrudPage, SmartCrudRolesProvider, type SummaryCalculateContext, type SummaryFormat, type SummaryItem, type SummaryTextContext, type SummaryType, ToolbarSelect, type ToolbarSelectOption, type ToolbarSelectProps, type ValidationRule, buildFieldColSpanContext, buildFields, buildWorkflowRowActions, checkboxField, computeSummaryValue, createCrudEvents, createRestResourceStore, crudRoute, currencyField, dateField, datetimeField, defineFieldContract, defineFields, defineResource, embeddedLinesUrl, entityField, enumField, fileField, formatSummaryValue, identityField, imageField, isLongTextField, isShortField, noneField, numberField, parseDrawerWidthPx, passwordField, resolveDrawerLayoutBucket, resolveDrawerSize, resolveDrawerWidth, resolveFieldColSpan, resolveFieldsColSpans, resolveSummaryText, resolveViewMode, selectField, switchField, textField, textareaField, useColumnPreset, useResourceStoreFactory, useSmartCrudRoles, validateFieldContract };
package/dist/index.mjs CHANGED
@@ -2707,6 +2707,10 @@ const NativeDataGridView = forwardRef((options, ref) => {
2707
2707
  })
2708
2708
  ]
2709
2709
  }),
2710
+ options.aboveGrid ? /* @__PURE__ */ jsx("div", {
2711
+ className: "nb-datagrid__above-grid",
2712
+ children: options.aboveGrid
2713
+ }) : null,
2710
2714
  isMobile && quickSearchField && (options.filterRow ?? true) && /* @__PURE__ */ jsxs("div", {
2711
2715
  className: "nb-datagrid__quick-search",
2712
2716
  children: [
@@ -5276,7 +5280,11 @@ function usePermissions(resource, supportedOperations = []) {
5276
5280
  canExport: resolve(p?.canExport, false),
5277
5281
  canBulkDelete: resolve(p?.canBulkDelete, false)
5278
5282
  };
5279
- }, [resource.id, opsKey]);
5283
+ }, [
5284
+ resource.id,
5285
+ opsKey,
5286
+ resource.permissions
5287
+ ]);
5280
5288
  }
5281
5289
  //#endregion
5282
5290
  //#region packages/crud/crud/useSelectionState.ts
@@ -6125,7 +6133,7 @@ function buildRoutingFilterRules(fields, initialFilters) {
6125
6133
  * Public export — wraps CrudPageInner in its own DialogStoreProvider so that
6126
6134
  * existing pages do not need to add a provider themselves.
6127
6135
  */
6128
- const CrudPage = ({ resource, onFormDataChange, initialRecordId, initialIsNew, computedValues, initialFilters, onFiltersChange, formRef, onSelectionChanged, editDisabled, deleteDisabled, gridRef, onOperationChange }) => /* @__PURE__ */ jsx(DialogStoreProvider, { children: /* @__PURE__ */ jsx(CrudPageInner, {
6136
+ const CrudPage = ({ resource, onFormDataChange, initialRecordId, initialIsNew, computedValues, initialFilters, onFiltersChange, formRef, onSelectionChanged, editDisabled, deleteDisabled, gridRef, aboveGrid, onOperationChange }) => /* @__PURE__ */ jsx(DialogStoreProvider, { children: /* @__PURE__ */ jsx(CrudPageInner, {
6129
6137
  resource,
6130
6138
  onFormDataChange,
6131
6139
  initialRecordId,
@@ -6138,9 +6146,10 @@ const CrudPage = ({ resource, onFormDataChange, initialRecordId, initialIsNew, c
6138
6146
  editDisabled,
6139
6147
  deleteDisabled,
6140
6148
  gridRef,
6149
+ aboveGrid,
6141
6150
  onOperationChange
6142
6151
  }) });
6143
- const CrudPageInner = ({ resource, onFormDataChange, initialRecordId = null, initialIsNew = false, computedValues = EMPTY_COMPUTED_VALUES, initialFilters = EMPTY_INITIAL_FILTERS, onFiltersChange, formRef: externalFormRef, onSelectionChanged: externalOnSelectionChanged, editDisabled, deleteDisabled, gridRef: externalGridRef, onOperationChange }) => {
6152
+ const CrudPageInner = ({ resource, onFormDataChange, initialRecordId = null, initialIsNew = false, computedValues = EMPTY_COMPUTED_VALUES, initialFilters = EMPTY_INITIAL_FILTERS, onFiltersChange, formRef: externalFormRef, onSelectionChanged: externalOnSelectionChanged, editDisabled, deleteDisabled, gridRef: externalGridRef, aboveGrid: aboveGridOverride, onOperationChange }) => {
6144
6153
  const { t } = useCoreTranslation();
6145
6154
  const { events, resource: resolvedResource, fields, formFields, formRef, permissions, selectionState, presetState } = useCrudPage(useMemo(() => resolveCrudResource(resource), [resource]), externalFormRef);
6146
6155
  const datagridFields = useMemo(() => fields.filter((field) => field.isIdentity || field.visible !== false), [fields]);
@@ -6403,6 +6412,16 @@ const CrudPageInner = ({ resource, onFormDataChange, initialRecordId = null, ini
6403
6412
  resolvedResource.rowActions,
6404
6413
  t
6405
6414
  ]);
6415
+ const aboveGridSlot = aboveGridOverride ?? resolvedResource.aboveGrid;
6416
+ const aboveGridContent = (() => {
6417
+ if (!aboveGridSlot) return;
6418
+ if (typeof aboveGridSlot === "function") return aboveGridSlot({
6419
+ resource: resolvedResource,
6420
+ gridRef,
6421
+ refresh: () => gridRef.current?.refresh()
6422
+ });
6423
+ return aboveGridSlot;
6424
+ })();
6406
6425
  /** Preset selector rendered as a toolbar slot via `beforeToolbar`. */
6407
6426
  const renderPresetSelector = hasPresets ? () => /* @__PURE__ */ jsx(ColumnPresetSelector, {
6408
6427
  resourceId: resolvedResource.id,
@@ -6465,6 +6484,7 @@ const CrudPageInner = ({ resource, onFormDataChange, initialRecordId = null, ini
6465
6484
  editMode: resolvedResource.editMode,
6466
6485
  visibleColumns: presetState.visibleColumns,
6467
6486
  beforeToolbar: renderPresetSelector,
6487
+ aboveGrid: aboveGridContent,
6468
6488
  detailUrl: gridDetail?.url,
6469
6489
  detailFields: gridDetail?.fields,
6470
6490
  onFilterChange: onFiltersChange,
@@ -7586,7 +7606,7 @@ function formatRuntimeErrorMessage(error) {
7586
7606
  *
7587
7607
  * URL deep-linking is wired via `initialRecordId` / `initialIsNew` props on CrudPage.
7588
7608
  */
7589
- function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged, editDisabled, deleteDisabled, gridRef }) {
7609
+ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged, editDisabled, deleteDisabled, gridRef, aboveGrid }) {
7590
7610
  const queryClient = useQueryClient();
7591
7611
  const internalGridRef = useRef(null);
7592
7612
  const effectiveGridRef = gridRef ?? internalGridRef;
@@ -7671,6 +7691,7 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7671
7691
  editDisabled,
7672
7692
  deleteDisabled,
7673
7693
  gridRef: effectiveGridRef,
7694
+ aboveGrid: aboveGrid ?? resource.aboveGrid,
7674
7695
  onOperationChange: (operation) => {
7675
7696
  if (operation === "create") {
7676
7697
  startCreate();
package/dist/style.css CHANGED
@@ -31,6 +31,15 @@
31
31
  padding-top: var(--content-padding);
32
32
  }
33
33
 
34
+ .nb-datagrid__above-grid {
35
+ display: flex;
36
+ flex: 0 0 auto;
37
+ flex-direction: column;
38
+ gap: var(--space-3);
39
+ margin-bottom: var(--space-3);
40
+ min-width: 0;
41
+ }
42
+
34
43
  .nb-datagrid__toolbar {
35
44
  align-items: center;
36
45
  background: var(--surface-1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nubitio/crud",
3
- "version": "0.5.23",
3
+ "version": "0.5.24",
4
4
  "type": "module",
5
5
  "description": "Declarative CRUD engine with field DSL, forms, datagrids, RBAC, conditional logic and pluggable adapters (Hydra/REST).",
6
6
  "license": "MIT",
@@ -56,7 +56,7 @@
56
56
  "react-dom": "^19.0.0",
57
57
  "react-i18next": "^14.0.0",
58
58
  "react-router-dom": "^6.0.0",
59
- "@nubitio/core": "^0.5.23",
60
- "@nubitio/ui": "^0.5.23"
59
+ "@nubitio/core": "^0.5.24",
60
+ "@nubitio/ui": "^0.5.24"
61
61
  }
62
62
  }