- favicon.svg: pending logo tweaks - base.html: pending template changes - CHANGELOG.md: add waitlist mode and logo redesign entries missed in prior commits - scratch/: add design prototype HTML/JSX files, remove old markdown notes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
856 lines
31 KiB
JavaScript
856 lines
31 KiB
JavaScript
import { useState } from "react";
|
||
|
||
const COLORS = {
|
||
bg: "#0A0F1C",
|
||
surface: "#111827",
|
||
surfaceLight: "#1A2236",
|
||
surfaceHover: "#1F2A40",
|
||
border: "#2A3550",
|
||
borderLight: "#354263",
|
||
accent: "#22D3A7",
|
||
accentDim: "rgba(34,211,167,0.12)",
|
||
accentGlow: "rgba(34,211,167,0.25)",
|
||
warning: "#F59E0B",
|
||
warningDim: "rgba(245,158,11,0.12)",
|
||
hot: "#EF4444",
|
||
hotDim: "rgba(239,68,68,0.12)",
|
||
blue: "#3B82F6",
|
||
blueDim: "rgba(59,130,246,0.12)",
|
||
purple: "#A78BFA",
|
||
purpleDim: "rgba(167,139,250,0.12)",
|
||
text: "#F1F5F9",
|
||
textMuted: "#94A3B8",
|
||
textDim: "#64748B",
|
||
};
|
||
|
||
const tabs = [
|
||
{ id: "dashboard", label: "Dashboard", icon: "◐" },
|
||
{ id: "leads", label: "Lead Feed", icon: "◉" },
|
||
{ id: "listing", label: "My Listing", icon: "◧" },
|
||
{ id: "boost", label: "Boost & Upsells", icon: "△" },
|
||
];
|
||
|
||
const mockLeads = [
|
||
{
|
||
id: 1,
|
||
type: "Indoor Padel Center",
|
||
courts: 6,
|
||
region: "Lower Saxony, Germany",
|
||
budget: "€400K – €600K",
|
||
timeline: "Q3 2026",
|
||
specs: ["Indoor steel hall", "LED lighting", "Panoramic glass walls", "Pro-grade turf"],
|
||
posted: "2 hours ago",
|
||
heat: "hot",
|
||
credits: 25,
|
||
unlocked: false,
|
||
bidders: 1,
|
||
},
|
||
{
|
||
id: 2,
|
||
type: "Outdoor Padel Courts",
|
||
courts: 4,
|
||
region: "Bavaria, Germany",
|
||
budget: "€180K – €280K",
|
||
timeline: "Q2 2026",
|
||
specs: ["Outdoor installation", "Drainage system", "Windbreak fencing", "Night lighting"],
|
||
posted: "5 hours ago",
|
||
heat: "warm",
|
||
credits: 15,
|
||
unlocked: false,
|
||
bidders: 3,
|
||
},
|
||
{
|
||
id: 3,
|
||
type: "Indoor Padel & Fitness Complex",
|
||
courts: 10,
|
||
region: "North Rhine-Westphalia, Germany",
|
||
budget: "€800K – €1.2M",
|
||
timeline: "Q1 2027",
|
||
specs: ["Mixed-use facility", "Padel + gym + café", "Climate control", "Spectator area"],
|
||
posted: "1 day ago",
|
||
heat: "hot",
|
||
credits: 40,
|
||
unlocked: false,
|
||
bidders: 0,
|
||
},
|
||
{
|
||
id: 4,
|
||
type: "Padel Court Addition",
|
||
courts: 2,
|
||
region: "Hesse, Germany",
|
||
budget: "€80K – €140K",
|
||
timeline: "Q4 2026",
|
||
specs: ["Tennis club conversion", "2 courts", "Basic lighting", "Standard glass"],
|
||
posted: "2 days ago",
|
||
heat: "cool",
|
||
credits: 8,
|
||
unlocked: true,
|
||
bidders: 5,
|
||
contact: { name: "Thomas M.", org: "TC Wiesbaden e.V.", email: "t.m***@tc-wiesbaden.de", phone: "+49 611 ***" },
|
||
},
|
||
{
|
||
id: 5,
|
||
type: "Commercial Padel Venue",
|
||
courts: 8,
|
||
region: "Hamburg, Germany",
|
||
budget: "€500K – €750K",
|
||
timeline: "Q2 2026",
|
||
specs: ["Standalone commercial", "Bar/lounge area", "Premium courts", "Booking system integration"],
|
||
posted: "3 days ago",
|
||
heat: "warm",
|
||
credits: 30,
|
||
unlocked: false,
|
||
bidders: 2,
|
||
},
|
||
];
|
||
|
||
const upsells = [
|
||
{
|
||
id: "logo",
|
||
label: "Show Company Logo",
|
||
desc: "Display your logo next to your listing name",
|
||
price: 29,
|
||
period: "/mo",
|
||
boost: "+40% more clicks",
|
||
preselected: true,
|
||
active: true,
|
||
},
|
||
{
|
||
id: "highlight",
|
||
label: "Highlight Listing",
|
||
desc: "Yellow background to stand out in search results",
|
||
price: 39,
|
||
period: "/mo",
|
||
boost: "+65% more views",
|
||
preselected: true,
|
||
active: true,
|
||
},
|
||
{
|
||
id: "brandcolor",
|
||
label: "Custom Brand Color",
|
||
desc: "Use your brand color instead of yellow",
|
||
price: 59,
|
||
period: "/mo",
|
||
boost: "+80% brand recognition",
|
||
preselected: false,
|
||
active: false,
|
||
},
|
||
{
|
||
id: "verified",
|
||
label: "Verified Badge ✓",
|
||
desc: "Trusted supplier badge on your profile",
|
||
price: 49,
|
||
period: "/mo",
|
||
boost: "+55% more inquiries",
|
||
preselected: true,
|
||
active: true,
|
||
},
|
||
{
|
||
id: "sticky_week",
|
||
label: "Sticky Top 1 Week",
|
||
desc: "Pinned to the top of your category for 7 days",
|
||
price: 79,
|
||
period: "one-time",
|
||
boost: "2× more views",
|
||
preselected: false,
|
||
active: false,
|
||
},
|
||
{
|
||
id: "sticky_month",
|
||
label: "Sticky Top 1 Month",
|
||
desc: "Pinned to the top of your category for 30 days",
|
||
price: 199,
|
||
period: "one-time",
|
||
boost: "6× more views",
|
||
preselected: false,
|
||
active: false,
|
||
},
|
||
{
|
||
id: "newsletter",
|
||
label: "Featured in Newsletter",
|
||
desc: "Included in weekly email to 2,400+ project planners",
|
||
price: 99,
|
||
period: "/mo",
|
||
boost: "+120 targeted impressions/week",
|
||
preselected: false,
|
||
active: false,
|
||
},
|
||
{
|
||
id: "leadaccess",
|
||
label: "Lead Feed Access",
|
||
desc: "Browse & unlock leads in your categories",
|
||
price: 199,
|
||
period: "/mo",
|
||
boost: "Direct customer contact",
|
||
preselected: true,
|
||
active: true,
|
||
},
|
||
];
|
||
|
||
function HeatBadge({ heat }) {
|
||
const conf = {
|
||
hot: { bg: COLORS.hotDim, color: COLORS.hot, label: "🔥 Hot", border: "rgba(239,68,68,0.3)" },
|
||
warm: { bg: COLORS.warningDim, color: COLORS.warning, label: "◐ Warm", border: "rgba(245,158,11,0.3)" },
|
||
cool: { bg: COLORS.blueDim, color: COLORS.blue, label: "○ Cool", border: "rgba(59,130,246,0.3)" },
|
||
}[heat];
|
||
return (
|
||
<span style={{
|
||
background: conf.bg,
|
||
color: conf.color,
|
||
border: `1px solid ${conf.border}`,
|
||
padding: "3px 10px",
|
||
borderRadius: 20,
|
||
fontSize: 12,
|
||
fontWeight: 600,
|
||
letterSpacing: 0.3,
|
||
}}>{conf.label}</span>
|
||
);
|
||
}
|
||
|
||
function StatCard({ label, value, sub, color }) {
|
||
return (
|
||
<div style={{
|
||
background: COLORS.surface,
|
||
border: `1px solid ${COLORS.border}`,
|
||
borderRadius: 12,
|
||
padding: "20px 22px",
|
||
flex: 1,
|
||
minWidth: 160,
|
||
}}>
|
||
<div style={{ fontSize: 12, color: COLORS.textDim, textTransform: "uppercase", letterSpacing: 1.2, marginBottom: 8, fontWeight: 600 }}>{label}</div>
|
||
<div style={{ fontSize: 28, fontWeight: 700, color: color || COLORS.text, lineHeight: 1.1 }}>{value}</div>
|
||
{sub && <div style={{ fontSize: 12, color: COLORS.textMuted, marginTop: 6 }}>{sub}</div>}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function DashboardView() {
|
||
return (
|
||
<div>
|
||
<div style={{ marginBottom: 28 }}>
|
||
<h2 style={{ fontSize: 22, fontWeight: 700, color: COLORS.text, margin: 0 }}>Welcome back, PadelBau GmbH</h2>
|
||
<p style={{ color: COLORS.textMuted, margin: "6px 0 0", fontSize: 14 }}>Your supplier dashboard — February 2026</p>
|
||
</div>
|
||
<div style={{ display: "flex", gap: 14, flexWrap: "wrap", marginBottom: 28 }}>
|
||
<StatCard label="Profile Views" value="1,247" sub="↑ 23% vs last month" color={COLORS.accent} />
|
||
<StatCard label="Leads Unlocked" value="14" sub="This month" color={COLORS.blue} />
|
||
<StatCard label="Credits Balance" value="85" sub="~3 premium leads" color={COLORS.warning} />
|
||
<StatCard label="Directory Rank" value="#3" sub="Padel Courts category" color={COLORS.purple} />
|
||
</div>
|
||
|
||
<div style={{
|
||
background: `linear-gradient(135deg, ${COLORS.accentDim}, ${COLORS.purpleDim})`,
|
||
border: `1px solid ${COLORS.border}`,
|
||
borderRadius: 12,
|
||
padding: "22px 26px",
|
||
marginBottom: 28,
|
||
}}>
|
||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: 12 }}>
|
||
<div>
|
||
<div style={{ fontSize: 15, fontWeight: 600, color: COLORS.text, marginBottom: 4 }}>🔥 3 new leads match your profile</div>
|
||
<div style={{ fontSize: 13, color: COLORS.textMuted }}>Indoor facilities in Lower Saxony, NRW, and Hamburg — total project value €1.7M+</div>
|
||
</div>
|
||
<button style={{
|
||
background: COLORS.accent,
|
||
color: COLORS.bg,
|
||
border: "none",
|
||
borderRadius: 8,
|
||
padding: "10px 20px",
|
||
fontWeight: 700,
|
||
fontSize: 13,
|
||
cursor: "pointer",
|
||
whiteSpace: "nowrap",
|
||
}}>View Lead Feed →</button>
|
||
</div>
|
||
</div>
|
||
|
||
<h3 style={{ fontSize: 15, fontWeight: 600, color: COLORS.text, marginBottom: 14 }}>Recent Activity</h3>
|
||
{[
|
||
{ time: "2h ago", text: "New lead posted: 6-court indoor center in Lower Saxony (€400K–€600K)", dot: COLORS.hot },
|
||
{ time: "1d ago", text: "Your listing was viewed 47 times today — 12 from the Padel Courts category", dot: COLORS.accent },
|
||
{ time: "2d ago", text: "Lead unlocked: TC Wiesbaden — 2-court addition project. Contact details available.", dot: COLORS.blue },
|
||
{ time: "3d ago", text: "Your 'Sticky Top' placement in Padel Courts expired. Renew to stay on top.", dot: COLORS.warning },
|
||
{ time: "5d ago", text: "Newsletter sent to 2,400 subscribers — your company was featured", dot: COLORS.purple },
|
||
].map((item, i) => (
|
||
<div key={i} style={{
|
||
display: "flex",
|
||
gap: 14,
|
||
padding: "12px 0",
|
||
borderBottom: i < 4 ? `1px solid ${COLORS.border}` : "none",
|
||
alignItems: "flex-start",
|
||
}}>
|
||
<div style={{ width: 8, height: 8, borderRadius: 4, background: item.dot, marginTop: 6, flexShrink: 0 }} />
|
||
<div style={{ flex: 1 }}>
|
||
<div style={{ fontSize: 13, color: COLORS.text, lineHeight: 1.5 }}>{item.text}</div>
|
||
</div>
|
||
<div style={{ fontSize: 12, color: COLORS.textDim, whiteSpace: "nowrap", flexShrink: 0 }}>{item.time}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function LeadFeedView() {
|
||
const [leads, setLeads] = useState(mockLeads);
|
||
const [filter, setFilter] = useState("all");
|
||
|
||
const unlock = (id) => {
|
||
setLeads(leads.map(l => l.id === id ? {
|
||
...l,
|
||
unlocked: true,
|
||
contact: { name: "Max S.", org: "Padel Invest Nord GmbH", email: "m.s***@padel-invest.de", phone: "+49 40 ***" }
|
||
} : l));
|
||
};
|
||
|
||
const filtered = filter === "all" ? leads : leads.filter(l => l.heat === filter);
|
||
|
||
return (
|
||
<div>
|
||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 24, flexWrap: "wrap", gap: 12 }}>
|
||
<div>
|
||
<h2 style={{ fontSize: 22, fontWeight: 700, color: COLORS.text, margin: 0 }}>Lead Feed</h2>
|
||
<p style={{ color: COLORS.textMuted, margin: "6px 0 0", fontSize: 14 }}>Browse anonymized project briefs · Spend credits to unlock contact details</p>
|
||
</div>
|
||
<div style={{
|
||
background: COLORS.surface,
|
||
border: `1px solid ${COLORS.border}`,
|
||
borderRadius: 8,
|
||
padding: "8px 14px",
|
||
display: "flex",
|
||
alignItems: "center",
|
||
gap: 8,
|
||
}}>
|
||
<span style={{ fontSize: 12, color: COLORS.textDim }}>Balance:</span>
|
||
<span style={{ fontSize: 16, fontWeight: 700, color: COLORS.warning }}>85 credits</span>
|
||
<button style={{
|
||
background: COLORS.warningDim,
|
||
color: COLORS.warning,
|
||
border: `1px solid rgba(245,158,11,0.3)`,
|
||
borderRadius: 6,
|
||
padding: "4px 10px",
|
||
fontSize: 11,
|
||
fontWeight: 600,
|
||
cursor: "pointer",
|
||
marginLeft: 4,
|
||
}}>Buy More</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div style={{ display: "flex", gap: 8, marginBottom: 20 }}>
|
||
{[
|
||
{ id: "all", label: "All Leads" },
|
||
{ id: "hot", label: "🔥 Hot" },
|
||
{ id: "warm", label: "◐ Warm" },
|
||
{ id: "cool", label: "○ Cool" },
|
||
].map(f => (
|
||
<button key={f.id} onClick={() => setFilter(f.id)} style={{
|
||
background: filter === f.id ? COLORS.accentDim : "transparent",
|
||
color: filter === f.id ? COLORS.accent : COLORS.textMuted,
|
||
border: `1px solid ${filter === f.id ? "rgba(34,211,167,0.3)" : COLORS.border}`,
|
||
borderRadius: 20,
|
||
padding: "6px 16px",
|
||
fontSize: 12,
|
||
fontWeight: 600,
|
||
cursor: "pointer",
|
||
}}>{f.label}</button>
|
||
))}
|
||
</div>
|
||
|
||
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
|
||
{filtered.map(lead => (
|
||
<div key={lead.id} style={{
|
||
background: lead.unlocked ? COLORS.surface : COLORS.surfaceLight,
|
||
border: `1px solid ${lead.heat === "hot" ? "rgba(239,68,68,0.2)" : COLORS.border}`,
|
||
borderRadius: 12,
|
||
padding: "20px 22px",
|
||
position: "relative",
|
||
overflow: "hidden",
|
||
}}>
|
||
{lead.heat === "hot" && !lead.unlocked && (
|
||
<div style={{
|
||
position: "absolute",
|
||
top: 0,
|
||
left: 0,
|
||
right: 0,
|
||
height: 2,
|
||
background: `linear-gradient(90deg, ${COLORS.hot}, ${COLORS.warning})`,
|
||
}} />
|
||
)}
|
||
|
||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 12, flexWrap: "wrap", gap: 8 }}>
|
||
<div>
|
||
<div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 6 }}>
|
||
<span style={{ fontSize: 16, fontWeight: 700, color: COLORS.text }}>{lead.type}</span>
|
||
<HeatBadge heat={lead.heat} />
|
||
</div>
|
||
<div style={{ display: "flex", gap: 16, flexWrap: "wrap" }}>
|
||
<span style={{ fontSize: 13, color: COLORS.textMuted }}>📍 {lead.region}</span>
|
||
<span style={{ fontSize: 13, color: COLORS.textMuted }}>🏟 {lead.courts} courts</span>
|
||
<span style={{ fontSize: 13, color: COLORS.textMuted }}>💰 {lead.budget}</span>
|
||
<span style={{ fontSize: 13, color: COLORS.textMuted }}>📅 {lead.timeline}</span>
|
||
</div>
|
||
</div>
|
||
<div style={{ fontSize: 12, color: COLORS.textDim }}>{lead.posted}</div>
|
||
</div>
|
||
|
||
<div style={{ display: "flex", gap: 6, flexWrap: "wrap", marginBottom: 14 }}>
|
||
{lead.specs.map((s, i) => (
|
||
<span key={i} style={{
|
||
background: COLORS.bg,
|
||
color: COLORS.textMuted,
|
||
border: `1px solid ${COLORS.border}`,
|
||
borderRadius: 6,
|
||
padding: "3px 10px",
|
||
fontSize: 11,
|
||
fontWeight: 500,
|
||
}}>{s}</span>
|
||
))}
|
||
</div>
|
||
|
||
{lead.unlocked ? (
|
||
<div style={{
|
||
background: COLORS.accentDim,
|
||
border: `1px solid rgba(34,211,167,0.2)`,
|
||
borderRadius: 8,
|
||
padding: "14px 18px",
|
||
}}>
|
||
<div style={{ fontSize: 12, color: COLORS.accent, fontWeight: 600, marginBottom: 8, textTransform: "uppercase", letterSpacing: 0.8 }}>✓ Contact Unlocked</div>
|
||
<div style={{ display: "flex", gap: 24, flexWrap: "wrap" }}>
|
||
<div>
|
||
<div style={{ fontSize: 11, color: COLORS.textDim }}>Contact</div>
|
||
<div style={{ fontSize: 13, color: COLORS.text, fontWeight: 600 }}>{lead.contact.name}</div>
|
||
</div>
|
||
<div>
|
||
<div style={{ fontSize: 11, color: COLORS.textDim }}>Organization</div>
|
||
<div style={{ fontSize: 13, color: COLORS.text, fontWeight: 600 }}>{lead.contact.org}</div>
|
||
</div>
|
||
<div>
|
||
<div style={{ fontSize: 11, color: COLORS.textDim }}>Email</div>
|
||
<div style={{ fontSize: 13, color: COLORS.text, fontWeight: 600 }}>{lead.contact.email}</div>
|
||
</div>
|
||
<div>
|
||
<div style={{ fontSize: 11, color: COLORS.textDim }}>Phone</div>
|
||
<div style={{ fontSize: 13, color: COLORS.text, fontWeight: 600 }}>{lead.contact.phone}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
) : (
|
||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: 10 }}>
|
||
<div style={{ fontSize: 12, color: COLORS.textDim }}>
|
||
{lead.bidders === 0 ? (
|
||
<span style={{ color: COLORS.accent, fontWeight: 600 }}>✦ No other suppliers yet — be first!</span>
|
||
) : (
|
||
<span>{lead.bidders} supplier{lead.bidders > 1 ? "s" : ""} already unlocked this lead</span>
|
||
)}
|
||
</div>
|
||
<button onClick={() => unlock(lead.id)} style={{
|
||
background: `linear-gradient(135deg, ${COLORS.accent}, #1AAB8A)`,
|
||
color: COLORS.bg,
|
||
border: "none",
|
||
borderRadius: 8,
|
||
padding: "10px 20px",
|
||
fontWeight: 700,
|
||
fontSize: 13,
|
||
cursor: "pointer",
|
||
display: "flex",
|
||
alignItems: "center",
|
||
gap: 8,
|
||
}}>
|
||
<span>Unlock Contact</span>
|
||
<span style={{
|
||
background: "rgba(0,0,0,0.2)",
|
||
borderRadius: 4,
|
||
padding: "2px 8px",
|
||
fontSize: 12,
|
||
}}>{lead.credits} credits</span>
|
||
</button>
|
||
</div>
|
||
)}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function ListingView() {
|
||
return (
|
||
<div>
|
||
<h2 style={{ fontSize: 22, fontWeight: 700, color: COLORS.text, margin: "0 0 6px" }}>Your Listing Preview</h2>
|
||
<p style={{ color: COLORS.textMuted, margin: "0 0 24px", fontSize: 14 }}>This is how your company appears in the directory</p>
|
||
|
||
{/* Listing card preview */}
|
||
<div style={{
|
||
background: "linear-gradient(135deg, rgba(245,158,11,0.06), rgba(245,158,11,0.02))",
|
||
border: `1px solid rgba(245,158,11,0.25)`,
|
||
borderRadius: 14,
|
||
padding: "24px 26px",
|
||
marginBottom: 28,
|
||
position: "relative",
|
||
}}>
|
||
<div style={{
|
||
position: "absolute",
|
||
top: 12,
|
||
right: 14,
|
||
background: COLORS.warningDim,
|
||
color: COLORS.warning,
|
||
border: `1px solid rgba(245,158,11,0.3)`,
|
||
padding: "3px 10px",
|
||
borderRadius: 20,
|
||
fontSize: 11,
|
||
fontWeight: 700,
|
||
letterSpacing: 0.5,
|
||
}}>⭐ PROMOTED</div>
|
||
|
||
<div style={{ display: "flex", gap: 18, alignItems: "flex-start", marginBottom: 16 }}>
|
||
<div style={{
|
||
width: 56,
|
||
height: 56,
|
||
borderRadius: 10,
|
||
background: `linear-gradient(135deg, ${COLORS.accent}, ${COLORS.blue})`,
|
||
display: "flex",
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
fontSize: 22,
|
||
fontWeight: 800,
|
||
color: COLORS.bg,
|
||
flexShrink: 0,
|
||
}}>PB</div>
|
||
<div>
|
||
<div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 4 }}>
|
||
<span style={{ fontSize: 18, fontWeight: 700, color: COLORS.text }}>PadelBau GmbH</span>
|
||
<span style={{
|
||
background: COLORS.accentDim,
|
||
color: COLORS.accent,
|
||
border: `1px solid rgba(34,211,167,0.3)`,
|
||
padding: "2px 8px",
|
||
borderRadius: 4,
|
||
fontSize: 10,
|
||
fontWeight: 700,
|
||
}}>✓ VERIFIED</span>
|
||
</div>
|
||
<div style={{ fontSize: 13, color: COLORS.textMuted, marginBottom: 8 }}>Premium padel court manufacturer & installer. 150+ courts built across DACH region. Full-service from planning to turnkey delivery.</div>
|
||
<div style={{ display: "flex", gap: 12, flexWrap: "wrap" }}>
|
||
<span style={{ fontSize: 12, color: COLORS.textDim }}>📍 Cologne, Germany</span>
|
||
<span style={{ fontSize: 12, color: COLORS.textDim }}>🏟 150+ courts installed</span>
|
||
<span style={{ fontSize: 12, color: COLORS.textDim }}>⭐ 4.8/5 (23 reviews)</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
|
||
{["Padel Courts", "Court Construction", "LED Lighting", "Artificial Turf", "Planning & Design"].map((tag, i) => (
|
||
<span key={i} style={{
|
||
background: COLORS.bg,
|
||
color: COLORS.textMuted,
|
||
border: `1px solid ${COLORS.border}`,
|
||
borderRadius: 6,
|
||
padding: "4px 10px",
|
||
fontSize: 11,
|
||
fontWeight: 500,
|
||
}}>{tag}</span>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
<h3 style={{ fontSize: 15, fontWeight: 600, color: COLORS.text, marginBottom: 14 }}>Active Boosts on Your Listing</h3>
|
||
<div style={{ display: "flex", gap: 10, flexWrap: "wrap", marginBottom: 28 }}>
|
||
{[
|
||
{ label: "Logo Visible", color: COLORS.accent },
|
||
{ label: "Yellow Highlight", color: COLORS.warning },
|
||
{ label: "Verified Badge", color: COLORS.accent },
|
||
{ label: "Lead Feed Access", color: COLORS.blue },
|
||
].map((b, i) => (
|
||
<span key={i} style={{
|
||
background: `${b.color}15`,
|
||
color: b.color,
|
||
border: `1px solid ${b.color}30`,
|
||
padding: "6px 14px",
|
||
borderRadius: 8,
|
||
fontSize: 12,
|
||
fontWeight: 600,
|
||
}}>✓ {b.label}</span>
|
||
))}
|
||
</div>
|
||
|
||
<h3 style={{ fontSize: 15, fontWeight: 600, color: COLORS.text, marginBottom: 14 }}>Listing Performance (Last 30 Days)</h3>
|
||
<div style={{ display: "flex", gap: 14, flexWrap: "wrap" }}>
|
||
<StatCard label="Views" value="1,247" sub="From directory search" color={COLORS.accent} />
|
||
<StatCard label="Profile Clicks" value="183" sub="14.7% click rate" color={COLORS.blue} />
|
||
<StatCard label="Website Clicks" value="67" sub="From your listing" color={COLORS.purple} />
|
||
<StatCard label="Contact Requests" value="12" sub="Direct inquiries" color={COLORS.warning} />
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function BoostView() {
|
||
const [selected, setSelected] = useState(
|
||
Object.fromEntries(upsells.map(u => [u.id, u.preselected]))
|
||
);
|
||
|
||
const toggle = (id) => setSelected({ ...selected, [id]: !selected[id] });
|
||
|
||
const total = upsells.reduce((sum, u) => {
|
||
if (!selected[u.id]) return sum;
|
||
return sum + u.price;
|
||
}, 0);
|
||
|
||
const monthlyTotal = upsells.reduce((sum, u) => {
|
||
if (!selected[u.id] || u.period !== "/mo") return sum;
|
||
return sum + u.price;
|
||
}, 0);
|
||
|
||
const viewMultiplier = upsells.reduce((mult, u) => {
|
||
if (!selected[u.id]) return mult;
|
||
const m = parseFloat(u.boost.match(/[\d.]+/)?.[0] || 0);
|
||
if (u.boost.includes("×")) return mult * m;
|
||
if (u.boost.includes("%")) return mult * (1 + m / 100);
|
||
return mult;
|
||
}, 1);
|
||
|
||
return (
|
||
<div>
|
||
<h2 style={{ fontSize: 22, fontWeight: 700, color: COLORS.text, margin: "0 0 6px" }}>Boost Your Visibility</h2>
|
||
<p style={{ color: COLORS.textMuted, margin: "0 0 24px", fontSize: 14 }}>Select upgrades to stand out · Pre-selected options are our recommendation</p>
|
||
|
||
<div style={{ display: "flex", gap: 20, flexWrap: "wrap" }}>
|
||
{/* Upsell list */}
|
||
<div style={{ flex: "1 1 400px", display: "flex", flexDirection: "column", gap: 10 }}>
|
||
{upsells.map(u => {
|
||
const isOn = selected[u.id];
|
||
return (
|
||
<div key={u.id} onClick={() => toggle(u.id)} style={{
|
||
background: isOn ? COLORS.surfaceLight : COLORS.surface,
|
||
border: `1px solid ${isOn ? COLORS.accent + "40" : COLORS.border}`,
|
||
borderRadius: 10,
|
||
padding: "16px 18px",
|
||
cursor: "pointer",
|
||
transition: "all 0.15s",
|
||
display: "flex",
|
||
justifyContent: "space-between",
|
||
alignItems: "center",
|
||
gap: 14,
|
||
}}>
|
||
<div style={{ display: "flex", gap: 14, alignItems: "center", flex: 1 }}>
|
||
<div style={{
|
||
width: 22,
|
||
height: 22,
|
||
borderRadius: 6,
|
||
border: `2px solid ${isOn ? COLORS.accent : COLORS.textDim}`,
|
||
background: isOn ? COLORS.accent : "transparent",
|
||
display: "flex",
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
fontSize: 13,
|
||
color: COLORS.bg,
|
||
fontWeight: 800,
|
||
flexShrink: 0,
|
||
}}>{isOn ? "✓" : ""}</div>
|
||
<div>
|
||
<div style={{ fontSize: 14, fontWeight: 600, color: COLORS.text, marginBottom: 2 }}>{u.label}</div>
|
||
<div style={{ fontSize: 12, color: COLORS.textDim }}>{u.desc}</div>
|
||
</div>
|
||
</div>
|
||
<div style={{ textAlign: "right", flexShrink: 0 }}>
|
||
<div style={{ fontSize: 16, fontWeight: 700, color: isOn ? COLORS.accent : COLORS.textMuted }}>€{u.price}<span style={{ fontSize: 11, fontWeight: 400, color: COLORS.textDim }}>{u.period}</span></div>
|
||
<div style={{
|
||
fontSize: 11,
|
||
color: COLORS.accent,
|
||
background: COLORS.accentDim,
|
||
borderRadius: 4,
|
||
padding: "2px 8px",
|
||
marginTop: 4,
|
||
display: "inline-block",
|
||
fontWeight: 600,
|
||
}}>{u.boost}</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
|
||
{/* Summary sidebar */}
|
||
<div style={{
|
||
flex: "0 0 260px",
|
||
position: "sticky",
|
||
top: 20,
|
||
alignSelf: "flex-start",
|
||
}}>
|
||
<div style={{
|
||
background: COLORS.surface,
|
||
border: `1px solid ${COLORS.border}`,
|
||
borderRadius: 12,
|
||
padding: "22px 20px",
|
||
}}>
|
||
<div style={{ fontSize: 13, color: COLORS.textDim, textTransform: "uppercase", letterSpacing: 1, marginBottom: 16, fontWeight: 600 }}>Your Plan</div>
|
||
|
||
{upsells.filter(u => selected[u.id]).map(u => (
|
||
<div key={u.id} style={{
|
||
display: "flex",
|
||
justifyContent: "space-between",
|
||
fontSize: 13,
|
||
color: COLORS.text,
|
||
marginBottom: 8,
|
||
}}>
|
||
<span>{u.label}</span>
|
||
<span style={{ color: COLORS.textMuted }}>€{u.price}</span>
|
||
</div>
|
||
))}
|
||
|
||
<div style={{
|
||
borderTop: `1px solid ${COLORS.border}`,
|
||
marginTop: 14,
|
||
paddingTop: 14,
|
||
}}>
|
||
<div style={{ display: "flex", justifyContent: "space-between", marginBottom: 4 }}>
|
||
<span style={{ fontSize: 14, fontWeight: 700, color: COLORS.text }}>Monthly</span>
|
||
<span style={{ fontSize: 18, fontWeight: 700, color: COLORS.accent }}>€{monthlyTotal}/mo</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div style={{
|
||
background: COLORS.accentDim,
|
||
borderRadius: 8,
|
||
padding: "12px 14px",
|
||
marginTop: 16,
|
||
textAlign: "center",
|
||
}}>
|
||
<div style={{ fontSize: 12, color: COLORS.textMuted, marginBottom: 2 }}>Estimated view boost</div>
|
||
<div style={{ fontSize: 22, fontWeight: 800, color: COLORS.accent }}>{viewMultiplier.toFixed(1)}×</div>
|
||
<div style={{ fontSize: 11, color: COLORS.textDim }}>more visibility vs basic listing</div>
|
||
</div>
|
||
|
||
<button style={{
|
||
width: "100%",
|
||
background: `linear-gradient(135deg, ${COLORS.accent}, #1AAB8A)`,
|
||
color: COLORS.bg,
|
||
border: "none",
|
||
borderRadius: 8,
|
||
padding: "12px 0",
|
||
fontWeight: 700,
|
||
fontSize: 14,
|
||
cursor: "pointer",
|
||
marginTop: 16,
|
||
}}>Update Plan →</button>
|
||
|
||
<div style={{ fontSize: 11, color: COLORS.textDim, textAlign: "center", marginTop: 10 }}>
|
||
Cancel anytime · No lock-in contracts
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default function SupplierDashboard() {
|
||
const [activeTab, setActiveTab] = useState("dashboard");
|
||
|
||
return (
|
||
<div style={{
|
||
fontFamily: "'DM Sans', 'Manrope', system-ui, sans-serif",
|
||
background: COLORS.bg,
|
||
color: COLORS.text,
|
||
minHeight: "100vh",
|
||
display: "flex",
|
||
}}>
|
||
{/* Sidebar */}
|
||
<div style={{
|
||
width: 220,
|
||
background: COLORS.surface,
|
||
borderRight: `1px solid ${COLORS.border}`,
|
||
padding: "20px 0",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
flexShrink: 0,
|
||
}}>
|
||
<div style={{
|
||
padding: "0 20px 20px",
|
||
borderBottom: `1px solid ${COLORS.border}`,
|
||
marginBottom: 14,
|
||
}}>
|
||
<div style={{
|
||
fontSize: 17,
|
||
fontWeight: 800,
|
||
color: COLORS.accent,
|
||
letterSpacing: -0.5,
|
||
}}>◈ PadelConnect</div>
|
||
<div style={{ fontSize: 11, color: COLORS.textDim, marginTop: 2 }}>Supplier Portal</div>
|
||
</div>
|
||
|
||
<div style={{ flex: 1 }}>
|
||
{tabs.map(tab => (
|
||
<button key={tab.id} onClick={() => setActiveTab(tab.id)} style={{
|
||
width: "100%",
|
||
display: "flex",
|
||
alignItems: "center",
|
||
gap: 10,
|
||
padding: "10px 20px",
|
||
border: "none",
|
||
background: activeTab === tab.id ? COLORS.accentDim : "transparent",
|
||
color: activeTab === tab.id ? COLORS.accent : COLORS.textMuted,
|
||
fontSize: 13,
|
||
fontWeight: activeTab === tab.id ? 600 : 400,
|
||
cursor: "pointer",
|
||
textAlign: "left",
|
||
borderRight: activeTab === tab.id ? `2px solid ${COLORS.accent}` : "2px solid transparent",
|
||
}}>
|
||
<span style={{ fontSize: 16 }}>{tab.icon}</span>
|
||
{tab.label}
|
||
{tab.id === "leads" && (
|
||
<span style={{
|
||
marginLeft: "auto",
|
||
background: COLORS.hotDim,
|
||
color: COLORS.hot,
|
||
borderRadius: 10,
|
||
padding: "1px 7px",
|
||
fontSize: 11,
|
||
fontWeight: 700,
|
||
}}>3</span>
|
||
)}
|
||
</button>
|
||
))}
|
||
</div>
|
||
|
||
<div style={{
|
||
padding: "14px 20px",
|
||
borderTop: `1px solid ${COLORS.border}`,
|
||
}}>
|
||
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
||
<div style={{
|
||
width: 32,
|
||
height: 32,
|
||
borderRadius: 8,
|
||
background: `linear-gradient(135deg, ${COLORS.accent}, ${COLORS.blue})`,
|
||
display: "flex",
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
fontSize: 13,
|
||
fontWeight: 700,
|
||
color: COLORS.bg,
|
||
}}>PB</div>
|
||
<div>
|
||
<div style={{ fontSize: 12, fontWeight: 600, color: COLORS.text }}>PadelBau GmbH</div>
|
||
<div style={{ fontSize: 11, color: COLORS.textDim }}>Premium Plan</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Main content */}
|
||
<div style={{
|
||
flex: 1,
|
||
padding: "28px 32px",
|
||
overflowY: "auto",
|
||
maxHeight: "100vh",
|
||
}}>
|
||
{activeTab === "dashboard" && <DashboardView />}
|
||
{activeTab === "leads" && <LeadFeedView />}
|
||
{activeTab === "listing" && <ListingView />}
|
||
{activeTab === "boost" && <BoostView />}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|