Skip to content

Instantly share code, notes, and snippets.

@darronj
Created December 9, 2025 15:44
Show Gist options
  • Select an option

  • Save darronj/3006e1bdd466a50d1629e20677bdfb12 to your computer and use it in GitHub Desktop.

Select an option

Save darronj/3006e1bdd466a50d1629e20677bdfb12 to your computer and use it in GitHub Desktop.
FlyBirmingham.com Development Plan - Alloy Catalyst Migration

FlyBirmingham.com Website Development

Project Overview

This document outlines the development plan for building the FlyBirmingham.com website using modern web technologies. The project migrates the existing WordPress site to a new platform with enhanced functionality, improved performance, and easier content management.


What We're Building

Core Airport Features

  • Flight Search & Schedules - Real-time arrivals and departures with search functionality
  • Parking Information - Clear display of parking rates and options
  • Airlines & Destinations - Airline listings with route information and booking links
  • Interactive Terminal Map - Searchable map with dining, shopping, and gate locations

Content & Display

  • Enhanced Homepage - Hero with quick navigation, news carousel, destination highlights
  • Venue Pages - Restaurant and shop detail pages with hours, menus, and locations
  • Document Centers - Downloadable reports, meeting minutes, and public documents
  • Event Listings - Schedules for airport events and programs
  • Media Galleries - Photo and video displays for art, community programs, and testimonials

Content Migration

  • All existing page content migrated from WordPress
  • Images and documents transferred to new system
  • Content structure preserved and enhanced

Phased Delivery Plan

Phase 1: Foundation

What gets done:

  • Content structure setup (airlines, venues, parking rates, announcements)
  • Flight data API research and configuration
  • Development environment ready

Milestone: Content editors can begin entering airline and venue data

Effort Range: 12-18 hours


Phase 2: Core Airport Functionality

What gets done:

  • Homepage flight search widget
  • Arrivals and Departures pages with live data
  • Parking rates display
  • Airlines & Destinations page with route map

Milestone: Core airport pages functional with real or test data

Effort Range: 20-32 hours

Note: Flight search and schedule components are ported from existing implementation, reducing development time.


Phase 3: Content & Display Modules

What gets done:

  • Enhanced homepage hero with quick navigation icons
  • News and announcements carousel
  • Destination showcase
  • Restaurant and shop detail pages
  • Flexible card layouts for various content sections

Milestone: Homepage fully buildable, venue pages complete

Effort Range: 18-28 hours


Phase 4: Supporting Modules

What gets done:

  • Document download sections (Annual Reports, Budget, Contracts)
  • Airport history timeline
  • Photo galleries (Living Wall, Therapy Dogs)
  • Board meeting schedules with agendas and minutes
  • Event pages (Holiday Music Series)
  • Video testimonial displays (Economic Impact)

Milestone: All document and media pages functional

Effort Range: 16-26 hours


Phase 5: Complex Features & Enhancements

What gets done:

  • Interactive terminal map with search and filtering
  • Enhanced team/staff pages with LinkedIn and expandable bios
  • Improved news listing with featured articles and category filters
  • Contact forms with contact information sidebar
  • Image carousels for venue and content pages

Milestone: All features complete and polished

Effort Range: 14-22 hours

Note: Terminal map is ported from existing implementation.


Phase 6: Content Migration & Launch Prep

What gets done:

  • All WordPress content migrated to new system
  • Images and documents transferred
  • Content validation and cleanup
  • Final testing across all pages

Milestone: Site ready for launch with all content in place

Effort Range: 12-20 hours

Client involvement: Content review and approval of migrated content


Effort Summary

Phase Description Effort Range
1 Foundation 12-18h
2 Core Airport Functionality 20-32h
3 Content & Display Modules 18-28h
4 Supporting Modules 16-26h
5 Complex Features & Enhancements 14-22h
6 Content Migration & Launch Prep 12-20h
Base Total 92-146h
Buffer (15%) 14-22h
Project Total 106-168h

Realistic estimate: ~140 hours

The tighter range reflects leveraging existing implementations from the current Gatsby site for flight search, flight schedules, and the terminal map. These components are ported and adapted rather than built from scratch.


What We Need From You

Before Development Starts

  • WordPress API access (read-only credentials)
  • Flight data API provider identified
  • Flight API credentials and documentation

