npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

fansunited-widget-sports-data

v1.2.0

Published

Welcome to Fans United Sport Data Management Widget. The widget lets staff members with sufficient permissions browse the shared sport catalogue — competitions, teams, athletes, coaches, staff, venues and countries — and tailor it for a single client. You

Downloads

650

Readme

Sport Data Management Widget

Welcome to Fans United Sport Data Management Widget. The widget lets staff members with sufficient permissions browse the shared sport catalogue — competitions, teams, athletes, coaches, staff, venues and countries — and tailor it for a single client. You can use this widget as it is and embed it in your platform, or you can use the Management Panel to manage sport data. Entities can be renamed and translated, given logos, brand colors, socials, related entities, season history and provider references, and any entity can be overridden for one client without disturbing the version every other client reads. With successful installation, you can search, create, edit and override sport entities, and see exactly how each one will be found by the Search API.

Installation and usage

The easiest way to use fansunited-widget-sports-data is to install it from npm as follows:

npm install fansunited-widget-sports-data

or:

yarn add fansunited-widget-sports-data

Here is an example how you can integrate Fans United Sport Data Management Widget:

import 'fansunited-widget-sports-data/style.css';
import { SportsDataManager } from 'fansunited-widget-sports-data';

const App = () => {
  return (
    <SportsDataManager
      fansUnitedApiKey={'your-api-key'}
      fansUnitedClientId={'your-client-id'} />
  );
}

Notes

  • The widget is bundled as a React component and its usage is as simple as the above example

  • Don't worry about your project's CSS. The widget's styling is done with Joy UI which is based on headless unstyle react components. This means that the widget's CSS will NOT affect yours. One important thing is that you need to import style.css file from package folder:

import 'fansunited-widget-sports-data/style.css';

N.B: If you are using an entry point for importing all your stylings, you need to import fansunited-widget-sports-data/style.css there as well

Props

type SportsDataManagerProps = {
  fansUnitedApiKey: string;
  fansUnitedClientId: string;
  fansUnitedClientName?: string;
  fansUnitedEnvironment?: string;
  labels?: LabelsModel;
  hideSignOutButton?: boolean;
};

Fans United Sport Data Management Widget depends on Fans United JS SDK and Fans United Client API. Thats why fansUnitedApiKey and fansUnitedClientId are required. They will be provided to you from Fans United team. For other optional props here is some information:

  • fansUnitedClientName - the name used whenever the widget refers to the client in copy, for example "Sport data for Ludogorets only". Display only, and never used to address an index — if no value is given the client ID is shown instead.

  • fansUnitedEnvironment - You can run the widget in the following environments: staging and production. If no value given it will be set to production (prod).

  • labels - you can easily translate all labels, UI messages, descriptions, placeholders and etc. with this prop. By default, Fans United Sport Data Management Widget is available in English so if no value is given, the widget will be translated in English.

  • hideSignOutButton - The prop is used to hide the sign out button which is positioned on the top right corner on the main screen of the widget.

Unlike the other management widgets, there is no fansUnitedLanguage prop. This widget reads and writes sport entities through the Ikebana proxy and calls no localized sports API, so there is no response language to choose. The widget's own wording is set with labels, and per-language entity names are data you edit in the Translations section — neither depends on such a prop.

The catalogue and overrides

This is the one concept worth reading before using the widget, because it decides who sees your changes.

Sport data lives in two places:

| Scope | What it is | Who it affects | | --- | --- | --- | | Catalogue | The global, shared catalogue. | Every client on the platform. | | Overrides | One client's own overlay on top of it. | That client only. |

An entity read by a client is the global entity, unless that client has an override for it — in which case the override replaces it entirely.

An override is a client-owned copy of a global entity. It keeps the same ID as the global entity, which is what lets it shadow it. It starts from the global values at the moment you create it, and from then on it is independent: later changes to the global entity no longer reach that client. Removing the override deletes only the copy and the client falls back to the global entity again — the global entity is never touched by any of this.

You can also create an entity directly in the Overrides scope. That is not an override of anything: it is a client-only entity with a newly generated ID, it never enters the global catalogue, and no other client can see it.

Entities can be overridden one at a time from a row, or in bulk by ticking several rows. Rows already overridden are badged and cannot be selected, since a second override would only overwrite the first.

Overrides are removed the same way — from a row, or by ticking several in the Overrides scope and removing them together. Removing one deletes only the client's copy; the entity itself is untouched and that client goes back to reading the global version.

Permissions

The widget reads a roles custom claim from the signed-in account's token.

| Role | Catalogue | Overrides | | --- | --- | --- | | Platform Operator | Read and write | Read and write, for every client | | Client Admin | Read-only | Read and write, for their own client |

What you can manage

Seven entity types: competitions, teams, athletes, coaches, staff, venues and countries. Every type except countries is scoped to a sport — Football, Basketball, Tennis, Hockey or American Football — which is encoded in its ID and cannot be changed after it is created.

Depending on the type, an entity carries some of: General (naming), Translations, Assets, Branding, Socials, Related, Season History, Additional Information and Provider References. Only the sections that apply to a type are offered.

Each entity also has a How people find this entity panel, which shows in plain language how the Search API will match it: the name, the other terms it answers to, the links apps can filter by, and the popularity score that breaks ties. It updates as you edit.

Required fields are checked as soon as an entity is opened, not only once you edit them, so an entity stored without one shows the error straight away and the section holding it is marked in the rail. Popularity is required — the Search API ranks on it — so an entity saved without one opens at a default of 80, which counts as an unsaved change until you save it.

Translation

You can easily translate Fans United Sport Data Management Widget in your language! You just need to pass an object to labels prop with concrete keys and values and that's it! Here's an example how you can do it:

import 'fansunited-widget-sports-data/style.css';
import { SportsDataManager } from 'fansunited-widget-sports-data';

const labels = {
  sportDataTitle: 'Sport Data',
  createLabel: 'Create',
  saveLabel: 'Save',
  editAction: 'Edit',
  overrideAction: 'Override',
};

const App = () => {
  return (
    <SportsDataManager
      fansUnitedApiKey={'your-api-key'}
      fansUnitedClientId={'your-client-id'}
      labels={labels} />
  );
}

Every key is optional — anything you leave out keeps its English default.

Some labels contain placeholders in braces, such as {name}, {client}, {count} or {entities}. These are filled in at runtime, so keep them in your translation. A placeholder you drop simply will not be shown; one you misspell is printed as written.

The full list follows, grouped by where it appears.

Access and session

Shown before the catalogue is reachable, or in the header.

| Key | Description | Default value | | --- | --- | --- | | clientConfigErrorMessage | Shown when the client configuration cannot be fetched, usually a wrong API key or client ID. | There was a problem fetching client's configuration. Please check again your API key and client ID. For more information contact Fans United staff. | | featureDisabled | Shown when Sport Data is not enabled for the client. | Feature disabled! | | noPermissionMessage | Shown when the signed-in account carries no role granting access. | You have no permission for this feature! | | tryAgainButton | Retries the failed request. | Try again | | userNotFound | Shown when the signed-in account cannot be resolved. | User not found. Please try again. | | signOut | The sign-out button, top right. Hidden by hideSignOutButton. | Sign out |

Page header

Title and standfirst above the tabs.

| Key | Description | Default value | | --- | --- | --- | | sportDataTitle | Page title. | Sport Data | | sportDataDescription | Standfirst under the title. | Browse the shared sport catalogue and manage your own entries — rename entities, add logos and metadata for your audience, or create records that only exist for your account. |

Scope tabs

The two scopes the catalogue is browsed in.

| Key | Description | Default value | | --- | --- | --- | | globalCatalogueTab | The shared catalogue every client reads. | Catalogue | | clientOverridesTab | The client's own overlay, shown to platform operators. | Overrides | | mySportDataTab | The same overlay, shown to client admins. | Overrides |

Scope notes

The banner under the tabs. Which one shows depends on the scope and on the role.

| Key | Description | Default value | | --- | --- | --- | | globalScopeOperatorNote | Catalogue note for an operator, who can write here. Takes {client}. | Changes here are visible to ALL clients. To change one for {client} only, use Override on its row, or tick several rows and create overrides together. | | globalScopeAdminNote | Catalogue note for a client admin, who cannot. Takes {client}. | Read-only. To customize an entity for {client}, use Override on its row, or tick several rows and create overrides together. The global entity stays unchanged. | | overridesScopeOperatorNote | Overrides note for an operator. Takes {client}. | Sport data for {client} only. Overrides keep the global ID and shadow it for this client; client-only entities you create here get a newly generated ID. | | overridesScopeAdminNote | Overrides note for a client admin. | Entities you have customised, plus entities you created yourself. Open one and use Remove override to go back to the global version. |

Toolbar

Search, sport filter and Create.

| Key | Description | Default value | | --- | --- | --- | | searchPlaceholder | Placeholder in the search field. | Search by name, title, or ID... | | allSports | The unfiltered option in the sport filter. | All sports | | createLabel | Starts a new entity in the current scope. | Create | | importJsonLabel | Opens Create from JSON. Platform operators only. | Import JSON | | importJsonHint | Its tooltip. Takes {entity}. | Create one {entity} from JSON |

Entity list

Column headings, row actions and list-level messages.

| Key | Description | Default value | | --- | --- | --- | | nameColumn | Name column heading. | Name | | countryColumn | Country column heading. | Country | | actionsColumn | Actions column heading. | Actions | | notAvailable | Stands in for a value the entity does not carry. | N/A | | copyId | Tooltip on the copy-ID button beside each row. | Copy ID | | copiedId | Replaces it briefly once the ID is copied. | Copied | | editAction | Row action opening the editor. Shown as a pencil, with this as its tooltip. | Edit | | viewAction | Row action for read-only access. Shown as an eye. | View | | emptyStateMessage | Shown when the scope holds no entities of this type. Takes {entities}. | No {entities} yet. Create the first one from the button above. | | loadEntitiesErrorMessage | Shown when the list request fails. | Could not load the catalogue. | | entityNotFound | Shown when an entity is opened that no longer exists in this scope. | This entity no longer exists here. |

Editor and saving

The editor shell: save bar, toasts and the banner naming what is being edited.

