Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/SdazaP/ruTournament/llms.txt

Use this file to discover all available pages before exploring further.

All TypeScript interfaces described on this page are defined in src/common/db.ts and represent every piece of data stored in the RuTournamentDB IndexedDB database. The database is managed by Dexie.js and contains a single top-level table — tournaments — which holds self-contained TournamentLocal documents. All related data (categories, rounds, results, competitors) is embedded directly inside each tournament document rather than in separate tables.
id fields for top-level tournaments are generated as Date.now().toString(). Category IDs are formed as tournamentId + index (e.g. "1718000000000_0"), and competitor IDs are formed as tournamentId + 'c' + index (e.g. "1718000000000c0"). These conventions are applied in the creation wizards and are not enforced by Dexie itself.

TournamentLocal

The root document stored in the tournaments table. Every tournament is a single record of this type.
// src/common/db.ts
export interface TournamentLocal {
  id?: string;
  name: string;
  description: string;
  location: string;
  status: string; // 'activo' | 'proximamente' | 'finalizado'
  date: string;   // ISO date string YYYY-MM-DD
  logo?: string;  // base64 image or SVG string
  categories: CategoryLocal[];
  competitors: CompetitorLocal[];
}
id
string
Primary key used by Dexie. Generated as Date.now().toString() at creation time. Dexie indexes this field alongside name, status, and date for efficient querying.
name
string
required
Human-readable tournament name displayed throughout the UI (e.g. "Open Regional 2026").
description
string
required
Free-text description shown on the tournament dashboard. May be an empty string.
location
string
required
Venue or city name (e.g. "Bogotá, Colombia").
status
'activo' | 'proximamente' | 'finalizado'
required
Lifecycle state of the tournament. Controls UI permissions via the useTournamentStatus hook:
ValuecanEditcanUploadResults
activo
proximamente
finalizado
date
string
required
Competition date in YYYY-MM-DD ISO format (e.g. "2026-06-15").
Optional tournament logo stored as a base64-encoded image string or raw SVG markup. The upload UI automatically resizes images to 200 px wide before encoding.
categories
CategoryLocal[]
required
Ordered list of competition categories (events). Each element is a CategoryLocal object. See CategoryLocal below.
competitors
CompetitorLocal[]
required
Flat list of all registered competitors. Category membership is stored on the competitor (via category IDs), not on the category itself.

CategoryLocal

Represents one competition event within a tournament (e.g. “3x3”, “Megaminx”).
// src/common/db.ts
export interface CategoryLocal {
  id?: string;
  name: string;
  format: string;        // 'wca' | 'redbull'
  startTime?: string;    // HH:MM
  endTime?: string;      // HH:MM
  room?: string;
  rounds: RoundLocal[];
  hasSeeding?: boolean;
  seedingFormat?: 'ao3' | 'ao5';
  bracketMode?: 'random' | 'manual';
}
id
string
Generated as tournamentId + "_" + index during category creation.
name
string
required
Display name of the category. For WCA events this should match a key in WCA_EVENT_CONFIG (e.g. "3x3", "Megaminx"). Custom names are also supported.
format
'wca' | 'redbull'
required
Determines which result-entry and bracket UI is used. 'wca' uses the standard average-based results view; 'redbull' uses a head-to-head bracket.
startTime
string
Scheduled start time in HH:MM 24-hour format.
endTime
string
Scheduled end time in HH:MM 24-hour format.
room
string
Name of the room or station where the category takes place.
rounds
RoundLocal[]
required
Ordered list of rounds for this category. See RoundLocal.
hasSeeding
boolean
When true, the first round is treated as a seeding round. Seeding results determine bracket seeding for subsequent rounds.
seedingFormat
'ao3' | 'ao5'
Round format used for the seeding round. Only relevant when hasSeeding is true.
bracketMode
'random' | 'manual'
Controls how competitors are assigned to bracket slots in Red Bull categories. 'random' shuffles competitors; 'manual' lets the organiser drag-and-drop.

RoundLocal

