@boxcustodia/library 2.0.0-alpha.22 → 2.0.0-alpha.23

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 (102) hide show
  1. package/dist/components/calendar/calendar.cjs.js +1 -1
  2. package/dist/components/calendar/calendar.es.js +43 -44
  3. package/dist/components/date-picker/date-input.cjs.js +1 -1
  4. package/dist/components/date-picker/date-input.es.js +160 -140
  5. package/dist/components/pagination/pagination.cjs.js +1 -1
  6. package/dist/components/pagination/pagination.es.js +37 -35
  7. package/dist/components/scroll-area/scroll-area.cjs.js +1 -1
  8. package/dist/components/scroll-area/scroll-area.es.js +4 -4
  9. package/dist/components/select/select.cjs.js +1 -1
  10. package/dist/components/select/select.es.js +94 -90
  11. package/dist/hooks/use-action/use-action.cjs.js +1 -0
  12. package/dist/hooks/use-action/use-action.es.js +41 -0
  13. package/dist/hooks/use-pagination/use-pagination.cjs.js +1 -1
  14. package/dist/hooks/use-pagination/use-pagination.es.js +77 -32
  15. package/dist/hooks/use-range-pagination/use-range-pagination.cjs.js +1 -1
  16. package/dist/hooks/use-range-pagination/use-range-pagination.es.js +8 -5
  17. package/dist/hooks/use-selection/use-selection.cjs.js +1 -1
  18. package/dist/hooks/use-selection/use-selection.es.js +95 -33
  19. package/dist/hooks/use-session-storage/use-session-storage.cjs.js +1 -0
  20. package/dist/hooks/use-session-storage/use-session-storage.es.js +57 -0
  21. package/dist/index.cjs.js +1 -1
  22. package/dist/index.es.js +61 -63
  23. package/dist/src/components/select/select.d.ts +9 -2
  24. package/dist/src/hooks/index.d.ts +2 -3
  25. package/dist/src/hooks/internal/index.d.ts +1 -0
  26. package/dist/src/hooks/internal/serializer.d.ts +4 -0
  27. package/dist/src/hooks/use-action/index.d.ts +1 -0
  28. package/dist/src/hooks/use-action/use-action.d.ts +22 -0
  29. package/dist/src/hooks/use-local-storage/use-local-storage.d.ts +2 -4
  30. package/dist/src/hooks/use-pagination/use-pagination.d.ts +47 -32
  31. package/dist/src/hooks/use-range-pagination/use-range-pagination.d.ts +16 -10
  32. package/dist/src/hooks/use-selection/use-selection.d.ts +39 -45
  33. package/dist/src/hooks/use-session-storage/index.d.ts +1 -0
  34. package/dist/src/hooks/use-session-storage/use-session-storage.d.ts +11 -0
  35. package/package.json +1 -1
  36. package/src/components/calendar/calendar.tsx +10 -8
  37. package/src/components/combobox/combobox.stories.tsx +16 -0
  38. package/src/components/date-picker/date-input.tsx +23 -2
  39. package/src/components/form/form.tsx +3 -2
  40. package/src/components/pagination/pagination.tsx +5 -3
  41. package/src/components/scroll-area/scroll-area.tsx +2 -2
  42. package/src/components/select/select.tsx +14 -3
  43. package/src/hooks/index.ts +2 -3
  44. package/src/hooks/internal/index.ts +1 -0
  45. package/src/hooks/internal/serializer.ts +4 -0
  46. package/src/hooks/use-action/index.ts +1 -0
  47. package/src/hooks/{use-mutation/use-mutation.stories.tsx → use-action/use-action.stories.tsx} +34 -34
  48. package/src/hooks/{use-mutation/use-mutation.test.ts → use-action/use-action.test.ts} +53 -53
  49. package/src/hooks/{use-mutation/use-mutation.ts → use-action/use-action.ts} +20 -20
  50. package/src/hooks/use-click-outside/use-click-outside.stories.tsx +0 -1
  51. package/src/hooks/use-clipboard/use-clipboard.stories.tsx +0 -1
  52. package/src/hooks/use-document-title/use-document-title.stories.tsx +0 -1
  53. package/src/hooks/use-is-visible/use-is-visible.test.tsx +1 -1
  54. package/src/hooks/use-local-storage/use-local-storage.stories.tsx +0 -1
  55. package/src/hooks/use-local-storage/use-local-storage.ts +2 -5
  56. package/src/hooks/use-media-query/use-media-query.stories.tsx +0 -1
  57. package/src/hooks/use-pagination/use-pagination.stories.tsx +720 -57
  58. package/src/hooks/use-pagination/use-pagination.test.tsx +560 -48
  59. package/src/hooks/use-pagination/use-pagination.ts +266 -0
  60. package/src/hooks/use-prevent-page-close/use-prevent-page-close.stories.tsx +0 -1
  61. package/src/hooks/use-range-pagination/use-range-pagination.test.tsx +2 -2
  62. package/src/hooks/use-range-pagination/use-range-pagination.tsx +24 -21
  63. package/src/hooks/use-selection/use-selection.stories.tsx +339 -84
  64. package/src/hooks/use-selection/use-selection.test.tsx +417 -2
  65. package/src/hooks/use-selection/use-selection.ts +212 -102
  66. package/src/hooks/use-session-storage/index.ts +1 -0
  67. package/src/hooks/use-session-storage/use-session-storage.stories.tsx +122 -0
  68. package/src/hooks/use-session-storage/use-session-storage.test.ts +164 -0
  69. package/src/hooks/use-session-storage/use-session-storage.ts +115 -0
  70. package/dist/hooks/use-async/use-async.cjs.js +0 -1
  71. package/dist/hooks/use-async/use-async.es.js +0 -57
  72. package/dist/hooks/use-focus-trap/scope-tab.cjs.js +0 -1
  73. package/dist/hooks/use-focus-trap/scope-tab.es.js +0 -21
  74. package/dist/hooks/use-focus-trap/tabbable.cjs.js +0 -1
  75. package/dist/hooks/use-focus-trap/tabbable.es.js +0 -38
  76. package/dist/hooks/use-focus-trap/use-focus-trap.cjs.js +0 -1
  77. package/dist/hooks/use-focus-trap/use-focus-trap.es.js +0 -34
  78. package/dist/hooks/use-mutation/use-mutation.cjs.js +0 -1
  79. package/dist/hooks/use-mutation/use-mutation.es.js +0 -41
  80. package/dist/src/hooks/use-async/index.d.ts +0 -1
  81. package/dist/src/hooks/use-async/use-async.d.ts +0 -21
  82. package/dist/src/hooks/use-focus-trap/index.d.ts +0 -1
  83. package/dist/src/hooks/use-focus-trap/scope-tab.d.ts +0 -1
  84. package/dist/src/hooks/use-focus-trap/tabbable.d.ts +0 -4
  85. package/dist/src/hooks/use-focus-trap/use-focus-trap.d.ts +0 -1
  86. package/dist/src/hooks/use-mutation/index.d.ts +0 -1
  87. package/dist/src/hooks/use-mutation/use-mutation.d.ts +0 -22
  88. package/dist/src/hooks/use-mutation/use-mutation.test.d.ts +0 -1
  89. package/src/hooks/use-async/index.ts +0 -1
  90. package/src/hooks/use-async/use-async.stories.tsx +0 -272
  91. package/src/hooks/use-async/use-async.test.ts +0 -397
  92. package/src/hooks/use-async/use-async.ts +0 -135
  93. package/src/hooks/use-focus-trap/index.ts +0 -1
  94. package/src/hooks/use-focus-trap/scope-tab.ts +0 -38
  95. package/src/hooks/use-focus-trap/tabbable.ts +0 -70
  96. package/src/hooks/use-focus-trap/use-focus-trap.stories.tsx +0 -37
  97. package/src/hooks/use-focus-trap/use-focus-trap.test.ts +0 -355
  98. package/src/hooks/use-focus-trap/use-focus-trap.ts +0 -78
  99. package/src/hooks/use-mutation/index.ts +0 -1
  100. package/src/hooks/use-pagination/use-pagination.tsx +0 -84
  101. /package/dist/src/hooks/{use-async/use-async.test.d.ts → use-action/use-action.test.d.ts} +0 -0
  102. /package/dist/src/hooks/{use-focus-trap/use-focus-trap.test.d.ts → use-session-storage/use-session-storage.test.d.ts} +0 -0
