MyKindlebox/memory-bank/mykindlebox-shows-design-do...

14 KiB
Raw Blame History

MyKindlebox: Shows Section — Design Document

Overview

Add a Shows section to MyKindlebox that pulls live show listings from the ARBA show search page, lets users filter and browse upcoming shows, build personal show events, assign rabbits from their herd, track judges and placements, and generate a show entries report for email.


1. ARBA Data Source

Source URL: https://arba.net/show-search/

The page renders a DataTables-powered table client-side from an embedded dataset. The table columns map to the following data fields:

Field Notes
sanctionNumber e.g. YOA4567 — unique per show
showDateStart String, format varies (06/26/2026 or 6/27/2026)
stateProvinceCountry State/province abbreviation; blank for international
district ARBA district number (1–9+); blank for international
city City name
showInformation Club name + show type (e.g. OPEN ALL BREED SHOW, YOUTH SPECIALTY BREED SHOW, HOLLAND LOP)
showAddress Full street address or just city/state if no address on file
mapsUrl Google Maps link (may be absent)
showSecretary Secretary name
email Secretary email
catalogUrl PDF catalog link on arba.net (optional, ~20% of shows)

Data Fetch Strategy

The ARBA page renders via JavaScript (DataTables). Options:

Option A — Server-side proxy scrape (recommended)

  • Node.js backend route: GET /api/shows/arba
  • Use puppeteer or playwright headless browser to load the page, wait for the table to render, then extract the full dataset as JSON.
  • Cache result in SQLite with a fetched_at timestamp. Refresh on first request of the day (ARBA updates Mon–Fri during business hours).
  • Cache table: arba_shows_cache with columns: id, fetched_at, data (JSON blob).

Option B — Parse embedded data

  • The DataTables source may be an inline JSON variable in the page source. Inspect and parse with cheerio if available — faster than headless browser, but brittle if ARBA changes rendering.

Recommendation: start with Option B, fall back to headless if the JSON isn't in source.

Parsed Show Fields

After scraping, normalize to:

{
  "sanctionNumber": "YOA4567",
  "date": "2026-06-26",
  "state": "SC",
  "district": 9,
  "city": "LAURENS",
  "clubName": "FLINT FIRE UNITED RABBIT REBELS",
  "showType": "OPEN ALL BREED SHOW",
  "showTypeFull": "OPEN ALL BREED SHOW",
  "specialBreed": null,
  "address": "186 FAIRGROUNDS RD, LAURENS, SC 29360",
  "mapsUrl": "https://local.google.com/maps/?q=...",
  "secretary": "SHELLY KURTZ",
  "email": "SKURTZSHOWSECRETARY@GMAIL.COM",
  "catalogUrl": null
}

Parse showInformation to split into clubName, showType (OPEN/YOUTH ALL BREED or SPECIALTY), and specialBreed (the breed name after the last comma in specialty shows).


2. Shows Browser (Read-Only View)

Route

/shows — top-level nav item in MyKindlebox sidebar.

Filters (above the table)

Filter Type Behavior
State / Province Multi-select dropdown Populated dynamically from cached data. Includes blank option for international.
District Multi-select dropdown Values 1–9 plus blank.
Date From Date picker Defaults to today's date.
Date To Date picker Defaults to empty (no upper bound).
Show Type Multi-select ALL BREED OPEN, ALL BREED YOUTH, SPECIALTY OPEN, SPECIALTY YOUTH, FAIR SHOW
Keyword Text input Filters against club name, city, secretary

All filters are client-side after the initial load. A Reset Filters button restores defaults.

A Refresh Data button (with last-fetched timestamp) triggers a server-side re-scrape.

Results Table

Columns:

  • Date
  • State
  • District
  • City
  • Club / Show Name (parsed from showInformation)
  • Show Type
  • Specialty Breed (blank if all-breed)
  • Secretary
  • Email (mailto link)
  • Address (links to Maps URL if present)
  • Catalog (PDF icon link if present)
  • Action — "Add to Event" button

Default sort: Date ascending, then State.

Pagination: 50 rows per page, client-side.

Each row is selectable. Selecting multiple rows enables a bulk "Add selected to Event" action in the toolbar.


3. Data Model Additions

show_events table

A user-created event grouping one or more ARBA shows together (e.g. a weekend away with 4 shows across 2 days).

CREATE TABLE show_events (
  id          INTEGER PRIMARY KEY AUTOINCREMENT,
  user_id     INTEGER NOT NULL,
  name        TEXT NOT NULL,
  notes       TEXT,
  created_at  DATETIME DEFAULT CURRENT_TIMESTAMP,
  updated_at  DATETIME DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (user_id) REFERENCES users(id)
);

