Product Backlog
EPICs, user stories, sprints, and pull requests pulled live from the GitHub Project.
| EPIC | User Story | Acceptance Criteria | Estimate |
|---|---|---|---|
| EPIC 1 | US1-1 As new user I want to register as either a student or a tutor using my email and password So that I can access the platform with an account matching my role | Given a valid email, a password of at least 8 characters, and a role of "student" or "tutor" When the user submits registration Then the account is created and the role is stored correctly | 3 |
Given a password shorter than 8 characters When the user submits registration Then it is rejected with a clear error | |||
Given an email that already exists When the user submits registration Then it is rejected with a clear error | |||
Given a successful registration When the account is created Then the user receives an authentication token | |||
Given a user registers When the password is stored Then it is never stored in plain text | |||
| US1-2 As registered user I want to log in with my email and password So that I can access my account and use the platform | Given a registered user with a correct email and password When they log in Then they receive an authentication token | 3 | |
Given an incorrect password When the user attempts to log in Then it is rejected with a generic invalid-credentials error | |||
Given an email that doesn't exist When the user attempts to log in Then it is rejected with the same generic invalid-credentials error | |||
Given a valid issued token When the user accesses an authenticated endpoint Then access is granted | |||
| US1-3 As newly registered user I want to be required to complete my role-specific profile information right after registering So that the platform has the data it needs before I can use other features | Given a user has just completed registration When registration succeeds Then they are directed to a profile completion page | 5 | |
Given the user's role When the profile completion page loads Then a student sees the student-specific field set and a tutor sees the tutor-specific field set | |||
Given the profile is incomplete When the user attempts to navigate to other pages Then they cannot access them until all required fields are filled in | |||
Given required fields are missing When the user submits the form Then it is rejected with clear validation errors | |||
Given all required fields are filled in When the user submits the form successfully Then the profile data is saved and the user can proceed to the rest of the app | |||
| EPIC 2 | US2-1 As user I want to view my account profile So that I can review my personal information | Given the user is signed in When the user opens My Profile Management Then the system displays the account's common fields and role-specific fields | 3 |
Given private fields such as phone, Line ID, and email When the owner views the profile Then they are visible only to the owner unless explicit visibility settings allow otherwise | |||
| US2-2 As user I want to edit my personal information So that my account details remain up to date | Given a user opens the profile management page When the page loads Then their current personal information is pre-filled | 3 | |
Given the user updates one or more fields When they save the changes Then the updates are accepted | |||
Given changes have been saved When the profile page is loaded again Then the saved changes are reflected | |||
Given a field is edited When it is validated Then the same validation rules as initial profile completion apply (per role) | |||
Given invalid data is submitted When the user saves Then clear validation errors are shown and no changes are saved | |||
| US2-3 As student I want to top up my balance by selecting from preset amount options So that I can access teaching services | Given a student is in their account/dashboard area When they navigate to the top-up page Then the page loads | 3 | |
Given the top-up page is open When it loads Then it displays the student's current balance | |||
Given the top-up page is open When it loads Then it shows a fixed list of preset top-up amounts as selectable choices (no numeric input field) | |||
Given a preset amount is selected When the student confirms Then the top-up is applied | |||
Given a successful top-up confirmation When the balance updates Then the displayed balance reflects the increase without a full manual refresh being required to see updated state on next load | |||
Given a top-up is confirmed When it is processed Then no real payment provider, bank transfer, or card charge is triggered — the top-up is simulated/mocked | |||
Given no preset amount is selected When the student attempts to confirm Then a validation message is shown and the balance does not change | |||
Given a non-student role When they attempt to access the top-up page Then access is denied | |||
| US2-4 As tutor I want to withdraw funds from my balance by selecting from preset amount options So that I can access my earnings outside the platform | Given a tutor is in their account/dashboard area When they navigate to the balance withdraw page Then the page loads | 2 | |
Given the withdraw page is open When it loads Then it displays the tutor's current balance | |||
Given the withdraw page is open When it loads Then it shows a fixed list of preset withdrawal amounts as selectable choices (no numeric input field) | |||
Given a preset amount is selected that is less than or equal to the current balance When the tutor confirms Then the withdrawal is applied | |||
Given a successful withdrawal confirmation When the balance updates Then the displayed balance reflects the decrease without a full manual refresh being required to see updated state on next load | |||
Given a withdrawal is confirmed When it is processed Then no real payout, bank transfer, or card transaction is triggered — the withdrawal is simulated/mocked | |||
Given no preset amount is selected When the tutor attempts to confirm Then a validation message is shown and the balance does not change | |||
Given a selected amount exceeds the current balance When the tutor attempts to confirm Then it is rejected with a clear error and the balance does not change | |||
Given a non-tutor role When they attempt to access the withdraw page Then access is denied | |||
| US2-5 As tutor I want to set my account to active or inactive So that I can stop taking on new work without deleting my account or being blocked by outstanding-obligation checks | Given an authenticated tutor When they set their status to inactive Then the status is saved without any outstanding-class or outstanding-balance check. | 2 | |
Given a tutor is inactive When students search or attempt to book Then that tutor does not appear as available. | |||
Given a tutor is inactive When they set their status back to active Then they become available for new bookings again. | |||
Given a tutor has outstanding classes or an outstanding balance When they toggle to inactive Then the toggle still succeeds (no rejection). | |||
| US2-6 As user I want to log out from the profile management page So that I can securely end my session | Given a signed-in user is on the profile management page When they click the logout button Then their session ends | 2 | |
Given a user has logged out When they attempt to access authenticated pages Then they are redirected away and cannot access them without logging in again | |||
Given no active session When the logout API is called Then it does not crash and returns an appropriate response | |||
| US2-7 As user I want to my profile picture upload on the complete profile and edit profile pages to actually save my image So that my real photo is visible instead of a mocked placeholder | Given a signed-in user is on the complete profile page When they upload a profile image Then the image is stored in GCS and its URL is saved to the database | 5 | |
Given a signed-in user is on the edit profile page When they upload a profile image Then the image is stored in GCS and its URL is saved to the database | |||
Given a profile image has been uploaded successfully When the user views their profile Then the uploaded image is retrievable and displays correctly | |||
Given an upload fails When the user attempts to upload a profile image Then an error is shown to the user and no broken/invalid URL is saved to the database | |||
Given the real upload flow is implemented When either profile page is inspected Then no mock upload logic remains | |||
| EPIC 3 | US3-1 As student I want to browse active tutors with search, filters, and sorting So that I can quickly find a tutor that fits my subject, budget, and quality preferences | Given tutors with mixed active and inactive status When a student views the tutor list Then only active tutors appear | 8 |
Given a search term is entered When a student searches Then only tutors matching the term are returned | |||
Given a subject filter is applied When a student filters by subject Then only tutors teaching that subject are shown | |||
Given a price range is applied When a student filters by price Then only tutors within the given min/max price appear | |||
Given rating sort is selected When a student sorts by rating Then tutors are ordered by average review score, descending by default | |||
Given price sort is selected When a student sorts by price Then tutors are ordered by price, ascending by default | |||
Given popularity sort is selected When a student sorts by popularity Then tutors are ordered by number of past students, descending by default | |||
Given search, filters, and sort are all set When a student applies them together Then the results reflect all criteria combined | |||
Given a tutor has a review score When their rating is displayed Then it shows as a score out of 5 (e.g. "4.8/5") | |||
Given a tutor has taught students When popularity is displayed Then it shows the count of students who have learned with the tutor | |||
| US3-2 As student I want to click a tutor card and see their full details with reviews So that I can evaluate a tutor before deciding to contact them | Given a student is on the tutor list page When they click a tutor card Then they are navigated to that tutor's detail page | 5 | |
Given a student is on a tutor's detail page When the page loads Then the tutor's full information is displayed | |||
Given a tutor has mocked reviews When a student views the detail page Then a list of reviews (reviewer, rating, comment) is displayed | |||
Given a student is on the tutor detail page When the page loads Then a "Message" button is visible | |||
Given a student is on the tutor detail page When they click the "Message" button Then they are navigated to a chat page route | |||
| EPIC 4 | US4-1 As user I want to see a list of my conversations So that I can quickly find and open a chat with a specific person | Given a signed-in user has conversations When they open the chat inbox page Then they see a list of all their conversations | 8 |
Given a conversation is in the list When it is displayed Then it shows the other participant's name and last message preview | |||
Given a conversation has unread messages When it is displayed in the list Then it is visually indicated as unread | |||
Given multiple conversations exist When the list is displayed Then conversations are sorted with the most recently active first | |||
Given a conversation is in the list When the user clicks it Then its chat view opens | |||
| US4-2 As user I want to send and receive messages in real time within a conversation So that I can communicate with a tutor or student instantly, like a normal chat app | Given an existing conversation When a user opens it Then the existing message history loads | 13 | |
Given a user sends a message When it is sent Then it appears in the sender's view immediately | |||
Given a message is sent When the other participant is viewing the conversation Then they receive it in real time without refreshing the page | |||
Given the chat connection When messages are sent or received Then delivery uses WebSocket | |||
Given the signed-in user is a tutor viewing a chat with a student When the chat view loads Then a "Create Classroom" button is visible | |||
Given the "Create Classroom" button is visible When a tutor clicks it Then no action is required to occur yet | |||
| EPIC 5 | US5-1 As tutor I want to create a classroom from a student's message inbox So that I can manage the course for that student | Given I am an active tutor viewing a conversation with a student When the inbox loads Then a Create Classroom action is visible. | — |
Given I am a student or the conversation is not with a student When the inbox loads Then the Create Classroom action is not available. | |||
Given I start classroom creation from a student conversation When the form opens Then that student is selected automatically and cannot be replaced with another user. | |||
Given the form opens When my tutor profile has a default hourly rate Then the price field is prefilled with that value. | |||
Given I keep the default price When I create the classroom Then the tutor's current default hourly rate is stored as the classroom's `pricePerHour`. | |||
Given I enter a different valid price When I create the classroom Then the new value is stored for that classroom without changing my tutor profile. | |||
Given I select a valid subject and provide all required information When I submit the form Then exactly one active classroom is created for me and the student in the conversation. | |||
Given the selected subject does not exist or is not assigned to me When I submit the form Then creation is rejected. | |||
Given the price is zero, negative, or invalid When I submit the form Then a validation error is displayed and no classroom is created. | |||
Given no price override or valid tutor default rate is available When I submit the form Then creation is rejected with a clear error. | |||
Given classroom creation succeeds When the API responds Then I am redirected to `/classroom/[classroomId]`. | |||
Given classroom creation fails When the error is returned Then I remain in the creation flow and my entered values are preserved. | |||
Given a conversation does not include the authenticated tutor When its ID is submitted Then the API rejects the request. | |||
Given I am authenticated as a student When I call the classroom creation endpoint Then access is denied. | |||
| US5-2 As tutor I want to review a classroom's details So that I can confirm its information and current status | Given I am the tutor assigned to a classroom When I select it from My Classroom Then I am navigated to `/classroom/[classroomId]`. | — | |
Given I am the assigned tutor When the detail page loads Then I can see the subject, assigned student summary, price per hour, status, and creation date. | |||
Given I am the student assigned to a classroom When I select it from My Classroom Then I can open its detail page. | |||
Given I am the assigned student When the detail page loads Then I can see the subject, assigned tutor summary, price per hour, and status. | |||
Given I am the assigned student When the detail page loads Then tutor-only information is not returned or displayed. | |||
Given I am neither the assigned tutor nor the assigned student When I request the classroom detail Then access is denied and no classroom information is exposed. | |||
Given the classroom ID is malformed When the detail endpoint is requested Then the API returns a validation error. | |||
Given the classroom does not exist When the detail endpoint is requested Then a not-found state is displayed. | |||
Given classroom data is loading When the page is waiting for the API Then an appropriate loading state is displayed. | |||
Given the request fails When the error is returned Then a clear error state is displayed without stale classroom data. | |||
Given the classroom price is stored as a Prisma decimal When it is returned by the API Then the frontend receives and displays a valid numeric value. | |||
Given the page is viewed on a narrow screen When the detail content renders Then it remains readable without horizontal overflow. | |||
| US5-3 As tutor I want to update classroom details after creation So that the classroom information remains accurate | Given I am the tutor assigned to a classroom When I open its detail page Then an Edit action is visible. | — | |
Given I am the student assigned to a classroom When I open its detail page Then no Edit action is displayed. | |||
Given I am the assigned tutor When I enter edit mode Then the current subject and price per hour are prefilled. | |||
Given I select a valid subject and enter a valid positive price When I save Then the classroom is updated and the new values are displayed. | |||
Given I update the classroom price When the save succeeds Then my tutor profile's default hourly rate remains unchanged. | |||
Given I select Cancel When I return to view mode Then no API update occurs and the previous saved values remain displayed. | |||
Given the price is zero, negative, or invalid When I attempt to save Then a validation error is displayed and the classroom is not updated. | |||
Given the selected subject does not exist or is not assigned to me When I attempt to save Then the API rejects the request. | |||
Given an update is pending When I attempt to save again Then a duplicate update is not submitted. | |||
Given the update fails When the error is returned Then I remain in edit mode and my entered values are preserved. | |||
Given I am the assigned student When I call the update endpoint directly Then access is denied. | |||
Given I am an unrelated tutor When I call the update endpoint directly Then access is denied. | |||
Given an update request includes a tutor, student, status, ID, or creation date change When it reaches the API Then those fields are rejected or ignored and remain unchanged. | |||
Given the classroom ID is malformed or does not exist When an update is attempted Then an appropriate validation or not-found error is returned. | |||
Given the page is viewed on a narrow screen When edit mode is displayed Then the form remains usable without horizontal overflow. | |||
| US5-4 As tutor I want to search and filter my classrooms So that I can find and browse them easily | Given I am logged in as a tutor When I open My Classroom Then only classrooms assigned to me are displayed. | — | |
Given another tutor has classrooms When I open My Classroom Then their classrooms are not returned or displayed. | |||
Given I have active classrooms When the page first loads Then my active classrooms are shown by default. | |||
Given I enter part of a student's name When the search completes Then matching classrooms are displayed. | |||
Given I enter part of a subject name When the search completes Then matching classrooms are displayed. | |||
Given I use different letter casing or surrounding whitespace When I search Then matching results are still returned. | |||
Given I select a subject filter When the results update Then only classrooms for that subject are displayed. | |||
Given I select the Active status filter When the results update Then only active classrooms are displayed. | |||
Given I select the Completed status filter When the results update Then only completed classrooms are displayed. | |||
Given I select the All status option When the results update Then both active and completed classrooms may be displayed. | |||
Given I combine search, subject, and status filters When the results update Then only classrooms matching every selected condition are displayed. | |||
Given no classrooms match the current search and filters When the request succeeds Then a clear no-results state is displayed. | |||
Given I do not have any classrooms When the page loads Then a clear empty-list state is displayed. | |||
Given I clear the search and filters When the results refresh Then the default active classroom list is restored. | |||
Given classroom results are displayed When I view an item Then I can see its student, subject, price per hour, status, and creation date. | |||
Given I select a classroom When navigation occurs Then I am taken to `/classroom/[classroomId]`. | |||
Given the API request is pending When the page is waiting Then an appropriate loading state is displayed. | |||
Given the API request fails When the error is returned Then a clear error state is displayed without showing stale results as current. | |||
Given I am logged in as a student When I call the tutor classroom-list endpoint Then access is denied. | |||
Given the page is viewed on a narrow screen When the list and filters render Then they remain usable without horizontal overflow. | |||
| US5-5 As tutor I want to deactivate a finished classroom So that it no longer appears in my active classroom list | Given I am the tutor assigned to an active classroom When I open its detail page Then a Deactivate Classroom action is visible. | — | |
Given I am the assigned student When I open the classroom detail page Then the deactivation action is not displayed. | |||
Given I am an unrelated user When I open or request the classroom details Then I cannot deactivate the classroom. | |||
Given I select Deactivate Classroom When the confirmation dialog opens Then it explains that the classroom will leave the active list while its data is retained. | |||
Given the confirmation dialog is open When I select Cancel Then no API request is made and the classroom remains active. | |||
Given I confirm deactivation When the request succeeds Then the classroom status changes from `active` to `completed`. | |||
Given deactivation succeeds When the update is complete Then the classroom row and related records remain in the database. | |||
Given deactivation succeeds When the frontend handles the response Then I am redirected to `/classroom`. | |||
Given active classroom queries filter by `status = active` When the classroom has been deactivated Then it is no longer included in their results. | |||
Given deactivation is pending When I select Confirm again Then another request is not submitted. | |||
Given deactivation fails When the API returns an error Then I remain on the detail page and the classroom is not presented as successfully deactivated. | |||
Given I am a student or unrelated tutor When I call the deactivation endpoint directly Then access is denied. | |||
Given the classroom ID is malformed or does not exist When deactivation is requested Then an appropriate validation or not-found error is returned. | |||
Given the classroom is already completed When deactivation is requested again Then the operation does not create additional side effects. |