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
 
(14 intermediate revisions by the same user not shown)
Line 50: Line 50:


-- Helper: Fetch Winner/RunnerUp from PrizeMoney Table
-- Helper: Fetch Winner/RunnerUp from PrizeMoney Table
-- SUPER ROBUST VERSION: Checks for "1", "1st", and uses tournament name or page name
local function getTeamFromPrizeMoney(pageName, tournamentName, placeType)
local function getTeamFromPrizeMoney(pageName, tournamentName, placeType)
     if not cargo then return nil end
     if not cargo then return nil end
      
      
    -- 1. Define Placement Logic (Handles "1" vs "1st")
     local placeClause = ""
     local placeClause = ""
     if placeType == "1" then
     if placeType == "1" then
Line 64: Line 62:
     end
     end
      
      
    -- 2. Define Page/Tournament Logic
    -- Matches either the exact _pageName OR the 'tournament' column
     local pageClause = ""
     local pageClause = ""
     if tournamentName and tournamentName ~= "" then
     if tournamentName and tournamentName ~= "" then
Line 73: Line 69:
     end
     end
      
      
    -- 3. Run Query
     local results = cargo.query('PrizeMoney', 'team', {
     local results = cargo.query('PrizeMoney', 'team', {
         where = pageClause .. ' AND ' .. placeClause,
         where = pageClause .. ' AND ' .. placeClause,
Line 85: Line 80:
end
end


-- Social Icons (File Upload Method)
-- Social Icons
local function getSocials(args)
local function getSocials(args)
     local container = html.create('div'):addClass('fib-socials')
     local container = html.create('div'):addClass('fib-socials')
Line 172: Line 167:
     end
     end
     return tostring(container)
     return tostring(container)
end
-- Helper: Get Tier Class (NEW)
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 184: Line 190:
     local dbPageName = mw.title.getCurrentTitle().prefixedText
     local dbPageName = mw.title.getCurrentTitle().prefixedText
      
      
    -- 1. Determine Winner (Manual OR Auto-Fetch)
    -- We pass cleanName (e.g. "Battlegrounds Mobile India Series 2025") to match 'tournament' column
     local finalWinner = args.winner
     local finalWinner = args.winner
     if not finalWinner or finalWinner == "" then
     if not finalWinner or finalWinner == "" then
Line 191: Line 195:
     end
     end
      
      
    -- 2. Determine Runner Up (Manual OR Auto-Fetch)
     local finalRunnerUp = args.runner_up
     local finalRunnerUp = args.runner_up
     if not finalRunnerUp or finalRunnerUp == "" then
     if not finalRunnerUp or finalRunnerUp == "" then
Line 262: Line 265:
     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 finalWinner then addRow('Winner', "'''[[" .. finalWinner .. "]]'''") end
     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
     if args.series and args.series_value then
        root:wikitext(getSeriesNav(args.series, args.series_value))
    end
 
     return tostring(root)
     return tostring(root)
end
end


-- ============================================================
-- ============================================================
-- MAIN 2: TABLE ROW (Sortable Table on Tournaments Page)
-- MAIN 2: TABLE ROW (UPDATED FOR HYBRID DESIGN)
-- ============================================================
-- ============================================================
function p.tableRow(frame)
function p.tableRow(frame)
Line 284: Line 283:
     local endDate = args.end_date or ""
     local endDate = args.end_date or ""
     local prize = args.prize_pool or "0"
     local prize = args.prize_pool or "0"
    local tier = args.tier or "-"
      
      
     -- Auto-Fetch "1st" and "2nd" from Database if not provided
     -- Auto-Fetch Winners
     local winner = args.winner
     local winner = args.winner
     if not winner or winner == "" then
     if not winner or winner == "" then winner = getTeamFromPrizeMoney(page, name, "1") end
        winner = getTeamFromPrizeMoney(page, name, "1")
    end
      
      
     local runnerUp = args.runner_up
     local runnerUp = args.runner_up
     if not runnerUp or runnerUp == "" then
     if not runnerUp or runnerUp == "" then runnerUp = getTeamFromPrizeMoney(page, name, "2") end
        runnerUp = getTeamFromPrizeMoney(page, name, "2")
    end
      
      
     local row = html.create('tr')
     local row = html.create('tr')
      
      
     row:tag('td'):attr('data-sort-value', startDate):css('white-space', 'nowrap'):wikitext(formatDateRange(startDate, endDate))
     -- 1. Date (Desktop: Box, Mobile: Label)
     row:tag('td'):css('text-align', 'center'):wikitext(args.tier or '-')
     row:tag('td')
    row:tag('td'):css('font-weight', 'bold'):wikitext('[[' .. page .. '|' .. name .. ']]')
      :attr('data-label', 'Date')
    row:tag('td'):wikitext(args.location or '-')
      :attr('data-sort-value', startDate)
    row:tag('td'):attr('data-sort-value', prize):css('text-align', 'right'):wikitext(formatCurrency(prize))
      :tag('div'):addClass('tr-date'):wikitext(formatDateRange(startDate, endDate))
        
        
    -- 2. Tier (Color Coded Pill)
    row:tag('td')
      :attr('data-label', 'Tier')
      :css('text-align', 'center')
      :tag('span'):addClass('tier-badge'):addClass(getTierClass(tier)):wikitext(tier)
     
    -- 3. Tournament Name
    row:tag('td')
      :attr('data-label', 'Tournament')
      :css('font-weight', 'bold'):wikitext('[[' .. page .. '|' .. name .. ']]')
     
    -- 4. Location
    row:tag('td')
      :attr('data-label', 'Location')
      :wikitext(args.location or '-')
   
    -- 5. Prize
    row:tag('td')
      :attr('data-label', 'Prize')
      :attr('data-sort-value', prize)
      :addClass('tr-prize')
      :css('text-align', 'right')
      :wikitext(formatCurrency(prize))
     
    -- 6. Winner (Gold Background on Desktop)
     local winCell = row:tag('td')
     local winCell = row:tag('td')
        :attr('data-label', 'Winner')
        :addClass('cell-winner')
   
     if winner and winner ~= "" then
     if winner and winner ~= "" then
         winCell:wikitext(getSmallTeamLogo(winner) .. '[[' .. winner .. ']]')
         winCell:wikitext(getSmallTeamLogo(winner) .. '[[' .. winner .. ']]')
Line 311: Line 335:
     end
     end
      
      
     local runCell = row:tag('td')
    -- 7. Runner Up
     local runCell = row:tag('td'):attr('data-label', 'Runner Up')
     if runnerUp and runnerUp ~= "" then
     if runnerUp and runnerUp ~= "" then
         runCell:wikitext(getSmallTeamLogo(runnerUp) .. '[[' .. runnerUp .. ']]')
         runCell:wikitext(getSmallTeamLogo(runnerUp) .. '[[' .. runnerUp .. ']]')
Line 322: Line 347:


-- ============================================================
-- ============================================================
-- MAIN 3: LIST ROW
-- MAIN 3: LIST ROW (With Left Strip Logic)
-- ============================================================
-- ============================================================
function p.listRow(frame)
function p.listRow(frame)
Line 328: Line 353:
     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 endDate = args.end_date or ""
     local startDate = args.start_date or args.date or args.startdate or ""
     local endDate = args.end_date or args.enddate or ""
    local prize = args.prize_pool or args.prizepool or args.prize or "0"
    local tier = args.tier or ""
    local image = args.image or ""
    local imageDark = args.image_dark or ""
      
      
     local winner = args.winner
     local winner = args.winner
Line 336: Line 366:
     end
     end
      
      
    -- CREATE ROW: Removed inline background styles to allow CSS variables to work
     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
   
    -- Date Box
     row:tag('div'):addClass('tr-date'):wikitext(formatDateRange(startDate, endDate))
     row:tag('div'):addClass('tr-date'):wikitext(formatDateRange(startDate, endDate))
   
    -- Logo
    row:tag('div'):addClass('tr-event-logo-col'):wikitext(getTourneyLogo(image, imageDark))
   
    -- Info
     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
        local tierClass = getTierClass(tier)
        nameRow:tag('span'):addClass('tier-badge'):addClass(tierClass):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
      
      
    -- Winner
     local winDiv = row:tag('div'):addClass('tr-winner mobile-hide')
     local winDiv = row:tag('div'):addClass('tr-winner mobile-hide')
     if winner and winner ~= "" then  
     if winner and winner ~= "" then  
         winDiv:wikitext("🏆 " .. winner)  
         winDiv:wikitext("🏆 " .. winner)  
     else  
     else  
         winDiv:tag('span'):addClass('dim-text'):wikitext('-')  
         winDiv:tag('span'):css('opacity', '0.3'):wikitext('-')  
     end
     end
      
      
    -- Prize
     local prizeDiv = row:tag('div'):addClass('tr-prize')
     local prizeDiv = row:tag('div'):addClass('tr-prize')
     prizeDiv:wikitext(formatCurrency(args.prize_pool))
     prizeDiv:wikitext(formatCurrency(prize))
   
     return tostring(row)
     return tostring(row)
end
end
Line 374: Line 426:
     prizeDiv:wikitext(formatCurrency(args.prize_pool))
     prizeDiv:wikitext(formatCurrency(args.prize_pool))
     return tostring(row)
     return tostring(row)
end
-- ============================================================
-- MAIN 5: PAGINATED QUERY (Connects to listRow)
-- ============================================================
function p.mainQuery(frame)
    local args = frame.args
   
    -- 1. PAGINATION SETUP
    -- Get offset from URL (defaults to 0 if missing)
    local offset = tonumber(args.offset) or 0
    local limit = 50 -- Show 50 tournaments per page
   
    -- 2. BUILD QUERY
    local whereClause = "start_date IS NOT NULL"
   
    -- 3. RUN CARGO QUERY
    -- We select the exact fields required by your p.listRow function
    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
        }
    )
    -- 4. CONTAINER & EMPTY STATE
    local container = html.create('div'):addClass('tournament-list-wrapper')
   
    if not results or #results == 0 then
        if offset > 0 then
            -- User went too far, give a link back to start
            return '<div style="padding:40px; text-align:center; color:#64748b;">No more tournaments found.<br><br><b>[[' .. tostring(mw.uri.fullUrl(mw.title.getCurrentTitle().text)) .. '|Back to Start]]</b></div>'
        else
            return '<div style="padding:40px; text-align:center; color:#64748b;">No tournaments found in database.</div>'
        end
    end
   
    -- 5. RENDER ROWS (Using your Main 3 logic)
    for _, row in ipairs(results) do
        -- We structure the data exactly how p.listRow expects it
        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
        }
        -- Call your existing function
        container:wikitext(p.listRow({ args = rowArgs }))
    end
    -- 6. ADD PAGINATION BUTTONS
    local navDiv = container:tag('div'):addClass('pagination-controls')
   
    -- "Previous" Button (Hide if on first page)
    if offset > 0 then
        local prevOffset = math.max(0, offset - limit)
        local prevUrl = mw.uri.fullUrl(mw.title.getCurrentTitle().text, {offset=prevOffset})
        navDiv:tag('span'):addClass('page-btn')
              :wikitext('[' .. tostring(prevUrl) .. ' « Newer Events]')
    end
   
    -- "Next" Button (Hide if we have fewer results than the limit)
    if #results == limit then
        local nextOffset = offset + limit
        local nextUrl = mw.uri.fullUrl(mw.title.getCurrentTitle().text, {offset=nextOffset})
        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 09:17, 28 January 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()

-- ============================================================
-- HELPER FUNCTIONS
-- ============================================================

local function formatCurrency(amount)
    if not amount or amount == "" or amount == "0" then return "TBD" end
    local currency = "₹" 
    if string.find(amount, "%$") then currency = "$" end
    local n = tostring(amount):gsub(",", ""):gsub("₹", ""):gsub("%$", "")
    if #n <= 3 then return currency .. " " .. n end
    local last3 = n:sub(-3)
    local rest = n:sub(1, -4)
    local formattedRest = rest:reverse():gsub("(%d%d)", "%1,"):reverse()
    if formattedRest:sub(1, 1) == "," then formattedRest = formattedRest:sub(2) end
    return '<span class="pz-prize">' .. currency .. " " .. formattedRest .. "," .. last3 .. '</span>'
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

-- Helper: Small Team Logo
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

-- Helper: Fetch Winner/RunnerUp from PrizeMoney Table
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

-- Social Icons
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

-- Smart Series Navigation
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', currentSeries, currentValue),
        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', currentSeries, currentValue),
        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

-- Infobox Logo
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

-- List Logo
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

-- Helper: Get Tier Class (NEW)
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 prizeMoney = args.prize_pool or args.prizepool
    local dbPageName = mw.title.getCurrentTitle().prefixedText
    
    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 = prizeMoney,
            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 prizeMoney 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(formatCurrency(prizeMoney)):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 (UPDATED FOR HYBRID DESIGN)
-- ============================================================
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 prize = args.prize_pool or "0"
    local tier = args.tier or "-"
    
    -- Auto-Fetch Winners
    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')
    
    -- 1. Date (Desktop: Box, Mobile: Label)
    row:tag('td')
       :attr('data-label', 'Date')
       :attr('data-sort-value', startDate)
       :tag('div'):addClass('tr-date'):wikitext(formatDateRange(startDate, endDate))
       
    -- 2. Tier (Color Coded Pill)
    row:tag('td')
       :attr('data-label', 'Tier')
       :css('text-align', 'center')
       :tag('span'):addClass('tier-badge'):addClass(getTierClass(tier)):wikitext(tier)
       
    -- 3. Tournament Name
    row:tag('td')
       :attr('data-label', 'Tournament')
       :css('font-weight', 'bold'):wikitext('[[' .. page .. '|' .. name .. ']]')
       
    -- 4. Location
    row:tag('td')
       :attr('data-label', 'Location')
       :wikitext(args.location or '-')
    
    -- 5. Prize
    row:tag('td')
       :attr('data-label', 'Prize')
       :attr('data-sort-value', prize)
       :addClass('tr-prize')
       :css('text-align', 'right')
       :wikitext(formatCurrency(prize))
       
    -- 6. Winner (Gold Background on Desktop)
    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
    
    -- 7. Runner Up
    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 (With Left Strip Logic)
-- ============================================================
function p.listRow(frame)
    local args = frame.args
    local page = args.Page or ""
    local name = getCleanTitle(page)
    
    local startDate = args.start_date or args.date or args.startdate or ""
    local endDate = args.end_date or args.enddate or ""
    local prize = args.prize_pool or args.prizepool or args.prize or "0"
    local tier = args.tier or ""
    local image = args.image or ""
    local imageDark = args.image_dark or ""
    
    local winner = args.winner
    if not winner or winner == "" then
        winner = getTeamFromPrizeMoney(page, name, "1")
    end
    
    -- CREATE ROW: Removed inline background styles to allow CSS variables to work
    local row = html.create('div'):addClass('tourney-row')
    
    if tier ~= "" then
        row:addClass('row-' .. getTierClass(tier))
    end
    
    -- Date Box
    row:tag('div'):addClass('tr-date'):wikitext(formatDateRange(startDate, endDate))
    
    -- Logo
    row:tag('div'):addClass('tr-event-logo-col'):wikitext(getTourneyLogo(image, imageDark))
    
    -- Info
    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
        local tierClass = getTierClass(tier) 
        nameRow:tag('span'):addClass('tier-badge'):addClass(tierClass):css('margin-left','8px'):wikitext(tier)
    end
    
    if args.organizer then info:tag('div'):addClass('tr-org'):wikitext(args.organizer) end
    
    -- Winner
    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
    
    -- Prize
    local prizeDiv = row:tag('div'):addClass('tr-prize')
    prizeDiv:wikitext(formatCurrency(prize))
    
    return tostring(row)
end

-- ============================================================
-- MAIN 4: LIST ROW MAIN
-- ============================================================
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 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
    local prizeDiv = row:tag('div'):addClass('tr-prize')
    prizeDiv:wikitext(formatCurrency(args.prize_pool))
    return tostring(row)
end

-- ============================================================
-- MAIN 5: PAGINATED QUERY (Connects to listRow)
-- ============================================================
function p.mainQuery(frame)
    local args = frame.args
    
    -- 1. PAGINATION SETUP
    -- Get offset from URL (defaults to 0 if missing)
    local offset = tonumber(args.offset) or 0
    local limit = 50 -- Show 50 tournaments per page
    
    -- 2. BUILD QUERY
    local whereClause = "start_date IS NOT NULL"
    
    -- 3. RUN CARGO QUERY
    -- We select the exact fields required by your p.listRow function
    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 
        }
    )

    -- 4. CONTAINER & EMPTY STATE
    local container = html.create('div'):addClass('tournament-list-wrapper')
    
    if not results or #results == 0 then
        if offset > 0 then
             -- User went too far, give a link back to start
            return '<div style="padding:40px; text-align:center; color:#64748b;">No more tournaments found.<br><br><b>[[' .. tostring(mw.uri.fullUrl(mw.title.getCurrentTitle().text)) .. '|Back to Start]]</b></div>'
        else
            return '<div style="padding:40px; text-align:center; color:#64748b;">No tournaments found in database.</div>'
        end
    end
    
    -- 5. RENDER ROWS (Using your Main 3 logic)
    for _, row in ipairs(results) do
        -- We structure the data exactly how p.listRow expects it
        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
        }
        -- Call your existing function
        container:wikitext(p.listRow({ args = rowArgs }))
    end

    -- 6. ADD PAGINATION BUTTONS
    local navDiv = container:tag('div'):addClass('pagination-controls')
    
    -- "Previous" Button (Hide if on first page)
    if offset > 0 then
        local prevOffset = math.max(0, offset - limit)
        local prevUrl = mw.uri.fullUrl(mw.title.getCurrentTitle().text, {offset=prevOffset})
        navDiv:tag('span'):addClass('page-btn')
              :wikitext('[' .. tostring(prevUrl) .. ' « Newer Events]')
    end
    
    -- "Next" Button (Hide if we have fewer results than the limit)
    if #results == limit then
        local nextOffset = offset + limit
        local nextUrl = mw.uri.fullUrl(mw.title.getCurrentTitle().text, {offset=nextOffset})
        navDiv:tag('span'):addClass('page-btn')
              :wikitext('[' .. tostring(nextUrl) .. ' Older Events »]')
    end

    return tostring(container)
end

return p