413 lines
21 KiB
HTML
413 lines
21 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Curio Settings | Hospital Configuration</title>
|
|
<link rel="stylesheet" href="globals.css">
|
|
<link rel="stylesheet" href="index.css">
|
|
<link rel="stylesheet" href="dashboard.css">
|
|
<link rel="stylesheet" href="settings.css">
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Outfit:wght@500;600;700&display=swap" rel="stylesheet">
|
|
<script src="globals.js"></script>
|
|
<script src="tenantLoader.js"></script>
|
|
<script>requireLogin(['OWNER', 'DOCTOR', 'SUPER_ADMIN']);</script>
|
|
</head>
|
|
<body class="dashboard-body">
|
|
<aside class="sidebar">
|
|
<div class="logo">
|
|
<div class="logo-icon">C</div>
|
|
<span class="logo-text">Curio</span>
|
|
</div>
|
|
<nav class="side-nav">
|
|
<a href="dashboard.html"><span>🏠</span> Overview</a>
|
|
<a href="dashboard.html"><span>👥</span> Queue</a>
|
|
<a href="dashboard.html"><span>📂</span> Patients</a>
|
|
<a href="pharmacy.html"><span>💊</span> Pharmacy</a>
|
|
<a href="lab.html"><span>🔬</span> Lab</a>
|
|
<a href="billing.html"><span>💳</span> Billing & Accounts</a>
|
|
<a href="staff.html"><span>👔</span> Staff & Payroll</a>
|
|
<a href="settings.html" class="active"><span>⚙️</span> Settings</a>
|
|
</nav>
|
|
<div class="user-profile">
|
|
<div class="avatar" id="userAvatar">AD</div>
|
|
<div class="info">
|
|
<strong id="userNameDisplay">Admin</strong>
|
|
<span id="userRoleDisplay">Settings</span>
|
|
<a href="login.html" class="logout-btn"><span>🚪</span> Logout</a>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
|
|
<main class="dashboard-main">
|
|
<header class="dash-header">
|
|
<div class="header-title">
|
|
<h1>Hospital Settings</h1>
|
|
<p>Configure pricing, slot limits, and clinic profile.</p>
|
|
</div>
|
|
<div class="header-actions">
|
|
<button class="btn-primary" id="saveSettings">Save Changes</button>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="settings-grid admin-only">
|
|
<div class="section-card">
|
|
<h2>Pricing & Fees</h2>
|
|
<div class="input-group">
|
|
<label>Consultation Fee (₹)</label>
|
|
<input type="number" value="500" id="config-fee">
|
|
</div>
|
|
<div class="input-group">
|
|
<label>Default Currency</label>
|
|
<select>
|
|
<option>INR (₹)</option>
|
|
<option>USD ($)</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section-card">
|
|
<h2>Slot Management</h2>
|
|
<div class="input-group">
|
|
<label>Slot Duration (Minutes)</label>
|
|
<input type="number" value="30" id="config-duration">
|
|
</div>
|
|
<div class="input-group">
|
|
<label>Max Online Bookings per Slot</label>
|
|
<input type="number" value="3" id="config-online">
|
|
</div>
|
|
<div class="input-group">
|
|
<label>Max Walk-in Tokens per Slot</label>
|
|
<input type="number" value="5" id="config-walkin">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section-card">
|
|
<h2>Pharmacy Config</h2>
|
|
<p>Manage medicine pricing and GST rates.</p>
|
|
<div class="input-group">
|
|
<label>Default GST %</label>
|
|
<input type="number" value="12">
|
|
</div>
|
|
<div class="input-group">
|
|
<label>Markup Percentage (%)</label>
|
|
<input type="number" value="20">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section-card">
|
|
<h2>Services Catalog</h2>
|
|
<p>Add specialized services/amenities offered by your clinic.</p>
|
|
<div id="servicesList" style="margin-bottom: 1rem;">
|
|
<!-- Dynamic List -->
|
|
</div>
|
|
<div class="input-group" style="display:flex; gap:0.5rem;">
|
|
<input type="text" id="newServiceName" placeholder="Service Name (e.g. ECG)">
|
|
<input type="number" id="newServicePrice" placeholder="Price (₹)" style="width: 100px;">
|
|
<button class="btn-secondary" onclick="addService()">+ Add</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section-card" style="grid-column: 1 / -1;">
|
|
<h2>AI Copilots & Automation</h2>
|
|
<p>Enable and configure autonomous agents to assist with your clinic operations.</p>
|
|
|
|
<div style="display:grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1rem; margin-top: 1rem;">
|
|
<!-- Copilot Cards -->
|
|
<div class="bill-card" style="flex-direction:column; align-items:flex-start;">
|
|
<div style="display:flex; justify-content:space-between; width:100%; margin-bottom:0.5rem;">
|
|
<strong>🧾 Automated Medical Billing</strong>
|
|
<label class="switch">
|
|
<input type="checkbox" id="agent-BILLING" onchange="toggleAgent('BILLING', this.checked)">
|
|
<span class="slider round"></span>
|
|
</label>
|
|
</div>
|
|
<p class="text-muted" style="font-size:0.875rem;">Automatically extracts ICD-10 and CPT codes from Zen Scribe clinical summaries.</p>
|
|
</div>
|
|
|
|
<div class="bill-card" style="flex-direction:column; align-items:flex-start;">
|
|
<div style="display:flex; justify-content:space-between; width:100%; margin-bottom:0.5rem;">
|
|
<strong>💬 Patient Triage Agent</strong>
|
|
<label class="switch">
|
|
<input type="checkbox" id="agent-TRIAGE" onchange="toggleAgent('TRIAGE', this.checked)">
|
|
<span class="slider round"></span>
|
|
</label>
|
|
</div>
|
|
<p class="text-muted" style="font-size:0.875rem;">Listens to WhatsApp messages and categorizes patient symptoms by urgency.</p>
|
|
</div>
|
|
|
|
<div class="bill-card" style="flex-direction:column; align-items:flex-start;">
|
|
<div style="display:flex; justify-content:space-between; width:100%; margin-bottom:0.5rem;">
|
|
<strong>🔄 Post-Visit Follow-Up</strong>
|
|
<label class="switch">
|
|
<input type="checkbox" id="agent-FOLLOWUP" onchange="toggleAgent('FOLLOWUP', this.checked)">
|
|
<span class="slider round"></span>
|
|
</label>
|
|
</div>
|
|
<p class="text-muted" style="font-size:0.875rem;">Automatically sends empathetic check-in messages to patients a few days after their visit.</p>
|
|
</div>
|
|
|
|
<div class="bill-card" style="flex-direction:column; align-items:flex-start;">
|
|
<div style="display:flex; justify-content:space-between; width:100%; margin-bottom:0.5rem;">
|
|
<strong>📦 Pharmacy Inventory Agent</strong>
|
|
<label class="switch">
|
|
<input type="checkbox" id="agent-INVENTORY" onchange="toggleAgent('INVENTORY', this.checked)">
|
|
<span class="slider round"></span>
|
|
</label>
|
|
</div>
|
|
<p class="text-muted" style="font-size:0.875rem;">Monitors pharmacy stock and automatically drafts purchase orders when supplies run low.</p>
|
|
</div>
|
|
|
|
<div class="bill-card" style="flex-direction:column; align-items:flex-start;">
|
|
<div style="display:flex; justify-content:space-between; width:100%; margin-bottom:0.5rem;">
|
|
<strong>🧠 Clinical Decision Support</strong>
|
|
<label class="switch">
|
|
<input type="checkbox" id="agent-CDSS" onchange="toggleAgent('CDSS', this.checked)">
|
|
<span class="slider round"></span>
|
|
</label>
|
|
</div>
|
|
<p class="text-muted" style="font-size:0.875rem;">Analyzes prescriptions against patient history to warn doctors of drug-drug interactions.</p>
|
|
</div>
|
|
|
|
<div class="bill-card" style="flex-direction:column; align-items:flex-start;">
|
|
<div style="display:flex; justify-content:space-between; width:100%; margin-bottom:0.5rem;">
|
|
<strong>📷 Prescription Vision Parser</strong>
|
|
<label class="switch">
|
|
<input type="checkbox" id="agent-RX_VISION" onchange="toggleAgent('RX_VISION', this.checked)">
|
|
<span class="slider round"></span>
|
|
</label>
|
|
</div>
|
|
<p class="text-muted" style="font-size:0.875rem;">Automatically converts handwritten prescription photos into digital, structured medication lists.</p>
|
|
</div>
|
|
|
|
<div class="bill-card" style="flex-direction:column; align-items:flex-start;">
|
|
<div style="display:flex; justify-content:space-between; width:100%; margin-bottom:0.5rem;">
|
|
<strong>📄 Document Intelligence</strong>
|
|
<label class="switch">
|
|
<input type="checkbox" id="agent-DOC_INTELLIGENCE" onchange="toggleAgent('DOC_INTELLIGENCE', this.checked)">
|
|
<span class="slider round"></span>
|
|
</label>
|
|
</div>
|
|
<p class="text-muted" style="font-size:0.875rem;">Extracts data from uploaded lab reports and medical documents into structured records.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="settings-grid doctor-only hidden">
|
|
<div class="section-card">
|
|
<h2>Clinical Profile</h2>
|
|
<div class="input-group">
|
|
<label>Full Name</label>
|
|
<input type="text" value="Dr. Sharma">
|
|
</div>
|
|
<div class="input-group">
|
|
<label>Specialization</label>
|
|
<input type="text" value="Cardiologist">
|
|
</div>
|
|
<div class="input-group">
|
|
<label>Consultation Fee (Personal Override)</label>
|
|
<input type="number" value="800">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section-card">
|
|
<h2>Zen Scribe Preferences</h2>
|
|
<div class="input-group">
|
|
<label>Default Scribe Language</label>
|
|
<select>
|
|
<option value="en-US">English (US)</option>
|
|
<option value="hi-IN">Hindi</option>
|
|
<option value="te-IN">Telugu</option>
|
|
</select>
|
|
</div>
|
|
<div class="input-group">
|
|
<label>Auto-Generate Clinical Summary</label>
|
|
<select>
|
|
<option>Enabled</option>
|
|
<option>Disabled</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section-card">
|
|
<h2>Blocked Dates</h2>
|
|
<p>Select dates you are unavailable for consultations.</p>
|
|
<div class="input-group" style="display:flex; gap:0.5rem;">
|
|
<input type="date" id="blockDateInput">
|
|
<button class="btn-secondary" onclick="addBlockDate()">+ Block Date</button>
|
|
</div>
|
|
<ul id="blockedDatesList" style="list-style:none; margin-top:1rem;"></ul>
|
|
</div>
|
|
|
|
<div class="section-card">
|
|
<h2>Profile Media (Public)</h2>
|
|
<p>Upload a profile video or image for the patient portal.</p>
|
|
<div class="signature-upload" style="border: 2px dashed var(--border); padding: 2rem; text-align: center; border-radius: var(--radius-md);">
|
|
<input type="file" id="mediaUploadInput" accept="image/*,video/*" style="display:none;" onchange="uploadMedia()">
|
|
<p>Click to upload image or video</p>
|
|
<button class="btn-small secondary" onclick="document.getElementById('mediaUploadInput').click()">Upload Media</button>
|
|
</div>
|
|
<div id="mediaPreview" style="margin-top: 1rem; display:flex; gap: 1rem; flex-wrap: wrap;"></div>
|
|
</div>
|
|
|
|
<div class="section-card">
|
|
<h2>Q&A (Public)</h2>
|
|
<p>Add common questions patients ask you.</p>
|
|
<div id="qaList" style="margin-bottom: 1rem;"></div>
|
|
<div class="input-group">
|
|
<input type="text" id="qaQuestion" placeholder="Question" style="margin-bottom:0.5rem;">
|
|
<textarea id="qaAnswer" placeholder="Answer" rows="2" style="width:100%; border: 1px solid var(--border); border-radius: var(--radius-md); padding: 0.5rem; font-family: inherit;"></textarea>
|
|
</div>
|
|
<button class="btn-secondary" onclick="addQA()" style="margin-top:0.5rem;">+ Add Q&A</button>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<script>
|
|
async function fetchSettings() {
|
|
try {
|
|
const [settingsRes, agentsRes] = await Promise.all([
|
|
fetch('/api/clinic/settings', {
|
|
headers: { 'Authorization': `Bearer ${localStorage.getItem('curio_token')}` }
|
|
}),
|
|
fetch('/api/clinic/agents', {
|
|
headers: { 'Authorization': `Bearer ${localStorage.getItem('curio_token')}` }
|
|
})
|
|
]);
|
|
|
|
if (settingsRes.ok) {
|
|
const data = await settingsRes.json();
|
|
document.getElementById('clinicName').value = data.name;
|
|
document.getElementById('consultationFee').value = data.consultation_fee;
|
|
document.getElementById('primaryColor').value = data.primary_color || '#0F172A';
|
|
document.getElementById('secondaryColor').value = data.secondary_color || '#38BDF8';
|
|
}
|
|
|
|
if (agentsRes.ok) {
|
|
const agents = await agentsRes.json();
|
|
agents.forEach(a => {
|
|
const toggle = document.getElementById(`agent-${a.agent_type}`);
|
|
if (toggle) toggle.checked = a.is_active;
|
|
});
|
|
}
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
}
|
|
|
|
async function toggleAgent(type, isActive) {
|
|
try {
|
|
const res = await fetch(`/api/clinic/agents/${type}`, {
|
|
method: 'PATCH',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${localStorage.getItem('curio_token')}`
|
|
},
|
|
body: JSON.stringify({ isActive })
|
|
});
|
|
if (!res.ok) {
|
|
alert('Failed to update agent config');
|
|
document.getElementById(`agent-${type}`).checked = !isActive;
|
|
}
|
|
} catch(e) {
|
|
console.error(e);
|
|
alert('Network Error');
|
|
document.getElementById(`agent-${type}`).checked = !isActive;
|
|
}
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
fetchSettings();
|
|
const role = localStorage.getItem('userRole') || 'OWNER';
|
|
const name = localStorage.getItem('userName') || 'Admin';
|
|
|
|
// Update Profile UI
|
|
document.getElementById('userNameDisplay').innerText = role === 'DOCTOR' ? 'Dr. Sharma' : name;
|
|
document.getElementById('userRoleDisplay').innerText = role;
|
|
document.getElementById('userAvatar').innerText = role === 'DOCTOR' ? 'DS' : 'AD';
|
|
|
|
if (role === 'DOCTOR') {
|
|
document.querySelector('.admin-only').classList.add('hidden');
|
|
document.querySelector('.doctor-only').classList.remove('hidden');
|
|
document.querySelector('.header-title h1').innerText = 'Doctor Preferences';
|
|
document.querySelector('.header-title p').innerText = 'Manage your profile and clinical tool settings.';
|
|
|
|
// Hide admin sidebar links
|
|
document.querySelectorAll('.side-nav a').forEach(link => {
|
|
const text = link.innerText.toLowerCase();
|
|
if (text.includes('billing') || text.includes('staff') || text.includes('overview')) {
|
|
link.style.display = 'none';
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
document.getElementById('saveSettings').onclick = () => {
|
|
alert('Settings saved successfully!');
|
|
};
|
|
|
|
// UI Stub Functions for Advanced Profiles
|
|
function addService() {
|
|
const name = document.getElementById('newServiceName').value;
|
|
const price = document.getElementById('newServicePrice').value;
|
|
if (!name || !price) return alert('Enter service name and price');
|
|
const div = document.createElement('div');
|
|
div.innerHTML = `<strong>${name}</strong> - ₹${price}`;
|
|
document.getElementById('servicesList').appendChild(div);
|
|
document.getElementById('newServiceName').value = '';
|
|
document.getElementById('newServicePrice').value = '';
|
|
}
|
|
|
|
function addBlockDate() {
|
|
const date = document.getElementById('blockDateInput').value;
|
|
if (!date) return;
|
|
const li = document.createElement('li');
|
|
li.textContent = `🚫 ${date}`;
|
|
document.getElementById('blockedDatesList').appendChild(li);
|
|
}
|
|
|
|
async function uploadMedia() {
|
|
const input = document.getElementById('mediaUploadInput');
|
|
if (!input.files || input.files.length === 0) return;
|
|
|
|
const file = input.files[0];
|
|
const formData = new FormData();
|
|
formData.append('media', file);
|
|
|
|
try {
|
|
const res = await fetch('/api/doctor/upload-media', {
|
|
method: 'POST',
|
|
headers: { 'Authorization': `Bearer ${localStorage.getItem('curio_token')}` },
|
|
body: formData
|
|
});
|
|
const data = await res.json();
|
|
if (data.success) {
|
|
const div = document.createElement('div');
|
|
div.innerHTML = `<img src="${data.url}" style="max-width: 150px; border-radius: var(--radius-md);">`;
|
|
document.getElementById('mediaPreview').appendChild(div);
|
|
alert('Media uploaded to MinIO!');
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
alert('Upload failed');
|
|
}
|
|
}
|
|
|
|
function addQA() {
|
|
const q = document.getElementById('qaQuestion').value;
|
|
const a = document.getElementById('qaAnswer').value;
|
|
if (!q || !a) return;
|
|
const div = document.createElement('div');
|
|
div.style.marginBottom = '0.5rem';
|
|
div.innerHTML = `<strong>Q: ${q}</strong><br><span class="text-muted">A: ${a}</span>`;
|
|
document.getElementById('qaList').appendChild(div);
|
|
document.getElementById('qaQuestion').value = '';
|
|
document.getElementById('qaAnswer').value = '';
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|