Create Wonderful WebUI Widgets

In case you have not yet tried the Wonderful WebUI Widgets app yourself, you may want to scan Working with Wonderful WebUI Widgets first.

Welcome and workflow

It is good practice to provide a welcome text to the end-user. This way the end-user reads about the operation of the app and about expected actions. In addition, it is good practice to make the welcome page part of the workflow, such that the user feels guided right from the start.

Map page

The latitude and longitude for the map widget of arbitrarily selected German cities are extracted from data provided by simplemaps.

Icons for nodes

The support for icons on nodes makes it easy to specify the icons to be used on the nodes via a definition similar to the following:

 1StringParameter sp_locationIcon {
 2    IndexDomain: i_loc;
 3    Definition: {
 4        if i_loc in s_sources then
 5            "aimms-factory"
 6        elseif i_loc in s_destinations then
 7            "aimms-store"
 8        else
 9            "aimms-width"
10        endif ;
11    }
12}

Detail tooltip

HTML can be used for tooltips on nodes so a definition like the following is used:

 1StringParameter sp_locationTooltip {
 2    IndexDomain: i_loc;
 3    Definition: {
 4        "<div align=\"left\">"  +
 5        "<Table>" +
 6                "<TR>"  +
 7                        "<TD>"  +
 8                                        "<B> Name : </B>" +
 9                        "</TD>" +
10                        "<TD>"  +
11                                        formatString("%e", i_loc) +
12                        "</TD>" +
13                "</TR>" +
14                "<TR>"  +
15                        "<TD>"  +
16                                        "<B> Type : </B>" +
17                        "</TD>" +
18                        "<TD>"  +
19                                        sp_locationType( i_loc ) +
20                        "</TD>" +
21                "</TR>" +
22                "<TR>"  +
23                        "<TD>"  +
24                                        "<B> " +
25                                        if i_loc in s_sources then
26                                            "Capacity"
27                                        elseif i_loc in s_intermediate then
28                                            "Capacity"
29                                        else
30                                            "Demand"
31                                        endif
32                                        + " : </B>" +
33                        "</TD>" +
34                        "<TD>"  +
35                                        p_locationSize(i_loc) +
36                        "</TD>" +
37                "</TR>"  +
38                if sum( i_locFrom, v_prod(i_locFrom ) ) then
39                "<TR>"  +
40                        "<TD>"  +
41                                        "<B> " +
42                                        if i_loc in s_sources then
43                                            "Production"
44                                        elseif i_loc in s_intermediate then
45                                            "Flow"
46                                        else
47                                            "Unmet demand"
48                                        endif
49                                        + " : </B>" +
50                        "</TD>" +
51                        "<TD>"  +
52                                        if i_loc in s_sources then
53                                            v_prod(i_loc)
54                                        elseif i_loc in s_intermediate then
55                                            sum( i_locFrom, v_flow(i_locFrom, i_loc) )
56                                        else
57                                            v_unmetDemand(i_loc)
58                                        endif +
59                        "</TD>" +
60                "</TR>"
61                else "" endif
62                +
63        "</Table>"
64    }
65}

There are some remarks regarding the above definition:

  1. It uses the table syntax of HTML.

  2. It is admittedly lengthy, but having this length and indentation, makes the structure apparent.

  3. Lines 38-61 are part of an if-then-else expression; based on a condition, a row is added to the tooltip table.

Detail context menu

 1StringParameter sp_mapLocationItemActions {
 2    IndexDomain: (webui::indexWidgetItemActionSpec,webui::indexPageExtension,webui::indexWidgetActionSpec);
 3    Definition: {
 4        {
 5            ('p_locationSize', '1', 'displaytext') : "Debug",
 6            ('p_locationSize', '1', 'icon'       ) : "aimms-bug",
 7            ('p_locationSize', '1', 'procedure'  ) : "pr_locDebug",
 8            ('p_locationSize', '1', 'state'      ) : if bp_developmentSupport then "Active" else "Hide" endif,
 9
10            ('p_locationSize', '2', 'displaytext') : formatString("Increase %s %e by 1", if ep_selectedLocation in s_destinations then "demand" else "capacity" endif, ep_selectedLocation),
11            ('p_locationSize', '2', 'icon'       ) : "aimms-volume-increase2",
12            ('p_locationSize', '2', 'procedure'  ) : "pr_locSizeIncrease1",
13            ('p_locationSize', '2', 'state'      ) : "Active",
14
15            ('p_locationSize', '3', 'displaytext') : formatString("Increase %s %e by 5", if ep_selectedLocation in s_destinations then "demand" else "capacity" endif, ep_selectedLocation),
16            ('p_locationSize', '3', 'icon'       ) : "aimms-volume-increase",
17            ('p_locationSize', '3', 'procedure'  ) : "pr_locSizeIncrease5",
18            ('p_locationSize', '3', 'state'      ) : "Active",
19
20            ('p_locationSize', '4', 'displaytext') : formatString("Decrease %s %e by 1", if ep_selectedLocation in s_destinations then "demand" else "capacity" endif, ep_selectedLocation),
21            ('p_locationSize', '4', 'icon'       ) : "aimms-volume-decrease2",
22            ('p_locationSize', '4', 'procedure'  ) : "pr_locSizeDecrease1",
23            ('p_locationSize', '4', 'state'      ) : "Active",
24
25            ('p_locationSize', '5', 'displaytext') : formatString("Decrease %s %e by 5", if ep_selectedLocation in s_destinations then "demand" else "capacity" endif, ep_selectedLocation),
26            ('p_locationSize', '5', 'icon'       ) : "aimms-volume-decrease",
27            ('p_locationSize', '5', 'procedure'  ) : "pr_locSizeDecrease5",
28            ('p_locationSize', '5', 'state'      ) : "Active",
29
30            ('p_locationSize', '6', 'displaytext') : formatString("Edit %e", ep_selectedLocation),
31            ('p_locationSize', '6', 'icon'       ) : "aimms-quill",
32            ('p_locationSize', '6', 'procedure'  ) : "pr_locSizeDetails",
33            ('p_locationSize', '6', 'state'      ) : "Active"
34        }
35    }
36}

