@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.
- package/README.md +549 -549
- package/package.json +48 -48
- package/template/.claude/skills/anti-patterns.md +150 -0
- package/template/.claude/skills/drizzle-schema.md +178 -0
- package/template/.claude/skills/formatting.md +56 -0
- package/template/.claude/skills/module-architecture.md +143 -0
- package/template/.claude/skills/supabase-server-actions.md +199 -0
- package/template/.claude/skills/ui-patterns.md +161 -0
- package/template/CLAUDE.md +114 -1239
- package/template/drizzle.config.ts +12 -12
- package/template/eslint.config.mjs +16 -16
- package/template/gitignore +36 -36
- package/template/next.config.ts +7 -7
- package/template/package.json +86 -86
- package/template/postcss.config.mjs +7 -7
- package/template/proxy.ts +12 -12
- package/template/public/logolft.svg +11 -11
- package/template/src/app/(auth)/dashboard/dashboard-content.tsx +124 -124
- package/template/src/app/(auth)/dashboard/page.tsx +9 -9
- package/template/src/app/(auth)/layout.tsx +7 -7
- package/template/src/app/(auth)/users/page.tsx +9 -9
- package/template/src/app/(auth)/users/users-content.tsx +26 -26
- package/template/src/app/(public)/layout.tsx +7 -7
- package/template/src/app/(public)/login/page.tsx +17 -17
- package/template/src/app/api/webhooks/route.ts +20 -20
- package/template/src/app/globals.css +249 -249
- package/template/src/app/layout.tsx +37 -37
- package/template/src/app/page.tsx +5 -5
- package/template/src/app/providers.tsx +27 -27
- package/template/src/components/layout/main-content.tsx +28 -28
- package/template/src/components/layout/sidebar-context.tsx +33 -33
- package/template/src/components/layout/sidebar.tsx +141 -141
- package/template/src/components/tables/data-table-column-header.tsx +68 -68
- package/template/src/components/tables/data-table-date-filter.tsx +203 -203
- package/template/src/components/tables/data-table-faceted-filter.tsx +185 -185
- package/template/src/components/tables/data-table-filters-dropdown.tsx +130 -130
- package/template/src/components/tables/data-table-number-filter.tsx +295 -295
- package/template/src/components/tables/data-table-pagination.tsx +99 -99
- package/template/src/components/tables/data-table-toolbar.tsx +140 -140
- package/template/src/components/tables/data-table-view-options.tsx +63 -63
- package/template/src/components/tables/data-table.tsx +148 -148
- package/template/src/components/tables/index.ts +9 -9
- package/template/src/components/ui/accordion.tsx +58 -58
- package/template/src/components/ui/alert-dialog.tsx +165 -165
- package/template/src/components/ui/alert.tsx +66 -66
- package/template/src/components/ui/animations/index.ts +44 -44
- package/template/src/components/ui/avatar.tsx +55 -55
- package/template/src/components/ui/badge.tsx +50 -50
- package/template/src/components/ui/button.tsx +118 -118
- package/template/src/components/ui/calendar.tsx +220 -220
- package/template/src/components/ui/card.tsx +113 -113
- package/template/src/components/ui/checkbox.tsx +38 -38
- package/template/src/components/ui/collapsible.tsx +33 -33
- package/template/src/components/ui/command.tsx +196 -196
- package/template/src/components/ui/dialog.tsx +156 -156
- package/template/src/components/ui/dropdown-menu.tsx +280 -280
- package/template/src/components/ui/form.tsx +171 -171
- package/template/src/components/ui/icons.tsx +167 -167
- package/template/src/components/ui/input.tsx +28 -28
- package/template/src/components/ui/label.tsx +25 -25
- package/template/src/components/ui/motion.tsx +197 -197
- package/template/src/components/ui/page-transition.tsx +166 -166
- package/template/src/components/ui/popover.tsx +59 -59
- package/template/src/components/ui/progress.tsx +32 -32
- package/template/src/components/ui/radio-group.tsx +45 -45
- package/template/src/components/ui/scroll-area.tsx +63 -63
- package/template/src/components/ui/select.tsx +208 -208
- package/template/src/components/ui/separator.tsx +28 -28
- package/template/src/components/ui/sheet.tsx +170 -170
- package/template/src/components/ui/sidebar.tsx +726 -726
- package/template/src/components/ui/skeleton.tsx +15 -15
- package/template/src/components/ui/slider.tsx +58 -58
- package/template/src/components/ui/sonner.tsx +47 -47
- package/template/src/components/ui/spinner.tsx +27 -27
- package/template/src/components/ui/submit-button.tsx +47 -47
- package/template/src/components/ui/switch.tsx +31 -31
- package/template/src/components/ui/table.tsx +120 -120
- package/template/src/components/ui/tabs.tsx +75 -75
- package/template/src/components/ui/textarea.tsx +26 -26
- package/template/src/components/ui/tooltip.tsx +70 -70
- package/template/src/config/navigation.ts +59 -59
- package/template/src/config/roles.ts +27 -27
- package/template/src/config/site.ts +12 -12
- package/template/src/db/index.ts +12 -12
- package/template/src/db/schema/index.ts +1 -1
- package/template/src/db/schema/users.ts +16 -16
- package/template/src/db/seed.ts +39 -39
- package/template/src/hooks/index.ts +3 -3
- package/template/src/hooks/use-mobile.ts +21 -21
- package/template/src/hooks/useDataTable.ts +82 -82
- package/template/src/hooks/useDebounce.ts +49 -49
- package/template/src/hooks/useMediaQuery.ts +36 -36
- package/template/src/lib/date/config.ts +36 -36
- package/template/src/lib/date/formatters.ts +127 -127
- package/template/src/lib/date/index.ts +26 -26
- package/template/src/lib/excel/exporter.ts +89 -89
- package/template/src/lib/excel/index.ts +14 -14
- package/template/src/lib/excel/parser.ts +96 -96
- package/template/src/lib/query-client.ts +35 -35
- package/template/src/lib/supabase/admin.ts +23 -23
- package/template/src/lib/supabase/client.ts +11 -11
- package/template/src/lib/supabase/proxy.ts +67 -67
- package/template/src/lib/supabase/server.ts +38 -38
- package/template/src/lib/supabase/types.ts +53 -53
- package/template/src/lib/utils.ts +6 -6
- package/template/src/lib/validations/common.ts +75 -75
- package/template/src/lib/validations/index.ts +20 -20
- package/template/src/modules/auth/actions/auth-actions.ts +59 -59
- package/template/src/modules/auth/components/login-form.tsx +68 -68
- package/template/src/modules/auth/hooks/useAuth.ts +38 -38
- package/template/src/modules/auth/hooks/useAuthMutations.ts +43 -43
- package/template/src/modules/auth/hooks/useAuthQueries.ts +43 -43
- package/template/src/modules/auth/index.ts +12 -12
- package/template/src/modules/auth/schemas/auth.schema.ts +32 -32
- package/template/src/modules/auth/stores/useAuthStore.ts +37 -37
- package/template/src/modules/users/actions/users-actions.ts +166 -166
- package/template/src/modules/users/columns.tsx +106 -106
- package/template/src/modules/users/components/users-list.tsx +48 -48
- package/template/src/modules/users/hooks/useUsers.ts +39 -39
- package/template/src/modules/users/hooks/useUsersMutations.ts +55 -55
- package/template/src/modules/users/hooks/useUsersQueries.ts +35 -35
- package/template/src/modules/users/index.ts +30 -30
- package/template/src/modules/users/schemas/users.schema.ts +51 -51
- package/template/src/modules/users/stores/useUsersStore.ts +60 -60
- package/template/src/modules/users/types/auth-user.types.ts +42 -42
- package/template/src/modules/users/utils/user-mapper.ts +32 -32
- package/template/src/stores/index.ts +1 -1
- package/template/src/stores/useUiStore.ts +55 -55
- package/template/src/types/api.ts +28 -28
- package/template/src/types/index.ts +2 -2
- package/template/src/types/table.ts +34 -34
- package/template/supabase/config.toml +94 -94
- package/template/tsconfig.json +42 -42
|
@@ -1,203 +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
|
-
}
|
|
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
|
+
}
|