// ============================================================ // Módulo Control de Calidad // ============================================================ const ControlCalidadLocal = ({ libros, currentUser, onUpdate, onSelectBook }) => { const [revisando, setRevisando] = useState(null); const [decision, setDecision] = useState(null); // 'aceptado' | 'rechazado' const [observaciones, setObservaciones] = useState(''); // Cola: libros editados esperando CC const cola = libros.filter(l => l.estatus === 'editado') .sort((a, b) => a.fechaEdicion.localeCompare(b.fechaEdicion)); const revisadosHoy = libros.filter(l => l.ccPor === currentUser.id && l.fechaControlCalidad === '2026-08-02' ); const aceptadosHoy = revisadosHoy.filter(l => ['aceptado','en_conversion','finalizado'].includes(l.estatus)).length; const rechazadosHoy = revisadosHoy.filter(l => l.estatus === 'rechazado').length; const totalAceptados = libros.filter(l => ['aceptado','en_conversion','finalizado'].includes(l.estatus)).length; const totalRechazados = libros.filter(l => l.estatus === 'rechazado').length; const tasaAcept = totalAceptados + totalRechazados > 0 ? (totalAceptados / (totalAceptados + totalRechazados)) * 100 : 0; const abrirRevision = (libro) => { setRevisando(libro); setDecision(null); setObservaciones(''); }; const guardarDecision = () => { if (!revisando || !decision) return; if (decision === 'rechazado' && !observaciones.trim()) return; onUpdate(revisando.cons, { estatus: decision, fechaControlCalidad: '2026-08-02', ccPor: currentUser.id, observaciones: decision === 'rechazado' ? observaciones : null, }); setRevisando(null); }; return (
Cola de control de calidad (FIFO)
Libros editados esperando revisión · ordenados por fecha de edición
{cola.length === 0 && ( )} {cola.map((l, i) => { const editor = getUser(l.editadoPor); return ( ); })}
# Núm. Título Editor Fecha edición Imágs. Acción
{i + 1} #{l.cons}
{l.titulo}
{l.autor}
{editor &&
{editor.nombre.split(' ')[0]}
}
{fmtDate(l.fechaEdicion)} {fmtNum(l.imagenesEditadas)}
{/* Historial reciente */}
Últimos aceptados
Pasan a conversión
{libros.filter(l => ['aceptado','en_conversion','finalizado'].includes(l.estatus)) .slice(-5).reverse().map(l => ( onSelectBook(l)} style={{ cursor: 'pointer' }}> ))}
#{l.cons}
{l.titulo}
{fmtDate(l.fechaControlCalidad)}
Rechazados con observaciones
Requieren reproceso
{libros.filter(l => l.estatus === 'rechazado').length === 0 && ( )} {libros.filter(l => l.estatus === 'rechazado').slice(0, 5).map(l => (
onSelectBook(l)}>
#{l.cons}
{fmtDate(l.fechaControlCalidad)}
{l.titulo}
Obs: {l.observaciones}
))}
{/* Modal de revisión */} setRevisando(null)} title="Revisión de Control de Calidad" maxWidth={560} footer={<> } > {revisando && (
#{revisando.cons} · {revisando.serie}
{revisando.titulo}
{revisando.autor}
Digitalizado
{fmtDate(revisando.fechaDigitalizacion)} · {getUser(revisando.digitalizadoPor)?.nombre}
Editado
{fmtDate(revisando.fechaEdicion)} · {getUser(revisando.editadoPor)?.nombre}
Imágenes originales
{fmtNum(revisando.imagenesResultantes)}
Imágenes editadas
{fmtNum(revisando.imagenesEditadas)}
Decisión
{decision === 'rechazado' && (