# Technical documentation — intersect_cloud_prod (Simpleflow Hub)

Evidence date: 2026-10-12. Revision: `main` @ `f4affe164d19d6d8eba86cf05b13504af7201bb5` (2025-01-06, author Jaco Brits, message "latest update").

Remote: `git@bitbucket.org:overdrive/intersect_cloud_prod.git`. Only ref: `refs/heads/main`. Commit count: 1.

## 1. What this repository is

A Flutter application. Package name `simpleflow_hub`. User-facing name **Simpleflow Hub**. Version `1.0.0+1`. `publish_to: none`.

The file headers in `lib/custom_code/` are FlutterFlow's generated banner ("Automatic FlutterFlow imports", "DO NOT REMOVE OR MODIFY THE CODE ABOVE"). Provenance: **Implemented**. The project was exported from FlutterFlow and then committed as one snapshot.

`README.md` is the FlutterFlow starter (title plus "FlutterFlow projects are built to run on the Flutter stable release").

## 2. Repository shape

```text
analysis_options.yaml
android/                         applicationId co.za.intersectcloud.simpleflowhub
assets/                          fonts, images, environment_values/environment.json
bitbucket-pipelines.yml
build/                           tracked; build/web is present
commit.bat
flutter_native_splash.yaml
ios/                             bundle id co.za.intersectcloud.simpleflowhub
                                 ImageNotification extension target
lib/                             application source
pubspec.yaml
pubspec.lock
README.md
test/widget_test.dart
web/
windows/
```

Not in the tree, though OVER-161 named them: `sonar-project.properties`, `sonarqube_scan.bat`.

`.gitignore` comments out `/build/` (`#/build/`), so Flutter build output is not ignored. `build/web` is in the commit. That matters because the pipeline deploys that folder.

## 3. Application structure

```text
lib/
  main.dart                      entry, theme, phone nav
  index.dart                     exports of routed pages only
  app_state.dart                 FFAppState (Provider ChangeNotifier)
  app_constants.dart
  environment_values.dart        reads the JSON asset
  auth/supabase_auth/            email auth, auth manager, user provider
  backend/supabase/              client, storage, database/tables/*.dart
  backend/api_requests/          api_calls.dart, api_manager.dart
  flutter_flow/                  generated framework, including nav/nav.dart
  custom_code/actions/           FlutterFlow custom actions
  custom_code/widgets/           signature pads, dynamic form builders
  components/
  forms/
  pages/
    auth/                        routed login and password pages
    authentication/              second auth tree, not exported by index.dart
    dashboard/
    management/                  routed admin UI
    user_management/             parallel admin UI, not exported
    general_forms/               routed tasks, assets, calendar, documents, tickets
    tasks/                       parallel task UI
    job_card/
    process_hub/process_hub/     routed Process Hub
    process_hub/process_hub_*/   copies, not referenced outside themselves
    pest_elite/
    plateau/
    messages/ notifications/ reporting/ profile/ my_profile/ include/
```

State: `ChangeNotifierProvider` of `FFAppState` in `main.dart`. `flutter_riverpod` is in `pubspec.yaml`. The shell that was read uses Provider, not Riverpod.

Routing: `go_router` 12.1.3, `usePathUrlStrategy()` so the web URL has no hash. `GoRouter.optionURLReflectsImperativeAPIs = true`.

## 4. Runtime and toolchain

| Item | Value | Source |
|---|---|---|
| Dart SDK constraint | `>=3.0.0 <4.0.0` | `pubspec.yaml` |
| Flutter revision recorded by the tool | `4d7946a68d26794349189cf21b3f68cc6fe61dcb`, channel stable | `.metadata` |
| Pipeline Flutter tarball | `flutter_linux_3.10.0-stable` from `mirrors.cloud.tencent.com`, then `flutter channel stable` and `flutter upgrade` | `bitbucket-pipelines.yml` |
| Lints | `flutter_lints` 4.0.0, `analysis_options.yaml` | |

The pipeline upgrade means a future pipeline run does not stay on 3.10.0. The committed `build/web` was produced by whatever Flutter last exported the project, which is **Unknown** beyond `.metadata`.

## 5. Platforms

| Target | Id / name | Shipped by the pipeline |
|---|---|---|
| Web | `MaterialApp` title Simpleflow Hub | Yes, via committed `build/web` |
| Android | `co.za.intersectcloud.simpleflowhub`, label Simpleflow Hub | No |
| iOS | same bundle id, display name Simpleflow Hub, URL scheme `simpleflowhub` / `simpleflowhub.com` | No |
| iOS notification extension | `ios/ImageNotification` | No push SDK in `pubspec.yaml` |
| Windows | `windows/` project present | No |
| Linux, macOS | no top-level project folders | No |

Deep link intent filter: `android/app/src/main/AndroidManifest.xml` scheme `simpleflowhub`, host `simpleflowhub.com`.

`app_links` 6.3.2 is a dependency. How incoming links are mapped onto routes was not traced past the package being present.

## 6. Backend

### Endpoint

`FFDevEnvironmentValues.currentEnvironment` is the string `Production`.

`assets/environment_values/environment.json` supplies:

- `SupabaseAPIURL` = `https://prod-alb.simpleflowhub.app`
- `SupabaseAnonKey` = a JWT whose payload role is `anon` and whose issuer is `supabase`

The raw JWT is not repeated here. It is committed in that JSON file.

`SupaFlow.initialize` passes that URL and key to `supabase_flutter` with `AuthFlowType.implicit` and `debug: false`.

The host is an application load balancer name, not `*.supabase.co`. **Inferred:** Supabase (or a compatible API) is self-hosted or proxied. **Unknown / Requires Confirmation:** the real Supabase version, region, and whether the ALB fronts Kong or a custom gateway.

A second host appears as a hardcoded default avatar: `lwbyelfbryqvcvebcsin.supabase.co`. Provenance: **Implemented** in the add-user insert. That is a hosted Supabase project ref, different from the ALB.

### Client libraries

From `pubspec.yaml`: `supabase` 2.3.0, `supabase_flutter` 2.6.0, `gotrue` 2.8.4, `postgrest` 2.1.4, `storage_client` 2.0.3, `realtime_client` 2.2.1, `functions_client` 2.3.2.

### Tables and views

109 Dart files in `lib/backend/supabase/database/tables/`. Each class sets `tableName`. They are the contract the client was generated against. They are not migrations.

Groups:

- Identity: `user`, `app_user`, `list_auth_users`, `list_users`, `tenant`, `tenant_user_link`, `tenant_user_list`, `list_tenant_user`, `list_user_tenants`, `client`, `old_client`, `tenant_client`, `tenant_client_link`, `client_user_link`, and the `list_*` variants.
- Access (unused by the shell, present as classes): `role`, `permission`, `menu`, `role_menu_link`, `user_role_link`, `list_user_override_role`.
- Process: `process`, `process_step`, `process_action`, `process_instance`, `tenant_process`, `user_process`, `action`, `action_template`.
- Work: `task`, `task_comments`, `create_task_form`, `create_task_form_comments`, `job_card`, `job_card_item`, `dummy_data_job_card`, `total_sum`.
- Inspections and equipment: `building_inspection_form`, `equipment_inspection_form`, `equipment_inspection_assign_form`, `equiment_inspection_assign_form`, `equipment_register`, `equipment_register_assigned`, `equipment_register_change_log`, `equipment_locations`, `list_equipment`, `list_equipment_categories`.
- Pest Elite: `pest_elite_siteform`, `pest_elite_sitemap`, `pest_elite_service_report`, `pest_elite_recommendation_report`, `pest_elite_action_report`, `pest_elite_action_report_revisit_dates`, checklist and trap tables for rodent, cockroach, flies, and SPI.
- Plateau: `plateau_service_report`, `plateau_service_report_areas_treated`, `plateau_non_conformance_report`, `plateau_corrective_action_report`, plus `list_plateau_*`.
- Support: `support_ticket`, `support_ticket_files`, `support_ticket_assigned_users`, `support_comments`, and `list_support_*`.
- Comms: `messages`, `chat`, `chat_messages`, `notification`, `user_notification`, `mail`, `send_que`, `email_template`.
- Files and forms: `document`, `document_access`, `form_definition`, `form_field`, `form_submission`.
- Other: `booking`, `booking_assigned_to`, `department`, `department_user`, `global_colors`, `report`, `test`, `test_tenant`, `pdf_test`.

