# Technical documentation — ohlief-app-client

Evidence date: 2026-10-08. Source: `git@bitbucket.org:overdrive/ohlief-app-client.git`, `master` `98891e4` (2020-07-05, Jaco Brits).

AWS identifiers below are copied from `src/config.js` because that file is the client's only configuration. They are not secrets added by this document. No passwords are stored in the repository.

## What this repository is

A Create React App (`react-scripts` 3.4.0) named `ohlief-app-client` version `0.1.0`. The page title is **Oh-lief EDI**. Custom screens live under `src/containers`. Data calls go through AWS Amplify to Cognito, API Gateway, and S3. There is no server in this repository.

`README.md` is the upstream CRA readme (`npm start`, `npm test`, `npm run build`, `npm run eject`). It does not describe Oh-lief. `public/index.html` meta description still says "A simple note taking app".

## Repository facts

| Item | Value |
|---|---|
| Remote | `git@bitbucket.org:overdrive/ohlief-app-client.git` |
| Default branch | `master` |
| HEAD | `98891e4` (2020-07-05, Jaco Brits, "Update the major screen product screen") |
| First commit | `c214a93` (2020-02-16, "React base") |
| Commit count | 57 |
| Authors | Jaco Brits (39), Alex Kedian (18) |
| Other branches | none on this clone after a full history fetch |

The task's scan listing included `sonar-project.properties` and `sonarqube_scan.bat`. They are not in the git tree at HEAD.

The scan-host path `~/projects/sonar_scan/repos/ohlief-app-client` was not on the machine that produced this document. Evidence is the git tree above.

## Application structure

```text
public/                 index.html, manifest, logo, favicons, robots.txt
src/index.js            mounts Router + App, then Amplify.configure, unregisters the service worker
src/App.js              session flag, navbar, logout
src/Routes.js           route table
src/config.js           region, S3 bucket, API URL, Cognito ids
src/components/         AppliedRoute, AuthenticatedRoute, UnauthenticatedRoute, LoaderButton
src/containers/         Home, Login, Signup, order/invoice/product screens, leftover note screens
src/libs/awsLib.js      S3 vault upload
src/libs/commonLib.js   thousand separators
src/libs/hooksLib.js    useFormFields
src/App.test.js         CRA sample test (looks for "learn react")
src/serviceWorker.js    CRA helper; unregister() is what index.js calls
src/setupTests.js       jest-dom
```

There is no `src/setupProxy.js`, no environment file, and no `homepage` field in `package.json`. The app expects to be served at the site root (`BrowserRouter` with no `basename`).

## Frameworks and languages

| Piece | Version in `package.json` |
|---|---|
| JavaScript (no TypeScript) | — |
| React / React DOM | `^16.12.0` |
| react-scripts | `3.4.0` (exact) |
| aws-amplify | `^2.2.5` |
| react-router-dom | `^5.0.1` |
| react-bootstrap | `^0.33.0` (Bootstrap 3 markup; Bootstrap 3.3.7 CSS is linked from MaxCDN in `index.html`) |
| react-bootstrap-table-next | `^4.0.0` |
| react-bootstrap-table2-filter | `^1.3.1` |
| react-bootstrap-table2-paginator | `^2.1.2` |
| react-bootstrap-table2-toolkit | `^2.1.2` |
| react-router-bootstrap | `^0.25.0` |
| Testing Library | jest-dom 4, react 9, user-event 7 |

`index.html` also loads Google fonts PT Serif and Open Sans. `react-bootstrap-table-next` CSS is imported from `App.js`.

Bootstrap and the font CSS are loaded from public CDNs. A build does not vendor them.

## Routing

React Router 5 `Switch`, first match wins. `exact` is set on every named route.

| Path | Gate | Component file | Export name |
|---|---|---|---|
| `/` | none | `Home.js` | `Home` |
| `/login` | guest only | `Login.js` | `Login` |
| `/signup` | session required | `Signup.js` | `Signup` |
| `/ohlief/new` | session | `NewNote.js` | `NewNote` |
| `/ohlief/newproduct` | session | `NewProduct.js` | `NewOrder` |
| `/ohlief/:id` | session | `Notes.js` | `Notes` |
| `/ohlief/old` | session | `Processed.js` | `Processed` |
| `/ohlief/orders/:id` | session | `OrderDetail.js` | `OrderDetail` |
| `/ohlief/invoices/:id` | session | `InvoiceDetail.js` | `OrderDetail` |
| `/ohlief/products/:id` | session | `ProductDetail.js` | `InvoiceDetail` |
| anything else | none | `NotFound.js` | `NotFound` |

`/ohlief/old` is registered after `/ohlief/:id`. A path with one segment matches `:id` first, so `Processed` does not render. Paths with two segments (`orders`, `invoices`, `products`) do not match `/ohlief/:id` because of `exact`.

`Orders.js` and `NewOrder.js` are not imported.

## Auth

`src/index.js` configures Amplify Auth:

| Key | Value |
|---|---|
| `mandatorySignIn` | `true` |
| region | `eu-west-1` |
| user pool | `eu-west-1_rXTL9gyYP` |
| app client | `5r1hmv1885cv8tc1hdnrr46388` |
| identity pool | `eu-west-1:aeacda5a-adf0-4561-8696-7cede4d5d6f7` |

`mandatorySignIn` does not hide the lander. The lander is local React state.

Session check: `Auth.currentSession()` in `App`. Sign-in: `Auth.signIn`. Sign-up: `Auth.signUp({ username: email, password })` plus an unused `Auth.confirmSignUp`. Sign-out: `Auth.signOut`.

No refresh handling is written in the app code. Amplify's own session behaviour applies. **Unknown** which Cognito app-client settings (hosted UI, MFA, password policy) are turned on in AWS. They are not in this repo.

## API

Amplify API endpoint name `ohlief`:

```text
https://0tsz4u0k70.execute-api.eu-west-1.amazonaws.com/prod
```

Region `eu-west-1`. No `custom_header` and no explicit authorizer configuration in the client. Amplify attaches the Cognito session when the user is signed in.

| Method | Path | Caller |
|---|---|---|
| GET | `/ohlief` | `Home.js`, `Processed.js` |
| GET | `/listinvoices` | `Home.js` |
| GET | `/listProducts` | `Home.js` |
| GET | `/invoiceupdate/{orderNumber}` | `Home.js` |
| GET | `/ohlief/{id}` | `OrderDetail.js`, `InvoiceDetail.js`, `Notes.js` (call commented out), `Orders.js` (not routed) |
| PUT | `/ohlief/{id}` | `OrderDetail.js` body `{ content, attachment, invoiceQty }`. Also `Notes.js` and `InvoiceDetail.js` (invoice screen has no submit button) |
| DELETE | `/ohlief/{id}` | defined on several screens; reached from the Back button on `Notes.js` after confirm |
| POST | `/ohlief` | `NewNote.js`, `NewOrder.js` body `{ content, attachment }` |
| GET | `/getproduct/{id}` | `ProductDetail.js` |
| PUT | `/updateProduct/{id}` | `ProductDetail.js` |
| POST | `/createProduct` | `NewProduct.js` |

Response shapes are whatever the screens destructure. Order line fields the client reads: `transactionId`, `orderNumber`, `barCode`, `sellerProductDescription`, `singleUnitPrice`, `orderQty`, `invoiceQty`, `quantityPerPack`, `content`, `attachment`, `storeSiteDescription`, `createdAt` (processed list only). Product fields: `transactionId`, `barcode`, `packageBarcode`, `productCode`, `productDescription`, `unitPrice`, `quantityPerPack`, `sizeOfProduct`, `unit`, `rsp`, `invoiceQty` (read and not shown).

The server, its auth check, and its status codes are **Unknown / Requires Confirmation**. The client treats a thrown Amplify error as `alert`.

## Storage

| Key | Value |
|---|---|
| Bucket | `ohlief-app-uploads` |
| Region | `eu-west-1` |
| Access | `Storage.vault.put` (Amplify private prefix for the identity) |
| Key format | `{timestamp}-{original file name}` |
| Cap | 5 000 000 bytes (`src/config.js`) |

The file input is rendered on `NewNote.js` and `NewOrder.js`. It is commented out on the order, invoice, and note-detail screens. Those screens still contain the size check, which only runs if `file.current` is set.

## Fields the UI computes

Order total and invoice total on the home tables, and the order-detail total:

```text
Math.round(quantity * singleUnitPrice * quantityPerPack).toFixed(2)
```

`quantity` is `orderQty` or `invoiceQty` depending on the column. See business rule BR-008.

`addThousandSeperator` in `src/libs/commonLib.js` adds comma groups. It does not round.

Pagination is `paginationFactory()` with no argument. This repo does not set `sizePerPage`. The paginator package's own default is 10. **Inferred** from that package, not from a setting here. Not executed in a browser for this document.

`textFilter` is imported in `Home.js` and not used. `filterFactory()` is passed to `ToolkitProvider`, and no column defines `filter`, so column filters are not configured. The SearchBar from the toolkit is what the tables render.

## Infrastructure and deployment

Not in the repository:

- Dockerfile, docker-compose, serverless.yml, CloudFormation, Terraform
- Bitbucket Pipelines or any other CI file
- Hosting config (S3 website, CloudFront, Amplify Console)
- Monitoring, logging sink, or analytics snippet
- Environment split. Dev and prod would require editing `src/config.js`

`npm start` is the CRA dev server (README says port 3000). `npm run build` writes `build/`. `.gitignore` ignores `node_modules`, `coverage`, `build`, and local env files. No `.env` is committed; configuration is not read from the environment.

`public/robots.txt` allows all user agents.

## Security notes visible in the client

- Cognito user-pool and identity-pool ids, the app client id, the API URL, and the bucket name are in source control.
- Any signed-in user receives the same screens. There is no role check.
- `GET /invoiceupdate/{orderNumber}` is a GET that the UI treats as a command.
- `Notes.js` Back confirms a delete after it has already navigated away.
- Signup can be opened only with a session, and it never asks for the confirmation code.
- The service worker is unregistered. The manifest still describes a standalone app.
- CDN stylesheets (Bootstrap 3.3.7, Google fonts) are loaded without a Subresource Integrity attribute in `index.html`.

## Development and tests

Scripts: `start`, `build`, `test`, `eject`.

`src/App.test.js` renders `App` and expects `/learn react/i`. The Oh-lief UI does not contain that string. **Inferred:** `npm test` fails on this file. Not executed for this document. `setupTests.js` only imports `@testing-library/jest-dom`.

No ESLint override beyond `"extends": "react-app"`. No Prettier config. No husky hooks.

## File-name map

Several files do not match the component they export. Routing uses the default export, so the file's behaviour is what the route runs.

| File | Export function | What it actually does |
|---|---|---|
| `InvoiceDetail.js` | `OrderDetail` | Read-only invoice line |
| `ProductDetail.js` | `InvoiceDetail` | Edit product |
| `NewProduct.js` | `NewOrder` | Create product |
| `Orders.js` | `Orders` | Not imported |
| `NewOrder.js` | `NewOrder` | Not imported. Note-style create form |
