{"id":81,"date":"2026-03-31T04:45:53","date_gmt":"2026-03-31T04:45:53","guid":{"rendered":"https:\/\/ph-taskportal.zyneventures.com\/?page_id=81"},"modified":"2026-07-09T05:43:20","modified_gmt":"2026-07-09T05:43:20","slug":"assign-work","status":"publish","type":"page","link":"https:\/\/ph-portal-v2.zyneventures.com\/index.php\/assign-work\/","title":{"rendered":"Assign Work"},"content":{"rendered":"<style>.kadence-column81_24e0e0-be > .kt-inside-inner-col,.kadence-column81_24e0e0-be > .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-column81_24e0e0-be > .kt-inside-inner-col{column-gap:var(--global-kb-gap-sm, 1rem);}.kadence-column81_24e0e0-be > .kt-inside-inner-col{flex-direction:column;}.kadence-column81_24e0e0-be > .kt-inside-inner-col > .aligncenter{width:100%;}.kadence-column81_24e0e0-be > .kt-inside-inner-col:before{opacity:0.3;}.kadence-column81_24e0e0-be{position:relative;}@media all and (max-width: 1024px){.kadence-column81_24e0e0-be > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}@media all and (max-width: 767px){.kadence-column81_24e0e0-be > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}<\/style>\n<div class=\"wp-block-kadence-column kadence-column81_24e0e0-be\"><div class=\"kt-inside-inner-col\"><style>.kb-row-layout-id81_867e53-d4 > .kt-row-column-wrap{align-content:start;}:where(.kb-row-layout-id81_867e53-d4 > .kt-row-column-wrap) > .wp-block-kadence-column{justify-content:start;}.kb-row-layout-id81_867e53-d4 > .kt-row-column-wrap{column-gap:var(--global-kb-gap-md, 2rem);row-gap:var(--global-kb-gap-md, 2rem);padding-top:var(--global-kb-spacing-sm, 1.5rem);padding-bottom:var(--global-kb-spacing-sm, 1.5rem);grid-template-columns:minmax(0, 1fr);}.kb-row-layout-id81_867e53-d4 > .kt-row-layout-overlay{opacity:0.30;}@media all and (max-width: 1024px){.kb-row-layout-id81_867e53-d4 > .kt-row-column-wrap{grid-template-columns:minmax(0, 1fr);}}@media all and (max-width: 767px){.kb-row-layout-id81_867e53-d4 > .kt-row-column-wrap{grid-template-columns:minmax(0, 1fr);}}<\/style><div class=\"kb-row-layout-wrap kb-row-layout-id81_867e53-d4 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-column81_ba4a29-37 > .kt-inside-inner-col,.kadence-column81_ba4a29-37 > .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-column81_ba4a29-37 > .kt-inside-inner-col{column-gap:var(--global-kb-gap-sm, 1rem);}.kadence-column81_ba4a29-37 > .kt-inside-inner-col{flex-direction:column;}.kadence-column81_ba4a29-37 > .kt-inside-inner-col > .aligncenter{width:100%;}.kadence-column81_ba4a29-37 > .kt-inside-inner-col:before{opacity:0.3;}.kadence-column81_ba4a29-37{position:relative;}@media all and (max-width: 1024px){.kadence-column81_ba4a29-37 > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}@media all and (max-width: 767px){.kadence-column81_ba4a29-37 > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}<\/style>\n<div class=\"wp-block-kadence-column kadence-column81_ba4a29-37\"><div class=\"kt-inside-inner-col\">\n<style>\n    :root {\n        --primary-blue: #0180FF;\n        --hover-blue: #3499FF;\n        --text-dark: #212529;\n        --text-muted: #8D8C9C;\n        --border-color: #EFEFEF;\n        --bg-form: #F5F5F5; \n        --input-bg: #FFFFFF;\n    }\n\n    body {\n        margin: 0;\n        font-family: 'Sora', sans-serif;\n        color: var(--text-dark);\n        overflow-x: hidden;\n    }\n\n    \/* Header Layout *\/\n    .header-container {\n        display: flex;\n        justify-content: space-between;\n        align-items: center;\n        margin-bottom: 40px;\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 Buttons *\/\n    .btn {\n        padding: 8px 25px;\n        border-radius: 6px;\n        font-size: 12px;\n        font-weight: 500;\n        cursor: pointer;\n        border: none;\n        transition: 0.2s;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        font-family: 'Sora';\n    }\n\n    .btn-primary { background: var(--primary-blue); color: white; }\n    .btn-primary:hover { background: var(--hover-blue); }\n    \n    .btn-close { \n        background: transparent; \n        color: #212529;\n        font-weight: 500;\n        font-size: 12px;\n        border: none;\n        cursor: pointer;\n        padding: 8px 15px;\n    }\n    .btn-close:hover { \n        color: #212529;\n        background-color: transparent; \n    }\n\n    \/* Form Card - 100% Match to Screenshot *\/\n    .assignment-form-card {\n        background: var(--bg-form);\n        border-radius: 12px;\n        padding: 30px;\n        margin-bottom: 40px;\n    }\n\n    .grid-container {\n        display: grid;\n        grid-template-columns: repeat(3, 1fr);\n        gap: 20px 40px;\n    }\n\n    .form-group {\n        display: flex;\n        flex-direction: column;\n        gap: 8px;\n    }\n\n    .form-group label {\n        font-size: 14px;\n        font-weight: 400;\n        color: var(--text-dark);\n    }\n\n    .form-group label span {\n        color: #FF4D4F;\n        margin-left: 2px;\n    }\n\n    .form-control {\n        width: 100%;\n        padding: 8px 15px;\n        border: 1px solid #E0E0E0;\n        border-radius: 8px;\n        background: var(--input-bg);\n        font-family: 'Sora';\n        font-size: 12px;\n        color: var(--text-dark);\n        box-sizing: border-box;\n        outline: none;\n    }\n\n    select.form-control {\n        appearance: none;\n        background-image: url(\"data:image\/svg+xml,%3Csvg xmlns='http:\/\/www.w3.org\/2000\/svg' width='12' height='12' fill='%23999' viewBox='0 0 16 16'%3E%3Cpath d='M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z'\/%3E%3C\/svg%3E\");\n        background-repeat: no-repeat;\n        background-position: right 15px center;\n    }\n\n    .file-dropdown-item {\n        padding: 10px 14px;\n        font-size: 12px;\n        cursor: pointer;\n        color: #212529;\n        border-bottom: 1px solid #f0f0f0;\n    }\n    .file-dropdown-item:hover,\n    .file-dropdown-item.highlighted {\n        background: #f5f5f5;\n    }\n\n    \/* Action Bar *\/\n    .action-bar {\n        display: flex;\n        justify-content: space-between;\n        align-items: center;\n        margin-bottom: 25px;\n        padding-top: 20px;\n        border-top: 1px solid var(--border-color);\n    }\n\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: 10px;\n    }\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    }\n\n    \/* Table Design *\/\n    .table-container {\n        width: 100%;\n        overflow-x: auto;\n    }\n\n    table {\n        width: 100%;\n        border-collapse: collapse;\n    }\n\n    thead {\n        display: table-header-group;\n    }\n\n    thead tr {\n        display: table-row;\n    }\n\n    th {\n        text-align: left;\n        padding: 15px;\n        font-size: 12px;\n        text-transform: uppercase;\n        color: var(--text-dark);\n        font-weight: 500;\n        background: #f5f5f5;\n        letter-spacing: 0.5px;\n        white-space: nowrap;\n        display: table-cell;\n    }\n\n    td {\n        padding: 15px;\n        font-size: 12px;\n        border-bottom: 1px solid var(--border-color);\n    }\n\n    .no-data {\n        text-align: center;\n        padding: 80px 0;\n        color: #BCC1C8;\n        font-size: 64px;\n        font-weight: 500;\n    }\n\n    \/* Filter Button *\/\n    .btn-filter {\n        background: #FFFFFF;\n        border: 1px solid #D1D5DB;\n        border-radius: 6px;\n        padding: 8px 12px;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        cursor: pointer;\n        transition: 0.2s;\n        min-width: 38px;\n        height: 38px;\n    }\n    .btn-filter:hover { background: #F9FAFB; border-color: #9CA3AF; }\n\n    \/* Amount Field - precisely aligned $ prefix *\/\n    .amount-wrapper {\n        position: relative;\n        width: 100%;\n        display: block;\n    }\n    .amount-wrapper .amount-prefix {\n        position: absolute;\n        left: 15px;\n        top: 50%;\n        transform: translateY(-50%);\n        font-size: 12px;\n        color: #666;\n        font-family: 'Sora', sans-serif;\n        pointer-events: none;\n        line-height: 1;\n        z-index: 1;\n    }\n    .amount-wrapper .amount-input {\n        width: 100%;\n        padding: 8px 15px 8px 28px;\n        border: 1px solid #E0E0E0;\n        border-radius: 8px;\n        background: var(--input-bg);\n        font-family: 'Sora', sans-serif;\n        font-size: 12px;\n        color: var(--text-dark);\n        box-sizing: border-box;\n        outline: none;\n    }\n\n    \/* Responsive *\/\n    @media (max-width: 1100px) {\n        .grid-container { grid-template-columns: repeat(2, 1fr); }\n    }\n    @media (max-width: 900px) {\n        .action-bar { flex-direction: column; align-items: flex-start; gap: 12px; }\n        .action-bar > div { width: 100%; flex-wrap: wrap; }\n        .search-wrapper { width: 100%; flex: 1; min-width: 0; }\n        #submitBtn { flex: 1; }\n    }\n    @media (max-width: 768px) {\n        .grid-container { grid-template-columns: 1fr; }\n        body { padding: 15px; }\n        .header-container { flex-direction: column; align-items: flex-start; gap: 12px; }\n        .header-container > div { width: 100%; display: flex; justify-content: flex-end; }\n        .table-container { overflow-x: auto; -webkit-overflow-scrolling: touch; }\n    }\n    @media (max-width: 480px) {\n        .action-bar > div { flex-direction: column; align-items: stretch; }\n        .search-wrapper { width: 100%; box-sizing: border-box; }\n        #submitBtn { width: 100%; }\n        .btn-filter { align-self: flex-end; }\n    }\n<\/style>\n\n\n<div class=\"header-container\">\n    <h2>Work Assignment &#8211; Data Team<\/h2>\n    <div style=\"display: flex; gap: 10px; align-items: center;\">\n        <button class=\"btn-close\">Close<\/button>\n        <button class=\"btn btn-primary\" id=\"addTopBtn\">+ Add<\/button>\n    <\/div>\n<\/div>\n\n<div class=\"assignment-form-card\">\n    <form id=\"assignmentForm\">\n        <div class=\"grid-container\">\n            <div class=\"form-group\">\n                <label>Project Type <span>*<\/span><\/label>\n                <select class=\"form-control\" id=\"projectType\" required=\"\">\n                    <option value=\"\">Select Project Type<\/option>\n                <\/select>\n            <\/div>\n            <div class=\"form-group\">\n                <label>Assigned date <span>*<\/span><\/label>\n                <input type=\"date\" class=\"form-control\" id=\"assignedDate\" required=\"\">\n            <\/div>\n            <div class=\"form-group\">\n                <label>Assigned to <span>*<\/span><\/label>\n                <select class=\"form-control\" id=\"assignedTo\" required=\"\">\n                    <option value=\"\">Select Assigned To<\/option>\n                <\/select>\n            <\/div>\n\n            <div class=\"form-group\">\n                <label>Received on <span>*<\/span><\/label>\n                <input type=\"date\" class=\"form-control\" id=\"receivedOn\" required=\"\">\n            <\/div>\n            <div class=\"form-group\">\n                <label>Account <span>*<\/span><\/label>\n                <select class=\"form-control\" id=\"account\" required=\"\">\n                    <option value=\"\">Select Account<\/option>\n                <\/select>\n            <\/div>\n            <div class=\"form-group\">\n                <label>Insurance <span>*<\/span><\/label>\n                <select class=\"form-control\" id=\"insurance\" required=\"\">\n                    <option value=\"\">Select Insurance<\/option>\n                <\/select>\n            <\/div>\n\n            <div class=\"form-group\">\n                <label>Pages <span>*<\/span><\/label>\n                <input type=\"number\" class=\"form-control\" id=\"pages\" value=\"0\">\n            <\/div>\n            <div class=\"form-group\" style=\"position:relative;\">\n                <label>File Name <span>*<\/span><\/label>\n                <input type=\"text\" class=\"form-control\" id=\"fileName\" placeholder=\"Search file name...\" autocomplete=\"off\" oninput=\"searchFiles(this.value)\" onkeydown=\"handleFileNameKeyDown(event)\">\n                <input type=\"hidden\" id=\"fileId\">\n                <div id=\"fileDropdown\" role=\"listbox\" style=\"display:none; position:absolute; top:100%; left:0; right:0; background:#fff; border:1px solid #E0E0E0; border-radius:8px; box-shadow:0 4px 12px rgba(0,0,0,0.1); z-index:999; max-height:200px; overflow-y:auto;\"><\/div>\n            <\/div>\n            <div class=\"form-group\">\n                <label>Client <span>*<\/span><\/label>\n                <select class=\"form-control\" id=\"client\" required=\"\">\n                    <option value=\"\">Select Client<\/option>\n                <\/select>\n            <\/div>\n\n            <div class=\"form-group\">\n                <label>Category <span>*<\/span><\/label>\n                <select class=\"form-control\" id=\"category\" required=\"\">\n                    <option value=\"\">Select Category<\/option>\n                <\/select>\n            <\/div>\n            <div class=\"form-group\">\n                <label>Project <span>*<\/span><\/label>\n                <select class=\"form-control\" id=\"project\" required=\"\">\n                    <option value=\"\">Select Project<\/option>\n                <\/select>\n            <\/div>\n            <div class=\"form-group\">\n                <label>Entity <span>*<\/span><\/label>\n                <select class=\"form-control\" id=\"entity\" required=\"\">\n                    <option value=\"\">Select entity<\/option>\n                <\/select>\n            <\/div>\n\n            <div class=\"form-group\">\n                <label>Dos Count <span>*<\/span><\/label>\n                <input type=\"number\" class=\"form-control\" id=\"dosCount\" value=\"0\">\n            <\/div>\n            <div class=\"form-group\">\n                <label>Amount <span>*<\/span><\/label>\n                <div class=\"amount-wrapper\">\n                    <span class=\"amount-prefix\">$<\/span>\n                    <input type=\"number\" class=\"amount-input\" id=\"amount\" value=\"0\">\n                <\/div>\n            <\/div>\n            <div class=\"form-group\">\n                <label>Assigned DOS <span>*<\/span><\/label>\n                <input type=\"number\" class=\"form-control\" id=\"assignedDos\" value=\"0\">\n            <\/div>\n\n            <div class=\"form-group\">\n                <label>Hours <span>*<\/span><\/label>\n                <input type=\"number\" class=\"form-control\" id=\"hours\" value=\"0\">\n            <\/div>\n            <div class=\"form-group\">\n                <label>File Link<\/label>\n                <input type=\"text\" class=\"form-control\" id=\"fileLink\" placeholder=\"N\/A\">\n            <\/div>\n        <\/div>\n    <\/form>\n<\/div>\n\n<div class=\"action-bar\">\n    <button class=\"btn btn-primary\" onclick=\"downloadCSV()\">+ Download CSV<\/button>\n    <div style=\"display: flex; gap: 15px; align-items: center;\">\n        <div class=\"search-wrapper\">\n            <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#8D8C9C\" stroke-width=\"2.5\"><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=\"tableSearch\" placeholder=\"Search workassignment...\" oninput=\"filterTable(this.value)\">\n        <\/div>\n        <button class=\"btn btn-filter\" title=\"Filter\">\n            <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#212529\" stroke-width=\"2\" stroke-linecap=\"round\"><line x1=\"4\" y1=\"6\" x2=\"20\" y2=\"6\"><\/line><line x1=\"7\" y1=\"12\" x2=\"17\" y2=\"12\"><\/line><line x1=\"10\" y1=\"18\" x2=\"14\" y2=\"18\"><\/line><\/svg>\n        <\/button>\n        <button class=\"btn btn-primary\" id=\"submitBtn\">+ Submit work assignments<\/button>\n    <\/div>\n<\/div>\n\n<div class=\"table-container\">\n    <table>\n        <thead id=\"tableHead\"><\/thead>\n        <tbody id=\"tableBody\">\n            <tr>\n                <td colspan=\"10\" class=\"no-data\">No Data Found<\/td>\n            <\/tr>\n        <\/tbody>\n    <\/table>\n<\/div>\n\n<script>\n    const BASE_URL = 'https:\/\/api-ph-portal.zyneventures.com\/api\/v1';\n    const LOGIN_URL = 'https:\/\/ph-portal-v2.zyneventures.com\/index.php\/login\/';\n    const ENDPOINTS = {\n        ADD_TEMP:         `${BASE_URL}\/work_assignment_temp\/add`,\n        GET_TEMP_ALL:     `${BASE_URL}\/work_assignment_temp\/all`,\n        TEMP_SUBMIT:      `${BASE_URL}\/work_assignment\/temp-submit`,\n        PROJECT_TYPES:    `${BASE_URL}\/projecttype\/all`,\n        PROJECT_TYPE_DATA:`${BASE_URL}\/project_type\/databyid`,\n        ACCOUNT_DETAIL:   `${BASE_URL}\/accountandbenchmark\/detail`,\n        FILE_AUTOCOMPLETE:`${BASE_URL}\/file\/autocomplete`,\n        FILE_INFO:        `${BASE_URL}\/file\/info`,\n        INSURANCE_ALL:    `${BASE_URL}\/insurance\/all`\n    };\n\n    \/\/ \u2500\u2500 TOKEN VALIDATION (top-of-page gate) \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    \/\/ Nav_Sidebar.html also checks this, but we guard again here in case the\n    \/\/ page loads standalone or the sidebar JS is deferred.\n    (function enforceAuth() {\n        const t = localStorage.getItem(\"TOKEN\") ||\n                  (document.cookie.match(\/(?:^|;\\s*)TOKEN=([^;]+)\/) || [])[1];\n        if (!t) {\n            window.location.href = LOGIN_URL;\n        }\n    })();\n\n    const getHeaders = () => ({\n        'Content-Type': 'application\/json',\n        'Authorization': `Bearer ${localStorage.getItem(\"TOKEN\") || \"\"}`\n    });\n\n    \/\/ \u2500\u2500 TOKEN EXPIRY HANDLER \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\u2500\u2500\u2500\u2500\u2500\u2500\n    \/\/ Call after every fetch. If the server rejects the token, force re-login.\n    function handleAuthError(res) {\n        if (res.status === 401 || res.status === 403) {\n            localStorage.removeItem(\"TOKEN\");\n            localStorage.removeItem(\"USER_NAME\");\n            localStorage.removeItem(\"USER_DATA\");\n            localStorage.removeItem(\"USER_ID\");\n            localStorage.removeItem(\"USER_ROLE\");\n            localStorage.removeItem(\"PERMISSION_TIER\");\n            localStorage.removeItem(\"USER_PERMISSIONS\");\n            document.cookie = \"TOKEN=; path=\/; expires=Thu, 01 Jan 1970 00:00:00 GMT;\";\n            window.location.href = LOGIN_URL;\n            return true;\n        }\n        return false;\n    }\n\n    \/\/ \u2500\u2500 ROLE-BASED ACCESS CONTROL \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\u2500\u2500\n    \/\/ Source: login.html \u2013 PERMISSION_TIER & USER_DATA set on login\n    \/\/ These values are UI hints ONLY. Backend re-validates every request\n    \/\/ against the authenticated user derived from the Bearer token.\n    const PERMISSION_TIER = (localStorage.getItem(\"PERMISSION_TIER\") || \"none\").toLowerCase();\n    const CURRENT_USER_DATA = (() => {\n        try { return JSON.parse(localStorage.getItem(\"USER_DATA\") || \"{}\"); } catch(e) { return {}; }\n    })();\n    const CURRENT_USER_ID =\n        (localStorage.getItem(\"USER_ID\")) ||\n        (CURRENT_USER_DATA.id ? String(CURRENT_USER_DATA.id) : null);\n\n    \/\/ Capability flags (match product spec)\n    const IS_ASSOCIATE = PERMISSION_TIER === \"associate\";\n    const IS_TEAMLEAD  = PERMISSION_TIER === \"teamlead\";\n    const IS_MANAGER   = PERMISSION_TIER === \"manager\";\n    const IS_ADMIN     = PERMISSION_TIER === \"admin\";\n    const CAN_ADD      = IS_MANAGER || IS_ADMIN;\n    const CAN_EDIT     = IS_TEAMLEAD || IS_MANAGER || IS_ADMIN;\n    const CAN_APPROVE  = IS_MANAGER || IS_ADMIN;\n    const CAN_REVERT   = IS_MANAGER || IS_ADMIN;\n\n    \/\/ Data scope for reads: associate \u2192 personal, teamlead\/manager \u2192 team, admin \u2192 all\n    function getScope() {\n        if (IS_ADMIN) return \"all\";\n        if (IS_MANAGER || IS_TEAMLEAD) return \"team\";\n        return \"personal\";\n    }\n    function withScope(url) {\n        if (IS_ADMIN) return url;\n        const sep = url.includes('?') ? '&' : '?';\n        return `${url}${sep}scope=${encodeURIComponent(getScope())}`;\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\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\n    \/\/ Stores account-detail payload so submit uses correct IDs\n    let selectedAccountDetail = null;\n    const TABLE_COLUMNS = [\n        { key: 'select', label: '', width: '40px' },\n        { key: 'assigned_date', label: 'Assigned Date' },\n        { key: 'assigned_to_name', label: 'Assigned To' },\n        { key: 'pesudo_name', label: 'Pesudo Name' },\n        { key: 'file_name', label: 'File Name' },\n        { key: 'received_on', label: 'Received On' },\n        { key: 'insurance_name', label: 'Insurance' },\n        { key: 'account_name', label: 'Account' },\n        { key: 'client_name', label: 'Client' },\n        { key: 'category_name', label: 'Category' }\n    ];\n\n    \/\/ \u2500\u2500\u2500 INIT \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    window.onload = () => {\n        const today = new Date().toISOString().split('T')[0];\n        document.getElementById('assignedDate').value = today;\n        document.getElementById('receivedOn').value  = today;\n        renderTableHead();\n        renderTableState('Loading work assignments...');\n        fetchProjectTypes();\n        fetchTableData();\n        fetchInsurances();\n        applyReadonlyDependentFields();\n        \/\/ + Add button \u2192 addToTemp\n        document.getElementById('addTopBtn').onclick = addToTemp;\n\n        \/\/ \u2500\u2500 UI CONTROL: hide add\/submit UI for roles that cannot add \u2500\u2500\u2500\u2500\u2500\n        \/\/ Per spec: only Manager + Admin can add work assignments.\n        \/\/ Associates & Team Leads see this page in read-only mode.\n        applyAssignWorkUIPermissions();\n        initSelectKeyboardNavigation();\n    };\n\n    function applyReadonlyDependentFields() {\n        \/\/ PHP parity: client\/category\/project are auto-set from account (readonly selects).\n        ['client', 'category', 'project'].forEach(id => {\n            const el = document.getElementById(id);\n            if (el) el.style.pointerEvents = 'none';\n        });\n    }\n\n    function applyAssignWorkUIPermissions() {\n        if (!CAN_ADD) {\n            \/\/ Hide + Add button in header\n            const addBtn = document.getElementById('addTopBtn');\n            if (addBtn) addBtn.style.display = 'none';\n\n            \/\/ Hide + Submit work assignments button (submitting the add-queue is a write action)\n            const submitBtn = document.getElementById('submitBtn');\n            if (submitBtn) submitBtn.style.display = 'none';\n\n            \/\/ Hide the entire add-assignment form card (associate\/teamlead cannot create)\n            const formCard = document.querySelector('.assignment-form-card');\n            if (formCard) formCard.style.display = 'none';\n\n            \/\/ Swap the page subtitle line to reflect read-only mode\n            const h2 = document.querySelector('.header-container h2');\n            if (h2 && IS_ASSOCIATE) h2.textContent = 'Work Assignment - My Assignments';\n        }\n    }\n\n    \/\/ \u2500\u2500\u2500 INSURANCE \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    async function fetchInsurances() {\n        try {\n            const res    = await fetch(ENDPOINTS.INSURANCE_ALL, { headers: getHeaders() });\n            if (handleAuthError(res)) return;\n            const result = await res.json();\n            if (result.code === 200) {\n                const sel = document.getElementById('insurance');\n                sel.innerHTML = '<option value=\"\">Select Insurance<\/option>';\n                result.data.data.forEach(ins => {\n                    sel.innerHTML += `<option value=\"${ins.id}\">${ins.name}<\/option>`;\n                });\n            }\n        } catch (err) { console.error('Insurance Error:', err); }\n    }\n\n    \/\/ \u2500\u2500\u2500 PROJECT TYPES \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   async function fetchProjectTypes() {\ntry {\n    let allProjectTypes = [];\n    let page = 1;\n    let lastPage = 1;\n\n    do {\n        const res = await fetch(\n            `${ENDPOINTS.PROJECT_TYPES}?page=${page}&per_page=100`,\n            { headers: getHeaders() }\n        );\n        if (handleAuthError(res)) return;\n        const result = await res.json();\n\n        if (result.code === 200) {\n            const data = result.data;\n            allProjectTypes = allProjectTypes.concat(data.data);\n            lastPage = data.last_page;\n            page++;\n        } else {\n            break;\n        }\n    } while (page <= lastPage);\n\n    \/\/ Filter active only\n    const activeTypes = allProjectTypes.filter(pt => pt.is_active === true);\n\n    const sel = document.getElementById('projectType');\n    sel.innerHTML = '<option value=\"\">Select Project Type<\/option>';\n    activeTypes.forEach(pt => {\n        sel.innerHTML += `<option value=\"${pt.id}\">${pt.type}<\/option>`;\n    });\n\n} catch (err) {\n    console.error('Project Types Error:', err);\n}\n}\n    \n    \/\/ \u2500\u2500\u2500 PROJECT TYPE CHANGE \u2192 Accounts + Users \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    document.getElementById('projectType').onchange = async function () {\n        const ptId = this.value;\n        resetSelect('assignedTo', 'Select Assigned To');\n        resetSelect('account',    'Select Account');\n        resetSelect('client',     'Select Client');\n        resetSelect('category',   'Select Category');\n        resetSelect('project',    'Select Project');\n        resetSelect('entity',     'Select entity');\n        selectedAccountDetail = null;\n        if (!ptId) return;\n\n        try {\n            const res    = await fetch(withScope(`${ENDPOINTS.PROJECT_TYPE_DATA}?project_type=${ptId}`), { headers: getHeaders() });\n            if (handleAuthError(res)) return;\n            const result = await res.json();\n            if (result.code === 200) {\n                \/\/ Account dropdown  \u2192  { id: \"name\" }\n                const accountSel = document.getElementById('account');\n                accountSel.innerHTML = '<option value=\"\">Select Account<\/option>';\n                Object.entries(result.data.account).forEach(([id, name]) => {\n                    accountSel.innerHTML += `<option value=\"${id}\">${name}<\/option>`;\n                });\n\n                \/\/ Assigned To dropdown  \u2192  { \"name\": id }\n                const assignedSel = document.getElementById('assignedTo');\n                assignedSel.innerHTML = '<option value=\"\">Select Assigned To<\/option>';\n                Object.entries(result.data.user).forEach(([name, id]) => {\n                    assignedSel.innerHTML += `<option value=\"${id}\">${name}<\/option>`;\n                });\n\n                \/\/ RBAC: ASSOCIATE_ROLES \u2192 personal view only; lock to own user\n                if (IS_ASSOCIATE && CURRENT_USER_ID) {\n                    assignedSel.value = CURRENT_USER_ID;\n                    assignedSel.disabled = true;\n                } else {\n                    assignedSel.disabled = false;\n                }\n            }\n        } catch (err) { console.error('Project Type Data Error:', err); }\n    };\n\n    \/\/ \u2500\u2500\u2500 ACCOUNT CHANGE \u2192 Client, Category, Project, Entity \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    document.getElementById('account').onchange = async function () {\n        const accountId = this.value;\n        resetSelect('client',   'Select Client');\n        resetSelect('category', 'Select Category');\n        resetSelect('project',  'Select Project');\n        resetSelect('entity',   'Select entity');\n        selectedAccountDetail = null;\n        if (!accountId) return;\n\n        try {\n            const res    = await fetch(ENDPOINTS.ACCOUNT_DETAIL, {\n                method:  'POST',\n                headers: getHeaders(),\n                body:    JSON.stringify({ id: parseInt(accountId, 10) })\n            });\n            if (handleAuthError(res)) return;\n            const result = await res.json();\n            if (result.code === 200) {\n                const d = result.data;\n                selectedAccountDetail = d;\n\n                \/\/ Client (auto-set single value)\n                document.getElementById('client').innerHTML =\n                    `<option value=\"${d.client.id}\" selected>${d.client.name}<\/option>`;\n\n                \/\/ Category (auto-set single value)\n                document.getElementById('category').innerHTML =\n                    `<option value=\"${d.category.id}\" selected>${d.category.category}<\/option>`;\n\n                \/\/ Project (auto-set single value)\n                document.getElementById('project').innerHTML =\n                    `<option value=\"${d.project.id}\" selected>${d.project.name}<\/option>`;\n\n                \/\/ PHP parity: account selection also sets project type dropdown.\n                if (d.projectType && d.projectType.id) {\n                    document.getElementById('projectType').value = String(d.projectType.id);\n                }\n\n                \/\/ Entity \u2192 list from client.entities\n                const entitySel = document.getElementById('entity');\n                entitySel.innerHTML = '<option value=\"\">Select entity<\/option>';\n                d.client.entities.forEach(ent => {\n                    entitySel.innerHTML += `<option value=\"${ent.id}\">${ent.entity}<\/option>`;\n                });\n\n                \/\/ Recalculate hours if benchmark exists\n                calculateHours();\n            }\n        } catch (err) { console.error('Account Detail Error:', err); }\n    };\n\n    \/\/ \u2500\u2500\u2500 HOURS CALCULATION \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\u2500\u2500\u2500\u2500\u2500\u2500\n    function calculateHours() {\n        const assignedDos = parseFloat(document.getElementById('assignedDos').value) || 0;\n        const benchmark = selectedAccountDetail ? selectedAccountDetail.operational_benchmark : 0;\n        \n        if (benchmark && benchmark > 0) {\n            const hours = assignedDos \/ benchmark;\n            document.getElementById('hours').value = hours % 1 === 0 ? hours : hours.toFixed(2);\n        } else {\n            document.getElementById('hours').value = 0;\n        }\n    }\n\n    document.getElementById('assignedDos').oninput = calculateHours;\n\n    \/\/ \u2500\u2500\u2500 HELPER \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    function resetSelect(id, placeholder) {\n        document.getElementById(id).innerHTML = `<option value=\"\">${placeholder}<\/option>`;\n    }\n\n    const selectSearchBuffers = new WeakMap();\n\n    function getSelectableOptions(select) {\n        return Array.from(select.options).filter(opt => !opt.disabled);\n    }\n\n    function moveSelectOption(select, direction) {\n        const opts = getSelectableOptions(select).filter(o => o.value !== '');\n        if (!opts.length) return;\n        let idx = opts.findIndex(o => o.value === select.value);\n        if (idx < 0) idx = 0;\n        idx = direction > 0\n            ? (idx + 1) % opts.length\n            : (idx - 1 + opts.length) % opts.length;\n        select.value = opts[idx].value;\n        select.dispatchEvent(new Event('change', { bubbles: true }));\n    }\n\n    function handleSelectTypeAhead(select, char) {\n        const buffer = (selectSearchBuffers.get(select) || '') + char.toLowerCase();\n        selectSearchBuffers.set(select, buffer);\n        clearTimeout(select._typeAheadTimer);\n        select._typeAheadTimer = setTimeout(() => selectSearchBuffers.delete(select), 700);\n\n        const opts = getSelectableOptions(select).filter(o => o.value !== '');\n        const startIdx = Math.max(opts.findIndex(o => o.value === select.value), 0);\n        const findMatch = (fromIdx) => {\n            for (let i = 0; i < opts.length; i++) {\n                const idx = (fromIdx + i) % opts.length;\n                if (opts[idx].text.toLowerCase().includes(buffer)) return opts[idx];\n            }\n            return null;\n        };\n        const match = findMatch(startIdx + 1) || findMatch(0);\n        if (match) {\n            select.value = match.value;\n            select.dispatchEvent(new Event('change', { bubbles: true }));\n        }\n    }\n\n    function initSelectKeyboardNavigation() {\n        document.querySelectorAll('select.form-control').forEach(select => {\n            if (select.dataset.kbNav === '1') return;\n            select.dataset.kbNav = '1';\n\n            select.addEventListener('keydown', (e) => {\n                if (select.disabled) return;\n\n                if (e.key === 'ArrowDown') {\n                    e.preventDefault();\n                    moveSelectOption(select, 1);\n                    return;\n                }\n                if (e.key === 'ArrowUp') {\n                    e.preventDefault();\n                    moveSelectOption(select, -1);\n                    return;\n                }\n                if (e.key === 'Escape') {\n                    select.blur();\n                    selectSearchBuffers.delete(select);\n                    return;\n                }\n                if (e.key === 'Enter' || e.key === 'Tab') {\n                    selectSearchBuffers.delete(select);\n                    return;\n                }\n                if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {\n                    e.preventDefault();\n                    handleSelectTypeAhead(select, e.key);\n                }\n            });\n        });\n    }\n\n    \/\/ \u2500\u2500\u2500 TABLE \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    let cachedTableData = [];\n\n    function renderTableHead() {\n        const head = document.getElementById('tableHead');\n        head.innerHTML = `\n            <tr>\n                ${TABLE_COLUMNS.map(column => {\n                    if (column.key === 'select') {\n                        return `<th style=\"width: ${column.width};\"><input type=\"checkbox\" id=\"selectAll\" onclick=\"document.querySelectorAll('.row-check').forEach(c => c.checked = this.checked)\"><\/th>`;\n                    }\n                    return `<th>${column.label}<\/th>`;\n                }).join('')}\n            <\/tr>`;\n    }\n\n    function renderTableState(message) {\n        document.getElementById('tableBody').innerHTML = `\n            <tr>\n                <td colspan=\"${TABLE_COLUMNS.length}\" class=\"no-data\">${message}<\/td>\n            <\/tr>`;\n    }\n\n    function extractTableRows(payload) {\n        if (Array.isArray(payload)) return payload;\n        if (Array.isArray(payload?.data)) return payload.data;\n        if (Array.isArray(payload?.data?.data)) return payload.data.data;\n        if (Array.isArray(payload?.rows)) return payload.rows;\n        if (Array.isArray(payload?.data?.rows)) return payload.data.rows;\n        return [];\n    }\n\n    function safeText(value) {\n        if (value === null || value === undefined || value === '') return 'N\/A';\n        return String(value)\n            .replace(\/&\/g, '&amp;')\n            .replace(\/<\/g, '&lt;')\n            .replace(\/>\/g, '&gt;')\n            .replace(\/\"\/g, '&quot;')\n            .replace(\/'\/g, '&#39;');\n    }\n\n    async function fetchTableData() {\n        try {\n            renderTableHead();\n            renderTableState('Loading work assignments...');\n            \/\/ RBAC: scope param tells backend whether to return personal\/team\/all data\n            let tempAllUrl = withScope(ENDPOINTS.GET_TEMP_ALL);\n            \/\/ Additional legacy filter for associates (backend also enforces this)\n            if (IS_ASSOCIATE && CURRENT_USER_ID) {\n                tempAllUrl += (tempAllUrl.includes('?') ? '&' : '?') + `assigned_to=${CURRENT_USER_ID}`;\n            }\n            const res    = await fetch(tempAllUrl, { headers: getHeaders() });\n            if (handleAuthError(res)) return;\n            const result = await res.json();\n            if (res.ok && (result.status === 'success' || result.code === 200)) {\n                const rows = extractTableRows(result);\n                cachedTableData = rows;\n                renderTable(cachedTableData);\n            } else {\n                console.error('fetchTableData failed:', result);\n                cachedTableData = [];\n                renderTableState('Failed to load work assignments');\n            }\n        } catch (err) {\n            console.error('API Error:', err);\n            cachedTableData = [];\n            renderTableState('Failed to load work assignments');\n        }\n    }\n\n    function renderTable(data) {\n        const tbody = document.getElementById('tableBody');\n        if (!data.length) {\n            renderTableState('No Data Found');\n            return;\n        }\n        tbody.innerHTML = data.map(item => `\n            <tr>\n                <td><input type=\"checkbox\" class=\"row-check\" data-id=\"${safeText(item.id)}\"><\/td>\n                <td>${safeText(item.assigned_date)}<\/td>\n                <td>${safeText(item.assigned_to_name)}<\/td>\n                <td>${safeText(item.pesudo_name || item.pseudo_name)}<\/td>\n                <td>${safeText(item.file_name)}<\/td>\n                <td>${safeText(item.received_on)}<\/td>\n                <td>${safeText(item.insurance_name)}<\/td>\n                <td>${safeText(item.account_name)}<\/td>\n                <td>${safeText(item.client_name)}<\/td>\n                <td>${safeText(item.category_name)}<\/td>\n            <\/tr>`).join('');\n    }\n\n    function filterTable(query) {\n        const q = query.toLowerCase();\n        const filtered = cachedTableData.filter(item =>\n            (item.assigned_to_name || '').toLowerCase().includes(q) ||\n            (item.file_name        || '').toLowerCase().includes(q) ||\n            (item.account_name     || '').toLowerCase().includes(q) ||\n            (item.client_name      || '').toLowerCase().includes(q) ||\n            (item.category_name    || '').toLowerCase().includes(q) ||\n            (item.insurance_name   || '').toLowerCase().includes(q)\n        );\n        renderTable(filtered);\n    }\n\n    \/\/ \u2500\u2500\u2500 ADD TO TEMP \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    async function addToTemp() {\n        \/\/ Function-level RBAC guard \u2014 backend must also enforce this.\n        if (!CAN_ADD) {\n            alert('Access denied: you do not have permission to add work assignments.');\n            return;\n        }\n        const ptId      = parseInt(document.getElementById('projectType').value, 10);\n        const assignedTo= parseInt(document.getElementById('assignedTo').value, 10);\n        const accountId = parseInt(document.getElementById('account').value, 10);\n        const entityId  = parseInt(document.getElementById('entity').value, 10);\n        const fileId = parseInt(document.getElementById('fileId').value, 10);\n        if (!ptId || !assignedTo || !accountId || !entityId) {\n            alert('Please fill all required fields first.');\n            return;\n        }\n        if (!fileId) {\n            alert('Please select a file from the search dropdown.');\n            return;\n        }\n\n        const payload = {\n            project_type_id: ptId,\n            assigned_date:   document.getElementById('assignedDate').value,\n            assigned_to:     assignedTo,\n            received_on:     document.getElementById('receivedOn').value,\n            account_id:      accountId,\n            insurance_id:    parseInt(document.getElementById('insurance').value, 10) || 0,\n            pages:           parseInt(document.getElementById('pages').value, 10),\n            file_id:         fileId,\n            client_id:       selectedAccountDetail ? selectedAccountDetail.client.id   : null,\n            category_id:     selectedAccountDetail ? selectedAccountDetail.category.id : null,\n            project_id:      selectedAccountDetail ? selectedAccountDetail.project.id  : null,\n            entity_id:       entityId,\n            dos_count:       parseInt(document.getElementById('dosCount').value, 10),\n            amount:          parseFloat(document.getElementById('amount').value),\n            assigned_dos:    parseInt(document.getElementById('assignedDos').value, 10),\n            hours:           document.getElementById('hours').value,\n            file_link:       document.getElementById('fileLink').value || 'N\/A',\n            is_active:       1\n        };\n\n        try {\n            const res    = await fetch(ENDPOINTS.ADD_TEMP, {\n                method:  'POST',\n                headers: getHeaders(),\n                body:    JSON.stringify(payload)\n            });\n            if (handleAuthError(res)) return;\n            const result = await res.json();\n            if (result.code === 200) {\n                \/\/ Reset form fields after successful add\n                document.getElementById('assignmentForm').reset();\n                document.getElementById('fileId').value = '';\n                document.getElementById('fileName').value = '';\n                const today = new Date().toISOString().split('T')[0];\n                document.getElementById('assignedDate').value = today;\n                document.getElementById('receivedOn').value  = today;\n                resetSelect('assignedTo', 'Select Assigned To');\n                resetSelect('account',    'Select Account');\n                resetSelect('client',     'Select Client');\n                resetSelect('category',   'Select Category');\n                resetSelect('project',    'Select Project');\n                resetSelect('entity',     'Select entity');\n                selectedAccountDetail = null;\n                fetchTableData();\n            } else {\n                alert(result.messages?.[0] || 'Add failed');\n            }\n        } catch (err) { console.error('Add Temp Error:', err); }\n    }\n\n    \/\/ \u2500\u2500\u2500 SUBMIT selected rows \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\u2500\u2500\u2500\u2500\n    document.getElementById('submitBtn').onclick = async () => {\n        \/\/ Function-level RBAC guard \u2014 submitting is a write action.\n        if (!CAN_ADD) {\n            alert('Access denied: you do not have permission to submit work assignments.');\n            return;\n        }\n        const checked = [...document.querySelectorAll('.row-check:checked')];\n        if (!checked.length) {\n            alert('Please select at least one row to submit.');\n            return;\n        }\n        const ids = checked.map(cb => parseInt(cb.dataset.id, 10));\n        let successCount = 0;\n        for (const id of ids) {\n            try {\n                const res    = await fetch(ENDPOINTS.TEMP_SUBMIT, {\n                    method:  'POST',\n                    headers: getHeaders(),\n                    body:    JSON.stringify({ id })\n                });\n                if (handleAuthError(res)) return;\n                const result = await res.json();\n                if (result.code === 200) successCount++;\n            } catch (err) { console.error(`Submit Error for id ${id}:`, err); }\n        }\n        alert(`${successCount} of ${ids.length} assignment(s) submitted successfully!`);\n        fetchTableData();\n    };\n\n    \/\/ \u2500\u2500\u2500 FILE SEARCH + AUTO-POPULATE (PHP custom.js parity) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n    let fileSearchTimer = null;\n    let fileSearchMatches = [];\n    let fileHighlightIndex = -1;\n\n    function updateFileDropdownHighlight() {\n        const dropdown = document.getElementById('fileDropdown');\n        const items = dropdown.querySelectorAll('.file-dropdown-item');\n        items.forEach((item, i) => {\n            item.classList.toggle('highlighted', i === fileHighlightIndex);\n            if (i === fileHighlightIndex) item.scrollIntoView({ block: 'nearest' });\n        });\n    }\n\n    function renderFileDropdown(matches) {\n        const dropdown = document.getElementById('fileDropdown');\n        fileSearchMatches = matches;\n        fileHighlightIndex = matches.length ? 0 : -1;\n        dropdown.innerHTML = '';\n\n        if (!matches.length) {\n            dropdown.style.display = 'none';\n            return;\n        }\n\n        matches.forEach((f, index) => {\n            const item = document.createElement('div');\n            item.className = 'file-dropdown-item' + (index === 0 ? ' highlighted' : '');\n            item.textContent = f.name;\n            item.setAttribute('role', 'option');\n            item.onclick = () => selectFileOption(f.id, f.name);\n            item.onmouseenter = () => {\n                fileHighlightIndex = index;\n                updateFileDropdownHighlight();\n            };\n            dropdown.appendChild(item);\n        });\n        dropdown.style.display = 'block';\n    }\n\n    function handleFileNameKeyDown(e) {\n        const dropdown = document.getElementById('fileDropdown');\n        const isOpen = dropdown.style.display !== 'none' && fileSearchMatches.length > 0;\n\n        if (e.key === 'ArrowDown') {\n            if (!isOpen) return;\n            e.preventDefault();\n            fileHighlightIndex = (fileHighlightIndex + 1) % fileSearchMatches.length;\n            updateFileDropdownHighlight();\n            return;\n        }\n        if (e.key === 'ArrowUp') {\n            if (!isOpen) return;\n            e.preventDefault();\n            fileHighlightIndex = fileHighlightIndex <= 0\n                ? fileSearchMatches.length - 1\n                : fileHighlightIndex - 1;\n            updateFileDropdownHighlight();\n            return;\n        }\n        if (e.key === 'Enter') {\n            if (!isOpen) return;\n            e.preventDefault();\n            const selected = fileSearchMatches[fileHighlightIndex];\n            if (selected) selectFileOption(selected.id, selected.name);\n            return;\n        }\n        if (e.key === 'Escape') {\n            dropdown.style.display = 'none';\n            fileHighlightIndex = -1;\n            fileSearchMatches = [];\n        }\n    }\n\n    function formatDateInput(value) {\n        if (!value) return '';\n        return String(value).split('T')[0].split(' ')[0];\n    }\n\n    async function loadFileInfo(fileId) {\n        if (!fileId) return;\n        try {\n            const res = await fetch(\n                `${ENDPOINTS.FILE_INFO}?id=${encodeURIComponent(fileId)}`,\n                { headers: getHeaders() }\n            );\n            if (handleAuthError(res)) return;\n            const result = await res.json();\n            if (result.code !== 200 || !result.data) return;\n\n            const d = result.data;\n            const pendingDos = parseFloat(d.total_pending_dos ?? 0) || 0;\n\n            \/\/ PHP: alert when pending DOS is zero after file pick.\n            if (pendingDos === 0) {\n                alert('Pending Hours are 0 please change file Name');\n            }\n\n            document.getElementById('dosCount').value = d.dos_count ?? 0;\n            document.getElementById('receivedOn').value = formatDateInput(d.received_on) || document.getElementById('receivedOn').value;\n            document.getElementById('assignedDos').value = pendingDos;\n            document.getElementById('fileLink').value = d.file_link || 'N\/A';\n            document.getElementById('pages').value = d.pages ?? 0;\n\n            \/\/ PHP send(): hours = assigned_dos \/ operational_benchmark\n            calculateHours();\n        } catch (err) {\n            console.error('File Info Error:', err);\n        }\n    }\n\n    function selectFileOption(fileId, fileName) {\n        document.getElementById('fileName').value = fileName;\n        document.getElementById('fileId').value = fileId;\n        document.getElementById('fileDropdown').style.display = 'none';\n        fileHighlightIndex = -1;\n        fileSearchMatches = [];\n        loadFileInfo(fileId);\n    }\n\n    function searchFiles(query) {\n        const dropdown = document.getElementById('fileDropdown');\n        document.getElementById('fileId').value = '';\n        clearTimeout(fileSearchTimer);\n        fileHighlightIndex = -1;\n        fileSearchMatches = [];\n\n        if (!query || !String(query).trim()) {\n            dropdown.style.display = 'none';\n            return;\n        }\n\n        fileSearchTimer = setTimeout(async () => {\n            try {\n                const res = await fetch(\n                    `${ENDPOINTS.FILE_AUTOCOMPLETE}?q=${encodeURIComponent(query.trim())}`,\n                    { headers: getHeaders() }\n                );\n                if (handleAuthError(res)) return;\n                const items = await res.json();\n                const matches = (Array.isArray(items) ? items : []).map(item => ({\n                    id: item.value || item.id,\n                    name: item.label || item.file_name\n                })).filter(item => item.id && item.name);\n\n                renderFileDropdown(matches);\n            } catch (err) {\n                console.error('File Autocomplete Error:', err);\n                dropdown.style.display = 'none';\n            }\n        }, 250);\n    }\n\n    \/\/ Close dropdown when clicking outside\n    document.addEventListener('click', e => {\n        if (!e.target.closest('#fileName') && !e.target.closest('#fileDropdown')) {\n            document.getElementById('fileDropdown').style.display = 'none';\n            fileHighlightIndex = -1;\n            fileSearchMatches = [];\n        }\n    });\n\n    function downloadCSV() {\n        if (!cachedTableData || !cachedTableData.length) {\n            alert('No data available to export.');\n            return;\n        }\n\n        const columns = TABLE_COLUMNS.filter(c => c.key !== 'select');\n        const headers = columns.map(c => c.label);\n\n        const csvRows = [\n            headers.join(','),\n            ...cachedTableData.map(item => columns.map(col => {\n                let val = '';\n                if (col.key === 'pesudo_name') {\n                    val = item.pesudo_name || item.pseudo_name || '';\n                } else {\n                    val = item[col.key] !== null && item[col.key] !== undefined ? item[col.key] : '';\n                }\n                \/\/ Escape commas and quotes\n                val = String(val).replace(\/\"\/g, '\"\"');\n                return `\"${val}\"`;\n            }).join(','))\n        ];\n\n        const csvContent = csvRows.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        const date = new Date().toISOString().split('T')[0];\n        link.setAttribute('href', url);\n        link.setAttribute('download', `work_assignments_${date}.csv`);\n        document.body.appendChild(link);\n        link.click();\n        document.body.removeChild(link);\n        URL.revokeObjectURL(url);\n    }\n<\/script>\n\n<\/div><\/div>\n\n<\/div><\/div><\/div><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Work Assignment &#8211; Data Team Close + Add Project Type * Select Project Type Assigned date * Assigned to * Select Assigned To Received on * Account * Select Account Insurance * Select Insurance Pages * File Name * Client * Select Client Category * Select Category Project * Select Project Entity * Select entity&#8230;<\/p>\n","protected":false},"author":2,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_kad_post_transparent":"default","_kad_post_title":"default","_kad_post_layout":"default","_kad_post_sidebar_id":"","_kad_post_content_style":"default","_kad_post_vertical_padding":"default","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","footnotes":""},"class_list":["post-81","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/ph-portal-v2.zyneventures.com\/index.php\/wp-json\/wp\/v2\/pages\/81","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ph-portal-v2.zyneventures.com\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/ph-portal-v2.zyneventures.com\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/ph-portal-v2.zyneventures.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/ph-portal-v2.zyneventures.com\/index.php\/wp-json\/wp\/v2\/comments?post=81"}],"version-history":[{"count":7,"href":"https:\/\/ph-portal-v2.zyneventures.com\/index.php\/wp-json\/wp\/v2\/pages\/81\/revisions"}],"predecessor-version":[{"id":1208,"href":"https:\/\/ph-portal-v2.zyneventures.com\/index.php\/wp-json\/wp\/v2\/pages\/81\/revisions\/1208"}],"wp:attachment":[{"href":"https:\/\/ph-portal-v2.zyneventures.com\/index.php\/wp-json\/wp\/v2\/media?parent=81"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}