@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,256 @@
1
+ import { useState } from 'react';
2
+ import { Link } from 'react-router-dom';
3
+ import { Badge, Button, Input, ScrollArea, cn } from '@imansi/ui';
4
+ import { MarketingLayout } from '../layouts/MarketingLayout.jsx';
5
+
6
+ export function DocsPage({
7
+ brandName = 'Imansi',
8
+ brandLogo,
9
+ navLinks = [],
10
+ headerCTA,
11
+ footerColumns = [],
12
+
13
+ sidebarTitle = 'Documentación',
14
+ sidebarSearchPlaceholder = 'Buscar...',
15
+ sections = [],
16
+ // sections: [{ title, items: [{ label, href, badge }] }]
17
+
18
+ breadcrumb = [],
19
+ title = 'Introducción',
20
+ description = '',
21
+ badges = [],
22
+
23
+ tocItems = [],
24
+ // tocItems: [{ label, href, level? }]
25
+
26
+ prevPage,
27
+ nextPage,
28
+ // { label, href }
29
+
30
+ children,
31
+ onEditClick,
32
+ }) {
33
+ const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
34
+ const [search, setSearch] = useState('');
35
+
36
+ const filteredSections = search
37
+ ? sections
38
+ .map((section) => ({
39
+ ...section,
40
+ items: section.items.filter((item) =>
41
+ item.label.toLowerCase().includes(search.toLowerCase())
42
+ ),
43
+ }))
44
+ .filter((section) => section.items.length > 0)
45
+ : sections;
46
+
47
+ const Sidebar = (
48
+ <div className="space-y-6">
49
+ <div className="space-y-3">
50
+ <p className="text-caption uppercase tracking-wider font-semibold text-muted-foreground">
51
+ {sidebarTitle}
52
+ </p>
53
+ <Input
54
+ type="search"
55
+ placeholder={sidebarSearchPlaceholder}
56
+ value={search}
57
+ onChange={(e) => setSearch(e.target.value)}
58
+ className="h-9"
59
+ />
60
+ </div>
61
+
62
+ <nav className="space-y-6">
63
+ {filteredSections.map((section, i) => (
64
+ <div key={i} className="space-y-2">
65
+ <p className="text-caption uppercase tracking-wider font-semibold text-foreground">
66
+ {section.title}
67
+ </p>
68
+ <ul className="space-y-1">
69
+ {section.items.map((item, j) => {
70
+ const isActive = item.href === window?.location?.pathname;
71
+ return (
72
+ <li key={j}>
73
+ <Link
74
+ to={item.href}
75
+ onClick={() => setMobileSidebarOpen(false)}
76
+ className={cn(
77
+ 'flex items-center justify-between gap-2 rounded-md px-3 py-1.5 text-small transition-colors',
78
+ isActive
79
+ ? 'bg-primary/10 text-primary font-medium'
80
+ : 'text-muted-foreground hover:bg-muted hover:text-foreground'
81
+ )}
82
+ >
83
+ <span className="truncate">{item.label}</span>
84
+ {item.badge && (
85
+ <Badge variant="outline" className="text-[10px]">
86
+ {item.badge}
87
+ </Badge>
88
+ )}
89
+ </Link>
90
+ </li>
91
+ );
92
+ })}
93
+ </ul>
94
+ </div>
95
+ ))}
96
+ </nav>
97
+ </div>
98
+ );
99
+
100
+ return (
101
+ <MarketingLayout
102
+ brandName={brandName}
103
+ brandLogo={brandLogo}
104
+ navLinks={navLinks}
105
+ headerCTA={headerCTA}
106
+ footerColumns={footerColumns}
107
+ >
108
+ <div className="imansi-container py-8 lg:py-12">
109
+ <div className="grid gap-10 lg:grid-cols-[260px_1fr_200px]">
110
+ {/* Sidebar desktop */}
111
+ <aside className="hidden lg:block">
112
+ <div className="sticky top-24">
113
+ <ScrollArea className="h-[calc(100vh-8rem)] pr-2">
114
+ {Sidebar}
115
+ </ScrollArea>
116
+ </div>
117
+ </aside>
118
+
119
+ {/* Contenido principal */}
120
+ <main className="min-w-0">
121
+ {/* Mobile: abrir sidebar */}
122
+ <div className="lg:hidden mb-6">
123
+ <Button
124
+ variant="outline"
125
+ size="sm"
126
+ onClick={() => setMobileSidebarOpen((v) => !v)}
127
+ >
128
+ <svg width="14" height="14" viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" className="mr-2">
129
+ <line x1="3" y1="6" x2="17" y2="6" />
130
+ <line x1="3" y1="10" x2="17" y2="10" />
131
+ <line x1="3" y1="14" x2="17" y2="14" />
132
+ </svg>
133
+ {mobileSidebarOpen ? 'Cerrar menú' : 'Abrir menú'}
134
+ </Button>
135
+ </div>
136
+
137
+ {mobileSidebarOpen && (
138
+ <div className="lg:hidden mb-8 rounded-lg border border-border bg-card p-5">
139
+ {Sidebar}
140
+ </div>
141
+ )}
142
+
143
+ {/* Breadcrumb */}
144
+ {breadcrumb.length > 0 && (
145
+ <nav className="flex flex-wrap items-center gap-1.5 text-caption text-muted-foreground mb-6">
146
+ {breadcrumb.map((crumb, i) => (
147
+ <span key={i} className="flex items-center gap-1.5">
148
+ {i > 0 && <span className="opacity-50">/</span>}
149
+ {crumb.href ? (
150
+ <Link
151
+ to={crumb.href}
152
+ className="hover:text-foreground transition-colors"
153
+ >
154
+ {crumb.label}
155
+ </Link>
156
+ ) : (
157
+ <span className="text-foreground">{crumb.label}</span>
158
+ )}
159
+ </span>
160
+ ))}
161
+ </nav>
162
+ )}
163
+
164
+ {/* Título */}
165
+ <header className="space-y-4 pb-8 border-b border-border">
166
+ <div className="flex flex-wrap items-center gap-2">
167
+ <h1 className="text-h1 font-bold tracking-tight">{title}</h1>
168
+ {badges.map((b, i) => (
169
+ <Badge key={i} variant="outline">{b}</Badge>
170
+ ))}
171
+ </div>
172
+ {description && (
173
+ <p className="text-body text-muted-foreground leading-relaxed">
174
+ {description}
175
+ </p>
176
+ )}
177
+ {onEditClick && (
178
+ <button
179
+ type="button"
180
+ onClick={onEditClick}
181
+ className="text-caption text-muted-foreground hover:text-foreground transition-colors inline-flex items-center gap-1.5"
182
+ >
183
+ <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
184
+ <path d="M8.5 1.5l2 2L4 10H2v-2z" />
185
+ </svg>
186
+ Editar esta página
187
+ </button>
188
+ )}
189
+ </header>
190
+
191
+ {/* Contenido */}
192
+ <div className="prose-imansi py-8 space-y-6">{children}</div>
193
+
194
+ {/* Navegación prev/next */}
195
+ {(prevPage || nextPage) && (
196
+ <nav className="grid gap-4 sm:grid-cols-2 pt-8 border-t border-border">
197
+ {prevPage ? (
198
+ <Link
199
+ to={prevPage.href}
200
+ className="group rounded-lg border border-border bg-card p-4 hover:border-primary/40 transition-colors"
201
+ >
202
+ <p className="text-caption text-muted-foreground mb-1">
203
+ ← Anterior
204
+ </p>
205
+ <p className="text-small font-medium group-hover:text-primary transition-colors">
206
+ {prevPage.label}
207
+ </p>
208
+ </Link>
209
+ ) : <div />}
210
+
211
+ {nextPage && (
212
+ <Link
213
+ to={nextPage.href}
214
+ className="group rounded-lg border border-border bg-card p-4 text-right hover:border-primary/40 transition-colors"
215
+ >
216
+ <p className="text-caption text-muted-foreground mb-1">
217
+ Siguiente →
218
+ </p>
219
+ <p className="text-small font-medium group-hover:text-primary transition-colors">
220
+ {nextPage.label}
221
+ </p>
222
+ </Link>
223
+ )}
224
+ </nav>
225
+ )}
226
+ </main>
227
+
228
+ {/* TOC desktop */}
229
+ <aside className="hidden xl:block">
230
+ {tocItems.length > 0 && (
231
+ <div className="sticky top-24 space-y-3">
232
+ <p className="text-caption uppercase tracking-wider font-semibold text-muted-foreground">
233
+ En esta página
234
+ </p>
235
+ <nav className="space-y-1.5 border-l border-border">
236
+ {tocItems.map((item, i) => (
237
+ <a
238
+ key={i}
239
+ href={item.href}
240
+ className={cn(
241
+ 'block pl-3 text-caption text-muted-foreground hover:text-foreground transition-colors border-l-2 border-transparent hover:border-primary -ml-px',
242
+ item.level === 3 && 'pl-6'
243
+ )}
244
+ >
245
+ {item.label}
246
+ </a>
247
+ ))}
248
+ </nav>
249
+ </div>
250
+ )}
251
+ </aside>
252
+ </div>
253
+ </div>
254
+ </MarketingLayout>
255
+ );
256
+ }