ar-saas 0.3.0 → 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,156 +1,156 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { useState } from 'react'
|
|
4
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
|
5
|
-
import { Switch } from '@/components/ui/switch'
|
|
6
|
-
import { Separator } from '@/components/ui/separator'
|
|
7
|
-
import { Button } from '@/components/ui/button'
|
|
8
|
-
import { Label } from '@/components/ui/label'
|
|
9
|
-
import { Badge } from '@/components/ui/badge'
|
|
10
|
-
|
|
11
|
-
interface NotificationSettings {
|
|
12
|
-
emailMarketing: boolean
|
|
13
|
-
emailProduct: boolean
|
|
14
|
-
emailSecurity: boolean
|
|
15
|
-
pushAll: boolean
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface WorkspaceSettings {
|
|
19
|
-
publicProfile: boolean
|
|
20
|
-
allowInvites: boolean
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export default function SettingsPage() {
|
|
24
|
-
const [notifications, setNotifications] = useState<NotificationSettings>({
|
|
25
|
-
emailMarketing: false,
|
|
26
|
-
emailProduct: true,
|
|
27
|
-
emailSecurity: true,
|
|
28
|
-
pushAll: false,
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
const [workspace, setWorkspace] = useState<WorkspaceSettings>({
|
|
32
|
-
publicProfile: false,
|
|
33
|
-
allowInvites: true,
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
const [saved, setSaved] = useState(false)
|
|
37
|
-
|
|
38
|
-
function toggleNotification(key: keyof NotificationSettings) {
|
|
39
|
-
setNotifications((prev) => ({ ...prev, [key]: !prev[key] }))
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function toggleWorkspace(key: keyof WorkspaceSettings) {
|
|
43
|
-
setWorkspace((prev) => ({ ...prev, [key]: !prev[key] }))
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function handleSave() {
|
|
47
|
-
// TODO: llamar a PATCH /users/me/settings con notifications y workspace
|
|
48
|
-
console.log('Guardar ajustes:', { notifications, workspace })
|
|
49
|
-
setSaved(true)
|
|
50
|
-
setTimeout(() => setSaved(false), 2000)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<div className="max-w-2xl space-y-6">
|
|
55
|
-
{/* Notificaciones */}
|
|
56
|
-
<Card>
|
|
57
|
-
<CardHeader>
|
|
58
|
-
<CardTitle>Notificaciones por email</CardTitle>
|
|
59
|
-
<CardDescription>Elegí qué correos querés recibir de nuestra parte.</CardDescription>
|
|
60
|
-
</CardHeader>
|
|
61
|
-
<CardContent className="space-y-4">
|
|
62
|
-
{[
|
|
63
|
-
{
|
|
64
|
-
key: 'emailSecurity' as const,
|
|
65
|
-
label: 'Seguridad',
|
|
66
|
-
description: 'Alertas de inicio de sesión y cambios de contraseña.',
|
|
67
|
-
badge: 'Recomendado',
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
key: 'emailProduct' as const,
|
|
71
|
-
label: 'Actualizaciones del producto',
|
|
72
|
-
description: 'Nuevas funcionalidades, mejoras y fixes importantes.',
|
|
73
|
-
badge: null,
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
key: 'emailMarketing' as const,
|
|
77
|
-
label: 'Marketing y promociones',
|
|
78
|
-
description: 'Ofertas, descuentos y contenido educativo.',
|
|
79
|
-
badge: null,
|
|
80
|
-
},
|
|
81
|
-
].map(({ key, label, description, badge }) => (
|
|
82
|
-
<div key={key} className="flex items-start justify-between gap-4">
|
|
83
|
-
<div className="space-y-0.5">
|
|
84
|
-
<div className="flex items-center gap-2">
|
|
85
|
-
<Label className="text-sm font-medium">{label}</Label>
|
|
86
|
-
{badge && <Badge variant="secondary" className="text-xs">{badge}</Badge>}
|
|
87
|
-
</div>
|
|
88
|
-
<p className="text-xs text-muted-foreground">{description}</p>
|
|
89
|
-
</div>
|
|
90
|
-
<Switch
|
|
91
|
-
checked={notifications[key]}
|
|
92
|
-
onCheckedChange={() => toggleNotification(key)}
|
|
93
|
-
/>
|
|
94
|
-
</div>
|
|
95
|
-
))}
|
|
96
|
-
</CardContent>
|
|
97
|
-
</Card>
|
|
98
|
-
|
|
99
|
-
{/* Workspace */}
|
|
100
|
-
<Card>
|
|
101
|
-
<CardHeader>
|
|
102
|
-
<CardTitle>Workspace</CardTitle>
|
|
103
|
-
<CardDescription>Configuración de tu espacio de trabajo.</CardDescription>
|
|
104
|
-
</CardHeader>
|
|
105
|
-
<CardContent className="space-y-4">
|
|
106
|
-
<div className="flex items-start justify-between gap-4">
|
|
107
|
-
<div className="space-y-0.5">
|
|
108
|
-
<Label className="text-sm font-medium">Perfil público</Label>
|
|
109
|
-
<p className="text-xs text-muted-foreground">
|
|
110
|
-
Permite que otros usuarios encuentren tu perfil.
|
|
111
|
-
</p>
|
|
112
|
-
</div>
|
|
113
|
-
<Switch
|
|
114
|
-
checked={workspace.publicProfile}
|
|
115
|
-
onCheckedChange={() => toggleWorkspace('publicProfile')}
|
|
116
|
-
/>
|
|
117
|
-
</div>
|
|
118
|
-
|
|
119
|
-
<Separator />
|
|
120
|
-
|
|
121
|
-
<div className="flex items-start justify-between gap-4">
|
|
122
|
-
<div className="space-y-0.5">
|
|
123
|
-
<Label className="text-sm font-medium">Invitaciones de equipo</Label>
|
|
124
|
-
<p className="text-xs text-muted-foreground">
|
|
125
|
-
Permite que otros te inviten a sus workspaces.
|
|
126
|
-
</p>
|
|
127
|
-
</div>
|
|
128
|
-
<Switch
|
|
129
|
-
checked={workspace.allowInvites}
|
|
130
|
-
onCheckedChange={() => toggleWorkspace('allowInvites')}
|
|
131
|
-
/>
|
|
132
|
-
</div>
|
|
133
|
-
</CardContent>
|
|
134
|
-
</Card>
|
|
135
|
-
|
|
136
|
-
{/* Danger zone */}
|
|
137
|
-
<Card className="border-destructive/40">
|
|
138
|
-
<CardHeader>
|
|
139
|
-
<CardTitle className="text-destructive">Zona peligrosa</CardTitle>
|
|
140
|
-
<CardDescription>Acciones irreversibles sobre tu cuenta.</CardDescription>
|
|
141
|
-
</CardHeader>
|
|
142
|
-
<CardContent className="space-y-3">
|
|
143
|
-
<div className="flex items-center justify-between">
|
|
144
|
-
<div>
|
|
145
|
-
<p className="text-sm font-medium">Exportar mis datos</p>
|
|
146
|
-
<p className="text-xs text-muted-foreground">Descargá toda tu información en formato JSON.</p>
|
|
147
|
-
</div>
|
|
148
|
-
<Button variant="outline" size="sm" disabled>Exportar</Button>
|
|
149
|
-
</div>
|
|
150
|
-
</CardContent>
|
|
151
|
-
</Card>
|
|
152
|
-
|
|
153
|
-
<Button onClick={handleSave}>{saved ? 'Guardado' : 'Guardar ajustes'}</Button>
|
|
154
|
-
</div>
|
|
155
|
-
)
|
|
156
|
-
}
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
|
5
|
+
import { Switch } from '@/components/ui/switch'
|
|
6
|
+
import { Separator } from '@/components/ui/separator'
|
|
7
|
+
import { Button } from '@/components/ui/button'
|
|
8
|
+
import { Label } from '@/components/ui/label'
|
|
9
|
+
import { Badge } from '@/components/ui/badge'
|
|
10
|
+
|
|
11
|
+
interface NotificationSettings {
|
|
12
|
+
emailMarketing: boolean
|
|
13
|
+
emailProduct: boolean
|
|
14
|
+
emailSecurity: boolean
|
|
15
|
+
pushAll: boolean
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface WorkspaceSettings {
|
|
19
|
+
publicProfile: boolean
|
|
20
|
+
allowInvites: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default function SettingsPage() {
|
|
24
|
+
const [notifications, setNotifications] = useState<NotificationSettings>({
|
|
25
|
+
emailMarketing: false,
|
|
26
|
+
emailProduct: true,
|
|
27
|
+
emailSecurity: true,
|
|
28
|
+
pushAll: false,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const [workspace, setWorkspace] = useState<WorkspaceSettings>({
|
|
32
|
+
publicProfile: false,
|
|
33
|
+
allowInvites: true,
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const [saved, setSaved] = useState(false)
|
|
37
|
+
|
|
38
|
+
function toggleNotification(key: keyof NotificationSettings) {
|
|
39
|
+
setNotifications((prev) => ({ ...prev, [key]: !prev[key] }))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function toggleWorkspace(key: keyof WorkspaceSettings) {
|
|
43
|
+
setWorkspace((prev) => ({ ...prev, [key]: !prev[key] }))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function handleSave() {
|
|
47
|
+
// TODO: llamar a PATCH /users/me/settings con notifications y workspace
|
|
48
|
+
console.log('Guardar ajustes:', { notifications, workspace })
|
|
49
|
+
setSaved(true)
|
|
50
|
+
setTimeout(() => setSaved(false), 2000)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div className="max-w-2xl space-y-6">
|
|
55
|
+
{/* Notificaciones */}
|
|
56
|
+
<Card>
|
|
57
|
+
<CardHeader>
|
|
58
|
+
<CardTitle>Notificaciones por email</CardTitle>
|
|
59
|
+
<CardDescription>Elegí qué correos querés recibir de nuestra parte.</CardDescription>
|
|
60
|
+
</CardHeader>
|
|
61
|
+
<CardContent className="space-y-4">
|
|
62
|
+
{[
|
|
63
|
+
{
|
|
64
|
+
key: 'emailSecurity' as const,
|
|
65
|
+
label: 'Seguridad',
|
|
66
|
+
description: 'Alertas de inicio de sesión y cambios de contraseña.',
|
|
67
|
+
badge: 'Recomendado',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
key: 'emailProduct' as const,
|
|
71
|
+
label: 'Actualizaciones del producto',
|
|
72
|
+
description: 'Nuevas funcionalidades, mejoras y fixes importantes.',
|
|
73
|
+
badge: null,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
key: 'emailMarketing' as const,
|
|
77
|
+
label: 'Marketing y promociones',
|
|
78
|
+
description: 'Ofertas, descuentos y contenido educativo.',
|
|
79
|
+
badge: null,
|
|
80
|
+
},
|
|
81
|
+
].map(({ key, label, description, badge }) => (
|
|
82
|
+
<div key={key} className="flex items-start justify-between gap-4">
|
|
83
|
+
<div className="space-y-0.5">
|
|
84
|
+
<div className="flex items-center gap-2">
|
|
85
|
+
<Label className="text-sm font-medium">{label}</Label>
|
|
86
|
+
{badge && <Badge variant="secondary" className="text-xs">{badge}</Badge>}
|
|
87
|
+
</div>
|
|
88
|
+
<p className="text-xs text-muted-foreground">{description}</p>
|
|
89
|
+
</div>
|
|
90
|
+
<Switch
|
|
91
|
+
checked={notifications[key]}
|
|
92
|
+
onCheckedChange={() => toggleNotification(key)}
|
|
93
|
+
/>
|
|
94
|
+
</div>
|
|
95
|
+
))}
|
|
96
|
+
</CardContent>
|
|
97
|
+
</Card>
|
|
98
|
+
|
|
99
|
+
{/* Workspace */}
|
|
100
|
+
<Card>
|
|
101
|
+
<CardHeader>
|
|
102
|
+
<CardTitle>Workspace</CardTitle>
|
|
103
|
+
<CardDescription>Configuración de tu espacio de trabajo.</CardDescription>
|
|
104
|
+
</CardHeader>
|
|
105
|
+
<CardContent className="space-y-4">
|
|
106
|
+
<div className="flex items-start justify-between gap-4">
|
|
107
|
+
<div className="space-y-0.5">
|
|
108
|
+
<Label className="text-sm font-medium">Perfil público</Label>
|
|
109
|
+
<p className="text-xs text-muted-foreground">
|
|
110
|
+
Permite que otros usuarios encuentren tu perfil.
|
|
111
|
+
</p>
|
|
112
|
+
</div>
|
|
113
|
+
<Switch
|
|
114
|
+
checked={workspace.publicProfile}
|
|
115
|
+
onCheckedChange={() => toggleWorkspace('publicProfile')}
|
|
116
|
+
/>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<Separator />
|
|
120
|
+
|
|
121
|
+
<div className="flex items-start justify-between gap-4">
|
|
122
|
+
<div className="space-y-0.5">
|
|
123
|
+
<Label className="text-sm font-medium">Invitaciones de equipo</Label>
|
|
124
|
+
<p className="text-xs text-muted-foreground">
|
|
125
|
+
Permite que otros te inviten a sus workspaces.
|
|
126
|
+
</p>
|
|
127
|
+
</div>
|
|
128
|
+
<Switch
|
|
129
|
+
checked={workspace.allowInvites}
|
|
130
|
+
onCheckedChange={() => toggleWorkspace('allowInvites')}
|
|
131
|
+
/>
|
|
132
|
+
</div>
|
|
133
|
+
</CardContent>
|
|
134
|
+
</Card>
|
|
135
|
+
|
|
136
|
+
{/* Danger zone */}
|
|
137
|
+
<Card className="border-destructive/40">
|
|
138
|
+
<CardHeader>
|
|
139
|
+
<CardTitle className="text-destructive">Zona peligrosa</CardTitle>
|
|
140
|
+
<CardDescription>Acciones irreversibles sobre tu cuenta.</CardDescription>
|
|
141
|
+
</CardHeader>
|
|
142
|
+
<CardContent className="space-y-3">
|
|
143
|
+
<div className="flex items-center justify-between">
|
|
144
|
+
<div>
|
|
145
|
+
<p className="text-sm font-medium">Exportar mis datos</p>
|
|
146
|
+
<p className="text-xs text-muted-foreground">Descargá toda tu información en formato JSON.</p>
|
|
147
|
+
</div>
|
|
148
|
+
<Button variant="outline" size="sm" disabled>Exportar</Button>
|
|
149
|
+
</div>
|
|
150
|
+
</CardContent>
|
|
151
|
+
</Card>
|
|
152
|
+
|
|
153
|
+
<Button onClick={handleSave}>{saved ? 'Guardado' : 'Guardar ajustes'}</Button>
|
|
154
|
+
</div>
|
|
155
|
+
)
|
|
156
|
+
}
|