Files
rx.scoreodonto.com/dental-server/public/index.html
T
VPS 4 Deploy Agent 610c183b35
continuous-integration/webhook Deploy concluído (VPS4)
feat(deploy): add GitOps automation - listener 9003, update-version.js, deploy-prod.sh GitOps
2026-05-30 15:32:11 +02:00

600 lines
36 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>Gerenciador de Imagens Dentais</title>
<meta name="description" content="Sistema de gerenciamento de imagens de raio-X dental">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Outfit:wght@300;400;500;600;700&display=swap">
<link rel="stylesheet" href="/style.css">
</head>
<body>
<!-- Overlay Global de Alerta Wasabi -->
<div id="wasabi-alert-banner" class="wasabi-alert-banner" style="display: none;">
<span class="wasabi-alert-icon">⚠️</span>
<div class="wasabi-alert-content">
<strong>Atenção:</strong> Falha de autenticação com o Wasabi S3 (<span id="wasabi-alert-error">Erro 403</span>). As imagens não poderão ser salvas ou visualizadas. Por favor, verifique suas configurações de armazenamento imediatamente.
</div>
<button class="wasabi-alert-btn" onclick="openSettingsTab()">Configurar Agora</button>
</div>
<!-- App Layout Container -->
<div class="app-container">
<!-- Sidebar -->
<aside class="sidebar">
<div class="sidebar-header">
<div class="logo-icon">
<i class="fa-solid fa-tooth"></i>
</div>
<h2>RF Dental <span style="font-size: 10px; opacity: 0.5; font-weight: 500; margin-left: 4px; vertical-align: middle;">v2.1.1</span></h2>
</div>
<nav class="sidebar-nav">
<div class="nav-section-title">Menu Principal</div>
<a href="/" class="nav-item active" id="menu-dashboard" onclick="if(window.location.pathname === '/') { showPatientsView(); return false; }">
<i class="fa-solid fa-images"></i> Galeria
</a>
<a href="#" class="nav-item" id="menu-new-patient" onclick="openCreatePatientModal(); return false;">
<i class="fa-solid fa-user-plus"></i> Novo Paciente
</a>
<a href="#" class="nav-item" id="menu-upload" onclick="openSyncModal('upload'); return false;">
<i class="fa-solid fa-upload"></i> Enviar Imagens
</a>
<a href="#" class="nav-item" id="menu-settings" onclick="openSettingsModal(); return false;">
<i class="fa-solid fa-user-gear"></i> Minha Conta
</a>
<div class="admin-only">
<div class="nav-section-title">Área Administrativa</div>
<a href="/clients" class="nav-item" id="menu-clients">
<i class="fa-solid fa-network-wired"></i> Conexões
</a>
<a href="/admin-clinics" class="nav-item" id="menu-clinics">
<i class="fa-solid fa-hospital"></i> Clínicas e Acessos
</a>
<a href="#" class="nav-item" id="menu-users" onclick="openUserManagementModal(); return false;">
<i class="fa-solid fa-users"></i> Gerenciar Usuários
</a>
<a href="#" class="nav-item" id="menu-sync" onclick="openSyncModal('devices'); return false;">
<i class="fa-solid fa-rotate"></i> Dispositivos / Sync
</a>
<a href="/download.html" class="nav-item" id="menu-download-client" style="color: #0284c7;">
<i class="fa-solid fa-download"></i> Baixar Cliente (.exe)
</a>
<a href="#" class="nav-item" id="menu-plugins" onclick="openPluginsModal(); return false;">
<i class="fa-solid fa-plug"></i> Plugins / Wasabi
</a>
<a href="/reset" class="nav-item" id="menu-reset" style="color: #dc2626;">
<i class="fa-solid fa-triangle-exclamation"></i> Zerar Sistema
</a>
</div>
</nav>
<div class="sidebar-footer">
<button id="logoutBtn" class="logout-btn" onclick="logout(); return false;">
<i class="fa-solid fa-arrow-right-from-bracket"></i> Sair
</button>
</div>
</aside>
<!-- Main Content -->
<main class="main-content">
<!-- Header -->
<header class="header">
<div class="header-content">
<div class="header-left">
<button id="backBtn" class="btn btn-back" style="display:none;">
← Voltar
</button>
<h1 id="mainTitle">Galeria de Imagens</h1>
</div>
<div class="header-actions">
<!-- Filtro de Clientes -->
<select id="clientFilter" class="client-filter" style="padding: 10px 16px; border-radius: 10px; border: 1px solid var(--border-color); background: var(--bg-surface); color: var(--text-primary); font-size: 0.95rem; font-weight: 500; outline: none; min-width: 180px; cursor: pointer; height: 42px;">
<option value="">📋 Todos os Clientes</option>
</select>
<!-- Mostrar Ocultas -->
<button id="toggleDisabledBtn" class="btn btn-secondary" style="padding: 10px 16px; border-radius: 10px; font-weight: 500; font-size: 0.95rem; display: flex; align-items: center; gap: 6px; cursor: pointer; border: 1px solid var(--border-color); background: var(--bg-surface); color: var(--text-primary); height: 42px;">
👁️ <span id="toggleText">Ocultas</span>
</button>
<input
type="search"
id="searchInput"
class="search-input"
placeholder="🔍 Pesquisar paciente..."
style="height: 42px; min-width: 200px;"
>
<button class="btn btn-secondary" onclick="loadClientsList()" style="height: 42px; display: flex; align-items: center; justify-content: center; padding: 10px 16px;">
<span class="icon">🔄</span>
</button>
</div>
</div>
<!-- Patient sub-header (shown in patient-images view) -->
<div id="patientHeader" class="patient-header" style="display:none;">
<div class="patient-header-info">
<div id="patientTitle" class="patient-header-name"></div>
<div id="patientMeta" class="patient-header-meta"></div>
</div>
</div>
</header>
<!-- Scrollable content area -->
<div class="content-scroll">
<!-- Loading State -->
<div id="loadingState" class="loading">
<div class="spinner"></div>
<p>Carregando...</p>
</div>
<!-- Empty State -->
<div id="emptyState" class="empty-state" style="display: none;">
<div class="empty-icon">📭</div>
<h2>Nenhum resultado encontrado</h2>
<p>As imagens aparecerão aqui quando forem enviadas pelo cliente Windows</p>
</div>
<!-- Images / Patients Grid -->
<div id="imagesGrid" class="images-grid"> </div>
</div><!-- end .content-scroll -->
</main>
</div>
<!-- Transform Modal -->
<div id="transformModal" class="modal">
<div class="modal-content modal-large">
<div class="modal-header">
<div class="modal-title-group">
<h2 id="transformModalTitle"><!-- Dynamic Title --></h2>
<div class="modal-actions-group">
<button id="btnShowTransform" class="btn btn-primary" onclick="toggleModalView('transform')" title="Ajustar Imagem">🔄</button>
<button id="btnShowGto" class="btn btn-secondary" onclick="toggleModalView('gto')" title="Gerenciar GTOs">📋</button>
<button id="btnShowInfo" class="btn btn-secondary" onclick="toggleModalInfo()" title="Informações"></button>
<button id="btnResetImage" class="btn btn-danger" onclick="resetImageToOriginal()" title="Resetar para Original">↩️</button>
</div>
</div>
<span class="modal-close" id="closeTransformModal">&times;</span>
</div>
<div class="modal-body">
<!-- Bloco oculto de informações -->
<div id="transformExtraInfo" class="extra-info-panel" style="display: none;">
<!-- Preenchido via JS -->
</div>
<!-- View 1: Transforms -->
<div id="transformViewWrapper" class="transform-view-wrapper">
<div id="newTransformLayout" class="new-transform-layout">
<!-- Painel Original -->
<div class="transform-panel panel-original">
<h3 class="panel-title">Original</h3>
<div class="img-container">
<img id="originalImgPreview" src="" alt="Original">
</div>
</div>
<!-- Painel Preview -->
<div class="transform-panel panel-preview">
<h3 class="panel-title">Preview</h3>
<div class="img-container">
<img id="transformedImgPreview" src="" alt="Transformed">
</div>
</div>
<!-- Barra de ferramentas lateral -->
<div class="transform-sidebar">
<h3 class="sidebar-title">Ajuste</h3>
<div class="sidebar-buttons" id="sidebarTransformButtons" style="display: flex; flex-direction: column; gap: 8px; align-items: center; width: 100%;">
<button type="button" class="sidebar-btn" onclick="adjustFineTune(-5)" title="Rotacionar -5°" style="margin-bottom: 4px;">
<span class="icon"></span>
<span class="label">-5°</span>
</button>
<button type="button" class="sidebar-btn" onclick="adjustFineTune(-1)" title="Rotacionar -1°" style="margin-bottom: 4px;">
<span class="icon"></span>
<span class="label">-1°</span>
</button>
<div id="fineTuneValDisplay" class="fine-tune-display" style="min-width: 40px; padding: 6px; font-size: 1rem; margin: 8px 0;"></div>
<button type="button" class="sidebar-btn" onclick="adjustFineTune(1)" title="Rotacionar +1°" style="margin-top: 4px;">
<span class="icon"></span>
<span class="label">+1°</span>
</button>
<button type="button" class="sidebar-btn" onclick="adjustFineTune(5)" title="Rotacionar +5°" style="margin-top: 4px;">
<span class="icon"></span>
<span class="label">+5°</span>
</button>
<button type="button" class="btn btn-danger btn-small" id="btnResetFineTune" onclick="resetFineTune()" style="display: none; margin-top: 12px; width: 100%;" title="Resetar ajuste">Reset</button>
</div>
</div>
</div>
<div id="transformActionArea" class="transform-action-area" style="display: none;">
<!-- Apenas a observação e os botões de salvar ficaram na área inferior -->
<div class="form-group">
<label>Nova Observação / Detalhes (Opcional):</label>
<textarea id="newImageRemark" class="form-control" rows="2" placeholder="Ex: Raio-X Dente 45..."></textarea>
</div>
<div class="form-actions-inline">
<button class="btn btn-secondary" onclick="clearTransformSelection()">Cancelar</button>
<button id="btnSaveTransform" class="btn btn-primary" onclick="saveSelectedTransform()">Salvar Nova Imagem</button>
</div>
</div>
</div>
<!-- View 2: GTO List & Link -->
<div id="gtoView" style="display: none;">
<div class="gto-create-section">
<h3 class="section-title">Cadastrar Nova GTO</h3>
<div class="form-actions-inline">
<input type="text" id="inlineGtoNumber" class="form-control" placeholder="Número/ID da GTO" style="flex: 1;">
<input type="text" id="inlineGtoDesc" class="form-control" placeholder="Descrição (Opcional)" style="flex: 2;">
<button class="btn btn-primary" onclick="inlineCreateGto()">Cadastrar</button>
</div>
</div>
<div class="gto-link-section">
<h3 class="section-title">Vincular esta imagem a uma GTO:</h3>
<button class="btn btn-success" onclick="linkImageToGto()" id="btnLinkGto">✅ Vincular Selecionada</button>
</div>
<div id="gtoListContainer" class="gto-list-container">
<!-- GTO list goes here -->
</div>
</div>
</div>
</div>
</div>
<!-- Modal Novo Paciente -->
<div class="modal" id="create-patient-modal">
<div class="modal-content">
<div class="modal-header">
<h2>Novo Paciente</h2>
<div class="modal-close">&times;</div>
</div>
<div class="modal-body">
<form id="create-patient-form" onsubmit="submitCreatePatient(event)">
<div class="form-group">
<label>Nome *</label>
<input type="text" id="patient-first-name" required placeholder="Ex: João">
</div>
<div class="form-group">
<label>Sobrenome *</label>
<input type="text" id="patient-last-name" required placeholder="Ex: Silva">
</div>
<div class="form-group">
<label>Dentista</label>
<input type="text" id="patient-doctor" placeholder="Nome do dentista responsável">
</div>
<div style="display: flex; gap: 12px;">
<div class="form-group" style="flex: 1;">
<label>Data de Nascimento</label>
<input type="date" id="patient-birthday">
</div>
<div class="form-group" style="flex: 1;">
<label>Gênero</label>
<select id="patient-gender">
<option value="0">Masculino</option>
<option value="1">Feminino</option>
</select>
</div>
</div>
<div class="form-group">
<label>Telefone</label>
<input type="text" id="patient-phone" placeholder="(11) 99999-9999">
</div>
<div class="form-group">
<label>Observações</label>
<textarea id="patient-remark" rows="3" placeholder="Informações adicionais..."></textarea>
</div>
<button type="submit" class="btn btn-primary" style="width: 100%; margin-top: 10px;">
Salvar Paciente
</button>
</form>
</div>
</div>
</div>
<!-- Modal Nova GTO -->
<div class="modal" id="create-gto-modal">
<div class="modal-content">
<div class="modal-header">
<h2>Nova GTO</h2>
<div class="modal-close">&times;</div>
</div>
<div class="modal-body">
<form id="create-gto-form" onsubmit="submitCreateGto(event)">
<div class="form-group">
<label>Número/ID da GTO *</label>
<input type="text" id="gto-number" required placeholder="Ex: GTO-2023-001">
</div>
<div class="form-group">
<label>Descrição (opcional)</label>
<textarea id="gto-description" rows="3" placeholder="Informações da guia..."></textarea>
</div>
<button type="submit" class="btn btn-primary" style="width: 100%; margin-top: 10px;">
Criar GTO
</button>
</form>
</div>
</div>
</div>
<!-- Toast de Notificação -->
<div class="toast" id="toast">Operação realizada com sucesso!</div>
<!-- Settings Modal -->
<div id="settingsModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2>⚙️ Configurações da Conta</h2>
<span class="modal-close">&times;</span>
</div>
<div class="modal-body">
<form id="settingsForm" onsubmit="updateCredentials(event)">
<div class="form-group">
<label for="currentPassword">Senha Atual (Obrigatório)*</label>
<input type="password" id="currentPassword" class="form-control" placeholder="Digite sua senha atual para autorizar" required>
</div>
<hr style="margin: 20px 0; border: none; border-top: 1px solid #eee;">
<p style="margin-bottom: 15px; color: #666; font-size: 0.9rem;">Preencha apenas os campos que deseja alterar:</p>
<div class="form-group">
<label for="newUsername">Novo Nome de Usuário</label>
<input type="text" id="newUsername" class="form-control" placeholder="Deixe em branco para não alterar">
</div>
<div class="form-group" style="margin-top: 15px;">
<label for="newEmail">Novo E-mail de Acesso</label>
<input type="email" id="newEmail" class="form-control" placeholder="Deixe em branco para não alterar">
</div>
<div class="form-group" style="margin-top: 15px;">
<label for="newPassword">Nova Senha</label>
<input type="password" id="newPassword" class="form-control" placeholder="Deixe em branco para não alterar">
</div>
<div class="form-actions" style="margin-top: 25px; display: flex; justify-content: flex-end; gap: 10px;">
<button type="button" class="btn btn-secondary" onclick="closeSettingsModal()">Cancelar</button>
<button type="submit" class="btn btn-primary" id="btnSaveSettings">Salvar Alterações</button>
</div>
</form>
</div>
</div>
</div>
<!-- User Management Modal (Admin Only) -->
<div id="userManagementModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2>👥 Gerenciamento de Usuários</h2>
<span class="modal-close">&times;</span>
</div>
<div class="modal-body" style="display: flex; gap: 24px; flex-direction: row; flex-wrap: wrap; align-items: flex-start;">
<!-- Formulário de Cadastro (Esquerda) -->
<div style="flex: 1; min-width: 260px; background: rgba(0,0,0,0.02); padding: 20px; border-radius: 10px; border: 1px solid var(--border-color);">
<h3 style="margin: 0 0 16px; font-size: 1.05rem; font-weight: 700; color: var(--text-primary);">Cadastrar Novo Usuário</h3>
<form id="createUserForm" onsubmit="createUser(event)">
<div class="form-group" style="margin-bottom: 14px;">
<label style="display:block; margin-bottom:5px; font-weight:600; font-size:0.85rem; color:var(--text-secondary);">NOME DE USUÁRIO *</label>
<input type="text" id="new_username" required placeholder="Ex: dentista.ana" style="width: 100%; padding: 10px 12px; border: 1px solid var(--border-color); border-radius: 8px; outline:none; background:var(--bg-color); color:var(--text-primary); font-size:0.95rem; box-sizing:border-box;">
</div>
<div class="form-group" style="margin-bottom: 14px;">
<label style="display:block; margin-bottom:5px; font-weight:600; font-size:0.85rem; color:var(--text-secondary);">E-MAIL *</label>
<input type="email" id="new_email" required placeholder="ana@consultorio.com" style="width: 100%; padding: 10px 12px; border: 1px solid var(--border-color); border-radius: 8px; outline:none; background:var(--bg-color); color:var(--text-primary); font-size:0.95rem; box-sizing:border-box;">
</div>
<div class="form-group" style="margin-bottom: 14px;">
<label style="display:block; margin-bottom:5px; font-weight:600; font-size:0.85rem; color:var(--text-secondary);">SENHA *</label>
<input type="password" id="new_password" required placeholder="Senha de acesso" style="width: 100%; padding: 10px 12px; border: 1px solid var(--border-color); border-radius: 8px; outline:none; background:var(--bg-color); color:var(--text-primary); font-size:0.95rem; box-sizing:border-box;">
</div>
<div class="form-group" style="margin-bottom: 14px; display: flex; align-items: center; gap: 8px;">
<input type="checkbox" id="new_is_admin" style="width: 16px; height: 16px; cursor: pointer; margin: 0; flex-shrink: 0;">
<label for="new_is_admin" style="font-weight:600; font-size:0.9rem; color:var(--text-primary); cursor: pointer; user-select: none; margin: 0;">Permissão de Administrador</label>
</div>
<button type="submit" class="btn btn-primary" style="width: 100%; margin-top: 8px; padding: 10px; border-radius: 8px; font-weight: 600; background:var(--primary-color); color:white; border:none; cursor:pointer; font-size:0.95rem;">
<i class="fa-solid fa-user-plus"></i> Cadastrar Usuário
</button>
</form>
</div>
<!-- Lista de Usuários (Direita) -->
<div style="flex: 2; min-width: 300px; display:flex; flex-direction:column;">
<h3 style="margin: 0 0 16px; font-size: 1.05rem; font-weight: 700; color: var(--text-primary);">Usuários Cadastrados</h3>
<div style="flex: 1; overflow-y: auto; border: 1px solid var(--border-color); border-radius: 10px; background:var(--bg-surface);">
<table style="width: 100%; border-collapse: collapse; font-size: 0.9rem; text-align: left;">
<thead>
<tr style="background: rgba(0,0,0,0.04); border-bottom: 1px solid var(--border-color); color:var(--text-secondary); font-size:0.78rem; text-transform:uppercase; letter-spacing:0.05em; font-weight:700;">
<th style="padding: 10px 14px;">Usuário</th>
<th style="padding: 10px 14px;">E-mail</th>
<th style="padding: 10px 14px;">Função</th>
<th style="padding: 10px 14px; text-align: center;">Ações</th>
</tr>
</thead>
<tbody id="usersListBody">
<!-- Preenchido via JS -->
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Plugins / Wasabi Modal -->
<div id="pluginsModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2>🔌 Configuração do Wasabi Storage</h2>
<span class="modal-close">&times;</span>
</div>
<div class="modal-body">
<form id="pluginsForm" onsubmit="updatePluginsConfig(event)">
<div style="background: #f8f9fa; padding: 12px 18px; border-radius: 8px; border: 1px solid #eee; margin-bottom: 20px; font-size: 0.95rem; color: #555;">
Configure as credenciais e bucket do **Wasabi S3** para habilitar o armazenamento na nuvem. Se desativado, o sistema usará o armazenamento local do servidor de forma automática.
</div>
<div class="form-group">
<label for="wasabiAccessKey">Access Key *</label>
<input type="text" id="wasabiAccessKey" placeholder="Ex: AIPL5M4G..." required>
</div>
<div class="form-group">
<label for="wasabiSecretKey">Secret Key *</label>
<input type="password" id="wasabiSecretKey" placeholder="Sua Secret Key" required>
</div>
<div class="form-group">
<label for="wasabiBucket">Bucket Ativo *</label>
<input type="text" id="wasabiBucket" placeholder="Ex: meu-bucket-rx" required>
</div>
<div style="display: flex; gap: 12px;">
<div class="form-group" style="flex: 1;">
<label for="wasabiRegion">Região</label>
<input type="text" id="wasabiRegion" placeholder="Ex: us-east-1 (padrão)">
</div>
<div class="form-group" style="flex: 1;">
<label for="wasabiEndpoint">Endpoint</label>
<input type="text" id="wasabiEndpoint" placeholder="Ex: s3.wasabisys.com">
</div>
</div>
<div id="pluginsStatusAlert" style="display: none; padding: 10px; border-radius: 6px; font-size: 0.9rem; margin-top: 15px;"></div>
<div class="form-actions" style="margin-top: 25px; display: flex; justify-content: flex-end; gap: 10px; border-top: 1px solid #eee; padding-top: 15px;">
<button type="button" class="btn btn-secondary" onclick="closePluginsModal()">Cancelar</button>
<button type="submit" class="btn btn-primary" id="btnSavePlugins">Salvar Configurações</button>
</div>
</form>
</div>
</div>
</div>
<!-- Sync / Upload Modal -->
<div id="syncModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2>🔄 Sincronização e Envio de Imagens</h2>
<span class="modal-close">&times;</span>
</div>
<div class="modal-body">
<div class="sync-tabs">
<button type="button" id="tabBtnDevices" class="sync-tab-btn admin-only active" onclick="switchSyncTab('devices')">💻 Dispositivos Conectados</button>
<button type="button" id="tabBtnUpload" class="sync-tab-btn" onclick="switchSyncTab('upload')">📤 Envio Manual de Imagens</button>
</div>
<!-- Tab 1: Dispositivos -->
<div id="tab-devices" class="sync-tab-content active">
<div style="background: #f8f9fa; padding: 15px; border-radius: 8px; border: 1px solid #eee; margin-bottom: 20px; font-size: 0.95rem; color: #555; line-height: 1.5;">
Os computadores clientes com Windows instalados e ativos enviam imagens em tempo real. Você pode solicitar que eles façam uma sincronização manual forçada com o servidor enviando um sinal.
</div>
<div id="syncDevicesLoading" class="loading" style="padding: 20px 0;">
<div class="spinner" style="width: 30px; height: 30px; margin-bottom: 10px;"></div>
<p>Buscando dispositivos online...</p>
</div>
<div id="syncDevicesEmpty" style="display: none; text-align: center; padding: 30px 20px; color: #666;">
<span style="font-size: 2.5rem; display: block; margin-bottom: 10px;">📭</span>
<p>Nenhum dispositivo Windows conectado no momento.</p>
</div>
<div id="syncDeviceList" class="sync-device-list" style="display: none;">
<!-- Preenchido via Javascript -->
</div>
<div style="margin-top: 25px; border-top: 1px solid #eee; padding-top: 15px; display: flex; justify-content: flex-end; gap: 10px;">
<button type="button" class="btn btn-secondary" onclick="closeSyncModal()">Fechar</button>
<button type="button" class="btn btn-primary" onclick="triggerSyncFromModal()" id="btnTriggerSyncModal">
<span class="icon">🔄</span> Enviar Sinal de Sincronização
</button>
</div>
</div>
<!-- Tab 2: Envio Manual -->
<div id="tab-upload" class="sync-tab-content">
<form id="manualUploadForm" onsubmit="handleManualUploadSubmit(event)">
<div class="form-group">
<label for="uploadPatientSelect">Paciente *</label>
<div style="display: flex; gap: 10px;">
<select id="uploadPatientSelect" onchange="toggleNewPatientFields()" required style="flex: 1;">
<option value="">-- Selecione o Paciente --</option>
<option value="NEW_PATIENT"> Novo Paciente...</option>
</select>
<button type="button" class="btn btn-secondary" onclick="refreshUploadPatients()" title="Atualizar Lista" style="width: 44px; padding: 0; display: flex; align-items: center; justify-content: center;">
🔄
</button>
</div>
</div>
<!-- Campos para Novo Paciente (ocultos por padrão) -->
<div id="newPatientFields" style="display: none; background: #f8f9fa; padding: 15px; border-radius: 8px; border: 1px solid #eee; margin-bottom: 18px; animation: slideDown 0.2s ease;">
<h4 style="margin: 0 0 12px 0; color: var(--dark-color); font-size: 0.95rem;">Dados do Novo Paciente</h4>
<div style="display: flex; gap: 12px; margin-bottom: 12px;">
<div style="flex: 1;">
<label style="font-size: 0.82rem; font-weight: 600; display: block; margin-bottom: 5px;">Nome *</label>
<input type="text" id="uploadNewPatientFirstName" placeholder="Ex: Maria" style="width: 100%; padding: 8px 12px; border: 1px solid #ccc; border-radius: 4px;">
</div>
<div style="flex: 1;">
<label style="font-size: 0.82rem; font-weight: 600; display: block; margin-bottom: 5px;">Sobrenome *</label>
<input type="text" id="uploadNewPatientLastName" placeholder="Ex: Oliveira" style="width: 100%; padding: 8px 12px; border: 1px solid #ccc; border-radius: 4px;">
</div>
</div>
<div>
<label style="font-size: 0.82rem; font-weight: 600; display: block; margin-bottom: 5px;">Dentista Responsável</label>
<input type="text" id="uploadNewPatientDoctor" placeholder="Nome do dentista (opcional)" style="width: 100%; padding: 8px 12px; border: 1px solid #ccc; border-radius: 4px;">
</div>
</div>
<div class="form-group">
<label for="uploadRemark">Observações / Detalhes (Opcional)</label>
<textarea id="uploadRemark" placeholder="Ex: Raio-X Panorâmico" rows="2" style="width: 100%; padding: 10px; border: 2px solid var(--border-color); border-radius: var(--radius-sm); font-family: inherit; font-size: 1rem; resize: vertical;"></textarea>
</div>
<!-- Zona de Drag and Drop -->
<div class="drag-drop-zone" id="uploadDragDropZone">
<div class="drag-drop-icon">📸</div>
<div class="drag-drop-text">Arraste as imagens aqui ou clique para selecionar</div>
<div class="drag-drop-subtext">Suporta arquivos JPG, JPEG e PNG (máx. 10MB por imagem)</div>
<input type="file" id="uploadFileInput" multiple accept="image/png, image/jpeg, image/jpg" style="display: none;">
</div>
<!-- Previews das Imagens Selecionadas -->
<div class="upload-previews" id="uploadPreviewsContainer"></div>
<!-- Progresso de Envio -->
<div class="upload-progress-container" id="uploadProgressContainer" style="display: none;">
<div class="progress-bar-label">
<span id="uploadProgressText">Enviando imagens...</span>
<span id="uploadProgressPercent">0%</span>
</div>
<div class="progress-bar-track">
<div class="progress-bar-fill" id="uploadProgressBarFill"></div>
</div>
</div>
<div class="form-actions" style="margin-top: 25px; border-top: 1px solid #eee; padding-top: 15px; display: flex; justify-content: flex-end; gap: 10px;">
<button type="button" class="btn btn-secondary" onclick="closeSyncModal()">Cancelar</button>
<button type="submit" class="btn btn-primary" id="btnSubmitManualUpload" disabled>Enviar Imagens</button>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- Application Script -->
<script src="/socket.io/socket.io.js"></script>
<script src="/app.js"></script>
</body>
</html>