@create-lft-app/nextjs 3.1.0 → 3.2.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 (128) hide show
  1. package/README.md +549 -549
  2. package/package.json +48 -48
  3. package/template/CLAUDE.md +1239 -279
  4. package/template/drizzle.config.ts +12 -12
  5. package/template/eslint.config.mjs +16 -16
  6. package/template/gitignore +36 -36
  7. package/template/next.config.ts +7 -7
  8. package/template/package.json +86 -86
  9. package/template/postcss.config.mjs +7 -7
  10. package/template/proxy.ts +12 -12
  11. package/template/public/logolft.svg +11 -11
  12. package/template/src/app/(auth)/dashboard/dashboard-content.tsx +124 -124
  13. package/template/src/app/(auth)/dashboard/page.tsx +9 -9
  14. package/template/src/app/(auth)/layout.tsx +7 -7
  15. package/template/src/app/(auth)/users/page.tsx +9 -9
  16. package/template/src/app/(auth)/users/users-content.tsx +26 -26
  17. package/template/src/app/(public)/layout.tsx +7 -7
  18. package/template/src/app/(public)/login/page.tsx +17 -17
  19. package/template/src/app/api/webhooks/route.ts +20 -20
  20. package/template/src/app/globals.css +249 -249
  21. package/template/src/app/layout.tsx +37 -37
  22. package/template/src/app/page.tsx +5 -5
  23. package/template/src/app/providers.tsx +27 -27
  24. package/template/src/components/layout/main-content.tsx +28 -28
  25. package/template/src/components/layout/sidebar-context.tsx +33 -33
  26. package/template/src/components/layout/sidebar.tsx +141 -146
  27. package/template/src/components/tables/data-table-column-header.tsx +68 -68
  28. package/template/src/components/tables/data-table-date-filter.tsx +203 -0
  29. package/template/src/components/tables/data-table-faceted-filter.tsx +185 -0
  30. package/template/src/components/tables/data-table-filters-dropdown.tsx +130 -0
  31. package/template/src/components/tables/data-table-number-filter.tsx +295 -0
  32. package/template/src/components/tables/data-table-pagination.tsx +99 -99
  33. package/template/src/components/tables/data-table-toolbar.tsx +140 -50
  34. package/template/src/components/tables/data-table-view-options.tsx +63 -59
  35. package/template/src/components/tables/data-table.tsx +148 -128
  36. package/template/src/components/tables/index.ts +9 -5
  37. package/template/src/components/ui/accordion.tsx +58 -58
  38. package/template/src/components/ui/alert-dialog.tsx +165 -165
  39. package/template/src/components/ui/alert.tsx +66 -66
  40. package/template/src/components/ui/animations/index.ts +44 -44
  41. package/template/src/components/ui/avatar.tsx +55 -55
  42. package/template/src/components/ui/badge.tsx +50 -50
  43. package/template/src/components/ui/button.tsx +118 -118
  44. package/template/src/components/ui/calendar.tsx +220 -220
  45. package/template/src/components/ui/card.tsx +113 -113
  46. package/template/src/components/ui/checkbox.tsx +38 -38
  47. package/template/src/components/ui/collapsible.tsx +33 -33
  48. package/template/src/components/ui/command.tsx +196 -196
  49. package/template/src/components/ui/dialog.tsx +156 -156
  50. package/template/src/components/ui/dropdown-menu.tsx +280 -280
  51. package/template/src/components/ui/form.tsx +171 -171
  52. package/template/src/components/ui/icons.tsx +167 -167
  53. package/template/src/components/ui/input.tsx +28 -28
  54. package/template/src/components/ui/label.tsx +25 -25
  55. package/template/src/components/ui/motion.tsx +197 -197
  56. package/template/src/components/ui/page-transition.tsx +166 -166
  57. package/template/src/components/ui/popover.tsx +59 -59
  58. package/template/src/components/ui/progress.tsx +32 -32
  59. package/template/src/components/ui/radio-group.tsx +45 -45
  60. package/template/src/components/ui/scroll-area.tsx +63 -63
  61. package/template/src/components/ui/select.tsx +208 -208
  62. package/template/src/components/ui/separator.tsx +28 -28
  63. package/template/src/components/ui/sheet.tsx +170 -170
  64. package/template/src/components/ui/sidebar.tsx +726 -726
  65. package/template/src/components/ui/skeleton.tsx +15 -15
  66. package/template/src/components/ui/slider.tsx +58 -58
  67. package/template/src/components/ui/sonner.tsx +47 -47
  68. package/template/src/components/ui/spinner.tsx +27 -27
  69. package/template/src/components/ui/submit-button.tsx +47 -47
  70. package/template/src/components/ui/switch.tsx +31 -31
  71. package/template/src/components/ui/table.tsx +120 -120
  72. package/template/src/components/ui/tabs.tsx +75 -75
  73. package/template/src/components/ui/textarea.tsx +26 -26
  74. package/template/src/components/ui/tooltip.tsx +70 -70
  75. package/template/src/config/navigation.ts +59 -69
  76. package/template/src/config/roles.ts +27 -0
  77. package/template/src/config/site.ts +12 -12
  78. package/template/src/db/index.ts +12 -12
  79. package/template/src/db/schema/index.ts +1 -1
  80. package/template/src/db/schema/users.ts +16 -16
  81. package/template/src/db/seed.ts +39 -39
  82. package/template/src/hooks/index.ts +3 -3
  83. package/template/src/hooks/use-mobile.ts +21 -21
  84. package/template/src/hooks/useDataTable.ts +82 -82
  85. package/template/src/hooks/useDebounce.ts +49 -49
  86. package/template/src/hooks/useMediaQuery.ts +36 -36
  87. package/template/src/lib/date/config.ts +36 -34
  88. package/template/src/lib/date/formatters.ts +127 -120
  89. package/template/src/lib/date/index.ts +26 -19
  90. package/template/src/lib/excel/exporter.ts +89 -89
  91. package/template/src/lib/excel/index.ts +14 -14
  92. package/template/src/lib/excel/parser.ts +96 -96
  93. package/template/src/lib/query-client.ts +35 -35
  94. package/template/src/lib/supabase/admin.ts +23 -0
  95. package/template/src/lib/supabase/client.ts +11 -11
  96. package/template/src/lib/supabase/proxy.ts +67 -67
  97. package/template/src/lib/supabase/server.ts +38 -38
  98. package/template/src/lib/supabase/types.ts +53 -53
  99. package/template/src/lib/utils.ts +6 -6
  100. package/template/src/lib/validations/common.ts +75 -75
  101. package/template/src/lib/validations/index.ts +20 -20
  102. package/template/src/modules/auth/actions/auth-actions.ts +59 -59
  103. package/template/src/modules/auth/components/login-form.tsx +68 -68
  104. package/template/src/modules/auth/hooks/useAuth.ts +38 -38
  105. package/template/src/modules/auth/hooks/useAuthMutations.ts +43 -43
  106. package/template/src/modules/auth/hooks/useAuthQueries.ts +43 -43
  107. package/template/src/modules/auth/index.ts +12 -12
  108. package/template/src/modules/auth/schemas/auth.schema.ts +32 -32
  109. package/template/src/modules/auth/stores/useAuthStore.ts +37 -37
  110. package/template/src/modules/users/actions/users-actions.ts +166 -94
  111. package/template/src/modules/users/columns.tsx +106 -86
  112. package/template/src/modules/users/components/users-list.tsx +48 -22
  113. package/template/src/modules/users/hooks/useUsers.ts +39 -39
  114. package/template/src/modules/users/hooks/useUsersMutations.ts +55 -55
  115. package/template/src/modules/users/hooks/useUsersQueries.ts +35 -35
  116. package/template/src/modules/users/index.ts +30 -12
  117. package/template/src/modules/users/schemas/users.schema.ts +51 -23
  118. package/template/src/modules/users/stores/useUsersStore.ts +60 -60
  119. package/template/src/modules/users/types/auth-user.types.ts +42 -0
  120. package/template/src/modules/users/utils/user-mapper.ts +32 -0
  121. package/template/src/stores/index.ts +1 -1
  122. package/template/src/stores/useUiStore.ts +55 -55
  123. package/template/src/types/api.ts +28 -28
  124. package/template/src/types/index.ts +2 -2
  125. package/template/src/types/table.ts +34 -34
  126. package/template/supabase/config.toml +94 -94
  127. package/template/tsconfig.json +42 -42
  128. package/template/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,203 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { Column } from '@tanstack/react-table'
