# Project plan — ClickLearnerPortal

Evidence date: 2026-10-19.

Source repository: `git@bitbucket.org:overdrive/clicklearnerportal.git`.

Inspected revision: `master` at `2cb0ae55b32459d58471c6b571372ad8ae978a1b` (2023-02-07 16:50:41 +0200, author Dirk `<dirk@overdrive.co.za>`, message "Initial commit"). That commit is the only commit. `origin/HEAD` is `master`. No other branches and no tags were present.

The scan-host path named in the onboarding task (`~/projects/sonar_scan/repos/ClickLearnerPortal`) was not on this machine. Evidence comes from a read-only clone of `origin/master`.

`silicon_ark/ClickLearnerPortal/` did not exist before this set. The source repository has no product brief. `ClickLearnerPortal/ClientApp/README.md` is the Create React App boilerplate, not a project description.

This plan describes what the repository contains. It does not decide what the product should become.

## Product

The browser title, the login page title, and the web-app manifest name the product **Click Foundation Learner Portal** (`ClientApp/public/index.html`, `manifest.json`, `pages/learner/LearnerLoginPage.js`).

What the running UI does is let a person sign in with a username and password, then send the browser to a URL returned by the database. A second page, `/click-home-learning`, lists external programme logins (Reading Eggs, Matific, Reflective Learning) and, when the query string says a test is finished, posts a quiz-completion notice to this app's API. Other HTTP endpoints accept Mobenzi form traffic, an iTouch SMS callback, and a quiz learner-name lookup. Those callers are named in comments, not in a requirements document.

Provenance: **Implemented** (titles, screens, controllers). A written statement of organisational purpose beyond the sentences on the login page is **Unknown / Requires Confirmation**. The login page itself says the portal is for registered learners, mentions the Reading Eggs password used at school, says unregistered visitors can open material, and says the content is for children aged 5–14. That copy is **Documented** only as UI text in `LearnerLoginPage.js`.

## Branch reality

`origin/HEAD` is `master`. History is the single commit above. No release tags.

The task-creation scan listed `sonar-project.properties` and `sonarqube_scan.bat` at the repository root. Neither file is in `2cb0ae5`. Provenance: **Unknown** — they may have been untracked files on the scan host. The same gap is recorded for `silicon_ark/ClickPortal/` and `silicon_ark/click-learner-portal-platform/`.

## Objectives (inferred from the application, not from a written plan)

No project plan or requirements document exists in the repository. The following are **Inferred** from the screens and API:

- Let a learner sign in with a username and password and leave this site for a programme URL stored in the database.
- When that programme code is `QUIZ`, build a longer URL and record that the learner started the quiz.
- Offer a page of links to Reading Eggs, Matific, and Reflective Learning, plus a how-to PDF and a contact address.
- Accept a return from an external test and record that the quiz form was submitted when the message is `Test is finished`.
- Let a Mobenzi form look up an ID number and post a form submission into the database.
- Let an SMS provider (a comment names iTouch) post delivery notifications and replies.
- Let a caller with a shared query secret read a learner's first and last name.

## Scope

### In this repository

- One Visual Studio 2019 solution, `ClickLearnerPortal.sln`, with one ASP.NET Core 3.1 web project.
- A React 16 client under `ClickLearnerPortal/ClientApp`, served by the ASP.NET Core SPA host.
- Dapper calls against PostgreSQL (`Npgsql`) using routines in a `portal` schema. The routine bodies are not in this repository.
- Anonymous learner login, programme read, quiz name lookup, TAO form callback, Mobenzi form endpoints, and an SMS callback.
- JWT bearer authentication is registered. No live action requires a token, and no live action issues one.

### Not in this repository

- Stored-procedure and table definitions for `ClickDW` / `ClickSTAGING`.
- The LTI platform that launches TAO. That application is a different repository, documented in `silicon_ark/click-learner-portal-platform/`.
- The staff support portal. That application is `clickportal.git`, documented in `silicon_ark/ClickPortal/`.
- A deployment pipeline, a publish profile, a Dockerfile, or infrastructure-as-code.
- Tests that exercise the API. The only test file does not match the component's export (see technical-documentation.md).

## Architecture (as implemented)

```text
Browser
  ├─ React 16 SPA (ClientApp)
  │    ├─ / and /learner/login     LearnerLoginPage
  │    └─ /click-home-learning     ClickHomeLearningPage
  └─ ASP.NET Core 3.1 (Kestrel in the project profile; IIS settings also present)
       ├─ Controllers → Repositories (Dapper)
       ├─ DWSqlConnection      → connection "DefaultConnection"  → database name ClickDW
       └─ StagingSqlConnection → connection "StagingConnection"  → database name ClickSTAGING
```

Both connection wrappers are `NpgsqlConnection`. Comments in those files say the SQL Server adapter was removed. `Microsoft.Data.SqlClient` is still a package reference. `ProgrammeRepository.cs` still imports it and does not use it.

JWT issuer configured in `appsettings.json` is `learn.clicklearning.org`. Serilog's file sink path is `c:/websites/learnerportal/logs/log.txt`. Whether that host and that folder are the live deployment is **Unknown / Requires Confirmation**.

## Major components

| Component | Path | Role |
|---|---|---|
| Host | `Program.cs`, `Startup.cs` | Serilog, DI, JWT, CORS, SPA, MVC |
| Learner API | `Controllers/LearnerController.cs` | Username/password check and redirect |
| Programme API | `Controllers/ProgrammeController.cs` | Read one programme by integer id |
| Quiz API | `Controllers/QuizController.cs` | Learner name for a shared-secret caller |
| TAO API | `Controllers/TAOController.cs` | Quiz form submission callback |
| Mobenzi API | `Controllers/MobenziController.cs` | ID lookup HTML and form POST |
| SMS API | `Controllers/NotificationController.cs` | iTouch-shaped notification and reply |
| Data | `Data/DWSqlConnection.cs`, `Data/StagingSqlConnection.cs` | Npgsql wrappers |
| Login UI | `ClientApp/src/pages/learner/LearnerLoginPage.js` | Sign-in form and open-material link |
| Home learning UI | `ClientApp/src/pages/ClickHomeLearningPage.js` | Programme links and quiz-finished POST |
| Account API | `Controllers/AccountController.cs` | Entire file is a block comment. Not a route |

## Milestones

No milestone list, version plan, or roadmap is in the repository. `ClientApp/package.json` version is `0.2.0`. That number is not tied to a changelog.

The only dated event in git is the initial commit on 2023-02-07. Current status: **single snapshot, no later commits on `origin/master`**. Whether a newer copy is deployed outside this remote is **Unknown**.

## Implementation plan

There is no implementation plan in the repository. Nothing in the tree describes remaining work. Empty project folders (`Services`, `Extensions`) and a `dotnet-ef` tool manifest exist without Entity Framework types or migrations. That is leftover structure, not a written plan.

## Known dependencies

Runtime and libraries are listed in technical-documentation.md. External systems the code names or calls:

| System | How this repo touches it | Provenance |
|---|---|---|
| PostgreSQL databases `ClickDW` and `ClickSTAGING` | Connection strings in `appsettings.json`. Hosts are AWS RDS names in `af-south-1`. User id written there is `postgres`. Port `5432`. | **Implemented** config. Live reachability **Unknown** |
| Reading Eggs | Hard-coded SSO login URL on the home-learning page. Login copy also names Reading Eggs. | **Implemented** link and copy |
| Matific | Hard-coded `https://www.matific.com/za/en-za/login-page/` | **Implemented** link |
| Reflective Learning | Hard-coded `https://app.reflectivelearning.co.za/auth/login` | **Implemented** link |
| South African COVID-19 site | Banner link `https://www.sacoronavirus.co.za` | **Implemented** link |
| Click Foundation mail | `info@clickfoundation.co.za` | **Implemented** mailto |
| Google Analytics | Measurement id `UA-162415700-1` in `ClientApp/.env` | **Implemented** config |
| Intercom | Widget app id `nszrac5k` in `index.html`, gated by an env flag | **Implemented** |
| Font Awesome kit | `https://kit.fontawesome.com/37337f48a1.js` | **Implemented** |
| Mobenzi | Comments on `MobenziController` and `QuizController` | Comment only. Caller **Requires Confirmation** |
| iTouch | Comment on `NotificationController.SMS` | Comment only. Caller **Requires Confirmation** |
| TAO | Route name, repository name, and the home-learning return handler | **Implemented** as this app's callback. The test runner itself is not in this repo |

Passwords, the JWT signing key, and the shared query secret are committed in source. They are not copied into this document. Treat them as exposed.

## Related Silicon Ark sets

| Set | Repository | What overlaps | Status of the link |
|---|---|---|---|
| `silicon_ark/click-learner-portal-platform/` | `click-learner-portal-platform.git` | Production branch return URL is `https://learn.clicklearning.org/click-home-learning?learner_id=…&quiz_id=…`. This repo serves `/click-home-learning` and reads those query names. A `QUIZ` login redirect is `{stored URL}/{learner id}/{source quiz id}/{end date}`, which matches that platform's `/default/{learnerId}/{quizId}/{quizEndDate}` shape if the stored URL ends in `/default`. | **Inferred**. This repo does not name `clms.clicklearning.org` or the platform repository. |
| `silicon_ark/ClickPortal/` | `clickportal.git` | Same author, initial commit the same afternoon (17:00 +0200). Both talk to databases named `ClickDW` and `ClickSTAGING` and both have learner login, programme get, and quiz learner-detail. ClickPortal's documented data access is `SqlClient`. This repo's wrappers are `NpgsqlConnection`, and several procedure names were renamed (`portal.puserbyusername` with `par_*` arguments). ClickPortal's learner login is described there as programme `RE`. This repo passes a null programme code. | Separate repositories. Whether they share a server **Requires Confirmation**. Do not treat ClickPortal's procedure list as the contract for this repo. |

## Current project status

- Code: one commit on `master`, 2023-02-07.
- Documentation in the source repo: Create React App README only.
- Documentation in Silicon Ark: this set.
- Pipeline and hosting: not in git. IIS rewrite transform `web.production.config` is present and is not referenced by a publish profile in git.

## Human confirmation

- Whether `learn.clicklearning.org` is the host that serves this build.
- Whether `c:/websites/learnerportal` is the deploy directory, and how the site is published (no pipeline in git).
- Whether the committed database passwords, JWT key, and shared query secret are still valid, and whether they should be rotated. They are in git history.
- Whether the PostgreSQL routines exist with the names and argument names this code sends. The names are inconsistent inside this repo (see technical-documentation.md).
- What programme id `999` is. The login page loads it for "Access Open Material".
- Whether a `QUIZ` redirect is meant to hit the LTI platform in `click-learner-portal-platform`.
- Whether Mobenzi and iTouch are still the callers of those endpoints.
- Whether IIS Windows authentication from `launchSettings.json` is applied in any environment. If it is, anonymous login and the webhooks would be blocked. The checked-in `web.config` does not set authentication.
- Whether `web.production.config` is applied. Its `/support` rules do not match routes in this app, and the rule named for `/account/login` matches the URL `support`.
- Whether plaintext password comparison, and writing the password into the Serilog scope, are known.
- The sonar files named at task creation. They are not in `2cb0ae5`.
