Turfi Platform Documentation
Official Turfi documentation portal for users, admins, and developers.
Documentation Search
Search only within Turfi documentation pages.
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_adminandsuper_adminunlock full admin navigation (Play,Recruit,Compete,Media,Admin).association_adminunlocksRecruitandCompetebut notMediaorAdmin.club_adminunlocksRecruit,Compete, andMedia, but notAdmin.recruiterandscoutunlockRecruit.coachunlocksCompeteandMedia.- 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_organizationsv_clubsv_leaguesv_seasonsv_competitionsv_teamsv_playersv_player_registrationsv_team_competitionsv_venuesv_turfsv_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 & Competitionshub - 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
CompetitionsandGames. - 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 optionalplayer_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 Type | Database Table | UI Label |
|---|---|---|
| organization | organizations | Associations |
| league | leagues | Leagues |
| season | seasons | Seasons |
| competition | competitions | Competitions |
| club | clubs | Clubs |
| team | teams | Teams |
| player | players | Players |
| venue | venues | Venues |
| turf | turfs | Turfs |
| business | businesses | Businesses |
| address | addresses | Addresses |
| contact | contacts | Contacts |
Resolution Workflow
- Navigate to
Operations -> Entity Resolution. - Select the target registry from the grouped registry list.
- Review duplicate groups in the horizontal grid.
- Open a duplicate group from the actions column.
- Inspect the cell-by-cell comparison modal.
- Select the primary record to keep.
- Merge duplicates or delete unnecessary records.
- 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:
| Tab | Purpose |
|---|---|
| Overview | Quick actions and summary counts for the media pipeline |
| Videos | Registry of all registered source videos |
| Moments | Review and approval workspace for extracted or imported event candidates |
| Highlights | Management surface for generated clips and collection placement |
| Players | Resolution queue for player identity mapping |
| Processing | Engineering and operations monitoring tools |
Overview Tab
The Overview tab is the fast-entry surface for the media workflow.
Primary actions:
Broadcast GameUpload Game VideoUpload 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:
| Surface | Purpose |
|---|---|
| Video Instances | Inventory-first view of governed source and derived video rows |
| Video Processing | Processing console for inventory state, derived jobs, and error handling |
| Video Diagnostics | Deep inspection view that explains why a video is visible, duplicated, orphaned, or blocked |
| Video Sources | Shows all source video registrations and linked footage |
| Ingestion Jobs | Shows queue status for validation, transcode, poster extraction, event detection, and downstream media preparation |
| Event Imports | Shows raw provider or AI event payloads before canonical review |
| Moment Generation Queue | Shows approved events waiting for video_moments creation |
| Clip Generation Queue | Shows 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
- Source video is registered against a game.
- Ingestion jobs run and create raw import or processing records.
- Failed mappings and unknown players appear in admin queues.
- Reviewer resolves players, event mappings, or visibility/moderation flags.
- Approved events become canonical
game_events. - Approved events enqueue moment-generation work through
video_moment_generation_queue. - Workers consume
v_video_moment_generation_jobsand createvideo_moments. - Moments enqueue clip-generation work through
video_clip_generation_queue. - Workers consume
v_video_clip_generation_jobs, createhighlight_clips, and associate clips to players throughplayer_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.
| Button | Icon | Tooltip | Action |
|---|---|---|---|
| Import | Upload icon | "Import Data" | Opens the Import Engine for the current entity |
| Template | Download icon | "Download Template" | Downloads a portable CSV template for the entity |
| ID | Magnifying glass + "ID" label | "Find Entity ID" | Opens the ID Finder modal |
| Add | Text label | — | Opens 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:
- The grid resolves the current entity key (for example
players,venues,seasons). - It loads label rows from
api_ui_field_labels. - It selects the active language label (
label_en,label_fr,label_es). - If a translation is missing, it falls back to English.
- 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, andvenue_key - When a registry contract uses a plain relationship column such as
teamorclub, 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
typeandformat, but those values now come fromcompetition_types.keyandcompetition_formats.key - The import engine resolves
_key,_slug, and supported name columns back into*_idvalues 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
inactiveandarchived - Status is part of the standard registry lifecycle across all core registries
players,teams,venues, andturfsnow rely on indexed status fields for efficient filtering
Club licensing expectations:
clubs.license_level_idis 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 (
organizationsin the database), Leagues, and Clubs now ownphone,email, andwebsitedirectly 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, andProvince CityandProvinceare derived from the linkedaddressesrecord 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_idback to the entity - import templates for Associations, Leagues, and Clubs include
address_line1,address_line2,city,province,postal_code,country,phone,email, andwebsite - if import rows omit address fields, the entity import leaves
address_idasNULL - reusable
contactsrecords still exist for person-centric and shared operational workflows, withfirst_name,last_name, and generatedfull_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 whenentity_addresseslinks were created successfully
Typical workflow:
- Download template from
/api/templates/csv?entity={entity} - Populate rows offline
- Upload in Admin Imports
- Map CSV columns to destination fields
- Validate and run import
- 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_idandcompetition_format_id - competition filters, grids, and forms must resolve readable labels from
competition_typesandcompetition_formats - if the competition type is
Tournament,Showcase, orFriendly 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 baseteamstable - teams may be registered in multiple competitions, and a competition may include teams from multiple organizations
- the competition editor now exposes a
Teamstab where admins create and maintainteam_competitionsrows with optionalgroup_nameandseed - games are only valid when both
home_team_idandaway_team_idare registered inteam_competitionsfor the selected competition - scheduler inputs must come from the registered teams in
team_competitions;seedmay 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_registrationsso resolved player, team, club, competition, season, and organization labels stay flattened in the registry - competition registration grids should read through
v_team_competitionsso resolved competition, season, organization, team, and club labels stay flattened in the registry - player registration writes still target the base
player_registrationstable through a dedicated service path - competition registration writes should continue to use the
team_competitionsvalidation service so age-group, gender, and duplicate-registration rules stay enforced
For address/contact infrastructure work:
- reusable records live in centralized
addressesandcontactstables - entity-specific linkage lives in
entity_addressesandentity_contacts - relationship roles resolve through
address_rolesandcontact_roles - deprecated text columns
entity_addresses.address_roleandentity_contacts.contact_rolemust not be written by admin forms or import/export tooling
- Import
venues,turfs, andbusinesseswith summary fields and explicit resolver columns such asowner_organization_keyorvenue_key - Import
addressesandcontactsas standalone normalized records when needed - For Associations, Leagues, and Clubs, import address fields inline so the import engine can create
addressesrecords and assignaddress_id - 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:
202620272026 Summer2026 Indoor
Data Model
Table:
seasons
Fields:
idnamestart_dateend_datecreated_at
API Layer
The Admin interface reads season data through the registry view:
v_seasons
This view exposes the following fields:
idnamestart_dateend_datecreated_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_idis optional for standalone competitionscompetition_type_idresolves throughcompetition_types.idcompetition_format_idresolves throughcompetition_formats.id- if
competition_types.key = league,league_idmust be present - if
competition_types.keyistournament,showcase, orfriendly_series,league_idmay beNULL
Admin examples:
- League competition:
League1 Quebec Senior Menunder organizationSoccer Quebec, leagueLeague1 Quebec, season2026 Summer, typeLeague, formatRound Robin - Standalone competition:
Quebec U17 Showcaseunder organizationSoccer Quebec, league blank, season2026 Summer, typeShowcase, formatGroup + 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_modeselected_idsexcluded_idsfilter_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 EditBulk Delete- clear selection action
This toolbar is implemented as a shared component and reused across Admin Data pages.
Bulk Delete Workflow
- Admin selects rows (
manual,page, orall_filtered). - Admin clicks
Bulk Delete. - Confirmation modal displays impact count and irreversible warning.
- On confirm:
- manual/page mode deletes by explicit IDs.
- all-filtered mode resolves IDs from
filter_snapshot, applies exclusions, then deletes.
- Grid refreshes and selection is cleared.
Bulk Edit Workflow
- Admin selects rows.
- Admin clicks
Bulk Edit. - Modal prompts for:
- editable field selector
- new value
- On confirm:
- manual/page mode updates rows by explicit IDs.
- all-filtered mode resolves IDs from filter snapshot and updates all matched rows.
- 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:
idslugcreated_atupdated_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_modeselected_idsexcluded_idsfilter_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 EditBulk Delete- clear selection action
This toolbar is implemented as a shared component and reused across Admin Data pages.
Bulk Delete Workflow
- Admin selects rows (
manual,page, orall_filtered). - Admin clicks
Bulk Delete. - Confirmation modal displays impact count and irreversible warning.
- On confirm:
- manual/page mode deletes by explicit IDs.
- all-filtered mode resolves IDs from
filter_snapshot, applies exclusions, then deletes.
- Grid refreshes and selection is cleared.
Bulk Edit Workflow
- Admin selects rows.
- Admin clicks
Bulk Edit. - Modal prompts for:
- editable field selector
- new value
- On confirm:
- manual/page mode updates rows by explicit IDs.
- all-filtered mode resolves IDs from filter snapshot and updates all matched rows.
- 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:
idslugcreated_atupdated_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_playersv_teamsv_clubsv_leaguesv_competitionsv_businessesv_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:
organizationsleaguesseasonscompetitionsclubsteamsplayersplayer_registrationsteam_competitionsvenuesturfsbusinesses
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, andupdated_at normalized_nameis added where duplicate detection or search requires itstatusis now mandatory across registry entities with the lifecycle valuesactive,inactive, andarchived- 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, andleaguesnow store a database-maintainednormalized_name- the database computes it with
normalize_text()and keeps it current throughset_normalized_name()triggers on insert and update - duplicate resolution groups those registries by
normalized_namefirst and only falls back to displaynamewhen the canonical value is missing - import matching and entity resolver suggestions prioritize
normalized_namebefore 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_namedirectly
Normalized category rules:
- leagues may optionally filter and edit
gender_id - competitions filter and edit
age_group_idandgender_id - teams filter and edit
age_group_idandgender_id - players filter and edit
gender_id - lookup dropdowns must resolve through
age_groupsandgenders - 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.keygenders.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_groupandgender age_group_idandgender_idare 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:
idcreated_atupdated_at
Competition templates now include optional structured competition classification fields:
age_group_keygender_key