diff --git a/frontend/css/style.css b/frontend/css/style.css index 72625834a..25057d477 100644 --- a/frontend/css/style.css +++ b/frontend/css/style.css @@ -7501,3 +7501,26 @@ dialog.book-preview-modal::backdrop { font-size: 0.95rem; } } +/* ── Reading Time Badge — Issue #606 ── */ +.reading-time-badge, +.reading-time-remaining { + display: inline-flex; + align-items: center; + gap: 5px; + font-size: 0.7rem; + padding: 3px 8px; + border-radius: 10px; + margin-bottom: 6px; +} + +.reading-time-badge { + background: rgba(212, 175, 55, 0.15); + color: var(--accent-gold); + border: 1px solid rgba(212, 175, 55, 0.3); +} + +.reading-time-remaining { + background: rgba(29, 158, 117, 0.12); + color: #1D9E75; + border: 1px solid rgba(29, 158, 117, 0.25); +} \ No newline at end of file diff --git a/frontend/js/app.js b/frontend/js/app.js index bc22b829b..d45334e17 100644 --- a/frontend/js/app.js +++ b/frontend/js/app.js @@ -79,6 +79,8 @@ // API_BASE and MOOD_API_BASE are declared globally in config.js (loaded first). // Do NOT re-declare them here — use the globals from config.js directly. +import { calcReadingTime, calcRemainingTime } from './readingTime.js'; + if (typeof window.IS_DEV === 'undefined') { window.IS_DEV = typeof window !== 'undefined' && ['localhost', '127.0.0.1', '::1'].includes(window.location.hostname); } @@ -710,6 +712,17 @@ class BookRenderer { Read Details + ${(() => { + const pages = bookData?.volumeInfo?.pageCount; + const { label: totalLabel } = calcReadingTime(pages); + const { label: remainLabel } = calcRemainingTime(pages, progress); + if (!totalLabel) return ''; + return ` +
+ + ${shelf === 'current' && remainLabel ? remainLabel + ' left' : totalLabel} +
`; +})()} ${shelf === 'current' ? `
diff --git a/frontend/js/library-3d.js b/frontend/js/library-3d.js index fcf93481d..25cb27623 100644 --- a/frontend/js/library-3d.js +++ b/frontend/js/library-3d.js @@ -4,6 +4,7 @@ */ // Sample books data for demonstration +import { calcReadingTime, calcRemainingTime } from './readingTime.js'; const SAMPLE_BOOKS = { current: [ { @@ -1869,6 +1870,16 @@ class BookshelfRenderer3D { authorSpan.className = 'spine-author'; authorSpan.textContent = book.author ? book.author.split(' ').pop() : ''; face.appendChild(authorSpan); + // Issue #606 — Reading Time Badge on spine +const pages = book.pageCount || book.page_count || book.volumeInfo?.pageCount; +const { label: timeLabel } = calcReadingTime(pages); +if (timeLabel) { + const timeBadge = document.createElement('div'); + timeBadge.className = 'reading-time-badge'; + timeBadge.style.cssText = 'writing-mode: vertical-rl; transform: rotate(180deg); font-size: 0.55rem; margin-top: 6px; padding: 2px 4px;'; + timeBadge.innerHTML = ` ${timeLabel}`; + face.appendChild(timeBadge); +} if (traits.pattern.includes('ornament')) { const ornament = document.createElement('div'); @@ -2262,6 +2273,22 @@ class BookshelfRenderer3D { if (titleEl) titleEl.textContent = book.title; if (authorEl) authorEl.textContent = book.author; // Removed "by" prefix to match design + // Issue #606 — Populate reading time in modal +const readingTimeStat = document.getElementById('modal-reading-time-stat'); +const readingTimeLabel = document.getElementById('modal-reading-time-label'); +const pages = book.pageCount || book.page_count; +const progress = typeof book.progress === 'number' ? book.progress : 0; +const { label: totalTimeLabel } = calcReadingTime(pages); +const { label: remainLabel } = calcRemainingTime(pages, progress); + +if (readingTimeStat && totalTimeLabel) { + readingTimeStat.style.display = 'block'; + if (readingTimeLabel) { + readingTimeLabel.textContent = book.shelfType === 'current' && remainLabel + ? `${remainLabel} left` + : totalTimeLabel; + } +} if (starsEl) starsEl.textContent = this.getStarRating(book.rating); if (scoreEl) scoreEl.textContent = (book.rating != null ? book.rating.toFixed(1) : 'N/A'); if (countEl) countEl.textContent = `(${book.ratingCount} ratings)`; diff --git a/frontend/pages/library.html b/frontend/pages/library.html index 7a67ad7e2..952627600 100644 --- a/frontend/pages/library.html +++ b/frontend/pages/library.html @@ -435,6 +435,14 @@

1,234 ratings
+ +