# Technical documentation — magalies-pharma-heisenberg-frontend

Evidence date: 2026-10-09. Source: `master` at `f153f4f` (`f153f4f5d4807c85e49c01a5e5a77c594eff0cc3`).

Credential values from `src/environments/*.ts` are not reproduced here. The files themselves are in git.

## Stack

| Item | Value | Evidence |
|---|---|---|
| Language | TypeScript ~4.3.5 | `package.json` |
| UI | Angular ~12.2.0, Angular Material ^12.2.13, Bootstrap ^5.1.3, SCSS components | `package.json`, `angular.json` (`inlineStyleLanguage: scss`) |
| Auth | `aws-amplify` ^4.3.27, `@aws-amplify/ui-angular` ^2.4.14 | `package.json`, `app.component.ts` |
| HTTP | `@angular/common/http`. `axios` is a dependency; the services read use `HttpClient` | services under `src/core/services/` |
| Charts | `@swimlane/ngx-charts` ^20.1.0 | dashboard |
| PDF | `jspdf` ^3.0.1, `jspdf-autotable` ^5.0.2 | prescription review |
| Spreadsheet | `exceljs` ^4.3.0, `file-saver` ^2.0.5 | dashboard |
| Rich text | `ngx-quill` 15, `quill` ^1.3.7 | `QuillModule.forRoot()` in `core.module.ts` |
| Addresses | `@what3words/api` ^4.0.5 | `shipping.service.ts` |
| Node for CI build | 14 | all `buildspec*.yml` |
| Package name | `magalies-pharma` `0.0.0` private | `package.json` |

`angular.json` project name is `magalies-pharma`. Output path `dist/magalies-pharma`. Default build configuration is `production`. Default serve configuration is `development`. Prefix `app`. Strict application schematic is on; component style is scss.

## Repository layout

```text
angular.json
buildspec.yml            production-style build → s3://heisenberg-frontend-prod
buildspec-prod.yml       ng build --prod → s3://heisenberg-frontend-prod-overdrive
buildspec-dev.yml        development build → s3://sil-heisenberg-frontend-dev
cypress/                 two integration specs and page objects
karma.conf.js
package.json
puppeteer-test.js        npm test
README.md
src/
  main.ts                bootstraps AppModule; enableProdMode only if environment.production
  index.html             title Magaliesberg Pharmaceuticals
  app/                   AppModule, routing, unused PrescriptionModule, stub order-medicine list, thanks
  core/                  components, services, guards, one directive, interceptors
  form/                  client form (routed) and public form (not routed)
  environments/          environment.ts and environment.prod.ts
  enums/                 EntityType, ForWho, DropdownOptions
  interfaces/
  material/              Angular Material module
  resolvers/
  assets/
test/                    Angular test.ts bootstrap (Karma)
```

661 commits on `master` only. No tags. Authors are summarised in `project-plan.md`.

`sonar-project.properties` and `sonarqube_scan.bat` were named when OVER-189 was created. They are not in this clone.

Compiled `dist` JavaScript is committed under parts of `src/` (for example `src/enums/dist`, `src/core/interceptors/dist`). The CLI build compiles TypeScript. Those JS files are not the deploy artifact.

## Application structure

`AppModule` imports `BrowserModule`, `AppRoutingModule`, `BrowserAnimationsModule`, `MaterialModule`, `CoreModule`, `FormModule`. It declares `AppComponent`, `ThanksComponent` (`src/app/thanks`), and a stub `OrderMedicineListComponent` (`src/app/core/components/order-medicine-list`).

`CoreModule` declares the portal, lists, dialogs, and the real order-medicine components, and provides `LoaderInterceptor` and `BasicAuthInterceptor`.

`PrescriptionModule` (`src/app/prescription/prescription.module.ts`) is not imported.

Routing is a single `RouterModule.forRoot` table in `src/app/app-routing.module.ts`. Child screens of `portal` use resolvers (`OrderResolver`, `OrdersResolver`, `BatchResolver`, `BatchesResolver`, `UserResolver`, `UsersResolver`, `IndividualResolver`, `IndividualsResolver`, `MedicineResolver`, `MedicineListResolver`, `CompanyResolver`, `CompanyListResolver`, `LogsResolver`, `DashboardResolver`).

`AppComponent` calls `Amplify.configure({ Auth: environment.auth })` at load.

There is no service worker, no Angular Universal, and no queue or worker process in this repository. Background work is whatever the API does after a POST.

## Authentication

Cognito settings live under `environment.auth`: `identityPoolId`, `region` (`eu-west-1`), `userPoolId`, `userPoolWebClientId`, `mandatorySignIn: true`. Dev and prod files carry different pool and client ids.

`AuthService`:

- Listens to Amplify Hub `auth`.
- On `signIn`, stores the user, copies `cognito:groups` into `currentRoles`, and reloads the window.
- `authenticateUser` uses `Auth.currentAuthenticatedUser({ bypassCache: true })`. The string error `The user is not authenticated` returns true.
- `generateRequestHeaders` and `BasicAuthInterceptor` send `accesstoken` (access token JWT) and `idtoken` (id token JWT). Header names are those lowercase strings, not `Authorization`.
- Sign-out is `Auth.signOut({ global: true })`.

