users weekly stats as bar-chart in dashboard (#847)
This commit is contained in:
42
src/Widget/Renderer/AbstractTwigRenderer.php
Normal file
42
src/Widget/Renderer/AbstractTwigRenderer.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Widget\Renderer;
|
||||
|
||||
use App\Widget\WidgetRendererInterface;
|
||||
use Twig\Environment;
|
||||
|
||||
abstract class AbstractTwigRenderer implements WidgetRendererInterface
|
||||
{
|
||||
/**
|
||||
* @var Environment
|
||||
*/
|
||||
protected $twig;
|
||||
|
||||
/**
|
||||
* @param Environment $twig
|
||||
*/
|
||||
public function __construct(Environment $twig)
|
||||
{
|
||||
$this->twig = $twig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $template
|
||||
* @param array $data
|
||||
* @return string
|
||||
* @throws \Twig\Error\LoaderError
|
||||
* @throws \Twig\Error\RuntimeError
|
||||
* @throws \Twig\Error\SyntaxError
|
||||
*/
|
||||
protected function renderTemplate(string $template, array $data): string
|
||||
{
|
||||
return $this->twig->render($template, $data);
|
||||
}
|
||||
}
|
||||
29
src/Widget/Renderer/CompoundChartRenderer.php
Normal file
29
src/Widget/Renderer/CompoundChartRenderer.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Widget\Renderer;
|
||||
|
||||
use App\Widget\Type\CompoundChart;
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
class CompoundChartRenderer extends AbstractTwigRenderer
|
||||
{
|
||||
public function supports(WidgetInterface $widget): bool
|
||||
{
|
||||
return ($widget instanceof CompoundChart);
|
||||
}
|
||||
|
||||
public function render(WidgetInterface $widget, array $options = []): string
|
||||
{
|
||||
return $this->renderTemplate('widget/section-chart.html.twig', [
|
||||
'title' => $widget->getTitle(),
|
||||
'widgets' => $widget->getData(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
29
src/Widget/Renderer/CompoundRowRenderer.php
Normal file
29
src/Widget/Renderer/CompoundRowRenderer.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Widget\Renderer;
|
||||
|
||||
use App\Widget\Type\CompoundRow;
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
class CompoundRowRenderer extends AbstractTwigRenderer
|
||||
{
|
||||
public function supports(WidgetInterface $widget): bool
|
||||
{
|
||||
return ($widget instanceof CompoundRow);
|
||||
}
|
||||
|
||||
public function render(WidgetInterface $widget, array $options = []): string
|
||||
{
|
||||
return $this->renderTemplate('widget/section-simple.html.twig', [
|
||||
'title' => $widget->getTitle(),
|
||||
'widgets' => $widget->getData(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
33
src/Widget/Renderer/SimpleWidgetRenderer.php
Normal file
33
src/Widget/Renderer/SimpleWidgetRenderer.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Widget\Renderer;
|
||||
|
||||
use App\Widget\Type\SimpleWidget;
|
||||
use App\Widget\WidgetInterface;
|
||||
use ReflectionClass;
|
||||
|
||||
class SimpleWidgetRenderer extends AbstractTwigRenderer
|
||||
{
|
||||
public function supports(WidgetInterface $widget): bool
|
||||
{
|
||||
return $widget instanceof SimpleWidget;
|
||||
}
|
||||
|
||||
public function render(WidgetInterface $widget, array $options = []): string
|
||||
{
|
||||
$name = (new ReflectionClass($widget))->getShortName();
|
||||
|
||||
return $this->renderTemplate(sprintf('widget/widget-%s.html.twig', strtolower($name)), [
|
||||
'data' => $widget->getData($options),
|
||||
'options' => $widget->getOptions($options),
|
||||
'title' => $widget->getTitle(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
95
src/Widget/Type/AbstractContainer.php
Normal file
95
src/Widget/Type/AbstractContainer.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Widget\Type;
|
||||
|
||||
use App\Widget\WidgetContainerInterface;
|
||||
use App\Widget\WidgetInterface;
|
||||
use BadMethodCallException;
|
||||
|
||||
abstract class AbstractContainer implements WidgetContainerInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $title = '';
|
||||
/**
|
||||
* @var WidgetInterface[]
|
||||
*/
|
||||
protected $widgets = [];
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $order = 0;
|
||||
|
||||
public function setTitle(string $title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @return WidgetInterface[]|array|mixed|null
|
||||
*/
|
||||
public function getData(array $options = [])
|
||||
{
|
||||
return $this->getWidgets();
|
||||
}
|
||||
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function setOption(string $name, $value): void
|
||||
{
|
||||
throw new BadMethodCallException('setOption() is not supported on AbstractContainer');
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function getOrder(): int
|
||||
{
|
||||
return $this->order;
|
||||
}
|
||||
|
||||
public function setOrder(int $order)
|
||||
{
|
||||
$this->order = $order;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WidgetInterface[]
|
||||
*/
|
||||
public function getWidgets(): array
|
||||
{
|
||||
return $this->widgets;
|
||||
}
|
||||
|
||||
public function addWidget(WidgetInterface $widget)
|
||||
{
|
||||
$this->widgets[] = $widget;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
109
src/Widget/Type/AbstractWidgetType.php
Normal file
109
src/Widget/Type/AbstractWidgetType.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Widget\Type;
|
||||
|
||||
use App\Widget\WidgetInterface;
|
||||
|
||||
abstract class AbstractWidgetType implements WidgetInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $id = '';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $title = '';
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [];
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
public function setId(string $id): AbstractWidgetType
|
||||
{
|
||||
$this->id = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setData($data): AbstractWidgetType
|
||||
{
|
||||
$this->data = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getData(array $options = [])
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function setTitle(string $title): AbstractWidgetType
|
||||
{
|
||||
$this->title = $title;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setOptions(array $options): AbstractWidgetType
|
||||
{
|
||||
foreach ($options as $key => $value) {
|
||||
$this->options[$key] = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function setOption(string $name, $value): void
|
||||
{
|
||||
$this->options[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param mixed|null $default
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getOption(string $name, $default = null)
|
||||
{
|
||||
if (array_key_exists($name, $this->options)) {
|
||||
return $this->options[$name];
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
return array_merge($this->options, $options);
|
||||
}
|
||||
}
|
||||
14
src/Widget/Type/CompoundChart.php
Normal file
14
src/Widget/Type/CompoundChart.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Widget\Type;
|
||||
|
||||
class CompoundChart extends AbstractContainer
|
||||
{
|
||||
}
|
||||
14
src/Widget/Type/CompoundRow.php
Normal file
14
src/Widget/Type/CompoundRow.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Widget\Type;
|
||||
|
||||
class CompoundRow extends AbstractContainer
|
||||
{
|
||||
}
|
||||
18
src/Widget/Type/Counter.php
Normal file
18
src/Widget/Type/Counter.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Widget\Type;
|
||||
|
||||
class Counter extends SimpleWidget
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->setOption('dataType', 'int');
|
||||
}
|
||||
}
|
||||
65
src/Widget/Type/DailyWorkingTimeChart.php
Normal file
65
src/Widget/Type/DailyWorkingTimeChart.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Widget\Type;
|
||||
|
||||
use App\Repository\TimesheetRepository;
|
||||
use App\Security\CurrentUser;
|
||||
use DateTime;
|
||||
|
||||
class DailyWorkingTimeChart extends SimpleWidget
|
||||
{
|
||||
public const DEFAULT_CHART = 'bar';
|
||||
|
||||
/**
|
||||
* @var TimesheetRepository
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
public function __construct(TimesheetRepository $repository, CurrentUser $user)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->setId('DailyWorkingTimeChart');
|
||||
$this->setTitle('stats.yourWorkingHours');
|
||||
$this->setOptions([
|
||||
'begin' => 'monday this week 00:00:00',
|
||||
'end' => 'sunday this week 23:59:59',
|
||||
'color' => '',
|
||||
'user' => $user->getUser(),
|
||||
'type' => self::DEFAULT_CHART,
|
||||
'id' => '',
|
||||
]);
|
||||
}
|
||||
|
||||
public function getOptions(array $options = []): array
|
||||
{
|
||||
$options = parent::getOptions($options);
|
||||
|
||||
if (!in_array($options['type'], ['bar', 'line'])) {
|
||||
$options['type'] = self::DEFAULT_CHART;
|
||||
}
|
||||
|
||||
if (empty($options['id'])) {
|
||||
$options['id'] = uniqid('DailyWorkingTimeChart_');
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function getData(array $options = [])
|
||||
{
|
||||
$options = $this->getOptions($options);
|
||||
|
||||
$user = $options['user'];
|
||||
$begin = new DateTime($options['begin']);
|
||||
$end = new DateTime($options['end']);
|
||||
|
||||
return $this->repository->getDailyStats($user, $begin, $end);
|
||||
}
|
||||
}
|
||||
18
src/Widget/Type/More.php
Normal file
18
src/Widget/Type/More.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Widget\Type;
|
||||
|
||||
class More extends SimpleWidget
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->setOption('dataType', 'int');
|
||||
}
|
||||
}
|
||||
14
src/Widget/Type/SimpleWidget.php
Normal file
14
src/Widget/Type/SimpleWidget.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Widget\Type;
|
||||
|
||||
class SimpleWidget extends AbstractWidgetType
|
||||
{
|
||||
}
|
||||
14
src/Widget/Type/YearChart.php
Normal file
14
src/Widget/Type/YearChart.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Widget\Type;
|
||||
|
||||
class YearChart extends SimpleWidget
|
||||
{
|
||||
}
|
||||
24
src/Widget/WidgetContainerInterface.php
Normal file
24
src/Widget/WidgetContainerInterface.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Widget;
|
||||
|
||||
interface WidgetContainerInterface extends WidgetInterface
|
||||
{
|
||||
public function getOrder(): int;
|
||||
|
||||
public function setOrder(int $order);
|
||||
|
||||
/**
|
||||
* @return WidgetInterface[]
|
||||
*/
|
||||
public function getWidgets(): array;
|
||||
|
||||
public function addWidget(WidgetInterface $widget);
|
||||
}
|
||||
14
src/Widget/WidgetException.php
Normal file
14
src/Widget/WidgetException.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Widget;
|
||||
|
||||
class WidgetException extends \Exception
|
||||
{
|
||||
}
|
||||
65
src/Widget/WidgetInterface.php
Normal file
65
src/Widget/WidgetInterface.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Widget;
|
||||
|
||||
interface WidgetInterface
|
||||
{
|
||||
/**
|
||||
* Returns a unique ID for this widget.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getId(): string;
|
||||
|
||||
/**
|
||||
* Returns the widget title.
|
||||
*
|
||||
* If no title is necessary, return an empty string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle(): string;
|
||||
|
||||
/**
|
||||
* Returns the widgets data, to be used in the frontend rendering.
|
||||
*
|
||||
* If your widget relies on options to dynamically change the result data,
|
||||
* make sure that the given $options will overwrite the internal option for
|
||||
* this one call.
|
||||
*
|
||||
* @param array $options
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getData(array $options = []);
|
||||
|
||||
/**
|
||||
* Returns all widget options to be used in the frontend.
|
||||
*
|
||||
* The given $options are not meant to be persisted, but only to
|
||||
* overwrite the default values one time.
|
||||
*
|
||||
* You can validate the options or simply return:
|
||||
* return array_merge($this->options, $options);
|
||||
*
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
public function getOptions(array $options = []): array;
|
||||
|
||||
/**
|
||||
* Sets one widget option, both for internal use and for frontend rendering.
|
||||
*
|
||||
* The given option should be persisted and permanently overwrite the internal option.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function setOption(string $name, $value): void;
|
||||
}
|
||||
35
src/Widget/WidgetRendererInterface.php
Normal file
35
src/Widget/WidgetRendererInterface.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Widget;
|
||||
|
||||
interface WidgetRendererInterface
|
||||
{
|
||||
/**
|
||||
* Checks if the given widget can be rendered.
|
||||
*
|
||||
* Most renderer check something like:
|
||||
* return $widget instanceof MyWidgetType;
|
||||
*
|
||||
* @param WidgetInterface $widget
|
||||
* @return bool
|
||||
*/
|
||||
public function supports(WidgetInterface $widget): bool;
|
||||
|
||||
/**
|
||||
* Renders the given widget.
|
||||
*
|
||||
* The given $options array overwrites the widgets internal options for this call.
|
||||
*
|
||||
* @param WidgetInterface $widget
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
public function render(WidgetInterface $widget, array $options = []): string;
|
||||
}
|
||||
81
src/Widget/WidgetService.php
Normal file
81
src/Widget/WidgetService.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Widget;
|
||||
|
||||
use App\Repository\WidgetRepository;
|
||||
|
||||
class WidgetService
|
||||
{
|
||||
/**
|
||||
* @var WidgetRendererInterface[]
|
||||
*/
|
||||
protected $renderer = [];
|
||||
/**
|
||||
* @var WidgetRepository
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* @param WidgetRepository $repository
|
||||
* @param WidgetRendererInterface[] $renderer
|
||||
*/
|
||||
public function __construct(WidgetRepository $repository, iterable $renderer)
|
||||
{
|
||||
foreach ($renderer as $render) {
|
||||
$this->addRenderer($render);
|
||||
}
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $widget
|
||||
* @return bool
|
||||
*/
|
||||
public function hasWidget(string $widget): bool
|
||||
{
|
||||
return $this->repository->has($widget);
|
||||
}
|
||||
|
||||
public function getWidget(string $widget): WidgetInterface
|
||||
{
|
||||
return $this->repository->get($widget);
|
||||
}
|
||||
|
||||
public function addRenderer(WidgetRendererInterface $renderer): WidgetService
|
||||
{
|
||||
$this->renderer[] = $renderer;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WidgetInterface $widget
|
||||
* @return WidgetRendererInterface
|
||||
* @throws WidgetException
|
||||
*/
|
||||
public function findRenderer(WidgetInterface $widget): WidgetRendererInterface
|
||||
{
|
||||
foreach ($this->renderer as $renderer) {
|
||||
if ($renderer->supports($widget)) {
|
||||
return $renderer;
|
||||
}
|
||||
}
|
||||
|
||||
throw new WidgetException(sprintf('No renderer available for widget "%s"', get_class($widget)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WidgetRendererInterface[]
|
||||
*/
|
||||
public function getRenderer(): array
|
||||
{
|
||||
return $this->renderer;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user