# PRD — blindside (BlindSide)

Evidence date: 2026-10-19. Source: `git@bitbucket.org:overdrive/blindside.git`, `master` at `25a8c70` (2019-06-17).

There is no prior PRD in the repository or in Silicon Ark. Every requirement below was recovered from the HTML, JavaScript, and deploy files. Provenance is marked on each item.

The repository has one branch. "Current" means present at that tip, not proven production.

## Product purpose

BlindSide is a browser application shell. The document title is "BlindSide" (`index.html`). After the sign-in button is clicked, the shell shows a dashboard whose visible headings are "Tournament 2019", "Get Into Rugby", "Plan & Budgets", and "SARU Provincial EPG 2016 Form".

Provenance: **Implemented** as static markup. No requirements document states the purpose.

The footer credits "Evolv by EVOLVaV" (`http://evolvav.com`). Provenance: **Implemented** copy in `core/view/footer.html` since `2ef3f33`. Whether Evolv is the product, the vendor, or leftover branding is **Unknown / Requires Confirmation**.

The chart data was introduced with the commit message "Dashboard controller created and some fake data" (`a9ecc7e`). Treat the numbers and the sample union rows as demo content unless a human confirms otherwise.

## Users and roles

| Role | What the screen shows | Evidence | Provenance |
|---|---|---|---|
| Anonymous visitor | Lands on a login card inside the shell | `core/routes/routes.js` loads `core/view/login/login.html` on ready | Implemented |
| Signed-in user | Intended by comments and by cookie helpers. The sign-in button does not create a session | Dead code after `return false` in the `login` case; `common.js` cookie helpers | Inferred intent. Not implemented |
| Named person "Heinrich" | Hardcoded user-menu label | `core/view/headerTopNav.html` | Implemented display text. Who this is: **Unknown / Requires Confirmation** |
| "Eugene" | Greeting "Good morning, Eugene" | `core/view/headerContent.html` | Implemented display text. The Limitless kit's author is Eugene Kopyov; the stock theme footer used that name. **Inferred** leftover |

No role, security group, or permission check exists. The Settings menu item "Access Contol" (spelling as stored) has no page.

The dead login success handler expected JSON fields `name`, `surname`, `id`, and `role`, and would store them in cookies `bsUserName`, `bsSurname`, `bsid`, and `bsrole`. That handler never runs. `common.js` reads a different cookie prefix (`ffUserName`, `ffUsersurname`, `ffid`, `ffrole`). Provenance: **Implemented** inconsistency. What "ff" refers to is **Unknown**.

## Main workflows

### W1 — Open the application

Trigger: the browser loads `index.html`.

Steps:

1. The shell renders empty regions `headerTopNav`, `headerContent`, `headerDesctiption`, `pageContent`, and `pageFooter`.
2. On document ready, `userSession` is `false`, so the script sets it to `true` and loads `core/view/login/login.html` into `#pageContent`.
3. The body receives class `bg-slate-800`.
4. Headers and footer are not loaded yet. `loadIndex()` runs only after the sign-in button.

Provenance: **Implemented** (`core/routes/routes.js`).

The commented block above that logic would have redirected to `./login.html` when `checkSession()` was false. `checkSession` is also commented out. Provenance: **Implemented** as comments. Not active.

`template/index_4.html` is a second shell. It loads `core/routes/routes.js`, `core/controller/common.js`, `core/controller/main.js` (file absent), and `core/controller/dashboard.js`. Nothing in `index.html` links to it. Provenance: **Implemented** file, not the entry point.

### W2 — Sign in

Trigger: click the control with `action="login"` and `eltarget="pageContent"` (the "Sign in" button).

Inputs shown on the card: email (`name="indexEmail"`), password (`name="indexPassword"`), a "Remember" checkbox, a "Forgot password?" link.

Expected behaviour of the active code:

1. `loadIndex()` removes `bg-slate-800` and loads the top nav, page header, secondary nav, and footer.
2. `loadPage` replaces `#pageContent` with `core/view/dashboard/mainDashboard.html`.
3. The handler `return false` stops the click. Everything after that return is unreachable.

The form is `method="POST"` with no `action`. jQuery's `return false` cancels the submit. No network login call is made.

Provenance: **Implemented**.

Unreachable code in the same case, kept as evidence of an earlier intent:

