{"id":157,"date":"2026-03-31T07:09:38","date_gmt":"2026-03-31T07:09:38","guid":{"rendered":"https:\/\/ph-portal.zyneventures.com\/?page_id=157"},"modified":"2026-03-31T07:09:38","modified_gmt":"2026-03-31T07:09:38","slug":"resource-calculator","status":"publish","type":"page","link":"https:\/\/ph-portal.zyneventures.com\/index.php\/resource-calculator\/","title":{"rendered":"Resource Calculator"},"content":{"rendered":"<style>.kadence-column157_7420bb-46 > .kt-inside-inner-col,.kadence-column157_7420bb-46 > .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-column157_7420bb-46 > .kt-inside-inner-col{column-gap:var(--global-kb-gap-sm, 1rem);}.kadence-column157_7420bb-46 > .kt-inside-inner-col{flex-direction:column;}.kadence-column157_7420bb-46 > .kt-inside-inner-col > .aligncenter{width:100%;}.kadence-column157_7420bb-46 > .kt-inside-inner-col:before{opacity:0.3;}.kadence-column157_7420bb-46{position:relative;}@media all and (max-width: 1024px){.kadence-column157_7420bb-46 > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}@media all and (max-width: 767px){.kadence-column157_7420bb-46 > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}<\/style>\n<div class=\"wp-block-kadence-column kadence-column157_7420bb-46\"><div class=\"kt-inside-inner-col\"><style>.kb-row-layout-id157_e1c627-fa > .kt-row-column-wrap{align-content:start;}:where(.kb-row-layout-id157_e1c627-fa > .kt-row-column-wrap) > .wp-block-kadence-column{justify-content:start;}.kb-row-layout-id157_e1c627-fa > .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-id157_e1c627-fa > .kt-row-layout-overlay{opacity:0.30;}@media all and (max-width: 1024px){.kb-row-layout-id157_e1c627-fa > .kt-row-column-wrap{grid-template-columns:minmax(0, 1fr);}}@media all and (max-width: 767px){.kb-row-layout-id157_e1c627-fa > .kt-row-column-wrap{grid-template-columns:minmax(0, 1fr);}}<\/style><div class=\"kb-row-layout-wrap kb-row-layout-id157_e1c627-fa 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-column157_3df17e-58 > .kt-inside-inner-col,.kadence-column157_3df17e-58 > .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-column157_3df17e-58 > .kt-inside-inner-col{column-gap:var(--global-kb-gap-sm, 1rem);}.kadence-column157_3df17e-58 > .kt-inside-inner-col{flex-direction:column;}.kadence-column157_3df17e-58 > .kt-inside-inner-col > .aligncenter{width:100%;}.kadence-column157_3df17e-58 > .kt-inside-inner-col:before{opacity:0.3;}.kadence-column157_3df17e-58{position:relative;}@media all and (max-width: 1024px){.kadence-column157_3df17e-58 > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}@media all and (max-width: 767px){.kadence-column157_3df17e-58 > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}<\/style>\n<div class=\"wp-block-kadence-column kadence-column157_3df17e-58\"><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            --error-red:    #D93025;\n            --input-bg:     #ebf2ff;\n            --sub-row-bg:   #F5F5F5;\n        }\n        * { box-sizing: border-box; margin: 0; padding: 0; }\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 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: 32px;\n        }\n\n        \/* \u2500\u2500 Filter Row \u2500\u2500 *\/\n        .filter-row {\n            display: flex;\n            align-items: flex-end;\n            gap: 20px;\n            margin-bottom: 32px;\n            flex-wrap: wrap;\n        }\n        .filter-group {\n            display: flex;\n            flex-direction: column;\n            gap: 8px;\n            min-width: 185px;\n            flex: 1;\n        }\n        .filter-group label { font-size: 14px; font-weight: 400; }\n        .filter-group label span { color: var(--error-red); }\n\n        \/* \u2500\u2500 Custom Multi-Select \u2500\u2500 *\/\n        .cs-wrapper { position: relative; }\n        .cs-trigger {\n            display: flex;\n            align-items: center;\n            justify-content: space-between;\n            gap: 8px;\n            padding: 10px 14px;\n            background: #fff;\n            border: 1px solid #D0D5DD;\n            border-radius: 6px;\n            cursor: pointer;\n            font-family: 'Sora', sans-serif;\n            font-size: 12px;\n            color: #444;\n            user-select: none;\n            min-height: 44px;\n            transition: border-color 0.2s, box-shadow 0.2s;\n        }\n        .cs-trigger:hover          { border-color: var(--primary-blue); }\n        .cs-trigger.open           { border-color: var(--primary-blue); box-shadow: 0 0 0 3px rgba(1,128,255,.1); }\n        .cs-tags                   { display: flex; flex-wrap: wrap; gap: 4px; flex: 1; min-width: 0; }\n        .ph                        { color: #99abb4; font-size: 12px; }\n        .s-tag {\n            background: var(--input-bg);\n            color: var(--primary-blue);\n            border-radius: 4px;\n            padding: 2px 6px;\n            font-size: 12px;\n            font-weight: 400;\n            display: flex; align-items: center; gap: 3px;\n            white-space: nowrap;\n        }\n        .rm { cursor: pointer; font-size: 12px; color: #99abb4; line-height: 1; }\n        .rm:hover { color: var(--error-red); }\n        .chv {\n            width: 16px;\n            height: 16px;\n            color: #99abb4;\n            flex-shrink: 0;\n            transition: transform .2s;\n        }\n        .cs-trigger.open .chv { transform: rotate(180deg); }\n        .cs-dropdown {\n            display: none;\n            position: absolute;\n            top: calc(100% + 4px); left: 0; right: 0;\n            background: #fff;\n            border: 1px solid #D0D5DD;\n            border-radius: 6px;\n            box-shadow: 0 8px 24px rgba(0,0,0,.1);\n            z-index: 300;\n            max-height: 220px;\n            overflow-y: auto;\n        }\n        .cs-dropdown.open { display: block; }\n        .dd-opt {\n            display: flex;\n            align-items: center;\n            gap: 10px;\n            padding: 9px 14px;\n            font-size: 12px;\n            cursor: pointer;\n            color: var(--text-dark);\n            transition: background .12s;\n        }\n        .dd-opt:hover { background: #f5f9ff; }\n        .dd-opt.selected { background: #EBF2FF; color: var(--primary-blue); }\n        .dd-opt input[type=\"checkbox\"] { accent-color: var(--primary-blue); width:14px; height:14px; flex-shrink:0; }\n\n        \/* \u2500\u2500 Native Select \u2500\u2500 *\/\n        .native-sel {\n            appearance: none; -webkit-appearance: none;\n            padding: 10px 38px 10px 14px;\n            background: #fff url(\"data:image\/svg+xml,%3Csvg xmlns='http:\/\/www.w3.org\/2000\/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2399abb4' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C\/polyline%3E%3C\/svg%3E\") no-repeat right 12px center;\n            border: 1px solid #D0D5DD;\n            border-radius: 6px;\n            font-family: 'Sora', sans-serif;\n            font-size: 12px; \n            color: #444;\n            width: 100%; \n            min-height: 44px;\n            outline: none; \n            cursor: pointer;\n            transition: border-color .2s;\n        }\n        .native-sel:hover, .native-sel:focus { border-color: var(--primary-blue); }\n\n        \/* \u2500\u2500 Search Btn \u2500\u2500 *\/\n        .btn-search {\n            padding: 10px 32px;\n            background: var(--primary-blue);\n            color: #fff; \n            border: none; \n            border-radius: 6px;\n            font-family: 'Sora', sans-serif;\n            font-size: 12px; \n            font-weight: 400;\n            cursor: pointer; \n            height: 44px; \n            white-space: nowrap;\n            align-self: flex-end;\n            transition: background .2s;\n        }\n        .btn-search:hover    { background: var(--hover-blue); }\n        .btn-search:disabled { background: #ccc; cursor: not-allowed; }\n\n        \/* \u2500\u2500 Tabs \u2500\u2500 *\/\n        .tabs-bar {\n            display: flex;\n            border-bottom: 1px solid var(--border-color);\n            margin-bottom: 28px;\n        }\n        .tab-item {\n            padding: 12px 22px;\n            font-size: 16px;\n            font-weight: 400;\n            color: var(--text-muted);\n            cursor: pointer;\n            border-bottom: 2px solid transparent;\n            margin-bottom: -1px;\n            white-space: nowrap;\n            transition: color .15s;\n        }\n        .tab-item:hover { color: var(--text-dark); }\n        .tab-item.active { color: var(--text-dark); font-weight: 600; border-bottom-color: var(--primary-blue); }\n\n        \/* \u2500\u2500 No Data \/ Loading \u2500\u2500 *\/\n        .no-data {\n            padding: 80px 20px; \n            text-align: center;\n            font-size: 24px; \n            font-weight: 400; \n            color: #C8C8D0;\n        }\n        .loading-wrap {\n            padding: 60px 20px; \n            text-align: center;\n            display: flex; \n            flex-direction: column; \n            align-items: center; \n            gap: 12px;\n        }\n        .spinner {\n            width: 28px; height: 28px;\n            border: 3px solid #e0e8f0;\n            border-top-color: var(--primary-blue);\n            border-radius: 50%;\n            animation: spin .7s linear infinite;\n        }\n        @keyframes spin { to { transform: rotate(360deg); } }\n\n        \/* CATEGORY STATS TABLE  - Simple 2-column flat list *\/\n        .t-stats {\n            width: 100%;\n            border-collapse: collapse;\n            font-size: 14px;\n        }\n        .t-stats thead tr { border-bottom: 1px solid var(--border-color); background: #F8FAFB; }\n        .t-stats thead th {\n            padding: 14px 20px;\n            text-align: left;\n            font-size: 12px;\n            font-weight: 500;\n            color: var(--text-dark);\n        }\n        .t-stats tbody tr { border-bottom: 1px solid var(--border-color); }\n        .t-stats tbody tr:hover { background: #fafcff; }\n        .t-stats tbody td { padding: 14px 20px; font-size: 12px; color: #555; }\n        .t-stats tbody td:nth-child(2) { color: var(--text-dark); }\n\n        \/*  ACCOUNT STATS TABLE -  Multi-column flat table, one row per account  *\/\n        .t-acct-stats {\n            width: 100%;\n            border-collapse: collapse;\n            font-size: 12px;\n            min-width: 680px;\n        }\n        .t-acct-stats thead tr { border-bottom: 1px solid var(--border-color); background: #F8FAFB; }\n        .t-acct-stats thead th {\n            padding: 12px 16px;\n            text-align: left;\n            font-size: 12px;\n            font-weight: 500;\n            color: var(--text-dark);\n            white-space: nowrap;\n        }\n        .t-acct-stats tbody tr { border-bottom: 1px solid var(--border-color); }\n        .t-acct-stats tbody tr:hover { background: #fafcff; }\n        .t-acct-stats tbody td {\n            padding: 12px 16px;\n            font-size: 12px;\n            color: #555;\n            white-space: nowrap;\n        }\n        .t-acct-stats tbody td:first-child { color: var(--text-dark); }\n\n        \/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n           GROUPED TABLES  (Account Wise \/ Category Wise)\n           Images 2 & 4\n        \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\n        .t-grouped {\n            width: 100%;\n            border-collapse: collapse;\n            font-size: 13px;\n        }\n\n        \/* Column headers *\/\n        .t-grouped thead th {\n            padding: 11px 12px;\n            text-align: left;\n            font-size: 12px;\n             font-weight: 500;\n            color: var(--text-muted);\n            background: #fff;\n            border-bottom: 1px solid var(--border-color);\n            white-space: nowrap;\n        }\n        .t-grouped thead th.col-label { min-width: 180px; }\n        .t-grouped thead th.col-month { min-width: 68px; text-align: right; }\n\n        \/* Year row *\/\n        .t-grouped tr.r-year td {\n            padding: 22px 12px 10px;\n            font-size: 20px;\n            font-weight: 600;\n            color: var(--text-dark);\n            border: none; background: #fff;\n        }\n\n        \/* Entity header row (white bg, completedDOS) *\/\n        .t-grouped tr.r-entity td {\n            padding: 10px 12px;\n            border-bottom: 1px solid var(--border-color);\n            background: #fff;\n        }\n        .t-grouped tr.r-entity td.col-label {\n            font-size: 12px; font-weight: 400;\n            color: var(--text-muted);\n        }\n        .t-grouped tr.r-entity td.col-val {\n            font-size: 13px; text-align: right;\n            color: var(--text-dark);\n        }\n\n        \/* Sub-metric rows (gray bg) *\/\n        .t-grouped tr.r-sub td {\n            padding: 9px 12px;\n            background: var(--sub-row-bg);\n            border-bottom: 1px solid #e8e8e8;\n        }\n        .t-grouped tr.r-sub td.col-label {\n            font-size: 12px; font-weight: 500;\n            color: var(--text-dark);\n        }\n        .t-grouped tr.r-sub td.col-val {\n            font-size: 12px; text-align: right;\n            color: var(--text-dark);\n        }\n        .t-grouped tr.r-sub td.col-val.zero { color: var(--text-muted); }\n\n        \/* Separator row *\/\n        .t-grouped tr.r-sep td {\n            padding: 5px 12px;\n            text-align: center;\n            font-size: 12px; color: #D0D0D8;\n            border: none; background: #fff;\n        }\n\n        \/* \u2500\u2500 Table wrapper \u2500\u2500 *\/\n        .t-wrap { overflow-x: auto; width: 100%; }\n\n        \/* \u2500\u2500 Pagination \u2500\u2500 *\/\n        .pag-footer {\n            margin-top: 28px;\n            display: flex;\n            justify-content: space-between;\n             align-items: center;\n            color: var(--text-muted); font-size: 12px;\n            flex-wrap: wrap; gap: 10px;\n        }\n        .pag-controls { display: flex; align-items: center; gap: 8px; }\n        .pn {\n            cursor: pointer;\n            width: 32px;\n            height: 32px;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            border-radius: 50%;\n            font-weight: 400;\n            font-size: 12px;\n            transition: background .15s, color .15s;\n        }\n        .pn:hover { background: var(--input-bg); color: var(--primary-blue); }\n        .pn.active { background: var(--primary-blue); color: #fff; }\n        .nav-btn {\n            cursor: pointer;\n            color: var(--text-dark);\n            font-weight: 400;\n            font-size: 12px;\n            padding: 4px 8px;\n        }\n        .nav-btn:hover { color: var(--primary-blue); }\n        .nav-btn.disabled { color: #ccc; pointer-events: none; }\n\n        @media (max-width: 768px) {\n            body { padding: 16px; }\n            .filter-row { flex-direction: column; }\n            .filter-group { min-width: 100%; }\n            .tabs-bar { overflow-x: auto; }\n            .tab-item { padding: 10px 14px; font-size: 12px; }\n        }\n    <\/style>\n\n\n<h2 class=\"page-title\" id=\"pageTitle\">RESOURCE CALCULATOR | ACCOUNT WISE<\/h2><br>\n\n<!-- \u2500\u2500 Filters \u2500\u2500 -->\n<div class=\"filter-row\">\n    <!-- Year -->\n    <div class=\"filter-group\">\n        <label>Year <span>*<\/span><\/label>\n        <div class=\"cs-wrapper\">\n            <div class=\"cs-trigger\" id=\"yearTrigger\" onclick=\"toggleDD('year')\">\n                <div class=\"cs-tags\" id=\"yearTags\"><span class=\"ph\">Select Year<\/span><\/div>\n                <svg class=\"chv\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polyline points=\"6 9 12 15 18 9\"><\/polyline><\/svg>\n            <\/div>\n            <div class=\"cs-dropdown\" id=\"yearDropdown\"><\/div>\n        <\/div>\n    <\/div>\n\n    <!-- Project Type -->\n    <div class=\"filter-group\">\n        <label>Project Type <span>*<\/span><\/label>\n        <div class=\"cs-wrapper\">\n            <div class=\"cs-trigger\" id=\"projectTypeTrigger\" onclick=\"toggleDD('projectType')\">\n                <div class=\"cs-tags\" id=\"projectTypeTags\"><span class=\"ph\">Select Project Type<\/span><\/div>\n                <svg class=\"chv\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polyline points=\"6 9 12 15 18 9\"><\/polyline><\/svg>\n            <\/div>\n            <div class=\"cs-dropdown\" id=\"projectTypeDropdown\">\n                <div class=\"loading-wrap\" style=\"padding:14px;flex-direction:row;gap:8px;\">\n                    <div class=\"spinner\" style=\"width:14px;height:14px;border-width:2px;\"><\/div>\n                    <span style=\"font-size:12px;color:#8D8C9C;\">Loading\u2026<\/span>\n                <\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <!-- Working Hours -->\n    <div class=\"filter-group\">\n        <label>Working Hours <span>*<\/span><\/label>\n        <select class=\"native-sel\" id=\"hoursSelect\">\n            <option value=\"6\">6<\/option>\n            <option value=\"6.5\">6.5<\/option>\n            <option value=\"7\" selected>7<\/option>\n            <option value=\"7.5\">7.5<\/option>\n            <option value=\"8\">8<\/option>\n            <option value=\"8.5\">8.5<\/option>\n            <option value=\"9\">9<\/option>\n            <option value=\"9.5\">9.5<\/option>\n            <option value=\"10\">10<\/option>\n        <\/select>\n    <\/div>\n\n    <button class=\"btn-search\" id=\"searchBtn\" onclick=\"handleSearch()\">Search<\/button>\n<\/div>\n\n<!-- \u2500\u2500 Tabs \u2500\u2500 -->\n<div class=\"tabs-bar\">\n    <div class=\"tab-item active\" data-tab=\"accountWise\"   onclick=\"switchTab(this)\">Account Wise<\/div>\n    <div class=\"tab-item\"        data-tab=\"accountStats\"  onclick=\"switchTab(this)\">Account Stats<\/div>\n    <div class=\"tab-item\"        data-tab=\"categoryWise\"  onclick=\"switchTab(this)\">Category Wise<\/div>\n    <div class=\"tab-item\"        data-tab=\"categoryStats\" onclick=\"switchTab(this)\">Category Stats<\/div>\n<\/div>\n\n<!-- \u2500\u2500 Content \u2500\u2500 -->\n<div id=\"contentArea\"><div class=\"no-data\">No Data Found<\/div><\/div>\n\n<!-- \u2500\u2500 Pagination \u2500\u2500 -->\n<div class=\"pag-footer\" id=\"pagFooter\" style=\"display:none;\">\n    <div class=\"pag-controls\">\n        <span class=\"nav-btn disabled\" id=\"prevBtn\" onclick=\"changePage(-1)\">\u00ab Previous<\/span>\n        <div id=\"pageNums\" style=\"display:flex;gap:5px;\"><\/div>\n        <span class=\"nav-btn disabled\" id=\"nextBtn\" onclick=\"changePage(1)\">Next \u00bb<\/span>\n    <\/div>\n    <div id=\"entryInfo\">Showing 0 to 0 of 0 entries<\/div>\n<\/div>\n\n<script>\n\/\/  CONSTANTS & CONFIG\nconst BASE_URL            = 'https:\/\/api-ph-portal.zyneventures.com\/api\/v1';\nconst GET_PROJECT_TYPES   = `${BASE_URL}\/projecttype\/all`;\nconst GET_CALCULATOR_DATA = `${BASE_URL}\/resource-calculator\/project-type-wise`;\n\nconst MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];\n\nconst TAB_TITLES = {\n    accountWise:   'RESOURCE CALCULATOR | ACCOUNT WISE',\n    accountStats:  'RESOURCE CALCULATOR | ACCOUNT STATS WISE',\n    categoryWise:  'RESOURCE CALCULATOR | CATEGORY WISE',\n    categoryStats: 'RESOURCE CALCULATOR | CATEGORY STATS WISE',\n};\n\n\/\/ STATE\nconst S = {\n    activeTab:   'accountWise',\n    years:       [],   \/\/ selected year strings\n    ptypes:      [],   \/\/ selected project type id strings\n    hours:       '7',\n    rawData:     null,\n    ptypeList:   [],   \/\/ [{id,type}] from API\n    pageItems:   [],   \/\/ current rendered flat list (for pagination)\n    page:        1,\n    flatPer:     20,   \/\/ rows per page for flat tables\n    blockPer:    10,   \/\/ blocks per page for grouped tables\n};\n\n\/\/  AUTH\nconst getToken  = () =>\n    localStorage.getItem('TOKEN') ||\n    (document.cookie.match(\/(?:^|;\\s*)TOKEN=([^;]*)\/) || [])[1] || '';\nconst getHdrs   = () => ({\n    'Content-Type': 'application\/json',\n    'Authorization': `Bearer ${getToken()}`,\n});\n\n\/\/  YEAR DROPDOWN\nfunction buildYears() {\n    const cur = new Date().getFullYear();\n    const dd  = document.getElementById('yearDropdown');\n    dd.innerHTML = '';\n    for (let y = cur + 1; y >= 2020; y--)\n        dd.appendChild(mkOpt(String(y), String(y), 'year'));\n}\n\n\/\/ PROJECT TYPES\nasync function loadPTypes() {\n    try {\n        const r  = await fetch(\n            `${GET_PROJECT_TYPES}?page=1&limit=100`, \n            { headers: getHdrs() }\n        );\n        const js = await r.json();\n        const ts = js?.data?.data || [];\n        S.ptypeList = ts;\n        const dd = document.getElementById('projectTypeDropdown');\n        dd.innerHTML = '';\n        if (!ts.length) { dd.innerHTML = '<div class=\"dd-opt\" style=\"color:#ccc;pointer-events:none;\">No types found<\/div>'; return; }\n        ts.forEach(pt => dd.appendChild(mkOpt(String(pt.id), pt.type, 'projectType')));\n    } catch(e) {\n        document.getElementById('projectTypeDropdown').innerHTML =\n            '<div class=\"dd-opt\" style=\"color:var(--error-red);pointer-events:none;\">Failed to load<\/div>';\n    }\n}\n\nfunction mkOpt(value, label, type) {\n    const el = document.createElement('div');\n    el.className    = 'dd-opt';\n    el.dataset.val  = value;\n    el.innerHTML    = `<input type=\"checkbox\"><span>${label}<\/span>`;\n    el.addEventListener('click', e => {\n        if (e.target.tagName !== 'INPUT') el.querySelector('input').checked = !el.querySelector('input').checked;\n        toggleSel(type, value);\n    });\n    return el;\n}\n\n\/\/ DROPDOWN TOGGLE\nfunction toggleDD(type) {\n    const tr = document.getElementById(type + 'Trigger');\n    const dd = document.getElementById(type + 'Dropdown');\n    const was = dd.classList.contains('open');\n    closeAllDD();\n    if (!was) { dd.classList.add('open'); tr.classList.add('open'); }\n}\nfunction closeAllDD() {\n    document.querySelectorAll('.cs-dropdown').forEach(d => d.classList.remove('open'));\n    document.querySelectorAll('.cs-trigger').forEach(t => t.classList.remove('open'));\n}\ndocument.addEventListener('click', e => { if (!e.target.closest('.cs-wrapper')) closeAllDD(); });\n\n\/\/  SELECTION TOGGLE\nfunction toggleSel(type, val) {\n    const arr  = type === 'year' ? S.years : S.ptypes;\n    const idx  = arr.indexOf(val);\n    if (idx === -1) arr.push(val); else arr.splice(idx, 1);\n\n    const ddId = type === 'year' ? 'yearDropdown' : 'projectTypeDropdown';\n    document.getElementById(ddId).querySelectorAll('.dd-opt').forEach(opt => {\n        const v  = opt.dataset.val;\n        const cb = opt.querySelector('input');\n        const ok = arr.includes(v);\n        if (cb) cb.checked = ok;\n        opt.classList.toggle('selected', ok);\n    });\n    syncTags(type);\n}\n\nfunction syncTags(type) {\n    const arr = type === 'year' ? S.years : S.ptypes;\n    const el  = document.getElementById(type === 'year' ? 'yearTags' : 'projectTypeTags');\n\n    if (!arr.length) {\n        el.innerHTML = `<span class=\"ph\">${type === 'year' ? 'Select Year' : 'Select Project Type'}<\/span>`;\n        return;\n    }\n    if (arr.length >= 2) {\n        el.innerHTML = `<span style=\"font-size:13px;color:#444;\">${arr.length} selected<\/span>`;\n        return;\n    }\n    const label = type === 'year' ? arr[0] :\n        (S.ptypeList.find(p => String(p.id) === arr[0])?.type || arr[0]);\n    el.innerHTML = `<span class=\"s-tag\">${label}<span class=\"rm\" onclick=\"rmTag(event,'${type}','${arr[0]}')\">\u00d7<\/span><\/span>`;\n}\n\nfunction rmTag(e, type, val) { e.stopPropagation(); toggleSel(type, val); }\n\n\/\/ TAB SWITCH\nfunction switchTab(el) {\n    document.querySelectorAll('.tab-item').forEach(t => t.classList.remove('active'));\n    el.classList.add('active');\n    S.activeTab = el.dataset.tab;\n    S.page      = 1;\n    document.getElementById('pageTitle').textContent = TAB_TITLES[S.activeTab];\n    if (S.rawData) dispatch();\n}\n\n\/\/ SEARCH\nasync function handleSearch() {\n    if (!S.years.length)  { alert('Please select at least one Year.'); return; }\n    if (!S.ptypes.length) { alert('Please select at least one Project Type.'); return; }\n\n    S.hours = document.getElementById('hoursSelect').value;\n    S.page  = 1;\n\n    const btn = document.getElementById('searchBtn');\n    btn.disabled = true; btn.textContent = 'Searching\u2026';\n    loading();\n\n    try {\n        const qs = new URLSearchParams({\n            year:         S.years.join(','),\n            project_type: S.ptypes.join(','),\n            hours:        S.hours,\n        });\n        const res = await fetch(`${GET_CALCULATOR_DATA}?${qs}`, { headers: getHdrs() });\n        const js  = await res.json();\n\n        if (!res.ok || !js.data) { noData(); return; }\n        S.rawData = js.data;\n        dispatch();\n    } catch(e) {\n        console.error(e); noData();\n    } finally {\n        btn.disabled = false; btn.textContent = 'Search';\n    }\n}\n\n\/\/ DISPATCH\nfunction dispatch() {\n    const t = S.activeTab;\n    if      (t === 'accountWise')   renderAccountWise();\n    else if (t === 'accountStats')  renderAccountStats();\n    else if (t === 'categoryWise')  renderCategoryWise();\n    else if (t === 'categoryStats') renderCategoryStats();\n}\n\n\/\/  HELPERS\nfunction flatten() {\n    const rows = [];\n    for (const yr of Object.keys(S.rawData))\n        for (const aid of Object.keys(S.rawData[yr]))\n            for (const mo of Object.keys(S.rawData[yr][aid])) {\n                const e = S.rawData[yr][aid][mo];\n                if (e) rows.push({ ...e, year: +yr });\n            }\n    return rows;\n}\n\nconst fi   = v  => (v == null ? '0'      : parseInt(v).toLocaleString());\nconst fd   = v  => (v == null ? '0.00'   : parseFloat(v).toFixed(2));\nconst fd4  = v  => (v == null ? '0.0000' : parseFloat(v).toFixed(4));\nconst toQC = v  => (parseFloat(v || 0) * 0.30).toFixed(2);\n\nfunction vCell(val, isZero) {\n    return `<td class=\"col-val${isZero ? ' zero' : ''}\">${val}<\/td>`;\n}\n\n\/* 12 month cells for entity rows *\/\nfunction entityMonthCells(months) {\n    return MONTHS.map((_, i) => {\n        const e = months[i + 1];\n        const v = e ? fi(e.completedDOS) : '0';\n        return `<td class=\"col-val\">${v}<\/td>`;\n    }).join('');\n}\n\n\/* 12 month cells for a sub-row given a value-fn *\/\nfunction subMonthCells(months, fn) {\n    return MONTHS.map((_, i) => {\n        const e   = months[i + 1];\n        const val = fn(e);\n        const z   = parseFloat(val) === 0;\n        return vCell(val, z);\n    }).join('');\n}\n\n\/* Standard sub-row definitions *\/\nconst SUB_DEFS = [\n    { label: 'Ops. Resource count', fn: e => fd(e?.resource_count) },\n    { label: 'To be QC',            fn: e => e ? toQC(e.completedDOS) : '0.00' },\n    { label: 'QC Performed',        fn: e => e ? fd(e.QCCompletedDOS) : '0.00' },\n    { label: 'QC. Resource count',  fn: e => fd(e?.qc_resource_count) },\n];\n\nfunction subRows(months) {\n    return SUB_DEFS.map(sd =>\n        `<tr class=\"r-sub\">\n            <td class=\"col-label\">${sd.label}<\/td>\n            ${subMonthCells(months, sd.fn)}\n        <\/tr>`\n    ).join('');\n}\n\nconst COL_HEADERS = `<thead><tr>\n    <th class=\"col-label\"><\/th>\n    ${MONTHS.map(m => `<th class=\"col-month\">${m}<\/th>`).join('')}\n<\/tr><\/thead>`;\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   TAB 1 \u2014 ACCOUNT WISE  \n   Grouped by Year \u2192 Account\n   Entity row: AccountName | completedDOS\/month\n   Sub rows:   Ops.Resource count | To be QC | QC Performed | QC.Resource count\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction renderAccountWise() {\n    const rows = flatten();\n    if (!rows.length) { noData(); return; }\n\n    \/* Build year\u2192account map *\/\n    const byYA = {};\n    rows.forEach(r => {\n        if (!byYA[r.year]) byYA[r.year] = {};\n        if (!byYA[r.year][r.account_id]) byYA[r.year][r.account_id] = { name: r.name, months: {} };\n        byYA[r.year][r.account_id].months[r.month] = r;\n    });\n\n    \/* Flat blocks list: [{type:'year'|'account', ...}] *\/\n    const blocks = [];\n    Object.keys(byYA).sort().forEach(yr => {\n        blocks.push({ type: 'year', yr: +yr });\n        Object.keys(byYA[yr]).sort().forEach(aid =>\n            blocks.push({ type: 'account', yr: +yr, aid, data: byYA[yr][aid] })\n        );\n    });\n\n    \/* Paginate account-type blocks only *\/\n    const acctBlocks = blocks.filter(b => b.type === 'account');\n    S.pageItems = acctBlocks;\n    const total  = acctBlocks.length;\n    const start  = (S.page - 1) * S.blockPer;\n    const vis    = acctBlocks.slice(start, start + S.blockPer);\n    const visYrs = [...new Set(vis.map(b => b.yr))];\n\n    let tbody = '';\n    visYrs.forEach(yr => {\n        tbody += `<tr class=\"r-year\"><td colspan=\"13\" style=\"padding:22px 12px 10px;font-size:20px;font-weight:700;\">${yr}<\/td><\/tr>`;\n        vis.filter(b => b.yr === yr).forEach(b => {\n            const { name, months } = b.data;\n            tbody += `<tr class=\"r-entity\">\n                <td class=\"col-label\">${name}<\/td>\n                ${entityMonthCells(months)}\n            <\/tr>`;\n            tbody += subRows(months);\n            tbody += `<tr class=\"r-sep\"><td colspan=\"13\">-<\/td><\/tr>`;\n        });\n    });\n\n    paint(`<div class=\"t-wrap\"><table class=\"t-grouped\">${COL_HEADERS}<tbody>${tbody}<\/tbody><\/table><\/div>`, total);\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   TAB 2 \u2014 ACCOUNT STATS  \n   Flat table: Account | Year | Months | Completed DOS | Avg Resource | QC Performed | Benchmark\/Month\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction renderAccountStats() {\n    const rows = flatten();\n    if (!rows.length) { noData(); return; }\n\n    const map = {};\n    rows.forEach(r => {\n        const k = `${r.year}_${r.account_id}`;\n        if (!map[k]) map[k] = {\n            year: r.year, name: r.name, category: r.category_name,\n            dos: 0, qcDos: 0, resSum: 0, months: 0,\n            hpm: parseFloat(r.hrs_per_month || 0),\n        };\n        const g = map[k];\n        g.dos    += parseInt(r.completedDOS || 0);\n        g.qcDos  += parseInt(r.QCCompletedDOS || 0);\n        g.resSum += parseFloat(r.resource_count || 0);\n        g.months++;\n    });\n\n    const items = Object.values(map).sort((a, b) => a.year - b.year || a.name.localeCompare(b.name));\n    S.pageItems = items;\n    const total  = items.length;\n    const start  = (S.page - 1) * S.flatPer;\n    const slice  = items.slice(start, start + S.flatPer);\n\n    const html = `<div class=\"t-wrap\"><table class=\"t-acct-stats\">\n        <thead><tr>\n            <th>Account<\/th>\n            <th>Year<\/th>\n            <th>No. of Months<\/th>\n            <th>Completed DOS<\/th>\n            <th>Avg Resource Count<\/th>\n            <th>QC Performed<\/th>\n            <th>Benchmark\/Month<\/th>\n        <\/tr><\/thead>\n        <tbody>${slice.map(e => `<tr>\n            <td>${e.name}<\/td>\n            <td>${e.year}<\/td>\n            <td>${e.months}<\/td>\n            <td>${e.dos.toLocaleString()}<\/td>\n            <td>${e.months ? (e.resSum \/ e.months).toFixed(4) : '0.0000'}<\/td>\n            <td>${e.qcDos ? e.qcDos.toLocaleString() : '0'}<\/td>\n            <td>${e.hpm.toFixed(2)}<\/td>\n        <\/tr>`).join('')}<\/tbody>\n    <\/table><\/div>`;\n\n    paint(html, total, true);\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   TAB 3 \u2014 CATEGORY WISE  \n   Grouped by Year \u2192 Category\n   Entity row: CategoryName | completedDOS\/month (aggregated across accounts)\n   Sub rows:   Ops.Resource count | To be QC | QC Performed | QC.Resource count\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction renderCategoryWise() {\n    const rows = flatten();\n    if (!rows.length) { noData(); return; }\n\n    \/* Build year\u2192category map, aggregating across accounts *\/\n    const byYC = {};\n    rows.forEach(r => {\n        if (!byYC[r.year]) byYC[r.year] = {};\n        const ck = r.category_id;\n        if (!byYC[r.year][ck]) byYC[r.year][ck] = { name: r.category_name, months: {} };\n        const mo  = r.month;\n        const g   = byYC[r.year][ck];\n        if (!g.months[mo]) g.months[mo] = { completedDOS: 0, resource_count: 0, QCCompletedDOS: 0, qc_resource_count: 0 };\n        const m = g.months[mo];\n        m.completedDOS      += parseInt(r.completedDOS || 0);\n        m.resource_count    += parseFloat(r.resource_count || 0);\n        m.QCCompletedDOS    += parseInt(r.QCCompletedDOS || 0);\n        m.qc_resource_count += parseFloat(r.qc_resource_count || 0);\n    });\n\n    const catBlocks = [];\n    Object.keys(byYC).sort().forEach(yr => {\n        Object.keys(byYC[yr])\n            .sort((a, b) => byYC[yr][a].name.localeCompare(byYC[yr][b].name))\n            .forEach(ck => catBlocks.push({ yr: +yr, ck, data: byYC[yr][ck] }));\n    });\n\n    S.pageItems = catBlocks;\n    const total  = catBlocks.length;\n    const start  = (S.page - 1) * S.blockPer;\n    const vis    = catBlocks.slice(start, start + S.blockPer);\n    const visYrs = [...new Set(vis.map(b => b.yr))];\n\n    let tbody = '';\n    visYrs.forEach(yr => {\n        tbody += `<tr class=\"r-year\"><td colspan=\"13\" style=\"padding:22px 12px 10px;font-size:20px;font-weight:700;\">${yr}<\/td><\/tr>`;\n        vis.filter(b => b.yr === yr).forEach(b => {\n            const { name, months } = b.data;\n            tbody += `<tr class=\"r-entity\">\n                <td class=\"col-label\">${name}<\/td>\n                ${entityMonthCells(months)}\n            <\/tr>`;\n            tbody += subRows(months);\n            tbody += `<tr class=\"r-sep\"><td colspan=\"13\">-<\/td><\/tr>`;\n        });\n    });\n\n    paint(`<div class=\"t-wrap\"><table class=\"t-grouped\">${COL_HEADERS}<tbody>${tbody}<\/tbody><\/table><\/div>`, total);\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   TAB 4 \u2014 CATEGORY STATS \n   Simple 2-col: Category | Benchmark\/Month\n   Benchmark\/Month = sum of hrs_per_month across distinct accounts in that category\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\nfunction renderCategoryStats() {\n    const rows = flatten();\n    if (!rows.length) { noData(); return; }\n\n    const map = {};\n    rows.forEach(r => {\n        const k = String(r.category_id);\n        if (!map[k]) map[k] = { name: r.category_name, accounts: new Set(), bench: 0 };\n        const g = map[k];\n        if (!g.accounts.has(r.account_id)) {\n            g.accounts.add(r.account_id);\n            g.bench += parseFloat(r.hrs_per_month || 0);\n        }\n    });\n\n    const items = Object.values(map).sort((a, b) => a.name.localeCompare(b.name));\n    S.pageItems  = items;\n    const total  = items.length;\n    const start  = (S.page - 1) * S.flatPer;\n    const slice  = items.slice(start, start + S.flatPer);\n\n    const html = `<div class=\"t-wrap\"><table class=\"t-stats\">\n        <thead><tr>\n            <th>Category<\/th>\n            <th>Benchmark\/Month<\/th>\n        <\/tr><\/thead>\n        <tbody>${slice.map(e => `<tr>\n            <td>${e.name}<\/td>\n            <td>${e.bench.toFixed(2)}<\/td>\n        <\/tr>`).join('')}<\/tbody>\n    <\/table><\/div>`;\n\n    paint(html, total, true);\n}\n\n\/\/  UI HELPERS\nfunction loading() {\n    document.getElementById('contentArea').innerHTML =\n        `<div class=\"loading-wrap\"><div class=\"spinner\"><\/div><span style=\"color:var(--text-muted);font-size:14px;\">Loading data\u2026<\/span><\/div>`;\n    document.getElementById('pagFooter').style.display = 'none';\n}\nfunction noData() {\n    document.getElementById('contentArea').innerHTML = `<div class=\"no-data\">No Data Found<\/div>`;\n    document.getElementById('pagFooter').style.display = 'none';\n}\nfunction paint(html, total, flat = false) {\n    if (!html || total === 0) { noData(); return; }\n    document.getElementById('contentArea').innerHTML = html;\n    renderPag(total, flat);\n}\n\n\/\/  PAGINATION\nfunction renderPag(total, flat) {\n    const per  = flat ? S.flatPer : S.blockPer;\n    const tpgs = Math.ceil(total \/ per);\n    const foot = document.getElementById('pagFooter');\n\n    if (tpgs <= 1) { foot.style.display = 'none'; return; }\n    foot.style.display = 'flex';\n\n    const s = (S.page - 1) * per + 1;\n    const e = Math.min(S.page * per, total);\n    document.getElementById('entryInfo').textContent = `Showing ${s} to ${e} of ${total} entries`;\n\n    document.getElementById('prevBtn').className = S.page <= 1 ? 'nav-btn disabled' : 'nav-btn';\n    document.getElementById('nextBtn').className = S.page >= tpgs ? 'nav-btn disabled' : 'nav-btn';\n\n    const nums = document.getElementById('pageNums');\n    nums.innerHTML = '';\n    pgRange(S.page, tpgs).forEach(p => {\n        if (p === '\u2026') {\n            const el = document.createElement('span');\n            el.style.cssText = 'width:32px;height:32px;display:flex;align-items:center;justify-content:center;color:#ccc;font-size:13px;';\n            el.textContent = '\u2026'; nums.appendChild(el);\n        } else {\n            const el = document.createElement('span');\n            el.className    = p === S.page ? 'pn active' : 'pn';\n            el.textContent  = p;\n            el.onclick      = () => gotoPage(p);\n            nums.appendChild(el);\n        }\n    });\n}\n\nfunction pgRange(cur, tot) {\n    if (tot <= 7) return Array.from({ length: tot }, (_, i) => i + 1);\n    if (cur <= 4)         return [1,2,3,4,5,'\u2026',tot];\n    if (cur >= tot - 3)   return [1,'\u2026',tot-4,tot-3,tot-2,tot-1,tot];\n    return [1,'\u2026',cur-1,cur,cur+1,'\u2026',tot];\n}\n\nfunction gotoPage(p) {\n    S.page = p; dispatch();\n    window.scrollTo({ top: 0, behavior: 'smooth' });\n}\n\nfunction changePage(d) {\n    const flat  = ['accountStats','categoryStats'].includes(S.activeTab);\n    const per   = flat ? S.flatPer : S.blockPer;\n    const tpgs  = Math.ceil(S.pageItems.length \/ per);\n    const next  = S.page + d;\n    if (next >= 1 && next <= tpgs) gotoPage(next);\n}\n\n\/\/INIT\n    buildYears();\n    loadPTypes();\n<\/script>\n<\/div><\/div>\n\n<\/div><\/div><\/div><\/div>\n","protected":false},"excerpt":{"rendered":"<p>RESOURCE CALCULATOR | ACCOUNT WISE Year * Select Year Project Type * Select Project Type Loading\u2026 Working Hours * 66.577.588.599.510 Search Account Wise Account Stats Category Wise Category Stats No Data Found \u00ab Previous Next \u00bb 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-157","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/ph-portal.zyneventures.com\/index.php\/wp-json\/wp\/v2\/pages\/157","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=157"}],"version-history":[{"count":1,"href":"https:\/\/ph-portal.zyneventures.com\/index.php\/wp-json\/wp\/v2\/pages\/157\/revisions"}],"predecessor-version":[{"id":158,"href":"https:\/\/ph-portal.zyneventures.com\/index.php\/wp-json\/wp\/v2\/pages\/157\/revisions\/158"}],"wp:attachment":[{"href":"https:\/\/ph-portal.zyneventures.com\/index.php\/wp-json\/wp\/v2\/media?parent=157"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}