improve analytics chart with hourly today view and readable labels

- add visitors_by_hour query for hourly breakdown on "today" period
- replace SVG-only chart with HTML/CSS grid layout (bars + labels)
- Y-axis scale with nice rounded max, midpoint, and zero
- X-axis date labels (formatted as "Feb 18") spaced evenly
- adaptive bar gaps (1px for sparse data, 0 for 365-day dense view)
- labels use real HTML text so they're readable on mobile

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jamey
2026-02-22 23:28:35 +00:00
parent 65e646a7eb
commit 08fcd60eb6
3 changed files with 130 additions and 35 deletions

View File

@@ -148,6 +148,29 @@ defmodule Berrypod.Analytics do
|> Repo.all()
end
@doc """
Hourly visitor counts for the trend chart (used for "today" period).
Returns a list of `%{hour: integer, visitors: integer}` maps for all 24 hours.
"""
def visitors_by_hour(date_range) do
counts =
base_query(date_range)
|> where([e], e.name == "pageview")
|> group_by([e], fragment("CAST(strftime('%H', ?) AS INTEGER)", e.inserted_at))
|> select([e], %{
hour: fragment("CAST(strftime('%H', ?) AS INTEGER)", e.inserted_at),
visitors: count(e.visitor_hash, :distinct)
})
|> Repo.all()
|> Map.new(&{&1.hour, &1.visitors})
# Fill in all 24 hours so the chart has no gaps
Enum.map(0..23, fn h ->
%{hour: h, visitors: Map.get(counts, h, 0)}
end)
end
@doc """
Top pages by unique visitors.
"""