> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tuturuuu.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Teach teacher companion app

> Teacher-facing education companion app for Tuturuuu workspaces.

Teach lives in `apps/teach` and runs locally on port `7813`. It is the teacher-facing operations app for Tuturuuu education workspaces. Teachers create and publish courses, enroll existing workspace users, author modules, save attendance, write posts and reports, and enter metrics inside Teach. Protected data and mutations still run through centralized `apps/web` API routes.

## Ownership model

Teach is a standalone teacher UI, but not a separate backend. Core teacher actions must stay inside `apps/teach`; avoid protected dashboard and course actions that jump teachers back to `apps/web`. The app forwards `/api/v1/*` and `/api/ai/*` traffic to the central web app through the satellite rewrite and API proxy guard; Teach-local API routes must stay limited to the auth cookie handoff and logout surfaces that require the Teach host.

`apps/web` remains the protected API owner. Teach-callable routes must opt in to app-session auth for the `teach` audience, resolve permissions from the forwarded app-session user, and then perform admin-backed reads/writes only after workspace membership is verified. New Teach-owned flows should add typed helpers in `@tuturuuu/internal-api` and consume those helpers from Teach client components with TanStack Query.

When a Teach route uses an admin client to write rows keyed by request-body IDs, the route must validate those body IDs against the URL workspace and course before writing. Workspace permission plus course existence is not enough, because the admin client bypasses RLS; score writes such as `indicator_id` and `user_id` must prove that the indicator belongs to the URL course/workspace and the user is an active course member in the same workspace.

Courses are stored as `workspace_user_groups`. The course-level Learn visibility switch is `workspace_user_groups.is_course_published`; module visibility remains `workspace_course_modules.is_published`. New courses default to unpublished drafts, non-guest, active, and empty. Teach can enroll existing workspace users into a course, but this self-serve pass does not create or invite new users.

Teach-owned attendance uses the course schedule fields on `workspace_user_groups`: `sessions`, `starting_date`, and `ending_date`. Teachers can generate recurring class sessions inside Teach, and the attendance calendar should mark unscheduled days, scheduled unchecked days, partial days, complete days, late days, and absent days without linking back to the web user-group schedule page.

Teach-local logout clears the host-only app-session cookies and stale Supabase Auth cookies on `teach.tuturuuu.*`, then redirects browser form submissions to the central `apps/web` `/logout?from=Teach` continuation. JSON callers can still POST `/api/auth/logout` and receive `{ success: true }`.

Teach does not render its own login portal. Its `/login` route first checks for an existing Teach app-session JWT. If the app session is already present, it redirects inside Teach to the requested `next` path, usually `/dashboard`. Otherwise it redirects to the platform login at `apps/web` with a `returnUrl` pointing back to `/verify-token`. After the platform login confirms the account, `apps/web` generates a cross-app token for the `teach` target app and redirects back. Teach's local `POST /api/auth/verify-app-token` route only completes the host-only cookie handoff; token validation is delegated back to the central web app, and Teach does not create a Teach-local Supabase Auth session. The handoff stores both a Teach-local app-session cookie for satellite route guards and a Web-issued app-session cookie for rewritten Web API requests. Protected Teach routes require both cookies; if an older local-only cookie is present, Teach sends the user back through the platform handoff so the coordinated cookie pair is refreshed without manual cookie deletion.

The `/dashboard` entry route must only send users to `/login` when the Teach app-session is missing. If the app-session exists but the central bootstrap API returns no eligible education workspace, render an empty teacher state instead of redirecting back to login.

Learn follows the same platform-login pattern: the learner app keeps `/verify-token` for cross-app session completion, but `/login` delegates account selection to `apps/web`.

Teach and Learn both hide locale prefixes from public and protected URLs. Legacy locale-prefixed paths such as `/vi/dashboard` redirect to the unprefixed canonical path while preserving the selected locale in `NEXT_LOCALE`.

Teach proxy locale detection must sanitize `Accept-Language` before it reaches `intl-localematcher`. Wildcard or malformed locale tokens can throw a `RangeError` in the proxy and surface as a broken `/login` or protected course page even when the route itself still exists.

Teach metadata and auth return URLs must resolve to absolute HTTP(S) app URLs. Prefer `TEACH_APP_URL` or `NEXT_PUBLIC_TEACH_APP_URL` for the Teach origin and `LEARN_APP_URL` or `NEXT_PUBLIC_LEARN_APP_URL` for Learn handoffs. A valid absolute `BASE_URL` can be used as a fallback for Teach, but non-URL environment values such as `development` are ignored so local development falls back to `https://teach.tuturuuu.localhost` through Portless.

## Visual direction

All non-`apps/web` education apps use the shared Neobrutalist design language from `apps/learn/DESIGN.md`: heavy foreground borders, offset shadows, paper-like surfaces, compact responsive grids, and theme-adaptive dynamic accents. Teach uses this language for public teacher orientation surfaces and protected dashboards.

Teach should read as a teacher operations dashboard, not a landing page. The protected surface should keep courses, module authoring, learner enrollment, schedule-aware attendance, posts, report previews, score metrics, settings, and learner-preview handoffs visible as first-class paths. Use multiple dynamic accent roles across these work loops and keep rails readable in light, dark, and system themes.

Teach module generation now accepts optional teacher context in the AI modal. Teachers can add class level, learning goals, or other constraints before uploading source material; the central course-generation route passes that context into the AI prompt alongside the document content.

Report authoring should include a Teach-local preview before save. The preview should show learner identity, course context, score, report body, feedback, and a metric snapshot so teachers do not need to open `apps/web` report pages for normal course reporting. When a learner-facing check is useful, link intentionally to `apps/learn` course, assignment, report, or marks pages.

Teach reads and mutates course groups and module data through central `apps/web` APIs. Web API routes that serve Teach must opt in to app-session auth for the `teach` audience and resolve permissions from the forwarded app-session user before falling back to workspace group membership filters.

## Verification

After changing Teach code, run:

```bash theme={null}
bun i18n:sort
bun type-check:teach
bun check
```

Run focused route or component tests when login bridging, cross-app token handling, or protected API forwarding changes.

## CI and deployment

Teach has dedicated Vercel workflows:

* `.github/workflows/vercel-preview-teach.yaml`
* `.github/workflows/vercel-production-teach.yaml`

Both workflows are registered in `tuturuuu.ts` and use the shared `ci-check.yml` switchboard. They require environment-scoped Vercel credentials plus `VERCEL_TEACH_PROJECT_ID`; production Supabase values should live in the Vercel project environment rather than GitHub Actions.

Deployment credentials must stay environment-scoped in GitHub Actions. The preview job is bound to the `vercel-preview-teach` GitHub Environment, and the production job is bound to `vercel-production-teach`. Store `VERCEL_TOKEN`, `VERCEL_ORG_ID`, and `VERCEL_TEACH_PROJECT_ID` in those environments instead of repository-wide or organization-wide secrets. Production Supabase values remain in the Vercel project environment pulled by `vercel pull`. The repository-level `TURBO_TOKEN` and `TURBO_TEAM` variable are passed only to the wrapped `vercel build` step; never place them at workflow or job scope, and never expose the token to pull-request or Dependabot code.

Preview dispatch is manual-only. Run `vercel-preview-teach.yaml` from `main`, set `preview_ref` to the reviewed branch, tag, or SHA, and keep `TRUSTED_PREVIEW_DEPLOY_ACTORS` limited to maintainers approved to run secret-backed preview builds. Manual production dispatch is only valid from `refs/heads/production`.
