@nubitio/crud 0.4.0 → 0.5.0

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
@@ -597,10 +597,12 @@ function serializeDateField(formData, field) {
597
597
  }
598
598
  function serializeNumericField(formData, field) {
599
599
  if (field.isIdentity) return;
600
- if (field.sendAsString) {
601
- const val = formData[field.name];
602
- formData[field.name] = val != null ? Number(val).toFixed(field.precision || 2) : null;
603
- } else formData[field.name] = Number(formData[field.name]);
600
+ const val = formData[field.name];
601
+ if (val === null || val === void 0 || val === "") {
602
+ delete formData[field.name];
603
+ return;
604
+ }
605
+ formData[field.name] = field.sendAsString ? Number(val).toFixed(field.precision || 2) : Number(val);
604
606
  }
605
607
  /**
606
608
  * Serializes detail grid rows (entity refs, numeric coercion, identity cleanup).
@@ -615,10 +617,12 @@ function serializeDetailRows(rows, detailFields, detailIdField, isEditMode, adap
615
617
  else detail[field.name] = serialized;
616
618
  });
617
619
  if (field.type === "number" || field.type === "currency") details.forEach((detail) => {
618
- if (field.sendAsString) {
619
- const val = detail[field.name];
620
- detail[field.name] = val != null ? Number(val).toFixed(field.precision || 2) : null;
621
- } else detail[field.name] = Number(detail[field.name]);
620
+ const val = detail[field.name];
621
+ if (val === null || val === void 0 || val === "") {
622
+ delete detail[field.name];
623
+ return;
624
+ }
625
+ detail[field.name] = field.sendAsString ? Number(val).toFixed(field.precision || 2) : Number(val);
622
626
  });
623
627
  });
624
628
  details.forEach((detail) => {
@@ -6908,20 +6912,22 @@ function ResourceSchemaProvider({ children, resolver }) {
6908
6912
  children
6909
6913
  });
6910
6914
  }
6911
- function resolveWithRuntimeErrors(resolver, supportedOperations = []) {
6915
+ function resolveWithRuntimeErrors(resolver, supportedOperations = [], formLayout) {
6912
6916
  try {
6913
6917
  return {
6914
6918
  fields: resolver(),
6915
6919
  isLoading: false,
6916
6920
  error: void 0,
6917
- supportedOperations
6921
+ supportedOperations,
6922
+ formLayout
6918
6923
  };
6919
6924
  } catch (runtimeError) {
6920
6925
  return {
6921
6926
  fields: [],
6922
6927
  isLoading: false,
6923
6928
  error: runtimeError instanceof Error ? runtimeError : new Error(String(runtimeError)),
6924
- supportedOperations
6929
+ supportedOperations,
6930
+ formLayout
6925
6931
  };
6926
6932
  }
6927
6933
  }
@@ -6947,7 +6953,7 @@ function useResolvedResourceFields({ apiUrl, manualFields, overrides, fieldContr
6947
6953
  baselineFields: baseline.fields,
6948
6954
  contract: fieldContract,
6949
6955
  legacyOverrides: fieldContract ? void 0 : overrides
6950
- }), baseline.supportedOperations);
6956
+ }), baseline.supportedOperations, baseline.formLayout);
6951
6957
  }, [
6952
6958
  baseline,
6953
6959
  fieldContract,
@@ -7027,7 +7033,7 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7027
7033
  const effectiveGridRef = gridRef ?? internalGridRef;
7028
7034
  const resolvedBaseResource = (0, react.useMemo)(() => resolveCrudResource(resource), [resource]);
7029
7035
  const hasManualFields = !resource.fieldContract && Array.isArray(resource.fields) && resource.fields.length > 0;
7030
- const { fields, isLoading, error, supportedOperations } = useResolvedResourceFields({
7036
+ const { fields, isLoading, error, supportedOperations, formLayout: inferredFormLayout } = useResolvedResourceFields({
7031
7037
  apiUrl: resolvedBaseResource.apiUrl,
7032
7038
  manualFields: hasManualFields ? buildFields(resource.fields) : void 0,
7033
7039
  overrides: hasManualFields ? void 0 : fieldOverrides,
@@ -7062,11 +7068,13 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7062
7068
  apiUrl: normalizedApiUrl,
7063
7069
  fields: hasManualFields ? buildFields(resource.fields) : gridFields,
7064
7070
  formFields: processedFields,
7071
+ formLayout: resolvedBaseResource.formLayout ?? inferredFormLayout,
7065
7072
  _supportedOperations: supportedOperations
7066
7073
  }), [
7067
7074
  fields,
7068
7075
  gridFields,
7069
7076
  hasManualFields,
7077
+ inferredFormLayout,
7070
7078
  normalizedApiUrl,
7071
7079
  processedFields,
7072
7080
  resolvedBaseResource,
package/dist/index.d.cts CHANGED
@@ -1871,6 +1871,11 @@ interface ResourceSchemaResolution {
1871
1871
  isLoading: boolean;
1872
1872
  error: Error | undefined;
1873
1873
  supportedOperations: string[];
1874
+ /**
1875
+ * Backend-declared form layout (sections/tabs) from the API doc, when the
1876
+ * resource publishes one. Explicit `ResourceConfig.formLayout` wins.
1877
+ */
1878
+ formLayout?: FormLayout;
1874
1879
  }
