Last deployment:

Turfi Platform Documentation

Official Turfi documentation portal for users, admins, and developers.

Back to support

Documentation Search

Search only within Turfi documentation pages.

C

Admin Operations

Operator-facing registry workflows, lookup registries, games and competitions admin surfaces, and governance tooling.

Turfi needs operator-facing tooling that makes structured sports data editable without sacrificing governance or consistency. This document explains the registry workflows, admin data surfaces, and operational tools that support platform maintenance. It fits into the architecture as the human control layer for imports, registry review, bulk changes, and administrative governance. As more operational workflows are added, this file should remain the canonical guide for how administrators work with Turfi data.

Shared status legend: [docs/_shared/status-legend.md](./_shared/status-legend.md)


Admin Guide

Admin Roles and Permissions

Admin-level menus and pages are controlled by role checks in frontend navigation and route guards.

  • system_admin and super_admin unlock full admin navigation (Play, Recruit, Compete, Media, Admin).
  • association_admin unlocks Recruit and Compete but not Media or Admin.
  • club_admin unlocks Recruit, Compete, and Media, but not Admin.
  • recruiter and scout unlock Recruit.
  • coach unlocks Compete and Media.
  • Non-admin users remain restricted to Play.

Fail-safe behavior: if role detection fails or is unresolved, Turfi does not expose elevated menus and defaults to Play access only.

Admin Data Page Standard

Purpose

Admin Data pages provide administrators with a consistent interface to manage platform entities such as players, teams, clubs, competitions, and organizations.

These pages support both manual data management and bulk data ingestion.

All new data management pages must follow this standard layout and behavior.

Entity Management Coverage

The standardized Admin Data system currently manages:

  • Players
  • Teams
  • Clubs
  • Competitions
  • Venues
  • Turfs
  • Businesses
  • Seasons
  • Organizations
  • Regions
  • Associations (expansion target in current admin roadmap)

Each entity page follows the same UX contract and now reads through the admin registry view layer where available so operators see ordered, normalized data instead of raw foreign-key IDs.

Registry View Layer

Turfi now uses a three-layer data access model:

  • Base tables are the authoritative write layer for inserts, updates, deletes, imports, and merge operations.
  • api_* views are the platform API layer used by application-facing features and public/runtime services.
  • v_* views are the admin registry layer used by Data Registry grids, filters, detail panels, and registry exports.

Current registry read views:

  • v_organizations
  • v_clubs
  • v_leagues
  • v_seasons
  • v_competitions
  • v_teams
  • v_players
  • v_player_registrations
  • v_team_competitions
  • v_venues
  • v_turfs
  • v_businesses

These views surface resolved registry-facing values such as competition_type and competition_format while keeping the underlying _id columns in the base tables for writes and reference integrity.

Operational rule: if a registry grid is missing flattened address or relationship values after a successful import, fix the corresponding admin v_* read model rather than changing the authoritative write-table import path. The import may be correct while the grid remains incomplete because the registry view is missing a join or does not exist yet.

Page Location

Admin Data {Entity}

The Data workspace is a horizontal top-level menu item with two sub-pages:

  • Registries (/admin/data/registries): Manages core platform entities. Contains Players, Teams, Clubs, Competitions, Associations, Organizations, Regions, Venues, Turfs, Businesses, Addresses, Contacts, Seasons. Each registry page shows a data grid with filtering, CRUD, bulk edit, bulk delete, and icon-based action buttons.
  • Operations (/admin/data/operations): Tools for data ingestion, resolution, and governance. Contains Entity Resolution, Unresolved Entities, Import Engine, and Import History.

Routes:

  • Registries: /admin/data/registries, /admin/data/registries/players, /admin/data/registries/teams, /admin/data/registries/clubs, /admin/data/registries/competitions, /admin/data/registries/associations, /admin/data/registries/organizations, /admin/data/registries/regions, /admin/data/registries/venues, /admin/data/registries/turfs, /admin/data/registries/businesses, /admin/data/registries/addresses, /admin/data/registries/contacts, /admin/data/registries/seasons
  • Operations: /admin/data/operations, /admin/data/operations/duplicates, /admin/data/operations/entity-resolver, /admin/data/operations/import-engine, /admin/data/operations/import-history

Examples:

  • Admin -> Data -> Registries -> Players
  • Admin -> Data -> Registries -> Teams
  • Admin -> Data -> Registries -> Clubs
  • Admin -> Data -> Registries -> Competitions
  • Admin -> Data -> Registries -> Organizations
  • Admin -> Data -> Operations -> Entity Resolution
  • Admin -> Data -> Operations -> Import Engine

Games & Competitions Workspace

Status: IMPLEMENTED

