Files
jogos_matheus/panels/CLAUDE.md
T
2026-09-23 12:29:08 -03:00

20 KiB

Panels

Panels are UI screens defined entirely in XML. Vitruvio parses the XML against its XSD schema and renders Vaadin 7 components through a Java presenter layer — no Java UI code lives here.


vitruvio.json Registration

{
  "key": "my-panel",
  "name": "My Panel",
  "description": "Short description.",
  "category": "Category/Subcategory",
  "displayOrder": 10,
  "showInPresentation": false,
  "openInNewWindow": false,
  "showInMobileList": false,
  "displayTimeInSeconds": 0,
  "allowedGroups": ["group-key"],
  "allowedUsers": [],
  "forms": {
    "desktop": "panels/my-panel/my-panel-desktop.xml",
    "mobile": "panels/my-panel/my-panel-mobile.xml"
  }
}
  • displayOrder — use gaps (10, 20, 30…) to allow future insertions.
  • forms.mobile is optional. Omit if there is no mobile form.
  • allowedGroups and allowedUsers are additive — a user in any allowed group or listed directly gets access.

Desktop Form — File Structure

<?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="uniqueFormKey" width="100%" height="100%">
    <name>Human-readable title</name>
    <description>Short description.</description>

    <initScript language="JavaScript">
      <![CDATA[
        function run() {
          // called when the panel loads
        }
      ]]>
    </initScript>

    <components>
      <!-- root layout component -->
    </components>
  </form>
</panel-form>
  • formKey must be unique within the panel. By convention it matches the panel key.
  • <initScript> runs once when the panel is opened. The run() function is the entry point.
  • Everything in <components> is the Vaadin component tree.

Mobile Form — File Structure

Mobile forms use a different namespace and XSD, and support a <ServerSide> block for server-executed bridge functions.

<?xml version="1.0" encoding="UTF-8"?>
<panel-form xmlns="http://www.davinti.com.br/vitruvio/form/mobile/panel"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.davinti.com.br/vitruvio/form/mobile/panel
    https://bitbucket.org/davinTI/vitruvio-xds/raw/master/vitruvio-mobile-panel-form.xsd">

  <form formKey="uniqueFormKey">
    <name>Title</name>
    <description></description>

    <initScript language="JavaScript">
      <![CDATA[
        function run() { ... }
      ]]>
    </initScript>

    <ServerSide>
      <DataSources>
        <SQLBuilderScriptDataSource id="dsName" connection-key="datasource-key"
          language="JavaScript" workOnline="true">
          <![CDATA[
            function buildSQL(requestParams, sql_params) {
              return "SELECT ...";
            }
          ]]>
        </SQLBuilderScriptDataSource>
      </DataSources>

      <Bridges>
        <Bridge id="myBridge" language="JavaScript">
          <![CDATA[
            function execute(params) {
              // runs on the server — full access to libService, db, etc.
              return { result: ... };
            }
          ]]>
        </Bridge>
      </Bridges>
    </ServerSide>

    <components>
      <!-- mobile component tree -->
    </components>
  </form>
</panel-form>

Calling bridges from mobile client scripts

vCommunicationService.executeOnServer('myBridge', params).then(function(result) {
    // handle result
}).catch(function(error) {
    console.log('Error: ' + error);
});

Mobile timers

engine.startTimer('timerName', function() {
    // runs every intervalMs
}, intervalMs);

engine.stopTimer('timerName');

The engine API (Desktop)

engine is injected into all script contexts inside a panel.

Fields

var field = engine.getField('fieldId');
field.getValue();  // returns null when empty; for DB-backed fields returns a Java type — use == not === when comparing
field.setValue(value);
field.clear();
field.setEnabled(true/false);
field.setVisible(true/false);
field.setRequired(true/false);
field.focus();
field.refresh();           // DB components — re-runs the datasource query

Layouts

var layout = engine.getLayout('layoutId');
layout.getRootComposition().setVisible(true/false);
layout.getRootComposition().setCaption('New caption');
layout.getRootComposition().removeAllComponents();
layout.getRootComposition().addComponent(component);
layout.getRootComposition().setScrollTop(100000);   // scroll to bottom

// WindowLayout only
layout.showWindow();
layout.closeWindow();

User & Session

var user = engine.getLoggedUser();
user.getLogin();   // username string
user.getNome();    // display name (desktop)

Global variables — sharing state across components

// Set in initScript or any handler
engine.setGlobalVariable('myHelper', function() { ... });
engine.setGlobalVariable('myData', { key: 'value' });

// Read anywhere in the form
engine.getGlobalVariable('myHelper')();
var data = engine.getGlobalVariable('myData');

// Clean up
engine.unsetGlobalVariable('myVar');

