Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

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