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,133 @@
1
+ import { useState, useEffect, useRef, useCallback } from 'react';
2
+ import api from '../features/password-reset/api';
3
+
4
+ export default function useAdminPasswordReset() {
5
+ const [requestLoading, setRequestLoading] = useState(false);
6
+ const [pwdLoading, setPwdLoading] = useState(false);
7
+ const [error, setError] = useState(null);
8
+ const [success, setSuccess] = useState(false);
9
+ const [status, setStatus] = useState('Idle'); // Idle, Pending, Accepted, Completed
10
+ const [pendingList, setPendingList] = useState([]);
11
+ const [fetchLoading, setFetchLoading] = useState(false);
12
+ const [activeTicket, setActiveTicket] = useState(null);
13
+
14
+ const pollingInterval = useRef(null);
15
+
16
+ const clearError = () => setError(null);
17
+
18
+ const fetchPendingList = useCallback(async () => {
19
+ setFetchLoading(true);
20
+ try {
21
+ const data = await api.getPendingResets();
22
+ setPendingList(Array.isArray(data) ? data : []);
23
+ } catch (err) {
24
+ console.error("Error al obtener reseteos pendientes:", err);
25
+ } finally {
26
+ setFetchLoading(false);
27
+ }
28
+ }, []);
29
+
30
+ // Paso 1: Solicitar el reseteo
31
+ const requestPasswordReset = async (email) => {
32
+ setRequestLoading(true);
33
+ setError(null);
34
+ setSuccess(false);
35
+ try {
36
+ await api.requestPasswordReset(email);
37
+ setStatus('Pending');
38
+ startPolling(email);
39
+ return true;
40
+ } catch (err) {
41
+ setError(err?.message || 'Error al solicitar el reseteo de contraseña');
42
+ return false;
43
+ } finally {
44
+ setRequestLoading(false);
45
+ }
46
+ };
47
+
48
+ const stopPolling = useCallback(() => {
49
+ if (pollingInterval.current) {
50
+ clearInterval(pollingInterval.current);
51
+ pollingInterval.current = null;
52
+ }
53
+ }, []);
54
+
55
+ // Paso 2: Polling
56
+ const checkStatus = useCallback(async (email) => {
57
+ try {
58
+ const res = await api.getResetStatus(email);
59
+ const data = res?.data ?? res;
60
+ if (data) {
61
+ setActiveTicket(data);
62
+ const currentStatus = data.status;
63
+ if (currentStatus === 'Accepted') {
64
+ setStatus('Accepted');
65
+ stopPolling();
66
+ } else if (currentStatus === 'None') {
67
+ setStatus('Idle');
68
+ stopPolling();
69
+ }
70
+ }
71
+ } catch (err) {
72
+ console.error("Error al consultar el estado del ticket:", err);
73
+ }
74
+ }, [stopPolling]);
75
+
76
+ const startPolling = useCallback((email) => {
77
+ stopPolling();
78
+ checkStatus(email);
79
+ // Iniciar polling cada 5 segundos
80
+ pollingInterval.current = setInterval(() => {
81
+ checkStatus(email);
82
+ }, 5000);
83
+ }, [stopPolling, checkStatus]);
84
+
85
+ // Paso 3: Asignar nueva clave
86
+ const assignNewPassword = async (email, newPassword) => {
87
+ setPwdLoading(true);
88
+ setError(null);
89
+ try {
90
+ await api.assignNewPassword(email, newPassword);
91
+ setSuccess(true);
92
+ setStatus('Completed');
93
+ return true;
94
+ } catch (err) {
95
+ setError(err?.message || 'Error al asignar la nueva contraseña');
96
+ return false;
97
+ } finally {
98
+ setPwdLoading(false);
99
+ }
100
+ };
101
+
102
+ // Limpiar polling al desmontar
103
+ useEffect(() => {
104
+ return () => stopPolling();
105
+ }, []);
106
+
107
+ const resetResetState = useCallback(() => {
108
+ setSuccess(false);
109
+ setStatus('Idle');
110
+ setError(null);
111
+ setActiveTicket(null);
112
+ }, []);
113
+
114
+ return {
115
+ requestPasswordReset,
116
+ assignNewPassword,
117
+ requestLoading,
118
+ pwdLoading,
119
+ error,
120
+ success,
121
+ status,
122
+ pendingList,
123
+ fetchLoading,
124
+ clearError,
125
+ setStatus,
126
+ stopPolling,
127
+ fetchPendingList,
128
+ startPolling,
129
+ activeTicket,
130
+ setActiveTicket,
131
+ resetResetState
132
+ };
133
+ }
package/index.js ADDED
@@ -0,0 +1,57 @@
1
+ // Barrel de la librería: permite `import { Button, Sidebar } from 'anteriority-ui'`
2
+ // en vez de conocer el subpath exacto de cada componente. Los subpaths
3
+ // (`anteriority-ui/screens/components/Button`) siguen funcionando igual para quien
4
+ // los prefiera o para acceder a exports nombrados extra (ej. Button.VARIANTS).
5
+ //
6
+ // Al agregar un componente nuevo en screens/components/, agregarlo también acá.
7
+
8
+ export { default as AlertModal } from './screens/components/AlertModal/AlertModal.js';
9
+ export { default as Badge } from './screens/components/Badge/index.js';
10
+ export { default as Barnner } from './screens/components/Barnner/index.js';
11
+ export { default as BlueLink } from './screens/components/BlueLink/index.js';
12
+ export { default as Breadcrumbs } from './screens/components/Breadcrumbs/index.js';
13
+ export { default as Button } from './screens/components/Button/index.js';
14
+ export { default as CalendarComponent } from './screens/components/CalendarComponent/CustomDatePicker.js';
15
+ export { default as CalendarPicker } from './screens/components/CalendarPicker/index.js';
16
+ export { default as CardV1 } from './screens/components/CardV1/index.js';
17
+ export { default as ChatWindow } from './screens/components/ChatWindow/index.js';
18
+ export { default as Checks } from './screens/components/Checks/index.js';
19
+ export { default as Container } from './screens/components/Container/index.js';
20
+ export { default as CopyText } from './screens/components/CopyText/index.js';
21
+ export { default as CountdownBar } from './screens/components/CountdownBar/index.js';
22
+ export { default as DateTag } from './screens/components/DateTag/index.js';
23
+ export { default as DesplegablePanel } from './screens/components/DesplegablePanel/index.js';
24
+ export { default as DrawPanel } from './screens/components/DrawPanel/index.js';
25
+ export { default as Filter } from './screens/components/Filter/index.js';
26
+ export { FormCard, FormInput, FormTextArea } from './screens/components/FormControls/index.js';
27
+ export { default as Frame } from './screens/components/Frame/index.js';
28
+ export { default as GreenHighlight } from './screens/components/GreenHighlight/index.js';
29
+ export { default as HoursPicker } from './screens/components/HoursPicker/index.js';
30
+ export { default as InfoTooltip } from './screens/components/InfoTooltip/index.js';
31
+ export { default as Input } from './screens/components/Input/index.js';
32
+ export { default as Loading } from './screens/components/Loading/index.js';
33
+ export { default as MarkdownContent } from './screens/components/MarkdownContent/index.js';
34
+ export { default as MarkdownEditor } from './screens/components/MarkdownEditor/index.js';
35
+ export { default as InputWithIcon } from './screens/components/Material-UI/Components/InputWithIcon/index.js';
36
+ export { default as MenuPopover } from './screens/components/Material-UI/Components/MenuPopover/index.js';
37
+ export { default as MultilinePlaceholder } from './screens/components/Material-UI/Components/MultilinePlaceholder/index.js';
38
+ export { default as Paginador } from './screens/components/Material-UI/Components/Paginador/index.js';
39
+ export { default as Search } from './screens/components/Material-UI/Components/Search/index.js';
40
+ export { default as Selector } from './screens/components/Material-UI/Components/Selector/index.js';
41
+ export { default as TextField } from './screens/components/Material-UI/Components/TextField/index.js';
42
+ export { default as ModalTooltip } from './screens/components/ModalTooltip/ModalTooltip.js';
43
+ export { default as ErrorMessage } from './screens/components/Notification/Error/ErrorMessage.js';
44
+ export { default as PasswordResetPanel } from './screens/components/PasswordResetPanel/index.js';
45
+ export { default as PlaceholderButton } from './screens/components/PlaceholderButton/index.js';
46
+ export { default as Profile } from './screens/components/Profile/index.js';
47
+ export { default as ProfileAvatar } from './screens/components/Profile/Avatar/index.js';
48
+ export { default as ScrollBar } from './screens/components/ScrollBar/index.js';
49
+ export { default as Sidebar } from './screens/components/Sidebar/index.js';
50
+ export { default as Skeleton } from './screens/components/Skeleton/index.js';
51
+ export { default as SubSidebarLayout } from './screens/components/SubSidebarLayout/index.js';
52
+ export { default as SummaryCard } from './screens/components/SummaryCard/index.js';
53
+ export { default as Swich } from './screens/components/Swich/index.js';
54
+ export { default as TableA } from './screens/components/TableA/index.js';
55
+ export { default as TableB } from './screens/components/TableB/index.js';
56
+ export { default as ToggleCard } from './screens/components/ToggleCard/index.js';
57
+ export { default as UserEditPopover } from './screens/components/UserEditPopover/index.js';
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "anteriority-ui",
3
+ "version": "1.0.0",
4
+ "description": "Librería interna de componentes React (UI) construida sobre MUI, lucide-react y CSS Modules.",
5
+ "packageManager": "pnpm@11.1.2",
6
+ "main": "index.js",
7
+ "scripts": {
8
+ "test": "echo \"Error: no test specified\" && exit 1",
9
+ "storybook": "storybook dev -p 6006",
10
+ "build-storybook": "storybook build",
11
+ "check-case": "node scripts/check-case.mjs",
12
+ "build": "node scripts/check-case.mjs && node scripts/build.mjs && node scripts/verify-dist.mjs"
13
+ },
14
+ "keywords": [],
15
+ "author": "",
16
+ "license": "ISC",
17
+ "type": "module",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/AndresPrograma-desing/AnteriorityUI.git"
21
+ },
22
+ "bugs": {
23
+ "url": "https://github.com/AndresPrograma-desing/AnteriorityUI/issues"
24
+ },
25
+ "homepage": "https://github.com/AndresPrograma-desing/AnteriorityUI#readme",
26
+ "publishConfig": {
27
+ "directory": "dist",
28
+ "linkDirectory": true,
29
+ "registry": "https://registry.npmjs.org/"
30
+ },
31
+ "devDependencies": {
32
+ "@emotion/react": "^11.14.0",
33
+ "@emotion/styled": "^11.14.1",
34
+ "@mui/material": "^9.4.0",
35
+ "@storybook/addon-a11y": "^10.6.0",
36
+ "@storybook/addon-docs": "^10.6.0",
37
+ "@storybook/react-vite": "^10.6.0",
38
+ "esbuild": "^0.28.2",
39
+ "eslint-plugin-storybook": "^10.6.0",
40
+ "react": "^19.3.0",
41
+ "react-dom": "^19.3.0",
42
+ "storybook": "^10.6.0"
43
+ },
44
+ "dependencies": {
45
+ "date-fns": "^4.4.0",
46
+ "lucide-react": "^1.26.0",
47
+ "react-markdown": "^10.1.0",
48
+ "remark-gfm": "^4.0.1"
49
+ },
50
+ "peerDependencies": {
51
+ "@emotion/react": "^11.0.0",
52
+ "@emotion/styled": "^11.0.0",
53
+ "@mui/material": "^5.0.0 || ^6.0.0 || ^9.0.0",
54
+ "react": "^18.0.0 || ^19.0.0",
55
+ "react-dom": "^18.0.0 || ^19.0.0"
56
+ }
57
+ }
@@ -0,0 +1,3 @@
1
+ packages: []
2
+ allowBuilds:
3
+ esbuild: true
@@ -0,0 +1,259 @@
1
+ import React from 'react';
2
+ import { createPortal } from 'react-dom';
3
+ import { TriangleAlert } from 'lucide-react';
4
+ import styles from './AlertModal.module.css';
5
+ import Button from '../Button/index';
6
+ import TextField from '../Material-UI/Components/TextField/index';
7
+ import Selector from '../Material-UI/Components/Selector/index';
8
+
9
+
10
+ const FIELD_LABELS = {
11
+ nombre: 'Nombre',
12
+ apellido: 'Apellido',
13
+ email: 'Correo electronico',
14
+ telefono: 'Telefono',
15
+ direccion: 'Direccion',
16
+ identificacion: 'Identificacion',
17
+ mascota_nombre: 'Nombre de la mascota',
18
+ especie: 'Especie',
19
+ raza: 'Raza',
20
+ color: 'Color',
21
+ sexo: 'Sexo',
22
+ fecha_nacimiento: 'Fecha de nacimiento',
23
+ peso: 'Peso',
24
+ identeficacion_mascota: 'Identificacion de mascota',
25
+ alergias: 'Alergias',
26
+ condiciones_medicas: 'Condiciones medicas',
27
+ esterilizado: 'Esterilizado',
28
+ id_cliente: 'Cliente',
29
+ cliente_id: 'Cliente',
30
+ };
31
+
32
+ function humanizeField(field) {
33
+ const key = String(field || '').trim();
34
+ if (!key) return '';
35
+ if (FIELD_LABELS[key]) return FIELD_LABELS[key];
36
+ const normalized = key
37
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
38
+ .replace(/_/g, ' ')
39
+ .toLowerCase();
40
+ return normalized.charAt(0).toUpperCase() + normalized.slice(1);
41
+ }
42
+
43
+ function translateText(input) {
44
+ const text = String(input || '').trim();
45
+ if (!text) return '';
46
+
47
+ return text
48
+ .replace(/\bvalidation error\b/gi, 'Error de validacion')
49
+ .replace(/\bfailed validation\b/gi, 'fallo de validacion')
50
+ .replace(/\bis required\b/gi, 'es obligatorio')
51
+ .replace(/\bmust not be empty\b/gi, 'no puede estar vacio')
52
+ .replace(/\bcannot be empty\b/gi, 'no puede estar vacio')
53
+ .replace(/\bmust be a valid email\b/gi, 'debe ser un correo electronico valido')
54
+ .replace(/\binvalid email\b/gi, 'correo electronico invalido')
55
+ .replace(/\binvalid\b/gi, 'invalido')
56
+ .replace(/\balready exists\b/gi, 'ya existe')
57
+ .replace(/\balready registered\b/gi, 'ya esta registrado')
58
+ .replace(/\btoo short\b/gi, 'es demasiado corto')
59
+ .replace(/\btoo long\b/gi, 'es demasiado largo')
60
+ .replace(/\bmust be\b/gi, 'debe ser')
61
+ .replace(/\bmust contain\b/gi, 'debe contener')
62
+ .replace(/\bnot found\b/gi, 'no fue encontrado')
63
+ .replace(/\bbad request\b/gi, 'solicitud invalida')
64
+ .replace(/\bserver error\b/gi, 'error del servidor');
65
+ }
66
+
67
+ function extractErrorLines(raw) {
68
+ if (raw == null || raw === false) return [];
69
+
70
+ if (typeof raw === 'string' || typeof raw === 'number') {
71
+ const line = translateText(raw);
72
+ return line ? [line] : [];
73
+ }
74
+
75
+ if (Array.isArray(raw)) {
76
+ return raw.flatMap((item) => extractErrorLines(item));
77
+ }
78
+
79
+ if (typeof raw !== 'object') return [];
80
+
81
+ const objectLines = [];
82
+ const listSources = [raw?.errors, raw?.details, raw?.body?.errors, raw?.body?.details].filter(Boolean);
83
+
84
+ listSources.forEach((list) => {
85
+ if (!Array.isArray(list)) return;
86
+ list.forEach((item) => {
87
+ if (typeof item === 'string') {
88
+ const translated = translateText(item);
89
+ if (translated) objectLines.push(translated);
90
+ return;
91
+ }
92
+
93
+ const field =
94
+ item?.field ??
95
+ item?.path ??
96
+ item?.param ??
97
+ item?.property ??
98
+ (Array.isArray(item?.instancePath) ? item.instancePath.join('.') : item?.instancePath) ??
99
+ (Array.isArray(item?.path) ? item.path.join('.') : item?.path) ??
100
+ '';
101
+ const fieldLabel = humanizeField(field);
102
+
103
+ const detailMsg =
104
+ item?.message ??
105
+ item?.msg ??
106
+ item?.error ??
107
+ item?.description ??
108
+ (item?.constraints && Object.values(item.constraints).join(', '));
109
+
110
+ const translatedDetail = translateText(detailMsg || 'Dato invalido');
111
+ objectLines.push(fieldLabel ? `${fieldLabel}: ${translatedDetail}` : translatedDetail);
112
+ });
113
+ });
114
+
115
+ if (objectLines.length) return objectLines;
116
+
117
+ const topLevel = [raw?.body?.message, raw?.message, raw?.error, raw?.body?.error].filter(Boolean);
118
+ if (topLevel.length) return topLevel.map((line) => translateText(line));
119
+
120
+ return [];
121
+ }
122
+
123
+ function formatModalMessage(message) {
124
+ const lines = extractErrorLines(message).filter(Boolean);
125
+ if (!lines.length) {
126
+ return {
127
+ text: typeof message === 'string' ? message : '',
128
+ list: [],
129
+ };
130
+ }
131
+
132
+ const deduped = Array.from(new Set(lines));
133
+ return {
134
+ text: deduped.length === 1 ? deduped[0] : '',
135
+ list: deduped.length > 1 ? deduped : [],
136
+ };
137
+ }
138
+
139
+ export default function AlertModal({
140
+ open,
141
+ title = 'Alerta',
142
+ message = '',
143
+ onClose,
144
+ onConfirm,
145
+ confirmText = 'Ok',
146
+ cancelText = 'Cancelar',
147
+ hideActions = false,
148
+ children,
149
+ showInput = false,
150
+ inputType = 'text',
151
+ inputLabel = '',
152
+ inputPlaceholder = '',
153
+ inputValue = '',
154
+ onInputChange,
155
+ inputOptions = [],
156
+ inputError = '',
157
+ imageUrl = '',
158
+ showWarningIcon = true,
159
+ bgColor,
160
+ titleBorderColor,
161
+ errorTextColor,
162
+ errorBgColor,
163
+ }) {
164
+ if (!open) return null;
165
+
166
+ const parsed = formatModalMessage(message);
167
+
168
+ const themeVars = {
169
+ ...(bgColor ? { '--alertmodal-bg': bgColor } : {}),
170
+ ...(titleBorderColor ? { '--alertmodal-title-border': titleBorderColor } : {}),
171
+ ...(errorTextColor ? { '--alertmodal-error-text': errorTextColor } : {}),
172
+ ...(errorBgColor ? { '--alertmodal-error-bg': errorBgColor, '--alertmodal-error-border': errorBgColor } : {}),
173
+ };
174
+
175
+ const content = (
176
+ <div data-test="alert-modal" className={styles.modalOverlay} onClick={onClose || (() => { })}>
177
+ <div className={styles.modalBox} style={themeVars} onClick={(e) => e.stopPropagation()}>
178
+ <h3 className={styles.modalTitle}>
179
+ {showWarningIcon && <TriangleAlert className={styles.warningIcon} />}
180
+ {title}</h3>
181
+ <div className={styles.modalBody}>
182
+ {imageUrl && (
183
+ <div style={{ display: 'flex', justifyContent: 'center', marginTop: '8px', marginBottom: '16px' }}>
184
+ <img
185
+ src={imageUrl}
186
+ alt="preview"
187
+ style={{ maxWidth: '100%', maxHeight: '400px', borderRadius: '6px', objectFit: 'contain' }}
188
+ />
189
+ </div>
190
+ )}
191
+ {parsed.text && <p className={styles.modalText}>{parsed.text}</p>}
192
+ {parsed.list.length > 0 && (
193
+ <ul className={styles.modalErrorList}>
194
+ {parsed.list.map((line, index) => (
195
+ <li key={`${line}-${index}`} className={styles.modalErrorItem}>{line}</li>
196
+ ))}
197
+ </ul>
198
+ )}
199
+
200
+ {/* Render native inputs if requested via props */}
201
+ {showInput && inputType === 'select' && (
202
+ <div style={{ marginTop: '16px', marginBottom: '8px' }}>
203
+ <Selector
204
+ label={inputLabel}
205
+ value={inputValue}
206
+ onChange={onInputChange}
207
+ options={inputOptions}
208
+ placeholder={inputPlaceholder}
209
+ />
210
+ </div>
211
+ )}
212
+
213
+ {showInput && inputType !== 'select' && (
214
+ <div style={{ marginTop: '16px', marginBottom: '8px' }}>
215
+ <TextField
216
+ type={inputType}
217
+ label={inputLabel}
218
+ placeholder={inputPlaceholder}
219
+ value={inputValue}
220
+ onChange={onInputChange}
221
+ error={!!inputError}
222
+ helperText={inputError}
223
+ fullWidth
224
+ />
225
+ </div>
226
+ )}
227
+
228
+ {/* Render passed children (e.g., a form) or nothing by default */}
229
+ {children}
230
+ </div>
231
+
232
+ {!hideActions && (
233
+ <div className={styles.modalActions} style={{ marginTop: 12 }}>
234
+ {typeof onClose === 'function' && (
235
+ <Button
236
+ fullWidth
237
+ borderColor="var(--border-gray-low)"
238
+ onClick={onClose}>{cancelText}
239
+ </Button>
240
+ )}
241
+
242
+ {typeof onConfirm === 'function' ? (
243
+ <Button
244
+ fullWidth
245
+ // borderColor="var(--border-gray-low)"
246
+ onClick={onConfirm}>{confirmText}</Button>
247
+ ) : (
248
+ <Button variant="primary"
249
+ fullWidth
250
+ onClick={onClose || (() => { })}>{confirmText}</Button>
251
+ )}
252
+ </div>
253
+ )}
254
+ </div>
255
+ </div>
256
+ );
257
+
258
+ return createPortal(content, document.body);
259
+ }
@@ -0,0 +1,185 @@
1
+
2
+ .modalOverlay {
3
+ position: fixed;
4
+ inset: 0;
5
+ background: var(--modal-overlay-bg);
6
+ backdrop-filter: blur(2px);
7
+ display: flex;
8
+ align-items: center;
9
+ justify-content: center;
10
+ z-index: 1299;
11
+ animation: fadeIn 0.2s ease-out;
12
+ }
13
+
14
+ .modalBox {
15
+ background: var(--alertmodal-bg, white);
16
+ backdrop-filter: var(--blur);
17
+ padding: 1.5rem;
18
+ border-radius: var(--border-radius-lg);
19
+ width: min(820px, calc(100% - 2rem));
20
+ box-shadow: var(--modal-shadow);
21
+ border: var(--modal-border);
22
+ animation: slideUp 0.25s cubic-bezier(0.2, 0.9, 0.4, 1.1);
23
+ display: flex;
24
+ flex-direction: column;
25
+ max-height: calc(100vh - 3rem);
26
+ }
27
+
28
+ .modalBody {
29
+ overflow: auto;
30
+ flex: 1 1 auto;
31
+ padding-right: 0.25rem;
32
+ }
33
+
34
+ .modalTitle {
35
+ margin: 0 0 0.5rem 0;
36
+ font-size: 1.25rem;
37
+ font-weight: 600;
38
+ line-height: 1.4;
39
+ color: var(--modal-title-color);
40
+ text-align: center;
41
+ background-color: transparent;
42
+ border: 1px solid var(--alertmodal-title-border, black);
43
+ padding: 5px;
44
+ border-radius: 20px;
45
+ }
46
+
47
+ .modalText {
48
+ margin: 0 0 1.25rem 0;
49
+ color: rgb(0, 0, 0);
50
+ font-size: 0.95rem;
51
+ line-height: 1.5;
52
+ text-align: center;
53
+ margin-top: 20px;
54
+ }
55
+
56
+ .modalErrorList {
57
+ margin: 0 0 1.25rem 0;
58
+ padding: 0.75rem 1rem;
59
+ border-radius: 0.6rem;
60
+ border: 1px solid var(--alertmodal-error-border, #fecaca);
61
+ background: var(--alertmodal-error-bg, #fff1f2);
62
+ }
63
+
64
+ .modalErrorItem {
65
+ color: var(--alertmodal-error-text, #9f1239);
66
+ font-size: 0.92rem;
67
+ line-height: 1.45;
68
+ margin-left: 1rem;
69
+ }
70
+ .warningIcon {
71
+ width: 20px;
72
+ height: 20px;
73
+ margin-right: 8px;
74
+ vertical-align: middle;
75
+ }
76
+
77
+ .modalActions {
78
+ display: flex;
79
+ justify-content: flex-end;
80
+ gap: 0.75rem;
81
+ margin-top: 0.5rem;
82
+ /* Evitar que los botones se oculten al hacer scroll */
83
+ flex-shrink: 0;
84
+ }
85
+
86
+ .modalPrimary {
87
+ background: var(--primary-color);
88
+ color: white;
89
+ border: none;
90
+ padding: 0.5rem 1rem;
91
+ border-radius: var(--border-radius-md);
92
+ font-size: 0.875rem;
93
+ font-weight: 500;
94
+ cursor: pointer;
95
+ transition: var(--transition);
96
+ }
97
+
98
+ .modalPrimary:hover {
99
+ background: var(--primary-hover);
100
+ transform: translateY(-1px);
101
+ }
102
+
103
+ .modalPrimary:active {
104
+ transform: translateY(0);
105
+ }
106
+
107
+ .modalSecondary {
108
+ background: var(--secondary-color);
109
+ color: black;
110
+ border: 1px solid #e2e8f0;
111
+ padding: 0.5rem 1rem;
112
+ border-radius: var(--border-radius-md);
113
+ font-size: 0.875rem;
114
+ font-weight: 500;
115
+ cursor: pointer;
116
+ transition: var(--transition);
117
+ text-shadow: none;
118
+ }
119
+
120
+ .modalSecondary:hover {
121
+ background: var(--secondary-hover);
122
+ border-color: #cbd5e1;
123
+ transform: translateY(-1px);
124
+ }
125
+
126
+ .modalSecondary:active {
127
+ transform: translateY(0);
128
+ }
129
+
130
+ /* Foco visible para accesibilidad */
131
+ .modalPrimary:focus-visible,
132
+ .modalSecondary:focus-visible {
133
+ outline: 2px solid var(--primary-color);
134
+ outline-offset: 2px;
135
+ }
136
+
137
+ /* Animaciones */
138
+ @keyframes fadeIn {
139
+ from {
140
+ opacity: 0;
141
+ }
142
+ to {
143
+ opacity: 1;
144
+ }
145
+ }
146
+
147
+ @keyframes slideUp {
148
+ from {
149
+ opacity: 0;
150
+ transform: translateY(20px) scale(0.96);
151
+ }
152
+ to {
153
+ opacity: 1;
154
+ transform: translateY(0) scale(1);
155
+ }
156
+ }
157
+
158
+ /* Responsive: ajustes en móvil */
159
+ @media (max-width: 640px) {
160
+ .modalBox {
161
+ padding: 1.25rem;
162
+ }
163
+
164
+ .modalTitle {
165
+ font-size: 1.125rem;
166
+ }
167
+
168
+ .modalActions {
169
+ gap: 0.5rem;
170
+ }
171
+
172
+ .modalPrimary,
173
+ .modalSecondary {
174
+ padding: 0.5rem 0.875rem;
175
+ font-size: 0.8125rem;
176
+ }
177
+ }
178
+
179
+ /* Preferencia reducción de movimiento */
180
+ @media (prefers-reduced-motion: reduce) {
181
+ .modalOverlay,
182
+ .modalBox {
183
+ animation: none;
184
+ }
185
+ }