From 09cc6dae3056794bb86267cb0be93e36588d1dc9 Mon Sep 17 00:00:00 2001 From: niklhut <49069026+niklhut@users.noreply.github.com> Date: Tue, 19 May 2026 23:22:56 -0400 Subject: [PATCH 1/8] Refine lending navigation and loan history views --- app/components/AppHeader.vue | 26 +- app/components/BookCard.vue | 19 + app/components/BookLendingModal.vue | 209 ++++++++ app/pages/(auth)/login.vue | 2 +- app/pages/(auth)/register.vue | 12 +- app/pages/i/[token].vue | 203 ++++++++ app/pages/library/[id].vue | 138 +++++- app/pages/library/borrowed.vue | 7 + app/pages/library/index.vue | 15 + app/pages/library/loans.vue | 467 ++++++++++++++++++ server/api/books/[id]/index.delete.ts | 5 +- server/api/books/[id]/loans/index.post.ts | 18 + server/api/borrowed/index.get.ts | 7 + server/api/invite/[token]/accept.post.ts | 13 + server/api/invite/[token]/cover.get.ts | 31 ++ server/api/invite/[token]/index.get.ts | 19 + server/api/loans/[id]/cancel.post.ts | 13 + server/api/loans/[id]/return.post.ts | 13 + server/api/loans/index.get.ts | 7 + .../sqlite/0010_lending_history.sql | 84 ++++ .../db/migrations/sqlite/meta/_journal.json | 7 + server/db/schema/domain.ts | 33 +- server/repositories/book.repository.ts | 116 ++++- server/repositories/lending.repository.ts | 404 +++++++++++++++ server/services/book.service.ts | 21 +- server/services/lending.service.ts | 129 +++++ server/utils/effect.ts | 20 +- shared/types/book.ts | 51 ++ shared/utils/schemas.ts | 42 ++ test/unit/server/api/_helpers/api-route.ts | 33 +- .../api/books/[id]/index.delete.test.ts | 14 +- .../api/books/[id]/loans/index.post.test.ts | 64 +++ .../api/invite/[token]/accept.post.test.ts | 43 ++ .../api/invite/[token]/index.get.test.ts | 44 ++ 34 files changed, 2277 insertions(+), 52 deletions(-) create mode 100644 app/components/BookLendingModal.vue create mode 100644 app/pages/i/[token].vue create mode 100644 app/pages/library/borrowed.vue create mode 100644 app/pages/library/loans.vue create mode 100644 server/api/books/[id]/loans/index.post.ts create mode 100644 server/api/borrowed/index.get.ts create mode 100644 server/api/invite/[token]/accept.post.ts create mode 100644 server/api/invite/[token]/cover.get.ts create mode 100644 server/api/invite/[token]/index.get.ts create mode 100644 server/api/loans/[id]/cancel.post.ts create mode 100644 server/api/loans/[id]/return.post.ts create mode 100644 server/api/loans/index.get.ts create mode 100644 server/db/migrations/sqlite/0010_lending_history.sql create mode 100644 server/repositories/lending.repository.ts create mode 100644 server/services/lending.service.ts create mode 100644 test/unit/server/api/books/[id]/loans/index.post.test.ts create mode 100644 test/unit/server/api/invite/[token]/accept.post.test.ts create mode 100644 test/unit/server/api/invite/[token]/index.get.test.ts diff --git a/app/components/AppHeader.vue b/app/components/AppHeader.vue index 6bae473..fa936f6 100644 --- a/app/components/AppHeader.vue +++ b/app/components/AppHeader.vue @@ -23,13 +23,25 @@ const logoTo = computed(() => user.value ? '/library' : '/login') // Navigation links - only show Sign Out when logged in const links = computed(() => { if (user.value) { - return [{ - label: 'Sign Out', - icon: 'i-lucide-log-out', - color: 'neutral' as const, - variant: 'ghost' as const, - onClick: handleSignOut - }] + return [ + { + label: 'Library', + icon: 'i-lucide-library', + to: '/library' + }, + { + label: 'Loans', + icon: 'i-lucide-handshake', + to: '/library/loans' + }, + { + label: 'Sign Out', + icon: 'i-lucide-log-out', + color: 'neutral' as const, + variant: 'ghost' as const, + onClick: handleSignOut + } + ] } return [] }) diff --git a/app/components/BookCard.vue b/app/components/BookCard.vue index 64559e6..f83c78c 100644 --- a/app/components/BookCard.vue +++ b/app/components/BookCard.vue @@ -7,6 +7,7 @@ interface Props { isbn?: string | null coverPath?: string | null addedAt?: string | Date + activeLoan?: ActiveLoanSummary | null selected?: boolean selectable?: boolean } @@ -69,6 +70,15 @@ function handleClick(e: MouseEvent) { class="text-4xl text-muted" /> + + Lent out + @@ -111,6 +121,15 @@ function handleClick(e: MouseEvent) { name="i-lucide-book" class="text-4xl text-muted" /> + + Lent out +
diff --git a/app/components/BookLendingModal.vue b/app/components/BookLendingModal.vue new file mode 100644 index 0000000..baeabb9 --- /dev/null +++ b/app/components/BookLendingModal.vue @@ -0,0 +1,209 @@ + + + diff --git a/app/pages/(auth)/login.vue b/app/pages/(auth)/login.vue index a31110b..d84865e 100644 --- a/app/pages/(auth)/login.vue +++ b/app/pages/(auth)/login.vue @@ -113,7 +113,7 @@ async function onSubmit(payload: FormSubmitEvent) {