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

Module:Team: Difference between revisions

From eSportsAmaze
Undo revision 2839 by Esportsamaze (talk)
Tag: Undo
No edit summary
Line 2: Line 2:
local cargo = mw.ext.cargo
local cargo = mw.ext.cargo
local html = mw.html
local html = mw.html
local currentTitle = mw.title.getCurrentTitle()


-- ============================================================
-- ============================================================
-- HELPER FUNCTIONS
-- HELPER FUNCTIONS (Logos, Socials, Currency)
-- ============================================================
-- ============================================================


-- Currency Formatter
local function formatCurrency(amount)
local function formatCurrency(amount)
     if not amount then return "0" end
     if not amount then return "0" end
Line 15: Line 15:
end
end


-- Social Icons Helper
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', file='Icon_instagram.png'},
         {arg='instagram', file='Icon_instagram.png'},
Line 28: Line 26:
         {arg='website',  file='Icon_website.png'}
         {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
Line 35: Line 32:
         end
         end
     end
     end
   
     if hasSocials then return tostring(container) else return "" end
     if hasSocials then return tostring(container) else return "" end
end
end


-- Infobox Logo Helper
local function getInfoboxLogo(teamName, image, imageDark)
local function getInfoboxLogo(teamName, image, imageDark)
     local lightFile = (image ~= "" and image) or (teamName .. '.png')
     local lightFile = (image ~= "" and image) or (teamName .. '.png')
     local darkFile = (imageDark ~= "" and imageDark) or (teamName .. '_dark.png')
     local darkFile = (imageDark ~= "" and imageDark) or (teamName .. '_dark.png')
   
     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')
     if hasLight then lSpan:wikitext('[[File:' .. lightFile .. '|220px]]') else lSpan:wikitext('[[File:Shield_team.png|180px]]') end
     if hasLight then lSpan:wikitext('[[File:' .. lightFile .. '|220px]]') else lSpan:wikitext('[[File:Shield_team.png|180px]]') end
   
     local dSpan = container:tag('span'):addClass('logo-darkmode')
     local dSpan = container:tag('span'):addClass('logo-darkmode')
     if hasDark then dSpan:wikitext('[[File:' .. darkFile .. '|220px]]') elseif hasLight then dSpan:wikitext('[[File:' .. lightFile .. '|220px]]') else dSpan:wikitext('[[File:Shield_team_dark.png|180px]]') end
     if hasDark then dSpan:wikitext('[[File:' .. darkFile .. '|220px]]') elseif hasLight then dSpan:wikitext('[[File:' .. lightFile .. '|220px]]') else dSpan:wikitext('[[File:Shield_team_dark.png|180px]]') end
   
     return tostring(container)
     return tostring(container)
end
end


-- ============================================================
-- ============================================================
-- MAIN 1: TEAM INFOBOX (STABLE VERSION)
-- MAIN 1: TEAM INFOBOX (Standard)
-- ============================================================
-- ============================================================
function p.infobox(frame)
function p.infobox(frame)
     local args = frame:getParent().args
     local args = frame:getParent().args
    -- Get clean name (GodLike Esports) instead of path
     local team = args.name or currentTitle.subpageText
     local team = args.name or mw.title.getCurrentTitle().subpageText
      
      
     local root = html.create('div'):addClass('flat-infobox')
     local root = html.create('div'):addClass('flat-infobox')
   
     root:tag('div'):addClass('fib-header'):tag('div'):addClass('fib-title'):wikitext(team)
    -- Header (Just like the old {{name|{{PAGENAME}}}})
     root:tag('div'):addClass('fib-header')
        :tag('div'):addClass('fib-title'):wikitext(team)
   
    -- Logo
     root:wikitext(getInfoboxLogo(team, args.image, args.image_dark))
     root:wikitext(getInfoboxLogo(team, args.image, args.image_dark))
      
      
    -- Calculations
     local earnings = "0"
     local earnings = "0"
     if cargo and cargo.query then
     if cargo and cargo.query then
         local tables = "PrizeMoney"
         local results = cargo.query("PrizeMoney", "SUM(prize)=total", { where = "team = '" .. team:gsub("'", "\\'") .. "' AND (player='' OR player IS NULL)" })
        local fields = "SUM(prize)=total"
         if results and #results > 0 and results[1].total then earnings = results[1].total end
        local queryArgs = {  
            where = "team = '" .. team:gsub("'", "\\'") .. "' AND (player='' OR player IS NULL)"
        }
        local results = cargo.query(tables, fields, queryArgs)
         if results and #results > 0 and results[1].total then
            earnings = results[1].total
        end
     end
     end
      
      
Line 95: Line 71:
     end
     end
      
      
    -- Status
     local statusColor = "#333"
     local statusColor = "#333"
     local statusText = args.status or "Active"
     local statusText = args.status or "Active"
     if args.status then
     if args.status then
         local s = args.status:lower()
         local s = args.status:lower()
         if s == "active" then statusColor = "#16a34a"
         if s == "active" then statusColor = "#16a34a" elseif s == "inactive" or s == "disbanded" then statusColor = "#dc2626" end
        elseif s == "inactive" or s == "disbanded" then statusColor = "#dc2626"
        end
     end
     end


    -- Grids
     local grid1 = root:tag('div'):addClass('fib-grid')
     local grid1 = root:tag('div'):addClass('fib-grid')
     grid1:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Status'):done():tag('div'):addClass('fib-value-sm'):css('color', statusColor):wikitext(statusText):done()
     grid1:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Status'):done():tag('div'):addClass('fib-value-sm'):css('color', statusColor):wikitext(statusText):done()
Line 114: Line 86:
     grid2:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Tag'):done():tag('div'):addClass('fib-value-sm'):wikitext(args.short_code or '-'):done()
     grid2:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Tag'):done():tag('div'):addClass('fib-value-sm'):wikitext(args.short_code or '-'):done()


    -- Earnings
     if earnings and tonumber(earnings) and tonumber(earnings) > 0 then
     if earnings and tonumber(earnings) and tonumber(earnings) > 0 then
         root:tag('div'):addClass('fib-prize')
         root:tag('div'):addClass('fib-prize'):tag('div'):addClass('fib-label-sm'):wikitext('Total Earnings'):done():tag('div'):addClass('fib-prize-val'):wikitext(formatCurrency(earnings)):done()
            :tag('div'):addClass('fib-label-sm'):wikitext('Total Earnings'):done()
            :tag('div'):addClass('fib-prize-val'):wikitext(formatCurrency(earnings)):done()
     end
     end
      
      
    -- List
     local list = root:tag('div'):addClass('fib-list')
     local list = root:tag('div'):addClass('fib-list')
     local function addRow(label, value)
     local function addRow(label, value)
         if value and value ~= "" then
         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
            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('Full Name', args.other_team)
     addRow('Full Name', args.other_team)
     if args.sponsors then addRow('Sponsors', args.sponsors:gsub(",", "<br>")) end
     if args.sponsors then addRow('Sponsors', args.sponsors:gsub(",", "<br>")) end
   
    -- Socials
     root:wikitext(getSocials(args))
     root:wikitext(getSocials(args))
    -- JUST RETURN THE INFOBOX (No Page Title Tricks)
     return tostring(root)
     return tostring(root)
end
end


-- ============================================================
-- ============================================================
-- MAIN 2: HISTORY (Preserved)
-- MAIN 2: HYBRID ACTIVE ROSTER (HERO GRID)
-- ============================================================
-- ============================================================
function p.history(frame)
function p.roster(frame)
     local args = frame:getParent().args
     local args = frame:getParent().args
     local team = args.team or mw.title.getCurrentTitle().subpageText
     local team = args.team or currentTitle.subpageText
     local results = cargo.query("StageStandings", "tournament, stage, totalpts, matchesplayed, wwcd, elimpts, lastmatchrank, result", {
   
         where = "team = '" .. team:gsub("'", "\\'") .. "'",
    -- 1. Fetch Database Players
         orderBy = "tournament DESC",
     local dbPlayers = {}
         limit = 50
    if cargo and cargo.query then
     })
        local tables = "Players"
        local fields = "id, real_name, role, nationality, image, _pageName"
         local where = "current_team = '" .. team:gsub("'", "\\'") .. "' AND status='Active'"
         local results = cargo.query(tables, fields, { where = where, orderBy = "role ASC" })
       
        for _, row in ipairs(results) do
            dbPlayers[row.id] = {
                id = row.id,
                name = row.real_name,
                role = row.role,
                flag = row.nationality,
                image = row.image,
                link = row._pageName,
                source = "auto"
            }
         end
     end
      
      
     local root = html.create('div')
     -- 2. Process Manual Inputs (Check for duplicates)
     root:wikitext('== Tournament Statistics ==')
     local finalRoster = {}
      
      
     if #results > 0 then
     -- Add DB players first
         local tbl = root:tag('table'):addClass('modern-history-table')
    for _, pData in pairs(dbPlayers) do
         local thead = tbl:tag('thead'):tag('tr')
        table.insert(finalRoster, pData)
         thead:tag('th'):wikitext('Tournament'); thead:tag('th'):wikitext('Stage'); thead:tag('th'):wikitext('Rank')
    end
         thead:tag('th'):wikitext('MP'); thead:tag('th'):wikitext('WWCD'); thead:tag('th'):wikitext('Elims'); thead:tag('th'):wikitext('Pts')
   
    -- Check manual args (player1, player2...)
    for i = 1, 10 do
        local mid = args['player' .. i]
        if mid and mid ~= "" then
            -- Only add if NOT in DB list
            if not dbPlayers[mid] then
                table.insert(finalRoster, {
                    id = mid,
                    name = args['name' .. i] or "",
                    role = args['role' .. i] or "Player",
                    flag = args['flag' .. i] or "India",
                    image = "", -- Manual entries usually have no image
                    link = mid, -- Redlink assumption
                    source = "manual"
                })
            end
         end
    end
   
    -- 3. Render the Grid (USING NEW CLASS NAMES)
    local grid = html.create('div'):addClass('hero-roster-grid')
   
    for _, player in ipairs(finalRoster) do
         local card = grid:tag('div'):addClass('hero-player-card')
          
        -- Header (Image Area)
        local header = card:tag('div'):addClass('hero-card-image')
        if player.image and player.image ~= "" then
            header:wikitext('[[File:' .. player.image .. '|link=' .. player.link .. ']]')
        else
            -- Fallback Silhouette
            header:wikitext('[[File:Player_Placeholder.png|link=' .. player.link .. ']]')
        end
       
        -- Info Body
        local body = card:tag('div'):addClass('hero-card-body')
          
        -- Role Pill
        local roleColor = "#64748b" -- Default Gray
        local r = (player.role or ""):lower()
        if r:find("igl") then roleColor = "#eab308" -- Gold
        elseif r:find("fragger") or r:find("entry") or r:find("assault") then roleColor = "#ef4444" -- Red
        elseif r:find("support") or r:find("medic") then roleColor = "#22c55e" -- Green
        elseif r:find("sniper") then roleColor = "#3b82f6" -- Blue
        elseif r:find("coach") then roleColor = "#a855f7" -- Purple
        end
          
          
         for _, row in ipairs(results) do
         body:tag('div'):addClass('hero-role-pill')
            local tr = tbl:tag('tr')
             :css('background-color', roleColor)
            local res = (row.result or ""):lower()
             :wikitext(player.role or "Player")
             if res == "q" or res == "qualified" then tr:css('background-color', '#E9FFF0')
             elseif res == "e" or res == "eliminated" then tr:css('background-color', '#FFE9E9') end
              
              
            tr:tag('td'):css('font-weight','bold'):wikitext('[[' .. row.tournament .. ']]')
        -- ID and Name
            tr:tag('td'):wikitext(row.stage)
        body:tag('div'):addClass('hero-player-id'):wikitext('[[' .. player.link .. '|' .. player.id .. ']]')
             tr:tag('td'):css('font-weight','bold'):css('text-align','center'):wikitext('#' .. (row.lastmatchrank or '?'))
        if player.name and player.name ~= "" then
            tr:tag('td'):css('text-align','center'):wikitext(row.matchesplayed)
             body:tag('div'):addClass('hero-player-name'):wikitext(player.name)
             tr:tag('td'):css('text-align','center'):wikitext(row.wwcd)
        end
            tr:tag('td'):css('text-align','center'):wikitext(row.elimpts)
       
            tr:tag('td'):css('text-align','center'):css('font-weight','800'):wikitext(row.totalpts)
        -- Flag
        if player.flag then
             body:tag('div'):addClass('hero-player-flag'):wikitext('[[File:Flag_' .. player.flag .. '.png|20px]] ' .. player.flag)
         end
         end
     else root:wikitext("''No tournament data found for this team.''") end
     end
     return tostring(root)
   
    return tostring(grid)
end
 
-- ============================================================
-- MAIN 3: FORMER PLAYERS LIST
-- ============================================================
function p.formerPlayers(frame)
    local args = frame:getParent().args
    local team = args.team or currentTitle.subpageText
   
    local results = cargo.query("Player_Former_Teams, Players",
        "Player_Former_Teams.player_id=id, Players.real_name=Name, Player_Former_Teams.role=role, Player_Former_Teams.join_date=join_date, Player_Former_Teams.leave_date=leave_date",
        {
            joinOn = "Player_Former_Teams.player_id = Players.id",
            where = "Player_Former_Teams.team = '" .. team:gsub("'", "\\'") .. "'",
            orderBy = "Player_Former_Teams.leave_date DESC"
        }
    )
   
    if not results or #results == 0 then
        return '<div style="font-style:italic; color:#64748b; padding:10px;">No former players recorded.</div>'
    end
   
    local tbl = html.create('table'):addClass('wikitable flat-table sortable'):css('width','100%')
    local h = tbl:tag('tr')
    h:tag('th'):wikitext('ID')
    h:tag('th'):wikitext('Name')
    h:tag('th'):wikitext('Role')
    h:tag('th'):wikitext('Join Date')
    h:tag('th'):wikitext('Leave Date')
   
    for _, row in ipairs(results) do
        local tr = tbl:tag('tr')
        tr:tag('td'):css('font-weight','bold'):wikitext('[[' .. row.id .. ']]')
        tr:tag('td'):wikitext(row.Name or "-")
        tr:tag('td'):wikitext(row.role or "-")
        tr:tag('td'):wikitext(row.join_date or "?")
        tr:tag('td'):wikitext(row.leave_date or "?")
    end
   
     return tostring(tbl)
end
end


-- ============================================================
-- ============================================================
-- MAIN 3: ACTIVE ROSTER (Preserved)
-- MAIN 4: STAT HEADER (KEY METRICS)
-- ============================================================
-- ============================================================
function p.activeRoster(frame)
function p.statHeader(frame)
     local args = frame:getParent().args
     local args = frame:getParent().args
     local team = args.team or mw.title.getCurrentTitle().subpageText
     local team = args.team or currentTitle.subpageText
     local results = cargo.query("Players", "id, real_name, role, nationality, image", { where = "current_team = '" .. team:gsub("'", "\\'") .. "'", orderBy = "role ASC, id ASC" })
   
    local earnings = "0"
     local results = cargo.query("PrizeMoney", "SUM(prize)=total", { where = "team = '" .. team:gsub("'", "\\'") .. "' AND (player='' OR player IS NULL)" })
    if results and #results > 0 and results[1].total then earnings = results[1].total end
      
      
     local root = html.create('table'):addClass('wikitable flat-table sortable'):css('width', '100%'):css('text-align', 'center')
     local rank = "N/A"
     local header = root:tag('tr')
    local rRes = cargo.query("Krafton_Rankings", "rank", { where = "name = '" .. team:gsub("'", "\\'") .. "' AND type='Team'", limit = 1 })
    header:tag('th'):wikitext('ID'); header:tag('th'):wikitext('Name'); header:tag('th'):wikitext('Role'); header:tag('th'):wikitext('Nationality')
     if rRes and #rRes > 0 then rank = "#" .. rRes[1].rank end
      
      
     if #results > 0 then
     local bar = html.create('div'):addClass('team-dashboard-bar')
        for _, row in ipairs(results) do
   
            local tr = root:tag('tr')
    local m1 = bar:tag('div'):addClass('tdb-item')
            local idCell = tr:tag('td'):css('text-align', 'left'):css('font-weight', 'bold')
    m1:tag('div'):addClass('tdb-label'):wikitext('Krafton Rank')
            if row.image and row.image ~= "" then idCell:wikitext('[[File:' .. row.image .. '|30px|link=' .. row.id .. ']] ') end
    local val1 = m1:tag('div'):addClass('tdb-value'):wikitext(rank)
            idCell:wikitext('[[' .. row.id .. ']]')
    if rank ~= "N/A" then val1:addClass('highlight-gold') end
            tr:tag('td'):wikitext(row.real_name); tr:tag('td'):wikitext(row.role); tr:tag('td'):wikitext(row.nationality)
   
        end
    local m2 = bar:tag('div'):addClass('tdb-item')
     else root:tag('tr'):tag('td'):attr('colspan', '4'):wikitext('No players currently listed for this team.') end
    m2:tag('div'):addClass('tdb-label'):wikitext('Total Earnings')
     return tostring(root)
    m2:tag('div'):addClass('tdb-value'):wikitext('₹ ' .. formatCurrency(earnings):gsub("₹ ", ""):gsub('<[^>]+>',''))
   
    local m3 = bar:tag('div'):addClass('tdb-item')
    m3:tag('div'):addClass('tdb-label'):wikitext('Recent Form')
     m3:tag('div'):addClass('tdb-value'):css('font-size','1em'):wikitext('Coming Soon')
   
     return tostring(bar)
end
end


return p
return p

Revision as of 20:19, 29 January 2026

Documentation for this module may be created at Module:Team/doc

local p = {}
local cargo = mw.ext.cargo
local html = mw.html
local currentTitle = mw.title.getCurrentTitle()

-- ============================================================
-- HELPER FUNCTIONS (Logos, Socials, Currency)
-- ============================================================

local function formatCurrency(amount)
    if not amount then return "0" end
    local n = tonumber(amount) or 0
    local formatted = tostring(n):reverse():gsub("(%d%d%d)(%d%d%d)","%1,%2"):reverse()
    return '<span class="indian-currency">₹ ' .. formatted .. '</span>'
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 getInfoboxLogo(teamName, image, imageDark)
    local lightFile = (image ~= "" and image) or (teamName .. '.png')
    local darkFile = (imageDark ~= "" and imageDark) or (teamName .. '_dark.png')
    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 .. '|220px]]') else lSpan:wikitext('[[File:Shield_team.png|180px]]') end
    local dSpan = container:tag('span'):addClass('logo-darkmode')
    if hasDark then dSpan:wikitext('[[File:' .. darkFile .. '|220px]]') elseif hasLight then dSpan:wikitext('[[File:' .. lightFile .. '|220px]]') else dSpan:wikitext('[[File:Shield_team_dark.png|180px]]') end
    return tostring(container)
