@create-lft-app/nextjs 1.0.0 → 1.0.2

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.
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "target": "ES2017",
4
- "lib": ["dom", "dom.iterable", "esnext"],
4
+ "lib": [
5
+ "dom",
6
+ "dom.iterable",
7
+ "esnext"
8
+ ],
5
9
  "allowJs": true,
6
10
  "skipLibCheck": true,
7
11
  "strict": true,
@@ -11,7 +15,7 @@
11
15
  "moduleResolution": "bundler",
12
16
  "resolveJsonModule": true,
13
17
  "isolatedModules": true,
14
- "jsx": "preserve",
18
+ "jsx": "react-jsx",
15
19
  "incremental": true,
16
20
  "plugins": [
17
21
  {
@@ -19,9 +23,19 @@
19
23
  }
20
24
  ],
21
25
  "paths": {
22
- "@/*": ["./src/*"]
26
+ "@/*": [
27
+ "./src/*"
28
+ ]
23
29
  }
24
30
  },
25
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26
- "exclude": ["node_modules"]
31
+ "include": [
32
+ "next-env.d.ts",
33
+ "**/*.ts",
34
+ "**/*.tsx",
35
+ ".next/types/**/*.ts",
36
+ ".next/dev/types/**/*.ts"
37
+ ],
38
+ "exclude": [
39
+ "node_modules"
40
+ ]
27
41
  }
