Update microsite rendering and add environment example.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
709607aeca
commit
d9270632dc
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Local development — Vite proxies /api to VITE_API_PROXY_TARGET
|
||||||
|
VITE_API_BASE_URL=/api
|
||||||
|
VITE_API_PROXY_TARGET=http://localhost:3001
|
||||||
|
VITE_PUBLIC_WEB_BASE=http://localhost:5175
|
||||||
|
|
||||||
|
# Production (see .env.production — used automatically by `npm run build`)
|
||||||
|
# VITE_API_BASE_URL=https://api.eventbuzz.live/api
|
||||||
|
# VITE_PUBLIC_WEB_BASE=https://web.eventbuzz.live
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
"name": "eventbuzz-web",
|
"name": "eventbuzz-web",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"bootstrap-icons": "^1.13.1",
|
||||||
"flatpickr": "^4.6.13",
|
"flatpickr": "^4.6.13",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
|
|
@ -1275,6 +1276,22 @@
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/bootstrap-icons": {
|
||||||
|
"version": "1.13.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.13.1.tgz",
|
||||||
|
"integrity": "sha512-ijombt4v6bv5CLeXvRWKy7CuM3TRTuPEuGaGKvTV5cz65rQSY8RQ2JcHt6b90cBBAC7s8fsf2EkQDldzCoXUjw==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/twbs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/bootstrap"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/browserslist": {
|
"node_modules/browserslist": {
|
||||||
"version": "4.28.4",
|
"version": "4.28.4",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz",
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
"preview": "vite preview --port 5175"
|
"preview": "vite preview --port 5175"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"bootstrap-icons": "^1.13.1",
|
||||||
"flatpickr": "^4.6.13",
|
"flatpickr": "^4.6.13",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,21 @@ function str(v: unknown, fallback = ''): string {
|
||||||
return v != null ? String(v).trim() : fallback;
|
return v != null ? String(v).trim() : fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isRegisterLabel(label: string): boolean {
|
||||||
|
return /\bregister\b/i.test(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
function looksLikeRegistrationUrl(url: string): boolean {
|
||||||
|
return /\/register\/?$/i.test(url) || /\/form\/[a-f0-9]{24}\/?$/i.test(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveRegistrationCtaUrl(label: string, configuredUrl: unknown, registrationUrl: string): string {
|
||||||
|
const configured = str(configuredUrl);
|
||||||
|
const wantsRegistration = isRegisterLabel(label) || looksLikeRegistrationUrl(configured);
|
||||||
|
if (wantsRegistration) return registrationUrl;
|
||||||
|
return configured || '#';
|
||||||
|
}
|
||||||
|
|
||||||
function HeaderSection({ section, ctx }: { section: MicrositeSection; ctx: Ctx }) {
|
function HeaderSection({ section, ctx }: { section: MicrositeSection; ctx: Ctx }) {
|
||||||
const c = section.config;
|
const c = section.config;
|
||||||
const logoText = str(c.logoText, ctx.data.event.name);
|
const logoText = str(c.logoText, ctx.data.event.name);
|
||||||
|
|
@ -40,6 +55,7 @@ function HeaderSection({ section, ctx }: { section: MicrositeSection; ctx: Ctx }
|
||||||
const label = str(item.label);
|
const label = str(item.label);
|
||||||
const url = str(item.url, '#');
|
const url = str(item.url, '#');
|
||||||
const isReg = label.toLowerCase() === 'register';
|
const isReg = label.toLowerCase() === 'register';
|
||||||
|
if (isReg && !ctx.registrationUrl) return null;
|
||||||
return (
|
return (
|
||||||
<li key={i}>
|
<li key={i}>
|
||||||
<a
|
<a
|
||||||
|
|
@ -59,13 +75,13 @@ function HeaderSection({ section, ctx }: { section: MicrositeSection; ctx: Ctx }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function BannerSection({ section }: { section: MicrositeSection }) {
|
function BannerSection({ section, ctx }: { section: MicrositeSection; ctx: Ctx }) {
|
||||||
const c = section.config;
|
const c = section.config;
|
||||||
const headline = str(c.headline, 'Welcome');
|
const headline = str(c.headline, 'Welcome');
|
||||||
const subtext = str(c.subtext);
|
const subtext = str(c.subtext);
|
||||||
const heroMetaLine = str(c.heroMetaLine);
|
const heroMetaLine = str(c.heroMetaLine);
|
||||||
const ctaText = str(c.ctaText);
|
const ctaText = str(c.ctaText);
|
||||||
const ctaUrl = str(c.ctaUrl, '#');
|
const ctaUrl = resolveRegistrationCtaUrl(ctaText, c.ctaUrl, ctx.registrationUrl);
|
||||||
const bgImage = str(c.backgroundImage);
|
const bgImage = str(c.backgroundImage);
|
||||||
const secText = str(c.secondaryCtaText);
|
const secText = str(c.secondaryCtaText);
|
||||||
const secUrl = str(c.secondaryCtaUrl, '#schedule');
|
const secUrl = str(c.secondaryCtaUrl, '#schedule');
|
||||||
|
|
@ -80,7 +96,7 @@ function BannerSection({ section }: { section: MicrositeSection }) {
|
||||||
{subtext ? <p className="eb-hero-intro mb-4">{subtext}</p> : null}
|
{subtext ? <p className="eb-hero-intro mb-4">{subtext}</p> : null}
|
||||||
{(ctaText || secText) && (
|
{(ctaText || secText) && (
|
||||||
<div className="eb-hero-cta">
|
<div className="eb-hero-cta">
|
||||||
{ctaText ? (
|
{ctaText && ctaUrl ? (
|
||||||
<a className="btn btn-primary btn-lg" href={ctaUrl}>
|
<a className="btn btn-primary btn-lg" href={ctaUrl}>
|
||||||
{ctaText}
|
{ctaText}
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -141,13 +157,13 @@ function CountdownSection({ section, ctx }: { section: MicrositeSection; ctx: Ct
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function AboutSection({ section }: { section: MicrositeSection }) {
|
function AboutSection({ section, ctx }: { section: MicrositeSection; ctx: Ctx }) {
|
||||||
const c = section.config;
|
const c = section.config;
|
||||||
const title = str(c.title, 'About');
|
const title = str(c.title, 'About');
|
||||||
const body = str(c.body);
|
const body = str(c.body) || str(c.lead);
|
||||||
const bullets = Array.isArray(c.bullets) ? (c.bullets as string[]) : [];
|
const bullets = Array.isArray(c.bullets) ? (c.bullets as string[]) : [];
|
||||||
const ctaText = str(c.ctaText);
|
const ctaText = str(c.ctaText) || str(c.ctaLabel);
|
||||||
const ctaUrl = str(c.ctaUrl, '#');
|
const ctaUrl = resolveRegistrationCtaUrl(ctaText, c.ctaUrl, ctx.registrationUrl);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="about" className="py-5 eb-section-light">
|
<section id="about" className="py-5 eb-section-light">
|
||||||
|
|
@ -161,7 +177,7 @@ function AboutSection({ section }: { section: MicrositeSection }) {
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
) : null}
|
) : null}
|
||||||
{ctaText ? (
|
{ctaText && ctaUrl ? (
|
||||||
<div className="text-center mt-4">
|
<div className="text-center mt-4">
|
||||||
<a className="btn btn-primary btn-lg" href={ctaUrl}>
|
<a className="btn btn-primary btn-lg" href={ctaUrl}>
|
||||||
{ctaText}
|
{ctaText}
|
||||||
|
|
@ -427,40 +443,27 @@ function AboutSection({ section }: { section: MicrositeSection }) {
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
function AgendaSection({
|
function AgendaSection({
|
||||||
|
|
||||||
section,
|
section,
|
||||||
items,
|
items,
|
||||||
|
ctx,
|
||||||
}: {
|
}: {
|
||||||
|
|
||||||
section: MicrositeSection;
|
section: MicrositeSection;
|
||||||
items: AgendaItem[];
|
items: AgendaItem[];
|
||||||
|
ctx: Ctx;
|
||||||
}) {
|
}) {
|
||||||
console.log("Items:",items);
|
const title = str(section.config.title, 'Schedule');
|
||||||
|
|
||||||
|
|
||||||
const title = str(section.config.title, "Schedule");
|
|
||||||
|
|
||||||
const list = items.length
|
const list = items.length
|
||||||
? items
|
? items
|
||||||
: [{ title: "Session 1", time: "9:00 AM", track: "Main", date: "" }];
|
: [{ title: 'Session 1', time: '9:00 AM', track: 'Main', date: '' }];
|
||||||
|
|
||||||
// Get unique dates
|
|
||||||
const dates = [...new Set(list.map((item) => item.date).filter(Boolean))];
|
const dates = [...new Set(list.map((item) => item.date).filter(Boolean))];
|
||||||
|
|
||||||
// Selected day
|
const [selectedDate, setSelectedDate] = useState(dates.length ? dates[0]! : '');
|
||||||
const [selectedDate, setSelectedDate] = useState(
|
|
||||||
dates.length ? dates[0] : ""
|
|
||||||
);
|
|
||||||
|
|
||||||
// Expanded accordion
|
|
||||||
const [expandedIndex, setExpandedIndex] = useState<number | null>(null);
|
const [expandedIndex, setExpandedIndex] = useState<number | null>(null);
|
||||||
|
|
||||||
// Filter sessions according to selected date
|
const filteredList = dates.length ? list.filter((item) => item.date === selectedDate) : list;
|
||||||
const filteredList = list.filter((item) => item.date === selectedDate);
|
|
||||||
console.log("dates:", dates);
|
|
||||||
console.log("selectedDate:", selectedDate);
|
|
||||||
console.log("filteredList:", filteredList);
|
|
||||||
console.log("list:", list);
|
|
||||||
return (
|
return (
|
||||||
<section id="schedule" className="eb-schedule-section section py-5">
|
<section id="schedule" className="eb-schedule-section section py-5">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
|
|
@ -728,9 +731,11 @@ console.log("list:", list);
|
||||||
|
|
||||||
{/* Action Buttons */}
|
{/* Action Buttons */}
|
||||||
<div className="text-center mt-4">
|
<div className="text-center mt-4">
|
||||||
<a className="btn btn-primary btn-lg me-2" href="#register">
|
{ctx.registrationUrl ? (
|
||||||
|
<a className="btn btn-primary btn-lg me-2" href={ctx.registrationUrl}>
|
||||||
Register
|
Register
|
||||||
</a>
|
</a>
|
||||||
|
) : null}
|
||||||
<a className="btn btn-outline-secondary btn-lg" href="#hero">
|
<a className="btn btn-outline-secondary btn-lg" href="#hero">
|
||||||
Back to top
|
Back to top
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -790,10 +795,12 @@ function SpeakersSection({ section, speakers }: { section: MicrositeSection; spe
|
||||||
}
|
}
|
||||||
|
|
||||||
function RegisterLinksSection({ section, ctx }: { section: MicrositeSection; ctx: Ctx }) {
|
function RegisterLinksSection({ section, ctx }: { section: MicrositeSection; ctx: Ctx }) {
|
||||||
|
if (!ctx.registrationUrl) return null;
|
||||||
|
|
||||||
const c = section.config;
|
const c = section.config;
|
||||||
const title = str(c.title, 'Register');
|
const title = str(c.title, 'Register');
|
||||||
const primaryLabel = str(c.primaryLabel, 'Register Now');
|
const primaryLabel = str(c.primaryLabel, 'Register Now');
|
||||||
const primaryUrl = str(c.primaryUrl, ctx.registrationUrl);
|
const primaryUrl = ctx.registrationUrl;
|
||||||
const secondaryLabel = str(c.secondaryLabel);
|
const secondaryLabel = str(c.secondaryLabel);
|
||||||
const secondaryUrl = str(c.secondaryUrl, '#');
|
const secondaryUrl = str(c.secondaryUrl, '#');
|
||||||
|
|
||||||
|
|
@ -959,16 +966,18 @@ function CtaSection({ section, ctx }: { section: MicrositeSection; ctx: Ctx }) {
|
||||||
const headline = str(c.headline, 'Join us');
|
const headline = str(c.headline, 'Join us');
|
||||||
const subtext = str(c.subtext);
|
const subtext = str(c.subtext);
|
||||||
const buttonLabel = str(c.buttonLabel, 'Register');
|
const buttonLabel = str(c.buttonLabel, 'Register');
|
||||||
const buttonUrl = str(c.buttonUrl, ctx.registrationUrl);
|
const buttonUrl = resolveRegistrationCtaUrl(buttonLabel, c.buttonUrl, ctx.registrationUrl);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="cta" className="py-5 text-white text-center" style={{ background: 'var(--eb-conf-primary)' }}>
|
<section id="cta" className="py-5 text-white text-center" style={{ background: 'var(--eb-conf-primary)' }}>
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<h2 className="eb-section-heading text-white mb-3">{headline}</h2>
|
<h2 className="eb-section-heading text-white mb-3">{headline}</h2>
|
||||||
{subtext ? <p className="lead mb-4">{subtext}</p> : null}
|
{subtext ? <p className="lead mb-4">{subtext}</p> : null}
|
||||||
|
{buttonUrl && buttonUrl !== '#' ? (
|
||||||
<a className="btn btn-light btn-lg" href={buttonUrl}>
|
<a className="btn btn-light btn-lg" href={buttonUrl}>
|
||||||
{buttonLabel}
|
{buttonLabel}
|
||||||
</a>
|
</a>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|
@ -1068,9 +1077,11 @@ function TicketsSection({ section, ctx }: { section: MicrositeSection; ctx: Ctx
|
||||||
<h3 style={{ marginTop: 0 }}>{str(tier.name)}</h3>
|
<h3 style={{ marginTop: 0 }}>{str(tier.name)}</h3>
|
||||||
<p style={{ fontSize: '1.5rem', fontWeight: 700, color: 'var(--eb-conf-primary)' }}>{str(tier.price)}</p>
|
<p style={{ fontSize: '1.5rem', fontWeight: 700, color: 'var(--eb-conf-primary)' }}>{str(tier.price)}</p>
|
||||||
{tier.description ? <p className="text-muted">{tier.description}</p> : null}
|
{tier.description ? <p className="text-muted">{tier.description}</p> : null}
|
||||||
|
{ctx.registrationUrl ? (
|
||||||
<a className="btn btn-primary" href={ctx.registrationUrl}>
|
<a className="btn btn-primary" href={ctx.registrationUrl}>
|
||||||
Register
|
Register
|
||||||
</a>
|
</a>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
@ -1085,13 +1096,13 @@ export function SectionRenderer({ section, ctx }: { section: MicrositeSection; c
|
||||||
case 'header':
|
case 'header':
|
||||||
return <HeaderSection section={section} ctx={ctx} />;
|
return <HeaderSection section={section} ctx={ctx} />;
|
||||||
case 'banner':
|
case 'banner':
|
||||||
return <BannerSection section={section} />;
|
return <BannerSection section={section} ctx={ctx} />;
|
||||||
case 'countdown':
|
case 'countdown':
|
||||||
return <CountdownSection section={section} ctx={ctx} />;
|
return <CountdownSection section={section} ctx={ctx} />;
|
||||||
case 'about':
|
case 'about':
|
||||||
return <AboutSection section={section} />;
|
return <AboutSection section={section} ctx={ctx} />;
|
||||||
case 'agenda':
|
case 'agenda':
|
||||||
return <AgendaSection section={section} items={ctx.data.agenda} />;
|
return <AgendaSection section={section} items={ctx.data.agenda} ctx={ctx} />;
|
||||||
case 'speakers':
|
case 'speakers':
|
||||||
return <SpeakersSection section={section} speakers={ctx.data.speakers} />;
|
return <SpeakersSection section={section} speakers={ctx.data.speakers} />;
|
||||||
case 'register_links':
|
case 'register_links':
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,9 @@ export function EventMicrositePage() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const registrationUrl = data.registrationUrl || `${PUBLIC_WEB_BASE}/event/${eventId}/register`;
|
const registrationUrl = data.registrationFormId
|
||||||
|
? `${PUBLIC_WEB_BASE}/event/${eventId}/form/${data.registrationFormId}`
|
||||||
|
: '';
|
||||||
const ctx = { data, eventId, registrationUrl };
|
const ctx = { data, eventId, registrationUrl };
|
||||||
|
|
||||||
if (data.micrositeHtml) {
|
if (data.micrositeHtml) {
|
||||||
|
|
@ -53,7 +55,9 @@ export function EventMicrositePage() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const sections = [...(data.sections || [])].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
const sections = [...(data.sections || [])]
|
||||||
|
.filter((section) => section.type !== 'sponsors')
|
||||||
|
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MicrositeLayout data={data}>
|
<MicrositeLayout data={data}>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue