# PRD — clientSalesTracking

Evidence date: 2026-10-18. Source: `git@bitbucket.org:overdrive/clientsalestracking.git`, `master` at `5d5d307` (2020-06-24, "react components").

There is no prior PRD in Silicon Ark and no requirements document in the source repository. Requirements below are recovered from the implementation. Provenance is marked on each item. Nothing here is a **Documented** requirement, because the repository does not state requirements in prose.

## Product purpose

A person can open a client list and edit contact details for one client. The list and the form are backed by MySQL table `tblClient`.

Provenance: **Inferred** from `src/components/ListView.js`, `src/components/Card.js`, `contactDetails.php`, `getClient.php`, and `update.php`.

The repository name suggests sales tracking. No screen, endpoint, or comment implements opportunities, pipeline stages, activities, or quotes. That wider purpose is **Unknown / Requires Confirmation**.

## Users and roles

| Role | What they can do | Evidence | Provenance |
|---|---|---|---|
| Anyone who can open the app | View the client list and submit the edit form | No login check in `src/` or the PHP scripts | **Inferred** |
| Anonymous HTTP caller | List clients, read one client, update contact fields, list `sysuser` | Endpoints do not check a session, token, or password | **Implemented** |

No user role, permission, or account model is defined in this repository. `userDetails.php` returns rows from `sysuser` (user id, name, location, email, telephone, last edit) and the React app never calls it. Whether those rows are operators of this product is **Unknown / Requires Confirmation**.

## Main workflows

### W1 — View the client list

Trigger: the person follows the navbar link "View", which routes to `/ClientDetails`.

Implemented behaviour:

- `ListView` requests `GET http://localhost/unity_/contactDetails.php` once on mount.
- On success, the JSON array is rendered in a Bootstrap table with client-side pagination (`paginationFactory()` with no custom options).
- Columns shown: ID (`ClientID`, sortable), Client (`strClient`), Tel (`strTel`), Fax (`strFax`), Email (`strEmail`), Address (`strAddress`), Payment Type (`refPaymentType`), ClientJob (`txtClientJob`), Last Edit (`dtLastEdit`), and an Edit button.
- Edit navigates to `/edit/{ClientID}`.
- A failed request is written to the browser console. The table is not replaced with an error message.
- The "Loading..." branch is unreachable: state starts as `[]`, and an empty array is truthy, so an empty or not-yet-loaded list still renders the table.

There is no route for `/`. Opening the app root shows the navbar only.

Provenance: **Implemented** (`src/App.js`, `src/components/ListView.js`, `contactDetails.php`).

### W2 — Edit account details

Trigger: Edit on a list row, which opens `/edit/:id`. `Card.js` exports `Profile` and reads `id` with `useParams`.

Implemented behaviour:

1. On mount, `GET http://localhost/unity_/getClient.php?id={id}`.
2. The form is filled from `strClient`, `strEmail`, `strAddress`, `strTel`, `strFax`, and `txtClientJob`.
3. The person can change those fields locally. Email uses an input of type `email`. Other inputs are plain text. A range control labelled "Workload" is rendered with no value and no change handler.
4. Submit posts JSON to `http://localhost/unity_/update.php` with `client_id`, `client_name`, `client_email`, `client_address`, `client_telNumber`, and `client_fax`.
5. Client job is not included in that JSON. A `note` state variable exists and is never shown or sent.
6. The response is written to the console. The UI does not show success, failure, or a return to the list.
7. A failed load is written to the console. Fields stay empty.

The card beside the form shows the client name, the literal text "Sub-title", the unbound Workload slider, and the Create React App sample sentence "Some quick example text…". Provenance: **Implemented** placeholder copy in `src/components/Card.js`.

Provenance of the workflow: **Implemented**.

## Functional requirements

### FR-001 — List clients

Requirement: The system shows client contact rows in a table.

Who: any person who can open `/ClientDetails`.

Trigger: the list component mounts.

Inputs: none from the user. The browser calls `contactDetails.php`.

Expected behaviour: the response is a JSON array. Each object includes `ClientID`, `strTel`, `strFax`, `strEmail`, `strAddress`, `refPaymentType`, `txtClientJob`, `strClient`, and `dtLastEdit`. The table paginates in the browser. `ClientID` can be sorted.

Outputs: the table, or a console error if the request fails.

Validation: none on the request. The PHP script does not filter, page, or authorise.

Error conditions: if the SQL query fails, the script prints the text `failed` (not a JSON object). If the query succeeds and the table is empty, the script prints `[]`. Connection failure is handled in `dbconnect.php` by stopping with the mysqli error text.

Dependencies: MySQL table `tblClient`, PHP reachable at the hardcoded URL.

Implementation: **Implemented**.

Confidence: High.

Evidence: `contactDetails.php`, `src/components/ListView.js`.

### FR-002 — Open one client

Requirement: Edit on a row opens that client's form.

Who: the person using the list.

Trigger: the Edit button.

Inputs: `ClientID` from the row.

Expected behaviour: the browser path becomes `/edit/{ClientID}` and the profile component mounts.

Outputs: the account-details form.

Validation: the id is not checked before navigation.

Error conditions: none in the navigation itself. A missing row is handled by the load request (FR-003).

Implementation: **Implemented**.

Confidence: High.

Evidence: `Edit` in `src/components/ListView.js`, route in `src/App.js`.

### FR-003 — Load one client

Requirement: The form shows the stored contact fields for the id in the URL.

Who: the person on `/edit/:id`.

Trigger: the profile component mounts.

Inputs: query parameter `id` on `getClient.php`, taken from the route.

Expected behaviour: `SELECT * FROM tblClient WHERE ClientID='{id}' LIMIT 1`. The matching row is JSON. The form sets name, email, address, telephone, fax, and client job from `strClient`, `strEmail`, `strAddress`, `strTel`, `strFax`, and `txtClientJob`.

