This repository contains the official website for Great Mills High School Mini-THON, built with Astro and deployed on Vercel. The site provides information about our club, events, fundraising, and resources for members and sponsors.
- Framework: Astro (static site generator with SSR support)
- Hosting: Vercel (via
@astrojs/verceladapter) - Content: JSON data files + MDX pages
- Runtime & Package Manager: Bun
src/
├── components/ # Reusable UI components
│ ├── Card.astro # Card display component
│ ├── EventCountdown.astro # Countdown timer for upcoming events
│ ├── EventMDXContent.astro # Renders MDX content for event pages
│ ├── ImageCarousel.astro # Image slideshow component
│ ├── MDXContent.astro # General MDX content renderer
│ ├── NavBehavior.astro # Navigation bar behavior/logic
│ └── SocialPopup.astro # Social media links popup
├── data/ # JSON data files (edit these to update site content)
│ ├── clubInfo.json # Officers, mission, meeting times, social media
│ ├── fundraising.json # Fundraising totals, goals, and history
│ ├── memberAnnouncements.json # Member portal announcements
│ ├── memberResources.json # Member resource links
│ ├── redirects.json # Short URL redirects (e.g., /redirect/donate)
│ └── sponsors.json # Sponsor names, tiers, logos, and websites
├── layouts/ # Page layout templates
│ ├── EventLayout.astro # Layout for individual event pages
│ ├── Layout.astro # Base layout
│ ├── MemberEventLayout.astro # Layout for member-facing event details
│ ├── MemberLayout.astro # Layout for member portal pages
│ └── PublicLayout.astro # Layout for public-facing pages
├── pages/ # Site pages (each file = a route)
│ ├── index.astro # Homepage
│ ├── about.astro # About the club
│ ├── events.astro # Events listing page
│ ├── events/ # Individual event pages (MDX files)
│ ├── fundraising.astro # Fundraising progress and history
│ ├── get-involved.astro # How to join or help
│ ├── sponsors.astro # Current sponsors display
│ ├── sponsorship.astro # Sponsorship info for potential sponsors
│ ├── members/ # Member-only portal pages
│ ├── api/ # API endpoints (e.g., newsletter)
│ ├── redirect/[slug].astro # Dynamic redirect handler
│ └── 404.astro # Custom 404 page
└── utils/ # Utility functions
├── announcements.ts # Announcement display helpers
├── auth.ts # Member authentication logic
└── events.ts # Event data processing
Key configuration files in the project root:
| File | Purpose |
|---|---|
astro.config.ts |
Astro framework configuration (Vercel adapter, MDX, analytics) |
package.json |
Dependencies and npm scripts |
bun.lock |
Locked dependency versions |
Most site content is managed through JSON files in src/data/. Edit these files and the site will automatically reflect your changes on the next deploy.
Events are individual MDX files in src/pages/events/. Each file becomes a page at /events/<filename>.
- Create a new
.mdxfile insrc/pages/events/(e.g.,bake-sale.mdx). - Use this template:
---
layout: ../../layouts/EventLayout.astro
title: Your Event Name
date: MM-DD-YYYY
location: Event Location
event_type: type
summary: A brief one-line description of the event.
---
Public-facing description of the event goes here. This is what everyone sees.
<Fragment slot="member-details">
**Event Lead:** Name
### Member notes
- Internal details only visible to logged-in members
</Fragment>Frontmatter fields:
layout— Always use../../layouts/EventLayout.astrotitle— Event name displayed as the headingdate— Event date inMM-DD-YYYYformatlocation— Where the event takes placeevent_type— Category of event (e.g.,race,game,fundraiser)summary— Short description shown on the events listing page
Content sections:
- The main body (outside
<Fragment>) is the public event description - Content inside
<Fragment slot="member-details">is only visible to logged-in members
Sponsors are stored in src/data/sponsors.json. Each sponsor has:
{
"name": "Sponsor Name",
"tier": "Gold",
"logo": "https://example.com/logo.png",
"website": "https://example.com"
}To add a new sponsor:
- Upload the sponsor's logo image using the upload script (
bun run upload) or add it to a hosted location. - Add a new entry to the
sponsorsarray insrc/data/sponsors.json. - Set the
tierto one of:Platinum,Gold,Silver, orBronze.
To remove a sponsor: Delete their entry from the JSON array.
Edit src/data/fundraising.json:
{
"currentYear": 2026,
"currentTotal": 3751,
"goalTotal": 13000,
"history": [{ "year": 2025, "total": 12474.54 }],
"donorDriveLink": "https://fourdiamonds.donordrive.com/GMHSMT"
}- Update
currentTotalas donations come in - Update
goalTotalat the start of each year - Add previous year totals to the
historyarray - Update
currentYearwhen a new fundraising cycle begins
Edit the officers array in src/data/clubInfo.json. Each officer entry has a role, name, and email field. Update these at the start of each school year when new officers are elected.
All in src/data/clubInfo.json:
mission— Club mission statement (array of paragraphs)about/aboutContinued— About page text (arrays of paragraphs)meetings— Meeting schedule withgeneralandboardfieldssocialMedia— Links to Instagram, GroupMe, Facebook, TikTok, etc.
Edit src/data/memberAnnouncements.json to add entries to the announcements array:
{
"title": "Announcement Title",
"descriptionMdx": "Announcement body text (supports MDX formatting)",
"type": "info",
"dateAdded": "YYYY-MM-DD",
"pin": false
}Set pin: true to keep an announcement at the top of the list.
Edit src/data/redirects.json to create short URLs. Each entry maps a slug to a destination:
{
"slug": "donate",
"label": "Donate",
"url": "https://fourdiamonds.donordrive.com/GMHSMT"
}This creates a redirect at /redirect/donate that points to the specified URL.
-
Clone the repository:
git clone https://github.com/Psavvas/gmhsminithon.org.git cd gmhsminithon.org -
Install dependencies:
bun install
-
Start the development server:
bun run dev
The site will be available at
http://localhost:4321. -
Make your changes (edit JSON files, add pages/components, update assets).
bun run build # Build for production
bun run preview # Preview the production build locallyPUBLIC_SHOO_BASE_URL(optional): overrides the Shoo base URL. Defaults tohttps://shoo.dev.MEMBER_APPROVED_SHOO_SUBS(required for member access): a comma-separated or newline-separated list of approved Shoopairwise_subvalues.MEMBER_APPROVED_SHOO_SUBS_GOOGLE_SHEET_CSV_URL(optional): an HTTPSdocs.google.comCSV export URL for a Google Sheet whose first column contains approved Shoopairwise_subvalues. The server combines this with any IDs inMEMBER_APPROVED_SHOO_SUBS.MEMBER_AUTH_DEBUG(optional): set totrueto enable detailed server-side auth logging for member session verification, approval cache refreshes, and member auth API decisions.MEMBER_SESSION_SECRET(recommended for production): a long random secret used to issue a server-signed member session after approval. This avoids re-checking the Google Sheet on every protected page request and is the recommended setup for Bun on Vercel.
For Vercel deployments, add MEMBER_APPROVED_SHOO_SUBS in the project's
Environment Variables settings for each environment that needs member login
(Production / Preview / Development as applicable), then redeploy so the server
functions pick it up.
Quick setup on Vercel:
- In Shoo, make sure your app's callback/return URL points to
/members/auth/callbackon the deployed site. - In Vercel, open the project's Settings → Environment Variables.
- Add
MEMBER_APPROVED_SHOO_SUBSwith any starter Shoo user IDs you want to allow immediately. - If you want to manage approvals in Google Sheets, add
MEMBER_APPROVED_SHOO_SUBS_GOOGLE_SHEET_CSV_URLwith the sheet's CSV export URL. - Add
MEMBER_SESSION_SECRETwith a long random value for stable production member sessions. - Redeploy the site so the server picks up the new values.
- Visit
/members/loginand sign in once to confirm the setup. If a user is not approved yet, the page will show that user's Shoo user ID so it can be added to the env var list or Google Sheet.
Example:
MEMBER_APPROVED_SHOO_SUBS="sub_123,sub_456"Google Sheet example:
MEMBER_APPROVED_SHOO_SUBS_GOOGLE_SHEET_CSV_URL="https://docs.google.com/spreadsheets/d/.../export?format=csv"Notes for the Google Sheet allowlist:
- Publish or share the sheet so the server can read the CSV export URL.
- Put one Shoo
pairwise_subvalue per row in the first column. - An optional header such as
pairwise_suboruser idis allowed in the first row. - If you also keep some IDs in
MEMBER_APPROVED_SHOO_SUBS, the server merges both sources automatically. - The server only accepts HTTPS
docs.google.comCSV export URLs, caches the result briefly, and fails closed if the sheet cannot be loaded.
If a user can authenticate with Shoo but is not approved yet, the members login flow will show that user's Shoo user ID so an admin can add it privately to the approved member list without committing personal data to the repository. If the environment variable is missing on a deployment, users can still try to sign in and the login flow will explain that member access is not configured yet after Shoo authentication.
The site is automatically deployed to Vercel when changes are pushed to the main branch. No manual deployment steps are needed.
- All site content is managed via JSON files and MDX pages — no database is required.
- Static assets (images, logos) can be placed in the
src/assets/directory or uploaded externally. - The member portal (
/members/) uses Shoo authentication plus a private server-side allowlist stored inMEMBER_APPROVED_SHOO_SUBSand/or an optional Google Sheet CSV configured withMEMBER_APPROVED_SHOO_SUBS_GOOGLE_SHEET_CSV_URL. Use Shoopairwise_subvalues rather than committed email addresses. - For questions or help, contact the current club officers (see
src/data/clubInfo.json).
For more information about Astro, see Astro Documentation.