During Phase 2

  • Flight API access confirmed and working

During Phase 6

  • Content review and approval of migrated pages
  • Final sign-off before launch

Key Dependencies

Dependency Impacts When Needed
WordPress API access Content migration Before Phase 6
Asset files (logos, images) Various pages Throughout

Risk Factors

Items That Could Affect Timeline

  1. Gatsby Porting Complexity - Some components may have deeper Gatsby/WordPress dependencies than expected. Mitigation: Most React code is portable; we'll isolate platform-specific logic early.

  2. Content Volume - If content is more extensive than estimated, migration may take longer. Mitigation: Prioritize critical pages; AI-assisted migration accelerates the process.

  3. WordPress API Access - If API access is problematic, manual content migration is possible but slower. Mitigation: Request credentials early in Phase 1.

Why the Estimate Range is Tighter

The 106-168 hour range (vs. a wider spread) reflects:

  • Existing flight search, schedule, and terminal map implementations being ported rather than built new
  • Proven component patterns from the current site
  • AI-assisted development and content migration tools

Technology Benefits

The new platform provides:

  • Faster Performance - Modern architecture for quick page loads
  • Easier Content Editing - Visual editing interface for content updates
  • Real-time Preview - See changes before publishing
  • Mobile Responsive - Optimized for all device sizes
  • Accessibility Compliant - Built with accessibility standards in mind
  • SEO Optimized - Structured for search engine visibility

Next Steps

  1. Review and approve this project plan
  2. Provide WordPress API credentials
  3. Identify and provide flight data API information
  4. Schedule kickoff to begin Phase 1

Document prepared for: FlyBirmingham / Birmingham Airport Authority

Prepared by: Alloy Digital

FlyBirmingham Sanity Migration - Development Plan

Executive Summary

This document outlines the technical implementation plan for building the FlyBirmingham.com website using the Alloy Catalyst framework (Next.js 16 + Sanity CMS + Tailwind 4). The project involves creating 15 new modules, enhancing 6 existing modules, defining 6 new document types, and migrating content from the existing WordPress headless CMS.

Estimated Effort: 106-168 hours (realistic: ~140 hours with buffer)

Key Success Factors:

  • AI-assisted development (Claude Code) for productivity gains
  • Existing Gatsby implementations for terminal map and flight API (port to Next.js)
  • Sanity MCP for streamlined content migration
  • Phased delivery with clear milestones

Prerequisites & Setup

Before starting development:

  1. Environment Setup

    • Clone Alloy Catalyst repository
    • Configure .env.local with Sanity credentials
    • Verify npm run dev starts successfully
    • Run npm run generate-types to confirm type generation works
  2. Access Requirements

    • WordPress API credentials (read access)
    • Flight data API provider identified
    • Flight API credentials/documentation
    • Sanity project access (editor role minimum)
  3. Reference Materials

    • Module Analysis Document
    • Existing module examples in src/sanity/schemaTypes/modules/
    • Query patterns in src/sanity/lib/queries.ts

Phase 1: Foundation

Milestone: All document types created, infrastructure ready, flight API identified

Effort Range: 12-18 hours

Document Types to Create

Create in src/sanity/schemaTypes/documents/:

1. airline.ts

// Fields: logo, name, bookingUrl, destinations (reference array)
// Preview: Show logo + name
// Reference: destination document type

2. destination.ts

// Fields: code (3-letter), city, seasonal (boolean), guideUrl
// Preview: Show code + city

3. venue.ts

// Fields: name, logo, heroImage, description (richtext),
//         menuPdf (file), hours (array of {days, times}), location
// Preview: Show logo + name
// Note: Create reusable 'hours' object type

4. art-installation.ts

// Fields: image, title, locationRef (reference to venue or map location)
// Preview: Show image + title

5. announcement.ts

// Fields: image, backgroundImage, title, cta (link),
//         startDate, endDate (for scheduling)
// Preview: Show title + date range

6. parking-rate.ts

// Fields: name, hourlyRate, dailyRate, description (richtext), heightLimit
// Preview: Show name + daily rate

