Sankey looks good now

This commit is contained in:
Rich Lott / Artful Robot 2025-03-31 12:14:01 +01:00
parent 8591d6c060
commit 216d9a46ee
3 changed files with 41 additions and 16 deletions

View file

@ -72,3 +72,10 @@ crm-contact-category-settings .hint {
crm-contact-category-settings .name-input-wrapper:focus-within .hint { crm-contact-category-settings .name-input-wrapper:focus-within .hint {
display: block; display: block;
} }
/* Category Flows page */
.contact-cats-sankey {
background:white;
border-radius: 4px;
padding: 1rem;
}

View file

@ -38,7 +38,6 @@
<svg viewBox="0 0 {{ $ctrl.sankey.width }} {{ $ctrl.sankey.height }}" <svg viewBox="0 0 {{ $ctrl.sankey.width }} {{ $ctrl.sankey.height }}"
width="{{$ctrl.sankey.width}}" width="{{$ctrl.sankey.width}}"
height="{{$ctrl.sankey.height}}" height="{{$ctrl.sankey.height}}"
style="width: 100%; height: auto;"
version="1.1" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns="http://www.w3.org/2000/svg"
> >
<defs> <defs>
@ -51,7 +50,7 @@
</linearGradient> </linearGradient>
</defs> </defs>
<g transform="translate({{$ctrl.sankey.labelWidth}})"> <g transform="translate({{$ctrl.sankey.iconWidth}})">
<path ng-repeat="p in $ctrl.sankey.snakes" <path ng-repeat="p in $ctrl.sankey.snakes"
d="{{p.d}}" d="{{p.d}}"
fill="{{p.fill}}" fill="{{p.fill}}"
@ -61,7 +60,7 @@
</path> </path>
</g> </g>
<!-- to-side labels --> <!-- to-side labels -->
<g transform="translate({{$ctrl.sankey.labelWidth + $ctrl.sankey.snakeWidth}})"> <g transform="translate({{$ctrl.sankey.iconWidth + $ctrl.sankey.snakeWidth}})">
<rect ng-repeat="toAna in $ctrl.sankey.analysisArray" <rect ng-repeat="toAna in $ctrl.sankey.analysisArray"
x=0 x=0
y="{{toAna.y}}" y="{{toAna.y}}"
@ -71,11 +70,14 @@
></rect> ></rect>
<foreignobject ng-repeat="toAna in $ctrl.sankey.analysisArray" <foreignobject ng-repeat="toAna in $ctrl.sankey.analysisArray"
x="20" y="{{toAna.y}}" width="{{$ctrl.sankey.labelWidth}}" height="{{toAna.thisCatHeight}}"> x="20" y="{{toAna.y}}" width="{{$ctrl.sankey.labelWidth}}" height="{{toAna.thisCatHeight}}">
<span class=sankey-label style="display:block;color:{{toAna.cat.color}}"><i class="crm-i {{toAna.cat.icon}}" title="{{toAna.cat.label}}"></i></span> <span class=sankey-label style="display:block;color:{{toAna.cat.color}}">
<i class="crm-i {{toAna.cat.icon}}" title="{{toAna.cat.label}}"></i>
{{toAna.cat.label}} {{toAna.now.toLocaleString()}}
</span>
</foreignobject> </foreignobject>
</g> </g>
<!-- from-side labels --> <!-- from-side labels -->
<g transform="translate({{$ctrl.sankey.labelWidth - 16}})"> <g transform="translate({{$ctrl.sankey.iconWidth}})">
<rect ng-repeat="toAna in $ctrl.sankey.analysisArray" <rect ng-repeat="toAna in $ctrl.sankey.analysisArray"
x=0 x=0
y="{{toAna.y}}" y="{{toAna.y}}"
@ -83,9 +85,16 @@
height="{{toAna.fromCatHeight}}" height="{{toAna.fromCatHeight}}"
fill="{{toAna.cat.color}}" fill="{{toAna.cat.color}}"
></rect> ></rect>
</g>
<g>
<foreignobject ng-repeat="toAna in $ctrl.sankey.analysisArray" <foreignobject ng-repeat="toAna in $ctrl.sankey.analysisArray"
x="-32" y="{{toAna.y}}" width="32" height="{{toAna.thisCatHeight}}"> x=0 y="{{toAna.y}}" width="{{$ctrl.sankey.iconWidth}}"
<span class=sankey-label style="display:block;color:{{toAna.cat.color}}"><i class="crm-i {{toAna.cat.icon}}" title="{{toAna.cat.label}}"></i></span> height="{{toAna.thisCatHeight}}">
<span class=sankey-label
title="{{toAna.previous}}"
style="display:block;text-align:center;color:{{toAna.cat.color}}"
><i class="crm-i {{toAna.cat.icon}}" title="{{toAna.cat.label}}"></i>
</span>
</foreignobject> </foreignobject>
</g> </g>
<!-- <!--

View file

@ -2,7 +2,6 @@
angular.module("crmContactcats").component("crmContactCategoryFlows", { angular.module("crmContactcats").component("crmContactCategoryFlows", {
templateUrl: "~/crmContactcats/crmContactCategoryFlows.html", templateUrl: "~/crmContactcats/crmContactCategoryFlows.html",
controller: function($scope, $timeout, crmApi4, crmStatus, $document) { controller: function($scope, $timeout, crmApi4, crmStatus, $document) {
const catHeightMin = 50, catHeightGap = 8;
var ts = ($scope.ts = CRM.ts(null)), var ts = ($scope.ts = CRM.ts(null)),
ctrl = this; ctrl = this;
@ -19,9 +18,8 @@
: allFlowsData; : allFlowsData;
ctrl.loading = false; ctrl.loading = false;
const labelWidth = ctrl.sankey.labelWidth, const width = Math.max(600, document.querySelector('.contact-cats-sankey').clientWidth),
width = Math.max(600, document.querySelector('.contact-cats-sankey').clientWidth), snakeWidth = width - ctrl.sankey.labelWidth - ctrl.sankey.iconWidth;
snakeWidth = width - 2*labelWidth;
ctrl.sankey.width = width; ctrl.sankey.width = width;
ctrl.sankey.snakeWidth = snakeWidth; ctrl.sankey.snakeWidth = snakeWidth;
@ -79,7 +77,7 @@
}); });
// Allow a cat height to grow from its min to 3x min. // Allow a cat height to grow from its min to 3x min.
let maxCatHeight = 3 * catHeightMin; let maxCatHeight = 3 * ctrl.sankey.catHeightMin;
let maxContactsAtOneCat = Math.max(... analysisArray.map(cat => Math.max(cat.now, cat.previous))); let maxContactsAtOneCat = Math.max(... analysisArray.map(cat => Math.max(cat.now, cat.previous)));
let scale = maxCatHeight / maxContactsAtOneCat; let scale = maxCatHeight / maxContactsAtOneCat;
console.log({maxCatHeight, maxContactsAtOneCat,scale}); console.log({maxCatHeight, maxContactsAtOneCat,scale});
@ -92,8 +90,8 @@
toAna.y = accumulatedY; toAna.y = accumulatedY;
toAna.toCatHeight = Math.max(1, Math.round(scale * toAna.now)); toAna.toCatHeight = Math.max(1, Math.round(scale * toAna.now));
toAna.fromCatHeight = Math.max(1,Math.round(scale * toAna.previous)); toAna.fromCatHeight = Math.max(1,Math.round(scale * toAna.previous));
toAna.thisCatHeight = Math.ceil(Math.max(catHeightMin, scale * Math.max(toAna.now, toAna.previous))); toAna.thisCatHeight = Math.ceil(Math.max(ctrl.sankey.catHeightMin, scale * Math.max(toAna.now, toAna.previous)));
accumulatedY += toAna.thisCatHeight + catHeightGap; accumulatedY += toAna.thisCatHeight + ctrl.sankey.catHeightGap;
// console.log({toCat: toAna.cat.label, toCatY: toAna.y, accumulatedY, thisCatHeight}); // console.log({toCat: toAna.cat.label, toCatY: toAna.y, accumulatedY, thisCatHeight});
// Intialise source offsets for this category. // Intialise source offsets for this category.
offsetWithinSourcesByCat[toAna.cat.id] = 0; offsetWithinSourcesByCat[toAna.cat.id] = 0;
@ -103,7 +101,7 @@
return a.cat.presentation_order - b.cat.presentation_order; return a.cat.presentation_order - b.cat.presentation_order;
}) })
}); });
ctrl.sankey.height = Math.ceil(accumulatedY); ctrl.sankey.height = analysisArray[analysisArray.length -1].y + analysisArray[analysisArray.length -1].thisCatHeight;
// Make a list of snakes we need to draw. // Make a list of snakes we need to draw.
const snakes = []; const snakes = [];
@ -167,7 +165,18 @@
ctrl.startDate = new Date('2025-03-25'); ctrl.startDate = new Date('2025-03-25');
ctrl.endDate = null; ctrl.endDate = null;
ctrl.stats = null; ctrl.stats = null;
ctrl.sankey = { height: 500, width: 900, labelWidth: 150, snakeWidth: 600, snakes: [], analysisArray: [] }; ctrl.sankey = {
catHeightMin : 50,
labelWidth: 220,
iconWidth: 32,
catHeightGap: 8,
// the following values get overwritten.
height: 500,
width: 900,
snakeWidth: 600,
snakes: [],
analysisArray: []
};
if (useTestFixtures) { if (useTestFixtures) {
ctrl.catDefs = [ ctrl.catDefs = [