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,162 @@
1
+ .configContainer {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: 1.5rem;
5
+ width: 100%;
6
+ max-width: 500px;
7
+ }
8
+
9
+ .disabledContainer {
10
+ opacity: 0.7;
11
+ }
12
+
13
+ .inputGroup {
14
+ display: flex;
15
+ align-items: center;
16
+ gap: 1.25rem;
17
+ }
18
+
19
+ .inputWrapper {
20
+ position: relative;
21
+ display: flex;
22
+ align-items: center;
23
+ background: #f8fafc;
24
+ border: 1.5px solid #e2e8f0;
25
+ border-radius: 10px;
26
+ padding: 0 1rem;
27
+ flex: 1;
28
+ height: 48px;
29
+ transition: all 0.2s ease;
30
+ }
31
+
32
+ .inputWrapper:focus-within {
33
+ border-color: var(--placeholderbutton-focus, #2563eb);
34
+ background: #fff;
35
+ box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.1);
36
+ }
37
+
38
+ .inputIcon {
39
+ color: #94a3b8;
40
+ margin-right: 12px;
41
+ }
42
+
43
+ .daysInput {
44
+ border: none;
45
+ background: transparent;
46
+ padding: 10px 0;
47
+ width: 100%;
48
+ font-size: 1rem;
49
+ font-weight: 600;
50
+ color: #1e293b;
51
+ outline: none;
52
+ }
53
+
54
+ .daysInput::-webkit-inner-spin-button,
55
+ .daysInput::-webkit-outer-spin-button {
56
+ -webkit-appearance: none;
57
+ margin: 0;
58
+ }
59
+
60
+ .unitText {
61
+ font-size: 0.7rem;
62
+ font-weight: 800;
63
+ color: #64748b;
64
+ text-transform: uppercase;
65
+ letter-spacing: 0.05em;
66
+ margin-left: 8px;
67
+ background: var(--placeholderbutton-unit-bg, #e2e8f0);
68
+ padding: 2px 6px;
69
+ border-radius: 4px;
70
+ }
71
+
72
+ .saveButton {
73
+ display: flex;
74
+ align-items: center;
75
+ justify-content: center;
76
+ gap: 10px;
77
+ background-color: #2563eb;
78
+ color: white;
79
+ border: none;
80
+ height: 48px;
81
+ padding: 0 1.5rem;
82
+ border-radius: 10px;
83
+ font-weight: 600;
84
+ font-size: 0.9rem;
85
+ cursor: pointer;
86
+ transition: all 0.2s ease;
87
+ }
88
+
89
+ .saveButton:hover:not(:disabled) {
90
+ background-color: #1d4ed8;
91
+ transform: translateY(-1px);
92
+ box-shadow: 0 4px 12px rgba(37, 99, 235, 0.2);
93
+ }
94
+
95
+ .saveButton:active {
96
+ transform: translateY(0);
97
+ }
98
+
99
+ .saveButton:disabled {
100
+ background-color: #94a3b8;
101
+ cursor: not-allowed;
102
+ }
103
+
104
+ .statusMessage {
105
+ margin: 0;
106
+ font-size: 0.85rem;
107
+ font-weight: 500;
108
+ display: flex;
109
+ align-items: center;
110
+ gap: 6px;
111
+ padding: 8px 12px;
112
+ border-radius: 6px;
113
+ }
114
+
115
+ .success {
116
+ color: #059669;
117
+ background: #ecfdf5;
118
+ }
119
+
120
+ .error {
121
+ color: #dc2626;
122
+ background: #fef2f2;
123
+ }
124
+
125
+ .hintText {
126
+ font-size: 0.8rem;
127
+ color: #64748b;
128
+ margin: 0;
129
+ line-height: 1.5;
130
+ border-left: 3px solid #e2e8f0;
131
+ padding-left: 12px;
132
+ }
133
+
134
+ .infoTextDisabled {
135
+ font-size: 0.8rem;
136
+ color: #dc2626;
137
+ margin: 0;
138
+ }
139
+
140
+ .spinner {
141
+ animation: spin 1s linear infinite;
142
+ }
143
+
144
+ @keyframes spin {
145
+ from { transform: rotate(0deg); }
146
+ to { transform: rotate(360deg); }
147
+ }
148
+
149
+ @media (max-width: 640px) {
150
+ .configContainer {
151
+ max-width: 100%;
152
+ }
153
+
154
+ .inputGroup {
155
+ flex-direction: column;
156
+ align-items: stretch;
157
+ }
158
+
159
+ .saveButton {
160
+ width: 100%;
161
+ }
162
+ }
@@ -0,0 +1,69 @@
1
+ import { Calendar } from 'lucide-react';
2
+ import PlaceholderButton from './index';
3
+
4
+ export default {
5
+ title: 'Components/PlaceholderButton',
6
+ component: PlaceholderButton,
7
+ };
8
+
9
+ const fakeSavePromise = (value) =>
10
+ new Promise((resolve) => {
11
+ setTimeout(() => {
12
+ console.log('saved', value);
13
+ resolve({ message: 'Guardado correctamente' });
14
+ }, 800);
15
+ });
16
+
17
+ export const Default = {
18
+ args: {
19
+ icon: Calendar,
20
+ placeholder: 'Días',
21
+ unitText: 'días',
22
+ buttonText: 'Guardar',
23
+ hintText: 'Define cuántos días de anticipación se requieren.',
24
+ initialValue: '7',
25
+ inputType: 'number',
26
+ min: '1',
27
+ onSavePromise: fakeSavePromise,
28
+ successMessage: 'Valor guardado con éxito',
29
+ errorMessage: 'Ocurrió un error al guardar',
30
+ validationErrorMsg: 'El valor debe ser mayor o igual a 1',
31
+ },
32
+ };
33
+
34
+ export const Disabled = {
35
+ args: {
36
+ ...Default.args,
37
+ disabled: true,
38
+ disabledHintText: 'Esta opción está deshabilitada por ahora.',
39
+ },
40
+ };
41
+
42
+ export const Multiline = {
43
+ args: {
44
+ placeholder: 'Escribe una nota...',
45
+ buttonText: 'Guardar nota',
46
+ hintText: 'Puedes usar Ctrl+Enter para agregar una nueva línea.',
47
+ initialValue: '',
48
+ multiline: true,
49
+ onSavePromise: fakeSavePromise,
50
+ successMessage: 'Nota guardada',
51
+ errorMessage: 'Error al guardar la nota',
52
+ validationErrorMsg: 'La nota no puede estar vacía',
53
+ clearOnSuccess: true,
54
+ },
55
+ };
56
+
57
+ export const TextInput = {
58
+ args: {
59
+ placeholder: 'Ingresa un texto',
60
+ buttonText: 'Guardar',
61
+ hintText: 'Texto libre de ejemplo.',
62
+ initialValue: '',
63
+ inputType: 'text',
64
+ onSavePromise: fakeSavePromise,
65
+ successMessage: 'Guardado',
66
+ errorMessage: 'Error',
67
+ validationErrorMsg: 'Campo requerido',
68
+ },
69
+ };
@@ -0,0 +1,42 @@
1
+ export const handleAction = async ({
2
+ actionPromise,
3
+ value,
4
+ setIsSaving,
5
+ setMessage,
6
+ clearCache,
7
+ successMessage,
8
+ errorMessage,
9
+ validationErrorMsg,
10
+ validate,
11
+ setValue,
12
+ clearOnSuccess
13
+ }) => {
14
+ if (validate && !validate(value)) {
15
+ setMessage({ text: validationErrorMsg, type: 'error' });
16
+ return;
17
+ }
18
+
19
+ setIsSaving(true);
20
+ setMessage({ text: '', type: '' });
21
+
22
+ try {
23
+ const response = await actionPromise(value);
24
+ if (clearCache) clearCache();
25
+ if (clearOnSuccess && setValue) setValue('');
26
+
27
+ const apiMsg = response?.message || (typeof response === 'string' ? response : null);
28
+ const finalSuccessMessage = apiMsg || successMessage;
29
+
30
+ setMessage({ text: finalSuccessMessage, type: 'success' });
31
+ setTimeout(() => setMessage({ text: '', type: '' }), 3000);
32
+ } catch (error) {
33
+ console.error(errorMessage, error);
34
+
35
+ const apiErrorMsg = error?.message || error?.body?.message || error?.body?.error || (typeof error === 'string' ? error : null);
36
+ const finalErrorMessage = apiErrorMsg || errorMessage;
37
+
38
+ setMessage({ text: finalErrorMessage, type: 'error' });
39
+ } finally {
40
+ setIsSaving(false);
41
+ }
42
+ };
@@ -0,0 +1,125 @@
1
+ import React, { useState } from 'react';
2
+ import Avatar from '@mui/material/Avatar';
3
+ import { Camera } from 'lucide-react';
4
+ import { getAvatarInitial, stringToColor } from './utils';
5
+ import styles from './index.module.css';
6
+ import ModalTooltip from '../../ModalTooltip/ModalTooltip';
7
+ import AlertModal from '../../AlertModal/AlertModal';
8
+ import Loading from '../../Loading/index';
9
+
10
+ export default function UserAvatar({
11
+ src,
12
+ name = 'Usuario',
13
+ size = 35,
14
+ editable = false,
15
+ onEditClick = () => {},
16
+ loading = false,
17
+ viewable = false,
18
+ isActive = null,
19
+ fallbackBgColor,
20
+ activeStatusColor = '#10b981',
21
+ inactiveStatusColor = '#ef4444'
22
+ }) {
23
+ const [isFullViewOpen, setIsFullViewOpen] = useState(false);
24
+
25
+ if (loading) {
26
+ return (
27
+ <div
28
+ style={{
29
+ width: size,
30
+ height: size,
31
+ display: 'flex',
32
+ alignItems: 'center',
33
+ justifyContent: 'center',
34
+ backgroundColor: '#f3f4f6',
35
+ borderRadius: '50%'
36
+ }}
37
+ >
38
+ <Loading size="small" />
39
+ </div>
40
+ );
41
+ }
42
+
43
+ const initial = getAvatarInitial(name);
44
+ const backgroundColor = stringToColor(name);
45
+
46
+ const avatarElement = (
47
+ <Avatar
48
+ alt={name}
49
+ src={src || undefined}
50
+ className={styles.avatarContainer}
51
+ sx={{
52
+ width: size,
53
+ height: size,
54
+ fontSize: `${size * 0.43}px`,
55
+ bgcolor: src ? 'transparent' : backgroundColor,
56
+ }}
57
+ >
58
+ <span className={styles.fallbackText}>
59
+ {initial}
60
+ </span>
61
+ </Avatar>
62
+ );
63
+
64
+ return (
65
+ <>
66
+ <div
67
+ className={`${styles.avatarWrapper}
68
+ ${editable ? styles.editable : ''}
69
+ ${viewable && src ? styles.viewable : ''}`}
70
+ onClick={(e) => {
71
+ if (editable) {
72
+ if (typeof onEditClick === 'function') {
73
+ onEditClick(e);
74
+ }
75
+ } else if (viewable && src) {
76
+ setIsFullViewOpen(true);
77
+ }
78
+ }}
79
+ style={{
80
+ width: size,
81
+ height: size,
82
+ position: 'relative',
83
+ display: 'inline-flex',
84
+ ...(fallbackBgColor ? { '--avatar-fallback-bg': fallbackBgColor } : {}),
85
+ }}
86
+ >
87
+ {editable ? (
88
+ <ModalTooltip text='Cambiar Foto de Perfil' position="top">
89
+ {avatarElement}
90
+ <div className={styles.overlay}>
91
+ <Camera size={size * 0.45} />
92
+ </div>
93
+ </ModalTooltip>
94
+ ) : viewable && src ? (
95
+ <ModalTooltip text='Toca para ver imagen'>
96
+ {avatarElement}
97
+ </ModalTooltip>
98
+ ) : (
99
+ avatarElement
100
+ )}
101
+
102
+ {isActive !== null && isActive !== undefined && (
103
+
104
+ <span
105
+ className={styles.statusDot}
106
+ style={{
107
+ backgroundColor: isActive ? activeStatusColor : inactiveStatusColor,
108
+ width: `${Math.max(8, size * 0.22)}px`,
109
+ height: `${Math.max(8, size * 0.22)}px`,
110
+ }}
111
+ />
112
+ )}
113
+ </div>
114
+
115
+ <AlertModal
116
+ open={isFullViewOpen}
117
+ title="Foto de Perfil"
118
+ imageUrl={src}
119
+ onClose={() => setIsFullViewOpen(false)}
120
+ showWarningIcon={false}
121
+ confirmText="Cerrar"
122
+ />
123
+ </>
124
+ );
125
+ }
@@ -0,0 +1,86 @@
1
+ .avatarWrapper {
2
+ position: relative;
3
+ display: inline-flex; /* Cambiado a inline-flex para contener perfectamente el avatar */
4
+ align-items: center;
5
+ justify-content: center;
6
+ border-radius: 50%;
7
+ background-color: var(--avatar-fallback-bg, #193360); /* Tu color azul oscuro */
8
+ margin: 0 auto;
9
+ }
10
+
11
+ .editable {
12
+ cursor: pointer;
13
+ }
14
+
15
+ .avatarContainer {
16
+ /* Eliminamos display, align-items y justify-conten t para que MUI maneje su estructura */
17
+ user-select: none;
18
+ box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.15);
19
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
20
+ }
21
+
22
+ .avatarContainer:hover {
23
+ transform: scale(1.04);
24
+ box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.2);
25
+ }
26
+
27
+ .fallbackText {
28
+ font-weight: 700;
29
+ color: #ffffff;
30
+ text-align: center;
31
+ line-height: 1; /* Forzamos alineación vertical de la inicial */
32
+ }
33
+
34
+ .overlay {
35
+ position: absolute;
36
+ top: 0;
37
+ left: 0;
38
+ width: 100%;
39
+ height: 100%;
40
+ border-radius: 50%;
41
+ background-color: rgba(0, 0, 0, 0.6);
42
+ display: flex;
43
+ align-items: center;
44
+ justify-content: center;
45
+ opacity: 0;
46
+ transition: opacity 0.2s ease;
47
+ color: #ffffff;
48
+ }
49
+
50
+ .avatarWrapper:hover .overlay {
51
+ opacity: 1;
52
+ }
53
+
54
+ .skeleton {
55
+ border-radius: 50%;
56
+ background: #f1f5f9;
57
+ animation: pulse 1.5s infinite;
58
+ box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.05);
59
+ }
60
+
61
+ @keyframes pulse {
62
+ 0% {
63
+ opacity: 0.6;
64
+ }
65
+ 50% {
66
+ opacity: 1;
67
+ }
68
+ 100% {
69
+ opacity: 0.6;
70
+ }
71
+ }
72
+
73
+ .viewable {
74
+ cursor: pointer;
75
+ }
76
+
77
+ .statusDot {
78
+ position: absolute;
79
+ bottom: 0;
80
+ right: 0;
81
+ border-radius: 50%;
82
+ border: 2px solid var(--color-card-bg, #ffffff);
83
+ box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.15);
84
+ pointer-events: none;
85
+ z-index: 2;
86
+ }
@@ -0,0 +1,48 @@
1
+ import React from 'react';
2
+ import UserAvatar from './index';
3
+
4
+ export default {
5
+ title: 'Components/Profile/Avatar',
6
+ component: UserAvatar,
7
+ };
8
+
9
+ export const Default = {
10
+ args: {
11
+ name: 'Ana Gomez',
12
+ size: 48,
13
+ },
14
+ };
15
+
16
+ export const WithImage = {
17
+ args: {
18
+ name: 'Luis Perez',
19
+ src: 'https://i.pravatar.cc/100?img=12',
20
+ size: 48,
21
+ viewable: true,
22
+ },
23
+ };
24
+
25
+ export const Editable = {
26
+ args: {
27
+ name: 'Marta Ruiz',
28
+ size: 64,
29
+ editable: true,
30
+ onEditClick: () => console.log('edit avatar clicked'),
31
+ },
32
+ };
33
+
34
+ export const WithStatusDot = {
35
+ args: {
36
+ name: 'Carlos Diaz',
37
+ size: 48,
38
+ isActive: true,
39
+ },
40
+ };
41
+
42
+ export const LoadingState = {
43
+ args: {
44
+ name: 'Ana Gomez',
45
+ size: 48,
46
+ loading: true,
47
+ },
48
+ };
@@ -0,0 +1,18 @@
1
+ export const getAvatarInitial = (name) => {
2
+ if (!name) return '?';
3
+ return name.trim().charAt(0).toUpperCase();
4
+ };
5
+
6
+ export const stringToColor = (str) => {
7
+ if (!str) return '#0f6e52';
8
+ let hash = 0;
9
+ for (let i = 0; i < str.length; i++) {
10
+ hash = str.charCodeAt(i) + ((hash << 5) - hash);
11
+ }
12
+ let color = '#';
13
+ for (let i = 0; i < 3; i++) {
14
+ const value = (hash >> (i * 8)) & 0xff;
15
+ color += `00${value.toString(16)}`.slice(-2);
16
+ }
17
+ return color;
18
+ };
@@ -0,0 +1,58 @@
1
+ import React from 'react';
2
+ import UserAvatar from './Avatar/index';
3
+ import { Pencil } from 'lucide-react';
4
+
5
+ export default function UserProfileHeader({ username, avatarUrl, onEditClick, textColor = '#ffffff' }) {
6
+ return (
7
+ <div style={{
8
+ display: 'inline-flex',
9
+ alignItems: 'center',
10
+ gap: '12px',
11
+ height: '100%',
12
+ verticalAlign: 'middle'
13
+ }}>
14
+ <div style={{ display: 'flex', alignItems: 'center' }}>
15
+ <UserAvatar
16
+ src={avatarUrl}
17
+ name={username}
18
+ size={35}
19
+ />
20
+ </div>
21
+
22
+ <span style={{
23
+ color: textColor,
24
+ fontWeight: 700,
25
+ fontSize: '15px',
26
+ lineHeight: '1',
27
+ display: 'flex',
28
+ alignItems: 'center',
29
+ userSelect: 'none'
30
+ }}>
31
+ {username}
32
+ </span>
33
+
34
+ <button
35
+ onClick={onEditClick}
36
+ style={{
37
+ background: 'none',
38
+ border: 'none',
39
+ cursor: 'pointer',
40
+ display: 'flex',
41
+ alignItems: 'center',
42
+ justifyContent: 'center',
43
+ padding: '0',
44
+ color: textColor,
45
+ opacity: 0.85,
46
+ transition: 'opacity 0.2s',
47
+ height: '16px',
48
+ width: '16px'
49
+ }}
50
+ onMouseEnter={(e) => e.currentTarget.style.opacity = '1'}
51
+ onMouseLeave={(e) => e.currentTarget.style.opacity = '0.85'}
52
+ aria-label="Editar nombre"
53
+ >
54
+ <Pencil size={15} style={{ display: 'block' }} />
55
+ </button>
56
+ </div>
57
+ );
58
+ }
File without changes
@@ -0,0 +1,38 @@
1
+ import React from 'react';
2
+ import UserProfileHeader from './index';
3
+
4
+ export default {
5
+ title: 'Components/Profile',
6
+ component: UserProfileHeader,
7
+ decorators: [
8
+ (Story) => (
9
+ <div style={{ background: 'var(--sidebar-bg, #0f172a)', padding: '16px', display: 'inline-block' }}>
10
+ <Story />
11
+ </div>
12
+ ),
13
+ ],
14
+ };
15
+
16
+ export const Default = {
17
+ args: {
18
+ username: 'Ana Gomez',
19
+ avatarUrl: '',
20
+ onEditClick: () => console.log('edit clicked'),
21
+ },
22
+ };
23
+
24
+ export const WithAvatarImage = {
25
+ args: {
26
+ username: 'Luis Perez',
27
+ avatarUrl: 'https://i.pravatar.cc/100?img=12',
28
+ onEditClick: () => console.log('edit clicked'),
29
+ },
30
+ };
31
+
32
+ export const LongUsername = {
33
+ args: {
34
+ username: 'Maria Fernanda Rodriguez Castillo',
35
+ avatarUrl: '',
36
+ onEditClick: () => console.log('edit clicked'),
37
+ },
38
+ };
File without changes