@supunkalharajayasinghe/project-cli 1.3.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 +380 -0
- package/dist/commands/create.d.ts +3 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +48 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/greet.d.ts +3 -0
- package/dist/commands/greet.d.ts.map +1 -0
- package/dist/commands/greet.js +10 -0
- package/dist/commands/greet.js.map +1 -0
- package/dist/generators/applyAiPlan.d.ts +3 -0
- package/dist/generators/applyAiPlan.d.ts.map +1 -0
- package/dist/generators/applyAiPlan.js +426 -0
- package/dist/generators/applyAiPlan.js.map +1 -0
- package/dist/generators/applyFeatureModules.d.ts +4 -0
- package/dist/generators/applyFeatureModules.d.ts.map +1 -0
- package/dist/generators/applyFeatureModules.js +242 -0
- package/dist/generators/applyFeatureModules.js.map +1 -0
- package/dist/generators/applyFullstackPlan.d.ts +3 -0
- package/dist/generators/applyFullstackPlan.d.ts.map +1 -0
- package/dist/generators/applyFullstackPlan.js +413 -0
- package/dist/generators/applyFullstackPlan.js.map +1 -0
- package/dist/generators/applyPlan.d.ts +3 -0
- package/dist/generators/applyPlan.d.ts.map +1 -0
- package/dist/generators/applyPlan.js +22 -0
- package/dist/generators/applyPlan.js.map +1 -0
- package/dist/generators/applyWebsitePlan.d.ts +3 -0
- package/dist/generators/applyWebsitePlan.d.ts.map +1 -0
- package/dist/generators/applyWebsitePlan.js +1053 -0
- package/dist/generators/applyWebsitePlan.js.map +1 -0
- package/dist/generators/createNextBase.d.ts +3 -0
- package/dist/generators/createNextBase.d.ts.map +1 -0
- package/dist/generators/createNextBase.js +20 -0
- package/dist/generators/createNextBase.js.map +1 -0
- package/dist/generators/generateBasicProject.d.ts +3 -0
- package/dist/generators/generateBasicProject.d.ts.map +1 -0
- package/dist/generators/generateBasicProject.js +65 -0
- package/dist/generators/generateBasicProject.js.map +1 -0
- package/dist/generators/starterBranding.d.ts +9 -0
- package/dist/generators/starterBranding.d.ts.map +1 -0
- package/dist/generators/starterBranding.js +48 -0
- package/dist/generators/starterBranding.js.map +1 -0
- package/dist/generators/writeGeneratedReadme.d.ts +3 -0
- package/dist/generators/writeGeneratedReadme.d.ts.map +1 -0
- package/dist/generators/writeGeneratedReadme.js +99 -0
- package/dist/generators/writeGeneratedReadme.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/askBasicScaffoldOptions.d.ts +3 -0
- package/dist/prompts/askBasicScaffoldOptions.d.ts.map +1 -0
- package/dist/prompts/askBasicScaffoldOptions.js +36 -0
- package/dist/prompts/askBasicScaffoldOptions.js.map +1 -0
- package/dist/prompts/askCreatePlan.d.ts +3 -0
- package/dist/prompts/askCreatePlan.d.ts.map +1 -0
- package/dist/prompts/askCreatePlan.js +232 -0
- package/dist/prompts/askCreatePlan.js.map +1 -0
- package/dist/types.d.ts +64 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/files.d.ts +14 -0
- package/dist/utils/files.d.ts.map +1 -0
- package/dist/utils/files.js +47 -0
- package/dist/utils/files.js.map +1 -0
- package/dist/utils/generateSecret.d.ts +2 -0
- package/dist/utils/generateSecret.d.ts.map +1 -0
- package/dist/utils/generateSecret.js +5 -0
- package/dist/utils/generateSecret.js.map +1 -0
- package/dist/utils/pathExists.d.ts +2 -0
- package/dist/utils/pathExists.d.ts.map +1 -0
- package/dist/utils/pathExists.js +11 -0
- package/dist/utils/pathExists.js.map +1 -0
- package/dist/utils/runCommand.d.ts +8 -0
- package/dist/utils/runCommand.d.ts.map +1 -0
- package/dist/utils/runCommand.js +59 -0
- package/dist/utils/runCommand.js.map +1 -0
- package/dist/utils/strings.d.ts +3 -0
- package/dist/utils/strings.d.ts.map +1 -0
- package/dist/utils/strings.js +15 -0
- package/dist/utils/strings.js.map +1 -0
- package/dist/utils/validateFreeText.d.ts +13 -0
- package/dist/utils/validateFreeText.d.ts.map +1 -0
- package/dist/utils/validateFreeText.js +19 -0
- package/dist/utils/validateFreeText.js.map +1 -0
- package/dist/utils/validateProjectName.d.ts +7 -0
- package/dist/utils/validateProjectName.d.ts.map +1 -0
- package/dist/utils/validateProjectName.js +42 -0
- package/dist/utils/validateProjectName.js.map +1 -0
- package/dist/utils/writeJson.d.ts +2 -0
- package/dist/utils/writeJson.d.ts.map +1 -0
- package/dist/utils/writeJson.js +2 -0
- package/dist/utils/writeJson.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1,1053 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { writeFileSafe } from '../utils/files.js';
|
|
3
|
+
import { toPascal, toTitle } from '../utils/strings.js';
|
|
4
|
+
import { starterBranding } from './starterBranding.js';
|
|
5
|
+
export async function applyWebsitePlan(plan) {
|
|
6
|
+
if (plan.blueprint !== 'website') {
|
|
7
|
+
throw new Error('applyWebsitePlan can only be used with website plans.');
|
|
8
|
+
}
|
|
9
|
+
await writeLayout(plan);
|
|
10
|
+
await writeSiteConfig(plan);
|
|
11
|
+
await writeNavbar(plan);
|
|
12
|
+
await writeFooter(plan);
|
|
13
|
+
if (plan.modules.includes('contact-form')) {
|
|
14
|
+
await writeContactForm(plan);
|
|
15
|
+
}
|
|
16
|
+
if (plan.shape === 'one-page') {
|
|
17
|
+
await writeOnePageWebsite(plan);
|
|
18
|
+
}
|
|
19
|
+
if (plan.shape === 'multi-page') {
|
|
20
|
+
await writeMultiPageWebsite(plan);
|
|
21
|
+
}
|
|
22
|
+
if (plan.modules.includes('seo')) {
|
|
23
|
+
await writeSeoFiles(plan);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function getWebsiteSections(plan) {
|
|
27
|
+
const sections = plan.sections.length > 0
|
|
28
|
+
? [...plan.sections]
|
|
29
|
+
: ['hero', 'features', 'contact'];
|
|
30
|
+
if (plan.modules.includes('contact-form') && !sections.includes('contact')) {
|
|
31
|
+
sections.push('contact');
|
|
32
|
+
}
|
|
33
|
+
return sections;
|
|
34
|
+
}
|
|
35
|
+
function getWebsitePages(plan) {
|
|
36
|
+
const pages = plan.pages.length > 0 ? [...plan.pages] : ['home', 'about', 'contact'];
|
|
37
|
+
if (!pages.includes('home')) {
|
|
38
|
+
pages.unshift('home');
|
|
39
|
+
}
|
|
40
|
+
if (plan.modules.includes('contact-form') && !pages.includes('contact')) {
|
|
41
|
+
pages.push('contact');
|
|
42
|
+
}
|
|
43
|
+
return pages;
|
|
44
|
+
}
|
|
45
|
+
async function writeLayout(plan) {
|
|
46
|
+
await writeFileSafe(path.join(plan.targetPath, 'src/app/layout.tsx'), `import type { Metadata } from 'next';
|
|
47
|
+
import type { ReactNode } from 'react';
|
|
48
|
+
import './globals.css';
|
|
49
|
+
|
|
50
|
+
export const metadata: Metadata = {
|
|
51
|
+
title: ${JSON.stringify(toTitle(plan.projectName))},
|
|
52
|
+
description: 'Generated by ts-cli-tool',
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export default function RootLayout({
|
|
56
|
+
children,
|
|
57
|
+
}: Readonly<{
|
|
58
|
+
children: ReactNode;
|
|
59
|
+
}>) {
|
|
60
|
+
return (
|
|
61
|
+
<html lang="en">
|
|
62
|
+
<body className="antialiased">{children}</body>
|
|
63
|
+
</html>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
`);
|
|
67
|
+
}
|
|
68
|
+
async function writeSiteConfig(plan) {
|
|
69
|
+
await writeFileSafe(path.join(plan.targetPath, 'src/lib/site-config.ts'), `export const siteConfig = {
|
|
70
|
+
name: ${JSON.stringify(toTitle(plan.projectName))},
|
|
71
|
+
description: 'Generated website starter.',
|
|
72
|
+
url: 'https://example.com',
|
|
73
|
+
} as const;
|
|
74
|
+
`);
|
|
75
|
+
}
|
|
76
|
+
async function writeNavbar(plan) {
|
|
77
|
+
const links = plan.shape === 'one-page'
|
|
78
|
+
? getWebsiteSections(plan)
|
|
79
|
+
.map((section) => ` <a href="#${section}" className="text-slate-600 hover:text-slate-900 transition font-medium">${toTitle(section)}</a>`)
|
|
80
|
+
.join('\n')
|
|
81
|
+
: getWebsitePages(plan)
|
|
82
|
+
.map((page) => {
|
|
83
|
+
const href = page === 'home' ? '/' : `/${page}`;
|
|
84
|
+
return ` <Link href="${href}" className="text-slate-600 hover:text-slate-900 transition font-medium">${toTitle(page)}</Link>`;
|
|
85
|
+
})
|
|
86
|
+
.join('\n');
|
|
87
|
+
const importLink = plan.shape === 'multi-page' ? `import Link from 'next/link';\n` : '';
|
|
88
|
+
const contactHref = plan.shape === 'one-page' ? '#contact' : '/contact';
|
|
89
|
+
const ctaElement = plan.shape === 'one-page'
|
|
90
|
+
? `<a
|
|
91
|
+
href="${contactHref}"
|
|
92
|
+
className="inline-flex items-center gap-1.5 rounded-full bg-blue-600 px-4 py-2 text-xs font-semibold text-white shadow-sm hover:bg-blue-500 transition duration-200"
|
|
93
|
+
>
|
|
94
|
+
Start Editing
|
|
95
|
+
<ArrowRight className="h-3.5 w-3.5" />
|
|
96
|
+
</a>`
|
|
97
|
+
: `<Link
|
|
98
|
+
href="${contactHref}"
|
|
99
|
+
className="inline-flex items-center gap-1.5 rounded-full bg-blue-600 px-4 py-2 text-xs font-semibold text-white shadow-sm hover:bg-blue-500 transition duration-200"
|
|
100
|
+
>
|
|
101
|
+
Start Editing
|
|
102
|
+
<ArrowRight className="h-3.5 w-3.5" />
|
|
103
|
+
</Link>`;
|
|
104
|
+
await writeFileSafe(path.join(plan.targetPath, 'src/components/layout/Navbar.tsx'), `${importLink}import { siteConfig } from '@/lib/site-config';
|
|
105
|
+
import { Layers, ArrowRight } from 'lucide-react';
|
|
106
|
+
|
|
107
|
+
export function Navbar() {
|
|
108
|
+
return (
|
|
109
|
+
<header className="sticky top-0 z-50 w-full border-b border-slate-200/80 bg-white/80 backdrop-blur-md">
|
|
110
|
+
<nav className="mx-auto flex max-w-6xl items-center justify-between px-6 py-4">
|
|
111
|
+
<div className="flex items-center gap-2">
|
|
112
|
+
<Layers className="h-6 w-6 text-blue-600" />
|
|
113
|
+
<span className="text-base font-bold tracking-tight text-slate-900">
|
|
114
|
+
{siteConfig.name}
|
|
115
|
+
</span>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<div className="flex items-center gap-6">
|
|
119
|
+
<div className="hidden md:flex gap-6 text-sm">
|
|
120
|
+
${links}
|
|
121
|
+
</div>
|
|
122
|
+
${ctaElement}
|
|
123
|
+
</div>
|
|
124
|
+
</nav>
|
|
125
|
+
</header>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
`);
|
|
129
|
+
}
|
|
130
|
+
async function writeFooter(plan) {
|
|
131
|
+
await writeFileSafe(path.join(plan.targetPath, 'src/components/layout/Footer.tsx'), `import { siteConfig } from '@/lib/site-config';
|
|
132
|
+
import { Github, ExternalLink, Sparkles } from 'lucide-react';
|
|
133
|
+
|
|
134
|
+
export function Footer() {
|
|
135
|
+
return (
|
|
136
|
+
<footer className="border-t border-slate-200/80 bg-slate-50/50 py-12 px-6">
|
|
137
|
+
<div className="mx-auto max-w-6xl">
|
|
138
|
+
<div className="flex flex-col md:flex-row items-center justify-between gap-6">
|
|
139
|
+
<div className="text-center md:text-left">
|
|
140
|
+
<div className="flex items-center justify-center md:justify-start gap-2">
|
|
141
|
+
<Sparkles className="h-5 w-5 text-blue-600" />
|
|
142
|
+
<h3 className="font-bold text-slate-900 text-base">{siteConfig.name}</h3>
|
|
143
|
+
</div>
|
|
144
|
+
<p className="mt-1.5 text-sm text-slate-500 max-w-md">
|
|
145
|
+
A premium, production-shaped starter template generated to jumpstart your development workflow.
|
|
146
|
+
</p>
|
|
147
|
+
</div>
|
|
148
|
+
<div className="flex gap-6 text-sm">
|
|
149
|
+
<a
|
|
150
|
+
href="${starterBranding.githubUrl}"
|
|
151
|
+
target="_blank"
|
|
152
|
+
rel="noopener noreferrer"
|
|
153
|
+
className="inline-flex items-center gap-1.5 text-slate-500 hover:text-slate-900 transition font-medium"
|
|
154
|
+
>
|
|
155
|
+
<Github className="h-4 w-4" />
|
|
156
|
+
GitHub
|
|
157
|
+
</a>
|
|
158
|
+
<a
|
|
159
|
+
href="${starterBranding.mediumUrl}"
|
|
160
|
+
target="_blank"
|
|
161
|
+
rel="noopener noreferrer"
|
|
162
|
+
className="inline-flex items-center gap-1.5 text-slate-500 hover:text-slate-900 transition font-medium"
|
|
163
|
+
>
|
|
164
|
+
<ExternalLink className="h-4 w-4" />
|
|
165
|
+
Medium Docs
|
|
166
|
+
</a>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
<div className="mt-8 pt-8 border-t border-slate-200/60 flex flex-col sm:flex-row items-center justify-between gap-4 text-xs text-slate-400">
|
|
170
|
+
<div>
|
|
171
|
+
© {new Date().getFullYear()} {siteConfig.name}. All rights reserved.
|
|
172
|
+
</div>
|
|
173
|
+
<div className="flex items-center gap-1.5">
|
|
174
|
+
<span>Generated with ${starterBranding.cliName} ${starterBranding.version}</span>
|
|
175
|
+
<span className="text-slate-300">•</span>
|
|
176
|
+
<span className="font-medium text-slate-500">${starterBranding.releaseName}</span>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
</footer>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
`);
|
|
184
|
+
}
|
|
185
|
+
async function writeOnePageWebsite(plan) {
|
|
186
|
+
const sections = getWebsiteSections(plan);
|
|
187
|
+
for (const section of sections) {
|
|
188
|
+
await writeSectionComponent(plan, section);
|
|
189
|
+
}
|
|
190
|
+
const imports = sections
|
|
191
|
+
.map((section) => {
|
|
192
|
+
const componentName = `${toPascal(section)}Section`;
|
|
193
|
+
return `import { ${componentName} } from '@/components/sections/${componentName}';`;
|
|
194
|
+
})
|
|
195
|
+
.join('\n');
|
|
196
|
+
const renderedSections = sections
|
|
197
|
+
.map((section) => {
|
|
198
|
+
const componentName = `${toPascal(section)}Section`;
|
|
199
|
+
return ` <${componentName} />`;
|
|
200
|
+
})
|
|
201
|
+
.join('\n');
|
|
202
|
+
await writeFileSafe(path.join(plan.targetPath, 'src/app/page.tsx'), `${imports}
|
|
203
|
+
import { Footer } from '@/components/layout/Footer';
|
|
204
|
+
import { Navbar } from '@/components/layout/Navbar';
|
|
205
|
+
|
|
206
|
+
export default function HomePage() {
|
|
207
|
+
return (
|
|
208
|
+
<>
|
|
209
|
+
<Navbar />
|
|
210
|
+
<main>
|
|
211
|
+
${renderedSections}
|
|
212
|
+
</main>
|
|
213
|
+
<Footer />
|
|
214
|
+
</>
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
`);
|
|
218
|
+
}
|
|
219
|
+
async function writeSectionComponent(plan, section) {
|
|
220
|
+
const componentName = `${toPascal(section)}Section`;
|
|
221
|
+
const shouldRenderContactForm = section === 'contact' && plan.modules.includes('contact-form');
|
|
222
|
+
const contactImport = shouldRenderContactForm
|
|
223
|
+
? `import { ContactForm } from '@/components/forms/ContactForm';\n`
|
|
224
|
+
: '';
|
|
225
|
+
const contactForm = shouldRenderContactForm
|
|
226
|
+
? `
|
|
227
|
+
<div className="mt-8 max-w-xl mx-auto">
|
|
228
|
+
<ContactForm />
|
|
229
|
+
</div>`
|
|
230
|
+
: `
|
|
231
|
+
<div className="mt-8 rounded-2xl border border-slate-200/80 bg-white p-8 text-center max-w-xl mx-auto shadow-sm">
|
|
232
|
+
<p className="text-sm text-slate-500">Contact form placeholder. Enable the "Contact form placeholder" module to generate the interactive form component.</p>
|
|
233
|
+
<a href="mailto:hello@example.com" className="inline-block mt-4 text-sm font-semibold text-blue-600 hover:text-blue-500">hello@example.com</a>
|
|
234
|
+
</div>`;
|
|
235
|
+
let sectionContent = '';
|
|
236
|
+
let imports = '';
|
|
237
|
+
switch (section) {
|
|
238
|
+
case 'hero':
|
|
239
|
+
imports = `import { Sparkles, ArrowRight, Zap, ShieldCheck, Rocket } from 'lucide-react';\n`;
|
|
240
|
+
sectionContent = ` <section id="hero" className="relative mx-auto max-w-6xl px-6 pt-28 pb-24 text-center overflow-hidden">
|
|
241
|
+
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 -z-10 h-72 w-72 rounded-full bg-blue-400/10 blur-3xl pointer-events-none" />
|
|
242
|
+
|
|
243
|
+
<div className="mx-auto max-w-3xl">
|
|
244
|
+
<span className="inline-flex items-center gap-1.5 rounded-full bg-blue-50 px-3.5 py-1.5 text-xs font-semibold text-blue-700 ring-1 ring-inset ring-blue-700/10">
|
|
245
|
+
<Sparkles className="h-3.5 w-3.5 animate-pulse" />
|
|
246
|
+
v1.3.0 • Premium Blueprint Starters
|
|
247
|
+
</span>
|
|
248
|
+
<h1 className="mt-8 text-4xl font-extrabold tracking-tight text-slate-900 sm:text-6xl bg-gradient-to-r from-blue-600 via-indigo-600 to-indigo-500 bg-clip-text text-transparent">
|
|
249
|
+
Build Your Next Big Idea Faster
|
|
250
|
+
</h1>
|
|
251
|
+
<p className="mt-6 text-lg leading-8 text-slate-600 max-w-2xl mx-auto font-medium">
|
|
252
|
+
A premium Next.js starter generated by project-cli, pre-integrated with Tailwind CSS and Lucide React. Ready for production-scale customization.
|
|
253
|
+
</p>
|
|
254
|
+
<div className="mt-10 flex flex-col sm:flex-row items-center justify-center gap-4">
|
|
255
|
+
<a
|
|
256
|
+
href="#features"
|
|
257
|
+
className="inline-flex items-center gap-2 rounded-xl bg-blue-600 px-6 py-3.5 text-sm font-semibold text-white shadow-md shadow-blue-500/20 hover:bg-blue-500 transition duration-200"
|
|
258
|
+
>
|
|
259
|
+
Explore Features
|
|
260
|
+
<ArrowRight className="h-4 w-4" />
|
|
261
|
+
</a>
|
|
262
|
+
<a
|
|
263
|
+
href="#contact"
|
|
264
|
+
className="inline-flex items-center gap-1.5 rounded-xl border border-slate-200 bg-white/80 px-6 py-3.5 text-sm font-semibold text-slate-700 hover:bg-slate-50 transition duration-200"
|
|
265
|
+
>
|
|
266
|
+
Get in Touch
|
|
267
|
+
</a>
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
|
|
271
|
+
<div className="mx-auto mt-20 grid max-w-4xl grid-cols-1 gap-6 sm:grid-cols-3">
|
|
272
|
+
{[
|
|
273
|
+
{ label: 'Developer Setup Time', value: 'Under 5m', icon: Zap, color: 'text-amber-500 bg-amber-50' },
|
|
274
|
+
{ label: 'Next.js App Build Uptime', value: '99.9%', icon: ShieldCheck, color: 'text-emerald-500 bg-emerald-50' },
|
|
275
|
+
{ label: 'Visual Component Quality', value: 'Premium', icon: Rocket, color: 'text-indigo-500 bg-indigo-50' }
|
|
276
|
+
].map((stat, idx) => {
|
|
277
|
+
const Icon = stat.icon;
|
|
278
|
+
return (
|
|
279
|
+
<div key={idx} className="flex items-center gap-4 rounded-2xl border border-slate-200/80 bg-white/70 p-6 backdrop-blur-sm shadow-sm hover:shadow-md hover:translate-y-[-2px] transition duration-300">
|
|
280
|
+
<span className={\`p-3 rounded-xl \${stat.color} flex items-center justify-center\`}>
|
|
281
|
+
<Icon className="h-6 w-6" />
|
|
282
|
+
</span>
|
|
283
|
+
<div className="text-left">
|
|
284
|
+
<dt className="text-xs font-semibold uppercase tracking-wider text-slate-400">{stat.label}</dt>
|
|
285
|
+
<dd className="text-xl font-bold tracking-tight text-slate-900">{stat.value}</dd>
|
|
286
|
+
</div>
|
|
287
|
+
</div>
|
|
288
|
+
);
|
|
289
|
+
})}
|
|
290
|
+
</div>
|
|
291
|
+
</section>`;
|
|
292
|
+
break;
|
|
293
|
+
case 'features':
|
|
294
|
+
imports = `import { Zap, ShieldCheck, Layers, Smartphone, Database, Code2 } from 'lucide-react';\n`;
|
|
295
|
+
sectionContent = ` <section id="features" className="mx-auto max-w-6xl px-6 py-24 border-t border-slate-200/50">
|
|
296
|
+
<div className="text-center max-w-3xl mx-auto">
|
|
297
|
+
<p className="text-xs font-bold uppercase tracking-widest text-blue-600">Core Features</p>
|
|
298
|
+
<h2 className="mt-3 text-3xl font-extrabold tracking-tight text-slate-900 sm:text-4xl">
|
|
299
|
+
Everything You Need to Launch
|
|
300
|
+
</h2>
|
|
301
|
+
<p className="mt-4 text-base text-slate-500 leading-relaxed font-medium">
|
|
302
|
+
Boilerplate setups waste hours of development time. This blueprint handles structure, tooling, and layouts out of the box.
|
|
303
|
+
</p>
|
|
304
|
+
</div>
|
|
305
|
+
|
|
306
|
+
<div className="mt-16 grid gap-8 sm:grid-cols-2 lg:grid-cols-3">
|
|
307
|
+
{[
|
|
308
|
+
{ title: 'Next.js 16 App Router', desc: 'Leverage React Server Components and file-based layouts for blazing fast loads.', icon: Layers, color: 'text-blue-600 bg-blue-50' },
|
|
309
|
+
{ title: 'Tailwind CSS v4 Styling', desc: 'Pre-configured color palettes and responsive breakpoints make updating UI a breeze.', icon: Zap, color: 'text-amber-600 bg-amber-50' },
|
|
310
|
+
{ title: 'TypeScript Core', desc: 'Robust type safety ensures fewer runtime exceptions and a better coding experience.', icon: Code2, color: 'text-indigo-600 bg-indigo-50' },
|
|
311
|
+
{ title: 'Responsive Design First', desc: 'Designed to fit and feel premium on mobile viewports as well as large desktop displays.', icon: Smartphone, color: 'text-pink-600 bg-pink-50' },
|
|
312
|
+
{ title: 'Data Store Support', desc: 'Pre-configured Prisma and PostgreSQL modules are available in the project plan.', icon: Database, color: 'text-emerald-600 bg-emerald-50' },
|
|
313
|
+
{ title: 'Secure Environments', desc: 'Strict env checks and GitHub Actions CI pipelines compile and lint code continuously.', icon: ShieldCheck, color: 'text-purple-600 bg-purple-50' }
|
|
314
|
+
].map((feat, idx) => {
|
|
315
|
+
const Icon = feat.icon;
|
|
316
|
+
return (
|
|
317
|
+
<div key={idx} className="group rounded-2xl border border-slate-200/80 bg-white p-6 shadow-sm hover:shadow-md hover:translate-y-[-2px] transition duration-300">
|
|
318
|
+
<span className={\`inline-flex p-3 rounded-xl \${feat.color} group-hover:scale-110 transition duration-200\`}>
|
|
319
|
+
<Icon className="h-6 w-6" />
|
|
320
|
+
</span>
|
|
321
|
+
<h3 className="mt-4 text-lg font-bold text-slate-905">{feat.title}</h3>
|
|
322
|
+
<p className="mt-2 text-sm text-slate-500 leading-relaxed font-medium">{feat.desc}</p>
|
|
323
|
+
</div>
|
|
324
|
+
);
|
|
325
|
+
})}
|
|
326
|
+
</div>
|
|
327
|
+
</section>`;
|
|
328
|
+
break;
|
|
329
|
+
case 'about':
|
|
330
|
+
imports = `import { CheckCircle2, BookOpen } from 'lucide-react';\n`;
|
|
331
|
+
sectionContent = ` <section id="about" className="mx-auto max-w-6xl px-6 py-24 border-t border-slate-200/50">
|
|
332
|
+
<div className="grid gap-12 lg:grid-cols-2 items-center">
|
|
333
|
+
<div>
|
|
334
|
+
<p className="text-xs font-bold uppercase tracking-widest text-blue-600">About the Project</p>
|
|
335
|
+
<h2 className="mt-3 text-3xl font-extrabold tracking-tight text-slate-900 sm:text-4xl">
|
|
336
|
+
Designed for Modern Web Creators
|
|
337
|
+
</h2>
|
|
338
|
+
<p className="mt-6 text-base text-slate-600 leading-relaxed font-medium">
|
|
339
|
+
Our blueprint templates serve as robust launchpads for a wide variety of web applications. We believe that developer tooling should be clean, extensible, and aesthetically pleasing from the very first run.
|
|
340
|
+
</p>
|
|
341
|
+
<p className="mt-4 text-base text-slate-600 leading-relaxed font-medium">
|
|
342
|
+
Whether you are building a corporate landing page, a complex SaaS platform, or an interactive AI assistant, this codebase provides the structural consistency and quality standards you need.
|
|
343
|
+
</p>
|
|
344
|
+
</div>
|
|
345
|
+
<div className="rounded-2xl border border-slate-200/80 bg-gradient-to-br from-indigo-50/50 to-blue-50/50 p-8 shadow-sm relative overflow-hidden">
|
|
346
|
+
<div className="absolute top-0 right-0 -translate-y-4 translate-x-4 h-24 w-24 bg-indigo-500/5 rounded-full blur-2xl" />
|
|
347
|
+
<div className="flex items-center gap-2 mb-6">
|
|
348
|
+
<BookOpen className="h-5 w-5 text-indigo-600" />
|
|
349
|
+
<h3 className="text-base font-bold text-slate-950">Starter Architecture Checklist</h3>
|
|
350
|
+
</div>
|
|
351
|
+
<ul className="space-y-4 text-sm text-slate-705 font-medium">
|
|
352
|
+
{[
|
|
353
|
+
'Zero configuration required to start local development',
|
|
354
|
+
'Highly structured and modular layout system',
|
|
355
|
+
'Tailwind configurations pre-tuned for high legibility',
|
|
356
|
+
'Built-in support for search engine optimization best practices'
|
|
357
|
+
].map((item, idx) => (
|
|
358
|
+
<li key={idx} className="flex gap-3">
|
|
359
|
+
<CheckCircle2 className="h-5 w-5 text-indigo-600 flex-shrink-0" />
|
|
360
|
+
<span>{item}</span>
|
|
361
|
+
</li>
|
|
362
|
+
))}
|
|
363
|
+
</ul>
|
|
364
|
+
</div>
|
|
365
|
+
</div>
|
|
366
|
+
</section>`;
|
|
367
|
+
break;
|
|
368
|
+
case 'pricing':
|
|
369
|
+
imports = `import { Check } from 'lucide-react';\n`;
|
|
370
|
+
sectionContent = ` <section id="pricing" className="mx-auto max-w-6xl px-6 py-24 border-t border-slate-200/50">
|
|
371
|
+
<div className="text-center max-w-3xl mx-auto">
|
|
372
|
+
<p className="text-xs font-bold uppercase tracking-widest text-blue-600">Pricing Plans</p>
|
|
373
|
+
<h2 className="mt-3 text-3xl font-extrabold tracking-tight text-slate-900 sm:text-4xl">
|
|
374
|
+
Simple, Transparent Pricing
|
|
375
|
+
</h2>
|
|
376
|
+
<p className="mt-4 text-base text-slate-500 leading-relaxed font-medium">
|
|
377
|
+
No hidden fees or complex contracts. Choose the model that matches your current scale.
|
|
378
|
+
</p>
|
|
379
|
+
</div>
|
|
380
|
+
|
|
381
|
+
<div className="mt-16 grid gap-8 md:grid-cols-2 max-w-4xl mx-auto">
|
|
382
|
+
{[
|
|
383
|
+
{ name: 'Starter', price: '$0', desc: 'Ideal for prototyping and personal projects.', features: ['1 User License', 'Community Support', 'Lifetime Free Updates', 'Basic Analytics'], highlight: false },
|
|
384
|
+
{ name: 'Pro', price: '$29', desc: 'Perfect for commercial products and scaling.', features: ['Unlimited Projects', 'Priority Email Support', 'Custom Branding', 'Advanced SEO & Integrations'], highlight: true }
|
|
385
|
+
].map((plan, idx) => (
|
|
386
|
+
<div key={idx} className={\`rounded-2xl border p-8 shadow-sm flex flex-col justify-between hover:shadow-md hover:translate-y-[-2px] transition duration-300 \${plan.highlight ? 'border-blue-600 ring-1 ring-blue-600 bg-white' : 'border-slate-200/80 bg-white/60'}\`}>
|
|
387
|
+
<div>
|
|
388
|
+
<div className="flex items-center justify-between">
|
|
389
|
+
<h3 className="text-lg font-bold text-slate-955">{plan.name}</h3>
|
|
390
|
+
{plan.highlight && (
|
|
391
|
+
<span className="rounded-full bg-blue-50 px-2.5 py-1 text-xs font-semibold text-blue-700 ring-1 ring-inset ring-blue-700/10">Popular</span>
|
|
392
|
+
)}
|
|
393
|
+
</div>
|
|
394
|
+
<p className="mt-4 text-sm text-slate-500 leading-relaxed font-medium">{plan.desc}</p>
|
|
395
|
+
<p className="mt-6 flex items-baseline gap-x-1">
|
|
396
|
+
<span className="text-4xl font-extrabold tracking-tight text-slate-900">{plan.price}</span>
|
|
397
|
+
<span className="text-sm font-semibold leading-6 text-slate-500">/month</span>
|
|
398
|
+
</p>
|
|
399
|
+
<ul className="mt-8 space-y-4 text-sm text-slate-700 font-medium border-t border-slate-105 pt-6">
|
|
400
|
+
{plan.features.map((feat, fidx) => (
|
|
401
|
+
<li key={fidx} className="flex gap-x-3 items-center">
|
|
402
|
+
<Check className="h-4 w-4 text-blue-600 flex-shrink-0" />
|
|
403
|
+
<span>{feat}</span>
|
|
404
|
+
</li>
|
|
405
|
+
))}
|
|
406
|
+
</ul>
|
|
407
|
+
</div>
|
|
408
|
+
<div className="mt-8">
|
|
409
|
+
<a
|
|
410
|
+
href="#contact"
|
|
411
|
+
className={\`block w-full text-center rounded-xl py-3.5 px-4 text-sm font-semibold transition duration-200 \${plan.highlight ? 'bg-blue-600 text-white hover:bg-blue-500 shadow-md shadow-blue-500/20' : 'bg-slate-100 text-slate-900 hover:bg-slate-200'}\`}
|
|
412
|
+
>
|
|
413
|
+
Get Started
|
|
414
|
+
</a>
|
|
415
|
+
</div>
|
|
416
|
+
</div>
|
|
417
|
+
))}
|
|
418
|
+
</div>
|
|
419
|
+
</section>`;
|
|
420
|
+
break;
|
|
421
|
+
case 'faq':
|
|
422
|
+
imports = `import { HelpCircle } from 'lucide-react';\n`;
|
|
423
|
+
sectionContent = ` <section id="faq" className="mx-auto max-w-4xl px-6 py-24 border-t border-slate-200/50">
|
|
424
|
+
<div className="text-center mb-16">
|
|
425
|
+
<p className="text-xs font-bold uppercase tracking-widest text-blue-600">FAQ</p>
|
|
426
|
+
<h2 className="mt-3 text-3xl font-extrabold tracking-tight text-slate-900">
|
|
427
|
+
Frequently Asked Questions
|
|
428
|
+
</h2>
|
|
429
|
+
</div>
|
|
430
|
+
|
|
431
|
+
<div className="space-y-6">
|
|
432
|
+
{[
|
|
433
|
+
{ q: 'How do I customize this template?', a: 'You can easily customize this template by editing the code in src/app. All layouts, pages, and components use standard React and Tailwind CSS.' },
|
|
434
|
+
{ q: 'Is there a commercial license?', a: 'Yes. The boilerplate is generated under the MIT license, meaning you can build personal and commercial software with it.' },
|
|
435
|
+
{ q: 'Can I integrate a database?', a: 'Absolutely. You can run the Prisma feature module generator to add PostgreSQL support automatically or connect your own API layer.' }
|
|
436
|
+
].map((faq, idx) => (
|
|
437
|
+
<div key={idx} className="rounded-2xl border border-slate-200/80 bg-white p-6 shadow-sm flex gap-4">
|
|
438
|
+
<HelpCircle className="h-6 w-6 text-blue-600 flex-shrink-0 mt-0.5" />
|
|
439
|
+
<div>
|
|
440
|
+
<h3 className="text-base font-bold text-slate-955">{faq.q}</h3>
|
|
441
|
+
<p className="mt-2 text-sm text-slate-505 leading-relaxed font-medium">{faq.a}</p>
|
|
442
|
+
</div>
|
|
443
|
+
</div>
|
|
444
|
+
))}
|
|
445
|
+
</div>
|
|
446
|
+
</section>`;
|
|
447
|
+
break;
|
|
448
|
+
case 'contact':
|
|
449
|
+
imports = `import { Mail, Phone, MapPin, Clock } from 'lucide-react';\n`;
|
|
450
|
+
sectionContent = ` <section id="contact" className="mx-auto max-w-6xl px-6 py-24 border-t border-slate-200/50">
|
|
451
|
+
<div className="max-w-3xl mx-auto text-center mb-16">
|
|
452
|
+
<p className="text-xs font-bold uppercase tracking-widest text-blue-600">Get In Touch</p>
|
|
453
|
+
<h2 className="mt-3 text-3xl font-extrabold tracking-tight text-slate-900 sm:text-4xl">
|
|
454
|
+
Connect With Us
|
|
455
|
+
</h2>
|
|
456
|
+
<p className="mt-4 text-base text-slate-500 font-medium">
|
|
457
|
+
Have questions or want to collaborate? Send us a message and we will respond as soon as possible.
|
|
458
|
+
</p>
|
|
459
|
+
</div>
|
|
460
|
+
|
|
461
|
+
<div className="grid gap-8 lg:grid-cols-3 max-w-5xl mx-auto">
|
|
462
|
+
<div className="lg:col-span-1 space-y-6">
|
|
463
|
+
<div className="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm">
|
|
464
|
+
<h3 className="font-bold text-slate-950 text-base mb-4">Contact Information</h3>
|
|
465
|
+
<ul className="space-y-4 text-sm text-slate-600 font-medium">
|
|
466
|
+
<li className="flex items-center gap-3">
|
|
467
|
+
<Mail className="h-5 w-5 text-blue-600" />
|
|
468
|
+
<span>hello@example.com</span>
|
|
469
|
+
</li>
|
|
470
|
+
<li className="flex items-center gap-3">
|
|
471
|
+
<Phone className="h-5 w-5 text-blue-600" />
|
|
472
|
+
<span>+1 (555) 000-0000</span>
|
|
473
|
+
</li>
|
|
474
|
+
<li className="flex items-center gap-3">
|
|
475
|
+
<MapPin className="h-5 w-5 text-blue-600" />
|
|
476
|
+
<span>San Francisco, CA</span>
|
|
477
|
+
</li>
|
|
478
|
+
</ul>
|
|
479
|
+
</div>
|
|
480
|
+
|
|
481
|
+
<div className="rounded-2xl border border-blue-105 bg-blue-50/50 p-6 shadow-sm flex items-start gap-4">
|
|
482
|
+
<Clock className="h-6 w-6 text-blue-600 flex-shrink-0" />
|
|
483
|
+
<div>
|
|
484
|
+
<h4 className="text-sm font-bold text-blue-900">Response Window</h4>
|
|
485
|
+
<p className="mt-1 text-xs text-blue-700 leading-relaxed font-medium">
|
|
486
|
+
Our support team is online Monday through Friday, responding to all inquiries within 12 hours.
|
|
487
|
+
</p>
|
|
488
|
+
</div>
|
|
489
|
+
</div>
|
|
490
|
+
</div>
|
|
491
|
+
|
|
492
|
+
<div className="lg:col-span-2">
|
|
493
|
+
${contactForm}
|
|
494
|
+
</div>
|
|
495
|
+
</div>
|
|
496
|
+
</section>`;
|
|
497
|
+
break;
|
|
498
|
+
case 'projects':
|
|
499
|
+
imports = `import { FolderGit2, ArrowUpRight } from 'lucide-react';\n`;
|
|
500
|
+
sectionContent = ` <section id="projects" className="mx-auto max-w-6xl px-6 py-24 border-t border-slate-200/50">
|
|
501
|
+
<div className="text-center max-w-3xl mx-auto mb-16">
|
|
502
|
+
<p className="text-xs font-bold uppercase tracking-widest text-blue-600">Projects</p>
|
|
503
|
+
<h2 className="mt-3 text-3xl font-extrabold tracking-tight text-slate-900 sm:text-4xl">
|
|
504
|
+
Our Latest Work
|
|
505
|
+
</h2>
|
|
506
|
+
<p className="mt-4 text-base text-slate-500 font-medium">
|
|
507
|
+
Explore the projects and products we have built using modern frameworks.
|
|
508
|
+
</p>
|
|
509
|
+
</div>
|
|
510
|
+
|
|
511
|
+
<div className="mt-12 grid gap-8 md:grid-cols-2 max-w-4xl mx-auto">
|
|
512
|
+
{[
|
|
513
|
+
{ title: 'Alpha Platform', desc: 'A secure cloud platform for managing large-scale server operations.', category: 'SaaS', link: '#' },
|
|
514
|
+
{ title: 'Beta Analytics', desc: 'Next-gen analytics engine for tracking real-time user behavior graphs.', category: 'Web App', link: '#' }
|
|
515
|
+
].map((item, idx) => (
|
|
516
|
+
<div key={idx} className="group rounded-2xl border border-slate-200/80 bg-white p-6 shadow-sm hover:shadow-md hover:translate-y-[-2px] transition duration-300">
|
|
517
|
+
<div className="flex justify-between items-start">
|
|
518
|
+
<span className="inline-block rounded-full bg-slate-100 px-3 py-1 text-xs font-semibold text-slate-600">{item.category}</span>
|
|
519
|
+
<FolderGit2 className="h-5 w-5 text-slate-400 group-hover:text-blue-600 transition" />
|
|
520
|
+
</div>
|
|
521
|
+
<h3 className="mt-6 text-xl font-bold text-slate-950">{item.title}</h3>
|
|
522
|
+
<p className="mt-2 text-sm text-slate-505 leading-relaxed font-medium">{item.desc}</p>
|
|
523
|
+
<a href={item.link} className="inline-flex items-center gap-1 mt-6 text-sm font-semibold text-blue-600 hover:text-blue-500 transition">
|
|
524
|
+
View Project
|
|
525
|
+
<ArrowUpRight className="h-4 w-4" />
|
|
526
|
+
</a>
|
|
527
|
+
</div>
|
|
528
|
+
))}
|
|
529
|
+
</div>
|
|
530
|
+
</section>`;
|
|
531
|
+
break;
|
|
532
|
+
case 'blog':
|
|
533
|
+
imports = `import { BookOpen, Clock, ArrowRight } from 'lucide-react';\n`;
|
|
534
|
+
sectionContent = ` <section id="blog" className="mx-auto max-w-6xl px-6 py-24 border-t border-slate-200/50">
|
|
535
|
+
<div className="text-center max-w-3xl mx-auto mb-16">
|
|
536
|
+
<p className="text-xs font-bold uppercase tracking-widest text-blue-600">Blog</p>
|
|
537
|
+
<h2 className="mt-3 text-3xl font-extrabold tracking-tight text-slate-900 sm:text-4xl">
|
|
538
|
+
From the Journal
|
|
539
|
+
</h2>
|
|
540
|
+
<p className="mt-4 text-base text-slate-500 font-medium">
|
|
541
|
+
Read our latest insights, tutorials, and engineering deep dives.
|
|
542
|
+
</p>
|
|
543
|
+
</div>
|
|
544
|
+
|
|
545
|
+
<div className="mt-12 grid gap-8 md:grid-cols-2 max-w-4xl mx-auto">
|
|
546
|
+
{[
|
|
547
|
+
{ title: 'Accelerating Development with project-cli', desc: 'Discover how blueprint-driven CLI scaffolding saves developers time and enforces consistency.', date: 'Jun 15, 2026', readTime: '5 min read' },
|
|
548
|
+
{ title: 'The Power of Next.js & Tailwind CSS', desc: 'A deep-dive into why the combination of Next.js and Tailwind is the ultimate tech stack for builders.', date: 'Jun 12, 2026', readTime: '3 min read' }
|
|
549
|
+
].map((post, idx) => (
|
|
550
|
+
<article key={idx} className="group rounded-2xl border border-slate-200/80 bg-white p-6 shadow-sm hover:shadow-md hover:translate-y-[-2px] transition duration-300 flex flex-col justify-between">
|
|
551
|
+
<div>
|
|
552
|
+
<div className="flex items-center gap-x-3 text-xs text-slate-400 font-medium">
|
|
553
|
+
<time>{post.date}</time>
|
|
554
|
+
<span>•</span>
|
|
555
|
+
<span className="flex items-center gap-1">
|
|
556
|
+
<Clock className="h-3 w-3" />
|
|
557
|
+
{post.readTime}
|
|
558
|
+
</span>
|
|
559
|
+
</div>
|
|
560
|
+
<h3 className="mt-4 text-lg font-bold text-slate-950 group-hover:text-blue-600 transition">
|
|
561
|
+
{post.title}
|
|
562
|
+
</h3>
|
|
563
|
+
<p className="mt-2 text-sm text-slate-500 leading-relaxed font-medium">{post.desc}</p>
|
|
564
|
+
</div>
|
|
565
|
+
<div className="mt-6 border-t border-slate-50 pt-4 flex items-center justify-between">
|
|
566
|
+
<span className="inline-flex items-center gap-1 text-sm font-semibold text-blue-600 group-hover:text-blue-500 transition cursor-pointer">
|
|
567
|
+
Read article
|
|
568
|
+
<ArrowRight className="h-4 w-4" />
|
|
569
|
+
</span>
|
|
570
|
+
<BookOpen className="h-4 w-4 text-slate-300" />
|
|
571
|
+
</div>
|
|
572
|
+
</article>
|
|
573
|
+
))}
|
|
574
|
+
</div>
|
|
575
|
+
</section>`;
|
|
576
|
+
break;
|
|
577
|
+
case 'services':
|
|
578
|
+
imports = `import { Laptop, Cpu, Plug } from 'lucide-react';\n`;
|
|
579
|
+
sectionContent = ` <section id="services" className="mx-auto max-w-6xl px-6 py-24 border-t border-slate-200/50">
|
|
580
|
+
<div className="text-center max-w-3xl mx-auto mb-16">
|
|
581
|
+
<p className="text-xs font-bold uppercase tracking-widest text-blue-600">Services</p>
|
|
582
|
+
<h2 className="mt-3 text-3xl font-extrabold tracking-tight text-slate-900 sm:text-4xl">
|
|
583
|
+
Our Specializations
|
|
584
|
+
</h2>
|
|
585
|
+
<p className="mt-4 text-base text-slate-505 font-medium">
|
|
586
|
+
We offer specialized engineering services to bring your concepts to life.
|
|
587
|
+
</p>
|
|
588
|
+
</div>
|
|
589
|
+
|
|
590
|
+
<div className="mt-12 grid gap-8 md:grid-cols-3 max-w-5xl mx-auto">
|
|
591
|
+
{[
|
|
592
|
+
{ title: 'Custom Development', desc: 'End-to-end design and engineering of high-performance web applications.', icon: Laptop, color: 'text-blue-600 bg-blue-50' },
|
|
593
|
+
{ title: 'Technical Consulting', desc: 'Architecture reviews, performance tuning, and software scale planning.', icon: Cpu, color: 'text-indigo-600 bg-indigo-50' },
|
|
594
|
+
{ title: 'API Integration', desc: 'Seamlessly connect database layers, cloud providers, and AI endpoints.', icon: Plug, color: 'text-amber-600 bg-amber-50' }
|
|
595
|
+
].map((item, idx) => {
|
|
596
|
+
const Icon = item.icon;
|
|
597
|
+
return (
|
|
598
|
+
<div key={idx} className="group rounded-2xl border border-slate-200/80 bg-white p-6 shadow-sm hover:shadow-md hover:translate-y-[-2px] transition duration-300">
|
|
599
|
+
<span className={\`inline-flex p-3 rounded-xl \... \${item.color} group-hover:scale-110 transition duration-200\`}>
|
|
600
|
+
<Icon className="h-6 w-6" />
|
|
601
|
+
</span>
|
|
602
|
+
<h3 className="mt-4 text-lg font-bold text-slate-950">{item.title}</h3>
|
|
603
|
+
<p className="mt-2 text-sm text-slate-500 leading-relaxed font-medium">{item.desc}</p>
|
|
604
|
+
</div>
|
|
605
|
+
);
|
|
606
|
+
})}
|
|
607
|
+
</div>
|
|
608
|
+
</section>`;
|
|
609
|
+
// Let's fix the template syntax bug above: replace '\... ' with ''
|
|
610
|
+
sectionContent = sectionContent.replace('... ', '');
|
|
611
|
+
break;
|
|
612
|
+
case 'testimonials':
|
|
613
|
+
imports = `import { Quote } from 'lucide-react';\n`;
|
|
614
|
+
sectionContent = ` <section id="testimonials" className="mx-auto max-w-5xl px-6 py-24 border-t border-slate-200/50">
|
|
615
|
+
<div className="text-center mb-16">
|
|
616
|
+
<p className="text-xs font-bold uppercase tracking-widest text-blue-600">Testimonials</p>
|
|
617
|
+
<h2 className="mt-3 text-3xl font-extrabold tracking-tight text-slate-900">
|
|
618
|
+
Trusted by Builders
|
|
619
|
+
</h2>
|
|
620
|
+
</div>
|
|
621
|
+
|
|
622
|
+
<div className="grid gap-8 md:grid-cols-2">
|
|
623
|
+
{[
|
|
624
|
+
{ quote: 'This CLI and template completely transformed our prototype cycle. The generated code is incredibly clean and easily adaptable.', author: 'Alex Rivera', role: 'CTO, TechVenture' },
|
|
625
|
+
{ quote: 'Highly recommended starter for any developer using Next.js. Spared us hours of layout, structure, and configuration design.', author: 'Sarah Chen', role: 'Senior Developer, InnovateCo' }
|
|
626
|
+
].map((q, idx) => (
|
|
627
|
+
<figure key={idx} className="relative rounded-2xl border border-slate-200/80 bg-white/50 p-6 shadow-sm backdrop-blur-sm">
|
|
628
|
+
<Quote className="absolute top-6 right-6 h-8 w-8 text-blue-500/10 pointer-events-none" />
|
|
629
|
+
<blockquote className="text-slate-705 text-sm leading-relaxed italic font-medium pr-8">
|
|
630
|
+
“{q.quote}”
|
|
631
|
+
</blockquote>
|
|
632
|
+
<figcaption className="mt-6 flex items-center gap-x-3 border-t border-slate-100 pt-4">
|
|
633
|
+
<div>
|
|
634
|
+
<div className="text-sm font-bold text-slate-950">{q.author}</div>
|
|
635
|
+
<div className="text-xs text-slate-400 font-medium">{q.role}</div>
|
|
636
|
+
</div>
|
|
637
|
+
</figcaption>
|
|
638
|
+
</figure>
|
|
639
|
+
))}
|
|
640
|
+
</div>
|
|
641
|
+
</section>`;
|
|
642
|
+
break;
|
|
643
|
+
default:
|
|
644
|
+
sectionContent = ` <section id="${section}" className="mx-auto max-w-6xl px-6 py-20">
|
|
645
|
+
<p className="mb-3 text-sm font-medium uppercase tracking-wide text-blue-600">
|
|
646
|
+
\${toTitle(section)}
|
|
647
|
+
</p>
|
|
648
|
+
<h2 className="max-w-3xl text-3xl font-bold tracking-tight text-slate-900 sm:text-4xl">
|
|
649
|
+
\${toTitle(section)} Section
|
|
650
|
+
</h2>
|
|
651
|
+
<p className="mt-4 max-w-2xl text-slate-600">
|
|
652
|
+
Replace this starter content with your real \${section} content.
|
|
653
|
+
</p>
|
|
654
|
+
</section>`;
|
|
655
|
+
}
|
|
656
|
+
await writeFileSafe(path.join(plan.targetPath, `src/components/sections/${componentName}.tsx`), `${imports}${contactImport}export function ${componentName}() {
|
|
657
|
+
return (
|
|
658
|
+
${sectionContent}
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
`);
|
|
662
|
+
}
|
|
663
|
+
async function writeMultiPageWebsite(plan) {
|
|
664
|
+
const pages = getWebsitePages(plan);
|
|
665
|
+
for (const page of pages) {
|
|
666
|
+
await writeWebsitePage(plan, page);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
async function writeWebsitePage(plan, page) {
|
|
670
|
+
const isHome = page === 'home';
|
|
671
|
+
const isContactPage = page === 'contact';
|
|
672
|
+
const shouldRenderContactForm = isContactPage && plan.modules.includes('contact-form');
|
|
673
|
+
const pagePath = isHome
|
|
674
|
+
? path.join(plan.targetPath, 'src/app/page.tsx')
|
|
675
|
+
: path.join(plan.targetPath, `src/app/${page}/page.tsx`);
|
|
676
|
+
const componentName = isHome ? 'HomePage' : `${toPascal(page)}Page`;
|
|
677
|
+
const contactImport = shouldRenderContactForm
|
|
678
|
+
? `import { ContactForm } from '@/components/forms/ContactForm';\n`
|
|
679
|
+
: '';
|
|
680
|
+
const contactForm = shouldRenderContactForm
|
|
681
|
+
? `
|
|
682
|
+
<div className="mt-8 max-w-xl mx-auto">
|
|
683
|
+
<ContactForm />
|
|
684
|
+
</div>`
|
|
685
|
+
: `
|
|
686
|
+
<div className="mt-8 rounded-2xl border border-slate-200/80 bg-white p-8 text-center max-w-xl mx-auto shadow-sm">
|
|
687
|
+
<p className="text-sm text-slate-500">Contact form placeholder. Enable the "Contact form placeholder" module to generate the interactive form component.</p>
|
|
688
|
+
<a href="mailto:hello@example.com" className="inline-block mt-4 text-sm font-semibold text-blue-600 hover:text-blue-500">hello@example.com</a>
|
|
689
|
+
</div>`;
|
|
690
|
+
let pageContent = '';
|
|
691
|
+
const iconImports = [];
|
|
692
|
+
if (page === 'home')
|
|
693
|
+
iconImports.push('Sparkles', 'ArrowRight', 'Layers', 'Zap', 'ShieldCheck');
|
|
694
|
+
if (page === 'about')
|
|
695
|
+
iconImports.push('CheckCircle2', 'BookOpen');
|
|
696
|
+
if (page === 'services')
|
|
697
|
+
iconImports.push('Laptop', 'Cpu', 'Plug');
|
|
698
|
+
if (page === 'projects')
|
|
699
|
+
iconImports.push('FolderGit2', 'ArrowUpRight');
|
|
700
|
+
if (page === 'blog')
|
|
701
|
+
iconImports.push('BookOpen', 'Clock', 'ArrowRight');
|
|
702
|
+
if (page === 'contact')
|
|
703
|
+
iconImports.push('Mail', 'Phone', 'MapPin', 'Clock');
|
|
704
|
+
const lucideImport = iconImports.length > 0 ? `import { ${iconImports.join(', ')} } from 'lucide-react';\n` : '';
|
|
705
|
+
switch (page) {
|
|
706
|
+
case 'home':
|
|
707
|
+
pageContent = ` <main className="mx-auto max-w-6xl px-6 pt-24 pb-20 text-center">
|
|
708
|
+
<div className="mx-auto max-w-3xl">
|
|
709
|
+
<span className="inline-flex items-center gap-1.5 rounded-full bg-blue-50 px-3.5 py-1.5 text-xs font-semibold text-blue-700 ring-1 ring-inset ring-blue-700/10 mb-8">
|
|
710
|
+
<Sparkles className="h-3.5 w-3.5 animate-pulse" />
|
|
711
|
+
Multi-Page Website Starter
|
|
712
|
+
</span>
|
|
713
|
+
<h1 className="mt-6 text-4xl font-extrabold tracking-tight text-slate-900 sm:text-6xl bg-gradient-to-r from-blue-600 via-indigo-600 to-indigo-500 bg-clip-text text-transparent">
|
|
714
|
+
Welcome to ${toTitle(plan.projectName)}
|
|
715
|
+
</h1>
|
|
716
|
+
<p className="mt-6 text-lg leading-8 text-slate-600 max-w-2xl mx-auto font-medium">
|
|
717
|
+
A beautiful, multi-page website architecture pre-built with responsive layouts, sticky header, customized footer, and modular code structures.
|
|
718
|
+
</p>
|
|
719
|
+
<div className="mt-10 flex items-center justify-center gap-x-6">
|
|
720
|
+
<Link
|
|
721
|
+
href="/about"
|
|
722
|
+
className="inline-flex items-center gap-1.5 rounded-xl bg-blue-600 px-5 py-3 text-sm font-semibold text-white shadow-sm hover:bg-blue-500 transition duration-200"
|
|
723
|
+
>
|
|
724
|
+
Learn More About Us
|
|
725
|
+
<ArrowRight className="h-4 w-4" />
|
|
726
|
+
</Link>
|
|
727
|
+
<Link
|
|
728
|
+
href="/contact"
|
|
729
|
+
className="text-sm font-semibold leading-6 text-slate-900 hover:text-blue-600 transition"
|
|
730
|
+
>
|
|
731
|
+
Get in Touch
|
|
732
|
+
</Link>
|
|
733
|
+
</div>
|
|
734
|
+
</div>
|
|
735
|
+
|
|
736
|
+
<div className="mx-auto mt-20 max-w-5xl border-t border-slate-200/80 pt-16">
|
|
737
|
+
<h2 className="text-2xl font-bold tracking-tight text-slate-900 mb-8">What makes us different</h2>
|
|
738
|
+
<div className="grid gap-8 sm:grid-cols-3">
|
|
739
|
+
{[
|
|
740
|
+
{ title: 'Modular Architecture', desc: 'Standardized layout, navigation, and page styles.', icon: Layers },
|
|
741
|
+
{ title: 'Tailwind Stylings', desc: 'Pre-configured color schemes, spacing patterns, and responsive breaks.', icon: Zap },
|
|
742
|
+
{ title: 'SEO Optimized', desc: 'Auto-configured sitemaps and robots rules for crawler support.', icon: ShieldCheck }
|
|
743
|
+
].map((item, idx) => {
|
|
744
|
+
const Icon = item.icon;
|
|
745
|
+
return (
|
|
746
|
+
<div key={idx} className="rounded-2xl border border-slate-200/80 bg-white p-6 shadow-sm text-left">
|
|
747
|
+
<span className="inline-flex p-2 rounded-lg bg-blue-50 text-blue-600 mb-4">
|
|
748
|
+
<Icon className="h-5 w-5" />
|
|
749
|
+
</span>
|
|
750
|
+
<h3 className="font-bold text-slate-950">{item.title}</h3>
|
|
751
|
+
<p className="mt-2 text-sm text-slate-500 leading-relaxed font-medium">{item.desc}</p>
|
|
752
|
+
</div>
|
|
753
|
+
);
|
|
754
|
+
})}
|
|
755
|
+
</div>
|
|
756
|
+
</div>
|
|
757
|
+
</main>`;
|
|
758
|
+
break;
|
|
759
|
+
case 'about':
|
|
760
|
+
pageContent = ` <main className="mx-auto max-w-6xl px-6 py-20">
|
|
761
|
+
<div className="max-w-3xl">
|
|
762
|
+
<p className="text-xs font-bold uppercase tracking-widest text-blue-600">About Us</p>
|
|
763
|
+
<h1 className="mt-3 text-4xl font-extrabold tracking-tight text-slate-900 sm:text-5xl">
|
|
764
|
+
Designed for Modern Creators
|
|
765
|
+
</h1>
|
|
766
|
+
<p className="mt-6 text-lg text-slate-600 leading-relaxed font-medium">
|
|
767
|
+
Our mission is to supply modular, scalable blueprint projects that enable teams to build robust software without getting bogged down by boilerplate environment setups.
|
|
768
|
+
</p>
|
|
769
|
+
</div>
|
|
770
|
+
|
|
771
|
+
<div className="mt-16 grid gap-8 md:grid-cols-3">
|
|
772
|
+
{[
|
|
773
|
+
{ title: 'Our Vision', desc: 'To provide developer tooling that is clean, extensible, and aesthetically pleasing from the very first launch.', icon: BookOpen },
|
|
774
|
+
{ title: 'Our Strategy', desc: 'Scaffold real layouts, sitemaps, components, and schema integrations based on high-level developer intents.', icon: CheckCircle2 },
|
|
775
|
+
{ title: 'Our Values', desc: 'Prioritize type safety, accessibility, modern styling standards, and clean, readable code.', icon: CheckCircle2 }
|
|
776
|
+
].map((card, idx) => {
|
|
777
|
+
const Icon = card.icon;
|
|
778
|
+
return (
|
|
779
|
+
<div key={idx} className="rounded-2xl border border-slate-200/80 bg-white p-6 shadow-sm">
|
|
780
|
+
<span className="inline-flex p-2 rounded-lg bg-indigo-50 text-indigo-600 mb-4">
|
|
781
|
+
<Icon className="h-5 w-5" />
|
|
782
|
+
</span>
|
|
783
|
+
<h2 className="text-lg font-bold text-slate-950">{card.title}</h2>
|
|
784
|
+
<p className="mt-2 text-sm text-slate-500 leading-relaxed font-medium">{card.desc}</p>
|
|
785
|
+
</div>
|
|
786
|
+
);
|
|
787
|
+
})}
|
|
788
|
+
</div>
|
|
789
|
+
</main>`;
|
|
790
|
+
break;
|
|
791
|
+
case 'services':
|
|
792
|
+
pageContent = ` <main className="mx-auto max-w-6xl px-6 py-20">
|
|
793
|
+
<div className="max-w-3xl">
|
|
794
|
+
<p className="text-xs font-bold uppercase tracking-widest text-blue-600">Our Services</p>
|
|
795
|
+
<h1 className="mt-3 text-4xl font-extrabold tracking-tight text-slate-900 sm:text-5xl">
|
|
796
|
+
Specialized Engineering Solutions
|
|
797
|
+
</h1>
|
|
798
|
+
<p className="mt-6 text-lg text-slate-600 leading-relaxed font-medium">
|
|
799
|
+
We provide high-quality developer templates, technical architecture consulting, and advanced application integration services.
|
|
800
|
+
</p>
|
|
801
|
+
</div>
|
|
802
|
+
|
|
803
|
+
<div className="mt-16 grid gap-8 sm:grid-cols-2 lg:grid-cols-3">
|
|
804
|
+
{[
|
|
805
|
+
{ title: 'Custom Web Development', desc: 'End-to-end design, implementation, and deployment of responsive modern web products.', icon: Laptop },
|
|
806
|
+
{ title: 'Technical Consulting', desc: 'Architecture review, technology stack recommendations, and database schema layouts.', icon: Cpu },
|
|
807
|
+
{ title: 'API & Microservice Integration', desc: 'Seamlessly hook databases, caching solutions, external modules, and third-party APIs.', icon: Plug }
|
|
808
|
+
].map((item, idx) => {
|
|
809
|
+
const Icon = item.icon;
|
|
810
|
+
return (
|
|
811
|
+
<div key={idx} className="rounded-2xl border border-slate-200/80 bg-white p-6 shadow-sm hover:shadow-md transition duration-200">
|
|
812
|
+
<span className="inline-flex p-2 rounded-lg bg-blue-50 text-blue-600 mb-4">
|
|
813
|
+
<Icon className="h-5 w-5" />
|
|
814
|
+
</span>
|
|
815
|
+
<h2 className="mt-4 text-lg font-bold text-slate-950">{item.title}</h2>
|
|
816
|
+
<p className="mt-2 text-sm text-slate-500 leading-relaxed font-medium">{item.desc}</p>
|
|
817
|
+
</div>
|
|
818
|
+
);
|
|
819
|
+
})}
|
|
820
|
+
</div>
|
|
821
|
+
</main>`;
|
|
822
|
+
break;
|
|
823
|
+
case 'projects':
|
|
824
|
+
pageContent = ` <main className="mx-auto max-w-6xl px-6 py-20">
|
|
825
|
+
<div className="max-w-3xl">
|
|
826
|
+
<p className="text-xs font-bold uppercase tracking-widest text-blue-600">Portfolio</p>
|
|
827
|
+
<h1 className="mt-3 text-4xl font-extrabold tracking-tight text-slate-900 sm:text-5xl">
|
|
828
|
+
Our Latest Work
|
|
829
|
+
</h1>
|
|
830
|
+
<p className="mt-6 text-lg text-slate-600 leading-relaxed font-medium">
|
|
831
|
+
Explore the responsive web projects, dashboard suites, and custom AI implementations we have delivered.
|
|
832
|
+
</p>
|
|
833
|
+
</div>
|
|
834
|
+
|
|
835
|
+
<div className="mt-16 grid gap-8 md:grid-cols-2 max-w-4xl">
|
|
836
|
+
{[
|
|
837
|
+
{ title: 'Alpha Platform', desc: 'Secure cloud platform for managing large-scale server operations.', category: 'SaaS Suite' },
|
|
838
|
+
{ title: 'Beta Analytics', desc: 'Next-gen analytics engine for tracking real-time user behavior graphs.', category: 'Web Application' }
|
|
839
|
+
].map((project, idx) => (
|
|
840
|
+
<div key={idx} className="rounded-2xl border border-slate-200/80 bg-white p-6 shadow-sm hover:shadow-md transition duration-200">
|
|
841
|
+
<div className="flex justify-between items-center mb-4">
|
|
842
|
+
<span className="inline-block rounded-full bg-slate-100 px-2.5 py-0.5 text-xs font-semibold text-slate-600">{project.category}</span>
|
|
843
|
+
<FolderGit2 className="h-5 w-5 text-slate-400" />
|
|
844
|
+
</div>
|
|
845
|
+
<h2 className="text-xl font-bold text-slate-950">{project.title}</h2>
|
|
846
|
+
<p className="mt-2 text-sm text-slate-500 leading-relaxed font-medium">{project.desc}</p>
|
|
847
|
+
<span className="inline-flex items-center gap-1 mt-6 text-xs font-bold text-blue-600 cursor-pointer">
|
|
848
|
+
View Project Details
|
|
849
|
+
<ArrowUpRight className="h-3.5 w-3.5" />
|
|
850
|
+
</span>
|
|
851
|
+
</div>
|
|
852
|
+
))}
|
|
853
|
+
</div>
|
|
854
|
+
</main>`;
|
|
855
|
+
break;
|
|
856
|
+
case 'blog':
|
|
857
|
+
pageContent = ` <main className="mx-auto max-w-6xl px-6 py-20">
|
|
858
|
+
<div className="max-w-3xl">
|
|
859
|
+
<p className="text-xs font-bold uppercase tracking-widest text-blue-600">Journal</p>
|
|
860
|
+
<h1 className="mt-3 text-4xl font-extrabold tracking-tight text-slate-900 sm:text-5xl">
|
|
861
|
+
Insights & Engineering
|
|
862
|
+
</h1>
|
|
863
|
+
<p className="mt-6 text-lg text-slate-600 leading-relaxed font-medium">
|
|
864
|
+
Stay updated with our latest tutorials, architectural deep-dives, and product design updates.
|
|
865
|
+
</p>
|
|
866
|
+
</div>
|
|
867
|
+
|
|
868
|
+
<div className="mt-16 grid gap-8 md:grid-cols-2 max-w-4xl">
|
|
869
|
+
{[
|
|
870
|
+
{ title: 'Accelerating Development with project-cli', desc: 'Discover how blueprint-driven CLI scaffolding saves developers time and enforces consistency.', date: 'Jun 15, 2026', read: '5m read' },
|
|
871
|
+
{ title: 'The Power of Next.js & Tailwind CSS', desc: 'A deep-dive into why the combination of Next.js and Tailwind is the ultimate tech stack for builders.', date: 'Jun 12, 2026', read: '3m read' }
|
|
872
|
+
].map((post, idx) => (
|
|
873
|
+
<article key={idx} className="rounded-2xl border border-slate-200/80 bg-white p-6 shadow-sm flex flex-col justify-between hover:shadow-md transition duration-200">
|
|
874
|
+
<div>
|
|
875
|
+
<div className="flex gap-3 text-xs text-slate-400 font-medium">
|
|
876
|
+
<time>{post.date}</time>
|
|
877
|
+
<span>•</span>
|
|
878
|
+
<span className="flex items-center gap-1"><Clock className="h-3 w-3" />{post.read}</span>
|
|
879
|
+
</div>
|
|
880
|
+
<h2 className="mt-3 text-lg font-bold text-slate-950">{post.title}</h2>
|
|
881
|
+
<p className="mt-2 text-sm text-slate-505 leading-relaxed font-medium">{post.desc}</p>
|
|
882
|
+
</div>
|
|
883
|
+
<div className="mt-6 pt-4 border-t border-slate-50 flex items-center justify-between">
|
|
884
|
+
<span className="text-sm font-semibold text-blue-600 hover:text-blue-500 cursor-pointer inline-flex items-center gap-1">
|
|
885
|
+
Read article
|
|
886
|
+
<ArrowRight className="h-4 w-4" />
|
|
887
|
+
</span>
|
|
888
|
+
<BookOpen className="h-4 w-4 text-slate-300" />
|
|
889
|
+
</div>
|
|
890
|
+
</article>
|
|
891
|
+
))}
|
|
892
|
+
</div>
|
|
893
|
+
</main>`;
|
|
894
|
+
break;
|
|
895
|
+
case 'contact':
|
|
896
|
+
pageContent = ` <main className="mx-auto max-w-6xl px-6 py-20">
|
|
897
|
+
<div className="max-w-2xl mx-auto text-center mb-16">
|
|
898
|
+
<p className="text-xs font-bold uppercase tracking-widest text-blue-600">Contact</p>
|
|
899
|
+
<h1 className="mt-3 text-4xl font-extrabold tracking-tight text-slate-900 sm:text-5xl">
|
|
900
|
+
Get in touch with us
|
|
901
|
+
</h1>
|
|
902
|
+
<p className="mt-4 text-base text-slate-500 font-medium">
|
|
903
|
+
Have questions or want to collaborate? Send us a message and we will respond as soon as possible.
|
|
904
|
+
</p>
|
|
905
|
+
</div>
|
|
906
|
+
|
|
907
|
+
<div className="grid gap-8 lg:grid-cols-3 max-w-5xl mx-auto">
|
|
908
|
+
<div className="lg:col-span-1 space-y-6">
|
|
909
|
+
<div className="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm">
|
|
910
|
+
<h3 className="font-bold text-slate-950 text-base mb-4">Contact Info</h3>
|
|
911
|
+
<ul className="space-y-4 text-sm text-slate-600 font-medium">
|
|
912
|
+
<li className="flex items-center gap-3">
|
|
913
|
+
<Mail className="h-5 w-5 text-blue-600" />
|
|
914
|
+
<span>hello@example.com</span>
|
|
915
|
+
</li>
|
|
916
|
+
<li className="flex items-center gap-3">
|
|
917
|
+
<Phone className="h-5 w-5 text-blue-600" />
|
|
918
|
+
<span>+1 (555) 000-0000</span>
|
|
919
|
+
</li>
|
|
920
|
+
<li className="flex items-center gap-3">
|
|
921
|
+
<MapPin className="h-5 w-5 text-blue-600" />
|
|
922
|
+
<span>San Francisco, CA</span>
|
|
923
|
+
</li>
|
|
924
|
+
</ul>
|
|
925
|
+
</div>
|
|
926
|
+
|
|
927
|
+
<div className="rounded-2xl border border-blue-100 bg-blue-50/50 p-6 shadow-sm flex items-start gap-4">
|
|
928
|
+
<Clock className="h-6 w-6 text-blue-600 flex-shrink-0" />
|
|
929
|
+
<div>
|
|
930
|
+
<h4 className="text-sm font-bold text-blue-900">Response Speed</h4>
|
|
931
|
+
<p className="mt-1 text-xs text-blue-700 leading-relaxed font-medium">
|
|
932
|
+
We check client inquiries multiple times a day and reply within 12 hours.
|
|
933
|
+
</p>
|
|
934
|
+
</div>
|
|
935
|
+
</div>
|
|
936
|
+
</div>
|
|
937
|
+
|
|
938
|
+
<div className="lg:col-span-2">
|
|
939
|
+
${contactForm}
|
|
940
|
+
</div>
|
|
941
|
+
</div>
|
|
942
|
+
</main>`;
|
|
943
|
+
break;
|
|
944
|
+
default:
|
|
945
|
+
pageContent = ` <main className="mx-auto max-w-6xl px-6 py-20">
|
|
946
|
+
<p className="mb-3 text-sm font-medium uppercase tracking-wide text-slate-500">
|
|
947
|
+
${toTitle(page)}
|
|
948
|
+
</p>
|
|
949
|
+
<h1 className="max-w-3xl text-4xl font-bold tracking-tight text-slate-900 sm:text-5xl">
|
|
950
|
+
${toTitle(page)} page
|
|
951
|
+
</h1>
|
|
952
|
+
<p className="mt-4 max-w-2xl text-slate-600">
|
|
953
|
+
Replace this starter content with your real ${page} page content.
|
|
954
|
+
</p>
|
|
955
|
+
</main>`;
|
|
956
|
+
}
|
|
957
|
+
await writeFileSafe(pagePath, `${lucideImport}${contactImport}import { Footer } from '@/components/layout/Footer';
|
|
958
|
+
import { Navbar } from '@/components/layout/Navbar';
|
|
959
|
+
import Link from 'next/link';
|
|
960
|
+
|
|
961
|
+
export default function ${componentName}() {
|
|
962
|
+
return (
|
|
963
|
+
<>
|
|
964
|
+
<Navbar />
|
|
965
|
+
${pageContent}
|
|
966
|
+
<Footer />
|
|
967
|
+
</>
|
|
968
|
+
);
|
|
969
|
+
}
|
|
970
|
+
`);
|
|
971
|
+
}
|
|
972
|
+
async function writeContactForm(plan) {
|
|
973
|
+
await writeFileSafe(path.join(plan.targetPath, 'src/components/forms/ContactForm.tsx'), `export function ContactForm() {
|
|
974
|
+
return (
|
|
975
|
+
<form className="space-y-4 rounded-2xl border border-slate-200/80 bg-white p-6 shadow-sm">
|
|
976
|
+
<div>
|
|
977
|
+
<label className="mb-1.5 block text-xs font-semibold uppercase tracking-wider text-slate-500" htmlFor="name">
|
|
978
|
+
Name
|
|
979
|
+
</label>
|
|
980
|
+
<input
|
|
981
|
+
id="name"
|
|
982
|
+
className="w-full rounded-xl border border-slate-200 bg-slate-50/50 px-4 py-2.5 text-sm text-slate-800 placeholder-slate-400 outline-none transition focus:border-blue-500 focus:bg-white focus:ring-1 focus:ring-blue-500"
|
|
983
|
+
placeholder="Your name"
|
|
984
|
+
type="text"
|
|
985
|
+
required
|
|
986
|
+
/>
|
|
987
|
+
</div>
|
|
988
|
+
|
|
989
|
+
<div>
|
|
990
|
+
<label className="mb-1.5 block text-xs font-semibold uppercase tracking-wider text-slate-500" htmlFor="email">
|
|
991
|
+
Email
|
|
992
|
+
</label>
|
|
993
|
+
<input
|
|
994
|
+
id="email"
|
|
995
|
+
className="w-full rounded-xl border border-slate-200 bg-slate-50/50 px-4 py-2.5 text-sm text-slate-800 placeholder-slate-400 outline-none transition focus:border-blue-500 focus:bg-white focus:ring-1 focus:ring-blue-500"
|
|
996
|
+
placeholder="you@example.com"
|
|
997
|
+
type="email"
|
|
998
|
+
required
|
|
999
|
+
/>
|
|
1000
|
+
</div>
|
|
1001
|
+
|
|
1002
|
+
<div>
|
|
1003
|
+
<label className="mb-1.5 block text-xs font-semibold uppercase tracking-wider text-slate-500" htmlFor="message">
|
|
1004
|
+
Message
|
|
1005
|
+
</label>
|
|
1006
|
+
<textarea
|
|
1007
|
+
id="message"
|
|
1008
|
+
rows={4}
|
|
1009
|
+
className="w-full rounded-xl border border-slate-200 bg-slate-50/50 px-4 py-2.5 text-sm text-slate-800 placeholder-slate-400 outline-none transition focus:border-blue-500 focus:bg-white focus:ring-1 focus:ring-blue-500"
|
|
1010
|
+
placeholder="Tell us about your project..."
|
|
1011
|
+
required
|
|
1012
|
+
/>
|
|
1013
|
+
</div>
|
|
1014
|
+
|
|
1015
|
+
<button
|
|
1016
|
+
className="w-full rounded-xl bg-blue-600 px-5 py-3 text-sm font-semibold text-white shadow-sm hover:bg-blue-500 transition"
|
|
1017
|
+
type="submit"
|
|
1018
|
+
>
|
|
1019
|
+
Send Message
|
|
1020
|
+
</button>
|
|
1021
|
+
</form>
|
|
1022
|
+
);
|
|
1023
|
+
}
|
|
1024
|
+
`);
|
|
1025
|
+
}
|
|
1026
|
+
async function writeSeoFiles(plan) {
|
|
1027
|
+
await writeFileSafe(path.join(plan.targetPath, 'src/app/robots.ts'), `import type { MetadataRoute } from 'next';
|
|
1028
|
+
import { siteConfig } from '@/lib/site-config';
|
|
1029
|
+
|
|
1030
|
+
export default function robots(): MetadataRoute.Robots {
|
|
1031
|
+
return {
|
|
1032
|
+
rules: {
|
|
1033
|
+
userAgent: '*',
|
|
1034
|
+
allow: '/',
|
|
1035
|
+
},
|
|
1036
|
+
sitemap: \`\${siteConfig.url}/sitemap.xml\`,
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
`);
|
|
1040
|
+
await writeFileSafe(path.join(plan.targetPath, 'src/app/sitemap.ts'), `import type { MetadataRoute } from 'next';
|
|
1041
|
+
import { siteConfig } from '@/lib/site-config';
|
|
1042
|
+
|
|
1043
|
+
export default function sitemap(): MetadataRoute.Sitemap {
|
|
1044
|
+
return [
|
|
1045
|
+
{
|
|
1046
|
+
url: siteConfig.url,
|
|
1047
|
+
lastModified: new Date(),
|
|
1048
|
+
},
|
|
1049
|
+
];
|
|
1050
|
+
}
|
|
1051
|
+
`);
|
|
1052
|
+
}
|
|
1053
|
+
//# sourceMappingURL=applyWebsitePlan.js.map
|