Mentor Platform — UX flow review

74 screens captured from the running app on 2026-11-03 · complete static copy — images travel with the folder · “open live” links go to the staging deployment ↗ (sign in there; its data differs from these captures) · ecosystem: YUP project generator ↗ · set a status + note per screen, then export

0 / 74 reviewed

0. Entry, sign-in and identity 6

Sign in

Sign in

Not signed in
/en/sign-in open live ↗

Role buttons for the demo accounts; one click + submit should enter that role. Check focus order and that the chosen role is obvious.

You are signed out

You are signed out

Not signed in
/en/signed-out open live ↗

Confirms the session ended and offers a way back in.

Check your email

Check your email

Not signed in
/en/sign-in/verify open live ↗

Email-code step. Blocked when no code has been issued — does the copy explain what to do?

Sign in

Sign in

Not signed in
/en/sign-in/two-factor open live ↗
redirects to /en/sign-in

Second factor prompt. Redirects to sign-in when the account has no factor enrolled.

Sign in

Sign in

Not signed in
/en/sign-in/two-factor/setup open live ↗
redirects to /en/sign-in

Enrolment screen. Also redirects to sign-in — is that intended for this demo build?

Sign in

Sign in

Not signed in
/en/forbidden open live ↗
redirects to /en/sign-in

Permission-denied page. While signed out it bounces to sign-in; signed in it should explain what is missing.

1. Public programme page → mentor application 3

JC YUP F.2 (test) — step 1 of 5

JC YUP F.2 (test) — step 1 of 5

Not signed in
/en/join/{slug}/mentor-signup open live ↗

The mentor sign-up wizard: profile → languages/industry → availability → final questions → submit.

This link does not work anymore

This link does not work anymore

Not signed in
/en/join/link open live ↗

Entry point for a one-time join link. Should explain what the link does and who it is for.

Mentor Charter

Mentor Charter

Not signed in
/en/join/documents/{id} open live ↗

Document view for a sign-up applicant. Check readability and that it shows which application it belongs to.

2. Admin: set up a programme 8

Welcome, Amy Cheung

Welcome, Amy Cheung

Admin
/en/admin open live ↗

Admin landing. Should summarise what the admin can do and surface programmes that need attention.

Programmes

Programmes

Admin
/en/admin/programmes open live ↗

Programme list with status. Check empty state, sorting and the path into settings.

New programme

New programme

Admin
/en/admin/programmes/new open live ↗

Create-programme form. Check required fields, validation messages and cancel behaviour.

JC YUP F.3 (sample)

JC YUP F.3 (sample)

Admin
/en/admin/programmes/{id} open live ↗

Programme overview: status, dates, counts. The main hub an admin returns to.

JC YUP F.3 (sample)

JC YUP F.3 (sample)

Admin
/en/admin/programmes/{id}/settings open live ↗

Programme settings. Long form — check save feedback, validation and unsaved-change warnings.

JC YUP F.3 (sample)

JC YUP F.3 (sample)

Admin
/en/admin/programmes/{id}/history open live ↗

Version history of settings. Should show who changed what and when.

JC YUP F.3 (sample)

JC YUP F.3 (sample)

Admin
/en/admin/programmes/{id}/organisations open live ↗

Which schools/groups are attached. Check add/remove clarity.

JC YUP F.3 (sample)

JC YUP F.3 (sample)

Admin
/en/admin/programmes/{id}/signup-pages open live ↗

Sign-up page(s) for this programme. Entry to the wizard CMS.

3. Admin: content and sign-up questions 2

JC YUP F.3 (sample)

JC YUP F.3 (sample)

Admin
/en/admin/programmes/{id}/pages open live ↗

Content pages (CMS) for the programme, with preview/history/links per page.

Mentor sign-up pages

Mentor sign-up pages

Admin
/en/admin/signup-pages open live ↗

Sign-up page list. Each page is the questionnaire mentors/mentees answer.

