{
const svg = d3.create("svg")
.attr("width", dashboardConfig.width)
.attr("height", dashboardConfig.height)
.style("background", "#f8f9fa")
.style("border-radius", "8px")
.style("box-shadow", "0 2px 4px rgba(0,0,0,0.1)")
.style("margin-top", "20px");
const xScale = d3.scaleBand()
.domain(datosFiltrados.map(d => d.mes))
.range([dashboardConfig.margin.left, dashboardConfig.width -
dashboardConfig.margin.right])
.padding(0.1);
const yScale = d3.scaleLinear()
.domain([0, d3.max(datosFiltrados, d => Math.max(d.ventas,
d.gastos))])
.range([dashboardConfig.height - dashboardConfig.margin.bottom,
dashboardConfig.margin.top])
.nice();
const lineVentas = d3.line()
.x(d => xScale(d.mes) + xScale.bandwidth()/2)
.y(d => yScale(d.ventas))
.curve(d3.curveMonotoneX);
const lineGastos = d3.line()
.x(d => xScale(d.mes) + xScale.bandwidth()/2)
.y(d => yScale(d.gastos))
.curve(d3.curveMonotoneX);
svg.append("path")
.datum(datosFiltrados)
.attr("fill", "none")
.attr("stroke", dashboardConfig.colors.ventasColor)
.attr("stroke-width", 3)
.attr("d", lineVentas);
// Línea de gastos
svg.append("path")
.datum(datosFiltrados)
.attr("fill", "none")
.attr("stroke", dashboardConfig.colors.gastosColor)
.attr("stroke-width", 3)
.attr("d", lineGastos);
// Puntos de ventas
svg.selectAll(".dot-ventas")
.data(datosFiltrados)
.enter()
.append("circle")
.attr("class", "dot-ventas")
.attr("cx", d => xScale(d.mes) + xScale.bandwidth()/2)
.attr("cy", d => yScale(d.ventas))
.attr("r", 5)
.attr("fill", dashboardConfig.colors.ventasColor)
.attr("stroke", "white")
.attr("stroke-width", 2);
// Puntos de gastos
svg.selectAll(".dot-gastos")
.data(datosFiltrados)
.enter()
.append("circle")
.attr("class", "dot-gastos")
.attr("cx", d => xScale(d.mes) + xScale.bandwidth()/2)
.attr("cy", d => yScale(d.gastos))
.attr("r", 5)
.attr("fill", dashboardConfig.colors.gastosColor)
.attr("stroke", "white")
.attr("stroke-width", 2);
// Ejes
svg.append("g")
.attr("transform", `translate(0,${dashboardConfig.height -
dashboardConfig.margin.bottom})`)
.call(d3.axisBottom(xScale));
svg.append("g")
.attr("transform", `translate(${dashboardConfig.margin.left},0)`)
.call(d3.axisLeft(yScale).tickFormat(d => `${d}`));
// Leyenda
const legend = svg.append("g")
.attr("transform", `translate(${dashboardConfig.width - 150}, 50)`);
legend.append("line")
.attr("x1", 0).attr("x2", 20)
.attr("y1", 0).attr("y2", 0)
.attr("stroke", dashboardConfig.colors.ventasColor)
.attr("stroke-width", 3);
legend.append("text")
.attr("x", 25).attr("y", 5)
.text("Ventas")
.style("font-size", "12px");
legend.append("line")
.attr("x1", 0).attr("x2", 20)
.attr("y1", 20).attr("y2", 20)
.attr("stroke", dashboardConfig.colors.gastosColor)
.attr("stroke-width", 3);
legend.append("text")
.attr("x", 25).attr("y", 25)
.text("Gastos")
.style("font-size", "12px");
// Título
svg.append("text")
.attr("x", dashboardConfig.width / 2)
.attr("y", 25)
.attr("text-anchor", "middle")
.style("font-size", "16px")
.style("font-weight", "bold")
.style("fill", "#2c3e50")
.text("Tendencia de Ventas vs Gastos");
return svg.node();
}