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";
|
"use client";
|
||||||
import { useAuthStore } from "@/store/authStore";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { getUserSites } from "@/lib/sites";
|
||||||
import Navbar from "@/components/Navbar";
|
|
||||||
|
|
||||||
export default function DashboardPage() {
|
export default function Dashboard() {
|
||||||
const { user, token, logout, loading } = useAuthStore();
|
const userId = parseInt(localStorage.getItem("userId") || "0");
|
||||||
const router = useRouter();
|
if (typeof window === "undefined") return null; // SSR safe
|
||||||
|
const userSites = getUserSites();
|
||||||
|
|
||||||
if (loading) return <p className="text-center mt-10">Loading...</p>;
|
const sites = userSites.filter((s) => s.userId === userId);
|
||||||
if (!user) {
|
|
||||||
router.push("/login");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<main className="container mx-auto p-8">
|
||||||
<Navbar />
|
<h1 className="text-3xl font-bold mb-6">Your Sites</h1>
|
||||||
<div className="flex flex-col items-center justify-center min-h-screen">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<h1 className="text-3xl font-bold">Welcome, {user.email}</h1>
|
{sites.map((site) => (
|
||||||
|
<div
|
||||||
{token ? (
|
key={site.id}
|
||||||
<div className="mt-4 p-4 bg-gray-100 rounded w-[600px] break-words">
|
className="border p-4 rounded shadow hover:shadow-lg"
|
||||||
<p className="text-sm font-mono">{token}</p>
|
|
||||||
</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
|
<h2 className="text-xl font-semibold mb-2">{site.name}</h2>
|
||||||
</button>
|
<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>
|
</div>
|
||||||
</>
|
))}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1 @@
|
||||||
@import "tailwindcss";
|
@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"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue