Mobile Publisher

Mobile Publisher · موبائل پبلشر — staff tool · Book portal
Dr. Wasif — Mobile Publisher :root { –bg: #0b1f33; –card: #0b1f33; –card2: #112240; –blue: #2176ab; –blue-light: #3385FF; –gold: #C9A84C; –gold-light: #E8C96A; –text: #E8EDF5; –muted: #8899AA; –border: #1E3050; –danger: #FF4444; –success: #22C55E; –warning: #F59E0B; –radius: 14px; –radius-sm: 8px; } * { box-sizing: border-box; margin: 0; padding: 0; -webkit-tap-highlight-color: transparent; } body { background: var(–bg); color: var(–text); font-family: ‘Outfit’, -apple-system, BlinkMacSystemFont, ‘Segoe UI’, sans-serif; min-height: 100vh; padding-bottom: 40px; } .header { background: linear-gradient(135deg, #0b1f33 0%, #112240 100%); border-bottom: 1px solid var(–border); padding: 16px 20px 14px; position: sticky; top: 0; z-index: 100; display: flex; align-items: center; gap: 12px; } .header-logo { width: 38px; height: 38px; background: linear-gradient(135deg, var(–blue), #0044CC); border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 18px; flex-shrink: 0; } .header-text h1 { font-size: 15px; font-weight: 700; color: var(–text); line-height: 1.2; } .header-text p { font-size: 11px; color: var(–gold); font-weight: 500; letter-spacing: 0.5px; text-transform: uppercase; } .header-badge { margin-left: auto; background: rgba(0,103,255,0.15); border: 1px solid rgba(0,103,255,0.3); color: var(–blue-light); font-size: 10px; font-weight: 600; padding: 4px 8px; border-radius: 20px; letter-spacing: 0.5px; text-transform: uppercase; } /* LOGIN */ #loginScreen { padding: 40px 20px; display: flex; flex-direction: column; align-items: center; gap: 20px; } .login-icon { width: 80px; height: 80px; background: linear-gradient(135deg, var(–blue), #0044CC); border-radius: 24px; display: flex; align-items: center; justify-content: center; font-size: 36px; box-shadow: 0 8px 32px rgba(0,103,255,0.3); } .login-title { font-size: 22px; font-weight: 700; text-align: center; } .login-sub { font-size: 13px; color: var(–muted); text-align: center; line-height: 1.5; } .login-card { background: var(–card); border: 1px solid var(–border); border-radius: var(–radius); padding: 24px 20px; width: 100%; max-width: 400px; display: flex; flex-direction: column; gap: 16px; } /* FORM */ .field-group { display: flex; flex-direction: column; gap: 6px; } .field-label { font-size: 11px; font-weight: 600; color: var(–gold); letter-spacing: 0.8px; text-transform: uppercase; } input[type=”text”], input[type=”password”], textarea, select { background: var(–card2); border: 1px solid var(–border); border-radius: var(–radius-sm); color: var(–text); font-family: inherit; font-size: 15px; padding: 12px 14px; width: 100%; outline: none; transition: border-color 0.2s; -webkit-appearance: none; appearance: none; } input:focus, textarea:focus, select:focus { border-color: var(–blue); box-shadow: 0 0 0 3px rgba(0,103,255,0.15); } textarea { min-height: 120px; resize: vertical; line-height: 1.6; } select { background-image: url(“data:image/svg+xml,%3Csvg xmlns=’http://www.w3.org/2000/svg’ width=’12’ height=’8′ viewBox=’0 0 12 8’%3E%3Cpath d=’M1 1l5 5 5-5′ stroke=’%238899AA’ stroke-width=’1.5′ fill=’none’ stroke-linecap=’round’/%3E%3C/svg%3E”); background-repeat: no-repeat; background-position: right 14px center; padding-right: 36px; } textarea.urdu-input { font-family: ‘Noto Nastaliq Urdu’, serif; direction: rtl; text-align: right; font-size: 16px; line-height: 2; } /* BUTTONS */ .btn { border: none; border-radius: var(–radius-sm); cursor: pointer; font-family: inherit; font-weight: 600; padding: 14px 20px; font-size: 15px; transition: all 0.2s; width: 100%; display: flex; align-items: center; justify-content: center; gap: 8px; } .btn-primary { background: linear-gradient(135deg, var(–blue), #0044CC); color: #fff; box-shadow: 0 4px 16px rgba(0,103,255,0.35); } .btn-primary:active { transform: scale(0.97); } .btn-gold { background: linear-gradient(135deg, var(–gold), #A07830); color: #fff; } .btn-outline { background: transparent; border: 1px solid var(–border); color: var(–muted); } .btn-danger { background: rgba(255,68,68,0.15); border: 1px solid rgba(255,68,68,0.3); color: var(–danger); } .btn-sm { padding: 8px 14px; font-size: 13px; width: auto; border-radius: 6px; } .btn:disabled { opacity: 0.5; cursor: not-allowed; } /* APP */ #appScreen { display: none; padding: 0 0 20px; } .tabs { display: flex; background: var(–card); border-bottom: 1px solid var(–border); overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; } .tabs::-webkit-scrollbar { display: none; } .tab { flex: 1; min-width: 80px; padding: 12px 8px; text-align: center; font-size: 11px; font-weight: 600; color: var(–muted); cursor: pointer; border-bottom: 2px solid transparent; transition: all 0.2s; white-space: nowrap; letter-spacing: 0.3px; } .tab.active { color: var(–blue-light); border-bottom-color: var(–blue); } .tab.live-tab { color: #FF4444; } .tab.live-tab.active { color: #FF6666; border-bottom-color: #FF4444; } .live-dot { width: 8px; height: 8px; background: #FF4444; border-radius: 50%; display: inline-block; margin-right: 5px; animation: livePulse 1.2s infinite; } @keyframes livePulse { 0%,100%{opacity:1;transform:scale(1)} 50%{opacity:0.4;transform:scale(0.8)} } .live-badge { background: #FF4444; color: #fff; font-size: 10px; font-weight: 700; padding: 2px 7px; border-radius: 4px; letter-spacing: 0.5px; display: inline-flex; align-items: center; gap: 4px; } .stream-option-card { background: var(–card2); border: 2px solid var(–border); border-radius: var(–radius); padding: 16px; cursor: pointer; transition: all 0.2s; margin-bottom: 10px; display: flex; align-items: center; gap: 14px; } .stream-option-card:hover, .stream-option-card.selected { border-color: var(–blue); background: rgba(0,103,255,0.08); } .stream-option-icon { font-size: 32px; flex-shrink: 0; } .stream-option-text h3 { font-size: 14px; font-weight: 700; color: var(–text); margin-bottom: 3px; } .stream-option-text p { font-size: 12px; color: var(–muted); line-height: 1.4; } .stream-status-card { background: rgba(255,68,68,0.08); border: 1px solid rgba(255,68,68,0.25); border-radius: var(–radius); padding: 20px; text-align: center; margin-bottom: 16px; } .stream-status-title { font-size: 18px; font-weight: 700; color: #FF6666; margin-bottom: 6px; } .stream-status-sub { font-size: 13px; color: var(–muted); } .tab-icon { font-size: 16px; display: block; margin-bottom: 2px; } .tab-panel { display: none; padding: 20px; } .tab-panel.active { display: block; } .section-card { background: var(–card); border: 1px solid var(–border); border-radius: var(–radius); padding: 18px; margin-bottom: 16px; } .section-title { font-size: 13px; font-weight: 700; color: var(–gold); letter-spacing: 0.5px; text-transform: uppercase; margin-bottom: 14px; display: flex; align-items: center; gap: 8px; } .section-title .urdu { font-family: ‘Noto Nastaliq Urdu’, serif; font-size: 13px; color: var(–muted); text-transform: none; letter-spacing: 0; direction: rtl; } .type-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; } .type-pill { background: var(–card2); border: 2px solid var(–border); border-radius: 10px; padding: 10px 8px; text-align: center; cursor: pointer; transition: all 0.2s; font-size: 12px; font-weight: 600; color: var(–muted); } .type-pill .pill-icon { font-size: 20px; display: block; margin-bottom: 4px; } .type-pill.selected { border-color: var(–blue); background: rgba(0,103,255,0.12); color: var(–blue-light); } .lang-toggle { display: flex; background: var(–card2); border: 1px solid var(–border); border-radius: var(–radius-sm); overflow: hidden; } .lang-btn { flex: 1; padding: 9px; text-align: center; font-size: 12px; font-weight: 600; cursor: pointer; transition: all 0.2s; color: var(–muted); border: none; background: transparent; font-family: inherit; } .lang-btn.active { background: var(–blue); color: #fff; } .media-upload-area { border: 2px dashed var(–border); border-radius: var(–radius); padding: 28px 20px; text-align: center; cursor: pointer; transition: all 0.2s; background: var(–card2); position: relative; } .media-upload-area input[type=”file”] { position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%; } .upload-icon { font-size: 36px; margin-bottom: 8px; } .upload-text { font-size: 14px; font-weight: 600; color: var(–text); margin-bottom: 4px; } .upload-sub { font-size: 12px; color: var(–muted); } #mediaPreviewGrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-top: 12px; } .preview-item { position: relative; border-radius: 8px; overflow: hidden; aspect-ratio: 1; background: var(–card2); } .preview-item img, .preview-item video { width: 100%; height: 100%; object-fit: cover; } .preview-item .remove-btn { position: absolute; top: 4px; right: 4px; background: rgba(0,0,0,0.7); border: none; border-radius: 50%; color: #fff; font-size: 14px; width: 22px; height: 22px; display: flex; align-items: center; justify-content: center; cursor: pointer; } .preview-item .video-badge { position: absolute; bottom: 4px; left: 4px; background: rgba(0,0,0,0.7); color: #fff; font-size: 10px; font-weight: 600; padding: 2px 6px; border-radius: 4px; } .preview-item .file-size { position: absolute; bottom: 4px; right: 4px; background: rgba(0,0,0,0.7); color: var(–muted); font-size: 9px; padding: 2px 5px; border-radius: 4px; } .consent-box { background: rgba(201,168,76,0.08); border: 1px solid rgba(201,168,76,0.25); border-radius: var(–radius-sm); padding: 14px; } .consent-box p { font-size: 12px; color: var(–muted); line-height: 1.6; margin-bottom: 10px; } .consent-box .urdu-consent { font-family: ‘Noto Nastaliq Urdu’, serif; direction: rtl; font-size: 12px; color: var(–muted); line-height: 1.8; margin-bottom: 10px; display: block; } .checkbox-row { display: flex; align-items: flex-start; gap: 10px; cursor: pointer; } .checkbox-row input[type=”checkbox”] { width: 20px; height: 20px; flex-shrink: 0; accent-color: var(–gold); margin-top: 1px; cursor: pointer; } .checkbox-row label { font-size: 13px; font-weight: 600; color: var(–gold-light); cursor: pointer; line-height: 1.4; } .alert { border-radius: var(–radius-sm); padding: 12px 14px; font-size: 13px; font-weight: 500; margin-bottom: 12px; display: flex; align-items: flex-start; gap: 8px; line-height: 1.4; } .alert-error { background: rgba(255,68,68,0.1); border: 1px solid rgba(255,68,68,0.25); color: #FF8888; } .alert-success { background: rgba(34,197,94,0.1); border: 1px solid rgba(34,197,94,0.25); color: #4ADE80; } .alert-info { background: rgba(0,103,255,0.1); border: 1px solid rgba(0,103,255,0.25); color: #60A5FA; } .alert-warning { background: rgba(245,158,11,0.1); border: 1px solid rgba(245,158,11,0.25); color: #FCD34D; } .progress-wrap { background: var(–card2); border-radius: 4px; height: 6px; overflow: hidden; margin: 8px 0; } .progress-bar { height: 100%; background: linear-gradient(90deg, var(–blue), var(–blue-light)); border-radius: 4px; transition: width 0.3s ease; width: 0%; } .progress-text { font-size: 12px; color: var(–muted); text-align: center; } .post-card { background: var(–card); border: 1px solid var(–border); border-radius: var(–radius); padding: 14px; margin-bottom: 12px; } .post-card-title { font-size: 14px; font-weight: 600; color: var(–text); line-height: 1.3; margin-bottom: 8px; } .post-card-meta { font-size: 11px; color: var(–muted); display: flex; gap: 10px; flex-wrap: wrap; } .post-status-dot { width: 6px; height: 6px; border-radius: 50%; display: inline-block; margin-right: 4px; } .dot-published { background: var(–success); } .dot-draft { background: var(–warning); } .post-card-actions { display: flex; gap: 8px; margin-top: 10px; } .settings-row { display: flex; align-items: center; justify-content: space-between; padding: 14px 0; border-bottom: 1px solid var(–border); } .settings-row:last-child { border-bottom: none; } .settings-label { font-size: 14px; font-weight: 500; } .settings-sub { font-size: 11px; color: var(–muted); margin-top: 2px; } .toggle-switch { position: relative; width: 44px; height: 24px; cursor: pointer; flex-shrink: 0; } .toggle-switch input { opacity: 0; width: 0; height: 0; } .toggle-slider { position: absolute; inset: 0; background: var(–border); border-radius: 24px; transition: 0.2s; } .toggle-slider:before { content: ”; position: absolute; width: 18px; height: 18px; left: 3px; top: 3px; background: #fff; border-radius: 50%; transition: 0.2s; } .toggle-switch input:checked + .toggle-slider { background: var(–blue); } .toggle-switch input:checked + .toggle-slider:before { transform: translateX(20px); } .char-counter { font-size: 11px; color: var(–muted); text-align: right; margin-top: 4px; } .char-counter.warn { color: var(–warning); } .char-counter.over { color: var(–danger); } .spinner { width: 18px; height: 18px; border: 2px solid rgba(255,255,255,0.3); border-top-color: #fff; border-radius: 50%; animation: spin 0.7s linear infinite; flex-shrink: 0; } @keyframes spin { to { transform: rotate(360deg); } } #successOverlay { display: none; position: fixed; inset: 0; background: rgba(11,31,51,0.95); z-index: 200; flex-direction: column; align-items: center; justify-content: center; gap: 16px; padding: 40px; text-align: center; } #successOverlay.show { display: flex; } .success-icon { font-size: 64px; animation: pop 0.4s ease; } @keyframes pop { 0% { transform: scale(0); } 80% { transform: scale(1.1); } 100% { transform: scale(1); } } .success-title { font-size: 24px; font-weight: 700; color: var(–success); } .success-sub { font-size: 14px; color: var(–muted); line-height: 1.6; } .info-chip { display: inline-flex; align-items: center; gap: 4px; background: rgba(0,103,255,0.1); border: 1px solid rgba(0,103,255,0.2); color: var(–blue-light); font-size: 11px; font-weight: 600; padding: 4px 10px; border-radius: 20px; } .required-star { color: var(–danger); margin-left: 2px; } .optional-tag { color: var(–muted); font-size: 10px; font-weight: 400; margin-left: 4px; } @media (min-width: 480px) { .type-grid { grid-template-columns: repeat(4, 1fr); } #mediaPreviewGrid { grid-template-columns: repeat(4, 1fr); } }

Dr. Wasif — Publisher

drwasifmalik.com

Checking…
✍️NEW POST
📋MY POSTS
🔴GO LIVE
⚙️SETTINGS
📌 Post Type پوسٹ کی قسم
🔬Procedure
🩻Investigation
Feedback
📝General
🌐 Language زبان
📄 Title عنوان
English Title *
0 / 120
اردو عنوان
📝 Content مواد
English Content *
0 / 5000
اردو مواد
📸 Photos & Videos تصاویر اور ویڈیوز
📷
Tap to add Photos or Videos
Camera • Gallery • Files — JPG, PNG, MP4, MOV (max 10 files)
⚖️ Patient Consent مریض کی اجازت
Consent Reference (optional)
🚀 Publish Options
Status
💾 Save as Draft (review before publishing) 🌐 Publish Immediately
Tags (comma-separated)
⏳ Uploading…
Preparing…
📋 Recent Posts
📭
Loading…
⚠️
YouTube Mobile Live requires 1,000 subscribers. Your channel currently has 1 subscriber. Use StreamYard below to go live from your phone right now — it works with any subscriber count.
📡 Stream Details
Stream Title
Description (optional)
Option 1 — Go Live NOW (Any Subscriber Count)
StreamYard lets you broadcast from your phone to YouTube with zero subscriber requirement. Free plan available.
🔴 Open StreamYard — Go Live
Sign in with your Google account → Create Broadcast → Select YouTube → Go Live
After going live, paste your YouTube Video/Stream ID here to embed it on your website:
Find the ID in your YouTube Studio → Live → copy the last part of the URL
📺 Option 2 — YouTube Live Direct (1,000 subs required)
Once your channel reaches 1,000 subscribers, you can go live directly from the YouTube app on your phone.
Current subscribers: 1 / 1,000 needed
How to grow faster: Share your YouTube channel link on WhatsApp, Facebook, and with patients.
🌐 Your Live Page on Website
When you are live, your patients and followers can watch at:
🔴 drwasifmalik.com/live
👤 Account
Logged in as
✅ Connected
drwasifmalik.com
WordPress site
🌐 Live
🚪 Session
Done!
👁️ View Post
const WP_URL = ‘https://drwasifmalik.com’; const WP_API = WP_URL + ‘/wp-json/wp/v2’; let state = { nonce: null, userName: null, postType: ‘procedure’, lang: ‘en’, mediaFiles: [], uploading: false }; // ── INIT ── window.addEventListener(‘load’, () => { checkExistingSession(); }); async function checkExistingSession() { // Method 1: Check if WordPress injected a nonce via wp.apiFetch (fastest — works when already logged in) try { if (typeof wp !== ‘undefined’ && wp.apiFetch && wp.apiFetch.nonceMiddleware && wp.apiFetch.nonceMiddleware.nonce) { const nonce = wp.apiFetch.nonceMiddleware.nonce; const userResp = await fetch(WP_API + ‘/users/me’, { credentials: ‘include’, headers: { ‘X-WP-Nonce’: nonce } }); if (userResp.ok) { const user = await userResp.json(); state.nonce = nonce; state.userName = user.name; showApp(); return; } } } catch(e) {} // Method 2: Extract nonce from inline page scripts try { const scripts = Array.from(document.querySelectorAll(‘script’)); for (const s of scripts) { const m = s.textContent.match(/createNonceMiddleware(s*[“‘]([a-f0-9]+)[“‘]/); if (m) { const nonce = m[1]; const userResp = await fetch(WP_API + ‘/users/me’, { credentials: ‘include’, headers: { ‘X-WP-Nonce’: nonce } }); if (userResp.ok) { const user = await userResp.json(); state.nonce = nonce; state.userName = user.name; showApp(); return; } } } } catch(e) {} // Not logged in — show login screen document.getElementById(‘headerBadge’).textContent = ‘Login Required’; document.getElementById(‘loginScreen’).style.display = ‘flex’; } // ── LOGIN ── async function doLogin() { const user = document.getElementById(‘loginUser’).value.trim(); const pass = document.getElementById(‘loginPass’).value.trim(); const alertEl = document.getElementById(‘loginAlert’); const btn = document.getElementById(‘loginBtn’); if (!user || !pass) { alertEl.innerHTML = ‘
⚠️ Please enter username and password.
‘; return; } alertEl.innerHTML = ‘
🔄 Signing in…
‘; btn.disabled = true; try { // Step 1: Log in via wp-login.php form submission const formData = new FormData(); formData.append(‘log’, user); formData.append(‘pwd’, pass); formData.append(‘wp-submit’, ‘Log In’); formData.append(‘redirect_to’, ‘/wp-admin/’); formData.append(‘testcookie’, ‘1’); const loginResp = await fetch(WP_URL + ‘/wp-login.php’, { method: ‘POST’, body: formData, credentials: ‘include’, redirect: ‘follow’ }); // Step 2: Get nonce (proves login worked) const nonceResp = await fetch(WP_URL + ‘/wp-admin/admin-ajax.php?action=rest-nonce’, { credentials: ‘include’ }); if (!nonceResp.ok) throw new Error(‘Login failed — could not get session token.’); const nonce = await nonceResp.text(); if (!nonce || nonce.length < 5 || nonce.includes('<') || nonce === '0') { throw new Error('Invalid credentials. Please check your username and password.'); } // Step 3: Verify with REST API const userResp = await fetch(WP_API + '/users/me', { credentials: 'include', headers: { 'X-WP-Nonce': nonce } }); if (!userResp.ok) throw new Error('Authentication failed. Please try again.'); const userData = await userResp.json(); state.nonce = nonce; state.userName = userData.name; showApp(); loadPosts(); loadCategories(); } catch(e) { alertEl.innerHTML = `
❌ ${e.message}
`; btn.disabled = false; } } function showApp() { document.getElementById(‘loginScreen’).style.display = ‘none’; document.getElementById(‘appScreen’).style.display = ‘block’; document.getElementById(‘settingsUsername’).textContent = state.userName || ‘Dr. Wasif’; document.getElementById(‘headerBadge’).textContent = ‘🟢 Live’; document.getElementById(‘headerBadge’).style.cssText = ‘background:rgba(34,197,94,0.15); border-color:rgba(34,197,94,0.3); color:#4ADE80’; loadPosts(); loadCategories(); } function doLogout() { if (!confirm(‘Sign out?’)) return; state.nonce = null; state.userName = null; // Navigate to WP logout window.location.href = WP_URL + ‘/wp-login.php?action=logout&_wpnonce=’ + state.nonce; } // ── TABS ── function switchTab(name) { document.querySelectorAll(‘.tab’).forEach(t => t.classList.remove(‘active’)); document.querySelectorAll(‘.tab-panel’).forEach(p => p.classList.remove(‘active’)); document.getElementById(‘tab-‘ + name).classList.add(‘active’); document.getElementById(‘panel-‘ + name).classList.add(‘active’); if (name === ‘posts’) loadPosts(); } // ── POST TYPE ── function selectType(el, type) { document.querySelectorAll(‘.type-pill’).forEach(p => p.classList.remove(‘selected’)); el.classList.add(‘selected’); state.postType = type; updatePublishBtn(); } // ── LANGUAGE ── function setLang(lang, btn) { document.querySelectorAll(‘.lang-btn’).forEach(b => b.classList.remove(‘active’)); btn.classList.add(‘active’); state.lang = lang; const showEn = lang === ‘en’ || lang === ‘both’; const showUr = lang === ‘ur’ || lang === ‘both’; document.getElementById(‘enTitleGroup’).style.display = showEn ? ‘flex’ : ‘none’; document.getElementById(‘enBodyGroup’).style.display = showEn ? ‘flex’ : ‘none’; document.getElementById(‘urTitleGroup’).style.display = showUr ? ‘block’ : ‘none’; document.getElementById(‘urBodyGroup’).style.display = showUr ? ‘block’ : ‘none’; } // ── CHAR COUNTER ── function updateCharCount(el, id, max) { const len = el.value.length; const c = document.getElementById(id); c.textContent = len + ‘ / ‘ + max; c.className = ‘char-counter’ + (len >= max ? ‘ over’ : len > max * 0.9 ? ‘ warn’ : ”); } // ── MEDIA ── function handleMediaSelect(input) { Array.from(input.files).forEach(file => { if (state.mediaFiles.length >= 10) return; state.mediaFiles.push(file); }); renderMediaPreviews(); input.value = ”; updateMediaInfo(); } function renderMediaPreviews() { const grid = document.getElementById(‘mediaPreviewGrid’); grid.innerHTML = ”; state.mediaFiles.forEach((file, idx) => { const isVideo = file.type.startsWith(‘video/’); const url = URL.createObjectURL(file); const sizeText = file.size > 1048576 ? (file.size/1048576).toFixed(1)+’MB’ : Math.round(file.size/1024)+’KB’; const item = document.createElement(‘div’); item.className = ‘preview-item’; item.innerHTML = isVideo ? `▶ VIDEO${sizeText}` : `preview${sizeText}`; grid.appendChild(item); }); } function removeMedia(idx) { state.mediaFiles.splice(idx, 1); renderMediaPreviews(); updateMediaInfo(); } function updateMediaInfo() { const info = document.getElementById(‘mediaInfo’); if (!state.mediaFiles.length) { info.innerHTML = ”; return; } const photos = state.mediaFiles.filter(f => f.type.startsWith(‘image/’)).length; const videos = state.mediaFiles.filter(f => f.type.startsWith(‘video/’)).length; const mb = (state.mediaFiles.reduce((a,f) => a+f.size, 0)/1048576).toFixed(1); info.innerHTML = `
📎 ${state.mediaFiles.length} file(s): ${photos} photo(s), ${videos} video(s) — ${mb} MB
`; } // ── PUBLISH BTN STATE ── function updatePublishBtn() { const btn = document.getElementById(‘publishBtn’); const consentOk = document.getElementById(‘consentCheck’).checked; btn.disabled = !consentOk; } // ── CATEGORIES ── let categoryMap = {}; async function loadCategories() { try { const r = await fetch(WP_API + ‘/categories?per_page=50’, { credentials: ‘include’, headers: { ‘X-WP-Nonce’: state.nonce } }); if (r.ok) { const cats = await r.json(); cats.forEach(c => { categoryMap[c.slug] = c.id; }); } } catch(e) {} } // ── UPLOAD MEDIA ── async function uploadMediaFile(file, idx, total) { setProgress(Math.round((idx/total)*60), `Uploading ${file.type.startsWith(‘video/’) ? ‘video’ : ‘photo’} ${idx+1} of ${total}…`); const fd = new FormData(); fd.append(‘file’, file, file.name || `upload-${Date.now()}`); const r = await fetch(WP_API + ‘/media’, { method: ‘POST’, credentials: ‘include’, headers: { ‘X-WP-Nonce’: state.nonce }, body: fd }); if (!r.ok) { const e = await r.json(); throw new Error(‘Upload failed: ‘ + (e.message || r.status)); } return (await r.json()).id; } // ── PUBLISH ── async function publishPost() { if (state.uploading) return; const alertEl = document.getElementById(‘composeAlert’); alertEl.innerHTML = ”; const needEn = state.lang === ‘en’ || state.lang === ‘both’; const needUr = state.lang === ‘ur’ || state.lang === ‘both’; const title = document.getElementById(‘postTitle’).value.trim(); const body = document.getElementById(‘postBody’).value.trim(); const titleUr = document.getElementById(‘postTitleUr’).value.trim(); const bodyUr = document.getElementById(‘postBodyUr’).value.trim(); if (needEn && !title) { alertEl.innerHTML = ‘
⚠️ English title is required.
‘; alertEl.scrollIntoView({behavior:’smooth’}); return; } if (needEn && !body) { alertEl.innerHTML = ‘
⚠️ English content is required.
‘; alertEl.scrollIntoView({behavior:’smooth’}); return; } if (needUr && !titleUr) { alertEl.innerHTML = ‘
⚠️ Urdu title is required.
‘; alertEl.scrollIntoView({behavior:’smooth’}); return; } if (!document.getElementById(‘consentCheck’).checked) { alertEl.innerHTML = ‘
⚠️ Patient consent must be confirmed.
‘; alertEl.scrollIntoView({behavior:’smooth’}); return; } state.uploading = true; const btn = document.getElementById(‘publishBtn’); btn.disabled = true; btn.innerHTML = ‘
Publishing…’; document.getElementById(‘uploadProgress’).style.display = ‘block’; setProgress(5, ‘Starting…’); try { // Upload media const mediaIds = []; for (let i = 0; i < state.mediaFiles.length; i++) { mediaIds.push(await uploadMediaFile(state.mediaFiles[i], i, state.mediaFiles.length)); } setProgress(65, 'Building post…'); // Build content const typeLabels = { procedure:'🔬 Procedure', investigation:'🩻 Patient Investigation', feedback:'⭐ Patient Feedback', general:'📝 General' }; const consentRef = document.getElementById('consentRef').value.trim(); let content = `

${typeLabels[state.postType]}

nn`; if (needEn && body) content += body.replace(/n/g, ‘
‘) + ‘nn’; if (needUr && bodyUr) content += `
${bodyUr.replace(/n/g,’
‘)}
nn`; if (document.getElementById(‘consentCheck’).checked) content += `n

✅ Patient consent obtained for educational publication.${consentRef ? ‘ Ref: ‘+consentRef : ”}

`; if (mediaIds.length > 0) { content += ‘nnnn’; } setProgress(75, ‘Publishing…’); // Category const catSlug = { procedure:’procedures’, investigation:’investigations’, feedback:’patient-stories’, general:’neurosurgery-news’ }[state.postType]; const catIds = categoryMap[catSlug] ? [categoryMap[catSlug]] : []; // Title let finalTitle = needEn ? title : titleUr; if (needEn && needUr && title && titleUr) finalTitle = title + ‘ — ‘ + titleUr; // Tags const tagNames = document.getElementById(‘postTags’).value.split(‘,’).map(t=>t.trim()).filter(Boolean); const tagIds = []; for (const tagName of tagNames) { try { const tr = await fetch(WP_API + ‘/tags’, { method:’POST’, credentials:’include’, headers:{‘X-WP-Nonce’:state.nonce,’Content-Type’:’application/json’}, body:JSON.stringify({name:tagName}) }); if (tr.ok) { tagIds.push((await tr.json()).id); } else { const sr = await fetch(WP_API + ‘/tags?search=’ + encodeURIComponent(tagName), { credentials:’include’, headers:{‘X-WP-Nonce’:state.nonce} }); if (sr.ok) { const ex = await sr.json(); if (ex.length) tagIds.push(ex[0].id); } } } catch(e) {} } setProgress(88, ‘Finalising…’); const postData = { title: finalTitle, content, status: document.getElementById(‘postStatus’).value, categories: catIds, tags: tagIds }; const firstImgIdx = state.mediaFiles.findIndex(f => f.type.startsWith(‘image/’)); if (firstImgIdx >= 0 && mediaIds[firstImgIdx]) postData.featured_media = mediaIds[firstImgIdx]; const pr = await fetch(WP_API + ‘/posts’, { method:’POST’, credentials:’include’, headers:{‘X-WP-Nonce’:state.nonce,’Content-Type’:’application/json’}, body:JSON.stringify(postData) }); if (!pr.ok) { const e = await pr.json(); throw new Error(‘Post failed: ‘ + (e.message || pr.status)); } const post = await pr.json(); setProgress(100, ‘Done!’); setTimeout(() => showSuccess(post), 500); resetForm(); } catch(e) { alertEl.innerHTML = `
❌ ${e.message}
`; alertEl.scrollIntoView({behavior:’smooth’}); document.getElementById(‘uploadProgress’).style.display = ‘none’; } finally { state.uploading = false; btn.disabled = false; btn.innerHTML = ‘🚀 Publish Post‘; updatePublishBtn(); } } function setProgress(pct, text) { document.getElementById(‘progressBar’).style.width = pct + ‘%’; document.getElementById(‘progressText’).textContent = text; } function showSuccess(post) { const isDraft = post.status === ‘draft’; document.getElementById(‘successMsg’).textContent = isDraft ? ‘Saved as draft. Review it in WordPress Admin before publishing.’ : ‘Your post is now live on drwasifmalik.com!’; document.getElementById(‘viewPostBtn’).href = isDraft ? `https://drwasifmalik.com/wp-admin/post.php?post=${post.id}&action=edit` : post.link; document.getElementById(‘viewPostBtn’).textContent = isDraft ? ‘✏️ Edit Draft’ : ‘👁️ View Post’; document.getElementById(‘successOverlay’).classList.add(‘show’); document.getElementById(‘uploadProgress’).style.display = ‘none’; } function dismissSuccess() { document.getElementById(‘successOverlay’).classList.remove(‘show’); switchTab(‘compose’); } function resetForm() { [‘postTitle’,’postBody’,’postTitleUr’,’postBodyUr’,’postTags’,’consentRef’].forEach(id => { document.getElementById(id).value = ”; }); document.getElementById(‘consentCheck’).checked = false; document.getElementById(‘mediaPreviewGrid’).innerHTML = ”; document.getElementById(‘mediaInfo’).innerHTML = ”; document.getElementById(‘composeAlert’).innerHTML = ”; document.getElementById(‘titleCount’).textContent = ‘0 / 120’; document.getElementById(‘bodyCount’).textContent = ‘0 / 5000’; state.mediaFiles = []; } // ── POSTS LIST ── async function loadPosts() { if (!state.nonce) return; const list = document.getElementById(‘postsList’); list.innerHTML = ‘
🔄 Loading…
‘; try { const r = await fetch(WP_API + ‘/posts?per_page=15&orderby=date&order=desc’, { credentials:’include’, headers:{‘X-WP-Nonce’:state.nonce} }); if (!r.ok) throw new Error(‘Failed’); const posts = await r.json(); if (!posts.length) { list.innerHTML = ‘
📭
No posts yet.
‘; return; } list.innerHTML = posts.map(p => { const date = new Date(p.date).toLocaleDateString(‘en-PK’,{day:’numeric’,month:’short’,year:’numeric’}); const isDraft = p.status === ‘draft’; const title = p.title.rendered.replace(/]+>/g,”); return `
${title}
${isDraft?’Draft’:’Published’}📅 ${date}
${isDraft?’✏️ Edit’:’👁️ View’}${isDraft?``:”}
`; }).join(”); } catch(e) { list.innerHTML = ‘
❌ Could not load posts.
‘; } } async function publishDraft(id, btn) { if (!confirm(‘Publish this draft to the live website now?’)) return; btn.disabled = true; btn.textContent = ‘⏳…’; try { const r = await fetch(WP_API + ‘/posts/’ + id, { method:’POST’, credentials:’include’, headers:{‘X-WP-Nonce’:state.nonce,’Content-Type’:’application/json’}, body:JSON.stringify({status:’publish’}) }); if (r.ok) { btn.textContent = ‘✅ Published!’; setTimeout(loadPosts, 1000); } else { btn.textContent = ‘❌ Failed’; btn.disabled = false; } } catch(e) { btn.textContent = ‘❌ Error’; btn.disabled = false; } } // ── EMBED LIVE STREAM ── async function embedLiveStream() { const videoId = document.getElementById(‘streamVideoId’).value.trim(); const title = document.getElementById(‘streamTitle’).value.trim() || ‘Live Stream — Dr. Wasif Rizwan Malik’; const desc = document.getElementById(‘streamDesc’).value.trim(); const statusEl = document.getElementById(’embedStatus’); if (!videoId) { statusEl.innerHTML = ‘
❌ Please paste your YouTube Video ID first.
‘; return; } // Extract video ID if full URL was pasted const idMatch = videoId.match(/(?:v=|youtu.be/|live/)([w-]{11})/); const cleanId = idMatch ? idMatch[1] : videoId; statusEl.innerHTML = ‘
⏳ Updating live page on your website…
‘; const liveContent = `

🔴 LIVE NOW — ${title}

${desc ? `

