Parent App Guide
This guide covers the navigation, auth, and shell of the parent app — feature behavior lives in Feature Flows.
App overview
The parent app is a React + Vite + Capacitor build (dev port 5176) used by parents and guardians to follow their children's school life: results, attendance, diary, projects (homework, re-skinned), PTM, notice board, class tests, syllabus, analytics, and chat with teachers. Like the other student-facing apps it ships as Android, iOS, and web from the same source and renders mobile-shaped (max-w-xl) on every viewport.
What sets the parent app apart is the multi-child switcher — one parent account is linked to one or more linkedStudents, and the home screen exposes a horizontal pill list to switch context between them. Most data on screens like Attendance, Results, Diary, Projects, and Class Tests is scoped to the currently-selected child via a studentProfileId carried in route state.
Navigation & layout
Two zones: the public auth shell (/login-method, /login, /forgot-password, /set-password, /reset-password) and the authenticated app (everything else). Anything unmatched redirects to /login-method.
Bottom nav (AppLayout)
Four tabs, no FAB. Used on Home, Results, Messages, and Progress (analytics):
| Tab | Path | Notes |
|---|---|---|
| Home | /dashboard | The HomePage with the multi-child switcher |
| Results | /results | Scoped to selected child |
| Messages | /messages | Chat threads — unread badge from chat store |
| Progress | /analytics | Trends, attendance %, score charts |
Active state matches paths exactly except Messages, which prefix-matches so /messages/:id keeps the tab highlighted.
Authenticated routes
/dashboard, /results, /diary, /projects, /ptm, /attendance, /notice-board, /class-tests, /analytics, /notifications, /buy-coins, /syllabus (with nested /:subjectId and /:subjectId/chapters/:chapterId), /messages, /messages/:id. All wrapped in ProtectedRoute.
Multi-child switcher
The HomePage (/dashboard) is where the parent picks which child they're looking at. The selected child's studentProfileId is then passed as location.state when navigating to scoped pages — so feature pages read the child id from route state, not from a global "current child" store.
If linkedStudents is empty, the switcher is hidden and the parent sees a degraded home view.
Dashboard landing (/dashboard)
Sticky header with logo + greeting + parent first name, a notifications bell with unread badge (via getNotifications({ limit: 1 })), and a Logout button (clears tokens, navigates to /login-method). Below: child switcher → quick action cards (Results, Diary, Projects, PTM, etc.) → school-services cards (Attendance, Notice Board, Syllabus, Class Tests, Buy Coins, Analytics).
For the active child the home page also shows a 30-day attendance percentage, total class tests, and due-tasks count fetched in parallel from the relevant services.
Authentication
Entry: /login-method
The default unauthenticated screen — full-screen gradient with the Scholiphi logo and a single CTA: Log in. If the user store rehydrates with an existing user or access token, this page redirects to /dashboard after a 50ms grace (waits for Capacitor Preferences async hydration so logged-in users don't flash the login CTA on cold start).
Login (/login)
Email-or-phone + password form. Unlike the teacher and student apps which require an email, the parent app's emailOrPhone field accepts either — useful in markets where parents don't have a school-issued email.
Network-error state shows the actual API base URL so QA can spot misconfigured builds. Auth errors render as a red alert above the submit button with a generic "Email or password incorrect" message.
Forgot password (/forgot-password)
Email-only form. On submit, fires the reset request and swaps to a success state — even on API failure it shows success (intentional, to avoid leaking which emails are registered). Note: parent app does not ship a separate Maintenance / NotFound / ServerError / Blocked page — those code-paths fall back to the catch-all Navigate to /login-method.
Set / reset password
/set-password— used for first-time password setup via an emailed link with?token=.... Enforces password ≥ 8 chars, ≥ 1 number, ≥ 1 special character, plus confirm match./reset-password— same shape, used after the forgot-password email link.
No registration
Parents are not self-signup users. Their accounts are provisioned by the school admin and linked to one or more student profiles; the parent's first interaction is the password-set email link.
Profile & settings
There is no dedicated /profile route in the current build. Instead, profile-adjacent affordances live on the home page:
- Logout —
LogOuticon button in the home header, clears the user store and navigates to/login-method. - Profile data — surfaced inline (greeting + parent name; school name on the school info banner; linked children in the switcher).
- Avatar — child avatars come from each
linkedStudent.profilePicture; the parent's own photo isn't surfaced anywhere user-facing.
Error & utility pages
The parent app does not currently ship dedicated maintenance / 404 / 500 / blocked pages — unmatched routes hard-redirect to /login-method. Native deep-linking errors (e.g. payment cancel from the external checkout browser) surface as Sonner toasts via the appUrlOpen listener in App.tsx.
| Scenario | Behavior |
|---|---|
| Unknown route | <Navigate to="/login-method" replace /> |
| Payment success deep-link | Toast: Payment successful. {N} coins added to your child's wallet. |
| Payment failed deep-link | Toast: "Payment failed. Please try again." |
| Payment cancelled | Toast: "Payment cancelled" |
| 401 on a protected route | ProtectedRoute redirects to the login flow |
If a global maintenance state needs to be shown, the parent app currently has no UI for it — see edge cases.
Edge cases & things to test
- Single-child parent: linkedStudents has exactly one entry — does the switcher hide or render as a single (selected) pill? Verify the selected child still drives child-scoped fetches.
- No-children parent: linkedStudents is empty — home should not crash; quick actions and school services should be either hidden or render an empty/CTA state, not throw.
- Multi-child different schools: a parent linked to children at two different schools — does the school info banner update when switching? Are child-scoped fetches scoped correctly across schools?
- Switcher → deep page → back: select child A, navigate to
/results, hit back, select child B, navigate to/resultsagain — does the page reload with B's data, or does cached state from A leak? - Push deep-link with multi-child: a "homework assigned" push for child B arrives — tapping it should land on the right child's project view, not the currently-selected child's.
- Email-or-phone login: phone with country code, phone without country code, email — confirm the API accepts each variant; client-side validation only checks "min length 1".
- Forgot-password obscure email: submit a non-existent email — UI shows success regardless. Confirm no real email is sent and no error leaks.
- Set-password with expired token: open
/set-password?token=expired— expect a clean error state, not a silent success. - Cold start hydration: kill app, reopen —
/login-methodmust not flash before the user-store rehydrates and redirects logged-in users to/dashboard. - External checkout return: complete a coin purchase, then come back via deep-link — toast must fire exactly once even if
appUrlOpenis invoked twice (thelastHandledUrlguard). - Logout from home header: confirm tokens cleared, socket disconnected, chat store reset, push token deregistered.
- Bottom nav on Messages thread: open
/messages/:id— the Messages tab must stay highlighted (prefix match). - Maintenance state with no UI: simulate the global maintenance flag — what does the parent see? (No dedicated screen exists; this is a known gap.)
Related
- Homework — surfaces in the parent app as Projects (
/projects) - Notifications
- Coins & Credits — purchases happen via
/buy-coins+ external checkout - Diary
- Notice Board & Alerts