- Read `#indexEmail` and `#indexPassword`. The inputs have `name` attributes, not those ids, so the selectors would not see the typed values.
- If either value is `""`, show `#loginErrorEmpty`. That element is not in `login.html`.
- POST JSON to `api/login.php` with `username`, `password`, and a literal `apikey`. The API file is not in the repository. The key value is not copied into this PRD.
- If `data.error` equals the string `Encorrect Usename or Password`, show `#loginErrorEmpty`.
- On another success shape, set `currentUserName`, `currentUserSurname`, `currentUserId`, `currentUserRole` and cookies for 365 days, then load the dashboard.

Provenance of the unreachable block: **Implemented** source, not executed. The error string is stored with those spellings.

Other login-card behaviour:

- "Remember" is checked in the HTML and is not read by script.
- "Forgot password?" points at `login_password_recover.html`, which is not in the tree.
- Terms & Conditions and Cookie Policy are `href="#"`.
- `core/controller/login.js` is an empty file. The login behaviour lives in `routes.js`.

### W3 — Use the secondary navigation

Trigger: the secondary nav is present only after W2 (`core/view/headerDesctiption.html`).

| Control | Mechanism | Result |
|---|---|---|
| Dashboard | `href="index.html"` | Full reload. Ready-handler shows the login card again. The `dashboard` route case is not used by this link |
| List Personel | `action="listMember"` | Loads `core/view/member/listMember.html` |
| Add Personel | `action="addMember"` | Loads `core/view/member/addMember.html` |
| All Organisastions | `action="listOrganisastion"` | Loads `core/view/organisastion/listOrganisastion.html` |
| Create Organisastions | `action="addOrganisastion"` | Loads `core/view/organisastion/addOrganisastion.html` |
| Efilling Forms | `action="efillingForm"` | No matching case. Falls through to `default`, which calls `loadPage(eltarget, loadUrl)`. `loadUrl` is never defined |
| Form Templates | `action="formTemplate"` | Same default path. The class attribute is broken: `class="btn button "dropdown-item"` |
| Projects | `action="project"` | Default path |
| Manage Projects | `action="manageProject"` | Default path |
| Grants Categories | `action="efillingForm"` | Default path |
| Grants Classifications | `action="efillingForm"` | Default path |
| Access Contol | `action="project"` | Default path |
| Emails Sent | `action="project"` | Default path |
| Email Templates | `action="project"` | Default path |

Provenance: **Implemented** menu and switch. Pages for Forms, Plan and Budget, and Settings are **not implemented**.

Clicks are delegated from `body` to `.button`. The handler logs `eltarget` and `action` to the console.

### W4 — View the dashboard

Trigger: W2, or a `.button` with `action="dashboard"` (no such control is in the nav).

The fragment `core/view/dashboard/mainDashboard.html` then loads FullCalendar, ECharts, and `core/controller/dashboard.js`.

Content:

1. **Tournament 2019.** ECharts line chart on `#line_zoom`. Four series named Black, Colored, White, and Indian. Category axis is a fixed list of rugby competitions (HSBC sevens events, Super Rugby, Currie Cup, provincial 2019 sides, age-group weeks, and similar labels — the full list is the `xAxis.data` array in `dashboard.js`). Each series is a fixed number array. Zoom starts at 30 and ends at 70. If `#line_zoom` is missing, `echarts.init` still runs on a null element.
2. **Get Into Rugby.** Table columns Total, Generic, African, Colerd, Indian, White. Six body rows, each labelled Boys, each cell `0`.
3. **Plan & Budgets.** FullCalendar month/week/day grid, `defaultDate` `2014-11-12`, `editable: true`, events are the theme's 2014 sample set (All Day Event, Long Event, Conference, Meeting, Lunch, Happy Hour, Dinner, Birthday Party, "Click for Google" linking to `http://google.com/`).
4. **SARU Provincial EPG 2016 Form.** Two columns, Status and Organisation. Rows are static:

| Status text | Organisation |
|---|---|
| DRAFT | Limpopo Blue Bulls |
| IN PROGRESS | South Western Districts |
| APPROVE | Western Province |
| APPROVE | Pumas |
| APPROVE | Leopards |
| APPROVE | Kwazulu-Natal |
| APPROVED | Griquas |
| APPROVED | Griffons |
| APPROVED | Golden Lions |
| IN PROGRESS | Free State |
| APPROVED | Valke Rugby Union |
| APPROVED | Eastern Province |
| APPROVED | Boland |
| APPROVED | Border |
| APPROVED | Blue Bulls |

