blocks-dusted 0.1.0 → 0.1.2
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/README.md +967 -3
- package/package.json +40 -40
- package/src/commands/add.js +143 -45
- package/src/commands/list.js +2 -2
- package/src/installers/checkRequirements.js +1 -0
- package/src/installers/copyTemplateFiles.js +23 -2
- package/src/installers/patchPayloadConfigCollections.js +92 -0
- package/src/installers/writeInstalledBlockReadme.js +17 -4
- package/src/registry/listTemplates.js +16 -7
- package/src/registry/loadTemplateManifest.js +20 -11
- package/src/registry/validateTemplateManifest.js +6 -5
- package/src/utils/paths.js +7 -0
- package/templates/blocks/DD-BookCall/Component.tsx +535 -573
- package/templates/blocks/DD-CardTemplate01/Component.tsx +45 -49
- package/templates/blocks/DD-Carousel-A/Component.tsx +249 -266
- package/templates/blocks/DD-Carousel-B/Component.tsx +403 -432
- package/templates/blocks/DD-ComparisonTable/Component.tsx +256 -272
- package/templates/blocks/DD-Contact/Component.tsx +434 -439
- package/templates/blocks/DD-Contact/config.ts +8 -3
- package/templates/blocks/DD-FeatCat/Component.tsx +302 -361
- package/templates/blocks/DD-FeatCat/config.ts +201 -204
- package/templates/blocks/DD-FeatStrip/Component.tsx +171 -201
- package/templates/blocks/DD-Hero/Component.tsx +297 -317
- package/templates/blocks/DD-HorizontalScroll/Component.tsx +244 -303
- package/templates/blocks/DD-MasonaryMedia/Component.tsx +202 -228
- package/templates/blocks/DD-MasonaryMedia/config.ts +13 -14
- package/templates/blocks/DD-Pricing/Component.tsx +372 -380
- package/templates/blocks/DD-Process/Component.tsx +149 -155
- package/templates/blocks/DD-Section/Component.tsx +266 -327
- package/templates/blocks/DD-Services/Component.tsx +176 -188
- package/templates/blocks/DD-ShowcaseGrid/Component.tsx +307 -317
- package/templates/blocks/DD-Slider-A/Component.tsx +237 -252
- package/templates/blocks/DD-StackCards/Component.tsx +137 -160
- package/templates/blocks/DD-Team/Component.tsx +247 -265
- package/templates/blocks/DD-TechStack/Component.tsx +57 -72
- package/templates/blocks/DD-Testimonials/Component.tsx +147 -147
- package/templates/blocks/DD-Testimonials/config.ts +66 -66
- package/templates/blocks/DD-Work/Component.tsx +315 -328
- package/templates/blocks/DD-Work-B/Component.tsx +294 -320
- package/templates/collections/Testimonials/README.md +11 -0
- package/templates/collections/Testimonials/Testimonials.ts +161 -0
- package/templates/collections/Testimonials/manifest.json +55 -0
- package/templates/components/RichText/README.md +13 -0
- package/templates/components/RichText/converter/componentConverter/blocks.tsx +43 -0
- package/templates/components/RichText/converter/componentConverter/types.ts +11 -0
- package/templates/components/RichText/converter/index.tsx +18 -0
- package/templates/components/RichText/converter/internalLinks.tsx +45 -0
- package/templates/components/RichText/converter/textConverter.tsx +27 -0
- package/templates/components/RichText/index.tsx +35 -0
- package/templates/components/RichText/manifest.json +80 -0
|
@@ -1,380 +1,372 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import React, { useState } from
|
|
4
|
-
import Link from
|
|
5
|
-
import { Check, X } from
|
|
6
|
-
|
|
7
|
-
export type BillingCycle =
|
|
8
|
-
|
|
9
|
-
interface PricingFeature {
|
|
10
|
-
id?: string | null
|
|
11
|
-
label?: string | null
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
interface PricingTier {
|
|
15
|
-
id?: string | null
|
|
16
|
-
name?: string | null
|
|
17
|
-
tagline?: string | null
|
|
18
|
-
monthlyPrice?: number | null
|
|
19
|
-
annualPrice?: number | null
|
|
20
|
-
cta?: string | null
|
|
21
|
-
ctaLink?: string | null
|
|
22
|
-
highlight?: boolean | null
|
|
23
|
-
badge?: string | null
|
|
24
|
-
features?: PricingFeature[] | null
|
|
25
|
-
missing?: PricingFeature[] | null
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
interface StudioPricingBlockProps {
|
|
29
|
-
eyebrow?: string | null
|
|
30
|
-
heading?: string | null
|
|
31
|
-
description?: string | null
|
|
32
|
-
defaultBilling?: BillingCycle | null
|
|
33
|
-
annualDiscountLabel?: string | null
|
|
34
|
-
monthlyLabel?: string | null
|
|
35
|
-
annualLabel?: string | null
|
|
36
|
-
tiers?: PricingTier[] | null
|
|
37
|
-
guaranteeText?: string | null
|
|
38
|
-
guaranteeHighlight?: string | null
|
|
39
|
-
sectionId?: string | null
|
|
40
|
-
customCSS?: string | null
|
|
41
|
-
[key: string]: unknown
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const DEFAULT_TIERS: PricingTier[] = [
|
|
45
|
-
{
|
|
46
|
-
id:
|
|
47
|
-
name:
|
|
48
|
-
tagline:
|
|
49
|
-
monthlyPrice: 0,
|
|
50
|
-
annualPrice: 0,
|
|
51
|
-
cta:
|
|
52
|
-
ctaLink:
|
|
53
|
-
highlight: false,
|
|
54
|
-
badge: null,
|
|
55
|
-
features: [
|
|
56
|
-
{ label:
|
|
57
|
-
{ label:
|
|
58
|
-
{ label:
|
|
59
|
-
{ label:
|
|
60
|
-
{ label:
|
|
61
|
-
{ label:
|
|
62
|
-
],
|
|
63
|
-
missing: [
|
|
64
|
-
{ label:
|
|
65
|
-
{ label:
|
|
66
|
-
{ label:
|
|
67
|
-
{ label:
|
|
68
|
-
{ label:
|
|
69
|
-
],
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
id:
|
|
73
|
-
name:
|
|
74
|
-
tagline:
|
|
75
|
-
monthlyPrice: 49,
|
|
76
|
-
annualPrice: 39,
|
|
77
|
-
cta:
|
|
78
|
-
ctaLink:
|
|
79
|
-
highlight: true,
|
|
80
|
-
badge:
|
|
81
|
-
features: [
|
|
82
|
-
{ label:
|
|
83
|
-
{ label:
|
|
84
|
-
{ label:
|
|
85
|
-
{ label:
|
|
86
|
-
{ label:
|
|
87
|
-
{ label:
|
|
88
|
-
{ label:
|
|
89
|
-
{ label:
|
|
90
|
-
{ label:
|
|
91
|
-
],
|
|
92
|
-
missing: [
|
|
93
|
-
{ label:
|
|
94
|
-
{ label:
|
|
95
|
-
{ label:
|
|
96
|
-
],
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
id:
|
|
100
|
-
name:
|
|
101
|
-
tagline:
|
|
102
|
-
monthlyPrice: 129,
|
|
103
|
-
annualPrice: 99,
|
|
104
|
-
cta:
|
|
105
|
-
ctaLink:
|
|
106
|
-
highlight: false,
|
|
107
|
-
badge: null,
|
|
108
|
-
features: [
|
|
109
|
-
{ label:
|
|
110
|
-
{ label:
|
|
111
|
-
{ label:
|
|
112
|
-
{ label:
|
|
113
|
-
{ label:
|
|
114
|
-
{ label:
|
|
115
|
-
{ label:
|
|
116
|
-
{ label:
|
|
117
|
-
{ label:
|
|
118
|
-
],
|
|
119
|
-
missing: [],
|
|
120
|
-
},
|
|
121
|
-
]
|
|
122
|
-
|
|
123
|
-
export function DDPricing({
|
|
124
|
-
eyebrow,
|
|
125
|
-
heading,
|
|
126
|
-
description,
|
|
127
|
-
defaultBilling =
|
|
128
|
-
annualDiscountLabel =
|
|
129
|
-
monthlyLabel =
|
|
130
|
-
annualLabel =
|
|
131
|
-
tiers,
|
|
132
|
-
guaranteeText =
|
|
133
|
-
guaranteeHighlight =
|
|
134
|
-
sectionId,
|
|
135
|
-
}: StudioPricingBlockProps) {
|
|
136
|
-
const [billing, setBilling] = useState<BillingCycle>(
|
|
137
|
-
defaultBilling ===
|
|
138
|
-
)
|
|
139
|
-
const pricingTiers = tiers && tiers.length > 0 ? tiers : DEFAULT_TIERS
|
|
140
|
-
|
|
141
|
-
return (
|
|
142
|
-
<section
|
|
143
|
-
id={sectionId ?? undefined}
|
|
144
|
-
className="studio-pricing-block px-6 pb-20 pt-32 text-stone-700 dark:text-stone-50"
|
|
145
|
-
>
|
|
146
|
-
<div className="mx-auto max-w-3xl text-center">
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
{
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const
|
|
207
|
-
const
|
|
208
|
-
typeof tier.
|
|
209
|
-
const annualPrice
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
</p>
|
|
239
|
-
)}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
<
|
|
243
|
-
{
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
className=
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
</p>
|
|
374
|
-
)}
|
|
375
|
-
</div>
|
|
376
|
-
</section>
|
|
377
|
-
);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
export default DDPricing;
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { useState } from 'react'
|
|
4
|
+
import Link from 'next/link'
|
|
5
|
+
import { Check, X } from 'lucide-react'
|
|
6
|
+
|
|
7
|
+
export type BillingCycle = 'monthly' | 'annual'
|
|
8
|
+
|
|
9
|
+
interface PricingFeature {
|
|
10
|
+
id?: string | null
|
|
11
|
+
label?: string | null
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface PricingTier {
|
|
15
|
+
id?: string | null
|
|
16
|
+
name?: string | null
|
|
17
|
+
tagline?: string | null
|
|
18
|
+
monthlyPrice?: number | null
|
|
19
|
+
annualPrice?: number | null
|
|
20
|
+
cta?: string | null
|
|
21
|
+
ctaLink?: string | null
|
|
22
|
+
highlight?: boolean | null
|
|
23
|
+
badge?: string | null
|
|
24
|
+
features?: PricingFeature[] | null
|
|
25
|
+
missing?: PricingFeature[] | null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface StudioPricingBlockProps {
|
|
29
|
+
eyebrow?: string | null
|
|
30
|
+
heading?: string | null
|
|
31
|
+
description?: string | null
|
|
32
|
+
defaultBilling?: BillingCycle | null
|
|
33
|
+
annualDiscountLabel?: string | null
|
|
34
|
+
monthlyLabel?: string | null
|
|
35
|
+
annualLabel?: string | null
|
|
36
|
+
tiers?: PricingTier[] | null
|
|
37
|
+
guaranteeText?: string | null
|
|
38
|
+
guaranteeHighlight?: string | null
|
|
39
|
+
sectionId?: string | null
|
|
40
|
+
customCSS?: string | null
|
|
41
|
+
[key: string]: unknown
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const DEFAULT_TIERS: PricingTier[] = [
|
|
45
|
+
{
|
|
46
|
+
id: 'free',
|
|
47
|
+
name: 'Free',
|
|
48
|
+
tagline: 'Start building today',
|
|
49
|
+
monthlyPrice: 0,
|
|
50
|
+
annualPrice: 0,
|
|
51
|
+
cta: 'Get Started Free',
|
|
52
|
+
ctaLink: '/account',
|
|
53
|
+
highlight: false,
|
|
54
|
+
badge: null,
|
|
55
|
+
features: [
|
|
56
|
+
{ label: 'Access to all free components' },
|
|
57
|
+
{ label: 'Access to free templates' },
|
|
58
|
+
{ label: 'Community Discord access' },
|
|
59
|
+
{ label: 'MIT license for personal use' },
|
|
60
|
+
{ label: 'Basic documentation' },
|
|
61
|
+
{ label: 'Copy JSX code' },
|
|
62
|
+
],
|
|
63
|
+
missing: [
|
|
64
|
+
{ label: 'Pro components & templates' },
|
|
65
|
+
{ label: 'Source files (Figma, PSD)' },
|
|
66
|
+
{ label: 'Payload CMS blocks' },
|
|
67
|
+
{ label: 'Priority support' },
|
|
68
|
+
{ label: 'Commercial license' },
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: 'pro',
|
|
73
|
+
name: 'Pro',
|
|
74
|
+
tagline: 'For individual developers',
|
|
75
|
+
monthlyPrice: 49,
|
|
76
|
+
annualPrice: 39,
|
|
77
|
+
cta: 'Get Pro Access',
|
|
78
|
+
ctaLink: '/account',
|
|
79
|
+
highlight: true,
|
|
80
|
+
badge: 'Most Popular',
|
|
81
|
+
features: [
|
|
82
|
+
{ label: 'Everything in Free' },
|
|
83
|
+
{ label: '120+ premium components' },
|
|
84
|
+
{ label: '24+ full website templates' },
|
|
85
|
+
{ label: 'Payload CMS v3 blocks' },
|
|
86
|
+
{ label: 'Figma source files' },
|
|
87
|
+
{ label: 'Commercial license (1 project)' },
|
|
88
|
+
{ label: 'Priority email support' },
|
|
89
|
+
{ label: 'Lifetime updates' },
|
|
90
|
+
{ label: 'Early access to new releases' },
|
|
91
|
+
],
|
|
92
|
+
missing: [
|
|
93
|
+
{ label: 'Unlimited commercial projects' },
|
|
94
|
+
{ label: 'Team seats' },
|
|
95
|
+
{ label: 'White-label license' },
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: 'team',
|
|
100
|
+
name: 'Team',
|
|
101
|
+
tagline: 'For studios & agencies',
|
|
102
|
+
monthlyPrice: 129,
|
|
103
|
+
annualPrice: 99,
|
|
104
|
+
cta: 'Get Team Access',
|
|
105
|
+
ctaLink: '/account',
|
|
106
|
+
highlight: false,
|
|
107
|
+
badge: null,
|
|
108
|
+
features: [
|
|
109
|
+
{ label: 'Everything in Pro' },
|
|
110
|
+
{ label: 'Unlimited commercial projects' },
|
|
111
|
+
{ label: 'Up to 10 team seats' },
|
|
112
|
+
{ label: 'White-label license' },
|
|
113
|
+
{ label: 'Custom Payload CMS schemas' },
|
|
114
|
+
{ label: 'Dedicated Slack channel' },
|
|
115
|
+
{ label: '1-on-1 onboarding call' },
|
|
116
|
+
{ label: 'Invoice & billing management' },
|
|
117
|
+
{ label: 'SLA support guarantee' },
|
|
118
|
+
],
|
|
119
|
+
missing: [],
|
|
120
|
+
},
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
export function DDPricing({
|
|
124
|
+
eyebrow,
|
|
125
|
+
heading,
|
|
126
|
+
description,
|
|
127
|
+
defaultBilling = 'monthly',
|
|
128
|
+
annualDiscountLabel = '-20%',
|
|
129
|
+
monthlyLabel = 'Pay Monthly',
|
|
130
|
+
annualLabel = 'Pay Annually',
|
|
131
|
+
tiers,
|
|
132
|
+
guaranteeText = 'All paid plans include a',
|
|
133
|
+
guaranteeHighlight = '14-day money-back guarantee',
|
|
134
|
+
sectionId,
|
|
135
|
+
}: StudioPricingBlockProps) {
|
|
136
|
+
const [billing, setBilling] = useState<BillingCycle>(
|
|
137
|
+
defaultBilling === 'annual' ? 'annual' : 'monthly',
|
|
138
|
+
)
|
|
139
|
+
const pricingTiers = tiers && tiers.length > 0 ? tiers : DEFAULT_TIERS
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<section
|
|
143
|
+
id={sectionId ?? undefined}
|
|
144
|
+
className="studio-pricing-block px-6 pb-20 pt-32 text-stone-700 dark:text-stone-50"
|
|
145
|
+
>
|
|
146
|
+
<div className="mx-auto max-w-3xl text-center">
|
|
147
|
+
#BlockName-DDPricing
|
|
148
|
+
{eyebrow && (
|
|
149
|
+
<p className="mb-4 text-xs font-semibold uppercase tracking-[0.28em] text-stone-700 dark:text-stone-500">
|
|
150
|
+
{eyebrow}
|
|
151
|
+
</p>
|
|
152
|
+
)}
|
|
153
|
+
|
|
154
|
+
{heading && (
|
|
155
|
+
<h2 className="mx-auto mb-4 max-w-3xl text-4xl font-black tracking-tight text-stone-700 dark:text-stone-50 md:text-5xl">
|
|
156
|
+
{heading}
|
|
157
|
+
</h2>
|
|
158
|
+
)}
|
|
159
|
+
|
|
160
|
+
{description && (
|
|
161
|
+
<p className="mx-auto mb-10 max-w-2xl text-sm leading-relaxed text-stone-500 md:text-base">
|
|
162
|
+
{description}
|
|
163
|
+
</p>
|
|
164
|
+
)}
|
|
165
|
+
|
|
166
|
+
<div className="inline-flex items-center gap-1 rounded-xl border border-stone-100 dark:border-stone-800 bg-stone-200 dark:bg-stone-900 p-1">
|
|
167
|
+
<button
|
|
168
|
+
type="button"
|
|
169
|
+
onClick={() => setBilling('monthly')}
|
|
170
|
+
className={`cursor-pointer whitespace-nowrap rounded-lg px-5 py-2 text-sm font-medium transition-all ${
|
|
171
|
+
billing === 'monthly' ? 'bg-stone-50 text-stone-900' : 'text-stone-500 '
|
|
172
|
+
}`}
|
|
173
|
+
>
|
|
174
|
+
{monthlyLabel}
|
|
175
|
+
</button>
|
|
176
|
+
|
|
177
|
+
<button
|
|
178
|
+
type="button"
|
|
179
|
+
onClick={() => setBilling('annual')}
|
|
180
|
+
className={`flex cursor-pointer items-center gap-2 whitespace-nowrap rounded-lg px-5 py-2 text-sm font-medium transition-all ${
|
|
181
|
+
billing === 'annual'
|
|
182
|
+
? 'bg-stone-50 text-stone-900'
|
|
183
|
+
: 'text-stone-500 hover:text-stone-700'
|
|
184
|
+
}`}
|
|
185
|
+
>
|
|
186
|
+
{annualLabel}
|
|
187
|
+
{annualDiscountLabel && (
|
|
188
|
+
<span
|
|
189
|
+
className={`rounded-full px-1.5 py-0.5 text-[10px] font-bold ${
|
|
190
|
+
billing === 'annual'
|
|
191
|
+
? 'bg-lime-500 text-stone-50'
|
|
192
|
+
: 'bg-lime-900/60 text-lime-400'
|
|
193
|
+
}`}
|
|
194
|
+
>
|
|
195
|
+
{annualDiscountLabel}
|
|
196
|
+
</span>
|
|
197
|
+
)}
|
|
198
|
+
</button>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<div className="mx-auto mt-16 max-w-6xl">
|
|
203
|
+
<div className="grid grid-cols-1 items-start gap-5 md:grid-cols-3">
|
|
204
|
+
{pricingTiers.map((tier, index) => {
|
|
205
|
+
const isHighlighted = tier.highlight === true
|
|
206
|
+
const monthlyPrice = typeof tier.monthlyPrice === 'number' ? tier.monthlyPrice : 0
|
|
207
|
+
const annualPrice =
|
|
208
|
+
typeof tier.annualPrice === 'number' ? tier.annualPrice : monthlyPrice
|
|
209
|
+
const price = billing === 'annual' ? annualPrice : monthlyPrice
|
|
210
|
+
const tierId = tier.id ?? tier.name ?? String(index)
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
<div
|
|
214
|
+
key={tierId}
|
|
215
|
+
className={`relative flex flex-col rounded-2xl border p-7 ${
|
|
216
|
+
isHighlighted
|
|
217
|
+
? 'dark:border-white dark:bg-stone-50/50'
|
|
218
|
+
: 'border-stone-200 dark:border-stone-800 bg-stone-50/50 dark:bg-stone-900/50'
|
|
219
|
+
}`}
|
|
220
|
+
style={{ cornerShape: 'squircle' } as React.CSSProperties}
|
|
221
|
+
>
|
|
222
|
+
{tier.badge && (
|
|
223
|
+
<div className="absolute -top-3 left-1/2 -translate-x-1/2">
|
|
224
|
+
<span className="whitespace-nowrap rounded-full border border-indigo-400 bg-indigo-900/90 px-3 py-1 text-[10px] font-bold uppercase tracking-widest text-stone-300 shadow-md">
|
|
225
|
+
{tier.badge}
|
|
226
|
+
</span>
|
|
227
|
+
</div>
|
|
228
|
+
)}
|
|
229
|
+
|
|
230
|
+
<div className="mb-6">
|
|
231
|
+
{tier.name && (
|
|
232
|
+
<p className="mb-1 text-base font-semibold uppercase tracking-widest text-stone-500">
|
|
233
|
+
{tier.name}
|
|
234
|
+
</p>
|
|
235
|
+
)}
|
|
236
|
+
|
|
237
|
+
{tier.tagline && (
|
|
238
|
+
<p className="mb-5 text-[11.5px] text-stone-500">{tier.tagline}</p>
|
|
239
|
+
)}
|
|
240
|
+
|
|
241
|
+
<div className="flex items-end gap-1">
|
|
242
|
+
<span
|
|
243
|
+
className={`text-5xl text-stone-900 dark:text-stone-50 font-bold tracking-tight ${
|
|
244
|
+
isHighlighted
|
|
245
|
+
? 'text-stone-900 dark:text-stone-50'
|
|
246
|
+
: 'text-stone-50 dark:text-stone-50'
|
|
247
|
+
}`}
|
|
248
|
+
>
|
|
249
|
+
{price === 0 ? 'Free' : `$${price}`}
|
|
250
|
+
</span>
|
|
251
|
+
|
|
252
|
+
{price > 0 && (
|
|
253
|
+
<span className="mb-2 text-sm text-stone-500">
|
|
254
|
+
/{billing === 'annual' ? 'mo, billed annually' : 'mo'}
|
|
255
|
+
</span>
|
|
256
|
+
)}
|
|
257
|
+
</div>
|
|
258
|
+
|
|
259
|
+
{billing === 'annual' && price > 0 && (
|
|
260
|
+
<p
|
|
261
|
+
className={`mt-1 text-xs ${isHighlighted ? 'text-stone-800 dark:text-stone-500' : 'text-stone-800 dark:text-stone-600'}`}
|
|
262
|
+
>
|
|
263
|
+
${monthlyPrice}/mo if billed monthly
|
|
264
|
+
</p>
|
|
265
|
+
)}
|
|
266
|
+
</div>
|
|
267
|
+
|
|
268
|
+
<Link
|
|
269
|
+
href={tier.ctaLink ?? '#'}
|
|
270
|
+
className={`mb-7 flex w-full cursor-pointer items-center justify-center rounded-xl py-3 text-sm font-semibold transition-all hover:shadow-2xl ${
|
|
271
|
+
isHighlighted
|
|
272
|
+
? 'bg-indigo-900 text-stone-50 hover:bg-indigo-800'
|
|
273
|
+
: tierId === 'free'
|
|
274
|
+
? 'border border-indigo-700 bg-indigo-800 text-stone-200 hover:bg-indigo-700'
|
|
275
|
+
: 'bg-indigo-50 text-indigo-900 hover:bg-indigo-100'
|
|
276
|
+
}`}
|
|
277
|
+
style={{ cornerShape: 'squircle' } as React.CSSProperties}
|
|
278
|
+
>
|
|
279
|
+
{tier.cta ?? 'Get Started'}
|
|
280
|
+
</Link>
|
|
281
|
+
|
|
282
|
+
<div
|
|
283
|
+
className={`mb-6 h-px ${isHighlighted ? 'bg-stone-800 dark:bg-stone-200' : 'bg-stone-200 dark:bg-stone-800'}`}
|
|
284
|
+
/>
|
|
285
|
+
|
|
286
|
+
<ul className="flex-1 space-y-3">
|
|
287
|
+
{(tier.features ?? []).map((feature, featureIndex) => {
|
|
288
|
+
const label = feature.label
|
|
289
|
+
if (!label) return null
|
|
290
|
+
|
|
291
|
+
return (
|
|
292
|
+
<li
|
|
293
|
+
key={feature.id ?? `${label}-${featureIndex}`}
|
|
294
|
+
className="flex items-start gap-2.5"
|
|
295
|
+
>
|
|
296
|
+
<div
|
|
297
|
+
className={`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full ${
|
|
298
|
+
isHighlighted
|
|
299
|
+
? 'bg-stone-800 dark:bg-stone-200'
|
|
300
|
+
: 'bg-stone-200 dark:bg-stone-800'
|
|
301
|
+
}`}
|
|
302
|
+
>
|
|
303
|
+
<Check
|
|
304
|
+
className={`h-2.5 w-2.5 ${
|
|
305
|
+
isHighlighted
|
|
306
|
+
? 'text-stone-700 dark:text-stone-200'
|
|
307
|
+
: 'text-stone-800 dark:text-stone-300'
|
|
308
|
+
}`}
|
|
309
|
+
/>
|
|
310
|
+
</div>
|
|
311
|
+
<span
|
|
312
|
+
className={`text-sm leading-snug ${
|
|
313
|
+
isHighlighted
|
|
314
|
+
? 'text-stone-700 dark:text-stone-200'
|
|
315
|
+
: 'text-stone-800 dark:text-stone-300'
|
|
316
|
+
}`}
|
|
317
|
+
>
|
|
318
|
+
{label}
|
|
319
|
+
</span>
|
|
320
|
+
</li>
|
|
321
|
+
)
|
|
322
|
+
})}
|
|
323
|
+
|
|
324
|
+
{(tier.missing ?? []).map((feature, featureIndex) => {
|
|
325
|
+
const label = feature.label
|
|
326
|
+
if (!label) return null
|
|
327
|
+
|
|
328
|
+
return (
|
|
329
|
+
<li
|
|
330
|
+
key={feature.id ?? `${label}-${featureIndex}`}
|
|
331
|
+
className="flex items-start gap-2.5 opacity-40"
|
|
332
|
+
>
|
|
333
|
+
<div
|
|
334
|
+
className={`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full ${
|
|
335
|
+
isHighlighted
|
|
336
|
+
? 'bg-stone-800 dark:bg-stone-200'
|
|
337
|
+
: 'bg-stone-200 dark:bg-stone-800'
|
|
338
|
+
}`}
|
|
339
|
+
>
|
|
340
|
+
<X className="h-2.5 w-2.5 text-stone-700 dark:text-stone-400" />
|
|
341
|
+
</div>
|
|
342
|
+
<span
|
|
343
|
+
className={`text-sm leading-snug ${
|
|
344
|
+
isHighlighted
|
|
345
|
+
? 'text-stone-700 dark:text-stone-200'
|
|
346
|
+
: 'text-stone-800 dark:text-stone-300'
|
|
347
|
+
}`}
|
|
348
|
+
>
|
|
349
|
+
{label}
|
|
350
|
+
</span>
|
|
351
|
+
</li>
|
|
352
|
+
)
|
|
353
|
+
})}
|
|
354
|
+
</ul>
|
|
355
|
+
</div>
|
|
356
|
+
)
|
|
357
|
+
})}
|
|
358
|
+
</div>
|
|
359
|
+
|
|
360
|
+
{(guaranteeText || guaranteeHighlight) && (
|
|
361
|
+
<p className="mt-16 text-center text-xs text-stone-500 dark:text-stone-600">
|
|
362
|
+
{guaranteeText}{' '}
|
|
363
|
+
{guaranteeHighlight && <strong className="text-stone-500">{guaranteeHighlight}</strong>}
|
|
364
|
+
. No questions asked.
|
|
365
|
+
</p>
|
|
366
|
+
)}
|
|
367
|
+
</div>
|
|
368
|
+
</section>
|
|
369
|
+
)
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
export default DDPricing
|