| Key | Description | Default value | | --- | --- | --- | | generalSection | General section heading. | General | | generalSectionHint | Hint under that heading. | Identity and naming. The ID and sport are fixed once the entity exists. | | saveLabel | Save button in the save bar. | Save | | entityUpdatedGlobalMessage | Toast after saving a global entity. Takes {name}. | {name} updated in the catalogue — live for every client. | | entityUpdatedClientMessage | Toast after saving an override. Takes {name} and {client}. | {name} updated for {client}. The global entity is unchanged. | | entityUpdateErrorMessage | Prefix on a failed save, followed by the reason. Takes {name}. | Could not update {name}: | | unsavedGlobalNote | Save-bar status while a global entity has unsaved changes. | Unsaved changes — publishing updates all clients | | unsavedClientNote | The same for an override. Takes {client}. | Unsaved changes — affects only {client} | | noChangesNote | Save-bar status while nothing has changed. | No unsaved changes | | readOnlyNote | Save-bar status when the account cannot write here. Takes {client}. | Read-only — create an override to change these values for {client} | | fieldErrorsNote | Save-bar status with several invalid fields. Takes {count}. | {count} fields need attention before saving | | fieldErrorNote | The same for exactly one. | 1 field needs attention before saving | | backLabel | Returns to the list, keeping your place in it. | Back | | bannerCreatingGlobalTitle | Banner title while creating a global entity. | Creating a new GLOBAL entity | | bannerCreatingGlobalText | Its body. | once saved it enters the global catalogue and every client can read it. | | bannerCreatingClientTitle | Banner title while creating a client-only entity. | Creating a client-only entity | | bannerCreatingClientText | Its body. Takes {client}. | it lives only in {client}’s data and never appears in the global catalogue. | | bannerViewingGlobalTitle | Banner title while viewing a global entity read-only. | Viewing the GLOBAL entity | | bannerViewingGlobalText | Its body. Takes {client}. | {client} has no override for this entity, so it is served exactly as published. | | bannerEditingGlobalTitle | Banner title while editing a global entity. | Editing the GLOBAL entity | | bannerEditingGlobalText | Its body. | changes are published to every client that does not have its own override of this entity. | | bannerEditingOverrideTitle | Banner title while editing an override. Takes {client}. | Editing {client}’s override | | bannerEditingOverrideText | Its body. | the global entity is left unchanged and keeps the same ID. |

How people find this entity

The panel explaining how the Search API will match this entity.

| Key | Description | Default value | | --- | --- | --- | | searchPanelTitle | Heading of the panel. | How people find this entity | | searchPanelHint | Hint under it. | Updates as you edit. Saved automatically. | | searchNameStep | Step 1 heading: the name itself. | Searching for this name finds it | | searchNameStepHint | Its hint. | Typing the first few letters is enough. Change it in General. | | searchVariationsStep | Step 2 heading: the other terms that match. | It is also found by | | searchVariationsStepHint | Its hint. | Collected from the short name, three letter code, nickname and every translation you add. | | noVariationsNote | Shown when the entity has no variations beyond its name. | Only the name above. Add a short name, nickname or translation to widen this. | | searchFiltersStep | Step 3 heading: the links apps filter by. | It is returned when apps filter by | | searchFiltersStepHint | Its hint. | These are the things this entity is linked to. An app asking for every team in a competition, or every competition in a country, gets this entity back because of a link listed here. Add or remove them in Related. | | noLinksNote | Shown when the entity is linked to nothing. | Not linked to anything, so it can only be found by name. Add links in Related. | | moreFilters | Expands a truncated list of filter values. Takes {count}. | +{count} more | | fewerFilters | Collapses it again. | Show less | | copyFilter | Tooltip on a filter's copy button. | Copy filter | | copiedFilter | Replaces it briefly once copied. | Copied | | showParams | Reveals the query parameters for the Search API call. | Show API parameters | | hideParams | Hides them again. | Hide API parameters | | paramsHint | Explains what those parameters are. | Query parameters for Search API GET /entities/search. One example per link type - relationship and value are always sent together. | | paramsMore | Notes how many further values share the shape shown. Takes {count}. | and {count} more of these | | searchRankingStep | Step 4 heading: popularity as a tie-breaker. | If several entities match | | searchRankingStepHint | Its hint. | The higher this score, the earlier it appears in the results. | | searchUnfilterableWarning | Warns that links are stored in a field the Search API does not filter on. Takes {count}. | This entity has {count} link(s) saved in a field the Search API does not filter on, so apps cannot narrow results by them. Re-saving this entity fixes it. |

Creating an entity

The create form and its outcomes.

| Key | Description | Default value | | --- | --- | --- | | createEntityTitle | Title of the create form. Takes {entity}. | New {entity} | | createEntityHint | Hint under it. | The ID and sport are set once, here — they cannot be changed afterwards. | | entityCreatedGlobalMessage | Toast after creating a global entity. Takes {name}. | {name} added to the catalogue — available to every client. | | entityCreatedClientMessage | Toast after creating a client-only entity. Takes {name} and {client}. | {name} created for {client}. It exists only in their data. | | entityCreateErrorMessage | Prefix on a failed create, followed by the reason. Takes {name}. | Could not create {name}: | | idAlreadyExists | Shown when the generated or entered ID is already taken in this scope. | An entity with this ID already exists here. Pick a different ID. |

Creating an entity from JSON

The Import JSON view, for platform operators. One entity per import; its ID is always generated, and invalid or unknown properties are left out and listed rather than refused. A file can be up to 512 KB.

