@zauru-sdk/components 1.0.112 → 1.0.114
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/Form/ReactZodForm/index.d.ts +1 -0
- package/dist/HOC/ValidateEmployeeAccess/index.d.ts +5 -0
- package/dist/Layouts/errorLayout/index.d.ts +1 -0
- package/dist/Layouts/index.d.ts +1 -0
- package/dist/SidePanel/index.d.ts +21 -0
- package/dist/cjs/Buttons/Button.js +7 -7
- package/dist/cjs/Form/DatePicker/index.js +1 -1
- package/dist/cjs/Form/ReactZodForm/index.js +2 -4
- package/dist/cjs/Form/SelectField/index.js +29 -12
- package/dist/cjs/Form/TextField/index.js +10 -16
- package/dist/cjs/Form/TimePicker/index.js +1 -1
- package/dist/cjs/HOC/ValidateEmployeeAccess/index.js +17 -0
- package/dist/cjs/Layouts/errorLayout/index.js +14 -0
- package/dist/cjs/Layouts/index.js +1 -0
- package/dist/cjs/SidePanel/index.js +48 -0
- package/dist/cjs/index.js +2 -0
- package/dist/esm/Buttons/Button.js +7 -7
- package/dist/esm/Form/DatePicker/index.js +2 -2
- package/dist/esm/Form/ReactZodForm/index.js +2 -4
- package/dist/esm/Form/SelectField/index.js +30 -13
- package/dist/esm/Form/TextField/index.js +10 -16
- package/dist/esm/Form/TimePicker/index.js +1 -1
- package/dist/esm/HOC/ValidateEmployeeAccess/index.js +13 -0
- package/dist/esm/Layouts/errorLayout/index.js +10 -0
- package/dist/esm/Layouts/index.js +1 -0
- package/dist/esm/SidePanel/index.js +46 -0
- package/dist/esm/index.js +2 -0
- package/dist/index.d.ts +2 -0
- package/package.json +4 -4
- package/src/Buttons/Button.tsx +29 -28
- package/src/Form/DatePicker/index.tsx +2 -2
- package/src/Form/ReactZodForm/index.tsx +4 -2
- package/src/Form/SelectField/index.tsx +54 -36
- package/src/Form/TextField/index.tsx +14 -17
- package/src/Form/TimePicker/index.tsx +1 -1
- package/src/HOC/ValidateEmployeeAccess/index.tsx +51 -0
- package/src/Layouts/errorLayout/index.tsx +47 -0
- package/src/Layouts/index.ts +1 -0
- package/src/SidePanel/index.tsx +153 -0
- package/src/index.ts +2 -0
|
@@ -6,7 +6,7 @@ export const TextField = (props) => {
|
|
|
6
6
|
const { id, name, defaultValue = "", hidden, type = "text", onChange, onKeyDown, disabled = false, readOnly = false, min, integer = false, stopChangeEvents, style, title, helpText, className, hint, required, } = props;
|
|
7
7
|
const [showTooltip, setShowTooltip] = useState(false);
|
|
8
8
|
const [value, setValue] = useState(defaultValue);
|
|
9
|
-
const { register: tempRegister, formState: { errors }, } = useFormContext() || { formState: {} }; // Obtener el contexto solo si existe
|
|
9
|
+
const { register: tempRegister, formState: { errors }, setValue: setOnFormValue, } = useFormContext() || { formState: {} }; // Obtener el contexto solo si existe
|
|
10
10
|
const error = errors ? errors[props.name ?? "-1"] : undefined;
|
|
11
11
|
const register = tempRegister
|
|
12
12
|
? tempRegister(props.name ?? "-1", {
|
|
@@ -18,11 +18,15 @@ export const TextField = (props) => {
|
|
|
18
18
|
const isReadOnly = disabled || readOnly;
|
|
19
19
|
const bgColor = isReadOnly ? "bg-gray-200" : `bg-${color}-50`;
|
|
20
20
|
const textColor = isReadOnly ? "text-gray-500" : `text-${color}-900`;
|
|
21
|
-
const borderColor = isReadOnly ? "border-gray-300" : `border-${color}-
|
|
21
|
+
const borderColor = isReadOnly ? "border-gray-300" : `border-${color}-200`;
|
|
22
22
|
useEffect(() => {
|
|
23
|
+
if (setOnFormValue) {
|
|
24
|
+
setOnFormValue(name ?? "-1", defaultValue);
|
|
25
|
+
}
|
|
23
26
|
setValue(defaultValue);
|
|
24
27
|
}, [defaultValue]);
|
|
25
28
|
const handleInputChange = (event) => {
|
|
29
|
+
const newValue = event.target.value;
|
|
26
30
|
if (register) {
|
|
27
31
|
register.onChange(event);
|
|
28
32
|
}
|
|
@@ -30,18 +34,8 @@ export const TextField = (props) => {
|
|
|
30
34
|
event.stopPropagation();
|
|
31
35
|
event.preventDefault();
|
|
32
36
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const isInteger = /^[0-9]*$/.test(value);
|
|
36
|
-
if (isInteger || value === "") {
|
|
37
|
-
setValue(value);
|
|
38
|
-
onChange && onChange(value, event);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
setValue(event.target.value);
|
|
43
|
-
onChange && onChange(event.target.value, event);
|
|
44
|
-
}
|
|
37
|
+
setValue(newValue);
|
|
38
|
+
onChange && onChange(newValue, event);
|
|
45
39
|
};
|
|
46
40
|
const handleKeyDown = (event) => {
|
|
47
41
|
if (integer && type === "number") {
|
|
@@ -60,9 +54,9 @@ export const TextField = (props) => {
|
|
|
60
54
|
}
|
|
61
55
|
};
|
|
62
56
|
if (hidden) {
|
|
63
|
-
return (_jsx("input", { type:
|
|
57
|
+
return (_jsx("input", { type: type, id: id ?? name, value: value, hidden: true, ...(register ?? {}), name: name, onChange: handleInputChange }));
|
|
64
58
|
}
|
|
65
|
-
const inputComponent = (_jsx("input", { type: type, readOnly:
|
|
59
|
+
const inputComponent = (_jsx("input", { type: type, readOnly: isReadOnly, disabled: disabled, id: id ?? name, autoComplete: "off", value: value, onWheel: (e) => {
|
|
66
60
|
e.currentTarget.blur();
|
|
67
61
|
}, step: type === "number" ? 0.01 : undefined, onKeyDown: (event) => {
|
|
68
62
|
handleKeyDown(event);
|
|
@@ -14,7 +14,7 @@ export const FormTimePicker = (props) => {
|
|
|
14
14
|
const color = error ? "red" : "gray";
|
|
15
15
|
const isReadOnly = disabled;
|
|
16
16
|
const bgColor = isReadOnly ? "bg-gray-200" : `bg-${color}-50`;
|
|
17
|
-
const textColor = isReadOnly ? "text-gray-500" : `text-${color}-
|
|
17
|
+
const textColor = isReadOnly ? "text-gray-500" : `text-${color}-900`;
|
|
18
18
|
const borderColor = isReadOnly ? "border-gray-300" : `border-${color}-200`;
|
|
19
19
|
useEffect(() => {
|
|
20
20
|
setValue(defaultValue);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useGetEmployeeProfile, useGetSessionAttribute, } from "@zauru-sdk/hooks";
|
|
3
|
+
import { Link } from "@remix-run/react";
|
|
4
|
+
export const ValidateEmployeeAccess = ({ children, permissionVariableName, showIfNoPermission = false, }) => {
|
|
5
|
+
const { data: employee } = useGetEmployeeProfile();
|
|
6
|
+
const variable_string = useGetSessionAttribute(permissionVariableName, "sessionVariable");
|
|
7
|
+
const variable = variable_string?.split(",");
|
|
8
|
+
const hasPermission = variable?.includes(employee?.id?.toString() || "-1");
|
|
9
|
+
if (showIfNoPermission && !hasPermission) {
|
|
10
|
+
return (_jsxs("div", { className: "bg-gray-900 text-white min-h-screen flex flex-col items-center justify-center p-4", children: [_jsx("img", { src: "/logo.png", alt: "Zauru Logo", className: "mb-8 h-20" }), _jsx("h1", { className: "text-5xl font-extrabold text-red-500 mb-6", children: "\u00A1Acceso Denegado!" }), _jsx("div", { className: "w-full max-w-2xl", children: _jsx("p", { className: "text-2xl text-gray-300 mb-8 text-center", children: "Lo sentimos, no tienes permiso para acceder a esta p\u00E1gina." }) }), _jsx(Link, { to: "/", className: "bg-blue-600 text-white py-3 px-8 rounded-full text-lg font-semibold hover:bg-blue-700 transition duration-300 transform hover:scale-105", children: "Regresar al inicio" }), _jsx("div", { className: "mt-12 text-gray-500", children: _jsx("p", { children: "Si crees que esto es un error, por favor contacta a soporte." }) })] }));
|
|
11
|
+
}
|
|
12
|
+
return _jsx(_Fragment, { children: hasPermission ? children : null });
|
|
13
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { isRouteErrorResponse, Links, Meta, Scripts, useRouteError, Link, } from "@remix-run/react";
|
|
3
|
+
export const ErrorLayout = () => {
|
|
4
|
+
const error = useRouteError();
|
|
5
|
+
return (_jsxs("html", { lang: "es", className: "bg-gray-900 text-white", children: [_jsxs("head", { children: [_jsx("meta", { charSet: "utf-8" }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }), _jsx("title", { children: "\u00A1Ups! Algo sali\u00F3 mal" }), _jsx(Meta, {}), _jsx(Links, {})] }), _jsxs("body", { className: "min-h-screen flex flex-col items-center justify-center p-4", children: [_jsx("img", { src: "/logo.png", alt: "Zauru Logo", className: "mb-8 h-20" }), _jsx("h1", { className: "text-5xl font-extrabold text-red-500 mb-6", children: "\u00A1Ups!" }), _jsx("div", { className: "w-full max-w-2xl", children: _jsx("p", { className: "text-2xl text-gray-300 mb-8 text-center", children: isRouteErrorResponse(error)
|
|
6
|
+
? `Error ${error.status}: ${error.statusText}`
|
|
7
|
+
: error instanceof Error
|
|
8
|
+
? error.message
|
|
9
|
+
: "Ha ocurrido un error inesperado" }) }), _jsx(Link, { to: "/", className: "bg-blue-600 text-white py-3 px-8 rounded-full text-lg font-semibold hover:bg-blue-700 transition duration-300 transform hover:scale-105", children: "Regresar al inicio" }), _jsx("div", { className: "mt-12 text-gray-500", children: _jsx("p", { children: "Si el problema persiste, por favor contacta a soporte." }) }), _jsx(Scripts, {})] })] }));
|
|
10
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect, useRef } from "react";
|
|
3
|
+
import { motion, AnimatePresence } from "framer-motion";
|
|
4
|
+
// SVG icon for the left-pointing chevron
|
|
5
|
+
const ChevronLeftIcon = () => (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "h-6 w-6", children: _jsx("path", { fillRule: "evenodd", d: "M7.72 12.53a.75.75 0 010-1.06l7.5-7.5a.75.75 0 111.06 1.06L9.31 12l6.97 6.97a.75.75 0 11-1.06 1.06l-7.5-7.5z", clipRule: "evenodd" }) }));
|
|
6
|
+
// SVG icon for the right-pointing chevron
|
|
7
|
+
const ChevronRightIcon = () => (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "h-6 w-6", children: _jsx("path", { fillRule: "evenodd", d: "M16.28 11.47a.75.75 0 010 1.06l-7.5 7.5a.75.75 0 01-1.06-1.06L14.69 12 7.72 5.03a.75.75 0 011.06-1.06l7.5 7.5z", clipRule: "evenodd" }) }));
|
|
8
|
+
// SVG icon for the filter (bars filter icon)
|
|
9
|
+
const FilterIcon = () => (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "h-6 w-6", children: _jsx("path", { fillRule: "evenodd", d: "M3 6a1 1 0 011-1h16a1 1 0 110 2H4a1 1 0 01-1-1zm0 6a1 1 0 011-1h10a1 1 0 110 2H4a1 1 0 01-1-1zm0 6a1 1 0 011-1h4a1 1 0 110 2H4a1 1 0 01-1-1z", clipRule: "evenodd" }) }));
|
|
10
|
+
const SidePanel = ({ children, closeOnClickOutside = true, widthPercentage = 25, buttonIcon = "chevron", }) => {
|
|
11
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
12
|
+
const panelRef = useRef(null);
|
|
13
|
+
const togglePanel = () => {
|
|
14
|
+
setIsOpen(!isOpen);
|
|
15
|
+
};
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const handleClickOutside = (event) => {
|
|
18
|
+
if (closeOnClickOutside &&
|
|
19
|
+
panelRef.current &&
|
|
20
|
+
!panelRef.current.contains(event.target)) {
|
|
21
|
+
setIsOpen(false);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
25
|
+
return () => {
|
|
26
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
27
|
+
};
|
|
28
|
+
}, [closeOnClickOutside]);
|
|
29
|
+
const renderIcon = () => {
|
|
30
|
+
if (typeof buttonIcon === "string") {
|
|
31
|
+
switch (buttonIcon) {
|
|
32
|
+
case "chevron":
|
|
33
|
+
return isOpen ? _jsx(ChevronRightIcon, {}) : _jsx(ChevronLeftIcon, {});
|
|
34
|
+
case "filter":
|
|
35
|
+
return _jsx(FilterIcon, {});
|
|
36
|
+
default:
|
|
37
|
+
return _jsx(ChevronLeftIcon, {});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
return buttonIcon;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
return (_jsxs(_Fragment, { children: [_jsx(AnimatePresence, { children: isOpen && (_jsxs(motion.div, { ref: panelRef, initial: { x: "100%" }, animate: { x: 0 }, exit: { x: "100%" }, transition: { type: "spring", stiffness: 300, damping: 30 }, className: "fixed top-0 right-0 h-full bg-white shadow-lg z-[9999] overflow-y-auto", style: { width: `${widthPercentage}%` }, children: [_jsx("button", { onClick: togglePanel, className: "absolute top-4 left-4 p-2 rounded-full bg-gray-200 hover:bg-gray-300 transition-colors", children: _jsx(ChevronRightIcon, {}) }), _jsx("div", { className: "p-6 mt-16", children: children })] })) }), !isOpen && (_jsx("button", { onClick: togglePanel, className: "fixed top-1/2 right-0 transform -translate-y-1/2 bg-indigo-600 text-white p-3 rounded-l-lg shadow-md hover:bg-indigo-700 transition-colors z-[10000]", children: renderIcon() }))] }));
|
|
45
|
+
};
|
|
46
|
+
export default SidePanel;
|
package/dist/esm/index.js
CHANGED
package/dist/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zauru-sdk/components",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.114",
|
|
4
4
|
"description": "Componentes reutilizables en las WebApps de Zauru.",
|
|
5
5
|
"main": "./dist/cjs/index.js",
|
|
6
6
|
"module": "./dist/esm/index.js",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"@zauru-sdk/hooks": "^1.0.112",
|
|
39
39
|
"@zauru-sdk/icons": "^1.0.60",
|
|
40
40
|
"@zauru-sdk/types": "^1.0.109",
|
|
41
|
-
"@zauru-sdk/utils": "^1.0.
|
|
42
|
-
"framer-motion": "^11.0
|
|
41
|
+
"@zauru-sdk/utils": "^1.0.114",
|
|
42
|
+
"framer-motion": "^11.7.0",
|
|
43
43
|
"jsonwebtoken": "^9.0.2",
|
|
44
44
|
"react": "^18.2.0",
|
|
45
45
|
"react-data-table-component": "^7.6.2",
|
|
@@ -50,5 +50,5 @@
|
|
|
50
50
|
"styled-components": "^5.3.5",
|
|
51
51
|
"zod": "^3.23.8"
|
|
52
52
|
},
|
|
53
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "f24dbaac6f5cc9543bdfcd33f20f704ead3965c3"
|
|
54
54
|
}
|
package/src/Buttons/Button.tsx
CHANGED
|
@@ -83,34 +83,35 @@ export const Button = (props: Props) => {
|
|
|
83
83
|
: "";
|
|
84
84
|
|
|
85
85
|
const buttonContent = (
|
|
86
|
-
|
|
87
|
-
type={
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
86
|
+
<>
|
|
87
|
+
<input type="hidden" name="action" value={name} />
|
|
88
|
+
<button
|
|
89
|
+
type={type}
|
|
90
|
+
disabled={
|
|
91
|
+
loading || disabled || (enableFormErrorsValidation && formHasErrors)
|
|
92
|
+
}
|
|
93
|
+
onClick={onClickSave}
|
|
94
|
+
className={`ml-2 ${
|
|
95
|
+
loading || disabled || (enableFormErrorsValidation && formHasErrors)
|
|
96
|
+
? " bg-opacity-25 "
|
|
97
|
+
: ""
|
|
98
|
+
} ${
|
|
99
|
+
loading
|
|
100
|
+
? " cursor-progress"
|
|
101
|
+
: `${
|
|
102
|
+
disabled || (enableFormErrorsValidation && formHasErrors)
|
|
103
|
+
? ""
|
|
104
|
+
: `hover:${color.bg700}`
|
|
105
|
+
}`
|
|
106
|
+
} inline-flex justify-center rounded-md border border-transparent ${
|
|
107
|
+
color.bg600
|
|
108
|
+
} py-2 px-4 text-sm font-medium text-white shadow-sm focus:outline-none focus:ring-2 focus:${
|
|
109
|
+
color.ring500
|
|
110
|
+
} focus:ring-offset-2 ${className}`}
|
|
111
|
+
>
|
|
112
|
+
{loading ? loadingText : inside}
|
|
113
|
+
</button>
|
|
114
|
+
</>
|
|
114
115
|
);
|
|
115
116
|
|
|
116
117
|
return (
|
|
@@ -61,7 +61,7 @@ export const FormDatePicker = (props: Props) => {
|
|
|
61
61
|
};
|
|
62
62
|
|
|
63
63
|
return (
|
|
64
|
-
|
|
64
|
+
<div>
|
|
65
65
|
{title && (
|
|
66
66
|
<label
|
|
67
67
|
htmlFor={error ? `${name}-error` : `${name}-success`}
|
|
@@ -130,6 +130,6 @@ export const FormDatePicker = (props: Props) => {
|
|
|
130
130
|
{hint}
|
|
131
131
|
</p>
|
|
132
132
|
)}
|
|
133
|
-
|
|
133
|
+
</div>
|
|
134
134
|
);
|
|
135
135
|
};
|
|
@@ -15,6 +15,7 @@ type Props = {
|
|
|
15
15
|
onSubmit?: SubmitHandler<FieldValues>;
|
|
16
16
|
id?: string;
|
|
17
17
|
method?: "post" | "put" | "delete" | "patch";
|
|
18
|
+
className?: string;
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
const emptySchema = z.any();
|
|
@@ -26,6 +27,7 @@ export const ReactZodForm = (props: Props) => {
|
|
|
26
27
|
schema = emptySchema,
|
|
27
28
|
onSubmit,
|
|
28
29
|
id,
|
|
30
|
+
className,
|
|
29
31
|
} = props;
|
|
30
32
|
|
|
31
33
|
const submit = useSubmit();
|
|
@@ -46,10 +48,10 @@ export const ReactZodForm = (props: Props) => {
|
|
|
46
48
|
return (
|
|
47
49
|
<FormProvider {...methods}>
|
|
48
50
|
<Form
|
|
49
|
-
onSubmit={
|
|
50
|
-
//onSubmit={methods.handleSubmit(handleSubmit)}
|
|
51
|
+
onSubmit={methods.handleSubmit(handleSubmit)}
|
|
51
52
|
method={method}
|
|
52
53
|
id={id}
|
|
54
|
+
className={className}
|
|
53
55
|
>
|
|
54
56
|
{children}
|
|
55
57
|
</Form>
|
|
@@ -2,7 +2,7 @@ import { IdeaIconSVG } from "@zauru-sdk/icons";
|
|
|
2
2
|
import { SelectFieldOption } from "@zauru-sdk/types";
|
|
3
3
|
import React, { useEffect, useState, useRef, KeyboardEvent } from "react";
|
|
4
4
|
import { LoadingInputSkeleton } from "../../Skeletons/index.js";
|
|
5
|
-
import { useFormContext
|
|
5
|
+
import { useFormContext } from "react-hook-form";
|
|
6
6
|
|
|
7
7
|
type Props = {
|
|
8
8
|
id?: string;
|
|
@@ -45,7 +45,7 @@ export const SelectField = (props: Props) => {
|
|
|
45
45
|
loading = false,
|
|
46
46
|
className = "",
|
|
47
47
|
onInputChange,
|
|
48
|
-
required,
|
|
48
|
+
required = false,
|
|
49
49
|
} = props;
|
|
50
50
|
|
|
51
51
|
const [value, setValue] = useState<SelectFieldOption | null>(
|
|
@@ -64,17 +64,22 @@ export const SelectField = (props: Props) => {
|
|
|
64
64
|
const [isEnterPressed, setIsEnterPressed] = useState<boolean>(false);
|
|
65
65
|
const [isSearching, setIsSearching] = useState<boolean>(false);
|
|
66
66
|
const {
|
|
67
|
-
|
|
67
|
+
register: tempRegister,
|
|
68
68
|
formState: { errors },
|
|
69
69
|
setValue: setFormValue,
|
|
70
70
|
} = useFormContext() || { formState: {} };
|
|
71
71
|
const error = errors ? errors[props.name ?? "-1"] : undefined;
|
|
72
|
+
const register = tempRegister
|
|
73
|
+
? tempRegister(props.name ?? "-1", {
|
|
74
|
+
required,
|
|
75
|
+
})
|
|
76
|
+
: undefined; // Solo usar register si está disponible
|
|
72
77
|
|
|
73
78
|
const color = error ? "red" : "gray";
|
|
74
79
|
const isReadOnly = disabled || readOnly;
|
|
75
80
|
const bgColor = isReadOnly ? "bg-gray-200" : `bg-${color}-50`;
|
|
76
81
|
const textColor = isReadOnly ? "text-gray-500" : `text-${color}-900`;
|
|
77
|
-
const borderColor = isReadOnly ? "border-gray-300" : `border-${color}-
|
|
82
|
+
const borderColor = isReadOnly ? "border-gray-300" : `border-${color}-200`;
|
|
78
83
|
|
|
79
84
|
useEffect(() => {
|
|
80
85
|
setFilteredOptions(options);
|
|
@@ -93,7 +98,9 @@ export const SelectField = (props: Props) => {
|
|
|
93
98
|
if (defaultValue) {
|
|
94
99
|
setValue(defaultValue);
|
|
95
100
|
setInputValue(defaultValue.label);
|
|
96
|
-
setFormValue
|
|
101
|
+
if (setFormValue) {
|
|
102
|
+
setFormValue(name || "", defaultValue.value);
|
|
103
|
+
}
|
|
97
104
|
}
|
|
98
105
|
|
|
99
106
|
document.addEventListener("mousedown", handleClickOutside);
|
|
@@ -104,6 +111,9 @@ export const SelectField = (props: Props) => {
|
|
|
104
111
|
|
|
105
112
|
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
106
113
|
const newValue = e.target.value;
|
|
114
|
+
if (register) {
|
|
115
|
+
register.onChange(e);
|
|
116
|
+
}
|
|
107
117
|
setInputValue(newValue);
|
|
108
118
|
onInputChange && onInputChange(newValue);
|
|
109
119
|
setIsSearching(true);
|
|
@@ -121,12 +131,19 @@ export const SelectField = (props: Props) => {
|
|
|
121
131
|
: [...valueMulti, option];
|
|
122
132
|
setValueMulti(newValue);
|
|
123
133
|
onChangeMulti && onChangeMulti(newValue);
|
|
124
|
-
setFormValue
|
|
134
|
+
if (setFormValue) {
|
|
135
|
+
setFormValue(
|
|
136
|
+
name || "",
|
|
137
|
+
newValue.map((v) => v.value)
|
|
138
|
+
);
|
|
139
|
+
}
|
|
125
140
|
} else {
|
|
126
141
|
setValue(option);
|
|
127
142
|
setInputValue(option.label);
|
|
128
143
|
onChange && onChange(option);
|
|
129
|
-
setFormValue
|
|
144
|
+
if (setFormValue) {
|
|
145
|
+
setFormValue(name || "", option.value);
|
|
146
|
+
}
|
|
130
147
|
}
|
|
131
148
|
setIsOpen(false);
|
|
132
149
|
};
|
|
@@ -135,11 +152,15 @@ export const SelectField = (props: Props) => {
|
|
|
135
152
|
if (isMulti) {
|
|
136
153
|
setValueMulti([]);
|
|
137
154
|
onChangeMulti && onChangeMulti([]);
|
|
138
|
-
setFormValue
|
|
155
|
+
if (setFormValue) {
|
|
156
|
+
setFormValue(name || "", []);
|
|
157
|
+
}
|
|
139
158
|
} else {
|
|
140
159
|
setValue(null);
|
|
141
160
|
onChange && onChange(null);
|
|
142
|
-
setFormValue
|
|
161
|
+
if (setFormValue) {
|
|
162
|
+
setFormValue(name || "", "");
|
|
163
|
+
}
|
|
143
164
|
}
|
|
144
165
|
setInputValue("");
|
|
145
166
|
};
|
|
@@ -245,33 +266,30 @@ export const SelectField = (props: Props) => {
|
|
|
245
266
|
</label>
|
|
246
267
|
)}
|
|
247
268
|
<div className="relative">
|
|
248
|
-
<
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
autoComplete="off"
|
|
273
|
-
/>
|
|
274
|
-
)}
|
|
269
|
+
<input
|
|
270
|
+
type="text"
|
|
271
|
+
id={id}
|
|
272
|
+
value={inputValue}
|
|
273
|
+
onFocus={() => setIsOpen(true)}
|
|
274
|
+
onKeyDown={handleKeyDown}
|
|
275
|
+
readOnly={isReadOnly}
|
|
276
|
+
disabled={disabled}
|
|
277
|
+
className={`block w-full rounded-md ${bgColor} ${borderColor} ${textColor} shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm`}
|
|
278
|
+
placeholder={isMulti ? "Select options..." : "Select an option..."}
|
|
279
|
+
autoComplete="off"
|
|
280
|
+
onChange={handleInputChange}
|
|
281
|
+
onBlur={handleBlur}
|
|
282
|
+
required={required}
|
|
283
|
+
/>
|
|
284
|
+
<input
|
|
285
|
+
type="hidden"
|
|
286
|
+
{...(register ?? {})}
|
|
287
|
+
name={name}
|
|
288
|
+
value={
|
|
289
|
+
isMulti
|
|
290
|
+
? valueMulti.map((v) => v.value).join(",")
|
|
291
|
+
: value?.value || ""
|
|
292
|
+
}
|
|
275
293
|
/>
|
|
276
294
|
{isClearable && (value || valueMulti.length > 0) && (
|
|
277
295
|
<button
|
|
@@ -53,6 +53,7 @@ export const TextField = (props: Props) => {
|
|
|
53
53
|
const {
|
|
54
54
|
register: tempRegister,
|
|
55
55
|
formState: { errors },
|
|
56
|
+
setValue: setOnFormValue,
|
|
56
57
|
} = useFormContext() || { formState: {} }; // Obtener el contexto solo si existe
|
|
57
58
|
const error = errors ? errors[props.name ?? "-1"] : undefined;
|
|
58
59
|
const register = tempRegister
|
|
@@ -67,13 +68,18 @@ export const TextField = (props: Props) => {
|
|
|
67
68
|
const isReadOnly = disabled || readOnly;
|
|
68
69
|
const bgColor = isReadOnly ? "bg-gray-200" : `bg-${color}-50`;
|
|
69
70
|
const textColor = isReadOnly ? "text-gray-500" : `text-${color}-900`;
|
|
70
|
-
const borderColor = isReadOnly ? "border-gray-300" : `border-${color}-
|
|
71
|
+
const borderColor = isReadOnly ? "border-gray-300" : `border-${color}-200`;
|
|
71
72
|
|
|
72
73
|
useEffect(() => {
|
|
74
|
+
if (setOnFormValue) {
|
|
75
|
+
setOnFormValue(name ?? "-1", defaultValue);
|
|
76
|
+
}
|
|
73
77
|
setValue(defaultValue);
|
|
74
78
|
}, [defaultValue]);
|
|
75
79
|
|
|
76
80
|
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
81
|
+
const newValue = event.target.value;
|
|
82
|
+
|
|
77
83
|
if (register) {
|
|
78
84
|
register.onChange(event);
|
|
79
85
|
}
|
|
@@ -81,17 +87,9 @@ export const TextField = (props: Props) => {
|
|
|
81
87
|
event.stopPropagation();
|
|
82
88
|
event.preventDefault();
|
|
83
89
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (isInteger || value === "") {
|
|
88
|
-
setValue(value);
|
|
89
|
-
onChange && onChange(value, event);
|
|
90
|
-
}
|
|
91
|
-
} else {
|
|
92
|
-
setValue(event.target.value);
|
|
93
|
-
onChange && onChange(event.target.value, event);
|
|
94
|
-
}
|
|
90
|
+
|
|
91
|
+
setValue(newValue);
|
|
92
|
+
onChange && onChange(newValue, event);
|
|
95
93
|
};
|
|
96
94
|
|
|
97
95
|
const handleKeyDown = (event: React.KeyboardEvent) => {
|
|
@@ -114,11 +112,10 @@ export const TextField = (props: Props) => {
|
|
|
114
112
|
if (hidden) {
|
|
115
113
|
return (
|
|
116
114
|
<input
|
|
117
|
-
type={
|
|
115
|
+
type={type}
|
|
118
116
|
id={id ?? name}
|
|
119
117
|
value={value}
|
|
120
|
-
|
|
121
|
-
hidden={true}
|
|
118
|
+
hidden
|
|
122
119
|
{...(register ?? {})}
|
|
123
120
|
name={name}
|
|
124
121
|
onChange={handleInputChange}
|
|
@@ -129,10 +126,10 @@ export const TextField = (props: Props) => {
|
|
|
129
126
|
const inputComponent = (
|
|
130
127
|
<input
|
|
131
128
|
type={type}
|
|
132
|
-
readOnly={
|
|
129
|
+
readOnly={isReadOnly}
|
|
133
130
|
disabled={disabled}
|
|
134
131
|
id={id ?? name}
|
|
135
|
-
autoComplete="
|
|
132
|
+
autoComplete="off"
|
|
136
133
|
value={value}
|
|
137
134
|
onWheel={(e: React.WheelEvent<HTMLInputElement>) => {
|
|
138
135
|
e.currentTarget.blur();
|
|
@@ -45,7 +45,7 @@ export const FormTimePicker = (props: Props) => {
|
|
|
45
45
|
|
|
46
46
|
const isReadOnly = disabled;
|
|
47
47
|
const bgColor = isReadOnly ? "bg-gray-200" : `bg-${color}-50`;
|
|
48
|
-
const textColor = isReadOnly ? "text-gray-500" : `text-${color}-
|
|
48
|
+
const textColor = isReadOnly ? "text-gray-500" : `text-${color}-900`;
|
|
49
49
|
const borderColor = isReadOnly ? "border-gray-300" : `border-${color}-200`;
|
|
50
50
|
|
|
51
51
|
useEffect(() => {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useGetEmployeeProfile,
|
|
3
|
+
useGetSessionAttribute,
|
|
4
|
+
} from "@zauru-sdk/hooks";
|
|
5
|
+
import { Link } from "@remix-run/react";
|
|
6
|
+
|
|
7
|
+
export const ValidateEmployeeAccess = ({
|
|
8
|
+
children,
|
|
9
|
+
permissionVariableName,
|
|
10
|
+
showIfNoPermission = false,
|
|
11
|
+
}: {
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
permissionVariableName: string;
|
|
14
|
+
showIfNoPermission?: boolean;
|
|
15
|
+
}) => {
|
|
16
|
+
const { data: employee } = useGetEmployeeProfile();
|
|
17
|
+
const variable_string = useGetSessionAttribute(
|
|
18
|
+
permissionVariableName,
|
|
19
|
+
"sessionVariable"
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const variable = variable_string?.split(",");
|
|
23
|
+
const hasPermission = variable?.includes(employee?.id?.toString() || "-1");
|
|
24
|
+
|
|
25
|
+
if (showIfNoPermission && !hasPermission) {
|
|
26
|
+
return (
|
|
27
|
+
<div className="bg-gray-900 text-white min-h-screen flex flex-col items-center justify-center p-4">
|
|
28
|
+
<img src="/logo.png" alt="Zauru Logo" className="mb-8 h-20" />
|
|
29
|
+
<h1 className="text-5xl font-extrabold text-red-500 mb-6">
|
|
30
|
+
¡Acceso Denegado!
|
|
31
|
+
</h1>
|
|
32
|
+
<div className="w-full max-w-2xl">
|
|
33
|
+
<p className="text-2xl text-gray-300 mb-8 text-center">
|
|
34
|
+
Lo sentimos, no tienes permiso para acceder a esta página.
|
|
35
|
+
</p>
|
|
36
|
+
</div>
|
|
37
|
+
<Link
|
|
38
|
+
to="/"
|
|
39
|
+
className="bg-blue-600 text-white py-3 px-8 rounded-full text-lg font-semibold hover:bg-blue-700 transition duration-300 transform hover:scale-105"
|
|
40
|
+
>
|
|
41
|
+
Regresar al inicio
|
|
42
|
+
</Link>
|
|
43
|
+
<div className="mt-12 text-gray-500">
|
|
44
|
+
<p>Si crees que esto es un error, por favor contacta a soporte.</p>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return <>{hasPermission ? children : null}</>;
|
|
51
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isRouteErrorResponse,
|
|
3
|
+
Links,
|
|
4
|
+
Meta,
|
|
5
|
+
Scripts,
|
|
6
|
+
useRouteError,
|
|
7
|
+
Link,
|
|
8
|
+
} from "@remix-run/react";
|
|
9
|
+
|
|
10
|
+
export const ErrorLayout = () => {
|
|
11
|
+
const error = useRouteError();
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<html lang="es" className="bg-gray-900 text-white">
|
|
15
|
+
<head>
|
|
16
|
+
<meta charSet="utf-8" />
|
|
17
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
18
|
+
<title>¡Ups! Algo salió mal</title>
|
|
19
|
+
<Meta />
|
|
20
|
+
<Links />
|
|
21
|
+
</head>
|
|
22
|
+
<body className="min-h-screen flex flex-col items-center justify-center p-4">
|
|
23
|
+
<img src="/logo.png" alt="Zauru Logo" className="mb-8 h-20" />
|
|
24
|
+
<h1 className="text-5xl font-extrabold text-red-500 mb-6">¡Ups!</h1>
|
|
25
|
+
<div className="w-full max-w-2xl">
|
|
26
|
+
<p className="text-2xl text-gray-300 mb-8 text-center">
|
|
27
|
+
{isRouteErrorResponse(error)
|
|
28
|
+
? `Error ${error.status}: ${error.statusText}`
|
|
29
|
+
: error instanceof Error
|
|
30
|
+
? error.message
|
|
31
|
+
: "Ha ocurrido un error inesperado"}
|
|
32
|
+
</p>
|
|
33
|
+
</div>
|
|
34
|
+
<Link
|
|
35
|
+
to="/"
|
|
36
|
+
className="bg-blue-600 text-white py-3 px-8 rounded-full text-lg font-semibold hover:bg-blue-700 transition duration-300 transform hover:scale-105"
|
|
37
|
+
>
|
|
38
|
+
Regresar al inicio
|
|
39
|
+
</Link>
|
|
40
|
+
<div className="mt-12 text-gray-500">
|
|
41
|
+
<p>Si el problema persiste, por favor contacta a soporte.</p>
|
|
42
|
+
</div>
|
|
43
|
+
<Scripts />
|
|
44
|
+
</body>
|
|
45
|
+
</html>
|
|
46
|
+
);
|
|
47
|
+
};
|
package/src/Layouts/index.ts
CHANGED