Skip to main content

Teacher App Guide

This guide covers the navigation, auth, and shell of the teacher app — feature behavior lives in Feature Flows.

App overview

The teacher app is a React + Vite app wrapped with Capacitor — the same bundle ships as a mobile app (Android / iOS) and a web app (dev port 5175). It's used by subject teachers and class teachers to run their day: take attendance, mark homework, post diary entries, send alerts, run live classes, and chat with parents and students.

Layout is mobile-first (max-w-xl, fixed bottom nav, safe-area padding) — even on desktop the UI is rendered as a phone-shaped column. UI uses Radix + Tailwind; state is Zustand + TanStack Query; push notifications are Capacitor-native and route through React Router so deep-links land on the actual screen instead of reloading the WebView.

The app has two top-level zones: the public shell (/, /login, /register, /forgot-password) and the authenticated dashboard (/dashboard/* and a handful of sibling routes). Everything authenticated is wrapped in ProtectedRoute, which redirects to /login if the access token is missing.

Bottom nav

Visible on most authenticated screens. Five tabs plus a floating AI Assistant button:

TabPathNotes
Home/dashboardThe dashboard landing page
Classes/dashboard/classesList of teacher's sections
Messages/messagesChat threads — shows an unread-count badge sourced from the chat store
Alerts/dashboard/alertsNotice / alert composer
Profile/dashboard/profileProfile view
AI Assistant (FAB)/ai-assistantFloating circular button centered above the bar

The Home, Alerts, and Profile tabs match exactly; Classes and Messages match by prefix (so deeper routes like /messages/:id keep the Messages tab highlighted).

Top-level routes (outside /dashboard)

/profile, /profile/edit, /profile/change-password, /attendance, /notifications, /diary, /diary/create, /tests, /ai-assistant, /messages, /messages/:id — all ProtectedRoute-gated. Plus the dashboard tree (/dashboard/*) which holds classes, assignments, alerts, exams, doubts, group study, live class, teaching plan, etc.

Public landing (/)

Auto-playing 5-slide carousel (3.5s interval) with marketing copy and two CTAs: Go to Login and Go to Register. If the persisted Zustand store rehydrates with a teacher / admin / super_admin user, the page redirects to /dashboard before the carousel renders, so signed-in users never see it flash.

Authentication

Login (/login)

Email + password form. Role is hard-coded to teacher in the request — a student or parent account cannot sign in here even with valid credentials. On success the access token, refresh token, and user are persisted, the full profile is fetched, and the app navigates to /dashboard.

Validation errors render inline; server errors render in a destructive banner above the submit button. The "Logging in..." state disables the button and shows a spinner.

Register (/register/*)

Multi-step wrapper with these sub-routes: emailotppasswordsuccess. Each step is its own component, but the wrapper does no path enforcement — direct deep-links to /register/password work, which is intentional but worth knowing for QA.

Forgot password (/forgot-password/*)

Two screens: /forgot-password shows the intro card with a "Request Password Reset" CTA; /forgot-password/request is the email entry form. On submit it calls the reset endpoint and swaps to a success state with mail-icon confirmation. There's no OTP verification on the device — the reset link arrives by email and is opened in a browser.

Blocked state

If a teacher's account is suspended, the API returns a flag and the app routes them to /blocked (UserBlockedPage). That page shows "Access Restricted", a "Contact Support" mailto button (support@scholify.app), and a "Logout Account" button that clears the user store and goes back to /login.

Profile & settings

  • /profile — view profile. Shows avatar, name, contact (phone, email), school, joining date, role, classes-count and students-count stats. Includes a theme toggle (light / dark / system) and a Logout dropdown.
  • /profile/edit — edit the editable subset (name, phone, profile picture). Profile picture upload goes through the profile service and is stored as either an attachment ID or a fileUrl.
  • /profile/change-password — old + new + confirm password. Client-side check: new and confirm must match, new must be ≥ 6 chars. No automatic logout after change — the existing tokens stay valid.

Error & utility pages

PathPurpose
/404 (and any unknown route)Friendly not-found
/500Server error fallback
/maintenanceAnimated "Currently Upgrading" page with a "Reconnect to Server" reload button
/blockedSuspended-account screen (see Authentication > Blocked state)

Unknown routes redirect via <Navigate to="/404" replace />. The maintenance page is reached when global app-state flags it — in practice, deploys.

Edge cases & things to test

  • Wrong-role login: try a student email/password on the teacher login — the API rejection should surface as a clean error toast, not a partial token persist.
  • Stale token: open the app with an expired access token but valid refresh token — the request interceptor should refresh silently and not bounce to /login.
  • Hydration flicker on cold start: Zustand persists to Capacitor Preferences (async) — the / carousel and /login-method-style screens must wait for persist.hasHydrated() before deciding to redirect, otherwise signed-in users see a flash of marketing.
  • Push deep-link: tap a homework-graded push while the app is closed — it should land on the assignment submission screen, not on /dashboard.
  • Bottom nav on Messages thread: open /messages/:id — the Messages tab must stay highlighted (prefix match), not lose the active state.
  • AI Assistant FAB tap from any tab: it should always go to /ai-assistant regardless of current route.
  • Profile picture upload on slow connection: ensure the spinner stays up until the upload completes; cancelling shouldn't leave a half-set picture.
  • Theme persistence: toggle to dark, kill the app, reopen — theme should restore from vite-ui-theme localStorage key.
  • Maintenance flag during an active session: when the global maintenance flag flips on, in-flight requests should not corrupt local state; the /maintenance redirect should be clean.
  • Multi-device login: log in on phone and web with the same account — both sessions should remain valid; logging out on one shouldn't kill the other (refresh tokens are per-device).
  • Status bar contrast: on iOS, the status bar text must be dark on the light background (configured at app start via configureStatusBar()).