diff --git a/apps/frontend/src/chart.mjs b/apps/frontend/src/chart.mjs index 01d1f6d..e27fb9a 100644 --- a/apps/frontend/src/chart.mjs +++ b/apps/frontend/src/chart.mjs @@ -21,12 +21,13 @@ export class LineChart extends HTMLElement { const data = this.datasets.data.toArray() const wrapper = document.createElement('div') wrapper.style.position = 'relative' - // wrapper.style.maxWidth = '400px' + wrapper.style.maxWidth = '850px' + // wrapper.style.padding = '12px' // wrapper.style.maxHeight = '150px' this.#canvas = document.createElement('canvas') wrapper.appendChild(this.#canvas) this.#shadow.appendChild(wrapper) - + Chart.defaults.font.family = 'Lexend' new Chart(this.#canvas, { type: 'line', data: { @@ -34,8 +35,9 @@ export class LineChart extends HTMLElement { datasets: [ { data, - borderColor: '#8c3a96', - fill: false, + borderColor: '#8c3a96aa', + backgroundColor: '#8c3a9622', + fill: true, tension: 0.4, }, ], @@ -44,14 +46,32 @@ export class LineChart extends HTMLElement { responsive: true, animation: false, events: [], + layout: { + padding: { + right: 12, + }, + }, plugins: { legend: { display: false, }, }, scales: { - x: { display: true, title: { display: true } }, - y: { display: true, title: { display: true, text: 'Value' } }, + x: { + display: true, + grid: { drawTicks: false, display: true }, + ticks: { padding: 0, align: 'inner', padding: 5 }, + }, + y: { + display: true, + grid: { drawTicks: false, display: true }, + ticks: { + padding: 5, + // mirror: true, + includeBounds: false, + backdropPadding: 0, + }, + }, }, }, }) diff --git a/apps/frontend/src/frontend/view/body/body.gleam b/apps/frontend/src/frontend/view/body/body.gleam index 1d115b1..69733e2 100644 --- a/apps/frontend/src/frontend/view/body/body.gleam +++ b/apps/frontend/src/frontend/view/body/body.gleam @@ -11,11 +11,10 @@ import gleam/bool import gleam/dict import gleam/float import gleam/int -import gleam/io import gleam/list import gleam/result import gleam/string -import line_chart +import line_chart.{Dataset} import lustre/attribute as a import lustre/element as el import lustre/element/html as h @@ -239,6 +238,28 @@ fn format_huge_number(number: Int) { } } +fn analytics_box(title: String, count: Int) { + h.div([a.class("analytics-box")], [ + h.div([a.class("analytics-title")], [h.text(title)]), + h.text(format_huge_number(count)), + ]) +} + +fn analytics_chart(model: Model) { + let data = model.timeseries + use <- bool.guard(when: list.is_empty(data), return: el.none()) + line_chart.line_chart({ + let acc = Dataset([], []) + use Dataset(dates, value), #(count, date) <- list.fold(data, acc) + let day = birl.get_day(date) + let label = + [day.year, day.month, day.date] + |> list.map(int.to_string) + |> string.join("/") + Dataset([label, ..dates], [count, ..value]) + }) +} + pub fn body(model: Model) { case model.route { router.Home -> h.main([a.class("main")], [view_search_input(model)]) @@ -246,54 +267,34 @@ pub fn body(model: Model) { router.Analytics -> el.fragment([ sidebar(model), - h.main([a.class("main")], [ + h.main([a.class("main"), a.style([#("padding", "24px")])], [ + h.div([a.class("matches-titles")], [ + h.div([a.class("matches-title")], [h.text("Global analytics")]), + ]), + h.div([a.class("analytics-box-wrapper")], [ + analytics_box("Number of searches", model.total_searches), + analytics_box( + "Number of signatures indexed", + model.total_signatures, + ), + analytics_box("Number of packages indexed", model.total_packages), + ]), + h.div([a.class("matches-titles")], [ + h.div([a.class("matches-title")], [ + h.text("Searches per day — Last 30 days"), + ]), + ]), h.div( - [a.class("items-wrapper"), a.style([#("padding-left", "24px")])], [ - h.div([a.class("matches-titles")], [ - h.div([a.class("matches-title")], [h.text("Global analytics")]), - ]), - h.div([a.class("analytics-box-wrapper")], [ - h.div([a.class("analytics-box")], [ - h.div([a.class("analytics-title")], [ - h.text("Number of searches"), - ]), - h.text(format_huge_number(model.total_searches)), - ]), - h.div([a.class("analytics-box")], [ - h.div([a.class("analytics-title")], [ - h.text("Number of signatures indexed"), - ]), - h.text(format_huge_number(model.total_signatures)), - ]), - h.div([a.class("analytics-box")], [ - h.div([a.class("analytics-title")], [ - h.text("Number of packages indexed"), - ]), - h.text(format_huge_number(model.total_packages)), - ]), - ]), - h.div([a.class("matches-titles")], [ - h.div([a.class("matches-title")], [h.text("Last 30 days")]), - ]), - h.div([a.style([#("width", "auto"), #("height", "500px")])], [ - case model.timeseries { - [] -> el.none() - data -> { - line_chart.line_chart({ - use line_chart.Dataset(dates, value), #(count, date) <- list.fold( - data, - line_chart.Dataset([], []), - ) - line_chart.Dataset([birl.to_iso8601(date), ..dates], [ - count, - ..value - ]) - }) - } - }, + a.style([ + #("max-width", "850px"), + #("border", "1px solid var(--border-color)"), + #("border-radius", "10px"), + #("overflow", "hidden"), + #("padding", "12px"), ]), ], + [analytics_chart(model)], ), ]), ]) diff --git a/apps/frontend/src/stylesheets/all.css b/apps/frontend/src/stylesheets/all.css index b648e63..94c4fd3 100644 --- a/apps/frontend/src/stylesheets/all.css +++ b/apps/frontend/src/stylesheets/all.css @@ -565,8 +565,8 @@ lazy-node:has(:not(:defined)) { line-height: 1.75; width: 250px; height: 250px; - /* height: 200px; */ justify-content: space-between; + flex-shrink: 0; } .analytics-title { @@ -576,4 +576,5 @@ lazy-node:has(:not(:defined)) { .analytics-box-wrapper { display: flex; gap: 24px; + flex-wrap: wrap; }