Turfi now separates competition management into a domain workspace instead of routing operators straight into the raw registry layer.

This workspace exists so administrators can move through competition-focused tasks as a product flow:

  • open the Games & Competitions hub
  • choose competitions or games
  • inspect competition detail context
  • manage game records and game-level player/event views

Canonical routes:

  • Hub: /admin/games_competitions
  • Competitions list: /admin/games_competitions/competitions
  • Competition detail: /admin/games_competitions/competitions/{competitionId}
  • Games list: /admin/games_competitions/games
  • Create game: /admin/games_competitions/games/new
  • Game detail: /admin/games_competitions/games/{gameId}
  • Game player detail: /admin/games_competitions/games/{gameId}/players/{playerId}

Compatibility note: the legacy /admin/competitions/ routes still exist temporarily, but the admin menus and in-flow links now treat /admin/games_competitions/ as the canonical surface.

Domain vs registry separation

The competition registry remains available in Admin -> Data -> Registries -> Competitions at /admin/data/registries/competitions.

That registry is still the raw admin data surface for CRUD, bulk actions, imports, and structural maintenance. The Games & Competitions workspace is the domain surface that operators use when they want competition context rather than generic registry editing.

Current domain behavior:

  • The hub page is a lightweight navigation surface with separate cards for Competitions and Games.
  • The competitions list uses a domain-oriented grid that opens competition detail pages rather than registry edit views.
  • Competition detail combines leaderboard context, a games grid with a visible game count in the section heading, and a player stats grid backed by player_competition_stats.
  • The player stats surface intentionally uses strict database columns (player_id, team_id, games_played, goals, assists, minutes_played, yellow_cards, red_cards, plus optional player_name / team_name) while still tolerating null identity values in display.

Lookup Registries

Status: IMPLEMENTED

Turfi now exposes a dedicated Admin -> Lookup Registries workspace for canonical lookup maintenance.

This workspace exists because lookup-backed metadata now drives more of the platform model than free-text admin forms did in earlier phases. Separating lookup editing from the main entity registries keeps reference data governance explicit and reduces the risk of treating enum-like values as ordinary entities.

Route:

  • /admin/lookup-registries

Current lookup surfaces include:

  • Age Groups
  • Club Licenses
  • Competition Formats
  • Competition Types
  • Genders
  • Languages
  • Types
  • Values

Operational rule: lookup registries are the maintenance surface for canonical option sets and translated labels. Entity registries should consume these values through ids/keys rather than duplicating free-text category fields in ad hoc forms.

Entity Resolution

Status: IMPLEMENTED

The Entity Resolution page is the duplicate-review workspace for registry data. It lives at:

Admin -> Operations -> Entity Resolution

This workflow exists so administrators can inspect likely duplicates without leaving the Data workspace, compare records side by side, and safely consolidate them into one canonical row.

Supported Registry Entities

These duplicate-resolution targets correspond directly to Data Registry entities:

Entity TypeDatabase TableUI Label
organizationorganizationsAssociations
leagueleaguesLeagues
seasonseasonsSeasons
competitioncompetitionsCompetitions
clubclubsClubs
teamteamsTeams
playerplayersPlayers
venuevenuesVenues
turfturfsTurfs
businessbusinessesBusinesses
addressaddressesAddresses
contactcontactsContacts

Resolution Workflow

  1. Navigate to Operations -> Entity Resolution.
  2. Select the target registry from the grouped registry list.
  3. Review duplicate groups in the horizontal grid.
  4. Open a duplicate group from the actions column.
  5. Inspect the cell-by-cell comparison modal.
  6. Select the primary record to keep.
  7. Merge duplicates or delete unnecessary records.
  8. Confirm the operation after reviewing dependency impact.

The duplicate-group grid uses the same horizontal admin list language as the registry surfaces and includes:

  • Group ID
  • Records
  • Confidence score
  • Preview name
  • Context
  • Actions

The modal stays in popup mode rather than navigating away from the list. It provides:

  • side-by-side record comparison
  • field-level inspection
  • per-record "Keep this record" selection
  • dependency preview before merge
  • merge, delete, and skip actions

This keeps the workflow fast for operators because they can resolve duplicate groups in sequence without losing page context.

Media

Status: IMPLEMENTED

Turfi now exposes a dedicated Admin -> Media workspace module for operator control of the video and highlight pipeline.

This module exists so administrators can supervise video ingestion, extracted event review, player identity resolution, clip publishing, and queue health without treating media workflows as generic registry edits.

Purpose

Media is the operational control center for the Media Engine.

It gives administrators one governed place to:

  • register and review source videos
  • monitor ingestion and processing health
  • approve or correct extracted moments
  • resolve player identity from jersey numbers or partial labels
  • manage highlight clips and collections

Navigation Structure

Route:

  • /admin/media-operations

Tabs:

TabPurpose
OverviewQuick actions and summary counts for the media pipeline
VideosRegistry of all registered source videos
MomentsReview and approval workspace for extracted or imported event candidates
HighlightsManagement surface for generated clips and collection placement
PlayersResolution queue for player identity mapping
ProcessingEngineering and operations monitoring tools

Overview Tab

The Overview tab is the fast-entry surface for the media workflow.

Primary actions:

  • Broadcast Game
  • Upload Game Video
  • Upload Highlight

Summary counts surface:

  • videos
  • moments
  • highlights
  • unresolved players

Videos Tab

The Videos tab manages all registered source videos.

It is the object-first registry for:

  • uploaded match videos
  • linked provider footage
  • source ownership and ingestion status
  • video-to-game context

Moments Tab

The Moments tab manages extracted or imported video events before and after approval.

It exists so operators can:

  • inspect proposed moment types
  • review confidence and player context
  • approve canonical mappings
  • trigger downstream clip generation workflows

Highlights Tab

The Highlights tab manages generated clips and highlight collections.

This is the operator surface for:

  • publishing clips
  • assigning clips to collections
  • reviewing visibility state
  • opening generated playback outputs

Players Tab

The Players tab resolves unresolved player detections.

Typical cases include:

  • jersey-number-only detections
  • partial provider names
  • ambiguous team/player mappings

The purpose is to prevent unverified player identity from leaking into canonical player media or downstream stats.

Processing Tab

The Processing tab contains the engineering-oriented queue and monitoring surfaces that support the rest of the media workflow.

Processing tools include:

SurfacePurpose
Video InstancesInventory-first view of governed source and derived video rows
Video ProcessingProcessing console for inventory state, derived jobs, and error handling
Video DiagnosticsDeep inspection view that explains why a video is visible, duplicated, orphaned, or blocked
Video SourcesShows all source video registrations and linked footage
Ingestion JobsShows queue status for validation, transcode, poster extraction, event detection, and downstream media preparation
Event ImportsShows raw provider or AI event payloads before canonical review
Moment Generation QueueShows approved events waiting for video_moments creation
Clip Generation QueueShows generated moments waiting for highlight_clips extraction

These operational views connect directly to the Media Engine, Match Intelligence Engine, Broadcast Engine, and player-highlight pipeline.

Review Workflow

  1. Source video is registered against a game.
  2. Ingestion jobs run and create raw import or processing records.
  3. Failed mappings and unknown players appear in admin queues.
  4. Reviewer resolves players, event mappings, or visibility/moderation flags.
  5. Approved events become canonical game_events.
  6. Approved events enqueue moment-generation work through video_moment_generation_queue.
  7. Workers consume v_video_moment_generation_jobs and create video_moments.
  8. Moments enqueue clip-generation work through video_clip_generation_queue.
  9. Workers consume v_video_clip_generation_jobs, create highlight_clips, and associate clips to players through player_highlight_items.

Reusable Service Rule

The admin module must not own media business logic directly.

Media workflow actions are implemented in reusable platform services so they can later be consumed by:

  • Studio
  • Club
  • Player
  • Recruiter

Representative service responsibilities:

  • createVideoSource()
  • startVideoIngestion()
  • importVideoEvents()
  • approveMoment()
  • generateHighlightClip()
  • resolvePlayerIdentity()
  • publishHighlight()

UI Design Principle

Media follows the same UX language used by Data Registry:

  • entity-based grids
  • filters
  • status badges
  • action menus
  • registry-style navigation

The goal is to make media management feel consistent with the rest of the Turfi admin workspace instead of introducing a disconnected pipeline console.

Header Actions

Each Data page displays icon-based action buttons in the top right of the page header. Buttons use neutral monochrome icons with tooltips for accessibility.

ButtonIconTooltipAction
ImportUpload icon"Import Data"Opens the Import Engine for the current entity
TemplateDownload icon"Download Template"Downloads a portable CSV template for the entity
IDMagnifying glass + "ID" label"Find Entity ID"Opens the ID Finder modal
AddText labelOpens a form to create a new record
Resolve"Resolve" text"Run auto entity resolution"Calls auto_resolve_entities()

Import (Upload icon, tooltip: "Import Data")

Opens the Import Engine for the current entity. Route: /admin/imports/new?entity={entity}

Template (Download icon, tooltip: "Download Template")

Downloads a portable CSV template. Endpoint: /api/templates/csv?entity={entity}

ID Finder (Magnifying glass + "ID", tooltip: "Find Entity ID")

Opens a reusable entity lookup utility that helps administrators search for and copy entity IDs by name while preparing imports or troubleshooting relationships.

Add

Opens a form allowing administrators to manually create a new record for the entity (e.g. Add Player, Add Team).

Resolve

Triggers auto_resolve_entities() to automatically attach entity IDs when text names match existing records. Replaces the former "Run Auto Resolver" button. Available on Operations and registry pages.

The ID Finder queries entity_search through /api/entity-search?q= and returns up to 50 matches with:

  • entity type
  • entity name
  • parent context
  • ID

Each result row supports quick copy actions (Copy ID, Copy Name) so import preparation does not require manual UUID discovery.

Field Label Mapping

Admin Data pages must never display raw database column names.

Instead they use the Field Label Registry.

The label registry is stored in the table:

ui_field_labels

This table maps entity field keys to multilingual display labels.

Lookup-backed admin fields follow the same rule. Tables such as club_license_levels store stable keys, while the readable labels shown in forms, grids, and filters are sourced from the centralized ui_field_labels system.

Supported languages:

  • English
  • French
  • Spanish

How it works in the UI:

  1. The grid resolves the current entity key (for example players, venues, seasons).
  2. It loads label rows from api_ui_field_labels.
  3. It selects the active language label (label_en, label_fr, label_es).
  4. If a translation is missing, it falls back to English.
  5. If no label record exists, it formats the field key to a readable title.

Example mapping:

Entity: players Field: first_name

English: First Name French: Prenom Spanish: Nombre

Admin grids retrieve label definitions from the view:

api_ui_field_labels

If a translation is missing the interface falls back to the English label or the field key.

This ensures the interface remains readable even when translations are incomplete.

Per-lookup translation tables should not be introduced for registry metadata. For example, club_license_level_translations is no longer part of the target architecture because club license labels belong in the shared multilingual label system.

Grid Layout

Each Data page contains a data grid displaying entity records.

Typical grid features include:

  • Search bar
  • Sortable columns
  • Pagination
  • Row actions
  • Header action bar (Import / Template / ID Finder / Add)

Row actions include:

  • View
  • Edit
  • Delete

Large Dataset Handling

Some entities such as players will eventually contain hundreds of thousands of records.

To maintain performance, these pages use filtered loading.

Records are not loaded automatically when the page opens.

Instead users select filters before loading the grid.

Typical filters include:

  • Organization
  • Competition
  • Club
  • Team
  • Region

A global search field may also be provided for direct entity lookup.

Example:

Player Search

Once filters are selected the system loads results using paginated queries.

CSV Import Integration

Data pages integrate directly with the Import Engine.

Administrators can:

  • Download a template
  • Populate data in a CSV file
  • Import the data through the Import Engine

This workflow enables both small scale manual entry and large scale dataset imports.

Templates define a practical import contract per entity:

  • Allowed columns (what can be mapped)
  • Operationally required columns (validated during import)
  • Example value shapes (date, boolean, normalized references, free text)

Portable export rules:

  • Registry exports use the same normalized column model as templates so exported files can be re-imported into another Turfi environment
  • Entity relationships export human-readable references instead of UUIDs, preferring stable keys such as organization_key, season_key, and venue_key
  • When a registry contract uses a plain relationship column such as team or club, the export contains the normalized readable reference rather than the internal *_id
  • Lookup-backed fields export lookup keys rather than lookup table IDs
  • Competition exports keep portable CSV columns type and format, but those values now come from competition_types.key and competition_formats.key
  • The import engine resolves _key, _slug, and supported name columns back into *_id values during preview and execution

Examples:

  • Players: first_name,last_name,birth_year,team,club
  • Teams: name,club,age_group,gender
  • Competitions: key,name,slug,organization_key,league_key,season_key,age_group_key,gender_key,type,format,start_date,end_date
  • Venues: key,name,slug,owner_organization_key,venue_type,city,province,country,website,status
  • Turfs: key,name,slug,venue_key,surface_type,field_format,is_indoor,is_lit,length_m,width_m,status
  • Businesses: key,name,slug,venue_key,business_type,city,province,country,website,status
  • Addresses: key,name,slug,label,address_line1,address_line2,city,province,postal_code,country,status
  • Contacts: first_name,last_name,title,email,phone,status
  • Seasons: name,start_date,end_date

Registry lifecycle expectations:

  • Registry pages default to showing status = active
  • Operators must be able to filter for inactive and archived
  • Status is part of the standard registry lifecycle across all core registries
  • players, teams, venues, and turfs now rely on indexed status fields for efficient filtering

Club licensing expectations:

  • clubs.license_level_id is optional descriptive metadata
  • club license options come from club_license_levels
  • labels for those options come from ui_field_labels
  • licensing must not be presented as a compliance gate for leagues, competitions, or team eligibility