4. Admin: mentors, people, invites 4

Mentors

Mentors

Admin
/en/admin/mentors open live ↗

Mentor pool. Row action opens the invite dialog (choose programme → Send invite).

Raj Patel

Raj Patel

Admin
/en/admin/mentors/{id} open live ↗

One mentor: profile, applications, invite state.

People

People

Admin
/en/admin/people open live ↗

All people. Check search and the role/type columns.

Lucas Tse謝樂軒

Lucas Tse謝樂軒

Admin
/en/admin/people/{id} open live ↗

One person: roles, memberships, applications.

5. Admin: schools, teams, imports 6

Schools & groups

Schools & groups

Admin
/en/admin/orgs open live ↗

Schools and other organisations.

Demo Secondary School

Demo Secondary School

Admin
/en/admin/orgs/{id} open live ↗

One organisation: teams, programmes, contacts.

Teams

Teams

Admin
/en/admin/orgs/{id}/teams open live ↗

Teams inside an organisation, with build/approve actions.

Page not found

Page not found

Admin
/en/admin/orgs/{id}/teams/build open live ↗

Team-building tool — the step that must exist before matching and calls.

Import students

Import students

Admin
/en/admin/orgs/import open live ↗

Spreadsheet import (CSV/XLSX/paste/Google Sheet URL) with a preview before commit.

Import history

Import history

Admin
/en/admin/orgs/imports open live ↗

Import history and per-row outcomes.

5b. Student intake — live run, CSV and Google Sheet 7

Choose the list — Google Sheet link

Choose the list — Google Sheet link

Admin
/en/admin/orgs/import#sheet open live ↗

One organisation: teams, programmes, contacts.

Match the columns — read from the sheet

Match the columns — read from the sheet

Admin
/en/admin/orgs/import#match-sheet open live ↗

One organisation: teams, programmes, contacts.

Check — blocked on unmapped columns

Check — blocked on unmapped columns

Admin
/en/admin/orgs/import#check-sheet open live ↗

One organisation: teams, programmes, contacts.

Match the columns — CSV upload

Match the columns — CSV upload

Admin
/en/admin/orgs/import#match open live ↗

One organisation: teams, programmes, contacts.

Check — a row that fails validation

Check — a row that fails validation

Admin
/en/admin/orgs/import#check open live ↗

One organisation: teams, programmes, contacts.

Check — one row still blocked

Check — one row still blocked

Admin
/en/admin/orgs/import#check-clean open live ↗

One organisation: teams, programmes, contacts.

Done — the import is saved

Done — the import is saved

Admin
/en/admin/orgs/import#done open live ↗

One organisation: teams, programmes, contacts.

6. Admin: operations 3

Source tags

Source tags

Admin
/en/admin/tags open live ↗

Tag vocabulary used across programmes and people.

Activity history

Activity history

Admin
/en/admin/audit open live ↗

Audit trail — the evidence surface for every action the admin takes.

Failed tasks

Failed tasks

Admin
/en/admin/jobs open live ↗

Background jobs (invites, emails). A stuck job here explains a missing email.

7. Mentor: profile, applications, joining 4

My profile

My profile

Mentor
/en/mentor/profile open live ↗

Mentor's own profile — the record students see.

Find a programme

Find a programme

Mentor
/en/mentor/programmes open live ↗

Programmes a mentor can join. Join creates a draft application.

Supported programmes

Supported programmes

Mentor
/en/mentor/applications open live ↗

The mentor's applications and their status (invited → draft → submitted → accepted).

My briefs

My briefs

Mentor
/en/mentor/ideas open live ↗

Idea/project submissions from mentors.

8. Student, teacher, partner, social worker 9

My profile

My profile

Student
/en/student/profile open live ↗

Student profile; also the landing page after sign-in.

Your account

Your account

Student

Personal account view.

My profile

My profile

