Skip to content

Statistics blocks

Statistics blocks widget is a tool designed to display aggregated data in a visually accessible format. This data can be sourced either from a database or from external sources.

Basics

You have the option to utilize custom field names for standard properties such as color, icon, etc. When doing so, you'll need to establish mappings for these fields to standard criteria

Custom fields: Live Sample GitHub

Default fields: Live Sample GitHub

How does it look?

img_stat.png

How to add?

Example

You have the option to utilized default field names for standard properties such as color, icon, etc. When doing so, you'll not need to establish mappings for these fields to standard criteria

Step1 Create DataResponseDTO with custom fields.

@Getter
@Setter
@NoArgsConstructor
public class MyExampleDTO extends DataResponseDTO {

    private String title;

    private Long value;

    private String color;

    private String description;

    private String icon;
}
Step2 Create DAO extends AbstractAnySourceBaseDAO<> implements AnySourceBaseDAO.

Title. Optional

Value - field that specifies formulas for data aggregation

Icon. Optional

Color. Optional

Description - comment for field. Optional

Id - We recommend assigning unique identifiers to each block. This is essential for the proper functioning of the application and for enabling drilldown.

@Service
@RequiredArgsConstructor
public class MyExampleDao extends AbstractAnySourceBaseDAO<MyExampleDTO> implements
        AnySourceBaseDAO<MyExampleDTO> {

    public static final String COUNT_ROW_ID = "0";

    public static final String SUM_CUSTOM_FIELD_NUM = "1";


    private final MyEntityRepository repository;

    @Override
    public String getId(final MyExampleDTO entity) {
        return entity.getId();
    }

    @Override
    public void setId(final String id, final MyExampleDTO entity) {
        entity.setId(id);
    }

    @Override
    public MyExampleDTO getByIdIgnoringFirstLevelCache(final BusinessComponent bc) {
        return getStats().stream().filter(s -> Objects.equals(s.getId(), bc.getId())).findFirst().orElse(null);
    }

    @Override
    public void delete(final BusinessComponent bc) {
        throw new IllegalStateException();
    }

    @Override
    public Page<MyExampleDTO> getList(final BusinessComponent bc, final QueryParameters queryParameters) {
        return new PageImpl<>(getStats());
    }

    @Override
    public MyExampleDTO update(BusinessComponent bc, MyExampleDTO entity) {
        throw new IllegalStateException();
    }

    @Override
    public MyExampleDTO create(final BusinessComponent bc, final MyExampleDTO entity) {
        throw new IllegalStateException();
    }

    @NonNull
    private List<MyExampleDTO> getStats() {
        List<MyExampleDTO> result = new ArrayList<>();
        MyExampleDTO newRow = new MyExampleDTO()
                .setTitle("All record")
                .setValue(repository.count())
                .setIcon("team")
                .setDescription("Count rows in table");
        newRow.setId(COUNT_ROW_ID);
        result.add(newRow);
        MyExampleDTO newSum = new MyExampleDTO()
                .setTitle("Custom Field Num Total")
                .setValue( repository.customTotal())
                .setIcon("team")
                .setDescription("Custom Field Num Total");
        newRow.setId(COUNT_ROW_ID);
        newSum.setId(SUM_CUSTOM_FIELD_NUM);
        result.add(newSum);

        return result;
    }

}
Step3 Create Meta extends AnySourceFieldMetaBuilder.
@Service
public class MyExampleMeta extends AnySourceFieldMetaBuilder<MyExampleDTO> {

    @Override  
   // --8<-- [start:buildRowDependentMeta]
    public void buildRowDependentMeta(RowDependentFieldsMeta<MyExampleDTO> fields, BcDescription bc,
                                      String id, String parentId)  {
    }
   // --8<-- [end:buildRowDependentMeta]
    @Override
    public void buildIndependentMeta(FieldsMeta<MyExampleDTO> fields,  BcDescription bc, String parentId) {
    }

}
Step4 Create Service extends AnySourceVersionAwareResponseService.
@Service
public class MyExampleService extends AnySourceVersionAwareResponseService<MyExampleDTO, MyExampleDTO> {


