From 20c1d02b40bcb9abb5882d0503e596c82e9819bb Mon Sep 17 00:00:00 2001 From: Tyler Hoang Date: Fri, 15 May 2026 01:50:15 -0700 Subject: Refine Lumi stats and detail UX --- templates/detail.html | 82 +++++++++++------- templates/index.html | 10 ++- templates/stats.html | 231 +++++++++++++++++++++++++++++++++----------------- 3 files changed, 210 insertions(+), 113 deletions(-) (limited to 'templates') diff --git a/templates/detail.html b/templates/detail.html index f51372d..94612d7 100644 --- a/templates/detail.html +++ b/templates/detail.html @@ -27,7 +27,7 @@
-

{{ film.shelf|title }} Entry

+

Back to {{ film.shelf|title }}

{{ film.title }}

{% if film.original_title %}

{{ film.original_title }}

@@ -66,31 +66,51 @@
{% endif %} +
+ {{ film.shelf|title }} + {% if film.date_watched %}{{ film.date_watched }}{% endif %} + {% if film.runtime %}{{ film.runtime }} min{% endif %} + {% if film.rewatch %}Rewatch{% if film.rewatch_count %} #{{ film.rewatch_count }}{% endif %}{% endif %} + {% if film.stars %}{% for _ in range(film.stars) %}✦{% endfor %}{% endif %} +
- Edit - {% if film.shelf == 'queue' %} - Mark watched -
- -
- {% elif film.shelf == 'diary' %} -
- -
-
- -
- {% else %} - Mark watched -
- +
+ {% if film.shelf == 'queue' %} + Mark watched + {% elif film.shelf == 'abandoned' %} + Mark watched + {% else %} + Edit entry + {% endif %} +
+
+ {% if film.shelf != 'diary' %} + Edit + {% endif %} + {% if film.shelf == 'queue' %} + + + + {% elif film.shelf == 'diary' %} +
+ +
+
+ +
+ {% else %} +
+ +
+ {% endif %} +
+
+
+
- {% endif %} -
- -
+
@@ -191,6 +211,15 @@
+
+

Notes

+ {% if film.notes %} +
{{ film.notes }}
+ {% else %} +

No notes saved.

+ {% endif %} +
+ {% if tmdb_context %}

Summary

@@ -209,15 +238,6 @@
{% endif %} -
-

Notes

- {% if film.notes %} -
{{ film.notes }}
- {% else %} -

No notes saved.

- {% endif %} -
- {% if rewatch_history|length > 1 %}

Rewatches

diff --git a/templates/index.html b/templates/index.html index 7e74c07..5820cf4 100644 --- a/templates/index.html +++ b/templates/index.html @@ -10,9 +10,13 @@

{{ shelf_meta.empty_text }}

-
- Entries - {{ total_films or 0 }} +
+ {% for stat in shelf_snapshot %} +
+ {{ stat.label }} + {{ stat.value }} +
+ {% endfor %}
{% if active_shelf == 'queue' %} Surprise me 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 %} -
-
-
-

Stats

-

Watching patterns

-
+
+
+

Stats

+

Watching patterns

+

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.

+
+
-
-
-
-
- Films watched - -
-
- Total runtime - -
-
-
+
+
+ Films watched + +
+
+ Total runtime + +
+
+ Average stars + +
+
+ Rewatch rate + +
+
-
+
+
+ Top director + +

No repeat viewings yet.

+
+
+ Most active month + +

No monthly cluster yet.

+
+
+ Countries logged + +

Based on films with country metadata.

+
+
+ +
+
-

World Map

-

Films per country

+

World map

+

Films by country

+

The map functions as a reading aid. Hover to inspect where your diary clusters geographically.

-
-
- Hover a country - Film count will appear here. +
+
+
+ Hover a country + Film count will appear here. +
-
+
-

Heatmap

+

Calendar

Past 365 days

+

A density view of your diary cadence over the trailing year.

-
-
-
-

Directors

-

Most watched

+
+
+
+
+

Directors

+

Most watched

+
-
-
    -
    - -
    -
    -
    -

    Stars

    -

    Distribution

    +
      +
      + +
      +
      +
      +

      Genres

      +

      Most watched

      +
      -
      -
      -
      - -
      -
      -
      -

      Films

      -

      Rewatches

      +
        +
        + +
        +
        +
        +

        Companions

        +

        Watched with

        +
        -
        -
        +
          +
          -
          -
          -
          -

          Companions

          -

          Watched with

          +
          +
          +
          +
          +

          Stars

          +

          Distribution

          +
          -
          -
            -
            - -
            -
            -
            -

            Genres

            -

            Most watched

            +
            +
            + +
            +
            +
            +

            Films

            +

            Rewatches

            +
            - -
              -
              - -
              -
              -
              -

              Decades

              -

              By era

              +
              +
              + +
              +
              +
              +

              Decades

              +

              By era

              +
              - -
                +
                  +
                  {% 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) => `
                1. ${item.director}${item.count}
                2. @@ -200,7 +259,7 @@ const gap = item.days_between != null ? `${item.days_between}d apart` : ""; - return `
                  + return `
                  ${item.title} ${gap}${drift}${item.watches}×
                  `; @@ -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(); -- cgit v1.3-2-g0d8e