@nubitio/crud 0.5.19 → 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
@@ -7466,14 +7466,15 @@ function ResourceSchemaProvider({ children, resolver }) {
7466
7466
  children
7467
7467
  });
7468
7468
  }
7469
- function resolveWithRuntimeErrors(resolver, supportedOperations = [], formLayout) {
7469
+ function resolveWithRuntimeErrors(resolver, supportedOperations = [], formLayout, workflow) {
7470
7470
  try {
7471
7471
  return {
7472
7472
  fields: resolver(),
7473
7473
  isLoading: false,
7474
7474
  error: void 0,
7475
7475
  supportedOperations,
7476
- formLayout
7476
+ formLayout,
7477
+ workflow
7477
7478
  };
7478
7479
  } catch (runtimeError) {
7479
7480
  return {
@@ -7481,7 +7482,8 @@ function resolveWithRuntimeErrors(resolver, supportedOperations = [], formLayout
7481
7482
  isLoading: false,
7482
7483
  error: runtimeError instanceof Error ? runtimeError : new Error(String(runtimeError)),
7483
7484
  supportedOperations,
7484
- formLayout
7485
+ formLayout,
7486
+ workflow
7485
7487
  };
7486
7488
  }
7487
7489
  }
@@ -7507,7 +7509,7 @@ function useResolvedResourceFields({ apiUrl, manualFields, overrides, fieldContr
7507
7509
  baselineFields: baseline.fields,
7508
7510
  contract: fieldContract,
7509
7511
  legacyOverrides: fieldContract ? void 0 : overrides
7510
- }), baseline.supportedOperations, baseline.formLayout);
7512
+ }), baseline.supportedOperations, baseline.formLayout, baseline.workflow);
7511
7513
  }, [
7512
7514
  baseline,
7513
7515
  fieldContract,
@@ -7515,6 +7517,28 @@ function useResolvedResourceFields({ apiUrl, manualFields, overrides, fieldContr
7515
7517
  ]);
7516
7518
  }
7517
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
7518
7542
  //#region packages/crud/crud/SmartCrudPage.tsx
