@create-lft-app/nextjs 3.2.0 → 3.3.0

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 (133) hide show
  1. package/README.md +549 -549
  2. package/package.json +48 -48
  3. package/template/.claude/skills/anti-patterns.md +150 -0
  4. package/template/.claude/skills/drizzle-schema.md +178 -0
  5. package/template/.claude/skills/formatting.md +56 -0
  6. package/template/.claude/skills/module-architecture.md +143 -0
  7. package/template/.claude/skills/supabase-server-actions.md +199 -0
  8. package/template/.claude/skills/ui-patterns.md +161 -0
  9. package/template/CLAUDE.md +114 -1239
  10. package/template/drizzle.config.ts +12 -12
  11. package/template/eslint.config.mjs +16 -16
  12. package/template/gitignore +36 -36
  13. package/template/next.config.ts +7 -7
  14. package/template/package.json +86 -86
  15. package/template/postcss.config.mjs +7 -7
  16. package/template/proxy.ts +12 -12
  17. package/template/public/logolft.svg +11 -11
  18. package/template/src/app/(auth)/dashboard/dashboard-content.tsx +124 -124
  19. package/template/src/app/(auth)/dashboard/page.tsx +9 -9
  20. package/template/src/app/(auth)/layout.tsx +7 -7
  21. package/template/src/app/(auth)/users/page.tsx +9 -9
  22. package/template/src/app/(auth)/users/users-content.tsx +26 -26
  23. package/template/src/app/(public)/layout.tsx +7 -7
  24. package/template/src/app/(public)/login/page.tsx +17 -17
  25. package/template/src/app/api/webhooks/route.ts +20 -20
  26. package/template/src/app/globals.css +249 -249
  27. package/template/src/app/layout.tsx +37 -37
  28. package/template/src/app/page.tsx +5 -5
  29. package/template/src/app/providers.tsx +27 -27
  30. package/template/src/components/layout/main-content.tsx +28 -28
  31. package/template/src/components/layout/sidebar-context.tsx +33 -33
  32. package/template/src/components/layout/sidebar.tsx +141 -141
  33. package/template/src/components/tables/data-table-column-header.tsx +68 -68
  34. package/template/src/components/tables/data-table-date-filter.tsx +203 -203
  35. package/template/src/components/tables/data-table-faceted-filter.tsx +185 -185
  36. package/template/src/components/tables/data-table-filters-dropdown.tsx +130 -130
  37. package/template/src/components/tables/data-table-number-filter.tsx +295 -295
  38. package/template/src/components/tables/data-table-pagination.tsx +99 -99
  39. package/template/src/components/tables/data-table-toolbar.tsx +140 -140
  40. package/template/src/components/tables/data-table-view-options.tsx +63 -63
  41. package/template/src/components/tables/data-table.tsx +148 -148
  42. package/template/src/components/tables/index.ts +9 -9
  43. package/template/src/components/ui/accordion.tsx +58 -58
  44. package/template/src/components/ui/alert-dialog.tsx +165 -165
  45. package/template/src/components/ui/alert.tsx +66 -66
  46. package/template/src/components/ui/animations/index.ts +44 -44
  47. package/template/src/components/ui/avatar.tsx +55 -55
  48. package/template/src/components/ui/badge.tsx +50 -50
  49. package/template/src/components/ui/button.tsx +118 -118
  50. package/template/src/components/ui/calendar.tsx +220 -220
  51. package/template/src/components/ui/card.tsx +113 -113
  52. package/template/src/components/ui/checkbox.tsx +38 -38
  53. package/template/src/components/ui/collapsible.tsx +33 -33
  54. package/template/src/components/ui/command.tsx +196 -196
  55. package/template/src/components/ui/dialog.tsx +156 -156
  56. package/template/src/components/ui/dropdown-menu.tsx +280 -280
  57. package/template/src/components/ui/form.tsx +171 -171
  58. package/template/src/components/ui/icons.tsx +167 -167
  59. package/template/src/components/ui/input.tsx +28 -28
  60. package/template/src/components/ui/label.tsx +25 -25
  61. package/template/src/components/ui/motion.tsx +197 -197
  62. package/template/src/components/ui/page-transition.tsx +166 -166
  63. package/template/src/components/ui/popover.tsx +59 -59
  64. package/template/src/components/ui/progress.tsx +32 -32
  65. package/template/src/components/ui/radio-group.tsx +45 -45
  66. package/template/src/components/ui/scroll-area.tsx +63 -63
  67. package/template/src/components/ui/select.tsx +208 -208
  68. package/template/src/components/ui/separator.tsx +28 -28
  69. package/template/src/components/ui/sheet.tsx +170 -170
  70. package/template/src/components/ui/sidebar.tsx +726 -726
  71. package/template/src/components/ui/skeleton.tsx +15 -15
  72. package/template/src/components/ui/slider.tsx +58 -58
  73. package/template/src/components/ui/sonner.tsx +47 -47
  74. package/template/src/components/ui/spinner.tsx +27 -27
  75. package/template/src/components/ui/submit-button.tsx +47 -47
  76. package/template/src/components/ui/switch.tsx +31 -31
  77. package/template/src/components/ui/table.tsx +120 -120
  78. package/template/src/components/ui/tabs.tsx +75 -75
  79. package/template/src/components/ui/textarea.tsx +26 -26
  80. package/template/src/components/ui/tooltip.tsx +70 -70
  81. package/template/src/config/navigation.ts +59 -59
  82. package/template/src/config/roles.ts +27 -27
  83. package/template/src/config/site.ts +12 -12
  84. package/template/src/db/index.ts +12 -12
  85. package/template/src/db/schema/index.ts +1 -1
  86. package/template/src/db/schema/users.ts +16 -16
  87. package/template/src/db/seed.ts +39 -39
  88. package/template/src/hooks/index.ts +3 -3
  89. package/template/src/hooks/use-mobile.ts +21 -21
  90. package/template/src/hooks/useDataTable.ts +82 -82
  91. package/template/src/hooks/useDebounce.ts +49 -49
  92. package/template/src/hooks/useMediaQuery.ts +36 -36
  93. package/template/src/lib/date/config.ts +36 -36
  94. package/template/src/lib/date/formatters.ts +127 -127
  95. package/template/src/lib/date/index.ts +26 -26
  96. package/template/src/lib/excel/exporter.ts +89 -89
  97. package/template/src/lib/excel/index.ts +14 -14
  98. package/template/src/lib/excel/parser.ts +96 -96
  99. package/template/src/lib/query-client.ts +35 -35
  100. package/template/src/lib/supabase/admin.ts +23 -23
  101. package/template/src/lib/supabase/client.ts +11 -11
  102. package/template/src/lib/supabase/proxy.ts +67 -67
  103. package/template/src/lib/supabase/server.ts +38 -38
  104. package/template/src/lib/supabase/types.ts +53 -53
  105. package/template/src/lib/utils.ts +6 -6
  106. package/template/src/lib/validations/common.ts +75 -75
  107. package/template/src/lib/validations/index.ts +20 -20
  108. package/template/src/modules/auth/actions/auth-actions.ts +59 -59
  109. package/template/src/modules/auth/components/login-form.tsx +68 -68
  110. package/template/src/modules/auth/hooks/useAuth.ts +38 -38
  111. package/template/src/modules/auth/hooks/useAuthMutations.ts +43 -43
  112. package/template/src/modules/auth/hooks/useAuthQueries.ts +43 -43
  113. package/template/src/modules/auth/index.ts +12 -12
  114. package/template/src/modules/auth/schemas/auth.schema.ts +32 -32
  115. package/template/src/modules/auth/stores/useAuthStore.ts +37 -37
  116. package/template/src/modules/users/actions/users-actions.ts +166 -166
  117. package/template/src/modules/users/columns.tsx +106 -106
  118. package/template/src/modules/users/components/users-list.tsx +48 -48
  119. package/template/src/modules/users/hooks/useUsers.ts +39 -39
  120. package/template/src/modules/users/hooks/useUsersMutations.ts +55 -55
  121. package/template/src/modules/users/hooks/useUsersQueries.ts +35 -35
  122. package/template/src/modules/users/index.ts +30 -30
  123. package/template/src/modules/users/schemas/users.schema.ts +51 -51
  124. package/template/src/modules/users/stores/useUsersStore.ts +60 -60
  125. package/template/src/modules/users/types/auth-user.types.ts +42 -42
  126. package/template/src/modules/users/utils/user-mapper.ts +32 -32
  127. package/template/src/stores/index.ts +1 -1
  128. package/template/src/stores/useUiStore.ts +55 -55
  129. package/template/src/types/api.ts +28 -28
  130. package/template/src/types/index.ts +2 -2
  131. package/template/src/types/table.ts +34 -34
  132. package/template/supabase/config.toml +94 -94
  133. package/template/tsconfig.json +42 -42
