Project

General

Profile

Task #194

Updated by Ayush Yadav on 09/21/2026 12:55 PM

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

 h2. Read this first: your lane has changed 

 Until now you worked on Nikita owns the Spring Boot backend rules 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: tools. Redmine tickets she touches: #100 (M9), #96 (M5), #95 (M4), #99 (M8), #100 (M9), #102 (M11), #103 (M12), #93 (M2), #97 (M6), #112 (QB-1). *Do not create new tickets.* #98 (M7), #101 (M10). 

 h2. How to read every task below 

 Each task always has the same four parts: 

 * *Goal* Week 1 (W9) — one sentence saying what who is Premium, scoring, start 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. log 

 h2. 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 Task N1 — 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 One backend rule: who is 20 working days and 20 tasks. Premium? (M9, #100) 

 h2. Ground rules for all your work 

 * The API From scratch: 





 Open [Users.java](backend/src/main/java/com/teamtalks/backend/entity/Users.java). Find subscriptionPlan. Default 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 FREEMIUM. 



 Add a file next to the component that uses it. *Do not add Tailwind, Redux, axios, small helper class or any new UI kit.* 
 * method, for example PlanRules.isPremium(String plan). Return false for null, blank, or FREEMIUM. Return true for MONTHLY / QUARTERLY / ANNUAL / ENTERPRISE (ignore case). 



 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. 

 h2. 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 this helper 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 AssessmentService.assertPaid() instead of a real student attempt is saved in one-off string check. 



 Tell Harshit the database. *Read @TestAuthoringPanel.tsx@ before you start — it already calls these endpoints correctly and is your best example.* exact function name so his frontend helper matches. 

 h1. Week 11 Task N2 — Mon 21 to Fri 25 September 

 h3. N1 — Monday 21 September: Start Admin can change 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 user’s plan (M9, #100) 





 Open [AdminController.java](backend/src/main/java/com/teamtalks/backend/controller/AdminController.java). There are already exists. Until an attempt id exists, nothing else can be saved. 

 *Steps.* 

 # Open @Frontend/src/LMS/Practice.tsx@. Read @TestAuthoringPanel.tsx@ first and copy the way PUTs for role, profile, status, MFA. 



 Add something like PUT /admin/users/{userId}/plan with body { "subscriptionPlan": "MONTHLY" }. Validate it calls the API. 
 # When the student starts, call @POST http://localhost:8081/api/attempts/start@ using @apiFetch@ and @authHeaders@. 
 # If you have a real test id, send it. If this is free practice, send one of the exam code instead and let five names. Use @Valid DTO. Constructor injection. 



 Return the backend build a practice test for you — @AssessmentService.createPracticeTest@ updated UserResponse (it already does exactly that. 
 # Store the returned attempt in React state: @attemptId@, the @questions@ array, @durationMinutes@ and @navMode@. 
 # Drive the countdown clock from the server's @durationMinutes@ and @startedAt@, *not* from a number hardcoded in the component. 
 # Handle failures properly. If the API is down, includes subscriptionPlan). 



 In [UserManagementPanel.tsx](Frontend/src/pages/admin/UserManagementPanel.tsx), show a clear message like "Could not start the test, please try again" and keep plan on the Start button usable. Do not show user detail. Add a blank screen and do not silently fall back to offline mode. 
 # 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 dropdown with status IN_PROGRESS. The questions on screen are the ones the API returned. 

 h3. N2 — Tuesday 22 September: Autosave every answer, Freemium / Monthly / Quarterly / Annual / Enterprise 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 calls @PUT http://localhost:8081/api/attempts/{attemptId}/answers@. 
 # Send the full current answer list each time: for every question the @testQuestionId@, @responseText@, @markedForReview@ and @timeSpentSeconds@. Also send the running @tabSwitchCount@. 
 # 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 new PUT. 



 Test: pick a timer every 15 seconds, and when they press Save and Next. 
 # *Rename the button.* It currently says "Next" — Candidate, set MONTHLY, refresh, confirm it must say *Save & Next*, and it must wait for the save stayed MONTHLY. Set back 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. FREEMIUM. 

 *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. 

 h3. Task N3 — Wednesday 23 September: Submit through the server and show the real score (M4 and M5, #95 and Bonus marks when scoring (M5, #96) 

 *Goal.* Finishing a test sends it to the server, 





 Open AssessmentService.gradeObjective(). Negative marks 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 final @saveAnswers()@ 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@, @attemptRank@ and @percentile@ from 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@. 

 h3. 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}@ MULTI_MCQ 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.tsx@ with @Results.css@ beside it, and register @/results/:attemptId@ in @App.tsx@. 
 # Read the id with @useParams@ and fetch @GET 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 work. Bonus does not exist or belongs to someone else. 
 # *Do not add exist. 



 Add a charting library.* Simple coloured bars made with @div@ elements and a percentage width are enough. 
 # Make it work bonusMarks (or similar) field 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. 

 h3. 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 question if missing. Default 0. 



 After a large *View detailed result* button on the finish screen in @Practice.tsx@, linking to @/results/{attemptId}@. 
 # Create @Frontend/src/pages/MyAttempts.tsx@ with its own CSS, and correct answer, add the route @/my-attempts@ in @App.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 when @isLoggedIn()@ is true. 
 # If the student has never taken a test, show a friendly empty state with a button bonus to the Practice Hub, score. Wrong / skipped answers do not get bonus. 



 Submit a blank page. 

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

 h1. Week 12 — Mon 28 September to Fri 2 October 

 Theme: finish Phase bonus 1. Everything on Confirm the Gantt for M6, M8, M9 and M10 ends this Friday. total went up by marks + bonus. 

 h3. N6 Task N4 — Monday 28 September: Show all five subscription plans (M9, #100) Start the attempt audit log (M4, #95) 

 *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 Autosave 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 exists (PUT /attempts/{id}/answers). What is included, and missing is a button. 
 # Show the saving on the longer plans, history table. 





 Create an entity, 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 @/contact@ rather 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 AttemptAuditLog, with: attempt id, user sees "Your current plan" on the Freemium card, and selecting Annual carries the id, action (START, SAVE, SUBMIT, TAB_SWITCH), time, optional short note. 



 Table name ANNUAL through to checkout. 

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

 *Goal.* A free user opening in snake_case. Repository + constructor-injected service. 



 Write a premium exam sees row when an upgrade prompt, attempt starts, when answers are saved, 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 test is decoration. Second, @UpgradeModal@ sends users to @/payment@ while the Header sends them to @/pricing@. Two different upgrade journeys submitted. 



 Add GET /attempts/{id}/audit for admins (or reuse /admin if that is confusing and splits our conversion data. 

 *Steps.* 

 # Create cleaner). Return a small reusable guard, for example @Frontend/src/components/PremiumGate.tsx@. Give it the exam's @segment@ and the children to render. list, newest last. 



 If the segment this is PREMIUM and @isPremiumPlan(user?.subscriptionPlan)@ is false, render the upgrade prompt instead of the children. 
 # 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. 
 # If nobody is logged in at all, send them to @/signin@ with a @returnTo@ value so they come back to the exam afterwards. 
 # Make @UpgradeModal@ link to @/pricing@, not @/payment@. Check @Frontend/src/utils/topicAccess.ts@ and everywhere @UpgradeModal@ finished by Saturday, continue Monday (it is used, and change them all. @/pricing@ becomes 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.ts@ where the first topic is free. Both rules apply allowed — the exam-level check M4 runs first. through W10). 

 *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@. 

 h3. N8 Week 2 (W10) — Wednesday 30 September: Filters on the Practice Hub (M9 extra, #100) lists, emails, help Harshit’s report 

 *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 Task N5 — @examCategories.ts@ defines six streams and every exam has Finish audit log + autosave check (M4 close, #95) 





 Start a @segment@ — we simply never built test, refresh the controls. A student preparing for NEET should not have to scroll past CAT and CUET. 

 *Steps.* 

 # Add a row of stream filter buttons page, confirm it resumes (IN_PROGRESS, lastSavedAt updates). 



 Confirm three audit rows exist: start, at the top of @PracticeHome.tsx@, built from @EXAM_CATEGORIES@ in @Frontend/src/course/examCategories.ts@: All, Engineering, Medical, MBA, CUET, Boards, UPSC. 
 # Add a second row of chips: All, Free, Premium, driven by the @segment@ field on @STREAM_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. least one save, submit. 



 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 build proctoring or shared and the back button behaves sensibly. 
 # @boards@ and @upsc@ have 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. PWA. 

 *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. 

 h3. N9 Task N6 — Thursday 1 October: Answer screens for question types we cannot currently answer (M2 and M4, #93 and #95) Faculty vs Candidate lists (M7, #98) 

 *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.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. 
 # 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 @gradeObjective@ splits 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 There 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. 

 h3. 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 @QuestionRenderer@ from N9 into a proper editor. role named Faculty. Use @contentEditable@ with 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 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. 
 # On the result page from N4, show the submitted answer, the attached file, and the examiner's rubric marks and comments once they exist. already exists. 





 Faculty group: Examiner, Author, Reviewer 



 Candidate group: Candidate, USER 

 *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. 

 h1. 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. 

 h3. 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 Steps: 





 In Admin User Management, add two tabs or MathJax. For a platform whose biggest exams are JEE, NEET two buttons: Faculty 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 @MathText@ inside the @QuestionRenderer@ from N9 for the question content, every option, and the solution. 
 # 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. 
 # Load KaTeX's CSS Candidates. 



 Faculty tab calls GET /admin/users?role= 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. 

 h3. 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.tsx@ with its own CSS. 
 # Build a deliberately *simple* player: faculty role (or 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}/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. 
 # *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. 

 h3. 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 backend endpoint on Friday. Browser microphone work has many edge cases — permissions, missing hardware, browser differences — so it gets that accepts a full day. *Agree group). Keep 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-in @MediaRecorder@ API. *Do existing table component. 



 Do not add invent a recording library.* 
 # Ask for microphone permission with @navigator.mediaDevices.getUserMedia({ audio: true })@ new Faculty role unless Ayush asks. 



 Institution Admin should still 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 see 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, institution users 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. 

 h3. 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 filter 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. exists. 

 *Steps.* 

 # Read @Frontend/src/cat-practice/components/ReadingComprehensionQuiz.jsx@ first 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 Task N7 — a student reading the passage must not lose their place Email candidates 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 @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. 

 h3. 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 published (M10 start, #101) 





 Today, publish email goes 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. 
 # 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 author, 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. 

 h1. Week 14 — Mon 12 to Fri 16 October 

 Theme: M12 Adaptive Testing starts students. Result email 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*. 

 h3. 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 submit 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* exists — 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 @QuestionRenderer@ from 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, do 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. 

 h3. 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 rebuild 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 



 When 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 published or the running ability estimate.* Showing a student they are on the easy path schedule 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 saved, email each assigned candidate: test name, start time, link to finish, no back navigation Practice / test. 



 Reuse [AssessmentMailService.java](backend/src/main/java/com/teamtalks/backend/services/AssessmentMailService.java). If SMTP is possible, the progress count not set, console fallback is right, and transitions never leave a blank screen. OK (OTP_CONSOLE_FALLBACK style). 



 No SMS. No WhatsApp. 

 h3. N18 Task N8 — 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 @language@ category to @Frontend/src/course/examCategories.ts@ alongside the existing six, with a clear description. 
 # 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. 
 # Create a landing Help Harshit’s Results 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 (M8, #99) 





 Open [ReportController.java](backend/src/main/java/com/teamtalks/backend/controller/ReportController.java). Confirm GET /reports/attempts/{id} returns score, rank, percentile, time, topics. 



 If a device readiness check before any listening or speaking test: confirm audio output works and the microphone field 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. 

 h3. 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 missing, add it properly. 

 *Steps.* 

 # Create a practice test listing page fetching @GET /api/tests/available@ filtered to the exam. 
 # Show each paper as a card with name, question count, duration, total marks, and AttemptResponse — 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 do not done, not create a second report format. 



 Write the first one alphabetically. 
 # Add filters for attempted, not attempted, and exact JSON example 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 Slack/comment so Harshit generated appear, you can start one, leave it, come back and resume it, finish it, and see bind the score on the card afterwards. 

 h3. 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 @test@ script to @package.json@. 
 # 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. 
 # *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 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. 

 h1. 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 UI. No IRT. No 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. 
 export.

Back