# Technical documentation — bitool_frontend

Evidence date: 2026-10-19. Revision: `master` `937e09cb` (2026-09-22).

This document describes the frontend repository. It does not describe the API implementation. `silicon_ark/bi_tool_php/` is a separate import-script repository. `silicon_ark/bi_tool_frontend_phase6/` describes branch `phase_6`, including a CodeIgniter dev proxy that `master` does not have.

## Identity

| Item | Value |
|---|---|
| Package name | `SAWIS BI` (`package.json` `version` `0.1.0`, `private: true`) |
| HTML title | SAWIS Report (`public/index.html`) |
| Remote | `git@bitbucket.org:overdrive/bitool_frontend.git` |
| HTTPS | `https://bitbucket.org/overdrive/bitool_frontend.git` |
| Default branch | `master` (`origin/HEAD`) |
| Sonar project key | `overdrive_bitool_frontend_3287b814-b882-4dde-8a5d-7400f836de1f` (`sonar-project.properties`) |
| History on `master` | 269 commits from `978f2c9` (2023-11-03) |

## Stack on `master`

| Concern | Choice | Evidence |
|---|---|---|
| UI | React 18.2.0, Create React App `react-scripts` 5.0.1 | `package.json` |
| Routing | `react-router-dom` 7.18.2. `BrowserRouter` basename is the `<base href>` | `src/index.js`, `src/App.js` |
| State | Zustand 4.4.3 stores | `src/store/` |
| HTTP | `fetch`. `axios` is a dependency and is not imported under `src/` | stores and pages |
| CSS | Bootstrap 5.2.3 and inline styles. Tailwind 3.3.3 is installed with `content: []` | `tailwind.config.js` |
| Charts | ECharts `^6.1.0` via `BIChart.jsx` (`init`, `setOption`, `resize`, `dispose`) | `NewKnowledgeBase.md` |
| Icons | Font Awesome | `Navbar.js`, pages |
| Excel | `@linways/table-to-excel`, `react-export-table-to-excel`, `table-to-excel`, `xlsx` 0.20.3 from the SheetJS CDN tarball, `xlsx-js-style` | `package.json` |
| Auth widget | `react-google-recaptcha` | `LoginForm.jsx` |
| Analytics | `ga-4-react`, id `G-1RFV9ZJG9K` | `src/index.js` |
| Cookies | `js-cookie` and raw `document.cookie` | `authStore.js` |
| Tests | Jest via `react-scripts test`. Playwright is a devDependency. The `tests/` directory is gitignored | `package.json`, `.gitignore` |
| Patches | `patch-package` on `postinstall` for `react-scripts` 5.0.1 and `serialize-javascript` 7.1.0 | `patches/` |

`qa` commit `ff8dd70` replaces CRA with Vite. That file (`vite.config.mjs`) is not on `master`.

## Application structure

```text
public/index.html          title, base href, favicon from sawisonline.co.za
src/index.js               bootstrap, GA4, service worker unregister
src/App.js                 Layout + Routes
src/AppRoutes.js           /, /reports, /grape-production, /vineyard-status
src/components/            Navbar, Layout, Sidebar, SidebarFilters, charts, pivots, forms
src/pages/                 Home, Reports, GrapeProduction, VineyardStatus
src/store/                 auth, filter, priceRange, grapeProduction, vineyardStatus
src/utils/authSession.js   401-only logout
src/api/runQuery.js        abort and error helpers
src/lib/constants.js       chart colours and filtersFallback
src/lib/helpers.js         numbers, reporting period, excel, auth headers
src/setupProxy.js          /weatherforecast only
aspnetcore-https.js        writes SSL file paths into .env.development.local
aspnetcore-react.js        same certificate helper
master_deploy.php          remote deploy trigger
bitbucket-pipelines.yml    SonarQube on master
```

`src/components/NavMenu.js` exists and is not mounted. `Home.js` comments out `<NavMenu />`. `src/components/PivotTable.jsx` at the components root is a second pivot beside `src/components/pivotTable/`.

