Files
rx.scoreodonto.com/dental-server/public/index.html
T
VPS 4 Builder da40ab11f6
continuous-integration/webhook Deploy concluido (rx.scoreodonto.com)
feat: implement user management dashboard and visibility access controls
2026-05-26 20:45:00 +02:00

549 lines
34 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="stylesheet" href="/style.css">
</head>
<body>
<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</h2>
</div>
<nav class="sidebar-nav">
<a href="/" class="nav-item active">
<i class="fa-solid fa-chart-line"></i> Dashboard
</a>
<a href="/clients" class="nav-item">
<i class="fa-solid fa-network-wired"></i> Conexões
</a>
<a href="/admin-clinics" class="nav-item">
<i class="fa-solid fa-hospital"></i> Clínicas e Acessos
</a>
</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">
<input
type="search"
id="searchInput"
class="search-input"
placeholder="🔍 Pesquisar paciente..."
>
<button class="btn btn-secondary" onclick="loadClientsList()">
<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">
<!-- Toolbar de Filtros e Ações Rápidas (Migradas da Sidebar) -->
<div class="toolbar glass-card" style="margin-bottom: 24px; padding: 16px; display: flex; gap: 16px; align-items: center; justify-content: space-between; flex-wrap: wrap; background: rgba(255, 255, 255, 0.4); border: 1px solid rgba(255, 255, 255, 0.3); border-radius: 12px; box-shadow: var(--shadow-sm);">
<div class="toolbar-left" style="display: flex; gap: 12px; align-items: center; flex-wrap: wrap;">
<!-- 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: 200px; cursor: pointer;">
<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);">
👁️ <span id="toggleText">Ocultas</span>
</button>
</div>
<div class="toolbar-right" style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
<button class="btn btn-primary" onclick="openCreatePatientModal()" style="padding: 10px 16px; border-radius: 10px; font-weight: 600; font-size: 0.95rem; background: var(--primary-color); color: white; border: none; cursor: pointer; display: flex; align-items: center; gap: 6px; box-shadow: 0 4px 10px rgba(79, 70, 229, 0.2);">
Novo Paciente
</button>
<button id="btnUserManagement" class="btn btn-secondary" onclick="openUserManagementModal()" title="Gerenciar Usuários" style="display: none; padding: 10px 16px; border-radius: 10px; font-weight: 500; font-size: 0.95rem; border: 1px solid var(--border-color); background: var(--bg-surface); color: var(--text-primary); cursor: pointer; align-items: center; gap: 6px;">
👥 Gerenciar Usuários
</button>
<button class="btn btn-secondary" onclick="openSettingsModal()" title="Configurações" style="padding: 10px 16px; border-radius: 10px; font-weight: 500; font-size: 0.95rem; border: 1px solid var(--border-color); background: var(--bg-surface); color: var(--text-primary); cursor: pointer; display: flex; align-items: center; gap: 6px;">
⚙️ Configurações
</button>
<button class="btn btn-secondary" onclick="openPluginsModal()" title="Plugins / Wasabi" style="padding: 10px 16px; border-radius: 10px; font-weight: 500; font-size: 0.95rem; border: 1px solid var(--border-color); background: var(--bg-surface); color: var(--text-primary); cursor: pointer; display: flex; align-items: center; gap: 6px;">
🔌 Plugins / Wasabi
</button>
<button class="btn btn-secondary" onclick="openSyncModal()" title="Sincronização" style="padding: 10px 16px; border-radius: 10px; font-weight: 500; font-size: 0.95rem; border: 1px solid var(--border-color); background: var(--bg-surface); color: var(--text-primary); cursor: pointer; display: flex; align-items: center; gap: 6px;">
🔄 Sincronização
</button>
<a href="/reset" class="btn btn-danger" title="Zerar Sistema" style="padding: 10px 16px; border-radius: 10px; font-weight: 500; font-size: 0.95rem; background: #fee2e2; color: #dc2626; border: 1px solid #fecaca; text-decoration: none; cursor: pointer; display: inline-flex; align-items: center; gap: 6px;">
⚠️ Zerar Sistema
</a>
</div>
</div>
<!-- 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="transformOptions" class="transform-grid"></div>
<div id="transformActionArea" class="transform-action-area" style="display: none;">
<!-- Ajuste Fino de Rotação -->
<div class="fine-tune-section">
<span class="fine-tune-title">
📐 Ajuste Fino de Ângulo (Sensor Inclinado)
</span>
<div class="fine-tune-controls">
<button type="button" class="btn btn-secondary btn-small" onclick="adjustFineTune(-5)" title="Rotacionar -5°">🔄 -5°</button>
<button type="button" class="btn btn-secondary btn-small" onclick="adjustFineTune(-1)" title="Rotacionar -1°">🔄 -1°</button>
<div id="fineTuneValDisplay" class="fine-tune-display">
</div>
<button type="button" class="btn btn-secondary btn-small" onclick="adjustFineTune(1)" title="Rotacionar +1°">🔄 +1°</button>
<button type="button" class="btn btn-secondary btn-small" onclick="adjustFineTune(5)" title="Rotacionar +5°">🔄 +5°</button>
<button type="button" class="btn btn-danger btn-small" id="btnResetFineTune" onclick="resetFineTune()" style="display: none;" title="Resetar ajuste fino">Reset</button>
</div>
</div>
<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" onclick="closeCreatePatientModal()">&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" onclick="closeCreateGtoModal()">&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="close-modal" onclick="closeSettingsModal()">&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">
<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 modal-large" style="max-width: 800px; background: var(--bg-surface); border-radius: 12px; box-shadow: var(--shadow-lg); padding: 32px;">
<div class="modal-header" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; border-bottom: 1px solid var(--border-color); padding-bottom: 12px;">
<h2 style="font-family: var(--font-heading); color: var(--text-primary); font-size: 1.6rem; font-weight: 600;">👥 Gerenciamento de Usuários</h2>
<span class="close-modal" onclick="closeUserManagementModal()" style="font-size: 2rem; cursor: pointer; color: var(--text-secondary);">&times;</span>
</div>
<div class="modal-body" style="display: flex; gap: 32px; flex-direction: row; flex-wrap: wrap;">
<!-- Formulário de Cadastro (Esquerda) -->
<div style="flex: 1; min-width: 280px; background: rgba(0,0,0,0.02); padding: 24px; border-radius: 12px; border: 1px solid var(--border-color);">
<h3 style="margin-bottom: 20px; font-family: var(--font-heading); color: var(--text-primary); font-size: 1.25rem;">Cadastrar Novo Usuário</h3>
<form id="createUserForm" onsubmit="createUser(event)">
<div class="form-group" style="margin-bottom: 16px;">
<label style="display:block; margin-bottom:6px; font-weight:600; color:var(--text-primary);">Nome de Usuário *</label>
<input type="text" id="new_username" required placeholder="Ex: dentista.ana" style="width: 100%; padding: 12px; border: 1px solid var(--border-color); border-radius: 8px; outline:none; background:var(--bg-color); color:var(--text-primary);">
</div>
<div class="form-group" style="margin-bottom: 16px;">
<label style="display:block; margin-bottom:6px; font-weight:600; color:var(--text-primary);">E-mail *</label>
<input type="email" id="new_email" required placeholder="ana@consultorio.com" style="width: 100%; padding: 12px; border: 1px solid var(--border-color); border-radius: 8px; outline:none; background:var(--bg-color); color:var(--text-primary);">
</div>
<div class="form-group" style="margin-bottom: 16px;">
<label style="display:block; margin-bottom:6px; font-weight:600; color:var(--text-primary);">Senha *</label>
<input type="password" id="new_password" required placeholder="Senha de acesso" style="width: 100%; padding: 12px; border: 1px solid var(--border-color); border-radius: 8px; outline:none; background:var(--bg-color); color:var(--text-primary);">
</div>
<button type="submit" class="btn btn-primary" style="width: 100%; margin-top: 12px; padding: 12px; border-radius: 8px; font-weight: 600; background:var(--primary-color); color:white; border:none; cursor:pointer;">
Cadastrar Usuário
</button>
</form>
</div>
<!-- Lista de Usuários (Direita) -->
<div style="flex: 1.5; min-width: 320px; display:flex; flex-direction:column;">
<h3 style="margin-bottom: 20px; font-family: var(--font-heading); color: var(--text-primary); font-size: 1.25rem;">Usuários Cadastrados</h3>
<div style="max-height: 320px; overflow-y: auto; border: 1px solid var(--border-color); border-radius: 12px; background:var(--bg-surface);">
<table style="width: 100%; border-collapse: collapse; font-size: 0.95rem; text-align: left;">
<thead>
<tr style="background: rgba(0,0,0,0.03); border-bottom: 1px solid var(--border-color); color:var(--text-primary); font-weight:600;">
<th style="padding: 14px 16px;">Usuário</th>
<th style="padding: 14px 16px;">E-mail</th>
<th style="padding: 14px 16px;">Função</th>
<th style="padding: 14px 16px; 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" onclick="closePluginsModal()">&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" style="max-width: 650px; height: auto; border-radius: var(--radius);">
<div class="modal-header">
<h2>🔄 Área de Sincronização</h2>
<span class="modal-close" onclick="closeSyncModal()">&times;</span>
</div>
<div class="modal-body">
<div class="sync-tabs">
<button type="button" id="tabBtnDevices" class="sync-tab-btn active" onclick="switchSyncTab('devices')">💻 Dispositivos Conectados</button>
<button type="button" id="tabBtnUpload" class="sync-tab-btn" onclick="switchSyncTab('upload')">📤 Envio Manual</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>