Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ async function createAction(projectName, options) {
// Architecture availability map
const ARCHITECTURES = {
'nextjs-monolith': { label: 'Next.js 14 — App Router', available: ['portfolio', 'ecommerce', 'school', 'saas', 'blog'] },
'vite-react': { label: 'Vite + React 18 — SPA', available: ['saas'] },
'vite-react': { label: 'Vite + React 18 — SPA', available: ['portfolio', 'ecommerce', 'saas'] },
'nextjs-turborepo': { label: 'Turborepo — Monorepo', available: [] },
};

Expand Down
4 changes: 2 additions & 2 deletions scripts/test-single-template.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ if (!template) {
const DESIGNS = ['Minimal Clean', 'Dark Terminal', 'Glassmorphism'];

const ARCHITECTURES = {
portfolio: ['nextjs-monolith'],
ecommerce: ['nextjs-monolith'],
portfolio: ['nextjs-monolith', 'vite-react'],
ecommerce: ['nextjs-monolith', 'vite-react'],
school: ['nextjs-monolith'],
saas: ['nextjs-monolith', 'vite-react'],
blog: ['nextjs-monolith'],
Expand Down
4 changes: 2 additions & 2 deletions scripts/test-templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ const DESIGNS = ['Minimal Clean', 'Dark Terminal', 'Glassmorphism'];

// Template → available architectures
const ARCHITECTURES = {
portfolio: ['nextjs-monolith'],
ecommerce: ['nextjs-monolith'],
portfolio: ['nextjs-monolith', 'vite-react'],
ecommerce: ['nextjs-monolith', 'vite-react'],
school: ['nextjs-monolith'],
saas: ['nextjs-monolith', 'vite-react'],
blog: ['nextjs-monolith'],
Expand Down
137 changes: 137 additions & 0 deletions templates/ecommerce/nextjs-monolith/app/account/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import Link from 'next/link';

export default function AccountPage() {
const orders = [
{ id: 'ORD-2024-001', date: 'Jun 15, 2024', items: 3, total: '$469.97', status: 'Delivered' },
{ id: 'ORD-2024-002', date: 'Jun 2, 2024', items: 1, total: '$89.99', status: 'Delivered' },
{ id: 'ORD-2024-003', date: 'May 18, 2024', items: 2, total: '$189.98', status: 'Delivered' },
{ id: 'ORD-2024-004', date: 'May 5, 2024', items: 1, total: '$249.99', status: 'Returned' },
];

const addresses = [
{ label: 'Home', name: 'John Doe', address: '123 Main Street, Apt 4B', city: 'New York, NY 10001', default: true },
{ label: 'Office', name: 'John Doe', address: '456 Business Ave, Floor 12', city: 'New York, NY 10018', default: false },
];

return (
<div className="min-h-screen bg-background">
<div className="max-w-5xl mx-auto px-6 py-10">
<h1 className="text-3xl font-bold text-foreground mb-2">My Account</h1>
<p className="text-text-secondary mb-10">Manage your profile, orders, and preferences</p>

<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Sidebar */}
<div className="lg:col-span-1">
<div className="border border-border rounded-theme bg-card p-6">
<div className="flex items-center gap-4 mb-6">
<div className="w-14 h-14 rounded-full bg-primary flex items-center justify-center text-white font-bold text-lg">
JD
</div>
<div>
<p className="font-semibold text-foreground">John Doe</p>
<p className="text-sm text-text-secondary">john@example.com</p>
</div>
</div>
<nav>
<ul className="space-y-1">
{['Orders', 'Addresses', 'Payment Methods', 'Wishlist', 'Settings'].map((item, i) => (
<li key={item}>
<button className={`w-full text-left px-4 py-2.5 rounded-theme text-sm transition ${i === 0 ? 'bg-primary text-white font-medium' : 'text-text-secondary hover:bg-bg-secondary hover:text-foreground'}`}>
{item}
</button>
</li>
))}
</ul>
</nav>
<button className="mt-6 w-full py-2 text-sm text-text-secondary border border-border rounded-theme hover:bg-bg-secondary transition">
Sign Out
</button>
</div>
</div>

{/* Main Content */}
<div className="lg:col-span-2 space-y-8">
{/* Recent Orders */}
<div className="border border-border rounded-theme bg-card">
<div className="px-6 py-4 border-b border-border flex items-center justify-between">
<h2 className="text-lg font-semibold text-foreground">Recent Orders</h2>
<button className="text-sm text-primary hover:underline">View All</button>
</div>
<div className="divide-y divide-border">
{orders.map((order) => (
<div key={order.id} className="px-6 py-4 flex items-center justify-between">
<div>
<p className="text-sm font-medium text-foreground">{order.id}</p>
<p className="text-xs text-text-secondary mt-0.5">{order.date} &middot; {order.items} item{order.items > 1 ? 's' : ''}</p>
</div>
<div className="text-right">
<p className="text-sm font-semibold text-foreground">{order.total}</p>
<span className={`inline-flex px-2 py-0.5 text-xs font-medium rounded-full mt-1 ${
order.status === 'Delivered' ? 'bg-green-100 text-green-700' : 'bg-yellow-100 text-yellow-700'
}`}>
{order.status}
</span>
</div>
</div>
))}
</div>
</div>

{/* Saved Addresses */}
<div className="border border-border rounded-theme bg-card">
<div className="px-6 py-4 border-b border-border flex items-center justify-between">
<h2 className="text-lg font-semibold text-foreground">Saved Addresses</h2>
<button className="text-sm text-primary hover:underline">Add New</button>
</div>
<div className="p-6 grid grid-cols-1 sm:grid-cols-2 gap-4">
{addresses.map((addr) => (
<div key={addr.label} className={`border rounded-theme p-4 ${addr.default ? 'border-primary' : 'border-border'}`}>
<div className="flex items-center justify-between mb-2">
<span className="text-sm font-semibold text-foreground">{addr.label}</span>
{addr.default && (
<span className="px-2 py-0.5 text-xs font-medium rounded-full bg-primary/10 text-primary">Default</span>
)}
</div>
<p className="text-sm text-foreground">{addr.name}</p>
<p className="text-sm text-text-secondary">{addr.address}</p>
<p className="text-sm text-text-secondary">{addr.city}</p>
<div className="mt-3 flex gap-3">
<button className="text-xs text-primary hover:underline">Edit</button>
<button className="text-xs text-text-secondary hover:text-red-500">Remove</button>
</div>
</div>
))}
</div>
</div>

{/* Account Settings */}
<div className="border border-border rounded-theme bg-card p-6">
<h2 className="text-lg font-semibold text-foreground mb-6">Account Settings</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-foreground mb-1.5">First Name</label>
<input type="text" defaultValue="John" className="w-full px-4 py-2.5 rounded-theme border border-border bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary" />
</div>
<div>
<label className="block text-sm font-medium text-foreground mb-1.5">Last Name</label>
<input type="text" defaultValue="Doe" className="w-full px-4 py-2.5 rounded-theme border border-border bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary" />
</div>
<div>
<label className="block text-sm font-medium text-foreground mb-1.5">Email</label>
<input type="email" defaultValue="john@example.com" className="w-full px-4 py-2.5 rounded-theme border border-border bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary" />
</div>
<div>
<label className="block text-sm font-medium text-foreground mb-1.5">Phone</label>
<input type="tel" defaultValue="+1 (555) 123-4567" className="w-full px-4 py-2.5 rounded-theme border border-border bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary" />
</div>
</div>
<button className="mt-6 px-6 py-2.5 rounded-theme bg-primary text-white font-medium hover:bg-primary-hover transition">
Save Changes
</button>
</div>
</div>
</div>
</div>
</div>
);
}
13 changes: 13 additions & 0 deletions templates/ecommerce/vite-react/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en" data-theme="{{design}}">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{projectName}} - Store</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
26 changes: 26 additions & 0 deletions templates/ecommerce/vite-react/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "{{projectName}}",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.23.0"
},
"devDependencies": {
"@types/react": "^18.3.0",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.2.1",
"autoprefixer": "^10.4.19",
"postcss": "^8.4.38",
"tailwindcss": "^3.4.3",
"typescript": "^5.4.5",
"vite": "^5.2.11"
}
}
6 changes: 6 additions & 0 deletions templates/ecommerce/vite-react/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
30 changes: 30 additions & 0 deletions templates/ecommerce/vite-react/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* ⚠️ SEO WARNING: This is a client-side SPA (Vite + React).
* E-commerce stores typically need SSR for SEO and initial page load performance.
* Consider using the Next.js App Router architecture for public-facing stores.
* This Vite SPA is best suited for internal admin panels or private storefronts.
*/

import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Layout from './components/Layout';
import Home from './pages/Home';
import Products from './pages/Products';
import ProductDetail from './pages/ProductDetail';
import Cart from './pages/Cart';
import Account from './pages/Account';

export default function App() {
return (
<BrowserRouter>
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<Home />} />
<Route path="/products" element={<Products />} />
<Route path="/products/:slug" element={<ProductDetail />} />
<Route path="/cart" element={<Cart />} />
<Route path="/account" element={<Account />} />
</Route>
</Routes>
</BrowserRouter>
);
}
74 changes: 74 additions & 0 deletions templates/ecommerce/vite-react/src/components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { Link, Outlet } from 'react-router-dom';
import { navLinks } from '../lib/nav';

