Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.
Revision as of 03:30, 9 March 2026 by Esportsamaze (talk | contribs)

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 placeText  = tostring(placement or "—")
    local badgeClass = "place-def"
    if rank==1 then badgeClass="place-1"; placeText="1st"
    elseif rank==2 then badgeClass="place-2"; placeText="2nd"
    elseif rank==3 then badgeClass="place-3"; placeText="3rd"
    elseif placeText:match("^%d+$") then placeText=placeText.."th"
    end
    td:tag('span'):addClass('place-badge '..badgeClass):wikitext(placeText)
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 team's game ─────────────────
    -- PrizeMoney has no game field, so we get tournament names first
    -- then cross-check with Tournaments.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 {}

    -- Collect tournament names to fetch metadata
    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

    -- Fetch tier + date + game for all tournaments
    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

    -- Filter achievements: only keep rows where tournament game matches team game
    -- If team game is blank, show all (safe fallback)
    local achRows = {}
    for _,r in ipairs(achRaw) do
        if teamGame=="" then
            table.insert(achRows,r)
        else
            local meta = tournMeta[r.tournament or ""] or {}
            local tGame = (meta.game or ""):lower()
            local tTeam = teamGame:lower()
            -- match if game contains the team's game string (e.g. "bgmi" in "Battlegrounds Mobile India")
            if tGame=="" or tGame:find(tTeam,1,true) or tTeam:find(tGame,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
    local logoDiv  = hero:tag('div'):addClass('td-hero-logo')
    logoDiv:wikitext("[[File:"..lightLogo.."|72px|link=|class=logo-lightmode]]")
    logoDiv:wikitext("[[File:"..darkLogo .."|72px|link=|class=logo-darkmode]]")

    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 stColor,stBg,stBorder
    if sl=="active" then
        stColor="#4ade80";stBg="rgba(34,197,94,.2)";stBorder="rgba(34,197,94,.35)"
    elseif sl=="inactive" or sl=="disbanded" then
        stColor="#f87171";stBg="rgba(239,68,68,.2)";stBorder="rgba(239,68,68,.35)"
    else
        stColor="#94a3b8";stBg="rgba(148,163,184,.2)";stBorder="rgba(148,163,184,.35)"
    end
    tags:tag('span'):addClass('pd-tag')
        :css('color',stColor):css('background',stBg)
        :css('border','1px solid '..stBorder)
        :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

    -- Socials
    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 ─────────────────────────────────────────────
    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 ────────────────────────────────────────────
    -- Pure CSS tabs: hidden radio inputs + label bar + panels
    -- All inputs, labels, and panels are direct siblings of .td-tabs-wrap children
    -- so the :checked ~ sibling selector works correctly
    if #formerRows>0 then
        right:tag('div'):addClass('pd-table-title td-section-gap')
            :wikitext('📋 Former Players')

        -- Safe ID: only alphanumeric
        local tabId='tdfp'..teamName:gsub("[^%w]","")

        -- Build raw HTML string (mw.html escapes inputs/labels incorrectly)
        local tabHtml = '<div class="td-tabs-wrap">'

        -- Radio inputs first (must be siblings of label bar and panels)
        for i,year in ipairs(yearOrder) do
            local checked = (i==1) and ' checked="checked"' or ''
            tabHtml = tabHtml..'<input type="radio" class="td-radio" '
                ..'name="'..tabId..'" id="'..tabId..year..'"'..checked..'/>'
        end

        -- Label bar
        tabHtml = tabHtml..'<div class="td-tab-bar">'
        for _,year in ipairs(yearOrder) do
            tabHtml = tabHtml..'<label class="td-tab-btn" for="'
                ..tabId..year..'">'..year..'</label>'
        end
        tabHtml = tabHtml..'</div>'

        -- Panels
        tabHtml = tabHtml..'<div class="td-panels">'
        for _,year in ipairs(yearOrder) do
            tabHtml = tabHtml..'<div class="td-year-panel" data-year="'..year..'">'

            -- Table header
            tabHtml = tabHtml..'<table class="pd-tourn-table td-former-table">'
                ..'<tr><th>Player</th><th>Name</th><th>Role</th>'
                ..'<th>Joined</th><th>Left</th></tr>'

            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 roleColor=getRoleColor(r.role)
                local roleBadge=""
                if r.role and r.role~="" then
                    roleBadge='<span class="td-role-badge" style="background:'
                        ..roleColor..'">'..r.role..'</span>'
                else roleBadge="—" end
                tabHtml = tabHtml..'<tr>'
                    ..'<td style="font-weight:700">[['..'<nowiki/>'..pid..'|'..dispId..']]</td>'
                    ..'<td>'..(r.real_name~="" and r.real_name or "—")..'</td>'
                    ..'<td>'..roleBadge..'</td>'
                    ..'<td class="ac-date">'..(r.join_date  or "?")..'</td>'
                    ..'<td class="ac-date">'..(r.leave_date or "?")..'</td>'
                    ..'</tr>'
            end
            tabHtml = tabHtml..'</table></div>'
        end
        tabHtml = tabHtml..'</div>'  -- td-panels

        -- Inline CSS: one rule per year for :checked → show panel + highlight label
        tabHtml = tabHtml..'<style>'
        tabHtml = tabHtml..'.td-panels .td-year-panel{display:none}'
        for _,year in ipairs(yearOrder) do
            local id=tabId..year
            -- Show panel
            tabHtml = tabHtml..'#'..id..':checked~.td-tab-bar~.td-panels'
                ..' .td-year-panel[data-year="'..year.."\"]{display:block}"
            -- Highlight label
            tabHtml = tabHtml..'#'..id..':checked~.td-tab-bar'
                ..' label[for="'..id..'"]'
                ..'{background:rgba(37,99,235,.25);color:#93c5fd;'
                ..'border-color:rgba(37,99,235,.5)}'
        end
        tabHtml = tabHtml..'</style></div>'  -- close td-tabs-wrap

        right:wikitext(tabHtml)
    end

    -- ── Achievements ──────────────────────────────────────────────
    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('ac-place'):wikitext('Place')
        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')
            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