@create-lft-app/nextjs 1.0.0 → 2.0.1
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 +7 -2
- package/template/package.json +1 -1
- package/template/public/logolft.svg +12 -0
- package/template/src/app/dashboard/page.tsx +93 -84
- package/template/src/app/layout.tsx +9 -13
- package/template/src/components/layout/midday-sidebar.tsx +22 -252
- package/template/src/modules/auth/actions/auth-actions.ts +4 -4
- package/template/src/test/test.js +0 -0
- package/template/tsconfig.json +19 -5
- package/template/src/app/auth/login/page.tsx +0 -153
- package/template/src/components/layout/admin-midday-sidebar.tsx +0 -247
- package/template/src/components/layout/admin-sidebar.tsx +0 -146
- package/template/src/components/layout/header.tsx +0 -71
- package/template/src/components/layout/nav-user.tsx +0 -108
- package/template/src/components/layout/page-header.tsx +0 -95
- package/template/src/components/layout/sidebar.tsx +0 -194
- package/template/src/components/layout/suspension-banner.tsx +0 -21
package/template/tsconfig.json
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
3
|
"target": "ES2017",
|
|
4
|
-
"lib": [
|
|
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": "
|
|
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
|
-
"@/*": [
|
|
26
|
+
"@/*": [
|
|
27
|
+
"./src/*"
|
|
28
|
+
]
|
|
23
29
|
}
|
|
24
30
|
},
|
|
25
|
-
"include": [
|
|
26
|
-
|
|
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
|
-
}
|