Outputs: a populated form, or empty fields if the request fails.

Validation: the id is interpolated into SQL with no escaping and no numeric check.

Error conditions: query failure prints `failed`. A query that succeeds with no row encodes `null` (`mysqli_fetch_assoc` returns `null`). The React effect then reads properties of that value. The effect's dependency list is empty, so a later change of `id` without a remount does not reload.

Dependencies: `getClient.php`, `tblClient`.

Implementation: **Implemented**.

Confidence: High for the happy path. The null-row behaviour is **Inferred** from the PHP and the property reads in `Card.js`.

Evidence: `getClient.php`, `src/components/Card.js`.

### FR-004 — Update contact fields

Requirement: Submitting the form writes name, email, address, telephone, and fax back to `tblClient`.

Who: the person on the profile form.

Trigger: the button labelled "Update User".

Inputs: JSON body `client_id`, `client_name`, `client_email`, `client_address`, `client_telNumber`, `client_fax`. PHP trims each value. The update runs only when `client_name` is set on the decoded body.

Expected behaviour: `UPDATE tblClient SET strEmail, strClient, strFax, strAddress, strTel WHERE ClientID = id`.

Outputs: the React code logs the response and leaves the person on the form.

Validation: HTML `type="email"` on the email field is the only client check. PHP does not validate format, length, or required fields beyond `isset($data->client_name)`. Values are interpolated into SQL.

Error conditions: see the response behaviour under discrepancies. A connection failure stops in `dbconnect2.php` with the connect error. If `client_name` is absent, the script prints nothing.

Fields the form shows but this update does not write: client job (`txtClientJob`). Fields the list shows that this update does not write: `refPaymentType`, `dtLastEdit`.

Implementation: **Implemented**, with the response-path defect below.

Confidence: High.

Evidence: `onSubmit` in `src/components/Card.js`, `update.php`.

### FR-005 — Show payment type and last edit

Requirement: The list displays `refPaymentType` and `dtLastEdit`. The person cannot change them in this application.

Who: the person on the list.

Trigger: list load (FR-001).

Expected behaviour: both values are columns. No control posts a new value.

Outputs: table text.

Implementation: **Implemented** as read-only display. Whether another system updates those columns is **Unknown / Requires Confirmation**.

Confidence: High for this repository. Low for the rest of the data lifecycle.

Evidence: column config in `ListView.js`; `update.php` SET clause omits both columns.

### FR-006 — User list endpoint

Requirement: `userDetails.php` returns a JSON array of `sysuser` rows with `UserID`, `strUser`, `strLocation`, `strEmail`, `strTel`, and `dtLastEdit`.

Who: any HTTP caller. No screen calls it.

Trigger: a GET (or other) request to the script. The React app has no such request.

Expected behaviour: same success and `failed` pattern as the client list.

Implementation: **Implemented** as an endpoint. A user-facing feature is **not implemented**.

Confidence: High.

Evidence: `userDetails.php`. Commented import of `AddUserModal` in `ListView.js` refers to a file that is not in the repository.

## Integrations

| System | Purpose | Trigger | Direction | Auth | Response | Failure |
|---|---|---|---|---|---|---|
| MySQL database `unity` on `localhost` | Store `tblClient` and `sysuser` | Every PHP script | App reads; `update.php` writes `tblClient` | Hardcoded in `dbconnect.php` and `dbconnect2.php`. Host `localhost`, user `root`, empty password, database `unity`. The password value is empty in source; it is not a copied secret beyond that fact | JSON rows, or connect/query failure text | `dbconnect.php` dies with the mysqli error. `dbconnect2.php` prints the error and exits. Query failure on the three read scripts prints `failed` |
| Local PHP at `http://localhost/unity_/` | JSON API for the React app | List mount, profile mount, form submit | Browser → PHP | None. `header.php` allows origin `*` | JSON, or the text `failed`, or the update script's echo | Axios `.catch` logs the error. No retry |

No other external system (mail, payment provider, queue, identity provider) appears in the source.

`refPaymentType` is a column name only. This repository does not call a payment API.

## Constraints

- API base URL is fixed to `http://localhost/unity_/`. There is no environment variable in the React code.
- List pagination is entirely in the browser. The PHP list returns every row `SELECT *` mapped to the nine fields.
- CORS is open (`Access-Control-Allow-Origin: *`). `Access-Control-Allow-Methods` is set to `POST` even though the read scripts are called with GET.
- No authentication and no authorisation.
- SQL is built by string concatenation.

Provenance: **Implemented**.

## Behaviour that is not a requirement

These are defects or leftovers visible in the code. They are recorded so the PRD is not read as asking for them.

- `update.php` treats a successful `UPDATE` as a result set. `mysqli::query` returns `true` for a successful update, and the script then calls `mysqli_num_rows` on that value. The `else` branch prints `0` and then JSON `{"success":1,"msg":"User Inserted."}`. The message says inserted. The statement is an update. Provenance: **Implemented**.
- The default test in `src/App.test.js` expects the text "learn react". `App.js` does not render that text. Provenance: **Implemented** mismatch. The test was not executed during this investigation.
- `src/App.css` is the Create React App template and is not imported. `public/index.html` still describes "Web site created using create-react-app".
- Commented imports in `ListView.js` (`filterFactory` / `textFilter`, `AddUserModal`) have no matching files.

## Out of scope of this repository

- Sales pipeline, activities, and forecasting.
- Create client, delete client, and search or column filters (the filter import is commented out).
- Authentication and permissions.
- Persisting Workload, notes, or client job from the profile form.
- Production hosting, CI, and monitoring. None are configured in Git.