Use global variables to expose reusable functions (e.g. limparCampos, carregarDados) and shared state across components and events.

Form validation

if (engine.isValid()) {
    // all required fields are filled
}

Repeating timers (desktop polling)

var timer = engine.registerRepeatingTimer(3000, function() {
    // do something every 3s
    // return true to keep running, false/undefined to stop
});
engine.setGlobalVariable('myTimer', timer);

// Later, to stop it:
engine.getGlobalVariable('myTimer').setEnabled(false);
engine.unsetGlobalVariable('myTimer');

Script Blocks and Events

initScript

Runs when the panel loads. Define helper functions here and expose them via setGlobalVariable so other components can call them.

<initScript language="JavaScript">
  <![CDATA[
    var db = libService.loadScript('db');
    var banco = new db(db.VITRUVIO_DATASOURCE);

    function carregarDados() { ... }

    function run() {
      engine.setGlobalVariable('carregarDados', carregarDados);
      carregarDados();
    }
  ]]>
</initScript>

Button click

<ButtonWidget id="btnSalvar" caption="Salvar" defaultIcon="SAVE" style="GREEN">
  <onClickScript language="JavaScript">
    <![CDATA[
      function run() {
        // handle click
      }
    ]]>
  </onClickScript>
</ButtonWidget>

Field value change

<TextField id="txfPesquisa" type="string" caption="Pesquisar" immediate="true">
  <events>
    <valueChange>
      <script language="JavaScript">
        <![CDATA[
          function run() {
            engine.getField('myTable').refresh();
          }
        ]]>
      </script>
    </valueChange>
  </events>
</TextField>

immediate="true" is required for valueChange to fire on every keystroke.


Layout Components

VerticalLayout / HorizontalLayout

<VerticalLayout width="100%" height="100%" spacing="true" margin="true"
  id="myLayout" align="TOP_LEFT" expandRatio="1" visible="true"
  backgroundColor="#f8b916">

Sizing fields inside layouts: always prefer width="100%" combined with expandRatio instead of fixed pixel widths. expandRatio controls the proportional share of the available space each child receives.

<HorizontalLayout width="100%" spacing="true">
  <NumericField id="nmfId"    width="100%" expandRatio="1" ... />
  <TextField    id="txfNome"  width="100%" expandRatio="3" ... />
  <ButtonWidget id="btnAcao"  width="100%" expandRatio="1" ... />
</HorizontalLayout>

Panel

A styled container with an optional caption and background color.

<Panel width="100%" height="100%" caption="Section title" backgroundColor="#f0f0f0" margin="true">
  <VerticalLayout>...</VerticalLayout>
</Panel>

CrudPanel

The standard page chrome: header bar + section content area.

Validators will reject it as a root element. Always wrap it in a Layout element like VerticalLayout, HorizontalLayout, TabLayout, or AbsoluteLayout as the root component inside <components>.

<VerticalLayout width="100%" height="100%">
  <CrudPanel width="100%" height="100%" expandRatio="1">
    <Header>
      <Caption>Page Title</Caption>
      <SubCaption>Subtitle text</SubCaption>
    </Header>
    <Section caption="Tab label" subCaption="..." showHeader="false" width="100%" height="100%">
      <VerticalLayout width="100%" height="100%">
        <!-- content — Section requires a layout component (VerticalLayout, HorizontalLayout, etc.) as its direct child, not bare components -->
      </VerticalLayout>
    </Section>
  </CrudPanel>
</VerticalLayout>

TabLayout

<TabLayout width="100%">
  <Tab caption="Tab 1">
    <!-- content -->
  </Tab>
  <Tab caption="Tab 2">
    <!-- content -->
  </Tab>
</TabLayout>

ScrollPanel

<ScrollPanel id="scrollArea" width="100%" height="100%">
  <VerticalLayout id="content" width="100%">
    <!-- scrollable content -->
  </VerticalLayout>
</ScrollPanel>

WindowLayout (modal)

<WindowLayout id="myModal" windowHeight="40%" windowWidth="40%"
  windowResizable="false" windowClosable="false" windowModal="true">
  <VerticalLayout width="100%" height="100%" margin="true">
    <!-- modal content -->
  </VerticalLayout>
</WindowLayout>

Open/close via engine.getLayout('myModal').showWindow() / .closeWindow().


Input Components

Common attributes

Attribute Purpose
id Required for engine.getField() access
caption Label above the component
width CSS width (100%, 200px)
expandRatio Flex grow ratio within parent layout
visible true/false
required Marks field as required for engine.isValid()
enabled true/false
description Tooltip text
immediate true to fire events on every change
maxLength Max character count (TextField, TextArea)

TextField / TextArea / NumericField / DateField

