Monorepo for the Skill Scale Up learning management system front end. Two Next.js 16 apps (student, admin) share internal packages for UI, API, types, validation, and TanStack Query hooks. Trainers/tutors sign in through admin with a restricted role. Each app uses the App Router (src/app); screen modules live under src/views to avoid colliding with the legacy Pages Router.
- Node.js >= 24.14.0 (see root
package.jsonengines; use.nvmrc/.node-versionfor 24.14.0) - Package manager: npm workspaces (
package-lock.jsonat the repo root).
Each app uses public/firstlogo.png for the Chrome tab icon and Open Graph previews (title, description, logo when you share a link).
Set the public origin in .env.local so share URLs resolve correctly:
| App | NEXT_PUBLIC_SITE_URL (local) |
|---|---|
| Student | http://localhost:5173 |
| Admin | http://localhost:5175 |
Use your deployed HTTPS URL in production (e.g. https://student.skillscaleup.org).
For the student app, copy env vars into apps/student/.env.local (Next.js reads env from the app folder, not the monorepo root).
Signup uses POST /api/v1/students/auth/signup via the Next.js BFF at /api/auth/signup.
- Programs load from
GET /api/v1/programs/available(dropdown shows API titles and fees). - The
programfield must be a program id from that list (not a display label). - Phone numbers must be Nigerian format like
08012345678(+234is normalized on the server).
Set NEXT_PUBLIC_API_URL=https://base-api.skillscaleup.org in apps/student/.env.local.
From the repository root:
npm installDependencies are managed with npm workspaces (apps/*, packages/*).
Each app is a Next.js workspace with its own dev port. Run them from the repo root:
| App | Command | URL |
|---|---|---|
| Student | npm run dev or npm run dev:student |
http://localhost:5173 |
| Admin | npm run dev:admin |
http://localhost:5175 |
Use one terminal per app if you want both open at once.
The demo API in @ssu/api accepts any non-empty password for known emails. Use the account that matches the app (each app checks role):
| Role | Use in app | |
|---|---|---|
| Student | student@skillscaleup.dev |
Student |
| Tutor (trainer) | trainer@skillscaleup.dev |
Admin (restricted tutor role) |
| Admin | admin@skillscaleup.dev |
Admin |
There is also pending@skillscaleup.dev (tutor, pending approval) for testing pending trainer flows in the admin app.
This monorepo uses Turborepo to run build, lint, typecheck, and test across apps/* and packages/* in parallel with caching.
| Script | What it does |
|---|---|
npm run build |
next build for student and admin (parallel) |
npm run build:student |
Build only @ssu/student |
npm run build:admin |
Build only @ssu/admin |
npm run build:qa |
Admin + student (staging QA) |
npm run lint |
Per-package lint via Turbo + root oxlint |
npm run typecheck |
tsc --noEmit in all workspaces that define typecheck |
npm run test |
Tests in workspaces that define test |
npm run verify |
Lint + typecheck + test |
Config lives in turbo.json. Cache output is under .turbo (gitignored).
Dev servers are still one app at a time: npm run dev, npm run dev:admin, etc. (Turbo dev is persistent and not used from the root script.)
| Script | Description |
|---|---|
npm run storybook |
Storybook for the shared @ssu/ui package |
apps/student,apps/admin: role-specific Next.js apps (App Router, shared layout +RequireAuth, Jest tests). Trainers use admin with a restricted sidebar.packages/ui: shared design system and layouts.packages/api: client-side mock API and demo auth (localStoragesession).packages/queries: React Query hooks over the API layer.packages/schema,packages/types,packages/utils,packages/config: Zod schemas, shared types, helpers, Tailwind/TS config.
For full product and UX requirements, see instruction.md in this repository.
| Location | What lives there |
|---|---|
packages/config |
Shared Tailwind, site-metadata, env.ts (getSiteUrl), route constants |
apps/student |
netlify.toml, .env.example, src/config/routes.ts |
apps/admin |
netlify.toml, .env.example, src/config/routes.ts |
Repo root .env.example |
Pointer only — Next reads env from each app folder |
Copy apps/<portal>/.env.example to apps/<portal>/.env.local for local dev.
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_STAGING_URL |
Staging host for that portal |
NEXT_PUBLIC_PRODUCTION_URL |
Production host on skillscaleup.org |
NEXT_PUBLIC_APP_ENV |
local | staging | production |
NEXT_PUBLIC_SITE_URL |
Local dev override (port per app) |
Use @/config/routes inside an app, or @ssu/config/routes from packages. Next.js src/app/** remains the real router; route constants are for links and redirects only.
Both sites deploy from branch dev using the same root netlify.toml. Site names use prefixes: ssuhub* (student, e.g. ssuhubstg) and ssuadmin* (admin, e.g. ssuadmin).
Auto-detection uses SITE_NAME / URL (ssuhub* → student, ssuadmin* → admin). You can also set explicitly:
| Site | Variable | Value |
|---|---|---|
| Student | NETLIFY_APP |
student |
| Admin | NETLIFY_APP |
admin |
Scope: All scopes (or at least Builds). Redeploy after adding.
Leave Base directory and Package directory empty. Set Publish directory manually:
| Setting | Student site | Admin site |
|---|---|---|
| Production branch | dev |
dev |
| Base directory | (empty) | (empty) |
| Package directory | (empty) | (empty) |
| Build command | (empty — from toml) | (empty — from toml) |
| Publish directory | apps/student/.next |
apps/admin/.next |
| Functions directory | (empty) | (empty) |
| Runtime | Not set | Not set |
Config: root netlify.toml + scripts/netlify-build.sh
- Root
netlify.tomlrunsnpm ci && bash scripts/netlify-build.sh - The script reads
NETLIFY_APPand builds@ssu/studentor@ssu/admin - Each site's Publish directory (in UI) tells the Next.js plugin which
.nextfolder to deploy
- Base or Package set to
apps/student— leave both empty. - Missing
NETLIFY_APP— build fails or builds the wrong app. - Publish =
apps/student/(source folder) — must beapps/student/.next. - Runtime: Next.js in UI — remove it; plugin is in
netlify.toml.
This usually means npm run dev is still running for the student app, or a stale lock was left behind.
- Stop the dev server (Ctrl+C in that terminal).
- Remove the lock if needed:
rm -f apps/student/.next/dev/lock
npm run buildDo not run npm run dev and npm run build for the same app at the same time.
For QA deploy (student + admin only, sequential):
npm run build:qaFonts load in the browser at runtime (not during next build). Users need network access when viewing the app for Plus Jakarta Sans; system fonts are used until the stylesheet loads.