hoangsonww / hoangsonww/CollabNote-Fullstack-App
Feature: Note Version History + Diff Viewer + Restore
- Dominant language
- TypeScript
- Stars
- 27
- Forks
- 11
- PR merge metrics
- No merged PRs in 30d
Description
**Summary**
Add **automatic versioning** for notes with a **timeline**, **rich diff viewer** (title/content, markdown-aware), and **one-click restore**. Every edit creates a compact version record; users can browse history, compare any two versions, and restore a prior state (without losing newer history).
**Why**
* Prevents accidental loss and enables safe collaboration.
* Clear audit trail for shared notes.
* Pairs well with existing real-time syncing (Supabase).
---
## Scope (MVP)
**UX**
* “History” button on Note Details → opens side drawer/panel.
* Timeline list: version number, author, timestamp, summary (first line / chars changed).
* Actions per version: **View diff**, **Restore this version**.
* Diff viewer: side-by-side (desktop) / inline (mobile); highlights additions/deletions; supports markdown blocks.
* After restore: new version is appended (no destructive rewinds).
**Data Model (PostgreSQL/Supabase)**
* `note_versions`
* `id UUID PK`
* `note_id UUID FK -> notes(id)`
* `title TEXT`
* `content TEXT`
* `author_id UUID`
* `created_at TIMESTAMPTZ DEFAULT now()`
* `summary TEXT NULL` (optional, auto-generated “Changed X chars / Y lines”)
* Indexes: `(note_id, created_at DESC)`, `(author_id, created_at DESC)`
**API (REST + GraphQL)**
* REST:
* `GET /notes/:id/versions` → list (paginated)
* `GET /notes/:id/versions/:versionId` → fetch specific
* `POST /notes/:id/versions/:versionId/restore` → creates a new head from that snapshot
* GraphQL:
* `type NoteVersion { id, noteId, title, content, authorId, createdAt, summary }`
* `versions(noteId: ID!, limit: Int, cursor: String): VersionConnection`
* `restoreVersion(noteId: ID!, versionId: ID!): Note`
**Creation Policy**
* Create a version on:
* Explicit save/update (`PATCH /notes/:id`).
* Debounced autosave (e.g., 10–15s of inactivity or blur).
* Optional size guard: skip if no delta.
**Diff**
* Compute textual diffs on demand (server) using a word-aware + line-aware algorithm (e.g., Myers/LCS).
* Markdown aware rendering in UI (code blocks/headings not “exploded”).
* Return a compact diff payload for large docs.
**Permissions**
* Only users with read access see history; only users with write access can restore.
* Versions inherit `notes` sharing permissions.
**Out of Scope (MVP)**
* Per-character live cursors/OT/CRDT.
* Commenting/annotations on versions.
---
## Acceptance Criteria
* Editing a note creates version records; timeline is visible and ordered newest→oldest.
* Diff viewer accurately highlights changes for title and content (including markdown).
* Restoring a version appends a new latest version; no history loss.
* Works for shared notes respecting existing RBAC.
* Pagination supports ≥1k versions without UI lag.
* Unit/integration tests cover version create/list/restore and diff correctness for common cases.
---
## Implementation Notes
**Backend (NestJS)**
* Add `note_versions` entity + repo/service.
* In `NotesService.updateNote(...)`: wrap write with:
1. Fetch current note; 2) insert snapshot into `note_versions`; 3) write new content/title.
* New controller routes for list/get/restore.
* GraphQL resolvers mirroring REST.
* Optional optimization: store `content` gzipped; compute diffs server-side and send a lean structure to FE.
**Frontend (Vite/React/MUI)**
* `HistoryDrawer.tsx`: timeline list with virtualization (react-window) for large histories.
* `DiffViewer.tsx`: side-by-side on ≥md breakpoint; inline on mobile. Consider `diff-match-patch` or a lightweight diff lib client-side when server only returns raw texts.
* Buttons: “View diff” opens diff; “Restore” → confirm dialog → POST restore → refetch note & versions.
* Loading states, error toasts, optimistic UI for restore.
**Performance**
* Server-side pagination (limit + cursor).
* Cache latest N versions in Supabase/Redis (optional later).
* Avoid creating a version if patch is empty (trimmed).
**Telemetry**
* Emit events: `version.created`, `version.restored` (useful for audits/metrics).
**Migrations**
* SQL migration to create `note_versions` + indexes.
* Backfill: create an initial version for each existing note (optional).
**Testing**
* Backend unit: create/list/restore, permission gates, empty delta skip.
* Backend integration: diff endpoint returns correct hunks.
* Frontend: timeline rendering, diff toggling, restore flow, pagination.
---
## Tasks
* [ ] DB migration: `note_versions` table + indexes.
* [ ] Backend: service + controller + GraphQL resolvers.
* [ ] Hook version creation into note updates/autosave.
* [ ] Diff computation API (or FE lib with server raw texts).
* [ ] Frontend UI: History drawer, Diff viewer, Restore flow.
* [ ] AuthZ checks for read/restore on shared notes.
* [ ] Tests (BE unit/integration, FE component/e2e light).
* [ ] Docs: README section with screenshots + API/GraphQL notes.
---
Contributor guide
Assessment
This issue has not been assessed yet.