@oondemand/oon-core-front 0.3.27 → 0.3.29

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
@@ -28,11 +28,12 @@ function normalizeError(err) {
28
28
  };
29
29
  }
30
30
  const envelope = data?.error;
31
+ const legacyDetails = envelope && envelope.details === void 0 && envelope.field ? envelope : void 0;
31
32
  return {
32
33
  code: envelope?.code || codeFromStatus(status),
33
34
  message: envelope?.message || data?.message || err.message || "Erro inesperado.",
34
35
  status,
35
- details: envelope?.details,
36
+ details: envelope?.details ?? legacyDetails,
36
37
  requestId: envelope?.requestId || err.response?.headers?.["x-request-id"]
37
38
  };
38
39
  }
@@ -1035,6 +1036,11 @@ function ChevronIcon2({ open }) {
1035
1036
  function ClearIcon() {
1036
1037
  return /* @__PURE__ */ jsx8("svg", { viewBox: "0 0 20 20", width: "14", height: "14", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("path", { d: "m6 6 8 8M14 6l-8 8", fill: "none", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round" }) });
1037
1038
  }
1039
+ function cleanFilters(filters) {
1040
+ return Object.fromEntries(
1041
+ Object.entries(filters ?? {}).filter(([, value]) => value !== void 0 && value !== null && value !== "")
1042
+ );
1043
+ }
1038
1044
  function ReferenceSelect({
1039
1045
  field,
1040
1046
  inputId,
@@ -1046,9 +1052,16 @@ function ReferenceSelect({
1046
1052
  required,
1047
1053
  invalid,
1048
1054
  error,
1055
+ filters,
1056
+ emptyFields = [],
1057
+ disabled = false,
1058
+ disabledMessage,
1049
1059
  onChange
1050
1060
  }) {
1051
1061
  const initialLabel = useMemo3(() => initialReferenceLabel(initialValue), [initialValue]);
1062
+ const resolvedFilters = useMemo3(() => cleanFilters(filters), [filters]);
1063
+ const filtersKey = useMemo3(() => JSON.stringify(resolvedFilters), [resolvedFilters]);
1064
+ const emptyFieldsKey = useMemo3(() => JSON.stringify(emptyFields), [emptyFields]);
1052
1065
  const [selectedLabel, setSelectedLabel] = useState4(initialLabel);
1053
1066
  const [search, setSearch] = useState4("");
1054
1067
  const [debouncedSearch, setDebouncedSearch] = useState4("");
@@ -1069,6 +1082,11 @@ function ReferenceSelect({
1069
1082
  if (!value) setSelectedLabel("");
1070
1083
  else if (initialLabel) setSelectedLabel(initialLabel);
1071
1084
  }, [initialLabel, value]);
1085
+ useEffect3(() => {
1086
+ if (!disabled) return;
1087
+ setOpen(false);
1088
+ setActiveIndex(-1);
1089
+ }, [disabled]);
1072
1090
  useEffect3(() => {
1073
1091
  if (!open) return;
1074
1092
  const onPointerDown = (event) => {
@@ -1085,9 +1103,14 @@ function ReferenceSelect({
1085
1103
  window.setTimeout(() => searchRef.current?.focus(), 0);
1086
1104
  }, [open]);
1087
1105
  const listQuery = useQuery3({
1088
- queryKey: ["oon", "reference-options", refModel, basePath, debouncedSearch],
1089
- queryFn: () => resource.list({ pageIndex: 0, pageSize: 30, searchTerm: debouncedSearch || void 0 }),
1090
- enabled: !!schema && open,
1106
+ queryKey: ["oon", "reference-options", refModel, basePath, debouncedSearch, filtersKey, emptyFieldsKey],
1107
+ queryFn: () => resource.list({
1108
+ pageIndex: 0,
1109
+ pageSize: emptyFields.length ? 200 : 30,
1110
+ searchTerm: debouncedSearch || void 0,
1111
+ ...resolvedFilters
1112
+ }),
1113
+ enabled: !!schema && open && !disabled,
1091
1114
  staleTime: 3e4
1092
1115
  });
1093
1116
  const currentQuery = useQuery3({
@@ -1100,11 +1123,17 @@ function ReferenceSelect({
1100
1123
  if (!currentQuery.data) return;
1101
1124
  setSelectedLabel(referenceOptionLabel(currentQuery.data, schema?.searchable));
1102
1125
  }, [currentQuery.data, schema?.searchable]);
1103
- const items = listQuery.data?.results ?? [];
1126
+ const items = (listQuery.data?.results ?? []).filter(
1127
+ (record) => emptyFields.every((fieldName) => {
1128
+ const candidate = record[fieldName];
1129
+ return candidate === void 0 || candidate === null || candidate === "";
1130
+ })
1131
+ );
1104
1132
  const listId = `oon-reference-${field}`;
1105
1133
  const resolvedInputId = inputId ?? `oon-reference-input-${field}`;
1106
1134
  const activeOptionId = activeIndex >= 0 ? `${listId}-option-${activeIndex}` : void 0;
1107
1135
  const openList = (seed = "") => {
1136
+ if (disabled) return;
1108
1137
  if (seed) setSearch(seed);
1109
1138
  setOpen(true);
1110
1139
  setActiveIndex((current) => current >= 0 ? current : 0);
@@ -1139,6 +1168,7 @@ function ReferenceSelect({
1139
1168
  return next;
1140
1169
  });
1141
1170
  };
1171
+ const placeholder = disabled ? disabledMessage ?? "Selecione primeiro o campo relacionado." : "Selecione ou digite para pesquisar...";
1142
1172
  return /* @__PURE__ */ jsxs4(Box3, { ref: rootRef, position: "relative", children: [
1143
1173
  /* @__PURE__ */ jsxs4(
1144
1174
  Button3,
@@ -1161,6 +1191,7 @@ function ReferenceSelect({
1161
1191
  fontSize: "11px",
1162
1192
  fontWeight: "400",
1163
1193
  textAlign: "left",
1194
+ disabled,
1164
1195
  "aria-haspopup": "listbox",
1165
1196
  "aria-expanded": open,
1166
1197
  "aria-controls": listId,
@@ -1170,6 +1201,7 @@ function ReferenceSelect({
1170
1201
  "aria-describedby": error ? errorId : void 0,
1171
1202
  onClick: () => open ? closeList() : openList(),
1172
1203
  onKeyDown: (event) => {
1204
+ if (disabled) return;
1173
1205
  if (event.key === "ArrowDown") {
1174
1206
  event.preventDefault();
1175
1207
  if (!open) openList();
@@ -1200,10 +1232,11 @@ function ReferenceSelect({
1200
1232
  },
1201
1233
  _hover: { bg: "white", borderColor: "#B9C7D1" },
1202
1234
  _focusVisible: { borderColor: invalid ? "#DC2626" : "#0F8DD0", boxShadow: invalid ? "0 0 0 3px rgba(220,38,38,.08)" : "0 0 0 3px rgba(15,141,208,.10)" },
1235
+ _disabled: { cursor: "not-allowed", color: "#98A2B3", bg: "#F8FAFC", opacity: 1 },
1203
1236
  children: [
1204
- /* @__PURE__ */ jsx8(Text4, { as: "span", minW: 0, flex: "1", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", color: selectedLabel || value ? "#475467" : "#98A2B3", children: selectedLabel || (value ? String(value) : "Selecione ou digite para pesquisar...") }),
1237
+ /* @__PURE__ */ jsx8(Text4, { as: "span", minW: 0, flex: "1", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", color: selectedLabel || value ? "#475467" : "#98A2B3", children: selectedLabel || (value ? String(value) : placeholder) }),
1205
1238
  /* @__PURE__ */ jsxs4(Flex3, { as: "span", align: "center", gap: 1, flex: "0 0 auto", color: "#98A2B3", children: [
1206
- value ? /* @__PURE__ */ jsx8(
1239
+ value && !disabled ? /* @__PURE__ */ jsx8(
1207
1240
  Box3,
1208
1241
  {
1209
1242
  as: "span",
@@ -1331,7 +1364,8 @@ function ReferenceSelect({
1331
1364
  }) })
1332
1365
  ]
1333
1366
  }
1334
- ) : null
1367
+ ) : null,
1368
+ error ? /* @__PURE__ */ jsx8(Text4, { id: errorId, role: "alert", mt: "5px", color: "#DC2626", fontSize: "10px", fontWeight: "500", children: error }) : null
1335
1369
  ] });
1336
1370
  }
1337
1371
 
@@ -1464,6 +1498,16 @@ function buildItems(fields) {
1464
1498
  });
1465
1499
  return items;
1466
1500
  }
1501
+ function resolveReference(field, values) {
1502
+ const filters = { ...field.referenceFilters ?? {} };
1503
+ const missing = [];
1504
+ Object.entries(field.referenceFilterFrom ?? {}).forEach(([queryField, formField]) => {
1505
+ const value = values[formField];
1506
+ if (value) filters[queryField] = value;
1507
+ else missing.push(formField);
1508
+ });
1509
+ return { filters, disabled: missing.length > 0 };
1510
+ }
1467
1511
  function DynamicForm({ title, eyebrow = "Cadastro", fields, initialValues, submitting, error, onSubmit, onCancel, onFieldChange }) {
1468
1512
  const [values, setValues] = useState5(() => Object.fromEntries(fields.map((field) => [field.field, toValue(initialValues?.[field.field], field.kind)])));
1469
1513
  const [errors, setErrors] = useState5({});
@@ -1489,12 +1533,23 @@ function DynamicForm({ title, eyebrow = "Cadastro", fields, initialValues, submi
1489
1533
  const first = fields.find((field) => next[field.field]);
1490
1534
  if (first) focusField(first.field);
1491
1535
  }, [error]);
1492
- const change = (field, value) => {
1493
- setValues((current) => ({ ...current, [field]: value }));
1536
+ const change = (fieldName, value) => {
1537
+ const extendedFields = fields;
1538
+ const dependentFields = extendedFields.filter(
1539
+ (candidate) => Object.values(candidate.referenceFilterFrom ?? {}).includes(fieldName)
1540
+ );
1541
+ setValues((current) => {
1542
+ const changed = current[fieldName] !== value;
1543
+ const next = { ...current, [fieldName]: value };
1544
+ if (changed) dependentFields.forEach((candidate) => {
1545
+ next[candidate.field] = "";
1546
+ });
1547
+ return next;
1548
+ });
1494
1549
  setErrors((current) => {
1495
- if (!current[field]) return current;
1496
1550
  const next = { ...current };
1497
- delete next[field];
1551
+ delete next[fieldName];
1552
+ dependentFields.forEach((candidate) => delete next[candidate.field]);
1498
1553
  return next;
1499
1554
  });
1500
1555
  setDismissed(true);
@@ -1575,7 +1630,10 @@ function DynamicForm({ title, eyebrow = "Cadastro", fields, initialValues, submi
1575
1630
  false
1576
1631
  );
1577
1632
  }
1578
- if (field.kind === "ref" && field.ref) 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, onChange: (value) => change(field.field, value) }));
1633
+ if (field.kind === "ref" && field.ref) {
1634
+ const reference = resolveReference(field, values);
1635
+ 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: (value) => change(field.field, value) }));
1636
+ }
1579
1637
  if (field.kind === "enum" && field.options?.length) return wrap(/* @__PURE__ */ jsxs5(NativeSelect2.Root, { size: "sm", children: [
1580
1638
  /* @__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: [
1581
1639
  /* @__PURE__ */ jsx9("option", { value: "", children: "\u2014 Selecione \u2014" }),
@@ -1905,6 +1963,63 @@ function toValue2(value, kind) {
1905
1963
  function isMultiline2(field) {
1906
1964
  return /observa|descricao|mensagem|detalhe|necessidade/i.test(field.field);
1907
1965
  }
1966
+ function messageOf3(error) {
1967
+ if (!error) return null;
1968
+ if (typeof error === "string") return error;
1969
+ if (error instanceof Error) return error.message;
1970
+ if (typeof error === "object") {
1971
+ const record = error;
1972
+ if (typeof record.message === "string") return record.message;
1973
+ if (record.error && typeof record.error === "object") {
1974
+ const nested = record.error;
1975
+ if (typeof nested.message === "string") return nested.message;
1976
+ }
1977
+ }
1978
+ return "N\xE3o foi poss\xEDvel salvar o registro.";
1979
+ }
1980
+ function fieldErrorsOf(error) {
1981
+ if (!error || typeof error !== "object") return {};
1982
+ const record = error;
1983
+ const envelope = record.error && typeof record.error === "object" ? record.error : void 0;
1984
+ const details = record.details ?? envelope?.details ?? envelope;
1985
+ const result = {};
1986
+ const visit = (value) => {
1987
+ if (!value) return;
1988
+ if (Array.isArray(value)) {
1989
+ value.forEach(visit);
1990
+ return;
1991
+ }
1992
+ if (typeof value !== "object") return;
1993
+ const item = value;
1994
+ if (typeof item.field === "string" && typeof item.message === "string") {
1995
+ result[item.field] = item.message;
1996
+ }
1997
+ Object.values(item).forEach((nested) => {
1998
+ if (nested !== item.field && nested !== item.message) visit(nested);
1999
+ });
2000
+ };
2001
+ visit(details);
2002
+ return result;
2003
+ }
2004
+ function requiredErrors(fields, values) {
2005
+ const errors = {};
2006
+ fields.forEach((field) => {
2007
+ if (field.required && !(values[field.field] ?? "").trim()) {
2008
+ errors[field.field] = `${fieldLabel({ name: field.field, label: field.label })} \xE9 obrigat\xF3rio.`;
2009
+ }
2010
+ });
2011
+ return errors;
2012
+ }
2013
+ function resolveReference2(field, values) {
2014
+ const filters = { ...field.referenceFilters ?? {} };
2015
+ const missing = [];
2016
+ Object.entries(field.referenceFilterFrom ?? {}).forEach(([queryField, formField]) => {
2017
+ const value = values[formField];
2018
+ if (value) filters[queryField] = value;
2019
+ else missing.push(formField);
2020
+ });
2021
+ return { filters, disabled: missing.length > 0 };
2022
+ }
1908
2023
  function FieldLabel({ field, htmlFor }) {
1909
2024
  const label = fieldLabel({ name: field.field, label: field.label });
1910
2025
  return /* @__PURE__ */ jsx14("label", { htmlFor, children: /* @__PURE__ */ jsxs10(Text9, { as: "span", display: "block", mb: "5px", color: "#475467", fontSize: "11px", fontWeight: "600", children: [
@@ -1912,48 +2027,68 @@ function FieldLabel({ field, htmlFor }) {
1912
2027
  field.required ? /* @__PURE__ */ jsx14(Text9, { as: "span", color: "#DC2626", children: " *" }) : null
1913
2028
  ] }) });
1914
2029
  }
2030
+ function ErrorText2({ id, message }) {
2031
+ return message ? /* @__PURE__ */ jsx14(Text9, { id, role: "alert", mt: "5px", color: "#DC2626", fontSize: "10px", fontWeight: "500", children: message }) : null;
2032
+ }
1915
2033
  function FieldControl({
1916
2034
  field,
1917
2035
  value,
2036
+ values,
2037
+ initialValue,
2038
+ error,
1918
2039
  onChange
1919
2040
  }) {
1920
2041
  const label = fieldLabel({ name: field.field, label: field.label });
1921
2042
  const inputId = `oon-detail-field-${field.field.replace(/[^a-zA-Z0-9_-]/g, "-")}`;
2043
+ const errorId = `${inputId}-error`;
2044
+ const invalidProps = {
2045
+ "aria-invalid": error ? true : void 0,
2046
+ "aria-describedby": error ? errorId : void 0,
2047
+ borderColor: error ? "#DC2626" : void 0,
2048
+ boxShadow: error ? "0 0 0 3px rgba(220,38,38,.08)" : void 0
2049
+ };
1922
2050
  if (field.widget === "checkbox") {
1923
2051
  const checkedValue = String(field.checkedValue ?? true);
1924
2052
  const uncheckedValue = String(field.uncheckedValue ?? false);
1925
2053
  const checked = (value || uncheckedValue) === checkedValue;
1926
- return /* @__PURE__ */ jsxs10(
1927
- Box7,
1928
- {
1929
- as: "label",
1930
- display: "flex",
1931
- minH: "38px",
1932
- px: "11px",
1933
- alignItems: "center",
1934
- gap: "8px",
1935
- color: "#344054",
1936
- bg: checked ? "#F0F7FF" : "white",
1937
- borderWidth: "1px",
1938
- borderColor: checked ? "#8FC9E7" : "#D5DDE3",
1939
- borderRadius: "7px",
1940
- cursor: "pointer",
1941
- children: [
1942
- /* @__PURE__ */ jsx14(
1943
- "input",
1944
- {
1945
- type: "checkbox",
1946
- checked,
1947
- style: { width: 16, height: 16, accentColor: "#0474AF", cursor: "pointer" },
1948
- onChange: (event) => onChange(event.currentTarget.checked ? checkedValue : uncheckedValue)
1949
- }
1950
- ),
1951
- /* @__PURE__ */ jsx14(Text9, { as: "span", fontSize: "11px", fontWeight: "600", children: label })
1952
- ]
1953
- }
1954
- );
2054
+ return /* @__PURE__ */ jsxs10(Box7, { children: [
2055
+ /* @__PURE__ */ jsxs10(
2056
+ Box7,
2057
+ {
2058
+ as: "label",
2059
+ display: "flex",
2060
+ minH: "38px",
2061
+ px: "11px",
2062
+ alignItems: "center",
2063
+ gap: "8px",
2064
+ color: "#344054",
2065
+ bg: checked ? "#F0F7FF" : "white",
2066
+ borderWidth: "1px",
2067
+ borderColor: error ? "#DC2626" : checked ? "#8FC9E7" : "#D5DDE3",
2068
+ borderRadius: "7px",
2069
+ cursor: "pointer",
2070
+ children: [
2071
+ /* @__PURE__ */ jsx14(
2072
+ "input",
2073
+ {
2074
+ id: inputId,
2075
+ type: "checkbox",
2076
+ checked,
2077
+ "aria-invalid": error ? true : void 0,
2078
+ "aria-describedby": error ? errorId : void 0,
2079
+ style: { width: 16, height: 16, accentColor: "#0474AF", cursor: "pointer" },
2080
+ onChange: (event) => onChange(event.currentTarget.checked ? checkedValue : uncheckedValue)
2081
+ }
2082
+ ),
2083
+ /* @__PURE__ */ jsx14(Text9, { as: "span", fontSize: "11px", fontWeight: "600", children: label })
2084
+ ]
2085
+ }
2086
+ ),
2087
+ /* @__PURE__ */ jsx14(ErrorText2, { id: errorId, message: error })
2088
+ ] });
1955
2089
  }
1956
2090
  if (field.kind === "ref" && field.ref) {
2091
+ const reference = resolveReference2(field, values);
1957
2092
  return /* @__PURE__ */ jsxs10(Box7, { children: [
1958
2093
  /* @__PURE__ */ jsx14(FieldLabel, { field, htmlFor: inputId }),
1959
2094
  /* @__PURE__ */ jsx14(
@@ -1961,48 +2096,59 @@ function FieldControl({
1961
2096
  {
1962
2097
  field: field.field,
1963
2098
  inputId,
2099
+ errorId,
1964
2100
  label,
1965
2101
  refModel: field.ref,
1966
2102
  value,
1967
- initialValue: value,
2103
+ initialValue,
1968
2104
  required: field.required,
2105
+ invalid: !!error,
2106
+ error,
2107
+ filters: reference.filters,
2108
+ emptyFields: field.referenceEmptyFields,
2109
+ disabled: reference.disabled,
2110
+ disabledMessage: field.referenceDisabledMessage,
1969
2111
  onChange
1970
2112
  }
1971
2113
  )
1972
2114
  ] });
1973
2115
  }
1974
2116
  const control = field.kind === "enum" && field.options?.length ? /* @__PURE__ */ jsxs10(NativeSelect4.Root, { size: "sm", children: [
1975
- /* @__PURE__ */ jsxs10(NativeSelect4.Field, { id: inputId, value, onChange: (event) => onChange(event.currentTarget.value), children: [
2117
+ /* @__PURE__ */ jsxs10(NativeSelect4.Field, { id: inputId, value, ...invalidProps, onChange: (event) => onChange(event.currentTarget.value), children: [
1976
2118
  /* @__PURE__ */ jsx14("option", { value: "", children: "\u2014 Selecione \u2014" }),
1977
2119
  field.options.map((option) => /* @__PURE__ */ jsx14("option", { value: option, children: option }, option))
1978
2120
  ] }),
1979
2121
  /* @__PURE__ */ jsx14(NativeSelect4.Indicator, {})
1980
2122
  ] }) : field.kind === "boolean" ? /* @__PURE__ */ jsxs10(NativeSelect4.Root, { size: "sm", children: [
1981
- /* @__PURE__ */ jsxs10(NativeSelect4.Field, { id: inputId, value, onChange: (event) => onChange(event.currentTarget.value), children: [
2123
+ /* @__PURE__ */ jsxs10(NativeSelect4.Field, { id: inputId, value, ...invalidProps, onChange: (event) => onChange(event.currentTarget.value), children: [
1982
2124
  /* @__PURE__ */ jsx14("option", { value: "", children: "\u2014 Selecione \u2014" }),
1983
2125
  /* @__PURE__ */ jsx14("option", { value: "true", children: "Sim" }),
1984
2126
  /* @__PURE__ */ jsx14("option", { value: "false", children: "N\xE3o" })
1985
2127
  ] }),
1986
2128
  /* @__PURE__ */ jsx14(NativeSelect4.Indicator, {})
1987
- ] }) : isMultiline2(field) ? /* @__PURE__ */ jsx14(Textarea3, { id: inputId, rows: 4, resize: "vertical", value, onChange: (event) => onChange(event.currentTarget.value) }) : /* @__PURE__ */ jsx14(
2129
+ ] }) : isMultiline2(field) ? /* @__PURE__ */ jsx14(Textarea3, { id: inputId, rows: 4, resize: "vertical", value, ...invalidProps, onChange: (event) => onChange(event.currentTarget.value) }) : /* @__PURE__ */ jsx14(
1988
2130
  Input5,
1989
2131
  {
1990
2132
  id: inputId,
1991
2133
  size: "sm",
1992
2134
  type: field.kind === "date" ? "date" : ["number", "currency", "currencyConverted"].includes(field.kind ?? "") ? "number" : "text",
1993
2135
  value,
2136
+ ...invalidProps,
1994
2137
  onChange: (event) => onChange(event.currentTarget.value)
1995
2138
  }
1996
2139
  );
1997
2140
  return /* @__PURE__ */ jsxs10(Box7, { children: [
1998
2141
  /* @__PURE__ */ jsx14(FieldLabel, { field, htmlFor: inputId }),
1999
- control
2142
+ control,
2143
+ /* @__PURE__ */ jsx14(ErrorText2, { id: errorId, message: error })
2000
2144
  ] });
2001
2145
  }
2002
2146
  function GroupSection({
2003
2147
  group,
2004
2148
  fields,
2005
2149
  values,
2150
+ initialValues,
2151
+ errors,
2006
2152
  onChange
2007
2153
  }) {
2008
2154
  if (!fields.length) return null;
@@ -2015,6 +2161,9 @@ function GroupSection({
2015
2161
  {
2016
2162
  field,
2017
2163
  value: values[field.field] ?? "",
2164
+ values,
2165
+ initialValue: initialValues[field.field],
2166
+ error: errors[field.field],
2018
2167
  onChange: (value) => onChange(field.field, value)
2019
2168
  },
2020
2169
  field.field
@@ -2022,21 +2171,41 @@ function GroupSection({
2022
2171
  ] });
2023
2172
  }
2024
2173
  function InlineParentForm({
2025
- fields,
2174
+ fields: rawFields,
2026
2175
  groups,
2027
2176
  parent,
2028
2177
  resource,
2029
2178
  onSaved
2030
2179
  }) {
2180
+ const fields = rawFields;
2031
2181
  const [values, setValues] = useState8({});
2032
2182
  const [saving, setSaving] = useState8(false);
2033
2183
  const [error, setError] = useState8(null);
2184
+ const [fieldErrors, setFieldErrors] = useState8({});
2034
2185
  useEffect5(() => {
2035
2186
  setValues(Object.fromEntries(fields.map((field) => [field.field, toValue2(parent[field.field], field.kind)])));
2036
- }, [parent, fields]);
2037
- const fieldsByName = useMemo6(() => new Map(fields.map((field) => [field.field, field])), [fields]);
2038
- const change = (field, value) => {
2039
- setValues((current) => ({ ...current, [field]: value }));
2187
+ setError(null);
2188
+ setFieldErrors({});
2189
+ }, [parent, rawFields]);
2190
+ const fieldsByName = useMemo6(() => new Map(fields.map((field) => [field.field, field])), [rawFields]);
2191
+ const change = (fieldName, value) => {
2192
+ const dependentFields = fields.filter(
2193
+ (candidate) => Object.values(candidate.referenceFilterFrom ?? {}).includes(fieldName)
2194
+ );
2195
+ setValues((current) => {
2196
+ const changed = current[fieldName] !== value;
2197
+ const next = { ...current, [fieldName]: value };
2198
+ if (changed) dependentFields.forEach((candidate) => {
2199
+ next[candidate.field] = "";
2200
+ });
2201
+ return next;
2202
+ });
2203
+ setFieldErrors((current) => {
2204
+ const next = { ...current };
2205
+ delete next[fieldName];
2206
+ dependentFields.forEach((candidate) => delete next[candidate.field]);
2207
+ return next;
2208
+ });
2040
2209
  setError(null);
2041
2210
  };
2042
2211
  const renderFields = () => {
@@ -2047,6 +2216,8 @@ function InlineParentForm({
2047
2216
  group,
2048
2217
  fields: group.fields.map((field) => fieldsByName.get(field)).filter(Boolean),
2049
2218
  values,
2219
+ initialValues: parent,
2220
+ errors: fieldErrors,
2050
2221
  onChange: change
2051
2222
  },
2052
2223
  `${group.label}-${group.fields.join("-")}`
@@ -2057,6 +2228,9 @@ function InlineParentForm({
2057
2228
  {
2058
2229
  field,
2059
2230
  value: values[field.field] ?? "",
2231
+ values,
2232
+ initialValue: parent[field.field],
2233
+ error: fieldErrors[field.field],
2060
2234
  onChange: (value) => change(field.field, value)
2061
2235
  },
2062
2236
  field.field
@@ -2065,10 +2239,18 @@ function InlineParentForm({
2065
2239
  return /* @__PURE__ */ jsx14(
2066
2240
  "form",
2067
2241
  {
2242
+ noValidate: true,
2068
2243
  onSubmit: async (event) => {
2069
2244
  event.preventDefault();
2245
+ const validation = requiredErrors(fields, values);
2246
+ if (Object.keys(validation).length) {
2247
+ setFieldErrors(validation);
2248
+ setError({ message: "Preencha os campos obrigat\xF3rios destacados." });
2249
+ return;
2250
+ }
2070
2251
  setSaving(true);
2071
2252
  setError(null);
2253
+ setFieldErrors({});
2072
2254
  try {
2073
2255
  const payload = Object.fromEntries(
2074
2256
  fields.map((field) => [field.field, coerce2(values[field.field] ?? "", field.kind)]).filter(([, value]) => value !== void 0)
@@ -2077,12 +2259,13 @@ function InlineParentForm({
2077
2259
  onSaved(saved);
2078
2260
  } catch (requestError) {
2079
2261
  setError(requestError);
2262
+ setFieldErrors(fieldErrorsOf(requestError));
2080
2263
  } finally {
2081
2264
  setSaving(false);
2082
2265
  }
2083
2266
  },
2084
2267
  children: /* @__PURE__ */ jsxs10(Stack3, { gap: "16px", children: [
2085
- error ? /* @__PURE__ */ jsx14(Text9, { color: "#B42318", fontSize: "11px", children: "N\xE3o foi poss\xEDvel salvar." }) : null,
2268
+ error ? /* @__PURE__ */ jsx14(Box7, { role: "alert", p: "10px 12px", color: "#B42318", bg: "#FEF3F2", borderWidth: "1px", borderColor: "#FECDCA", borderRadius: "7px", fontSize: "11px", children: messageOf3(error) }) : null,
2086
2269
  renderFields(),
2087
2270
  /* @__PURE__ */ jsx14(Flex6, { justify: "flex-end", children: /* @__PURE__ */ jsx14(Button7, { type: "submit", colorPalette: "brand", loading: saving, children: "Salvar dados principais" }) })
2088
2271
  ] })
@@ -2378,7 +2561,27 @@ function ticketId(row) {
2378
2561
  const id = row._id ?? row.id;
2379
2562
  return id == null ? void 0 : String(id);
2380
2563
  }
2381
- function CorePipeline({ model, label, endpoint, stageField = "status", stages, titleField }) {
2564
+ function mergeFormFields2(metadataFields, overrides) {
2565
+ if (!overrides?.length) return metadataFields;
2566
+ const byField = new Map(overrides.map((field) => [field.field, field]));
2567
+ const merged = metadataFields.map((field) => ({ ...field, ...byField.get(field.field) }));
2568
+ const known = new Set(metadataFields.map((field) => field.field));
2569
+ overrides.forEach((field) => {
2570
+ if (!known.has(field.field)) merged.push(field);
2571
+ });
2572
+ return merged;
2573
+ }
2574
+ function CorePipeline({
2575
+ model,
2576
+ label,
2577
+ endpoint,
2578
+ stageField = "status",
2579
+ stages,
2580
+ titleField,
2581
+ form,
2582
+ relations,
2583
+ ticketModal
2584
+ }) {
2382
2585
  const schemaQuery = useModelSchema(model);
2383
2586
  const schema = schemaQuery.data;
2384
2587
  const basePath = endpoint ?? schema?.basePath ?? `/${model.toLowerCase()}s`;
@@ -2391,6 +2594,7 @@ function CorePipeline({ model, label, endpoint, stageField = "status", stages, t
2391
2594
  queryFn: () => resource.list({ pageSize: 200 }),
2392
2595
  enabled: !!schema
2393
2596
  });
2597
+ const invalidate = () => queryClient.invalidateQueries({ queryKey: ["oon", "pipeline", basePath] });
2394
2598
  const saveMutation = useMutation3({
2395
2599
  mutationFn: (values) => {
2396
2600
  const id = selectedTicket ? ticketId(selectedTicket) : void 0;
@@ -2398,7 +2602,7 @@ function CorePipeline({ model, label, endpoint, stageField = "status", stages, t
2398
2602
  return resource.update(id, values);
2399
2603
  },
2400
2604
  onSuccess: async () => {
2401
- await queryClient.invalidateQueries({ queryKey: ["oon", "pipeline", basePath] });
2605
+ await invalidate();
2402
2606
  setSelectedTicket(null);
2403
2607
  setFormError(null);
2404
2608
  },
@@ -2409,9 +2613,9 @@ function CorePipeline({ model, label, endpoint, stageField = "status", stages, t
2409
2613
  if (listQuery.isError) return /* @__PURE__ */ jsx18(ErrorState, { error: listQuery.error });
2410
2614
  const stageFieldMeta = schema?.fields.find((field) => field.name === stageField);
2411
2615
  const resolvedStages = stages ?? (stageFieldMeta?.options ?? []).map((option) => ({ id: option, label: option }));
2412
- const title = titleField ?? schema?.fields.find((field) => field.kind === "string")?.name ?? schema?.searchable[0] ?? "_id";
2413
- const detailFields = (schema?.fields ?? []).filter((field) => field.name !== stageField && field.name !== title && !["raw"].includes(field.kind)).slice(0, 3);
2414
- const formFields = formFieldsFromMeta(schema?.fields ?? []);
2616
+ const resolvedTitleField = titleField ?? ticketModal?.titleField ?? schema?.fields.find((field) => field.kind === "string")?.name ?? schema?.searchable[0] ?? "_id";
2617
+ const detailFields = (schema?.fields ?? []).filter((field) => field.name !== stageField && field.name !== resolvedTitleField && !["raw"].includes(field.kind)).slice(0, 3);
2618
+ const formFields = mergeFormFields2(formFieldsFromMeta(schema?.fields ?? []), form);
2415
2619
  const rows = listQuery.data?.results ?? [];
2416
2620
  const byStage = /* @__PURE__ */ new Map();
2417
2621
  for (const stage of resolvedStages) byStage.set(stage.id, []);
@@ -2445,7 +2649,7 @@ function CorePipeline({ model, label, endpoint, stageField = "status", stages, t
2445
2649
  /* @__PURE__ */ jsx18(Badge4, { colorPalette: "brand", variant: "solid", borderRadius: "full", minW: "24px", justifyContent: "center", children: items.length })
2446
2650
  ] }),
2447
2651
  /* @__PURE__ */ jsx18(Stack5, { gap: 2, children: items.length === 0 ? /* @__PURE__ */ jsx18(Box10, { borderWidth: "1px", borderStyle: "dashed", borderColor: "#C8D0D5", borderRadius: "10px", p: 5, textAlign: "center", children: /* @__PURE__ */ jsx18(Text12, { fontSize: "12px", color: "gray.500", children: "Nenhum ticket nesta etapa." }) }) : items.map((row, index) => {
2448
- const ticketTitle = formatCell(row[title], "string");
2652
+ const ticketTitle = formatCell(row[resolvedTitleField], "string");
2449
2653
  return /* @__PURE__ */ jsxs13(
2450
2654
  Button9,
2451
2655
  {
@@ -2487,7 +2691,23 @@ function CorePipeline({ model, label, endpoint, stageField = "status", stages, t
2487
2691
  stageId
2488
2692
  );
2489
2693
  }) }),
2490
- selectedTicket ? /* @__PURE__ */ jsx18(
2694
+ selectedTicket && ticketModal && ticketModal.enabled !== false ? /* @__PURE__ */ jsx18(
2695
+ CoreDetailModal,
2696
+ {
2697
+ model,
2698
+ title: schema?.singular ?? label ?? model,
2699
+ config: { ...ticketModal, titleField: ticketModal.titleField ?? resolvedTitleField },
2700
+ formFields,
2701
+ recordId: ticketId(selectedTicket),
2702
+ initialRecord: selectedTicket,
2703
+ relations,
2704
+ onClose: () => {
2705
+ setSelectedTicket(null);
2706
+ setFormError(null);
2707
+ },
2708
+ onSaved: invalidate
2709
+ }
2710
+ ) : selectedTicket ? /* @__PURE__ */ jsx18(
2491
2711
  DynamicForm,
2492
2712
  {
2493
2713
  title: `Editar ${schema?.singular ?? label ?? model}`,
@@ -2756,7 +2976,11 @@ function renderView(view, registry) {
2756
2976
  model: v.model,
2757
2977
  label: viewLabel(v),
2758
2978
  stageField: v.stageField,
2759
- stages: v.stages
2979
+ stages: v.stages,
2980
+ titleField: v.titleField,
2981
+ form: v.form,
2982
+ relations: v.relations,
2983
+ ticketModal: v.ticketModal
2760
2984
  }
2761
2985
  );
2762
2986
  }
@@ -3883,7 +4107,12 @@ function manifestToConfig(manifest, runtime) {
3883
4107
  label: p.label ?? p.name,
3884
4108
  path: p.path,
3885
4109
  stageField: p.stageField,
3886
- section: p.section
4110
+ stages: p.stages,
4111
+ titleField: p.titleField,
4112
+ section: p.section,
4113
+ form: p.form,
4114
+ relations: p.relations,
4115
+ ticketModal: p.ticketModal
3887
4116
  };
3888
4117
  views.push(view);
3889
4118
  }