Student
/en/home open live ↗
redirects to /en/student/profile

Legacy landing route — redirects to the student profile.

My teams

My teams

Teacher
/en/teacher/teams open live ↗

Teacher view of their school's teams.

My people

My people

Partner
/en/partner/people open live ↗

Partner side: people in the partner organisation.

My mentors

My mentors

Partner
/en/partner/mentors open live ↗

Partner side: mentors available/linked.

Your imports

Your imports

Partner
/en/partner/imports open live ↗

Partner-side import history.

Import your students

Import your students

Partner
/en/partner/imports/new open live ↗

Partner-side import flow.

My teams

My teams

Social worker
/en/social-worker/teams open live ↗

Social worker view of teams they support.

9. Reference 1

Mentor sign-up

Mentor sign-up

Not signed in
/en/design open live ↗

Design-system reference page — verify tokens/components match the shipped UI.

10. Other captured screens 21

Check — 30 new, 10 not in the list

Check — 30 new, 10 not in the list

Admin
/en/admin/orgs/import#notinlist open live ↗

One organisation: teams, programmes, contacts.

Demo Secondary School in JC YUP F.3 (sample)

Demo Secondary School in JC YUP F.3 (sample)

Admin
/en/admin/orgs/{id}/programmes/{id} open live ↗

One organisation: teams, programmes, contacts.

JC YUP F.3 (sample)

JC YUP F.3 (sample)

Admin
/en/admin/programmes/{id}/pages/{id} open live ↗

Programme overview: status, dates, counts. The main hub an admin returns to.

JC YUP F.3 (sample)

JC YUP F.3 (sample)

Admin
/en/admin/programmes/{id}/pages/{id}/history open live ↗

Programme overview: status, dates, counts. The main hub an admin returns to.

JC YUP F.3 (sample)

JC YUP F.3 (sample)

Admin
/en/admin/programmes/{id}/pages/{id}/links open live ↗

Programme overview: status, dates, counts. The main hub an admin returns to.

JC YUP F.3 (sample)

JC YUP F.3 (sample)

Admin
/en/admin/programmes/{id}/pages/{id}/preview open live ↗

Programme overview: status, dates, counts. The main hub an admin returns to.

JC YUP F.3 (sample)

JC YUP F.3 (sample)

Admin
/en/admin/programmes/{id}/settings/alarms open live ↗

Programme overview: status, dates, counts. The main hub an admin returns to.

JC YUP F.3 (sample)

JC YUP F.3 (sample)

Admin
/en/admin/programmes/{id}/settings/basics open live ↗

Programme overview: status, dates, counts. The main hub an admin returns to.

JC YUP F.3 (sample)

JC YUP F.3 (sample)

Admin
/en/admin/programmes/{id}/settings/mentors open live ↗

Programme overview: status, dates, counts. The main hub an admin returns to.

JC YUP F.3 (sample)

JC YUP F.3 (sample)

Admin
/en/admin/programmes/{id}/settings/people open live ↗

Programme overview: status, dates, counts. The main hub an admin returns to.

JC YUP F.3 (sample)

JC YUP F.3 (sample)

Admin
/en/admin/programmes/{id}/settings/schedule open live ↗

Programme overview: status, dates, counts. The main hub an admin returns to.

JC YUP F.3 (sample)

JC YUP F.3 (sample)

Admin
/en/admin/programmes/{id}/settings/signup-pages open live ↗

Programme overview: status, dates, counts. The main hub an admin returns to.

JC YUP F.3 (sample)

JC YUP F.3 (sample)

Admin
/en/admin/programmes/{id}/settings/student-fields open live ↗

Programme overview: status, dates, counts. The main hub an admin returns to.

JC YUP F.3 (sample)

JC YUP F.3 (sample)

Admin
/en/admin/programmes/{id}/settings/teams open live ↗

Programme overview: status, dates, counts. The main hub an admin returns to.

Page not found

