Skip to content

Add book reading progress tracking#22

Merged
niklhut merged 3 commits into
mainfrom
feat/add-book-reading-progress-tracking
May 16, 2026
Merged

Add book reading progress tracking#22
niklhut merged 3 commits into
mainfrom
feat/add-book-reading-progress-tracking

Conversation

@niklhut
Copy link
Copy Markdown
Owner

@niklhut niklhut commented May 16, 2026

Summary

  • Add per-book reading progress tracking with unread/reading/read states, page or percent progress, and started/finished dates
  • Persist progress through the backend with a dedicated reading update API, schema updates, and migration support
  • Replace the inline editor with a modal on the book detail page and keep the summary view separate
  • Add unit coverage for progress normalization, API updates, and book detail hydration

Testing

  • pnpm lint
  • pnpm typecheck
  • pnpm test:unit

Summary by CodeRabbit

  • New Features

    • Track per-book reading progress (status: unread/reading/read), current page, percent, and optional start/finish dates
    • Edit progress in a modal with page/percent modes, conversions, change detection, and keyboard save
    • Visual progress indicator with an Update action
    • Server endpoint and validation to persist and normalize reading progress
  • Tests

    • Unit tests for normalization logic and API route behavior

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 16, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 135813c0-1359-4fab-ad5a-1bfad0f6a8f5

📥 Commits

Reviewing files that changed from the base of the PR and between b19c904 and 128bd96.

📒 Files selected for processing (1)
  • app/components/BookReadingProgressModal.vue
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/components/BookReadingProgressModal.vue

📝 Walkthrough

Walkthrough

Adds end-to-end reading progress: shared types and schemas, normalization logic, DB columns and constraints, repository/service methods, a PUT API, display and edit Vue components, library-page optimistic saves with sequencing, and unit tests.

Changes

Reading Progress Feature

Layer / File(s) Summary
Reading progress types and validation schemas
shared/types/book.ts, shared/utils/reading-progress.ts, shared/utils/schemas.ts
ReadingStatus and ReadingProgress types, input/detail interfaces, nullableDateSchema, and bookReadingProgressSchema are added.
Database schema for reading progress
server/db/schema/domain.ts
userBooks table extends with readingStatus, currentPage, progressPercent, startedAt, finishedAt and adds check constraints for valid status and progress bounds.
Reading progress normalization
shared/utils/reading-progress.ts, test/unit/reading-progress.test.ts
normalizeReadingProgress merges overrides with persisted state, validates page counts, clamps/derives percent, enforces status transitions and timestamp normalization; tests validate downgrades and completion behavior.
Repository layer for reading progress
server/repositories/book.repository.ts
updateReadingProgress persists reading fields; getUserBookWithDetails now includes a readingProgress object with null-safe timestamp conversion.
Service layer orchestration
server/services/book.service.ts, server/utils/effect.ts
Introduces InvalidReadingProgressError, wraps normalization to map thrown errors to that tag, implements BookService.updateReadingProgress, exports helper, and maps the error to HTTP 400.
PUT API endpoint for reading progress
server/api/books/[id]/reading.put.ts, test/unit/server/api/books/[id]/reading.put.test.ts
Endpoint validates route param and request body, delegates to service, and returns { success: true, readingProgress }; tests cover success cases and validation failures.
Reading progress display component
app/components/BookReadingProgress.vue
Component renders status/progress labels, capped progress bar, optional localized started/finished dates, and emits edit when Update is clicked.
Reading progress edit modal
app/components/BookReadingProgressModal.vue
Modal with status selection, pages/percent mode, numeric and date inputs, conversion logic, comprehensive change detection, and emits save:progress with parsed numbers or null dates.
Library page integration
app/pages/library/[id].vue
Adds modal open/saving state, readingRequestId sequencing, and saveReadingProgress that does optimistic updates, persists via PUT, applies only latest responses, and recovers on error.
Test utilities and fixtures
test/unit/server/api/_helpers/api-route.ts, test/unit/server/api/books/[id]/index.get.test.ts
Test helper mocks updateReadingProgress and wires it through globals; GET fixture expanded to include full readingProgress in mocked book.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I nibble lines of code and dates,
Track pages turned and tiny states,
From schema roots to modal lights,
Progress saved in optimistic flights—
A rabbit cheers as each book finds its place.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and directly describes the main feature being added: book reading progress tracking functionality across the entire changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/add-book-reading-progress-tracking

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

app/components/BookReadingProgressModal.vue

Oops! Something went wrong! :(

ESLint: 10.3.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/.nuxt/eslint.config.mjs' imported from /eslint.config.mjs
at finalizeResolution (node:internal/modules/esm/resolve:271:11)
at moduleResolve (node:internal/modules/esm/resolve:861:10)
at defaultResolve (node:internal/modules/esm/resolve:988:11)
at #cachedDefaultResolve (node:internal/modules/esm/loader:697:20)
at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:714:38)
at ModuleLoader.resolveSync (node:internal/modules/esm/loader:746:52)
at #resolve (node:internal/modules/esm/loader:679:17)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:599:35)
at ModuleJob.syncLink (node:internal/modules/esm/module_job:162:33)
at ModuleJob.link (node:internal/modules/esm/module_job:252:17)

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
test/unit/reading-progress.test.ts (1)

