mirror of
https://github.com/kevinpapst/kimai2.git
synced 2025-04-15 01:38:44 +00:00
Timesheet toolbar (#12)
* add toolbar to timesheet #11 * add customer filter #11 * add project filter #11 * add activity filter #11 * add page size selector #11 * added state filter #11 * fixed csrf token names * fixed typo
This commit is contained in:
parent
c9c82e767f
commit
80d0dc2369
20 changed files with 742 additions and 46 deletions
app/Resources
translations
views
src/TimesheetBundle
Controller
DataFixtures/ORM
Form
Model/Query
Repository
Resources
web
|
@ -22,6 +22,10 @@
|
|||
<source>subtitle.amount</source>
|
||||
<target> (insgesamt %count%)</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="This is a mandatory field">
|
||||
<source>This is a mandatory field</source>
|
||||
<target>Pflichtfeld</target>
|
||||
</trans-unit>
|
||||
|
||||
<!--
|
||||
Login / Security
|
||||
|
@ -478,6 +482,10 @@
|
|||
<!--
|
||||
ROLES
|
||||
-->
|
||||
<trans-unit id="ROLE_SUPER_ADMIN">
|
||||
<source>ROLE_SUPER_ADMIN</source>
|
||||
<target>System-Admin</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="ROLE_ADMIN">
|
||||
<source>ROLE_ADMIN</source>
|
||||
<target>Administrator</target>
|
||||
|
|
|
@ -32,17 +32,6 @@
|
|||
{% block avanzu_head %}
|
||||
<link rel="stylesheet" href="{{ asset('css/kimai.css') }}">
|
||||
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
|
||||
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
|
||||
<script src="{{ asset('theme/bootstrap/js/bootstrap.min.js') }}"></script>
|
||||
<script src="{{ asset('js/kimai.js') }}"></script>
|
||||
<script src="{{ asset('js/Chart.min.js') }}"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('.dropdown-toggle').dropdown();
|
||||
$(document).kimai({imagePath: '{{ asset('images') }}'});
|
||||
//$(document).kimai('pauseRecord', 'li.messages-menu ul.menu li');
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block avanzu_footer %}
|
||||
|
@ -91,5 +80,21 @@
|
|||
|
||||
{% block avanzu_breadcrumb %}{{ parent() }}{% endblock %}
|
||||
{% block avanzu_control_sidebar %}{{ parent() }}{% endblock %}
|
||||
{% block avanzu_javascripts %}{{ parent() }}{% endblock %}
|
||||
{% block avanzu_javascripts_inline %}{{ parent() }}{% endblock %}
|
||||
|
||||
{% block avanzu_javascripts %}
|
||||
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
|
||||
<script src="{{ asset('theme/bootstrap/js/bootstrap.min.js') }}"></script>
|
||||
<script src="{{ asset('js/kimai.js') }}"></script>
|
||||
<script src="{{ asset('js/Chart.min.js') }}"></script>
|
||||
{% block javascript_imports %}{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
{% block avanzu_javascripts_inline %}
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$('.dropdown-toggle').dropdown();
|
||||
$(document).kimai({imagePath: '{{ asset('images') }}'});
|
||||
//$(document).kimai('pauseRecord', 'li.messages-menu ul.menu li');
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
9
app/Resources/views/default/_toolbar_form.html.twig
Normal file
9
app/Resources/views/default/_toolbar_form.html.twig
Normal file
|
@ -0,0 +1,9 @@
|
|||
{% import "macros/toolbar.html.twig" as toolbar %}
|
||||
|
||||
{{ toolbar.start() }}
|
||||
{{ form_start(form) }}
|
||||
<div class="box-body">
|
||||
{{ form_widget(form) }}
|
||||
</div>
|
||||
{{ form_end(form) }}
|
||||
{{ toolbar.end() }}
|
11
app/Resources/views/macros/toolbar.html.twig
Normal file
11
app/Resources/views/macros/toolbar.html.twig
Normal file
|
@ -0,0 +1,11 @@
|
|||
{% macro start(columns) %}
|
||||
<div class="box toolbar">
|
||||
<div class="box-header">
|
||||
<div class="row">
|
||||
{% endmacro %}
|
||||
|
||||
{% macro end() %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
|
@ -21,7 +21,6 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
|||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use TimesheetBundle\Form\TimesheetEditForm;
|
||||
use TimesheetBundle\Repository\TimesheetRepository;
|
||||
|
||||
/**
|
||||
* Controller used to manage timesheet contents in the public part of the site.
|
||||
|
@ -33,13 +32,7 @@ use TimesheetBundle\Repository\TimesheetRepository;
|
|||
*/
|
||||
class TimesheetController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @return TimesheetRepository
|
||||
*/
|
||||
protected function getRepository()
|
||||
{
|
||||
return $this->getDoctrine()->getRepository(Timesheet::class);
|
||||
}
|
||||
use TimesheetControllerTrait;
|
||||
|
||||
/**
|
||||
* @Route("/", defaults={"page": 1}, name="timesheet")
|
||||
|
@ -47,15 +40,20 @@ class TimesheetController extends AbstractController
|
|||
* @Method("GET")
|
||||
* @Cache(smaxage="10")
|
||||
*/
|
||||
public function indexAction($page)
|
||||
public function indexAction($page, Request $request)
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$query = $this->getQueryForRequest($request);
|
||||
$query->setUser($this->getUser());
|
||||
$query->setPage($page);
|
||||
|
||||
/* @var $entries Pagerfanta */
|
||||
$entries = $this->getRepository()->findLatest($user, $page);
|
||||
$entries = $this->getRepository()->findByQuery($query);
|
||||
|
||||
return $this->render('TimesheetBundle:timesheet:index.html.twig', [
|
||||
'entries' => $entries,
|
||||
'page' => $page
|
||||
'page' => $page,
|
||||
'query' => $query,
|
||||
'toolbarForm' => $this->getToolbarForm($query)->createView(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
108
src/TimesheetBundle/Controller/TimesheetControllerTrait.php
Normal file
108
src/TimesheetBundle/Controller/TimesheetControllerTrait.php
Normal file
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai package.
|
||||
*
|
||||
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace TimesheetBundle\Controller;
|
||||
|
||||
use TimesheetBundle\Entity\Activity;
|
||||
use TimesheetBundle\Entity\Customer;
|
||||
use TimesheetBundle\Entity\Project;
|
||||
use TimesheetBundle\Entity\Timesheet;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use TimesheetBundle\Form\TimesheetToolbarForm;
|
||||
use TimesheetBundle\Repository\TimesheetRepository;
|
||||
use TimesheetBundle\Model\Query\Timesheet as TimesheetQuery;
|
||||
|
||||
/**
|
||||
* Helper functions for Timesheet controller
|
||||
*
|
||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||
*/
|
||||
trait TimesheetControllerTrait
|
||||
{
|
||||
/**
|
||||
* @return TimesheetRepository
|
||||
*/
|
||||
protected function getRepository()
|
||||
{
|
||||
return $this->getDoctrine()->getRepository(Timesheet::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return TimesheetQuery
|
||||
*/
|
||||
protected function getQueryForRequest(Request $request)
|
||||
{
|
||||
$activity = $request->get('activity');
|
||||
$activity = !empty(trim($activity)) ? trim($activity) : null;
|
||||
$project = $request->get('project');
|
||||
$project = !empty(trim($project)) ? trim($project) : null;
|
||||
$customer = $request->get('customer');
|
||||
$customer = !empty(trim($customer)) ? trim($customer) : null;
|
||||
$state = $request->get('state');
|
||||
$state = !empty(trim($state)) ? trim($state) : null;
|
||||
$pageSize = (int) $request->get('pageSize');
|
||||
|
||||
if ($activity !== null) {
|
||||
$repo = $this->getDoctrine()->getRepository(Activity::class);
|
||||
$activity = $repo->getById($activity);
|
||||
if ($activity !== null) {
|
||||
$project = $activity->getProject();
|
||||
if ($project !== null) {
|
||||
$customer = $project->getCustomer();
|
||||
}
|
||||
} else {
|
||||
$customer = null;
|
||||
$project = null;
|
||||
}
|
||||
} elseif ($project !== null) {
|
||||
$repo = $this->getDoctrine()->getRepository(Project::class);
|
||||
$project = $repo->getById($project);
|
||||
if ($project !== null) {
|
||||
$customer = $project->getCustomer();
|
||||
} else {
|
||||
$customer = null;
|
||||
}
|
||||
} else if ($customer !== null) {
|
||||
$repo = $this->getDoctrine()->getRepository(Customer::class);
|
||||
$customer = $repo->getById($customer);
|
||||
}
|
||||
|
||||
$query = new TimesheetQuery();
|
||||
$query
|
||||
->setActivity($activity)
|
||||
->setProject($project)
|
||||
->setCustomer($customer)
|
||||
->setPageSize($pageSize)
|
||||
->setState($state);
|
||||
|
||||
return $query ;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TimesheetQuery $query
|
||||
* @param string $route
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getToolbarForm(TimesheetQuery $query, $route = 'timesheet')
|
||||
{
|
||||
return $this->createForm(
|
||||
TimesheetToolbarForm::class,
|
||||
$query,
|
||||
[
|
||||
'action' => $this->generateUrl($route, [
|
||||
'page' => $query->getPage(),
|
||||
]),
|
||||
'method' => 'GET',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
|
@ -283,7 +283,7 @@ class LoadFixtures extends AppBundleLoadFixtures
|
|||
'Customer Relations',
|
||||
'Infrastructure',
|
||||
'Software Upgrade',
|
||||
'Office Managemenr',
|
||||
'Office Management',
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -133,7 +133,7 @@ class CustomerEditForm extends AbstractType
|
|||
'data_class' => Customer::class,
|
||||
'csrf_protection' => true,
|
||||
'csrf_field_name' => '_token',
|
||||
'csrf_token_id' => 'admin_activity_edit',
|
||||
'csrf_token_id' => 'admin_customer_edit',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ class ProjectEditForm extends AbstractType
|
|||
'data_class' => Project::class,
|
||||
'csrf_protection' => true,
|
||||
'csrf_field_name' => '_token',
|
||||
'csrf_token_id' => 'admin_activity_edit',
|
||||
'csrf_token_id' => 'admin_project_edit',
|
||||
'currency' => Customer::DEFAULT_CURRENCY,
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ use Symfony\Component\Form\FormBuilderInterface;
|
|||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use TimesheetBundle\Entity\Customer;
|
||||
use TimesheetBundle\Entity\Timesheet;
|
||||
use TimesheetBundle\Form\Type\ActivityGroupedWithCustomerNameType;
|
||||
|
||||
/**
|
||||
* Defines the form used to manipulate Timesheet entries.
|
||||
|
@ -47,18 +48,15 @@ class TimesheetEditForm extends AbstractType
|
|||
])
|
||||
// integer
|
||||
/*
|
||||
->add('duration', RangeType::class, [
|
||||
'label' => 'label.duration',
|
||||
])
|
||||
// User
|
||||
->add('user', UserType::class, [
|
||||
'label' => 'label.user',
|
||||
])
|
||||
*/
|
||||
// Activity
|
||||
->add('activity', ActivityType::class, [
|
||||
->add('activity', ActivityGroupedWithCustomerNameType::class, [
|
||||
'label' => 'label.activity',
|
||||
])
|
||||
*/
|
||||
// customer
|
||||
->add('description', TextareaType::class, [
|
||||
'label' => 'label.description',
|
||||
|
@ -81,7 +79,7 @@ class TimesheetEditForm extends AbstractType
|
|||
'data_class' => Timesheet::class,
|
||||
'csrf_protection' => true,
|
||||
'csrf_field_name' => '_token',
|
||||
'csrf_token_id' => 'admin_timsheet_edit',
|
||||
'csrf_token_id' => 'timesheet_edit',
|
||||
'currency' => Customer::DEFAULT_CURRENCY,
|
||||
]);
|
||||
}
|
||||
|
|
131
src/TimesheetBundle/Form/TimesheetToolbarForm.php
Normal file
131
src/TimesheetBundle/Form/TimesheetToolbarForm.php
Normal file
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai package.
|
||||
*
|
||||
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace TimesheetBundle\Form;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use TimesheetBundle\Form\Type\ActivityType;
|
||||
use TimesheetBundle\Form\Type\CustomerType;
|
||||
use TimesheetBundle\Form\Type\ProjectType;
|
||||
use TimesheetBundle\Model\Query\Timesheet as TimesheetQuery;
|
||||
|
||||
/**
|
||||
* Defines the form used for filtering the timesheet.
|
||||
*
|
||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||
*/
|
||||
class TimesheetToolbarForm extends AbstractType
|
||||
{
|
||||
/**
|
||||
* Dirty hack to enable easy handling of GET form in controller and javascript.
|
||||
*Cleans up the name of all form elents (and unfortunately of the form itself).
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
/** @var TimesheetQuery $query */
|
||||
$query = $options['data'];
|
||||
|
||||
$builder
|
||||
->add('pageSize', ChoiceType::class, [
|
||||
'label' => 'label.pageSize',
|
||||
'choices' => [10 => 10, 25 => 25, 50 => 50, 75 => 75, 100 => 100],
|
||||
'required' => false,
|
||||
])
|
||||
->add('state', ChoiceType::class, [
|
||||
'label' => 'label.entryState',
|
||||
'choices' => [
|
||||
'entryState.all' => TimesheetQuery::STATE_ALL,
|
||||
'entryState.running' => TimesheetQuery::STATE_RUNNING,
|
||||
'entryState.stopped' => TimesheetQuery::STATE_STOPPED
|
||||
],
|
||||
])
|
||||
->add('customer', CustomerType::class, [
|
||||
'label' => 'label.customer',
|
||||
'required' => false,
|
||||
])
|
||||
;
|
||||
|
||||
$this->addProjectChoice($builder, $query);
|
||||
$this->addActivityChoice($builder, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FormBuilderInterface $builder
|
||||
* @param TimesheetQuery $query
|
||||
*/
|
||||
protected function addProjectChoice(FormBuilderInterface $builder, TimesheetQuery $query)
|
||||
{
|
||||
if ($query->getCustomer() === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$choices = [];
|
||||
foreach ($query->getCustomer()->getProjects() as $project) {
|
||||
$choices[] = $project;
|
||||
//$choices[$project->getName()] = $project->getId();
|
||||
}
|
||||
|
||||
$builder
|
||||
->add('project', ProjectType::class, [
|
||||
'label' => 'label.project',
|
||||
'required' => false,
|
||||
'choices' => $choices,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FormBuilderInterface $builder
|
||||
* @param TimesheetQuery $query
|
||||
*/
|
||||
protected function addActivityChoice(FormBuilderInterface $builder, TimesheetQuery $query)
|
||||
{
|
||||
if ($query->getProject() === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$choices = [];
|
||||
foreach ($query->getProject()->getActivities() as $activity) {
|
||||
$choices[] = $activity;
|
||||
//$choices[$activity->getName()] = $activity->getId();
|
||||
}
|
||||
|
||||
$builder
|
||||
->add('activity', ActivityType::class, [
|
||||
'label' => 'label.activity',
|
||||
'required' => false,
|
||||
'choices' => $choices,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => TimesheetQuery::class,
|
||||
'csrf_protection' => false,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai package.
|
||||
*
|
||||
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace TimesheetBundle\Form\Type;
|
||||
|
||||
use TimesheetBundle\Entity\Activity;
|
||||
|
||||
/**
|
||||
* Custom form field type to select an activity which are grouped by their Projects, preceeded by their customer names.
|
||||
*
|
||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||
*/
|
||||
class ActivityGroupedWithCustomerNameType extends ActivityType
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Activity $activity
|
||||
* @param $key
|
||||
* @param $index
|
||||
* @return string
|
||||
*/
|
||||
public function groupBy(Activity $activity, $key, $index)
|
||||
{
|
||||
return $activity->getProject()->getCustomer()->getName() . ': ' . $activity->getProject()->getName();
|
||||
}
|
||||
}
|
58
src/TimesheetBundle/Form/Type/ActivityType.php
Normal file
58
src/TimesheetBundle/Form/Type/ActivityType.php
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai package.
|
||||
*
|
||||
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace TimesheetBundle\Form\Type;
|
||||
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use TimesheetBundle\Entity\Activity;
|
||||
|
||||
/**
|
||||
* Custom form field type to select an activity.
|
||||
*
|
||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||
*/
|
||||
class ActivityType extends AbstractType
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Activity $activity
|
||||
* @param $key
|
||||
* @param $index
|
||||
* @return string
|
||||
*/
|
||||
public function groupBy(Activity $activity, $key, $index)
|
||||
{
|
||||
return $activity->getProject()->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'class' => 'TimesheetBundle:Activity',
|
||||
'choice_label' => 'name',
|
||||
'choice_value' => 'id',
|
||||
'group_by' => array($this, 'groupBy'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return EntityType::class;
|
||||
}
|
||||
}
|
|
@ -11,9 +11,10 @@
|
|||
|
||||
namespace TimesheetBundle\Form\Type;
|
||||
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use TimesheetBundle\Entity\Project;
|
||||
|
||||
/**
|
||||
* Custom form field type to select a project.
|
||||
|
@ -30,14 +31,10 @@ class ProjectType extends AbstractType
|
|||
{
|
||||
$resolver->setDefaults([
|
||||
'class' => 'TimesheetBundle:Project',
|
||||
'choice_label' => function ($project) {
|
||||
/* @var $project Project */
|
||||
return
|
||||
//'[' . $project->getId() . '] ' .
|
||||
$project->getName() .
|
||||
' (' .
|
||||
$project->getCustomer()->getName() .
|
||||
')';
|
||||
'choice_label' => 'name',
|
||||
'choice_value' => 'id',
|
||||
'group_by' => function(Project $project, $key, $index) {
|
||||
return $project->getCustomer()->getName();
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
@ -47,6 +44,6 @@ class ProjectType extends AbstractType
|
|||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return EntityType::class;
|
||||
return ChoiceType::class;
|
||||
}
|
||||
}
|
||||
|
|
201
src/TimesheetBundle/Model/Query/Timesheet.php
Normal file
201
src/TimesheetBundle/Model/Query/Timesheet.php
Normal file
|
@ -0,0 +1,201 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai package.
|
||||
*
|
||||
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace TimesheetBundle\Model\Query;
|
||||
|
||||
use AppBundle\Entity\User;
|
||||
use TimesheetBundle\Entity\Activity;
|
||||
use TimesheetBundle\Entity\Customer;
|
||||
use TimesheetBundle\Entity\Project;
|
||||
|
||||
/**
|
||||
* Can be used for advanced timesheet repository queries.
|
||||
*
|
||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||
*/
|
||||
class Timesheet
|
||||
{
|
||||
|
||||
const DEFAULT_PAGESIZE = 25;
|
||||
const DEFAULT_PAGE = 1;
|
||||
|
||||
const STATE_ALL = 0;
|
||||
const STATE_RUNNING = 1;
|
||||
const STATE_STOPPED = 2;
|
||||
|
||||
/**
|
||||
* @var User
|
||||
*/
|
||||
protected $user;
|
||||
/**
|
||||
* @var Activity
|
||||
*/
|
||||
protected $activity;
|
||||
/**
|
||||
* @var Project
|
||||
*/
|
||||
protected $project;
|
||||
/**
|
||||
* @var Customer
|
||||
*/
|
||||
protected $customer;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $page = self::DEFAULT_PAGE;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $pageSize = self::DEFAULT_PAGESIZE;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $state = self::STATE_ALL;
|
||||
|
||||
/**
|
||||
* @return User
|
||||
*/
|
||||
public function getUser()
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @return Timesheet
|
||||
*/
|
||||
public function setUser(User $user = null)
|
||||
{
|
||||
$this->user = $user;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activity overwrites: setProject() and setCustomer()
|
||||
*
|
||||
* @return Activity
|
||||
*/
|
||||
public function getActivity()
|
||||
{
|
||||
return $this->activity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Activity $activity
|
||||
* @return Timesheet
|
||||
*/
|
||||
public function setActivity(Activity $activity = null)
|
||||
{
|
||||
$this->activity = $activity;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Project
|
||||
*/
|
||||
public function getProject()
|
||||
{
|
||||
return $this->project;
|
||||
}
|
||||
|
||||
/**
|
||||
* Project overwrites: setCustomer()
|
||||
* Is overwritten by: setActivity()
|
||||
*
|
||||
* @param Project $project
|
||||
* @return Timesheet
|
||||
*/
|
||||
public function setProject(Project $project = null)
|
||||
{
|
||||
$this->project = $project;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Customer
|
||||
*/
|
||||
public function getCustomer()
|
||||
{
|
||||
return $this->customer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Project overwrites: none
|
||||
* Is overwritten by: setActivity() and setProject()
|
||||
*
|
||||
* @param Customer $customer
|
||||
* @return Timesheet
|
||||
*/
|
||||
public function setCustomer(Customer $customer = null)
|
||||
{
|
||||
$this->customer = $customer;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getPage()
|
||||
{
|
||||
return $this->page;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $page
|
||||
* @return Timesheet
|
||||
*/
|
||||
public function setPage($page)
|
||||
{
|
||||
$this->page = $page;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getPageSize()
|
||||
{
|
||||
return $this->pageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $pageSize
|
||||
* @return Timesheet
|
||||
*/
|
||||
public function setPageSize($pageSize)
|
||||
{
|
||||
if (!empty($pageSize) && (int) $pageSize > 0) {
|
||||
$this->pageSize = (int) $pageSize;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getState()
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $state
|
||||
* @return Timesheet
|
||||
*/
|
||||
public function setState($state)
|
||||
{
|
||||
if (in_array($state, [self::STATE_ALL, self::STATE_RUNNING, self::STATE_STOPPED])) {
|
||||
$this->state = $state;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
|
@ -22,6 +22,7 @@ use Pagerfanta\Pagerfanta;
|
|||
use TimesheetBundle\Model\Statistic\Month;
|
||||
use TimesheetBundle\Model\Statistic\Year;
|
||||
use TimesheetBundle\Model\TimesheetGlobalStatistic;
|
||||
use TimesheetBundle\Model\Query\Timesheet as TimesheetQuery;
|
||||
use TimesheetBundle\Model\TimesheetStatistic;
|
||||
use DateTime;
|
||||
|
||||
|
@ -291,6 +292,42 @@ class TimesheetRepository extends EntityRepository
|
|||
return $qb->getQuery();
|
||||
}
|
||||
|
||||
public function findByQuery(TimesheetQuery $query)
|
||||
{
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
|
||||
$qb->select('t', 'a')
|
||||
->from('TimesheetBundle:Timesheet', 't')
|
||||
->join('t.activity', 'a')
|
||||
->orderBy('t.begin', 'DESC');
|
||||
|
||||
if ($query->getUser() !== null) {
|
||||
$qb->andWhere('t.user = :user')
|
||||
->setParameter('user', $query->getUser());
|
||||
}
|
||||
|
||||
if ($query->getState() == TimesheetQuery::STATE_RUNNING) {
|
||||
$qb->andWhere($qb->expr()->isNull('t.end'));
|
||||
} elseif ($query->getState() == TimesheetQuery::STATE_STOPPED) {
|
||||
$qb->andWhere($qb->expr()->isNotNull('t.end'));
|
||||
}
|
||||
|
||||
if ($query->getActivity() !== null) {
|
||||
$qb->andWhere('t.activity = :activity')
|
||||
->setParameter('activity', $query->getActivity());
|
||||
} elseif ($query->getProject() !== null) {
|
||||
$qb->andWhere('a.project = :project')
|
||||
->setParameter('project', $query->getProject());
|
||||
} elseif ($query->getCustomer() !== null) {
|
||||
$qb->join('a.project', 'p')
|
||||
->join('p.customer', 'c')
|
||||
->andWhere('p.customer = :customer')
|
||||
->setParameter('customer', $query->getCustomer());
|
||||
}
|
||||
|
||||
return $this->getPager($qb->getQuery(), $query->getPage(), $query->getPageSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @param int $page
|
||||
|
@ -314,12 +351,13 @@ class TimesheetRepository extends EntityRepository
|
|||
/**
|
||||
* @param Query $query
|
||||
* @param int $page
|
||||
* @param int $maxPerPage
|
||||
* @return Pagerfanta
|
||||
*/
|
||||
protected function getPager(Query $query, $page = 1)
|
||||
protected function getPager(Query $query, $page = 1, $maxPerPage = 25)
|
||||
{
|
||||
$paginator = new Pagerfanta(new DoctrineORMAdapter($query, false));
|
||||
$paginator->setMaxPerPage(25);
|
||||
$paginator->setMaxPerPage($maxPerPage);
|
||||
$paginator->setCurrentPage($page);
|
||||
|
||||
return $paginator;
|
||||
|
|
60
src/TimesheetBundle/Resources/translations/messages.de.xliff
Normal file
60
src/TimesheetBundle/Resources/translations/messages.de.xliff
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<file date="2016-10-19T21:46:45Z" source-language="en" target-language="de" datatype="plaintext" original="not.available">
|
||||
<body>
|
||||
|
||||
<!--
|
||||
TIMESHEET TOOLBAR
|
||||
-->
|
||||
<trans-unit id="label.pageSize">
|
||||
<source>label.pageSize</source>
|
||||
<target>Anzahl Einträge</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="timesheet.toolbar.submit">
|
||||
<source>timesheet.toolbar.submit</source>
|
||||
<target>Einträge filtern</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="label.entryState">
|
||||
<source>label.entryState</source>
|
||||
<target>Zeiten</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="entryState.all">
|
||||
<source>entryState.all</source>
|
||||
<target>Alle</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="entryState.running">
|
||||
<source>entryState.running</source>
|
||||
<target>Laufende</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="entryState.stopped">
|
||||
<source>entryState.stopped</source>
|
||||
<target>Beendete</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="0">
|
||||
<source>0</source>
|
||||
<target>0</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="10">
|
||||
<source>10</source>
|
||||
<target>10</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="25">
|
||||
<source>25</source>
|
||||
<target>25</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="50">
|
||||
<source>50</source>
|
||||
<target>50</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="75">
|
||||
<source>75</source>
|
||||
<target>75</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="100">
|
||||
<source>100</source>
|
||||
<target>100</target>
|
||||
</trans-unit>
|
||||
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
|
@ -4,8 +4,13 @@
|
|||
|
||||
{% block page_title %}{{ 'timesheet.title'|trans }}{% endblock %}
|
||||
{% block page_subtitle %}{{ 'timesheet.subtitle'|trans }}{% endblock %}
|
||||
{% block javascript_imports %}<script src="{{ asset('js/timesheet.js') }}"></script>{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% if toolbarForm %}
|
||||
{{ include('default/_toolbar_form.html.twig', {'form': toolbarForm}) }}
|
||||
{% endif %}
|
||||
|
||||
{% if entries.count > 0 %}
|
||||
{{ datatables.data_table_header({
|
||||
'label.date': '',
|
||||
|
|
|
@ -31,6 +31,17 @@ li.open .ticktac i.running{
|
|||
}
|
||||
*/
|
||||
|
||||
/* ================================ TOOLBAR ================================ */
|
||||
|
||||
.toolbar form input,
|
||||
.toolbar form select {
|
||||
display: inline-block;
|
||||
width: inherit;
|
||||
}
|
||||
|
||||
.toolbar form .form-group {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/* ================================ SIDEBAR ================================ */
|
||||
|
||||
|
|
24
web/js/timesheet.js
Normal file
24
web/js/timesheet.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
$(document).ready(function () {
|
||||
|
||||
$('.toolbar form select').change(function (event) {
|
||||
switch (event.target.id) {
|
||||
case 'customer':
|
||||
if ($(this).val() === '') {
|
||||
$('.toolbar form select#project').parent().remove();
|
||||
} else {
|
||||
$('.toolbar form select#project').val('');
|
||||
}
|
||||
$('.toolbar form select#activity').parent().remove();
|
||||
break;
|
||||
case 'project':
|
||||
if ($(this).val() === '') {
|
||||
$('.toolbar form select#activity').parent().remove();
|
||||
} else {
|
||||
$('.toolbar form select#activity').val('');
|
||||
}
|
||||
break;
|
||||
}
|
||||
$('.toolbar form').submit();
|
||||
});
|
||||
|
||||
});
|
Loading…
Add table
Reference in a new issue