@@ -1,14 +1,8 @@
1
- import { Meta } from "@storybook/react-vite";
1
+ import type { Meta } from "@storybook/react-vite";
2
2
  import { useState } from "react";
3
- import { action } from "storybook/actions";
4
- import { Button } from "../../components";
3
+ import { Button, type ColumnDef, Pagination, Table } from "../../components";
5
4
  import { usePagination } from "../use-pagination";
6
5
 
7
- const TABLE_PAGE_SIZES = [5, 10, 20, 50, 100];
8
- /**
9
- * Hook que facilita el manejo de paginaciones, recibe como param la cantidad de elementos, la cantidad de elementos por página y una función
10
- * que se ejecuta cuando cambia la pagina
11
- */
12
6
  const meta: Meta = {
13
7
  title: "hooks/usePagination",
14
8
  tags: ["autodocs"],
@@ -16,57 +10,726 @@ const meta: Meta = {
16
10
 
17
11
  export default meta;
18
12
 
19
- export const Default = () => {
20
- const [pageSize, setPageSize] = useState(TABLE_PAGE_SIZES[0]);
21
- const { currentPage, maxPage, isLastPage, isFirstPage, prev, next, goTo } =
22
- usePagination({
13
+ // ---------------------------------------------------------------------------
14
+ // Story 1 Default (uncontrolled)
15
+ // Shows all returned values and all 6 action buttons.
16
+ // ---------------------------------------------------------------------------
17
+
18
+ export const Default = {
19
+ render: () => {
20
+ const {
21
+ page,
22
+ pageSize,
23
+ pageCount,
24
+ isFirstPage,
25
+ isLastPage,
26
+ hasPrevPage,
27
+ hasNextPage,
28
+ range,
29
+ next,
30
+ prev,
31
+ firstPage,
32
+ lastPage,
33
+ goTo,
34
+ setPageSize,
35
+ } = usePagination({ totalItems: 100, defaultPageSize: 10, defaultPage: 1 });
36
+
37
+ return (
38
+ <div className="space-y-4">
39
+ <pre className="rounded-md bg-slate-950 p-4 text-sm text-white font-mono">
40
+ <code className="block">
41
+ page: <span className="text-blue-300">{page}</span>
42
+ </code>
43
+ <code className="block">
44
+ pageSize: <span className="text-blue-300">{pageSize}</span>
45
+ </code>
46
+ <code className="block">
47
+ pageCount: <span className="text-blue-300">{pageCount}</span>
48
+ </code>
49
+ <code className="block">
50
+ isFirstPage:{" "}
51
+ <span className={isFirstPage ? "text-green-400" : "text-red-400"}>
52
+ {String(isFirstPage)}
53
+ </span>
54
+ </code>
55
+ <code className="block">
56
+ isLastPage:{" "}
57
+ <span className={isLastPage ? "text-green-400" : "text-red-400"}>
58
+ {String(isLastPage)}
59
+ </span>
60
+ </code>
61
+ <code className="block">
62
+ hasPrevPage:{" "}
63
+ <span className={hasPrevPage ? "text-green-400" : "text-red-400"}>
64
+ {String(hasPrevPage)}
65
+ </span>
66
+ </code>
67
+ <code className="block">
68
+ hasNextPage:{" "}
69
+ <span className={hasNextPage ? "text-green-400" : "text-red-400"}>
70
+ {String(hasNextPage)}
71
+ </span>
72
+ </code>
73
+ <code className="block">
74
+ range:{" "}
75
+ <span className="text-yellow-300">{`{ start: ${range.start}, end: ${range.end} }`}</span>
76
+ </code>
77
+ </pre>
78
+ <div className="flex flex-wrap gap-2">
79
+ <Button size="sm" onClick={firstPage} disabled={isFirstPage}>
80
+ First
81
+ </Button>
82
+ <Button size="sm" onClick={prev} disabled={!hasPrevPage}>
83
+ Prev
84
+ </Button>
85
+ <Button size="sm" onClick={next} disabled={!hasNextPage}>
86
+ Next
87
+ </Button>
88
+ <Button size="sm" onClick={lastPage} disabled={isLastPage}>
89
+ Last
90
+ </Button>
91
+ <Button size="sm" onClick={() => goTo(5)}>
92
+ Go to 5
93
+ </Button>
94
+ <Button size="sm" onClick={() => setPageSize(20)}>
95
+ Set size 20
96
+ </Button>
97
+ </div>
98
+ </div>
99
+ );
100
+ },
101
+ };
102
+
103
+ // ---------------------------------------------------------------------------
104
+ // Story 2 — Controlled
105
+ // page + onPageChange wired to local useState.
106
+ // ---------------------------------------------------------------------------
107
+
108
+ export const Controlled = {
109
+ render: () => {
110
+ const [page, setPage] = useState(1);
111
+ const pagination = usePagination({
112
+ totalItems: 50,
113
+ defaultPageSize: 10,
114
+ page,
115
+ onPageChange: setPage,
116
+ });
117
+
118
+ return (
119
+ <div className="space-y-4">
120
+ <p className="text-sm text-muted-foreground">
121
+ Page controlled by parent <code>useState</code>. Current:{" "}
122
+ <strong>{page}</strong>
123
+ </p>
124
+ <pre className="rounded-md bg-slate-950 p-4 text-sm text-white font-mono">
125
+ <code className="block">
126
+ page: <span className="text-blue-300">{pagination.page}</span>
127
+ </code>
128
+ <code className="block">
129
+ pageCount:{" "}
130
+ <span className="text-blue-300">{pagination.pageCount}</span>
131
+ </code>
132
+ <code className="block">
133
+ isFirstPage:{" "}
134
+ <span
135
+ className={
136
+ pagination.isFirstPage ? "text-green-400" : "text-red-400"
137
+ }
138
+ >
139
+ {String(pagination.isFirstPage)}
140
+ </span>
141
+ </code>
142
+ <code className="block">
143
+ isLastPage:{" "}
144
+ <span
145
+ className={
146
+ pagination.isLastPage ? "text-green-400" : "text-red-400"
147
+ }
148
+ >
149
+ {String(pagination.isLastPage)}
150
+ </span>
151
+ </code>
152
+ </pre>
153
+ <div className="flex gap-2">
154
+ <Button
155
+ size="sm"
156
+ onClick={pagination.firstPage}
157
+ disabled={pagination.isFirstPage}
158
+ >
159
+ First
160
+ </Button>
161
+ <Button
162
+ size="sm"
163
+ onClick={pagination.prev}
164
+ disabled={!pagination.hasPrevPage}
165
+ >
166
+ Prev
167
+ </Button>
168
+ <Button
169
+ size="sm"
170
+ onClick={pagination.next}
171
+ disabled={!pagination.hasNextPage}
172
+ >
173
+ Next
174
+ </Button>
175
+ <Button
176
+ size="sm"
177
+ onClick={pagination.lastPage}
178
+ disabled={pagination.isLastPage}
179
+ >
180
+ Last
181
+ </Button>
182
+ </div>
183
+ </div>
184
+ );
185
+ },
186
+ };
187
+
188
+ // ---------------------------------------------------------------------------
189
+ // Story 3 — ControllablePageSize
190
+ // pageSize + onPageSizeChange with a dropdown. Shows auto-reset to page 1.
191
+ // ---------------------------------------------------------------------------
192
+
193
+ export const ControllablePageSize = {
194
+ render: () => {
195
+ const [pageSize, setPageSize] = useState(10);
196
+ const [page, setPage] = useState(3);
197
+ // Single fetch-trigger log. onPaginationChange fires ONCE with both final
198
+ // values, even when changing the page size resets the page to 1 — so there
199
+ // is never a duplicate fetch.
200
+ const [fetchLog, setFetchLog] = useState<string[]>([]);
201
+
202
+ const pagination = usePagination({
23
203
  totalItems: 100,
24
- pageSize: pageSize,
25
- onChange: action("change"),
26
- initialCurrentPage: 1,
204
+ pageSize,
205
+ onPageSizeChange: setPageSize,
206
+ page,
207
+ onPageChange: setPage,
208
+ onPaginationChange: ({ page, pageSize }) => {
209
+ setFetchLog((prev) => [
210
+ `fetch → page ${page}, pageSize ${pageSize}`,
211
+ ...prev.slice(0, 4),
212
+ ]);
213
+ },
214
+ });
215
+
216
+ return (
217
+ <div className="space-y-4">
218
+ <p className="text-sm text-muted-foreground">
219
+ Change page size — page resets to 1 automatically, and{" "}
220
+ <code>onPaginationChange</code> fires exactly once with both final
221
+ values (a single fetch).
222
+ </p>
223
+ <div className="flex items-center gap-2">
224
+ <label htmlFor="page-size" className="text-sm font-medium">
225
+ Page size:
226
+ </label>
227
+ <select
228
+ id="page-size"
229
+ value={pageSize}
230
+ onChange={(e) => pagination.setPageSize(Number(e.target.value))}
231
+ className="border rounded px-2 py-1 text-sm"
232
+ >
233
+ {[5, 10, 20, 50].map((s) => (
234
+ <option key={s} value={s}>
235
+ {s}
236
+ </option>
237
+ ))}
238
+ </select>
239
+ </div>
240
+ <pre className="rounded-md bg-slate-950 p-4 text-sm text-white font-mono">
241
+ <code className="block">
242
+ page: <span className="text-blue-300">{pagination.page}</span>
243
+ </code>
244
+ <code className="block">
245
+ pageSize:{" "}
246
+ <span className="text-blue-300">{pagination.pageSize}</span>
247
+ </code>
248
+ <code className="block">
249
+ pageCount:{" "}
250
+ <span className="text-blue-300">{pagination.pageCount}</span>
251
+ </code>
252
+ <code className="block">
253
+ range:{" "}
254
+ <span className="text-yellow-300">{`{ start: ${pagination.range.start}, end: ${pagination.range.end} }`}</span>
255
+ </code>
256
+ </pre>
257
+ <div className="flex gap-2">
258
+ <Button
259
+ size="sm"
260
+ onClick={pagination.prev}
261
+ disabled={!pagination.hasPrevPage}
262
+ >
263
+ Prev
264
+ </Button>
265
+ <Button
266
+ size="sm"
267
+ onClick={pagination.next}
268
+ disabled={!pagination.hasNextPage}
269
+ >
270
+ Next
271
+ </Button>
272
+ </div>
273
+ {fetchLog.length > 0 && (
274
+ <div className="space-y-1">
275
+ <p className="text-xs font-medium text-slate-500">
276
+ onPaginationChange log:
277
+ </p>
278
+ {fetchLog.map((entry, i) => (
279
+ <p key={i} className="font-mono text-xs text-slate-600">
280
+ {entry}
281
+ </p>
282
+ ))}
283
+ </div>
284
+ )}
285
+ </div>
286
+ );
287
+ },
288
+ };
289
+
290
+ // ---------------------------------------------------------------------------
291
+ // Story 4 — EdgeCases
292
+ // totalItems=0, last partial page, single-page scenario shown side by side.
293
+ // ---------------------------------------------------------------------------
294
+
295
+ export const EdgeCases = {
296
+ render: () => {
297
+ const empty = usePagination({ totalItems: 0 });
298
+ const partial = usePagination({
299
+ totalItems: 25,
300
+ defaultPageSize: 10,
301
+ defaultPage: 3,
27
302
  });
303
+ const single = usePagination({ totalItems: 8, defaultPageSize: 10 });
304
+
305
+ const StateBlock = ({
306
+ label,
307
+ state,
308
+ }: {
309
+ label: string;
310
+ state: ReturnType<typeof usePagination>;
311
+ }) => (
312
+ <div className="flex-1 min-w-48">
313
+ <p className="text-sm font-semibold mb-2">{label}</p>
314
+ <pre className="rounded-md bg-slate-950 p-3 text-xs text-white font-mono">
315
+ <code className="block">page: {state.page}</code>
316
+ <code className="block">pageCount: {state.pageCount}</code>
317
+ <code className="block">pageSize: {state.pageSize}</code>
318
+ <code className="block">isFirst: {String(state.isFirstPage)}</code>
319
+ <code className="block">isLast: {String(state.isLastPage)}</code>
320
+ <code className="block">
321
+ range: {`{${state.range.start}…${state.range.end}}`}
322
+ </code>
323
+ </pre>
324
+ </div>
325
+ );
326
+
327
+ return (
328
+ <div className="flex flex-wrap gap-4">
329
+ <StateBlock label="Empty (totalItems=0)" state={empty} />
330
+ <StateBlock
331
+ label="Last partial page (25 items, page 3 of 3)"
332
+ state={partial}
333
+ />
334
+ <StateBlock label="Single page (8 items, pageSize=10)" state={single} />
335
+ </div>
336
+ );
337
+ },
338
+ };
339
+
340
+ // ---------------------------------------------------------------------------
341
+ // Story 5 — Integration
342
+ // usePagination is the single source of truth: it drives both the Table slice
343
+ // and the Pagination component (controlled via currentPage / onCurrentPageChange).
344
+ // ---------------------------------------------------------------------------
345
+
346
+ type Employee = {
347
+ id: number;
348
+ name: string;
349
+ department: string;
350
+ role: string;
351
+ status: "Active" | "On leave" | "Inactive";
352
+ };
353
+
354
+ const EMPLOYEES: Employee[] = [
355
+ {
356
+ id: 1,
357
+ name: "Ana García",
358
+ department: "Engineering",
359
+ role: "Senior Frontend",
360
+ status: "Active",
361
+ },
362
+ {
363
+ id: 2,
364
+ name: "Carlos Méndez",
365
+ department: "Engineering",
366
+ role: "Backend Lead",
367
+ status: "Active",
368
+ },
369
+ {
370
+ id: 3,
371
+ name: "Sofía Torres",
372
+ department: "Design",
373
+ role: "Product Designer",
374
+ status: "Active",
375
+ },
376
+ {
377
+ id: 4,
378
+ name: "Matías Romero",
379
+ department: "Engineering",
380
+ role: "DevOps Engineer",
381
+ status: "On leave",
382
+ },
383
+ {
384
+ id: 5,
385
+ name: "Valentina Cruz",
386
+ department: "Product",
387
+ role: "Product Manager",
388
+ status: "Active",
389
+ },
390
+ {
391
+ id: 6,
392
+ name: "Ignacio Ruiz",
393
+ department: "Engineering",
394
+ role: "Mobile Engineer",
395
+ status: "Active",
396
+ },
397
+ {
398
+ id: 7,
399
+ name: "Lucía Fernández",
400
+ department: "Design",
401
+ role: "UX Researcher",
402
+ status: "Active",
403
+ },
404
+ {
405
+ id: 8,
406
+ name: "Facundo López",
407
+ department: "Engineering",
408
+ role: "Staff Engineer",
409
+ status: "Inactive",
410
+ },
411
+ {
412
+ id: 9,
413
+ name: "Camila Vargas",
414
+ department: "Marketing",
415
+ role: "Growth Manager",
416
+ status: "Active",
417
+ },
418
+ {
419
+ id: 10,
420
+ name: "Tomás Jiménez",
421
+ department: "Engineering",
422
+ role: "Frontend Engineer",
423
+ status: "Active",
424
+ },
425
+ {
426
+ id: 11,
427
+ name: "Martina Sosa",
428
+ department: "Product",
429
+ role: "Product Analyst",
430
+ status: "Active",
431
+ },
432
+ {
433
+ id: 12,
434
+ name: "Agustín Herrera",
435
+ department: "Engineering",
436
+ role: "Data Engineer",
437
+ status: "On leave",
438
+ },
439
+ {
440
+ id: 13,
441
+ name: "Florencia Ríos",
442
+ department: "Design",
443
+ role: "Brand Designer",
444
+ status: "Active",
445
+ },
446
+ {
447
+ id: 14,
448
+ name: "Sebastián Morales",
449
+ department: "Engineering",
450
+ role: "Security Engineer",
451
+ status: "Active",
452
+ },
453
+ {
454
+ id: 15,
455
+ name: "Julieta Vega",
456
+ department: "Marketing",
457
+ role: "Content Strategist",
458
+ status: "Active",
459
+ },
460
+ {
461
+ id: 16,
462
+ name: "Rodrigo Blanco",
463
+ department: "Engineering",
464
+ role: "Backend Engineer",
465
+ status: "Active",
466
+ },
467
+ {
468
+ id: 17,
469
+ name: "Emilia Reyes",
470
+ department: "Product",
471
+ role: "Product Designer",
472
+ status: "Active",
473
+ },
474
+ {
475
+ id: 18,
476
+ name: "Nicolás Acosta",
477
+ department: "Engineering",
478
+ role: "Frontend Lead",
479
+ status: "Active",
480
+ },
481
+ {
482
+ id: 19,
483
+ name: "Bianca Molina",
484
+ department: "Marketing",
485
+ role: "SEO Specialist",
486
+ status: "Inactive",
487
+ },
488
+ {
489
+ id: 20,
490
+ name: "Esteban Peralta",
491
+ department: "Engineering",
492
+ role: "Platform Engineer",
493
+ status: "Active",
494
+ },
495
+ {
496
+ id: 21,
497
+ name: "Aldana Castillo",
498
+ department: "Design",
499
+ role: "Motion Designer",
500
+ status: "Active",
501
+ },
502
+ {
503
+ id: 22,
504
+ name: "Bruno Campos",
505
+ department: "Engineering",
506
+ role: "QA Engineer",
507
+ status: "Active",
508
+ },
509
+ {
510
+ id: 23,
511
+ name: "Xiomara Navarro",
512
+ department: "Product",
513
+ role: "Head of Product",
514
+ status: "Active",
515
+ },
516
+ {
517
+ id: 24,
518
+ name: "Gonzalo Ibáñez",
519
+ department: "Engineering",
520
+ role: "iOS Engineer",
521
+ status: "On leave",
522
+ },
523
+ {
524
+ id: 25,
525
+ name: "Renata Suárez",
526
+ department: "Marketing",
527
+ role: "Performance Marketer",
528
+ status: "Active",
529
+ },
530
+ {
531
+ id: 26,
532
+ name: "Hernán Delgado",
533
+ department: "Engineering",
534
+ role: "Android Engineer",
535
+ status: "Active",
536
+ },
537
+ {
538
+ id: 27,
539
+ name: "Pilar Guzmán",
540
+ department: "Design",
541
+ role: "Design Systems Lead",
542
+ status: "Active",
543
+ },
544
+ {
545
+ id: 28,
546
+ name: "Diego Ponce",
547
+ department: "Engineering",
548
+ role: "Tech Lead",
549
+ status: "Active",
550
+ },
551
+ {
552
+ id: 29,
553
+ name: "Violeta Medina",
554
+ department: "Product",
555
+ role: "Product Strategist",
556
+ status: "Active",
557
+ },
558
+ {
559
+ id: 30,
560
+ name: "Mauricio Arias",
561
+ department: "Engineering",
562
+ role: "Backend Engineer",
563
+ status: "Active",
564
+ },
565
+ {
566
+ id: 31,
567
+ name: "Catalina Flores",
568
+ department: "Marketing",
569
+ role: "Brand Manager",
570
+ status: "Active",
571
+ },
572
+ {
573
+ id: 32,
574
+ name: "Leandro Ortiz",
575
+ department: "Engineering",
576
+ role: "ML Engineer",
577
+ status: "Active",
578
+ },
579
+ {
580
+ id: 33,
581
+ name: "Inés Carrillo",
582
+ department: "Design",
583
+ role: "UX Designer",
584
+ status: "On leave",
585
+ },
586
+ {
587
+ id: 34,
588
+ name: "Pablo Montoya",
589
+ department: "Engineering",
590
+ role: "Infrastructure Lead",
591
+ status: "Active",
592
+ },
593
+ {
594
+ id: 35,
595
+ name: "Verónica Sandoval",
596
+ department: "Product",
597
+ role: "Product Owner",
598
+ status: "Active",
599
+ },
600
+ {
601
+ id: 36,
602
+ name: "Ramiro Espinoza",
603
+ department: "Engineering",
604
+ role: "Fullstack Engineer",
605
+ status: "Active",
606
+ },
607
+ {
608
+ id: 37,
609
+ name: "Cecilia Contreras",
610
+ department: "Marketing",
611
+ role: "CRM Manager",
612
+ status: "Active",
613
+ },
614
+ {
615
+ id: 38,
616
+ name: "Adrián Guerrero",
617
+ department: "Engineering",
618
+ role: "Site Reliability Eng.",
619
+ status: "Inactive",
620
+ },
621
+ {
622
+ id: 39,
623
+ name: "Gabriela Miranda",
624
+ department: "Design",
625
+ role: "Interaction Designer",
626
+ status: "Active",
627
+ },
628
+ {
629
+ id: 40,
630
+ name: "Claudio Fuentes",
631
+ department: "Engineering",
632
+ role: "API Architect",
633
+ status: "Active",
634
+ },
635
+ {
636
+ id: 41,
637
+ name: "Luciana Rojas",
638
+ department: "Product",
639
+ role: "Data Product Manager",
640
+ status: "Active",
641
+ },
642
+ {
643
+ id: 42,
644
+ name: "Marcos Benítez",
645
+ department: "Engineering",
646
+ role: "Frontend Engineer",
647
+ status: "Active",
648
+ },
649
+ {
650
+ id: 43,
651
+ name: "Daniela Castro",
652
+ department: "Marketing",
653
+ role: "Social Media Lead",
654
+ status: "On leave",
655
+ },
656
+ {
657
+ id: 44,
658
+ name: "Fernando Vera",
659
+ department: "Engineering",
660
+ role: "Platform Architect",
661
+ status: "Active",
662
+ },
663
+ {
664
+ id: 45,
665
+ name: "Antonella Ramos",
666
+ department: "Design",
667
+ role: "Visual Designer",
668
+ status: "Active",
669
+ },
670
+ {
671
+ id: 46,
672
+ name: "Julio Pereira",
673
+ department: "Engineering",
674
+ role: "Security Researcher",
675
+ status: "Active",
676
+ },
677
+ {
678
+ id: 47,
679
+ name: "Belén Alvarado",
680
+ department: "Product",
681
+ role: "Growth Product Manager",
682
+ status: "Active",
683
+ },
684
+ ];
685
+
686
+ const STATUS_STYLES: Record<Employee["status"], string> = {
687
+ Active: "bg-green-100 text-green-800",
688
+ "On leave": "bg-yellow-100 text-yellow-800",
689
+ Inactive: "bg-slate-100 text-slate-600",
690
+ };
691
+
692
+ const EMPLOYEE_COLUMNS: ColumnDef<Employee>[] = [
693
+ { key: "name", header: "Name" },
694
+ { key: "department", header: "Department" },
695
+ { key: "role", header: "Role" },
696
+ {
697
+ key: "status",
698
+ header: "Status",
699
+ cell: (row) => (
700
+ <span
701
+ className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${STATUS_STYLES[row.status]}`}
702
+ >
703
+ {row.status}
704
+ </span>
705
+ ),
706
+ },
707
+ ];
708
+
709
+ export const Integration = {
710
+ render: () => {
711
+ const { page, pageSize, range, goTo, setPageSize } = usePagination({
712
+ totalItems: EMPLOYEES.length,
713
+ defaultPageSize: 10,
714
+ });
715
+
716
+ const pageData = EMPLOYEES.slice(range.start - 1, range.end);
28
717
 
29
- return (
30
- <div className="space-y-2">
31
- <pre className="rounded-md bg-slate-950 p-4 text-white">
32
- <code className="block">
33
- CurrentPage:{" "}
34
- <span className="text-blue-300">
35
- {JSON.stringify(currentPage, null, 2)}
36
- </span>
37
- </code>
38
- <code className="block">PageSize: {pageSize}</code>
39
- <code className="block">Maxpage: {maxPage}</code>
40
- <code className="block">
41
- isLastPage:{" "}
42
- <span className={isLastPage ? "text-green-500" : "text-red-500"}>
43
- {JSON.stringify(isLastPage)}
44
- </span>
45
- </code>
46
- <code className="block">
47
- IsFirstPage:{" "}
48
- <span className={isFirstPage ? "text-green-500" : "text-red-500"}>
49
- {JSON.stringify(isFirstPage)}
50
- </span>
51
- </code>
52
- </pre>
53
-
54
- <div className="flex gap-2">
55
- <select
56
- className="border rounded w-24 px-2"
57
- value={pageSize}
58
- onChange={(event) => setPageSize(Number(event.target.value))}
59
- >
60
- {TABLE_PAGE_SIZES.map((option: number, index) => (
61
- <option key={index} value={option}>
62
- {option}
63
- </option>
64
- ))}
65
- </select>
66
- <Button onClick={prev}>Página anterior</Button>
67
- <Button onClick={next}>Siguiente pagina</Button>
68
- <Button onClick={() => goTo(10)}>Ir a la pagina 10</Button>
718
+ return (
719
+ <div className="space-y-4">
720
+ <Table
721
+ data={pageData}
722
+ columns={EMPLOYEE_COLUMNS}
723
+ getRowKey={(row) => row.id}
724
+ />
725
+ <Pagination
726
+ totalItems={EMPLOYEES.length}
727
+ currentPage={page}
728
+ onCurrentPageChange={goTo}
729
+ pageSize={pageSize}
730
+ onPageSizeChange={setPageSize}
731
+ />
69
732
  </div>
70
- </div>
71
- );
733
+ );
734
+ },
72
735
  };