import React, { useState, useEffect } from 'react';
import {
Plus,
Trash2,
Download,
Settings,
User,
Monitor,
ClipboardCheck,
Stethoscope,
Wrench,
DollarSign,
ShieldCheck,
MapPin,
Mail,
Phone,
Loader2,
RotateCcw
} from 'lucide-react';
const App = () => {
const [isGenerating, setIsGenerating] = useState(false);
const [isPreview, setIsPreview] = useState(false);
const [showResetConfirm, setShowResetConfirm] = useState(false);
// Función para crear la estructura base de un nuevo equipo
const createNewEquipment = () => ({
id: Date.now() + Math.random(),
info: { type: '', brandModel: '', serialNumber: '' },
reception: { hasCharger: false, physicalState: '', password: '' },
diagnosis: { reportedFailure: '', findings: '', hardwareStatus: '', softwareStatus: '' },
works: [{ id: Date.now() + 1, description: '' }],
costs: { labor: 0, partsTotal: 0, partsDetails: '' }
});
// Datos iniciales para el formulario
const getInitialData = () => ({
orderNumber: 'OT-' + Math.floor(Math.random() * 1000000),
date: new Date().toLocaleDateString('es-ES', { day: 'numeric', month: 'long', year: 'numeric' }),
client: { name: '', id: '', phone: '', email: '' },
equipments: [createNewEquipment()],
recommendations: [
{ id: 1, description: 'Realizar un mantenimiento preventivo cada 6 meses.' },
{ id: 2, description: 'Utilizar un regulador de voltaje o protector de picos.' }
],
warrantyDays: 30,
technicianName: 'ING. IVÁN BOLAGAY',
technicianTitle: 'Gerente General'
});
const [data, setData] = useState(getInitialData());
// Carga dinámica de la librería para exportar a PDF
useEffect(() => {
const script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js';
script.async = true;
document.body.appendChild(script);
return () => { if (document.body.contains(script)) document.body.removeChild(script); };
}, []);
const handleGlobalChange = (section, field, value) => {
setData(prev => ({
...prev,
[section]: typeof prev[section] === 'object' ? { ...prev[section], [field]: value } : value
}));
};
const handleEquipChange = (equipId, section, field, value) => {
setData(prev => ({
...prev,
equipments: prev.equipments.map(e => {
if (e.id !== equipId) return e;
return {
...e,
[section]: typeof e[section] === 'object' ? { ...e[section], [field]: value } : value
};
})
}));
};
const addEquipment = () => {
setData(prev => ({ ...prev, equipments: [...prev.equipments, createNewEquipment()] }));
};
const removeEquipment = (id) => {
if (data.equipments.length > 1) {
setData(prev => ({ ...prev, equipments: prev.equipments.filter(e => e.id !== id) }));
}
};
const handleWorkChange = (equipId, workId, value) => {
setData(prev => ({
...prev,
equipments: prev.equipments.map(e => {
if (e.id !== equipId) return e;
return {
...e,
works: e.works.map(w => w.id === workId ? { ...w, description: value } : w)
};
})
}));
};
const addWork = (equipId) => {
setData(prev => ({
...prev,
equipments: prev.equipments.map(e => {
if (e.id !== equipId) return e;
return { ...e, works: [...e.works, { id: Date.now(), description: '' }] };
})
}));
};
const removeWork = (equipId, workId) => {
setData(prev => ({
...prev,
equipments: prev.equipments.map(e => {
if (e.id !== equipId) return e;
if (e.works.length <= 1) return e;
return { ...e, works: e.works.filter(w => w.id !== workId) };
})
}));
};
// Función para resetear el formulario a su estado original
const handleResetForm = () => {
setData(getInitialData());
setShowResetConfirm(false);
setIsPreview(false);
};
// Lógica para descargar el documento PDF
const handleDownloadPDF = async () => {
if (isGenerating) return;
const wasPreview = isPreview;
if (!wasPreview) setIsPreview(true);
setIsGenerating(true);
setTimeout(() => {
const element = document.getElementById('report-document');
const opt = {
margin: [10, 10, 10, 10],
filename: `Informe_${data.orderNumber}_${data.client.name.replace(/\s+/g, '_')}.pdf`,
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2, useCORS: true, letterRendering: true, scrollY: 0 },
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
};
if (window.html2pdf) {
window.html2pdf().from(element).set(opt).save().then(() => {
setIsGenerating(false);
if (!wasPreview) setIsPreview(false);
});
} else {
setIsGenerating(false);
}
}, 600);
};
const totalLabor = data.equipments.reduce((acc, e) => acc + parseFloat(e.costs.labor || 0), 0);
const totalParts = data.equipments.reduce((acc, e) => acc + parseFloat(e.costs.partsTotal || 0), 0);
const grandTotal = totalLabor + totalParts;
return (
{/* Header Interactivo */}
{/* Botón de Limpiar Formulario */}
{!isPreview && (
{/* Modal de Confirmación */}
{showResetConfirm && (
¿Deseas vaciar todos los campos del formulario?
)}
)}
{!isPreview ? (
/* PESTAÑA DE EDICIÓN (FORMULARIO) */
Identificación de Servicio
Equipos Registrados ({data.equipments.length})
{data.equipments.map((equip, index) => (
Equipo #{index + 1}
{data.equipments.length > 1 && (
)}
Datos Técnicos
handleEquipChange(equip.id, 'info', 'type', e.target.value)} className="w-full p-2 bg-slate-50 border-none rounded-lg text-sm outline-none focus:ring-1 focus:ring-blue-500" />
handleEquipChange(equip.id, 'info', 'brandModel', e.target.value)} className="w-full p-2 bg-slate-50 border-none rounded-lg text-sm outline-none focus:ring-1 focus:ring-blue-500" />
handleEquipChange(equip.id, 'info', 'serialNumber', e.target.value)} className="w-full p-2 bg-slate-50 border-none rounded-lg text-sm font-mono outline-none focus:ring-1 focus:ring-blue-500" />
Análisis y Trabajos
Acciones Realizadas
{equip.works.map((work, idx) => (
))}
))}
{/* Cuadro de Resumen General */}
Resumen de Orden
Acumulado total de {data.equipments.length} equipo(s)
M. de Obra
${totalLabor.toFixed(2)}
Insumos
${totalParts.toFixed(2)}
Gran Total
${grandTotal.toFixed(2)}
) : (
/* PESTAÑA DE VISTA PREVIA (DOCUMENTO FINAL) */
{/* Header del Documento */}
CS
COMPUSERVER
SOLUCIONES TECNOLÓGICAS
Av. Luis Cordero 407 / Pichincha 212 y Colombia
compuserver@gmail.com | alexbolagay@gmail.com
+593 998 375 496 | +593 996 847 316
Número de Orden
#{data.orderNumber}
{data.date}
Informe Técnico de Servicio
{/* Datos del Cliente Consolidados */}
Nombre Cliente: {data.client.name || '---'}
Identificación/RUC: {data.client.id || '---'}
Teléfono Contacto: {data.client.phone || '---'}
Técnico Asignado: {data.technicianName}
{/* Mapeo de Equipos en el PDF */}
{data.equipments.map((e, idx) => (
Equipo #{idx+1}: {e.info.type || 'N/A'}
Serie: {e.info.serialNumber || 'No Identificado'}
{/* Lateral Izquierdo Info Equipo */}
Marca / Modelo:
{e.info.brandModel || '---'}
Accesorios:
{e.reception.hasCharger ? '✓ INCLUYE CARGADOR' : '✗ SIN CARGADOR'}
Falla Reportada:
"{e.diagnosis.reportedFailure || 'No detallada'}"
{/* Contenido Técnico: HALLAZGOS Y TRABAJOS */}
{/* SECCIÓN DE HALLAZGOS TÉCNICOS DESTACADA */}
Hallazgos Técnicos (Diagnóstico)
{e.diagnosis.findings || 'No se han detallado hallazgos específicos para este equipo.'}
Trabajos Realizados:
{e.works.map((w, wIdx) => w.description && (
{wIdx+1}
{w.description}
))}
{/* Desglose de costo por equipo */}
Total Equipo #{idx+1}:
${(parseFloat(e.costs.labor || 0) + parseFloat(e.costs.partsTotal || 0)).toFixed(2)}
))}
{/* Recomendaciones y Liquidación Final (Optimizado) */}
Recomendaciones Técnicas
{data.recommendations.map(r => (
{r.description}
))}
{/* CUADRO DE LIQUIDACIÓN RE-ESTILIZADO (Llamativo pero ligero) */}
Liquidación Final
Servicio Técnico
${totalLabor.toFixed(2)}
Partes y Repuestos
${totalParts.toFixed(2)}
Gran Total:
Cobro en efectivo/transf*
${grandTotal.toFixed(2)}
{/* Bloque de Firmas Final */}
{data.technicianName}
{data.technicianTitle}
Validado por CompuServer Tech Intelligence
Firma de Conformidad
{data.client.name || 'CLIENTE'}
{/* Pie de Página */}
CompuServer • Ingeniería y Excelencia al Servicio de su Tecnología • 2026
)}
{/* Reglas de Impresión y Estilos */}
);
};
export default App;