Compare commits
No commits in common. "main" and "shakil" have entirely different histories.
24 changed files with 270 additions and 511 deletions
62
app/(auth)/forgot-password/page.tsx
Normal file
62
app/(auth)/forgot-password/page.tsx
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
"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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
80
app/(auth)/login/page.tsx
Normal file
80
app/(auth)/login/page.tsx
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
"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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
72
app/(auth)/signup/page.tsx
Normal file
72
app/(auth)/signup/page.tsx
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
"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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
"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 />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
"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 />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
// 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 />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
"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 />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
"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,34 +1,39 @@
|
||||||
"use client";
|
"use client";
|
||||||
import Link from "next/link";
|
import { useAuthStore } from "@/store/authStore";
|
||||||
import { getUserSites } from "@/lib/sites";
|
import { useRouter } from "next/navigation";
|
||||||
|
import Navbar from "@/components/Navbar";
|
||||||
|
|
||||||
export default function Dashboard() {
|
export default function DashboardPage() {
|
||||||
const userId = parseInt(localStorage.getItem("userId") || "0");
|
const { user, token, logout, loading } = useAuthStore();
|
||||||
if (typeof window === "undefined") return null; // SSR safe
|
const router = useRouter();
|
||||||
const userSites = getUserSites();
|
|
||||||
|
|
||||||
const sites = userSites.filter((s) => s.userId === userId);
|
if (loading) return <p className="text-center mt-10">Loading...</p>;
|
||||||
|
if (!user) {
|
||||||
|
router.push("/login");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="container mx-auto p-8">
|
<>
|
||||||
<h1 className="text-3xl font-bold mb-6">Your Sites</h1>
|
<Navbar />
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div className="flex flex-col items-center justify-center min-h-screen">
|
||||||
{sites.map((site) => (
|
<h1 className="text-3xl font-bold">Welcome, {user.email}</h1>
|
||||||
<div
|
|
||||||
key={site.id}
|
{token ? (
|
||||||
className="border p-4 rounded shadow hover:shadow-lg"
|
<div className="mt-4 p-4 bg-gray-100 rounded w-[600px] break-words">
|
||||||
>
|
<p className="text-sm font-mono">{token}</p>
|
||||||
<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>
|
</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>
|
</div>
|
||||||
</main>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,26 @@
|
||||||
@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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
"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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
"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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
"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;
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
// 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";
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
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
53
lib/sites.ts
|
|
@ -1,53 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
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
15
lib/users.ts
|
|
@ -1,15 +0,0 @@
|
||||||
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,12 +22,6 @@
|
||||||
"@/*": ["./*"]
|
"@/*": ["./*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||||
"next-env.d.ts",
|
|
||||||
"**/*.ts",
|
|
||||||
"**/*.tsx",
|
|
||||||
".next/types/**/*.ts",
|
|
||||||
"components/**/*"
|
|
||||||
],
|
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue