# Business rules — unity-frontend

Evidence date: 2026-09-30. Source: `master` `0de9d02` unless a branch is named.

Rules are taken from code or from the root README. **Inferred** means the rule is a reasonable reading of the implementation and is not written down as policy. Nothing here is a new business decision.

The Laravel/Passport rules in the README are **Documented** only. This repository does not enforce them.

## Identity and access

### BR-001 — Guest is the starting Fuse user

**Implemented.** `user.reducer.js` initial `role` is `[]`. `FuseUtils.hasPermission` treats a missing or empty role as the guest.

### BR-002 — A route with no `auth` is open

**Implemented.** `hasPermission` returns true when `auth` is null or undefined. `/login`, login-2, register, and the demo apps do not set `auth`.

### BR-003 — An empty `auth` array is guest-only

**Implemented.** `authRoles.onlyGuest` is `[]`. `/auth/guest-role-example` uses it. A comment on that config says `//['guest']`; the imported value is the empty array, and there is no role string `guest`. A user whose role array is non-empty fails that check.

### BR-004 — Named roles

**Implemented** in `authRoles.js`:

- `admin` passes only `admin`.
- `staff` passes `admin` or `staff`.
- `user` passes `admin`, `staff`, or `user`.

Comparison is exact string inclusion. There is no role named `team manager` or similar in this repository.

### BR-005 — Failed authorization sends a guest to `/login`

**Implemented** in `FuseAuthorization.js`. If `userRole` is missing or empty, the redirect is `/login` and the blocked path is stored as `location.state.redirectUrl`. If the role is non-empty, the redirect is that stored URL, or `/` when there is none. `/` itself redirects to `/login`, so a signed-in user who fails a check and has no stored URL is sent to the login page. **Inferred** from the two redirects in sequence. Not executed in a browser.

### BR-006 — Logout requires a `user`-level role

**Implemented.** `/logout` sets `auth: authRoles.user`. A guest never reaches the `logoutUser` dispatch through this route.

### BR-007 — Custom login does not establish a session

**Inferred** from `Login.js`. On HTTP 200 the code builds `role: ['admin']` and `from: 'auth0'` but calls a local `setUserData` that returns a thunk and is not passed to `dispatch`. It does not write `localStorage`. `Auth.js` does not call `jwtCheck()` on startup, so a token stored by some other means would also not be loaded. The Redux user remains the guest.

The `displayName` on that unused object is the literal string `Jaco`, not the submitted email's local-part. The email field on the object is the submitted email.

### BR-008 — `public/` dashboard is not protected

**Implemented.** `public/src/index.js` renders `Dashboard` for `/dashboard` with no guard. The `sergei` app `react-frontend` does guard its dashboard: if `userAuth.isAuthenticated` is false it redirects to `/`. That flag is memory-only and is not on `master`.

## Login validation

### BR-009 — Fuse login fields must be non-empty

**Implemented** on `/login` and `/pages/auth/login-2`. `isFormValid` is `email.length > 0 && password.length > 0`. The LOGIN button is disabled otherwise. No format check. Remember Me defaults to checked and is not posted.

### BR-010 — `public/` email must look like an email

**Implemented** on `master` via Yup `.email().required("Required")`.

### BR-011 — `public/` password length and digit

**Implemented** on `master`:

- Missing password: "No password provided."
- Shorter than 8 characters: "Password is too short - should be 8 chars minimum."
- No digit: "Password must contain a number." (`/(?=.*[0-9])/`).

No uppercase, symbol, or maximum length rule.

On `sergei` this schema is commented out. **Not on `master`.** **Inferred:** that branch would submit empty fields. The `this.props.history` call is unchanged, so the navigation problem remains.

### BR-012 — Commented `super` / `dev` check does not run

**Implemented** as a comment in `LoginForm.js`. It is not a rule the app applies.

### BR-013 — `sergei` react-frontend login ignores the fields

**Implemented** on `sergei` only. `login()` calls `userAuth.authenticate`, which sets `isAuthenticated = true` after 100 ms. The username and password inputs are uncontrolled and unread. Any click on Login counts as success. This is not on `master`.

## Login outcomes (Fuse `/login`)

### BR-014 — HTTP 200 is treated as success

**Implemented.** Any resolved response with `status === 200` logs "Login successfull". The body is not checked. Axios 0.19 resolves 2xx, so a 200 with an error payload would take this branch. **Inferred** from the axios version in `package.json` and the status check. Not executed against a server.

### BR-015 — HTTP 204 is a credential mismatch

