@oondemand/oon-core-front 0.3.26 → 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,80 +1963,192 @@ 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
+ }
2023
+ function FieldLabel({ field, htmlFor }) {
2024
+ const label = fieldLabel({ name: field.field, label: field.label });
2025
+ return /* @__PURE__ */ jsx14("label", { htmlFor, children: /* @__PURE__ */ jsxs10(Text9, { as: "span", display: "block", mb: "5px", color: "#475467", fontSize: "11px", fontWeight: "600", children: [
2026
+ label,
2027
+ field.required ? /* @__PURE__ */ jsx14(Text9, { as: "span", color: "#DC2626", children: " *" }) : null
2028
+ ] }) });
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
+ }
1908
2033
  function FieldControl({
1909
2034
  field,
1910
2035
  value,
2036
+ values,
2037
+ initialValue,
2038
+ error,
1911
2039
  onChange
1912
2040
  }) {
1913
2041
  const label = fieldLabel({ name: field.field, label: field.label });
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
+ };
1914
2050
  if (field.widget === "checkbox") {
1915
2051
  const checkedValue = String(field.checkedValue ?? true);
1916
2052
  const uncheckedValue = String(field.uncheckedValue ?? false);
1917
2053
  const checked = (value || uncheckedValue) === checkedValue;
1918
- return /* @__PURE__ */ jsxs10(
1919
- Box7,
1920
- {
1921
- as: "label",
1922
- display: "flex",
1923
- minH: "38px",
1924
- px: "11px",
1925
- alignItems: "center",
1926
- gap: "8px",
1927
- color: "#344054",
1928
- bg: checked ? "#F0F7FF" : "white",
1929
- borderWidth: "1px",
1930
- borderColor: checked ? "#8FC9E7" : "#D5DDE3",
1931
- borderRadius: "7px",
1932
- cursor: "pointer",
1933
- children: [
1934
- /* @__PURE__ */ jsx14(
1935
- "input",
1936
- {
1937
- type: "checkbox",
1938
- checked,
1939
- style: { width: 16, height: 16, accentColor: "#0474AF", cursor: "pointer" },
1940
- onChange: (event) => onChange(event.currentTarget.checked ? checkedValue : uncheckedValue)
1941
- }
1942
- ),
1943
- /* @__PURE__ */ jsx14(Text9, { as: "span", fontSize: "11px", fontWeight: "600", children: label })
1944
- ]
1945
- }
1946
- );
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
+ ] });
2089
+ }
2090
+ if (field.kind === "ref" && field.ref) {
2091
+ const reference = resolveReference2(field, values);
2092
+ return /* @__PURE__ */ jsxs10(Box7, { children: [
2093
+ /* @__PURE__ */ jsx14(FieldLabel, { field, htmlFor: inputId }),
2094
+ /* @__PURE__ */ jsx14(
2095
+ ReferenceSelect,
2096
+ {
2097
+ field: field.field,
2098
+ inputId,
2099
+ errorId,
2100
+ label,
2101
+ refModel: field.ref,
2102
+ value,
2103
+ initialValue,
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,
2111
+ onChange
2112
+ }
2113
+ )
2114
+ ] });
1947
2115
  }