One round within a category.
// src/common/db.ts
export interface RoundLocal {
  id?: string;
  num: number;
  format: string;                   // 'ao3' | 'ao5' | 'rb'
  results: ResultLocal[];
  competitorsToAdvance: number | 'all';
  scrambles?: ScrambleRecord[];
  groups?: GroupLocal[];
  matches?: RedBullMatchLocal[];    // Red Bull format only
  bracketMode?: 'random' | 'manual';
  isSeeding?: boolean;
}
id
string
Optional round identifier.
num
number
required
Round number (1-based). Used to determine which competitors advance from the previous round.
format
'ao3' | 'ao5' | 'rb'
required
Solving format. 'ao3' = average of 3, 'ao5' = average of 5, 'rb' = Red Bull head-to-head.
results
ResultLocal[]
required
Array of entered results, one per competitor who has at least one recorded solve. See ResultLocal.
competitorsToAdvance
number | 'all'
required
How many top-ranked competitors advance to the next round. The special value 'all' passes every competitor through.
scrambles
ScrambleRecord[]
Round-level scrambles generated by the csTimer engine. See ScrambleRecord.
groups
GroupLocal[]
Competitor groups for this round. Organises competitors and staff across stations and time slots.
matches
RedBullMatchLocal[]
Head-to-head match bracket. Only populated when format is 'rb'. See RedBullMatchLocal.
bracketMode
'random' | 'manual'
Per-round bracket assignment override, mirroring the category-level field.
isSeeding
boolean
Marks this round as the seeding round. Set automatically when CategoryLocal.hasSeeding is true.

GroupLocal

A competitor group within a round, used to organise who solves at the same time and who staffs them.
// src/common/db.ts
export interface GroupLocal {
  id: string;
  name: string;
  startTime: string;
  endTime: string;
  competitors: string[];   // competitor IDs
  staff: {
    judge: string[];       // competitor IDs acting as judges
    runner: string[];
    scrambler: string[];
  };
  scrambles?: ScrambleRecord[];
}
id
string
required
Unique group identifier.
name
string
required
Display name (e.g. "Group A").
startTime
string
required
Group start time in HH:MM format.
endTime
string
required
Group end time in HH:MM format.
competitors
string[]
required
Array of competitor IDs who are solving in this group.
staff
object
required
Staff assignments for this group.
scrambles
ScrambleRecord[]
Group-specific scrambles. When present, these take precedence over round-level scrambles for this group.

CompetitorLocal

A registered competitor. Category membership and role assignments are stored on this object.
// src/common/db.ts
export interface CompetitorLocal {
  id?: string;
  name?: string;
  categories: string[];                        // category IDs
  roles?: string[];                            // available global roles
  assignedRoles?: Record<string, string[]>;    // { categoryId: ['judge', 'runner'] }
}
id
string
Generated as tournamentId + "c" + index during competitor registration.
name
string
Full display name of the competitor.
categories
string[]
required
List of category IDs the competitor is registered to compete in.
roles
string[]
Global role labels available to this competitor (e.g. ['judge', 'scrambler']).
assignedRoles
Record<string, string[]>
Per-category role assignments. Each key is a category ID; the value is an array of role strings assigned to this competitor within that category.Example:
{
  "1718000000000_1": ["judge"],
  "1718000000000_2": ["runner", "scrambler"]
}

ResultLocal

Stores the raw solve times and computed average for one competitor in one round.
// src/common/db.ts
export interface ResultLocal {
  idCompetitor: string;
  times: (number | TimeRecord)[];
  media: string;
}
idCompetitor
string
required
The ID of the competitor this result belongs to.
times
(number | TimeRecord)[]
required
Array of individual solve times. Elements may be plain number values (legacy) or TimeRecord objects. Use normalizeTime() to convert any element to a consistent TimeRecord before processing.
media
string
required
Computed average stored as a formatted string (e.g. "12.34"). "-1" indicates a DNF average. Recalculated on every save by calculateRulesStats().

TimeRecord

The canonical representation of a single solve time, including any WCA penalty.
// src/common/db.ts
export type Penalty = '' | '+2' | 'DNF';

export interface TimeRecord {
  base: number;     // raw solve time in seconds (floating-point)
  penalty: Penalty;
}
base
number
required
Raw solve time in seconds as a floating-point number (e.g. 9.53, 67.12). This is the time before applying a +2 penalty. A value of 0 with penalty: 'DNF' represents a pure DNF with no recorded time.
penalty
'' | '+2' | 'DNF'
required
WCA penalty applied to this solve:
  • '' — no penalty; effective time equals base.
  • '+2' — 2-second penalty; effective time equals base + 2.
  • 'DNF' — Did Not Finish; solve does not count towards the average.

ScrambleRecord

A single scramble sequence together with its rendered puzzle-state image.
// src/common/db.ts
export interface ScrambleRecord {
  text: string;   // scramble notation (e.g. "R U R' U'")
  svg: string;    // SVG markup of the resulting puzzle state
}
text
string
required
The scramble sequence in standard WCA notation.
svg
string
required
SVG markup string rendered by the csTimer engine, showing the puzzle state after applying the scramble. Displayed inline in the Scrambles view.

RedBullMatchLocal

