@nubitio/crud 0.5.16 → 0.5.20

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
@@ -62,6 +62,17 @@ function defineResource(apiUrl, overrides) {
62
62
  return resource;
63
63
  }
64
64
  //#endregion
65
+ //#region packages/crud/crud/embeddedLinesUrl.ts
66
+ /**
67
+ * Builds a formDetail reload URL for {@code #[EmbeddedLines]} line entities.
68
+ *
69
+ * @example embeddedLinesUrl('/api/sales_document_lines', 'document')
70
+ * → '/api/sales_document_lines?document={id}'
71
+ */
72
+ function embeddedLinesUrl(route, parentQueryParam) {
73
+ return `${route}${route.includes("?") ? "&" : "?"}${parentQueryParam}={id}`;
74
+ }
75
+ //#endregion
65
76
  //#region packages/crud/datagrid/DataGridEvents.ts
66
77
  const DATA_GRID_EVENTS = {
67
78
  SELECTION_CHANGED: "datagrid:selection.changed",
@@ -3588,6 +3599,17 @@ function normalizeEntityField(row, field, adapter, prependDataByField) {
3588
3599
  }
3589
3600
  }
3590
3601
  //#endregion
3602
+ //#region packages/crud/form/loadDetailRows.ts
3603
+ /**
3604
+ * Loads embedded line rows for formDetail edit mode. Accepts both plain JSON
3605
+ * arrays (nubit embedded-lines endpoint) and Hydra collections.
3606
+ */
3607
+ async function loadDetailRows(httpClient, detailUrl, adapter) {
3608
+ const response = await httpClient.get(detailUrl);
3609
+ const { items } = (adapter ?? HydraAdapter).parseListResponse(response.data);
3610
+ return items;
3611
+ }
3612
+ //#endregion
3591
3613
  //#region packages/crud/form/safeRandomId.ts
