@create-lft-app/cli 1.0.14 → 1.1.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/dist/bin/cli.js +13 -146
- package/dist/bin/cli.js.map +1 -1
- package/dist/src/index.js +11 -144
- package/dist/src/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/app/auth/login/page.tsx +0 -153
- package/templates/app/dashboard/page.tsx +0 -102
- package/templates/app/globals.css +0 -249
- package/templates/app/layout.tsx +0 -40
- package/templates/app/page.tsx +0 -5
- package/templates/components/dashboard/widget.tsx +0 -113
- package/templates/components/layout/admin-midday-sidebar.tsx +0 -247
- package/templates/components/layout/admin-sidebar.tsx +0 -146
- package/templates/components/layout/header.tsx +0 -71
- package/templates/components/layout/main-content.tsx +0 -28
- package/templates/components/layout/midday-sidebar.tsx +0 -381
- package/templates/components/layout/nav-user.tsx +0 -108
- package/templates/components/layout/page-header.tsx +0 -95
- package/templates/components/layout/sidebar-context.tsx +0 -33
- package/templates/components/layout/sidebar.tsx +0 -194
- package/templates/components/layout/suspension-banner.tsx +0 -21
- package/templates/components/ui/accordion.tsx +0 -58
- package/templates/components/ui/alert-dialog.tsx +0 -165
- package/templates/components/ui/alert.tsx +0 -66
- package/templates/components/ui/avatar.tsx +0 -55
- package/templates/components/ui/badge.tsx +0 -50
- package/templates/components/ui/button.tsx +0 -89
- package/templates/components/ui/calendar.tsx +0 -220
- package/templates/components/ui/card.tsx +0 -89
- package/templates/components/ui/checkbox.tsx +0 -38
- package/templates/components/ui/collapsible.tsx +0 -33
- package/templates/components/ui/command.tsx +0 -196
- package/templates/components/ui/dialog.tsx +0 -153
- package/templates/components/ui/dropdown-menu.tsx +0 -280
- package/templates/components/ui/form.tsx +0 -171
- package/templates/components/ui/icons.tsx +0 -167
- package/templates/components/ui/input.tsx +0 -28
- package/templates/components/ui/label.tsx +0 -25
- package/templates/components/ui/popover.tsx +0 -59
- package/templates/components/ui/progress.tsx +0 -32
- package/templates/components/ui/radio-group.tsx +0 -45
- package/templates/components/ui/scroll-area.tsx +0 -63
- package/templates/components/ui/select.tsx +0 -208
- package/templates/components/ui/separator.tsx +0 -28
- package/templates/components/ui/sheet.tsx +0 -146
- package/templates/components/ui/sidebar.tsx +0 -726
- package/templates/components/ui/skeleton.tsx +0 -15
- package/templates/components/ui/slider.tsx +0 -58
- package/templates/components/ui/sonner.tsx +0 -47
- package/templates/components/ui/spinner.tsx +0 -27
- package/templates/components/ui/submit-button.tsx +0 -47
- package/templates/components/ui/switch.tsx +0 -31
- package/templates/components/ui/table.tsx +0 -120
- package/templates/components/ui/tabs.tsx +0 -75
- package/templates/components/ui/textarea.tsx +0 -26
- package/templates/components/ui/tooltip.tsx +0 -70
- package/templates/hooks/use-mobile.ts +0 -21
- package/templates/lib/supabase/client.ts +0 -8
- package/templates/lib/supabase/server.ts +0 -29
- package/templates/lib/utils.ts +0 -6
- package/templates/modules/auth/actions/auth-actions.ts +0 -12
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { Header } from "@/components/layout/header"
|
|
2
|
-
import { Widget, WidgetGrid } from "@/components/dashboard/widget"
|
|
3
|
-
import {
|
|
4
|
-
Users,
|
|
5
|
-
DollarSign,
|
|
6
|
-
Activity,
|
|
7
|
-
TrendingUp,
|
|
8
|
-
Building2,
|
|
9
|
-
FileText,
|
|
10
|
-
AlertCircle,
|
|
11
|
-
CheckCircle,
|
|
12
|
-
} from "lucide-react"
|
|
13
|
-
|
|
14
|
-
export default function DashboardPage() {
|
|
15
|
-
return (
|
|
16
|
-
<>
|
|
17
|
-
<Header title="Dashboard" />
|
|
18
|
-
<main className="flex-1 p-6">
|
|
19
|
-
<div className="space-y-6">
|
|
20
|
-
<div>
|
|
21
|
-
<h2 className="text-2xl font-bold tracking-tight">
|
|
22
|
-
Bienvenido de vuelta
|
|
23
|
-
</h2>
|
|
24
|
-
<p className="text-muted-foreground">
|
|
25
|
-
Aquí está el resumen de tu actividad
|
|
26
|
-
</p>
|
|
27
|
-
</div>
|
|
28
|
-
|
|
29
|
-
<WidgetGrid>
|
|
30
|
-
<Widget
|
|
31
|
-
title="Usuarios Activos"
|
|
32
|
-
icon={<Users className="h-4 w-4" />}
|
|
33
|
-
value="1,234"
|
|
34
|
-
subValue="usuarios registrados"
|
|
35
|
-
trend={{ value: 12.5, positive: true }}
|
|
36
|
-
href="/admin/users"
|
|
37
|
-
/>
|
|
38
|
-
<Widget
|
|
39
|
-
title="Ingresos"
|
|
40
|
-
icon={<DollarSign className="h-4 w-4" />}
|
|
41
|
-
value="$45,231"
|
|
42
|
-
subValue="este mes"
|
|
43
|
-
trend={{ value: 8.2, positive: true }}
|
|
44
|
-
variant="success"
|
|
45
|
-
/>
|
|
46
|
-
<Widget
|
|
47
|
-
title="Organizaciones"
|
|
48
|
-
icon={<Building2 className="h-4 w-4" />}
|
|
49
|
-
value="89"
|
|
50
|
-
subValue="organizaciones activas"
|
|
51
|
-
trend={{ value: 3.1, positive: true }}
|
|
52
|
-
href="/admin/organizations"
|
|
53
|
-
/>
|
|
54
|
-
<Widget
|
|
55
|
-
title="Actividad"
|
|
56
|
-
icon={<Activity className="h-4 w-4" />}
|
|
57
|
-
value="2,345"
|
|
58
|
-
subValue="eventos hoy"
|
|
59
|
-
trend={{ value: 5.4, positive: true }}
|
|
60
|
-
/>
|
|
61
|
-
</WidgetGrid>
|
|
62
|
-
|
|
63
|
-
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
64
|
-
<WidgetGrid>
|
|
65
|
-
<Widget
|
|
66
|
-
title="Documentos Pendientes"
|
|
67
|
-
icon={<FileText className="h-4 w-4" />}
|
|
68
|
-
value="23"
|
|
69
|
-
subValue="requieren revisión"
|
|
70
|
-
variant="warning"
|
|
71
|
-
/>
|
|
72
|
-
<Widget
|
|
73
|
-
title="Alertas"
|
|
74
|
-
icon={<AlertCircle className="h-4 w-4" />}
|
|
75
|
-
value="5"
|
|
76
|
-
subValue="alertas activas"
|
|
77
|
-
variant="danger"
|
|
78
|
-
/>
|
|
79
|
-
</WidgetGrid>
|
|
80
|
-
|
|
81
|
-
<WidgetGrid>
|
|
82
|
-
<Widget
|
|
83
|
-
title="Tareas Completadas"
|
|
84
|
-
icon={<CheckCircle className="h-4 w-4" />}
|
|
85
|
-
value="156"
|
|
86
|
-
subValue="esta semana"
|
|
87
|
-
variant="success"
|
|
88
|
-
/>
|
|
89
|
-
<Widget
|
|
90
|
-
title="Crecimiento"
|
|
91
|
-
icon={<TrendingUp className="h-4 w-4" />}
|
|
92
|
-
value="+24%"
|
|
93
|
-
subValue="vs mes anterior"
|
|
94
|
-
trend={{ value: 24, positive: true }}
|
|
95
|
-
/>
|
|
96
|
-
</WidgetGrid>
|
|
97
|
-
</div>
|
|
98
|
-
</div>
|
|
99
|
-
</main>
|
|
100
|
-
</>
|
|
101
|
-
)
|
|
102
|
-
}
|
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
@import "tailwindcss";
|
|
2
|
-
@import "tw-animate-css";
|
|
3
|
-
|
|
4
|
-
@custom-variant dark (&:is(.dark *));
|
|
5
|
-
|
|
6
|
-
@theme inline {
|
|
7
|
-
--color-background: var(--background);
|
|
8
|
-
--color-foreground: var(--foreground);
|
|
9
|
-
--font-sans: var(--font-geist-sans);
|
|
10
|
-
--font-mono: var(--font-geist-mono);
|
|
11
|
-
--color-sidebar-ring: var(--sidebar-ring);
|
|
12
|
-
--color-sidebar-border: var(--sidebar-border);
|
|
13
|
-
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
14
|
-
--color-sidebar-accent: var(--sidebar-accent);
|
|
15
|
-
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
16
|
-
--color-sidebar-primary: var(--sidebar-primary);
|
|
17
|
-
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
18
|
-
--color-sidebar: var(--sidebar);
|
|
19
|
-
--color-chart-5: var(--chart-5);
|
|
20
|
-
--color-chart-4: var(--chart-4);
|
|
21
|
-
--color-chart-3: var(--chart-3);
|
|
22
|
-
--color-chart-2: var(--chart-2);
|
|
23
|
-
--color-chart-1: var(--chart-1);
|
|
24
|
-
--color-ring: var(--ring);
|
|
25
|
-
--color-input: var(--input);
|
|
26
|
-
--color-border: var(--border);
|
|
27
|
-
--color-destructive: var(--destructive);
|
|
28
|
-
--color-accent-foreground: var(--accent-foreground);
|
|
29
|
-
--color-accent: var(--accent);
|
|
30
|
-
--color-muted-foreground: var(--muted-foreground);
|
|
31
|
-
--color-muted: var(--muted);
|
|
32
|
-
--color-secondary-foreground: var(--secondary-foreground);
|
|
33
|
-
--color-secondary: var(--secondary);
|
|
34
|
-
--color-primary-foreground: var(--primary-foreground);
|
|
35
|
-
--color-primary: var(--primary);
|
|
36
|
-
--color-popover-foreground: var(--popover-foreground);
|
|
37
|
-
--color-popover: var(--popover);
|
|
38
|
-
--color-card-foreground: var(--card-foreground);
|
|
39
|
-
--color-card: var(--card);
|
|
40
|
-
--radius-sm: calc(var(--radius) - 4px);
|
|
41
|
-
--radius-md: calc(var(--radius) - 2px);
|
|
42
|
-
--radius-lg: var(--radius);
|
|
43
|
-
--radius-xl: calc(var(--radius) + 4px);
|
|
44
|
-
--radius-2xl: calc(var(--radius) + 8px);
|
|
45
|
-
--radius-3xl: calc(var(--radius) + 12px);
|
|
46
|
-
--radius-4xl: calc(var(--radius) + 16px);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/* ========================================
|
|
50
|
-
MIDDAY DESIGN SYSTEM - Color Palette
|
|
51
|
-
======================================== */
|
|
52
|
-
|
|
53
|
-
:root {
|
|
54
|
-
--radius: 0.5rem;
|
|
55
|
-
|
|
56
|
-
/* Midday Dark Theme - Pure blacks (DEFAULT) */
|
|
57
|
-
--background: hsl(0 0% 5%);
|
|
58
|
-
--foreground: hsl(0 0% 98%);
|
|
59
|
-
|
|
60
|
-
--card: hsl(0 0% 7%);
|
|
61
|
-
--card-foreground: hsl(0 0% 98%);
|
|
62
|
-
|
|
63
|
-
--popover: hsl(0 0% 4%);
|
|
64
|
-
--popover-foreground: hsl(0 0% 98%);
|
|
65
|
-
|
|
66
|
-
--primary: hsl(0 0% 98%);
|
|
67
|
-
--primary-foreground: hsl(0 0% 9%);
|
|
68
|
-
|
|
69
|
-
--secondary: hsl(0 0% 11%);
|
|
70
|
-
--secondary-foreground: hsl(0 0% 98%);
|
|
71
|
-
|
|
72
|
-
--muted: hsl(0 0% 11%);
|
|
73
|
-
--muted-foreground: hsl(0 0% 64%);
|
|
74
|
-
|
|
75
|
-
--accent: hsl(0 0% 11%);
|
|
76
|
-
--accent-foreground: hsl(0 0% 98%);
|
|
77
|
-
|
|
78
|
-
--destructive: hsl(0 63% 31%);
|
|
79
|
-
--destructive-foreground: hsl(0 0% 98%);
|
|
80
|
-
|
|
81
|
-
--border: hsl(0 0% 11%);
|
|
82
|
-
--input: hsl(0 0% 15%);
|
|
83
|
-
--ring: hsl(0 0% 83%);
|
|
84
|
-
|
|
85
|
-
/* Chart colors dark */
|
|
86
|
-
--chart-1: hsl(220 70% 50%);
|
|
87
|
-
--chart-2: hsl(160 60% 45%);
|
|
88
|
-
--chart-3: hsl(30 80% 55%);
|
|
89
|
-
--chart-4: hsl(280 65% 60%);
|
|
90
|
-
--chart-5: hsl(340 75% 55%);
|
|
91
|
-
|
|
92
|
-
/* Sidebar dark */
|
|
93
|
-
--sidebar: hsl(0 0% 7%);
|
|
94
|
-
--sidebar-foreground: hsl(0 0% 98%);
|
|
95
|
-
--sidebar-primary: hsl(0 0% 98%);
|
|
96
|
-
--sidebar-primary-foreground: hsl(0 0% 9%);
|
|
97
|
-
--sidebar-accent: hsl(0 0% 11%);
|
|
98
|
-
--sidebar-accent-foreground: hsl(0 0% 98%);
|
|
99
|
-
--sidebar-border: hsl(0 0% 11%);
|
|
100
|
-
--sidebar-ring: hsl(0 0% 83%);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
.light {
|
|
104
|
-
/* Midday Light Theme - Warm beige/neutral palette */
|
|
105
|
-
--background: hsl(0 0% 100%);
|
|
106
|
-
--foreground: hsl(0 0% 7%);
|
|
107
|
-
|
|
108
|
-
--card: hsl(45 18% 96%);
|
|
109
|
-
--card-foreground: hsl(0 0% 4%);
|
|
110
|
-
|
|
111
|
-
--popover: hsl(0 0% 100%);
|
|
112
|
-
--popover-foreground: hsl(0 0% 4%);
|
|
113
|
-
|
|
114
|
-
--primary: hsl(240 6% 10%);
|
|
115
|
-
--primary-foreground: hsl(0 0% 98%);
|
|
116
|
-
|
|
117
|
-
--secondary: hsl(40 11% 89%);
|
|
118
|
-
--secondary-foreground: hsl(0 0% 9%);
|
|
119
|
-
|
|
120
|
-
--muted: hsl(40 11% 89%);
|
|
121
|
-
--muted-foreground: hsl(0 0% 38%);
|
|
122
|
-
|
|
123
|
-
--accent: hsl(40 10% 94%);
|
|
124
|
-
--accent-foreground: hsl(0 0% 9%);
|
|
125
|
-
|
|
126
|
-
--destructive: hsl(0 84% 60%);
|
|
127
|
-
--destructive-foreground: hsl(0 0% 98%);
|
|
128
|
-
|
|
129
|
-
--border: hsl(45 5% 85%);
|
|
130
|
-
--input: hsl(0 0% 90%);
|
|
131
|
-
--ring: hsl(0 0% 4%);
|
|
132
|
-
|
|
133
|
-
/* Chart colors */
|
|
134
|
-
--chart-1: hsl(12 76% 61%);
|
|
135
|
-
--chart-2: hsl(173 58% 39%);
|
|
136
|
-
--chart-3: hsl(197 37% 24%);
|
|
137
|
-
--chart-4: hsl(43 74% 66%);
|
|
138
|
-
--chart-5: hsl(27 87% 67%);
|
|
139
|
-
|
|
140
|
-
/* Sidebar - Midday style */
|
|
141
|
-
--sidebar: hsl(0 0% 99%);
|
|
142
|
-
--sidebar-foreground: hsl(0 0% 7%);
|
|
143
|
-
--sidebar-primary: hsl(240 6% 10%);
|
|
144
|
-
--sidebar-primary-foreground: hsl(0 0% 98%);
|
|
145
|
-
--sidebar-accent: hsl(40 10% 97%);
|
|
146
|
-
--sidebar-accent-foreground: hsl(0 0% 9%);
|
|
147
|
-
--sidebar-border: hsl(45 5% 85%);
|
|
148
|
-
--sidebar-ring: hsl(0 0% 4%);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
@layer base {
|
|
152
|
-
* {
|
|
153
|
-
@apply border-border outline-ring/50;
|
|
154
|
-
}
|
|
155
|
-
body {
|
|
156
|
-
@apply bg-background text-foreground;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/* ========================================
|
|
161
|
-
MIDDAY UTILITIES
|
|
162
|
-
======================================== */
|
|
163
|
-
|
|
164
|
-
/* Scrollbar hide utility */
|
|
165
|
-
.scrollbar-hide {
|
|
166
|
-
-ms-overflow-style: none;
|
|
167
|
-
scrollbar-width: none;
|
|
168
|
-
}
|
|
169
|
-
.scrollbar-hide::-webkit-scrollbar {
|
|
170
|
-
display: none;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/* ========================================
|
|
174
|
-
MIDDAY ANIMATIONS
|
|
175
|
-
======================================== */
|
|
176
|
-
|
|
177
|
-
@keyframes shimmer {
|
|
178
|
-
0% {
|
|
179
|
-
transform: translateX(-100%);
|
|
180
|
-
}
|
|
181
|
-
100% {
|
|
182
|
-
transform: translateX(100%);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
@keyframes accordion-down {
|
|
187
|
-
from {
|
|
188
|
-
height: 0;
|
|
189
|
-
}
|
|
190
|
-
to {
|
|
191
|
-
height: var(--radix-accordion-content-height);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
@keyframes accordion-up {
|
|
196
|
-
from {
|
|
197
|
-
height: var(--radix-accordion-content-height);
|
|
198
|
-
}
|
|
199
|
-
to {
|
|
200
|
-
height: 0;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
@keyframes dialog-overlay-show {
|
|
205
|
-
from {
|
|
206
|
-
opacity: 0;
|
|
207
|
-
}
|
|
208
|
-
to {
|
|
209
|
-
opacity: 1;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
@keyframes dialog-content-show {
|
|
214
|
-
from {
|
|
215
|
-
opacity: 0;
|
|
216
|
-
transform: translate(-50%, -50%) scale(0.97);
|
|
217
|
-
}
|
|
218
|
-
to {
|
|
219
|
-
opacity: 1;
|
|
220
|
-
transform: translate(-50%, -50%) scale(1);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
@keyframes caret-blink {
|
|
225
|
-
0%, 70%, 100% {
|
|
226
|
-
opacity: 1;
|
|
227
|
-
}
|
|
228
|
-
20%, 50% {
|
|
229
|
-
opacity: 0;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/* Loading ellipsis animation */
|
|
234
|
-
@keyframes loading-ellipsis {
|
|
235
|
-
0% { content: ''; }
|
|
236
|
-
25% { content: '.'; }
|
|
237
|
-
50% { content: '..'; }
|
|
238
|
-
75% { content: '...'; }
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
.loading-ellipsis::after {
|
|
242
|
-
content: '';
|
|
243
|
-
animation: loading-ellipsis 1.5s infinite;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/* Animate shimmer class */
|
|
247
|
-
.animate-shimmer {
|
|
248
|
-
animation: shimmer 2.5s linear infinite;
|
|
249
|
-
}
|
package/templates/app/layout.tsx
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import type { Metadata } from "next"
|
|
2
|
-
import { Inter } from "next/font/google"
|
|
3
|
-
import "./globals.css"
|
|
4
|
-
import { SidebarProvider, SidebarInset } from "@/components/ui/sidebar"
|
|
5
|
-
import { AdminSidebar } from "@/components/layout/admin-sidebar"
|
|
6
|
-
import { Toaster } from "@/components/ui/sonner"
|
|
7
|
-
|
|
8
|
-
const inter = Inter({ subsets: ["latin"] })
|
|
9
|
-
|
|
10
|
-
export const metadata: Metadata = {
|
|
11
|
-
title: "Dashboard - LFT App",
|
|
12
|
-
description: "Panel de administración creado con create-lft-app",
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export default function RootLayout({
|
|
16
|
-
children,
|
|
17
|
-
}: {
|
|
18
|
-
children: React.ReactNode
|
|
19
|
-
}) {
|
|
20
|
-
// Usuario de ejemplo - en producción esto vendría de Supabase Auth
|
|
21
|
-
const user = {
|
|
22
|
-
email: "admin@ejemplo.com",
|
|
23
|
-
full_name: "Admin Usuario",
|
|
24
|
-
role: "Super Admin",
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
<html lang="es" className="dark" suppressHydrationWarning>
|
|
29
|
-
<body className={`${inter.className} font-sans antialiased`}>
|
|
30
|
-
<SidebarProvider>
|
|
31
|
-
<AdminSidebar user={user} />
|
|
32
|
-
<SidebarInset>
|
|
33
|
-
{children}
|
|
34
|
-
</SidebarInset>
|
|
35
|
-
</SidebarProvider>
|
|
36
|
-
<Toaster />
|
|
37
|
-
</body>
|
|
38
|
-
</html>
|
|
39
|
-
)
|
|
40
|
-
}
|
package/templates/app/page.tsx
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { cn } from '@/lib/utils'
|
|
4
|
-
import Link from 'next/link'
|
|
5
|
-
|
|
6
|
-
interface WidgetProps {
|
|
7
|
-
title: string
|
|
8
|
-
icon: React.ReactNode
|
|
9
|
-
description?: string
|
|
10
|
-
value: string | number
|
|
11
|
-
subValue?: string
|
|
12
|
-
href?: string
|
|
13
|
-
trend?: {
|
|
14
|
-
value: number
|
|
15
|
-
positive?: boolean
|
|
16
|
-
}
|
|
17
|
-
variant?: 'default' | 'warning' | 'danger' | 'success'
|
|
18
|
-
children?: React.ReactNode
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function Widget({
|
|
22
|
-
title,
|
|
23
|
-
icon,
|
|
24
|
-
description,
|
|
25
|
-
value,
|
|
26
|
-
subValue,
|
|
27
|
-
href,
|
|
28
|
-
trend,
|
|
29
|
-
variant = 'default',
|
|
30
|
-
children,
|
|
31
|
-
}: WidgetProps) {
|
|
32
|
-
const content = (
|
|
33
|
-
<div
|
|
34
|
-
className={cn(
|
|
35
|
-
'bg-background border border-border p-4 h-[180px] flex flex-col justify-between transition-all duration-200',
|
|
36
|
-
href && 'cursor-pointer hover:bg-accent/50 hover:border-border/80',
|
|
37
|
-
)}
|
|
38
|
-
>
|
|
39
|
-
<div>
|
|
40
|
-
<div className="flex items-center gap-2 mb-3">
|
|
41
|
-
<span className="text-[#606060]">{icon}</span>
|
|
42
|
-
<h3 className="text-xs text-[#606060] font-medium uppercase tracking-wide">{title}</h3>
|
|
43
|
-
</div>
|
|
44
|
-
|
|
45
|
-
{description && (
|
|
46
|
-
<p className="text-sm text-[#878787]">{description}</p>
|
|
47
|
-
)}
|
|
48
|
-
</div>
|
|
49
|
-
|
|
50
|
-
<div>
|
|
51
|
-
{children ? (
|
|
52
|
-
children
|
|
53
|
-
) : (
|
|
54
|
-
<div className="flex flex-col gap-1">
|
|
55
|
-
<div className="flex items-baseline gap-2">
|
|
56
|
-
<span
|
|
57
|
-
className={cn(
|
|
58
|
-
'text-3xl font-medium tabular-nums',
|
|
59
|
-
variant === 'danger' && 'text-destructive',
|
|
60
|
-
variant === 'warning' && 'text-yellow-500',
|
|
61
|
-
variant === 'success' && 'text-green-500',
|
|
62
|
-
)}
|
|
63
|
-
>
|
|
64
|
-
{value}
|
|
65
|
-
</span>
|
|
66
|
-
{trend && (
|
|
67
|
-
<span
|
|
68
|
-
className={cn(
|
|
69
|
-
'text-xs',
|
|
70
|
-
trend.positive ? 'text-green-500' : 'text-destructive'
|
|
71
|
-
)}
|
|
72
|
-
>
|
|
73
|
-
{trend.positive ? '+' : ''}{trend.value}%
|
|
74
|
-
</span>
|
|
75
|
-
)}
|
|
76
|
-
</div>
|
|
77
|
-
{subValue && (
|
|
78
|
-
<span className="text-xs text-[#878787]">{subValue}</span>
|
|
79
|
-
)}
|
|
80
|
-
</div>
|
|
81
|
-
)}
|
|
82
|
-
|
|
83
|
-
{href && (
|
|
84
|
-
<span className="text-xs text-[#878787] group-hover:text-foreground transition-colors mt-2 block">
|
|
85
|
-
Ver más →
|
|
86
|
-
</span>
|
|
87
|
-
)}
|
|
88
|
-
</div>
|
|
89
|
-
</div>
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
if (href) {
|
|
93
|
-
return (
|
|
94
|
-
<Link href={href} className="group">
|
|
95
|
-
{content}
|
|
96
|
-
</Link>
|
|
97
|
-
)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return content
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
interface WidgetGridProps {
|
|
104
|
-
children: React.ReactNode
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export function WidgetGrid({ children }: WidgetGridProps) {
|
|
108
|
-
return (
|
|
109
|
-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
110
|
-
{children}
|
|
111
|
-
</div>
|
|
112
|
-
)
|
|
113
|
-
}
|