@j2inn/app

FIN application framework

The core FIN application framework.

The types and components in this project are used to create a UI application that can integrate seamlessly as part of the FIN framework.

APIs

Please click here for the API documentation.

Concepts

The shell UI refers to the outer UI that renders an application's view...

-------------------------------Shell---------------------------------
| Header | x | |
| App launcher / Target selector / | x | Sidebar target |
| Header app views | x | selector |
|-------------------------------------------| x | |
| | x | |
| Sidebar apps -> | x | |
| | x | |
| | - | |
| | x | |
| Dynamic -> | x | |
| sidebar apps | x | Sidebar app |
| Main app view | - | view |
| | x | |
| Quicklinks -> | x | |
| | x | |
| | | |
| | | |
| | | |
| | | |
---------------------------------------------------------------------

Main app views

  • Application views are selected via the app launcher.
  • Clicking an icon in the app launcher loads a new main app view.

Sidebar app views

  • Sidebar app icons can be clicked on to load a sidebar app view.
  • Dynamic sidebar apps can be added from a main app view. These sidebar apps only exist for the lifetime of the main app view.
    • Dynamic sidebars can also be registered via declared by declaring the associated app view ids by overriding AppView#associatedAppViewIds.

Header app views

  • Enables views to appear in the header. These are always visible irrespective of whether a main application view is open or not.

Editor app views

  • Enables views to be used as editors in the DB Builder application.
  • An editor is associated with both an app and an entity (i.e. a site for a site editor).
  • An editor has several properties implicity passed to it...
    • value: the current HVal representation of the value being editor.
    • onChange: an optional callback that takes an event with an updated HVal value and valid flag. This should be invoked everytime the editor makes a change.
    • readonly: an optional boolean that's true if the editor should be readonly and not editable.

Quicklinks

Quicklinks are also registered dynamicaly by a main app view. A quicklink can be any command a user want to invoke.

Targets

The server has a database of records. Each record has a unique id. For instance, there could be a record to represent a piece of equipment, a point or a site. Application views can work relative to a record. For an application a record's id is known as a target.

A target can be selected for a main application view via the target selector. Once a target has been selected, an application view can react to that target accordingly. For instance, a user loads a graphic and selects a boiler from the target selector. The graphic is using the information from the target (boiler) to display information. If the user then selects a different boiler, the graphic will then use that data to display its data.

A sidebar can also use the target from the target selector. A developer also has the option of allowing a user to select an alternative target via the sidebar target selector. This is useful for when a user wants to display some information about one thing on the left and then have something else on the right.

All target information can be accessed via the app root store...

const store = useAppRootStore()

// Current main target...
console.log(store.target)

// Current sidebar target...
console.log(store.targetSidebar)

Inter-app messaging

An advanced feature of the application architecture is the ability for an application to send and receive messages to other interested applications or the shell.

To send an message, an application uses the app root store...

// Send happy birthday from application A.
const store = useAppRootStore()
store.postAppMessage('happyBirthday', { date: new Date().toISOString() })

To receive a message, an application must first declare the messages the application is interested in. The application's store is used to handle the messages.

Remember that state can be held in the application's store and reflected in the application's views (via useAppStore()).

const myEventsApp: App = {
id: 'events',
messages: ['happyBirthday'],
store: {
onMessage({
message,
params,
}: {
message: string
params: Record<string, unknown>
}) {
console.log(message, params)
},
},
views: {
...
}
}

Types

App

The core interface for an application. An application's views are used to render the application's UI in a shell.

As well as defined application views, a user can also define a store that can be used throughout the application to hold stateful information.

App View

An application has many views. An application view can show in the main or sidebar areas. Each application view has its own icon.

Any application views for main can also specify a category that provides a form of organizational grouping.

The category name of host has special meaning. If no project is selected, the application main or header view will still be available to use.

App Store

An application may need to share information between its different views. This can be down using an app store. An app store is declared on the app.

An application store can be accessed via the useAppStore() react hook. For example...

const store = useAppStore<MyAppStore>()

At J2 Innovations we use Mobx that enables mutations in our stores to automatically update our React based UI.

App Root Store

