chore: commit pending changes — logo, base template, scratch designs, changelog
- 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>
This commit is contained in:
855
scratch/supplier-dashboard.jsx
Normal file
855
scratch/supplier-dashboard.jsx
Normal file
@@ -0,0 +1,855 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user