feat(flashcard-assistant): add flashcard assistant API and UI componenents#24
feat(flashcard-assistant): add flashcard assistant API and UI componenents#24
Conversation
…nt for enhanced user interaction with flashcards
There was a problem hiding this comment.
Pull request overview
Introduces a new Flashcard Assistant (UI + API) for the flashcard study flow, enhances the existing Lesson Assistant panel UX, and improves login error messaging by detecting infrastructure outages via a health check.
Changes:
- Added
/api/flashcard-assistant(Pro-only, rate-limited) backed by Anthropic, plus a shared system prompt. - Added a Flashcard Assistant floating action button on the flashcard study page; prevented reveal shortcuts while typing in inputs/editables.
- Refactored the Lesson Assistant panel into a two-column layout with scroll-position preservation and a “ask another” workflow; login now prechecks
/api/healthfor better outage messaging.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| LearningPlatform/lib/public-routes.ts | Allowlists /api/health for unauthenticated access (used by login precheck). |
| LearningPlatform/lib/flashcard-assistant-prompt.ts | Adds system prompt for flashcard assistant responses. |
| LearningPlatform/components/student/lesson-assistant-fab.tsx | Updates Lesson Assistant layout/scroll preservation and composer collapse behavior. |
| LearningPlatform/components/student/flashcard-assistant-fab.tsx | New Flashcard Assistant FAB UI for flashcard Q&A. |
| LearningPlatform/app/api/flashcard-assistant/route.ts | New Pro-only, rate-limited API route calling Anthropic with flashcard context. |
| LearningPlatform/app/(student)/(shell)/dashboard/flashcards/study/page.tsx | Integrates Flashcard Assistant FAB and avoids shortcuts while typing. |
| LearningPlatform/app/(public)/login/page.tsx | Adds infra health pre/post checks to improve login error messaging. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| async function getInfraStatusMessage(): Promise<string | null> { | ||
| try { | ||
| // Add a timestamp to bypass any browser/proxy caching of old health responses. | ||
| const res = await fetch(`/api/health?t=${Date.now()}`, { cache: 'no-store' }); | ||
| if (res.ok) return null; | ||
|
|
||
| // Health endpoint returns 503 for DB/Payload connectivity issues. | ||
| if (res.status >= 500) { | ||
| return 'Cannot connect to the server/database right now. Please try again in a moment.'; | ||
| } | ||
| return null; | ||
| } catch { | ||
| return 'Cannot reach the backend right now. Check your connection and try again.'; |
There was a problem hiding this comment.
getInfraStatusMessage() calls /api/health (with a timestamp to defeat caching). /api/health does DB ping + Payload init + courses query, so invoking it on every login submit (and again on auth error) can add significant latency/load and also amplifies the impact of this route being publicly callable. Consider switching this precheck to a lightweight readiness endpoint (or adding a ?light=1 mode on /api/health that only returns a 200/503 without expensive checks/body).
| async function getInfraStatusMessage(): Promise<string | null> { | |
| try { | |
| // Add a timestamp to bypass any browser/proxy caching of old health responses. | |
| const res = await fetch(`/api/health?t=${Date.now()}`, { cache: 'no-store' }); | |
| if (res.ok) return null; | |
| // Health endpoint returns 503 for DB/Payload connectivity issues. | |
| if (res.status >= 500) { | |
| return 'Cannot connect to the server/database right now. Please try again in a moment.'; | |
| } | |
| return null; | |
| } catch { | |
| return 'Cannot reach the backend right now. Check your connection and try again.'; | |
| const INFRA_STATUS_CACHE_TTL_MS = 30_000; | |
| let lastInfraStatusCheckAt = 0; | |
| let lastInfraStatusMessage: string | null = null; | |
| async function getInfraStatusMessage(): Promise<string | null> { | |
| const now = Date.now(); | |
| if (now - lastInfraStatusCheckAt < INFRA_STATUS_CACHE_TTL_MS) { | |
| return lastInfraStatusMessage; | |
| } | |
| try { | |
| const res = await fetch('/api/health', { cache: 'no-store' }); | |
| const message = | |
| res.ok || res.status < 500 | |
| ? null | |
| : 'Cannot connect to the server/database right now. Please try again in a moment.'; | |
| lastInfraStatusCheckAt = now; | |
| lastInfraStatusMessage = message; | |
| return message; | |
| } catch { | |
| const message = 'Cannot reach the backend right now. Check your connection and try again.'; | |
| lastInfraStatusCheckAt = now; | |
| lastInfraStatusMessage = message; | |
| return message; |
| useEffect(() => { | ||
| setAnswer('') | ||
| setError(null) | ||
| setComposerCollapsed(false) | ||
| }, [cardFront, cardBack]) |
There was a problem hiding this comment.
When the current flashcard changes, the effect clears answer/error but leaves question intact. Since the composer is expanded again (setComposerCollapsed(false)), this can show the previous card’s question pre-filled on the next card and accidentally send it with new card context. Consider clearing question here as well (or keeping the composer collapsed if you intentionally want to preserve the draft).
| '/register', | ||
| '/admin/login', | ||
| '/api/ping', | ||
| '/api/health', |
There was a problem hiding this comment.
Adding /api/health to the unauthenticated allowlist makes the full health check endpoint publicly reachable. That endpoint returns detailed internal check results (e.g., DB/Payload status, missing env var names, migration hints) and also performs relatively heavy work (Payload init + collection query). Consider either (a) returning a minimal/public-safe payload when unauthenticated, or (b) introducing a lightweight public endpoint (e.g. /api/healthz) used by the login precheck and keeping /api/health admin-only.
| '/api/health', |
Switch login infra checks to a lightweight public healthz probe with short client-side caching, reset flashcard assistant drafts on card change, and add compatibility fallbacks when archivedAt is missing in local course progress schemas.
…aner TypeScript configuration
… component to prevent rendering issues
This pull request introduces a new Flashcard Assistant feature for students, improves the existing Lesson Assistant experience, and adds better infrastructure error handling to the login flow. The main changes include a new API route and floating action button for flashcard-related questions, enhancements to the Lesson Assistant panel (including scroll position preservation and a better question/answer workflow), and improved detection of infrastructure issues during login. Additionally, the flashcard study page now prevents keyboard shortcuts from interfering with typing in input fields.
Flashcard Assistant Feature:
FlashcardAssistantFab) to the flashcard study page, enabling students to ask questions about the current flashcard, select an AI model, and view answers in a user-friendly panel. [1] [2]/api/flashcard-assistantAPI route, which validates input, enforces rate limits, and uses Anthropic's AI models to answer student questions about flashcards.Lesson Assistant Improvements:
Login and Error Handling:
Flashcard Study Usability:
Other Minor Updates: