@create-lft-app/nextjs 1.0.2 → 3.0.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/package.json +9 -3
- package/template/CLAUDE.md +279 -0
- package/template/drizzle.config.ts +12 -0
- package/template/package.json +31 -6
- package/template/proxy.ts +12 -0
- package/template/src/app/(auth)/dashboard/dashboard-content.tsx +124 -0
- package/template/src/app/(auth)/dashboard/page.tsx +9 -0
- package/template/src/app/(auth)/layout.tsx +7 -0
- package/template/src/app/(auth)/users/page.tsx +9 -0
- package/template/src/app/(auth)/users/users-content.tsx +26 -0
- package/template/src/app/(public)/layout.tsx +7 -0
- package/template/src/app/(public)/login/page.tsx +17 -0
- package/template/src/app/api/webhooks/route.ts +20 -0
- package/template/src/app/layout.tsx +13 -12
- package/template/src/app/providers.tsx +27 -0
- package/template/src/components/layout/{midday-sidebar.tsx → sidebar.tsx} +2 -7
- package/template/src/components/tables/data-table-column-header.tsx +68 -0
- package/template/src/components/tables/data-table-pagination.tsx +99 -0
- package/template/src/components/tables/data-table-toolbar.tsx +50 -0
- package/template/src/components/tables/data-table-view-options.tsx +59 -0
- package/template/src/components/tables/data-table.tsx +128 -0
- package/template/src/components/tables/index.ts +5 -0
- package/template/src/components/ui/animations/index.ts +44 -0
- package/template/src/components/ui/button.tsx +50 -21
- package/template/src/components/ui/card.tsx +27 -3
- package/template/src/components/ui/dialog.tsx +38 -35
- package/template/src/components/ui/motion.tsx +197 -0
- package/template/src/components/ui/page-transition.tsx +166 -0
- package/template/src/components/ui/sheet.tsx +65 -41
- package/template/src/config/navigation.ts +69 -0
- package/template/src/config/site.ts +12 -0
- package/template/src/db/index.ts +12 -0
- package/template/src/db/schema/index.ts +1 -0
- package/template/src/db/schema/users.ts +16 -0
- package/template/src/db/seed.ts +39 -0
- package/template/src/hooks/index.ts +3 -0
- package/template/src/hooks/useDataTable.ts +82 -0
- package/template/src/hooks/useDebounce.ts +49 -0
- package/template/src/hooks/useMediaQuery.ts +36 -0
- package/template/src/lib/date/config.ts +34 -0
- package/template/src/lib/date/formatters.ts +120 -0
- package/template/src/lib/date/index.ts +19 -0
- package/template/src/lib/excel/exporter.ts +89 -0
- package/template/src/lib/excel/index.ts +14 -0
- package/template/src/lib/excel/parser.ts +96 -0
- package/template/src/lib/query-client.ts +35 -0
- package/template/src/lib/supabase/client.ts +5 -2
- package/template/src/lib/supabase/proxy.ts +67 -0
- package/template/src/lib/supabase/server.ts +6 -4
- package/template/src/lib/supabase/types.ts +53 -0
- package/template/src/lib/validations/common.ts +75 -0
- package/template/src/lib/validations/index.ts +20 -0
- package/template/src/modules/auth/actions/auth-actions.ts +51 -4
- package/template/src/modules/auth/components/login-form.tsx +68 -0
- package/template/src/modules/auth/hooks/useAuth.ts +38 -0
- package/template/src/modules/auth/hooks/useAuthMutations.ts +43 -0
- package/template/src/modules/auth/hooks/useAuthQueries.ts +43 -0
- package/template/src/modules/auth/index.ts +12 -0
- package/template/src/modules/auth/schemas/auth.schema.ts +32 -0
- package/template/src/modules/auth/stores/useAuthStore.ts +37 -0
- package/template/src/modules/users/actions/users-actions.ts +94 -0
- package/template/src/modules/users/columns.tsx +86 -0
- package/template/src/modules/users/components/users-list.tsx +22 -0
- package/template/src/modules/users/hooks/useUsers.ts +39 -0
- package/template/src/modules/users/hooks/useUsersMutations.ts +55 -0
- package/template/src/modules/users/hooks/useUsersQueries.ts +35 -0
- package/template/src/modules/users/index.ts +12 -0
- package/template/src/modules/users/schemas/users.schema.ts +23 -0
- package/template/src/modules/users/stores/useUsersStore.ts +60 -0
- package/template/src/stores/index.ts +1 -0
- package/template/src/stores/useUiStore.ts +55 -0
- package/template/src/types/api.ts +28 -0
- package/template/src/types/index.ts +2 -0
- package/template/src/types/table.ts +34 -0
- package/template/supabase/config.toml +94 -0
- package/template/tsconfig.json +2 -1
- package/template/tsconfig.tsbuildinfo +1 -0
- package/template/next-env.d.ts +0 -6
- package/template/package-lock.json +0 -8454
- package/template/src/app/dashboard/page.tsx +0 -111
- package/template/src/components/dashboard/widget.tsx +0 -113
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
import { QueryClientProvider } from '@tanstack/react-query'
|
|
5
|
+
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
|
|
6
|
+
import { ThemeProvider } from 'next-themes'
|
|
7
|
+
import { Toaster } from 'sonner'
|
|
8
|
+
import { getQueryClient } from '@/lib/query-client'
|
|
9
|
+
|
|
10
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
11
|
+
const [queryClient] = useState(() => getQueryClient())
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<QueryClientProvider client={queryClient}>
|
|
15
|
+
<ThemeProvider
|
|
16
|
+
attribute="class"
|
|
17
|
+
defaultTheme="system"
|
|
18
|
+
enableSystem
|
|
19
|
+
disableTransitionOnChange
|
|
20
|
+
>
|
|
21
|
+
{children}
|
|
22
|
+
<Toaster richColors position="top-right" />
|
|
23
|
+
</ThemeProvider>
|
|
24
|
+
<ReactQueryDevtools initialIsOpen={false} />
|
|
25
|
+
</QueryClientProvider>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
@@ -20,15 +20,10 @@ const navItems: NavItem[] = [
|
|
|
20
20
|
icon: 'Dashboard',
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
|
-
path: '/
|
|
24
|
-
name: '
|
|
23
|
+
path: '/users',
|
|
24
|
+
name: 'Usuarios',
|
|
25
25
|
icon: 'Users',
|
|
26
26
|
},
|
|
27
|
-
{
|
|
28
|
-
path: '/documents',
|
|
29
|
-
name: 'Documentos',
|
|
30
|
-
icon: 'Documents',
|
|
31
|
-
},
|
|
32
27
|
{
|
|
33
28
|
path: '/settings',
|
|
34
29
|
name: 'Configuración',
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Column } from '@tanstack/react-table'
|
|
4
|
+
import { ArrowDown, ArrowUp, ChevronsUpDown, EyeOff } from 'lucide-react'
|
|
5
|
+
|
|
6
|
+
import { cn } from '@/lib/utils'
|
|
7
|
+
import { Button } from '@/components/ui/button'
|
|
8
|
+
import {
|
|
9
|
+
DropdownMenu,
|
|
10
|
+
DropdownMenuContent,
|
|
11
|
+
DropdownMenuItem,
|
|
12
|
+
DropdownMenuSeparator,
|
|
13
|
+
DropdownMenuTrigger,
|
|
14
|
+
} from '@/components/ui/dropdown-menu'
|
|
15
|
+
|
|
16
|
+
interface DataTableColumnHeaderProps<TData, TValue>
|
|
17
|
+
extends React.HTMLAttributes<HTMLDivElement> {
|
|
18
|
+
column: Column<TData, TValue>
|
|
19
|
+
title: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function DataTableColumnHeader<TData, TValue>({
|
|
23
|
+
column,
|
|
24
|
+
title,
|
|
25
|
+
className,
|
|
26
|
+
}: DataTableColumnHeaderProps<TData, TValue>) {
|
|
27
|
+
if (!column.getCanSort()) {
|
|
28
|
+
return <div className={cn(className)}>{title}</div>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div className={cn('flex items-center space-x-2', className)}>
|
|
33
|
+
<DropdownMenu>
|
|
34
|
+
<DropdownMenuTrigger asChild>
|
|
35
|
+
<Button
|
|
36
|
+
variant="ghost"
|
|
37
|
+
size="sm"
|
|
38
|
+
className="-ml-3 h-8 data-[state=open]:bg-accent"
|
|
39
|
+
>
|
|
40
|
+
<span>{title}</span>
|
|
41
|
+
{column.getIsSorted() === 'desc' ? (
|
|
42
|
+
<ArrowDown className="ml-2 h-4 w-4" />
|
|
43
|
+
) : column.getIsSorted() === 'asc' ? (
|
|
44
|
+
<ArrowUp className="ml-2 h-4 w-4" />
|
|
45
|
+
) : (
|
|
46
|
+
<ChevronsUpDown className="ml-2 h-4 w-4" />
|
|
47
|
+
)}
|
|
48
|
+
</Button>
|
|
49
|
+
</DropdownMenuTrigger>
|
|
50
|
+
<DropdownMenuContent align="start">
|
|
51
|
+
<DropdownMenuItem onClick={() => column.toggleSorting(false)}>
|
|
52
|
+
<ArrowUp className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
|
|
53
|
+
Ascendente
|
|
54
|
+
</DropdownMenuItem>
|
|
55
|
+
<DropdownMenuItem onClick={() => column.toggleSorting(true)}>
|
|
56
|
+
<ArrowDown className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
|
|
57
|
+
Descendente
|
|
58
|
+
</DropdownMenuItem>
|
|
59
|
+
<DropdownMenuSeparator />
|
|
60
|
+
<DropdownMenuItem onClick={() => column.toggleVisibility(false)}>
|
|
61
|
+
<EyeOff className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
|
|
62
|
+
Ocultar
|
|
63
|
+
</DropdownMenuItem>
|
|
64
|
+
</DropdownMenuContent>
|
|
65
|
+
</DropdownMenu>
|
|
66
|
+
</div>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
@@ -0,0 +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
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Table } from '@tanstack/react-table'
|
|
4
|
+
import { X } from 'lucide-react'
|
|
5
|
+
|
|
6
|
+
import { Button } from '@/components/ui/button'
|
|
7
|
+
import { Input } from '@/components/ui/input'
|
|
8
|
+
import { DataTableViewOptions } from './data-table-view-options'
|
|
9
|
+
|
|
10
|
+
interface DataTableToolbarProps<TData> {
|
|
11
|
+
table: Table<TData>
|
|
12
|
+
searchKey?: string
|
|
13
|
+
searchPlaceholder?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function DataTableToolbar<TData>({
|
|
17
|
+
table,
|
|
18
|
+
searchKey,
|
|
19
|
+
searchPlaceholder = 'Buscar...',
|
|
20
|
+
}: DataTableToolbarProps<TData>) {
|
|
21
|
+
const isFiltered = table.getState().columnFilters.length > 0
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div className="flex items-center justify-between">
|
|
25
|
+
<div className="flex flex-1 items-center space-x-2">
|
|
26
|
+
{searchKey && (
|
|
27
|
+
<Input
|
|
28
|
+
placeholder={searchPlaceholder}
|
|
29
|
+
value={(table.getColumn(searchKey)?.getFilterValue() as string) ?? ''}
|
|
30
|
+
onChange={(event) =>
|
|
31
|
+
table.getColumn(searchKey)?.setFilterValue(event.target.value)
|
|
32
|
+
}
|
|
33
|
+
className="h-8 w-[150px] lg:w-[250px]"
|
|
34
|
+
/>
|
|
35
|
+
)}
|
|
36
|
+
{isFiltered && (
|
|
37
|
+
<Button
|
|
38
|
+
variant="ghost"
|
|
39
|
+
onClick={() => table.resetColumnFilters()}
|
|
40
|
+
className="h-8 px-2 lg:px-3"
|
|
41
|
+
>
|
|
42
|
+
Limpiar
|
|
43
|
+
<X className="ml-2 h-4 w-4" />
|
|
44
|
+
</Button>
|
|
45
|
+
)}
|
|
46
|
+
</div>
|
|
47
|
+
<DataTableViewOptions table={table} />
|
|
48
|
+
</div>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Table } from '@tanstack/react-table'
|
|
4
|
+
import { Settings2 } from 'lucide-react'
|
|
5
|
+
|
|
6
|
+
import { Button } from '@/components/ui/button'
|
|
7
|
+
import {
|
|
8
|
+
DropdownMenu,
|
|
9
|
+
DropdownMenuCheckboxItem,
|
|
10
|
+
DropdownMenuContent,
|
|
11
|
+
DropdownMenuLabel,
|
|
12
|
+
DropdownMenuSeparator,
|
|
13
|
+
DropdownMenuTrigger,
|
|
14
|
+
} from '@/components/ui/dropdown-menu'
|
|
15
|
+
|
|
16
|
+
interface DataTableViewOptionsProps<TData> {
|
|
17
|
+
table: Table<TData>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function DataTableViewOptions<TData>({
|
|
21
|
+
table,
|
|
22
|
+
}: DataTableViewOptionsProps<TData>) {
|
|
23
|
+
return (
|
|
24
|
+
<DropdownMenu>
|
|
25
|
+
<DropdownMenuTrigger asChild>
|
|
26
|
+
<Button
|
|
27
|
+
variant="outline"
|
|
28
|
+
size="sm"
|
|
29
|
+
className="ml-auto hidden h-8 lg:flex"
|
|
30
|
+
>
|
|
31
|
+
<Settings2 className="mr-2 h-4 w-4" />
|
|
32
|
+
Columnas
|
|
33
|
+
</Button>
|
|
34
|
+
</DropdownMenuTrigger>
|
|
35
|
+
<DropdownMenuContent align="end" className="w-[150px]">
|
|
36
|
+
<DropdownMenuLabel>Mostrar columnas</DropdownMenuLabel>
|
|
37
|
+
<DropdownMenuSeparator />
|
|
38
|
+
{table
|
|
39
|
+
.getAllColumns()
|
|
40
|
+
.filter(
|
|
41
|
+
(column) =>
|
|
42
|
+
typeof column.accessorFn !== 'undefined' && column.getCanHide()
|
|
43
|
+
)
|
|
44
|
+
.map((column) => {
|
|
45
|
+
return (
|
|
46
|
+
<DropdownMenuCheckboxItem
|
|
47
|
+
key={column.id}
|
|
48
|
+
className="capitalize"
|
|
49
|
+
checked={column.getIsVisible()}
|
|
50
|
+
onCheckedChange={(value) => column.toggleVisibility(!!value)}
|
|
51
|
+
>
|
|
52
|
+
{column.id}
|
|
53
|
+
</DropdownMenuCheckboxItem>
|
|
54
|
+
)
|
|
55
|
+
})}
|
|
56
|
+
</DropdownMenuContent>
|
|
57
|
+
</DropdownMenu>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import {
|
|
5
|
+
ColumnDef,
|
|
6
|
+
ColumnFiltersState,
|
|
7
|
+
SortingState,
|
|
8
|
+
VisibilityState,
|
|
9
|
+
flexRender,
|
|
10
|
+
getCoreRowModel,
|
|
11
|
+
getFilteredRowModel,
|
|
12
|
+
getPaginationRowModel,
|
|
13
|
+
getSortedRowModel,
|
|
14
|
+
useReactTable,
|
|
15
|
+
} from '@tanstack/react-table'
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
Table,
|
|
19
|
+
TableBody,
|
|
20
|
+
TableCell,
|
|
21
|
+
TableHead,
|
|
22
|
+
TableHeader,
|
|
23
|
+
TableRow,
|
|
24
|
+
} from '@/components/ui/table'
|
|
25
|
+
import { Input } from '@/components/ui/input'
|
|
26
|
+
import { DataTablePagination } from './data-table-pagination'
|
|
27
|
+
import { DataTableViewOptions } from './data-table-view-options'
|
|
28
|
+
|
|
29
|
+
interface DataTableProps<TData, TValue> {
|
|
30
|
+
columns: ColumnDef<TData, TValue>[]
|
|
31
|
+
data: TData[]
|
|
32
|
+
searchKey?: string
|
|
33
|
+
searchPlaceholder?: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function DataTable<TData, TValue>({
|
|
37
|
+
columns,
|
|
38
|
+
data,
|
|
39
|
+
searchKey,
|
|
40
|
+
searchPlaceholder = 'Buscar...',
|
|
41
|
+
}: DataTableProps<TData, TValue>) {
|
|
42
|
+
const [sorting, setSorting] = React.useState<SortingState>([])
|
|
43
|
+
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([])
|
|
44
|
+
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({})
|
|
45
|
+
const [rowSelection, setRowSelection] = React.useState({})
|
|
46
|
+
|
|
47
|
+
const table = useReactTable({
|
|
48
|
+
data,
|
|
49
|
+
columns,
|
|
50
|
+
getCoreRowModel: getCoreRowModel(),
|
|
51
|
+
getPaginationRowModel: getPaginationRowModel(),
|
|
52
|
+
onSortingChange: setSorting,
|
|
53
|
+
getSortedRowModel: getSortedRowModel(),
|
|
54
|
+
onColumnFiltersChange: setColumnFilters,
|
|
55
|
+
getFilteredRowModel: getFilteredRowModel(),
|
|
56
|
+
onColumnVisibilityChange: setColumnVisibility,
|
|
57
|
+
onRowSelectionChange: setRowSelection,
|
|
58
|
+
state: {
|
|
59
|
+
sorting,
|
|
60
|
+
columnFilters,
|
|
61
|
+
columnVisibility,
|
|
62
|
+
rowSelection,
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<div className="space-y-4">
|
|
68
|
+
<div className="flex items-center justify-between">
|
|
69
|
+
{searchKey && (
|
|
70
|
+
<Input
|
|
71
|
+
placeholder={searchPlaceholder}
|
|
72
|
+
value={(table.getColumn(searchKey)?.getFilterValue() as string) ?? ''}
|
|
73
|
+
onChange={(event) =>
|
|
74
|
+
table.getColumn(searchKey)?.setFilterValue(event.target.value)
|
|
75
|
+
}
|
|
76
|
+
className="max-w-sm"
|
|
77
|
+
/>
|
|
78
|
+
)}
|
|
79
|
+
<DataTableViewOptions table={table} />
|
|
80
|
+
</div>
|
|
81
|
+
<div className="rounded-md border">
|
|
82
|
+
<Table>
|
|
83
|
+
<TableHeader>
|
|
84
|
+
{table.getHeaderGroups().map((headerGroup) => (
|
|
85
|
+
<TableRow key={headerGroup.id}>
|
|
86
|
+
{headerGroup.headers.map((header) => {
|
|
87
|
+
return (
|
|
88
|
+
<TableHead key={header.id}>
|
|
89
|
+
{header.isPlaceholder
|
|
90
|
+
? null
|
|
91
|
+
: flexRender(
|
|
92
|
+
header.column.columnDef.header,
|
|
93
|
+
header.getContext()
|
|
94
|
+
)}
|
|
95
|
+
</TableHead>
|
|
96
|
+
)
|
|
97
|
+
})}
|
|
98
|
+
</TableRow>
|
|
99
|
+
))}
|
|
100
|
+
</TableHeader>
|
|
101
|
+
<TableBody>
|
|
102
|
+
{table.getRowModel().rows?.length ? (
|
|
103
|
+
table.getRowModel().rows.map((row) => (
|
|
104
|
+
<TableRow
|
|
105
|
+
key={row.id}
|
|
106
|
+
data-state={row.getIsSelected() && 'selected'}
|
|
107
|
+
>
|
|
108
|
+
{row.getVisibleCells().map((cell) => (
|
|
109
|
+
<TableCell key={cell.id}>
|
|
110
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
111
|
+
</TableCell>
|
|
112
|
+
))}
|
|
113
|
+
</TableRow>
|
|
114
|
+
))
|
|
115
|
+
) : (
|
|
116
|
+
<TableRow>
|
|
117
|
+
<TableCell colSpan={columns.length} className="h-24 text-center">
|
|
118
|
+
No hay resultados.
|
|
119
|
+
</TableCell>
|
|
120
|
+
</TableRow>
|
|
121
|
+
)}
|
|
122
|
+
</TableBody>
|
|
123
|
+
</Table>
|
|
124
|
+
</div>
|
|
125
|
+
<DataTablePagination table={table} />
|
|
126
|
+
</div>
|
|
127
|
+
)
|
|
128
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { DataTable } from './data-table'
|
|
2
|
+
export { DataTablePagination } from './data-table-pagination'
|
|
3
|
+
export { DataTableColumnHeader } from './data-table-column-header'
|
|
4
|
+
export { DataTableToolbar } from './data-table-toolbar'
|
|
5
|
+
export { DataTableViewOptions } from './data-table-view-options'
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Motion primitives y variantes
|
|
2
|
+
export {
|
|
3
|
+
MotionDiv,
|
|
4
|
+
MotionSpan,
|
|
5
|
+
MotionButton,
|
|
6
|
+
MotionUl,
|
|
7
|
+
MotionLi,
|
|
8
|
+
MotionNav,
|
|
9
|
+
MotionSection,
|
|
10
|
+
MotionArticle,
|
|
11
|
+
MotionHeader,
|
|
12
|
+
MotionFooter,
|
|
13
|
+
MotionMain,
|
|
14
|
+
MotionAside,
|
|
15
|
+
FadeIn,
|
|
16
|
+
ScaleIn,
|
|
17
|
+
StaggerList,
|
|
18
|
+
StaggerItem,
|
|
19
|
+
AnimatePresence,
|
|
20
|
+
// Variantes
|
|
21
|
+
fadeIn,
|
|
22
|
+
fadeInUp,
|
|
23
|
+
fadeInDown,
|
|
24
|
+
fadeInLeft,
|
|
25
|
+
fadeInRight,
|
|
26
|
+
scaleIn,
|
|
27
|
+
slideInFromBottom,
|
|
28
|
+
slideInFromTop,
|
|
29
|
+
staggerContainer,
|
|
30
|
+
staggerItem,
|
|
31
|
+
// Transiciones
|
|
32
|
+
springTransition,
|
|
33
|
+
easeTransition,
|
|
34
|
+
smoothTransition,
|
|
35
|
+
} from '../motion'
|
|
36
|
+
|
|
37
|
+
// Page transitions
|
|
38
|
+
export {
|
|
39
|
+
PageTransition,
|
|
40
|
+
PageSection,
|
|
41
|
+
PageHeader,
|
|
42
|
+
PageTitle,
|
|
43
|
+
PageDescription,
|
|
44
|
+
} from '../page-transition'
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
1
3
|
import * as React from "react"
|
|
2
4
|
import { Slot } from "@radix-ui/react-slot"
|
|
5
|
+
import { motion } from "framer-motion"
|
|
3
6
|
import { cva, type VariantProps } from "class-variance-authority"
|
|
4
|
-
|
|
5
7
|
import { cn } from "@/lib/utils"
|
|
6
8
|
|
|
7
9
|
const buttonVariants = cva(
|
|
@@ -19,7 +21,6 @@ const buttonVariants = cva(
|
|
|
19
21
|
default: [
|
|
20
22
|
"bg-primary text-primary-foreground",
|
|
21
23
|
"hover:bg-primary/90",
|
|
22
|
-
"active:scale-[0.98]",
|
|
23
24
|
].join(" "),
|
|
24
25
|
destructive: [
|
|
25
26
|
"bg-destructive text-destructive-foreground",
|
|
@@ -63,27 +64,55 @@ const buttonVariants = cva(
|
|
|
63
64
|
}
|
|
64
65
|
)
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
className,
|
|
68
|
-
variant = "default",
|
|
69
|
-
size = "default",
|
|
70
|
-
asChild = false,
|
|
71
|
-
...props
|
|
72
|
-
}: React.ComponentProps<"button"> &
|
|
67
|
+
type ButtonProps = React.ComponentProps<"button"> &
|
|
73
68
|
VariantProps<typeof buttonVariants> & {
|
|
74
69
|
asChild?: boolean
|
|
75
|
-
|
|
76
|
-
|
|
70
|
+
animate?: boolean
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
74
|
+
({ className, variant = "default", size = "default", asChild = false, animate = true, ...props }, ref) => {
|
|
75
|
+
if (asChild) {
|
|
76
|
+
return (
|
|
77
|
+
<Slot
|
|
78
|
+
ref={ref as React.Ref<HTMLElement>}
|
|
79
|
+
data-slot="button"
|
|
80
|
+
data-variant={variant}
|
|
81
|
+
data-size={size}
|
|
82
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
83
|
+
{...props}
|
|
84
|
+
/>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
77
87
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
+
if (!animate) {
|
|
89
|
+
return (
|
|
90
|
+
<button
|
|
91
|
+
ref={ref}
|
|
92
|
+
data-slot="button"
|
|
93
|
+
data-variant={variant}
|
|
94
|
+
data-size={size}
|
|
95
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
96
|
+
{...props}
|
|
97
|
+
/>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<motion.button
|
|
103
|
+
ref={ref}
|
|
104
|
+
data-slot="button"
|
|
105
|
+
data-variant={variant}
|
|
106
|
+
data-size={size}
|
|
107
|
+
whileHover={{ scale: 1.02 }}
|
|
108
|
+
whileTap={{ scale: 0.98 }}
|
|
109
|
+
transition={{ duration: 0.1 }}
|
|
110
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
111
|
+
{...(props as React.ComponentPropsWithoutRef<typeof motion.button>)}
|
|
112
|
+
/>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
Button.displayName = "Button"
|
|
88
117
|
|
|
89
118
|
export { Button, buttonVariants }
|
|
@@ -1,13 +1,37 @@
|
|
|
1
|
-
|
|
1
|
+
'use client'
|
|
2
2
|
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { motion, type HTMLMotionProps } from "framer-motion"
|
|
3
5
|
import { cn } from "@/lib/utils"
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
interface CardProps extends HTMLMotionProps<"div"> {
|
|
8
|
+
animate?: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function Card({ className, animate = true, ...props }: CardProps) {
|
|
12
|
+
if (!animate) {
|
|
13
|
+
return (
|
|
14
|
+
<div
|
|
15
|
+
data-slot="card"
|
|
16
|
+
className={cn(
|
|
17
|
+
"rounded-lg border border-border bg-card text-card-foreground",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...(props as React.ComponentProps<"div">)}
|
|
21
|
+
/>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
6
25
|
return (
|
|
7
|
-
<div
|
|
26
|
+
<motion.div
|
|
8
27
|
data-slot="card"
|
|
28
|
+
initial={{ opacity: 0, y: 10 }}
|
|
29
|
+
animate={{ opacity: 1, y: 0 }}
|
|
30
|
+
transition={{ duration: 0.3, ease: "easeOut" }}
|
|
31
|
+
whileHover={{ y: -2, transition: { duration: 0.2 } }}
|
|
9
32
|
className={cn(
|
|
10
33
|
"rounded-lg border border-border bg-card text-card-foreground",
|
|
34
|
+
"transition-shadow hover:shadow-md",
|
|
11
35
|
className
|
|
12
36
|
)}
|
|
13
37
|
{...props}
|