Centralized address/contact expectations:

  • Associations (organizations in the database), Leagues, and Clubs now own phone, email, and website directly on the entity row
  • those same entities store location through address_id -> addresses.id
  • registry grids for those entities now expose Phone, Email, Website, City, and Province
  • City and Province are derived from the linked addresses record rather than treated as the authoritative location fields on the entity row
  • add/edit forms create or update the linked address record and then persist the resulting address_id back to the entity
  • import templates for Associations, Leagues, and Clubs include address_line1, address_line2, city, province, postal_code, country, phone, email, and website
  • if import rows omit address fields, the entity import leaves address_id as NULL
  • reusable contacts records still exist for person-centric and shared operational workflows, with first_name, last_name, and generated full_name
  • venues remain facility rows while turfs remain child play surfaces; turf imports and forms must always resolve a parent venue
  • venue and turf validation should explicitly check typed-column coercion before execution so blank strings do not reach boolean or integer columns during import
  • venue/turf operator verification should confirm the Venues grid is reading through v_venues; otherwise address joins can appear blank even when entity_addresses links were created successfully

Typical workflow:

  1. Download template from /api/templates/csv?entity={entity}
  2. Populate rows offline
  3. Upload in Admin Imports
  4. Map CSV columns to destination fields
  5. Validate and run import
  6. Review job results and row-level warnings/errors

Competition registry notes:

  • every competition row in Admin Data must resolve an owning organization
  • league context is optional unless the competition type is League
  • competition type and competition format are stored through competition_type_id and competition_format_id
  • competition filters, grids, and forms must resolve readable labels from competition_types and competition_formats
  • if the competition type is Tournament, Showcase, or Friendly Series, operators may leave the league reference blank
  • competition imports and exports must support both league-linked and standalone competition rows
  • competition team registration is managed through team_competitions, not through the base teams table
  • teams may be registered in multiple competitions, and a competition may include teams from multiple organizations
  • the competition editor now exposes a Teams tab where admins create and maintain team_competitions rows with optional group_name and seed
  • games are only valid when both home_team_id and away_team_id are registered in team_competitions for the selected competition
  • scheduler inputs must come from the registered teams in team_competitions; seed may be used to balance groups or distribute fixtures
  • competition registration imports use the normalized contract team_key, competition_key, group_name, seed

Registration registry notes:

  • player registrations are now exposed through /admin/data/registries/player-registrations
  • competition registrations are now exposed through /admin/data/registries/competition-registrations
  • player registration grids should read through v_player_registrations so resolved player, team, club, competition, season, and organization labels stay flattened in the registry
  • competition registration grids should read through v_team_competitions so resolved competition, season, organization, team, and club labels stay flattened in the registry
  • player registration writes still target the base player_registrations table through a dedicated service path
  • competition registration writes should continue to use the team_competitions validation service so age-group, gender, and duplicate-registration rules stay enforced

For address/contact infrastructure work:

  • reusable records live in centralized addresses and contacts tables
  • entity-specific linkage lives in entity_addresses and entity_contacts
  • relationship roles resolve through address_roles and contact_roles
  • deprecated text columns entity_addresses.address_role and entity_contacts.contact_role must not be written by admin forms or import/export tooling
  1. Import venues, turfs, and businesses with summary fields and explicit resolver columns such as owner_organization_key or venue_key
  2. Import addresses and contacts as standalone normalized records when needed
  3. For Associations, Leagues, and Clubs, import address fields inline so the import engine can create addresses records and assign address_id
  4. Link additional reusable addresses/contacts explicitly from the admin edit workflow where that relationship model still applies

Design Consistency

All Admin Data pages must follow the same structure and interaction patterns.

This ensures that administrators can manage different entities using a familiar interface.

When new entities are added to the platform, their Data pages should replicate this standard layout and behavior.

Managing Seasons

The Seasons entity defines the time boundaries used by competitions, games, player statistics, and analytics. A season represents a defined competition period such as a calendar year or league cycle.

Example seasons:

  • 2026
  • 2027
  • 2026 Summer
  • 2026 Indoor

Data Model

Table:

  • seasons

Fields:

  • id
  • name
  • start_date
  • end_date
  • created_at

API Layer

The Admin interface reads season data through the registry view:

  • v_seasons

This view exposes the following fields:

  • id
  • name
  • start_date
  • end_date
  • created_at

Admin Interface

Seasons are managed from:

Admin -> Data -> Seasons

The page provides:

  • Grid view of all seasons
  • Import functionality via CSV
  • Manual creation of a season
  • CSV template download

Grid Fields:

  • Season
  • Start Date
  • End Date
  • Created

Import System

Seasons can be imported through the platform Import Engine.

CSV template format:

