Numeric, difference between 2 values
The main KPI settings
title
: name of the KPI, below the value
Dataset setting :
dataset_id
Dataset filter (simple way, with 1 refine) :
dataset_refine_key
: a facet field,dataset_refine_value
: a value,
Dataset filters (advanced way, with 1 object) :
dataset_advanced_refine_parameters
: an object, ex:{'q':'some text query', 'refine.field':'value'}
Value settings, to setup the aggregation :
-
aggregation_function
: can be SUM, AVG, MIN, MAX, STD, COUNT -
aggregation_expression
: a numerical field id -
link
: a link to redirect the user -
image_url
: the url of the item image
Then, the reference value, and how to compare it
compare_mode
: can bedate
ornumeric
compare_strategie
: can benone
,diff
orprogression
-
valref_dataset_id
-
valref_dataset_refine_key
-
valref_dataset_refine_value
-
valref_dataset_advanced_refine_parameters
-
valref_aggregation_function
-
valref_aggregation_expression
-
valref_aggregation_unit
-
valref_prefix
: a sentence to add before the reference value -
valref_suffix
: a sentence to add after the reference value
Dataset in use: donnees-relatives-aux-stocks-des-doses-de-vaccins-contre-la-covid-19-departement
(See it on userclub domain)
Fields in use:
vaccine_type | dose_number (numeric) |
---|---|
Pfizer | 6345 |
Pfizer | 5045 |
Pfizer | 2525 |
Pfizer | 1270 |
<div class="container"
ng-init="
kpis = [
{
'title':'Pfizer doses',
'dataset_id':'donnees-relatives-aux-stocks-des-doses-de-vaccins-contre-la-covid-19-departement',
'dataset_refine_key': 'vaccine_type',
'dataset_refine_value': 'Pfizer',
'aggregation_function':'SUM',
'aggregation_expression':'dose_number',
'link':'https://userclub.opendatasoft.com/explore/dataset/donnees-relatives-aux-stocks-des-doses-de-vaccins-contre-la-covid-19-departement/custom/',
'image_url':'https://img.huffingtonpost.com/asset/602fff2626000025036bcaf3.jpg?ops=scalefit_630_noupscale',
'compare_mode':'numeric',
'compare_strategie':'diff',
'valref_dataset_id':'donnees-relatives-aux-stocks-des-doses-de-vaccins-contre-la-covid-19-departement',
'valref_aggregation_function':'SUM',
'valref_aggregation_expression':'dose_number',
'valref_aggregation_unit':'%',
'valref_prefix':'of all doses.',
'valref_suffix':'doses in total.'
}
];
itemsPerRowDesktop = 3;
itemsPerRowTablet = 2;
dateFormat = { 'prefix': 'from ', 'infix': ' to ', 'suffix': '' };
DO_NOT_MODIFY_BELOW;
activecategory = 'metakpicompare';
values = { 'popin': false };">
<!-- TABS -->
<div ng-init="categories = ((kpis | toObject : 'category') | keys)">
<ods-simple-tabs ng-show="categories.length > 0 && categories[0] != 'undefined'"
class="tab-pills"
sync-to-scope="activecategory">
<ods-simple-tab ng-repeat="category in categories"
ng-if="category != 'undefined'"
label="{{ category }}"
keep-content="true">
</ods-simple-tab>
</ods-simple-tabs>
</div>
<!-- CARDS -->
<section>
<div class="row row-equal-height">
<div ng-repeat="item in kpis"
ng-if="activecategory == (item.category | slugify) || !item.category"
class="{{ 'col-md-' + (12/itemsPerRowDesktop) }} {{ 'col-sm-' + (12/itemsPerRowTablet) }} col-xs-12 col-print-6 margin-bottom-20 {{ item.title | slugify }}">
<ods-dataset-context context="kpictx"
kpictx-dataset="{{item.dataset_id}}">
<!-- Apply filters to kpi context -->
<!-- if no advanced parameters is defined, get the simple one -->
<span ng-if="!item.dataset_advanced_refine_parameters">{{ kpictx.parameters['refine.' + item.dataset_refine_key] = item.dataset_refine_value ; '' }}</span>
<span ng-if="item.dataset_advanced_refine_parameters">{{ kpictx.parameters = (item.dataset_advanced_refine_parameters.replaceAll('\'','"') | fromjson) ; '' }}</span>
<span ng-if="item.compare_mode == 'numeric'">
<span ods-aggregation-context="kpictx"
ods-aggregation="result"
ods-aggregation-expression="{{item.aggregation_expression}}"
ods-aggregation-function="{{item.aggregation_function}}">
{{ item.result = result ; '' }}
</span>
<!-- KPI box component -->
<div class="card-flip"
ng-init="isFlipped = false"
ng-click="isFlipped = (item.flip_additional_description?!isFlipped:isFlipped)"
ng-class="{'not-flippable': !item.flip_additional_description,
'is-flipped': isFlipped}">
<div class="kpi-card card-face card-face-front">
<div class="kpi-card-top">
<div ng-if="item.image_url" class="kpi-img">
<img ng-src="{{ item.image_url }}"/>
</div>
</div>
<div class="kpi-card-middle">
<h2 class="kpi-value">
{{ item.result | number : (item.aggregation_precision || 0) }} <span class="kpi-unit">{{ item.aggregation_unit }}</span>
</h2>
<p class="kpi-title">
{{ item.title }}
<span ng-if="item.strings.filtrenend">
{{ dateFormat.prefix + item.strings.filtrenstart + dateFormat.infix + item.strings.filtrenend + dateFormat.suffix }}
</span>
<span ng-if="!item.strings.filtrenend">
{{ item.strings.filtrenstart | capitalize }}
</span>
</p>
<div class="kpi-valref"
ng-if="item.compare_strategie">
<ods-dataset-context context="kpivalrefctx"
kpivalrefctx-dataset="{{item.valref_dataset_id}}">
<!-- Apply filters to kpi context -->
<!-- if no advanced parameters is defined, get the simple one -->
<span ng-if="!item.valref_dataset_advanced_refine_parameters">{{ kpivalrefctx.parameters['refine.' + item.valref_dataset_refine_key] = item.valref_dataset_refine_value ; '' }}</span>
<span ng-if="item.valref_dataset_advanced_refine_parameters">{{ kpivalrefctx.parameters = (item.valref_dataset_advanced_refine_parameters.replaceAll('\'','"') | fromjson) ; '' }}</span>
<!-- KPI box component -->
<div ods-aggregation-context="kpivalrefctx"
ods-aggregation="resultvalref"
ods-aggregation-expression="{{item.valref_aggregation_expression}}"
ods-aggregation-function="{{item.valref_aggregation_function}}">
<div ng-switch="item.compare_strategie">
<div ng-switch-when="none">
{{ item.value = resultvalref ; '' }}
</div>
<div ng-switch-when="diff">
{{ item.value = (item.result/resultvalref)*100 ; '' }}
</div>
<div ng-switch-when="progression">
{{ item.value = (item.result - resultvalref)/resultvalref*100 ; '' }}
</div>
<div ng-switch-default>
{{ item.value = '' }}
</div>
</div>
<div ng-if="item.compare_strategie == 'diff' || item.compare_strategie == 'progression'">
<span class="kpi-valref-value"><span ng-if="item.compare_strategie == 'progression'">{{ item.value > 0 ? '+' : '' }}</span>{{ item.value | number : (item.valref_aggregation_precision || 0) }}</span>
<span class="kpi-valref-unit">{{ item.valref_aggregation_unit }}</span>
<span class="kpi-valref-carret" ng-if="item.higher">
<i ng-if="item.value > 0" class="fa fa-caret-up"
ng-class="{'kpi-valref-carret-good': item.higher == 'isbetter'}" aria-hidden="true"></i>
<i ng-if="item.value < 0" class="fa fa-caret-down"
ng-class="{'kpi-valref-carret-good': item.higher != 'isbetter'}" aria-hidden="true"></i>
</span>
<div class="kpi-valref-text">
{{ item.valref_prefix }}
<span class="kpi-valref-secondaryvalue">{{ resultvalref | number : (item.valref_aggregation_precision || 0) }}
<span class="kpi-unit">{{ item.aggregation_unit }}</span>
</span>
{{ item.valref_suffix }}
<span class="kpi-valref-date">{{
(item.strings.filtrenmoinsunend?dateFormat.prefix:'') +
item.strings.filtrenmoinsunstart +
(item.strings.filtrenmoinsunend?dateFormat.infix + item.strings.filtrenmoinsunend + dateFormat.suffix:'')
}}
</span>
</div>
</div>
<div ng-if="!item.compare_strategie">
<div class="kpi-valref-text">
<span class="kpi-valref-value">{{ resultvalref | number : (item.valref_aggregation_precision || 0) }}</span>
<span class="kpi-valref-unit">{{ item.valref_aggregation_unit }}</span>
</div>
{{ item.valref_prefix }}
{{ item.valref_suffix }}
</div>
<!--
<div ng-if="!(item.compare_strategie == 'diff' || item.compare_strategie == 'progression')">
Wrong setup : unknown compare_strategie (can be diff or progression)
</div>-->
</div>
</ods-dataset-context>
</div>
<div ng-if="item.link" class="kpi-link">
<a target="_blank"
ng-click="values.popin = (item.link_mode == 'popin' ? (item.title | slugify) : false); item.link_mode == 'popin' ? $event.preventDefault() : ''; $event.stopPropagation()"
ng-href="{{ item.link }}">{{ item.link_label || "See the data" }}</a>
</div>
</div>
<div ng-if="item.flip_hint"
class="kpi-card-bottom">
<p class="hint">{{ item.flip_hint }}</p>
</div>
</div>
<div class="kpi-card card-face card-face-back">
<p>
{{ item.flip_additional_description }}
</p>
<a target="_blank" ng-if="item.flip_additional_link" ng-href="{{ item.flip_additional_link }}">
{{ item.flip_additional_link_label || item.flip_additional_link }}
</a>
</div>
</div>
</span>
<span ng-if="item.compare_mode == 'date'"
ng-init="item.values = {'date':undefined};">
<!-- Date selection strategy -->
<div ng-switch="item.get_date_from">
<div ng-switch-when="now">
<div ods-datetime="today">
{{ item.values.date = (today | moment : 'YYYY/MM/DD') ; '' }}
</div>
</div>
<div ng-switch-when="data">
{{ kpictx.parameters['sort'] = item.date_field_id; '' }}
<div ods-results="items"
ods-results-context="kpictx"
ods-results-max="1">
<span ng-if="items[0]">
{{ item.values.date = (items[0].fields[item.date_field_id] | moment : 'YYYY/MM/DD') ; '' }}
</span>
</div>
</div>
<div ng-switch-when="ods-analysis">
<div ods-analysis="analysis"
ods-analysis-context="kpictx"
ods-analysis-x="{{ item.date_field_id }}"
ods-analysis-serie-count="COUNT()">
<span ng-if="analysis.results">
{{ xstruct = analysis.results[analysis.results.length - 1].x;
item.values.date = xstruct.year+ (xstruct.month ?('/'+(xstruct.month > 9 ?'':'0')+xstruct.month+(xstruct.day ?('/'+(xstruct.day > 9 ?'':'0')+xstruct.day):'')):'');
'' }}
</span>
</div>
</div>
<div ng-switch-default></div>
</div>
<span ng-if="item.values.date">
<!-- Compute the date range by the desired date compare strategy -->
{{
item.strings.date = (item.values.date | moment | date : (item.date_format || 'd MMM yyyy'));
item.values.debutdumois = (item.values.date | moment : 'YYYY/MM/01');
item.strings.debutdumois = (item.values.debutdumois | moment | date : 'mediumDate');
item.values.debutdumoisdelanneeprecedente = (item.values.date | moment : 'YYYY/MM/01' | momentadd : 'year' : -1 | moment : 'YYYY/MM/DD');
item.strings.debutdumoisdelanneeprecedente = (item.values.date | moment : 'YYYY/MM/01' | momentadd : 'year' : -1 | date : 'mediumDate');
item.values.debutdelannee = (item.values.date | moment : 'YYYY/01/01');
item.strings.debutdelannee = (item.values.debutdelannee | moment | date : 'mediumDate');
item.values.nowdumoisprecedent = (item.values.date | momentadd : 'month' : -1 | moment : 'YYYY/MM/DD');
item.strings.nowdumoisprecedent = (item.values.date | momentadd : 'month' : -1 | date : 'mediumDate');
item.values.nowdumoisprecedentdelanneeprecedente = (item.values.date | momentadd : 'month' : -13 | moment : 'YYYY/MM/DD');
item.strings.nowdumoisprecedentdelanneeprecedente = (item.values.date | momentadd : 'month' : -13 | date : 'mediumDate');
item.values.nowdumoisprecedentprecedent = (item.values.date | momentadd : 'month' : -2 | moment : 'YYYY/MM/DD');
item.strings.nowdumoisprecedentprecedent = (item.values.date | momentadd : 'month' : -2 | date : 'mediumDate');
item.values.nowdelanneeprecedente = (item.values.date | momentadd : 'year' : -1 | moment : 'YYYY/MM/DD');
item.strings.nowdelanneeprecedente = (item.values.date | momentadd : 'year' : -1 | date : 'mediumDate');
item.values.nowdelanneeprecedenteprecedente = (item.values.date | momentadd : 'year' : -2 | moment : 'YYYY/MM/DD');
item.strings.nowdelanneeprecedenteprecedente = (item.values.date | momentadd : 'year' : -2 | date : 'mediumDate');
item.values.moisplein = (item.values.debutdumois | momentadd : 'month' : -1 | moment : 'YYYY/MM/DD');
item.strings.moisplein = (item.values.debutdumois | momentadd : 'month' : -1 | date : 'MMM yyyy');
item.values.moispleindelanneeprecedente = (item.values.debutdumois | momentadd : 'month' : -13 | moment : 'YYYY/MM/DD');
item.strings.moispleindelanneeprecedente = (item.values.debutdumois | momentadd : 'month' : -13 | date : 'MMM yyyy');
item.values.moispleinprecedent = (item.values.debutdumois | momentadd : 'month' : -2 | moment : 'YYYY/MM/DD');
item.strings.moispleinprecedent = (item.values.debutdumois | momentadd : 'month' : -2 | date : 'MMM yyyy');
item.values.debutdelanneepleine = (item.values.debutdelannee | momentadd : 'year' : -1 | moment : 'YYYY/MM/DD');
item.strings.debutdelanneepleine = (item.values.debutdelannee | momentadd : 'year' : -1 | date : 'mediumDate');
item.values.debutdelanneeprecedente = (item.values.debutdelannee | momentadd : 'year' : -2 | moment : 'YYYY/MM/DD');
item.strings.debutdelanneeprecedente = (item.values.debutdelannee | momentadd : 'year' : -2 | date : 'mediumDate');
"" }}
<!-- Keep for later, possible upgrades of this templates with quarters and semesters -->
<!--
item.values.nowtrimestre = ((item.values.date | moment : 'MM') / 3 | math: 'ceil');
item.values.nowtrimestredebut = (item.values.date | moment : 'YYYY') + '/' + (item.values.nowtrimestre * 3 - 2 > 9?'':'0') + (item.values.nowtrimestre * 3 - 2);
item.values.nowtrimestrefin = (item.values.date | moment : 'YYYY') + '/' + (item.values.nowtrimestre * 3 > 9?'':'0') + (item.values.nowtrimestre * 3);
item.values.nowsemestre = ((item.values.date | moment : 'MM') / 6 | math: 'ceil');
item.values.nowsemestredebut = (item.values.date | moment : 'YYYY') + '/' + (item.values.nowsemestre * 6 - 5 > 9?'':'0') + (item.values.nowsemestre * 6 - 5);
item.values.nowsemestrefin = (item.values.date | moment : 'YYYY') + '/' + (item.values.nowsemestre * 6 > 9?'':'0') + (item.values.nowsemestre * 6);
-->
<div ng-switch="item.compare_date_strategie">
<div ng-switch-when="RM/RM-1">
{{
item.values.filtren = "[" + item.values.debutdumois + " TO " + item.values.date + "]";
item.values.filtrenmoinsun = "[" + item.values.moisplein + " TO " + item.values.nowdumoisprecedent + "]";
item.strings.filtrenstart = item.strings.debutdumois;
item.strings.filtrenend = item.strings.date;
item.strings.filtrenmoinsunstart = item.strings.moisplein;
item.strings.filtrenmoinsunend = item.strings.nowdumoisprecedent;
''}}
</div>
<div ng-switch-when="RM/RM-12">
{{
item.values.filtren = "[" + item.values.debutdumois + " TO " + item.values.date + "]";
item.values.filtrenmoinsun = "[" + item.values.debutdumoisdelanneeprecedente + " TO " + item.values.nowdelanneeprecedente + "]";
item.strings.filtrenstart = item.strings.debutdumois;
item.strings.filtrenend = item.strings.date;
item.strings.filtrenmoinsunstart = item.strings.debutdumoisdelanneeprecedente;
item.strings.filtrenmoinsunend = item.strings.nowdelanneeprecedente;
''}}
</div>
<div ng-switch-when="FM/FM-1">
{{
item.values.filtren = (item.values.moisplein | moment : 'YYYY/MM');
item.values.filtrenmoinsun = (item.values.moispleinprecedent | moment : 'YYYY/MM');
item.strings.filtrenstart = item.strings.moisplein;
item.strings.filtrenmoinsunstart = item.strings.moispleinprecedent;
''}}
</div>
<div ng-switch-when="FM/FM-12">
{{
item.values.filtren = (item.values.moisplein | moment : 'YYYY/MM');
item.values.filtrenmoinsun = (item.values.moispleindelanneeprecedente | moment : 'YYYY/MM');
item.strings.filtrenstart = item.strings.moisplein;
item.strings.filtrenmoinsunstart = item.strings.moispleindelanneeprecedente;
''}}
</div>
<div ng-switch-when="SM/SM-1">
{{
item.values.filtren = "[" + item.values.nowdumoisprecedent + " TO " + item.values.date + "]";
item.values.filtrenmoinsun = "[" + item.values.nowdumoisprecedentprecedent + " TO " + item.values.nowdumoisprecedent + "]";
item.strings.filtrenstart = item.strings.nowdumoisprecedent;
item.strings.filtrenend = item.strings.date;
item.strings.filtrenmoinsunstart = item.strings.nowdumoisprecedentprecedent;
item.strings.filtrenmoinsunend = item.strings.nowdumoisprecedent;
''}}
</div>
<div ng-switch-when="SM/SM-12">
{{
item.values.filtren = "[" + item.values.nowdumoisprecedent + " TO " + item.values.date + "]";
item.values.filtrenmoinsun = "[" + item.values.nowdumoisprecedentdelanneeprecedente + " TO " +
item.values.nowdelanneeprecedente + "]";
item.strings.filtrenstart = item.strings.nowdumoisprecedent;
item.strings.filtrenend = item.strings.date;
item.strings.filtrenmoinsunstart = item.strings.nowdumoisprecedentdelanneeprecedente;
item.strings.filtrenmoinsunend = item.strings.nowdelanneeprecedente;
''}}
</div>
<div ng-switch-when="RY/RY-1">
{{
item.values.filtren = "[" + item.values.debutdelannee + " TO " + item.values.date + "]";
item.values.filtrenmoinsun = "[" + item.values.debutdelanneepleine + " TO " + item.values.nowdelanneeprecedente + "]";
item.strings.filtrenstart = item.strings.debutdelannee;
item.strings.filtrenend = item.strings.date;
item.strings.filtrenmoinsunstart = item.strings.debutdelanneepleine;
item.strings.filtrenmoinsunend = item.strings.nowdelanneeprecedente;
''}}
</div>
<div ng-switch-when="FY/FY-1">
{{
item.values.filtren = (item.values.debutdelanneepleine | moment : 'YYYY');
item.values.filtrenmoinsun = (item.values.debutdelanneeprecedente | moment : 'YYYY');
item.strings.filtrenstart = item.strings.debutdelanneepleine;
item.strings.filtrenmoinsunstart = item.strings.debutdelanneeprecedente;
''}}
</div>
<div ng-switch-when="SY/SY-1">
{{
item.values.filtren = "[" + item.values.nowdelanneeprecedente + " TO " + item.values.date + "]";
item.values.filtrenmoinsun = "[" + item.values.nowdelanneeprecedenteprecedente + " TO " + item.values.nowdelanneeprecedente +
"]";
item.strings.filtrenstart = item.strings.nowdelanneeprecedente;
item.strings.filtrenend = item.strings.date;
item.strings.filtrenmoinsunstart = item.strings.nowdelanneeprecedenteprecedente;
item.strings.filtrenmoinsunend = item.strings.nowdelanneeprecedente;
''}}
</div>
<div ng-switch-default></div>
</div>
<span ng-if="item.values.filtren && item.values.filtrenmoinsun">
<ods-dataset-context context="kpidatectxn,kpidatectxnmoinsun"
kpidatectxn-dataset="{{item.dataset_id}}"
kpidatectxnmoinsun-dataset="{{item.valref_dataset_id}}">
<!-- Apply filters to date kpi contexts from configuration -->
<span ng-if="!item.dataset_advanced_refine_parameters">{{ kpidatectxn.parameters['refine.' + item.dataset_refine_key] = item.dataset_refine_value ; '' }}</span>
<span ng-if="item.dataset_advanced_refine_parameters"
ng-repeat="key in (item.dataset_advanced_refine_parameters | keys)">
{{ kpidatectxn.parameters[key] = item.dataset_advanced_refine_parameters[key]; '' }}
</span>
<span ng-if="!item.valref_dataset_advanced_refine_parameters">{{ kpidatectxnmoinsun.parameters['refine.' + item.valref_dataset_refine_key] = item.valref_dataset_refine_value ; '' }}</span>
<span ng-if="item.valref_dataset_advanced_refine_parameters"
ng-repeat="key in (item.valref_dataset_advanced_refine_parameters | keys)">
{{ kpidatectxnmoinsun.parameters[key] = item.valref_dataset_advanced_refine_parameters[key]; '' }}
</span>
<!-- Then apply filter from computed date ranges -->
{{ kpidatectxn.parameters['q.date'] = item.date_field_id + ':' + item.values.filtren ; '' }}
{{ kpidatectxnmoinsun.parameters['q.date'] = item.date_field_id + ':' + item.values.filtrenmoinsun ; '' }}
<span ods-aggregation-context="kpidatectxn"
ods-aggregation="result"
ods-aggregation-expression="{{item.aggregation_expression}}"
ods-aggregation-function="{{item.aggregation_function}}">
{{ item.result = result ; '' }}
</span>
<!-- KPI box component with date compare -->
<div class="card-flip"
ng-init="isFlipped = false"
ng-click="isFlipped = (item.flip_additional_description?!isFlipped:isFlipped)"
ng-class="{'not-flippable': !item.flip_additional_description,
'is-flipped': isFlipped}">
<div class="kpi-card card-face card-face-front">
<div class="kpi-card-top">
<div ng-if="item.image_url" class="kpi-img">
<img ng-src="{{ item.image_url }}"/>
</div>
</div>
<div class="kpi-card-middle">
<h2 class="kpi-value">
{{ item.result | number : (item.aggregation_precision || 0) }} <span class="kpi-unit">{{ item.aggregation_unit }}</span>
</h2>
<p class="kpi-title">
{{ item.title }}
<span ng-if="item.strings.filtrenend">
{{ dateFormat.prefix + item.strings.filtrenstart + dateFormat.infix + item.strings.filtrenend + dateFormat.suffix }}
</span>
<span ng-if="!item.strings.filtrenend">
{{ item.strings.filtrenstart | capitalize }}
</span>
</p>
<div class="kpi-valref"
ng-if="item.compare_strategie">
<!-- KPI box component -->
<div ods-aggregation-context="kpidatectxnmoinsun"
ods-aggregation="resultvalref"
ods-aggregation-expression="{{item.valref_aggregation_expression}}"
ods-aggregation-function="{{item.valref_aggregation_function}}">
<div ng-switch="item.compare_strategie">
<div ng-switch-when="none">
{{ item.value = resultvalref ; '' }}
</div>
<div ng-switch-when="diff">
{{ item.value = (item.result/resultvalref)*100 ; '' }}
</div>
<div ng-switch-when="progression">
{{ item.value = (item.result - resultvalref)/resultvalref*100 ; '' }}
</div>
<div ng-switch-default>
{{ item.value = '' }}
</div>
</div>
<div ng-if="item.compare_strategie == 'diff' || item.compare_strategie == 'progression'">
<span class="kpi-valref-value"><span ng-if="item.compare_strategie == 'progression'">{{ item.value > 0 ? '+' : '' }}</span>{{ item.value | number : (item.valref_aggregation_precision || 0) }}</span>
<span class="kpi-valref-unit">{{ item.valref_aggregation_unit }}</span>
<span class="kpi-valref-carret" ng-if="item.higher">
<i ng-if="item.value > 0" class="fa fa-caret-up"
ng-class="{'kpi-valref-carret-good': item.higher == 'isbetter'}" aria-hidden="true"></i>
<i ng-if="item.value < 0" class="fa fa-caret-down"
ng-class="{'kpi-valref-carret-good': item.higher != 'isbetter'}" aria-hidden="true"></i>
</span>
<div class="kpi-valref-text">
{{ item.valref_prefix }}
<span class="kpi-valref-secondaryvalue">{{ resultvalref | number : (item.valref_aggregation_precision || 0) }}
<span class="kpi-unit">{{ item.aggregation_unit }}</span>
</span>
{{ item.valref_suffix }}
<span class="kpi-valref-date">{{
(item.strings.filtrenmoinsunend?dateFormat.prefix:'') +
item.strings.filtrenmoinsunstart +
(item.strings.filtrenmoinsunend?dateFormat.infix + item.strings.filtrenmoinsunend + dateFormat.suffix:'')
}}
</span>
</div>
</div>
<div ng-if="!item.compare_strategie">
<div class="kpi-valref-text">
<span class="kpi-valref-value">{{ resultvalref | number : (item.valref_aggregation_precision || 0) }}</span>
<span class="kpi-valref-unit">{{ item.valref_aggregation_unit }}</span>
</div>
{{ item.valref_prefix }}
{{ item.valref_suffix }}
</div>
<!--
<div ng-if="!(item.compare_strategie == 'diff' || item.compare_strategie == 'progression')">
Wrong setup : unknown compare_strategie (can be diff or progression)
</div>-->
</div>
</div>
<div ng-if="item.link" class="kpi-link">
<a target="_blank"
ng-click="values.popin = (item.link_mode == 'popin' ? (item.title | slugify) : false); item.link_mode == 'popin' ? $event.preventDefault() : ''; $event.stopPropagation()"
ng-href="{{ item.link }}">{{ item.link_label || "See the data" }}</a>
</div>
</div>
<div ng-if="item.flip_hint"
class="kpi-card-bottom">
<p class="hint">{{ item.flip_hint }}</p>
</div>
</div>
<div class="kpi-card card-face card-face-back">
<p>
{{ item.flip_additional_description }}
</p>
<a target="_blank" ng-if="item.flip_additional_link" ng-href="{{ item.flip_additional_link }}">
{{ item.flip_additional_link_label || item.flip_additional_link }}
</a>
</div>
</div>
</ods-dataset-context>
</span>
</span>
</span>
</ods-dataset-context> </div>
<!-- Pop-ins of all KPIs -->
<div ng-repeat="item in kpis"
class="ods-pop-in"
ng-if="values.popin == (item.title | slugify)">
<div class="ods-pop-in__container iframe-container">
<iframe ng-src="{{ item.link }}"
width="100%"
height="100%"></iframe>
</div>
<div class="ods-pop-in__backdrop"
ng-click="values.popin = false"></div>
</div>
</div>
</section>
</div>
/* Style of your page goes here */
/* Variables & Shared classes
========================================================================== */
:root {
--secondary-color: #142E7B;
--positive-color: #24b837;
--negative-color: #d61e00; }
/* General Layout
========================================================================== */
@media screen and (min-width: 992px) {
.row-equal-height {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-bottom: 20px; }
/* Fix for early content wrapping in Safari*/
.row-equal-height:before,
.row-equal-height:after {
content: normal; } }
.row-equal-height {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-bottom: 20px; }
/* Fix for early content wrapping in Safari*/
.row-equal-height:before,
.row-equal-height:after {
content: normal; }
.margin-bottom-20 {
margin-bottom: 20px; }
/* Tab Pills
========================================================================== */
.ods-simple-tabs-container {
margin-top: 1.5rem; }
.tab-pills .ods-simple-tabs-nav-item {
width: 100%; }
.tab-pills .ods-simple-tabs-nav-item:first-child .ods-simple-tabs-nav-link {
border-radius: 30px 0 0 30px; }
.tab-pills .ods-simple-tabs-nav-item:last-child .ods-simple-tabs-nav-link {
border-radius: 0 30px 30px 0; }
.tab-pills .ods-simple-tabs-nav-item:first-child:last-child .ods-simple-tabs-nav-link {
border-radius: 30px; }
.tab-pills .ods-simple-tabs-nav-link {
padding: 0.5rem 1.5rem;
border-bottom: 0;
background-color: transparent;
border: 1px solid var(--highlight);
text-transform: uppercase;
font-size: 1.2em;
font-weight: 300; }
.tab-pills .ods-simple-tabs-nav-link:hover {
opacity: 0.65; }
.tab-pills .ods-simple-tabs-nav-link-active {
background-color: var(--highlight); }
.tab-pills .ods-simple-tabs-nav-link-active,
.tab-pills .ods-simple-tabs-nav-link-active:hover {
color: #FFFFFF; }
/* KPI Card
========================================================================== */
.kpi-card {
background-color: var(--boxes-background);
height: 100%;
padding: 39px;
border-radius: 4px;
margin-bottom: 20px;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
text-align: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
text-align: center; }
.kpi-icon {
color: var(--highlight);
color: var(--secondary-color);
font-size: 4rem;
margin-top: 0;
margin-bottom: 13px;
max-width: 100%; }
.kpi-img {
width: 100px;
height: 100px;
margin-bottom: 26px; }
.kpi-img img {
width: 100%;
height: 100%;
border-radius: 100%;
object-fit: cover; }
.kpi-value {
font-weight: 600;
color: var(--secondary-color);
font-size: 3rem;
margin-top: 0;
margin-bottom: 13px;
max-width: 100%;
white-space: nowrap; }
.kpi-value .kpi-unit {
font-size: 1.5rem;
color: inherit; }
.kpi-title {
color: var(--text);
font-size: 1.1rem;
line-height: 1.5;
font-weight: 500;
margin-top: 0;
margin-bottom: 0;
max-width: 100%; }
.kpi-link {
margin-top: 13px;
align-self: end; }
.kpi-link a {
padding: 6px 13px;
border-radius: 9999px;
border: 1px solid var(--links); }
.kpi-link a:hover {
text-decoration: none;
background-color: var(--links);
color: white; }
.kpi-description-detail {
opacity: 0.9;
font-style: italic;
font-size: 0.9em;
margin-top: 0.8em; }
/* Flip component
========================================================================== */
.kpi-card.card-face-front {
justify-content: start; }
.card-flip {
position: relative;
height: 100%;
width: 100%;
-webkit-transition: -webkit-transform 0.2s;
transition: -webkit-transform 0.2s;
-o-transition: transform 0.2s;
transition: transform 0.2s;
transition: transform 0.2s, -webkit-transform 0.2s;
-webkit-perspective: 1200px;
perspective: 1200px;
-webkit-transform-style: preserve-3d;
transform-style: preserve-3d;
border-radius: 4px;
margin-bottom: 20px;
box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.13); }
.card-face {
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
height: 100%;
width: 100%;
margin-bottom: 0;
/* remove kpi-card margin bottom */ }
.card-face-back {
position: absolute;
top: 0;
left: 0;
-webkit-transform: rotateY(180deg);
transform: rotateY(180deg);
-webkit-box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12);
box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12); }
.card-flip.is-flipped {
-webkit-transform: rotateY(180deg);
transform: rotateY(180deg); }
.card-flip:not(.not-flippable):hover {
-webkit-transform: rotateY(5deg);
transform: rotateY(5deg);
-webkit-box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12);
box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12); }
.card-flip.is-flipped:hover {
-webkit-transform: rotateY(180deg);
transform: rotateY(180deg); }
.kpi-card-middle {
display: grid;
height: 100%; }
.kpi-card-bottom {
align-self: flex-end;
margin: 9px -26px -30px 0; }
p.hint {
color: darkgrey; }
/* Not flippable */
.card-flip.not-flippable {
cursor: default; }
.card-flip.not-flippable .card-face-back {
display: none; }
/* Print safe */
@media print {
.col-print-6 {
width: 50%; }
.card-flip {
page-break-before: auto;
page-break-after: auto;
page-break-inside: avoid; }
/* .card-face-back {
display: none;
}*/ }
/* Value reference / compare specific */
.kpi-valref {
font-size: 1em;
font-weight: 200;
margin: 15px 0; }
.kpi-valref .kpi-valref-unit,
.kpi-valref .kpi-valref-value {
color: var(--secondary-color);
font-size: 2rem;
font-weight: 400; }
.kpi-valref .kpi-valref-carret i.fa {
font-size: 3.5em;
margin-left: 0.2em;
line-height: 10px;
vertical-align: sub;
color: var(--negative-color); }
.kpi-valref .kpi-valref-carret i.fa.kpi-valref-carret-good {
color: var(--positive-color); }
.kpi-valref .kpi-valref-text {
margin-top: 5px;
font-weight: 300; }
.kpi-valref .kpi-valref-secondaryvalue {
white-space: nowrap; }
/* pop-in
========================================================================== */
.ods-pop-in__container {
z-index: 102; }
.ods-pop-in__backdrop {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.7);
z-index: 101;
cursor: auto; }
.ods-pop-in__backdrop:before {
content: url('data:image/svg+xml; utf8, <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 -2 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-arrow-left"><line x1="19" y1="12" x2="5" y2="12"></line><polyline points="12 19 5 12 12 5"></polyline></svg>');
background-color: white;
width: 40px;
height: 40px;
border-radius: 100%;
position: absolute;
top: 20px;
left: 20px;
z-index: 103;
justify-content: center;
align-items: center;
cursor: pointer;
display: none; }
.iframe-container {
height: 80%;
max-height: inherit;
width: 100%;
padding: 10px;
max-width: inherit;
margin-left: 120px;
margin-right: 120px; }
.iframe-container iframe {
border: none; }
@media screen and (max-width: 768px) {
.ods-pop-in__backdrop:before {
display: flex; }
.iframe-container {
height: calc(100% - 80px);
margin: 0; }
.ods-pop-in {
align-items: flex-end; } }
/** Print **/
@media print {
header, nav, footer {
display: none; } }
/**
SPECIFIC
**/
.depuis-le-dbut-de-lanne .kpi-valref {
visibility: hidden; }