From 90816c6bbcce4922a8e49220a26f9687820fc2ed Mon Sep 17 00:00:00 2001 From: Josephat-S Date: Tue, 26 May 2026 10:36:43 +0200 Subject: [PATCH] Implement Vite + React SPA for E-Commerce Template --- index.js | 2 +- scripts/test-single-template.js | 4 +- scripts/test-templates.js | 4 +- .../nextjs-monolith/app/account/page.tsx | 137 ++++++++++++++++++ templates/ecommerce/vite-react/index.html | 13 ++ templates/ecommerce/vite-react/package.json | 26 ++++ .../ecommerce/vite-react/postcss.config.js | 6 + templates/ecommerce/vite-react/src/App.tsx | 30 ++++ .../vite-react/src/components/Layout.tsx | 74 ++++++++++ templates/ecommerce/vite-react/src/index.css | 48 ++++++ templates/ecommerce/vite-react/src/main.tsx | 10 ++ .../vite-react/src/pages/Account.tsx | 98 +++++++++++++ .../ecommerce/vite-react/src/pages/Cart.tsx | 97 +++++++++++++ .../ecommerce/vite-react/src/pages/Home.tsx | 96 ++++++++++++ .../vite-react/src/pages/ProductDetail.tsx | 101 +++++++++++++ .../vite-react/src/pages/Products.tsx | 76 ++++++++++ .../ecommerce/vite-react/tailwind.config.ts | 28 ++++ templates/ecommerce/vite-react/tsconfig.json | 21 +++ .../ecommerce/vite-react/tsconfig.node.json | 10 ++ templates/ecommerce/vite-react/vite.config.ts | 6 + 20 files changed, 882 insertions(+), 5 deletions(-) create mode 100644 templates/ecommerce/nextjs-monolith/app/account/page.tsx create mode 100644 templates/ecommerce/vite-react/index.html create mode 100644 templates/ecommerce/vite-react/package.json create mode 100644 templates/ecommerce/vite-react/postcss.config.js create mode 100644 templates/ecommerce/vite-react/src/App.tsx create mode 100644 templates/ecommerce/vite-react/src/components/Layout.tsx create mode 100644 templates/ecommerce/vite-react/src/index.css create mode 100644 templates/ecommerce/vite-react/src/main.tsx create mode 100644 templates/ecommerce/vite-react/src/pages/Account.tsx create mode 100644 templates/ecommerce/vite-react/src/pages/Cart.tsx create mode 100644 templates/ecommerce/vite-react/src/pages/Home.tsx create mode 100644 templates/ecommerce/vite-react/src/pages/ProductDetail.tsx create mode 100644 templates/ecommerce/vite-react/src/pages/Products.tsx create mode 100644 templates/ecommerce/vite-react/tailwind.config.ts create mode 100644 templates/ecommerce/vite-react/tsconfig.json create mode 100644 templates/ecommerce/vite-react/tsconfig.node.json create mode 100644 templates/ecommerce/vite-react/vite.config.ts diff --git a/index.js b/index.js index 46398ba..d59cd49 100755 --- a/index.js +++ b/index.js @@ -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: [] }, }; diff --git a/scripts/test-single-template.js b/scripts/test-single-template.js index 4b4024e..f1952ad 100644 --- a/scripts/test-single-template.js +++ b/scripts/test-single-template.js @@ -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'], diff --git a/scripts/test-templates.js b/scripts/test-templates.js index 115122a..eddb1bd 100644 --- a/scripts/test-templates.js +++ b/scripts/test-templates.js @@ -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'], diff --git a/templates/ecommerce/nextjs-monolith/app/account/page.tsx b/templates/ecommerce/nextjs-monolith/app/account/page.tsx new file mode 100644 index 0000000..7650ac1 --- /dev/null +++ b/templates/ecommerce/nextjs-monolith/app/account/page.tsx @@ -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 ( +
+
+

My Account

+

Manage your profile, orders, and preferences

+ +
+ {/* Sidebar */} +
+
+
+
+ JD +
+
+

John Doe

+

john@example.com

+
+
+ + +
+
+ + {/* Main Content */} +
+ {/* Recent Orders */} +
+
+

Recent Orders

+ +
+
+ {orders.map((order) => ( +
+
+

{order.id}

+

{order.date} · {order.items} item{order.items > 1 ? 's' : ''}

+
+
+

{order.total}

+ + {order.status} + +
+
+ ))} +
+
+ + {/* Saved Addresses */} +
+
+

Saved Addresses

+ +
+
+ {addresses.map((addr) => ( +
+
+ {addr.label} + {addr.default && ( + Default + )} +
+

{addr.name}

+

{addr.address}

+

{addr.city}

+
+ + +
+
+ ))} +
+
+ + {/* Account Settings */} +
+

Account Settings

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+
+ ); +} diff --git a/templates/ecommerce/vite-react/index.html b/templates/ecommerce/vite-react/index.html new file mode 100644 index 0000000..93ad3da --- /dev/null +++ b/templates/ecommerce/vite-react/index.html @@ -0,0 +1,13 @@ + + + + + + + {{projectName}} - Store + + +
+ + + diff --git a/templates/ecommerce/vite-react/package.json b/templates/ecommerce/vite-react/package.json new file mode 100644 index 0000000..086d63f --- /dev/null +++ b/templates/ecommerce/vite-react/package.json @@ -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" + } +} diff --git a/templates/ecommerce/vite-react/postcss.config.js b/templates/ecommerce/vite-react/postcss.config.js new file mode 100644 index 0000000..2aa7205 --- /dev/null +++ b/templates/ecommerce/vite-react/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/templates/ecommerce/vite-react/src/App.tsx b/templates/ecommerce/vite-react/src/App.tsx new file mode 100644 index 0000000..8385ef8 --- /dev/null +++ b/templates/ecommerce/vite-react/src/App.tsx @@ -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 ( + + + }> + } /> + } /> + } /> + } /> + } /> + + + + ); +} diff --git a/templates/ecommerce/vite-react/src/components/Layout.tsx b/templates/ecommerce/vite-react/src/components/Layout.tsx new file mode 100644 index 0000000..cb5dcd8 --- /dev/null +++ b/templates/ecommerce/vite-react/src/components/Layout.tsx @@ -0,0 +1,74 @@ +import { Link, Outlet } from 'react-router-dom'; +import { navLinks } from '../lib/nav'; + +function Navbar() { + return ( +
+ +
+ ); +} + +function Sidebar() { + return ( + + ); +} + +export default function Layout() { + const useSidebar = ('{{includeSidebar}}' as string) === 'true'; + + if (useSidebar) { + return ( +
+ +
+ +
+
+ ); + } + + return ( + <> + +
+ +
+ + ); +} diff --git a/templates/ecommerce/vite-react/src/index.css b/templates/ecommerce/vite-react/src/index.css new file mode 100644 index 0000000..7a2f894 --- /dev/null +++ b/templates/ecommerce/vite-react/src/index.css @@ -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); +} diff --git a/templates/ecommerce/vite-react/src/main.tsx b/templates/ecommerce/vite-react/src/main.tsx new file mode 100644 index 0000000..9aa52ff --- /dev/null +++ b/templates/ecommerce/vite-react/src/main.tsx @@ -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( + + + , +); diff --git a/templates/ecommerce/vite-react/src/pages/Account.tsx b/templates/ecommerce/vite-react/src/pages/Account.tsx new file mode 100644 index 0000000..9129d5a --- /dev/null +++ b/templates/ecommerce/vite-react/src/pages/Account.tsx @@ -0,0 +1,98 @@ +export default function Account() { + 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' }, + ]; + + return ( +
+
+

My Account

+

Manage your profile, orders, and preferences

+ +
+ {/* Profile Card */} +
+
+
+
+ JD +
+
+

John Doe

+

john@example.com

+
+
+ + +
+
+ + {/* Orders */} +
+
+
+

Recent Orders

+
+
+ {orders.map((order) => ( +
+
+

{order.id}

+

{order.date} · {order.items} items

+
+
+

{order.total}

+ + {order.status} + +
+
+ ))} +
+
+ + {/* Settings */} +
+

Account Settings

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+
+ ); +} diff --git a/templates/ecommerce/vite-react/src/pages/Cart.tsx b/templates/ecommerce/vite-react/src/pages/Cart.tsx new file mode 100644 index 0000000..d7d1c70 --- /dev/null +++ b/templates/ecommerce/vite-react/src/pages/Cart.tsx @@ -0,0 +1,97 @@ +import { Link } from 'react-router-dom'; + +export default function Cart() { + const items = [ + { name: 'Classic White Sneakers', price: 89.99, quantity: 1, color: 'White', size: '10', imageColor: '#f1f5f9' }, + { name: 'Leather Crossbody Bag', price: 129.99, quantity: 1, color: 'Tan', size: 'One Size', imageColor: '#d4a574' }, + { name: 'Oversized Wool Coat', price: 249.99, quantity: 1, color: 'Charcoal', size: 'M', imageColor: '#1e293b' }, + ]; + + const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0); + const tax = subtotal * 0.08; + const total = subtotal + tax; + + return ( +
+
+

Shopping Cart

+

{items.length} items in your cart

+ +
+ {/* Items */} +
+
+ {items.map((item, index) => ( +
+
+
+
+
+

{item.name}

+

{item.color} · Size {item.size}

+
+ +
+
+
+ + {item.quantity} + +
+ ${(item.price * item.quantity).toFixed(2)} +
+
+
+ ))} +
+ + ← Continue Shopping + +
+ + {/* Summary */} +
+
+

Order Summary

+
+
+ Subtotal + ${subtotal.toFixed(2)} +
+
+ Shipping + Calculated at checkout +
+
+ Estimated Tax + ${tax.toFixed(2)} +
+
+
+
+ Total + ${total.toFixed(2)} +
+
+ +
+

Accepted Payment Methods

+
+ {['Visa', 'Mastercard', 'Amex', 'PayPal'].map((m) => ( + {m} + ))} +
+
+
+
+
+
+
+ ); +} diff --git a/templates/ecommerce/vite-react/src/pages/Home.tsx b/templates/ecommerce/vite-react/src/pages/Home.tsx new file mode 100644 index 0000000..a29177b --- /dev/null +++ b/templates/ecommerce/vite-react/src/pages/Home.tsx @@ -0,0 +1,96 @@ +import { Link } from 'react-router-dom'; + +export default function Home() { + const categories = [ + { name: 'Women', count: 124, color: '#f9a8d4' }, + { name: 'Men', count: 98, color: '#93c5fd' }, + { name: 'Accessories', count: 67, color: '#fcd34d' }, + { name: 'Sale', count: 45, color: '#fca5a5' }, + ]; + + const featured = [ + { slug: 'classic-white-sneakers', name: 'Classic White Sneakers', price: '$89.99', color: '#f1f5f9' }, + { slug: 'leather-crossbody-bag', name: 'Leather Crossbody Bag', price: '$129.99', originalPrice: '$159.99', color: '#d4a574' }, + { slug: 'oversized-wool-coat', name: 'Oversized Wool Coat', price: '$249.99', color: '#1e293b' }, + { slug: 'silk-scarf', name: 'Silk Scarf Collection', price: '$59.99', color: '#c4b5fd' }, + ]; + + return ( +
+ {/* Hero */} +
+
+
+
+

+ Welcome to {{projectName}} +

+

+ Discover our curated {{variant}} collection — where timeless elegance meets modern design. +

+
+ + Shop Now + + + View Collections + +
+
+
+
+ + {/* Categories */} +
+

Shop by Category

+
+ {categories.map((cat) => ( + +
+
+

{cat.name}

+

{cat.count} items

+
+ + ))} +
+
+ + {/* Featured Products */} +
+
+

Featured Products

+ View All → +
+
+ {featured.map((product) => ( + +
+
+
+
+

{product.name}

+
+ {product.price} + {product.originalPrice && {product.originalPrice}} +
+
+ + ))} +
+
+ + {/* Newsletter */} +
+
+

Join the {{projectName}} Community

+

Get 10% off your first order when you subscribe.

+
+ + +
+
+
+
+ ); +} diff --git a/templates/ecommerce/vite-react/src/pages/ProductDetail.tsx b/templates/ecommerce/vite-react/src/pages/ProductDetail.tsx new file mode 100644 index 0000000..8df6720 --- /dev/null +++ b/templates/ecommerce/vite-react/src/pages/ProductDetail.tsx @@ -0,0 +1,101 @@ +import { useParams, Link } from 'react-router-dom'; + +export default function ProductDetail() { + const { slug } = useParams(); + const productName = (slug || 'product').replace(/-/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()); + + const sizes = ['XS', 'S', 'M', 'L', 'XL']; + const colors = [ + { name: 'Black', hex: '#1a1a1a' }, + { name: 'White', hex: '#ffffff' }, + { name: 'Navy', hex: '#1e3a5f' }, + { name: 'Red', hex: '#dc2626' }, + ]; + + return ( +
+
+ {/* Breadcrumb */} + + +
+ {/* Images */} +
+
+
+ {[0, 1, 2, 3].map((i) => ( +
+ ))} +
+
+ + {/* Info */} +
+

{productName}

+
+
+ {[1, 2, 3, 4, 5].map((star) => ( + + + + ))} +
+ 4.5 (128 reviews) +
+ +
+ $99.99 + $129.99 +
+ +

+ Crafted from premium materials with meticulous attention to detail. This piece combines timeless design with modern comfort. +

+ + {/* Colors */} +
+

Color

+
+ {colors.map((c, i) => ( +
+
+ + {/* Sizes */} +
+

Size

+
+ {sizes.map((size, i) => ( + + ))} +
+
+ + {/* Quantity */} +
+

Quantity

+
+ + 1 + +
+
+ + +
+
+
+
+ ); +} diff --git a/templates/ecommerce/vite-react/src/pages/Products.tsx b/templates/ecommerce/vite-react/src/pages/Products.tsx new file mode 100644 index 0000000..3d1e0a4 --- /dev/null +++ b/templates/ecommerce/vite-react/src/pages/Products.tsx @@ -0,0 +1,76 @@ +import { Link } from 'react-router-dom'; + +export default function Products() { + const products = [ + { slug: 'classic-white-sneakers', name: 'Classic White Sneakers', price: '$89.99', color: '#f1f5f9' }, + { slug: 'leather-crossbody-bag', name: 'Leather Crossbody Bag', price: '$129.99', originalPrice: '$159.99', badge: 'Sale', color: '#d4a574' }, + { slug: 'oversized-wool-coat', name: 'Oversized Wool Coat', price: '$249.99', badge: 'New', color: '#1e293b' }, + { slug: 'silk-scarf', name: 'Silk Scarf Collection', price: '$59.99', color: '#c4b5fd' }, + { slug: 'cashmere-sweater', name: 'Cashmere V-Neck Sweater', price: '$179.99', originalPrice: '$219.99', badge: 'Sale', color: '#fef3c7' }, + { slug: 'tailored-blazer', name: 'Tailored Linen Blazer', price: '$199.99', badge: 'New', color: '#e2e8f0' }, + { slug: 'canvas-tote', name: 'Canvas Tote Bag', price: '$49.99', color: '#d6d3d1' }, + { slug: 'gold-earrings', name: 'Gold Hoop Earrings', price: '$39.99', color: '#fde68a' }, + { slug: 'chelsea-boots', name: 'Leather Chelsea Boots', price: '$189.99', badge: 'New', color: '#292524' }, + ]; + + const categories = ['Clothing', 'Shoes', 'Bags', 'Accessories']; + + return ( +
+
+
+

All Products

+

Showing {products.length} products

+
+ +
+ {/* Filters */} + + + {/* Grid */} +
+
+ {products.map((product) => ( + +
+
+ {product.badge && ( + + {product.badge} + + )} +
+
+

{product.name}

+
+ {product.price} + {product.originalPrice && {product.originalPrice}} +
+
+ + ))} +
+
+
+
+
+ ); +} diff --git a/templates/ecommerce/vite-react/tailwind.config.ts b/templates/ecommerce/vite-react/tailwind.config.ts new file mode 100644 index 0000000..c677f76 --- /dev/null +++ b/templates/ecommerce/vite-react/tailwind.config.ts @@ -0,0 +1,28 @@ +import type { Config } from 'tailwindcss'; + +const config: Config = { + content: ['./index.html', './src/**/*.{ts,tsx}'], + theme: { + extend: { + colors: { + background: 'var(--bg-color)', + 'bg-secondary': 'var(--bg-secondary)', + foreground: 'var(--text-color)', + 'text-secondary': 'var(--text-secondary)', + primary: 'var(--primary)', + 'primary-hover': 'var(--primary-hover)', + border: 'var(--border-color)', + card: 'var(--card-bg)', + }, + borderRadius: { + theme: 'var(--radius)', + }, + fontFamily: { + theme: 'var(--font-family)', + }, + }, + }, + plugins: [], +}; + +export default config; diff --git a/templates/ecommerce/vite-react/tsconfig.json b/templates/ecommerce/vite-react/tsconfig.json new file mode 100644 index 0000000..3934b8f --- /dev/null +++ b/templates/ecommerce/vite-react/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/templates/ecommerce/vite-react/tsconfig.node.json b/templates/ecommerce/vite-react/tsconfig.node.json new file mode 100644 index 0000000..42872c5 --- /dev/null +++ b/templates/ecommerce/vite-react/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/templates/ecommerce/vite-react/vite.config.ts b/templates/ecommerce/vite-react/vite.config.ts new file mode 100644 index 0000000..0466183 --- /dev/null +++ b/templates/ecommerce/vite-react/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], +});