@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,205 @@
|
|
|
1
|
+
import { Link } from 'react-router-dom';
|
|
2
|
+
import { Badge, Button, cn } from '@imansi/ui';
|
|
3
|
+
import { MarketingLayout } from '../layouts/MarketingLayout.jsx';
|
|
4
|
+
|
|
5
|
+
export function PricingPage({
|
|
6
|
+
brandName = 'Imansi',
|
|
7
|
+
brandLogo,
|
|
8
|
+
navLinks = [],
|
|
9
|
+
headerCTA,
|
|
10
|
+
headerSecondaryCTA,
|
|
11
|
+
footerColumns = [],
|
|
12
|
+
|
|
13
|
+
title = 'Precios simples, sin sorpresas',
|
|
14
|
+
subtitle = 'Elegí el plan que mejor se adapta a tu equipo. Podés cambiar en cualquier momento.',
|
|
15
|
+
|
|
16
|
+
plans = [],
|
|
17
|
+
showComparison = false,
|
|
18
|
+
comparisonFeatures = [],
|
|
19
|
+
|
|
20
|
+
faqTitle = 'Preguntas frecuentes',
|
|
21
|
+
faq = [],
|
|
22
|
+
|
|
23
|
+
ctaTitle = '¿Listo para empezar?',
|
|
24
|
+
ctaSubtitle = 'Empezá gratis. Sin tarjeta de crédito.',
|
|
25
|
+
ctaPrimary = { label: 'Crear cuenta', href: '/registro' },
|
|
26
|
+
}) {
|
|
27
|
+
return (
|
|
28
|
+
<MarketingLayout
|
|
29
|
+
brandName={brandName}
|
|
30
|
+
brandLogo={brandLogo}
|
|
31
|
+
navLinks={navLinks}
|
|
32
|
+
headerCTA={headerCTA}
|
|
33
|
+
headerSecondaryCTA={headerSecondaryCTA}
|
|
34
|
+
footerColumns={footerColumns}
|
|
35
|
+
>
|
|
36
|
+
{/* Header */}
|
|
37
|
+
<section className="imansi-container py-16 sm:py-20 text-center max-w-2xl mx-auto space-y-4">
|
|
38
|
+
<h1 className="text-display font-bold tracking-tight">{title}</h1>
|
|
39
|
+
<p className="text-body text-muted-foreground">{subtitle}</p>
|
|
40
|
+
</section>
|
|
41
|
+
|
|
42
|
+
{/* Plans */}
|
|
43
|
+
<section className="imansi-container pb-16">
|
|
44
|
+
<div className={cn(
|
|
45
|
+
'grid gap-6',
|
|
46
|
+
plans.length === 3 ? 'md:grid-cols-3' : 'md:grid-cols-2 lg:grid-cols-4'
|
|
47
|
+
)}>
|
|
48
|
+
{plans.map((plan, i) => (
|
|
49
|
+
<div
|
|
50
|
+
key={i}
|
|
51
|
+
className={cn(
|
|
52
|
+
'relative rounded-xl border bg-card p-7 flex flex-col transition-shadow hover:shadow-lg',
|
|
53
|
+
plan.popular
|
|
54
|
+
? 'border-primary shadow-lg ring-1 ring-primary'
|
|
55
|
+
: 'border-border'
|
|
56
|
+
)}
|
|
57
|
+
>
|
|
58
|
+
{plan.popular && (
|
|
59
|
+
<div className="absolute -top-3 left-1/2 -translate-x-1/2">
|
|
60
|
+
<Badge variant="primary" className="shadow-md">
|
|
61
|
+
Más popular
|
|
62
|
+
</Badge>
|
|
63
|
+
</div>
|
|
64
|
+
)}
|
|
65
|
+
|
|
66
|
+
<div className="space-y-2">
|
|
67
|
+
<h3 className="text-h4 font-semibold">{plan.name}</h3>
|
|
68
|
+
<p className="text-small text-muted-foreground min-h-[2.5rem]">
|
|
69
|
+
{plan.description}
|
|
70
|
+
</p>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<div className="my-6">
|
|
74
|
+
<div className="flex items-baseline gap-1">
|
|
75
|
+
<span className="text-display font-bold tracking-tight">
|
|
76
|
+
{plan.price}
|
|
77
|
+
</span>
|
|
78
|
+
{plan.period && (
|
|
79
|
+
<span className="text-small text-muted-foreground">
|
|
80
|
+
/{plan.period}
|
|
81
|
+
</span>
|
|
82
|
+
)}
|
|
83
|
+
</div>
|
|
84
|
+
{plan.note && (
|
|
85
|
+
<p className="text-caption text-muted-foreground mt-1">
|
|
86
|
+
{plan.note}
|
|
87
|
+
</p>
|
|
88
|
+
)}
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<ul className="space-y-3 mb-8 flex-1">
|
|
92
|
+
{plan.features.map((feature, j) => (
|
|
93
|
+
<li key={j} className="flex items-start gap-2.5 text-small">
|
|
94
|
+
<span className="mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full bg-success/10 text-success">
|
|
95
|
+
<svg width="9" height="9" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
|
|
96
|
+
<polyline points="2,6 5,9 10,3" />
|
|
97
|
+
</svg>
|
|
98
|
+
</span>
|
|
99
|
+
<span className="text-foreground">{feature}</span>
|
|
100
|
+
</li>
|
|
101
|
+
))}
|
|
102
|
+
</ul>
|
|
103
|
+
|
|
104
|
+
<Link to={plan.href || '/registro'} className="mt-auto">
|
|
105
|
+
<Button
|
|
106
|
+
fullWidth
|
|
107
|
+
variant={plan.popular ? 'primary' : 'outline'}
|
|
108
|
+
size="lg"
|
|
109
|
+
>
|
|
110
|
+
{plan.cta || 'Elegir plan'}
|
|
111
|
+
</Button>
|
|
112
|
+
</Link>
|
|
113
|
+
</div>
|
|
114
|
+
))}
|
|
115
|
+
</div>
|
|
116
|
+
</section>
|
|
117
|
+
|
|
118
|
+
{/* Comparison table */}
|
|
119
|
+
{showComparison && comparisonFeatures.length > 0 && (
|
|
120
|
+
<section className="imansi-container pb-20">
|
|
121
|
+
<div className="rounded-xl border border-border overflow-hidden">
|
|
122
|
+
<table className="w-full text-small">
|
|
123
|
+
<thead className="bg-muted/50">
|
|
124
|
+
<tr>
|
|
125
|
+
<th className="text-left p-4 font-semibold">Característica</th>
|
|
126
|
+
{plans.map((p) => (
|
|
127
|
+
<th key={p.name} className="text-center p-4 font-semibold">
|
|
128
|
+
{p.name}
|
|
129
|
+
</th>
|
|
130
|
+
))}
|
|
131
|
+
</tr>
|
|
132
|
+
</thead>
|
|
133
|
+
<tbody>
|
|
134
|
+
{comparisonFeatures.map((feature, i) => (
|
|
135
|
+
<tr key={i} className="border-t border-border">
|
|
136
|
+
<td className="p-4 text-foreground">{feature.label}</td>
|
|
137
|
+
{plans.map((p) => {
|
|
138
|
+
const val = feature.values?.[p.name];
|
|
139
|
+
return (
|
|
140
|
+
<td key={p.name} className="text-center p-4">
|
|
141
|
+
{val === true ? (
|
|
142
|
+
<span className="text-success text-base">✓</span>
|
|
143
|
+
) : val === false ? (
|
|
144
|
+
<span className="text-muted-foreground">—</span>
|
|
145
|
+
) : (
|
|
146
|
+
<span className="text-foreground">{val}</span>
|
|
147
|
+
)}
|
|
148
|
+
</td>
|
|
149
|
+
);
|
|
150
|
+
})}
|
|
151
|
+
</tr>
|
|
152
|
+
))}
|
|
153
|
+
</tbody>
|
|
154
|
+
</table>
|
|
155
|
+
</div>
|
|
156
|
+
</section>
|
|
157
|
+
)}
|
|
158
|
+
|
|
159
|
+
{/* FAQ */}
|
|
160
|
+
{faq.length > 0 && (
|
|
161
|
+
<section className="imansi-container pb-20 max-w-3xl mx-auto">
|
|
162
|
+
<h2 className="text-h1 font-bold tracking-tight text-center mb-10">
|
|
163
|
+
{faqTitle}
|
|
164
|
+
</h2>
|
|
165
|
+
<div className="space-y-3">
|
|
166
|
+
{faq.map((item, i) => (
|
|
167
|
+
<details
|
|
168
|
+
key={i}
|
|
169
|
+
className="group rounded-lg border border-border bg-card p-5 [&_summary::-webkit-details-marker]:hidden"
|
|
170
|
+
>
|
|
171
|
+
<summary className="flex items-center justify-between cursor-pointer">
|
|
172
|
+
<span className="text-small font-semibold">{item.question}</span>
|
|
173
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" className="transition-transform group-open:rotate-180 shrink-0">
|
|
174
|
+
<polyline points="4,6 8,10 12,6" />
|
|
175
|
+
</svg>
|
|
176
|
+
</summary>
|
|
177
|
+
<p className="text-small text-muted-foreground mt-3 leading-relaxed">
|
|
178
|
+
{item.answer}
|
|
179
|
+
</p>
|
|
180
|
+
</details>
|
|
181
|
+
))}
|
|
182
|
+
</div>
|
|
183
|
+
</section>
|
|
184
|
+
)}
|
|
185
|
+
|
|
186
|
+
{/* CTA final */}
|
|
187
|
+
<section className="imansi-container pb-20">
|
|
188
|
+
<div className="rounded-2xl bg-primary text-primary-foreground p-10 sm:p-14 text-center space-y-5">
|
|
189
|
+
<h2 className="text-h1 font-bold tracking-tight">{ctaTitle}</h2>
|
|
190
|
+
<p className="text-body opacity-80 max-w-xl mx-auto">{ctaSubtitle}</p>
|
|
191
|
+
<div className="pt-2">
|
|
192
|
+
<Link to={ctaPrimary.href}>
|
|
193
|
+
<Button
|
|
194
|
+
size="lg"
|
|
195
|
+
className="!bg-primary-foreground !text-primary hover:opacity-90"
|
|
196
|
+
>
|
|
197
|
+
{ctaPrimary.label}
|
|
198
|
+
</Button>
|
|
199
|
+
</Link>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
</section>
|
|
203
|
+
</MarketingLayout>
|
|
204
|
+
);
|
|
205
|
+
}
|