Some remarks on the above:

  1. A defined list is created using { ... }. Note the absence of the word data here. It does require to put the element literals between single quotes (''). But then you can nicely use expressions behind the :.

    The advantage of this style of defining context menu behavior is that the small procedures that modify the data of the string parameter to fine control the behavior of such menus are no longer needed.

    This application uses a similar style for controlling the behavior workflow, status bar, widget menus, and page actions.

  2. Line 5: Debug - to help test the application. The visibility of the item

  3. Line 10: FormatString is used to make the descriptions of the menu items more to the point.

Gantt page

Data representation

There are three different data representations that need to be kept consistent:

  1. User data

  2. Gantt Chart data: start and length of each task.

  3. Coefficients used by the optimization algorithm.

Border: use of CSS

We built on the intuition that blue indicates “can be changed manually in this session” and that black indicates “derived or computed data” and is treated as read-only in this session.

Specifying tooltips is similar to what is used for the map widget, and not repeated here.

Context menu

The context menu is to be used for:

  1. The scheduled jobs in the Gantt Chart

  2. Each column/row in the Order Details tab.

 1StringParameter sp_ganttSpecItemActions {
 2    IndexDomain: (webui::indexWidgetItemActionSpec,webui::indexPageExtension,webui::indexWidgetActionSpec);
 3    Definition: {
 4        {
 5            ('p_WebUIGNTDuration', '1', 'displaytext') : "Debug",
 6            ('p_WebUIGNTDuration', '1', 'icon'       ) : "aimms-bug",
 7            ('p_WebUIGNTDuration', '1', 'procedure'  ) : "pr_jobDebug",
 8            ('p_WebUIGNTDuration', '1', 'state'      ) : if bp_developmentSupport then "Active" else "Hide" endif,
 9
10            ('p_WebUIGNTDuration', '2', 'displaytext') : formatString("Move order %e to front", ep_selectedOrder),
11            ('p_WebUIGNTDuration', '2', 'icon'       ) : "aimms-first",
12            ('p_WebUIGNTDuration', '2', 'procedure'  ) : "pr_moveToFront",
13            ('p_WebUIGNTDuration', '2', 'state'      ) : if bp_orderScheduledByLivingCreature( ep_selectedOrder ) then "Active" else "Inactive" endif,
14
15            ('p_WebUIGNTDuration', '3', 'displaytext') : formatString("Delay order %e until deadline", ep_selectedOrder),
16            ('p_WebUIGNTDuration', '3', 'icon'       ) : "aimms-last",
17            ('p_WebUIGNTDuration', '3', 'procedure'  ) : "pr_delayUntilDeadline",
18            ('p_WebUIGNTDuration', '3', 'state'      ) : if bp_orderScheduledByLivingCreature( ep_selectedOrder ) then "Active" else "Inactive" endif,
19
20            ('p_WebUIGNTDuration', '4', 'displaytext') : formatString("Allow scheduling of %e by living creature", ep_selectedOrder),
21            ('p_WebUIGNTDuration', '4', 'icon'       ) : "aimms-grab",
22            ('p_WebUIGNTDuration', '4', 'procedure'  ) : "pr_scheduleByLivingCreature",
23            ('p_WebUIGNTDuration', '4', 'state'      ) : if not bp_orderScheduledByLivingCreature( ep_selectedOrder ) then "Active" else "Inactive" endif,
24
25            ('p_WebUIGNTDuration', '5', 'displaytext') : formatString( "Allow scheduling of %e by optimization algorithm", ep_selectedOrder),
26            ('p_WebUIGNTDuration', '5', 'icon'       ) : "aimms-music",
27            ('p_WebUIGNTDuration', '5', 'procedure'  ) : "pr_scheduleByOptimizationAlgorithm",
28            ('p_WebUIGNTDuration', '5', 'state'      ) : if bp_orderScheduledByLivingCreature( ep_selectedOrder ) then "Active" else "Inactive" endif,
29
30            ('p_WebUIGNTDuration', '6', 'displaytext') : formatString("Delete order %e", ep_selectedOrder),
31            ('p_WebUIGNTDuration', '6', 'icon'       ) : "aimms-bin",
32            ('p_WebUIGNTDuration', '6', 'procedure'  ) : "pr_deleteJob",
33            ('p_WebUIGNTDuration', '6', 'state'      ) : "Active",
34
35            ('p_WebUIGNTDuration', '7', 'displaytext') : formatString("Edit order %e", ep_selectedOrder),
36            ('p_WebUIGNTDuration', '7', 'icon'       ) : "aimms-quill",
37            ('p_WebUIGNTDuration', '7', 'procedure'  ) : "pr_editJob",
38            ('p_WebUIGNTDuration', '7', 'state'      ) : "Active"
39        }
40    }
41}