1948
2116
  const control = field.kind === "enum" && field.options?.length ? /* @__PURE__ */ jsxs10(NativeSelect4.Root, { size: "sm", children: [
1949
- /* @__PURE__ */ jsxs10(NativeSelect4.Field, { value, onChange: (event) => onChange(event.currentTarget.value), children: [
2117
+ /* @__PURE__ */ jsxs10(NativeSelect4.Field, { id: inputId, value, ...invalidProps, onChange: (event) => onChange(event.currentTarget.value), children: [
1950
2118
  /* @__PURE__ */ jsx14("option", { value: "", children: "\u2014 Selecione \u2014" }),
1951
2119
  field.options.map((option) => /* @__PURE__ */ jsx14("option", { value: option, children: option }, option))
1952
2120
  ] }),
1953
2121
  /* @__PURE__ */ jsx14(NativeSelect4.Indicator, {})
1954
2122
  ] }) : field.kind === "boolean" ? /* @__PURE__ */ jsxs10(NativeSelect4.Root, { size: "sm", children: [
1955
- /* @__PURE__ */ jsxs10(NativeSelect4.Field, { value, onChange: (event) => onChange(event.currentTarget.value), children: [
2123
+ /* @__PURE__ */ jsxs10(NativeSelect4.Field, { id: inputId, value, ...invalidProps, onChange: (event) => onChange(event.currentTarget.value), children: [
1956
2124
  /* @__PURE__ */ jsx14("option", { value: "", children: "\u2014 Selecione \u2014" }),
1957
2125
  /* @__PURE__ */ jsx14("option", { value: "true", children: "Sim" }),
1958
2126
  /* @__PURE__ */ jsx14("option", { value: "false", children: "N\xE3o" })
1959
2127
  ] }),
1960
2128
  /* @__PURE__ */ jsx14(NativeSelect4.Indicator, {})
1961
- ] }) : isMultiline2(field) ? /* @__PURE__ */ jsx14(Textarea3, { 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(
1962
2130
  Input5,
1963
2131
  {
2132
+ id: inputId,
1964
2133
  size: "sm",
1965
2134
  type: field.kind === "date" ? "date" : ["number", "currency", "currencyConverted"].includes(field.kind ?? "") ? "number" : "text",
1966
2135
  value,
2136
+ ...invalidProps,
1967
2137
  onChange: (event) => onChange(event.currentTarget.value)
1968
2138
  }
1969
2139
  );
1970
2140
  return /* @__PURE__ */ jsxs10(Box7, { children: [
1971
- /* @__PURE__ */ jsxs10(Text9, { as: "label", display: "block", mb: "5px", color: "#475467", fontSize: "11px", fontWeight: "600", children: [
1972
- label,
1973
- field.required ? /* @__PURE__ */ jsx14(Text9, { as: "span", color: "#DC2626", children: " *" }) : null
1974
- ] }),
1975
- control
2141
+ /* @__PURE__ */ jsx14(FieldLabel, { field, htmlFor: inputId }),
2142
+ control,
2143
+ /* @__PURE__ */ jsx14(ErrorText2, { id: errorId, message: error })
1976
2144
  ] });
1977
2145
  }
1978
2146
  function GroupSection({
1979
2147
  group,
1980
2148
  fields,
1981
2149
  values,
2150
+ initialValues,
2151
+ errors,
1982
2152
  onChange
1983
2153
  }) {
1984
2154
  if (!fields.length) return null;
@@ -1991,6 +2161,9 @@ function GroupSection({
1991
2161
  {
1992
2162
  field,
1993
2163
  value: values[field.field] ?? "",
2164
+ values,
2165
+ initialValue: initialValues[field.field],
2166
+ error: errors[field.field],
1994
2167
  onChange: (value) => onChange(field.field, value)
1995
2168
  },
1996
2169
  field.field
@@ -1998,21 +2171,41 @@ function GroupSection({
1998
2171
  ] });
1999
2172
  }
2000
2173
  function InlineParentForm({
2001
- fields,
2174
+ fields: rawFields,
2002
2175
  groups,
2003
2176
  parent,
2004
2177
  resource,
2005
2178
  onSaved
2006
2179
  }) {
2180
+ const fields = rawFields;
2007
2181
  const [values, setValues] = useState8({});
2008
2182
  const [saving, setSaving] = useState8(false);
2009
2183
  const [error, setError] = useState8(null);
2184
+ const [fieldErrors, setFieldErrors] = useState8({});
2010
2185
  useEffect5(() => {
2011
2186
  setValues(Object.fromEntries(fields.map((field) => [field.field, toValue2(parent[field.field], field.kind)])));
2012
- }, [parent, fields]);
2013
- const fieldsByName = useMemo6(() => new Map(fields.map((field) => [field.field, field])), [fields]);
2014
- const change = (field, value) => {
2015
- 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
+ });
2016
2209
  setError(null);
2017
2210
  };
2018
2211
  const renderFields = () => {
@@ -2023,6 +2216,8 @@ function InlineParentForm({
2023
2216
  group,
2024
2217
  fields: group.fields.map((field) => fieldsByName.get(field)).filter(Boolean),
2025
2218
  values,
2219
+ initialValues: parent,
2220
+ errors: fieldErrors,
2026
2221
  onChange: change
2027
2222
  },
2028
2223
  `${group.label}-${group.fields.join("-")}`
@@ -2033,6 +2228,9 @@ function InlineParentForm({
2033
2228
  {
2034
2229
  field,
2035
2230
  value: values[field.field] ?? "",
2231
+ values,
2232
+ initialValue: parent[field.field],
2233
+ error: fieldErrors[field.field],
2036
2234
  onChange: (value) => change(field.field, value)
2037
2235
  },
2038
2236
  field.field
@@ -2041,10 +2239,18 @@ function InlineParentForm({
2041
2239
  return /* @__PURE__ */ jsx14(
2042
2240
  "form",
2043
2241
  {
2242
+ noValidate: true,
2044
2243
  onSubmit: async (event) => {
2045
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
+ }
2046
2251
  setSaving(true);
2047
2252
  setError(null);
2253
+ setFieldErrors({});
2048
2254
  try {
2049
2255
  const payload = Object.fromEntries(
2050
2256
  fields.map((field) => [field.field, coerce2(values[field.field] ?? "", field.kind)]).filter(([, value]) => value !== void 0)
@@ -2053,12 +2259,13 @@ function InlineParentForm({
2053
2259
  onSaved(saved);
2054
2260
  } catch (requestError) {
2055
2261
  setError(requestError);
2262
+ setFieldErrors(fieldErrorsOf(requestError));
2056
2263
  } finally {
2057
2264
  setSaving(false);
2058
2265
  }
2059
2266
  },
2060
2267
  children: /* @__PURE__ */ jsxs10(Stack3, { gap: "16px", children: [
2061
- 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,
2062
2269
  renderFields(),
2063
2270
  /* @__PURE__ */ jsx14(Flex6, { justify: "flex-end", children: /* @__PURE__ */ jsx14(Button7, { type: "submit", colorPalette: "brand", loading: saving, children: "Salvar dados principais" }) })
2064
2271
  ] })