wexts 4.1.0 → 4.1.5
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/README.md +49 -49
- package/bin/wexts.cjs +2 -2
- package/package.json +153 -148
- package/templates/.dockerignore +43 -43
- package/templates/.env.example +17 -17
- package/templates/Dockerfile +60 -60
- package/templates/Procfile +1 -1
- package/templates/README.md +67 -67
- package/templates/api-sdk.ts +115 -115
- package/templates/docker-compose.yml +34 -34
- package/templates/nestjs-api/.env.example +3 -3
- package/templates/nestjs-api/README.md +87 -87
- package/templates/nestjs-api/nest-cli.json +6 -6
- package/templates/nestjs-api/package.json +40 -40
- package/templates/nestjs-api/prisma/migrations/20251123205437_init/migration.sql +24 -24
- package/templates/nestjs-api/prisma/migrations/migration_lock.toml +3 -3
- package/templates/nestjs-api/prisma/schema.prisma +29 -29
- package/templates/nestjs-api/src/app.module.ts +17 -17
- package/templates/nestjs-api/src/auth/auth.controller.ts +27 -27
- package/templates/nestjs-api/src/auth/auth.module.ts +37 -37
- package/templates/nestjs-api/src/auth/auth.service.ts +86 -86
- package/templates/nestjs-api/src/auth/dto/auth.dto.ts +22 -22
- package/templates/nestjs-api/src/auth/guards/jwt-auth.guard.ts +5 -5
- package/templates/nestjs-api/src/auth/strategies/jwt.strategy.ts +27 -27
- package/templates/nestjs-api/src/main.ts +32 -32
- package/templates/nestjs-api/src/prisma/prisma.module.ts +9 -9
- package/templates/nestjs-api/src/prisma/prisma.service.ts +14 -14
- package/templates/nestjs-api/src/todos/dto/todo.dto.ts +24 -24
- package/templates/nestjs-api/src/todos/todos.controller.ts +39 -39
- package/templates/nestjs-api/src/todos/todos.module.ts +11 -11
- package/templates/nestjs-api/src/todos/todos.service.ts +53 -53
- package/templates/nestjs-api/src/users/users.controller.ts +14 -14
- package/templates/nestjs-api/src/users/users.module.ts +12 -12
- package/templates/nestjs-api/src/users/users.service.ts +19 -19
- package/templates/nestjs-api/tsconfig.json +39 -39
- package/templates/nextjs-web/README.md +76 -76
- package/templates/nextjs-web/app/actions/auth.ts +108 -108
- package/templates/nextjs-web/app/dashboard/error.tsx +39 -39
- package/templates/nextjs-web/app/dashboard/loading.tsx +14 -14
- package/templates/nextjs-web/app/dashboard/page.tsx +5 -5
- package/templates/nextjs-web/app/globals.css +93 -93
- package/templates/nextjs-web/app/layout.tsx +29 -29
- package/templates/nextjs-web/app/login/page.tsx +5 -5
- package/templates/nextjs-web/app/page.tsx +28 -28
- package/templates/nextjs-web/app/register/page.tsx +5 -5
- package/templates/nextjs-web/components/ui/button.tsx +56 -56
- package/templates/nextjs-web/components/ui/card.tsx +79 -79
- package/templates/nextjs-web/components/ui/input.tsx +25 -25
- package/templates/nextjs-web/components/ui/label.tsx +24 -24
- package/templates/nextjs-web/features/auth/LoginForm.tsx +140 -140
- package/templates/nextjs-web/features/auth/RegisterForm.tsx +159 -159
- package/templates/nextjs-web/features/auth/api.ts +35 -35
- package/templates/nextjs-web/features/auth/index.ts +3 -3
- package/templates/nextjs-web/features/dashboard/DashboardView.tsx +204 -204
- package/templates/nextjs-web/features/dashboard/api.ts +9 -9
- package/templates/nextjs-web/features/dashboard/components.tsx +74 -74
- package/templates/nextjs-web/features/dashboard/index.ts +3 -3
- package/templates/nextjs-web/hooks/index.ts +4 -4
- package/templates/nextjs-web/lib/api-client.ts +89 -89
- package/templates/nextjs-web/lib/api.ts +115 -115
- package/templates/nextjs-web/lib/axios-global-config.ts +17 -17
- package/templates/nextjs-web/lib/utils.ts +6 -6
- package/templates/nextjs-web/lib/wexts-client.ts +4 -4
- package/templates/nextjs-web/next-env.d.ts +6 -6
- package/templates/nextjs-web/next.config.ts +20 -20
- package/templates/nextjs-web/package.json +37 -37
- package/templates/nextjs-web/postcss.config.js +6 -6
- package/templates/nextjs-web/tailwind.config.ts +69 -69
- package/templates/nextjs-web/tsconfig.json +41 -41
- package/templates/nixpacks.toml +11 -11
- package/templates/root-package.json +31 -31
- package/templates/server.ts +66 -66
- package/templates/tsconfig.json +30 -30
- package/dist/chunk-2KAQYLVN.js +0 -1
- package/dist/chunk-2KAQYLVN.js.map +0 -1
- package/dist/chunk-2LJVUMXW.js +0 -228
- package/dist/chunk-2LJVUMXW.js.map +0 -1
- package/dist/chunk-342VRT25.mjs +0 -504
- package/dist/chunk-342VRT25.mjs.map +0 -1
- package/dist/chunk-7HNQWJWV.js +0 -504
- package/dist/chunk-7HNQWJWV.js.map +0 -1
- package/dist/chunk-7QKLIVRF.js +0 -94
- package/dist/chunk-7QKLIVRF.js.map +0 -1
- package/dist/chunk-7SSCNCTW.mjs +0 -137
- package/dist/chunk-7SSCNCTW.mjs.map +0 -1
- package/dist/chunk-7TLSPR65.mjs +0 -95
- package/dist/chunk-7TLSPR65.mjs.map +0 -1
- package/dist/chunk-7WULUGLH.mjs +0 -22
- package/dist/chunk-7WULUGLH.mjs.map +0 -1
- package/dist/chunk-AVMQJWYD.js +0 -95
- package/dist/chunk-AVMQJWYD.js.map +0 -1
- package/dist/chunk-BG56B4DE.js +0 -106
- package/dist/chunk-BG56B4DE.js.map +0 -1
- package/dist/chunk-CLM5PNSG.mjs +0 -496
- package/dist/chunk-CLM5PNSG.mjs.map +0 -1
- package/dist/chunk-DNLGCKTT.js +0 -31
- package/dist/chunk-DNLGCKTT.js.map +0 -1
- package/dist/chunk-JHOVXH3X.mjs +0 -65
- package/dist/chunk-JHOVXH3X.mjs.map +0 -1
- package/dist/chunk-MXINIFPC.js +0 -105
- package/dist/chunk-MXINIFPC.js.map +0 -1
- package/dist/chunk-O4II6N34.js +0 -137
- package/dist/chunk-O4II6N34.js.map +0 -1
- package/dist/chunk-SE32ZPOZ.js +0 -496
- package/dist/chunk-SE32ZPOZ.js.map +0 -1
- package/dist/chunk-UAL54DVV.mjs +0 -106
- package/dist/chunk-UAL54DVV.mjs.map +0 -1
- package/dist/chunk-WCKSKU3C.js +0 -65
- package/dist/chunk-WCKSKU3C.js.map +0 -1
- package/dist/chunk-WU6FW77M.mjs +0 -105
- package/dist/chunk-WU6FW77M.mjs.map +0 -1
- package/dist/chunk-XE4OXN2W.js +0 -12
- package/dist/chunk-XE4OXN2W.js.map +0 -1
- package/dist/chunk-YBM3IJEA.mjs +0 -94
- package/dist/chunk-YBM3IJEA.mjs.map +0 -1
- package/dist/chunk-YN6WIWNQ.mjs +0 -228
- package/dist/chunk-YN6WIWNQ.mjs.map +0 -1
- package/dist/chunk-YSLEF5C5.mjs +0 -1
- package/dist/chunk-YSLEF5C5.mjs.map +0 -1
- package/dist/chunk-ZX7QIN24.mjs +0 -31
- package/dist/chunk-ZX7QIN24.mjs.map +0 -1
- package/dist/cli/index.d.mts +0 -23
- package/dist/cli/index.d.ts +0 -23
- package/dist/cli/index.js +0 -716
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/index.mjs +0 -718
- package/dist/cli/index.mjs.map +0 -1
- package/dist/client/index.d.mts +0 -21
- package/dist/client/index.d.ts +0 -21
- package/dist/client/index.js +0 -13
- package/dist/client/index.js.map +0 -1
- package/dist/client/index.mjs +0 -13
- package/dist/client/index.mjs.map +0 -1
- package/dist/codegen/index.d.mts +0 -2
- package/dist/codegen/index.d.ts +0 -2
- package/dist/codegen/index.js +0 -16
- package/dist/codegen/index.js.map +0 -1
- package/dist/codegen/index.mjs +0 -16
- package/dist/codegen/index.mjs.map +0 -1
- package/dist/decorators-BT1FFqN0.d.mts +0 -29
- package/dist/decorators-DvS58PqC.d.ts +0 -29
- package/dist/dev-server/index.d.mts +0 -1
- package/dist/dev-server/index.d.ts +0 -1
- package/dist/dev-server/index.js +0 -13
- package/dist/dev-server/index.js.map +0 -1
- package/dist/dev-server/index.mjs +0 -13
- package/dist/dev-server/index.mjs.map +0 -1
- package/dist/index-7QeQEf37.d.ts +0 -92
- package/dist/index-7RvU-jGE.d.mts +0 -66
- package/dist/index-7RvU-jGE.d.ts +0 -66
- package/dist/index-8nzxy0NN.d.mts +0 -92
- package/dist/index-Co5ZsLqq.d.ts +0 -58
- package/dist/index-D94W1__r.d.mts +0 -58
- package/dist/index-DQmyVp6F.d.mts +0 -27
- package/dist/index-KL_1BrQb.d.ts +0 -27
- package/dist/index.d.mts +0 -294
- package/dist/index.d.ts +0 -294
- package/dist/index.js +0 -424
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -424
- package/dist/index.mjs.map +0 -1
- package/dist/nest/index.d.mts +0 -3
- package/dist/nest/index.d.ts +0 -3
- package/dist/nest/index.js +0 -38
- package/dist/nest/index.js.map +0 -1
- package/dist/nest/index.mjs +0 -38
- package/dist/nest/index.mjs.map +0 -1
- package/dist/next/index.d.mts +0 -66
- package/dist/next/index.d.ts +0 -66
- package/dist/next/index.js +0 -289
- package/dist/next/index.js.map +0 -1
- package/dist/next/index.mjs +0 -251
- package/dist/next/index.mjs.map +0 -1
- package/dist/rpc/index.d.mts +0 -2
- package/dist/rpc/index.d.ts +0 -2
- package/dist/rpc/index.js +0 -23
- package/dist/rpc/index.js.map +0 -1
- package/dist/rpc/index.mjs +0 -23
- package/dist/rpc/index.mjs.map +0 -1
- package/dist/runtime/index.d.mts +0 -55
- package/dist/runtime/index.d.ts +0 -55
- package/dist/runtime/index.js +0 -221
- package/dist/runtime/index.js.map +0 -1
- package/dist/runtime/index.mjs +0 -221
- package/dist/runtime/index.mjs.map +0 -1
- package/dist/types/index.d.mts +0 -12
- package/dist/types/index.d.ts +0 -12
- package/dist/types/index.js +0 -2
- package/dist/types/index.js.map +0 -1
- package/dist/types/index.mjs +0 -3
- package/dist/types/index.mjs.map +0 -1
- package/dist/types-7d_fC-C3.d.mts +0 -32
- package/dist/types-7d_fC-C3.d.ts +0 -32
- package/dist/vercel-builder/index.d.mts +0 -58
- package/dist/vercel-builder/index.d.ts +0 -58
- package/dist/vercel-builder/index.js +0 -330
- package/dist/vercel-builder/index.js.map +0 -1
- package/dist/vercel-builder/index.mjs +0 -330
- package/dist/vercel-builder/index.mjs.map +0 -1
|
@@ -1,76 +1,76 @@
|
|
|
1
|
-
# Deprecated Legacy Web Template
|
|
2
|
-
|
|
3
|
-
This template predates the verified `examples/hello-rpc` runtime/RPC/security model. It is retained for compatibility and should not be used as the production reference.
|
|
4
|
-
|
|
5
|
-
Use `examples/hello-rpc` for the canonical generated-RPC path.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Fusion Next.js Web
|
|
10
|
-
|
|
11
|
-
Modern Next.js 16 frontend with wexts integration.
|
|
12
|
-
|
|
13
|
-
## Features
|
|
14
|
-
|
|
15
|
-
- ✅ Next.js 16 (App Router)
|
|
16
|
-
- ✅ React 19
|
|
17
|
-
- ✅ Tailwind CSS v4
|
|
18
|
-
- ✅ TypeScript
|
|
19
|
-
- ✅ wexts Provider & Hooks
|
|
20
|
-
- ✅ Authentication Flow
|
|
21
|
-
- ✅ Todo Management Dashboard
|
|
22
|
-
|
|
23
|
-
## Setup
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
# Install dependencies
|
|
27
|
-
npm install
|
|
28
|
-
|
|
29
|
-
# Copy environment variables
|
|
30
|
-
cp .env.local.example .env.local
|
|
31
|
-
|
|
32
|
-
# Update NEXT_PUBLIC_API_URL in .env.local to point to your API
|
|
33
|
-
|
|
34
|
-
# Start development server
|
|
35
|
-
npm run dev
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Project Structure
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
app/
|
|
42
|
-
├── layout.tsx # Root layout with FusionProvider
|
|
43
|
-
├── page.tsx # Homepage with auth redirect
|
|
44
|
-
├── globals.css # Global styles
|
|
45
|
-
├── login/
|
|
46
|
-
│ └── page.tsx # Login page
|
|
47
|
-
├── register/
|
|
48
|
-
│ └── page.tsx # Registration page
|
|
49
|
-
└── dashboard/
|
|
50
|
-
└── page.tsx # Protected dashboard with todos
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## Using Fusion Hooks
|
|
54
|
-
|
|
55
|
-
```tsx
|
|
56
|
-
import { useFusion, useAuth } from 'wexts/next';
|
|
57
|
-
|
|
58
|
-
function MyComponent() {
|
|
59
|
-
const { client } = useFusion();
|
|
60
|
-
const { user, isAuthenticated } = useAuth();
|
|
61
|
-
|
|
62
|
-
// Make API calls
|
|
63
|
-
const data = await client.get('/endpoint');
|
|
64
|
-
}
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## Building for Production
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
npm run build
|
|
71
|
-
npm start
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
## Environment Variables
|
|
75
|
-
|
|
76
|
-
- `NEXT_PUBLIC_API_URL` - Backend API URL (default: http://localhost:5050)
|
|
1
|
+
# Deprecated Legacy Web Template
|
|
2
|
+
|
|
3
|
+
This template predates the verified `examples/hello-rpc` runtime/RPC/security model. It is retained for compatibility and should not be used as the production reference.
|
|
4
|
+
|
|
5
|
+
Use `examples/hello-rpc` for the canonical generated-RPC path.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Fusion Next.js Web
|
|
10
|
+
|
|
11
|
+
Modern Next.js 16 frontend with wexts integration.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- ✅ Next.js 16 (App Router)
|
|
16
|
+
- ✅ React 19
|
|
17
|
+
- ✅ Tailwind CSS v4
|
|
18
|
+
- ✅ TypeScript
|
|
19
|
+
- ✅ wexts Provider & Hooks
|
|
20
|
+
- ✅ Authentication Flow
|
|
21
|
+
- ✅ Todo Management Dashboard
|
|
22
|
+
|
|
23
|
+
## Setup
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Install dependencies
|
|
27
|
+
npm install
|
|
28
|
+
|
|
29
|
+
# Copy environment variables
|
|
30
|
+
cp .env.local.example .env.local
|
|
31
|
+
|
|
32
|
+
# Update NEXT_PUBLIC_API_URL in .env.local to point to your API
|
|
33
|
+
|
|
34
|
+
# Start development server
|
|
35
|
+
npm run dev
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Project Structure
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
app/
|
|
42
|
+
├── layout.tsx # Root layout with FusionProvider
|
|
43
|
+
├── page.tsx # Homepage with auth redirect
|
|
44
|
+
├── globals.css # Global styles
|
|
45
|
+
├── login/
|
|
46
|
+
│ └── page.tsx # Login page
|
|
47
|
+
├── register/
|
|
48
|
+
│ └── page.tsx # Registration page
|
|
49
|
+
└── dashboard/
|
|
50
|
+
└── page.tsx # Protected dashboard with todos
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Using Fusion Hooks
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import { useFusion, useAuth } from 'wexts/next';
|
|
57
|
+
|
|
58
|
+
function MyComponent() {
|
|
59
|
+
const { client } = useFusion();
|
|
60
|
+
const { user, isAuthenticated } = useAuth();
|
|
61
|
+
|
|
62
|
+
// Make API calls
|
|
63
|
+
const data = await client.get('/endpoint');
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Building for Production
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npm run build
|
|
71
|
+
npm start
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Environment Variables
|
|
75
|
+
|
|
76
|
+
- `NEXT_PUBLIC_API_URL` - Backend API URL (default: http://localhost:5050)
|
|
@@ -1,108 +1,108 @@
|
|
|
1
|
-
'use server';
|
|
2
|
-
|
|
3
|
-
import { cookies } from 'next/headers';
|
|
4
|
-
import { redirect } from 'next/navigation';
|
|
5
|
-
import { api } from '@/lib/api';
|
|
6
|
-
|
|
7
|
-
export type ActionState = {
|
|
8
|
-
message?: string;
|
|
9
|
-
errors?: {
|
|
10
|
-
email?: string[];
|
|
11
|
-
password?: string[];
|
|
12
|
-
};
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export async function loginAction(prevState: ActionState, formData: FormData): Promise<ActionState> {
|
|
16
|
-
const email = formData.get('email') as string;
|
|
17
|
-
const password = formData.get('password') as string;
|
|
18
|
-
|
|
19
|
-
const errors: { email?: string[]; password?: string[] } = {};
|
|
20
|
-
|
|
21
|
-
if (!email || !email.includes('@')) {
|
|
22
|
-
errors.email = ['Invalid email address'];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (!password || password.length < 6) {
|
|
26
|
-
errors.password = ['Password must be at least 6 characters'];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (Object.keys(errors).length > 0) {
|
|
30
|
-
return { errors };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
// Legacy SDK call retained for deprecated template compatibility.
|
|
35
|
-
const data = await api.auth.login({ email, password });
|
|
36
|
-
|
|
37
|
-
// Store JWT token
|
|
38
|
-
if (data.access_token) {
|
|
39
|
-
const cookieStore = await cookies();
|
|
40
|
-
cookieStore.set('wexts_token', data.access_token, {
|
|
41
|
-
httpOnly: true,
|
|
42
|
-
secure: process.env.NODE_ENV === 'production',
|
|
43
|
-
maxAge: 60 * 60 * 24 * 7, // 1 week
|
|
44
|
-
path: '/',
|
|
45
|
-
});
|
|
46
|
-
} else {
|
|
47
|
-
return {
|
|
48
|
-
message: 'Login failed: No token received',
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
} catch (error: any) {
|
|
52
|
-
console.error('Login error:', error);
|
|
53
|
-
return {
|
|
54
|
-
message: error.message || 'Invalid credentials',
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
redirect('/dashboard');
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export async function registerAction(prevState: ActionState, formData: FormData): Promise<ActionState> {
|
|
62
|
-
const email = formData.get('email') as string;
|
|
63
|
-
const password = formData.get('password') as string;
|
|
64
|
-
const name = formData.get('name') as string;
|
|
65
|
-
|
|
66
|
-
const errors: { email?: string[]; password?: string[] } = {};
|
|
67
|
-
|
|
68
|
-
if (!email || !email.includes('@')) {
|
|
69
|
-
errors.email = ['Invalid email address'];
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (!password || password.length < 6) {
|
|
73
|
-
errors.password = ['Password must be at least 6 characters'];
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (Object.keys(errors).length > 0) {
|
|
77
|
-
return { errors };
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
try {
|
|
81
|
-
// Legacy SDK call retained for deprecated template compatibility.
|
|
82
|
-
const data = await api.auth.register({ email, password, name });
|
|
83
|
-
|
|
84
|
-
// Store JWT token
|
|
85
|
-
if (data.access_token) {
|
|
86
|
-
const cookieStore = await cookies();
|
|
87
|
-
cookieStore.set('wexts_token', data.access_token, {
|
|
88
|
-
httpOnly: true,
|
|
89
|
-
secure: process.env.NODE_ENV === 'production',
|
|
90
|
-
maxAge: 60 * 60 * 24 * 7,
|
|
91
|
-
path: '/',
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
} catch (error: any) {
|
|
95
|
-
console.error('Register error:', error);
|
|
96
|
-
return {
|
|
97
|
-
message: error.message || 'Registration failed',
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
redirect('/dashboard');
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export async function logoutAction() {
|
|
105
|
-
const cookieStore = await cookies();
|
|
106
|
-
cookieStore.delete('wexts_token');
|
|
107
|
-
redirect('/login');
|
|
108
|
-
}
|
|
1
|
+
'use server';
|
|
2
|
+
|
|
3
|
+
import { cookies } from 'next/headers';
|
|
4
|
+
import { redirect } from 'next/navigation';
|
|
5
|
+
import { api } from '@/lib/api';
|
|
6
|
+
|
|
7
|
+
export type ActionState = {
|
|
8
|
+
message?: string;
|
|
9
|
+
errors?: {
|
|
10
|
+
email?: string[];
|
|
11
|
+
password?: string[];
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export async function loginAction(prevState: ActionState, formData: FormData): Promise<ActionState> {
|
|
16
|
+
const email = formData.get('email') as string;
|
|
17
|
+
const password = formData.get('password') as string;
|
|
18
|
+
|
|
19
|
+
const errors: { email?: string[]; password?: string[] } = {};
|
|
20
|
+
|
|
21
|
+
if (!email || !email.includes('@')) {
|
|
22
|
+
errors.email = ['Invalid email address'];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!password || password.length < 6) {
|
|
26
|
+
errors.password = ['Password must be at least 6 characters'];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (Object.keys(errors).length > 0) {
|
|
30
|
+
return { errors };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
// Legacy SDK call retained for deprecated template compatibility.
|
|
35
|
+
const data = await api.auth.login({ email, password });
|
|
36
|
+
|
|
37
|
+
// Store JWT token
|
|
38
|
+
if (data.access_token) {
|
|
39
|
+
const cookieStore = await cookies();
|
|
40
|
+
cookieStore.set('wexts_token', data.access_token, {
|
|
41
|
+
httpOnly: true,
|
|
42
|
+
secure: process.env.NODE_ENV === 'production',
|
|
43
|
+
maxAge: 60 * 60 * 24 * 7, // 1 week
|
|
44
|
+
path: '/',
|
|
45
|
+
});
|
|
46
|
+
} else {
|
|
47
|
+
return {
|
|
48
|
+
message: 'Login failed: No token received',
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
} catch (error: any) {
|
|
52
|
+
console.error('Login error:', error);
|
|
53
|
+
return {
|
|
54
|
+
message: error.message || 'Invalid credentials',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
redirect('/dashboard');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export async function registerAction(prevState: ActionState, formData: FormData): Promise<ActionState> {
|
|
62
|
+
const email = formData.get('email') as string;
|
|
63
|
+
const password = formData.get('password') as string;
|
|
64
|
+
const name = formData.get('name') as string;
|
|
65
|
+
|
|
66
|
+
const errors: { email?: string[]; password?: string[] } = {};
|
|
67
|
+
|
|
68
|
+
if (!email || !email.includes('@')) {
|
|
69
|
+
errors.email = ['Invalid email address'];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!password || password.length < 6) {
|
|
73
|
+
errors.password = ['Password must be at least 6 characters'];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (Object.keys(errors).length > 0) {
|
|
77
|
+
return { errors };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
// Legacy SDK call retained for deprecated template compatibility.
|
|
82
|
+
const data = await api.auth.register({ email, password, name });
|
|
83
|
+
|
|
84
|
+
// Store JWT token
|
|
85
|
+
if (data.access_token) {
|
|
86
|
+
const cookieStore = await cookies();
|
|
87
|
+
cookieStore.set('wexts_token', data.access_token, {
|
|
88
|
+
httpOnly: true,
|
|
89
|
+
secure: process.env.NODE_ENV === 'production',
|
|
90
|
+
maxAge: 60 * 60 * 24 * 7,
|
|
91
|
+
path: '/',
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
} catch (error: any) {
|
|
95
|
+
console.error('Register error:', error);
|
|
96
|
+
return {
|
|
97
|
+
message: error.message || 'Registration failed',
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
redirect('/dashboard');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function logoutAction() {
|
|
105
|
+
const cookieStore = await cookies();
|
|
106
|
+
cookieStore.delete('wexts_token');
|
|
107
|
+
redirect('/login');
|
|
108
|
+
}
|
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useEffect } from 'react';
|
|
4
|
-
import { Button } from '@/components/ui/button';
|
|
5
|
-
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
6
|
-
import { AlertTriangle } from 'lucide-react';
|
|
7
|
-
|
|
8
|
-
export default function DashboardError({
|
|
9
|
-
error,
|
|
10
|
-
reset,
|
|
11
|
-
}: {
|
|
12
|
-
error: Error & { digest?: string };
|
|
13
|
-
reset: () => void;
|
|
14
|
-
}) {
|
|
15
|
-
useEffect(() => {
|
|
16
|
-
console.error(error);
|
|
17
|
-
}, [error]);
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<div className="min-h-screen bg-background flex items-center justify-center p-8">
|
|
21
|
-
<Card className="glass border-destructive/20 max-w-md w-full">
|
|
22
|
-
<CardHeader>
|
|
23
|
-
<CardTitle className="text-destructive flex items-center gap-2">
|
|
24
|
-
<AlertTriangle className="w-5 h-5" />
|
|
25
|
-
Something went wrong
|
|
26
|
-
</CardTitle>
|
|
27
|
-
</CardHeader>
|
|
28
|
-
<CardContent className="space-y-4">
|
|
29
|
-
<p className="text-muted-foreground">
|
|
30
|
-
We encountered an error while loading your dashboard. Please try again.
|
|
31
|
-
</p>
|
|
32
|
-
<Button onClick={reset} className="w-full">
|
|
33
|
-
Try again
|
|
34
|
-
</Button>
|
|
35
|
-
</CardContent>
|
|
36
|
-
</Card>
|
|
37
|
-
</div>
|
|
38
|
-
);
|
|
39
|
-
}
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect } from 'react';
|
|
4
|
+
import { Button } from '@/components/ui/button';
|
|
5
|
+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
6
|
+
import { AlertTriangle } from 'lucide-react';
|
|
7
|
+
|
|
8
|
+
export default function DashboardError({
|
|
9
|
+
error,
|
|
10
|
+
reset,
|
|
11
|
+
}: {
|
|
12
|
+
error: Error & { digest?: string };
|
|
13
|
+
reset: () => void;
|
|
14
|
+
}) {
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
console.error(error);
|
|
17
|
+
}, [error]);
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div className="min-h-screen bg-background flex items-center justify-center p-8">
|
|
21
|
+
<Card className="glass border-destructive/20 max-w-md w-full">
|
|
22
|
+
<CardHeader>
|
|
23
|
+
<CardTitle className="text-destructive flex items-center gap-2">
|
|
24
|
+
<AlertTriangle className="w-5 h-5" />
|
|
25
|
+
Something went wrong
|
|
26
|
+
</CardTitle>
|
|
27
|
+
</CardHeader>
|
|
28
|
+
<CardContent className="space-y-4">
|
|
29
|
+
<p className="text-muted-foreground">
|
|
30
|
+
We encountered an error while loading your dashboard. Please try again.
|
|
31
|
+
</p>
|
|
32
|
+
<Button onClick={reset} className="w-full">
|
|
33
|
+
Try again
|
|
34
|
+
</Button>
|
|
35
|
+
</CardContent>
|
|
36
|
+
</Card>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { Loader2 } from 'lucide-react';
|
|
2
|
-
|
|
3
|
-
export default function DashboardLoading() {
|
|
4
|
-
return (
|
|
5
|
-
<div className="min-h-screen bg-background flex items-center justify-center">
|
|
6
|
-
<div className="text-center space-y-4">
|
|
7
|
-
<div className="inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-primary/10 text-primary animate-pulse">
|
|
8
|
-
<Loader2 className="w-8 h-8 animate-spin" />
|
|
9
|
-
</div>
|
|
10
|
-
<p className="text-muted-foreground animate-pulse">Loading dashboard...</p>
|
|
11
|
-
</div>
|
|
12
|
-
</div>
|
|
13
|
-
);
|
|
14
|
-
}
|
|
1
|
+
import { Loader2 } from 'lucide-react';
|
|
2
|
+
|
|
3
|
+
export default function DashboardLoading() {
|
|
4
|
+
return (
|
|
5
|
+
<div className="min-h-screen bg-background flex items-center justify-center">
|
|
6
|
+
<div className="text-center space-y-4">
|
|
7
|
+
<div className="inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-primary/10 text-primary animate-pulse">
|
|
8
|
+
<Loader2 className="w-8 h-8 animate-spin" />
|
|
9
|
+
</div>
|
|
10
|
+
<p className="text-muted-foreground animate-pulse">Loading dashboard...</p>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DashboardView } from '@/features/dashboard';
|
|
2
|
-
|
|
3
|
-
export default function DashboardPage() {
|
|
4
|
-
return <DashboardView />;
|
|
5
|
-
}
|
|
1
|
+
import { DashboardView } from '@/features/dashboard';
|
|
2
|
+
|
|
3
|
+
export default function DashboardPage() {
|
|
4
|
+
return <DashboardView />;
|
|
5
|
+
}
|
|
@@ -1,93 +1,93 @@
|
|
|
1
|
-
@tailwind base;
|
|
2
|
-
@tailwind components;
|
|
3
|
-
@tailwind utilities;
|
|
4
|
-
|
|
5
|
-
@layer base {
|
|
6
|
-
:root {
|
|
7
|
-
/* Brand Colors - Deep Violet & Electric Purple */
|
|
8
|
-
--background: 260 30% 98%;
|
|
9
|
-
--foreground: 260 40% 10%;
|
|
10
|
-
|
|
11
|
-
--primary: 265 89% 66%;
|
|
12
|
-
--primary-foreground: 210 40% 98%;
|
|
13
|
-
|
|
14
|
-
--secondary: 260 20% 90%;
|
|
15
|
-
--secondary-foreground: 260 40% 10%;
|
|
16
|
-
|
|
17
|
-
--muted: 260 20% 96%;
|
|
18
|
-
--muted-foreground: 260 20% 40%;
|
|
19
|
-
|
|
20
|
-
--accent: 265 89% 96%;
|
|
21
|
-
--accent-foreground: 265 89% 66%;
|
|
22
|
-
|
|
23
|
-
--destructive: 0 84% 60%;
|
|
24
|
-
--destructive-foreground: 210 40% 98%;
|
|
25
|
-
|
|
26
|
-
--border: 260 20% 90%;
|
|
27
|
-
--input: 260 20% 90%;
|
|
28
|
-
--ring: 265 89% 66%;
|
|
29
|
-
|
|
30
|
-
--radius: 1rem;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
.dark {
|
|
34
|
-
--background: 260 40% 5%;
|
|
35
|
-
--foreground: 210 40% 98%;
|
|
36
|
-
|
|
37
|
-
--primary: 265 89% 66%;
|
|
38
|
-
--primary-foreground: 210 40% 98%;
|
|
39
|
-
|
|
40
|
-
--secondary: 260 30% 15%;
|
|
41
|
-
--secondary-foreground: 210 40% 98%;
|
|
42
|
-
|
|
43
|
-
--muted: 260 30% 15%;
|
|
44
|
-
--muted-foreground: 215 20% 65%;
|
|
45
|
-
|
|
46
|
-
--accent: 260 30% 15%;
|
|
47
|
-
--accent-foreground: 210 40% 98%;
|
|
48
|
-
|
|
49
|
-
--destructive: 0 62% 30%;
|
|
50
|
-
--destructive-foreground: 210 40% 98%;
|
|
51
|
-
|
|
52
|
-
--border: 260 30% 15%;
|
|
53
|
-
--input: 260 30% 15%;
|
|
54
|
-
--ring: 265 89% 66%;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
@layer base {
|
|
59
|
-
* {
|
|
60
|
-
@apply border-border;
|
|
61
|
-
}
|
|
62
|
-
body {
|
|
63
|
-
@apply bg-background text-foreground antialiased;
|
|
64
|
-
font-feature-settings: "rlig" 1, "calt" 1;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
@layer utilities {
|
|
69
|
-
.glass {
|
|
70
|
-
@apply bg-white/10 backdrop-blur-lg border border-white/20 shadow-xl;
|
|
71
|
-
}
|
|
72
|
-
.glass-dark {
|
|
73
|
-
@apply bg-black/30 backdrop-blur-lg border border-white/10 shadow-xl;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.text-gradient {
|
|
77
|
-
@apply bg-clip-text text-transparent bg-gradient-to-r from-violet-600 to-indigo-600 dark:from-violet-400 dark:to-indigo-400;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
.animate-float {
|
|
81
|
-
animation: float 6s ease-in-out infinite;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
.animate-pulse-slow {
|
|
85
|
-
animation: pulse 4s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
@keyframes float {
|
|
90
|
-
0% { transform: translateY(0px); }
|
|
91
|
-
50% { transform: translateY(-20px); }
|
|
92
|
-
100% { transform: translateY(0px); }
|
|
93
|
-
}
|
|
1
|
+
@tailwind base;
|
|
2
|
+
@tailwind components;
|
|
3
|
+
@tailwind utilities;
|
|
4
|
+
|
|
5
|
+
@layer base {
|
|
6
|
+
:root {
|
|
7
|
+
/* Brand Colors - Deep Violet & Electric Purple */
|
|
8
|
+
--background: 260 30% 98%;
|
|
9
|
+
--foreground: 260 40% 10%;
|
|
10
|
+
|
|
11
|
+
--primary: 265 89% 66%;
|
|
12
|
+
--primary-foreground: 210 40% 98%;
|
|
13
|
+
|
|
14
|
+
--secondary: 260 20% 90%;
|
|
15
|
+
--secondary-foreground: 260 40% 10%;
|
|
16
|
+
|
|
17
|
+
--muted: 260 20% 96%;
|
|
18
|
+
--muted-foreground: 260 20% 40%;
|
|
19
|
+
|
|
20
|
+
--accent: 265 89% 96%;
|
|
21
|
+
--accent-foreground: 265 89% 66%;
|
|
22
|
+
|
|
23
|
+
--destructive: 0 84% 60%;
|
|
24
|
+
--destructive-foreground: 210 40% 98%;
|
|
25
|
+
|
|
26
|
+
--border: 260 20% 90%;
|
|
27
|
+
--input: 260 20% 90%;
|
|
28
|
+
--ring: 265 89% 66%;
|
|
29
|
+
|
|
30
|
+
--radius: 1rem;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.dark {
|
|
34
|
+
--background: 260 40% 5%;
|
|
35
|
+
--foreground: 210 40% 98%;
|
|
36
|
+
|
|
37
|
+
--primary: 265 89% 66%;
|
|
38
|
+
--primary-foreground: 210 40% 98%;
|
|
39
|
+
|
|
40
|
+
--secondary: 260 30% 15%;
|
|
41
|
+
--secondary-foreground: 210 40% 98%;
|
|
42
|
+
|
|
43
|
+
--muted: 260 30% 15%;
|
|
44
|
+
--muted-foreground: 215 20% 65%;
|
|
45
|
+
|
|
46
|
+
--accent: 260 30% 15%;
|
|
47
|
+
--accent-foreground: 210 40% 98%;
|
|
48
|
+
|
|
49
|
+
--destructive: 0 62% 30%;
|
|
50
|
+
--destructive-foreground: 210 40% 98%;
|
|
51
|
+
|
|
52
|
+
--border: 260 30% 15%;
|
|
53
|
+
--input: 260 30% 15%;
|
|
54
|
+
--ring: 265 89% 66%;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@layer base {
|
|
59
|
+
* {
|
|
60
|
+
@apply border-border;
|
|
61
|
+
}
|
|
62
|
+
body {
|
|
63
|
+
@apply bg-background text-foreground antialiased;
|
|
64
|
+
font-feature-settings: "rlig" 1, "calt" 1;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@layer utilities {
|
|
69
|
+
.glass {
|
|
70
|
+
@apply bg-white/10 backdrop-blur-lg border border-white/20 shadow-xl;
|
|
71
|
+
}
|
|
72
|
+
.glass-dark {
|
|
73
|
+
@apply bg-black/30 backdrop-blur-lg border border-white/10 shadow-xl;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.text-gradient {
|
|
77
|
+
@apply bg-clip-text text-transparent bg-gradient-to-r from-violet-600 to-indigo-600 dark:from-violet-400 dark:to-indigo-400;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.animate-float {
|
|
81
|
+
animation: float 6s ease-in-out infinite;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.animate-pulse-slow {
|
|
85
|
+
animation: pulse 4s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@keyframes float {
|
|
90
|
+
0% { transform: translateY(0px); }
|
|
91
|
+
50% { transform: translateY(-20px); }
|
|
92
|
+
100% { transform: translateY(0px); }
|
|
93
|
+
}
|