3592
3614
  /**
3593
3615
  * Generate a unique-enough id string for internal React keys.
@@ -4648,7 +4670,7 @@ const NativeFormView = (0, react.forwardRef)((options, ref) => {
4648
4670
  const detailUrl = typeof detailId === "string" || typeof detailId === "number" ? options.detailUrl?.replace("{id}", String(detailId)) : void 0;
4649
4671
  if (!detailUrl) return;
4650
4672
  emit(FORM_EVENTS.LOADING, true);
4651
- httpClient.get(detailUrl).then((response) => setNextDetailRows(response.data)).finally(() => emit(FORM_EVENTS.LOADING, false));
4673
+ loadDetailRows(httpClient, detailUrl, options.adapter).then((rows) => setNextDetailRows(rows)).finally(() => emit(FORM_EVENTS.LOADING, false));
4652
4674
  }, [
4653
4675
  captureExistingMedia,
4654
4676
  emit,
@@ -7444,14 +7466,15 @@ function ResourceSchemaProvider({ children, resolver }) {
7444
7466
  children
7445
7467
  });
7446
7468
  }
7447
- function resolveWithRuntimeErrors(resolver, supportedOperations = [], formLayout) {
7469
+ function resolveWithRuntimeErrors(resolver, supportedOperations = [], formLayout, workflow) {
7448
7470
  try {
7449
7471
  return {
7450
7472
  fields: resolver(),
7451
7473
  isLoading: false,
7452
7474
  error: void 0,
7453
7475
  supportedOperations,
7454
- formLayout
7476
+ formLayout,
7477
+ workflow
7455
7478
  };
7456
7479
  } catch (runtimeError) {
7457
7480
  return {
@@ -7459,7 +7482,8 @@ function resolveWithRuntimeErrors(resolver, supportedOperations = [], formLayout
7459
7482
  isLoading: false,
7460
7483
  error: runtimeError instanceof Error ? runtimeError : new Error(String(runtimeError)),
7461
7484
  supportedOperations,
7462
- formLayout
7485
+ formLayout,
7486
+ workflow
7463
7487
  };
7464
7488
  }
7465
7489
  }
@@ -7485,7 +7509,7 @@ function useResolvedResourceFields({ apiUrl, manualFields, overrides, fieldContr
7485
7509
  baselineFields: baseline.fields,
7486
7510
  contract: fieldContract,
7487
7511
  legacyOverrides: fieldContract ? void 0 : overrides
7488
- }), baseline.supportedOperations, baseline.formLayout);
7512
+ }), baseline.supportedOperations, baseline.formLayout, baseline.workflow);
7489
7513
  }, [
7490
7514
  baseline,
7491
7515
  fieldContract,
@@ -7493,6 +7517,28 @@ function useResolvedResourceFields({ apiUrl, manualFields, overrides, fieldContr
7493
7517
  ]);
7494
7518
  }
7495
7519
  //#endregion
7520
+ //#region packages/crud/workflow/buildWorkflowRowActions.ts
7521
+ function buildWorkflowRowActions(row, workflow, apiUrl, roles, onDone) {
7522
+ if (!workflow) return [];
7523
+ const current = String(row[workflow.field] ?? "");
7524
+ return workflow.transitions.filter((transition) => transition.from.includes(current)).filter((transition) => !transition.roles?.length || transition.roles.some((role) => roles.includes(role))).map((transition) => ({
7525
+ text: transition.label ?? transition.name,
7526
+ onClick: async () => {
7527
+ const base = apiUrl.replace(/\/$/, "");
7528
+ const id = row.id;
7529
+ const response = await fetch(`${base}/${id}/transition/${transition.name}`, {
7530
+ method: "POST",
7531
+ credentials: "include"
7532
+ });
7533
+ if (!response.ok) {
7534
+ const detail = await response.text().catch(() => "");
7535
+ throw new Error(detail || `Transition "${transition.name}" failed (${response.status})`);
7536
+ }
7537
+ onDone?.();
7538
+ }
7539
+ }));
7540
+ }
7541
+ //#endregion
7496
7542
  //#region packages/crud/crud/SmartCrudPage.tsx
7497
7543
  function CrudSkeleton() {
7498
7544
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -7565,7 +7611,7 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7565
7611
  const effectiveGridRef = gridRef ?? internalGridRef;
7566
7612
  const resolvedBaseResource = (0, react.useMemo)(() => resolveCrudResource(resource), [resource]);
7567
7613
  const hasManualFields = !resource.fieldContract && Array.isArray(resource.fields) && resource.fields.length > 0;
7568
- const { fields, isLoading, error, supportedOperations, formLayout: inferredFormLayout } = useResolvedResourceFields({
7614
+ const { fields, isLoading, error, supportedOperations, formLayout: inferredFormLayout, workflow } = useResolvedResourceFields({
7569
7615
  apiUrl: resolvedBaseResource.apiUrl,
7570
7616
  manualFields: hasManualFields ? buildFields(resource.fields) : void 0,
7571
7617
  overrides: hasManualFields ? void 0 : fieldOverrides,
@@ -7589,21 +7635,29 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7589
7635
  const routingState = useRouting(resource.routing);
7590
7636
  const { activeOperation, formData, handleFormDataChange, startCreate, startEdit, resetOperation } = useSmartCrudOperation(void 0, routingState);
7591
7637
  const roles = useSmartCrudRoles();
7592
- const { gridFields, processedFields, computedValues } = useSmartCrudFields(fields, activeOperation, formData, (0, react.useMemo)(() => roles ?? [], [roles]));
7638
+ const stableRoles = (0, react.useMemo)(() => roles ?? [], [roles]);
7639
+ const { gridFields, processedFields, computedValues } = useSmartCrudFields(fields, activeOperation, formData, stableRoles);
7593
7640
  const formFields = (0, react.useMemo)(() => applyFormDetailFormFieldOverrides(processedFields, resolvedBaseResource.formDetail), [processedFields, resolvedBaseResource.formDetail]);
7594
7641
  (0, _nubitio_core.useMercureSubscription)(resource.apiUrl, () => {
7595
7642
  effectiveGridRef.current?.refresh();
7596
7643
  }, resolvedBaseResource.mercure !== false);
7597
7644
  const normalizedApiUrl = resolvedBaseResource.apiUrl.startsWith("/") ? resolvedBaseResource.apiUrl : `/${resolvedBaseResource.apiUrl}`;
7598
- const resolvedResource = (0, react.useMemo)(() => ({
7599
- ...resolvedBaseResource,
7600
- ...!hasManualFields ? { fields: gridFields } : {},
7601
- apiUrl: normalizedApiUrl,
7602
- fields: hasManualFields ? buildFields(resource.fields) : gridFields,
7603
- formFields,
7604
- formLayout: resolvedBaseResource.formLayout ?? inferredFormLayout,
7605
- _supportedOperations: supportedOperations
7606
- }), [
7645
+ const resolvedResource = (0, react.useMemo)(() => {
7646
+ const rowActions = resolvedBaseResource.rowActions ?? (workflow ? (row) => buildWorkflowRowActions(row, workflow, normalizedApiUrl, stableRoles, () => {
7647
+ effectiveGridRef.current?.refresh();
7648
+ }) : void 0);
7649
+ return {
7650
+ ...resolvedBaseResource,
7651
+ ...!hasManualFields ? { fields: gridFields } : {},
7652
+ apiUrl: normalizedApiUrl,
7653
+ fields: hasManualFields ? buildFields(resource.fields) : gridFields,
7654
+ formFields,
7655
+ formLayout: resolvedBaseResource.formLayout ?? inferredFormLayout,
7656
+ _supportedOperations: supportedOperations,
7657
+ rowActions
7658
+ };
7659
+ }, [
7660
+ effectiveGridRef,
7607
7661
  fields,
7608
7662
  gridFields,
7609
7663
  hasManualFields,
@@ -7612,7 +7666,9 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7612
7666
  formFields,
7613
7667
  resolvedBaseResource,
7614
7668
  resource.fields,
7615
- supportedOperations
7669
+ stableRoles,
7670
+ supportedOperations,
7671
+ workflow
7616
7672
  ]);
7617
7673
  if (!hasManualFields && isLoading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CrudSkeleton, {});
7618
7674
  if (!hasManualFields && error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CrudError, {
@@ -8617,6 +8673,7 @@ exports.SmartCrudRolesProvider = SmartCrudRolesProvider;
8617
8673
  exports.ToolbarSelect = ToolbarSelect;
8618
8674
  exports.buildFieldColSpanContext = buildFieldColSpanContext;
8619
8675
  exports.buildFields = buildFields;
8676
+ exports.buildWorkflowRowActions = buildWorkflowRowActions;
8620
8677
  exports.checkboxField = checkboxField;
8621
8678
  exports.computeSummaryValue = computeSummaryValue;
8622
8679
  Object.defineProperty(exports, "createCrudEvents", {
@@ -8633,6 +8690,7 @@ exports.datetimeField = datetimeField;
8633
8690
  exports.defineFieldContract = defineFieldContract;
8634
8691
  exports.defineFields = defineFields;
8635
8692
  exports.defineResource = defineResource;
8693
+ exports.embeddedLinesUrl = embeddedLinesUrl;
8636
8694
  exports.entityField = entityField;
8637
8695
  exports.enumField = enumField;
8638
8696
  exports.fileField = fileField;
package/dist/index.d.cts CHANGED
@@ -1,6 +1,7 @@
1
1
  import React, { ReactElement, ReactNode, RefObject } from "react";
2
2
  import { CoreHttpClient, DataGridEventNames, DataRecord, DataRecord as DataRecord$1, DialogEventNames, FormEventNames, GridData, GridData as GridData$1, createCrudEvents } from "@nubitio/core";
3
3
  import { AppDropdownOption } from "@nubitio/ui";
4
+ import { WorkflowSchema } from "@nubitio/hydra";
4
5
 
5
6
  //#region packages/crud/field/FieldType.d.ts
6
7
  declare enum FieldType {
@@ -1013,6 +1014,15 @@ declare function defineResource<T extends DataRecord$1>(apiUrl: string, override
1013
1014
  id?: string;
1014
1015
  }): ResourceConfig<T>;
1015
1016
  //#endregion
1017
+ //#region packages/crud/crud/embeddedLinesUrl.d.ts
1018
+ /**
1019
+ * Builds a formDetail reload URL for {@code #[EmbeddedLines]} line entities.
1020
+ *
1021
+ * @example embeddedLinesUrl('/api/sales_document_lines', 'document')
1022
+ * → '/api/sales_document_lines?document={id}'
1023
+ */
1024
+ declare function embeddedLinesUrl(route: string, parentQueryParam: string): string;
1025
+ //#endregion
1016
1026
  //#region packages/crud/crud/CrudPage.d.ts