function Navbar() {
return (
<header className="border-b border-border bg-card">
<nav className="max-w-7xl mx-auto px-6 h-16 flex items-center justify-between">
<Link to="/" className="text-xl font-bold text-primary">
{{projectName}}
</Link>
<ul className="flex gap-6">
{navLinks.map((link) => (
<li key={link.href}>
<Link to={link.href} className="text-text-secondary hover:text-foreground transition">
{link.label}
</Link>
</li>
))}
</ul>
<Link to="/cart" className="relative text-text-secondary hover:text-foreground transition">
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" />
</svg>
<span className="absolute -top-1 -right-1 w-4 h-4 bg-primary text-white text-xs rounded-full flex items-center justify-center">3</span>
</Link>
</nav>
</header>
);
}

function Sidebar() {
return (
<aside className="w-64 border-r border-border bg-card min-h-screen p-6">
<Link to="/" className="text-xl font-bold text-primary block mb-8">
{{projectName}}
</Link>
<nav>
<ul className="space-y-2">
{navLinks.map((link) => (
<li key={link.href}>
<Link to={link.href} className="block px-4 py-2 rounded-theme text-text-secondary hover:bg-bg-secondary hover:text-foreground transition">
{link.label}
</Link>
</li>
))}
</ul>
</nav>
</aside>
);
}