| Key | Description | Default value | | --- | --- | --- | | importBreadcrumb | Last breadcrumb segment. | Create from JSON | | importTitle | Page title. Takes {entity}. | Create a {entity} from JSON | | importDescription | Standfirst under the title. Takes {entity}. | Paste the JSON for one {entity} or drop a .json file of up to 512 KB. It is checked as you type: anything invalid is left out and listed here before you create it. | | importTypeLabel | Label on the entity type select. | Entity type | | importTargetLabel | Label on the target switch. | Write to | | importGlobalTarget | Target: the global catalogue. | Global catalogue | | importClientTarget | Target: the client's own data. Takes {client}. | {client} | | importSourceTitle | Heading of the JSON editor. Also used by Paste season history. | JSON | | importSourceEmpty | Shown in the empty editor. Takes {entity}. | Paste one {entity}’s JSON here, or drop a .json file of up to 512 KB. | | importDropHere | Shown while a file is dragged over the editor. | Drop the file to load it | | importChooseFile | Opens the file picker. | Choose file | | importInsertExample | Fills the editor with the type's example. | Insert example | | importFormat | Pretty-prints the JSON. | Format | | importClear | Empties the editor. | Clear | | importShortcutHint | Footer of the editor. Takes {keys} and {entity}. | {keys} creates the {entity}. | | importFileNotJson | Shown for a file that is not JSON. Takes {file}. | {file} isn’t a .json file. | | importFileTooLarge | Shown for a file over the limit. Takes {file}. | {file} is larger than 512 KB. A JSON file can be up to 512 KB. | | importOneFile | Shown when several files are dropped. | Drop one file at a time. | | importFileReadError | Shown when a file cannot be read. Takes {file}. | Could not read {file}. | | importParseErrorTitle | Review heading while the JSON does not parse. | This isn’t valid JSON | | importParseErrorLine | Where the syntax error is. Takes {line} and {column}. | Line {line}, column {column} | | importUnnamed | Review heading while the JSON has no name. Takes {entity}. | Unnamed {entity} | | importReadyStatus | Status when the entity can be created. | Ready to create | | importBlockedStatus | Status when required fields need fixing. Takes {count}. | {count} to fix before creating | | importCheckingStatus | Status while linked IDs are looked up. | Checking linked entities… | | importMustFixHeading | Heading of the blocking problems. | Must fix | | importLeftOutHeading | Heading of the properties that will not be written. | Left out | | importLeftOutNote | Note under it. Takes {entity}. | These properties will not be written. The {entity} is created from the rest. | | importNotesHeading | Heading of defaults applied and fields ignored. | Notes | | importWholeDocument | Stands in for a problem with the whole object rather than one property. | Whole object | | importLineShort | Line a problem is on. Takes {line}. | Line {line} | | importFieldReference | Heading of the field reference. | Field reference | | importReferenceIntro | Intro to it. Takes {entity}. | What a {entity} accepts. Properties sit at the top level of the object; a stored document with a data object is read too. | | importRequired | Marks a required property. | Required | | importOptional | Marks an optional property. | Optional | | importDefaulted | Marks a property filled in when missing. | Has default | | importGenerated | Marks the generated ID. | Generated | | importCopyExample | Copies the type's example. | Copy example | | importCopied | Replaces it briefly once copied. | Copied | | importExampleLabel | Introduces a property's example value. | Example | | importCreate | The create button. Takes {entity}. | Create {entity} | | importEmptyReason | Why Create is disabled with no JSON. | Paste or drop JSON to begin. | | importParseReason | Why it is disabled with broken JSON. | Fix the JSON syntax to continue. | | importBlockedReason | Why it is disabled with blocking problems. | Fix the items under Must fix to create. | | importCheckingReason | Why it is disabled while linked IDs are looked up. | Waiting for the linked entities to be checked. | | importCreatedTitle | Heading once created. Takes {name}. | {name} created | | importCreatedGlobalText | Its text for a global entity. | Added to the global catalogue. Every client can read it. | | importCreatedClientText | Its text for a client-only entity. Takes {client}. | Added to {client}’s data only. | | importOpenEntity | Opens the created entity in the editor. Takes {entity}. | Open {entity} | | importCreateAnother | Clears the JSON for the next entity, keeping type and target. | Create another | | importCreateErrorMessage | Prefix on a failed create, followed by the reason. Takes {name}. | Could not create {name}. |

Translations section

Localized names, one row per language.

| Key | Description | Default value | | --- | --- | --- | | languageColumn | Language column heading. | Language | | filterLanguagesPlaceholder | Placeholder in the language search field. | Filter languages... | | addLanguage | Opens the language picker. | + Add language | | removeTranslation | Tooltip on a translation row's remove button. | Remove translation | | noTranslations | Shown when no language has been added. | No translations yet — add a language to start. | | noLanguageMatches | Shown when the filter matches no language. | No language matches that filter. | | showAllLanguages | Expands the truncated language list. Takes {count}. | Show all {count} languages | | showFewerLanguages | Collapses it again. | Show less | | translationsSummary | Section summary. Takes {count} and {total}. | {count} of {total} supported languages translated. |

Assets section

One image per asset key, uploaded or pointed at an external URL.

