Files
jogos_matheus/.claude/commands/vitruvio-criar-form-desktop.md
2026-09-23 12:29:08 -03:00

280 lines
10 KiB
Markdown

# Create Vitruvio Desktop Form
> All messages shown to the user must be written in Portuguese.
Desktop forms are Vaadin 8 forms defined in XML and rendered by the Vitruvio engine. This
skill creates the **desktop** form. There are two variants that share almost all of their
component vocabulary but differ in their root element and how variables flow — see
**Differences: panel vs process** below.
## 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 target
Ask (only what is missing):
- **Panel or process form?** (decides root element / file location — see below)
- **Key** — the panel or process key (the folder name).
- For a **process**, which `formKey`(s) are needed — they must match the `activiti:formKey`
values in `processes/<key>/<key>.bpmn`.
- **What should the form show/do?** — fields and behaviour, so you can scaffold something useful.
## Differences: panel vs process
| | Panel | Process |
|----------------|-------|---------|
| File | `panels/<key>/<key>-desktop.xml` | `processes/<key>/<key>-desktop.xml` |
| Root element | `<panel-form>` | `<forms>` (with `processKey` attribute optional) |
| Namespace | `http://www.davinti.com.br/vitruvio/form/panel` | `http://www.davinti.com.br/vitruvio/form` |
| XSD | `vitruvio-panel-form.xsd` | `vitruvio-form.xsd` |
| Forms per file | exactly one `<form>` | **one `<form formKey>` per BPMN `activiti:formKey`** |
| Variables | none built-in; use `engine.getGlobalVariable` | process variables: `engine.getVariable`/`setVariable`; submitted field `id="X"` in `formKey="A"` → variable `A_X` |
| `<library>` | not used | optional: shared `<complex-component id>` reused via `<component-ref refId>` |
Everything below (components, DBTable, engine API, ES5 rules) is **identical** for both.
## Step 3 — Scaffold the file
### Panel variant — `panels/<key>/<key>-desktop.xml`
```xml
<?xml version="1.0" encoding="UTF-8"?>
<panel-form xmlns="http://www.davinti.com.br/vitruvio/form/panel"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.davinti.com.br/vitruvio/form/panel https://bitbucket.org/davinTI/vitruvio-xds/raw/master/vitruvio-panel-form.xsd">
<form formKey="<key>" width="100%" height="100%">
<name><name></name>
<description><description></description>
<initScript language="JavaScript">
<![CDATA[
function run() {
// called once when the panel opens
}
]]>
</initScript>
<components>
<VerticalLayout spacing="true" margin="true" width="100%" height="100%">
<!-- add widgets here -->
</VerticalLayout>
</components>
</form>
</panel-form>
```
### Process variant — `processes/<key>/<key>-desktop.xml`
```xml
<?xml version="1.0" encoding="UTF-8"?>
<forms xmlns="http://www.davinti.com.br/vitruvio/form"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.davinti.com.br/vitruvio/form https://bitbucket.org/davinTI/vitruvio-xds/raw/master/vitruvio-form.xsd">
<!-- Optional: shared components used across multiple forms -->
<library>
<!-- <complex-component id="libShared">...</complex-component> -->
</library>
<!-- One <form> per activiti:formKey in the BPMN -->
<form formKey="formAbertura" width="100%">
<name>Abertura</name>
<description>Abertura do processo</description>
<initScript language="JavaScript">
<![CDATA[
function run() {
// called when the form opens
}
]]>
</initScript>
<components>
<VerticalLayout spacing="true" margin="true" width="100%">
<TextField id="descricao" type="string" caption="Descrição" width="100%" required="true" />
</VerticalLayout>
</components>
</form>
<form formKey="formExecutar" width="100%">
<name>Executar</name>
<description>Etapa de execução</description>
<initScript language="JavaScript">
<![CDATA[
function run() {
// read a process variable set during a previous step
// var valor = engine.getVariable('formAbertura_descricao');
}
]]>
</initScript>
<components>
<VerticalLayout spacing="true" margin="true" width="100%">
<TextField id="resultado" type="string" caption="Resultado" width="100%" required="true" />
<ComboBox id="aprovado" type="string" caption="Aprovado?" required="true" allowNullSelection="false">
<entry key="1" value="Sim"/>
<entry key="0" value="Não"/>
</ComboBox>
</VerticalLayout>
</components>
</form>
</forms>
```
## Components (78 desktop components)
Before using a component you are unsure about, read its reference:
`~/.local/share/vitruvio-platform/docs/components/desktop/<ComponentName>.md`
(full list in `docs/components/INDEX.md`).
### Layout
| Component | Common attributes |
|---|---|
| `VerticalLayout` | `spacing`, `margin`, `width`, `height`, `expandRatio` |
| `HorizontalLayout` | same as above |
| `Panel` | `id`, `caption`, `width`, `height`, `margin` |
| `TabLayout` | `id`, `width`, `framed` — contains `<Tab caption="...">` children |
### Widgets
| Component | Key attributes |
|---|---|
| `TextField` | `id`, `type` (`string`/`number`), `caption`, `width`, `required` |
| `NumericField` | `id`, `type`, `caption`, `width`, `visible` |
| `DateField` | `id`, `type` (`date`/`datetime`), `caption`, `resolution` (`DAY`/`MINUTE`) |
| `ComboBox` | `id`, `type`, `caption`, `allowNullSelection` — children: `<entry key="..." value="..."/>` |
| `Label` | `id`, `width`, `contentMode` (`HTML`/`TEXT`) — child: `<value>...</value>` |
| `ButtonWidget` | `id`, `caption`, `style` (`GREEN`/`RED`/`DEFAULT`), `defaultIcon` — child: `<onClickScript>` |
| `RichTextArea` | `id`, `type`, `caption`, `width`, `height` |
| `ImageWidget` | `id`, `width`, `height` — child: `<image><base64 extension="png">...</base64></image>` |
### DBTable (data grid)
```xml
<DBTable id="tbDados" type="string" width="100%" rows="8"
exportXLS="true" showRowCount="true" selectable="true">
<datasource>
<!-- Option A: static query -->
<freeQuery connection-key="vitruvio_producao">
<![CDATA[
SELECT col1, col2
FROM my_table
WHERE param = ${myParam}
]]>
</freeQuery>
<!-- Option B: dynamic query built in JS -->
<sqlBuilderDataSource connection-key="vitruvio_producao" language="JavaScript">
<![CDATA[
function buildSQL(params) {
var sql = 'SELECT col1, col2 FROM my_table WHERE 1=1';
var val = engine.getField('myFilter').getValue();
if (val) {
sql += ' AND col1 = ${val}';
params.put('val', val);
}
return sql;
}
]]>
</sqlBuilderDataSource>
</datasource>
<key-field>CHAVE</key-field>
<columns>
<column name="COL1" caption="Column 1" expand-ratio="1"/>
<column name="COL2" caption="Column 2" expand-ratio="2"/>
<generated name="Action" expand-ratio="0.5">
<scriptColumnGenerator language="JavaScript">
<![CDATA[
function Generator() {
var com = libService.loadScript('vaadinComponents');
this.generate = function(itemId, columnId, item, container) {
var btn = com.buttonIcon('action', function() {
var id = item.getItemProperty('CHAVE').getValue();
// do something
}, 'edit');
return com.horizontalLayout([btn]);
}
}
var script = new Generator();
]]>
</scriptColumnGenerator>
</generated>
</columns>
<bind>
<parameter value-type="number" defaultValue="0" parameterName="myParam" field-ref="otherTable"/>
</bind>
</DBTable>
```
**SQL in datasources:** use `${paramName}` for substitution — NOT `:paramName`. Named
params (`:paramName`) are only for `queries/*.sql` files.
## engine API
```javascript
// Fields
engine.getField('id').getValue()
engine.getField('id').getConvertedValue() // typed value (number, date, etc.)
engine.getField('id').setValue(value)
engine.getField('id').setEnabled(bool)
engine.getField('id').setVisible(bool)
engine.getField('id').setRequired(bool)
engine.getField('id').setCaption('new caption')
engine.getField('id').refresh() // DBTable — re-run its query
// Widgets / layouts
engine.getWidgetController('id').getButton()
engine.getLayout('id').getSelectedTab()
// User
engine.getLoggedUser().getLogin()
engine.getLoggedUser().getNome()
// Global variables (survive tab changes within a session)
engine.setGlobalVariable('key', value)
engine.getGlobalVariable('key')
// Process forms only — process variables:
engine.getVariable('varName')
engine.setVariable('varName', value)
engine.getProcessDefinitionId()
engine.formKey() // current form's formKey
engine.getFormName()
// Open another panel / load a library
var vUI = libService.loadScript('vUI');
vUI.showPanel('panelKey', { param1: value1 });
var lib = libService.loadScript('scriptKey');
```
## Script rules (Rhino ES5)
Desktop form scripts run on **Rhino ES5** — no `let`/`const`, arrow functions, template
literals, destructuring, `class`, `import/export`. Use `var`, string `+` concat,
`JSON.parse`/`JSON.stringify`, `importClass(Packages.some.java.Class)` for Java interop.
(See the repo `CLAUDE.md` "JavaScript — ES5 / Rhino Engine" section.)
> Note: this ES5 rule is for **desktop**. Mobile forms use modern JS on the client — see
> **vitruvio-criar-form-mobile**.
## Step 4 — Manifest
If run **standalone**, set `"forms"."desktop"` to the file path on the existing panel or
process entry in `vitruvio.json`. Full entry creation is handled by **vitruvio-criar-painel**
/ **vitruvio-criar-processo**.
## Step 5 — Report
Tell the user:
- File created/updated and which variant (panel/process).
- For processes: each `<form formKey>` must match an `activiti:formKey` in the BPMN, and
submitted field `id="X"` in `formKey="A"` becomes process variable `A_X`.
- `run()` in `<initScript>` is called every time the form opens.
- `${paramName}` for SQL substitution in datasources; `:paramName` only in named query files.