Update microsite rendering and add environment example.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ravi Gulhane 2026-07-13 15:11:22 +05:30
parent 709607aeca
commit d9270632dc
5 changed files with 84 additions and 43 deletions

8
.env.example Normal file
View File

@ -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

17
package-lock.json generated
View File

@ -8,6 +8,7 @@
"name": "eventbuzz-web",
"version": "0.0.1",
"dependencies": {
"bootstrap-icons": "^1.13.1",
"flatpickr": "^4.6.13",
"react": "^18.3.1",
"react-dom": "^18.3.1",
@ -1275,6 +1276,22 @@
"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": {
"version": "4.28.4",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz",

View File

@ -9,6 +9,7 @@
"preview": "vite preview --port 5175"
},
"dependencies": {
"bootstrap-icons": "^1.13.1",
"flatpickr": "^4.6.13",
"react": "^18.3.1",
"react-dom": "^18.3.1",

View File

@ -12,6 +12,21 @@ function str(v: unknown, fallback = ''): string {
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 }) {
const c = section.config;
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 url = str(item.url, '#');
const isReg = label.toLowerCase() === 'register';
if (isReg && !ctx.registrationUrl) return null;
return (
<li key={i}>
<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 headline = str(c.headline, 'Welcome');
const subtext = str(c.subtext);
const heroMetaLine = str(c.heroMetaLine);
const ctaText = str(c.ctaText);
const ctaUrl = str(c.ctaUrl, '#');
const ctaUrl = resolveRegistrationCtaUrl(ctaText, c.ctaUrl, ctx.registrationUrl);
const bgImage = str(c.backgroundImage);
const secText = str(c.secondaryCtaText);
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}
{(ctaText || secText) && (
<div className="eb-hero-cta">
{ctaText ? (
{ctaText && ctaUrl ? (
<a className="btn btn-primary btn-lg" href={ctaUrl}>
{ctaText}
</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 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 ctaText = str(c.ctaText);
const ctaUrl = str(c.ctaUrl, '#');
const ctaText = str(c.ctaText) || str(c.ctaLabel);
const ctaUrl = resolveRegistrationCtaUrl(ctaText, c.ctaUrl, ctx.registrationUrl);
return (
<section id="about" className="py-5 eb-section-light">
@ -161,7 +177,7 @@ function AboutSection({ section }: { section: MicrositeSection }) {
))}
</ul>
) : null}
{ctaText ? (
{ctaText && ctaUrl ? (
<div className="text-center mt-4">
<a className="btn btn-primary btn-lg" href={ctaUrl}>
{ctaText}
@ -427,40 +443,27 @@ function AboutSection({ section }: { section: MicrositeSection }) {
// );
// }
function AgendaSection({
section,
items,
ctx,
}: {
section: MicrositeSection;
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
? 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))];
// 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);
// Filter sessions according to selected date
const filteredList = list.filter((item) => item.date === selectedDate);
console.log("dates:", dates);
console.log("selectedDate:", selectedDate);
console.log("filteredList:", filteredList);
console.log("list:", list);
const filteredList = dates.length ? list.filter((item) => item.date === selectedDate) : list;
return (
<section id="schedule" className="eb-schedule-section section py-5">
<div className="container">
@ -728,9 +731,11 @@ console.log("list:", list);
{/* Action Buttons */}
<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
</a>
) : null}
<a className="btn btn-outline-secondary btn-lg" href="#hero">
Back to top
</a>
@ -790,10 +795,12 @@ function SpeakersSection({ section, speakers }: { section: MicrositeSection; spe
}
function RegisterLinksSection({ section, ctx }: { section: MicrositeSection; ctx: Ctx }) {
if (!ctx.registrationUrl) return null;
const c = section.config;
const title = str(c.title, 'Register');
const primaryLabel = str(c.primaryLabel, 'Register Now');
const primaryUrl = str(c.primaryUrl, ctx.registrationUrl);
const primaryUrl = ctx.registrationUrl;
const secondaryLabel = str(c.secondaryLabel);
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 subtext = str(c.subtext);
const buttonLabel = str(c.buttonLabel, 'Register');
const buttonUrl = str(c.buttonUrl, ctx.registrationUrl);
const buttonUrl = resolveRegistrationCtaUrl(buttonLabel, c.buttonUrl, ctx.registrationUrl);
return (
<section id="cta" className="py-5 text-white text-center" style={{ background: 'var(--eb-conf-primary)' }}>
<div className="container">
<h2 className="eb-section-heading text-white mb-3">{headline}</h2>
{subtext ? <p className="lead mb-4">{subtext}</p> : null}
{buttonUrl && buttonUrl !== '#' ? (
<a className="btn btn-light btn-lg" href={buttonUrl}>
{buttonLabel}
</a>
) : null}
</div>
</section>
);
@ -1068,9 +1077,11 @@ function TicketsSection({ section, ctx }: { section: MicrositeSection; ctx: Ctx
<h3 style={{ marginTop: 0 }}>{str(tier.name)}</h3>
<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}
{ctx.registrationUrl ? (
<a className="btn btn-primary" href={ctx.registrationUrl}>
Register
</a>
) : null}
</div>
</div>
))}
@ -1085,13 +1096,13 @@ export function SectionRenderer({ section, ctx }: { section: MicrositeSection; c
case 'header':
return <HeaderSection section={section} ctx={ctx} />;
case 'banner':
return <BannerSection section={section} />;
return <BannerSection section={section} ctx={ctx} />;
case 'countdown':
return <CountdownSection section={section} ctx={ctx} />;
case 'about':
return <AboutSection section={section} />;
return <AboutSection section={section} ctx={ctx} />;
case 'agenda':
return <AgendaSection section={section} items={ctx.data.agenda} />;
return <AgendaSection section={section} items={ctx.data.agenda} ctx={ctx} />;
case 'speakers':
return <SpeakersSection section={section} speakers={ctx.data.speakers} />;
case 'register_links':

View File

@ -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 };
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 (
<MicrositeLayout data={data}>