### RPCs called by name

From `lib/backend/api_requests/api_calls.dart`:

| RPC | Dart class |
|---|---|
| `calculate_total_job_card_new` | `JobCardSumCall` |
| `calculate_sum_by_jobcard` | `JobCardItemSumCall` |
| `get_user_tasks` | `ListUserTasksCall` |
| `get_user_documents_details` | `UserDocumentsCall` |
| `insert_document_access` | `InsertDocumentAccessCall` |
| `insert_pest_elite_rodent_traps` | `InsertRodentTrapsCall` |
| `insert_pest_elite_cockroach_traps` | `InsertCockroachTrapsCall` |
| `insert_pest_elite_flies_traps` | `InsertFliesTrapsCall` |
| `insert_pest_elite_spi_traps` | `InsertSPITrapsCall` |
| `send_custom_email` | `SendCustomEmailCall` |

Other calls in that file are PostgREST queries against `user`, `list_tenant_user`, `task`, `tenant`, `list_equipment`, `list_support_tickets`, `support_ticket_assigned_users`, `document`, `client`, `document_access`, and `dummy_data_job_card`. They send header `apikey` set to the anon key.

### Storage buckets named in UI code

`uploads`, `files`, `profilePictures`, `messages`, `chats`.

Upload helper: `lib/backend/supabase/storage/storage.dart`. It uploads bytes and returns `getPublicUrl`. Policies (public vs authenticated) are **Unknown**.

### Realtime

`lib/custom_code/actions/subscribe.dart` joins channel `public:<table>` and listens for inserts, with an equality filter (the chat id argument). `unsubscribe.dart` is the pair.

## 7. Auth

- Email sign-in: `lib/auth/supabase_auth/email_auth.dart` and the login page.
- Sign-up exists as `createAccountWithEmail` / `auth.signUp`. The admin add-user flow expects an auth user id (`_model.authCheck`) before inserting `user`.
- Password reset: `resetPasswordForEmail`.
- Apple: declared on `AuthManager` and as package `sign_in_with_apple` 6.1.2. Not used by the login page that was read.
- Session check helper: `lib/custom_code/actions/check_auth.dart` (true when `currentSession != null`).
- After login, roles and tenant id are kept in `FlutterSecureStorage` through `FFAppState` setters. Keys are prefixed `ff_` (`ff_currentTenantId`, `ff_userRole`, `ff_systemRole`, `ff_tenantRole`, colour ints, page title).

JWT refresh behaviour is the Supabase client default for implicit flow. No custom refresh code was found.

## 8. Custom code worth knowing

| Path | What it does |
|---|---|
| `custom_code/actions/create_*_pdf.dart` | Builds PDFs for job cards, tasks, inspections, Pest Elite reports, Plateau reports |
| `custom_code/actions/replace_*_html_placeholders.dart` | Fills HTML templates for those same objects |
| `custom_code/widgets/signature_pad.dart` (and 2, 3) | Three signature capture widgets |
| `custom_code/widgets/dynamic_form_builder.dart` | Renders `form_definition` / `form_field` |
| `custom_code/widgets/dynamic_draggable_form_builder.dart` | Editor-style form layout |
| `custom_code/actions/get_otp.dart`, `confirm_otp.dart` | OTP helpers |
| `custom_code/actions/on_login_success.dart` | Refreshes colours and `Navigator.pushNamed` Dashboard |

`pdf` and `pdfx` are the PDF stack. Generation is on the device, then the file path is stored (columns such as `generated_pdf`, `file_name`).

## 9. Deployment

`bitbucket-pipelines.yml`, branch `main` only:

