@axzydev/axzy_ui_system 1.2.8 → 1.2.11
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/README.md +109 -51
- package/dist/index.cjs +141 -73
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +141 -5
- package/dist/index.d.ts +141 -5
- package/dist/index.js +141 -73
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/snippets/axzy-ui-system.code-snippets +547 -0
- package/src/App.tsx +43 -33
- package/src/components/avatar/avatar.props.ts +12 -1
- package/src/components/avatar/avatar.stories.tsx +19 -0
- package/src/components/avatar/avatar.tsx +26 -3
- package/src/components/confirm-dialog/confirm-dialog.tsx +1 -1
- package/src/components/data-table/dataTable.props.ts +10 -0
- package/src/components/data-table/dataTable.tsx +24 -13
- package/src/components/date-picker/datePicker.tsx +1 -1
- package/src/components/dialog/dialog.tsx +2 -1
- package/src/components/drawer/drawer.tsx +1 -1
- package/src/components/dropfile/dropfile.props.ts +6 -0
- package/src/components/dropfile/dropfile.tsx +51 -16
- package/src/components/layout/layout.tsx +1 -1
- package/src/components/navbar/navbar.tsx +2 -2
- package/src/components/popover/popover.tsx +1 -1
- package/src/components/search-select/search-select.tsx +4 -4
- package/src/components/select/select.tsx +6 -8
- package/src/components/sidebar/sidebar.tsx +15 -15
- package/src/components/stepper/stepper.props.ts +4 -0
- package/src/components/table/table.props.ts +21 -3
- package/src/components/table/table.tsx +24 -13
- package/src/components/theme-provider/themeProvider.props.ts +21 -3
- package/src/components/time-picker/timePicker.tsx +1 -1
- package/src/components/toast/toast.tsx +1 -1
- package/src/components/tooltip/tooltip.tsx +1 -1
- package/src/components/topbar/topbar.tsx +1 -1
- package/src/dev.css +1 -1
- package/src/hooks/useClickOutside.ts +8 -0
- package/src/hooks/useTableState.ts +5 -2
- package/src/index.css +1 -1
- package/src/showcases/GettingStartedShowcase.tsx +207 -0
- package/src/showcases/HomeShowcase.tsx +95 -3
- package/src/theme/theme.ts +21 -0
- package/src/types/field.types.ts +70 -7
package/src/App.tsx
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useState } from "react";
|
|
2
2
|
import {
|
|
3
3
|
FaCreditCard,
|
|
4
|
-
FaHome,
|
|
4
|
+
FaHome,
|
|
5
5
|
FaKeyboard,
|
|
6
6
|
FaRegBell,
|
|
7
|
-
FaSearch,
|
|
8
7
|
FaSlidersH,
|
|
9
8
|
FaTable,
|
|
10
9
|
} from "react-icons/fa";
|
|
@@ -13,6 +12,7 @@ import "./index.css";
|
|
|
13
12
|
|
|
14
13
|
// Import Showcases
|
|
15
14
|
import { HomeShowcase } from "./showcases/HomeShowcase";
|
|
15
|
+
import { GettingStartedShowcase } from "./showcases/GettingStartedShowcase";
|
|
16
16
|
import {
|
|
17
17
|
CardShowcase,
|
|
18
18
|
LayoutShowcase,
|
|
@@ -60,34 +60,37 @@ import {
|
|
|
60
60
|
ThemeProviderShowcase,
|
|
61
61
|
} from "./showcases/FeedbackShowcases";
|
|
62
62
|
|
|
63
|
+
type ViewMode = "home" | "ui-system";
|
|
64
|
+
|
|
63
65
|
function App() {
|
|
64
|
-
const [
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const [
|
|
66
|
+
const [view, setView] = useState<ViewMode>(() => {
|
|
67
|
+
const hash = window.location.hash.replace("#", "");
|
|
68
|
+
return hash.startsWith("ui-system") ? "ui-system" : "home";
|
|
69
|
+
});
|
|
70
|
+
const [showroomActive, setShowroomActive] = useState("getting-started");
|
|
71
|
+
const [searchTerm] = useState("");
|
|
72
|
+
const [subitemConnector] = useState<
|
|
69
73
|
"dot" | "|" | "none"
|
|
70
74
|
>("dot");
|
|
71
75
|
|
|
72
76
|
useEffect(() => {
|
|
73
77
|
const onHashChange = () => {
|
|
74
|
-
const
|
|
75
|
-
|
|
78
|
+
const hash = window.location.hash.replace("#", "");
|
|
79
|
+
setView(hash.startsWith("ui-system") ? "ui-system" : "home");
|
|
76
80
|
};
|
|
77
81
|
window.addEventListener("hashchange", onHashChange);
|
|
78
82
|
return () => window.removeEventListener("hashchange", onHashChange);
|
|
79
83
|
}, []);
|
|
80
84
|
|
|
81
|
-
|
|
82
|
-
window.location.hash = activeComponentId;
|
|
83
|
-
}, [activeComponentId]);
|
|
84
|
-
|
|
85
|
-
// Group definitions for the sidebar
|
|
85
|
+
// Group definitions for the sidebar (UI System showroom)
|
|
86
86
|
const categories = [
|
|
87
87
|
{
|
|
88
88
|
id: "general",
|
|
89
89
|
label: "General",
|
|
90
90
|
icon: <FaHome />,
|
|
91
|
+
subitems: [
|
|
92
|
+
{ id: "getting-started", label: "Getting Started" },
|
|
93
|
+
],
|
|
91
94
|
},
|
|
92
95
|
{
|
|
93
96
|
id: "struc",
|
|
@@ -169,8 +172,8 @@ function App() {
|
|
|
169
172
|
if (matches) {
|
|
170
173
|
return {
|
|
171
174
|
...cat,
|
|
172
|
-
isActive:
|
|
173
|
-
action: () =>
|
|
175
|
+
isActive: showroomActive === cat.id,
|
|
176
|
+
action: () => setShowroomActive(cat.id),
|
|
174
177
|
};
|
|
175
178
|
}
|
|
176
179
|
return null;
|
|
@@ -185,8 +188,8 @@ function App() {
|
|
|
185
188
|
const mappedSubitems = matchingSubitems.map((sub) => ({
|
|
186
189
|
id: sub.id,
|
|
187
190
|
label: sub.label,
|
|
188
|
-
isActive:
|
|
189
|
-
action: () =>
|
|
191
|
+
isActive: showroomActive === sub.id,
|
|
192
|
+
action: () => setShowroomActive(sub.id),
|
|
190
193
|
}));
|
|
191
194
|
|
|
192
195
|
const isAnySubitemActive = mappedSubitems.some((sub) => sub.isActive);
|
|
@@ -202,7 +205,7 @@ function App() {
|
|
|
202
205
|
if (cat.subitems) return cat.subitems.length > 0;
|
|
203
206
|
return true;
|
|
204
207
|
});
|
|
205
|
-
}, [searchTerm,
|
|
208
|
+
}, [searchTerm, showroomActive]);
|
|
206
209
|
|
|
207
210
|
const sidebarProps = {
|
|
208
211
|
navigationItems: filteredNavigationItems,
|
|
@@ -210,27 +213,26 @@ function App() {
|
|
|
210
213
|
};
|
|
211
214
|
|
|
212
215
|
const topBarProps = {
|
|
213
|
-
logoText: "AXZY
|
|
216
|
+
logoText: "AXZY UI System",
|
|
214
217
|
userMenu: {
|
|
215
218
|
userName: "Alex Dev",
|
|
216
219
|
userEmail: "alex@axzy.dev",
|
|
217
220
|
menuItems: [
|
|
218
221
|
{
|
|
219
|
-
label: "
|
|
222
|
+
label: "Ir al Portafolio",
|
|
220
223
|
onClick: () => {
|
|
221
|
-
|
|
222
|
-
setSearchTerm("");
|
|
224
|
+
window.location.hash = "home";
|
|
223
225
|
},
|
|
224
226
|
},
|
|
225
227
|
],
|
|
226
228
|
},
|
|
227
229
|
};
|
|
228
230
|
|
|
229
|
-
// Render correct component based on active navigation
|
|
231
|
+
// Render correct component based on active showroom navigation
|
|
230
232
|
const renderShowcase = () => {
|
|
231
|
-
switch (
|
|
232
|
-
case "
|
|
233
|
-
return <
|
|
233
|
+
switch (showroomActive) {
|
|
234
|
+
case "getting-started":
|
|
235
|
+
return <GettingStartedShowcase />;
|
|
234
236
|
// Structure
|
|
235
237
|
case "layout":
|
|
236
238
|
return <LayoutShowcase />;
|
|
@@ -301,17 +303,25 @@ function App() {
|
|
|
301
303
|
case "themeprovider":
|
|
302
304
|
return <ThemeProviderShowcase />;
|
|
303
305
|
default:
|
|
304
|
-
return <
|
|
306
|
+
return <GettingStartedShowcase />;
|
|
305
307
|
}
|
|
306
308
|
};
|
|
307
309
|
|
|
308
310
|
return (
|
|
309
|
-
<ITThemeProvider showFab={
|
|
310
|
-
|
|
311
|
-
<div className="
|
|
312
|
-
|
|
311
|
+
<ITThemeProvider showFab={false}>
|
|
312
|
+
{view === "home" ? (
|
|
313
|
+
<div className="min-h-screen bg-slate-50 dark:bg-slate-950 py-10 px-4 sm:px-6 lg:px-8">
|
|
314
|
+
<div className="max-w-6xl mx-auto">
|
|
315
|
+
<HomeShowcase />
|
|
316
|
+
</div>
|
|
313
317
|
</div>
|
|
314
|
-
|
|
318
|
+
) : (
|
|
319
|
+
<ITLayout sidebar={sidebarProps} topBar={topBarProps}>
|
|
320
|
+
<div className="max-w-7xl mx-auto">
|
|
321
|
+
{renderShowcase()}
|
|
322
|
+
</div>
|
|
323
|
+
</ITLayout>
|
|
324
|
+
)}
|
|
315
325
|
</ITThemeProvider>
|
|
316
326
|
);
|
|
317
327
|
}
|
|
@@ -11,7 +11,18 @@ export interface ITAvatarProps {
|
|
|
11
11
|
initials?: string;
|
|
12
12
|
/** Avatar dimensions. Valid values: `"xs"`, `"sm"`, `"md"`, `"lg"`, `"xl"`. @default "md" */
|
|
13
13
|
size?: AvatarSize;
|
|
14
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Color de fondo para el fallback de iniciales. Acepta un valor de color
|
|
16
|
+
* real (`"#8b5cf6"`, `"rgb(...)"`, `"hsl(...)"`, `"var(--...)"`) — se
|
|
17
|
+
* aplica como `style` inline y SIEMPRE se renderiza — o una clase de
|
|
18
|
+
* Tailwind (`"bg-purple-600"`) por compatibilidad con código existente.
|
|
19
|
+
* Una clase de Tailwind solo se ve si esa clase exacta ya existe en el
|
|
20
|
+
* CSS compilado que consume la app (esta librería se distribuye con un
|
|
21
|
+
* CSS estático pre-compilado, ver scripts/build-css.mjs), así que para
|
|
22
|
+
* cualquier color calculado en tiempo de ejecución (p.ej. un hash por
|
|
23
|
+
* usuario) se recomienda pasar el valor de color directamente.
|
|
24
|
+
* @default "bg-primary-600"
|
|
25
|
+
*/
|
|
15
26
|
color?: string;
|
|
16
27
|
/** Additional CSS class names for the avatar container. */
|
|
17
28
|
className?: string;
|
|
@@ -44,3 +44,22 @@ export const Sizes: Story = {
|
|
|
44
44
|
</div>
|
|
45
45
|
),
|
|
46
46
|
};
|
|
47
|
+
|
|
48
|
+
export const WithRawColor: Story = {
|
|
49
|
+
name: "Con color calculado en runtime",
|
|
50
|
+
parameters: {
|
|
51
|
+
docs: {
|
|
52
|
+
description: {
|
|
53
|
+
story:
|
|
54
|
+
"Cuando el color se calcula dinámicamente (por ejemplo, un hash por usuario o etiqueta), pasa un valor de color real en vez de una clase de Tailwind. Una clase de Tailwind arbitraria solo se ve si ya existe en el CSS pre-compilado de esta librería; un color inline siempre se aplica.",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
render: () => (
|
|
59
|
+
<div className="flex items-center gap-4">
|
|
60
|
+
{["#3b82f6", "#a855f7", "#059669", "#f59e0b", "#ec4899"].map((hex) => (
|
|
61
|
+
<ITAvatar key={hex} initials="JD" size="md" color={hex} />
|
|
62
|
+
))}
|
|
63
|
+
</div>
|
|
64
|
+
),
|
|
65
|
+
};
|
|
@@ -14,6 +14,17 @@ const DEFAULT_COLOR = "bg-primary-600";
|
|
|
14
14
|
const DEFAULT_BG = "var(--color-primary-600)";
|
|
15
15
|
const DEFAULT_SHADOW = "0 4px 14px 0 rgba(37, 99, 235, 0.35)";
|
|
16
16
|
|
|
17
|
+
// Un valor tipo "#8b5cf6", "rgb(...)", "hsl(...)" o "var(--...)" se aplica
|
|
18
|
+
// como color inline; cualquier otra cosa se trata como clase de Tailwind
|
|
19
|
+
// (comportamiento previo, sin romper a quien ya pasa "bg-purple-600" etc.).
|
|
20
|
+
// Motivo: esta librería se consume vía un CSS estático pre-compilado
|
|
21
|
+
// (ver scripts/build-css.mjs) que solo contiene las clases usadas en el
|
|
22
|
+
// código FUENTE de la librería. Si una app consumidora pasa un color de
|
|
23
|
+
// Tailwind que la librería nunca usa en ningún otro lado, esa clase no
|
|
24
|
+
// existe en el CSS entregado y el avatar se renderiza sin color visible.
|
|
25
|
+
// Un valor de color inline no tiene ese problema: siempre se aplica.
|
|
26
|
+
const isRawColorValue = (value: string) => /^#|^rgb|^hsl|^var\(/i.test(value.trim());
|
|
27
|
+
|
|
17
28
|
/**
|
|
18
29
|
* Circular avatar component with image, initials fallback, and optional badge overlay.
|
|
19
30
|
*
|
|
@@ -22,6 +33,11 @@ const DEFAULT_SHADOW = "0 4px 14px 0 rgba(37, 99, 235, 0.35)";
|
|
|
22
33
|
*
|
|
23
34
|
* @example
|
|
24
35
|
* <ITAvatar initials="JD" size="md" color="bg-purple-600" />
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* // Recomendado cuando el color se calcula en tiempo de ejecución (p.ej. un
|
|
39
|
+
* // hash por usuario/etiqueta): pasa un valor de color real, no una clase.
|
|
40
|
+
* <ITAvatar initials="JD" size="md" color="#8b5cf6" />
|
|
25
41
|
*/
|
|
26
42
|
export default function ITAvatar({
|
|
27
43
|
src,
|
|
@@ -34,17 +50,24 @@ export default function ITAvatar({
|
|
|
34
50
|
onClick,
|
|
35
51
|
}: ITAvatarProps) {
|
|
36
52
|
const { container, text } = sizeMap[size];
|
|
37
|
-
const
|
|
53
|
+
const useDefaultStyle = !color || color === DEFAULT_COLOR;
|
|
54
|
+
const useRawColorStyle = !useDefaultStyle && isRawColorValue(color);
|
|
38
55
|
|
|
39
56
|
return (
|
|
40
57
|
<div
|
|
41
58
|
className={clsx(
|
|
42
59
|
"relative inline-flex items-center justify-center rounded-full flex-shrink-0 overflow-hidden text-white font-bold tracking-wide",
|
|
43
60
|
container,
|
|
44
|
-
!
|
|
61
|
+
!useDefaultStyle && !useRawColorStyle && color,
|
|
45
62
|
className,
|
|
46
63
|
)}
|
|
47
|
-
style={
|
|
64
|
+
style={
|
|
65
|
+
useDefaultStyle
|
|
66
|
+
? { backgroundColor: DEFAULT_BG, boxShadow: DEFAULT_SHADOW }
|
|
67
|
+
: useRawColorStyle
|
|
68
|
+
? { backgroundColor: color }
|
|
69
|
+
: undefined
|
|
70
|
+
}
|
|
48
71
|
onClick={onClick}
|
|
49
72
|
role={onClick ? "button" : undefined}
|
|
50
73
|
tabIndex={onClick ? 0 : undefined}
|
|
@@ -37,7 +37,7 @@ export default function ITConfirmDialog({
|
|
|
37
37
|
if (!isOpen) return null;
|
|
38
38
|
|
|
39
39
|
return (
|
|
40
|
-
<div className="fixed inset-0 z-[
|
|
40
|
+
<div className="fixed inset-0 z-[60] flex items-center justify-center p-4">
|
|
41
41
|
<div className="absolute inset-0 bg-black/40 backdrop-blur-sm" onClick={onClose} />
|
|
42
42
|
<div
|
|
43
43
|
className={clsx(
|
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
import { ReactNode } from "react";
|
|
2
2
|
import { Column } from "../table/table.props";
|
|
3
3
|
|
|
4
|
+
/** Parameters passed to `fetchData` every time pagination, filters, or sorting change. */
|
|
4
5
|
export interface ITDataTableFetchParams {
|
|
6
|
+
/** 1-indexed current page number. */
|
|
5
7
|
page: number;
|
|
8
|
+
/** Number of rows requested per page. */
|
|
6
9
|
limit: number;
|
|
10
|
+
/** Active per-column filter values (from `Column.filter`), keyed by column `key`. */
|
|
7
11
|
filters: Record<string, string | number | boolean | Date>;
|
|
12
|
+
/** Active sort, present only when the user has clicked a sortable column header. */
|
|
8
13
|
sort?: {
|
|
14
|
+
/** Column `key` currently sorted by. */
|
|
9
15
|
key: string;
|
|
16
|
+
/** Sort direction. */
|
|
10
17
|
direction: "asc" | "desc";
|
|
11
18
|
};
|
|
12
19
|
}
|
|
13
20
|
|
|
21
|
+
/** Expected shape of the Promise returned by `fetchData`. */
|
|
14
22
|
export interface ITDataTableResponse<T> {
|
|
23
|
+
/** Rows for the requested page. */
|
|
15
24
|
data: T[];
|
|
25
|
+
/** Total row count across all pages (used to compute the paginator's total pages). */
|
|
16
26
|
total: number;
|
|
17
27
|
}
|
|
18
28
|
|
|
@@ -11,6 +11,17 @@ import { Column } from "../table/table.props";
|
|
|
11
11
|
import { formatCurrencyMX } from "../table/table";
|
|
12
12
|
import { ITDataTableProps } from "./dataTable.props";
|
|
13
13
|
import ITText from "@/components/text/text";
|
|
14
|
+
import {
|
|
15
|
+
tableActionsCell,
|
|
16
|
+
tableBody,
|
|
17
|
+
tableCell,
|
|
18
|
+
tableCellText,
|
|
19
|
+
tableContainer,
|
|
20
|
+
tableEmptyContent,
|
|
21
|
+
tableHeaderCell,
|
|
22
|
+
tableHeaderRow,
|
|
23
|
+
tableRow,
|
|
24
|
+
} from "@/utils/styles";
|
|
14
25
|
|
|
15
26
|
const getNestedValue = (obj: unknown, path: string) => {
|
|
16
27
|
return path.split(".").reduce((acc, part) => acc && acc[part], obj);
|
|
@@ -157,7 +168,7 @@ export default function ITDataTable<T extends Record<string, unknown>>({
|
|
|
157
168
|
return <FaSpinner className="animate-spin" aria-label="Cargando opciones" title="Cargando opciones" />;
|
|
158
169
|
}
|
|
159
170
|
if (col.catalogOptions.error) {
|
|
160
|
-
return <ITText as="span" className="text-
|
|
171
|
+
return <ITText as="span" className="text-danger-500 text-xs">Error cargando</ITText>;
|
|
161
172
|
}
|
|
162
173
|
return (
|
|
163
174
|
<ITSelect
|
|
@@ -203,9 +214,9 @@ export default function ITDataTable<T extends Record<string, unknown>>({
|
|
|
203
214
|
return typeof value === "number" && col.currencyMX ? formatCurrencyMX(value) : value;
|
|
204
215
|
case "boolean":
|
|
205
216
|
return value ? (
|
|
206
|
-
<FaCheck className="text-
|
|
217
|
+
<FaCheck className="text-success-500" aria-label="Verdadero" title="Verdadero" />
|
|
207
218
|
) : (
|
|
208
|
-
<FaTimes className="text-
|
|
219
|
+
<FaTimes className="text-danger-500" aria-label="Falso" title="Falso" />
|
|
209
220
|
);
|
|
210
221
|
case "actions":
|
|
211
222
|
return col.actions ? col.actions(row) : null;
|
|
@@ -253,7 +264,7 @@ export default function ITDataTable<T extends Record<string, unknown>>({
|
|
|
253
264
|
|
|
254
265
|
return (
|
|
255
266
|
<div className={clsx("space-y-4 w-full relative", containerClassName)}>
|
|
256
|
-
<div className=
|
|
267
|
+
<div className={tableContainer} style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)' }}>
|
|
257
268
|
{title && (
|
|
258
269
|
<div className="px-6 py-5 flex items-center justify-between" style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)' }}>
|
|
259
270
|
<ITText as="h2" className="text-xl font-bold text-secondary-900 leading-tight">{title}</ITText>
|
|
@@ -316,7 +327,7 @@ export default function ITDataTable<T extends Record<string, unknown>>({
|
|
|
316
327
|
))
|
|
317
328
|
) : (
|
|
318
329
|
!isLoading && (
|
|
319
|
-
<div className="
|
|
330
|
+
<div className={clsx(tableEmptyContent, "py-12")}>
|
|
320
331
|
<ITText as="span" className="text-lg">No se encontraron resultados</ITText>
|
|
321
332
|
<ITText as="span" className="text-sm mt-1">Intenta ajustar los filtros</ITText>
|
|
322
333
|
</div>
|
|
@@ -336,9 +347,9 @@ export default function ITDataTable<T extends Record<string, unknown>>({
|
|
|
336
347
|
)}
|
|
337
348
|
>
|
|
338
349
|
<thead>
|
|
339
|
-
<tr className="
|
|
350
|
+
<tr className={clsx(tableHeaderRow, "dark:text-slate-200")}>
|
|
340
351
|
{columns.map((col) => (
|
|
341
|
-
<th key={col.key} scope="col" className={
|
|
352
|
+
<th key={col.key} scope="col" className={tableHeaderCell(col.className)}>
|
|
342
353
|
<div className="flex flex-col gap-3 min-w-[150px]">
|
|
343
354
|
<div className="flex items-center justify-between gap-2">
|
|
344
355
|
<ITText as="span" className="text-slate-900 dark:text-white font-bold">{col.label}</ITText>
|
|
@@ -364,18 +375,18 @@ export default function ITDataTable<T extends Record<string, unknown>>({
|
|
|
364
375
|
))}
|
|
365
376
|
</tr>
|
|
366
377
|
</thead>
|
|
367
|
-
<tbody className="
|
|
378
|
+
<tbody className={clsx(tableBody, "dark:divide-slate-700/30")}>
|
|
368
379
|
{data.length > 0 ? (
|
|
369
380
|
data.map((row, rowIndex) => (
|
|
370
|
-
<tr key={rowIndex} className={clsx(
|
|
381
|
+
<tr key={rowIndex} className={clsx(tableRow, variant === "striped" && "odd:bg-secondary-50/40 dark:odd:bg-slate-800/20")}>
|
|
371
382
|
{columns.map((col) => (
|
|
372
|
-
<td key={`${rowIndex}-${col.key}`} className={
|
|
383
|
+
<td key={`${rowIndex}-${col.key}`} className={tableCell(col.className)}>
|
|
373
384
|
{col.type === "actions" ? (
|
|
374
|
-
<div className=
|
|
385
|
+
<div className={tableActionsCell}>
|
|
375
386
|
{renderCellContent(col, row) as React.ReactNode}
|
|
376
387
|
</div>
|
|
377
388
|
) : (
|
|
378
|
-
<div className=
|
|
389
|
+
<div className={tableCellText}>
|
|
379
390
|
{renderCellContent(col, row) as React.ReactNode}
|
|
380
391
|
</div>
|
|
381
392
|
)}
|
|
@@ -387,7 +398,7 @@ export default function ITDataTable<T extends Record<string, unknown>>({
|
|
|
387
398
|
<tr>
|
|
388
399
|
<td colSpan={columns.length} className="px-6 py-12 text-center">
|
|
389
400
|
{!isLoading && (
|
|
390
|
-
<div className=
|
|
401
|
+
<div className={tableEmptyContent}>
|
|
391
402
|
<ITText as="span" className="text-lg">No se encontraron resultados</ITText>
|
|
392
403
|
<ITText as="span" className="text-sm mt-1">Intenta ajustar los filtros</ITText>
|
|
393
404
|
</div>
|
|
@@ -67,9 +67,10 @@ export default function ITDialog({
|
|
|
67
67
|
|
|
68
68
|
const content = (
|
|
69
69
|
<div
|
|
70
|
+
data-it-dialog="true"
|
|
70
71
|
className={`fixed inset-0 flex ${
|
|
71
72
|
fullScreen ? "items-stretch" : "items-center justify-center"
|
|
72
|
-
} bg-black/50 z-[
|
|
73
|
+
} bg-black/50 z-[60]`}
|
|
73
74
|
>
|
|
74
75
|
<div
|
|
75
76
|
ref={modalRef}
|
|
@@ -41,7 +41,7 @@ export default function ITDrawer({
|
|
|
41
41
|
return (
|
|
42
42
|
<>
|
|
43
43
|
{isOpen && (
|
|
44
|
-
<div className="fixed inset-0 z-[
|
|
44
|
+
<div className="fixed inset-0 z-[50] flex">
|
|
45
45
|
<div className="absolute inset-0 bg-black/40 backdrop-blur-sm transition-opacity" />
|
|
46
46
|
<div
|
|
47
47
|
ref={panelRef}
|
|
@@ -7,6 +7,12 @@ export enum FileTypeEnum {
|
|
|
7
7
|
PNG = "image/png",
|
|
8
8
|
JPG = "image/jpg",
|
|
9
9
|
JPEG = "image/jpeg",
|
|
10
|
+
MP4 = "video/mp4",
|
|
11
|
+
MOV = "video/quicktime",
|
|
12
|
+
AVI = "video/x-msvideo",
|
|
13
|
+
MKV = "video/x-matroska",
|
|
14
|
+
VIDEO_3GPP = "video/3gpp",
|
|
15
|
+
WEBM = "video/webm",
|
|
10
16
|
}
|
|
11
17
|
|
|
12
18
|
/** Upload lifecycle status */
|
|
@@ -3,6 +3,7 @@ import { useDropzone } from "react-dropzone";
|
|
|
3
3
|
// import pdfjsLib from "@/hooks/pdf"; // Disabled as hook is missing
|
|
4
4
|
import clsx from "clsx";
|
|
5
5
|
import ITText from "@/components/text/text";
|
|
6
|
+
import ITButton from "@/components/button/button";
|
|
6
7
|
import { ITDropfileProps, FileTypeEnum, UploadStatus } from "./dropfile.props";
|
|
7
8
|
export { FileTypeEnum, UploadStatus } from "./dropfile.props";
|
|
8
9
|
|
|
@@ -94,6 +95,24 @@ const ITDropfile: React.FC<ITDropfileProps> = ({
|
|
|
94
95
|
case FileTypeEnum.JPEG:
|
|
95
96
|
accept[FileTypeEnum.JPEG] = [".jpeg", ".jpg"];
|
|
96
97
|
break;
|
|
98
|
+
case FileTypeEnum.MP4:
|
|
99
|
+
accept[FileTypeEnum.MP4] = [".mp4"];
|
|
100
|
+
break;
|
|
101
|
+
case FileTypeEnum.MOV:
|
|
102
|
+
accept[FileTypeEnum.MOV] = [".mov"];
|
|
103
|
+
break;
|
|
104
|
+
case FileTypeEnum.AVI:
|
|
105
|
+
accept[FileTypeEnum.AVI] = [".avi"];
|
|
106
|
+
break;
|
|
107
|
+
case FileTypeEnum.MKV:
|
|
108
|
+
accept[FileTypeEnum.MKV] = [".mkv"];
|
|
109
|
+
break;
|
|
110
|
+
case FileTypeEnum.VIDEO_3GPP:
|
|
111
|
+
accept[FileTypeEnum.VIDEO_3GPP] = [".3gp"];
|
|
112
|
+
break;
|
|
113
|
+
case FileTypeEnum.WEBM:
|
|
114
|
+
accept[FileTypeEnum.WEBM] = [".webm"];
|
|
115
|
+
break;
|
|
97
116
|
}
|
|
98
117
|
});
|
|
99
118
|
|
|
@@ -121,6 +140,14 @@ const ITDropfile: React.FC<ITDropfileProps> = ({
|
|
|
121
140
|
case FileTypeEnum.JPEG:
|
|
122
141
|
if (!extensions.includes("IMAGEN")) extensions.push("IMAGEN");
|
|
123
142
|
break;
|
|
143
|
+
case FileTypeEnum.MP4:
|
|
144
|
+
case FileTypeEnum.MOV:
|
|
145
|
+
case FileTypeEnum.AVI:
|
|
146
|
+
case FileTypeEnum.MKV:
|
|
147
|
+
case FileTypeEnum.VIDEO_3GPP:
|
|
148
|
+
case FileTypeEnum.WEBM:
|
|
149
|
+
if (!extensions.includes("VIDEO")) extensions.push("VIDEO");
|
|
150
|
+
break;
|
|
124
151
|
}
|
|
125
152
|
});
|
|
126
153
|
|
|
@@ -341,35 +368,43 @@ const ITDropfile: React.FC<ITDropfileProps> = ({
|
|
|
341
368
|
<div className="px-3 py-2 bg-white border-t border-gray-100 flex justify-end gap-2">
|
|
342
369
|
{!isConfirmed ? (
|
|
343
370
|
<>
|
|
344
|
-
<
|
|
371
|
+
<ITButton
|
|
345
372
|
type="button"
|
|
373
|
+
variant="outlined"
|
|
374
|
+
color="secondary"
|
|
375
|
+
size="small"
|
|
346
376
|
onClick={handleCancel}
|
|
347
|
-
className="px-3 py-1.5 text-xs font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
|
|
348
377
|
>
|
|
349
|
-
<ITText
|
|
350
|
-
</
|
|
351
|
-
<
|
|
378
|
+
<ITText>Cancelar</ITText>
|
|
379
|
+
</ITButton>
|
|
380
|
+
<ITButton
|
|
352
381
|
type="button"
|
|
382
|
+
variant="filled"
|
|
383
|
+
color="primary"
|
|
384
|
+
size="small"
|
|
353
385
|
onClick={handleConfirm}
|
|
354
|
-
className="px-3 py-1.5 text-xs font-medium text-white bg-primary-600 rounded-lg hover:bg-primary-700 shadow-sm transition-colors flex items-center gap-1"
|
|
355
386
|
>
|
|
356
|
-
<
|
|
357
|
-
|
|
358
|
-
<
|
|
359
|
-
|
|
360
|
-
|
|
387
|
+
<div className="flex items-center gap-2">
|
|
388
|
+
<ITText>Confirmar</ITText>
|
|
389
|
+
<svg className="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
390
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
|
391
|
+
</svg>
|
|
392
|
+
</div>
|
|
393
|
+
</ITButton>
|
|
361
394
|
</>
|
|
362
395
|
) : (
|
|
363
|
-
<
|
|
396
|
+
<ITButton
|
|
364
397
|
type="button"
|
|
398
|
+
variant="outlined"
|
|
399
|
+
color="danger"
|
|
400
|
+
size="small"
|
|
365
401
|
onClick={handleDelete}
|
|
366
|
-
className="px-3 py-1.5 text-xs font-medium text-danger-600 bg-danger-50 border border-danger-100 rounded-lg hover:bg-danger-100 transition-colors flex items-center gap-1"
|
|
367
402
|
>
|
|
368
403
|
<svg className="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
369
404
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
|
370
405
|
</svg>
|
|
371
|
-
<ITText
|
|
372
|
-
</
|
|
406
|
+
<ITText>Eliminar</ITText>
|
|
407
|
+
</ITButton>
|
|
373
408
|
)}
|
|
374
409
|
</div>
|
|
375
410
|
|
|
@@ -392,4 +427,4 @@ const ITDropfile: React.FC<ITDropfileProps> = ({
|
|
|
392
427
|
);
|
|
393
428
|
};
|
|
394
429
|
|
|
395
|
-
export default ITDropfile;
|
|
430
|
+
export default ITDropfile;
|
|
@@ -73,7 +73,7 @@ export default function ITLayout({
|
|
|
73
73
|
{/* MOBILE SIDEBAR PANE */}
|
|
74
74
|
{mobileSidebarOpen && (
|
|
75
75
|
<div
|
|
76
|
-
className="lg:hidden fixed inset-0 z-
|
|
76
|
+
className="lg:hidden fixed inset-0 z-[40] transition-opacity duration-300 backdrop-blur-sm bg-black/40"
|
|
77
77
|
onClick={() => setMobileSidebarOpen(false)}
|
|
78
78
|
>
|
|
79
79
|
<div
|
|
@@ -116,7 +116,7 @@ export default function ITNavbar({
|
|
|
116
116
|
{isUserMenuOpen && (
|
|
117
117
|
<div
|
|
118
118
|
ref={userMenuRef}
|
|
119
|
-
className="z-
|
|
119
|
+
className="z-[70] absolute right-0 mt-2 text-base list-none bg-white divide-y divide-gray-100 rounded-lg shadow-sm"
|
|
120
120
|
>
|
|
121
121
|
<div className="px-4 py-3">
|
|
122
122
|
<ITText as="span" className="block text-sm text-gray-900">
|
|
@@ -152,7 +152,7 @@ export default function ITNavbar({
|
|
|
152
152
|
|
|
153
153
|
<div className="flex-1 flex overflow-hidden relative">
|
|
154
154
|
{(showSidebar || showSidebarOnMobile) && (
|
|
155
|
-
<aside className="fixed inset-y-0 left-0 w-64 bg-gray-50 transform transition-transform duration-300 ease-in-out z-
|
|
155
|
+
<aside className="fixed inset-y-0 left-0 w-64 bg-gray-50 transform transition-transform duration-300 ease-in-out z-[40] shadow-lg md:static md:transform-none md:shadow-none md:border-r md:border-gray-200">
|
|
156
156
|
<div className="h-full overflow-y-auto py-4 px-3">
|
|
157
157
|
<ul className="space-y-2 font-medium">{sidebarItems}</ul>
|
|
158
158
|
</div>
|
|
@@ -47,7 +47,7 @@ export default function ITPopover({
|
|
|
47
47
|
{trigger}
|
|
48
48
|
</div>
|
|
49
49
|
{open && (
|
|
50
|
-
<div className={clsx("absolute z-[
|
|
50
|
+
<div className={clsx("absolute z-[70]", positionClasses[position])}>
|
|
51
51
|
<div className="bg-white dark:bg-slate-800 rounded-xl shadow-xl border border-slate-200 dark:border-slate-700 p-3 min-w-[160px]">
|
|
52
52
|
{children}
|
|
53
53
|
</div>
|
|
@@ -148,7 +148,7 @@ export default function ITSearchSelect({
|
|
|
148
148
|
const getInputStyle = () => {
|
|
149
149
|
const style: React.CSSProperties = {
|
|
150
150
|
backgroundColor: inputTheme.backgroundColor || "#ffffff",
|
|
151
|
-
borderColor: inputTheme.borderColor || "
|
|
151
|
+
borderColor: inputTheme.borderColor || "var(--color-secondary-300)",
|
|
152
152
|
borderRadius: inputTheme.borderRadius || "0.5rem",
|
|
153
153
|
padding: inputTheme.padding || "0.5rem 0.75rem",
|
|
154
154
|
fontSize: inputTheme.fontSize || "0.875rem",
|
|
@@ -160,8 +160,8 @@ export default function ITSearchSelect({
|
|
|
160
160
|
};
|
|
161
161
|
|
|
162
162
|
if (disabled) {
|
|
163
|
-
style.backgroundColor = inputTheme.disabled?.backgroundColor || "
|
|
164
|
-
style.borderColor = inputTheme.disabled?.borderColor || "
|
|
163
|
+
style.backgroundColor = inputTheme.disabled?.backgroundColor || "var(--color-secondary-100)";
|
|
164
|
+
style.borderColor = inputTheme.disabled?.borderColor || "var(--color-secondary-200)";
|
|
165
165
|
style.opacity = 0.7;
|
|
166
166
|
style.cursor = "not-allowed";
|
|
167
167
|
}
|
|
@@ -217,7 +217,7 @@ export default function ITSearchSelect({
|
|
|
217
217
|
|
|
218
218
|
{/* Dropdown Panel */}
|
|
219
219
|
{isOpen && (
|
|
220
|
-
<div className="absolute z-
|
|
220
|
+
<div className="absolute z-[70] w-full mt-1 bg-white dark:bg-slate-900 border border-gray-200 dark:border-slate-800 rounded-lg shadow-xl overflow-hidden animate-in fade-in zoom-in duration-200 origin-top">
|
|
221
221
|
<div className="max-h-60 overflow-y-auto">
|
|
222
222
|
{filteredOptions.length > 0 ? (
|
|
223
223
|
filteredOptions.map((option) => (
|