Agenda Planner — Data Maintenance Guide ​

The Agenda Planner loads its data from two JSON files at runtime. You can update races and epithets by editing these files directly — no component changes needed.

File Locations ​

docs/.vitepress/theme/data/AgendaData/
├── race-list.json    ← All career races (186 entries)
└── epithets.json     ← Epithet definitions (4 entries)

These files are imported directly by the AgendaPlanner component (no longer fetched as public assets).


Updating the Race List ​

File: docs/.vitepress/theme/data/AgendaData/race-list.json

Each race is a JSON object with these fields:

FieldTypeDescriptionExample
raceNamestringFull English race name"Arima Kinen"
gradestring"G1", "G2", or "G3""G1"
yearstring"First Year", "Second Year", "Third Year""Third Year"
turnstringMM_HH — month (01–12) and half (01=Early, 02=Late)"12_02" (Late December)
typestring"Turf" or "Dirt""Turf"
locationstringRacecourse with direction arrow"⇒ Nakayama"
lengthstringCategory: "Short", "Mile", "Medium", "Long""Long"
lengthMstringDistance in metres"2500 m"

Adding a race ​

Add a new object to the array:

json
{
  "raceName": "New Race Name",
  "grade": "G2",
  "year": "Second Year",
  "turn": "05_01",
  "type": "Turf",
  "location": "⇒ Tokyo",
  "length": "Medium",
  "lengthM": "2000 m"
}

Removing a race ​

Delete the corresponding object from the array.

Notes ​

  • raceName must be unique within each year (the planner uses year|raceName as a key).
  • turn controls calendar placement. "05_01" = Early May, "05_02" = Late May.
  • grade must be exactly "G1", "G2", or "G3" for filters and styling to work.
  • length must be exactly "Short", "Mile", "Medium", or "Long" for the length filter to work.

Updating Epithets ​

File: docs/.vitepress/theme/data/AgendaData/epithets.json

Each epithet is a JSON object. All fields except key, reward, and note are optional — use only the ones you need.

FieldTypeDescription
keystringDisplay name of the epithet
rewardstringReward text, e.g. "2 Random Stats +10" or "Mile Straightaways Hint +1"
notestringRequirement description shown in the UI
required(string | {race, year?})[]Races that must all be planned. Strings match within year; objects can override year per-race
yearstringYear filter: "Junior", "Classic", "Senior". Omit to match any year
requiredAnyOfstring[]Races where at least requiredAnyCount must be planned
requiredAnyCountnumberHow many of requiredAnyOf are needed
requiresEpithetstring[]Other epithet keys that must ALL be completed first
requiresAnyEpithetstring[]Other epithet keys where at least ONE must be completed
requireCountobjectCount-based: plan must have ≥ count races matching filters (see sub-fields below)

requireCount sub-fields:

Sub-fieldTypeDescription
countnumber(required) Minimum number of matching races needed
surfacestringFilter by surface: "Turf" or "Dirt"
gradestringExact grade filter: "G1", "G2", or "G3"
gradeMinstringMinimum grade: "OP" means G1/G2/G3/OP all count
lengthCategorystring"standard" = distance divisible by 400m, "nonstandard" = not divisible by 400m
tracksstring[]Race location must contain one of these track names (e.g. ["Tokyo", "Nakayama"])
nameContainsstring[]Race name must contain one of these substrings (case-insensitive)

Examples ​

Simple — all listed races in a specific year:

json
{
  "key": "Lady",
  "required": ["Oka Sho", "Japanese Oaks", "Shuka Sho"],
  "year": "Classic",
  "reward": "2 Random Stats +10",
  "note": "Win the Oka Sho, Japanese Oaks, and Shuka Sho"
}

With epithet prerequisite:

json
{
  "key": "Heroine",
  "required": ["Queen Elizabeth II Cup"],
  "year": "Classic",
  "requiresEpithet": ["Lady"],
  "reward": "2 Random Stats +10",
  "note": "Acquire the Lady Epithet, and win the Queen Elizabeth II Cup (Classic)"
}

Year-specific race objects (same race in multiple years):

json
{
  "key": "Dirt Sprinter",
  "required": [
    { "race": "JBC Sprint", "year": "Classic" },
    { "race": "JBC Sprint", "year": "Senior" }
  ],
  "reward": "2 Random Stats +10",
  "note": "Win both JBC Sprints"
}

Any-of (pick N from a list):

json
{
  "key": "Phenomenal",
  "requiresEpithet": ["Stunning"],
  "requiredAnyOf": ["Tenno Sho (Spring)", "Takarazuka Kinen", "Japan Cup", "Tenno Sho (Autumn)", "Osaka Hai", "Arima Kinen"],
  "requiredAnyCount": 2,
  "reward": "2 Random Stats +15",
  "note": "..."
}

Count-based (win N races matching a filter):

json
{
  "key": "Dirt G1 Achiever",
  "requireCount": { "surface": "Dirt", "grade": "G1", "count": 3 },
  "reward": "2 Random Stats +10",
  "note": "Win 3 Dirt G1 Races"
}

How rewards are parsed ​

The component parses reward strings to calculate totals:

  • "2 Random Stats +10" → 2 × 10 = 20 stat points
  • "Mile Straightaways Hint +1" → tracked as a skill hint
  • Total stat points and earned hints are shown in the summary

How matching works ​

  • "First Year" → "Junior", "Second Year" → "Classic", "Third Year" → "Senior".
  • Each required entry must exactly match a raceName from race-list.json (case-sensitive).
  • Epithet prerequisites (requiresEpithet) are resolved in order — place prerequisite epithets before dependent ones in the array.
  • Count-based requirements count all planned races matching the filters across all years.
  • lengthCategory uses the lengthM field (e.g. "2400 m" → 2400). Standard = divisible by 400, non-standard = not divisible by 400.

Race Icons ​

Race icons are not maintained by the agenda planner. They come from the site's existing TerumiRaceData.json via dataService. The component automatically maps each raceName to a raceId and loads the icon from /icon/race/thum_race_rt_000_{raceId}_00.webp.

This means:

  • All G1, G2, and G3 races get icons automatically if they exist in the data.
  • If a race name in race-list.json doesn't exactly match a name in TerumiRaceData.json, it will fall back to a text-only card.