@axzydev/axzy_ui_system 1.2.0 → 1.2.2
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/dist/index.css +82 -197
- package/dist/index.css.map +1 -1
- package/package.json +2 -2
- package/src/App.tsx +354 -0
- package/src/assets/logo.png +0 -0
- package/src/assets/react.svg +1 -0
- package/src/components/alert/alert.props.ts +13 -0
- package/src/components/alert/alert.stories.tsx +41 -0
- package/src/components/alert/alert.tsx +53 -0
- package/src/components/avatar/avatar.props.ts +14 -0
- package/src/components/avatar/avatar.stories.tsx +46 -0
- package/src/components/avatar/avatar.tsx +53 -0
- package/src/components/badget/badget.props.ts +12 -0
- package/src/components/badget/badget.stories.tsx +76 -0
- package/src/components/badget/badget.tsx +61 -0
- package/src/components/breadcrumbs/breadcrumbs.props.ts +13 -0
- package/src/components/breadcrumbs/breadcrumbs.stories.tsx +21 -0
- package/src/components/breadcrumbs/breadcrumbs.tsx +34 -0
- package/src/components/button/button.props.ts +18 -0
- package/src/components/button/button.stories.tsx +174 -0
- package/src/components/button/button.tsx +117 -0
- package/src/components/calendar/calendar.props.ts +33 -0
- package/src/components/calendar/calendar.stories.tsx +91 -0
- package/src/components/calendar/calendar.tsx +608 -0
- package/src/components/calendar/index.ts +3 -0
- package/src/components/card/card.props.ts +13 -0
- package/src/components/card/card.stories.tsx +58 -0
- package/src/components/card/card.tsx +79 -0
- package/src/components/checkbox/checkbox.props.ts +11 -0
- package/src/components/checkbox/checkbox.stories.tsx +54 -0
- package/src/components/checkbox/checkbox.tsx +52 -0
- package/src/components/confirm-dialog/confirm-dialog.props.ts +14 -0
- package/src/components/confirm-dialog/confirm-dialog.stories.tsx +33 -0
- package/src/components/confirm-dialog/confirm-dialog.tsx +45 -0
- package/src/components/data-table/ITDataTable.stories.tsx +213 -0
- package/src/components/data-table/dataTable.props.ts +69 -0
- package/src/components/data-table/dataTable.tsx +313 -0
- package/src/components/date-picker/date-picker.props.ts +30 -0
- package/src/components/date-picker/date-picker.stories.tsx +90 -0
- package/src/components/date-picker/datePicker.tsx +307 -0
- package/src/components/dialog/dialog.props.ts +9 -0
- package/src/components/dialog/dialog.stories.tsx +80 -0
- package/src/components/dialog/dialog.tsx +88 -0
- package/src/components/divider/divider.props.ts +8 -0
- package/src/components/divider/divider.stories.tsx +34 -0
- package/src/components/divider/divider.tsx +21 -0
- package/src/components/drawer/drawer.props.ts +14 -0
- package/src/components/drawer/drawer.stories.tsx +41 -0
- package/src/components/drawer/drawer.tsx +53 -0
- package/src/components/dropfile/dropfile.stories.tsx +75 -0
- package/src/components/dropfile/dropfile.tsx +407 -0
- package/src/components/empty-state/empty-state.props.ts +9 -0
- package/src/components/empty-state/empty-state.stories.tsx +20 -0
- package/src/components/empty-state/empty-state.tsx +21 -0
- package/src/components/flex/flex.props.ts +22 -0
- package/src/components/flex/flex.stories.tsx +71 -0
- package/src/components/flex/flex.tsx +79 -0
- package/src/components/form-builder/fieldRenderer.tsx +218 -0
- package/src/components/form-builder/formBuilder.context.tsx +70 -0
- package/src/components/form-builder/formBuilder.props.ts +43 -0
- package/src/components/form-builder/formBuilder.stories.tsx +317 -0
- package/src/components/form-builder/formBuilder.tsx +186 -0
- package/src/components/form-builder/useFormBuilder.ts +80 -0
- package/src/components/form-header/form-header.props.ts +5 -0
- package/src/components/form-header/form-header.tsx +38 -0
- package/src/components/grid/grid.props.ts +17 -0
- package/src/components/grid/grid.stories.tsx +72 -0
- package/src/components/grid/grid.tsx +69 -0
- package/src/components/image/image.props.ts +7 -0
- package/src/components/image/image.tsx +38 -0
- package/src/components/input/input.props.ts +49 -0
- package/src/components/input/input.stories.tsx +115 -0
- package/src/components/input/input.tsx +615 -0
- package/src/components/layout/layout.props.ts +10 -0
- package/src/components/layout/layout.stories.tsx +114 -0
- package/src/components/layout/layout.tsx +80 -0
- package/src/components/loader/loader.props.ts +8 -0
- package/src/components/loader/loader.stories.tsx +105 -0
- package/src/components/loader/loader.tsx +108 -0
- package/src/components/navbar/navbar.props.ts +37 -0
- package/src/components/navbar/navbar.tsx +328 -0
- package/src/components/page/page.props.ts +19 -0
- package/src/components/page/page.stories.tsx +98 -0
- package/src/components/page/page.tsx +90 -0
- package/src/components/page-header/page-header.props.ts +11 -0
- package/src/components/page-header/page-header.stories.tsx +61 -0
- package/src/components/page-header/page-header.tsx +62 -0
- package/src/components/pagination/pagination.props.ts +53 -0
- package/src/components/pagination/pagination.stories.tsx +111 -0
- package/src/components/pagination/pagination.tsx +241 -0
- package/src/components/popover/popover.props.ts +12 -0
- package/src/components/popover/popover.stories.tsx +25 -0
- package/src/components/popover/popover.tsx +45 -0
- package/src/components/progress/progress.props.ts +12 -0
- package/src/components/progress/progress.stories.tsx +40 -0
- package/src/components/progress/progress.tsx +52 -0
- package/src/components/radio/radio.props.ts +16 -0
- package/src/components/radio/radio.stories.tsx +50 -0
- package/src/components/radio/radio.tsx +58 -0
- package/src/components/search-select/index.ts +2 -0
- package/src/components/search-select/search-select.props.ts +46 -0
- package/src/components/search-select/search-select.stories.tsx +129 -0
- package/src/components/search-select/search-select.tsx +229 -0
- package/src/components/searchTable/components/EditableCell.tsx +149 -0
- package/src/components/searchTable/components/PaginationControls.tsx +86 -0
- package/src/components/searchTable/components/PaginationInfo.tsx +20 -0
- package/src/components/searchTable/components/SearchAndSortBar.tsx +53 -0
- package/src/components/searchTable/components/SearchInput.tsx +33 -0
- package/src/components/searchTable/components/SortButton.tsx +50 -0
- package/src/components/searchTable/components/TableEmptyState.tsx +22 -0
- package/src/components/searchTable/components/TableHeader.tsx +35 -0
- package/src/components/searchTable/components/TableHeaderCell.tsx +43 -0
- package/src/components/searchTable/components/TableRow.tsx +144 -0
- package/src/components/searchTable/searchTable.props.ts +56 -0
- package/src/components/searchTable/searchTable.tsx +187 -0
- package/src/components/segmented-control/segmented-control.props.ts +18 -0
- package/src/components/segmented-control/segmented-control.stories.tsx +63 -0
- package/src/components/segmented-control/segmented-control.tsx +52 -0
- package/src/components/select/select.props.ts +25 -0
- package/src/components/select/select.stories.tsx +86 -0
- package/src/components/select/select.tsx +150 -0
- package/src/components/sidebar/sidebar.props.ts +28 -0
- package/src/components/sidebar/sidebar.stories.tsx +117 -0
- package/src/components/sidebar/sidebar.tsx +313 -0
- package/src/components/skeleton/skeleton.props.ts +12 -0
- package/src/components/skeleton/skeleton.stories.tsx +30 -0
- package/src/components/skeleton/skeleton.tsx +45 -0
- package/src/components/slide/slide.props.ts +45 -0
- package/src/components/slide/slide.stories.tsx +121 -0
- package/src/components/slide/slide.tsx +109 -0
- package/src/components/slider/slider.props.ts +10 -0
- package/src/components/slider/slider.stories.tsx +30 -0
- package/src/components/slider/slider.tsx +49 -0
- package/src/components/stack/stack.props.ts +19 -0
- package/src/components/stack/stack.stories.tsx +79 -0
- package/src/components/stack/stack.tsx +79 -0
- package/src/components/stat-card/stat-card.props.ts +13 -0
- package/src/components/stat-card/stat-card.stories.tsx +41 -0
- package/src/components/stat-card/stat-card.tsx +44 -0
- package/src/components/stepper/stepper.css +26 -0
- package/src/components/stepper/stepper.props.ts +29 -0
- package/src/components/stepper/stepper.stories.tsx +155 -0
- package/src/components/stepper/stepper.tsx +227 -0
- package/src/components/table/table.props.ts +43 -0
- package/src/components/table/table.stories.tsx +189 -0
- package/src/components/table/table.tsx +376 -0
- package/src/components/tabs/tabs.props.ts +18 -0
- package/src/components/tabs/tabs.stories.tsx +32 -0
- package/src/components/tabs/tabs.tsx +74 -0
- package/src/components/text/text.props.ts +9 -0
- package/src/components/text/text.tsx +20 -0
- package/src/components/textarea/textarea.props.ts +15 -0
- package/src/components/textarea/textarea.stories.tsx +27 -0
- package/src/components/textarea/textarea.tsx +55 -0
- package/src/components/theme-provider/themeProvider.props.ts +28 -0
- package/src/components/theme-provider/themeProvider.tsx +1854 -0
- package/src/components/time-picker/timePicker.props.ts +16 -0
- package/src/components/time-picker/timePicker.stories.tsx +131 -0
- package/src/components/time-picker/timePicker.tsx +317 -0
- package/src/components/toast/toast.css +32 -0
- package/src/components/toast/toast.props.ts +13 -0
- package/src/components/toast/toast.stories.tsx +138 -0
- package/src/components/toast/toast.tsx +87 -0
- package/src/components/tooltip/tooltip.props.ts +11 -0
- package/src/components/tooltip/tooltip.stories.tsx +20 -0
- package/src/components/tooltip/tooltip.tsx +55 -0
- package/src/components/topbar/topbar.props.ts +21 -0
- package/src/components/topbar/topbar.stories.tsx +80 -0
- package/src/components/topbar/topbar.tsx +205 -0
- package/src/components/triple-filter/tripleFilter.props.ts +15 -0
- package/src/components/triple-filter/tripleFilter.stories.tsx +32 -0
- package/src/components/triple-filter/tripleFilter.tsx +50 -0
- package/src/hooks/useClickOutside.ts +21 -0
- package/src/hooks/useDebouncedSearch.ts +55 -0
- package/src/hooks/useEditableRow.ts +157 -0
- package/src/hooks/useTableState.ts +122 -0
- package/src/index.css +168 -0
- package/src/index.ts +165 -0
- package/src/main.tsx +9 -0
- package/src/showcases/DataShowcases.tsx +260 -0
- package/src/showcases/FeedbackShowcases.tsx +268 -0
- package/src/showcases/FormShowcases.tsx +1159 -0
- package/src/showcases/HomeShowcase.tsx +324 -0
- package/src/showcases/LayoutPrimitivesShowcases.tsx +569 -0
- package/src/showcases/NavigationShowcases.tsx +193 -0
- package/src/showcases/PageShowcases.tsx +207 -0
- package/src/showcases/ShowcaseLayout.tsx +139 -0
- package/src/showcases/StructureShowcases.tsx +152 -0
- package/src/types/badget.types.ts +37 -0
- package/src/types/button.types.ts +16 -0
- package/src/types/colors.types.ts +3 -0
- package/src/types/field.types.ts +103 -0
- package/src/types/formik.types.ts +15 -0
- package/src/types/input.types.ts +14 -0
- package/src/types/loader.types.ts +9 -0
- package/src/types/sizes.types.ts +1 -0
- package/src/types/table.types.ts +15 -0
- package/src/types/toast.types.ts +8 -0
- package/src/types/yup.types.ts +11 -0
- package/src/utils/color.utils.ts +99 -0
- package/src/utils/styles.ts +120 -0
- package/src/utils/table.utils.ts +10 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import clsx from "clsx";
|
|
2
|
+
import { ITCardProps } from "./card.props";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import ITText from "@/components/text/text";
|
|
5
|
+
|
|
6
|
+
export default function ITCard({
|
|
7
|
+
title,
|
|
8
|
+
image,
|
|
9
|
+
alt = "Card Image",
|
|
10
|
+
children,
|
|
11
|
+
actions,
|
|
12
|
+
className,
|
|
13
|
+
imageClassName,
|
|
14
|
+
titleClassName,
|
|
15
|
+
contentClassName,
|
|
16
|
+
actionClassName,
|
|
17
|
+
onClick,
|
|
18
|
+
}: ITCardProps) {
|
|
19
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
20
|
+
|
|
21
|
+
const containerStyle: React.CSSProperties = {
|
|
22
|
+
backgroundColor: "var(--card-bg, #ffffff)",
|
|
23
|
+
borderColor: "var(--card-border, #e2e8f0)",
|
|
24
|
+
borderWidth: "1px",
|
|
25
|
+
borderRadius: "var(--card-radius, 0.75rem)",
|
|
26
|
+
boxShadow: onClick
|
|
27
|
+
? isHovered
|
|
28
|
+
? "0 8px 25px -5px rgba(0, 0, 0, 0.1)"
|
|
29
|
+
: "0 1px 3px 0 rgba(0, 0, 0, 0.05)"
|
|
30
|
+
: "none",
|
|
31
|
+
transition: onClick ? "all 0.2s ease-in-out" : "none",
|
|
32
|
+
cursor: onClick ? "pointer" : "default",
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const bodyStyle: React.CSSProperties = {
|
|
36
|
+
padding: "1.25rem",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div
|
|
41
|
+
onClick={onClick}
|
|
42
|
+
onMouseEnter={() => onClick && setIsHovered(true)}
|
|
43
|
+
onMouseLeave={() => onClick && setIsHovered(false)}
|
|
44
|
+
className={clsx("overflow-hidden flex flex-col", className)}
|
|
45
|
+
style={containerStyle}
|
|
46
|
+
>
|
|
47
|
+
{image && (
|
|
48
|
+
<img
|
|
49
|
+
src={image}
|
|
50
|
+
alt={alt}
|
|
51
|
+
className={clsx("w-full h-48 object-cover", imageClassName)}
|
|
52
|
+
/>
|
|
53
|
+
)}
|
|
54
|
+
|
|
55
|
+
<div className={clsx(contentClassName)} style={bodyStyle}>
|
|
56
|
+
{title && (
|
|
57
|
+
<ITText
|
|
58
|
+
as="h3"
|
|
59
|
+
className={clsx("text-xl font-semibold mb-2", titleClassName)}
|
|
60
|
+
>
|
|
61
|
+
{title}
|
|
62
|
+
</ITText>
|
|
63
|
+
)}
|
|
64
|
+
<ITText as="div" className="text-gray-600">{children}</ITText>
|
|
65
|
+
</div>
|
|
66
|
+
{actions && (
|
|
67
|
+
<div
|
|
68
|
+
className={clsx(
|
|
69
|
+
"p-4 border-t border-gray-100 mt-auto",
|
|
70
|
+
actionClassName,
|
|
71
|
+
)}
|
|
72
|
+
>
|
|
73
|
+
{actions}
|
|
74
|
+
</div>
|
|
75
|
+
)}
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import ITCheckbox from "./checkbox";
|
|
4
|
+
import ITStack from "../stack/stack";
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof ITCheckbox> = {
|
|
7
|
+
title: "Components/Inputs/ITCheckbox",
|
|
8
|
+
component: ITCheckbox,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default meta;
|
|
13
|
+
type Story = StoryObj<typeof ITCheckbox>;
|
|
14
|
+
|
|
15
|
+
export const Default: Story = {
|
|
16
|
+
render: () => {
|
|
17
|
+
const [val, setVal] = useState(false);
|
|
18
|
+
return <ITCheckbox checked={val} onChange={setVal} label="Acepto términos" />;
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const Indeterminate: Story = {
|
|
23
|
+
args: { indeterminate: true, label: "Selección parcial" },
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const Disabled: Story = {
|
|
27
|
+
args: { checked: true, disabled: true, label: "Opción bloqueada" },
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const Group: Story = {
|
|
31
|
+
render: () => {
|
|
32
|
+
const [vals, setVals] = useState<Record<string, boolean>>({ a: true, b: false, c: false });
|
|
33
|
+
const all = Object.values(vals).every(Boolean);
|
|
34
|
+
const some = Object.values(vals).some(Boolean) && !all;
|
|
35
|
+
return (
|
|
36
|
+
<ITStack spacing={2}>
|
|
37
|
+
<ITCheckbox
|
|
38
|
+
checked={all}
|
|
39
|
+
indeterminate={some}
|
|
40
|
+
onChange={(v) => setVals({ a: v, b: v, c: v })}
|
|
41
|
+
label="Seleccionar todo"
|
|
42
|
+
/>
|
|
43
|
+
{Object.entries(vals).map(([k, v]) => (
|
|
44
|
+
<ITCheckbox
|
|
45
|
+
key={k}
|
|
46
|
+
checked={v}
|
|
47
|
+
onChange={(val) => setVals(p => ({ ...p, [k]: val }))}
|
|
48
|
+
label={`Opción ${k.toUpperCase()}`}
|
|
49
|
+
/>
|
|
50
|
+
))}
|
|
51
|
+
</ITStack>
|
|
52
|
+
);
|
|
53
|
+
},
|
|
54
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import clsx from "clsx";
|
|
2
|
+
import { ITCheckboxProps } from "./checkbox.props";
|
|
3
|
+
import ITText from "@/components/text/text";
|
|
4
|
+
|
|
5
|
+
export default function ITCheckbox({
|
|
6
|
+
checked = false,
|
|
7
|
+
onChange,
|
|
8
|
+
label,
|
|
9
|
+
disabled = false,
|
|
10
|
+
indeterminate = false,
|
|
11
|
+
className,
|
|
12
|
+
name,
|
|
13
|
+
}: ITCheckboxProps) {
|
|
14
|
+
return (
|
|
15
|
+
<label
|
|
16
|
+
className={clsx(
|
|
17
|
+
"inline-flex items-center gap-2 cursor-pointer select-none",
|
|
18
|
+
disabled && "opacity-50 cursor-not-allowed",
|
|
19
|
+
className
|
|
20
|
+
)}
|
|
21
|
+
>
|
|
22
|
+
<input
|
|
23
|
+
type="checkbox"
|
|
24
|
+
name={name}
|
|
25
|
+
checked={checked}
|
|
26
|
+
onChange={(e) => onChange?.(e.target.checked)}
|
|
27
|
+
disabled={disabled}
|
|
28
|
+
ref={(el) => { if (el) el.indeterminate = indeterminate; }}
|
|
29
|
+
className="peer sr-only"
|
|
30
|
+
/>
|
|
31
|
+
<div
|
|
32
|
+
className={clsx(
|
|
33
|
+
"w-4 h-4 rounded border-2 flex items-center justify-center transition-all",
|
|
34
|
+
checked
|
|
35
|
+
? "bg-primary-500 border-primary-500"
|
|
36
|
+
: "border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800",
|
|
37
|
+
!disabled && "peer-focus:ring-2 peer-focus:ring-primary-200",
|
|
38
|
+
)}
|
|
39
|
+
>
|
|
40
|
+
{checked && (
|
|
41
|
+
<svg className="w-2.5 h-2.5 text-white" viewBox="0 0 12 12" fill="none">
|
|
42
|
+
<path d="M2 6L5 9L10 3" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
|
43
|
+
</svg>
|
|
44
|
+
)}
|
|
45
|
+
{indeterminate && !checked && (
|
|
46
|
+
<div className="w-2 h-0.5 bg-slate-500 rounded" />
|
|
47
|
+
)}
|
|
48
|
+
</div>
|
|
49
|
+
{label && <ITText as="span" className="text-sm text-slate-700 dark:text-slate-300">{label}</ITText>}
|
|
50
|
+
</label>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { ColorsTypes } from "@/types/colors.types";
|
|
3
|
+
|
|
4
|
+
export interface ITConfirmDialogProps {
|
|
5
|
+
isOpen: boolean;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
onConfirm: () => void;
|
|
8
|
+
title?: string;
|
|
9
|
+
message?: ReactNode;
|
|
10
|
+
confirmLabel?: string;
|
|
11
|
+
cancelLabel?: string;
|
|
12
|
+
variant?: ColorsTypes;
|
|
13
|
+
loading?: boolean;
|
|
14
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import ITConfirmDialog from "./confirm-dialog";
|
|
4
|
+
import ITButton from "../button/button";
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof ITConfirmDialog> = {
|
|
7
|
+
title: "Components/Overlay/ITConfirmDialog",
|
|
8
|
+
component: ITConfirmDialog,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default meta;
|
|
13
|
+
type Story = StoryObj<typeof ITConfirmDialog>;
|
|
14
|
+
|
|
15
|
+
export const Default: Story = {
|
|
16
|
+
render: () => {
|
|
17
|
+
const [open, setOpen] = useState(false);
|
|
18
|
+
return (
|
|
19
|
+
<>
|
|
20
|
+
<ITButton label="Eliminar" color="danger" onClick={() => setOpen(true)} />
|
|
21
|
+
<ITConfirmDialog
|
|
22
|
+
isOpen={open}
|
|
23
|
+
onClose={() => setOpen(false)}
|
|
24
|
+
onConfirm={() => { alert("Confirmado"); setOpen(false); }}
|
|
25
|
+
title="Eliminar usuario"
|
|
26
|
+
message="Esta acción no se puede deshacer. ¿Deseas eliminar este usuario?"
|
|
27
|
+
confirmLabel="Eliminar"
|
|
28
|
+
variant="danger"
|
|
29
|
+
/>
|
|
30
|
+
</>
|
|
31
|
+
);
|
|
32
|
+
},
|
|
33
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import clsx from "clsx";
|
|
2
|
+
import { ITConfirmDialogProps } from "./confirm-dialog.props";
|
|
3
|
+
import ITButton from "../button/button";
|
|
4
|
+
import { FaExclamationTriangle } from "react-icons/fa";
|
|
5
|
+
import ITText from "@/components/text/text";
|
|
6
|
+
|
|
7
|
+
export default function ITConfirmDialog({
|
|
8
|
+
isOpen,
|
|
9
|
+
onClose,
|
|
10
|
+
onConfirm,
|
|
11
|
+
title = "Confirmar acción",
|
|
12
|
+
message = "¿Estás seguro de que deseas continuar?",
|
|
13
|
+
confirmLabel = "Confirmar",
|
|
14
|
+
cancelLabel = "Cancelar",
|
|
15
|
+
variant = "primary",
|
|
16
|
+
loading = false,
|
|
17
|
+
}: ITConfirmDialogProps) {
|
|
18
|
+
if (!isOpen) return null;
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className="fixed inset-0 z-[300] flex items-center justify-center p-4">
|
|
22
|
+
<div className="absolute inset-0 bg-black/40 backdrop-blur-sm" onClick={onClose} />
|
|
23
|
+
<div
|
|
24
|
+
className={clsx(
|
|
25
|
+
"relative z-10 bg-white dark:bg-slate-900 rounded-2xl shadow-2xl max-w-md w-full p-6",
|
|
26
|
+
"border border-slate-200 dark:border-slate-700"
|
|
27
|
+
)}
|
|
28
|
+
>
|
|
29
|
+
<div className="flex items-start gap-4">
|
|
30
|
+
<div className="w-10 h-10 rounded-full bg-amber-100 dark:bg-amber-950/30 flex items-center justify-center flex-shrink-0">
|
|
31
|
+
<FaExclamationTriangle className="text-amber-600" size={18} />
|
|
32
|
+
</div>
|
|
33
|
+
<div className="flex-1">
|
|
34
|
+
<ITText as="h3" className="text-lg font-bold text-slate-800 dark:text-white">{title}</ITText>
|
|
35
|
+
<ITText as="p" className="text-sm text-slate-500 dark:text-slate-400 mt-1">{message}</ITText>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
<div className="flex justify-end gap-3 mt-6">
|
|
39
|
+
<ITButton label={cancelLabel} variant="outlined" size="small" onClick={onClose} disabled={loading} />
|
|
40
|
+
<ITButton label={confirmLabel} color={variant} size="small" onClick={onConfirm} disabled={loading} />
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import React, { useState, useCallback, useMemo } from 'react';
|
|
2
|
+
import { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
import ITDataTable from './dataTable';
|
|
4
|
+
import { ITDataTableFetchParams, ITDataTableResponse } from './dataTable.props';
|
|
5
|
+
import ITCard from '../card/card';
|
|
6
|
+
import ITBadget from '../badget/badget';
|
|
7
|
+
import ITDatePicker from '../date-picker/datePicker';
|
|
8
|
+
|
|
9
|
+
const meta: Meta<typeof ITDataTable> = {
|
|
10
|
+
title: 'Data Display/ITDataTable',
|
|
11
|
+
component: ITDataTable,
|
|
12
|
+
tags: ['autodocs'],
|
|
13
|
+
decorators: [
|
|
14
|
+
(Story) => (
|
|
15
|
+
<div className="p-8 bg-gray-50 min-h-screen">
|
|
16
|
+
<Story />
|
|
17
|
+
</div>
|
|
18
|
+
),
|
|
19
|
+
],
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
type Story = StoryObj<typeof ITDataTable>;
|
|
24
|
+
|
|
25
|
+
// --- MOCK CONSTANTS ---
|
|
26
|
+
const ROLES = ['ADMIN', 'CAJERO', 'TECNICO', 'SUPERVISOR'];
|
|
27
|
+
const MOCK_DATA = Array.from({ length: 100 }).map((_, i) => ({
|
|
28
|
+
id: i + 1,
|
|
29
|
+
username: `user_${i + 1}`,
|
|
30
|
+
name: `Nombre ${i + 1}`,
|
|
31
|
+
role: ROLES[i % ROLES.length],
|
|
32
|
+
active: i % 3 !== 0,
|
|
33
|
+
createdAt: new Date(Date.now() - i * 86400000).toISOString(),
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
// --- MOCK FETCH FUNCTION ---
|
|
37
|
+
const mockFetchItems = async (
|
|
38
|
+
params: ITDataTableFetchParams
|
|
39
|
+
): Promise<ITDataTableResponse<any>> => {
|
|
40
|
+
console.log('Fetching with params:', params);
|
|
41
|
+
|
|
42
|
+
// Simulate network delay
|
|
43
|
+
await new Promise((resolve) => setTimeout(resolve, 800));
|
|
44
|
+
|
|
45
|
+
let filtered = [...MOCK_DATA];
|
|
46
|
+
|
|
47
|
+
// Apply filters
|
|
48
|
+
Object.entries(params.filters).forEach(([key, value]) => {
|
|
49
|
+
if (value === undefined || value === '') return;
|
|
50
|
+
|
|
51
|
+
if (key === 'username') {
|
|
52
|
+
filtered = filtered.filter(item =>
|
|
53
|
+
item.username.toLowerCase().includes(String(value).toLowerCase())
|
|
54
|
+
);
|
|
55
|
+
} else if (key === 'role') {
|
|
56
|
+
filtered = filtered.filter(item => item.role === value);
|
|
57
|
+
} else if (key === 'active') {
|
|
58
|
+
filtered = filtered.filter(item => item.active === value);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Apply sorting
|
|
63
|
+
if (params.sort) {
|
|
64
|
+
const { key, direction } = params.sort;
|
|
65
|
+
filtered.sort((a: any, b: any) => {
|
|
66
|
+
if (a[key] < b[key]) return direction === 'asc' ? -1 : 1;
|
|
67
|
+
if (a[key] > b[key]) return direction === 'asc' ? 1 : -1;
|
|
68
|
+
return 0;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Apply pagination
|
|
73
|
+
const start = (params.page - 1) * params.limit;
|
|
74
|
+
const end = start + params.limit;
|
|
75
|
+
const data = filtered.slice(start, end);
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
data,
|
|
79
|
+
total: filtered.length,
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const columns = [
|
|
84
|
+
{
|
|
85
|
+
key: 'id',
|
|
86
|
+
label: 'ID',
|
|
87
|
+
type: 'number' as const,
|
|
88
|
+
sortable: true,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
key: 'username',
|
|
92
|
+
label: 'Usuario',
|
|
93
|
+
type: 'string' as const,
|
|
94
|
+
filter: true,
|
|
95
|
+
sortable: true,
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
key: 'name',
|
|
99
|
+
label: 'Nombre Completo',
|
|
100
|
+
type: 'string' as const,
|
|
101
|
+
filter: true,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
key: 'role',
|
|
105
|
+
label: 'Rol',
|
|
106
|
+
type: 'string' as const,
|
|
107
|
+
filter: 'catalog' as const,
|
|
108
|
+
catalogOptions: {
|
|
109
|
+
loading: false,
|
|
110
|
+
data: [
|
|
111
|
+
{ id: 'ADMIN', name: 'Administrador' },
|
|
112
|
+
{ id: 'CAJERO', name: 'Cajero' },
|
|
113
|
+
{ id: 'TECNICO', name: 'Técnico' },
|
|
114
|
+
{ id: 'SUPERVISOR', name: 'Supervisor' },
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
render: (row: any) => (
|
|
118
|
+
<ITBadget
|
|
119
|
+
label={row.role}
|
|
120
|
+
color={
|
|
121
|
+
row.role === 'ADMIN' ? 'primary' :
|
|
122
|
+
row.role === 'CAJERO' ? 'success' :
|
|
123
|
+
row.role === 'TECNICO' ? 'warning' : 'secondary'
|
|
124
|
+
}
|
|
125
|
+
/>
|
|
126
|
+
)
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
key: 'active',
|
|
130
|
+
label: 'Estado',
|
|
131
|
+
type: 'boolean' as const,
|
|
132
|
+
filter: true,
|
|
133
|
+
sortable: true,
|
|
134
|
+
},
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
export const Default: Story = {
|
|
138
|
+
args: {
|
|
139
|
+
title: 'Gestión de Usuarios',
|
|
140
|
+
columns,
|
|
141
|
+
fetchData: mockFetchItems,
|
|
142
|
+
defaultItemsPerPage: 10,
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export const RemoteWithExternalFilters: Story = {
|
|
147
|
+
render: () => {
|
|
148
|
+
const [selectedDate, setSelectedDate] = useState<Date | undefined>(undefined);
|
|
149
|
+
const [dateRange, setDateRange] = useState<[Date | null, Date | null]>([null, null]);
|
|
150
|
+
|
|
151
|
+
// Stable references for ITDataTable
|
|
152
|
+
const memoizedFetch = useCallback(mockFetchItems, []);
|
|
153
|
+
const memoizedColumns = useMemo(() => columns, []);
|
|
154
|
+
const externalFilters = useMemo(() => ({
|
|
155
|
+
date: selectedDate,
|
|
156
|
+
startDate: dateRange[0],
|
|
157
|
+
endDate: dateRange[1]
|
|
158
|
+
}), [selectedDate, dateRange]);
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<div className="space-y-6">
|
|
162
|
+
<ITCard title="Filtros Globales de Negocio" className="bg-white">
|
|
163
|
+
<div className="flex flex-wrap gap-4 items-end">
|
|
164
|
+
<div className="w-64">
|
|
165
|
+
<ITDatePicker
|
|
166
|
+
name="single"
|
|
167
|
+
label="Fecha Específica"
|
|
168
|
+
value={selectedDate}
|
|
169
|
+
onChange={(e: any) => setSelectedDate(e.target.value)}
|
|
170
|
+
placeholder="Seleccionar..."
|
|
171
|
+
/>
|
|
172
|
+
</div>
|
|
173
|
+
<div className="w-80">
|
|
174
|
+
<ITDatePicker
|
|
175
|
+
range
|
|
176
|
+
name="range"
|
|
177
|
+
label="Rango de Auditoría"
|
|
178
|
+
value={dateRange}
|
|
179
|
+
onChange={(e: any) => setDateRange(e.target.value)}
|
|
180
|
+
placeholder="Seleccionar rango..."
|
|
181
|
+
/>
|
|
182
|
+
</div>
|
|
183
|
+
<button
|
|
184
|
+
onClick={() => { setSelectedDate(undefined); setDateRange([null, null]); }}
|
|
185
|
+
className="px-4 py-2 text-sm text-primary-600 hover:text-primary-700 font-medium"
|
|
186
|
+
>
|
|
187
|
+
Resetear Filtros
|
|
188
|
+
</button>
|
|
189
|
+
</div>
|
|
190
|
+
</ITCard>
|
|
191
|
+
|
|
192
|
+
<ITDataTable
|
|
193
|
+
title="Resultados Filtrados"
|
|
194
|
+
columns={memoizedColumns}
|
|
195
|
+
fetchData={memoizedFetch}
|
|
196
|
+
externalFilters={externalFilters}
|
|
197
|
+
defaultItemsPerPage={5}
|
|
198
|
+
/>
|
|
199
|
+
</div>
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export const SkeletonLoading: Story = {
|
|
205
|
+
args: {
|
|
206
|
+
title: 'Cargando Datos...',
|
|
207
|
+
columns,
|
|
208
|
+
fetchData: async () => {
|
|
209
|
+
await new Promise(r => setTimeout(r, 10000));
|
|
210
|
+
return { data: [], total: 0 };
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { Column } from "../table/table.props";
|
|
3
|
+
|
|
4
|
+
export interface ITDataTableFetchParams {
|
|
5
|
+
page: number;
|
|
6
|
+
limit: number;
|
|
7
|
+
filters: Record<string, string | number | boolean | Date>;
|
|
8
|
+
sort?: {
|
|
9
|
+
key: string;
|
|
10
|
+
direction: "asc" | "desc";
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ITDataTableResponse<T> {
|
|
15
|
+
data: T[];
|
|
16
|
+
total: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ITDataTableProps<T extends Record<string, unknown>> {
|
|
20
|
+
/**
|
|
21
|
+
* The column configuration array matching ITTable but adapted for Server-Side processing
|
|
22
|
+
*/
|
|
23
|
+
columns: Column<T>[];
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Async callback that the component will trigger whenever pagination, filtering or sorting changes.
|
|
27
|
+
* It must return a Promise with `data` array and the `total` items matching the query.
|
|
28
|
+
*/
|
|
29
|
+
fetchData: (params: ITDataTableFetchParams) => Promise<ITDataTableResponse<T>>;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The amount of milliseconds to wait after internal `filters` state changes
|
|
33
|
+
* before triggering `fetchData`. Helpful to avoid spamming the backend while typing.
|
|
34
|
+
* @default 400
|
|
35
|
+
*/
|
|
36
|
+
debounceMs?: number;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Filters managed outside of the ITDataTable (e.g. a date range picker).
|
|
40
|
+
* These will be merged with the internal column filters before calling fetchData.
|
|
41
|
+
*/
|
|
42
|
+
externalFilters?: Record<string, string | number | boolean | Date>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Custom element to display instead of the default spinner while `isLoading` is true.
|
|
46
|
+
*/
|
|
47
|
+
loadingIndicator?: ReactNode;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Re-fetches the table automatically upon mounting.
|
|
51
|
+
* @default true
|
|
52
|
+
*/
|
|
53
|
+
fetchOnMount?: boolean;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* External hook to force the component to re-fetch the current page.
|
|
57
|
+
* Example: trigger after a successful modal form submission.
|
|
58
|
+
*/
|
|
59
|
+
reloadTrigger?: number | string | boolean;
|
|
60
|
+
|
|
61
|
+
// Visual Inherited Props from ITTable
|
|
62
|
+
containerClassName?: string;
|
|
63
|
+
className?: string;
|
|
64
|
+
variant?: "default" | "striped" | "bordered" | "minimal";
|
|
65
|
+
size?: "sm" | "md" | "lg";
|
|
66
|
+
itemsPerPageOptions?: number[];
|
|
67
|
+
defaultItemsPerPage?: number;
|
|
68
|
+
title?: string | ReactNode;
|
|
69
|
+
}
|