@oondemand/oon-core-front 0.3.41 → 0.3.43

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.js CHANGED
@@ -1,3 +1,7 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+
1
5
  // src/start.tsx
2
6
  import { StrictMode } from "react";
3
7
  import { createRoot } from "react-dom/client";
@@ -677,15 +681,30 @@ function formOptions(field) {
677
681
  }
678
682
  return void 0;
679
683
  }
684
+ function formReadonly(field) {
685
+ const raw = field;
686
+ return Boolean(raw.readonly || raw.immutable || raw.computed);
687
+ }
688
+ function formComputed(field) {
689
+ const raw = field;
690
+ if (!raw.computed || typeof raw.computed !== "object" || Array.isArray(raw.computed)) return void 0;
691
+ return raw.computed;
692
+ }
680
693
  function formFieldsFromMeta(fields) {
681
- return fields.filter(isEditableField).map((field) => ({
682
- field: field.name,
683
- label: fieldLabel(field),
684
- kind: field.kind,
685
- required: formRequired(field),
686
- options: formOptions(field),
687
- ref: field.ref
688
- }));
694
+ return fields.filter(isEditableField).map((field) => {
695
+ const computed = formComputed(field);
696
+ const readonly = formReadonly(field);
697
+ return {
698
+ field: field.name,
699
+ label: fieldLabel(field),
700
+ kind: field.kind,
701
+ required: formRequired(field),
702
+ options: formOptions(field),
703
+ ref: field.ref,
704
+ ...readonly ? { readonly: true } : {},
705
+ ...computed ? { computed } : {}
706
+ };
707
+ });
689
708
  }