<TextField id="txfNome" type="string" caption="Nome" width="100%" required="true" maxLength="100" />
<TextArea id="txaDescricao" type="string" caption="Descrição" width="100%" />
<NumericField id="nmfCodigo" type="number" caption="Código" width="100%" enabled="false" />
<DateField id="dtInicio" type="date" caption="Data Início" resolution="DAY" width="100%" />

Label (HTML content)

<Label id="lblInfo" contentMode="HTML" align="MIDDLE_CENTER">
  <value>
    <![CDATA[
      <b style="font-size: 16px;">HTML content here</b>
    ]]>
  </value>
</Label>

ButtonWidget

<ButtonWidget id="btnAcao" caption="Label" defaultIcon="SAVE"
  style="GREEN" width="100%" height="30px" expandRatio="1" align="MIDDLE_RIGHT"
  keyCode="ENTER">

style values: BLUE, RED, GREEN, GRAY defaultIcon values: SAVE, ADD, EDIT, REMOVE, TRASH, SEARCH, BACK, SEND, ARROW_UP, ARROW_DOWN


DB Components

DBTable

The primary data grid. Fetches data from a SQL datasource.

<DBTable id="dbtItems" type="number" immediate="true" width="100%"
  height="100%" expandRatio="1" caption="Items"
  showRowCount="true" drawRefreshButton="true" selectable="false"
  rows="10" multivalue="false" exportXLS="true" useGridComponent="true">

  <datasource>
    <sqlBuilderDataSource connection-key="vitruvio" language="JavaScript">
      <![CDATA[
        function buildSQL(params) {
          // params is a Map<String, Object> — put values here and reference with ${name} in SQL.
          // Read form fields via engine (guard with if(engine) — datasource may run before form init).
          var search = engine ? engine.getField('txfPesquisa').getValue() : null;
          var searchValue = (search && String(search) != '') ? '%' + String(search) + '%' : '%';
          params.put('search', searchValue);
          return "SELECT * FROM MY_TABLE WHERE upper(NAME) LIKE upper(${search}) ORDER BY NAME";
        }
      ]]>
    </sqlBuilderDataSource>
  </datasource>

  <key-field>ID</key-field>

  <columns>
    <column name="ID" caption="Código" expand-ratio=".1" />
    <column name="NAME" caption="Nome" expand-ratio=".6" />
    <generated name="edit" caption="Editar" expand-ratio=".1">
      <!-- see Generated Columns below -->
    </generated>
  </columns>

  <bind>
    <parameter value-type="string" defaultValue="" parameterName="parFilter" field-ref="txfFilter" />
  </bind>

  <events>
    <valueChange>
      <script language="JavaScript">
        <![CDATA[
          function run() {
            var id = engine.getField('dbtItems').getValue();
            // react to row selection
          }
        ]]>
      </script>
    </valueChange>
  </events>

  <styleGenerator>
    <scriptGenerator language="JavaScript">
      <![CDATA[
        function getStyle() {
          return 'background-light-gray';
        }
      ]]>
    </scriptGenerator>
  </styleGenerator>

</DBTable>

Datasource variants:

  • <sqlBuilderDataSource> — dynamic SQL built in JavaScript; engine is available inside
  • <freeQuery connection-key="..."> — static SQL with ${paramName} tokens bound via <bind>
  • <form-datasource ref="dsId" /> — reference a <DataSources> definition (mobile)

Parameter syntax — important distinction:

  • sqlBuilderDataSource and freeQuery: use ${paramName} in SQL, bound via params.put() or <bind>
  • db library scripts (inline SQL): use :paramName syntax instead

Note: In sqlBuilderDataSource, always guard with if (engine) before calling engine.getField(...) because the datasource may be evaluated before the form is fully initialised.

Generated Columns (action buttons in table rows)

var vc = libService.loadScript('vaadinComponents');

function Generator() {
    this.generate = function(itemId, columnId, item, container) {
        var btn = vc.buttonIcon('Editar', function() {
            var nome = item.getItemProperty('NAME').getValue();
            engine.getField('txfNome').setValue(nome);
        }, 'edit');
        return btn;
    }
}
var script = new Generator();

For delete with confirmation dialog:

var vc = libService.loadScript('vaadinComponents');
importClass(Packages.br.com.davinti.base.vaadin.components.layout.ConfirmationBox);