Infrastructure Tasks

  1. Register Document Types

    • Update src/sanity/schemaTypes/index.ts
    • Add to appropriate exports array
  2. Studio Structure

    • Update src/sanity/structure.tsx
    • Create "FlyBirmingham" section with:
      • Airlines & Destinations
      • Venues (Dining & Shopping)
      • Announcements
      • Parking Rates
      • Art Installations
  3. Shared Object Types

    • Create src/sanity/schemaTypes/objects/hours.ts for venue hours
    • Register in objects index
  4. Type Generation

    • Run npm run generate-types
    • Verify no TypeScript errors
  5. Flight API Research

    • Document API endpoints
    • Create client stub in src/lib/flightApi.ts
    • Define TypeScript interfaces for flight data

Patterns to Follow

Document Type Pattern:

// Reference: src/sanity/schemaTypes/documents/person.ts
import { defineField, defineType } from 'sanity'

export default defineType({
  name: 'airline',
  title: 'Airline',
  type: 'document',
  icon: PlaneIcon,
  fields: [
    defineField({
      name: 'name',
      type: 'string',
      validation: (rule) => rule.required(),
    }),
    // ... more fields
  ],
  preview: {
    select: { title: 'name', media: 'logo' },
  },
})

Phase 2: Core Airport Functionality

Milestone: Flight search, flight schedule, parking rates, and airlines/destinations modules functional

Effort Range: 20-32 hours

Note: Flight search, flight schedule, and related API integration are ported from existing Gatsby implementation. Work involves adapting React components to Next.js patterns and swapping WordPress data sources for Sanity.

Module Build Order

Build in this order to validate patterns with simpler modules first:

1. parking-rates (3-6.5h)

Schema: src/sanity/schemaTypes/modules/parking-rates.ts

// Fields:
// - title (string)
// - helpText (string) - "Need help? Call..."
// - rates (array of references to parking-rate documents)
//   OR inline array with name, hourlyRate, dailyRate, description, heightLimit

Query Addition: src/sanity/lib/queries.ts

_type == "parking-rates" => {
  ...,
  rates[]-> {
    name, hourlyRate, dailyRate, description, heightLimit
  }
}

Component: src/ui/modules/ParkingRates.tsx

  • Responsive card/table layout
  • Rate comparison styling
  • Height limit badges

2. flight-search (3-5h) — Ported from Gatsby

Schema: src/sanity/schemaTypes/modules/flight-search.ts

// Fields:
// - title (string)
// - defaultTab (arrivals | departures)
// - placeholder (string) - search input placeholder
// - arrivalsPageLink (reference to page)
// - departuresPageLink (reference to page)

Component: src/ui/modules/FlightSearch.tsx

  • Tab toggle (Arrivals/Departures)
  • Autocomplete search input
  • Debounced API calls
  • Results dropdown
  • "View All" link to full schedule

API Integration:

// src/lib/flightApi.ts
export async function searchFlights(query: string, type: 'arrivals' | 'departures') {
  // Implementation depends on API provider
}

3. flight-schedule (4-7h) — Ported from Gatsby

Schema: src/sanity/schemaTypes/modules/flight-schedule.ts

// Fields:
// - title (string)
// - type (arrivals | departures)
// - autoRefresh (boolean)
// - refreshInterval (number) - seconds
// - columns (array) - configurable visible columns

Component: src/ui/modules/FlightSchedule.tsx

  • Date grouping (Today, Tomorrow, etc.)
  • Sortable/filterable table
  • Status badges (On Time, Delayed, Cancelled, Boarding, Landed)
  • Airline logo display
  • Auto-refresh with countdown indicator
  • Loading states

Subcomponents:

  • FlightRow.tsx - individual flight display
  • FlightStatusBadge.tsx - status styling
  • useFlightData.ts - hook for API + refresh logic

4. airlines-destinations (4-6h)

Schema: src/sanity/schemaTypes/modules/airlines-destinations.ts

// Fields:
// - intro (richtext)
// - routeMapImage (image)
// - airlines (array of references to airline documents)

Query Addition:

_type == "airlines-destinations" => {
  ...,
  intro,
  routeMapImage,
  airlines[]-> {
    name, logo, bookingUrl,
    destinations[]-> { code, city, seasonal }
  }
}

Component: src/ui/modules/AirlinesDestinations.tsx

  • Intro text section
  • Route map image (zoomable?)
  • Airline sections with:
    • Logo
    • "Book Now" button
    • Destination pills (with seasonal badge)

