# Dynamic Forms Kotlin App

This directory contains an Android application written in Kotlin for rendering forms and wizards that are defined on the server. The app communicates with the TAF API to download form metadata at runtime and submit user responses.

## Login

Users sign in with their normal ERP credentials. The app posts the username and password to `/api/auth/login` and stores the returned JWT in `SharedPreferences`. The token is attached to all subsequent API requests so the server can scope the data to the correct tenant and branch. When no connection is available the stored token is still used to allow offline data entry. Pending requests are synced once the device is back online.

## Features

1. **Fetch form definitions** – Retrieves JSON schemas from `/api/forms/{id}` and builds the UI dynamically.
2. **Multi-step wizards** – Chains multiple forms into step by step flows using wizard configurations.
3. **Offline caching** – Completed forms are stored locally with Room when connectivity is unavailable and synced later.
4. **Photo attachments** – Allows capturing images as part of form responses.
5. **Location tagging** – Optionally records GPS coordinates with each submission when permission is granted.

The project uses Retrofit for networking and Room for local persistence.

## Offline Capability

All definitions and submissions are cached locally so the app remains usable without network access. `LocalSyncManager` queues form responses and attachments for the `/api/sync` endpoint. When connectivity returns, the queued data is uploaded automatically.

Form and wizard definitions follow the same configuration format used by the web application. See [docs/FormBuilder.md](../docs/FormBuilder.md), [docs/frontend/ModalBuilder.md](../docs/frontend/ModalBuilder.md) and [docs/MultiStepForms.md](../docs/MultiStepForms.md) for the JSON schemas and examples. At runtime the app downloads these definitions from the `forms` and `form_wizards` tables and renders them on the device with `FormRenderer` and `WizardManager`.

Binary assets such as the TensorFlow Lite model, logo image, Gradle wrapper JAR and module icons are not stored in Git.
Download the PNG drawables from the [Material Design Icons repository](https://github.com/google/material-design-icons) and place them in `kotlin/app/src/main/res/drawable/`.
Run `./setup.sh` after the icons are downloaded to copy them into this project before building.

### Module Icons

The app expects several PNG icons referenced by name in the `ui_modals.icon` column. Place these files in `kotlin/app/src/main/res/drawable` before running `setup.sh`. After the script completes the icons will be copied into `app/src/main/res/drawable/`:

```
ic_attendance.png
ic_dashboard.png
ic_time_clock.png
ic_location.png
ic_approval.png
ic_adjustment.png
ic_summary.png
ic_table_list.png
```

Ensure these names are used when defining modules so the images can be resolved at runtime.

Open the `kotlinDynamicForms` folder in Android Studio or run `./gradlew assembleDebug` after the setup script completes.

## Module Launcher

Recent updates introduce a dashboard grid that lists all available modules. The
`DashboardActivity` queries the local database for records in `ui_modals` and
displays them in a two‑column `RecyclerView`. Selecting a card launches
`ModuleActivity` to render the chosen form or wizard.

Each card's image comes from the `icon` field of the corresponding row in
`ui_modals`. The value may reference a drawable name packaged with the app or a
direct image URL. `ModuleAdapter` resolves the drawable resource first and falls
back to loading the URL if needed.

Run `./setup.sh` before building to copy the required PNG assets into
`app/src/main/res/drawable` so the launcher icons display correctly.

## Modules with Tables

`ModalRenderer` now renders `table` elements in addition to forms. When a table
entry is encountered the renderer fetches the data using `DynamicRepository` and
shows it with `TableRenderer`. The element must specify its source table by
setting either the `id` property or `options.tableName`; otherwise the entry is
ignored and a warning is logged.

Table display is read‑only for now. Features such as inline editing, custom row
actions and advanced filtering have not been ported from the web interface yet.
