Skip to content

Commit

Permalink
impl
Browse files Browse the repository at this point in the history
Issue #606
  • Loading branch information
rsoika committed Sep 23, 2024
1 parent 6485e62 commit 669e504
Show file tree
Hide file tree
Showing 5 changed files with 365 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core" xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:h="http://xmlns.jcp.org/jsf/html">


<!-- Chart Diagramm -->
<style>
.analytic-card-chart {
min-height: 200px;
padding: 20px;
background-color: #f3f3f3;
border-radius: 5px;
-webkit-box-shadow: 0 0 5px 0 rgba(43, 43, 43, .1), 0 11px 6px -7px rgba(43, 43, 43, .1);
box-shadow: 0 0 5px 0 rgba(43, 43, 43, .1), 0 11px 6px -7px rgba(43, 43, 43, .1);
border: none;
-webkit-transition: all .3s ease-in-out;
transition: all .3s ease-in-out;
font-size: 40px;
font-weight: bold;
color: #4398f3;
}

.analytic-card-chart-detail {
position: relative;
background-color: #fff;
height: 100%;
width: 100%;
}
</style>
<div class="analytic-card-chart">
<div class="analytic-card-chart-detail">
<canvas id="analytics_canvas_#{item.name}" style=""></canvas>
</div>
</div>







<script type="text/javascript"
src="#{facesContext.externalContext.requestContextPath}/js/chartjs/chart.min.js?build=#{app.application_build_timestamp}"></script>

<script type="text/javascript">
/*<![CDATA[*/
$(document).ready(
function () {
// update the diagram
var ctx = document.getElementById("analytics_canvas_#{item.name}").getContext("2d");
var chartData = #{ analyticController.getAsString(item.name)
};
console.log(chartData);
//jsObject = JSON.parse(chartData);
window.myBar = new Chart(ctx, chartData);
});

/*]]>*/
</script>

</ui:composition>
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core" xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:h="http://xmlns.jcp.org/jsf/html">


<style>
.analytic-card {
display: flex;
flex-flow: column;

min-height: 200px;
padding: 20px;
background-color: #f3f3f3;
border-radius: 5px;
-webkit-box-shadow: 0 0 5px 0 rgba(43, 43, 43, .1), 0 11px 6px -7px rgba(43, 43, 43, .1);
box-shadow: 0 0 5px 0 rgba(43, 43, 43, .1), 0 11px 6px -7px rgba(43, 43, 43, .1);
border: none;
-webkit-transition: all .3s ease-in-out;
transition: all .3s ease-in-out;
font-size: 40px;
font-weight: bold;
color: #4398f3;
}

.analytic-card-header {}

.analytic-card-footer {
font-size: 12px;
font-weight: normal;
color: #333;
}

.analytic-card-content {
flex: 1;
font-size: 20px;
}

.analytic-card-link {
float: right;
font-size: 18px;
margin-left: 20px;
}
</style>
<div class="analytic-card">
<div class="analytic-card-header">
<h:outputText value="#{analyticController.getAsDouble(item.name)}">
<f:convertNumber minFractionDigits="2" locale="de" />
</h:outputText>
</div>


<div class="analytic-card-content">#{analyticController.getLabel(item.name)}</div>


<div class="analytic-card-footer">
<span>#{analyticController.getDescription(item.name)}</span>

<h:panelGroup styleClass="analytic-card-link" rendered="#{!empty analyticController.getLink(item.name)}">
<a href="#{analyticController.getLink(item.name)}"><span class="typcn typcn-zoom-outline" /></a>
</h:panelGroup>


</div>

</div>

</ui:composition>
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package org.imixs.workflow.office.forms;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.faces.data.WorkflowController;

import jakarta.enterprise.context.ConversationScoped;
import jakarta.enterprise.event.Event;
import jakarta.inject.Inject;
import jakarta.inject.Named;