7519
7543
  function CrudSkeleton() {
7520
7544
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -7587,7 +7611,7 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7587
7611
  const effectiveGridRef = gridRef ?? internalGridRef;
7588
7612
  const resolvedBaseResource = (0, react.useMemo)(() => resolveCrudResource(resource), [resource]);
7589
7613
  const hasManualFields = !resource.fieldContract && Array.isArray(resource.fields) && resource.fields.length > 0;
7590
- const { fields, isLoading, error, supportedOperations, formLayout: inferredFormLayout } = useResolvedResourceFields({
7614
+ const { fields, isLoading, error, supportedOperations, formLayout: inferredFormLayout, workflow } = useResolvedResourceFields({
7591
7615
  apiUrl: resolvedBaseResource.apiUrl,
7592
7616
  manualFields: hasManualFields ? buildFields(resource.fields) : void 0,
7593
7617
  overrides: hasManualFields ? void 0 : fieldOverrides,
@@ -7611,21 +7635,29 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7611
7635
  const routingState = useRouting(resource.routing);
7612
7636
  const { activeOperation, formData, handleFormDataChange, startCreate, startEdit, resetOperation } = useSmartCrudOperation(void 0, routingState);
7613
7637
  const roles = useSmartCrudRoles();
7614
- 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);
7615
7640
  const formFields = (0, react.useMemo)(() => applyFormDetailFormFieldOverrides(processedFields, resolvedBaseResource.formDetail), [processedFields, resolvedBaseResource.formDetail]);
7616
7641
  (0, _nubitio_core.useMercureSubscription)(resource.apiUrl, () => {
7617
7642
  effectiveGridRef.current?.refresh();
7618
7643
  }, resolvedBaseResource.mercure !== false);
7619
7644
  const normalizedApiUrl = resolvedBaseResource.apiUrl.startsWith("/") ? resolvedBaseResource.apiUrl : `/${resolvedBaseResource.apiUrl}`;
7620
- const resolvedResource = (0, react.useMemo)(() => ({
7621
- ...resolvedBaseResource,
7622
- ...!hasManualFields ? { fields: gridFields } : {},
7623
- apiUrl: normalizedApiUrl,
7624
- fields: hasManualFields ? buildFields(resource.fields) : gridFields,
7625
- formFields,
7626
- formLayout: resolvedBaseResource.formLayout ?? inferredFormLayout,
7627
- _supportedOperations: supportedOperations
7628
- }), [
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,
7629
7661
  fields,
7630
7662
  gridFields,
7631
7663
  hasManualFields,
@@ -7634,7 +7666,9 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7634
7666
  formFields,
7635
7667
  resolvedBaseResource,
7636
7668
  resource.fields,
7637
- supportedOperations
7669
+ stableRoles,
7670
+ supportedOperations,
7671
+ workflow
7638
7672
  ]);
7639
7673
  if (!hasManualFields && isLoading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CrudSkeleton, {});
7640
7674
  if (!hasManualFields && error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CrudError, {
@@ -8460,24 +8494,6 @@ function ToolbarSelect({ id, label, icon = "ph-funnel", value, options, onChange
8460
8494
  });
8461
8495
  }
8462
8496
  //#endregion
8463
- //#region packages/crud/workflow/buildWorkflowRowActions.ts
8464
- function buildWorkflowRowActions(row, workflow, apiUrl, roles, onDone) {
8465
- if (!workflow) return [];
8466
- const current = String(row[workflow.field] ?? "");
8467
- return workflow.transitions.filter((transition) => transition.from.includes(current)).filter((transition) => !transition.roles?.length || transition.roles.some((role) => roles.includes(role))).map((transition) => ({
8468
- text: transition.label ?? transition.name,
8469
- onClick: async () => {
8470
- const base = apiUrl.replace(/\/$/, "");
8471
- const id = row.id;
8472
- await fetch(`${base}/${id}/transition/${transition.name}`, {
8473
- method: "POST",
8474
- credentials: "include"
8475
- });
8476
- onDone?.();
8477
- }
8478
- }));
8479
- }
8480
- //#endregion
8481
8497
  //#region packages/crud/adapter/RestAdapter.ts
8482
8498
  /**
8483
8499
  * Backend adapter for plain OpenAPI / REST backends.
package/dist/index.mjs CHANGED
@@ -7442,14 +7442,15 @@ function ResourceSchemaProvider({ children, resolver }) {
7442
7442
  children
7443
7443
  });
7444
7444
  }
7445
- function resolveWithRuntimeErrors(resolver, supportedOperations = [], formLayout) {
7445
+ function resolveWithRuntimeErrors(resolver, supportedOperations = [], formLayout, workflow) {
7446
7446
  try {
7447
7447
  return {
7448
7448
  fields: resolver(),
7449
7449
  isLoading: false,
7450
7450
  error: void 0,
7451
7451
  supportedOperations,
7452
- formLayout
7452
+ formLayout,
7453
+ workflow
7453
7454
  };
7454
7455
  } catch (runtimeError) {
7455
7456
  return {
@@ -7457,7 +7458,8 @@ function resolveWithRuntimeErrors(resolver, supportedOperations = [], formLayout
7457
7458
  isLoading: false,
7458
7459
  error: runtimeError instanceof Error ? runtimeError : new Error(String(runtimeError)),
7459
7460
  supportedOperations,
7460
- formLayout
7461
+ formLayout,
7462
+ workflow
7461
7463
  };
7462
7464
  }
7463
7465
  }
@@ -7483,7 +7485,7 @@ function useResolvedResourceFields({ apiUrl, manualFields, overrides, fieldContr
7483
7485
  baselineFields: baseline.fields,
7484
7486
  contract: fieldContract,
7485
7487
  legacyOverrides: fieldContract ? void 0 : overrides
7486
- }), baseline.supportedOperations, baseline.formLayout);
7488
+ }), baseline.supportedOperations, baseline.formLayout, baseline.workflow);
7487
7489
  }, [
7488
7490
  baseline,
7489
7491
  fieldContract,
@@ -7491,6 +7493,28 @@ function useResolvedResourceFields({ apiUrl, manualFields, overrides, fieldContr
7491
7493
  ]);
7492
7494
  }
7493
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
7494
7518
  //#region packages/crud/crud/SmartCrudPage.tsx
7495
7519
  function CrudSkeleton() {
7496
7520
  return /* @__PURE__ */ jsxs("div", {
@@ -7563,7 +7587,7 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7563
7587
  const effectiveGridRef = gridRef ?? internalGridRef;
7564
7588
  const resolvedBaseResource = useMemo(() => resolveCrudResource(resource), [resource]);
7565
7589
  const hasManualFields = !resource.fieldContract && Array.isArray(resource.fields) && resource.fields.length > 0;
7566
- const { fields, isLoading, error, supportedOperations, formLayout: inferredFormLayout } = useResolvedResourceFields({
7590
+ const { fields, isLoading, error, supportedOperations, formLayout: inferredFormLayout, workflow } = useResolvedResourceFields({
7567
7591
  apiUrl: resolvedBaseResource.apiUrl,
7568
7592
  manualFields: hasManualFields ? buildFields(resource.fields) : void 0,
7569
7593
  overrides: hasManualFields ? void 0 : fieldOverrides,
@@ -7587,21 +7611,29 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7587
7611
  const routingState = useRouting(resource.routing);
7588
7612
  const { activeOperation, formData, handleFormDataChange, startCreate, startEdit, resetOperation } = useSmartCrudOperation(void 0, routingState);
7589
7613
  const roles = useSmartCrudRoles();
7590
- 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);
7591
7616
  const formFields = useMemo(() => applyFormDetailFormFieldOverrides(processedFields, resolvedBaseResource.formDetail), [processedFields, resolvedBaseResource.formDetail]);
7592
7617
  useMercureSubscription(resource.apiUrl, () => {
7593
7618
  effectiveGridRef.current?.refresh();
7594
7619
  }, resolvedBaseResource.mercure !== false);
7595
7620
  const normalizedApiUrl = resolvedBaseResource.apiUrl.startsWith("/") ? resolvedBaseResource.apiUrl : `/${resolvedBaseResource.apiUrl}`;
7596
- const resolvedResource = useMemo(() => ({
7597
- ...resolvedBaseResource,
7598
- ...!hasManualFields ? { fields: gridFields } : {},
7599
- apiUrl: normalizedApiUrl,
7600
- fields: hasManualFields ? buildFields(resource.fields) : gridFields,
7601
- formFields,
7602
- formLayout: resolvedBaseResource.formLayout ?? inferredFormLayout,
7603
- _supportedOperations: supportedOperations
7604
- }), [
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,
7605
7637
  fields,
7606
7638
  gridFields,
7607
7639
  hasManualFields,
@@ -7610,7 +7642,9 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7610
7642
  formFields,
7611
7643
  resolvedBaseResource,
7612
7644
  resource.fields,
7613
- supportedOperations
7645
+ stableRoles,
7646
+ supportedOperations,
7647
+ workflow
7614
7648
  ]);
7615
7649
  if (!hasManualFields && isLoading) return /* @__PURE__ */ jsx(CrudSkeleton, {});
7616
7650
  if (!hasManualFields && error) return /* @__PURE__ */ jsx(CrudError, {
@@ -8436,24 +8470,6 @@ function ToolbarSelect({ id, label, icon = "ph-funnel", value, options, onChange
8436
8470
  });
8437
8471
  }
8438
8472
  //#endregion
8439
- //#region packages/crud/workflow/buildWorkflowRowActions.ts
8440
- function buildWorkflowRowActions(row, workflow, apiUrl, roles, onDone) {
8441
- if (!workflow) return [];
8442
- const current = String(row[workflow.field] ?? "");
8443
- return workflow.transitions.filter((transition) => transition.from.includes(current)).filter((transition) => !transition.roles?.length || transition.roles.some((role) => roles.includes(role))).map((transition) => ({
8444
- text: transition.label ?? transition.name,
8445
- onClick: async () => {
8446
- const base = apiUrl.replace(/\/$/, "");
8447
- const id = row.id;
8448
- await fetch(`${base}/${id}/transition/${transition.name}`, {
8449
- method: "POST",
8450
- credentials: "include"
8451
- });
8452
- onDone?.();
8453
- }
8454
- }));
8455
- }
8456
- //#endregion
8457
8473
  //#region packages/crud/adapter/RestAdapter.ts
8458
8474
  /**
8459
8475
  * Backend adapter for plain OpenAPI / REST backends.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nubitio/crud",
3
- "version": "0.5.19",
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.19",
60
- "@nubitio/ui": "^0.5.19"
59
+ "@nubitio/core": "^0.5.20",
60
+ "@nubitio/ui": "^0.5.20"
61
61
  }
62
62
  }