@nubitio/crud 0.2.0 → 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.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  import React, { createContext, forwardRef, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState } from "react";
2
- import { useLocation, useNavigate, useParams, useSearchParams } from "react-router-dom";
2
+ import { Route, useLocation, useNavigate, useParams, useSearchParams } from "react-router-dom";
3
3
  import { createPortal } from "react-dom";
4
4
  import { AppDialog, AppDropdown, Button, ConfirmDialog, DatePicker, DateRangePicker, Drawer, EmptyState, IconButton, Skeleton } from "@nubitio/ui";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
- import { createCrudEvents, createScopedEventBus, getCoreLocale, getCoreTimezone, useCoreHttpClient, useCoreRuntime, useCoreTranslation, useEvents, useMercureSubscription } from "@nubitio/core";
6
+ import { createCrudEvents, createScopedEventBus, getCoreCurrency, getCoreLocale, getCoreTimezone, useCoreHttpClient, useCoreRuntime, useCoreTranslation, useEvents, useMercureSubscription } from "@nubitio/core";
7
7
  import { useDropzone } from "react-dropzone";
8
8
  import { useQueryClient } from "@tanstack/react-query";
9
9
  //#region packages/crud/crud/defineResource.ts
@@ -1067,12 +1067,16 @@ function formatSummaryValue(value, item) {
1067
1067
  minimumFractionDigits: precision,
1068
1068
  maximumFractionDigits: precision
1069
1069
  } : void 0 };
