@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.
- package/LICENSE +21 -0
- package/README.md +557 -0
- package/package.json +35 -0
- package/src/auth/ForgotPasswordPage.jsx +176 -0
- package/src/auth/LoginPage.jsx +265 -0
- package/src/auth/RegisterPage.jsx +302 -0
- package/src/auth/ResetPasswordPage.jsx +158 -0
- package/src/auth/Verify2FAPage.jsx +175 -0
- package/src/dashboard/BillingPage.jsx +345 -0
- package/src/dashboard/DashboardHome.jsx +246 -0
- package/src/dashboard/NotificationsPage.jsx +230 -0
- package/src/dashboard/ProfilePage.jsx +272 -0
- package/src/dashboard/SettingsPage.jsx +415 -0
- package/src/dashboard/UsersTablePage.jsx +317 -0
- package/src/extra/CareersPage.jsx +166 -0
- package/src/extra/ChangelogPage.jsx +127 -0
- package/src/extra/CheckoutPage.jsx +383 -0
- package/src/extra/ComingSoonPage.jsx +147 -0
- package/src/extra/ErrorPage.jsx +91 -0
- package/src/extra/InvoicePage.jsx +241 -0
- package/src/extra/LegalPage.jsx +115 -0
- package/src/extra/MaintenancePage.jsx +172 -0
- package/src/extra/OnboardingPage.jsx +197 -0
- package/src/extra/StatusPage.jsx +181 -0
- package/src/index.js +53 -0
- package/src/layouts/AuthLayout.jsx +104 -0
- package/src/layouts/DashboardLayout.jsx +249 -0
- package/src/layouts/MarketingLayout.jsx +184 -0
- package/src/marketing/AboutPage.jsx +189 -0
- package/src/marketing/BlogPage.jsx +182 -0
- package/src/marketing/BlogPostPage.jsx +192 -0
- package/src/marketing/ContactPage.jsx +546 -0
- package/src/marketing/DocsPage.jsx +256 -0
- package/src/marketing/HomePage.jsx +702 -0
- package/src/marketing/LandingPage.jsx +755 -0
- 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
|
+
}
|