@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.
Files changed (202) hide show
  1. package/dist/index.css +82 -197
  2. package/dist/index.css.map +1 -1
  3. package/package.json +2 -2
  4. package/src/App.tsx +354 -0
  5. package/src/assets/logo.png +0 -0
  6. package/src/assets/react.svg +1 -0
  7. package/src/components/alert/alert.props.ts +13 -0
  8. package/src/components/alert/alert.stories.tsx +41 -0
  9. package/src/components/alert/alert.tsx +53 -0
  10. package/src/components/avatar/avatar.props.ts +14 -0
  11. package/src/components/avatar/avatar.stories.tsx +46 -0
  12. package/src/components/avatar/avatar.tsx +53 -0
  13. package/src/components/badget/badget.props.ts +12 -0
  14. package/src/components/badget/badget.stories.tsx +76 -0
  15. package/src/components/badget/badget.tsx +61 -0
  16. package/src/components/breadcrumbs/breadcrumbs.props.ts +13 -0
  17. package/src/components/breadcrumbs/breadcrumbs.stories.tsx +21 -0
  18. package/src/components/breadcrumbs/breadcrumbs.tsx +34 -0
  19. package/src/components/button/button.props.ts +18 -0
  20. package/src/components/button/button.stories.tsx +174 -0
  21. package/src/components/button/button.tsx +117 -0
  22. package/src/components/calendar/calendar.props.ts +33 -0
  23. package/src/components/calendar/calendar.stories.tsx +91 -0
  24. package/src/components/calendar/calendar.tsx +608 -0
  25. package/src/components/calendar/index.ts +3 -0
  26. package/src/components/card/card.props.ts +13 -0
  27. package/src/components/card/card.stories.tsx +58 -0
  28. package/src/components/card/card.tsx +79 -0
  29. package/src/components/checkbox/checkbox.props.ts +11 -0
  30. package/src/components/checkbox/checkbox.stories.tsx +54 -0
  31. package/src/components/checkbox/checkbox.tsx +52 -0
  32. package/src/components/confirm-dialog/confirm-dialog.props.ts +14 -0
  33. package/src/components/confirm-dialog/confirm-dialog.stories.tsx +33 -0
  34. package/src/components/confirm-dialog/confirm-dialog.tsx +45 -0
  35. package/src/components/data-table/ITDataTable.stories.tsx +213 -0
  36. package/src/components/data-table/dataTable.props.ts +69 -0
  37. package/src/components/data-table/dataTable.tsx +313 -0
  38. package/src/components/date-picker/date-picker.props.ts +30 -0
  39. package/src/components/date-picker/date-picker.stories.tsx +90 -0
  40. package/src/components/date-picker/datePicker.tsx +307 -0
  41. package/src/components/dialog/dialog.props.ts +9 -0
  42. package/src/components/dialog/dialog.stories.tsx +80 -0
  43. package/src/components/dialog/dialog.tsx +88 -0
  44. package/src/components/divider/divider.props.ts +8 -0
  45. package/src/components/divider/divider.stories.tsx +34 -0
  46. package/src/components/divider/divider.tsx +21 -0
  47. package/src/components/drawer/drawer.props.ts +14 -0
  48. package/src/components/drawer/drawer.stories.tsx +41 -0
  49. package/src/components/drawer/drawer.tsx +53 -0
  50. package/src/components/dropfile/dropfile.stories.tsx +75 -0
  51. package/src/components/dropfile/dropfile.tsx +407 -0
  52. package/src/components/empty-state/empty-state.props.ts +9 -0
  53. package/src/components/empty-state/empty-state.stories.tsx +20 -0
  54. package/src/components/empty-state/empty-state.tsx +21 -0
  55. package/src/components/flex/flex.props.ts +22 -0
  56. package/src/components/flex/flex.stories.tsx +71 -0
  57. package/src/components/flex/flex.tsx +79 -0
  58. package/src/components/form-builder/fieldRenderer.tsx +218 -0
  59. package/src/components/form-builder/formBuilder.context.tsx +70 -0
  60. package/src/components/form-builder/formBuilder.props.ts +43 -0
  61. package/src/components/form-builder/formBuilder.stories.tsx +317 -0
  62. package/src/components/form-builder/formBuilder.tsx +186 -0
  63. package/src/components/form-builder/useFormBuilder.ts +80 -0
  64. package/src/components/form-header/form-header.props.ts +5 -0
  65. package/src/components/form-header/form-header.tsx +38 -0
  66. package/src/components/grid/grid.props.ts +17 -0
  67. package/src/components/grid/grid.stories.tsx +72 -0
  68. package/src/components/grid/grid.tsx +69 -0
  69. package/src/components/image/image.props.ts +7 -0
  70. package/src/components/image/image.tsx +38 -0
  71. package/src/components/input/input.props.ts +49 -0
  72. package/src/components/input/input.stories.tsx +115 -0
  73. package/src/components/input/input.tsx +615 -0
  74. package/src/components/layout/layout.props.ts +10 -0
  75. package/src/components/layout/layout.stories.tsx +114 -0
  76. package/src/components/layout/layout.tsx +80 -0
  77. package/src/components/loader/loader.props.ts +8 -0
  78. package/src/components/loader/loader.stories.tsx +105 -0
  79. package/src/components/loader/loader.tsx +108 -0
  80. package/src/components/navbar/navbar.props.ts +37 -0
  81. package/src/components/navbar/navbar.tsx +328 -0
  82. package/src/components/page/page.props.ts +19 -0
  83. package/src/components/page/page.stories.tsx +98 -0
  84. package/src/components/page/page.tsx +90 -0
  85. package/src/components/page-header/page-header.props.ts +11 -0
  86. package/src/components/page-header/page-header.stories.tsx +61 -0
  87. package/src/components/page-header/page-header.tsx +62 -0
  88. package/src/components/pagination/pagination.props.ts +53 -0
  89. package/src/components/pagination/pagination.stories.tsx +111 -0
  90. package/src/components/pagination/pagination.tsx +241 -0
  91. package/src/components/popover/popover.props.ts +12 -0
  92. package/src/components/popover/popover.stories.tsx +25 -0
  93. package/src/components/popover/popover.tsx +45 -0
  94. package/src/components/progress/progress.props.ts +12 -0
  95. package/src/components/progress/progress.stories.tsx +40 -0
  96. package/src/components/progress/progress.tsx +52 -0
  97. package/src/components/radio/radio.props.ts +16 -0
  98. package/src/components/radio/radio.stories.tsx +50 -0
  99. package/src/components/radio/radio.tsx +58 -0
  100. package/src/components/search-select/index.ts +2 -0
  101. package/src/components/search-select/search-select.props.ts +46 -0
  102. package/src/components/search-select/search-select.stories.tsx +129 -0
  103. package/src/components/search-select/search-select.tsx +229 -0
  104. package/src/components/searchTable/components/EditableCell.tsx +149 -0
  105. package/src/components/searchTable/components/PaginationControls.tsx +86 -0
  106. package/src/components/searchTable/components/PaginationInfo.tsx +20 -0
  107. package/src/components/searchTable/components/SearchAndSortBar.tsx +53 -0
  108. package/src/components/searchTable/components/SearchInput.tsx +33 -0
  109. package/src/components/searchTable/components/SortButton.tsx +50 -0
  110. package/src/components/searchTable/components/TableEmptyState.tsx +22 -0
  111. package/src/components/searchTable/components/TableHeader.tsx +35 -0
  112. package/src/components/searchTable/components/TableHeaderCell.tsx +43 -0
  113. package/src/components/searchTable/components/TableRow.tsx +144 -0
  114. package/src/components/searchTable/searchTable.props.ts +56 -0
  115. package/src/components/searchTable/searchTable.tsx +187 -0
  116. package/src/components/segmented-control/segmented-control.props.ts +18 -0
  117. package/src/components/segmented-control/segmented-control.stories.tsx +63 -0
  118. package/src/components/segmented-control/segmented-control.tsx +52 -0
  119. package/src/components/select/select.props.ts +25 -0
  120. package/src/components/select/select.stories.tsx +86 -0
  121. package/src/components/select/select.tsx +150 -0
  122. package/src/components/sidebar/sidebar.props.ts +28 -0
  123. package/src/components/sidebar/sidebar.stories.tsx +117 -0
  124. package/src/components/sidebar/sidebar.tsx +313 -0
  125. package/src/components/skeleton/skeleton.props.ts +12 -0
  126. package/src/components/skeleton/skeleton.stories.tsx +30 -0
  127. package/src/components/skeleton/skeleton.tsx +45 -0
  128. package/src/components/slide/slide.props.ts +45 -0
  129. package/src/components/slide/slide.stories.tsx +121 -0
  130. package/src/components/slide/slide.tsx +109 -0
  131. package/src/components/slider/slider.props.ts +10 -0
  132. package/src/components/slider/slider.stories.tsx +30 -0
  133. package/src/components/slider/slider.tsx +49 -0
  134. package/src/components/stack/stack.props.ts +19 -0
  135. package/src/components/stack/stack.stories.tsx +79 -0
  136. package/src/components/stack/stack.tsx +79 -0
  137. package/src/components/stat-card/stat-card.props.ts +13 -0
  138. package/src/components/stat-card/stat-card.stories.tsx +41 -0
  139. package/src/components/stat-card/stat-card.tsx +44 -0
  140. package/src/components/stepper/stepper.css +26 -0
  141. package/src/components/stepper/stepper.props.ts +29 -0
  142. package/src/components/stepper/stepper.stories.tsx +155 -0
  143. package/src/components/stepper/stepper.tsx +227 -0
  144. package/src/components/table/table.props.ts +43 -0
  145. package/src/components/table/table.stories.tsx +189 -0
  146. package/src/components/table/table.tsx +376 -0
  147. package/src/components/tabs/tabs.props.ts +18 -0
  148. package/src/components/tabs/tabs.stories.tsx +32 -0
  149. package/src/components/tabs/tabs.tsx +74 -0
  150. package/src/components/text/text.props.ts +9 -0
  151. package/src/components/text/text.tsx +20 -0
  152. package/src/components/textarea/textarea.props.ts +15 -0
  153. package/src/components/textarea/textarea.stories.tsx +27 -0
  154. package/src/components/textarea/textarea.tsx +55 -0
  155. package/src/components/theme-provider/themeProvider.props.ts +28 -0
  156. package/src/components/theme-provider/themeProvider.tsx +1854 -0
  157. package/src/components/time-picker/timePicker.props.ts +16 -0
  158. package/src/components/time-picker/timePicker.stories.tsx +131 -0
  159. package/src/components/time-picker/timePicker.tsx +317 -0
  160. package/src/components/toast/toast.css +32 -0
  161. package/src/components/toast/toast.props.ts +13 -0
  162. package/src/components/toast/toast.stories.tsx +138 -0
  163. package/src/components/toast/toast.tsx +87 -0
  164. package/src/components/tooltip/tooltip.props.ts +11 -0
  165. package/src/components/tooltip/tooltip.stories.tsx +20 -0
  166. package/src/components/tooltip/tooltip.tsx +55 -0
  167. package/src/components/topbar/topbar.props.ts +21 -0
  168. package/src/components/topbar/topbar.stories.tsx +80 -0
  169. package/src/components/topbar/topbar.tsx +205 -0
  170. package/src/components/triple-filter/tripleFilter.props.ts +15 -0
  171. package/src/components/triple-filter/tripleFilter.stories.tsx +32 -0
  172. package/src/components/triple-filter/tripleFilter.tsx +50 -0
  173. package/src/hooks/useClickOutside.ts +21 -0
  174. package/src/hooks/useDebouncedSearch.ts +55 -0
  175. package/src/hooks/useEditableRow.ts +157 -0
  176. package/src/hooks/useTableState.ts +122 -0
  177. package/src/index.css +168 -0
  178. package/src/index.ts +165 -0
  179. package/src/main.tsx +9 -0
  180. package/src/showcases/DataShowcases.tsx +260 -0
  181. package/src/showcases/FeedbackShowcases.tsx +268 -0
  182. package/src/showcases/FormShowcases.tsx +1159 -0
  183. package/src/showcases/HomeShowcase.tsx +324 -0
  184. package/src/showcases/LayoutPrimitivesShowcases.tsx +569 -0
  185. package/src/showcases/NavigationShowcases.tsx +193 -0
  186. package/src/showcases/PageShowcases.tsx +207 -0
  187. package/src/showcases/ShowcaseLayout.tsx +139 -0
  188. package/src/showcases/StructureShowcases.tsx +152 -0
  189. package/src/types/badget.types.ts +37 -0
  190. package/src/types/button.types.ts +16 -0
  191. package/src/types/colors.types.ts +3 -0
  192. package/src/types/field.types.ts +103 -0
  193. package/src/types/formik.types.ts +15 -0
  194. package/src/types/input.types.ts +14 -0
  195. package/src/types/loader.types.ts +9 -0
  196. package/src/types/sizes.types.ts +1 -0
  197. package/src/types/table.types.ts +15 -0
  198. package/src/types/toast.types.ts +8 -0
  199. package/src/types/yup.types.ts +11 -0
  200. package/src/utils/color.utils.ts +99 -0
  201. package/src/utils/styles.ts +120 -0
  202. 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,11 @@
1
+ import { ReactNode } from "react";
2
+
3
+ export interface ITCheckboxProps {
4
+ checked?: boolean;
5
+ onChange?: (checked: boolean) => void;
6
+ label?: ReactNode;
7
+ disabled?: boolean;
8
+ indeterminate?: boolean;
9
+ className?: string;
10
+ name?: string;
11
+ }
@@ -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
+ }