Organisation names are links to `#`. Status is a badge, not a workflow. "APPROVE" and "APPROVED" both appear. Nothing in script changes these rows.

Provenance: **Implemented** static content. The commit that added the controller calls the chart data fake. Whether the series names or the status words are an approved reporting scheme is **Unknown / Requires Confirmation**.

### W5 — Members

Trigger: List Personel or Add Personel.

List (`listMember.html`): four cards with placeholder images and the names James Alexander (Lead developer), Nathan Jacobson (Lead UX designer), Margo Baker (Sales manager), Barbara Walden (SEO specialist). Profile links go to `user_pages_profile.html`, which is not in the tree. Social icons are `#` links. `listMember.js` is an empty `document.ready`.

Add (`addMember.html`): an `h2` whose text is the file path `core/view/member/addMember.html`. `addMember.js` is an empty `document.ready`.

Both fragments request `assets/js/plugins/s`, which is not a file.

Provenance: **Implemented** stubs. The cards match Limitless demo people, not a member directory.

### W6 — Organisations

Trigger: All Organisastions or Create Organisastions.

Each view is an `h2` of its own path (`core/view/Organisastion/listOrganisastion.html` or `addOrganisastion.html`; the real directories are `organisastion`, lowercase o). Controllers are empty `document.ready` handlers. Both request `assets/js/plugins/s`.

Provenance: **Implemented** stubs. The directory name is spelled "organisastion" throughout routes and folders.

### W7 — Sign out

A `logout` case calls `logOut()` in `common.js`. `logOut` expires cookies `ffUserName`, `ffSurname`, and `ffid`, then `location.reload()`.

The user-menu item "Logout" is `href="#"` and has neither class `button` nor `action="logout"`. No visible control calls `logOut`. `checkLoggedinCookie` is never called from `routes.js`. It looks for `ffUserName` and would load `./core/views/dashboard/mainDashboard.html` (plural `views`), which does not exist.

Provenance: **Implemented** unused helpers. Session end as a user-facing action is not wired.

### W8 — Deploy

Trigger: a commit to `master` on Bitbucket Pipelines, if the pipeline is enabled. The YAML does not declare pipeline variables.

Steps in `bitbucket-pipelines.yml`: install `zip`, `pip install boto3==1.3.0`, zip the working tree to `/tmp/artifact.zip` (`zip -r /tmp/artifact.zip *`), run `python codedeploy_deploy.py`.

`codedeploy_deploy.py` uploads that zip to S3 at `{APPLICATION_NAME}/{timestamp}-bitbucket_builds.zip` and calls CodeDeploy `create_deployment`. It polls until status is Succeeded, Failed, or Stopped. `ignoreApplicationStopFailures` is true. Missing environment variables or AWS errors print a message and exit 1.

`appspec.yml` copies `/` to `/var/www/html/` on Linux. Hooks, all `runas: root`, timeout 300:

- BeforeInstall: `scripts/install_dependencies` — `yum install -y httpd`
- ApplicationStop: `scripts/stop_server` — the assignment is `isExistApp = `pgrep httpd`` (spaces around `=`), then `chmod 777` on `/var/www/html`
- ApplicationStart: `scripts/start_server` — `chmod 755` on `/var/www/html`, `service httpd start`

Provenance: **Implemented** scripts. The AWS application name, bucket, deployment group, and config name are **Unknown** (environment only). Whether this pipeline still runs is **Unknown / Requires Confirmation**.

## Functional requirements

### FR-001 — Application shell

The system serves a single HTML document titled BlindSide. Global theme CSS and JS load from `assets/`. Silicon Overdrive scripts loaded by the shell are `core/controller/common.js` and `core/routes/routes.js`.

- Who: any browser user.
- Trigger: open `index.html`.
- Output: empty header, content, and footer regions, then the login fragment.
- Validation: none.
- Error: if jQuery fails to load, the ready handler does not run and the regions stay empty.
- Evidence: `index.html`, `core/routes/routes.js`.
- Implementation: Implemented.
- Confidence: High.

### FR-002 — Login card on first ready

When `userSession` is false, the shell sets it true and injects `core/view/login/login.html` into `#pageContent`.

- The fragment contains its own `<body>`, so the injected markup is not a valid fragment. Browsers still show the card.
- Evidence: `routes.js` ready handler, `core/view/login/login.html`.
- Implementation: Implemented.
- Confidence: High.

### FR-003 — Sign-in opens the dashboard without credentials

A click on `.button` with `action="login"` loads the chrome via `loadIndex()` and the dashboard into the button's `eltarget`, then returns. Email and password are not validated.

- Inputs: click. Field values are ignored.
- Output: header, secondary nav, footer, dashboard fragment.
- Error conditions of the unreachable API path do not apply.
- Evidence: `routes.js` case `login`.
- Implementation: Implemented.
- Confidence: High.

### FR-004 — Unreachable login client

The repository contains a login POST that the `return false` above makes unreachable.

- URL: `api/login.php` (not in this repository).
- Body fields: `username`, `password`, `apikey`.
- Success fields the script reads: `error`, `name`, `surname`, `id`, `role`.
- Cookies written on that path: `bsUserName`, `bsSurname`, `bsid`, `bsrole`, 365 days, `path=/`.
- The empty-field and error UI depend on `#loginErrorEmpty`, which is absent.
- Implementation: code present, behaviour not reachable. API absent.
- Confidence: High that it does not run. The intended contract with `api/login.php` is **Unknown / Requires Confirmation**.

### FR-005 — Fragment loading

`loadPage(container, page)` clears `#container`, jQuery-loads `page`, and scrolls to the top.

- Evidence: `routes.js`.
- Failure: a missing file leaves the container empty or shows the server's 404 body. There is no application error message.
- Implementation: Implemented.
- Confidence: High.

### FR-006 — Secondary navigation labels

After sign-in, the secondary bar shows Dashboard, Members, Organisastions, Forms, Plan and Budget, and Settings, with the child labels listed in W3.

- Evidence: `core/view/headerDesctiption.html`.
- Implementation: Implemented as navigation. Child screens other than members and organisations are not implemented (FR-010).
- Confidence: High.

### FR-007 — Dashboard chart, tables, and calendar

The dashboard shows the four blocks in W4, using the static series, zero-filled Get Into Rugby rows, 2014 sample calendar events, and the provincial status rows.

- Trigger: dashboard fragment load.
- Libraries: ECharts and FullCalendar, loaded by the fragment.
- The calendar initialises only when `.fullcalendar-basic` exists.
- Evidence: `mainDashboard.html`, `dashboard.js`, commit `a9ecc7e`.
- Implementation: Implemented static demo.
- Confidence: High for what is rendered. Confidence low that the numbers are real measures.

### FR-008 — Member list and add-member stub

List Personel shows four static profile cards. Add Personel shows a path heading. Neither screen reads or writes a member record.

- Evidence: `core/view/member/`, `listMember.js`, `addMember.js`.
- Implementation: Implemented as stubs.
- Confidence: High.

### FR-009 — Organisation stubs

All Organisastions and Create Organisastions show a path heading and load an empty controller.

- Evidence: `core/view/organisastion/`, matching controllers, `routes.js`.
- Implementation: Implemented as stubs.
- Confidence: High.

### FR-010 — Menu targets without screens

Efilling Forms, Form Templates, Projects, Manage Projects, Grants Categories, Grants Classifications, Access Contol, Emails Sent, and Email Templates do not map to a view. The default branch calls `loadPage` with an undefined URL.

- Evidence: `headerDesctiption.html`, `routes.js` `default`.
- Implementation: menu Implemented; destination Not implemented.
- Confidence: High.

### FR-011 — Cookie and logout helpers

`common.js` can set and read cookies and defines `logOut` and `checkLoggedinCookie` using the `ff*` names and a `core/views/` path. The live login path, if it were reached, would write `bs*` cookies. No screen calls these helpers.

- Implementation: Implemented unused code.
- Confidence: High.

### FR-012 — Header chrome is static

The top bar shows a logo (`assets/images/logo_light.png`), a users-online list (Jordana Ansley, Will Brason, Hanna Walden, Dori Laperriere, Vanessa Aurelius), an activity feed (including a "Limitless" repository mention), and a user menu (My profile, Messages with badge 58, Account settings, Logout). The page title line is "Dashboard" plus "Good morning, Eugene" and a search box. A floating button offers Compose email, Conversations (badge 5), and Chat with Jack.

