popeye-cli 1.4.7 → 1.6.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/CHANGELOG.md +54 -0
- package/README.md +264 -63
- package/dist/adapters/gemini.d.ts +1 -0
- package/dist/adapters/gemini.d.ts.map +1 -1
- package/dist/adapters/gemini.js +9 -4
- package/dist/adapters/gemini.js.map +1 -1
- package/dist/adapters/grok.d.ts +1 -0
- package/dist/adapters/grok.d.ts.map +1 -1
- package/dist/adapters/grok.js +9 -4
- package/dist/adapters/grok.js.map +1 -1
- package/dist/adapters/openai.d.ts +1 -1
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +35 -9
- package/dist/adapters/openai.js.map +1 -1
- package/dist/cli/commands/create.d.ts.map +1 -1
- package/dist/cli/commands/create.js +54 -4
- package/dist/cli/commands/create.js.map +1 -1
- package/dist/cli/interactive.d.ts +29 -0
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +132 -7
- package/dist/cli/interactive.js.map +1 -1
- package/dist/generators/all.d.ts +8 -2
- package/dist/generators/all.d.ts.map +1 -1
- package/dist/generators/all.js +37 -316
- package/dist/generators/all.js.map +1 -1
- package/dist/generators/doc-parser.d.ts +64 -0
- package/dist/generators/doc-parser.d.ts.map +1 -0
- package/dist/generators/doc-parser.js +407 -0
- package/dist/generators/doc-parser.js.map +1 -0
- package/dist/generators/frontend-design-analyzer.d.ts +30 -0
- package/dist/generators/frontend-design-analyzer.d.ts.map +1 -0
- package/dist/generators/frontend-design-analyzer.js +208 -0
- package/dist/generators/frontend-design-analyzer.js.map +1 -0
- package/dist/generators/shared-packages.d.ts +45 -0
- package/dist/generators/shared-packages.d.ts.map +1 -0
- package/dist/generators/shared-packages.js +456 -0
- package/dist/generators/shared-packages.js.map +1 -0
- package/dist/generators/templates/index.d.ts +8 -0
- package/dist/generators/templates/index.d.ts.map +1 -1
- package/dist/generators/templates/index.js +8 -0
- package/dist/generators/templates/index.js.map +1 -1
- package/dist/generators/templates/website-components.d.ts +33 -0
- package/dist/generators/templates/website-components.d.ts.map +1 -0
- package/dist/generators/templates/website-components.js +303 -0
- package/dist/generators/templates/website-components.js.map +1 -0
- package/dist/generators/templates/website-config.d.ts +55 -0
- package/dist/generators/templates/website-config.d.ts.map +1 -0
- package/dist/generators/templates/website-config.js +425 -0
- package/dist/generators/templates/website-config.js.map +1 -0
- package/dist/generators/templates/website-conversion.d.ts +27 -0
- package/dist/generators/templates/website-conversion.d.ts.map +1 -0
- package/dist/generators/templates/website-conversion.js +326 -0
- package/dist/generators/templates/website-conversion.js.map +1 -0
- package/dist/generators/templates/website-landing.d.ts +24 -0
- package/dist/generators/templates/website-landing.d.ts.map +1 -0
- package/dist/generators/templates/website-landing.js +276 -0
- package/dist/generators/templates/website-landing.js.map +1 -0
- package/dist/generators/templates/website-layout.d.ts +42 -0
- package/dist/generators/templates/website-layout.d.ts.map +1 -0
- package/dist/generators/templates/website-layout.js +408 -0
- package/dist/generators/templates/website-layout.js.map +1 -0
- package/dist/generators/templates/website-pricing.d.ts +11 -0
- package/dist/generators/templates/website-pricing.d.ts.map +1 -0
- package/dist/generators/templates/website-pricing.js +313 -0
- package/dist/generators/templates/website-pricing.js.map +1 -0
- package/dist/generators/templates/website-sections.d.ts +102 -0
- package/dist/generators/templates/website-sections.d.ts.map +1 -0
- package/dist/generators/templates/website-sections.js +444 -0
- package/dist/generators/templates/website-sections.js.map +1 -0
- package/dist/generators/templates/website-seo.d.ts +76 -0
- package/dist/generators/templates/website-seo.d.ts.map +1 -0
- package/dist/generators/templates/website-seo.js +326 -0
- package/dist/generators/templates/website-seo.js.map +1 -0
- package/dist/generators/templates/website.d.ts +10 -83
- package/dist/generators/templates/website.d.ts.map +1 -1
- package/dist/generators/templates/website.js +12 -875
- package/dist/generators/templates/website.js.map +1 -1
- package/dist/generators/website-content-scanner.d.ts +37 -0
- package/dist/generators/website-content-scanner.d.ts.map +1 -0
- package/dist/generators/website-content-scanner.js +165 -0
- package/dist/generators/website-content-scanner.js.map +1 -0
- package/dist/generators/website-context.d.ts +119 -0
- package/dist/generators/website-context.d.ts.map +1 -0
- package/dist/generators/website-context.js +350 -0
- package/dist/generators/website-context.js.map +1 -0
- package/dist/generators/website-debug.d.ts +68 -0
- package/dist/generators/website-debug.d.ts.map +1 -0
- package/dist/generators/website-debug.js +93 -0
- package/dist/generators/website-debug.js.map +1 -0
- package/dist/generators/website.d.ts +5 -0
- package/dist/generators/website.d.ts.map +1 -1
- package/dist/generators/website.js +136 -11
- package/dist/generators/website.js.map +1 -1
- package/dist/generators/workspace-root.d.ts +27 -0
- package/dist/generators/workspace-root.d.ts.map +1 -0
- package/dist/generators/workspace-root.js +100 -0
- package/dist/generators/workspace-root.js.map +1 -0
- package/dist/state/index.d.ts +35 -0
- package/dist/state/index.d.ts.map +1 -1
- package/dist/state/index.js +40 -0
- package/dist/state/index.js.map +1 -1
- package/dist/types/consensus.d.ts +3 -0
- package/dist/types/consensus.d.ts.map +1 -1
- package/dist/types/consensus.js +1 -0
- package/dist/types/consensus.js.map +1 -1
- package/dist/types/website-strategy.d.ts +263 -0
- package/dist/types/website-strategy.d.ts.map +1 -0
- package/dist/types/website-strategy.js +105 -0
- package/dist/types/website-strategy.js.map +1 -0
- package/dist/types/workflow.d.ts +21 -0
- package/dist/types/workflow.d.ts.map +1 -1
- package/dist/types/workflow.js +8 -0
- package/dist/types/workflow.js.map +1 -1
- package/dist/upgrade/handlers.d.ts +15 -0
- package/dist/upgrade/handlers.d.ts.map +1 -1
- package/dist/upgrade/handlers.js +52 -0
- package/dist/upgrade/handlers.js.map +1 -1
- package/dist/workflow/auto-fix-bundler.d.ts +37 -0
- package/dist/workflow/auto-fix-bundler.d.ts.map +1 -0
- package/dist/workflow/auto-fix-bundler.js +320 -0
- package/dist/workflow/auto-fix-bundler.js.map +1 -0
- package/dist/workflow/auto-fix.d.ts.map +1 -1
- package/dist/workflow/auto-fix.js +10 -3
- package/dist/workflow/auto-fix.js.map +1 -1
- package/dist/workflow/consensus.d.ts.map +1 -1
- package/dist/workflow/consensus.js +2 -0
- package/dist/workflow/consensus.js.map +1 -1
- package/dist/workflow/execution-mode.d.ts.map +1 -1
- package/dist/workflow/execution-mode.js +18 -0
- package/dist/workflow/execution-mode.js.map +1 -1
- package/dist/workflow/index.d.ts +4 -0
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +37 -0
- package/dist/workflow/index.js.map +1 -1
- package/dist/workflow/overview.d.ts +89 -0
- package/dist/workflow/overview.d.ts.map +1 -0
- package/dist/workflow/overview.js +358 -0
- package/dist/workflow/overview.js.map +1 -0
- package/dist/workflow/plan-mode.d.ts +6 -4
- package/dist/workflow/plan-mode.d.ts.map +1 -1
- package/dist/workflow/plan-mode.js +148 -6
- package/dist/workflow/plan-mode.js.map +1 -1
- package/dist/workflow/website-strategy.d.ts +79 -0
- package/dist/workflow/website-strategy.d.ts.map +1 -0
- package/dist/workflow/website-strategy.js +310 -0
- package/dist/workflow/website-strategy.js.map +1 -0
- package/dist/workflow/website-updater.d.ts +17 -0
- package/dist/workflow/website-updater.d.ts.map +1 -0
- package/dist/workflow/website-updater.js +116 -0
- package/dist/workflow/website-updater.js.map +1 -0
- package/dist/workflow/workflow-logger.d.ts +1 -1
- package/dist/workflow/workflow-logger.d.ts.map +1 -1
- package/dist/workflow/workflow-logger.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/gemini.ts +10 -4
- package/src/adapters/grok.ts +10 -4
- package/src/adapters/openai.ts +38 -6
- package/src/cli/commands/create.ts +58 -4
- package/src/cli/interactive.ts +143 -7
- package/src/generators/all.ts +49 -332
- package/src/generators/doc-parser.ts +449 -0
- package/src/generators/frontend-design-analyzer.ts +261 -0
- package/src/generators/shared-packages.ts +500 -0
- package/src/generators/templates/index.ts +8 -0
- package/src/generators/templates/website-components.ts +330 -0
- package/src/generators/templates/website-config.ts +444 -0
- package/src/generators/templates/website-conversion.ts +341 -0
- package/src/generators/templates/website-landing.ts +331 -0
- package/src/generators/templates/website-layout.ts +443 -0
- package/src/generators/templates/website-pricing.ts +330 -0
- package/src/generators/templates/website-sections.ts +541 -0
- package/src/generators/templates/website-seo.ts +370 -0
- package/src/generators/templates/website.ts +38 -905
- package/src/generators/website-content-scanner.ts +208 -0
- package/src/generators/website-context.ts +493 -0
- package/src/generators/website-debug.ts +130 -0
- package/src/generators/website.ts +178 -20
- package/src/generators/workspace-root.ts +113 -0
- package/src/state/index.ts +56 -0
- package/src/types/consensus.ts +3 -0
- package/src/types/website-strategy.ts +243 -0
- package/src/types/workflow.ts +21 -0
- package/src/upgrade/handlers.ts +65 -0
- package/src/workflow/auto-fix-bundler.ts +392 -0
- package/src/workflow/auto-fix.ts +11 -3
- package/src/workflow/consensus.ts +2 -0
- package/src/workflow/execution-mode.ts +21 -0
- package/src/workflow/index.ts +37 -0
- package/src/workflow/overview.ts +475 -0
- package/src/workflow/plan-mode.ts +193 -8
- package/src/workflow/website-strategy.ts +379 -0
- package/src/workflow/website-updater.ts +142 -0
- package/src/workflow/workflow-logger.ts +1 -0
- package/tests/adapters/persona-switching.test.ts +63 -0
- package/tests/cli/project-naming.test.ts +136 -0
- package/tests/generators/doc-parser.test.ts +121 -0
- package/tests/generators/frontend-design-analyzer.test.ts +90 -0
- package/tests/generators/quality-gate.test.ts +183 -0
- package/tests/generators/shared-packages.test.ts +83 -0
- package/tests/generators/website-components.test.ts +159 -0
- package/tests/generators/website-config.test.ts +84 -0
- package/tests/generators/website-content-scanner.test.ts +181 -0
- package/tests/generators/website-context.test.ts +331 -0
- package/tests/generators/website-debug.test.ts +77 -0
- package/tests/generators/website-landing.test.ts +188 -0
- package/tests/generators/website-pricing.test.ts +98 -0
- package/tests/generators/website-sections.test.ts +245 -0
- package/tests/generators/website-seo-quality.test.ts +246 -0
- package/tests/generators/workspace-root.test.ts +105 -0
- package/tests/upgrade/handlers.test.ts +162 -0
- package/tests/workflow/auto-fix-bundler.test.ts +242 -0
- package/tests/workflow/overview.test.ts +392 -0
- package/tests/workflow/plan-mode.test.ts +111 -1
- package/tests/workflow/website-strategy.test.ts +246 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lead capture and conversion templates
|
|
3
|
+
* Generates API route handlers for lead capture and contact form components
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Escape a string for safe use inside JSX template literals
|
|
7
|
+
*/
|
|
8
|
+
function escapeJsx(str) {
|
|
9
|
+
return str
|
|
10
|
+
.replace(/\\/g, '\\\\')
|
|
11
|
+
.replace(/'/g, "\\'")
|
|
12
|
+
.replace(/`/g, '\\`')
|
|
13
|
+
.replace(/\$/g, '\\$');
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Generate lead capture API route handler
|
|
17
|
+
*
|
|
18
|
+
* @param provider - Lead capture provider type
|
|
19
|
+
* @returns API route source code (src/app/api/lead/route.ts)
|
|
20
|
+
*/
|
|
21
|
+
export function generateLeadCaptureRoute(provider = 'webhook') {
|
|
22
|
+
if (provider === 'none') {
|
|
23
|
+
return `import { NextResponse } from 'next/server';
|
|
24
|
+
|
|
25
|
+
export async function POST() {
|
|
26
|
+
return NextResponse.json(
|
|
27
|
+
{ error: 'Lead capture not configured' },
|
|
28
|
+
{ status: 501 }
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
32
|
+
}
|
|
33
|
+
if (provider === 'resend') {
|
|
34
|
+
return `import { NextResponse } from 'next/server';
|
|
35
|
+
|
|
36
|
+
interface LeadPayload {
|
|
37
|
+
name: string;
|
|
38
|
+
email: string;
|
|
39
|
+
message?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function POST(request: Request) {
|
|
43
|
+
try {
|
|
44
|
+
const body: LeadPayload = await request.json();
|
|
45
|
+
|
|
46
|
+
if (!body.name || !body.email) {
|
|
47
|
+
return NextResponse.json(
|
|
48
|
+
{ error: 'Name and email are required' },
|
|
49
|
+
{ status: 400 }
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const apiKey = process.env.RESEND_API_KEY;
|
|
54
|
+
if (!apiKey) {
|
|
55
|
+
console.error('RESEND_API_KEY not configured');
|
|
56
|
+
return NextResponse.json(
|
|
57
|
+
{ error: 'Lead capture not configured' },
|
|
58
|
+
{ status: 500 }
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const response = await fetch('https://api.resend.com/emails', {
|
|
63
|
+
method: 'POST',
|
|
64
|
+
headers: {
|
|
65
|
+
'Content-Type': 'application/json',
|
|
66
|
+
'Authorization': \`Bearer \${apiKey}\`,
|
|
67
|
+
},
|
|
68
|
+
body: JSON.stringify({
|
|
69
|
+
from: process.env.RESEND_FROM_EMAIL || 'onboarding@resend.dev',
|
|
70
|
+
to: process.env.LEAD_NOTIFICATION_EMAIL || 'team@example.com',
|
|
71
|
+
subject: \`New lead: \${body.name}\`,
|
|
72
|
+
text: \`Name: \${body.name}\\nEmail: \${body.email}\\nMessage: \${body.message || 'N/A'}\`,
|
|
73
|
+
}),
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
if (!response.ok) {
|
|
77
|
+
console.error('Resend API error:', await response.text());
|
|
78
|
+
return NextResponse.json({ error: 'Failed to send' }, { status: 500 });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return NextResponse.json({ success: true });
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error('Lead capture error:', error);
|
|
84
|
+
return NextResponse.json({ error: 'Internal error' }, { status: 500 });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
`;
|
|
88
|
+
}
|
|
89
|
+
if (provider === 'postmark') {
|
|
90
|
+
return `import { NextResponse } from 'next/server';
|
|
91
|
+
|
|
92
|
+
interface LeadPayload {
|
|
93
|
+
name: string;
|
|
94
|
+
email: string;
|
|
95
|
+
message?: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function POST(request: Request) {
|
|
99
|
+
try {
|
|
100
|
+
const body: LeadPayload = await request.json();
|
|
101
|
+
|
|
102
|
+
if (!body.name || !body.email) {
|
|
103
|
+
return NextResponse.json(
|
|
104
|
+
{ error: 'Name and email are required' },
|
|
105
|
+
{ status: 400 }
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const apiKey = process.env.POSTMARK_API_KEY;
|
|
110
|
+
if (!apiKey) {
|
|
111
|
+
console.error('POSTMARK_API_KEY not configured');
|
|
112
|
+
return NextResponse.json(
|
|
113
|
+
{ error: 'Lead capture not configured' },
|
|
114
|
+
{ status: 500 }
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const response = await fetch('https://api.postmarkapp.com/email', {
|
|
119
|
+
method: 'POST',
|
|
120
|
+
headers: {
|
|
121
|
+
'Content-Type': 'application/json',
|
|
122
|
+
'X-Postmark-Server-Token': apiKey,
|
|
123
|
+
},
|
|
124
|
+
body: JSON.stringify({
|
|
125
|
+
From: process.env.POSTMARK_FROM_EMAIL || 'no-reply@example.com',
|
|
126
|
+
To: process.env.LEAD_NOTIFICATION_EMAIL || 'team@example.com',
|
|
127
|
+
Subject: \`New lead: \${body.name}\`,
|
|
128
|
+
TextBody: \`Name: \${body.name}\\nEmail: \${body.email}\\nMessage: \${body.message || 'N/A'}\`,
|
|
129
|
+
}),
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
if (!response.ok) {
|
|
133
|
+
console.error('Postmark API error:', await response.text());
|
|
134
|
+
return NextResponse.json({ error: 'Failed to send' }, { status: 500 });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return NextResponse.json({ success: true });
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error('Lead capture error:', error);
|
|
140
|
+
return NextResponse.json({ error: 'Internal error' }, { status: 500 });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
`;
|
|
144
|
+
}
|
|
145
|
+
// Default: webhook provider
|
|
146
|
+
return `import { NextResponse } from 'next/server';
|
|
147
|
+
|
|
148
|
+
interface LeadPayload {
|
|
149
|
+
name: string;
|
|
150
|
+
email: string;
|
|
151
|
+
message?: string;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export async function POST(request: Request) {
|
|
155
|
+
try {
|
|
156
|
+
const body: LeadPayload = await request.json();
|
|
157
|
+
|
|
158
|
+
if (!body.name || !body.email) {
|
|
159
|
+
return NextResponse.json(
|
|
160
|
+
{ error: 'Name and email are required' },
|
|
161
|
+
{ status: 400 }
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const webhookUrl = process.env.LEAD_WEBHOOK_URL;
|
|
166
|
+
if (!webhookUrl) {
|
|
167
|
+
console.error('LEAD_WEBHOOK_URL not configured');
|
|
168
|
+
return NextResponse.json(
|
|
169
|
+
{ error: 'Lead capture not configured' },
|
|
170
|
+
{ status: 500 }
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const response = await fetch(webhookUrl, {
|
|
175
|
+
method: 'POST',
|
|
176
|
+
headers: { 'Content-Type': 'application/json' },
|
|
177
|
+
body: JSON.stringify({
|
|
178
|
+
name: body.name,
|
|
179
|
+
email: body.email,
|
|
180
|
+
message: body.message || '',
|
|
181
|
+
timestamp: new Date().toISOString(),
|
|
182
|
+
source: 'website',
|
|
183
|
+
}),
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
if (!response.ok) {
|
|
187
|
+
console.error('Webhook error:', response.status);
|
|
188
|
+
return NextResponse.json({ error: 'Failed to submit' }, { status: 500 });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return NextResponse.json({ success: true });
|
|
192
|
+
} catch (error) {
|
|
193
|
+
console.error('Lead capture error:', error);
|
|
194
|
+
return NextResponse.json({ error: 'Internal error' }, { status: 500 });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
`;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Generate contact form component
|
|
201
|
+
*
|
|
202
|
+
* @param strategy - Optional strategy for CTA text
|
|
203
|
+
* @returns ContactForm component source code
|
|
204
|
+
*/
|
|
205
|
+
export function generateContactForm(strategy) {
|
|
206
|
+
const ctaText = strategy?.conversionStrategy.primaryCta.text || 'Get Started';
|
|
207
|
+
return `'use client';
|
|
208
|
+
|
|
209
|
+
import { useState, type FormEvent } from 'react';
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Lead capture contact form
|
|
213
|
+
* Submits to /api/lead endpoint
|
|
214
|
+
*/
|
|
215
|
+
export default function ContactForm() {
|
|
216
|
+
const [status, setStatus] = useState<'idle' | 'submitting' | 'success' | 'error'>('idle');
|
|
217
|
+
|
|
218
|
+
async function handleSubmit(e: FormEvent<HTMLFormElement>) {
|
|
219
|
+
e.preventDefault();
|
|
220
|
+
setStatus('submitting');
|
|
221
|
+
|
|
222
|
+
const formData = new FormData(e.currentTarget);
|
|
223
|
+
const data = {
|
|
224
|
+
name: formData.get('name') as string,
|
|
225
|
+
email: formData.get('email') as string,
|
|
226
|
+
message: formData.get('message') as string,
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
const response = await fetch('/api/lead', {
|
|
231
|
+
method: 'POST',
|
|
232
|
+
headers: { 'Content-Type': 'application/json' },
|
|
233
|
+
body: JSON.stringify(data),
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
if (response.ok) {
|
|
237
|
+
setStatus('success');
|
|
238
|
+
e.currentTarget.reset();
|
|
239
|
+
} else {
|
|
240
|
+
setStatus('error');
|
|
241
|
+
}
|
|
242
|
+
} catch {
|
|
243
|
+
setStatus('error');
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (status === 'success') {
|
|
248
|
+
return (
|
|
249
|
+
<div className="rounded-lg bg-green-50 p-6 text-center">
|
|
250
|
+
<p className="text-lg font-medium text-green-800">Thank you for reaching out!</p>
|
|
251
|
+
<p className="mt-2 text-sm text-green-700">We will get back to you shortly.</p>
|
|
252
|
+
</div>
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return (
|
|
257
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
258
|
+
<div>
|
|
259
|
+
<label htmlFor="name" className="block text-sm font-medium text-gray-700">
|
|
260
|
+
Name
|
|
261
|
+
</label>
|
|
262
|
+
<input
|
|
263
|
+
type="text"
|
|
264
|
+
id="name"
|
|
265
|
+
name="name"
|
|
266
|
+
required
|
|
267
|
+
className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500"
|
|
268
|
+
/>
|
|
269
|
+
</div>
|
|
270
|
+
<div>
|
|
271
|
+
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
|
|
272
|
+
Email
|
|
273
|
+
</label>
|
|
274
|
+
<input
|
|
275
|
+
type="email"
|
|
276
|
+
id="email"
|
|
277
|
+
name="email"
|
|
278
|
+
required
|
|
279
|
+
className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500"
|
|
280
|
+
/>
|
|
281
|
+
</div>
|
|
282
|
+
<div>
|
|
283
|
+
<label htmlFor="message" className="block text-sm font-medium text-gray-700">
|
|
284
|
+
Message
|
|
285
|
+
</label>
|
|
286
|
+
<textarea
|
|
287
|
+
id="message"
|
|
288
|
+
name="message"
|
|
289
|
+
rows={4}
|
|
290
|
+
className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500"
|
|
291
|
+
/>
|
|
292
|
+
</div>
|
|
293
|
+
<button
|
|
294
|
+
type="submit"
|
|
295
|
+
disabled={status === 'submitting'}
|
|
296
|
+
className="w-full rounded-md bg-primary-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-primary-500 disabled:opacity-50"
|
|
297
|
+
>
|
|
298
|
+
{status === 'submitting' ? 'Sending...' : '${escapeJsx(ctaText)}'}
|
|
299
|
+
</button>
|
|
300
|
+
{status === 'error' && (
|
|
301
|
+
<p className="text-sm text-red-600">Something went wrong. Please try again.</p>
|
|
302
|
+
)}
|
|
303
|
+
</form>
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
`;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Generate .env.example entries for lead capture provider
|
|
310
|
+
*
|
|
311
|
+
* @param provider - Lead capture provider type
|
|
312
|
+
* @returns Environment variable example lines
|
|
313
|
+
*/
|
|
314
|
+
export function generateLeadCaptureEnvExample(provider) {
|
|
315
|
+
switch (provider) {
|
|
316
|
+
case 'webhook':
|
|
317
|
+
return 'LEAD_WEBHOOK_URL=https://your-webhook-endpoint.com/leads\n';
|
|
318
|
+
case 'resend':
|
|
319
|
+
return 'RESEND_API_KEY=re_xxxxxxxxxxxx\nRESEND_FROM_EMAIL=onboarding@resend.dev\nLEAD_NOTIFICATION_EMAIL=team@example.com\n';
|
|
320
|
+
case 'postmark':
|
|
321
|
+
return 'POSTMARK_API_KEY=xxxxxxxxxxxx\nPOSTMARK_FROM_EMAIL=no-reply@example.com\nLEAD_NOTIFICATION_EMAIL=team@example.com\n';
|
|
322
|
+
default:
|
|
323
|
+
return '';
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
//# sourceMappingURL=website-conversion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"website-conversion.js","sourceRoot":"","sources":["../../../src/generators/templates/website-conversion.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;GAEG;AACH,SAAS,SAAS,CAAC,GAAW;IAC5B,OAAO,GAAG;SACP,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAC3B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CACtC,WAAuD,SAAS;IAEhE,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QACxB,OAAO;;;;;;;;CAQV,CAAC;IACA,CAAC;IAED,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqDV,CAAC;IACA,CAAC;IAED,IAAI,QAAQ,KAAK,UAAU,EAAE,CAAC;QAC5B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqDV,CAAC;IACA,CAAC;IAED,4BAA4B;IAC5B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmDR,CAAC;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAAkC;IAElC,MAAM,OAAO,GAAG,QAAQ,EAAE,kBAAkB,CAAC,UAAU,CAAC,IAAI,IAAI,aAAa,CAAC;IAE9E,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qDA2F4C,SAAS,CAAC,OAAO,CAAC;;;;;;;;CAQtE,CAAC;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,6BAA6B,CAC3C,QAAoD;IAEpD,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,SAAS;YACZ,OAAO,4DAA4D,CAAC;QACtE,KAAK,QAAQ;YACX,OAAO,qHAAqH,CAAC;QAC/H,KAAK,UAAU;YACb,OAAO,qHAAqH,CAAC;QAC/H;YACE,OAAO,EAAE,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Landing page generator with 10 data-driven sections
|
|
3
|
+
* Each section: strategy data -> fallback -> graceful skip
|
|
4
|
+
*/
|
|
5
|
+
import type { WebsiteContentContext } from '../website-context.js';
|
|
6
|
+
import { type SectionRenderInfo } from './website-sections.js';
|
|
7
|
+
/**
|
|
8
|
+
* Result of landing page generation with section metadata for tracing
|
|
9
|
+
*/
|
|
10
|
+
export interface LandingPageResult {
|
|
11
|
+
code: string;
|
|
12
|
+
sections: SectionRenderInfo[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Generate landing page.tsx with 10 data-driven sections
|
|
16
|
+
* Sections: Hero, PainPoints, Differentiators, Features, HowItWorks,
|
|
17
|
+
* Stats, SocialProof, PricingTeaser, FAQ, FinalCTA
|
|
18
|
+
*/
|
|
19
|
+
export declare function generateWebsiteLandingPage(projectName: string, context?: WebsiteContentContext): string;
|
|
20
|
+
/**
|
|
21
|
+
* Generate landing page with section render info for debug tracing
|
|
22
|
+
*/
|
|
23
|
+
export declare function generateWebsiteLandingPageWithInfo(projectName: string, context?: WebsiteContentContext): LandingPageResult;
|
|
24
|
+
//# sourceMappingURL=website-landing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"website-landing.d.ts","sourceRoot":"","sources":["../../../src/generators/templates/website-landing.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EAYL,KAAK,iBAAiB,EACvB,MAAM,uBAAuB,CAAC;AAa/B;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,MAAM,CAGR;AAED;;GAEG;AACH,wBAAgB,kCAAkC,CAChD,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,iBAAiB,CA+QnB"}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Landing page generator with 10 data-driven sections
|
|
3
|
+
* Each section: strategy data -> fallback -> graceful skip
|
|
4
|
+
*/
|
|
5
|
+
import { mapFeatureIcon, generatePainPointsSection, generateDifferentiatorsSection, generateHowItWorksSection, generateStatsSection, generateSocialProofSection, generatePricingTeaserSection, generateFaqSection, buildFaqItemsDeclaration, generateFaqItemComponent, buildFaqSchema, } from './website-sections.js';
|
|
6
|
+
/**
|
|
7
|
+
* Escape a string for safe use inside JSX template literals
|
|
8
|
+
*/
|
|
9
|
+
function escapeJsx(str) {
|
|
10
|
+
return str
|
|
11
|
+
.replace(/\\/g, '\\\\')
|
|
12
|
+
.replace(/'/g, "\\'")
|
|
13
|
+
.replace(/`/g, '\\`')
|
|
14
|
+
.replace(/\$/g, '\\$');
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Generate landing page.tsx with 10 data-driven sections
|
|
18
|
+
* Sections: Hero, PainPoints, Differentiators, Features, HowItWorks,
|
|
19
|
+
* Stats, SocialProof, PricingTeaser, FAQ, FinalCTA
|
|
20
|
+
*/
|
|
21
|
+
export function generateWebsiteLandingPage(projectName, context) {
|
|
22
|
+
const result = generateWebsiteLandingPageWithInfo(projectName, context);
|
|
23
|
+
return result.code;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Generate landing page with section render info for debug tracing
|
|
27
|
+
*/
|
|
28
|
+
export function generateWebsiteLandingPageWithInfo(projectName, context) {
|
|
29
|
+
const title = projectName
|
|
30
|
+
.split('-')
|
|
31
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
32
|
+
.join(' ');
|
|
33
|
+
const strategy = context?.strategy;
|
|
34
|
+
const displayName = context?.productName || title;
|
|
35
|
+
const sections = [];
|
|
36
|
+
// --- Hero data ---
|
|
37
|
+
const headline = strategy?.messaging.headline || displayName;
|
|
38
|
+
const subheadline = strategy?.messaging.subheadline || '';
|
|
39
|
+
const eyebrow = strategy?.positioning.category || '';
|
|
40
|
+
const heroText = strategy?.messaging.longDescription
|
|
41
|
+
? escapeJsx(strategy.messaging.longDescription)
|
|
42
|
+
: context?.description
|
|
43
|
+
? escapeJsx(context.description)
|
|
44
|
+
: null;
|
|
45
|
+
const primaryCtaText = strategy?.conversionStrategy.primaryCta.text || 'Get started';
|
|
46
|
+
const primaryCtaHref = strategy?.conversionStrategy.primaryCta.href || '/pricing';
|
|
47
|
+
const secondaryCtaText = strategy?.conversionStrategy.secondaryCta.text || 'Learn more';
|
|
48
|
+
const secondaryCtaHref = strategy?.conversionStrategy.secondaryCta.href || '/docs';
|
|
49
|
+
sections.push({
|
|
50
|
+
name: 'Hero',
|
|
51
|
+
dataSource: strategy?.messaging ? 'strategy' : heroText ? 'docs' : 'defaults',
|
|
52
|
+
itemCount: 1,
|
|
53
|
+
});
|
|
54
|
+
// --- Features data ---
|
|
55
|
+
const features = context?.features && context.features.length > 0
|
|
56
|
+
? context.features.slice(0, 6)
|
|
57
|
+
: null;
|
|
58
|
+
const featuresHeading = strategy?.positioning.valueProposition || 'Everything you need';
|
|
59
|
+
const featuresSubtitle = context?.description
|
|
60
|
+
? escapeJsx(context.description).slice(0, 120)
|
|
61
|
+
: '';
|
|
62
|
+
sections.push({
|
|
63
|
+
name: 'Features',
|
|
64
|
+
dataSource: features ? 'docs' : 'defaults',
|
|
65
|
+
itemCount: features?.length || 0,
|
|
66
|
+
});
|
|
67
|
+
// --- Build features block ---
|
|
68
|
+
const featuresBlock = features
|
|
69
|
+
? features.map((f) => {
|
|
70
|
+
const icon = mapFeatureIcon(f.title);
|
|
71
|
+
return ` {
|
|
72
|
+
title: '${escapeJsx(f.title)}',
|
|
73
|
+
description: '${escapeJsx(f.description)}',
|
|
74
|
+
icon: '${icon}',
|
|
75
|
+
}`;
|
|
76
|
+
}).join(',\n')
|
|
77
|
+
: '';
|
|
78
|
+
// --- Trust signals ---
|
|
79
|
+
const trustSignals = strategy?.conversionStrategy.trustSignals || [];
|
|
80
|
+
// --- Generate conditional sections ---
|
|
81
|
+
const painPoints = generatePainPointsSection(strategy);
|
|
82
|
+
sections.push(painPoints.info);
|
|
83
|
+
const differentiators = generateDifferentiatorsSection(strategy);
|
|
84
|
+
sections.push(differentiators.info);
|
|
85
|
+
const howItWorks = generateHowItWorksSection(strategy);
|
|
86
|
+
sections.push(howItWorks.info);
|
|
87
|
+
const stats = generateStatsSection(strategy);
|
|
88
|
+
sections.push(stats.info);
|
|
89
|
+
const socialProof = generateSocialProofSection(strategy);
|
|
90
|
+
sections.push(socialProof.info);
|
|
91
|
+
const pricingTeaser = generatePricingTeaserSection(context);
|
|
92
|
+
sections.push(pricingTeaser.info);
|
|
93
|
+
const faq = generateFaqSection(strategy);
|
|
94
|
+
sections.push(faq.info);
|
|
95
|
+
// Final CTA section
|
|
96
|
+
sections.push({
|
|
97
|
+
name: 'FinalCTA',
|
|
98
|
+
dataSource: strategy?.messaging ? 'strategy' : 'defaults',
|
|
99
|
+
itemCount: 1,
|
|
100
|
+
});
|
|
101
|
+
// Determine which lucide icons are needed
|
|
102
|
+
const iconSet = new Set();
|
|
103
|
+
if (features) {
|
|
104
|
+
features.forEach((f) => iconSet.add(mapFeatureIcon(f.title)));
|
|
105
|
+
}
|
|
106
|
+
// Pain points icons
|
|
107
|
+
if (painPoints.jsx) {
|
|
108
|
+
iconSet.add('AlertTriangle');
|
|
109
|
+
iconSet.add('XCircle');
|
|
110
|
+
iconSet.add('AlertOctagon');
|
|
111
|
+
}
|
|
112
|
+
// Differentiators
|
|
113
|
+
if (differentiators.jsx) {
|
|
114
|
+
iconSet.add('CheckCircle');
|
|
115
|
+
}
|
|
116
|
+
// Stats
|
|
117
|
+
if (stats.jsx) {
|
|
118
|
+
iconSet.add('CheckCircle');
|
|
119
|
+
}
|
|
120
|
+
// FAQ
|
|
121
|
+
if (faq.jsx) {
|
|
122
|
+
iconSet.add('ChevronDown');
|
|
123
|
+
}
|
|
124
|
+
// Always useful
|
|
125
|
+
iconSet.add('ArrowRight');
|
|
126
|
+
const iconImports = Array.from(iconSet).sort().join(', ');
|
|
127
|
+
// SEO metadata
|
|
128
|
+
const metaTitle = strategy?.seoStrategy.titleTemplates?.home || 'Welcome';
|
|
129
|
+
const metaDesc = strategy?.seoStrategy.metaDescriptions?.home || `Welcome to ${displayName}`;
|
|
130
|
+
// FAQ data declarations
|
|
131
|
+
const faqItemsDecl = buildFaqItemsDeclaration(strategy);
|
|
132
|
+
const faqItemComponent = faq.needsClientDirective ? generateFaqItemComponent() : '';
|
|
133
|
+
const faqSchemaDecl = buildFaqSchema(strategy);
|
|
134
|
+
// Build icon mapping for features
|
|
135
|
+
const iconComponentMap = features
|
|
136
|
+
? `const ICON_MAP: Record<string, React.ElementType> = {\n${Array.from(new Set(features.map(f => mapFeatureIcon(f.title)))).map(icon => ` ${icon},`).join('\n')}\n};\n`
|
|
137
|
+
: '';
|
|
138
|
+
const code = `${faq.needsClientDirective ? "'use client';\n\nimport { useState } from 'react';\n" : ''}import type { Metadata } from 'next';
|
|
139
|
+
import Link from 'next/link';
|
|
140
|
+
import { ${iconImports} } from 'lucide-react';
|
|
141
|
+
import Header from '@/components/Header';
|
|
142
|
+
import Footer from '@/components/Footer';
|
|
143
|
+
import JsonLd from '@/components/JsonLd';
|
|
144
|
+
|
|
145
|
+
export const metadata: Metadata = {
|
|
146
|
+
title: '${escapeJsx(metaTitle)}',
|
|
147
|
+
description: '${escapeJsx(metaDesc)}',
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const ORG_SCHEMA = {
|
|
151
|
+
'@context': 'https://schema.org',
|
|
152
|
+
'@type': 'Organization',
|
|
153
|
+
name: '${escapeJsx(displayName)}',
|
|
154
|
+
url: process.env.NEXT_PUBLIC_SITE_URL || 'https://${projectName}.com',
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const PRODUCT_SCHEMA = {
|
|
158
|
+
'@context': 'https://schema.org',
|
|
159
|
+
'@type': 'SoftwareApplication',
|
|
160
|
+
name: '${escapeJsx(displayName)}',
|
|
161
|
+
applicationCategory: 'BusinessApplication',
|
|
162
|
+
operatingSystem: 'Web',
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
${faqSchemaDecl ? faqSchemaDecl + '\n' : ''}${faqItemsDecl ? '\n' + faqItemsDecl : ''}${iconComponentMap ? '\n' + iconComponentMap : ''}${features ? `\nconst features = [\n${featuresBlock}\n];\n` : ''}
|
|
166
|
+
${faqItemComponent ? '\n' + faqItemComponent + '\n' : ''}
|
|
167
|
+
export default function HomePage() {
|
|
168
|
+
return (
|
|
169
|
+
<>
|
|
170
|
+
<Header />
|
|
171
|
+
<JsonLd schema={ORG_SCHEMA} />
|
|
172
|
+
<JsonLd schema={PRODUCT_SCHEMA} />
|
|
173
|
+
${faqSchemaDecl ? ' <JsonLd schema={FAQ_SCHEMA} />\n' : ''} <main className="flex min-h-screen flex-col">
|
|
174
|
+
{/* Hero Section */}
|
|
175
|
+
<section className="relative overflow-hidden bg-gradient-to-br from-primary-50 via-white to-primary-50/30 py-24 sm:py-36">
|
|
176
|
+
<div className="container">
|
|
177
|
+
<div className="mx-auto max-w-3xl text-center">
|
|
178
|
+
${eyebrow ? ` <p className="mb-4 inline-block rounded-full bg-primary-100 px-4 py-1.5 text-sm font-medium text-primary-700">\n ${escapeJsx(eyebrow)}\n </p>\n` : ''} <h1 className="text-4xl font-bold tracking-tight text-foreground sm:text-6xl lg:text-7xl">
|
|
179
|
+
${escapeJsx(headline)}
|
|
180
|
+
</h1>
|
|
181
|
+
${subheadline ? ` <p className="mt-4 text-xl font-medium text-primary-600">\n ${escapeJsx(subheadline)}\n </p>` : ''}
|
|
182
|
+
<p className="mt-6 text-lg leading-8 text-muted-foreground">
|
|
183
|
+
${heroText || ''}
|
|
184
|
+
</p>
|
|
185
|
+
<div className="mt-10 flex items-center justify-center gap-x-4">
|
|
186
|
+
<Link
|
|
187
|
+
href="${escapeJsx(primaryCtaHref)}"
|
|
188
|
+
className="rounded-lg bg-primary-600 px-6 py-3 text-sm font-semibold text-white shadow-lg shadow-primary-600/25 hover:bg-primary-500 transition-all hover:shadow-primary-600/40 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600"
|
|
189
|
+
>
|
|
190
|
+
${escapeJsx(primaryCtaText)}
|
|
191
|
+
</Link>
|
|
192
|
+
<Link
|
|
193
|
+
href="${escapeJsx(secondaryCtaHref)}"
|
|
194
|
+
className="group flex items-center gap-1 text-sm font-semibold text-foreground hover:text-primary-600 transition-colors"
|
|
195
|
+
>
|
|
196
|
+
${escapeJsx(secondaryCtaText)}
|
|
197
|
+
<ArrowRight className="h-4 w-4 transition-transform group-hover:translate-x-0.5" />
|
|
198
|
+
</Link>
|
|
199
|
+
</div>
|
|
200
|
+
${trustSignals.length > 0 ? ` <div className="mt-10 flex flex-wrap items-center justify-center gap-x-8 gap-y-2">
|
|
201
|
+
${trustSignals.map(s => ` <p className="text-sm font-medium text-muted-foreground">${escapeJsx(s)}</p>`).join('\n')}
|
|
202
|
+
</div>` : ''}
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
</section>
|
|
206
|
+
${painPoints.jsx}${differentiators.jsx}
|
|
207
|
+
{/* Features Section */}
|
|
208
|
+
${features ? ` <section id="features" className="py-20 sm:py-28">
|
|
209
|
+
<div className="container">
|
|
210
|
+
<div className="mx-auto max-w-2xl text-center">
|
|
211
|
+
<h2 className="text-3xl font-bold tracking-tight text-foreground sm:text-4xl">
|
|
212
|
+
${escapeJsx(featuresHeading)}
|
|
213
|
+
</h2>
|
|
214
|
+
${featuresSubtitle ? ` <p className="mt-4 text-lg text-muted-foreground">\n ${featuresSubtitle}\n </p>` : ''}
|
|
215
|
+
</div>
|
|
216
|
+
<div className="mx-auto mt-16 max-w-5xl">
|
|
217
|
+
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3">
|
|
218
|
+
{features.map((feature) => {
|
|
219
|
+
const Icon = ICON_MAP[feature.icon] || Star;
|
|
220
|
+
return (
|
|
221
|
+
<div
|
|
222
|
+
key={feature.title}
|
|
223
|
+
className="group rounded-2xl border border-border bg-card p-8 transition-all hover:shadow-lg hover:-translate-y-1"
|
|
224
|
+
>
|
|
225
|
+
<div className="mb-4 flex h-10 w-10 items-center justify-center rounded-lg bg-primary-100">
|
|
226
|
+
<Icon className="h-5 w-5 text-primary-600" />
|
|
227
|
+
</div>
|
|
228
|
+
<h3 className="text-lg font-semibold text-foreground">
|
|
229
|
+
{feature.title}
|
|
230
|
+
</h3>
|
|
231
|
+
<p className="mt-2 text-muted-foreground">{feature.description}</p>
|
|
232
|
+
</div>
|
|
233
|
+
);
|
|
234
|
+
})}
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
</section>` : ''}
|
|
239
|
+
${howItWorks.jsx}${stats.jsx}${socialProof.jsx}${pricingTeaser.jsx}${faq.jsx}
|
|
240
|
+
{/* Final CTA Section */}
|
|
241
|
+
<section className="bg-gradient-to-br from-primary-600 to-primary-700 py-20 sm:py-28">
|
|
242
|
+
<div className="container text-center">
|
|
243
|
+
<h2 className="text-3xl font-bold tracking-tight text-white sm:text-4xl">
|
|
244
|
+
${strategy?.messaging.elevatorPitch ? escapeJsx(strategy.messaging.elevatorPitch) : 'Ready to get started?'}
|
|
245
|
+
</h2>
|
|
246
|
+
<p className="mt-4 text-lg text-primary-100">
|
|
247
|
+
${strategy?.messaging.subheadline ? escapeJsx(strategy.messaging.subheadline) : 'Start building today.'}
|
|
248
|
+
</p>
|
|
249
|
+
<div className="mt-8 flex items-center justify-center gap-x-4">
|
|
250
|
+
<Link
|
|
251
|
+
href="${escapeJsx(primaryCtaHref)}"
|
|
252
|
+
className="rounded-lg bg-white px-6 py-3 text-sm font-semibold text-primary-600 shadow-lg hover:bg-primary-50 transition-colors"
|
|
253
|
+
>
|
|
254
|
+
${escapeJsx(primaryCtaText)}
|
|
255
|
+
</Link>
|
|
256
|
+
<Link
|
|
257
|
+
href="${escapeJsx(secondaryCtaHref)}"
|
|
258
|
+
className="rounded-lg border border-primary-300 px-6 py-3 text-sm font-semibold text-white hover:bg-primary-500 transition-colors"
|
|
259
|
+
>
|
|
260
|
+
${escapeJsx(secondaryCtaText)}
|
|
261
|
+
</Link>
|
|
262
|
+
</div>
|
|
263
|
+
${trustSignals.length > 0 ? ` <div className="mt-8 flex flex-wrap items-center justify-center gap-x-6 gap-y-2">
|
|
264
|
+
${trustSignals.map(s => ` <p className="text-sm text-primary-200">${escapeJsx(s)}</p>`).join('\n')}
|
|
265
|
+
</div>` : ''}
|
|
266
|
+
</div>
|
|
267
|
+
</section>
|
|
268
|
+
</main>
|
|
269
|
+
<Footer />
|
|
270
|
+
</>
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
`;
|
|
274
|
+
return { code, sections };
|
|
275
|
+
}
|
|
276
|
+
//# sourceMappingURL=website-landing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"website-landing.js","sourceRoot":"","sources":["../../../src/generators/templates/website-landing.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,cAAc,EACd,yBAAyB,EACzB,8BAA8B,EAC9B,yBAAyB,EACzB,oBAAoB,EACpB,0BAA0B,EAC1B,4BAA4B,EAC5B,kBAAkB,EAClB,wBAAwB,EACxB,wBAAwB,EACxB,cAAc,GAEf,MAAM,uBAAuB,CAAC;AAE/B;;GAEG;AACH,SAAS,SAAS,CAAC,GAAW;IAC5B,OAAO,GAAG;SACP,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAC3B,CAAC;AAUD;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CACxC,WAAmB,EACnB,OAA+B;IAE/B,MAAM,MAAM,GAAG,kCAAkC,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IACxE,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kCAAkC,CAChD,WAAmB,EACnB,OAA+B;IAE/B,MAAM,KAAK,GAAG,WAAW;SACtB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SAClD,IAAI,CAAC,GAAG,CAAC,CAAC;IAEb,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,CAAC;IACnC,MAAM,WAAW,GAAG,OAAO,EAAE,WAAW,IAAI,KAAK,CAAC;IAClD,MAAM,QAAQ,GAAwB,EAAE,CAAC;IAEzC,oBAAoB;IACpB,MAAM,QAAQ,GAAG,QAAQ,EAAE,SAAS,CAAC,QAAQ,IAAI,WAAW,CAAC;IAC7D,MAAM,WAAW,GAAG,QAAQ,EAAE,SAAS,CAAC,WAAW,IAAI,EAAE,CAAC;IAC1D,MAAM,OAAO,GAAG,QAAQ,EAAE,WAAW,CAAC,QAAQ,IAAI,EAAE,CAAC;IACrD,MAAM,QAAQ,GAAG,QAAQ,EAAE,SAAS,CAAC,eAAe;QAClD,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,eAAe,CAAC;QAC/C,CAAC,CAAC,OAAO,EAAE,WAAW;YACtB,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,WAAW,CAAC;YAChC,CAAC,CAAC,IAAI,CAAC;IAET,MAAM,cAAc,GAAG,QAAQ,EAAE,kBAAkB,CAAC,UAAU,CAAC,IAAI,IAAI,aAAa,CAAC;IACrF,MAAM,cAAc,GAAG,QAAQ,EAAE,kBAAkB,CAAC,UAAU,CAAC,IAAI,IAAI,UAAU,CAAC;IAClF,MAAM,gBAAgB,GAAG,QAAQ,EAAE,kBAAkB,CAAC,YAAY,CAAC,IAAI,IAAI,YAAY,CAAC;IACxF,MAAM,gBAAgB,GAAG,QAAQ,EAAE,kBAAkB,CAAC,YAAY,CAAC,IAAI,IAAI,OAAO,CAAC;IAEnF,QAAQ,CAAC,IAAI,CAAC;QACZ,IAAI,EAAE,MAAM;QACZ,UAAU,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU;QAC7E,SAAS,EAAE,CAAC;KACb,CAAC,CAAC;IAEH,wBAAwB;IACxB,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;QAC/D,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;QAC9B,CAAC,CAAC,IAAI,CAAC;IAET,MAAM,eAAe,GAAG,QAAQ,EAAE,WAAW,CAAC,gBAAgB,IAAI,qBAAqB,CAAC;IACxF,MAAM,gBAAgB,GAAG,OAAO,EAAE,WAAW;QAC3C,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;QAC9C,CAAC,CAAC,EAAE,CAAC;IAEP,QAAQ,CAAC,IAAI,CAAC;QACZ,IAAI,EAAE,UAAU;QAChB,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU;QAC1C,SAAS,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;KACjC,CAAC,CAAC;IAEH,+BAA+B;IAC/B,MAAM,aAAa,GAAG,QAAQ;QAC5B,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACjB,MAAM,IAAI,GAAG,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YACrC,OAAO;4BACa,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC;kCACZ,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC;2BAC/B,IAAI;kBACb,CAAC;QACb,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;QAChB,CAAC,CAAC,EAAE,CAAC;IAEP,wBAAwB;IACxB,MAAM,YAAY,GAAG,QAAQ,EAAE,kBAAkB,CAAC,YAAY,IAAI,EAAE,CAAC;IAErE,wCAAwC;IACxC,MAAM,UAAU,GAAG,yBAAyB,CAAC,QAAQ,CAAC,CAAC;IACvD,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAE/B,MAAM,eAAe,GAAG,8BAA8B,CAAC,QAAQ,CAAC,CAAC;IACjE,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IAEpC,MAAM,UAAU,GAAG,yBAAyB,CAAC,QAAQ,CAAC,CAAC;IACvD,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAE/B,MAAM,KAAK,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAC7C,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAE1B,MAAM,WAAW,GAAG,0BAA0B,CAAC,QAAQ,CAAC,CAAC;IACzD,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAEhC,MAAM,aAAa,GAAG,4BAA4B,CAAC,OAAO,CAAC,CAAC;IAC5D,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAElC,MAAM,GAAG,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IACzC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAExB,oBAAoB;IACpB,QAAQ,CAAC,IAAI,CAAC;QACZ,IAAI,EAAE,UAAU;QAChB,UAAU,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU;QACzD,SAAS,EAAE,CAAC;KACb,CAAC,CAAC;IAEH,0CAA0C;IAC1C,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,IAAI,QAAQ,EAAE,CAAC;QACb,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAChE,CAAC;IACD,oBAAoB;IACpB,IAAI,UAAU,CAAC,GAAG,EAAE,CAAC;QACnB,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAC9B,CAAC;IACD,kBAAkB;IAClB,IAAI,eAAe,CAAC,GAAG,EAAE,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAC7B,CAAC;IACD,QAAQ;IACR,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAC7B,CAAC;IACD,MAAM;IACN,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAC7B,CAAC;IACD,gBAAgB;IAChB,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAE1B,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE1D,eAAe;IACf,MAAM,SAAS,GAAG,QAAQ,EAAE,WAAW,CAAC,cAAc,EAAE,IAAI,IAAI,SAAS,CAAC;IAC1E,MAAM,QAAQ,GAAG,QAAQ,EAAE,WAAW,CAAC,gBAAgB,EAAE,IAAI,IAAI,cAAc,WAAW,EAAE,CAAC;IAE7F,wBAAwB;IACxB,MAAM,YAAY,GAAG,wBAAwB,CAAC,QAAQ,CAAC,CAAC;IACxD,MAAM,gBAAgB,GAAG,GAAG,CAAC,oBAAoB,CAAC,CAAC,CAAC,wBAAwB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACpF,MAAM,aAAa,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IAE/C,kCAAkC;IAClC,MAAM,gBAAgB,GAAG,QAAQ;QAC/B,CAAC,CAAC,0DAA0D,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ;QACxK,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,IAAI,GAAG,GAAG,GAAG,CAAC,oBAAoB,CAAC,CAAC,CAAC,sDAAsD,CAAC,CAAC,CAAC,EAAE;;WAE7F,WAAW;;;;;;YAMV,SAAS,CAAC,SAAS,CAAC;kBACd,SAAS,CAAC,QAAQ,CAAC;;;;;;WAM1B,SAAS,CAAC,WAAW,CAAC;sDACqB,WAAW;;;;;;WAMtD,SAAS,CAAC,WAAW,CAAC;;;;;EAK/B,aAAa,CAAC,CAAC,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC,IAAI,GAAG,YAAY,CAAC,CAAC,CAAC,EAAE,GAAG,gBAAgB,CAAC,CAAC,CAAC,IAAI,GAAG,gBAAgB,CAAC,CAAC,CAAC,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,yBAAyB,aAAa,QAAQ,CAAC,CAAC,CAAC,EAAE;EACtM,gBAAgB,CAAC,CAAC,CAAC,IAAI,GAAG,gBAAgB,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE;;;;;;;EAOtD,aAAa,CAAC,CAAC,CAAC,wCAAwC,CAAC,CAAC,CAAC,EAAE;;;;;EAK7D,OAAO,CAAC,CAAC,CAAC,iJAAiJ,SAAS,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC,CAAC,EAAE;kBAC1L,SAAS,CAAC,QAAQ,CAAC;;EAEnC,WAAW,CAAC,CAAC,CAAC,4FAA4F,SAAS,CAAC,WAAW,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE;;kBAE3I,QAAQ,IAAI,EAAE;;;;0BAIN,SAAS,CAAC,cAAc,CAAC;;;oBAG/B,SAAS,CAAC,cAAc,CAAC;;;0BAGnB,SAAS,CAAC,gBAAgB,CAAC;;;oBAGjC,SAAS,CAAC,gBAAgB,CAAC;;;;EAI7C,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;EAC1B,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,4EAA4E,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;qBAC7G,CAAC,CAAC,CAAC,EAAE;;;;EAIxB,UAAU,CAAC,GAAG,GAAG,eAAe,CAAC,GAAG;;EAEpC,QAAQ,CAAC,CAAC,CAAC;;;;kBAIK,SAAS,CAAC,eAAe,CAAC;;EAE1C,gBAAgB,CAAC,CAAC,CAAC,qFAAqF,gBAAgB,sBAAsB,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;mBAwBlI,CAAC,CAAC,CAAC,EAAE;EACtB,UAAU,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,GAAG,WAAW,CAAC,GAAG,GAAG,aAAa,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG;;;;;gBAK5D,QAAQ,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,uBAAuB;;;gBAGzG,QAAQ,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,uBAAuB;;;;wBAI7F,SAAS,CAAC,cAAc,CAAC;;;kBAG/B,SAAS,CAAC,cAAc,CAAC;;;wBAGnB,SAAS,CAAC,gBAAgB,CAAC;;;kBAGjC,SAAS,CAAC,gBAAgB,CAAC;;;EAG3C,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;EAC1B,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,yDAAyD,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;mBAC5F,CAAC,CAAC,CAAC,EAAE;;;;;;;;CAQvB,CAAC;IAEA,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC5B,CAAC"}
|