Shared Infrastructure

Flight API Client: src/lib/flightApi.ts — Port from existing Gatsby implementation (2-3h)

interface Flight {
  flightNumber: string
  airline: { code: string; name: string; logo?: string }
  origin?: { code: string; city: string }
  destination?: { code: string; city: string }
  scheduledTime: string
  estimatedTime?: string
  status: 'scheduled' | 'boarding' | 'departed' | 'arrived' | 'delayed' | 'cancelled'
  gate?: string
  terminal?: string
}

interface FlightApiClient {
  getArrivals(date?: Date): Promise<Flight[]>
  getDepartures(date?: Date): Promise<Flight[]>
  searchFlights(query: string, type: 'arrivals' | 'departures'): Promise<Flight[]>
}

Module Registry Updates:

  • Add all 4 modules to src/ui/modules/index.tsx
  • Add to MODULES_QUERY in queries.ts

Phase 3: Content & Display Modules

Milestone: Homepage fully buildable, venue pages functional, card-list variants available

Effort Range: 18-28 hours

New Modules

1. hero-with-quicklinks (2-4h)

Schema: Extends hero pattern

// Additional fields beyond standard hero:
// - quickLinks (array)
//   - icon (image or icon picker)
//   - label (string)
//   - link (reference or url)

Component: Reuse existing hero logic, add quick links row

  • Horizontal icon bar below CTA
  • Responsive: scroll on mobile, grid on desktop

2. announcements-carousel (3-5h)

Schema:

// Fields:
// - title (string) - "News | Announcements | Events"
// - items (array)
//   - image (image)
//   - backgroundImage (image) - optional overlay background
//   - title (string)
//   - cta (link)

Component:

  • Slider with dot/arrow navigation
  • Image with text overlay
  • Auto-advance with pause on hover
  • Consider using Embla Carousel

3. destination-carousel (2-4h)

Schema:

// Fields:
// - title (string)
// - destinations (array of references to destination)
//   OR inline with image, city, guideUrl

Component:

  • Horizontal scroll with snap
  • City card with image, name overlay
  • "City Guide" link
  • Touch-friendly swipe

4. venue-detail (4-6h)

Note: Consider implementing as page template rather than module

Schema:

// Fields:
// - venue (reference to venue document)
// OR used directly on venue document type

Component:

  • Back button navigation
  • Hero image with logo overlay
  • Description richtext
  • Hours table (formatted nicely)
  • "View Menu" PDF link
  • Location with map embed
  • Link to terminal map location

Module Enhancement: card-list Variants

Schema Update: Add variant field

defineField({
  name: 'variant',
  title: 'Card Style',
  type: 'string',
  options: {
    list: [
      { title: 'Default', value: 'default' },
      { title: 'Hover Overlay', value: 'hover-overlay' },
      { title: 'Icon Card', value: 'icon-card' },
      { title: 'Venue', value: 'venue' },
      { title: 'Transportation', value: 'transportation' },
      { title: 'Department', value: 'department' },
      { title: 'Art Exhibit', value: 'art-exhibit' },
      { title: 'Service Provider', value: 'service-provider' },
      { title: 'Feature Icon', value: 'feature-icon' },
    ],
  },
})

Component Updates:

  • Add variant prop to CardList
  • Create variant-specific card components or styles
  • Each variant may need additional fields (icon, phone, website, etc.)

Variant Specifications:

Variant Layout Key Features
hover-overlay Image with hover text Title appears on hover
icon-card Icon + title + description Large icon above text
venue Logo + name + description Restaurant/shop style
transportation Icon + title + link Service category cards
department Background image + icon Career department cards
art-exhibit Image + title + location Gallery style
service-provider Logo + description + contact Rental car/taxi style
feature-icon Icon + title + description Business services

Phase 4: Supporting Modules

Milestone: All content-focused modules complete, PDF downloads and schedules functional

Effort Range: 16-26 hours

Modules to Build

1. pdf-download-list (1.5-2.5h)

Schema:

// Fields:
// - title (string)
// - description (richtext) - optional intro
// - items (array)
//   - title (string)
//   - file (file) - PDF upload
//   - date (date) - optional publish date

