dashboard widgets are configurable via config (#269)
This commit is contained in:
@@ -9,26 +9,11 @@
|
||||
{% block main %}
|
||||
|
||||
{% for row in widget_rows %}
|
||||
{% if row.title %}
|
||||
{{ widgets.page_header(row.title) }}
|
||||
{% if row.type == constant('App\\Model\\DashboardSection::TYPE_CHART') %}
|
||||
{% embed 'dashboard/section-chart.html.twig' with { section: row } %}{% endembed %}
|
||||
{% else %}
|
||||
{% embed 'dashboard/section-simple.html.twig' with { section: row } %}{% endembed %}
|
||||
{% endif %}
|
||||
|
||||
{% set width = row.widgets|length %}
|
||||
{% set rawWidth = 12 / width %}
|
||||
{% set columnWidth = rawWidth|round(0, 'floor') %}
|
||||
<div class="row">
|
||||
{% for widgetTemplate in row.widgets %}
|
||||
<div class="col-md-{{ columnWidth }} col-sm-{{ columnWidth * 2 }} col-xs-{{ columnWidth * 4 }}">
|
||||
{{
|
||||
include(
|
||||
template_from_string(
|
||||
'{% import "macros/widgets.html.twig" as widgets %}' ~ widgetTemplate
|
||||
)
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
180
templates/dashboard/section-chart.html.twig
Normal file
180
templates/dashboard/section-chart.html.twig
Normal file
@@ -0,0 +1,180 @@
|
||||
{% set chartWidget = section.widgets.0 %}
|
||||
{% set colors = chartWidget.color|split(';') %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="box">
|
||||
{#
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">{{ section.title|trans }}</h3>
|
||||
<div class="box-tools pull-right">
|
||||
<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i>
|
||||
</button>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-box-tool dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-wrench"></i></button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li><a href="#">Action</a></li>
|
||||
<li><a href="#">Another action</a></li>
|
||||
<li><a href="#">Something else here</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="#">Separated link</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<button type="button" class="btn btn-box-tool" data-widget="remove"><i class="fa fa-times"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
#}
|
||||
<div class="box-body">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
{#
|
||||
<p class="text-center">
|
||||
<strong>
|
||||
{% for yearName, year in chartWidget.data|sort %}
|
||||
{% if loop.first or loop.last %}
|
||||
{% for id, month in year.months %}
|
||||
{% if loop.first %}
|
||||
{{ ('month.'~loop.index)|trans }} {{ yearName }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if loop.first %} - {% endif %}
|
||||
{% endfor %}
|
||||
</strong>
|
||||
</p>
|
||||
#}
|
||||
<div class="chart">
|
||||
<canvas id="timeChart" style="height: 180px;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
{#
|
||||
<div class="col-md-4">
|
||||
<p class="text-center">
|
||||
<strong>Goal Completion</strong>
|
||||
</p>
|
||||
<div class="progress-group">
|
||||
<span class="progress-text">Add Products to Cart</span>
|
||||
<span class="progress-number"><b>160</b>/200</span>
|
||||
|
||||
<div class="progress sm">
|
||||
<div class="progress-bar progress-bar-aqua" style="width: 80%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress-group">
|
||||
<span class="progress-text">Complete Purchase</span>
|
||||
<span class="progress-number"><b>310</b>/400</span>
|
||||
<div class="progress sm">
|
||||
<div class="progress-bar progress-bar-red" style="width: 80%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress-group">
|
||||
<span class="progress-text">Visit Premium Page</span>
|
||||
<span class="progress-number"><b>480</b>/800</span>
|
||||
<div class="progress sm">
|
||||
<div class="progress-bar progress-bar-green" style="width: 80%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress-group">
|
||||
<span class="progress-text">Send Inquiries</span>
|
||||
<span class="progress-number"><b>250</b>/500</span>
|
||||
<div class="progress sm">
|
||||
<div class="progress-bar progress-bar-yellow" style="width: 80%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
#}
|
||||
</div>
|
||||
</div>
|
||||
{% if section.widgets|length > 1 %}
|
||||
<div class="box-footer">
|
||||
<div class="row">
|
||||
{% set width = (section.widgets|length) - 1 %}
|
||||
{% set rawWidth = 12 / width %}
|
||||
{% set columnWidth = rawWidth|round(0, 'floor') %}
|
||||
{% for widget in section.widgets|slice(1, width) %}
|
||||
{% set data = widget.data %}
|
||||
{% if widget.dataType == constant('App\\Model\\Widget::DATA_TYPE_DURATION') %}
|
||||
{% set data = widget.data|duration %}
|
||||
{% elseif widget.dataType == constant('App\\Model\\Widget::DATA_TYPE_MONEY') %}
|
||||
{% set data = widget.data|money %}
|
||||
{% endif %}
|
||||
<div class="col-sm-{{ columnWidth }} col-xs-{{ columnWidth * 2 }}">
|
||||
<div class="description-block border-right">
|
||||
{#<span class="description-percentage text-green"><i class="fa fa-caret-up"></i> 17%</span>#}
|
||||
{#<span class="description-percentage text-yellow"><i class="fa fa-caret-left"></i> 0%</span>#}
|
||||
{#<span class="description-percentage text-green"><i class="fa fa-caret-up"></i> 20%</span>#}
|
||||
{#<span class="description-percentage text-red"><i class="fa fa-caret-down"></i> 18%</span>#}
|
||||
<h5 class="description-header">{{ data }}</h5>
|
||||
<span class="description-text">{{ widget.title|trans }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<script>
|
||||
$(function () {
|
||||
'use strict';
|
||||
var timeChartData = {
|
||||
labels : [
|
||||
{% for i in 1..12 %}
|
||||
'{{ ('month.' ~i)|trans }}'
|
||||
{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
],
|
||||
datasets: [
|
||||
{% for yearName, year in chartWidget.data %}
|
||||
{% set yearColors = colors[loop.index-1]|split('|') %}
|
||||
{
|
||||
label : '{{ yearName }}',
|
||||
backgroundColor : '{{ yearColors.1 }}',
|
||||
borderColor : '{{ yearColors.0 }}',
|
||||
pointRadius : 2,
|
||||
borderWidth : 1,
|
||||
pointHitRadius : 10,
|
||||
lineTension : 0.3,
|
||||
data : [
|
||||
{% for month in year.months %}
|
||||
{{ (month.totalDuration / 3600)|round }}
|
||||
{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
]
|
||||
}
|
||||
{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
]
|
||||
};
|
||||
|
||||
var timeChartOptions = {
|
||||
maintainAspectRatio : true,
|
||||
responsive : true,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
gridLines: {
|
||||
display: false
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
gridLines: {
|
||||
display: true,
|
||||
color: 'rgba(0,0,0,.05)',
|
||||
lineWidth: 1
|
||||
}
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
var timeChartCanvas = $('#timeChart').get(0).getContext('2d');
|
||||
|
||||
var timeChart = new Chart(
|
||||
timeChartCanvas, {
|
||||
type: 'line',
|
||||
data: timeChartData,
|
||||
options: timeChartOptions
|
||||
}
|
||||
);
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
34
templates/dashboard/section-simple.html.twig
Normal file
34
templates/dashboard/section-simple.html.twig
Normal file
@@ -0,0 +1,34 @@
|
||||
{% import "macros/widgets.html.twig" as widgets %}
|
||||
|
||||
{% if section.title %}
|
||||
{{ widgets.page_header(section.title) }}
|
||||
{% endif %}
|
||||
|
||||
{% set width = section.widgets|length %}
|
||||
{% set rawWidth = 12 / width %}
|
||||
{% set columnWidth = rawWidth|round(0, 'floor') %}
|
||||
<div class="row">
|
||||
{% for widget in section.widgets %}
|
||||
{% set columnSize = columnWidth %}
|
||||
{% if width == 5 and (loop.first or loop.last) %}
|
||||
{% set columnSize = columnWidth + 1 %}
|
||||
{% endif %}
|
||||
<div class="col-md-{{ columnSize }} col-sm-{{ columnSize * 2 }} col-xs-{{ columnSize * 4 }}">
|
||||
|
||||
{% set data = widget.data %}
|
||||
{% if widget.dataType == constant('App\\Model\\Widget::DATA_TYPE_DURATION') %}
|
||||
{% set data = widget.data|duration %}
|
||||
{% elseif widget.dataType == constant('App\\Model\\Widget::DATA_TYPE_MONEY') %}
|
||||
{% set data = widget.data|money %}
|
||||
{% endif %}
|
||||
|
||||
{% set url = null %}
|
||||
{% if widget.route %}
|
||||
{% set url = path(widget.route, widget.routeOptions) %}
|
||||
{% endif %}
|
||||
|
||||
{% set embedOptions = {data: data, title: widget.title, icon: widget.icon, color: widget.color, url: url} %}
|
||||
{% embed 'embeds/widget-'~widget.type~'.html.twig' with embedOptions %}{% endembed %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
10
templates/embeds/widget-counter.html.twig
Normal file
10
templates/embeds/widget-counter.html.twig
Normal file
@@ -0,0 +1,10 @@
|
||||
<div class="info-box">
|
||||
<span class="info-box-icon bg-{{ color|default(kimai_context.box_color) }}"><i class="{{ icon|icon(icon) }}"></i></span>
|
||||
|
||||
<div class="info-box-content">
|
||||
{% if url %}<a href="{{ url }}" class="small-box-footer">{% endif %}
|
||||
<span class="info-box-text">{{ title|trans }}</span>
|
||||
<span class="info-box-number">{{ data }}</span>
|
||||
{% if url %}</a>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
14
templates/embeds/widget-more.html.twig
Normal file
14
templates/embeds/widget-more.html.twig
Normal file
@@ -0,0 +1,14 @@
|
||||
<div class="small-box bg-{{ color|default(kimai_context.box_color) }}">
|
||||
<div class="inner">
|
||||
<h3>{{ data }}<sup style="font-size: 20px">{{ unit|default('') }}</sup></h3>
|
||||
<p>{{ title|trans }}</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="{{ icon|icon(icon) }}"></i>
|
||||
</div>
|
||||
{% if url %}
|
||||
<a href="{{ url }}" class="small-box-footer">
|
||||
{{ 'more.info.link'|trans }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -108,24 +108,6 @@
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro info_box_counter(title, amount, icon, color, url) %}
|
||||
<div class="info-box">
|
||||
<span class="info-box-icon bg-{{ color|default(kimai_context.box_color) }}"><i class="{{ icon|icon(icon) }}"></i></span>
|
||||
|
||||
<div class="info-box-content">
|
||||
{# this is a ugly hack, make me look nicely (dashboard widget with link) #}
|
||||
{% if url %}
|
||||
<a href="{{ url }}" class="small-box-footer">
|
||||
{% endif %}
|
||||
<span class="info-box-text">{{ title|trans }}</span>
|
||||
<span class="info-box-number">{{ amount }}</span>
|
||||
{% if url %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro info_box_progress(title, description, amount, percentage, icon, color) %}
|
||||
<div class="info-box bg-{{ color|default(kimai_context.box_color) }}">
|
||||
<span class="info-box-icon"><i class="{{ icon|icon(icon) }}"></i></span>
|
||||
@@ -145,21 +127,6 @@
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro info_box_more(title, amount, unit, url, icon, color) %}
|
||||
<div class="small-box bg-{{ color|default(kimai_context.box_color) }}">
|
||||
<div class="inner">
|
||||
<h3>{{ amount }}<sup style="font-size: 20px">{{ unit|default('') }}</sup></h3>
|
||||
<p>{{ title|trans }}</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="{{ icon|icon(icon) }}"></i>
|
||||
</div>
|
||||
<a href="{{ url }}" class="small-box-footer">
|
||||
{{ 'more.info.link'|trans }} <i class="{{ icon|icon(icon) }}"></i>
|
||||
</a>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro button_group_dropdown(title, actions) %}
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-default">{{ title|trans }}</button>
|
||||
|
||||
@@ -28,67 +28,79 @@
|
||||
{% endif %}
|
||||
|
||||
<script type="text/javascript">
|
||||
var barChartOptions = {
|
||||
scaleBeginAtZero: true,
|
||||
scaleShowGridLines: true,
|
||||
scaleGridLineColor: "rgba(0,0,0,.05)",
|
||||
scaleGridLineWidth: 1,
|
||||
scaleShowHorizontalLines: true,
|
||||
scaleShowVerticalLines: true,
|
||||
barShowStroke: true,
|
||||
barStrokeWidth: 2,
|
||||
barValueSpacing: 5,
|
||||
barDatasetSpacing: 1,
|
||||
responsive: true,
|
||||
maintainAspectRatio: true,
|
||||
tooltipTemplate: "<%= value %> {{ 'label.hours'|trans }}",
|
||||
};
|
||||
var barChartLabels = [
|
||||
{% for i in 1..11 %}
|
||||
"{{ ('month.' ~i)|trans }}",
|
||||
{% endfor %}
|
||||
"{{ 'month.12'|trans }}"
|
||||
];
|
||||
var userProfileChartOptions = {
|
||||
maintainAspectRatio : true,
|
||||
responsive : true,
|
||||
legend : false,
|
||||
barPercentage : 0.5,
|
||||
categoryPercentage : 0.9,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
gridLines: {
|
||||
display: false
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
gridLines: {
|
||||
display: true,
|
||||
color: 'rgba(0,0,0,.05)',
|
||||
lineWidth: 1
|
||||
}
|
||||
}]
|
||||
}
|
||||
};
|
||||
var userProfileChartLabels = [
|
||||
{% for i in 1..12 %}
|
||||
'{{ ('month.' ~i)|trans }}'
|
||||
{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
];
|
||||
</script>
|
||||
|
||||
{% for year,yearStat in years %}
|
||||
<h2>{{ year }}</h2>
|
||||
<div class="chart">
|
||||
<canvas id="barChart{{ year }}" style="height: 230px;"></canvas>
|
||||
<canvas id="userProfileChart{{ year }}" style="height: 200px;"></canvas>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
var barChartData{{ year }} = {
|
||||
labels: barChartLabels,
|
||||
var userProfileChartData{{ year }} = {
|
||||
labels: userProfileChartLabels,
|
||||
datasets: [
|
||||
{
|
||||
{# label: '# hours',#}
|
||||
fillColor: "#00a65a",
|
||||
strokeColor: "#00a65a",
|
||||
pointColor: "#00a65a",
|
||||
pointStrokeColor: "#c1c7d1",
|
||||
pointHighlightFill: "#fff",
|
||||
pointHighlightStroke: "rgba(220,220,220,1)",
|
||||
label: '{{ year }}',
|
||||
backgroundColor : '#00a65a',
|
||||
borderColor : '#00a65a',
|
||||
data: [
|
||||
{% for i in 1..11 %}
|
||||
{{ (yearStat.month(i).totalDuration / 3600)|round }},
|
||||
{% for month in yearStat.months %}
|
||||
{{ (month.totalDuration / 3600)|round }}
|
||||
{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
{{ (yearStat.month(12).totalDuration / 3600)|round }}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
{% if tab == "charts" %}
|
||||
$(function () {
|
||||
var barChartCanvas{{ year }} = $("#barChart{{ year }}").get(0).getContext("2d");
|
||||
var barChart = new Chart(barChartCanvas{{ year }});
|
||||
barChart.Bar(barChartData{{ year }}, barChartOptions);
|
||||
});
|
||||
|
||||
var userProfileChartCanvas{{ year }} = $("#userProfileChart{{ year }}").get(0).getContext("2d");
|
||||
|
||||
{% if tab == 'charts' %}
|
||||
var userProfileChart{{ year }} = new Chart(
|
||||
userProfileChartCanvas{{ year }}, {
|
||||
type: 'bar',
|
||||
data: userProfileChartData{{ year }},
|
||||
options: userProfileChartOptions
|
||||
}
|
||||
);
|
||||
{% endif %}
|
||||
$('a[href="#charts"]').on('shown.bs.tab', function(e){
|
||||
var barChartCanvas{{ year }} = $("#barChart{{ year }}").get(0).getContext("2d");
|
||||
var barChart = new Chart(barChartCanvas{{ year }});
|
||||
barChart.Bar(barChartData{{ year }}, barChartOptions);
|
||||
|
||||
$('a[href="#charts"]').on('shown.bs.tab', function(event){
|
||||
var userProfileChart{{ year }} = new Chart(
|
||||
userProfileChartCanvas{{ year }}, {
|
||||
type: 'bar',
|
||||
data: userProfileChartData{{ year }},
|
||||
options: userProfileChartOptions
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -140,12 +152,12 @@
|
||||
<ul class="list-group list-group-unbordered">
|
||||
|
||||
<li class="list-group-item">
|
||||
<b>{{ 'stats.durationThisMonth'|trans }}</b> <a class="pull-right">{{ stats.durationThisMonth|duration }}</a>
|
||||
<b>{{ 'stats.durationMonth'|trans }}</b> <a class="pull-right">{{ stats.durationThisMonth|duration }}</a>
|
||||
</li>
|
||||
|
||||
{% if is_granted('ROLE_ADMIN') %}
|
||||
<li class="list-group-item">
|
||||
<b>{{ 'stats.amountThisMonth'|trans }}</b> <a class="pull-right">{{ stats.amountThisMonth|money }}</a>
|
||||
<b>{{ 'stats.amountMonth'|trans }}</b> <a class="pull-right">{{ stats.amountThisMonth|money }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
@@ -220,13 +232,13 @@
|
||||
<div class="col-sm-3 border-right">
|
||||
<div class="description-block">
|
||||
<h5 class="description-header">{{ stats.durationThisMonth|duration }}</h5>
|
||||
<span class="description-text">{{ 'stats.durationThisMonth'|trans }}</span>
|
||||
<span class="description-text">{{ 'stats.durationMonth'|trans }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3 border-right">
|
||||
<div class="description-block">
|
||||
<h5 class="description-header">{{ stats.amountThisMonth|money }}</h5>
|
||||
<span class="description-text">{{ 'stats.amountThisMonth'|trans }}</span>
|
||||
<span class="description-text">{{ 'stats.amountMonth'|trans }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user