@axzydev/axzy_ui_system 1.2.1 → 1.2.3
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.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +82 -1
- package/dist/index.css.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.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/dev.css +2 -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,313 @@
|
|
|
1
|
+
import { useTableState } from "@/hooks/useTableState";
|
|
2
|
+
import { sizeStyles, variantStyles } from "@/types/table.types";
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
import React, { useState, useEffect, useCallback, useRef } from "react";
|
|
5
|
+
import { FaCheck, FaSpinner, FaTimes } from "react-icons/fa";
|
|
6
|
+
import { MdOutlineSwapVert } from "react-icons/md";
|
|
7
|
+
import ITInput from "../input/input";
|
|
8
|
+
import ITPagination from "../pagination/pagination";
|
|
9
|
+
import ITSelect from "../select/select";
|
|
10
|
+
import { Column } from "../table/table.props";
|
|
11
|
+
import { formatCurrencyMX } from "../table/table";
|
|
12
|
+
import { ITDataTableProps } from "./dataTable.props";
|
|
13
|
+
import ITText from "@/components/text/text";
|
|
14
|
+
|
|
15
|
+
const getNestedValue = (obj: unknown, path: string) => {
|
|
16
|
+
return path.split(".").reduce((acc, part) => acc && acc[part], obj);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const EMPTY_OBJECT = {};
|
|
20
|
+
|
|
21
|
+
export default function ITDataTable<T extends Record<string, unknown>>({
|
|
22
|
+
columns,
|
|
23
|
+
fetchData,
|
|
24
|
+
debounceMs = 500,
|
|
25
|
+
externalFilters = EMPTY_OBJECT,
|
|
26
|
+
loadingIndicator,
|
|
27
|
+
fetchOnMount = true,
|
|
28
|
+
reloadTrigger,
|
|
29
|
+
containerClassName,
|
|
30
|
+
className,
|
|
31
|
+
variant = "default",
|
|
32
|
+
size = "md",
|
|
33
|
+
itemsPerPageOptions = [5, 10, 20],
|
|
34
|
+
defaultItemsPerPage = 10,
|
|
35
|
+
title,
|
|
36
|
+
}: ITDataTableProps<T>) {
|
|
37
|
+
const [data, setData] = useState<T[]>([]);
|
|
38
|
+
const [totalItems, setTotalItems] = useState(0);
|
|
39
|
+
const [isLoading, setIsLoading] = useState(fetchOnMount);
|
|
40
|
+
|
|
41
|
+
const {
|
|
42
|
+
currentPage,
|
|
43
|
+
itemsPerPage,
|
|
44
|
+
filters,
|
|
45
|
+
sortConfig,
|
|
46
|
+
totalPages,
|
|
47
|
+
goToPage,
|
|
48
|
+
handleFilterChange,
|
|
49
|
+
handleSort,
|
|
50
|
+
handleItemsPerPageChange,
|
|
51
|
+
} = useTableState({ defaultItemsPerPage });
|
|
52
|
+
|
|
53
|
+
const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
54
|
+
|
|
55
|
+
const computedTotalPages = Math.ceil(totalItems / itemsPerPage) || 1;
|
|
56
|
+
|
|
57
|
+
const performFetch = useCallback(async () => {
|
|
58
|
+
setIsLoading(true);
|
|
59
|
+
try {
|
|
60
|
+
const response = await fetchData({
|
|
61
|
+
page: currentPage,
|
|
62
|
+
limit: itemsPerPage,
|
|
63
|
+
filters: { ...filters, ...externalFilters },
|
|
64
|
+
sort: sortConfig || undefined,
|
|
65
|
+
});
|
|
66
|
+
setData(response.data || []);
|
|
67
|
+
setTotalItems(response.total || 0);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error("ITDataTable: Error fetching data", error);
|
|
70
|
+
setData([]);
|
|
71
|
+
setTotalItems(0);
|
|
72
|
+
} finally {
|
|
73
|
+
setIsLoading(false);
|
|
74
|
+
}
|
|
75
|
+
}, [currentPage, itemsPerPage, filters, sortConfig, fetchData, externalFilters]);
|
|
76
|
+
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
if (!fetchOnMount && data.length === 0 && !isLoading) return;
|
|
79
|
+
|
|
80
|
+
if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
|
|
81
|
+
|
|
82
|
+
debounceTimerRef.current = setTimeout(() => {
|
|
83
|
+
performFetch();
|
|
84
|
+
}, debounceMs);
|
|
85
|
+
|
|
86
|
+
return () => {
|
|
87
|
+
if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
|
|
88
|
+
};
|
|
89
|
+
}, [currentPage, itemsPerPage, sortConfig, filters, externalFilters, reloadTrigger, fetchOnMount, performFetch]);
|
|
90
|
+
|
|
91
|
+
const renderFilterInput = (col: Column<T>) => {
|
|
92
|
+
if (!col.filter) return null;
|
|
93
|
+
|
|
94
|
+
if (col.type === "boolean") {
|
|
95
|
+
const currentValue = filters[col.key];
|
|
96
|
+
const nextValue =
|
|
97
|
+
currentValue === undefined ? true : currentValue === true ? false : undefined;
|
|
98
|
+
|
|
99
|
+
const getToggleLabel = () => {
|
|
100
|
+
if (currentValue === undefined) return "Mostrar todos";
|
|
101
|
+
if (currentValue === true) return "Filtrar solo verdaderos";
|
|
102
|
+
return "Filtrar solo falsos";
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<button
|
|
107
|
+
className="flex items-center justify-center cursor-pointer focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 rounded-full p-1 transition-all duration-200"
|
|
108
|
+
onClick={() => handleFilterChange(col.key, nextValue)}
|
|
109
|
+
aria-label={`${getToggleLabel()} para ${col.label}`}
|
|
110
|
+
title={`${getToggleLabel()} para ${col.label}`}
|
|
111
|
+
disabled={isLoading}
|
|
112
|
+
>
|
|
113
|
+
<div className="relative w-10 h-5 bg-gray-300 rounded-full">
|
|
114
|
+
<div
|
|
115
|
+
className={clsx(
|
|
116
|
+
"absolute top-0.5 w-4 h-4 rounded-full transition-all duration-300 shadow-sm",
|
|
117
|
+
{
|
|
118
|
+
"left-0.5 bg-gray-400": currentValue === undefined,
|
|
119
|
+
"left-5 bg-slate-500": currentValue === true,
|
|
120
|
+
"left-0.5 bg-gray-500": currentValue === false,
|
|
121
|
+
}
|
|
122
|
+
)}
|
|
123
|
+
/>
|
|
124
|
+
</div>
|
|
125
|
+
</button>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (col.filter === "catalog" && col.catalogOptions) {
|
|
130
|
+
if (col.catalogOptions.loading) {
|
|
131
|
+
return <FaSpinner className="animate-spin" aria-label="Cargando opciones" title="Cargando opciones" />;
|
|
132
|
+
}
|
|
133
|
+
if (col.catalogOptions.error) {
|
|
134
|
+
return <ITText as="span" className="text-red-500 text-xs">Error cargando</ITText>;
|
|
135
|
+
}
|
|
136
|
+
return (
|
|
137
|
+
<ITSelect
|
|
138
|
+
name={`filter-${col.key}`}
|
|
139
|
+
options={[
|
|
140
|
+
{ value: "", label: "Todos" },
|
|
141
|
+
...col.catalogOptions.data.map((item) => ({
|
|
142
|
+
value: String(item.id),
|
|
143
|
+
label: item.name,
|
|
144
|
+
})),
|
|
145
|
+
]}
|
|
146
|
+
value={String(filters[col.key] || "")}
|
|
147
|
+
onChange={(e) => {
|
|
148
|
+
const value = e.target.value === "" ? undefined : e.target.value;
|
|
149
|
+
handleFilterChange(col.key, value);
|
|
150
|
+
}}
|
|
151
|
+
onBlur={() => {}}
|
|
152
|
+
className="w-full text-xs"
|
|
153
|
+
disabled={isLoading}
|
|
154
|
+
/>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<ITInput
|
|
160
|
+
name={`filter-${col.key}`}
|
|
161
|
+
className="w-full text-xs"
|
|
162
|
+
placeholder="Buscar..."
|
|
163
|
+
value={String(filters[col.key] || "")}
|
|
164
|
+
onChange={(e) => handleFilterChange(col.key, e.target.value)}
|
|
165
|
+
onBlur={() => {}}
|
|
166
|
+
disabled={isLoading}
|
|
167
|
+
/>
|
|
168
|
+
);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const renderCellContent = (col: Column<T>, row: T) => {
|
|
172
|
+
const value = getNestedValue(row, col.key);
|
|
173
|
+
if (col.render) return col.render(row);
|
|
174
|
+
|
|
175
|
+
switch (col.type) {
|
|
176
|
+
case "number":
|
|
177
|
+
return typeof value === "number" && col.currencyMX ? formatCurrencyMX(value) : value;
|
|
178
|
+
case "boolean":
|
|
179
|
+
return value ? (
|
|
180
|
+
<FaCheck className="text-green-500" aria-label="Verdadero" title="Verdadero" />
|
|
181
|
+
) : (
|
|
182
|
+
<FaTimes className="text-red-500" aria-label="Falso" title="Falso" />
|
|
183
|
+
);
|
|
184
|
+
case "actions":
|
|
185
|
+
return col.actions ? col.actions(row) : null;
|
|
186
|
+
case "catalog":
|
|
187
|
+
if (col.catalogOptions) {
|
|
188
|
+
const catalogItem = col.catalogOptions.data.find((item) => item.id === value);
|
|
189
|
+
return catalogItem?.name || value;
|
|
190
|
+
}
|
|
191
|
+
return value as React.ReactNode;
|
|
192
|
+
default:
|
|
193
|
+
return value as React.ReactNode;
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
return (
|
|
198
|
+
<div className={clsx("space-y-4 w-full relative", containerClassName)}>
|
|
199
|
+
<div className="rounded-xl shadow-sm border border-secondary-200 overflow-hidden" style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)' }}>
|
|
200
|
+
{title && (
|
|
201
|
+
<div className="px-6 py-5 border-b border-secondary-100 flex justify-between items-center" style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)' }}>
|
|
202
|
+
<ITText as="h2" className="text-xl font-bold text-secondary-900 leading-tight">{title}</ITText>
|
|
203
|
+
{isLoading && (
|
|
204
|
+
<div className="text-secondary-400">
|
|
205
|
+
{loadingIndicator || <FaSpinner className="animate-spin text-primary-500 text-xl" />}
|
|
206
|
+
</div>
|
|
207
|
+
)}
|
|
208
|
+
</div>
|
|
209
|
+
)}
|
|
210
|
+
|
|
211
|
+
<div className="overflow-x-auto relative min-h-[200px]">
|
|
212
|
+
{isLoading && (
|
|
213
|
+
<div className="absolute inset-0 z-20 flex items-center justify-center bg-white/40 backdrop-blur-[2px] transition-all duration-300">
|
|
214
|
+
<div className="flex flex-col items-center gap-3 p-6 rounded-2xl shadow-xl border border-secondary-100 animate-in fade-in zoom-in duration-300" style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)' }}>
|
|
215
|
+
{loadingIndicator || (
|
|
216
|
+
<>
|
|
217
|
+
<FaSpinner className="animate-spin text-primary-500 text-4xl" />
|
|
218
|
+
<ITText as="span" className="text-sm font-semibold text-secondary-600 animate-pulse">Cargando datos...</ITText>
|
|
219
|
+
</>
|
|
220
|
+
)}
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
)}
|
|
224
|
+
|
|
225
|
+
<table
|
|
226
|
+
className={clsx(
|
|
227
|
+
"min-w-max w-full text-sm text-left text-secondary-600 transition-opacity duration-300",
|
|
228
|
+
isLoading ? "opacity-50" : "opacity-100",
|
|
229
|
+
variantStyles[variant],
|
|
230
|
+
sizeStyles[size],
|
|
231
|
+
className
|
|
232
|
+
)}
|
|
233
|
+
>
|
|
234
|
+
<thead>
|
|
235
|
+
<tr className="bg-secondary-50 border-b border-secondary-200 text-xs uppercase tracking-wider font-semibold text-secondary-500">
|
|
236
|
+
{columns.map((col) => (
|
|
237
|
+
<th key={col.key} scope="col" className={clsx("px-4 py-4 align-top", col.className)}>
|
|
238
|
+
<div className="flex flex-col gap-3 min-w-[150px]">
|
|
239
|
+
<div className="flex items-center justify-between gap-2">
|
|
240
|
+
<ITText as="span" className="text-secondary-700 font-bold">{col.label}</ITText>
|
|
241
|
+
{col.sortable && col.type !== "actions" && (
|
|
242
|
+
<button
|
|
243
|
+
onClick={() => handleSort(col.key)}
|
|
244
|
+
disabled={isLoading}
|
|
245
|
+
className={`p-1 rounded-md transition-colors ${
|
|
246
|
+
sortConfig?.key === col.key
|
|
247
|
+
? "bg-secondary-200 text-secondary-900"
|
|
248
|
+
: "hover:bg-secondary-200 text-secondary-400 hover:text-secondary-700"
|
|
249
|
+
} disabled:opacity-50`}
|
|
250
|
+
title={`Ordenar por ${col.label}`}
|
|
251
|
+
>
|
|
252
|
+
<MdOutlineSwapVert className="w-4 h-4" aria-hidden="true" />
|
|
253
|
+
</button>
|
|
254
|
+
)}
|
|
255
|
+
</div>
|
|
256
|
+
|
|
257
|
+
<div className="w-full">{col.filter ? renderFilterInput(col) : null}</div>
|
|
258
|
+
</div>
|
|
259
|
+
</th>
|
|
260
|
+
))}
|
|
261
|
+
</tr>
|
|
262
|
+
</thead>
|
|
263
|
+
<tbody className="divide-y divide-secondary-100">
|
|
264
|
+
{data.length > 0 ? (
|
|
265
|
+
data.map((row, rowIndex) => (
|
|
266
|
+
<tr key={rowIndex} className="hover:bg-secondary-50/50 transition-colors duration-150 group">
|
|
267
|
+
{columns.map((col) => (
|
|
268
|
+
<td key={`${rowIndex}-${col.key}`} className={clsx("px-4 py-3 align-middle", col.className)}>
|
|
269
|
+
{col.type === "actions" ? (
|
|
270
|
+
<div className="flex items-center justify-center gap-2">
|
|
271
|
+
{renderCellContent(col, row) as React.ReactNode}
|
|
272
|
+
</div>
|
|
273
|
+
) : (
|
|
274
|
+
<div className="text-secondary-700 font-medium">
|
|
275
|
+
{renderCellContent(col, row) as React.ReactNode}
|
|
276
|
+
</div>
|
|
277
|
+
)}
|
|
278
|
+
</td>
|
|
279
|
+
))}
|
|
280
|
+
</tr>
|
|
281
|
+
))
|
|
282
|
+
) : (
|
|
283
|
+
<tr>
|
|
284
|
+
<td colSpan={columns.length} className="px-6 py-20 text-center">
|
|
285
|
+
{!isLoading && (
|
|
286
|
+
<div className="flex flex-col items-center justify-center text-secondary-400">
|
|
287
|
+
<ITText as="span" className="text-lg">No se encontraron resultados</ITText>
|
|
288
|
+
<ITText as="span" className="text-sm mt-1">Intenta ajustar los filtros</ITText>
|
|
289
|
+
</div>
|
|
290
|
+
)}
|
|
291
|
+
</td>
|
|
292
|
+
</tr>
|
|
293
|
+
)}
|
|
294
|
+
</tbody>
|
|
295
|
+
</table>
|
|
296
|
+
</div>
|
|
297
|
+
</div>
|
|
298
|
+
|
|
299
|
+
<div className="rounded-b-xl border-t border-secondary-200 px-6 py-4" style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)' }}>
|
|
300
|
+
<ITPagination
|
|
301
|
+
currentPage={currentPage}
|
|
302
|
+
totalPages={computedTotalPages}
|
|
303
|
+
onPageChange={goToPage}
|
|
304
|
+
color="primary"
|
|
305
|
+
itemsPerPageOptions={itemsPerPageOptions}
|
|
306
|
+
itemsPerPage={itemsPerPage}
|
|
307
|
+
onItemsPerPageChange={handleItemsPerPageChange}
|
|
308
|
+
totalItems={totalItems}
|
|
309
|
+
/>
|
|
310
|
+
</div>
|
|
311
|
+
</div>
|
|
312
|
+
);
|
|
313
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ColorsTypes } from "@/types/colors.types";
|
|
2
|
+
import { SizesTypes } from "@/types/sizes.types";
|
|
3
|
+
|
|
4
|
+
export interface ITDatePickerProps {
|
|
5
|
+
name: string;
|
|
6
|
+
value?: Date | [Date | null, Date | null];
|
|
7
|
+
onChange: (
|
|
8
|
+
event:
|
|
9
|
+
| React.ChangeEvent<HTMLInputElement>
|
|
10
|
+
| { target: { name: string; value: Date | [Date | null, Date | null] } }
|
|
11
|
+
) => void;
|
|
12
|
+
onBlur?: (
|
|
13
|
+
event:
|
|
14
|
+
| React.FocusEvent<HTMLInputElement>
|
|
15
|
+
| { target: { name: string; value: Date | [Date | null, Date | null] } }
|
|
16
|
+
) => void;
|
|
17
|
+
range?: boolean;
|
|
18
|
+
variant?: ColorsTypes;
|
|
19
|
+
size?: SizesTypes;
|
|
20
|
+
className?: string;
|
|
21
|
+
calendarClassName?: string;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
placeholder?: string;
|
|
24
|
+
label?: string;
|
|
25
|
+
required?: boolean;
|
|
26
|
+
touched?: boolean;
|
|
27
|
+
error?: string | boolean;
|
|
28
|
+
minDate?: Date;
|
|
29
|
+
maxDate?: Date;
|
|
30
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import ITDatePicker from './datePicker';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
title: 'Components/Form Elements/ITDatePicker',
|
|
7
|
+
component: ITDatePicker,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: 'padded',
|
|
10
|
+
},
|
|
11
|
+
tags: ['autodocs'],
|
|
12
|
+
argTypes: {
|
|
13
|
+
value: { control: 'date' },
|
|
14
|
+
minDate: { control: 'date' },
|
|
15
|
+
maxDate: { control: 'date' },
|
|
16
|
+
onChange: { action: 'changed' },
|
|
17
|
+
onBlur: { action: 'blurred' },
|
|
18
|
+
disabled: { control: 'boolean' },
|
|
19
|
+
error: { control: 'text' },
|
|
20
|
+
},
|
|
21
|
+
} satisfies Meta<typeof ITDatePicker>;
|
|
22
|
+
|
|
23
|
+
export default meta;
|
|
24
|
+
type Story = StoryObj<typeof meta>;
|
|
25
|
+
|
|
26
|
+
// Wrapper for controlled component state
|
|
27
|
+
const DatePickerWrapper = (args: any) => {
|
|
28
|
+
const [date, setDate] = useState<Date>(args.value || new Date());
|
|
29
|
+
|
|
30
|
+
const handleChange = (e: any) => {
|
|
31
|
+
// Handle both event types the component might emit
|
|
32
|
+
if (e.target && e.target.value) {
|
|
33
|
+
setDate(e.target.value instanceof Date ? e.target.value : new Date(e.target.value));
|
|
34
|
+
}
|
|
35
|
+
args.onChange(e);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return <ITDatePicker {...args} value={date} onChange={handleChange} />;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const Default: any = {
|
|
42
|
+
render: (args) => <DatePickerWrapper {...args} />,
|
|
43
|
+
args: {
|
|
44
|
+
name: 'birthdate',
|
|
45
|
+
label: 'Select Date',
|
|
46
|
+
placeholder: 'DD/MM/YYYY',
|
|
47
|
+
} as any,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const WithError: any = {
|
|
51
|
+
render: (args) => <DatePickerWrapper {...args} />,
|
|
52
|
+
args: {
|
|
53
|
+
name: 'error_date',
|
|
54
|
+
label: 'Invalid Date',
|
|
55
|
+
error: 'This field is required',
|
|
56
|
+
touched: true,
|
|
57
|
+
} as any,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const Disabled: any = {
|
|
61
|
+
args: {
|
|
62
|
+
name: 'disabled_date',
|
|
63
|
+
label: 'Disabled Input',
|
|
64
|
+
value: new Date(),
|
|
65
|
+
disabled: true,
|
|
66
|
+
} as any,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Wrapper for range state
|
|
70
|
+
const RangePickerWrapper = (args: any) => {
|
|
71
|
+
const [range, setRange] = useState<[Date | null, Date | null]>([null, null]);
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<ITDatePicker
|
|
75
|
+
{...args}
|
|
76
|
+
value={range}
|
|
77
|
+
onChange={(e: any) => setRange(e.target.value)}
|
|
78
|
+
/>
|
|
79
|
+
);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const Range: any = {
|
|
83
|
+
render: (args) => <RangePickerWrapper {...args} />,
|
|
84
|
+
args: {
|
|
85
|
+
range: true,
|
|
86
|
+
name: 'audit_range',
|
|
87
|
+
label: 'Select Date Range',
|
|
88
|
+
placeholder: 'DD/MM/YYYY - DD/MM/YYYY',
|
|
89
|
+
} as any,
|
|
90
|
+
};
|