@nubitio/crud 0.2.1 → 0.3.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
@@ -1091,12 +1091,16 @@ function formatSummaryValue(value, item) {
1091
1091
  minimumFractionDigits: precision,
1092
1092
  maximumFractionDigits: precision
1093
1093
  } : void 0 };
1094
- if (item.valueFormat === "currency") return new Intl.NumberFormat((0, _nubitio_core.getCoreLocale)(), {
1095
- ...baseOptions,
1096
- style: "currency",
1097
- currency: item.currency ?? "PEN",
1098
- currencyDisplay: item.currencyDisplay ?? "narrowSymbol"
1099
- }).format(value);
1094
+ if (item.valueFormat === "currency") {
1095
+ const currency = item.currency ?? (0, _nubitio_core.getCoreCurrency)();
1096
+ if (currency === void 0) return new Intl.NumberFormat((0, _nubitio_core.getCoreLocale)(), baseOptions).format(value);
1097
+ return new Intl.NumberFormat((0, _nubitio_core.getCoreLocale)(), {
1098
+ ...baseOptions,
1099
+ style: "currency",
1100
+ currency,
1101
+ currencyDisplay: item.currencyDisplay ?? "narrowSymbol"
1102
+ }).format(value);
1103
+ }
1100
1104
  if (item.valueFormat === "percent") return new Intl.NumberFormat((0, _nubitio_core.getCoreLocale)(), {
1101
1105
  ...baseOptions,
1102
1106
  style: "percent"
@@ -1314,6 +1318,7 @@ function useLookupDropdown() {
1314
1318
  };
1315
1319
  }
1316
1320
  function LookupDropdown({ activeOptionId, children, className, containerRef, disabled, id, label, menuRef, menuStyle, normalizedValue, onClear, onKeyDown, onQueryChange, onScroll, open, query, readOnly, required, setDraftQuery, setOpen }) {
1321
+ const { t } = (0, _nubitio_core.useCoreTranslation)();
1317
1322
  const blurTimer = (0, react.useRef)(void 0);
1318
1323
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1319
1324
  ref: containerRef,
@@ -1348,7 +1353,7 @@ function LookupDropdown({ activeOptionId, children, className, containerRef, dis
1348
1353
  !disabled && !readOnly && normalizedValue !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1349
1354
  type: "button",
1350
1355
  className: "nb-form__lookup-clear",
1351
- "aria-label": `Limpiar ${label}`,
1356
+ "aria-label": t("form.lookupClear", { label }),
1352
1357
  onMouseDown: (e) => e.preventDefault(),
1353
1358
  onClick: onClear,
1354
1359
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {
@@ -1359,7 +1364,7 @@ function LookupDropdown({ activeOptionId, children, className, containerRef, dis
1359
1364
  !disabled && !readOnly && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1360
1365
  type: "button",
1361
1366
  className: "nb-form__lookup-toggle",
1362
- "aria-label": `Abrir ${label}`,
1367
+ "aria-label": t("form.lookupOpen", { label }),
1363
1368
  onMouseDown: (e) => e.preventDefault(),
1364
1369
  onClick: () => {
1365
1370
  clearTimeout(blurTimer.current);
@@ -2574,7 +2579,7 @@ const NativeFormView = (0, react.forwardRef)((options, ref) => {
2574
2579
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2575
2580
  className: "nb-form__tabs-nav",
2576
2581
  role: "tablist",
2577
- "aria-label": "Secciones del formulario",
2582
+ "aria-label": t("form.tabsAriaLabel"),
2578
2583
  children: groups.map((group, index) => {
2579
2584
  const tabIcon = resolveTabIcon(group.icon);
2580
2585
  const isActive = index === activeTab;
@@ -2760,8 +2765,18 @@ function getCellText(field, row, entityOptions, yesLabel = "Yes", noLabel = "No"
2760
2765
  if (field.type === "enum" || field.type === "switch") return getEnumDisplayValue(field, value);
2761
2766
  if (field.type === "date") return getDateDisplay(value);
2762
2767
  if (field.type === "datetime") return getDateTimeDisplay(value);
2768
+ if (field.type === "currency") return getCurrencyDisplay(value);
2763
2769
  return getPrimitiveDisplay(value, yesLabel, noLabel);
2764
2770
  }
2771
+ function getCurrencyDisplay(value) {
2772
+ if (value === null || value === void 0 || value === "") return "";
2773
+ const num = Number(value);
2774
+ if (!Number.isFinite(num)) return String(value);
2775
+ return num.toLocaleString((0, _nubitio_core.getCoreLocale)(), {
2776
+ minimumFractionDigits: 2,
2777
+ maximumFractionDigits: 2
2778
+ });
2779
+ }
2765
2780
  function renderCell(field, row, rowIndex, columnIndex, entityOptions, yesLabel = "Yes", noLabel = "No") {
2766
2781
  const value = row[field.name];
2767
2782
  if (field.formatter) return field.formatter({
@@ -2774,6 +2789,7 @@ function renderCell(field, row, rowIndex, columnIndex, entityOptions, yesLabel =
2774
2789
  if (field.type === "enum" || field.type === "switch") return getEnumDisplayValue(field, value);
2775
2790
  if (field.type === "date") return getDateDisplay(value);
2776
2791
  if (field.type === "datetime") return getDateTimeDisplay(value);
2792
+ if (field.type === "currency") return getCurrencyDisplay(value);
2777
2793
  return getPrimitiveDisplay(value, yesLabel, noLabel);
2778
2794
  }
2779
2795
  function normalizeIcon$1(icon) {
@@ -3611,8 +3627,10 @@ const NativeDataGridView = (0, react.forwardRef)((options, ref) => {
3611
3627
  options.onSelectionChanged?.({ selectedRowsData: nextRows });
3612
3628
  emit(DATA_GRID_EVENTS.SELECTION_CHANGED, nextRows);
3613
3629
  };
3630
+ const rowEditable = (row) => options.canEditRow?.(row) !== false;
3631
+ const rowDeletable = (row) => options.canDeleteRow?.(row) !== false;
3614
3632
  const buildRowActions = (row) => [
3615
- ...options.allowEdit && (options.onEdit || options.events?.EDIT) ? [{
3633
+ ...options.allowEdit && rowEditable(row) && (options.onEdit || options.events?.EDIT) ? [{
3616
3634
  text: t("grid.buttonEdit"),
3617
3635
  icon: "ph-pencil-simple",
3618
3636
  disabled: options.editDisabled,
@@ -3627,7 +3645,7 @@ const NativeDataGridView = (0, react.forwardRef)((options, ref) => {
3627
3645
  onClick: () => options.onView(row)
3628
3646
  }] : [],
3629
3647
  ...getResolvedRowActions(row, options.rowActions),
3630
- ...options.allowDelete && (options.onDelete || options.events?.DELETE) ? [{
3648
+ ...options.allowDelete && rowDeletable(row) && (options.onDelete || options.events?.DELETE) ? [{
3631
3649
  text: t("grid.buttonDelete"),
3632
3650
  icon: "ph-trash",
3633
3651
  type: "danger",
@@ -3636,7 +3654,7 @@ const NativeDataGridView = (0, react.forwardRef)((options, ref) => {
3636
3654
  }] : []
3637
3655
  ];
3638
3656
  const openRow = (row) => {
3639
- if (options.allowEdit && (options.onEdit || options.events?.EDIT)) {
3657
+ if (options.allowEdit && rowEditable(row) && (options.onEdit || options.events?.EDIT)) {
3640
3658
  if (options.onEdit) options.onEdit(row);
3641
3659
  else emit(options.events.EDIT, { row });
3642
3660
  return;
@@ -4965,218 +4983,519 @@ function resolveCrudResource(resource, overrides = {}) {
4965
4983
  };
4966
4984
  }
4967
4985
  //#endregion
4968
- //#region packages/crud/crud/useCrudPage.ts
4969
- function useCrudPage(resource, externalFormRef) {
4970
- const resolvedResource = (0, react.useMemo)(() => resolveCrudResource(resource), [resource]);
4971
- const events = resolvedResource.events;
4972
- const _internalFormRef = (0, react.useRef)(null);
4973
- const formRef = externalFormRef ?? _internalFormRef;
4974
- const fields = (0, react.useMemo)(() => resolvedResource.fields ?? [], [resolvedResource.fields]);
4975
- return {
4976
- events,
4977
- resource: resolvedResource,
4978
- fields,
4979
- formFields: (0, react.useMemo)(() => resolvedResource.formFields ?? fields, [fields, resolvedResource.formFields]),
4980
- formRef,
4981
- permissions: usePermissions(resolvedResource, resolvedResource._supportedOperations ?? []),
4982
- selectionState: useSelectionState((0, react.useMemo)(() => {
4983
- return fields.find((f) => f.isIdentity)?.name ?? "id";
4984
- }, [fields])),
4985
- presetState: useColumnPreset(resolvedResource)
4986
- };
4987
- }
4988
- //#endregion
4989
- //#region packages/crud/crud/AuditTrailPanel.tsx
4990
- const ACTION_COLORS = {
4991
- create: "#2e7d32",
4992
- update: "#1565c0",
4993
- delete: "#c62828"
4994
- };
4995
- function DefaultEntryRenderer({ entry }) {
4996
- const { t } = (0, _nubitio_core.useCoreTranslation)();
4997
- const date = new Date(entry.timestamp);
4998
- const formatted = Number.isNaN(date.getTime()) ? entry.timestamp : date.toLocaleString((0, _nubitio_core.getCoreLocale)(), { timeZone: (0, _nubitio_core.getCoreTimezone)() });
4999
- const actionLabels = {
5000
- create: t("auditTrail.action.create"),
5001
- update: t("auditTrail.action.update"),
5002
- delete: t("auditTrail.action.delete")
5003
- };
5004
- const changeKeys = Object.keys(entry.changes);
5005
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
5006
- style: {
5007
- borderBottom: "1px solid #e0e0e0",
5008
- padding: "10px 0",
5009
- listStyle: "none"
5010
- },
5011
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5012
- style: {
5013
- display: "flex",
5014
- alignItems: "center",
5015
- gap: 8,
5016
- marginBottom: 4
5017
- },
5018
- children: [
5019
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5020
- style: {
5021
- fontSize: 12,
5022
- color: "#757575"
5023
- },
5024
- children: formatted
5025
- }),
5026
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5027
- style: {
5028
- fontSize: 12,
5029
- color: "#424242"
5030
- },
5031
- children: entry.user
5032
- }),
5033
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5034
- style: {
5035
- fontSize: 11,
5036
- fontWeight: 600,
5037
- padding: "1px 6px",
5038
- borderRadius: 4,
5039
- backgroundColor: ACTION_COLORS[entry.action],
5040
- color: "#fff"
5041
- },
5042
- children: actionLabels[entry.action]
5043
- })
5044
- ]
5045
- }), changeKeys.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
5046
- style: {
5047
- margin: "4px 0 0 0",
5048
- padding: "0 0 0 16px"
5049
- },
5050
- children: changeKeys.map((field) => {
5051
- const { before, after } = entry.changes[field];
5052
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
5053
- style: {
5054
- fontSize: 12,
5055
- color: "#616161"
5056
- },
5057
- children: [
5058
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", { children: [field, ":"] }),
5059
- " ",
5060
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5061
- style: { color: "#c62828" },
5062
- children: String(before ?? "—")
5063
- }),
5064
- " → ",
5065
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5066
- style: { color: "#2e7d32" },
5067
- children: String(after ?? "—")
5068
- })
5069
- ]
5070
- }, field);
5071
- })
5072
- })]
5073
- });
5074
- }
5075
- function AuditTrailPanel({ url, renderEntry, visible, onClose }) {
5076
- const { t } = (0, _nubitio_core.useCoreTranslation)();
5077
- const httpClient = (0, _nubitio_core.useCoreHttpClient)();
5078
- const [fetchState, setFetchState] = (0, react.useState)({ status: "idle" });
5079
- (0, react.useEffect)(() => {
5080
- if (!visible || url === null) {
5081
- setFetchState({ status: "idle" });
5082
- return;
5083
- }
5084
- let cancelled = false;
5085
- setFetchState({ status: "loading" });
5086
- httpClient.get(url).then((response) => {
5087
- if (!cancelled) setFetchState({
5088
- status: "success",
5089
- entries: response.data
5090
- });
5091
- }).catch(() => {
5092
- if (!cancelled) setFetchState({ status: "error" });
5093
- });
5094
- return () => {
5095
- cancelled = true;
4986
+ //#region packages/crud/field/BaseFieldBuilder.ts
4987
+ var BaseFieldBuilder = class {
4988
+ _field;
4989
+ constructor(type) {
4990
+ this._field = {
4991
+ isIdentity: false,
4992
+ type,
4993
+ col: void 0,
4994
+ name: "",
4995
+ label: "",
4996
+ width: void 0,
4997
+ height: null,
4998
+ minWidth: void 0,
4999
+ align: "left",
5000
+ sortable: true,
5001
+ filterable: true,
5002
+ hideable: true,
5003
+ validators: [],
5004
+ url: void 0,
5005
+ loadOptions: [],
5006
+ filters: [],
5007
+ byKeyUrl: null,
5008
+ textField: "",
5009
+ valueField: "",
5010
+ valueType: "string",
5011
+ filterValue: void 0,
5012
+ selectedFilterOperation: void 0,
5013
+ data: [],
5014
+ format: "",
5015
+ formatter: void 0,
5016
+ itemFormatter: void 0,
5017
+ visible: true,
5018
+ defaultValue: void 0,
5019
+ onChange: void 0,
5020
+ onSelect: void 0,
5021
+ onClick: void 0,
5022
+ readonly: false,
5023
+ disabled: false,
5024
+ hidden: false,
5025
+ required: false,
5026
+ precision: 0,
5027
+ accept: null,
5028
+ buttons: [],
5029
+ searchEnabled: true,
5030
+ searchExpr: null,
5031
+ helpText: void 0,
5032
+ contentRender: null,
5033
+ visibleOnForm: true,
5034
+ maxLength: void 0,
5035
+ multiple: false,
5036
+ autoSelectIfSingle: false,
5037
+ sendAsString: false
5096
5038
  };
5097
- }, [
5098
- httpClient,
5099
- url,
5100
- visible
5101
- ]);
5102
- if (!visible) return null;
5103
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
5104
- className: "nb-audit-trail-panel",
5105
- style: {
5106
- border: "1px solid #e0e0e0",
5107
- borderRadius: 4,
5108
- padding: "12px 16px",
5109
- backgroundColor: "#fafafa",
5110
- minWidth: 280
5111
- },
5112
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5113
- style: {
5114
- display: "flex",
5115
- alignItems: "center",
5116
- justifyContent: "space-between",
5117
- marginBottom: 12
5118
- },
5119
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
5120
- style: { fontSize: 14 },
5121
- children: t("auditTrail.title")
5122
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5123
- type: "button",
5124
- onClick: onClose,
5125
- "aria-label": t("auditTrail.closeButton"),
5126
- style: {
5127
- background: "none",
5128
- border: "none",
5129
- cursor: "pointer",
5130
- fontSize: 18,
5131
- lineHeight: 1,
5132
- padding: "0 4px",
5133
- color: "#616161"
5134
- },
5135
- children: "×"
5136
- })]
5137
- }), url === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5138
- style: {
5139
- fontSize: 13,
5140
- color: "#757575",
5141
- margin: 0
5142
- },
5143
- children: t("auditTrail.selectRecord")
5144
- }) : fetchState.status === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5145
- style: {
5146
- fontSize: 13,
5147
- color: "#757575",
5148
- margin: 0
5149
- },
5150
- children: t("auditTrail.loading")
5151
- }) : fetchState.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5152
- style: {
5153
- fontSize: 13,
5154
- color: "#c62828",
5155
- margin: 0
5156
- },
5157
- children: t("auditTrail.error")
5158
- }) : fetchState.status === "success" ? fetchState.entries.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5159
- style: {
5160
- fontSize: 13,
5161
- color: "#757575",
5162
- margin: 0
5163
- },
5164
- children: t("auditTrail.empty")
5165
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
5166
- style: {
5167
- margin: 0,
5168
- padding: 0
5169
- },
5170
- children: fetchState.entries.map((entry) => renderEntry ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.default.Fragment, { children: renderEntry(entry) }, String(entry.id)) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultEntryRenderer, { entry }, String(entry.id)))
5171
- }) : null]
5172
- });
5039
+ }
5040
+ isIdentity(isIdentity) {
5041
+ this._field.isIdentity = isIdentity;
5042
+ return this;
5043
+ }
5044
+ col(col) {
5045
+ this._field.col = col;
5046
+ return this;
5047
+ }
5048
+ layoutHint(value) {
5049
+ this._field.layoutHint = value;
5050
+ return this;
5051
+ }
5052
+ cardRole(value) {
5053
+ this._field.cardRole = value;
5054
+ return this;
5055
+ }
5056
+ preferredColSpan(value) {
5057
+ this._field.preferredColSpan = value;
5058
+ return this;
5059
+ }
5060
+ minColSpan(value) {
5061
+ this._field.minColSpan = value;
5062
+ return this;
5063
+ }
5064
+ forceFullWidth(value = true) {
5065
+ this._field.forceFullWidth = value;
5066
+ return this;
5067
+ }
5068
+ name(name) {
5069
+ this._field.name = name;
5070
+ return this;
5071
+ }
5072
+ label(label) {
5073
+ this._field.label = label;
5074
+ return this;
5075
+ }
5076
+ width(width) {
5077
+ this._field.width = width;
5078
+ return this;
5079
+ }
5080
+ height(height) {
5081
+ this._field.height = height;
5082
+ return this;
5083
+ }
5084
+ minWidth(minWidth) {
5085
+ this._field.minWidth = minWidth;
5086
+ return this;
5087
+ }
5088
+ align(align) {
5089
+ this._field.align = align;
5090
+ return this;
5091
+ }
5092
+ sortable(sortable) {
5093
+ this._field.sortable = sortable;
5094
+ return this;
5095
+ }
5096
+ filterable(filterable) {
5097
+ this._field.filterable = filterable;
5098
+ return this;
5099
+ }
5100
+ hideable(hideable) {
5101
+ this._field.hideable = hideable;
5102
+ return this;
5103
+ }
5104
+ validators(validators) {
5105
+ this._field.validators = validators;
5106
+ return this;
5107
+ }
5108
+ valueType(valueType) {
5109
+ this._field.valueType = valueType;
5110
+ return this;
5111
+ }
5112
+ filterValue(filterValue) {
5113
+ this._field.filterValue = filterValue;
5114
+ return this;
5115
+ }
5116
+ data(data) {
5117
+ this._field.data = data;
5118
+ return this;
5119
+ }
5120
+ format(format) {
5121
+ this._field.format = format;
5122
+ return this;
5123
+ }
5124
+ formatter(formatter) {
5125
+ this._field.formatter = formatter;
5126
+ return this;
5127
+ }
5128
+ itemFormatter(itemFormatter) {
5129
+ this._field.itemFormatter = itemFormatter;
5130
+ return this;
5131
+ }
5132
+ visible(visible) {
5133
+ this._field.visible = visible;
5134
+ return this;
5135
+ }
5136
+ defaultValue(defaultValue) {
5137
+ this._field.defaultValue = defaultValue;
5138
+ return this;
5139
+ }
5140
+ onChange(onChange) {
5141
+ this._field.onChange = onChange;
5142
+ return this;
5143
+ }
5144
+ onSelect(onSelect) {
5145
+ this._field.onSelect = onSelect;
5146
+ return this;
5147
+ }
5148
+ onClick(onClick) {
5149
+ this._field.onClick = onClick;
5150
+ return this;
5151
+ }
5152
+ readonly(readonly) {
5153
+ this._field.readonly = readonly;
5154
+ return this;
5155
+ }
5156
+ disabled(disabled) {
5157
+ this._field.disabled = disabled;
5158
+ return this;
5159
+ }
5160
+ hidden(hidden) {
5161
+ this._field.hidden = hidden;
5162
+ return this;
5163
+ }
5164
+ required(required) {
5165
+ this._field.required = required;
5166
+ return this;
5167
+ }
5168
+ precision(value) {
5169
+ this._field.precision = value;
5170
+ return this;
5171
+ }
5172
+ accept(value) {
5173
+ this._field.accept = value;
5174
+ return this;
5175
+ }
5176
+ buttons(value) {
5177
+ this._field.buttons = value;
5178
+ return this;
5179
+ }
5180
+ searchEnabled(value) {
5181
+ this._field.searchEnabled = value;
5182
+ return this;
5183
+ }
5184
+ searchExpr(value) {
5185
+ this._field.searchExpr = value;
5186
+ return this;
5187
+ }
5188
+ helpText(value) {
5189
+ this._field.helpText = value;
5190
+ return this;
5191
+ }
5192
+ contentRender(value) {
5193
+ this._field.contentRender = value;
5194
+ return this;
5195
+ }
5196
+ visibleOnForm(value) {
5197
+ this._field.visibleOnForm = value;
5198
+ return this;
5199
+ }
5200
+ autoSelectIfSingle(value) {
5201
+ this._field.autoSelectIfSingle = value;
5202
+ return this;
5203
+ }
5204
+ url(url) {
5205
+ this._field.url = url;
5206
+ return this;
5207
+ }
5208
+ loadOptions(param) {
5209
+ this._field.loadOptions = param;
5210
+ return this;
5211
+ }
5212
+ filters(filters) {
5213
+ this._field.filters = filters;
5214
+ return this;
5215
+ }
5216
+ byKeyUrl(byKeyUrl) {
5217
+ this._field.byKeyUrl = byKeyUrl;
5218
+ return this;
5219
+ }
5220
+ textField(textField) {
5221
+ this._field.textField = textField;
5222
+ return this;
5223
+ }
5224
+ valueField(valueField) {
5225
+ this._field.valueField = valueField;
5226
+ return this;
5227
+ }
5228
+ selectedFilterOperation(operation) {
5229
+ this._field.selectedFilterOperation = operation;
5230
+ return this;
5231
+ }
5232
+ sendAsString(value) {
5233
+ this._field.sendAsString = value;
5234
+ return this;
5235
+ }
5236
+ multiple(value) {
5237
+ this._field.multiple = value;
5238
+ return this;
5239
+ }
5240
+ maxLength(value) {
5241
+ this._field.maxLength = value;
5242
+ return this;
5243
+ }
5244
+ visibleWhen(fn) {
5245
+ this._field.visibleWhen = fn;
5246
+ return this;
5247
+ }
5248
+ disabledWhen(fn) {
5249
+ this._field.disabledWhen = fn;
5250
+ return this;
5251
+ }
5252
+ computed(fn) {
5253
+ this._field.computed = fn;
5254
+ return this;
5255
+ }
5256
+ defaultWhen(fn) {
5257
+ this._field.defaultWhen = fn;
5258
+ return this;
5259
+ }
5260
+ requiredWhen(fn) {
5261
+ this._field.requiredWhen = fn;
5262
+ return this;
5263
+ }
5264
+ clearWhenHidden(value = true) {
5265
+ this._field.clearWhenHidden = value;
5266
+ return this;
5267
+ }
5268
+ dependsOn(fields) {
5269
+ this._field.dependsOn = fields;
5270
+ return this;
5271
+ }
5272
+ permissions(perms) {
5273
+ this._field.permissions = perms;
5274
+ return this;
5275
+ }
5276
+ build() {
5277
+ return { ...this._field };
5278
+ }
5279
+ };
5280
+ //#endregion
5281
+ //#region packages/crud/field/buildFields.ts
5282
+ /** Normalizes a mixed array of Fields and builders into plain Fields. */
5283
+ function buildFields(items) {
5284
+ return items.map((item) => item instanceof BaseFieldBuilder ? item.build() : item);
5173
5285
  }
5174
5286
  //#endregion
5175
- //#region packages/crud/crud/dialogStore.ts
5176
- function initialDialogState() {
5287
+ //#region packages/crud/crud/useCrudPage.ts
5288
+ function useCrudPage(resource, externalFormRef) {
5289
+ const resolvedResource = (0, react.useMemo)(() => resolveCrudResource(resource), [resource]);
5290
+ const events = resolvedResource.events;
5291
+ const _internalFormRef = (0, react.useRef)(null);
5292
+ const formRef = externalFormRef ?? _internalFormRef;
5293
+ const fields = (0, react.useMemo)(() => buildFields(resolvedResource.fields ?? []), [resolvedResource.fields]);
5177
5294
  return {
5178
- isOpen: false,
5179
- mode: "add",
5295
+ events,
5296
+ resource: resolvedResource,
5297
+ fields,
5298
+ formFields: (0, react.useMemo)(() => resolvedResource.formFields ? buildFields(resolvedResource.formFields) : fields, [fields, resolvedResource.formFields]),
5299
+ formRef,
5300
+ permissions: usePermissions(resolvedResource, resolvedResource._supportedOperations ?? []),
5301
+ selectionState: useSelectionState((0, react.useMemo)(() => {
5302
+ return fields.find((f) => f.isIdentity)?.name ?? "id";
5303
+ }, [fields])),
5304
+ presetState: useColumnPreset(resolvedResource)
5305
+ };
5306
+ }
5307
+ //#endregion
5308
+ //#region packages/crud/crud/AuditTrailPanel.tsx
5309
+ const ACTION_COLORS = {
5310
+ create: "#2e7d32",
5311
+ update: "#1565c0",
5312
+ delete: "#c62828"
5313
+ };
5314
+ function DefaultEntryRenderer({ entry }) {
5315
+ const { t } = (0, _nubitio_core.useCoreTranslation)();
5316
+ const date = new Date(entry.timestamp);
5317
+ const formatted = Number.isNaN(date.getTime()) ? entry.timestamp : date.toLocaleString((0, _nubitio_core.getCoreLocale)(), { timeZone: (0, _nubitio_core.getCoreTimezone)() });
5318
+ const actionLabels = {
5319
+ create: t("auditTrail.action.create"),
5320
+ update: t("auditTrail.action.update"),
5321
+ delete: t("auditTrail.action.delete")
5322
+ };
5323
+ const changeKeys = Object.keys(entry.changes);
5324
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
5325
+ style: {
5326
+ borderBottom: "1px solid #e0e0e0",
5327
+ padding: "10px 0",
5328
+ listStyle: "none"
5329
+ },
5330
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5331
+ style: {
5332
+ display: "flex",
5333
+ alignItems: "center",
5334
+ gap: 8,
5335
+ marginBottom: 4
5336
+ },
5337
+ children: [
5338
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5339
+ style: {
5340
+ fontSize: 12,
5341
+ color: "#757575"
5342
+ },
5343
+ children: formatted
5344
+ }),
5345
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5346
+ style: {
5347
+ fontSize: 12,
5348
+ color: "#424242"
5349
+ },
5350
+ children: entry.user
5351
+ }),
5352
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5353
+ style: {
5354
+ fontSize: 11,
5355
+ fontWeight: 600,
5356
+ padding: "1px 6px",
5357
+ borderRadius: 4,
5358
+ backgroundColor: ACTION_COLORS[entry.action],
5359
+ color: "#fff"
5360
+ },
5361
+ children: actionLabels[entry.action]
5362
+ })
5363
+ ]
5364
+ }), changeKeys.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
5365
+ style: {
5366
+ margin: "4px 0 0 0",
5367
+ padding: "0 0 0 16px"
5368
+ },
5369
+ children: changeKeys.map((field) => {
5370
+ const { before, after } = entry.changes[field];
5371
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
5372
+ style: {
5373
+ fontSize: 12,
5374
+ color: "#616161"
5375
+ },
5376
+ children: [
5377
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", { children: [field, ":"] }),
5378
+ " ",
5379
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5380
+ style: { color: "#c62828" },
5381
+ children: String(before ?? "—")
5382
+ }),
5383
+ " → ",
5384
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5385
+ style: { color: "#2e7d32" },
5386
+ children: String(after ?? "—")
5387
+ })
5388
+ ]
5389
+ }, field);
5390
+ })
5391
+ })]
5392
+ });
5393
+ }
5394
+ function AuditTrailPanel({ url, renderEntry, visible, onClose }) {
5395
+ const { t } = (0, _nubitio_core.useCoreTranslation)();
5396
+ const httpClient = (0, _nubitio_core.useCoreHttpClient)();
5397
+ const [fetchState, setFetchState] = (0, react.useState)({ status: "idle" });
5398
+ (0, react.useEffect)(() => {
5399
+ if (!visible || url === null) {
5400
+ setFetchState({ status: "idle" });
5401
+ return;
5402
+ }
5403
+ let cancelled = false;
5404
+ setFetchState({ status: "loading" });
5405
+ httpClient.get(url).then((response) => {
5406
+ if (!cancelled) setFetchState({
5407
+ status: "success",
5408
+ entries: response.data
5409
+ });
5410
+ }).catch(() => {
5411
+ if (!cancelled) setFetchState({ status: "error" });
5412
+ });
5413
+ return () => {
5414
+ cancelled = true;
5415
+ };
5416
+ }, [
5417
+ httpClient,
5418
+ url,
5419
+ visible
5420
+ ]);
5421
+ if (!visible) return null;
5422
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
5423
+ className: "nb-audit-trail-panel",
5424
+ style: {
5425
+ border: "1px solid #e0e0e0",
5426
+ borderRadius: 4,
5427
+ padding: "12px 16px",
5428
+ backgroundColor: "#fafafa",
5429
+ minWidth: 280
5430
+ },
5431
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5432
+ style: {
5433
+ display: "flex",
5434
+ alignItems: "center",
5435
+ justifyContent: "space-between",
5436
+ marginBottom: 12
5437
+ },
5438
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
5439
+ style: { fontSize: 14 },
5440
+ children: t("auditTrail.title")
5441
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5442
+ type: "button",
5443
+ onClick: onClose,
5444
+ "aria-label": t("auditTrail.closeButton"),
5445
+ style: {
5446
+ background: "none",
5447
+ border: "none",
5448
+ cursor: "pointer",
5449
+ fontSize: 18,
5450
+ lineHeight: 1,
5451
+ padding: "0 4px",
5452
+ color: "#616161"
5453
+ },
5454
+ children: "×"
5455
+ })]
5456
+ }), url === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5457
+ style: {
5458
+ fontSize: 13,
5459
+ color: "#757575",
5460
+ margin: 0
5461
+ },
5462
+ children: t("auditTrail.selectRecord")
5463
+ }) : fetchState.status === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5464
+ style: {
5465
+ fontSize: 13,
5466
+ color: "#757575",
5467
+ margin: 0
5468
+ },
5469
+ children: t("auditTrail.loading")
5470
+ }) : fetchState.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5471
+ style: {
5472
+ fontSize: 13,
5473
+ color: "#c62828",
5474
+ margin: 0
5475
+ },
5476
+ children: t("auditTrail.error")
5477
+ }) : fetchState.status === "success" ? fetchState.entries.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5478
+ style: {
5479
+ fontSize: 13,
5480
+ color: "#757575",
5481
+ margin: 0
5482
+ },
5483
+ children: t("auditTrail.empty")
5484
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
5485
+ style: {
5486
+ margin: 0,
5487
+ padding: 0
5488
+ },
5489
+ children: fetchState.entries.map((entry) => renderEntry ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.default.Fragment, { children: renderEntry(entry) }, String(entry.id)) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultEntryRenderer, { entry }, String(entry.id)))
5490
+ }) : null]
5491
+ });
5492
+ }
5493
+ //#endregion
5494
+ //#region packages/crud/crud/dialogStore.ts
5495
+ function initialDialogState() {
5496
+ return {
5497
+ isOpen: false,
5498
+ mode: "add",
5180
5499
  rowData: null
5181
5500
  };
5182
5501
  }