Remarks:

  1. Similar to the context menu in the map widget above.

  2. Several actions are only available to a job that can be scheduled manually.

  3. Every job can be edited.

  4. The use of 'p_WebUIGNTDuration' in the above definition implies: Only available to the Gantt Chart, so how to reuse this menu in the order table?

Let’s first identify the identifiers to be used, namely the ones in the columns:

 1Set s_ganttItemActionIdentifiers {
 2    SubsetOf: AllIdentifiers;
 3    Definition: {
 4        data  {
 5            bp_orderScheduledByLivingCreature,
 6            ep_orderProduct,
 7            p_orderLength,
 8            ep_orderProductType,
 9            p_orderQuantity,
10            ep_orderDeliveryDueDate,
11            ep_orderProductionLine,
12            ep_orderStartDate,
13            p_WebUIGNTDuration,
14            p_WebUIGNTStartTime
15        }
16    }
17}

And then we can easily replicate the data of sp_ganttSpecItemActions for each of these column names using the below definition:

 1StringParameter sp_ganttItemActions {
 2    IndexDomain: (webui::indexWidgetItemActionSpec,webui::indexPageExtension,webui::indexWidgetActionSpec);
 3    Definition: {
 4        if webui::indexWidgetItemActionSpec in s_ganttItemActionIdentifiers then
 5            sp_ganttSpecItemActions('p_WebUIGNTDuration', webui::indexPageExtension, webui::indexWidgetActionSpec)
 6        else
 7            ""
 8        endif
 9    }
10}

Pure combi page

Tooltip

a simple sentence for the tooltip suffices for this application, and a HTML table is not created.

To make multiple identifiers accessible for the right mouse menu and the tooltip, the <IDENTIFIER-SET> index should not be placed in the Totals: pivot group. That is why the <IDENTIFIER-SET> is placed in the Stacked: pivot group.

Slack, definition

The slack is what can be carried minus what is already assigned to a youngster. For the element 'me' there is no slack - in the story the “me” just carries the booth and the bags of fruit left behind by the youngsters myself without regard to a maximum weight that can be carried.

Visualizing slack

item order

The item order is that the slack should be on top.

The design of the column chart is such that items are placed in order of the set on top of each other; so the first visible element will be at the bottom!

../../_images/slack-order-bottom-to-top.png

As the <IDENTIFIER-SET> is in the Stacked: pivot group, this also applies to the order of the contents. By swapping this order, the columns look as follows (clearly not desired).

../../_images/slack-order-bottom-to-top-wrong.png

White with blue border

To give the impression of empty space to be filled a blue rectangle around a white box is used. This is achieved using the following .css code:

1.annotation-hassomeslack {
2    fill:white;
3    stroke:blue!Important;
4    stroke-width:4px;
5}

Remarks:

  • The annotation hassomeslack is added in the model.

  • The Important is needed to make this coloring of the borderline sufficiently specific to be accepted.

Right mouse menu

This page has only one widget: the combination chart, introduced with AIMMS 4.84. The story is about assigning an item to an element in a set. To invoke an assignment, the right mouse menu should be popped up at a colored rectangle in the column chart.

The right mouse menu contains an entry for each element in a set. The code shows how to generate these lines for an arbitrary set (to be kind to the end-user, please limit the size of such a set to no more than 10 elements).

For each line in the menu, there needs to be a corresponding procedure. To create the proper number of procedures, a runtime library is used.