name,start_date,end_date

Example:

2026,2026-01-01,2026-12-31

Relationship with Competitions

Competitions reference seasons through:

  • competitions.season_id

Example structure:

  • Season 2026
  • Competition U15 LDIR
  • Competition U17 LDIR

This ensures historical separation between competition cycles.

Design Principles

Seasons act as the temporal container for competitions, games, and player statistics. This allows Turfi to maintain historical data integrity and support multiple concurrent competition cycles.

Managing Competitions

The Competitions registry manages the records that sit between governance and actual games. In Turfi, a competition always belongs to an organization and may optionally belong to a league.

Hierarchy:

Organization → League (optional) → Competition → Games

Competition rules:

  • every competition must resolve organization_id
  • league_id is optional for standalone competitions
  • competition_type_id resolves through competition_types.id
  • competition_format_id resolves through competition_formats.id
  • if competition_types.key = league, league_id must be present
  • if competition_types.key is tournament, showcase, or friendly_series, league_id may be NULL

Admin examples:

  • League competition: League1 Quebec Senior Men under organization Soccer Quebec, league League1 Quebec, season 2026 Summer, type League, format Round Robin
  • Standalone competition: Quebec U17 Showcase under organization Soccer Quebec, league blank, season 2026 Summer, type Showcase, format Group + Knockout

Admin Data Bulk Operations

Purpose

Admin Data Bulk Operations allow administrators to update or remove large sets of records safely without opening each row individually.

This is designed for operational workflows such as correcting imported values, applying common field changes, or removing invalid datasets in batches.

Selection Architecture

Bulk operations use a selection state model that supports page-level and filter-level intent:

  • selection_mode
  • selected_ids
  • excluded_ids
  • filter_snapshot

Selection modes:

  • manual: user checks individual rows.
  • page: header checkbox selects rows currently visible in the grid page.
  • all_filtered: user selects all rows matching active filters, including rows not currently loaded.

When page selection is active, the UI can promote to all-filtered mode:

  • Example: 25 selected on this page. Select all 124 players matching this filter.

Bulk Action Toolbar

When selection is not empty, a bulk toolbar appears above the grid:

  • selected count
  • Bulk Edit
  • Bulk Delete
  • clear selection action

This toolbar is implemented as a shared component and reused across Admin Data pages.

Bulk Delete Workflow

  1. Admin selects rows (manual, page, or all_filtered).
  2. Admin clicks Bulk Delete.
  3. Confirmation modal displays impact count and irreversible warning.
  4. On confirm:
  • manual/page mode deletes by explicit IDs.
  • all-filtered mode resolves IDs from filter_snapshot, applies exclusions, then deletes.
  1. Grid refreshes and selection is cleared.

Bulk Edit Workflow

  1. Admin selects rows.
  2. Admin clicks Bulk Edit.
  3. Modal prompts for:
  • editable field selector
  • new value
  1. On confirm:
  • manual/page mode updates rows by explicit IDs.
  • all-filtered mode resolves IDs from filter snapshot and updates all matched rows.
  1. Grid refreshes and selection is cleared.

Filter-Based Selection

all_filtered mode relies on a serialized filter_snapshot captured from the current filter bar state. This allows the backend to apply the same filter logic during bulk execution.

Examples:

  • Players by organization + team + search term
  • Venues/businesses by organization/province/city
  • Seasons by start and end date windows

Safety Mechanisms

System fields are protected from bulk editing and excluded from editable field selectors:

  • id
  • slug
  • created_at
  • updated_at

Bulk actions are executed server-side through authenticated admin services, with per-entity write-table boundaries and explicit error reporting.

Entity Coverage

Bulk operations are implemented on shared Admin Data grid infrastructure and apply consistently across current Admin Data entities:

  • players
  • teams
  • clubs
  • competitions
  • venues
  • businesses
  • seasons

Additional entities inherit bulk capabilities automatically when using the standard Admin Data grid pattern.

Admin Data Bulk Operations

Purpose

Admin Data Bulk Operations allow administrators to update or remove large sets of records safely without opening each row individually.

This is designed for operational workflows such as correcting imported values, applying common field changes, or removing invalid datasets in batches.

Selection Architecture

Bulk operations use a selection state model that supports page-level and filter-level intent:

  • selection_mode
  • selected_ids
  • excluded_ids
  • filter_snapshot

Selection modes:

  • manual: user checks individual rows.
  • page: header checkbox selects rows currently visible in the grid page.
  • all_filtered: user selects all rows matching active filters, including rows not currently loaded.

When page selection is active, the UI can promote to all-filtered mode:

  • Example: 25 selected on this page. Select all 124 players matching this filter.

Bulk Action Toolbar

