Skip to content

feat: interactive quiz UX - single question + navigation + progress (…#482

Open
Tanushreesmallikarjuna wants to merge 5 commits intoAOSSIE-Org:mainfrom
Tanushreesmallikarjuna:feature/quiz-progress-ui
Open

feat: interactive quiz UX - single question + navigation + progress (…#482
Tanushreesmallikarjuna wants to merge 5 commits intoAOSSIE-Org:mainfrom
Tanushreesmallikarjuna:feature/quiz-progress-ui

Conversation

@Tanushreesmallikarjuna
Copy link

@Tanushreesmallikarjuna Tanushreesmallikarjuna commented Feb 25, 2026

fixes #474

Addressed Issues:

Fixes #474

Demo Flow:

  1. Progress indicator shows "Question 1 of 5"
  2. Next/Previous navigation works smoothly
  3. Clean, single-question focus improves readability

Additional Notes:

  • Frontend-only changes (no backend/model modifications)
  • Responsive design works on mobile + desktop
  • Production-ready React components
  • Ready for mentor feedback and iteration

Checklist

  • My PR addresses a single issue, fixes a single bug or makes a single improvement.
  • My code follows the project's code style and conventions
  • If applicable, I have made corresponding changes or additions to the documentation
  • If applicable, I have made corresponding changes or additions to tests
  • My changes generate no new warnings or errors
  • I have joined the Discord server and I will share a link to this PR with the project maintainers there
  • I have read the Contribution Guidelines
  • Once I submit my PR, CodeRabbit AI will automatically review it and I will address CodeRabbit's comments.

AI Usage Disclosure

  • This PR does not contain AI-generated code at all.

Summary by CodeRabbit

  • New Features

    • Sequential navigation (Previous/Next) with question counter ("Question X of N").
    • Per-item Edit/Save/Cancel for the current question.
    • Generate Google Form action with on-screen error display.
  • UI/UX Changes

    • Single-view panel and compact EduAid header replacing multi-item dropdowns/PDF modes.
    • Navigation footer and per-question shuffled options including the correct answer.
  • Improvements

    • Combined question loading from local storage and consistent current-question editing flow.

@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Replaced per-item PDF mode and multi-view rendering with a single currentIndex-driven UI showing one QA pair at a time; added Previous/Next navigation, per-view editing tied to currentIndex, consolidated localStorage loading into qaPairs, revamped option shuffling, and updated Google Form generation/error handling.

Changes

Cohort / File(s) Summary
Single-view UI & Navigation
eduaid_web/src/pages/Output.jsx
Removed per-item pdfMode and multi-card rendering. Added currentIndex, "Question X of N" indicator, single QA panel, and handleNext / handlePrevious navigation with footer controls.
Editing state & handlers
eduaid_web/src/pages/Output.jsx
Refactored editing to operate on the active index: editedQuestion, editedAnswer, editedOptions and handlers (handleEditQuestion, handleSaveQuestion, handleCancelEdit) now target qaPairs[currentIndex].
Shuffle & Options Generation
eduaid_web/src/pages/Output.jsx
Introduced local shuffleArray helper; generate per-item shuffledOptions with deduplication and ensure inclusion of correct answer; handleShuffleQuestions shuffles qaPairs and resets currentIndex.
Persistence / Data loading
eduaid_web/src/pages/Output.jsx
Consolidated localStorage loading: combine MCQ/Short entries into a single qaPairs list and initialize currentIndex to 0. Simplified reconstruction logic.
Google Forms integration & error handling
eduaid_web/src/pages/Output.jsx
Updated generateGoogleForm and related flows to use the qaPairs + currentIndex shape; added formError state and adjusted error displays.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant UI as Output.jsx (UI)
    participant Storage as localStorage
    participant Google as Google Form API

    rect rgba(135,206,235,0.5)
    User->>UI: Click Next / Previous / Edit / Save
    UI->>UI: Update currentIndex, editing state, shuffledOptions
    UI->>Storage: Read/Write qaPairs (persist edits)
    Storage-->>UI: Acknowledge
    end

    rect rgba(144,238,144,0.5)
    User->>UI: Click "Generate Google Form"
    UI->>Google: POST form payload (qaPairs)
    Google-->>UI: Success / Error
    UI-->>User: Show success / error notification
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • #383: Modifies the same eduaid_web/src/pages/Output.jsx areas—editing handlers, shuffle logic, and qaPairs handling—likely overlapping with this change.

Poem

🐇 I hop through one question, tidy and bright,
A header counts each little plight.
I shuffle options, save with a tap,
From local store to form — a neat little map.
Hop, edit, send — a rabbit’s quiz-night clap! 🎓

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive The PR implements partial requirements from #474: single-question view, progress indicator, and Next/Previous navigation are complete, but immediate feedback after answering, final summary screen, and retry flow are acknowledged as not yet implemented by the author. Complete the remaining requirements from #474: implement answer interaction with immediate feedback, add final summary screen showing score and weak areas, and enable retry of incorrect questions.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: interactive quiz UX - single question + navigation + progress' accurately summarizes the main changes in the pull request, which include implementing single-question view with progress indicator and navigation controls.
Out of Scope Changes check ✅ Passed All changes are within scope of the linked issue #474 objectives; modifications to Output.jsx focus on frontend quiz UX with no backend/model changes, remaining frontend-only as required.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@eduaid_web/src/pages/Output.jsx`:
- Around line 36-37: The bug is that currentIndex can point past the newly
loaded qaPairs (and shuffledOptionsMap) causing blank renders; update the
component to reset currentIndex to a valid value whenever the loaded QA data
changes (e.g., when qaPairs length changes or new data is assigned) — inside the
effect or handler that sets qaPairs/shuffledOptionsMap, call setCurrentIndex(0)
or clamp it to Math.max(0, Math.min(currentIndex, qaPairs.length - 1)), and
similarly ensure any uses of shuffledOptionsMap[currentIndex] handle the new
index (reinitialize or clamp shuffledOptionsMap indices) so both the references
around qaPairs/currentIndex and shuffledOptionsMap/currentIndex are safe after
reload.
- Around line 157-246: The screen currently only supports navigation and editing
(qaPair, shuffledOptions, currentIndex, qaPairs, isEditing, handleEditQuestion,
handleSaveQuestion, handleCancelEdit, handlePrevious, handleNext,
generateGoogleForm) but lacks answer attempt flow, immediate feedback, scoring,
and retry-incorrect behavior; implement: add local state for selectedAnswer and
per-question result (correct/incorrect) and score summary, render clickable
answer buttons for shuffledOptions that call an attempt handler (e.g.,
handleAttemptAnswer(questionIndex, option)), show immediate feedback UI
(correct/incorrect) and disable further attempts for that question, track score
and list of incorrect question indices, add end-of-quiz summary view showing
total correct, percentage, and a "Retry Incorrect" action that resets only the
incorrect questions (or navigates through them) while preserving edits, and wire
these flows into navigation (handleNext/handlePrevious) so feedback and attempt
state persist when paging between questions.
- Around line 145-147: The progress and nav controls don't handle an empty
qaPairs list or editing state: change the progress header that uses currentIndex
and qaPairs to conditionally render a friendly empty state (e.g., "No questions"
or nothing) when qaPairs.length === 0, and only render "Question {currentIndex +
1} of {qaPairs.length}" when qaPairs.length > 0; additionally, disable the
navigation buttons (Prev/Next) and any handlers like handlePrev/handleNext when
qaPairs.length === 0 or when the editing flag (e.g., isEditing) is true so
navigation is unavailable while editing or when there are no questions.
- Around line 30-33: The shuffledOptionsMap useMemo currently appends qa.answer
unconditionally which can create duplicates when qa.answer already exists in
qa.options; update the logic inside useMemo (where shuffledOptionsMap is
computed from qaPairs and uses shuffleArray) to first build a deduplicated
options array—e.g., take qa.options || [], filter out any entries equal to
qa.answer, then push qa.answer once (or use a Set) before calling
shuffleArray—so shuffledOptionsMap contains unique choices with the correct
answer included exactly once.
- Around line 95-118: The effect reading localStorage in useEffect can throw if
the stored value is missing or malformed and will crash when accessing nested
arrays like stored.output_mcq.questions or stored.output.forEach; wrap the
JSON.parse in a try/catch and guard downstream accesses by verifying stored is
an object and using Array.isArray before iterating (e.g., check
stored.output_mcq && Array.isArray(stored.output_mcq.questions) and
stored.output && Array.isArray(stored.output)), and handle fallback empty arrays
so combined building always iterates safely without throwing (retain existing
fields question/question_statement, answer, options, question_type).

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fc3bf1a and 658c249.

📒 Files selected for processing (1)
  • eduaid_web/src/pages/Output.jsx

@Tanushreesmallikarjuna
Copy link
Author

Thanks for the detailed review 🙌
You’re right — this PR currently completes the navigation and progress UX, but not the full quiz-taking flow required by #474.

I’m extending this PR to add:

Answer attempt interaction with immediate correct/incorrect feedback

Per-question result persistence across navigation

Final summary screen with score and weak areas

Retry-incorrect flow

I’m also addressing the noted robustness fixes (deduplicated options, index reset on reload, localStorage guards, and empty-state handling).

Will update shortly for re-review.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
eduaid_web/src/pages/Output.jsx (2)

178-180: Consider adding an empty-state message.

When qaPairs is empty, the content area renders nothing. A user-friendly message like "No questions available" would improve the experience when there's no data to display.

💡 Suggested empty state UI
         <div className="flex-1 overflow-y-auto px-6 mt-4">
-          {qaPair && (
+          {!hasQuestions ? (
+            <div className="text-gray-400 text-center py-8">
+              No questions available. Please generate questions first.
+            </div>
+          ) : qaPair && (
             <div className="bg-[`#ffffff0d`] p-4 rounded-xl">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eduaid_web/src/pages/Output.jsx` around lines 178 - 180, The UI currently
renders nothing when there are no qaPairs; update the render logic in Output.jsx
around the qaPair/qaPairs usage so that when qaPairs is empty (or qaPair is
falsy) you render a simple empty-state message/component (e.g., "No questions
available" with subtle styling inside the same container) instead of nothing;
change the conditional that now only shows the <div className="bg-[`#ffffff0d`]
p-4 rounded-xl"> when qaPair exists to render the empty-state element when
qaPairs.length === 0 or !qaPair, keeping the same layout container and class
names for consistent spacing.

165-176: Duplicate progress indicator is intentional but consider consolidating.

The "Question X of Y" text appears both in the header (line 167) and in the navigation footer (line 250). If this duplication is intentional for visibility, consider differentiating their styling or removing one to reduce visual redundancy.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eduaid_web/src/pages/Output.jsx` around lines 165 - 176, The UI shows a
duplicated progress indicator "Question {hasQuestions ? currentIndex + 1 : 0} of
{totalQuestions}" in both the header and navigation footer; decide whether to
remove one or differentiate them: either remove the header instance (the div
using hasQuestions/currentIndex/totalQuestions) or adjust its styling/text
(e.g., shorten to "Q {currentIndex+1}/{totalQuestions}" or move into an
aria-label) so it doesn't visually duplicate the footer; update the JSX where
that expression is rendered and ensure any related logic (hasQuestions,
currentIndex, totalQuestions) remains used where needed and accessibility is
preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@eduaid_web/src/pages/Output.jsx`:
- Around line 202-233: The edit UI is missing a field to modify editedAnswer
(initialized in handleEditQuestion) so the answer never changes; add a
controlled input (or textarea) bound to editedAnswer with onChange to
setEditedAnswer inside the edit block that renders editedQuestion/editedOptions
(near the map over editedOptions and before the Save/Cancel buttons) and ensure
handleSaveQuestion uses editedAnswer when updating the question at currentIndex
so saved edits include the new answer.

---

Nitpick comments:
In `@eduaid_web/src/pages/Output.jsx`:
- Around line 178-180: The UI currently renders nothing when there are no
qaPairs; update the render logic in Output.jsx around the qaPair/qaPairs usage
so that when qaPairs is empty (or qaPair is falsy) you render a simple
empty-state message/component (e.g., "No questions available" with subtle
styling inside the same container) instead of nothing; change the conditional
that now only shows the <div className="bg-[`#ffffff0d`] p-4 rounded-xl"> when
qaPair exists to render the empty-state element when qaPairs.length === 0 or
!qaPair, keeping the same layout container and class names for consistent
spacing.
- Around line 165-176: The UI shows a duplicated progress indicator "Question
{hasQuestions ? currentIndex + 1 : 0} of {totalQuestions}" in both the header
and navigation footer; decide whether to remove one or differentiate them:
either remove the header instance (the div using
hasQuestions/currentIndex/totalQuestions) or adjust its styling/text (e.g.,
shorten to "Q {currentIndex+1}/{totalQuestions}" or move into an aria-label) so
it doesn't visually duplicate the footer; update the JSX where that expression
is rendered and ensure any related logic (hasQuestions, currentIndex,
totalQuestions) remains used where needed and accessibility is preserved.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 658c249 and c67afae.

📒 Files selected for processing (1)
  • eduaid_web/src/pages/Output.jsx

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
eduaid_web/src/pages/Output.jsx (2)

203-217: ⚠️ Potential issue | 🟠 Major

editedAnswer is still not editable in edit mode.

Line 74 initializes editedAnswer and Line 83 saves it, but there is no bound input in the edit UI, so answer edits cannot be made.

🔧 Proposed fix
                 <textarea
                   value={editedQuestion}
                   onChange={(e) => setEditedQuestion(e.target.value)}
                   className="w-full p-2 bg-black text-white rounded"
                 />
+                <input
+                  value={editedAnswer}
+                  onChange={(e) => setEditedAnswer(e.target.value)}
+                  className="w-full p-2 mt-2 bg-black text-white rounded"
+                  placeholder="Correct answer"
+                />
 
                 {editedOptions.map((opt, i) => (
                   <input
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eduaid_web/src/pages/Output.jsx` around lines 203 - 217, The edit UI is
missing a bound input for editedAnswer so edits are not saved; add a controlled
input (e.g., a textarea or input) bound to editedAnswer with an onChange that
calls setEditedAnswer(value) placed alongside the existing editedQuestion
textarea and the editedOptions mapping (near editedQuestion, editedOptions,
handleOptionChange). Ensure the new input uses the same styling pattern and a
unique key if in a list so the saved editedAnswer (used when saving on line ~83)
reflects user changes.

178-272: ⚠️ Potential issue | 🟠 Major

Issue #474 flow is still incomplete in this screen.

Line 185–193 only displays options; there is still no answer-attempt interaction, immediate correct/incorrect feedback, final summary, or retry-incorrect flow required by #474.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eduaid_web/src/pages/Output.jsx` around lines 178 - 272, The UI currently
only displays options (shuffledOptions) and lacks answer-attempt flow, immediate
feedback, final summary and retry-incorrect flow required by `#474`; implement an
interactive answer flow by adding state (selectedOption, attemptsByIndex,
feedbackMessage, incorrectIndices, showSummary) and new handlers
(handleOptionSelect/handleAnswerSubmit/handleRetryIncorrect) to the Output
component: render radio/buttons for options that call handleOptionSelect,
validate submissions in handleAnswerSubmit (compare selectedOption to
qaPair.correctAnswer or canonical answer), update attemptsByIndex and
incorrectIndices, set feedbackMessage to immediate correct/incorrect feedback
and optionally show the correct answer, disable navigation while answering (use
isEditing-like guard), and at the end (when currentIndex === totalQuestions-1 or
via a dedicated finish button) show a summary view (showSummary) with total
correct, list of incorrect questions and a "Retry Incorrect" button that calls
handleRetryIncorrect to reset currentIndex to the first incorrect index and
clear only those question attempt states; ensure existing functions
(handlePrevious, handleNext, generateGoogleForm) respect the new guards and that
shuffledOptions usage integrates with answer checking.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@eduaid_web/src/pages/Output.jsx`:
- Around line 145-150: The response handling for the POST to /generate_gform
uses res.form_link directly and calls window.open without security flags; update
the code around apiClient.post("/generate_gform", ...) to unwrap the actual
response payload (e.g., use res.data or the shape returned by your client),
validate that the form link is a non-empty string (throw or show an error if
missing), and open it with window.open(validUrl, "_blank",
"noopener,noreferrer") (or create a temporary anchor with rel="noopener
noreferrer") to prevent opener exposure; ensure checks are applied before
calling window.open to avoid silent failures.

---

Duplicate comments:
In `@eduaid_web/src/pages/Output.jsx`:
- Around line 203-217: The edit UI is missing a bound input for editedAnswer so
edits are not saved; add a controlled input (e.g., a textarea or input) bound to
editedAnswer with an onChange that calls setEditedAnswer(value) placed alongside
the existing editedQuestion textarea and the editedOptions mapping (near
editedQuestion, editedOptions, handleOptionChange). Ensure the new input uses
the same styling pattern and a unique key if in a list so the saved editedAnswer
(used when saving on line ~83) reflects user changes.
- Around line 178-272: The UI currently only displays options (shuffledOptions)
and lacks answer-attempt flow, immediate feedback, final summary and
retry-incorrect flow required by `#474`; implement an interactive answer flow by
adding state (selectedOption, attemptsByIndex, feedbackMessage,
incorrectIndices, showSummary) and new handlers
(handleOptionSelect/handleAnswerSubmit/handleRetryIncorrect) to the Output
component: render radio/buttons for options that call handleOptionSelect,
validate submissions in handleAnswerSubmit (compare selectedOption to
qaPair.correctAnswer or canonical answer), update attemptsByIndex and
incorrectIndices, set feedbackMessage to immediate correct/incorrect feedback
and optionally show the correct answer, disable navigation while answering (use
isEditing-like guard), and at the end (when currentIndex === totalQuestions-1 or
via a dedicated finish button) show a summary view (showSummary) with total
correct, list of incorrect questions and a "Retry Incorrect" button that calls
handleRetryIncorrect to reset currentIndex to the first incorrect index and
clear only those question attempt states; ensure existing functions
(handlePrevious, handleNext, generateGoogleForm) respect the new guards and that
shuffledOptions usage integrates with answer checking.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c67afae and eb21f1a.

⛔ Files ignored due to path filters (1)
  • eduaid_web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (1)
  • eduaid_web/src/pages/Output.jsx

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (5)
eduaid_web/src/pages/Output.jsx (5)

143-160: Consider providing user feedback on form generation failure.

The error handling logs to console but the user receives no indication when form generation fails. Consider adding a toast notification or error state.

💡 Example approach using state
+ const [formError, setFormError] = useState(null);
+
  const generateGoogleForm = async () => {
+   setFormError(null);
    try {
      const res = await apiClient.post("/generate_gform", {
        qa_pairs: qaPairs,
        question_type: questionType,
      });

      const formUrl = res?.form_link;

      if (formUrl) {
        window.open(formUrl, "_blank", "noopener,noreferrer");
      } else {
        console.error("Form link missing in response", res);
+       setFormError("Failed to generate form. Please try again.");
      }
    } catch (err) {
      console.error("Failed to generate Google Form:", err);
+     setFormError("Failed to generate form. Please try again.");
    }
  };

Then render {formError && <p className="text-red-500 text-sm">{formError}</p>} near the button.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eduaid_web/src/pages/Output.jsx` around lines 143 - 160, The
generateGoogleForm function currently only logs errors to the console; add
user-facing feedback by introducing a state variable (e.g., formError or use a
toast utility) and set it inside both the "else" branch when formUrl is missing
and inside the catch block when the request fails, including a short,
user-friendly message plus optional details; then update the component render to
show {formError && <p className="text-red-500 text-sm">{formError}</p>} near the
generate button (or trigger the toast) so users see failures related to
qaPairs/questionType and API errors.

226-232: Answer editor field placement after options may confuse users.

The answer input appears after the options list, which could lead users to think it's another option rather than the correct answer field. Consider adding a label or moving it to a more prominent position (e.g., immediately after the question textarea).

💡 Add a label for clarity
 {/* ✅ Answer editor (CodeRabbit fix) */}
+<label className="text-gray-400 text-sm mt-3 block">Correct Answer</label>
 <input
   value={editedAnswer}
   onChange={(e) => setEditedAnswer(e.target.value)}
   placeholder="Correct answer"
-  className="w-full p-2 mt-3 bg-black text-white rounded border border-gray-600"
+  className="w-full p-2 mt-1 bg-black text-white rounded border border-green-600"
 />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eduaid_web/src/pages/Output.jsx` around lines 226 - 232, The answer input
(controlled by editedAnswer and setEditedAnswer) is currently rendered after the
options list and may be mistaken for an option; relocate this input immediately
after the question textarea or add a clear label (e.g., "Correct answer") and
associate it with the input so users can’t confuse it with options—update the
JSX where editedAnswer is used to either move the input node to just below the
question textarea component or wrap it with a <label> and accessible attributes
to clarify its purpose.

217-224: Inconsistent indentation in option inputs.

The <input> elements for editing options have inconsistent indentation compared to surrounding code (they're not indented within the parent block). This doesn't affect functionality but reduces readability.

🧹 Consistent indentation
-                  {editedOptions.map((opt, i) => (
-  <input
-    key={i}
-    value={opt}
-    onChange={(e) => handleOptionChange(i, e.target.value)}
-    className="w-full p-2 mt-2 bg-black text-white rounded"
-  />
-))}
+                  {editedOptions.map((opt, i) => (
+                    <input
+                      key={i}
+                      value={opt}
+                      onChange={(e) => handleOptionChange(i, e.target.value)}
+                      className="w-full p-2 mt-2 bg-black text-white rounded"
+                    />
+                  ))}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eduaid_web/src/pages/Output.jsx` around lines 217 - 224, The JSX block
rendering editedOptions has inconsistent indentation for the input elements;
tidy the formatting inside the map callback so the <input> is properly indented
under the JSX expression for readability—locate the map over editedOptions and
the onChange that calls handleOptionChange and align/indent the <input> tag and
its props consistently with surrounding JSX.

202-207: Icon components render as separate elements without visible alignment.

The icon components (FiEdit2, FiCheck, FiX) are rendered adjacent to text without flex alignment, which may cause inconsistent vertical positioning. Adding flex items-center gap-1 to these buttons would ensure proper alignment.

💅 Proposed fix for button styling
-                  <button
-                    className="mt-4 bg-teal-600 px-4 py-2 rounded text-white"
+                  <button
+                    className="mt-4 bg-teal-600 px-4 py-2 rounded text-white flex items-center gap-1"
                     onClick={() => handleEditQuestion(currentIndex)}
                   >
                     <FiEdit2 /> Edit
                   </button>

...

-                    <button
-                      onClick={() => handleSaveQuestion(currentIndex)}
-                      className="bg-green-600 px-4 py-2 rounded text-white"
+                    <button
+                      onClick={() => handleSaveQuestion(currentIndex)}
+                      className="bg-green-600 px-4 py-2 rounded text-white flex items-center gap-1"
                     >
                       <FiCheck /> Save
                     </button>
-                    <button
-                      onClick={handleCancelEdit}
-                      className="bg-gray-600 px-4 py-2 rounded text-white"
+                    <button
+                      onClick={handleCancelEdit}
+                      className="bg-gray-600 px-4 py-2 rounded text-white flex items-center gap-1"
                     >
                       <FiX /> Cancel
                     </button>

Also applies to: 234-246

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eduaid_web/src/pages/Output.jsx` around lines 202 - 207, The button icons
(FiEdit2, FiCheck, FiX) are not vertically aligned with their button text;
update the button elements in Output.jsx (the buttons that call
handleEditQuestion and the other buttons around lines with FiCheck/FiX) to
include the Tailwind classes "flex items-center gap-1" on their className so the
icon and text render as a single aligned inline row; adjust the className
strings for the Edit button and the corresponding Confirm/Cancel buttons to
include those classes.

172-175: Progress indicator is duplicated in header and navigation footer.

The "Question X of Y" text appears both at line 174 (header) and line 265 (footer nav). While this may be intentional for UX, consider whether both are necessary since they show identical information and the page is a single-question view.

Also applies to: 264-266

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eduaid_web/src/pages/Output.jsx` around lines 172 - 175, The "Question X of
Y" progress indicator is rendered twice in Output.jsx (header block inside the
div with className "flex justify-between items-center px-6" and again in the
footer nav around lines 264-266); remove the duplicate by keeping the indicator
in the preferred location (either header or footer) and deleting the other JSX,
or make it conditional (e.g., renderProgress && ...) so it only shows in one
place; update any references to hasQuestions/currentIndex/totalQuestions in the
removed location to avoid unused variables or dead JSX and ensure the remaining
instance uses the existing state variables (hasQuestions, currentIndex,
totalQuestions) to preserve functionality.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@eduaid_web/src/pages/Output.jsx`:
- Around line 143-160: The generateGoogleForm function currently only logs
errors to the console; add user-facing feedback by introducing a state variable
(e.g., formError or use a toast utility) and set it inside both the "else"
branch when formUrl is missing and inside the catch block when the request
fails, including a short, user-friendly message plus optional details; then
update the component render to show {formError && <p className="text-red-500
text-sm">{formError}</p>} near the generate button (or trigger the toast) so
users see failures related to qaPairs/questionType and API errors.
- Around line 226-232: The answer input (controlled by editedAnswer and
setEditedAnswer) is currently rendered after the options list and may be
mistaken for an option; relocate this input immediately after the question
textarea or add a clear label (e.g., "Correct answer") and associate it with the
input so users can’t confuse it with options—update the JSX where editedAnswer
is used to either move the input node to just below the question textarea
component or wrap it with a <label> and accessible attributes to clarify its
purpose.
- Around line 217-224: The JSX block rendering editedOptions has inconsistent
indentation for the input elements; tidy the formatting inside the map callback
so the <input> is properly indented under the JSX expression for
readability—locate the map over editedOptions and the onChange that calls
handleOptionChange and align/indent the <input> tag and its props consistently
with surrounding JSX.
- Around line 202-207: The button icons (FiEdit2, FiCheck, FiX) are not
vertically aligned with their button text; update the button elements in
Output.jsx (the buttons that call handleEditQuestion and the other buttons
around lines with FiCheck/FiX) to include the Tailwind classes "flex
items-center gap-1" on their className so the icon and text render as a single
aligned inline row; adjust the className strings for the Edit button and the
corresponding Confirm/Cancel buttons to include those classes.
- Around line 172-175: The "Question X of Y" progress indicator is rendered
twice in Output.jsx (header block inside the div with className "flex
justify-between items-center px-6" and again in the footer nav around lines
264-266); remove the duplicate by keeping the indicator in the preferred
location (either header or footer) and deleting the other JSX, or make it
conditional (e.g., renderProgress && ...) so it only shows in one place; update
any references to hasQuestions/currentIndex/totalQuestions in the removed
location to avoid unused variables or dead JSX and ensure the remaining instance
uses the existing state variables (hasQuestions, currentIndex, totalQuestions)
to preserve functionality.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb21f1a and da64fd8.

📒 Files selected for processing (1)
  • eduaid_web/src/pages/Output.jsx

@Tanushreesmallikarjuna
Copy link
Author

Thanks for the review!

✅ Added user-facing error feedback for Google Form generation
✅ Added a labeled “Correct Answer” field for clarity
✅ Fixed indentation for option inputs
✅ Updated button styles to properly align icons

I’ve intentionally kept the progress indicator in both header and footer for better visibility while navigating questions.

Please let me know if you’d like any further tweaks!

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
eduaid_web/src/pages/Output.jsx (2)

27-33: Options are re-shuffled for all questions when any question is edited.

When handleSaveQuestion updates qaPairs, this useMemo recomputes and calls shuffleArray for every question with fresh Math.random() calls. This causes all questions' options to be reshuffled unexpectedly after editing a single question.

Consider computing the shuffled options once (on initial load or explicit shuffle) and storing them in state, updating only the specific question's options when edited.

♻️ Suggested approach
-const shuffledOptionsMap = useMemo(() => {
-  return qaPairs.map((qa) =>
-    qa.options
-      ? shuffleArray([...new Set([...(qa.options || []), qa.answer].filter(Boolean))])
-      : []
-  );
-}, [qaPairs]);

+const [shuffledOptionsMap, setShuffledOptionsMap] = useState([]);
+
+// Compute shuffled options once when qaPairs is loaded
+useEffect(() => {
+  setShuffledOptionsMap(
+    qaPairs.map((qa) =>
+      qa.options
+        ? shuffleArray([...new Set([...(qa.options || []), qa.answer].filter(Boolean))])
+        : []
+    )
+  );
+}, [qaPairs.length]); // Only recompute when count changes, not on edits

Then update handleSaveQuestion to also update the specific index in shuffledOptionsMap if options changed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eduaid_web/src/pages/Output.jsx` around lines 27 - 33, The current useMemo
for shuffledOptionsMap calls shuffleArray on every qaPairs change, causing all
questions to reshuffle whenever any question is edited; instead initialize
shuffledOptionsMap as component state (e.g., useState(() => ...)) once from
qaPairs on mount or when explicitly shuffling, and remove the useMemo, then
update only the specific index inside handleSaveQuestion when that question's
options or answer change (use the same shuffleArray to generate the new options
for that index and setState immutably); keep the symbols shuffledOptionsMap,
shuffleArray, handleSaveQuestion and qaPairs to locate and modify the logic.

169-237: Add a friendly empty state when no questions are available.

When qaPairs is empty, the content area renders nothing. Consider showing a user-friendly message to guide users.

💡 Proposed enhancement
         <div className="flex-1 overflow-y-auto px-6 mt-4">
+          {!hasQuestions && (
+            <div className="text-gray-400 text-center py-8">
+              No questions available. Please generate questions first.
+            </div>
+          )}
           {qaPair && (
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eduaid_web/src/pages/Output.jsx` around lines 169 - 237, The view renders
nothing when there are no questions (qaPairs empty / qaPair falsy); add a
friendly empty state in the same container (inside the div with className
"flex-1 overflow-y-auto px-6 mt-4") that displays a message like "No questions
yet" and guidance plus an action button (e.g., "Create Question" or "Upload
Questions") which calls an existing handler (or a new handler such as
handleCreateQuestion) to start adding questions; ensure the empty-state is shown
when qaPair is falsy or qaPairs.length === 0 and removed when
currentIndex/qaPair exists so the existing edit/view UI (using qaPair,
shuffledOptions, handleEditQuestion, handleSaveQuestion, handleCancelEdit)
continues to work unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@eduaid_web/src/pages/Output.jsx`:
- Around line 27-33: The current useMemo for shuffledOptionsMap calls
shuffleArray on every qaPairs change, causing all questions to reshuffle
whenever any question is edited; instead initialize shuffledOptionsMap as
component state (e.g., useState(() => ...)) once from qaPairs on mount or when
explicitly shuffling, and remove the useMemo, then update only the specific
index inside handleSaveQuestion when that question's options or answer change
(use the same shuffleArray to generate the new options for that index and
setState immutably); keep the symbols shuffledOptionsMap, shuffleArray,
handleSaveQuestion and qaPairs to locate and modify the logic.
- Around line 169-237: The view renders nothing when there are no questions
(qaPairs empty / qaPair falsy); add a friendly empty state in the same container
(inside the div with className "flex-1 overflow-y-auto px-6 mt-4") that displays
a message like "No questions yet" and guidance plus an action button (e.g.,
"Create Question" or "Upload Questions") which calls an existing handler (or a
new handler such as handleCreateQuestion) to start adding questions; ensure the
empty-state is shown when qaPair is falsy or qaPairs.length === 0 and removed
when currentIndex/qaPair exists so the existing edit/view UI (using qaPair,
shuffledOptions, handleEditQuestion, handleSaveQuestion, handleCancelEdit)
continues to work unchanged.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da64fd8 and c75d4b9.

📒 Files selected for processing (1)
  • eduaid_web/src/pages/Output.jsx

@Tanushreesmallikarjuna
Copy link
Author

All CodeRabbit feedback has been addressed.
The option shuffle is now stable per question, and a friendly empty state has been added.
Ready for maintainer review. Thanks!

This signals confidence and closure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[GOOD FIRST ISSUE]: Improve Quiz-Taking UX with Progress, Feedback, and Summary Flow

1 participant