# Phase 4: Folders, Sharing, Quotas & Document UX - Context **Gathered:** 2026-05-25 **Status:** Ready for planning ## Phase Boundary Deliver a complete document management experience on top of the Phase 3 multi-user foundation: folder organization (create/rename/delete/move, breadcrumb navigation, sort), document sharing by exact handle with immediate revocation, quota feedback (already-built QuotaBar, upload rejection with detailed error), PDF in-browser preview proxied through the app with a per-user open-mode preference, full-text search via PostgreSQL tsvector, and an admin audit log viewer with export and daily automated CSV backups. This phase does NOT include cloud storage backend connectivity (Phase 5). The QuotaBar component and upload rejection flow are already built (Phase 3) — Phase 4 wires the remaining document UX features around them. ## Implementation Decisions ### Folder Navigation Layout - **D-01:** Hybrid layout — AppSidebar shows top-level folders only (no nested expansion in sidebar). Once inside a folder, the main content area shows sub-folder rows + breadcrumb navigation. Top-level folders in sidebar are clickable to navigate directly. - **D-02:** Unlimited nesting depth — no API or UI cap. The `Folder.parent_id` self-referential FK is the authoritative constraint. Breadcrumbs truncate long paths with an ellipsis when path depth > N segments. - **D-03:** Non-empty folder delete shows a warning modal that includes the document count (e.g., "This folder contains 5 documents. Deleting it will permanently delete all documents inside."). If the user confirms, cascade-delete all documents (MinIO objects + DB rows + quota decrements) and the folder. If the user cancels, no action taken. Documents are NOT moved to root — they are destroyed with the folder. ### Sharing - **D-04:** Exact handle input — user types the recipient's handle (no `@` prefix required by API, but UI may accept it). No autocomplete or user search endpoint needed. API returns 404 if handle not found; UI shows a clear "User not found" error. - **D-05:** Share button is on the DocumentCard (inline icon button). Clicking opens a sharing modal containing: (a) a text input for the recipient handle, (b) a "Share" submit button, (c) a list of current recipients with their permission level and a "Revoke" button per row. - **D-06:** "Shared with me" is a fixed virtual folder entry in AppSidebar, rendered above the user's own folder list. It is not stored as a real folder — the backend filters by `shares.recipient_id = current_user.id`. Shared documents count zero bytes against the recipient's quota (SHARE-02). - **D-07:** Share permission is `view` only for Phase 4 (SHARE-03 — owner controls permission level; only `view` implemented; `edit` deferred). ### PDF Proxy & Preview - **D-08:** Streaming proxy endpoint: `GET /api/documents/{id}/content` retrieves bytes from MinIO via the storage backend and returns them via FastAPI `StreamingResponse`. Supports `Range` headers for partial content (large file performance). `Content-Disposition: inline` with correct `Content-Type` from the document's `content_type` column. No presigned URL is ever generated or exposed to the browser (DOC-02 privacy model). - **D-09:** Native browser PDF rendering — no PDF.js library. DOC-02 specifies PDF.js but the user explicitly chose native rendering (intent over literal spec). Content-Type header drives browser rendering. Zero frontend dependencies added. - **D-10:** Per-user PDF open preference stored in DB. New column `users.pdf_open_mode` (String, default `'in_app'`, allowed values: `'in_app'` | `'new_tab'`). Exposed via `PATCH /api/me/preferences` endpoint. Setting is shown in SettingsView for all users (not admin-only). - `in_app`: PDF opens in a modal/overlay containing `