@@ -5247,9 +5566,16 @@ function useCrudDialogStore(resourceId) {
5247
5566
  //#endregion
5248
5567
  //#region packages/crud/crud/resolveResourceDetails.ts
5249
5568
  function resolveResourceDetails(resource) {
5569
+ const { gridDetail, formDetail } = resource;
5250
5570
  return {
5251
- gridDetail: resource.gridDetail,
5252
- formDetail: resource.formDetail
5571
+ gridDetail: gridDetail && {
5572
+ ...gridDetail,
5573
+ fields: typeof gridDetail.fields === "function" ? (parentRow) => buildFields(gridDetail.fields(parentRow)) : buildFields(gridDetail.fields)
5574
+ },
5575
+ formDetail: formDetail && {
5576
+ ...formDetail,
5577
+ fields: buildFields(formDetail.fields)
5578
+ }
5253
5579
  };
5254
5580
  }
5255
5581
  //#endregion
@@ -5341,6 +5667,7 @@ const CrudPageInner = ({ resource, onFormDataChange, initialRecordId = null, ini
5341
5667
  const { t } = (0, _nubitio_core.useCoreTranslation)();
5342
5668
  const { events, resource: resolvedResource, fields, formFields, formRef, permissions, selectionState, presetState } = useCrudPage((0, react.useMemo)(() => resolveCrudResource(resource), [resource]), externalFormRef);
5343
5669
  const datagridFields = (0, react.useMemo)(() => fields.filter((field) => field.isIdentity || field.visible !== false), [fields]);
5670
+ const rawPermissions = typeof resolvedResource.permissions === "function" ? void 0 : resolvedResource.permissions;
5344
5671
  const routeAwareGridFields = (0, react.useMemo)(() => datagridFields.map((field) => {
5345
5672
  const routeFilterValue = initialFilters[field.name];
5346
5673
  if (routeFilterValue === void 0) {
@@ -5600,6 +5927,9 @@ const CrudPageInner = ({ resource, onFormDataChange, initialRecordId = null, ini
5600
5927
  allowExport: permissions.canExport,
5601
5928
  editDisabled,
5602
5929
  deleteDisabled,
5930
+ canEditRow: rawPermissions?.canEditRow,
5931
+ canDeleteRow: rawPermissions?.canDeleteRow,
5932
+ summaryFields: resolvedResource.summaryFields,
5603
5933
  sort: resolvedResource.sort,
5604
5934
  filter: routeAwareFilters,
5605
5935
  paging: resolvedResource.paging,
@@ -5641,6 +5971,7 @@ const CrudPageInner = ({ resource, onFormDataChange, initialRecordId = null, ini
5641
5971
  events,
5642
5972
  detailFields: formDetail?.fields,
5643
5973
  detailUrl: formDetail?.url,
5974
+ detailSummary: formDetail?.summary,
5644
5975
  detailPropertyName: formDetail?.propertyName,
5645
5976
  allowAdding: dialogMode !== "view" && formDetail?.allowAdding,
5646
5977
  allowDeleting: dialogMode !== "view" && formDetail?.allowDeleting,
@@ -5703,6 +6034,38 @@ const CrudPageInner = ({ resource, onFormDataChange, initialRecordId = null, ini
5703
6034
  });
5704
6035
  };
5705
6036
  //#endregion
6037
+ //#region packages/crud/crud/crudRoute.tsx
6038
+ /**
6039
+ * Wires the two React Router v6 routes a page-mode resource needs:
6040
+ * the list route and the record route (`/sales` and `/sales/:id`, where
6041
+ * `:id` is also matched by the literal `new` for the create form).
6042
+ *
6043
+ * React Router v6 has no optional params (`:id?`), so page mode
6044
+ * (`viewMode: 'page'` + `routing: { routeParam: 'id' }`) requires both
6045
+ * routes to render the same element. This helper returns them in one call:
6046
+ *
6047
+ * ```tsx
6048
+ * <Routes>
6049
+ * {crudRoute('/sales', <SalesPage />)}
6050
+ * {crudRoute('/purchases', <PurchasesPage />, 'purchaseId')}
6051
+ * </Routes>
6052
+ * ```
6053
+ *
6054
+ * @param path - The list path, e.g. `/sales`.
6055
+ * @param element - The page element (typically a `SmartCrudPage` wrapper).
6056
+ * @param routeParam - Param name used in `routing.routeParam`. Default `'id'`.
6057
+ */
6058
+ function crudRoute(path, element, routeParam = "id") {
6059
+ const base = path.replace(/\/+$/, "");
6060
+ return [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_router_dom.Route, {
6061
+ path: base,
6062
+ element
6063
+ }, base), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_router_dom.Route, {
6064
+ path: `${base}/:${routeParam}`,
6065
+ element
6066
+ }, `${base}/:${routeParam}`)];
6067
+ }
6068
+ //#endregion
5706
6069
  //#region packages/crud/crud/routing/useRouting.ts
5707
6070
  const NO_FILTERS = {};
5708
6071
  /**
@@ -6666,7 +7029,7 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
6666
7029
  const hasManualFields = !resource.fieldContract && Array.isArray(resource.fields) && resource.fields.length > 0;
6667
7030
  const { fields, isLoading, error, supportedOperations } = useResolvedResourceFields({
6668
7031
  apiUrl: resolvedBaseResource.apiUrl,
6669
- manualFields: hasManualFields ? resource.fields : void 0,
7032
+ manualFields: hasManualFields ? buildFields(resource.fields) : void 0,
6670
7033
  overrides: hasManualFields ? void 0 : fieldOverrides,
6671
7034
  fieldContract: resource.fieldContract
6672
7035
  });
@@ -6697,7 +7060,7 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
6697
7060
  ...resolvedBaseResource,
6698
7061
  ...!hasManualFields ? { fields: gridFields } : {},
6699
7062
  apiUrl: normalizedApiUrl,
6700
- fields: hasManualFields ? resource.fields : gridFields,
7063
+ fields: hasManualFields ? buildFields(resource.fields) : gridFields,
6701
7064
  formFields: processedFields,
6702
7065
  _supportedOperations: supportedOperations
6703
7066
  }), [
@@ -6748,301 +7111,6 @@ function defineFields(contract) {
6748
7111
  }
6749
7112
  const defineFieldContract = defineFields;
6750
7113
  //#endregion
6751
- //#region packages/crud/field/BaseFieldBuilder.ts
6752
- var BaseFieldBuilder = class {
6753
- _field;
6754
- constructor(type) {
6755
- this._field = {
6756
- isIdentity: false,
6757
- type,
6758
- col: void 0,
6759
- name: "",
6760
- label: "",
6761
- width: void 0,
6762
- height: null,
6763
- minWidth: void 0,
6764
- align: "left",
6765
- sortable: true,
6766
- filterable: true,
6767
- hideable: true,
6768
- validators: [],
6769
- url: void 0,
6770
- loadOptions: [],
6771
- filters: [],
6772
- byKeyUrl: null,
6773
- textField: "",
6774
- valueField: "",
6775
- valueType: "string",
6776
- filterValue: void 0,
6777
- selectedFilterOperation: void 0,
6778
- data: [],
6779
- format: "",
6780
- formatter: void 0,
6781
- itemFormatter: void 0,
6782
- visible: true,
6783
- defaultValue: void 0,
6784
- onChange: void 0,
6785
- onSelect: void 0,
6786
- onClick: void 0,
6787
- readonly: false,
6788
- disabled: false,
6789
- hidden: false,
6790
- required: false,
6791
- precision: 0,
6792
- accept: null,
6793
- buttons: [],
6794
- searchEnabled: true,
6795
- searchExpr: null,
6796
- helpText: void 0,
6797
- contentRender: null,
6798
- visibleOnForm: true,
6799
- maxLength: void 0,
6800
- multiple: false,
6801
- autoSelectIfSingle: false,
6802
- sendAsString: false
6803
- };
6804
- }
6805
- isIdentity(isIdentity) {
6806
- this._field.isIdentity = isIdentity;
6807
- return this;
6808
- }
6809
- col(col) {
6810
- this._field.col = col;
6811
- return this;
6812
- }
6813
- layoutHint(value) {
6814
- this._field.layoutHint = value;
6815
- return this;
6816
- }
6817
- cardRole(value) {
6818
- this._field.cardRole = value;
6819
- return this;
6820
- }
6821
- preferredColSpan(value) {
6822
- this._field.preferredColSpan = value;
6823
- return this;
6824
- }
6825
- minColSpan(value) {
6826
- this._field.minColSpan = value;
6827
- return this;
6828
- }
6829
- forceFullWidth(value = true) {
6830
- this._field.forceFullWidth = value;
6831
- return this;
6832
- }
6833
- name(name) {
6834
- this._field.name = name;
6835
- return this;
6836
- }
6837
- label(label) {
6838
- this._field.label = label;
6839
- return this;
6840
- }
6841
- width(width) {
6842
- this._field.width = width;
6843
- return this;
6844
- }
6845
- height(height) {
6846
- this._field.height = height;
6847
- return this;
6848
- }
6849
- minWidth(minWidth) {
6850
- this._field.minWidth = minWidth;
6851
- return this;
6852
- }
6853
- align(align) {
6854
- this._field.align = align;
6855
- return this;
6856
- }
6857
- sortable(sortable) {
6858
- this._field.sortable = sortable;
6859
- return this;
6860
- }
6861
- filterable(filterable) {
6862
- this._field.filterable = filterable;
6863
- return this;
6864
- }
6865
- hideable(hideable) {
6866
- this._field.hideable = hideable;
6867
- return this;
6868
- }
6869
- validators(validators) {
6870
- this._field.validators = validators;
6871
- return this;
6872
- }
6873
- valueType(valueType) {
6874
- this._field.valueType = valueType;
6875
- return this;
6876
- }
6877
- filterValue(filterValue) {
6878
- this._field.filterValue = filterValue;
6879
- return this;
6880
- }
6881
- data(data) {
6882
- this._field.data = data;
6883
- return this;
6884
- }
6885
- format(format) {
6886
- this._field.format = format;
6887
- return this;
6888
- }
6889
- formatter(formatter) {
6890
- this._field.formatter = formatter;
6891
- return this;
6892
- }
6893
- itemFormatter(itemFormatter) {
6894
- this._field.itemFormatter = itemFormatter;
6895
- return this;
6896
- }
6897
- visible(visible) {
6898
- this._field.visible = visible;
6899
- return this;
6900
- }
6901
- defaultValue(defaultValue) {
6902
- this._field.defaultValue = defaultValue;
6903
- return this;
6904
- }
6905
- onChange(onChange) {
6906
- this._field.onChange = onChange;
6907
- return this;
6908
- }
6909
- onSelect(onSelect) {
6910
- this._field.onSelect = onSelect;
6911
- return this;
6912
- }
6913
- onClick(onClick) {
6914
- this._field.onClick = onClick;
6915
- return this;
6916
- }
6917
- readonly(readonly) {
6918
- this._field.readonly = readonly;
6919
- return this;
6920
- }
6921
- disabled(disabled) {
6922
- this._field.disabled = disabled;
6923
- return this;
6924
- }
6925
- hidden(hidden) {
6926
- this._field.hidden = hidden;
6927
- return this;
6928
- }
6929
- required(required) {
6930
- this._field.required = required;
6931
- return this;
6932
- }
6933
- precision(value) {
6934
- this._field.precision = value;
6935
- return this;
6936
- }
6937
- accept(value) {
6938
- this._field.accept = value;
6939
- return this;
6940
- }
6941
- buttons(value) {
6942
- this._field.buttons = value;
6943
- return this;
6944
- }
6945
- searchEnabled(value) {
6946
- this._field.searchEnabled = value;
6947
- return this;
6948
- }
6949
- searchExpr(value) {
6950
- this._field.searchExpr = value;
6951
- return this;
6952
- }
6953
- helpText(value) {
6954
- this._field.helpText = value;
6955
- return this;
6956
- }
6957
- contentRender(value) {
6958
- this._field.contentRender = value;
6959
- return this;
6960
- }
6961
- visibleOnForm(value) {
6962
- this._field.visibleOnForm = value;
6963
- return this;
6964
- }
6965
- autoSelectIfSingle(value) {
6966
- this._field.autoSelectIfSingle = value;
6967
- return this;
6968
- }
6969
- url(url) {
6970
- this._field.url = url;
6971
- return this;
6972
- }
6973
- loadOptions(param) {
6974
- this._field.loadOptions = param;
6975
- return this;
6976
- }
6977
- filters(filters) {
6978
- this._field.filters = filters;
6979
- return this;
6980
- }
6981
- byKeyUrl(byKeyUrl) {
6982
- this._field.byKeyUrl = byKeyUrl;
6983
- return this;
6984
- }
6985
- textField(textField) {
6986
- this._field.textField = textField;
6987
- return this;
6988
- }
6989
- valueField(valueField) {
6990
- this._field.valueField = valueField;
6991
- return this;
6992
- }
6993
- selectedFilterOperation(operation) {
6994
- this._field.selectedFilterOperation = operation;
6995
- return this;
6996
- }
6997
- sendAsString(value) {
6998
- this._field.sendAsString = value;
6999
- return this;
7000
- }
7001
- multiple(value) {
7002
- this._field.multiple = value;
7003
- return this;
7004
- }
7005
- maxLength(value) {
7006
- this._field.maxLength = value;
7007
- return this;
7008
- }
7009
- visibleWhen(fn) {
7010
- this._field.visibleWhen = fn;
7011
- return this;
7012
- }
7013
- disabledWhen(fn) {
7014
- this._field.disabledWhen = fn;
7015
- return this;
7016
- }
7017
- computed(fn) {
7018
- this._field.computed = fn;
7019
- return this;
7020
- }
7021
- defaultWhen(fn) {
7022
- this._field.defaultWhen = fn;
7023
- return this;
7024
- }
7025
- requiredWhen(fn) {
7026
- this._field.requiredWhen = fn;
7027
- return this;
7028
- }
7029
- clearWhenHidden(value = true) {
7030
- this._field.clearWhenHidden = value;
7031
- return this;
7032
- }
7033
- dependsOn(fields) {
7034
- this._field.dependsOn = fields;
7035
- return this;
7036
- }
7037
- permissions(perms) {
7038
- this._field.permissions = perms;
7039
- return this;
7040
- }
7041
- build() {
7042
- return { ...this._field };
7043
- }
7044
- };
7045
- //#endregion
7046
7114
  //#region packages/crud/field/FieldBuilders.ts
7047
7115
  /**
7048
7116
  * Factory for the standard hidden identity field (type NONE, name 'id').
@@ -7923,6 +7991,7 @@ exports.SmartCrudPage = SmartCrudPage;
7923
7991
  exports.SmartCrudRolesProvider = SmartCrudRolesProvider;
7924
7992
  exports.ToolbarSelect = ToolbarSelect;
7925
7993
  exports.buildFieldColSpanContext = buildFieldColSpanContext;
7994
+ exports.buildFields = buildFields;
7926
7995
  exports.checkboxField = checkboxField;
7927
7996
  exports.computeSummaryValue = computeSummaryValue;
7928
7997
  Object.defineProperty(exports, "createCrudEvents", {
@@ -7931,6 +8000,7 @@ Object.defineProperty(exports, "createCrudEvents", {
7931
8000
  return _nubitio_core.createCrudEvents;
7932
8001
  }
7933
8002
  });
8003
+ exports.crudRoute = crudRoute;
7934
8004
  exports.currencyField = currencyField;
7935
8005
  exports.dateField = dateField;
7936
8006
  exports.datetimeField = datetimeField;