    public MyExampleService() {
        super(MyExampleDTO.class, MyExampleDTO.class, MyExampleMeta.class, MyExampleDao.class);

    }

    @Override
    protected CreateResult<MyExampleDTO> doCreateEntity(MyExampleDTO entity, BusinessComponent bc) {

        return new CreateResult<>(entityToDto(bc, entity));
    }

    @Override
    protected ActionResultDTO<MyExampleDTO> doUpdateEntity(MyExampleDTO entity, MyExampleDTO data, BusinessComponent bc) {
        return new ActionResultDTO<>(entityToDto(bc, entity));
    }

}

Step5 Create Widget with type StatsBlock !!! tips fields. We recommend including all fields used in the widget within this block. This maintains the principle of consistency in your application

{
  "name": "MyExampleList",
  "title": "StatsBlock title",
  "type": "StatsBlock",
  "bc": "myExampleBc",
  "fields": [
    {
      "title": "value",
      "key": "value",
      "type": "number"
    },
    {
      "title": "title",
      "key": "title",
      "type": "input"
    },
    {
      "title": "icon",
      "key": "icon",
      "type": "input"
    },
    {
      "title": "description",
      "key": "description",
      "type": "hint"
    }
  ],
  "options": {
  }
}

!!! tips To display statistical blocks on the same screen where data is added, you need to add RefreshBC property.

You have the option to utilize custom field names for standard properties such as color, icon, etc. When doing so, you'll need to establish mappings for these fields to standard criteria

Step1 Create DataResponseDTO with custom fields.

@Getter
@Setter
@NoArgsConstructor
public class MyExampleDTO extends DataResponseDTO {

    private String customFieldTitle;
    private String customFieldValue;
    private String customFieldIcon;
    private String customFieldDescription;
    private String customField;
    private String customFieldNum;
}
Step2 Create DAO extends AbstractAnySourceBaseDAO<> implements AnySourceBaseDAO.

Title. Optional

Value - field that specifies formulas for data aggregation

Icon. Optional

Color. Optional

Description - comment for field. Optional

Id - We recommend assigning unique identifiers to each block. This is essential for the proper functioning of the application and for enabling drilldown.

@Service
@RequiredArgsConstructor
public class MyExampleDao extends AbstractAnySourceBaseDAO<MyExampleDTO> implements
        AnySourceBaseDAO<MyExampleDTO> {

    public static final String COUNT_ROW_ID = "0";

    public static final String SUM_CUSTOM_FIELD_NUM = "1";


    private final MyEntityRepository repository;

    @Override
    public String getId(final MyExampleDTO entity) {
        return entity.getId();
    }

    @Override
    public void setId(final String id, final MyExampleDTO entity) {
        entity.setId(id);
    }

    @Override
    public MyExampleDTO getByIdIgnoringFirstLevelCache(final BusinessComponent bc) {
        return getStats().stream().filter(s -> Objects.equals(s.getId(), bc.getId())).findFirst().orElse(null);
    }

    @Override
    public void delete(final BusinessComponent bc) {
        throw new IllegalStateException();
    }

    @Override
    public Page<MyExampleDTO> getList(final BusinessComponent bc, final QueryParameters queryParameters) {
        return new PageImpl<>(getStats());
    }

    @Override
    public MyExampleDTO update(BusinessComponent bc, MyExampleDTO entity) {
        throw new IllegalStateException();
    }

    @Override
    public MyExampleDTO create(final BusinessComponent bc, final MyExampleDTO entity) {
        throw new IllegalStateException();
    }

    @NonNull
    private List<MyExampleDTO> getStats() {
        List<MyExampleDTO> result = new ArrayList<>();
        MyExampleDTO newRow = new MyExampleDTO()
                .setCustomFieldTitle("All record")
                .setCustomFieldValue(String.valueOf(repository.count()))
                .setCustomFieldIcon("team")
                .setCustomFieldDescription("Count rows in table");
        newRow.setId(COUNT_ROW_ID);
        result.add(newRow);
        MyExampleDTO newSum = new MyExampleDTO()
                .setCustomFieldTitle("Custom Field Num Total")
                .setCustomFieldValue(String.valueOf(repository.customTotal()))
                .setCustomFieldIcon("team")
                .setCustomFieldDescription("Sum customFieldNum");
        newSum.setId(SUM_CUSTOM_FIELD_NUM);
        result.add(newSum);

        return result;
    }

}
Step3 Create Meta extends AnySourceFieldMetaBuilder.
@Service
public class MyExampleMeta extends AnySourceFieldMetaBuilder<MyExampleDTO> {