end

-- ============================================================
-- MAIN 1: TEAM INFOBOX (Standard)
-- ============================================================
function p.infobox(frame)
    local args = frame:getParent().args
    local team = args.name or currentTitle.subpageText
    
    local root = html.create('div'):addClass('flat-infobox')
    root:tag('div'):addClass('fib-header'):tag('div'):addClass('fib-title'):wikitext(team)
    root:wikitext(getInfoboxLogo(team, args.image, args.image_dark))
    
    local earnings = "0"
    if cargo and cargo.query then
        local results = cargo.query("PrizeMoney", "SUM(prize)=total", { where = "team = '" .. team:gsub("'", "\\'") .. "' AND (player='' OR player IS NULL)" })
        if results and #results > 0 and results[1].total then earnings = results[1].total end
    end
    
    local rankVal = "Unranked"
    if cargo and cargo.query then
        local rResults = cargo.query("Krafton_Rankings", "rank", { where = "name = '" .. team:gsub("'", "\\'") .. "' AND type='Team'", limit = 1 })
        if rResults and #rResults > 0 then rankVal = "#" .. rResults[1].rank end
    end
    
    local statusColor = "#333"
    local statusText = args.status or "Active"
    if args.status then
        local s = args.status:lower()
        if s == "active" then statusColor = "#16a34a" elseif s == "inactive" or s == "disbanded" then statusColor = "#dc2626" end
    end

    local grid1 = root:tag('div'):addClass('fib-grid')
    grid1:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Status'):done():tag('div'):addClass('fib-value-sm'):css('color', statusColor):wikitext(statusText):done()
    grid1:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Krafton Rank'):done():tag('div'):addClass('fib-value-sm'):wikitext(rankVal):done()

    local grid2 = root:tag('div'):addClass('fib-grid')
    grid2:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Country'):done():tag('div'):addClass('fib-value-sm'):wikitext(args.country or 'TBD'):done()
    grid2:tag('div'):addClass('fib-cell'):tag('div'):addClass('fib-label-sm'):wikitext('Tag'):done():tag('div'):addClass('fib-value-sm'):wikitext(args.short_code or '-'):done()

    if earnings and tonumber(earnings) and tonumber(earnings) > 0 then
        root:tag('div'):addClass('fib-prize'):tag('div'):addClass('fib-label-sm'):wikitext('Total Earnings'):done():tag('div'):addClass('fib-prize-val'):wikitext(formatCurrency(earnings)):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('Full Name', args.other_team)
    if args.sponsors then addRow('Sponsors', args.sponsors:gsub(",", "<br>")) end
    root:wikitext(getSocials(args))
    return tostring(root)