Component:

  • Simple list layout
  • PDF icon
  • File size display (computed)
  • Date formatting

2. timeline (2-4h)

Schema:

// Fields:
// - title (string)
// - items (array)
//   - date (string) - flexible format "1931", "March 1965"
//   - title (string)
//   - description (richtext)
//   - image (image) - optional

Component:

  • Vertical timeline with line connector
  • Date badges
  • Alternating left/right layout (desktop)
  • Single column (mobile)

3. photo-grid (2.5-4h)

Schema:

// Fields:
// - title (string)
// - description (richtext)
// - columns (number) - 2, 3, or 4
// - photos (array)
//   - image (image)
//   - caption (string)
//   - description (richtext) - optional extended text

Component:

  • Responsive grid
  • Lightbox modal on click
  • Caption overlay or below
  • Lazy loading for performance

4. meeting-schedule (3-5h)

Schema:

// Fields:
// - title (string)
// - description (richtext)
// - years (array)
//   - year (string) - "2024"
//   - meetings (array)
//     - month (string)
//     - date (date)
//     - agendaFile (file)
//     - minutesFile (file)
//     - status (scheduled | cancelled | no-meeting)

Component:

  • Collapsible year sections (accordion)
  • Table per year with columns: Month, Date, Agenda, Minutes
  • PDF download icons
  • Status indicators (cancelled styling)

5. event-schedule (3-5h)

Schema:

// Fields:
// - title (string)
// - heroImage (image)
// - description (richtext)
// - events (array)
//   - date (date)
//   - title (string) - optional event name
//   - performers (array)
//     - name (string)
//     - bio (richtext)
//     - contactEmail (string)
//     - contactPhone (string)
//     - website (url)

Component:

  • Date-grouped sections
  • Performer cards with expandable bios
  • Contact information display
  • Hero image header

6. video-grid (3-5h)

Schema:

// Fields:
// - title (string)
// - description (richtext)
// - videos (array)
//   - videoUrl (url) - YouTube/Vimeo
//   - thumbnail (image) - optional, auto-fetch if not provided
//   - speakerName (string)
//   - speakerTitle (string)

Component:

  • Responsive grid layout
  • Video thumbnail with play button overlay
  • Speaker name/title overlay
  • Modal video player on click
  • Support YouTube and Vimeo embed URLs

Video Embed Utility:

// src/lib/videoEmbed.ts
export function getEmbedUrl(url: string): string | null {
  // Parse YouTube: youtube.com/watch?v=xxx, youtu.be/xxx
  // Parse Vimeo: vimeo.com/xxx
  // Return embed URL
}

export function getThumbnail(url: string): string | null {
  // Return thumbnail URL for YouTube/Vimeo
}

Phase 5: Complex Features & Remaining Enhancements

Milestone: Terminal map functional, all module enhancements complete

Effort Range: 14-22 hours

Complex Module: terminal-map (4-6h) — Ported from Gatsby

Note: Existing implementation in Gatsby site. Work involves porting React components to Next.js and replacing WordPress rich text blocks with Sanity Portable Text.

Schema:

// Fields:
// - title (string)
// - levels (array)
//   - name (string) - "Level 1", "Level 2"
//   - mapImage (image)
//   - locations (array)
//     - name (string)
//     - category (dining | shopping | services | gates)
//     - coordinates (object) - { x: number, y: number } as percentage
//     - venue (reference to venue) - optional link

Component:

  • Level switcher tabs/dropdown
  • Map image with positioned markers
  • Search/filter by category
  • Click marker for popup with venue info
  • "Get Directions" link (if SDK supports)

Module Enhancements

1. person-list (1.5-3h)

Schema Updates:

// Add to person document or module:
defineField({
  name: 'linkedinUrl',
  title: 'LinkedIn URL',
  type: 'url',
}),
defineField({
  name: 'extendedBio',
  title: 'Extended Bio',
  type: 'richtext', // For modal display
}),

Component Updates:

  • LinkedIn icon/link on person card
  • "Read More" trigger for modal
  • Modal with full bio content

2. step-list (0.5-1h)

Verify/Update:

  • Ensure icon field exists in schema
  • Icon rendering in component (use Icon component)
  • Numbered vs icon variant toggle

