{"id":911,"date":"2025-09-25T00:24:41","date_gmt":"2025-09-25T08:24:41","guid":{"rendered":"https:\/\/helpdesk.costbook.ph\/?page_id=911"},"modified":"2025-10-28T00:20:13","modified_gmt":"2025-10-28T08:20:13","slug":"category","status":"publish","type":"page","link":"https:\/\/helpdesk.costbook.ph\/index.php\/category\/","title":{"rendered":"Category"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"911\" class=\"elementor elementor-911\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-9e44e3d e-con-full e-flex e-con e-parent\" data-id=\"9e44e3d\" data-element_type=\"container\">\n\t\t<div class=\"elementor-element elementor-element-c467f17 e-con-full e-flex e-con e-child\" data-id=\"c467f17\" data-element_type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t<div class=\"elementor-element elementor-element-be65a9e elementor-widget__width-inherit elementor-widget elementor-widget-template\" data-id=\"be65a9e\" data-element_type=\"widget\" data-widget_type=\"template.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-template\">\n\t\t\t\t\t<div data-elementor-type=\"page\" data-elementor-id=\"925\" class=\"elementor elementor-925\" data-elementor-post-type=\"elementor_library\">\n\t\t\t\t<div class=\"elementor-element elementor-element-7946887d e-con-full e-flex e-con e-parent\" data-id=\"7946887d\" data-element_type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t<div class=\"elementor-element elementor-element-64491c90 e-con-full e-flex e-con e-child\" data-id=\"64491c90\" data-element_type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-19812326 elementor-widget elementor-widget-image\" data-id=\"19812326\" data-element_type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img fetchpriority=\"high\" decoding=\"async\" width=\"300\" height=\"169\" src=\"https:\/\/helpdesk.costbook.ph\/wp-content\/uploads\/2025\/09\/gffggfgf-removebg-preview-300x169.png\" class=\"attachment-medium size-medium wp-image-297\" alt=\"\" srcset=\"https:\/\/helpdesk.costbook.ph\/wp-content\/uploads\/2025\/09\/gffggfgf-removebg-preview-300x169.png 300w, https:\/\/helpdesk.costbook.ph\/wp-content\/uploads\/2025\/09\/gffggfgf-removebg-preview.png 666w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-2644b23e e-con-full e-flex e-con e-child\" data-id=\"2644b23e\" data-element_type=\"container\" data-settings=\"{&quot;position&quot;:&quot;absolute&quot;}\">\n\t\t\t\t<div class=\"elementor-element elementor-element-2beff56e elementor-widget-tablet__width-initial elementor-widget__width-initial eael_simple_menu_hamburger_disable_selected_menu_no eael-simple-menu-hamburger-align-right eael-hamburger--tablet elementor-widget elementor-widget-eael-simple-menu\" data-id=\"2beff56e\" data-element_type=\"widget\" data-widget_type=\"eael-simple-menu.default\">\n\t\t\t\t\t<style>\n                        @media screen and (max-width: 1024px) {\n                            .eael-hamburger--tablet {\n                                .eael-simple-menu-horizontal,\n                                .eael-simple-menu-vertical {\n                                    display: none;\n                                }\n                            }\n                            .eael-hamburger--tablet {\n                                .eael-simple-menu-container .eael-simple-menu-toggle {\n                                    display: block;\n                                }\n                            }\n                        }\n                    <\/style>            <div data-hamburger-icon=\"&lt;svg aria-hidden=&quot;true&quot; class=&quot;e-font-icon-svg e-fas-bars&quot; viewBox=&quot;0 0 448 512&quot; xmlns=&quot;http:\/\/www.w3.org\/2000\/svg&quot;&gt;&lt;path d=&quot;M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z&quot;&gt;&lt;\/path&gt;&lt;\/svg&gt;\" data-indicator-icon=\"&lt;svg aria-hidden=&quot;true&quot; class=&quot;e-font-icon-svg e-fas-angle-down&quot; viewBox=&quot;0 0 320 512&quot; xmlns=&quot;http:\/\/www.w3.org\/2000\/svg&quot;&gt;&lt;path d=&quot;M143 352.3L7 216.3c-9.4-9.4-9.4-24.6 0-33.9l22.6-22.6c9.4-9.4 24.6-9.4 33.9 0l96.4 96.4 96.4-96.4c9.4-9.4 24.6-9.4 33.9 0l22.6 22.6c9.4 9.4 9.4 24.6 0 33.9l-136 136c-9.2 9.4-24.4 9.4-33.8 0z&quot;&gt;&lt;\/path&gt;&lt;\/svg&gt;\" data-dropdown-indicator-icon=\"&lt;svg class=&quot;e-font-icon-svg e-fas-angle-down&quot; viewBox=&quot;0 0 320 512&quot; xmlns=&quot;http:\/\/www.w3.org\/2000\/svg&quot;&gt;&lt;path d=&quot;M143 352.3L7 216.3c-9.4-9.4-9.4-24.6 0-33.9l22.6-22.6c9.4-9.4 24.6-9.4 33.9 0l96.4 96.4 96.4-96.4c9.4-9.4 24.6-9.4 33.9 0l22.6 22.6c9.4 9.4 9.4 24.6 0 33.9l-136 136c-9.2 9.4-24.4 9.4-33.8 0z&quot;&gt;&lt;\/path&gt;&lt;\/svg&gt;\" class=\"eael-simple-menu-container eael-simple-menu-align-center eael-simple-menu-dropdown-align-left preset-1\" data-hamburger-breakpoints=\"{&quot;mobile&quot;:&quot;Mobile Portrait (&gt; 767px)&quot;,&quot;tablet&quot;:&quot;Tablet Portrait (&gt; 1024px)&quot;,&quot;desktop&quot;:&quot;Desktop (&gt; 2400px)&quot;,&quot;none&quot;:&quot;None&quot;}\" data-hamburger-device=\"tablet\">\n                <ul id=\"menu-main-menu\" class=\"eael-simple-menu eael-simple-menu-dropdown-animate-to-top eael-simple-menu-indicator eael-simple-menu-horizontal\"><li id=\"menu-item-875\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-875\"><a href=\"https:\/\/helpdesk.costbook.ph\/index.php\/dashboard-2-2\/\">Home<\/a><\/li>\n<li id=\"menu-item-878\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-878\"><a href=\"https:\/\/helpdesk.costbook.ph\/index.php\/elementor-131\/\">Ticket<\/a><\/li>\n<li id=\"menu-item-1004\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-1004\"><a href=\"https:\/\/helpdesk.costbook.ph\/index.php\/employees\/\">Employees<\/a><\/li>\n<li id=\"menu-item-1057\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-1057\"><a href=\"https:\/\/helpdesk.costbook.ph\/index.php\/assets\/\">Assets<\/a><\/li>\n<li id=\"menu-item-996\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-996\"><a href=\"https:\/\/helpdesk.costbook.ph\/index.php\/category\/\">Category<\/a><\/li>\n<li id=\"menu-item-876\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-876\"><a href=\"https:\/\/helpdesk.costbook.ph\/index.php\/report-analytics\/\">Reports<\/a><\/li>\n<li id=\"menu-item-877\" class=\"menu-item menu-item-type-post_type menu-item-object-page menu-item-877\"><a href=\"https:\/\/helpdesk.costbook.ph\/index.php\/dashboard-2\/\">Settings<\/a><\/li>\n<\/ul>                <button class=\"eael-simple-menu-toggle\">\n                    <span class=\"sr-only \">Hamburger Toggle Menu<\/span>\n                    <svg aria-hidden=\"true\" class=\"e-font-icon-svg e-fas-bars\" viewBox=\"0 0 448 512\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\"><path d=\"M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z\"><\/path><\/svg>                <\/button>\n            <\/div>\n            \t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-8fcb808 e-con-full e-flex e-con e-parent\" data-id=\"8fcb808\" data-element_type=\"container\">\n\t\t<div class=\"elementor-element elementor-element-68a0b8e e-con-full e-flex e-con e-child\" data-id=\"68a0b8e\" data-element_type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t<div class=\"elementor-element elementor-element-ac9e47a e-con-full e-flex e-con e-child\" data-id=\"ac9e47a\" data-element_type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-05a1131 elementor-widget elementor-widget-heading\" data-id=\"05a1131\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Management<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-0aa7359 e-n-tabs-mobile elementor-widget elementor-widget-n-tabs\" data-id=\"0aa7359\" data-element_type=\"widget\" data-widget_type=\"nested-tabs.default\">\n\t\t\t\t\t\t\t<div class=\"e-n-tabs\" data-widget-number=\"11170649\" aria-label=\"Tabs. Open items with Enter or Space, close with Escape and navigate using the Arrow keys.\">\n\t\t\t<div class=\"e-n-tabs-heading\" role=\"tablist\">\n\t\t\t\t\t<button id=\"e-n-tab-title-111706491\" class=\"e-n-tab-title\" aria-selected=\"true\" data-tab-index=\"1\" role=\"tab\" tabindex=\"0\" aria-controls=\"e-n-tab-content-111706491\" style=\"--n-tabs-title-order: 1;\">\n\t\t\t\t\t\t<span class=\"e-n-tab-title-text\">\n\t\t\t\tTab #1\t\t\t<\/span>\n\t\t<\/button>\n\t\t\t\t<button id=\"e-n-tab-title-111706492\" class=\"e-n-tab-title\" aria-selected=\"false\" data-tab-index=\"2\" role=\"tab\" tabindex=\"-1\" aria-controls=\"e-n-tab-content-111706492\" style=\"--n-tabs-title-order: 2;\">\n\t\t\t\t\t\t<span class=\"e-n-tab-title-text\">\n\t\t\t\tTab #2\t\t\t<\/span>\n\t\t<\/button>\n\t\t\t\t<button id=\"e-n-tab-title-111706493\" class=\"e-n-tab-title\" aria-selected=\"false\" data-tab-index=\"3\" role=\"tab\" tabindex=\"-1\" aria-controls=\"e-n-tab-content-111706493\" style=\"--n-tabs-title-order: 3;\">\n\t\t\t\t\t\t<span class=\"e-n-tab-title-text\">\n\t\t\t\tTab #3\t\t\t<\/span>\n\t\t<\/button>\n\t\t\t\t\t<\/div>\n\t\t\t<div class=\"e-n-tabs-content\">\n\t\t\t\t<div id=\"e-n-tab-content-111706491\" role=\"tabpanel\" aria-labelledby=\"e-n-tab-title-111706491\" data-tab-index=\"1\" style=\"--n-tabs-title-order: 1;\" class=\"e-active elementor-element elementor-element-05f40d2 e-con-full e-flex e-con e-child\" data-id=\"05f40d2\" data-element_type=\"container\" data-settings=\"{&quot;position&quot;:&quot;absolute&quot;}\">\n\t\t\t\t<div class=\"elementor-element elementor-element-3de3129 elementor-widget elementor-widget-html\" data-id=\"3de3129\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div class=\"licenses-widget\" data-instance-id=\"licenses1\">\r\n  <style>\r\n    .licenses-widget{\r\n      --bg:#f6f8fb; \r\n      --surface:#ffffff; \r\n      --text:#1e2430; \r\n      --muted:#6b7280; \r\n      --line:#e7eaf2;\r\n      --primary:#2d6bff; \r\n      --focus:rgba(45,107,255,.18);\r\n      --good:#16a34a; \r\n      --warn:#f59e0b; \r\n      --danger:#d92d20;\r\n      --radius-lg:16px;\r\n      --radius-md:12px;\r\n      --shadow-sm:0 1px 2px rgba(16,24,40,.06); \r\n      --shadow-md:0 6px 18px rgba(16,24,40,.08);\r\n      font:14px\/1.55 Inter, ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, Arial;\r\n      color:var(--text); \r\n      background:var(--bg); \r\n      padding:28px; \r\n      border-radius:16px; \r\n      box-shadow:var(--shadow-sm)\r\n    }\r\n    .licenses-widget *{ \r\n        box-sizing:border-box\r\n    }\r\n    .licenses-widget header{ \r\n        max-width:100%; \r\n        margin:0 auto 18px\r\n    }\r\n    .licenses-widget .h1{ \r\n        font-size:22px; \r\n        font-weight:700; \r\n        margin:0 0 6px\r\n    }\r\n    .licenses-widget .subtle{ \r\n        color:var(--muted)\r\n    }\r\n    .licenses-widget .container{ \r\n        max-width:1200px; \r\n        margin:0 auto; \r\n        display:grid; gap:22px\r\n    }\r\n    .card{ \r\n        background:var(--surface); \r\n        border:1px solid var(--line); \r\n        border-radius:var(--radius-lg); \r\n        box-shadow:var(--shadow-sm); \r\n        overflow:hidden\r\n    }\r\n    .card-h{ \r\n        padding:18px 20px 12px; \r\n        border-bottom:1px solid var(--line); \r\n        background:linear-gradient(180deg,#fff,#fafbff)\r\n    }\r\n    .card-t{ \r\n        font-weight:700\r\n    }\r\n    .card-d{ \r\n        color:var(--muted); \r\n        font-size:13px; \r\n        margin-top:2px\r\n    }\r\n    .card-c{ \r\n        padding:18px 20px\r\n    }\r\n\r\n    label{ \r\n        display:block; \r\n        font-weight:600; \r\n        font-size:12px; \r\n        letter-spacing:.28px; \r\n        text-transform:uppercase; \r\n        color:#7b8497;\r\n        margin:6px 0 8px\r\n    }\r\n    input, select, textarea{\r\n      width:100%; \r\n      background:#fff; \r\n      border:1px solid var(--line); \r\n      color:var(--text);\r\n      padding:12px; \r\n      border-radius:12px; \r\n      outline:none; \r\n      transition:border-color .18s, \r\n      box-shadow .18s;\r\n    }\r\n    input:focus, \r\n    select:focus, \r\n    textarea:focus{ border-color:var(--primary); \r\n    box-shadow:0 0 0 4px var(--focus) }\r\n    textarea{ \r\n        min-height:80px; \r\n        resize:vertical\r\n    }\r\n    .row{ \r\n        display:flex; \r\n        gap:10px; \r\n        align-items:center; \r\n        flex-wrap:wrap\r\n    }\r\n    .right{ \r\n        margin-left:auto\r\n    }\r\n\r\n    .btn{ \r\n        border:1px solid var(--line); \r\n        background:#fff; \r\n        color:var(--text); \r\n        padding:10px 14px; \r\n        border-radius:10px; \r\n        cursor:pointer; \r\n        font-weight:600; \r\n        transition:transform .12s, \r\n        background .18s, \r\n        border .18s }\r\n    .btn:hover{ \r\n        background:#f9fafe }\r\n    .btn:active{ \r\n        transform:translateY(1px) }\r\n    .btn.primary{ \r\n        background:var(--primary);\r\n        border-color:#275de0; \r\n        color:#fff }\r\n    .btn.danger{ \r\n        background:#fff5f5; \r\n        border-color:#ffd6d6; \r\n        color:#b42318 }\r\n    .btn.small{ \r\n        padding:7px 10px }\r\n    .btn[disabled]{ \r\n        opacity:.6; cursor:not-allowed }\r\n\r\n    .badge{ \r\n        display:inline-flex; \r\n        padding:5px 8px; \r\n        font-size:12px; \r\n        border-radius:999px; \r\n        border:1px solid var(--line); \r\n        background:#f7f8fc; \r\n        color:#4b5563\r\n    }\r\n    .badge.good{ \r\n        background:#ecfdf5; \r\n        border-color:#a7f3d0; \r\n        color:#065f46\r\n    }\r\n    .badge.warn{ \r\n        background:#fffbeb; \r\n        border-color:#fde68a; \r\n        color:#92400e\r\n    }\r\n    .badge.danger{ \r\n        background:#fef2f2; \r\n        border-color:#fecaca; \r\n        color:#7f1d1d\r\n    }\r\n\r\n    table{ \r\n        width:100%; \r\n        border-collapse:separate;\r\n        border-spacing:0 8px\r\n    }\r\n    th{ \r\n        font-size:12px; \r\n        color:#7b8497; \r\n        text-align:left; \r\n        padding:0 10px\r\n    }\r\n    td{ \r\n        background:#fff; \r\n        border:1px solid var(--line); \r\n        padding:10px; \r\n        border-radius:8px; \r\n        vertical-align:middle\r\n    }\r\n    tr td:first-child{ \r\n        border-top-right-radius:0; \r\n        border-bottom-right-radius:0\r\n    }\r\n    tr td:last-child{ \r\n        border-top-left-radius:0; \r\n        border-bottom-left-radius:0\r\n    }\r\n\r\n    \/* Modal *\/\r\n    .modal{ \r\n        position:fixed; inset:0; \r\n        display:none; \r\n        align-items:center; \r\n        justify-content:center; \r\n        background:rgba(2,8,23,.45); \r\n        z-index:9999; \r\n        padding:22px\r\n    }\r\n    .modal.open{ \r\n        display:flex\r\n    }\r\n    .dialog{ \r\n        width:min(620px, 96vw); \r\n        background:#fff; \r\n        border:1px solid var(--line); \r\n        border-radius:16px; \r\n        box-shadow:var(--shadow-md); \r\n        overflow:hidden\r\n    }\r\n    .dh{ \r\n        padding:16px 18px; \r\n        border-bottom:1px solid var(--line); \r\n        background:linear-gradient(180deg,#fff,#fafbff)\r\n    }\r\n    .dc{ \r\n        padding:16px 18px\r\n    }\r\n    .df{ \r\n        padding:14px 18px; \r\n        border-top:1px solid var(--line); \r\n        display:flex; gap:10px; \r\n        justify-content:flex-end\r\n    }\r\n\r\n    .toast{ \r\n        position:fixed; \r\n        right:20px; \r\n        bottom:20px; \r\n        background:#243b7b; \r\n        border:1px solid #314b95; \r\n        color:#e8efff; padding:12px 14px; \r\n        border-radius:12px; \r\n        box-shadow:var(--shadow-md); \r\n        opacity:0; \r\n        transform:translateY(8px); pointer-events:none; \r\n        transition:.25s\r\n    }\r\n    .toast.show{ \r\n        opacity:1; \r\n        transform:translateY(0)\r\n    }\r\n  <\/style>\r\n\r\n  <header>\r\n    <div class=\"h1\">Licenses<\/div>\r\n    <div class=\"subtle\">Track software entitlements, seats, and renewals. Demo data persists in localStorage.<\/div>\r\n  <\/header>\r\n\r\n  <main class=\"container\">\r\n    <section class=\"card\">\r\n      <div class=\"card-h\">\r\n        <div class=\"card-t\">Catalog<\/div>\r\n        <div class=\"card-d\">Add or update licenses, assign seats, and watch renewals.<\/div>\r\n      <\/div>\r\n      <div class=\"card-c\">\r\n        <div class=\"row\" style=\"margin-bottom:10px\">\r\n          <button class=\"btn js-addBtn\" type=\"button\">Add License<\/button>\r\n          <div class=\"right row\">\r\n            <input class=\"js-search\" type=\"text\" placeholder=\"Search product, vendor, key\u2026\" style=\"min-width:220px\"\/>\r\n            <select class=\"js-vendorFilter\">\r\n              <option value=\"\">All vendors<\/option>\r\n            <\/select>\r\n            <select class=\"js-statusFilter\">\r\n              <option value=\"\">All status<\/option>\r\n              <option>Active<\/option>\r\n              <option>Suspended<\/option>\r\n            <\/select>\r\n            <select class=\"js-expFilter\" title=\"Expiry filter\">\r\n              <option value=\"\">Expiry: Any<\/option>\r\n              <option value=\"soon\">Expiring \u2264 30 days<\/option>\r\n              <option value=\"expired\">Expired<\/option>\r\n            <\/select>\r\n          <\/div>\r\n        <\/div>\r\n\r\n        <table aria-label=\"Licenses table\">\r\n          <thead>\r\n            <tr>\r\n              <th>Product<\/th>\r\n              <th>Vendor<\/th>\r\n              <th>Seats<\/th>\r\n              <th>Key<\/th>\r\n              <th>Expiry<\/th>\r\n              <th>Status<\/th>\r\n              <th><\/th>\r\n            <\/tr>\r\n          <\/thead>\r\n          <tbody class=\"js-tbody\"><\/tbody>\r\n        <\/table>\r\n      <\/div>\r\n    <\/section>\r\n  <\/main>\r\n\r\n  <!-- Modal -->\r\n  <div class=\"modal js-modal\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"licTitle\">\r\n    <div class=\"dialog\">\r\n      <div class=\"dh\"><div class=\"card-t js-title\">Add License<\/div><\/div>\r\n      <div class=\"dc\">\r\n        <div class=\"row\" style=\"gap:14px; flex-wrap:wrap\">\r\n          <div style=\"flex:1 1 280px\">\r\n            <label>Product<\/label>\r\n            <input class=\"js-mProduct\" type=\"text\" placeholder=\"e.g., Microsoft 365 Business\"\/>\r\n            <div class=\"error js-mProductErr\"><\/div>\r\n          <\/div>\r\n          <div style=\"flex:1 1 220px\">\r\n            <label>Vendor<\/label>\r\n            <input class=\"js-mVendor\" type=\"text\" placeholder=\"Microsoft\"\/>\r\n            <div class=\"error js-mVendorErr\"><\/div>\r\n          <\/div>\r\n        <\/div>\r\n        <div class=\"row\" style=\"gap:14px; flex-wrap:wrap\">\r\n          <div style=\"flex:1 1 200px\">\r\n            <label>Seats (Total)<\/label>\r\n            <input class=\"js-mSeats\" type=\"number\" min=\"0\" value=\"10\"\/>\r\n          <\/div>\r\n          <div style=\"flex:1 1 200px\">\r\n            <label>Seats Used<\/label>\r\n            <input class=\"js-mUsed\" type=\"number\" min=\"0\" value=\"0\"\/>\r\n          <\/div>\r\n          <div style=\"flex:1 1 220px\">\r\n            <label>Expiry Date<\/label>\r\n            <input class=\"js-mExpiry\" type=\"date\"\/>\r\n          <\/div>\r\n        <\/div>\r\n        <div class=\"row\" style=\"gap:14px; flex-wrap:wrap\">\r\n          <div style=\"flex:1 1 280px\">\r\n            <label>License Key<\/label>\r\n            <input class=\"js-mKey\" type=\"text\" placeholder=\"XXXX-XXXX-XXXX-XXXX\"\/>\r\n          <\/div>\r\n          <div style=\"flex:1 1 180px\">\r\n            <label>Status<\/label>\r\n            <select class=\"js-mStatus\"><option>Active<\/option><option>Suspended<\/option><\/select>\r\n          <\/div>\r\n        <\/div>\r\n        <div>\r\n          <label>Notes<\/label>\r\n          <textarea class=\"js-mNotes\" placeholder=\"PO#, cost, entitlement, portal link\u2026\"><\/textarea>\r\n        <\/div>\r\n      <\/div>\r\n      <div class=\"df\">\r\n        <button class=\"btn js-cancel\" type=\"button\">Cancel<\/button>\r\n        <button class=\"btn primary js-save\" type=\"button\">Save<\/button>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <div class=\"toast js-toast\" role=\"status\" aria-live=\"polite\"><\/div>\r\n\r\n  <script>\r\n    (function initLicenses(root){\r\n      if(!root) return;\r\n      const instance = root.getAttribute('data-instance-id') || 'licenses1';\r\n      const STORAGE_KEY = 'it.licenses.v1.' + instance;\r\n      const $  = (s,ctx=root)=>ctx.querySelector(s);\r\n      const $$ = (s,ctx=root)=>Array.from(ctx.querySelectorAll(s));\r\n      const toast = (msg)=>{ const t=$('.js-toast'); t.textContent=msg; t.classList.add('show'); setTimeout(()=>t.classList.remove('show'),1500); };\r\n      const id = ()=> Math.random().toString(36).slice(2,10);\r\n      const today = ()=> new Date().toISOString().slice(0,10);\r\n      function addDays(d){ const dt=new Date(); dt.setDate(dt.getDate()+d); return dt.toISOString().slice(0,10); }\r\n\r\n      const defaultState = {\r\n        items: [\r\n          { id:id(), product:\"Microsoft 365 Business\", vendor:\"Microsoft\", seats:50, used:45, key:\"M365-XXXX-ABCD\", expiry:addDays(60), status:\"Active\", notes:\"EA 2024; Auto-renew\" },\r\n          { id:id(), product:\"Adobe Creative Cloud\",   vendor:\"Adobe\",     seats:10, used:8,  key:\"ADBE-XXXX-CC22\", expiry:addDays(20), status:\"Active\", notes:\"Renew via portal\" },\r\n          { id:id(), product:\"JetBrains All Products\",  vendor:\"JetBrains\", seats:5,  used:5,  key:\"JBAP-XXXX-2024\", expiry:addDays(-5), status:\"Active\", notes:\"Overused \u2014 add seats\" }\r\n        ]\r\n      };\r\n\r\n      let state = load();\r\n      function load(){ try{ const s=localStorage.getItem(STORAGE_KEY); return s? JSON.parse(s) : structuredClone(defaultState); }catch(e){ return structuredClone(defaultState);} }\r\n      function save(){ try{ localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); }catch(e){ console.warn('storage blocked', e); } }\r\n\r\n      function maskKey(k){ if(!k) return \"\u2014\"; return k.replace(\/[A-Za-z0-9]\/g,'X').replace(\/(.{4})(?=.)\/g,'$1-'); }\r\n      function daysUntil(dateStr){ const d=new Date(dateStr+'T00:00:00'), n=new Date(today()+'T00:00:00'); return Math.round((d-n)\/86400000); }\r\n      function badgeExpiry(exp){\r\n        if(!exp) return '<span class=\"badge\">\u2014<\/span>';\r\n        const d = daysUntil(exp);\r\n        if(d < 0) return `<span class=\"badge danger\">Expired ${Math.abs(d)}d<\/span>`;\r\n        if(d <= 30) return `<span class=\"badge warn\">Soon ${d}d<\/span>`;\r\n        return `<span class=\"badge good\">${exp}<\/span>`;\r\n      }\r\n      function badgeStatus(s){ return s===\"Active\" ? '<span class=\"badge good\">Active<\/span>' : '<span class=\"badge danger\">Suspended<\/span>'; }\r\n\r\n      function uniqueVendors(){\r\n        const set = new Set(state.items.map(i=>i.vendor).filter(Boolean));\r\n        return Array.from(set).sort();\r\n      }\r\n      function renderFilters(){\r\n        const sel = $('.js-vendorFilter');\r\n        const current = sel.value || \"\";\r\n        sel.innerHTML = '<option value=\"\">All vendors<\/option>' + uniqueVendors().map(v=>`<option ${v===current?'selected':''}>${v}<\/option>`).join('');\r\n      }\r\n\r\n      function render(){\r\n        renderFilters();\r\n        const q = $('.js-search').value.trim().toLowerCase();\r\n        const vf = $('.js-vendorFilter').value;\r\n        const sf = $('.js-statusFilter').value;\r\n        const ef = $('.js-expFilter').value;\r\n\r\n        const tb = $('.js-tbody'); tb.innerHTML = '';\r\n        state.items\r\n          .filter(i => (!vf || i.vendor===vf))\r\n          .filter(i => (!sf || i.status===sf))\r\n          .filter(i => {\r\n            if(!ef) return true;\r\n            const d = daysUntil(i.expiry);\r\n            if(ef==='soon') return d >= 0 && d <= 30;\r\n            if(ef==='expired') return d < 0;\r\n            return true;\r\n          })\r\n          .filter(i => !q || [i.product,i.vendor,i.key].join(' ').toLowerCase().includes(q))\r\n          .forEach(i=>{\r\n            const over = i.used > i.seats;\r\n            const tr = document.createElement('tr');\r\n            tr.innerHTML = `\r\n              <td>${i.product}<\/td>\r\n              <td>${i.vendor}<\/td>\r\n              <td>\r\n                <div class=\"row\">\r\n                  <span ${over?'style=\"color:#b42318;font-weight:700\"':''}>${i.used}\/${i.seats}<\/span>\r\n                  ${over?'<span class=\"badge danger\">Overused<\/span>':''}\r\n                <\/div>\r\n              <\/td>\r\n              <td><span title=\"${i.key||''}\">${maskKey(i.key)}<\/span><\/td>\r\n              <td>${badgeExpiry(i.expiry)}<\/td>\r\n              <td>${badgeStatus(i.status)}<\/td>\r\n              <td style=\"text-align:right;white-space:nowrap\">\r\n                <button class=\"btn small\" data-edit=\"${i.id}\">Edit<\/button>\r\n                <button class=\"btn small\" data-assign=\"${i.id}\">Assign<\/button>\r\n                <button class=\"btn small\" data-reclaim=\"${i.id}\">Reclaim<\/button>\r\n                <button class=\"btn small ${i.status==='Suspended'?'':'danger'}\" data-toggle=\"${i.id}\">${i.status==='Suspended'?'Activate':'Suspend'}<\/button>\r\n                <button class=\"btn small ghost\" data-remove=\"${i.id}\">Remove<\/button>\r\n              <\/td>`;\r\n            tb.appendChild(tr);\r\n          });\r\n\r\n        if(!tb.children.length){\r\n          const tr=document.createElement('tr');\r\n          tr.innerHTML = `<td colspan=\"7\" style=\"text-align:center;background:#fafbff\">No licenses match your filters.<\/td>`;\r\n          tb.appendChild(tr);\r\n        }\r\n      }\r\n\r\n      \/* ---------- MODAL ---------- *\/\r\n      const modal = $('.js-modal');\r\n      let editingId = null;\r\n      function openModal(id=null){\r\n        editingId = id;\r\n        $('.js-title').textContent = id? 'Edit License' : 'Add License';\r\n        $('.js-mProductErr').textContent = $('.js-mVendorErr').textContent = '';\r\n        if(id){\r\n          const x = state.items.find(i=>i.id===id);\r\n          $('.js-mProduct').value = x.product || '';\r\n          $('.js-mVendor').value  = x.vendor  || '';\r\n          $('.js-mSeats').value   = x.seats ?? 0;\r\n          $('.js-mUsed').value    = x.used ?? 0;\r\n          $('.js-mExpiry').value  = x.expiry || '';\r\n          $('.js-mKey').value     = x.key || '';\r\n          $('.js-mStatus').value  = x.status || 'Active';\r\n          $('.js-mNotes').value   = x.notes || '';\r\n        }else{\r\n          $('.js-mProduct').value = '';\r\n          $('.js-mVendor').value  = '';\r\n          $('.js-mSeats').value   = 10;\r\n          $('.js-mUsed').value    = 0;\r\n          $('.js-mExpiry').value  = today();\r\n          $('.js-mKey').value     = '';\r\n          $('.js-mStatus').value  = 'Active';\r\n          $('.js-mNotes').value   = '';\r\n        }\r\n        modal.classList.add('open');\r\n      }\r\n      function closeModal(){ modal.classList.remove('open'); }\r\n\r\n      \/* ---------- EVENTS ---------- *\/\r\n      \/\/ Top controls\r\n      $('.js-addBtn').addEventListener('click', ()=> openModal());\r\n      $('.js-search').addEventListener('input', debounce(render, 120));\r\n      $('.js-vendorFilter').addEventListener('change', render);\r\n      $('.js-statusFilter').addEventListener('change', render);\r\n      $('.js-expFilter').addEventListener('change', render);\r\n\r\n      \/\/ Modal actions\r\n      $('.js-cancel').addEventListener('click', (e)=>{ e.preventDefault(); closeModal(); });\r\n      $('.js-save').addEventListener('click', (e)=>{\r\n        e.preventDefault();\r\n        const product = $('.js-mProduct').value.trim();\r\n        const vendor  = $('.js-mVendor').value.trim();\r\n        const seats   = Math.max(0, parseInt($('.js-mSeats').value||'0',10));\r\n        const used    = Math.max(0, parseInt($('.js-mUsed').value||'0',10));\r\n        const expiry  = $('.js-mExpiry').value || '';\r\n        const key     = $('.js-mKey').value.trim();\r\n        const status  = $('.js-mStatus').value;\r\n        const notes   = $('.js-mNotes').value.trim();\r\n\r\n        $('.js-mProductErr').textContent = product? '' : 'Product is required';\r\n        $('.js-mVendorErr').textContent  = vendor? '' : 'Vendor is required';\r\n        if(!product || !vendor) return;\r\n        if(used > seats){ toast('Seats Used cannot exceed Seats Total'); return; }\r\n\r\n        if(editingId){\r\n          const x = state.items.find(i=>i.id===editingId);\r\n          Object.assign(x, { product, vendor, seats, used, expiry, key, status, notes });\r\n          toast('License updated \u2713');\r\n        }else{\r\n          state.items.unshift({ id:id(), product, vendor, seats, used, expiry, key, status, notes });\r\n          toast('License added \u2713');\r\n        }\r\n        save(); closeModal(); render();\r\n      });\r\n\r\n      \/\/ Row actions (delegated so Elementor re-renders don't break anything)\r\n      root.addEventListener('click', (e)=>{\r\n        const t = e.target.closest('[data-edit],[data-assign],[data-reclaim],[data-toggle],[data-remove]');\r\n        if(!t) return;\r\n\r\n        const idv = t.getAttribute('data-edit')||t.getAttribute('data-assign')||t.getAttribute('data-reclaim')||t.getAttribute('data-toggle')||t.getAttribute('data-remove');\r\n        const x = state.items.find(i=>i.id===idv);\r\n        if(!x) return;\r\n\r\n        if(t.hasAttribute('data-edit')){ openModal(idv); return; }\r\n\r\n        if(t.hasAttribute('data-assign')){\r\n          if(x.status!=='Active'){ toast('Cannot assign seats on suspended license'); return; }\r\n          if(x.used >= x.seats){ if(!confirm('All seats are used. Over-assign by 1?')) return; }\r\n          x.used += 1; save(); render(); toast('Seat assigned'); return;\r\n        }\r\n\r\n        if(t.hasAttribute('data-reclaim')){\r\n          if(x.used <= 0){ toast('No seats to reclaim'); return; }\r\n          x.used -= 1; save(); render(); toast('Seat reclaimed'); return;\r\n        }\r\n\r\n        if(t.hasAttribute('data-toggle')){\r\n          x.status = (x.status==='Suspended') ? 'Active' : 'Suspended';\r\n          save(); render(); toast(`License ${x.status==='Active'?'activated':'suspended'}`); return;\r\n        }\r\n\r\n        if(t.hasAttribute('data-remove')){\r\n          if(!confirm(`Remove ${x.product} (${x.vendor})?`)) return;\r\n          state.items = state.items.filter(i=>i.id!==idv); save(); render(); toast('License removed'); return;\r\n        }\r\n      });\r\n\r\n      \/\/ Modal close on outside-click \/ Esc\r\n      $('.js-modal').addEventListener('click', (e)=>{ if(e.target.classList.contains('js-modal')) closeModal(); });\r\n      document.addEventListener('keydown', (e)=>{ if(e.key==='Escape') closeModal(); }, { passive:true });\r\n\r\n      \/\/ Helpers\r\n      function debounce(fn, ms){ let h; return (...a)=>{ clearTimeout(h); h=setTimeout(()=>fn(...a), ms); }; }\r\n\r\n      \/\/ Initial render\r\n      render();\r\n    })(document.currentScript.closest('.licenses-widget'));\r\n  <\/script>\r\n<\/div>\r\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div id=\"e-n-tab-content-111706492\" role=\"tabpanel\" aria-labelledby=\"e-n-tab-title-111706492\" data-tab-index=\"2\" style=\"--n-tabs-title-order: 2;\" class=\" elementor-element elementor-element-8dfc4b0 e-con-full e-flex e-con e-child\" data-id=\"8dfc4b0\" data-element_type=\"container\">\n\t\t\t\t<\/div>\n\t\t<div id=\"e-n-tab-content-111706493\" role=\"tabpanel\" aria-labelledby=\"e-n-tab-title-111706493\" data-tab-index=\"3\" style=\"--n-tabs-title-order: 3;\" class=\" elementor-element elementor-element-7771aff e-con-full e-flex e-con e-child\" data-id=\"7771aff\" data-element_type=\"container\">\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Management Tab #1 Tab #2 Tab #3 Licenses Track software entitlements, seats, and renewals. Demo data persists in localStorage. Catalog Add or update licenses, assign seats, and watch renewals. Add License All vendors All statusActiveSuspended Expiry: AnyExpiring \u2264 30 daysExpired Product Vendor Seats Key Expiry Status Add License Product Vendor Seats (Total) Seats Used Expiry [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"_acf_changed":false,"footnotes":""},"class_list":["post-911","page","type-page","status-publish","hentry"],"acf":[],"_links":{"self":[{"href":"https:\/\/helpdesk.costbook.ph\/index.php\/wp-json\/wp\/v2\/pages\/911","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/helpdesk.costbook.ph\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/helpdesk.costbook.ph\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/helpdesk.costbook.ph\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/helpdesk.costbook.ph\/index.php\/wp-json\/wp\/v2\/comments?post=911"}],"version-history":[{"count":22,"href":"https:\/\/helpdesk.costbook.ph\/index.php\/wp-json\/wp\/v2\/pages\/911\/revisions"}],"predecessor-version":[{"id":1427,"href":"https:\/\/helpdesk.costbook.ph\/index.php\/wp-json\/wp\/v2\/pages\/911\/revisions\/1427"}],"wp:attachment":[{"href":"https:\/\/helpdesk.costbook.ph\/index.php\/wp-json\/wp\/v2\/media?parent=911"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}