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

Module:TeamDashboard: Difference between revisions

From eSportsAmaze
Created page with "-- ================================================================ -- Module:TeamDashboard v1 -- Usage on team page: -- {{#invoke:TeamDashboard|main -- |player1=Aurum |name1=Pratik Mehra |role1=Coach |image1=Defaultplayer.png -- |player2=Nub |name2=- |role2=Analyst -- }} -- -- Sections: -- HERO — logo, team name, tags, total earnings -- LEFT panel — team info rows + socials -- RIGHT panel — Active Roster cards (reuses hero-pl..."
 
No edit summary
Line 1: Line 1:
-- ================================================================
-- Module:TeamDashboard v1
-- Usage on team page:
--  {{#invoke:TeamDashboard|main
--    |player1=Aurum |name1=Pratik Mehra |role1=Coach |image1=Defaultplayer.png
--    |player2=Nub  |name2=-            |role2=Analyst
--  }}
--
-- Sections:
--  HERO        — logo, team name, tags, total earnings
--  LEFT panel  — team info rows + socials
--  RIGHT panel — Active Roster cards (reuses hero-player-card CSS)
--                Former Players with year tabs
--                Achievements table (same style as player page)
-- ================================================================
local p    = {}
local p    = {}
local cargo = mw.ext.cargo
local cargo = mw.ext.cargo
Line 172: Line 156:


     -- ── 5. Former players (join Players for display info) ─────────
     -- ── 5. Former players (join Players for display info) ─────────
     local formerRows = cargo.query(
    -- Step 1: get former player IDs for this team
        "Player_Former_Teams,Players",
     local fRows = cargo.query("Player_Former_Teams",
         "Player_Former_Teams.player_id=pid,"
         "player_id,role,join_date,leave_date",
        .."Player_Former_Teams.role=role,"
         { where  = "team='"..sqlTeam.."' AND leave_date!=''",
        .."Player_Former_Teams.join_date=join_date,"
           orderBy = "leave_date DESC",
        .."Player_Former_Teams.leave_date=leave_date,"
        .."Players.real_name=real_name,"
        .."Players.id=short_id,"
        .."Players.image=image",
         { joinOn  = "Player_Former_Teams.player_id=Players._pageName",
          where  = "Player_Former_Teams.team='"..sqlTeam
                    .."' AND Player_Former_Teams.leave_date!=''",
           orderBy = "Player_Former_Teams.leave_date DESC",
           limit  = 200 })
           limit  = 200 })
     formerRows = formerRows or {}
     fRows = fRows or {}


    -- Step 2: batch fetch player details
    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
    -- Step 3: merge into formerRows
    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 of leave_date
     -- Group by year of leave_date
     local formerByYear,yearOrder,yearSeen = {},{},{}
     local formerByYear,yearOrder,yearSeen = {},{},{}

Revision as of 03:18, 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()

-- ── Helpers ──────────────────────────────────────────────────────

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

-- Role color config — identical to Module:Team
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 from Cargo ───────────────────────────────────
    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

    -- ── 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 ─────────────────────────────────────────
    -- Merge DB players with manual args (same logic as Module:Team|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 (join Players for display info) ─────────
    -- Step 1: get former player IDs for this team
    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 {}

    -- Step 2: batch fetch player details
    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

    -- Step 3: merge into formerRows
    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 of leave_date
    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 ───────────────────────────────────────────
    local achRows = cargo.query("PrizeMoney","tournament,placement,prize,award",
        {where="team='"..sqlTeam.."' AND (player='' OR player IS NULL)",
         orderBy="prize DESC", limit=50})
    achRows = achRows or {}

    -- Batch fetch tournament meta (tier + date)
    local tournNames,tournSeen2 = {},{}
    for _,r in ipairs(achRows) 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",
            {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}
            end
        end
    end

    -- ── BUILD HTML ────────────────────────────────────────────────

    local root = html.create('div'):addClass('td-dashboard')

    -- ════ HERO ════
    -- [Logo] [Display name + short_code tag + status/game/rank tags] [Earnings]

    local hero = root:tag('div'):addClass('td-hero')

    -- Logo (light/dark)
    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]]")

    -- Info: name + tags
    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 tm.game and tm.game~="" then
        tags:tag('span'):addClass('pd-tag pd-tag-game'):wikitext(tm.game)
    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

    -- Earnings (right side, same structure as player dashboard)
    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 panel ───────────────────────────────────────────────
    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',        tm.game)
    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
    -- Roster count (players only, exclude coach/analyst)
    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 panel ──────────────────────────────────────────────
    local right = body:tag('div'):addClass('td-right')

    -- ── Active Roster ─────────────────────────────────────────────
    -- Reuses existing .hero-player-card CSS exactly as Module:Team|roster does
    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)

            -- Image area (same as Module:Team)
            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).."]]")

            -- Body
            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 ────────────────────────────────────────────
    if #formerRows>0 then
        right:tag('div'):addClass('pd-table-title td-section-gap')
            :wikitext('📋 Former Players')

        -- Unique tab group ID (safe for JS)
        local tabId = 'tdfp-'..teamName:gsub("[^%w]","_")

        -- Tab buttons (one per year)
        local tabBar = right:tag('div'):addClass('td-tab-bar')
            :attr('id',tabId..'-tabs')
        for i,year in ipairs(yearOrder) do
            tabBar:tag('button')
                :addClass('td-tab-btn'..(i==1 and ' td-tab-active' or ''))
                :attr('onclick',string.format(
                    "tdSwitchTab('%s','%s',this)",tabId,year))
                :wikitext(year)
        end

        -- Year panels
        local panels = right:tag('div'):attr('id',tabId..'-panels')
        for i,year in ipairs(yearOrder) do
            local panel = panels:tag('div')
                :addClass('td-year-panel'..(i~=1 and ' td-hidden' or ''))
                :attr('id',tabId..'-'..year)

            local tbl = panel: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 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 ─────────────────────────────────────────────
    -- Same style as player dashboard and existing achievements table
    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

    -- ── Tab switching JS ─────────────────────────────────────────
    root:wikitext([[<script>
function tdSwitchTab(tabId,year,btn){
    var panels=document.getElementById(tabId+'-panels');
    if(!panels)return;
    panels.querySelectorAll('.td-year-panel').forEach(function(p){p.classList.add('td-hidden');});
    var t=document.getElementById(tabId+'-'+year);
    if(t)t.classList.remove('td-hidden');
    var tabs=document.getElementById(tabId+'-tabs');
    if(tabs)tabs.querySelectorAll('.td-tab-btn').forEach(function(b){b.classList.remove('td-tab-active');});
    btn.classList.add('td-tab-active');
}
</script>]])

    return tostring(root)
end

return p