Skip to content

feat: improve library dashboard state management#15

Merged
niklhut merged 3 commits intomainfrom
feat/improve-dashboard-loading
Apr 8, 2026
Merged

feat: improve library dashboard state management#15
niklhut merged 3 commits intomainfrom
feat/improve-dashboard-loading

Conversation

@niklhut
Copy link
Copy Markdown
Owner

@niklhut niklhut commented Apr 7, 2026

Summary by CodeRabbit

  • New Features

    • Centralized library dashboard state for consistent pagination, caching and sync.
    • Improved synchronization when adding, bulk-adding or deleting books; loaded pages are flagged for refresh.
    • Conditional initial fetching and targeted multi-page re-syncing of the library view.
    • Scroll position restoration on library navigation.
  • Style

    • Adjusted Select button width in the library view.
  • Tests

    • Added unit tests covering dashboard state behaviors and sync logic.
  • Chores

    • Updated package manager version.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 7, 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: eb93866d-602d-47af-96ba-ff5af50a52de

📥 Commits

Reviewing files that changed from the base of the PR and between dfc983d and 31ed3ae.

📒 Files selected for processing (1)
  • app/pages/library/index.vue
✅ Files skipped from review due to trivial changes (1)
  • app/pages/library/index.vue

📝 Walkthrough

Walkthrough

Centralized client-side library dashboard state added via a new useLibraryDashboardState() composable. Components that add, remove, or fetch books now update the shared cache and mark specific loaded pages for re-sync so the dashboard refreshes only affected pages.

Changes

Cohort / File(s) Summary
New State Management
app/composables/useLibraryDashboardState.ts
Add composable exposing page, pageSize, allBooks, pagination, scrollY, sync flags, getLoadedPages(), addBook(), removeBooks(), markNeedsSync(), and clearNeedsSync().
ISBN Add Flow Integration
app/components/add/IsbnLookupTab.vue, app/composables/useIsbnScanner.ts
Both call getLoadedPages() and markNeedsSync(); IsbnLookupTab types POST response as LibraryBook, calls addBook(addedBook), then markNeedsSync(loadedPagesBeforeAdd); useIsbnScanner calls markNeedsSync(getLoadedPages()) after successful bulk-add.
Library Page Integration
app/pages/library/index.vue
Page now consumes state from useLibraryDashboardState(), uses allBooks/pagination from the composable, implements syncLoadedPages() to refresh specific page ranges, restores scroll conditionally, and updates batch-delete handling to call removeBooks() + re-sync. Minor template class tweak (min-w-[100px]min-w-25).
Book Detail Page
app/pages/library/[id].vue
After successful DELETE, calls removeBooks([userBookId]) and markNeedsSync(getLoadedPages()) to update dashboard cache and schedule sync.
Tests
test/unit/library-dashboard-state.test.ts
New Vitest suite validating initial state, getLoadedPages(), addBook(), removeBooks(), and sync flag behavior (mocks useState).
Package metadata
package.json
packageManager updated from pnpm@10.28.0 to pnpm@10.33.0+sha512....

Sequence Diagram

sequenceDiagram
    participant User
    participant IsbnTab as IsbnLookupTab
    participant Scanner as useIsbnScanner
    participant API as /api/books
    participant State as useLibraryDashboardState
    participant Dashboard as library/index.vue

    User->>IsbnTab: Click Add Book
    IsbnTab->>State: getLoadedPages()
    State-->>IsbnTab: loadedPages

    IsbnTab->>API: POST /api/books (new book)
    API-->>IsbnTab: LibraryBook

    IsbnTab->>State: addBook(addedBook)
    State->>State: insert/update, adjust pagination
    State-->>IsbnTab: ack

    IsbnTab->>State: markNeedsSync(loadedPages)
    State->>State: set shouldSync & syncTargetPages
    State-->>IsbnTab: ack

    alt bulk scan flow
        User->>Scanner: Add selected ISBNs
        Scanner->>API: POST /api/books (bulk)
        API-->>Scanner: added list
        Scanner->>State: markNeedsSync(getLoadedPages())
    end

    Dashboard->>State: observe shouldSync
    State-->>Dashboard: syncTargetPages
    Dashboard->>API: GET pages 1..N
    API-->>Dashboard: page data
    Dashboard->>State: clearNeedsSync()
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly Related PRs

Poem

🐰 I nibbled through code, left hops on the map,
New state in my burrow, no more stale gap,
Pages marked for a hop, books front and bright,
Syncs queued like carrots — everything's right,
Hooray for tidy caches and one happy lap! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'improve library dashboard state management' accurately reflects the main change—introduction of a centralized useLibraryDashboardState composable and its integration across multiple components for better state management.

✏️ 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/improve-dashboard-loading

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.

🧹 Nitpick comments (1)
app/pages/library/index.vue (1)

