Module:PlayerDashboard: Difference between revisions
From eSportsAmaze
More actions
Esportsamaze (talk | contribs) No edit summary |
Esportsamaze (talk | contribs) No edit summary |
||
| Line 1: | Line 1: | ||
-- Module:PlayerDashboard | -- Module:PlayerDashboard v2 | ||
-- | -- All 8 fixes applied. | ||
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 | -- 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 "# | if s == "active" then return "#4ade80", "rgba(34,197,94,.2)", "rgba(34,197,94,.35)" end | ||
if s == "inactive" then return "# | 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,. | if s == "retired" then return "#94a3b8", "rgba(148,163,184,.2)", "rgba(148,163,184,.35)"end | ||
if s == "banned" then return "# | if s == "banned" then return "#f87171", "rgba(239,68,68,.2)", "rgba(239,68,68,.35)" end | ||
return "#94a3b8", "rgba(148,163,184,. | 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. Player row | ||
local pRows = cargo.query("Players", | local pRows = cargo.query("Players", | ||
"id,real_name,image,current_team,nationality,status,game | "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 | ||
local rankVal = | 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, | 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 | ||
local indEarnings, teamEarnings = 0, 0 | local indEarnings, teamEarnings = 0, 0 | ||
local | 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 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, | sqlPage,sqlName, sqlPage,sqlName, sqlPage,sqlName, | ||
sqlPage,sqlName, sqlPage,sqlName, sqlPage,sqlName), | |||
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 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= | { 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='".. | { where = "player_id='" .. sqlName .. "'", | ||
orderBy = "join_date DESC", limit = 20 }) | |||
end | end | ||
-- | -- 5a. Individual awards (player= set in PrizeMoney) | ||
local indAwards = cargo.query("PrizeMoney", | |||
"tournament,award,prize", | |||
local | { 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 | if participation and #participation > 0 then | ||
local | 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( | 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 | ||
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 | |||
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 = | award = pr and pr.award or "", | ||
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 = | 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 | ||
local hero = root:tag('div'):addClass('pd-hero') | local hero = root:tag('div'):addClass('pd-hero') | ||
local | -- 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 | ||
photo:wikitext("[[File:" .. pl.image .. "|100px|link=|class=pd-hero-img]]") | |||
else | else | ||
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 | ||
if pl.game and pl.game ~= "" then tags:tag('span'):addClass('pd-tag pd-tag-game'):wikitext(pl.game) end | |||
if pl.game and pl.game ~= "" then | |||
if rankVal | 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 | 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 = | local bd = er:tag('div'):addClass('pd-earnings-breakdown') | ||
bd:tag('div | 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 | 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 | ||
local body = root:tag('div'):addClass('pd-body') | local body = root:tag('div'):addClass('pd-body') | ||
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 | 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( | row:tag('span'):addClass('pd-info-label'):wikitext('Current Team') | ||
local | local val = row:tag('span'):addClass('pd-info-val') | ||
if | 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 | end | ||
if rankVal then infoRow('Krafton Rank', rankVal) end | |||
-- 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 | 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) | ||
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 | 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 | ||
tr:wikitext("[[File:"..ti.image.."|26px|link=|class=pd-team-logo-img]]") | |||
tr:tag('div'):addClass('pd-team-name'):wikitext( | 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 | and fmtDate(h.leave_date) or "Now")) | ||
end | end | ||
end | end | ||
-- | -- RIGHT PANEL | ||
local right = body:tag('div'):addClass('pd-right') | local right = body:tag('div'):addClass('pd-right') | ||
if # | -- Individual awards table | ||
local tbl = | 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 _, | 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 | ||
for _, t in ipairs( | end | ||
local | |||
local | -- 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 res = (t.result or ""):lower() | ||
local | 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 "—") | :wikitext(t.prize > 0 and fmtCurrency(t.prize) or "—") | ||
end | end | ||
else | else | ||
sec2:tag('div'):addClass('pd-empty'):wikitext('No tournament data found.') | |||
end | end | ||
-- Stats placeholder | -- 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 | :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