    @Override
   // --8<-- [start:buildRowDependentMeta]
    public void buildRowDependentMeta(RowDependentFieldsMeta<MyExampleDTO> fields, BcDescription bc,
                                      String id, String parentId)  {
    }
   // --8<-- [end:buildRowDependentMeta]
    @Override
    public void buildIndependentMeta(FieldsMeta<MyExampleDTO> fields,  BcDescription bc, String parentId) {
    }

}
Step4 Create Service extends AnySourceVersionAwareResponseService.
@Service
public class MyExampleService extends AnySourceVersionAwareResponseService<MyExampleDTO, MyExampleDTO> {


    public MyExampleService( ) {
        super(MyExampleDTO.class, MyExampleDTO.class, MyExampleMeta.class, MyExampleDao.class);

    }

    @Override
    protected CreateResult<MyExampleDTO> doCreateEntity(MyExampleDTO entity, BusinessComponent bc) {

        return new CreateResult<>(entityToDto(bc, entity));
    }

    @Override
    protected ActionResultDTO<MyExampleDTO> doUpdateEntity(MyExampleDTO entity, MyExampleDTO data, BusinessComponent bc) {
        return new ActionResultDTO<>(entityToDto(bc, entity));
    }



}
Step5 Create Widget with type StatsBlock

Tips

fields.We recommend including all fields used in the widget within this block. This maintains the principle of consistency in your application

options.stats - This map how custom fields are matched to standard properties.

{
  "name": "MyExampleList",
  "title": "StatsBlock title",
  "type": "StatsBlock",
  "bc": "myExampleBc",
  "fields": [
    {
      "title": "Custom Field Title",
      "key": "customFieldTitle",
      "type": "input"
    },
    {
      "title": "Custom Field Value",
      "key": "customFieldValue",
      "type": "input"
    },
    {
      "title": "Custom Field Value",
      "key": "customFieldDescription",
      "type": "input"
    },
    {
      "title": "Custom Field Value",
      "key": "customFieldIcon",
      "type": "input"
    },
    {
      "title": "Custom Field Total",
      "key": "customFieldNum",
      "type": "input"
    }
  ],
  "options": {
    "stats": {
      "valueFieldKey": "customFieldValue",
      "titleFieldKey": "customFieldTitle",
      "iconFieldKey": "customFieldIcon",
      "descriptionFieldKey": "customFieldDescription"
    }
  }
}

Tips

To display statistical blocks on the same screen where data is added, you need to add RefreshBC property.

Main visual parts

We can modify the following parameters on this widget:

  • Title. Optional
  • Value - field that specifies formulas for data aggregation
  • Description - comment for field. Optional

img_stat_description.png

Title

Title Basic

Title - a name displayed in a block. It is optional.

Live Sample · GitHub

There are types of:

  • constant title: shows constant text.
  • constant title empty.

How does it look?

img_title.png

img_no_title.png

How to add?

Example

Step1 Add name for title to .widget.json.

{
  "name": "MyExampleStat",
  "title": "StatsBlock title",
  "type": "StatsBlock",
  "bc": "myexample",
  "fields": [
    {
      "title": "value",
      "key": "value",
      "type": "number",
      "bgColor": "#edaa"
    },
    {
      "title": "title",
      "key": "title",
      "type": "input"
    },
    {
      "title": "icon",
      "key": "icon",
      "type": "input"
    },
    {
      "title": "description",
      "key": "description",
      "type": "hint"
    }
  ]
}

Step1 No use parameter title to .widget.json.

{
  "name": "MyExampleStat",
  "title": "StatsBlock title",
  "type": "StatsBlock",
  "bc": "myexample",
  "fields": [
    {
      "title": "value",
      "key": "value",
      "type": "number",
      "bgColor": "#edaa"
    },
    {
      "title": "icon",
      "key": "icon",
      "type": "input"
    },
    {
      "title": "description",
      "key": "description",
      "type": "hint"
    }
  ]
}

Title Color

not applicable

Business component

This specifies the business component (BC) to which this form belongs. A business component represents a specific part of a system that handles a particular business logic or data.

see more Business component

Show condition

not applicable

Fields

Options layout

options.layout - no use in this type.

Actions

Actions show available actions as separate buttons see Actions

Additional properties

Icon

Live Sample · GitHub

Icon - picture representing a particular function.Optional Example icon)

There are types of:

  • icon.
  • icon empty.

How does it look?

icon.png

empyicon.png

How to add?

Example

Step1 Add name for title to .widget.json.

{
  "name": "MyExampleStat",
  "title": "StatsBlock title",
  "type": "StatsBlock",
  "bc": "myexample",
  "fields": [
    {
      "title": "value",
      "key": "value",
      "type": "number",
      "bgColor": "#edaa"
    },
    {
      "title": "title",
      "key": "title",
      "type": "input"
    },
    {
      "title": "icon",
      "key": "icon",
      "type": "input"
    },
    {
      "title": "description",
      "key": "description",
      "type": "hint"
    }
  ]
}

Step1 No use parameter title to .widget.json.

{
  "name": "MyExampleStat",
  "title": "StatsBlock title",
  "type": "StatsBlock",
  "bc": "myexample",
  "fields": [
    {
      "title": "value",
      "key": "value",
      "type": "number",
      "bgColor": "#edaa"
    },
    {
      "title": "title",
      "key": "title",
      "type": "input"
    },
    {
      "title": "description",
      "key": "description",
      "type": "hint"
    }
  ]
}

Color

Color allows you to specify a field block.Optional. It can be calculated based on business logic of application

Calculated color

Live Sample · GitHub

Constant color

Live Sample · GitHub

How does it look?

img_color.png

How to add?

Example

Step 1 Add custom field for color to corresponding DataResponseDTO. The field can contain a HEX color or be null.

@Getter
@Setter
@NoArgsConstructor
public class MyExampleDTO extends DataResponseDTO {
    private String title;

    private Long value;

    private String color;

    private String description;

    private String icon;
}

Step 2 Add "bgColorKey" : custom field for color to .widget.json.

{
  "name": "MyExampleList",
  "title": "StatsBlock title",
  "type": "StatsBlock",
  "bc": "myExampleBc",
  "fields": [
    {
      "title": "value",
      "key": "value",
      "type": "number",
      "bgColorKey": "color"
    },
    {
      "title": "title",
      "key": "title",
      "type": "input"
    },
    {
      "title": "icon",
      "key": "icon",
      "type": "input"
    },
    {
      "title": "description",
      "key": "description",
      "type": "hint"
    }
  ]
}

Add "bgColor" : HEX color to .widget.json.

{
  "name": "MyExampleList",
  "title": "StatsBlock title",
  "type": "StatsBlock",
  "bc": "myExampleBc",
  "fields": [
    {
      "title": "value",
      "key": "value",
      "type": "number",
      "bgColor": "#edaa"
    },
    {
      "title": "title",
      "key": "title",
      "type": "input"
    },
    {
      "title": "icon",
      "key": "icon",
      "type": "input"
    },
    {
      "title": "description",
      "key": "description",
      "type": "hint"
    }
  ],
  "options": {
  }
}

Widget size

By default, we use the view gridWidth field to arrange widgets in a row, each occupying 1/3 of the given size. For example, if the row has a total width of 24 spans, each widget will take up 8 spans.

Live Sample · GitHub

How does it look?

gridwidth24.png

gridwidth12.png

gridwidth6.png

How to add?

Example

Change gridWidth = 24 to corresponding view

{
  "name": "MyExampleStat",
  "title": "MyExampleStat",
  "template": "DashboardView",
  "url": "/screen/myexample/view/MyExampleStat",
  "widgets": [
    {
      "widgetName": "SecondLevelMenu",
      "position": 0,
      "gridWidth": 24
    },
    {
      "widgetName": "MyExampleStat",
      "position": 20,
      "gridWidth": 24
    }
  ],
  "rolesAllowed": [
    "CXBOX_USER"
  ]
}

Change gridWidth = 12 to corresponding view

{
  "name": "MyExampleStat",
  "title": "MyExampleStat",
  "template": "DashboardView",
  "url": "/screen/myexample/view/MyExampleStat",
  "widgets": [
    {
      "widgetName": "SecondLevelMenu",
      "position": 0,
      "gridWidth": 24
    },
    {
      "widgetName": "MyExampleStat",
      "position": 20,
      "gridWidth": 12
    }
  ],
  "rolesAllowed": [
    "CXBOX_USER"
  ]
}

Change gridWidth = 6 to corresponding view

{
  "name": "MyExampleStat",
  "title": "MyExampleStat",
  "template": "DashboardView",
  "url": "/screen/myexample/view/MyExampleStat",
  "widgets": [
    {
      "widgetName": "SecondLevelMenu",
      "position": 0,
      "gridWidth": 24
    },
    {
      "widgetName": "MyExampleStat",
      "position": 20,
      "gridWidth": 6
    }
  ],
  "rolesAllowed": [
    "CXBOX_USER"
  ]
}

Drilldown

Live Sample · GitHub

DrillDown allows you to navigate to another view by simply tapping on it. Target view and other drill-down parts can be calculated based on business logic of application

Also, it optionally allows you to filter data on target view before it will be opened see more DrillDown

How does it look?

drilldown.gif

How to add?

Example

Option 1
Step 1 Add fields.setDrilldown to corresponding FieldMetaBuilder.

    @Override
    public void buildRowDependentMeta(RowDependentFieldsMeta<MyExampleDTO> fields, BcDescription bc,
                                      String id, String parentId) {
        String urlBC ="/screen/myexample/view/myexamplelist" + "/" + PlatformMyExampleController.myExampleBc;
        String urlFilterForField = URLEncoder.encode("customFieldFilterDictionary.equalsOneOf=%5B%22Low%22%2C%22High%22%5");
        String urlFilter = "?filters={\""
                + PlatformMyExampleController.myExampleBc
                + "\":\""
                + urlFilterForField
                + "\"}";

        fields.setDrilldown(
                MyExampleDTO_.value,
                DrillDownType.INNER,
                urlBC + urlFilter
        );
    }

    private String getStatusFilterValues(@NonNull String id) {
        if (COUNT_NEW_ROW_ID.equals(id)) {
            return CustomFieldEnum.NEW.getValue();
        } else if (COUNT_NEW_IN_PROGRESS_ROW_ID.equals(id)) {
            return CustomFieldEnum.NEW.getValue() + "," + CustomFieldEnum.IN_PROGRESS.getValue();
        }

        throw new IllegalStateException("Unexpected value: " + id);
    }

Step 2 Add "drillDown": "true" to .widget.json.

{
  "name": "MyExampleList",
  "title": "StatsBlock title",
  "type": "StatsBlock",
  "bc": "myExampleBc",
  "fields": [
    {
      "title": "value",
      "key": "value",
      "type": "number",
      "drillDown": true
    },
    {
      "title": "title",
      "key": "title",
      "type": "input"
    },
    {
      "title": "icon",
      "key": "icon",
      "type": "input"
    },
    {
      "title": "description",
      "key": "description",
      "type": "hint"
    }
  ]
}

Option 2 Add "drillDownKey" : custom field to .widget.json. See more Drilldown

Advanced customization