1017
1027
  interface CrudPageProps<T extends DataRecord$1 = DataRecord$1> {
1018
1028
  resource: ResourceConfig<T>;
@@ -1867,6 +1877,9 @@ interface ColumnPresetState {
1867
1877
  }
1868
1878
  declare function useColumnPreset(resource: ResourceConfig): ColumnPresetState;
1869
1879
  //#endregion
1880
+ //#region packages/crud/workflow/buildWorkflowRowActions.d.ts
1881
+ declare function buildWorkflowRowActions<T extends DataRecord$1 = DataRecord$1>(row: T, workflow: WorkflowSchema | undefined, apiUrl: string, roles: string[], onDone?: () => void): ResourceToolbarAction[];
1882
+ //#endregion
1870
1883
  //#region packages/crud/adapter/HydraAdapter.d.ts
1871
1884
  /**
1872
1885
  * Default backend adapter for API Platform / JSON-LD + Hydra backends.
@@ -1905,6 +1918,7 @@ interface ResourceSchemaResolution {
1905
1918
  * resource publishes one. Explicit `ResourceConfig.formLayout` wins.
1906
1919
  */
1907
1920
  formLayout?: FormLayout;
1921
+ workflow?: WorkflowSchema;
1908
1922
  }
1909
1923
  interface ResourceSchemaResolver {
1910
1924
  useResourceSchema(request: ResourceSchemaRequest): ResourceSchemaResolution;
@@ -1999,4 +2013,4 @@ interface RestQueryDialect {
1999
2013
  */
2000
2014
  declare function createRestResourceStore(dialect?: RestQueryDialect): ResourceStoreFactory;
2001
2015
  //#endregion
2002
- 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, checkboxField, computeSummaryValue, createCrudEvents, createRestResourceStore, crudRoute, currencyField, dateField, datetimeField, defineFieldContract, defineFields, defineResource, 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 };
2016
+ 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 };
package/dist/index.d.mts CHANGED
@@ -1,6 +1,7 @@
1
1
  import React, { ReactElement, ReactNode, RefObject } from "react";