An upload is stored on the entity as a host-relative path such as /teams/photo.png, never a full URL, so the CDN host can change without rewriting every document — the Search API puts the host back and serves an absolute URL. An external URL is stored and returned exactly as entered. The editor resolves the host itself to show a preview, so a preview that fails to load does not mean the stored value is wrong.

| Key | Description | Default value | | --- | --- | --- | | assetUploadedChip | Marks an asset stored in Cloud Storage. | Uploaded | | assetExternalChip | Marks an asset pointing at an external URL. | External | | assetReplace | Replaces the current image. | Replace | | assetRemove | Clears the asset. | Remove | | assetChooseFile | Opens the file picker. | Choose file | | assetDropHere | Shown in an empty asset slot. | Not set. | | assetUseExternalUrl | Introduces the external-URL field. | Or use an external URL | | assetExternalUrlPlaceholder | Placeholder in that field. | https://cdn.example.com/logo.png | | assetSaveUrl | Accepts the entered URL. | Save | | assetKeepCurrent | Abandons the entered URL and keeps what is set. | Keep current | | assetStoredPathNote | Explains how an uploaded asset is stored and served. | Saved as this location. Apps receive the full web address. | | assetExternalUrlNote | Explains that an external asset is served by its own host. | Served from its original host, which we do not control. | | assetUploading | Shown while an upload is in flight. | Uploading… | | assetPreviewUnavailable | Shown in place of an image the editor cannot load. | Cannot show the image here. What is saved is fine. | | assetsSummary | Section summary. Takes {count} and {total}. | {count} of {total} images added. | | assetsStorageDisclaimer | Explains how images are stored and returned. Takes {folder} and {host}. | An image you upload is saved as a short location - /{folder}/photo.png - and apps receive the full address, {host}/{folder}/photo.png. A link you paste yourself is saved and given back exactly as you typed it. |

Branding section

Brand and shirt colors.

| Key | Description | Default value | | --- | --- | --- | | brandNotSet | Shown in place of an unset color. | Not set | | brandClear | Clears a color back to unset. | Clear | | shirtColorsHeading | Heading above the shirt colors. | Shirt colors | | addShirt | Adds a shirt. | Add shirt | | removeShirt | Tooltip on a shirt's remove button. | Remove shirt | | shirtPrimary | Primary shirt color field. | Primary | | shirtSecondary | Secondary shirt color field. | Secondary | | brandingSummary | Section summary. Takes {count} and {total}. | {count} of {total} colors set. Unset colors fall back to platform defaults. |

Socials section

Links to official channels.

| Key | Description | Default value | | --- | --- | --- | | socialInvalidUrl | Validation message on a malformed social URL. | Enter a full URL starting with https:// | | socialOpenLink | Tooltip on the button opening a channel. | Open in a new tab | | socialsSummary | Section summary. Takes {count} and {total}. | {count} of {total} channels set. |

Related section

Links to other entities, which become the filters apps search by.

Links are grouped by relationship and can be dragged into order within their group. They are saved in the order shown, so the Search API returns them in that order too. Links stored with their relationships mixed together are regrouped the next time Related is edited; just opening an entity does not change it.

| Key | Description | Default value | | --- | --- | --- | | relatedAddHeading | Heading above the relationship picker. | Add a relationship | | relatedSearchPlaceholder | Placeholder in the entity search. Takes {type}. | Search {type} by name or ID... | | relatedNoResults | Shown when the search matches nothing. | No match in the catalogue. | | relatedSearching | Shown while the search is in flight. | Searching… | | relatedRemove | Tooltip on a link's remove button. | Remove link | | relatedUnknownEntity | Stands in for a linked entity that cannot be resolved. | Unknown entity | | relatedUnknownRelationship | Marks a stored relationship not offered for this entity type. | Not offered for this type | | relatedShowMore | Expands a truncated list of links. Takes {count}. | +{count} more | | relatedShowLess | Collapses it again. | Show less | | relatedReorder | Tooltip on a link's drag handle. | Drag to reorder. Apps receive links in this order. | | relatedEmpty | Shown when the entity has no links. | No links yet. Apps can only find this entity by name. | | relatedSummary | Section summary. Takes {count}. | {count} links. These become the filters apps search by. |

Season history section

Per-season links, assets and attributes.

A season's links are found with the same search as Related and show each entity's name, picture and ID. Unlike Related, they are one list rather than grouped: each row is tagged with its relationship, and rows can be dragged to any position. The list is saved, and returned by the Search API, in the order shown. A link whose ID is blank or matches no entity is shown as a relationship and an ID to correct instead; an ID typed there is taken when you press Enter or leave the field.

Every season needs an ID, built as the competition's ID followed by the season — fb:c:1 and 2026/27 make fb:c:1:2026/27. It is the only thing tying a season to a competition, so a season without one cannot be saved and the entity's Save button stays disabled until it is filled in.

| Key | Description | Default value | | --- | --- | --- | | seasonAddSeason | Adds a season. | Add season | | seasonRemoveSeason | Tooltip on a season's remove button. | Remove season | | seasonIdLabel | Season ID field. | Season ID | | seasonIdPlaceholder | Its placeholder, showing the expected shape. | fb:c:1:2026/27 | | seasonIdHint | Explains how to build a season ID, shown under the field. | The competition id, then a colon, then the season - fb:c:1 and 2026/27 together make fb:c:1:2026/27. Copy the competition id from the Related section or the competition itself, and write the season the way that competition already writes it. | | seasonIdInvalid | Validation message on a malformed season ID. | This is not a competition id followed by a season, like fb:c:1:2026/27 | | seasonIdRequired | Validation message on a season with no ID. | Required — for example fb:c:1:2026/27 | | seasonUntitled | Stands in for a season with no ID yet. | New season | | seasonLinksHeading | Heading above the season's links. | Related | | seasonEntityIdColumn | Placeholder in the ID field of a link typed by hand. | Entity ID | | seasonAddLink | Adds a link by typing its ID, for an entity the search cannot find. | Add by ID instead | | seasonRemoveLink | Tooltip on a link's remove button. | Remove related entity | | seasonNoLinks | Shown when the season has no links. | No related entities for this season. | | seasonAssetsHeading | Heading above the season's assets. | Assets | | seasonAttributesHeading | Heading above the season's attributes. | Attributes | | seasonAddAttribute | Adds an attribute to the season. | Add attribute | | seasonRemoveAttribute | Tooltip on an attribute's remove button. | Remove attribute | | seasonNoAttributes | Shown when the season has no attributes. | No attributes for this season. | | seasonEmpty | Shown when the entity has no seasons. | No seasons yet. | | seasonSummary | Section summary. Takes {count}. | {count} seasons recorded. |

Pasting season history

The Paste JSON dialog in Season History, for anyone who can edit the entity. It uses the same JSON editor as Creating an entity from JSON, so the editor's buttons and file messages come from the import… labels above. Applying changes the draft only; nothing is written until the entity is saved.

| Key | Description | Default value | | --- | --- | --- | | seasonImportAction | Opens the dialog, beside the section heading. | Paste JSON | | seasonImportTitle | Dialog title. | Paste season history | | seasonImportSubtitle | Its subtitle. Takes {entity}. | Paste a list of seasons, a single season, or a whole {entity}’s JSON. Nothing is saved until you save the {entity}. | | seasonImportModeLabel | Label on the Merge / Replace all switch, shown when the entity already has seasons. | Existing seasons | | seasonImportMerge | Merges by season ID. | Merge | | seasonImportReplace | Replaces every season. | Replace all | | seasonImportMergeHint | Explains Merge. | A pasted season replaces the one with the same season_id. Every other season is kept. | | seasonImportReplaceHint | Explains Replace all. | The pasted seasons become the whole history. Seasons not in the JSON are removed. | | seasonImportSourceEmpty | Shown in the empty editor. | Paste the season history here, or drop a .json file of up to 512 KB. | | seasonImportShortcutHint | Footer of the editor. Takes {keys}. | {keys} applies the seasons. | | seasonImportEmpty | Shown under the editor before anything is pasted. | Paste JSON to see what will change. | | seasonImportNoSeasons | Shown when nothing in the JSON can be applied. | There are no seasons to apply. | | seasonImportAdded | Count of new seasons. Takes {count}. | {count} new | | seasonImportReplaced | Count of seasons replacing existing ones. Takes {count}. | {count} replaced | | seasonImportRemoved | Count of seasons Replace all would remove. Takes {count}. | {count} removed | | seasonImportRemovedSeason | Shown against each season that would be removed. | Not in the pasted JSON, so this season will be removed. | | seasonImportLeftOut | Count of seasons and links left out. Takes {count}. | {count} left out | | seasonImportChecking | Shown while linked IDs are looked up. | Checking linked entities… | | seasonImportApply | The apply button. Takes {count}. | Apply {count} seasons | | seasonImportApplyOne | The same for exactly one. | Apply 1 season | | seasonImportApplyNone | The same before anything is pasted. | Apply | | seasonImportAppliedMessage | Toast after applying. Takes {entity}. | Season history updated. Save the {entity} to keep it. |

Attributes and provider references

Key/value attributes, and the external IDs per data provider.

| Key | Description | Default value | | --- | --- | --- | | attributeColumn | Attribute column heading. | Attribute | | valueColumn | Value column heading. | Value | | providerColumn | Provider column heading. | Provider | | providerIdColumn | Provider ID column heading. | Provider ID | | addAttribute | Adds an attribute row. | Add attribute | | addProviderReference | Adds a provider reference row. | Add provider reference | | noAttributes | Shown when the entity has no attributes. | No attributes yet. | | noProviderReferences | Shown when the entity has no provider references. | No provider references yet. | | removeAttribute | Tooltip on an attribute's remove button. | Remove attribute | | removeProviderReference | Tooltip on a provider reference's remove button. | Remove provider reference | | attributesSummary | Section summary. Takes {count}. | {count} attributes set. Each key should appear once. | | attributeSummarySingular | The same for exactly one. | 1 attribute set. Each key should appear once. | | providersSummary | Section summary. Takes {count}. | {count} provider references. Each provider should appear once. | | providerSummarySingular | The same for exactly one. | 1 provider reference. Each provider should appear once. | | customAttributeOption | The option that turns the attribute key into a free-text field. Additional Information only. | Custom | | customAttributeOptionHint | Hint under that option in the list. | Type your own attribute key | | customAttributePlaceholder | Placeholder in the custom key field. | Shirt Sponsor | | customAttributeNote | Note under it, explaining how a custom key is stored. | Write it however reads best — "Shirt Sponsor" is saved as SHIRT_SPONSOR. | | duplicateAttributeNote | Tooltip on the warning icon when a key repeats. Both rows are still saved. | Another attribute uses this key. Both are saved — whatever reads them decides which it takes. |

