03 — User Journey (Library Module)
- 1. Add a book to the catalog (librarian/catalog admin)
- 2. Find a book (student / librarian)
- 3. Issue a book to a student (librarian)
- 4. Return a book, settle the fine (librarian + bursar)
- 5. Student checks their loans (student / parent)
- 6. Librarian's overdue sweep (daily)
- Journey → screen map
End-to-end journeys mapped to exact endpoints. Write actions are server-confirmed (no optimistic mutations); conflicts (409) are surfaced as banners, never silent.
1. Add a book to the catalog (librarian/catalog admin)
Discover missing title → [Book form] → POST /api/v1/books (library.controller.ts:27-31)
→ 201 BookDoc → navigate to detail
→ 409 "Book with ISBN ... already exists." (library.service.ts:37-39) → inline error, edit ISBN
→ 400 → field errors (title/author/isbn required: create-book.dto.ts:5-15)
- Copies default to 1;
availableCopiesmirrorstotalCopies(library.service.ts:41-46). - Bulk entry (many titles) is
(planned)via catalog import (IMPLEMENTATION_PLAN.md:172,228).
2. Find a book (student / librarian)
Search box (title/author/ISBN) → GET /api/v1/books?q=…&page=1&limit=20 (library.controller.ts:33-41)
→ server regex match (book.repository.ts:26-30) → list rows with availability badge
→ GET /api/v1/books/:id (library.controller.ts:43-47) → detail
- No filter → full catalog sorted by title asc (
library.service.ts:68-74). - Category/publisher/status filtering is client-side only (OQ-5).
3. Issue a book to a student (librarian)
Book detail (available) → [Issue form] (student picker + due date)
→ POST /api/v1/books/issue {bookId, studentId, dueDate, notes?} (library.controller.ts:61-65)
→ 201 BorrowRecordDoc (ACTIVE, borrowedAt=now, dueDate as sent) → success → refresh detail
→ 409 "No copies available for borrowing." (library.service.ts:109-111) → availability refreshed
→ 409 "Student already has maximum number of borrowed books." (library.service.ts:116-120) → show count
→ 404 → book deleted meanwhile → back to catalog
- Side effect:
BookIssueddomain event (library.service.ts:141-148).
4. Return a book, settle the fine (librarian + bursar)
Borrowed-by-me / active list → select record → [Return form]
→ POST /api/v1/books/return {borrowRecordId, fineAmount?, notes?} (library.controller.ts:67-71)
→ server computes fine = ceil(overdueDays) × 5 (library.service.ts:159,211-217) or uses override
→ 201/200 record: status=RETURNED, returnedAt=now, fineAmount, fineStatus=pending|undefined
→ if fine > 0 → [Pay fine] → POST /api/v1/books/fines/:borrowRecordId/pay (library.controller.ts:85-89)
→ fineStatus: PAID (library.service.ts:203-209)
→ 409 "Book was not actively borrowed." (library.service.ts:155-157) → refresh, record already closed
- Side effect:
BookReturnedevent withfineAmount(library.service.ts:180-191).
5. Student checks their loans (student / parent)
My loans → GET /api/v1/books/borrows/:studentId/active (library.controller.ts:79-83)
→ active list (populated bookId; borrow-record.repository.ts:31) → cards with due dates
→ GET /api/v1/books/borrows/:studentId (library.controller.ts:73-77) → full history (returned incl.)
- Overdue detection is client-derived (server has no worker):
now > dueDate && status == active→ overdue badge + computed fine preview (OQ-2). studentIdcomes from the student profile state, not ameendpoint (OQ-9).
6. Librarian's overdue sweep (daily)
Overdue view → client asks active borrows for all students? NO — server lacks a list-all-active endpoint.
Fallback today: per-student active lists from borrowed-by-me screens;
a server overdue scan + endpoint is (planned) (borrow-record.repository.ts:34-43 exists, unused; IMPLEMENTATION_PLAN.md:228)
- Until then: librarian sees overdue only when a student returns a book (fine computed) or via the client's derived-overdue badge on a student's borrowed-by-me view.
Journey → screen map
| Journey | Screens (05_Screen_Inventory.md) |
|---|---|
| Add/edit book | 4 Catalog List → 5 Book Detail → 7 Book Form |
| Find book | 4 Catalog List (search) → 5 Book Detail |
| Issue | 5 Book Detail → 8 Issue Form |
| Return + fine | 6 Borrowed-by-Me → 9 Return Form → fine pay on record |
| Student loans check | 6 Borrowed-by-Me (active) → history tab |
| Overdue sweep | 10 Overdue View (derived) |