chart = {
const width = 1000;
const height = 1500;
const margin = ({top: 20, right: 20, bottom: 120, left: 160});
const colorScale = d3.scaleOrdinal(d3.schemeCategory10).domain(allCommunities);
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);
d3.select(".custom-tooltip").remove();
const tooltip = d3.select("body").append("div").attr("class", "custom-tooltip").style("position", "absolute").style("visibility", "hidden").style("background", "rgba(0,0,0,0.85)").style("color", "white").style("border-radius", "6px").style("padding", "10px").style("font-family", "sans-serif").style("font-size", "13px").style("pointer-events", "none").style("max-width", "400px").style("white-space", "pre-wrap").style("z-index", "10");
if (entityFilter.length === 0) {
svg.append("text").attr("x", width / 2).attr("y", 100).attr("text-anchor", "middle").style("font-size", "1.2em").text("Por favor, selecciona entidades para visualizar.");
return svg.node();
}
const defs = svg.append("defs");
allCommunities.forEach(community => { defs.append("marker").attr("id", `arrowhead-${community}`).attr("viewBox", "0 -5 10 10").attr("refX", 10).attr("refY", 0).attr("markerWidth", 6).attr("markerHeight", 6).attr("orient", "auto").append("path").attr("d", "M0,-5L10,0L0,5").attr("fill", colorScale(community)); });
const yScale = d3.scaleUtc().domain([startDateFilter, endDateFilter]).range([margin.top, height - margin.bottom]);
const xScale = d3.scalePoint().domain(entityFilter).range([margin.left, width - margin.right]).padding(0.5);
svg.append("g").attr("transform", `translate(${margin.left},0)`).call(d3.axisLeft(yScale).ticks(d3.utcHour.every(4)).tickFormat(d3.utcFormat("%H:%M %b %d")));
svg.append("g").attr("transform", `translate(0,${height - margin.bottom})`).call(d3.axisBottom(xScale)).selectAll("text").style("text-anchor", "end").attr("dx", "-.8em").attr("dy", ".15em").attr("transform", "rotate(-45)").style("fill", d => { const row = data.find(row => row.from_id === d || row.to_id === d); return row ? colorScale(row.source_community || row.target_community) : '#000'; });
const lastState = {};
const pathData = [];
processedConversations.singleMessages.forEach(d => { if (!xScale(d.from_id) || !xScale(d.to_id)) return; const y_current = yScale(d.time); const x_source = xScale(d.from_id); const x_target = xScale(d.to_id); let x1, y1; if (lastState[d.from_id]) { x1 = lastState[d.from_id].x; y1 = lastState[d.from_id].y; } else { x1 = x_source; y1 = y_current; } pathData.push({ x1, y1, x2: x_target, y2: y_current, ...d }); lastState[d.from_id] = { x: x_source, y: y_current }; lastState[d.to_id] = { x: x_target, y: y_current }; });
// --- DIBUJAR ELEMENTOS ---
// 1. Líneas de Monitoreo (CON NUEVO COLOR)
svg.selectAll(".monitor-line").data(filteredMonitoring).join("line")
.attr("class", "monitor-line")
.attr("x1", margin.left).attr("x2", width - margin.right)
.attr("y1", d => yScale(d.time)).attr("y2", d => yScale(d.time))
.style("cursor", "pointer")
// ---- CAMBIO AQUÍ ----
.attr("stroke", d => monitoringColorScale(d.monitoring_subtype))
.attr("stroke-width", 3).attr("stroke-dasharray", "4 4").attr("opacity", 0.7)
.on("mouseover", (event, d) => tooltip.style("visibility", "visible").html(`<b>Tipo:</b> ${d.monitoring_subtype}<br><hr style="margin:4px 0;"><b>Hallazgos:</b><br>${d.event_findings}`))
.on("mousemove", (event) => tooltip.style("top", (event.pageY - 10) + "px").style("left", (event.pageX + 10) + "px"))
.on("mouseout", () => tooltip.style("visibility", "hidden"));
// ... (El resto del código para sesiones y mensajes únicos se queda igual) ...
svg.append("g").selectAll(".session-rect").data(processedConversations.sessions).join("rect").attr("class", "session-rect").attr("x", d => Math.min(xScale(d.participants[0]), xScale(d.participants[1]))).attr("y", d => yScale(d.startTime)).attr("width", d => Math.abs(xScale(d.participants[0]) - xScale(d.participants[1]))).attr("height", d => Math.max(1, yScale(d.endTime) - yScale(d.startTime))).attr("fill", d => colorScale(d.community)).attr("opacity", 0.4).style("cursor", "pointer").on("mouseover", (event, d) => { const messagesText = d.messages.map(m => `<b>${m.from_id} (${d3.utcFormat("%H:%M:%S")(m.time)}):</b><br>${m.text}`).join('<br><hr style="margin: 4px 0;">'); tooltip.style("visibility", "visible").html(`<b>Conversación:</b><br><hr style="margin: 4px 0;">${messagesText}`); }).on("mousemove", (event) => tooltip.style("top", (event.pageY - 10) + "px").style("left", (event.pageX + 10) + "px")).on("mouseout", () => tooltip.style("visibility", "hidden"));
svg.selectAll(".comm-line").data(pathData).join("line").attr("class", "comm-line").attr("x1", d => d.x1).attr("y1", d => d.y1).attr("x2", d => d.x2).attr("y2", d => d.y2).style("cursor", "pointer").attr("stroke", d => colorScale(d.source_community)).attr("stroke-width", 3).attr("marker-end", d => `url(#arrowhead-${d.source_community})`).on("mouseover", (event, d) => tooltip.style("visibility", "visible").html(`<b>De:</b> ${d.from_id}<br><b>A:</b> ${d.to_id}<hr><b>Mensaje:</b><br>${d.text}`)).on("mousemove", (event) => tooltip.style("top", (event.pageY - 10) + "px").style("left", (event.pageX + 10) + "px")).on("mouseout", () => tooltip.style("visibility", "hidden"));
invalidation.then(() => tooltip.remove());
return svg.node();
}