Module:Tournament: Difference between revisions
From eSportsAmaze
More actions
Esportsamaze (talk | contribs) No edit summary |
Esportsamaze (talk | contribs) No edit summary |
||
| (27 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
local p = {} | local p = {} | ||
local html = mw.html | local html = mw.html | ||
local cargo = mw.ext.cargo | |||
local lang = mw.getContentLanguage() | local lang = mw.getContentLanguage() | ||
local function sqlEscape(s) | |||
if not s then return "" end | |||
s = s:gsub("\\", "\\\\") | |||
s = s:gsub("'", "\\'") | |||
return s | |||
end | |||
-- ============================================================ | -- ============================================================ | ||
| Line 7: | Line 15: | ||
-- ============================================================ | -- ============================================================ | ||
-- | -- Indian Format (1,00,000) | ||
local function | local function formatINR(amount) | ||
if not amount | if not amount then return "0" end | ||
local | local n = tostring(math.floor(tonumber(amount) or 0)) | ||
if #n <= 3 then return n end | |||
if #n <= 3 then return | |||
local last3 = n:sub(-3) | local last3 = n:sub(-3) | ||
local rest = n:sub(1, -4) | local rest = n:sub(1, -4) | ||
local formattedRest = rest:reverse():gsub("(%d%d)", "%1,"):reverse() | -- Indian format: group remaining digits in pairs (from right) | ||
if | local formattedRest = rest:reverse():gsub("(%d%d)", "%1,"):reverse():gsub("^,", "") | ||
return formattedRest .. "," .. last3 | |||
end | |||
local function formatStd(amount) | |||
if not amount then return "0" end | |||
local n = tostring(math.floor(tonumber(amount) or 0)) | |||
return n:reverse():gsub("(%d%d%d)", "%1,"):reverse():gsub("^,", "") | |||
end | |||
-- Returns: { displayHTML, dbValue (INR) } | |||
local function processMoney(amount, currency, rate) | |||
if not amount or amount == "" or amount == "0" then | |||
return { display = "TBD", dbValue = 0 } | |||
end | |||
local val = tonumber(amount) or 0 | |||
local r = tonumber(rate) or 1 | |||
local symbol = currency or "₹" | |||
-- Clean the amount string (remove commas if passed by accident) | |||
-- FIX: Extra parens around gsub to discard the 'count' return value | |||
-- Fix (match Module:PrizePool's pattern): | |||
if type(amount) == "string" then | |||
val = tonumber((amount:gsub(",", ""))) or 0 | |||
else | |||
val = tonumber(amount) or 0 | |||
end | |||
if symbol == "₹" or symbol == "INR" or symbol == "Rs" then | |||
local inrVal = val | |||
local usdVal = val * r | |||
local display = "₹ " .. formatINR(inrVal) | |||
if r ~= 1 then | |||
display = display .. '<br><span style="font-size:0.8em; opacity:0.7">($ ' .. formatStd(usdVal) .. ')</span>' | |||
end | |||
return { display = display, dbValue = inrVal } | |||
else | |||
local localVal = val | |||
local inrVal = val * r | |||
local display = symbol .. " " .. formatStd(localVal) | |||
display = display .. '<br><span style="font-size:0.8em; opacity:0.7">(₹ ' .. formatINR(inrVal) .. ')</span>' | |||
return { display = display, dbValue = inrVal } | |||
end | |||
end | end | ||
local function formatDateRange(startStr, endStr) | local function formatDateRange(startStr, endStr) | ||
if not startStr or startStr == "" then return "TBA" end | if not startStr or startStr == "" then return "TBA" end | ||
| Line 33: | Line 82: | ||
end | end | ||
local function getCleanTitle(pageLink) | local function getCleanTitle(pageLink) | ||
if not pageLink then return "" end | if not pageLink then return "" end | ||
| Line 41: | Line 89: | ||
end | end | ||
-- | local function getSmallTeamLogo(teamName) | ||
if not teamName or teamName == "" then return "" end | |||
local fileName = teamName .. '.png' | |||
if mw.title.new('File:' .. fileName).exists then | |||
return '[[File:' .. fileName .. '|25px|link=' .. teamName .. '|class=team-logo-small]] ' | |||
end | |||
return '' | |||
end | |||
local function getTeamFromPrizeMoney(pageName, tournamentName, placeType) | |||
if not cargo then return nil end | |||
local placeClause = "" | |||
if placeType == "1" then | |||
placeClause = '(placement="1" OR placement="1st" OR placement="Winner" OR placement="Champion")' | |||
elseif placeType == "2" then | |||
placeClause = '(placement="2" OR placement="2nd" OR placement="Runner-up" OR placement="Runner Up")' | |||
else | |||
return nil | |||
end | |||
local pageClause = "" | |||
if tournamentName and tournamentName ~= "" then | |||
pageClause = string.format('(tournament="%s" OR _pageName="%s" OR _pageName LIKE "%%%s%%")', tournamentName, pageName, tournamentName) | |||
else | |||
pageClause = string.format('(_pageName="%s" OR _pageName LIKE "%s/%%")', pageName, pageName) | |||
end | |||
local results = cargo.query('PrizeMoney', 'team', { | |||
where = pageClause .. ' AND ' .. placeClause, | |||
limit = 1 | |||
}) | |||
if results and #results > 0 then return results[1].team end | |||
return nil | |||
end | |||
local function getSocials(args) | local function getSocials(args) | ||
local container = html.create('div'):addClass('fib-socials') | |||
local container = html.create('div'):addClass('fib-socials | |||
local hasSocials = false | local hasSocials = false | ||
local platforms = { | local platforms = { | ||
{arg='instagram', | {arg='instagram', file='Icon_instagram.png'}, | ||
{arg='twitter', | {arg='twitter', file='Icon_twitter.png'}, | ||
{arg='youtube', | {arg='youtube', file='Icon_youtube.png'}, | ||
{arg='discord', | {arg='discord', file='Icon_discord.png'}, | ||
{arg='facebook', | {arg='facebook', file='Icon_facebook.png'}, | ||
{arg='website', | {arg='website', file='Icon_website.png'} | ||
} | } | ||
for _, p in ipairs(platforms) do | for _, p in ipairs(platforms) do | ||
if args[p.arg] and args[p.arg] ~= "" then | if args[p.arg] and args[p.arg] ~= "" then | ||
hasSocials = true | hasSocials = true | ||
container:wikitext('[[File:' .. p.file .. '|24px|link=' .. args[p.arg] .. '|class=social-img]]') | |||
container:wikitext('[' .. | |||
end | end | ||
end | end | ||
if hasSocials then return tostring(container) else return "" end | if hasSocials then return tostring(container) else return "" end | ||
end | end | ||
local function getSeriesNav(currentSeries, currentValue) | |||
local function | if not currentSeries or not currentValue or not cargo then return "" end | ||
local | local nav = html.create('div'):addClass('fib-nav') | ||
local prevQuery = cargo.query('Tournaments', '_pageName, series_season', { | |||
where = string.format("series = '%s' AND series_value < %s", sqlEscape(currentSeries), tonumber(currentValue) or 0), | |||
orderBy = 'series_value DESC', limit = 1 | |||
}) | |||
local prevBtn = nav:tag('div'):addClass('fib-nav-btn prev') | |||
if prevQuery and #prevQuery > 0 then | |||
prevBtn:wikitext('[[' .. prevQuery[1]._pageName .. '|« ' .. (prevQuery[1].series_season or "Previous") .. ']]') | |||
else | |||
local | prevBtn:css('opacity', '0.3'):wikitext('« Previous') | ||
end | |||
local nextQuery = cargo.query('Tournaments', '_pageName, series_season', { | |||
where = string.format("series = '%s' AND series_value > %s", sqlEscape(currentSeries), tonumber(currentValue) or 0), | |||
orderBy = 'series_value ASC', limit = 1 | |||
}) | |||
local nextBtn = nav:tag('div'):addClass('fib-nav-btn next') | |||
if nextQuery and #nextQuery > 0 then | |||
nextBtn:wikitext('[[' .. nextQuery[1]._pageName .. '|' .. (nextQuery[1].series_season or "Next") .. ' »]]') | |||
else | else | ||
nextBtn:css('opacity', '0.3'):wikitext('Next »') | |||
end | end | ||
return tostring( | return tostring(nav) | ||
end | end | ||
local function getInfoboxLogo(pageName, image, imageDark) | local function getInfoboxLogo(pageName, image, imageDark) | ||
local lightFile = (image ~= "" and image) or (pageName .. '.png') | local lightFile = (image ~= "" and image) or (pageName .. '.png') | ||
| Line 96: | Line 182: | ||
darkFile = name .. "_dark" .. ext | darkFile = name .. "_dark" .. ext | ||
end | end | ||
local hasLight = mw.title.new('File:' .. lightFile).exists | local hasLight = mw.title.new('File:' .. lightFile).exists | ||
local hasDark = mw.title.new('File:' .. darkFile).exists | local hasDark = mw.title.new('File:' .. darkFile).exists | ||
local container = html.create('div'):addClass('fib-image') | local container = html.create('div'):addClass('fib-image') | ||
local lSpan = container:tag('span'):addClass('logo-lightmode') | local lSpan = container:tag('span'):addClass('logo-lightmode') | ||
| Line 106: | Line 190: | ||
if hasDark then dSpan:wikitext('[[File:' .. darkFile .. '|250px]]') elseif hasLight then dSpan:wikitext('[[File:' .. lightFile .. '|250px]]') else dSpan:wikitext('[[File:Shield_team_dark.png|150px]]') end | if hasDark then dSpan:wikitext('[[File:' .. darkFile .. '|250px]]') elseif hasLight then dSpan:wikitext('[[File:' .. lightFile .. '|250px]]') else dSpan:wikitext('[[File:Shield_team_dark.png|150px]]') end | ||
return tostring(container) | return tostring(container) | ||
end | |||
local function getTourneyLogo(imageFile, darkImageFile) | |||
local container = html.create('div'):addClass('tr-event-logo') | |||
if imageFile and imageFile ~= "" then | |||
local darkFile = darkImageFile | |||
if not darkFile or darkFile == "" then | |||
local ext = imageFile:match("^.+(%..+)$") or ".png" | |||
local name = imageFile:gsub("%..+$", "") | |||
darkFile = name .. "_dark" .. ext | |||
end | |||
local hasDark = mw.title.new('File:' .. darkFile).exists | |||
container:tag('span'):addClass('logo-lightmode'):wikitext('[[File:' .. imageFile .. '|40px|link=]]') | |||
local dSpan = container:tag('span'):addClass('logo-darkmode') | |||
if hasDark then dSpan:wikitext('[[File:' .. darkFile .. '|40px|link=]]') else dSpan:wikitext('[[File:' .. imageFile .. '|40px|link=]]') end | |||
else | |||
container:wikitext('<i class="fa-solid fa-trophy" style="color:var(--text-muted); opacity:0.3;"></i>') | |||
end | |||
return tostring(container) | |||
end | |||
local function getTierClass(tier) | |||
if not tier then return "tier-misc" end | |||
local t = tier:lower() | |||
if string.find(t, "s") and string.find(t, "tier") then return "tier-s" end | |||
if string.find(t, "a") and string.find(t, "tier") then return "tier-a" end | |||
if string.find(t, "b") and string.find(t, "tier") then return "tier-b" end | |||
if string.find(t, "c") and string.find(t, "tier") then return "tier-c" end | |||
return "tier-misc" | |||
end | end | ||
| Line 115: | Line 228: | ||
local page = args.name or mw.title.getCurrentTitle().text | local page = args.name or mw.title.getCurrentTitle().text | ||
local cleanName = mw.title.getCurrentTitle().subpageText | local cleanName = mw.title.getCurrentTitle().subpageText | ||
local | local dbPageName = mw.title.getCurrentTitle().prefixedText | ||
local prizeRaw = args.prize_pool or args.prizepool | |||
local currency = args.currency or "₹" | |||
local rate = args.rate or 1 | |||
-- Process Money for Display & Database | |||
local moneyData = processMoney(prizeRaw, currency, rate) | |||
local finalWinner = args.winner | |||
if not finalWinner or finalWinner == "" then | |||
finalWinner = getTeamFromPrizeMoney(dbPageName, cleanName, "1") | |||
end | |||
local finalRunnerUp = args.runner_up | |||
if not finalRunnerUp or finalRunnerUp == "" then | |||
finalRunnerUp = getTeamFromPrizeMoney(dbPageName, cleanName, "2") | |||
end | |||
if mw.ext.cargo and mw.ext.cargo.store then | if mw.ext.cargo and mw.ext.cargo.store then | ||
mw.ext.cargo.store('Tournaments', { | mw.ext.cargo.store('Tournaments', { | ||
_pageName = | _pageName = dbPageName, | ||
name = args.name or cleanName, | name = args.name or cleanName, | ||
series = args.series, | series = args.series, | ||
series_season = args.series_season, | |||
series_value = args.series_value, | |||
organizer = args.organizer, | organizer = args.organizer, | ||
sponsor = args.sponsor, | sponsor = args.sponsor, | ||
| Line 131: | Line 263: | ||
location = args.location, | location = args.location, | ||
venue = args.venue, | venue = args.venue, | ||
prize_pool = | prize_pool = moneyData.dbValue, -- STORES INR INTEGER | ||
start_date = args.start_date, | start_date = args.start_date, | ||
end_date = args.end_date, | end_date = args.end_date, | ||
winner = | winner = finalWinner, | ||
runner_up = finalRunnerUp, | |||
image = args.image, | image = args.image, | ||
image_dark = args.image_dark, | image_dark = args.image_dark, | ||
| Line 149: | Line 280: | ||
local root = html.create('div'):addClass('flat-infobox') | local root = html.create('div'):addClass('flat-infobox') | ||
local header = root:tag('div'):addClass('fib-header') | local header = root:tag('div'):addClass('fib-header') | ||
header:tag('div'):addClass('fib-title'):wikitext(args.name or cleanName) | header:tag('div'):addClass('fib-title'):wikitext(args.name or cleanName) | ||
root:wikitext(getInfoboxLogo(page, args.image, args.image_dark)) | root:wikitext(getInfoboxLogo(page, args.image, args.image_dark)) | ||
| Line 159: | Line 287: | ||
grid1:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Event Tier'):done():tag('div'):addClass('fib-value-sm'):wikitext(args.tier or 'Unranked'):done() | grid1:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Event Tier'):done():tag('div'):addClass('fib-value-sm'):wikitext(args.tier or 'Unranked'):done() | ||
grid1:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Type'):done():tag('div'):addClass('fib-value-sm'):wikitext(args.type or 'Online'):done() | grid1:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Type'):done():tag('div'):addClass('fib-value-sm'):wikitext(args.type or 'Online'):done() | ||
local grid2 = root:tag('div'):addClass('fib-grid') | local grid2 = root:tag('div'):addClass('fib-grid') | ||
grid2:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Mode'):done():tag('div'):addClass('fib-value-sm'):wikitext(args.mode or 'TBD'):done() | grid2:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Mode'):done():tag('div'):addClass('fib-value-sm'):wikitext(args.mode or 'TBD'):done() | ||
grid2:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Location'):done():tag('div'):addClass('fib-value-sm'):wikitext(args.location or 'India'):done() | grid2:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Location'):done():tag('div'):addClass('fib-value-sm'):wikitext(args.location or 'India'):done() | ||
if | if prizeRaw then | ||
root:tag('div'):addClass('fib-prize') | root:tag('div'):addClass('fib-prize') | ||
:tag('div'):addClass('fib-label-sm'):wikitext('Total Prize Pool'):done() | :tag('div'):addClass('fib-label-sm'):wikitext('Total Prize Pool'):done() | ||
:tag('div'):addClass('fib-prize-val'):wikitext( | :tag('div'):addClass('fib-prize-val'):wikitext(moneyData.display):done() | ||
end | end | ||
| Line 178: | Line 306: | ||
addRow('Series', args.series and '[[' .. args.series .. ']]') | addRow('Series', args.series and '[[' .. args.series .. ']]') | ||
if args.series_season then addRow('Season', args.series_season) end | |||
addRow('Organizer', args.organizer) | addRow('Organizer', args.organizer) | ||
addRow('Sponsors', args.sponsor) | addRow('Sponsors', args.sponsor) | ||
| Line 183: | Line 312: | ||
addRow('Dates', formatDateRange(args.start_date, args.end_date)) | addRow('Dates', formatDateRange(args.start_date, args.end_date)) | ||
addRow('Device', args.device) | addRow('Device', args.device) | ||
if | if finalWinner then addRow('Winner', "'''[[" .. finalWinner .. "]]'''") end | ||
if finalRunnerUp then addRow('Runner Up', "'''[[" .. finalRunnerUp .. "]]'''") end | |||
root:wikitext(getSocials(args)) | root:wikitext(getSocials(args)) | ||
if args.series and args.series_value then root:wikitext(getSeriesNav(args.series, args.series_value)) end | |||
return tostring(root) | |||
end | |||
-- ============================================================ | |||
-- MAIN 2: TABLE ROW (Connects to List) | |||
-- ============================================================ | |||
function p.tableRow(frame) | |||
local args = frame.args | |||
local page = args.Page or "" | |||
local name = getCleanTitle(page) | |||
local startDate = args.start_date or "" | |||
local endDate = args.end_date or "" | |||
local tier = args.tier or "-" | |||
local prizeVal = args.prize_pool or 0 | |||
-- | -- Format prize for list (Simple INR display for consistency in tables) | ||
local prizeDisplay = "₹ " .. formatINR(prizeVal) | |||
local winner = args.winner | |||
if not winner or winner == "" then winner = getTeamFromPrizeMoney(page, name, "1") end | |||
local runnerUp = args.runner_up | |||
if not runnerUp or runnerUp == "" then runnerUp = getTeamFromPrizeMoney(page, name, "2") end | |||
local row = html.create('tr') | |||
row:tag('td'):attr('data-label', 'Date'):attr('data-sort-value', startDate):tag('div'):addClass('tr-date'):wikitext(formatDateRange(startDate, endDate)) | |||
row:tag('td'):attr('data-label', 'Tier'):css('text-align', 'center'):tag('span'):addClass('tier-badge'):addClass(getTierClass(tier)):wikitext(tier) | |||
row:tag('td'):attr('data-label', 'Tournament'):css('font-weight', 'bold'):wikitext('[[' .. page .. '|' .. name .. ']]') | |||
row:tag('td'):attr('data-label', 'Location'):wikitext(args.location or '-') | |||
row:tag('td'):attr('data-label', 'Prize'):attr('data-sort-value', prizeVal):addClass('tr-prize'):css('text-align', 'right'):wikitext(prizeDisplay) | |||
local winCell = row:tag('td'):attr('data-label', 'Winner'):addClass('cell-winner') | |||
if winner and winner ~= "" then winCell:wikitext(getSmallTeamLogo(winner) .. '[[' .. winner .. ']]') else winCell:css('text-align', 'center'):wikitext('-') end | |||
return tostring( | |||
local runCell = row:tag('td'):attr('data-label', 'Runner Up') | |||
if runnerUp and runnerUp ~= "" then runCell:wikitext(getSmallTeamLogo(runnerUp) .. '[[' .. runnerUp .. ']]') else runCell:css('text-align', 'center'):wikitext('-') end | |||
return tostring(row) | |||
end | end | ||
-- ============================================================ | -- ============================================================ | ||
-- MAIN | -- MAIN 3: LIST ROW (Card Style) | ||
-- ============================================================ | -- ============================================================ | ||
function p.listRow(frame) | function p.listRow(frame) | ||
| Line 218: | Line 367: | ||
local startDate = args.start_date or "" | local startDate = args.start_date or "" | ||
local endDate = args.end_date or "" | local endDate = args.end_date or "" | ||
local tier = args.tier or "" | |||
local image = args.image or "" | |||
local imageDark = args.image_dark or "" | |||
local prizeVal = args.prize_pool or 0 | |||
local prizeDisplay = "₹ " .. formatINR(prizeVal) | |||
local winner = args.winner | |||
if not winner or winner == "" then winner = getTeamFromPrizeMoney(page, name, "1") end | |||
local row = html.create('div'):addClass('tourney-row') | local row = html.create('div'):addClass('tourney-row') | ||
if tier ~= "" then row:addClass('row-' .. getTierClass(tier)) end | |||
row:tag('div'):addClass('tr-date'):wikitext(formatDateRange(startDate, endDate)) | row:tag('div'):addClass('tr-date'):wikitext(formatDateRange(startDate, endDate)) | ||
row:tag('div'):addClass('tr-event-logo-col'):wikitext(getTourneyLogo(image, imageDark)) | |||
local info = row:tag('div'):addClass('tr-info') | local info = row:tag('div'):addClass('tr-info') | ||
info:tag('div'):addClass('tr-name'):wikitext('[[' .. page .. '|' .. name .. ']]') | local nameRow = info:tag('div'):addClass('tr-name-row') | ||
nameRow:tag('span'):addClass('tr-name'):wikitext('[[' .. page .. '|' .. name .. ']]') | |||
if tier ~= "" then nameRow:tag('span'):addClass('tier-badge'):addClass(getTierClass(tier)):css('margin-left','8px'):wikitext(tier) end | |||
if args.organizer then info:tag('div'):addClass('tr-org'):wikitext(args.organizer) end | if args.organizer then info:tag('div'):addClass('tr-org'):wikitext(args.organizer) end | ||
local winDiv = row:tag('div'):addClass('tr-winner mobile-hide') | local winDiv = row:tag('div'):addClass('tr-winner mobile-hide') | ||
if | if winner and winner ~= "" then winDiv:wikitext("🏆 " .. winner) else winDiv:tag('span'):css('opacity', '0.3'):wikitext('-') end | ||
row:tag('div'):addClass('tr-prize'):wikitext(prizeDisplay) | |||
return tostring(row) | return tostring(row) | ||
end | end | ||
-- ============================================================ | -- ============================================================ | ||
-- MAIN | -- MAIN 4: LIST ROW MAIN (Compact) | ||
-- ============================================================ | -- ============================================================ | ||
function p.listRowMain(frame) | function p.listRowMain(frame) | ||
| Line 242: | Line 407: | ||
local image = args.image or "" | local image = args.image or "" | ||
local imageDark = args.image_dark or "" | local imageDark = args.image_dark or "" | ||
local prizeVal = args.prize_pool or 0 | |||
local prizeDisplay = "₹ " .. formatINR(prizeVal) | |||
local row = html.create('div'):addClass('tourney-row tr-compact') | local row = html.create('div'):addClass('tourney-row tr-compact') | ||
row:tag('div'):addClass('tr-date'):wikitext(formatDateRange(startDate, endDate)) | row:tag('div'):addClass('tr-date'):wikitext(formatDateRange(startDate, endDate)) | ||
| Line 249: | Line 416: | ||
info:tag('div'):addClass('tr-name'):wikitext('[[' .. page .. '|' .. name .. ']]') | info:tag('div'):addClass('tr-name'):wikitext('[[' .. page .. '|' .. name .. ']]') | ||
if args.organizer then info:tag('div'):addClass('tr-org'):wikitext(args.organizer) end | if args.organizer then info:tag('div'):addClass('tr-org'):wikitext(args.organizer) end | ||
row:tag('div'):addClass('tr-prize'):wikitext(prizeDisplay) | |||
return tostring(row) | return tostring(row) | ||
end | |||
-- ============================================================ | |||
-- MAIN 5: QUERY | |||
-- ============================================================ | |||
function p.mainQuery(frame) | |||
local args = frame.args | |||
local offset = tonumber(args.offset) or 0 | |||
local limit = 50 | |||
local targetGame = args.game or "BGMI" | |||
local whereClause = "start_date IS NOT NULL" | |||
if targetGame ~= "All" then whereClause = whereClause .. string.format(' AND game="%s"', targetGame) end | |||
local results = cargo.query('Tournaments', '_pageName, start_date, end_date, tier, prize_pool, winner, organizer, image, image_dark', { | |||
where = whereClause, orderBy = 'start_date DESC', limit = limit, offset = offset | |||
}) | |||
local container = html.create('div'):addClass('tournament-list-wrapper') | |||
if not results or #results == 0 then | |||
return '<div style="padding:40px; text-align:center; color:#64748b;">No tournaments found.</div>' | |||
end | |||
for _, row in ipairs(results) do | |||
local rowArgs = { | |||
Page = row._pageName, | |||
start_date = row.start_date, | |||
end_date = row.end_date, | |||
tier = row.tier, | |||
prize_pool = row.prize_pool, | |||
winner = row.winner, | |||
organizer = row.organizer, | |||
image = row.image, | |||
image_dark = row.image_dark | |||
} | |||
container:wikitext(p.listRow({ args = rowArgs })) | |||
end | |||
-- Pagination | |||
local navDiv = container:tag('div'):addClass('pagination-controls') | |||
if offset > 0 then | |||
local prevUrl = mw.uri.fullUrl(mw.title.getCurrentTitle().text, {offset=math.max(0, offset - limit)}) | |||
navDiv:tag('span'):addClass('page-btn'):wikitext('[' .. tostring(prevUrl) .. ' « Newer Events]') | |||
end | |||
if #results == limit then | |||
local nextUrl = mw.uri.fullUrl(mw.title.getCurrentTitle().text, {offset=offset + limit}) | |||
navDiv:tag('span'):addClass('page-btn'):wikitext('[' .. tostring(nextUrl) .. ' Older Events »]') | |||
end | |||
return tostring(container) | |||
end | end | ||
return p | return p | ||
Latest revision as of 15:45, 14 March 2026
Documentation for this module may be created at Module:Tournament/doc
local p = {}
local html = mw.html
local cargo = mw.ext.cargo
local lang = mw.getContentLanguage()
local function sqlEscape(s)
if not s then return "" end
s = s:gsub("\\", "\\\\")
s = s:gsub("'", "\\'")
return s
end
-- ============================================================
-- HELPER FUNCTIONS
-- ============================================================
-- Indian Format (1,00,000)
local function formatINR(amount)
if not amount then return "0" end
local n = tostring(math.floor(tonumber(amount) or 0))
if #n <= 3 then return n end
local last3 = n:sub(-3)
local rest = n:sub(1, -4)
-- Indian format: group remaining digits in pairs (from right)
local formattedRest = rest:reverse():gsub("(%d%d)", "%1,"):reverse():gsub("^,", "")
return formattedRest .. "," .. last3
end
local function formatStd(amount)
if not amount then return "0" end
local n = tostring(math.floor(tonumber(amount) or 0))
return n:reverse():gsub("(%d%d%d)", "%1,"):reverse():gsub("^,", "")
end
-- Returns: { displayHTML, dbValue (INR) }
local function processMoney(amount, currency, rate)
if not amount or amount == "" or amount == "0" then
return { display = "TBD", dbValue = 0 }
end
local val = tonumber(amount) or 0
local r = tonumber(rate) or 1
local symbol = currency or "₹"
-- Clean the amount string (remove commas if passed by accident)
-- FIX: Extra parens around gsub to discard the 'count' return value
-- Fix (match Module:PrizePool's pattern):
if type(amount) == "string" then
val = tonumber((amount:gsub(",", ""))) or 0
else
val = tonumber(amount) or 0
end
if symbol == "₹" or symbol == "INR" or symbol == "Rs" then
local inrVal = val
local usdVal = val * r
local display = "₹ " .. formatINR(inrVal)
if r ~= 1 then
display = display .. '<br><span style="font-size:0.8em; opacity:0.7">($ ' .. formatStd(usdVal) .. ')</span>'
end
return { display = display, dbValue = inrVal }
else
local localVal = val
local inrVal = val * r
local display = symbol .. " " .. formatStd(localVal)
display = display .. '<br><span style="font-size:0.8em; opacity:0.7">(₹ ' .. formatINR(inrVal) .. ')</span>'
return { display = display, dbValue = inrVal }
end
end
local function formatDateRange(startStr, endStr)
if not startStr or startStr == "" then return "TBA" end
if endStr and endStr ~= "" and endStr ~= startStr then
local s = lang:formatDate('d M', startStr)
local e = lang:formatDate('d M, Y', endStr)
return s .. " – " .. e
else
return lang:formatDate('d M, Y', startStr)
end
end
local function getCleanTitle(pageLink)
if not pageLink then return "" end
local title = mw.title.new(pageLink)
if title then return title.subpageText end
return pageLink
end
local function getSmallTeamLogo(teamName)
if not teamName or teamName == "" then return "" end
local fileName = teamName .. '.png'
if mw.title.new('File:' .. fileName).exists then
return '[[File:' .. fileName .. '|25px|link=' .. teamName .. '|class=team-logo-small]] '
end
return ''
end
local function getTeamFromPrizeMoney(pageName, tournamentName, placeType)
if not cargo then return nil end
local placeClause = ""
if placeType == "1" then
placeClause = '(placement="1" OR placement="1st" OR placement="Winner" OR placement="Champion")'
elseif placeType == "2" then
placeClause = '(placement="2" OR placement="2nd" OR placement="Runner-up" OR placement="Runner Up")'
else
return nil
end
local pageClause = ""
if tournamentName and tournamentName ~= "" then
pageClause = string.format('(tournament="%s" OR _pageName="%s" OR _pageName LIKE "%%%s%%")', tournamentName, pageName, tournamentName)
else
pageClause = string.format('(_pageName="%s" OR _pageName LIKE "%s/%%")', pageName, pageName)
end
local results = cargo.query('PrizeMoney', 'team', {
where = pageClause .. ' AND ' .. placeClause,
limit = 1
})
if results and #results > 0 then return results[1].team end
return nil
end
local function getSocials(args)
local container = html.create('div'):addClass('fib-socials')
local hasSocials = false
local platforms = {
{arg='instagram', file='Icon_instagram.png'},
{arg='twitter', file='Icon_twitter.png'},
{arg='youtube', file='Icon_youtube.png'},
{arg='discord', file='Icon_discord.png'},
{arg='facebook', file='Icon_facebook.png'},
{arg='website', file='Icon_website.png'}
}
for _, p in ipairs(platforms) do
if args[p.arg] and args[p.arg] ~= "" then
hasSocials = true
container:wikitext('[[File:' .. p.file .. '|24px|link=' .. args[p.arg] .. '|class=social-img]]')
end
end
if hasSocials then return tostring(container) else return "" end
end
local function getSeriesNav(currentSeries, currentValue)
if not currentSeries or not currentValue or not cargo then return "" end
local nav = html.create('div'):addClass('fib-nav')
local prevQuery = cargo.query('Tournaments', '_pageName, series_season', {
where = string.format("series = '%s' AND series_value < %s", sqlEscape(currentSeries), tonumber(currentValue) or 0),
orderBy = 'series_value DESC', limit = 1
})
local prevBtn = nav:tag('div'):addClass('fib-nav-btn prev')
if prevQuery and #prevQuery > 0 then
prevBtn:wikitext('[[' .. prevQuery[1]._pageName .. '|« ' .. (prevQuery[1].series_season or "Previous") .. ']]')
else
prevBtn:css('opacity', '0.3'):wikitext('« Previous')
end
local nextQuery = cargo.query('Tournaments', '_pageName, series_season', {
where = string.format("series = '%s' AND series_value > %s", sqlEscape(currentSeries), tonumber(currentValue) or 0),
orderBy = 'series_value ASC', limit = 1
})
local nextBtn = nav:tag('div'):addClass('fib-nav-btn next')
if nextQuery and #nextQuery > 0 then
nextBtn:wikitext('[[' .. nextQuery[1]._pageName .. '|' .. (nextQuery[1].series_season or "Next") .. ' »]]')
else
nextBtn:css('opacity', '0.3'):wikitext('Next »')
end
return tostring(nav)
end
local function getInfoboxLogo(pageName, image, imageDark)
local lightFile = (image ~= "" and image) or (pageName .. '.png')
local darkFile = imageDark
if not darkFile or darkFile == "" then
local ext = lightFile:match("^.+(%..+)$") or ".png"
local name = lightFile:gsub("%..+$", "")
darkFile = name .. "_dark" .. ext
end
local hasLight = mw.title.new('File:' .. lightFile).exists
local hasDark = mw.title.new('File:' .. darkFile).exists
local container = html.create('div'):addClass('fib-image')
local lSpan = container:tag('span'):addClass('logo-lightmode')
if hasLight then lSpan:wikitext('[[File:' .. lightFile .. '|250px]]') else lSpan:wikitext('[[File:Shield_team.png|150px]]') end
local dSpan = container:tag('span'):addClass('logo-darkmode')
if hasDark then dSpan:wikitext('[[File:' .. darkFile .. '|250px]]') elseif hasLight then dSpan:wikitext('[[File:' .. lightFile .. '|250px]]') else dSpan:wikitext('[[File:Shield_team_dark.png|150px]]') end
return tostring(container)
end
local function getTourneyLogo(imageFile, darkImageFile)
local container = html.create('div'):addClass('tr-event-logo')
if imageFile and imageFile ~= "" then
local darkFile = darkImageFile
if not darkFile or darkFile == "" then
local ext = imageFile:match("^.+(%..+)$") or ".png"
local name = imageFile:gsub("%..+$", "")
darkFile = name .. "_dark" .. ext
end
local hasDark = mw.title.new('File:' .. darkFile).exists
container:tag('span'):addClass('logo-lightmode'):wikitext('[[File:' .. imageFile .. '|40px|link=]]')
local dSpan = container:tag('span'):addClass('logo-darkmode')
if hasDark then dSpan:wikitext('[[File:' .. darkFile .. '|40px|link=]]') else dSpan:wikitext('[[File:' .. imageFile .. '|40px|link=]]') end
else
container:wikitext('<i class="fa-solid fa-trophy" style="color:var(--text-muted); opacity:0.3;"></i>')
end
return tostring(container)
end
local function getTierClass(tier)
if not tier then return "tier-misc" end
local t = tier:lower()
if string.find(t, "s") and string.find(t, "tier") then return "tier-s" end
if string.find(t, "a") and string.find(t, "tier") then return "tier-a" end
if string.find(t, "b") and string.find(t, "tier") then return "tier-b" end
if string.find(t, "c") and string.find(t, "tier") then return "tier-c" end
return "tier-misc"
end
-- ============================================================
-- MAIN 1: TOURNAMENT INFOBOX
-- ============================================================
function p.infobox(frame)
local args = frame:getParent().args
local page = args.name or mw.title.getCurrentTitle().text
local cleanName = mw.title.getCurrentTitle().subpageText
local dbPageName = mw.title.getCurrentTitle().prefixedText
local prizeRaw = args.prize_pool or args.prizepool
local currency = args.currency or "₹"
local rate = args.rate or 1
-- Process Money for Display & Database
local moneyData = processMoney(prizeRaw, currency, rate)
local finalWinner = args.winner
if not finalWinner or finalWinner == "" then
finalWinner = getTeamFromPrizeMoney(dbPageName, cleanName, "1")
end
local finalRunnerUp = args.runner_up
if not finalRunnerUp or finalRunnerUp == "" then
finalRunnerUp = getTeamFromPrizeMoney(dbPageName, cleanName, "2")
end
if mw.ext.cargo and mw.ext.cargo.store then
mw.ext.cargo.store('Tournaments', {
_pageName = dbPageName,
name = args.name or cleanName,
series = args.series,
series_season = args.series_season,
series_value = args.series_value,
organizer = args.organizer,
sponsor = args.sponsor,
game = args.game or "BGMI",
mode = args.mode,
type = args.type,
tier = args.tier,
device = args.device,
location = args.location,
venue = args.venue,
prize_pool = moneyData.dbValue, -- STORES INR INTEGER
start_date = args.start_date,
end_date = args.end_date,
winner = finalWinner,
runner_up = finalRunnerUp,
image = args.image,
image_dark = args.image_dark,
instagram = args.instagram,
twitter = args.twitter,
youtube = args.youtube,
discord = args.discord,
facebook = args.facebook,
website = args.website
})
end
local root = html.create('div'):addClass('flat-infobox')
local header = root:tag('div'):addClass('fib-header')
header:tag('div'):addClass('fib-title'):wikitext(args.name or cleanName)
root:wikitext(getInfoboxLogo(page, args.image, args.image_dark))
local grid1 = root:tag('div'):addClass('fib-grid')
grid1:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Event Tier'):done():tag('div'):addClass('fib-value-sm'):wikitext(args.tier or 'Unranked'):done()
grid1:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Type'):done():tag('div'):addClass('fib-value-sm'):wikitext(args.type or 'Online'):done()
local grid2 = root:tag('div'):addClass('fib-grid')
grid2:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Mode'):done():tag('div'):addClass('fib-value-sm'):wikitext(args.mode or 'TBD'):done()
grid2:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Location'):done():tag('div'):addClass('fib-value-sm'):wikitext(args.location or 'India'):done()
if prizeRaw then
root:tag('div'):addClass('fib-prize')
:tag('div'):addClass('fib-label-sm'):wikitext('Total Prize Pool'):done()
:tag('div'):addClass('fib-prize-val'):wikitext(moneyData.display):done()
end
local list = root:tag('div'):addClass('fib-list')
local function addRow(label, value)
if value and value ~= "" then
list:tag('div'):addClass('fib-row'):tag('div'):addClass('fib-label'):wikitext(label):done():tag('div'):addClass('fib-data'):wikitext(value):done()
end
end
addRow('Series', args.series and '[[' .. args.series .. ']]')
if args.series_season then addRow('Season', args.series_season) end
addRow('Organizer', args.organizer)
addRow('Sponsors', args.sponsor)
addRow('Venue', args.venue)
addRow('Dates', formatDateRange(args.start_date, args.end_date))
addRow('Device', args.device)
if finalWinner then addRow('Winner', "'''[[" .. finalWinner .. "]]'''") end
if finalRunnerUp then addRow('Runner Up', "'''[[" .. finalRunnerUp .. "]]'''") end
root:wikitext(getSocials(args))
if args.series and args.series_value then root:wikitext(getSeriesNav(args.series, args.series_value)) end
return tostring(root)
end
-- ============================================================
-- MAIN 2: TABLE ROW (Connects to List)
-- ============================================================
function p.tableRow(frame)
local args = frame.args
local page = args.Page or ""
local name = getCleanTitle(page)
local startDate = args.start_date or ""
local endDate = args.end_date or ""
local tier = args.tier or "-"
local prizeVal = args.prize_pool or 0
-- Format prize for list (Simple INR display for consistency in tables)
local prizeDisplay = "₹ " .. formatINR(prizeVal)
local winner = args.winner
if not winner or winner == "" then winner = getTeamFromPrizeMoney(page, name, "1") end
local runnerUp = args.runner_up
if not runnerUp or runnerUp == "" then runnerUp = getTeamFromPrizeMoney(page, name, "2") end
local row = html.create('tr')
row:tag('td'):attr('data-label', 'Date'):attr('data-sort-value', startDate):tag('div'):addClass('tr-date'):wikitext(formatDateRange(startDate, endDate))
row:tag('td'):attr('data-label', 'Tier'):css('text-align', 'center'):tag('span'):addClass('tier-badge'):addClass(getTierClass(tier)):wikitext(tier)
row:tag('td'):attr('data-label', 'Tournament'):css('font-weight', 'bold'):wikitext('[[' .. page .. '|' .. name .. ']]')
row:tag('td'):attr('data-label', 'Location'):wikitext(args.location or '-')
row:tag('td'):attr('data-label', 'Prize'):attr('data-sort-value', prizeVal):addClass('tr-prize'):css('text-align', 'right'):wikitext(prizeDisplay)
local winCell = row:tag('td'):attr('data-label', 'Winner'):addClass('cell-winner')
if winner and winner ~= "" then winCell:wikitext(getSmallTeamLogo(winner) .. '[[' .. winner .. ']]') else winCell:css('text-align', 'center'):wikitext('-') end
local runCell = row:tag('td'):attr('data-label', 'Runner Up')
if runnerUp and runnerUp ~= "" then runCell:wikitext(getSmallTeamLogo(runnerUp) .. '[[' .. runnerUp .. ']]') else runCell:css('text-align', 'center'):wikitext('-') end
return tostring(row)
end
-- ============================================================
-- MAIN 3: LIST ROW (Card Style)
-- ============================================================
function p.listRow(frame)
local args = frame.args
local page = args.Page or ""
local name = getCleanTitle(page)
local startDate = args.start_date or ""
local endDate = args.end_date or ""
local tier = args.tier or ""
local image = args.image or ""
local imageDark = args.image_dark or ""
local prizeVal = args.prize_pool or 0
local prizeDisplay = "₹ " .. formatINR(prizeVal)
local winner = args.winner
if not winner or winner == "" then winner = getTeamFromPrizeMoney(page, name, "1") end
local row = html.create('div'):addClass('tourney-row')
if tier ~= "" then row:addClass('row-' .. getTierClass(tier)) end
row:tag('div'):addClass('tr-date'):wikitext(formatDateRange(startDate, endDate))
row:tag('div'):addClass('tr-event-logo-col'):wikitext(getTourneyLogo(image, imageDark))
local info = row:tag('div'):addClass('tr-info')
local nameRow = info:tag('div'):addClass('tr-name-row')
nameRow:tag('span'):addClass('tr-name'):wikitext('[[' .. page .. '|' .. name .. ']]')
if tier ~= "" then nameRow:tag('span'):addClass('tier-badge'):addClass(getTierClass(tier)):css('margin-left','8px'):wikitext(tier) end
if args.organizer then info:tag('div'):addClass('tr-org'):wikitext(args.organizer) end
local winDiv = row:tag('div'):addClass('tr-winner mobile-hide')
if winner and winner ~= "" then winDiv:wikitext("🏆 " .. winner) else winDiv:tag('span'):css('opacity', '0.3'):wikitext('-') end
row:tag('div'):addClass('tr-prize'):wikitext(prizeDisplay)
return tostring(row)
end
-- ============================================================
-- MAIN 4: LIST ROW MAIN (Compact)
-- ============================================================
function p.listRowMain(frame)
local args = frame.args
local page = args.Page or ""
local name = getCleanTitle(page)
local startDate = args.start_date or ""
local endDate = args.end_date or ""
local image = args.image or ""
local imageDark = args.image_dark or ""
local prizeVal = args.prize_pool or 0
local prizeDisplay = "₹ " .. formatINR(prizeVal)
local row = html.create('div'):addClass('tourney-row tr-compact')
row:tag('div'):addClass('tr-date'):wikitext(formatDateRange(startDate, endDate))
row:tag('div'):addClass('tr-event-logo-col'):wikitext(getTourneyLogo(image, imageDark))
local info = row:tag('div'):addClass('tr-info')
info:tag('div'):addClass('tr-name'):wikitext('[[' .. page .. '|' .. name .. ']]')
if args.organizer then info:tag('div'):addClass('tr-org'):wikitext(args.organizer) end
row:tag('div'):addClass('tr-prize'):wikitext(prizeDisplay)
return tostring(row)
end
-- ============================================================
-- MAIN 5: QUERY
-- ============================================================
function p.mainQuery(frame)
local args = frame.args
local offset = tonumber(args.offset) or 0
local limit = 50
local targetGame = args.game or "BGMI"
local whereClause = "start_date IS NOT NULL"
if targetGame ~= "All" then whereClause = whereClause .. string.format(' AND game="%s"', targetGame) end
local results = cargo.query('Tournaments', '_pageName, start_date, end_date, tier, prize_pool, winner, organizer, image, image_dark', {
where = whereClause, orderBy = 'start_date DESC', limit = limit, offset = offset
})
local container = html.create('div'):addClass('tournament-list-wrapper')
if not results or #results == 0 then
return '<div style="padding:40px; text-align:center; color:#64748b;">No tournaments found.</div>'
end
for _, row in ipairs(results) do
local rowArgs = {
Page = row._pageName,
start_date = row.start_date,
end_date = row.end_date,
tier = row.tier,
prize_pool = row.prize_pool,
winner = row.winner,
organizer = row.organizer,
image = row.image,
image_dark = row.image_dark
}
container:wikitext(p.listRow({ args = rowArgs }))
end
-- Pagination
local navDiv = container:tag('div'):addClass('pagination-controls')
if offset > 0 then
local prevUrl = mw.uri.fullUrl(mw.title.getCurrentTitle().text, {offset=math.max(0, offset - limit)})
navDiv:tag('span'):addClass('page-btn'):wikitext('[' .. tostring(prevUrl) .. ' « Newer Events]')
end
if #results == limit then
local nextUrl = mw.uri.fullUrl(mw.title.getCurrentTitle().text, {offset=offset + limit})
navDiv:tag('span'):addClass('page-btn'):wikitext('[' .. tostring(nextUrl) .. ' Older Events »]')
end
return tostring(container)
end
return p