- None of these controls has an application handler. Search is `form action="#"`.
- Provenance: **Implemented** markup. **Inferred** that the people, activity, and chat items are Limitless demo chrome carried into `headerTopNav.html` and `headerDesctiption.html`.
- Confidence: High that they do not call BlindSide logic.

### FR-013 — Deploy static files to Apache

On a `master` pipeline run, the tree is zipped and sent to CodeDeploy, which copies it to `/var/www/html/` and starts `httpd`.

- Configuration inputs are environment variables, not files.
- Failure: upload or deploy errors exit 1. ApplicationStop failures are ignored by the CodeDeploy API flag.
- Evidence: `bitbucket-pipelines.yml`, `codedeploy_deploy.py`, `appspec.yml`, `scripts/*`.
- Implementation: Implemented as pipeline definition.
- Confidence: High for the script. Live AWS targets are Unknown.

## Integrations

| System | Purpose | Trigger | Direction | Auth | Response | Failure |
|---|---|---|---|---|---|---|
| `api/login.php` | Intended credential check | Unreachable code in the login case | Browser POST JSON | Literal `apikey` field. Value omitted here | JSON `error` or `name`/`surname`/`id`/`role` | `failure` callback is empty. File not in repo |
| Google Fonts | Roboto stylesheet | Page load | Browser GET | None | CSS | Page still loads without the font |
| AWS S3 | Store the zip revision | Pipeline | Pipeline PUT | boto3 default credential chain. Keys not in repo | Object key under `APPLICATION_NAME` | Prints error, exit 1 |
| AWS CodeDeploy | Copy revision onto Linux and run hooks | After S3 upload | Pipeline API | Same credential chain | Deployment id, then status poll | Prints error, exit 1. In-progress polling has no sleep despite `sleep` being imported |
| `http://google.com/` | "Click for Google" calendar sample | User opens that event | Browser | None | External site | Not an application integration |
| `http://evolvav.com` | Footer credit link | User click | Browser | None | External site | Not called by script |

No database, email sender, queue, or payment integration is present. Menu labels for email templates and "Emails Sent" have no client.

## Constraints

- jQuery is required for routing. There is no build step and no module bundler.
- Routing is a click switch on `action` attributes, not a URL router. Refresh on the dashboard returns to the login card (FR-002 and the Dashboard link).
- `$.ajaxSetup` adds header `CsrfToken` from `meta[name="csrf-token"]`. `index.html` has no such meta tag, so the header value is empty. The only AJAX call that would send it is the unreachable login POST.
- Demographic labels and union rows are hardcoded. There is no data API behind the dashboard.
- The pipeline image is `python:3.5.7` and pins `boto3==1.3.0` (2016-era script header in `codedeploy_deploy.py`).

## Behaviour that looks like a defect

Recorded so it is not mistaken for a requirement:

- Sign-in ignores credentials (`return false` before the checks).
- Login selectors use ids the form does not set.
- Cookie prefixes `bs*` and `ff*` do not match. Logout is not attached to the Logout item.
- `checkLoggedinCookie` loads a path under `core/views/` that does not exist.
- Several views request `assets/js/plugins/s`.
- `template/index_4.html` requests `core/controller/main.js`, which does not exist.
- Form Templates markup breaks the `class` attribute.
- `stop_server` uses an invalid shell assignment and then `chmod 777`.
- Status badges use both "APPROVE" and "APPROVED". Column heading "Colerd" is the spelling in the file. Menu spellings "Personel", "Organisastions", "Efilling", and "Access Contol" are the spellings in the file.

## Unknown / requires confirmation

- Product owner, users, and whether anyone still runs this site.
- Meaning of EPG, and whether the provincial rows were sample data or a real 2016 form.
- Whether the chart series names are an approved taxonomy.
- The contract and location of `api/login.php`, and whether the embedded API key was ever a live secret.
- CodeDeploy application name, S3 bucket, deployment group, region, and account.
- Why Sonar files were listed at task creation but are absent from git history.
- Relationship between the names BlindSide, Evolv, EVOLVaV, and the hardcoded name Heinrich.
