<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Historia Clínica Dental</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
body {
font-family: 'Inter', sans-serif;
background-color: #f3f4f6;
}
.container {
max-width: 900px;
}
.card {
background-color: #ffffff;
border-radius: 1rem;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
padding: 2rem;
}
.scrollable-content {
max-height: 500px;
overflow-y: auto;
}
</style>
</head>
<body class="bg-gray-100 flex items-center justify-center min-h-screen p-4">
<div id="app-container" class="container mx-auto">
<div id="loading" class="flex items-center justify-center h-full">
<div class="text-xl font-semibold text-gray-600">Cargando...</div>
</div>
<div id="content" class="hidden">
<h1 class="text-3xl font-bold text-gray-800 mb-6 text-center">Historia Clínica del Paciente</h1>
<p id="user-info" class="text-sm text-gray-600 text-center mb-6"></p>
<!-- Sección de Datos del Paciente (Ejemplo de integración) -->
<div class="card mb-6">
<h2 class="text-xl font-semibold text-gray-700 mb-4">Datos del Paciente</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<p class="text-sm text-gray-500">Nombre del Paciente</p>
<p class="font-medium text-gray-900">Juan Pérez</p>
</div>
<div>
<p class="text-sm text-gray-500">ID del Paciente</p>
<p class="font-medium text-gray-900" id="patient-id">patient-001</p>
</div>
<!-- Se pueden agregar más datos como antecedentes médicos y odontograma aquí -->
</div>
</div>
<!-- Formulario para agregar una nueva consulta -->
<div class="card mb-6">
<h2 class="text-xl font-semibold text-gray-700 mb-4">Nueva Consulta</h2>
<form id="record-form">
<div class="mb-4">
<label for="reason" class="block text-sm font-medium text-gray-700 mb-1">Motivo de la Consulta</label>
<input type="text" id="reason" name="reason" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="Ej: Dolor de muela" required>
</div>
<div class="mb-4">
<label for="diagnosis" class="block text-sm font-medium text-gray-700 mb-1">Diagnóstico</label>
<textarea id="diagnosis" name="diagnosis" rows="3" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="Ej: Caries en pieza 46" required></textarea>
</div>
<div class="mb-4">
<label for="treatment" class="block text-sm font-medium text-gray-700 mb-1">Plan de Tratamiento</label>
<textarea id="treatment" name="treatment" rows="3" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="Ej: Restauración con resina"></textarea>
</div>
<div id="message-box" class="hidden p-3 mt-4 text-sm text-center font-medium rounded-lg" role="alert"></div>
<button type="submit" class="w-full px-4 py-2 mt-2 font-semibold text-white bg-blue-600 rounded-lg hover:bg-blue-700 transition duration-300">
Guardar Registro
</button>
</form>
</div>
<!-- Historial de consultas -->
<div class="card">
<h2 class="text-xl font-semibold text-gray-700 mb-4">Historial de Consultas</h2>
<div id="records-container" class="scrollable-content space-y-4">
<!-- Los registros se insertarán aquí dinámicamente -->
</div>
</div>
</div>
</div>
<script type="module">
import { initializeApp } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-app.js";
import { getAuth, signInAnonymously, signInWithCustomToken, onAuthStateChanged } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-auth.js";
import { getFirestore, doc, getDoc, addDoc, setDoc, updateDoc, deleteDoc, onSnapshot, collection, query, where, orderBy, getDocs } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-firestore.js";
// --- Configuración de Firebase y Autenticación ---
// Se utilizan las variables globales proporcionadas por el entorno de Canvas
const firebaseConfig = JSON.parse(typeof __firebase_config !== 'undefined' ? __firebase_config : '{}');
const appId = typeof __app_id !== 'undefined' ? __app_id : 'default-app-id';
const initialAuthToken = typeof __initial_auth_token !== 'undefined' ? __initial_auth_token : null;
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
const auth = getAuth(app);
// Variables para los elementos del DOM
const loadingScreen = document.getElementById('loading');
const mainContent = document.getElementById('content');
const userInfo = document.getElementById('user-info');
const recordForm = document.getElementById('record-form');
const recordsContainer = document.getElementById('records-container');
const messageBox = document.getElementById('message-box');
// Se usa un ID de paciente de ejemplo. En una app real, este ID vendría de la lista de pacientes.
const patientId = document.getElementById('patient-id').textContent;
// Lógica de Autenticación
onAuthStateChanged(auth, async (user) => {
if (user) {
const userId = user.uid;
userInfo.textContent = `Usuario autenticado: ${userId}`;
loadingScreen.classList.add('hidden');
mainContent.classList.remove('hidden');
// Iniciar la escucha de los registros una vez que el usuario esté autenticado
listenForClinicalHistory(patientId);
} else {
// Si no hay token, se inicia sesión de forma anónima
if (initialAuthToken) {
try {
await signInWithCustomToken(auth, initialAuthToken);
} catch (error) {
console.error("Error al iniciar sesión con token personalizado:", error);
console.log("Intentando iniciar sesión de forma anónima...");
await signInAnonymously(auth);
}
} else {
await signInAnonymously(auth);
}
}
});
// --- Funciones de la Aplicación ---
/**
* Muestra un mensaje en la caja de mensajes.
* @param {string} message - El mensaje a mostrar.
* @param {string} type - El tipo de mensaje ('success' o 'error').
*/
function showMessage(message, type) {
messageBox.textContent = message;
messageBox.classList.remove('hidden', 'bg-green-100', 'text-green-700', 'bg-red-100', 'text-red-700');
if (type === 'success') {
messageBox.classList.add('bg-green-100', 'text-green-700');
} else if (type === 'error') {
messageBox.classList.add('bg-red-100', 'text-red-700');
}
}
/**
* Escucha los cambios en la colección de historia clínica para un paciente.
* Utiliza onSnapshot para actualizaciones en tiempo real.
* @param {string} patientId - El ID del paciente.
*/
function listenForClinicalHistory(patientId) {
const clinicalHistoryRef = collection(db, `artifacts/${appId}/public/data/clinical_histories`);
const q = query(clinicalHistoryRef, where("patientId", "==", patientId));
onSnapshot(q, (snapshot) => {
recordsContainer.innerHTML = ''; // Limpiar el contenedor
if (snapshot.empty) {
recordsContainer.innerHTML = '<p class="text-gray-500 text-center">No hay registros de consultas para este paciente.</p>';
}
const records = [];
snapshot.forEach(doc => {
records.push({ id: doc.id, ...doc.data() });
});
// Ordenar los registros por fecha (del más reciente al más antiguo)
records.sort((a, b) => new Date(b.date) - new Date(a.date));
records.forEach(record => {
const recordElement = document.createElement('div');
recordElement.className = 'border-l-4 border-blue-500 pl-4 py-2 bg-gray-50 rounded-lg';
recordElement.innerHTML = `
<div class="flex justify-between items-start">
<p class="text-sm text-gray-400">${new Date(record.date).toLocaleDateString()}</p>
</div>
<p class="font-semibold text-gray-800">${record.reason}</p>
<p class="text-gray-600 mt-1">**Diagnóstico:** ${record.diagnosis}</p>
<p class="text-gray-600 mt-1">**Tratamiento:** ${record.treatment || 'N/A'}</p>
`;
recordsContainer.appendChild(recordElement);
});
}, (error) => {
console.error("Error al escuchar los datos:", error);
showMessage("Error al cargar la historia clínica. Inténtelo de nuevo.", 'error');
});
}
/**
* Guarda un nuevo registro de consulta en Firestore.
* @param {Event} e - El evento de envío del formulario.
*/
async function addClinicalRecord(e) {
e.preventDefault();
const reason = document.getElementById('reason').value;
const diagnosis = document.getElementById('diagnosis').value;
const treatment = document.getElementById('treatment').value;
const date = new Date().toISOString();
if (!reason || !diagnosis) {
showMessage("Motivo y diagnóstico son campos obligatorios.", 'error');
return;
}
try {
const clinicalHistoryRef = collection(db, `artifacts/${appId}/public/data/clinical_histories`);
await addDoc(clinicalHistoryRef, {
patientId: patientId,
date: date,
reason: reason,
diagnosis: diagnosis,
treatment: treatment
});
showMessage("¡Registro guardado exitosamente!", 'success');
recordForm.reset();
} catch (error) {
console.error("Error al guardar el registro:", error);
showMessage("Error al guardar el registro. Por favor, inténtelo de nuevo.", 'error');
}
}
// --- Event Listeners ---
recordForm.addEventListener('submit', addClinicalRecord);
</script>
</body>
</html>