# Data Model: McDonald's IT Halloween Event Page **Feature**: Halloween event announcement webpage **Date**: 2025-10-04 **Status**: Complete ## Overview This document defines the data entities and their relationships for the Halloween event page. While this is a static page with no backend storage, defining the data model helps structure the HTML content and JavaScript logic. ## Entities ### 1. Event Represents the Halloween event information. **Purpose**: Store and display all event details **Fields**: | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `title` | String | Yes | - | Event title: "2025 MITA Halloween" | | `date` | Date | Yes | - | Event date: October 31, 2025 | | `startTime` | Time | Yes | - | Event start: 18:00 GMT+8 | | `endTime` | Time | Yes | - | Event end: 21:00 GMT+8 | | `timezone` | String | Yes | "GMT+8" | Timezone for event | | `location` | String | Yes | - | "MITA Building 2F Pantry" | | `organizer` | Object | Yes | - | Organizer details | | `organizer.name` | String | Yes | - | "Jessi Pan" | | `organizer.email` | String | Yes | - | "jessi.pan@cn.mcd.com" | | `activities` | Array | Yes | - | List of activities | | `notes` | Array | Yes | - | Additional information | **Validation Rules**: - `date` must be valid ISO date format - `startTime` must be before `endTime` - Times must be in 24-hour format (HH:MM) - `organizer.email` must be valid email format - `activities` must contain at least 1 item **Example**: ```json { "title": "2025 MITA Halloween", "date": "2025-10-31", "startTime": "18:00", "endTime": "21:00", "timezone": "GMT+8", "location": "MITA Building 2F Pantry", "organizer": { "name": "Jessi Pan", "email": "jessi.pan@cn.mcd.com" }, "activities": [ "Cosplay (encouraged)", "Bug Debugging Games", "Lucky Draw" ], "notes": [ "Costumes encouraged", "Food and drinks provided" ] } ``` **State Transitions**: None (static data) --- ### 2. Countdown Represents the countdown timer state and logic. **Purpose**: Calculate and display time remaining until event **Fields**: | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `targetDateTime` | DateTime | Yes | - | Oct 31 2025 18:00:00 GMT+8 (in UTC: 10:00:00 UTC) | | `currentDateTime` | DateTime | Yes | - | User's current time (updated every second) | | `timeRemaining` | Object | No | null | Calculated time remaining | | `timeRemaining.days` | Integer | No | 0 | Days remaining | | `timeRemaining.hours` | Integer | No | 0 | Hours remaining (0-23) | | `timeRemaining.minutes` | Integer | No | 0 | Minutes remaining (0-59) | | `timeRemaining.seconds` | Integer | No | 0 | Seconds remaining (0-59) | | `status` | Enum | Yes | "before" | Current event status | **Status Values**: - `before`: Current time is before event start (show countdown) - `active`: Event has started, current time is between start and end (show "EVENT STARTING NOW!") - `ended`: Event has ended, current time is after event end (show "EVENT COMPLETED") **Validation Rules**: - `targetDateTime` must be a valid Date object - All time values must be >= 0 - `status` must be one of: "before", "active", "ended" - `timeRemaining` is null when status is not "before" **Calculation Logic**: ```javascript // Convert GMT+8 to UTC for calculation // Oct 31 2025 18:00 GMT+8 = Oct 31 2025 10:00 UTC const targetDate = new Date('2025-10-31T10:00:00Z'); const eventEnd = new Date('2025-10-31T13:00:00Z'); // 21:00 GMT+8 function updateCountdown() { const now = new Date(); if (now >= eventEnd) { return { status: 'ended', timeRemaining: null }; } if (now >= targetDate) { return { status: 'active', timeRemaining: null }; } const diff = targetDate - now; const days = Math.floor(diff / (1000 * 60 * 60 * 24)); const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); const seconds = Math.floor((diff % (1000 * 60)) / 1000); return { status: 'before', timeRemaining: { days, hours, minutes, seconds } }; } ``` **State Transitions**: ``` before β†’ active (when current time >= targetDateTime) active β†’ ended (when current time >= eventEnd) ``` --- ### 3. VisualTheme Represents the visual design configuration. **Purpose**: Define and apply consistent styling across the page **Fields**: | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `backgroundColor` | Color | Yes | "#000000" | Pure black background | | `primaryColor` | Color | Yes | "#00FF00" | Terminal green for main text | | `accentColor` | Color | Yes | "#FFBF00" | Amber for key information | | `fontFamily` | String | Yes | See below | Monospace font stack | | `fontSize` | Object | Yes | - | Font sizes for different elements | | `fontSize.base` | String | Yes | "16px" | Base font size | | `fontSize.heading` | String | Yes | "20px" | Heading size | | `fontSize.small` | String | Yes | "14px" | Small text size | | `asciiArt` | Object | Yes | - | ASCII art content | | `asciiArt.logoDesktop` | String | Yes | - | McDonald's logo for desktop | | `asciiArt.logoMobile` | String | Yes | - | McDonald's logo for mobile | | `asciiArt.decorations` | Array | Yes | - | Halloween ASCII decorations | **Font Stack**: ```css "Courier New", Monaco, Consolas, "Liberation Mono", Menlo, monospace ``` **Validation Rules**: - All color values must be valid hex format (#RRGGBB) - `backgroundColor` must be #000000 (black) - `primaryColor` and `accentColor` must meet WCAG AA contrast ratio with background - `fontFamily` must include at least one monospace fallback - ASCII art strings must contain only ASCII characters (0-127) **Example**: ```json { "backgroundColor": "#000000", "primaryColor": "#00FF00", "accentColor": "#FFBF00", "fontFamily": "\"Courier New\", Monaco, Consolas, Menlo, monospace", "fontSize": { "base": "16px", "heading": "20px", "small": "14px" }, "asciiArt": { "logoDesktop": "[Multi-line ASCII art]", "logoMobile": "[Simplified ASCII art]", "decorations": ["πŸŽƒ", "πŸ‘»", "πŸ¦‡"] } } ``` **State Transitions**: None (static configuration) --- ### 4. Content (Bilingual) Represents bilingual text content structure. **Purpose**: Store and display English/Chinese content pairs **Fields**: | Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | `section` | String | Yes | - | Content section identifier | | `english` | String | Yes | - | English text | | `chinese` | String | Yes | - | Chinese text (Simplified) | | `order` | Integer | Yes | - | Display order | **Example Content Pairs**: ```json [ { "section": "date", "english": "Date: October 31, 2025", "chinese": "ζ—₯期:2025εΉ΄10月31ζ—₯", "order": 1 }, { "section": "time", "english": "Time: 18:00 - 21:00 (6:00 PM - 9:00 PM)", "chinese": "ζ—Άι—΄οΌš18:00 - 21:00", "order": 2 }, { "section": "location", "english": "Location: MITA Building 2F Pantry", "chinese": "εœ°η‚ΉοΌšMITA倧厦2ζ₯ΌθŒΆζ°΄ι—΄", "order": 3 }, { "section": "organizer", "english": "Organizer: Jessi Pan (jessi.pan@cn.mcd.com)", "chinese": "η»„η»‡θ€…οΌšJessi Pan (jessi.pan@cn.mcd.com)", "order": 4 } ] ``` **Display Pattern**: ```html

