Project

General

Profile

Actions

Task #194

open

Nikita's Task List

Added by Ayush Yadav on 09/10/2026 11:18 AM. Updated on 09/21/2026 12:55 PM.

Status:
New
Priority:
Normal
Start date:
09/10/2026
Due date:
% Done:

0%

Estimated time:

Description

Nikita's task list — Gantt W11 to W14 (21 Sep to 16 Oct 2026)

Read this first: your lane has changed

Until now you worked on the Spring Boot backend and the admin panel. From 21 September 2026 you own the React frontend — the part a student actually sees. Harshit has taken over the backend on issue #193.

Everything you built in W9 and W10 stays and is being used: PlanRules.isPremium, the admin plan dropdown, the Faculty and Candidate tabs, bonus marks in scoring, and the attempt audit log. You now build the student-facing screens on top of that work.

Tickets you will comment on: #95 (M4), #99 (M8), #100 (M9), #102 (M11), #103 (M12), #93 (M2), #97 (M6), #112 (QB-1). Do not create new tickets.

How to read every task below

Each task always has the same four parts:

  • Goal — one sentence saying what the finished thing does.
  • Why — the reason it matters, so you understand the task instead of just copying it.
  • Steps — what to actually change, with the real file paths from our repository.
  • Done when — how you prove to yourself it works before you say it is finished.

The calendar (no Saturdays, no Sundays)

Week Working days
W11 Mon 21, Tue 22, Wed 23, Thu 24, Fri 25 September
W12 Mon 28, Tue 29, Wed 30 September, Thu 1, Fri 2 October — Phase 1 MVP must be finished by this Friday
W13 Mon 5, Tue 6, Wed 7, Thu 8, Fri 9 October — Phase 2 opens
W14 Mon 12, Tue 13, Wed 14, Thu 15, Fri 16 October

That is 20 working days and 20 tasks.

Ground rules for all your work

  • The API is at http://localhost:8081/api. Never use port 8080.
  • Functional components only. Routes go in Frontend/src/App.tsx, nav items in Frontend/src/components/Header.tsx.
  • CSS lives in a file next to the component that uses it. Do not add Tailwind, Redux, axios, or any new UI kit.
  • Use the helpers in Frontend/src/utils/api.ts (API_BASE, authHeaders, apiFetch, readError) so the session token is always sent.
  • Auth helpers are in Frontend/src/utils/auth.ts: getLoggedInUser, setLoggedInUser, clearLoggedInUser, isLoggedIn, isPremiumPlan, displayPlanName, canAccessAdminPanel.
  • Never commit a .env file, a password or an API key.

The biggest problem, and why W11 looks like it does

Our Spring Boot backend has a complete, working exam engine. It starts attempts, saves answers, submits, scores with negative marks, partial marks and bonus marks, and calculates rank and percentile. But no student screen calls it.

Frontend/src/LMS/Practice.tsx, the assessment modal in Frontend/src/pages/Assesment.tsx, and the CAT quizzes all score questions inside the browser and then throw the result away. The only code that touches /attempts is an admin testing harness in Frontend/src/pages/admin/TestAuthoringPanel.tsx.

This is why your whole first week is about connecting the two halves. Nothing else in Phase 1 is worth much until a real student attempt is saved in the database. Read TestAuthoringPanel.tsx before you start — it already calls these endpoints correctly and is your best example.

Week 11 — Mon 21 to Fri 25 September

