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

Module:PlayerDashboard: Difference between revisions

From eSportsAmaze
No edit summary
No edit summary
Line 1: Line 1:
-- Module:PlayerDashboard
-- Module:PlayerDashboard v2
-- Full-width player dashboard. Reads all data from existing Cargo tables.
-- All 8 fixes applied.
-- No changes to any existing module or template needed.


local p    = {}
local p    = {}
Line 17: Line 16:
     local s = tostring(math.floor(n))
     local s = tostring(math.floor(n))
     local l3 = s:sub(-3)
     local l3 = s:sub(-3)
     local rest = s:sub(1,-4)
     local rest = s:sub(1, -4)
     local fmt = rest:reverse():gsub("(%d%d)","%1,"):reverse()
     local fmt = rest:reverse():gsub("(%d%d)", "%1,"):reverse()
     if fmt ~= "" then l3 = "," .. l3 end
     if fmt ~= "" then l3 = "," .. l3 end
     return "₹ " .. fmt .. l3
     return "₹ " .. fmt .. l3
Line 28: Line 27:
end
end


local function statusColor(s)
-- Flag emoji map
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(country)
    if not country or country == "" then return "" end
    local key = country:lower():gsub("%s+", "")
    return (FLAGS[key] or "") .. " "
end
 
local function statusStyle(s)
     s = (s or ""):lower()
     s = (s or ""):lower()
     if s == "active"  then return "#22c55e", "rgba(34,197,94,.15)", "rgba(34,197,94,.3)"  end
     if s == "active"  then return "#4ade80", "rgba(34,197,94,.2)",   "rgba(34,197,94,.35)"  end
     if s == "inactive" then return "#f59e0b", "rgba(245,158,11,.15)", "rgba(245,158,11,.3)" end
     if s == "inactive" then return "#fbbf24", "rgba(251,191,36,.2)", "rgba(251,191,36,.35)" end
     if s == "retired"  then return "#94a3b8", "rgba(148,163,184,.15)","rgba(148,163,184,.3)"end
     if s == "retired"  then return "#94a3b8", "rgba(148,163,184,.2)", "rgba(148,163,184,.35)"end
     if s == "banned"  then return "#ef4444", "rgba(239,68,68,.15)",  "rgba(239,68,68,.3)"  end
     if s == "banned"  then return "#f87171", "rgba(239,68,68,.2)",  "rgba(239,68,68,.35)"  end
     return "#94a3b8", "rgba(148,163,184,.15)", "rgba(148,163,184,.3)"
     return "#94a3b8", "rgba(148,163,184,.2)", "rgba(148,163,184,.35)"
end
 
-- Fetch team logo URL from Teams table
local function getTeamInfo(teamName)
    if not teamName or teamName == "" then return nil end
    local rows = cargo.query("Teams",
        "display_name,image,image_dark",
        { where = "name='" .. esc(teamName) .. "'", limit = 1 })
    if rows and #rows > 0 then return rows[1] end
    return nil
end
 
local function logoWikitext(imageFile, size, classes)
    if not imageFile or imageFile == "" then return nil end
    size = size or "26px"
    classes = classes or "pd-team-logo-img"
    return "[[File:" .. imageFile .. "|" .. size .. "|link=|class=" .. classes .. "]]"
end
end


