Module:TeamDashboard: Difference between revisions
From eSportsAmaze
More actions
Esportsamaze (talk | contribs) No edit summary |
Esportsamaze (talk | contribs) No edit summary |
||
| (2 intermediate revisions by the same user not shown) | |||
| Line 3: | Line 3: | ||
local html = mw.html | local html = mw.html | ||
local lang = mw.getContentLanguage() | local lang = mw.getContentLanguage() | ||
local function esc(s) | local function esc(s) | ||
| Line 45: | Line 43: | ||
return (FLAGS[c:lower():gsub("%s+",""):gsub("-","")] or "").." " | return (FLAGS[c:lower():gsub("%s+",""):gsub("-","")] or "").." " | ||
end | end | ||
local roleConfig = { | local roleConfig = { | ||
{key="igl", sort=1, color="#eab308"}, | {key="igl", sort=1, color="#eab308"}, | ||
| Line 64: | Line 60: | ||
if not role then return "#64748b" end | if not role then return "#64748b" end | ||
local r = role:lower() | local r = role:lower() | ||
for _,cfg in ipairs(roleConfig) do | for _,cfg in ipairs(roleConfig) do if r:find(cfg.key) then return cfg.color end end | ||
return "#64748b" | return "#64748b" | ||
end | end | ||
| Line 72: | Line 66: | ||
if not role then return 99 end | if not role then return 99 end | ||
local r = role:lower() | local r = role:lower() | ||
for _,cfg in ipairs(roleConfig) do | for _,cfg in ipairs(roleConfig) do if r:find(cfg.key) then return cfg.sort end end | ||
return 99 | return 99 | ||
end | end | ||
local function renderPlaceBadge(td, placement) | local function renderPlaceBadge(td, placement) | ||
local rank | local rank=tonumber(placement) or 99 | ||
local | local txt =tostring(placement or "—") | ||
local | local cls ="place-def" | ||
if rank==1 then | if rank==1 then cls="place-1"; txt="1st" | ||
elseif rank==2 then | elseif rank==2 then cls="place-2"; txt="2nd" | ||
elseif rank==3 then | elseif rank==3 then cls="place-3"; txt="3rd" | ||
elseif | elseif txt:match("^%d+$") then txt=txt.."th" | ||
end | end | ||
td:tag('span'):addClass('place-badge '.. | td:tag('span'):addClass('place-badge '..cls):wikitext(txt) | ||
end | end | ||
| Line 97: | Line 88: | ||
local sqlTeam = esc(teamName) | local sqlTeam = esc(teamName) | ||
-- ── 1. Team data | -- ── 1. Team data ────────────────────────────────────────────── | ||
local tRows = cargo.query("Teams", | local tRows = cargo.query("Teams", | ||
"display_name,short_code,game,image,image_dark,country,status," | "display_name,short_code,game,image,image_dark,country,status," | ||
| Line 103: | Line 94: | ||
{where="name='"..sqlTeam.."'", limit=1}) | {where="name='"..sqlTeam.."'", limit=1}) | ||
local tm = (tRows and #tRows>0) and tRows[1] or {} | local tm = (tRows and #tRows>0) and tRows[1] or {} | ||
local displayName = (tm.display_name and tm.display_name~="") | local displayName = (tm.display_name and tm.display_name~="") | ||
and tm.display_name or teamName | and tm.display_name or teamName | ||
local teamGame = tm.game or "" | |||
-- ── 2. Krafton rank ─────────────────────────────────────────── | -- ── 2. Krafton rank ─────────────────────────────────────────── | ||
| Line 120: | Line 111: | ||
-- ── 4. Active roster ───────────────────────────────────────── | -- ── 4. Active roster ───────────────────────────────────────── | ||
local dbPlayers = {} | local dbPlayers = {} | ||
local dbRows = cargo.query("Players","id,real_name,role,nationality,image,_pageName", | local dbRows = cargo.query("Players","id,real_name,role,nationality,image,_pageName", | ||
| Line 126: | Line 116: | ||
if dbRows then | if dbRows then | ||
for _,r in ipairs(dbRows) do | for _,r in ipairs(dbRows) do | ||
dbPlayers[r.id] = { | dbPlayers[r.id]={id=r.id, name=r.real_name or "", | ||
role=r.role or "Player", nat=r.nationality or "", | |||
image=r.image or "", link=r._pageName or r.id} | |||
end | end | ||
end | end | ||
local roster={} | |||
local roster = {} | |||
for _,p2 in pairs(dbPlayers) do table.insert(roster,p2) end | for _,p2 in pairs(dbPlayers) do table.insert(roster,p2) end | ||
for i=1,10 do | for i=1,10 do | ||
local mid = clean(args["player"..i]) | local mid=clean(args["player"..i]) | ||
if mid and not dbPlayers[mid] then | if mid and not dbPlayers[mid] then | ||
table.insert(roster,{ | table.insert(roster,{id=mid, name=clean(args["name"..i]) or "", | ||
role=clean(args["role"..i]) or "Player", | |||
nat=clean(args["flag"..i]) or "", | |||
role | image=clean(args["image"..i]) or "", link=mid}) | ||
nat | |||
image = clean(args["image"..i]) or "", | |||
end | end | ||
end | end | ||
table.sort(roster,function(a,b) | table.sort(roster,function(a,b) | ||
local sa,sb = getRoleSort(a.role),getRoleSort(b.role) | local sa,sb=getRoleSort(a.role),getRoleSort(b.role) | ||
if sa~=sb then return sa<sb end | if sa~=sb then return sa<sb end | ||
return (a.id or ""):lower()<(b.id or ""):lower() | return (a.id or ""):lower()<(b.id or ""):lower() | ||
end) | end) | ||
-- ── 5. Former players | -- ── 5. Former players ───────────────────────────────────────── | ||
local fRows = cargo.query("Player_Former_Teams", | local fRows = cargo.query("Player_Former_Teams", | ||
"player_id,role,join_date,leave_date", | "player_id,role,join_date,leave_date", | ||
{ where | {where="team='"..sqlTeam.."' AND leave_date!=''", | ||
orderBy="leave_date DESC", limit=200}) | |||
fRows = fRows or {} | fRows = fRows or {} | ||
local pageNames={} | |||
local pageNames = {} | |||
for _,r in ipairs(fRows) do | for _,r in ipairs(fRows) do | ||
if r.player_id and r.player_id~="" then | if r.player_id and r.player_id~="" then | ||
| Line 171: | Line 150: | ||
end | end | ||
end | end | ||
local playerDetails = {} | local playerDetails={} | ||
if #pageNames>0 then | if #pageNames>0 then | ||
local dRows = cargo.query("Players","_pageName,real_name,id", | local dRows=cargo.query("Players","_pageName,real_name,id", | ||
{ where="_pageName IN ("..table.concat(pageNames,",")..")", limit=200 }) | {where="_pageName IN ("..table.concat(pageNames,",")..")", limit=200}) | ||
if dRows then | if dRows then | ||
for _,d in ipairs(dRows) do | for _,d in ipairs(dRows) do | ||
| Line 181: | Line 160: | ||
end | end | ||
end | end | ||
local formerRows={} | |||
local formerRows = {} | |||
for _,r in ipairs(fRows) do | for _,r in ipairs(fRows) do | ||
local d = playerDetails[r.player_id] or {} | local d=playerDetails[r.player_id] or {} | ||
table.insert(formerRows, { | table.insert(formerRows,{pid=r.player_id, role=r.role, | ||
join_date=r.join_date, leave_date=r.leave_date, | |||
real_name=d.real_name or "", short_id=d.short_id or ""}) | |||
join_date | |||
real_name | |||
end | end | ||
-- Group by year | -- Group by year | ||
local formerByYear,yearOrder,yearSeen = {},{},{} | local formerByYear,yearOrder,yearSeen={},{},{} | ||
for _,r in ipairs(formerRows) do | for _,r in ipairs(formerRows) do | ||
local year = (r.leave_date or ""):match("^(%d%d%d%d)") or "Unknown" | local year=(r.leave_date or ""):match("^(%d%d%d%d)") or "Unknown" | ||
if not yearSeen[year] then yearSeen[year]=true; table.insert(yearOrder,year) end | if not yearSeen[year] then yearSeen[year]=true; table.insert(yearOrder,year) end | ||
if not formerByYear[year] then formerByYear[year]={} end | if not formerByYear[year] then formerByYear[year]={} end | ||
| Line 207: | Line 179: | ||
end) | end) | ||
-- ── 6. Achievements | -- ── 6. Achievements filtered by game ───────────────────────── | ||
local | local achRaw=cargo.query("PrizeMoney","tournament,placement,prize,award", | ||
{where="team='"..sqlTeam.."' AND (player='' OR player IS NULL)", | {where="team='"..sqlTeam.."' AND (player='' OR player IS NULL)", | ||
orderBy="prize DESC", limit= | orderBy="prize DESC", limit=100}) | ||
achRaw=achRaw or {} | |||
local tournNames,tournSeen2={},{} | |||
for _,r in ipairs(achRaw) do | |||
local tournNames,tournSeen2 = {},{} | |||
for _,r in ipairs( | |||
if r.tournament and r.tournament~="" and not tournSeen2[r.tournament] then | if r.tournament and r.tournament~="" and not tournSeen2[r.tournament] then | ||
tournSeen2[r.tournament]=true | tournSeen2[r.tournament]=true | ||
| Line 221: | Line 191: | ||
end | end | ||
end | end | ||
local tournMeta = {} | local tournMeta={} | ||
if #tournNames>0 then | if #tournNames>0 then | ||
local mRows = cargo.query("Tournaments","name,tier,end_date", | local mRows=cargo.query("Tournaments","name,tier,end_date,game", | ||
{where="name IN ("..table.concat(tournNames,",")..")", limit=200}) | {where="name IN ("..table.concat(tournNames,",")..")", limit=200}) | ||
if mRows then | if mRows then | ||
for _,row in ipairs(mRows) do | for _,row in ipairs(mRows) do | ||
tournMeta[row.name]={tier=row.tier, date=row.end_date} | tournMeta[row.name]={tier=row.tier, date=row.end_date, game=row.game} | ||
end | |||
end | |||
end | |||
local achRows={} | |||
for _,r in ipairs(achRaw) do | |||
if teamGame=="" then | |||
table.insert(achRows,r) | |||
else | |||
local meta=tournMeta[r.tournament or ""] or {} | |||
local tg=(meta.game or ""):lower() | |||
local tq=teamGame:lower() | |||
if tg=="" or tg:find(tq,1,true) or tq:find(tg,1,true) then | |||
table.insert(achRows,r) | |||
end | end | ||
end | end | ||
| Line 234: | Line 217: | ||
-- ── BUILD HTML ──────────────────────────────────────────────── | -- ── BUILD HTML ──────────────────────────────────────────────── | ||
local root = html.create('div'):addClass('td-dashboard') | local root=html.create('div'):addClass('td-dashboard') | ||
-- ════ HERO ════ | -- ════ HERO ════ | ||
-- [ | local hero=root:tag('div'):addClass('td-hero') | ||
local lightLogo=(tm.image and tm.image~="") and tm.image or "Shield_team.png" | |||
local darkLogo =(tm.image_dark and tm.image_dark~="") and tm.image_dark or lightLogo | |||
-- Hero is always a dark gradient, so always render the dark logo. | |||
-- Using logo-lightmode/logo-darkmode here would show the light logo in | |||
-- light mode, making black logos invisible against the dark background. | |||
local logoDiv=hero:tag('div'):addClass('td-hero-logo') | |||
logoDiv:wikitext("[[File:"..darkLogo.."|64px|link=]]") | |||
local heroInfo=hero:tag('div'):addClass('td-hero-info') | |||
local heroInfo = hero:tag('div'):addClass('td-hero-info') | |||
heroInfo:tag('div'):addClass('td-hero-name'):wikitext(displayName) | heroInfo:tag('div'):addClass('td-hero-name'):wikitext(displayName) | ||
local tags=heroInfo:tag('div'):addClass('pd-hero-tags') | |||
local tags = heroInfo:tag('div'):addClass('pd-hero-tags') | local sl=(tm.status or "active"):lower() | ||
local sl = (tm.status or "active"):lower() | local sc,sbg,sborder | ||
local | if sl=="active" then sc="#4ade80";sbg="rgba(34,197,94,.2)";sborder="rgba(34,197,94,.35)" | ||
if sl=="active" then | elseif sl=="inactive" or sl=="disbanded" then sc="#f87171";sbg="rgba(239,68,68,.2)";sborder="rgba(239,68,68,.35)" | ||
else sc="#94a3b8";sbg="rgba(148,163,184,.2)";sborder="rgba(148,163,184,.35)" end | |||
elseif sl=="inactive" or sl=="disbanded" then | |||
else | |||
tags:tag('span'):addClass('pd-tag') | tags:tag('span'):addClass('pd-tag') | ||
:css('color', | :css('color',sc):css('background',sbg):css('border','1px solid '..sborder) | ||
:wikitext('● '..(tm.status or "Active")) | :wikitext('● '..(tm.status or "Active")) | ||
if | if teamGame~="" then | ||
tags:tag('span'):addClass('pd-tag pd-tag-game'):wikitext( | tags:tag('span'):addClass('pd-tag pd-tag-game'):wikitext(teamGame) | ||
end | end | ||
if rankVal then | if rankVal then | ||
tags:tag('span'):addClass('pd-tag') | tags:tag('span'):addClass('pd-tag') | ||
:css('color','#fbbf24') | :css('color','#fbbf24'):css('background','rgba(251,191,36,.15)') | ||
:css('border','1px solid rgba(251,191,36,.3)') | :css('border','1px solid rgba(251,191,36,.3)') | ||
:wikitext('Krafton '..rankVal) | :wikitext('Krafton '..rankVal) | ||
end | end | ||
if totalEarnings>0 then | if totalEarnings>0 then | ||
local er = hero:tag('div'):addClass('pd-hero-right') | local er=hero:tag('div'):addClass('pd-hero-right') | ||
local et = er:tag('div'):addClass('pd-earnings-total') | local et=er:tag('div'):addClass('pd-earnings-total') | ||
et:tag('div'):addClass('pd-earnings-label'):wikitext('Total Earnings') | et:tag('div'):addClass('pd-earnings-label'):wikitext('Total Earnings') | ||
et:tag('div'):addClass('pd-earnings-val'):wikitext(fmtCurrency(totalEarnings)) | et:tag('div'):addClass('pd-earnings-val'):wikitext(fmtCurrency(totalEarnings)) | ||
| Line 286: | Line 257: | ||
-- ════ BODY ════ | -- ════ BODY ════ | ||
local body = root:tag('div'):addClass('td-body') | local body=root:tag('div'):addClass('td-body') | ||
-- ── LEFT | -- ── LEFT ───────────────────────────────────────────────────── | ||
local left = body:tag('div'):addClass('td-left') | local left=body:tag('div'):addClass('td-left') | ||
left:tag('div'):addClass('pd-section-title'):wikitext('Team Info') | left:tag('div'):addClass('pd-section-title'):wikitext('Team Info') | ||
local function infoRow(label,val) | local function infoRow(label,val) | ||
if not val or val=="" then return end | if not val or val=="" then return end | ||
| Line 298: | Line 268: | ||
:tag('span'):addClass('pd-info-val'):wikitext(val) | :tag('span'):addClass('pd-info-val'):wikitext(val) | ||
end | end | ||
if tm.country and tm.country~="" then infoRow('Country',getFlag(tm.country)..tm.country) end | |||
if tm.country and tm.country~="" then | infoRow('Game',teamGame) | ||
if tm.short_code and tm.short_code~="" then infoRow('Tag',tm.short_code) end | |||
if rankVal then infoRow('Krafton Rank',rankVal) end | |||
infoRow('Game', | if totalEarnings>0 then infoRow('Total Earnings',fmtCurrency(totalEarnings)) end | ||
if tm.short_code and tm.short_code~="" then | if tm.sponsors and tm.sponsors~="" then infoRow('Sponsors',tm.sponsors:gsub(",",", ")) end | ||
local playerCount=0 | |||
if rankVal then infoRow('Krafton Rank', rankVal) end | |||
if totalEarnings>0 then | |||
if tm.sponsors and tm.sponsors~="" then | |||
local playerCount = 0 | |||
for _,pl in ipairs(roster) do | for _,pl in ipairs(roster) do | ||
local r = (pl.role or ""):lower() | local r=(pl.role or ""):lower() | ||
if not r:find("coach") and not r:find("analyst") and not r:find("manager") then | if not r:find("coach") and not r:find("analyst") and not r:find("manager") then | ||
playerCount = playerCount+1 | playerCount=playerCount+1 | ||
end | end | ||
end | end | ||
if playerCount>0 then | if playerCount>0 then infoRow('Active Players',tostring(playerCount)) end | ||
if #formerRows>0 then infoRow('Alumni',tostring(#formerRows)) end | |||
if #formerRows>0 then | |||
local socials={ | |||
local socials = { | {k='instagram',file='Icon_instagram.png',base='https://instagram.com/'}, | ||
{k='instagram', file='Icon_instagram.png', base='https://instagram.com/'}, | {k='twitter', file='Icon_twitter.png', base='https://twitter.com/' }, | ||
{k='twitter', | {k='youtube', file='Icon_youtube.png', base='https://youtube.com/' }, | ||
{k='youtube', | {k='discord', file='Icon_discord.png', base='https://discord.gg/' }, | ||
{k='discord', | {k='facebook', file='Icon_facebook.png', base='https://facebook.com/' }, | ||
{k='facebook', | {k='website', file='Icon_website.png', base='https://' }, | ||
{k='website', | |||
} | } | ||
local hasSocial = false | local hasSocial=false | ||
for _,s in ipairs(socials) do | for _,s in ipairs(socials) do if tm[s.k] and tm[s.k]~="" then hasSocial=true; break end end | ||
if hasSocial then | if hasSocial then | ||
local socDiv = left:tag('div'):addClass('pd-socials') | local socDiv=left:tag('div'):addClass('pd-socials') | ||
for _,s in ipairs(socials) do | for _,s in ipairs(socials) do | ||
local v = tm[s.k] | local v=tm[s.k] | ||
if v and v~="" then | if v and v~="" then | ||
local url = v:match("^https?://") and v or (s.base..v) | local url=v:match("^https?://") and v or (s.base..v) | ||
socDiv:wikitext("[[File:"..s.file.."|32px|link="..url.."|class=social-img]]") | socDiv:wikitext("[[File:"..s.file.."|32px|link="..url.."|class=social-img]]") | ||
end | end | ||
| Line 352: | Line 305: | ||
end | end | ||
-- ── RIGHT | -- ── RIGHT ───────────────────────────────────────────────────── | ||
local right = body:tag('div'):addClass('td-right') | local right=body:tag('div'):addClass('td-right') | ||
-- ── Active Roster | -- ── Active Roster — reuses hero-player-card CSS ─────────────── | ||
right:tag('div'):addClass('pd-table-title'):wikitext('👥 Active Roster') | right:tag('div'):addClass('pd-table-title'):wikitext('👥 Active Roster') | ||
if #roster>0 then | if #roster>0 then | ||
local grid = right:tag('div'):addClass('hero-roster-grid') | local grid=right:tag('div'):addClass('hero-roster-grid') | ||
for _,pl in ipairs(roster) do | for _,pl in ipairs(roster) do | ||
local card | local card=grid:tag('div'):addClass('hero-player-card') | ||
local color = getRoleColor(pl.role | local color=getRoleColor(pl.role) | ||
local imgDiv=card:tag('div'):addClass('hero-card-image') | |||
local imgFile=(pl.image~="") and pl.image or "Player_Placeholder.png" | |||
local imgDiv = card:tag('div'):addClass('hero-card-image') | |||
local imgFile = (pl.image~="") and pl.image or "Player_Placeholder.png" | |||
imgDiv:wikitext("[[File:"..imgFile.."|link="..(pl.link or pl.id).."]]") | imgDiv:wikitext("[[File:"..imgFile.."|link="..(pl.link or pl.id).."]]") | ||
local cb=card:tag('div'):addClass('hero-card-body') | |||
cb:tag('div'):addClass('hero-role-pill'):css('background-color',color) | |||
local cb = card:tag('div'):addClass('hero-card-body') | :wikitext(pl.role or "Player") | ||
cb:tag('div'):addClass('hero-role-pill') | |||
cb:tag('div'):addClass('hero-player-id') | cb:tag('div'):addClass('hero-player-id') | ||
:wikitext("[["..( pl.link or pl.id).."|"..(pl.id or "").."]]") | :wikitext("[["..( pl.link or pl.id).."|"..(pl.id or "").."]]") | ||
if pl.name~="" then | if pl.name~="" then cb:tag('div'):addClass('hero-player-name'):wikitext(pl.name) end | ||
if pl.nat~="" then | if pl.nat~="" then | ||
cb:tag('div'):addClass('hero-player-flag') | cb:tag('div'):addClass('hero-player-flag'):wikitext(getFlag(pl.nat)..pl.nat) | ||
end | end | ||
end | end | ||
| Line 387: | Line 332: | ||
end | end | ||
-- ── Former Players | -- ── Former Players — year-headed sections, no JS/tabs ───────── | ||
-- Tabs are impossible in MediaWiki Lua (HTML gets sanitised). | |||
-- MediaWiki | -- Instead: each year is a collapsible header + table. Clean and reliable. | ||
-- | |||
if #formerRows>0 then | if #formerRows>0 then | ||
right:tag('div'):addClass('pd-table-title td-section-gap') | right:tag('div'):addClass('pd-table-title td-section-gap') | ||
:wikitext('📋 Former Players') | :wikitext('📋 Former Players') | ||
for _,year in ipairs(yearOrder) do | for _,year in ipairs(yearOrder) do | ||
-- Year sub-header | |||
right:tag('div'):addClass('td-year-header'):wikitext(year) | |||
local tbl = | local tbl=right:tag('table'):addClass('pd-tourn-table td-former-table') | ||
local hdr = tbl:tag('tr') | local hdr=tbl:tag('tr') | ||
hdr:tag('th'):wikitext('Player') | hdr:tag('th'):wikitext('Player') | ||
hdr:tag('th'):wikitext('Name') | hdr:tag('th'):wikitext('Name') | ||
| Line 429: | Line 352: | ||
for _,r in ipairs(formerByYear[year]) do | for _,r in ipairs(formerByYear[year]) do | ||
local pid | local pid=r.pid or "" | ||
local dispId = (r.short_id and r.short_id~="") | local dispId=(r.short_id and r.short_id~="") | ||
and r.short_id or pid:gsub("^.*/","") | and r.short_id or pid:gsub("^.*/","") | ||
local tr = tbl:tag('tr') | local tr=tbl:tag('tr') | ||
tr:tag('td'):css('font-weight','700') | tr:tag('td'):css('font-weight','700') | ||
:wikitext("[["..pid.."|"..dispId.."]]") | :wikitext("[["..pid.."|"..dispId.."]]") | ||
tr:tag('td'):wikitext(r.real_name or "—") | tr:tag('td'):wikitext(r.real_name~="" and r.real_name or "—") | ||
local rCell = tr:tag('td') | local rCell=tr:tag('td') | ||
if r.role and r.role~="" then | if r.role and r.role~="" then | ||
rCell:tag('span'):addClass('td-role-badge') | rCell:tag('span'):addClass('td-role-badge') | ||
| Line 445: | Line 368: | ||
end | end | ||
end | end | ||
end | end | ||
-- ── Achievements | -- ── Achievements — matches player dashboard style ────────────── | ||
right:tag('div'):addClass('pd-table-title td-section-gap') | right:tag('div'):addClass('pd-table-title td-section-gap'):wikitext('🏆 Achievements') | ||
if #achRows>0 then | if #achRows>0 then | ||
local tbl2 = right:tag('table'):addClass('pd-tourn-table') | local tbl2=right:tag('table'):addClass('pd-tourn-table') | ||
local hdr2 = tbl2:tag('tr') | local hdr2=tbl2:tag('tr') | ||
hdr2:tag('th'):wikitext('Date') | hdr2:tag('th'):wikitext('Date') | ||
hdr2:tag('th'):wikitext('Tier') | hdr2:tag('th'):wikitext('Tier') | ||
hdr2:tag('th'):wikitext('Tournament') | hdr2:tag('th'):wikitext('Tournament') | ||
hdr2:tag('th'):addClass(' | hdr2:tag('th'):addClass('td-place-col'):wikitext('Place/Award') | ||
hdr2:tag('th'):css('text-align','right'):wikitext('Prize') | hdr2:tag('th'):css('text-align','right'):wikitext('Prize') | ||
for _,r in ipairs(achRows) do | for _,r in ipairs(achRows) do | ||
local meta = tournMeta[r.tournament or ""] or {} | local meta=tournMeta[r.tournament or ""] or {} | ||
local tr2 | local tr2=tbl2:tag('tr') | ||
tr2:tag('td'):addClass('ac-date') | tr2:tag('td'):addClass('ac-date') | ||
:wikitext(meta.date and meta.date~="" and meta.date or "—") | :wikitext(meta.date and meta.date~="" and meta.date or "—") | ||
local tierTd = tr2:tag('td'):addClass('ac-tier') | local tierTd=tr2:tag('td'):addClass('ac-tier') | ||
if meta.tier and meta.tier~="" then | if meta.tier and meta.tier~="" then | ||
tierTd:tag('span'):addClass('tier-badge '..getTierClass(meta.tier)) | tierTd:tag('span'):addClass('tier-badge '..getTierClass(meta.tier)) | ||
| Line 489: | Line 393: | ||
tr2:tag('td'):css('font-weight','600') | tr2:tag('td'):css('font-weight','600') | ||
:wikitext("[["..(r.tournament or "").."]]") | :wikitext("[["..(r.tournament or "").."]]") | ||
local pTd = tr2:tag('td'):addClass('ac-place') | local pTd=tr2:tag('td'):addClass('ac-place td-place-col') | ||
if r.award and r.award~="" then | if r.award and r.award~="" then | ||
pTd:tag('span'):addClass('pd-award-badge'):wikitext(r.award) | pTd:tag('span'):addClass('pd-award-badge'):wikitext(r.award) | ||
else | else | ||
renderPlaceBadge(pTd, r.placement) | renderPlaceBadge(pTd,r.placement) | ||
end | end | ||
tr2:tag('td'):addClass('ac-prize') | tr2:tag('td'):addClass('ac-prize') | ||
Latest revision as of 03:55, 9 March 2026
Documentation for this module may be created at Module:TeamDashboard/doc
local p = {}
local cargo = mw.ext.cargo
local html = mw.html
local lang = mw.getContentLanguage()
local function esc(s)
if not s then return "" end
return s:gsub("\\","\\\\"):gsub("'","\\'")
end
local function clean(s)
if not s then return nil end
local c = s:gsub("[\r\n]",""):gsub("^%s*(.-)%s*$","%1")
return c ~= "" and c or nil
end
local function fmtCurrency(n)
if not n or n == 0 then return "—" end
local num = math.floor(tonumber(n) or 0)
local s = tostring(num)
if #s <= 3 then return "₹ " .. s end
local result = s:sub(-3)
local rem = s:sub(1,-4)
while #rem > 2 do result = rem:sub(-2)..","..result; rem = rem:sub(1,-3) end
return "₹ " .. rem .. "," .. result
end
local function getTierClass(tier)
if not tier then return "tier-def" end
local t = tier:lower()
if t:find("s") and t:find("tier") then return "tier-s" end
if t:find("a") and t:find("tier") then return "tier-a" end
if t:find("b") and t:find("tier") then return "tier-b" end
if t:find("c") and t:find("tier") then return "tier-c" end
return "tier-def"
end
local FLAGS = {
india="🇮🇳",bangladesh="🇧🇩",pakistan="🇵🇰",nepal="🇳🇵",
srilanka="🇱🇰",myanmar="🇲🇲",indonesia="🇮🇩",thailand="🇹🇭",
malaysia="🇲🇾",philippines="🇵🇭",vietnam="🇻🇳",singapore="🇸🇬",
china="🇨🇳",japan="🇯🇵",korea="🇰🇷",usa="🇺🇸",
uk="🇬🇧",germany="🇩🇪",brazil="🇧🇷",australia="🇦🇺"
}
local function getFlag(c)
if not c or c=="" then return "" end
return (FLAGS[c:lower():gsub("%s+",""):gsub("-","")] or "").." "
end
local roleConfig = {
{key="igl", sort=1, color="#eab308"},
{key="filter", sort=2, color="#14b8a6"},
{key="entry", sort=3, color="#f97316"},
{key="assault", sort=4, color="#3b82f6"},
{key="fragger", sort=4, color="#3b82f6"},
{key="support", sort=5, color="#22c55e"},
{key="medic", sort=5, color="#22c55e"},
{key="scout", sort=6, color="#14b8a6"},
{key="sniper", sort=7, color="#ef4444"},
{key="coach", sort=8, color="#a855f7"},
{key="analyst", sort=9, color="#a855f7"},
{key="manager", sort=10, color="#1f2937"},
}
local function getRoleColor(role)
if not role then return "#64748b" end
local r = role:lower()
for _,cfg in ipairs(roleConfig) do if r:find(cfg.key) then return cfg.color end end
return "#64748b"
end
local function getRoleSort(role)
if not role then return 99 end
local r = role:lower()
for _,cfg in ipairs(roleConfig) do if r:find(cfg.key) then return cfg.sort end end
return 99
end
local function renderPlaceBadge(td, placement)
local rank=tonumber(placement) or 99
local txt =tostring(placement or "—")
local cls ="place-def"
if rank==1 then cls="place-1"; txt="1st"
elseif rank==2 then cls="place-2"; txt="2nd"
elseif rank==3 then cls="place-3"; txt="3rd"
elseif txt:match("^%d+$") then txt=txt.."th"
end
td:tag('span'):addClass('place-badge '..cls):wikitext(txt)
end
-- ── Main ─────────────────────────────────────────────────────────
function p.main(frame)
local args = (frame:getParent() and frame:getParent().args) or frame.args
local teamName = clean(args.team) or mw.title.getCurrentTitle().subpageText
local sqlTeam = esc(teamName)
-- ── 1. Team data ──────────────────────────────────────────────
local tRows = cargo.query("Teams",
"display_name,short_code,game,image,image_dark,country,status,"
.."sponsors,instagram,twitter,youtube,facebook,discord,website",
{where="name='"..sqlTeam.."'", limit=1})
local tm = (tRows and #tRows>0) and tRows[1] or {}
local displayName = (tm.display_name and tm.display_name~="")
and tm.display_name or teamName
local teamGame = tm.game or ""
-- ── 2. Krafton rank ───────────────────────────────────────────
local rankVal = nil
local rRows = cargo.query("Krafton_Rankings","rank",
{where="name='"..sqlTeam.."' AND type='Team'", limit=1})
if rRows and #rRows>0 then rankVal="#"..rRows[1].rank end
-- ── 3. Total earnings ─────────────────────────────────────────
local totalEarnings = 0
local eRows = cargo.query("PrizeMoney","SUM(prize)=total",
{where="team='"..sqlTeam.."' AND (player='' OR player IS NULL)"})
if eRows and #eRows>0 then totalEarnings=tonumber(eRows[1].total) or 0 end
-- ── 4. Active roster ─────────────────────────────────────────
local dbPlayers = {}
local dbRows = cargo.query("Players","id,real_name,role,nationality,image,_pageName",
{where="current_team='"..sqlTeam.."' AND status='Active'", limit=20})
if dbRows then
for _,r in ipairs(dbRows) do
dbPlayers[r.id]={id=r.id, name=r.real_name or "",
role=r.role or "Player", nat=r.nationality or "",
image=r.image or "", link=r._pageName or r.id}
end
end
local roster={}
for _,p2 in pairs(dbPlayers) do table.insert(roster,p2) end
for i=1,10 do
local mid=clean(args["player"..i])
if mid and not dbPlayers[mid] then
table.insert(roster,{id=mid, name=clean(args["name"..i]) or "",
role=clean(args["role"..i]) or "Player",
nat=clean(args["flag"..i]) or "",
image=clean(args["image"..i]) or "", link=mid})
end
end
table.sort(roster,function(a,b)
local sa,sb=getRoleSort(a.role),getRoleSort(b.role)
if sa~=sb then return sa<sb end
return (a.id or ""):lower()<(b.id or ""):lower()
end)
-- ── 5. Former players ─────────────────────────────────────────
local fRows = cargo.query("Player_Former_Teams",
"player_id,role,join_date,leave_date",
{where="team='"..sqlTeam.."' AND leave_date!=''",
orderBy="leave_date DESC", limit=200})
fRows = fRows or {}
local pageNames={}
for _,r in ipairs(fRows) do
if r.player_id and r.player_id~="" then
table.insert(pageNames,"'"..esc(r.player_id).."'")
end
end
local playerDetails={}
if #pageNames>0 then
local dRows=cargo.query("Players","_pageName,real_name,id",
{where="_pageName IN ("..table.concat(pageNames,",")..")", limit=200})
if dRows then
for _,d in ipairs(dRows) do
playerDetails[d._pageName]={real_name=d.real_name, short_id=d.id}
end
end
end
local formerRows={}
for _,r in ipairs(fRows) do
local d=playerDetails[r.player_id] or {}
table.insert(formerRows,{pid=r.player_id, role=r.role,
join_date=r.join_date, leave_date=r.leave_date,
real_name=d.real_name or "", short_id=d.short_id or ""})
end
-- Group by year
local formerByYear,yearOrder,yearSeen={},{},{}
for _,r in ipairs(formerRows) do
local year=(r.leave_date or ""):match("^(%d%d%d%d)") or "Unknown"
if not yearSeen[year] then yearSeen[year]=true; table.insert(yearOrder,year) end
if not formerByYear[year] then formerByYear[year]={} end
table.insert(formerByYear[year],r)
end
table.sort(yearOrder,function(a,b)
return (tonumber(a) or 0)>(tonumber(b) or 0)
end)
-- ── 6. Achievements filtered by game ─────────────────────────
local achRaw=cargo.query("PrizeMoney","tournament,placement,prize,award",
{where="team='"..sqlTeam.."' AND (player='' OR player IS NULL)",
orderBy="prize DESC", limit=100})
achRaw=achRaw or {}
local tournNames,tournSeen2={},{}
for _,r in ipairs(achRaw) do
if r.tournament and r.tournament~="" and not tournSeen2[r.tournament] then
tournSeen2[r.tournament]=true
table.insert(tournNames,"'"..esc(r.tournament).."'")
end
end
local tournMeta={}
if #tournNames>0 then
local mRows=cargo.query("Tournaments","name,tier,end_date,game",
{where="name IN ("..table.concat(tournNames,",")..")", limit=200})
if mRows then
for _,row in ipairs(mRows) do
tournMeta[row.name]={tier=row.tier, date=row.end_date, game=row.game}
end
end
end
local achRows={}
for _,r in ipairs(achRaw) do
if teamGame=="" then
table.insert(achRows,r)
else
local meta=tournMeta[r.tournament or ""] or {}
local tg=(meta.game or ""):lower()
local tq=teamGame:lower()
if tg=="" or tg:find(tq,1,true) or tq:find(tg,1,true) then
table.insert(achRows,r)
end
end
end
-- ── BUILD HTML ────────────────────────────────────────────────
local root=html.create('div'):addClass('td-dashboard')
-- ════ HERO ════
local hero=root:tag('div'):addClass('td-hero')
local lightLogo=(tm.image and tm.image~="") and tm.image or "Shield_team.png"
local darkLogo =(tm.image_dark and tm.image_dark~="") and tm.image_dark or lightLogo
-- Hero is always a dark gradient, so always render the dark logo.
-- Using logo-lightmode/logo-darkmode here would show the light logo in
-- light mode, making black logos invisible against the dark background.
local logoDiv=hero:tag('div'):addClass('td-hero-logo')
logoDiv:wikitext("[[File:"..darkLogo.."|64px|link=]]")
local heroInfo=hero:tag('div'):addClass('td-hero-info')
heroInfo:tag('div'):addClass('td-hero-name'):wikitext(displayName)
local tags=heroInfo:tag('div'):addClass('pd-hero-tags')
local sl=(tm.status or "active"):lower()
local sc,sbg,sborder
if sl=="active" then sc="#4ade80";sbg="rgba(34,197,94,.2)";sborder="rgba(34,197,94,.35)"
elseif sl=="inactive" or sl=="disbanded" then sc="#f87171";sbg="rgba(239,68,68,.2)";sborder="rgba(239,68,68,.35)"
else sc="#94a3b8";sbg="rgba(148,163,184,.2)";sborder="rgba(148,163,184,.35)" end
tags:tag('span'):addClass('pd-tag')
:css('color',sc):css('background',sbg):css('border','1px solid '..sborder)
:wikitext('● '..(tm.status or "Active"))
if teamGame~="" then
tags:tag('span'):addClass('pd-tag pd-tag-game'):wikitext(teamGame)
end
if rankVal then
tags:tag('span'):addClass('pd-tag')
:css('color','#fbbf24'):css('background','rgba(251,191,36,.15)')
:css('border','1px solid rgba(251,191,36,.3)')
:wikitext('Krafton '..rankVal)
end
if totalEarnings>0 then
local er=hero:tag('div'):addClass('pd-hero-right')
local et=er:tag('div'):addClass('pd-earnings-total')
et:tag('div'):addClass('pd-earnings-label'):wikitext('Total Earnings')
et:tag('div'):addClass('pd-earnings-val'):wikitext(fmtCurrency(totalEarnings))
end
-- ════ BODY ════
local body=root:tag('div'):addClass('td-body')
-- ── LEFT ─────────────────────────────────────────────────────
local left=body:tag('div'):addClass('td-left')
left:tag('div'):addClass('pd-section-title'):wikitext('Team Info')
local function infoRow(label,val)
if not val or val=="" then return end
left:tag('div'):addClass('pd-info-row')
:tag('span'):addClass('pd-info-label'):wikitext(label):done()
:tag('span'):addClass('pd-info-val'):wikitext(val)
end
if tm.country and tm.country~="" then infoRow('Country',getFlag(tm.country)..tm.country) end
infoRow('Game',teamGame)
if tm.short_code and tm.short_code~="" then infoRow('Tag',tm.short_code) end
if rankVal then infoRow('Krafton Rank',rankVal) end
if totalEarnings>0 then infoRow('Total Earnings',fmtCurrency(totalEarnings)) end
if tm.sponsors and tm.sponsors~="" then infoRow('Sponsors',tm.sponsors:gsub(",",", ")) end
local playerCount=0
for _,pl in ipairs(roster) do
local r=(pl.role or ""):lower()
if not r:find("coach") and not r:find("analyst") and not r:find("manager") then
playerCount=playerCount+1
end
end
if playerCount>0 then infoRow('Active Players',tostring(playerCount)) end
if #formerRows>0 then infoRow('Alumni',tostring(#formerRows)) end
local socials={
{k='instagram',file='Icon_instagram.png',base='https://instagram.com/'},
{k='twitter', file='Icon_twitter.png', base='https://twitter.com/' },
{k='youtube', file='Icon_youtube.png', base='https://youtube.com/' },
{k='discord', file='Icon_discord.png', base='https://discord.gg/' },
{k='facebook', file='Icon_facebook.png', base='https://facebook.com/' },
{k='website', file='Icon_website.png', base='https://' },
}
local hasSocial=false
for _,s in ipairs(socials) do if tm[s.k] and tm[s.k]~="" then hasSocial=true; break end end
if hasSocial then
local socDiv=left:tag('div'):addClass('pd-socials')
for _,s in ipairs(socials) do
local v=tm[s.k]
if v and v~="" then
local url=v:match("^https?://") and v or (s.base..v)
socDiv:wikitext("[[File:"..s.file.."|32px|link="..url.."|class=social-img]]")
end
end
end
-- ── RIGHT ─────────────────────────────────────────────────────
local right=body:tag('div'):addClass('td-right')
-- ── Active Roster — reuses hero-player-card CSS ───────────────
right:tag('div'):addClass('pd-table-title'):wikitext('👥 Active Roster')
if #roster>0 then
local grid=right:tag('div'):addClass('hero-roster-grid')
for _,pl in ipairs(roster) do
local card=grid:tag('div'):addClass('hero-player-card')
local color=getRoleColor(pl.role)
local imgDiv=card:tag('div'):addClass('hero-card-image')
local imgFile=(pl.image~="") and pl.image or "Player_Placeholder.png"
imgDiv:wikitext("[[File:"..imgFile.."|link="..(pl.link or pl.id).."]]")
local cb=card:tag('div'):addClass('hero-card-body')
cb:tag('div'):addClass('hero-role-pill'):css('background-color',color)
:wikitext(pl.role or "Player")
cb:tag('div'):addClass('hero-player-id')
:wikitext("[["..( pl.link or pl.id).."|"..(pl.id or "").."]]")
if pl.name~="" then cb:tag('div'):addClass('hero-player-name'):wikitext(pl.name) end
if pl.nat~="" then
cb:tag('div'):addClass('hero-player-flag'):wikitext(getFlag(pl.nat)..pl.nat)
end
end
else
right:tag('div'):addClass('pd-empty'):wikitext('No active roster data found.')
end
-- ── Former Players — year-headed sections, no JS/tabs ─────────
-- Tabs are impossible in MediaWiki Lua (HTML gets sanitised).
-- Instead: each year is a collapsible header + table. Clean and reliable.
if #formerRows>0 then
right:tag('div'):addClass('pd-table-title td-section-gap')
:wikitext('📋 Former Players')
for _,year in ipairs(yearOrder) do
-- Year sub-header
right:tag('div'):addClass('td-year-header'):wikitext(year)
local tbl=right:tag('table'):addClass('pd-tourn-table td-former-table')
local hdr=tbl:tag('tr')
hdr:tag('th'):wikitext('Player')
hdr:tag('th'):wikitext('Name')
hdr:tag('th'):wikitext('Role')
hdr:tag('th'):wikitext('Joined')
hdr:tag('th'):wikitext('Left')
for _,r in ipairs(formerByYear[year]) do
local pid=r.pid or ""
local dispId=(r.short_id and r.short_id~="")
and r.short_id or pid:gsub("^.*/","")
local tr=tbl:tag('tr')
tr:tag('td'):css('font-weight','700')
:wikitext("[["..pid.."|"..dispId.."]]")
tr:tag('td'):wikitext(r.real_name~="" and r.real_name or "—")
local rCell=tr:tag('td')
if r.role and r.role~="" then
rCell:tag('span'):addClass('td-role-badge')
:css('background',getRoleColor(r.role)):wikitext(r.role)
else rCell:wikitext("—") end
tr:tag('td'):addClass('ac-date'):wikitext(r.join_date or "?")
tr:tag('td'):addClass('ac-date'):wikitext(r.leave_date or "?")
end
end
end
-- ── Achievements — matches player dashboard style ──────────────
right:tag('div'):addClass('pd-table-title td-section-gap'):wikitext('🏆 Achievements')
if #achRows>0 then
local tbl2=right:tag('table'):addClass('pd-tourn-table')
local hdr2=tbl2:tag('tr')
hdr2:tag('th'):wikitext('Date')
hdr2:tag('th'):wikitext('Tier')
hdr2:tag('th'):wikitext('Tournament')
hdr2:tag('th'):addClass('td-place-col'):wikitext('Place/Award')
hdr2:tag('th'):css('text-align','right'):wikitext('Prize')
for _,r in ipairs(achRows) do
local meta=tournMeta[r.tournament or ""] or {}
local tr2=tbl2:tag('tr')
tr2:tag('td'):addClass('ac-date')
:wikitext(meta.date and meta.date~="" and meta.date or "—")
local tierTd=tr2:tag('td'):addClass('ac-tier')
if meta.tier and meta.tier~="" then
tierTd:tag('span'):addClass('tier-badge '..getTierClass(meta.tier))
:wikitext(meta.tier)
else tierTd:wikitext("—") end
tr2:tag('td'):css('font-weight','600')
:wikitext("[["..(r.tournament or "").."]]")
local pTd=tr2:tag('td'):addClass('ac-place td-place-col')
if r.award and r.award~="" then
pTd:tag('span'):addClass('pd-award-badge'):wikitext(r.award)
else
renderPlaceBadge(pTd,r.placement)
end
tr2:tag('td'):addClass('ac-prize')
:wikitext(tonumber(r.prize) and tonumber(r.prize)>0
and fmtCurrency(tonumber(r.prize)) or "—")
end
else
right:tag('div'):addClass('pd-empty'):wikitext('No achievements recorded.')
end
return tostring(root)
end
return p