`RoleAuthorisationGuard` is documented in the PRD. It is not an effective child-route gate.

`authGroup` structural directive shows a template when any current role is in the input array.

Password strength helper: `src/core/validators/password.ts` (upper, lower, digit, special). Not bound to the create-user form.

## HTTP

Interceptors registered in `core.module.ts`:

| Class | Behaviour |
|---|---|
| `LoaderInterceptor` | Calls `LoaderService.show()` before the request and `hide()` in `finalize` |
| `BasicAuthInterceptor` | Clones every `HttpClient` request with Cognito headers, including calls to `assets/...` |

`WaybillAuthInterceptor` is not registered.

`ApiHelperService.buildQueryParams` joins parameters with `&&` (two ampersands) and supports an `exists` type that appends the name with no value. Callers that use it send that query string.

Services construct URLs as `` `${environment.api.baseUrl}/...` ``. One medicine call omits the slash: `` `${this.baseUrl}medicine/${medicineId}/dosage-type/${id}/activate` `` in `medicine.service.ts` (activate). **Implemented.** A base URL with no trailing slash would produce `...amazonaws.com/devmedicine/...`.

### API paths this UI calls

Base URL dev: `https://f420rd8gih.execute-api.eu-west-1.amazonaws.com/dev`.

Base URL prod file: `https://api.magaliespharma.com`.

| Method | Path | Service |
|---|---|---|
| POST, GET, PUT, DELETE | `/user`, `/user/{username}` | `user.service.ts` |
| GET | `/user/{username}/enable`, `/user/{name}/reset-password` | `user.service.ts` |
| POST | `/form-data` | user, shared, prescription |
| GET, POST, PUT, DELETE | `/individual`, `/individual/{id}` | `individual.service.ts` |
| GET, POST, PUT | `/company`, `/company/{id}` | `company.service.ts` (no delete method was seen) |
| GET, POST, PUT, DELETE | `/medicine` and nested `/dosage-type`, `/package` | `medicine.service.ts` |
| GET, POST, PUT, DELETE | `/dosage-unit`, `/dosage-type`, `/package-type` | `medicine.service.ts` |
| GET, POST, PUT, DELETE | `/ordermedicine` | `order-medicine.service.ts` |
| GET, POST | `/order`, `/order/{id}`, `/order/{id}/split` | `prescription.service.ts` |
| GET | `/order/{id}/shipped` | `shipping.service.ts` |
| POST | `/report/order`, `/report/shipped`, `/report/medicine`, `/report/batch` | prescription, dashboard, batch |
| POST | `/dashboard` | `dashboard.service.ts` |
| POST | `/prescription-pack` | `prescription.service.ts` |
| POST, PUT, GET | `/batch`, `/batch/{id}`, `/batch/number/{batchNumber}`, `/batch/by-package`, `/batch/{id}/link-order-item` | `batch.service.ts` |
| GET, POST | `/fulfilment/{orderItemId}` and `/scan`, `/fulfill` | `batch.service.ts` |
| POST | `/stocktake/count/`, `/stocktakebarcode/scan/`, `/stocktake/check/`, `/stocktake/update/` | `stocktake.service.ts` |
| POST | `/stocktake/report/difference`, `/stocktake/report/scanned` | stocktake and dashboard |
| GET | `/log` | `log.service.ts` |
| POST | `/presigned-url/generate-upload`, `/presigned-url/generate-download` | `presigned-url.service.ts` |
| POST | `/otp/by-order` | `otp.service.ts` |
| POST | `/waybill/auth`, `/waybill/capture`, `/parcel/capture`, `/waybill/complete` | `shipping.service.ts` |
| GET | `/waybill/generate`, `/waybillorder/{waybillNumber}` | `shipping.service.ts` |

Pagination query names used in the services that were read: `page`, `limit`, `search`, and for orders `order_status`. User list uses a `paginationToken` field on the service (`_paginationToken`).

`GET assets/tracking.json` is same-origin, not the API. `GET assets/swahilli.txt` is the terms body.

## S3

| | Dev file | Prod file |
|---|---|---|
| Region | `eu-west-1` | `eu-west-1` |
| Bucket | `heisenberg-prescriptions-dev` | `heisenberg-prescriptions-prod` |
| Folder | `prescriptions` | `prescriptions` |
| File name | `prescription.pdf` | `prescription.pdf` |

The browser asks the API for a presigned URL. This repo does not upload with the AWS SDK directly.

## Print server

`environment.api.printServerBaseUrl`:

- Dev: `https://10.10.0.96` (no port in the active string). Comments mention `http://10.10.0.17:3000` and `http://10.10.0.109:3000`.
- Prod: `http://printserver:3000`. Comments mention other `10.10.0.*:3000` hosts.

Calls:

- `POST {base}/label/` and `POST {base}/barcode/` from `batch.service.ts` (no auth headers on those posts).
- `POST {base}/deliverylabel/` from `shipping.service.ts`.

The Silicon Ark print-server document describes `POST /barcode` and `POST /label` on port 3000, without a trailing slash and without `/deliverylabel`. **Requires Confirmation.**

## Waybill and what3words

`environment.waybill` holds Dawn Wing UAT `baseUrl`, literal `userName` / `password` strings `username` and `password`, and `magaliesConfig` sender fields. Identical in dev and prod files.

`environment.what3words` points at `https://api.what3words.com` version `v3`. The key is in git and is omitted here.

`ShippingService.getToken` posts those credentials to `{api}/waybill/auth` and parses `JSON.parse(data)` for `token`.

## Build and deploy

Local, from `README.md`:

```text
set NODE_OPTIONS=--openssl-legacy-provider
ng serve
```

Dev server URL in the README: `http://localhost:4200/`. Cypress `baseUrl` is the same (`cypress.json`).

`npm` scripts: `start` → `ng serve`, `build` → `ng build`, `test` → `node puppeteer-test.js`, `e2e` → `ng e2e`, `cypress:open`, `cypress:run`.

`angular.json` production configuration replaces `environment.ts` with `environment.prod.ts` and hashes output. Development configuration turns optimization off and does not replace the environment file.

`environment.prod.ts` sets `production: false`. `main.ts` therefore does not call `enableProdMode()` for that configuration. **Implemented.**

| File | Build command | S3 bucket |
|---|---|---|
| `buildspec.yml` | `ng build --configuration production --aot` | `heisenberg-frontend-prod` |
| `buildspec-prod.yml` | `ng build --prod --aot` | `heisenberg-frontend-prod-overdrive` |
| `buildspec-dev.yml` | `ng build --configuration development` | `sil-heisenberg-frontend-dev` |

Each spec uses CodeBuild runtime Node 14, `npm install`, installs AWS CLI via pip, and `aws s3 sync` of `dist/magalies-pharma`. No CloudFront invalidation, no cache-control flags, and no pipeline YAML in the repo. Which spec AWS actually runs is **Unknown / Requires Confirmation**.

`buildspec-prod.yml` uses `--prod`, which Angular CLI 12 still accepted as an alias. `buildspec.yml` uses `--configuration production`. They also target different buckets.

A production-configuration build still uses Dawn Wing UAT settings, because those values are inside `environment.prod.ts`.

## Tests

| Kind | What exists |
|---|---|
| Karma | `angular.json` target `test`, `karma.conf.js`, `src/test.ts`, many `*.spec.ts` files that only create the component (generated spec stubs were seen) |
| Cypress | `cypress/integration/client-submission.ts` logs in with `cy.loginByCognitoApi` and visits `/portal/submit`. `public-submission.ts` visits `/submit` and expects `/thanks`. Support command is `cypress/support/auth-provider-commands/cognito.ts` |
| Puppeteer | `npm test` runs `puppeteer-test.js`, which boots Karma through `@angular-devkit/build-angular` and drives a page. `puppeteer` and `mkdirp` are required and are not listed in `package.json` |
| Fixtures | Cypress intercepts refer to `cypress/fixtures` (`form-data.json`, `companies.json`, `medicine.json`, and others named in the page objects) |

`package.json` `test` does not run `ng test`. The README still says `ng test` uses Karma.

No test run was executed for this documentation pass.

## Security notes (observed, not a penetration test)

- Cognito identifiers, a what3words key, Dawn Wing account numbers, phone numbers, and the strings `username` / `password` are committed in both environment files. They ship to every browser that loads the bundle.
- `BasicAuthInterceptor` attaches JWTs to all `HttpClient` traffic, including local asset GETs.
- Print-server posts carry no token.
- `/waybill/review/:waybill` and `/tandcs` do not sit inside the authenticator. Anyone who can open the SPA can request waybill order rows for a waybill number they know. The API may still reject the call.
- `RoleAuthorisationGuard` does not enforce the role map on child routes.
- Sign-up is hidden in the UI. That does not by itself disable sign-up on the Cognito app client.
- `index.html` has no Content-Security-Policy meta tag.

## Configuration flags worth a review

| Flag | Observation |
|---|---|
| `environment.prod.ts` `production: false` | Production bundle is not Angular production mode |
| Dawn Wing URL contains `/uat/` in the prod file | Prod build is pointed at UAT data |
| `printServerBaseUrl` dev has no port; prod uses hostname `printserver` | Dev and prod print targets differ in scheme, host, and port |
| `Fulfilmennt` | Typo in two side-nav allow-lists |
| Activate dosage URL missing `/` | `medicine.service.ts` activate path |
| Query joiner `&&` | `ApiHelperService` |
| `handleForgotPassword(formData.toString())` | Likely not an email string. **Unknown** until exercised |

## Processes not present

No database, ORM, migration, Redis, SQS, cron, or monitoring agent is in this repository. Logging the user sees is `GET /log` rendered by `LogListComponent`, plus browser `console.error` in `AuthService`.

No Docker file and no `.env` example. Configuration is the two TypeScript environment files, swapped by the Angular build.