export default function Layout() {
const useSidebar = ('{{includeSidebar}}' as string) === 'true';

if (useSidebar) {
return (
<div className="flex">
<Sidebar />
<main className="flex-1">
<Outlet />
</main>
</div>
);
}

return (
<>
<Navbar />
<main>
<Outlet />
</main>
</>
);
}
48 changes: 48 additions & 0 deletions templates/ecommerce/vite-react/src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
--bg-color: #ffffff;
--bg-secondary: #f9fafb;
--text-color: #111827;
--text-secondary: #6b7280;
--primary: #2563eb;
--primary-hover: #1d4ed8;
--border-color: #e5e7eb;
--card-bg: #ffffff;
--font-family: 'Inter', system-ui, sans-serif;
--radius: 0.5rem;
}

[data-theme="Glassmorphism"] {
--bg-color: #0f172a;
--bg-secondary: rgba(255, 255, 255, 0.05);
--text-color: #f1f5f9;
--text-secondary: #94a3b8;
--primary: #8b5cf6;
--primary-hover: #7c3aed;
--border-color: rgba(255, 255, 255, 0.1);
--card-bg: rgba(255, 255, 255, 0.08);
--font-family: 'Inter', system-ui, sans-serif;
--radius: 1rem;
}

[data-theme="Dark Terminal"] {
--bg-color: #000000;
--bg-secondary: #0a0a0a;
--text-color: #3fb950;
--text-secondary: #8b949e;
--primary: #3fb950;
--primary-hover: #2ea043;
--border-color: #21262d;
--card-bg: #0d1117;
--font-family: 'JetBrains Mono', 'Fira Code', monospace;
--radius: 0.25rem;
}

body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: var(--font-family);
}
10 changes: 10 additions & 0 deletions templates/ecommerce/vite-react/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>,
);
Loading
Loading