@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,28 +1,28 @@
1
- 'use client'
2
-
3
- import { cn } from '@/lib/utils'
4
- import { useSidebar } from './sidebar-context'
5
- import { ReactNode } from 'react'
6
-
7
- interface MainContentProps {
8
- children: ReactNode
9
- className?: string
10
- }
11
-
12
- export function MainContent({ children, className }: MainContentProps) {
13
- const { isExpanded } = useSidebar()
14
-
15
- return (
16
- <main
17
- className={cn(
18
- 'min-h-screen bg-background',
19
- 'transition-all duration-200 ease-[cubic-bezier(0.4,0,0.2,1)]',
20
- isExpanded ? 'md:ml-[240px] md:w-[calc(100%-240px)]' : 'md:ml-[70px] md:w-[calc(100%-70px)]',
21
- 'w-full',
22
- className
23
- )}
24
- >
25
- {children}
26
- </main>
27
- )
28
- }
1
+ 'use client'
2
+
3
+ import { cn } from '@/lib/utils'
4
+ import { useSidebar } from './sidebar-context'
5
+ import { ReactNode } from 'react'
6
+
7
+ interface MainContentProps {
8
+ children: ReactNode
9
+ className?: string
10
+ }
11
+
12
+ export function MainContent({ children, className }: MainContentProps) {
13
+ const { isExpanded } = useSidebar()
14
+
15
+ return (
16
+ <main
17
+ className={cn(
18
+ 'min-h-screen bg-background',
19
+ 'transition-all duration-200 ease-[cubic-bezier(0.4,0,0.2,1)]',
20
+ isExpanded ? 'md:ml-[240px] md:w-[calc(100%-240px)]' : 'md:ml-[70px] md:w-[calc(100%-70px)]',
21
+ 'w-full',
22
+ className
23
+ )}
24
+ >
25
+ {children}
26
+ </main>
27
+ )
28
+ }
@@ -1,33 +1,33 @@
1
- 'use client'
2
-
3
- import { createContext, useContext, useState, useCallback, ReactNode } from 'react'
4
-
5
- interface SidebarContextType {
6
- isExpanded: boolean
7
- setIsExpanded: (value: boolean) => void
8
- expand: () => void
9
- collapse: () => void
10
- }
11
-
12
- const SidebarContext = createContext<SidebarContextType | undefined>(undefined)
13
-
14
- export function SidebarProvider({ children }: { children: ReactNode }) {
15
- const [isExpanded, setIsExpanded] = useState(false)
16
-
17
- const expand = useCallback(() => setIsExpanded(true), [])
18
- const collapse = useCallback(() => setIsExpanded(false), [])
19
-
20
- return (
21
- <SidebarContext.Provider value={{ isExpanded, setIsExpanded, expand, collapse }}>
22
- {children}
23
- </SidebarContext.Provider>
24
- )
25
- }
26
-
27
- export function useSidebar() {
28
- const context = useContext(SidebarContext)
29
- if (context === undefined) {
30
- throw new Error('useSidebar must be used within a SidebarProvider')
31
- }
32
- return context
33
- }
1
+ 'use client'
2
+
3
+ import { createContext, useContext, useState, useCallback, ReactNode } from 'react'
4
+
5
+ interface SidebarContextType {
6
+ isExpanded: boolean
7
+ setIsExpanded: (value: boolean) => void
8
+ expand: () => void
9
+ collapse: () => void
10
+ }
11
+
12
+ const SidebarContext = createContext<SidebarContextType | undefined>(undefined)
13
+
14
+ export function SidebarProvider({ children }: { children: ReactNode }) {
15
+ const [isExpanded, setIsExpanded] = useState(false)
16
+
17
+ const expand = useCallback(() => setIsExpanded(true), [])
18
+ const collapse = useCallback(() => setIsExpanded(false), [])
19
+
20
+ return (
21
+ <SidebarContext.Provider value={{ isExpanded, setIsExpanded, expand, collapse }}>
22
+ {children}
23
+ </SidebarContext.Provider>
24
+ )
25
+ }
26
+
27
+ export function useSidebar() {
28
+ const context = useContext(SidebarContext)
29
+ if (context === undefined) {
30
+ throw new Error('useSidebar must be used within a SidebarProvider')
31
+ }
32
+ return context
33
+ }
@@ -1,141 +1,141 @@
1
- 'use client'
2
-
3
- import Link from 'next/link'
4
- import { usePathname } from 'next/navigation'
5
- import { useRef, useCallback } from 'react'
6
- import { cn } from '@/lib/utils'
7
- import { Icons } from '@/components/ui/icons'
8
- import { useSidebar } from './sidebar-context'
9
-
10
- interface NavItem {
11
- path: string
12
- name: string
13
- icon: keyof typeof Icons
14
- }
15
-
16
- const navItems: NavItem[] = [
17
- {
18
- path: '/dashboard',
19
- name: 'Dashboard',
20
- icon: 'Dashboard',
21
- },
22
- {
23
- path: '/users',
24
- name: 'Usuarios',
25
- icon: 'Users',
26
- },
27
- ]
28
-
29
- function MenuItem({
30
- item,
31
- isExpanded,
32
- pathname,
33
- }: {
34
- item: NavItem
35
- isExpanded: boolean
36
- pathname: string
37
- }) {
38
- const IconComponent = Icons[item.icon]
39
- const isActive = pathname === item.path || pathname.startsWith(item.path + '/')
40
-
41
- return (
42
- <li className="relative">
43
- <Link
44
- href={item.path}
45
- className={cn(
46
- 'h-[40px] flex items-center gap-3',
47
- 'mx-[10px] px-[10px]',
48
- 'rounded-md',
49
- 'transition-all duration-150',
50
- !isActive && 'hover:bg-accent',
51
- isActive && [
52
- 'bg-accent',
53
- 'border border-border',
54
- ]
55
- )}
56
- >
57
- <div className="w-5 h-5 flex items-center justify-center flex-shrink-0">
58
- <IconComponent
59
- size={20}
60
- className={cn(
61
- 'transition-colors',
62
- isActive ? 'text-foreground' : 'text-muted-foreground'
63
- )}
64
- />
65
- </div>
66
- {isExpanded && (
67
- <span
68
- className={cn(
69
- 'text-sm whitespace-nowrap',
70
- 'transition-all duration-150',
71
- isActive ? 'font-medium text-foreground' : 'text-muted-foreground'
72
- )}
73
- >
74
- {item.name}
75
- </span>
76
- )}
77
- </Link>
78
- </li>
79
- )
80
- }
81
-
82
- export function Sidebar() {
83
- const { isExpanded, setIsExpanded } = useSidebar()
84
- const pathname = usePathname()
85
- const sidebarRef = useRef<HTMLDivElement>(null)
86
- const hoverTimeoutRef = useRef<NodeJS.Timeout | null>(null)
87
-
88
- const handleMouseEnter = useCallback(() => {
89
- if (hoverTimeoutRef.current) {
90
- clearTimeout(hoverTimeoutRef.current)
91
- }
92
- setIsExpanded(true)
93
- }, [setIsExpanded])
94
-
95
- const handleMouseLeave = useCallback(() => {
96
- hoverTimeoutRef.current = setTimeout(() => {
97
- setIsExpanded(false)
98
- }, 100)
99
- }, [setIsExpanded])
100
-
101
- return (
102
- <aside
103
- ref={sidebarRef}
104
- className={cn(
105
- 'h-screen flex-shrink-0 flex-col justify-between fixed top-0 left-0 hidden md:flex z-50',
106
- 'bg-background border-r border-border',
107
- 'transition-[width] duration-200 ease-[cubic-bezier(0.4,0,0.2,1)]',
108
- isExpanded ? 'w-[240px]' : 'w-[70px]'
109
- )}
110
- onMouseEnter={handleMouseEnter}
111
- onMouseLeave={handleMouseLeave}
112
- >
113
- {/* Header / Logo */}
114
- <div
115
- className={cn(
116
- "absolute top-0 left-0 h-[70px] flex items-center justify-center bg-background border-b border-border transition-all duration-200 ease-[cubic-bezier(0.4,0,0.2,1)]",
117
- isExpanded ? "w-full" : "w-[69px]"
118
- )}
119
- >
120
- <Link href="/dashboard" className="absolute left-[22px] transition-none">
121
- {/* eslint-disable-next-line @next/next/no-img-element */}
122
- <img src="/logolft.svg" alt="Logo" className="h-7 w-7" />
123
- </Link>
124
- </div>
125
-
126
- {/* Navigation */}
127
- <nav className="flex-1 pt-[70px] pb-4 overflow-y-auto scrollbar-hide">
128
- <ul className="space-y-1">
129
- {navItems.map((item) => (
130
- <MenuItem
131
- key={item.path}
132
- item={item}
133
- isExpanded={isExpanded}
134
- pathname={pathname}
135
- />
136
- ))}
137
- </ul>
138
- </nav>
139
- </aside>
140
- )
141
- }
1
+ 'use client'
2
+
3
+ import Link from 'next/link'
4
+ import { usePathname } from 'next/navigation'
5
+ import { useRef, useCallback } from 'react'
6
+ import { cn } from '@/lib/utils'
7
+ import { Icons } from '@/components/ui/icons'
8
+ import { useSidebar } from './sidebar-context'
9
+
10
+ interface NavItem {
11
+ path: string
12
+ name: string
13
+ icon: keyof typeof Icons
14
+ }
15
+
16
+ const navItems: NavItem[] = [
17
+ {
18
+ path: '/dashboard',
19
+ name: 'Dashboard',
20
+ icon: 'Dashboard',
21
+ },
22
+ {
23
+ path: '/users',
24
+ name: 'Usuarios',
25
+ icon: 'Users',
26
+ },
27
+ ]
28
+
29
+ function MenuItem({
30
+ item,
31
+ isExpanded,
32
+ pathname,
33
+ }: {
34
+ item: NavItem
35
+ isExpanded: boolean
36
+ pathname: string
37
+ }) {
38
+ const IconComponent = Icons[item.icon]
39
+ const isActive = pathname === item.path || pathname.startsWith(item.path + '/')
40
+
41
+ return (
42
+ <li className="relative">
43
+ <Link
44
+ href={item.path}
45
+ className={cn(
46
+ 'h-[40px] flex items-center gap-3',
47
+ 'mx-[10px] px-[10px]',
48
+ 'rounded-md',
49
+ 'transition-all duration-150',
50
+ !isActive && 'hover:bg-accent',
51
+ isActive && [
52
+ 'bg-accent',
53
+ 'border border-border',
54
+ ]
55
+ )}
56
+ >
57
+ <div className="w-5 h-5 flex items-center justify-center flex-shrink-0">
58
+ <IconComponent
59
+ size={20}
60
+ className={cn(
61
+ 'transition-colors',
62
+ isActive ? 'text-foreground' : 'text-muted-foreground'
63
+ )}
64
+ />
65
+ </div>
66
+ {isExpanded && (
67
+ <span
68
+ className={cn(
69
+ 'text-sm whitespace-nowrap',
70
+ 'transition-all duration-150',
71
+ isActive ? 'font-medium text-foreground' : 'text-muted-foreground'
72
+ )}
73
+ >
74
+ {item.name}
75
+ </span>
76
+ )}
77
+ </Link>
78
+ </li>
79
+ )
80
+ }
81
+
82
+ export function Sidebar() {
83
+ const { isExpanded, setIsExpanded } = useSidebar()
84
+ const pathname = usePathname()
85
+ const sidebarRef = useRef<HTMLDivElement>(null)
86
+ const hoverTimeoutRef = useRef<NodeJS.Timeout | null>(null)
87
+
88
+ const handleMouseEnter = useCallback(() => {
89
+ if (hoverTimeoutRef.current) {
90
+ clearTimeout(hoverTimeoutRef.current)
91
+ }
92
+ setIsExpanded(true)
93
+ }, [setIsExpanded])
94
+
95
+ const handleMouseLeave = useCallback(() => {
96
+ hoverTimeoutRef.current = setTimeout(() => {
97
+ setIsExpanded(false)
98
+ }, 100)
99
+ }, [setIsExpanded])
100
+
101
+ return (
102
+ <aside
103
+ ref={sidebarRef}
104
+ className={cn(
105
+ 'h-screen flex-shrink-0 flex-col justify-between fixed top-0 left-0 hidden md:flex z-50',
106
+ 'bg-background border-r border-border',
107
+ 'transition-[width] duration-200 ease-[cubic-bezier(0.4,0,0.2,1)]',
108
+ isExpanded ? 'w-[240px]' : 'w-[70px]'
109
+ )}
110
+ onMouseEnter={handleMouseEnter}
111
+ onMouseLeave={handleMouseLeave}
112
+ >
113
+ {/* Header / Logo */}
114
+ <div
115
+ className={cn(
116
+ "absolute top-0 left-0 h-[70px] flex items-center justify-center bg-background border-b border-border transition-all duration-200 ease-[cubic-bezier(0.4,0,0.2,1)]",
117
+ isExpanded ? "w-full" : "w-[69px]"
118
+ )}
119
+ >
120
+ <Link href="/dashboard" className="absolute left-[22px] transition-none">
121
+ {/* eslint-disable-next-line @next/next/no-img-element */}
122
+ <img src="/logolft.svg" alt="Logo" className="h-7 w-7" />
123
+ </Link>
124
+ </div>
125
+
126
+ {/* Navigation */}
127
+ <nav className="flex-1 pt-[70px] pb-4 overflow-y-auto scrollbar-hide">
128
+ <ul className="space-y-1">
129
+ {navItems.map((item) => (
130
+ <MenuItem
131
+ key={item.path}
132
+ item={item}
133
+ isExpanded={isExpanded}
134
+ pathname={pathname}
135
+ />
136
+ ))}
137
+ </ul>
138
+ </nav>
139
+ </aside>
140
+ )
141
+ }
@@ -1,68 +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
- }
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
+ }