Pagination

Below the list.

| Key | Description | Default value | | --- | --- | --- | | rowsPerPage | Label on the page-size select. | Rows per page | | paginationSummary | Range summary. Takes {from}, {to}, {total} and {entities}. | Showing {from}–{to} of {total} {entities} | | searchResultsSummary | Replaces it while searching. Takes {total} and {entities}. | Found {total} {entities} |

Overrides, JSON preview and bulk actions

Creating and removing client overrides, and the read-only JSON drawer.

| Key | Description | Default value | | --- | --- | --- | | removeOverrideAction | Removes the override, in the editor header and as a row action. | Remove | | removeOverrideTitle | Title of the confirmation modal. | Remove this override? | | removeOverrideBody | Its body. Takes {name} and {client}. | {name} will no longer be customised for {client}. They go back to the global version that everyone else sees. | | removeOverrideFallsBackNote | Explains that the global entity is untouched. Takes {client}. | The entity is not deleted — only the customised copy. Updates to the global version reach {client} again. | | removeOverridePermanentNote | Warns that the customised values cannot be recovered. | The changes made here — names, translations, images, colours and links — cannot be recovered. You can customise again, starting from the global version. | | removeOverrideConfirm | Confirms removal. | Remove | | overrideRemovedMessage | Toast after removal. Takes {name} and {client}. | {name} is no longer customised for {client}. | | removeOverrideErrorMessage | Prefix on a failed removal, followed by the reason. | Could not remove the override: | | jsonAction | Opens the read-only JSON drawer. | JSON | | jsonGlobalSubtitle | Drawer subtitle for a global entity. | Global entity | | jsonOverrideSubtitle | Drawer subtitle for an override. | Client override | | jsonCopy | Copies the JSON. | Copy | | jsonCopied | Replaces it briefly once copied. | Copied | | jsonClose | Closes the drawer. | Close | | jsonFooterNote | Footer note in the drawer. | Read-only preview of the entity data this editor will save. | | deviationTitle | Tooltip heading on the badge counting fields that differ from the global entity. Takes {count}. | {count} values differ from the global entity | | deviationMore | Truncates that tooltip's list. Takes {count}. | +{count} more | | overrideAction | Creates an override from a catalogue row. | Override | | overriddenBadge | Badge on a catalogue row the client already overrides. | Overridden | | overriddenBadgeTitle | Its tooltip. Takes {client}. | {client} has its own copy of this entity — global changes no longer reach it. | | selectedSuffix | Count of rows ticked for bulk override. Takes {count} and {entities}. | {count} {entities} selected | | createOverridesFor | Introduces the client in the selection strip. | Create overrides for | | clearSelection | Clears the selection. | Clear | | createOverridesButton | Starts the bulk override. Takes {count}. | Create {count} overrides | | confirmOverrideTitle | Title of the single-override modal. | Create this override? | | confirmOverrideBody | Its body. Takes {client}. | A client copy of this global entity will be created for {client}. From then on your copy replaces the global entity everywhere {client} reads search data. | | confirmOverrideStartsFrom | Explains what the copy starts from. | The copy starts from the current global values. Fields you change are highlighted; global updates no longer reach this entity. | | confirmOverrideReversible | Explains that removing it later restores the global entity. | Removing the override later restores the global entity for you. | | confirmBulkTitle | Title of the bulk modal. Takes {count} and {client}. | Create {count} overrides for {client}? | | confirmBulkSubtitle | Its subtitle. | Each entity gets its own client copy, starting from the current global values. | | confirmBulkMore | Expands the list of selected entities. Takes {count}. | + {count} more | | confirmBulkLess | Collapses it again. | Show fewer | | cancel | Dismisses a modal without acting. | Cancel | | createOverrideConfirm | Confirms creation. | Create override | | overrideCreatedMessage | Toast after one override. Takes {name} and {client}. | Override created for {name} — {client} now has its own copy. | | overridesCreatedMessage | Toast after a bulk override. Takes {count} and {client}. | {count} overrides created for {client}. | | overridesSkippedMessage | Notes entities already overridden and left alone. Takes {count}. | {count} were already overridden and were left as they are. | | viewOverrides | Jumps to the Overrides scope from the toast. | View overrides | | createOverrideErrorMessage | Shown when creating an override fails. | Could not create the override. | | creatingOverridesProgress | Progress while a bulk override runs. Takes {done} and {total}. | Creating {done} of {total}… | | partialOverrideErrorMessage | Shown when a bulk override fails part way. Takes {done} and {total}. | Created {done} of {total} overrides, then failed: | | removeOverridesFor | Introduces the client in the selection strip, in the Overrides scope. | Remove overrides for | | removeOverridesButton | Starts the bulk removal. Takes {count}. | Remove {count} overrides | | confirmBulkRemoveTitle | Title of the bulk removal modal. Takes {count} and {client}. | Remove {count} overrides for {client}? | | confirmBulkRemoveSubtitle | Its subtitle. | Each one goes back to the global version that everyone else sees. | | overridesRemovedMessage | Toast after a bulk removal. Takes {count} and {client}. | {count} overrides removed for {client}. | | removingOverridesProgress | Progress while a bulk removal runs. Takes {done} and {total}. | Removing {done} of {total}… | | partialRemoveErrorMessage | Shown when a bulk removal fails part way. Takes {done} and {total}. | Removed {done} of {total} overrides, then failed: |

