Skip to content

feat: intern batch self-selection onboarding screen

Description

Display a batch selection screen when an intern logs in for the first time and has no batch_id assigned. The screen fetches available batches from GET /api/batches/, lets the intern pick one, and persists the choice via PATCH /api/me/batch. After selection, the profile is re-fetched and the intern proceeds to the main app.

Related Issue

Closes #

Type of Change

  • New feature (non-breaking change which adds functionality)
  • Bug fix (non-breaking change which fixes an issue)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update

Changes Made

File Change
src/api/index.ts Added setMyBatch() — calls PATCH /api/me/batch
src/contexts/AuthContext.tsx Exposed refetchMe() so the app can re-fetch the user profile after batch selection
src/components/auth/BatchSelectionPage.tsx New component — full-screen batch picker with radio buttons, loading/error/empty states
src/App.tsx Added guard: if authenticated intern has batchId == null, renders BatchSelectionPage instead of the main app

Component: BatchSelectionPage

  • Loading state: Shows "Loading batches…" while fetching
  • Empty state: "No batches are available yet. Ask an admin to create one."
  • Error state: Displays the API error message inline
  • Selection state: Radio-button list with visual highlight for selected batch
  • Success flow: Calls setMyBatch()refetchMe() → component disappears → main app renders
  • Reuses the existing .portal-login-page / .portal-login-shell CSS classes from CorpusLoginPage

How It Works

  1. App.tsx checks: role === 'intern' && batchId === null → renders BatchSelectionPage
  2. BatchSelectionPage mounts → calls getBatches() → renders radio buttons
  3. Intern picks a batch → clicks "Confirm Selection"
  4. Calls setMyBatch(batchId)PATCH /api/me/batch
  5. On success → calls refetchMe() → auth context updates → batchId is no longer null
  6. App.tsx re-renders → BatchSelectionPage is hidden → main app shown

How Has This Been Tested?

  • TypeScript compiles clean: tsc --noEmit — zero errors
  • Vite production build succeeds: 452 KB JS, 48 KB CSS
  • Manual testing with backend running locally
  • End-to-end test: login → batch selection → standup workspace

Screenshots (if appropriate)

Checklist

  • My code follows the project's coding conventions
  • I have performed a self-review of my own code
  • I have commented my code where necessary
  • I have added corresponding tests (if applicable)
  • My changes generate no new TypeScript errors or warnings
  • The production build completes without errors

Companion MR

  • Backend: intern-activity-tracker → MR title: feat: intern batch self-selection endpoint and service
  • Branch: feat/intern-batch-self-selection in both repos

Semantic Versioning Impact

  • Patch
  • Minor
  • Major

Reviewer Notes

  • BatchResponse count fields (team_count, member_count) are now null in the intern response from the backend. The BatchSelectionPage only reads id, name, and date — it does not display counts — so this change has no impact on this component.
  • The AuthContext.refetchMe() addition is minimal — it simply exposes the existing useQuery.refetch(). No new state handling is required.

Merge request reports

Loading