// app/javascript/controllers/chart_controller.js
import {Controller} from "@hotwired/stimulus"
import * as d3 from "d3";
// Connects to data-controller="chart"
export default class extends Controller {
static targets = [ "chart", "sale" ]
connect() {
this.sales = this.saleTargets
.map(sale => sale.dataset)
.map((value) => {
return { date: new Date(value.date), name: value.name, amount: +value.amount }
});
console.log(this.sales);
this.draw()
}
draw() {
const margin = { top: 70, right: 30, bottom: 40, left: 80 };
const width = 1200 - margin.left - margin.right;
const height = 500 - margin.top - margin.bottom;
// Set up the x and y scales
const x = d3.scaleTime()
.range([0, width]);
const y = d3.scaleLinear()
.range([height, 0]);
// Create the SVG element and append it to the chart container
const svg = d3.select(this.chartTarget)
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
// Define the x and y domains
x.domain(d3.extent(this.sales, d => d.date));
y.domain([0, d3.max(this.sales, d => d.amount)]);
// Add the x-axis
svg.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x)
.ticks(d3.timeMonth.every(1))
.tickFormat(d3.timeFormat("%b %Y")));
// Add the y-axis
svg.append("g")
.call(d3.axisLeft(y))
// Create the line generator
const line = d3.line()
.x(d => x(d.date))
.y(d => y(d.amount));
// Add the line to the chart
svg.append("path")
.datum(this.sales)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.attr("d", line);
}
}
<!-- a/v/pages/index.html -->
<div data-controller="chart">
<table class="w-full lg:w-2/3 border-collapse border border-slate-500 bg-red-50 table-fixed ">
<caption class="text-4xl mb-2">Best sales</caption>
<thead>
<tr class="hidden sm:table-row">
<th class="border border-gray-800 text-left px-2 py-1 bg-red-100 w-2/4">Date</th>
<th class="border border-gray-800 text-left px-2 py-1 bg-red-100">Name</th>
<th class="border border-gray-800 text-left px-2 py-1 bg-red-100">Amount</th>
</tr>
</thead>
<tbody>
<% @sales.each do |sale| %>
<%= tag.tr nil, class: "block mb-8 sm:table-row", data: { chart_target: "sale", **sale.as_json } do %>
<td class="block sm:table-cell sm:border border-gray-800 px-2 py-1 font-bold"><%= sale.date %></td>
<td class="block sm:table-cell sm:border border-gray-800 px-2 py-1"><%= sale.name %></td>
<td class="block sm:table-cell sm:border border-gray-800 px-2 py-1"><%= number_to_currency sale.amount, unit: "€" %></td>
<% end %>
<% end %>
</tbody>
</table>
<div data-chart-target="chart"></div>
</div>
# a/c/pages_controller
class PagesController < ApplicationController
class Sale
attr_reader :date, :name, :amount
def initialize(date:, name:, amount:)
@date = date
@name = name
@amount = amount
end
def as_json
{
date: date,
name: name,
amount: amount
}
end
end
def index
@sales = [
Sale.new(date: Date.parse("2024-01-02"), name: "John", amount: 100),
Sale.new(date: Date.parse("2024-02-02"), name: "Sally", amount: 2000),
Sale.new(date: Date.parse("2024-03-02"), name: "Bill", amount: 300),
Sale.new(date: Date.parse("2024-04-02"), name: "Jane", amount: 6000)
]
end
end