@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.
Files changed (41) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/Form/ReactZodForm/index.d.ts +1 -0
  3. package/dist/HOC/ValidateEmployeeAccess/index.d.ts +5 -0
  4. package/dist/Layouts/errorLayout/index.d.ts +1 -0
  5. package/dist/Layouts/index.d.ts +1 -0
  6. package/dist/SidePanel/index.d.ts +21 -0
  7. package/dist/cjs/Buttons/Button.js +7 -7
  8. package/dist/cjs/Form/DatePicker/index.js +1 -1
  9. package/dist/cjs/Form/ReactZodForm/index.js +2 -4
  10. package/dist/cjs/Form/SelectField/index.js +29 -12
  11. package/dist/cjs/Form/TextField/index.js +10 -16
  12. package/dist/cjs/Form/TimePicker/index.js +1 -1
  13. package/dist/cjs/HOC/ValidateEmployeeAccess/index.js +17 -0
  14. package/dist/cjs/Layouts/errorLayout/index.js +14 -0
  15. package/dist/cjs/Layouts/index.js +1 -0
  16. package/dist/cjs/SidePanel/index.js +48 -0
  17. package/dist/cjs/index.js +2 -0
  18. package/dist/esm/Buttons/Button.js +7 -7
  19. package/dist/esm/Form/DatePicker/index.js +2 -2
  20. package/dist/esm/Form/ReactZodForm/index.js +2 -4
  21. package/dist/esm/Form/SelectField/index.js +30 -13
  22. package/dist/esm/Form/TextField/index.js +10 -16
  23. package/dist/esm/Form/TimePicker/index.js +1 -1
  24. package/dist/esm/HOC/ValidateEmployeeAccess/index.js +13 -0
  25. package/dist/esm/Layouts/errorLayout/index.js +10 -0
  26. package/dist/esm/Layouts/index.js +1 -0
  27. package/dist/esm/SidePanel/index.js +46 -0
  28. package/dist/esm/index.js +2 -0
  29. package/dist/index.d.ts +2 -0
  30. package/package.json +4 -4
  31. package/src/Buttons/Button.tsx +29 -28
  32. package/src/Form/DatePicker/index.tsx +2 -2
  33. package/src/Form/ReactZodForm/index.tsx +4 -2
  34. package/src/Form/SelectField/index.tsx +54 -36
  35. package/src/Form/TextField/index.tsx +14 -17
  36. package/src/Form/TimePicker/index.tsx +1 -1
  37. package/src/HOC/ValidateEmployeeAccess/index.tsx +51 -0
  38. package/src/Layouts/errorLayout/index.tsx +47 -0
  39. package/src/Layouts/index.ts +1 -0
  40. package/src/SidePanel/index.tsx +153 -0
  41. 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}-500`;
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
- if (integer && type === "number") {
34
- const value = event.target.value;
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: "hidden", id: id ?? name, value: value, readOnly: true, hidden: true, ...(register ?? {}), name: name, onChange: handleInputChange }));
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: readOnly, disabled: disabled, id: id ?? name, autoComplete: "given-name", value: value, onWheel: (e) => {
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}-500`;
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
+ };
@@ -1 +1,2 @@
1
1
  export * from "./homeLayout/index.js";
2
+ export * from "./errorLayout/index.js";
@@ -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
@@ -23,3 +23,5 @@ export * from "./Tooltip/index.js";
23
23
  export * from "./WithTooltip/index.js";
24
24
  export * from "./Wizards/index.js";
25
25
  export * from "./Zendesk/index.js";
26
+ export * from "./HOC/ValidateEmployeeAccess/index.js";
27
+ export * from "./SidePanel/index.js";
package/dist/index.d.ts CHANGED
@@ -23,3 +23,5 @@ export * from "./Tooltip/index.js";
23
23
  export * from "./WithTooltip/index.js";
24
24
  export * from "./Wizards/index.js";
25
25
  export * from "./Zendesk/index.js";
26
+ export * from "./HOC/ValidateEmployeeAccess/index.js";
27
+ export * from "./SidePanel/index.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zauru-sdk/components",
3
- "version": "1.0.112",
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.111",
42
- "framer-motion": "^11.0.8",
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": "965e3203a40d354fd5d512e985ad68cb821d6a32"
53
+ "gitHead": "f24dbaac6f5cc9543bdfcd33f20f704ead3965c3"
54
54
  }
@@ -83,34 +83,35 @@ export const Button = (props: Props) => {
83
83
  : "";
84
84
 
85
85
  const buttonContent = (
86
- <button
87
- type={type}
88
- name="action"
89
- disabled={
90
- loading || disabled || (enableFormErrorsValidation && formHasErrors)
91
- }
92
- value={name}
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>
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={onSubmit ? methods.handleSubmit(onSubmit) : undefined}
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, Controller } from "react-hook-form";
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
- control,
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}-500`;
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(name || "", defaultValue);
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(name || "", newValue);
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(name || "", option);
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(name || "", []);
155
+ if (setFormValue) {
156
+ setFormValue(name || "", []);
157
+ }
139
158
  } else {
140
159
  setValue(null);
141
160
  onChange && onChange(null);
142
- setFormValue(name || "", null);
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
- <Controller
249
- name={name || ""}
250
- control={control}
251
- rules={{ required }}
252
- defaultValue={defaultValue || (isMulti ? [] : null)}
253
- render={({ field }) => (
254
- <input
255
- {...field}
256
- type="text"
257
- id={id}
258
- value={inputValue}
259
- onFocus={() => setIsOpen(true)}
260
- onBlur={handleBlur}
261
- onKeyDown={handleKeyDown}
262
- readOnly={isReadOnly}
263
- disabled={disabled}
264
- className={`block w-full rounded-md ${bgColor} ${borderColor} ${textColor} shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm`}
265
- placeholder={
266
- isMulti ? "Select options..." : "Select an option..."
267
- }
268
- onChange={(e) => {
269
- field.onChange(e);
270
- handleInputChange(e);
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}-500`;
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
- if (integer && type === "number") {
85
- const value = event.target.value;
86
- const isInteger = /^[0-9]*$/.test(value);
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={"hidden"}
115
+ type={type}
118
116
  id={id ?? name}
119
117
  value={value}
120
- readOnly={true}
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={readOnly}
129
+ readOnly={isReadOnly}
133
130
  disabled={disabled}
134
131
  id={id ?? name}
135
- autoComplete="given-name"
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}-500`;
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
+ };
@@ -1 +1,2 @@
1
1
  export * from "./homeLayout/index.js";
2
+ export * from "./errorLayout/index.js";