{"id":132,"date":"2026-03-31T06:46:04","date_gmt":"2026-03-31T06:46:04","guid":{"rendered":"https:\/\/ph-portal.zyneventures.com\/?page_id=132"},"modified":"2026-03-31T11:54:00","modified_gmt":"2026-03-31T11:54:00","slug":"view-schedule","status":"publish","type":"page","link":"https:\/\/ph-portal.zyneventures.com\/index.php\/view-schedule\/","title":{"rendered":"View Schedule"},"content":{"rendered":"<style>.kadence-column132_1d3208-74 > .kt-inside-inner-col,.kadence-column132_1d3208-74 > .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-column132_1d3208-74 > .kt-inside-inner-col{column-gap:var(--global-kb-gap-sm, 1rem);}.kadence-column132_1d3208-74 > .kt-inside-inner-col{flex-direction:column;}.kadence-column132_1d3208-74 > .kt-inside-inner-col > .aligncenter{width:100%;}.kadence-column132_1d3208-74 > .kt-inside-inner-col:before{opacity:0.3;}.kadence-column132_1d3208-74{position:relative;}@media all and (max-width: 1024px){.kadence-column132_1d3208-74 > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}@media all and (max-width: 767px){.kadence-column132_1d3208-74 > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}<\/style>\n<div class=\"wp-block-kadence-column kadence-column132_1d3208-74\"><div class=\"kt-inside-inner-col\"><style>.kb-row-layout-id132_3f12a2-0c > .kt-row-column-wrap{align-content:start;}:where(.kb-row-layout-id132_3f12a2-0c > .kt-row-column-wrap) > .wp-block-kadence-column{justify-content:start;}.kb-row-layout-id132_3f12a2-0c > .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-id132_3f12a2-0c > .kt-row-layout-overlay{opacity:0.30;}@media all and (max-width: 1024px){.kb-row-layout-id132_3f12a2-0c > .kt-row-column-wrap{grid-template-columns:minmax(0, 1fr);}}@media all and (max-width: 767px){.kb-row-layout-id132_3f12a2-0c > .kt-row-column-wrap{grid-template-columns:minmax(0, 1fr);}}<\/style><div class=\"kb-row-layout-wrap kb-row-layout-id132_3f12a2-0c 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-column132_45dbb1-41 > .kt-inside-inner-col,.kadence-column132_45dbb1-41 > .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-column132_45dbb1-41 > .kt-inside-inner-col{column-gap:var(--global-kb-gap-sm, 1rem);}.kadence-column132_45dbb1-41 > .kt-inside-inner-col{flex-direction:column;}.kadence-column132_45dbb1-41 > .kt-inside-inner-col > .aligncenter{width:100%;}.kadence-column132_45dbb1-41 > .kt-inside-inner-col:before{opacity:0.3;}.kadence-column132_45dbb1-41{position:relative;}@media all and (max-width: 1024px){.kadence-column132_45dbb1-41 > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}@media all and (max-width: 767px){.kadence-column132_45dbb1-41 > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}<\/style>\n<div class=\"wp-block-kadence-column kadence-column132_45dbb1-41\"><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: #28a745;\n        --error-red: #dc3545;\n    }\n\n    body { \n        margin: 0; \n        font-family: 'Sora', sans-serif; \n        color: var(--text-dark); \n    }\n\n    \/* Header Layout *\/\n    .header-container { \n        display: flex; \n        justify-content: space-between; \n        align-items: center; \n        margin-bottom: 30px; \n    }\n    \n    .header-container h2 { \n        margin: 0; \n        font-size: 21px; \n        font-weight: 500; \n        text-transform: uppercase;\n        letter-spacing: 0.5px;\n    }\n\n    .action-group { \n        display: flex; \n        gap: 12px; \n        align-items: center; \n    }\n\n    \/* Search & Filter UI *\/\n    .search-wrapper { \n        background: #F1F3F4; \n        border-radius: 6px; \n        padding: 9px 15px; \n        display: flex; \n        align-items: center; \n        width: 260px; \n        gap: 10px;\n        border: 1px solid transparent;\n        transition: border-color 0.2s;\n    }\n    .search-wrapper:focus-within {\n        border-color: #d0d5dd;\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', sans-serif;\n        color: var(--text-dark);\n    }\n    .search-wrapper input::placeholder {\n        color: var(--text-muted);\n    }\n\n    .btn { \n        padding: 9px 18px; \n        border-radius: 6px; \n        font-size: 12px; \n        font-weight: 400; \n        cursor: pointer; \n        border: none; \n        transition: background 0.2s, color 0.2s; \n        display: flex; \n        align-items: center; \n        justify-content: center;\n        gap: 6px;\n        white-space: nowrap;\n        font-family: 'Sora', sans-serif;\n    }\n    .btn-primary { \n        background: var(--primary-blue); \n        color: white; \n    }\n    .btn-primary:hover { \n        background: var(--hover-blue); \n    }\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: 9px 12px;\n        border-radius: 6px;\n        cursor: pointer;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        transition: background 0.2s;\n        height: 38px;\n        width: 38px;\n    }\n    .btn-filter:hover { \n        background: transparent; \n    }\n    .btn-filter svg {\n        display: block;\n        flex-shrink: 0;\n    }\n\n    \/* Table Design *\/\n    .table-container {\n        width: 100%;\n        overflow-x: auto;\n        border-radius: 6px;\n        margin-top: 10px;\n    }\n\n    table {\n        width: 100%;\n        border-collapse: collapse;\n        text-align: left;\n        min-width: 980px;\n    }\n\n    thead {\n        background-color: #f5f5f5;\n        border-bottom: 1px solid var(--border-color);\n    }\n\n    th {\n        padding: 10px 16px;\n        font-size: 12px;\n        font-weight: 500;\n        color: #212529;\n        text-transform: uppercase;\n        letter-spacing: 0.3px;\n    }\n\n    td {\n        padding: 14px 16px;\n        font-size: 12px;\n        font-weight: 400;\n        color: #8D8C9C;\n        border-bottom: 1px solid var(--border-color);\n    }\n\n    tbody tr:hover {\n        background: #fafafa;\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    }\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    \/* Delete Icon *\/\n    .delete-btn {\n        cursor: pointer;\n        color: var(--primary-blue);\n        transition: color 0.2s;\n        display: block;\n    }\n    .delete-btn:hover { color: var(--error-red); }\n\n    \/* Pagination Footer *\/\n    .pagination-footer { \n        margin-top: 28px; \n        display: flex; \n        justify-content: space-between; \n        align-items: center; \n        color: var(--text-muted); \n        font-size: 12px;\n        gap: 20px;\n    }\n    .pagination-controls { display: flex; align-items: center; gap: 8px; }\n    .page-num { \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 0.15s, color 0.15s;\n    }\n    .page-num:hover:not(.active) {\n        background: #f1f3f4;\n        color: var(--text-dark);\n    }\n    .page-num.active { \n        background: var(--primary-blue); \n        color: white; \n    }\n    .nav-btn { \n        cursor: pointer; \n        color: var(--text-dark); \n        text-decoration: none; \n        white-space: nowrap;\n        font-size: 12px;\n        padding: 4px 2px;\n        transition: color 0.15s;\n    }\n    .nav-btn:hover:not(.disabled) {\n        color: var(--primary-blue);\n    }\n    .nav-btn.disabled { \n        color: #ccc; \n        pointer-events: none; \n    }\n\n    \/* Responsive \u2014 Tablet *\/\n    @media (max-width: 900px) {\n        .search-wrapper { width: 200px; }\n    }\n\n    \/* Responsive \u2014 Mobile *\/\n    @media (max-width: 768px) {\n        body { padding: 20px; }\n\n        .header-container { \n            flex-direction: column; \n            align-items: flex-start; \n            gap: 16px; \n        }\n\n        .action-group { \n            width: 100%; \n            flex-wrap: wrap;\n            gap: 10px;\n        }\n\n        .search-wrapper { \n            width: 100%; \n            flex: 1 1 100%;\n        }\n\n        .btn-filter {\n            flex-shrink: 0;\n        }\n\n        .btn-primary { \n            flex: 1 1 calc(50% - 5px);\n            padding: 10px 8px; \n            font-size: 12px;\n            justify-content: center;\n        }\n\n        .pagination-footer {\n            flex-direction: column-reverse;\n            gap: 14px;\n            align-items: center;\n        }\n        .pagination-controls {\n            width: 100%;\n            justify-content: center;\n            flex-wrap: wrap;\n        }\n    }\n\n    @media (max-width: 420px) {\n        .btn-primary { \n            flex: 1 1 100%;\n        }\n    }\n<\/style>\n\n<div class=\"header-container\">\n    <h2>View Schedule<\/h2>\n    <div class=\"action-group\">\n        <div class=\"search-wrapper\">\n            <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"color:#8D8C9C;flex-shrink:0\"><circle cx=\"11\" cy=\"11\" r=\"8\"><\/circle><line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"><\/line><\/svg>\n            <input type=\"text\" id=\"searchInput\" placeholder=\"Search Here...\" oninput=\"handleSearch()\">\n        <\/div>\n        <button class=\"btn-filter\" title=\"Filter\">\n            <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"3\" y1=\"6\" x2=\"21\" y2=\"6\"><\/line><line x1=\"6\" y1=\"12\" x2=\"18\" y2=\"12\"><\/line><line x1=\"9\" y1=\"18\" x2=\"15\" y2=\"18\"><\/line><\/svg>\n        <\/button>\n        <button class=\"btn btn-primary\" onclick=\"downloadCSV()\">+ Download CSV<\/button>\n        <button class=\"btn btn-primary\" onclick=\"window.location.href='https:\/\/ph-portal.zyneventures.com\/index.php\/add-schedule\/'\">+ Add New Schedule<\/button>\n    <\/div>\n<\/div>\n\n<div class=\"table-container\">\n    <table id=\"scheduleTable\">\n        <thead>\n            <tr>\n                <th>Schedule Name<\/th>\n                <th>Schedule Hrs\/Day<\/th>\n                <th>From<\/th>\n                <th>To<\/th>\n                <th>Total Working Days<\/th>\n                <th>Schedule Hrs\/Months<\/th>\n                <th>Submitted Date<\/th>\n                <th>Status<\/th>\n                <th>Action<\/th>\n            <\/tr>\n        <\/thead>\n        <tbody id=\"tableBody\"><\/tbody>\n    <\/table>\n<\/div>\n\n<div class=\"pagination-footer\">\n    <div class=\"pagination-controls\">\n        <span id=\"prevBtn\" class=\"nav-btn disabled\">\u00ab Previous<\/span>\n        <div id=\"pageNumbers\" style=\"display:flex; gap:6px;\"><\/div>\n        <span id=\"nextBtn\" class=\"nav-btn disabled\">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        GET_SCHEDULES: `${BASE_URL}\/schedule\/all`,\n        DELETE_SCHEDULE: `${BASE_URL}\/schedule\/delete`\n    };\n\n    let allData = []; \n    let filteredData = []; \n    let pagination = {\n        currentPage: 1,\n        limit: 10,\n        total: 0,\n        pages: 0\n    };\n\n    const getHeaders = () => ({\n        'Content-Type': 'application\/json',\n        'Authorization': `Bearer ${localStorage.getItem(\"TOKEN\")}`\n    });\n\n    async function init() {\n        await fetchAllData();\n        updateTable();\n    }\n\n    async function fetchAllData() {\n        try {\n            const res = await fetch(`${ENDPOINTS.GET_SCHEDULES}?limit=1000`, { \n                headers: getHeaders() \n            });\n            const result = await res.json();\n            \n            if (result.code === 200) {\n                allData = result.data.data;\n                filteredData = [...allData];\n                pagination.total = result.data.pagination.total;\n                pagination.pages = Math.ceil(filteredData.length \/ pagination.limit);\n            }\n        } catch (err) {\n            console.error(\"Fetch Error:\", err);\n        }\n    }\n\n    async function deleteSchedule(id) {\n        if (!confirm(\"Are you sure you want to delete this schedule?\")) return;\n        \n        try {\n            const res = await fetch(ENDPOINTS.DELETE_SCHEDULE, {\n                method: 'POST',\n                headers: getHeaders(),\n                body: JSON.stringify({ id: id })\n            });\n            \n            if (res.ok) {\n                alert(\"Action performed successfully\");\n                await fetchAllData();\n                \/\/ Re-apply current search query after refresh\n                handleSearch();\n            }\n        } catch (err) {\n            console.error(\"Delete Error:\", err);\n        }\n    }\n\n    function handleSearch() {\n        const query = document.getElementById('searchInput').value.toLowerCase().trim();\n        \n        if (!query) {\n            filteredData = [...allData];\n        } else {\n            filteredData = allData.filter(item => \n                (item.name || '').toLowerCase().includes(query) ||\n                (item.status || '').toLowerCase().includes(query) ||\n                (item.submitted_date || '').toLowerCase().includes(query) ||\n                (item.expiry_from || '').toLowerCase().includes(query) ||\n                (item.expiry_to || '').toLowerCase().includes(query)\n            );\n        }\n\n        pagination.currentPage = 1;\n        pagination.pages = Math.ceil(filteredData.length \/ pagination.limit);\n        updateTable();\n    }\n\n    function downloadCSV() {\n        if (!filteredData.length) {\n            alert(\"No data to export.\");\n            return;\n        }\n\n        const headers = [\n            'Schedule Name',\n            'Schedule Hrs\/Day',\n            'From',\n            'To',\n            'Total Working Days',\n            'Schedule Hrs\/Months',\n            'Submitted Date',\n            'Status'\n        ];\n\n        const rows = filteredData.map(item => [\n            `\"${(item.name || '').replace(\/\"\/g, '\"\"')}\"`,\n            item.schedule_hours_day ?? '',\n            `\"${(item.expiry_from || '').replace(\/\"\/g, '\"\"')}\"`,\n            `\"${(item.expiry_to || '').replace(\/\"\/g, '\"\"')}\"`,\n            item.total_working_days ?? '',\n            item.schedule_hours_months ?? '',\n            `\"${(item.submitted_date || '').replace(\/\"\/g, '\"\"')}\"`,\n            item.status || ''\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 link = document.createElement('a');\n        link.href = url;\n        link.download = 'schedules.csv';\n        document.body.appendChild(link);\n        link.click();\n        document.body.removeChild(link);\n        URL.revokeObjectURL(url);\n    }\n\n    function updateTable() {\n        const tbody = document.getElementById('tableBody');\n        tbody.innerHTML = '';\n\n        const start = (pagination.currentPage - 1) * pagination.limit;\n        const end = start + pagination.limit;\n        const pageData = filteredData.slice(start, end);\n\n        if (pageData.length === 0) {\n            const tr = document.createElement('tr');\n            tr.innerHTML = `<td colspan=\"9\" style=\"text-align:center; padding:30px; color:#8D8C9C;\">No records found.<\/td>`;\n            tbody.appendChild(tr);\n        } else {\n            pageData.forEach(item => {\n                const tr = document.createElement('tr');\n                tr.innerHTML = `\n                    <td style=\"font-weight:400; color:#212529\">${item.name || '-'}<\/td>\n                    <td>${item.schedule_hours_day ?? '-'}<\/td>\n                    <td>${item.expiry_from || '-'}<\/td>\n                    <td>${item.expiry_to || '-'}<\/td>\n                    <td>${item.total_working_days ?? '-'}<\/td>\n                    <td>${item.schedule_hours_months ?? '-'}<\/td>\n                    <td>${item.submitted_date || '-'}<\/td>\n                    <td>\n                        <span class=\"status-badge ${item.status === 'Active' ? 'status-active' : 'status-inactive'}\">\n                            ${item.status || 'Inactive'}\n                        <\/span>\n                    <\/td>\n                    <td>\n                        <svg class=\"delete-btn\" onclick=\"deleteSchedule(${item.id})\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n                            <polyline points=\"3 6 5 6 21 6\"><\/polyline>\n                            <path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"><\/path>\n                            <line x1=\"10\" y1=\"11\" x2=\"10\" y2=\"17\"><\/line>\n                            <line x1=\"14\" y1=\"11\" x2=\"14\" y2=\"17\"><\/line>\n                        <\/svg>\n                    <\/td>\n                `;\n                tbody.appendChild(tr);\n            });\n        }\n\n        renderPagination();\n        \n        const displayStart = filteredData.length === 0 ? 0 : start + 1;\n        const displayEnd = Math.min(end, filteredData.length);\n        document.getElementById('entryInfo').textContent = \n            `Showing ${displayStart} to ${displayEnd} of ${filteredData.length} entries`;\n    }\n\n    function renderPagination() {\n        const container = document.getElementById('pageNumbers');\n        container.innerHTML = '';\n\n        const prevBtn = document.getElementById('prevBtn');\n        const nextBtn = document.getElementById('nextBtn');\n        const total = pagination.pages;\n        const cur = pagination.currentPage;\n\n        prevBtn.className = cur === 1 ? 'nav-btn disabled' : 'nav-btn';\n        prevBtn.onclick = () => { \n            if (cur > 1) { \n                pagination.currentPage--; \n                updateTable(); \n            }\n        };\n\n        nextBtn.className = cur === total || total === 0 ? 'nav-btn disabled' : 'nav-btn';\n        nextBtn.onclick = () => { \n            if (cur < total) { \n                pagination.currentPage++; \n                updateTable(); \n            }\n        };\n\n        \/\/ Ellipsis pagination: show max 5 page numbers\n        let pages = [];\n        if (total <= 5) {\n            for (let i = 1; i <= total; i++) pages.push(i);\n        } else {\n            pages.push(1);\n            if (cur > 3) pages.push('...');\n            for (let i = Math.max(2, cur - 1); i <= Math.min(total - 1, cur + 1); i++) {\n                pages.push(i);\n            }\n            if (cur < total - 2) pages.push('...');\n            pages.push(total);\n        }\n\n        pages.forEach(p => {\n            const span = document.createElement('span');\n            if (p === '...') {\n                span.textContent = '...';\n                span.style.cssText = 'width:32px;height:32px;display:flex;align-items:center;justify-content:center;font-size:13px;color:#8D8C9C;';\n            } else {\n                span.className = p === cur ? 'page-num active' : 'page-num';\n                span.textContent = p;\n                span.onclick = () => { pagination.currentPage = p; updateTable(); };\n            }\n            container.appendChild(span);\n        });\n    }\n\n    init();\n<\/script>\n<\/div><\/div>\n\n<\/div><\/div><\/div><\/div>\n","protected":false},"excerpt":{"rendered":"<p>View Schedule + Download CSV + Add New Schedule Schedule Name Schedule Hrs\/Day From To Total Working Days Schedule Hrs\/Months Submitted Date 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":"","_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-132","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/ph-portal.zyneventures.com\/index.php\/wp-json\/wp\/v2\/pages\/132","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=132"}],"version-history":[{"count":3,"href":"https:\/\/ph-portal.zyneventures.com\/index.php\/wp-json\/wp\/v2\/pages\/132\/revisions"}],"predecessor-version":[{"id":212,"href":"https:\/\/ph-portal.zyneventures.com\/index.php\/wp-json\/wp\/v2\/pages\/132\/revisions\/212"}],"wp:attachment":[{"href":"https:\/\/ph-portal.zyneventures.com\/index.php\/wp-json\/wp\/v2\/media?parent=132"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}