Page not found

Not signed in
/en/join/jc-yup-f3-sample open live ↗

Public programme landing → sign-up entry for mentors and mentees.

My profile

My profile

Mentor
/en/mentor open live ↗
redirects to /en/mentor/profile

My people

My people

Partner
/en/partner open live ↗
redirects to /en/partner/people

My teams

My teams

Social worker
/en/social-worker open live ↗
redirects to /en/social-worker/teams

My profile

My profile

Student
/en/student open live ↗
redirects to /en/student/profile

My teams

My teams

Teacher
/en/teacher open live ↗
redirects to /en/teacher/teams

Page not found

Page not found

Not signed in
/en/this-page-does-not-exist open live ↗

Student intake — what actually works today

Two intake paths exist and both were driven end to end on the running app on 2026-11-03. A class list is brought in through /en/admin/orgs/import: choose a school or group, supply a CSV file or a Google Sheet link, match columns to the template, check, import. Everything is previewed before a single row is written, and each confirmed import leaves an audit record.

worksCSV upload

Five students created, zero errors

Verified in the database, not just on the success screen: imports row source_type=csv, status=confirmed, summary new 5 / error 0 at 2026-11-03 14:18:56.

worksGoogle Sheet by URL

Thirty rows read live from a shared sheet, columns matched automatically

The public sample sheet was read through the service account and its thirty students arrived on the mapping step. It stopped at Check only because that sample has no email column and no VASK column - a data problem, not a code problem. This path is also where the credentials fault was hiding (see findings).

missingdocs · slides · PDFs

No document or slide submission exists

There is no upload for a .docx, .pptx, PDF, Google Doc or Google Slide, no attachment table, no per-student submission record, and no review screen for a submitted file. The wizard is a class-list importer, not a submission inbox. This is the gap between the platform as built and the platform as intended.

Mentor sign-up wizard, walked

The wizard at /en/join/{slug}/mentor-signup was driven with real input: text fields filled, both pick-lists opened and selected, radios answered. These are the states a real applicant sees — including the section where it currently refuses to advance: the five captures marked “repeat attempt” are consecutive submissions of one screen, pixel-identical, save timestamp frozen at 1:52 pm. It never reached a submit or review screen — and nothing in the UI explains why.

Screen 1 — Your profile
Screen 1 — Your profileText fields only; no indication how many sections follow.
Screen 2 — Languages
Screen 2 — LanguagesSubmitting empty shows 2 errors at once; the picker itself gives no hint of the requirement.Please fix 2 problems.
Screen 3 — Industry
Screen 3 — IndustrySame pattern: required pick-list, error only after submit.Please fix 1 problem.
Screen 4 — Availability
Screen 4 — AvailabilityYes/no question rendered as a list of native radios.
Screen 5 — Partnership question
Screen 5 — Partnership questionAnswering it does not satisfy the required group; eight attempts stalled here.Please fix 1 problem.
Repeat attempt — identical wall
Repeat attempt — identical wallSame Availability section, same single error, save timestamp frozen at 1:52 pm.Please fix 1 problem.
Repeat attempt — identical wall
Repeat attempt — identical wallNothing on the page acknowledges the click; the error block simply re-renders.Please fix 1 problem.
Repeat attempt — identical wall
Repeat attempt — identical wallNo scroll-to-error, no changed state — indistinguishable from the previous attempt.Please fix 1 problem.
Repeat attempt — identical wall
Repeat attempt — identical wallCaptures 6–10 are pixel-identical; the applicant cannot tell the submit did anything.Please fix 1 problem.
Repeat attempt — identical wall
Repeat attempt — identical wallFive consecutive submissions, one unactionable error. The stall behind the findings entry below.Please fix 1 problem.
After answering the radio group
After answering the radio group“Save and continue” leaves the applicant on the same section — no review screen.
Next attempt
Next attemptTwo errors: the partnership answer was lost and a new question (group count) appeared.Please fix 2 problems.

