{"id":1416,"date":"2026-05-19T06:19:58","date_gmt":"2026-05-19T06:19:58","guid":{"rendered":"https:\/\/tourvill.com\/?page_id=1416"},"modified":"2026-05-19T11:22:11","modified_gmt":"2026-05-19T11:22:11","slug":"add-product","status":"publish","type":"page","link":"https:\/\/tourvill.com\/add-product\/","title":{"rendered":"Add product"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"1416\" class=\"elementor elementor-1416\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-a6c2ca1 e-flex e-con-boxed e-con e-parent\" data-id=\"a6c2ca1\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-b8afd7c elementor-widget elementor-widget-html\" data-id=\"b8afd7c\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n<meta charset=\"UTF-8\">\r\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n<title>Tourvill Admin<\/title>\r\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap\" rel=\"stylesheet\">\r\n<style>\r\n:root{--blue:#1a56db;--blue-d:#1342b0;--blue-l:#e8f0fe;--blue-m:#c7d9fc;--text:#0f172a;--muted:#64748b;--border:#e2e8f0;--bg:#f8faff;--white:#fff;--danger:#dc2626;--danger-l:#fef2f2;--success:#16a34a;--success-l:#f0fdf4;--shadow:0 1px 3px rgba(0,0,0,.08);--shadow-md:0 4px 16px rgba(26,86,219,.10)}\r\n*{margin:0;padding:0;box-sizing:border-box}\r\nbody{background:var(--bg);color:var(--text);font-family:'Plus Jakarta Sans',sans-serif;min-height:100vh}\r\n\r\n\/* LOGIN *\/\r\n.login-screen{position:fixed;inset:0;background:var(--bg);display:flex;align-items:center;justify-content:center;z-index:999}\r\n.login-screen.hidden{display:none}\r\n.login-box{background:var(--white);border:1px solid var(--border);border-radius:20px;padding:48px 44px;width:420px;box-shadow:var(--shadow-md);text-align:center}\r\n.login-logo{font-size:28px;font-weight:800;color:var(--blue);margin-bottom:6px}\r\n.login-logo span{color:var(--text)}\r\n.login-sub{color:var(--muted);font-size:14px;margin-bottom:32px}\r\n.login-field{text-align:left;margin-bottom:16px}\r\n.login-field label{display:block;font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.8px;margin-bottom:7px}\r\n.login-field input{width:100%;padding:11px 16px;border:1.5px solid var(--border);border-radius:10px;font-size:15px;font-family:inherit;outline:none;transition:border-color .2s;color:var(--text)}\r\n.login-field input:focus{border-color:var(--blue)}\r\n.login-btn{width:100%;padding:12px;background:var(--blue);color:#fff;border:none;border-radius:10px;font-size:15px;font-weight:700;font-family:inherit;cursor:pointer;margin-top:8px;transition:background .2s}\r\n.login-btn:hover{background:var(--blue-d)}\r\n.login-err{color:var(--danger);font-size:13px;margin-top:12px;display:none}\r\n\r\n\/* LAYOUT *\/\r\n.sidebar{position:fixed;left:0;top:0;bottom:0;width:230px;background:var(--white);border-right:1px solid var(--border);display:flex;flex-direction:column;z-index:100}\r\n.logo{padding:24px;font-size:20px;font-weight:800;color:var(--blue);border-bottom:1px solid var(--border)}\r\n.logo span{color:var(--text)}\r\n.logo small{display:block;font-size:11px;font-weight:500;color:var(--muted);margin-top:2px}\r\n.nav{padding:16px 12px;flex:1}\r\n.nav-item{padding:10px 14px;border-radius:10px;cursor:pointer;font-size:14px;font-weight:600;color:var(--muted);transition:all .15s;display:flex;align-items:center;gap:10px;margin-bottom:2px}\r\n.nav-item:hover{background:var(--blue-l);color:var(--blue)}\r\n.nav-item.active{background:var(--blue);color:#fff}\r\n.sidebar-footer{padding:16px;border-top:1px solid var(--border)}\r\n.logout-btn{width:100%;padding:9px;border-radius:8px;border:1.5px solid var(--border);background:transparent;color:var(--muted);font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;transition:all .15s}\r\n.logout-btn:hover{border-color:var(--danger);color:var(--danger);background:var(--danger-l)}\r\n.main{margin-left:230px;padding:32px 36px;min-height:100vh}\r\n\r\n\/* HEADER *\/\r\n.page-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:28px}\r\n.page-title{font-size:24px;font-weight:800;letter-spacing:-.3px}\r\n.page-sub{color:var(--muted);font-size:13px;margin-top:3px}\r\n\r\n\/* BUTTONS *\/\r\n.btn{padding:9px 18px;border-radius:9px;font-size:13px;font-weight:700;cursor:pointer;border:none;transition:all .15s;font-family:inherit;display:inline-flex;align-items:center;gap:6px}\r\n.btn-primary{background:var(--blue);color:#fff}\r\n.btn-primary:hover{background:var(--blue-d)}\r\n.btn-danger{background:transparent;color:var(--danger);border:1.5px solid var(--border)}\r\n.btn-danger:hover{background:var(--danger-l);border-color:var(--danger)}\r\n.btn-edit{background:transparent;color:var(--blue);border:1.5px solid var(--border)}\r\n.btn-edit:hover{background:var(--blue-l);border-color:var(--blue)}\r\n.btn-sm{padding:5px 12px;font-size:12px;border-radius:7px}\r\n.btn-cancel{background:transparent;color:var(--muted);border:1.5px solid var(--border)}\r\n.btn-cancel:hover{background:var(--bg)}\r\n\r\n\/* STATS *\/\r\n.stats{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-bottom:24px}\r\n.stat-card{background:var(--white);border:1px solid var(--border);border-radius:14px;padding:20px;box-shadow:var(--shadow);display:flex;align-items:center;gap:14px}\r\n.stat-icon{width:44px;height:44px;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0}\r\n.stat-icon.total{background:var(--blue-l)}\r\n.stat-icon.our{background:#ede9fe}\r\n.stat-icon.popular{background:#fffbeb}\r\n.stat-icon.hot{background:var(--danger-l)}\r\n.stat-label{font-size:12px;color:var(--muted);font-weight:600}\r\n.stat-value{font-size:26px;font-weight:800;margin-top:2px}\r\n\r\n\/* TABLE *\/\r\n.table-wrap{background:var(--white);border:1px solid var(--border);border-radius:16px;overflow:hidden;box-shadow:var(--shadow)}\r\n.table-toolbar{padding:16px 20px;display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid var(--border)}\r\n.table-toolbar-title{font-weight:700;font-size:15px}\r\n.filter-tabs{display:flex;gap:6px}\r\n.filter-tab{padding:5px 14px;border-radius:20px;font-size:12px;font-weight:700;cursor:pointer;border:1.5px solid var(--border);background:transparent;color:var(--muted);transition:all .15s;font-family:inherit}\r\n.filter-tab.active{background:var(--blue);color:#fff;border-color:var(--blue)}\r\n.filter-tab:hover:not(.active){background:var(--blue-l);color:var(--blue);border-color:var(--blue-m)}\r\ntable{width:100%;border-collapse:collapse}\r\nthead tr{background:var(--bg)}\r\nth{padding:11px 16px;text-align:left;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.8px;color:var(--muted)}\r\ntd{padding:12px 16px;font-size:13px;border-top:1px solid var(--border);vertical-align:middle}\r\ntr:hover td{background:#fafbff}\r\n.prop-img{width:52px;height:38px;border-radius:8px;overflow:hidden;background:var(--blue-l);display:flex;align-items:center;justify-content:center;font-size:16px;flex-shrink:0}\r\n.prop-img img{width:100%;height:100%;object-fit:cover}\r\n.prop-info{display:flex;align-items:center;gap:12px}\r\n.prop-name{font-weight:700;font-size:13px}\r\n.prop-loc{font-size:12px;color:var(--muted);margin-top:1px}\r\n.cat-badge{padding:3px 8px;border-radius:20px;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;white-space:nowrap;margin:2px}\r\n.cat-our{background:#ede9fe;color:#6d28d9}\r\n.cat-popular{background:#fffbeb;color:#d97706}\r\n.cat-hot{background:var(--danger-l);color:var(--danger)}\r\n.cats-wrap{display:flex;flex-wrap:wrap;gap:2px}\r\n.actions{display:flex;gap:6px}\r\n\r\n\/* MODAL *\/\r\n.modal-overlay{position:fixed;inset:0;background:rgba(15,23,42,.35);backdrop-filter:blur(3px);z-index:200;display:none;align-items:center;justify-content:center}\r\n.modal-overlay.open{display:flex}\r\n.modal{background:var(--white);border:1px solid var(--border);border-radius:18px;width:620px;max-height:90vh;overflow-y:auto;padding:32px;box-shadow:0 20px 60px rgba(26,86,219,.15);animation:slideUp .22s ease}\r\n@keyframes slideUp{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}\r\n.modal-title{font-size:18px;font-weight:800;margin-bottom:22px;letter-spacing:-.2px}\r\n.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}\r\n.form-full{grid-column:1\/-1}\r\n.field label{display:block;font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.8px;margin-bottom:7px}\r\n.field input,.field select,.field textarea{width:100%;background:var(--bg);border:1.5px solid var(--border);border-radius:9px;padding:10px 13px;color:var(--text);font-size:13px;font-family:inherit;transition:border-color .2s;outline:none;resize:vertical}\r\n.field input:focus,.field select:focus,.field textarea:focus{border-color:var(--blue);background:var(--white)}\r\n.field textarea{min-height:80px}\r\n\r\n\/* CATEGORY CHECKBOXES *\/\r\n.cat-checks{display:flex;gap:12px;flex-wrap:wrap}\r\n.cat-check{display:flex;align-items:center;gap:7px;padding:8px 14px;border:1.5px solid var(--border);border-radius:9px;cursor:pointer;transition:all .15s;font-size:13px;font-weight:600;user-select:none}\r\n.cat-check:hover{background:var(--blue-l);border-color:var(--blue-m)}\r\n.cat-check.checked{background:var(--blue);color:#fff;border-color:var(--blue)}\r\n.cat-check input{display:none}\r\n\r\n\/* KEYPOINTS *\/\r\n.keypoints-list{display:flex;flex-direction:column;gap:8px;margin-top:8px}\r\n.keypoint-row{display:flex;gap:8px;align-items:center}\r\n.keypoint-row input{flex:1}\r\n.btn-icon{width:32px;height:32px;border-radius:7px;border:1.5px solid var(--border);background:transparent;cursor:pointer;font-size:14px;display:flex;align-items:center;justify-content:center;transition:all .15s;flex-shrink:0}\r\n.btn-icon:hover{background:var(--danger-l);border-color:var(--danger);color:var(--danger)}\r\n.btn-add-kp{font-size:12px;font-weight:700;color:var(--blue);background:var(--blue-l);border:none;padding:7px 14px;border-radius:8px;cursor:pointer;font-family:inherit;margin-top:4px;transition:background .15s}\r\n.btn-add-kp:hover{background:var(--blue-m)}\r\n\r\n.img-preview{width:100%;height:110px;background:var(--bg);border-radius:9px;border:1.5px dashed var(--border);display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--muted);overflow:hidden;margin-top:8px}\r\n.img-preview img{width:100%;height:100%;object-fit:cover;border-radius:8px}\r\n.modal-actions{display:flex;justify-content:flex-end;gap:10px;margin-top:22px}\r\n.section-divider{border:none;border-top:1px solid var(--border);margin:18px 0}\r\n.section-label{font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.8px;margin-bottom:12px}\r\n\r\n\/* CONFIRM *\/\r\n.confirm-modal{width:360px;text-align:center}\r\n.confirm-icon{font-size:36px;margin-bottom:14px}\r\n.confirm-title{font-size:17px;font-weight:800;margin-bottom:6px}\r\n.confirm-sub{color:var(--muted);font-size:13px;margin-bottom:22px}\r\n\r\n\/* TOAST *\/\r\n.toast{position:fixed;bottom:28px;right:28px;border-radius:10px;padding:13px 18px;font-size:13px;font-weight:600;z-index:999;transform:translateY(70px);opacity:0;transition:all .3s cubic-bezier(.34,1.56,.64,1);box-shadow:var(--shadow-md)}\r\n.toast.show{transform:translateY(0);opacity:1}\r\n.toast.success{background:var(--success-l);color:var(--success);border:1.5px solid #bbf7d0}\r\n.toast.error{background:var(--danger-l);color:var(--danger);border:1.5px solid #fecaca}\r\n.toast.info{background:var(--blue-l);color:var(--blue);border:1.5px solid var(--blue-m)}\r\n\r\n\/* STATES *\/\r\n.empty{padding:52px;text-align:center;color:var(--muted)}\r\n.empty-icon{font-size:36px;margin-bottom:10px}\r\n.loading{padding:36px;text-align:center;color:var(--muted);font-size:13px}\r\n.spinner{width:22px;height:22px;border:2.5px solid var(--border);border-top-color:var(--blue);border-radius:50%;animation:spin .7s linear infinite;margin:0 auto 10px}\r\n@keyframes spin{to{transform:rotate(360deg)}}\r\n\r\n.wp-status{font-size:11px;color:var(--muted);margin-top:4px}\r\n.wp-status a{color:var(--blue);text-decoration:none}\r\n.wp-status a:hover{text-decoration:underline}\r\n<\/style>\r\n<\/head>\r\n<body>\r\n\r\n<!-- LOGIN -->\r\n<div class=\"login-screen\" id=\"login-screen\">\r\n  <div class=\"login-box\">\r\n    <div class=\"login-logo\">Tour<span>vill<\/span><\/div>\r\n    <div class=\"login-sub\">Admin Panel \u2014 Authorized Access Only<\/div>\r\n    <div class=\"login-field\">\r\n      <label>Password<\/label>\r\n      <input type=\"password\" id=\"login-pass\" placeholder=\"Enter admin password\" onkeydown=\"if(event.key==='Enter')doLogin()\">\r\n    <\/div>\r\n    <button class=\"login-btn\" onclick=\"doLogin()\">Login \u2192<\/button>\r\n    <div class=\"login-err\" id=\"login-err\">\u274c Wrong password. Try again.<\/div>\r\n  <\/div>\r\n<\/div>\r\n\r\n<!-- SIDEBAR -->\r\n<aside class=\"sidebar\">\r\n  <div class=\"logo\">Tour<span>vill<\/span><small>Property Manager<\/small><\/div>\r\n  <nav class=\"nav\">\r\n    <div class=\"nav-item active\" id=\"nav-all\" onclick=\"setNav(this);loadProperties()\">\ud83c\udfe0 All Properties<\/div>\r\n    <div class=\"nav-item\" id=\"nav-add\" onclick=\"setNav(this);openAddModal()\">\u2795 Add Property<\/div>\r\n  <\/nav>\r\n  <div class=\"sidebar-footer\">\r\n    <button class=\"logout-btn\" onclick=\"doLogout()\">\ud83d\udd12 Logout<\/button>\r\n  <\/div>\r\n<\/aside>\r\n\r\n<!-- MAIN -->\r\n<main class=\"main\">\r\n  <div class=\"page-header\">\r\n    <div>\r\n      <div class=\"page-title\">All Properties<\/div>\r\n      <div class=\"page-sub\">Manage your tourvill property listings<\/div>\r\n    <\/div>\r\n    <button class=\"btn btn-primary\" onclick=\"openAddModal()\">+ Add Property<\/button>\r\n  <\/div>\r\n\r\n  <div class=\"stats\">\r\n    <div class=\"stat-card\"><div class=\"stat-icon total\">\ud83c\udfe0<\/div><div><div class=\"stat-label\">Total<\/div><div class=\"stat-value\" id=\"stat-total\">\u2014<\/div><\/div><\/div>\r\n    <div class=\"stat-card\"><div class=\"stat-icon our\">\ud83c\udfe1<\/div><div><div class=\"stat-label\">Our Property<\/div><div class=\"stat-value\" id=\"stat-our\">\u2014<\/div><\/div><\/div>\r\n    <div class=\"stat-card\"><div class=\"stat-icon popular\">\u2b50<\/div><div><div class=\"stat-label\">Popular<\/div><div class=\"stat-value\" id=\"stat-popular\">\u2014<\/div><\/div><\/div>\r\n    <div class=\"stat-card\"><div class=\"stat-icon hot\">\ud83d\udd25<\/div><div><div class=\"stat-label\">Hot<\/div><div class=\"stat-value\" id=\"stat-hot\">\u2014<\/div><\/div><\/div>\r\n  <\/div>\r\n\r\n  <div class=\"table-wrap\">\r\n    <div class=\"table-toolbar\">\r\n      <div class=\"table-toolbar-title\">Property List<\/div>\r\n      <div class=\"filter-tabs\">\r\n        <button class=\"filter-tab active\" onclick=\"filterTable('all',this)\">All<\/button>\r\n        <button class=\"filter-tab\" onclick=\"filterTable('Our Property',this)\">Our Property<\/button>\r\n        <button class=\"filter-tab\" onclick=\"filterTable('Popular',this)\">Popular<\/button>\r\n        <button class=\"filter-tab\" onclick=\"filterTable('Hot',this)\">Hot<\/button>\r\n      <\/div>\r\n    <\/div>\r\n    <div id=\"table-container\"><div class=\"loading\"><div class=\"spinner\"><\/div>Loading...<\/div><\/div>\r\n  <\/div>\r\n<\/main>\r\n\r\n<!-- ADD\/EDIT MODAL -->\r\n<div class=\"modal-overlay\" id=\"modal-add\">\r\n  <div class=\"modal\">\r\n    <div class=\"modal-title\" id=\"modal-title\">Add New Property<\/div>\r\n\r\n    <div class=\"section-label\">Basic Info<\/div>\r\n    <div class=\"form-grid\">\r\n      <div class=\"field\"><label>Property Name *<\/label><input type=\"text\" id=\"f-name\" placeholder=\"e.g. Ocean View Villa\"><\/div>\r\n      <div class=\"field\">\r\n        <label>Location *<\/label>\r\n        <select id=\"f-location\">\r\n          <option value=\"\">Select location...<\/option>\r\n          <option>Cox's Bazar<\/option>\r\n          <option>Bandarban<\/option>\r\n          <option>Sundarban<\/option>\r\n          <option>Saint Martin<\/option>\r\n          <option>Sreemangal<\/option>\r\n          <option>Sajek<\/option>\r\n          <option>Tanguar Haor<\/option>\r\n        <\/select>\r\n      <\/div>\r\n      <div class=\"field form-full\">\r\n        <label>Image URL<\/label>\r\n        <input type=\"text\" id=\"f-image\" placeholder=\"https:\/\/...\" oninput=\"previewImage()\">\r\n        <div class=\"img-preview\" id=\"img-preview\"><span>Image preview will appear here<\/span><\/div>\r\n      <\/div>\r\n    <\/div>\r\n\r\n    <hr class=\"section-divider\">\r\n    <div class=\"section-label\">Categories (select one or more)<\/div>\r\n    <div class=\"cat-checks\">\r\n      <div class=\"cat-check\" id=\"cc-our\" onclick=\"toggleCat(this,'Our Property')\">\ud83c\udfe1 Our Property<\/div>\r\n      <div class=\"cat-check\" id=\"cc-popular\" onclick=\"toggleCat(this,'Popular')\">\u2b50 Popular<\/div>\r\n      <div class=\"cat-check\" id=\"cc-hot\" onclick=\"toggleCat(this,'Hot')\">\ud83d\udd25 Hot<\/div>\r\n    <\/div>\r\n\r\n    <hr class=\"section-divider\">\r\n    <div class=\"section-label\">View Info Page Content<\/div>\r\n    <div class=\"form-grid\">\r\n      <div class=\"field form-full\"><label>Page Title (H1)<\/label><input type=\"text\" id=\"f-h1\" placeholder=\"e.g. Discover Ocean View Villa in Cox's Bazar\"><\/div>\r\n      <div class=\"field form-full\"><label>Description<\/label><textarea id=\"f-desc\" placeholder=\"Describe the property...\"><\/textarea><\/div>\r\n      <div class=\"field form-full\">\r\n        <label>Key Highlights<\/label>\r\n        <div class=\"keypoints-list\" id=\"kp-list\">\r\n          <div class=\"keypoint-row\"><input type=\"text\" placeholder=\"e.g. Private beach access\"><button class=\"btn-icon\" onclick=\"removeKP(this)\">\u2715<\/button><\/div>\r\n        <\/div>\r\n        <button class=\"btn-add-kp\" onclick=\"addKP()\">+ Add Point<\/button>\r\n      <\/div>\r\n    <\/div>\r\n\r\n    <div class=\"modal-actions\">\r\n      <button class=\"btn btn-cancel\" onclick=\"closeModal('modal-add')\">Cancel<\/button>\r\n      <button class=\"btn btn-primary\" onclick=\"saveProperty()\" id=\"save-btn\">Save & Create Page<\/button>\r\n    <\/div>\r\n  <\/div>\r\n<\/div>\r\n\r\n<!-- DELETE MODAL -->\r\n<div class=\"modal-overlay\" id=\"modal-delete\">\r\n  <div class=\"modal confirm-modal\">\r\n    <div class=\"confirm-icon\">\ud83d\uddd1\ufe0f<\/div>\r\n    <div class=\"confirm-title\">Delete Property?<\/div>\r\n    <div class=\"confirm-sub\">WordPress page will also be removed.<\/div>\r\n    <div class=\"modal-actions\" style=\"justify-content:center\">\r\n      <button class=\"btn btn-cancel\" onclick=\"closeModal('modal-delete')\">Cancel<\/button>\r\n      <button class=\"btn btn-danger\" onclick=\"confirmDelete()\">Yes, Delete<\/button>\r\n    <\/div>\r\n  <\/div>\r\n<\/div>\r\n\r\n<div class=\"toast\" id=\"toast\"><\/div>\r\n\r\n<script>\r\nconst API = \"https:\/\/script.google.com\/macros\/s\/AKfycbzJB0prAF2to6k6xzTtMcKDAhG7YpyPR-Za6ovs3GdUvv5R9Xzi3DQzM_kyF2MEVof6\/exec\";\r\nconst ADMIN_PASS = \"tourvill2024\";\r\n\r\nlet allProperties = [];\r\nlet editingRow = null;\r\nlet deletingRow = null;\r\nlet selectedCats = [];\r\n\r\n\/\/ AUTH\r\nfunction doLogin() {\r\n  if (document.getElementById('login-pass').value === ADMIN_PASS) {\r\n    document.getElementById('login-screen').classList.add('hidden');\r\n    sessionStorage.setItem('tv_auth','1');\r\n    loadProperties();\r\n  } else {\r\n    document.getElementById('login-err').style.display = 'block';\r\n  }\r\n}\r\nfunction doLogout() {\r\n  sessionStorage.removeItem('tv_auth');\r\n  document.getElementById('login-screen').classList.remove('hidden');\r\n  document.getElementById('login-pass').value = '';\r\n  document.getElementById('login-err').style.display = 'none';\r\n}\r\nif (sessionStorage.getItem('tv_auth')==='1') {\r\n  document.getElementById('login-screen').classList.add('hidden');\r\n  loadProperties();\r\n}\r\n\r\n\/\/ JSONP\r\nfunction apiCall(params) {\r\n  return new Promise((resolve, reject) => {\r\n    const cb = 'tvcb_' + Date.now() + '_' + Math.floor(Math.random()*99999);\r\n    const url = API + '?' + new URLSearchParams(params).toString() + '&callback=' + cb;\r\n    const timer = setTimeout(() => { delete window[cb]; reject(new Error('Timeout')); }, 30000);\r\n    window[cb] = (data) => { clearTimeout(timer); resolve(data); delete window[cb]; const el=document.getElementById(cb); if(el)el.remove(); };\r\n    const s = document.createElement('script');\r\n    s.id = cb; s.src = url;\r\n    s.onerror = () => { clearTimeout(timer); delete window[cb]; reject(new Error('Script error')); };\r\n    document.head.appendChild(s);\r\n  });\r\n}\r\n\r\n\/\/ LOAD\r\nasync function loadProperties() {\r\n  document.getElementById('table-container').innerHTML = '<div class=\"loading\"><div class=\"spinner\"><\/div>Loading...<\/div>';\r\n  try {\r\n    const json = await apiCall({ action:'getAll' });\r\n    allProperties = json.data || [];\r\n    updateStats();\r\n    renderTable(allProperties);\r\n  } catch(e) {\r\n    document.getElementById('table-container').innerHTML = '<div class=\"empty\"><div class=\"empty-icon\">\u26a0\ufe0f<\/div><div>Failed to load.<\/div><\/div>';\r\n  }\r\n}\r\n\r\nfunction updateStats() {\r\n  document.getElementById('stat-total').textContent = allProperties.length;\r\n  document.getElementById('stat-our').textContent = allProperties.filter(p=>p.Category&&p.Category.includes('Our Property')).length;\r\n  document.getElementById('stat-popular').textContent = allProperties.filter(p=>p.Category&&p.Category.includes('Popular')).length;\r\n  document.getElementById('stat-hot').textContent = allProperties.filter(p=>p.Category&&p.Category.includes('Hot')).length;\r\n}\r\n\r\nfunction renderTable(data) {\r\n  const c = document.getElementById('table-container');\r\n  if (!data.length) { c.innerHTML='<div class=\"empty\"><div class=\"empty-icon\">\ud83c\udfdd\ufe0f<\/div><div>No properties. Add one!<\/div><\/div>'; return; }\r\n  c.innerHTML = `<table><thead><tr>\r\n    <th>Property<\/th><th>Categories<\/th><th>View Info Page<\/th><th>Actions<\/th>\r\n  <\/tr><\/thead><tbody>${data.map(p=>{\r\n    const cats = (p.Category||'').split(',').filter(Boolean);\r\n    const catBadges = cats.map(c=>`<span class=\"cat-badge cat-${catClass(c.trim())}\">${c.trim()}<\/span>`).join('');\r\n    const wpLink = p.Info_Link ? `<a href=\"${p.Info_Link}\" target=\"_blank\" style=\"color:var(--blue);font-size:12px;font-weight:600\">\ud83d\udd17 View Page<\/a>` : '<span style=\"color:var(--muted);font-size:12px\">Not created yet<\/span>';\r\n    return `<tr>\r\n      <td><div class=\"prop-info\">\r\n        <div class=\"prop-img\">${p.Image_URL?`<img decoding=\"async\" src=\"${p.Image_URL}\" onerror=\"this.parentElement.textContent='\ud83c\udfe0'\">`:'\ud83c\udfe0'}<\/div>\r\n        <div><div class=\"prop-name\">${p.Name||'\u2014'}<\/div><div class=\"prop-loc\">\ud83d\udccd ${p.Location||'\u2014'}<\/div><\/div>\r\n      <\/div><\/td>\r\n      <td><div class=\"cats-wrap\">${catBadges||'\u2014'}<\/div><\/td>\r\n      <td>${wpLink}<\/td>\r\n      <td><div class=\"actions\">\r\n        <button class=\"btn btn-edit btn-sm\" onclick=\"openEditModal(${p.rowIndex})\">Edit<\/button>\r\n        <button class=\"btn btn-danger btn-sm\" onclick=\"openDeleteModal(${p.rowIndex})\">Delete<\/button>\r\n      <\/div><\/td>\r\n    <\/tr>`;\r\n  }).join('')}<\/tbody><\/table>`;\r\n}\r\n\r\nfunction catClass(c) {\r\n  if(c==='Our Property') return 'our';\r\n  if(c==='Popular') return 'popular';\r\n  if(c==='Hot') return 'hot';\r\n  return 'our';\r\n}\r\n\r\nfunction filterTable(cat, el) {\r\n  document.querySelectorAll('.filter-tab').forEach(t=>t.classList.remove('active'));\r\n  el.classList.add('active');\r\n  renderTable(cat==='all' ? allProperties : allProperties.filter(p=>p.Category&&p.Category.includes(cat)));\r\n}\r\n\r\n\/\/ CATEGORY CHECKBOXES\r\nfunction toggleCat(el, val) {\r\n  el.classList.toggle('checked');\r\n  if (el.classList.contains('checked')) {\r\n    if (!selectedCats.includes(val)) selectedCats.push(val);\r\n  } else {\r\n    selectedCats = selectedCats.filter(c=>c!==val);\r\n  }\r\n}\r\n\r\nfunction setCats(catString) {\r\n  selectedCats = [];\r\n  document.querySelectorAll('.cat-check').forEach(el => el.classList.remove('checked'));\r\n  if (!catString) return;\r\n  catString.split(',').forEach(c => {\r\n    const v = c.trim();\r\n    if (v) {\r\n      selectedCats.push(v);\r\n      if(v==='Our Property') document.getElementById('cc-our').classList.add('checked');\r\n      if(v==='Popular') document.getElementById('cc-popular').classList.add('checked');\r\n      if(v==='Hot') document.getElementById('cc-hot').classList.add('checked');\r\n    }\r\n  });\r\n}\r\n\r\n\/\/ KEY POINTS\r\nfunction addKP(val='') {\r\n  const list = document.getElementById('kp-list');\r\n  const row = document.createElement('div');\r\n  row.className = 'keypoint-row';\r\n  row.innerHTML = `<input type=\"text\" placeholder=\"e.g. Sea view from every room\" value=\"${val}\"><button class=\"btn-icon\" onclick=\"removeKP(this)\">\u2715<\/button>`;\r\n  list.appendChild(row);\r\n}\r\n\r\nfunction removeKP(btn) {\r\n  const list = document.getElementById('kp-list');\r\n  if (list.children.length > 1) btn.parentElement.remove();\r\n  else btn.previousElementSibling.value = '';\r\n}\r\n\r\nfunction getKeyPoints() {\r\n  return Array.from(document.querySelectorAll('#kp-list input'))\r\n    .map(i=>i.value.trim()).filter(Boolean).join('|');\r\n}\r\n\r\nfunction setKeyPoints(str) {\r\n  const list = document.getElementById('kp-list');\r\n  list.innerHTML = '';\r\n  const points = (str||'').split('|').filter(Boolean);\r\n  if (!points.length) points.push('');\r\n  points.forEach(p => addKP(p));\r\n}\r\n\r\n\/\/ ADD\/EDIT MODAL\r\nfunction openAddModal() {\r\n  editingRow = null;\r\n  document.getElementById('modal-title').textContent = 'Add New Property';\r\n  document.getElementById('save-btn').textContent = 'Save & Create Page';\r\n  clearForm();\r\n  document.getElementById('modal-add').classList.add('open');\r\n}\r\n\r\nfunction openEditModal(rowIndex) {\r\n  const p = allProperties.find(x=>x.rowIndex===rowIndex);\r\n  if (!p) return;\r\n  editingRow = rowIndex;\r\n  document.getElementById('modal-title').textContent = 'Edit Property';\r\n  document.getElementById('save-btn').textContent = 'Update Property';\r\n  document.getElementById('f-name').value = p.Name||'';\r\n  document.getElementById('f-location').value = p.Location||'';\r\n  document.getElementById('f-image').value = p.Image_URL||'';\r\n  document.getElementById('f-h1').value = p.H1||'';\r\n  document.getElementById('f-desc').value = p.Description||'';\r\n  setCats(p.Category||'');\r\n  setKeyPoints(p.KeyPoints||'');\r\n  previewImage();\r\n  document.getElementById('modal-add').classList.add('open');\r\n}\r\n\r\nasync function saveProperty() {\r\n  const name = document.getElementById('f-name').value.trim();\r\n  const location = document.getElementById('f-location').value;\r\n  if (!name) { showToast('\u26a0\ufe0f Property name required','error'); return; }\r\n  if (!location) { showToast('\u26a0\ufe0f Please select a location','error'); return; }\r\n  if (!selectedCats.length) { showToast('\u26a0\ufe0f Select at least one category','error'); return; }\r\n\r\n  const btn = document.getElementById('save-btn');\r\n  btn.textContent = '\u23f3 Creating page...'; btn.disabled = true;\r\n  showToast('\ud83d\udd04 Creating WordPress page...','info');\r\n\r\n  const params = {\r\n    action: editingRow ? 'edit' : 'add',\r\n    Name: name,\r\n    Location: location,\r\n    Image_URL: document.getElementById('f-image').value.trim(),\r\n    Category: selectedCats.join(','),\r\n    Booking_Link: 'https:\/\/wa.me\/8801891120929?text=I want to book: ' + encodeURIComponent(name),\r\n    H1: document.getElementById('f-h1').value.trim() || name,\r\n    Description: document.getElementById('f-desc').value.trim(),\r\n    KeyPoints: getKeyPoints()\r\n  };\r\n  if (editingRow) params.rowIndex = editingRow;\r\n\r\n  try {\r\n    const res = await apiCall(params);\r\n    if (res.status==='success') {\r\n      closeModal('modal-add');\r\n      const msg = res.wp_url\r\n        ? `\u2705 Done! Page: ${res.wp_url.substring(0,40)}...`\r\n        : (editingRow ? '\u2705 Property updated!' : '\u2705 Property added!');\r\n      showToast(msg,'success');\r\n      await loadProperties();\r\n    } else {\r\n      showToast('\u274c '+res.message,'error');\r\n    }\r\n  } catch(e) {\r\n    showToast('\u274c Error: '+e.message,'error');\r\n  }\r\n  btn.textContent = editingRow ? 'Update Property' : 'Save & Create Page';\r\n  btn.disabled = false;\r\n}\r\n\r\n\/\/ DELETE\r\nfunction openDeleteModal(rowIndex) {\r\n  deletingRow = rowIndex;\r\n  document.getElementById('modal-delete').classList.add('open');\r\n}\r\nasync function confirmDelete() {\r\n  try {\r\n    await apiCall({ action:'delete', rowIndex:deletingRow });\r\n    closeModal('modal-delete');\r\n    showToast('\ud83d\uddd1\ufe0f Property deleted!','success');\r\n    await loadProperties();\r\n  } catch(e) {\r\n    showToast('\u274c Error deleting.','error');\r\n  }\r\n}\r\n\r\n\/\/ HELPERS\r\nfunction clearForm() {\r\n  ['f-name','f-h1','f-desc'].forEach(id=>document.getElementById(id).value='');\r\n  document.getElementById('f-image').value='';\r\n  document.getElementById('f-location').value='';\r\n  document.getElementById('img-preview').innerHTML='<span>Image preview will appear here<\/span>';\r\n  setCats('');\r\n  setKeyPoints('');\r\n}\r\nfunction previewImage() {\r\n  const url = document.getElementById('f-image').value.trim();\r\n  const prev = document.getElementById('img-preview');\r\n  prev.innerHTML = url ? `<img decoding=\"async\" src=\"${url}\" onerror=\"this.parentElement.innerHTML='<span>Invalid URL<\/span>'\">` : '<span>Image preview will appear here<\/span>';\r\n}\r\nfunction closeModal(id) { document.getElementById(id).classList.remove('open'); }\r\nfunction setNav(el) { document.querySelectorAll('.nav-item').forEach(n=>n.classList.remove('active')); el.classList.add('active'); }\r\nfunction showToast(msg, type='success') {\r\n  const t=document.getElementById('toast');\r\n  t.textContent=msg; t.className=`toast ${type} show`;\r\n  setTimeout(()=>t.classList.remove('show'),4000);\r\n}\r\ndocument.querySelectorAll('.modal-overlay').forEach(o=>{\r\n  o.addEventListener('click',e=>{ if(e.target===o) o.classList.remove('open'); });\r\n});\r\n<\/script>\r\n<\/body>\r\n<\/html>\t\t\t\t<\/div>\n\t\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>Tourvill Admin Tourvill Admin Panel \u2014 Authorized Access Only Password Login \u2192 \u274c Wrong password. Try again. TourvillProperty Manager \ud83c\udfe0 All Properties \u2795 Add Property \ud83d\udd12 Logout All Properties Manage your tourvill property listings + Add Property \ud83c\udfe0Total\u2014 \ud83c\udfe1Our Property\u2014 \u2b50Popular\u2014 \ud83d\udd25Hot\u2014 Property List All Our Property Popular Hot Loading&#8230; Add New Property Basic Info [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"footnotes":""},"class_list":["post-1416","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/tourvill.com\/wp-json\/wp\/v2\/pages\/1416","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tourvill.com\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/tourvill.com\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/tourvill.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tourvill.com\/wp-json\/wp\/v2\/comments?post=1416"}],"version-history":[{"count":16,"href":"https:\/\/tourvill.com\/wp-json\/wp\/v2\/pages\/1416\/revisions"}],"predecessor-version":[{"id":1436,"href":"https:\/\/tourvill.com\/wp-json\/wp\/v2\/pages\/1416\/revisions\/1436"}],"wp:attachment":[{"href":"https:\/\/tourvill.com\/wp-json\/wp\/v2\/media?parent=1416"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}