Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

08 — Form Specifications (Library Module)

Field-level contracts for the three forms. Server validation is authoritative; client validation mirrors it for latency only. DTO sources: create-book.dto.ts, update-book.dto.ts, issue-book.dto.ts, return-book.dto.ts.


F1. Book form (create / edit)

Create body = CreateBookDto (create-book.dto.ts:4-47); edit body = UpdateBookDto = PartialType(CreateBookDto) (update-book.dto.ts:4) — every field optional on edit.

FieldTypeRequiredConstraints (server)Notes
titlestring@IsString (:6-7)trimmed server-side (book.schema.ts:16-17)
authorstring@IsString (:10-11)trimmed (book.schema.ts:19-20)
isbnstring@IsString (:14-15)trimmed; unique per tenant — 409 on duplicate (library.service.ts:35-40, book.schema.ts:51)
publisherstring@IsOptional @IsString (:17-20)
categorystring@IsOptional @IsString (:22-25)client filter chip source (OQ-5)
editionstring@IsOptional @IsString (:27-30)
totalCopiesnumber@IsOptional @IsNumber @Min(1) (:32-36)default 1 (library.service.ts:41); create sets availableCopies = totalCopies (:44-46); edit shifts availableCopies by Δ, floor 0 (library.service.ts:87-90)
shelfLocationstring@IsOptional @IsString (:38-41)
descriptionstring@IsOptional @IsString (:43-46)

Validation order on submit: required → type → ISBN uniqueness hint (client may pre-check from cache, but the server 409 is authoritative).

Copy projection widget: create → "Available will equal total (N)"; edit → "Available: 2 → 3" using the Δ rule.


F2. Issue form

Body = IssueBookDto (issue-book.dto.ts:4-21).

FieldTypeRequiredConstraints (server)Notes
bookIdstring@IsString (:6-7)from Book Detail context; 404 if unknown (library.service.ts:108)
studentIdstring@IsString (:9-10)from StudentPicker; ref Student (borrow-record.schema.ts:25-26)
dueDatestring (ISO)@IsDateString (:14-15)no server min-date rule — client must require future dates; new Date(dueDate) stored (library.service.ts:135)
notesstring@IsOptional @IsString (:17-20)stored on record (library.service.ts:138)

Server-side gates (client shows the same copy pre-submit where possible):

GateConditionError
Availabilitybook.availableCopies < 1409 "No copies available for borrowing." (library.service.ts:109-111)
Student capactive borrows ≥ 5409 "Student already has maximum number of borrowed books." (library.service.ts:116-120)

Defaults: due date = today + 14 days (client default only; no server default — dueDate required).

On success: record created with status: ACTIVE, borrowedAt: now (library.service.ts:132-139); book availableCopies − 1, status → borrowed when 0 remain (library.service.ts:122-130).


F3. Return form

Body = ReturnBookDto (return-book.dto.ts:4-19).

FieldTypeRequiredConstraints (server)Notes
borrowRecordIdstring@IsString (:6-7)404 if unknown (library.service.ts:153-154); 409 if not ACTIVE (library.service.ts:155-157)
fineAmountnumber@IsOptional @IsNumber @Min(0) (:9-13)staff override: server prefers dto.fineAmount ?? calculateFine(dueDate) (library.service.ts:159); 0 is legal (on-time or waived-by-zero)
notesstring@IsOptional @IsString (:15-18)replaces record notes on return (library.service.ts:167)

Fine computation (server, authoritative):

calculateFine(dueDate): 0 if now <= dueDate; else ceil((now - dueDate) / 1 day) × 5
(library.service.ts:211-217; DAILY_FINE_RATE = 5 at :25)

On success: status → RETURNED, returnedAt: now, fineAmount set, fineStatus → pending iff fineAmount > 0 (else left unset) (library.service.ts:161-169); book availableCopies + 1, status → available (library.service.ts:172-178).

Post-return follow-up: if fineAmount > 0, offer "Pay fine" → POST /books/fines/:borrowRecordId/pay (no body — library.controller.ts:85-89) → fineStatus: PAID (library.service.ts:203-209). Server does not guard pay state (OQ-6): client only offers it on fineStatus == pending.


F4. Validation & error UX (all forms)

CodeMeaningUI
400 VALIDATION_ERRORdetails[].message per fieldinline field errors, focus first invalid
409 DUPLICATE_RESOURCEISBN dup / no copies / cap / not-activebanner + contextual action (edit ISBN / refresh availability)
404 RESOURCE_NOT_FOUNDbook/record goneclose form, snackbar
429 RATE_LIMITEDapi tiercountdown, no auto-retry
5xxservergeneric + requestId, manual retry

Envelope + codes: response-envelope.interceptor.ts:44-52, http-exception.filter.ts:28-34,74-78.

No optimistic writes in any form — every submit awaits the server response (00-shared/07 §9).