curaflow/settings.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>