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

Module:Tournament: Difference between revisions

From eSportsAmaze
No edit summary
No edit summary
 
(32 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:
-- ============================================================
-- ============================================================


-- Helper: Currency Formatter (Indian Format)
-- Indian Format (1,00,000)
local function formatCurrency(amount)
local function formatINR(amount)
     if not amount or amount == "" or amount == "0" then return "TBD" end
     if not amount then return "0" end
   
     local n = tostring(math.floor(tonumber(amount) or 0))
    -- Check for currency symbol ($ or ₹)
     if #n <= 3 then return n end
     local currency = "₹"
    if string.find(amount, "%$") then currency = "$" end
   
    -- Clean string
    local n = tostring(amount):gsub(",", ""):gsub("₹", ""):gsub("%$", "")
   
    -- Format Logic
     if #n <= 3 then return currency .. " " .. n end
   
     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 formattedRest:sub(1, 1) == "," then formattedRest = formattedRest:sub(2) end
     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 "₹"
      
      
     return '<span class="pz-prize">' .. currency .. " " .. formattedRest .. "," .. last3 .. '</span>'
     -- 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
end


-- Helper: Get Clean Title (Removes "BGMI/Tournaments/" path)
local function getCleanTitle(pageLink)
local function getCleanTitle(pageLink)
     if not pageLink then return "" end
     if not pageLink then return "" end
     local title = mw.title.new(pageLink)
     local title = mw.title.new(pageLink)
     if title then  
     if title then return title.subpageText end
         return title.subpageText -- Returns only text after the last "/"
    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
     end
     return pageLink
     return ''
end
end


-- Helper: Logo Logic (Light/Dark/Shield)
local function getTeamFromPrizeMoney(pageName, tournamentName, placeType)
local function getLogo(pageName, image, imageDark)
     if not cargo then return nil end
    local lightFile = (image ~= "" and image) or (pageName .. '.png')
    local darkFile = (imageDark ~= "" and imageDark) or (pageName .. '_dark.png')
   
    local hasLight = mw.title.new('File:' .. lightFile).exists
     local hasDark = mw.title.new('File:' .. darkFile).exists
   
    local container = html.create('div'):addClass('infobox-image')
      
      
     -- Light Mode
     local placeClause = ""
     local lDiv = container:tag('div'):addClass('logo-lightmode')
     if placeType == "1" then
     if hasLight then
        placeClause = '(placement="1" OR placement="1st" OR placement="Winner" OR placement="Champion")'
         lDiv:wikitext('[[File:' .. lightFile .. '|200px]]')
     elseif placeType == "2" then
         placeClause = '(placement="2" OR placement="2nd" OR placement="Runner-up" OR placement="Runner Up")'
     else
     else
         lDiv:wikitext('[[File:Shield_team.png|200px]]')
         return nil
     end
     end
      
      
    -- Dark Mode
     local pageClause = ""
     local dDiv = container:tag('div'):addClass('logo-darkmode')
     if tournamentName and tournamentName ~= "" then
     if hasDark then
         pageClause = string.format('(tournament="%s" OR _pageName="%s" OR _pageName LIKE "%%%s%%")', tournamentName, pageName, tournamentName)
         dDiv:wikitext('[[File:' .. darkFile .. '|200px]]')
    elseif hasLight then
        dDiv:wikitext('[[File:' .. lightFile .. '|200px]]')
     else
     else
         dDiv:wikitext('[[File:Shield_team_dark.png|200px]]')
         pageClause = string.format('(_pageName="%s" OR _pageName LIKE "%s/%%")', pageName, pageName)
     end
     end
      
      
     return container
     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
end


-- Helper: Small Logo for Lists
local function getSeriesNav(currentSeries, currentValue)
local function getSmallLogo(teamName)
     if not currentSeries or not currentValue or not cargo then return "" end
     if not teamName or teamName == "" then return "" end
     local nav = html.create('div'):addClass('fib-nav')
     local clean = teamName:gsub("'", "")
    local light = clean .. '.png'
    local dark = clean .. '_dark.png'
   
    local container = html.create('span'):addClass('tr-logo')
      
      
     if mw.title.new('File:' .. light).exists then
     local prevQuery = cargo.query('Tournaments', '_pageName, series_season', {
         container:wikitext('[[File:' .. light .. '|link=|class=logo-lightmode]]')
        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
     else
         container:wikitext('[[File:Shield_team.png|link=|class=logo-lightmode]]')
         prevBtn:css('opacity', '0.3'):wikitext('« Previous')
     end
     end
      
      
     if mw.title.new('File:' .. dark).exists then
     local nextQuery = cargo.query('Tournaments', '_pageName, series_season', {
         container:wikitext('[[File:' .. dark .. '|link=|class=logo-darkmode]]')
        where = string.format("series = '%s' AND series_value > %s", sqlEscape(currentSeries), tonumber(currentValue) or 0),
     elseif mw.title.new('File:' .. light).exists then
         orderBy = 'series_value ASC', limit = 1
         container:wikitext('[[File:' .. light .. '|link=|class=logo-darkmode]]')
    })
     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
         container:wikitext('[[File:Shield_team_dark.png|link=|class=logo-darkmode]]')
         nextBtn:css('opacity', '0.3'):wikitext('Next »')
     end
     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)
     return tostring(container)
end
end


-- Helper: Date Ranger (Jan 20 – Jan 25, 2026)
local function getTourneyLogo(imageFile, darkImageFile)
local function formatDateRange(startStr, endStr)
    local container = html.create('div'):addClass('tr-event-logo')
     if not startStr or startStr == "" then return "TBA" end
     if imageFile and imageFile ~= "" then
   
        local darkFile = darkImageFile
    -- If there is an end date
        if not darkFile or darkFile == "" then
    if endStr and endStr ~= "" and endStr ~= startStr then
            local ext = imageFile:match("^.+(%..+)$") or ".png"
         local s = lang:formatDate('d M', startStr)
            local name = imageFile:gsub("%..+$", "")
         local e = lang:formatDate('d M, Y', endStr)
            darkFile = name .. "_dark" .. ext
         return s .. " –<br>" .. e -- <br> helps mobile layout
        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
     else
         -- Single date only
         container:wikitext('<i class="fa-solid fa-trophy" style="color:var(--text-muted); opacity:0.3;"></i>')
        return lang:formatDate('d M, Y', startStr)
     end
     end
    return tostring(container)
end
end


-- Helper: Add Infobox Row
local function getTierClass(tier)
local function addRow(container, label, value)
     if not tier then return "tier-misc" end
     if value and value ~= "" then
    local t = tier:lower()
        container:tag('div'):addClass('infobox-row')
    if string.find(t, "s") and string.find(t, "tier") then return "tier-s" end
            :tag('div'):addClass('infobox-label'):wikitext(label):done()
    if string.find(t, "a") and string.find(t, "tier") then return "tier-a" end
            :tag('div'):addClass('infobox-data'):wikitext(value):done()
    if string.find(t, "b") and string.find(t, "tier") then return "tier-b" end
     end
    if string.find(t, "c") and string.find(t, "tier") then return "tier-c" end
     return "tier-misc"
end
end


-- ============================================================
-- ============================================================
-- MAIN 1: TOURNAMENT INFOBOX (Preserved)
-- MAIN 1: TOURNAMENT INFOBOX
-- ============================================================
-- ============================================================
function p.infobox(frame)
function p.infobox(frame)
Line 126: 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 dbPageName = mw.title.getCurrentTitle().prefixedText
      
      
     local root = html.create('div'):addClass('infobox')
     local prizeRaw = args.prize_pool or args.prizepool
    local currency = args.currency or "₹"
    local rate = args.rate or 1
      
      
     -- Header
     -- Process Money for Display & Database
     root:tag('div'):addClass('infobox-header'):wikitext(args.name or cleanName)
     local moneyData = processMoney(prizeRaw, currency, rate)
      
      
     -- Logo
     local finalWinner = args.winner
     root:node(getLogo(page, args.image, args.image_dark))
     if not finalWinner or finalWinner == "" then
        finalWinner = getTeamFromPrizeMoney(dbPageName, cleanName, "1")
    end
      
      
     -- Data Container
     local finalRunnerUp = args.runner_up
     local data = root:tag('div'):addClass('infobox-data-container')
     if not finalRunnerUp or finalRunnerUp == "" then
        finalRunnerUp = getTeamFromPrizeMoney(dbPageName, cleanName, "2")
    end
      
      
     if args.series then
     if mw.ext.cargo and mw.ext.cargo.store then
         addRow(data, "Series", '[[' .. args.series .. ']]')
         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
     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()
      
      
     addRow(data, "Organizer", args.organizer)
     if prizeRaw then
    addRow(data, "Type", args.type)
        root:tag('div'):addClass('fib-prize')
    addRow(data, "Location", args.location)
            :tag('div'):addClass('fib-label-sm'):wikitext('Total Prize Pool'):done()
            :tag('div'):addClass('fib-prize-val'):wikitext(moneyData.display):done()
    end
      
      
     -- Use formatting but strip the span for infobox text compatibility if needed
     local list = root:tag('div'):addClass('fib-list')
     -- or just pass raw if you prefer plain text. Using formatted here:
     local function addRow(label, value)
    if args.prizepool then
        if value and value ~= "" then
        addRow(data, "Prize Pool", formatCurrency(args.prizepool))
            list:tag('div'):addClass('fib-row'):tag('div'):addClass('fib-label'):wikitext(label):done():tag('div'):addClass('fib-data'):wikitext(value):done()
        end
     end
     end
      
      
     addRow(data, "Start Date", args.start_date)
     addRow('Series', args.series and '[[' .. args.series .. ']]')
     addRow(data, "End Date", args.end_date)
    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
      
      
     if args.winner then
    root:wikitext(getSocials(args))
        addRow(data, "Winner", "'''[[" .. args.winner .. "]]'''")
     if args.series and args.series_value then root:wikitext(getSeriesNav(args.series, args.series_value)) end
     end
     return tostring(root)
end


     return tostring(root)
-- ============================================================
-- 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
end


-- ============================================================
-- ============================================================
-- MAIN 2: LIST ROW (Tournaments Page) - NEW CARD DESIGN
-- MAIN 3: LIST ROW (Card Style)
-- ============================================================
-- ============================================================
function p.listRow(frame)
function p.listRow(frame)
     local args = frame.args -- Arguments passed via #invoke
     local args = frame.args
     local page = args.Page or ""
     local page = args.Page or ""
     local name = getCleanTitle(page)
     local name = getCleanTitle(page)
     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
      
      
    -- Container (Div instead of Tr)
     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-event-logo-col'):wikitext(getTourneyLogo(image, imageDark))
      
      
    -- 1. Date Column
    row:tag('div'):addClass('tr-date')
      :wikitext(formatDateRange(startDate, endDate))
       
    -- 2. Info Column (Name + Organizer)
     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
       
    -- 3. Winner Column
     local winDiv = row:tag('div'):addClass('tr-winner mobile-hide')
     local winDiv = row:tag('div'):addClass('tr-winner mobile-hide')
     if args.winner and args.winner ~= "" then
     if winner and winner ~= "" then winDiv:wikitext("🏆 " .. winner) else winDiv:tag('span'):css('opacity', '0.3'):wikitext('-') end
        winDiv:wikitext(getSmallLogo(args.winner))
      
        winDiv:tag('span'):wikitext(args.winner)
     row:tag('div'):addClass('tr-prize'):wikitext(prizeDisplay)
    else
        winDiv:tag('span'):addClass('dim-text'):wikitext('-')
    end
       
     -- 4. Prize Pool Column
     local prizeDiv = row:tag('div'):addClass('tr-prize')
    prizeDiv:wikitext(formatCurrency(args.prize_pool))
       
     return tostring(row)
     return tostring(row)
end
end


-- ============================================================
-- ============================================================
-- MAIN 3: LIST ROW MAIN (Homepage) - COMPACT CARD
-- MAIN 4: LIST ROW MAIN (Compact)
-- ============================================================
-- ============================================================
function p.listRowMain(frame)
function p.listRowMain(frame)
Line 212: Line 405:
     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 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')
     local row = html.create('div'):addClass('tourney-row tr-compact')
   
     row:tag('div'):addClass('tr-date'):wikitext(formatDateRange(startDate, endDate))
    -- 1. Date Column (Simplified)
     row:tag('div'):addClass('tr-event-logo-col'):wikitext(getTourneyLogo(image, imageDark))
     row:tag('div'):addClass('tr-date')
      :wikitext(formatDateRange(startDate, endDate))
       
     -- 2. Info Column
     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 .. ']]')
     info:tag('div'):addClass('tr-name'):wikitext('[[' .. page .. '|' .. name .. ']]')
     if args.organizer then
     if args.organizer then info:tag('div'):addClass('tr-org'):wikitext(args.organizer) end
        info:tag('div'):addClass('tr-org'):wikitext(args.organizer)
    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
     end
       
     if #results == limit then
     -- 3. Prize Pool Column
        local nextUrl = mw.uri.fullUrl(mw.title.getCurrentTitle().text, {offset=offset + limit})
    local prizeDiv = row:tag('div'):addClass('tr-prize')
        navDiv:tag('span'):addClass('page-btn'):wikitext('[' .. tostring(nextUrl) .. ' Older Events »]')
    prizeDiv:wikitext(formatCurrency(args.prize_pool))
    end
       
 
     return tostring(row)
     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