1875
1880
  interface ResourceSchemaResolver {
1876
1881
  useResourceSchema(request: ResourceSchemaRequest): ResourceSchemaResolution;
package/dist/index.d.mts CHANGED
@@ -1871,6 +1871,11 @@ interface ResourceSchemaResolution {
1871
1871
  isLoading: boolean;
1872
1872
  error: Error | undefined;
1873
1873
  supportedOperations: string[];
1874
+ /**
1875
+ * Backend-declared form layout (sections/tabs) from the API doc, when the
1876
+ * resource publishes one. Explicit `ResourceConfig.formLayout` wins.
1877
+ */
1878
+ formLayout?: FormLayout;
1874
1879
  }
1875
1880
  interface ResourceSchemaResolver {
1876
1881
  useResourceSchema(request: ResourceSchemaRequest): ResourceSchemaResolution;
package/dist/index.mjs CHANGED
@@ -573,10 +573,12 @@ function serializeDateField(formData, field) {
573
573
  }
574
574
  function serializeNumericField(formData, field) {
575
575
  if (field.isIdentity) return;
576
- if (field.sendAsString) {
577
- const val = formData[field.name];
578
- formData[field.name] = val != null ? Number(val).toFixed(field.precision || 2) : null;
579
- } else formData[field.name] = Number(formData[field.name]);
576
+ const val = formData[field.name];
577
+ if (val === null || val === void 0 || val === "") {
578
+ delete formData[field.name];
579
+ return;
580
+ }
581
+ formData[field.name] = field.sendAsString ? Number(val).toFixed(field.precision || 2) : Number(val);
580
582
  }
581
583
  /**
582
584
  * Serializes detail grid rows (entity refs, numeric coercion, identity cleanup).
@@ -591,10 +593,12 @@ function serializeDetailRows(rows, detailFields, detailIdField, isEditMode, adap
591
593
  else detail[field.name] = serialized;
592
594
  });
593
595
  if (field.type === "number" || field.type === "currency") details.forEach((detail) => {
594
- if (field.sendAsString) {
595
- const val = detail[field.name];
596
- detail[field.name] = val != null ? Number(val).toFixed(field.precision || 2) : null;
597
- } else detail[field.name] = Number(detail[field.name]);
596
+ const val = detail[field.name];
597
+ if (val === null || val === void 0 || val === "") {
598
+ delete detail[field.name];
599
+ return;
600
+ }
601
+ detail[field.name] = field.sendAsString ? Number(val).toFixed(field.precision || 2) : Number(val);
598
602
  });
599
603
  });
600
604
  details.forEach((detail) => {
@@ -6884,20 +6888,22 @@ function ResourceSchemaProvider({ children, resolver }) {
6884
6888
  children
6885
6889
  });
6886
6890
  }
6887
- function resolveWithRuntimeErrors(resolver, supportedOperations = []) {
6891
+ function resolveWithRuntimeErrors(resolver, supportedOperations = [], formLayout) {
6888
6892
  try {
6889
6893
  return {
6890
6894
  fields: resolver(),
6891
6895
  isLoading: false,
6892
6896
  error: void 0,
6893
- supportedOperations
6897
+ supportedOperations,
6898
+ formLayout
6894
6899
  };
6895
6900
  } catch (runtimeError) {
6896
6901
  return {
6897
6902
  fields: [],
6898
6903
  isLoading: false,
6899
6904
  error: runtimeError instanceof Error ? runtimeError : new Error(String(runtimeError)),
6900
- supportedOperations
6905
+ supportedOperations,
6906
+ formLayout
6901
6907
  };
6902
6908
  }
6903
6909
  }
@@ -6923,7 +6929,7 @@ function useResolvedResourceFields({ apiUrl, manualFields, overrides, fieldContr
6923
6929
  baselineFields: baseline.fields,
6924
6930
  contract: fieldContract,
6925
6931
  legacyOverrides: fieldContract ? void 0 : overrides
6926
- }), baseline.supportedOperations);
6932
+ }), baseline.supportedOperations, baseline.formLayout);
6927
6933
  }, [
6928
6934
  baseline,
6929
6935
  fieldContract,
@@ -7003,7 +7009,7 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7003
7009
  const effectiveGridRef = gridRef ?? internalGridRef;
7004
7010
  const resolvedBaseResource = useMemo(() => resolveCrudResource(resource), [resource]);
7005
7011
  const hasManualFields = !resource.fieldContract && Array.isArray(resource.fields) && resource.fields.length > 0;
7006
- const { fields, isLoading, error, supportedOperations } = useResolvedResourceFields({
7012
+ const { fields, isLoading, error, supportedOperations, formLayout: inferredFormLayout } = useResolvedResourceFields({
7007
7013
  apiUrl: resolvedBaseResource.apiUrl,
7008
7014
  manualFields: hasManualFields ? buildFields(resource.fields) : void 0,
7009
7015
  overrides: hasManualFields ? void 0 : fieldOverrides,
@@ -7038,11 +7044,13 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
7038
7044
  apiUrl: normalizedApiUrl,
7039
7045
  fields: hasManualFields ? buildFields(resource.fields) : gridFields,
7040
7046
  formFields: processedFields,
7047
+ formLayout: resolvedBaseResource.formLayout ?? inferredFormLayout,
7041
7048
  _supportedOperations: supportedOperations
7042
7049
  }), [
7043
7050
  fields,
7044
7051
  gridFields,
7045
7052
  hasManualFields,
7053
+ inferredFormLayout,
7046
7054
  normalizedApiUrl,
7047
7055
  processedFields,
7048
7056
  resolvedBaseResource,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nubitio/crud",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
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,8 +56,8 @@
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.4.0",
60
- "@nubitio/ui": "^0.4.0"
59
+ "@nubitio/ui": "^0.5.0",
60
+ "@nubitio/core": "^0.5.0"
61
61
  },
62
62
  "dependencies": {
63
63
  "react-dropzone": "^15.0.0"