5
+ import { CalendarIcon, X } from 'lucide-react'
6
+ import { DateRange } from 'react-day-picker'
7
+
8
+ import { cn } from '@/lib/utils'
9
+ import { formatDateShort } from '@/lib/date'
10
+ import { Button } from '@/components/ui/button'
11
+ import { Calendar } from '@/components/ui/calendar'
12
+ import {
13
+ Popover,
14
+ PopoverContent,
15
+ PopoverTrigger,
16
+ } from '@/components/ui/popover'
17
+
18
+ interface DataTableDateFilterProps<TData> {
19
+ column?: Column<TData, unknown>
20
+ title?: string
21
+ }
22
+
23
+ export function DataTableDateFilter<TData>({
24
+ column,
25
+ title = 'Fecha',
26
+ }: DataTableDateFilterProps<TData>) {
27
+ const filterValue = column?.getFilterValue() as DateRange | undefined
28
+ const [open, setOpen] = React.useState(false)
29
+
30
+ // Sincronizar estado local con el filtro de la columna
31
+ // Esto permite que "Limpiar" del toolbar resetee este filtro
32
+ React.useEffect(() => {
33
+ if (!filterValue) {
34
+ setDate(undefined)
35
+ }
36
+ }, [filterValue])
37
+
38
+ const [date, setDate] = React.useState<DateRange | undefined>(filterValue)
39
+
40
+ const handleSelect = (range: DateRange | undefined) => {
41
+ setDate(range)
42
+ // No aplicamos el filtro inmediatamente, esperamos al botón Aplicar
43
+ }
44
+
45
+ const handleApply = () => {
46
+ column?.setFilterValue(date)
47
+ setOpen(false)
48
+ }
49
+
50
+ const handleClear = () => {
51
+ setDate(undefined)
52
+ column?.setFilterValue(undefined)
53
+ }
54
+
55
+ const handleOpenChange = (isOpen: boolean) => {
56
+ if (!isOpen) {
57
+ // Al cerrar sin aplicar, resetear al valor actual del filtro
58
+ setDate(filterValue)
59
+ }
60
+ setOpen(isOpen)
61
+ }
62
+
63
+ const hasFilter = date?.from || date?.to
64
+
65
+ return (
66
+ <Popover open={open} onOpenChange={handleOpenChange}>
67
+ <PopoverTrigger asChild>
68
+ <Button
69
+ variant="outline"
70
+ size="sm"
71
+ className={cn(
72
+ 'h-8 border-dashed',
73
+ hasFilter && 'border-solid'
74
+ )}
75
+ >
76
+ <CalendarIcon className="mr-2 h-4 w-4" />
77
+ {hasFilter ? (
78
+ <span className="flex items-center gap-1">
79
+ {date?.from && formatDateShort(date.from)}
80
+ {date?.to && ` - ${formatDateShort(date.to)}`}
81
+ </span>
82
+ ) : (
83
+ title
84
+ )}
85
+ {hasFilter && (
86
+ <span
87
+ role="button"
88
+ tabIndex={0}
89
+ onClick={(e) => {
90
+ e.stopPropagation()
91
+ handleClear()
92
+ }}
93
+ onKeyDown={(e) => {
94
+ if (e.key === 'Enter' || e.key === ' ') {
95
+ e.stopPropagation()
96
+ handleClear()
97
+ }
98
+ }}
99
+ className="ml-2 rounded-full p-0.5 hover:bg-muted"
100
+ >
101
+ <X className="h-3 w-3" />
102
+ </span>
103
+ )}
104
+ </Button>
105
+ </PopoverTrigger>
106
+ <PopoverContent
107
+ className="w-auto p-0"
108
+ side="bottom"
109
+ align="start"
110
+ sideOffset={4}
111
+ >
112
+ <div className="flex flex-col">
113
+ <div className="flex items-center gap-2 border-b border-border p-3">
114
+ <Button
115
+ variant="ghost"
116
+ size="sm"
117
+ className="h-7 text-xs"
118
+ onClick={() => {
119
+ const today = new Date()
120
+ setDate({ from: today, to: today })
121
+ }}
122
+ >
123
+ Hoy
124
+ </Button>
125
+ <Button
126
+ variant="ghost"
127
+ size="sm"
128
+ className="h-7 text-xs"
129
+ onClick={() => {
130
+ const today = new Date()
131
+ const weekAgo = new Date(today)
132
+ weekAgo.setDate(today.getDate() - 7)
133
+ setDate({ from: weekAgo, to: today })
134
+ }}
135
+ >
136
+ Última semana
137
+ </Button>
138
+ <Button
139
+ variant="ghost"
140
+ size="sm"
141
+ className="h-7 text-xs"
142
+ onClick={() => {
143
+ const today = new Date()
144
+ const monthAgo = new Date(today)
145
+ monthAgo.setMonth(today.getMonth() - 1)
146
+ setDate({ from: monthAgo, to: today })
147
+ }}
148
+ >
149
+ Último mes
150
+ </Button>
151
+ </div>
152
+ <Calendar
153
+ mode="range"
154
+ defaultMonth={date?.from}
155
+ selected={date}
156
+ onSelect={handleSelect}
157
+ numberOfMonths={2}
158
+ />
159
+ <div className="flex gap-2 border-t border-border p-3">
160
+ <Button
161
+ variant="outline"
162
+ size="sm"
163
+ className="flex-1"
164
+ onClick={handleClear}
165
+ >
166
+ Limpiar
167
+ </Button>
168
+ <Button
169
+ size="sm"
170
+ className="flex-1"
171
+ onClick={handleApply}
172
+ >
173
+ Aplicar
174
+ </Button>
175
+ </div>
176
+ </div>
177
+ </PopoverContent>
178
+ </Popover>
179
+ )
180
+ }
181
+
182
+ // Filter function para usar con TanStack Table
183
+ export function dateRangeFilterFn<TData>(
184
+ row: { getValue: (id: string) => unknown },
185
+ columnId: string,
186
+ filterValue: DateRange | undefined
187
+ ): boolean {
188
+ if (!filterValue?.from) return true
189
+
190
+ const cellValue = row.getValue(columnId)
191
+ if (!cellValue) return false
192
+
193
+ const date = new Date(cellValue as string)
194
+ const from = filterValue.from
195
+ const to = filterValue.to || from
196
+
197
+ // Reset hours for comparison
198
+ const dateOnly = new Date(date.getFullYear(), date.getMonth(), date.getDate())
199
+ const fromOnly = new Date(from.getFullYear(), from.getMonth(), from.getDate())
200
+ const toOnly = new Date(to.getFullYear(), to.getMonth(), to.getDate())
201
+
202
+ return dateOnly >= fromOnly && dateOnly <= toOnly
203
+ }
@@ -0,0 +1,185 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { Column } from '@tanstack/react-table'
5
+ import { Check, PlusCircle } from 'lucide-react'
6
+
7
+ import { cn } from '@/lib/utils'
8
+ import { Badge } from '@/components/ui/badge'
9
+ import { Button } from '@/components/ui/button'
10
+ import {
11
+ Command,
12
+ CommandEmpty,
13
+ CommandGroup,
14
+ CommandInput,
15
+ CommandItem,
16
+ CommandList,
17
+ CommandSeparator,
18
+ } from '@/components/ui/command'
19
+ import {
20
+ Popover,
21
+ PopoverContent,
22
+ PopoverTrigger,
23
+ } from '@/components/ui/popover'
24
+ import { Separator } from '@/components/ui/separator'
25
+
26
+ export interface FacetedFilterOption {
27
+ label: string
28
+ value: string
29
+ icon?: React.ComponentType<{ className?: string }>
30
+ }
31
+
32
+ interface DataTableFacetedFilterProps<TData, TValue> {
33
+ column?: Column<TData, TValue>
34
+ title?: string
35
+ options: FacetedFilterOption[]
36
+ }
37
+
38
+ export function DataTableFacetedFilter<TData, TValue>({
39
+ column,
40
+ title,
41
+ options,
42
+ }: DataTableFacetedFilterProps<TData, TValue>) {
43
+ const facets = column?.getFacetedUniqueValues()
44
+ const filterValue = column?.getFilterValue() as string[] | undefined
45
+ const [open, setOpen] = React.useState(false)
46
+ const [selectedValues, setSelectedValues] = React.useState<Set<string>>(
47
+ new Set(filterValue)
48
+ )
49
+
50
+ // Sincronizar estado local con el filtro de la columna
51
+ React.useEffect(() => {
52
+ setSelectedValues(new Set(filterValue))
53
+ }, [filterValue])
54
+
55
+ const handleApply = () => {
56
+ const filterValues = Array.from(selectedValues)
57
+ column?.setFilterValue(filterValues.length ? filterValues : undefined)
58
+ setOpen(false)
59
+ }
60
+
61
+ const handleClear = () => {
62
+ setSelectedValues(new Set())
63
+ column?.setFilterValue(undefined)
64
+ }
65
+
66
+ const handleOpenChange = (isOpen: boolean) => {
67
+ if (!isOpen) {
68
+ // Al cerrar sin aplicar, resetear al valor actual del filtro
69
+ setSelectedValues(new Set(filterValue))
70
+ }
71
+ setOpen(isOpen)
72
+ }
73
+
74
+ return (
75
+ <Popover open={open} onOpenChange={handleOpenChange}>
76
+ <PopoverTrigger asChild>
77
+ <Button
78
+ variant="outline"
79
+ size="sm"
80
+ className="h-8 border-dashed"
81
+ >
82
+ <PlusCircle className="mr-2 h-4 w-4" />
83
+ {title}
84
+ {selectedValues.size > 0 && (
85
+ <>
86
+ <Separator orientation="vertical" className="mx-2 h-4" />
87
+ <Badge
88
+ variant="secondary"
89
+ className="rounded-sm px-1 font-normal lg:hidden"
90
+ >
91
+ {selectedValues.size}
92
+ </Badge>
93
+ <div className="hidden space-x-1 lg:flex">
94
+ {selectedValues.size > 2 ? (
95
+ <Badge
96
+ variant="secondary"
97
+ className="rounded-sm px-1 font-normal"
98
+ >
99
+ {selectedValues.size} seleccionados
100
+ </Badge>
101
+ ) : (
102
+ options
103
+ .filter((option) => selectedValues.has(option.value))
104
+ .map((option) => (
105
+ <Badge
106
+ variant="secondary"
107
+ key={option.value}
108
+ className="rounded-sm px-1 font-normal"
109
+ >
110
+ {option.label}
111
+ </Badge>
112
+ ))
113
+ )}
114
+ </div>
115
+ </>
116
+ )}
117
+ </Button>
118
+ </PopoverTrigger>
119
+ <PopoverContent className="w-[200px] p-0" side="bottom" align="start" sideOffset={4}>
120
+ <Command>
121
+ <CommandInput placeholder={title} />
122
+ <CommandList>
123
+ <CommandEmpty>Sin resultados.</CommandEmpty>
124
+ <CommandGroup>
125
+ {options.map((option) => {
126
+ const isSelected = selectedValues.has(option.value)
127
+ return (
128
+ <CommandItem
129
+ key={option.value}
130
+ onSelect={() => {
131
+ const newSelected = new Set(selectedValues)
132
+ if (isSelected) {
133
+ newSelected.delete(option.value)
134
+ } else {
135
+ newSelected.add(option.value)
136
+ }
137
+ setSelectedValues(newSelected)
138
+ }}
139
+ >
140
+ <div
141
+ className={cn(
142
+ 'mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary',
143
+ isSelected
144
+ ? 'bg-primary text-primary-foreground'
145
+ : 'opacity-50 [&_svg]:invisible'
146
+ )}
147
+ >
148
+ <Check className="h-4 w-4" />
149
+ </div>
150
+ {option.icon && (
151
+ <option.icon className="mr-2 h-4 w-4 text-muted-foreground" />
152
+ )}
153
+ <span>{option.label}</span>
154
+ {facets?.get(option.value) && (
155
+ <span className="ml-auto flex h-4 w-4 items-center justify-center font-mono text-xs">
156
+ {facets.get(option.value)}
157
+ </span>
158
+ )}
159
+ </CommandItem>
160
+ )
161
+ })}
162
+ </CommandGroup>
163
+ </CommandList>
164
+ </Command>
165
+ <div className="flex gap-2 border-t border-border p-2">
166
+ <Button
167
+ variant="outline"
168
+ size="sm"
169
+ className="flex-1"
170
+ onClick={handleClear}
171
+ >
172
+ Limpiar
173
+ </Button>
174
+ <Button
175
+ size="sm"
176
+ className="flex-1"
177
+ onClick={handleApply}
178
+ >
179
+ Aplicar
180
+ </Button>
181
+ </div>
182
+ </PopoverContent>
183
+ </Popover>
184
+ )
185
+ }
@@ -0,0 +1,130 @@
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 { cn } from '@/lib/utils'
8
+ import { Badge } from '@/components/ui/badge'
9
+ import { Button } from '@/components/ui/button'
10
+ import {
11
+ Popover,
12
+ PopoverContent,
13
+ PopoverTrigger,
14
+ } from '@/components/ui/popover'
15
+ import { Separator } from '@/components/ui/separator'
16
+ import { DataTableFacetedFilter } from './data-table-faceted-filter'
17
+ import { DataTableDateFilter } from './data-table-date-filter'
18
+ import { DataTableNumberFilter } from './data-table-number-filter'
19
+ import type { FilterConfig } from './data-table'
20
+
21
+ interface DataTableFiltersDropdownProps<TData> {
22
+ table: Table<TData>
23
+ filters: FilterConfig[]
24
+ }
25
+
26
+ export function DataTableFiltersDropdown<TData>({
27
+ table,
28
+ filters,
29
+ }: DataTableFiltersDropdownProps<TData>) {
30
+ const [open, setOpen] = React.useState(false)
31
+
32
+ // Contar filtros activos (excluyendo el search)
33
+ const activeFiltersCount = table.getState().columnFilters.filter(
34
+ (filter) => filters.some((f) => f.columnId === filter.id)
35
+ ).length
36
+
37
+ const handleClearAll = () => {
38
+ // Solo limpiar los filtros configurados, no el search
39
+ filters.forEach((filter) => {
40
+ table.getColumn(filter.columnId)?.setFilterValue(undefined)
41
+ })
42
+ }
43
+
44
+ return (
45
+ <Popover open={open} onOpenChange={setOpen}>
46
+ <PopoverTrigger asChild>
47
+ <Button
48
+ variant="outline"
49
+ size="sm"
50
+ className={cn(
51
+ 'h-8 border-dashed',
52
+ activeFiltersCount > 0 && 'border-solid'
53
+ )}
54
+ >
55
+ <Filter className="mr-2 h-4 w-4" />
56
+ Filtros
57
+ {activeFiltersCount > 0 && (
58
+ <Badge
59
+ variant="secondary"
60
+ className="ml-2 rounded-sm px-1 font-normal"
61
+ >
62
+ {activeFiltersCount}
63
+ </Badge>
64
+ )}
65
+ </Button>
66
+ </PopoverTrigger>
67
+ <PopoverContent className="w-[320px] p-0" align="start">
68
+ <div className="flex flex-col">
69
+ {/* Header */}
70
+ <div className="flex items-center justify-between border-b border-border px-4 py-3">
71
+ <span className="text-sm font-medium">Filtros</span>
72
+ {activeFiltersCount > 0 && (
73
+ <Button
74
+ variant="ghost"
75
+ size="sm"
76
+ className="h-7 px-2 text-xs"
77
+ onClick={handleClearAll}
78
+ >
79
+ Limpiar todo
80
+ <X className="ml-1 h-3 w-3" />
81
+ </Button>
82
+ )}
83
+ </div>
84
+
85
+ {/* Filters */}
86
+ <div className="flex flex-col gap-3 p-4">
87
+ {filters.map((filter, index) => {
88
+ const column = table.getColumn(filter.columnId)
89
+ if (!column) return null
90
+
91
+ return (
92
+ <div key={filter.columnId} className="flex flex-col gap-2">
93
+ {index > 0 && <Separator className="my-1" />}
94
+ <label className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
95
+ {filter.title}
96
+ </label>
97
+
98
+ {filter.type === 'faceted' && filter.options && (
99
+ <DataTableFacetedFilter
100
+ column={column}
101
+ title={filter.title}
102
+ options={filter.options}
103
+ />
104
+ )}
105
+
106
+ {filter.type === 'date-range' && (
107
+ <DataTableDateFilter
108
+ column={column}
109
+ title={filter.title}
110
+ />
111
+ )}
112
+
113
+ {filter.type === 'number-range' && (
114
+ <DataTableNumberFilter
115
+ column={column}
116
+ title={filter.title}
117
+ format={filter.format}
118
+ currencySymbol={filter.currencySymbol}
119
+ step={filter.step}
120
+ />
121
+ )}
122
+ </div>
123
+ )
124
+ })}
125
+ </div>
126
+ </div>
127
+ </PopoverContent>
128
+ </Popover>
129
+ )
130
+ }