diff options
Diffstat (limited to 'templates/stats.html')
| -rw-r--r-- | templates/stats.html | 223 |
1 files changed, 148 insertions, 75 deletions
diff --git a/templates/stats.html b/templates/stats.html index 6e3a987..efb952c 100644 --- a/templates/stats.html +++ b/templates/stats.html @@ -3,113 +3,146 @@ {% block title %}Stats · Lumière{% endblock %} {% block content %} - <section class="page-heading"> - <div class="page-heading-row"> - <div> - <p class="eyebrow">Stats</p> - <h1>Watching patterns</h1> - </div> + <section class="stats-hero"> + <div class="stats-hero-copy"> + <p class="eyebrow">Stats</p> + <h1>Watching patterns</h1> + <p class="stats-hero-text">A research spread for your diary: the geography of what you watch, the people and genres that recur, and the rhythms that define a year of logging.</p> + </div> + <div class="stats-hero-actions"> <a class="button-link" href="/stats/year-in-review">Year in review</a> </div> </section> - <section class="stats-layout"> - <section class="stats-panel stats-panel-wide"> - <div class="stats-overview-row"> - <div class="stats-metric"> - <span class="eyebrow">Films watched</span> - <strong id="stats-total-films">—</strong> - </div> - <div class="stats-metric"> - <span class="eyebrow">Total runtime</span> - <strong id="stats-total-runtime">—</strong> - </div> - </div> - </section> + <section class="stats-summary"> + <article class="stats-summary-card"> + <span class="summary-label">Films watched</span> + <strong id="stats-total-films">—</strong> + </article> + <article class="stats-summary-card"> + <span class="summary-label">Total runtime</span> + <strong id="stats-total-runtime">—</strong> + </article> + <article class="stats-summary-card"> + <span class="summary-label">Average stars</span> + <strong><span id="stats-average-stars">—</span><span class="stats-average-mark" aria-hidden="true">✦</span></strong> + </article> + <article class="stats-summary-card"> + <span class="summary-label">Rewatch rate</span> + <strong id="stats-rewatch-rate">—</strong> + </article> + </section> + + <section class="stats-context-strip"> + <article class="stats-context-card"> + <span class="summary-label">Top director</span> + <strong id="stats-top-director">—</strong> + <p id="stats-top-director-meta" class="stats-context-meta">No repeat viewings yet.</p> + </article> + <article class="stats-context-card"> + <span class="summary-label">Most active month</span> + <strong id="stats-top-month">—</strong> + <p id="stats-top-month-meta" class="stats-context-meta">No monthly cluster yet.</p> + </article> + <article class="stats-context-card"> + <span class="summary-label">Countries logged</span> + <strong id="stats-country-count">—</strong> + <p class="stats-context-meta">Based on films with country metadata.</p> + </article> + </section> - <section class="stats-panel stats-panel-wide"> + <section class="stats-research"> + <section class="stats-panel stats-panel-feature"> <div class="stats-panel-header"> <div> - <p class="eyebrow">World Map</p> - <h2>Films per country</h2> + <p class="eyebrow">World map</p> + <h2>Films by country</h2> </div> + <p class="stats-panel-note">The map functions as a reading aid. Hover to inspect where your diary clusters geographically.</p> </div> - <div id="world-map" class="stats-map" aria-label="World map of watched films"></div> - <div id="map-readout" class="map-readout" aria-live="polite"> - <strong>Hover a country</strong> - <span>Film count will appear here.</span> + <div class="stats-map-shell"> + <div id="world-map" class="stats-map" aria-label="World map of watched films"></div> + <div id="map-readout" class="map-readout" aria-live="polite"> + <strong>Hover a country</strong> + <span>Film count will appear here.</span> + </div> </div> <div id="map-tooltip" class="stats-tooltip" hidden></div> </section> - <section class="stats-panel stats-panel-wide"> + <section class="stats-panel stats-panel-feature"> <div class="stats-panel-header"> <div> - <p class="eyebrow">Heatmap</p> + <p class="eyebrow">Calendar</p> <h2>Past 365 days</h2> </div> + <p class="stats-panel-note">A density view of your diary cadence over the trailing year.</p> </div> <div id="watch-heatmap" class="heatmap-shell" aria-label="Daily watch heatmap"></div> </section> - <section class="stats-panel"> - <div class="stats-panel-header"> - <div> - <p class="eyebrow">Directors</p> - <h2>Most watched</h2> + <section class="stats-column"> + <section class="stats-panel"> + <div class="stats-panel-header"> + <div> + <p class="eyebrow">Directors</p> + <h2>Most watched</h2> + </div> </div> - </div> - <ol id="top-directors" class="stats-list"></ol> - </section> + <ol id="top-directors" class="stats-list"></ol> + </section> - <section class="stats-panel"> - <div class="stats-panel-header"> - <div> - <p class="eyebrow">Stars</p> - <h2>Distribution</h2> + <section class="stats-panel"> + <div class="stats-panel-header"> + <div> + <p class="eyebrow">Genres</p> + <h2>Most watched</h2> + </div> </div> - </div> - <div id="star-distribution" class="stats-bars"></div> - </section> + <ol id="top-genres" class="stats-list"></ol> + </section> - <section class="stats-panel"> - <div class="stats-panel-header"> - <div> - <p class="eyebrow">Films</p> - <h2>Rewatches</h2> + <section class="stats-panel"> + <div class="stats-panel-header"> + <div> + <p class="eyebrow">Companions</p> + <h2>Watched with</h2> + </div> </div> - </div> - <div id="rewatch-patterns-list" class="stats-list"></div> + <ol id="watched-with" class="stats-list"></ol> + </section> </section> - <section class="stats-panel"> - <div class="stats-panel-header"> - <div> - <p class="eyebrow">Companions</p> - <h2>Watched with</h2> + <section class="stats-column"> + <section class="stats-panel"> + <div class="stats-panel-header"> + <div> + <p class="eyebrow">Stars</p> + <h2>Distribution</h2> + </div> </div> - </div> - <ol id="watched-with" class="stats-list"></ol> - </section> + <div id="star-distribution" class="stats-bars"></div> + </section> - <section class="stats-panel"> - <div class="stats-panel-header"> - <div> - <p class="eyebrow">Genres</p> - <h2>Most watched</h2> + <section class="stats-panel"> + <div class="stats-panel-header"> + <div> + <p class="eyebrow">Films</p> + <h2>Rewatches</h2> + </div> </div> - </div> - <ol id="top-genres" class="stats-list"></ol> - </section> + <div id="rewatch-patterns-list" class="stats-list"></div> + </section> - <section class="stats-panel"> - <div class="stats-panel-header"> - <div> - <p class="eyebrow">Decades</p> - <h2>By era</h2> + <section class="stats-panel"> + <div class="stats-panel-header"> + <div> + <p class="eyebrow">Decades</p> + <h2>By era</h2> + </div> </div> - </div> - <ol id="film-decades" class="stats-list"></ol> + <ol id="film-decades" class="stats-list"></ol> + </section> </section> </section> {% endblock %} @@ -133,6 +166,13 @@ return new Date(`${dateString}T00:00:00`).toLocaleDateString(undefined, { month: "short" }); } + function formatStatsMonth(monthKey) { + if (!monthKey) return "—"; + const [year, month] = monthKey.split("-"); + const date = new Date(Number(year), Number(month) - 1, 1); + return date.toLocaleDateString(undefined, { month: "long", year: "numeric" }); + } + function formatCountryCode(value) { return String(value).padStart(3, "0"); } @@ -156,6 +196,25 @@ const totalRuntime = document.getElementById("stats-total-runtime"); if (totalRuntime) totalRuntime.textContent = formatRuntime(data.total_runtime_minutes); + const averageStars = document.getElementById("stats-average-stars"); + if (averageStars) averageStars.textContent = data.average_stars ? data.average_stars.toFixed(1) : "—"; + + const rewatchRate = document.getElementById("stats-rewatch-rate"); + if (rewatchRate) rewatchRate.textContent = data.rewatch_rate ? `${Math.round(data.rewatch_rate.rate * 100)}%` : "—"; + + const topDirector = document.getElementById("stats-top-director"); + const topDirectorMeta = document.getElementById("stats-top-director-meta"); + if (topDirector) topDirector.textContent = data.top_director ? data.top_director.director : "—"; + if (topDirectorMeta) topDirectorMeta.textContent = data.top_director ? `${data.top_director.count} films logged` : "No repeat viewings yet."; + + const topMonth = document.getElementById("stats-top-month"); + const topMonthMeta = document.getElementById("stats-top-month-meta"); + if (topMonth) topMonth.textContent = data.top_month ? formatStatsMonth(data.top_month.month) : "—"; + if (topMonthMeta) topMonthMeta.textContent = data.top_month ? `${data.top_month.count} diary entries` : "No monthly cluster yet."; + + const countryCount = document.getElementById("stats-country-count"); + if (countryCount) countryCount.textContent = data.films_per_country.length; + const topDirectors = document.getElementById("top-directors"); topDirectors.innerHTML = data.most_watched_directors.slice(0, 8).map((item) => ` <li><span>${item.director}</span><strong>${item.count}</strong></li> @@ -200,7 +259,7 @@ const gap = item.days_between != null ? `<span style="color:var(--subtle);font-size:13px">${item.days_between}d apart</span>` : ""; - return `<div class="stats-list-row"> + return `<div class="stats-list-row stats-list-row-rewatch"> <span>${item.title}</span> <span style="display:flex;gap:12px;align-items:center">${gap}${drift}<strong>${item.watches}×</strong></span> </div>`; @@ -218,6 +277,19 @@ return { ...item, dateObj: date }; }); + const containerWidth = container.clientWidth || 720; + const maxCell = 13; + const minCell = 8; + const weekdayWidth = containerWidth < 640 ? 20 : 34; + const gap = containerWidth < 640 ? 2 : 4; + const availableWidth = Math.max(53 * minCell, containerWidth - weekdayWidth - 8); + const computedCell = Math.floor((availableWidth - (52 * gap)) / 53); + const cellSize = Math.max(minCell, Math.min(maxCell, computedCell)); + + container.style.setProperty("--heatmap-cell", `${cellSize}px`); + container.style.setProperty("--heatmap-gap", `${gap}px`); + container.style.setProperty("--heatmap-weekday-width", `${weekdayWidth}px`); + const firstWeekStart = new Date(parsedDays[0].dateObj); const mondayOffset = (firstWeekStart.getDay() + 6) % 7; firstWeekStart.setDate(firstWeekStart.getDate() - mondayOffset); @@ -343,6 +415,7 @@ renderLists(data); renderHeatmap(data.films_per_day_365); await renderMap(data); + window.addEventListener("resize", () => renderHeatmap(data.films_per_day_365)); } bootStats(); |