Changelog

1.2.0 - 2026-10-07

  • Added: Platform operators can create an entity from JSON with Import JSON next to Create. Paste the JSON for one entity or drop a .json file of up to 512 KB, pick the entity type and whether it goes to the global catalogue or the client's own data, and review it before creating. The JSON is checked as it is typed: invalid or unknown properties are listed with the reason and left out, and only a missing or invalid required field blocks creation. Each problem points to its line in the JSON. The ID is always generated, as when creating in the form, and an ID in the JSON is ignored. A field reference and an example for each type are shown alongside
  • Changed: Creating in the form and creating from JSON write the entity through the same code, so both produce the same document
  • Added: Season History has a Paste JSON button. Paste a list of seasons, a single season or a whole entity's JSON, and choose whether it merges with the existing seasons (a season with the same season_id is replaced) or replaces them all. Seasons are checked as the form checks them, invalid seasons and links are listed and left out, and nothing is saved until the entity is saved. Available to anyone who can edit the entity

1.1.1 - 2026-10-06

  • Changed: A platform operator creating a new entity in the Catalogue can edit its ID, so an entity can keep the ID it already has in another sport data source. The ID still starts out generated, must match the entity type's format and must not already exist. Everywhere else - editing an existing entity, creating in Overrides, or a client admin - the ID stays locked
  • Added: Teams have a Tertiary brand color
  • Added: Athletes have Date of Birth and Position fields in General
  • Added: Venues can be linked to a country in Related
  • Changed: Entities are saved with every field the Search API returns for their type, so a new or edited entity has the same shape as one imported from a data provider. Fields with no value are saved as null or an empty list instead of being left out, and every entity carries its content_type, participant_type and a Fans United provider reference
  • Changed: The GENDER search filter follows the entity's gender, so new entities can be filtered by gender and a changed gender is no longer indexed under the old value
  • Changed: An entity's country is edited as a Country link in Related and saved to country_id as well. Teams imported from a provider, which held their country only in country_id, now show it there
  • Changed: Translations are saved with uppercase language codes, and a cleared field is saved as null instead of an empty string. Athlete translations stored as shortName and threeLetterCode are shown and saved as short_name and three_letter_code
  • Changed: A country's Type offers Political and Sport, the values countries are actually stored with
  • Fixed: Saving a team imported from a provider removed its COUNTRY search filter, so filtering by country no longer found it
  • Fixed: An entity saved by an older editor without a top-level sport is saved with one again

1.1.0 - 2026-09-28

  • Added: Related entities can be dragged into order, and are saved and returned by the Search API in the order shown. In Related they are ordered within their relationship group; in Season History, anywhere in the season's list
  • Added: Season History links show the linked entity's name, picture and ID, are added with the same search as Related, and carry a tag naming their relationship. A link whose ID matches no entity falls back to editable relationship and ID fields
  • Added: relatedReorder label for the drag handle tooltip
  • Added: Tennis competitions have a Participant Type - Singles, Doubles or Team - defaulting to Singles
  • Changed: seasonAddLink now adds a link by typing its ID, and defaults to "Add by ID instead"
  • Changed: Competitions in every other sport are always saved with a participant type of Team, and the field is not shown for them
  • Removed: seasonRelationshipColumn label, as season links no longer have column headings
  • Removed: Formula 1, from the sport filter, the Sport field and entity ID validation
  • Fixed: The "Feature disabled!" card flashed briefly on sign-in and on reload for users who do have access. The widget now waits for the user's roles before deciding
  • Fixed: A dropdown field stored with a value its options do not list showed as blank. The stored value is now shown

1.0.1 - 2026-09-23

  • Fixed: Related entities of an overridden entity all showed as "Unknown entity". A link is now resolved from the global catalogue whenever the client's own overlay cannot answer for it, including when that overlay does not exist yet
  • Fixed: Save could stay disabled with nothing on screen explaining why. Validation errors an entity was already stored with are now shown as soon as it opens, and the section rail marks the section holding them
  • Fixed: An entity stored without a popularity could not be saved at all. It now opens with a default of 80, which reads as an unsaved change, and a newly created override is written with one
  • Fixed: A season could be saved without an ID, leaving a season nothing could resolve back to a competition. A season ID is now required, and the field explains how to build one
  • Fixed: An uploaded image could appear broken after saving. The preview now uses the address the upload itself returns, and an image the editor cannot load says so instead of showing a broken-image icon
  • Plainer wording in the Assets section describing how an image is stored and what apps receive

1.0.0 - 2026-09-17

  • Initial Release:
    • Browse the global sport catalogue across seven entity types, with search, sport filter and pagination
    • Create and edit entities, with per-type sections for translations, assets, branding, socials, related entities, season history, additional information and provider references
    • Client overrides: create one from a row or several at once, see which fields differ from the global entity, and remove an override to fall back
    • "How people find this entity" panel explaining name matching, variations, filterable links and popularity ranking
    • Read-only JSON preview of the entity data being saved
    • Role-aware UI for platform_operator and client_admin