{english}

{chinese}

``` --- ## Relationships ``` Event (1) β”œβ”€β”€ has timezone (1) β†’ Countdown └── has theme (1) β†’ VisualTheme Countdown (1) └── uses targetDateTime from Event VisualTheme (1) └── styles all Content Content (many) └── derived from Event ``` ## HTML Representation The data model maps to HTML structure as follows: ```html
[McDonald's ASCII Logo]
[McDonald's ASCII Logo Simplified]

i'm lovin' IT_

0d 0h 0m 0s

Date: {Event.date}

ζ—₯期:{Event.date}

Activities:

活动:

  • {activity}
  • {activity_zh}
{decoration}
``` ## JavaScript Data Structure ```javascript const eventData = { event: { title: "2025 MITA Halloween", date: "2025-10-31", startTime: "18:00", endTime: "21:00", timezone: "GMT+8", location: "MITA Building 2F Pantry", organizer: { name: "Jessi Pan", email: "jessi.pan@cn.mcd.com" }, activities: ["Cosplay", "Bug Debugging Games", "Lucky Draw"], notes: ["Costumes encouraged", "Food and drinks provided"] }, countdown: { targetDateTime: new Date('2025-10-31T10:00:00Z'), // GMT+8 = UTC+8 eventEnd: new Date('2025-10-31T13:00:00Z'), status: 'before', timeRemaining: null }, theme: { backgroundColor: "#000000", primaryColor: "#00FF00", accentColor: "#FFBF00", fontFamily: '"Courier New", Monaco, Consolas, Menlo, monospace' } }; ``` ## Validation Contract All data must conform to the schema defined in `/contracts/page-content.schema.json`. See: [page-content.schema.json](./contracts/page-content.schema.json) ## Notes - This is a static page, so data is hardcoded in HTML/JS - No database or API calls required - Data updates only via code changes - Countdown is the only dynamic data (calculated client-side) ## Next Steps - Create contracts/page-content.schema.json (JSON Schema validation) - Create quickstart.md (testing checklist) - Update CLAUDE.md (agent context)