feat: add admin presenter overview and current-state endpoints#64
Conversation
📝 WalkthroughWalkthroughAdds presenter-facing types, utilities, and admin API endpoints to expose quiz overview and live polling state; centralizes per-option result aggregation; enforces unique English answer-option labels on question creation; and updates API docs to describe new endpoints and bearer auth options. ChangesPresenter API and State Management
Sequence DiagramsequenceDiagram
participant Admin as Admin Client
participant Handler as API Handler
participant Auth as verifyAdmin()
participant Presenter as Presenter Utility
participant Storage as Storage/DB
Admin->>Handler: GET /api/admin/presenter/current-state
Handler->>Auth: verifyAdmin(event)
Auth-->>Handler: authorized
Handler->>Presenter: getPresenterCurrentState()
Presenter->>Storage: getQuestions()
Storage-->>Presenter: Question[]
Presenter->>Storage: getPeers()
Storage-->>Presenter: peer count
Presenter->>Presenter: findActiveQuestion()
alt active question found
Presenter->>Storage: getAnswersForQuestion(activeQuestionId)
Storage-->>Presenter: Answer[]
Presenter->>Presenter: buildQuestionOptionResults()
Presenter-->>Handler: PresenterCurrentState (hasActiveQuestion: true)
else no active question
Presenter-->>Handler: PresenterCurrentState (hasActiveQuestion: false)
end
Handler-->>Admin: PresenterCurrentState JSON
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly Related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Review rate limit: 4/5 reviews remaining, refill in 12 minutes. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@server/utils/quiz-results.ts`:
- Around line 11-23: Results accumulator should be a null-prototype object and
must use a stable per-option key (not raw display text) to avoid collapsing
duplicate English labels; change the results init to Object.create(null),
iterate question.answer_options and assign each bucket under a uniqueKey (e.g.,
option.id ?? `idx:${i}`) storing { label: option.text.en, emoji: option.emoji,
count: 0 }, and when tallying answers (from answerList) resolve the
corresponding uniqueKey (prefer a selected_option id if available, otherwise
match by text+emoji or fallback to first matching option) before incrementing
results[uniqueKey].count so duplicate labels do not collide.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: d48b2185-b12b-449f-92b0-c2698d99eb2f
📒 Files selected for processing (7)
app/types.tsdocs/api.mdserver/api/admin/presenter/current-state.get.tsserver/api/admin/presenter/overview.get.tsserver/utils/presenter.tsserver/utils/quiz-results.tsserver/utils/storage.ts
There was a problem hiding this comment.
♻️ Duplicate comments (1)
server/utils/quiz-results.ts (1)
16-18:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAvoid hard-failing presenter state aggregation on duplicate legacy labels
Line 17 throws and can take down
/api/admin/presenter/current-statepolling if older data contains duplicate option labels. Prefer a non-fatal merge path so presenter views stay available.Suggested resilient fallback
export function buildQuestionOptionResults(question: Question, answerList: Answer[]): Results['results'] { const results = Object.create(null) as Results['results'] - const normalizedOptionLabels = new Set<string>() + const normalizedOptionLabels = new Set<string>() + const normalizedToResultKey = new Map<string, string>() for (const option of question.answer_options) { const resultKey = option.text.en const normalizedResultKey = resultKey.toLowerCase() - if (normalizedOptionLabels.has(normalizedResultKey)) { - throw new Error(`Duplicate answer option label is not supported: "${resultKey}"`) + if (normalizedOptionLabels.has(normalizedResultKey)) { + // Legacy/malformed data fallback: merge duplicates under first bucket + continue } normalizedOptionLabels.add(normalizedResultKey) + normalizedToResultKey.set(normalizedResultKey, resultKey) results[resultKey] = { count: 0, emoji: option.emoji, } } for (const answer of answerList) { - const selectedAnswer = answer.selected_answer.en + const selectedAnswer = answer.selected_answer.en.toLowerCase() + const resultKey = normalizedToResultKey.get(selectedAnswer) - if (Object.prototype.hasOwnProperty.call(results, selectedAnswer)) { - results[selectedAnswer]!.count += 1 + if (resultKey && Object.prototype.hasOwnProperty.call(results, resultKey)) { + results[resultKey]!.count += 1 } } return results }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/utils/quiz-results.ts` around lines 16 - 18, The current check throws an Error when normalizedOptionLabels.has(normalizedResultKey), which crashes presenter polling; change this to a non‑fatal merge: instead of throwing in the duplicate branch for normalizedResultKey/resultKey, log a warning and generate a unique fallback key (e.g., append a numeric suffix or “__dupN” to normalizedResultKey) until it is not present in normalizedOptionLabels, then continue processing and insert that remapped key into normalizedOptionLabels and the same aggregated state. Ensure the warning includes the original resultKey and the generated fallback key so debugging remains possible.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@server/utils/quiz-results.ts`:
- Around line 16-18: The current check throws an Error when
normalizedOptionLabels.has(normalizedResultKey), which crashes presenter
polling; change this to a non‑fatal merge: instead of throwing in the duplicate
branch for normalizedResultKey/resultKey, log a warning and generate a unique
fallback key (e.g., append a numeric suffix or “__dupN” to normalizedResultKey)
until it is not present in normalizedOptionLabels, then continue processing and
insert that remapped key into normalizedOptionLabels and the same aggregated
state. Ensure the warning includes the original resultKey and the generated
fallback key so debugging remains possible.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: ec7c9757-1957-4ad3-b37a-546c9a363ee4
📒 Files selected for processing (3)
docs/api.mdserver/api/questions/create.post.tsserver/utils/quiz-results.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- docs/api.md
Summary by CodeRabbit
New Features
Documentation
Bug Fixes / Validation