link reporting screen for users and month charts (#1842)

This commit is contained in:
Kevin Papst
2020-07-26 20:28:48 +02:00
committed by GitHub
parent 130e6ac057
commit 599b18ac99
9 changed files with 80 additions and 23 deletions

View File

@@ -20,6 +20,7 @@ use App\Form\UserRolesType;
use App\Form\UserTeamsType;
use App\Repository\TeamRepository;
use App\Repository\TimesheetRepository;
use App\Utils\LocaleSettings;
use Doctrine\Common\Collections\ArrayCollection;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
@@ -69,7 +70,7 @@ class ProfileController extends AbstractController
* @Route(path="/{username}", name="user_profile", methods={"GET"})
* @Security("is_granted('view', profile)")
*/
public function indexAction(User $profile, TimesheetRepository $repository)
public function indexAction(User $profile, TimesheetRepository $repository, LocaleSettings $localeSettings)
{
$userStats = $repository->getUserStatistics($profile);
$monthlyStats = $repository->getMonthlyStats($profile);
@@ -79,6 +80,7 @@ class ProfileController extends AbstractController
'user' => $profile,
'stats' => $userStats,
'years' => $monthlyStats,
'stat_date_format' => $localeSettings->getDatePickerFormat(),
];
return $this->render('user/stats.html.twig', $viewVars);

View File

@@ -64,21 +64,23 @@ final class ReportingController extends AbstractController
$values->setDate($this->dateTimeFactory->getStartOfMonth());
$form = $this->createForm(MonthByUserForm::class, $values, [
'method' => 'POST',
'include_user' => $this->isGranted('view_other_timesheet') && $user->hasTeamAssignment(),
]);
$form->handleRequest($request);
$form->submit($request->query->all(), false);
if ($form->isSubmitted() && !$form->isValid()) {
if ($values->getUser() === null) {
$values->setUser($user);
$values->setDate($this->dateTimeFactory->getStartOfMonth());
}
if ($user !== $values->getUser() && !$this->isGranted('view_other_timesheet')) {
throw new AccessDeniedException('User is not allowed to see other users timesheet');
}
if ($values->getDate() === null) {
$values->setDate($this->dateTimeFactory->getStartOfMonth());
}
$start = $values->getDate();
$start->modify('first day of 00:00:00');
@@ -124,16 +126,18 @@ final class ReportingController extends AbstractController
$values = new MonthlyUserList();
$values->setDate($this->dateTimeFactory->getStartOfMonth());
$form = $this->createForm(MonthlyUserListForm::class, $values, [
'method' => 'POST',
]);
$form = $this->createForm(MonthlyUserListForm::class, $values, []);
$form->handleRequest($request);
$form->submit($request->query->all(), false);
if ($form->isSubmitted() && !$form->isValid()) {
$values->setDate($this->dateTimeFactory->getStartOfMonth());
}
if ($values->getDate() === null) {
$values->setDate($this->dateTimeFactory->getStartOfMonth());
}
$start = $values->getDate();
$start->modify('first day of 00:00:00');

View File

@@ -20,6 +20,8 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Custom form field type to select a month via picker and select previous and next month.
*
* Always falls back to the current month if none or an invalid date is given.
*/
final class MonthPickerType extends AbstractType
{
@@ -67,6 +69,7 @@ final class MonthPickerType extends AbstractType
$date = $this->dateTime->getStartOfMonth();
}
$view->vars['month'] = $date;
$view->vars['previousMonth'] = (clone $date)->modify('-1 month');
$view->vars['nextMonth'] = (clone $date)->modify('+1 month');
$view->vars['momentFormat'] = (new MomentFormatConverter())->convert($this->localeSettings->getDateTypeFormat());

View File

@@ -17,6 +17,16 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
class MonthByUserForm extends AbstractType
{
/**
* Simplify cross linking between pages by removing the block prefix.
*
* @return null|string
*/
public function getBlockPrefix()
{
return null;
}
/**
* {@inheritdoc}
*/
@@ -37,6 +47,8 @@ class MonthByUserForm extends AbstractType
$resolver->setDefaults([
'data_class' => MonthByUser::class,
'include_user' => false,
'csrf_protection' => false,
'method' => 'GET',
]);
}
}

View File

@@ -16,6 +16,16 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
class MonthlyUserListForm extends AbstractType
{
/**
* Simplify cross linking between pages by removing the block prefix.
*
* @return null|string
*/
public function getBlockPrefix()
{
return null;
}
/**
* {@inheritdoc}
*/
@@ -31,6 +41,8 @@ class MonthlyUserListForm extends AbstractType
{
$resolver->setDefaults([
'data_class' => MonthlyUserList::class,
'csrf_protection' => false,
'method' => 'GET',
]);
}
}

View File

@@ -137,13 +137,18 @@ final class LocaleHelper
if (null === $this->moneyFormatterNoCurrency) {
// if anyone knows a better way of achieving this, please let me know!
$this->moneyFormatterNoCurrency = new NumberFormatter($this->locale, NumberFormatter::CURRENCY);
$pattern = $this->moneyFormatterNoCurrency->getPattern();
$pattern = str_replace('¤ ', '¤', $pattern);
$pattern = str_replace(' ¤', '¤', $pattern);
$this->moneyFormatterNoCurrency->setPattern($pattern);
$this->moneyFormatterNoCurrency->setSymbol(NumberFormatter::CURRENCY_SYMBOL, '');
$this->moneyFormatterNoCurrency->setSymbol(NumberFormatter::CURRENCY_CODE, '');
$this->moneyFormatterNoCurrency->setTextAttribute(NumberFormatter::CURRENCY_CODE, '');
$this->moneyFormatterNoCurrency->setSymbol(NumberFormatter::INTL_CURRENCY_SYMBOL, '');
$this->moneyFormatterNoCurrency->setSymbol(NumberFormatter::CURRENCY_SYMBOL, '');
// don't understand why this is needed, I'd say this shouldn't be necessary after the above calls
// even worse: the logic changes either between PHP/ICU versions
$pattern = $this->moneyFormatterNoCurrency->getPattern();
$pattern = str_replace(['¤ ', ' ¤', '-¤', ' XXX', 'XXX '], '¤', $pattern);
$pattern = str_replace('XXX', '¤', $pattern);
$pattern = str_replace('¤', '', $pattern);
$this->moneyFormatterNoCurrency->setPattern($pattern);
}
return $this->moneyFormatterNoCurrency;

View File

@@ -52,7 +52,7 @@
<i class="{{ 'left'|icon }}"></i>
</a>
<a class="btn btn-default" href="#" onclick="return false;">
<span id="{{ form.vars.id }}_month_name">{{ data|month_name|trans ~ ' ' ~ data|date_format('Y') }}</span>
<span id="{{ form.vars.id }}_month_name">{{ month|month_name|trans ~ ' ' ~ month|date_format('Y') }}</span>
</a>
<a class="btn btn-default btn-right" href="#" onclick="$('#{{ form.vars.id }}').val('{{nextMonth|date_short}}').change()">
<i class="{{ 'right'|icon }}"></i>

View File

@@ -58,6 +58,7 @@
{% set actions = {} %}
{% if user.id is not empty %}
{% set view_other = is_granted('view_other_timesheet') %}
{% if is_granted('view', user) %}
{% set actions = actions|merge({'profile-stats': {'url': path('user_profile', {'username' : user.username})}}) %}
{% endif %}
@@ -70,7 +71,12 @@
{% if actions|length > 0 %}
{% set actions = actions|merge({'divider': null}) %}
{% endif %}
{% if is_granted('view_other_timesheet') and user.enabled %}
{% if is_granted('view_reporting') %}
{% if view_other or app.user.id == user.id %}
{% set actions = actions|merge({'report': path('report_user_month', {'user': user.id})}) %}
{% endif %}
{% endif %}
{% if view_other and user.enabled %}
{% set actions = actions|merge({'timesheet': path('admin_timesheet', {'users': [user.id]})}) %}
{% endif %}
{% if view == 'index' and is_granted('delete', user) %}

View File

@@ -49,6 +49,16 @@
}
}
}
{%- if is_granted('view_reporting') and (app.user.id == user.id or is_granted('view_other_timesheet')) -%}
,
onClick: function(event, elements) {
var element = elements[0];
var month = this.data.datasets[0].monthData[element._index];
var formattedMonth = moment(month).format('{{ stat_date_format }}');
var reportUrl = '{{ path('report_user_month', {'user': user.id, 'date': 'XXXXX'})|raw }}'.replace('XXXXX', formattedMonth);
document.location = reportUrl;
}
{% endif %}
};
}
</script>
@@ -58,7 +68,6 @@
{% embed '@AdminLTE/Widgets/box-widget.html.twig' %}
{% block box_title %}{{ year }}{% endblock %}
{% block box_body %}
<div class="chart">
<canvas id="userProfileChart{{ year }}" style="height: {{ kimai_context.chart.height }}px;"></canvas>
</div>
@@ -77,20 +86,24 @@
{% if not loop.last %},{% endif %}
{% endfor %}
],
realData : [
realData: [
{% for month in yearStat.months %}
'{{ month.totalDuration|duration }}'
{% if not loop.last %},{% endif %}
{% endfor %}
]
],
monthData: [
{% for month in yearStat.months %}
'{{ yearStat.year }}-{{ month.month|length == 1 ? '0'~ month.month : month.month }}-01'
{% if not loop.last %},{% endif %}
{% endfor %}
],
}
]
};
var userProfileChartCanvas{{ year }} = $("#userProfileChart{{ year }}").get(0).getContext("2d");
var userProfileChart{{ year }} = new Chart(
userProfileChartCanvas{{ year }}, {
$("#userProfileChart{{ year }}").get(0).getContext("2d"), {
type: 'bar',
data: userProfileChartData{{ year }},
options: userProfileChartOptions()