**Implemented** as an alert: "username password do not match". Only if the promise resolves. A server that returns 204 with an empty body can resolve; a server that returns 401 does not, and falls through to BR-017.

### BR-016 — Any other resolved status means the username does not exist

**Implemented** as an alert: "Username does not exist". The email field is not otherwise checked for existence.

### BR-017 — Transport and non-2xx failures are not shown in the form

**Implemented.** The `catch` handler console.logs the error. No inline field error and no alert.

### BR-018 — Login-2 uses a body code, not the HTTP status

**Implemented** in `Login2Page.js`: `response.data.code === 200` success (console only), `=== 204` mismatch alert, else the username-does-not-exist alert. This does not match BR-014, which uses `response.status`. The two screens encode two different contracts. Which contract the API uses is **Unknown / Requires Confirmation**.

### BR-019 — Login-2 URL is the README URL plus an extra `login` segment

**Implemented.** Base string already ends with `/login`. The post appends `login`. **Inferred:** this was not an intentional second resource name. The README **Documents** a single `/login` path. Requires confirmation before treating `loginlogin` as a real endpoint.

## Client list

### BR-020 — The table shows the hard-coded four rows

**Implemented.** Initial state `clients` is Wasif, Ali, Saad, and Asad with the emails in `Dashboard.js`. `renderTableData` maps that array. The API payload is stored on `contacts` and is not rendered.

### BR-021 — The client list request is unauthenticated

**Implemented.** `axios.get` with no `Authorization` header and no credentials option (axios default). URL is `http://localhost/coreUnit/api/public/api/client/list`. There is no `.catch`. **Inferred:** a failed request leaves the hard-coded rows in place and logs an unhandled rejection. Not executed in a browser.

## Registration and providers

### BR-022 — README: Passport is the API auth

**Documented** in the root README: "Auth uses Passport" and the login URL `http://localhost/coreUnit/api/public/api/v1/login`. No Passport middleware, token storage, or Laravel route exists in this repository. The rule is not enforced here.

### BR-023 — Google and Facebook buttons do not authenticate

**Implemented.** Both buttons on `/login` and login-2 have no click handler and no provider SDK call from those components. They are not a login method.

### BR-024 — Firebase and Auth0 are not configured

**Implemented.** `firebaseServiceConfig.js` and `auth0ServiceConfig.js` export objects whose keys are comments. `Auth.js` does not call `firebaseCheck` or `auth0Check`. The register tabs still render.

## Navigation and profile

### BR-025 — After a imagined successful Fuse login, the code names `/pages/profile`

**Implemented** as `<Redirect to='/pages/profile' />` inside the axios `then`, which does not render. **Inferred:** profile was the intended landing page. It does not happen. The profile page itself is a Fuse template that GETs `/api/profile/timeline`, `/api/profile/photos-videos`, and `/api/profile/about`.

### BR-026 — The only nav entry is Example, and its route config is unused

**Implemented.** `navigationConfig.js` has one child, URL `/example`. `ExampleConfig` is not in `routeConfigs`. **Inferred:** the nav item does not open `Example.js`.

## Cross-cutting

### BR-027 — Any origin may read responses if Apache applies the htaccess

**Implemented** in both `.htaccess` files: `Access-Control-Allow-Origin "*"`. This is a deployment header, not an application check. Whether those files are installed with a build is **Unknown / Requires Confirmation**.

### BR-028 — No password is stored by the `public/` form

**Implemented.** Values are logged with `console.log("Logging in", values)` and then the broken navigation runs. They are not written to `localStorage` or sent to `api_call_url`.

### BR-029 — Service workers stay off

**Implemented.** Both `frontend/src/index.js` and `public` (no service worker file; the Fuse one calls `unregister()`). `sergei`'s `react-frontend/src/index.js` also calls `serviceWorker.unregister()`.

## Conflicts that need a human

| Topic | README / one screen | Other screen |
|---|---|---|
| Login URL | README: `http://localhost/coreUnit/api/public/api/v1/login` | `/login` uses `http://local.unity/api/public/api/v1/login`. Login-2 appends a second `login` |
| Success signal | Login-2: `response.data.code` | `/login`: `response.status` |
| Where login goes | Fuse callback names `/pages/profile` | `public/` callback names `/dashboard` |
| Product title | README: Unity | HTML: Unity WebSystem (`frontend`) and Unity (`public`) |
| Register splash | "Welcome to the FUSE!" | Login splash: "Welcome to the UNITY!" |
