# Features — flutter_app (Datafy Ai)

Evidence date: 2026-10-15. Source: `develop` at `7d30344`. Version `1.0.79+100103`.

Status values: **Implemented** (in the client on this commit), **Documented** (README or PowerSync guide, not re-traced line by line), **Inactive** (code present, not on the startup path), **Unknown**.

## Current features

### Identity and session

| Feature | What it does | Where | Status |
|---|---|---|---|
| Phone sign-in | Digits become `{digits}@datafy.co.za`, then email/password | `lib/flutter_flow/custom_functions.dart` `phoneToEmail`, `login_widget.dart` | Implemented |
| Email sign-in | Toggle on the login page | `login_model.dart` `emailSignin` | Implemented |
| Location gate | Brands opens only with location permission and a passing GPS status check | `login_widget.dart` | Implemented |
| Role normalisation | `admin` (any case) → Admin, else Rep | `main.dart`, login, `brands_widget.dart` | Implemented |
| Login activity | Writes `user_activity` type `login` with GPS and rep name | `login_widget.dart` | Implemented |
| Session restore | Existing session refreshes role from `app_user`. Does not start PowerSync | `main.dart` | Implemented |
| English and Afrikaans | Locales registered | `main.dart`, `ios/Runner/Info.plist` | Implemented |

`sign_in_with_apple` is a dependency. No login-page call was found. **Unknown** whether it is a product feature.

### Brand and store selection

| Feature | What it does | Where | Status |
|---|---|---|---|
| Brand picker | Lists the user's customers and routes Admin vs Rep | `lib/pages/rep/dashboard/brands/` | Implemented |
| Rep sync on brand tap | PowerSync starts after the brand is chosen | `brands_widget.dart` | Implemented |
| Change brand | Reps with more than one brand get a profile card | README, profile | Documented |
| Store list | Active `store_user_link` rows. List still shows if PowerSync `app_user` is not synced yet | `store_page_main`, sync rules `active = 1` | Implemented |
| Channel artwork | Store row uses `channel_image` | `store_page_main_widget.dart` | Implemented |

### Store execution — Rep

| Feature | Route | Where | Status |
|---|---|---|---|
| Rep dashboard | `/dashboard` | `lib/pages/rep/dashboard/dashboard/` | Implemented |
| Store home | `/storePageMain` | `lib/pages/rep/stores/store_page_main/` | Implemented |
| Store items | `/storeItems` | `lib/pages/rep/stores/store_items/` | Implemented |
| Product overview | `/allItemsOverview` | `lib/pages/rep/stores/all_items_overview/` | Implemented |
| SPAR product overview | `/allItemsOverviewSpar` | `all_items_overview_spar/` | Implemented |
| Categories | `/categoryPage` | `lib/pages/rep/category/category_page/` | Implemented |
| SPAR categories | `/categoryPageSpar` | `category_page_spar/` | Implemented |
| RFG category block | Not a route. SPAR and SUPERSPAR branch inside the widget | `r_f_g_categories_widget.dart` | Implemented |
| Tasks | `/tasksList`, `/tasksListAll` | `lib/pages/rep/tasks/` | Implemented |
| SPAR tasks | `/tasksListSpar`, `/tasksListAllSpar` | same | Implemented |
| Task detail | `/taskDetails`, `/taskDetailsCategory` | same | Implemented |
| Task gallery | `/taskGallery` | `task_gallery/` | Implemented |
| Specials (promos) | `/promosList`, `/storePromos`, `/promosStoresList` | `lib/pages/rep/promo/` | Implemented |
| Special detail | `/taskDetailsPromo`, `/taskDetailsPromoInstore` | same | Implemented |
| KaChing list | `/kachingList`, `/kachingStoresList` | `lib/pages/rep/ka_ching/` | Implemented |
| KaChing detail | `/taskDetailsKachinga` | `task_details_kaching/` | Implemented. Path spelling includes a trailing `a` |
| Surveys | `/formList`, `/surveyQ1` | `lib/pages/rep/surveys/` | Implemented |
| Profile | `/profileNew` | `lib/pages/includes/profile/profile_new/` | Implemented |
| Notifications | `/notifications` | `lib/pages/includes/notification/notifications/` | Implemented |
| Verify | `/verify` | `lib/pages/includes/auth/verify/` | Present. What it verifies was not traced |

SPAR, SUPERSPAR, and KWIKSPAR are the chains that select the SPAR screens (`store_items_widget.dart`).

### Store execution — Admin

Parallel screens under `lib/pages/admin/`, each with `requireAdmin: true`.

| Feature | Route |
|---|---|
| Dashboard | `/dashboardAdmin` |
| Store home | `/storePageMainAdmin` |
| Store items | `/storeItemsAdmin` |
| Product overview | `/allItemsOverviewAdmin` |
| SPAR product overview | `/allItemsOverviewSparAdmin` |
| Categories | `/categoryPageAdmin`, `/categoryPageSparAdmin` |
| Tasks | `/tasksListAdmin`, `/tasksListAllAdmin`, `/tasksListSparAdmin`, `/tasksListAllSparAdmin` |
| Task detail | `/taskDetailsAdmin`, `/taskDetailsCategoryAdmin` |
| Task gallery | `/taskGalleryAdmin` |
| Specials | `/storePromosAdmin`, `/taskDetailsPromoAdmin`, `/taskDetailsPromoInstoreAdmin` |
| KaChing | `/kachingListAdmin`, `/taskDetailsKachingAdmin` |
| Surveys | `/formListAdmin`, `/surveyQ1Admin` |
| Profile | `/profileNewAdmin` |