@@ -1,99 +1,99 @@
1
- 'use client'
2
-
3
- import { Table } from '@tanstack/react-table'
4
- import {
5
- ChevronLeft,
6
- ChevronRight,
7
- ChevronsLeft,
8
- ChevronsRight,
9
- } from 'lucide-react'
10
-
11
- import { Button } from '@/components/ui/button'
12
- import {
13
- Select,
14
- SelectContent,
15
- SelectItem,
16
- SelectTrigger,
17
- SelectValue,
18
- } from '@/components/ui/select'
19
-
20
- interface DataTablePaginationProps<TData> {
21
- table: Table<TData>
22
- }
23
-
24
- export function DataTablePagination<TData>({
25
- table,
26
- }: DataTablePaginationProps<TData>) {
27
- return (
28
- <div className="flex items-center justify-between px-2">
29
- <div className="flex-1 text-sm text-muted-foreground">
30
- {table.getFilteredSelectedRowModel().rows.length} de{' '}
31
- {table.getFilteredRowModel().rows.length} fila(s) seleccionada(s).
32
- </div>
33
- <div className="flex items-center space-x-6 lg:space-x-8">
34
- <div className="flex items-center space-x-2">
35
- <p className="text-sm font-medium">Filas por página</p>
36
- <Select
37
- value={`${table.getState().pagination.pageSize}`}
38
- onValueChange={(value) => {
39
- table.setPageSize(Number(value))
40
- }}
41
- >
42
- <SelectTrigger className="h-8 w-[70px]">
43
- <SelectValue placeholder={table.getState().pagination.pageSize} />
44
- </SelectTrigger>
45
- <SelectContent side="top">
46
- {[10, 20, 30, 40, 50].map((pageSize) => (
47
- <SelectItem key={pageSize} value={`${pageSize}`}>
48
- {pageSize}
49
- </SelectItem>
50
- ))}
51
- </SelectContent>
52
- </Select>
53
- </div>
54
- <div className="flex w-[100px] items-center justify-center text-sm font-medium">
55
- Página {table.getState().pagination.pageIndex + 1} de{' '}
56
- {table.getPageCount()}
57
- </div>
58
- <div className="flex items-center space-x-2">
59
- <Button
60
- variant="outline"
61
- className="hidden h-8 w-8 p-0 lg:flex"
62
- onClick={() => table.setPageIndex(0)}
63
- disabled={!table.getCanPreviousPage()}
64
- >
65
- <span className="sr-only">Ir a primera página</span>
66
- <ChevronsLeft className="h-4 w-4" />
67
- </Button>
68
- <Button
69
- variant="outline"
70
- className="h-8 w-8 p-0"
71
- onClick={() => table.previousPage()}
72
- disabled={!table.getCanPreviousPage()}
73
- >
74
- <span className="sr-only">Ir a página anterior</span>
75
- <ChevronLeft className="h-4 w-4" />
76
- </Button>
77
- <Button
78
- variant="outline"
79
- className="h-8 w-8 p-0"
80
- onClick={() => table.nextPage()}
81
- disabled={!table.getCanNextPage()}
82
- >
83
- <span className="sr-only">Ir a página siguiente</span>
84
- <ChevronRight className="h-4 w-4" />
85
- </Button>
86
- <Button
87
- variant="outline"
88
- className="hidden h-8 w-8 p-0 lg:flex"
89
- onClick={() => table.setPageIndex(table.getPageCount() - 1)}
90
- disabled={!table.getCanNextPage()}
91
- >
92
- <span className="sr-only">Ir a última página</span>
93
- <ChevronsRight className="h-4 w-4" />
94
- </Button>
95
- </div>
96
- </div>
97
- </div>
98
- )
99
- }
1
+ 'use client'
2
+
3
+ import { Table } from '@tanstack/react-table'
4
+ import {
5
+ ChevronLeft,
6
+ ChevronRight,
7
+ ChevronsLeft,
8
+ ChevronsRight,
9
+ } from 'lucide-react'
10
+
11
+ import { Button } from '@/components/ui/button'
12
+ import {
13
+ Select,
14
+ SelectContent,
15
+ SelectItem,
16
+ SelectTrigger,
17
+ SelectValue,
18
+ } from '@/components/ui/select'
19
+
20
+ interface DataTablePaginationProps<TData> {
21
+ table: Table<TData>
22
+ }
23
+
24
+ export function DataTablePagination<TData>({
25
+ table,
26
+ }: DataTablePaginationProps<TData>) {
27
+ return (
28
+ <div className="flex items-center justify-between px-2">
29
+ <div className="flex-1 text-sm text-muted-foreground">
30
+ {table.getFilteredSelectedRowModel().rows.length} de{' '}
31
+ {table.getFilteredRowModel().rows.length} fila(s) seleccionada(s).
32
+ </div>
33
+ <div className="flex items-center space-x-6 lg:space-x-8">
34
+ <div className="flex items-center space-x-2">
35
+ <p className="text-sm font-medium">Filas por página</p>
36
+ <Select
37
+ value={`${table.getState().pagination.pageSize}`}
38
+ onValueChange={(value) => {
39
+ table.setPageSize(Number(value))
40
+ }}
41
+ >
42
+ <SelectTrigger className="h-8 w-[70px]">
43
+ <SelectValue placeholder={table.getState().pagination.pageSize} />
44
+ </SelectTrigger>
45
+ <SelectContent side="top">
46
+ {[10, 20, 30, 40, 50].map((pageSize) => (
47
+ <SelectItem key={pageSize} value={`${pageSize}`}>
48
+ {pageSize}
49
+ </SelectItem>
50
+ ))}
51
+ </SelectContent>
52
+ </Select>
53
+ </div>
54
+ <div className="flex w-[100px] items-center justify-center text-sm font-medium">
55
+ Página {table.getState().pagination.pageIndex + 1} de{' '}
56
+ {table.getPageCount()}
57
+ </div>
58
+ <div className="flex items-center space-x-2">
59
+ <Button
60
+ variant="outline"
61
+ className="hidden h-8 w-8 p-0 lg:flex"
62
+ onClick={() => table.setPageIndex(0)}
63
+ disabled={!table.getCanPreviousPage()}
64
+ >
65
+ <span className="sr-only">Ir a primera página</span>
66
+ <ChevronsLeft className="h-4 w-4" />
67
+ </Button>
68
+ <Button
69
+ variant="outline"
70
+ className="h-8 w-8 p-0"
71
+ onClick={() => table.previousPage()}
72
+ disabled={!table.getCanPreviousPage()}
73
+ >
74
+ <span className="sr-only">Ir a página anterior</span>
75
+ <ChevronLeft className="h-4 w-4" />
76
+ </Button>
77
+ <Button
78
+ variant="outline"
79
+ className="h-8 w-8 p-0"
80
+ onClick={() => table.nextPage()}
81
+ disabled={!table.getCanNextPage()}
82
+ >
83
+ <span className="sr-only">Ir a página siguiente</span>
84
+ <ChevronRight className="h-4 w-4" />
85
+ </Button>
86
+ <Button
87
+ variant="outline"
88
+ className="hidden h-8 w-8 p-0 lg:flex"
89
+ onClick={() => table.setPageIndex(table.getPageCount() - 1)}
90
+ disabled={!table.getCanNextPage()}
91
+ >
92
+ <span className="sr-only">Ir a última página</span>
93
+ <ChevronsRight className="h-4 w-4" />
94
+ </Button>
95
+ </div>
96
+ </div>
97
+ </div>
98
+ )
99
+ }
@@ -1,140 +1,140 @@
1
- 'use client'
2
-
3
- import * as React from 'react'
4
- import { Table } from '@tanstack/react-table'
5
- import { Filter, X } from 'lucide-react'
6
-
7
- import { Button } from '@/components/ui/button'
8
- import { Input } from '@/components/ui/input'
9
- import { DataTableFacetedFilter } from './data-table-faceted-filter'
10
- import { DataTableDateFilter } from './data-table-date-filter'
11
- import { DataTableNumberFilter } from './data-table-number-filter'
12
- import { DataTableFiltersDropdown } from './data-table-filters-dropdown'
13
- import type { FilterConfig } from './data-table'
14
-
15
- interface DataTableToolbarProps<TData> {
16
- table: Table<TData>
17
- searchKey?: string
18
- searchPlaceholder?: string
19
- filters?: FilterConfig[]
20
- /** Modo de visualización: 'dropdown' agrupa filtros, 'inline' los muestra en línea, 'inline-collapsible' los muestra en línea pero ocultables */
21
- filterMode?: 'dropdown' | 'inline' | 'inline-collapsible'
22
- }
23
-
24
- export function DataTableToolbar<TData>({
25
- table,
26
- searchKey,
27
- searchPlaceholder = 'Buscar...',
28
- filters,
29
- filterMode = 'dropdown',
30
- }: DataTableToolbarProps<TData>) {
31
- const isFiltered = table.getState().columnFilters.length > 0
32
- const [showFilters, setShowFilters] = React.useState(false)
33
-
34
- const renderFilters = () => {
35
- return filters?.map((filter) => {
36
- const column = table.getColumn(filter.columnId)
37
- if (!column) return null
38
-
39
- if (filter.type === 'faceted' && filter.options) {
40
- return (
41
- <DataTableFacetedFilter
42
- key={filter.columnId}
43
- column={column}
44
- title={filter.title}
45
- options={filter.options}
46
- />
47
- )
48
- }
49
-
50
- if (filter.type === 'date-range') {
51
- return (
52
- <DataTableDateFilter
53
- key={filter.columnId}
54
- column={column}
55
- title={filter.title}
56
- />
57
- )
58
- }
59
-
60
- if (filter.type === 'number-range') {
61
- return (
62
- <DataTableNumberFilter
63
- key={filter.columnId}
64
- column={column}
65
- title={filter.title}
66
- format={filter.format}
67
- currencySymbol={filter.currencySymbol}
68
- step={filter.step}
69
- />
70
- )
71
- }
72
-
73
- return null
74
- })
75
- }
76
-
77
- return (
78
- <div className="flex flex-col gap-2">
79
- <div className="flex items-center justify-between">
80
- <div className="flex flex-1 flex-wrap items-center gap-2">
81
- {searchKey && (
82
- <Input
83
- placeholder={searchPlaceholder}
84
- value={(table.getColumn(searchKey)?.getFilterValue() as string) ?? ''}
85
- onChange={(event) =>
86
- table.getColumn(searchKey)?.setFilterValue(event.target.value)
87
- }
88
- className="h-8 w-[150px] lg:w-[250px]"
89
- />
90
- )}
91
-
92
- {/* Modo dropdown: un solo botón que agrupa filtros */}
93
- {filterMode === 'dropdown' && filters && filters.length > 0 && (
94
- <DataTableFiltersDropdown table={table} filters={filters} />
95
- )}
96
-
97
- {/* Modo inline: filtros visibles individualmente */}
98
- {filterMode === 'inline' && renderFilters()}
99
-
100
- {/* Modo inline-collapsible: botón para mostrar/ocultar filtros */}
101
- {filterMode === 'inline-collapsible' && filters && filters.length > 0 && (
102
- <Button
103
- variant="outline"
104
- size="sm"
105
- className="h-8 border-dashed"
106
- onClick={() => setShowFilters(!showFilters)}
107
- >
108
- <Filter className="mr-2 h-4 w-4" />
109
- Filtros
110
- {isFiltered && (
111
- <span className="ml-2 rounded-full bg-primary px-1.5 py-0.5 text-xs text-primary-foreground">
112
- {table.getState().columnFilters.length}
113
- </span>
114
- )}
115
- </Button>
116
- )}
117
-
118
- {/* Botón limpiar para modos inline */}
119
- {(filterMode === 'inline' || (filterMode === 'inline-collapsible' && showFilters)) && isFiltered && (
120
- <Button
121
- variant="ghost"
122
- onClick={() => table.resetColumnFilters()}
123
- className="h-8 px-2 lg:px-3"
124
- >
125
- Limpiar
126
- <X className="ml-2 h-4 w-4" />
127
- </Button>
128
- )}
129
- </div>
130
- </div>
131
-
132
- {/* Filtros inline colapsables - fila separada */}
133
- {filterMode === 'inline-collapsible' && showFilters && filters && filters.length > 0 && (
134
- <div className="flex flex-wrap items-center gap-2">
135
- {renderFilters()}
136
- </div>
137
- )}
138
- </div>
139
- )
140
- }
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { Table } from '@tanstack/react-table'
5
+ import { Filter, X } from 'lucide-react'
6
+
7
+ import { Button } from '@/components/ui/button'
8
+ import { Input } from '@/components/ui/input'
9
+ import { DataTableFacetedFilter } from './data-table-faceted-filter'
10
+ import { DataTableDateFilter } from './data-table-date-filter'
11
+ import { DataTableNumberFilter } from './data-table-number-filter'
12
+ import { DataTableFiltersDropdown } from './data-table-filters-dropdown'
13
+ import type { FilterConfig } from './data-table'
14
+
15
+ interface DataTableToolbarProps<TData> {
16
+ table: Table<TData>
17
+ searchKey?: string
18
+ searchPlaceholder?: string
19
+ filters?: FilterConfig[]
20
+ /** Modo de visualización: 'dropdown' agrupa filtros, 'inline' los muestra en línea, 'inline-collapsible' los muestra en línea pero ocultables */
21
+ filterMode?: 'dropdown' | 'inline' | 'inline-collapsible'
22
+ }
23
+
24
+ export function DataTableToolbar<TData>({
25
+ table,
26
+ searchKey,
27
+ searchPlaceholder = 'Buscar...',
28
+ filters,
29
+ filterMode = 'dropdown',
30
+ }: DataTableToolbarProps<TData>) {
31
+ const isFiltered = table.getState().columnFilters.length > 0
32
+ const [showFilters, setShowFilters] = React.useState(false)
33
+
34
+ const renderFilters = () => {
35
+ return filters?.map((filter) => {
36
+ const column = table.getColumn(filter.columnId)
37
+ if (!column) return null
38
+
39
+ if (filter.type === 'faceted' && filter.options) {
40
+ return (
41
+ <DataTableFacetedFilter
42
+ key={filter.columnId}
43
+ column={column}
44
+ title={filter.title}
45
+ options={filter.options}
46
+ />
47
+ )
48
+ }
49
+
50
+ if (filter.type === 'date-range') {
51
+ return (
52
+ <DataTableDateFilter
53
+ key={filter.columnId}
54
+ column={column}
55
+ title={filter.title}
56
+ />
57
+ )
58
+ }
59
+
60
+ if (filter.type === 'number-range') {
61
+ return (
62
+ <DataTableNumberFilter
63
+ key={filter.columnId}
64
+ column={column}
65
+ title={filter.title}
66
+ format={filter.format}
67
+ currencySymbol={filter.currencySymbol}
68
+ step={filter.step}
69
+ />
70
+ )
71
+ }
72
+
73
+ return null
74
+ })
75
+ }
76
+
77
+ return (
78
+ <div className="flex flex-col gap-2">
79
+ <div className="flex items-center justify-between">
80
+ <div className="flex flex-1 flex-wrap items-center gap-2">
81
+ {searchKey && (
82
+ <Input
83
+ placeholder={searchPlaceholder}
84
+ value={(table.getColumn(searchKey)?.getFilterValue() as string) ?? ''}
85
+ onChange={(event) =>
86
+ table.getColumn(searchKey)?.setFilterValue(event.target.value)
87
+ }
88
+ className="h-8 w-[150px] lg:w-[250px]"
89
+ />
90
+ )}
91
+
92
+ {/* Modo dropdown: un solo botón que agrupa filtros */}
93
+ {filterMode === 'dropdown' && filters && filters.length > 0 && (
94
+ <DataTableFiltersDropdown table={table} filters={filters} />
95
+ )}
96
+
97
+ {/* Modo inline: filtros visibles individualmente */}
98
+ {filterMode === 'inline' && renderFilters()}
99
+
100
+ {/* Modo inline-collapsible: botón para mostrar/ocultar filtros */}
101
+ {filterMode === 'inline-collapsible' && filters && filters.length > 0 && (
102
+ <Button
103
+ variant="outline"
104
+ size="sm"
105
+ className="h-8 border-dashed"
106
+ onClick={() => setShowFilters(!showFilters)}
107
+ >
108
+ <Filter className="mr-2 h-4 w-4" />
109
+ Filtros
110
+ {isFiltered && (
111
+ <span className="ml-2 rounded-full bg-primary px-1.5 py-0.5 text-xs text-primary-foreground">
112
+ {table.getState().columnFilters.length}
113
+ </span>
114
+ )}
115
+ </Button>
116
+ )}
117
+
118
+ {/* Botón limpiar para modos inline */}
119
+ {(filterMode === 'inline' || (filterMode === 'inline-collapsible' && showFilters)) && isFiltered && (
120
+ <Button
121
+ variant="ghost"
122
+ onClick={() => table.resetColumnFilters()}
123
+ className="h-8 px-2 lg:px-3"
124
+ >
125
+ Limpiar
126
+ <X className="ml-2 h-4 w-4" />
127
+ </Button>
128
+ )}
129
+ </div>
130
+ </div>
131
+
132
+ {/* Filtros inline colapsables - fila separada */}
133
+ {filterMode === 'inline-collapsible' && showFilters && filters && filters.length > 0 && (
134
+ <div className="flex flex-wrap items-center gap-2">
135
+ {renderFilters()}
136
+ </div>
137
+ )}
138
+ </div>
139
+ )
140
+ }
@@ -1,63 +1,63 @@
1
- 'use client'
2
-
3
- import * as React from 'react'
4
- import { Table } from '@tanstack/react-table'
5
- import { Settings2 } from 'lucide-react'
6
-
7
- import { Button } from '@/components/ui/button'
8
- import {
9
- DropdownMenu,
10
- DropdownMenuCheckboxItem,
11
- DropdownMenuContent,
12
- DropdownMenuLabel,
13
- DropdownMenuSeparator,
14
- DropdownMenuTrigger,
15
- } from '@/components/ui/dropdown-menu'
16
-
17
- interface DataTableViewOptionsProps<TData> {
18
- table: Table<TData>
19
- }
20
-
21
- export function DataTableViewOptions<TData>({
22
- table,
23
- }: DataTableViewOptionsProps<TData>) {
24
- const [open, setOpen] = React.useState(false)
25
-
26
- return (
27
- <DropdownMenu open={open} onOpenChange={setOpen}>
28
- <DropdownMenuTrigger asChild>
29
- <Button
30
- variant="ghost"
31
- size="icon"
32
- className="h-8 w-8"
33
- >
34
- <Settings2 className="h-4 w-4" />
35
- <span className="sr-only">Columnas</span>
36
- </Button>
37
- </DropdownMenuTrigger>
38
- <DropdownMenuContent align="end" className="w-[150px]">
39
- <DropdownMenuLabel>Mostrar columnas</DropdownMenuLabel>
40
- <DropdownMenuSeparator />
41
- {table
42
- .getAllColumns()
43
- .filter(
44
- (column) =>
45
- typeof column.accessorFn !== 'undefined' && column.getCanHide()
46
- )
47
- .map((column) => {
48
- return (
49
- <DropdownMenuCheckboxItem
50
- key={column.id}
51
- className="capitalize"
52
- checked={column.getIsVisible()}
53
- onSelect={(e) => e.preventDefault()}
54
- onCheckedChange={(value) => column.toggleVisibility(!!value)}
55
- >
56
- {column.id}
57
- </DropdownMenuCheckboxItem>
58
- )
59
- })}
60
- </DropdownMenuContent>
61
- </DropdownMenu>
62
- )
63
- }
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { Table } from '@tanstack/react-table'
5
+ import { Settings2 } from 'lucide-react'
6
+
7
+ import { Button } from '@/components/ui/button'
8
+ import {
9
+ DropdownMenu,
10
+ DropdownMenuCheckboxItem,
11
+ DropdownMenuContent,
12
+ DropdownMenuLabel,
13
+ DropdownMenuSeparator,
14
+ DropdownMenuTrigger,
15
+ } from '@/components/ui/dropdown-menu'
16
+
17
+ interface DataTableViewOptionsProps<TData> {
18
+ table: Table<TData>
19
+ }
20
+
21
+ export function DataTableViewOptions<TData>({
22
+ table,
23
+ }: DataTableViewOptionsProps<TData>) {
24
+ const [open, setOpen] = React.useState(false)
25
+
26
+ return (
27
+ <DropdownMenu open={open} onOpenChange={setOpen}>
28
+ <DropdownMenuTrigger asChild>
29
+ <Button
30
+ variant="ghost"
31
+ size="icon"
32
+ className="h-8 w-8"
33
+ >
34
+ <Settings2 className="h-4 w-4" />
35
+ <span className="sr-only">Columnas</span>
36
+ </Button>
37
+ </DropdownMenuTrigger>
38
+ <DropdownMenuContent align="end" className="w-[150px]">
39
+ <DropdownMenuLabel>Mostrar columnas</DropdownMenuLabel>
40
+ <DropdownMenuSeparator />
41
+ {table
42
+ .getAllColumns()
43
+ .filter(
44
+ (column) =>
45
+ typeof column.accessorFn !== 'undefined' && column.getCanHide()
46
+ )
47
+ .map((column) => {
48
+ return (
49
+ <DropdownMenuCheckboxItem
50
+ key={column.id}
51
+ className="capitalize"
52
+ checked={column.getIsVisible()}
53
+ onSelect={(e) => e.preventDefault()}
54
+ onCheckedChange={(value) => column.toggleVisibility(!!value)}
55
+ >
56
+ {column.id}
57
+ </DropdownMenuCheckboxItem>
58
+ )
59
+ })}
60
+ </DropdownMenuContent>
61
+ </DropdownMenu>
62
+ )
63
+ }