@@ -1,153 +0,0 @@
1
- "use client"
2
-
3
- import { useState } from "react"
4
- import { useRouter } from "next/navigation"
5
- import Link from "next/link"
6
- import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "@/components/ui/card"
7
- import { Input } from "@/components/ui/input"
8
- import { Button } from "@/components/ui/button"
9
- import { Label } from "@/components/ui/label"
10
- import { Separator } from "@/components/ui/separator"
11
- import { Spinner } from "@/components/ui/spinner"
12
-
13
- export default function LoginPage() {
14
- const router = useRouter()
15
- const [isLoading, setIsLoading] = useState(false)
16
- const [error, setError] = useState<string | null>(null)
17
-
18
- const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
19
- e.preventDefault()
20
- setIsLoading(true)
21
- setError(null)
22
-
23
- const formData = new FormData(e.currentTarget)
24
- const email = formData.get("email") as string
25
- const password = formData.get("password") as string
26
-
27
- try {
28
- // TODO: Implementar autenticación con Supabase
29
- // const { error } = await supabase.auth.signInWithPassword({ email, password })
30
-
31
- // Simulación de login exitoso
32
- await new Promise((resolve) => setTimeout(resolve, 1000))
33
-
34
- router.push("/dashboard")
35
- } catch (err) {
36
- setError("Error al iniciar sesión. Verifica tus credenciales.")
37
- } finally {
38
- setIsLoading(false)
39
- }
40
- }
41
-
42
- return (
43
- <div className="min-h-screen flex items-center justify-center bg-background p-4">
44
- <Card className="w-full max-w-md">
45
- <CardHeader className="space-y-1 text-center">
46
- <CardTitle className="text-2xl font-bold">Iniciar Sesión</CardTitle>
47
- <CardDescription>
48
- Ingresa tus credenciales para acceder al panel
49
- </CardDescription>
50
- </CardHeader>
51
- <CardContent>
52
- <form onSubmit={handleSubmit} className="space-y-4">
53
- {error && (
54
- <div className="p-3 text-sm text-destructive bg-destructive/10 border border-destructive/20 rounded-md">
55
- {error}
56
- </div>
57
- )}
58
-
59
- <div className="space-y-2">
60
- <Label htmlFor="email">Email</Label>
61
- <Input
62
- id="email"
63
- name="email"
64
- type="email"
65
- placeholder="tu@email.com"
66
- required
67
- disabled={isLoading}
68
- autoComplete="email"
69
- />
70
- </div>
71
-
72
- <div className="space-y-2">
73
- <div className="flex items-center justify-between">
74
- <Label htmlFor="password">Contraseña</Label>
75
- <Link
76
- href="/auth/forgot-password"
77
- className="text-sm text-primary hover:underline"
78
- >
79
- ¿Olvidaste tu contraseña?
80
- </Link>
81
- </div>
82
- <Input
83
- id="password"
84
- name="password"
85
- type="password"
86
- placeholder="••••••••"
87
- required
88
- disabled={isLoading}
89
- autoComplete="current-password"
90
- />
91
- </div>
92
-
93
- <Button type="submit" className="w-full" disabled={isLoading}>
94
- {isLoading ? (
95
- <>
96
- <Spinner className="mr-2 h-4 w-4" />
97
- Iniciando sesión...
98
- </>
99
- ) : (
100
- "Iniciar Sesión"
101
- )}
102
- </Button>
103
- </form>
104
-
105
- <div className="relative my-6">
106
- <Separator />
107
- <span className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 bg-background px-2 text-xs text-muted-foreground">
108
- O continuar con
109
- </span>
110
- </div>
111
-
112
- <div className="grid grid-cols-2 gap-4">
113
- <Button variant="outline" disabled={isLoading}>
114
- <svg className="mr-2 h-4 w-4" viewBox="0 0 24 24">
115
- <path
116
- fill="currentColor"
117
- d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
118
- />
119
- <path
120
- fill="currentColor"
121
- d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
122
- />
123
- <path
124
- fill="currentColor"
125
- d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
126
- />
127
- <path
128
- fill="currentColor"
129
- d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
130
- />
131
- </svg>
132
- Google
133
- </Button>
134
- <Button variant="outline" disabled={isLoading}>
135
- <svg className="mr-2 h-4 w-4" fill="currentColor" viewBox="0 0 24 24">
136
- <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
137
- </svg>
138
- GitHub
139
- </Button>
140
- </div>
141
- </CardContent>
142
- <CardFooter className="flex justify-center">
143
- <p className="text-sm text-muted-foreground">
144
- ¿No tienes una cuenta?{" "}
145
- <Link href="/auth/register" className="text-primary hover:underline">
146
- Regístrate
147
- </Link>
148
- </p>
149
- </CardFooter>
150
- </Card>
151
- </div>
152
- )
153
- }
@@ -1,247 +0,0 @@
1
- 'use client'
2
-
3
- import Link from 'next/link'
4
- import { usePathname } from 'next/navigation'
5
- import { useState, useRef, useEffect } from 'react'
6
- import { cn } from '@/lib/utils'
7
- import {
8
- LayoutDashboard,
9
- Building2,
10
- Users,
11
- CreditCard,
12
- Settings,
13
- Shield,
14
- Clock,
15
- ChevronLeft,
16
- LogOut,
17
- } from 'lucide-react'
18
- import {
19
- DropdownMenu,
20
- DropdownMenuContent,
21
- DropdownMenuItem,
22
- DropdownMenuSeparator,
23
- DropdownMenuTrigger,
24
- } from '@/components/ui/dropdown-menu'
25
- import { logout } from '@/modules/auth/actions/auth-actions'
26
-
27
- type NavItem = {
28
- path: string
29
- name: string
30
- icon: React.ComponentType<{ className?: string }>
31
- }
32
-
33
- const adminNavItems: NavItem[] = [
34
- { path: '/admin', name: 'Dashboard', icon: LayoutDashboard },
35
- { path: '/admin/organizations', name: 'Organizaciones', icon: Building2 },
36
- { path: '/admin/users', name: 'Usuarios', icon: Users },
37
- { path: '/admin/subscriptions', name: 'Suscripciones', icon: CreditCard },
38
- ]
39
-
40
- const systemNavItems: NavItem[] = [
41
- { path: '/admin/jobs', name: 'Jobs', icon: Clock },
42
- { path: '/admin/settings', name: 'Configuración', icon: Settings },
43
- ]
44
-
45
- interface AdminMiddaySidebarProps {
46
- user: {
47
- email: string
48
- full_name: string | null
49
- role: string
50
- } | null
51
- }
52
-
53
- export function AdminMiddaySidebar({ user }: AdminMiddaySidebarProps) {
54
- const pathname = usePathname()
55
- const [isExpanded, setIsExpanded] = useState(false)
56
- const sidebarRef = useRef<HTMLElement>(null)
57
- const timeoutRef = useRef<NodeJS.Timeout | null>(null)
58
-
59
- const handleMouseEnter = () => {
60
- if (timeoutRef.current) clearTimeout(timeoutRef.current)
61
- setIsExpanded(true)
62
- }
63
-
64
- const handleMouseLeave = () => {
65
- timeoutRef.current = setTimeout(() => setIsExpanded(false), 100)
66
- }
67
-
68
- useEffect(() => {
69
- return () => {
70
- if (timeoutRef.current) clearTimeout(timeoutRef.current)
71
- }
72
- }, [])
73
-
74
- const isActive = (path: string) => {
75
- if (path === '/admin') return pathname === '/admin'
76
- return pathname.startsWith(path)
77
- }
78
-
79
- const initials = user?.full_name
80
- ?.split(' ')
81
- .map((n) => n[0])
82
- .join('')
83
- .toUpperCase()
84
- .slice(0, 2) || 'SA'
85
-
86
- return (
87
- <aside
88
- ref={sidebarRef}
89
- className={cn(
90
- 'h-screen flex-shrink-0 flex-col justify-between fixed top-0 left-0 hidden md:flex z-50',
91
- 'bg-background border-r border-border',
92
- 'transition-[width] duration-200 ease-[cubic-bezier(0.4,0,0.2,1)]',
93
- isExpanded ? 'w-[240px]' : 'w-[70px]'
94
- )}
95
- onMouseEnter={handleMouseEnter}
96
- onMouseLeave={handleMouseLeave}
97
- >
98
- {/* Logo */}
99
- <div className="h-[70px] flex items-center border-b border-border px-4">
100
- <Link href="/admin" className="flex items-center gap-3">
101
- <div className="flex h-8 w-8 items-center justify-center rounded-md bg-red-500 text-white flex-shrink-0">
102
- <Shield className="h-4 w-4" />
103
- </div>
104
- <span
105
- className={cn(
106
- 'font-semibold text-sm whitespace-nowrap transition-opacity duration-200',
107
- isExpanded ? 'opacity-100' : 'opacity-0 w-0'
108
- )}
109
- >
110
- Super Admin
111
- </span>
112
- </Link>
113
- </div>
114
-
115
- {/* Navigation */}
116
- <nav className="flex-1 overflow-y-auto py-4">
117
- {/* Main Nav */}
118
- <div className="mb-6">
119
- {isExpanded && (
120
- <p className="px-4 mb-2 text-[10px] font-medium text-[#878787] uppercase tracking-wider">
121
- Gestión
122
- </p>
123
- )}
124
- <ul className="space-y-1 px-2">
125
- {adminNavItems.map((item) => {
126
- const Icon = item.icon
127
- const active = isActive(item.path)
128
-
129
- return (
130
- <li key={item.path}>
131
- <Link
132
- href={item.path}
133
- className={cn(
134
- 'flex items-center gap-3 rounded-md px-3 py-2 text-sm transition-colors',
135
- active
136
- ? 'bg-accent text-foreground'
137
- : 'text-[#878787] hover:bg-accent/50 hover:text-foreground'
138
- )}
139
- >
140
- <Icon className="h-4 w-4 flex-shrink-0" />
141
- {isExpanded && (
142
- <span className="whitespace-nowrap">
143
- {item.name}
144
- </span>
145
- )}
146
- </Link>
147
- </li>
148
- )
149
- })}
150
- </ul>
151
- </div>
152
-
153
- {/* System Nav */}
154
- <div>
155
- {isExpanded && (
156
- <p className="px-4 mb-2 text-[10px] font-medium text-[#878787] uppercase tracking-wider">
157
- Sistema
158
- </p>
159
- )}
160
- <ul className="space-y-1 px-2">
161
- {systemNavItems.map((item) => {
162
- const Icon = item.icon
163
- const active = isActive(item.path)
164
-
165
- return (
166
- <li key={item.path}>
167
- <Link
168
- href={item.path}
169
- className={cn(
170
- 'flex items-center gap-3 rounded-md px-3 py-2 text-sm transition-colors',
171
- active
172
- ? 'bg-accent text-foreground'
173
- : 'text-[#878787] hover:bg-accent/50 hover:text-foreground'
174
- )}
175
- >
176
- <Icon className="h-4 w-4 flex-shrink-0" />
177
- {isExpanded && (
178
- <span className="whitespace-nowrap">
179
- {item.name}
180
- </span>
181
- )}
182
- </Link>
183
- </li>
184
- )
185
- })}
186
- </ul>
187
- </div>
188
- </nav>
189
-
190
- {/* Back to Dashboard & User */}
191
- <div className="border-t border-border">
192
- {/* Back Link */}
193
- <Link
194
- href="/"
195
- className="flex items-center gap-3 px-4 py-3 text-sm text-[#878787] hover:text-foreground hover:bg-accent/50 transition-colors"
196
- >
197
- <ChevronLeft className="h-4 w-4 flex-shrink-0" />
198
- <span
199
- className={cn(
200
- 'whitespace-nowrap transition-opacity duration-200',
201
- isExpanded ? 'opacity-100' : 'opacity-0 w-0'
202
- )}
203
- >
204
- Volver al Dashboard
205
- </span>
206
- </Link>
207
-
208
- {/* User Menu */}
209
- <DropdownMenu>
210
- <DropdownMenuTrigger asChild>
211
- <button className="flex items-center gap-3 w-full px-4 py-3 hover:bg-accent/50 transition-colors">
212
- <div className="flex h-8 w-8 items-center justify-center rounded-full bg-red-500/10 text-red-500 text-xs font-medium flex-shrink-0">
213
- {initials}
214
- </div>
215
- <div
216
- className={cn(
217
- 'flex flex-col items-start text-left transition-opacity duration-200',
218
- isExpanded ? 'opacity-100' : 'opacity-0 w-0'
219
- )}
220
- >
221
- <span className="text-sm font-medium truncate max-w-[160px]">
222
- {user?.full_name || 'Super Admin'}
223
- </span>
224
- <span className="text-xs text-[#878787] truncate max-w-[160px]">
225
- {user?.email}
226
- </span>
227
- </div>
228
- </button>
229
- </DropdownMenuTrigger>
230
- <DropdownMenuContent align="end" className="w-56">
231
- <div className="px-2 py-1.5">
232
- <p className="text-sm font-medium">{user?.full_name}</p>
233
- <p className="text-xs text-[#878787]">{user?.email}</p>
234
- </div>
235
- <DropdownMenuSeparator />
236
- <DropdownMenuItem asChild>
237
- <button onClick={() => logout()} className="w-full cursor-pointer">
238
- <LogOut className="h-4 w-4 mr-2" />
239
- Cerrar sesión
240
- </button>
241
- </DropdownMenuItem>
242
- </DropdownMenuContent>
243
- </DropdownMenu>
244
- </div>
245
- </aside>
246
- )
247
- }
@@ -1,146 +0,0 @@
1
- 'use client'
2
-
3
- import Link from 'next/link'
4
- import { usePathname } from 'next/navigation'
5
- import {
6
- LayoutDashboard,
7
- Building2,
8
- Users,
9
- CreditCard,
10
- Settings,
11
- Shield,
12
- Clock,
13
- } from 'lucide-react'
14
- import {
15
- Sidebar,
16
- SidebarContent,
17
- SidebarGroup,
18
- SidebarGroupContent,
19
- SidebarGroupLabel,
20
- SidebarMenu,
21
- SidebarMenuButton,
22
- SidebarMenuItem,
23
- SidebarHeader,
24
- SidebarFooter,
25
- } from '@/components/ui/sidebar'
26
- import { NavUser } from './nav-user'
27
-
28
- const adminNavItems = [
29
- {
30
- title: 'Dashboard',
31
- url: '/admin',
32
- icon: LayoutDashboard,
33
- },
34
- {
35
- title: 'Organizaciones',
36
- url: '/admin/organizations',
37
- icon: Building2,
38
- },
39
- {
40
- title: 'Usuarios',
41
- url: '/admin/users',
42
- icon: Users,
43
- },
44
- {
45
- title: 'Suscripciones',
46
- url: '/admin/subscriptions',
47
- icon: CreditCard,
48
- },
49
- ]
50
-
51
- const systemNavItems = [
52
- {
53
- title: 'Jobs',
54
- url: '/admin/jobs',
55
- icon: Clock,
56
- },
57
- {
58
- title: 'Configuración',
59
- url: '/admin/settings',
60
- icon: Settings,
61
- },
62
- ]
63
-
64
- interface AdminSidebarProps {
65
- user: {
66
- email: string
67
- full_name: string | null
68
- role: string
69
- } | null
70
- }
71
-
72
- export function AdminSidebar({ user }: AdminSidebarProps) {
73
- const pathname = usePathname()
74
-
75
- return (
76
- <Sidebar>
77
- <SidebarHeader className="border-b border-sidebar-border">
78
- <div className="flex items-center gap-2 px-4 py-3">
79
- <div className="flex h-8 w-8 items-center justify-center rounded-lg bg-destructive text-destructive-foreground font-bold">
80
- <Shield className="h-4 w-4" />
81
- </div>
82
- <div className="flex flex-col">
83
- <span className="text-sm font-semibold">
84
- {user?.full_name?.split(' ')[0] || 'Admin'}
85
- </span>
86
- <span className="text-xs text-muted-foreground">
87
- Super Admin
88
- </span>
89
- </div>
90
- </div>
91
- </SidebarHeader>
92
-
93
- <SidebarContent>
94
- <SidebarGroup>
95
- <SidebarGroupLabel>Gestión</SidebarGroupLabel>
96
- <SidebarGroupContent>
97
- <SidebarMenu>
98
- {adminNavItems.map((item) => (
99
- <SidebarMenuItem key={item.title}>
100
- <SidebarMenuButton
101
- asChild
102
- isActive={
103
- item.url === '/admin'
104
- ? pathname === '/admin'
105
- : pathname.startsWith(item.url)
106
- }
107
- >
108
- <Link href={item.url}>
109
- <item.icon className="h-4 w-4" />
110
- <span>{item.title}</span>
111
- </Link>
112
- </SidebarMenuButton>
113
- </SidebarMenuItem>
114
- ))}
115
- </SidebarMenu>
116
- </SidebarGroupContent>
117
- </SidebarGroup>
118
-
119
- <SidebarGroup>
120
- <SidebarGroupLabel>Sistema</SidebarGroupLabel>
121
- <SidebarGroupContent>
122
- <SidebarMenu>
123
- {systemNavItems.map((item) => (
124
- <SidebarMenuItem key={item.title}>
125
- <SidebarMenuButton
126
- asChild
127
- isActive={pathname.startsWith(item.url)}
128
- >
129
- <Link href={item.url}>
130
- <item.icon className="h-4 w-4" />
131
- <span>{item.title}</span>
132
- </Link>
133
- </SidebarMenuButton>
134
- </SidebarMenuItem>
135
- ))}
136
- </SidebarMenu>
137
- </SidebarGroupContent>
138
- </SidebarGroup>
139
- </SidebarContent>
140
-
141
- <SidebarFooter className="border-t border-sidebar-border">
142
- <NavUser user={user} />
143
- </SidebarFooter>
144
- </Sidebar>
145
- )
146
- }
@@ -1,71 +0,0 @@
1
- 'use client'
2
-
3
- import { Bell } from 'lucide-react'
4
- import { Button } from '@/components/ui/button'
5
- import { SidebarTrigger } from '@/components/ui/sidebar'
6
- import { Separator } from '@/components/ui/separator'
7
- import {
8
- DropdownMenu,
9
- DropdownMenuContent,
10
- DropdownMenuItem,
11
- DropdownMenuLabel,
12
- DropdownMenuSeparator,
13
- DropdownMenuTrigger,
14
- } from '@/components/ui/dropdown-menu'
15
- import { Badge } from '@/components/ui/badge'
16
-
17
- interface HeaderProps {
18
- title?: string
19
- }
20
-
21
- export function Header({ title }: HeaderProps) {
22
- return (
23
- <header className="flex h-14 shrink-0 items-center gap-2 border-b px-4">
24
- <SidebarTrigger className="-ml-1" />
25
- <Separator orientation="vertical" className="mr-2 h-4" />
26
- {title && <h1 className="text-lg font-semibold">{title}</h1>}
27
-
28
- <div className="ml-auto flex items-center gap-2">
29
- <DropdownMenu>
30
- <DropdownMenuTrigger asChild>
31
- <Button variant="ghost" size="icon" className="relative">
32
- <Bell className="h-5 w-5" />
33
- <Badge
34
- variant="destructive"
35
- className="absolute -right-1 -top-1 h-5 w-5 rounded-full p-0 text-xs flex items-center justify-center"
36
- >
37
- 3
38
- </Badge>
39
- </Button>
40
- </DropdownMenuTrigger>
41
- <DropdownMenuContent align="end" className="w-80">
42
- <DropdownMenuLabel>Notificaciones</DropdownMenuLabel>
43
- <DropdownMenuSeparator />
44
- <DropdownMenuItem className="flex flex-col items-start gap-1 cursor-pointer">
45
- <span className="font-medium">Documento por vencer</span>
46
- <span className="text-xs text-muted-foreground">
47
- Balance de Empresa ABC S.A. vence en 7 días
48
- </span>
49
- </DropdownMenuItem>
50
- <DropdownMenuItem className="flex flex-col items-start gap-1 cursor-pointer">
51
- <span className="font-medium">Nueva alerta BCRA</span>
52
- <span className="text-xs text-muted-foreground">
53
- Se detectó deuda en situación 3 para cliente
54
- </span>
55
- </DropdownMenuItem>
56
- <DropdownMenuItem className="flex flex-col items-start gap-1 cursor-pointer">
57
- <span className="font-medium">Cliente pendiente</span>
58
- <span className="text-xs text-muted-foreground">
59
- Juan Pérez está esperando aprobación
60
- </span>
61
- </DropdownMenuItem>
62
- <DropdownMenuSeparator />
63
- <DropdownMenuItem className="text-center cursor-pointer">
64
- <span className="text-sm text-primary">Ver todas</span>
65
- </DropdownMenuItem>
66
- </DropdownMenuContent>
67
- </DropdownMenu>
68
- </div>
69
- </header>
70
- )
71
- }