@oondemand/oon-core-front 0.3.27 → 0.3.28

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.d.ts CHANGED
@@ -571,6 +571,16 @@ declare const oonCoreFront: {
571
571
  start: typeof start;
572
572
  };
573
573
 
574
+ interface CentralFormFieldDef extends OonFormFieldDef {
575
+ /** Filtros fixos enviados como query params ao CRUD da model referenciada. */
576
+ referenceFilters?: Record<string, string | number | boolean | null>;
577
+ /** Mapeia query param da referência para outro campo do mesmo formulário. */
578
+ referenceFilterFrom?: Record<string, string>;
579
+ /** Mensagem exibida enquanto algum campo de referenceFilterFrom estiver vazio. */
580
+ referenceDisabledMessage?: string;
581
+ /** Mantém somente opções em que estes campos estejam vazios/nulos. */
582
+ referenceEmptyFields?: string[];
583
+ }
574
584
  /**
575
585
  * Schema do `central.ui.json` (Seção 9 do escopo). É o que o frontend
576
586
  * consumidor gerado pelo `create-central-oon` carrega — declaração pura, sem
@@ -598,7 +608,7 @@ interface CentralUiManifest {
598
608
  label?: string;
599
609
  section?: string;
600
610
  /** Overrides pontuais de campos; os demais continuam vindo da metadata. */
601
- form?: OonFormFieldDef[];
611
+ form?: CentralFormFieldDef[];
602
612
  list?: OonCollectionListConfig;
603
613
  relations?: Record<string, OonRelationDef>;
604
614
  detailModal?: OonDetailModalConfig;
@@ -892,4 +902,4 @@ declare function useCoreMetadata(): _tanstack_react_query.UseQueryResult<NoInfer
892
902
  /** Descrição de uma model específica — base do modo "dynamic". */
893
903
  declare function useModelSchema(name: string): _tanstack_react_query.UseQueryResult<NoInfer<ModelSummary>, Error>;
894
904
 
895
- export { type CentralUiManifest, type CollectionMode, type CollectionViewDef, CoreActions, CoreAssistant, CoreCards, CoreCollection, CoreCurrency, CoreDashboard, CoreDataGrid, CoreDocument, CoreEmptyState, CoreField, CoreForm, CoreIntegration, CoreLoadingState, type CoreMetadata, CorePage, CorePageHeader, CorePipeline, CoreRelationField, CoreToolbar, type DashboardViewDef, type DashboardWidgetDef, type DocumentViewDef, type FieldKind, type FieldMeta, type ListParams, type ManifestRuntime, type ModelSummary, type OonActionDef, type OonApiConfig, type OonAppConfig, type OonAppKind, type OonAuthConfig, type OonAuthMode, type OonBlockDef, type OonColumnDef, type OonComponentRegistry, type OonCoreFrontConfig, type OonError, type OonFormFieldDef, type OonLayoutConfig, type OonMenuItem, type OonModule, type OonNavigationConfig, type OonNavigationMode, type OonPageDef, type OonRoute, type OonSchemaVersion, type OonSecurityConfig, type OonUiManifest, type OonUser, type OonViewDefinition, type PaginatedResult, type Pagination, PermissionGate, type PipelineViewDef, defineCollectionView, defineDashboard, defineDocumentView, defineOonModule, definePipelineView, manifestToConfig, oonCoreFront, start, startFromManifest, useCoreMetadata, useModelSchema, useOonApi, useOonAuth, useOonResource };
905
+ export { type CentralFormFieldDef, type CentralUiManifest, type CollectionMode, type CollectionViewDef, CoreActions, CoreAssistant, CoreCards, CoreCollection, CoreCurrency, CoreDashboard, CoreDataGrid, CoreDocument, CoreEmptyState, CoreField, CoreForm, CoreIntegration, CoreLoadingState, type CoreMetadata, CorePage, CorePageHeader, CorePipeline, CoreRelationField, CoreToolbar, type DashboardViewDef, type DashboardWidgetDef, type DocumentViewDef, type FieldKind, type FieldMeta, type ListParams, type ManifestRuntime, type ModelSummary, type OonActionDef, type OonApiConfig, type OonAppConfig, type OonAppKind, type OonAuthConfig, type OonAuthMode, type OonBlockDef, type OonColumnDef, type OonComponentRegistry, type OonCoreFrontConfig, type OonError, type OonFormFieldDef, type OonLayoutConfig, type OonMenuItem, type OonModule, type OonNavigationConfig, type OonNavigationMode, type OonPageDef, type OonRoute, type OonSchemaVersion, type OonSecurityConfig, type OonUiManifest, type OonUser, type OonViewDefinition, type PaginatedResult, type Pagination, PermissionGate, type PipelineViewDef, defineCollectionView, defineDashboard, defineDocumentView, defineOonModule, definePipelineView, manifestToConfig, oonCoreFront, start, startFromManifest, useCoreMetadata, useModelSchema, useOonApi, useOonAuth, useOonResource };
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
  ] })