@imansi/templates 0.1.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 (36) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +557 -0
  3. package/package.json +35 -0
  4. package/src/auth/ForgotPasswordPage.jsx +176 -0
  5. package/src/auth/LoginPage.jsx +265 -0
  6. package/src/auth/RegisterPage.jsx +302 -0
  7. package/src/auth/ResetPasswordPage.jsx +158 -0
  8. package/src/auth/Verify2FAPage.jsx +175 -0
  9. package/src/dashboard/BillingPage.jsx +345 -0
  10. package/src/dashboard/DashboardHome.jsx +246 -0
  11. package/src/dashboard/NotificationsPage.jsx +230 -0
  12. package/src/dashboard/ProfilePage.jsx +272 -0
  13. package/src/dashboard/SettingsPage.jsx +415 -0
  14. package/src/dashboard/UsersTablePage.jsx +317 -0
  15. package/src/extra/CareersPage.jsx +166 -0
  16. package/src/extra/ChangelogPage.jsx +127 -0
  17. package/src/extra/CheckoutPage.jsx +383 -0
  18. package/src/extra/ComingSoonPage.jsx +147 -0
  19. package/src/extra/ErrorPage.jsx +91 -0
  20. package/src/extra/InvoicePage.jsx +241 -0
  21. package/src/extra/LegalPage.jsx +115 -0
  22. package/src/extra/MaintenancePage.jsx +172 -0
  23. package/src/extra/OnboardingPage.jsx +197 -0
  24. package/src/extra/StatusPage.jsx +181 -0
  25. package/src/index.js +53 -0
  26. package/src/layouts/AuthLayout.jsx +104 -0
  27. package/src/layouts/DashboardLayout.jsx +249 -0
  28. package/src/layouts/MarketingLayout.jsx +184 -0
  29. package/src/marketing/AboutPage.jsx +189 -0
  30. package/src/marketing/BlogPage.jsx +182 -0
  31. package/src/marketing/BlogPostPage.jsx +192 -0
  32. package/src/marketing/ContactPage.jsx +546 -0
  33. package/src/marketing/DocsPage.jsx +256 -0
  34. package/src/marketing/HomePage.jsx +702 -0
  35. package/src/marketing/LandingPage.jsx +755 -0
  36. package/src/marketing/PricingPage.jsx +205 -0
