From 295b4b9f3199db6b11cae789d736cf14ef42edd8 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Tue, 10 Mar 2026 10:36:22 +0200 Subject: [PATCH 01/10] adjust logic --- .../Widget/Participants/Team/Roster.lua | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/lua/wikis/commons/Widget/Participants/Team/Roster.lua b/lua/wikis/commons/Widget/Participants/Team/Roster.lua index cd85e116bc0..3faa1a00d82 100644 --- a/lua/wikis/commons/Widget/Participants/Team/Roster.lua +++ b/lua/wikis/commons/Widget/Participants/Team/Roster.lua @@ -46,6 +46,20 @@ local PERSON_TYPE_TO_TAB = { staff = TAB_ENUM.STAFF, } +---@param player table +---@return boolean +local function isStaffMember(player) + local playerType = player.extradata.type + if playerType == 'staff' then + return true + end + if playerType == 'former' then + local roles = player.extradata.roles or {} + return not Array.all(roles, function(role) return role.type ~= RoleUtil.ROLE_TYPE.STAFF end) + end + return false +end + -- The biz logic behind the role display is somewhat complicated. -- There's 2 areas we show the role, left-role and right-role -- * Right-role: @@ -136,7 +150,16 @@ function ParticipantsTeamRoster:render() local tabTypeEnum, tabData = tabTuple[1], tabTuple[2] local tabPlayers = Array.filter(participant.opponent.players or {}, function(player) local personType = player.extradata.type - return PERSON_TYPE_TO_TAB[personType] == tabTypeEnum + if tabTypeEnum == TAB_ENUM.STAFF then + return personType == 'staff' or isStaffMember(player) + elseif tabTypeEnum == TAB_ENUM.FORMER then + return PERSON_TYPE_TO_TAB[personType] == tabTypeEnum and not isStaffMember(player) + else + return PERSON_TYPE_TO_TAB[personType] == tabTypeEnum + end + end) + tabPlayers = Array.sortBy(tabPlayers, function(player) + return player.extradata.type == 'former' and 1 or 0 end) return { order = tabData.order, From d3c9cd97a760b24ffce81922ad4dc3b03a1596bc Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Tue, 10 Mar 2026 14:16:11 +0200 Subject: [PATCH 02/10] move all formers to former tab with subheaders --- .../Participants/Team/PotentialQualifiers.lua | 2 +- .../Widget/Participants/Team/Roster.lua | 64 +++++++++++++++++-- stylesheets/commons/TeamParticipantCard.scss | 28 ++++---- 3 files changed, 73 insertions(+), 21 deletions(-) diff --git a/lua/wikis/commons/Widget/Participants/Team/PotentialQualifiers.lua b/lua/wikis/commons/Widget/Participants/Team/PotentialQualifiers.lua index 0945255a2ae..e5e5dcea239 100644 --- a/lua/wikis/commons/Widget/Participants/Team/PotentialQualifiers.lua +++ b/lua/wikis/commons/Widget/Participants/Team/PotentialQualifiers.lua @@ -30,7 +30,7 @@ function PotentialQualifiers:render() local children = { Div{ - classes = {'team-participant-card__potential-qualifiers-title'}, + classes = {'team-participant-card__subheader'}, children = 'Potential qualifiers' }, Div{ diff --git a/lua/wikis/commons/Widget/Participants/Team/Roster.lua b/lua/wikis/commons/Widget/Participants/Team/Roster.lua index 3faa1a00d82..c637fcce65c 100644 --- a/lua/wikis/commons/Widget/Participants/Team/Roster.lua +++ b/lua/wikis/commons/Widget/Participants/Team/Roster.lua @@ -124,6 +124,62 @@ function ParticipantsTeamRoster:render() end return orderA < orderB end) + + -- Split into former players and former staff + local formerPlayers = Array.filter(players, function(player) + return player.extradata.type == 'former' and not isStaffMember(player) + end) + local formerStaff = Array.filter(players, function(player) + return player.extradata.type == 'former' and isStaffMember(player) + end) + + -- If we have former players or staff, render with subheaders + if #formerPlayers > 0 or #formerStaff > 0 then + local function makeRosterSection(sectionPlayers) + return Div{ + classes = { 'team-participant-roster' }, + children = Array.map(sectionPlayers, function(player, index) + local playerTeam = participant.opponent.template ~= player.team and player.team or nil + local playerTeamAsOpponent = playerTeam and Opponent.readOpponentArgs{ + type = Opponent.team, + template = playerTeam, + } or nil + local roleLeft, roleRight = getRoleDisplays(player) + return ParticipantsTeamMember{ + player = player, + team = playerTeamAsOpponent, + even = index % 2 == 0, + roleLeft = roleLeft, + roleRight = roleRight, + trophies = player.extradata.trophies or 0, + } + end) + } + end + + local children = {} + if #formerPlayers > 0 then + table.insert(children, Div{ + classes = {'team-participant-card__subheader'}, + children = 'Players' + }) + table.insert(children, makeRosterSection(formerPlayers)) + end + if #formerStaff > 0 then + table.insert(children, Div{ + classes = {'team-participant-card__subheader'}, + children = 'Staff' + }) + table.insert(children, makeRosterSection(formerStaff)) + end + + return Div{ + classes = { 'team-participant-roster' }, + children = children + } + end + + -- Otherwise render as a single roster (for non-former tabs) return Div{ classes = { 'team-participant-roster' }, children = Array.map(players, function(player, index) @@ -140,7 +196,6 @@ function ParticipantsTeamRoster:render() roleLeft = roleLeft, roleRight = roleRight, trophies = player.extradata.trophies or 0, - strikethrough = player.extradata.type == 'former', } end) } @@ -151,16 +206,13 @@ function ParticipantsTeamRoster:render() local tabPlayers = Array.filter(participant.opponent.players or {}, function(player) local personType = player.extradata.type if tabTypeEnum == TAB_ENUM.STAFF then - return personType == 'staff' or isStaffMember(player) + return personType == 'staff' elseif tabTypeEnum == TAB_ENUM.FORMER then - return PERSON_TYPE_TO_TAB[personType] == tabTypeEnum and not isStaffMember(player) + return PERSON_TYPE_TO_TAB[personType] == tabTypeEnum or (personType == 'former' and isStaffMember(player)) else return PERSON_TYPE_TO_TAB[personType] == tabTypeEnum end end) - tabPlayers = Array.sortBy(tabPlayers, function(player) - return player.extradata.type == 'former' and 1 or 0 - end) return { order = tabData.order, title = tabData.title, diff --git a/stylesheets/commons/TeamParticipantCard.scss b/stylesheets/commons/TeamParticipantCard.scss index c202ec485e0..7f0fa6364e9 100644 --- a/stylesheets/commons/TeamParticipantCard.scss +++ b/stylesheets/commons/TeamParticipantCard.scss @@ -427,20 +427,6 @@ $compact-selector: '[data-switch-group="team-cards-compact"]'; flex-direction: column; gap: 0.25rem; - &-title { - padding: 0 0.5rem; - font-size: 0.875rem; - font-weight: bold; - - .theme--light & { - color: var( --clr-secondary-25 ); - } - - .theme--dark & { - color: var( --clr-secondary-90 ); - } - } - &-list { display: flex; flex-direction: column; @@ -467,6 +453,20 @@ $compact-selector: '[data-switch-group="team-cards-compact"]'; } } } + + &__subheader { + padding: 0 0.5rem; + font-size: 0.875rem; + font-weight: bold; + + .theme--light & { + color: var( --clr-secondary-25 ); + } + + .theme--dark & { + color: var( --clr-secondary-90 ); + } + } } body:has( .switch-toggle-active#{ $compact-selector } ) { From c71372255ff49eb79fc40e84c1971e867f17bee4 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Tue, 10 Mar 2026 16:56:46 +0200 Subject: [PATCH 03/10] add a extradata field to preserve the type=former for staff members for the ui --- lua/wikis/commons/TeamParticipants/Parse/Wiki.lua | 2 ++ lua/wikis/commons/Widget/Participants/Team/Roster.lua | 10 +++------- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/lua/wikis/commons/TeamParticipants/Parse/Wiki.lua b/lua/wikis/commons/TeamParticipants/Parse/Wiki.lua index fbe9807ba4e..653b96f49dc 100644 --- a/lua/wikis/commons/TeamParticipants/Parse/Wiki.lua +++ b/lua/wikis/commons/TeamParticipants/Parse/Wiki.lua @@ -203,6 +203,7 @@ function TeamParticipantsWikiParser.parsePlayer(playerInput) local resultsInput = Logic.readBoolOrNil(playerInput.results) local roles = RoleUtil.readRoleArgs(playerInput.role) local playerType = playerInput.type or 'player' + local isFormer = playerType == 'former' local hasNoStaffRoles = Array.all(roles, function(role) return role.type ~= RoleUtil.ROLE_TYPE.STAFF end) @@ -214,6 +215,7 @@ function TeamParticipantsWikiParser.parsePlayer(playerInput) roles = roles, trophies = tonumber(playerInput.trophies), type = playerType, + isFormer = isFormer, played = Logic.nilOr(playedInput, true), results = Logic.nilOr(resultsInput, playedInput, true), } diff --git a/lua/wikis/commons/Widget/Participants/Team/Roster.lua b/lua/wikis/commons/Widget/Participants/Team/Roster.lua index c637fcce65c..8cdf917b32b 100644 --- a/lua/wikis/commons/Widget/Participants/Team/Roster.lua +++ b/lua/wikis/commons/Widget/Participants/Team/Roster.lua @@ -53,10 +53,6 @@ local function isStaffMember(player) if playerType == 'staff' then return true end - if playerType == 'former' then - local roles = player.extradata.roles or {} - return not Array.all(roles, function(role) return role.type ~= RoleUtil.ROLE_TYPE.STAFF end) - end return false end @@ -127,10 +123,10 @@ function ParticipantsTeamRoster:render() -- Split into former players and former staff local formerPlayers = Array.filter(players, function(player) - return player.extradata.type == 'former' and not isStaffMember(player) + return player.extradata.isFormer and not isStaffMember(player) end) local formerStaff = Array.filter(players, function(player) - return player.extradata.type == 'former' and isStaffMember(player) + return player.extradata.isFormer and isStaffMember(player) end) -- If we have former players or staff, render with subheaders @@ -208,7 +204,7 @@ function ParticipantsTeamRoster:render() if tabTypeEnum == TAB_ENUM.STAFF then return personType == 'staff' elseif tabTypeEnum == TAB_ENUM.FORMER then - return PERSON_TYPE_TO_TAB[personType] == tabTypeEnum or (personType == 'former' and isStaffMember(player)) + return player.extradata.isFormer else return PERSON_TYPE_TO_TAB[personType] == tabTypeEnum end From c7ea65d03adb56911e3b54c7df6fceb57921ebeb Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Tue, 10 Mar 2026 17:07:33 +0200 Subject: [PATCH 04/10] remove redundant helper function --- .../commons/Widget/Participants/Team/Roster.lua | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/lua/wikis/commons/Widget/Participants/Team/Roster.lua b/lua/wikis/commons/Widget/Participants/Team/Roster.lua index 8cdf917b32b..4b44724d732 100644 --- a/lua/wikis/commons/Widget/Participants/Team/Roster.lua +++ b/lua/wikis/commons/Widget/Participants/Team/Roster.lua @@ -46,16 +46,6 @@ local PERSON_TYPE_TO_TAB = { staff = TAB_ENUM.STAFF, } ----@param player table ----@return boolean -local function isStaffMember(player) - local playerType = player.extradata.type - if playerType == 'staff' then - return true - end - return false -end - -- The biz logic behind the role display is somewhat complicated. -- There's 2 areas we show the role, left-role and right-role -- * Right-role: @@ -123,10 +113,10 @@ function ParticipantsTeamRoster:render() -- Split into former players and former staff local formerPlayers = Array.filter(players, function(player) - return player.extradata.isFormer and not isStaffMember(player) + return player.extradata.isFormer and player.extradata.type ~= 'staff' end) local formerStaff = Array.filter(players, function(player) - return player.extradata.isFormer and isStaffMember(player) + return player.extradata.isFormer and player.extradata.type == 'staff' end) -- If we have former players or staff, render with subheaders From c83591a8d86f45f25b35b5bcec6ee09d83a15d45 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Tue, 17 Mar 2026 13:32:29 +0200 Subject: [PATCH 05/10] separate type and status --- lua/spec/team_participants_tbd_spec.lua | 38 +++++++------- .../commons/TeamParticipants/Controller.lua | 9 ++-- .../commons/TeamParticipants/Parse/Wiki.lua | 26 +++++++--- .../Widget/Participants/Team/Roster.lua | 52 ++++++++----------- 4 files changed, 66 insertions(+), 59 deletions(-) diff --git a/lua/spec/team_participants_tbd_spec.lua b/lua/spec/team_participants_tbd_spec.lua index 8608e4a0414..75f522f74d2 100644 --- a/lua/spec/team_participants_tbd_spec.lua +++ b/lua/spec/team_participants_tbd_spec.lua @@ -26,9 +26,9 @@ describe('Team Participants TBD Functionality', function() it('fills roster when player count is below expected', function() local opponent = { players = { - {displayName = 'Player1', extradata = {type = 'player'}}, - {displayName = 'Player2', extradata = {type = 'player'}}, - {displayName = 'Player3', extradata = {type = 'player'}}, + {displayName = 'Player1', extradata = {type = 'player', status = 'active'}}, + {displayName = 'Player2', extradata = {type = 'player', status = 'active'}}, + {displayName = 'Player3', extradata = {type = 'player', status = 'active'}}, } } @@ -43,11 +43,11 @@ describe('Team Participants TBD Functionality', function() it('does not fill when roster is complete', function() local opponent = { players = { - {displayName = 'Player1', extradata = {type = 'player'}}, - {displayName = 'Player2', extradata = {type = 'player'}}, - {displayName = 'Player3', extradata = {type = 'player'}}, - {displayName = 'Player4', extradata = {type = 'player'}}, - {displayName = 'Player5', extradata = {type = 'player'}}, + {displayName = 'Player1', extradata = {type = 'player', status = 'active'}}, + {displayName = 'Player2', extradata = {type = 'player', status = 'active'}}, + {displayName = 'Player3', extradata = {type = 'player', status = 'active'}}, + {displayName = 'Player4', extradata = {type = 'player', status = 'active'}}, + {displayName = 'Player5', extradata = {type = 'player', status = 'active'}}, } } @@ -60,13 +60,13 @@ describe('Team Participants TBD Functionality', function() it('does not fill when roster exceeds expected', function() local opponent = { players = { - {displayName = 'Player1', extradata = {type = 'player'}}, - {displayName = 'Player2', extradata = {type = 'player'}}, - {displayName = 'Player3', extradata = {type = 'player'}}, - {displayName = 'Player4', extradata = {type = 'player'}}, - {displayName = 'Player5', extradata = {type = 'player'}}, - {displayName = 'Player6', extradata = {type = 'player'}}, - {displayName = 'Player7', extradata = {type = 'player'}}, + {displayName = 'Player1', extradata = {type = 'player', status = 'active'}}, + {displayName = 'Player2', extradata = {type = 'player', status = 'active'}}, + {displayName = 'Player3', extradata = {type = 'player', status = 'active'}}, + {displayName = 'Player4', extradata = {type = 'player', status = 'active'}}, + {displayName = 'Player5', extradata = {type = 'player', status = 'active'}}, + {displayName = 'Player6', extradata = {type = 'player', status = 'active'}}, + {displayName = 'Player7', extradata = {type = 'player', status = 'active'}}, } } @@ -78,9 +78,9 @@ describe('Team Participants TBD Functionality', function() it('only counts type=player when checking roster size', function() local opponent = { players = { - {displayName = 'Player1', extradata = {type = 'player'}}, - {displayName = 'Player2', extradata = {type = 'player'}}, - {displayName = 'Player3', extradata = {type = 'player'}}, + {displayName = 'Player1', extradata = {type = 'player', status = 'active'}}, + {displayName = 'Player2', extradata = {type = 'player', status = 'active'}}, + {displayName = 'Player3', extradata = {type = 'player', status = 'active'}}, {displayName = 'Coach1', extradata = {type = 'staff'}}, {displayName = 'Coach2', extradata = {type = 'staff'}}, } @@ -98,7 +98,7 @@ describe('Team Participants TBD Functionality', function() it('handles missing data gracefully', function() local opponent1 = { players = { - {displayName = 'Player1', extradata = {type = 'player'}}, + {displayName = 'Player1', extradata = {type = 'player', status = 'active'}}, } } TeamParticipantsWikiParser.fillIncompleteRoster(opponent1, nil) diff --git a/lua/wikis/commons/TeamParticipants/Controller.lua b/lua/wikis/commons/TeamParticipants/Controller.lua index 45960bb575f..f41388e3692 100644 --- a/lua/wikis/commons/TeamParticipants/Controller.lua +++ b/lua/wikis/commons/TeamParticipants/Controller.lua @@ -105,11 +105,11 @@ function TeamParticipantsController.importSquadMembersFromDatabase(participant) end) return Array.map(membersToImport, function (member) - local memberType = member.type + local status = 'active' if member.hasLeft then - memberType = 'former' + status = 'former' elseif member.role and member.role:lower() == 'substitute' then - memberType = 'sub' + status = 'sub' end return TeamParticipantsWikiParser.parsePlayer{ member.displayName, @@ -117,7 +117,8 @@ function TeamParticipantsController.importSquadMembersFromDatabase(participant) flag = member.nationality, faction = member.faction, role = member.role, - type = memberType, + type = member.type, + status = status, } end) end diff --git a/lua/wikis/commons/TeamParticipants/Parse/Wiki.lua b/lua/wikis/commons/TeamParticipants/Parse/Wiki.lua index 653b96f49dc..ba4716730b4 100644 --- a/lua/wikis/commons/TeamParticipants/Parse/Wiki.lua +++ b/lua/wikis/commons/TeamParticipants/Parse/Wiki.lua @@ -202,20 +202,32 @@ function TeamParticipantsWikiParser.parsePlayer(playerInput) local playedInput = Logic.readBoolOrNil(playerInput.played) local resultsInput = Logic.readBoolOrNil(playerInput.results) local roles = RoleUtil.readRoleArgs(playerInput.role) - local playerType = playerInput.type or 'player' - local isFormer = playerType == 'former' - - local hasNoStaffRoles = Array.all(roles, function(role) return role.type ~= RoleUtil.ROLE_TYPE.STAFF end) + local inputType = playerInput.type or 'player' + local hasStaffRoles = Array.any(roles, function(role) return role.type == RoleUtil.ROLE_TYPE.STAFF end) + + local status = playerInput.status + if not status then + if inputType == 'former' then + status = 'former' + elseif inputType == 'sub' then + status = 'sub' + else + status = 'active' + end + end - if playerType ~= 'staff' and not hasNoStaffRoles then + local playerType + if inputType == 'staff' or hasStaffRoles then playerType = 'staff' + else + playerType = 'player' end player.extradata = { roles = roles, trophies = tonumber(playerInput.trophies), type = playerType, - isFormer = isFormer, + status = status, played = Logic.nilOr(playedInput, true), results = Logic.nilOr(resultsInput, playedInput, true), } @@ -233,7 +245,7 @@ function TeamParticipantsWikiParser.fillIncompleteRoster(opponent, minimumPlayer end local actualPlayers = Array.filter(opponent.players, function(player) - return player.extradata.type == 'player' + return player.extradata.type == 'player' and player.extradata.status == 'active' end) local actualPlayerCount = #actualPlayers diff --git a/lua/wikis/commons/Widget/Participants/Team/Roster.lua b/lua/wikis/commons/Widget/Participants/Team/Roster.lua index 4b44724d732..64d534e086e 100644 --- a/lua/wikis/commons/Widget/Participants/Team/Roster.lua +++ b/lua/wikis/commons/Widget/Participants/Team/Roster.lua @@ -38,13 +38,15 @@ local TAB_DATA = { [TAB_ENUM.STAFF] = {title = 'Staff', order = 4}, } ----@type table -local PERSON_TYPE_TO_TAB = { - player = TAB_ENUM.MAIN, - sub = TAB_ENUM.SUB, - former = TAB_ENUM.FORMER, - staff = TAB_ENUM.STAFF, -} +---@param player table +---@return ParticipantsTeamCardTabs +local function getPlayerTab(player) + local status = player.extradata.status + if status == 'former' then return TAB_ENUM.FORMER end + if status == 'sub' then return TAB_ENUM.SUB end + if player.extradata.type == 'staff' then return TAB_ENUM.STAFF end + return TAB_ENUM.MAIN +end -- The biz logic behind the role display is somewhat complicated. -- There's 2 areas we show the role, left-role and right-role @@ -58,7 +60,6 @@ local PERSON_TYPE_TO_TAB = { ---@return string?, string? local function getRoleDisplays(player) local roles = player.extradata.roles or {} - local playerType = player.extradata.type local played = player.extradata.played local function roleLeftDisplay() @@ -78,7 +79,7 @@ local function getRoleDisplays(player) return role.display end end - if playerType == 'former' then + if player.extradata.status == 'former' then return 'Left' elseif not played then return 'DNP' @@ -94,7 +95,7 @@ local ParticipantsTeamRoster = Class.new(Widget) ---@return Widget function ParticipantsTeamRoster:render() local participant = self.props.participant - local makeRostersDisplay = function(players) + local makeRostersDisplay = function(players, tabType) -- Used for making the sorting stable local playerToIndex = Table.map(players, function(index, player) return player, index end) -- Sort the players based on their roles first, then by their original order @@ -111,16 +112,15 @@ function ParticipantsTeamRoster:render() return orderA < orderB end) - -- Split into former players and former staff - local formerPlayers = Array.filter(players, function(player) - return player.extradata.isFormer and player.extradata.type ~= 'staff' - end) - local formerStaff = Array.filter(players, function(player) - return player.extradata.isFormer and player.extradata.type == 'staff' - end) + if tabType == TAB_ENUM.FORMER then + local formerPlayers = Array.filter(players, function(player) + return player.extradata.type ~= 'staff' + end) + local formerStaff = Array.filter(players, function(player) + return player.extradata.type == 'staff' + end) - -- If we have former players or staff, render with subheaders - if #formerPlayers > 0 or #formerStaff > 0 then + if #formerPlayers > 0 and #formerStaff > 0 then local function makeRosterSection(sectionPlayers) return Div{ classes = { 'team-participant-roster' }, @@ -163,9 +163,10 @@ function ParticipantsTeamRoster:render() classes = { 'team-participant-roster' }, children = children } + end end - -- Otherwise render as a single roster (for non-former tabs) + -- Render as a single roster return Div{ classes = { 'team-participant-roster' }, children = Array.map(players, function(player, index) @@ -190,14 +191,7 @@ function ParticipantsTeamRoster:render() local tabs = Array.map(Table.entries(TAB_DATA), function(tabTuple) local tabTypeEnum, tabData = tabTuple[1], tabTuple[2] local tabPlayers = Array.filter(participant.opponent.players or {}, function(player) - local personType = player.extradata.type - if tabTypeEnum == TAB_ENUM.STAFF then - return personType == 'staff' - elseif tabTypeEnum == TAB_ENUM.FORMER then - return player.extradata.isFormer - else - return PERSON_TYPE_TO_TAB[personType] == tabTypeEnum - end + return getPlayerTab(player) == tabTypeEnum end) return { order = tabData.order, @@ -232,7 +226,7 @@ function ParticipantsTeamRoster:render() tabs = Array.map(tabs, function(tab) return { label = tab.title, - content = makeRostersDisplay(tab.players), + content = makeRostersDisplay(tab.players, tab.type), } end), } From 98c3999bbea29506cf402b2a57268041f57e8b30 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Tue, 17 Mar 2026 15:55:21 +0200 Subject: [PATCH 06/10] fix identation --- .../Widget/Participants/Team/Roster.lua | 78 +++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/lua/wikis/commons/Widget/Participants/Team/Roster.lua b/lua/wikis/commons/Widget/Participants/Team/Roster.lua index 64d534e086e..2998e3419f3 100644 --- a/lua/wikis/commons/Widget/Participants/Team/Roster.lua +++ b/lua/wikis/commons/Widget/Participants/Team/Roster.lua @@ -121,49 +121,49 @@ function ParticipantsTeamRoster:render() end) if #formerPlayers > 0 and #formerStaff > 0 then - local function makeRosterSection(sectionPlayers) + local function makeRosterSection(sectionPlayers) + return Div{ + classes = { 'team-participant-roster' }, + children = Array.map(sectionPlayers, function(player, index) + local playerTeam = participant.opponent.template ~= player.team and player.team or nil + local playerTeamAsOpponent = playerTeam and Opponent.readOpponentArgs{ + type = Opponent.team, + template = playerTeam, + } or nil + local roleLeft, roleRight = getRoleDisplays(player) + return ParticipantsTeamMember{ + player = player, + team = playerTeamAsOpponent, + even = index % 2 == 0, + roleLeft = roleLeft, + roleRight = roleRight, + trophies = player.extradata.trophies or 0, + } + end) + } + end + + local children = {} + if #formerPlayers > 0 then + table.insert(children, Div{ + classes = {'team-participant-card__subheader'}, + children = 'Players' + }) + table.insert(children, makeRosterSection(formerPlayers)) + end + if #formerStaff > 0 then + table.insert(children, Div{ + classes = {'team-participant-card__subheader'}, + children = 'Staff' + }) + table.insert(children, makeRosterSection(formerStaff)) + end + return Div{ classes = { 'team-participant-roster' }, - children = Array.map(sectionPlayers, function(player, index) - local playerTeam = participant.opponent.template ~= player.team and player.team or nil - local playerTeamAsOpponent = playerTeam and Opponent.readOpponentArgs{ - type = Opponent.team, - template = playerTeam, - } or nil - local roleLeft, roleRight = getRoleDisplays(player) - return ParticipantsTeamMember{ - player = player, - team = playerTeamAsOpponent, - even = index % 2 == 0, - roleLeft = roleLeft, - roleRight = roleRight, - trophies = player.extradata.trophies or 0, - } - end) + children = children } end - - local children = {} - if #formerPlayers > 0 then - table.insert(children, Div{ - classes = {'team-participant-card__subheader'}, - children = 'Players' - }) - table.insert(children, makeRosterSection(formerPlayers)) - end - if #formerStaff > 0 then - table.insert(children, Div{ - classes = {'team-participant-card__subheader'}, - children = 'Staff' - }) - table.insert(children, makeRosterSection(formerStaff)) - end - - return Div{ - classes = { 'team-participant-roster' }, - children = children - } - end end -- Render as a single roster From 3c2db0aa5c53080fd1415ad52b730a3036add05f Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Tue, 17 Mar 2026 16:01:17 +0200 Subject: [PATCH 07/10] only set status if other than active --- lua/spec/team_participants_tbd_spec.lua | 38 +++++++++---------- .../commons/TeamParticipants/Controller.lua | 2 +- .../commons/TeamParticipants/Parse/Wiki.lua | 4 +- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/lua/spec/team_participants_tbd_spec.lua b/lua/spec/team_participants_tbd_spec.lua index 75f522f74d2..8608e4a0414 100644 --- a/lua/spec/team_participants_tbd_spec.lua +++ b/lua/spec/team_participants_tbd_spec.lua @@ -26,9 +26,9 @@ describe('Team Participants TBD Functionality', function() it('fills roster when player count is below expected', function() local opponent = { players = { - {displayName = 'Player1', extradata = {type = 'player', status = 'active'}}, - {displayName = 'Player2', extradata = {type = 'player', status = 'active'}}, - {displayName = 'Player3', extradata = {type = 'player', status = 'active'}}, + {displayName = 'Player1', extradata = {type = 'player'}}, + {displayName = 'Player2', extradata = {type = 'player'}}, + {displayName = 'Player3', extradata = {type = 'player'}}, } } @@ -43,11 +43,11 @@ describe('Team Participants TBD Functionality', function() it('does not fill when roster is complete', function() local opponent = { players = { - {displayName = 'Player1', extradata = {type = 'player', status = 'active'}}, - {displayName = 'Player2', extradata = {type = 'player', status = 'active'}}, - {displayName = 'Player3', extradata = {type = 'player', status = 'active'}}, - {displayName = 'Player4', extradata = {type = 'player', status = 'active'}}, - {displayName = 'Player5', extradata = {type = 'player', status = 'active'}}, + {displayName = 'Player1', extradata = {type = 'player'}}, + {displayName = 'Player2', extradata = {type = 'player'}}, + {displayName = 'Player3', extradata = {type = 'player'}}, + {displayName = 'Player4', extradata = {type = 'player'}}, + {displayName = 'Player5', extradata = {type = 'player'}}, } } @@ -60,13 +60,13 @@ describe('Team Participants TBD Functionality', function() it('does not fill when roster exceeds expected', function() local opponent = { players = { - {displayName = 'Player1', extradata = {type = 'player', status = 'active'}}, - {displayName = 'Player2', extradata = {type = 'player', status = 'active'}}, - {displayName = 'Player3', extradata = {type = 'player', status = 'active'}}, - {displayName = 'Player4', extradata = {type = 'player', status = 'active'}}, - {displayName = 'Player5', extradata = {type = 'player', status = 'active'}}, - {displayName = 'Player6', extradata = {type = 'player', status = 'active'}}, - {displayName = 'Player7', extradata = {type = 'player', status = 'active'}}, + {displayName = 'Player1', extradata = {type = 'player'}}, + {displayName = 'Player2', extradata = {type = 'player'}}, + {displayName = 'Player3', extradata = {type = 'player'}}, + {displayName = 'Player4', extradata = {type = 'player'}}, + {displayName = 'Player5', extradata = {type = 'player'}}, + {displayName = 'Player6', extradata = {type = 'player'}}, + {displayName = 'Player7', extradata = {type = 'player'}}, } } @@ -78,9 +78,9 @@ describe('Team Participants TBD Functionality', function() it('only counts type=player when checking roster size', function() local opponent = { players = { - {displayName = 'Player1', extradata = {type = 'player', status = 'active'}}, - {displayName = 'Player2', extradata = {type = 'player', status = 'active'}}, - {displayName = 'Player3', extradata = {type = 'player', status = 'active'}}, + {displayName = 'Player1', extradata = {type = 'player'}}, + {displayName = 'Player2', extradata = {type = 'player'}}, + {displayName = 'Player3', extradata = {type = 'player'}}, {displayName = 'Coach1', extradata = {type = 'staff'}}, {displayName = 'Coach2', extradata = {type = 'staff'}}, } @@ -98,7 +98,7 @@ describe('Team Participants TBD Functionality', function() it('handles missing data gracefully', function() local opponent1 = { players = { - {displayName = 'Player1', extradata = {type = 'player', status = 'active'}}, + {displayName = 'Player1', extradata = {type = 'player'}}, } } TeamParticipantsWikiParser.fillIncompleteRoster(opponent1, nil) diff --git a/lua/wikis/commons/TeamParticipants/Controller.lua b/lua/wikis/commons/TeamParticipants/Controller.lua index f41388e3692..ff03ef69e71 100644 --- a/lua/wikis/commons/TeamParticipants/Controller.lua +++ b/lua/wikis/commons/TeamParticipants/Controller.lua @@ -105,7 +105,7 @@ function TeamParticipantsController.importSquadMembersFromDatabase(participant) end) return Array.map(membersToImport, function (member) - local status = 'active' + local status if member.hasLeft then status = 'former' elseif member.role and member.role:lower() == 'substitute' then diff --git a/lua/wikis/commons/TeamParticipants/Parse/Wiki.lua b/lua/wikis/commons/TeamParticipants/Parse/Wiki.lua index f25c59c5277..b5fdbc23bf5 100644 --- a/lua/wikis/commons/TeamParticipants/Parse/Wiki.lua +++ b/lua/wikis/commons/TeamParticipants/Parse/Wiki.lua @@ -220,8 +220,6 @@ function TeamParticipantsWikiParser.parsePlayer(playerInput) status = 'former' elseif inputType == 'sub' then status = 'sub' - else - status = 'active' end end @@ -254,7 +252,7 @@ function TeamParticipantsWikiParser.fillIncompleteRoster(opponent, minimumPlayer end local actualPlayers = Array.filter(opponent.players, function(player) - return player.extradata.type == 'player' and player.extradata.status == 'active' + return player.extradata.type == 'player' and not player.extradata.status end) local actualPlayerCount = #actualPlayers From 62f55de4b58faa06118be7367a0eaf167904310e Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Thu, 19 Mar 2026 15:26:50 +0200 Subject: [PATCH 08/10] rename vars --- lua/wikis/commons/TeamParticipants/Parse/Wiki.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lua/wikis/commons/TeamParticipants/Parse/Wiki.lua b/lua/wikis/commons/TeamParticipants/Parse/Wiki.lua index b5fdbc23bf5..ae2c8e00b5e 100644 --- a/lua/wikis/commons/TeamParticipants/Parse/Wiki.lua +++ b/lua/wikis/commons/TeamParticipants/Parse/Wiki.lua @@ -251,16 +251,16 @@ function TeamParticipantsWikiParser.fillIncompleteRoster(opponent, minimumPlayer return end - local actualPlayers = Array.filter(opponent.players, function(player) + local activePlayers = Array.filter(opponent.players, function(player) return player.extradata.type == 'player' and not player.extradata.status end) - local actualPlayerCount = #actualPlayers - if actualPlayerCount >= expectedPlayerCount then + local activePlayerCount = #activePlayers + if activePlayerCount >= expectedPlayerCount then return end - local tbdPlayers = TeamParticipantsWikiParser.createTBDPlayers(expectedPlayerCount - actualPlayerCount) + local tbdPlayers = TeamParticipantsWikiParser.createTBDPlayers(expectedPlayerCount - activePlayerCount) Array.extendWith(opponent.players, tbdPlayers) end From 2e22c316dfe060bc28d68d15221b3f87a725755c Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Tue, 24 Mar 2026 10:52:39 +0200 Subject: [PATCH 09/10] rework Roster rendering logic --- .../Widget/Participants/Team/Roster.lua | 143 ++++++++---------- 1 file changed, 67 insertions(+), 76 deletions(-) diff --git a/lua/wikis/commons/Widget/Participants/Team/Roster.lua b/lua/wikis/commons/Widget/Participants/Team/Roster.lua index 19535251aa4..5f69b68a754 100644 --- a/lua/wikis/commons/Widget/Participants/Team/Roster.lua +++ b/lua/wikis/commons/Widget/Participants/Team/Roster.lua @@ -101,11 +101,11 @@ local ParticipantsTeamRoster = Class.new(Widget) ---@return Widget function ParticipantsTeamRoster:render() local participant = self.props.participant - local makeRostersDisplay = function(players, tabType) - -- Used for making the sorting stable + + -- Used for making the sorting stable + local sortPlayers = function(players) local playerToIndex = Table.map(players, function(index, player) return player, index end) - -- Sort the players based on their roles first, then by their original order - players = Array.sortBy(players, FnUtil.identity, function (a, b) + return Array.sortBy(players, FnUtil.identity, function(a, b) local function getPlayerSortOrder(player) local roles = player.extradata.roles or {} return roles[1] and roles[1].sortOrder or math.huge @@ -117,92 +117,82 @@ function ParticipantsTeamRoster:render() end return orderA < orderB end) + end - if tabType == TAB_ENUM.FORMER then - local formerPlayers = Array.filter(players, function(player) - return player.extradata.type ~= 'staff' - end) - local formerStaff = Array.filter(players, function(player) - return player.extradata.type == 'staff' - end) + local makePlayerWidget = function(player, index) + local playerTeam = participant.opponent.template ~= player.team and player.team or nil + local playerTeamAsOpponent = playerTeam and Opponent.readOpponentArgs{ + type = Opponent.team, + template = playerTeam, + } or nil + local roleLeft, roleRight = getRoleDisplays(player) + return ParticipantsTeamMember{ + player = player, + team = playerTeamAsOpponent, + even = index % 2 == 0, + roleLeft = roleLeft, + roleRight = roleRight, + trophies = player.extradata.trophies or 0, + } + end - if #formerPlayers > 0 and #formerStaff > 0 then - local function makeRosterSection(sectionPlayers) - return Div{ - classes = { 'team-participant-roster' }, - children = Array.map(sectionPlayers, function(player, index) - local playerTeam = participant.opponent.template ~= player.team and player.team or nil - local playerTeamAsOpponent = playerTeam and Opponent.readOpponentArgs{ - type = Opponent.team, - template = playerTeam, - } or nil - local roleLeft, roleRight = getRoleDisplays(player) - return ParticipantsTeamMember{ - player = player, - team = playerTeamAsOpponent, - even = index % 2 == 0, - roleLeft = roleLeft, - roleRight = roleRight, - trophies = player.extradata.trophies or 0, - } - end) - } - end - - local children = {} - if #formerPlayers > 0 then - table.insert(children, Div{ - classes = {'team-participant-card__subheader'}, - children = 'Players' - }) - table.insert(children, makeRosterSection(formerPlayers)) - end - if #formerStaff > 0 then - table.insert(children, Div{ - classes = {'team-participant-card__subheader'}, - children = 'Staff' - }) - table.insert(children, makeRosterSection(formerStaff)) - end - - return Div{ - classes = { 'team-participant-roster' }, - children = children - } + ---@param groups {label: string?, players: table[]}[] + local makeRostersDisplay = function(groups) + if #groups == 1 then + return Div{ + classes = { 'team-participant-roster' }, + children = Array.map(groups[1].players, makePlayerWidget), + } + end + local children = {} + for _, group in ipairs(groups) do + if group.label then + table.insert(children, Div{ + classes = {'team-participant-card__subheader'}, + children = group.label, + }) end + table.insert(children, Div{ + classes = { 'team-participant-roster' }, + children = Array.map(group.players, makePlayerWidget), + }) end - - -- Render as a single roster return Div{ classes = { 'team-participant-roster' }, - children = Array.map(players, function(player, index) - local playerTeam = participant.opponent.template ~= player.team and player.team or nil - local playerTeamAsOpponent = playerTeam and Opponent.readOpponentArgs{ - type = Opponent.team, - template = playerTeam, - } or nil - local roleLeft, roleRight = getRoleDisplays(player) - return ParticipantsTeamMember{ - player = player, - team = playerTeamAsOpponent, - even = index % 2 == 0, - roleLeft = roleLeft, - roleRight = roleRight, - trophies = player.extradata.trophies or 0, - } - end) + children = children, } end local tabs = Array.map(Table.entries(TAB_DATA), function(tabTuple) local tabTypeEnum, tabData = tabTuple[1], tabTuple[2] - local tabPlayers = Array.filter(participant.opponent.players or {}, function(player) + local tabPlayers = sortPlayers(Array.filter(participant.opponent.players or {}, function(player) return getPlayerTab(player) == tabTypeEnum - end) + end)) + + local groups + if tabTypeEnum == TAB_ENUM.FORMER then + local formerPlayers = Array.filter(tabPlayers, function(player) + return player.extradata.type ~= 'staff' + end) + local formerStaff = Array.filter(tabPlayers, function(player) + return player.extradata.type == 'staff' + end) + if #formerPlayers > 0 and #formerStaff > 0 then + groups = { + { label = 'Players', players = formerPlayers }, + { label = 'Staff', players = formerStaff }, + } + end + end + if not groups then + groups = { { label = nil, players = tabPlayers } } + end + return { order = tabData.order, title = tabData.title, type = tabTypeEnum, + groups = groups, players = tabPlayers, } end) @@ -217,7 +207,8 @@ function ParticipantsTeamRoster:render() and #tabs[2].players == 1 then -- If we only have main and staff, and exactly one staff, just show both rosters without a switch - return makeRostersDisplay(Array.extend(tabs[1].players, tabs[2].players)) + local mergedPlayers = sortPlayers(Array.extend(tabs[1].players, tabs[2].players)) + return makeRostersDisplay({ { label = nil, players = mergedPlayers } }) end tabs = Array.sortBy(tabs, Operator.property('order')) @@ -232,7 +223,7 @@ function ParticipantsTeamRoster:render() tabs = Array.map(tabs, function(tab) return { label = tab.title, - content = makeRostersDisplay(tab.players, tab.type), + content = makeRostersDisplay(tab.groups), } end), } From ab5bdeb9934dfe25e48bb9092c6c5dd3ab41788d Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Tue, 24 Mar 2026 21:25:13 +0200 Subject: [PATCH 10/10] remove early-return shortcut and label nil calls --- lua/spec/team_participants_tbd_spec.lua | 18 +++++++++--------- .../Widget/Participants/Team/Roster.lua | 10 ++-------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/lua/spec/team_participants_tbd_spec.lua b/lua/spec/team_participants_tbd_spec.lua index 8608e4a0414..1a1ac1a2ba8 100644 --- a/lua/spec/team_participants_tbd_spec.lua +++ b/lua/spec/team_participants_tbd_spec.lua @@ -89,10 +89,10 @@ describe('Team Participants TBD Functionality', function() TeamParticipantsWikiParser.fillIncompleteRoster(opponent, 5) assert.are_equal(7, #opponent.players) - local actualPlayers = Array.filter(opponent.players, function(p) - return p.extradata.type == 'player' + local activePlayers = Array.filter(opponent.players, function(p) + return p.extradata.type == 'player' and not p.extradata.status end) - assert.are_equal(5, #actualPlayers) + assert.are_equal(5, #activePlayers) end) it('handles missing data gracefully', function() @@ -184,14 +184,14 @@ describe('Team Participants TBD Functionality', function() TeamParticipantsController.fillIncompleteRosters(parsedData) local opponent = parsedData.participants[1].opponent - local actualPlayers = Array.filter(opponent.players, function(p) - return p.extradata.type == 'player' + local activePlayers = Array.filter(opponent.players, function(p) + return p.extradata.type == 'player' and not p.extradata.status end) - assert.are_equal(5, #actualPlayers) - assert.are_equal('alexis', actualPlayers[1].displayName) - assert.are_equal('TBD', actualPlayers[4].displayName) - assert.are_equal('TBD', actualPlayers[5].displayName) + assert.are_equal(5, #activePlayers) + assert.are_equal('alexis', activePlayers[1].displayName) + assert.are_equal('TBD', activePlayers[4].displayName) + assert.are_equal('TBD', activePlayers[5].displayName) LpdbQuery:revert() TeamTemplateMock.tearDown() diff --git a/lua/wikis/commons/Widget/Participants/Team/Roster.lua b/lua/wikis/commons/Widget/Participants/Team/Roster.lua index 5f69b68a754..442608350bd 100644 --- a/lua/wikis/commons/Widget/Participants/Team/Roster.lua +++ b/lua/wikis/commons/Widget/Participants/Team/Roster.lua @@ -138,12 +138,6 @@ function ParticipantsTeamRoster:render() ---@param groups {label: string?, players: table[]}[] local makeRostersDisplay = function(groups) - if #groups == 1 then - return Div{ - classes = { 'team-participant-roster' }, - children = Array.map(groups[1].players, makePlayerWidget), - } - end local children = {} for _, group in ipairs(groups) do if group.label then @@ -185,7 +179,7 @@ function ParticipantsTeamRoster:render() end end if not groups then - groups = { { label = nil, players = tabPlayers } } + groups = { { players = tabPlayers } } end return { @@ -208,7 +202,7 @@ function ParticipantsTeamRoster:render() then -- If we only have main and staff, and exactly one staff, just show both rosters without a switch local mergedPlayers = sortPlayers(Array.extend(tabs[1].players, tabs[2].players)) - return makeRostersDisplay({ { label = nil, players = mergedPlayers } }) + return makeRostersDisplay({ { players = mergedPlayers } }) end tabs = Array.sortBy(tabs, Operator.property('order'))