@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
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from "react"
|
|
4
4
|
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
|
5
|
+
import { motion, AnimatePresence } from "framer-motion"
|
|
5
6
|
import { XIcon } from "lucide-react"
|
|
6
|
-
|
|
7
7
|
import { cn } from "@/lib/utils"
|
|
8
8
|
|
|
9
9
|
function Dialog({
|
|
@@ -30,50 +30,52 @@ function DialogClose({
|
|
|
30
30
|
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
<
|
|
33
|
+
const DialogOverlay = React.forwardRef<
|
|
34
|
+
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
|
35
|
+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
|
|
36
|
+
>(({ className, ...props }, ref) => (
|
|
37
|
+
<DialogPrimitive.Overlay ref={ref} asChild {...props}>
|
|
38
|
+
<motion.div
|
|
39
39
|
data-slot="dialog-overlay"
|
|
40
|
+
initial={{ opacity: 0 }}
|
|
41
|
+
animate={{ opacity: 1 }}
|
|
42
|
+
exit={{ opacity: 0 }}
|
|
43
|
+
transition={{ duration: 0.2 }}
|
|
40
44
|
className={cn(
|
|
41
45
|
"fixed inset-0 z-50",
|
|
42
|
-
"bg-
|
|
43
|
-
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
|
44
|
-
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
46
|
+
"bg-black/60 backdrop-blur-sm",
|
|
45
47
|
className
|
|
46
48
|
)}
|
|
47
|
-
{...props}
|
|
48
49
|
/>
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
</DialogPrimitive.Overlay>
|
|
51
|
+
))
|
|
52
|
+
DialogOverlay.displayName = "DialogOverlay"
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<DialogOverlay />
|
|
63
|
-
<DialogPrimitive.Content
|
|
54
|
+
const DialogContent = React.forwardRef<
|
|
55
|
+
React.ElementRef<typeof DialogPrimitive.Content>,
|
|
56
|
+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
|
|
57
|
+
showCloseButton?: boolean
|
|
58
|
+
}
|
|
59
|
+
>(({ className, children, showCloseButton = true, ...props }, ref) => (
|
|
60
|
+
<DialogPortal>
|
|
61
|
+
<DialogOverlay />
|
|
62
|
+
<DialogPrimitive.Content ref={ref} asChild {...props}>
|
|
63
|
+
<motion.div
|
|
64
64
|
data-slot="dialog-content"
|
|
65
|
+
initial={{ opacity: 0, scale: 0.95, y: -10 }}
|
|
66
|
+
animate={{ opacity: 1, scale: 1, y: 0 }}
|
|
67
|
+
exit={{ opacity: 0, scale: 0.95, y: -10 }}
|
|
68
|
+
transition={{
|
|
69
|
+
type: "spring",
|
|
70
|
+
stiffness: 300,
|
|
71
|
+
damping: 30,
|
|
72
|
+
}}
|
|
65
73
|
className={cn(
|
|
66
74
|
"fixed left-[50%] top-[50%] z-50",
|
|
67
75
|
"translate-x-[-50%] translate-y-[-50%]",
|
|
68
76
|
"grid w-full max-w-xl gap-4 border bg-background p-6",
|
|
69
|
-
"duration-200",
|
|
70
|
-
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
71
|
-
"data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]",
|
|
72
|
-
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
73
|
-
"data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%]",
|
|
74
77
|
className
|
|
75
78
|
)}
|
|
76
|
-
{...props}
|
|
77
79
|
>
|
|
78
80
|
{children}
|
|
79
81
|
{showCloseButton && (
|
|
@@ -85,10 +87,11 @@ function DialogContent({
|
|
|
85
87
|
<span className="sr-only">Close</span>
|
|
86
88
|
</DialogPrimitive.Close>
|
|
87
89
|
)}
|
|
88
|
-
</
|
|
89
|
-
</
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
</motion.div>
|
|
91
|
+
</DialogPrimitive.Content>
|
|
92
|
+
</DialogPortal>
|
|
93
|
+
))
|
|
94
|
+
DialogContent.displayName = "DialogContent"
|
|
92
95
|
|
|
93
96
|
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
94
97
|
return (
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { motion, type HTMLMotionProps, type Variants } from 'framer-motion'
|
|
4
|
+
import { forwardRef } from 'react'
|
|
5
|
+
|
|
6
|
+
// Variantes de animación reutilizables
|
|
7
|
+
export const fadeIn: Variants = {
|
|
8
|
+
hidden: { opacity: 0 },
|
|
9
|
+
visible: { opacity: 1 },
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const fadeInUp: Variants = {
|
|
13
|
+
hidden: { opacity: 0, y: 20 },
|
|
14
|
+
visible: { opacity: 1, y: 0 },
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const fadeInDown: Variants = {
|
|
18
|
+
hidden: { opacity: 0, y: -20 },
|
|
19
|
+
visible: { opacity: 1, y: 0 },
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const fadeInLeft: Variants = {
|
|
23
|
+
hidden: { opacity: 0, x: -20 },
|
|
24
|
+
visible: { opacity: 1, x: 0 },
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const fadeInRight: Variants = {
|
|
28
|
+
hidden: { opacity: 0, x: 20 },
|
|
29
|
+
visible: { opacity: 1, x: 0 },
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const scaleIn: Variants = {
|
|
33
|
+
hidden: { opacity: 0, scale: 0.95 },
|
|
34
|
+
visible: { opacity: 1, scale: 1 },
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const slideInFromBottom: Variants = {
|
|
38
|
+
hidden: { y: '100%' },
|
|
39
|
+
visible: { y: 0 },
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const slideInFromTop: Variants = {
|
|
43
|
+
hidden: { y: '-100%' },
|
|
44
|
+
visible: { y: 0 },
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const staggerContainer: Variants = {
|
|
48
|
+
hidden: { opacity: 0 },
|
|
49
|
+
visible: {
|
|
50
|
+
opacity: 1,
|
|
51
|
+
transition: {
|
|
52
|
+
staggerChildren: 0.1,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const staggerItem: Variants = {
|
|
58
|
+
hidden: { opacity: 0, y: 20 },
|
|
59
|
+
visible: { opacity: 1, y: 0 },
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Transiciones predefinidas
|
|
63
|
+
export const springTransition = {
|
|
64
|
+
type: 'spring',
|
|
65
|
+
stiffness: 300,
|
|
66
|
+
damping: 30,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const easeTransition = {
|
|
70
|
+
duration: 0.3,
|
|
71
|
+
ease: [0.25, 0.1, 0.25, 1],
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const smoothTransition = {
|
|
75
|
+
duration: 0.4,
|
|
76
|
+
ease: 'easeInOut',
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Componentes de animación base
|
|
80
|
+
export const MotionDiv = motion.div
|
|
81
|
+
export const MotionSpan = motion.span
|
|
82
|
+
export const MotionButton = motion.button
|
|
83
|
+
export const MotionUl = motion.ul
|
|
84
|
+
export const MotionLi = motion.li
|
|
85
|
+
export const MotionNav = motion.nav
|
|
86
|
+
export const MotionSection = motion.section
|
|
87
|
+
export const MotionArticle = motion.article
|
|
88
|
+
export const MotionHeader = motion.header
|
|
89
|
+
export const MotionFooter = motion.footer
|
|
90
|
+
export const MotionMain = motion.main
|
|
91
|
+
export const MotionAside = motion.aside
|
|
92
|
+
|
|
93
|
+
// Componente FadeIn genérico
|
|
94
|
+
interface FadeInProps extends HTMLMotionProps<'div'> {
|
|
95
|
+
direction?: 'up' | 'down' | 'left' | 'right' | 'none'
|
|
96
|
+
delay?: number
|
|
97
|
+
duration?: number
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export const FadeIn = forwardRef<HTMLDivElement, FadeInProps>(
|
|
101
|
+
({ children, direction = 'up', delay = 0, duration = 0.4, ...props }, ref) => {
|
|
102
|
+
const variants: Record<string, Variants> = {
|
|
103
|
+
up: fadeInUp,
|
|
104
|
+
down: fadeInDown,
|
|
105
|
+
left: fadeInLeft,
|
|
106
|
+
right: fadeInRight,
|
|
107
|
+
none: fadeIn,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<motion.div
|
|
112
|
+
ref={ref}
|
|
113
|
+
initial="hidden"
|
|
114
|
+
animate="visible"
|
|
115
|
+
variants={variants[direction]}
|
|
116
|
+
transition={{ duration, delay, ease: 'easeOut' }}
|
|
117
|
+
{...props}
|
|
118
|
+
>
|
|
119
|
+
{children}
|
|
120
|
+
</motion.div>
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
FadeIn.displayName = 'FadeIn'
|
|
125
|
+
|
|
126
|
+
// Componente ScaleIn
|
|
127
|
+
interface ScaleInProps extends HTMLMotionProps<'div'> {
|
|
128
|
+
delay?: number
|
|
129
|
+
duration?: number
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export const ScaleIn = forwardRef<HTMLDivElement, ScaleInProps>(
|
|
133
|
+
({ children, delay = 0, duration = 0.3, ...props }, ref) => {
|
|
134
|
+
return (
|
|
135
|
+
<motion.div
|
|
136
|
+
ref={ref}
|
|
137
|
+
initial="hidden"
|
|
138
|
+
animate="visible"
|
|
139
|
+
variants={scaleIn}
|
|
140
|
+
transition={{ duration, delay, ...springTransition }}
|
|
141
|
+
{...props}
|
|
142
|
+
>
|
|
143
|
+
{children}
|
|
144
|
+
</motion.div>
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
)
|
|
148
|
+
ScaleIn.displayName = 'ScaleIn'
|
|
149
|
+
|
|
150
|
+
// Componente para listas con stagger
|
|
151
|
+
interface StaggerListProps extends HTMLMotionProps<'ul'> {
|
|
152
|
+
staggerDelay?: number
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export const StaggerList = forwardRef<HTMLUListElement, StaggerListProps>(
|
|
156
|
+
({ children, staggerDelay = 0.1, ...props }, ref) => {
|
|
157
|
+
return (
|
|
158
|
+
<motion.ul
|
|
159
|
+
ref={ref}
|
|
160
|
+
initial="hidden"
|
|
161
|
+
animate="visible"
|
|
162
|
+
variants={{
|
|
163
|
+
hidden: { opacity: 0 },
|
|
164
|
+
visible: {
|
|
165
|
+
opacity: 1,
|
|
166
|
+
transition: {
|
|
167
|
+
staggerChildren: staggerDelay,
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
}}
|
|
171
|
+
{...props}
|
|
172
|
+
>
|
|
173
|
+
{children}
|
|
174
|
+
</motion.ul>
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
)
|
|
178
|
+
StaggerList.displayName = 'StaggerList'
|
|
179
|
+
|
|
180
|
+
export const StaggerItem = forwardRef<HTMLLIElement, HTMLMotionProps<'li'>>(
|
|
181
|
+
({ children, ...props }, ref) => {
|
|
182
|
+
return (
|
|
183
|
+
<motion.li
|
|
184
|
+
ref={ref}
|
|
185
|
+
variants={staggerItem}
|
|
186
|
+
transition={easeTransition}
|
|
187
|
+
{...props}
|
|
188
|
+
>
|
|
189
|
+
{children}
|
|
190
|
+
</motion.li>
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
)
|
|
194
|
+
StaggerItem.displayName = 'StaggerItem'
|
|
195
|
+
|
|
196
|
+
// Componente de presencia animada para entradas/salidas
|
|
197
|
+
export { AnimatePresence } from 'framer-motion'
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { motion, type HTMLMotionProps } from 'framer-motion'
|
|
4
|
+
import { cn } from '@/lib/utils'
|
|
5
|
+
|
|
6
|
+
const pageVariants = {
|
|
7
|
+
initial: {
|
|
8
|
+
opacity: 0,
|
|
9
|
+
y: 20,
|
|
10
|
+
},
|
|
11
|
+
enter: {
|
|
12
|
+
opacity: 1,
|
|
13
|
+
y: 0,
|
|
14
|
+
transition: {
|
|
15
|
+
duration: 0.4,
|
|
16
|
+
ease: [0.25, 0.1, 0.25, 1],
|
|
17
|
+
when: 'beforeChildren',
|
|
18
|
+
staggerChildren: 0.1,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
exit: {
|
|
22
|
+
opacity: 0,
|
|
23
|
+
y: -20,
|
|
24
|
+
transition: {
|
|
25
|
+
duration: 0.3,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const childVariants = {
|
|
31
|
+
initial: {
|
|
32
|
+
opacity: 0,
|
|
33
|
+
y: 20,
|
|
34
|
+
},
|
|
35
|
+
enter: {
|
|
36
|
+
opacity: 1,
|
|
37
|
+
y: 0,
|
|
38
|
+
},
|
|
39
|
+
exit: {
|
|
40
|
+
opacity: 0,
|
|
41
|
+
y: -20,
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface PageTransitionProps extends HTMLMotionProps<'div'> {
|
|
46
|
+
children: React.ReactNode
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function PageTransition({
|
|
50
|
+
children,
|
|
51
|
+
className,
|
|
52
|
+
...props
|
|
53
|
+
}: PageTransitionProps) {
|
|
54
|
+
return (
|
|
55
|
+
<motion.div
|
|
56
|
+
initial="initial"
|
|
57
|
+
animate="enter"
|
|
58
|
+
exit="exit"
|
|
59
|
+
variants={pageVariants}
|
|
60
|
+
className={cn('w-full', className)}
|
|
61
|
+
{...props}
|
|
62
|
+
>
|
|
63
|
+
{children}
|
|
64
|
+
</motion.div>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface PageSectionProps extends HTMLMotionProps<'section'> {
|
|
69
|
+
children: React.ReactNode
|
|
70
|
+
delay?: number
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function PageSection({
|
|
74
|
+
children,
|
|
75
|
+
className,
|
|
76
|
+
delay = 0,
|
|
77
|
+
...props
|
|
78
|
+
}: PageSectionProps) {
|
|
79
|
+
return (
|
|
80
|
+
<motion.section
|
|
81
|
+
variants={childVariants}
|
|
82
|
+
transition={{
|
|
83
|
+
duration: 0.4,
|
|
84
|
+
delay,
|
|
85
|
+
ease: [0.25, 0.1, 0.25, 1],
|
|
86
|
+
}}
|
|
87
|
+
className={className}
|
|
88
|
+
{...props}
|
|
89
|
+
>
|
|
90
|
+
{children}
|
|
91
|
+
</motion.section>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
interface PageHeaderProps extends HTMLMotionProps<'header'> {
|
|
96
|
+
children: React.ReactNode
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function PageHeader({
|
|
100
|
+
children,
|
|
101
|
+
className,
|
|
102
|
+
...props
|
|
103
|
+
}: PageHeaderProps) {
|
|
104
|
+
return (
|
|
105
|
+
<motion.header
|
|
106
|
+
initial={{ opacity: 0, y: -20 }}
|
|
107
|
+
animate={{ opacity: 1, y: 0 }}
|
|
108
|
+
transition={{
|
|
109
|
+
duration: 0.4,
|
|
110
|
+
ease: [0.25, 0.1, 0.25, 1],
|
|
111
|
+
}}
|
|
112
|
+
className={cn('mb-6', className)}
|
|
113
|
+
{...props}
|
|
114
|
+
>
|
|
115
|
+
{children}
|
|
116
|
+
</motion.header>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
interface PageTitleProps extends HTMLMotionProps<'h1'> {
|
|
121
|
+
children: React.ReactNode
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function PageTitle({ children, className, ...props }: PageTitleProps) {
|
|
125
|
+
return (
|
|
126
|
+
<motion.h1
|
|
127
|
+
initial={{ opacity: 0, x: -20 }}
|
|
128
|
+
animate={{ opacity: 1, x: 0 }}
|
|
129
|
+
transition={{
|
|
130
|
+
duration: 0.4,
|
|
131
|
+
delay: 0.1,
|
|
132
|
+
ease: [0.25, 0.1, 0.25, 1],
|
|
133
|
+
}}
|
|
134
|
+
className={cn('text-2xl font-bold tracking-tight', className)}
|
|
135
|
+
{...props}
|
|
136
|
+
>
|
|
137
|
+
{children}
|
|
138
|
+
</motion.h1>
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
interface PageDescriptionProps extends HTMLMotionProps<'p'> {
|
|
143
|
+
children: React.ReactNode
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function PageDescription({
|
|
147
|
+
children,
|
|
148
|
+
className,
|
|
149
|
+
...props
|
|
150
|
+
}: PageDescriptionProps) {
|
|
151
|
+
return (
|
|
152
|
+
<motion.p
|
|
153
|
+
initial={{ opacity: 0 }}
|
|
154
|
+
animate={{ opacity: 1 }}
|
|
155
|
+
transition={{
|
|
156
|
+
duration: 0.4,
|
|
157
|
+
delay: 0.2,
|
|
158
|
+
ease: [0.25, 0.1, 0.25, 1],
|
|
159
|
+
}}
|
|
160
|
+
className={cn('text-muted-foreground', className)}
|
|
161
|
+
{...props}
|
|
162
|
+
>
|
|
163
|
+
{children}
|
|
164
|
+
</motion.p>
|
|
165
|
+
)
|
|
166
|
+
}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from "react"
|
|
4
4
|
import * as SheetPrimitive from "@radix-ui/react-dialog"
|
|
5
|
+
import { motion } from "framer-motion"
|
|
5
6
|
import { XIcon } from "lucide-react"
|
|
6
|
-
|
|
7
7
|
import { cn } from "@/lib/utils"
|
|
8
8
|
|
|
9
9
|
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
|
|
@@ -28,65 +28,89 @@ function SheetPortal({
|
|
|
28
28
|
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
<
|
|
31
|
+
const SheetOverlay = React.forwardRef<
|
|
32
|
+
React.ElementRef<typeof SheetPrimitive.Overlay>,
|
|
33
|
+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
|
|
34
|
+
>(({ className, ...props }, ref) => (
|
|
35
|
+
<SheetPrimitive.Overlay ref={ref} asChild {...props}>
|
|
36
|
+
<motion.div
|
|
37
37
|
data-slot="sheet-overlay"
|
|
38
|
+
initial={{ opacity: 0 }}
|
|
39
|
+
animate={{ opacity: 1 }}
|
|
40
|
+
exit={{ opacity: 0 }}
|
|
41
|
+
transition={{ duration: 0.2 }}
|
|
38
42
|
className={cn(
|
|
39
43
|
"fixed inset-0 z-50",
|
|
40
|
-
"bg-
|
|
41
|
-
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
|
42
|
-
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
44
|
+
"bg-black/60 backdrop-blur-sm",
|
|
43
45
|
className
|
|
44
46
|
)}
|
|
45
|
-
{...props}
|
|
46
47
|
/>
|
|
47
|
-
|
|
48
|
+
</SheetPrimitive.Overlay>
|
|
49
|
+
))
|
|
50
|
+
SheetOverlay.displayName = "SheetOverlay"
|
|
51
|
+
|
|
52
|
+
const slideVariants = {
|
|
53
|
+
right: {
|
|
54
|
+
initial: { x: "100%" },
|
|
55
|
+
animate: { x: 0 },
|
|
56
|
+
exit: { x: "100%" },
|
|
57
|
+
},
|
|
58
|
+
left: {
|
|
59
|
+
initial: { x: "-100%" },
|
|
60
|
+
animate: { x: 0 },
|
|
61
|
+
exit: { x: "-100%" },
|
|
62
|
+
},
|
|
63
|
+
top: {
|
|
64
|
+
initial: { y: "-100%" },
|
|
65
|
+
animate: { y: 0 },
|
|
66
|
+
exit: { y: "-100%" },
|
|
67
|
+
},
|
|
68
|
+
bottom: {
|
|
69
|
+
initial: { y: "100%" },
|
|
70
|
+
animate: { y: 0 },
|
|
71
|
+
exit: { y: "100%" },
|
|
72
|
+
},
|
|
48
73
|
}
|
|
49
74
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
<SheetOverlay />
|
|
61
|
-
<SheetPrimitive.Content
|
|
75
|
+
const SheetContent = React.forwardRef<
|
|
76
|
+
React.ElementRef<typeof SheetPrimitive.Content>,
|
|
77
|
+
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content> & {
|
|
78
|
+
side?: "top" | "right" | "bottom" | "left"
|
|
79
|
+
}
|
|
80
|
+
>(({ className, children, side = "right", ...props }, ref) => (
|
|
81
|
+
<SheetPortal>
|
|
82
|
+
<SheetOverlay />
|
|
83
|
+
<SheetPrimitive.Content ref={ref} asChild {...props}>
|
|
84
|
+
<motion.div
|
|
62
85
|
data-slot="sheet-content"
|
|
86
|
+
initial={slideVariants[side].initial}
|
|
87
|
+
animate={slideVariants[side].animate}
|
|
88
|
+
exit={slideVariants[side].exit}
|
|
89
|
+
transition={{
|
|
90
|
+
type: "spring",
|
|
91
|
+
stiffness: 300,
|
|
92
|
+
damping: 30,
|
|
93
|
+
}}
|
|
63
94
|
className={cn(
|
|
64
95
|
"fixed z-50 gap-4 p-6",
|
|
65
|
-
"bg-
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
side === "
|
|
70
|
-
"inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-[520px] data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right",
|
|
71
|
-
side === "left" &&
|
|
72
|
-
"inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-[520px] data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left",
|
|
73
|
-
side === "top" &&
|
|
74
|
-
"inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
|
|
75
|
-
side === "bottom" &&
|
|
76
|
-
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
|
|
96
|
+
"bg-background border",
|
|
97
|
+
side === "right" && "inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-[520px]",
|
|
98
|
+
side === "left" && "inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-[520px]",
|
|
99
|
+
side === "top" && "inset-x-0 top-0 border-b",
|
|
100
|
+
side === "bottom" && "inset-x-0 bottom-0 border-t",
|
|
77
101
|
className
|
|
78
102
|
)}
|
|
79
|
-
{...props}
|
|
80
103
|
>
|
|
81
104
|
{children}
|
|
82
105
|
<SheetPrimitive.Close className="absolute right-6 top-6 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none disabled:pointer-events-none">
|
|
83
106
|
<XIcon className="h-4 w-4" />
|
|
84
107
|
<span className="sr-only">Close</span>
|
|
85
108
|
</SheetPrimitive.Close>
|
|
86
|
-
</
|
|
87
|
-
</
|
|
88
|
-
|
|
89
|
-
|
|
109
|
+
</motion.div>
|
|
110
|
+
</SheetPrimitive.Content>
|
|
111
|
+
</SheetPortal>
|
|
112
|
+
))
|
|
113
|
+
SheetContent.displayName = "SheetContent"
|
|
90
114
|
|
|
91
115
|
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
92
116
|
return (
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { LucideIcon, LayoutDashboard, Users, Settings, FileText } from 'lucide-react'
|
|
2
|
+
|
|
3
|
+
export interface NavItem {
|
|
4
|
+
title: string
|
|
5
|
+
href: string
|
|
6
|
+
icon?: LucideIcon
|
|
7
|
+
disabled?: boolean
|
|
8
|
+
external?: boolean
|
|
9
|
+
badge?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface NavSection {
|
|
13
|
+
title?: string
|
|
14
|
+
items: NavItem[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const mainNav: NavItem[] = [
|
|
18
|
+
{
|
|
19
|
+
title: 'Dashboard',
|
|
20
|
+
href: '/dashboard',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
title: 'Usuarios',
|
|
24
|
+
href: '/users',
|
|
25
|
+
},
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
export const sidebarNav: NavSection[] = [
|
|
29
|
+
{
|
|
30
|
+
items: [
|
|
31
|
+
{
|
|
32
|
+
title: 'Dashboard',
|
|
33
|
+
href: '/dashboard',
|
|
34
|
+
icon: LayoutDashboard,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
title: 'Usuarios',
|
|
38
|
+
href: '/users',
|
|
39
|
+
icon: Users,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
title: 'Reportes',
|
|
43
|
+
href: '/reports',
|
|
44
|
+
icon: FileText,
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
title: 'Configuración',
|
|
50
|
+
items: [
|
|
51
|
+
{
|
|
52
|
+
title: 'Ajustes',
|
|
53
|
+
href: '/settings',
|
|
54
|
+
icon: Settings,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
export const footerNav: NavItem[] = [
|
|
61
|
+
{
|
|
62
|
+
title: 'Términos',
|
|
63
|
+
href: '/terms',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
title: 'Privacidad',
|
|
67
|
+
href: '/privacy',
|
|
68
|
+
},
|
|
69
|
+
]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const siteConfig = {
|
|
2
|
+
name: 'LFT App',
|
|
3
|
+
description: 'Next.js application with LFT stack',
|
|
4
|
+
url: process.env.NEXT_PUBLIC_APP_URL ?? 'http://localhost:3000',
|
|
5
|
+
ogImage: '/og.png',
|
|
6
|
+
links: {
|
|
7
|
+
github: 'https://github.com',
|
|
8
|
+
},
|
|
9
|
+
creator: 'LFT Team',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type SiteConfig = typeof siteConfig
|