update
This commit is contained in:
parent
9174cb5375
commit
dd9691acdc
24 changed files with 511 additions and 270 deletions
|
|
@ -1,62 +0,0 @@
|
|||
"use client";
|
||||
import { useState } from "react";
|
||||
import { sendPasswordResetEmail } from "firebase/auth";
|
||||
import { auth } from "@/lib/firebase";
|
||||
import Navbar from "@/components/Navbar";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function ForgotPasswordPage() {
|
||||
const [email, setEmail] = useState("");
|
||||
const [message, setMessage] = useState("");
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const handleReset = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setMessage("");
|
||||
setError("");
|
||||
|
||||
try {
|
||||
await sendPasswordResetEmail(auth, email);
|
||||
setMessage("Password reset link sent to your email!");
|
||||
} catch (err: any) {
|
||||
setError(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<div className="flex min-h-screen items-center justify-center">
|
||||
<form
|
||||
className="p-6 bg-white rounded shadow-md w-full max-w-md"
|
||||
onSubmit={handleReset}
|
||||
>
|
||||
<h2 className="text-2xl font-bold mb-4 text-center">
|
||||
Forgot Password
|
||||
</h2>
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Enter your email"
|
||||
className="border p-2 w-full mb-2 rounded"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
<button
|
||||
className="w-full bg-blue-600 text-white p-2 rounded mt-2"
|
||||
type="submit"
|
||||
>
|
||||
Send Reset Link
|
||||
</button>
|
||||
<Link
|
||||
className="py-1 mt-3 bg-amber-400 text-white flex justify-center items-center"
|
||||
href={"/login"}
|
||||
>
|
||||
back to login
|
||||
</Link>
|
||||
{message && <p className="text-green-600 mt-2">{message}</p>}
|
||||
{error && <p className="text-red-600 mt-2">{error}</p>}
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
"use client";
|
||||
import { useState } from "react";
|
||||
import { signInWithEmailAndPassword, signInWithPopup } from "firebase/auth";
|
||||
import { auth, googleProvider } from "@/lib/firebase";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Navbar from "@/components/Navbar";
|
||||
|
||||
export default function LoginPage() {
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const router = useRouter();
|
||||
|
||||
const handleLogin = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
await signInWithEmailAndPassword(auth, email, password);
|
||||
router.push("/dashboard");
|
||||
} catch (error: any) {
|
||||
alert(error.message);
|
||||
}
|
||||
};
|
||||
|
||||
const handleGoogleLogin = async () => {
|
||||
try {
|
||||
await signInWithPopup(auth, googleProvider);
|
||||
router.push("/dashboard");
|
||||
} catch (error: any) {
|
||||
alert(error.message);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<div className="flex min-h-screen items-center justify-center">
|
||||
<form
|
||||
className="p-6 bg-white rounded shadow-md w-full max-w-md"
|
||||
onSubmit={handleLogin}
|
||||
>
|
||||
<h2 className="text-2xl font-bold mb-4 text-center">Login</h2>
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
className="border p-2 w-full mb-2 rounded"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
className="border p-2 w-full mb-2 rounded"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
<button
|
||||
className="w-full bg-green-600 text-white p-2 rounded mt-2"
|
||||
type="submit"
|
||||
>
|
||||
Login
|
||||
</button>
|
||||
<p className="mt-4 text-sm text-right">
|
||||
<a
|
||||
href="/forgot-password"
|
||||
className="text-blue-600 hover:underline"
|
||||
>
|
||||
Forgot Password?
|
||||
</a>
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleGoogleLogin}
|
||||
className="w-full bg-red-500 text-white p-2 rounded mt-2"
|
||||
>
|
||||
Login with Google
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
"use client";
|
||||
import { useState } from "react";
|
||||
import { createUserWithEmailAndPassword, signInWithPopup } from "firebase/auth";
|
||||
import { auth, googleProvider } from "@/lib/firebase";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Navbar from "@/components/Navbar";
|
||||
|
||||
export default function SignupPage() {
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const router = useRouter();
|
||||
|
||||
const handleSignup = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
await createUserWithEmailAndPassword(auth, email, password);
|
||||
router.push("/dashboard");
|
||||
} catch (error: any) {
|
||||
alert(error.message);
|
||||
}
|
||||
};
|
||||
|
||||
const handleGoogleLogin = async () => {
|
||||
try {
|
||||
await signInWithPopup(auth, googleProvider);
|
||||
router.push("/dashboard");
|
||||
} catch (error: any) {
|
||||
alert(error.message);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<div className="flex min-h-screen items-center justify-center">
|
||||
<form
|
||||
className="p-6 bg-white rounded shadow-md w-full max-w-md"
|
||||
onSubmit={handleSignup}
|
||||
>
|
||||
<h2 className="text-2xl font-bold mb-4 text-center">Signup</h2>
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
className="border p-2 w-full mb-2 rounded"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
className="border p-2 w-full mb-2 rounded"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
<button
|
||||
className="w-full bg-blue-600 text-white p-2 rounded mt-2"
|
||||
type="submit"
|
||||
>
|
||||
Signup
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleGoogleLogin}
|
||||
className="w-full bg-red-500 text-white p-2 rounded mt-2"
|
||||
>
|
||||
Signup with Google
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
29
app/builder/[site]/cart/page.tsx
Normal file
29
app/builder/[site]/cart/page.tsx
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
"use client";
|
||||
|
||||
import { getUserSites } from "@/lib/sites";
|
||||
import { templates } from "@/lib/template";
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
interface CartPageProps {
|
||||
params: { site: string };
|
||||
}
|
||||
|
||||
export default function CartPage({ params }: CartPageProps) {
|
||||
if (typeof window === "undefined") return null; // SSR safe
|
||||
const userSites = getUserSites();
|
||||
const site = userSites.find((s) => s.slug === params.site);
|
||||
if (!site) return notFound();
|
||||
|
||||
const Template = templates[site.template];
|
||||
const Header = Template.Header;
|
||||
const Footer = Template.Footer;
|
||||
const Cart = Template.pages.Cart;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<Cart />
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
29
app/builder/[site]/checkout/page.tsx
Normal file
29
app/builder/[site]/checkout/page.tsx
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
"use client";
|
||||
|
||||
import { templates } from "@/lib/template";
|
||||
import { getUserSites } from "@/lib/sites";
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
interface CheckoutPageProps {
|
||||
params: { site: string };
|
||||
}
|
||||
|
||||
export default function CheckoutPage({ params }: CheckoutPageProps) {
|
||||
if (typeof window === "undefined") return null; // SSR safe
|
||||
const userSites = getUserSites();
|
||||
const site = userSites.find((s) => s.slug === params.site);
|
||||
if (!site) return notFound();
|
||||
|
||||
const Template = templates[site.template];
|
||||
const Header = Template.Header;
|
||||
const Footer = Template.Footer;
|
||||
const Checkout = Template.pages.Checkout;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<Checkout />
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
35
app/builder/[site]/page.tsx
Normal file
35
app/builder/[site]/page.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// app/builder/[site]/page.tsx
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { templates } from "@/lib/template";
|
||||
import { getUserSites, Site } from "@/lib/sites";
|
||||
|
||||
interface BuilderPageProps {
|
||||
params: { site: string };
|
||||
}
|
||||
|
||||
export default function BuilderPage({ params }: BuilderPageProps) {
|
||||
const [site, setSite] = useState<Site | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const userSites = getUserSites();
|
||||
const found = userSites.find((s) => s.slug === params.site) || null;
|
||||
setSite(found);
|
||||
}, [params.site]);
|
||||
|
||||
if (!site) return <div className="p-8 text-center">Loading...</div>;
|
||||
|
||||
const Template = templates[site.template];
|
||||
const Header = Template.Header;
|
||||
const Footer = Template.Footer;
|
||||
const HomePage = Template.pages.Home;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<HomePage siteData={site.data} />
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
29
app/builder/[site]/product/[id]/page.tsx
Normal file
29
app/builder/[site]/product/[id]/page.tsx
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
"use client";
|
||||
|
||||
import { templates } from "@/lib/template";
|
||||
import { getUserSites } from "@/lib/sites";
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
interface ProductPageProps {
|
||||
params: { site: string; id: string };
|
||||
}
|
||||
|
||||
export default function ProductPage({ params }: ProductPageProps) {
|
||||
if (typeof window === "undefined") return null;
|
||||
const userSites = getUserSites();
|
||||
const site = userSites.find((s) => s.slug === params.site);
|
||||
if (!site) return notFound();
|
||||
|
||||
const Template = templates[site.template];
|
||||
const Header = Template.Header;
|
||||
const Footer = Template.Footer;
|
||||
const Product = Template.pages.Product;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<Product params={{ id: params.id }} />
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
28
app/builder/[site]/shop/page.tsx
Normal file
28
app/builder/[site]/shop/page.tsx
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
"use client";
|
||||
import { getUserSites } from "@/lib/sites";
|
||||
import { templates } from "@/lib/template";
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
interface ShopPageProps {
|
||||
params: { site: string };
|
||||
}
|
||||
|
||||
export default function ShopPage({ params }: ShopPageProps) {
|
||||
if (typeof window === "undefined") return null; // SSR safe
|
||||
const userSites = getUserSites();
|
||||
const site = userSites.find((s) => s.slug === params.site);
|
||||
if (!site) return notFound();
|
||||
|
||||
const Template = templates[site.template];
|
||||
const Header = Template.Header;
|
||||
const Footer = Template.Footer;
|
||||
const Shop = Template.pages.Shop;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<Shop />
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,39 +1,34 @@
|
|||
"use client";
|
||||
import { useAuthStore } from "@/store/authStore";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Navbar from "@/components/Navbar";
|
||||
import Link from "next/link";
|
||||
import { getUserSites } from "@/lib/sites";
|
||||
|
||||
export default function DashboardPage() {
|
||||
const { user, token, logout, loading } = useAuthStore();
|
||||
const router = useRouter();
|
||||
export default function Dashboard() {
|
||||
const userId = parseInt(localStorage.getItem("userId") || "0");
|
||||
if (typeof window === "undefined") return null; // SSR safe
|
||||
const userSites = getUserSites();
|
||||
|
||||
if (loading) return <p className="text-center mt-10">Loading...</p>;
|
||||
if (!user) {
|
||||
router.push("/login");
|
||||
return null;
|
||||
}
|
||||
const sites = userSites.filter((s) => s.userId === userId);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<div className="flex flex-col items-center justify-center min-h-screen">
|
||||
<h1 className="text-3xl font-bold">Welcome, {user.email}</h1>
|
||||
|
||||
{token ? (
|
||||
<div className="mt-4 p-4 bg-gray-100 rounded w-[600px] break-words">
|
||||
<p className="text-sm font-mono">{token}</p>
|
||||
<main className="container mx-auto p-8">
|
||||
<h1 className="text-3xl font-bold mb-6">Your Sites</h1>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{sites.map((site) => (
|
||||
<div
|
||||
key={site.id}
|
||||
className="border p-4 rounded shadow hover:shadow-lg"
|
||||
>
|
||||
<h2 className="text-xl font-semibold mb-2">{site.name}</h2>
|
||||
<p className="text-gray-600 mb-4">Template: {site.template}</p>
|
||||
<Link
|
||||
href={`/builder/${site.slug}`}
|
||||
className="text-blue-600 hover:underline"
|
||||
>
|
||||
Open Builder
|
||||
</Link>
|
||||
</div>
|
||||
) : (
|
||||
<p className="mt-4 text-gray-500">Fetching token...</p>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={logout}
|
||||
className="mt-4 bg-red-500 text-white p-2 rounded"
|
||||
>
|
||||
Logout
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1 @@
|
|||
@import "tailwindcss";
|
||||
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--font-sans: var(--font-geist-sans);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #0a0a0a;
|
||||
--foreground: #ededed;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--background);
|
||||
color: var(--foreground);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
|
|
|||
51
app/signup/page.tsx
Normal file
51
app/signup/page.tsx
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
"use client";
|
||||
import { useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { createUser } from "@/lib/users";
|
||||
|
||||
export default function Signup() {
|
||||
const router = useRouter();
|
||||
const [form, setForm] = useState({ name: "", email: "", businessName: "" });
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
const user = createUser(form);
|
||||
localStorage.setItem("userId", user.id.toString());
|
||||
router.push("/template-select");
|
||||
};
|
||||
|
||||
return (
|
||||
<main className="container mx-auto p-8">
|
||||
<h1 className="text-2xl font-bold mb-6">Sign Up</h1>
|
||||
<form onSubmit={handleSubmit} className="space-y-4 max-w-md">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Name"
|
||||
className="w-full border p-2 rounded"
|
||||
value={form.name}
|
||||
onChange={(e) => setForm({ ...form, name: e.target.value })}
|
||||
required
|
||||
/>
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
className="w-full border p-2 rounded"
|
||||
value={form.email}
|
||||
onChange={(e) => setForm({ ...form, email: e.target.value })}
|
||||
required
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Business Name"
|
||||
className="w-full border p-2 rounded"
|
||||
value={form.businessName}
|
||||
onChange={(e) => setForm({ ...form, businessName: e.target.value })}
|
||||
required
|
||||
/>
|
||||
<button className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700">
|
||||
Next
|
||||
</button>
|
||||
</form>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
49
app/template-select/page.tsx
Normal file
49
app/template-select/page.tsx
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
"use client";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { createSite, Site } from "@/lib/sites";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function TemplateSelect() {
|
||||
const router = useRouter();
|
||||
const userId = parseInt(localStorage.getItem("userId") || "0");
|
||||
const templates = ["template1"];
|
||||
const [selectedTemplate, setSelectedTemplate] = useState<string | null>(null);
|
||||
|
||||
const handleSelect = () => {
|
||||
if (!selectedTemplate) return;
|
||||
const site = createSite({
|
||||
userId,
|
||||
businessName: "My Business",
|
||||
template: selectedTemplate,
|
||||
});
|
||||
router.push(`/dashboard`);
|
||||
};
|
||||
|
||||
return (
|
||||
<main className="container mx-auto p-8">
|
||||
<h1 className="text-2xl font-bold mb-6">Select a Template</h1>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{templates.map((t) => (
|
||||
<div
|
||||
key={t}
|
||||
onClick={() => setSelectedTemplate(t)}
|
||||
className={`border p-4 rounded shadow cursor-pointer hover:shadow-lg transition ${
|
||||
selectedTemplate === t ? "border-blue-600" : ""
|
||||
}`}
|
||||
>
|
||||
<h2 className="font-semibold mb-2">{t}</h2>
|
||||
<div className="h-40 bg-gray-100 flex items-center justify-center">
|
||||
Preview
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<button
|
||||
onClick={handleSelect}
|
||||
className="mt-6 bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"
|
||||
>
|
||||
Create Site
|
||||
</button>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
9
components/templates/template1/layout/footer.tsx
Normal file
9
components/templates/template1/layout/footer.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
export default function Footer() {
|
||||
return (
|
||||
<footer className="bg-blue-600 text-white p-4 mt-10">
|
||||
<div className="container mx-auto text-center">
|
||||
© 2025 Template 1
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
27
components/templates/template1/layout/header.tsx
Normal file
27
components/templates/template1/layout/header.tsx
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import Link from "next/link";
|
||||
|
||||
const Header: React.FC = () => {
|
||||
return (
|
||||
<header className="bg-blue-600 text-white p-4 shadow-md">
|
||||
<div className="container mx-auto flex justify-between items-center">
|
||||
<h1 className="text-xl font-bold">Template 1</h1>
|
||||
<nav className="space-x-4">
|
||||
<Link href="#" className="hover:underline">
|
||||
Home
|
||||
</Link>
|
||||
<Link href="#" className="hover:underline">
|
||||
Shop
|
||||
</Link>
|
||||
<Link href="#" className="hover:underline">
|
||||
Cart
|
||||
</Link>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
8
components/templates/template1/pages/cart.tsx
Normal file
8
components/templates/template1/pages/cart.tsx
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
export default function Cart() {
|
||||
return (
|
||||
<main className="container mx-auto p-8">
|
||||
<h2 className="text-2xl font-bold mb-4">Your Cart</h2>
|
||||
<p className="text-gray-700">No items in cart yet.</p>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
10
components/templates/template1/pages/checkout.tsx
Normal file
10
components/templates/template1/pages/checkout.tsx
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
export default function Checkout() {
|
||||
return (
|
||||
<main className="container mx-auto p-8">
|
||||
<h2 className="text-2xl font-bold mb-4">Checkout</h2>
|
||||
<p className="text-gray-700">
|
||||
Checkout functionality will be implemented here.
|
||||
</p>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
14
components/templates/template1/pages/home.tsx
Normal file
14
components/templates/template1/pages/home.tsx
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
interface HomeProps {
|
||||
siteData: { heroTitle: string; heroSubtitle: string };
|
||||
}
|
||||
|
||||
export default function Home({ siteData }: HomeProps) {
|
||||
return (
|
||||
<main className="container mx-auto p-8">
|
||||
<section className="text-center bg-gray-100 p-8 rounded-md shadow-md">
|
||||
<h2 className="text-3xl font-bold mb-2">{siteData.heroTitle}</h2>
|
||||
<p className="text-gray-700">{siteData.heroSubtitle}</p>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
6
components/templates/template1/pages/index.ts
Normal file
6
components/templates/template1/pages/index.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// components/templates/template1/pages/index.ts
|
||||
export { default as Home } from "./home";
|
||||
export { default as Shop } from "./shop";
|
||||
export { default as Product } from "./product";
|
||||
export { default as Cart } from "./cart";
|
||||
export { default as Checkout } from "./checkout";
|
||||
32
components/templates/template1/pages/product.tsx
Normal file
32
components/templates/template1/pages/product.tsx
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
interface Product {
|
||||
id: number;
|
||||
name: string;
|
||||
price: number;
|
||||
}
|
||||
|
||||
const products: Product[] = [
|
||||
{ id: 1, name: "Product 1", price: 50 },
|
||||
{ id: 2, name: "Product 2", price: 30 },
|
||||
{ id: 3, name: "Product 3", price: 20 },
|
||||
];
|
||||
|
||||
interface ProductProps {
|
||||
params: { id: string };
|
||||
}
|
||||
|
||||
export default function Product({ params }: ProductProps) {
|
||||
const product = products.find((p) => p.id === parseInt(params.id));
|
||||
if (!product) return <div className="p-8">Product not found</div>;
|
||||
|
||||
return (
|
||||
<main className="container mx-auto p-8">
|
||||
<div className="border rounded p-6 shadow">
|
||||
<h2 className="text-2xl font-bold mb-2">{product.name}</h2>
|
||||
<p className="text-gray-700 mb-4">Price: ${product.price}</p>
|
||||
<button className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700">
|
||||
Add to Cart
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
38
components/templates/template1/pages/shop.tsx
Normal file
38
components/templates/template1/pages/shop.tsx
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import Link from "next/link";
|
||||
|
||||
interface Product {
|
||||
id: number;
|
||||
name: string;
|
||||
price: number;
|
||||
}
|
||||
|
||||
const products: Product[] = [
|
||||
{ id: 1, name: "Product 1", price: 50 },
|
||||
{ id: 2, name: "Product 2", price: 30 },
|
||||
{ id: 3, name: "Product 3", price: 20 },
|
||||
];
|
||||
|
||||
export default function Shop() {
|
||||
return (
|
||||
<main className="container mx-auto p-8">
|
||||
<h2 className="text-2xl font-bold mb-4">Shop</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{products.map((p) => (
|
||||
<div
|
||||
key={p.id}
|
||||
className="border rounded p-4 shadow hover:shadow-lg transition"
|
||||
>
|
||||
<h3 className="font-semibold">{p.name}</h3>
|
||||
<p className="text-gray-700">${p.price}</p>
|
||||
<Link
|
||||
href={`/builder/site-1/product/${p.id}`}
|
||||
className="text-blue-600 hover:underline mt-2 inline-block"
|
||||
>
|
||||
View Product
|
||||
</Link>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
53
lib/sites.ts
Normal file
53
lib/sites.ts
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
export interface SiteData {
|
||||
heroTitle: string;
|
||||
heroSubtitle: string;
|
||||
}
|
||||
|
||||
export interface Site {
|
||||
id: number;
|
||||
userId: number;
|
||||
slug: string;
|
||||
name: string;
|
||||
template: string;
|
||||
data: SiteData;
|
||||
}
|
||||
|
||||
// Get sites from localStorage
|
||||
export function getUserSites(): Site[] {
|
||||
if (typeof window === "undefined") return []; // SSR safe
|
||||
const data = localStorage.getItem("userSites");
|
||||
return data ? JSON.parse(data) : [];
|
||||
}
|
||||
|
||||
// Save sites to localStorage
|
||||
export function saveUserSites(sites: Site[]) {
|
||||
localStorage.setItem("userSites", JSON.stringify(sites));
|
||||
}
|
||||
|
||||
export function createSite({
|
||||
userId,
|
||||
businessName,
|
||||
template,
|
||||
}: {
|
||||
userId: number;
|
||||
businessName: string;
|
||||
template: string;
|
||||
}): Site {
|
||||
const sites = getUserSites();
|
||||
const id = sites.length + 1;
|
||||
const slug = `site-${id}`;
|
||||
const newSite: Site = {
|
||||
id,
|
||||
userId,
|
||||
slug,
|
||||
name: businessName,
|
||||
template,
|
||||
data: {
|
||||
heroTitle: `Welcome to ${businessName}`,
|
||||
heroSubtitle: "Best products ever",
|
||||
},
|
||||
};
|
||||
sites.push(newSite);
|
||||
saveUserSites(sites);
|
||||
return newSite;
|
||||
}
|
||||
17
lib/template.ts
Normal file
17
lib/template.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import T1Header from "@/components/templates/template1/layout/header";
|
||||
import T1Footer from "@/components/templates/template1/layout/footer";
|
||||
import * as T1Pages from "@/components//templates/template1/pages";
|
||||
|
||||
export interface Template {
|
||||
Header: React.ComponentType;
|
||||
Footer: React.ComponentType;
|
||||
pages: typeof T1Pages;
|
||||
}
|
||||
|
||||
export const templates: Record<string, Template> = {
|
||||
template1: {
|
||||
Header: T1Header,
|
||||
Footer: T1Footer,
|
||||
pages: T1Pages,
|
||||
},
|
||||
};
|
||||
15
lib/users.ts
Normal file
15
lib/users.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
export interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
businessName: string;
|
||||
}
|
||||
|
||||
export const users: User[] = [];
|
||||
|
||||
export function createUser(data: Omit<User, "id">): User {
|
||||
const id = users.length + 1;
|
||||
const newUser: User = { id, ...data };
|
||||
users.push(newUser);
|
||||
return newUser;
|
||||
}
|
||||
|
|
@ -22,6 +22,12 @@
|
|||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts",
|
||||
"components/**/*"
|
||||
],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue