Skip to content

Commit 2bf47fd

Browse files
authored
Merge pull request #65 from Ebyte-Lab/feat/vite-and-react-spa
Implement Vite and React for E-Commerce SPA Template
2 parents c03aa37 + 90816c6 commit 2bf47fd

20 files changed

Lines changed: 882 additions & 5 deletions

index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ async function createAction(projectName, options) {
328328
// Architecture availability map
329329
const ARCHITECTURES = {
330330
'nextjs-monolith': { label: 'Next.js 14 — App Router', available: ['portfolio', 'ecommerce', 'school', 'saas', 'blog'] },
331-
'vite-react': { label: 'Vite + React 18 — SPA', available: ['saas'] },
331+
'vite-react': { label: 'Vite + React 18 — SPA', available: ['portfolio', 'ecommerce', 'saas'] },
332332
'nextjs-turborepo': { label: 'Turborepo — Monorepo', available: [] },
333333
};
334334

scripts/test-single-template.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ if (!template) {
2121
const DESIGNS = ['Minimal Clean', 'Dark Terminal', 'Glassmorphism'];
2222

2323
const ARCHITECTURES = {
24-
portfolio: ['nextjs-monolith'],
25-
ecommerce: ['nextjs-monolith'],
24+
portfolio: ['nextjs-monolith', 'vite-react'],
25+
ecommerce: ['nextjs-monolith', 'vite-react'],
2626
school: ['nextjs-monolith'],
2727
saas: ['nextjs-monolith', 'vite-react'],
2828
blog: ['nextjs-monolith'],

scripts/test-templates.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ const DESIGNS = ['Minimal Clean', 'Dark Terminal', 'Glassmorphism'];
1919

2020
// Template → available architectures
2121
const ARCHITECTURES = {
22-
portfolio: ['nextjs-monolith'],
23-
ecommerce: ['nextjs-monolith'],
22+
portfolio: ['nextjs-monolith', 'vite-react'],
23+
ecommerce: ['nextjs-monolith', 'vite-react'],
2424
school: ['nextjs-monolith'],
2525
saas: ['nextjs-monolith', 'vite-react'],
2626
blog: ['nextjs-monolith'],
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import Link from 'next/link';
2+
3+
export default function AccountPage() {
4+
const orders = [
5+
{ id: 'ORD-2024-001', date: 'Jun 15, 2024', items: 3, total: '$469.97', status: 'Delivered' },
6+
{ id: 'ORD-2024-002', date: 'Jun 2, 2024', items: 1, total: '$89.99', status: 'Delivered' },
7+
{ id: 'ORD-2024-003', date: 'May 18, 2024', items: 2, total: '$189.98', status: 'Delivered' },
8+
{ id: 'ORD-2024-004', date: 'May 5, 2024', items: 1, total: '$249.99', status: 'Returned' },
9+
];
10+
11+
const addresses = [
12+
{ label: 'Home', name: 'John Doe', address: '123 Main Street, Apt 4B', city: 'New York, NY 10001', default: true },
13+
{ label: 'Office', name: 'John Doe', address: '456 Business Ave, Floor 12', city: 'New York, NY 10018', default: false },
14+
];
15+
16+
return (
17+
<div className="min-h-screen bg-background">
18+
<div className="max-w-5xl mx-auto px-6 py-10">
19+
<h1 className="text-3xl font-bold text-foreground mb-2">My Account</h1>
20+
<p className="text-text-secondary mb-10">Manage your profile, orders, and preferences</p>
21+
22+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
23+
{/* Sidebar */}
24+
<div className="lg:col-span-1">
25+
<div className="border border-border rounded-theme bg-card p-6">
26+
<div className="flex items-center gap-4 mb-6">
27+
<div className="w-14 h-14 rounded-full bg-primary flex items-center justify-center text-white font-bold text-lg">
28+
JD
29+
</div>
30+
<div>
31+
<p className="font-semibold text-foreground">John Doe</p>
32+
<p className="text-sm text-text-secondary">john@example.com</p>
33+
</div>
34+
</div>
35+
<nav>
36+
<ul className="space-y-1">
37+
{['Orders', 'Addresses', 'Payment Methods', 'Wishlist', 'Settings'].map((item, i) => (
38+
<li key={item}>
39+
<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'}`}>
40+
{item}
41+
</button>
42+
</li>
43+
))}
44+
</ul>
45+
</nav>
46+
<button className="mt-6 w-full py-2 text-sm text-text-secondary border border-border rounded-theme hover:bg-bg-secondary transition">
47+
Sign Out
48+
</button>
49+
</div>
50+
</div>
51+
52+
{/* Main Content */}
53+
<div className="lg:col-span-2 space-y-8">
54+
{/* Recent Orders */}
55+
<div className="border border-border rounded-theme bg-card">
56+
<div className="px-6 py-4 border-b border-border flex items-center justify-between">
57+
<h2 className="text-lg font-semibold text-foreground">Recent Orders</h2>
58+
<button className="text-sm text-primary hover:underline">View All</button>
59+
</div>
60+
<div className="divide-y divide-border">
61+
{orders.map((order) => (
62+
<div key={order.id} className="px-6 py-4 flex items-center justify-between">
63+
<div>
64+
<p className="text-sm font-medium text-foreground">{order.id}</p>
65+
<p className="text-xs text-text-secondary mt-0.5">{order.date} &middot; {order.items} item{order.items > 1 ? 's' : ''}</p>
66+
</div>
67+
<div className="text-right">
68+
<p className="text-sm font-semibold text-foreground">{order.total}</p>
69+
<span className={`inline-flex px-2 py-0.5 text-xs font-medium rounded-full mt-1 ${
70+
order.status === 'Delivered' ? 'bg-green-100 text-green-700' : 'bg-yellow-100 text-yellow-700'
71+
}`}>
72+
{order.status}
73+
</span>
74+
</div>
75+
</div>
76+
))}
77+
</div>
78+
</div>
79+
80+
{/* Saved Addresses */}
81+
<div className="border border-border rounded-theme bg-card">
82+
<div className="px-6 py-4 border-b border-border flex items-center justify-between">
83+
<h2 className="text-lg font-semibold text-foreground">Saved Addresses</h2>
84+
<button className="text-sm text-primary hover:underline">Add New</button>
85+
</div>
86+
<div className="p-6 grid grid-cols-1 sm:grid-cols-2 gap-4">
87+
{addresses.map((addr) => (
88+
<div key={addr.label} className={`border rounded-theme p-4 ${addr.default ? 'border-primary' : 'border-border'}`}>
89+
<div className="flex items-center justify-between mb-2">
90+
<span className="text-sm font-semibold text-foreground">{addr.label}</span>
91+
{addr.default && (
92+
<span className="px-2 py-0.5 text-xs font-medium rounded-full bg-primary/10 text-primary">Default</span>
93+
)}
94+
</div>
95+
<p className="text-sm text-foreground">{addr.name}</p>
96+
<p className="text-sm text-text-secondary">{addr.address}</p>
97+
<p className="text-sm text-text-secondary">{addr.city}</p>
98+
<div className="mt-3 flex gap-3">
99+
<button className="text-xs text-primary hover:underline">Edit</button>
100+
<button className="text-xs text-text-secondary hover:text-red-500">Remove</button>
101+
</div>
102+
</div>
103+
))}
104+
</div>
105+
</div>
106+
107+
{/* Account Settings */}
108+
<div className="border border-border rounded-theme bg-card p-6">
109+
<h2 className="text-lg font-semibold text-foreground mb-6">Account Settings</h2>
110+
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
111+
<div>
112+
<label className="block text-sm font-medium text-foreground mb-1.5">First Name</label>
113+
<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" />
114+
</div>
115+
<div>
116+
<label className="block text-sm font-medium text-foreground mb-1.5">Last Name</label>
117+
<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" />
118+
</div>
119+
<div>
120+
<label className="block text-sm font-medium text-foreground mb-1.5">Email</label>
121+
<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" />
122+
</div>
123+
<div>
124+
<label className="block text-sm font-medium text-foreground mb-1.5">Phone</label>
125+
<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" />
126+
</div>
127+
</div>
128+
<button className="mt-6 px-6 py-2.5 rounded-theme bg-primary text-white font-medium hover:bg-primary-hover transition">
129+
Save Changes
130+
</button>
131+
</div>
132+
</div>
133+
</div>
134+
</div>
135+
</div>
136+
);
137+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<html lang="en" data-theme="{{design}}">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>{{projectName}} - Store</title>
8+
</head>
9+
<body>
10+
<div id="root"></div>
11+
<script type="module" src="/src/main.tsx"></script>
12+
</body>
13+
</html>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "{{projectName}}",
3+
"version": "1.0.0",
4+
"private": true,
5+
"type": "module",
6+
"scripts": {
7+
"dev": "vite",
8+
"build": "tsc && vite build",
9+
"preview": "vite preview"
10+
},
11+
"dependencies": {
12+
"react": "^18.3.1",
13+
"react-dom": "^18.3.1",
14+
"react-router-dom": "^6.23.0"
15+
},
16+
"devDependencies": {
17+
"@types/react": "^18.3.0",
18+
"@types/react-dom": "^18.3.0",
19+
"@vitejs/plugin-react": "^4.2.1",
20+
"autoprefixer": "^10.4.19",
21+
"postcss": "^8.4.38",
22+
"tailwindcss": "^3.4.3",
23+
"typescript": "^5.4.5",
24+
"vite": "^5.2.11"
25+
}
26+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
plugins: {
3+
tailwindcss: {},
4+
autoprefixer: {},
5+
},
6+
};
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* ⚠️ SEO WARNING: This is a client-side SPA (Vite + React).
3+
* E-commerce stores typically need SSR for SEO and initial page load performance.
4+
* Consider using the Next.js App Router architecture for public-facing stores.
5+
* This Vite SPA is best suited for internal admin panels or private storefronts.
6+
*/
7+
8+
import { BrowserRouter, Routes, Route } from 'react-router-dom';
9+
import Layout from './components/Layout';
10+
import Home from './pages/Home';
11+
import Products from './pages/Products';
12+
import ProductDetail from './pages/ProductDetail';
13+
import Cart from './pages/Cart';
14+
import Account from './pages/Account';
15+
16+
export default function App() {
17+
return (
18+
<BrowserRouter>
19+
<Routes>
20+
<Route element={<Layout />}>
21+
<Route path="/" element={<Home />} />
22+
<Route path="/products" element={<Products />} />
23+
<Route path="/products/:slug" element={<ProductDetail />} />
24+
<Route path="/cart" element={<Cart />} />
25+
<Route path="/account" element={<Account />} />
26+
</Route>
27+
</Routes>
28+
</BrowserRouter>
29+
);
30+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { Link, Outlet } from 'react-router-dom';
2+
import { navLinks } from '../lib/nav';
3+
4+
function Navbar() {
5+
return (
6+
<header className="border-b border-border bg-card">
7+
<nav className="max-w-7xl mx-auto px-6 h-16 flex items-center justify-between">
8+
<Link to="/" className="text-xl font-bold text-primary">
9+
{{projectName}}
10+
</Link>
11+
<ul className="flex gap-6">
12+
{navLinks.map((link) => (
13+
<li key={link.href}>
14+
<Link to={link.href} className="text-text-secondary hover:text-foreground transition">
15+
{link.label}
16+
</Link>
17+
</li>
18+
))}
19+
</ul>
20+
<Link to="/cart" className="relative text-text-secondary hover:text-foreground transition">
21+
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
22+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" />
23+
</svg>
24+
<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>
25+
</Link>
26+
</nav>
27+
</header>
28+
);
29+
}
30+
31+
function Sidebar() {
32+
return (
33+
<aside className="w-64 border-r border-border bg-card min-h-screen p-6">
34+
<Link to="/" className="text-xl font-bold text-primary block mb-8">
35+
{{projectName}}
36+
</Link>
37+
<nav>
38+
<ul className="space-y-2">
39+
{navLinks.map((link) => (
40+
<li key={link.href}>
41+
<Link to={link.href} className="block px-4 py-2 rounded-theme text-text-secondary hover:bg-bg-secondary hover:text-foreground transition">
42+
{link.label}
43+
</Link>
44+
</li>
45+
))}
46+
</ul>
47+
</nav>
48+
</aside>
49+
);
50+
}
51+
52+
export default function Layout() {
53+
const useSidebar = ('{{includeSidebar}}' as string) === 'true';
54+
55+
if (useSidebar) {
56+
return (
57+
<div className="flex">
58+
<Sidebar />
59+
<main className="flex-1">
60+
<Outlet />
61+
</main>
62+
</div>
63+
);
64+
}
65+
66+
return (
67+
<>
68+
<Navbar />
69+
<main>
70+
<Outlet />
71+
</main>
72+
</>
73+
);
74+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
@tailwind base;
2+
@tailwind components;
3+
@tailwind utilities;
4+
5+
:root {
6+
--bg-color: #ffffff;
7+
--bg-secondary: #f9fafb;
8+
--text-color: #111827;
9+
--text-secondary: #6b7280;
10+
--primary: #2563eb;
11+
--primary-hover: #1d4ed8;
12+
--border-color: #e5e7eb;
13+
--card-bg: #ffffff;
14+
--font-family: 'Inter', system-ui, sans-serif;
15+
--radius: 0.5rem;
16+
}
17+
18+
[data-theme="Glassmorphism"] {
19+
--bg-color: #0f172a;
20+
--bg-secondary: rgba(255, 255, 255, 0.05);
21+
--text-color: #f1f5f9;
22+
--text-secondary: #94a3b8;
23+
--primary: #8b5cf6;
24+
--primary-hover: #7c3aed;
25+
--border-color: rgba(255, 255, 255, 0.1);
26+
--card-bg: rgba(255, 255, 255, 0.08);
27+
--font-family: 'Inter', system-ui, sans-serif;
28+
--radius: 1rem;
29+
}
30+
31+
[data-theme="Dark Terminal"] {
32+
--bg-color: #000000;
33+
--bg-secondary: #0a0a0a;
34+
--text-color: #3fb950;
35+
--text-secondary: #8b949e;
36+
--primary: #3fb950;
37+
--primary-hover: #2ea043;
38+
--border-color: #21262d;
39+
--card-bg: #0d1117;
40+
--font-family: 'JetBrains Mono', 'Fira Code', monospace;
41+
--radius: 0.25rem;
42+
}
43+
44+
body {
45+
background-color: var(--bg-color);
46+
color: var(--text-color);
47+
font-family: var(--font-family);
48+
}

0 commit comments

Comments
 (0)