From 80d0dc2369f95da3d084534e91570b1d1583161a Mon Sep 17 00:00:00 2001 From: Kevin Papst Date: Fri, 5 Jan 2018 09:30:22 +0100 Subject: [PATCH] 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 --- app/Resources/translations/messages.de.xliff | 8 + app/Resources/views/base.html.twig | 31 +-- .../views/default/_toolbar_form.html.twig | 9 + app/Resources/views/macros/toolbar.html.twig | 11 + .../Controller/TimesheetController.php | 22 +- .../Controller/TimesheetControllerTrait.php | 108 ++++++++++ .../DataFixtures/ORM/LoadFixtures.php | 2 +- src/TimesheetBundle/Form/CustomerEditForm.php | 2 +- src/TimesheetBundle/Form/ProjectEditForm.php | 2 +- .../Form/TimesheetEditForm.php | 10 +- .../Form/TimesheetToolbarForm.php | 131 ++++++++++++ .../ActivityGroupedWithCustomerNameType.php | 34 +++ .../Form/Type/ActivityType.php | 58 +++++ src/TimesheetBundle/Form/Type/ProjectType.php | 17 +- src/TimesheetBundle/Model/Query/Timesheet.php | 201 ++++++++++++++++++ .../Repository/TimesheetRepository.php | 42 +++- .../Resources/translations/messages.de.xliff | 60 ++++++ .../Resources/views/timesheet/index.html.twig | 5 + web/css/kimai.css | 11 + web/js/timesheet.js | 24 +++ 20 files changed, 742 insertions(+), 46 deletions(-) create mode 100644 app/Resources/views/default/_toolbar_form.html.twig create mode 100644 app/Resources/views/macros/toolbar.html.twig create mode 100644 src/TimesheetBundle/Controller/TimesheetControllerTrait.php create mode 100644 src/TimesheetBundle/Form/TimesheetToolbarForm.php create mode 100644 src/TimesheetBundle/Form/Type/ActivityGroupedWithCustomerNameType.php create mode 100644 src/TimesheetBundle/Form/Type/ActivityType.php create mode 100644 src/TimesheetBundle/Model/Query/Timesheet.php create mode 100644 src/TimesheetBundle/Resources/translations/messages.de.xliff create mode 100644 web/js/timesheet.js diff --git a/app/Resources/translations/messages.de.xliff b/app/Resources/translations/messages.de.xliff index c5e91af6..d512eda9 100644 --- a/app/Resources/translations/messages.de.xliff +++ b/app/Resources/translations/messages.de.xliff @@ -22,6 +22,10 @@ subtitle.amount (insgesamt %count%) + + This is a mandatory field + Pflichtfeld + + + ROLE_SUPER_ADMIN + System-Admin + ROLE_ADMIN Administrator diff --git a/app/Resources/views/base.html.twig b/app/Resources/views/base.html.twig index 49be2e55..dd3fa8a7 100644 --- a/app/Resources/views/base.html.twig +++ b/app/Resources/views/base.html.twig @@ -32,17 +32,6 @@ {% block avanzu_head %} - - - - - {% 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 %} + + + + + {% block javascript_imports %}{% endblock %} +{% endblock %} + +{% block avanzu_javascripts_inline %} + +{% endblock %} diff --git a/app/Resources/views/default/_toolbar_form.html.twig b/app/Resources/views/default/_toolbar_form.html.twig new file mode 100644 index 00000000..ac43ff02 --- /dev/null +++ b/app/Resources/views/default/_toolbar_form.html.twig @@ -0,0 +1,9 @@ +{% import "macros/toolbar.html.twig" as toolbar %} + +{{ toolbar.start() }} + {{ form_start(form) }} +
+ {{ form_widget(form) }} +
+ {{ form_end(form) }} +{{ toolbar.end() }} diff --git a/app/Resources/views/macros/toolbar.html.twig b/app/Resources/views/macros/toolbar.html.twig new file mode 100644 index 00000000..dc048bfd --- /dev/null +++ b/app/Resources/views/macros/toolbar.html.twig @@ -0,0 +1,11 @@ +{% macro start(columns) %} +
+
+
+{% endmacro %} + +{% macro end() %} +
+
+
+{% endmacro %} diff --git a/src/TimesheetBundle/Controller/TimesheetController.php b/src/TimesheetBundle/Controller/TimesheetController.php index 702e2e77..5fe667a3 100644 --- a/src/TimesheetBundle/Controller/TimesheetController.php +++ b/src/TimesheetBundle/Controller/TimesheetController.php @@ -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(), ]); } diff --git a/src/TimesheetBundle/Controller/TimesheetControllerTrait.php b/src/TimesheetBundle/Controller/TimesheetControllerTrait.php new file mode 100644 index 00000000..28c9efe6 --- /dev/null +++ b/src/TimesheetBundle/Controller/TimesheetControllerTrait.php @@ -0,0 +1,108 @@ + + * + * 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 + */ +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', + ] + ); + } +} diff --git a/src/TimesheetBundle/DataFixtures/ORM/LoadFixtures.php b/src/TimesheetBundle/DataFixtures/ORM/LoadFixtures.php index 05865645..011f2395 100644 --- a/src/TimesheetBundle/DataFixtures/ORM/LoadFixtures.php +++ b/src/TimesheetBundle/DataFixtures/ORM/LoadFixtures.php @@ -283,7 +283,7 @@ class LoadFixtures extends AppBundleLoadFixtures 'Customer Relations', 'Infrastructure', 'Software Upgrade', - 'Office Managemenr', + 'Office Management', ]; } diff --git a/src/TimesheetBundle/Form/CustomerEditForm.php b/src/TimesheetBundle/Form/CustomerEditForm.php index 2959615b..49e4c2cd 100644 --- a/src/TimesheetBundle/Form/CustomerEditForm.php +++ b/src/TimesheetBundle/Form/CustomerEditForm.php @@ -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', ]); } } diff --git a/src/TimesheetBundle/Form/ProjectEditForm.php b/src/TimesheetBundle/Form/ProjectEditForm.php index 93f43878..7d82589b 100644 --- a/src/TimesheetBundle/Form/ProjectEditForm.php +++ b/src/TimesheetBundle/Form/ProjectEditForm.php @@ -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, ]); } diff --git a/src/TimesheetBundle/Form/TimesheetEditForm.php b/src/TimesheetBundle/Form/TimesheetEditForm.php index bb103ea5..85d71844 100644 --- a/src/TimesheetBundle/Form/TimesheetEditForm.php +++ b/src/TimesheetBundle/Form/TimesheetEditForm.php @@ -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, ]); } diff --git a/src/TimesheetBundle/Form/TimesheetToolbarForm.php b/src/TimesheetBundle/Form/TimesheetToolbarForm.php new file mode 100644 index 00000000..c7b93965 --- /dev/null +++ b/src/TimesheetBundle/Form/TimesheetToolbarForm.php @@ -0,0 +1,131 @@ + + * + * 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 + */ +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, + ]); + } +} diff --git a/src/TimesheetBundle/Form/Type/ActivityGroupedWithCustomerNameType.php b/src/TimesheetBundle/Form/Type/ActivityGroupedWithCustomerNameType.php new file mode 100644 index 00000000..5d3ea4d6 --- /dev/null +++ b/src/TimesheetBundle/Form/Type/ActivityGroupedWithCustomerNameType.php @@ -0,0 +1,34 @@ + + * + * 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 + */ +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(); + } +} diff --git a/src/TimesheetBundle/Form/Type/ActivityType.php b/src/TimesheetBundle/Form/Type/ActivityType.php new file mode 100644 index 00000000..45996bf3 --- /dev/null +++ b/src/TimesheetBundle/Form/Type/ActivityType.php @@ -0,0 +1,58 @@ + + * + * 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 + */ +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; + } +} diff --git a/src/TimesheetBundle/Form/Type/ProjectType.php b/src/TimesheetBundle/Form/Type/ProjectType.php index 6f041205..67a1eabb 100644 --- a/src/TimesheetBundle/Form/Type/ProjectType.php +++ b/src/TimesheetBundle/Form/Type/ProjectType.php @@ -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; } } diff --git a/src/TimesheetBundle/Model/Query/Timesheet.php b/src/TimesheetBundle/Model/Query/Timesheet.php new file mode 100644 index 00000000..c58a7569 --- /dev/null +++ b/src/TimesheetBundle/Model/Query/Timesheet.php @@ -0,0 +1,201 @@ + + * + * 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 + */ +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; + } + +} diff --git a/src/TimesheetBundle/Repository/TimesheetRepository.php b/src/TimesheetBundle/Repository/TimesheetRepository.php index acac527c..8e74a2a9 100644 --- a/src/TimesheetBundle/Repository/TimesheetRepository.php +++ b/src/TimesheetBundle/Repository/TimesheetRepository.php @@ -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; diff --git a/src/TimesheetBundle/Resources/translations/messages.de.xliff b/src/TimesheetBundle/Resources/translations/messages.de.xliff new file mode 100644 index 00000000..35606a5a --- /dev/null +++ b/src/TimesheetBundle/Resources/translations/messages.de.xliff @@ -0,0 +1,60 @@ + + + + + + + + label.pageSize + Anzahl Einträge + + + timesheet.toolbar.submit + Einträge filtern + + + label.entryState + Zeiten + + + entryState.all + Alle + + + entryState.running + Laufende + + + entryState.stopped + Beendete + + + 0 + 0 + + + 10 + 10 + + + 25 + 25 + + + 50 + 50 + + + 75 + 75 + + + 100 + 100 + + + + + diff --git a/src/TimesheetBundle/Resources/views/timesheet/index.html.twig b/src/TimesheetBundle/Resources/views/timesheet/index.html.twig index 87523b8e..2a914bcf 100644 --- a/src/TimesheetBundle/Resources/views/timesheet/index.html.twig +++ b/src/TimesheetBundle/Resources/views/timesheet/index.html.twig @@ -4,8 +4,13 @@ {% block page_title %}{{ 'timesheet.title'|trans }}{% endblock %} {% block page_subtitle %}{{ 'timesheet.subtitle'|trans }}{% endblock %} +{% block javascript_imports %}{% endblock %} {% block main %} + {% if toolbarForm %} + {{ include('default/_toolbar_form.html.twig', {'form': toolbarForm}) }} + {% endif %} + {% if entries.count > 0 %} {{ datatables.data_table_header({ 'label.date': '', diff --git a/web/css/kimai.css b/web/css/kimai.css index 2bc92e17..a323c19e 100644 --- a/web/css/kimai.css +++ b/web/css/kimai.css @@ -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 ================================ */ diff --git a/web/js/timesheet.js b/web/js/timesheet.js new file mode 100644 index 00000000..549bc3a6 --- /dev/null +++ b/web/js/timesheet.js @@ -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(); + }); + +});