Issues found while capturing

Each of these was observed in the running app, not inferred from code. Severity is about user-visible damage, not implementation cost.

High/en/join/{slug}/mentor-signup

Radio questions can swallow an answer

The 'Would you be interested in extending this partnership…' question is a custom radiogroup (role=radiogroup, aria-required). Clicking the option marks it aria-checked=true, but Submit still reports 'Please answer this question.', and two options in the same group can end up aria-checked=true at once because the underlying inputs share no name attribute. A mentor can therefore be blocked on a required question they have visibly answered.

Suggested fix — Give the radio inputs a shared name, drive form state from the group value, and clear the field error on change.

Highall routes

Signed-out access to /en/forbidden lands on /en/sign-in

Requesting a forbidden page while signed out redirects to sign-in instead of explaining the permission problem. Reviewers cannot tell 'you lack the role' from 'you are not signed in'.

Suggested fix — Keep the 403 for authenticated users; only redirect anonymous traffic, and say which permission is missing.

Medium/en/join/{slug}/mentor-signup

Language picker is a 150-item alphabetical list

The 'Which languages can you mentor in?' control opens the full ISO-639 list (150 options, Abkhazian first). A Hong Kong programme needs English, Cantonese and Mandarin at the top; there is no grouping or recent-first ordering.

Suggested fix — Put programme-relevant languages first, add type-to-filter, collapse the rest behind 'more languages'.

Mediumrole landing routes

Role home pages redirect into a sub-page

/en/mentor → /en/mentor/profile, /en/student → /en/student/profile, /en/teacher → /en/teacher/teams, /en/partner → /en/partner/people, /en/social-worker → /en/social-worker/teams. Each role therefore has no overview screen; the first thing a new user sees is a profile edit form or a list.

Suggested fix — Decide whether an overview is wanted; if not, rename the nav entries so the label matches the destination.

Mediumwizard / forms

Wizard progress is invisible between sections

The sign-up wizard paginates sections (Your profile → mentoring → industry → availability → questions) but shows the same shell each time; nothing indicates how many steps remain until the review screen.

Suggested fix — Add a step counter or progress bar and keep answered sections marked.

High/en/admin/orgs/import

Submissions: only CSV and Google Sheets can be brought in

The stated purpose is students submitting sheets, docs and slides. The only intake that exists is a class-list spreadsheet: the wizard accepts a CSV file or a Google Sheet URL and maps columns to the student template. There is no path for a .docx, .pptx, PDF or Google Doc/Slide, no attachment storage, and no per-student submission record - the check step in fact requires an email and a VASK column, which no document or slide deck has. Every document workflow a programme needs sits behind this gap.

Suggested fix — Decide the submission shape first (does a student hand in a file, or does staff import a class list?). For files: add an attachment table, a per-student submission record, a parser that extracts text, and map the extracted content onto the same template fields this wizard already validates - do not build a parallel intake screen.

Medium/en/admin/orgs/import#check-sheet

Counters disagree with what they list

On the sheet run the counters read Need fixing 0 while the import button is disabled for unmapped required columns, and 'Not in the list (10)' is shown on both a five-row CSV and a thirty-row sheet - so it is not a row count, yet it sits in a row of person-counts. A reviewer cannot tell whether 10 people are affected or whether something else is being counted.

Suggested fix — Label each counter with what it counts ('10 template columns not in this file'), and let the blocker panel drive the Need-fixing count so a blocked import never reads as zero work.

Medium/en/admin/orgs/import#sheet

'Not set up yet' is a dead end for an admin

When the Google service account is absent or unparseable the sheet source renders the sheet option disabled with 'not set up yet'. Nothing names the setting, the service account email or who owns it, and the failure is silent: the monorepo root .env carried a valid value while apps/web/.env.local carried an unusable one, so the app looked unconfigured even though the credential existed on the machine.

