Module:PlayerDashboard
From eSportsAmaze
More actions
Documentation for this module may be created at Module:PlayerDashboard/doc
-- ================================================================
-- Module:PlayerDashboard v3
-- Full rewrite with all fixes:
-- • Circular photo, vertically centered hero
-- • Current team: logo (light/dark) + display_name
-- • Team history: logos (light/dark)
-- • Individual awards table (from PrizeMoney where player= set)
-- • Team results table:
-- - Place from new Tournament_Results cargo table
-- - Prize/award from PrizeMoney (team rows)
-- - Shows ALL tournaments player appeared in
-- - Place/Award column shows chip OR award badge
-- • Flag emojis for nationality
-- • Dark mode link + badge overrides via CSS variables
-- • Mobile friendly
-- ================================================================
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 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+",""):gsub("-","")
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
-- ── Team info + logo helpers ──────────────────────────────────────
local teamInfoCache = {}
local function getTeamInfo(teamName)
if not teamName or teamName == "" then return nil end
if teamInfoCache[teamName] then return teamInfoCache[teamName] end
local rows = cargo.query("Teams",
"display_name,image,image_dark",
{ where = "name='" .. esc(teamName) .. "'", limit = 1 })
local result = (rows and #rows > 0) and rows[1] or nil
teamInfoCache[teamName] = result
return result
end
-- Single image wikitext (player photo etc.)
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
-- Team logo with light/dark switching
-- Uses your existing logo-lightmode / logo-darkmode CSS classes
local function teamLogoWikitext(ti, size)
if not ti then return nil end
size = size or "26px"
local light = (ti.image and ti.image ~= "") and ti.image or nil
local dark = (ti.image_dark and ti.image_dark ~= "") and ti.image_dark or light
if not light then return nil end
return "[[File:"..light.."|"..size.."|link=|class=pd-team-logo-img logo-lightmode]]"
.. "[[File:"..dark .."|"..size.."|link=|class=pd-team-logo-img logo-darkmode]]"
end
-- ── Main ─────────────────────────────────────────────────────────
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. 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 })
-- ── 4. 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
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
-- ── 5. Team history ───────────────────────────────────────────
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
-- ── 6a. Individual awards ─────────────────────────────────────
-- PrizeMoney rows where player= is set (individual awards)
local indAwards = cargo.query("PrizeMoney",
"tournament,award,prize",
{ where = string.format(
"(player='%s' OR player='%s') AND player != ''",
sqlPage, sqlName),
orderBy = "tournament DESC", limit = 100 })
-- ── 6b. Team tournament results ───────────────────────────────
-- Source 1: Tournament_Results — new invisible table, stores place per team per tournament
-- Source 2: PrizeMoney — prize and award for team rows (player= empty)
-- Shows ALL tournaments from participation, even if no prize/placement
local teamResults = {}
local seenKey = {}
if participation and #participation > 0 then
-- Build unique tournament+team pairs
local pairs_list = {}
for _, r in ipairs(participation) do
if r.tournament and r.team then
local k = r.tournament .. "|" .. r.team
if not seenKey[k] then
seenKey[k] = true
table.insert(pairs_list, { tournament = r.tournament, team = r.team })
end
end
end
-- Build WHERE conditions for batch queries
local ttConds = {}
for _, r in ipairs(pairs_list) do
table.insert(ttConds, string.format(
"(tournament='%s' AND team='%s')",
esc(r.tournament), esc(r.team)))
end
if #ttConds > 0 then
local whereStr = table.concat(ttConds, " OR ")
-- Get placements from Tournament_Results table
local placeIndex = {}
local placeRows = cargo.query("Tournament_Results",
"tournament,team,place",
{ where = whereStr, limit = 500 })
if placeRows then
for _, row in ipairs(placeRows) do
placeIndex[row.tournament .. "|" .. row.team] = tonumber(row.place)
end
end
-- Get prize + award from PrizeMoney (team rows only, player= empty)
local prizeIndex = {}
local prizeRows = cargo.query("PrizeMoney",
"tournament,team,prize,placement,award",
{ where = "(" .. whereStr .. ")"
.. " AND (player='' OR player IS NULL)",
limit = 500 })
if prizeRows then
for _, row in ipairs(prizeRows) do
local k = row.tournament .. "|" .. row.team
-- keep highest prize row if multiple entries
local existing = prizeIndex[k]
local newPrize = tonumber(row.prize) or 0
if not existing or newPrize > (existing.prize or 0) then
prizeIndex[k] = {
prize = newPrize,
placement = row.placement or "",
award = row.award or "",
}
end
end
end
-- Assemble final list
for _, r in ipairs(pairs_list) do
local k = r.tournament .. "|" .. r.team
local pr = prizeIndex[k]
local pl_num = placeIndex[k]
-- fallback: try to parse place from PrizeMoney placement string
if not pl_num and pr and pr.placement ~= "" then
local n = pr.placement:match("(%d+)")
if n then pl_num = tonumber(n) end
end
table.insert(teamResults, {
tournament = r.tournament,
team = r.team,
place = pl_num,
award = (pr and pr.award ~= "" and pr.award) or nil,
prize = (pr and pr.prize > 0 and pr.prize) or 0,
})
end
end
end
-- Sort: placed entries first (by place), then unplaced
table.sort(teamResults, function(a, b)
if a.place and b.place then return a.place < b.place end
if a.place then return true end
if b.place then return false end
return (a.tournament or "") > (b.tournament or "") -- recent first
end)
-- ── 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
local photo = hero:tag('div'):addClass('pd-hero-photo')
if pl.image and pl.image ~= "" then
photo:wikitext("[[File:" .. pl.image
.. "|96px|link=|class=pd-hero-img]]")
else
photo:wikitext(initials)
end
-- Name + tags
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
-- Earnings
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 + display_name
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 logoHtml = teamLogoWikitext(ti, "22px")
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 logoHtml then wrap:wikitext(logoHtml) 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
.. "|32px|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 logoHtml = teamLogoWikitext(ti, "24px")
local tr = left:tag('div'):addClass('pd-team-row')
if logoHtml then
tr:wikitext(logoHtml)
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 / Award') -- combined heading
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 badge OR dash
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 then
placeTd:tag('span'):addClass('pd-award-badge'):wikitext(t.award)
else
placeTd: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