Files
halloween-test/specs/001-mcdonald-s-it/data-model.md
snowprint 325eeaf063 feat(spec): complete implementation plan for Halloween event page
Created comprehensive planning documentation following Spec-Kit standards:

Phase 0 - Research:
- Analyzed 10 technical decisions (HTML5, ASCII art, countdown, responsive design)
- Resolved edge cases (post-countdown, post-event, JS disabled, small screens)
- Documented rationale and alternatives for each decision

Phase 1 - Design:
- Defined 4 data entities (Event, Countdown, VisualTheme, Content)
- Created JSON schema contract for page content validation
- Mapped 35 functional requirements to implementation approach
- Generated quickstart testing checklist with 10 test categories

Technical decisions:
- Single-file HTML architecture (no external dependencies)
- Pure CSS animations (blinking cursor)
- JavaScript countdown with GMT+8 timezone handling
- Responsive ASCII art (desktop/mobile versions)
- WCAG 2.1 AA compliance (21:1 contrast ratio)

Constitution compliance: PASS
- No violations detected
- Follows user-centric, professional excellence principles
- Minimal technical stack aligns with simplicity requirement

Artifacts created:
- spec.md: 35 functional requirements
- plan.md: Implementation strategy and phases
- research.md: 10 technical research decisions
- data-model.md: 4 entity definitions with validation
- contracts/page-content.schema.json: JSON Schema
- quickstart.md: Comprehensive testing checklist

Ready for: /tasks command to generate tasks.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-04 19:20:25 +08:00

365 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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<String> | Yes | - | List of activities |
| `notes` | Array<String> | 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<String> | 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
<p lang="en">{english}</p>
<p lang="zh-CN">{chinese}</p>
```
---
## 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
<main>
<!-- VisualTheme.asciiArt.logoDesktop -->
<header>
<pre class="ascii-desktop">[McDonald's ASCII Logo]</pre>
<pre class="ascii-mobile">[McDonald's ASCII Logo Simplified]</pre>
<p class="tagline">i'm lovin' IT<span class="cursor">_</span></p>
</header>
<!-- Countdown -->
<section class="countdown">
<div id="countdown-display">
<span id="days">0</span>d
<span id="hours">0</span>h
<span id="minutes">0</span>m
<span id="seconds">0</span>s
</div>
</section>
<!-- Event + Content (bilingual) -->
<section class="event-info">
<p lang="en">Date: {Event.date}</p>
<p lang="zh-CN">日期:{Event.date}</p>
<!-- ... more bilingual pairs -->
</section>
<!-- Event.activities -->
<section class="activities">
<p lang="en">Activities:</p>
<p lang="zh-CN">活动:</p>
<ul>
<li lang="en">{activity}</li>
<li lang="zh-CN">{activity_zh}</li>
</ul>
</section>
<!-- VisualTheme.asciiArt.decorations -->
<section class="decorations">
<pre>{decoration}</pre>
</section>
</main>
```
## 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)