@zauru-sdk/components 2.0.119 → 2.0.120

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/CHANGELOG.md CHANGED
@@ -3,6 +3,14 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [2.0.120](https://github.com/intuitiva/zauru-typescript-sdk/compare/v2.0.119...v2.0.120) (2025-02-05)
7
+
8
+ **Note:** Version bump only for package @zauru-sdk/components
9
+
10
+
11
+
12
+
13
+
6
14
  ## [2.0.119](https://github.com/intuitiva/zauru-typescript-sdk/compare/v2.0.118...v2.0.119) (2025-02-05)
7
15
 
8
16
  **Note:** Version bump only for package @zauru-sdk/components
@@ -1,9 +1,10 @@
1
1
  import React from "react";
2
2
  import { GenericDynamicTableColumn, RowDataType, SelectFieldOption } from "@zauru-sdk/types";
3
3
  export type FooterColumnConfig = {
4
- content: React.ReactNode;
4
+ content?: React.ReactNode;
5
5
  className?: string;
6
6
  name?: string;
7
+ cell?: (rows: RowDataType[]) => React.ReactNode;
7
8
  };
8
9
  type Props = {
9
10
  name?: string;
@@ -14,17 +15,31 @@ type Props = {
14
15
  footerRow?: FooterColumnConfig[];
15
16
  thCSSProperties?: React.CSSProperties;
16
17
  thElementsClassName?: string;
18
+ /** Controla si se pueden o no editar los campos (oculta botones y/o deshabilita campos). */
17
19
  editable?: boolean;
20
+ /** Opciones de búsqueda. */
18
21
  searcheables?: SelectFieldOption[];
22
+ /** Activa o desactiva el “skeleton” de carga. */
19
23
  loading?: boolean;
24
+ /** Controla la paginación. */
20
25
  paginated?: boolean;
21
26
  defaultItemsPerPage?: number;
22
27
  itemsPerPageOptions?: number[];
28
+ /** Quita el color de fondo por defecto a las filas pares. */
23
29
  withoutBg?: boolean;
30
+ /** Orientación de los encabezados (`horizontal` o `vertical`). */
24
31
  orientation?: "horizontal" | "vertical";
32
+ /** Máximo número de filas permitidas al hacer clic en “agregar fila”. */
25
33
  maxRows?: number;
34
+ /** Muestra un diálogo de confirmación antes de eliminar una fila. */
26
35
  confirmDelete?: boolean;
36
+ /** Función personalizada para manejar el botón de “agregar fila”. */
27
37
  addRowButtonHandler?: (tableData: RowDataType[], setTableData: (data: RowDataType[]) => void) => void;
38
+ /**
39
+ * Controla si todo el componente se renderiza en modo de solo lectura,
40
+ * es decir, sin permitir ningún tipo de interacción de edición o eliminación.
41
+ */
42
+ readOnly?: boolean;
28
43
  };
29
44
  /**
30
45
  *
@@ -40,6 +55,23 @@ type Props = {
40
55
  defaultValue={
41
56
  invoiceDetailsDefaultValue ?? [{ id: crypto.randomUUID() }]
42
57
  }
58
+ addRowButtonHandler={async (tableData, setTableData) => {
59
+ const selectedItem = await createItemModal(ecommerceItems, {
60
+ itemSize: {
61
+ width: "150px",
62
+ height: "150px",
63
+ },
64
+ });
65
+ if (selectedItem) {
66
+ setTableData([
67
+ ...tableData,
68
+ {
69
+ id: crypto.randomUUID(),
70
+ code: selectedItem.code,
71
+ },
72
+ ]);
73
+ }
74
+ }}
43
75
  columns={[
44
76
  {
45
77
  label: "Producto",
@@ -70,11 +102,23 @@ type Props = {
70
102
  }
71
103
  ]}
72
104
  footerRow={[
73
- { content: "Total", className: "text-left font-bold" },
74
- { content: calculateTotal(), className: "text-center" },
75
- { content: "", className: "text-center" }
105
+ {
106
+ name: "code",
107
+ content: "Total",
108
+ className: "text-left font-bold",
109
+ },
110
+ {
111
+ name: "total",
112
+ className: "text-left font-bold",
113
+ cell: (rows: any) => {
114
+ return `${rows.reduce((acc: number, row: any) => {
115
+ return acc + row.total;
116
+ }, 0)}`;
117
+ },
118
+ },
76
119
  ]}
77
120
  maxRows={2}
121
+ readOnly={false}
78
122
  />
79
123
  */
80
124
  export declare const GenericDynamicTable: (props: Props) => import("react/jsx-runtime").JSX.Element;
@@ -1,6 +1,15 @@
1
1
  import { FormGraphQL, FormSubmissionValueGraphQL } from "@zauru-sdk/types";
2
+ import { z } from "zod";
3
+ export declare const getDynamicBaculoFormSchema: (form?: FormGraphQL, extraFieldValidations?: {
4
+ [key: string]: any;
5
+ }) => z.ZodAny | z.ZodObject<{
6
+ [x: string]: any;
7
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
8
+ [x: string]: any;
9
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
10
+ [x: string]: any;
11
+ }, z.ZodTypeAny, "passthrough">>;
2
12
  type Props = {
3
- formName?: string;
4
13
  form?: FormGraphQL;
5
14
  options?: {
6
15
  showTitle: boolean;
@@ -7,7 +7,6 @@ type Props = {
7
7
  helpText?: string;
8
8
  hint?: string;
9
9
  onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
10
- disabled?: boolean;
11
10
  readOnly?: boolean;
12
11
  fileTypes?: string[];
13
12
  showAvailableTypes?: boolean;
@@ -1,24 +1,35 @@
1
1
  type Item = {
2
+ item_id: string;
2
3
  name: string;
3
4
  code: string;
4
5
  unitPrice: number;
5
6
  stock: number;
6
7
  currencyPrefix: string;
7
8
  imageUrl: string;
9
+ quantity: number;
10
+ flexiblePrice: boolean;
11
+ priceText: string;
8
12
  };
9
- type ItemCategory = {
13
+ export type ItemModalCategory = {
10
14
  name: string;
11
15
  items: Item[];
12
16
  };
17
+ type OnCloseType = (selectedItem: Item | null) => void;
13
18
  type ItemModalProps = {
14
- itemList: ItemCategory[];
15
- onClose: (selectedItem: Item | null) => void;
19
+ itemList: ItemModalCategory[];
20
+ onClose: OnCloseType;
16
21
  config?: {
17
- itemSize: {
22
+ itemSize?: {
18
23
  width: string;
19
24
  height: string;
20
25
  };
21
26
  };
27
+ /**
28
+ * Controla cómo se muestran las categorías.
29
+ * - "text": Estilo por defecto (un simple título con flecha).
30
+ * - "card": Tarjeta más grande y ancha, útil para pantallas táctiles (tablet).
31
+ */
32
+ categoryViewMode?: "text" | "card";
22
33
  };
23
- export declare const createItemModal: (itemList: ItemCategory[], config?: ItemModalProps["config"]) => Promise<Item | null>;
34
+ export declare const createItemModal: (itemList: ItemModalCategory[], config?: ItemModalProps["config"], categoryViewMode?: ItemModalProps["categoryViewMode"]) => Promise<Item | null>;
24
35
  export {};
@@ -3,7 +3,7 @@ export type NavBarItem = {
3
3
  link: string;
4
4
  loggedIn: boolean;
5
5
  icon?: any;
6
- selectedColor?: "green";
6
+ selectedColor?: "pink" | "purple" | "slate" | "green" | "yellow" | "red" | "sky";
7
7
  color?: ColorInterface;
8
8
  childrens?: Exclude<NavBarItem, "loggedIn">[];
9
9
  reduxNotificationBadge?: (state: any) => string | number;
@@ -25,6 +25,7 @@ export type ColorInterface = {
25
25
  bg700: string;
26
26
  bg600: string;
27
27
  bg500: string;
28
+ bg200: string;
28
29
  ring600: string;
29
30
  ring500: string;
30
31
  };
@@ -4,6 +4,7 @@ export declare const COLORS: {
4
4
  bg700: string;
5
5
  bg600: string;
6
6
  bg500: string;
7
+ bg200: string;
7
8
  ring600: string;
8
9
  ring500: string;
9
10
  };
@@ -12,6 +13,7 @@ export declare const COLORS: {
12
13
  bg700: string;
13
14
  bg600: string;
14
15
  bg500: string;
16
+ bg200: string;
15
17
  ring600: string;
16
18
  ring500: string;
17
19
  };
@@ -20,6 +22,7 @@ export declare const COLORS: {
20
22
  bg700: string;
21
23
  bg600: string;
22
24
  bg500: string;
25
+ bg200: string;
23
26
  ring600: string;
24
27
  ring500: string;
25
28
  };
@@ -28,6 +31,7 @@ export declare const COLORS: {
28
31
  bg700: string;
29
32
  bg600: string;
30
33
  bg500: string;
34
+ bg200: string;
31
35
  ring600: string;
32
36
  ring500: string;
33
37
  };
@@ -36,6 +40,7 @@ export declare const COLORS: {
36
40
  bg700: string;
37
41
  bg600: string;
38
42
  bg500: string;
43
+ bg200: string;
39
44
  ring600: string;
40
45
  ring500: string;
41
46
  };
@@ -44,6 +49,7 @@ export declare const COLORS: {
44
49
  bg700: string;
45
50
  bg600: string;
46
51
  bg500: string;
52
+ bg200: string;
47
53
  ring600: string;
48
54
  ring500: string;
49
55
  };
@@ -52,6 +58,7 @@ export declare const COLORS: {
52
58
  bg700: string;
53
59
  bg600: string;
54
60
  bg500: string;
61
+ bg200: string;
55
62
  ring600: string;
56
63
  ring500: string;
57
64
  };
@@ -11,6 +11,7 @@ export const Button = (props) => {
11
11
  bg700: "bg-green-700",
12
12
  bg600: "bg-green-600",
13
13
  bg500: "bg-green-500",
14
+ bg200: "bg-green-200",
14
15
  ring600: "ring-green-600",
15
16
  ring500: "ring-green-500",
16
17
  },
@@ -19,6 +20,7 @@ export const Button = (props) => {
19
20
  bg700: "bg-indigo-700",
20
21
  bg600: "bg-indigo-600",
21
22
  bg500: "bg-indigo-500",
23
+ bg200: "bg-indigo-200",
22
24
  ring600: "ring-indigo-600",
23
25
  ring500: "ring-indigo-500",
24
26
  },
@@ -27,6 +29,7 @@ export const Button = (props) => {
27
29
  bg700: "bg-red-700",
28
30
  bg600: "bg-red-600",
29
31
  bg500: "bg-red-500",
32
+ bg200: "bg-red-200",
30
33
  ring600: "ring-red-600",
31
34
  ring500: "ring-red-500",
32
35
  },
@@ -35,6 +38,7 @@ export const Button = (props) => {
35
38
  bg700: "bg-yellow-700",
36
39
  bg600: "bg-yellow-600",
37
40
  bg500: "bg-yellow-500",
41
+ bg200: "bg-yellow-200",
38
42
  ring600: "ring-yellow-600",
39
43
  ring500: "ring-yellow-500",
40
44
  },
@@ -46,13 +50,13 @@ export const Button = (props) => {
46
50
  .map((error) => error?.message?.toString())
47
51
  .join(", ")
48
52
  : "";
49
- const buttonContent = (_jsxs(_Fragment, { children: [_jsx("input", { type: "hidden", name: "action", value: name }), _jsx("button", { type: type, disabled: loading || disabled || (enableFormErrorsValidation && formHasErrors), onClick: onClickSave, className: `ml-2 ${loading || disabled || (enableFormErrorsValidation && formHasErrors)
50
- ? " bg-opacity-25 "
51
- : ""} ${loading
52
- ? " cursor-progress"
53
- : `${disabled || (enableFormErrorsValidation && formHasErrors)
54
- ? ""
55
- : `hover:${color.bg700}`}`} inline-flex justify-center rounded-md border border-transparent ${color.bg600} py-2 px-4 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:${color.ring500} focus:ring-offset-2 ${className}`, children: loading ? loadingText : inside })] }));
53
+ const buttonContent = (_jsx(_Fragment, { children: _jsx("button", { type: type, name: "action", value: name, disabled: loading || disabled || (enableFormErrorsValidation && formHasErrors), onClick: onClickSave, className: `ml-2 ${loading || disabled || (enableFormErrorsValidation && formHasErrors)
54
+ ? " bg-opacity-25 "
55
+ : ""} ${loading
56
+ ? " cursor-progress"
57
+ : `${disabled || (enableFormErrorsValidation && formHasErrors)
58
+ ? ""
59
+ : `hover:${color.bg700}`}`} inline-flex justify-center rounded-md border border-transparent ${color.bg600} py-2 px-4 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:${color.ring500} focus:ring-offset-2 ${className}`, children: loading ? loadingText : inside }) }));
56
60
  return (_jsxs(_Fragment, { children: [(enableFormErrorsValidation && formHasErrors && errorMessage) ||
57
61
  (enableFormErrorsDescriptions && errorMessage) ? (_jsx("div", { className: "flex flex-col items-end mb-2", children: _jsx("div", { className: "p-2 bg-red-100 border border-red-400 text-red-700 rounded-md shadow-sm", children: _jsx("p", { className: "text-sm", children: errorMessage }) }) })) : null, buttonContent] }));
58
62
  };
@@ -12,7 +12,7 @@ import { TrashSvg } from "@zauru-sdk/icons";
12
12
  import { useFormContext } from "react-hook-form";
13
13
  import { ComponentError } from "../Alerts/index.js";
14
14
  const GenericDynamicTableErrorComponent = ({ name }) => {
15
- const { formState: { errors }, } = useFormContext() || { formState: {} }; // Obtener el contexto solo si existe
15
+ const { formState: { errors }, } = useFormContext() || { formState: {} };
16
16
  const error = errors ? errors[name ?? "-1"] : undefined;
17
17
  return error ? (_jsxs("p", { className: `mt-2 text-sm text-red-600 dark:text-red-500`, children: [_jsx("span", { className: "font-medium", children: "Oops!" }), " ", error?.message?.toString()] })) : (_jsx(_Fragment, {}));
18
18
  };
@@ -30,6 +30,23 @@ const GenericDynamicTableErrorComponent = ({ name }) => {
30
30
  defaultValue={
31
31
  invoiceDetailsDefaultValue ?? [{ id: crypto.randomUUID() }]
32
32
  }
33
+ addRowButtonHandler={async (tableData, setTableData) => {
34
+ const selectedItem = await createItemModal(ecommerceItems, {
35
+ itemSize: {
36
+ width: "150px",
37
+ height: "150px",
38
+ },
39
+ });
40
+ if (selectedItem) {
41
+ setTableData([
42
+ ...tableData,
43
+ {
44
+ id: crypto.randomUUID(),
45
+ code: selectedItem.code,
46
+ },
47
+ ]);
48
+ }
49
+ }}
33
50
  columns={[
34
51
  {
35
52
  label: "Producto",
@@ -60,15 +77,33 @@ const GenericDynamicTableErrorComponent = ({ name }) => {
60
77
  }
61
78
  ]}
62
79
  footerRow={[
63
- { content: "Total", className: "text-left font-bold" },
64
- { content: calculateTotal(), className: "text-center" },
65
- { content: "", className: "text-center" }
80
+ {
81
+ name: "code",
82
+ content: "Total",
83
+ className: "text-left font-bold",
84
+ },
85
+ {
86
+ name: "total",
87
+ className: "text-left font-bold",
88
+ cell: (rows: any) => {
89
+ return `${rows.reduce((acc: number, row: any) => {
90
+ return acc + row.total;
91
+ }, 0)}`;
92
+ },
93
+ },
66
94
  ]}
67
95
  maxRows={2}
96
+ readOnly={false}
68
97
  />
69
98
  */
70
99
  export const GenericDynamicTable = (props) => {
71
- const { columns, onChange, className, footerRow, defaultValue = [], thCSSProperties, thElementsClassName = "", editable = true, searcheables = [], loading = false, paginated = true, defaultItemsPerPage = 10, itemsPerPageOptions = [10, 50, 100], name, withoutBg = false, orientation = "horizontal", maxRows, confirmDelete = true, addRowButtonHandler, } = props;
100
+ const { columns, onChange, className, footerRow, defaultValue = [], thCSSProperties, thElementsClassName = "", editable = true, readOnly = false, // Nuevo prop
101
+ searcheables = [], loading = false, paginated = true, defaultItemsPerPage = 10, itemsPerPageOptions = [10, 50, 100], name, withoutBg = false, orientation = "horizontal", maxRows, confirmDelete = true, addRowButtonHandler, } = props;
102
+ /**
103
+ * Definimos una variable interna para saber si los campos son
104
+ * efectivamente editables: solo si `editable` es true y `readOnly` es false.
105
+ */
106
+ const isEditable = editable && !readOnly;
72
107
  try {
73
108
  const [tableData, setTableData] = useState(defaultValue);
74
109
  const [deletedData, setDeletedData] = useState([]);
@@ -80,12 +115,14 @@ export const GenericDynamicTable = (props) => {
80
115
  if (defaultValue.length) {
81
116
  setTableData(defaultValue);
82
117
  }
118
+ // eslint-disable-next-line react-hooks/exhaustive-deps
83
119
  }, []);
84
120
  useEffect(() => {
85
121
  setFilteredTableData(tableData);
86
122
  }, [tableData]);
87
123
  useEffect(() => {
88
124
  changeFilteredData();
125
+ // eslint-disable-next-line react-hooks/exhaustive-deps
89
126
  }, [tableData, search]);
90
127
  const totalPages = () => {
91
128
  return Math.ceil(filteredTableData.length / itemsPerPage);
@@ -97,11 +134,11 @@ export const GenericDynamicTable = (props) => {
97
134
  const defs = {};
98
135
  columns.forEach((x) => {
99
136
  defs[`${x.name}`] =
100
- x.type == "label" || x.type == "textField"
137
+ x.type === "label" || x.type === "textField"
101
138
  ? ""
102
- : x.type == "selectField"
139
+ : x.type === "selectField"
103
140
  ? 0
104
- : x.type == "checkbox"
141
+ : x.type === "checkbox"
105
142
  ? false
106
143
  : 0;
107
144
  });
@@ -113,6 +150,8 @@ export const GenericDynamicTable = (props) => {
113
150
  const removeRow = (rowId) => {
114
151
  const newDeletedData = [...deletedData];
115
152
  const deletedItem = tableData?.find((x) => x.id === rowId);
153
+ // Si la fila tenía un "id" no numérico (ej. generamos un UUID al vuelo),
154
+ // igual se procede a eliminar, aunque no se guarde en "deletedData".
116
155
  if (deletedItem && !isNaN(deletedItem.id)) {
117
156
  newDeletedData.push(deletedItem);
118
157
  }
@@ -132,23 +171,27 @@ export const GenericDynamicTable = (props) => {
132
171
  });
133
172
  };
134
173
  const renderHeader = () => {
174
+ const rendereableColumns = columns.filter((column) => column.type !== "hidden");
135
175
  if (orientation === "horizontal") {
136
- return (_jsxs("tr", { style: { ...thCSSProperties }, children: [columns.map((column, index) => {
137
- const ancho = column.width ?? (editable ? 94 : 100) / (columns.length ?? 1);
176
+ return (_jsxs("tr", { style: { ...thCSSProperties }, children: [rendereableColumns.map((column, index) => {
177
+ const ancho = column.width ??
178
+ (isEditable ? 94 : 100) / (rendereableColumns.length ?? 1);
138
179
  return (_jsx("th", { className: `text-left align-middle p-2 ${thElementsClassName} ${column.headerClassName || ""}`, style: { width: `${ancho}%` }, children: column.label }, index));
139
- }), editable && _jsx("th", { style: { width: "4%" } })] }));
180
+ }), isEditable && _jsx("th", { style: { width: "4%" } })] }));
140
181
  }
141
182
  else {
142
183
  return null;
143
184
  }
144
185
  };
145
186
  const renderRow = (rowData, index) => {
187
+ const rendereableColumns = columns.filter((column) => column.type !== "hidden");
146
188
  if (orientation === "horizontal") {
147
- return (_jsxs("tr", { className: index % 2 === 0 ? `${withoutBg ? "" : "bg-gray-200"}` : "", children: [columns.map((column) => renderCell(rowData, column)), editable && renderDeleteButton(rowData)] }, rowData.id));
189
+ return (_jsxs("tr", { className: index % 2 === 0 ? `${withoutBg ? "" : "bg-gray-200"}` : "", children: [rendereableColumns.map((column) => renderCell(rowData, column)), isEditable && renderDeleteButton(rowData)] }, rowData.id));
148
190
  }
149
191
  else {
150
- return columns.map((column) => (_jsxs("tr", { className: index % 2 === 0 ? `${withoutBg ? "" : "bg-gray-200"}` : "", children: [_jsx("th", { className: `text-left align-middle p-2 ${thElementsClassName} ${column.headerClassName || ""}`, children: column.label }), renderCell(rowData, column), editable &&
151
- column === columns[columns.length - 1] &&
192
+ // Orientación vertical
193
+ return rendereableColumns.map((column) => (_jsxs("tr", { className: index % 2 === 0 ? `${withoutBg ? "" : "bg-gray-200"}` : "", children: [_jsx("th", { className: `text-left align-middle p-2 ${thElementsClassName} ${column.headerClassName || ""}`, children: column.label }), renderCell(rowData, column), isEditable &&
194
+ column === rendereableColumns[rendereableColumns.length - 1] &&
152
195
  renderDeleteButton(rowData)] }, `${rowData.id}-${column.name}`)));
153
196
  }
154
197
  };
@@ -156,13 +199,24 @@ export const GenericDynamicTable = (props) => {
156
199
  if (loading) {
157
200
  return (_jsx("td", { className: `align-middle p-1 ${column.cellClassName || ""}`, children: _jsx(LoadingInputSkeleton, {}) }, `${rowData.id}-${column.name}`));
158
201
  }
202
+ if (column.type === "hidden") {
203
+ return _jsx(_Fragment, {});
204
+ }
159
205
  const tempVal = rowData[column.name];
160
206
  const defaultVal = column.type === "selectField"
161
207
  ? column.options?.find((x) => x.value === tempVal)
162
208
  : tempVal;
209
+ // Solo lectura: en este caso mostramos el valor como label
210
+ if (readOnly) {
211
+ return (_jsx("td", { className: `align-middle p-1 ${column.cellClassName || ""}`, children: _jsx("div", { children: column.cell
212
+ ? column.cell(rowData)
213
+ : defaultVal?.label ?? tempVal }) }, `${rowData.id}-${column.name}`));
214
+ }
215
+ // Modo normal
163
216
  if (column.type === "label") {
164
217
  return (_jsx("td", { className: `align-middle p-1 ${column.cellClassName || ""}`, children: _jsx("div", { children: column.cell ? column.cell(rowData) : defaultVal }) }, `${rowData.id}-${column.name}`));
165
218
  }
219
+ // Determinamos el componente que usaremos según "type"
166
220
  const FieldComponent = column.type === "textField"
167
221
  ? TextField
168
222
  : column.type === "checkbox"
@@ -171,7 +225,9 @@ export const GenericDynamicTable = (props) => {
171
225
  const setTableValue = (columnName, newValue) => {
172
226
  handleChange(columnName, newValue, rowData.id);
173
227
  };
174
- return (_jsx("td", { className: `align-middle p-1 ${column.cellClassName || ""}`, children: column.loadingOptions ? (_jsx(LoadingInputSkeleton, {})) : (_jsx(FieldComponent, { name: `${rowData.id}-${column.name}`, type: column.textFieldType, integer: !!column.integer, disabled: column.disabled, isClearable: true, onChange: (value) => {
228
+ return (_jsx("td", { className: `align-middle p-1 ${column.cellClassName || ""}`, children: column.loadingOptions ? (_jsx(LoadingInputSkeleton, {})) : (_jsx(FieldComponent, { name: `${rowData.id}-${column.name}`, type: column.textFieldType, integer: !!column.integer,
229
+ /** Se deshabilita si la columna lo exige o si la tabla está en modo no editable */
230
+ disabled: column.disabled || !isEditable, isClearable: true, onChange: (value) => {
175
231
  const sendValue = value?.value ?? value;
176
232
  handleChange(column.name, sendValue, rowData.id);
177
233
  column.onChange &&
@@ -197,6 +253,7 @@ export const GenericDynamicTable = (props) => {
197
253
  }, type: "button", children: _jsx(TrashSvg, {}) }) }) }));
198
254
  const renderRows = () => {
199
255
  let mapeable = filteredTableData.slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage);
256
+ // Si estamos cargando, mostramos celdas skeleton
200
257
  if (loading) {
201
258
  mapeable = [
202
259
  { id: 1 },
@@ -244,7 +301,7 @@ export const GenericDynamicTable = (props) => {
244
301
  };
245
302
  return (_jsxs(_Fragment, { children: [name && (_jsxs(_Fragment, { children: [_jsx(GenericDynamicTableErrorComponent, { name: name }), _jsx("input", { name: name, type: "hidden", value: JSON.stringify(tableData), hidden: true }), _jsx("input", { name: `deleted_${name}`, type: "hidden", value: JSON.stringify(deletedData), hidden: true })] })), _jsxs("div", { className: `${className}`, children: [searcheables.length > 0 && (_jsx("div", { children: _jsx(TextField, { className: "mb-2", name: "search", title: `Buscar por: ${searcheables
246
303
  .map((x) => x.label)
247
- .join(", ")}`, onChange: handleChangeSearch, disabled: loading }) })), _jsxs("table", { className: "w-full", children: [orientation === "horizontal" && _jsx("thead", { children: renderHeader() }), _jsx("tbody", { children: renderRows() }), editable && (_jsx("tfoot", { children: _jsx("tr", { children: _jsx("td", { colSpan: orientation === "horizontal" ? columns.length + 1 : 2, className: "align-middle", children: (!maxRows || tableData.length < maxRows) && (_jsx("button", { className: "bg-blue-500 hover:bg-blue-600 font-bold py-2 px-4 rounded", onClick: (event) => {
304
+ .join(", ")}`, onChange: handleChangeSearch, disabled: loading || readOnly }) })), _jsxs("table", { className: "w-full", children: [orientation === "horizontal" && _jsx("thead", { children: renderHeader() }), _jsx("tbody", { children: renderRows() }), isEditable && (_jsx("tfoot", { children: _jsx("tr", { children: _jsx("td", { colSpan: orientation === "horizontal" ? columns.length + 1 : 2, className: "align-middle", children: (!maxRows || tableData.length < maxRows) && (_jsx("button", { className: "bg-blue-500 hover:bg-blue-600 font-bold py-2 px-4 rounded", onClick: (event) => {
248
305
  event.preventDefault();
249
306
  event.stopPropagation();
250
307
  if (addRowButtonHandler) {
@@ -253,10 +310,12 @@ export const GenericDynamicTable = (props) => {
253
310
  else {
254
311
  addRow();
255
312
  }
256
- }, type: "button", children: "+" })) }) }) })), footerRow && (_jsx("tfoot", { className: "border-t-2 border-black", children: _jsxs("tr", { children: [columns.map((column, index) => {
313
+ }, type: "button", children: "+" })) }) }) })), footerRow && (_jsx("tfoot", { className: "border-t-2 border-black", children: _jsxs("tr", { children: [columns
314
+ .filter((column) => column.type !== "hidden")
315
+ .map((column, index) => {
257
316
  const footerCell = footerRow.find((fc) => fc.name === column.name);
258
- return (_jsx("td", { colSpan: orientation === "vertical" ? 2 : 1, className: `align-middle ${footerCell?.className || ""}`, children: footerCell ? footerCell.content : _jsx(_Fragment, {}) }, index));
259
- }), editable && _jsx("td", {})] }) }))] }), paginated && totalPages() > 1 && (_jsxs("div", { className: "flex justify-between items-center mt-4", children: [_jsxs("div", { className: "flex items-center", children: [_jsx(Button, { type: "button", disabled: currentPage === 1, onClickSave: () => setCurrentPage((old) => Math.max(old - 1, 1)), children: "Anterior" }), _jsx("span", { className: "mx-2", children: `Página ${currentPage} de ${totalPages()}` }), _jsx(Button, { type: "button", disabled: currentPage === totalPages(), onClickSave: () => setCurrentPage((old) => Math.min(old + 1, totalPages())), children: "Siguiente" })] }), _jsx("div", { children: _jsx("select", { value: itemsPerPage, onChange: (e) => {
317
+ return (_jsx("td", { colSpan: orientation === "vertical" ? 2 : 1, className: `align-middle ${footerCell?.className || ""}`, children: footerCell ? (footerCell.cell ? (footerCell.cell(tableData)) : (footerCell.content)) : (_jsx(_Fragment, {})) }, index));
318
+ }), isEditable && _jsx("td", {})] }) }))] }), paginated && totalPages() > 1 && (_jsxs("div", { className: "flex justify-between items-center mt-4", children: [_jsxs("div", { className: "flex items-center", children: [_jsx(Button, { type: "button", disabled: currentPage === 1 || readOnly, onClickSave: () => setCurrentPage((old) => Math.max(old - 1, 1)), children: "Anterior" }), _jsx("span", { className: "mx-2", children: `Página ${currentPage} de ${totalPages()}` }), _jsx(Button, { type: "button", disabled: currentPage === totalPages() || readOnly, onClickSave: () => setCurrentPage((old) => Math.min(old + 1, totalPages())), children: "Siguiente" })] }), _jsx("div", { children: _jsx("select", { value: itemsPerPage, onChange: (e) => {
260
319
  setItemsPerPage(Number(e.target.value));
261
320
  setCurrentPage(1); // resetear la página al cambiar los elementos por página
262
321
  }, children: itemsPerPageOptions.map((option) => (_jsxs("option", { value: option, children: [option, " elementos por p\u00E1gina"] }, option))) }) })] }))] })] }));
@@ -13,10 +13,34 @@ import { getDepSelectOptions, getMunSelectOptions } from "@zauru-sdk/common";
13
13
  import { StaticAlert } from "../../Alerts/index.js";
14
14
  import { SubContainer } from "../../Containers/index.js";
15
15
  import { LineSeparator } from "../../LineSeparator/index.js";
16
+ import { z } from "zod";
17
+ export const getDynamicBaculoFormSchema = (form, extraFieldValidations = {}) => {
18
+ if (!form) {
19
+ return z.any();
20
+ }
21
+ let fieldValidations = { ...extraFieldValidations };
22
+ form.settings_form_fields.forEach((field) => {
23
+ if (field.required) {
24
+ if (field.field_type === "yes_no") {
25
+ //se ignora la validación
26
+ }
27
+ else {
28
+ // Si el campo es requerido, se debe tener al menos un carácter
29
+ fieldValidations = {
30
+ ...fieldValidations,
31
+ [`${field.form_id}_${field.id}`]: z.coerce
32
+ .string()
33
+ .min(1, `Este campo es requerido.`),
34
+ };
35
+ }
36
+ }
37
+ });
38
+ return z.object(fieldValidations).passthrough(); // Iniciar con un esquema que deja pasar todo.
39
+ };
16
40
  export function DynamicBaculoForm(props) {
17
- const { form, options = { showDescription: false, showTitle: false }, formName = "", namesStr = "", defaultValues = [], showingRules = [], readOnly = false, } = props;
41
+ const { form, options = { showDescription: false, showTitle: false }, namesStr = "", defaultValues = [], showingRules = [], readOnly = false, } = props;
18
42
  if (!form) {
19
- return (_jsx(StaticAlert, { title: "No se encontr\u00F3 el formulario din\u00E1mico", description: `Ocurrió un error encontrando el formulario para ${formName}, contacte al administrador con este mensaje de error.`, type: "info" }));
43
+ return (_jsx(StaticAlert, { title: "No se encontr\u00F3 el formulario din\u00E1mico", description: `Ocurrió un error encontrando el formulario, contacte al administrador con este mensaje de error.`, type: "info" }));
20
44
  }
21
45
  const renderFieldComponent = (field) => {
22
46
  const defaultValue = defaultValues?.find((x) => x.settings_form_field.print_var_name === field.print_var_name);
@@ -39,11 +63,11 @@ export function DynamicBaculoForm(props) {
39
63
  case "date":
40
64
  return (_jsx(FormDatePicker, { title: `${field.required ? "*" : ""}${field.name}`, name: `${namesStr}${field.form_id}_${field.id}`, hint: field.hint, disabled: readOnly, defaultValue: defaultValue?.value }, field.id));
41
65
  case "file":
42
- return (_jsx(FileUploadField, { title: `${field.required ? "*" : ""}${field.name}`, name: `${namesStr}${field.form_id}_${field.id}`, hint: field.hint, disabled: readOnly, defaultValue: defaultValue?.value, download: true }, field.id));
66
+ return (_jsx(FileUploadField, { title: `${field.required ? "*" : ""}${field.name}`, name: `${namesStr}${field.form_id}_${field.id}`, hint: field.hint, readOnly: readOnly, defaultValue: defaultValue?.value, download: true }, field.id));
43
67
  case "image":
44
- return (_jsx(FileUploadField, { title: `${field.required ? "*" : ""}${field.name}`, name: `${namesStr}${field.form_id}_${field.id}`, hint: field.hint, showAvailableTypes: true, fileTypes: ["png", "jpg", "jpeg"], disabled: readOnly, defaultValue: defaultValue?.value }, field.id));
68
+ return (_jsx(FileUploadField, { title: `${field.required ? "*" : ""}${field.name}`, name: `${namesStr}${field.form_id}_${field.id}`, hint: field.hint, showAvailableTypes: true, fileTypes: ["png", "jpg", "jpeg"], readOnly: readOnly, defaultValue: defaultValue?.value }, field.id));
45
69
  case "pdf":
46
- return (_jsx(FileUploadField, { title: `${field.required ? "*" : ""}${field.name}`, name: `${namesStr}${field.form_id}_${field.id}`, hint: field.hint, showAvailableTypes: true, fileTypes: ["pdf"], disabled: readOnly, defaultValue: defaultValue?.value, download: true }, field.id));
70
+ return (_jsx(FileUploadField, { title: `${field.required ? "*" : ""}${field.name}`, name: `${namesStr}${field.form_id}_${field.id}`, hint: field.hint, showAvailableTypes: true, fileTypes: ["pdf"], readOnly: readOnly, defaultValue: defaultValue?.value, download: true }, field.id));
47
71
  case "email":
48
72
  case "url":
49
73
  case "text":