show_event_shows table

Junction: which ARBA shows are included in an event.

CREATE TABLE show_event_shows (
  id               INTEGER PRIMARY KEY AUTOINCREMENT,
  show_event_id    INTEGER NOT NULL,
  sanction_number  TEXT NOT NULL,
  show_date        TEXT NOT NULL,
  club_name        TEXT NOT NULL,
  show_type        TEXT NOT NULL,
  specialty_breed  TEXT,
  city             TEXT,
  state            TEXT,
  address          TEXT,
  secretary        TEXT,
  secretary_email  TEXT,
  catalog_url      TEXT,
  maps_url         TEXT,
  display_order    INTEGER DEFAULT 0,
  FOREIGN KEY (show_event_id) REFERENCES show_events(id) ON DELETE CASCADE
);

Store a snapshot of the ARBA data at time of adding (the ARBA listing may change or disappear).

show_event_judges table

Judges assigned per show within an event.

CREATE TABLE show_event_judges (
  id                    INTEGER PRIMARY KEY AUTOINCREMENT,
  show_event_show_id    INTEGER NOT NULL,
  judge_name            TEXT NOT NULL,
  judge_arba_number     TEXT,
  notes                 TEXT,
  FOREIGN KEY (show_event_show_id) REFERENCES show_event_shows(id) ON DELETE CASCADE
);

show_event_entries table

Rabbits entered into a specific show within an event.

CREATE TABLE show_event_entries (
  id                    INTEGER PRIMARY KEY AUTOINCREMENT,
  show_event_show_id    INTEGER NOT NULL,
  rabbit_id             INTEGER NOT NULL,
  entry_class           TEXT,
  entry_number          TEXT,
  notes                 TEXT,
  created_at            DATETIME DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (show_event_show_id) REFERENCES show_event_shows(id) ON DELETE CASCADE,
  FOREIGN KEY (rabbit_id) REFERENCES rabbits(id)
);

entry_class is a free-text field — user fills in the class the rabbit is competing in (e.g. Sr Buck, 6-8 Doe).

show_event_placements table

Post-show placement tracking per entry.

CREATE TABLE show_event_placements (
  id               INTEGER PRIMARY KEY AUTOINCREMENT,
  entry_id         INTEGER NOT NULL UNIQUE,
  placement        TEXT,
  legs_earned      INTEGER DEFAULT 0,
  bos              BOOLEAN DEFAULT 0,
  bob              BOOLEAN DEFAULT 0,
  bis              BOOLEAN DEFAULT 0,
  ris              BOOLEAN DEFAULT 0,
  notes            TEXT,
  FOREIGN KEY (entry_id) REFERENCES show_event_entries(id) ON DELETE CASCADE
);

placement is free text to handle variety: 1st, 2nd, BOB, BOSV, etc. The boolean flags (bob, bos, bis, ris) are quick toggles for rollup stats.


4. Show Event Management

Route

/shows/events — list of user's show events. /shows/events/:id — event detail / management page. /shows/events/new — create new event.

Event List View

  • Cards or table showing event name, date range of included shows, number of shows, number of rabbits entered.
  • Quick access to open/edit/delete.

Event Detail View

Organized in tabs or sections:

Shows Tab

  • List of all ARBA shows added to this event, sorted by date.
  • Each show card shows: date, sanction #, club name, show type, city/state, secretary email, catalog link.
  • Per show: Judge(s) management (add/edit/remove judges with name and optional ARBA number).
  • Button to remove a show from the event.
  • Button to add more shows (opens the browser modal).

Entries Tab

  • Per-show accordion or grouped list.
  • Each show shows the rabbits entered into it.
  • "Add Rabbit" opens a picker from the user's herd — searchable by name, tattoo, breed, variety.
  • Each entry row: Rabbit name, tattoo, breed, variety, entry class (editable inline), notes.
  • Remove entry button.
  • After the show date, an "Enter Placements" toggle switches the row into placement-entry mode: placement text field, legs +/- counter, BOB/BOS/BIS/RIS checkboxes.

Results Summary Tab

  • Aggregate view of all placements across the event.
  • Rabbits sorted by total legs earned.
  • Highlight BOB/BOS/BIS/RIS with badge icons.
  • Read-only — auto-generated from placement data.

5. Show Entries Report

Accessible from the Entries tab as "Generate Entries Report".

Output is a plain-text formatted block in a modal with a "Copy to Clipboard" button. Suitable for pasting directly into an email to the show secretary.

Report Format

SHOW ENTRIES — [Event Name]
Generated: [date]