end

-- ============================================================
-- MAIN 2: HYBRID ACTIVE ROSTER (HERO GRID)
-- ============================================================
function p.roster(frame)
    local args = frame:getParent().args
    local team = args.team or currentTitle.subpageText
    
    -- 1. Fetch Database Players
    local dbPlayers = {}
    if cargo and cargo.query then
        local tables = "Players"
        local fields = "id, real_name, role, nationality, image, _pageName"
        local where = "current_team = '" .. team:gsub("'", "\\'") .. "' AND status='Active'"
        local results = cargo.query(tables, fields, { where = where, orderBy = "role ASC" })
        
        for _, row in ipairs(results) do
            dbPlayers[row.id] = {
                id = row.id,
                name = row.real_name,
                role = row.role,
                flag = row.nationality,
                image = row.image,
                link = row._pageName,
                source = "auto"
            }
        end
    end
    
    -- 2. Process Manual Inputs (Check for duplicates)
    local finalRoster = {}
    
    -- Add DB players first
    for _, pData in pairs(dbPlayers) do
        table.insert(finalRoster, pData)
    end
    
    -- Check manual args (player1, player2...)
    for i = 1, 10 do
        local mid = args['player' .. i]
        if mid and mid ~= "" then
            -- Only add if NOT in DB list
            if not dbPlayers[mid] then
                table.insert(finalRoster, {
                    id = mid,
                    name = args['name' .. i] or "",
                    role = args['role' .. i] or "Player",
                    flag = args['flag' .. i] or "India",
                    image = "", -- Manual entries usually have no image
                    link = mid, -- Redlink assumption
                    source = "manual"
                })
            end
        end
    end
    
    -- 3. Render the Grid (USING NEW CLASS NAMES)
    local grid = html.create('div'):addClass('hero-roster-grid')
    
    for _, player in ipairs(finalRoster) do
        local card = grid:tag('div'):addClass('hero-player-card')
        
        -- Header (Image Area)
        local header = card:tag('div'):addClass('hero-card-image')
        if player.image and player.image ~= "" then
            header:wikitext('[[File:' .. player.image .. '|link=' .. player.link .. ']]')
        else
            -- Fallback Silhouette
            header:wikitext('[[File:Player_Placeholder.png|link=' .. player.link .. ']]')
        end
        
        -- Info Body
        local body = card:tag('div'):addClass('hero-card-body')
        
        -- Role Pill
        local roleColor = "#64748b" -- Default Gray
        local r = (player.role or ""):lower()
        if r:find("igl") then roleColor = "#eab308" -- Gold
        elseif r:find("fragger") or r:find("entry") or r:find("assault") then roleColor = "#ef4444" -- Red
        elseif r:find("support") or r:find("medic") then roleColor = "#22c55e" -- Green
        elseif r:find("sniper") then roleColor = "#3b82f6" -- Blue
        elseif r:find("coach") then roleColor = "#a855f7" -- Purple
        end
        
        body:tag('div'):addClass('hero-role-pill')
            :css('background-color', roleColor)
            :wikitext(player.role or "Player")
            
        -- ID and Name
        body:tag('div'):addClass('hero-player-id'):wikitext('[[' .. player.link .. '|' .. player.id .. ']]')
        if player.name and player.name ~= "" then
            body:tag('div'):addClass('hero-player-name'):wikitext(player.name)
        end
        
        -- Flag
        if player.flag then
            body:tag('div'):addClass('hero-player-flag'):wikitext('[[File:Flag_' .. player.flag .. '.png|20px]] ' .. player.flag)
        end
    end
    
    return tostring(grid)
