{"id":147,"date":"2026-03-31T07:01:42","date_gmt":"2026-03-31T07:01:42","guid":{"rendered":"https:\/\/ph-portal.zyneventures.com\/?page_id=147"},"modified":"2026-03-31T07:04:12","modified_gmt":"2026-03-31T07:04:12","slug":"productivity-executive","status":"publish","type":"page","link":"https:\/\/ph-portal.zyneventures.com\/index.php\/productivity-executive\/","title":{"rendered":"Productivity Executive"},"content":{"rendered":"<style>.kadence-column147_495f3b-5f > .kt-inside-inner-col,.kadence-column147_495f3b-5f > .kt-inside-inner-col:before{border-top-left-radius:0px;border-top-right-radius:0px;border-bottom-right-radius:0px;border-bottom-left-radius:0px;}.kadence-column147_495f3b-5f > .kt-inside-inner-col{column-gap:var(--global-kb-gap-sm, 1rem);}.kadence-column147_495f3b-5f > .kt-inside-inner-col{flex-direction:column;}.kadence-column147_495f3b-5f > .kt-inside-inner-col > .aligncenter{width:100%;}.kadence-column147_495f3b-5f > .kt-inside-inner-col:before{opacity:0.3;}.kadence-column147_495f3b-5f{position:relative;}@media all and (max-width: 1024px){.kadence-column147_495f3b-5f > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}@media all and (max-width: 767px){.kadence-column147_495f3b-5f > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}<\/style>\n<div class=\"wp-block-kadence-column kadence-column147_495f3b-5f\"><div class=\"kt-inside-inner-col\"><style>.kb-row-layout-id147_48076e-b9 > .kt-row-column-wrap{align-content:start;}:where(.kb-row-layout-id147_48076e-b9 > .kt-row-column-wrap) > .wp-block-kadence-column{justify-content:start;}.kb-row-layout-id147_48076e-b9 > .kt-row-column-wrap{column-gap:var(--global-kb-gap-md, 2rem);row-gap:var(--global-kb-gap-md, 2rem);padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;grid-template-columns:minmax(0, 1fr);}.kb-row-layout-id147_48076e-b9 > .kt-row-layout-overlay{opacity:0.30;}@media all and (max-width: 1024px){.kb-row-layout-id147_48076e-b9 > .kt-row-column-wrap{grid-template-columns:minmax(0, 1fr);}}@media all and (max-width: 767px){.kb-row-layout-id147_48076e-b9 > .kt-row-column-wrap{grid-template-columns:minmax(0, 1fr);}}<\/style><div class=\"kb-row-layout-wrap kb-row-layout-id147_48076e-b9 alignnone wp-block-kadence-rowlayout\"><div class=\"kt-row-column-wrap kt-has-1-columns kt-row-layout-equal kt-tab-layout-inherit kt-mobile-layout-row kt-row-valign-top\">\n<style>.kadence-column147_b5e20c-93 > .kt-inside-inner-col,.kadence-column147_b5e20c-93 > .kt-inside-inner-col:before{border-top-left-radius:0px;border-top-right-radius:0px;border-bottom-right-radius:0px;border-bottom-left-radius:0px;}.kadence-column147_b5e20c-93 > .kt-inside-inner-col{column-gap:var(--global-kb-gap-sm, 1rem);}.kadence-column147_b5e20c-93 > .kt-inside-inner-col{flex-direction:column;}.kadence-column147_b5e20c-93 > .kt-inside-inner-col > .aligncenter{width:100%;}.kadence-column147_b5e20c-93 > .kt-inside-inner-col:before{opacity:0.3;}.kadence-column147_b5e20c-93{position:relative;}@media all and (max-width: 1024px){.kadence-column147_b5e20c-93 > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}@media all and (max-width: 767px){.kadence-column147_b5e20c-93 > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}<\/style>\n<div class=\"wp-block-kadence-column kadence-column147_b5e20c-93\"><div class=\"kt-inside-inner-col\">\n<style>\n    :root {\n        --primary-blue: #0180FF;\n        --hover-blue: #3499FF;\n        --text-dark: #212529;\n        --text-muted: #8D8C9C;\n        --border-color: #EFEFEF;\n        --success-green: #198754;\n    }\n    * { box-sizing: border-box; margin: 0; padding: 0; }\n    body { \n        font-family: 'Sora', sans-serif; \n        color: var(--text-dark); \n        background: #fff; \n    }\n\n    .page-title {\n        font-size: 21px; \n        font-weight: 500;\n        text-transform: uppercase; \n        letter-spacing: 0.5px;\n        margin-bottom: 28px;\n    }\n\n    \/* FILTER BAR *\/\n    .filter-bar { display: flex; align-items: center; gap: 12px; margin-bottom: 24px; flex-wrap: wrap; }\n\n    \/* CUSTOM DROPDOWN *\/\n    .custom-dd { position: relative; min-width: 160px; }\n    .dd-trigger {\n        display: flex; \n        align-items: center; \n        justify-content: space-between; \n        gap: 8px;\n        border: 1px solid #D1D5DB; \n        border-radius: 6px;\n        padding: 9px 14px;\n        font-family: 'Sora', sans-serif; \n        font-size: 12px; \n        color: var(--text-dark);\n        background: #fff; \n        cursor: pointer; \n        user-select: none; \n        white-space: nowrap;\n        transition: border-color 0.15s;\n    }\n    .dd-trigger:hover { border-color: #8D8C9C; }\n    .dd-trigger .dd-label { flex: 1; overflow: hidden; text-overflow: ellipsis; }\n    .dd-trigger .dd-arrow { flex-shrink: 0; transition: transform 0.2s; }\n    .custom-dd.open .dd-trigger .dd-arrow { transform: rotate(180deg); }\n    .dd-placeholder { color: var(--text-muted) !important; }\n\n    .dd-menu {\n        display: none; \n        position: absolute; \n        top: calc(100% + 4px); \n        left: 0;\n        min-width: 100%; \n        max-height: 220px; \n        overflow-y: auto;\n        background: #fff; \n        border: 1px solid #E5E7EB;\n        border-radius: 6px; \n        box-shadow: 0 8px 24px rgba(0,0,0,0.10); \n        z-index: 999;\n    }\n    .custom-dd.open .dd-menu { display: block; }\n    .dd-item {\n        padding: 9px 14px; \n        font-size: 12px; \n        cursor: pointer;\n        color: var(--text-dark); \n        transition: background 0.13s;\n    }\n    .dd-item:hover { background: #EBF4FF; color: var(--primary-blue); }\n    .dd-item.selected { color: var(--primary-blue); font-weight: 500; background: #f0f8ff; }\n\n    \/* BUTTONS *\/\n    .btn-filter {\n        background: var(--primary-blue); \n        color: #fff; \n        border: none;\n        border-radius: 6px; \n        padding: 9px 24px;\n        font-family: 'Sora', \n        sans-serif; \n        font-size: 12px; \n        font-weight: 500;\n        cursor: pointer; \n        transition: background 0.2s; \n        white-space: nowrap;\n    }\n    .btn-filter:hover { background: var(--hover-blue); }\n    .btn-csv {\n        background: var(--primary-blue); \n        color: #fff; \n        border: none;\n        border-radius: 6px; \n        padding: 9px 20px;\n        font-family: 'Sora', sans-serif; \n        font-size: 12px; \n        font-weight: 500;\n        cursor: pointer; \n        transition: background 0.2s; \n        white-space: nowrap;\n    }\n    .btn-csv:hover { background: var(--hover-blue); }\n\n    \/* SUMMARY CARDS *\/\n    .summary-cards { display: flex; gap: 16px; justify-content: flex-end; margin-bottom: 28px; }\n    .summary-card { \n        background: #F8F9FA; \n        border-radius: 12px; \n        padding: 20px 32px; \n        min-width: 180px; \n        text-align: center; \n    }\n    .summary-card .card-label { \n        font-size: 12px; \n        color: var(--text-muted); \n        margin-bottom: 8px; \n        font-weight: 500; \n    }\n    .summary-card .card-value { \n        font-size: 32px; \n        font-weight: 500; \n        color: var(--success-green); \n        line-height: 1; \n    }\n\n    \/* TABS *\/\n    .tab-nav { display: flex; border-bottom: 2px solid var(--border-color); overflow: visible; flex-wrap: wrap; }\n    .tab-btn {\n        background: none; \n        border: none; \n        border-radius: 0px; \n        border-bottom: 3px solid transparent;\n        margin-bottom: -2px; \n        padding: 12px 20px;\n        font-family: 'Sora', sans-serif; \n        font-size: 16px;\n        color: var(--text-muted); \n        cursor: pointer; \n        font-weight: 400;\n        transition: all 0.2s; \n        white-space: nowrap;\n    }\n    .tab-btn.active { \n        color: var(--text-dark); \n        font-weight: 600; \n        background-color: transparent;\n        border-bottom-color: var(--primary-blue); \n    }\n    .tab-btn:hover:not(.active) { \n        color: var(--text-muted); \n        background-color: transparent; \n    }\n\n    \/* TABLE *\/\n    .table-wrapper { overflow-x: auto; }\n    table { width: 100%; border-collapse: collapse; font-size: 12px; }\n    thead tr { background: #F8F9FA; }\n    thead th {\n        padding: 14px 18px; \n        text-align: left; \n        font-weight: 500; \n        font-size: 12px;\n        color: var(--text-dark); \n        text-transform: uppercase; \n        letter-spacing: 0.4px; \n        white-space: nowrap;\n    }\n    tbody tr { border-bottom: 1px solid var(--border-color); }\n    tbody tr:last-child { border-bottom: none; }\n    tbody td { padding: 14px 18px; color: #8D8C9C; font-size: 12px; }\n    tbody tr:hover { background: #FAFAFA; }\n    .state-row td { text-align: center; padding: 40px; color: var(--text-muted); }\n\n    \/* PAGINATION *\/\n    .pagination-footer {\n        display: flex; \n        align-items: center; \n        justify-content: space-between;\n        padding: 16px 0; \n        flex-wrap: wrap; \n        gap: 12px;\n    }\n    .pagination-controls { display: flex; align-items: center; gap: 6px; }\n    .nav-btn {\n        padding: 6px 14px; \n        border-radius: 6px; \n        cursor: pointer; \n        font-size: 12px;\n        color: var(--text-dark); \n        background: transparent; \n        border: none;\n        font-family: 'Sora', sans-serif; \n        transition: all 0.2s; \n        user-select: none;\n    }\n    .nav-btn:hover:not(.disabled) { background: var(--primary-blue); color: #fff; }\n    .nav-btn.disabled { opacity: 0.4; cursor: not-allowed; pointer-events: none; }\n    .page-num {\n        width: 32px; \n        height: 32px; \n        display: flex; \n        align-items: center; \n        justify-content: center;\n        border-radius: 50%; \n        cursor: pointer; \n        font-size: 12px;\n        color: var(--text-dark); \n        background: transparent;\n        transition: all 0.2s; \n        user-select: none;\n    }\n    .page-num.active { background: var(--primary-blue); color: #fff; }\n    .page-num:hover:not(.active) { background: transparent; }\n    .entry-info { font-size: 12px; color: var(--text-muted); }\n\n    @media (max-width: 768px) {\n        body { padding: 20px 16px; }\n        .custom-dd { min-width: 130px; }\n        .summary-cards { justify-content: flex-start; }\n    }\n    @media (max-width: 480px) {\n        .filter-bar { flex-direction: column; align-items: stretch; }\n        .custom-dd { min-width: unset; width: 100%; }\n        .btn-filter, .btn-csv { width: 100%; }\n        .summary-cards { flex-direction: column; }\n        .summary-card { min-width: unset; }\n    }\n<\/style>\n\n\n<h2 class=\"page-title\" id=\"pageTitle\">EXECUTIVE WISE PRODUCTIVITY AND QUALITY SUMMARY<\/h2><br>\n\n<div class=\"filter-bar\" id=\"filterBar\"><\/div>\n\n<div class=\"summary-cards\" id=\"summaryCards\">\n    <div class=\"summary-card\">\n        <div class=\"card-label\">Productivity %<\/div>\n        <div class=\"card-value\" id=\"summaryProductivity\">0.00%<\/div>\n    <\/div>\n    <div class=\"summary-card\">\n        <div class=\"card-label\">Quality %<\/div>\n        <div class=\"card-value\" id=\"summaryQuality\">0.00%<\/div>\n    <\/div>\n<\/div>\n\n<div class=\"tab-nav\">\n    <button class=\"tab-btn active\" onclick=\"switchTab('executive')\">Executive Wise<\/button>\n    <button class=\"tab-btn\" onclick=\"switchTab('qc')\">QC Executive Wise<\/button>\n    <button class=\"tab-btn\" onclick=\"switchTab('data')\">Data Team Wise<\/button>\n    <button class=\"tab-btn\" onclick=\"switchTab('teamlead')\">Team Lead Wise<\/button>\n    <button class=\"tab-btn\" onclick=\"switchTab('project')\">Project Wise<\/button>\n<\/div>\n\n<div class=\"table-wrapper\">\n    <table>\n        <thead id=\"tableHead\"><\/thead>\n        <tbody id=\"tableBody\"><\/tbody>\n    <\/table>\n<\/div>\n\n<div class=\"pagination-footer\">\n    <div class=\"pagination-controls\">\n        <span class=\"nav-btn\" id=\"prevBtn\" onclick=\"changePage(-1)\">&#171; Previous<\/span>\n        <div id=\"pageNumbers\" style=\"display:flex;gap:6px;\"><\/div>\n        <span class=\"nav-btn\" id=\"nextBtn\" onclick=\"changePage(1)\">Next &#187;<\/span>\n    <\/div>\n    <div class=\"entry-info\" id=\"entryInfo\">Showing 0 to 0 of 0 entries<\/div>\n<\/div>\n\n<script>\n\/\/ ENDPOINTS\nconst BASE_URL = 'https:\/\/api-ph-portal.zyneventures.com\/api\/v1';\nconst ENDPOINTS = {\n    EXECUTIVE: `${BASE_URL}\/productivity\/indexexecutive`,\n    QC:        `${BASE_URL}\/productivity\/qc\/indexexecutive`,\n    DATA:      `${BASE_URL}\/productivity\/data\/indexexecutive`,\n    TEAM_LEAD: `${BASE_URL}\/productivity\/team-lead-wise`,\n    PROJECT:   `${BASE_URL}\/productivity\/project-wise`,\n};\n\n\/\/ CONSTANTS\nconst PER_PAGE = 10;\nconst MONTHS = ['January','February','March','April','May','June',\n                'July','August','September','October','November','December'];\n\nlet currentTab = 'executive';\nlet allData    = [];\nlet currentPage = 1;\nlet ddValues   = {}; \/\/ { key: value }\n\nconst getHeaders = () => ({\n    'Content-Type': 'application\/json',\n    'Authorization': `Bearer ${localStorage.getItem('TOKEN')}`\n});\n\n\/\/ TAB CONFIG\nconst TABS = {\n    executive: {\n        title: 'EXECUTIVE WISE PRODUCTIVITY AND QUALITY SUMMARY',\n        showCards: true, showCSV: true,\n        columns: ['A. MANAGER','T. LEADs','PESUDO NAME','PRODUCTIVITY','QUALITY'],\n        fields:  ['a_manager','team_lead','pesudo_name','productivity_percentage','quality_percentage'],\n        dds: [\n            { id:'dd_month',  key:'month',  label:'Select Month', type:'month'   },\n            { id:'dd_year',   key:'year',   label:'Year',         type:'year'    },\n            { id:'dd_search', key:'search', label:'Select Executive', type:'dynamic', dynamicField:'pesudo_name' },\n        ]\n    },\n    qc: {\n        title: 'EXECUTIVE WISE PRODUCTIVITY AND QUALITY SUMMARY',\n        showCards: true, showCSV: true,\n        columns: ['A. MANAGER','T. LEADs','PESUDO NAME','PRODUCTIVITY','QUALITY'],\n        fields:  ['a_manager','team_lead','pesudo_name','productivity_percentage','quality_percentage'],\n        dds: [\n            { id:'dd_month',  key:'month',  label:'Select Month', type:'month'   },\n            { id:'dd_year',   key:'year',   label:'Year',         type:'year'    },\n            { id:'dd_search', key:'search', label:'Select QC Executive', type:'dynamic', dynamicField:'pesudo_name' },\n        ]\n    },\n    data: {\n        title: 'EXECUTIVE WISE PRODUCTIVITY AND QUALITY SUMMARY',\n        showCards: false, showCSV: true,\n        columns: ['A. MANAGER','T. LEADs','PESUDO NAME','PRODUCTIVITY','QUALITY'],\n        fields:  ['a_manager','team_lead','pesudo_name','productivity_percentage','quality_percentage'],\n        dds: [\n            { id:'dd_month',  key:'month',  label:'Select Month', type:'month'   },\n            { id:'dd_year',   key:'year',   label:'Year',         type:'year'    },\n            { id:'dd_search', key:'search', label:'Select Data',  type:'dynamic', dynamicField:'pesudo_name' },\n        ]\n    },\n    teamlead: {\n        title: 'TEAM LEAD WISE PRODUCTIVITY AND QUALITY SUMMARY',\n        showCards: false, showCSV: false,\n        columns: ['A. MANAGER','PESUDO NAME','PRODUCTIVITY','QUALITY'],\n        fields:  ['a_manager','pesudo_name','productivity_percentage','quality_percentage'],\n        dds: [\n            { id:'dd_month',  key:'month',  label:'Select Month',     type:'month'   },\n            { id:'dd_year',   key:'year',   label:'Year',             type:'year'    },\n            { id:'dd_search', key:'search', label:'Select Team Lead', type:'dynamic', dynamicField:'pesudo_name' },\n        ]\n    },\n    project: {\n        title: 'EXECUTIVE WISE-PRODUCTIVITY',\n        showCards: false, showCSV: false,\n        columns: ['PROJECT NAME','PRODUCTIVITY','QUALITY'],\n        fields:  ['project_name','productivity','quality'],\n        dds: [\n            { id:'dd_month',   key:'month',   label:'Select Month',   type:'month'   },\n            { id:'dd_year',    key:'year',    label:'Year',            type:'year'    },\n            { id:'dd_clients', key:'client',  label:'Select Clients',  type:'dynamic', dynamicField:'project_name' },\n        ]\n    }\n};\n\n\/\/ OPTION GENERATORS\nfunction monthOpts() {\n    return [{ v:'', l:'Select Month' }, ...MONTHS.map((m,i) => ({ v:i+1, l:m }))];\n}\nfunction yearOpts() {\n    const y = new Date().getFullYear();\n    const opts = [{ v:'', l:'Year' }];\n    for (let i = y; i >= y-10; i--) opts.push({ v:i, l:String(i) });\n    return opts;\n}\nfunction dateOpts(forEnd) {\n    const now = new Date();\n    const opts = [{ v:'', l: forEnd ? 'End Date' : 'Start Date' }];\n    for (let i = 0; i < 24; i++) {\n        const d = new Date(now.getFullYear(), now.getMonth() - i, 1);\n        const y = d.getFullYear(), m = d.getMonth();\n        const first = `${y}-${String(m+1).padStart(2,'0')}-01`;\n        const lastD = new Date(y, m+1, 0).getDate();\n        const last  = `${y}-${String(m+1).padStart(2,'0')}-${lastD}`;\n        const lbl   = d.toLocaleDateString('en-US', { month:'long', year:'numeric' });\n        opts.push({ v: forEnd ? last : first, l: lbl });\n    }\n    return opts;\n}\n\n\/\/ CUSTOM DROPDOWN FACTORY\nfunction makeDd(cfg, opts = []) {\n    const wrap = document.createElement('div');\n    wrap.className = 'custom-dd';\n    wrap.id = 'wrap_' + cfg.id;\n\n    const trigger = document.createElement('div');\n    trigger.className = 'dd-trigger';\n    trigger.innerHTML = `\n        <span class=\"dd-label dd-placeholder\">${cfg.label}<\/span>\n        <svg class=\"dd-arrow\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\"\n             fill=\"none\" stroke=\"#8D8C9C\" stroke-width=\"2.5\">\n          <polyline points=\"6 9 12 15 18 9\"\/>\n        <\/svg>`;\n\n    const menu = document.createElement('div');\n    menu.className = 'dd-menu';\n    menu.id = cfg.id;\n\n    wrap.appendChild(trigger);\n    wrap.appendChild(menu);\n\n    fillDd(cfg, wrap, opts);\n\n    trigger.addEventListener('click', e => {\n        e.stopPropagation();\n        const wasOpen = wrap.classList.contains('open');\n        closeAllDds();\n        if (!wasOpen) wrap.classList.add('open');\n    });\n\n    return wrap;\n}\n\nfunction fillDd(cfg, wrap, opts) {\n    const menu    = wrap.querySelector('.dd-menu');\n    const trigger = wrap.querySelector('.dd-trigger');\n    const labelEl = trigger.querySelector('.dd-label');\n    menu.innerHTML = '';\n    opts.forEach(opt => {\n        const item = document.createElement('div');\n        item.className = 'dd-item' + (String(ddValues[cfg.key] || '') === String(opt.v) ? ' selected' : '');\n        item.textContent = opt.l;\n        item.addEventListener('click', e => {\n            e.stopPropagation();\n            ddValues[cfg.key] = opt.v;\n            labelEl.textContent = opt.v !== '' ? opt.l : cfg.label;\n            labelEl.classList.toggle('dd-placeholder', opt.v === '');\n            menu.querySelectorAll('.dd-item').forEach(x => x.classList.remove('selected'));\n            item.classList.add('selected');\n            closeAllDds();\n            \/\/ Dynamic dropdowns (search\/client) filter immediately\n            if (cfg.type === 'dynamic') { currentPage = 1; renderCurrentPage(); }\n        });\n        menu.appendChild(item);\n    });\n}\n\nfunction closeAllDds() {\n    document.querySelectorAll('.custom-dd.open').forEach(d => d.classList.remove('open'));\n}\n\n\/\/ RENDER FILTER BAR\nfunction renderFilterBar() {\n    const cfg = TABS[currentTab];\n    ddValues = {};\n    const bar = document.getElementById('filterBar');\n    bar.innerHTML = '';\n\n    cfg.dds.forEach(ddCfg => {\n        let opts = [];\n        if      (ddCfg.type === 'month')      opts = monthOpts();\n        else if (ddCfg.type === 'year')       opts = yearOpts();\n        else if (ddCfg.type === 'date_start') opts = dateOpts(false);\n        else if (ddCfg.type === 'date_end')   opts = dateOpts(true);\n        \/\/ dynamic: starts empty, populated after fetch\n        bar.appendChild(makeDd(ddCfg, opts));\n    });\n\n    const btnF = document.createElement('button');\n    btnF.className = 'btn-filter';\n    btnF.textContent = 'Filter';\n    btnF.onclick = () => { currentPage = 1; fetchData(); };\n    bar.appendChild(btnF);\n\n    if (cfg.showCSV) {\n        const btnC = document.createElement('button');\n        btnC.className = 'btn-csv';\n        btnC.textContent = 'Download CSV';\n        btnC.onclick = downloadCSV;\n        bar.appendChild(btnC);\n    }\n}\n\n\/\/ refresh dynamic dropdowns after data loaded\nfunction refreshDynamic() {\n    const cfg = TABS[currentTab];\n    cfg.dds.forEach(ddCfg => {\n        if (ddCfg.type !== 'dynamic') return;\n        const wrap = document.getElementById('wrap_' + ddCfg.id);\n        if (!wrap) return;\n        const seen = new Set();\n        const opts = [{ v:'', l:'\u2014 All \u2014' }];\n        allData.forEach(row => {\n            const v = row[ddCfg.dynamicField];\n            if (v != null && !seen.has(v)) { seen.add(v); opts.push({ v, l:v }); }\n        });\n        fillDd(ddCfg, wrap, opts);\n    });\n}\n\n\/\/ BUILD API URL\nfunction buildUrl() {\n    const p = new URLSearchParams();\n    const v = ddValues;\n\n    if (currentTab === 'executive') {\n        if (v.month) p.set('month', v.month);\n        if (v.year)  p.set('year',  v.year);\n        return `${ENDPOINTS.EXECUTIVE}?${p}`;\n    }\n    if (currentTab === 'qc') {\n        if (v.month && v.year) {\n            const last = new Date(v.year, v.month, 0).getDate();\n            p.set('start_date', `${v.year}-${String(v.month).padStart(2,'0')}-01`);\n            p.set('end_date',   `${v.year}-${String(v.month).padStart(2,'0')}-${last}`);\n        }\n        return `${ENDPOINTS.QC}?${p}`;\n    }\n    if (currentTab === 'data') {\n        if (v.month) p.set('month', v.month);\n        if (v.year)  p.set('year',  v.year);\n        return `${ENDPOINTS.DATA}?${p}`;\n    }\n    if (currentTab === 'teamlead') {\n        if (v.month) p.set('month', v.month);\n        if (v.year)  p.set('year',  v.year);\n        return `${ENDPOINTS.TEAM_LEAD}?${p}`;\n    }\n    return ENDPOINTS.PROJECT;\n}\n\n\/\/ FETCH\nasync function fetchData() {\n    showLoading();\n    try {\n        const res  = await fetch(buildUrl(), { headers: getHeaders() });\n        const json = await res.json();\n\n        let data = [], summary = null;\n        if (Array.isArray(json))                                { data = json; }\n        else if (Array.isArray(json.data))                      { data = json.data; summary = json.summary; }\n        else if (json.data && Array.isArray(json.data.data))    { data = json.data.data; summary = json.summary; }\n\n        allData = data;\n\n        if (summary && TABS[currentTab].showCards) {\n            document.getElementById('summaryProductivity').textContent =\n                parseFloat(summary.productivity_percentage || 0).toFixed(2) + '%';\n            document.getElementById('summaryQuality').textContent =\n                parseFloat(summary.quality_percentage || 0).toFixed(2) + '%';\n        }\n\n        refreshDynamic();\n        currentPage = 1;\n        renderCurrentPage();\n    } catch (err) {\n        console.error('API error:', err);\n        renderTableBody([]);\n        renderPagination(0);\n    }\n}\n\n\/\/ FILTER + PAGE\nfunction filtered() {\n    const v = ddValues;\n    return allData.filter(row => {\n        if (v.search && row[TABS[currentTab].dds.find(d=>d.key==='search')?.dynamicField] !== v.search) return false;\n        if (v.client && row.project_name !== v.client) return false;\n        return true;\n    });\n}\n\nfunction renderCurrentPage() {\n    const rows = filtered();\n    const start = (currentPage - 1) * PER_PAGE;\n    renderTableBody(rows.slice(start, start + PER_PAGE));\n    renderPagination(rows.length);\n    updateCards(rows);\n}\n\nfunction updateCards(rows) {\n    if (!TABS[currentTab].showCards) return;\n    if (!rows.length) {\n        document.getElementById('summaryProductivity').textContent = '0.00%';\n        document.getElementById('summaryQuality').textContent = '0.00%';\n        return;\n    }\n    const avgProd = rows.reduce((s, r) => s + parseFloat(r.productivity_percentage || 0), 0) \/ rows.length;\n    const avgQual = rows.reduce((s, r) => s + parseFloat(r.quality_percentage || 0), 0) \/ rows.length;\n    document.getElementById('summaryProductivity').textContent = avgProd.toFixed(2) + '%';\n    document.getElementById('summaryQuality').textContent = avgQual.toFixed(2) + '%';\n}\n\nfunction changePage(dir) {\n    const max = Math.ceil(filtered().length \/ PER_PAGE) || 1;\n    currentPage = Math.max(1, Math.min(currentPage + dir, max));\n    renderCurrentPage();\n}\n\n\/\/ TABLE\nfunction renderTableHead() {\n    document.getElementById('tableHead').innerHTML =\n        '<tr>' + TABS[currentTab].columns.map(c => `<th>${c}<\/th>`).join('') + '<\/tr>';\n}\n\nfunction renderTableBody(rows) {\n    const cfg  = TABS[currentTab];\n    const body = document.getElementById('tableBody');\n    if (!rows.length) {\n        body.innerHTML = `<tr class=\"state-row\"><td colspan=\"${cfg.columns.length}\">No data found<\/td><\/tr>`;\n        return;\n    }\n    body.innerHTML = rows.map(row => '<tr>' + cfg.fields.map(f => {\n        let val = row[f] != null ? row[f] : '\u2014';\n        if (f === 'productivity_percentage' || f === 'quality_percentage')\n            val = parseFloat(val || 0).toFixed(2) + '%';\n        if (f === 'productivity' && currentTab === 'project')\n            val = parseFloat(val || 0).toFixed(2);\n        return `<td>${val}<\/td>`;\n    }).join('') + '<\/tr>').join('');\n}\n\nfunction showLoading() {\n    const cfg = TABS[currentTab];\n    document.getElementById('tableBody').innerHTML =\n        `<tr class=\"state-row\"><td colspan=\"${cfg.columns.length}\">Loading...<\/td><\/tr>`;\n}\n\n\/\/ PAGINATION\nfunction renderPagination(total) {\n    const max   = Math.ceil(total \/ PER_PAGE) || 1;\n    const start = total === 0 ? 0 : (currentPage - 1) * PER_PAGE + 1;\n    const end   = Math.min(currentPage * PER_PAGE, total);\n\n    document.getElementById('entryInfo').textContent = `Showing ${start} to ${end} of ${total} entries`;\n    document.getElementById('prevBtn').classList.toggle('disabled', currentPage === 1);\n    document.getElementById('nextBtn').classList.toggle('disabled', currentPage >= max);\n\n    const cont = document.getElementById('pageNumbers');\n    cont.innerHTML = '';\n    let s = Math.max(1, currentPage - 2), e = Math.min(max, s + 4);\n    if (e - s < 4) s = Math.max(1, e - 4);\n    for (let i = s; i <= e; i++) {\n        const span = document.createElement('span');\n        span.className = 'page-num' + (i === currentPage ? ' active' : '');\n        span.textContent = i;\n        const pg = i;\n        span.onclick = () => { currentPage = pg; renderCurrentPage(); };\n        cont.appendChild(span);\n    }\n}\n\n\/\/ CSV\nfunction downloadCSV() {\n    const cfg  = TABS[currentTab];\n    const rows = [cfg.columns.join(','), ...filtered().map(row =>\n        cfg.fields.map(f => {\n            let v = row[f] != null ? row[f] : '';\n            if (String(v).includes(',')) v = `\"${v}\"`;\n            return v;\n        }).join(',')\n    )];\n    const a = document.createElement('a');\n    a.href = URL.createObjectURL(new Blob([rows.join('\\n')], { type:'text\/csv' }));\n    a.download = `${currentTab}_productivity.csv`;\n    a.click();\n}\n\n\/\/ SWITCH TAB\nfunction switchTab(tab) {\n    currentTab  = tab;\n    currentPage = 1;\n    allData     = [];\n    ddValues    = {};\n\n    document.querySelectorAll('.tab-btn').forEach((btn, i) => {\n        btn.classList.toggle('active',\n            ['executive','qc','data','teamlead','project'][i] === tab);\n    });\n\n    const cfg = TABS[tab];\n    document.getElementById('pageTitle').textContent   = cfg.title;\n    document.getElementById('summaryCards').style.display = cfg.showCards ? 'flex' : 'none';\n\n    renderFilterBar();\n    renderTableHead();\n    renderTableBody([]);\n    renderPagination(0);\n    fetchData();\n}\n\n\/\/ CLOSE DROPDOWNS ON OUTSIDE CLICK\ndocument.addEventListener('click', closeAllDds);\n\n\/\/ INIT\n    renderFilterBar();\n    renderTableHead();\n    fetchData();\n<\/script>\n<\/div><\/div>\n\n<\/div><\/div><\/div><\/div>\n","protected":false},"excerpt":{"rendered":"<p>EXECUTIVE WISE PRODUCTIVITY AND QUALITY SUMMARY Productivity % 0.00% Quality % 0.00% Executive Wise QC Executive Wise Data Team Wise Team Lead Wise Project Wise &#171; Previous Next &#187; Showing 0 to 0 of 0 entries<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","footnotes":""},"class_list":["post-147","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/ph-portal.zyneventures.com\/index.php\/wp-json\/wp\/v2\/pages\/147","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ph-portal.zyneventures.com\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/ph-portal.zyneventures.com\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/ph-portal.zyneventures.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ph-portal.zyneventures.com\/index.php\/wp-json\/wp\/v2\/comments?post=147"}],"version-history":[{"count":4,"href":"https:\/\/ph-portal.zyneventures.com\/index.php\/wp-json\/wp\/v2\/pages\/147\/revisions"}],"predecessor-version":[{"id":152,"href":"https:\/\/ph-portal.zyneventures.com\/index.php\/wp-json\/wp\/v2\/pages\/147\/revisions\/152"}],"wp:attachment":[{"href":"https:\/\/ph-portal.zyneventures.com\/index.php\/wp-json\/wp\/v2\/media?parent=147"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}