@axzydev/axzy_ui_system 1.2.6 → 1.2.8

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 (185) hide show
  1. package/dist/index.cjs +777 -535
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.css +1 -1
  4. package/dist/index.css.map +1 -1
  5. package/dist/index.d.cts +1470 -87
  6. package/dist/index.d.ts +1470 -87
  7. package/dist/index.js +799 -555
  8. package/dist/index.js.map +1 -1
  9. package/package.json +1 -1
  10. package/src/App.tsx +19 -54
  11. package/src/components/alert/alert.doc.mdx +48 -0
  12. package/src/components/alert/alert.props.ts +7 -0
  13. package/src/components/alert/alert.tsx +8 -0
  14. package/src/components/avatar/avatar.doc.mdx +48 -0
  15. package/src/components/avatar/avatar.props.ts +8 -0
  16. package/src/components/avatar/avatar.tsx +25 -5
  17. package/src/components/badget/badget.doc.mdx +46 -0
  18. package/src/components/badget/badget.props.ts +6 -0
  19. package/src/components/badget/badget.tsx +11 -0
  20. package/src/components/breadcrumbs/breadcrumbs.doc.mdx +57 -0
  21. package/src/components/breadcrumbs/breadcrumbs.props.ts +6 -0
  22. package/src/components/breadcrumbs/breadcrumbs.tsx +12 -0
  23. package/src/components/button/button.doc.mdx +59 -0
  24. package/src/components/button/button.props.ts +12 -0
  25. package/src/components/button/button.tsx +11 -0
  26. package/src/components/calendar/calendar.doc.mdx +75 -0
  27. package/src/components/calendar/calendar.props.ts +27 -7
  28. package/src/components/calendar/calendar.tsx +13 -0
  29. package/src/components/card/card.doc.mdx +54 -0
  30. package/src/components/card/card.props.ts +11 -0
  31. package/src/components/card/card.tsx +13 -0
  32. package/src/components/checkbox/checkbox.doc.mdx +54 -0
  33. package/src/components/checkbox/checkbox.props.ts +7 -0
  34. package/src/components/checkbox/checkbox.tsx +9 -0
  35. package/src/components/confirm-dialog/confirm-dialog.doc.mdx +60 -0
  36. package/src/components/confirm-dialog/confirm-dialog.props.ts +9 -0
  37. package/src/components/confirm-dialog/confirm-dialog.tsx +19 -0
  38. package/src/components/data-table/ITDataTable.doc.mdx +59 -0
  39. package/src/components/data-table/dataTable.props.ts +15 -0
  40. package/src/components/data-table/dataTable.tsx +178 -73
  41. package/src/components/date-picker/date-picker.doc.mdx +65 -0
  42. package/src/components/date-picker/date-picker.props.ts +17 -0
  43. package/src/components/date-picker/datePicker.tsx +31 -1
  44. package/src/components/dialog/dialog.doc.mdx +54 -0
  45. package/src/components/dialog/dialog.props.ts +7 -0
  46. package/src/components/dialog/dialog.tsx +26 -0
  47. package/src/components/divider/divider.doc.mdx +42 -0
  48. package/src/components/divider/divider.props.ts +5 -0
  49. package/src/components/divider/divider.tsx +16 -0
  50. package/src/components/drawer/drawer.doc.mdx +59 -0
  51. package/src/components/drawer/drawer.props.ts +9 -0
  52. package/src/components/drawer/drawer.tsx +20 -0
  53. package/src/components/dropfile/dropfile.doc.mdx +59 -0
  54. package/src/components/dropfile/dropfile.props.ts +41 -0
  55. package/src/components/dropfile/dropfile.tsx +21 -33
  56. package/src/components/empty-state/empty-state.doc.mdx +50 -0
  57. package/src/components/empty-state/empty-state.props.ts +5 -0
  58. package/src/components/empty-state/empty-state.tsx +16 -0
  59. package/src/components/flex/flex.doc.mdx +59 -0
  60. package/src/components/flex/flex.props.ts +13 -0
  61. package/src/components/flex/flex.tsx +15 -0
  62. package/src/components/form-builder/fieldRenderer.tsx +52 -2
  63. package/src/components/form-builder/formBuilder.doc.mdx +124 -0
  64. package/src/components/form-builder/formBuilder.props.ts +12 -2
  65. package/src/components/form-builder/formBuilder.tsx +31 -0
  66. package/src/components/form-header/form-header.doc.mdx +38 -0
  67. package/src/components/form-header/form-header.props.ts +3 -0
  68. package/src/components/form-header/form-header.stories.tsx +53 -0
  69. package/src/components/form-header/form-header.tsx +11 -0
  70. package/src/components/grid/grid.doc.mdx +54 -0
  71. package/src/components/grid/grid.props.ts +13 -0
  72. package/src/components/grid/grid.tsx +26 -2
  73. package/src/components/image/image.doc.mdx +44 -0
  74. package/src/components/image/image.props.ts +5 -0
  75. package/src/components/image/image.stories.tsx +49 -0
  76. package/src/components/image/image.tsx +19 -0
  77. package/src/components/input/input.doc.mdx +70 -0
  78. package/src/components/input/input.props.ts +34 -2
  79. package/src/components/input/input.tsx +29 -0
  80. package/src/components/layout/layout.doc.mdx +51 -0
  81. package/src/components/layout/layout.props.ts +5 -0
  82. package/src/components/layout/layout.tsx +41 -17
  83. package/src/components/loader/loader.doc.mdx +43 -0
  84. package/src/components/loader/loader.props.ts +4 -0
  85. package/src/components/loader/loader.tsx +17 -0
  86. package/src/components/navbar/navbar.doc.mdx +77 -0
  87. package/src/components/navbar/navbar.props.ts +37 -1
  88. package/src/components/navbar/navbar.stories.tsx +84 -0
  89. package/src/components/navbar/navbar.tsx +78 -46
  90. package/src/components/page/page.doc.mdx +80 -0
  91. package/src/components/page/page.props.ts +25 -0
  92. package/src/components/page/page.tsx +35 -7
  93. package/src/components/page-header/page-header.doc.mdx +54 -0
  94. package/src/components/page-header/page-header.props.ts +11 -0
  95. package/src/components/page-header/page-header.stories.tsx +14 -0
  96. package/src/components/page-header/page-header.tsx +50 -15
  97. package/src/components/pagination/pagination.doc.mdx +62 -0
  98. package/src/components/pagination/pagination.props.ts +11 -32
  99. package/src/components/pagination/pagination.tsx +17 -0
  100. package/src/components/popover/popover.doc.mdx +53 -0
  101. package/src/components/popover/popover.props.ts +8 -0
  102. package/src/components/popover/popover.tsx +13 -0
  103. package/src/components/progress/progress.doc.mdx +48 -0
  104. package/src/components/progress/progress.props.ts +8 -0
  105. package/src/components/progress/progress.tsx +9 -0
  106. package/src/components/radio/radio.doc.mdx +62 -0
  107. package/src/components/radio/radio.props.ts +11 -0
  108. package/src/components/radio/radio.tsx +18 -0
  109. package/src/components/search-select/search-select.doc.mdx +72 -0
  110. package/src/components/search-select/search-select.props.ts +23 -18
  111. package/src/components/search-select/search-select.tsx +27 -2
  112. package/src/components/searchTable/searchTable.doc.mdx +79 -0
  113. package/src/components/searchTable/searchTable.props.ts +42 -3
  114. package/src/components/searchTable/searchTable.stories.tsx +74 -0
  115. package/src/components/searchTable/searchTable.tsx +23 -0
  116. package/src/components/segmented-control/segmented-control.doc.mdx +60 -0
  117. package/src/components/segmented-control/segmented-control.props.ts +12 -0
  118. package/src/components/segmented-control/segmented-control.tsx +15 -0
  119. package/src/components/select/select.doc.mdx +75 -0
  120. package/src/components/select/select.props.ts +22 -0
  121. package/src/components/select/select.tsx +2 -5
  122. package/src/components/sidebar/sidebar.doc.mdx +86 -0
  123. package/src/components/sidebar/sidebar.props.ts +22 -0
  124. package/src/components/sidebar/sidebar.tsx +22 -0
  125. package/src/components/skeleton/skeleton.doc.mdx +43 -0
  126. package/src/components/skeleton/skeleton.props.ts +8 -0
  127. package/src/components/skeleton/skeleton.tsx +10 -0
  128. package/src/components/slide/slide.doc.mdx +46 -0
  129. package/src/components/slide/slide.props.ts +11 -24
  130. package/src/components/slide/slide.tsx +10 -1
  131. package/src/components/slider/slider.doc.mdx +49 -0
  132. package/src/components/slider/slider.props.ts +9 -0
  133. package/src/components/slider/slider.tsx +15 -0
  134. package/src/components/stack/stack.doc.mdx +126 -0
  135. package/src/components/stack/stack.props.ts +18 -0
  136. package/src/components/stack/stack.stories.tsx +221 -16
  137. package/src/components/stack/stack.tsx +21 -0
  138. package/src/components/stat-card/stat-card.doc.mdx +48 -0
  139. package/src/components/stat-card/stat-card.props.ts +9 -0
  140. package/src/components/stat-card/stat-card.tsx +10 -0
  141. package/src/components/stepper/stepper.doc.mdx +54 -0
  142. package/src/components/stepper/stepper.props.ts +11 -1
  143. package/src/components/stepper/stepper.tsx +15 -0
  144. package/src/components/table/table.doc.mdx +58 -0
  145. package/src/components/table/table.props.ts +17 -0
  146. package/src/components/table/table.tsx +199 -84
  147. package/src/components/tabs/tabs.doc.mdx +52 -0
  148. package/src/components/tabs/tabs.props.ts +11 -0
  149. package/src/components/tabs/tabs.tsx +14 -0
  150. package/src/components/text/text.doc.mdx +41 -0
  151. package/src/components/text/text.props.ts +5 -0
  152. package/src/components/text/text.stories.tsx +67 -0
  153. package/src/components/text/text.tsx +10 -0
  154. package/src/components/textarea/textarea.doc.mdx +64 -0
  155. package/src/components/textarea/textarea.props.ts +11 -0
  156. package/src/components/textarea/textarea.tsx +16 -0
  157. package/src/components/theme-provider/themeProvider.doc.mdx +75 -0
  158. package/src/components/theme-provider/themeProvider.props.ts +3 -0
  159. package/src/components/theme-provider/themeProvider.stories.tsx +59 -0
  160. package/src/components/theme-provider/themeProvider.tsx +14 -0
  161. package/src/components/time-picker/timePicker.doc.mdx +68 -0
  162. package/src/components/time-picker/timePicker.props.ts +14 -0
  163. package/src/components/time-picker/timePicker.tsx +22 -1
  164. package/src/components/toast/toast.doc.mdx +50 -0
  165. package/src/components/toast/toast.props.ts +5 -0
  166. package/src/components/toast/toast.tsx +21 -0
  167. package/src/components/tooltip/tooltip.doc.mdx +47 -0
  168. package/src/components/tooltip/tooltip.props.ts +6 -0
  169. package/src/components/tooltip/tooltip.tsx +19 -0
  170. package/src/components/topbar/topbar.doc.mdx +69 -0
  171. package/src/components/topbar/topbar.props.ts +15 -0
  172. package/src/components/topbar/topbar.tsx +38 -0
  173. package/src/components/triple-filter/tripleFilter.doc.mdx +62 -0
  174. package/src/components/triple-filter/tripleFilter.props.ts +7 -0
  175. package/src/components/triple-filter/tripleFilter.tsx +32 -0
  176. package/src/index.css +17 -0
  177. package/src/showcases/DataShowcases.tsx +529 -45
  178. package/src/showcases/FormShowcases.tsx +399 -29
  179. package/src/showcases/HomeShowcase.tsx +252 -272
  180. package/src/showcases/LayoutPrimitivesShowcases.tsx +1224 -248
  181. package/src/showcases/PageShowcases.tsx +868 -151
  182. package/src/showcases/ShowcaseLayout.tsx +237 -73
  183. package/src/showcases/StructureShowcases.tsx +1039 -97
  184. package/src/types/table.types.ts +1 -1
  185. package/src/utils/styles.ts +2 -1
