anteriority-ui 1.0.0

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 (199) hide show
  1. package/.github/workflows/ci.yml +31 -0
  2. package/.github/workflows/publish.yml +45 -0
  3. package/.npmrc.bak +1 -0
  4. package/.storybook/main.js +15 -0
  5. package/.storybook/preview.jsx +26 -0
  6. package/AnteriorityUI.md +2400 -0
  7. package/CLAUDE.md +91 -0
  8. package/README.md +182 -0
  9. package/assets/anteriorityUI/AnteriorityUI.logo.jpeg +0 -0
  10. package/assets/favicon/anteriorityUI.ico +0 -0
  11. package/common/api/client.js +43 -0
  12. package/common/storage/LocalStorage.js +13 -0
  13. package/common/styles/typography.css +5 -0
  14. package/common/styles/variables.css +192 -0
  15. package/common/theme/muiTheme.js +9 -0
  16. package/features/password-reset/api.js +39 -0
  17. package/features/user-profile/api.js +5 -0
  18. package/hooks/useAdminPasswordReset.js +133 -0
  19. package/index.js +57 -0
  20. package/package.json +57 -0
  21. package/pnpm-workspace.yaml +3 -0
  22. package/screens/components/AlertModal/AlertModal.jsx +259 -0
  23. package/screens/components/AlertModal/AlertModal.module.css +185 -0
  24. package/screens/components/AlertModal/AlertModal.stories.jsx +87 -0
  25. package/screens/components/Avatar/index.jsx +0 -0
  26. package/screens/components/Avatar/index.module.css +0 -0
  27. package/screens/components/Avatar/utils.js +0 -0
  28. package/screens/components/Badge/index.jsx +33 -0
  29. package/screens/components/Badge/index.module.css +18 -0
  30. package/screens/components/Badge/index.stories.jsx +40 -0
  31. package/screens/components/Barnner/index.jsx +21 -0
  32. package/screens/components/Barnner/index.module.css +33 -0
  33. package/screens/components/Barnner/index.stories.jsx +39 -0
  34. package/screens/components/Barnner/utils.js +0 -0
  35. package/screens/components/BlueLink/index.jsx +27 -0
  36. package/screens/components/BlueLink/index.module.css +11 -0
  37. package/screens/components/BlueLink/index.stories.jsx +29 -0
  38. package/screens/components/Breadcrumbs/index.jsx +62 -0
  39. package/screens/components/Breadcrumbs/index.module.css +158 -0
  40. package/screens/components/Breadcrumbs/index.stories.jsx +37 -0
  41. package/screens/components/Button/index.jsx +169 -0
  42. package/screens/components/Button/index.module.css +168 -0
  43. package/screens/components/Button/index.stories.jsx +71 -0
  44. package/screens/components/Button/utils.js +28 -0
  45. package/screens/components/CalendarComponent/CustomDatePicker.jsx +248 -0
  46. package/screens/components/CalendarComponent/CustomDatePicker.module.css +323 -0
  47. package/screens/components/CalendarComponent/CustomDatePicker.stories.jsx +45 -0
  48. package/screens/components/CalendarPicker/Constants.js +12 -0
  49. package/screens/components/CalendarPicker/index.jsx +263 -0
  50. package/screens/components/CalendarPicker/index.module.css +147 -0
  51. package/screens/components/CalendarPicker/index.stories.jsx +45 -0
  52. package/screens/components/CalendarPicker/utils.js +42 -0
  53. package/screens/components/CardV1/index.jsx +165 -0
  54. package/screens/components/CardV1/index.module.css +232 -0
  55. package/screens/components/CardV1/index.stories.jsx +62 -0
  56. package/screens/components/CardV1/utils.js +49 -0
  57. package/screens/components/ChatWindow/index.jsx +251 -0
  58. package/screens/components/ChatWindow/index.module.css +311 -0
  59. package/screens/components/ChatWindow/index.stories.jsx +62 -0
  60. package/screens/components/Checks/index.jsx +31 -0
  61. package/screens/components/Checks/index.module.css +32 -0
  62. package/screens/components/Checks/index.stories.jsx +32 -0
  63. package/screens/components/Container/index.jsx +36 -0
  64. package/screens/components/Container/index.module.css +24 -0
  65. package/screens/components/Container/index.stories.jsx +32 -0
  66. package/screens/components/CopyText/index.jsx +42 -0
  67. package/screens/components/CopyText/index.module.css +52 -0
  68. package/screens/components/CopyText/index.stories.jsx +24 -0
  69. package/screens/components/CountdownBar/constants.js +3 -0
  70. package/screens/components/CountdownBar/index.jsx +81 -0
  71. package/screens/components/CountdownBar/index.module.css +45 -0
  72. package/screens/components/CountdownBar/index.stories.jsx +43 -0
  73. package/screens/components/CountdownBar/utils.js +119 -0
  74. package/screens/components/DateTag/index.jsx +33 -0
  75. package/screens/components/DateTag/index.module.css +12 -0
  76. package/screens/components/DateTag/index.stories.jsx +29 -0
  77. package/screens/components/DesplegablePanel/constants.js +0 -0
  78. package/screens/components/DesplegablePanel/index.jsx +61 -0
  79. package/screens/components/DesplegablePanel/index.module.css +120 -0
  80. package/screens/components/DesplegablePanel/index.stories.jsx +44 -0
  81. package/screens/components/DesplegablePanel/utils.js +0 -0
  82. package/screens/components/DrawPanel/index.jsx +217 -0
  83. package/screens/components/DrawPanel/index.module.css +252 -0
  84. package/screens/components/DrawPanel/index.stories.jsx +66 -0
  85. package/screens/components/DrawPanel/utils.js +0 -0
  86. package/screens/components/Filter/index.jsx +105 -0
  87. package/screens/components/Filter/index.module.css +91 -0
  88. package/screens/components/Filter/index.stories.jsx +46 -0
  89. package/screens/components/Filter/utils.js +0 -0
  90. package/screens/components/FormControls/FormControls.module.css +86 -0
  91. package/screens/components/FormControls/index.jsx +51 -0
  92. package/screens/components/FormControls/index.stories.jsx +36 -0
  93. package/screens/components/Frame/index.jsx +54 -0
  94. package/screens/components/Frame/index.module.css +102 -0
  95. package/screens/components/Frame/index.stories.jsx +37 -0
  96. package/screens/components/Frame/utils.js +0 -0
  97. package/screens/components/GreenHighlight/index.jsx +11 -0
  98. package/screens/components/GreenHighlight/index.module.css +7 -0
  99. package/screens/components/GreenHighlight/index.stories.jsx +26 -0
  100. package/screens/components/GreenHighlight/utils.jsx +45 -0
  101. package/screens/components/HoursPicker/Constants.js +26 -0
  102. package/screens/components/HoursPicker/index.jsx +350 -0
  103. package/screens/components/HoursPicker/index.module.css +99 -0
  104. package/screens/components/HoursPicker/index.stories.jsx +44 -0
  105. package/screens/components/HoursPicker/utils.js +48 -0
  106. package/screens/components/InfoTooltip/index.jsx +59 -0
  107. package/screens/components/InfoTooltip/index.module.css +98 -0
  108. package/screens/components/InfoTooltip/index.stories.jsx +35 -0
  109. package/screens/components/Input/index.jsx +36 -0
  110. package/screens/components/Input/index.module.css +49 -0
  111. package/screens/components/Input/index.stories.jsx +37 -0
  112. package/screens/components/Input/utils.js +0 -0
  113. package/screens/components/Loading/index.jsx +76 -0
  114. package/screens/components/Loading/index.module.css +193 -0
  115. package/screens/components/Loading/index.stories.jsx +38 -0
  116. package/screens/components/Loading/utils.js +0 -0
  117. package/screens/components/MarkdownContent/index.jsx +16 -0
  118. package/screens/components/MarkdownContent/index.module.css +149 -0
  119. package/screens/components/MarkdownContent/index.stories.jsx +30 -0
  120. package/screens/components/MarkdownEditor/index.jsx +430 -0
  121. package/screens/components/MarkdownEditor/index.module.css +192 -0
  122. package/screens/components/MarkdownEditor/index.stories.jsx +50 -0
  123. package/screens/components/Material-UI/Components/InputWithIcon/index.jsx +87 -0
  124. package/screens/components/Material-UI/Components/InputWithIcon/index.stories.jsx +49 -0
  125. package/screens/components/Material-UI/Components/MenuPopover/index.jsx +117 -0
  126. package/screens/components/Material-UI/Components/MenuPopover/index.stories.jsx +36 -0
  127. package/screens/components/Material-UI/Components/MultilinePlaceholder/index.jsx +44 -0
  128. package/screens/components/Material-UI/Components/MultilinePlaceholder/index.stories.jsx +45 -0
  129. package/screens/components/Material-UI/Components/Paginador/index.jsx +79 -0
  130. package/screens/components/Material-UI/Components/Paginador/index.stories.jsx +49 -0
  131. package/screens/components/Material-UI/Components/Search/index.jsx +81 -0
  132. package/screens/components/Material-UI/Components/Search/index.stories.jsx +68 -0
  133. package/screens/components/Material-UI/Components/Selector/index.jsx +100 -0
  134. package/screens/components/Material-UI/Components/Selector/index.module.css +0 -0
  135. package/screens/components/Material-UI/Components/Selector/index.stories.jsx +52 -0
  136. package/screens/components/Material-UI/Components/Selector/utils.js +0 -0
  137. package/screens/components/Material-UI/Components/TextField/index.jsx +61 -0
  138. package/screens/components/Material-UI/Components/TextField/index.stories.jsx +53 -0
  139. package/screens/components/ModalTooltip/ModalTooltip.jsx +122 -0
  140. package/screens/components/ModalTooltip/ModalTooltip.module.css +65 -0
  141. package/screens/components/ModalTooltip/ModalTooltip.stories.jsx +39 -0
  142. package/screens/components/Notification/Error/ErrorMessage.jsx +30 -0
  143. package/screens/components/Notification/Error/ErrorMessage.module.css +57 -0
  144. package/screens/components/Notification/Error/ErrorMessage.stories.jsx +37 -0
  145. package/screens/components/PasswordResetPanel/index.jsx +325 -0
  146. package/screens/components/PasswordResetPanel/index.stories.jsx +26 -0
  147. package/screens/components/PlaceholderButton/index.jsx +169 -0
  148. package/screens/components/PlaceholderButton/index.module.css +162 -0
  149. package/screens/components/PlaceholderButton/index.stories.jsx +69 -0
  150. package/screens/components/PlaceholderButton/utils.js +42 -0
  151. package/screens/components/Profile/Avatar/index.jsx +125 -0
  152. package/screens/components/Profile/Avatar/index.module.css +86 -0
  153. package/screens/components/Profile/Avatar/index.stories.jsx +48 -0
  154. package/screens/components/Profile/Avatar/utils.js +18 -0
  155. package/screens/components/Profile/index.jsx +58 -0
  156. package/screens/components/Profile/index.module.css +0 -0
  157. package/screens/components/Profile/index.stories.jsx +38 -0
  158. package/screens/components/Profile/utils.js +0 -0
  159. package/screens/components/ScrollBar/index.jsx +62 -0
  160. package/screens/components/ScrollBar/index.module.css +78 -0
  161. package/screens/components/ScrollBar/index.stories.jsx +64 -0
  162. package/screens/components/ScrollBar/utils.js +127 -0
  163. package/screens/components/Sidebar/constants.js +16 -0
  164. package/screens/components/Sidebar/index.jsx +399 -0
  165. package/screens/components/Sidebar/index.module.css +405 -0
  166. package/screens/components/Sidebar/index.stories.jsx +86 -0
  167. package/screens/components/Sidebar/utils.js +75 -0
  168. package/screens/components/Skeleton/index.jsx +11 -0
  169. package/screens/components/Skeleton/index.module.css +26 -0
  170. package/screens/components/Skeleton/index.stories.jsx +42 -0
  171. package/screens/components/SubSidebarLayout/index.jsx +44 -0
  172. package/screens/components/SubSidebarLayout/index.module.css +80 -0
  173. package/screens/components/SubSidebarLayout/index.stories.jsx +51 -0
  174. package/screens/components/SummaryCard/index.jsx +50 -0
  175. package/screens/components/SummaryCard/index.module.css +30 -0
  176. package/screens/components/SummaryCard/index.stories.jsx +29 -0
  177. package/screens/components/Swich/constants.js +4 -0
  178. package/screens/components/Swich/index.jsx +85 -0
  179. package/screens/components/Swich/index.module.css +68 -0
  180. package/screens/components/Swich/index.stories.jsx +44 -0
  181. package/screens/components/Swich/utils.js +0 -0
  182. package/screens/components/TableA/index.jsx +156 -0
  183. package/screens/components/TableA/index.module.css +105 -0
  184. package/screens/components/TableA/index.stories.jsx +116 -0
  185. package/screens/components/TableA/utils.js +11 -0
  186. package/screens/components/TableB/index.jsx +222 -0
  187. package/screens/components/TableB/index.module.css +295 -0
  188. package/screens/components/TableB/index.stories.jsx +110 -0
  189. package/screens/components/TableB/utils.js +11 -0
  190. package/screens/components/ToggleCard/index.jsx +64 -0
  191. package/screens/components/ToggleCard/index.module.css +111 -0
  192. package/screens/components/ToggleCard/index.stories.jsx +49 -0
  193. package/screens/components/UserEditPopover/index.jsx +102 -0
  194. package/screens/components/UserEditPopover/index.module.css +97 -0
  195. package/screens/components/UserEditPopover/index.stories.jsx +49 -0
  196. package/scripts/build.mjs +129 -0
  197. package/scripts/check-case.mjs +127 -0
  198. package/scripts/verify-dist.mjs +45 -0
  199. package/vitest.config.js +36 -0
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import { AlertCircle, CheckCircle } from 'lucide-react';
3
+ import styles from './ErrorMessage.module.css';
4
+
5
+ const ErrorMessage = ({ message, title = "Atención", variant = "danger", bgColor, borderColor, style }) => {
6
+ if (!message) return null;
7
+
8
+ return (
9
+ <div
10
+ className={`${styles.errorBanner} ${styles[variant] || ''}`}
11
+ style={{
12
+ ...(bgColor ? { '--errormessage-bg': bgColor } : {}),
13
+ ...(borderColor ? { '--errormessage-border': borderColor } : {}),
14
+ ...style,
15
+ }}
16
+ >
17
+ {variant === 'success' ? (
18
+ <CheckCircle size={18} className={styles.successIcon} />
19
+ ) : (
20
+ <AlertCircle size={18} className={styles.errorIcon} />
21
+ )}
22
+ <div className={styles.errorTextContent}>
23
+ <strong className={styles.errorTitle}>{title}</strong>
24
+ <span className={styles.errorText}>{message}</span>
25
+ </div>
26
+ </div>
27
+ );
28
+ };
29
+
30
+ export default ErrorMessage;
@@ -0,0 +1,57 @@
1
+ .errorBanner {
2
+ display: flex;
3
+ align-items: flex-start;
4
+ gap: 12px;
5
+ padding: 12px 16px;
6
+ border-radius: 8px;
7
+ margin: 10px 0;
8
+ animation: slideIn 0.3s ease-out;
9
+ border: 1px solid var(--errormessage-border, #fecdd3);
10
+ background-color: var(--errormessage-bg, #fff1f2);
11
+ }
12
+
13
+ .errorIcon {
14
+ color: #dc2626;
15
+ flex-shrink: 0;
16
+ margin-top: 2px;
17
+ }
18
+
19
+ .errorTextContent {
20
+ display: flex;
21
+ flex-direction: column;
22
+ font-size: 0.85rem;
23
+ line-height: 1.4;
24
+ }
25
+
26
+ .errorTitle {
27
+ color: #991b1b;
28
+ margin-bottom: 2px;
29
+ }
30
+
31
+ .errorText {
32
+ color: #b91c1c;
33
+ }
34
+
35
+ @keyframes slideIn {
36
+ from { opacity: 0; transform: translateY(-10px); }
37
+ to { opacity: 1; transform: translateY(0); }
38
+ }
39
+
40
+ .success {
41
+ border-color: var(--errormessage-border, #bbf7d0);
42
+ background-color: var(--errormessage-bg, #f0fdf4);
43
+ }
44
+
45
+ .success .errorTitle {
46
+ color: #166534;
47
+ }
48
+
49
+ .success .errorText {
50
+ color: #15803d;
51
+ }
52
+
53
+ .successIcon {
54
+ color: #22c55e;
55
+ flex-shrink: 0;
56
+ margin-top: 2px;
57
+ }
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+ import ErrorMessage from './ErrorMessage';
3
+
4
+ export default {
5
+ title: 'Components/Notification/Error/ErrorMessage',
6
+ component: ErrorMessage,
7
+ };
8
+
9
+ export const Danger = {
10
+ args: {
11
+ title: 'Atención',
12
+ message: 'No se pudo completar la operación. Intenta de nuevo.',
13
+ variant: 'danger',
14
+ },
15
+ };
16
+
17
+ export const Success = {
18
+ args: {
19
+ title: 'Operación Exitosa',
20
+ message: '¡Contraseña actualizada correctamente!',
21
+ variant: 'success',
22
+ },
23
+ };
24
+
25
+ export const CustomTitle = {
26
+ args: {
27
+ title: 'Error de validación',
28
+ message: 'El correo electrónico ya está registrado.',
29
+ variant: 'danger',
30
+ },
31
+ };
32
+
33
+ export const NoMessage = {
34
+ args: {
35
+ message: '',
36
+ },
37
+ };
@@ -0,0 +1,325 @@
1
+ import React, { useState, useEffect } from "react";
2
+ import { Mail, Eye, EyeOff } from "lucide-react";
3
+ import DrawPanel from "../DrawPanel/index";
4
+ import TextField from "../Material-UI/Components/TextField/index";
5
+ import ToggleCard from "../ToggleCard";
6
+ import Loading from "../Loading/index";
7
+ import ErrorMessage from "../Notification/Error/ErrorMessage";
8
+ import Button from "../Button/index";
9
+ import CountdownBar from "../CountdownBar";
10
+ import DateTag from "../DateTag";
11
+ import useAdminPasswordReset from "../../../hooks/useAdminPasswordReset";
12
+
13
+ const DEFAULT_PENDING_COLOR = { label: '#b45309', bg: '#fffbeb', text: '#92400e' };
14
+ const DEFAULT_ACCEPTED_COLOR = { label: '#16a34a', bg: '#f0fdf4', text: '#166534' };
15
+ const DEFAULT_EXPIRED_COLOR = { label: '#ef4444', bg: '#fef2f2', text: '#991b1b' };
16
+
17
+ export default function PasswordResetPanel({
18
+ isOpen,
19
+ onClose,
20
+ cancelColor = 'var(--button-red, #ef4444)',
21
+ pendingColor = DEFAULT_PENDING_COLOR,
22
+ acceptedColor = DEFAULT_ACCEPTED_COLOR,
23
+ expiredColor = DEFAULT_EXPIRED_COLOR,
24
+ }) {
25
+ const {
26
+ requestPasswordReset,
27
+ assignNewPassword,
28
+ requestLoading,
29
+ pwdLoading,
30
+ error: apiError,
31
+ success: pwdSuccess,
32
+ status: pwdStatus,
33
+ setStatus: setPwdStatus,
34
+ clearError,
35
+ pendingList,
36
+ fetchLoading,
37
+ fetchPendingList,
38
+ startPolling,
39
+ stopPolling,
40
+ activeTicket,
41
+ setActiveTicket,
42
+ resetResetState
43
+ } = useAdminPasswordReset();
44
+
45
+ const [pwdData, setPwdData] = useState({ email: '', newPassword: '', confirmPassword: '' });
46
+ const [showPass, setShowPass] = useState(false);
47
+ const [localPwdError, setLocalPwdError] = useState(null);
48
+
49
+ const resolvedPendingColor = { ...DEFAULT_PENDING_COLOR, ...pendingColor };
50
+ const resolvedAcceptedColor = { ...DEFAULT_ACCEPTED_COLOR, ...acceptedColor };
51
+ const resolvedExpiredColor = { ...DEFAULT_EXPIRED_COLOR, ...expiredColor };
52
+
53
+ // Map API status to UI Steps
54
+ const pwdStep = pwdStatus === 'Idle' ? 1 : pwdStatus === 'Pending' ? 2 : 3;
55
+
56
+ const [showPwdToast, setShowPwdToast] = useState(false);
57
+
58
+ useEffect(() => {
59
+ if (pwdStep === 2) {
60
+ setShowPwdToast(true);
61
+ } else {
62
+ setShowPwdToast(false);
63
+ }
64
+ }, [pwdStep]);
65
+
66
+ useEffect(() => {
67
+ if (isOpen) {
68
+ fetchPendingList();
69
+ } else {
70
+ setPwdData({ email: '', newPassword: '', confirmPassword: '' });
71
+ setLocalPwdError(null);
72
+ resetResetState();
73
+ stopPolling();
74
+ }
75
+ }, [isOpen, fetchPendingList, resetResetState, stopPolling]);
76
+
77
+ const handleContinuePending = (pendingUser) => {
78
+ setPwdData({ ...pwdData, email: pendingUser.email });
79
+ setPwdStatus(pendingUser.status);
80
+ setActiveTicket(pendingUser);
81
+ if (pendingUser.status === 'Pending') {
82
+ startPolling(pendingUser.email);
83
+ }
84
+ };
85
+
86
+ useEffect(() => {
87
+ if (localPwdError) setLocalPwdError(null);
88
+ }, [pwdData]);
89
+
90
+ const handlePwdChange = (e) => setPwdData({ ...pwdData, [e.target.name]: e.target.value });
91
+
92
+ const submitPasswordChange = async (e) => {
93
+ if (e) e.preventDefault();
94
+ clearError();
95
+
96
+ if (pwdStep === 1) {
97
+ if (!pwdData.email) {
98
+ setLocalPwdError("Ingresa el correo del usuario.");
99
+ return;
100
+ }
101
+ await requestPasswordReset(pwdData.email);
102
+ return;
103
+ }
104
+
105
+ if (pwdStep === 3) {
106
+ if (pwdData.newPassword !== pwdData.confirmPassword) {
107
+ setLocalPwdError("Las contraseñas nuevas no coinciden");
108
+ return;
109
+ }
110
+ if (pwdData.newPassword.length < 6) {
111
+ setLocalPwdError("La contraseña debe tener al menos 6 caracteres");
112
+ return;
113
+ }
114
+ const ok = await assignNewPassword(pwdData.email, pwdData.newPassword);
115
+ if (ok) {
116
+ setTimeout(() => {
117
+ resetResetState();
118
+ setPwdData({ email: '', newPassword: '', confirmPassword: '' });
119
+ fetchPendingList();
120
+ }, 3000);
121
+ }
122
+ }
123
+ };
124
+
125
+ return (
126
+ <DrawPanel
127
+ isOpen={isOpen}
128
+ onClose={onClose}
129
+ title="Restablecimiento de Contraseñas"
130
+ showActions={true}
131
+ onConfirm={submitPasswordChange}
132
+ confirmText={pwdStep === 1 ? "Solicitar Aprobación" : "Cambiar Clave"}
133
+ cancelText="Cancelar"
134
+ loading={pwdLoading || requestLoading}
135
+ formId="changePwdForm"
136
+ >
137
+ {showPwdToast && (
138
+ <ToggleCard
139
+ title="Recuperación de Contraseña"
140
+ description={`Se envio la solicitua al usuario ${pwdData.email}`}
141
+ icon={Mail}
142
+ iconColorVariant="blue"
143
+ onClose={() => setShowPwdToast(false)}
144
+ variant="floating"
145
+ />
146
+ )}
147
+
148
+ <form id="changePwdForm" onSubmit={submitPasswordChange} autoComplete="off" style={{ display: 'flex', flexDirection: 'column', gap: '1.2rem', padding: '10px 0' }}>
149
+
150
+ {(requestLoading || pwdLoading || fetchLoading || (pwdStep === 2 && (!activeTicket || !activeTicket.expiracion))) ? (
151
+ <div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', minHeight: '300px' }}>
152
+ <Loading
153
+ size="large"
154
+ phrases={["Iniciando solicitud"]}
155
+ variant=''
156
+ />
157
+ </div>
158
+ ) : pwdStep === 2 ? (
159
+ <div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', minHeight: '300px', gap: '1.5rem', width: '100%' }}>
160
+ <Loading
161
+ size="large"
162
+ phrases={["Cargando", "Esperando la aceptación del usuario", "Verificando"]}
163
+ variant='bounce'
164
+ />
165
+ <div style={{ textAlign: 'center', width: '100%' }}>
166
+ <p style={{ margin: '0 0 8px 0', fontSize: '0.9rem', color: '#475569' }}>
167
+ Esperando por: <b>{pwdData.email}</b>
168
+ </p>
169
+ <div style={{ width: '100%', padding: '0 20px', boxSizing: 'border-box', marginBottom: '15px' }}>
170
+ <CountdownBar
171
+ active={true}
172
+ expiresAt={activeTicket?.expiracion}
173
+ totalDuration={(activeTicket?.expiresInSeconds || activeTicket?.expires_in_seconds || 900) * 1000}
174
+ />
175
+ </div>
176
+ <Button
177
+ type="button"
178
+ onClick={() => {
179
+ resetResetState();
180
+ stopPolling();
181
+ }}
182
+ color={cancelColor}
183
+ size="small"
184
+ style={{ fontSize: '0.75rem', marginTop: '10px' }}
185
+ >
186
+ Cancelar Espera
187
+ </Button>
188
+ </div>
189
+ </div>
190
+ ) : (
191
+ <>
192
+ {pwdStep === 1 && !fetchLoading && (
193
+ <>
194
+ {pendingList && pendingList.length > 0 && (
195
+ <div style={{ marginBottom: '20px' }}>
196
+ <h4 style={{ margin: '0 0 10px 0', color: '#334155', fontSize: '0.9rem' }}>Hay solicitudes en proceso:</h4>
197
+ <ul style={{ listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexDirection: 'column', gap: '8px' }}>
198
+ {pendingList.map((item, idx) => {
199
+ const isExpired = item.status === 'Pending' && (
200
+ item.expiresInSeconds === 0 ||
201
+ item.expires_in_seconds === 0 ||
202
+ (item.expiracion && new Date(item.expiracion).getTime() <= Date.now())
203
+ );
204
+
205
+ return (
206
+ <li key={idx} style={{ background: '#f8fafc', border: '1px solid #e2e8f0', borderRadius: '6px', padding: '10px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
207
+ <div>
208
+ <div style={{ fontSize: '0.85rem', fontWeight: 'bold', color: '#0f172a' }}>{item.email}</div>
209
+ <div style={{
210
+ fontSize: '0.75rem',
211
+ color: isExpired ? resolvedExpiredColor.label : item.status === 'Accepted' ? resolvedAcceptedColor.label : resolvedPendingColor.label,
212
+ marginBottom: '6px'
213
+ }}>
214
+ {isExpired ? 'Solicitud expirada' : item.status === 'Accepted' ? 'Lista para asignar nueva clave' : 'Enviado al correo'}
215
+ </div>
216
+ {item.expiracion && (
217
+ <DateTag
218
+ date={item.expiracion}
219
+ bgColor={isExpired ? resolvedExpiredColor.bg : item.status === 'Accepted' ? resolvedAcceptedColor.bg : resolvedPendingColor.bg}
220
+ textColor={isExpired ? resolvedExpiredColor.text : item.status === 'Accepted' ? resolvedAcceptedColor.text : resolvedPendingColor.text}
221
+ />
222
+ )}
223
+ </div>
224
+ <Button
225
+ onClick={() => handleContinuePending(item)}
226
+ color={isExpired ? 'inherit' : item.status === 'Accepted' ? 'var(--button-green)' : 'primary'}
227
+ size="small"
228
+ style={{ fontSize: '0.75rem', padding: '4px 10px', minWidth: 'auto' }}
229
+ disabled={isExpired}
230
+ >
231
+ Continuar
232
+ </Button>
233
+ </li>
234
+ );
235
+ })}
236
+ </ul>
237
+ <hr style={{ border: '0', borderTop: '1px solid #e2e8f0', margin: '20px 0' }} />
238
+ <h4 style={{ margin: '0 0 10px 0', color: '#334155', fontSize: '0.9rem' }}>O iniciar nueva solicitud:</h4>
239
+ </div>
240
+ )}
241
+ <ToggleCard
242
+ title="Correo Electrónico"
243
+ description="Ingresa el correo electrónico del usuario para enviarle una solicitud de aprobación para el cambio de clave."
244
+ icon={Mail}
245
+ iconColorVariant="blue"
246
+ isOpen={false}
247
+ />
248
+ <TextField
249
+ label="Correo Electrónico"
250
+ name="email"
251
+ value={pwdData.email}
252
+ onChange={handlePwdChange}
253
+ placeholder="usuario@correo.com"
254
+ disabled={pwdLoading || requestLoading}
255
+ autoComplete="off"
256
+ />
257
+ </>
258
+ )}
259
+
260
+ {pwdStep === 3 && (
261
+ <>
262
+ <ToggleCard
263
+ title="Correo Electrónico"
264
+ description="A continuación ingrese la nueva clave, esta debe tener al menos 6 caracteres, puede cerrar el panel, pero tras 15 minutos expirará la solicitud."
265
+ icon={Mail}
266
+ iconColorVariant="blue"
267
+ isOpen={false}
268
+ />
269
+ <TextField
270
+ label="Correo Electrónico (Autocompletado)"
271
+ name="email"
272
+ value={pwdData.email || 'usuario@correo.com'}
273
+ disabled={true}
274
+ />
275
+ <div style={{ position: 'relative', marginTop: '10px' }}>
276
+ <TextField
277
+ label="Nueva Contraseña"
278
+ name="newPassword"
279
+ type={showPass ? "text" : "password"}
280
+ value={pwdData.newPassword}
281
+ onChange={handlePwdChange}
282
+ placeholder="Mínimo 6 caracteres"
283
+ disabled={pwdLoading}
284
+ autoComplete="new-password"
285
+ slotProps={{
286
+ input: {
287
+ endAdornment: (
288
+ <button type="button" onClick={() => setShowPass(!showPass)} tabIndex={-1} style={{ position: 'absolute', right: '10px', top: '50%', transform: 'translateY(-50%)', background: 'none', border: 'none', cursor: 'pointer', display: 'flex', alignItems: 'center' }}>
289
+ {showPass ? <EyeOff size={18} color="#64748b" /> : <Eye size={18} color="#64748b" />}
290
+ </button>
291
+ )
292
+ }
293
+ }}
294
+ />
295
+ </div>
296
+ <TextField
297
+ label="Confirmar Nueva Contraseña"
298
+ name="confirmPassword"
299
+ type={showPass ? "text" : "password"}
300
+ value={pwdData.confirmPassword}
301
+ onChange={handlePwdChange}
302
+ placeholder="Repite la nueva contraseña"
303
+ disabled={pwdLoading}
304
+ autoComplete="new-password"
305
+ />
306
+ </>
307
+ )}
308
+ <ErrorMessage
309
+ message={apiError || localPwdError}
310
+ title="Error en la solicitud"
311
+ variant="danger"
312
+ />
313
+ {pwdSuccess && (
314
+ <ErrorMessage
315
+ message="¡Contraseña actualizada correctamente!"
316
+ title="Operación Exitosa"
317
+ variant="success"
318
+ />
319
+ )}
320
+ </>
321
+ )}
322
+ </form>
323
+ </DrawPanel>
324
+ );
325
+ }
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+ import PasswordResetPanel from './index';
3
+
4
+ export default {
5
+ title: 'Components/PasswordResetPanel',
6
+ component: PasswordResetPanel,
7
+ };
8
+
9
+ // Note: this component calls a real hook (useAdminPasswordReset) which performs
10
+ // fetch() calls to relative API paths (e.g. /users/pending-resets). There is no
11
+ // backend in Storybook, so those requests will fail/reject — this is expected,
12
+ // and the panel still renders its initial/loading/error UI without crashing.
13
+
14
+ export const Open = {
15
+ args: {
16
+ isOpen: true,
17
+ onClose: () => console.log('close panel'),
18
+ },
19
+ };
20
+
21
+ export const Closed = {
22
+ args: {
23
+ isOpen: false,
24
+ onClose: () => console.log('close panel'),
25
+ },
26
+ };
@@ -0,0 +1,169 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Save, Loader2 } from 'lucide-react';
3
+ import styles from './index.module.css';
4
+ import { handleAction } from './utils';
5
+
6
+ import Button from '../Button/index';
7
+ import InputWithIcon from '../Material-UI/Components/InputWithIcon/index';
8
+
9
+ const PlaceholderButton = ({
10
+ icon: Icon,
11
+ tooltipText,
12
+ placeholder,
13
+ unitText,
14
+ buttonText,
15
+ hintText,
16
+ disabledHintText,
17
+
18
+ initialValue = '',
19
+ inputType = 'number',
20
+ min = "1",
21
+ disabled = false,
22
+ multiline = false,
23
+
24
+ onSavePromise,
25
+ clearCache,
26
+
27
+ successMessage,
28
+ errorMessage,
29
+ validationErrorMsg,
30
+ clearOnSuccess = false,
31
+
32
+ focusColor,
33
+ unitBgColor,
34
+ }) => {
35
+ const [value, setValue] = useState(initialValue);
36
+ const [isSaving, setIsSaving] = useState(false);
37
+ const [message, setMessage] = useState({ text: '', type: '' });
38
+
39
+ useEffect(() => {
40
+ setValue(initialValue);
41
+ }, [initialValue]);
42
+
43
+ const onSaveClick = () => {
44
+ handleAction({
45
+ actionPromise: onSavePromise,
46
+ value: inputType === 'number' ? parseInt(value) : value,
47
+ setIsSaving,
48
+ setMessage,
49
+ clearCache,
50
+ successMessage,
51
+ errorMessage,
52
+ validationErrorMsg,
53
+ validate: (v) => inputType === 'number' ? (v && v >= 1) : !!v,
54
+ setValue,
55
+ clearOnSuccess
56
+ });
57
+ };
58
+
59
+ const handleKeyDown = (e) => {
60
+ if (e.key === 'Enter') {
61
+ if (multiline) {
62
+ if (e.ctrlKey) {
63
+ e.preventDefault();
64
+ const start = e.target.selectionStart;
65
+ const end = e.target.selectionEnd;
66
+ const val = e.target.value;
67
+ setValue(val.substring(0, start) + "\n" + val.substring(end));
68
+ setTimeout(() => {
69
+ e.target.selectionStart = e.target.selectionEnd = start + 1;
70
+ }, 0);
71
+ } else {
72
+ e.preventDefault();
73
+ if (!isSaving && !disabled) {
74
+ onSaveClick();
75
+ }
76
+ }
77
+ } else {
78
+ if (!isSaving && !disabled) {
79
+ e.preventDefault();
80
+ onSaveClick();
81
+ }
82
+ }
83
+ }
84
+ };
85
+
86
+ return (
87
+ <div
88
+ className={`${styles.configContainer} ${disabled ? styles.disabledContainer : ''}`}
89
+ style={{
90
+ ...(focusColor ? { '--placeholderbutton-focus': focusColor } : {}),
91
+ ...(unitBgColor ? { '--placeholderbutton-unit-bg': unitBgColor } : {}),
92
+ }}
93
+ >
94
+ <div className={styles.inputGroup}>
95
+ {multiline ? (
96
+ <InputWithIcon
97
+ value={value}
98
+ onChange={(e) => setValue(e.target.value)}
99
+ onKeyDown={handleKeyDown}
100
+ placeholder={placeholder}
101
+ disabled={disabled}
102
+ multiline
103
+ maxRows={4}
104
+ variant="outlined"
105
+ style={{ flex: 1 }}
106
+ />
107
+ ) : (
108
+ <div className={styles.inputWrapper}>
109
+ <InputWithIcon
110
+ variant="standard"
111
+ icon={Icon}
112
+ type={inputType}
113
+ value={value}
114
+ onChange={(e) => setValue(e.target.value)}
115
+ onKeyDown={handleKeyDown}
116
+ placeholder={placeholder}
117
+ disabled={disabled}
118
+ disableUnderline
119
+ fullWidth
120
+ inputProps={{
121
+ min: min,
122
+ className: styles.daysInput
123
+ }}
124
+ sx={{
125
+ '& .MuiInput-root': {
126
+ height: '100%',
127
+ marginTop: '0px !important',
128
+ },
129
+ flex: 1
130
+ }}
131
+ />
132
+ {unitText && <span className={styles.unitText}>{unitText}</span>}
133
+ </div>
134
+ )}
135
+
136
+ <Button
137
+ variant={Button.VARIANTS.PRIMARY}
138
+ size={Button.SIZES.LARGE}
139
+ icon={isSaving ? Loader2 : Save}
140
+ color={Button.COLORS.BLUE}
141
+ onClick={onSaveClick}
142
+ loading={isSaving}
143
+ >
144
+ {buttonText}
145
+ </Button>
146
+ </div>
147
+
148
+ {disabled && disabledHintText && (
149
+ <p className={styles.infoTextDisabled}>
150
+ {disabledHintText}
151
+ </p>
152
+ )}
153
+
154
+ {message.text && (
155
+ <p className={`${styles.statusMessage} ${styles[message.type]}`}>
156
+ {message.text}
157
+ </p>
158
+ )}
159
+
160
+ {hintText && (
161
+ <p className={styles.hintText}>
162
+ {hintText}
163
+ </p>
164
+ )}
165
+ </div>
166
+ );
167
+ };
168
+
169
+ export default PlaceholderButton;