ar-saas 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +338 -314
- package/dist/cli.js +19 -0
- package/dist/generator.js +166 -55
- package/package.json +52 -50
- package/templates/backend/.env.example +67 -67
- package/templates/backend/.prettierrc +4 -4
- package/templates/backend/README.md +249 -168
- package/templates/backend/eslint.config.mjs +35 -35
- package/templates/backend/nest-cli.json +8 -8
- package/templates/backend/package-lock.json +10979 -10979
- package/templates/backend/package.json +88 -88
- package/templates/backend/src/app.controller.spec.ts +24 -24
- package/templates/backend/src/app.controller.ts +15 -15
- package/templates/backend/src/app.module.ts +40 -40
- package/templates/backend/src/app.service.ts +11 -11
- package/templates/backend/src/common/base/base.repository.ts +221 -221
- package/templates/backend/src/common/base/base.schema.ts +24 -24
- package/templates/backend/src/common/decorators/cookie.decorator.ts +9 -9
- package/templates/backend/src/common/decorators/current-user.decorator.ts +20 -20
- package/templates/backend/src/common/decorators/workspace-id.decorator.ts +14 -14
- package/templates/backend/src/common/filters/global-exception.filter.ts +61 -61
- package/templates/backend/src/common/guards/jwt-auth.guard.ts +5 -5
- package/templates/backend/src/common/interceptors/workspace-tenant.interceptor.ts +45 -45
- package/templates/backend/src/main.ts +51 -51
- package/templates/backend/src/modules/auth/auth.controller.ts +158 -158
- package/templates/backend/src/modules/auth/auth.module.ts +20 -20
- package/templates/backend/src/modules/auth/auth.service.ts +257 -257
- package/templates/backend/src/modules/auth/dto/forgot-password.dto.ts +9 -9
- package/templates/backend/src/modules/auth/dto/login.dto.ts +14 -14
- package/templates/backend/src/modules/auth/dto/refresh-token.dto.ts +12 -12
- package/templates/backend/src/modules/auth/dto/register.dto.ts +26 -26
- package/templates/backend/src/modules/auth/dto/reset-password.dto.ts +16 -16
- package/templates/backend/src/modules/auth/dto/verify-email.dto.ts +9 -9
- package/templates/backend/src/modules/auth/strategies/jwt.strategy.ts +43 -43
- package/templates/backend/src/modules/mail/mail.module.ts +9 -9
- package/templates/backend/src/modules/mail/mail.service.ts +141 -141
- package/templates/backend/src/modules/users/schemas/user.schema.ts +54 -54
- package/templates/backend/src/modules/users/users.module.ts +14 -14
- package/templates/backend/src/modules/users/users.repository.ts +51 -51
- package/templates/backend/src/modules/users/users.service.ts +104 -104
- package/templates/backend/src/modules/workspaces/schemas/workspace.schema.ts +26 -26
- package/templates/backend/src/modules/workspaces/workspaces.module.ts +16 -16
- package/templates/backend/src/modules/workspaces/workspaces.repository.ts +34 -34
- package/templates/backend/src/modules/workspaces/workspaces.service.ts +42 -42
- package/templates/backend/test/app.e2e-spec.ts +25 -25
- package/templates/backend/test/jest-e2e.json +9 -9
- package/templates/backend/tsconfig.build.json +4 -4
- package/templates/backend/tsconfig.json +26 -26
- package/templates/frontend/.env.local.example +1 -1
- package/templates/frontend/README.md +152 -0
- package/templates/frontend/components.json +20 -20
- package/templates/frontend/eslint.config.mjs +14 -14
- package/templates/frontend/next.config.ts +5 -5
- package/templates/frontend/package-lock.json +6722 -6722
- package/templates/frontend/package.json +48 -48
- package/templates/frontend/pnpm-lock.yaml +5012 -5012
- package/templates/frontend/pnpm-workspace.yaml +3 -3
- package/templates/frontend/postcss.config.mjs +7 -7
- package/templates/frontend/src/app/(auth)/forgot-password/page.tsx +84 -84
- package/templates/frontend/src/app/(auth)/layout.tsx +28 -28
- package/templates/frontend/src/app/(auth)/login/page.tsx +111 -111
- package/templates/frontend/src/app/(auth)/register/page.tsx +161 -161
- package/templates/frontend/src/app/(auth)/reset-password/page.tsx +120 -120
- package/templates/frontend/src/app/(auth)/verify-email/page.tsx +78 -78
- package/templates/frontend/src/app/(dashboard)/billing/page.tsx +111 -111
- package/templates/frontend/src/app/(dashboard)/dashboard/page.tsx +105 -105
- package/templates/frontend/src/app/(dashboard)/layout.tsx +38 -38
- package/templates/frontend/src/app/(dashboard)/profile/page.tsx +226 -226
- package/templates/frontend/src/app/(dashboard)/settings/page.tsx +156 -156
- package/templates/frontend/src/app/(dashboard)/team/page.tsx +178 -178
- package/templates/frontend/src/app/(legal)/privacy/page.tsx +127 -127
- package/templates/frontend/src/app/(legal)/terms/page.tsx +118 -118
- package/templates/frontend/src/app/globals.css +81 -81
- package/templates/frontend/src/app/layout.tsx +26 -26
- package/templates/frontend/src/app/page.tsx +5 -45
- package/templates/frontend/src/app/setup/page.tsx +371 -275
- package/templates/frontend/src/components/dashboard/header.tsx +89 -89
- package/templates/frontend/src/components/dashboard/sidebar.tsx +71 -71
- package/templates/frontend/src/components/dashboard/stat-card.tsx +34 -34
- package/templates/frontend/src/components/landing/faq.tsx +39 -39
- package/templates/frontend/src/components/landing/features.tsx +54 -54
- package/templates/frontend/src/components/landing/footer.tsx +76 -76
- package/templates/frontend/src/components/landing/hero.tsx +72 -72
- package/templates/frontend/src/components/landing/navbar.tsx +78 -78
- package/templates/frontend/src/components/landing/pricing.tsx +90 -90
- package/templates/frontend/src/components/ui/accordion.tsx +52 -52
- package/templates/frontend/src/components/ui/avatar.tsx +46 -46
- package/templates/frontend/src/components/ui/badge.tsx +30 -30
- package/templates/frontend/src/components/ui/button.tsx +52 -52
- package/templates/frontend/src/components/ui/card.tsx +50 -50
- package/templates/frontend/src/components/ui/checkbox.tsx +27 -27
- package/templates/frontend/src/components/ui/dialog.tsx +100 -100
- package/templates/frontend/src/components/ui/dropdown-menu.tsx +173 -173
- package/templates/frontend/src/components/ui/form.tsx +158 -158
- package/templates/frontend/src/components/ui/input.tsx +21 -21
- package/templates/frontend/src/components/ui/label.tsx +22 -22
- package/templates/frontend/src/components/ui/separator.tsx +25 -25
- package/templates/frontend/src/components/ui/skeleton.tsx +7 -7
- package/templates/frontend/src/components/ui/switch.tsx +28 -28
- package/templates/frontend/src/components/ui/tabs.tsx +54 -54
- package/templates/frontend/src/components/ui/textarea.tsx +20 -20
- package/templates/frontend/src/components/ui/toast.tsx +109 -109
- package/templates/frontend/src/components/ui/toaster.tsx +30 -30
- package/templates/frontend/src/config/site.ts +197 -197
- package/templates/frontend/src/hooks/use-toast.ts +116 -116
- package/templates/frontend/src/lib/api/auth.ts +39 -39
- package/templates/frontend/src/lib/api/client.ts +66 -66
- package/templates/frontend/src/lib/hooks/use-auth.ts +1 -1
- package/templates/frontend/src/lib/utils.ts +6 -6
- package/templates/frontend/src/providers/auth-provider.tsx +60 -60
- package/templates/frontend/src/types/api.ts +12 -12
- package/templates/frontend/src/types/auth.ts +27 -27
- package/templates/frontend/tsconfig.json +23 -23
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
import apiClient from './client'
|
|
2
|
-
import type { User } from '@/types/auth'
|
|
3
|
-
|
|
4
|
-
export const authApi = {
|
|
5
|
-
register(data: { name: string; email: string; password: string }) {
|
|
6
|
-
return apiClient.post<never, { message: string }>('/api/auth/register', data)
|
|
7
|
-
},
|
|
8
|
-
|
|
9
|
-
login(data: { email: string; password: string }) {
|
|
10
|
-
return apiClient.post<never, User>('/api/auth/login', data)
|
|
11
|
-
},
|
|
12
|
-
|
|
13
|
-
logout() {
|
|
14
|
-
return apiClient.post<never, { message: string }>('/api/auth/logout')
|
|
15
|
-
},
|
|
16
|
-
|
|
17
|
-
refresh() {
|
|
18
|
-
return apiClient.post<never, { message: string }>('/api/auth/refresh')
|
|
19
|
-
},
|
|
20
|
-
|
|
21
|
-
verifyEmail(token: string) {
|
|
22
|
-
return apiClient.get<never, { message: string }>(`/api/auth/verify-email?token=${token}`)
|
|
23
|
-
},
|
|
24
|
-
|
|
25
|
-
forgotPassword(email: string) {
|
|
26
|
-
return apiClient.post<never, { message: string }>('/api/auth/forgot-password', { email })
|
|
27
|
-
},
|
|
28
|
-
|
|
29
|
-
resetPassword(token: string, newPassword: string) {
|
|
30
|
-
return apiClient.post<never, { message: string }>('/api/auth/reset-password', {
|
|
31
|
-
token,
|
|
32
|
-
newPassword,
|
|
33
|
-
})
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
getMe() {
|
|
37
|
-
return apiClient.get<never, User>('/api/auth/me')
|
|
38
|
-
},
|
|
39
|
-
}
|
|
1
|
+
import apiClient from './client'
|
|
2
|
+
import type { User } from '@/types/auth'
|
|
3
|
+
|
|
4
|
+
export const authApi = {
|
|
5
|
+
register(data: { name: string; email: string; password: string }) {
|
|
6
|
+
return apiClient.post<never, { message: string }>('/api/auth/register', data)
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
login(data: { email: string; password: string }) {
|
|
10
|
+
return apiClient.post<never, User>('/api/auth/login', data)
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
logout() {
|
|
14
|
+
return apiClient.post<never, { message: string }>('/api/auth/logout')
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
refresh() {
|
|
18
|
+
return apiClient.post<never, { message: string }>('/api/auth/refresh')
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
verifyEmail(token: string) {
|
|
22
|
+
return apiClient.get<never, { message: string }>(`/api/auth/verify-email?token=${token}`)
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
forgotPassword(email: string) {
|
|
26
|
+
return apiClient.post<never, { message: string }>('/api/auth/forgot-password', { email })
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
resetPassword(token: string, newPassword: string) {
|
|
30
|
+
return apiClient.post<never, { message: string }>('/api/auth/reset-password', {
|
|
31
|
+
token,
|
|
32
|
+
newPassword,
|
|
33
|
+
})
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
getMe() {
|
|
37
|
+
return apiClient.get<never, User>('/api/auth/me')
|
|
38
|
+
},
|
|
39
|
+
}
|
|
@@ -1,66 +1,66 @@
|
|
|
1
|
-
import axios, { type AxiosError, type InternalAxiosRequestConfig } from 'axios'
|
|
2
|
-
import type { ApiError } from '@/types/api'
|
|
3
|
-
|
|
4
|
-
const apiClient = axios.create({
|
|
5
|
-
baseURL: process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3000',
|
|
6
|
-
withCredentials: true,
|
|
7
|
-
})
|
|
8
|
-
|
|
9
|
-
let isRefreshing = false
|
|
10
|
-
let failedQueue: Array<{
|
|
11
|
-
resolve: (value: unknown) => void
|
|
12
|
-
reject: (reason?: unknown) => void
|
|
13
|
-
}> = []
|
|
14
|
-
|
|
15
|
-
function processQueue(error: unknown) {
|
|
16
|
-
failedQueue.forEach(({ resolve, reject }) => {
|
|
17
|
-
if (error) {
|
|
18
|
-
reject(error)
|
|
19
|
-
} else {
|
|
20
|
-
resolve(undefined)
|
|
21
|
-
}
|
|
22
|
-
})
|
|
23
|
-
failedQueue = []
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
apiClient.interceptors.response.use(
|
|
27
|
-
(response) => response.data,
|
|
28
|
-
async (error: AxiosError<ApiError>) => {
|
|
29
|
-
const originalRequest = error.config as InternalAxiosRequestConfig & { _retry?: boolean }
|
|
30
|
-
|
|
31
|
-
if (error.response?.status !== 401 || originalRequest._retry) {
|
|
32
|
-
return Promise.reject(error.response?.data ?? error)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (isRefreshing) {
|
|
36
|
-
return new Promise((resolve, reject) => {
|
|
37
|
-
failedQueue.push({ resolve, reject })
|
|
38
|
-
})
|
|
39
|
-
.then(() => apiClient(originalRequest))
|
|
40
|
-
.catch((err) => Promise.reject(err))
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
originalRequest._retry = true
|
|
44
|
-
isRefreshing = true
|
|
45
|
-
|
|
46
|
-
try {
|
|
47
|
-
await axios.post(
|
|
48
|
-
`${process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3000'}/api/auth/refresh`,
|
|
49
|
-
{},
|
|
50
|
-
{ withCredentials: true },
|
|
51
|
-
)
|
|
52
|
-
processQueue(null)
|
|
53
|
-
return apiClient(originalRequest)
|
|
54
|
-
} catch (refreshError) {
|
|
55
|
-
processQueue(refreshError)
|
|
56
|
-
if (typeof window !== 'undefined') {
|
|
57
|
-
window.location.href = '/login'
|
|
58
|
-
}
|
|
59
|
-
return Promise.reject(refreshError)
|
|
60
|
-
} finally {
|
|
61
|
-
isRefreshing = false
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
export default apiClient
|
|
1
|
+
import axios, { type AxiosError, type InternalAxiosRequestConfig } from 'axios'
|
|
2
|
+
import type { ApiError } from '@/types/api'
|
|
3
|
+
|
|
4
|
+
const apiClient = axios.create({
|
|
5
|
+
baseURL: process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3000',
|
|
6
|
+
withCredentials: true,
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
let isRefreshing = false
|
|
10
|
+
let failedQueue: Array<{
|
|
11
|
+
resolve: (value: unknown) => void
|
|
12
|
+
reject: (reason?: unknown) => void
|
|
13
|
+
}> = []
|
|
14
|
+
|
|
15
|
+
function processQueue(error: unknown) {
|
|
16
|
+
failedQueue.forEach(({ resolve, reject }) => {
|
|
17
|
+
if (error) {
|
|
18
|
+
reject(error)
|
|
19
|
+
} else {
|
|
20
|
+
resolve(undefined)
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
failedQueue = []
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
apiClient.interceptors.response.use(
|
|
27
|
+
(response) => response.data,
|
|
28
|
+
async (error: AxiosError<ApiError>) => {
|
|
29
|
+
const originalRequest = error.config as InternalAxiosRequestConfig & { _retry?: boolean }
|
|
30
|
+
|
|
31
|
+
if (error.response?.status !== 401 || originalRequest._retry) {
|
|
32
|
+
return Promise.reject(error.response?.data ?? error)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (isRefreshing) {
|
|
36
|
+
return new Promise((resolve, reject) => {
|
|
37
|
+
failedQueue.push({ resolve, reject })
|
|
38
|
+
})
|
|
39
|
+
.then(() => apiClient(originalRequest))
|
|
40
|
+
.catch((err) => Promise.reject(err))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
originalRequest._retry = true
|
|
44
|
+
isRefreshing = true
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
await axios.post(
|
|
48
|
+
`${process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3000'}/api/auth/refresh`,
|
|
49
|
+
{},
|
|
50
|
+
{ withCredentials: true },
|
|
51
|
+
)
|
|
52
|
+
processQueue(null)
|
|
53
|
+
return apiClient(originalRequest)
|
|
54
|
+
} catch (refreshError) {
|
|
55
|
+
processQueue(refreshError)
|
|
56
|
+
if (typeof window !== 'undefined') {
|
|
57
|
+
window.location.href = '/login'
|
|
58
|
+
}
|
|
59
|
+
return Promise.reject(refreshError)
|
|
60
|
+
} finally {
|
|
61
|
+
isRefreshing = false
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
export default apiClient
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { useAuth } from '@/providers/auth-provider'
|
|
1
|
+
export { useAuth } from '@/providers/auth-provider'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type ClassValue, clsx } from 'clsx'
|
|
2
|
-
import { twMerge } from 'tailwind-merge'
|
|
3
|
-
|
|
4
|
-
export function cn(...inputs: ClassValue[]) {
|
|
5
|
-
return twMerge(clsx(inputs))
|
|
6
|
-
}
|
|
1
|
+
import { type ClassValue, clsx } from 'clsx'
|
|
2
|
+
import { twMerge } from 'tailwind-merge'
|
|
3
|
+
|
|
4
|
+
export function cn(...inputs: ClassValue[]) {
|
|
5
|
+
return twMerge(clsx(inputs))
|
|
6
|
+
}
|
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import React, { createContext, useCallback, useContext, useEffect, useState } from 'react'
|
|
4
|
-
import { useRouter } from 'next/navigation'
|
|
5
|
-
import { authApi } from '@/lib/api/auth'
|
|
6
|
-
import type { AuthState, User } from '@/types/auth'
|
|
7
|
-
|
|
8
|
-
interface AuthContextValue extends AuthState {
|
|
9
|
-
login: (email: string, password: string) => Promise<void>
|
|
10
|
-
logout: () => Promise<void>
|
|
11
|
-
register: (name: string, email: string, password: string) => Promise<{ message: string }>
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const AuthContext = createContext<AuthContextValue | null>(null)
|
|
15
|
-
|
|
16
|
-
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|
17
|
-
const router = useRouter()
|
|
18
|
-
const [state, setState] = useState<AuthState>({
|
|
19
|
-
user: null,
|
|
20
|
-
isLoading: true,
|
|
21
|
-
isAuthenticated: false,
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
useEffect(() => {
|
|
25
|
-
authApi
|
|
26
|
-
.getMe()
|
|
27
|
-
.then((user: User) => setState({ user, isLoading: false, isAuthenticated: true }))
|
|
28
|
-
.catch(() => setState({ user: null, isLoading: false, isAuthenticated: false }))
|
|
29
|
-
}, [])
|
|
30
|
-
|
|
31
|
-
const login = useCallback(async (email: string, password: string) => {
|
|
32
|
-
const user = await authApi.login({ email, password })
|
|
33
|
-
setState({ user, isLoading: false, isAuthenticated: true })
|
|
34
|
-
router.push('/dashboard')
|
|
35
|
-
}, [router])
|
|
36
|
-
|
|
37
|
-
const logout = useCallback(async () => {
|
|
38
|
-
await authApi.logout()
|
|
39
|
-
setState({ user: null, isLoading: false, isAuthenticated: false })
|
|
40
|
-
router.push('/login')
|
|
41
|
-
}, [router])
|
|
42
|
-
|
|
43
|
-
const register = useCallback(
|
|
44
|
-
(name: string, email: string, password: string) =>
|
|
45
|
-
authApi.register({ name, email, password }),
|
|
46
|
-
[],
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
return (
|
|
50
|
-
<AuthContext.Provider value={{ ...state, login, logout, register }}>
|
|
51
|
-
{children}
|
|
52
|
-
</AuthContext.Provider>
|
|
53
|
-
)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function useAuth(): AuthContextValue {
|
|
57
|
-
const ctx = useContext(AuthContext)
|
|
58
|
-
if (!ctx) throw new Error('useAuth debe usarse dentro de <AuthProvider>')
|
|
59
|
-
return ctx
|
|
60
|
-
}
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { createContext, useCallback, useContext, useEffect, useState } from 'react'
|
|
4
|
+
import { useRouter } from 'next/navigation'
|
|
5
|
+
import { authApi } from '@/lib/api/auth'
|
|
6
|
+
import type { AuthState, User } from '@/types/auth'
|
|
7
|
+
|
|
8
|
+
interface AuthContextValue extends AuthState {
|
|
9
|
+
login: (email: string, password: string) => Promise<void>
|
|
10
|
+
logout: () => Promise<void>
|
|
11
|
+
register: (name: string, email: string, password: string) => Promise<{ message: string }>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const AuthContext = createContext<AuthContextValue | null>(null)
|
|
15
|
+
|
|
16
|
+
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|
17
|
+
const router = useRouter()
|
|
18
|
+
const [state, setState] = useState<AuthState>({
|
|
19
|
+
user: null,
|
|
20
|
+
isLoading: true,
|
|
21
|
+
isAuthenticated: false,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
authApi
|
|
26
|
+
.getMe()
|
|
27
|
+
.then((user: User) => setState({ user, isLoading: false, isAuthenticated: true }))
|
|
28
|
+
.catch(() => setState({ user: null, isLoading: false, isAuthenticated: false }))
|
|
29
|
+
}, [])
|
|
30
|
+
|
|
31
|
+
const login = useCallback(async (email: string, password: string) => {
|
|
32
|
+
const user = await authApi.login({ email, password })
|
|
33
|
+
setState({ user, isLoading: false, isAuthenticated: true })
|
|
34
|
+
router.push('/dashboard')
|
|
35
|
+
}, [router])
|
|
36
|
+
|
|
37
|
+
const logout = useCallback(async () => {
|
|
38
|
+
await authApi.logout()
|
|
39
|
+
setState({ user: null, isLoading: false, isAuthenticated: false })
|
|
40
|
+
router.push('/login')
|
|
41
|
+
}, [router])
|
|
42
|
+
|
|
43
|
+
const register = useCallback(
|
|
44
|
+
(name: string, email: string, password: string) =>
|
|
45
|
+
authApi.register({ name, email, password }),
|
|
46
|
+
[],
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<AuthContext.Provider value={{ ...state, login, logout, register }}>
|
|
51
|
+
{children}
|
|
52
|
+
</AuthContext.Provider>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function useAuth(): AuthContextValue {
|
|
57
|
+
const ctx = useContext(AuthContext)
|
|
58
|
+
if (!ctx) throw new Error('useAuth debe usarse dentro de <AuthProvider>')
|
|
59
|
+
return ctx
|
|
60
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
export interface ApiError {
|
|
2
|
-
statusCode: number
|
|
3
|
-
message: string
|
|
4
|
-
error: string
|
|
5
|
-
timestamp: string
|
|
6
|
-
path: string
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface ApiResponse<T> {
|
|
10
|
-
data: T
|
|
11
|
-
message?: string
|
|
12
|
-
}
|
|
1
|
+
export interface ApiError {
|
|
2
|
+
statusCode: number
|
|
3
|
+
message: string
|
|
4
|
+
error: string
|
|
5
|
+
timestamp: string
|
|
6
|
+
path: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ApiResponse<T> {
|
|
10
|
+
data: T
|
|
11
|
+
message?: string
|
|
12
|
+
}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
export interface User {
|
|
2
|
-
_id: string
|
|
3
|
-
name: string
|
|
4
|
-
email: string
|
|
5
|
-
emailVerified: boolean
|
|
6
|
-
role: 'owner' | 'admin' | 'member'
|
|
7
|
-
workspaceId: string
|
|
8
|
-
lastLoginAt?: string
|
|
9
|
-
createdAt: string
|
|
10
|
-
updatedAt: string
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface Workspace {
|
|
14
|
-
_id: string
|
|
15
|
-
name: string
|
|
16
|
-
slug: string
|
|
17
|
-
ownerId: string
|
|
18
|
-
status: 'active' | 'suspended'
|
|
19
|
-
createdAt: string
|
|
20
|
-
updatedAt: string
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface AuthState {
|
|
24
|
-
user: User | null
|
|
25
|
-
isLoading: boolean
|
|
26
|
-
isAuthenticated: boolean
|
|
27
|
-
}
|
|
1
|
+
export interface User {
|
|
2
|
+
_id: string
|
|
3
|
+
name: string
|
|
4
|
+
email: string
|
|
5
|
+
emailVerified: boolean
|
|
6
|
+
role: 'owner' | 'admin' | 'member'
|
|
7
|
+
workspaceId: string
|
|
8
|
+
lastLoginAt?: string
|
|
9
|
+
createdAt: string
|
|
10
|
+
updatedAt: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface Workspace {
|
|
14
|
+
_id: string
|
|
15
|
+
name: string
|
|
16
|
+
slug: string
|
|
17
|
+
ownerId: string
|
|
18
|
+
status: 'active' | 'suspended'
|
|
19
|
+
createdAt: string
|
|
20
|
+
updatedAt: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface AuthState {
|
|
24
|
+
user: User | null
|
|
25
|
+
isLoading: boolean
|
|
26
|
+
isAuthenticated: boolean
|
|
27
|
+
}
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2017",
|
|
4
|
-
"lib": ["dom", "dom.iterable", "esnext"],
|
|
5
|
-
"allowJs": true,
|
|
6
|
-
"skipLibCheck": true,
|
|
7
|
-
"strict": true,
|
|
8
|
-
"noEmit": true,
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"module": "esnext",
|
|
11
|
-
"moduleResolution": "bundler",
|
|
12
|
-
"resolveJsonModule": true,
|
|
13
|
-
"isolatedModules": true,
|
|
14
|
-
"jsx": "preserve",
|
|
15
|
-
"incremental": true,
|
|
16
|
-
"plugins": [{ "name": "next" }],
|
|
17
|
-
"paths": {
|
|
18
|
-
"@/*": ["./src/*"]
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
22
|
-
"exclude": ["node_modules"]
|
|
23
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2017",
|
|
4
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"module": "esnext",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"jsx": "preserve",
|
|
15
|
+
"incremental": true,
|
|
16
|
+
"plugins": [{ "name": "next" }],
|
|
17
|
+
"paths": {
|
|
18
|
+
"@/*": ["./src/*"]
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
22
|
+
"exclude": ["node_modules"]
|
|
23
|
+
}
|