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.
- package/.github/workflows/ci.yml +31 -0
- package/.github/workflows/publish.yml +45 -0
- package/.npmrc.bak +1 -0
- package/.storybook/main.js +15 -0
- package/.storybook/preview.jsx +26 -0
- package/AnteriorityUI.md +2400 -0
- package/CLAUDE.md +91 -0
- package/README.md +182 -0
- package/assets/anteriorityUI/AnteriorityUI.logo.jpeg +0 -0
- package/assets/favicon/anteriorityUI.ico +0 -0
- package/common/api/client.js +43 -0
- package/common/storage/LocalStorage.js +13 -0
- package/common/styles/typography.css +5 -0
- package/common/styles/variables.css +192 -0
- package/common/theme/muiTheme.js +9 -0
- package/features/password-reset/api.js +39 -0
- package/features/user-profile/api.js +5 -0
- package/hooks/useAdminPasswordReset.js +133 -0
- package/index.js +57 -0
- package/package.json +57 -0
- package/pnpm-workspace.yaml +3 -0
- package/screens/components/AlertModal/AlertModal.jsx +259 -0
- package/screens/components/AlertModal/AlertModal.module.css +185 -0
- package/screens/components/AlertModal/AlertModal.stories.jsx +87 -0
- package/screens/components/Avatar/index.jsx +0 -0
- package/screens/components/Avatar/index.module.css +0 -0
- package/screens/components/Avatar/utils.js +0 -0
- package/screens/components/Badge/index.jsx +33 -0
- package/screens/components/Badge/index.module.css +18 -0
- package/screens/components/Badge/index.stories.jsx +40 -0
- package/screens/components/Barnner/index.jsx +21 -0
- package/screens/components/Barnner/index.module.css +33 -0
- package/screens/components/Barnner/index.stories.jsx +39 -0
- package/screens/components/Barnner/utils.js +0 -0
- package/screens/components/BlueLink/index.jsx +27 -0
- package/screens/components/BlueLink/index.module.css +11 -0
- package/screens/components/BlueLink/index.stories.jsx +29 -0
- package/screens/components/Breadcrumbs/index.jsx +62 -0
- package/screens/components/Breadcrumbs/index.module.css +158 -0
- package/screens/components/Breadcrumbs/index.stories.jsx +37 -0
- package/screens/components/Button/index.jsx +169 -0
- package/screens/components/Button/index.module.css +168 -0
- package/screens/components/Button/index.stories.jsx +71 -0
- package/screens/components/Button/utils.js +28 -0
- package/screens/components/CalendarComponent/CustomDatePicker.jsx +248 -0
- package/screens/components/CalendarComponent/CustomDatePicker.module.css +323 -0
- package/screens/components/CalendarComponent/CustomDatePicker.stories.jsx +45 -0
- package/screens/components/CalendarPicker/Constants.js +12 -0
- package/screens/components/CalendarPicker/index.jsx +263 -0
- package/screens/components/CalendarPicker/index.module.css +147 -0
- package/screens/components/CalendarPicker/index.stories.jsx +45 -0
- package/screens/components/CalendarPicker/utils.js +42 -0
- package/screens/components/CardV1/index.jsx +165 -0
- package/screens/components/CardV1/index.module.css +232 -0
- package/screens/components/CardV1/index.stories.jsx +62 -0
- package/screens/components/CardV1/utils.js +49 -0
- package/screens/components/ChatWindow/index.jsx +251 -0
- package/screens/components/ChatWindow/index.module.css +311 -0
- package/screens/components/ChatWindow/index.stories.jsx +62 -0
- package/screens/components/Checks/index.jsx +31 -0
- package/screens/components/Checks/index.module.css +32 -0
- package/screens/components/Checks/index.stories.jsx +32 -0
- package/screens/components/Container/index.jsx +36 -0
- package/screens/components/Container/index.module.css +24 -0
- package/screens/components/Container/index.stories.jsx +32 -0
- package/screens/components/CopyText/index.jsx +42 -0
- package/screens/components/CopyText/index.module.css +52 -0
- package/screens/components/CopyText/index.stories.jsx +24 -0
- package/screens/components/CountdownBar/constants.js +3 -0
- package/screens/components/CountdownBar/index.jsx +81 -0
- package/screens/components/CountdownBar/index.module.css +45 -0
- package/screens/components/CountdownBar/index.stories.jsx +43 -0
- package/screens/components/CountdownBar/utils.js +119 -0
- package/screens/components/DateTag/index.jsx +33 -0
- package/screens/components/DateTag/index.module.css +12 -0
- package/screens/components/DateTag/index.stories.jsx +29 -0
- package/screens/components/DesplegablePanel/constants.js +0 -0
- package/screens/components/DesplegablePanel/index.jsx +61 -0
- package/screens/components/DesplegablePanel/index.module.css +120 -0
- package/screens/components/DesplegablePanel/index.stories.jsx +44 -0
- package/screens/components/DesplegablePanel/utils.js +0 -0
- package/screens/components/DrawPanel/index.jsx +217 -0
- package/screens/components/DrawPanel/index.module.css +252 -0
- package/screens/components/DrawPanel/index.stories.jsx +66 -0
- package/screens/components/DrawPanel/utils.js +0 -0
- package/screens/components/Filter/index.jsx +105 -0
- package/screens/components/Filter/index.module.css +91 -0
- package/screens/components/Filter/index.stories.jsx +46 -0
- package/screens/components/Filter/utils.js +0 -0
- package/screens/components/FormControls/FormControls.module.css +86 -0
- package/screens/components/FormControls/index.jsx +51 -0
- package/screens/components/FormControls/index.stories.jsx +36 -0
- package/screens/components/Frame/index.jsx +54 -0
- package/screens/components/Frame/index.module.css +102 -0
- package/screens/components/Frame/index.stories.jsx +37 -0
- package/screens/components/Frame/utils.js +0 -0
- package/screens/components/GreenHighlight/index.jsx +11 -0
- package/screens/components/GreenHighlight/index.module.css +7 -0
- package/screens/components/GreenHighlight/index.stories.jsx +26 -0
- package/screens/components/GreenHighlight/utils.jsx +45 -0
- package/screens/components/HoursPicker/Constants.js +26 -0
- package/screens/components/HoursPicker/index.jsx +350 -0
- package/screens/components/HoursPicker/index.module.css +99 -0
- package/screens/components/HoursPicker/index.stories.jsx +44 -0
- package/screens/components/HoursPicker/utils.js +48 -0
- package/screens/components/InfoTooltip/index.jsx +59 -0
- package/screens/components/InfoTooltip/index.module.css +98 -0
- package/screens/components/InfoTooltip/index.stories.jsx +35 -0
- package/screens/components/Input/index.jsx +36 -0
- package/screens/components/Input/index.module.css +49 -0
- package/screens/components/Input/index.stories.jsx +37 -0
- package/screens/components/Input/utils.js +0 -0
- package/screens/components/Loading/index.jsx +76 -0
- package/screens/components/Loading/index.module.css +193 -0
- package/screens/components/Loading/index.stories.jsx +38 -0
- package/screens/components/Loading/utils.js +0 -0
- package/screens/components/MarkdownContent/index.jsx +16 -0
- package/screens/components/MarkdownContent/index.module.css +149 -0
- package/screens/components/MarkdownContent/index.stories.jsx +30 -0
- package/screens/components/MarkdownEditor/index.jsx +430 -0
- package/screens/components/MarkdownEditor/index.module.css +192 -0
- package/screens/components/MarkdownEditor/index.stories.jsx +50 -0
- package/screens/components/Material-UI/Components/InputWithIcon/index.jsx +87 -0
- package/screens/components/Material-UI/Components/InputWithIcon/index.stories.jsx +49 -0
- package/screens/components/Material-UI/Components/MenuPopover/index.jsx +117 -0
- package/screens/components/Material-UI/Components/MenuPopover/index.stories.jsx +36 -0
- package/screens/components/Material-UI/Components/MultilinePlaceholder/index.jsx +44 -0
- package/screens/components/Material-UI/Components/MultilinePlaceholder/index.stories.jsx +45 -0
- package/screens/components/Material-UI/Components/Paginador/index.jsx +79 -0
- package/screens/components/Material-UI/Components/Paginador/index.stories.jsx +49 -0
- package/screens/components/Material-UI/Components/Search/index.jsx +81 -0
- package/screens/components/Material-UI/Components/Search/index.stories.jsx +68 -0
- package/screens/components/Material-UI/Components/Selector/index.jsx +100 -0
- package/screens/components/Material-UI/Components/Selector/index.module.css +0 -0
- package/screens/components/Material-UI/Components/Selector/index.stories.jsx +52 -0
- package/screens/components/Material-UI/Components/Selector/utils.js +0 -0
- package/screens/components/Material-UI/Components/TextField/index.jsx +61 -0
- package/screens/components/Material-UI/Components/TextField/index.stories.jsx +53 -0
- package/screens/components/ModalTooltip/ModalTooltip.jsx +122 -0
- package/screens/components/ModalTooltip/ModalTooltip.module.css +65 -0
- package/screens/components/ModalTooltip/ModalTooltip.stories.jsx +39 -0
- package/screens/components/Notification/Error/ErrorMessage.jsx +30 -0
- package/screens/components/Notification/Error/ErrorMessage.module.css +57 -0
- package/screens/components/Notification/Error/ErrorMessage.stories.jsx +37 -0
- package/screens/components/PasswordResetPanel/index.jsx +325 -0
- package/screens/components/PasswordResetPanel/index.stories.jsx +26 -0
- package/screens/components/PlaceholderButton/index.jsx +169 -0
- package/screens/components/PlaceholderButton/index.module.css +162 -0
- package/screens/components/PlaceholderButton/index.stories.jsx +69 -0
- package/screens/components/PlaceholderButton/utils.js +42 -0
- package/screens/components/Profile/Avatar/index.jsx +125 -0
- package/screens/components/Profile/Avatar/index.module.css +86 -0
- package/screens/components/Profile/Avatar/index.stories.jsx +48 -0
- package/screens/components/Profile/Avatar/utils.js +18 -0
- package/screens/components/Profile/index.jsx +58 -0
- package/screens/components/Profile/index.module.css +0 -0
- package/screens/components/Profile/index.stories.jsx +38 -0
- package/screens/components/Profile/utils.js +0 -0
- package/screens/components/ScrollBar/index.jsx +62 -0
- package/screens/components/ScrollBar/index.module.css +78 -0
- package/screens/components/ScrollBar/index.stories.jsx +64 -0
- package/screens/components/ScrollBar/utils.js +127 -0
- package/screens/components/Sidebar/constants.js +16 -0
- package/screens/components/Sidebar/index.jsx +399 -0
- package/screens/components/Sidebar/index.module.css +405 -0
- package/screens/components/Sidebar/index.stories.jsx +86 -0
- package/screens/components/Sidebar/utils.js +75 -0
- package/screens/components/Skeleton/index.jsx +11 -0
- package/screens/components/Skeleton/index.module.css +26 -0
- package/screens/components/Skeleton/index.stories.jsx +42 -0
- package/screens/components/SubSidebarLayout/index.jsx +44 -0
- package/screens/components/SubSidebarLayout/index.module.css +80 -0
- package/screens/components/SubSidebarLayout/index.stories.jsx +51 -0
- package/screens/components/SummaryCard/index.jsx +50 -0
- package/screens/components/SummaryCard/index.module.css +30 -0
- package/screens/components/SummaryCard/index.stories.jsx +29 -0
- package/screens/components/Swich/constants.js +4 -0
- package/screens/components/Swich/index.jsx +85 -0
- package/screens/components/Swich/index.module.css +68 -0
- package/screens/components/Swich/index.stories.jsx +44 -0
- package/screens/components/Swich/utils.js +0 -0
- package/screens/components/TableA/index.jsx +156 -0
- package/screens/components/TableA/index.module.css +105 -0
- package/screens/components/TableA/index.stories.jsx +116 -0
- package/screens/components/TableA/utils.js +11 -0
- package/screens/components/TableB/index.jsx +222 -0
- package/screens/components/TableB/index.module.css +295 -0
- package/screens/components/TableB/index.stories.jsx +110 -0
- package/screens/components/TableB/utils.js +11 -0
- package/screens/components/ToggleCard/index.jsx +64 -0
- package/screens/components/ToggleCard/index.module.css +111 -0
- package/screens/components/ToggleCard/index.stories.jsx +49 -0
- package/screens/components/UserEditPopover/index.jsx +102 -0
- package/screens/components/UserEditPopover/index.module.css +97 -0
- package/screens/components/UserEditPopover/index.stories.jsx +49 -0
- package/scripts/build.mjs +129 -0
- package/scripts/check-case.mjs +127 -0
- package/scripts/verify-dist.mjs +45 -0
- package/vitest.config.js +36 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
.itemContainer {
|
|
2
|
+
display: flex !important;
|
|
3
|
+
align-items: center !important;
|
|
4
|
+
justify-content: flex-start !important;
|
|
5
|
+
width: 100%;
|
|
6
|
+
padding: 8px 12px;
|
|
7
|
+
border-radius: 8px;
|
|
8
|
+
cursor: pointer;
|
|
9
|
+
transition: background-color 0.2s ease;
|
|
10
|
+
text-align: left !important;
|
|
11
|
+
user-select: none;
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.itemContainer:hover {
|
|
16
|
+
background-color: rgba(0, 0, 0, 0.04);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.checked {
|
|
20
|
+
background-color: var(--checks-checked-bg, rgba(25, 118, 210, 0.08));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.label {
|
|
24
|
+
font-size: 0.95rem;
|
|
25
|
+
color: #e5e7eb;
|
|
26
|
+
margin-left: 8px;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.checked .label {
|
|
30
|
+
color: #94a3b8;
|
|
31
|
+
text-decoration: line-through;
|
|
32
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PermissionCheckboxItem from './index';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: 'Components/Checks',
|
|
6
|
+
component: PermissionCheckboxItem,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const Unchecked = {
|
|
10
|
+
args: {
|
|
11
|
+
label: 'Ver historial clínico',
|
|
12
|
+
checked: false,
|
|
13
|
+
onChange: () => console.log('toggled'),
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const Checked = {
|
|
18
|
+
args: {
|
|
19
|
+
label: 'Editar información de pacientes',
|
|
20
|
+
checked: true,
|
|
21
|
+
onChange: () => console.log('toggled'),
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const Disabled = {
|
|
26
|
+
args: {
|
|
27
|
+
label: 'Eliminar registros (bloqueado)',
|
|
28
|
+
checked: false,
|
|
29
|
+
disabled: true,
|
|
30
|
+
onChange: () => console.log('toggled'),
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styles from './index.module.css';
|
|
3
|
+
import Paginador from '../Material-UI/Components/Paginador';
|
|
4
|
+
|
|
5
|
+
export default function Container({
|
|
6
|
+
children,
|
|
7
|
+
className = '',
|
|
8
|
+
showPagination = false,
|
|
9
|
+
paginationCount = 10,
|
|
10
|
+
paginationPage = 1,
|
|
11
|
+
onPaginationChange,
|
|
12
|
+
paginationProps = {},
|
|
13
|
+
pageSize = 10,
|
|
14
|
+
pageSizeOptions = [5, 10, 20, 50],
|
|
15
|
+
onPageSizeChange,
|
|
16
|
+
...props
|
|
17
|
+
}) {
|
|
18
|
+
return (
|
|
19
|
+
<div className={`${styles.containerCard} ${className}`} {...props}>
|
|
20
|
+
{children}
|
|
21
|
+
{showPagination && (
|
|
22
|
+
<div className={styles.paginationContainer}>
|
|
23
|
+
<Paginador
|
|
24
|
+
count={paginationCount}
|
|
25
|
+
page={paginationPage}
|
|
26
|
+
onChange={onPaginationChange}
|
|
27
|
+
pageSize={pageSize}
|
|
28
|
+
pageSizeOptions={pageSizeOptions}
|
|
29
|
+
onPageSizeChange={onPageSizeChange}
|
|
30
|
+
{...paginationProps}
|
|
31
|
+
/>
|
|
32
|
+
</div>
|
|
33
|
+
)}
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
.containerCard {
|
|
2
|
+
position: relative;
|
|
3
|
+
width: 100%;
|
|
4
|
+
max-width: 1300px;
|
|
5
|
+
background: var(--letter-white, #ffffff);
|
|
6
|
+
backdrop-filter: var(--blur, blur(10px));
|
|
7
|
+
-webkit-backdrop-filter: var(--blur, blur(10px));
|
|
8
|
+
padding: 35px;
|
|
9
|
+
border-radius: 12px;
|
|
10
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
|
|
11
|
+
border: 1px solid var(--logo-yellow-border, #e2e8f0);
|
|
12
|
+
min-height: 550px; /* Tamaño estático/mínimo por defecto para evitar saltos al cargar */
|
|
13
|
+
box-sizing: border-box;
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@media (max-width: 768px) {
|
|
19
|
+
.containerCard {
|
|
20
|
+
max-width: 100%;
|
|
21
|
+
padding: 1.5rem;
|
|
22
|
+
min-height: auto; /* Permite que sea fluido en móviles */
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Container from './index';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: 'Components/Container',
|
|
6
|
+
component: Container,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const Default = {
|
|
10
|
+
args: {
|
|
11
|
+
children: <p>Contenido de ejemplo dentro del contenedor.</p>,
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const WithPagination = {
|
|
16
|
+
args: {
|
|
17
|
+
children: <p>Lista de resultados paginados.</p>,
|
|
18
|
+
showPagination: true,
|
|
19
|
+
paginationCount: 50,
|
|
20
|
+
paginationPage: 1,
|
|
21
|
+
pageSize: 10,
|
|
22
|
+
onPaginationChange: (page) => console.log('page changed', page),
|
|
23
|
+
onPageSizeChange: (size) => console.log('page size changed', size),
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const CustomClassName = {
|
|
28
|
+
args: {
|
|
29
|
+
children: <p>Contenedor con clase personalizada.</p>,
|
|
30
|
+
className: 'custom-container',
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Copy, Check } from 'lucide-react';
|
|
3
|
+
import ModalTooltip from '../ModalTooltip/ModalTooltip';
|
|
4
|
+
import styles from './index.module.css';
|
|
5
|
+
|
|
6
|
+
export default function CopyableText({ text, bgColor, borderColor, textColor }) {
|
|
7
|
+
|
|
8
|
+
const [copied, setCopied] = useState(false);
|
|
9
|
+
|
|
10
|
+
const handleCopy = async (e) => {
|
|
11
|
+
e.preventDefault();
|
|
12
|
+
e.stopPropagation();
|
|
13
|
+
try {
|
|
14
|
+
await navigator.clipboard.writeText(text);
|
|
15
|
+
setCopied(true);
|
|
16
|
+
setTimeout(() => setCopied(false), 2000);
|
|
17
|
+
} catch (err) {
|
|
18
|
+
console.error('Error al copiar el texto: ', err);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<ModalTooltip text={copied ? "¡Copiado!" : "Haga clic para copiar"}>
|
|
24
|
+
<span
|
|
25
|
+
className={styles.copyableContainer}
|
|
26
|
+
onClick={handleCopy}
|
|
27
|
+
style={{
|
|
28
|
+
...(bgColor ? { '--copytext-bg': bgColor } : {}),
|
|
29
|
+
...(borderColor ? { '--copytext-border': borderColor } : {}),
|
|
30
|
+
...(textColor ? { '--copytext-text': textColor } : {}),
|
|
31
|
+
}}
|
|
32
|
+
>
|
|
33
|
+
<span className={styles.copyableText}>{text}</span>
|
|
34
|
+
{copied ? (
|
|
35
|
+
<Check size={14} className={styles.copyIconSuccess} />
|
|
36
|
+
) : (
|
|
37
|
+
<Copy size={14} className={styles.copyIcon} />
|
|
38
|
+
)}
|
|
39
|
+
</span>
|
|
40
|
+
</ModalTooltip>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
.copyableContainer {
|
|
2
|
+
display: inline-flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
gap: 6px;
|
|
5
|
+
background-color: var(--copytext-bg, rgba(226, 232, 240, 0.6)); /* Fondo gris suave e interactivo */
|
|
6
|
+
border: 1px solid var(--copytext-border, #cbd5e1);
|
|
7
|
+
padding: 2px 8px;
|
|
8
|
+
border-radius: 6px;
|
|
9
|
+
cursor: pointer;
|
|
10
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; /* Tipografía limpia para datos copiables */
|
|
11
|
+
font-weight: 600;
|
|
12
|
+
color: var(--copytext-text, #334155);
|
|
13
|
+
transition: all 0.2s ease;
|
|
14
|
+
user-select: all;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.copyableContainer:hover {
|
|
18
|
+
background-color: #e2e8f0;
|
|
19
|
+
border-color: #94a3b8;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.copyableText {
|
|
23
|
+
font-size: 0.9em;
|
|
24
|
+
line-height: 1.2;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.copyIcon {
|
|
28
|
+
color: #64748b;
|
|
29
|
+
flex-shrink: 0;
|
|
30
|
+
transition: color 0.2s ease;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.copyableContainer:hover .copyIcon {
|
|
34
|
+
color: #334155;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.copyIconSuccess {
|
|
38
|
+
color: #10b981; /* Verde esmeralda para el feedback visual */
|
|
39
|
+
flex-shrink: 0;
|
|
40
|
+
animation: popIn 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@keyframes popIn {
|
|
44
|
+
0% {
|
|
45
|
+
transform: scale(0.6);
|
|
46
|
+
opacity: 0;
|
|
47
|
+
}
|
|
48
|
+
100% {
|
|
49
|
+
transform: scale(1);
|
|
50
|
+
opacity: 1;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import CopyableText from './index.jsx';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'Components/CopyText',
|
|
5
|
+
component: CopyableText,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const Default = {
|
|
9
|
+
args: {
|
|
10
|
+
text: 'ABC-12345-XYZ',
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const Email = {
|
|
15
|
+
args: {
|
|
16
|
+
text: 'contacto@empresa.com',
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const LongCode = {
|
|
21
|
+
args: {
|
|
22
|
+
text: 'f47ac10b-58cc-4372-a567-0e02b2c3d479',
|
|
23
|
+
},
|
|
24
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styles from './index.module.css';
|
|
3
|
+
import { useCountdown, getColorForProgress } from './utils';
|
|
4
|
+
import ModalTooltip from '../ModalTooltip/ModalTooltip';
|
|
5
|
+
|
|
6
|
+
export default function CountdownBar({
|
|
7
|
+
active,
|
|
8
|
+
duration = 10000,
|
|
9
|
+
onComplete,
|
|
10
|
+
height = '6px',
|
|
11
|
+
width = '100%',
|
|
12
|
+
colors = ['#ef4444', '#f59e0b', '#10b981', '#3b82f6'], // Del rojo (acabándose) al azul (inicio)
|
|
13
|
+
expiresAt,
|
|
14
|
+
totalDuration = 900000,
|
|
15
|
+
value,
|
|
16
|
+
increment = false,
|
|
17
|
+
isOverlay = false,
|
|
18
|
+
overlayMessage
|
|
19
|
+
}) {
|
|
20
|
+
const timerState = useCountdown(active && value === undefined, duration, onComplete, expiresAt, totalDuration);
|
|
21
|
+
|
|
22
|
+
let progress = value !== undefined ? value : timerState.progress;
|
|
23
|
+
if (increment && value === undefined) {
|
|
24
|
+
progress = 100 - progress;
|
|
25
|
+
}
|
|
26
|
+
const remainingSeconds = timerState.remainingSeconds;
|
|
27
|
+
const remainingTime = remainingSeconds ? remainingSeconds.toFixed(1) : '0.0';
|
|
28
|
+
|
|
29
|
+
if (!active) return null;
|
|
30
|
+
|
|
31
|
+
const currentColor = getColorForProgress(progress, colors);
|
|
32
|
+
|
|
33
|
+
const tooltipText = value !== undefined
|
|
34
|
+
? `${Math.round(progress)}% completado`
|
|
35
|
+
: `${remainingTime}s restantes`;
|
|
36
|
+
|
|
37
|
+
const barContent = (
|
|
38
|
+
<div className={styles.wrapper} style={{ width }}>
|
|
39
|
+
<div
|
|
40
|
+
className={styles.container}
|
|
41
|
+
style={{ height }}
|
|
42
|
+
aria-valuemin="0"
|
|
43
|
+
aria-valuemax="100"
|
|
44
|
+
aria-valuenow={progress}
|
|
45
|
+
role="progressbar"
|
|
46
|
+
>
|
|
47
|
+
<div
|
|
48
|
+
className={styles.bar}
|
|
49
|
+
style={{
|
|
50
|
+
width: `${progress}%`,
|
|
51
|
+
// Crea un sutil gradiente interno que se mezcla de forma suave
|
|
52
|
+
backgroundImage: `linear-gradient(90deg, ${currentColor}, ${currentColor}dd)`
|
|
53
|
+
}}
|
|
54
|
+
/>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
if (isOverlay) {
|
|
60
|
+
return (
|
|
61
|
+
<div className={styles.overlayContainer}>
|
|
62
|
+
{overlayMessage && (
|
|
63
|
+
<p className={styles.overlayMessage}>
|
|
64
|
+
{overlayMessage}
|
|
65
|
+
</p>
|
|
66
|
+
)}
|
|
67
|
+
<div style={{ width: '80%' }}>
|
|
68
|
+
<ModalTooltip text={tooltipText} style={{ width: '100%', display: 'block' }}>
|
|
69
|
+
{barContent}
|
|
70
|
+
</ModalTooltip>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<ModalTooltip text={tooltipText} style={{ width, display: 'block' }}>
|
|
78
|
+
{barContent}
|
|
79
|
+
</ModalTooltip>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
.wrapper {
|
|
2
|
+
margin-top: 8px;
|
|
3
|
+
cursor: pointer;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.container {
|
|
7
|
+
width: 100%;
|
|
8
|
+
background-color: #f1f5f9;
|
|
9
|
+
border-radius: 3px;
|
|
10
|
+
overflow: hidden;
|
|
11
|
+
border: 1px solid #e2e8f0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.bar {
|
|
15
|
+
height: 100%;
|
|
16
|
+
border-radius: 3px;
|
|
17
|
+
transition: width 0.05s linear, background-color 0.3s ease;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.overlayContainer {
|
|
21
|
+
position: absolute;
|
|
22
|
+
top: 0;
|
|
23
|
+
left: 0;
|
|
24
|
+
right: 0;
|
|
25
|
+
bottom: 0;
|
|
26
|
+
background-color: rgba(255, 255, 255, 0.4);
|
|
27
|
+
backdrop-filter: blur(5px);
|
|
28
|
+
display: flex;
|
|
29
|
+
flex-direction: column;
|
|
30
|
+
justify-content: center;
|
|
31
|
+
align-items: center;
|
|
32
|
+
z-index: 100;
|
|
33
|
+
border-radius: 4px;
|
|
34
|
+
padding: 0.75rem;
|
|
35
|
+
box-sizing: border-box;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.overlayMessage {
|
|
39
|
+
color: #ff0000;
|
|
40
|
+
font-size: 0.95rem;
|
|
41
|
+
font-weight: 700;
|
|
42
|
+
text-align: center;
|
|
43
|
+
margin: 0 0 1rem 0;
|
|
44
|
+
line-height: 1.4;
|
|
45
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import CountdownBar from './index.jsx';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'Components/CountdownBar',
|
|
5
|
+
component: CountdownBar,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const Default = {
|
|
9
|
+
args: {
|
|
10
|
+
active: true,
|
|
11
|
+
duration: 10000,
|
|
12
|
+
onComplete: () => console.log('countdown complete'),
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const CustomColors = {
|
|
17
|
+
args: {
|
|
18
|
+
active: true,
|
|
19
|
+
duration: 15000,
|
|
20
|
+
height: '10px',
|
|
21
|
+
colors: ['#ef4444', '#f97316', '#22c55e'],
|
|
22
|
+
onComplete: () => console.log('countdown complete'),
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const StaticValue = {
|
|
27
|
+
args: {
|
|
28
|
+
active: true,
|
|
29
|
+
value: 65,
|
|
30
|
+
increment: true,
|
|
31
|
+
height: '8px',
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const Overlay = {
|
|
36
|
+
args: {
|
|
37
|
+
active: true,
|
|
38
|
+
duration: 8000,
|
|
39
|
+
isOverlay: true,
|
|
40
|
+
overlayMessage: 'Guardando cambios, no cierres esta ventana...',
|
|
41
|
+
onComplete: () => console.log('countdown complete'),
|
|
42
|
+
},
|
|
43
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { useEffect, useState, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
export function useCountdown(active, duration, onComplete, expiresAt, totalDuration = 900000) {
|
|
4
|
+
const [state, setState] = useState({ progress: 100, remainingSeconds: duration / 1000 });
|
|
5
|
+
|
|
6
|
+
// Refs to store stable values
|
|
7
|
+
const expiresAtRef = useRef(expiresAt);
|
|
8
|
+
const totalDurationRef = useRef(totalDuration);
|
|
9
|
+
const durationRef = useRef(duration);
|
|
10
|
+
const onCompleteRef = useRef(onComplete);
|
|
11
|
+
|
|
12
|
+
// Keep refs updated with latest prop values
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (!active) {
|
|
15
|
+
expiresAtRef.current = null;
|
|
16
|
+
totalDurationRef.current = null;
|
|
17
|
+
} else {
|
|
18
|
+
// Only capture the initial value when it becomes active
|
|
19
|
+
if (expiresAt && !expiresAtRef.current) {
|
|
20
|
+
expiresAtRef.current = expiresAt;
|
|
21
|
+
}
|
|
22
|
+
if (totalDuration && !totalDurationRef.current) {
|
|
23
|
+
totalDurationRef.current = totalDuration;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}, [active, expiresAt, totalDuration]);
|
|
27
|
+
|
|
28
|
+
// Keep other refs updated
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
durationRef.current = duration;
|
|
31
|
+
onCompleteRef.current = onComplete;
|
|
32
|
+
}, [duration, onComplete]);
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (!active) {
|
|
36
|
+
setState({ progress: 100, remainingSeconds: durationRef.current / 1000 });
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const startTime = Date.now();
|
|
41
|
+
const interval = 50;
|
|
42
|
+
|
|
43
|
+
const timer = setInterval(() => {
|
|
44
|
+
let remaining;
|
|
45
|
+
let percentage;
|
|
46
|
+
|
|
47
|
+
const targetExpiresAt = expiresAtRef.current;
|
|
48
|
+
const targetTotalDuration = totalDurationRef.current || 900000;
|
|
49
|
+
|
|
50
|
+
if (targetExpiresAt) {
|
|
51
|
+
const expirationTime = new Date(targetExpiresAt).getTime();
|
|
52
|
+
remaining = Math.max(0, expirationTime - Date.now());
|
|
53
|
+
percentage = (remaining / targetTotalDuration) * 100;
|
|
54
|
+
} else {
|
|
55
|
+
const elapsed = Date.now() - startTime;
|
|
56
|
+
remaining = Math.max(0, durationRef.current - elapsed);
|
|
57
|
+
percentage = (remaining / durationRef.current) * 100;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const finalProgress = Math.max(0, Math.min(100, percentage));
|
|
61
|
+
setState({
|
|
62
|
+
progress: finalProgress,
|
|
63
|
+
remainingSeconds: remaining / 1000
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (remaining <= 0) {
|
|
67
|
+
clearInterval(timer);
|
|
68
|
+
if (onCompleteRef.current) {
|
|
69
|
+
onCompleteRef.current();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}, interval);
|
|
73
|
+
|
|
74
|
+
return () => clearInterval(timer);
|
|
75
|
+
}, [active]);
|
|
76
|
+
|
|
77
|
+
return state;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Funciones auxiliares para interpolar colores en formato HEX
|
|
81
|
+
function hexToRgb(hex) {
|
|
82
|
+
const cleanHex = hex.replace('#', '');
|
|
83
|
+
const bigint = parseInt(cleanHex, 16);
|
|
84
|
+
return [(bigint >> 16) & 255, (bigint >> 8) & 255, bigint & 255];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function rgbToHex(r, g, b) {
|
|
88
|
+
return '#' + [r, g, b].map(x => {
|
|
89
|
+
const hex = Math.round(x).toString(16);
|
|
90
|
+
return hex.length === 1 ? '0' + hex : hex;
|
|
91
|
+
}).join('');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function interpolateColor(color1, color2, factor) {
|
|
95
|
+
const c1 = hexToRgb(color1);
|
|
96
|
+
const c2 = hexToRgb(color2);
|
|
97
|
+
const r = c1[0] + factor * (c2[0] - c1[0]);
|
|
98
|
+
const g = c1[1] + factor * (c2[1] - c1[1]);
|
|
99
|
+
const b = c1[2] + factor * (c2[2] - c1[2]);
|
|
100
|
+
return rgbToHex(r, g, b);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function getColorForProgress(progress, customColors = ['#ef4444', '#f59e0b', '#10b981', '#3b82f6']) {
|
|
104
|
+
// progress va de 100 (inicio) a 0 (fin)
|
|
105
|
+
// Invertimos el cálculo para que 100% sea el inicio del array y 0% sea el final
|
|
106
|
+
const p = Math.max(0, Math.min(100, progress)) / 100;
|
|
107
|
+
|
|
108
|
+
if (customColors.length < 2) return customColors[0] || '#3b82f6';
|
|
109
|
+
|
|
110
|
+
const scaled = p * (customColors.length - 1);
|
|
111
|
+
const index = Math.floor(scaled);
|
|
112
|
+
const factor = scaled - index;
|
|
113
|
+
|
|
114
|
+
if (index >= customColors.length - 1) {
|
|
115
|
+
return customColors[customColors.length - 1];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return interpolateColor(customColors[index], customColors[index + 1], factor);
|
|
119
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Calendar } from 'lucide-react';
|
|
3
|
+
import styles from './index.module.css';
|
|
4
|
+
|
|
5
|
+
export default function DateTag({ date, bgColor, textColor, iconColor, style = {} }) {
|
|
6
|
+
if (!date) return null;
|
|
7
|
+
|
|
8
|
+
let formattedDate = '';
|
|
9
|
+
try {
|
|
10
|
+
formattedDate = new Date(date).toLocaleString('es-ES', {
|
|
11
|
+
day: '2-digit',
|
|
12
|
+
month: '2-digit',
|
|
13
|
+
year: 'numeric',
|
|
14
|
+
hour: '2-digit',
|
|
15
|
+
minute: '2-digit'
|
|
16
|
+
});
|
|
17
|
+
} catch (e) {
|
|
18
|
+
formattedDate = String(date);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const customStyles = {
|
|
22
|
+
backgroundColor: bgColor || 'var(--tag-bg, #f1f5f9)',
|
|
23
|
+
color: textColor || 'var(--tag-text, #475569)',
|
|
24
|
+
...style
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div className={styles.tag} style={customStyles}>
|
|
29
|
+
<Calendar size={14} color={iconColor || textColor || 'var(--tag-text, #475569)'} />
|
|
30
|
+
<span>{formattedDate}</span>
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import DateTag from './index.jsx';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'Components/DateTag',
|
|
5
|
+
component: DateTag,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const Default = {
|
|
9
|
+
args: {
|
|
10
|
+
date: '2026-09-12T10:30:00',
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const CustomColors = {
|
|
15
|
+
args: {
|
|
16
|
+
date: '2026-01-05T08:00:00',
|
|
17
|
+
bgColor: '#fee2e2',
|
|
18
|
+
textColor: '#b91c1c',
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const CustomIconColor = {
|
|
23
|
+
args: {
|
|
24
|
+
date: '2026-12-25T00:00:00',
|
|
25
|
+
bgColor: '#dcfce7',
|
|
26
|
+
textColor: '#166534',
|
|
27
|
+
iconColor: '#16a34a',
|
|
28
|
+
},
|
|
29
|
+
};
|
|
File without changes
|