StudyBoost Docs

Feature: Structured Notes System

Metadata

  • Issue ID: FEAT-59
  • Status: Done
  • Owner: Kzu0-afk
  • Related PRs: 59-notes-system -> dev

Overview

Structured Notes gives authenticated users a study-planning workspace where they can create notes, attach references to documents/courses/institutions, track deadlines, and organize note cards in a visual canvas. It is not a plain text-notes module — its value is connecting StudyBoost catalog content to user planning: save a document, course, or institution as a structured reference; preserve whether a referenced document is free, paid, premium, locked, or unavailable; attach personal context; set note-level and reference-level dates; and arrange notes spatially in canvas mode. The backend is the source of truth, and all user-owned data is scoped to the authenticated user from FEAT-31.


Frontend Behavior

  • Users open /notes to view, search, create, edit, soft-delete, and organize their own notes.
  • A note has a title, freeform body, optional deadline/reminder date, and optional initial references.
  • References can target a document, course, or institution by id or slug-derived lookup, with optional contextual text per reference.
  • "Add to Notes" quick-add actions are available on document, course, and institution detail pages: a lightweight modal lets the user pick an existing note or create a new one, pre-fills reference metadata from the public slug, requires auth (FEAT-31 redirect to /login?next=<target> for unauthenticated users), shows inline success/failure feedback, and defaults to new-note mode when the user has no notes.
  • Notes list shows title, deadline state, and reference count. The note editor handles title, body, references, dates, and soft-delete controls, plus empty, loading, and retryable error states.
  • Canvas mode renders notes as cards in a scrollable surface; cards are moved with lightweight directional controls and positions persist via PATCH /notes/:id/canvas. List/detail mode remains fully usable when canvas mode is unavailable.
  • Document references display access state: free, paid, premium, or unavailable. Referencing a document never unlocks it or bypasses subscription/payment checks.
  • UI follows StudyBoost design-system spacing, cards, radius, and typography; controls are keyboard-accessible and mobile-first responsive.

Backend Behavior

A dedicated NestJS module lives under backend/src/notes/ (controller, module, service, dto/, enums/). All notes endpoints use CookieAuthGuard and derive user_id from request.user; the API never accepts arbitrary user_id in request bodies.

Endpoints (all auth required):

  • GET /notes — paginated notes for the authenticated user; query params page, limit, q, status, has_deadline, sort; excludes archived/deleted by default.
  • POST /notes — creates a note; accepts title, body, optional deadline/reminder, optional initial references and canvas metadata; rejects empty/whitespace-only titles.
  • GET /notes/:id — returns one owned note with references and canvas metadata; 404 if missing or owned by another user.
  • PATCH /notes/:id — updates title/body/status/deadline/reminder; owned notes only; rejects empty titles.
  • DELETE /notes/:id — soft-deletes with status=deleted and deleted_at.
  • POST /notes/:id/references — adds a document/course/institution reference; validates type and target existence; supports slug-based linking and reference-level dates.
  • PATCH /notes/:id/references/:referenceId — updates context, order, dates, or archived status.
  • DELETE /notes/:id/references/:referenceId — archives a reference with archived_at instead of hard-deleting.
  • PATCH /notes/:id/canvas — persists card position, size, and optional group label; validates numeric bounds.

Data model — migration 202605021540_add_structured_notes_tables: structured_notes, structured_note_references, structured_note_canvas_items. Additive only, with guarded IF NOT EXISTS operations and indexes on (user_id, status, updated_at), (user_id, deadline_at), (note_id), and reference target columns.

Business rules and failure modes: a note belongs to exactly one user; references belong to exactly one note; locked/paid/premium targets may be referenced but never granted content access; deadline/reminder timestamps are stored in UTC; invalid reference targets are rejected on create; deleted targets render as unavailable on read; DTOs are validated with class-validator and body/context lengths are bounded; ownership is enforced at the service layer for every read/write.


QA Test Scenarios

Scenario IDDescriptionStepsInputExpected Result
FEAT-59-01Create note happy pathLogin, open /notes, create a note with title/bodyValid title and bodyNote is saved for the current user and appears in the list
FEAT-59-02Create note with document referenceOpen document detail, click Add to Notes, create a new noteValid document slug/idNote is created with the document reference attached
FEAT-59-03Add locked document referenceAdd a paid/premium document to a notePaid or premium documentReference is saved and shown as locked/paid/premium; document is not unlocked
FEAT-59-04Add course referenceOpen course detail, Add to Notes, select existing noteValid course slug/idCourse reference is appended to the selected note
FEAT-59-05Add institution referenceOpen institution detail, Add to Notes, select existing noteValid institution slug/idInstitution reference is appended to the selected note
FEAT-59-06Deadline on noteCreate/edit a note with a future deadlineValid UTC dateDeadline is saved and displayed in note list/detail
FEAT-59-07Deadline on referenceAdd a reference-level dateValid UTC dateReference displays its own date without overwriting the note deadline
FEAT-59-08Canvas position saveMove a note card in canvas mode and saveValid x/y coordinatesPosition persists after refresh
FEAT-59-09Unauthenticated quick addOpen a document page while logged out and click Add to NotesNo auth cookiesUser is redirected to /login?next=<target>
FEAT-59-10Cross-user access blockedUser A requests User B's note by IDValid note ID owned by another userAPI returns 404 without note data
FEAT-59-11Invalid reference targetAdd a reference with an unknown document/course/institution ID or slugNonexistent targetAPI rejects with validation/not-found error; no reference created
FEAT-59-12Invalid canvas coordinatesSubmit extreme/NaN/oversized coordinatesInvalid canvas payloadAPI rejects with 400 and keeps the previous layout
FEAT-59-13Backend failure during saveSimulate API failure while saving a noteValid edit, backend unavailableUI shows a retryable error and does not falsely mark the note saved
FEAT-59-14Empty stateNew user opens the notes pageNo notesUI shows a helpful empty state and a create action
FEAT-59-15Archive/delete behaviorArchive/delete an owned noteExisting noteNote disappears from the default list but ownership data is not exposed
FEAT-59-16Search/filter notesSearch by note title/body/reference contextQuery stringList returns only owned matching notes

Edge Cases

  • Deleted or unpublished referenced document: keep the reference row and render it as unavailable; the migration allows hard-deleted targets to null out without deleting the reference.
  • Course/institution slug changes: stable UUID references are stored in the backend and the current slug/name is resolved on read.
  • Locked document state changes: live access state is recomputed where possible while preserving access_state_snapshot for historical context.
  • Concurrent edits: last write wins for v1.
  • Very long note bodies / many references: max lengths are enforced in DTOs and columns; long reference lists may be paginated.
  • Canvas on mobile: list/detail fallback or constrained movement controls instead of forcing desktop drag behavior.
  • Offline or flaky network: save operations show retryable failures and never pretend data is persisted.

Notes

  • Auth dependency: FEAT-31 secure cookie auth is required for all note APIs and quick-add actions.
  • Catalog dependency: references integrate with existing document/course/institution public catalog endpoints.
  • Subscription/access dependency: locked/paid/premium states must not bypass existing access rules.
  • Database dependency: requires additive migrations for notes, references, and canvas layout.
  • Feature flag: consider enable_structured_notes before public rollout if FEAT-35 is required by the target release.
  • Known limitations: real-time collaboration, rich block editor, in-note file uploads, and full Milanote parity are out of scope for v1.

Security Notes

  • No API accepts arbitrary user_id in request bodies; all notes endpoints require the auth guard.
  • Reference resolution supports slug-based linking and validates target existence.
  • Locked/paid/premium document references are stored as references only and do not grant content access.

Canonical Feature Document

Implementation and QA requirements are tracked in: docs/features/FEAT-59-Structured-Notes-System.md