import React, { useEffect, useState } from "react"; import { Search, Check, ArrowRight, Menu, Moon, Sun, Tag, TrendingUp, Plus, Trash, Edit, Download, Zap, } from "lucide-react"; import { motion } from "framer-motion"; /** * Full-featured Domain Marketplace (single-file React preview) * - Frontend wiring for Stripe & Razorpay (requires backend endpoints) * - Firebase & Supabase lead + analytics saving (optional, configure one) * - Admin dashboard to manage domains (add/edit/delete) * - Auto-sync from Google Sheets (calls backend /api/sync-google-sheets) * * IMPORTANT: * - This file shows front-end wiring only. You MUST implement the server endpoints * described below and provide API keys via environment variables on the server. * - In pure-browser environments `process` may be undefined; this file guards for that. */ // ---------------------- SAMPLE DATA (replace with your 50+ list) ---------------------- const initialDomains = [ { id: 1, name: "example1.com", price: 500, category: "Premium", tag: "Hot", views: 12 }, { id: 2, name: "example2.in", price: 250, category: "Brandable", tag: "Trending", views: 34 }, { id: 3, name: "example3.net", price: 750, category: "Tech", tag: "New", views: 7 }, ]; // ---------------------- CONFIG PLACEHOLDERS (fill these) // Use safe checks so code doesn't crash in environments without `process`. const _env = typeof process !== "undefined" && process && process.env ? process.env : {}; const STRIPE_PUBLISHABLE_KEY = _env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY || "pk_test_YOUR_KEY"; const RAZORPAY_KEY = _env.NEXT_PUBLIC_RAZORPAY_KEY || "rzp_test_YOUR_KEY"; // Firebase config example (if using Firebase) const FIREBASE_CONFIG = { apiKey: _env.NEXT_PUBLIC_FIREBASE_APIKEY || "YOUR_FIREBASE_APIKEY", authDomain: _env.NEXT_PUBLIC_FIREBASE_AUTHDOMAIN || "YOUR_FIREBASE_AUTHDOMAIN", projectId: _env.NEXT_PUBLIC_FIREBASE_PROJECTID || "YOUR_FIREBASE_PROJECTID", }; // Supabase config example (if using Supabase) const SUPABASE_URL = _env.NEXT_PUBLIC_SUPABASE_URL || "https://your.supabase.co"; const SUPABASE_ANON_KEY = _env.NEXT_PUBLIC_SUPABASE_ANON_KEY || "your-anon-key"; export default function DomainMarketplace() { const [domains, setDomains] = useState(initialDomains); const [search, setSearch] = useState(""); const [filter, setFilter] = useState("all"); const [theme, setTheme] = useState("light"); const [menuOpen, setMenuOpen] = useState(false); const [selectedDomain, setSelectedDomain] = useState(null); const [adminMode, setAdminMode] = useState(false); const [editing, setEditing] = useState(null); const [leadSavingBackend, setLeadSavingBackend] = useState("firebase"); // or 'supabase' or 'none' // basic analytics state const [analytics, setAnalytics] = useState({ viewsTotal: domains.reduce((s, d) => s + (d.views || 0), 0) }); useEffect(() => { setAnalytics((a) => ({ ...a, viewsTotal: domains.reduce((s, d) => s + (d.views || 0), 0) })); }, [domains]); const categories = [...new Set(domains.map((d) => d.category))]; // ---------------------- SEARCH / FILTER ---------------------- const filtered = domains.filter((d) => { const matchSearch = d.name.toLowerCase().includes(search.toLowerCase()); const matchFilter = filter === "all" || d.category === filter; return matchSearch && matchFilter; }); // ---------------------- THEME ---------------------- const toggleTheme = () => setTheme((t) => (t === "light" ? "dark" : "light")); // ---------------------- ADMIN: CRUD ---------------------- function addDomain(newDomain) { const id = Math.max(0, ...domains.map((d) => d.id)) + 1; setDomains([{ id, ...newDomain }, ...domains]); } function updateDomain(id, patch) { setDomains(domains.map((d) => (d.id === id ? { ...d, ...patch } : d))); } function removeDomain(id) { setDomains(domains.filter((d) => d.id !== id)); } // ---------------------- ANALYTICS / VIEWS ---------------------- // Track view locally and optionally to backend (Firebase / Supabase) async function trackView(domainId) { setDomains((prev) => prev.map((d) => (d.id === domainId ? { ...d, views: (d.views || 0) + 1 } : d))); // Example: call backend to record analytics try { await fetch("/api/track-view", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ domainId, timestamp: new Date().toISOString() }), }); } catch (e) { // silently fail — backend optional console.warn("trackView failed", e && e.message ? e.message : e); } } // ---------------------- SAVE LEAD (Firebase / Supabase / Backend) ---------------------- async function saveLead(lead) { // 1) Call your backend to save (recommended) which in turn will save to Firebase/Supabase try { const res = await fetch("/api/save-lead", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(lead), }); if (!res.ok) throw new Error("lead save failed"); return true; } catch (e) { console.warn("saveLead failed — falling back to client", e && e.message ? e.message : e); } // 2) Optional direct client-side save: (DEMO only — prefer backend) if (leadSavingBackend === "firebase") { // TODO: initialize firebase and save to Firestore console.warn("Direct Firebase save not implemented in preview — implement on backend or initialize firebase here."); return false; } if (leadSavingBackend === "supabase") { // TODO: initialize supabase client and save console.warn("Direct Supabase save not implemented in preview — implement on backend or initialize supabase here."); return false; } return false; } // ---------------------- PAYMENTS (Stripe + Razorpay) ---------------------- // NOTE: For security, create orders / checkout sessions on your server and return a URL or order info. async function startStripeCheckout(domain) { // frontend calls backend to create Checkout Session try { const res = await fetch("/api/create-stripe-session", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ domainId: domain.id }), }); const data = await res.json(); if (data.url) { window.location.href = data.url; // redirect to Stripe Checkout } else { alert("Stripe checkout failed: missing session URL"); } } catch (e) { console.error("Stripe checkout error", e); alert("Stripe checkout error — check console"); } } async function startRazorpay(domain) { // call backend to create Razorpay order and return order info try { const res = await fetch("/api/create-razorpay-order", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ domainId: domain.id }), }); const data = await res.json(); if (!data.order) throw new Error("no order returned"); // Open Razorpay checkout (this requires Razorpay script loaded on page) const options = { key: RAZORPAY_KEY, amount: data.order.amount, // in paise currency: data.order.currency, name: domain.name, order_id: data.order.id, handler: async function (response) { // response.razorpay_payment_id, response.razorpay_order_id, response.razorpay_signature // Verify payment server-side await fetch("/api/verify-razorpay", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...response, domainId: domain.id }), }); alert("Payment successful — thank you!"); }, modal: { ondismiss: function () { console.log("checkout closed"); }, }, }; // eslint-disable-next-line no-undef if (typeof window !== "undefined" && typeof window.Razorpay === "function") { const rzp = new window.Razorpay(options); rzp.open(); } else { alert("Razorpay is not available — make sure the Razorpay script is loaded on the page."); } } catch (e) { console.error("Razorpay error", e); alert("Razorpay checkout error — check console"); } } // ---------------------- GOOGLE SHEETS SYNC ---------------------- // This triggers a backend endpoint which will read a Google Sheet and return domain rows. async function syncFromGoogleSheets() { try { const res = await fetch("/api/sync-google-sheets", { method: "POST" }); if (!res.ok) throw new Error("sync failed"); const data = await res.json(); // expect data.domains = [{name, price, category, tag}] if (Array.isArray(data.domains)) { // map and assign ids const mapped = data.domains.map((d, i) => ({ id: Date.now() + i, ...d })); setDomains(mapped); alert("Sheet sync completed — domain list updated."); } else { alert("Sheet sync returned unexpected payload"); } } catch (e) { console.error("sheet sync error", e); alert("Google Sheets sync failed — check backend logs."); } } // ---------------------- EXPORT / DOWNLOAD CSV ---------------------- function downloadCSV() { const headers = ["id", "name", "price", "category", "tag", "views"]; const rows = domains.map((d) => [d.id, d.name, d.price, d.category, d.tag, d.views || 0]); const csv = [headers, ...rows] .map((r) => r.join(",")) .join("\n"); const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = "domains.csv"; a.click(); URL.revokeObjectURL(url); } // ---------------------- UI ---------------------- return (