To access information regarding the shell UI and environment, a developer can use the application root store. This is accessed via the useAppRootStore() hook...

const store = useAppRootStore()

// Print out the currently selected target.
console.log(store.target)

UI Components

Several useful UI components are provided so application views can have a common look and feel.

AppMainViewHeader

This can be used in an application's main view to render a common header. Any child components are rendered in the view headers top left hand corner. Here's an example main application view that uses this view header...

return (
<>
<AppMainViewHeader {...props}>
<TodoListCommands />
</AppMainViewHeader>
<TodoList />
</>
)

By default, the view header will display the application's icon and name.

AppSidebarViewHeader

This can be used in an application's sidebar view to render a common header. This component can also render child components. The showTargetSelector boolean property can be used to additionally render the sidebar target selector.

return (
<>
<AppSidebarViewHeader {...props} showTargetSeletor={true}>
<TodoListCommands />
</AppSidebarViewHeader>
<TodoList />
</>
)

Please note, it's recommended the sidebar target selector should only be shown if the application developer wants the user to select a different target from the main one.

AppViewTabs

A useful way to render tags in an application's main view.

Permissions

Application access

A user may or may not have access permissions to an application. These server side enforced permissions also need to be handled in an application's UI.

Each applicaton can be tested for read, write, delete or all access.

The access levels can be queried via the application's root store...

const rootStore = useAppRootStore()

if (!rootStore.hasAppAccess('myAppId', 'read')) {
return <div>No read access permissions!</div>
}

...

In addition, there's also a AppAccessChecker React component that's useful for rendering child UI if the user has the correct permissions...

<AppAccessChecker app='myAppId' access='read'>
<div>This text will only render if the user has `read` access to `myAppId`</div>
</AppAccessChecker>

Creating an app

Installing

An application can be created using the j2 cli tool. To install the tool globally...

npm install --global @j2inn/cli

Auto-generating a FIN application

Then run the CLI tool to create the skeleton boiler plate code for an application...

j2 init "Todo list" --internal

The internal flag is used because the APIs are still being tested and developed internally.

When the tool runs, remember to select the FIN platform and not FIN5.

Running

To build the application run...

npm install --force && npm run pod

Once built and deployed vai npm run pod, a FIN UI application runs as part of FIN...

  • The app's main view can be accessed via the app launcher.
  • The app's sidebar views can be accessed via the sidebar app launcher.

Structure

The UI components are built using React...

UI Components

AppMain

The UI component for the main view. This UI is launched via the FIN app launcher in FIN's main UI.

AppIcon

The icon for the application's main view. This is the icon used in the FIN app launcher.

appStore

The application's main data store that can be used for inter-app messaging. Any data that needs to be persisted between application views can be stored here. This store can be accessed in a view via the useAppStore() hook.

Localization

All language translation keys are stored in locale/en.json. Other language codes with translated values can be stored here. By default the name of the application and main application view are stored here.

Also note that in FIN, all translated values are namespaced by the application's main id. To access translated values in a FIN application's UI, the useI18n() hook should be used...

const {t} = useI18n()

// Print the application's name in a span...
return <span>{t('myAppId.name')}</span>

Lib

The application is lazily loaded into FIN's UI. In order for the system to understand what data is available before loading the UI components, some meta data is declared for each application. This is held in lib/lib.hayson.yaml and lib/app.hayson.yaml.

The structure of these files uses Haystack JSON. Please note, most of the world's IDEs have inbuilt support for Haystack JSON.

The data declared in these files are Haystack defs. When FIN starts, this data is normalized into the FIN's haystack namespace. It can then be queried by other applications accordingly.

For more information regarding Haystack please click here.

Development components

A generated application can be run in webpack dev mode. In order for this to happen, there are some special components created specifically for running an application view in its own isolated environment that doesn't require a full shell.

For instance, here's the code that creates a working enviroment to test out an application view...

const container = document.querySelector('app')

ReactDOM.render(
<DevAppContainer>
<AppMain />
</DevAppContainer>,
container
)

For more information, please see DevAppContainer and DevAppRootStore.

Haystack APIs

For information regarding Haystack APIs used to work with data and REST APIs from FIN, please use the haystack-core APIs...

Generated using TypeDoc