@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
@@ -1,59 +1,63 @@
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
- }
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { Table } from '@tanstack/react-table'
5
+ import { Settings2 } from 'lucide-react'
6
+
7
+ import { Button } from '@/components/ui/button'
8
+ import {
9
+ DropdownMenu,
10
+ DropdownMenuCheckboxItem,
11
+ DropdownMenuContent,
12
+ DropdownMenuLabel,
13
+ DropdownMenuSeparator,
14
+ DropdownMenuTrigger,
15
+ } from '@/components/ui/dropdown-menu'
16
+
17
+ interface DataTableViewOptionsProps<TData> {
18
+ table: Table<TData>
19
+ }
20
+
21
+ export function DataTableViewOptions<TData>({
22
+ table,
23
+ }: DataTableViewOptionsProps<TData>) {
24
+ const [open, setOpen] = React.useState(false)
25
+
26
+ return (
27
+ <DropdownMenu open={open} onOpenChange={setOpen}>
28
+ <DropdownMenuTrigger asChild>
29
+ <Button
30
+ variant="ghost"
31
+ size="icon"
32
+ className="h-8 w-8"
33
+ >
34
+ <Settings2 className="h-4 w-4" />
35
+ <span className="sr-only">Columnas</span>
36
+ </Button>
37
+ </DropdownMenuTrigger>
38
+ <DropdownMenuContent align="end" className="w-[150px]">
39
+ <DropdownMenuLabel>Mostrar columnas</DropdownMenuLabel>
40
+ <DropdownMenuSeparator />
41
+ {table
42
+ .getAllColumns()
43
+ .filter(
44
+ (column) =>
45
+ typeof column.accessorFn !== 'undefined' && column.getCanHide()
46
+ )
47
+ .map((column) => {
48
+ return (
49
+ <DropdownMenuCheckboxItem
50
+ key={column.id}
51
+ className="capitalize"
52
+ checked={column.getIsVisible()}
53
+ onSelect={(e) => e.preventDefault()}
54
+ onCheckedChange={(value) => column.toggleVisibility(!!value)}
55
+ >
56
+ {column.id}
57
+ </DropdownMenuCheckboxItem>
58
+ )
59
+ })}
60
+ </DropdownMenuContent>
61
+ </DropdownMenu>
62
+ )
63
+ }
@@ -1,128 +1,148 @@
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
- }
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
+ getFacetedRowModel,
12
+ getFacetedUniqueValues,
13
+ getFilteredRowModel,
14
+ getPaginationRowModel,
15
+ getSortedRowModel,
16
+ useReactTable,
17
+ } from '@tanstack/react-table'
18
+
19
+ import {
20
+ Table,
21
+ TableBody,
22
+ TableCell,
23
+ TableHead,
24
+ TableHeader,
25
+ TableRow,
26
+ } from '@/components/ui/table'
27
+ import { DataTableToolbar } from './data-table-toolbar'
28
+ import { DataTablePagination } from './data-table-pagination'
29
+ import { DataTableViewOptions } from './data-table-view-options'
30
+
31
+ export interface FilterConfig {
32
+ columnId: string
33
+ type: 'faceted' | 'date-range' | 'number-range'
34
+ title: string
35
+ options?: { label: string; value: string; icon?: React.ComponentType<{ className?: string }> }[]
36
+ /** Para number-range: formato del número */
37
+ format?: 'currency' | 'number'
38
+ /** Para number-range: símbolo de moneda */
39
+ currencySymbol?: string
40
+ /** Para number-range: paso del input */
41
+ step?: number
42
+ }
43
+
44
+ interface DataTableProps<TData, TValue> {
45
+ columns: ColumnDef<TData, TValue>[]
46
+ data: TData[]
47
+ searchKey?: string
48
+ searchPlaceholder?: string
49
+ filters?: FilterConfig[]
50
+ /** Modo de visualización de filtros: 'dropdown' (default), 'inline', 'inline-collapsible' */
51
+ filterMode?: 'dropdown' | 'inline' | 'inline-collapsible'
52
+ }
53
+
54
+ export function DataTable<TData, TValue>({
55
+ columns,
56
+ data,
57
+ searchKey,
58
+ searchPlaceholder = 'Buscar...',
59
+ filters,
60
+ filterMode = 'dropdown',
61
+ }: DataTableProps<TData, TValue>) {
62
+ const [sorting, setSorting] = React.useState<SortingState>([])
63
+ const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([])
64
+ const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({})
65
+ const [rowSelection, setRowSelection] = React.useState({})
66
+
67
+ const table = useReactTable({
68
+ data,
69
+ columns,
70
+ getCoreRowModel: getCoreRowModel(),
71
+ getPaginationRowModel: getPaginationRowModel(),
72
+ onSortingChange: setSorting,
73
+ getSortedRowModel: getSortedRowModel(),
74
+ onColumnFiltersChange: setColumnFilters,
75
+ getFilteredRowModel: getFilteredRowModel(),
76
+ onColumnVisibilityChange: setColumnVisibility,
77
+ onRowSelectionChange: setRowSelection,
78
+ getFacetedRowModel: getFacetedRowModel(),
79
+ getFacetedUniqueValues: getFacetedUniqueValues(),
80
+ state: {
81
+ sorting,
82
+ columnFilters,
83
+ columnVisibility,
84
+ rowSelection,
85
+ },
86
+ })
87
+
88
+ return (
89
+ <div className="space-y-4">
90
+ <DataTableToolbar
91
+ table={table}
92
+ searchKey={searchKey}
93
+ searchPlaceholder={searchPlaceholder}
94
+ filters={filters}
95
+ filterMode={filterMode}
96
+ />
97
+ <div className="rounded-md border">
98
+ <Table>
99
+ <TableHeader>
100
+ {table.getHeaderGroups().map((headerGroup) => (
101
+ <TableRow key={headerGroup.id}>
102
+ {headerGroup.headers.map((header, index) => {
103
+ const isLastHeader = index === headerGroup.headers.length - 1
104
+ return (
105
+ <TableHead key={header.id}>
106
+ <div className={isLastHeader ? 'flex items-center justify-end gap-2' : ''}>
107
+ {header.isPlaceholder
108
+ ? null
109
+ : flexRender(
110
+ header.column.columnDef.header,
111
+ header.getContext()
112
+ )}
113
+ {isLastHeader && <DataTableViewOptions table={table} />}
114
+ </div>
115
+ </TableHead>
116
+ )
117
+ })}
118
+ </TableRow>
119
+ ))}
120
+ </TableHeader>
121
+ <TableBody>
122
+ {table.getRowModel().rows?.length ? (
123
+ table.getRowModel().rows.map((row) => (
124
+ <TableRow
125
+ key={row.id}
126
+ data-state={row.getIsSelected() && 'selected'}
127
+ >
128
+ {row.getVisibleCells().map((cell) => (
129
+ <TableCell key={cell.id}>
130
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
131
+ </TableCell>
132
+ ))}
133
+ </TableRow>
134
+ ))
135
+ ) : (
136
+ <TableRow>
137
+ <TableCell colSpan={columns.length} className="h-24 text-center">
138
+ No hay resultados.
139
+ </TableCell>
140
+ </TableRow>
141
+ )}
142
+ </TableBody>
143
+ </Table>
144
+ </div>
145
+ <DataTablePagination table={table} />
146
+ </div>
147
+ )
148
+ }
@@ -1,5 +1,9 @@
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'
1
+ export { DataTable, type FilterConfig } 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'
6
+ export { DataTableFacetedFilter, type FacetedFilterOption } from './data-table-faceted-filter'
7
+ export { DataTableDateFilter, dateRangeFilterFn } from './data-table-date-filter'
8
+ export { DataTableNumberFilter, numberRangeFilterFn, type NumberRange } from './data-table-number-filter'
9
+ export { DataTableFiltersDropdown } from './data-table-filters-dropdown'
@@ -1,58 +1,58 @@
1
- "use client"
2
-
3
- import * as React from "react"
4
- import * as AccordionPrimitive from "@radix-ui/react-accordion"
5
- import { ChevronDown } from "lucide-react"
6
-
7
- import { cn } from "@/lib/utils"
8
-
9
- const Accordion = AccordionPrimitive.Root
10
-
11
- const AccordionItem = React.forwardRef<
12
- React.ElementRef<typeof AccordionPrimitive.Item>,
13
- React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
14
- >(({ className, ...props }, ref) => (
15
- <AccordionPrimitive.Item
16
- ref={ref}
17
- className={cn("", className)}
18
- {...props}
19
- />
20
- ))
21
- AccordionItem.displayName = "AccordionItem"
22
-
23
- const AccordionTrigger = React.forwardRef<
24
- React.ElementRef<typeof AccordionPrimitive.Trigger>,
25
- React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
26
- >(({ className, children, ...props }, ref) => (
27
- <AccordionPrimitive.Header className="flex">
28
- <AccordionPrimitive.Trigger
29
- ref={ref}
30
- className={cn(
31
- "flex flex-1 items-center justify-between py-4 font-medium transition-all [&[data-state=open]>svg]:rotate-180",
32
- className
33
- )}
34
- {...props}
35
- >
36
- {children}
37
- <ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
38
- </AccordionPrimitive.Trigger>
39
- </AccordionPrimitive.Header>
40
- ))
41
- AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
42
-
43
- const AccordionContent = React.forwardRef<
44
- React.ElementRef<typeof AccordionPrimitive.Content>,
45
- React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
46
- >(({ className, children, ...props }, ref) => (
47
- <AccordionPrimitive.Content
48
- ref={ref}
49
- className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
50
- {...props}
51
- >
52
- <div className={cn("pb-4 pt-0", className)}>{children}</div>
53
- </AccordionPrimitive.Content>
54
- ))
55
-
56
- AccordionContent.displayName = AccordionPrimitive.Content.displayName
57
-
58
- export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as AccordionPrimitive from "@radix-ui/react-accordion"
5
+ import { ChevronDown } from "lucide-react"
6
+
7
+ import { cn } from "@/lib/utils"
8
+
9
+ const Accordion = AccordionPrimitive.Root
10
+
11
+ const AccordionItem = React.forwardRef<
12
+ React.ElementRef<typeof AccordionPrimitive.Item>,
13
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
14
+ >(({ className, ...props }, ref) => (
15
+ <AccordionPrimitive.Item
16
+ ref={ref}
17
+ className={cn("", className)}
18
+ {...props}
19
+ />
20
+ ))
21
+ AccordionItem.displayName = "AccordionItem"
22
+
23
+ const AccordionTrigger = React.forwardRef<
24
+ React.ElementRef<typeof AccordionPrimitive.Trigger>,
25
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
26
+ >(({ className, children, ...props }, ref) => (
27
+ <AccordionPrimitive.Header className="flex">
28
+ <AccordionPrimitive.Trigger
29
+ ref={ref}
30
+ className={cn(
31
+ "flex flex-1 items-center justify-between py-4 font-medium transition-all [&[data-state=open]>svg]:rotate-180",
32
+ className
33
+ )}
34
+ {...props}
35
+ >
36
+ {children}
37
+ <ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
38
+ </AccordionPrimitive.Trigger>
39
+ </AccordionPrimitive.Header>
40
+ ))
41
+ AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
42
+
43
+ const AccordionContent = React.forwardRef<
44
+ React.ElementRef<typeof AccordionPrimitive.Content>,
45
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
46
+ >(({ className, children, ...props }, ref) => (
47
+ <AccordionPrimitive.Content
48
+ ref={ref}
49
+ className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
50
+ {...props}
51
+ >
52
+ <div className={cn("pb-4 pt-0", className)}>{children}</div>
53
+ </AccordionPrimitive.Content>
54
+ ))
55
+
56
+ AccordionContent.displayName = AccordionPrimitive.Content.displayName
57
+
58
+ export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }