ar-saas 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/dist/generator.js +2 -6
  2. package/package.json +1 -1
  3. package/templates/backend/.env.example +1 -1
  4. package/templates/backend/package.json +5 -2
  5. package/templates/backend/src/app.module.ts +68 -40
  6. package/templates/backend/src/common/interceptors/workspace-tenant.interceptor.ts +27 -45
  7. package/templates/backend/src/main.ts +50 -51
  8. package/templates/backend/src/modules/auth/auth.controller.ts +162 -158
  9. package/templates/backend/src/modules/auth/auth.service.ts +236 -257
  10. package/templates/backend/src/modules/auth/strategies/jwt.strategy.ts +45 -43
  11. package/templates/backend/src/modules/users/users.controller.ts +28 -0
  12. package/templates/backend/src/modules/users/users.module.ts +16 -14
  13. package/templates/backend/src/modules/users/users.repository.ts +57 -51
  14. package/templates/backend/src/modules/users/users.service.ts +130 -104
  15. package/templates/backend/src/modules/workspaces/workspaces.repository.ts +38 -34
  16. package/templates/backend/src/modules/workspaces/workspaces.service.ts +51 -42
  17. package/templates/frontend/package.json +2 -5
  18. package/templates/frontend/pnpm-workspace.yaml +2 -2
  19. package/templates/frontend/src/app/(auth)/layout.tsx +29 -28
  20. package/templates/frontend/src/app/(dashboard)/billing/page.tsx +111 -111
  21. package/templates/frontend/src/app/(dashboard)/profile/page.tsx +241 -226
  22. package/templates/frontend/src/app/(dashboard)/settings/page.tsx +155 -156
  23. package/templates/frontend/src/app/(dashboard)/team/page.tsx +179 -178
  24. package/templates/frontend/src/app/layout.tsx +29 -26
  25. package/templates/frontend/src/app/page.tsx +1 -1
  26. package/templates/frontend/src/app/setup/page.tsx +1 -1
  27. package/templates/frontend/src/components/dashboard/header.tsx +5 -3
  28. package/templates/frontend/src/config/site.ts +1 -1
@@ -1,156 +1,155 @@
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
+ function toggleNotification(key: keyof NotificationSettings) {
37
+ setNotifications((prev) => ({ ...prev, [key]: !prev[key] }))
38
+ }
39
+
40
+ function toggleWorkspace(key: keyof WorkspaceSettings) {
41
+ setWorkspace((prev) => ({ ...prev, [key]: !prev[key] }))
42
+ }
43
+
44
+ return (
45
+ <div className="max-w-2xl space-y-6">
46
+ <div className="rounded-lg border border-amber-200 bg-amber-50 p-3">
47
+ <p className="text-xs text-amber-800 font-medium">Próximamente</p>
48
+ <p className="text-xs text-amber-700 mt-0.5">La configuración de notificaciones y workspace estará disponible en la próxima actualización.</p>
49
+ </div>
50
+
51
+ {/* Notificaciones */}
52
+ <Card>
53
+ <CardHeader>
54
+ <CardTitle>Notificaciones por email</CardTitle>
55
+ <CardDescription>Elegí qué correos querés recibir de nuestra parte.</CardDescription>
56
+ </CardHeader>
57
+ <CardContent className="space-y-4">
58
+ {[
59
+ {
60
+ key: 'emailSecurity' as const,
61
+ label: 'Seguridad',
62
+ description: 'Alertas de inicio de sesión y cambios de contraseña.',
63
+ badge: 'Recomendado',
64
+ },
65
+ {
66
+ key: 'emailProduct' as const,
67
+ label: 'Actualizaciones del producto',
68
+ description: 'Nuevas funcionalidades, mejoras y fixes importantes.',
69
+ badge: null,
70
+ },
71
+ {
72
+ key: 'emailMarketing' as const,
73
+ label: 'Marketing y promociones',
74
+ description: 'Ofertas, descuentos y contenido educativo.',
75
+ badge: null,
76
+ },
77
+ ].map(({ key, label, description, badge }) => (
78
+ <div key={key} className="flex items-start justify-between gap-4">
79
+ <div className="space-y-0.5">
80
+ <div className="flex items-center gap-2">
81
+ <Label className="text-sm font-medium">{label}</Label>
82
+ {badge && <Badge variant="secondary" className="text-xs">{badge}</Badge>}
83
+ </div>
84
+ <p className="text-xs text-muted-foreground">{description}</p>
85
+ </div>
86
+ <Switch
87
+ checked={notifications[key]}
88
+ onCheckedChange={() => toggleNotification(key)}
89
+ disabled
90
+ />
91
+ </div>
92
+ ))}
93
+ </CardContent>
94
+ </Card>
95
+
96
+ {/* Workspace */}
97
+ <Card>
98
+ <CardHeader>
99
+ <CardTitle>Workspace</CardTitle>
100
+ <CardDescription>Configuración de tu espacio de trabajo.</CardDescription>
101
+ </CardHeader>
102
+ <CardContent className="space-y-4">
103
+ <div className="flex items-start justify-between gap-4">
104
+ <div className="space-y-0.5">
105
+ <Label className="text-sm font-medium">Perfil público</Label>
106
+ <p className="text-xs text-muted-foreground">
107
+ Permite que otros usuarios encuentren tu perfil.
108
+ </p>
109
+ </div>
110
+ <Switch
111
+ checked={workspace.publicProfile}
112
+ onCheckedChange={() => toggleWorkspace('publicProfile')}
113
+ disabled
114
+ />
115
+ </div>
116
+
117
+ <Separator />
118
+
119
+ <div className="flex items-start justify-between gap-4">
120
+ <div className="space-y-0.5">
121
+ <Label className="text-sm font-medium">Invitaciones de equipo</Label>
122
+ <p className="text-xs text-muted-foreground">
123
+ Permite que otros te inviten a sus workspaces.
124
+ </p>
125
+ </div>
126
+ <Switch
127
+ checked={workspace.allowInvites}
128
+ onCheckedChange={() => toggleWorkspace('allowInvites')}
129
+ disabled
130
+ />
131
+ </div>
132
+ </CardContent>
133
+ </Card>
134
+
135
+ {/* Danger zone */}
136
+ <Card className="border-destructive/40">
137
+ <CardHeader>
138
+ <CardTitle className="text-destructive">Zona peligrosa</CardTitle>
139
+ <CardDescription>Acciones irreversibles sobre tu cuenta.</CardDescription>
140
+ </CardHeader>
141
+ <CardContent className="space-y-3">
142
+ <div className="flex items-center justify-between">
143
+ <div>
144
+ <p className="text-sm font-medium">Exportar mis datos</p>
145
+ <p className="text-xs text-muted-foreground">Descargá toda tu información en formato JSON.</p>
146
+ </div>
147
+ <Button variant="outline" size="sm" disabled>Exportar</Button>
148
+ </div>
149
+ </CardContent>
150
+ </Card>
151
+
152
+ <Button disabled>Guardar ajustes</Button>
153
+ </div>
154
+ )
155
+ }