3. blog-list (2-4h)

Schema Updates:

defineField({
  name: 'showFeatured',
  title: 'Show Featured Article',
  type: 'boolean',
}),
defineField({
  name: 'featuredPost',
  title: 'Featured Post',
  type: 'reference',
  to: [{ type: 'blog.post' }],
  hidden: ({ parent }) => !parent?.showFeatured,
}),
defineField({
  name: 'enableFilters',
  title: 'Enable Category Filters',
  type: 'boolean',
}),

Component Updates:

  • Featured article layout (large card)
  • Category filter buttons
  • Client-side filtering or URL params

4. form-module (3-5h)

Schema Updates:

defineField({
  name: 'layout',
  title: 'Layout',
  type: 'string',
  options: {
    list: [
      { title: 'Full Width', value: 'full' },
      { title: 'With Contact Sidebar', value: 'with-sidebar' },
    ],
  },
}),
defineField({
  name: 'contactInfo',
  title: 'Contact Information',
  type: 'object',
  hidden: ({ parent }) => parent?.layout !== 'with-sidebar',
  fields: [
    { name: 'title', type: 'string' },
    { name: 'address', type: 'text' },
    { name: 'phone', type: 'string' },
    { name: 'email', type: 'string' },
    { name: 'hours', type: 'text' },
  ],
}),
// Add signature field type to form fields

Component Updates:

  • Two-column layout option
  • Contact sidebar component
  • Signature field (canvas-based or typed)

5. hero (2-4h)

Schema Updates:

defineField({
  name: 'variant',
  title: 'Variant',
  type: 'string',
  options: {
    list: [
      { title: 'Single Image', value: 'single' },
      { title: 'Image Carousel', value: 'carousel' },
    ],
  },
}),
defineField({
  name: 'slides',
  title: 'Slides',
  type: 'array',
  of: [{ type: 'image' }],
  hidden: ({ parent }) => parent?.variant !== 'carousel',
}),
defineField({
  name: 'autoAdvance',
  title: 'Auto Advance',
  type: 'boolean',
  hidden: ({ parent }) => parent?.variant !== 'carousel',
}),

Component Updates:

  • Carousel variant with slides
  • Dot indicators
  • Auto-advance with configurable interval
  • Manual prev/next controls

Phase 6: Content Migration & Integration Testing

Milestone: All WordPress content migrated to Sanity, site fully functional with real content

Effort Range: 12-20 hours

Migration Approach

Tooling Strategy:

  • Use Claude Code + Sanity MCP for AI-assisted migration
  • Batch processing with validation
  • Idempotent scripts (safe to re-run)

Step 1: WordPress API Analysis (1-4h)

// Document the WordPress API structure
interface WPPage {
  id: number
  slug: string
  title: { rendered: string }
  content: { rendered: string }
  acf?: Record<string, unknown> // Custom fields
  // ... map all relevant fields
}

// Map to Sanity structure
const contentMapping = {
  'wp:page': 'page',
  'wp:post': 'blog.post',
  'wp:venue': 'venue',
  // ...
}

Step 2: Migration Script Development (3-8h)

Script Structure: scripts/migrate/

scripts/migrate/
├── index.ts          # Main orchestrator
├── wordpress.ts      # WP API client
├── sanity.ts         # Sanity write client
├── transformers/
│   ├── page.ts       # Page content transformer
│   ├── venue.ts      # Venue transformer
│   ├── richtext.ts   # HTML to Portable Text
│   └── media.ts      # Image/PDF handling
├── validators/
│   └── index.ts      # Post-migration validation
└── config.ts         # Mapping configuration

Key Transformations:

  • HTML → Portable Text (use @portabletext/html-to-portable-text)
  • WordPress media URLs → Sanity asset references
  • ACF fields → Sanity object fields
  • Categories/tags → Sanity references

Step 3: Media Migration (2-6h)

// Download and upload workflow
async function migrateMedia(wpMediaUrl: string): Promise<SanityAssetRef> {
  // 1. Download from WordPress
  // 2. Upload to Sanity
  // 3. Return asset reference
  // 4. Cache URL � ref mapping for content updates
}

Considerations:

  • Batch uploads with rate limiting
  • Progress tracking
  • Skip already-migrated assets
  • Handle missing/broken media gracefully