When selection is not empty, a bulk toolbar appears above the grid:

  • selected count
  • Bulk Edit
  • Bulk Delete
  • clear selection action

This toolbar is implemented as a shared component and reused across Admin Data pages.

Bulk Delete Workflow

  1. Admin selects rows (manual, page, or all_filtered).
  2. Admin clicks Bulk Delete.
  3. Confirmation modal displays impact count and irreversible warning.
  4. On confirm:
  • manual/page mode deletes by explicit IDs.
  • all-filtered mode resolves IDs from filter_snapshot, applies exclusions, then deletes.
  1. Grid refreshes and selection is cleared.

Bulk Edit Workflow

  1. Admin selects rows.
  2. Admin clicks Bulk Edit.
  3. Modal prompts for:
  • editable field selector
  • new value
  1. On confirm:
  • manual/page mode updates rows by explicit IDs.
  • all-filtered mode resolves IDs from filter snapshot and updates all matched rows.
  1. Grid refreshes and selection is cleared.

Filter-Based Selection

all_filtered mode relies on a serialized filter_snapshot captured from the current filter bar state. This allows the backend to apply the same filter logic during bulk execution.

Examples:

  • Players by organization + team + search term
  • Venues/businesses by organization/province/city
  • Seasons by start and end date windows

Safety Mechanisms

System fields are protected from bulk editing and excluded from editable field selectors:

  • id
  • slug
  • created_at
  • updated_at

Bulk actions are executed server-side through authenticated admin services, with per-entity write-table boundaries and explicit error reporting.

Entity Coverage

Bulk operations are implemented on shared Admin Data grid infrastructure and apply consistently across current Admin Data entities:

  • players
  • teams
  • clubs
  • competitions
  • venues
  • businesses
  • seasons

Additional entities inherit bulk capabilities automatically when using the standard Admin Data grid pattern.

Data Management Interface

Status: IMPLEMENTED

Purpose

The Data Management Interface provides administrative control over all core entities stored in the Turfi platform.

This interface lives in the Admin workspace and allows administrators to browse, create, edit, and delete platform entities through structured data grids.

Location in application

Admin Data

Navigation structure

Admin Data Players Teams Clubs Competitions Seasons Venues Turfs Businesses Organizations Regions

Additional entities will be added over time.

Each entity page displays a data grid showing records stored in the platform database.

Typical grid features

  • Search bar
  • Column sorting
  • Pagination
  • Row level actions

Row actions

  • View
  • Edit
  • Delete

Each page also provides a Create button that opens a form allowing manual entry of a new record.

These pages act as the primary interface for managing platform data and verifying relationships between entities.

Example

Players page

Columns typically include

  • Player Name
  • Slug
  • Team
  • Club
  • Position
  • Created At

These pages query the registry views created for the admin layer such as:

  • v_players
  • v_teams
  • v_clubs
  • v_leagues
  • v_competitions
  • v_businesses
  • v_seasons

Read-path normalization is handled in the registry views, while platform-facing endpoints continue to use api_* views and field labels are still resolved through api_ui_field_labels so grid headers and form labels remain human-readable across languages.

Supported admin label languages:

  • English
  • French
  • Spanish

The Admin Data interface provides a controlled environment for seeding data and validating the system before public features are enabled.

Audit System

Status: IMPLEMENTED (architecture and workflows); implementation detail varies by schema

The Audit System is Turfi's record of who did what, when. When an admin archives a player, approves a profile claim, or runs a bulk import, the Audit System captures that action with enough context to reconstruct timelines, investigate incidents, and demonstrate compliance. It answers: What happened, and who was responsible?

Platforms that handle sensitive data and administrative power need accountability. Without audit trails, mistakes and misuse are hard to trace. The Audit System exists so that critical actions—claim approvals, moderation decisions, bulk deletes, imports—are logged and reviewable, both for operations and for governance.

This system owns audit log persistence (actor, target, action type, timestamp, metadata), integration points with claim workflows, moderation, bulk ops, and imports, query and review surfaces for operational monitoring, and the contract that sensitive actions produce audit records.

The Audit System observes actions from other engines rather than performing them. Player Engine claim approvals, Media moderation decisions, Admin bulk operations, and Import job execution all feed into audit. Infrastructure provides triggers and execution paths; Platform Systems may provide notification or alerting on audit events.

Future work includes richer query and export tools, retention policies, federation-scoped audit views, and integration with external compliance or SIEM systems as the platform matures.


Purpose

The Audit System provides traceability and accountability for critical platform actions performed by administrators, moderators, and automated processes. It records administrative actions, player claim approvals and rejections, media moderation decisions, bulk data operations, and data import operations.

Audit Coverage

  • Administrative actions
  • Player claim approvals and rejections
  • Media moderation decisions
  • Bulk data operations
  • Data import operations

