{"id":204,"date":"2026-03-31T11:35:02","date_gmt":"2026-03-31T11:35:02","guid":{"rendered":"https:\/\/ph-taskportal.zyneventures.com\/?page_id=204"},"modified":"2026-07-09T05:33:45","modified_gmt":"2026-07-09T05:33:45","slug":"quality-summary","status":"publish","type":"page","link":"https:\/\/ph-portal-v2.zyneventures.com\/index.php\/quality-summary\/","title":{"rendered":"Quality Summary"},"content":{"rendered":"<style>.kadence-column204_5e7fa4-d4 > .kt-inside-inner-col,.kadence-column204_5e7fa4-d4 > .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-column204_5e7fa4-d4 > .kt-inside-inner-col{column-gap:var(--global-kb-gap-sm, 1rem);}.kadence-column204_5e7fa4-d4 > .kt-inside-inner-col{flex-direction:column;}.kadence-column204_5e7fa4-d4 > .kt-inside-inner-col > .aligncenter{width:100%;}.kadence-column204_5e7fa4-d4 > .kt-inside-inner-col:before{opacity:0.3;}.kadence-column204_5e7fa4-d4{position:relative;}@media all and (max-width: 1024px){.kadence-column204_5e7fa4-d4 > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}@media all and (max-width: 767px){.kadence-column204_5e7fa4-d4 > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}<\/style>\n<div class=\"wp-block-kadence-column kadence-column204_5e7fa4-d4\"><div class=\"kt-inside-inner-col\"><style>.kb-row-layout-id204_de5ab0-14 > .kt-row-column-wrap{align-content:start;}:where(.kb-row-layout-id204_de5ab0-14 > .kt-row-column-wrap) > .wp-block-kadence-column{justify-content:start;}.kb-row-layout-id204_de5ab0-14 > .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-id204_de5ab0-14 > .kt-row-layout-overlay{opacity:0.30;}@media all and (max-width: 1024px){.kb-row-layout-id204_de5ab0-14 > .kt-row-column-wrap{grid-template-columns:minmax(0, 1fr);}}@media all and (max-width: 767px){.kb-row-layout-id204_de5ab0-14 > .kt-row-column-wrap{grid-template-columns:minmax(0, 1fr);}}<\/style><div class=\"kb-row-layout-wrap kb-row-layout-id204_de5ab0-14 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-column204_f4d6ec-0d > .kt-inside-inner-col,.kadence-column204_f4d6ec-0d > .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-column204_f4d6ec-0d > .kt-inside-inner-col{column-gap:var(--global-kb-gap-sm, 1rem);}.kadence-column204_f4d6ec-0d > .kt-inside-inner-col{flex-direction:column;}.kadence-column204_f4d6ec-0d > .kt-inside-inner-col > .aligncenter{width:100%;}.kadence-column204_f4d6ec-0d > .kt-inside-inner-col:before{opacity:0.3;}.kadence-column204_f4d6ec-0d{position:relative;}@media all and (max-width: 1024px){.kadence-column204_f4d6ec-0d > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}@media all and (max-width: 767px){.kadence-column204_f4d6ec-0d > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}<\/style>\n<div class=\"wp-block-kadence-column kadence-column204_f4d6ec-0d\"><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    --bg-light: #F8F9FA;\n  }\n  *, *::before, *::after { box-sizing: border-box;}\n  html, body {\n    overflow-x: hidden;\n    max-width: 100%;\n  }\n  body { \n    font-family: 'Sora', sans-serif; \n    color: var(--text-dark); \n    background: #fff; \n    min-height: 100vh; \n  }\n\n  \/* \u2500\u2500 PAGE WRAPPER \u2500\u2500 *\/\n  .page-wrapper {\n    max-width: 100%;\n    overflow-x: hidden;\n  }\n\n  \/* \u2500\u2500 PAGE TITLE \u2500\u2500 *\/\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  \/* \u2500\u2500 FILTER CARD \u2500\u2500 *\/\n  .filter-card { \n    background: transparent; \n    border: none; \n    border-radius: 0; \n    padding: 0; \n    margin-bottom: 24px; \n  }\n\n  .filter-row {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 12px;\n    align-items: center;\n    max-width: 100%;\n  }\n  .filter-row + .filter-row { margin-top: 14px; }\n\n  \/* custom select wrapper *\/\n  .sel-wrap { position: relative; flex: 1 1 160px; min-width: 140px; max-width: 220px; }\n  .sel-wrap select {\n    width: 100%; \n    appearance: none; \n    -webkit-appearance: none;\n    padding: 9px 32px 9px 14px; \n    border: 1px solid #DDE2E8; \n    border-radius: 7px;\n    font-family: 'Sora', sans-serif; \n    font-size: 12px; \n    color: var(--text-dark);\n    background: #fff; \n    cursor: pointer; \n    outline: none;\n  }\n  .sel-wrap select:focus { border-color: var(--primary-blue); }\n  .sel-wrap::after {\n    content: ''; \n    position: absolute; \n    right: 12px; \n    top: 50%; \n    transform: translateY(-50%);\n    width: 0; height: 0; \n    border-left: 5px solid transparent; \n    border-right: 5px solid transparent;\n    border-top: 5px solid #8D8C9C; \n    pointer-events: none;\n  }\n\n  \/* \u2500\u2500 CUSTOM MULTI-SELECT \u2500\u2500 *\/\n  .cs-wrap {\n    position: relative;\n    flex: 1 1 160px;\n    min-width: 140px;\n    max-width: 220px;\n  }\n  .cs-wrap.wide { max-width: 240px; }\n  .custom-select-trigger {\n    height: 37px;\n    padding: 0 32px 0 14px;\n    border: 1px solid #DDE2E8;\n    background-color: #fff;\n    border-radius: 7px;\n    font-family: 'Sora', sans-serif;\n    font-size: 12px;\n    color: var(--text-dark);\n    cursor: pointer;\n    display: flex;\n    align-items: center;\n    box-sizing: border-box;\n    width: 100%;\n    user-select: none;\n    position: relative;\n    transition: border-color 0.2s;\n    overflow: hidden;\n    white-space: nowrap;\n  }\n  .custom-select-trigger:hover { border-color: var(--primary-blue); }\n  .cs-wrap.open .custom-select-trigger {\n    border-color: var(--primary-blue);\n    border-radius: 7px 7px 0 0;\n    box-shadow: 0 0 0 3px rgba(1,128,255,0.1);\n  }\n  .custom-select-trigger.placeholder-shown { color: #8D8C9C; }\n  .custom-select-trigger .cs-arrow {\n    position: absolute;\n    right: 10px;\n    top: 50%;\n    transform: translateY(-50%);\n    pointer-events: none;\n    color: #8D8C9C;\n    transition: transform 0.2s;\n    flex-shrink: 0;\n  }\n  .cs-wrap.open .custom-select-trigger .cs-arrow { transform: translateY(-50%) rotate(180deg); }\n\n  .cs-label {\n    display: flex;\n    flex-wrap: nowrap;\n    align-items: center;\n    gap: 6px;\n    overflow-x: auto;\n    overflow-y: hidden;\n    white-space: nowrap;\n    width: 100%;\n    min-width: 0;\n    scrollbar-width: thin;\n    -ms-overflow-style: auto;\n  }\n  .cs-label::-webkit-scrollbar { display: block; height: 4px; }\n  .cs-label::-webkit-scrollbar-thumb { background: #ccc; border-radius: 4px; }\n\n  .custom-select-dropdown {\n    display: none;\n    position: absolute;\n    top: 100%;\n    left: 0;\n    right: 0;\n    background: #fff;\n    border: 1px solid var(--primary-blue);\n    border-top: none;\n    border-radius: 0 0 7px 7px;\n    z-index: 9999;\n    overflow-y: auto;\n    max-height: 250px;\n    box-shadow: 0 4px 12px rgba(0,0,0,0.10);\n  }\n  .cs-wrap.open .custom-select-dropdown { display: block; }\n\n  .cs-chips {\n    display: flex;\n    align-items: center;\n    gap: 4px;\n  }\n  .cs-chip {\n    display: inline-flex;\n    align-items: center;\n    background-color: #e8f0fe;\n    border: 1px solid #d2e3fc;\n    border-radius: 4px;\n    padding: 0 8px;\n    font-size: 12px;\n    color: #1967d2;\n    gap: 4px;\n    white-space: nowrap;\n    flex-shrink: 0;\n    height: 26px;\n    margin: 0;\n  }\n  .cs-chip-remove {\n    cursor: pointer;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    width: 14px;\n    height: 14px;\n    border-radius: 50%;\n    color: #5f6368;\n    font-weight: bold;\n    transition: background 0.2s;\n  }\n  .cs-chip-remove:hover { background: #dadce0; color: #202124; }\n\n  .custom-select-option {\n    padding: 8px 12px;\n    font-size: 12px;\n    font-family: 'Sora', sans-serif;\n    color: var(--text-dark);\n    cursor: pointer;\n    white-space: nowrap;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    line-height: 1.4;\n    display: flex;\n    align-items: center;\n    gap: 10px;\n  }\n  .custom-select-option:hover { background: #f0f6ff; color: var(--primary-blue); }\n  .custom-select-option.selected { background: #e8f1ff; color: var(--primary-blue); font-weight: 500; }\n  .custom-select-option .cs-checkbox {\n    width: 16px;\n    height: 16px;\n    border: 1px solid #ced4da;\n    border-radius: 3px;\n    background: #fff;\n    position: relative;\n    flex-shrink: 0;\n    display: block;\n  }\n  .custom-select-option.selected .cs-checkbox {\n    background: var(--primary-blue);\n    border-color: var(--primary-blue);\n  }\n  .custom-select-option.selected .cs-checkbox::after {\n    content: '';\n    position: absolute;\n    left: 5px;\n    top: 2px;\n    width: 4px;\n    height: 8px;\n    border: solid white;\n    border-width: 0 2px 2px 0;\n    transform: rotate(45deg);\n  }\n  .custom-select-option.placeholder-option { color: #999; }\n  .custom-select-option.placeholder-option .cs-checkbox { display: none; }\n\n  .custom-select-search {\n    padding: 8px;\n    border-bottom: 1px solid #EFEFEF;\n    position: sticky;\n    top: 0;\n    background: #fff;\n    z-index: 10;\n  }\n  .custom-select-search input {\n    width: 100%;\n    height: 30px;\n    padding: 0 10px;\n    border: 1px solid #DDE2E8;\n    border-radius: 4px;\n    font-family: 'Sora', sans-serif;\n    font-size: 11px;\n    outline: none;\n    box-sizing: border-box;\n  }\n  .custom-select-search input:focus { border-color: var(--primary-blue); }\n\n  \/* search input *\/\n  .search-box {\n    flex: 1 1 180px; \n    min-width: 140px; \n    max-width: 260px;\n    padding: 9px 14px; \n    border: 1px solid #DDE2E8; \n    border-radius: 7px;\n    font-family: 'Sora', sans-serif; \n    font-size: 12px; \n    color: var(--text-dark); \n    outline: none;\n  }\n  .search-box:focus { border-color: var(--primary-blue); }\n  .search-box::placeholder { color: #bbb; }\n\n  \/* buttons *\/\n  .btn { \n    padding: 9px 22px; \n    border-radius: 7px; \n    font-family: 'Sora', sans-serif; \n    font-size: 12px; \n    font-weight: 400; \n    cursor: pointer; \n    border: none; \n    transition: background 0.2s; \n    white-space: nowrap; \n  }\n  .btn-clear { background: #EBF2FF; color: var(--primary-blue); }\n  .btn-clear:hover { background: #d6e8ff; }\n  .btn-primary { background: var(--primary-blue); color: #fff; }\n  .btn-primary:hover { background: var(--hover-blue); }\n  .btn-csv { background: var(--primary-blue); color: #fff; }\n  .btn-csv:hover { background: var(--hover-blue); }\n  .btn-group { display: flex; gap: 10px; align-items: center; }\n  .btn-group-right { margin-left: auto; }\n\n  \/* \u2500\u2500 TABS \u2500\u2500 *\/\n  .tab-bar { \n    padding-top: 15px;\n    display: flex; \n    flex-wrap: wrap;\n    border-bottom: 2px solid var(--border-color); \n    margin-bottom: 0; \n    gap: 0; \n    max-width: 100%;\n  }\n  .tab-item {\n    padding: 10px 16px; \n    font-size: 14px; \n    font-weight: 400; \n    color: var(--text-muted);\n    cursor: pointer; \n    position: relative; \n    user-select: none;\n  }\n  .tab-item.active { color: #212529; font-weight: 500; }\n  .tab-item.active::after {\n    content: ''; \n    position: absolute; \n    bottom: -3px; \n    left: 0; \n    right: 0; \n    height: 3px; \n    background: var(--primary-blue);\n  }\n  .tab-item:hover:not(.active) { color:  var(--text-muted) }\n\n  \/* \u2500\u2500 TABLE WRAPPER \u2500\u2500 *\/\n  .table-wrapper { \n    overflow-x: hidden;\n    max-width: 100%;\n    margin-top: 0; \n    border: 1px solid var(--border-color); \n    border-top: none; \n    border-radius: 0 0 10px 10px; \n  }\n  table {\n    width: 100%;\n    max-width: 100%;\n    table-layout: fixed;\n    border-collapse: collapse;\n    font-size: 11px;\n  }\n  thead tr { background: #f5f5f5; }\n  thead th {\n    padding: 8px 6px; \n    font-weight: 500; \n    font-size: 11px; \n    text-transform: uppercase;\n    letter-spacing: 0.2px; \n    color: #212529; \n    border-bottom: 1px solid var(--border-color);\n    text-align: left;\n    white-space: normal;\n    word-break: break-word;\n    line-height: 1.3;\n  }\n  tbody tr { border-bottom: 1px solid var(--border-color); transition: background 0.15s; }\n  tbody tr:last-child { border-bottom: none; }\n  tbody tr:hover { background: #f5f5f5; }\n  tbody td { \n    padding: 8px 6px; \n    font-weight: 400;\n    color: var(--text-muted); \n    vertical-align: middle; \n    white-space: normal;\n    word-break: break-word;\n    overflow-wrap: anywhere;\n    line-height: 1.35;\n  }\n\n  \/* \u2500\u2500 COLORED BADGES \u2500\u2500 *\/\n  .badge-mi { \n    background: #FFF8E1; \n    color: #B7791F; \n    border: 1px solid #F6E05E; \n    border-radius: 6px; \n    padding: 2px 6px; \n    font-size: 11px; \n    font-weight: 400; \n    display: inline-block;\n    white-space: normal;\n    word-break: break-word;\n  }\n  .badge-cr { \n    background: #FFF0F0; \n    color: #C53030; \n    border: 1px solid #FEB2B2; \n    border-radius: 6px; \n    padding: 2px 6px; \n    font-size: 11px; \n    font-weight: 400; \n    display: inline-block;\n    white-space: normal;\n    word-break: break-word;\n  }\n  .badge-zte { \n    background: #FFF0F6; \n    color: #B83280; \n    border: 1px solid #FBB6CE; \n    border-radius: 6px; \n    padding: 2px 6px; \n    font-size: 11px; \n    font-weight: 400; \n    display: inline-block;\n    white-space: normal;\n    word-break: break-word;\n  }\n\n  \/* name pill *\/\n  .name-pill { \n    background: #E0F7F0; \n    color: #0D6E52; \n    border-radius: 20px; \n    padding: 3px 10px; \n    font-size: 11px; \n    font-weight: 400; \n    display: inline-block;\n    white-space: normal;\n    word-break: break-word;\n  }\n\n  \/* quality color *\/\n  .q-high { color: #1A7A4A; font-weight: 400; }\n  .q-mid  { color: #B7791F; font-weight: 400; }\n  .q-low  { color: #C53030; font-weight: 400; }\n\n  \/* \u2500\u2500 ENTRY INFO \u2500\u2500 *\/\n  .table-footer {\n    display: flex;\n    justify-content: flex-end;\n    align-items: center;\n    padding: 16px 4px;\n    font-size: 12px;\n    color: var(--text-muted);\n  }\n\n  \/* loading \/ empty *\/\n  .state-row td { text-align: center; padding: 40px; color: var(--text-muted); font-size: 13px; }\n  .spinner { \n    display: inline-block; \n    width: 18px; height: 18px; \n    border: 2px solid #ddd; \n    border-top-color: var(--primary-blue); \n    border-radius: 50%; \n    animation: spin 0.7s linear infinite; \n    vertical-align: middle; \n    margin-right: 6px; \n  }\n  @keyframes spin { to { transform: rotate(360deg); } }\n\n  \/* \u2500\u2500 RESPONSIVE \u2500\u2500 *\/\n  @media (max-width: 900px) {\n    .page-wrapper { padding: 20px 16px 40px; }\n    .btn-group { margin-left: 0; }\n    .filter-row { gap: 8px; }\n    .sel-wrap { max-width: 100%; }\n    .tab-item { padding: 10px 14px; font-size: 13px; }\n  }\n  @media (max-width: 600px) {\n    .tab-bar { overflow-x: auto; }\n    .btn-group { flex-wrap: wrap; }\n  }\n<\/style>\n\n\n<div class=\"page-wrapper\">\n\n  <!-- TITLE + dynamic tab title -->\n  <div class=\"page-title\" id=\"pageTitle\">EXECUTIVE WISE-QUALITY<\/div>\n\n  <!-- FILTER CARD (dynamic content per tab) -->\n  <div class=\"filter-card\" id=\"filterCard\"><\/div>\n\n  <!-- TABS -->\n  <div class=\"tab-bar\">\n    <div class=\"tab-item active\" data-tab=\"executive\" onclick=\"switchTab('executive')\">Executive Wise<\/div>\n    <div class=\"tab-item\" data-tab=\"qc_executive\" onclick=\"switchTab('qc_executive')\">QC Executive Wise<\/div>\n    <div class=\"tab-item\" data-tab=\"client\" onclick=\"switchTab('client')\">Client Wise<\/div>\n    <div class=\"tab-item\" data-tab=\"account\" onclick=\"switchTab('account')\">Account Wise<\/div>\n  <\/div>\n\n  <!-- TABLE -->\n  <div class=\"table-wrapper\">\n    <table id=\"dataTable\">\n      <thead id=\"tableHead\"><\/thead>\n      <tbody id=\"tableBody\"><tr class=\"state-row\"><td colspan=\"20\"><span class=\"spinner\"><\/span>Loading\u2026<\/td><\/tr><\/tbody>\n    <\/table>\n  <\/div>\n\n  <div class=\"table-footer\">\n    <div id=\"entryInfo\">Showing 0 entries<\/div>\n  <\/div>\n<\/div>\n\n<script>\n\/\/CONFIGURATION\nconst BASE_URL = 'https:\/\/api-ph-portal.zyneventures.com\/api\/v1';\nconst ENDPOINTS = {\n  TABLE_LIST:     `${BASE_URL}\/accountandbenchmark\/tableList`,\n  INDEX_CLIENT:   `${BASE_URL}\/quality\/index-client`,\n  INDEX_ACCOUNT:  `${BASE_URL}\/quality\/index-account`,\n  INDEX_EXECUTIVE:`${BASE_URL}\/quality\/index-executive`,\n  QC_EXECUTIVE:   `${BASE_URL}\/quality\/qc-index-executive`,\n  CLIENT_ALL:     `${BASE_URL}\/client\/all`,\n};\n\nconst MONTHS = ['January','February','March','April','May','June','July','August','September','October','November','December'];\nconst CUR_YEAR = new Date().getFullYear();\nconst YEARS = Array.from({length:10},(_,i)=>CUR_YEAR-i);\n\n\/\/ STATE\nlet activeTab       = 'executive';\nlet accountList     = [];  \/\/ from tableList\nlet executiveList   = [];  \/\/ from index-executive\nlet qcExecutiveList = [];  \/\/ from qc-index-executive\nlet filterState     = {};  \/\/ dynamic per tab\n\nfunction getDefaultFilterState(){\n  return {\n    month: '',\n    year: '',\n  };\n}\n\n\/\/ AUTH HELPER\nfunction getHeaders(){\n  const token = localStorage.getItem('TOKEN') || '';\n  return { 'Content-Type':'application\/json', 'Accept':'application\/json', 'Authorization':`Bearer ${token}` };\n}\n\nfunction extractRows(payload){\n  if (Array.isArray(payload)) return payload;\n  if (Array.isArray(payload?.data)) return payload.data;\n  if (Array.isArray(payload?.data?.data)) return payload.data.data;\n  if (Array.isArray(payload?.data?.rows)) return payload.data.rows;\n  if (Array.isArray(payload?.rows)) return payload.rows;\n  return [];\n}\n\nfunction extractPagination(payload, fallbackLimit, fallbackTotal){\n  const paginationInfo = payload?.pagination || payload?.data?.pagination || {};\n  const limitValue = Number(\n    paginationInfo.limit || payload?.limit || payload?.data?.limit || fallbackLimit || 10\n  );\n  const safeLimit = Number.isFinite(limitValue) && limitValue > 0 ? limitValue : (fallbackLimit || 10);\n\n  const totalValue = Number(\n    paginationInfo.total || payload?.total || payload?.data?.total || fallbackTotal || 0\n  );\n  const safeTotal = Number.isFinite(totalValue) && totalValue >= 0 ? totalValue : (fallbackTotal || 0);\n\n  const pagesValue = Number(\n    paginationInfo.pages || payload?.pages || payload?.data?.pages || (safeTotal > 0 ? Math.ceil(safeTotal \/ safeLimit) : 1)\n  );\n  const safePages = Number.isFinite(pagesValue) && pagesValue > 0 ? pagesValue : 1;\n\n  const pageValue = Number(\n    paginationInfo.page || payload?.page || payload?.data?.page || 1\n  );\n  const safePage = Number.isFinite(pageValue) && pageValue > 0 ? pageValue : 1;\n\n  return { page: safePage, limit: safeLimit, total: safeTotal, pages: safePages };\n}\n\nfunction hasRequestFilters(payload = {}){\n  return Object.keys(payload).some(key => !['page', 'limit'].includes(key) && payload[key] !== '' && payload[key] !== null && payload[key] !== undefined);\n}\n\nfunction hasMeaningfulResponse(payload){\n  const rows = extractRows(payload);\n  if (rows.length > 0) return true;\n  const paginationInfo = extractPagination(payload, 500, rows.length);\n  return paginationInfo.total > 0;\n}\n\nfunction requestViaXhr(method, url, payload){\n  return new Promise((resolve, reject) => {\n    const xhr = new XMLHttpRequest();\n    xhr.open(method, url, true);\n\n    Object.entries(getHeaders()).forEach(([key, value]) => {\n      xhr.setRequestHeader(key, value);\n    });\n\n    xhr.onload = () => {\n      if (xhr.status >= 200 && xhr.status < 300) {\n        try {\n          resolve(JSON.parse(xhr.responseText || '{}'));\n        } catch (error) {\n          reject(error);\n        }\n      } else {\n        reject(new Error(`HTTP ${xhr.status}`));\n      }\n    };\n\n    xhr.onerror = () => reject(new Error('Network request failed'));\n    xhr.send(payload ? JSON.stringify(payload) : null);\n  });\n}\n\nasync function requestWithBodyFallback(url, payload = {}){\n  \/\/ Some of these APIs are implemented as GET endpoints that still expect a JSON body.\n  const shouldRequireDataBeforeAccepting = hasRequestFilters(payload);\n\n  const shouldAcceptResponse = (responsePayload) => {\n    if (!shouldRequireDataBeforeAccepting) return true;\n    return hasMeaningfulResponse(responsePayload);\n  };\n\n  const buildQueryUrl = () => {\n    const params = new URLSearchParams();\n    Object.entries(payload).forEach(([k, v]) => {\n      if (v !== undefined && v !== null && v !== '') params.set(k, String(v));\n    });\n    return params.toString() ? `${url}?${params.toString()}` : url;\n  };\n\n  try {\n    const xhrPayload = await requestViaXhr('GET', url, payload);\n    if (shouldAcceptResponse(xhrPayload)) return xhrPayload;\n  } catch (e) {}\n\n  try {\n    const postRes = await fetch(url, {\n      method: 'POST',\n      headers: getHeaders(),\n      body: JSON.stringify(payload)\n    });\n    if (postRes.ok) {\n      const postPayload = await postRes.json();\n      if (shouldAcceptResponse(postPayload)) return postPayload;\n    }\n  } catch (e) {}\n\n  const getUrl = buildQueryUrl();\n  const getRes = await fetch(getUrl, { method: 'GET', headers: getHeaders() });\n  return await getRes.json();\n}\n\n\/\/ BOOT\nasync function init(){\n  await loadAccountList();\n  await loadExecutiveLists();\n  switchTab('executive');\n}\n\nasync function loadAccountList(){\n  try {\n    const r = await fetch(\n      ENDPOINTS.TABLE_LIST, { \n        method:'GET', \n        headers:getHeaders() \n      });\n    const d = await r.json();\n    accountList = extractRows(d);\n  } catch(e){ accountList = []; }\n}\n\nasync function loadExecutiveLists(){\n  try {\n    const execUrl = `${ENDPOINTS.INDEX_EXECUTIVE}?page=1&limit=500`;\n    const qcUrl = `${ENDPOINTS.QC_EXECUTIVE}?page=1&limit=500`;\n    const [re, rq] = await Promise.all([\n      fetch(execUrl, { method: 'GET', headers: getHeaders() }),\n      fetch(qcUrl, { method: 'GET', headers: getHeaders() })\n    ]);\n    const de = await re.json();\n    const dq = await rq.json();\n    executiveList   = extractRows(de);\n    qcExecutiveList = extractRows(dq);\n  } catch(e){ executiveList=[]; qcExecutiveList=[]; }\n}\n\n\/\/ TAB SWITCHING\nfunction switchTab(tab){\n  activeTab = tab;\n  filterState = getDefaultFilterState();\n\n  document.querySelectorAll('.tab-item').forEach(t=>t.classList.toggle('active', t.dataset.tab===tab));\n\n  const titles = {\n    executive:    'EXECUTIVE WISE-QUALITY',\n    qc_executive: 'QC EXECUTIVE WISE-QUALITY',\n    client:       'CLIENT WISE-QUALITY',\n    account:      'ACCOUNT WISE-QUALITY',\n  };\n  document.getElementById('pageTitle').textContent = titles[tab];\n\n  renderFilterForm();\n  fetchData();\n}\n\n\/\/ FILTER FORMS\nfunction monthSelHTML(id='fMonth'){\n  return `<div class=\"sel-wrap\"><select id=\"${id}\">\n    <option value=\"\">Select Month<\/option>\n    ${MONTHS.map((m,i)=>`<option value=\"${i+1}\">${m}<\/option>`).join('')}\n  <\/select><\/div>`;\n}\nfunction yearSelHTML(id='fYear'){\n  return `<div class=\"sel-wrap\"><select id=\"${id}\">\n    <option value=\"\">Year<\/option>\n    ${YEARS.map(y=>`<option value=\"${y}\">${y}<\/option>`).join('')}\n  <\/select><\/div>`;\n}\nfunction accountSelHTML(id='fAccount'){\n  const opts = accountList.map(a=>`\n    <div class=\"custom-select-option\" data-value=\"${a.id}\" onclick=\"csSelect('${id}', '${String(a.id).replace(\/'\/g,\"\\\\'\")}', this, true)\">\n      <div class=\"cs-checkbox\"><\/div><span class=\"opt-label\">${a.name}<\/span>\n    <\/div>`).join('');\n  return `<div class=\"cs-wrap wide\" id=\"csWrapper_${id}\">\n    <input type=\"hidden\" id=\"${id}\" value=\"\">\n    <div class=\"custom-select-trigger placeholder-shown\" onclick=\"csToggle('${id}', event)\">\n      <span class=\"cs-label\">Select Accounts<\/span>\n      <svg class=\"cs-arrow\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" viewBox=\"0 0 24 24\"><polyline points=\"6 9 12 15 18 9\"\/><\/svg>\n    <\/div>\n    <div class=\"custom-select-dropdown\" id=\"csDrop_${id}\">\n      <div class=\"custom-select-search\"><input type=\"text\" placeholder=\"Search...\" onkeyup=\"csFilter('${id}', this.value)\" onclick=\"event.stopPropagation()\"><\/div>\n      <div class=\"custom-select-options\">\n        <div class=\"custom-select-option placeholder-option\" data-value=\"\" onclick=\"csSelect('${id}', '', this, true)\"><span class=\"opt-label\">Select Accounts<\/span><\/div>\n        ${opts}\n      <\/div>\n    <\/div>\n  <\/div>`;\n}\nfunction clientSelHTML(id='fClient', clients=[]){\n  const opts = clients.map(c=>`\n    <div class=\"custom-select-option\" data-value=\"${c.client_id}\" onclick=\"csSelect('${id}', '${String(c.client_id).replace(\/'\/g,\"\\\\'\")}', this, true)\">\n      <div class=\"cs-checkbox\"><\/div><span class=\"opt-label\">${c.client_name}<\/span>\n    <\/div>`).join('');\n  return `<div class=\"cs-wrap wide\" id=\"csWrapper_${id}\">\n    <input type=\"hidden\" id=\"${id}\" value=\"\">\n    <div class=\"custom-select-trigger placeholder-shown\" onclick=\"csToggle('${id}', event)\">\n      <span class=\"cs-label\">Select Clients<\/span>\n      <svg class=\"cs-arrow\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" viewBox=\"0 0 24 24\"><polyline points=\"6 9 12 15 18 9\"\/><\/svg>\n    <\/div>\n    <div class=\"custom-select-dropdown\" id=\"csDrop_${id}\">\n      <div class=\"custom-select-search\"><input type=\"text\" placeholder=\"Search...\" onkeyup=\"csFilter('${id}', this.value)\" onclick=\"event.stopPropagation()\"><\/div>\n      <div class=\"custom-select-options\">\n        <div class=\"custom-select-option placeholder-option\" data-value=\"\" onclick=\"csSelect('${id}', '', this, true)\"><span class=\"opt-label\">Select Clients<\/span><\/div>\n        ${opts}\n      <\/div>\n    <\/div>\n  <\/div>`;\n}\nfunction executiveSelHTML(id='fExecutive', list=[]){\n  const opts = list.map(e=>`\n    <div class=\"custom-select-option\" data-value=\"${e.user_id}\" onclick=\"csSelect('${id}', '${String(e.user_id).replace(\/'\/g,\"\\\\'\")}', this, true)\">\n      <div class=\"cs-checkbox\"><\/div><span class=\"opt-label\">${e.pesudo_name}<\/span>\n    <\/div>`).join('');\n  return `<div class=\"cs-wrap\" id=\"csWrapper_${id}\">\n    <input type=\"hidden\" id=\"${id}\" value=\"\">\n    <div class=\"custom-select-trigger placeholder-shown\" onclick=\"csToggle('${id}', event)\">\n      <span class=\"cs-label\">Select Executive<\/span>\n      <svg class=\"cs-arrow\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" viewBox=\"0 0 24 24\"><polyline points=\"6 9 12 15 18 9\"\/><\/svg>\n    <\/div>\n    <div class=\"custom-select-dropdown\" id=\"csDrop_${id}\">\n      <div class=\"custom-select-search\"><input type=\"text\" placeholder=\"Search...\" onkeyup=\"csFilter('${id}', this.value)\" onclick=\"event.stopPropagation()\"><\/div>\n      <div class=\"custom-select-options\">\n        <div class=\"custom-select-option placeholder-option\" data-value=\"\" onclick=\"csSelect('${id}', '', this, true)\"><span class=\"opt-label\">Select Executive<\/span><\/div>\n        ${opts}\n      <\/div>\n    <\/div>\n  <\/div>`;\n}\nfunction qcExecutiveSelHTML(id='fQCExecutive', list=[]){\n  const opts = list.map(e=>`\n    <div class=\"custom-select-option\" data-value=\"${e.user_id}\" onclick=\"csSelect('${id}', '${String(e.user_id).replace(\/'\/g,\"\\\\'\")}', this, true)\">\n      <div class=\"cs-checkbox\"><\/div><span class=\"opt-label\">${e.pesudo_name}<\/span>\n    <\/div>`).join('');\n  return `<div class=\"cs-wrap\" id=\"csWrapper_${id}\">\n    <input type=\"hidden\" id=\"${id}\" value=\"\">\n    <div class=\"custom-select-trigger placeholder-shown\" onclick=\"csToggle('${id}', event)\">\n      <span class=\"cs-label\">Select QC Executive<\/span>\n      <svg class=\"cs-arrow\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" viewBox=\"0 0 24 24\"><polyline points=\"6 9 12 15 18 9\"\/><\/svg>\n    <\/div>\n    <div class=\"custom-select-dropdown\" id=\"csDrop_${id}\">\n      <div class=\"custom-select-search\"><input type=\"text\" placeholder=\"Search...\" onkeyup=\"csFilter('${id}', this.value)\" onclick=\"event.stopPropagation()\"><\/div>\n      <div class=\"custom-select-options\">\n        <div class=\"custom-select-option placeholder-option\" data-value=\"\" onclick=\"csSelect('${id}', '', this, true)\"><span class=\"opt-label\">Select QC Executive<\/span><\/div>\n        ${opts}\n      <\/div>\n    <\/div>\n  <\/div>`;\n}\nfunction actionBtnsHTML(leftAlign=false){\n  const cls = leftAlign ? 'btn-group' : 'btn-group btn-group-right';\n  return `<div class=\"${cls}\">\n    <button class=\"btn btn-clear\" onclick=\"clearFilters()\">Clear<\/button>\n    <button class=\"btn btn-primary\" onclick=\"applyFilters()\">Filter<\/button>\n    <button class=\"btn btn-csv\" onclick=\"downloadCSV()\">Download CSV<\/button>\n  <\/div>`;\n}\n\nasync function renderFilterForm(){\n  const card = document.getElementById('filterCard');\n  if(activeTab==='executive'){\n    card.innerHTML = `\n      <div class=\"filter-row\">\n        ${monthSelHTML()} ${yearSelHTML()}\n        ${executiveSelHTML('fExecutive', executiveList)}\n        ${actionBtnsHTML(false)}\n      <\/div>`;\n  } else if(activeTab==='qc_executive'){\n    card.innerHTML = `\n      <div class=\"filter-row\">\n        ${monthSelHTML()} ${yearSelHTML()}\n        ${qcExecutiveSelHTML('fQCExecutive', qcExecutiveList)}\n        ${actionBtnsHTML(false)}\n      <\/div>`;\n  } else if(activeTab==='client'){\n    const clients = await fetchClientList();\n    card.innerHTML = `\n      <div class=\"filter-row\">\n        ${monthSelHTML()} ${yearSelHTML()}\n        ${clientSelHTML('fClient', clients)}\n        ${actionBtnsHTML(false)}\n      <\/div>`;\n  } else if(activeTab==='account'){\n    const clients = await fetchClientList();\n    card.innerHTML = `\n      <div class=\"filter-row\">\n        ${monthSelHTML()} ${yearSelHTML()}\n        ${accountSelHTML()} ${clientSelHTML('fClient', clients)}\n      <\/div>\n      <div class=\"filter-row\" style=\"margin-top:12px;\">\n        ${actionBtnsHTML(true)}\n      <\/div>`;\n  }\n  restoreFilterState();\n}\n\nasync function fetchClientList(){\n  try {\n    const r0 = await fetch(ENDPOINTS.CLIENT_ALL, { method: 'GET', headers: getHeaders() });\n    const d0 = await r0.json();\n    const pg = extractPagination(d0);\n    let rows = extractRows(d0);\n\n    if (pg.total > rows.length) {\n      const r = await fetch(`${ENDPOINTS.CLIENT_ALL}?limit=${pg.total}`, { method: 'GET', headers: getHeaders() });\n      const d = await r.json();\n      rows = extractRows(d);\n    }\n    const seen = new Set();\n    return rows.filter(r => {\n      const cId = r.client_id || r.id;\n      if (seen.has(cId)) return false;\n      seen.add(cId);\n      return true;\n    }).map(r => ({\n      client_id: r.client_id || r.id,\n      client_name: r.client_name || r.name\n    }));\n  } catch(e){ return []; }\n}\n\n\/\/ \u2500\u2500 CUSTOM MULTI-SELECT LOGIC \u2500\u2500\nfunction csToggle(selectId, e) {\n  e.stopPropagation();\n  const wrapper = document.getElementById('csWrapper_' + selectId);\n  const isOpen = wrapper.classList.contains('open');\n  document.querySelectorAll('.cs-wrap.open').forEach(w => { if (w !== wrapper) w.classList.remove('open'); });\n  wrapper.classList.toggle('open');\n  if (!isOpen) {\n    const searchInput = wrapper.querySelector('.custom-select-search input');\n    if (searchInput) { searchInput.value = ''; csFilter(selectId, ''); setTimeout(() => searchInput.focus(), 50); }\n  }\n}\nfunction csFilter(selectId, query) {\n  const drop = document.getElementById('csDrop_' + selectId);\n  const options = drop.querySelectorAll('.custom-select-option');\n  const q = query.toLowerCase();\n  options.forEach(opt => {\n    const text = opt.textContent.toLowerCase();\n    opt.style.display = (text.includes(q) || opt.classList.contains('placeholder-option')) ? '' : 'none';\n  });\n}\nfunction csSelect(selectId, value, optionEl, multi) {\n  const wrapper = document.getElementById('csWrapper_' + selectId);\n  const input   = document.getElementById(selectId);\n  const trigger = wrapper.querySelector('.custom-select-trigger');\n  const labelEl = trigger.querySelector('.cs-label');\n  if (value === '') {\n    input.value = '';\n    labelEl.innerHTML = optionEl.querySelector('.opt-label').textContent.trim();\n    trigger.classList.add('placeholder-shown');\n    wrapper.querySelectorAll('.custom-select-option').forEach(o => o.classList.remove('selected'));\n    if (!multi) wrapper.classList.remove('open');\n    return;\n  }\n  if (!multi) {\n    input.value = value;\n    labelEl.textContent = optionEl.querySelector('.opt-label').textContent.trim();\n    trigger.classList.remove('placeholder-shown');\n    wrapper.querySelectorAll('.custom-select-option').forEach(o => o.classList.remove('selected'));\n    optionEl.classList.add('selected');\n    wrapper.classList.remove('open');\n  } else {\n    let values = input.value ? input.value.split(',') : [];\n    const idx = values.indexOf(String(value));\n    if (idx > -1) { values.splice(idx, 1); optionEl.classList.remove('selected'); }\n    else { values.push(String(value)); optionEl.classList.add('selected'); }\n    input.value = values.join(',');\n    if (values.length === 0) {\n      const ph = wrapper.querySelector('.placeholder-option');\n      labelEl.innerHTML = ph ? ph.querySelector('.opt-label').textContent.trim() : 'Select';\n      trigger.classList.add('placeholder-shown');\n    } else {\n      trigger.classList.remove('placeholder-shown');\n      const selectedLabels = Array.from(wrapper.querySelectorAll('.custom-select-option.selected'))\n        .map(o => ({ val: o.dataset.value, label: o.querySelector('.opt-label').textContent.trim() }));\n      labelEl.innerHTML = `<div class=\"cs-chips\">${selectedLabels.map(item =>\n        `<div class=\"cs-chip\"><span>${item.label}<\/span><span class=\"cs-chip-remove\" onclick=\"csRemoveChip(event,'${selectId}','${item.val}')\">&times;<\/span><\/div>`\n      ).join('')}<\/div>`;\n    }\n  }\n}\nfunction csRemoveChip(e, selectId, value) {\n  e.stopPropagation();\n  const wrapper = document.getElementById('csWrapper_' + selectId);\n  const optionEl = wrapper.querySelector(`.custom-select-option[data-value=\"${value}\"]`);\n  if (optionEl) csSelect(selectId, value, optionEl, true);\n}\ndocument.addEventListener('click', function(e){\n  if (!e.target.closest('.cs-wrap')) {\n    document.querySelectorAll('.cs-wrap.open').forEach(w => w.classList.remove('open'));\n  }\n});\n\nfunction restoreFilterState(){\n  if(filterState.month){     const el=document.getElementById('fMonth');      if(el) el.value=filterState.month; }\n  if(filterState.year){      const el=document.getElementById('fYear');       if(el) el.value=filterState.year;  }\n  if(filterState.executive){ const el=document.getElementById('fExecutive');  if(el) el.value=filterState.executive; }\n  if(filterState.qcExec){    const el=document.getElementById('fQCExecutive');if(el) el.value=filterState.qcExec; }\n  if(filterState.client){    const el=document.getElementById('fClient');     if(el) el.value=filterState.client; }\n  if(filterState.account){   const el=document.getElementById('fAccount');    if(el) el.value=filterState.account; }\n}\nfunction readFilterState(){\n  const v = id=>{ const el=document.getElementById(id); return el?el.value:''; };\n  filterState = {\n    month:     v('fMonth'),\n    year:      v('fYear'),\n    executive: v('fExecutive'),\n    qcExec:    v('fQCExecutive'),\n    client:    v('fClient'),\n    account:   v('fAccount'),\n  };\n}\nfunction applyFilters(){ readFilterState(); fetchData(); }\nfunction clearFilters(){\n  filterState=getDefaultFilterState();\n  ['fMonth','fYear'].forEach(id=>{ const el=document.getElementById(id); if(el) el.value=''; });\n  ['fExecutive','fQCExecutive','fClient','fAccount'].forEach(id=>{\n    const input = document.getElementById(id);\n    if(input){ input.value=''; }\n    const wrapper = document.getElementById('csWrapper_'+id);\n    if(wrapper){\n      const trigger = wrapper.querySelector('.custom-select-trigger');\n      const labelEl = trigger ? trigger.querySelector('.cs-label') : null;\n      const ph = wrapper.querySelector('.placeholder-option');\n      if(labelEl && ph) labelEl.innerHTML = ph.querySelector('.opt-label').textContent.trim();\n      if(trigger) trigger.classList.add('placeholder-shown');\n      wrapper.querySelectorAll('.custom-select-option').forEach(o => o.classList.remove('selected'));\n    }\n  });\n  restoreFilterState();\n  fetchData();\n}\n\n\/\/ PARSE COMMA-SEPARATED IDS FROM MULTI-SELECT\nfunction parseIds(val){\n  if(!val) return [];\n  return val.split(',').map(v=>parseInt(v.trim())).filter(n=>!isNaN(n));\n}\nfunction idsPayload(ids){ return ids.length===1 ? ids[0] : ids; }\n\n\/\/ API FETCH\nasync function fetchData(){\n  setLoading();\n  try {\n    const rows = await fetchAllRowsForDownload();\n    renderTable(rows);\n  } catch(e){\n    console.error(e);\n    document.getElementById('tableBody').innerHTML = `<tr class=\"state-row\"><td colspan=\"20\">Error loading data. Please try again.<\/td><\/tr>`;\n    updateEntryInfo(0);\n  }\n}\n\nfunction setLoading(){\n  document.getElementById('tableBody').innerHTML = `<tr class=\"state-row\"><td colspan=\"20\"><span class=\"spinner\"><\/span>Loading\u2026<\/td><\/tr>`;\n}\n\n\/\/ TABLE HEADERS\nconst HEADS = {\n  executive: ['A. Manager','T. Lead','Pseudo Name','DOS Completed','QC Completed','QC %','Minor','Critical','ZTE','Total Errors','Minor WTG','Critical WTG','ZTE WTG','Total Error %','Quality'],\n  qc_executive: ['A. Manager','T. Lead','Pseudo Name','DOS Assign','DOS Completed','DOS %','Minor','Critical','ZTE','Total Errors','Minor WTG','Critical WTG','ZTE WTG','Total Error Ratio','General Error Ratio','Quality'],\n  client:  ['Client','Account','DOS Completed','QC Completed','QC %','Minor','Critical','ZTE','Total Errors','Minor WTG','Critical WTG','ZTE WTG','Total Error %','Quality'],\n  account: ['Client','Account','# of DOS','QC Completed','QC %'],\n};\n\nfunction renderTable(rows){\n  const safeRows = Array.isArray(rows) ? rows : [];\n  const heads = HEADS[activeTab];\n  document.getElementById('tableHead').innerHTML = `<tr>${heads.map(h=>`<th>${h}<\/th>`).join('')}<\/tr>`;\n\n  const search = (filterState.search||'').toLowerCase();\n  const filtered = search\n    ? safeRows.filter(r=>JSON.stringify(r).toLowerCase().includes(search))\n    : safeRows;\n\n  if(!filtered.length){\n    document.getElementById('tableBody').innerHTML = `<tr class=\"state-row\"><td colspan=\"${heads.length}\">No data found.<\/td><\/tr>`;\n    updateEntryInfo(0);\n    return;\n  }\n\n  updateEntryInfo(filtered.length);\n\n  let html='';\n  filtered.forEach(row=>{\n    if(activeTab==='executive'){\n      html+=`<tr>\n        <td>${row.manager||'-'}<\/td>\n        <td>${row.team_lead||'-'}<\/td>\n        <td><span class=\"name-pill\">${row.pesudo_name||'-'}<\/span><\/td>\n        <td>${row.dos_completed??row.assigned_dos??row.dos_assign??'-'}<\/td>\n        <td>${row.qc_completed??0}<\/td>\n        <td>${fmt(row.qc_percentage)}%<\/td>\n        <td><span class=\"badge-mi\">MI (${row.minor_errors??0})<\/span><\/td>\n        <td><span class=\"badge-cr\">CR (${row.critical_errors??0})<\/span><\/td>\n        <td><span class=\"badge-zte\">ZTE(${row.zte_errors??0})<\/span><\/td>\n        <td>${row.total_errors??0}<\/td>\n        <td>${fmt(row.minor_wtg)}<\/td>\n        <td>${fmt(row.critical_wtg)}<\/td>\n        <td>${fmt(row.zte_wtg)}<\/td>\n        <td>${fmt(row.total_error_percentage??row.total_error_ratio)}%<\/td>\n        <td class=\"${qClass(row.quality_percentage)}\">${fmt(row.quality_percentage)}%<\/td>\n      <\/tr>`;\n    } else if(activeTab==='qc_executive'){\n      html+=`<tr>\n        <td>${row.manager||'-'}<\/td>\n        <td>${row.team_lead||'-'}<\/td>\n        <td><span class=\"name-pill\">${row.pesudo_name||'-'}<\/span><\/td>\n        <td>${row.dos_assign??row.assigned_dos??'-'}<\/td>\n        <td>${row.dos_completed??'-'}<\/td>\n        <td>${fmt(row.dos_percentage??0)}%<\/td>\n        <td><span class=\"badge-mi\">MI (${row.minor_errors??0})<\/span><\/td>\n        <td><span class=\"badge-cr\">CR (${row.critical_errors??0})<\/span><\/td>\n        <td><span class=\"badge-zte\">ZTE(${row.zte_errors??0})<\/span><\/td>\n        <td>${row.total_errors??0}<\/td>\n        <td>${fmt(row.minor_wtg)}<\/td>\n        <td>${fmt(row.critical_wtg)}<\/td>\n        <td>${fmt(row.zte_wtg)}<\/td>\n        <td>${fmt(row.total_error_ratio??row.total_error_percentage)}<\/td>\n        <td>${fmt(row.general_error_ratio??0)}<\/td>\n        <td class=\"${qClass(row.quality_percentage)}\">${fmt(row.quality_percentage)}%<\/td>\n      <\/tr>`;\n    } else if(activeTab==='client'){\n      html+=`<tr>\n        <td>${row.name??row.client_name??row.clientName??'-'}<\/td>\n        <td>${row.account_name??row.accountName??row.account??'-'}<\/td>\n        <td>${row.completedDos??row.dos_completed??row.dosCompleted??row.no_of_dos??row.noOfDos??'-'}<\/td>\n        <td>${row.qcCompletedDos??row.qc_completed??row.qcCompleted??row.qc_dos??'-'}<\/td>\n        <td>${fmt(row.qc_percentage??row.qcPercentage??0)}%<\/td>\n        <td><span class=\"badge-mi\">MI (${row.MI??row.minor_errors??row.minorErrors??row.minor??0})<\/span><\/td>\n        <td><span class=\"badge-cr\">CR (${row.CR??row.critical_errors??row.criticalErrors??row.critical??0})<\/span><\/td>\n        <td><span class=\"badge-zte\">ZTE(${row.ZTE??row.zte_errors??row.zteErrors??row.zte??0})<\/span><\/td>\n        <td>${row.total_errors??row.totalErrors??row.total_error??0}<\/td>\n        <td>${fmt(row.minor_wtg??row.minorWtg??0)}<\/td>\n        <td>${fmt(row.critical_wtg??row.criticalWtg??0)}<\/td>\n        <td>${fmt(row.zte_wtg??row.zteWtg??0)}<\/td>\n        <td>${fmt(row.total_error_percentage??row.totalErrorPercentage??row.total_error_ratio??row.totalErrorRatio??0)}%<\/td>\n        <td class=\"${qClass(row.quality_percentage??row.qualityPercentage??0)}\">${fmt(row.quality_percentage??row.qualityPercentage??0)}%<\/td>\n      <\/tr>`;\n    } else if(activeTab==='account'){\n      html+=`<tr>\n        <td>${row.client_info?.name??row.client_name??row.clientName??'-'}<\/td>\n        <td>${row.name??row.account_name??row.accountName??row.account??'-'}<\/td>\n        <td>${row.completedDos??row.no_of_dos??row.dos_completed??row.noOfDos??row.dosCompleted??'-'}<\/td>\n        <td>${row.qcCompletedDos??row.qc_completed??row.qcCompleted??row.qc_dos??'-'}<\/td>\n        <td>${fmt(row.qc_percentage??row.qcPercentage??0)}%<\/td>\n      <\/tr>`;\n    }\n  });\n  document.getElementById('tableBody').innerHTML = html;\n}\n\nfunction updateEntryInfo(total){\n  const el = document.getElementById('entryInfo');\n  if (!el) return;\n  el.textContent = total === 1 ? 'Showing 1 entry' : `Showing ${total} entries`;\n}\n\n\/\/ CSV DOWNLOAD\n\/\/ Builds the request URL for the active tab at a given page\/limit (mirrors fetchData()).\nfunction buildTabUrl(page, limit){\n  const params = new URLSearchParams();\n  params.set('page', page);\n  params.set('limit', limit);\n  if(activeTab==='executive'){\n    if(filterState.month) params.set('month', filterState.month);\n    if(filterState.year)  params.set('year',  filterState.year);\n    const execIds = parseIds(filterState.executive);\n    if(execIds.length) params.set('assigned_to', execIds.join(','));\n    return `${ENDPOINTS.INDEX_EXECUTIVE}?${params.toString()}`;\n  } else if(activeTab==='qc_executive'){\n    if(filterState.month) params.set('month', filterState.month);\n    if(filterState.year)  params.set('year',  filterState.year);\n    const qcIds = parseIds(filterState.qcExec);\n    if(qcIds.length) params.set('assigned_to', qcIds.join(','));\n    return `${ENDPOINTS.QC_EXECUTIVE}?${params.toString()}`;\n  } else if(activeTab==='client'){\n    if(filterState.month) params.set('month', filterState.month);\n    if(filterState.year)  params.set('year',  filterState.year);\n    const clientIds = parseIds(filterState.client);\n    if(clientIds.length) params.set('client_id', clientIds.join(','));\n    return `${ENDPOINTS.INDEX_CLIENT}?${params.toString()}`;\n  } else if(activeTab==='account'){\n    const accountIds = parseIds(filterState.account);\n    const acClientIds = parseIds(filterState.client);\n    if(accountIds.length)  params.set('account_id', accountIds.join(','));\n    if(acClientIds.length) params.set('client_id',  acClientIds.join(','));\n    if(filterState.month)  params.set('month', filterState.month);\n    if(filterState.year)   params.set('year',  filterState.year);\n    return `${ENDPOINTS.INDEX_ACCOUNT}?${params.toString()}`;\n  }\n  return '';\n}\n\n\/\/ Fetches ALL records for the active tab (not just the current page) so the CSV\n\/\/ contains the entire user network, matching what the on-screen view implies.\nasync function fetchAllRowsForDownload(){\n  const BIG_LIMIT = 100000;\n  const url = buildTabUrl(1, BIG_LIMIT);\n  const d = await fetch(url, { method:'GET', headers:getHeaders() }).then(r=>r.json());\n  let allRows = extractRows(d);\n\n  \/\/ Safety net: if the backend capped the response, page through the rest.\n  const info = extractPagination(d, BIG_LIMIT, allRows.length);\n  const needsMorePages = info.pages > 1 ? (info.total > allRows.length) : false;\n  if(needsMorePages){\n    for(let p=2; info.pages >= p; p++){\n      const purl = buildTabUrl(p, BIG_LIMIT);\n      const pd = await fetch(purl, { method:'GET', headers:getHeaders() }).then(r=>r.json());\n      allRows = allRows.concat(extractRows(pd));\n    }\n  }\n  return allRows;\n}\n\n\/\/ Returns the CSV cell values for one row, matching the visible table columns.\nfunction csvCellsFor(row){\n  if(activeTab==='executive'){\n    return [\n      row.manager||'-', row.team_lead||'-', row.pesudo_name||'-',\n      row.dos_completed??row.assigned_dos??row.dos_assign??'-',\n      row.qc_completed??0, fmt(row.qc_percentage)+'%',\n      row.minor_errors??0, row.critical_errors??0, row.zte_errors??0,\n      row.total_errors??0, fmt(row.minor_wtg), fmt(row.critical_wtg), fmt(row.zte_wtg),\n      fmt(row.total_error_percentage??row.total_error_ratio)+'%', fmt(row.quality_percentage)+'%',\n    ];\n  } else if(activeTab==='qc_executive'){\n    return [\n      row.manager||'-', row.team_lead||'-', row.pesudo_name||'-',\n      row.dos_assign??row.assigned_dos??'-', row.dos_completed??'-', fmt(row.dos_percentage??0)+'%',\n      row.minor_errors??0, row.critical_errors??0, row.zte_errors??0, row.total_errors??0,\n      fmt(row.minor_wtg), fmt(row.critical_wtg), fmt(row.zte_wtg),\n      fmt(row.total_error_ratio??row.total_error_percentage), fmt(row.general_error_ratio??0),\n      fmt(row.quality_percentage)+'%',\n    ];\n  } else if(activeTab==='client'){\n    return [\n      row.name??row.client_name??row.clientName??'-',\n      row.account_name??row.accountName??row.account??'-',\n      row.completedDos??row.dos_completed??row.dosCompleted??row.no_of_dos??row.noOfDos??'-',\n      row.qcCompletedDos??row.qc_completed??row.qcCompleted??row.qc_dos??'-',\n      fmt(row.qc_percentage??row.qcPercentage??0)+'%',\n      row.MI??row.minor_errors??row.minorErrors??row.minor??0,\n      row.CR??row.critical_errors??row.criticalErrors??row.critical??0,\n      row.ZTE??row.zte_errors??row.zteErrors??row.zte??0,\n      row.total_errors??row.totalErrors??row.total_error??0,\n      fmt(row.minor_wtg??row.minorWtg??0), fmt(row.critical_wtg??row.criticalWtg??0),\n      fmt(row.zte_wtg??row.zteWtg??0),\n      fmt(row.total_error_percentage??row.totalErrorPercentage??row.total_error_ratio??row.totalErrorRatio??0)+'%',\n      fmt(row.quality_percentage??row.qualityPercentage??0)+'%',\n    ];\n  } else if(activeTab==='account'){\n    return [\n      row.client_info?.name??row.client_name??row.clientName??'-',\n      row.name??row.account_name??row.accountName??row.account??'-',\n      row.completedDos??row.no_of_dos??row.dos_completed??row.noOfDos??row.dosCompleted??'-',\n      row.qcCompletedDos??row.qc_completed??row.qcCompleted??row.qc_dos??'-',\n      fmt(row.qc_percentage??row.qcPercentage??0)+'%',\n    ];\n  }\n  return [];\n}\n\nasync function downloadCSV(){\n  const heads = HEADS[activeTab];\n  const btns = [...document.querySelectorAll('.btn-csv')];\n  const prevLabels = btns.map(b=>b.textContent);\n  btns.forEach(b=>{ b.disabled = true; b.textContent = 'Preparing\u2026'; });\n  try {\n    let allRows = await fetchAllRowsForDownload();\n\n    \/\/ Apply the same client-side search filter that the table uses.\n    const search = (filterState.search||'').toLowerCase();\n    if(search){\n      allRows = allRows.filter(r=>JSON.stringify(r).toLowerCase().includes(search));\n    }\n\n    if(!allRows.length){ alert('No data to download.'); return; }\n\n    let csv = heads.map(h=>'\"'+String(h).replace(\/\"\/g,'\"\"')+'\"').join(',')+'\\n';\n    allRows.forEach(row=>{\n      const cells = csvCellsFor(row).map(v=>'\"'+String(v==null?'':v).trim().replace(\/\"\/g,'\"\"')+'\"');\n      csv += cells.join(',')+'\\n';\n    });\n\n    const blob = new Blob(['\\ufeff'+csv], { type:'text\/csv;charset=utf-8;' });\n    const a = document.createElement('a');\n    a.href = URL.createObjectURL(blob);\n    a.download = `quality_${activeTab}_${Date.now()}.csv`;\n    a.click();\n    URL.revokeObjectURL(a.href);\n  } catch(e){\n    console.error('CSV download error:', e);\n    alert('Could not download the file. Please try again.');\n  } finally {\n    btns.forEach((b,i)=>{ b.disabled = false; b.textContent = prevLabels[i]; });\n  }\n}\n\n\/\/ HELPERS\nfunction fmt(v){ \n  if(v===null||v===undefined||v==='') \n  return '0.00'; const n=parseFloat(v); \n  return isNaN(n)?v:n.toFixed(2); \n}\nfunction qClass(v){ \n  const n=parseFloat(v)||0; \n  return n>=99?'q-high':n>=90?'q-mid':'q-low'; \n}\n\n\/\/ BOOT \n  init();\n<\/script>\n<\/div><\/div>\n\n<\/div><\/div><\/div><\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>EXECUTIVE WISE-QUALITY Executive Wise QC Executive Wise Client Wise Account Wise Loading\u2026 Showing 0 entries<\/p>\n","protected":false},"author":2,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_kad_post_transparent":"disable","_kad_post_title":"hide","_kad_post_layout":"fullwidth","_kad_post_sidebar_id":"","_kad_post_content_style":"unboxed","_kad_post_vertical_padding":"hide","_kad_post_feature":"hide","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","footnotes":""},"class_list":["post-204","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/ph-portal-v2.zyneventures.com\/index.php\/wp-json\/wp\/v2\/pages\/204","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ph-portal-v2.zyneventures.com\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/ph-portal-v2.zyneventures.com\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/ph-portal-v2.zyneventures.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/ph-portal-v2.zyneventures.com\/index.php\/wp-json\/wp\/v2\/comments?post=204"}],"version-history":[{"count":29,"href":"https:\/\/ph-portal-v2.zyneventures.com\/index.php\/wp-json\/wp\/v2\/pages\/204\/revisions"}],"predecessor-version":[{"id":1207,"href":"https:\/\/ph-portal-v2.zyneventures.com\/index.php\/wp-json\/wp\/v2\/pages\/204\/revisions\/1207"}],"wp:attachment":[{"href":"https:\/\/ph-portal-v2.zyneventures.com\/index.php\/wp-json\/wp\/v2\/media?parent=204"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}