Internal Development Reference · Not Public-Facing

B-CU Homecoming — App Reference Guide

Access at /internal/reference. Not linked in the public navigation.

Public Pages

NameFile Path / ValueNotes
Homepages/Home.jsx/Block-driven. Renders the PageBlock records of the 'home' Page in sort_order: a hero block, an announcement_marquee block, and three builtin_section blocks (News & Updates, This Week's Highlights, Sponsor Strip) whose JSX lives in this file as local components. Hero is matched by block_type, not list position. The featured events strip fetches all Events and sorts them with compareEventsByDateThenStart before taking the first four, so a later-starting event can never be picked over an earlier one on the same date. StripeBar is always on and not admin-managed. Falls back to the original hardcoded layout if the block query fails or the Page record is missing. Defines NewsUpdatesGrid. Reads Event, NewsArticle, Page, PageBlock.
Aboutpages/DynamicPage.jsx/aboutThere is no About.jsx. The route renders <DynamicPage pageKey="about" />, which loads the Page record with page_key 'about' and renders its PageBlock records. Editable in Site Pages.
Sponsorshippages/DynamicPage.jsx/sponsorshipThere is no Sponsorship.jsx. Renders <DynamicPage pageKey="sponsorship" />. Editable in Site Pages.
Dynamic Page (by key)pages/DynamicPage.jsx(via pageKey prop)Generic CMS page renderer. Takes a pageKey prop, loads the matching Page plus its PageBlock records, and renders them through BlockRenderer. Used by the two system pages above.
Dynamic Page (by slug)pages/DynamicPageBySlug.jsx/pages/:slugGeneric route for admin-created CMS pages. Resolves the slug via useParams and delegates rendering. Any page created in Site Pages that is not a system page lives here.
Eventspages/Events.jsx/eventsFull schedule. Reads the Event entity; day filter bar with EventTicketCard grids.
Event Detailspages/EventDetails.jsx/events/:idSingle event view. Reads Event and FeaturedArtist by route id. Shows 'Event not found.' when the id does not resolve.
Alumnipages/Alumni.jsx/alumniAlumni engagement page with a registration form that writes to the AlumniSubmission entity.
Newspages/News.jsx/newsNews index. Reads NewsArticle and NewsCategory with category filtering.
News Article Detailpages/NewsArticleDetail.jsx/news/:slugSingle article view, resolved by slug from the NewsArticle entity. Article content is ReactQuill HTML and is rendered through dangerouslySetInnerHTML, so it MUST pass through sanitizeRichText first. Never render article.content raw.
Why Partnerpages/WhyPartner.jsx/why-partnerSponsorship rationale page. Renders the FAQ accordion from the FaqCategory and Faq entities, managed in FAQ Admin.
Activation Opportunitiespages/ActivationOpportunities.jsx/activation-opportunitiesStatic sponsor activation content. No entity reads.
A La Cartepages/AlaCarte.jsx/ala-carteStatic a-la-carte sponsorship options. No entity reads.
Travel Detailspages/TravelDetails.jsx/travel-detailsStatic travel and lodging information, including estimated rates. No entity reads.
EndZone Experiencepages/EndZoneExperience.jsx/endzone-experienceStatic EndZone Experience promotional page. No entity reads.

Admin Pages

NameFile Path / ValueNotes
Admin Dashboardpages/AdminDashboard.jsx/adminLanding page for the admin area. Counts records from Event, AlumniSubmission and SponsorshipSubmission.
Events Adminpages/AdminEvents.jsx/admin/eventsCRUD for the Event entity plus nested FeaturedArtist records. Uploads images via Core.UploadFile.
Form Submissionspages/AdminSubmissions.jsx/admin/submissionsReads and edits submissions, and triggers the exportSubmissions backend function for CSV download.
News Adminpages/admin/NewsAdmin.jsx/admin/newsManages NewsArticle and NewsCategory records and the featured-article flag. Also writes User.application_role, so this page grants the news role. Gated by useHasPermission('manage_news').
News Article Formpages/admin/NewsArticleForm.jsx/admin/news/new · /admin/news/edit/:idCreate and edit form for NewsArticle. One component serving both routes, distinguished by useParams. Gated by useHasPermission('manage_news').
Page Settingspages/admin/PageSettings.jsx/admin/pagesPer-page PageSettings records: live / Coming Soon / Maintenance mode, coming-soon image and text, and the in-content sidebar toggle and heading.
Site Pagespages/admin/SitePages.jsx/admin/site-pagesLists Page records and creates new ones from a starter template, seeding PageBlock records from utils/pageTemplates.js. Deleting a page also deletes its blocks. System pages cannot be deleted.
Site Page Editorpages/admin/SitePageEditor.jsx/admin/site-pages/edit/:idThe Page Builder. Edits page metadata and the PageBlock list: add, edit, show/hide, reorder by sort_order swap, delete. The Edit button only renders for block types present in BLOCK_EDITOR_REGISTRY, so builtin_section blocks correctly show no editor. On the Home page it also surfaces the 'Restore built-in sections' panel.
Navigationpages/admin/NavigationAdmin.jsx/admin/navigationManages the NavItem entity for both the header and footer Quick Links. Add, edit, delete, show/hide, one level of sub-items, move up/down reordering, and an Unlinked Pages panel listing published Pages no menu reaches.
FAQ Adminpages/admin/FaqAdmin.jsx/admin/faqsTwo-tab CRUD for FaqCategory and Faq, with display ordering. Feeds the Why Partner FAQ section.

Internal & Fallback Pages

NameFile Path / ValueNotes
App Reference Guidepages/AppReferenceGuide.jsx/internal/referenceThis document. Outside the Layout and AdminLayout wrappers, so it has no navbar or admin chrome, and is not linked from any menu. Maintained by hand.
Page Not Foundlib/PageNotFound.jsx*Platform-provided 404 fallback, wired to the catch-all route in App.jsx.

Layout & Shared Components

NameFile Path / ValueNotes
Layoutcomponents/Layout.jsxWraps every public page via React Router Outlet. Renders Navbar and Footer and reads PageSettings to pass page mode down.
AdminLayoutcomponents/AdminLayout.jsxWraps every /admin route. Sidebar link list (Dashboard, Events, Form Submissions, Page Settings, Site Pages, Navigation, FAQs), login redirect, and an 'Access Denied' state. News links are conditional on useHasPermission('manage_news'). This is where admin-area access is gated.
Navbarcomponents/Navbar.jsxSticky maroon #860038 header. Links come from the NavItem entity (menu='header'), not hardcoded. One level of dropdowns: opens on click, and on hover only where matchMedia('(hover: hover) and (min-width: 768px)') matches. Escape closes and restores focus to the toggle; outside click and route change also dismiss. A parent that is both a link and a dropdown renders a Link plus a separate chevron button. Mobile drawer uses an accordion with a 56x48px toggle. Falls back to DEFAULT_HEADER_ITEMS if the query returns nothing or fails.
Footercomponents/Footer.jsxDark #111827 footer. Quick Links column is driven by NavItem (menu='footer') with indented sub-links and falls back to DEFAULT_FOOTER_ITEMS. Internal links use router Link. The two Homecoming 2026 promo links are intentionally hardcoded editorial content. The copyright line has a double-click-to-/admin shortcut.
StripeBarcomponents/StripeBar.jsxDecorative 6px repeating gold/maroon gradient, aria-hidden. Contains no text and is not admin-editable.
ComingSoonGatecomponents/ComingSoonGate.jsxWraps a page and swaps it for a Coming Soon or Maintenance screen based on that page's PageSettings.mode. Home is wrapped in it.
ContentWithSidebarcomponents/ContentWithSidebar.jsxTwo-column shell that conditionally shows the in-content sidebar, driven by PageSettings.show_sidebar via useOutletContext.
SidebarNavcomponents/SidebarNav.jsxIn-content sidebar links for a single page, from the SidebarLink entity filtered by page_key, with its heading from PageSettings.sidebar_heading. Unrelated to the NavItem site navigation.
EventCardcomponents/EventCard.jsxCompact event card used in the Home highlights grid.
EventTicketCardcomponents/EventTicketCard.jsxTicket-style event card used on the Events page. Props: dayAbbr, dateNum, month, eventName, time, location, badge, badgeVariant, flagship. Left stub is #860038, or #111827 when flagship.
ProtectedRoutecomponents/ProtectedRoute.jsxAuth guard component using useAuth. Present in the codebase; not currently referenced by any route in App.jsx.
UserNotRegisteredErrorcomponents/UserNotRegisteredError.jsxAccess Restricted screen shown by App.jsx when auth returns user_not_registered.
shadcn/ui primitivescomponents/ui/ (49 files)Standard shadcn/ui component library. Used selectively; most admin screens are styled with inline styles rather than these.

Content Blocks (public renderers)

NameFile Path / ValueNotes
BlockRenderercomponents/blocks/BlockRenderer.jsxMaps a PageBlock record to its renderer via REGISTRY, parses content, and returns null for hidden blocks or unknown types. builtin_section is deliberately absent from REGISTRY because Home.jsx renders those itself. Injects page_id and page_key into inquiry_form content.
blockDefaultscomponents/blocks/blockDefaults.jsShared FONT and BRAND tokens, the shared field style, and safeParseContent, which tolerates both JSON strings and objects and returns {} on malformed content. Also owns the hero size ceiling: HERO_MAX_RATIO_W/H (2033x774), HERO_MAX_RATIO, HERO_MAX_HEIGHT_PX, HERO_MAX_RATIO_LABEL, plus parseAspectRatio (accepts '1920/400', '16:9' or a bare number) and clampHeroAspectRatio, which returns { ratio, css, clamped }. Both HeroBlock and HeroBlockEditor read the ceiling from here, so change it in one place only.
videoSourcecomponents/blocks/videoSource.jsParses an admin-pasted video URL into something embeddable. parseVideoSource returns { kind: 'youtube' | 'vimeo', id, embedUrl } or { kind: 'file', src }, or null. Handles watch?v=, youtu.be, /embed/, /shorts/, /live/ and Vimeo; anything unrecognised falls through to 'file' so Razuna and CDN links with signed query strings still work, with the hero poster as the fallback if the load fails. YouTube goes through youtube-nocookie.com, muted, looped and chromeless. describeVideoSource supplies the detection hint shown in the editor.
HeroBlockcomponents/blocks/HeroBlock.jsxHero banner in three modes: solid colour, background image, or background video. Video mode needs content.media_type === 'video' plus a background_video_url that parses; background_image_url doubles as the poster. The banner box is hard-clamped to 2033x774 in BOTH media modes — never proportionally taller than that ratio, never more than 774px tall — so no source file can enlarge the hero. Media is cropped to fill: <video> uses object-fit cover, while an iframe (which ignores object-fit) is sized by hand from the nominal ratio and centred. Sizing from the nominal ratio is deliberate: the 774px cap only ever makes the real box shorter, so the computed size can over-crop but never letterbox. Includes a 44x44 pause/play control (WCAG 2.2 SC 2.2.2) that unmounts the player, which is what actually stops a YouTube iframe, and honours prefers-reduced-motion on first render so those visitors never see a frame of video.
AnnouncementMarqueeBlockcomponents/blocks/AnnouncementMarqueeBlock.jsxScrolling announcement band. Admin controls text and scroll speed; maroon ground and gold text are brand-locked. The repeat count and the translateX percentage both derive from one REPEATS constant so the seamless loop cannot break. Pauses on hover and keyboard focus, and does not animate under prefers-reduced-motion.
TextBlockcomponents/blocks/TextBlock.jsxHeading plus body copy section.
TextWithVisualBlockcomponents/blocks/TextWithVisualBlock.jsxSplit layout: copy beside an image or a large-text visual, with an optional reverse toggle.
MilestoneTimelineBlockcomponents/blocks/MilestoneTimelineBlock.jsxOrdered milestone timeline from a list in content.
LeadershipMessageBlockcomponents/blocks/LeadershipMessageBlock.jsxLetter-style message with a signature name and title.
TierGridBlockcomponents/blocks/TierGridBlock.jsxSponsorship tier or package grid.
CardGridBlockcomponents/blocks/CardGridBlock.jsxGeneric card grid.
CTABannerBlockcomponents/blocks/CTABannerBlock.jsxCall-to-action banner with heading and buttons.
InquiryFormBlockcomponents/blocks/InquiryFormBlock.jsxPublic inquiry form. Writes to SponsorshipSubmission or to the generic PageFormSubmission entity depending on configuration.

Page Builder (admin editors)

NameFile Path / ValueNotes
BlockEditorRegistrycomponents/admin/pageBuilder/BlockEditorRegistry.jsxSingle source of truth for BLOCK_TYPE_LABELS, ADDABLE_BLOCK_TYPES (labels minus builtin_section), BLOCK_EDITOR_REGISTRY (editor + defaultContent per type) and summarizeBlock. A type absent from BLOCK_EDITOR_REGISTRY renders no Edit button.
BlockEditorDrawercomponents/admin/pageBuilder/BlockEditorDrawer.jsxSlide-over shell hosting the per-type editor with save and cancel.
fieldEditorscomponents/admin/pageBuilder/fieldEditors.jsxShared form controls: TextField, TextAreaField, ColorField, CheckboxField, SelectField, ImageUploadField (Core.UploadFile), StringListField, AddButton, RemoveButton, SectionDivider.
Block editorscomponents/admin/pageBuilder/blockEditors/ (10 files)One editor per editable block type: Hero, AnnouncementMarquee, Text, TextWithVisual, MilestoneTimeline, LeadershipMessage, TierGrid, CardGrid, CTABanner, InquiryForm. Each exports its component plus a *_DEFAULT_CONTENT object. builtin_section has no editor by design. HeroBlockEditor is the only one that imports from components/blocks/: it reads the hero ceiling from blockDefaults and the URL detection hint from videoSource, so the warning it shows always matches what the renderer actually enforces.

Entities

NameFile Path / ValueNotes
Pageentities/Page.jsoncCMS pages. Fields: page_key, title, template enum(about|home|sponsorship), status enum(draft|published), is_system_page, hero_heading, hero_subheading. RLS: public read, admin write. System pages keep fixed URLs; others live at /pages/:slug. 'home' is in the template enum only so the pre-existing Home record validates; it is not offered at creation.
PageBlockentities/PageBlock.jsoncOrdered content blocks belonging to a Page. Fields: page_id, block_type enum(hero|text|text_with_visual|milestone_timeline|leadership_message|tier_grid|card_grid|cta_banner|inquiry_form|announcement_marquee|builtin_section), content (JSON string), sort_order, is_visible. RLS: public read, admin write.
NavItementities/NavItem.jsoncHeader and footer navigation. Fields: menu enum(header|footer), label, link_type enum(page|route|external|anchor|none), page_key, url, parent_id, sort_order, is_active, open_in_new_tab. RLS: public read, admin write. Only one level of nesting renders. Distinct from SidebarLink.
SidebarLinkentities/SidebarLink.jsoncIn-content sidebar links for one page. Fields: page_key, label, url, is_external, is_active, sort_order. RLS: public read, admin write. Not site navigation.
PageSettingsentities/PageSettings.jsoncPer-page display settings. Fields: page_key, mode, coming_soon_image_url, coming_soon_text, sidebar_heading, show_sidebar. RLS: public read, admin write.
Evententities/Event.jsoncFields: name, description, date, start_time, end_time, location, ticket_link, sort_order, image_url, category enum(Concert|Cultural|Athletic|Alumni|Worship Experience). RLS: public read (anonymous visitors read this on Events, Home and Event Details), admin create/update/delete.
FeaturedArtistentities/FeaturedArtist.jsoncPerformers attached to an Event. Fields: event_id, artist_name, artist_image_url, performance_time, sort_order. RLS: public read (Event Details reads it anonymously), admin create/update/delete.
NewsArticleentities/NewsArticle.jsoncFields: title, slug, content, excerpt, featured_image_url, published_date, status enum(draft|published), author_name, category, is_featured. RLS: public read, admin write.
NewsCategoryentities/NewsCategory.jsoncFields: name, sort_order, is_active. RLS: public read, admin write.
FaqCategoryentities/FaqCategory.jsoncFields: name, display_order, is_active. RLS: public read, admin write.
Faqentities/Faq.jsoncFields: question, answer, category_id, display_order, is_active. RLS: public read, admin write.
SponsorshipSubmissionentities/SponsorshipSubmission.jsoncSponsorship enquiries. Fields: org_name, contact_name, email, phone, tier, message, status enum(new|contacted|confirmed|declined). RLS: public create (the public inquiry form submits anonymously), admin read/update/delete. Contains contact PII, so read must stay admin-only.
AlumniSubmissionentities/AlumniSubmission.jsoncAlumni registrations. Fields: first_name, last_name, grad_year, degree, city, state, email, phone, updates, status enum(new|reviewed|archived). RLS: public create (the Alumni page form submits anonymously), admin read/update/delete. Contains contact PII, so read must stay admin-only.
PageFormSubmissionentities/PageFormSubmission.jsoncGeneric submissions from inquiry_form blocks. Fields: page_id, page_key, name, email, phone, message, extra_fields, status enum(new|reviewed|archived). RLS: public create, admin read/update/delete. read was public until 30 Jul 2026, which exposed every submission; do not set it back to true.
RolePermissionentities/RolePermission.jsoncFields: role_name, permission_key, is_granted. RLS: public read, admin write. Read by PermissionsContext. Only permission_key currently in use is manage_news.
Userentities/User.jsoncBuilt-in user entity, extended with role enum(admin|user) and application_role. Written by News Admin when granting roles. Declares no rls block: permissions are platform-managed and the Security Scan does not flag it. Left deliberately alone.

Backend Functions

NameFile Path / ValueNotes
exportSubmissionsbase44/functions/exportSubmissions/entry.tsDeno function returning a CSV of SponsorshipSubmission or AlumniSubmission records. Checks user.role === 'admin' and returns 403 before using asServiceRole, and whitelists the entity_name argument. Per the Base44 changelog of 22 Jun 2026, asServiceRole bypasses entity access rules entirely, so that in-function admin check is the ONLY thing protecting this data. Do not remove it. Invoked directly via base44.functions.invoke, so it has no function.jsonc and is not automation-triggered, which is correct for an HTTP-invoked function.

Helpers, Hooks & Contexts

NameFile Path / ValueNotes
Navigation helpersutils/navigation.jsbuildNavTree (flat NavItems to a two-level tree, dropping inactive items, grandchildren and dead links), resolveNavUrl and resolvePageUrl, SYSTEM_PAGE_URL_OVERRIDES (page_key 'home' resolves to '/'), findUnlinkedPages (matches on page_key AND resolved URL), isNodeActive, isExternalUrl, toFallbackNodes, the DEFAULT_HEADER_ITEMS and DEFAULT_FOOTER_ITEMS fallbacks, and the NAV_LINK_TYPES and BUILT_IN_ROUTES admin pickers.
Home section helpersutils/homeSections.jsCanonical list of the hardcoded Home sections (section_key and label), shared by Home.jsx which renders them and SitePageEditor which restores deleted ones via findMissingBuiltInSections. Adding an entry without a matching case in Home.jsx yields a block that renders nothing.
Event time helpersutils/eventTime.jsparseTimeToMinutes converts the free-text Event.start_time / end_time strings ('7:00 PM', '19:00', '7 PM') into minutes since midnight, returning null for 'TBA' and anything unparseable. compareEventsByStart orders events within a single day: parsed start time is the primary key, sort_order only breaks ties between events starting at the same time, and null-time events sort last. compareEventsByDateThenStart adds date as the primary key for lists not already grouped by day, with undated events last. Shared by Events.jsx (day grouping), Home.jsx (featured strip selection and order) and EventDetails.jsx (Google Calendar and .ics export), so the schedule, the homepage and the calendar files can never interpret a time differently. Note that sort_order no longer controls day ordering on its own.
HTML sanitiserutils/sanitizeHtml.jssanitizeRichText wraps DOMPurify with an allowlist matching the ReactQuill toolbar (headings, bold/italic/underline/strike, lists, links) and blocks javascript:, data: and vbscript: URLs. External links are forced to rel='noopener noreferrer'. Note that '#text' must stay in ALLOWED_TAGS: without it DOMPurify silently strips all text and articles render blank.
Page templatesutils/pageTemplates.jsTEMPLATES with two starter block sets (about, sponsorship) used when creating a page, plus slugify. Note that TEMPLATES has no 'home' entry even though the template enum allows it.
PermissionsContextcontexts/PermissionsContext.jsxResolves an effective role (Base44 admin, else User.data.application_role, else 'user'), treats admin as super-admin, and otherwise loads granted permission_keys from RolePermission.
useHasPermissionhooks/useHasPermission.jsReturns true for super-admins, false while permissions load, otherwise checks the permission set. Used for manage_news only.
use-mobilehooks/use-mobile.jsxViewport breakpoint hook supplied with the shadcn/ui setup.
AuthContextlib/AuthContext.jsxPlatform auth context: loading states, auth errors, and navigateToLogin. Consumed by App.jsx and ProtectedRoute.
Base44 clientapi/base44Client.jsPre-initialised Base44 SDK client. Entity access is base44.entities.X with list, filter, create, update(id, data) and delete(id).
Query clientlib/query-client.jsTanStack Query client instance. Provided at the App root, though most components fetch directly through the SDK in useEffect rather than through Query.
Other lib/utilslib/app-params.js · lib/utils.js · utils/index.tsPlatform and utility helpers, including the shadcn class-name helper.

Integrations Used

NameFile Path / ValueNotes
Core.UploadFilebase44.integrations.Core.UploadFileImage uploads. Called from fieldEditors (ImageUploadField), Events Admin, News Article Form and Page Settings. Consumes integration credits per upload.
Core.SendEmail(not in use)Not referenced anywhere in src/. Earlier revisions of this guide claimed the Sponsorship and Alumni forms send email; they write to entities instead. Form notifications are therefore not currently sent.

Images & Hosted Assets

NameFile Path / ValueNotes
Homecoming logomedia.base44.com/.../dee9db58c_HC26SOCIALSTAMPS-2.pngNavbar logo, max height 50px.
Hero fallback imagemedia.base44.com/.../b38c7ebc3_HC26.pngUsed only by the Home fallback layout. The live hero image is stored in the hero PageBlock content and is editable in the Page Builder.
Clawchella artworkmedia.base44.com/.../d34c0c950_HC26Clawchella.pngReferenced in page content.
EndZone Experience artworkmedia.base44.com/.../f2801771f_EndZoneExperience.pngReferenced in page content.
Uploaded block images(per-record URLs)Images added through the Page Builder, Events Admin or News are stored per record via Core.UploadFile and are not listed here.

Open Issues & Follow-Ups

NameFile Path / ValueNotes
Security posture(reviewed 30 Jul 2026)Security Scan clean as of 30 Jul 2026. Five Critical permission issues were closed by declaring explicit RLS on Event, FeaturedArtist, AlumniSubmission, SponsorshipSubmission and PageFormSubmission; the stored XSS was closed by sanitizeRichText; X-Frame-Options was enabled from the dashboard. Two rules of thumb: entity read must stay public for content the public site renders anonymously, and must stay admin-only for anything holding PII. Never use bulk 'Fix all issues' on this app, since a blanket lockdown of Event or FeaturedArtist read would take the public Events pages offline.
Permissions-Policy header(dashboard: Security)The scan also checks for Permissions-Policy, which limits access to camera, microphone and location. This app needs none of them, so enabling it costs nothing and reduces attack surface.
Marquee pause controlcomponents/blocks/AnnouncementMarqueeBlock.jsxMotion pauses on hover and keyboard focus and is disabled under prefers-reduced-motion. Strict WCAG 2.2 SC 2.2.2 also wants a visible pause control. HeroBlock now has one — a 44x44 gold-on-scrim toggle in the bottom-right corner — so copy that pattern here rather than inventing a second one.
Sponsor strip placeholderspages/Home.jsxThe Sponsor Strip renders five dashed 'Sponsor Name' placeholder tiles. Acceptable pre-launch; hide the section from the Page Builder or replace it with real logos before launch.
Section background alternationpages/Home.jsxBuilt-in sections alternate #F9FAFB and #ffffff. Reordering them can place two same-coloured sections next to each other, which flattens the visual separation. Cosmetic only.
TEMPLATES vs template enumutils/pageTemplates.jsPage.template allows 'home' but TEMPLATES defines only about and sponsorship. Intentional today, since 'home' exists only for the pre-existing record.
ProtectedRoute unusedcomponents/ProtectedRoute.jsxExists but is not referenced by any route. Admin gating happens in AdminLayout instead. Either wire it up or remove it.

B-CU Homecoming 2026 · Internal Reference · Not for public distribution