diff --git a/public/features/blocks-gallery.png b/public/features/blocks-gallery.png new file mode 100644 index 00000000..f257c326 Binary files /dev/null and b/public/features/blocks-gallery.png differ diff --git a/public/features/blocks.png b/public/features/blocks.png new file mode 100644 index 00000000..4362f2e8 Binary files /dev/null and b/public/features/blocks.png differ diff --git a/public/features/faq.png b/public/features/faq.png new file mode 100644 index 00000000..4c8caeaf Binary files /dev/null and b/public/features/faq.png differ diff --git a/public/features/intermission-1.jpg b/public/features/intermission-1.jpg new file mode 100644 index 00000000..e1a49276 Binary files /dev/null and b/public/features/intermission-1.jpg differ diff --git a/public/features/intermission-2.jpg b/public/features/intermission-2.jpg new file mode 100644 index 00000000..16611bec Binary files /dev/null and b/public/features/intermission-2.jpg differ diff --git a/public/features/jobs.png b/public/features/jobs.png new file mode 100644 index 00000000..a2869f06 Binary files /dev/null and b/public/features/jobs.png differ diff --git a/public/features/public-site.png b/public/features/public-site.png new file mode 100644 index 00000000..6624194d Binary files /dev/null and b/public/features/public-site.png differ diff --git a/public/features/schedule.png b/public/features/schedule.png new file mode 100644 index 00000000..45936449 Binary files /dev/null and b/public/features/schedule.png differ diff --git a/public/features/session-edit.png b/public/features/session-edit.png new file mode 100644 index 00000000..ab8b2782 Binary files /dev/null and b/public/features/session-edit.png differ diff --git a/public/features/sessions.png b/public/features/sessions.png new file mode 100644 index 00000000..6699ed69 Binary files /dev/null and b/public/features/sessions.png differ diff --git a/public/features/speaker-self-edit.png b/public/features/speaker-self-edit.png new file mode 100644 index 00000000..ff60b6f8 Binary files /dev/null and b/public/features/speaker-self-edit.png differ diff --git a/public/features/speakers.png b/public/features/speakers.png new file mode 100644 index 00000000..911fad05 Binary files /dev/null and b/public/features/speakers.png differ diff --git a/public/features/sponsors.png b/public/features/sponsors.png new file mode 100644 index 00000000..ef64607a Binary files /dev/null and b/public/features/sponsors.png differ diff --git a/public/features/team.png b/public/features/team.png new file mode 100644 index 00000000..a6f240d5 Binary files /dev/null and b/public/features/team.png differ diff --git a/public/features/tickets.png b/public/features/tickets.png new file mode 100644 index 00000000..52ee39e1 Binary files /dev/null and b/public/features/tickets.png differ diff --git a/public/features/transcription.png b/public/features/transcription.png new file mode 100644 index 00000000..069e8081 Binary files /dev/null and b/public/features/transcription.png differ diff --git a/src/App.tsx b/src/App.tsx index e60f72cc..d5b34e1a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -16,6 +16,9 @@ import { EventApp } from './events/page/EventApp' import { lazyWithRetry } from './components/lazyWithRetry' const PublicApp = lazyWithRetry(() => import('./public/PublicApp').then((module) => ({ default: module.PublicApp }))) +const FeaturesPage = lazyWithRetry(() => + import('./public/features/FeaturesPage').then((module) => ({ default: module.FeaturesPage })) +) const EventsScreen = lazyWithRetry(() => import('./events/list/EventsScreen').then((module) => ({ default: module.EventsScreen })) @@ -73,6 +76,11 @@ export const App = ({}) => { + + }> + + + diff --git a/src/auth/LoginForm.tsx b/src/auth/LoginForm.tsx index 6d50a539..d2421944 100644 --- a/src/auth/LoginForm.tsx +++ b/src/auth/LoginForm.tsx @@ -37,6 +37,9 @@ export const LoginForm = ({}) => { height="200px" /> + + New here? Discover what OpenPlanner can do → + void }) => { + const [index, setIndex] = useState(0) + const image = images[index] + + return ( + + + onZoom(image)} + sx={{ width: '100%', display: 'block', cursor: 'zoom-in' }} + /> + + {images.length > 1 && ( + + setIndex((index - 1 + images.length) % images.length)}> + + + {images.map((dot, dotIndex) => ( + setIndex(dotIndex)} + sx={{ + width: 8, + height: 8, + borderRadius: '50%', + cursor: 'pointer', + backgroundColor: dotIndex === index ? 'primary.main' : 'action.disabled', + }} + /> + ))} + setIndex((index + 1) % images.length)}> + + + + )} + + ) +} + +const FeatureSection = ({ + section, + index, + onZoom, +}: { + section: FeatureSectionContent + index: number + onZoom: (image: FeatureImage) => void +}) => { + const imageFirst = index % 2 === 1 + const hasImages = !!section.images?.length + + return ( + + + + {section.title} + + + {section.tagline} + + + {section.bullets.map((bullet, bulletIndex) => ( + + {bullet} + + ))} + + + {hasImages && ( + + + + )} + + ) +} + +export const FeaturesPage = () => { + const theme = useTheme() + const [zoomedImage, setZoomedImage] = useState(null) + + useEffect(() => { + document.title = 'OpenPlanner | Features' + }, []) + + return ( + + + + + + Everything you need to run a conference + + + OpenPlanner is a free and open source platform to plan a conference and run the live show: + talks, speakers, sponsors, schedule, and day-of tooling. + + + + + + + {['Free & open source', 'ConferenceHall import', 'Static JSON API', 'Live transcription'].map( + (label) => ( + + ) + )} + + + + {FEATURE_SECTIONS.map((section, index) => ( + + ))} + + + + Plan your next event with OpenPlanner + + + No pricing page — it is open source. Sign in and create your event. + + + + + + Built by organizers, for organizers.{' '} + + Contribute on GitHub + + + + + + setZoomedImage(null)} + maxWidth={false} + PaperProps={{ sx: { backgroundColor: 'transparent', boxShadow: 'none', margin: 2 } }}> + {zoomedImage && ( + setZoomedImage(null)} sx={{ cursor: 'zoom-out' }}> + setZoomedImage(null)} + sx={{ + position: 'absolute', + top: 8, + right: 8, + backgroundColor: 'rgba(0,0,0,0.55)', + color: '#FFF', + }}> + + + + + )} + + + ) +} diff --git a/src/public/features/featuresContent.tsx b/src/public/features/featuresContent.tsx new file mode 100644 index 00000000..66c67e62 --- /dev/null +++ b/src/public/features/featuresContent.tsx @@ -0,0 +1,198 @@ +import { ReactNode } from 'react' +import { Link } from '@mui/material' + +export interface FeatureImage { + src: string + alt: string +} + +export interface FeatureSectionContent { + key: string + title: string + tagline: string + bullets: ReactNode[] + images?: FeatureImage[] +} + +const ExternalLink = ({ href, children }: { href: string; children: ReactNode }) => ( + + {children} + +) + +export const FEATURE_SECTIONS: FeatureSectionContent[] = [ + { + key: 'schedule', + title: 'A real schedule builder', + tagline: 'Drag, drop, done — your program takes shape in minutes.', + bullets: [ + 'Calendar view per day with your tracks and rooms side by side', + 'Formats with durations, categories with colors — sessions snap into place', + 'Schedule templates to prepare the grid before talks are picked', + 'Updated in realtime for every organizer, exportable as PDF', + ], + images: [ + { + src: '/features/schedule.png', + alt: 'The OpenPlanner schedule calendar with sessions across three tracks', + }, + ], + }, + { + key: 'sessions', + title: 'Sessions, from CFP to stage', + tagline: 'Import accepted talks or add your own.', + bullets: [ + <> + Pick accepted talks from ConferenceHall{' '} + without overwriting its data + , + 'Filter, bulk-edit, and export sessions any way you need', + 'Categories, formats, languages, levels, private notes', + <> + AI-assisted teasing posts and session visuals generated with{' '} + ShortVid + , + ], + images: [ + { src: '/features/sessions.png', alt: 'The sessions list with filters and quick actions' }, + { src: '/features/session-edit.png', alt: 'The session edit page with dates, speakers and media' }, + ], + }, + { + key: 'speakers', + title: 'Speakers who manage themselves', + tagline: 'Profiles, photos, socials — and a magic self-edit link.', + bullets: [ + 'Speaker profiles with photo, company, socials and private contact info', + 'Self-edit via a magic link: speakers update their own bio and photo', + 'Every change goes through admin approval before it is published', + 'Sessions linked automatically, visible at a glance', + ], + images: [ + { src: '/features/speakers.png', alt: 'The speakers list with linked sessions' }, + { + src: '/features/speaker-self-edit.png', + alt: 'The public speaker self-edit form opened from a magic link', + }, + ], + }, + { + key: 'sponsors', + title: 'Sponsors and a public job board', + tagline: 'Categories, logos, and job posts your sponsors submit themselves.', + bullets: [ + 'Sponsor categories with drag-and-drop ordering', + 'Each sponsor gets a private token to submit job offers', + 'You approve job posts before they reach the public site', + 'Everything ships in the exported JSON for your website', + ], + images: [ + { src: '/features/sponsors.png', alt: 'Sponsor categories with logos' }, + { src: '/features/jobs.png', alt: 'The job posts moderation page' }, + ], + }, + { + key: 'team', + title: 'Your team, on the website', + tagline: 'The people behind the event, grouped and ordered.', + bullets: [ + 'Team members with photos, roles and socials', + 'Multiple teams with drag-and-drop ordering across and within groups', + 'Published in the exported JSON like everything else', + ], + images: [{ src: '/features/team.png', alt: 'The team management page' }], + }, + { + key: 'tickets', + title: 'Tickets without the spreadsheet', + tagline: 'Tiers, prices, availability — ready for your site.', + bullets: [ + 'Ticket tiers with price, currency, quantity and sale dates', + 'Highlight a tier, flag sold out, add a custom message', + 'Optional newsletter opt-in per tier, all exported to your website', + ], + images: [{ src: '/features/tickets.png', alt: 'The tickets management page' }], + }, + { + key: 'blocks', + title: 'Building blocks for your website', + tagline: 'Describe any custom content — your site renders it.', + bullets: [ + 'Markdown, images, links/CTAs or raw JSON, organized by page and group', + 'Single values, ordered lists, or keyed maps — your choice per block', + 'Multi-image drop upload, live preview, enable/disable per block', + 'Exported as blocks.. in the same JSON as the rest', + ], + images: [ + { src: '/features/blocks.png', alt: 'The building blocks editor' }, + { src: '/features/blocks-gallery.png', alt: 'An image gallery block with multi-image drop upload' }, + ], + }, + { + key: 'faq', + title: 'FAQ, public or private', + tagline: 'One editor, two audiences.', + bullets: [ + 'Categories with drag-and-drop questions and markdown answers', + 'Public pages for attendees, private links for speakers or staff', + 'Share a whole category with a single URL, export as PDF', + ], + images: [{ src: '/features/faq.png', alt: 'The FAQ editor with categories' }], + }, + { + key: 'live', + title: 'Run the live show', + tagline: 'Day-of tooling built from running real conferences.', + bullets: [ + <> + Live transcription and on-screen subtitles powered by{' '} + Gladia + , + 'Intermission screens between talks: next-session lower third, sponsor and media slideshow, driven by your schedule', + 'WhatsApp track management: poll each room "ready?", broadcast the GO, auto-remind on timing', + 'These screens run in any browser — no login needed in the room', + ], + images: [ + { + src: '/features/intermission-1.jpg', + alt: 'Intermission screen at Sunny Tech with the next-session lower third', + }, + { + src: '/features/transcription.png', + alt: 'The live transcription screen with the current and next talk', + }, + { + src: '/features/intermission-2.jpg', + alt: 'Intermission screen rotating through the sponsor slideshow', + }, + ], + }, + { + key: 'publish', + title: 'Publish anywhere', + tagline: 'Your data, one JSON, any website.', + bullets: [ + <> + One click deploys a static JSON with sessions, speakers, sponsors, team, FAQ, tickets and blocks —{' '} + + see Sunny Tech 2026's real JSON + + , + 'Webhooks trigger your website build (GitHub repository_dispatch supported out of the box)', + <> + Built-in public schedule site if you do not want to build one —{' '} + + see Sunny Tech 2026's live + + , + <> + OpenFeedback and Voxxrin exports, plus a full{' '} + REST API with per-event keys + , + ], + images: [ + { src: '/features/public-site.png', alt: "Sunny Tech 2026's public website built on the exported JSON" }, + ], + }, +]