N1 — Monday 21 September: Start a real attempt when a student begins practice (M4, #95)

Goal. Pressing Start on the practice screen creates a real attempt row in the database and the screen remembers its id.

Why. This is the first step of connecting our student screens to the backend that already exists. Until an attempt id exists, nothing else can be saved.

Steps.

  1. Open Frontend/src/LMS/Practice.tsx. Read TestAuthoringPanel.tsx first and copy the way it calls the API.
  2. When the student starts, call POST http://localhost:8081/api/attempts/start using apiFetch and authHeaders.
  3. If you have a real test id, send it. If this is free practice, send the exam code instead and let the backend build a practice test for you — AssessmentService.createPracticeTest already does exactly that.
  4. Store the returned attempt in React state: attemptId, the questions array, durationMinutes and navMode.
  5. Drive the countdown clock from the server's durationMinutes and startedAt, not from a number hardcoded in the component.
  6. Handle failures properly. If the API is down, show a clear message like "Could not start the test, please try again" and keep the Start button usable. Do not show a blank screen and do not silently fall back to offline mode.
  7. Save the attempt id in sessionStorage too, so a page refresh does not orphan the attempt.

Done when. Start a practice test, then run SELECT * FROM assessment_attempts ORDER BY id DESC LIMIT 1 in MySQL and see your row with status IN_PROGRESS. The questions on screen are the ones the API returned.

N2 — Tuesday 22 September: Autosave every answer, and add a real Save and Next button (M4, #95)

Goal. Every answer a student picks is stored on the server within a few seconds, and closing the browser does not lose their work.

Why. Right now answers live only in React state. If the browser crashes or the battery dies, two hours of work vanishes. Every real CBT system saves continuously, and the backend endpoint for this already exists and is simply not being used.

Steps.

  1. Build one function saveAnswers() that calls PUT http://localhost:8081/api/attempts/{attemptId}/answers.
  2. Send the full current answer list each time: for every question the testQuestionId, responseText, markedForReview and timeSpentSeconds. Also send the running tabSwitchCount.
  3. Call it in three situations: when the student changes an answer (wait about 800 milliseconds after they stop clicking so rapid changes become one call), on a timer every 15 seconds, and when they press Save and Next.
  4. Rename the button. It currently says "Next" — it must say Save & Next, and it must wait for the save to return before moving on.
  5. Show a small saving indicator near the timer with three states: "Saving...", "Saved at 14:32", and "Not saved — retrying". Students need to trust that their work is safe.
  6. If a save fails, retry three times with an increasing gap. If it still fails, keep the answers in state and keep the warning visible. Never block the student from continuing.
  7. Track time per question. Record a timestamp when a question appears and add the elapsed seconds when they leave it. The result page in N4 needs this data.

Done when. Answer three questions, force-close the browser tab, reopen the attempt, and all three answers are still selected. The network tab shows a PUT roughly every 15 seconds while you work.

N3 — Wednesday 23 September: Submit through the server and show the real score (M4 and M5, #95 and #96)

Goal. Finishing a test sends it to the server, and the score shown is the server's score.

Why. The browser currently counts correct answers itself. That is wrong twice over. First it ignores negative marking, partial marking for multiple-correct questions, and bonus marks — all of which the backend applies properly, and which you wrote. Second, a score calculated in the browser can be edited by the student, so it can never be trusted.

Steps.

  1. Call POST http://localhost:8081/api/attempts/{attemptId}/submit. Before you do, run one final saveAnswers() so nothing in flight is lost.
  2. Add a confirmation dialog before submitting. Show how many questions are answered, unanswered, and marked for review. Let the student cancel.
  3. Delete the browser-side scoring code. Read score, maxScore, accuracy, attemptRank and percentile from the submit response instead.
  4. Never let a student submit twice. Disable the button as soon as it is pressed and keep it disabled.
  5. Handle the expired-attempt error Harshit is adding on Tuesday. If the server says time is up, show "Your time is over, your paper has been submitted automatically" and take the student to their result.
  6. Also submit automatically when the countdown reaches zero, without waiting for a click.

Done when. Submit a paper where you deliberately got some answers wrong. The score matches what you would calculate by hand including negative marks, and it matches the score column in assessment_attempts.

N4 — Thursday 24 September: Build the student result page (M8, #99)

Goal. A page at /results/:attemptId showing a student everything about how they performed.

Why. GET /api/reports/attempts/{id} already returns a rich report — score, rank, percentile, accuracy, time per question, time per section, and accuracy grouped by topic. Only the admin panel displays it. Students, the people who actually need this, cannot see it at all.

Steps.

  1. Create Frontend/src/pages/Results.tsx with Results.css beside it, and register /results/:attemptId in App.tsx.
  2. Read the id with useParams and fetch GET http://localhost:8081/api/reports/attempts/{id}.
  3. Lay the page out in four blocks:
    • Summary at the top — score out of max, accuracy percentage, rank, percentile, total time taken.
    • Section breakdown — one row per section from sectionTimes, showing name and time spent.
    • Topic strengths and weaknesses — read topicInsights, sort by accuracy, show the top three as "Your strong topics" in green and the bottom three as "Needs more practice" in red. Each row shows topic, attempted, correct and accuracy.
    • Question by question review — question text, the student's answer, the correct answer, right or wrong, marks awarded, time spent. Use colour for right and wrong.
  4. Show a loading state while fetching, and a friendly message if the attempt does not exist or belongs to someone else.
  5. Do not add a charting library. Simple coloured bars made with div elements and a percentage width are enough.
  6. Make it work on a phone. Students check results on mobile far more than on a laptop.

Done when. Finish a real attempt, open /results/{thatId}, and every number matches the JSON that GET /api/reports/attempts/{id} returns.

N5 — Friday 25 September: A list of past attempts, and a link to each result (M8, #99)

Goal. A student can find every test they have taken and reopen any result.

Why. N4 built the result page, but the only way to reach it is by typing the URL, and nobody will do that. Students also want to see whether they are improving over time.

Steps.

  1. Add a large View detailed result button on the finish screen in Practice.tsx, linking to /results/{attemptId}.
  2. Create Frontend/src/pages/MyAttempts.tsx with its own CSS, and add the route /my-attempts in App.tsx.
  3. Fetch GET http://localhost:8081/api/attempts/me. This endpoint already exists.
  4. Show a card per attempt with the test name, date, score out of max, accuracy and status. Newest first. Clicking a card opens its result page.
  5. Add a status chip. IN_PROGRESS shows Resume and goes back into the test. SUBMITTED shows View result.
  6. Add a "My Attempts" link in Header.tsx, shown only when isLoggedIn() is true.
  7. If the student has never taken a test, show a friendly empty state with a button to the Practice Hub, not a blank page.

Done when. Take two tests, open /my-attempts, see both newest first, and click through to each result. Leave one unfinished and check that Resume takes you back in with your answers still selected.

Week 12 — Mon 28 September to Fri 2 October

Theme: finish Phase 1. Everything on the Gantt for M6, M8, M9 and M10 ends this Friday.

N6 — Monday 28 September: Show all five subscription plans (M9, #100)

Goal. The Plans page shows Freemium, Monthly, Quarterly, Annual and Enterprise as five separate options.

Why. The backend supports five plans — your own PlanRules.ALLOWED_PLANS lists them and the admin dropdown already offers all five. But Frontend/src/pages/Membership.tsx only shows two cards, Free and Premium. A customer cannot buy Quarterly or Annual because we never show them, so we are losing the higher-value sales.

Steps.

  1. Open Frontend/src/pages/Membership.tsx. Replace the two-card layout with five cards driven by a single array at the top of the file, so prices are easy to change later.
  2. Each card needs a plan name, price, billing period, a short list of what is included, and a button.
  3. Show the saving on the longer plans, for example "Save 20 percent compared to Monthly". That is why customers pick them.
  4. Mark one plan as most popular with a visible highlight. Quarterly is the usual choice.
  5. The Enterprise card says "Contact us" and links to /contact rather than starting a checkout, because enterprise deals are negotiated.
  6. Read the logged-in user's plan with getLoggedInUser(). On their current plan, replace the button with a "Your current plan" label.
  7. Pass the chosen plan name through to Frontend/src/pages/Checkout.tsx, so Harshit's payment fix can record which plan was actually purchased.
  8. Make the five cards stack into one column on a phone.

Done when. All five plans are visible, a Freemium user sees "Your current plan" on the Freemium card, and selecting Annual carries the name ANNUAL through to checkout.

N7 — Tuesday 29 September: Lock premium exams and send everyone to one place (M9, #100)

Goal. A free user opening a premium exam sees an upgrade prompt, and every upgrade prompt in the app leads to the same page.

Why. Two problems. First, exams already carry a segment of FREE or PREMIUM and ExamTicketCard already shows the badge, but nothing checks it when the exam actually opens — the badge is decoration. Second, UpgradeModal sends users to /payment while the Header sends them to /pricing. Two different upgrade journeys is confusing and splits our conversion data.

Steps.

  1. Create a small reusable guard, for example Frontend/src/components/PremiumGate.tsx. Give it the exam's segment and the children to render. If the segment is PREMIUM and isPremiumPlan(user?.subscriptionPlan) is false, render the upgrade prompt instead of the children.
  2. Wrap the exam pages with it. Frontend/src/LMS/ExamPage.tsx is the shared component behind most exam routes, so changing it covers many pages at once.
  3. If nobody is logged in at all, send them to /signin with a returnTo value so they come back to the exam afterwards.
  4. Make UpgradeModal link to /pricing, not /payment. Check Frontend/src/utils/topicAccess.ts and everywhere UpgradeModal is used, and change them all. /pricing becomes the single upgrade destination.
  5. The upgrade prompt should be helpful rather than a wall: name the exam, say plainly that it needs a premium plan, and offer both "See plans" and "Browse free exams".
  6. Do not remove the existing topic-level locking in topicAccess.ts where the first topic is free. Both rules apply — the exam-level check runs first.

Done when. As a Freemium user, opening a PREMIUM exam shows the upgrade prompt and never the questions; a FREE exam opens normally. An ANNUAL user opens both. Every upgrade button anywhere in the app lands on /pricing.

N8 — Wednesday 30 September: Filters on the Practice Hub (M9 extra, #100)

Goal. Students can narrow the exam list by stream and by free or premium.

Why. Frontend/src/LMS/PracticeHome.tsx currently dumps every exam into one flat list. We already have the data to organise it — examCategories.ts defines six streams and every exam has a segment — we simply never built the controls. A student preparing for NEET should not have to scroll past CAT and CUET.

Steps.

  1. Add a row of stream filter buttons at the top of PracticeHome.tsx, built from EXAM_CATEGORIES in Frontend/src/course/examCategories.ts: All, Engineering, Medical, MBA, CUET, Boards, UPSC.
  2. Add a second row of chips: All, Free, Premium, driven by the segment field on STREAM_EXAMS.
  3. The two filters combine. Picking Medical and Free shows only free medical exams.
  4. Show the count next to each filter, for example "Engineering (14)", so students know what to expect before clicking.
  5. Write a proper empty state. If a combination has no exams, say "No premium exams in Boards yet" with a button to clear the filters. Do not show a blank area.
  6. Keep the chosen filters in the URL as query parameters like ?stream=medical&segment=free, so the view can be bookmarked or shared and the back button behaves sensibly.
  7. boards and upsc have category cards but no exams at all. Make sure they show the empty state and do not crash.
  8. On a phone, let the filter rows scroll sideways rather than wrapping into a tall block.

Done when. Selecting Medical plus Free shows only free medical exams, the counts are right, the URL updates, reloading keeps the filters, and Boards shows a clean empty message.

N9 — Thursday 1 October: Answer screens for question types we cannot currently answer (M2 and M4, #93 and #95)

Goal. Students can answer multiple-correct, numerical and true-or-false questions, not just single-choice.

Why. Our database has supported MULTI_MCQ, NUMERICAL and TRUE_FALSE for a long time, and gradeObjective scores all three correctly including partial marks. But every student screen renders radio buttons only. We are scoring question types that a student has no way to answer.

Steps.

  1. Create one component Frontend/src/components/QuestionRenderer.tsx that takes a question and its current answer, looks at questionType, and renders the right input. Use it everywhere instead of copying this logic into each screen.
  2. SINGLE_MCQ keeps today's radio buttons, moved into this component unchanged.
  3. MULTI_MCQ uses checkboxes with helper text "Select all correct options". Send the selected options back as a comma-separated string in the format the backend expects — check how gradeObjective splits the value before you decide the format.
  4. NUMERICAL uses a text input accepting digits, one decimal point and a leading minus. Do not use type="number" — its spinner arrows cause accidental changes during exams. Show a numeric keypad on mobile.
  5. TRUE_FALSE shows two large buttons rather than a dropdown, because it is faster to tap.
  6. DESCRIPTIVE shows a plain multi-line text box for now. You extend it tomorrow in N10.
  7. Update the question palette colours so a partly-answered multiple-correct question looks different from an untouched one.
  8. If a question arrives with an unknown type, fall back to single-choice and log a console warning. Never show a student a broken question.

Done when. Build a test containing one question of each type, answer them all, submit, and confirm the marks match what the backend calculated — including partial marks on the multiple-correct question.

N10 — Friday 2 October: Descriptive answer editor with file upload (M6, #97) — Phase 1 frontend sign-off

Goal. Students can write a long answer with basic formatting and attach a file, using the upload endpoint Harshit builds on Monday.

Why. This finishes the descriptive-answer story for Phase 1. Harshit's upload endpoint has no user interface, so right now only an API tool can send a file.

Steps.

  1. Extend the DESCRIPTIVE branch of QuestionRenderer from N9 into a proper editor. Use contentEditable with a small toolbar for bold, italic and bullets. Do not install a rich-text library for this.
  2. Show a live word count and character count. If the question sets a word limit, warn as they approach it and colour the count red past the limit, but never block typing.
  3. Add a file upload area below the editor accepting PDF, DOCX, PNG and JPEG, matching exactly what Harshit's endpoint allows. Support drag and drop as well as clicking.
  4. Show upload progress, then the file name with a remove button. For images show a small thumbnail, so the student can check they photographed the right page.
  5. Validate the file size in the browser before uploading — rejecting a 20 MB file after a two-minute upload is a bad experience. Remember the browser check is only a convenience; the server check is the real one.
  6. Autosave descriptive text on the same schedule as everything else from N2. Losing a 500-word essay is far worse than losing a tick box.
  7. Sanitise the HTML before sending it. Strip <script> tags and event attributes like onclick. An examiner views this content in the admin panel, so an unsanitised answer is a cross-site scripting risk aimed at our own staff.
  8. On the result page from N4, show the submitted answer, the attached file, and the examiner's rubric marks and comments once they exist.

Done when. Write a formatted 300-word answer, attach a photo, submit, and see both in the admin examiner panel. Refresh mid-answer and your text is still there.

Week 13 — Mon 5 to Fri 9 October

Phase 2 opens. M11 Language Assessment starts on the Gantt, and this week is mostly about building the four language test formats.

N11 — Monday 5 October: Render maths formulas and question images (M2, #93)

Goal. A formula shows as a properly typeset equation rather than raw backslash-filled text, and question diagrams appear.

Why. There is no maths rendering anywhere in Frontend/src — zero matches for KaTeX or MathJax. For a platform whose biggest exams are JEE, NEET and CAT, this is a serious gap. Harshit adds the backend fields on Tuesday, but build the rendering first so it is ready.

Steps.

  1. Install KaTeX, not MathJax. KaTeX is much faster, which matters when a page has fifty formulas, and it is smaller.
  2. Create Frontend/src/components/MathText.tsx. Give it a string and a format flag. LATEX renders with KaTeX, PLAIN renders the text as-is.
  3. Support formulas mixed into a sentence. "Find the value of $x^2 + 3$ when x equals 4" must render the prose normally and only the part between the dollar signs as maths. Split on the delimiters and render each piece.
  4. If KaTeX throws on a malformed formula, catch it and show the raw text with a subtle warning style. A broken formula must never blank out the whole question.
  5. Use MathText inside the QuestionRenderer from N9 for the question content, every option, and the solution.
  6. Add image support: if imageUrl is present, show the diagram under the question text, sized to fit but tappable to enlarge on mobile. Always set an alt attribute.
  7. Load KaTeX's CSS once in the app entry point, not per component.

Done when. A question containing \frac{1}{2} and an integral renders as real mathematical notation, a circuit diagram appears below it, and a deliberately broken formula degrades to plain text without breaking the page.

N12 — Tuesday 6 October: The listening test player (M11, #102)

Goal. A student hears an audio clip and answers questions about it, with the play limit clearly shown and respected.

Why. This is the student side of Harshit's audio work. Language assessment is a new Phase 2 module and this is its first visible feature.

Steps.

  1. Create Frontend/src/LMS/ListeningQuestion.tsx with its own CSS.
  2. Build a deliberately simple player: one large play button, a progress bar, elapsed and total time. Nothing else.
  3. Show the play allowance prominently, for example "You can play this 2 more times". At zero, disable the button and say "No plays remaining".
  4. Before each play call POST /attempts/{id}/questions/{qid}/play and only start the audio if the server allows it. Do not count plays in the browser alone — a page refresh would reset it.
  5. Disable seeking. Remove the scrub handle so a student cannot replay one sentence repeatedly to get around the play limit.
  6. Show a clear loading state while the audio buffers, and an error with a Retry button if it fails. Audio failing silently during an exam is very stressful.
  7. Show the questions below the player. The student can answer while listening, which is how real listening tests work.
  8. Warn on entry if no audio output is detected, and let the student test their sound before the timer starts.

Done when. A listening question plays, the counter drops correctly, the third play is refused, refreshing does not restore plays, and the audio cannot be scrubbed.

N13 — Wednesday 7 October: Recording a spoken answer (M11, #102)

Goal. A student records their spoken answer in the browser and uploads it.

Why. This pairs with Harshit's recording endpoint on Friday. Browser microphone work has many edge cases — permissions, missing hardware, browser differences — so it gets a full day. Agree the audio format with Harshit on Monday morning so what you send is what he accepts.

Steps.

  1. Create Frontend/src/LMS/SpeakingQuestion.tsx. Use the built-in MediaRecorder API. Do not add a recording library.
  2. Ask for microphone permission with navigator.mediaDevices.getUserMedia({ audio: true }) only when the student reaches the question, not on page load. An unexplained permission prompt makes people click Block.
  3. Handle refusal properly. If permission is denied, explain in plain words how to re-enable the microphone in their browser, and offer a Try again button.
  4. Follow the real exam structure: show the question, then a preparation countdown during which recording is disabled, then recording starts with its own countdown.
  5. Show a live level meter while recording so the student can see their voice is being picked up. A silent recording discovered after the exam is a disaster.
  6. Stop automatically at the time limit. Let the student stop early.
  7. Afterwards let them play it back once and either keep it or re-record, if the question allows re-recording. Then upload with a progress bar.
  8. Never lose a recording. Keep the audio blob in state until the upload is confirmed, and retry automatically on failure.
  9. Test in both Chrome and Firefox — they produce different audio formats.

Done when. Record a 30-second answer, play it back, upload it, and hear it in the admin examiner panel. Denying microphone permission shows helpful instructions rather than a broken screen.

N14 — Thursday 8 October: A reading comprehension layout that works for any exam (M11, #102)

Goal. A passage on one side, its questions on the other, usable for CAT, CUET and the new language tests.

Why. We already have reading comprehension, but it is locked inside Frontend/src/cat-practice/ and hardcoded to CAT. M11 needs the same layout for language assessment. Rather than copying it, lift it out into a shared component.

Steps.

  1. Read Frontend/src/cat-practice/components/ReadingComprehensionQuiz.jsx first and understand what it already does well. Reuse, do not rewrite from scratch.
  2. Create Frontend/src/components/PassageLayout.tsx: passage on the left, questions on the right, each panel scrolling independently. This matters — a student reading the passage must not lose their place when scrolling the questions.
  3. Add a draggable divider so the student can widen whichever side they prefer. Remember their choice in localStorage.
  4. On a phone, switch to a tab layout with Passage and Questions tabs, since side-by-side is unusable on a narrow screen.
  5. Add a text highlighter. Selecting text in the passage highlights it in yellow, and highlights persist while the student moves between questions in that passage. Students expect this.
  6. Add font size controls for the passage. Long passages on a small screen are genuinely hard to read.
  7. Point the existing CAT reading comprehension at the new shared component, so there is only one implementation to maintain.
  8. Use the MathText component from N11 for the questions, because CUET passages can include figures.

Done when. A CAT passage renders exactly as before through the new shared component, the divider drags, highlights survive question changes, and the mobile tab view works.

N15 — Friday 9 October: Essay and writing test screen (M11, #102)

Goal. A screen for timed written answers with a word count and a strong guarantee against losing work.

Why. Writing tests are on the Gantt for M11. This builds on the descriptive editor from N10, but a timed essay has stricter needs — the biggest risk is a student writing for forty minutes and losing everything.

Steps.

  1. Create Frontend/src/LMS/WritingQuestion.tsx, reusing the editor from N10 rather than writing a second one.
  2. Show the prompt in a panel that stays visible while they write. They will re-read it constantly.
  3. Show the word count against the required range, for example "245 / 250-300 words". Colour it amber when close and red when outside the range, but never stop them typing.
  4. Autosave to the server every 20 seconds and write to localStorage every 5 seconds. If the browser crashes, offer to restore the local copy when they return.
  5. Show the exact save state — "Saved 14:32:05", never a vague spinner.
  6. Show remaining time, and warn at 5 minutes and at 1 minute.
  7. Turn off spellcheck and autocorrect on the editor. In a language test, the browser correcting their spelling defeats the point of the assessment.
  8. Block paste into the editor and show "Pasting is not allowed in this test" when they try.
  9. Submit automatically when time runs out, with whatever has been written.

Done when. Write 300 words, kill the browser process, reopen, and be offered your text back. The word count is accurate, paste is blocked, and auto-submit at zero works.

Week 14 — Mon 12 to Fri 16 October

Theme: M12 Adaptive Testing starts on the Gantt, M11 finishes being made findable, and the first JEE Main practice papers get a listing screen. Friday is a quality day, because the frontend currently has no test framework at all.

N16 — Monday 12 October: Grammar, vocabulary and rearrangement question screens (M11, #102)

Goal. Question formats used in language tests that we cannot currently display.

Why. M11 lists grammar, vocabulary, sentence rearrangement and cloze tests. We have para-jumbles in the CAT section but they are CAT-specific. Generalise them so they work for any language exam.

Steps.

  1. Look at Frontend/src/cat-practice/ first. Para jumbles and odd-one-out already exist there and are the closest thing we have. Extract rather than duplicate.
  2. Build a cloze test view: a passage with gaps, each gap a dropdown or text input. Keep the surrounding sentence visible for context and never break a sentence across the fold.
  3. Build a sentence rearrangement view: draggable sentence cards with numbered positions. Provide keyboard controls as well as dragging — drag and drop is unusable for some students and on some devices.
  4. Build a matching view: terms on the left, definitions on the right, connected by clicking one then the other. Show the pairs made so far as a clear list, and let the student undo a pair.
  5. Add all three to the QuestionRenderer from N9 so they work anywhere a question appears.
  6. Make sure every one of these is answerable with a keyboard alone. Some students take exams on a device without a mouse.
  7. Send answers back in the format the backend scoring expects. Confirm the exact format with Harshit before you build, not after.

Done when. All three types can be answered, the answer survives leaving and returning to the question, and each works with the keyboard alone.

N17 — Tuesday 13 October: The adaptive test screen (M12, #103)

Goal. A test screen for adaptive exams, which behave very differently from normal ones.

Why. An adaptive test has no fixed question list, so our normal exam layout is wrong for it. There is no palette, because future questions do not exist yet. There is no going back, because the next question was chosen based on the previous answer. Students need this explained, or they will think the app is broken.

Steps.

  1. Create Frontend/src/LMS/AdaptiveTest.tsx.
  2. Fetch one question at a time from GET /attempts/{id}/next-question. Submit the answer, then request the next.
  3. Remove the question palette entirely and replace it with simple progress: "Question 7 of 20".
  4. Remove Previous, Mark for Review and Clear Response. None of them make sense here.
  5. Show an instruction screen before the test starts explaining, in plain words, that questions adapt to their answers, that they cannot go back, and that a paper feeling hard is normal and not a bad sign.
  6. Add a clear confirmation before each answer is locked in, because it cannot be changed. Something like "Submit and continue" with a confirm step.
  7. Show a smooth loading state between questions. The server is doing real work choosing the next one, and a blank screen will make students think it has frozen.
  8. Do not display the difficulty of the current question or the running ability estimate. Showing a student they are on the easy path is demoralising and changes how they perform.
  9. At the end show the scaled score with its confidence range, and explain what it means.

Done when. A full adaptive attempt runs start to finish, no back navigation is possible, the progress count is right, and transitions never leave a blank screen.

N18 — Wednesday 14 October: Language assessment section in the Practice Hub (M11, #102)

Goal. Everything built for M11 becomes findable by a student.

Why. Across W13 you built listening, speaking, reading and writing screens, and none of them are reachable from any menu. A feature nobody can find does not exist.

Steps.

  1. Add a language category to Frontend/src/course/examCategories.ts alongside the existing six, with a clear description.
  2. Add language assessment entries to Frontend/src/course/streamExams.ts, each with the right segment of FREE or PREMIUM so the badge and the gate from N7 both work.
  3. Create a landing page for the category with four clear cards: Listening, Speaking, Reading, Writing. Each explains what the test involves and roughly how long it takes.
  4. Add a device readiness check before any listening or speaking test: confirm audio output works and the microphone is permitted and picking up sound. Do this before the test starts, not during it.
  5. Make sure the stream filter from N8 includes the new category and its count is right.
  6. Add a short "How this test works" panel on each card. These formats are new to our students and unexplained formats cause panic.
  7. Check the whole thing on a phone. Language practice happens on mobile more than anything else.

Done when. A student can reach the Practice Hub, filter to Language, pick Listening, pass the device check, and complete a test end to end without help.

N19 — Thursday 15 October: Browsing and starting JEE Main practice tests (QB-1, #112)

Goal. The practice papers Harshit generates are visible and launchable by students.

Why. Harshit creates 10 real papers on the same day. If nobody can see them, the whole question-bank pipeline stops short of the student. This screen will be reused for every exam bank afterwards, so build it properly.

Steps.

  1. Create a practice test listing page fetching GET /api/tests/available filtered to the exam.
  2. Show each paper as a card with name, question count, duration, total marks, and — importantly — whether the student has already attempted it and what they scored.
  3. Sort so unattempted papers come first. A student wants the next paper they have not done, not the first one alphabetically.
  4. Add filters for attempted, not attempted, and in progress. With 100 papers eventually, an unfiltered list is useless.
  5. An in-progress paper shows Resume and goes back into the live attempt. A completed one shows the score and links to its result page from N4.
  6. Show an instructions screen before starting: duration, marking scheme including negative marks, and the navigation rules. Require an "I have read the instructions" tick before Start, exactly as the real exam does.
  7. Use pagination or lazy loading. Do not render a hundred cards at once.
  8. Apply the premium gate from N7 here too, since most practice papers will be premium content.

Done when. The 10 papers Harshit generated appear, you can start one, leave it, come back and resume it, finish it, and see the score on the card afterwards.

N20 — Friday 16 October: Frontend tests and a responsive pass (M4, #95)

Goal. Automated tests for the exam flow, and exam screens that work properly on a phone.

Why. Frontend/package.json has no test framework at all — the scripts are only dev, build, lint and preview. The attempt flow you built in W11 is now the most important code in the frontend and nothing protects it. Separately, most of our students are on phones, and the exam screens have not been checked at small sizes.

Steps.

  1. Install Vitest and React Testing Library. Vitest, not Jest, because we use Vite and the configuration is far simpler. Add a test script to package.json.
  2. Write tests for the pieces most likely to break silently:
    • QuestionRenderer renders the right input for each question type.
    • The autosave function bundles answers into the right request shape.
    • isPremiumPlan returns true for the four paid plans and false for FREEMIUM.
    • The result page displays the right numbers from a fixed sample of report JSON.
  3. Mock the network calls. These tests must run without a backend, or nobody will run them.
  4. Then do the responsive pass. Open every exam screen at 360 pixels wide, a common Android phone, and fix what breaks. Expect problems with the question palette, the timer and the passage layout.
  5. Check that tap targets are at least 44 pixels. Options that are hard to tap accurately cause wrong answers, which is much worse than an ugly layout.
  6. Check the exam screens in landscape too. Students rotate their phones for passages and diagrams.
  7. Confirm npm run test and npm run build both pass, and add the test command to the README.

Done when. npm run test runs green, deliberately breaking the premium check makes a test fail, and every exam screen is fully usable at 360 pixels wide in both orientations.

Do not do these

Say no if they come up. They are either scheduled for a later Gantt phase or explicitly deferred in our project rules.

  • Google and Microsoft sign-in buttons.
  • Live PayU, Stripe and PayPal checkout. The payment page stays a practice page.
  • SMS, WhatsApp and push notifications.
  • Installable app / offline support (PWA).
  • Camera-based proctoring.
  • PDF or Excel export of reports, and any charting library.
  • White-label branding and custom domains (M15, W17 to W20).
  • Adding Tailwind, Redux, axios, or a new UI kit.
  • Writing the bulk question content itself. Per the Gantt the question bank tracks belong to Ayush Sharma — you build the screens.
Actions

Also available in: Atom PDF