Client architecture

Client

A client part of the application consists of two React-based SPA:

  • Admin - the admin panel
  • Frontend - the application that builds forms and login pages for unauthorized users login.jsx. All three pages are located in the /wwwroot/js/app/ folder.

Admin

A React-based SPA. The main page, admin.jsx, is located in the /wwwroot/js/app/admin.jsx folder. This page contains a single React component, DWKitAdmin, and it is this component that represents the Admin application. Below is an example of its default settings:

    <DWKitAdmin
        apiUrl="/configapi"
        workflowApi="/workflow/designerapi"
        imageFolder="/images/"
        deltaWidth={0}
        deltaHeight={0}
        controlActions={globalActions}
        returnToAppUrl="/"
    />
  • apiUrl - the URL of the API method of the ConfigAPIController controller. This method is used to edit all metadata in Data Model, Forms, Server and client user-defined functions and other systems, except for the workflow schemes.
  • workflowApi - the URL of the DesignerAPI method of the WorkflowController controller. Find more details on how Workflow Designer connects to the server in the Workflow Engine documentation.

  • imageFolder - the server URL of a folder with images.
  • controlActions - a list of the system Client user-defined functions that are delivered with the system.

Below are some other important points on the work of Admin application that should be taken into account.

  • The Admin application is fully isolated from the Frontend application on the client side. They interact only via the metadata stored on the server.
  • Interaction between the Admin application and the server occurs only via the ConfigAPIController.API() and WorkflowController.DesignerAPI methods.
  • Usually, there is no need to make changes to the admin panel.

Frontend

A React-based SPA. The main page, app.jsx, is located in the /wwwroot/js/app/app.jsx folder. Normally, you won't have to make changes to this page. However, let's take a look at what the page contains. We are interested only in the layout that is returned by the component. It may seem too big and complicated, but what really matters to us is the settings of only five components of DWKitForm.

...
 return <div className="dwkit-application" key={this.state.pagekey}>
                    <DWKitForm {...sectorprops} formName="header" data={{currentUser: user.name}} modelurl="/ui/form/header" />
                    <div className="dwkit-application-basecontent">
                        <DWKitForm {...sectorprops} formName="top" data={{currentEmployee: currentEmployee}} modelurl="/ui/form/top"/>
                        <div className="dwkit-application-content">
                            <Provider store={Store}>
                                <BrowserRouter>
                                    <div className="dwkit-application-content-form">
                                        <ApplicationRouter onRefresh={this.onRefresh.bind(this)}/>
                                        <NotificationComponent 
                                            onFetchStarted={this.onFetchStarted.bind(this)} 
                                            onFetchFinished={this.onFetchFinished.bind(this)}/>
                                        <Switch>
                                            <Route path='/form' component={FormContent}  />
                                            <Route exact path='/'>
                                                <FormContent formName="Documents" />
                                            </Route>
                                            <Route nomatch render={() => {
                                                //Hack for back button
                                                let url = window.location.href;
                                                history.back();
                                                window.location.href = url;
                                                return null;
                                            }} />
                                        </Switch>
                                    </div>
                                </BrowserRouter>
                            </Provider>
                        </div>
                    </div>
                    <DWKitForm {...sectorprops} formName="footer" modelurl="/ui/form/footer" />
                </div>;
...
  • <DWKitForm ... formName="header" modelurl="/ui/form/header" /> - this component draws a default application header that is always drawn on the application page. To edit this component in the Admin application, open the form with the name header in the Manage forms section.
  • <DWKitForm ... formName="top" modelurl="/ui/form/top"/> - this component draws a top menu bar, loads the form with the name top, which can also be edited in Admin/Manage forms
  • <DWKitForm ... formName="footer" modelurl="/ui/form/footer" /> - this component draws an application footer, loads the form with the name footer, which can also be edited in Admin/Manage forms.

Apparently, these are the three forms constructed in the form builder of the Admin application. These forms display fixed content, such as menu, and their composition can change depending on your needs.

There are also two <DWKitForm/> components on the main page, which display dynamic forms:

  • the code below displays the form with the name Documents by default. In other words, this is the first form a user sees immediately after loading the application.

    <Route exact path='/'>
        <FormContent formName="Documents" />
    </Route>
  • <Route path='/form' component={FormContent} /> a form will be uploaded dynamically here. In case the page url is /form/{formName}/, a form with the name formName will be uploaded.

That is all there is to the key features of the application. The only requirement to the main page is that it shouldn't simultaneously display more than one <DWKitForm/> form displayed dynamically based on the url state, in other words, the one that doesn't have modelurl property given.

It is also crucial how the form interacts with the application state. Physically, in DWKit, it is determined by React + Redux combination. Good if you are familiar with these technologies, but if not, it doesn't mean you won't be able to use DWKit at full capacity as DWKit users don't have to deal with them directly.

Each application has a State that contains the following information (you can find more details on the structure of State in the Application section):

  • Description of the form or its Model is what determines the composition and location of controls in the main and child modal forms.
  • Data of the form - the data to be displayed. There are always two copies of the data, Original and Modified, i.e. those that were loaded from the server and those that are not displayed in the form.
  • Information about the current user and the impersonated user
  • list of errors in the form
  • security permissions
  • URL of the current page and link history

A control in the form can send the event that is processed by the sequence of calls of actions. For example, a typical sequence of processing an onClick event of the Save and Exit button is a call of three actions validate,save,exit, of the Save button - validate,save,refresh, of the Cancel button - exit. Each action is a function to which the following parameters are necessarily passed:

  • State of the application
  • Currently displayed Data
  • Other auxiliary parameters, for more details refer to the Application section

In this function you can:

  • change Data
  • return the delta of State change
  • return Promise to perform asynchronous operations

After calling the chain of actions, the cumulative change of State and/or Data is applied to State of the application that gets refreshed and influences drawing of the form controls.

There are the following system actions in the system:

  • save - saving a form on the server
  • validate - validating a form
  • apply - the analogue of saving for modal forms
  • exit - exit to the main page
  • redirect - redirection to the page set in the parameters
  • refresh - refreshment of a page
  • gridEdit, gridCreate, gridCopy, gridDelete, gridRefresh - key operations for the control of the grid type
  • workflowExecuteCommand, workflowSetState - execution of a workflow command, setting a state

It is also possible to write your own client user-defined actions in section Action Handlers of the admin panel.

Frontend application interacts with the server via the following controllers:

  • UserInterfaceController - getting a form via the GetForm method.
  • DataController - getting data via the GetData method, changing of data via the ChangeData method, deleting of data via the DeleteData method.
  • WorkflowController - getting a list of commands and states via the GetData method, execution of commands via the ExecuteCommand method, setting of states via the SetState method.