The Perth Machine Learning Group (PMLG) website has been fully redesigned, rebuilt, and rehosted as a high-performance, maintenance-free static web application hosted on GitHub Pages with a custom domain (pmlg.com.au) 1. This capstone document captures the complete architecture, technical stack, file organization, data flows, automated event lifecycle, testing methodology, and operational runbooks required to maintain and evolve the platform without prior context.
The platform is engineered as a client-side rendered single-page application (SPA) backed by lightweight JSON data stores and automated GitHub Actions. By eliminating backend server infrastructure, server-side database maintenance, and recurring hosting fees, the site achieves zero operational cost while maintaining strict security and privacy standards.
- Client-Side Runtime Merging: The application combines curated static events, a 379-record historical archive (
events-archive.json), and a runtime upcoming events feed (upcoming-events.json) directly in the browser. - Automated Event Lifecycle: Events are managed via JSON files. Completed events automatically transition from upcoming listings to the historical archive based on date comparison, with a daily GitHub Action automating data migration.
- Privacy-First Communication: All public email addresses and
mailto:links have been removed. RSVP, contact, and community engagement actions route directly through the official PMLG LinkedIn organisation page. - Asymmetric Visual Design: Built with a dark navy background, technical grid lines, diagonal-plane header motifs inspired by the PMLG logo, and a restrained typographic hierarchy using Space Grotesk, DM Sans, and IBM Plex Mono.
| Layer | Technology | Version | Purpose |
|---|---|---|---|
| UI Framework | React | 19.x | Component-based user interface and state management |
| Routing | Wouter | 3.7.x | Lightweight, hook-based client-side router |
| Styling | Tailwind CSS | 4.x | Utility-first styling with custom OKLCH design tokens |
| Icons & UI Primitives | Lucide React & Radix UI | Latest | Accessible modal, accordion, and iconography primitives |
| Build Tool | Vite | 7.x | Ultra-fast bundling, HMR, and production optimization |
| Testing | Vitest & React Testing Library | 2.1.x | Robust component, routing, and archive unit testing |
| Automation & Data | Python 3 | 3.11 | Data extraction, MHTML parsing, archive validation, and cron tasks |
| Hosting | GitHub Pages | N/A | Cost-free static hosting with custom domain support |
The repository is structured as a root-deployable project where static assets, automation workflows, maintenance scripts, and data feeds reside directly at the root.
PMLG-Events.github.io/
├── .github/
│ └── workflows/
│ └── archive-past-events.yml # Daily GitHub Action (8:00 PM AWST)
├── scripts/
│ ├── archive_past_events.py # Automated past event migrator
│ ├── build_github_pages_bundle.py # Production bundle assembler
│ ├── import_meetup_mhtml.py # MHTML historical parser
│ ├── patch_project_site.py # Base-path patching utility
│ ├── validate_github_pages_bundle.py # Deployment readiness validator
│ └── validate_upcoming_events.py # Upcoming feed schema checker
├── client/
│ ├── index.html # HTML entry point with Google Fonts
│ └── src/
│ ├── App.tsx # Root router and layout
│ ├── main.tsx # React DOM mount point
│ ├── index.css # Global Tailwind tokens and base styles
│ ├── components/ # Reusable UI primitives (Card, Badge, etc.)
│ ├── contexts/ # Theme and state providers
│ ├── hooks/ # Custom responsive and UI hooks
│ ├── lib/ # Core logic (events normalization, loading, merging)
│ ├── pages/ # Page components (Home, Events, About, Impact, Values, NotFound)
│ └── test/ # Vitest unit test suites
├── dist/ # Vite production build output
├── github-pages/ # Assembled static deployment folder
├── github-pages.zip # Standalone static deployment ZIP
├── pmlg-root-repo.zip # Complete root-deployable repository package
├── upcoming-events.json # Runtime feed for active upcoming events
├── upcoming-event-template.json # Template for organisers adding new events
├── events-archive.json # Permanent archive of historical records (379+ events)
├── ARCHIVE_MANIFEST.json # Archive metadata and build fingerprint
├── README_DEPLOY.md # Quick deployment guide
└── GITHUB_PAGES_SETUP.md # Comprehensive setup documentation
Every event record across curated lists, archives, and runtime feeds adheres to a unified TypeScript interface (client/src/lib/events.ts):
export interface PmlgEvent {
id: string; // Unique slug or Meetup ID
title: string; // Event title
date: string; // ISO date (YYYY-MM-DD)
time?: string; // Start time (e.g., "6:00 PM")
endTime?: string; // Optional end time
timezone?: string; // Timezone (e.g., "AWST")
location?: string; // Venue address or online link
description: string; // Full editorial description and agenda
attendees?: number; // Recorded attendee count
organisers?: string[]; // Organiser names
status: "upcoming" | "past";// Dynamic status
topics?: string[]; // Subject tags
url?: string; // Original external link (e.g., Meetup)
sourceFile?: string; // Archival provenance tag
slidesUrl?: string; // Link to presentation slides
paperUrl?: string; // Link to research paper or publication
repoUrl?: string; // Link to GitHub code repository
resourceLabel?: string; // Custom resource button label
}At runtime, the browser compares each event's date string against the current date (client/src/lib/events.ts):
- Upcoming:
date >= todayIso - Past:
date < todayIso
This ensures that the moment an event's date passes, it automatically shifts from the Upcoming view to the Past archive without requiring manual status edits.
To keep management effortless, a scheduled GitHub Action (.github/workflows/archive-past-events.yml) runs daily at 12:00 UTC (8:00 PM AWST Perth time).
- It executes
scripts/archive_past_events.py. - It scans
upcoming-events.jsonfor any events whose dates are earlier than today. - It automatically appends those events to
events-archive.json, sorts the archive in descending order by date, removes them fromupcoming-events.json, validates the remaining schedule usingvalidate_upcoming_events.py, and commits the updated JSON files back to the repository.
The codebase includes a robust test suite powered by Vitest and React Testing Library (client/src/test/), covering 43 distinct unit tests across 5 test files:
events-lib.test.ts: Validates record deduplication, multi-field search filtering, year extraction, statistics calculation, and date-based status evaluation.mhtml-importer.test.ts: Verifies MHTML regex parsing, header cleaning, and structured event extraction.routing.test.tsx: Tests client-side navigation across Home, Events, Impact, About, and Values.components.test.tsx: Asserts UI rendering, responsive card layouts, metric accuracy, and modal disclosures.events.test.tsx: Tests archive tab switching, year dropdown filtering, search input response, and LinkedIn RSVP button presence.
cd /home/ubuntu/pmlg-website
pnpm test:run
pnpm check # TypeScript check- Copy
upcoming-event-template.json. - Populate the fields with the new event details, ensuring a unique
idandYYYY-MM-DDdate format. - Add the event object into the JSON array inside
upcoming-events.json. - Validate the feed:
python3 scripts/validate_upcoming_events.py
- Commit and push
upcoming-events.jsonto the GitHub repository. The site will reflect the new event immediately at runtime.
If organisers wish to trigger the past-event archive migration immediately rather than waiting for the 8:00 PM AWST schedule:
- Navigate to the GitHub repository.
- Click the Actions tab.
- Select Archive Past Events from the left sidebar.
- Click Run workflow → Run workflow.
- Ensure the repository publishing source is set to Deploy from a branch (
mainorgh-pages) with folder set to/ (root). - Ensure the custom domain
pmlg.com.auis configured in repository settings. - Push updates to the default branch; GitHub Pages automatically builds and deploys the static site.
Hosting and platform operational policies are maintained externally as referenced in support resources 2.