Task #194
openNikita's Task List
0%
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 inFrontend/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
.envfile, 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.
- Open
Frontend/src/LMS/Practice.tsx. ReadTestAuthoringPanel.tsxfirst and copy the way it calls the API. - When the student starts, call
POST http://localhost:8081/api/attempts/startusingapiFetchandauthHeaders. - 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.createPracticeTestalready does exactly that. - Store the returned attempt in React state:
attemptId, thequestionsarray,durationMinutesandnavMode. - Drive the countdown clock from the server's
durationMinutesandstartedAt, not from a number hardcoded in the component. - 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.
- Save the attempt id in
sessionStoragetoo, 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.
- Build one function
saveAnswers()that callsPUT http://localhost:8081/api/attempts/{attemptId}/answers. - Send the full current answer list each time: for every question the
testQuestionId,responseText,markedForReviewandtimeSpentSeconds. Also send the runningtabSwitchCount. - 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.
- Rename the button. It currently says "Next" — it must say Save & Next, and it must wait for the save to return before moving on.
- 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.
- 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.
- 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.
- Call
POST http://localhost:8081/api/attempts/{attemptId}/submit. Before you do, run one finalsaveAnswers()so nothing in flight is lost. - Add a confirmation dialog before submitting. Show how many questions are answered, unanswered, and marked for review. Let the student cancel.
- Delete the browser-side scoring code. Read
score,maxScore,accuracy,attemptRankandpercentilefrom the submit response instead. - Never let a student submit twice. Disable the button as soon as it is pressed and keep it disabled.
- 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.
- 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.
- Create
Frontend/src/pages/Results.tsxwithResults.cssbeside it, and register/results/:attemptIdinApp.tsx. - Read the id with
useParamsand fetchGET http://localhost:8081/api/reports/attempts/{id}. - 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.
- Show a loading state while fetching, and a friendly message if the attempt does not exist or belongs to someone else.
- Do not add a charting library. Simple coloured bars made with
divelements and a percentage width are enough. - 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.
- Add a large View detailed result button on the finish screen in
Practice.tsx, linking to/results/{attemptId}. - Create
Frontend/src/pages/MyAttempts.tsxwith its own CSS, and add the route/my-attemptsinApp.tsx. - Fetch
GET http://localhost:8081/api/attempts/me. This endpoint already exists. - 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.
- Add a status chip. IN_PROGRESS shows Resume and goes back into the test. SUBMITTED shows View result.
- Add a "My Attempts" link in
Header.tsx, shown only whenisLoggedIn()is true. - 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.
- 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. - Each card needs a plan name, price, billing period, a short list of what is included, and a button.
- Show the saving on the longer plans, for example "Save 20 percent compared to Monthly". That is why customers pick them.
- Mark one plan as most popular with a visible highlight. Quarterly is the usual choice.
- The Enterprise card says "Contact us" and links to
/contactrather than starting a checkout, because enterprise deals are negotiated. - Read the logged-in user's plan with
getLoggedInUser(). On their current plan, replace the button with a "Your current plan" label. - Pass the chosen plan name through to
Frontend/src/pages/Checkout.tsx, so Harshit's payment fix can record which plan was actually purchased. - 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.
- Create a small reusable guard, for example
Frontend/src/components/PremiumGate.tsx. Give it the exam'ssegmentand the children to render. If the segment is PREMIUM andisPremiumPlan(user?.subscriptionPlan)is false, render the upgrade prompt instead of the children. - Wrap the exam pages with it.
Frontend/src/LMS/ExamPage.tsxis the shared component behind most exam routes, so changing it covers many pages at once. - If nobody is logged in at all, send them to
/signinwith areturnTovalue so they come back to the exam afterwards. - Make
UpgradeModallink to/pricing, not/payment. CheckFrontend/src/utils/topicAccess.tsand everywhereUpgradeModalis used, and change them all./pricingbecomes the single upgrade destination. - 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".
- Do not remove the existing topic-level locking in
topicAccess.tswhere 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.
- Add a row of stream filter buttons at the top of
PracticeHome.tsx, built fromEXAM_CATEGORIESinFrontend/src/course/examCategories.ts: All, Engineering, Medical, MBA, CUET, Boards, UPSC. - Add a second row of chips: All, Free, Premium, driven by the
segmentfield onSTREAM_EXAMS. - The two filters combine. Picking Medical and Free shows only free medical exams.
- Show the count next to each filter, for example "Engineering (14)", so students know what to expect before clicking.
- 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.
- 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. boardsandupschave category cards but no exams at all. Make sure they show the empty state and do not crash.- 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.
- Create one component
Frontend/src/components/QuestionRenderer.tsxthat takes a question and its current answer, looks atquestionType, and renders the right input. Use it everywhere instead of copying this logic into each screen. - SINGLE_MCQ keeps today's radio buttons, moved into this component unchanged.
- 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
gradeObjectivesplits the value before you decide the format. - 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. - TRUE_FALSE shows two large buttons rather than a dropdown, because it is faster to tap.
- DESCRIPTIVE shows a plain multi-line text box for now. You extend it tomorrow in N10.
- Update the question palette colours so a partly-answered multiple-correct question looks different from an untouched one.
- 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.
- Extend the DESCRIPTIVE branch of
QuestionRendererfrom N9 into a proper editor. UsecontentEditablewith a small toolbar for bold, italic and bullets. Do not install a rich-text library for this. - 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.
- 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.
- 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.
- 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.
- 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.
- Sanitise the HTML before sending it. Strip
<script>tags and event attributes likeonclick. An examiner views this content in the admin panel, so an unsanitised answer is a cross-site scripting risk aimed at our own staff. - 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.
- Install KaTeX, not MathJax. KaTeX is much faster, which matters when a page has fifty formulas, and it is smaller.
- Create
Frontend/src/components/MathText.tsx. Give it a string and a format flag. LATEX renders with KaTeX, PLAIN renders the text as-is. - 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.
- 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.
- Use
MathTextinside theQuestionRendererfrom N9 for the question content, every option, and the solution. - Add image support: if
imageUrlis present, show the diagram under the question text, sized to fit but tappable to enlarge on mobile. Always set analtattribute. - 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.
- Create
Frontend/src/LMS/ListeningQuestion.tsxwith its own CSS. - Build a deliberately simple player: one large play button, a progress bar, elapsed and total time. Nothing else.
- Show the play allowance prominently, for example "You can play this 2 more times". At zero, disable the button and say "No plays remaining".
- Before each play call
POST /attempts/{id}/questions/{qid}/playand only start the audio if the server allows it. Do not count plays in the browser alone — a page refresh would reset it. - Disable seeking. Remove the scrub handle so a student cannot replay one sentence repeatedly to get around the play limit.
- 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.
- Show the questions below the player. The student can answer while listening, which is how real listening tests work.
- 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.
- Create
Frontend/src/LMS/SpeakingQuestion.tsx. Use the built-inMediaRecorderAPI. Do not add a recording library. - 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. - 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.
- Follow the real exam structure: show the question, then a preparation countdown during which recording is disabled, then recording starts with its own countdown.
- 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.
- Stop automatically at the time limit. Let the student stop early.
- 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.
- Never lose a recording. Keep the audio blob in state until the upload is confirmed, and retry automatically on failure.
- 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.
- Read
Frontend/src/cat-practice/components/ReadingComprehensionQuiz.jsxfirst and understand what it already does well. Reuse, do not rewrite from scratch. - 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. - Add a draggable divider so the student can widen whichever side they prefer. Remember their choice in
localStorage. - On a phone, switch to a tab layout with Passage and Questions tabs, since side-by-side is unusable on a narrow screen.
- 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.
- Add font size controls for the passage. Long passages on a small screen are genuinely hard to read.
- Point the existing CAT reading comprehension at the new shared component, so there is only one implementation to maintain.
- Use the
MathTextcomponent 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.
- Create
Frontend/src/LMS/WritingQuestion.tsx, reusing the editor from N10 rather than writing a second one. - Show the prompt in a panel that stays visible while they write. They will re-read it constantly.
- 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.
- Autosave to the server every 20 seconds and write to
localStorageevery 5 seconds. If the browser crashes, offer to restore the local copy when they return. - Show the exact save state — "Saved 14:32:05", never a vague spinner.
- Show remaining time, and warn at 5 minutes and at 1 minute.
- Turn off spellcheck and autocorrect on the editor. In a language test, the browser correcting their spelling defeats the point of the assessment.
- Block paste into the editor and show "Pasting is not allowed in this test" when they try.
- 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.
- 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. - 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.
- 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.
- 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.
- Add all three to the
QuestionRendererfrom N9 so they work anywhere a question appears. - Make sure every one of these is answerable with a keyboard alone. Some students take exams on a device without a mouse.
- 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.
- Create
Frontend/src/LMS/AdaptiveTest.tsx. - Fetch one question at a time from
GET /attempts/{id}/next-question. Submit the answer, then request the next. - Remove the question palette entirely and replace it with simple progress: "Question 7 of 20".
- Remove Previous, Mark for Review and Clear Response. None of them make sense here.
- 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.
- Add a clear confirmation before each answer is locked in, because it cannot be changed. Something like "Submit and continue" with a confirm step.
- 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.
- 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.
- 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.
- Add a
languagecategory toFrontend/src/course/examCategories.tsalongside the existing six, with a clear description. - Add language assessment entries to
Frontend/src/course/streamExams.ts, each with the rightsegmentof FREE or PREMIUM so the badge and the gate from N7 both work. - 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.
- 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.
- Make sure the stream filter from N8 includes the new category and its count is right.
- Add a short "How this test works" panel on each card. These formats are new to our students and unexplained formats cause panic.
- 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.
- Create a practice test listing page fetching
GET /api/tests/availablefiltered to the exam. - 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.
- Sort so unattempted papers come first. A student wants the next paper they have not done, not the first one alphabetically.
- Add filters for attempted, not attempted, and in progress. With 100 papers eventually, an unfiltered list is useless.
- 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.
- 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.
- Use pagination or lazy loading. Do not render a hundred cards at once.
- 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.
- Install Vitest and React Testing Library. Vitest, not Jest, because we use Vite and the configuration is far simpler. Add a
testscript topackage.json. - Write tests for the pieces most likely to break silently:
QuestionRendererrenders the right input for each question type.- The autosave function bundles answers into the right request shape.
isPremiumPlanreturns true for the four paid plans and false for FREEMIUM.- The result page displays the right numbers from a fixed sample of report JSON.
- Mock the network calls. These tests must run without a backend, or nobody will run them.
- 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.
- 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.
- Check the exam screens in landscape too. Students rotate their phones for passages and diagrams.
- Confirm
npm run testandnpm run buildboth 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.