{"id":57,"date":"2026-03-30T12:43:11","date_gmt":"2026-03-30T12:43:11","guid":{"rendered":"https:\/\/ph-portal.zyneventures.com\/?page_id=57"},"modified":"2026-03-31T12:23:45","modified_gmt":"2026-03-31T12:23:45","slug":"users","status":"publish","type":"page","link":"https:\/\/ph-portal.zyneventures.com\/index.php\/users\/","title":{"rendered":"Users"},"content":{"rendered":"<style>.kadence-column57_6f0d9f-44 > .kt-inside-inner-col,.kadence-column57_6f0d9f-44 > .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-column57_6f0d9f-44 > .kt-inside-inner-col{column-gap:var(--global-kb-gap-sm, 1rem);}.kadence-column57_6f0d9f-44 > .kt-inside-inner-col{flex-direction:column;}.kadence-column57_6f0d9f-44 > .kt-inside-inner-col > .aligncenter{width:100%;}.kadence-column57_6f0d9f-44 > .kt-inside-inner-col:before{opacity:0.3;}.kadence-column57_6f0d9f-44{position:relative;}@media all and (max-width: 1024px){.kadence-column57_6f0d9f-44 > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}@media all and (max-width: 767px){.kadence-column57_6f0d9f-44 > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}<\/style>\n<div class=\"wp-block-kadence-column kadence-column57_6f0d9f-44\"><div class=\"kt-inside-inner-col\"><style>.kb-row-layout-id57_83e0e8-fa > .kt-row-column-wrap{align-content:start;}:where(.kb-row-layout-id57_83e0e8-fa > .kt-row-column-wrap) > .wp-block-kadence-column{justify-content:start;}.kb-row-layout-id57_83e0e8-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-id57_83e0e8-fa > .kt-row-layout-overlay{opacity:0.30;}@media all and (max-width: 1024px){.kb-row-layout-id57_83e0e8-fa > .kt-row-column-wrap{grid-template-columns:minmax(0, 1fr);}}@media all and (max-width: 767px){.kb-row-layout-id57_83e0e8-fa > .kt-row-column-wrap{grid-template-columns:minmax(0, 1fr);}}<\/style><div class=\"kb-row-layout-wrap kb-row-layout-id57_83e0e8-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-column57_129a7a-47 > .kt-inside-inner-col,.kadence-column57_129a7a-47 > .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-column57_129a7a-47 > .kt-inside-inner-col{column-gap:var(--global-kb-gap-sm, 1rem);}.kadence-column57_129a7a-47 > .kt-inside-inner-col{flex-direction:column;}.kadence-column57_129a7a-47 > .kt-inside-inner-col > .aligncenter{width:100%;}.kadence-column57_129a7a-47 > .kt-inside-inner-col:before{opacity:0.3;}.kadence-column57_129a7a-47{position:relative;}@media all and (max-width: 1024px){.kadence-column57_129a7a-47 > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}@media all and (max-width: 767px){.kadence-column57_129a7a-47 > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}<\/style>\n<div class=\"wp-block-kadence-column kadence-column57_129a7a-47\"><div class=\"kt-inside-inner-col\">\n<style>\n    :root {\n        --primary-blue: #0180FF;        \n        --hover-blue: #3499FF;\n        --text-dark: #212529;\n        --text-muted: #8D8C9C;\n        --border-color: #EFEFEF;\n        --success-green: #1E8E3E;\n        --error-red: #D93025;\n    }\n\n    body { \n        font-family: 'Sora', sans-serif; \n        color: var(--text-dark); \n        background-color: #fff;\n        box-sizing: border-box;\n    }\n\n    *, *::before, *::after { box-sizing: inherit; }\n    \n    \/* Header Section *\/\n    .header-container { \n        display: flex; \n        justify-content: space-between; \n        align-items: center; \n        margin-bottom: 40px;\n        flex-wrap: wrap;\n        gap: 16px;\n    }\n    .title-group { \n        display: flex; \n        align-items: center; \n        gap: 10px; \n    }\n    .title-group h2 { \n        margin: 0; \n        font-size: 21px; \n        font-weight: 500; \n        color: #212529;\n        text-transform: uppercase; \n        letter-spacing: 0.5px; \n    }\n    .count { \n        color: var(--text-muted); \n        font-size: 16px; \n        font-weight: 400; \n    }\n    \n    .action-group { \n        display: flex; \n        gap: 12px; \n        align-items: center;\n        flex-wrap: wrap;\n    }\n\n    \/* Search Bar *\/\n    .search-wrapper { \n        background: #F1F3F4; \n        border-radius: 6px; \n        padding: 8px 15px; \n        display: flex; \n        align-items: center; \n        width: 300px; \n        gap: 12px;\n    }\n    .search-icon { \n        width: 18px; \n        height: 18px; \n        color: #8D8C9C;\n        flex-shrink: 0;\n    }\n    .search-wrapper input { \n        border: none; \n        background: transparent; \n        outline: none; \n        width: 100%; \n        font-size: 12px; \n        font-family: 'Sora'; \n        color: #212529;\n    }\n\n    .btn { \n        padding: 8px 15px; \n        border-radius: 6px; \n        font-size: 12px; \n        font-weight: 400; \n        cursor: pointer; \n        border: none; \n        transition: 0.2s; \n        display: flex; \n        align-items: center; \n        justify-content: center;\n        gap: 6px;\n        white-space: nowrap;\n    }\n    .btn-primary { background: var(--primary-blue); color: white; }\n    .btn-primary:hover { background: var(--hover-blue); }\n\n    \/* Filter button \u2014 matches original screenshot exactly *\/\n    .btn-filter {\n        background: transparent;\n        border: 1px solid;\n        border-color: #F1F3F4;\n        color: #5f6368;\n        padding: 7px 10px;\n        border-radius: 6px;\n        cursor: pointer;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        transition: 0.2s;\n    }\n    .btn-filter:hover { background: transparent; color: #5f6368;}\n    .btn-filter svg { display: block; }\n\n    \/* CSV button with download arrow icon like screenshot *\/\n    .btn-csv {\n        background: var(--primary-blue);\n        color: white;\n        display: flex;\n        align-items: center;\n        gap: 6px;\n    }\n    .btn-csv:hover { background: var(--hover-blue); }\n\n    \/* Table Styling *\/\n    .table-container {\n        width: 100%;\n        overflow-x: auto;\n        -webkit-overflow-scrolling: touch;\n    }\n    .data-table {\n        width: 100%;\n        border-collapse: collapse;\n        min-width: 800px;\n    }\n    .data-table th {\n        text-align: left;\n        padding: 12px 15px;\n        font-size: 12px;\n        color: #212529;\n        font-weight: 500;\n        border-bottom: 1px solid var(--border-color);\n        background: #f5f5f5;\n        white-space: nowrap;\n        letter-spacing: 0.3px;\n    }\n    .data-table td {\n        padding: 10px 15px;\n        font-size: 12px;\n        border-bottom: 1px solid var(--border-color);\n        color: #8D8C9C;\n        vertical-align: middle;\n    }\n    \n    \/* Status Badge *\/\n    .status-badge { \n        padding: 4px 12px; \n        border-radius: 20px; \n        font-size: 12px; \n        font-weight: 400; \n        display: inline-flex; \n        align-items: center; \n        gap: 6px; \n        white-space: nowrap;\n    }\n    .status-active { background: #E6F4EA; color: var(--success-green); }\n    .status-active::before { content: '\u25cf'; font-size: 8px; }\n    .status-inactive { background: #FCE8E6; color: var(--error-red); }\n    .status-inactive::before { content: '\u25cf'; font-size: 8px; }\n\n    \/* Department Pill *\/\n    .dept-pill {\n        padding: 4px 12px;\n        border-radius: 12px;\n        background: #D0FDF5;\n        color: #8D8C9C;\n        font-size: 12px;\n        font-weight: 400;\n        white-space: nowrap;\n        display: inline-block;\n    }\n\n    \/* User Info Cell *\/\n    .user-info { display: flex; align-items: center; gap: 10px; }\n    .user-avatar { \n        width: 36px; \n        height: 36px; \n        border-radius: 50%;\n        flex-shrink: 0;\n        object-fit: cover;\n    }\n    .user-details b { display: block; color: var(--text-dark); font-size: 12px; font-weight: 500; }\n    .user-details span { font-size: 12px; color: var(--text-muted); }\n\n    \/* Action column *\/\n    .action-col { font-size: 12px; }\n    .action-col a { display: block; color: #718096; text-decoration: none; margin-bottom: 4px; }\n    .action-col .edit-link { color: #718096; cursor: pointer; text-decoration: underline; }\n\n    \/* Pagination *\/\n    .pagination-footer { \n        margin-top: 30px; \n        display: flex; \n        justify-content: space-between; \n        align-items: center; \n        color: var(--text-muted); \n        font-size: 12px;\n        flex-wrap: wrap;\n        gap: 12px;\n    }\n    .pagination-controls { \n        display: flex; \n        align-items: center; \n        gap: 6px;\n        flex-wrap: wrap;\n    }\n    .page-num { \n        cursor: pointer; \n        width: 30px; \n        height: 30px; \n        display: flex; \n        align-items: center; \n        justify-content: center; \n        border-radius: 50%; \n        font-weight: 500;\n        font-size: 12px;\n        transition: 0.15s;\n        user-select: none;\n    }\n    .page-num:hover:not(.active) { background: #F1F3F4; }\n    .page-num.active { background: var(--primary-blue); color: white; }\n    .page-ellipsis {\n        width: 30px;\n        height: 30px;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        font-size: 12px;\n        color: var(--text-muted);\n    }\n    .nav-btn { \n        cursor: pointer; \n        color: var(--text-dark); \n        font-weight: 500; \n        text-decoration: none;\n        padding: 4px 2px;\n        user-select: none;\n        white-space: nowrap;\n    }\n    .nav-btn.disabled { color: #ccc; pointer-events: none; }\n\n    \/* Responsive *\/\n    @media (max-width: 768px) {\n        body { padding: 20px 16px; }\n        .header-container { flex-direction: column; align-items: flex-start; }\n        .action-group { width: 100%; }\n        .search-wrapper { width: 100%; flex: 1; min-width: 0; }\n        .pagination-footer { flex-direction: column; align-items: flex-start; }\n        #entryInfo { order: -1; }\n    }\n\n    @media (max-width: 480px) {\n        .title-group h2 { font-size: 16px; }\n        .btn { font-size: 11px; padding: 7px 10px; }\n    }\n<\/style>\n\n<div class=\"header-container\">\n    <div class=\"title-group\">\n        <h2>Users<\/h2>\n        <span class=\"count\" id=\"userCount\">(0)<\/span>\n    <\/div>\n    <div class=\"action-group\">\n        <div class=\"search-wrapper\">\n            <svg class=\"search-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n                <circle cx=\"11\" cy=\"11\" r=\"8\"><\/circle>\n                <line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"><\/line>\n            <\/svg>\n            <input type=\"text\" id=\"searchInput\" placeholder=\"Search Users...\" oninput=\"handleGlobalSearch(this.value)\">\n        <\/div>\n\n        <!-- Filter button \u2014 matches original screenshot -->\n        <button class=\"btn-filter\" title=\"Filter\">\n            <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n                <line x1=\"3\" y1=\"6\" x2=\"21\" y2=\"6\"><\/line>\n                <line x1=\"7\" y1=\"12\" x2=\"17\" y2=\"12\"><\/line>\n                <line x1=\"10\" y1=\"18\" x2=\"14\" y2=\"18\"><\/line>\n            <\/svg>\n        <\/button>\n\n        <a href=\"https:\/\/ph-portal.zyneventures.com\/index.php\/add-new-user\/\" style=\"text-decoration: none;\">\n            <button class=\"btn btn-primary\">+ Add New Users<\/button>\n        <\/a>\n\n        <!-- CSV Download button with arrow icon like screenshot -->\n        <button class=\"btn btn-primary btn-csv\" onclick=\"downloadCSV()\">\n            <!-- <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n                <polyline points=\"8 17 12 21 16 17\"><\/polyline>\n                <line x1=\"12\" y1=\"12\" x2=\"12\" y2=\"21\"><\/line>\n                <path d=\"M20.88 18.09A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.29\"><\/path>\n            <\/svg> -->\n            CSV Download\n        <\/button>\n    <\/div>\n<\/div>\n\n<div class=\"table-container\">\n    <table class=\"data-table\">\n        <thead>\n            <tr>\n                <th>USER ID<\/th>\n                <th>FULL NAME<\/th>\n                <th>PESUDO NAME<\/th>\n                <th>ROLE<\/th>\n                <th>DEPARTMENT<\/th>\n                <th>TEAM<\/th>\n                <th>REPORT TO<\/th>\n                <th>PROJECTS<\/th>\n                <th>STATUS<\/th>\n                <th>ACTION<\/th>\n            <\/tr>\n        <\/thead>\n        <tbody id=\"userTableBody\"><\/tbody>\n    <\/table>\n<\/div>\n\n<div class=\"pagination-footer\">\n    <div class=\"pagination-controls\">\n        <span id=\"prevBtn\" class=\"nav-btn\">\u00ab Previous<\/span>\n        <div id=\"pageNumbers\" style=\"display:flex; gap:4px; align-items:center;\"><\/div>\n        <span id=\"nextBtn\" class=\"nav-btn\">Next \u00bb<\/span>\n    <\/div>\n    <div id=\"entryInfo\">Showing 0 to 0 of 0 entries<\/div>\n<\/div>\n\n<script>\n    const BASE_URL = 'https:\/\/api-ph-portal.zyneventures.com\/api\/v1';\n    const ENDPOINTS = {\n        ALL_USERS: `${BASE_URL}\/user\/all`,\n        EDIT_PAGE: 'https:\/\/ph-portal.zyneventures.com\/index.php\/edit-user\/'\n    };\n\n    let paginationData = { page: 1, limit: 10, total: 0, pages: 0 };\n    let currentSearch = '';\n    let searchTimer;\n    let allFetchedUsers = []; \/\/ cache for CSV\n\n    const getHeaders = () => ({\n        'Content-Type': 'application\/json',\n        'Authorization': `Bearer ${localStorage.getItem(\"TOKEN\")}`\n    });\n\n    async function fetchUsers(page = 1, search = '') {\n        try {\n            const res = await fetch(`${ENDPOINTS.ALL_USERS}?page=${page}&limit=10&search=${encodeURIComponent(search)}`, { \n                headers: getHeaders() \n            });\n            const result = await res.json();\n            \n            if (result.code === 200) {\n                const users = result.data.data;\n                paginationData = result.data.pagination;\n                allFetchedUsers = users;\n                renderUserTable(users);\n                renderPagination();\n            }\n        } catch (err) { \n            console.error(\"Fetch Error:\", err); \n        }\n    }\n\n    function renderUserTable(users) {\n        const tbody = document.getElementById('userTableBody');\n        document.getElementById('userCount').textContent = `(${paginationData.total})`;\n        \n        const start = paginationData.total === 0 ? 0 : ((paginationData.page - 1) * paginationData.limit) + 1;\n        const end = Math.min(paginationData.page * paginationData.limit, paginationData.total);\n        document.getElementById('entryInfo').textContent = `Showing ${start} to ${end} of ${paginationData.total} entries`;\n\n        if (users.length === 0) {\n            tbody.innerHTML = '<tr><td colspan=\"10\" style=\"text-align:center; padding:30px;\">No records found.<\/td><\/tr>';\n            return;\n        }\n\n        tbody.innerHTML = users.map(user => `\n            <tr>\n                <td>${user.id}<\/td>\n                <td>\n                    <div class=\"user-info\">\n                        <img decoding=\"async\" class=\"user-avatar\" src=\"https:\/\/ui-avatars.com\/api\/?name=${encodeURIComponent(user.name)}&#038;background=random\" alt=\"${user.name}\">\n                        <div class=\"user-details\">\n                            <b>${user.name}<\/b>\n                            <span>${user.email}<\/span>\n                        <\/div>\n                    <\/div>\n                <\/td>\n                <td>${user.pesudo_name || '\u2014'}<\/td>\n                <td>${user.role || '\u2014'}<\/td>\n                <td><span class=\"dept-pill\">${user.department_name || '\u2014'}<\/span><\/td>\n                <td>${user.team_name || '\u2014'}<\/td>\n                <td>${user.report_to_name || '\u2014'}<\/td>\n                <td>${user.projects || '\u2014'}<\/td>\n                <td>\n                    <span class=\"status-badge ${user.is_active ? 'status-active' : 'status-inactive'}\">\n                        ${user.is_active ? 'Active' : 'Inactive'}\n                    <\/span>\n                <\/td>\n                <td>\n                    <div class=\"action-col\">\n                        <a>Monthly %<\/a>\n                        <a class=\"edit-link\" onclick=\"handleEdit(${user.id})\">\u270e Edit<\/a>\n                    <\/div>\n                <\/td>\n            <\/tr>\n        `).join('');\n    }\n\n    function handleGlobalSearch(val) {\n        clearTimeout(searchTimer);\n        searchTimer = setTimeout(() => {\n            currentSearch = val;\n            fetchUsers(1, currentSearch);\n        }, 500);\n    }\n\n    function handleEdit(userId) {\n        window.location.href = `${ENDPOINTS.EDIT_PAGE}?id=${userId}`;\n    }\n\n    function renderPagination() {\n        const container = document.getElementById('pageNumbers');\n        container.innerHTML = '';\n        \n        const totalPages = paginationData.pages;\n        const currentPage = paginationData.page;\n\n        \/\/ Prev button\n        const prevBtn = document.getElementById('prevBtn');\n        prevBtn.onclick = () => fetchUsers(currentPage - 1, currentSearch);\n        prevBtn.className = currentPage === 1 ? 'nav-btn disabled' : 'nav-btn';\n        \n        \/\/ Build page number array with ellipsis logic\n        \/\/ Pattern: 1 2 3 4 5 ... 25 26  (like screenshot)\n        const pages = [];\n\n        if (totalPages <= 7) {\n            for (let i = 1; i <= totalPages; i++) pages.push(i);\n        } else {\n            \/\/ Always show first few pages + ellipsis + last pages\n            const nearStart = currentPage <= 5;\n            const nearEnd = currentPage >= totalPages - 4;\n\n            if (nearStart) {\n                for (let i = 1; i <= Math.min(7, totalPages - 2); i++) pages.push(i);\n                pages.push('...');\n                pages.push(totalPages - 1);\n                pages.push(totalPages);\n            } else if (nearEnd) {\n                pages.push(1);\n                pages.push(2);\n                pages.push('...');\n                for (let i = totalPages - 6; i <= totalPages; i++) pages.push(i);\n            } else {\n                pages.push(1);\n                pages.push(2);\n                pages.push('...');\n                for (let i = currentPage - 1; i <= currentPage + 1; i++) pages.push(i);\n                pages.push('...');\n                pages.push(totalPages - 1);\n                pages.push(totalPages);\n            }\n        }\n\n        pages.forEach(p => {\n            if (p === '...') {\n                const el = document.createElement('span');\n                el.className = 'page-ellipsis';\n                el.textContent = '...';\n                container.appendChild(el);\n            } else {\n                const span = document.createElement('span');\n                span.className = p === currentPage ? 'page-num active' : 'page-num';\n                span.textContent = p;\n                span.onclick = () => fetchUsers(p, currentSearch);\n                container.appendChild(span);\n            }\n        });\n\n        \/\/ Next button\n        const nextBtn = document.getElementById('nextBtn');\n        nextBtn.onclick = () => fetchUsers(currentPage + 1, currentSearch);\n        nextBtn.className = currentPage === totalPages ? 'nav-btn disabled' : 'nav-btn';\n    }\n\n    \/\/ CSV Download \u2014 fetches all pages for current search and downloads\n    async function downloadCSV() {\n        try {\n            const btn = document.querySelector('.btn-csv');\n            const origText = btn.innerHTML;\n            btn.innerHTML = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"\/><polyline points=\"12 6 12 12 16 14\"\/><\/svg> Downloading...`;\n            btn.disabled = true;\n\n            \/\/ Fetch all users for current search (up to total)\n            const totalPages = paginationData.pages || 1;\n            let allUsers = [];\n\n            for (let p = 1; p <= totalPages; p++) {\n                const res = await fetch(`${ENDPOINTS.ALL_USERS}?page=${p}&#038;limit=10&#038;search=${encodeURIComponent(currentSearch)}`, { \n                    headers: getHeaders() \n                });\n                const result = await res.json();\n                if (result.code === 200) {\n                    allUsers = allUsers.concat(result.data.data);\n                }\n            }\n\n            \/\/ Build CSV\n            const headers = ['User ID', 'Full Name', 'Email', 'Pseudo Name', 'Role', 'Department', 'Team', 'Report To', 'Projects', 'Status'];\n            const rows = allUsers.map(u => [\n                u.id,\n                escapeCsv(u.name),\n                escapeCsv(u.email),\n                escapeCsv(u.pesudo_name || ''),\n                escapeCsv(u.role || ''),\n                escapeCsv(u.department_name || ''),\n                escapeCsv(u.team_name || ''),\n                escapeCsv(u.report_to_name || ''),\n                escapeCsv(String(u.projects || '')),\n                u.is_active ? 'Active' : 'Inactive'\n            ]);\n\n            const csvContent = [headers.join(','), ...rows.map(r => r.join(','))].join('\\n');\n            const blob = new Blob([csvContent], { type: 'text\/csv;charset=utf-8;' });\n            const url = URL.createObjectURL(blob);\n            const a = document.createElement('a');\n            a.href = url;\n            a.download = `users_${new Date().toISOString().slice(0,10)}.csv`;\n            document.body.appendChild(a);\n            a.click();\n            document.body.removeChild(a);\n            URL.revokeObjectURL(url);\n\n            btn.innerHTML = origText;\n            btn.disabled = false;\n        } catch (err) {\n            console.error('CSV Download Error:', err);\n            const btn = document.querySelector('.btn-csv');\n            btn.innerHTML = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"8 17 12 21 16 17\"\/><line x1=\"12\" y1=\"12\" x2=\"12\" y2=\"21\"\/><path d=\"M20.88 18.09A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.29\"\/><\/svg> CSV Download`;\n            btn.disabled = false;\n        }\n    }\n\n    function escapeCsv(val) {\n        if (val === null || val === undefined) return '';\n        const str = String(val);\n        if (str.includes(',') || str.includes('\"') || str.includes('\\n')) {\n            return `\"${str.replace(\/\"\/g, '\"\"')}\"`;\n        }\n        return str;\n    }\n\n    \/\/ Initial Load\n    document.addEventListener('DOMContentLoaded', () => fetchUsers());\n<\/script>\n<\/div><\/div>\n\n<\/div><\/div><\/div><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Users (0) + Add New Users CSV Download USER ID FULL NAME PESUDO NAME ROLE DEPARTMENT TEAM REPORT TO PROJECTS STATUS ACTION \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":"default","_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-57","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/ph-portal.zyneventures.com\/index.php\/wp-json\/wp\/v2\/pages\/57","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=57"}],"version-history":[{"count":6,"href":"https:\/\/ph-portal.zyneventures.com\/index.php\/wp-json\/wp\/v2\/pages\/57\/revisions"}],"predecessor-version":[{"id":219,"href":"https:\/\/ph-portal.zyneventures.com\/index.php\/wp-json\/wp\/v2\/pages\/57\/revisions\/219"}],"wp:attachment":[{"href":"https:\/\/ph-portal.zyneventures.com\/index.php\/wp-json\/wp\/v2\/media?parent=57"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}