1070
- if (item.valueFormat === "currency") return new Intl.NumberFormat(getCoreLocale(), {
1071
- ...baseOptions,
1072
- style: "currency",
1073
- currency: item.currency ?? "PEN",
1074
- currencyDisplay: item.currencyDisplay ?? "narrowSymbol"
1075
- }).format(value);
1070
+ if (item.valueFormat === "currency") {
1071
+ const currency = item.currency ?? getCoreCurrency();
1072
+ if (currency === void 0) return new Intl.NumberFormat(getCoreLocale(), baseOptions).format(value);
1073
+ return new Intl.NumberFormat(getCoreLocale(), {
1074
+ ...baseOptions,
1075
+ style: "currency",
1076
+ currency,
1077
+ currencyDisplay: item.currencyDisplay ?? "narrowSymbol"
1078
+ }).format(value);
1079
+ }
1076
1080
  if (item.valueFormat === "percent") return new Intl.NumberFormat(getCoreLocale(), {
1077
1081
  ...baseOptions,
1078
1082
  style: "percent"
@@ -1290,6 +1294,7 @@ function useLookupDropdown() {
1290
1294
  };
1291
1295
  }
1292
1296
  function LookupDropdown({ activeOptionId, children, className, containerRef, disabled, id, label, menuRef, menuStyle, normalizedValue, onClear, onKeyDown, onQueryChange, onScroll, open, query, readOnly, required, setDraftQuery, setOpen }) {
1297
+ const { t } = useCoreTranslation();
1293
1298
  const blurTimer = useRef(void 0);
1294
1299
  return /* @__PURE__ */ jsxs("div", {
1295
1300
  ref: containerRef,
@@ -1324,7 +1329,7 @@ function LookupDropdown({ activeOptionId, children, className, containerRef, dis
1324
1329
  !disabled && !readOnly && normalizedValue !== "" && /* @__PURE__ */ jsx("button", {
1325
1330
  type: "button",
1326
1331
  className: "nb-form__lookup-clear",
1327
- "aria-label": `Limpiar ${label}`,
1332
+ "aria-label": t("form.lookupClear", { label }),
1328
1333
  onMouseDown: (e) => e.preventDefault(),
1329
1334
  onClick: onClear,
1330
1335
  children: /* @__PURE__ */ jsx("i", {
@@ -1335,7 +1340,7 @@ function LookupDropdown({ activeOptionId, children, className, containerRef, dis
1335
1340
  !disabled && !readOnly && /* @__PURE__ */ jsx("button", {
1336
1341
  type: "button",
1337
1342
  className: "nb-form__lookup-toggle",
1338
- "aria-label": `Abrir ${label}`,
1343
+ "aria-label": t("form.lookupOpen", { label }),
1339
1344
  onMouseDown: (e) => e.preventDefault(),
1340
1345
  onClick: () => {
1341
1346
  clearTimeout(blurTimer.current);
@@ -2550,7 +2555,7 @@ const NativeFormView = forwardRef((options, ref) => {
2550
2555
  children: [/* @__PURE__ */ jsx("div", {
2551
2556
  className: "nb-form__tabs-nav",
2552
2557
  role: "tablist",
2553
- "aria-label": "Secciones del formulario",
2558
+ "aria-label": t("form.tabsAriaLabel"),
2554
2559
  children: groups.map((group, index) => {
2555
2560
  const tabIcon = resolveTabIcon(group.icon);
2556
2561
  const isActive = index === activeTab;
@@ -2736,8 +2741,18 @@ function getCellText(field, row, entityOptions, yesLabel = "Yes", noLabel = "No"
2736
2741
  if (field.type === "enum" || field.type === "switch") return getEnumDisplayValue(field, value);
2737
2742
  if (field.type === "date") return getDateDisplay(value);
2738
2743
  if (field.type === "datetime") return getDateTimeDisplay(value);
2744
+ if (field.type === "currency") return getCurrencyDisplay(value);
2739
2745
  return getPrimitiveDisplay(value, yesLabel, noLabel);
2740
2746
  }
2747
+ function getCurrencyDisplay(value) {
2748
+ if (value === null || value === void 0 || value === "") return "";
2749
+ const num = Number(value);
2750
+ if (!Number.isFinite(num)) return String(value);
2751
+ return num.toLocaleString(getCoreLocale(), {
2752
+ minimumFractionDigits: 2,
2753
+ maximumFractionDigits: 2
2754
+ });
2755
+ }
2741
2756
  function renderCell(field, row, rowIndex, columnIndex, entityOptions, yesLabel = "Yes", noLabel = "No") {
2742
2757
  const value = row[field.name];
2743
2758
  if (field.formatter) return field.formatter({
@@ -2750,6 +2765,7 @@ function renderCell(field, row, rowIndex, columnIndex, entityOptions, yesLabel =
2750
2765
  if (field.type === "enum" || field.type === "switch") return getEnumDisplayValue(field, value);
2751
2766
  if (field.type === "date") return getDateDisplay(value);
2752
2767
  if (field.type === "datetime") return getDateTimeDisplay(value);
2768
+ if (field.type === "currency") return getCurrencyDisplay(value);
2753
2769
  return getPrimitiveDisplay(value, yesLabel, noLabel);
2754
2770
  }
2755
2771
  function normalizeIcon$1(icon) {
@@ -3587,8 +3603,10 @@ const NativeDataGridView = forwardRef((options, ref) => {
3587
3603
  options.onSelectionChanged?.({ selectedRowsData: nextRows });
3588
3604
  emit(DATA_GRID_EVENTS.SELECTION_CHANGED, nextRows);
3589
3605
  };
3606
+ const rowEditable = (row) => options.canEditRow?.(row) !== false;
3607
+ const rowDeletable = (row) => options.canDeleteRow?.(row) !== false;
3590
3608
  const buildRowActions = (row) => [
3591
- ...options.allowEdit && (options.onEdit || options.events?.EDIT) ? [{
3609
+ ...options.allowEdit && rowEditable(row) && (options.onEdit || options.events?.EDIT) ? [{
3592
3610
  text: t("grid.buttonEdit"),
3593
3611
  icon: "ph-pencil-simple",
3594
3612
  disabled: options.editDisabled,
@@ -3603,7 +3621,7 @@ const NativeDataGridView = forwardRef((options, ref) => {
3603
3621
  onClick: () => options.onView(row)
3604
3622
  }] : [],
3605
3623
  ...getResolvedRowActions(row, options.rowActions),
3606
- ...options.allowDelete && (options.onDelete || options.events?.DELETE) ? [{
3624
+ ...options.allowDelete && rowDeletable(row) && (options.onDelete || options.events?.DELETE) ? [{
3607
3625
  text: t("grid.buttonDelete"),
3608
3626
  icon: "ph-trash",
3609
3627
  type: "danger",
@@ -3612,7 +3630,7 @@ const NativeDataGridView = forwardRef((options, ref) => {
3612
3630
  }] : []
3613
3631
  ];
3614
3632
  const openRow = (row) => {
3615
- if (options.allowEdit && (options.onEdit || options.events?.EDIT)) {
3633
+ if (options.allowEdit && rowEditable(row) && (options.onEdit || options.events?.EDIT)) {
3616
3634
  if (options.onEdit) options.onEdit(row);
3617
3635
  else emit(options.events.EDIT, { row });
3618
3636
  return;
@@ -4941,218 +4959,519 @@ function resolveCrudResource(resource, overrides = {}) {
4941
4959
  };
4942
4960
  }
4943
4961
  //#endregion
4944
- //#region packages/crud/crud/useCrudPage.ts
4945
- function useCrudPage(resource, externalFormRef) {
4946
- const resolvedResource = useMemo(() => resolveCrudResource(resource), [resource]);
4947
- const events = resolvedResource.events;
4948
- const _internalFormRef = useRef(null);
4949
- const formRef = externalFormRef ?? _internalFormRef;
4950
- const fields = useMemo(() => resolvedResource.fields ?? [], [resolvedResource.fields]);
4951
- return {
4952
- events,
4953
- resource: resolvedResource,
4954
- fields,
4955
- formFields: useMemo(() => resolvedResource.formFields ?? fields, [fields, resolvedResource.formFields]),
4956
- formRef,
4957
- permissions: usePermissions(resolvedResource, resolvedResource._supportedOperations ?? []),
4958
- selectionState: useSelectionState(useMemo(() => {
4959
- return fields.find((f) => f.isIdentity)?.name ?? "id";
4960
- }, [fields])),
4961
- presetState: useColumnPreset(resolvedResource)
4962
- };
4963
- }
4964
- //#endregion
4965
- //#region packages/crud/crud/AuditTrailPanel.tsx
4966
- const ACTION_COLORS = {
4967
- create: "#2e7d32",
4968
- update: "#1565c0",
4969
- delete: "#c62828"
4970
- };
4971
- function DefaultEntryRenderer({ entry }) {
4972
- const { t } = useCoreTranslation();
4973
- const date = new Date(entry.timestamp);
4974
- const formatted = Number.isNaN(date.getTime()) ? entry.timestamp : date.toLocaleString(getCoreLocale(), { timeZone: getCoreTimezone() });
4975
- const actionLabels = {
4976
- create: t("auditTrail.action.create"),
4977
- update: t("auditTrail.action.update"),
4978
- delete: t("auditTrail.action.delete")
4979
- };
4980
- const changeKeys = Object.keys(entry.changes);
4981
- return /* @__PURE__ */ jsxs("li", {
4982
- style: {
4983
- borderBottom: "1px solid #e0e0e0",
4984
- padding: "10px 0",
4985
- listStyle: "none"
4986
- },
4987
- children: [/* @__PURE__ */ jsxs("div", {
4988
- style: {
4989
- display: "flex",
4990
- alignItems: "center",
4991
- gap: 8,
4992
- marginBottom: 4
4993
- },
4994
- children: [
4995
- /* @__PURE__ */ jsx("span", {
4996
- style: {
4997
- fontSize: 12,
4998
- color: "#757575"
4999
- },
5000
- children: formatted
5001
- }),
5002
- /* @__PURE__ */ jsx("span", {
5003
- style: {
5004
- fontSize: 12,
5005
- color: "#424242"
5006
- },
5007
- children: entry.user
5008
- }),
5009
- /* @__PURE__ */ jsx("span", {
5010
- style: {
5011
- fontSize: 11,
5012
- fontWeight: 600,
5013
- padding: "1px 6px",
5014
- borderRadius: 4,
5015
- backgroundColor: ACTION_COLORS[entry.action],
5016
- color: "#fff"
5017
- },
5018
- children: actionLabels[entry.action]
5019
- })
5020
- ]
5021
- }), changeKeys.length > 0 && /* @__PURE__ */ jsx("ul", {
5022
- style: {
5023
- margin: "4px 0 0 0",
5024
- padding: "0 0 0 16px"
5025
- },
5026
- children: changeKeys.map((field) => {
5027
- const { before, after } = entry.changes[field];
5028
- return /* @__PURE__ */ jsxs("li", {
5029
- style: {
5030
- fontSize: 12,
5031
- color: "#616161"
5032
- },
5033
- children: [
5034
- /* @__PURE__ */ jsxs("strong", { children: [field, ":"] }),
5035
- " ",
5036
- /* @__PURE__ */ jsx("span", {
5037
- style: { color: "#c62828" },
5038
- children: String(before ?? "—")
5039
- }),
5040
- " → ",
5041
- /* @__PURE__ */ jsx("span", {
5042
- style: { color: "#2e7d32" },
5043
- children: String(after ?? "—")
5044
- })
5045
- ]
5046
- }, field);
5047
- })
5048
- })]
5049
- });
5050
- }
5051
- function AuditTrailPanel({ url, renderEntry, visible, onClose }) {
5052
- const { t } = useCoreTranslation();
5053
- const httpClient = useCoreHttpClient();
5054
- const [fetchState, setFetchState] = useState({ status: "idle" });
5055
- useEffect(() => {
5056
- if (!visible || url === null) {
5057
- setFetchState({ status: "idle" });
5058
- return;
5059
- }
5060
- let cancelled = false;
5061
- setFetchState({ status: "loading" });
5062
- httpClient.get(url).then((response) => {
5063
- if (!cancelled) setFetchState({
5064
- status: "success",
5065
- entries: response.data
5066
- });
5067
- }).catch(() => {
5068
- if (!cancelled) setFetchState({ status: "error" });
5069
- });
5070
- return () => {
5071
- cancelled = true;
4962
+ //#region packages/crud/field/BaseFieldBuilder.ts
4963
+ var BaseFieldBuilder = class {
4964
+ _field;
4965
+ constructor(type) {
4966
+ this._field = {
4967
+ isIdentity: false,
4968
+ type,
4969
+ col: void 0,
4970
+ name: "",
4971
+ label: "",
4972
+ width: void 0,
4973
+ height: null,
4974
+ minWidth: void 0,
4975
+ align: "left",
4976
+ sortable: true,
4977
+ filterable: true,
4978
+ hideable: true,
4979
+ validators: [],
4980
+ url: void 0,
4981
+ loadOptions: [],
4982
+ filters: [],
4983
+ byKeyUrl: null,
4984
+ textField: "",
4985
+ valueField: "",
4986
+ valueType: "string",
4987
+ filterValue: void 0,
4988
+ selectedFilterOperation: void 0,
4989
+ data: [],
4990
+ format: "",
4991
+ formatter: void 0,
4992
+ itemFormatter: void 0,
4993
+ visible: true,
4994
+ defaultValue: void 0,
4995
+ onChange: void 0,
4996
+ onSelect: void 0,
4997
+ onClick: void 0,
4998
+ readonly: false,
4999
+ disabled: false,
5000
+ hidden: false,
5001
+ required: false,
5002
+ precision: 0,
5003
+ accept: null,
5004
+ buttons: [],
5005
+ searchEnabled: true,
5006
+ searchExpr: null,
5007
+ helpText: void 0,
5008
+ contentRender: null,
5009
+ visibleOnForm: true,
5010
+ maxLength: void 0,
5011
+ multiple: false,
5012
+ autoSelectIfSingle: false,
5013
+ sendAsString: false
5072
5014
  };
5073
- }, [
5074
- httpClient,
5075
- url,
5076
- visible
5077
- ]);
5078
- if (!visible) return null;
5079
- return /* @__PURE__ */ jsxs("aside", {
5080
- className: "nb-audit-trail-panel",
5081
- style: {
5082
- border: "1px solid #e0e0e0",
5083
- borderRadius: 4,
5084
- padding: "12px 16px",
5085
- backgroundColor: "#fafafa",
5086
- minWidth: 280
5087
- },
5088
- children: [/* @__PURE__ */ jsxs("div", {
5089
- style: {
5090
- display: "flex",
5091
- alignItems: "center",
5092
- justifyContent: "space-between",
5093
- marginBottom: 12
5094
- },
5095
- children: [/* @__PURE__ */ jsx("strong", {
5096
- style: { fontSize: 14 },
5097
- children: t("auditTrail.title")
5098
- }), /* @__PURE__ */ jsx("button", {
5099
- type: "button",
5100
- onClick: onClose,
5101
- "aria-label": t("auditTrail.closeButton"),
5102
- style: {
5103
- background: "none",
5104
- border: "none",
5105
- cursor: "pointer",
5106
- fontSize: 18,
5107
- lineHeight: 1,
5108
- padding: "0 4px",
5109
- color: "#616161"
5110
- },
5111
- children: "×"
5112
- })]
5113
- }), url === null ? /* @__PURE__ */ jsx("p", {
5114
- style: {
5115
- fontSize: 13,
5116
- color: "#757575",
5117
- margin: 0
5118
- },
5119
- children: t("auditTrail.selectRecord")
5120
- }) : fetchState.status === "loading" ? /* @__PURE__ */ jsx("p", {
5121
- style: {
5122
- fontSize: 13,
5123
- color: "#757575",
5124
- margin: 0
5125
- },
5126
- children: t("auditTrail.loading")
5127
- }) : fetchState.status === "error" ? /* @__PURE__ */ jsx("p", {
5128
- style: {
5129
- fontSize: 13,
5130
- color: "#c62828",
5131
- margin: 0
5132
- },
5133
- children: t("auditTrail.error")
5134
- }) : fetchState.status === "success" ? fetchState.entries.length === 0 ? /* @__PURE__ */ jsx("p", {
5135
- style: {
5136
- fontSize: 13,
5137
- color: "#757575",
5138
- margin: 0
5139
- },
5140
- children: t("auditTrail.empty")
5141
- }) : /* @__PURE__ */ jsx("ul", {
5142
- style: {
5143
- margin: 0,
5144
- padding: 0
5145
- },
5146
- children: fetchState.entries.map((entry) => renderEntry ? /* @__PURE__ */ jsx(React.Fragment, { children: renderEntry(entry) }, String(entry.id)) : /* @__PURE__ */ jsx(DefaultEntryRenderer, { entry }, String(entry.id)))
5147
- }) : null]
5148
- });
5015
+ }
5016
+ isIdentity(isIdentity) {
5017
+ this._field.isIdentity = isIdentity;
5018
+ return this;
5019
+ }
5020
+ col(col) {
5021
+ this._field.col = col;
5022
+ return this;
5023
+ }
5024
+ layoutHint(value) {
5025
+ this._field.layoutHint = value;
5026
+ return this;
5027
+ }
5028
+ cardRole(value) {
5029
+ this._field.cardRole = value;
5030
+ return this;
5031
+ }
5032
+ preferredColSpan(value) {
5033
+ this._field.preferredColSpan = value;
5034
+ return this;
5035
+ }
5036
+ minColSpan(value) {
5037
+ this._field.minColSpan = value;
5038
+ return this;
5039
+ }
5040
+ forceFullWidth(value = true) {
5041
+ this._field.forceFullWidth = value;
5042
+ return this;
5043
+ }
5044
+ name(name) {
5045
+ this._field.name = name;
5046
+ return this;
5047
+ }
5048
+ label(label) {
5049
+ this._field.label = label;
5050
+ return this;
5051
+ }
5052
+ width(width) {
5053
+ this._field.width = width;
5054
+ return this;
5055
+ }
5056
+ height(height) {
5057
+ this._field.height = height;
5058
+ return this;
5059
+ }
5060
+ minWidth(minWidth) {
5061
+ this._field.minWidth = minWidth;
5062
+ return this;
5063
+ }
5064
+ align(align) {
5065
+ this._field.align = align;
5066
+ return this;
5067
+ }
5068
+ sortable(sortable) {
5069
+ this._field.sortable = sortable;
5070
+ return this;
5071
+ }
5072
+ filterable(filterable) {
5073
+ this._field.filterable = filterable;
5074
+ return this;
5075
+ }
5076
+ hideable(hideable) {
5077
+ this._field.hideable = hideable;
5078
+ return this;
5079
+ }
5080
+ validators(validators) {
5081
+ this._field.validators = validators;
5082
+ return this;
5083
+ }
5084
+ valueType(valueType) {
5085
+ this._field.valueType = valueType;
5086
+ return this;
5087
+ }
5088
+ filterValue(filterValue) {
5089
+ this._field.filterValue = filterValue;
5090
+ return this;
5091
+ }
5092
+ data(data) {
5093
+ this._field.data = data;
5094
+ return this;
5095
+ }
5096
+ format(format) {
5097
+ this._field.format = format;
5098
+ return this;
5099
+ }
5100
+ formatter(formatter) {
5101
+ this._field.formatter = formatter;
5102
+ return this;
5103
+ }
5104
+ itemFormatter(itemFormatter) {
5105
+ this._field.itemFormatter = itemFormatter;
5106
+ return this;
5107
+ }
5108
+ visible(visible) {
5109
+ this._field.visible = visible;
5110
+ return this;
5111
+ }
5112
+ defaultValue(defaultValue) {
5113
+ this._field.defaultValue = defaultValue;
5114
+ return this;
5115
+ }
5116
+ onChange(onChange) {
5117
+ this._field.onChange = onChange;
5118
+ return this;
5119
+ }
5120
+ onSelect(onSelect) {
5121
+ this._field.onSelect = onSelect;
5122
+ return this;
5123
+ }
5124
+ onClick(onClick) {
5125
+ this._field.onClick = onClick;
5126
+ return this;
5127
+ }
5128
+ readonly(readonly) {
5129
+ this._field.readonly = readonly;
5130
+ return this;
5131
+ }
5132
+ disabled(disabled) {
5133
+ this._field.disabled = disabled;
5134
+ return this;
5135
+ }
5136
+ hidden(hidden) {
5137
+ this._field.hidden = hidden;
5138
+ return this;
5139
+ }
5140
+ required(required) {
5141
+ this._field.required = required;
5142
+ return this;
5143
+ }
5144
+ precision(value) {
5145
+ this._field.precision = value;
5146
+ return this;
5147
+ }
5148
+ accept(value) {
5149
+ this._field.accept = value;
5150
+ return this;
5151
+ }
5152
+ buttons(value) {
5153
+ this._field.buttons = value;
5154
+ return this;
5155
+ }
5156
+ searchEnabled(value) {
5157
+ this._field.searchEnabled = value;
5158
+ return this;
5159
+ }
5160
+ searchExpr(value) {
5161
+ this._field.searchExpr = value;
5162
+ return this;
5163
+ }
5164
+ helpText(value) {
5165
+ this._field.helpText = value;
5166
+ return this;
5167
+ }
5168
+ contentRender(value) {
5169
+ this._field.contentRender = value;
5170
+ return this;
5171
+ }
5172
+ visibleOnForm(value) {
5173
+ this._field.visibleOnForm = value;
5174
+ return this;
5175
+ }
5176
+ autoSelectIfSingle(value) {
5177
+ this._field.autoSelectIfSingle = value;
5178
+ return this;
5179
+ }
5180
+ url(url) {
5181
+ this._field.url = url;
5182
+ return this;
5183
+ }
5184
+ loadOptions(param) {
5185
+ this._field.loadOptions = param;
5186
+ return this;
5187
+ }
5188
+ filters(filters) {
5189
+ this._field.filters = filters;
5190
+ return this;
5191
+ }
5192
+ byKeyUrl(byKeyUrl) {
5193
+ this._field.byKeyUrl = byKeyUrl;
5194
+ return this;
5195
+ }
5196
+ textField(textField) {
5197
+ this._field.textField = textField;
5198
+ return this;
5199
+ }
5200
+ valueField(valueField) {
5201
+ this._field.valueField = valueField;
5202
+ return this;
5203
+ }
5204
+ selectedFilterOperation(operation) {
5205
+ this._field.selectedFilterOperation = operation;
5206
+ return this;
5207
+ }
5208
+ sendAsString(value) {
5209
+ this._field.sendAsString = value;
5210
+ return this;
5211
+ }
5212
+ multiple(value) {
5213
+ this._field.multiple = value;
5214
+ return this;
5215
+ }
5216
+ maxLength(value) {
5217
+ this._field.maxLength = value;
5218
+ return this;
5219
+ }
5220
+ visibleWhen(fn) {
5221
+ this._field.visibleWhen = fn;
5222
+ return this;
5223
+ }
5224
+ disabledWhen(fn) {
5225
+ this._field.disabledWhen = fn;
5226
+ return this;
5227
+ }
5228
+ computed(fn) {
5229
+ this._field.computed = fn;
5230
+ return this;
5231
+ }
5232
+ defaultWhen(fn) {
5233
+ this._field.defaultWhen = fn;
5234
+ return this;
5235
+ }
5236
+ requiredWhen(fn) {
5237
+ this._field.requiredWhen = fn;
5238
+ return this;
5239
+ }
5240
+ clearWhenHidden(value = true) {
5241
+ this._field.clearWhenHidden = value;
5242
+ return this;
5243
+ }
5244
+ dependsOn(fields) {
5245
+ this._field.dependsOn = fields;
5246
+ return this;
5247
+ }
5248
+ permissions(perms) {
5249
+ this._field.permissions = perms;
5250
+ return this;
5251
+ }
5252
+ build() {
5253
+ return { ...this._field };
5254
+ }
5255
+ };
5256
+ //#endregion
5257
+ //#region packages/crud/field/buildFields.ts
5258
+ /** Normalizes a mixed array of Fields and builders into plain Fields. */
5259
+ function buildFields(items) {
5260
+ return items.map((item) => item instanceof BaseFieldBuilder ? item.build() : item);
5149
5261
  }
5150
5262
  //#endregion
5151
- //#region packages/crud/crud/dialogStore.ts
5152
- function initialDialogState() {
5263
+ //#region packages/crud/crud/useCrudPage.ts
5264
+ function useCrudPage(resource, externalFormRef) {
5265
+ const resolvedResource = useMemo(() => resolveCrudResource(resource), [resource]);
5266
+ const events = resolvedResource.events;
5267
+ const _internalFormRef = useRef(null);
5268
+ const formRef = externalFormRef ?? _internalFormRef;
5269
+ const fields = useMemo(() => buildFields(resolvedResource.fields ?? []), [resolvedResource.fields]);
5153
5270
  return {
5154
- isOpen: false,
5155
- mode: "add",
5271
+ events,
5272
+ resource: resolvedResource,
5273
+ fields,
5274
+ formFields: useMemo(() => resolvedResource.formFields ? buildFields(resolvedResource.formFields) : fields, [fields, resolvedResource.formFields]),
5275
+ formRef,
5276
+ permissions: usePermissions(resolvedResource, resolvedResource._supportedOperations ?? []),
5277
+ selectionState: useSelectionState(useMemo(() => {
5278
+ return fields.find((f) => f.isIdentity)?.name ?? "id";
5279
+ }, [fields])),
5280
+ presetState: useColumnPreset(resolvedResource)
5281
+ };
5282
+ }
5283
+ //#endregion
5284
+ //#region packages/crud/crud/AuditTrailPanel.tsx
5285
+ const ACTION_COLORS = {
5286
+ create: "#2e7d32",
5287
+ update: "#1565c0",
5288
+ delete: "#c62828"
5289
+ };
5290
+ function DefaultEntryRenderer({ entry }) {
5291
+ const { t } = useCoreTranslation();
5292
+ const date = new Date(entry.timestamp);
5293
+ const formatted = Number.isNaN(date.getTime()) ? entry.timestamp : date.toLocaleString(getCoreLocale(), { timeZone: getCoreTimezone() });
5294
+ const actionLabels = {
5295
+ create: t("auditTrail.action.create"),
5296
+ update: t("auditTrail.action.update"),
5297
+ delete: t("auditTrail.action.delete")
5298
+ };
5299
+ const changeKeys = Object.keys(entry.changes);
5300
+ return /* @__PURE__ */ jsxs("li", {
5301
+ style: {
5302
+ borderBottom: "1px solid #e0e0e0",
5303
+ padding: "10px 0",
5304
+ listStyle: "none"
5305
+ },
5306
+ children: [/* @__PURE__ */ jsxs("div", {
5307
+ style: {
5308
+ display: "flex",
5309
+ alignItems: "center",
5310
+ gap: 8,
5311
+ marginBottom: 4
5312
+ },
5313
+ children: [
5314
+ /* @__PURE__ */ jsx("span", {
5315
+ style: {
5316
+ fontSize: 12,
5317
+ color: "#757575"
5318
+ },
5319
+ children: formatted
5320
+ }),
5321
+ /* @__PURE__ */ jsx("span", {
5322
+ style: {
5323
+ fontSize: 12,
5324
+ color: "#424242"
5325
+ },
5326
+ children: entry.user
5327
+ }),
5328
+ /* @__PURE__ */ jsx("span", {
5329
+ style: {
5330
+ fontSize: 11,
5331
+ fontWeight: 600,
5332
+ padding: "1px 6px",
5333
+ borderRadius: 4,
5334
+ backgroundColor: ACTION_COLORS[entry.action],
5335
+ color: "#fff"
5336
+ },
5337
+ children: actionLabels[entry.action]
5338
+ })
5339
+ ]
5340
+ }), changeKeys.length > 0 && /* @__PURE__ */ jsx("ul", {
5341
+ style: {
5342
+ margin: "4px 0 0 0",
5343
+ padding: "0 0 0 16px"
5344
+ },
5345
+ children: changeKeys.map((field) => {
5346
+ const { before, after } = entry.changes[field];
5347
+ return /* @__PURE__ */ jsxs("li", {
5348
+ style: {
5349
+ fontSize: 12,
5350
+ color: "#616161"
5351
+ },
5352
+ children: [
5353
+ /* @__PURE__ */ jsxs("strong", { children: [field, ":"] }),
5354
+ " ",
5355
+ /* @__PURE__ */ jsx("span", {
5356
+ style: { color: "#c62828" },
5357
+ children: String(before ?? "—")
5358
+ }),
5359
+ " → ",
5360
+ /* @__PURE__ */ jsx("span", {
5361
+ style: { color: "#2e7d32" },
5362
+ children: String(after ?? "—")
5363
+ })
5364
+ ]
5365
+ }, field);
5366
+ })
5367
+ })]
5368
+ });
5369
+ }
5370
+ function AuditTrailPanel({ url, renderEntry, visible, onClose }) {
5371
+ const { t } = useCoreTranslation();
5372
+ const httpClient = useCoreHttpClient();
5373
+ const [fetchState, setFetchState] = useState({ status: "idle" });
5374
+ useEffect(() => {
5375
+ if (!visible || url === null) {
5376
+ setFetchState({ status: "idle" });
5377
+ return;
5378
+ }
5379
+ let cancelled = false;
5380
+ setFetchState({ status: "loading" });
5381
+ httpClient.get(url).then((response) => {
5382
+ if (!cancelled) setFetchState({
5383
+ status: "success",
5384
+ entries: response.data
5385
+ });
5386
+ }).catch(() => {
5387
+ if (!cancelled) setFetchState({ status: "error" });
5388
+ });
5389
+ return () => {
5390
+ cancelled = true;
5391
+ };
5392
+ }, [
5393
+ httpClient,
5394
+ url,
5395
+ visible
5396
+ ]);
5397
+ if (!visible) return null;
5398
+ return /* @__PURE__ */ jsxs("aside", {
5399
+ className: "nb-audit-trail-panel",
5400
+ style: {
5401
+ border: "1px solid #e0e0e0",
5402
+ borderRadius: 4,
5403
+ padding: "12px 16px",
5404
+ backgroundColor: "#fafafa",
5405
+ minWidth: 280
5406
+ },
5407
+ children: [/* @__PURE__ */ jsxs("div", {
5408
+ style: {
5409
+ display: "flex",
5410
+ alignItems: "center",
5411
+ justifyContent: "space-between",
5412
+ marginBottom: 12
5413
+ },
5414
+ children: [/* @__PURE__ */ jsx("strong", {
5415
+ style: { fontSize: 14 },
5416
+ children: t("auditTrail.title")
5417
+ }), /* @__PURE__ */ jsx("button", {
5418
+ type: "button",
5419
+ onClick: onClose,
5420
+ "aria-label": t("auditTrail.closeButton"),
5421
+ style: {
5422
+ background: "none",
5423
+ border: "none",
5424
+ cursor: "pointer",
5425
+ fontSize: 18,
5426
+ lineHeight: 1,
5427
+ padding: "0 4px",
5428
+ color: "#616161"
5429
+ },
5430
+ children: "×"
5431
+ })]
5432
+ }), url === null ? /* @__PURE__ */ jsx("p", {
5433
+ style: {
5434
+ fontSize: 13,
5435
+ color: "#757575",
5436
+ margin: 0
5437
+ },
5438
+ children: t("auditTrail.selectRecord")
5439
+ }) : fetchState.status === "loading" ? /* @__PURE__ */ jsx("p", {
5440
+ style: {
5441
+ fontSize: 13,
5442
+ color: "#757575",
5443
+ margin: 0
5444
+ },
5445
+ children: t("auditTrail.loading")
5446
+ }) : fetchState.status === "error" ? /* @__PURE__ */ jsx("p", {
5447
+ style: {
5448
+ fontSize: 13,
5449
+ color: "#c62828",
5450
+ margin: 0
5451
+ },
5452
+ children: t("auditTrail.error")
5453
+ }) : fetchState.status === "success" ? fetchState.entries.length === 0 ? /* @__PURE__ */ jsx("p", {
5454
+ style: {
5455
+ fontSize: 13,
5456
+ color: "#757575",
5457
+ margin: 0
5458
+ },
5459
+ children: t("auditTrail.empty")
5460
+ }) : /* @__PURE__ */ jsx("ul", {
5461
+ style: {
5462
+ margin: 0,
5463
+ padding: 0
5464
+ },
5465
+ children: fetchState.entries.map((entry) => renderEntry ? /* @__PURE__ */ jsx(React.Fragment, { children: renderEntry(entry) }, String(entry.id)) : /* @__PURE__ */ jsx(DefaultEntryRenderer, { entry }, String(entry.id)))
5466
+ }) : null]
5467
+ });
5468
+ }
5469
+ //#endregion
5470
+ //#region packages/crud/crud/dialogStore.ts
5471
+ function initialDialogState() {
5472
+ return {
5473
+ isOpen: false,
5474
+ mode: "add",
5156
5475
  rowData: null
5157
5476
  };
5158
5477
  }
@@ -5223,9 +5542,16 @@ function useCrudDialogStore(resourceId) {
5223
5542
  //#endregion
5224
5543
  //#region packages/crud/crud/resolveResourceDetails.ts
5225
5544
  function resolveResourceDetails(resource) {
5545
+ const { gridDetail, formDetail } = resource;
5226
5546
  return {
5227
- gridDetail: resource.gridDetail,
5228
- formDetail: resource.formDetail
5547
+ gridDetail: gridDetail && {
5548
+ ...gridDetail,
5549
+ fields: typeof gridDetail.fields === "function" ? (parentRow) => buildFields(gridDetail.fields(parentRow)) : buildFields(gridDetail.fields)
5550
+ },
5551
+ formDetail: formDetail && {
5552
+ ...formDetail,
5553
+ fields: buildFields(formDetail.fields)
5554
+ }
5229
5555
  };
5230
5556
  }
5231
5557
  //#endregion
@@ -5317,6 +5643,7 @@ const CrudPageInner = ({ resource, onFormDataChange, initialRecordId = null, ini
5317
5643
  const { t } = useCoreTranslation();
5318
5644
  const { events, resource: resolvedResource, fields, formFields, formRef, permissions, selectionState, presetState } = useCrudPage(useMemo(() => resolveCrudResource(resource), [resource]), externalFormRef);
5319
5645
  const datagridFields = useMemo(() => fields.filter((field) => field.isIdentity || field.visible !== false), [fields]);
5646
+ const rawPermissions = typeof resolvedResource.permissions === "function" ? void 0 : resolvedResource.permissions;
5320
5647
  const routeAwareGridFields = useMemo(() => datagridFields.map((field) => {
5321
5648
  const routeFilterValue = initialFilters[field.name];
5322
5649
  if (routeFilterValue === void 0) {
@@ -5576,6 +5903,9 @@ const CrudPageInner = ({ resource, onFormDataChange, initialRecordId = null, ini
5576
5903
  allowExport: permissions.canExport,
5577
5904
  editDisabled,
5578
5905
  deleteDisabled,
5906
+ canEditRow: rawPermissions?.canEditRow,
5907
+ canDeleteRow: rawPermissions?.canDeleteRow,
5908
+ summaryFields: resolvedResource.summaryFields,
5579
5909
  sort: resolvedResource.sort,
5580
5910
  filter: routeAwareFilters,
5581
5911
  paging: resolvedResource.paging,
@@ -5617,6 +5947,7 @@ const CrudPageInner = ({ resource, onFormDataChange, initialRecordId = null, ini
5617
5947
  events,
5618
5948
  detailFields: formDetail?.fields,
5619
5949
  detailUrl: formDetail?.url,
5950
+ detailSummary: formDetail?.summary,
5620
5951
  detailPropertyName: formDetail?.propertyName,
5621
5952
  allowAdding: dialogMode !== "view" && formDetail?.allowAdding,
5622
5953
  allowDeleting: dialogMode !== "view" && formDetail?.allowDeleting,
@@ -5679,6 +6010,38 @@ const CrudPageInner = ({ resource, onFormDataChange, initialRecordId = null, ini
5679
6010
  });
5680
6011
  };
5681
6012
  //#endregion
6013
+ //#region packages/crud/crud/crudRoute.tsx
6014
+ /**
6015
+ * Wires the two React Router v6 routes a page-mode resource needs:
6016
+ * the list route and the record route (`/sales` and `/sales/:id`, where
6017
+ * `:id` is also matched by the literal `new` for the create form).
6018
+ *
6019
+ * React Router v6 has no optional params (`:id?`), so page mode
6020
+ * (`viewMode: 'page'` + `routing: { routeParam: 'id' }`) requires both
6021
+ * routes to render the same element. This helper returns them in one call:
6022
+ *
6023
+ * ```tsx
6024
+ * <Routes>
6025
+ * {crudRoute('/sales', <SalesPage />)}
6026
+ * {crudRoute('/purchases', <PurchasesPage />, 'purchaseId')}
6027
+ * </Routes>
6028
+ * ```
6029
+ *
6030
+ * @param path - The list path, e.g. `/sales`.
6031
+ * @param element - The page element (typically a `SmartCrudPage` wrapper).
6032
+ * @param routeParam - Param name used in `routing.routeParam`. Default `'id'`.
6033
+ */
6034
+ function crudRoute(path, element, routeParam = "id") {
6035
+ const base = path.replace(/\/+$/, "");
6036
+ return [/* @__PURE__ */ jsx(Route, {
6037
+ path: base,
6038
+ element
6039
+ }, base), /* @__PURE__ */ jsx(Route, {
6040
+ path: `${base}/:${routeParam}`,
6041
+ element
6042
+ }, `${base}/:${routeParam}`)];
6043
+ }
6044
+ //#endregion
5682
6045
  //#region packages/crud/crud/routing/useRouting.ts
5683
6046
  const NO_FILTERS = {};
5684
6047
  /**
@@ -6642,7 +7005,7 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
6642
7005
  const hasManualFields = !resource.fieldContract && Array.isArray(resource.fields) && resource.fields.length > 0;
6643
7006
  const { fields, isLoading, error, supportedOperations } = useResolvedResourceFields({
6644
7007
  apiUrl: resolvedBaseResource.apiUrl,
6645
- manualFields: hasManualFields ? resource.fields : void 0,
7008
+ manualFields: hasManualFields ? buildFields(resource.fields) : void 0,
6646
7009
  overrides: hasManualFields ? void 0 : fieldOverrides,
6647
7010
  fieldContract: resource.fieldContract
6648
7011
  });
@@ -6673,7 +7036,7 @@ function SmartCrudPage({ resource, fieldOverrides, formRef, onSelectionChanged,
6673
7036
  ...resolvedBaseResource,
6674
7037
  ...!hasManualFields ? { fields: gridFields } : {},
6675
7038
  apiUrl: normalizedApiUrl,
6676
- fields: hasManualFields ? resource.fields : gridFields,
7039
+ fields: hasManualFields ? buildFields(resource.fields) : gridFields,
6677
7040
  formFields: processedFields,
6678
7041
  _supportedOperations: supportedOperations
6679
7042
  }), [
@@ -6724,301 +7087,6 @@ function defineFields(contract) {
6724
7087
  }
6725
7088
  const defineFieldContract = defineFields;
6726
7089
  //#endregion
6727
- //#region packages/crud/field/BaseFieldBuilder.ts
6728
- var BaseFieldBuilder = class {
6729
- _field;
6730
- constructor(type) {
6731
- this._field = {
6732
- isIdentity: false,
6733
- type,
6734
- col: void 0,
6735
- name: "",
6736
- label: "",
6737
- width: void 0,
6738
- height: null,
6739
- minWidth: void 0,
6740
- align: "left",
6741
- sortable: true,
6742
- filterable: true,
6743
- hideable: true,
6744
- validators: [],
6745
- url: void 0,
6746
- loadOptions: [],
6747
- filters: [],
6748
- byKeyUrl: null,
6749
- textField: "",
6750
- valueField: "",
6751
- valueType: "string",
6752
- filterValue: void 0,
6753
- selectedFilterOperation: void 0,
6754
- data: [],
6755
- format: "",
6756
- formatter: void 0,
6757
- itemFormatter: void 0,
6758
- visible: true,
6759
- defaultValue: void 0,
6760
- onChange: void 0,
6761
- onSelect: void 0,
6762
- onClick: void 0,
6763
- readonly: false,
6764
- disabled: false,
6765
- hidden: false,
6766
- required: false,
6767
- precision: 0,
6768
- accept: null,
6769
- buttons: [],
6770
- searchEnabled: true,
6771
- searchExpr: null,
6772
- helpText: void 0,
6773
- contentRender: null,
6774
- visibleOnForm: true,
6775
- maxLength: void 0,
6776
- multiple: false,
6777
- autoSelectIfSingle: false,
6778
- sendAsString: false
6779
- };
6780
- }
6781
- isIdentity(isIdentity) {
6782
- this._field.isIdentity = isIdentity;
6783
- return this;
6784
- }
6785
- col(col) {
6786
- this._field.col = col;
6787
- return this;
6788
- }
6789
- layoutHint(value) {
6790
- this._field.layoutHint = value;
6791
- return this;
6792
- }
6793
- cardRole(value) {
6794
- this._field.cardRole = value;
6795
- return this;
6796
- }
6797
- preferredColSpan(value) {
6798
- this._field.preferredColSpan = value;
6799
- return this;
6800
- }
6801
- minColSpan(value) {
6802
- this._field.minColSpan = value;
6803
- return this;
6804
- }
6805
- forceFullWidth(value = true) {
6806
- this._field.forceFullWidth = value;
6807
- return this;
6808
- }
6809
- name(name) {
6810
- this._field.name = name;
6811
- return this;
6812
- }
6813
- label(label) {
6814
- this._field.label = label;
6815
- return this;
6816
- }
6817
- width(width) {
6818
- this._field.width = width;
6819
- return this;
6820
- }
6821
- height(height) {
6822
- this._field.height = height;
6823
- return this;
6824
- }
6825
- minWidth(minWidth) {
6826
- this._field.minWidth = minWidth;
6827
- return this;
6828
- }
6829
- align(align) {
6830
- this._field.align = align;
6831
- return this;
6832
- }
6833
- sortable(sortable) {
6834
- this._field.sortable = sortable;
6835
- return this;
6836
- }
6837
- filterable(filterable) {
6838
- this._field.filterable = filterable;
6839
- return this;
6840
- }
6841
- hideable(hideable) {
6842
- this._field.hideable = hideable;
6843
- return this;
6844
- }
6845
- validators(validators) {
6846
- this._field.validators = validators;
6847
- return this;
6848
- }
6849
- valueType(valueType) {
6850
- this._field.valueType = valueType;
6851
- return this;
6852
- }
6853
- filterValue(filterValue) {
6854
- this._field.filterValue = filterValue;
6855
- return this;
6856
- }
6857
- data(data) {
6858
- this._field.data = data;
6859
- return this;
6860
- }
6861
- format(format) {
6862
- this._field.format = format;
6863
- return this;
6864
- }
6865
- formatter(formatter) {
6866
- this._field.formatter = formatter;
6867
- return this;
6868
- }
6869
- itemFormatter(itemFormatter) {
6870
- this._field.itemFormatter = itemFormatter;
6871
- return this;
6872
- }
6873
- visible(visible) {
6874
- this._field.visible = visible;
6875
- return this;
6876
- }
6877
- defaultValue(defaultValue) {
6878
- this._field.defaultValue = defaultValue;
6879
- return this;
6880
- }
6881
- onChange(onChange) {
6882
- this._field.onChange = onChange;
6883
- return this;
6884
- }
6885
- onSelect(onSelect) {
6886
- this._field.onSelect = onSelect;
6887
- return this;
6888
- }
6889
- onClick(onClick) {
6890
- this._field.onClick = onClick;
6891
- return this;
6892
- }
6893
- readonly(readonly) {
6894
- this._field.readonly = readonly;
6895
- return this;
6896
- }
6897
- disabled(disabled) {
6898
- this._field.disabled = disabled;
6899
- return this;
6900
- }
6901
- hidden(hidden) {
6902
- this._field.hidden = hidden;
6903
- return this;
6904
- }
6905
- required(required) {
6906
- this._field.required = required;
6907
- return this;
6908
- }
6909
- precision(value) {
6910
- this._field.precision = value;
6911
- return this;
6912
- }
6913
- accept(value) {
6914
- this._field.accept = value;
6915
- return this;
6916
- }
6917
- buttons(value) {
6918
- this._field.buttons = value;
6919
- return this;
6920
- }
6921
- searchEnabled(value) {
6922
- this._field.searchEnabled = value;
6923
- return this;
6924
- }
6925
- searchExpr(value) {
6926
- this._field.searchExpr = value;
6927
- return this;
6928
- }
6929
- helpText(value) {
6930
- this._field.helpText = value;
6931
- return this;
6932
- }
6933
- contentRender(value) {
6934
- this._field.contentRender = value;
6935
- return this;
6936
- }
6937
- visibleOnForm(value) {
6938
- this._field.visibleOnForm = value;
6939
- return this;
6940
- }
6941
- autoSelectIfSingle(value) {
6942
- this._field.autoSelectIfSingle = value;
6943
- return this;
6944
- }
6945
- url(url) {
6946
- this._field.url = url;
6947
- return this;
6948
- }
6949
- loadOptions(param) {
6950
- this._field.loadOptions = param;
6951
- return this;
6952
- }
6953
- filters(filters) {
6954
- this._field.filters = filters;
6955
- return this;
6956
- }
6957
- byKeyUrl(byKeyUrl) {
6958
- this._field.byKeyUrl = byKeyUrl;
6959
- return this;
6960
- }
6961
- textField(textField) {
6962
- this._field.textField = textField;
6963
- return this;
6964
- }
6965
- valueField(valueField) {
6966
- this._field.valueField = valueField;
6967
- return this;
6968
- }
6969
- selectedFilterOperation(operation) {
6970
- this._field.selectedFilterOperation = operation;
6971
- return this;
6972
- }
6973
- sendAsString(value) {
6974
- this._field.sendAsString = value;
6975
- return this;
6976
- }
6977
- multiple(value) {
6978
- this._field.multiple = value;
6979
- return this;
6980
- }
6981
- maxLength(value) {
6982
- this._field.maxLength = value;
6983
- return this;
6984
- }
6985
- visibleWhen(fn) {
6986
- this._field.visibleWhen = fn;
6987
- return this;
6988
- }
6989
- disabledWhen(fn) {
6990
- this._field.disabledWhen = fn;
6991
- return this;
6992
- }
6993
- computed(fn) {
6994
- this._field.computed = fn;
6995
- return this;
6996
- }
6997
- defaultWhen(fn) {
6998
- this._field.defaultWhen = fn;
6999
- return this;
7000
- }
7001
- requiredWhen(fn) {
7002
- this._field.requiredWhen = fn;
7003
- return this;
7004
- }
7005
- clearWhenHidden(value = true) {
7006
- this._field.clearWhenHidden = value;
7007
- return this;
7008
- }
7009
- dependsOn(fields) {
7010
- this._field.dependsOn = fields;
7011
- return this;
7012
- }
7013
- permissions(perms) {
7014
- this._field.permissions = perms;
7015
- return this;
7016
- }
7017
- build() {
7018
- return { ...this._field };
7019
- }
7020
- };
7021
- //#endregion
7022
7090
  //#region packages/crud/field/FieldBuilders.ts
7023
7091
  /**
7024
7092
  * Factory for the standard hidden identity field (type NONE, name 'id').
@@ -7872,4 +7940,4 @@ const RestAdapter = {
7872
7940
  synthesizeEntityKey(_field, _entityValue) {}
7873
7941
  };
7874
7942
  //#endregion
7875
- 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, checkboxField, computeSummaryValue, createCrudEvents, 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 };
7943
+ 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, 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 };