${desc}

` : ”}

📺 Watching live: Open on YouTube  | 📲 Subscribe: Dr. Wasif on YouTube

`; try { const r = await fetch(WP_API.replace(‘/posts’, ‘/pages’) + ‘/1618’, { method: ‘POST’, credentials: ‘include’, headers: { ‘X-WP-Nonce’: state.nonce, ‘Content-Type’: ‘application/json’ }, body: JSON.stringify({ title: ‘🔴 Live — ‘ + title, content: liveContent, status: ‘publish’ }) }); if (r.ok) { statusEl.innerHTML = ‘
✅ Live page updated! View it now →
‘; } else { statusEl.innerHTML = ‘
❌ Could not update live page. Share the stream link manually.
‘; } } catch(e) { statusEl.innerHTML = ‘
❌ Error: ‘ + e.message + ‘
‘; } } async function deletePost(id, title, btn) { if (!confirm(`⚠️ Permanently delete this post?nn”${title}”nnThis cannot be undone.`)) return; btn.disabled = true; btn.textContent = ‘⏳…’; try { // First move to trash (status=trash), then force-delete const r = await fetch(WP_API + ‘/posts/’ + id + ‘?force=true’, { method: ‘DELETE’, credentials: ‘include’, headers: { ‘X-WP-Nonce’: state.nonce } }); if (r.ok) { btn.closest(‘.post-card’).style.transition = ‘opacity 0.3s’; btn.closest(‘.post-card’).style.opacity = ‘0’; setTimeout(loadPosts, 400); } else { const err = await r.json(); alert(‘❌ Could not delete: ‘ + (err.message || r.status)); btn.textContent = ‘🗑️ Delete’; btn.disabled = false; } } catch(e) { alert(‘❌ Error: ‘ + e.message); btn.textContent = ‘🗑️ Delete’; btn.disabled = false; } }
NeuroGuide AI
1

NeuroGuide AI

ڈاکٹر واصف ملک · مریض معاون

ایمرجنسی: 1122 · Booking: https://rx.drwasifmalik.com · طبی تشخیص نہیں