--- name: vitruvio-criar-form-mobile description: > Use when the user wants to create or edit the MOBILE form of a Vitruvio panel or process (the React-Native form rendered in the mobile app). Triggers: "create mobile form", "criar formulário mobile", "mobile panel", "painel mobile", "criar painel mobile", "process mobile form", "formulário mobile do processo", "add mobile form", "mobile form xml", "bridge mobile". This is the single home for mobile form knowledge; vitruvio-criar-painel and vitruvio-criar-processo call it for their mobile part. For the desktop form use vitruvio-criar-form-desktop. --- # Create Vitruvio Mobile Form > All messages shown to the user must be written in Portuguese. Mobile forms are **not** desktop forms with a different skin. They use a different schema, a smaller component set, a different JavaScript runtime, and an explicit server↔client data contract. Read this whole skill before scaffolding — the mistakes here are not the desktop mistakes. ## The four things that make mobile different 1. **Different schema.** Root is ``, namespace `http://www.davinti.com.br/vitruvio/mobile-form`, XSD `vitruvio-mobile-form.xsd`. 2. **Only 18 components** (vs 78 desktop). Do **not** assume a desktop widget exists on mobile. The full list: `CheckBox, ComboBox, GoogleMapsField, HorizontalLayout, ImageLibraryByFieldValue, ImageWidget, Label, MaskedField, MoneyField, NumericField, OptionGroup, ProgressBarWidget, RatingStars, SignaturePadField, TabLayout, TextField, Toggle, VerticalLayout` (plus structural `SubForm`/`ItemList`). Read `~/.local/share/vitruvio-platform/docs/components/mobile/.md` before using one. 3. **Two JavaScript runtimes.** - **Client-side** (`initScript`, `discoveryScript`, validators, component event scripts): **modern React-Native JS** — arrow functions, Promises, `.then()/.catch()` are fine and expected. Most APIs are **async** and return Promises. - **Server-side** (`` `execute(...)` bodies): **Rhino ES5**, same rules as scripts/desktop. This is the only place with access to platform libs and services (`libService`, `runtimeService`, db, etc.). 4. **Data is explicit.** Nothing is auto-injected the way desktop process variables are. Every piece of server data the form needs must be declared — via an `` variable, a `` (synced to the device, works offline), or fetched on demand from a named `` with `vCommunicationService.executeOnServer(...)`. ## Step 1 — Confirm you are inside a Vitruvio repo ```bash ls vitruvio.json 2>/dev/null && echo "OK" || echo "NOT_A_VITRUVIO_REPO" ``` If `NOT_A_VITRUVIO_REPO`, stop and tell the user to `cd` into the correct repo. ## Step 2 — Determine variant and gather the data contract Ask (only what is missing): - **Panel or process mobile form?** (see **Differences: panel vs process** below) - **Key** — the panel or process key (folder name). - For a **process**: which `formKey`(s) — they must match the `activiti:formKey` in `processes//.bpmn`. - **Which variables does the form need?** Because nothing is auto-injected, ask explicitly: - process variables to read (each becomes an `` `` and/or a Bridge call) - reference/lookup data (each becomes a `` backed by a `queries/*.sql`) - any server-only logic / libs needed (each becomes a ``) - **Offline?** Whether the data sources must be available without connectivity (affects `autoSyncOnInit` / `autoSyncOnDiscovery`). ## Differences: panel vs process The schema, component set, ServerSide/Bridge mechanics and JS runtimes below are **identical** for both. Only these differ: | | Panel mobile form | Process mobile form | |----------------|-------------------|---------------------| | File | `panels//-mobile.xml` | `processes//-mobile.xml` | | Root attribute | `` (no `processKey`) | `` | | Forms per file | one `
` | **one `` per BPMN `activiti:formKey`** (must match) | | Manifest | set `forms.mobile` **and** `showInMobileList: true` on the panel entry | set `forms.mobile` (and optionally `forms.mobileAlternative`) on the process entry | | Variables | use `engine.getGlobalVariable` / Autoload | process variables are fetched server-side via a Bridge (`runtimeService.getVariable`) and/or declared in `` | > Filename: name files by the **artifact key + suffix** — `-mobile.xml` (and > `-desktop.xml`, `.bpmn`). This keeps every form searchable by its key instead of > dozens of identical `form-mobile.xml` tabs. Older content used `form-mobile.xml` / > `form_web_mobile.xml`; the real path is whatever `forms.mobile` points to, so legacy files > still work — but new scaffolds use `-mobile.xml`. ## Step 3 — Scaffold the file ### Skeleton (process variant shown; for a panel drop `processKey` and use a single ``) ```xml Coleta Etapa de coleta no app { var n = parseInt(result, 10); execution.setVariable('numeroCarga', n).then(ok => {}).catch(err => { console.log('Erro ao setar numeroCarga localmente', err); }); }).catch(err => { console.log('Erro ao coletar numeroCarga no server', err); }); } ]]> nomeLoja numeroCarga ``` ## How libs and process variables reach the mobile form The mobile app cannot call `libService.loadScript(...)` or read process variables directly — those live on the server. The pattern is always **declare a Bridge, call it from the client**: ```xml ``` ```javascript // CLIENT-SIDE (modern JS): call the bridge, use the Promise result vCommunicationService.executeOnServer('imagemCodBarras', codigoBarras).then(base64 => { engine.getField('codigoImagem').setValue(base64); }).catch(err => console.log('Erro no bridge imagemCodBarras', err)); ``` Rules of thumb: - Anything needing a **platform lib, the database, or a platform service** → put it in a **Bridge** (server, ES5) and call it with `vCommunicationService.executeOnServer`. - **Reference/lookup tables** the form reads repeatedly → a **`QueryDataSource`** backed by a `queries/*.sql` (works offline once synced). - **Process variables** the form needs → either declare them in `` or fetch them in a Bridge via `runtimeService.getVariable(processInstanceId, 'varName')` and store locally with `execution.setVariable(...)`. - Client-side `execution.getVariable(...)` / `execution.setVariable(...)` are **async** and return Promises — use `.then()`. ## List-based entry: SubForm + ItemList For "add many items" screens (collect a list of rows), use a `SubForm` with an ``: ```xml Executar AÇÃO ``` Validate list completeness with `engine.getSubFormListSizeByStatus(function(size){ ... }, "all")` inside a `COMPLETE` validator. ## Step 4 — Manifest If run **standalone**, update the matching entry in `vitruvio.json`: - **Panel**: set `forms.mobile = "panels//-mobile.xml"` and `showInMobileList: true`. - **Process**: set `forms.mobile = "processes//-mobile.xml"`. Full entry creation is handled by **vitruvio-criar-painel** / **vitruvio-criar-processo**. ## Step 5 — Report Tell the user: - File created/updated and variant (panel/process). - Which variables/queries/bridges were declared, and that **only declared data is available** on the device — anything else must be added as an Autoload variable, QueryDataSource, or Bridge. - Reminder: client scripts are modern JS (Promises); Bridge bodies are server-side Rhino ES5. - For processes: each `
` must match an `activiti:formKey` in the BPMN. - Suggest reading `docs/components/mobile/` and an example (`~/.local/share/vitruvio-platform/examples/processes/*/form_web_mobile.xml`) for richer screens.