Line 41: Line 73:
     local sqlPage    = esc(pageName)
     local sqlPage    = esc(pageName)


     -- ── 1. Fetch player row from Players table ──────────────
     -- 1. Player row
     local pRows = cargo.query("Players",
     local pRows = cargo.query("Players",
         "id,real_name,image,current_team,nationality,status,game,birth_date,role," ..
         "id,real_name,image,current_team,nationality,status,game," ..
         "instagram,youtube,twitter,discord,facebook",
         "birth_date,role,instagram,youtube,twitter,discord,facebook",
         { where = "_pageName='" .. sqlPage .. "'", limit = 1 })
         { where = "_pageName='" .. sqlPage .. "'", limit = 1 })
     local pl = (pRows and #pRows > 0) and pRows[1] or {}
     local pl = (pRows and #pRows > 0) and pRows[1] or {}
     local displayName = pl.id or mw.title.getCurrentTitle().subpageText
     local displayName = pl.id or mw.title.getCurrentTitle().subpageText
    local sqlName = esc(displayName)


     -- ── 2. Krafton rank ─────────────────────────────────────
     -- 2. Krafton rank
     local rankVal = "Unranked"
     local rankVal = nil
     local rRows = cargo.query("Krafton_Rankings", "rank", {
     local rRows = cargo.query("Krafton_Rankings", "rank", {
         where = string.format("(name='%s' OR name='%s') AND type='Player'",
         where = string.format("(name='%s' OR name='%s') AND type='Player'",
             sqlPage, esc(displayName)), limit = 1 })
             sqlPage, sqlName), limit = 1 })
     if rRows and #rRows > 0 then rankVal = "#" .. rRows[1].rank end
     if rRows and #rRows > 0 then rankVal = "#" .. rRows[1].rank end


     -- ── 3. Earnings ──────────────────────────────────────────
     -- 3. Earnings
     local indEarnings, teamEarnings = 0, 0
     local indEarnings, teamEarnings = 0, 0
     local pWhere = string.format("(player='%s' OR player='%s')", sqlPage, esc(displayName))
     local eRows = cargo.query("PrizeMoney", "SUM(prize)=total", {
    local eRows = cargo.query("PrizeMoney", "SUM(prize)=total", { where = pWhere })
        where = string.format("(player='%s' OR player='%s')", sqlPage, sqlName) })
     if eRows and #eRows > 0 then indEarnings = tonumber(eRows[1].total) or 0 end
     if eRows and #eRows > 0 then indEarnings = tonumber(eRows[1].total) or 0 end


    -- All tournaments player appeared in
     local participation = cargo.query("Tournament_Teams", "tournament,team", {
     local participation = cargo.query("Tournament_Teams", "tournament,team", {
         where = string.format(
         where = string.format(
Line 67: Line 101:
             "p3='%s' OR p3='%s' OR p4='%s' OR p4='%s' OR " ..
             "p3='%s' OR p3='%s' OR p4='%s' OR p4='%s' OR " ..
             "p5='%s' OR p5='%s' OR p6='%s' OR p6='%s'",
             "p5='%s' OR p5='%s' OR p6='%s' OR p6='%s'",
             sqlPage,esc(displayName), sqlPage,esc(displayName),
             sqlPage,sqlName, sqlPage,sqlName, sqlPage,sqlName,
            sqlPage,esc(displayName), sqlPage,esc(displayName),
            sqlPage,sqlName, sqlPage,sqlName, sqlPage,sqlName),
            sqlPage,esc(displayName), sqlPage,esc(displayName)),
         limit = 500 })
         limit = 500 })


Line 82: Line 115:
         if #conds > 0 then
         if #conds > 0 then
             local tPrizes = cargo.query("PrizeMoney", "SUM(prize)=team_total", {
             local tPrizes = cargo.query("PrizeMoney", "SUM(prize)=team_total", {
                 where = "("..table.concat(conds," OR ")..")" ..
                 where = "(" .. table.concat(conds, " OR ") .. ")" ..
                         " AND (player='' OR player IS NULL)" })
                         " AND (player='' OR player IS NULL)" })
             if tPrizes and #tPrizes > 0 then
             if tPrizes and #tPrizes > 0 then
Line 91: Line 124:
     local totalEarnings = indEarnings + teamEarnings
     local totalEarnings = indEarnings + teamEarnings


     -- ── 4. Team history ──────────────────────────────────────
     -- 4. Team history with logos
     local teamHist = cargo.query("Player_Former_Teams",
     local teamHist = cargo.query("Player_Former_Teams",
         "team,join_date,leave_date",
         "team,join_date,leave_date",
         { where = "player_id='"..sqlPage.."'", orderBy="join_date DESC", limit=15 })
         { where = "player_id='" .. sqlPage .. "'",
          orderBy = "join_date DESC", limit = 20 })
     if #teamHist == 0 then
     if #teamHist == 0 then
         teamHist = cargo.query("Player_Former_Teams",
         teamHist = cargo.query("Player_Former_Teams",
             "team,join_date,leave_date",
             "team,join_date,leave_date",
             { where="player_id='"..esc(displayName).."'", orderBy="join_date DESC", limit=15 })
             { where = "player_id='" .. sqlName .. "'",
              orderBy = "join_date DESC", limit = 20 })
     end
     end


     -- ── 5. Tournament history ─────────────────────────────────
     -- 5a. Individual awards (player= set in PrizeMoney)
    -- For each tournament the player appeared in, find their team's
     local indAwards = cargo.query("PrizeMoney",
    -- final placement from StageStandings and prize from PrizeMoney
        "tournament,award,prize",
     local tournHistory = {}
        { where = string.format("player='%s' OR player='%s'", sqlPage, sqlName),
    local seenTournStage = {}
          orderBy = "tournament DESC", limit = 100 })


    -- 5b. Team results — all tournaments from participation, with standings + prize
    local teamResults = {}
    local seen = {}
     if participation and #participation > 0 then
     if participation and #participation > 0 then
         local tournTeamConds = {}
         local ttConds = {}
         for _, r in ipairs(participation) do
         for _, r in ipairs(participation) do
             if r.tournament and r.team then
             if r.tournament and r.team then
                 table.insert(tournTeamConds, string.format(
                 local key = r.tournament .. "|" .. r.team
                    "(tournament='%s' AND team='%s')", esc(r.tournament), esc(r.team)))
                if not seen[key] then
                    seen[key] = true
                    table.insert(ttConds, string.format(
                        "(tournament='%s' AND team='%s')", esc(r.tournament), esc(r.team)))
                end
             end
             end
         end
         end
        if #tournTeamConds > 0 then
            local standings = cargo.query("StageStandings",
                "tournament,stage,team,totalpts,wwcd,elimpts,result",
                { where = table.concat(tournTeamConds," OR "),
                  orderBy = "tournament DESC", limit = 200 })


             for _, row in ipairs(standings) do
        if #ttConds > 0 then
                local key = row.tournament .. "|" .. row.team
             -- Get standings (best stage = lowest rank = Finals > Semis etc.)
                -- Keep only the last stage per tournament per team (Finals > Semis > etc.)
            local stRows = cargo.query("StageStandings",
                if not seenTournStage[key] then
                "tournament,stage,team,totalpts,result",
                    seenTournStage[key] = true
                { where = table.concat(ttConds, " OR "),
                    -- Get rank within that stage
                  orderBy = "tournament DESC", limit = 500 })
                    local rankRows = cargo.query("StageStandings",
 
                        "COUNT(team)=cnt",
            -- index standings by tournament|team, keep last stage entry
                        { where = string.format(
            local stIndex = {}
                            "tournament='%s' AND stage='%s' AND totalpts >= %s",
            for _, row in ipairs(stRows) do
                            esc(row.tournament), esc(row.stage),
                local k = row.tournament .. "|" .. row.team
                            tonumber(row.totalpts) or 0) })
                stIndex[k] = row -- overwrites so last stage wins
                    local place = (rankRows and #rankRows > 0)
            end
                        and (tonumber(rankRows[1].cnt) or 99) or 99


                    -- Prize for this tournament + team
            -- Get prize per tournament+team
                    local prizeRows = cargo.query("PrizeMoney", "SUM(prize)=total", {
            local prizeIndex = {}
                        where = string.format(
            local pRows2 = cargo.query("PrizeMoney",
                            "tournament='%s' AND team='%s' AND (player='' OR player IS NULL)",
                "tournament,team,SUM(prize)=total,placement,award",
                            esc(row.tournament), esc(row.team)) })
                { where = "(" .. table.concat(ttConds, " OR ") .. ")" ..
                    local prize = (prizeRows and #prizeRows > 0)
                          " AND (player='' OR player IS NULL)",
                        and (tonumber(prizeRows[1].total) or 0) or 0
                  groupBy = "tournament,team", limit = 500 })
            for _, row in ipairs(pRows2) do
                prizeIndex[row.tournament .. "|" .. row.team] = row
            end


                     table.insert(tournHistory, {
            -- Build final list
                        tournament = row.tournament,
            for _, r in ipairs(participation) do
                         stage      = row.stage,
                if r.tournament and r.team then
                         team      = row.team,
                    local k  = r.tournament .. "|" .. r.team
                    local st  = stIndex[k]
                    local pr  = prizeIndex[k]
 
                     -- get rank within stage if standings exist
                    local place = nil
                    if st then
                        local rankQ = cargo.query("StageStandings", "COUNT(team)=cnt", {
                            where = string.format(
                                "tournament='%s' AND stage='%s' AND totalpts>=%s",
                                esc(st.tournament), esc(st.stage),
                                tonumber(st.totalpts) or 0) })
                        if rankQ and #rankQ > 0 then
                            place = tonumber(rankQ[1].cnt) or nil
                        end
                    end
                    if pr and pr.placement and pr.placement ~= "" then
                        local n = pr.placement:match("(%d+)")
                        if n then place = place or tonumber(n) end
                    end
 
                    table.insert(teamResults, {
                         tournament = r.tournament,
                         team      = r.team,
                         place      = place,
                         place      = place,
                         result    = row.result or "",
                        award      = pr and pr.award or "",
                         prize      = prize
                         result    = st and (st.result or "") or "",
                         prize      = pr and (tonumber(pr.total) or 0) or 0,
                     })
                     })
                 end
                 end
Line 156: Line 219:
         end
         end
     end
     end
    -- Remove duplicates (participation may repeat tournament+team)
    local dedupedResults, seenR = {}, {}
    for _, r in ipairs(teamResults) do
        local k = r.tournament .. "|" .. r.team
        if not seenR[k] then
            seenR[k] = true
            table.insert(dedupedResults, r)
        end
    end
    teamResults = dedupedResults


     -- ── Build HTML ────────────────────────────────────────────
     -- ── Build HTML ────────────────────────────────────────────
     local sc, sbg, sborder = statusColor(pl.status)
     local sc, sbg, sborder = statusStyle(pl.status)
     local initials = displayName:sub(1,2):upper()
     local initials = displayName:sub(1, 2):upper()
 
     local root = html.create('div'):addClass('pd-dashboard')
     local root = html.create('div'):addClass('pd-dashboard')


     -- ── HERO ─────────────────────────────────────────────────
     -- HERO
     local hero = root:tag('div'):addClass('pd-hero')
     local hero = root:tag('div'):addClass('pd-hero')


     local photoDiv = hero:tag('div'):addClass('pd-hero-photo')
    -- Photo (circular, image or initials)
     local photo = hero:tag('div'):addClass('pd-hero-photo')
     if pl.image and pl.image ~= "" then
     if pl.image and pl.image ~= "" then
         photoDiv:wikitext('[[File:'..pl.image..'|120px|link=]]')
         photo:wikitext("[[File:" .. pl.image .. "|100px|link=|class=pd-hero-img]]")
     else
     else
         photoDiv:wikitext(initials)
         photo:wikitext(initials)
     end
     end


Line 182: Line 256:
     if pl.status and pl.status ~= "" then
     if pl.status and pl.status ~= "" then
         tags:tag('span'):addClass('pd-tag')
         tags:tag('span'):addClass('pd-tag')
             :css('color', sc):css('background', sbg):css('border', '1px solid '..sborder)
             :css('color', sc):css('background', sbg):css('border', '1px solid ' .. sborder)
             :wikitext('● ' .. pl.status)
             :wikitext('● ' .. pl.status)
     end
     end
     if pl.role and pl.role ~= "" then
     if pl.role     and pl.role ~= ""     then tags:tag('span'):addClass('pd-tag pd-tag-role'):wikitext(pl.role) end
        tags:tag('span'):addClass('pd-tag pd-tag-role'):wikitext(pl.role)
     if pl.game     and pl.game ~= ""     then tags:tag('span'):addClass('pd-tag pd-tag-game'):wikitext(pl.game) end
    end
     if pl.game and pl.game ~= "" then
        tags:tag('span'):addClass('pd-tag pd-tag-game'):wikitext(pl.game)
    end
    if pl.nationality and pl.nationality ~= "" then
        tags:tag('span'):addClass('pd-tag pd-tag-nat'):wikitext(pl.nationality)
    end


     if rankVal ~= "Unranked" then
     if rankVal then
         heroInfo:tag('div'):addClass('pd-rank-chip')
         heroInfo:tag('div'):addClass('pd-rank-chip')
             :tag('span'):wikitext('Krafton Rank'):done()
             :tag('span'):wikitext('Krafton Rank'):done()
Line 202: Line 269:


     if totalEarnings > 0 then
     if totalEarnings > 0 then
         local heroRight = hero:tag('div'):addClass('pd-hero-right')
         local er = hero:tag('div'):addClass('pd-hero-right')
         heroRight:tag('div'):addClass('pd-earnings-label'):wikitext('Approx. Earnings')
         er:tag('div'):addClass('pd-earnings-label'):wikitext('Approx. Earnings')
         heroRight:tag('div'):addClass('pd-earnings-val'):wikitext(fmtCurrency(totalEarnings))
         er:tag('div'):addClass('pd-earnings-val'):wikitext(fmtCurrency(totalEarnings))
         local bd = heroRight:tag('div'):addClass('pd-earnings-breakdown')
         local bd = er:tag('div'):addClass('pd-earnings-breakdown')
         bd:tag('div'):addClass('pd-earn-sub')
         bd:tag('div')
             :tag('div'):addClass('pd-earn-sub-label'):wikitext('Team'):done()
             :tag('div'):addClass('pd-earn-sub-label'):wikitext('Team'):done()
             :tag('div'):addClass('pd-earn-sub-val'):wikitext(fmtCurrency(teamEarnings))
             :tag('div'):addClass('pd-earn-sub-val'):wikitext(fmtCurrency(teamEarnings))
         bd:tag('div'):addClass('pd-earn-sub')
         bd:tag('div')
             :tag('div'):addClass('pd-earn-sub-label'):wikitext('Individual'):done()
             :tag('div'):addClass('pd-earn-sub-label'):wikitext('Individual'):done()
             :tag('div'):addClass('pd-earn-sub-val'):wikitext(fmtCurrency(indEarnings))
             :tag('div'):addClass('pd-earn-sub-val'):wikitext(fmtCurrency(indEarnings))
     end
     end


     -- ── BODY ─────────────────────────────────────────────────
     -- BODY
     local body = root:tag('div'):addClass('pd-body')
     local body = root:tag('div'):addClass('pd-body')
    -- Left panel
     local left = body:tag('div'):addClass('pd-left')
     local left = body:tag('div'):addClass('pd-left')
     left:tag('div'):addClass('pd-section-title'):wikitext('Player Info')
     left:tag('div'):addClass('pd-section-title'):wikitext('Player Info')


     local function infoRow(label, val, link)
     local function infoRow(label, val)
         if not val or val == "" then return end
         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
    infoRow('Real Name', pl.real_name)
    if pl.birth_date and pl.birth_date ~= "" then
        infoRow('Born', lang:formatDate("d F Y", pl.birth_date))
    end
    if pl.nationality and pl.nationality ~= "" then
        infoRow('Nationality', getFlag(pl.nationality) .. pl.nationality)
    end
    infoRow('Role', pl.role)
    infoRow('Game', pl.game)
    -- Current team with logo
    if pl.current_team and pl.current_team ~= "" then
        local ti = getTeamInfo(pl.current_team)
        local teamDisplay = (ti and ti.display_name and ti.display_name ~= "")
            and ti.display_name or pl.current_team
        local teamLogoFile = ti and ti.image and ti.image ~= "" and ti.image or nil
         local row = left:tag('div'):addClass('pd-info-row')
         local row = left:tag('div'):addClass('pd-info-row')
         row:tag('span'):addClass('pd-info-label'):wikitext(label)
         row:tag('span'):addClass('pd-info-label'):wikitext('Current Team')
         local vspan = row:tag('span'):addClass('pd-info-val')
         local val = row:tag('span'):addClass('pd-info-val')
         if link then vspan:wikitext('[['..val..']]')
        local wrap = val:tag('span'):addClass('pd-current-team')
        else vspan:wikitext(val) end
         if teamLogoFile then
            wrap:wikitext("[[File:" .. teamLogoFile .. "|22px|link=|class=pd-current-team-logo]]")
        end
        wrap:tag('span'):addClass('pd-current-team-name')
            :wikitext("[[" .. pl.current_team .. "|" .. teamDisplay .. "]]")
     end
     end


     infoRow('Real Name',  pl.real_name)
     if rankVal then infoRow('Krafton Rank', rankVal) end
    infoRow('Born',        pl.birth_date and lang:formatDate("d F Y", pl.birth_date))
    infoRow('Nationality', pl.nationality)
    infoRow('Role',        pl.role)
    infoRow('Current Team',pl.current_team, true)
    infoRow('Game',        pl.game)
    infoRow('Krafton Rank',rankVal ~= "Unranked" and rankVal or nil)


     -- Socials
     -- Socials
Line 246: Line 331:
         {k='facebook',  file='Icon_facebook.png',  base='https://facebook.com/'},
         {k='facebook',  file='Icon_facebook.png',  base='https://facebook.com/'},
     }
     }
     local socialDiv = 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 = pl[s.k]
         local v = pl[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)
             socialDiv:wikitext('[[File:'..s.file..'|24px|link='..url..'|class=social-img]]')
             socDiv:wikitext("[[File:"..s.file.."|24px|link="..url.."|class=social-img]]")
         end
         end
     end
     end


     -- Team history
     -- Team history with logos
     if #teamHist > 0 then
     if #teamHist > 0 then
         left:tag('div'):addClass('pd-section-title pd-section-title-gap'):wikitext('Team History')
         left:tag('div'):addClass('pd-section-title pd-section-gap'):wikitext('Team History')
         for _, h in ipairs(teamHist) do
         for _, h in ipairs(teamHist) do
            local ti = getTeamInfo(h.team or "")
             local tr = left:tag('div'):addClass('pd-team-row')
             local tr = left:tag('div'):addClass('pd-team-row')
             tr:tag('div'):addClass('pd-team-logo')
             if ti and ti.image and ti.image ~= "" then
                :wikitext((h.team or "?"):sub(1,3):upper())
                tr:wikitext("[[File:"..ti.image.."|26px|link=|class=pd-team-logo-img]]")
             tr:tag('div'):addClass('pd-team-name'):wikitext('[['.. (h.team or "") ..']]')
            else
                tr:tag('div'):addClass('pd-team-logo-fb')
                    :wikitext((h.team or "?"):sub(1, 3):upper())
            end
             tr:tag('div'):addClass('pd-team-name')
                :wikitext("[[" .. (h.team or "") .. "]]")
             tr:tag('div'):addClass('pd-team-dates')
             tr:tag('div'):addClass('pd-team-dates')
                 :wikitext(fmtDate(h.join_date) .. ' ' ..
                 :wikitext(fmtDate(h.join_date) .. " " ..
                     (h.leave_date and h.leave_date ~= ""
                     (h.leave_date and h.leave_date ~= ""
                         and fmtDate(h.leave_date) or 'Now'))
                         and fmtDate(h.leave_date) or "Now"))
         end
         end
     end
     end


     -- Right panel — tournament history
     -- RIGHT PANEL
     local right = body:tag('div'):addClass('pd-right')
     local right = body:tag('div'):addClass('pd-right')
    right:tag('div'):addClass('pd-section-title'):wikitext('Tournament History')


     if #tournHistory > 0 then
    -- Individual awards table
         local tbl = right:tag('table'):addClass('pd-tourn-table')
     if indAwards and #indAwards > 0 then
        local sec = right:tag('div'):addClass('pd-prize-section')
        sec:tag('div'):addClass('pd-table-title'):wikitext('🏆 Individual Awards')
         local tbl = sec:tag('table'):addClass('pd-tourn-table')
         local hdr = tbl:tag('tr')
         local hdr = tbl:tag('tr')
         for _, h in ipairs({'Tournament','Team','Stage','Place','Result','Prize'}) do
        hdr:tag('th'):wikitext('Tournament')
             hdr:tag('th'):wikitext(h)
        hdr:tag('th'):wikitext('Award')
        hdr:tag('th'):wikitext('Prize')
         for _, a in ipairs(indAwards) do
            local tr = tbl:tag('tr')
            tr:tag('td'):wikitext("[[" .. (a.tournament or "") .. "]]")
            tr:tag('td'):tag('span'):addClass('pd-award-badge')
                :wikitext(a.award and a.award ~= "" and a.award or "Award")
             tr:tag('td'):addClass('pd-prize-val')
                :wikitext(tonumber(a.prize) and tonumber(a.prize) > 0
                    and fmtCurrency(tonumber(a.prize)) or "—")
         end
         end
         for _, t in ipairs(tournHistory) do
    end
             local tr    = tbl:tag('tr')
 
             local place = t.place
    -- Team results table
            local placeClass = place == 1 and 'pd-place-chip place-1'
    local sec2 = right:tag('div'):addClass('pd-prize-section')
                            or place == 2 and 'pd-place-chip place-2'
    sec2:tag('div'):addClass('pd-table-title'):wikitext('🎖 Team Tournament Results')
                            or place == 3 and 'pd-place-chip place-3'
    if #teamResults > 0 then
                            or 'pd-place-chip place-other'
        local tbl2 = sec2:tag('table'):addClass('pd-tourn-table')
        local hdr2 = tbl2:tag('tr')
        hdr2:tag('th'):wikitext('Tournament')
        hdr2:tag('th'):wikitext('Team')
        hdr2:tag('th'):wikitext('Place')
        hdr2:tag('th'):wikitext('Result')
        hdr2:tag('th'):wikitext('Prize')
         for _, t in ipairs(teamResults) do
             local tr2 = tbl2:tag('tr')
            tr2:tag('td'):wikitext("[[" .. t.tournament .. "]]")
            tr2:tag('td'):wikitext("[[" .. t.team .. "]]")
 
            -- Place chip or award name
             local placeTd = tr2:tag('td')
            if t.place then
                local pc = t.place == 1 and 'pd-place-chip place-1'
                        or t.place == 2 and 'pd-place-chip place-2'
                        or t.place == 3 and 'pd-place-chip place-3'
                        or 'pd-place-chip place-other'
                placeTd:tag('span'):addClass(pc):wikitext(tostring(t.place))
            elseif t.award and t.award ~= "" then
                placeTd:tag('span'):addClass('pd-award-badge'):wikitext(t.award)
            else
                placeTd:wikitext("—")
            end
 
             local res = (t.result or ""):lower()
             local res = (t.result or ""):lower()
             local resBadge = res == 'q' and '<span class="pd-result-q">Qualified</span>'
             local resTd = tr2:tag('td')
                          or res == 'e' and '<span class="pd-result-e">Eliminated</span>'
            if    res == 'q' then resTd:tag('span'):addClass('pd-result-q'):wikitext('Qualified')
                          or ""
            elseif res == 'q2' then resTd:tag('span'):addClass('pd-result-q'):wikitext('Qualified')
            elseif res == 'e' then resTd:tag('span'):addClass('pd-result-e'):wikitext('Eliminated')
            else resTd:wikitext("") end


             tr:tag('td'):wikitext('[['..t.tournament..']]')
             tr2:tag('td'):addClass(t.prize > 0 and 'pd-prize-val' or '')
            tr:tag('td'):wikitext('[['..t.team..']]')
            tr:tag('td'):wikitext(t.stage or "—")
            tr:tag('td'):tag('span'):addClass(placeClass):wikitext(tostring(place))
            tr:tag('td'):wikitext(resBadge)
            tr:tag('td'):addClass(t.prize > 0 and 'pd-prize-cell' or 'pd-prize-none')
                 :wikitext(t.prize > 0 and fmtCurrency(t.prize) or "—")
                 :wikitext(t.prize > 0 and fmtCurrency(t.prize) or "—")
         end
         end
     else
     else
         right:tag('div'):addClass('pd-empty')
         sec2:tag('div'):addClass('pd-empty'):wikitext('No tournament data found.')
            :wikitext('No tournament data found for this player.')
     end
     end


     -- Stats placeholder (ready for future)
     -- Stats placeholder
     right:tag('div'):addClass('pd-stats-placeholder')
     right:tag('div'):addClass('pd-stats-placeholder')
         :tag('b'):wikitext('📊 Performance Stats'):done()
         :tag('b'):wikitext('📊 Performance Stats'):done()
         :wikitext('Coming soon — per-match stats will appear here once Player_Stats data is available.')
         :wikitext('Coming soon — per-match stats will appear here.')


     return tostring(root)
     return tostring(root)

Revision as of 03:33, 7 March 2026

Documentation for this module may be created at Module:PlayerDashboard/doc

-- Module:PlayerDashboard v2
-- All 8 fixes applied.

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 fmtCurrency(n)
    if not n or n == 0 then return "—" end
    local s = tostring(math.floor(n))
    local l3 = s:sub(-3)
    local rest = s:sub(1, -4)
    local fmt = rest:reverse():gsub("(%d%d)", "%1,"):reverse()
    if fmt ~= "" then l3 = "," .. l3 end
    return "₹ " .. fmt .. l3
end

local function fmtDate(d)
    if not d or d == "" then return "?" end
    return lang:formatDate("M y", d)
end

-- Flag emoji map
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(country)
    if not country or country == "" then return "" end
    local key = country:lower():gsub("%s+", "")
    return (FLAGS[key] or "") .. " "
end

local function statusStyle(s)
    s = (s or ""):lower()
    if s == "active"   then return "#4ade80", "rgba(34,197,94,.2)",   "rgba(34,197,94,.35)"  end
    if s == "inactive" then return "#fbbf24", "rgba(251,191,36,.2)",  "rgba(251,191,36,.35)" end
    if s == "retired"  then return "#94a3b8", "rgba(148,163,184,.2)", "rgba(148,163,184,.35)"end
    if s == "banned"   then return "#f87171", "rgba(239,68,68,.2)",   "rgba(239,68,68,.35)"  end
    return "#94a3b8", "rgba(148,163,184,.2)", "rgba(148,163,184,.35)"
end

-- Fetch team logo URL from Teams table
local function getTeamInfo(teamName)
    if not teamName or teamName == "" then return nil end
    local rows = cargo.query("Teams",
        "display_name,image,image_dark",
        { where = "name='" .. esc(teamName) .. "'", limit = 1 })
    if rows and #rows > 0 then return rows[1] end
    return nil
end

local function logoWikitext(imageFile, size, classes)
    if not imageFile or imageFile == "" then return nil end
    size = size or "26px"
    classes = classes or "pd-team-logo-img"
    return "[[File:" .. imageFile .. "|" .. size .. "|link=|class=" .. classes .. "]]"
end

function p.main(frame)
    local pageName   = mw.title.getCurrentTitle().text
    local sqlPage    = esc(pageName)

    -- 1. Player row
    local pRows = cargo.query("Players",
        "id,real_name,image,current_team,nationality,status,game," ..
        "birth_date,role,instagram,youtube,twitter,discord,facebook",
        { where = "_pageName='" .. sqlPage .. "'", limit = 1 })
    local pl = (pRows and #pRows > 0) and pRows[1] or {}
    local displayName = pl.id or mw.title.getCurrentTitle().subpageText
    local sqlName = esc(displayName)

    -- 2. Krafton rank
    local rankVal = nil
    local rRows = cargo.query("Krafton_Rankings", "rank", {
        where = string.format("(name='%s' OR name='%s') AND type='Player'",
            sqlPage, sqlName), limit = 1 })
    if rRows and #rRows > 0 then rankVal = "#" .. rRows[1].rank end

    -- 3. Earnings
    local indEarnings, teamEarnings = 0, 0
    local eRows = cargo.query("PrizeMoney", "SUM(prize)=total", {
        where = string.format("(player='%s' OR player='%s')", sqlPage, sqlName) })
    if eRows and #eRows > 0 then indEarnings = tonumber(eRows[1].total) or 0 end

    -- All tournaments player appeared in
    local participation = cargo.query("Tournament_Teams", "tournament,team", {
        where = string.format(
            "p1='%s' OR p1='%s' OR p2='%s' OR p2='%s' OR " ..
            "p3='%s' OR p3='%s' OR p4='%s' OR p4='%s' OR " ..
            "p5='%s' OR p5='%s' OR p6='%s' OR p6='%s'",
            sqlPage,sqlName, sqlPage,sqlName, sqlPage,sqlName,
            sqlPage,sqlName, sqlPage,sqlName, sqlPage,sqlName),
        limit = 500 })

    if participation and #participation > 0 then
        local conds = {}
        for _, r in ipairs(participation) do
            if r.tournament and r.team then
                table.insert(conds, string.format(
                    "(tournament='%s' AND team='%s')", esc(r.tournament), esc(r.team)))
            end
        end
        if #conds > 0 then
            local tPrizes = cargo.query("PrizeMoney", "SUM(prize)=team_total", {
                where = "(" .. table.concat(conds, " OR ") .. ")" ..
                        " AND (player='' OR player IS NULL)" })
            if tPrizes and #tPrizes > 0 then
                teamEarnings = tonumber(tPrizes[1].team_total) or 0
            end
        end
    end
    local totalEarnings = indEarnings + teamEarnings

    -- 4. Team history with logos
    local teamHist = cargo.query("Player_Former_Teams",
        "team,join_date,leave_date",
        { where = "player_id='" .. sqlPage .. "'",
          orderBy = "join_date DESC", limit = 20 })
    if #teamHist == 0 then
        teamHist = cargo.query("Player_Former_Teams",
            "team,join_date,leave_date",
            { where = "player_id='" .. sqlName .. "'",
              orderBy = "join_date DESC", limit = 20 })
    end

    -- 5a. Individual awards (player= set in PrizeMoney)
    local indAwards = cargo.query("PrizeMoney",
        "tournament,award,prize",
        { where = string.format("player='%s' OR player='%s'", sqlPage, sqlName),
          orderBy = "tournament DESC", limit = 100 })

    -- 5b. Team results — all tournaments from participation, with standings + prize
    local teamResults = {}
    local seen = {}
    if participation and #participation > 0 then
        local ttConds = {}
        for _, r in ipairs(participation) do
            if r.tournament and r.team then
                local key = r.tournament .. "|" .. r.team
                if not seen[key] then
                    seen[key] = true
                    table.insert(ttConds, string.format(
                        "(tournament='%s' AND team='%s')", esc(r.tournament), esc(r.team)))
                end
            end
        end

        if #ttConds > 0 then
            -- Get standings (best stage = lowest rank = Finals > Semis etc.)
            local stRows = cargo.query("StageStandings",
                "tournament,stage,team,totalpts,result",
                { where = table.concat(ttConds, " OR "),
                  orderBy = "tournament DESC", limit = 500 })

            -- index standings by tournament|team, keep last stage entry
            local stIndex = {}
            for _, row in ipairs(stRows) do
                local k = row.tournament .. "|" .. row.team
                stIndex[k] = row -- overwrites so last stage wins
            end

            -- Get prize per tournament+team
            local prizeIndex = {}
            local pRows2 = cargo.query("PrizeMoney",
                "tournament,team,SUM(prize)=total,placement,award",
                { where = "(" .. table.concat(ttConds, " OR ") .. ")" ..
                           " AND (player='' OR player IS NULL)",
                  groupBy = "tournament,team", limit = 500 })
            for _, row in ipairs(pRows2) do
                prizeIndex[row.tournament .. "|" .. row.team] = row
            end

            -- Build final list
            for _, r in ipairs(participation) do
                if r.tournament and r.team then
                    local k   = r.tournament .. "|" .. r.team
                    local st  = stIndex[k]
                    local pr  = prizeIndex[k]

                    -- get rank within stage if standings exist
                    local place = nil
                    if st then
                        local rankQ = cargo.query("StageStandings", "COUNT(team)=cnt", {
                            where = string.format(
                                "tournament='%s' AND stage='%s' AND totalpts>=%s",
                                esc(st.tournament), esc(st.stage),
                                tonumber(st.totalpts) or 0) })
                        if rankQ and #rankQ > 0 then
                            place = tonumber(rankQ[1].cnt) or nil
                        end
                    end
                    if pr and pr.placement and pr.placement ~= "" then
                        local n = pr.placement:match("(%d+)")
                        if n then place = place or tonumber(n) end
                    end

                    table.insert(teamResults, {
                        tournament = r.tournament,
                        team       = r.team,
                        place      = place,
                        award      = pr and pr.award or "",
                        result     = st and (st.result or "") or "",
                        prize      = pr and (tonumber(pr.total) or 0) or 0,
                    })
                end
            end
        end
    end

    -- Remove duplicates (participation may repeat tournament+team)
    local dedupedResults, seenR = {}, {}
    for _, r in ipairs(teamResults) do
        local k = r.tournament .. "|" .. r.team
        if not seenR[k] then
            seenR[k] = true
            table.insert(dedupedResults, r)
        end
    end
    teamResults = dedupedResults

    -- ── Build HTML ────────────────────────────────────────────
    local sc, sbg, sborder = statusStyle(pl.status)
    local initials = displayName:sub(1, 2):upper()
    local root = html.create('div'):addClass('pd-dashboard')

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

    -- Photo (circular, image or initials)
    local photo = hero:tag('div'):addClass('pd-hero-photo')
    if pl.image and pl.image ~= "" then
        photo:wikitext("[[File:" .. pl.image .. "|100px|link=|class=pd-hero-img]]")
    else
        photo:wikitext(initials)
    end

    local heroInfo = hero:tag('div'):addClass('pd-hero-info')
    heroInfo:tag('div'):addClass('pd-hero-name'):wikitext(displayName)
    if pl.real_name and pl.real_name ~= "" then
        heroInfo:tag('div'):addClass('pd-hero-realname'):wikitext(pl.real_name)
    end

    local tags = heroInfo:tag('div'):addClass('pd-hero-tags')
    if pl.status and pl.status ~= "" then
        tags:tag('span'):addClass('pd-tag')
            :css('color', sc):css('background', sbg):css('border', '1px solid ' .. sborder)
            :wikitext('● ' .. pl.status)
    end
    if pl.role     and pl.role ~= ""     then tags:tag('span'):addClass('pd-tag pd-tag-role'):wikitext(pl.role) end
    if pl.game     and pl.game ~= ""     then tags:tag('span'):addClass('pd-tag pd-tag-game'):wikitext(pl.game) end

    if rankVal then
        heroInfo:tag('div'):addClass('pd-rank-chip')
            :tag('span'):wikitext('Krafton Rank'):done()
            :tag('b'):wikitext(rankVal)
    end

    if totalEarnings > 0 then
        local er = hero:tag('div'):addClass('pd-hero-right')
        er:tag('div'):addClass('pd-earnings-label'):wikitext('Approx. Earnings')
        er:tag('div'):addClass('pd-earnings-val'):wikitext(fmtCurrency(totalEarnings))
        local bd = er:tag('div'):addClass('pd-earnings-breakdown')
        bd:tag('div')
            :tag('div'):addClass('pd-earn-sub-label'):wikitext('Team'):done()
            :tag('div'):addClass('pd-earn-sub-val'):wikitext(fmtCurrency(teamEarnings))
        bd:tag('div')
            :tag('div'):addClass('pd-earn-sub-label'):wikitext('Individual'):done()
            :tag('div'):addClass('pd-earn-sub-val'):wikitext(fmtCurrency(indEarnings))
    end

    -- BODY
    local body = root:tag('div'):addClass('pd-body')
    local left = body:tag('div'):addClass('pd-left')
    left:tag('div'):addClass('pd-section-title'):wikitext('Player 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

    infoRow('Real Name', pl.real_name)
    if pl.birth_date and pl.birth_date ~= "" then
        infoRow('Born', lang:formatDate("d F Y", pl.birth_date))
    end
    if pl.nationality and pl.nationality ~= "" then
        infoRow('Nationality', getFlag(pl.nationality) .. pl.nationality)
    end
    infoRow('Role', pl.role)
    infoRow('Game', pl.game)

    -- Current team with logo
    if pl.current_team and pl.current_team ~= "" then
        local ti = getTeamInfo(pl.current_team)
        local teamDisplay = (ti and ti.display_name and ti.display_name ~= "")
            and ti.display_name or pl.current_team
        local teamLogoFile = ti and ti.image and ti.image ~= "" and ti.image or nil

        local row = left:tag('div'):addClass('pd-info-row')
        row:tag('span'):addClass('pd-info-label'):wikitext('Current Team')
        local val = row:tag('span'):addClass('pd-info-val')
        local wrap = val:tag('span'):addClass('pd-current-team')
        if teamLogoFile then
            wrap:wikitext("[[File:" .. teamLogoFile .. "|22px|link=|class=pd-current-team-logo]]")
        end
        wrap:tag('span'):addClass('pd-current-team-name')
            :wikitext("[[" .. pl.current_team .. "|" .. teamDisplay .. "]]")
    end

    if rankVal then infoRow('Krafton Rank', rankVal) 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/'},
    }
    local socDiv = left:tag('div'):addClass('pd-socials')
    for _, s in ipairs(socials) do
        local v = pl[s.k]
        if v and v ~= "" then
            local url = v:match("^https?://") and v or (s.base .. v)
            socDiv:wikitext("[[File:"..s.file.."|24px|link="..url.."|class=social-img]]")
        end
    end

    -- Team history with logos
    if #teamHist > 0 then
        left:tag('div'):addClass('pd-section-title pd-section-gap'):wikitext('Team History')
        for _, h in ipairs(teamHist) do
            local ti = getTeamInfo(h.team or "")
            local tr = left:tag('div'):addClass('pd-team-row')
            if ti and ti.image and ti.image ~= "" then
                tr:wikitext("[[File:"..ti.image.."|26px|link=|class=pd-team-logo-img]]")
            else
                tr:tag('div'):addClass('pd-team-logo-fb')
                    :wikitext((h.team or "?"):sub(1, 3):upper())
            end
            tr:tag('div'):addClass('pd-team-name')
                :wikitext("[[" .. (h.team or "") .. "]]")
            tr:tag('div'):addClass('pd-team-dates')
                :wikitext(fmtDate(h.join_date) .. " – " ..
                    (h.leave_date and h.leave_date ~= ""
                        and fmtDate(h.leave_date) or "Now"))
        end
    end

    -- RIGHT PANEL
    local right = body:tag('div'):addClass('pd-right')

    -- Individual awards table
    if indAwards and #indAwards > 0 then
        local sec = right:tag('div'):addClass('pd-prize-section')
        sec:tag('div'):addClass('pd-table-title'):wikitext('🏆 Individual Awards')
        local tbl = sec:tag('table'):addClass('pd-tourn-table')
        local hdr = tbl:tag('tr')
        hdr:tag('th'):wikitext('Tournament')
        hdr:tag('th'):wikitext('Award')
        hdr:tag('th'):wikitext('Prize')
        for _, a in ipairs(indAwards) do
            local tr = tbl:tag('tr')
            tr:tag('td'):wikitext("[[" .. (a.tournament or "") .. "]]")
            tr:tag('td'):tag('span'):addClass('pd-award-badge')
                :wikitext(a.award and a.award ~= "" and a.award or "Award")
            tr:tag('td'):addClass('pd-prize-val')
                :wikitext(tonumber(a.prize) and tonumber(a.prize) > 0
                    and fmtCurrency(tonumber(a.prize)) or "—")
        end
    end

    -- Team results table
    local sec2 = right:tag('div'):addClass('pd-prize-section')
    sec2:tag('div'):addClass('pd-table-title'):wikitext('🎖 Team Tournament Results')
    if #teamResults > 0 then
        local tbl2 = sec2:tag('table'):addClass('pd-tourn-table')
        local hdr2 = tbl2:tag('tr')
        hdr2:tag('th'):wikitext('Tournament')
        hdr2:tag('th'):wikitext('Team')
        hdr2:tag('th'):wikitext('Place')
        hdr2:tag('th'):wikitext('Result')
        hdr2:tag('th'):wikitext('Prize')
        for _, t in ipairs(teamResults) do
            local tr2 = tbl2:tag('tr')
            tr2:tag('td'):wikitext("[[" .. t.tournament .. "]]")
            tr2:tag('td'):wikitext("[[" .. t.team .. "]]")

            -- Place chip or award name
            local placeTd = tr2:tag('td')
            if t.place then
                local pc = t.place == 1 and 'pd-place-chip place-1'
                        or t.place == 2 and 'pd-place-chip place-2'
                        or t.place == 3 and 'pd-place-chip place-3'
                        or 'pd-place-chip place-other'
                placeTd:tag('span'):addClass(pc):wikitext(tostring(t.place))
            elseif t.award and t.award ~= "" then
                placeTd:tag('span'):addClass('pd-award-badge'):wikitext(t.award)
            else
                placeTd:wikitext("—")
            end

            local res = (t.result or ""):lower()
            local resTd = tr2:tag('td')
            if     res == 'q'  then resTd:tag('span'):addClass('pd-result-q'):wikitext('Qualified')
            elseif res == 'q2' then resTd:tag('span'):addClass('pd-result-q'):wikitext('Qualified')
            elseif res == 'e'  then resTd:tag('span'):addClass('pd-result-e'):wikitext('Eliminated')
            else resTd:wikitext("—") end

            tr2:tag('td'):addClass(t.prize > 0 and 'pd-prize-val' or '')
                :wikitext(t.prize > 0 and fmtCurrency(t.prize) or "—")
        end
    else
        sec2:tag('div'):addClass('pd-empty'):wikitext('No tournament data found.')
    end

    -- Stats placeholder
    right:tag('div'):addClass('pd-stats-placeholder')
        :tag('b'):wikitext('📊 Performance Stats'):done()
        :wikitext('Coming soon — per-match stats will appear here.')

    return tostring(root)
end

return p