pediatric-ai-scribe-v3/public/components/admin.html

486 lines
37 KiB
HTML

<div class="module-header">
<h2><i class="fas fa-user-shield"></i> Admin Panel</h2>
<p>Manage users, app settings, and view usage statistics</p>
</div>
<!-- Stats -->
<div class="admin-stats" id="admin-stats">
<div class="stat-card"><div class="stat-value" id="stat-users"></div><div class="stat-label">Total Users</div></div>
<div class="stat-card"><div class="stat-value" id="stat-api-total"></div><div class="stat-label">Total API Calls</div></div>
<div class="stat-card"><div class="stat-value" id="stat-api-today"></div><div class="stat-label">API Calls Today</div></div>
</div>
<!-- Registration -->
<div class="card">
<div class="card-header"><h3><i class="fas fa-door-open"></i> Registration</h3></div>
<div style="padding:16px;display:flex;align-items:center;gap:16px;flex-wrap:wrap;">
<span id="reg-status-text" style="font-size:13px;color:var(--g600);">Loading...</span>
<button id="btn-toggle-reg" class="btn-sm btn-primary">Toggle</button>
</div>
</div>
<!-- Users -->
<div class="card">
<div class="card-header">
<h3><i class="fas fa-users"></i> Users</h3>
<button id="btn-refresh-users" class="btn-sm btn-ghost"><i class="fas fa-rotate"></i> Refresh</button>
</div>
<div style="overflow-x:auto;">
<table class="admin-table" id="admin-users-table">
<thead>
<tr>
<th>Name / Email</th>
<th>Role</th>
<th>Status</th>
<th>Joined</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="admin-users-body">
<tr><td colspan="5" style="text-align:center;color:var(--g400);padding:20px;">Loading...</td></tr>
</tbody>
</table>
</div>
</div>
<!-- ── CMS: Announcement Banner ──────────────────────────────── -->
<div class="card">
<div class="card-header"><h3><i class="fas fa-bullhorn"></i> Announcement Banner</h3></div>
<div style="padding:16px;display:flex;flex-direction:column;gap:12px;">
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
<label style="font-size:13px;font-weight:600;">Show banner:</label>
<select id="cms-ann-enabled" style="font-size:13px;padding:4px 8px;border:1px solid var(--g300);border-radius:6px;">
<option value="false">Hidden</option>
<option value="true">Visible</option>
</select>
<select id="cms-ann-type" style="font-size:13px;padding:4px 8px;border:1px solid var(--g300);border-radius:6px;">
<option value="info">Info (blue)</option>
<option value="warning">Warning (amber)</option>
<option value="error">Error (red)</option>
<option value="success">Success (green)</option>
</select>
</div>
<textarea id="cms-ann-text" rows="2" style="font-size:13px;padding:8px;border:1px solid var(--g300);border-radius:6px;resize:vertical;" placeholder="Announcement message..."></textarea>
<div>
<button id="btn-save-announcement" class="btn-sm btn-primary">Save &amp; Publish</button>
</div>
</div>
</div>
<!-- ── CMS: Feature Flags ─────────────────────────────────────── -->
<div class="card">
<div class="card-header"><h3><i class="fas fa-toggle-on"></i> Feature Flags</h3></div>
<div style="padding:16px;display:flex;flex-direction:column;gap:10px;" id="cms-feature-flags">
<div style="display:flex;align-items:center;gap:12px;">
<label style="font-size:13px;font-weight:600;min-width:160px;">Read Aloud (TTS):</label>
<select id="cms-flag-read-aloud" style="font-size:13px;padding:4px 8px;border:1px solid var(--g300);border-radius:6px;">
<option value="true">Enabled</option>
<option value="false">Disabled</option>
</select>
</div>
<div style="display:flex;align-items:center;gap:12px;">
<label style="font-size:13px;font-weight:600;min-width:160px;">Nextcloud Integration:</label>
<select id="cms-flag-nextcloud" style="font-size:13px;padding:4px 8px;border:1px solid var(--g300);border-radius:6px;">
<option value="true">Enabled</option>
<option value="false">Disabled</option>
</select>
</div>
<div>
<button id="btn-save-flags" class="btn-sm btn-primary">Save Flags</button>
</div>
</div>
</div>
<!-- ── OIDC / SSO Configuration ──────────────────────────────── -->
<div class="card">
<div class="card-header"><h3><i class="fas fa-shield-halved"></i> Single Sign-On (OIDC)</h3></div>
<div style="padding:16px;display:flex;flex-direction:column;gap:12px;">
<p style="font-size:12px;color:var(--g500);margin:0;">Configure OpenID Connect for SSO with Azure AD, Okta, Keycloak, PocketID, Google, etc.<br>Callback URL: <code style="font-size:11px;background:var(--g100);padding:2px 6px;border-radius:4px;" id="oidc-callback-url"></code></p>
<div style="display:flex;align-items:center;gap:12px;">
<label style="font-size:13px;font-weight:600;min-width:160px;">Enable SSO:</label>
<select id="oidc-enabled" style="font-size:13px;padding:4px 8px;border:1px solid var(--g300);border-radius:6px;">
<option value="false">Disabled</option>
<option value="true">Enabled</option>
</select>
</div>
<div style="display:flex;align-items:center;gap:12px;">
<label style="font-size:13px;font-weight:600;min-width:160px;">Issuer URL:</label>
<input type="url" id="oidc-issuer" placeholder="https://id.example.com" style="flex:1;font-size:13px;padding:5px 8px;border:1px solid var(--g300);border-radius:6px;max-width:400px;">
</div>
<div style="display:flex;align-items:center;gap:12px;">
<label style="font-size:13px;font-weight:600;min-width:160px;">Client ID:</label>
<input type="text" id="oidc-client-id" placeholder="your-client-id" style="flex:1;font-size:13px;padding:5px 8px;border:1px solid var(--g300);border-radius:6px;max-width:400px;">
</div>
<div style="display:flex;align-items:center;gap:12px;">
<label style="font-size:13px;font-weight:600;min-width:160px;">Client Secret:</label>
<input type="password" id="oidc-client-secret" placeholder="Leave blank to keep current" style="flex:1;font-size:13px;padding:5px 8px;border:1px solid var(--g300);border-radius:6px;max-width:400px;">
</div>
<div style="display:flex;align-items:center;gap:12px;">
<label style="font-size:13px;font-weight:600;min-width:160px;">Button Label:</label>
<input type="text" id="oidc-button-label" placeholder="Sign in with SSO" style="flex:1;font-size:13px;padding:5px 8px;border:1px solid var(--g300);border-radius:6px;max-width:400px;">
</div>
<div style="display:flex;align-items:center;gap:12px;">
<label style="font-size:13px;font-weight:600;min-width:160px;">Disable local login:</label>
<select id="oidc-disable-local" style="font-size:13px;padding:4px 8px;border:1px solid var(--g300);border-radius:6px;">
<option value="false">No (both SSO and local login)</option>
<option value="true">Yes (SSO only)</option>
</select>
</div>
<div>
<button id="btn-save-oidc" class="btn-sm btn-primary"><i class="fas fa-floppy-disk"></i> Save OIDC Settings</button>
<span id="oidc-save-status" style="font-size:12px;margin-left:8px;color:var(--g500);"></span>
</div>
</div>
</div>
<!-- ── CMS: Email Templates ───────────────────────────────────── -->
<div class="card">
<div class="card-header"><h3><i class="fas fa-envelope"></i> Email Templates</h3></div>
<div style="padding:16px;display:flex;flex-direction:column;gap:16px;">
<!-- Email template selector -->
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
<label style="font-size:13px;font-weight:600;">Template:</label>
<select id="cms-email-template" style="font-size:13px;padding:4px 8px;border:1px solid var(--g300);border-radius:6px;">
<option value="verify">Email Verification</option>
<option value="reset">Password Reset</option>
</select>
</div>
<div>
<label style="font-size:12px;font-weight:600;color:var(--g600);margin-bottom:4px;display:block;">Subject</label>
<input type="text" id="cms-email-subject" style="width:100%;font-size:13px;padding:8px;border:1px solid var(--g300);border-radius:6px;box-sizing:border-box;" placeholder="Email subject...">
</div>
<div>
<label style="font-size:12px;font-weight:600;color:var(--g600);margin-bottom:4px;display:block;">Body (plain text — a button link will be appended automatically)</label>
<textarea id="cms-email-body" rows="4" style="width:100%;font-size:13px;padding:8px;border:1px solid var(--g300);border-radius:6px;resize:vertical;box-sizing:border-box;" placeholder="Email body text..."></textarea>
</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;">
<button id="btn-save-email" class="btn-sm btn-primary">Save Template</button>
<button id="btn-test-email" class="btn-sm btn-ghost"><i class="fas fa-paper-plane"></i> Send Test</button>
</div>
</div>
</div>
<!-- ── CMS: AI Scribe Prompts ─────────────────────────────────── -->
<div class="card">
<div class="card-header">
<h3><i class="fas fa-robot"></i> AI Scribe — plain-text / JSON instructions</h3>
<span style="font-size:12px;color:var(--g500);">Saved overrides replace shipped defaults immediately</span>
</div>
<div style="padding:16px;">
<p>Global Scribe instructions for the operations listed under each prompt. These are not Clinical Assistant prompts or Markdown formatting controls. Personal Memories and templates are separate and are not managed here.</p>
<div id="cms-scribe-prompts">Loading Scribe prompts...</div>
</div>
</div>
<!-- ── CMS: SMTP Email Server ─────────────────────────────────── -->
<div class="card">
<div class="card-header">
<h3><i class="fas fa-server"></i> SMTP Email Server</h3>
<span id="smtp-source-badge" style="font-size:11px;padding:2px 8px;border-radius:10px;background:var(--g100);color:var(--g600);">Loading...</span>
</div>
<div style="padding:16px;display:flex;flex-direction:column;gap:12px;">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
<div>
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:4px;">SMTP Host</label>
<input type="text" id="cms-smtp-host" style="width:100%;font-size:13px;padding:7px;border:1px solid var(--g300);border-radius:6px;box-sizing:border-box;" placeholder="smtp.gmail.com">
</div>
<div>
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:4px;">Port</label>
<input type="number" id="cms-smtp-port" style="width:100%;font-size:13px;padding:7px;border:1px solid var(--g300);border-radius:6px;box-sizing:border-box;" placeholder="587" value="587">
</div>
<div>
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:4px;">Username / Email</label>
<input type="text" id="cms-smtp-user" style="width:100%;font-size:13px;padding:7px;border:1px solid var(--g300);border-radius:6px;box-sizing:border-box;" placeholder="user@example.com">
</div>
<div>
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:4px;">Password / App Password</label>
<input type="password" id="cms-smtp-pass" style="width:100%;font-size:13px;padding:7px;border:1px solid var(--g300);border-radius:6px;box-sizing:border-box;" placeholder="Leave blank to keep current">
</div>
<div>
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:4px;">From Address</label>
<input type="text" id="cms-smtp-from" style="width:100%;font-size:13px;padding:7px;border:1px solid var(--g300);border-radius:6px;box-sizing:border-box;" placeholder="Pediatric Scribe &lt;noreply@example.com&gt;">
</div>
<div style="display:flex;align-items:flex-end;gap:8px;">
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;">TLS/SSL (port 465):</label>
<select id="cms-smtp-secure" style="font-size:13px;padding:7px;border:1px solid var(--g300);border-radius:6px;">
<option value="false">STARTTLS (587)</option>
<option value="true">SSL/TLS (465)</option>
</select>
</div>
</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;">
<button id="btn-save-smtp" class="btn-sm btn-primary">Save SMTP</button>
<button id="btn-clear-smtp" class="btn-sm btn-ghost"><i class="fas fa-trash"></i> Clear DB (use env)</button>
</div>
<p style="font-size:12px;color:var(--g500);margin:0;"><i class="fas fa-info-circle"></i> Environment variables take precedence over DB settings. Storing password in DB is convenient but consider using env vars for production security.</p>
</div>
</div>
<!-- ── CMS: Saved Encounters (Admin View) ─────────────────────── -->
<div class="card">
<div class="card-header">
<h3><i class="fas fa-database"></i> Saved Encounters (Site-wide)</h3>
<span style="font-size:12px;color:var(--g500);">Auto-deleted after <span id="admin-auto-delete-days">7</span> days</span>
</div>
<div style="padding:16px;display:flex;flex-direction:column;gap:10px;">
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
<label style="font-size:13px;font-weight:600;">Auto-delete after:</label>
<select id="cms-auto-delete-days" style="font-size:13px;padding:4px 8px;border:1px solid var(--g300);border-radius:6px;">
<option value="1">1 day</option>
<option value="3">3 days</option>
<option value="7" selected>7 days</option>
<option value="14">14 days</option>
<option value="30">30 days</option>
</select>
<button id="btn-save-auto-delete" class="btn-sm btn-primary">Save</button>
</div>
</div>
</div>
<!-- ── AI Model Management ─────────────────────────────────── -->
<div class="card">
<div class="card-header">
<h3><i class="fas fa-microchip"></i> AI Model Management</h3>
<span id="admin-model-provider-badge" style="font-size:11px;padding:2px 8px;border-radius:10px;background:var(--g100);color:var(--g600);">Loading...</span>
</div>
<div style="padding:16px;display:flex;flex-direction:column;gap:14px;">
<!-- Default Model -->
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
<label style="font-size:13px;font-weight:600;min-width:120px;">Default Model:</label>
<select id="admin-default-model" style="font-size:13px;padding:5px 8px;border:1px solid var(--g300);border-radius:6px;flex:1;max-width:400px;"></select>
<button id="btn-save-default-model" class="btn-sm btn-primary">Set Default</button>
</div>
<!-- Built-in Models Toggle -->
<div>
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:8px;">Built-in Models (toggle to enable/disable for users)</label>
<div id="admin-builtin-models" style="display:flex;flex-direction:column;gap:4px;max-height:300px;overflow-y:auto;padding-right:4px;">
<p style="color:var(--g400);font-size:13px;">Loading...</p>
</div>
</div>
<!-- Discover Models from API -->
<div style="border-top:1px solid var(--g100);padding-top:14px;">
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:8px;">Discover Models from Provider API</label>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;">
<input type="text" id="admin-model-search" placeholder="Search models (e.g. gemini, gpt, llama)" style="font-size:13px;padding:6px 10px;border:1px solid var(--g300);border-radius:6px;flex:1;min-width:200px;">
<button id="btn-discover-models" class="btn-sm btn-primary"><i class="fas fa-magnifying-glass"></i> Search API</button>
</div>
<div id="admin-discovered-models" style="margin-top:10px;display:flex;flex-direction:column;gap:4px;max-height:400px;overflow-y:auto;">
</div>
<p id="admin-discover-hint" style="font-size:12px;color:var(--g500);margin:8px 0 0;">Click "Search API" to query your configured provider for all available models. Use the search box to filter results.</p>
</div>
<!-- Custom Model (manual) -->
<div style="border-top:1px solid var(--g100);padding-top:14px;">
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:8px;">Add Custom Model (manual)</label>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;">
<input type="text" id="admin-custom-model-id" placeholder="Model ID (e.g. openai/gpt-4.1)" style="font-size:13px;padding:6px 10px;border:1px solid var(--g300);border-radius:6px;">
<input type="text" id="admin-custom-model-name" placeholder="Display name" style="font-size:13px;padding:6px 10px;border:1px solid var(--g300);border-radius:6px;">
<input type="text" id="admin-custom-model-cost" placeholder="Cost (e.g. ~$0.01)" style="font-size:13px;padding:6px 10px;border:1px solid var(--g300);border-radius:6px;">
<select id="admin-custom-model-cat" style="font-size:13px;padding:6px 10px;border:1px solid var(--g300);border-radius:6px;">
<option value="fast">Fast & Cheap</option>
<option value="smart" selected>Smart</option>
<option value="premium">Premium</option>
<option value="free">Free</option>
</select>
</div>
<div style="margin-top:8px;">
<button id="btn-add-custom-model" class="btn-sm btn-primary"><i class="fas fa-plus"></i> Add Model</button>
</div>
</div>
<!-- Custom Models List -->
<div id="admin-custom-models-list" style="display:flex;flex-direction:column;gap:4px;">
</div>
</div>
</div>
<!-- ── Clinical Assistant ─────────────────────────────────────── -->
<div class="card">
<div class="card-header">
<h3><i class="fas fa-brain"></i> Clinical Assistant</h3>
<span style="font-size:12px;color:var(--g500);">Chat and image model overrides for the assistant</span>
</div>
<div style="padding:16px;display:flex;flex-direction:column;gap:14px;">
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
<label style="font-size:13px;font-weight:600;min-width:130px;">Chat model:</label>
<select id="assistant-chat-model" style="font-size:13px;padding:5px 8px;border:1px solid var(--g300);border-radius:6px;flex:1;max-width:420px;"></select>
<button id="btn-test-assistant-chat-model" class="btn-sm btn-primary" type="button"><i class="fas fa-vial"></i> Test</button>
</div>
<div id="assistant-chat-test-result" style="font-size:12px;color:var(--g500);"></div>
<div style="border:1px solid var(--g100);border-radius:8px;padding:10px;display:flex;gap:10px;align-items:center;justify-content:space-between;flex-wrap:wrap;">
<div>
<div style="font-size:13px;font-weight:600;color:var(--g700);">Starter prompt pool — suggested questions, not global prompt history</div>
<div id="assistant-prompt-pool-status" style="font-size:12px;color:var(--g500);margin-top:3px;">Checking prompt pool...</div>
</div>
<button id="btn-regenerate-assistant-prompt-pool" class="btn-sm btn-ghost" type="button"><i class="fas fa-rotate"></i> Regenerate Pool</button>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;width:100%;">
<select id="assistant-prompt-pool-snapshots" style="font-size:12px;padding:5px 8px;border:1px solid var(--g300);border-radius:6px;flex:1;min-width:220px;"><option value="">Loading snapshots...</option></select>
<button id="btn-restore-assistant-prompt-pool" class="btn-sm btn-ghost" type="button"><i class="fas fa-clock-rotate-left"></i> Restore Snapshot</button>
</div>
</div>
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
<label style="font-size:13px;font-weight:600;min-width:130px;">Image model:</label>
<select id="assistant-image-model" style="font-size:13px;padding:5px 8px;border:1px solid var(--g300);border-radius:6px;flex:1;max-width:420px;"></select>
<button id="btn-refresh-assistant-image-models" class="btn-sm btn-ghost" type="button"><i class="fas fa-rotate"></i> Refresh</button>
<button id="btn-test-assistant-image-model" class="btn-sm btn-primary" type="button"><i class="fas fa-image"></i> Test</button>
</div>
<div id="assistant-image-test-result" style="font-size:12px;color:var(--g500);"></div>
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:-6px;">
<label style="font-size:12px;font-weight:600;color:var(--g600);min-width:130px;">Custom image model</label>
<input id="assistant-custom-image-model" type="text" placeholder="e.g. openrouter-gpt-5-image" style="font-size:13px;padding:5px 8px;border:1px solid var(--g300);border-radius:6px;flex:1;max-width:420px;">
<button id="btn-use-custom-assistant-image-model" class="btn-sm btn-ghost" type="button"><i class="fas fa-plus"></i> Use Custom</button>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
<div>
<label for="assistant-search-limit" style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:4px;">Retrieval result limit (count, not characters)</label>
<input id="assistant-search-limit" type="number" min="3" max="20" value="8" style="width:100%;font-size:13px;padding:6px 10px;border:1px solid var(--g300);border-radius:6px;">
</div>
<div>
<label for="assistant-context-chars" style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:4px;">Requested surrounding context per retrieval excerpt (characters)</label>
<input id="assistant-context-chars" type="number" min="300" max="4000" value="1400" style="width:100%;font-size:13px;padding:6px 10px;border:1px solid var(--g300);border-radius:6px;">
</div>
</div>
<p class="assistant-muted">The result limit also bounds selected sources; visual queries separately request up to 8 multimodal candidates before selection. Excerpt size is not the conversation budget.</p>
<div>
<strong>Conversation input budget — read-only server configuration</strong>
<p id="assistant-conversation-budget" role="status">Loading server budget metadata...</p>
<p id="assistant-conversation-help" class="assistant-muted">Set by CLINICAL_ASSISTANT_CONVERSATION_CHARS, or the server default when unset; legacy saved settings are ignored. Counts all prior user/assistant text plus the new question using JavaScript string length (UTF-16 code units), not provider tokens. Over-budget requests stop before inference; no automatic clipping or summarization.</p>
<p class="assistant-muted">Text answer output limits: 2,600 tokens initially and 5,000 for completion retries. Image input is separate: existing image routes trim and clip the user prompt at 5,000 UTF-16 code units before adding poster/layout instructions. This is not a new image budget or output-token limit.</p>
</div>
<section aria-labelledby="assistant-text-prompts-heading">
<h4 id="assistant-text-prompts-heading">Clinical Assistant TEXT — system behavior</h4>
<p>Overrides the default behavior for retrieved text answers (including streaming). Fixed greeting/no-source replies, search rewrites and explicit handoff instructions are separate, not controlled by this editor.</p>
<p><strong>Fixed citation safeguards (read-only):</strong> Use only retrieved evidence for factual claims and keep the exact provided source numbers. Do not invent, renumber, merge or move citations. Runtime evidence/citation safeguards are appended separately; this editor does not replace them.</p>
<div id="cms-clinical-text-prompts">Loading clinical text prompt...</div>
</section>
<section aria-labelledby="assistant-image-prompts-heading">
<h4 id="assistant-image-prompts-heading">Clinical Assistant IMAGE — poster instructions</h4>
<p>Overrides the default poster instruction appended to the image user prompt by both direct and background-job image generation. Existing automatic portrait/landscape layout suffixes still follow it. This does not change text answers or image-job storage.</p>
<div id="cms-clinical-image-prompts">Loading clinical image prompt...</div>
</section>
<div>
<button id="btn-save-assistant-config" class="btn-sm btn-primary" disabled><i class="fas fa-floppy-disk"></i> Save Model &amp; Retrieval Settings</button>
<button id="btn-retry-assistant-config" class="btn-sm btn-ghost" type="button" hidden>Retry loading settings</button>
<span id="assistant-admin-status" role="status" style="font-size:12px;color:var(--g500);margin-left:8px;"></span>
</div>
</div>
</div>
<!-- ── TTS Model Management ───────────────────────────────────── -->
<div class="card">
<div class="card-header">
<h3><i class="fas fa-volume-up"></i> Text-to-Speech (TTS)</h3>
<span id="admin-tts-provider-badge" style="font-size:11px;padding:2px 8px;border-radius:10px;background:var(--g100);color:var(--g600);">Loading...</span>
</div>
<div style="padding:16px;display:flex;flex-direction:column;gap:14px;">
<div id="admin-tts-info" style="font-size:12px;color:var(--g500);">Loading...</div>
<!-- Search voices from provider -->
<div>
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:8px;">Search Voices / Models from Provider</label>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;">
<input type="text" id="admin-tts-search" placeholder="Filter voices (e.g. Journey, Neural, alloy)" style="font-size:13px;padding:6px 10px;border:1px solid var(--g300);border-radius:6px;flex:1;min-width:180px;">
<button id="btn-discover-tts" class="btn-sm btn-primary"><i class="fas fa-magnifying-glass"></i> Search</button>
</div>
<div id="admin-tts-discovered" style="margin-top:8px;display:flex;flex-direction:column;gap:4px;max-height:300px;overflow-y:auto;"></div>
<p id="admin-tts-discover-hint" style="font-size:12px;color:var(--g500);margin:6px 0 0;">Click Search to query available voices from your configured TTS provider.</p>
</div>
<!-- Test TTS -->
<div style="border-top:1px solid var(--g100);padding-top:12px;">
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:8px;">Test Synthesis</label>
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:8px;">
<label style="font-size:13px;font-weight:600;min-width:80px;">Voice:</label>
<select id="admin-tts-voice" style="font-size:13px;padding:5px 8px;border:1px solid var(--g300);border-radius:6px;flex:1;max-width:280px;"></select>
</div>
<textarea id="admin-tts-test-text" rows="2" style="width:100%;font-size:13px;padding:8px;border:1px solid var(--g300);border-radius:6px;resize:vertical;box-sizing:border-box;" placeholder="Text to synthesize...">Hello, this is a text to speech test for the Pediatric AI Scribe.</textarea>
<div style="margin-top:8px;display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
<button id="btn-test-tts" class="btn-sm btn-primary"><i class="fas fa-play"></i> Synthesize &amp; Play</button>
<audio id="admin-tts-audio" controls style="display:none;height:32px;flex:1;min-width:180px;"></audio>
</div>
<p id="admin-tts-result" style="font-size:12px;color:var(--g500);margin:6px 0 0;"></p>
</div>
</div>
</div>
<!-- ── STT Model Management ───────────────────────────────────── -->
<div class="card">
<div class="card-header">
<h3><i class="fas fa-microphone"></i> Speech-to-Text (STT)</h3>
<span id="admin-stt-provider-badge" style="font-size:11px;padding:2px 8px;border-radius:10px;background:var(--g100);color:var(--g600);">Loading...</span>
</div>
<div style="padding:16px;display:flex;flex-direction:column;gap:14px;">
<div id="admin-stt-info" style="font-size:12px;color:var(--g500);">Loading...</div>
<!-- Search models from provider -->
<div>
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:8px;">Search Models from Provider</label>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;">
<input type="text" id="admin-stt-search" placeholder="Filter models (e.g. gemini, whisper)" style="font-size:13px;padding:6px 10px;border:1px solid var(--g300);border-radius:6px;flex:1;min-width:180px;">
<button id="btn-discover-stt" class="btn-sm btn-primary"><i class="fas fa-magnifying-glass"></i> Search</button>
</div>
<div id="admin-stt-discovered" style="margin-top:8px;display:flex;flex-direction:column;gap:4px;max-height:300px;overflow-y:auto;"></div>
<p id="admin-stt-discover-hint" style="font-size:12px;color:var(--g500);margin:6px 0 0;">Click Search to query available STT models from your configured provider.</p>
</div>
<!-- Test STT -->
<div style="border-top:1px solid var(--g100);padding-top:12px;">
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:8px;">Test STT — Record &amp; Transcribe</label>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
<button id="btn-stt-record" class="btn-sm btn-primary"><i class="fas fa-microphone"></i> Start Recording</button>
<span id="admin-stt-recording-status" style="font-size:12px;color:var(--g500);"></span>
</div>
<div id="admin-stt-result" style="margin-top:10px;padding:10px;background:var(--g50);border-radius:6px;font-size:13px;color:var(--g700);display:none;">
<strong>Transcription:</strong> <span id="admin-stt-text"></span>
<div id="admin-stt-meta" style="font-size:11px;color:var(--g400);margin-top:4px;"></div>
</div>
</div>
</div>
</div>
<!-- ── Embedding Models ───────────────────────────────────────── -->
<div class="card">
<div class="card-header">
<h3><i class="fas fa-network-wired"></i> Embedding Models</h3>
<span id="admin-embed-provider-badge" style="font-size:11px;padding:2px 8px;border-radius:10px;background:var(--g100);color:var(--g600);">Loading...</span>
</div>
<div style="padding:16px;display:flex;flex-direction:column;gap:14px;">
<div id="admin-embed-info" style="font-size:12px;color:var(--g500);">Loading...</div>
<!-- Search embedding models from provider -->
<div>
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:8px;">Search Models from Provider</label>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;">
<input type="text" id="admin-embed-search" placeholder="Filter models (e.g. embedding, vertex)" style="font-size:13px;padding:6px 10px;border:1px solid var(--g300);border-radius:6px;flex:1;min-width:180px;">
<button id="btn-discover-embeddings" class="btn-sm btn-primary"><i class="fas fa-magnifying-glass"></i> Search</button>
</div>
<div id="admin-embed-discovered" style="margin-top:8px;display:flex;flex-direction:column;gap:4px;max-height:300px;overflow-y:auto;"></div>
<p id="admin-embed-discover-hint" style="font-size:12px;color:var(--g500);margin:6px 0 0;">Click Search to query available embedding models from your configured provider.</p>
</div>
<!-- Test Embedding -->
<div style="border-top:1px solid var(--g100);padding-top:12px;">
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:8px;">Test Embedding</label>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;">
<input type="text" id="admin-embed-test-text" value="Pediatric patient with fever and cough" style="font-size:13px;padding:6px 10px;border:1px solid var(--g300);border-radius:6px;flex:1;min-width:200px;" placeholder="Sample text to embed...">
<button id="btn-test-embedding" class="btn-sm btn-primary"><i class="fas fa-code-branch"></i> Generate</button>
</div>
<div id="admin-embed-result" style="margin-top:8px;font-size:12px;color:var(--g500);"></div>
</div>
</div>
</div>
<!-- ── CMS: Reset to Defaults ─────────────────────────────────── -->
<div class="card" style="border:1px solid var(--red-light);">
<div class="card-header"><h3 style="color:var(--red);"><i class="fas fa-triangle-exclamation"></i> Reset Settings</h3></div>
<div style="padding:16px;display:flex;flex-direction:column;gap:10px;">
<p style="font-size:13px;color:var(--g600);margin:0;">Reset all announcement, feature flag, and email settings back to factory defaults. SMTP configuration and custom models are preserved.</p>
<div>
<button id="btn-reset-all-defaults" class="btn-sm" style="background:var(--red);color:white;border:none;border-radius:6px;padding:6px 14px;font-size:13px;cursor:pointer;"><i class="fas fa-rotate-left"></i> Reset All to Defaults</button>
</div>
</div>
</div>