summaryrefslogtreecommitdiff
path: root/static/app.js
diff options
context:
space:
mode:
authorTyler Hoang <tyler@tylerhoang.xyz>2026-05-12 03:15:17 -0700
committerTyler Hoang <tyler@tylerhoang.xyz>2026-05-12 03:15:17 -0700
commit4279408876268f4960c98492d3814f5475e36e38 (patch)
tree9fc4828768534368a575c2e60d39d02de0973b79 /static/app.js
parent61d68b339fee628c258e15c8664b6bcad2e70ab1 (diff)
Add stats totals, runtime summary, and duplicate detection on add form
- Stats page now shows total films watched and total runtime (formatted as Xd Yh) in an overview panel above the world map - /stats/data endpoint includes total_runtime_minutes in payload - New GET /films/find endpoint returns all shelf matches for a tmdb_id - Add film form shows an inline notice when the selected TMDB film is already logged, with shelf name, date, and a link to the entry - Update CLAUDE.md and README to reflect current auth, OMDb, and router/service structure Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'static/app.js')
-rw-r--r--static/app.js25
1 files changed, 21 insertions, 4 deletions
diff --git a/static/app.js b/static/app.js
index 938cf70..04997a0 100644
--- a/static/app.js
+++ b/static/app.js
@@ -111,15 +111,17 @@ const applyResult = async (film) => {
setValue("#tmdb_id", film.tmdb_id || "");
setPoster(film.poster_url);
- // Check if this is the add form (not edit)
+ const duplicateNotice = document.getElementById("duplicate-notice");
+ if (duplicateNotice) duplicateNotice.hidden = true;
+
const isAddForm = window.location.pathname.endsWith("/films/new");
if (film.tmdb_id) {
try {
- // Fetch full detail for genre and check for rewatches in parallel
- const [detailResponse, rewatchResponse] = await Promise.all([
+ const [detailResponse, rewatchResponse, findResponse] = await Promise.all([
fetch(`/tmdb/detail/${film.tmdb_id}`),
isAddForm ? fetch(`/films/check-rewatch?tmdb_id=${film.tmdb_id}`) : Promise.resolve(null),
+ isAddForm ? fetch(`/films/find?tmdb_id=${film.tmdb_id}`) : Promise.resolve(null),
]);
if (detailResponse.ok) {
@@ -137,8 +139,23 @@ const applyResult = async (film) => {
}
}
}
+
+ if (findResponse && findResponse.ok && duplicateNotice) {
+ const found = await findResponse.json();
+ if (found.matches && found.matches.length > 0) {
+ const shelfLabel = { diary: "Diary", queue: "Queue", abandoned: "Abandoned" };
+ const parts = found.matches.map((m) => {
+ const label = shelfLabel[m.shelf] || m.shelf;
+ const date = m.date_watched
+ ? ` · ${new Date(m.date_watched + "T00:00:00").toLocaleDateString(undefined, { month: "short", year: "numeric" })}`
+ : "";
+ return `<a href="/films/${m.id}" class="inline-link">${label}${date}</a>`;
+ });
+ duplicateNotice.innerHTML = `Already logged — ${parts.join(", ")}`;
+ duplicateNotice.hidden = false;
+ }
+ }
} catch (error) {
- // Fail silently if detail/rewatch fetch fails
console.error("Failed to fetch details", error);
}
}