end

-- ============================================================
-- MAIN 3: FORMER PLAYERS LIST
-- ============================================================
function p.formerPlayers(frame)
    local args = frame:getParent().args
    local team = args.team or currentTitle.subpageText
    
    local results = cargo.query("Player_Former_Teams, Players", 
        "Player_Former_Teams.player_id=id, Players.real_name=Name, Player_Former_Teams.role=role, Player_Former_Teams.join_date=join_date, Player_Former_Teams.leave_date=leave_date", 
        {
            joinOn = "Player_Former_Teams.player_id = Players.id", 
            where = "Player_Former_Teams.team = '" .. team:gsub("'", "\\'") .. "'",
            orderBy = "Player_Former_Teams.leave_date DESC"
        }
    )
    
    if not results or #results == 0 then
        return '<div style="font-style:italic; color:#64748b; padding:10px;">No former players recorded.</div>'
    end
    
    local tbl = html.create('table'):addClass('wikitable flat-table sortable'):css('width','100%')
    local h = tbl:tag('tr')
    h:tag('th'):wikitext('ID')
    h:tag('th'):wikitext('Name')
    h:tag('th'):wikitext('Role')
    h:tag('th'):wikitext('Join Date')
    h:tag('th'):wikitext('Leave Date')
    
    for _, row in ipairs(results) do
        local tr = tbl:tag('tr')
        tr:tag('td'):css('font-weight','bold'):wikitext('[[' .. row.id .. ']]')
        tr:tag('td'):wikitext(row.Name or "-")
        tr:tag('td'):wikitext(row.role or "-")
        tr:tag('td'):wikitext(row.join_date or "?")
        tr:tag('td'):wikitext(row.leave_date or "?")
    end
    
    return tostring(tbl)