One head-to-head match in a Red Bull bracket round.
// src/common/db.ts
export interface RedBullMatchLocal {
  id: string;
  competitor1Id: string;
  competitor2Id: string;
  winner?: string;
  times: {
    competitor1: (TimeRecord | null)[];
    competitor2: (TimeRecord | null)[];
  };
  wins: {
    competitor1: number;
    competitor2: number;
  };
}
id
string
required
Unique match identifier within the bracket.
competitor1Id
string
required
ID of the first competitor in this match.
competitor2Id
string
required
ID of the second competitor in this match.
winner
string
Competitor ID of the match winner. Populated after the match concludes.
times
object
required
Per-solve times for both competitors in this match. Each array element corresponds to one solve leg; elements may be null if not yet entered.
wins
object
required
Running win count for each competitor within the match.

Database access

The singleton db instance is exported from src/common/db.ts and should be used throughout the application for all IndexedDB operations.
import { db } from './common/db';

// Retrieve a single tournament by ID
const tournament = await db.tournaments.get(id);

// Persist an updated tournament (replaces the existing record)
await db.tournaments.put(updatedTournament as any);

// Insert a new tournament
await db.tournaments.add(newTournament as any);

// List all tournaments
const all = await db.tournaments.toArray();

// Query by status
const active = await db.tournaments.where('status').equals('activo').toArray();
The Dexie table is indexed on id, name, status, and date. All other fields (categories, competitors, rounds, results) are stored as plain JSON inside the document and are not individually indexed. Queries on non-indexed fields require loading the full tournament document first.

Utility functions

These helper functions are exported alongside the data types and used throughout the results views.

normalizeTime

// src/pages/Tournament/ResultsWCA.tsx
export const normalizeTime = (t: any): TimeRecord
Converts a raw time value of any shape into a canonical TimeRecord. Handles three input forms:
  • Object with base — returned as-is with base cast to Number.
  • number — becomes { base: t, penalty: '' } for positive values, or { base: 0, penalty: 'DNF' } for negative values.
  • Numeric string — parsed and treated the same as a plain number.
Use this before passing any time value to calculateRulesStats or rendering it in the UI.

calculateRulesStats

// src/pages/Tournament/ResultsWCA.tsx
export const calculateRulesStats = (
  times: TimeRecord[],
  format: 'ao3' | 'ao5'
): { best: number; average: number }
Computes the WCA best single and average for a set of solve times.
  • best — fastest non-DNF time in seconds; -1 if all solves are DNF.
  • average — computed average in seconds; -1 for a DNF average; 0 if not all solves have been entered yet.
  • ao3 — any DNF produces a DNF average; otherwise the sum of all three times divided by 3.
  • ao5 — two or more DNFs produce a DNF average; one DNF eliminates the DNF and the best time, averaging the remaining three; no DNFs eliminate the best and worst, averaging the middle three.
Results are rounded to two decimal places.

sortWCA

// src/pages/Tournament/ResultsWCA.tsx
export const sortWCA = (a: any, b: any): number
Comparator function for WCA leaderboard ranking. Sort priority:
  1. Competitors with a valid positive average rank above those with a DNF average, who rank above those with no average.
  2. Among competitors with valid averages, sort ascending by average.
  3. Tiebreak on ascending best single.
  4. Final tiebreak on competitor name (alphabetical).
Pass directly to Array.prototype.sort.

parseTimeToSeconds

// src/pages/Tournament/ResultsWCA.tsx
export const parseTimeToSeconds = (val: string | number): number
Converts a user-entered time string or number into a plain seconds value suitable for storing in a TimeRecord.base.
  • number — returned as-is.
  • string with : — split on : and parsed as minutes * 60 + seconds (e.g. "1:07.78"67.78).
  • Other string — parsed as parseFloat; returns 0 if parsing fails or the value is empty.
Use this after reading the raw text from a time input field before constructing or updating a TimeRecord.

isDuplicateName

// src/common/validation.ts
export const isDuplicateName = (
  name: string,
  existingNames: string[],
  excludeIndex?: number
): boolean
Returns true if name (case-insensitive, trimmed) already exists in existingNames. Pass excludeIndex to skip a specific index — useful when validating an in-place edit where the item being edited should not be compared against itself.

findDuplicateNames

// src/common/validation.ts
export const findDuplicateNames = (names: string[]): string[]
Returns a deduplicated list of names that appear more than once in the input array. Comparison is case-insensitive and trims whitespace. Empty strings are ignored. Use this to perform bulk duplicate detection before saving a list of competitors or categories.

Build docs developers (and LLMs) love