The Portals
The non-staff experiences — the guest application flow and the lighter student and guardian hubs.
Not everyone who logs into Education Hub is a member of staff. A parent applying for their child, a job-seeker filling out a vacancy form, a student checking a grade — these people need their own, simpler corner of the app. We call those corners the portals, and there are three of them: guest, student, and guardian.
Think of the portals as the friendly front door, and the staff hub as the back office. The staff hub is dense — multi-school switchers, data tables, permission-gated everything. The portals deliberately strip most of that away. They each live under src/routes/_authenticated/{guest,student,guardian}, so they're still behind the login wall — but past that wall they show only what one kind of person needs to see.
Honesty up front
Of the three, only the guest portal is fully built out today. The student and guardian hubs are wired up — routes, sidebars, guards, navbars — but most of their inner pages are still placeholders. Don't be surprised when you open student/hub.tsx and find it returns a single <div>. That's expected, not a bug.
How a portal is gated
Each portal is a layout route — a route.tsx whose only job is to render a shell (header or sidebar) and an <Outlet /> for the pages inside it. The important line is the guard in beforeLoad, which decides who is even allowed in. It keys off the user's userType, the same concept covered in User types.
export const Route = createFileRoute('/_authenticated/guardian')({
beforeLoad: requirePermission({ userType: 'guardian' }),
component: RouteComponent,
})So a guardian who tries to wander into /student is bounced by the guard before the page ever renders. Each portal locks itself to exactly one user type — guest, student, or guardian.
The guest portal
The guest portal (/guest) is for people who don't have an ongoing relationship with the school yet — applicants and job-seekers. Its landing page greets you by name and asks one question — "What brings you here today?" — then offers a few big tiles into the main flows:
- Admission Applications (
/guest/admissions) — start, save, and track an application to enrol. - Job Applications (
/guest/jobs) — browse vacancies and apply for staff roles. - My Letters (
/guest/letters) — admission or offer letters once they're issued.
const actionButtons = [
{ title: 'Admission Applications', path: '/guest/admissions', icon: TaskEdit01Icon },
{ title: 'Job Applications', path: '/guest/jobs', icon: WorkIcon },
{ title: 'My Letters', path: '/guest/letters', icon: Mail02Icon },
]No sidebar, just a header
The biggest tell that you're in the guest portal is the layout: there's no sidebar at all. The route.tsx renders a plain top header — the school logo on the left, an account dropdown on the right — and the page fills everything below it. That dropdown is where a guest copies their access code, opens their profile, toggles theme, or signs out.
<header className="w-full h-(--header-height) ...">
<Link to="/"> {/* logo + "Naalya S.S" */} </Link>
{me && <DropdownMenu> {/* account menu */} </DropdownMenu>}
</header>
<main className="w-full flex-1 min-h-0">
<Outlet />
</main>The admissions flow underneath is the fullest feature in any portal — a multi-section form (learner info, academic history, parents, declaration) that auto-saves as you go, plus a list view that lets you create a new application or resume a draft. It's a good place to see the forms and data-layer patterns in real use.
The student portal
The student portal (/student) is the learner's view — their classes, grades, enrolments, and the Naalya AI tutor. Unlike guest, it does get a sidebar (student.sidebar.tsx), with five destinations:
const paths = {
hub: '/student/hub',
classes: '/student/classes',
grades: '/student/grades',
enrollments: '/student/enrollments',
naalyaBot: '/student/naalya-bot',
}That Naalya Bot entry is the student-facing slice of the AI assistant — a tutor a learner can chat with. The sidebar even paints a small badge dot on it to flag activity.
The campus check
There's one subtlety in the student route worth knowing. A student record is meaningless without a campus — which school they belong to. So the beforeLoad guard does two things: it requires the student user type, and it reads the profile for a campusId. If there isn't one, it redirects to a friendly empty-state page instead of dumping the student into a broken hub.
const me = await queryClient.ensureQueryData(AuthOptions.getMe())
const campusId =
me && 'campusId' in me.user.profile ? me.user.profile.campusId : undefined
if (!campusId && location.pathname !== '/student/no-campus') {
throw redirect({ to: '/student/no-campus' })
}The /student/no-campus page is a polite "your administrator hasn't assigned you to a campus yet" screen with a Remind them button. The condition skips itself when you're already on no-campus, so the redirect can't loop.
The inner pages are mostly stubs
student/hub.tsx currently renders just <div className="hub-page">Student Hub</div>. The route, sidebar, navbar, and campus guard are all real — the dashboards behind each nav item are the part still waiting to be built.
The guardian portal
The guardian portal (/guardian) is for parents and guardians watching over a child. It mirrors the student shell — sidebar plus navbar plus an <Outlet /> — but its navigation points at a guardian's concerns: their children, those children's grades, payments, and again the Naalya AI assistant.
const paths = {
hub: '/guardian/hub',
children: '/guardian/children',
grades: '/guardian/grades',
payments: '/guardian/payments',
naalyaBot: '/guardian/naalya-bot',
}Like the student hub, the guardian hub is light today — guardian/hub.tsx is a placeholder too. The structure is there and ready; the screens are the next thing to fill in. If you're picking up portal work, this is fertile ground: the layout, guard, and navigation are done, so you can focus purely on building the pages.
How the three compare
| Portal | User type | Shell | State today |
|---|---|---|---|
| Guest | guest | header only, no sidebar | fully built (admissions, jobs) |
| Student | student | sidebar + navbar | shell done, pages mostly stubs |
| Guardian | guardian | sidebar + navbar | shell done, pages mostly stubs |
When you build out a portal page, you reach for the same building blocks the staff hub uses — they just appear in a calmer setting. New screens follow the add a page recipe, pull data through the data layer, and lean on the shared design system.
Where to go next
User types
The guest, student, and guardian types that gate each portal.
The AI assistant
The Naalya tutor that lives in the student and guardian sidebars.
The staff hub
The denser back-office experience the portals deliberately simplify.
Add a page
The recipe for building out one of those placeholder hub screens.