@@ -0,0 +1,415 @@
1
+ import { useState } from 'react';
2
+ import {
3
+ Alert,
4
+ Button,
5
+ Card,
6
+ CardContent,
7
+ CardDescription,
8
+ CardHeader,
9
+ CardTitle,
10
+ Input,
11
+ Label,
12
+ Separator,
13
+ Switch,
14
+ Tabs,
15
+ TabsContent,
16
+ TabsList,
17
+ TabsTrigger,
18
+ Textarea,
19
+ } from '@imansi/ui';
20
+ import { DashboardLayout } from '../layouts/DashboardLayout.jsx';
21
+
22
+ export function SettingsPage({
23
+ brandName,
24
+ brandLogo,
25
+ navSections,
26
+ user,
27
+ userMenuItems,
28
+ notificationsCount,
29
+ onLogout,
30
+
31
+ title = 'Configuración',
32
+ subtitle = 'Administrá tu cuenta y preferencias.',
33
+
34
+ defaultTab = 'profile',
35
+
36
+ onSaveProfile,
37
+ onSaveAccount,
38
+ onSaveNotifications,
39
+ onSaveSecurity,
40
+ onChangePassword,
41
+ onDeleteAccount,
42
+ }) {
43
+ const [tab, setTab] = useState(defaultTab);
44
+ const [saving, setSaving] = useState(false);
45
+
46
+ const [profile, setProfile] = useState({
47
+ name: user?.name || '',
48
+ email: user?.email || '',
49
+ bio: '',
50
+ company: '',
51
+ website: '',
52
+ });
53
+
54
+ const [account, setAccount] = useState({
55
+ language: 'es',
56
+ timezone: 'America/Argentina/Buenos_Aires',
57
+ dateFormat: 'dd/mm/yyyy',
58
+ });
59
+
60
+ const [notifications, setNotifications] = useState({
61
+ emailProduct: true,
62
+ emailSecurity: true,
63
+ emailMarketing: false,
64
+ pushProduct: false,
65
+ pushSecurity: true,
66
+ });
67
+
68
+ async function handleSave(handler, data) {
69
+ if (!handler) return;
70
+ setSaving(true);
71
+ await handler(data);
72
+ setSaving(false);
73
+ }
74
+
75
+ return (
76
+ <DashboardLayout
77
+ brandName={brandName}
78
+ brandLogo={brandLogo}
79
+ navSections={navSections}
80
+ user={user}
81
+ userMenuItems={userMenuItems}
82
+ notificationsCount={notificationsCount}
83
+ onLogout={onLogout}
84
+ title={title}
85
+ >
86
+ <div className="space-y-6 max-w-4xl mx-auto">
87
+ {/* Header */}
88
+ <div className="space-y-1">
89
+ <h1 className="text-h2 font-bold tracking-tight">{title}</h1>
90
+ <p className="text-small text-muted-foreground">{subtitle}</p>
91
+ </div>
92
+
93
+ {/* Tabs */}
94
+ <Tabs value={tab} onValueChange={setTab} className="space-y-6">
95
+ <TabsList>
96
+ <TabsTrigger value="profile">Perfil</TabsTrigger>
97
+ <TabsTrigger value="account">Cuenta</TabsTrigger>
98
+ <TabsTrigger value="notifications">Notificaciones</TabsTrigger>
99
+ <TabsTrigger value="security">Seguridad</TabsTrigger>
100
+ </TabsList>
101
+
102
+ {/* ============ PERFIL ============ */}
103
+ <TabsContent value="profile">
104
+ <Card>
105
+ <CardHeader>
106
+ <CardTitle>Información personal</CardTitle>
107
+ <CardDescription>
108
+ Esta información se mostrará en tu perfil público.
109
+ </CardDescription>
110
+ </CardHeader>
111
+ <CardContent className="space-y-6">
112
+ <div className="grid gap-5 sm:grid-cols-2">
113
+ <div className="space-y-1.5">
114
+ <Label htmlFor="settings-name">Nombre completo</Label>
115
+ <Input
116
+ id="settings-name"
117
+ value={profile.name}
118
+ onChange={(e) =>
119
+ setProfile({ ...profile, name: e.target.value })
120
+ }
121
+ />
122
+ </div>
123
+ <div className="space-y-1.5">
124
+ <Label htmlFor="settings-email">Correo electrónico</Label>
125
+ <Input
126
+ id="settings-email"
127
+ type="email"
128
+ value={profile.email}
129
+ onChange={(e) =>
130
+ setProfile({ ...profile, email: e.target.value })
131
+ }
132
+ />
133
+ </div>
134
+ </div>
135
+
136
+ <div className="space-y-1.5">
137
+ <Label htmlFor="settings-bio">Biografía</Label>
138
+ <Textarea
139
+ id="settings-bio"
140
+ rows={3}
141
+ placeholder="Contanos algo sobre vos..."
142
+ value={profile.bio}
143
+ onChange={(e) =>
144
+ setProfile({ ...profile, bio: e.target.value })
145
+ }
146
+ />
147
+ </div>
148
+
149
+ <div className="grid gap-5 sm:grid-cols-2">
150
+ <div className="space-y-1.5">
151
+ <Label htmlFor="settings-company">Empresa</Label>
152
+ <Input
153
+ id="settings-company"
154
+ value={profile.company}
155
+ onChange={(e) =>
156
+ setProfile({ ...profile, company: e.target.value })
157
+ }
158
+ />
159
+ </div>
160
+ <div className="space-y-1.5">
161
+ <Label htmlFor="settings-website">Sitio web</Label>
162
+ <Input
163
+ id="settings-website"
164
+ type="url"
165
+ placeholder="https://..."
166
+ value={profile.website}
167
+ onChange={(e) =>
168
+ setProfile({ ...profile, website: e.target.value })
169
+ }
170
+ />
171
+ </div>
172
+ </div>
173
+
174
+ <Separator />
175
+
176
+ <div className="flex justify-end gap-2">
177
+ <Button variant="ghost">Cancelar</Button>
178
+ <Button
179
+ disabled={saving}
180
+ onClick={() => handleSave(onSaveProfile, profile)}
181
+ >
182
+ {saving ? 'Guardando...' : 'Guardar cambios'}
183
+ </Button>
184
+ </div>
185
+ </CardContent>
186
+ </Card>
187
+ </TabsContent>
188
+
189
+ {/* ============ CUENTA ============ */}
190
+ <TabsContent value="account">
191
+ <Card>
192
+ <CardHeader>
193
+ <CardTitle>Preferencias</CardTitle>
194
+ <CardDescription>
195
+ Configurá el idioma, la zona horaria y los formatos.
196
+ </CardDescription>
197
+ </CardHeader>
198
+ <CardContent className="space-y-5">
199
+ <div className="grid gap-5 sm:grid-cols-2">
200
+ <div className="space-y-1.5">
201
+ <Label htmlFor="settings-language">Idioma</Label>
202
+ <select
203
+ id="settings-language"
204
+ value={account.language}
205
+ onChange={(e) =>
206
+ setAccount({ ...account, language: e.target.value })
207
+ }
208
+ className="flex h-10 w-full rounded-md border border-border bg-input px-3 py-2 text-body text-foreground focus:outline-none focus:ring-2 focus:ring-ring"
209
+ >
210
+ <option value="es">Español</option>
211
+ <option value="en">English</option>
212
+ <option value="pt">Português</option>
213
+ </select>
214
+ </div>
215
+ <div className="space-y-1.5">
216
+ <Label htmlFor="settings-timezone">Zona horaria</Label>
217
+ <select
218
+ id="settings-timezone"
219
+ value={account.timezone}
220
+ onChange={(e) =>
221
+ setAccount({ ...account, timezone: e.target.value })
222
+ }
223
+ className="flex h-10 w-full rounded-md border border-border bg-input px-3 py-2 text-body text-foreground focus:outline-none focus:ring-2 focus:ring-ring"
224
+ >
225
+ <option value="America/Argentina/Buenos_Aires">
226
+ Buenos Aires (GMT-3)
227
+ </option>
228
+ <option value="America/Mexico_City">
229
+ Ciudad de México (GMT-6)
230
+ </option>
231
+ <option value="Europe/Madrid">Madrid (GMT+1)</option>
232
+ <option value="UTC">UTC</option>
233
+ </select>
234
+ </div>
235
+ </div>
236
+
237
+ <Separator />
238
+
239
+ <div className="flex justify-end gap-2">
240
+ <Button variant="ghost">Cancelar</Button>
241
+ <Button
242
+ disabled={saving}
243
+ onClick={() => handleSave(onSaveAccount, account)}
244
+ >
245
+ {saving ? 'Guardando...' : 'Guardar cambios'}
246
+ </Button>
247
+ </div>
248
+ </CardContent>
249
+ </Card>
250
+ </TabsContent>
251
+
252
+ {/* ============ NOTIFICACIONES ============ */}
253
+ <TabsContent value="notifications">
254
+ <Card>
255
+ <CardHeader>
256
+ <CardTitle>Notificaciones</CardTitle>
257
+ <CardDescription>
258
+ Elegí qué notificaciones querés recibir.
259
+ </CardDescription>
260
+ </CardHeader>
261
+ <CardContent className="space-y-6">
262
+ <div className="space-y-4">
263
+ <h4 className="text-small font-semibold uppercase tracking-wider text-muted-foreground">
264
+ Por correo
265
+ </h4>
266
+ {[
267
+ {
268
+ key: 'emailProduct',
269
+ label: 'Novedades del producto',
270
+ desc: 'Lanzamientos y nuevas funciones.',
271
+ },
272
+ {
273
+ key: 'emailSecurity',
274
+ label: 'Alertas de seguridad',
275
+ desc: 'Inicios de sesión y cambios importantes.',
276
+ },
277
+ {
278
+ key: 'emailMarketing',
279
+ label: 'Promociones',
280
+ desc: 'Descuentos y ofertas especiales.',
281
+ },
282
+ ].map((item) => (
283
+ <div
284
+ key={item.key}
285
+ className="flex items-center justify-between gap-4 py-1"
286
+ >
287
+ <div className="space-y-0.5">
288
+ <Label className="cursor-pointer">{item.label}</Label>
289
+ <p className="text-caption text-muted-foreground">
290
+ {item.desc}
291
+ </p>
292
+ </div>
293
+ <Switch
294
+ checked={notifications[item.key]}
295
+ onCheckedChange={(v) =>
296
+ setNotifications({ ...notifications, [item.key]: v })
297
+ }
298
+ />
299
+ </div>
300
+ ))}
301
+ </div>
302
+
303
+ <Separator />
304
+
305
+ <div className="space-y-4">
306
+ <h4 className="text-small font-semibold uppercase tracking-wider text-muted-foreground">
307
+ Push
308
+ </h4>
309
+ {[
310
+ {
311
+ key: 'pushProduct',
312
+ label: 'Novedades del producto',
313
+ desc: 'Notificaciones del navegador.',
314
+ },
315
+ {
316
+ key: 'pushSecurity',
317
+ label: 'Alertas de seguridad',
318
+ desc: 'Recomendado mantener activo.',
319
+ },
320
+ ].map((item) => (
321
+ <div
322
+ key={item.key}
323
+ className="flex items-center justify-between gap-4 py-1"
324
+ >
325
+ <div className="space-y-0.5">
326
+ <Label className="cursor-pointer">{item.label}</Label>
327
+ <p className="text-caption text-muted-foreground">
328
+ {item.desc}
329
+ </p>
330
+ </div>
331
+ <Switch
332
+ checked={notifications[item.key]}
333
+ onCheckedChange={(v) =>
334
+ setNotifications({ ...notifications, [item.key]: v })
335
+ }
336
+ />
337
+ </div>
338
+ ))}
339
+ </div>
340
+
341
+ <Separator />
342
+
343
+ <div className="flex justify-end gap-2">
344
+ <Button
345
+ disabled={saving}
346
+ onClick={() => handleSave(onSaveNotifications, notifications)}
347
+ >
348
+ {saving ? 'Guardando...' : 'Guardar cambios'}
349
+ </Button>
350
+ </div>
351
+ </CardContent>
352
+ </Card>
353
+ </TabsContent>
354
+
355
+ {/* ============ SEGURIDAD ============ */}
356
+ <TabsContent value="security" className="space-y-6">
357
+ <Card>
358
+ <CardHeader>
359
+ <CardTitle>Cambiar contraseña</CardTitle>
360
+ <CardDescription>
361
+ Te recomendamos usar una contraseña fuerte y única.
362
+ </CardDescription>
363
+ </CardHeader>
364
+ <CardContent className="space-y-5">
365
+ <div className="space-y-1.5">
366
+ <Label htmlFor="current-password">Contraseña actual</Label>
367
+ <Input id="current-password" type="password" />
368
+ </div>
369
+ <div className="space-y-1.5">
370
+ <Label htmlFor="new-password">Nueva contraseña</Label>
371
+ <Input id="new-password" type="password" />
372
+ </div>
373
+ <div className="space-y-1.5">
374
+ <Label htmlFor="confirm-password">Confirmar contraseña</Label>
375
+ <Input id="confirm-password" type="password" />
376
+ </div>
377
+ <div className="flex justify-end">
378
+ <Button onClick={onChangePassword}>
379
+ Actualizar contraseña
380
+ </Button>
381
+ </div>
382
+ </CardContent>
383
+ </Card>
384
+
385
+ <Card className="border-destructive/40">
386
+ <CardHeader>
387
+ <CardTitle className="text-destructive">
388
+ Zona de peligro
389
+ </CardTitle>
390
+ <CardDescription>
391
+ Acciones irreversibles sobre tu cuenta.
392
+ </CardDescription>
393
+ </CardHeader>
394
+ <CardContent>
395
+ <div className="flex flex-wrap items-center justify-between gap-4">
396
+ <div className="space-y-1">
397
+ <p className="text-small font-medium">
398
+ Eliminar cuenta permanentemente
399
+ </p>
400
+ <p className="text-caption text-muted-foreground">
401
+ Se eliminarán todos tus datos. Esta acción no se puede deshacer.
402
+ </p>
403
+ </div>
404
+ <Button variant="destructive" onClick={onDeleteAccount}>
405
+ Eliminar cuenta
406
+ </Button>
407
+ </div>
408
+ </CardContent>
409
+ </Card>
410
+ </TabsContent>
411
+ </Tabs>
412
+ </div>
413
+ </DashboardLayout>
414
+ );
415
+ }