You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Game platform field: free-text → enum (single value per entry) #26
Game.Platform is currently a free-text string?. That's bad for filtering, search, normalisation, and IGDB enrichment (which surfaces a canonical platform name we then drop on the floor as a free-text string). Replace it with a curated enum, one platform per entry — multi-platform copies are modelled as separate entries (see "Why one-per-entry" below).
Proposed enum
publicenumGamePlatform{Other=0,Pc,Mac,Linux,Mobile,// XboxXboxOriginal,Xbox360,XboxOne,XboxSeriesXS,// PlayStationPS1,PS2,PS3,PS4,PS5,Vita,PSP,// NintendoNes,Snes,N64,GameCube,Wii,WiiU,Switch,Switch2,GameBoy,GameBoyColor,GameBoyAdvance,Ds,Ds3,// Sega + retro / PC handheldGenesis,Saturn,Dreamcast,SteamDeck,}
Display labels live in a GAME_PLATFORMS array on the client (label / value), same shape as the existing MUSIC_FORMATS / MOVIE_FORMAT_FLAGS.
Why one-per-entry (and not a multi-platform field)
A collection tracker tracks releases, which is also IGDB's model. Each release has its own per-platform attributes that don't generalise across platforms:
Format (Disc / Digital), digital store, barcode
Condition, purchase price / date, location
Completion status, hours played, last played
Status (Owned / Wishlist / OnOrder / Sold) — "owned on PS5, wishlist on Switch" works trivially with two entries
Year + developer can differ per release (e.g. Halo 3 on 360 vs MCC on Series X)
A multi-platform field forces every per-copy field into nested arrays / sub-records, which complicates the schema, every form, and every endpoint. Separate entries keeps the model flat and matches how barcode scanning + IGDB lookup feed data in (one release per scan).
A "duplicate to another platform" button on the edit page covers the convenience case (see follow-up below).
Implementation
Backend
Collectify.Domain.Enums.GamePlatform (new file).
Game.Platform becomes GamePlatform (non-nullable; Other is the safe default for unknown).
EF migration: rename existing Platform to PlatformLegacy (string), add new Platform (int), backfill via case-insensitive match against the enum's display labels (e.g. "PS5" → PS5, "Switch" → Switch, otherwise → Other and the original string is preserved in PlatformLegacy for one release so users can fix manually).
IGDB mapping: when IgdbGameProvider.Map extracts the first IGDB platform name, lookup-table it onto GamePlatform. Leave Other for unmapped values.
Endpoint tests: roundtrip the enum as a string via JsonStringEnumConverter (already configured globally).
Frontend
services/types.ts — add GAME_PLATFORMS: { value: GamePlatform; label: string }[] and the GamePlatform union type.
GameForm — swap the <Input> for a <Select> driven by GAME_PLATFORMS.
GamesList / list cards — render the label for the stored enum value.
IGDB lookup importLookup — use the new platform mapping so picked / scanned games land on a canonical enum.
Migration safety
Migration is one-way (string → int with a legacy column kept until a follow-up PR drops it).
Document any unmapped values surface as Other; the legacy column lets users see what they'd typed.
Out of scope (follow-ups)
"Duplicate to another platform" button on the edit page — pre-fills a new GameForm with the same metadata (title, developer, igdbId, cover, …) and a different Platform. Issue to be filed once this lands.
Drop the PlatformLegacy column once the user has had a chance to clean up unmapped values.
Motivation
Game.Platformis currently a free-textstring?. That's bad for filtering, search, normalisation, and IGDB enrichment (which surfaces a canonical platform name we then drop on the floor as a free-text string). Replace it with a curated enum, one platform per entry — multi-platform copies are modelled as separate entries (see "Why one-per-entry" below).Proposed enum
Display labels live in a
GAME_PLATFORMSarray on the client (label / value), same shape as the existingMUSIC_FORMATS/MOVIE_FORMAT_FLAGS.Why one-per-entry (and not a multi-platform field)
A collection tracker tracks releases, which is also IGDB's model. Each release has its own per-platform attributes that don't generalise across platforms:
A multi-platform field forces every per-copy field into nested arrays / sub-records, which complicates the schema, every form, and every endpoint. Separate entries keeps the model flat and matches how barcode scanning + IGDB lookup feed data in (one release per scan).
A "duplicate to another platform" button on the edit page covers the convenience case (see follow-up below).
Implementation
Backend
Collectify.Domain.Enums.GamePlatform(new file).Game.PlatformbecomesGamePlatform(non-nullable;Otheris the safe default for unknown).PlatformtoPlatformLegacy(string), add newPlatform(int), backfill via case-insensitive match against the enum's display labels (e.g. "PS5" →PS5, "Switch" →Switch, otherwise →Otherand the original string is preserved inPlatformLegacyfor one release so users can fix manually).IgdbGameProvider.Mapextracts the first IGDB platform name, lookup-table it ontoGamePlatform. LeaveOtherfor unmapped values.JsonStringEnumConverter(already configured globally).Frontend
services/types.ts— addGAME_PLATFORMS: { value: GamePlatform; label: string }[]and theGamePlatformunion type.GameForm— swap the<Input>for a<Select>driven byGAME_PLATFORMS.GamesList/ list cards — render thelabelfor the stored enum value.importLookup— use the new platform mapping so picked / scanned games land on a canonical enum.Migration safety
Other; the legacy column lets users see what they'd typed.Out of scope (follow-ups)
GameFormwith the same metadata (title, developer, igdbId, cover, …) and a differentPlatform. Issue to be filed once this lands.PlatformLegacycolumn once the user has had a chance to clean up unmapped values.igdbId.Verification