600 lines
36 KiB
HTML
600 lines
36 KiB
HTML
<!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.2</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">×</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;">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">×</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">×</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">×</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">×</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">×</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">×</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>
|