690
709
  function formatCell(value, kind) {
691
710
  if (value == null || value === "") return "\u2014";
@@ -964,6 +983,264 @@ function DataGrid({
964
983
  import { useEffect as useEffect4, useMemo as useMemo4, useState as useState5 } from "react";
965
984
  import { Box as Box4, Button as Button4, Flex as Flex4, Heading as Heading2, Input as Input3, NativeSelect as NativeSelect2, SimpleGrid, Stack as Stack2, Text as Text5, Textarea } from "@chakra-ui/react";
966
985
 
986
+ // src/domain/domainExpressions.ts
987
+ var DomainExpressionError = class extends Error {
988
+ constructor(message, options = {}) {
989
+ super(message);
990
+ __publicField(this, "code");
991
+ __publicField(this, "field");
992
+ this.name = "DomainExpressionError";
993
+ this.code = options.code ?? "OON_DOMAIN_EXPRESSION_INVALID";
994
+ this.field = options.field;
995
+ }
996
+ };
997
+ var NUMERIC_KINDS = /* @__PURE__ */ new Set(["number", "currency", "currencyConverted"]);
998
+ function hasOwn(object, key) {
999
+ return Object.prototype.hasOwnProperty.call(object, key);
1000
+ }
1001
+ function isEmpty(value) {
1002
+ return value === null || value === void 0 || typeof value === "string" && !value.trim();
1003
+ }
1004
+ function getFieldValue(record, fieldPath) {
1005
+ const parts = String(fieldPath).split(".");
1006
+ let current = record;
1007
+ for (const part of parts) {
1008
+ if (["__proto__", "prototype", "constructor"].includes(part)) return void 0;
1009
+ if (current === null || current === void 0 || typeof current !== "object") return void 0;
1010
+ current = current[part];
1011
+ }
1012
+ return current;
1013
+ }
1014
+ function toNumber(value, context = {}) {
1015
+ if (isEmpty(value)) return context.nullAsZero ? 0 : null;
1016
+ const numeric = typeof value === "number" ? value : Number(value);
1017
+ if (!Number.isFinite(numeric)) {
1018
+ throw new DomainExpressionError(
1019
+ `A f\xF3rmula do campo ${context.field ?? "calculado"} recebeu um valor n\xE3o num\xE9rico.`,
1020
+ { code: "OON_DOMAIN_FORMULA_ERROR", field: context.field }
1021
+ );
1022
+ }
1023
+ return numeric;
1024
+ }
1025
+ function numericArguments(args, record, context) {
1026
+ const values = args.map((arg) => evaluateDomainExpression(arg, record, context));
1027
+ if (!context.nullAsZero && values.some(isEmpty)) return null;
1028
+ return values.map((value) => toNumber(value, context) ?? 0);
1029
+ }
1030
+ function compareValues(leftValue, rightValue, operator) {
1031
+ let left = leftValue;
1032
+ let right = rightValue;
1033
+ if (["gt", "gte", "lt", "lte"].includes(operator)) {
1034
+ const leftNumber = Number(left);
1035
+ const rightNumber = Number(right);
1036
+ if (Number.isFinite(leftNumber) && Number.isFinite(rightNumber)) {
1037
+ left = leftNumber;
1038
+ right = rightNumber;
1039
+ }
1040
+ }
1041
+ switch (operator) {
1042
+ case "eq":
1043
+ return Object.is(left, right);
1044
+ case "neq":
1045
+ return !Object.is(left, right);
1046
+ case "gt":
1047
+ return left > right;
1048
+ case "gte":
1049
+ return left >= right;
1050
+ case "lt":
1051
+ return left < right;
1052
+ case "lte":
1053
+ return left <= right;
1054
+ default:
1055
+ return false;
1056
+ }
1057
+ }
1058
+ function evaluateDomainExpression(expression, record = {}, context = {}) {
1059
+ if (!expression || typeof expression !== "object" || Array.isArray(expression)) {
1060
+ throw new DomainExpressionError("Express\xE3o declarativa inv\xE1lida.", { field: context.field });
1061
+ }
1062
+ if (hasOwn(expression, "value")) return expression.value;
1063
+ if (hasOwn(expression, "field")) return getFieldValue(record, expression.field);
1064
+ const operation = expression;
1065
+ const args = Array.isArray(operation.args) ? operation.args : [];
1066
+ switch (operation.op) {
1067
+ case "add": {
1068
+ const values = numericArguments(args, record, context);
1069
+ return values === null ? null : values.reduce((total, value) => total + value, 0);
1070
+ }
1071
+ case "subtract": {
1072
+ const values = numericArguments(args, record, context);
1073
+ return values === null ? null : values.slice(1).reduce((total, value) => total - value, values[0] ?? 0);
1074
+ }
1075
+ case "multiply": {
1076
+ const values = numericArguments(args, record, context);
1077
+ return values === null ? null : values.reduce((total, value) => total * value, 1);
1078
+ }
1079
+ case "divide": {
1080
+ const values = numericArguments(args, record, context);
1081
+ if (values === null) return null;
1082
+ return values.slice(1).reduce((total, divisor) => {
1083
+ if (divisor === 0) {
1084
+ throw new DomainExpressionError(
1085
+ `A f\xF3rmula do campo ${context.field ?? "calculado"} tentou dividir por zero.`,
1086
+ { code: "OON_DOMAIN_FORMULA_ERROR", field: context.field }
1087
+ );
1088
+ }
1089
+ return total / divisor;
1090
+ }, values[0] ?? 0);
1091
+ }
1092
+ case "min": {
1093
+ const values = numericArguments(args, record, context);
1094
+ return values === null ? null : Math.min(...values);
1095
+ }
1096
+ case "max": {
1097
+ const values = numericArguments(args, record, context);
1098
+ return values === null ? null : Math.max(...values);
1099
+ }
1100
+ case "abs": {
1101
+ const values = numericArguments(args, record, context);
1102
+ return values === null ? null : Math.abs(values[0] ?? 0);
1103
+ }
1104
+ case "negate": {
1105
+ const values = numericArguments(args, record, context);
1106
+ return values === null ? null : -(values[0] ?? 0);
1107
+ }
1108
+ case "coalesce": {
1109
+ for (const arg of args) {
1110
+ const value = evaluateDomainExpression(arg, record, context);
1111
+ if (!isEmpty(value)) return value;
1112
+ }
1113
+ return null;
1114
+ }
1115
+ case "eq":
1116
+ case "neq":
1117
+ case "gt":
1118
+ case "gte":
1119
+ case "lt":
1120
+ case "lte":
1121
+ return compareValues(
1122
+ evaluateDomainExpression(args[0], record, context),
1123
+ evaluateDomainExpression(args[1], record, context),
1124
+ operation.op
1125
+ );
1126
+ case "and":
1127
+ return args.every((arg) => Boolean(evaluateDomainExpression(arg, record, context)));
1128
+ case "or":
1129
+ return args.some((arg) => Boolean(evaluateDomainExpression(arg, record, context)));
1130
+ case "not":
1131
+ return !Boolean(evaluateDomainExpression(args[0], record, context));
1132
+ case "present":
1133
+ return !isEmpty(evaluateDomainExpression(args[0], record, context));
1134
+ case "in": {
1135
+ const value = evaluateDomainExpression(args[0], record, context);
1136
+ const list = evaluateDomainExpression(args[1], record, context);
1137
+ return Array.isArray(list) && list.some((item) => Object.is(item, value));
1138
+ }
1139
+ default:
1140
+ throw new DomainExpressionError(`Operador declarativo n\xE3o suportado: ${String(operation.op)}.`, {
1141
+ field: context.field
1142
+ });
1143
+ }
1144
+ }
1145
+ function roundValue(value, precision = 0) {
1146
+ if (value === null || value === void 0) return value;
1147
+ const numeric = toNumber(value);
1148
+ if (numeric === null) return null;
1149
+ const factor = 10 ** precision;
1150
+ return Math.round((numeric + Number.EPSILON) * factor) / factor;
1151
+ }
1152
+ function expressionFieldReferences(expression, references = /* @__PURE__ */ new Set()) {
1153
+ if (hasOwn(expression, "field")) references.add(expression.field);
1154
+ if (hasOwn(expression, "op")) {
1155
+ for (const argument of expression.args ?? []) {
1156
+ expressionFieldReferences(argument, references);
1157
+ }
1158
+ }
1159
+ return references;
1160
+ }
1161
+ function orderComputedFields(fields) {
1162
+ const computed = fields.filter((field) => field.computed);
1163
+ const computedByName = new Map(computed.map((field) => [field.field, field]));
1164
+ const allNames = new Set(fields.map((field) => field.field));
1165
+ const visiting = /* @__PURE__ */ new Set();
1166
+ const visited = /* @__PURE__ */ new Set();
1167
+ const ordered = [];
1168
+ const visit = (field) => {
1169
+ if (visited.has(field.field)) return;
1170
+ if (visiting.has(field.field)) {
1171
+ throw new DomainExpressionError(`Depend\xEAncia circular em campos calculados envolvendo ${field.field}.`, {
1172
+ field: field.field
1173
+ });
1174
+ }
1175
+ visiting.add(field.field);
1176
+ for (const reference of expressionFieldReferences(field.computed.expression)) {
1177
+ if (!allNames.has(reference)) {
1178
+ throw new DomainExpressionError(`A f\xF3rmula de ${field.field} referencia o campo inexistente ${reference}.`, {
1179
+ field: field.field
1180
+ });
1181
+ }
1182
+ const dependency = computedByName.get(reference);
1183
+ if (dependency) visit(dependency);
1184
+ }
1185
+ visiting.delete(field.field);
1186
+ visited.add(field.field);
1187
+ ordered.push(field);
1188
+ };
1189
+ computed.forEach(visit);
1190
+ return ordered;
1191
+ }
1192
+ function coerceDomainFormValue(value, kind) {
1193
+ if (value === "") return void 0;
1194
+ if (NUMERIC_KINDS.has(kind ?? "")) return Number(value);
1195
+ if (kind === "boolean") return value === "true";
1196
+ return value;
1197
+ }
1198
+ function displayFormValue(value) {
1199
+ if (value === null || value === void 0) return "";
1200
+ return String(value);
1201
+ }
1202
+ function applyReactiveFormulas(fields, currentValues) {
1203
+ const values = { ...currentValues };
1204
+ const errors = {};
1205
+ const record = {};
1206
+ for (const field of fields) {
1207
+ record[field.field] = coerceDomainFormValue(values[field.field] ?? "", field.kind);
1208
+ }
1209
+ let ordered;
1210
+ try {
1211
+ ordered = orderComputedFields(fields);
1212
+ } catch (error) {
1213
+ const issue = error instanceof DomainExpressionError ? error : new DomainExpressionError(String(error));
1214
+ if (issue.field) errors[issue.field] = issue.message;
1215
+ return { values, errors };
1216
+ }
1217
+ for (const field of ordered) {
1218
+ try {
1219
+ const computed = field.computed;
1220
+ const value = evaluateDomainExpression(computed.expression, record, {
1221
+ field: field.field,
1222
+ nullAsZero: computed.nullAsZero !== false
1223
+ });
1224
+ const normalized2 = value === null || value === void 0 ? value : roundValue(value, computed.precision ?? 2);
1225
+ record[field.field] = normalized2;
1226
+ values[field.field] = displayFormValue(normalized2);
1227
+ } catch (error) {
1228
+ const issue = error instanceof DomainExpressionError ? error : new DomainExpressionError(String(error), { field: field.field });
1229
+ errors[field.field] = issue.message;
1230
+ }
1231
+ }
1232
+ return { values, errors };
1233
+ }
1234
+ function buildDomainMutationPayload(fields, values) {
1235
+ const payload = {};
1236
+ for (const field of fields) {
1237
+ if (field.readonly || field.computed) continue;
1238
+ const value = coerceDomainFormValue(values[field.field] ?? "", field.kind);
1239
+ if (value !== void 0) payload[field.field] = value;
1240
+ }
1241
+ return payload;
1242
+ }
1243
+
967
1244
  // src/components/internal/ReferenceSelect.tsx
968
1245
  import { useEffect as useEffect3, useMemo as useMemo3, useRef, useState as useState4 } from "react";
969
1246
  import { Box as Box3, Button as Button3, Flex as Flex3, Input as Input2, Spinner as Spinner2, Stack, Text as Text4 } from "@chakra-ui/react";
@@ -1512,16 +1789,16 @@ function toValue(value, kind) {
1512
1789
  function initialFieldValue(field, value) {
1513
1790
  return normalizeMaskedValue(toValue(value, field.kind), resolveInputMask(field));
1514
1791
  }
1515
- function coerce(value, kind) {
1516
- if (value === "") return void 0;
1517
- if (["number", "currency", "currencyConverted"].includes(kind ?? "")) return Number(value);
1518
- if (kind === "boolean") return value === "true";
1519
- return value;
1792
+ function initialReactiveValues(fields, initialValues) {
1793
+ const initial = Object.fromEntries(fields.map((field) => [field.field, initialFieldValue(field, initialValues?.[field.field])]));
1794
+ return applyReactiveFormulas(fields, initial);
1520
1795
  }
1521
1796
  function validate(fields, values) {
1522
1797
  const errors = {};
1523
1798
  fields.forEach((field) => {
1524
- if (field.required && !(values[field.field] ?? "").trim()) errors[field.field] = `${fieldLabel({ name: field.field, label: field.label })} \xE9 obrigat\xF3rio.`;
1799
+ if (!field.readonly && !field.computed && field.required && !(values[field.field] ?? "").trim()) {
1800
+ errors[field.field] = `${fieldLabel({ name: field.field, label: field.label })} \xE9 obrigat\xF3rio.`;
1801
+ }
1525
1802
  });
1526
1803
  return errors;
1527
1804
  }
@@ -1572,21 +1849,22 @@ function focusField(field) {
1572
1849
  function isMultiline(field) {
1573
1850
  return field.multiline === true || /observa|descricao|mensagem|detalhe|necessidade/i.test(field.field);
1574
1851
  }
1575
- function Label({ id, label, required }) {
1852
+ function Label({ id, label, required, readonly }) {
1576
1853
  return /* @__PURE__ */ jsx9("label", { htmlFor: id, children: /* @__PURE__ */ jsxs5(Text5, { as: "span", display: "block", mb: "6px", color: "#475467", fontSize: "11px", fontWeight: "600", children: [
1577
1854
  label,
1578
- required ? /* @__PURE__ */ jsx9(Text5, { as: "span", color: "#DC2626", children: " *" }) : null
1855
+ required && !readonly ? /* @__PURE__ */ jsx9(Text5, { as: "span", color: "#DC2626", children: " *" }) : null,
1856
+ readonly ? /* @__PURE__ */ jsx9(Text5, { as: "span", ml: "5px", color: "#98A2B3", fontSize: "9px", fontWeight: "500", children: "somente leitura" }) : null
1579
1857
  ] }) });
1580
1858
  }
1581
1859
  function ErrorText({ id, text }) {
1582
1860
  return text ? /* @__PURE__ */ jsx9(Text5, { id, role: "alert", mt: "5px", color: "#DC2626", fontSize: "10px", fontWeight: "500", children: text }) : null;
1583
1861
  }
1584
- var controlProps = (invalid) => ({
1862
+ var controlProps = (invalid, readonly = false) => ({
1585
1863
  minH: "37px",
1586
1864
  px: "10px",
1587
1865
  py: "8px",
1588
1866
  color: "#475467",
1589
- bg: "white",
1867
+ bg: readonly ? "#F8FAFC" : "white",
1590
1868
  borderColor: invalid ? "#DC2626" : "#D5DDE3",
1591
1869
  borderRadius: "6px",
1592
1870
  boxShadow: invalid ? "0 0 0 3px rgba(220,38,38,.08)" : void 0,
@@ -1616,11 +1894,14 @@ function resolveReference(field, values) {
1616
1894
  });
1617
1895
  return { filters, disabled: missing.length > 0 };
1618
1896
  }
1619
- function DynamicForm({ title, eyebrow = "Cadastro", fields, initialValues, submitting, error, onSubmit, onCancel, onFieldChange }) {
1620
- const [values, setValues] = useState5(() => Object.fromEntries(fields.map((field) => [field.field, initialFieldValue(field, initialValues?.[field.field])])));
1897
+ function DynamicForm({ title, eyebrow = "Cadastro", fields: rawFields, initialValues, submitting, error, onSubmit, onCancel, onFieldChange }) {
1898
+ const fields = rawFields;
1899
+ const initial = initialReactiveValues(fields, initialValues);
1900
+ const [values, setValues] = useState5(initial.values);
1901
+ const [formulaErrors, setFormulaErrors] = useState5(initial.errors);
1621
1902
  const [errors, setErrors] = useState5({});
1622
1903
  const [dismissed, setDismissed] = useState5(false);
1623
- const items = useMemo4(() => buildItems(fields), [fields]);
1904
+ const items = useMemo4(() => buildItems(fields), [rawFields]);
1624
1905
  useEffect4(() => {
1625
1906
  const listener = (event) => {
1626
1907
  if (event.key !== "Escape" || event.defaultPrevented || submitting) return;
@@ -1642,8 +1923,9 @@ function DynamicForm({ title, eyebrow = "Cadastro", fields, initialValues, submi
1642
1923
  if (first) focusField(first.field);
1643
1924
  }, [error]);
1644
1925
  const change = (fieldName, value) => {
1645
- const extendedFields = fields;
1646
- const dependentFields = extendedFields.filter(
1926
+ const selected = fields.find((field) => field.field === fieldName);
1927
+ if (selected?.readonly || selected?.computed) return;
1928
+ const dependentFields = fields.filter(
1647
1929
  (candidate) => Object.values(candidate.referenceFilterFrom ?? {}).includes(fieldName)
1648
1930
  );
1649
1931
  setValues((current) => {
@@ -1652,7 +1934,9 @@ function DynamicForm({ title, eyebrow = "Cadastro", fields, initialValues, submi
1652
1934
  if (changed) dependentFields.forEach((candidate) => {
1653
1935
  next[candidate.field] = "";
1654
1936
  });
1655
- return next;
1937
+ const reactive = applyReactiveFormulas(fields, next);
1938
+ setFormulaErrors(reactive.errors);
1939
+ return reactive.values;
1656
1940
  });
1657
1941
  setErrors((current) => {
1658
1942
  const next = { ...current };
@@ -1673,23 +1957,19 @@ function DynamicForm({ title, eyebrow = "Cadastro", fields, initialValues, submi
1673
1957
  if (first) focusField(first.field);
1674
1958
  return;
1675
1959
  }
1676
- const payload = {};
1677
- fields.forEach((field) => {
1678
- const value = coerce(values[field.field] ?? "", field.kind);
1679
- if (value !== void 0) payload[field.field] = value;
1680
- });
1681
1960
  setErrors({});
1682
- onSubmit(payload);
1961
+ onSubmit(buildDomainMutationPayload(fields, values));
1683
1962
  };
1684
1963
  const renderField = (field, grouped = false) => {
1685
1964
  const label = fieldLabel({ name: field.field, label: field.label });
1686
- const message = errors[field.field];
1965
+ const message = errors[field.field] ?? formulaErrors[field.field];
1687
1966
  const id = idFor(field.field);
1688
1967
  const errorId = message ? errorIdFor(field.field) : void 0;
1689
1968
  const multiline = isMultiline(field);
1690
1969
  const span = field.span === 2 || multiline;
1970
+ const readonly = Boolean(field.readonly || field.computed);
1691
1971
  const wrap = (control, showLabel = true) => /* @__PURE__ */ jsxs5(Box4, { minW: grouped ? "150px" : void 0, flex: grouped ? "0 1 auto" : void 0, gridColumn: !grouped && span ? "1 / -1" : void 0, children: [
1692
- showLabel ? /* @__PURE__ */ jsx9(Label, { id, label, required: field.required }) : null,
1972
+ showLabel ? /* @__PURE__ */ jsx9(Label, { id, label, required: field.required, readonly }) : null,
1693
1973
  control,
1694
1974
  /* @__PURE__ */ jsx9(ErrorText, { id: errorId, text: message })
1695
1975
  ] }, field.field);
@@ -1698,89 +1978,46 @@ function DynamicForm({ title, eyebrow = "Cadastro", fields, initialValues, submi
1698
1978
  const uncheckedValue = String(field.uncheckedValue ?? false);
1699
1979
  const checked = (values[field.field] ?? uncheckedValue) === checkedValue;
1700
1980
  return wrap(
1701
- /* @__PURE__ */ jsxs5(
1702
- Box4,
1703
- {
1704
- as: "label",
1705
- display: "flex",
1706
- minH: "37px",
1707
- px: "11px",
1708
- alignItems: "center",
1709
- gap: "8px",
1710
- color: "#344054",
1711
- bg: checked ? "#F0F7FF" : "white",
1712
- borderWidth: "1px",
1713
- borderColor: message ? "#DC2626" : checked ? "#8FC9E7" : "#D5DDE3",
1714
- borderRadius: "7px",
1715
- cursor: "pointer",
1716
- transition: "background .15s ease, border-color .15s ease",
1717
- _hover: { borderColor: "#8FC9E7", bg: "#F7FBFE" },
1718
- children: [
1719
- /* @__PURE__ */ jsx9(
1720
- "input",
1721
- {
1722
- id,
1723
- type: "checkbox",
1724
- checked,
1725
- "aria-invalid": !!message,
1726
- "aria-describedby": errorId,
1727
- style: { width: 16, height: 16, accentColor: "#0474AF", cursor: "pointer" },
1728
- onChange: (event) => change(field.field, event.currentTarget.checked ? checkedValue : uncheckedValue)
1729
- }
1730
- ),
1731
- /* @__PURE__ */ jsxs5(Text5, { as: "span", fontSize: "11px", fontWeight: "600", children: [
1732
- label,
1733
- field.required ? /* @__PURE__ */ jsx9(Text5, { as: "span", color: "#DC2626", children: " *" }) : null
1734
- ] })
1735
- ]
1736
- }
1737
- ),
1981
+ /* @__PURE__ */ jsxs5(Box4, { as: "label", display: "flex", minH: "37px", px: "11px", alignItems: "center", gap: "8px", color: "#344054", bg: readonly ? "#F8FAFC" : checked ? "#F0F7FF" : "white", borderWidth: "1px", borderColor: message ? "#DC2626" : checked ? "#8FC9E7" : "#D5DDE3", borderRadius: "7px", cursor: readonly ? "not-allowed" : "pointer", transition: "background .15s ease, border-color .15s ease", children: [
1982
+ /* @__PURE__ */ jsx9("input", { id, type: "checkbox", checked, disabled: readonly, "aria-invalid": !!message, "aria-describedby": errorId, style: { width: 16, height: 16, accentColor: "#0474AF", cursor: readonly ? "not-allowed" : "pointer" }, onChange: (event) => change(field.field, event.currentTarget.checked ? checkedValue : uncheckedValue) }),
1983
+ /* @__PURE__ */ jsxs5(Text5, { as: "span", fontSize: "11px", fontWeight: "600", children: [
1984
+ label,
1985
+ field.required && !readonly ? /* @__PURE__ */ jsx9(Text5, { as: "span", color: "#DC2626", children: " *" }) : null
1986
+ ] })
1987
+ ] }),
1738
1988
  false
1739
1989
  );
1740
1990
  }
1741
1991
  if (field.kind === "ref" && field.ref) {
1742
1992
  const reference = resolveReference(field, values);
1743
- return wrap(/* @__PURE__ */ jsx9(ReferenceSelect, { field: field.field, inputId: id, errorId, label, refModel: field.ref, value: values[field.field] ?? "", initialValue: initialValues?.[field.field], required: field.required, invalid: !!message, error: message, filters: reference.filters, emptyFields: field.referenceEmptyFields, disabled: reference.disabled, disabledMessage: field.referenceDisabledMessage, onChange: (value2) => change(field.field, value2) }));
1993
+ return wrap(/* @__PURE__ */ jsx9(ReferenceSelect, { field: field.field, inputId: id, errorId, label, refModel: field.ref, value: values[field.field] ?? "", initialValue: initialValues?.[field.field], required: field.required && !readonly, invalid: !!message, error: message, filters: reference.filters, emptyFields: field.referenceEmptyFields, disabled: readonly || reference.disabled, disabledMessage: field.referenceDisabledMessage, onChange: (value2) => change(field.field, value2) }));
1744
1994
  }
1745
- if (field.kind === "enum" && field.options?.length) return wrap(/* @__PURE__ */ jsxs5(NativeSelect2.Root, { size: "sm", children: [
1746
- /* @__PURE__ */ jsxs5(NativeSelect2.Field, { id, "aria-invalid": !!message, "aria-describedby": errorId, value: values[field.field] ?? "", ...controlProps(!!message), onChange: (event) => change(field.field, event.currentTarget.value), children: [
1995
+ if (field.kind === "enum" && field.options?.length) return wrap(/* @__PURE__ */ jsxs5(NativeSelect2.Root, { size: "sm", disabled: readonly, children: [
1996
+ /* @__PURE__ */ jsxs5(NativeSelect2.Field, { id, "aria-invalid": !!message, "aria-describedby": errorId, value: values[field.field] ?? "", ...controlProps(!!message, readonly), onChange: (event) => change(field.field, event.currentTarget.value), children: [
1747
1997
  /* @__PURE__ */ jsx9("option", { value: "", children: "\u2014 Selecione \u2014" }),
1748
1998
  field.options.map((option) => /* @__PURE__ */ jsx9("option", { value: option, children: option }, option))
1749
1999
  ] }),
1750
2000
  /* @__PURE__ */ jsx9(NativeSelect2.Indicator, {})
1751
2001
  ] }));
1752
- if (field.kind === "boolean") return wrap(/* @__PURE__ */ jsxs5(NativeSelect2.Root, { size: "sm", children: [
1753
- /* @__PURE__ */ jsxs5(NativeSelect2.Field, { id, "aria-invalid": !!message, "aria-describedby": errorId, value: values[field.field] ?? "", ...controlProps(!!message), onChange: (event) => change(field.field, event.currentTarget.value), children: [
2002
+ if (field.kind === "boolean") return wrap(/* @__PURE__ */ jsxs5(NativeSelect2.Root, { size: "sm", disabled: readonly, children: [
2003
+ /* @__PURE__ */ jsxs5(NativeSelect2.Field, { id, "aria-invalid": !!message, "aria-describedby": errorId, value: values[field.field] ?? "", ...controlProps(!!message, readonly), onChange: (event) => change(field.field, event.currentTarget.value), children: [
1754
2004
  /* @__PURE__ */ jsx9("option", { value: "", children: "\u2014 Selecione \u2014" }),
1755
2005
  /* @__PURE__ */ jsx9("option", { value: "true", children: "Sim" }),
1756
2006
  /* @__PURE__ */ jsx9("option", { value: "false", children: "N\xE3o" })
1757
2007
  ] }),
1758
2008
  /* @__PURE__ */ jsx9(NativeSelect2.Indicator, {})
1759
2009
  ] }));
1760
- if (multiline) return wrap(/* @__PURE__ */ jsx9(Textarea, { id, rows: field.rows ?? 4, resize: "vertical", "aria-invalid": !!message, "aria-describedby": errorId, value: values[field.field] ?? "", ...controlProps(!!message), lineHeight: "1.5", onChange: (event) => change(field.field, event.currentTarget.value) }));
2010
+ if (multiline) return wrap(/* @__PURE__ */ jsx9(Textarea, { id, rows: field.rows ?? 4, resize: "vertical", readOnly: readonly, "aria-invalid": !!message, "aria-describedby": errorId, value: values[field.field] ?? "", ...controlProps(!!message, readonly), lineHeight: "1.5", onChange: (event) => change(field.field, event.currentTarget.value) }));
1761
2011
  const mask = resolveInputMask(field);
1762
- const type = mask ? "text" : field.kind === "date" ? "date" : field.kind === "number" ? "number" : "text";
2012
+ const numeric = ["number", "currency", "currencyConverted"].includes(field.kind ?? "");
2013
+ const type = mask ? "text" : field.kind === "date" ? "date" : numeric ? "number" : "text";
1763
2014
  const value = values[field.field] ?? "";
1764
2015
  return wrap(
1765
- /* @__PURE__ */ jsx9(
1766
- Input3,
1767
- {
1768
- id,
1769
- type,
1770
- step: type === "number" ? "any" : void 0,
1771
- inputMode: inputModeForMask(mask),
1772
- maxLength: maxLengthForMask(mask),
1773
- autoComplete: "off",
1774
- "aria-invalid": !!message,
1775
- "aria-describedby": errorId,
1776
- value: mask ? formatMaskedValue(value, mask) : value,
1777
- ...controlProps(!!message),
1778
- onChange: (event) => change(field.field, parseMaskedInput(event.currentTarget.value, mask))
1779
- }
1780
- )
2016
+ /* @__PURE__ */ jsx9(Input3, { id, type, step: type === "number" ? "any" : void 0, inputMode: inputModeForMask(mask), maxLength: maxLengthForMask(mask), autoComplete: "off", readOnly: readonly, "aria-invalid": !!message, "aria-describedby": errorId, value: mask ? formatMaskedValue(value, mask) : value, ...controlProps(!!message, readonly), onChange: (event) => change(field.field, parseMaskedInput(event.currentTarget.value, mask)) })
1781
2017
  );
1782
2018
  };
1783
- const invalid = fields.filter((field) => errors[field.field]);
2019
+ const combinedErrors = { ...formulaErrors, ...errors };
2020
+ const invalid = fields.filter((field) => combinedErrors[field.field]);
1784
2021
  const generic = dismissed ? null : messageOf(error);
1785
2022
  return /* @__PURE__ */ jsx9(Flex4, { position: "fixed", inset: 0, zIndex: 1e3, align: "center", justify: "center", p: { base: 2, md: 6 }, bg: "rgba(15,23,42,.48)", backdropFilter: "blur(2px)", onMouseDown: (event) => {
1786
2023
  if (event.currentTarget === event.target && !submitting) onCancel();
@@ -1797,12 +2034,12 @@ function DynamicForm({ title, eyebrow = "Cadastro", fields, initialValues, submi
1797
2034
  invalid.length ? /* @__PURE__ */ jsxs5(Box4, { role: "alert", p: "11px 13px", color: "#9B1C1C", bg: "#FFF5F5", borderWidth: "1px", borderColor: "#FECACA", borderLeftWidth: "4px", borderLeftColor: "#DC2626", borderRadius: "8px", fontSize: "11px", children: [
1798
2035
  /* @__PURE__ */ jsxs5(Flex4, { align: "center", gap: "7px", children: [
1799
2036
  /* @__PURE__ */ jsx9(AlertIcon, {}),
1800
- /* @__PURE__ */ jsx9(Text5, { as: "strong", children: "N\xE3o foi poss\xEDvel salvar. Corrija os campos abaixo:" })
2037
+ /* @__PURE__ */ jsx9(Text5, { as: "strong", children: "Revise os campos abaixo:" })
1801
2038
  ] }),
1802
2039
  /* @__PURE__ */ jsx9(Box4, { as: "ul", mt: "7px", mb: 0, pl: "20px", display: "grid", gap: "3px", children: invalid.map((field) => /* @__PURE__ */ jsx9("li", { children: /* @__PURE__ */ jsxs5(Button4, { type: "button", variant: "plain", minH: "auto", h: "auto", p: 0, color: "#B42318", fontSize: "10px", fontWeight: "400", textDecoration: "underline", onClick: () => focusField(field.field), children: [
1803
2040
  fieldLabel({ name: field.field, label: field.label }),
1804
2041
  ": ",
1805
- errors[field.field]
2042
+ combinedErrors[field.field]
1806
2043
  ] }) }, field.field)) })
1807
2044
  ] }) : null,
1808
2045
  generic && !invalid.length ? /* @__PURE__ */ jsxs5(Flex4, { role: "alert", p: "10px 12px", align: "center", gap: 2, color: "#B42318", bg: "#FEF3F2", borderWidth: "1px", borderColor: "#FECDCA", borderRadius: "7px", fontSize: "11px", children: [
@@ -2397,12 +2634,6 @@ function CoreRelatedGrid({
2397
2634
 
2398
2635
  // src/components/internal/CoreTabbedDetail.tsx
2399
2636
  import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
2400
- function coerce2(value, kind) {
2401
- if (value === "") return void 0;
2402
- if (["number", "currency", "currencyConverted"].includes(kind ?? "")) return Number(value);
2403
- if (kind === "boolean") return value === "true";
2404
- return value;
2405
- }
2406
2637
  function toValue2(value, kind) {
2407
2638
  if (value == null) return "";
2408
2639
  if (kind === "date") {
@@ -2421,6 +2652,10 @@ function toValue2(value, kind) {
2421
2652
  function initialFieldValue2(field, value) {
2422
2653
  return normalizeMaskedValue(toValue2(value, field.kind), resolveInputMask(field));
2423
2654
  }
2655
+ function initialReactiveValues2(fields, parent) {
2656
+ const initial = Object.fromEntries(fields.map((field) => [field.field, initialFieldValue2(field, parent[field.field])]));
2657
+ return applyReactiveFormulas(fields, initial);
2658
+ }
2424
2659
  function isMultiline2(field) {
2425
2660
  return /observa|descricao|mensagem|detalhe|necessidade/i.test(field.field);
2426
2661
  }
@@ -2465,7 +2700,7 @@ function fieldErrorsOf(error) {
2465
2700
  function requiredErrors(fields, values) {
2466
2701
  const errors = {};
2467
2702
  fields.forEach((field) => {
2468
- if (field.required && !(values[field.field] ?? "").trim()) {
2703
+ if (!field.readonly && !field.computed && field.required && !(values[field.field] ?? "").trim()) {
2469
2704
  errors[field.field] = `${fieldLabel({ name: field.field, label: field.label })} \xE9 obrigat\xF3rio.`;
2470
2705
  }
2471
2706
  });
@@ -2483,9 +2718,11 @@ function resolveReference2(field, values) {
2483
2718
  }
2484
2719
  function FieldLabel({ field, htmlFor }) {
2485
2720
  const label = fieldLabel({ name: field.field, label: field.label });
2721
+ const readonly = Boolean(field.readonly || field.computed);
2486
2722
  return /* @__PURE__ */ jsx15("label", { htmlFor, children: /* @__PURE__ */ jsxs10(Text9, { as: "span", display: "block", mb: "5px", color: "#475467", fontSize: "11px", fontWeight: "600", children: [
2487
2723
  label,
2488
- field.required ? /* @__PURE__ */ jsx15(Text9, { as: "span", color: "#DC2626", children: " *" }) : null
2724
+ field.required && !readonly ? /* @__PURE__ */ jsx15(Text9, { as: "span", color: "#DC2626", children: " *" }) : null,
2725
+ readonly ? /* @__PURE__ */ jsx15(Text9, { as: "span", ml: "5px", color: "#98A2B3", fontSize: "9px", fontWeight: "500", children: "somente leitura" }) : null
2489
2726
  ] }) });
2490
2727
  }
2491
2728
  function ErrorText2({ id, message }) {
@@ -2502,11 +2739,13 @@ function FieldControl({
2502
2739
  const label = fieldLabel({ name: field.field, label: field.label });
2503
2740
  const inputId = `oon-detail-field-${field.field.replace(/[^a-zA-Z0-9_-]/g, "-")}`;
2504
2741
  const errorId = `${inputId}-error`;
2742
+ const readonly = Boolean(field.readonly || field.computed);
2505
2743
  const invalidProps = {
2506
2744
  "aria-invalid": error ? true : void 0,
2507
2745
  "aria-describedby": error ? errorId : void 0,
2508
2746
  borderColor: error ? "#DC2626" : void 0,
2509
- boxShadow: error ? "0 0 0 3px rgba(220,38,38,.08)" : void 0
2747
+ boxShadow: error ? "0 0 0 3px rgba(220,38,38,.08)" : void 0,
2748
+ bg: readonly ? "#F8FAFC" : void 0
2510
2749
  };
2511
2750
  if (field.widget === "checkbox") {
2512
2751
  const checkedValue = String(field.checkedValue ?? true);
@@ -2523,11 +2762,11 @@ function FieldControl({
2523
2762
  alignItems: "center",
2524
2763
  gap: "8px",
2525
2764
  color: "#344054",
2526
- bg: checked ? "#F0F7FF" : "white",
2765
+ bg: readonly ? "#F8FAFC" : checked ? "#F0F7FF" : "white",
2527
2766
  borderWidth: "1px",
2528
2767
  borderColor: error ? "#DC2626" : checked ? "#8FC9E7" : "#D5DDE3",
2529
2768
  borderRadius: "7px",
2530
- cursor: "pointer",
2769
+ cursor: readonly ? "not-allowed" : "pointer",
2531
2770
  children: [
2532
2771
  /* @__PURE__ */ jsx15(
2533
2772
  "input",
@@ -2535,9 +2774,10 @@ function FieldControl({
2535
2774
  id: inputId,
2536
2775
  type: "checkbox",
2537
2776
  checked,
2777
+ disabled: readonly,
2538
2778
  "aria-invalid": error ? true : void 0,
2539
2779
  "aria-describedby": error ? errorId : void 0,
2540
- style: { width: 16, height: 16, accentColor: "#0474AF", cursor: "pointer" },
2780
+ style: { width: 16, height: 16, accentColor: "#0474AF", cursor: readonly ? "not-allowed" : "pointer" },
2541
2781
  onChange: (event) => onChange(event.currentTarget.checked ? checkedValue : uncheckedValue)
2542
2782
  }
2543
2783
  ),
@@ -2562,12 +2802,12 @@ function FieldControl({
2562
2802
  refModel: field.ref,
2563
2803
  value,
2564
2804
  initialValue,
2565
- required: field.required,
2805
+ required: field.required && !readonly,
2566
2806
  invalid: !!error,
2567
2807
  error,
2568
2808
  filters: reference.filters,
2569
2809
  emptyFields: field.referenceEmptyFields,
2570
- disabled: reference.disabled,
2810
+ disabled: readonly || reference.disabled,
2571
2811
  disabledMessage: field.referenceDisabledMessage,
2572
2812
  onChange
2573
2813
  }
@@ -2575,28 +2815,29 @@ function FieldControl({
2575
2815
  ] });
2576
2816
  }
2577
2817
  const mask = resolveInputMask(field);
2578
- const control = field.kind === "enum" && field.options?.length ? /* @__PURE__ */ jsxs10(NativeSelect4.Root, { size: "sm", children: [
2818
+ const control = field.kind === "enum" && field.options?.length ? /* @__PURE__ */ jsxs10(NativeSelect4.Root, { size: "sm", disabled: readonly, children: [
2579
2819
  /* @__PURE__ */ jsxs10(NativeSelect4.Field, { id: inputId, value, ...invalidProps, onChange: (event) => onChange(event.currentTarget.value), children: [
2580
2820
  /* @__PURE__ */ jsx15("option", { value: "", children: "\u2014 Selecione \u2014" }),
2581
2821
  field.options.map((option) => /* @__PURE__ */ jsx15("option", { value: option, children: option }, option))
2582
2822
  ] }),
2583
2823
  /* @__PURE__ */ jsx15(NativeSelect4.Indicator, {})
2584
- ] }) : field.kind === "boolean" ? /* @__PURE__ */ jsxs10(NativeSelect4.Root, { size: "sm", children: [
2824
+ ] }) : field.kind === "boolean" ? /* @__PURE__ */ jsxs10(NativeSelect4.Root, { size: "sm", disabled: readonly, children: [
2585
2825
  /* @__PURE__ */ jsxs10(NativeSelect4.Field, { id: inputId, value, ...invalidProps, onChange: (event) => onChange(event.currentTarget.value), children: [
2586
2826
  /* @__PURE__ */ jsx15("option", { value: "", children: "\u2014 Selecione \u2014" }),
2587
2827
  /* @__PURE__ */ jsx15("option", { value: "true", children: "Sim" }),
2588
2828
  /* @__PURE__ */ jsx15("option", { value: "false", children: "N\xE3o" })
2589
2829
  ] }),
2590
2830
  /* @__PURE__ */ jsx15(NativeSelect4.Indicator, {})
2591
- ] }) : isMultiline2(field) ? /* @__PURE__ */ jsx15(Textarea3, { id: inputId, rows: 4, resize: "vertical", value, ...invalidProps, onChange: (event) => onChange(event.currentTarget.value) }) : /* @__PURE__ */ jsx15(
2831
+ ] }) : isMultiline2(field) ? /* @__PURE__ */ jsx15(Textarea3, { id: inputId, rows: 4, resize: "vertical", readOnly: readonly, value, ...invalidProps, onChange: (event) => onChange(event.currentTarget.value) }) : /* @__PURE__ */ jsx15(
2592
2832
  Input5,
2593
2833
  {
2594
2834
  id: inputId,
2595
2835
  size: "sm",
2596
- type: mask ? "text" : field.kind === "date" ? "date" : field.kind === "number" ? "number" : "text",
2597
- step: !mask && field.kind === "number" ? "any" : void 0,
2836
+ type: mask ? "text" : field.kind === "date" ? "date" : ["number", "currency", "currencyConverted"].includes(field.kind ?? "") ? "number" : "text",
2837
+ step: !mask && ["number", "currency", "currencyConverted"].includes(field.kind ?? "") ? "any" : void 0,
2598
2838
  inputMode: inputModeForMask(mask),
2599
2839
  maxLength: maxLengthForMask(mask),
2840
+ readOnly: readonly,
2600
2841
  value: mask ? formatMaskedValue(value, mask) : value,
2601
2842
  ...invalidProps,
2602
2843
  onChange: (event) => onChange(parseMaskedInput(event.currentTarget.value, mask))
@@ -2645,17 +2886,23 @@ function InlineParentForm({
2645
2886
  externalSubmit = false
2646
2887
  }) {
2647
2888
  const fields = rawFields;
2648
- const [values, setValues] = useState8({});
2889
+ const initial = initialReactiveValues2(fields, parent);
2890
+ const [values, setValues] = useState8(initial.values);
2891
+ const [formulaErrors, setFormulaErrors] = useState8(initial.errors);
2649
2892
  const [saving, setSaving] = useState8(false);
2650
2893
  const [error, setError] = useState8(null);
2651
2894
  const [fieldErrors, setFieldErrors] = useState8({});
2652
2895
  useEffect5(() => {
2653
- setValues(Object.fromEntries(fields.map((field) => [field.field, initialFieldValue2(field, parent[field.field])])));
2896
+ const next = initialReactiveValues2(fields, parent);
2897
+ setValues(next.values);
2898
+ setFormulaErrors(next.errors);
2654
2899
  setError(null);
2655
2900
  setFieldErrors({});
2656
2901
  }, [parent, rawFields]);
2657
2902
  const fieldsByName = useMemo6(() => new Map(fields.map((field) => [field.field, field])), [rawFields]);
2658
2903
  const change = (fieldName, value) => {
2904
+ const selected = fieldsByName.get(fieldName);
2905
+ if (selected?.readonly || selected?.computed) return;
2659
2906
  const dependentFields = fields.filter(
2660
2907
  (candidate) => Object.values(candidate.referenceFilterFrom ?? {}).includes(fieldName)
2661
2908
  );
@@ -2665,7 +2912,9 @@ function InlineParentForm({
2665
2912
  if (changed) dependentFields.forEach((candidate) => {
2666
2913
  next[candidate.field] = "";
2667
2914
  });
2668
- return next;
2915
+ const reactive = applyReactiveFormulas(fields, next);
2916
+ setFormulaErrors(reactive.errors);
2917
+ return reactive.values;
2669
2918
  });
2670
2919
  setFieldErrors((current) => {
2671
2920
  const next = { ...current };
@@ -2675,6 +2924,7 @@ function InlineParentForm({
2675
2924
  });
2676
2925
  setError(null);
2677
2926
  };
2927
+ const combinedErrors = { ...formulaErrors, ...fieldErrors };
2678
2928
  const renderFields = () => {
2679
2929
  if (groups?.length) {
2680
2930
  return /* @__PURE__ */ jsx15(Stack3, { gap: "14px", children: groups.map((group) => /* @__PURE__ */ jsx15(
@@ -2684,7 +2934,7 @@ function InlineParentForm({
2684
2934
  fields: group.fields.map((field) => fieldsByName.get(field)).filter(Boolean),
2685
2935
  values,
2686
2936
  initialValues: parent,
2687
- errors: fieldErrors,
2937
+ errors: combinedErrors,
2688
2938
  onChange: change
2689
2939
  },
2690
2940
  `${group.label}-${group.fields.join("-")}`
@@ -2697,7 +2947,7 @@ function InlineParentForm({
2697
2947
  value: values[field.field] ?? "",
2698
2948
  values,
2699
2949
  initialValue: parent[field.field],
2700
- error: fieldErrors[field.field],
2950
+ error: combinedErrors[field.field],
2701
2951
  onChange: (value) => change(field.field, value)
2702
2952
  },
2703
2953
  field.field
@@ -2720,9 +2970,7 @@ function InlineParentForm({
2720
2970
  setError(null);
2721
2971
  setFieldErrors({});
2722
2972
  try {
2723
- const payload = Object.fromEntries(
2724
- fields.map((field) => [field.field, coerce2(values[field.field] ?? "", field.kind)]).filter(([, value]) => value !== void 0)
2725
- );
2973
+ const payload = buildDomainMutationPayload(fields, values);
2726
2974
  const saved = parent._id ? await resource.update(String(parent._id), payload) : await resource.create(payload);
2727
2975
  onSaved(saved);
2728
2976
  } catch (requestError) {
@@ -5339,12 +5587,17 @@ export {
5339
5587
  CorePipeline,
5340
5588
  CoreRelationField,
5341
5589
  CoreToolbar,
5590
+ DomainExpressionError,
5342
5591
  PermissionGate,
5592
+ applyReactiveFormulas,
5593
+ buildDomainMutationPayload,
5594
+ coerceDomainFormValue,
5343
5595
  defineCollectionView,
5344
5596
  defineDashboard,
5345
5597
  defineDocumentView,
5346
5598
  defineOonModule,
5347
5599
  definePipelineView,
5600
+ evaluateDomainExpression,
5348
5601
  manifestToConfig,
5349
5602
  oonCoreFront,
5350
5603
  start,