# Learning Hub A CMS + content-delivery module for clinical education material inside the app. Supports articles, clinical pearls, quizzes, and Marp-rendered presentations with PPTX export. Quiz questions are stored alongside article content and can optionally be generated by AI from uploaded source material. ## Content types | Type | Description | |---|---| | `article` | Rich HTML body with an optional attached quiz | | `pearl` | Short clinical snippet (no quiz, no heavy media) | | `quiz` | Standalone quiz (no article body) | | `presentation` | Marp markdown rendered as slides; PPTX export supported | ## User-facing features - Browse by category. - Three search modes: - **Keyword** — Postgres full-text. - **Semantic** — pgvector cosine similarity on the embedding column. - **Hybrid** — weighted merge of both result sets. - Articles render with sanitized HTML (DOMPurify, loaded via SRI-pinned cdnjs). - Quizzes: multiple-choice, multi-select, true/false. Score computed on submit, per-question explanations revealed after. - Presentation viewer: modal with keyboard / swipe navigation. - Progress: `learning_progress` stores per-attempt score + total. ## CMS (moderator / admin) - Tiptap rich-text editor for article body. - Draft / published toggle. - Category assignment. - Quiz builder: add/remove questions, add/remove options, mark correct, enter explanation. - Marp editor for presentations with live preview. ## AI content generation `POST /api/admin/learning/generate` takes one of: | Input | Notes | |---|---| | `topic` | Plain-text description of the topic | | Uploaded files | PDF / TXT / MD / HTML / CSV / JSON, ≤ 100 MB each, max 10 files | | WebDAV path | Pulled from the user's connected Nextcloud instance | Parameters: `model` (from the provider whitelist), `slideCount` for presentations, `wordCount` for articles. File uploads pass the `src/utils/fileType.js` magic-byte check so a mismatched extension is rejected before it reaches the parser. ## Marp → PPTX export Uses `pptxgenjs`. - 16:9 widescreen. - Bottom-right slide numbers. - Supported Markdown elements: headings, sub-headings, bold, italic, inline code, numbered + bulleted lists, code blocks (grey background), blockquotes (blue accent bar), tables with alternating rows. - Mixed content per slide allowed. ## Semantic search | | | |---|---| | Store | `pgvector` on `learning_content.embedding VECTOR(768)` | | Index | IVFFLAT, cosine distance | | Primary model | Google Vertex `text-embedding-005` (768 dims) | | Fallback model | OpenAI `text-embedding-3-small` (truncated to 768 to match the column) | Embeddings are generated on content publish + on every edit. If the embedding provider is unreachable, the content still saves — keyword search remains available. ## Tables | Table | Purpose | |---|---| | `learning_categories` | Top-level groupings | | `learning_content` | Articles / pearls / quizzes / presentations. Body + `embedding` vector. | | `learning_questions` | Quiz question prompts (FK to content) | | `learning_options` | Answer options (FK to question) | | `learning_progress` | Per-user attempt history |