luxlabs 1.0.1 → 1.0.3
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 +1 -1
- package/templates/interface-boilerplate/app/api/auth/[...all]/route.ts +52 -0
- package/templates/interface-boilerplate/app/auth/callback/page.tsx +22 -31
- package/templates/interface-boilerplate/app/dashboard/page.tsx +41 -214
- package/templates/interface-boilerplate/app/favicon.ico +0 -0
- package/templates/interface-boilerplate/app/globals.css +18 -113
- package/templates/interface-boilerplate/app/layout.tsx +21 -33
- package/templates/interface-boilerplate/app/page.tsx +37 -116
- package/templates/interface-boilerplate/app/settings/page.tsx +71 -0
- package/templates/interface-boilerplate/app/sign-in/page.tsx +19 -0
- package/templates/interface-boilerplate/app/sign-up/page.tsx +19 -0
- package/templates/interface-boilerplate/components/auth/sign-in-form.tsx +144 -0
- package/templates/interface-boilerplate/components/auth/sign-up-form.tsx +236 -0
- package/templates/interface-boilerplate/components/ui/badge.tsx +18 -14
- package/templates/interface-boilerplate/components/ui/button.tsx +29 -24
- package/templates/interface-boilerplate/components/ui/card.tsx +76 -57
- package/templates/interface-boilerplate/components/ui/input.tsx +8 -9
- package/templates/interface-boilerplate/eslint.config.mjs +18 -0
- package/templates/interface-boilerplate/lib/auth-client.ts +18 -0
- package/templates/interface-boilerplate/lib/auth.config.ts +111 -0
- package/templates/interface-boilerplate/lib/lux.ts +8 -0
- package/templates/interface-boilerplate/lib/utils.ts +3 -3
- package/templates/interface-boilerplate/middleware.ts +60 -0
- package/templates/interface-boilerplate/next.config.ts +7 -0
- package/templates/interface-boilerplate/package-lock.json +6855 -0
- package/templates/interface-boilerplate/package.json +18 -37
- package/templates/interface-boilerplate/postcss.config.mjs +7 -0
- package/templates/interface-boilerplate/tsconfig.json +18 -4
- package/templates/interface-boilerplate/.env.example +0 -2
- package/templates/interface-boilerplate/.eslintrc.json +0 -4
- package/templates/interface-boilerplate/app/login/page.tsx +0 -178
- package/templates/interface-boilerplate/app/signup/page.tsx +0 -199
- package/templates/interface-boilerplate/components/AnalyticsProvider.tsx +0 -142
- package/templates/interface-boilerplate/components/AuthGuard.tsx +0 -76
- package/templates/interface-boilerplate/components/ErrorBoundary.tsx +0 -106
- package/templates/interface-boilerplate/components/theme-provider.tsx +0 -9
- package/templates/interface-boilerplate/components/theme-toggle.tsx +0 -39
- package/templates/interface-boilerplate/components/ui/avatar.tsx +0 -46
- package/templates/interface-boilerplate/components/ui/checkbox.tsx +0 -27
- package/templates/interface-boilerplate/components/ui/dialog.tsx +0 -100
- package/templates/interface-boilerplate/components/ui/dropdown-menu.tsx +0 -173
- package/templates/interface-boilerplate/components/ui/index.ts +0 -53
- package/templates/interface-boilerplate/components/ui/label.tsx +0 -20
- package/templates/interface-boilerplate/components/ui/progress.tsx +0 -24
- package/templates/interface-boilerplate/components/ui/select.tsx +0 -149
- package/templates/interface-boilerplate/components/ui/separator.tsx +0 -25
- package/templates/interface-boilerplate/components/ui/skeleton.tsx +0 -12
- package/templates/interface-boilerplate/components/ui/switch.tsx +0 -28
- package/templates/interface-boilerplate/components/ui/tabs.tsx +0 -54
- package/templates/interface-boilerplate/components/ui/textarea.tsx +0 -22
- package/templates/interface-boilerplate/components/ui/tooltip.tsx +0 -29
- package/templates/interface-boilerplate/lib/analytics.ts +0 -182
- package/templates/interface-boilerplate/lib/auth-context.tsx +0 -83
- package/templates/interface-boilerplate/lib/auth.ts +0 -199
- package/templates/interface-boilerplate/lib/callFlow.ts +0 -234
- package/templates/interface-boilerplate/lib/flowTracer.ts +0 -195
- package/templates/interface-boilerplate/lib/hooks/.gitkeep +0 -0
- package/templates/interface-boilerplate/lib/stores/.gitkeep +0 -0
- package/templates/interface-boilerplate/next.config.js +0 -6
- package/templates/interface-boilerplate/postcss.config.js +0 -6
- package/templates/interface-boilerplate/tailwind.config.js +0 -103
package/package.json
CHANGED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth API Proxy
|
|
3
|
+
*
|
|
4
|
+
* Proxies all auth requests to the Lux Studio API worker.
|
|
5
|
+
* The worker handles Better Auth with the org's database.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const API_URL = process.env.NEXT_PUBLIC_LUX_API_URL || "https://v2.uselux.ai";
|
|
9
|
+
const ORG_ID = process.env.NEXT_PUBLIC_LUX_ORG_ID || "";
|
|
10
|
+
|
|
11
|
+
async function proxyRequest(request: Request): Promise<Response> {
|
|
12
|
+
const url = new URL(request.url);
|
|
13
|
+
const path = url.pathname.replace("/api/auth", "");
|
|
14
|
+
const targetUrl = `${API_URL}/api/auth${path}${url.search}`;
|
|
15
|
+
|
|
16
|
+
// Forward the request to the worker
|
|
17
|
+
const headers = new Headers(request.headers);
|
|
18
|
+
headers.set("X-Org-Id", ORG_ID);
|
|
19
|
+
|
|
20
|
+
const response = await fetch(targetUrl, {
|
|
21
|
+
method: request.method,
|
|
22
|
+
headers,
|
|
23
|
+
body: request.method !== "GET" && request.method !== "HEAD" ? request.body : undefined,
|
|
24
|
+
// @ts-ignore - duplex is needed for streaming body
|
|
25
|
+
duplex: "half",
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Forward response back with CORS headers
|
|
29
|
+
const responseHeaders = new Headers(response.headers);
|
|
30
|
+
|
|
31
|
+
return new Response(response.body, {
|
|
32
|
+
status: response.status,
|
|
33
|
+
statusText: response.statusText,
|
|
34
|
+
headers: responseHeaders,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function GET(request: Request) {
|
|
39
|
+
return proxyRequest(request);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function POST(request: Request) {
|
|
43
|
+
return proxyRequest(request);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function PUT(request: Request) {
|
|
47
|
+
return proxyRequest(request);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function DELETE(request: Request) {
|
|
51
|
+
return proxyRequest(request);
|
|
52
|
+
}
|
|
@@ -1,43 +1,34 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
2
|
|
|
3
|
-
import { useEffect
|
|
4
|
-
import { useRouter } from
|
|
5
|
-
import {
|
|
6
|
-
import { Loader2 } from 'lucide-react';
|
|
3
|
+
import { useEffect } from "react";
|
|
4
|
+
import { useRouter } from "next/navigation";
|
|
5
|
+
import { useSession } from "@/lib/auth-client";
|
|
7
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Auth Callback Page
|
|
9
|
+
*
|
|
10
|
+
* Handles redirect after OAuth sign-in.
|
|
11
|
+
* Simply redirects authenticated users to dashboard.
|
|
12
|
+
*/
|
|
8
13
|
export default function AuthCallbackPage() {
|
|
9
14
|
const router = useRouter();
|
|
10
|
-
const
|
|
15
|
+
const { data: session, isPending } = useSession();
|
|
11
16
|
|
|
12
17
|
useEffect(() => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
if (!isPending) {
|
|
19
|
+
if (session) {
|
|
20
|
+
// Authenticated - go to dashboard
|
|
21
|
+
router.push("/dashboard");
|
|
22
|
+
} else {
|
|
23
|
+
// Not authenticated - go to sign in
|
|
24
|
+
router.push("/sign-in");
|
|
25
|
+
}
|
|
18
26
|
}
|
|
19
|
-
}, [router]);
|
|
20
|
-
|
|
21
|
-
if (error) {
|
|
22
|
-
return (
|
|
23
|
-
<div className="min-h-screen flex items-center justify-center">
|
|
24
|
-
<div className="text-center">
|
|
25
|
-
<h1 className="text-2xl font-bold text-destructive mb-2">Authentication Failed</h1>
|
|
26
|
-
<p className="text-muted-foreground mb-4">{error}</p>
|
|
27
|
-
<a href="/login" className="text-primary hover:underline">
|
|
28
|
-
Back to login
|
|
29
|
-
</a>
|
|
30
|
-
</div>
|
|
31
|
-
</div>
|
|
32
|
-
);
|
|
33
|
-
}
|
|
27
|
+
}, [session, isPending, router]);
|
|
34
28
|
|
|
35
29
|
return (
|
|
36
|
-
<div className="min-h-screen flex items-center justify-center">
|
|
37
|
-
<div className="text-
|
|
38
|
-
<Loader2 className="h-8 w-8 animate-spin mx-auto mb-4 text-primary" />
|
|
39
|
-
<p className="text-muted-foreground">Completing sign in...</p>
|
|
40
|
-
</div>
|
|
30
|
+
<div className="min-h-screen flex items-center justify-center bg-gray-50">
|
|
31
|
+
<div className="text-gray-600">Completing sign in...</div>
|
|
41
32
|
</div>
|
|
42
33
|
);
|
|
43
34
|
}
|
|
@@ -1,226 +1,53 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { useRouter } from
|
|
6
|
-
import { motion } from 'framer-motion';
|
|
7
|
-
import {
|
|
8
|
-
LayoutDashboard,
|
|
9
|
-
Users,
|
|
10
|
-
CreditCard,
|
|
11
|
-
Activity,
|
|
12
|
-
LogOut,
|
|
13
|
-
TrendingUp,
|
|
14
|
-
TrendingDown,
|
|
15
|
-
Menu,
|
|
16
|
-
} from 'lucide-react';
|
|
17
|
-
import { Button } from '@/components/ui/button';
|
|
18
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
|
19
|
-
import { Badge } from '@/components/ui/badge';
|
|
20
|
-
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
|
21
|
-
import { Skeleton } from '@/components/ui/skeleton';
|
|
22
|
-
import { ThemeToggle } from '@/components/theme-toggle';
|
|
23
|
-
import {
|
|
24
|
-
DropdownMenu,
|
|
25
|
-
DropdownMenuContent,
|
|
26
|
-
DropdownMenuItem,
|
|
27
|
-
DropdownMenuLabel,
|
|
28
|
-
DropdownMenuSeparator,
|
|
29
|
-
DropdownMenuTrigger,
|
|
30
|
-
} from '@/components/ui/dropdown-menu';
|
|
31
|
-
import { useAuth } from '@/lib/auth-context';
|
|
3
|
+
import { useEffect } from "react";
|
|
4
|
+
import { useSession, signOut } from "@/lib/auth-client";
|
|
5
|
+
import { useRouter } from "next/navigation";
|
|
32
6
|
|
|
33
|
-
|
|
34
|
-
{
|
|
35
|
-
{ title: 'Revenue', value: '$45,231', change: '+8%', trend: 'up', icon: CreditCard },
|
|
36
|
-
{ title: 'Active Now', value: '573', change: '-3%', trend: 'down', icon: Activity },
|
|
37
|
-
{ title: 'Growth', value: '23.5%', change: '+5%', trend: 'up', icon: TrendingUp },
|
|
38
|
-
];
|
|
39
|
-
|
|
40
|
-
function DashboardContent() {
|
|
41
|
-
const [loading, setLoading] = useState(true);
|
|
7
|
+
export default function DashboardPage() {
|
|
8
|
+
const { data: session, isPending } = useSession();
|
|
42
9
|
const router = useRouter();
|
|
43
|
-
const { user, signOut } = useAuth();
|
|
44
10
|
|
|
45
11
|
useEffect(() => {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
12
|
+
if (!isPending && !session) {
|
|
13
|
+
router.push("/sign-in");
|
|
14
|
+
}
|
|
15
|
+
}, [isPending, session, router]);
|
|
16
|
+
|
|
17
|
+
if (isPending) {
|
|
18
|
+
return (
|
|
19
|
+
<div className="min-h-screen flex items-center justify-center">
|
|
20
|
+
<p>Loading...</p>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!session) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function handleSignOut() {
|
|
55
30
|
await signOut();
|
|
56
|
-
router.push(
|
|
57
|
-
}
|
|
31
|
+
router.push("/");
|
|
32
|
+
}
|
|
58
33
|
|
|
59
34
|
return (
|
|
60
|
-
<div className="min-h-screen bg-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
<
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
</div>
|
|
73
|
-
<div className="flex items-center space-x-4">
|
|
74
|
-
<ThemeToggle />
|
|
75
|
-
<DropdownMenu>
|
|
76
|
-
<DropdownMenuTrigger asChild>
|
|
77
|
-
<Button variant="ghost" className="relative h-8 w-8 rounded-full">
|
|
78
|
-
<Avatar className="h-8 w-8">
|
|
79
|
-
<AvatarImage src={user?.image || '/avatar.png'} alt={user?.name || 'User'} />
|
|
80
|
-
<AvatarFallback>{user?.name?.charAt(0).toUpperCase() || 'U'}</AvatarFallback>
|
|
81
|
-
</Avatar>
|
|
82
|
-
</Button>
|
|
83
|
-
</DropdownMenuTrigger>
|
|
84
|
-
<DropdownMenuContent className="w-56" align="end" forceMount>
|
|
85
|
-
<DropdownMenuLabel className="font-normal">
|
|
86
|
-
<div className="flex flex-col space-y-1">
|
|
87
|
-
<p className="text-sm font-medium">{user?.name || 'User'}</p>
|
|
88
|
-
<p className="text-xs text-muted-foreground">{user?.email}</p>
|
|
89
|
-
</div>
|
|
90
|
-
</DropdownMenuLabel>
|
|
91
|
-
<DropdownMenuSeparator />
|
|
92
|
-
<DropdownMenuItem onClick={handleLogout}>
|
|
93
|
-
<LogOut className="mr-2 h-4 w-4" />
|
|
94
|
-
Log out
|
|
95
|
-
</DropdownMenuItem>
|
|
96
|
-
</DropdownMenuContent>
|
|
97
|
-
</DropdownMenu>
|
|
98
|
-
</div>
|
|
99
|
-
</div>
|
|
100
|
-
</header>
|
|
101
|
-
|
|
102
|
-
{/* Main Content */}
|
|
103
|
-
<main className="container mx-auto px-4 py-8">
|
|
104
|
-
<motion.div
|
|
105
|
-
initial={{ opacity: 0, y: 20 }}
|
|
106
|
-
animate={{ opacity: 1, y: 0 }}
|
|
107
|
-
transition={{ duration: 0.4 }}
|
|
35
|
+
<div className="min-h-screen bg-zinc-50 p-8">
|
|
36
|
+
<div className="max-w-2xl mx-auto bg-white p-8 rounded-lg shadow-md">
|
|
37
|
+
<h1 className="text-2xl font-semibold mb-4">Dashboard</h1>
|
|
38
|
+
<p className="text-zinc-600 mb-2">
|
|
39
|
+
Welcome, <span className="font-medium">{session.user.name}</span>!
|
|
40
|
+
</p>
|
|
41
|
+
<p className="text-zinc-600 mb-6">
|
|
42
|
+
Email: {session.user.email}
|
|
43
|
+
</p>
|
|
44
|
+
<button
|
|
45
|
+
onClick={handleSignOut}
|
|
46
|
+
className="px-4 py-2 bg-black text-white rounded-md hover:bg-zinc-800"
|
|
108
47
|
>
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
Here's what's happening with your app today.
|
|
113
|
-
</p>
|
|
114
|
-
</div>
|
|
115
|
-
|
|
116
|
-
{/* Stats Grid */}
|
|
117
|
-
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
|
118
|
-
{stats.map((stat, index) => (
|
|
119
|
-
<motion.div
|
|
120
|
-
key={stat.title}
|
|
121
|
-
initial={{ opacity: 0, y: 20 }}
|
|
122
|
-
animate={{ opacity: 1, y: 0 }}
|
|
123
|
-
transition={{ duration: 0.4, delay: index * 0.1 }}
|
|
124
|
-
>
|
|
125
|
-
<Card>
|
|
126
|
-
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
|
127
|
-
<CardTitle className="text-sm font-medium text-muted-foreground">
|
|
128
|
-
{stat.title}
|
|
129
|
-
</CardTitle>
|
|
130
|
-
<stat.icon className="h-4 w-4 text-muted-foreground" />
|
|
131
|
-
</CardHeader>
|
|
132
|
-
<CardContent>
|
|
133
|
-
{loading ? (
|
|
134
|
-
<Skeleton className="h-8 w-24" />
|
|
135
|
-
) : (
|
|
136
|
-
<>
|
|
137
|
-
<div className="text-2xl font-bold">{stat.value}</div>
|
|
138
|
-
<div className="flex items-center text-xs">
|
|
139
|
-
{stat.trend === 'up' ? (
|
|
140
|
-
<TrendingUp className="mr-1 h-3 w-3 text-green-500" />
|
|
141
|
-
) : (
|
|
142
|
-
<TrendingDown className="mr-1 h-3 w-3 text-red-500" />
|
|
143
|
-
)}
|
|
144
|
-
<span className={stat.trend === 'up' ? 'text-green-500' : 'text-red-500'}>
|
|
145
|
-
{stat.change}
|
|
146
|
-
</span>
|
|
147
|
-
<span className="ml-1 text-muted-foreground">from last month</span>
|
|
148
|
-
</div>
|
|
149
|
-
</>
|
|
150
|
-
)}
|
|
151
|
-
</CardContent>
|
|
152
|
-
</Card>
|
|
153
|
-
</motion.div>
|
|
154
|
-
))}
|
|
155
|
-
</div>
|
|
156
|
-
|
|
157
|
-
{/* Recent Activity */}
|
|
158
|
-
<div className="mt-8">
|
|
159
|
-
<Card>
|
|
160
|
-
<CardHeader>
|
|
161
|
-
<CardTitle>Recent Activity</CardTitle>
|
|
162
|
-
<CardDescription>Your latest actions and updates</CardDescription>
|
|
163
|
-
</CardHeader>
|
|
164
|
-
<CardContent>
|
|
165
|
-
{loading ? (
|
|
166
|
-
<div className="space-y-4">
|
|
167
|
-
{[1, 2, 3].map((i) => (
|
|
168
|
-
<div key={i} className="flex items-center space-x-4">
|
|
169
|
-
<Skeleton className="h-10 w-10 rounded-full" />
|
|
170
|
-
<div className="space-y-2">
|
|
171
|
-
<Skeleton className="h-4 w-48" />
|
|
172
|
-
<Skeleton className="h-3 w-24" />
|
|
173
|
-
</div>
|
|
174
|
-
</div>
|
|
175
|
-
))}
|
|
176
|
-
</div>
|
|
177
|
-
) : (
|
|
178
|
-
<div className="space-y-4">
|
|
179
|
-
<div className="flex items-center space-x-4">
|
|
180
|
-
<Avatar>
|
|
181
|
-
<AvatarFallback>US</AvatarFallback>
|
|
182
|
-
</Avatar>
|
|
183
|
-
<div className="flex-1">
|
|
184
|
-
<p className="text-sm font-medium">New user signed up</p>
|
|
185
|
-
<p className="text-xs text-muted-foreground">2 minutes ago</p>
|
|
186
|
-
</div>
|
|
187
|
-
<Badge>New</Badge>
|
|
188
|
-
</div>
|
|
189
|
-
<div className="flex items-center space-x-4">
|
|
190
|
-
<Avatar>
|
|
191
|
-
<AvatarFallback>OR</AvatarFallback>
|
|
192
|
-
</Avatar>
|
|
193
|
-
<div className="flex-1">
|
|
194
|
-
<p className="text-sm font-medium">Order #1234 completed</p>
|
|
195
|
-
<p className="text-xs text-muted-foreground">1 hour ago</p>
|
|
196
|
-
</div>
|
|
197
|
-
<Badge variant="success">Completed</Badge>
|
|
198
|
-
</div>
|
|
199
|
-
<div className="flex items-center space-x-4">
|
|
200
|
-
<Avatar>
|
|
201
|
-
<AvatarFallback>SY</AvatarFallback>
|
|
202
|
-
</Avatar>
|
|
203
|
-
<div className="flex-1">
|
|
204
|
-
<p className="text-sm font-medium">System update deployed</p>
|
|
205
|
-
<p className="text-xs text-muted-foreground">3 hours ago</p>
|
|
206
|
-
</div>
|
|
207
|
-
<Badge variant="secondary">System</Badge>
|
|
208
|
-
</div>
|
|
209
|
-
</div>
|
|
210
|
-
)}
|
|
211
|
-
</CardContent>
|
|
212
|
-
</Card>
|
|
213
|
-
</div>
|
|
214
|
-
</motion.div>
|
|
215
|
-
</main>
|
|
48
|
+
Sign Out
|
|
49
|
+
</button>
|
|
50
|
+
</div>
|
|
216
51
|
</div>
|
|
217
52
|
);
|
|
218
53
|
}
|
|
219
|
-
|
|
220
|
-
export default function DashboardPage() {
|
|
221
|
-
return (
|
|
222
|
-
<AuthGuard>
|
|
223
|
-
<DashboardContent />
|
|
224
|
-
</AuthGuard>
|
|
225
|
-
);
|
|
226
|
-
}
|
|
Binary file
|
|
@@ -1,121 +1,26 @@
|
|
|
1
|
-
@
|
|
2
|
-
@tailwind components;
|
|
3
|
-
@tailwind utilities;
|
|
1
|
+
@import "tailwindcss";
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
:
|
|
7
|
-
|
|
8
|
-
--foreground: 222.2 84% 4.9%;
|
|
9
|
-
--card: 0 0% 100%;
|
|
10
|
-
--card-foreground: 222.2 84% 4.9%;
|
|
11
|
-
--popover: 0 0% 100%;
|
|
12
|
-
--popover-foreground: 222.2 84% 4.9%;
|
|
13
|
-
--primary: 221.2 83.2% 53.3%;
|
|
14
|
-
--primary-foreground: 210 40% 98%;
|
|
15
|
-
--secondary: 210 40% 96.1%;
|
|
16
|
-
--secondary-foreground: 222.2 47.4% 11.2%;
|
|
17
|
-
--muted: 210 40% 96.1%;
|
|
18
|
-
--muted-foreground: 215.4 16.3% 46.9%;
|
|
19
|
-
--accent: 210 40% 96.1%;
|
|
20
|
-
--accent-foreground: 222.2 47.4% 11.2%;
|
|
21
|
-
--destructive: 0 84.2% 60.2%;
|
|
22
|
-
--destructive-foreground: 210 40% 98%;
|
|
23
|
-
--success: 142.1 76.2% 36.3%;
|
|
24
|
-
--success-foreground: 355.7 100% 97.3%;
|
|
25
|
-
--warning: 38 92% 50%;
|
|
26
|
-
--warning-foreground: 48 96% 89%;
|
|
27
|
-
--border: 214.3 31.8% 91.4%;
|
|
28
|
-
--input: 214.3 31.8% 91.4%;
|
|
29
|
-
--ring: 221.2 83.2% 53.3%;
|
|
30
|
-
--radius: 0.5rem;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
.dark {
|
|
34
|
-
--background: 222.2 84% 4.9%;
|
|
35
|
-
--foreground: 210 40% 98%;
|
|
36
|
-
--card: 222.2 84% 4.9%;
|
|
37
|
-
--card-foreground: 210 40% 98%;
|
|
38
|
-
--popover: 222.2 84% 4.9%;
|
|
39
|
-
--popover-foreground: 210 40% 98%;
|
|
40
|
-
--primary: 217.2 91.2% 59.8%;
|
|
41
|
-
--primary-foreground: 222.2 47.4% 11.2%;
|
|
42
|
-
--secondary: 217.2 32.6% 17.5%;
|
|
43
|
-
--secondary-foreground: 210 40% 98%;
|
|
44
|
-
--muted: 217.2 32.6% 17.5%;
|
|
45
|
-
--muted-foreground: 215 20.2% 65.1%;
|
|
46
|
-
--accent: 217.2 32.6% 17.5%;
|
|
47
|
-
--accent-foreground: 210 40% 98%;
|
|
48
|
-
--destructive: 0 62.8% 30.6%;
|
|
49
|
-
--destructive-foreground: 210 40% 98%;
|
|
50
|
-
--success: 142.1 70.6% 45.3%;
|
|
51
|
-
--success-foreground: 144.9 80.4% 10%;
|
|
52
|
-
--warning: 32 95% 44%;
|
|
53
|
-
--warning-foreground: 48 96% 89%;
|
|
54
|
-
--border: 217.2 32.6% 17.5%;
|
|
55
|
-
--input: 217.2 32.6% 17.5%;
|
|
56
|
-
--ring: 224.3 76.3% 48%;
|
|
57
|
-
}
|
|
3
|
+
:root {
|
|
4
|
+
--background: #ffffff;
|
|
5
|
+
--foreground: #171717;
|
|
58
6
|
}
|
|
59
7
|
|
|
60
|
-
@
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
body {
|
|
66
|
-
@apply bg-background text-foreground;
|
|
67
|
-
font-feature-settings: "rlig" 1, "calt" 1;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/* Smooth scrolling */
|
|
71
|
-
html {
|
|
72
|
-
scroll-behavior: smooth;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/* Better focus styles */
|
|
76
|
-
:focus-visible {
|
|
77
|
-
@apply outline-none ring-2 ring-ring ring-offset-2 ring-offset-background;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/* Reduced motion */
|
|
81
|
-
@media (prefers-reduced-motion: reduce) {
|
|
82
|
-
*,
|
|
83
|
-
::before,
|
|
84
|
-
::after {
|
|
85
|
-
animation-duration: 0.01ms !important;
|
|
86
|
-
animation-iteration-count: 1 !important;
|
|
87
|
-
transition-duration: 0.01ms !important;
|
|
88
|
-
scroll-behavior: auto !important;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
8
|
+
@theme inline {
|
|
9
|
+
--color-background: var(--background);
|
|
10
|
+
--color-foreground: var(--foreground);
|
|
11
|
+
--font-sans: var(--font-geist-sans);
|
|
12
|
+
--font-mono: var(--font-geist-mono);
|
|
91
13
|
}
|
|
92
14
|
|
|
93
|
-
@
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
scrollbar-width: none;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
.scrollbar-hide::-webkit-scrollbar {
|
|
101
|
-
display: none;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/* Text balance for headings */
|
|
105
|
-
.text-balance {
|
|
106
|
-
text-wrap: balance;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/* Animation delay utilities */
|
|
110
|
-
.animation-delay-150 {
|
|
111
|
-
animation-delay: 150ms;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
.animation-delay-300 {
|
|
115
|
-
animation-delay: 300ms;
|
|
15
|
+
@media (prefers-color-scheme: dark) {
|
|
16
|
+
:root {
|
|
17
|
+
--background: #0a0a0a;
|
|
18
|
+
--foreground: #ededed;
|
|
116
19
|
}
|
|
20
|
+
}
|
|
117
21
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
22
|
+
body {
|
|
23
|
+
background: var(--background);
|
|
24
|
+
color: var(--foreground);
|
|
25
|
+
font-family: Arial, Helvetica, sans-serif;
|
|
121
26
|
}
|
|
@@ -1,45 +1,33 @@
|
|
|
1
|
-
import type { Metadata } from
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import { ThemeProvider } from '@/components/theme-provider';
|
|
5
|
-
import { AuthProvider } from '@/lib/auth-context';
|
|
6
|
-
import { ErrorBoundary } from '@/components/ErrorBoundary';
|
|
7
|
-
import { AnalyticsProvider } from '@/components/AnalyticsProvider';
|
|
1
|
+
import type { Metadata } from "next";
|
|
2
|
+
import { Geist, Geist_Mono } from "next/font/google";
|
|
3
|
+
import "./globals.css";
|
|
8
4
|
|
|
9
|
-
const
|
|
5
|
+
const geistSans = Geist({
|
|
6
|
+
variable: "--font-geist-sans",
|
|
7
|
+
subsets: ["latin"],
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const geistMono = Geist_Mono({
|
|
11
|
+
variable: "--font-geist-mono",
|
|
12
|
+
subsets: ["latin"],
|
|
13
|
+
});
|
|
10
14
|
|
|
11
15
|
export const metadata: Metadata = {
|
|
12
|
-
title:
|
|
13
|
-
description:
|
|
14
|
-
openGraph: {
|
|
15
|
-
title: 'My App',
|
|
16
|
-
description: 'Built with Lux',
|
|
17
|
-
type: 'website',
|
|
18
|
-
},
|
|
16
|
+
title: "Create Next App",
|
|
17
|
+
description: "Generated by create next app",
|
|
19
18
|
};
|
|
20
19
|
|
|
21
20
|
export default function RootLayout({
|
|
22
21
|
children,
|
|
23
|
-
}: {
|
|
22
|
+
}: Readonly<{
|
|
24
23
|
children: React.ReactNode;
|
|
25
|
-
}) {
|
|
24
|
+
}>) {
|
|
26
25
|
return (
|
|
27
|
-
<html lang="en"
|
|
28
|
-
<body
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
enableSystem
|
|
33
|
-
disableTransitionOnChange
|
|
34
|
-
>
|
|
35
|
-
<ErrorBoundary>
|
|
36
|
-
<AnalyticsProvider>
|
|
37
|
-
<AuthProvider>
|
|
38
|
-
{children}
|
|
39
|
-
</AuthProvider>
|
|
40
|
-
</AnalyticsProvider>
|
|
41
|
-
</ErrorBoundary>
|
|
42
|
-
</ThemeProvider>
|
|
26
|
+
<html lang="en">
|
|
27
|
+
<body
|
|
28
|
+
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
|
29
|
+
>
|
|
30
|
+
{children}
|
|
43
31
|
</body>
|
|
44
32
|
</html>
|
|
45
33
|
);
|