Step 4: Content Migration (3-8h)

Migration Order:

  1. Document types with no references (destinations, parking-rates)
  2. Document types with simple references (airlines � destinations)
  3. Venues
  4. Pages (with module content)
  5. Blog posts

Validation Per Type:

  • Required fields populated
  • References resolve
  • Rich text renders correctly
  • Media displays

Step 5: Validation & Cleanup (3-8h)

Automated Checks:

// Validation queries
const validationChecks = [
  // All venues have hours
  `*[_type == "venue" && !defined(hours)]`,
  // All airlines have at least one destination
  `*[_type == "airline" && count(destinations) == 0]`,
  // All pages have title
  `*[_type == "page" && !defined(title)]`,
  // Orphaned references
  `*[references(*[_type == "venue"]._id)]`,
]

Manual Review:

  • Spot-check 10% of migrated pages
  • Verify rich text formatting
  • Check image quality/sizing
  • Validate PDF links work

Integration Testing

End-to-End Tests:

  • Homepage renders all modules
  • Flight pages show (mock) data
  • Venue detail pages load
  • PDF downloads work
  • Forms submit correctly

Performance Checks:

  • Page load times < 3s
  • Image optimization working
  • No N+1 query issues

Risk Register

High Risk

Risk Probability Impact Mitigation
Gatsby component porting issues Low Medium Most React code is portable; isolate Gatsby-specific logic
WordPress API access issues Low High Request credentials Phase 1; document manual fallback

Medium Risk

Risk Probability Impact Mitigation
Video embed provider variations Medium Medium Abstract embed logic; support YouTube/Vimeo first
Carousel cross-browser issues Medium Low Use proven library (Embla Carousel)
Content volume exceeds estimate Medium Medium Prioritize critical pages; parallelize migration
Rich text conversion quality Medium Medium Test early; manual cleanup budget

Low Risk

Risk Probability Impact Mitigation
Type generation issues Low Low Run frequently; fix incrementally
Module registry conflicts Low Low Follow existing patterns exactly

Assumptions

  1. Development Environment

    • AI-assisted development (Claude Code) available throughout
    • Developer has Next.js 14+ and Sanity experience
    • Local development environment configured
  2. Access & Credentials

    • WordPress API read access provided
    • Flight data API identified and credentials provided by Phase 1 end
    • Sanity project access with editor permissions
  3. Assets & Content

    • All logos, images, PDFs available in usable format
    • Content migration can run against production WordPress (read-only)
    • No content freeze required during migration
  4. Technical

    • No major Alloy Catalyst framework changes during project
    • Sanity schema changes can be deployed without data migration
    • Flight API provides data in documented, stable format
  5. Scope

    • Terminal map can use static fallback if SDK proves too complex
    • Mobile bottom nav and notification bell are stretch goals
    • Language switching uses existing i18n infrastructure

Appendix A: Module Registry Pattern

Adding a new module:

  1. Create schema: src/sanity/schemaTypes/modules/[module-name].ts
  2. Register schema: src/sanity/schemaTypes/modules/index.ts
  3. Add query: src/sanity/lib/queries.ts in MODULES_QUERY
  4. Create component: src/ui/modules/[ModuleName].tsx
  5. Register component: src/ui/modules/index.tsx
  6. Generate types: npm run generate-types

Example Query Pattern:

_type == "module-name" => {
  ...,
  // Expand references
  items[]-> {
    ...,
    image { asset-> { url, metadata } }
  },
  // Expand links
  cta { ${LINK_QUERY} }
}

Appendix B: Effort Summary

Phase Description Low High
1 Foundation 12h 18h
2 Core Airport Functionality 20h 32h
3 Content & Display Modules 18h 28h
4 Supporting Modules 16h 26h
5 Complex Features & Enhancements 14h 22h
6 Content Migration 12h 20h
Base Total 92h 146h
Buffer (15%) 14h 22h
Project Total 106h 168h

Realistic estimate: ~140 hours

Key factors reducing estimates from initial assessment:

  • Terminal map and flight API already implemented in Gatsby (porting vs. building)
  • Flight components are proven patterns needing adaptation, not new development
  • WordPress rich text → Portable Text is a known transformation

Document created: 2024 Last updated: 2024

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment