hoangsonww / hoangsonww/CollabNote-Fullstack-App

Feature: Note Version History + Diff Viewer + Restore

Open
#2 0 comments 0 reactions 1 assignee Claimed by @hoangsonww View on GitHub
bug documentation enhancement good first issue help wanted question
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.