2
2
  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
+ import { WorkflowSchema } from "@nubitio/hydra";
4
5
 
5
6
  //#region packages/crud/field/FieldType.d.ts
6
7
  declare enum FieldType {
@@ -1013,6 +1014,15 @@ declare function defineResource<T extends DataRecord$1>(apiUrl: string, override
1013
1014
  id?: string;
1014
1015
  }): ResourceConfig<T>;
1015
1016
  //#endregion
1017
+ //#region packages/crud/crud/embeddedLinesUrl.d.ts
1018
+ /**
1019
+ * Builds a formDetail reload URL for {@code #[EmbeddedLines]} line entities.
1020
+ *
1021
+ * @example embeddedLinesUrl('/api/sales_document_lines', 'document')
1022
+ * → '/api/sales_document_lines?document={id}'
1023
+ */
1024
+ declare function embeddedLinesUrl(route: string, parentQueryParam: string): string;
1025
+ //#endregion
1016
1026
  //#region packages/crud/crud/CrudPage.d.ts
1017
1027
  interface CrudPageProps<T extends DataRecord$1 = DataRecord$1> {
1018
1028
  resource: ResourceConfig<T>;
@@ -1867,6 +1877,9 @@ interface ColumnPresetState {
1867
1877
  }
1868
1878
  declare function useColumnPreset(resource: ResourceConfig): ColumnPresetState;
1869
1879
  //#endregion
1880
+ //#region packages/crud/workflow/buildWorkflowRowActions.d.ts
1881
+ declare function buildWorkflowRowActions<T extends DataRecord$1 = DataRecord$1>(row: T, workflow: WorkflowSchema | undefined, apiUrl: string, roles: string[], onDone?: () => void): ResourceToolbarAction[];
1882
+ //#endregion
1870
1883
  //#region packages/crud/adapter/HydraAdapter.d.ts
1871
1884
  /**
1872
1885
  * Default backend adapter for API Platform / JSON-LD + Hydra backends.
@@ -1905,6 +1918,7 @@ interface ResourceSchemaResolution {
1905
1918
  * resource publishes one. Explicit `ResourceConfig.formLayout` wins.
1906
1919
  */
1907
1920
  formLayout?: FormLayout;
1921
+ workflow?: WorkflowSchema;
1908
1922
  }
