Repository navigation
Customizing the user interface
The cockpit came with a user interface written in React. It is an example of a cockpit user interface and not the only one there can be, and for a first deployment it is usually enough.
Since 0.9.0 the jars of the cockpit do not carry it any more, and its sources are gone from the
cockpit repository. They are in its Git history, under business-cockpit/src/main/webapp.
Releases says what that means for a derived cockpit. Change
the theme and you have your colours and your fonts; that is one file.
Beyond that there are two ways to go. You copy the React application and maintain the copy, or you build a user interface of your own against a written description of what one has to do. For a long time only the first way existed, so copying was the honest answer. The description exists now, and this page says what each way gets you.
-
Changing the look
The theme object, and the colour names the lists insist on. -
The packages
Which of the NPM packages a cockpit user interface uses and which a workflow module does. -
Copying the application
What a copy has to keep working, and what it costs. -
What a user interface has to answer
The three boundaries it sits on, whatever it is written in. -
The description it is built from
Where it lives, what it covers, and why it is not in the wiki. -
What is still missing
What the description does not answer yet, and the one thing React stays in charge of.
The delivered user interface is built on Grommet, and its theme is one
exported object, theme from @vanillabp/bc-shared. It is a Grommet ThemeType whose parts are
written with styled-components, and it sets colours, the font, and the look of tables, buttons,
form fields and layers.
The application applies it in one place:
<Grommet theme={theme} full>Replace that object with one of your own and everything follows. There is no configuration key for it, because a theme is code.
A few colour names are not decoration. The list components pick a colour by the state of a row, so a
theme of your own has to define list-default, list-new, list-updated, list-ended and
list-removed_from_list, their list-text-* counterparts, and list-refresh, link,
placeholder and accent-1 through accent-4. Leave one out and the rows in that state are drawn
without a colour.
The font is loaded separately, through @fontsource/roboto imports in the application's bootstrap
file, so a different font is a different import there.
Four packages were published, and which one you need depends on which side you are on. The versions on npmjs.com stay there, but the cockpit repository releases no new ones.
| Package | Who uses it |
|---|---|
@vanillabp/bc-official-gui-client |
A cockpit user interface. The client generated from the OpenAPI specification of the GUI API |
@vanillabp/bc-types |
Both. The TypeScript types: Column, ListItem, Person, Translatable, and the two enriched objects BcUserTask and BcWorkflow
|
@vanillabp/bc-shared |
Both. The components and types a workflow module writes its forms against, the default list cell, and the theme |
@vanillabp/bc-ui |
A cockpit user interface. The lists, the pages, and the part which loads a workflow module's federated components |
A workflow module needs the middle two and never bc-ui, which is what keeps a form from pulling a
whole cockpit in. User task forms and status sites is that side.
The React application was the library's src/main/webapp. Take it from the Git history of the
cockpit repository, copy it into your own application,
change what you want, and let your build produce the static files your Spring Boot application
serves.
Two things a copy has to keep. It has to set window.i18n to an i18next instance, because the
components of @vanillabp/bc-ui register their own texts on it and register nothing where it is
unset. And it has to keep the module federation configuration of the delivered application: the
cockpit shares react, react-dom, react-router-dom, grommet, i18next and react-i18next
with the modules it loads, and a federated form built against those versions fails when the host no
longer offers them.
What a copy costs is the usual thing. Every later release of the cockpit changes the original, and your copy does not follow along. Nothing tells you when the two have drifted apart, because what the cockpit expects of a user interface is not a signature your compiler checks.
A cockpit user interface sits on three boundaries, and they are the same three whatever it is written in.
flowchart TB
U["Your user interface<br>lists, pages, the frame"]
subgraph C["Business Cockpit server"]
STATIC["/static and /assets<br>serve your built files"]
GUI["GUI API /gui/api/v1<br>lists, search, actions, updates"]
PROXY["Proxy /wm/<module><br>to the workflow modules"]
end
W["Workflow module<br>UserTaskForm, WorkflowPage,<br>UserTaskList, WorkflowList"]
STATIC -- "the browser loads it from here" --> U
U -- "everything it knows" --> GUI
U -- "loads a part, through" --> PROXY
PROXY --> W
W -. "React components, inside your page" .-> U
The GUI API is everything it knows. There is no second source: no access to a workflow module's database, no business logic of its own, and no login page, because the cockpit authenticates every request and answers with a cookie. What a user may see is decided on the server, and what the server does not send does not exist for the browser. Security is that half.
The parts of a workflow module are loaded into its pages. A task opens a form, a case opens a status site, and the two lists ask each module for its columns and its cells. User task forms and status sites describes that contract from the module's side. The user interface is the host of that contract.
The cockpit application serves it. The built files go below /static or /assets, the only two
prefixes a browser reaches without a login, and application.spa-default-file names the document
which answers a deep link. Building a custom Business Cockpit
is the application around that.
One more thing belongs here although it is not a boundary: whoever writes forms for a workflow module needs a workbench, and that is the dev shell rather than a running cockpit. See Developing UI components locally.
Those three boundaries are written down, in enough detail to build a user interface without reading the React sources: skills/business-cockpit-user-interface, in this repository.
It is written in the skill format, so an agent can load it, generate the user interface your organisation wants in your framework and your component library, and maintain it afterwards. A person reads it just as well. It covers:
- the GUI API, with paging, sorting, filtering by reported business data, and the update stream
- the module federation contract
- which types are the contract and which are only a convenience
- the behaviour a user expects of a list
- the dev shell
- the places where the obvious thing turns out to be the wrong thing
It lives in this repository, not in this wiki and not in vanillabp/skills with the other skills, for one reason: it describes this repository's own interfaces. Next to the code, a change which makes it wrong shows up in the same pull request as the change itself. Anywhere else it would go stale on a schedule of its own, and nobody would notice until somebody built against it. Where the description and the code disagree, the code is right and the description is the defect.
The skill named business-cockpit in vanillabp/skills points
here for everything about the user interface rather than repeating any of it.
One note for now: the text is German until it has been reviewed, and it will be translated afterwards. Everything else, this wiki included, stays English.
Two things, and both are worth knowing before you plan around them.
The forms of the workflow modules are React, whichever framework you choose. They are published as
React libraries, and nothing packages them as web components, whatever an older README may have
said. So a user interface in another framework builds its own lists and its own pages, and it still
brings React along and shares it, because that is what a loaded form runs in. Only a module whose
ui-uri-path is a whole address, starting with http:// or https://, gets around React, because
it is opened in a window of its own. The cockpit reads no value of ui-uri-type, so a user
interface of your own agrees on the names with the modules it loads.
Nothing checks the description against a running cockpit. A test which drives a generated user interface against the GUI API is the only way to notice that the two have drifted apart, and it does not exist yet. Until it does, the description is kept right by hand. Where nobody could say what the intended behaviour was, it names the gap and does not invent an answer.
