# Business rules — unity-lite

Evidence date: 2026-09-30. Revision: `master` `9b3a386` (2021-06-08).

No business-rules document existed in the repository or in `silicon_ark/unity-lite/` before this file. Nothing in the README states a commercial rule, a pricing rule, or an approval rule.

Rules below are labelled:

- **Documented** — written in the README, a UI string, or a code comment.
- **Implemented** — enforced by the code that runs.
- **Inferred** — a reasonable reading of that code, not executed against the live API for this document.

Where the server might enforce something the client does not show, the rule is **Unknown / Requires Confirmation**.

## Identity and access

### BR-001 — Five role names

**Documented** and **Implemented**. `src/config/Roles.js` lists `SUPER_ADMIN`, `ADMIN`, `MANAGER`, `CUSTOMER`, and `GUEST`, and the comment says they are placeholder roles that can be replaced.

The user form offers the same five strings in a multi-select (`src/components/User.js`). That selection is not saved.

### BR-002 — A `roles` localStorage key means signed in

**Documented** in the comment on `isLoggedIn` ("This app assume if local storage have roles it means user is authenticated") and **Implemented** in `src/utils/index.js`. The comment also says to update the logic. Cognito is not consulted on each navigation.

### BR-003 — Who may open which screen

**Implemented** in `src/config/PrivateRoutesConfig.js` and `getAllowedRoutes`.

| Screen | Allowed role strings |
|---|---|
| Calendar, Booking, Task list, Kanban, Users, User | Any visitor who has a `roles` key, including a stored `GUEST`, because these routes set no `permission` |
| Config, and through it Staff and Cron Jobs | `SUPER_ADMIN`, `ADMIN`, `MANAGER` |
| Dashboard | `SUPER_ADMIN`, `ADMIN` |
| Profile | `SUPER_ADMIN`, `ADMIN`, `MANAGER`, `CUSTOMER` |

A route is kept when the intersection of its `permission` array and the parsed `roles` value is non-empty. Missing or empty `permission` keeps the route.

`JSON.parse` is applied to `localStorage.roles`. Login writes `body.strRole` with `setItem` and does not `JSON.stringify` an array. **Inferred:** the private shell only renders if `strRole` is already a JSON array string. The API's actual format is **Unknown / Requires Confirmation**.

### BR-004 — Public pages are for visitors without a `roles` key

**Implemented** in `src/routes/Auth.js`. If the key is present, every path outside `/app` redirects to `/app`. Login, register, forgot password, and the landing page are then unreachable until logout.

The comment in that file calls the check temporary.

### BR-005 — Logout is local

**Implemented** in `TopNav.js`. Logout deletes `roles` and navigates to `/`. It does not delete `userDetails` and does not call `Auth.signOut()`.

**Inferred:** a later calendar load can still see the previous `userID` if some other code set `roles` again without rewriting `userDetails`. No such code path was found.

### BR-006 — Login does not check the typed value

**Implemented** in `Login.js`. There is no required-field check, no email format check, and no minimum length. The change handlers do not copy the input's value into state. The button is always enabled (the `disabled={!strEmail.length}` expression is commented out).

The initial email and password are literals in the file. They are omitted from this document.

### BR-007 — A failed sign-in still navigates to `/app`

**Implemented**. `history.push('/app')` is outside the success path's exclusive branch: it runs after the try/catch. If `roles` was not stored, `PrivateRoutes` sends the browser back to `/`.

### BR-008 — Uuid lookup failure is treated as a body

**Implemented** in `signInWithEmailAndPassword`: an exception from `API.get` returns `{ result: "Api Error" }` rather than throwing. The caller then reads `.strRole` and `.UserID` on that object.

There is no rule in the client for "unknown Cognito user" versus "known user". If element `[0]` is missing, reading `.strRole` throws, the outer catch logs it, and the app still navigates to `/app`. **Inferred**.

### BR-009 — No self-registration and no password reset

**Implemented**. `/register` and `/forgot-password` do not call Amplify Auth. **Inferred:** accounts are created somewhere outside this UI. Where, is **Unknown / Requires Confirmation**.

## Calendar