1909
1923
  interface ResourceSchemaResolver {
1910
1924
  useResourceSchema(request: ResourceSchemaRequest): ResourceSchemaResolution;
@@ -1999,4 +2013,4 @@ interface RestQueryDialect {
1999
2013
  */
2000
2014
  declare function createRestResourceStore(dialect?: RestQueryDialect): ResourceStoreFactory;
2001
2015
  //#endregion
2002
- 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, checkboxField, computeSummaryValue, createCrudEvents, createRestResourceStore, crudRoute, currencyField, dateField, datetimeField, defineFieldContract, defineFields, defineResource, 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 };
2016
+ 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 };
package/dist/index.mjs CHANGED
@@ -38,6 +38,17 @@ function defineResource(apiUrl, overrides) {
38
38
  return resource;
39
39
  }
40
40
  //#endregion
41
+ //#region packages/crud/crud/embeddedLinesUrl.ts
42
+ /**
43
+ * Builds a formDetail reload URL for {@code #[EmbeddedLines]} line entities.
44
+ *
45
+ * @example embeddedLinesUrl('/api/sales_document_lines', 'document')
46
+ * → '/api/sales_document_lines?document={id}'
47
+ */
48
+ function embeddedLinesUrl(route, parentQueryParam) {
49
+ return `${route}${route.includes("?") ? "&" : "?"}${parentQueryParam}={id}`;
50
+ }
51
+ //#endregion
41
52
  //#region packages/crud/datagrid/DataGridEvents.ts
42
53
  const DATA_GRID_EVENTS = {
43
54
  SELECTION_CHANGED: "datagrid:selection.changed",
@@ -3564,6 +3575,17 @@ function normalizeEntityField(row, field, adapter, prependDataByField) {
3564
3575
  }
3565
3576
  }
3566
3577
  //#endregion
3578
+ //#region packages/crud/form/loadDetailRows.ts
3579
+ /**
3580
+ * Loads embedded line rows for formDetail edit mode. Accepts both plain JSON
3581
+ * arrays (nubit embedded-lines endpoint) and Hydra collections.
3582
+ */
3583
+ async function loadDetailRows(httpClient, detailUrl, adapter) {
3584
+ const response = await httpClient.get(detailUrl);
3585
+ const { items } = (adapter ?? HydraAdapter).parseListResponse(response.data);
3586
+ return items;
3587
+ }
3588
+ //#endregion
3567
3589
  //#region packages/crud/form/safeRandomId.ts