Admin data access is Supabase, not PowerSync. Status: **Implemented** as routes and guard.

`lib/pages/sub_channel_list_admin/` exists and is not registered in `nav.dart`. **Unknown** whether it is embedded or unused.

### Offline and media

| Feature | What it does | Where | Status |
|---|---|---|---|
| PowerSync local DB | Schema for surveys, stores, tasks, promos, KaChing, products, activity | `lib/app_constants.dart` | Implemented |
| Sync rules source | Five buckets. Must be copied to the PowerSync dashboard | `sync_rules.yaml` | Implemented as a file. Live deploy **Unknown** |
| Attachment queue | Local-only photo queue. Not uploaded as a Supabase table | schema `attachments_queue`, README | Documented skip. Schema flag Implemented |
| Wi-Fi-only sync | Profile toggle | README | Documented |
| Sync now | Manual sync from Rep profile, including on mobile data | README | Documented |
| Sync progress overlay | Full-screen progress until first sync | README, dashboard | Documented |
| Cached images | `OptimizedNetworkImage` / `cached_network_image` for galleries | README, `pubspec.yaml` | Documented component. Package Implemented |
| Storage bucket | Constant `media` | `app_constants.dart` | Implemented |

### Safety and display rules that are features

| Feature | Status |
|---|---|
| Store-code fail-closed mappers | Implemented for `supabaseRowsToTask`. Documented for the other mappers named in the README |
| Cross-store detail pop | Documented for task, promo, and KaChing detail |
| No placeholder image on complete | Documented. Helper `hasUploadedImageUrl` Implemented |
| Admin Fixed archives the task | Documented. `isTaskStatusFixed` Implemented |
| Admin returns to Admin lists after KaChing/special complete | Documented |
| Date format `dd-MM-yyyy HH:mm` | Implemented as constants |
| User-linked surveys scoped by region, channel, customer | Implemented on the Rep form list |
| DISCHEM channel row | Migration `20250223000000_channel_data_dischem.sql` |

### Release

| Feature | Status |
|---|---|
| `develop` → Android App Bundle → Play production | Implemented (`bitbucket-pipelines.yml`, `android/fastlane/Fastfile`) |
| Version bump committed back with `[skip ci]` | Implemented (`scripts/bump_version.sh`) |
| Supabase `version.app_version` updated after upload when the service role key is set | Implemented |
| iOS TestFlight via custom pipeline on a self-hosted Mac | Implemented as a custom pipeline, not on `develop` |
| SonarQube on `main` | Implemented as a pipeline step. This clone has no `main` checkout |
| Local pipeline script | `scripts/test-pipeline-local.ps1` is documented in the README |

## Inactive or retained

| Feature | Evidence | Status |
|---|---|---|
| Force update dialog | `lib/services/force_update_service.dart`, `lib/widgets/force_update_overlay.dart` | Inactive. Not called from `main.dart`. README says removed from startup on purpose |
| iOS App Store URL | Placeholder id `0000000000` in the force-update service | Not a working store link |
| `environment.json` `appVersion` `1.0.38` | Loaded, no other reader found | Stale relative to pubspec `1.0.79` |

The pipeline still writes `app_version`. That write does not block the user while the client check stays unwired.

## Planned

No issue tracker, TODO product list, or milestone file in the repo describes planned features. README items that say "would require a separate implementation":

- Change brand for Admin (Supabase-based). **Documented as not built.**

Force update is documented as available to turn back on, not as a committed next release.

## Recently changed

`HEAD` is a version-bump commit. The functional commits immediately before it, from `git log`:

| Commit | Date / message | What the message claims |
|---|---|---|
| `72d29f4` | `change to production` | Aligns with Fastlane production track. The README still says internal testing |
| `acc4bec` | `admin product fix` | Message only |
| `73b8876` | `powersync sync issue` | Message only |
| `1c5acb4`, `26bae4c`, `03ff246` | `kaching` / `kaching fix` | KaChing |
| `865363f` | `kaching & special flow improvement` | Matches the README success-dialog and navigation notes |
| `fdab328` | `product limit fix in manager` | Matches the README removal of `limit: 100` on the admin overview |
| `9787dfc` | `data leak fix` | Matches the README store-isolation notes |
| `4ac9166`, `fa4a723` | `powersync sync improvements` | Message only |
| `0042a1f` | `added checks to improve performance` | Message only |

The README "Recent changes" section is the narrative for these themes (PowerSync lock timeout, Admin role race, KaChing/special layout and navigation, store isolation, survey user-links, DISCHEM, date format, Wi-Fi sync). It is **Documented** history, not a separate backlog.

`75ce743` (`IOS deploy remove retro`, 2026-02-06) is the commit OVER-119 recorded. Later commits on `develop` supersede that tree.

## Removed or not on the startup path

- Force-update check at startup. Files kept. **Inactive.**
- README says a default image is no longer stored when a task, special, or KaChing is completed without a photo. **Documented.**

## Generated data surfaces with no route

`lib/backend/supabase/database/tables/` includes classes that have no matching screen in `nav.dart`, including `sjreport`, `promo_uploads`, `kaching_uploads`, `store_processing_queue`, `product_ml`, `model_versions`, and backup table names (`store_backup_20241118`, `store_product_backup_20241128`). Those classes are FlutterFlow/Supabase output. They are not, by themselves, user features. What writes them is **Unknown / Requires Confirmation**.

## Tests

`test/widget_test.dart` pumps `MyApp` and does not assert a feature. Status: not product coverage.