==============================================
[Club Name] — [Show Type]
Sanction #: [YOA1234]
Date: [MM/DD/YYYY]
Secretary: [Name] <[email]>
==============================================

RABBITS ENTERED:

1. [Rabbit Name] ([Tattoo])
   Breed: [Breed] / [Variety]
   Class: [Entry Class]
   Owner: [User's rabbitry name or full name]
   ARBA #: [user's ARBA membership number if stored]

2. [Rabbit Name] ([Tattoo])
   Breed: [Breed] / [Variety]
   Class: [Entry Class]
   Owner: [User's rabbitry name or full name]
   ARBA #: [user's ARBA membership number if stored]

[Additional rabbits...]

==============================================
[Next Show in Event if applicable...]
==============================================

Per-show sections are repeated for each show in the event. If a rabbit is entered in multiple shows within the event, it appears under each show it is entered in.

Report Settings (optional, stored per user)

  • Include/exclude owner ARBA number
  • Include/exclude rabbit registration numbers (if tracked in herd)
  • Custom header/footer text block

6. UI / UX Notes

Adding Shows to an Event Flow

  1. User clicks "Add to Event" on a row in the shows browser.
  2. Modal appears: "Add to which event?" with dropdown of existing events + "Create new event" option.
  3. If creating new, prompt for event name and optional notes inline.
  4. Confirm — show is added to event. Toast confirmation with link to the event.

Alternatively, from the event detail page, a "Browse Shows" button opens the full browser in a slide-over panel where selecting shows adds them directly to the event in context.

Placement Entry UX

  • Placement entry should feel fast. After the show, the user flips a toggle on each row: "Mark Results."
  • Tab key should advance through fields (placement → legs → BOB → BOS checkboxes).
  • Legs can be incremented by clicking + since this is a count field (0–3 per show).

Linking to Existing Herd Data

  • Rabbit picker pulls from the rabbits table already in MyKindlebox.
  • Filter by breed and variety to find the right animal quickly.
  • Show tattoo prominently — this is the primary show identifier.
  • If a rabbit is already entered in another show within the same event, show a small indicator (useful when the same rabbit runs in multiple shows on the same weekend).

7. Backend API Endpoints

Method Route Description
GET /api/shows/arba Returns cached ARBA show list. Triggers refresh if stale (>24h).
POST /api/shows/arba/refresh Force re-scrape.
GET /api/shows/events List user's show events.
POST /api/shows/events Create new event.
GET /api/shows/events/:id Event detail with shows, judges, entries, placements.
PATCH /api/shows/events/:id Update event name/notes.
DELETE /api/shows/events/:id Delete event.
POST /api/shows/events/:id/shows Add an ARBA show to an event.
DELETE /api/shows/events/:id/shows/:showId Remove a show from an event.
POST /api/shows/events/:id/shows/:showId/judges Add a judge to a show.
DELETE /api/shows/events/:id/shows/:showId/judges/:judgeId Remove a judge.
POST /api/shows/events/:id/shows/:showId/entries Add a rabbit entry.
DELETE /api/shows/events/:id/shows/:showId/entries/:entryId Remove a rabbit entry.
PUT /api/shows/events/:id/shows/:showId/entries/:entryId/placement Set or update placement for an entry.
GET /api/shows/events/:id/report Returns the formatted entries report as plain text.

8. Implementation Phases

Phase 1 — ARBA Browser

  • Scraper/cache layer (Node, SQLite).
  • /shows route with filter UI and results table.
  • No event functionality yet — just browse and view.

Phase 2 — Event Management

  • show_events, show_event_shows tables and all CRUD.
  • "Add to Event" flow from browser.
  • Event list and detail views (Shows tab only).

Phase 3 — Entries

  • show_event_entries table.
  • Rabbit picker integration.
  • Entries tab in event detail.
  • Entries report generation.

Phase 4 — Judges & Placements

  • show_event_judges and show_event_placements tables.
  • Judge management UI per show.
  • Placement entry UI per entry.
  • Results Summary tab.

9. Resolved Decisions

Topic Decision
ARBA scrape reliability No user fallback. If the scraper fails, surface an error state in the UI and log it server-side. The cache remains available until it can be refreshed.
ARBA membership number Not required to show rabbits; not collected or included in the entries report.
Rabbitry name Already stored as part of farm setup — pull from existing profile for the report header. No new field needed.
Mobile filter bar Filters collapse to a drawer on small screens; wide table scrolls horizontally. No special redesign needed.
Cavy shows Filter out at the scrape/normalization layer. Any show with CAVY in the showInformation field is excluded before being written to the cache.
International shows Only US shows displayed. Rows with a blank or non-US stateProvinceCountry field are excluded at the normalization layer.