ar-saas 0.4.3 → 0.5.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.
- package/package.json +1 -1
- package/templates/backend/.env.example +13 -3
- package/templates/backend/README.md +22 -3
- package/templates/backend/package-lock.json +165 -2
- package/templates/backend/package.json +2 -0
- package/templates/backend/src/app.module.ts +14 -0
- package/templates/backend/src/common/guards/github-auth.guard.ts +5 -0
- package/templates/backend/src/main.ts +2 -2
- package/templates/backend/src/modules/auth/auth.controller.ts +51 -3
- package/templates/backend/src/modules/auth/auth.module.ts +2 -1
- package/templates/backend/src/modules/auth/auth.service.ts +96 -11
- package/templates/backend/src/modules/auth/strategies/github.strategy.ts +46 -0
- package/templates/backend/src/modules/clients/clients.controller.ts +91 -0
- package/templates/backend/src/modules/clients/clients.module.ts +16 -0
- package/templates/backend/src/modules/clients/clients.repository.ts +14 -0
- package/templates/backend/src/modules/clients/clients.service.ts +52 -0
- package/templates/backend/src/modules/clients/dto/create-client.dto.ts +40 -0
- package/templates/backend/src/modules/clients/dto/query-client.dto.ts +30 -0
- package/templates/backend/src/modules/clients/dto/update-client.dto.ts +4 -0
- package/templates/backend/src/modules/clients/schemas/client.schema.ts +32 -0
- package/templates/backend/src/modules/invoices/dto/create-invoice.dto.ts +79 -0
- package/templates/backend/src/modules/invoices/dto/invoice-item.dto.ts +23 -0
- package/templates/backend/src/modules/invoices/dto/query-invoice.dto.ts +40 -0
- package/templates/backend/src/modules/invoices/dto/update-invoice.dto.ts +4 -0
- package/templates/backend/src/modules/invoices/invoices.controller.ts +91 -0
- package/templates/backend/src/modules/invoices/invoices.module.ts +18 -0
- package/templates/backend/src/modules/invoices/invoices.repository.ts +14 -0
- package/templates/backend/src/modules/invoices/invoices.service.ts +104 -0
- package/templates/backend/src/modules/invoices/schemas/invoice.schema.ts +75 -0
- package/templates/backend/src/modules/notifications/dto/create-notification.dto.ts +45 -0
- package/templates/backend/src/modules/notifications/dto/query-notification.dto.ts +30 -0
- package/templates/backend/src/modules/notifications/dto/update-notification.dto.ts +4 -0
- package/templates/backend/src/modules/notifications/notifications.controller.ts +119 -0
- package/templates/backend/src/modules/notifications/notifications.module.ts +16 -0
- package/templates/backend/src/modules/notifications/notifications.repository.ts +31 -0
- package/templates/backend/src/modules/notifications/notifications.service.ts +64 -0
- package/templates/backend/src/modules/notifications/schemas/notification.schema.ts +38 -0
- package/templates/backend/src/modules/pipeline/dto/create-deal.dto.ts +40 -0
- package/templates/backend/src/modules/pipeline/dto/query-deal.dto.ts +35 -0
- package/templates/backend/src/modules/pipeline/dto/update-deal.dto.ts +4 -0
- package/templates/backend/src/modules/pipeline/pipeline.controller.ts +91 -0
- package/templates/backend/src/modules/pipeline/pipeline.module.ts +18 -0
- package/templates/backend/src/modules/pipeline/pipeline.repository.ts +14 -0
- package/templates/backend/src/modules/pipeline/pipeline.service.ts +64 -0
- package/templates/backend/src/modules/pipeline/schemas/deal.schema.ts +39 -0
- package/templates/backend/src/modules/planner/dto/create-planner-block.dto.ts +66 -0
- package/templates/backend/src/modules/planner/dto/query-planner-block.dto.ts +48 -0
- package/templates/backend/src/modules/planner/dto/update-block-status.dto.ts +10 -0
- package/templates/backend/src/modules/planner/dto/update-planner-block.dto.ts +4 -0
- package/templates/backend/src/modules/planner/planner.controller.ts +124 -0
- package/templates/backend/src/modules/planner/planner.module.ts +16 -0
- package/templates/backend/src/modules/planner/planner.repository.ts +45 -0
- package/templates/backend/src/modules/planner/planner.service.ts +104 -0
- package/templates/backend/src/modules/planner/schemas/planner-block.schema.ts +56 -0
- package/templates/backend/src/modules/task-columns/dto/create-task-column.dto.ts +20 -0
- package/templates/backend/src/modules/task-columns/dto/reorder-columns.dto.ts +9 -0
- package/templates/backend/src/modules/task-columns/dto/update-task-column.dto.ts +4 -0
- package/templates/backend/src/modules/task-columns/schemas/task-column.schema.ts +21 -0
- package/templates/backend/src/modules/task-columns/task-columns.controller.ts +86 -0
- package/templates/backend/src/modules/task-columns/task-columns.module.ts +16 -0
- package/templates/backend/src/modules/task-columns/task-columns.repository.ts +15 -0
- package/templates/backend/src/modules/task-columns/task-columns.service.ts +49 -0
- package/templates/backend/src/modules/tasks/dto/checklist-item.dto.ts +13 -0
- package/templates/backend/src/modules/tasks/dto/create-task.dto.ts +67 -0
- package/templates/backend/src/modules/tasks/dto/label.dto.ts +12 -0
- package/templates/backend/src/modules/tasks/dto/move-task.dto.ts +15 -0
- package/templates/backend/src/modules/tasks/dto/query-task.dto.ts +40 -0
- package/templates/backend/src/modules/tasks/dto/update-task.dto.ts +4 -0
- package/templates/backend/src/modules/tasks/schemas/task.schema.ts +66 -0
- package/templates/backend/src/modules/tasks/tasks.controller.ts +104 -0
- package/templates/backend/src/modules/tasks/tasks.module.ts +18 -0
- package/templates/backend/src/modules/tasks/tasks.repository.ts +14 -0
- package/templates/backend/src/modules/tasks/tasks.service.ts +76 -0
- package/templates/backend/src/modules/users/schemas/user.schema.ts +3 -0
- package/templates/backend/src/modules/users/users.repository.ts +8 -0
- package/templates/backend/src/modules/users/users.service.ts +34 -0
- package/templates/frontend/.env.local.example +1 -1
- package/templates/frontend/README.md +43 -1
- package/templates/frontend/package.json +48 -45
- package/templates/frontend/pnpm-lock.yaml +5096 -5012
- package/templates/frontend/src/app/(auth)/layout.tsx +7 -1
- package/templates/frontend/src/app/(auth)/login/page.tsx +13 -0
- package/templates/frontend/src/app/(auth)/register/page.tsx +13 -0
- package/templates/frontend/src/app/(dashboard)/clients/page.tsx +295 -0
- package/templates/frontend/src/app/(dashboard)/invoices/page.tsx +305 -0
- package/templates/frontend/src/app/(dashboard)/notifications/page.tsx +173 -0
- package/templates/frontend/src/app/(dashboard)/pipeline/page.tsx +244 -0
- package/templates/frontend/src/app/(dashboard)/planner/page.tsx +287 -0
- package/templates/frontend/src/app/(dashboard)/settings/page.tsx +165 -128
- package/templates/frontend/src/app/(dashboard)/tasks/page.tsx +366 -0
- package/templates/frontend/src/app/auth/github/callback/page.tsx +82 -0
- package/templates/frontend/src/app/landing/page.tsx +21 -0
- package/templates/frontend/src/app/page.tsx +5 -5
- package/templates/frontend/src/app/setup/page.tsx +15 -14
- package/templates/frontend/src/components/auth/github-button.tsx +25 -0
- package/templates/frontend/src/components/dashboard/sidebar.tsx +90 -71
- package/templates/frontend/src/components/ui/alert-dialog.tsx +141 -0
- package/templates/frontend/src/components/ui/button.tsx +56 -52
- package/templates/frontend/src/components/ui/popover.tsx +31 -0
- package/templates/frontend/src/components/ui/select.tsx +160 -0
- package/templates/frontend/src/components/ui/sheet.tsx +140 -0
- package/templates/frontend/src/lib/api/auth.ts +7 -0
- package/templates/frontend/src/lib/api/clients.ts +17 -0
- package/templates/frontend/src/lib/api/invoices.ts +18 -0
- package/templates/frontend/src/lib/api/notifications.ts +27 -0
- package/templates/frontend/src/lib/api/pipeline.ts +18 -0
- package/templates/frontend/src/lib/api/planner.ts +26 -0
- package/templates/frontend/src/lib/api/task-columns.ts +17 -0
- package/templates/frontend/src/lib/api/tasks.ts +21 -0
- package/templates/frontend/src/lib/hooks/use-unread-notifications.ts +23 -0
- package/templates/frontend/src/providers/auth-provider.tsx +7 -1
- package/templates/frontend/src/types/clients.ts +38 -0
- package/templates/frontend/src/types/invoices.ts +51 -0
- package/templates/frontend/src/types/notifications.ts +30 -0
- package/templates/frontend/src/types/pipeline.ts +35 -0
- package/templates/frontend/src/types/planner.ts +49 -0
- package/templates/frontend/src/types/tasks.ts +65 -0
|
@@ -1,155 +1,192 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
3
|
+
import { useEffect } from 'react'
|
|
4
|
+
import { useForm } from 'react-hook-form'
|
|
5
|
+
import { Settings, User, Lock, Building2 } from 'lucide-react'
|
|
6
|
+
import apiClient from '@/lib/api/client'
|
|
7
|
+
import { useAuth } from '@/lib/hooks/use-auth'
|
|
7
8
|
import { Button } from '@/components/ui/button'
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
import { Input } from '@/components/ui/input'
|
|
10
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
|
11
|
+
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form'
|
|
12
|
+
import { Separator } from '@/components/ui/separator'
|
|
13
|
+
import { useToast } from '@/hooks/use-toast'
|
|
14
|
+
|
|
15
|
+
interface ProfileForm {
|
|
16
|
+
name: string
|
|
17
|
+
phone: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface WorkspaceForm {
|
|
21
|
+
name: string
|
|
16
22
|
}
|
|
17
23
|
|
|
18
|
-
interface
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
interface PasswordForm {
|
|
25
|
+
currentPassword: string
|
|
26
|
+
newPassword: string
|
|
27
|
+
confirmPassword: string
|
|
21
28
|
}
|
|
22
29
|
|
|
23
30
|
export default function SettingsPage() {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
const { toast } = useToast()
|
|
32
|
+
const { user, refreshUser } = useAuth()
|
|
33
|
+
|
|
34
|
+
const profileForm = useForm<ProfileForm>({
|
|
35
|
+
defaultValues: { name: '', phone: '' },
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const workspaceForm = useForm<WorkspaceForm>({
|
|
39
|
+
defaultValues: { name: '' },
|
|
29
40
|
})
|
|
30
41
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
allowInvites: true,
|
|
42
|
+
const passwordForm = useForm<PasswordForm>({
|
|
43
|
+
defaultValues: { currentPassword: '', newPassword: '', confirmPassword: '' },
|
|
34
44
|
})
|
|
35
45
|
|
|
36
|
-
|
|
37
|
-
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (user) {
|
|
48
|
+
profileForm.reset({ name: user.name ?? '', phone: '' })
|
|
49
|
+
}
|
|
50
|
+
}, [user, profileForm])
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
const fetchWorkspace = async () => {
|
|
54
|
+
try {
|
|
55
|
+
const ws = await apiClient.get('/api/workspaces/me') as { name: string }
|
|
56
|
+
workspaceForm.reset({ name: ws.name ?? '' })
|
|
57
|
+
} catch { /* workspace may not exist yet */ }
|
|
58
|
+
}
|
|
59
|
+
fetchWorkspace()
|
|
60
|
+
}, [workspaceForm])
|
|
61
|
+
|
|
62
|
+
const onProfileSubmit = async (data: ProfileForm) => {
|
|
63
|
+
try {
|
|
64
|
+
await apiClient.patch('/api/users/me', data)
|
|
65
|
+
await refreshUser()
|
|
66
|
+
toast({ title: 'Perfil actualizado' })
|
|
67
|
+
} catch {
|
|
68
|
+
toast({ title: 'Error al actualizar perfil', variant: 'destructive' })
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const onWorkspaceSubmit = async (data: WorkspaceForm) => {
|
|
73
|
+
try {
|
|
74
|
+
await apiClient.patch('/api/workspaces/me', data)
|
|
75
|
+
toast({ title: 'Workspace actualizado' })
|
|
76
|
+
} catch {
|
|
77
|
+
toast({ title: 'Error al actualizar workspace', variant: 'destructive' })
|
|
78
|
+
}
|
|
38
79
|
}
|
|
39
80
|
|
|
40
|
-
|
|
41
|
-
|
|
81
|
+
const onPasswordSubmit = async (data: PasswordForm) => {
|
|
82
|
+
if (data.newPassword !== data.confirmPassword) {
|
|
83
|
+
passwordForm.setError('confirmPassword', { message: 'Las contraseñas no coinciden' })
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
await apiClient.patch('/api/users/me', {
|
|
88
|
+
currentPassword: data.currentPassword,
|
|
89
|
+
newPassword: data.newPassword,
|
|
90
|
+
})
|
|
91
|
+
passwordForm.reset()
|
|
92
|
+
toast({ title: 'Contraseña actualizada' })
|
|
93
|
+
} catch {
|
|
94
|
+
toast({ title: 'Error al cambiar contraseña', variant: 'destructive' })
|
|
95
|
+
}
|
|
42
96
|
}
|
|
43
97
|
|
|
44
98
|
return (
|
|
45
|
-
<div className="max-w-2xl
|
|
46
|
-
<div className="
|
|
47
|
-
<
|
|
48
|
-
<
|
|
99
|
+
<div className="flex flex-col gap-6 p-6 max-w-2xl">
|
|
100
|
+
<div className="flex items-center gap-2">
|
|
101
|
+
<Settings className="size-5" />
|
|
102
|
+
<h1 className="text-xl font-semibold">Configuración</h1>
|
|
49
103
|
</div>
|
|
50
104
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
105
|
+
<Tabs defaultValue="profile">
|
|
106
|
+
<TabsList>
|
|
107
|
+
<TabsTrigger value="profile">
|
|
108
|
+
<User className="mr-2 size-4" />Perfil
|
|
109
|
+
</TabsTrigger>
|
|
110
|
+
<TabsTrigger value="security">
|
|
111
|
+
<Lock className="mr-2 size-4" />Seguridad
|
|
112
|
+
</TabsTrigger>
|
|
113
|
+
<TabsTrigger value="workspace">
|
|
114
|
+
<Building2 className="mr-2 size-4" />Workspace
|
|
115
|
+
</TabsTrigger>
|
|
116
|
+
</TabsList>
|
|
117
|
+
|
|
118
|
+
{/* Perfil */}
|
|
119
|
+
<TabsContent value="profile" className="mt-6">
|
|
120
|
+
<div className="rounded-xl border p-6">
|
|
121
|
+
<h2 className="text-base font-semibold mb-4">Información de perfil</h2>
|
|
122
|
+
<Form {...profileForm}>
|
|
123
|
+
<form onSubmit={profileForm.handleSubmit(onProfileSubmit)} className="flex flex-col gap-4">
|
|
124
|
+
<FormField control={profileForm.control} name="name" rules={{ required: 'El nombre es obligatorio' }} render={({ field }) => (
|
|
125
|
+
<FormItem><FormLabel>Nombre</FormLabel><FormControl><Input {...field} /></FormControl><FormMessage /></FormItem>
|
|
126
|
+
)} />
|
|
127
|
+
<FormItem>
|
|
128
|
+
<FormLabel>Email</FormLabel>
|
|
129
|
+
<Input value={user?.email ?? ''} readOnly className="bg-muted/40 cursor-not-allowed" />
|
|
130
|
+
<p className="text-xs text-muted-foreground">El email no se puede modificar.</p>
|
|
131
|
+
</FormItem>
|
|
132
|
+
<FormField control={profileForm.control} name="phone" render={({ field }) => (
|
|
133
|
+
<FormItem><FormLabel>Teléfono</FormLabel><FormControl><Input {...field} /></FormControl></FormItem>
|
|
134
|
+
)} />
|
|
135
|
+
<div className="flex justify-end">
|
|
136
|
+
<Button type="submit" disabled={profileForm.formState.isSubmitting}>
|
|
137
|
+
{profileForm.formState.isSubmitting ? 'Guardando...' : 'Guardar cambios'}
|
|
138
|
+
</Button>
|
|
83
139
|
</div>
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
/>
|
|
140
|
+
</form>
|
|
141
|
+
</Form>
|
|
115
142
|
</div>
|
|
143
|
+
</TabsContent>
|
|
116
144
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
<div className="
|
|
120
|
-
<
|
|
121
|
-
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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>
|
|
145
|
+
{/* Seguridad */}
|
|
146
|
+
<TabsContent value="security" className="mt-6">
|
|
147
|
+
<div className="rounded-xl border p-6">
|
|
148
|
+
<h2 className="text-base font-semibold mb-4">Cambiar contraseña</h2>
|
|
149
|
+
<Form {...passwordForm}>
|
|
150
|
+
<form onSubmit={passwordForm.handleSubmit(onPasswordSubmit)} className="flex flex-col gap-4">
|
|
151
|
+
<FormField control={passwordForm.control} name="currentPassword" rules={{ required: 'Requerida' }} render={({ field }) => (
|
|
152
|
+
<FormItem><FormLabel>Contraseña actual</FormLabel><FormControl><Input type="password" {...field} /></FormControl><FormMessage /></FormItem>
|
|
153
|
+
)} />
|
|
154
|
+
<Separator />
|
|
155
|
+
<FormField control={passwordForm.control} name="newPassword" rules={{ required: 'Requerida', minLength: { value: 8, message: 'Mínimo 8 caracteres' } }} render={({ field }) => (
|
|
156
|
+
<FormItem><FormLabel>Nueva contraseña</FormLabel><FormControl><Input type="password" {...field} /></FormControl><FormMessage /></FormItem>
|
|
157
|
+
)} />
|
|
158
|
+
<FormField control={passwordForm.control} name="confirmPassword" rules={{ required: 'Requerida' }} render={({ field }) => (
|
|
159
|
+
<FormItem><FormLabel>Confirmar contraseña</FormLabel><FormControl><Input type="password" {...field} /></FormControl><FormMessage /></FormItem>
|
|
160
|
+
)} />
|
|
161
|
+
<div className="flex justify-end">
|
|
162
|
+
<Button type="submit" disabled={passwordForm.formState.isSubmitting}>
|
|
163
|
+
{passwordForm.formState.isSubmitting ? 'Actualizando...' : 'Cambiar contraseña'}
|
|
164
|
+
</Button>
|
|
165
|
+
</div>
|
|
166
|
+
</form>
|
|
167
|
+
</Form>
|
|
148
168
|
</div>
|
|
149
|
-
</
|
|
150
|
-
</Card>
|
|
169
|
+
</TabsContent>
|
|
151
170
|
|
|
152
|
-
|
|
171
|
+
{/* Workspace */}
|
|
172
|
+
<TabsContent value="workspace" className="mt-6">
|
|
173
|
+
<div className="rounded-xl border p-6">
|
|
174
|
+
<h2 className="text-base font-semibold mb-4">Configuración del workspace</h2>
|
|
175
|
+
<Form {...workspaceForm}>
|
|
176
|
+
<form onSubmit={workspaceForm.handleSubmit(onWorkspaceSubmit)} className="flex flex-col gap-4">
|
|
177
|
+
<FormField control={workspaceForm.control} name="name" rules={{ required: 'El nombre es obligatorio' }} render={({ field }) => (
|
|
178
|
+
<FormItem><FormLabel>Nombre del workspace</FormLabel><FormControl><Input {...field} /></FormControl><FormMessage /></FormItem>
|
|
179
|
+
)} />
|
|
180
|
+
<div className="flex justify-end">
|
|
181
|
+
<Button type="submit" disabled={workspaceForm.formState.isSubmitting}>
|
|
182
|
+
{workspaceForm.formState.isSubmitting ? 'Guardando...' : 'Guardar cambios'}
|
|
183
|
+
</Button>
|
|
184
|
+
</div>
|
|
185
|
+
</form>
|
|
186
|
+
</Form>
|
|
187
|
+
</div>
|
|
188
|
+
</TabsContent>
|
|
189
|
+
</Tabs>
|
|
153
190
|
</div>
|
|
154
191
|
)
|
|
155
192
|
}
|