3568
3590
  /**
3569
3591
  * Generate a unique-enough id string for internal React keys.
@@ -4624,7 +4646,7 @@ const NativeFormView = forwardRef((options, ref) => {
4624
4646
  const detailUrl = typeof detailId === "string" || typeof detailId === "number" ? options.detailUrl?.replace("{id}", String(detailId)) : void 0;
4625
4647
  if (!detailUrl) return;
4626
4648
  emit(FORM_EVENTS.LOADING, true);
4627
- httpClient.get(detailUrl).then((response) => setNextDetailRows(response.data)).finally(() => emit(FORM_EVENTS.LOADING, false));
4649
+ loadDetailRows(httpClient, detailUrl, options.adapter).then((rows) => setNextDetailRows(rows)).finally(() => emit(FORM_EVENTS.LOADING, false));
4628
4650
  }, [
4629
4651
  captureExistingMedia,
4630
4652
  emit,
@@ -7420,14 +7442,15 @@ function ResourceSchemaProvider({ children, resolver }) {
7420
7442
  children
7421
7443
  });
7422
7444
  }
7423
- function resolveWithRuntimeErrors(resolver, supportedOperations = [], formLayout) {
7445
+ function resolveWithRuntimeErrors(resolver, supportedOperations = [], formLayout, workflow) {
7424
7446
  try {
7425
7447
  return {
7426
7448
  fields: resolver(),
7427
7449
  isLoading: false,
7428
7450
  error: void 0,
7429
7451
  supportedOperations,
7430
- formLayout
7452
+ formLayout,
7453
+ workflow
7431
7454
  };
7432
7455
  } catch (runtimeError) {
7433
7456
  return {
@@ -7435,7 +7458,8 @@ function resolveWithRuntimeErrors(resolver, supportedOperations = [], formLayout
7435
7458
  isLoading: false,
7436
7459
  error: runtimeError instanceof Error ? runtimeError : new Error(String(runtimeError)),
7437
7460
  supportedOperations,
7438
- formLayout
7461
+ formLayout,
7462
+ workflow
7439
7463
  };
7440
7464
  }
7441
7465
  }
@@ -7461,7 +7485,7 @@ function useResolvedResourceFields({ apiUrl, manualFields, overrides, fieldContr
7461
7485
  baselineFields: baseline.fields,
7462
7486
  contract: fieldContract,
7463
7487
  legacyOverrides: fieldContract ? void 0 : overrides
7464
- }), baseline.supportedOperations, baseline.formLayout);
7488
+ }), baseline.supportedOperations, baseline.formLayout, baseline.workflow);
7465
7489
  }, [
7466
7490
  baseline,
7467
7491
  fieldContract,
@@ -7469,6 +7493,28 @@ function useResolvedResourceFields({ apiUrl, manualFields, overrides, fieldContr
7469
7493
  ]);
7470
7494
  }
7471
7495
  //#endregion
7496
+ //#region packages/crud/workflow/buildWorkflowRowActions.ts
7497
+ function buildWorkflowRowActions(row, workflow, apiUrl, roles, onDone) {
7498
+ if (!workflow) return [];
7499
+ const current = String(row[workflow.field] ?? "");
7500
+ return workflow.transitions.filter((transition) => transition.from.includes(current)).filter((transition) => !transition.roles?.length || transition.roles.some((role) => roles.includes(role))).map((transition) => ({
7501
+ text: transition.label ?? transition.name,
7502
+ onClick: async () => {
7503
+ const base = apiUrl.replace(/\/$/, "");
7504
+ const id = row.id;
7505
+ const response = await fetch(`${base}/${id}/transition/${transition.name}`, {
7506
+ method: "POST",
7507
+ credentials: "include"
7508
+ });
7509
+ if (!response.ok) {
7510
+ const detail = await response.text().catch(() => "");
7511
+ throw new Error(detail || `Transition "${transition.name}" failed (${response.status})`);
7512
+ }
7513
+ onDone?.();
7514
+ }
7515
+ }));
7516
+ }
7517
+ //#endregion
7472
7518
  //#region packages/crud/crud/SmartCrudPage.tsx
7473
7519
  function CrudSkeleton() {
7474
7520
  return /* @__PURE__ */ jsxs("div", {
@@ -7541,7 +7587,7 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7541
7587
  const effectiveGridRef = gridRef ?? internalGridRef;
7542
7588
  const resolvedBaseResource = useMemo(() => resolveCrudResource(resource), [resource]);
7543
7589
  const hasManualFields = !resource.fieldContract && Array.isArray(resource.fields) && resource.fields.length > 0;
7544
- const { fields, isLoading, error, supportedOperations, formLayout: inferredFormLayout } = useResolvedResourceFields({
7590
+ const { fields, isLoading, error, supportedOperations, formLayout: inferredFormLayout, workflow } = useResolvedResourceFields({
7545
7591
  apiUrl: resolvedBaseResource.apiUrl,
7546
7592
  manualFields: hasManualFields ? buildFields(resource.fields) : void 0,
7547
7593
  overrides: hasManualFields ? void 0 : fieldOverrides,
@@ -7565,21 +7611,29 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7565
7611
  const routingState = useRouting(resource.routing);
7566
7612
  const { activeOperation, formData, handleFormDataChange, startCreate, startEdit, resetOperation } = useSmartCrudOperation(void 0, routingState);
7567
7613
  const roles = useSmartCrudRoles();
7568
- const { gridFields, processedFields, computedValues } = useSmartCrudFields(fields, activeOperation, formData, useMemo(() => roles ?? [], [roles]));
7614
+ const stableRoles = useMemo(() => roles ?? [], [roles]);
7615
+ const { gridFields, processedFields, computedValues } = useSmartCrudFields(fields, activeOperation, formData, stableRoles);
7569
7616
  const formFields = useMemo(() => applyFormDetailFormFieldOverrides(processedFields, resolvedBaseResource.formDetail), [processedFields, resolvedBaseResource.formDetail]);
7570
7617
  useMercureSubscription(resource.apiUrl, () => {
7571
7618
  effectiveGridRef.current?.refresh();
7572
7619
  }, resolvedBaseResource.mercure !== false);
7573
7620
  const normalizedApiUrl = resolvedBaseResource.apiUrl.startsWith("/") ? resolvedBaseResource.apiUrl : `/${resolvedBaseResource.apiUrl}`;
7574
- const resolvedResource = useMemo(() => ({
7575
- ...resolvedBaseResource,
7576
- ...!hasManualFields ? { fields: gridFields } : {},
7577
- apiUrl: normalizedApiUrl,
7578
- fields: hasManualFields ? buildFields(resource.fields) : gridFields,
7579
- formFields,
7580
- formLayout: resolvedBaseResource.formLayout ?? inferredFormLayout,
7581
- _supportedOperations: supportedOperations
7582
- }), [
7621
+ const resolvedResource = useMemo(() => {
7622
+ const rowActions = resolvedBaseResource.rowActions ?? (workflow ? (row) => buildWorkflowRowActions(row, workflow, normalizedApiUrl, stableRoles, () => {
7623
+ effectiveGridRef.current?.refresh();
7624
+ }) : void 0);
7625
+ return {
7626
+ ...resolvedBaseResource,
7627
+ ...!hasManualFields ? { fields: gridFields } : {},
7628
+ apiUrl: normalizedApiUrl,
7629
+ fields: hasManualFields ? buildFields(resource.fields) : gridFields,
7630
+ formFields,
7631
+ formLayout: resolvedBaseResource.formLayout ?? inferredFormLayout,
7632
+ _supportedOperations: supportedOperations,
7633
+ rowActions
7634
+ };
7635
+ }, [
7636
+ effectiveGridRef,
7583
7637
  fields,
7584
7638
  gridFields,
7585
7639
  hasManualFields,
@@ -7588,7 +7642,9 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7588
7642
  formFields,
7589
7643
  resolvedBaseResource,
7590
7644
  resource.fields,
7591
- supportedOperations
7645
+ stableRoles,
7646
+ supportedOperations,
7647
+ workflow
7592
7648
  ]);
7593
7649
  if (!hasManualFields && isLoading) return /* @__PURE__ */ jsx(CrudSkeleton, {});
7594
7650
  if (!hasManualFields && error) return /* @__PURE__ */ jsx(CrudError, {
@@ -8565,4 +8621,4 @@ function createRestResourceStore(dialect = {}) {
8565
8621
  };
8566
8622
  }
8567
8623
  //#endregion
8568
- export { AuditTrailPanel, ColumnPresetSelector, CrudDialogShell, CrudDrawerShell, CrudFormShell, CrudPage, CrudPageShell, DATA_GRID_EVENTS, DEFAULT_DRAWER_SIZE, DEFAULT_DRAWER_WIDTH, DRAWER_WIDTHS, NativeDataGridView as DataGridView, CrudDialogView as DialogView, CrudDrawerView as DrawerView, FORM_EVENTS, FieldBuilder, FieldType, NativeFormView as FormView, HydraAdapter, CrudPageView as PageView, ResourceSchemaProvider, ResourceStoreProvider, RestAdapter, SmartCrudPage, SmartCrudRolesProvider, ToolbarSelect, buildFieldColSpanContext, buildFields, checkboxField, computeSummaryValue, createCrudEvents, createRestResourceStore, crudRoute, currencyField, dateField, datetimeField, defineFieldContract, defineFields, defineResource, 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 };
8624
+ export { AuditTrailPanel, ColumnPresetSelector, CrudDialogShell, CrudDrawerShell, CrudFormShell, CrudPage, CrudPageShell, DATA_GRID_EVENTS, DEFAULT_DRAWER_SIZE, DEFAULT_DRAWER_WIDTH, DRAWER_WIDTHS, NativeDataGridView as DataGridView, CrudDialogView as DialogView, CrudDrawerView as DrawerView, FORM_EVENTS, FieldBuilder, FieldType, NativeFormView as FormView, HydraAdapter, CrudPageView as PageView, ResourceSchemaProvider, ResourceStoreProvider, RestAdapter, SmartCrudPage, SmartCrudRolesProvider, ToolbarSelect, 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/style.css CHANGED
@@ -617,7 +617,7 @@
617
617
  font-size: var(--font-size-xs);
618
618
  }
619
619
 
620
- .nb-datagrid__filter-operator {
620
+ .nb-datagrid__filter-wrap > .nb-datagrid__filter-operator {
621
621
  left: 5px;
622
622
  position: absolute;
623
623
  top: 50%;
@@ -625,7 +625,7 @@
625
625
  width: 30px;
626
626
  z-index: 2;
627
627
  }
628
- .nb-datagrid__filter-operator.nb-dropdown .nb-dropdown__trigger {
628
+ .nb-datagrid__filter-wrap > .nb-datagrid__filter-operator.nb-dropdown .nb-dropdown__trigger {
629
629
  appearance: none;
630
630
  background: color-mix(in srgb, var(--accent-color) 10%, transparent);
631
631
  border: 1px solid color-mix(in srgb, var(--accent-color) 20%, transparent);
@@ -641,7 +641,7 @@
641
641
  text-align: center;
642
642
  width: 30px;
643
643
  }
644
- .nb-datagrid__filter-operator.nb-dropdown .nb-dropdown__caret {
644
+ .nb-datagrid__filter-wrap > .nb-datagrid__filter-operator.nb-dropdown .nb-dropdown__caret {
645
645
  display: none;
646
646
  }
647
647
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nubitio/crud",
3
- "version": "0.5.16",
3
+ "version": "0.5.20",
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.16",
60
- "@nubitio/ui": "^0.5.16"
59
+ "@nubitio/core": "^0.5.20",
60
+ "@nubitio/ui": "^0.5.20"
61
61
  }
62
62
  }