end

-- ============================================================
-- MAIN 4: STAT HEADER (KEY METRICS)
-- ============================================================
function p.statHeader(frame)
    local args = frame:getParent().args
    local team = args.team or currentTitle.subpageText
    
    local earnings = "0"
    local results = cargo.query("PrizeMoney", "SUM(prize)=total", { where = "team = '" .. team:gsub("'", "\\'") .. "' AND (player='' OR player IS NULL)" })
    if results and #results > 0 and results[1].total then earnings = results[1].total end
    
    local rank = "N/A"
    local rRes = cargo.query("Krafton_Rankings", "rank", { where = "name = '" .. team:gsub("'", "\\'") .. "' AND type='Team'", limit = 1 })
    if rRes and #rRes > 0 then rank = "#" .. rRes[1].rank end
    
    local bar = html.create('div'):addClass('team-dashboard-bar')
    
    local m1 = bar:tag('div'):addClass('tdb-item')
    m1:tag('div'):addClass('tdb-label'):wikitext('Krafton Rank')
    local val1 = m1:tag('div'):addClass('tdb-value'):wikitext(rank)
    if rank ~= "N/A" then val1:addClass('highlight-gold') end
    
    local m2 = bar:tag('div'):addClass('tdb-item')
    m2:tag('div'):addClass('tdb-label'):wikitext('Total Earnings')
    m2:tag('div'):addClass('tdb-value'):wikitext('₹ ' .. formatCurrency(earnings):gsub("₹ ", ""):gsub('<[^>]+>','')) 
    
    local m3 = bar:tag('div'):addClass('tdb-item')
    m3:tag('div'):addClass('tdb-label'):wikitext('Recent Form')
    m3:tag('div'):addClass('tdb-value'):css('font-size','1em'):wikitext('Coming Soon')
    
    return tostring(bar)
end

return p