@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 +8 -0
- package/dist/DynamicTable/GenericDynamicTable.d.ts +48 -4
- package/dist/Form/DynamicBaculoForm/index.d.ts +10 -1
- package/dist/Form/FileUpload/index.d.ts +0 -1
- package/dist/Modal/ItemModal.d.ts +16 -5
- package/dist/NavBar/NavBar.types.d.ts +2 -1
- package/dist/NavBar/NavBar.utils.d.ts +7 -0
- package/dist/esm/Buttons/Button.js +11 -7
- package/dist/esm/DynamicTable/GenericDynamicTable.js +78 -19
- package/dist/esm/Form/DynamicBaculoForm/index.js +29 -5
- package/dist/esm/Form/FileUpload/index.js +59 -52
- package/dist/esm/Form/ReactZodForm/index.js +11 -3
- package/dist/esm/Modal/ItemModal.js +93 -22
- package/dist/esm/NavBar/NavBar.js +14 -4
- package/dist/esm/NavBar/NavBar.utils.js +7 -0
- package/package.json +3 -3
- package/src/Buttons/Button.tsx +6 -1
- package/src/DynamicTable/GenericDynamicTable.tsx +140 -39
- package/src/Form/DynamicBaculoForm/index.tsx +32 -6
- package/src/Form/FileUpload/index.tsx +100 -77
- package/src/Form/ReactZodForm/index.tsx +16 -3
- package/src/Modal/ItemModal.tsx +351 -96
- package/src/NavBar/NavBar.tsx +53 -48
- package/src/NavBar/NavBar.types.ts +9 -1
- package/src/NavBar/NavBar.utils.ts +7 -0
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
|
|
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
|
-
{
|
|
74
|
-
|
|
75
|
-
|
|
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;
|
|
@@ -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
|
|
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:
|
|
15
|
-
onClose:
|
|
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:
|
|
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 = (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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: {} };
|
|
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
|
-
{
|
|
64
|
-
|
|
65
|
-
|
|
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,
|
|
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
|
|
137
|
+
x.type === "label" || x.type === "textField"
|
|
101
138
|
? ""
|
|
102
|
-
: x.type
|
|
139
|
+
: x.type === "selectField"
|
|
103
140
|
? 0
|
|
104
|
-
: x.type
|
|
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: [
|
|
137
|
-
const ancho = column.width ??
|
|
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
|
-
}),
|
|
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: [
|
|
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
|
-
|
|
151
|
-
|
|
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,
|
|
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() }),
|
|
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
|
|
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
|
-
}),
|
|
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 },
|
|
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
|
|
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,
|
|
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"],
|
|
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"],
|
|
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":
|