@nubitio/crud 0.5.20 → 0.5.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2110,7 +2110,7 @@ function buildToolbar(options, t, onAddClick, includeAddAction = true) {
2110
2110
  showRefresh: options.toolbar?.showRefresh ?? true
2111
2111
  };
2112
2112
  }
2113
- function SummaryFooter({ fields, hasCheckbox, hasDetail, hasRowActions, rows, summaryFields, footerRef, colWidths }) {
2113
+ function SummaryFooter({ fields, hasCheckbox, hasDetail, hasRowActions, rows, summaryFields, gridSummary, footerRef, colWidths }) {
2114
2114
  if (!summaryFields?.length) return null;
2115
2115
  const itemsByColumn = new Map(summaryFields.filter((item) => item.column).map((item) => [item.column, item]));
2116
2116
  const unboundItems = summaryFields.filter((item) => !item.column);
@@ -2138,7 +2138,7 @@ function SummaryFooter({ fields, hasCheckbox, hasDetail, hasRowActions, rows, su
2138
2138
  children: item.label
2139
2139
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2140
2140
  className: "nb-datagrid__summary-value",
2141
- children: resolveSummaryText(rows, item)
2141
+ children: item.column && gridSummary && item.column in gridSummary ? formatSummaryValue(gridSummary[item.column], item) : resolveSummaryText(rows, item)
2142
2142
  })]
2143
2143
  })
2144
2144
  }, field.name);
@@ -2159,6 +2159,7 @@ const NativeDataGridView = (0, react.forwardRef)((options, ref) => {
2159
2159
  const [rows, setRows] = (0, react.useState)([]);
2160
2160
  const rowsRef = (0, react.useRef)([]);
2161
2161
  const [totalCount, setTotalCount] = (0, react.useState)(0);
2162
+ const [gridSummary, setGridSummary] = (0, react.useState)(null);
2162
2163
  const [selectedKeys, setSelectedKeys] = (0, react.useState)([]);
2163
2164
  const [filters, setFilters] = (0, react.useState)({});
2164
2165
  const [filterInputs, setFilterInputs] = (0, react.useState)({});
@@ -2340,6 +2341,7 @@ const NativeDataGridView = (0, react.forwardRef)((options, ref) => {
2340
2341
  rowsRef.current = result.data;
2341
2342
  setRows(result.data);
2342
2343
  setTotalCount(result.totalCount);
2344
+ setGridSummary(result.gridSummary ?? null);
2343
2345
  setIsGridLoading(false);
2344
2346
  onContentReadyRef.current?.();
2345
2347
  return result.data;
@@ -3135,6 +3137,7 @@ const NativeDataGridView = (0, react.forwardRef)((options, ref) => {
3135
3137
  hasRowActions,
3136
3138
  rows,
3137
3139
  summaryFields: options.summaryFields,
3140
+ gridSummary,
3138
3141
  footerRef: tfootRef,
3139
3142
  colWidths: resolvedColWidths
3140
3143
  })
@@ -3173,7 +3176,7 @@ const NativeDataGridView = (0, react.forwardRef)((options, ref) => {
3173
3176
  children: item.label
3174
3177
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3175
3178
  className: "nb-datagrid__summary-value",
3176
- children: resolveSummaryText(rows, item)
3179
+ children: item.column && gridSummary && item.column in gridSummary ? formatSummaryValue(gridSummary[item.column], item) : resolveSummaryText(rows, item)
3177
3180
  })]
3178
3181
  }, index))
3179
3182
  }),
@@ -7466,7 +7469,7 @@ function ResourceSchemaProvider({ children, resolver }) {
7466
7469
  children
7467
7470
  });
7468
7471
  }