### BR-010 — Events are loaded for the signed-in user and the visible range

**Implemented** in `Calendar.js`. The request body is `userID` from `userDetails`, plus `dtStartDate` and `dtEndDate` from FullCalendar's `datesSet` range (start and end strings, which include the leading and trailing days of the month view).

Whether the server returns only that user's events is **Unknown / Requires Confirmation**. The client sends the id and renders whatever array comes back.

Historical deleted Lambda `getcalendarevents.js` selected from `crmCalendar` where `refSysUserID` matched path `userID` or was NULL, with date filters. That is **not** present at tip. Whether production still applies that rule is **Unknown / Requires Confirmation**.

### BR-011 — New events require a non-empty title from a prompt

**Implemented**. `handleDateSelect` only calls `addEvent` when `prompt` returns a truthy title. Empty cancel leaves no event.

### BR-012 — New events are attributed to the stored user

**Implemented**. Create passes `strFirstUser: userName` and `refSysUserID: userID` from `getUserDetails()`.

### BR-013 — Delete requires an explicit confirm

**Implemented**. `window.confirm` with the event title must succeed before `remove()`.

### BR-014 — Failed calendar writes undo the local change

**Implemented**. `addInfo.revert()`, `changeInfo.revert()`, `removeInfo.revert()` on catch.

### BR-015 — Field mapping between UI and API

**Implemented** transforms:

| FullCalendar / UI | API / DB-shaped field |
|---|---|
| `id` | `CalendarIDitem` |
| `title` | `txtDescription` |
| `description` | `txtNotes` |
| `start` | `dtStartDate` |
| `end` | `dtEndDate` |
| `strFirstUser` | `strFirstUser` |
| `refSysUserID` | `refSysUserID` |

Server-side column meaning beyond these names is **Unknown / Requires Confirmation**.

## Users

### BR-016 — User list comes from a fixed path segment `1`

**Implemented**. `POST /unity/getusers/1`. What `1` means (tenant, company, page, flag) is **Unknown / Requires Confirmation**.

### BR-017 — User list columns

**Implemented** accessors: `strUser`, `strTel`, `strJobTitle`, `strRole`, `strEmail`, `strLocation`, plus EDIT using `UserID`.

### BR-018 — User detail load uses location state, not the URL id alone

**Implemented**. `User.js` sets `userId = location.data`. The path `/app/user/{UserID}` is built by the link but the route definition is `/user` without a param. Opening `/app/user/123` without `location.data` yields a request with a falsy id. **Inferred**.

### BR-019 — User profile is not persisted from this form

**Implemented**. Update Profile has no API call and no `preventDefault` handler that saves. Company is hardcoded "Silicon Overdrive" and disabled.

### BR-020 — Cognito uuid maps to a user row (historical)

**Inferred** from deleted `lambda/getuseruuid.js`: `SELECT * FROM sysUser WHERE strCognitoUuid = "{id}"`. Current client still calls `GET /unity/uuid/{username}`. Live query is **Unknown / Requires Confirmation**.

## Storage and attachments

### BR-021 — Maximum attachment size constant

**Documented** only as config: `MAX_ATTACHMENT_SIZE: 5000000` in `aws-config.js`. No upload UI enforces it. **Unknown** whether any remote API uses the same limit.

### BR-022 — S3 bucket name

**Implemented** config only: bucket `unity-lite-files`, region `eu-west-1`. No client upload rule was found.

## Task list and dashboard

### BR-023 — Task list data is local sample content

**Implemented**. Two tasks are hard-coded in `Tasklist.js`. Save Task does not append to state or call an API.

### BR-024 — Dashboard numbers are static

**Implemented**. Labels and chart series are literals in `Dashboard.js`.

## Rules not found

No evidence in this repository for:

- Pricing, billing, or subscriptions.
- Approval workflows.
- Data retention or GDPR deletion.
- Password complexity (beyond whatever Cognito enforces outside this UI).
- Multi-tenant isolation beyond the unexplained `1` in getusers.
- Audit logging in the client (historical Lambda had a `log` helper; tip client only `console.log`s errors).

Any such rules, if they exist, live outside this tree — **Unknown / Requires Confirmation**.