1. Step `build` (anchor `&build`) installs Flutter 3.10.0, upgrades the stable channel, `flutter pub get`, `flutter build web --release`. **This step is commented out** in the `pipelines.branches.main` list.
2. Step `deploy-prod` runs. `deployment: production`, `oidc: true`.
   - Pipe `atlassian/aws-s3-deploy:1.1.0` uploads `./build/web`.
   - Pipe `atlassian/aws-cloudfront-invalidate:0.10.1` invalidates `$DISTRIBUTION_ID`.
   - Variables: `AWS_PROD_ARN`, `AWS_REGION`, `AWS_S3_BUCKET`, `DISTRIBUTION_ID`. Values are not in git.

**Implemented consequence:** a push to `main` publishes whatever `build/web` is already in the commit. It does not compile the Dart sources unless someone uncommented the build step outside this revision.

Image: `atlassian/default-image:3`. Cache key: `$BITBUCKET_COMMIT`, paths `.flutter` and `.dart_tool`. The cache is unused while the build step stays commented out.

`commit.bat` is at the repo root. The pipeline file is the deployment definition that was inspected.

## 10. Configuration and secrets

| Secret or config | Where | In git? |
|---|---|---|
| Supabase URL and anon JWT | `assets/environment_values/environment.json` | Yes |
| AWS role, region, bucket, distribution | Bitbucket deployment variables | No |
| User session | Supabase client storage | No |
| Tenant id, roles, colours | Flutter secure storage on the device | No |

There is no `.env`. Do not copy the anon JWT into tickets or docs. **Requires Confirmation** whether it should be rotated, because it is in history at `f4affe1`.

## 11. Local data and other libraries

Declared and relevant:

- `flutter_secure_storage` 9.2.2 — persisted app state.
- `shared_preferences` 2.3.2 — FlutterFlow often uses this; the state class that was read uses secure storage.
- `hive` 2.2.3 and `sqflite` 2.3.3+1 — declared. No product cache design was documented in code comments.
- `http` 1.2.2 — used by `api_manager.dart`.
- `file_picker`, `image_picker`, `url_launcher`, `webview_flutter`, `video_player`, `table_calendar`, `google_fonts`, `flutter_animate`.

`dependency_overrides`: `device_info_plus` 10.1.2, `http` 1.2.2, `photo_view` 0.15.0, `rxdart` 0.27.7, `uuid` ^4.0.0, `win32` 5.5.1.

Barcode scanner: git dependency `https://github.com/FlutterFlow/flutter_barcode_scanner` ref `v2.0.0`.

## 12. Testing and quality gates

- `test/widget_test.dart` pumps `MyApp` and asserts nothing about the product. It is the Flutter counter-template comment with the counter expectation removed.
- No `integration_test/` directory.
- Pipeline does not run `flutter test` or a Sonar scan.
- `analysis_options.yaml` and `flutter_lints` are present for the analyser. CI does not invoke them in the steps that run.

## 13. Security notes (from the tree, not a penetration test)

1. Anon JWT committed in `environment.json`.
2. Router `requireAuth` defaults to false. Only `SupportTicketList` opts in.
3. Storage uploads request a public URL. If the buckets are public, objects are world-readable. Bucket privacy is **Unknown**.
4. Login assumes a tenant link and will throw if it is missing, which is a reliability issue more than an access-control feature.
5. Process Hub deletes null-status job cards without a tenant filter in the Dart call. Safety depends on RLS, which is not in the repo.
6. Add-user "already linked" query, as read, filters by tenant id and not by user id.

## 14. Related repositories

| Repo | Fact |
|---|---|
| `intersect_cloud_v2` | One commit, `.gitignore` only. Not this codebase and not a successor in git. |
| `intersect_lambda` | Separate project. This app does not import it. |

## 15. Open technical questions

- Is `prod-alb.simpleflowhub.app` self-hosted Supabase, and where is its SQL?
- Are Android, iOS, or Windows builds released anywhere?
- Is the committed `build/web` the same source as `lib/` at `f4affe1`, or an older export?
- Which service drains `send_que`?
- Are the `list_*` relations views or tables?
- Should the unused page trees be kept?
