252 lines
8.6 KiB
JavaScript
252 lines
8.6 KiB
JavaScript
document.addEventListener('DOMContentLoaded', () => {
|
|
// Configurações
|
|
const API_URL = '/api/admin/clinics';
|
|
|
|
// Pegar Token JWT do Admin
|
|
const token = localStorage.getItem('token') || '';
|
|
|
|
// Se não tem token, redirecionar imediatamente para login
|
|
if (!token) {
|
|
window.location.href = '/login';
|
|
return;
|
|
}
|
|
|
|
const fetchOptions = {
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${token}`
|
|
}
|
|
};
|
|
|
|
// Elementos UI
|
|
const clinicsTableBody = document.getElementById('clinicsTableBody');
|
|
const addClinicBtn = document.getElementById('addClinicBtn');
|
|
const clinicModal = document.getElementById('clinicModal');
|
|
const tokenModal = document.getElementById('tokenModal');
|
|
const closeModals = document.querySelectorAll('.close-modal');
|
|
const closeTokenModal = document.querySelector('.close-token-modal');
|
|
const clinicForm = document.getElementById('clinicForm');
|
|
const generatedTokenElem = document.getElementById('generatedToken');
|
|
const copyTokenBtn = document.getElementById('copyTokenBtn');
|
|
|
|
// Stats
|
|
const totalDevicesElem = document.getElementById('totalDevices');
|
|
const activeDevicesElem = document.getElementById('activeDevices');
|
|
const blockedDevicesElem = document.getElementById('blockedDevices');
|
|
|
|
// Inicializar
|
|
loadClinics();
|
|
|
|
// ==========================================
|
|
// Carregar Clínicas
|
|
// ==========================================
|
|
async function loadClinics() {
|
|
try {
|
|
const response = await fetch(API_URL, fetchOptions);
|
|
|
|
// Se recebeu 401 ou 403, token expirou — vai para login
|
|
if (response.status === 401 || response.status === 403) {
|
|
localStorage.removeItem('token');
|
|
window.location.href = '/login';
|
|
return;
|
|
}
|
|
|
|
// Verificar se a resposta é realmente JSON (evita erro com HTML)
|
|
const contentType = response.headers.get('content-type') || '';
|
|
if (!contentType.includes('application/json')) {
|
|
throw new Error('Sessão expirada ou servidor indisponível. Faça login novamente.');
|
|
}
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Erro HTTP ${response.status}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
if (data.success) {
|
|
renderClinics(data.clinics);
|
|
updateStats(data.clinics);
|
|
} else {
|
|
showToast(data.message, 'error');
|
|
}
|
|
} catch (error) {
|
|
console.error(error);
|
|
clinicsTableBody.innerHTML = `<tr><td colspan="7" style="text-align:center;color:red;">${error.message || 'Erro ao carregar dados.'}</td></tr>`;
|
|
}
|
|
}
|
|
|
|
// ==========================================
|
|
// Renderizar Tabela
|
|
// ==========================================
|
|
function renderClinics(clinics) {
|
|
if (clinics.length === 0) {
|
|
clinicsTableBody.innerHTML = `<tr><td colspan="7" style="text-align:center;padding:30px;color:#a0aec0;">Nenhum dispositivo cadastrado.</td></tr>`;
|
|
return;
|
|
}
|
|
|
|
clinicsTableBody.innerHTML = '';
|
|
clinics.forEach(c => {
|
|
const isAct = c.is_active;
|
|
const statusClass = isAct ? 'status-active' : 'status-inactive';
|
|
const statusIcon = isAct ? 'fa-check' : 'fa-ban';
|
|
const statusText = isAct ? 'Ativo' : 'Bloqueado';
|
|
|
|
const tr = document.createElement('tr');
|
|
tr.innerHTML = `
|
|
<td><span class="status-badge ${statusClass}"><i class="fa-solid ${statusIcon}"></i> ${statusText}</span></td>
|
|
<td><strong>${c.clinic_name}</strong></td>
|
|
<td><i class="fa-solid fa-desktop" style="color:#a0aec0;margin-right:6px"></i>${c.pc_name || '-'}</td>
|
|
<td>${c.email}</td>
|
|
<td>${c.last_ip || 'Nunca conectou'}</td>
|
|
<td><span class="token-blur" title="Clique para copiar" onclick="navigator.clipboard.writeText('${c.machine_token}');showToast('Token copiado!')">${c.machine_token.split('-')[0]}...</span></td>
|
|
<td>
|
|
<div style="display:flex;gap:12px;align-items:center;">
|
|
<label class="toggle-switch" title="${isAct ? 'Bloquear Acesso' : 'Ativar Acesso'}">
|
|
<input type="checkbox" ${isAct ? 'checked' : ''} onchange="toggleStatus(${c.id}, this.checked)">
|
|
<span class="slider"></span>
|
|
</label>
|
|
<button class="icon-btn delete" onclick="deleteClinic(${c.id})" title="Excluir">
|
|
<i class="fa-solid fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
</td>
|
|
`;
|
|
clinicsTableBody.appendChild(tr);
|
|
});
|
|
}
|
|
|
|
function updateStats(clinics) {
|
|
const total = clinics.length;
|
|
const active = clinics.filter(c => c.is_active).length;
|
|
const blocked = total - active;
|
|
|
|
totalDevicesElem.textContent = total;
|
|
activeDevicesElem.textContent = active;
|
|
blockedDevicesElem.textContent = blocked;
|
|
}
|
|
|
|
// ==========================================
|
|
// Cadastrar Clínica
|
|
// ==========================================
|
|
clinicForm.addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
const submitBtn = document.getElementById('submitBtn');
|
|
submitBtn.disabled = true;
|
|
submitBtn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i> Salvando...';
|
|
|
|
const formData = new FormData(clinicForm);
|
|
const bodyData = Object.fromEntries(formData.entries());
|
|
|
|
try {
|
|
const response = await fetch(API_URL, {
|
|
method: 'POST',
|
|
headers: fetchOptions.headers,
|
|
body: JSON.stringify(bodyData)
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
clinicModal.classList.remove('active');
|
|
clinicForm.reset();
|
|
loadClinics();
|
|
|
|
// Show Token Modal
|
|
generatedTokenElem.textContent = data.machine_token;
|
|
tokenModal.classList.add('active');
|
|
} else {
|
|
showToast(data.message, 'error');
|
|
}
|
|
} catch (error) {
|
|
showToast('Erro de conexão ao salvar', 'error');
|
|
} finally {
|
|
submitBtn.disabled = false;
|
|
submitBtn.innerHTML = '<i class="fa-solid fa-check"></i> Salvar e Gerar Token';
|
|
}
|
|
});
|
|
|
|
// ==========================================
|
|
// Toggle Status
|
|
// ==========================================
|
|
window.toggleStatus = async (id, isActive) => {
|
|
try {
|
|
const response = await fetch(`${API_URL}/${id}/status`, {
|
|
method: 'PUT',
|
|
headers: fetchOptions.headers,
|
|
body: JSON.stringify({ is_active: isActive })
|
|
});
|
|
const data = await response.json();
|
|
if(data.success) {
|
|
showToast(isActive ? 'Dispositivo Ativado' : 'Dispositivo Bloqueado', 'success');
|
|
loadClinics();
|
|
} else {
|
|
showToast(data.message, 'error');
|
|
loadClinics(); // revert UI
|
|
}
|
|
} catch (error) {
|
|
showToast('Erro ao alterar status', 'error');
|
|
loadClinics();
|
|
}
|
|
};
|
|
|
|
// ==========================================
|
|
// Deletar
|
|
// ==========================================
|
|
window.deleteClinic = async (id) => {
|
|
if(!confirm('Tem certeza que deseja excluir este dispositivo permanentemente? O Desktop perderá acesso instantaneamente.')) return;
|
|
|
|
try {
|
|
const response = await fetch(`${API_URL}/${id}`, {
|
|
method: 'DELETE',
|
|
headers: fetchOptions.headers
|
|
});
|
|
const data = await response.json();
|
|
if(data.success) {
|
|
showToast('Dispositivo removido', 'success');
|
|
loadClinics();
|
|
} else {
|
|
showToast(data.message, 'error');
|
|
}
|
|
} catch (error) {
|
|
showToast('Erro ao excluir', 'error');
|
|
}
|
|
};
|
|
|
|
// ==========================================
|
|
// UI Actions (Modals, Copy, Logout)
|
|
// ==========================================
|
|
addClinicBtn.addEventListener('click', () => clinicModal.classList.add('active'));
|
|
|
|
closeModals.forEach(btn => btn.addEventListener('click', () => {
|
|
clinicModal.classList.remove('active');
|
|
}));
|
|
|
|
closeTokenModal.addEventListener('click', () => {
|
|
tokenModal.classList.remove('active');
|
|
});
|
|
|
|
copyTokenBtn.addEventListener('click', () => {
|
|
navigator.clipboard.writeText(generatedTokenElem.textContent).then(() => {
|
|
showToast('Token copiado com sucesso!', 'success');
|
|
copyTokenBtn.innerHTML = '<i class="fa-solid fa-check" style="color:var(--success)"></i>';
|
|
setTimeout(() => {
|
|
copyTokenBtn.innerHTML = '<i class="fa-regular fa-copy"></i>';
|
|
}, 2000);
|
|
});
|
|
});
|
|
|
|
document.getElementById('logoutBtn').addEventListener('click', () => {
|
|
localStorage.removeItem('token');
|
|
window.location.href = '/login';
|
|
});
|
|
|
|
function showToast(message, type = 'success') {
|
|
const toast = document.getElementById('toast');
|
|
toast.textContent = message;
|
|
toast.className = `toast show ${type}`;
|
|
|
|
setTimeout(() => {
|
|
toast.className = 'toast';
|
|
}, 3000);
|
|
}
|
|
});
|