/**
* The AnalyticController is a conversationScoped controller that provides
* values for the analytic-custom parts.
* <p>
* A custom implementation can react on AnalyticEvent to compute values and
* datasets.
* <p>
* The controller implements a caching mechanism to avoid repeated calls for new
* analytic values. If the analytic value is already stored in the current
* workitem, no new value will be fired.
*
*
* @author rsoika
*
*/
@Named
@ConversationScoped
public class AnalyticController implements Serializable {

private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(AnalyticController.class.getName());

@Inject
protected Event<AnalyticEvent> analyticEvents;

@Inject
protected WorkflowController workflowController;

/**
* Returns a analytic value as a String for a given key.
*
* @param key
* @return
*/
public String getAsString(String key) {
ItemCollection analyticData = computeValue(key);
return analyticData.getItemValueString("value");
}

/**
* Returns a analytic value as a Double for a given key.
*
* @param key
* @return
*/
public double getAsDouble(String key) {
ItemCollection analyticData = computeValue(key);
return analyticData.getItemValueDouble("value");
}

/**
* Returns the analytic label for a given key
*
* @param key
* @return
*/
public String getLabel(String key) {
ItemCollection analyticData = computeValue(key);
return analyticData.getItemValueString("label");
}

/**
* Returns the analytic optional link for a given key
*
* @param key
* @return
*/
public String getLink(String key) {
ItemCollection analyticData = computeValue(key);
return analyticData.getItemValueString("link");
}

/**
* Returns the analytic description for a given key
*
* @param key
* @return
*/
public String getDescription(String key) {
ItemCollection analyticData = computeValue(key);
return analyticData.getItemValueString("description");
}

/**
* Computes am analytic value. The method cache the value in the item
* 'analytic.KEY' to avoid feierring repeated AnalyticEvents.
*
* @param key
* @return
*/
protected ItemCollection computeValue(String key) {
if (workflowController.getWorkitem() != null) {
// try to fetch value from cache
if (!workflowController.getWorkitem().hasItem("analytic." + key)) {
logger.fine("fire analytic event for key '" + key + "'");
// Fire the Analytics Event for this key
AnalyticEvent event = new AnalyticEvent(key, workflowController.getWorkitem());
if (analyticEvents != null) {
analyticEvents.fire(event);
ItemCollection details = new ItemCollection();
details.setItemValue("value", event.getValue());
details.setItemValue("label", event.getLabel());
details.setItemValue("description", event.getDescription());
details.setItemValue("link", event.getLink());
implodeDetails(key, details);
return details;
}

if (event.getValue() == null) {
// set dummy value
ItemCollection details = new ItemCollection();
details.setItemValue("value", "");
details.setItemValue("label", "");
details.setItemValue("description", "No data available");
implodeDetails(key, details);
return details;
}
}
}

// analytic value is already cached!
return explodeDetails(key);
}

/**
* Convert the List of ItemCollections back into a List of Map elements
*
* @param workitem
*/
@SuppressWarnings({ "rawtypes" })
public void implodeDetails(String key, ItemCollection details) {
// convert the child ItemCollection elements into a List of Map
List<Map> detailsList = new ArrayList<Map>();
detailsList.add(details.getAllItems());
workflowController.getWorkitem().replaceItemValue(key, detailsList);
}

/**
* converts the Map List of a workitem into a List of ItemCollectons
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public ItemCollection explodeDetails(String key) {
// convert current list of childItems into ItemCollection elements
List<Object> mapOrderItems = workflowController.getWorkitem().getItemValue(key);
if (mapOrderItems != null && mapOrderItems.size() > 0) {
ItemCollection itemCol = new ItemCollection((Map) mapOrderItems.get(0));
return itemCol;
}
// return empty collection
return new ItemCollection();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.imixs.workflow.office.forms;

import org.imixs.workflow.ItemCollection;

/**
* An AnalyticEvent is send by the AnalyticController during initialization
*/
public class AnalyticEvent {

private String key;
private Object value = null;
private String label = "";
private String description = "";
private String link = "";
private ItemCollection workitem = null;

public AnalyticEvent(String key, ItemCollection workitem) {
this.key = key;
this.workitem = workitem;
}

public String getKey() {
return key;
}

public void setKey(String key) {
this.key = key;
}

public String getLink() {
return link;
}

public void setLink(String link) {
this.link = link;
}

public ItemCollection getWorkitem() {
return workitem;
}

public Object getValue() {
return value;
}

public void setValue(Object value) {
this.value = value;
}

public String getLabel() {
return label;
}

public void setLabel(String label) {
this.label = label;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

}
3 changes: 2 additions & 1 deletion src/site/markdown/forms/analyticparts.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ To use one of the part templates you need first to implement a CDI bean that rea
| Property | Type | Mandatory | Description |
|-----------|---------|-----------|---------------------------------------------------------|
| key | text | x | Name of the analytic data set |
| value | text | x | A text value representing the data |
| value | text | x | A text value representing the data S |
| label | text | | Optional label for the data |
| description| text | | Optional description |
| link | text | | Optional link for detail views |

The value part can be any kind of data string. This could be a number a date or a complex JSON structure like it is used to display complex data in a chart diagram.
Expand Down

0 comments on commit 669e504

Please sign in to comment.