15-84: ⚡ Quick win

Add a regression test for percent-only updates with existing currentPage.

Please add a case where existing progress has currentPage set, input only provides progressPercent, and the explicit percent must be preserved. This protects PATCH semantics and catches silent overwrites.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/unit/reading-progress.test.ts` around lines 15 - 84, Add a new unit test
in the same describe block that calls normalizeReadingProgress with an existing
readingProgress that has a non-null currentPage (e.g., 120) and then supplies an
incoming update that includes only progressPercent (e.g., 75) and no
currentPage; assert that the resulting object preserves the explicit
progressPercent (75), keeps the existing currentPage unchanged, preserves
startedAt, keeps finishedAt null, and leaves status as 'reading'. Reference
normalizeReadingProgress, readingProgress, currentPage, progressPercent and
startedAt in the test so it guards against silent overwrites of percent-only
PATCH updates.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/components/BookReadingProgressModal.vue`:
- Around line 95-104: The parsed numeric helpers are returning raw numbers which
can be fractional or outside allowed ranges; update parseOptionalNumber and the
saveProgress emitter code paths (e.g., the saveProgress method and any parsing
at the 148-157 region) to normalize values by converting to integers (use
Math.round or Math.floor per UX choice), then clamp them into valid boundaries
(minimum 0 and maximum equal to the book’s totalPages or another defined max)
and only emit/save those normalized, bounded numbers; ensure the emitted payload
uses these sanitized values instead of the raw parsed values.

In `@app/pages/library/`[id].vue:
- Around line 195-224: The rollback currently uses previousProgress on a failed
request and can reintroduce stale data when responses arrive out of order;
update the catch block (where currentRequestId, readingRequestId,
previousProgress, book.value, lastConfirmedReadingProgress and
isSavingReadingProgress are referenced) to, on currentRequestId ===
readingRequestId, fetch the authoritative readingProgress from the backend (e.g.
GET `/api/books/${userBookId}` or `/api/books/${userBookId}/reading`), apply
that result to book.value and lastConfirmedReadingProgress, and show an error
toast—do not blindly restore previousProgress; ensure isSavingReadingProgress is
cleared and only ignore stale older-request errors when currentRequestId !==
readingRequestId.

In `@shared/utils/reading-progress.ts`:
- Around line 38-40: The code always recalculates progressPercent from
currentPage when currentPage exists, which overwrites an explicit
input.progressPercent; change the logic in shared/utils/reading-progress.ts so
that if input.progressPercent (or a similarly named incoming param) is
non-null/defined you use that value (clamped/rounded to 0–100) instead of
recomputing, otherwise fall back to computing
Math.round((currentPage/totalPages)*100) when currentPage and totalPages are
available; keep the existing Math.min(100, ...) bound and ensure progressPercent
remains an integer.

---

Nitpick comments:
In `@test/unit/reading-progress.test.ts`:
- Around line 15-84: Add a new unit test in the same describe block that calls
normalizeReadingProgress with an existing readingProgress that has a non-null
currentPage (e.g., 120) and then supplies an incoming update that includes only
progressPercent (e.g., 75) and no currentPage; assert that the resulting object
preserves the explicit progressPercent (75), keeps the existing currentPage
unchanged, preserves startedAt, keeps finishedAt null, and leaves status as
'reading'. Reference normalizeReadingProgress, readingProgress, currentPage,
progressPercent and startedAt in the test so it guards against silent overwrites
of percent-only PATCH updates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 23803890-699b-47fd-9d4b-529a860c4e4a

📥 Commits

Reviewing files that changed from the base of the PR and between 9e9e225 and 31bd9bf.

⛔ Files ignored due to path filters (2)
  • server/db/migrations/sqlite/0009_add_reading_progress.sql is excluded by !server/db/migrations/**
  • server/db/migrations/sqlite/meta/_journal.json is excluded by !server/db/migrations/**
📒 Files selected for processing (15)
  • app/components/BookReadingProgress.vue
  • app/components/BookReadingProgressModal.vue
  • app/pages/library/[id].vue
  • server/api/books/[id]/reading.put.ts
  • server/db/schema/domain.ts
  • server/repositories/book.repository.ts
  • server/services/book.service.ts
  • server/utils/effect.ts
  • shared/types/book.ts
  • shared/utils/reading-progress.ts
  • shared/utils/schemas.ts
  • test/unit/reading-progress.test.ts
  • test/unit/server/api/_helpers/api-route.ts
  • test/unit/server/api/books/[id]/index.get.test.ts
  • test/unit/server/api/books/[id]/reading.put.test.ts

Comment thread app/components/BookReadingProgressModal.vue
Comment thread app/pages/library/[id].vue Outdated
Comment thread shared/utils/reading-progress.ts Outdated
@niklhut
Copy link
Copy Markdown
Owner Author

niklhut commented May 16, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 16, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@niklhut niklhut merged commit 91eca0b into main May 16, 2026
3 checks passed
@niklhut niklhut deleted the feat/add-book-reading-progress-tracking branch May 16, 2026 19:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant