@zauru-sdk/components 1.0.54 → 1.0.60

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.
Files changed (94) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/LICENCE.md +11 -11
  3. package/package.json +7 -7
  4. package/src/Alerts/ErrorBoundaryAlert/ErrorBoundaryAlert.tsx +66 -66
  5. package/src/Alerts/StaticAlert.tsx +121 -121
  6. package/src/Alerts/index.ts +2 -2
  7. package/src/BlockUI/BlockUI.tsx +50 -50
  8. package/src/BlockUI/index.tsx +1 -1
  9. package/src/Buttons/Button.tsx +90 -90
  10. package/src/Buttons/index.ts +1 -1
  11. package/src/Card/Card.tsx +24 -24
  12. package/src/Card/index.ts +1 -1
  13. package/src/Chat/ChatLayout.tsx +131 -131
  14. package/src/Chat/ChatMessageHistory.tsx +142 -142
  15. package/src/Chat/index.ts +2 -2
  16. package/src/ConnectionState/ConnectionState.tsx +27 -27
  17. package/src/ConnectionState/index.tsx +1 -1
  18. package/src/Containers/BodyContainer.tsx +14 -14
  19. package/src/Containers/ButtonSectionContainer.tsx +21 -21
  20. package/src/Containers/Container.tsx +39 -39
  21. package/src/Containers/DoubleContainer.tsx +48 -48
  22. package/src/Containers/MainContainer.tsx +17 -17
  23. package/src/Containers/OutletContainer.tsx +14 -14
  24. package/src/Containers/SubContainer.tsx +37 -37
  25. package/src/Containers/index.ts +7 -7
  26. package/src/DynamicTable/BasicPrintDynamicTable.tsx +73 -73
  27. package/src/DynamicTable/DynamicPrintTable.tsx +288 -288
  28. package/src/DynamicTable/DynamicTable.tsx +405 -405
  29. package/src/DynamicTable/GenericDynamicTable.tsx +456 -456
  30. package/src/DynamicTable/index.tsx +4 -4
  31. package/src/Footer/Footer.tsx +50 -50
  32. package/src/Footer/index.tsx +1 -1
  33. package/src/Form/Checkbox/index.tsx +96 -96
  34. package/src/Form/Checklist/index.tsx +35 -35
  35. package/src/Form/DatePicker/index.tsx +132 -132
  36. package/src/Form/DynamicBaculoForm/index.tsx +361 -361
  37. package/src/Form/FieldContainer/DoubleFieldContainer.tsx +35 -35
  38. package/src/Form/FieldContainer/QuadrupleFieldContainer.tsx +36 -36
  39. package/src/Form/FieldContainer/TripleFieldContainer.tsx +35 -35
  40. package/src/Form/FieldContainer/index.ts +3 -3
  41. package/src/Form/FileUpload/index.tsx +184 -184
  42. package/src/Form/FormButtons/index.tsx +78 -78
  43. package/src/Form/FormLayout/index.tsx +37 -37
  44. package/src/Form/SelectField/index.tsx +237 -237
  45. package/src/Form/TextArea/index.tsx +125 -125
  46. package/src/Form/TextField/index.tsx +194 -194
  47. package/src/Form/TimePicker/index.tsx +127 -127
  48. package/src/Form/YesNo/index.tsx +77 -77
  49. package/src/Form/index.ts +13 -13
  50. package/src/Labels/InfoLabel/index.tsx +21 -21
  51. package/src/Labels/index.tsx +1 -1
  52. package/src/Layouts/homeLayout/index.tsx +34 -34
  53. package/src/Layouts/index.ts +1 -1
  54. package/src/LineSeparator/LineSeparator.tsx +3 -3
  55. package/src/LineSeparator/index.tsx +1 -1
  56. package/src/Modal/Modal.tsx +104 -104
  57. package/src/Modal/index.tsx +1 -1
  58. package/src/NavBar/NavBar.tsx +223 -223
  59. package/src/NavBar/NavBar.types.ts +64 -64
  60. package/src/NavBar/NavBar.utils.ts +58 -58
  61. package/src/NavBar/index.tsx +5 -5
  62. package/src/ProgressBar/ProgressBar.tsx +25 -25
  63. package/src/ProgressBar/ProgressCircle.tsx +75 -75
  64. package/src/ProgressBar/index.tsx +2 -2
  65. package/src/Skeletons/LoadingInputSkeleton.tsx +12 -12
  66. package/src/Skeletons/index.ts +1 -1
  67. package/src/Tab/Tab.tsx +63 -63
  68. package/src/Tab/index.ts +1 -1
  69. package/src/Table/ZauruTable.tsx +265 -265
  70. package/src/Table/index.tsx +1 -1
  71. package/src/TaskList/TaskList.tsx +88 -88
  72. package/src/TaskList/index.ts +1 -1
  73. package/src/Titles/LabelArray.tsx +17 -17
  74. package/src/Titles/TableColumnTitle.tsx +9 -9
  75. package/src/Titles/TitleH1.tsx +10 -10
  76. package/src/Titles/TitleH2.tsx +10 -10
  77. package/src/Titles/TitleH3.tsx +10 -10
  78. package/src/Titles/index.ts +5 -5
  79. package/src/Tooltip/Tooltip.tsx +42 -42
  80. package/src/Tooltip/index.ts +1 -1
  81. package/src/WithTooltip/WithTooltip.tsx +21 -21
  82. package/src/WithTooltip/index.tsx +1 -1
  83. package/src/Wizards/StepWizard.tsx +88 -88
  84. package/src/Wizards/index.ts +1 -1
  85. package/src/Zendesk/Chat.tsx +83 -83
  86. package/src/Zendesk/index.ts +2 -2
  87. package/src/Zendesk/zendesk.config.ts +40 -40
  88. package/src/index.ts +24 -24
  89. package/src/postcss.config.mjs +5 -5
  90. package/src/tailwind.config.ts +10 -10
  91. package/src/tailwind.css +3 -3
  92. package/tsconfig.cjs.json +8 -8
  93. package/tsconfig.esm.json +11 -11
  94. package/tsconfig.json +17 -17