Architecture Overview

Audit records are generated through triggers, workflows, and administrative tools. Entry points include database triggers, admin workflow actions, edge-function execution paths, and server-side orchestration logic. Each record stores actor, target entity, action type, timestamp, and operation context/metadata.

Key Database Models

  • audit_logs (actor, target, action type, timestamp, operation metadata)
  • player_profile_claims (claim lifecycle and reviewer decisions)
  • admin_media_moderation_queue (moderation review queue and outcomes)
  • activity_feed (selected administrative events for operational monitoring)

Key Workflows

  • Administrative actions: audit entry persisted when admins approve media, delete/update records, or resolve claims
  • Player claim approvals/rejections: claim lifecycle captures submitter and reviewer identity plus decision metadata
  • Media moderation decisions: approvals/removals logged for accountability
  • Bulk data operations: bulk edits, deletes, and import-impacting actions persist summary audit records
  • Data import operations: large-scale import execution traced for governance and rollback capability
  • Operational review: admin tools query audit records to reconstruct timeline, investigate incidents, validate policy compliance

Interactions with Other Engines

  • Player Engine: logs claim approvals/rejections and ownership-link changes
  • Media Engine: logs moderation outcomes and media governance actions
  • Competition Engine: records administrative corrections when applicable
  • Import System: records large-scale import and data-mutation operations
  • Infrastructure Engine: supplies trigger/function and edge-function mechanisms for audit capture

Data Registry System

The Data Registry system is the operational layer that manages Turfi reference entities.

Core registries:

  • organizations
  • leagues
  • seasons
  • competitions
  • clubs
  • teams
  • players
  • player_registrations
  • team_competitions
  • venues
  • turfs
  • businesses

Current grouped registry navigation:

  • Governance: Associations, Leagues, Seasons, Competitions
  • Participants: Clubs, Teams, Players
  • Registrations: Player Registrations, Competition Registrations
  • Infrastructure: Venues, Turfs
  • Commercial: Businesses
  • Shared Data: Addresses, Contacts

Registry standardization rules:

  • Core registries follow a shared identity structure built around id, key, slug, name, status, created_at, and updated_at
  • normalized_name is added where duplicate detection or search requires it
  • status is now mandatory across registry entities with the lifecycle values active, inactive, and archived
  • Registry screens should load active rows by default while still exposing inactive and archived filters
  • Historical cleanup should prefer lifecycle changes over destructive deletion

Normalized Name System:

  • organizations, clubs, and leagues now store a database-maintained normalized_name
  • the database computes it with normalize_text() and keeps it current through set_normalized_name() triggers on insert and update
  • duplicate resolution groups those registries by normalized_name first and only falls back to display name when the canonical value is missing
  • import matching and entity resolver suggestions prioritize normalized_name before looser slug or display-name comparisons
  • registry search for Associations, Clubs, and Leagues includes normalized_name, making search tolerant to punctuation, accents, and capitalization differences
  • admin forms, imports, and edits must never write normalized_name directly

Normalized category rules:

  • leagues may optionally filter and edit gender_id
  • competitions filter and edit age_group_id and gender_id
  • teams filter and edit age_group_id and gender_id
  • players filter and edit gender_id
  • lookup dropdowns must resolve through age_groups and genders
  • visible labels for those dropdowns must come from the centralized multilingual label layer

Registries support:

  • entity storage
  • admin editing
  • import template generation
  • import resolution
  • duplicate resolution

Competition registry support includes structured age_group and gender attributes. These fields are exposed in:

  • grid columns
  • filter dropdowns
  • create and edit forms

Competition filters can now narrow results by:

  • age group
  • gender

These selectors are populated from the normalized lookup tables:

  • age_groups.key
  • genders.key

This allows the UI to support youth competition formats such as MU17 and FU17 through structured attributes rather than hard-coded text values.

Club licensing follows the same lookup-first approach. club_license_levels stores the canonical license keys, clubs.license_level_id stores the optional reference, and multilingual display labels are resolved through ui_field_labels instead of a dedicated translation table.

Category transition note:

  • teams still retain the legacy text fields age_group and gender
  • age_group_id and gender_id are now the authoritative values for teams
  • the application should prefer the lookup ids for filtering, editing, and assignment logic
  • the database sync trigger keeps the text fields aligned while the transition remains open

Template Generation System

Import templates should be generated from the database schema.

The system should read table structure directly using Supabase CLI.

Templates must:

  • include required fields
  • include foreign keys
  • exclude system columns

Examples of excluded columns:

  • id
  • created_at
  • updated_at

Competition templates now include optional structured competition classification fields:

  • age_group_key
  • gender_key