@zauru-sdk/components 1.0.12 → 1.0.14
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 +16 -0
- package/dist/Alerts/index.d.ts +0 -1
- package/dist/Alerts/index.js +0 -1
- package/dist/Chat/ChatLayout.d.ts +2 -2
- package/dist/Chat/ChatLayout.js +4 -3
- package/dist/Chat/ChatMessageHistory.js +1 -1
- package/dist/ConnectionState/ConnectionState.d.ts +2 -0
- package/dist/ConnectionState/ConnectionState.js +22 -0
- package/dist/ConnectionState/index.d.ts +1 -0
- package/dist/ConnectionState/index.js +1 -0
- package/dist/DynamicTable/BasicPrintDynamicTable.d.ts +10 -0
- package/dist/DynamicTable/BasicPrintDynamicTable.js +27 -0
- package/dist/DynamicTable/DynamicPrintTable.d.ts +23 -0
- package/dist/DynamicTable/DynamicPrintTable.js +132 -0
- package/dist/DynamicTable/GenericDynamicTable.d.ts +21 -0
- package/dist/DynamicTable/GenericDynamicTable.js +195 -0
- package/dist/DynamicTable/index.d.ts +24 -0
- package/dist/DynamicTable/index.js +193 -0
- package/dist/Footer/Footer.js +2 -2
- package/dist/Form/Checkbox/index.d.ts +17 -0
- package/dist/Form/Checkbox/index.js +34 -0
- package/dist/Form/Checklist/index.d.ts +14 -0
- package/dist/Form/Checklist/index.js +10 -0
- package/dist/Form/DatePicker/index.d.ts +18 -0
- package/dist/Form/DatePicker/index.js +31 -0
- package/dist/Form/DynamicBaculoForm/index.d.ts +18 -0
- package/dist/Form/DynamicBaculoForm/index.js +138 -0
- package/dist/Form/FieldContainer/DoubleFieldContainer.d.ts +8 -0
- package/dist/Form/FieldContainer/DoubleFieldContainer.js +14 -0
- package/dist/Form/FieldContainer/QuadrupleFieldContainer.d.ts +7 -0
- package/dist/Form/FieldContainer/QuadrupleFieldContainer.js +14 -0
- package/dist/Form/FieldContainer/TripleFieldContainer.d.ts +7 -0
- package/dist/Form/FieldContainer/TripleFieldContainer.js +14 -0
- package/dist/Form/FieldContainer/index.d.ts +3 -0
- package/dist/Form/FieldContainer/index.js +3 -0
- package/dist/Form/FileUpload/index.d.ts +21 -0
- package/dist/Form/FileUpload/index.js +54 -0
- package/dist/Form/FormButtons/index.d.ts +16 -0
- package/dist/Form/FormButtons/index.js +5 -0
- package/dist/Form/FormLayout/index.d.ts +11 -0
- package/dist/Form/FormLayout/index.js +7 -0
- package/dist/Form/SelectField/index.d.ts +27 -0
- package/dist/Form/SelectField/index.js +74 -0
- package/dist/Form/TextArea/index.d.ts +23 -0
- package/dist/Form/TextArea/index.js +36 -0
- package/dist/Form/TextField/index.d.ts +25 -0
- package/dist/Form/TextField/index.js +70 -0
- package/dist/Form/TimePicker/index.d.ts +16 -0
- package/dist/Form/TimePicker/index.js +31 -0
- package/dist/Form/YesNo/index.d.ts +12 -0
- package/dist/Form/YesNo/index.js +19 -0
- package/dist/Form/index.d.ts +13 -0
- package/dist/Form/index.js +13 -0
- package/dist/NavBar/NavBar.js +2 -2
- package/dist/Table/ZauruTable.js +1 -1
- package/dist/Zendesk/Chat.d.ts +2 -2
- package/dist/Zendesk/Chat.js +2 -1
- package/dist/Zendesk/zendesk.config.d.ts +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -2
- package/package.json +9 -8
- package/src/Alerts/index.ts +0 -1
- package/src/Chat/ChatLayout.tsx +133 -0
- package/src/Chat/ChatMessageHistory.tsx +142 -0
- package/src/Chat/index.ts +2 -0
- package/src/ConnectionState/ConnectionState.tsx +29 -0
- package/src/ConnectionState/index.tsx +1 -0
- package/src/DynamicTable/BasicPrintDynamicTable.tsx +73 -0
- package/src/DynamicTable/DynamicPrintTable.tsx +290 -0
- package/src/DynamicTable/GenericDynamicTable.tsx +455 -0
- package/src/DynamicTable/index.tsx +407 -0
- package/src/Footer/Footer.tsx +3 -3
- package/src/Form/Checkbox/index.tsx +96 -0
- package/src/Form/Checklist/index.tsx +35 -0
- package/src/Form/DatePicker/index.tsx +132 -0
- package/src/Form/DynamicBaculoForm/index.tsx +359 -0
- package/src/Form/FieldContainer/DoubleFieldContainer.tsx +35 -0
- package/src/Form/FieldContainer/QuadrupleFieldContainer.tsx +36 -0
- package/src/Form/FieldContainer/TripleFieldContainer.tsx +35 -0
- package/src/Form/FieldContainer/index.ts +3 -0
- package/src/Form/FileUpload/index.tsx +184 -0
- package/src/Form/FormButtons/index.tsx +78 -0
- package/src/Form/FormLayout/index.tsx +37 -0
- package/src/Form/SelectField/index.tsx +237 -0
- package/src/Form/TextArea/index.tsx +125 -0
- package/src/Form/TextField/index.tsx +194 -0
- package/src/Form/TimePicker/index.tsx +127 -0
- package/src/Form/YesNo/index.tsx +79 -0
- package/src/Form/index.ts +13 -0
- package/src/NavBar/NavBar.tsx +2 -2
- package/src/Table/ZauruTable.tsx +1 -1
- package/src/Zendesk/Chat.tsx +85 -0
- package/src/Zendesk/index.ts +2 -0
- package/src/Zendesk/zendesk.config.ts +40 -0
- package/src/index.ts +4 -2
- package/dist/Alerts/Alert.d.ts +0 -9
- package/dist/Alerts/Alert.js +0 -97
- package/dist/Icons/Icons.d.ts +0 -47
- package/dist/Icons/Icons.js +0 -110
- package/dist/Icons/StylesConstants.d.ts +0 -26
- package/dist/Icons/StylesConstants.js +0 -34
- package/src/Alerts/Alert.tsx +0 -149
- package/src/Icons/Icons.tsx +0 -782
- package/src/Icons/StylesConstants.tsx +0 -66
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import React, { useRef, useState } from "react";
|
|
2
|
+
import { TextField } from "../Form";
|
|
3
|
+
import { Form } from "@remix-run/react";
|
|
4
|
+
import { FormDocumentType } from "@zauru-sdk/types";
|
|
5
|
+
import { LoadingInputSkeleton } from "src";
|
|
6
|
+
import {
|
|
7
|
+
AttachmentIconSVG,
|
|
8
|
+
SendMessageIcon,
|
|
9
|
+
SpinnerSvg,
|
|
10
|
+
} from "@zauru-sdk/icons";
|
|
11
|
+
|
|
12
|
+
interface ChatLayoutProps {
|
|
13
|
+
children?: React.ReactNode;
|
|
14
|
+
sendingMessage?: boolean;
|
|
15
|
+
formConfig?: {
|
|
16
|
+
document_type: FormDocumentType;
|
|
17
|
+
form_id: number;
|
|
18
|
+
document_id: number;
|
|
19
|
+
id_number?: string;
|
|
20
|
+
reference?: string;
|
|
21
|
+
attachmentFieldId: number;
|
|
22
|
+
messageFieldId: number;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const ChatLayout: React.FC<ChatLayoutProps> = ({
|
|
27
|
+
children,
|
|
28
|
+
sendingMessage = false,
|
|
29
|
+
formConfig = undefined,
|
|
30
|
+
}) => {
|
|
31
|
+
const refAttachment = useRef<HTMLInputElement>(null);
|
|
32
|
+
const [formValues, setFormValues] = useState<{ image: any }>({ image: null });
|
|
33
|
+
|
|
34
|
+
const handleAttachmentClick = () => {
|
|
35
|
+
if (refAttachment.current) {
|
|
36
|
+
refAttachment.current.click();
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div className="flex flex-col h-full">
|
|
42
|
+
<div className="flex flex-col-reverse overflow-y-auto p-4 bg-gray-100 border border-gray-200 rounded-lg grow max-h-[65vh]">
|
|
43
|
+
{children}
|
|
44
|
+
</div>
|
|
45
|
+
<Form id="formRef" encType="multipart/form-data" method="post">
|
|
46
|
+
{formConfig ? (
|
|
47
|
+
<>
|
|
48
|
+
<TextField
|
|
49
|
+
hidden
|
|
50
|
+
name="reference"
|
|
51
|
+
defaultValue={formConfig.reference ?? ""}
|
|
52
|
+
/>
|
|
53
|
+
<TextField
|
|
54
|
+
hidden
|
|
55
|
+
name="form_id"
|
|
56
|
+
defaultValue={formConfig.form_id}
|
|
57
|
+
/>
|
|
58
|
+
<TextField
|
|
59
|
+
hidden
|
|
60
|
+
name="document_id"
|
|
61
|
+
defaultValue={formConfig.document_id}
|
|
62
|
+
/>
|
|
63
|
+
<TextField
|
|
64
|
+
hidden
|
|
65
|
+
name="document_type"
|
|
66
|
+
defaultValue={formConfig.document_type}
|
|
67
|
+
/>
|
|
68
|
+
<TextField
|
|
69
|
+
hidden
|
|
70
|
+
name="id_number"
|
|
71
|
+
defaultValue={formConfig?.id_number ?? ""}
|
|
72
|
+
/>
|
|
73
|
+
</>
|
|
74
|
+
) : (
|
|
75
|
+
<></>
|
|
76
|
+
)}
|
|
77
|
+
<div className="mt-4 flex">
|
|
78
|
+
{sendingMessage ? (
|
|
79
|
+
<LoadingInputSkeleton />
|
|
80
|
+
) : (
|
|
81
|
+
<input
|
|
82
|
+
name={`message${
|
|
83
|
+
formConfig?.messageFieldId
|
|
84
|
+
? `_${formConfig?.messageFieldId}`
|
|
85
|
+
: ""
|
|
86
|
+
}`}
|
|
87
|
+
type="text"
|
|
88
|
+
placeholder="Escribe un mensaje..."
|
|
89
|
+
className="form-input px-4 py-2 border border-gray-300 rounded-l-lg grow"
|
|
90
|
+
/>
|
|
91
|
+
)}
|
|
92
|
+
<button
|
|
93
|
+
onClick={handleAttachmentClick}
|
|
94
|
+
className={`${
|
|
95
|
+
formValues?.image ? "bg-blue-500" : ""
|
|
96
|
+
} hover:bg-blue-700 text-white font-bold py-2 px-4`}
|
|
97
|
+
type="button"
|
|
98
|
+
>
|
|
99
|
+
<AttachmentIconSVG />
|
|
100
|
+
</button>
|
|
101
|
+
<button
|
|
102
|
+
className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded-r-lg"
|
|
103
|
+
type="submit"
|
|
104
|
+
name="action"
|
|
105
|
+
value="sendMessage"
|
|
106
|
+
>
|
|
107
|
+
{sendingMessage ? <SpinnerSvg /> : <SendMessageIcon />}
|
|
108
|
+
</button>
|
|
109
|
+
</div>
|
|
110
|
+
<input
|
|
111
|
+
ref={refAttachment}
|
|
112
|
+
hidden
|
|
113
|
+
name={`attachment${
|
|
114
|
+
formConfig?.attachmentFieldId
|
|
115
|
+
? `_${formConfig?.attachmentFieldId}`
|
|
116
|
+
: ""
|
|
117
|
+
}`}
|
|
118
|
+
type="file"
|
|
119
|
+
accept=".jpg, .png, .jpeg, .png"
|
|
120
|
+
onChange={(e) => {
|
|
121
|
+
if (e.target.value && e.target.value !== "") {
|
|
122
|
+
setFormValues({ ...formValues, image: e.target.value });
|
|
123
|
+
} else {
|
|
124
|
+
setFormValues({ ...formValues, image: null });
|
|
125
|
+
}
|
|
126
|
+
}}
|
|
127
|
+
/>
|
|
128
|
+
</Form>
|
|
129
|
+
</div>
|
|
130
|
+
);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export default ChatLayout;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { CheckIconSVG, PencilSvg } from "@zauru-sdk/icons";
|
|
2
|
+
import { useState, useEffect, useRef } from "react";
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
author: string;
|
|
6
|
+
content: string;
|
|
7
|
+
date?: string;
|
|
8
|
+
onLike?: (like: boolean) => void;
|
|
9
|
+
onUpdateComment?: (text: string, itsNew?: boolean) => void;
|
|
10
|
+
id: string;
|
|
11
|
+
commentOwner?: boolean;
|
|
12
|
+
imageLink?: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const ChatMessageHistory = (props: Props) => {
|
|
16
|
+
const {
|
|
17
|
+
author,
|
|
18
|
+
content,
|
|
19
|
+
onLike,
|
|
20
|
+
onUpdateComment,
|
|
21
|
+
id,
|
|
22
|
+
commentOwner = false,
|
|
23
|
+
date,
|
|
24
|
+
imageLink,
|
|
25
|
+
} = props;
|
|
26
|
+
|
|
27
|
+
const initials = author ? author[0].toUpperCase() : "A";
|
|
28
|
+
const [isUpdating, setIsUpdating] = useState(false);
|
|
29
|
+
const [updatedContent, setUpdatedContent] = useState(content);
|
|
30
|
+
const textAreaRef = useRef<HTMLTextAreaElement | null>(null);
|
|
31
|
+
const [shouldPulse, setShouldPulse] = useState(false);
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (isUpdating && textAreaRef.current) {
|
|
35
|
+
textAreaRef.current.style.height = "auto";
|
|
36
|
+
textAreaRef.current.style.height = `${textAreaRef.current.scrollHeight}px`;
|
|
37
|
+
textAreaRef.current.focus();
|
|
38
|
+
}
|
|
39
|
+
}, [isUpdating]);
|
|
40
|
+
|
|
41
|
+
// Efecto secundario que se ejecuta cada vez que 'isUpdating' cambia
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
// Activar la animación
|
|
44
|
+
setShouldPulse(true);
|
|
45
|
+
|
|
46
|
+
// Establecer un temporizador para eliminar la clase después de que la animación se haya completado
|
|
47
|
+
const timer = setTimeout(() => {
|
|
48
|
+
setShouldPulse(false);
|
|
49
|
+
}, 15000); // Tiempo total de la animación
|
|
50
|
+
|
|
51
|
+
// Limpiar el temporizador si el componente se desmonta o si 'isUpdating' cambia de nuevo antes de que la animación se complete
|
|
52
|
+
return () => clearTimeout(timer);
|
|
53
|
+
}, [isUpdating]);
|
|
54
|
+
|
|
55
|
+
const handleUpdate = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
56
|
+
if (e.target.value.length <= 254) {
|
|
57
|
+
setUpdatedContent(e.target.value);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const handleUpdateClick = () => {
|
|
62
|
+
if (isUpdating) {
|
|
63
|
+
if (textAreaRef.current) {
|
|
64
|
+
textAreaRef.current.blur();
|
|
65
|
+
}
|
|
66
|
+
//onUpdateComment && onUpdateComment(updatedContent, itsNew);
|
|
67
|
+
}
|
|
68
|
+
setIsUpdating(!isUpdating);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<div className="p-1 rounded-xl flex items-center space-x-4 relative">
|
|
73
|
+
{commentOwner && (
|
|
74
|
+
<div className="absolute top-2 right-2">
|
|
75
|
+
<button
|
|
76
|
+
onClick={handleUpdateClick}
|
|
77
|
+
className={`${shouldPulse ? "animate-custom-pulse" : ""}`}
|
|
78
|
+
>
|
|
79
|
+
{isUpdating ? <CheckIconSVG /> : <PencilSvg />}
|
|
80
|
+
</button>
|
|
81
|
+
</div>
|
|
82
|
+
)}
|
|
83
|
+
<div
|
|
84
|
+
className={`w-[0%] lg:w-[8%] h-16 bg-purple-300 rounded-full flex items-center justify-center overflow-hidden text-white text-xl font-bold`}
|
|
85
|
+
>
|
|
86
|
+
{initials}
|
|
87
|
+
</div>
|
|
88
|
+
<div className="flex flex-col justify-between h-full w-[92%]">
|
|
89
|
+
<div>
|
|
90
|
+
<div className="flex flex-row space-x-2">
|
|
91
|
+
<div className="text-xl font-medium text-black">{author}</div>
|
|
92
|
+
<div className="text-xl font-medium text-gray-500">
|
|
93
|
+
{date && <> - {date}</>}
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
{isUpdating ? (
|
|
97
|
+
<textarea
|
|
98
|
+
ref={textAreaRef}
|
|
99
|
+
className="bg-gray-100 rounded p-2 w-full resize-none"
|
|
100
|
+
value={updatedContent}
|
|
101
|
+
onChange={handleUpdate}
|
|
102
|
+
maxLength={254}
|
|
103
|
+
placeholder="Deja tus buenos deseos..."
|
|
104
|
+
/>
|
|
105
|
+
) : (
|
|
106
|
+
<p className="text-gray-500">{updatedContent}</p>
|
|
107
|
+
)}
|
|
108
|
+
</div>
|
|
109
|
+
<div className="flex justify-end items-center">
|
|
110
|
+
<button onClick={() => {}}>
|
|
111
|
+
<></>
|
|
112
|
+
</button>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
{imageLink && (
|
|
116
|
+
<div
|
|
117
|
+
className={`w-[25%] flex items-center justify-center overflow-hidden text-white text-xl font-bold`}
|
|
118
|
+
>
|
|
119
|
+
<div
|
|
120
|
+
onClick={() => {
|
|
121
|
+
return window.open(imageLink, "_blank");
|
|
122
|
+
}}
|
|
123
|
+
>
|
|
124
|
+
<img
|
|
125
|
+
src={imageLink}
|
|
126
|
+
alt={"IMG_CHAT"}
|
|
127
|
+
className={`h-48 w-48 inline mr-1 pb-1`}
|
|
128
|
+
style={{
|
|
129
|
+
stroke: "currentColor",
|
|
130
|
+
strokeWidth: 2,
|
|
131
|
+
strokeLinecap: "round",
|
|
132
|
+
strokeLinejoin: "round",
|
|
133
|
+
fill: "none",
|
|
134
|
+
backgroundColor: "transparent",
|
|
135
|
+
}}
|
|
136
|
+
/>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
)}
|
|
140
|
+
</div>
|
|
141
|
+
);
|
|
142
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useIsOnline } from "@zauru-sdk/hooks";
|
|
2
|
+
|
|
3
|
+
const ConnectionState = () => {
|
|
4
|
+
const isOnline = useIsOnline();
|
|
5
|
+
|
|
6
|
+
// Definir estilos
|
|
7
|
+
const styles = {
|
|
8
|
+
container: {
|
|
9
|
+
padding: "10px",
|
|
10
|
+
borderRadius: "5px",
|
|
11
|
+
backgroundColor: isOnline ? "#d4edda" : "#f8d7da",
|
|
12
|
+
color: isOnline ? "#155724" : "#721c24",
|
|
13
|
+
fontWeight: "bold",
|
|
14
|
+
},
|
|
15
|
+
text: {
|
|
16
|
+
fontSize: "16px",
|
|
17
|
+
margin: 0,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Renderizar el estado de conexión
|
|
22
|
+
return (
|
|
23
|
+
<div style={styles.container}>
|
|
24
|
+
<p style={styles.text}>{isOnline ? "ONLINE ✅🌐" : "OFFLINE ❌🌐"}</p>
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default ConnectionState;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./ConnectionState";
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { isNumeric } from "@zauru-sdk/common";
|
|
2
|
+
import { GenericDynamicTableColumn } from "@zauru-sdk/types";
|
|
3
|
+
|
|
4
|
+
//TABLA PARA LA IMPRESION:
|
|
5
|
+
export const BasicTableHTML = (props: {
|
|
6
|
+
data: { [key: string]: string }[];
|
|
7
|
+
headers: GenericDynamicTableColumn[];
|
|
8
|
+
footer: { [key: string]: string };
|
|
9
|
+
}) => {
|
|
10
|
+
const { data, footer, headers } = props;
|
|
11
|
+
return (
|
|
12
|
+
<table style={{ borderCollapse: "collapse", width: "100%" }}>
|
|
13
|
+
<thead>
|
|
14
|
+
<tr>
|
|
15
|
+
{headers?.map((titulo, index) => (
|
|
16
|
+
<th
|
|
17
|
+
key={index}
|
|
18
|
+
style={{
|
|
19
|
+
border: "1px solid black",
|
|
20
|
+
padding: "10px",
|
|
21
|
+
textAlign: "center",
|
|
22
|
+
whiteSpace: "normal",
|
|
23
|
+
}}
|
|
24
|
+
>
|
|
25
|
+
{titulo.label}
|
|
26
|
+
</th>
|
|
27
|
+
))}
|
|
28
|
+
</tr>
|
|
29
|
+
</thead>
|
|
30
|
+
<tbody>
|
|
31
|
+
{data?.map((fila, index) => (
|
|
32
|
+
<tr key={index}>
|
|
33
|
+
{headers?.map((titulo, index) => (
|
|
34
|
+
<td
|
|
35
|
+
key={index}
|
|
36
|
+
style={{
|
|
37
|
+
border: "1px solid black",
|
|
38
|
+
padding: "1px",
|
|
39
|
+
textAlign: "center",
|
|
40
|
+
whiteSpace: "normal",
|
|
41
|
+
fontSize: isNumeric((fila as any)[titulo.name])
|
|
42
|
+
? "2em"
|
|
43
|
+
: "1em",
|
|
44
|
+
}}
|
|
45
|
+
>
|
|
46
|
+
{(fila as any)[titulo.name]}
|
|
47
|
+
</td>
|
|
48
|
+
))}
|
|
49
|
+
</tr>
|
|
50
|
+
))}
|
|
51
|
+
</tbody>
|
|
52
|
+
<tfoot>
|
|
53
|
+
<tr>
|
|
54
|
+
{headers?.map((titulo, index) => (
|
|
55
|
+
<td
|
|
56
|
+
key={index}
|
|
57
|
+
style={{
|
|
58
|
+
padding: "10px",
|
|
59
|
+
textAlign: "center",
|
|
60
|
+
whiteSpace: "normal",
|
|
61
|
+
fontSize: isNumeric((footer as any)[titulo.name])
|
|
62
|
+
? "2em"
|
|
63
|
+
: "1em",
|
|
64
|
+
}}
|
|
65
|
+
>
|
|
66
|
+
{(footer as any)[titulo.name]}
|
|
67
|
+
</td>
|
|
68
|
+
))}
|
|
69
|
+
</tr>
|
|
70
|
+
</tfoot>
|
|
71
|
+
</table>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import React, { useEffect, useCallback, useState, type ReactNode } from "react";
|
|
2
|
+
import { TextFieldWithoutValidation } from "src";
|
|
3
|
+
import { SelectFieldWithoutValidation } from "src";
|
|
4
|
+
import type { SingleValue } from "react-select";
|
|
5
|
+
import { useAppSelector } from "@zauru-sdk/redux";
|
|
6
|
+
import { SelectFieldOption } from "@zauru-sdk/types";
|
|
7
|
+
|
|
8
|
+
export type TableStateItem = {
|
|
9
|
+
item_id: string | undefined;
|
|
10
|
+
quantity: number | undefined;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type FormatedItem = {
|
|
14
|
+
label: string;
|
|
15
|
+
value: number;
|
|
16
|
+
template: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type Props = {
|
|
20
|
+
name: string;
|
|
21
|
+
formName?: string;
|
|
22
|
+
className?: string;
|
|
23
|
+
items: FormatedItem[];
|
|
24
|
+
onChange?: (tableState?: TableStateItem[]) => void;
|
|
25
|
+
forwardedRef?: React.RefObject<{
|
|
26
|
+
insertItems: (items: FormatedItem[]) => void;
|
|
27
|
+
getTableState: (
|
|
28
|
+
updatedData?: ReactNode[][]
|
|
29
|
+
) => TableStateItem[] | undefined;
|
|
30
|
+
}>;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const errorAnimation = {
|
|
34
|
+
hidden: { opacity: 0, y: -10 },
|
|
35
|
+
visible: { opacity: 1, y: 0 },
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const DynamicPrintTable = ({ forwardedRef, ...props }: Props) => {
|
|
39
|
+
const { items, onChange, className } = props;
|
|
40
|
+
const { formValidations } = useAppSelector((state) => state.formValidation);
|
|
41
|
+
|
|
42
|
+
const createItemSelect = (
|
|
43
|
+
rowIndex: number,
|
|
44
|
+
defaultValue?: SelectFieldOption
|
|
45
|
+
) => (
|
|
46
|
+
<SelectFieldWithoutValidation
|
|
47
|
+
key={rowIndex}
|
|
48
|
+
name="item_select"
|
|
49
|
+
isClearable
|
|
50
|
+
onChange={(value: SingleValue<SelectFieldOption>) => {
|
|
51
|
+
const selectedItem = items?.find((x) => x.value === value?.value);
|
|
52
|
+
updateRow(rowIndex, selectedItem);
|
|
53
|
+
}}
|
|
54
|
+
options={items}
|
|
55
|
+
defaultValue={defaultValue}
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const createItemQuantity = (rowIndex: number, defaultValue?: number) => (
|
|
60
|
+
<TextFieldWithoutValidation
|
|
61
|
+
key={rowIndex}
|
|
62
|
+
name="item_quantity"
|
|
63
|
+
type="number"
|
|
64
|
+
integer
|
|
65
|
+
defaultValue={defaultValue ?? 1}
|
|
66
|
+
min={1}
|
|
67
|
+
onChange={(value: string) => {
|
|
68
|
+
updateRow(rowIndex, undefined, Number(value));
|
|
69
|
+
}}
|
|
70
|
+
/>
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const createTemplateName = (rowIndex: number, defaultValue?: string) => (
|
|
74
|
+
<div key={rowIndex}>
|
|
75
|
+
{defaultValue != "" ? defaultValue : "No hay etiqueta"}
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const createRow = (
|
|
80
|
+
rowIndex: number,
|
|
81
|
+
item?: FormatedItem,
|
|
82
|
+
quantity?: number
|
|
83
|
+
) => {
|
|
84
|
+
const itemCreated = createItemSelect(rowIndex, item ?? undefined);
|
|
85
|
+
const quantityCreated = createItemQuantity(rowIndex, quantity ?? 1);
|
|
86
|
+
const templateCreated = createTemplateName(
|
|
87
|
+
rowIndex,
|
|
88
|
+
item ? item.template : "Seleccione un item para visualizar su etiqueta."
|
|
89
|
+
);
|
|
90
|
+
return [itemCreated, quantityCreated, templateCreated];
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const [tableData, setTableData] = useState<ReactNode[][] | undefined>([]);
|
|
94
|
+
const [isInitialItemAdded, setIsInitialItemAdded] = useState<boolean>(false);
|
|
95
|
+
|
|
96
|
+
const insertItems = useCallback(
|
|
97
|
+
(newItems: FormatedItem[]) => {
|
|
98
|
+
setTableData((prevData) => [
|
|
99
|
+
...(prevData ?? []),
|
|
100
|
+
...newItems.map((item, index) => {
|
|
101
|
+
const rowIndex = (prevData?.length ?? 0) + index;
|
|
102
|
+
const [itemCreated, quantityCreated, templateCreated] = createRow(
|
|
103
|
+
rowIndex,
|
|
104
|
+
item
|
|
105
|
+
);
|
|
106
|
+
updateRow(rowIndex, item, 1);
|
|
107
|
+
return [itemCreated, quantityCreated, templateCreated];
|
|
108
|
+
}),
|
|
109
|
+
]);
|
|
110
|
+
},
|
|
111
|
+
[items]
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const getTableState = (temp?: ReactNode[][]) => {
|
|
115
|
+
const updatedData = temp ?? tableData;
|
|
116
|
+
const tableState = updatedData?.map((rowData) => {
|
|
117
|
+
const firstElement = rowData[0];
|
|
118
|
+
const item_id = React.isValidElement(firstElement)
|
|
119
|
+
? firstElement.props.defaultValue?.value
|
|
120
|
+
: undefined;
|
|
121
|
+
|
|
122
|
+
const secondElement = rowData[1];
|
|
123
|
+
const quantity = React.isValidElement(secondElement)
|
|
124
|
+
? secondElement.props.defaultValue
|
|
125
|
+
: undefined;
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
item_id,
|
|
129
|
+
quantity,
|
|
130
|
+
} as TableStateItem;
|
|
131
|
+
});
|
|
132
|
+
return tableState;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const updateRow = (
|
|
136
|
+
rowIndex: number,
|
|
137
|
+
item?: FormatedItem,
|
|
138
|
+
quantity?: number
|
|
139
|
+
) => {
|
|
140
|
+
setTableData((prevData) => {
|
|
141
|
+
const updatedData = prevData?.map((_, index) => {
|
|
142
|
+
if (index === rowIndex) {
|
|
143
|
+
const firstElement = prevData[index][0];
|
|
144
|
+
const selectedItem =
|
|
145
|
+
item ??
|
|
146
|
+
(React.isValidElement(firstElement)
|
|
147
|
+
? items?.find(
|
|
148
|
+
(x) => x.value === firstElement.props.defaultValue?.value
|
|
149
|
+
)
|
|
150
|
+
: undefined);
|
|
151
|
+
|
|
152
|
+
const secondElement = prevData[index][1];
|
|
153
|
+
const newQuantity = quantity
|
|
154
|
+
? quantity
|
|
155
|
+
: React.isValidElement(secondElement)
|
|
156
|
+
? secondElement.props.defaultValue
|
|
157
|
+
: undefined;
|
|
158
|
+
|
|
159
|
+
return createRow(rowIndex, selectedItem, newQuantity);
|
|
160
|
+
} else {
|
|
161
|
+
return prevData[index];
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
if (onChange) {
|
|
166
|
+
const tableState = getTableState(updatedData);
|
|
167
|
+
|
|
168
|
+
onChange(tableState);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return updatedData;
|
|
172
|
+
});
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const addRow = () => {
|
|
176
|
+
setTableData((prevData) => [
|
|
177
|
+
...(prevData ?? []),
|
|
178
|
+
createRow(prevData?.length ?? 0),
|
|
179
|
+
]);
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const removeRow = (rowIndex: number) => {
|
|
183
|
+
setTableData((prevData) =>
|
|
184
|
+
prevData?.filter((_, index) => index !== rowIndex)
|
|
185
|
+
);
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
React.useImperativeHandle(forwardedRef, () => ({
|
|
189
|
+
insertItems,
|
|
190
|
+
getTableState,
|
|
191
|
+
}));
|
|
192
|
+
|
|
193
|
+
const pastelGrayBackground = {
|
|
194
|
+
backgroundColor: "#B69E99",
|
|
195
|
+
} as React.CSSProperties;
|
|
196
|
+
|
|
197
|
+
const renderHeader = () => (
|
|
198
|
+
<tr style={{ ...pastelGrayBackground }}>
|
|
199
|
+
<th className="text-left align-middle p-2">Item</th>
|
|
200
|
+
<th className="text-left align-middle p-2">Cantidad</th>
|
|
201
|
+
<th className="text-left align-middle p-2">Etiqueta</th>
|
|
202
|
+
<th className="w-16"></th>
|
|
203
|
+
</tr>
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const renderRow = (rowData: ReactNode[], rowIndex: number) => (
|
|
207
|
+
<tr key={rowIndex}>
|
|
208
|
+
{rowData.map((cellData, cellIndex) => (
|
|
209
|
+
<td key={cellIndex} className="align-middle p-2">
|
|
210
|
+
{cellData}
|
|
211
|
+
</td>
|
|
212
|
+
))}
|
|
213
|
+
{rowIndex !== 0 && (
|
|
214
|
+
<td className="align-middle w-16">
|
|
215
|
+
<button
|
|
216
|
+
className="bg-red-500 hover:bg-red-600 font-bold py-1 px-2 rounded ml-2"
|
|
217
|
+
onClick={(
|
|
218
|
+
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
|
219
|
+
) => {
|
|
220
|
+
event.preventDefault();
|
|
221
|
+
event.stopPropagation();
|
|
222
|
+
removeRow(rowIndex);
|
|
223
|
+
}}
|
|
224
|
+
type="button"
|
|
225
|
+
>
|
|
226
|
+
x
|
|
227
|
+
</button>
|
|
228
|
+
</td>
|
|
229
|
+
)}
|
|
230
|
+
</tr>
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
const renderRows = () => {
|
|
234
|
+
return tableData?.map((rowData, rowIndex) => renderRow(rowData, rowIndex));
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const error = formValidations[props.formName ?? "-1"][props.name];
|
|
238
|
+
const borderColor = error ? "border-red-500" : "border-transparent";
|
|
239
|
+
|
|
240
|
+
useEffect(() => {
|
|
241
|
+
if (!isInitialItemAdded && items?.length > 0) {
|
|
242
|
+
insertItems([items[0]]);
|
|
243
|
+
setIsInitialItemAdded(true);
|
|
244
|
+
}
|
|
245
|
+
}, [isInitialItemAdded, items, insertItems]);
|
|
246
|
+
|
|
247
|
+
return (
|
|
248
|
+
<div
|
|
249
|
+
className={`${className} ${borderColor} border-2`}
|
|
250
|
+
style={{ overflowX: "auto" }}
|
|
251
|
+
>
|
|
252
|
+
{!!error && <div className="text-red-500 text-sm mb-2">{error}</div>}
|
|
253
|
+
<table className="w-full">
|
|
254
|
+
<thead>{renderHeader()}</thead>
|
|
255
|
+
<tbody>{renderRows()}</tbody>
|
|
256
|
+
<tfoot>
|
|
257
|
+
<tr>
|
|
258
|
+
<td className="align-middle">
|
|
259
|
+
<button
|
|
260
|
+
className="bg-blue-500 hover:bg-blue-600 font-bold py-2 px-4 rounded"
|
|
261
|
+
onClick={(
|
|
262
|
+
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
|
263
|
+
) => {
|
|
264
|
+
event.preventDefault();
|
|
265
|
+
event.stopPropagation();
|
|
266
|
+
addRow();
|
|
267
|
+
}}
|
|
268
|
+
type="button"
|
|
269
|
+
>
|
|
270
|
+
+
|
|
271
|
+
</button>
|
|
272
|
+
</td>
|
|
273
|
+
<td colSpan={2}>
|
|
274
|
+
Total de etiquetas a imprimir:{" "}
|
|
275
|
+
{tableData?.reduce((sum, x) => {
|
|
276
|
+
const val = React.isValidElement(x[1])
|
|
277
|
+
? x[1]?.props?.defaultValue
|
|
278
|
+
: undefined;
|
|
279
|
+
if (!sum) return val;
|
|
280
|
+
return sum + val;
|
|
281
|
+
}, 0)}
|
|
282
|
+
</td>
|
|
283
|
+
</tr>
|
|
284
|
+
</tfoot>
|
|
285
|
+
</table>
|
|
286
|
+
</div>
|
|
287
|
+
);
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
export default React.forwardRef(DynamicPrintTable);
|