64-83: Consider adding error handling for the sync operation.

The syncAndRestore async function is invoked with void (fire-and-forget), but if syncLoadedPages throws (e.g., network failure during refresh()), the error will be silently swallowed. Consider wrapping in try/catch to show a toast notification on failure.

🛠️ Suggested improvement
 onMounted(() => {
   const syncAndRestore = async () => {
-    if (shouldSync.value) {
-      await syncLoadedPages(syncTargetPages.value)
-
-      clearNeedsSync()
+    try {
+      if (shouldSync.value) {
+        await syncLoadedPages(syncTargetPages.value)
+        clearNeedsSync()
+      }
+    } catch (err: unknown) {
+      const message = err instanceof Error ? err.message : 'Failed to sync library'
+      toast.add({
+        title: 'Sync failed',
+        description: message,
+        color: 'error'
+      })
+      clearNeedsSync()
     }

     if (!shouldRestoreScroll.value) return
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/pages/library/index.vue` around lines 64 - 83, Wrap the body of the
onMounted closure's async function syncAndRestore in a try/catch: call
syncLoadedPages(syncTargetPages.value) inside try, and in catch log/notify the
error (e.g., show a user toast or call a notify/toast helper) so network or
refresh failures aren’t silently swallowed; ensure finally still performs the
scroll restore logic (the nextTick/requestAnimationFrame/window.scrollTo and
setting shouldRestoreScroll.value = false) and keep existing clearNeedsSync()
behavior when the sync succeeds.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/pages/library/index.vue`:
- Around line 64-83: Wrap the body of the onMounted closure's async function
syncAndRestore in a try/catch: call syncLoadedPages(syncTargetPages.value)
inside try, and in catch log/notify the error (e.g., show a user toast or call a
notify/toast helper) so network or refresh failures aren’t silently swallowed;
ensure finally still performs the scroll restore logic (the
nextTick/requestAnimationFrame/window.scrollTo and setting
shouldRestoreScroll.value = false) and keep existing clearNeedsSync() behavior
when the sync succeeds.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8ce896d3-26b1-47ad-b0b5-bde6f994c125

📥 Commits

Reviewing files that changed from the base of the PR and between 1adec17 and 74af978.

📒 Files selected for processing (7)
  • app/components/add/IsbnLookupTab.vue
  • app/composables/useIsbnScanner.ts
  • app/composables/useLibraryDashboardState.ts
  • app/pages/library/[id].vue
  • app/pages/library/index.vue
  • package.json
  • test/unit/library-dashboard-state.test.ts

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/pages/library/index.vue`:
- Around line 67-70: Snapshot the current dashboard state (e.g., allBooks and
page.value) at the start of syncLoadedPages() and wrap the existing sync logic
in a try/catch; on error restore the snapshot (restore allBooks and page.value
to previous values) before rethrowing so UI isn't left truncated if a later page
fails. Update syncLoadedPages() to perform the snapshot/restore and ensure any
callers (the mount-sync path that calls refresh() and the batch-delete sync
path) continue to delegate to syncLoadedPages() so both paths get the rollback
behavior.
- Around line 98-104: The route-leave handler onBeforeRouteLeave incorrectly
treats the Add Book path as a detail route because isOpeningBookDetails uses
/^\/library\/[^/]+$/. Update the check in onBeforeRouteLeave (the
isOpeningBookDetails logic) to exclude the literal "add" (e.g., use
/^\/library\/(?!add)[^/]+$/ or explicitly ensure to.path !== '/library/add') so
that navigating to /library/add does not set scrollY/shouldRestoreScroll; keep
the rest of the logic (setting scrollY.value and shouldRestoreScroll.value)
unchanged for true detail routes.
- Around line 41-47: useFetch is currently auto-watching the reactive query
(computed using page.value/pageSize.value) which causes duplicate requests when
you also call refresh() in syncLoadedPages; update the useFetch calls (the ones
that assign { data, refresh, status }) to disable auto-watch (e.g., pass watch:
false) so pagination changes are only fetched when you explicitly call
refresh(), and ensure syncLoadedPages continues to call refresh() to load pages;
apply the same change to the other useFetch at lines 159-168 and keep the query
computed as-is but rely on manual refresh to avoid out-of-order/double requests
for allBooks.
🪄 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: de5da207-bc39-45ab-84b8-865504b4df99

📥 Commits

Reviewing files that changed from the base of the PR and between 74af978 and dfc983d.

📒 Files selected for processing (1)
  • app/pages/library/index.vue

@niklhut niklhut merged commit a24d773 into main Apr 8, 2026
3 checks passed
@niklhut niklhut deleted the feat/improve-dashboard-loading branch April 8, 2026 00:31
@coderabbitai coderabbitai bot mentioned this pull request Apr 8, 2026
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