## Runtime configuration

Committed `env` (no leading dot):

```text
BROWSER=none
# REACT_APP_API_BASE_URL=https://bi.sawisonline.co.za
REACT_APP_RECAPTCHA_DATA_SITE_KEY=
REACT_APP_API_BASE_URL=http://localhost:8080
REACT_APP_USE_AUTH_HEADERS=true
```

`.env.development`: `PORT=44487`, `HTTPS=false`.

`.env.e2e.local.example` names `BI_API_BASE_URL`, `BI_TEST_EMAIL`, `BI_TEST_PASSWORD`, `BI_TEST_LOGIN_TYPE=1` for a script `npm run test:api`. That script is not in `package.json` on `master`.

`.gitignore` ignores `.env`, `.env.local`, `.env.development.local`, `.env.test.local`, `.env.production.local`, and `.env.e2e.local`.

`npm start` runs `prestart` (`aspnetcore-https.js` then `aspnetcore-react.js`) and then `rimraf ./build && react-scripts start`. `npm run build` is `react-scripts build`. `npm test` is `cross-env CI=true react-scripts test --env=jsdom`. `npm run lint` is `eslint ./src/`.

Create React App loads `.env*` files, not a file named `env`. Whether developers copy `env` to `.env.local` was not confirmed. **Unknown / Requires Confirmation.**

## API surface called by `master`

Base: `process.env.REACT_APP_API_BASE_URL`.

Authorization, when used: header `Authorization` set to the raw token. No `Bearer ` prefix (`authHeaders` in `helpers.js`). Omitted when `REACT_APP_USE_AUTH_HEADERS` is `"false"`.

| Method | Path | When |
|---|---|---|
| POST | `/loginAuth` | Sign in. Body `{ email, password, typeOfLogin }` |
| GET | `/getFilters` | Guest export filters |
| GET | `/auth/getFilters` | Signed-in export filters |
| POST | `/getChartData` | Overview chart |
| POST | `/getYearOverYearComparison` | Overview chart |
| POST | `/getTotale_Liters` | Overview tiles |
| POST | `/getExports` | Overview chart |
| POST | `/getCultivarChartData` | Overview chart |
| POST | `/getTop25Data` | Overview chart |
| POST | `/tables/...` or `/auth/tables/...` | Five volume tables. `/auth` when `user` is set |
| POST | `/auth/getPriceRangeData` | Each price-range table |
| POST | `/auth/getPriceRangeFilterHistory` | Load saved tables. Body `{ email }` |
| POST | `/auth/setPriceRangeFilterHistory` | Save or delete tables. Body `{ email, filterHistory }` |
| POST | `/auth/reports/getRSAExportsReport` | Reports tab |
| GET | `/auth/getCountryGroup` | Country groups for that report |
| GET | `/getGrapeProductionFilters` | Grape filters |
| GET | `/getGrapeProductionReports` | Grape report catalogue |
| GET | `/getVineyardStatusFilters` | Vineyard filters |
| GET | `/getVineyardStatusReports` | Vineyard report catalogue |
| POST | `/{item.URL}` | One call per grape or vineyard report. `item.URL` is API-supplied |

`setupProxy.js` also proxies `/weatherforecast` to `ASPNETCORE_HTTPS_PORT`, or the first `ASPNETCORE_URLS` entry, or `http://localhost:34992`. Nothing in `AppRoutes.js` calls it.

Login `fetch` does not set `Content-Type`. The body is `JSON.stringify(...)`.

## Auth and session

Cookies written by script: `jwtToken`, `biUser`. They are readable by JavaScript. `HttpOnly` cannot be set from `document.cookie`. The assignment does not add `Secure` or `SameSite`.

Logout and the 401 helper are in `src/utils/authSession.js`. Coverage is `src/utils/authSession.test.js` (5 cases: 403, 404, 500 keep the session; 401 logs out; concurrent 401s alert once). `src/App.test.js` renders `App` inside `MemoryRouter` and waits one second.

Generated report, grape, and vineyard fetches do not call the 401 helper.

## Background work, queues, storage

No job runner, queue, or database client is in this repository. Persistence on the client is cookies plus whatever the API stores for price-range history. The service worker is unregistered. `workbox-*` packages are CRA dependencies and are unused while the worker stays unregistered.

## Build, CI, and deploy

Bitbucket Pipelines (`bitbucket-pipelines.yml`):

- Image `maven:3.3.9`.
- Branch pattern `{master}` only.
- One step: `sonarsource/sonarqube-scan:2.0.1` with `SONAR_HOST_URL` and `SONAR_TOKEN` from repository variables.
- Clone depth: full.
- No `npm install`, lint, test, or build step.

`master_deploy.php` is a token-gated curl trigger:

| Query case label | Host it calls |
|---|---|
| Label | `https://lui.sawisonline.co.za/deploy.php` |
| BIQA | `http://biqa.sawisonline.co.za/api/public/deploy.php` |
| BI | the same BIQA URL |

The IP allow-list denial is commented out. Curl uses `CURLOPT_SSL_VERIFYPEER` false. Query tokens are hard-coded in the file and are not copied into this document. **Human review:** rotate those tokens if this file is still deployed, and confirm the BI case should not use a distinct host.

How the React `build/` folder is published (copy, Docker, the PHP deploy script, or something else) is **Unknown / Requires Confirmation**.

## Testing

| Command | What it runs on `master` |
|---|---|
| `npm test` | Jest, CI mode, jsdom. Tracked tests found: `src/App.test.js`, `src/utils/authSession.test.js` |
| `npm run test:e2e` | Playwright, `testDir: ./tests`, base URL `http://localhost:44487`. `tests/` is not in git |
| `npm run lint` | ESLint on `./src` with `react-app` |

`docs/testsprite_run.md` is tracked even though `.gitignore` names it. It says that on 2026-09-16 a production build succeeded and the auth session tests passed 5/5, and that TestSprite could not start because `testsprite_tests/tmp/prd_files` was missing. This onboarding did not re-run the suite.

`NewKnowledgeBase.md` (2026-09-22) says `npm audit` reported 0 vulnerabilities and `npm run build` succeeded after the ECharts 6 bump. That was not re-run here.

## Security notes for review

These are observations from `master`, not a pentest.

- Deploy tokens and a downstream secret sit in `master_deploy.php`. The IP check is commented out. SSL verification is disabled on that curl.
- Session cookies are script-readable.
- The reCAPTCHA response is not sent to `/loginAuth`.
- `filtersFallback` includes cultivar strings that look like test data (`1202 COUDERC_test`, `as`). They are used only when the filter API fails.
- GA measurement id and the reCAPTCHA site-key variable name are public client configuration. The site key value in `env` is empty.

## Monitoring

- SonarQube project key above. Pipeline results were not opened.
- Google Analytics 4 as configured in `index.js`.
- No application log shipper, health endpoint, or error tracker (Sentry or similar) is referenced in `src/`.

## Discrepancies inside the repo docs

| Document | Says | `master` code |
|---|---|---|
| `DEVELOPER_DOCUMENTATION.md` | React Router 6, ECharts 5.4.3, oidc-client, port 3000, Playwright spec files, ASP.NET + SQL Server | Router 7.18.2, ECharts 6, no oidc package, port 44487, no `tests/` specs, API base URL is an env var |
| `DEVELOPER_DOCUMENTATION.md` | Zustand stores match the file list | The five stores exist. Field names in the doc's snippets are simplified |
| `README.md` | Mostly the CRA manual. First section matches the 401 rule | Accurate for that rule. The rest is upstream CRA text |
| `CHANGELOG.md` | Unreleased: audit cleanup and logout fix | Those commits are on `master` |
| `phase_6` Silicon Ark set | Domestic, admin, CI4 proxy | Not on this default branch |
