import { useState, useEffect, useRef } from 'react'; import { User, Mode } from './types'; import { processLabsWithGemini, processTreatmentsWithGemini } from './lib/gemini'; const STORAGE_KEY = "ordenador_clinico_usuarios_v3"; function simpleHash(text: string) { let hash = 0; for (let i = 0; i < text.length; i++) { hash = (hash << 5) - hash + text.charCodeAt(i); hash |= 0; } return String(hash); } function xorEncrypt(text: string, key: string) { let result = ""; for (let i = 0; i < text.length; i++) { const charCode = text.charCodeAt(i) ^ key.charCodeAt(i % key.length); result += String.fromCharCode(charCode); } return btoa(result); } function xorDecrypt(base64Text: string, key: string) { try { const text = atob(base64Text); let result = ""; for (let i = 0; i < text.length; i++) { const charCode = text.charCodeAt(i) ^ key.charCodeAt(i % key.length); result += String.fromCharCode(charCode); } return result; } catch { return null; } } export default function App() { const [users, setUsers] = useState([]); const [selectedUserIndex, setSelectedUserIndex] = useState(""); const [mode, setMode] = useState("labs"); const [inputText, setInputText] = useState(""); const [outputText, setOutputText] = useState(""); const [status, setStatus] = useState({ message: "Listo para pegar texto.", isError: false }); const [isModalOpen, setIsModalOpen] = useState(false); const [isProcessing, setIsProcessing] = useState(false); const [newUserName, setNewUserName] = useState(""); const [newUserApiKey, setNewUserApiKey] = useState(""); const [newUserPin, setNewUserPin] = useState(""); const [unlockedUserIndex, setUnlockedUserIndex] = useState(null); const [unlockedApiKey, setUnlockedApiKey] = useState(null); const [isPinModalOpen, setIsPinModalOpen] = useState(false); const [pinInput, setPinInput] = useState(""); useEffect(() => { const raw = localStorage.getItem(STORAGE_KEY); if (raw) { try { const parsed = JSON.parse(raw); if (Array.isArray(parsed)) { setUsers(parsed); } } catch (e) { console.error("Error loading users", e); } } }, []); const saveUsers = (newUsers: User[]) => { setUsers(newUsers); localStorage.setItem(STORAGE_KEY, JSON.stringify(newUsers)); }; const handleSaveUser = () => { if (!newUserName || !newUserApiKey || !newUserPin) { updateStatus("Debes escribir nombre, API key y PIN.", true); return; } if (newUserPin.length < 4) { updateStatus("El PIN debe tener al menos 4 caracteres.", true); return; } const encryptedApiKey = xorEncrypt(newUserApiKey, newUserPin); const pinHash = simpleHash(newUserPin); const existingIndex = users.findIndex(u => u.name.toLowerCase() === newUserName.toLowerCase()); const updatedUsers = [...users]; if (existingIndex >= 0) { updatedUsers[existingIndex] = { name: newUserName, encryptedApiKey, pinHash }; } else { updatedUsers.push({ name: newUserName, encryptedApiKey, pinHash }); } saveUsers(updatedUsers); setIsModalOpen(false); setNewUserName(""); setNewUserApiKey(""); setNewUserPin(""); updateStatus(`Usuario "${newUserName}" guardado correctamente.`); }; const updateStatus = (message: string, isError = false) => { setStatus({ message, isError }); }; const executeProcessing = async (apiKey: string, userName: string) => { try { setIsProcessing(true); updateStatus(`Procesando con Gemini para ${userName}...`); let result = ""; if (mode === "treatments") { result = await processTreatmentsWithGemini(apiKey, inputText); } else { result = await processLabsWithGemini(apiKey, inputText); } setOutputText(result); updateStatus(`Procesamiento completado para ${userName}.`); } catch (error: any) { console.error(error); updateStatus(error.message || "Error procesando con Gemini. Revisa la API key o la consola.", true); } finally { setIsProcessing(false); } }; const handleProcess = async () => { if (selectedUserIndex === "") { updateStatus("Selecciona un médico antes de procesar.", true); return; } if (!inputText.trim()) { updateStatus("Pega un texto antes de procesar.", true); return; } if (unlockedUserIndex === selectedUserIndex && unlockedApiKey) { executeProcessing(unlockedApiKey, users[selectedUserIndex as number].name); } else { setPinInput(""); setIsPinModalOpen(true); } }; const handlePinConfirm = () => { if (selectedUserIndex === "") return; const currentUser = users[selectedUserIndex as number]; if (simpleHash(pinInput) !== currentUser.pinHash) { updateStatus("PIN incorrecto.", true); return; } const apiKey = xorDecrypt(currentUser.encryptedApiKey, pinInput); if (!apiKey) { updateStatus("No se pudo desbloquear la API key.", true); return; } setUnlockedUserIndex(selectedUserIndex as number); setUnlockedApiKey(apiKey); setIsPinModalOpen(false); executeProcessing(apiKey, currentUser.name); }; const handleCopy = async () => { if (!outputText.trim()) { updateStatus("No hay resultado para copiar.", true); return; } try { await navigator.clipboard.writeText(outputText); updateStatus("Resultado copiado al portapapeles."); } catch { updateStatus("No se pudo copiar el resultado.", true); } }; const handleLock = () => { setUnlockedUserIndex(null); setUnlockedApiKey(null); updateStatus("Usuario bloqueado."); }; return (

Ordenador Clínico IA

Analíticas y tratamientos con anonimización local

Modo:
{status.message}

Texto de entrada