Suggested fix — Surface the service account email and the missing setting name on the disabled option, and log a startup warning when the credential fails to parse instead of only failing at click time.

Low/en/admin/mentors

Invite dialog offers a single programme

Only one programme appears in the invite picker. That is correct for the seeded data (the only active admin holds a programme-scoped role), but it reads as a bug to a reviewer.

Suggested fix — Show the scope in the dialog copy, e.g. 'Programmes you administer (1)'.

Infocalls / LiveKit

Call flows are specified but not built

The video-call system (auto-generated mentor and mentee links, LiveKit rooms, captions, post-call report) is designed in 05-calls.md but has 0 of 11 platform tasks implemented; matching is a prerequisite and is 1 of 7 done.

Suggested fix — Review the call section on this page for the intended flow; there is no live screen to capture yet.

Call system (video) — flows to review

Calls are derived, not booked: once a team is matched, the platform generates the schedule and issues each participant their own link. 0 of 11 platform tasks for this are implemented, so these are the intended flows to sign off on the design — there is no live screen yet.

  1. 1. Programme runs, teams approved

    Staff approve a team of students for a programme. Nothing is scheduled yet.

  2. 2. Matching

    Mentor ↔ team matching. Hard prerequisite for every call — currently 1 of 7 tasks.

  3. 3. Schedule generated

    The platform derives a schedule of sessions from programme dates and team availability.

  4. 4. Mentor link issued

    A personal join link is issued to the mentor automatically — no booking step.

  5. 5. Each mentee link issued

    Every mentee gets their own distinct link, so attendance is attributable person by person.

  6. 6. Staff / guest link

    Observers get a link with a different role, so they join without appearing as a participant.

  7. 7. LiveKit room

    Both links open the same room; captions run, and an AI monitor watches for safeguarding signals.

  8. 8. Post-call report

    After the room closes: transcript, summary, flags and attendance per person.

  9. 9. Flags → alarms

    Serious flags raise alarms to the programme lead; routine ones queue for review.

  10. 10. Programme review

    Sessions, attendance and outcomes roll up to the programme dashboard.

Runbook — running this stack locally

Everything runs natively on the Mac Studio (no Docker). Repo ~/Desktop/projects/mentorPlatform; call-stack scripts in ~/mentor-call. Verified 2026-11-03.

startapps + call stack

Three commands

brew services start postgresql@18 → cd ~/Desktop/projects/mentorPlatform && pnpm dev (web :3000 + worker, log /tmp/mentor-dev.log) → ~/mentor-call/up.sh (LiveKit :7880, spike API :8000, AI agent, call page via caddy :8080). Demo login at /en/sign-in → "Sign in as seeded super admin".

checkis it actually up?

Two lines, no guessing

curl -s -o /dev/null -w '%{http_code}' localhost:3000/en must print 200, and the log must print [worker] running N job(s). A detached pnpm dev that "exited 0" right after Ready is the wrapper detaching — check the port before restarting anything. The web server has been known to die silently while turbo and the worker keep running.

stopteardown — verify, don't trust

down.sh can leave the AI agent alive

~/mentor-call/down.sh + quit the pnpm dev process, then verify: pgrep -fl call-ai-spike must return nothing and ports 3000 / 7880 / 8000 / 8080 must be free (lsof -nP -iTCP:PORT -sTCP:LISTEN). Observed 2026-11-03: down.sh printed "not running: agent.py" while the agent process was still alive — kill stragglers by PID. Postgres is a shared brew service; leave it running unless asked.

reviewthis page

Live at mentor-ux.bsd.education

This review is published at https://mentor-ux.bsd.education/ (rebuild with python3 build_review.py, then sync index.html to S3 and invalidate CloudFront). Local copy works too: cd ~/mentor-ux-review && python3 -m http.server 4173. Review statuses/notes live in this browser's localStorage and leave via the export button — export before switching machines or browsers.