@@ -3,6 +3,11 @@ import {
3
3
  ITTable,
4
4
  ITDataTable,
5
5
  ITBadget,
6
+ ITAvatar,
7
+ ITButton,
8
+ ITFlex,
9
+ ITStack,
10
+ ITText,
6
11
  ITImage,
7
12
  ITInput,
8
13
  ITSelect,
@@ -12,49 +17,174 @@ import {
12
17
  } from "../index";
13
18
  import { ShowcaseLayout } from "./ShowcaseLayout";
14
19
 
20
+ const TABLE_DATA: Record<string, unknown>[] = [
21
+ { id: 1, name: "Sofía Castillo", email: "sofia@axzy.dev", role: "Admin", status: "active", lastLogin: "2026-07-18T09:30:00", sales: 28400 },
22
+ { id: 2, name: "Daniela Klein", email: "daniela@axzy.dev", role: "Editor", status: "active", lastLogin: "2026-07-17T14:22:00", sales: 15200 },
23
+ { id: 3, name: "Mariana Reyes", email: "mariana@axzy.dev", role: "Viewer", status: "inactive", lastLogin: "2026-07-10T11:00:00", sales: 3200 },
24
+ { id: 4, name: "Camila Torres", email: "camila@axzy.dev", role: "Admin", status: "active", lastLogin: "2026-07-18T08:15:00", sales: 42100 },
25
+ { id: 5, name: "Valentina Méndez", email: "valentina@axzy.dev", role: "Editor", status: "active", lastLogin: "2026-07-16T19:45:00", sales: 9800 },
26
+ { id: 6, name: "Luciana Rivas", email: "luciana@axzy.dev", role: "Viewer", status: "inactive", lastLogin: "2026-06-28T16:30:00", sales: 1500 },
27
+ { id: 7, name: "Isabella Cruz", email: "isabella@axzy.dev", role: "Editor", status: "active", lastLogin: "2026-07-18T10:05:00", sales: 20300 },
28
+ { id: 8, name: "Gabriela Vargas", email: "gabriela@axzy.dev", role: "Admin", status: "active", lastLogin: "2026-07-15T22:10:00", sales: 35600 },
29
+ { id: 9, name: "Ana López", email: "ana@axzy.dev", role: "Editor", status: "pending", lastLogin: "2026-07-14T11:20:00", sales: 7500 },
30
+ { id: 10, name: "Carla Jiménez", email: "carla@axzy.dev", role: "Viewer", status: "active", lastLogin: "2026-07-13T16:45:00", sales: 4100 },
31
+ { id: 11, name: "Luisa Fernández", email: "luisa@axzy.dev", role: "Admin", status: "active", lastLogin: "2026-07-12T08:00:00", sales: 51200 },
32
+ { id: 12, name: "Renata Morales", email: "renata@axzy.dev", role: "Editor", status: "inactive", lastLogin: "2026-06-30T12:15:00", sales: 6800 },
33
+ { id: 13, name: "Paola Herrera", email: "paola@axzy.dev", role: "Viewer", status: "pending", lastLogin: "2026-07-11T09:30:00", sales: 900 },
34
+ { id: 14, name: "Ximena Ríos", email: "ximena@axzy.dev", role: "Editor", status: "active", lastLogin: "2026-07-17T15:50:00", sales: 16700 },
35
+ { id: 15, name: "Florencia Díaz", email: "florencia@axzy.dev", role: "Admin", status: "active", lastLogin: "2026-07-18T07:15:00", sales: 48900 },
36
+ ];
37
+
38
+ const nameColumn = {
39
+ key: "name",
40
+ label: "Name",
41
+ type: "string" as const,
42
+ sortable: true,
43
+ filter: true,
44
+ render: (row: Record<string, unknown>) => {
45
+ const initials = (row.name as string).split(" ").map((w: string) => w[0]).join("");
46
+ return (
47
+ <div className="flex items-center gap-2.5">
48
+ <ITAvatar initials={initials} size="sm" />
49
+ <div>
50
+ <p className="font-semibold text-slate-800 dark:text-white text-sm leading-tight">{row.name as string}</p>
51
+ <p className="text-[10px] text-slate-400">{row.email as string}</p>
52
+ </div>
53
+ </div>
54
+ );
55
+ },
56
+ };
57
+
58
+ const statusColor = (s: string) =>
59
+ s === "active" ? "bg-emerald-100 text-emerald-700 dark:bg-emerald-950/30 dark:text-emerald-300" :
60
+ s === "pending" ? "bg-amber-100 text-amber-700 dark:bg-amber-950/30 dark:text-amber-300" :
61
+ "bg-slate-100 text-slate-600 dark:bg-slate-800 dark:text-slate-400";
62
+
63
+ const roleColor = (r: string) =>
64
+ r === "Admin" ? "bg-primary-100 text-primary-700 dark:bg-primary-950/40 dark:text-primary-300" :
65
+ r === "Editor" ? "bg-blue-100 text-blue-700 dark:bg-blue-950/40 dark:text-blue-300" :
66
+ "bg-slate-100 text-slate-600 dark:bg-slate-800 dark:text-slate-400";
67
+
15
68
  // 1. ITTable Showcase
16
69
  export const TableShowcase = () => {
17
70
  const [variant, setVariant] = useState<any>("default");
18
- const [size, setSize] = useState<any>("md");
71
+ const [size, setSize] = useState<any>("sm");
72
+ const [useCustomCard, setUseCustomCard] = useState(true);
73
+
74
+ const customCard = (row: Record<string, unknown>) => {
75
+ const initials = (row.name as string).split(" ").map((w: string) => w[0]).join("");
76
+ const status = row.status as string;
77
+ const role = row.role as string;
78
+ const date = new Date(row.lastLogin as string);
79
+ return (
80
+ <div className="group bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg hover:border-slate-300 dark:hover:border-slate-600">
81
+ <div className="flex items-start justify-between mb-3">
82
+ <div className="flex items-center gap-3">
83
+ <ITAvatar initials={initials} size="md" />
84
+ <div>
85
+ <p className="font-bold text-slate-800 dark:text-white text-sm">{row.name as string}</p>
86
+ <p className="text-xs text-slate-400">{row.email as string}</p>
87
+ </div>
88
+ </div>
89
+ <span className={`px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider rounded-md ${statusColor(status)}`}>
90
+ {status}
91
+ </span>
92
+ </div>
93
+ <div className="flex items-center gap-3 text-xs text-slate-500 mb-3">
94
+ <span className={`px-2 py-0.5 rounded-md font-semibold ${roleColor(role)}`}>{role}</span>
95
+ <span>Last login: {date.toLocaleDateString("en-US", { month: "short", day: "numeric" })}</span>
96
+ </div>
97
+ <div className="flex items-center justify-between pt-3 border-t border-slate-100 dark:border-slate-700">
98
+ <span className="text-xs text-slate-400">Sales: <strong className="text-slate-700 dark:text-slate-200">${(row.sales as number).toLocaleString()}</strong></span>
99
+ <div className="flex gap-1.5 opacity-0 group-hover:opacity-100 transition-opacity">
100
+ <button className="p-1.5 rounded-md hover:bg-slate-100 dark:hover:bg-slate-700 text-slate-400 hover:text-primary-600 transition-colors">
101
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
102
+ </button>
103
+ <button className="p-1.5 rounded-md hover:bg-slate-100 dark:hover:bg-slate-700 text-slate-400 hover:text-blue-600 transition-colors">
104
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
105
+ </button>
106
+ </div>
107
+ </div>
108
+ </div>
109
+ );
110
+ };
19
111
 
20
112
  const columns: any = [
21
- { key: "id", label: "ID", type: "number" },
22
- { key: "name", label: "Nombre", type: "string" },
23
- { key: "role", label: "Rol", type: "string" },
113
+ nameColumn,
114
+ { key: "email", label: "Email", type: "string", sortable: true, filter: true, className: "hidden lg:table-cell" },
115
+ {
116
+ key: "role",
117
+ label: "Role",
118
+ type: "string",
119
+ sortable: true,
120
+ filter: true,
121
+ render: (row: any) => (
122
+ <span className={`px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider rounded-md ${roleColor(row.role)}`}>
123
+ {row.role}
124
+ </span>
125
+ )
126
+ },
24
127
  {
25
128
  key: "status",
26
- label: "Estado",
27
- type: "actions",
129
+ label: "Status",
130
+ type: "string",
131
+ sortable: true,
132
+ filter: true,
28
133
  render: (row: any) => (
29
- <ITBadget
30
- label={row.status ? "Conectado" : "Inactivo"}
31
- color={row.status ? "success" : "danger"}
32
- size="small"
33
- />
134
+ <span className={`px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider rounded-md ${statusColor(row.status)}`}>
135
+ {row.status}
136
+ </span>
137
+ )
138
+ },
139
+ {
140
+ key: "sales",
141
+ label: "Sales",
142
+ type: "number",
143
+ sortable: true,
144
+ render: (row: any) => <span className="font-mono font-semibold text-slate-800 dark:text-white">${(row.sales as number).toLocaleString()}</span>
145
+ },
146
+ {
147
+ key: "actions",
148
+ label: "",
149
+ type: "actions",
150
+ actions: (row: any) => (
151
+ <div className="flex gap-1">
152
+ <button className="p-1.5 rounded-md hover:bg-slate-100 dark:hover:bg-slate-700 text-slate-400 hover:text-primary-600 transition-colors" title="View">
153
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
154
+ </button>
155
+ <button className="p-1.5 rounded-md hover:bg-slate-100 dark:hover:bg-slate-700 text-slate-400 hover:text-blue-600 transition-colors" title="Edit">
156
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
157
+ </button>
158
+ </div>
34
159
  )
35
160
  }
36
161
  ];
37
162
 
38
- const data = [
39
- { id: 101, name: "Esteban Dido", role: "Auditor", status: true },
40
- { id: 102, name: "Elsa Pato", role: "Administrador", status: true },
41
- { id: 103, name: "Aquiles Baeza", role: "Operador", status: false }
42
- ];
43
-
44
- const code = `<ITTable\n columns={columns}\n data={data}\n variant="${variant}"\n size="${size}"\n/>`;
163
+ const code = `<ITTable
164
+ columns={columns}
165
+ data={data}
166
+ title="Team members"
167
+ variant="${variant}"
168
+ size="${size}"
169
+ defaultItemsPerPage={5}
170
+ renderCard={(row) => <CustomCard row={row} />}
171
+ />`;
45
172
 
46
173
  return (
47
174
  <ShowcaseLayout
48
175
  title="ITTable"
49
- description="Base de renderizado de tablas estructuradas con soporte de alineamiento y tipado estricto."
176
+ description="Tabla con vista responsive Table / Cards, paginación, filtros y template de card personalizable."
50
177
  code={code}
51
178
  demo={
52
- <div className="w-full bg-white dark:bg-slate-900 rounded-xl overflow-hidden shadow-sm">
179
+ <div className="w-full bg-white dark:bg-slate-900 rounded-xl overflow-hidden shadow-sm">
53
180
  <ITTable
54
181
  columns={columns}
55
- data={data}
182
+ data={TABLE_DATA}
183
+ title="Team members"
56
184
  variant={variant}
57
185
  size={size}
186
+ defaultItemsPerPage={5}
187
+ renderCard={useCustomCard ? customCard : undefined}
58
188
  />
59
189
  </div>
60
190
  }
@@ -62,19 +192,19 @@ export const TableShowcase = () => {
62
192
  <>
63
193
  <ITSelect
64
194
  name="var_ctrl"
65
- label="Variante Estilo"
195
+ label="Table variant"
66
196
  value={variant}
67
197
  onChange={(e: any) => setVariant(e.target.value)}
68
198
  options={[
69
199
  { label: "Default", value: "default" },
70
- { label: "Striped (Cebra)", value: "striped" },
71
- { label: "Bordered (Bordes)", value: "bordered" },
72
- { label: "Clean (Limpio)", value: "clean" }
200
+ { label: "Striped", value: "striped" },
201
+ { label: "Bordered", value: "bordered" },
202
+ { label: "Clean", value: "clean" }
73
203
  ]}
74
204
  />
75
205
  <ITSelect
76
206
  name="sz_ctrl"
77
- label="Tamaño Filas"
207
+ label="Row size"
78
208
  value={size}
79
209
  onChange={(e: any) => setSize(e.target.value)}
80
210
  options={[
@@ -83,30 +213,169 @@ export const TableShowcase = () => {
83
213
  { label: "Large (lg)", value: "lg" }
84
214
  ]}
85
215
  />
216
+ <div className="flex items-center justify-between pt-2">
217
+ <span className="text-sm font-semibold text-gray-700 dark:text-slate-300">Custom card template</span>
218
+ <ITSlideToggle isOn={useCustomCard} onToggle={setUseCustomCard} size="sm" />
219
+ </div>
220
+ {!useCustomCard && (
221
+ <div className="p-2 bg-slate-100 dark:bg-slate-800 rounded text-xs text-slate-500">
222
+ Default card active — shows all columns as label-value pairs
223
+ </div>
224
+ )}
86
225
  </>
87
226
  }
227
+ gallery={
228
+ <div className="space-y-6">
229
+ <div>
230
+ <h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">Custom cards with hover</h4>
231
+ <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
232
+ {TABLE_DATA.slice(0, 4).map((row, i) => {
233
+ const initials = (row.name as string).split(" ").map((w: string) => w[0]).join("");
234
+ const status = row.status as string;
235
+ const role = row.role as string;
236
+ const date = new Date(row.lastLogin as string);
237
+ return (
238
+ <div key={i} className="group bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg hover:border-slate-300 dark:hover:border-slate-600 cursor-pointer">
239
+ <div className="flex items-start justify-between mb-3">
240
+ <div className="flex items-center gap-3">
241
+ <ITAvatar initials={initials} size="md" />
242
+ <div>
243
+ <p className="font-bold text-slate-800 dark:text-white text-sm">{row.name as string}</p>
244
+ <p className="text-xs text-slate-400">{row.email as string}</p>
245
+ </div>
246
+ </div>
247
+ <span className={`px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider rounded-md ${statusColor(status)}`}>{status}</span>
248
+ </div>
249
+ <div className="flex items-center gap-3 text-xs text-slate-500 mb-3">
250
+ <span className={`px-2 py-0.5 rounded-md font-semibold ${roleColor(role)}`}>{role}</span>
251
+ <span>Login: {date.toLocaleDateString("en-US", { month: "short", day: "numeric" })}</span>
252
+ </div>
253
+ <div className="flex items-center justify-between pt-3 border-t border-slate-100 dark:border-slate-700">
254
+ <span className="text-xs text-slate-400">Sales: <strong className="text-slate-700 dark:text-slate-200">${(row.sales as number).toLocaleString()}</strong></span>
255
+ <div className="flex gap-1.5 opacity-0 group-hover:opacity-100 transition-opacity">
256
+ <button className="p-1.5 rounded-md hover:bg-slate-100 dark:hover:bg-slate-700 text-slate-400 hover:text-primary-600"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg></button>
257
+ <button className="p-1.5 rounded-md hover:bg-slate-100 dark:hover:bg-slate-700 text-slate-400 hover:text-blue-600"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg></button>
258
+ </div>
259
+ </div>
260
+ </div>
261
+ );
262
+ })}
263
+ </div>
264
+ </div>
265
+
266
+ <div>
267
+ <h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">ITTable with default cards</h4>
268
+ <div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden">
269
+ <ITTable
270
+ columns={columns}
271
+ data={TABLE_DATA.slice(0, 6)}
272
+ size="sm"
273
+ defaultItemsPerPage={6}
274
+ defaultView="cards"
275
+ />
276
+ </div>
277
+ </div>
278
+
279
+ <div>
280
+ <h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">ITTable with custom cards + pagination</h4>
281
+ <div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden">
282
+ <ITTable
283
+ columns={columns}
284
+ data={TABLE_DATA}
285
+ size="sm"
286
+ title="All members"
287
+ defaultItemsPerPage={4}
288
+ renderCard={(row: any) => {
289
+ const initials = (row.name as string).split(" ").map((w: string) => w[0]).join("");
290
+ return (
291
+ <div className="group bg-white dark:bg-slate-800 rounded-lg border border-slate-200 dark:border-slate-700 p-3 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md hover:border-slate-300 dark:hover:border-slate-600 cursor-pointer">
292
+ <div className="flex items-center gap-3 mb-2">
293
+ <ITAvatar initials={initials} size="sm" />
294
+ <div className="flex-1 min-w-0">
295
+ <p className="font-semibold text-slate-800 dark:text-white text-sm truncate">{row.name as string}</p>
296
+ <p className="text-[11px] text-slate-400 truncate">{row.email as string}</p>
297
+ </div>
298
+ <span className={`px-2 py-0.5 text-[9px] font-bold uppercase tracking-wider rounded-md ${statusColor(row.status as string)}`}>{row.status as string}</span>
299
+ </div>
300
+ <div className="flex items-center justify-between text-xs text-slate-500">
301
+ <span className={`px-1.5 py-0.5 rounded font-semibold ${roleColor(row.role as string)}`}>{row.role as string}</span>
302
+ <span>${(row.sales as number).toLocaleString()}</span>
303
+ </div>
304
+ </div>
305
+ );
306
+ }}
307
+ />
308
+ </div>
309
+ </div>
310
+ </div>
311
+ }
88
312
  />
89
313
  );
90
314
  };
91
315
 
92
316
  // 2. ITDataTable Showcase
93
317
  export const DataTableShowcase = () => {
318
+ const [useCustomCard, setUseCustomCard] = useState(true);
319
+ const [apiState, setApiState] = useState<"normal" | "loading" | "empty" | "error">("normal");
320
+ const [reloadKey, setReloadKey] = useState(0);
321
+ const [showVBorder, setShowVBorder] = useState(true);
322
+ const [borderStyle, setBorderStyle] = useState<"default" | "blue" | "rose" | "none">("default");
323
+
94
324
  const userColumns = useMemo(() => [
95
325
  { key: "id", label: "ID", type: "number" as const },
96
- { key: "name", label: "Nombre", type: "string" as const, filter: true },
97
- { key: "role", label: "Rol", type: "string" as const, filter: true },
98
- { key: "active", label: "Activo", type: "boolean" as const, filter: true }
326
+ { key: "name", label: "Name", type: "string" as const, filter: true, sortable: true,
327
+ render: (row: any) => (
328
+ <div className="flex items-center gap-2.5">
329
+ <ITAvatar initials={(row.name as string).split(" ").map((w: string) => w[0]).join("")} size="sm" />
330
+ <span className="font-semibold text-slate-800 dark:text-white">{row.name}</span>
331
+ </div>
332
+ )
333
+ },
334
+ {
335
+ key: "role",
336
+ label: "Role",
337
+ type: "string" as const,
338
+ filter: true,
339
+ sortable: true,
340
+ render: (row: any) => (
341
+ <span className={`px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider rounded-md ${
342
+ row.role === "Admin" ? "bg-primary-100 text-primary-700 dark:bg-primary-950/40 dark:text-primary-300" :
343
+ row.role === "Editor" ? "bg-blue-100 text-blue-700 dark:bg-blue-950/40 dark:text-blue-300" :
344
+ "bg-slate-100 text-slate-600 dark:bg-slate-800 dark:text-slate-400"
345
+ }`}>{row.role}</span>
346
+ )
347
+ },
348
+ { key: "active", label: "Active", type: "boolean" as const, filter: true }
349
+ ], []);
350
+
351
+ const MOCK_USERS = useMemo(() => [
352
+ { id: 1, name: "Sofía Castillo", role: "Admin", active: true },
353
+ { id: 2, name: "Daniela Klein", role: "Editor", active: true },
354
+ { id: 3, name: "Mariana Reyes", role: "Viewer", active: false },
355
+ { id: 4, name: "Camila Torres", role: "Admin", active: true },
356
+ { id: 5, name: "Valentina Méndez", role: "Editor", active: true },
357
+ { id: 6, name: "Luciana Rivas", role: "Viewer", active: false },
358
+ { id: 7, name: "Isabella Cruz", role: "Editor", active: true },
359
+ { id: 8, name: "Gabriela Vargas", role: "Admin", active: true },
360
+ { id: 9, name: "Ana López", role: "Editor", active: false },
361
+ { id: 10, name: "Carla Jiménez", role: "Viewer", active: true },
362
+ { id: 11, name: "Luisa Fernández", role: "Admin", active: true },
363
+ { id: 12, name: "Renata Morales", role: "Viewer", active: false },
99
364
  ], []);
100
365
 
101
366
  const fetchData = useCallback(async (params: ITDataTableFetchParams): Promise<ITDataTableResponse<any>> => {
102
- await new Promise(resolve => setTimeout(resolve, 600));
103
- let mockData = [
104
- { id: 1, name: "Juan Pérez", role: "Administrador", active: true },
105
- { id: 2, name: "María García", role: "Usuario", active: true },
106
- { id: 3, name: "Pedro López", role: "Usuario", active: false },
107
- { id: 4, name: "Ana Torres", role: "Editor", active: true },
108
- { id: 5, name: "Luis Gómez", role: "Administrador", active: false }
109
- ];
367
+ const delay = apiState === "loading" ? 8000 : 400;
368
+ await new Promise(resolve => setTimeout(resolve, delay));
369
+
370
+ if (apiState === "error") {
371
+ throw new Error("Connection lost server is not responding");
372
+ }
373
+
374
+ if (apiState === "empty") {
375
+ return { data: [], total: 0 };
376
+ }
377
+
378
+ let mockData = [...MOCK_USERS];
110
379
 
111
380
  if (params.filters) {
112
381
  Object.keys(params.filters).forEach(key => {
@@ -119,32 +388,188 @@ export const DataTableShowcase = () => {
119
388
  });
120
389
  }
121
390
 
391
+ if (params.sort) {
392
+ const { key, direction } = params.sort;
393
+ mockData.sort((a: any, b: any) => {
394
+ const av = a[key], bv = b[key];
395
+ if (typeof av === "string") return direction === "asc" ? av.localeCompare(bv) : bv.localeCompare(av);
396
+ return direction === "asc" ? av - bv : bv - av;
397
+ });
398
+ }
399
+
400
+ const start = (params.page - 1) * params.limit;
401
+ const paged = mockData.slice(start, start + params.limit);
402
+
122
403
  return {
123
- data: mockData,
404
+ data: paged,
124
405
  total: mockData.length
125
406
  };
126
- }, []);
407
+ }, [apiState, MOCK_USERS]);
408
+
409
+ const handleStateChange = (s: typeof apiState) => {
410
+ setApiState(s);
411
+ setReloadKey(k => k + 1);
412
+ };
127
413
 
128
- const code = `<ITDataTable\n columns={[\n { key: 'id', label: 'ID', type: 'number' },\n { key: 'name', label: 'Nombre', type: 'string', filter: true }\n ]}\n fetchData={api.fetchUsers}\n/>`;
414
+ const code = `<ITDataTable
415
+ columns={columns}
416
+ fetchData={api.fetchUsers}
417
+ title="Users"
418
+ defaultItemsPerPage={5}
419
+ showVerticalBorder={${showVBorder}}${borderStyle !== "default" ? `\n verticalBorderClassname="${borderStyle === "blue" ? "[&_th]:border-blue-200 [&_td]:border-blue-100" : "[&_th]:border-rose-200 [&_td]:border-rose-100"}"` : ""}
420
+ renderCard={(row) => <CustomCard row={row} />}
421
+ />`;
422
+
423
+ const borderClass = borderStyle === "default" ? undefined : borderStyle === "blue" ? "[&_th]:border-blue-200 dark:[&_th]:border-blue-800/30 [&_td]:border-blue-100 dark:[&_td]:border-blue-800/20" : borderStyle === "rose" ? "[&_th]:border-rose-200 dark:[&_th]:border-rose-800/30 [&_td]:border-rose-100 dark:[&_td]:border-rose-800/20" : undefined;
129
424
 
130
425
  return (
131
426
  <ShowcaseLayout
132
427
  title="ITDataTable"
133
- description="Tabla de datos auto-suficiente con carga dinámica, paginación integrada y filtros avanzados."
428
+ description="Tabla auto-suficiente con carga dinámica, filtros, paginación y vista responsive Cards/Table."
134
429
  code={code}
135
430
  demo={
136
431
  <div className="w-full bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-xl overflow-hidden shadow-md">
137
432
  <ITDataTable
433
+ key={reloadKey}
138
434
  columns={userColumns}
139
435
  fetchData={fetchData}
436
+ title={apiState === "error" ? "Connection error" : apiState === "empty" ? "No data" : "Users"}
437
+ defaultItemsPerPage={4}
438
+ showVerticalBorder={showVBorder}
439
+ verticalBorderClassname={borderClass}
440
+ renderCard={useCustomCard ? (row: any) => {
441
+ const initials = (row.name as string).split(" ").map((w: string) => w[0]).join("");
442
+ return (
443
+ <div className="group bg-white dark:bg-slate-800 rounded-lg border border-slate-200 dark:border-slate-700 p-3 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md hover:border-slate-300 dark:hover:border-slate-600">
444
+ <div className="flex items-center gap-3 mb-2">
445
+ <ITAvatar initials={initials} size="sm" />
446
+ <div className="flex-1 min-w-0">
447
+ <p className="font-semibold text-slate-800 dark:text-white text-sm truncate">{row.name}</p>
448
+ </div>
449
+ <span className={`px-2 py-0.5 text-[9px] font-bold uppercase tracking-wider rounded-md ${
450
+ row.active ? "bg-emerald-100 text-emerald-700 dark:bg-emerald-950/30 dark:text-emerald-300" : "bg-slate-100 text-slate-600 dark:bg-slate-800 dark:text-slate-400"
451
+ }`}>{row.active ? "Active" : "Inactive"}</span>
452
+ </div>
453
+ <div className="flex items-center justify-between">
454
+ <span className={`px-1.5 py-0.5 text-[10px] font-bold rounded ${
455
+ row.role === "Admin" ? "bg-primary-100 text-primary-700 dark:bg-primary-950/40 dark:text-primary-300" :
456
+ row.role === "Editor" ? "bg-blue-100 text-blue-700 dark:bg-blue-950/40 dark:text-blue-300" :
457
+ "bg-slate-100 text-slate-600 dark:bg-slate-800 dark:text-slate-400"
458
+ }`}>{row.role}</span>
459
+ <span className="text-[10px] text-slate-400">ID: #{row.id}</span>
460
+ </div>
461
+ </div>
462
+ );
463
+ } : undefined}
140
464
  />
141
465
  </div>
142
466
  }
143
467
  controls={
144
- <div className="p-2 bg-slate-50 dark:bg-slate-900 rounded-lg text-xs space-y-2">
145
- <p className="text-slate-500">Esta tabla consume una función de carga de datos que se autogestiona en ordenamiento, paginado y filtros.</p>
146
- <div className="p-2 bg-amber-50 dark:bg-amber-950/20 border border-amber-200 dark:border-amber-900 rounded text-amber-800 dark:text-amber-300">
147
- Prueba a escribir en los inputs de filtro de la tabla!
468
+ <div className="space-y-3">
469
+ <div>
470
+ <p className="text-xs font-bold uppercase tracking-wider text-slate-400 dark:text-slate-500 mb-2">API state</p>
471
+ <div className="flex flex-wrap gap-2">
472
+ {(["normal", "loading", "empty", "error"] as const).map((s) => (
473
+ <button
474
+ key={s}
475
+ onClick={() => handleStateChange(s)}
476
+ className={`px-3 py-1.5 text-xs font-bold rounded-lg transition-all ${
477
+ apiState === s
478
+ ? "bg-slate-900 text-white dark:bg-white dark:text-slate-900 shadow-sm"
479
+ : "bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-300 hover:bg-slate-200 dark:hover:bg-slate-700"
480
+ }`}
481
+ >
482
+ {s === "normal" ? "✅ Normal" : s === "loading" ? "⏳ Loading" : s === "empty" ? "📭 Empty" : "💥 Error"}
483
+ </button>
484
+ ))}
485
+ </div>
486
+ </div>
487
+ <div className="flex items-center justify-between pt-1">
488
+ <span className="text-sm font-semibold text-gray-700 dark:text-slate-300">Custom card template</span>
489
+ <ITSlideToggle isOn={useCustomCard} onToggle={setUseCustomCard} size="sm" />
490
+ </div>
491
+ {!useCustomCard && (
492
+ <div className="p-2 bg-slate-100 dark:bg-slate-800 rounded text-xs text-slate-500">
493
+ Default card active — shows all columns as label-value pairs
494
+ </div>
495
+ )}
496
+ <div>
497
+ <p className="text-xs font-bold uppercase tracking-wider text-slate-400 dark:text-slate-500 mb-2">Borders</p>
498
+ <div className="flex flex-wrap gap-2">
499
+ {(["default", "blue", "rose", "none"] as const).map((s) => (
500
+ <button
501
+ key={s}
502
+ onClick={() => {
503
+ setBorderStyle(s);
504
+ setShowVBorder(s !== "none");
505
+ }}
506
+ className={`px-3 py-1.5 text-xs font-bold rounded-lg transition-all ${
507
+ borderStyle === s
508
+ ? "bg-slate-900 text-white dark:bg-white dark:text-slate-900 shadow-sm"
509
+ : "bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-300 hover:bg-slate-200 dark:hover:bg-slate-700"
510
+ }`}
511
+ >
512
+ {s === "default" ? "Gray" : s === "blue" ? "Blue" : s === "rose" ? "Rose" : "None"}
513
+ </button>
514
+ ))}
515
+ </div>
516
+ </div>
517
+ </div>
518
+ }
519
+ gallery={
520
+ <div className="space-y-6">
521
+ <div>
522
+ <h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">Custom cards with hover</h4>
523
+ <div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden">
524
+ <GalleryDataTable
525
+ title="Team members"
526
+ defaultItemsPerPage={4}
527
+ renderCard={(row: any) => {
528
+ const initials = (row.name as string).split(" ").map((w: string) => w[0]).join("");
529
+ return (
530
+ <div className="group bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg hover:border-slate-300 dark:hover:border-slate-600">
531
+ <div className="flex items-start justify-between mb-3">
532
+ <div className="flex items-center gap-3">
533
+ <ITAvatar initials={initials} size="md" />
534
+ <div>
535
+ <p className="font-bold text-slate-800 dark:text-white text-sm">{row.name as string}</p>
536
+ <p className="text-xs text-slate-400">{row.email as string}</p>
537
+ </div>
538
+ </div>
539
+ <span className={`px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider rounded-md ${
540
+ row.active ? "bg-emerald-100 text-emerald-700 dark:bg-emerald-950/30 dark:text-emerald-300" : "bg-slate-100 text-slate-600 dark:bg-slate-800 dark:text-slate-400"
541
+ }`}>{row.active ? "Active" : "Inactive"}</span>
542
+ </div>
543
+ <div className="flex items-center gap-2 text-xs text-slate-500 mb-3">
544
+ <span className={`px-2 py-0.5 rounded-md font-semibold ${
545
+ row.role === "Admin" ? "bg-primary-100 text-primary-700 dark:bg-primary-950/40 dark:text-primary-300" :
546
+ row.role === "Editor" ? "bg-blue-100 text-blue-700 dark:bg-blue-950/40 dark:text-blue-300" :
547
+ "bg-slate-100 text-slate-600 dark:bg-slate-800 dark:text-slate-400"
548
+ }`}>{row.role as string}</span>
549
+ </div>
550
+ <div className="flex items-center justify-between pt-3 border-t border-slate-100 dark:border-slate-700">
551
+ <span className="text-xs text-slate-400">ID: <strong className="text-slate-700 dark:text-slate-200">#{row.id}</strong></span>
552
+ <div className="flex gap-1.5 opacity-0 group-hover:opacity-100 transition-opacity">
553
+ <button className="p-1.5 rounded-md hover:bg-slate-100 dark:hover:bg-slate-700 text-slate-400 hover:text-primary-600"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg></button>
554
+ <button className="p-1.5 rounded-md hover:bg-slate-100 dark:hover:bg-slate-700 text-slate-400 hover:text-blue-600"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg></button>
555
+ </div>
556
+ </div>
557
+ </div>
558
+ );
559
+ }}
560
+ />
561
+ </div>
562
+ </div>
563
+
564
+ <div>
565
+ <h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">Default cards with pagination</h4>
566
+ <div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden">
567
+ <GalleryDataTable
568
+ size="sm"
569
+ defaultItemsPerPage={3}
570
+ defaultView="cards"
571
+ />
572
+ </div>
148
573
  </div>
149
574
  </div>
150
575
  }
@@ -152,6 +577,65 @@ export const DataTableShowcase = () => {
152
577
  );
153
578
  };
154
579
 
580
+ function GalleryDataTable({ title, size, defaultItemsPerPage, defaultView, renderCard }: {
581
+ title?: string;
582
+ size?: string;
583
+ defaultItemsPerPage?: number;
584
+ defaultView?: "table" | "cards";
585
+ renderCard?: (row: any) => React.ReactNode;
586
+ }) {
587
+ const columns = useMemo(() => [
588
+ { key: "id", label: "ID", type: "number" as const },
589
+ { key: "name", label: "Name", type: "string" as const, filter: true, sortable: true,
590
+ render: (row: any) => (
591
+ <div className="flex items-center gap-2.5">
592
+ <ITAvatar initials={(row.name as string).split(" ").map((w: string) => w[0]).join("")} size="sm" />
593
+ <span className="font-semibold text-slate-800 dark:text-white">{row.name}</span>
594
+ </div>
595
+ )
596
+ },
597
+ {
598
+ key: "role", label: "Role", type: "string" as const, filter: true, sortable: true,
599
+ render: (row: any) => (
600
+ <span className={`px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider rounded-md ${
601
+ row.role === "Admin" ? "bg-primary-100 text-primary-700 dark:bg-primary-950/40 dark:text-primary-300" :
602
+ row.role === "Editor" ? "bg-blue-100 text-blue-700 dark:bg-blue-950/40 dark:text-blue-300" :
603
+ "bg-slate-100 text-slate-600 dark:bg-slate-800 dark:text-slate-400"
604
+ }`}>{row.role}</span>
605
+ )
606
+ },
607
+ { key: "active", label: "Active", type: "boolean" as const, filter: true }
608
+ ], []);
609
+
610
+ const fetchData = useCallback(async (params: ITDataTableFetchParams) => {
611
+ const all = [
612
+ { id: 1, name: "Sofía Castillo", email: "sofia@axzy.dev", role: "Admin", active: true },
613
+ { id: 2, name: "Daniela Klein", email: "daniela@axzy.dev", role: "Editor", active: true },
614
+ { id: 3, name: "Mariana Reyes", email: "mariana@axzy.dev", role: "Viewer", active: false },
615
+ { id: 4, name: "Camila Torres", email: "camila@axzy.dev", role: "Admin", active: true },
616
+ { id: 5, name: "Valentina Méndez", email: "valentina@axzy.dev", role: "Editor", active: true },
617
+ { id: 6, name: "Luciana Rivas", email: "luciana@axzy.dev", role: "Viewer", active: false },
618
+ { id: 7, name: "Isabella Cruz", email: "isabella@axzy.dev", role: "Editor", active: true },
619
+ { id: 8, name: "Gabriela Vargas", email: "gabriela@axzy.dev", role: "Admin", active: true },
620
+ ];
621
+ await new Promise(r => setTimeout(r, 200));
622
+ const start = (params.page - 1) * params.limit;
623
+ return { data: all.slice(start, start + params.limit), total: all.length };
624
+ }, []);
625
+
626
+ return (
627
+ <ITDataTable
628
+ columns={columns}
629
+ fetchData={fetchData}
630
+ title={title}
631
+ size={size as any}
632
+ defaultItemsPerPage={defaultItemsPerPage}
633
+ defaultView={defaultView}
634
+ renderCard={renderCard}
635
+ />
636
+ );
637
+ }
638
+
155
639
  // 3. ITBadget Showcase
156
640
  export const BadgetShowcase = () => {
157
641
  const [color, setColor] = useState<any>("success");