7469
- function resolveWithRuntimeErrors(resolver, supportedOperations = [], formLayout, workflow) {
7472
+ function resolveWithRuntimeErrors(resolver, supportedOperations = [], formLayout, workflow, summaryFields) {
7470
7473
  try {
7471
7474
  return {
7472
7475
  fields: resolver(),
@@ -7474,7 +7477,8 @@ function resolveWithRuntimeErrors(resolver, supportedOperations = [], formLayout
7474
7477
  error: void 0,
7475
7478
  supportedOperations,
7476
7479
  formLayout,
7477
- workflow
7480
+ workflow,
7481
+ summaryFields
7478
7482
  };
7479
7483
  } catch (runtimeError) {
7480
7484
  return {
@@ -7483,7 +7487,8 @@ function resolveWithRuntimeErrors(resolver, supportedOperations = [], formLayout
7483
7487
  error: runtimeError instanceof Error ? runtimeError : new Error(String(runtimeError)),
7484
7488
  supportedOperations,
7485
7489
  formLayout,
7486
- workflow
7490
+ workflow,
7491
+ summaryFields
7487
7492
  };
7488
7493
  }
7489
7494
  }
@@ -7509,7 +7514,7 @@ function useResolvedResourceFields({ apiUrl, manualFields, overrides, fieldContr
7509
7514
  baselineFields: baseline.fields,
7510
7515
  contract: fieldContract,
7511
7516
  legacyOverrides: fieldContract ? void 0 : overrides
7512
- }), baseline.supportedOperations, baseline.formLayout, baseline.workflow);
7517
+ }), baseline.supportedOperations, baseline.formLayout, baseline.workflow, baseline.summaryFields);
7513
7518
  }, [
7514
7519
  baseline,
7515
7520
  fieldContract,
@@ -7611,7 +7616,7 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7611
7616
  const effectiveGridRef = gridRef ?? internalGridRef;
7612
7617
  const resolvedBaseResource = (0, react.useMemo)(() => resolveCrudResource(resource), [resource]);
7613
7618
  const hasManualFields = !resource.fieldContract && Array.isArray(resource.fields) && resource.fields.length > 0;
7614
- const { fields, isLoading, error, supportedOperations, formLayout: inferredFormLayout, workflow } = useResolvedResourceFields({
7619
+ const { fields, isLoading, error, supportedOperations, formLayout: inferredFormLayout, workflow, summaryFields: inferredSummaryFields } = useResolvedResourceFields({
7615
7620
  apiUrl: resolvedBaseResource.apiUrl,
7616
7621
  manualFields: hasManualFields ? buildFields(resource.fields) : void 0,
7617
7622
  overrides: hasManualFields ? void 0 : fieldOverrides,
@@ -7653,6 +7658,7 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7653
7658
  fields: hasManualFields ? buildFields(resource.fields) : gridFields,
7654
7659
  formFields,
7655
7660
  formLayout: resolvedBaseResource.formLayout ?? inferredFormLayout,
7661
+ summaryFields: resolvedBaseResource.summaryFields ?? inferredSummaryFields,
7656
7662
  _supportedOperations: supportedOperations,
7657
7663
  rowActions
7658
7664
  };
@@ -7662,6 +7668,7 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7662
7668
  gridFields,
7663
7669
  hasManualFields,
7664
7670
  inferredFormLayout,
7671
+ inferredSummaryFields,
7665
7672
  normalizedApiUrl,
7666
7673
  formFields,
7667
7674
  resolvedBaseResource,
@@ -8627,15 +8634,26 @@ function createRestResourceStore(dialect = {}) {
8627
8634
  if (result === null) return {
8628
8635
  data: [],
8629
8636
  totalCount: 0,
8630
- summary: null
8637
+ summary: null,
8638
+ gridSummary: null
8631
8639
  };
8632
8640
  const body = result.data;
8633
8641
  const data = Array.isArray(body) ? body : body.items ?? body.data ?? [];
8634
8642
  const headerTotal = Number(result.headers.get("x-total-count"));
8643
+ const totalCount = Array.isArray(body) ? Number.isFinite(headerTotal) && headerTotal > 0 ? headerTotal : body.length : body.total ?? body.totalCount ?? data.length;
8644
+ const gridSummaryHeader = result.headers.get("x-grid-summary");
8645
+ let gridSummary = null;
8646
+ if (gridSummaryHeader) try {
8647
+ const parsed = JSON.parse(gridSummaryHeader);
8648
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) gridSummary = parsed;
8649
+ } catch {
8650
+ gridSummary = null;
8651
+ }
8635
8652
  return {
8636
8653
  data,
8637
- totalCount: Array.isArray(body) ? Number.isFinite(headerTotal) && headerTotal > 0 ? headerTotal : body.length : body.total ?? body.totalCount ?? data.length,
8638
- summary: null
8654
+ totalCount,
8655
+ summary: null,
8656
+ gridSummary
8639
8657
  };
8640
8658
  },
8641
8659
  async byKey(key) {
package/dist/index.d.cts CHANGED
@@ -1549,6 +1549,8 @@ interface DataGridViewOptions {
1549
1549
  /** Per-row gate for the Delete action. Absent = allowed. */
1550
1550
  canDeleteRow?: (row: DataRecord$1) => boolean;
1551
1551
  summaryFields?: DataGridSummaryItem[];
1552
+ /** Server-side aggregates for the current filtered collection. */
1553
+ gridSummary?: Record<string, unknown> | null;
1552
1554
  filter?: FilterRule[];
1553
1555
  sort?: Array<{
1554
1556
  selector: string;
@@ -1919,6 +1921,7 @@ interface ResourceSchemaResolution {
1919
1921
  */
1920
1922
  formLayout?: FormLayout;
1921
1923
  workflow?: WorkflowSchema;
1924
+ summaryFields?: SummaryItem[];
1922
1925
  }
1923
1926
  interface ResourceSchemaResolver {
1924
1927
  useResourceSchema(request: ResourceSchemaRequest): ResourceSchemaResolution;
package/dist/index.d.mts CHANGED
@@ -1549,6 +1549,8 @@ interface DataGridViewOptions {
1549
1549
  /** Per-row gate for the Delete action. Absent = allowed. */
1550
1550
  canDeleteRow?: (row: DataRecord$1) => boolean;
1551
1551
  summaryFields?: DataGridSummaryItem[];
1552
+ /** Server-side aggregates for the current filtered collection. */
1553
+ gridSummary?: Record<string, unknown> | null;
1552
1554
  filter?: FilterRule[];
1553
1555
  sort?: Array<{
1554
1556
  selector: string;
@@ -1919,6 +1921,7 @@ interface ResourceSchemaResolution {
1919
1921
  */
1920
1922
  formLayout?: FormLayout;
1921
1923
  workflow?: WorkflowSchema;
1924
+ summaryFields?: SummaryItem[];
1922
1925
  }
1923
1926
  interface ResourceSchemaResolver {
1924
1927
  useResourceSchema(request: ResourceSchemaRequest): ResourceSchemaResolution;
package/dist/index.mjs CHANGED
@@ -2086,7 +2086,7 @@ function buildToolbar(options, t, onAddClick, includeAddAction = true) {
2086
2086
  showRefresh: options.toolbar?.showRefresh ?? true
2087
2087
  };
2088
2088
  }
2089
- function SummaryFooter({ fields, hasCheckbox, hasDetail, hasRowActions, rows, summaryFields, footerRef, colWidths }) {
2089
+ function SummaryFooter({ fields, hasCheckbox, hasDetail, hasRowActions, rows, summaryFields, gridSummary, footerRef, colWidths }) {
2090
2090
  if (!summaryFields?.length) return null;
2091
2091
  const itemsByColumn = new Map(summaryFields.filter((item) => item.column).map((item) => [item.column, item]));
2092
2092
  const unboundItems = summaryFields.filter((item) => !item.column);
@@ -2114,7 +2114,7 @@ function SummaryFooter({ fields, hasCheckbox, hasDetail, hasRowActions, rows, su
2114
2114
  children: item.label
2115
2115
  }), /* @__PURE__ */ jsx("span", {
2116
2116
  className: "nb-datagrid__summary-value",
2117
- children: resolveSummaryText(rows, item)
2117
+ children: item.column && gridSummary && item.column in gridSummary ? formatSummaryValue(gridSummary[item.column], item) : resolveSummaryText(rows, item)
2118
2118
  })]
2119
2119
  })
2120
2120
  }, field.name);
@@ -2135,6 +2135,7 @@ const NativeDataGridView = forwardRef((options, ref) => {
2135
2135
  const [rows, setRows] = useState([]);
2136
2136
  const rowsRef = useRef([]);
2137
2137
  const [totalCount, setTotalCount] = useState(0);
2138
+ const [gridSummary, setGridSummary] = useState(null);
2138
2139
  const [selectedKeys, setSelectedKeys] = useState([]);
2139
2140
  const [filters, setFilters] = useState({});
2140
2141
  const [filterInputs, setFilterInputs] = useState({});
@@ -2316,6 +2317,7 @@ const NativeDataGridView = forwardRef((options, ref) => {
2316
2317
  rowsRef.current = result.data;
2317
2318
  setRows(result.data);
2318
2319
  setTotalCount(result.totalCount);
2320
+ setGridSummary(result.gridSummary ?? null);
2319
2321
  setIsGridLoading(false);
2320
2322
  onContentReadyRef.current?.();
2321
2323
  return result.data;
@@ -3111,6 +3113,7 @@ const NativeDataGridView = forwardRef((options, ref) => {
3111
3113
  hasRowActions,
3112
3114
  rows,
3113
3115
  summaryFields: options.summaryFields,
3116
+ gridSummary,
3114
3117
  footerRef: tfootRef,
3115
3118
  colWidths: resolvedColWidths
3116
3119
  })
@@ -3149,7 +3152,7 @@ const NativeDataGridView = forwardRef((options, ref) => {
3149
3152
  children: item.label
3150
3153
  }), /* @__PURE__ */ jsx("span", {
3151
3154
  className: "nb-datagrid__summary-value",
3152
- children: resolveSummaryText(rows, item)
3155
+ children: item.column && gridSummary && item.column in gridSummary ? formatSummaryValue(gridSummary[item.column], item) : resolveSummaryText(rows, item)
3153
3156
  })]
3154
3157
  }, index))
3155
3158
  }),
@@ -7442,7 +7445,7 @@ function ResourceSchemaProvider({ children, resolver }) {
7442
7445
  children
7443
7446
  });
7444
7447
  }
7445
- function resolveWithRuntimeErrors(resolver, supportedOperations = [], formLayout, workflow) {
7448
+ function resolveWithRuntimeErrors(resolver, supportedOperations = [], formLayout, workflow, summaryFields) {
7446
7449
  try {
7447
7450
  return {
7448
7451
  fields: resolver(),
@@ -7450,7 +7453,8 @@ function resolveWithRuntimeErrors(resolver, supportedOperations = [], formLayout
7450
7453
  error: void 0,
7451
7454
  supportedOperations,
7452
7455
  formLayout,
7453
- workflow
7456
+ workflow,
7457
+ summaryFields
7454
7458
  };
7455
7459
  } catch (runtimeError) {
7456
7460
  return {
@@ -7459,7 +7463,8 @@ function resolveWithRuntimeErrors(resolver, supportedOperations = [], formLayout
7459
7463
  error: runtimeError instanceof Error ? runtimeError : new Error(String(runtimeError)),
7460
7464
  supportedOperations,
7461
7465
  formLayout,
7462
- workflow
7466
+ workflow,
7467
+ summaryFields
7463
7468
  };
7464
7469
  }
7465
7470
  }
@@ -7485,7 +7490,7 @@ function useResolvedResourceFields({ apiUrl, manualFields, overrides, fieldContr
7485
7490
  baselineFields: baseline.fields,
7486
7491
  contract: fieldContract,
7487
7492
  legacyOverrides: fieldContract ? void 0 : overrides
7488
- }), baseline.supportedOperations, baseline.formLayout, baseline.workflow);
7493
+ }), baseline.supportedOperations, baseline.formLayout, baseline.workflow, baseline.summaryFields);
7489
7494
  }, [
7490
7495
  baseline,
7491
7496
  fieldContract,
@@ -7587,7 +7592,7 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7587
7592
  const effectiveGridRef = gridRef ?? internalGridRef;
7588
7593
  const resolvedBaseResource = useMemo(() => resolveCrudResource(resource), [resource]);
7589
7594
  const hasManualFields = !resource.fieldContract && Array.isArray(resource.fields) && resource.fields.length > 0;
7590
- const { fields, isLoading, error, supportedOperations, formLayout: inferredFormLayout, workflow } = useResolvedResourceFields({
7595
+ const { fields, isLoading, error, supportedOperations, formLayout: inferredFormLayout, workflow, summaryFields: inferredSummaryFields } = useResolvedResourceFields({
7591
7596
  apiUrl: resolvedBaseResource.apiUrl,
7592
7597
  manualFields: hasManualFields ? buildFields(resource.fields) : void 0,
7593
7598
  overrides: hasManualFields ? void 0 : fieldOverrides,
@@ -7629,6 +7634,7 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7629
7634
  fields: hasManualFields ? buildFields(resource.fields) : gridFields,
7630
7635
  formFields,
7631
7636
  formLayout: resolvedBaseResource.formLayout ?? inferredFormLayout,
7637
+ summaryFields: resolvedBaseResource.summaryFields ?? inferredSummaryFields,
7632
7638
  _supportedOperations: supportedOperations,
7633
7639
  rowActions
7634
7640
  };
@@ -7638,6 +7644,7 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7638
7644
  gridFields,
7639
7645
  hasManualFields,
7640
7646
  inferredFormLayout,
7647
+ inferredSummaryFields,
7641
7648
  normalizedApiUrl,
7642
7649
  formFields,
7643
7650
  resolvedBaseResource,
@@ -8603,15 +8610,26 @@ function createRestResourceStore(dialect = {}) {
8603
8610
  if (result === null) return {
8604
8611
  data: [],
8605
8612
  totalCount: 0,
8606
- summary: null
8613
+ summary: null,
8614
+ gridSummary: null
8607
8615
  };
8608
8616
  const body = result.data;
8609
8617
  const data = Array.isArray(body) ? body : body.items ?? body.data ?? [];
8610
8618
  const headerTotal = Number(result.headers.get("x-total-count"));
8619
+ const totalCount = Array.isArray(body) ? Number.isFinite(headerTotal) && headerTotal > 0 ? headerTotal : body.length : body.total ?? body.totalCount ?? data.length;
8620
+ const gridSummaryHeader = result.headers.get("x-grid-summary");
8621
+ let gridSummary = null;
8622
+ if (gridSummaryHeader) try {
8623
+ const parsed = JSON.parse(gridSummaryHeader);
8624
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) gridSummary = parsed;
8625
+ } catch {
8626
+ gridSummary = null;
8627
+ }
8611
8628
  return {
8612
8629
  data,
8613
- totalCount: Array.isArray(body) ? Number.isFinite(headerTotal) && headerTotal > 0 ? headerTotal : body.length : body.total ?? body.totalCount ?? data.length,
8614
- summary: null
8630
+ totalCount,
8631
+ summary: null,
8632
+ gridSummary
8615
8633
  };
8616
8634
  },
8617
8635
  async byKey(key) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nubitio/crud",
3
- "version": "0.5.20",
3
+ "version": "0.5.22",
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.20",
60
- "@nubitio/ui": "^0.5.20"
59
+ "@nubitio/core": "^0.5.22",
60
+ "@nubitio/ui": "^0.5.22"
61
61
  }
62
62
  }