@@ -1,456 +1,456 @@
1
- import React, { useEffect, useState } from "react";
2
- import { SelectFieldWithoutValidation } from "../Form/SelectField/index.js";
3
- import { TextFieldWithoutValidation } from "../Form/TextField/index.js";
4
- import { CheckboxWithoutValidation } from "../Form/Checkbox/index.js";
5
- import { createModal } from "../Modal/index.js";
6
- import { Button } from "../Buttons/index.js";
7
- import {
8
- GenericDynamicTableColumn,
9
- RowDataType,
10
- SelectFieldOption,
11
- } from "@zauru-sdk/types";
12
- import { useAppSelector } from "@zauru-sdk/redux";
13
- import { generateClientUUID } from "@zauru-sdk/common";
14
- import { LoadingInputSkeleton } from "../Skeletons/index.js";
15
- import { WithTooltip } from "../WithTooltip/index.js";
16
- import { TrashSvg } from "@zauru-sdk/icons";
17
-
18
- type Props = {
19
- name?: string;
20
- className?: string;
21
- columns: GenericDynamicTableColumn[];
22
- onChange?: (tableState?: any[]) => void;
23
- defaultValue?: RowDataType[];
24
- footerRow?: RowDataType;
25
- thCSSProperties?: React.CSSProperties;
26
- thElementsClassName?: string;
27
- editable?: boolean;
28
- searcheables?: SelectFieldOption[];
29
- loading?: boolean;
30
- paginated?: boolean;
31
- defaultItemsPerPage?: number;
32
- itemsPerPageOptions?: number[];
33
- withoutBg?: boolean;
34
- };
35
-
36
- const GenericDynamicTableErrorComponent = ({
37
- name,
38
- formName,
39
- }: {
40
- name: string;
41
- formName?: string;
42
- }) => {
43
- const { formValidations } = useAppSelector((state) => state.formValidation);
44
- const error = formValidations[formName ?? "-1"]?.[name ?? "-1"];
45
-
46
- return error ? (
47
- <p className={`mt-2 text-sm text-red-600 dark:text-red-500`}>
48
- <span className="font-medium">Oops!</span> {error}
49
- </p>
50
- ) : (
51
- <></>
52
- );
53
- };
54
-
55
- export const GenericDynamicTable = (props: Props) => {
56
- const {
57
- columns,
58
- onChange,
59
- className,
60
- footerRow,
61
- defaultValue = [],
62
- thCSSProperties,
63
- thElementsClassName = "",
64
- editable = true,
65
- searcheables = [],
66
- loading = false,
67
- paginated = true,
68
- defaultItemsPerPage = 10,
69
- itemsPerPageOptions = [10, 50, 100],
70
- name,
71
- withoutBg = false,
72
- } = props;
73
-
74
- const [tableData, setTableData] = useState<RowDataType[]>(defaultValue);
75
- const [deletedData, setDeletedData] = useState<RowDataType[]>([]);
76
- const [search, setSearch] = useState("");
77
- const [filteredTableData, setFilteredTableData] =
78
- useState<RowDataType[]>(tableData);
79
- const [currentPage, setCurrentPage] = useState(1);
80
- const [itemsPerPage, setItemsPerPage] = useState(defaultItemsPerPage);
81
-
82
- useEffect(() => {
83
- if (defaultValue.length) {
84
- setTableData(defaultValue);
85
- }
86
- }, []);
87
-
88
- useEffect(() => {
89
- setFilteredTableData(tableData);
90
- }, [tableData]);
91
-
92
- useEffect(() => {
93
- changeFilteredData();
94
- }, [tableData, search]);
95
-
96
- const totalPages = () => {
97
- return Math.ceil(filteredTableData.length / itemsPerPage);
98
- };
99
-
100
- const addRow = () => {
101
- const defs: { [key: string]: any } = {};
102
- columns.forEach((x) => {
103
- defs[`${x.name}`] =
104
- x.type == "label" || x.type == "textField"
105
- ? ""
106
- : x.type == "selectField"
107
- ? 0
108
- : x.type == "checkbox"
109
- ? false
110
- : 0;
111
- });
112
- setTableData((prevData) => [
113
- ...prevData,
114
- { id: generateClientUUID(), ...defs },
115
- ]);
116
- };
117
-
118
- const removeRow = (rowId: string) => {
119
- const newDeletedData = [...deletedData];
120
- const deletedItem = tableData?.find((x) => x.id === rowId);
121
- if (deletedItem && !isNaN(deletedItem.id)) {
122
- newDeletedData.push(deletedItem);
123
- }
124
- setDeletedData(newDeletedData);
125
- setTableData((prevData) => prevData?.filter((x) => x.id !== rowId));
126
- };
127
-
128
- const handleChange = (name: string, value: any, rowId: string) => {
129
- // Encontrar el índice de la fila que está cambiando
130
- const rowIndex = tableData.findIndex((x) => x.id === rowId);
131
-
132
- // Crear una copia del objeto en esa fila
133
- const updatedRow = { ...tableData[rowIndex], [name]: value };
134
-
135
- // Copiar todo el array
136
- const updatedData = [...tableData];
137
-
138
- // Reemplazar el objeto en la fila que cambió
139
- updatedData[rowIndex] = updatedRow;
140
-
141
- // Actualizar el estado con el nuevo array
142
- setTableData(updatedData);
143
- onChange && onChange(updatedData);
144
- };
145
-
146
- const renderHeader = () => (
147
- <tr style={{ ...thCSSProperties }}>
148
- {columns.map((column, index) => {
149
- const ancho =
150
- column.width ?? (editable ? 94 : 100) / (columns.length ?? 1);
151
- return (
152
- <th
153
- key={index}
154
- className={`text-left align-middle p-2 ${thElementsClassName}`}
155
- style={{ width: `${ancho}%` }}
156
- >
157
- {column.label}
158
- </th>
159
- );
160
- })}
161
- {editable && <th style={{ width: "4%" }}></th>}
162
- </tr>
163
- );
164
-
165
- const renderRow = (rowData: RowDataType, index: number) => {
166
- return (
167
- <tr
168
- key={rowData.id}
169
- className={index % 2 === 0 ? `${withoutBg ? "" : "bg-gray-200"}` : ""}
170
- >
171
- {columns.map((column) => {
172
- if (loading) {
173
- return (
174
- <td
175
- key={`${rowData.id}-${column.name}`}
176
- className="align-middle p-1"
177
- >
178
- <LoadingInputSkeleton />
179
- </td>
180
- );
181
- }
182
- const tempVal = rowData[column.name as any];
183
-
184
- const defaultVal =
185
- column.type === "selectField"
186
- ? column.options?.find((x) => x.value === tempVal)
187
- : tempVal;
188
-
189
- if (column.type === "label") {
190
- return (
191
- <td
192
- key={`${rowData.id}-${column.name}`}
193
- className="align-middle p-1"
194
- >
195
- <div>{defaultVal}</div>
196
- </td>
197
- );
198
- }
199
-
200
- const FieldComponent =
201
- column.type === "textField"
202
- ? TextFieldWithoutValidation
203
- : column.type === "checkbox"
204
- ? CheckboxWithoutValidation
205
- : SelectFieldWithoutValidation;
206
-
207
- const setTableValue = (columnName: string, newValue: any) => {
208
- setTableData((prevState) => {
209
- // Encontrar el índice de la fila que está cambiando
210
- const rowIndex = prevState.findIndex((x) => x.id === rowData.id);
211
- // Crear una copia del objeto en esa fila
212
- const updatedRow = {
213
- ...prevState[rowIndex],
214
- [columnName]: newValue,
215
- };
216
- // Copiar todo el array
217
- const updatedData = [...prevState];
218
-
219
- // Reemplazar el objeto en la fila que cambió
220
- updatedData[rowIndex] = updatedRow;
221
- return updatedData;
222
- });
223
- };
224
-
225
- return (
226
- <td
227
- key={`${rowData.id}-${column.name}`}
228
- className="align-middle p-1"
229
- >
230
- {column.loadingOptions ? (
231
- <LoadingInputSkeleton />
232
- ) : (
233
- <FieldComponent
234
- key={`${rowData.id}-${column.name}`}
235
- //name={column.name}
236
- type={column.textFieldType}
237
- integer={!!column.integer}
238
- disabled={column.disabled}
239
- isClearable
240
- onChange={(value: any) => {
241
- const sendValue = value?.value ?? value;
242
- handleChange(column.name, sendValue, rowData.id);
243
- column.onChange &&
244
- column.onChange(rowData, sendValue, setTableValue);
245
- }}
246
- defaultValue={defaultVal}
247
- options={column.options ?? []}
248
- />
249
- )}
250
- </td>
251
- );
252
- })}
253
- {editable && (
254
- <td className="align-middle w-16">
255
- <WithTooltip text="Eliminar">
256
- <button
257
- className="bg-red-500 hover:bg-red-600 font-bold py-1 px-2 rounded ml-2"
258
- onClick={(
259
- event: React.MouseEvent<HTMLButtonElement, MouseEvent>
260
- ) => {
261
- event.preventDefault();
262
- event.stopPropagation();
263
- createModal({
264
- title: "¿Está seguro que quiere eliminar este registro?",
265
- description:
266
- "Una vez eliminada la información no podrá ser recuperada.",
267
- }).then((response) => {
268
- if (response === "OK") {
269
- removeRow(rowData.id);
270
- }
271
- });
272
- }}
273
- type="button"
274
- >
275
- <TrashSvg />
276
- </button>
277
- </WithTooltip>
278
- </td>
279
- )}
280
- </tr>
281
- );
282
- };
283
-
284
- const renderRows = () => {
285
- let mapeable = filteredTableData.slice(
286
- (currentPage - 1) * itemsPerPage,
287
- currentPage * itemsPerPage
288
- );
289
-
290
- if (loading) {
291
- mapeable = [
292
- { id: 1 },
293
- { id: 2 },
294
- { id: 3 },
295
- { id: 4 },
296
- { id: 5 },
297
- { id: 6 },
298
- { id: 7 },
299
- { id: 8 },
300
- { id: 9 },
301
- { id: 10 },
302
- ] as RowDataType[];
303
- }
304
-
305
- return mapeable.map((rowData, index) => renderRow(rowData, index));
306
- };
307
-
308
- const handleChangeSearch = (newSearch: string) => {
309
- setSearch(newSearch);
310
- };
311
-
312
- const changeFilteredData = () => {
313
- if (search) {
314
- const filteredData = tableData.filter((rowData) => {
315
- for (const searchable of searcheables) {
316
- const column = columns.find((col) => col.name === searchable.value);
317
- if (column) {
318
- const tempVal = rowData[column.name as any];
319
- const defaultVal =
320
- column.type === "selectField"
321
- ? column.options?.find((x) => x.value === tempVal)?.label
322
- : tempVal;
323
- if (
324
- defaultVal
325
- ?.toString()
326
- .toLowerCase()
327
- .includes(search.toLowerCase())
328
- ) {
329
- return true;
330
- }
331
- }
332
- }
333
- return false;
334
- });
335
- setFilteredTableData(filteredData);
336
- } else {
337
- setFilteredTableData(tableData);
338
- }
339
- };
340
-
341
- return (
342
- <>
343
- {name && (
344
- <>
345
- <GenericDynamicTableErrorComponent name={name} />
346
- <input
347
- name={name}
348
- type="hidden"
349
- value={JSON.stringify(tableData)}
350
- hidden
351
- />
352
- <input
353
- name={`deleted_${name}`}
354
- type="hidden"
355
- value={JSON.stringify(deletedData)}
356
- hidden
357
- />
358
- </>
359
- )}
360
- <div className={`${className}`}>
361
- {searcheables.length > 0 && (
362
- <div>
363
- <TextFieldWithoutValidation
364
- className="mb-2"
365
- name="search"
366
- title={`Buscar por: ${searcheables
367
- .map((x) => x.label)
368
- .join(", ")}`}
369
- onChange={handleChangeSearch}
370
- disabled={loading}
371
- />
372
- </div>
373
- )}
374
- <table className="w-full">
375
- <thead>{renderHeader()}</thead>
376
- <tbody>{renderRows()}</tbody>
377
- {footerRow && !editable ? (
378
- <tfoot className="border-t-2 border-black">
379
- <tr>
380
- {Object.keys(footerRow ?? {})?.map((x, indx) => {
381
- return (
382
- <td className="align-middle" key={indx}>
383
- {(footerRow as any)[x]}
384
- </td>
385
- );
386
- })}
387
- </tr>
388
- </tfoot>
389
- ) : editable ? (
390
- <tfoot>
391
- <tr>
392
- <td className="align-middle">
393
- <button
394
- className="bg-blue-500 hover:bg-blue-600 font-bold py-2 px-4 rounded"
395
- onClick={(
396
- event: React.MouseEvent<HTMLButtonElement, MouseEvent>
397
- ) => {
398
- event.preventDefault();
399
- event.stopPropagation();
400
- addRow();
401
- }}
402
- type="button"
403
- >
404
- +
405
- </button>
406
- </td>
407
- </tr>
408
- </tfoot>
409
- ) : (
410
- <></>
411
- )}
412
- </table>
413
- {paginated && totalPages() > 1 && (
414
- <div className="flex justify-between items-center mt-4">
415
- <div className="flex items-center">
416
- <Button
417
- type="button"
418
- disabled={currentPage === 1}
419
- onClickSave={() =>
420
- setCurrentPage((old) => Math.max(old - 1, 1))
421
- }
422
- >
423
- Anterior
424
- </Button>
425
- <span className="mx-2">{`Página ${currentPage} de ${totalPages()}`}</span>
426
- <Button
427
- type="button"
428
- disabled={currentPage === totalPages()}
429
- onClickSave={() =>
430
- setCurrentPage((old) => Math.min(old + 1, totalPages()))
431
- }
432
- >
433
- Siguiente
434
- </Button>
435
- </div>
436
- <div>
437
- <select
438
- value={itemsPerPage}
439
- onChange={(e) => {
440
- setItemsPerPage(Number(e.target.value));
441
- setCurrentPage(1); // resetear la página al cambiar los elementos por página
442
- }}
443
- >
444
- {itemsPerPageOptions.map((option) => (
445
- <option key={option} value={option}>
446
- {option} elementos por página
447
- </option>
448
- ))}
449
- </select>
450
- </div>
451
- </div>
452
- )}
453
- </div>
454
- </>
455
- );
456
- };
1
+ import React, { useEffect, useState } from "react";
2
+ import { SelectFieldWithoutValidation } from "../Form/SelectField/index.js";
3
+ import { TextFieldWithoutValidation } from "../Form/TextField/index.js";
4
+ import { CheckboxWithoutValidation } from "../Form/Checkbox/index.js";
5
+ import { createModal } from "../Modal/index.js";
6
+ import { Button } from "../Buttons/index.js";
7
+ import {
8
+ GenericDynamicTableColumn,
9
+ RowDataType,
10
+ SelectFieldOption,
11
+ } from "@zauru-sdk/types";
12
+ import { useAppSelector } from "@zauru-sdk/redux";
13
+ import { generateClientUUID } from "@zauru-sdk/common";
14
+ import { LoadingInputSkeleton } from "../Skeletons/index.js";
15
+ import { WithTooltip } from "../WithTooltip/index.js";
16
+ import { TrashSvg } from "@zauru-sdk/icons";
17
+
18
+ type Props = {
19
+ name?: string;
20
+ className?: string;
21
+ columns: GenericDynamicTableColumn[];
22
+ onChange?: (tableState?: any[]) => void;
23
+ defaultValue?: RowDataType[];
24
+ footerRow?: RowDataType;
25
+ thCSSProperties?: React.CSSProperties;
26
+ thElementsClassName?: string;
27
+ editable?: boolean;
28
+ searcheables?: SelectFieldOption[];
29
+ loading?: boolean;
30
+ paginated?: boolean;
31
+ defaultItemsPerPage?: number;
32
+ itemsPerPageOptions?: number[];
33
+ withoutBg?: boolean;
34
+ };
35
+
36
+ const GenericDynamicTableErrorComponent = ({
37
+ name,
38
+ formName,
39
+ }: {
40
+ name: string;
41
+ formName?: string;
42
+ }) => {
43
+ const { formValidations } = useAppSelector((state) => state.formValidation);
44
+ const error = formValidations[formName ?? "-1"]?.[name ?? "-1"];
45
+
46
+ return error ? (
47
+ <p className={`mt-2 text-sm text-red-600 dark:text-red-500`}>
48
+ <span className="font-medium">Oops!</span> {error}
49
+ </p>
50
+ ) : (
51
+ <></>
52
+ );
53
+ };
54
+
55
+ export const GenericDynamicTable = (props: Props) => {
56
+ const {
57
+ columns,
58
+ onChange,
59
+ className,
60
+ footerRow,
61
+ defaultValue = [],
62
+ thCSSProperties,
63
+ thElementsClassName = "",
64
+ editable = true,
65
+ searcheables = [],
66
+ loading = false,
67
+ paginated = true,
68
+ defaultItemsPerPage = 10,
69
+ itemsPerPageOptions = [10, 50, 100],
70
+ name,
71
+ withoutBg = false,
72
+ } = props;
73
+
74
+ const [tableData, setTableData] = useState<RowDataType[]>(defaultValue);
75
+ const [deletedData, setDeletedData] = useState<RowDataType[]>([]);
76
+ const [search, setSearch] = useState("");
77
+ const [filteredTableData, setFilteredTableData] =
78
+ useState<RowDataType[]>(tableData);
79
+ const [currentPage, setCurrentPage] = useState(1);
80
+ const [itemsPerPage, setItemsPerPage] = useState(defaultItemsPerPage);
81
+
82
+ useEffect(() => {
83
+ if (defaultValue.length) {
84
+ setTableData(defaultValue);
85
+ }
86
+ }, []);
87
+
88
+ useEffect(() => {
89
+ setFilteredTableData(tableData);
90
+ }, [tableData]);
91
+
92
+ useEffect(() => {
93
+ changeFilteredData();
94
+ }, [tableData, search]);
95
+
96
+ const totalPages = () => {
97
+ return Math.ceil(filteredTableData.length / itemsPerPage);
98
+ };
99
+
100
+ const addRow = () => {
101
+ const defs: { [key: string]: any } = {};
102
+ columns.forEach((x) => {
103
+ defs[`${x.name}`] =
104
+ x.type == "label" || x.type == "textField"
105
+ ? ""
106
+ : x.type == "selectField"
107
+ ? 0
108
+ : x.type == "checkbox"
109
+ ? false
110
+ : 0;
111
+ });
112
+ setTableData((prevData) => [
113
+ ...prevData,
114
+ { id: generateClientUUID(), ...defs },
115
+ ]);
116
+ };
117
+
118
+ const removeRow = (rowId: string) => {
119
+ const newDeletedData = [...deletedData];
120
+ const deletedItem = tableData?.find((x) => x.id === rowId);
121
+ if (deletedItem && !isNaN(deletedItem.id)) {
122
+ newDeletedData.push(deletedItem);
123
+ }
124
+ setDeletedData(newDeletedData);
125
+ setTableData((prevData) => prevData?.filter((x) => x.id !== rowId));
126
+ };
127
+
128
+ const handleChange = (name: string, value: any, rowId: string) => {
129
+ // Encontrar el índice de la fila que está cambiando
130
+ const rowIndex = tableData.findIndex((x) => x.id === rowId);
131
+
132
+ // Crear una copia del objeto en esa fila
133
+ const updatedRow = { ...tableData[rowIndex], [name]: value };
134
+
135
+ // Copiar todo el array
136
+ const updatedData = [...tableData];
137
+
138
+ // Reemplazar el objeto en la fila que cambió
139
+ updatedData[rowIndex] = updatedRow;
140
+
141
+ // Actualizar el estado con el nuevo array
142
+ setTableData(updatedData);
143
+ onChange && onChange(updatedData);
144
+ };
145
+
146
+ const renderHeader = () => (
147
+ <tr style={{ ...thCSSProperties }}>
148
+ {columns.map((column, index) => {
149
+ const ancho =
150
+ column.width ?? (editable ? 94 : 100) / (columns.length ?? 1);
151
+ return (
152
+ <th
153
+ key={index}
154
+ className={`text-left align-middle p-2 ${thElementsClassName}`}
155
+ style={{ width: `${ancho}%` }}
156
+ >
157
+ {column.label}
158
+ </th>
159
+ );
160
+ })}
161
+ {editable && <th style={{ width: "4%" }}></th>}
162
+ </tr>
163
+ );
164
+
165
+ const renderRow = (rowData: RowDataType, index: number) => {
166
+ return (
167
+ <tr
168
+ key={rowData.id}
169
+ className={index % 2 === 0 ? `${withoutBg ? "" : "bg-gray-200"}` : ""}
170
+ >
171
+ {columns.map((column) => {
172
+ if (loading) {
173
+ return (
174
+ <td
175
+ key={`${rowData.id}-${column.name}`}
176
+ className="align-middle p-1"
177
+ >
178
+ <LoadingInputSkeleton />
179
+ </td>
180
+ );
181
+ }
182
+ const tempVal = rowData[column.name as any];
183
+
184
+ const defaultVal =
185
+ column.type === "selectField"
186
+ ? column.options?.find((x) => x.value === tempVal)
187
+ : tempVal;
188
+
189
+ if (column.type === "label") {
190
+ return (
191
+ <td
192
+ key={`${rowData.id}-${column.name}`}
193
+ className="align-middle p-1"
194
+ >
195
+ <div>{defaultVal}</div>
196
+ </td>
197
+ );
198
+ }
199
+
200
+ const FieldComponent =
201
+ column.type === "textField"
202
+ ? TextFieldWithoutValidation
203
+ : column.type === "checkbox"
204
+ ? CheckboxWithoutValidation
205
+ : SelectFieldWithoutValidation;
206
+
207
+ const setTableValue = (columnName: string, newValue: any) => {
208
+ setTableData((prevState) => {
209
+ // Encontrar el índice de la fila que está cambiando
210
+ const rowIndex = prevState.findIndex((x) => x.id === rowData.id);
211
+ // Crear una copia del objeto en esa fila
212
+ const updatedRow = {
213
+ ...prevState[rowIndex],
214
+ [columnName]: newValue,
215
+ };
216
+ // Copiar todo el array
217
+ const updatedData = [...prevState];
218
+
219
+ // Reemplazar el objeto en la fila que cambió
220
+ updatedData[rowIndex] = updatedRow;
221
+ return updatedData;
222
+ });
223
+ };
224
+
225
+ return (
226
+ <td
227
+ key={`${rowData.id}-${column.name}`}
228
+ className="align-middle p-1"
229
+ >
230
+ {column.loadingOptions ? (
231
+ <LoadingInputSkeleton />
232
+ ) : (
233
+ <FieldComponent
234
+ key={`${rowData.id}-${column.name}`}
235
+ //name={column.name}
236
+ type={column.textFieldType}
237
+ integer={!!column.integer}
238
+ disabled={column.disabled}
239
+ isClearable
240
+ onChange={(value: any) => {
241
+ const sendValue = value?.value ?? value;
242
+ handleChange(column.name, sendValue, rowData.id);
243
+ column.onChange &&
244
+ column.onChange(rowData, sendValue, setTableValue);
245
+ }}
246
+ defaultValue={defaultVal}
247
+ options={column.options ?? []}
248
+ />
249
+ )}
250
+ </td>
251
+ );
252
+ })}
253
+ {editable && (
254
+ <td className="align-middle w-16">
255
+ <WithTooltip text="Eliminar">
256
+ <button
257
+ className="bg-red-500 hover:bg-red-600 font-bold py-1 px-2 rounded ml-2"
258
+ onClick={(
259
+ event: React.MouseEvent<HTMLButtonElement, MouseEvent>
260
+ ) => {
261
+ event.preventDefault();
262
+ event.stopPropagation();
263
+ createModal({
264
+ title: "¿Está seguro que quiere eliminar este registro?",
265
+ description:
266
+ "Una vez eliminada la información no podrá ser recuperada.",
267
+ }).then((response) => {
268
+ if (response === "OK") {
269
+ removeRow(rowData.id);
270
+ }
271
+ });
272
+ }}
273
+ type="button"
274
+ >
275
+ <TrashSvg />
276
+ </button>
277
+ </WithTooltip>
278
+ </td>
279
+ )}
280
+ </tr>
281
+ );
282
+ };
283
+
284
+ const renderRows = () => {
285
+ let mapeable = filteredTableData.slice(
286
+ (currentPage - 1) * itemsPerPage,
287
+ currentPage * itemsPerPage
288
+ );
289
+
290
+ if (loading) {
291
+ mapeable = [
292
+ { id: 1 },
293
+ { id: 2 },
294
+ { id: 3 },
295
+ { id: 4 },
296
+ { id: 5 },
297
+ { id: 6 },
298
+ { id: 7 },
299
+ { id: 8 },
300
+ { id: 9 },
301
+ { id: 10 },
302
+ ] as RowDataType[];
303
+ }
304
+
305
+ return mapeable.map((rowData, index) => renderRow(rowData, index));
306
+ };
307
+
308
+ const handleChangeSearch = (newSearch: string) => {
309
+ setSearch(newSearch);
310
+ };
311
+
312
+ const changeFilteredData = () => {
313
+ if (search) {
314
+ const filteredData = tableData.filter((rowData) => {
315
+ for (const searchable of searcheables) {
316
+ const column = columns.find((col) => col.name === searchable.value);
317
+ if (column) {
318
+ const tempVal = rowData[column.name as any];
319
+ const defaultVal =
320
+ column.type === "selectField"
321
+ ? column.options?.find((x) => x.value === tempVal)?.label
322
+ : tempVal;
323
+ if (
324
+ defaultVal
325
+ ?.toString()
326
+ .toLowerCase()
327
+ .includes(search.toLowerCase())
328
+ ) {
329
+ return true;
330
+ }
331
+ }
332
+ }
333
+ return false;
334
+ });
335
+ setFilteredTableData(filteredData);
336
+ } else {
337
+ setFilteredTableData(tableData);
338
+ }
339
+ };
340
+
341
+ return (
342
+ <>
343
+ {name && (
344
+ <>
345
+ <GenericDynamicTableErrorComponent name={name} />
346
+ <input
347
+ name={name}
348
+ type="hidden"
349
+ value={JSON.stringify(tableData)}
350
+ hidden
351
+ />
352
+ <input
353
+ name={`deleted_${name}`}
354
+ type="hidden"
355
+ value={JSON.stringify(deletedData)}
356
+ hidden
357
+ />
358
+ </>
359
+ )}
360
+ <div className={`${className}`}>
361
+ {searcheables.length > 0 && (
362
+ <div>
363
+ <TextFieldWithoutValidation
364
+ className="mb-2"
365
+ name="search"
366
+ title={`Buscar por: ${searcheables
367
+ .map((x) => x.label)
368
+ .join(", ")}`}
369
+ onChange={handleChangeSearch}
370
+ disabled={loading}
371
+ />
372
+ </div>
373
+ )}
374
+ <table className="w-full">
375
+ <thead>{renderHeader()}</thead>
376
+ <tbody>{renderRows()}</tbody>
377
+ {footerRow && !editable ? (
378
+ <tfoot className="border-t-2 border-black">
379
+ <tr>
380
+ {Object.keys(footerRow ?? {})?.map((x, indx) => {
381
+ return (
382
+ <td className="align-middle" key={indx}>
383
+ {(footerRow as any)[x]}
384
+ </td>
385
+ );
386
+ })}
387
+ </tr>
388
+ </tfoot>
389
+ ) : editable ? (
390
+ <tfoot>
391
+ <tr>
392
+ <td className="align-middle">
393
+ <button
394
+ className="bg-blue-500 hover:bg-blue-600 font-bold py-2 px-4 rounded"
395
+ onClick={(
396
+ event: React.MouseEvent<HTMLButtonElement, MouseEvent>
397
+ ) => {
398
+ event.preventDefault();
399
+ event.stopPropagation();
400
+ addRow();
401
+ }}
402
+ type="button"
403
+ >
404
+ +
405
+ </button>
406
+ </td>
407
+ </tr>
408
+ </tfoot>
409
+ ) : (
410
+ <></>
411
+ )}
412
+ </table>
413
+ {paginated && totalPages() > 1 && (
414
+ <div className="flex justify-between items-center mt-4">
415
+ <div className="flex items-center">
416
+ <Button
417
+ type="button"
418
+ disabled={currentPage === 1}
419
+ onClickSave={() =>
420
+ setCurrentPage((old) => Math.max(old - 1, 1))
421
+ }
422
+ >
423
+ Anterior
424
+ </Button>
425
+ <span className="mx-2">{`Página ${currentPage} de ${totalPages()}`}</span>
426
+ <Button
427
+ type="button"
428
+ disabled={currentPage === totalPages()}
429
+ onClickSave={() =>
430
+ setCurrentPage((old) => Math.min(old + 1, totalPages()))
431
+ }
432
+ >
433
+ Siguiente
434
+ </Button>
435
+ </div>
436
+ <div>
437
+ <select
438
+ value={itemsPerPage}
439
+ onChange={(e) => {
440
+ setItemsPerPage(Number(e.target.value));
441
+ setCurrentPage(1); // resetear la página al cambiar los elementos por página
442
+ }}
443
+ >
444
+ {itemsPerPageOptions.map((option) => (
445
+ <option key={option} value={option}>
446
+ {option} elementos por página
447
+ </option>
448
+ ))}
449
+ </select>
450
+ </div>
451
+ </div>
452
+ )}
453
+ </div>
454
+ </>
455
+ );
456
+ };