function Generator() {
    this.generate = function(itemId, columnId, item, container) {
        var btn = vc.buttonIcon('Deletar', function() {
            var listener = new ConfirmationBox.ConfirmationBoxListener() {
                dialogEnd: function(context, action) {
                    if (action == ConfirmationBox.Action.YES) {
                        var db = libService.loadScript('db');
                        var banco = new db(db.VITRUVIO_DATASOURCE);
                        banco.update("DELETE FROM MY_TABLE WHERE ID = :id", { id: Number(itemId) });
                        engine.getField('dbtItems').refresh();
                    }
                }
            };
            ConfirmationBox.show(
                ConfirmationBox.DialogIcon.WARNING,
                'Confirmar exclusão',
                'Deseja excluir o item ' + item.getItemProperty('NAME').getValue() + '?',
                listener,
                ConfirmationBox.ACTION_YES_CANCEL
            );
        }, 'trash');
        return btn;
    }
}
var script = new Generator();

DBTwinColSelect (multi-select from DB)

Multi-select widget populated from a query.

Reading selected values: getValue() on a multivalue="true" DBTwinColSelect returns a Java Collection, not a JS array. Use the Java iterator API:

var selected = engine.getField('dbtwItems').getValue();
if (selected != null) {
    var iter = selected.iterator();
    while (iter.hasNext()) {
        var item = String(iter.next());
        // process item
    }
}

Setting values programmatically: use a Java ArrayList:

var itens = new java.util.ArrayList();
banco.query(sql, params).each(function(row) {
    itens.add(java.lang.Long.valueOf(row.ID));
});
engine.getField('dbtwItems').setValue(itens);

DBComboBox

Single-select dropdown from a query. Use .getValue() / .setValue(val) / .clear().


Feedback to the User

MessageBox (desktop)

importClass(Packages.br.com.davinti.base.vaadin.components.layout.MessageBox);

MessageBox.show(MessageBox.BoxType.SUCESS, 'Sucesso', 'Saved.');
MessageBox.show(MessageBox.BoxType.ERROR, 'Erro', 'Something failed.');
MessageBox.show(MessageBox.BoxType.WARNING, 'Atenção', 'Please check...');

Note: BoxType.SUCESS has no second S — that's the platform's spelling.

Tray notification (non-blocking)

var messages = libService.loadScript('messages');
var n = messages.notification;
n.show({
    caption: 'Saved',
    msg: 'Data saved successfully.',
    delay: 3,
    type: n.type.tray
});

MessageBox (mobile)

The mobile API is different:

MessageBox.show('Title', 'Message text');
MessageBox.showLoading('Loading...');
MessageBox.hideLoading();
MessageBox.confirm('Message', 'Title', [
    { text: 'Sim', handler: function() { ... } },
    { text: 'Não', handler: function() { ... } }
]);

Loading Scripts and Libraries

// In initScript (runs once, available to all handlers via globalVariable)
var libIA = libService.loadScript('IA_lib');
engine.setGlobalVariable('libIA', libIA);

// Inside a button or event handler
var db = libService.loadScript('db');
var banco = new db(db.VITRUVIO_DATASOURCE);

The vaadinComponents lib is used heavily in generated columns to create dynamic Vaadin components programmatically (buttons, layouts, labels, etc.).


Database in Panels

Same as in endpoints — see the root CLAUDE.md for db usage notes. Key extras for panels:

banco.getSequenceNextVal('MY_SEQUENCE');   // get next ID before insert
banco.transaction(function() {
    this.update(sql1, params1);
    this.update(sql2, params2);
});
banco.isOracle();   // true if Oracle — use for DB-dialect differences (SYSDATE vs CURRENT_TIMESTAMP, etc.)

Field ID Naming Conventions

Prefix field IDs by component type for readability:

Prefix Component
txf TextField
txa TextArea
ncf / nmf NumericField
dbt DBTable
dbb DBComboBox
dbtw DBTwinColSelect
btn ButtonWidget
chk CheckBox
lbl Label
dt DateField
cmb ComboBox

Common Patterns

Show/hide navigation between views

Use multiple layouts within the same form and toggle visibility:

engine.getLayout('layoutForm').getRootComposition().setVisible(true);
engine.getLayout('layoutList').getRootComposition().setVisible(false);
engine.getLayout('sectionHeader').getRootComposition().setCaption('New Caption');

CRUD in a single form

The recurring pattern across panels:

  1. List view (layoutHome) — DBTable + search field + "Add" button
  2. Form view (layoutAddItem) — fields + Save + Back buttons, initially visible="false"
  3. initScript exposes a limparCampos global that clears all form fields
  4. Edit button in DBTable populates fields and switches to form view
  5. Save checks engine.isValid(), upserts based on whether a key field has a value, then returns to list
  6. Back clears fields and returns to list

Reading system config

var valor = vConfigService.getSystemConfigAsString('CONFIG_KEY');

Getting the logged user in a script

engine.getLoggedUser().getLogin()   // desktop
engine.getLoggedUser().getNome()    // desktop display name
engine.getLoggedUser().getName()    // mobile display name