fetch toolbar results without page reload (#518)
This commit is contained in:
@@ -97,11 +97,21 @@ $(function() {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
success: function(data){
|
success: function(data){
|
||||||
$('#' + targetSelect).find('option').remove().end().find('optgroup').remove().end();
|
var selectName = '#' + targetSelect;
|
||||||
|
var $select = $(selectName);
|
||||||
|
var $emptyOption = $(selectName + ' option[value=""]');
|
||||||
|
|
||||||
|
$select.find('option').remove().end().find('optgroup').remove().end();
|
||||||
|
|
||||||
|
if ($emptyOption.length != 0) {
|
||||||
|
$select.append('<option value="">' + $emptyOption.text() + '</option>');
|
||||||
|
}
|
||||||
|
|
||||||
$.each(data, function(i, obj) {
|
$.each(data, function(i, obj) {
|
||||||
$('#' + targetSelect).append('<option value="' + obj.id + '">' + obj.name + '</option>');
|
$select.append('<option value="' + obj.id + '">' + obj.name + '</option>');
|
||||||
});
|
});
|
||||||
$('#' + targetSelect).trigger('change')
|
|
||||||
|
$select.trigger('change');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,11 +6,8 @@
|
|||||||
*/
|
*/
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
|
||||||
//$('.navbar-form select').selectpicker({});
|
|
||||||
|
|
||||||
// DateRange - TODO improve me, so users can type without reloading in between
|
|
||||||
$('.toolbar form input').change(function (event) {
|
$('.toolbar form input').change(function (event) {
|
||||||
$('.toolbar form').submit();
|
toolbarLoadDataAfterChange();
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.toolbar form select').change(function (event) {
|
$('.toolbar form select').change(function (event) {
|
||||||
@@ -27,7 +24,25 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$('.toolbar form').submit();
|
toolbarLoadDataAfterChange();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function toolbarLoadDataAfterChange()
|
||||||
|
{
|
||||||
|
var $form = $('.toolbar form');
|
||||||
|
$.ajax({
|
||||||
|
url: $form.attr('action'),
|
||||||
|
type: $form.attr('method'),
|
||||||
|
data: $form.serialize(),
|
||||||
|
success: function(html) {
|
||||||
|
$('section.content').replaceWith(
|
||||||
|
$(html).find('section.content')
|
||||||
|
);
|
||||||
|
},
|
||||||
|
error: function(xhr, err) {
|
||||||
|
$form.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"build/app.js": "/build/app.js?d823754f6c3293656052",
|
"build/app.js": "/build/app.js?4231baed5706cefe8c25",
|
||||||
"build/app.css": "/build/app.css?c7fd4b9cec5de89467ba89547d2857aa",
|
"build/app.css": "/build/app.css?c7fd4b9cec5de89467ba89547d2857aa",
|
||||||
"build/images/blue@2x.png": "/build/images/blue@2x.png?2694acfd",
|
"build/images/blue@2x.png": "/build/images/blue@2x.png?2694acfd",
|
||||||
"build/images/blue.png": "/build/images/blue.png?96f8a905",
|
"build/images/blue.png": "/build/images/blue.png?96f8a905",
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\API;
|
namespace App\API;
|
||||||
|
|
||||||
use App\Entity\Activity;
|
|
||||||
use App\Repository\ActivityRepository;
|
use App\Repository\ActivityRepository;
|
||||||
use App\Repository\Query\ActivityQuery;
|
use App\Repository\Query\ActivityQuery;
|
||||||
use FOS\RestBundle\Controller\Annotations as Rest;
|
use FOS\RestBundle\Controller\Annotations as Rest;
|
||||||
@@ -59,6 +58,9 @@ class ActivityController extends BaseApiController
|
|||||||
* @Rest\QueryParam(name="project", requirements="\d+", strict=true, nullable=true, description="Project ID to filter activities. If none is provided, only global activities will be returned.")
|
* @Rest\QueryParam(name="project", requirements="\d+", strict=true, nullable=true, description="Project ID to filter activities. If none is provided, only global activities will be returned.")
|
||||||
* @Rest\QueryParam(name="visible", requirements="\d+", strict=true, nullable=true, description="Visibility status to filter activities (1=visible, 2=hidden, 3=both)")
|
* @Rest\QueryParam(name="visible", requirements="\d+", strict=true, nullable=true, description="Visibility status to filter activities (1=visible, 2=hidden, 3=both)")
|
||||||
* @Rest\QueryParam(name="globals", requirements="true", strict=true, nullable=true, description="Pass 'true' as string to fetch only global activities")
|
* @Rest\QueryParam(name="globals", requirements="true", strict=true, nullable=true, description="Pass 'true' as string to fetch only global activities")
|
||||||
|
* @Rest\QueryParam(name="globalsFirst", requirements="false", strict=true, nullable=true, description="Pass 'false' as string if you don't want the global activities to be listed first")
|
||||||
|
* @Rest\QueryParam(name="order", requirements="ASC|DESC", strict=true, nullable=true, description="The result order (allowed values: 'ASC', 'DESC')")
|
||||||
|
* @Rest\QueryParam(name="orderBy", requirements="id|name|project", strict=true, nullable=true, description="The field by which results will be ordered (allowed values: 'id', 'name', 'project')")
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return Response
|
||||||
*/
|
*/
|
||||||
@@ -66,12 +68,26 @@ class ActivityController extends BaseApiController
|
|||||||
{
|
{
|
||||||
$query = new ActivityQuery();
|
$query = new ActivityQuery();
|
||||||
$query->setOrderGlobalsFirst(true)
|
$query->setOrderGlobalsFirst(true)
|
||||||
->setResultType(ActivityQuery::RESULT_TYPE_OBJECTS);
|
->setResultType(ActivityQuery::RESULT_TYPE_OBJECTS)
|
||||||
|
->setOrderBy('name')
|
||||||
|
;
|
||||||
|
|
||||||
|
if (null !== ($order = $paramFetcher->get('order'))) {
|
||||||
|
$query->setOrder($order);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== ($orderBy = $paramFetcher->get('orderBy'))) {
|
||||||
|
$query->setOrderBy($orderBy);
|
||||||
|
}
|
||||||
|
|
||||||
if (null !== ($globals = $paramFetcher->get('globals'))) {
|
if (null !== ($globals = $paramFetcher->get('globals'))) {
|
||||||
$query->setGlobalsOnly(true);
|
$query->setGlobalsOnly(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ('false' === ($globalsFirst = $paramFetcher->get('globalsFirst'))) {
|
||||||
|
$query->setOrderGlobalsFirst(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (null !== ($project = $paramFetcher->get('project'))) {
|
if (null !== ($project = $paramFetcher->get('project'))) {
|
||||||
$query->setProject($project);
|
$query->setProject($project);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\API;
|
namespace App\API;
|
||||||
|
|
||||||
use App\Entity\Customer;
|
|
||||||
use App\Repository\CustomerRepository;
|
use App\Repository\CustomerRepository;
|
||||||
use App\Repository\Query\CustomerQuery;
|
use App\Repository\Query\CustomerQuery;
|
||||||
use FOS\RestBundle\Controller\Annotations as Rest;
|
use FOS\RestBundle\Controller\Annotations as Rest;
|
||||||
@@ -57,13 +56,26 @@ class CustomerController extends BaseApiController
|
|||||||
* @SWG\Schema(ref="#/definitions/CustomerCollection"),
|
* @SWG\Schema(ref="#/definitions/CustomerCollection"),
|
||||||
* )
|
* )
|
||||||
* @Rest\QueryParam(name="visible", requirements="\d+", strict=true, nullable=true, description="Visibility status to filter activities (1=visible, 2=hidden, 3=both)")
|
* @Rest\QueryParam(name="visible", requirements="\d+", strict=true, nullable=true, description="Visibility status to filter activities (1=visible, 2=hidden, 3=both)")
|
||||||
|
* @Rest\QueryParam(name="order", requirements="ASC|DESC", strict=true, nullable=true, description="The result order (allowed values: 'ASC', 'DESC')")
|
||||||
|
* @Rest\QueryParam(name="orderBy", requirements="id|name", strict=true, nullable=true, description="The field by which results will be ordered (allowed values: 'id', 'name')")
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return Response
|
||||||
*/
|
*/
|
||||||
public function cgetAction(ParamFetcherInterface $paramFetcher)
|
public function cgetAction(ParamFetcherInterface $paramFetcher)
|
||||||
{
|
{
|
||||||
$query = new CustomerQuery();
|
$query = new CustomerQuery();
|
||||||
$query->setResultType(CustomerQuery::RESULT_TYPE_OBJECTS);
|
$query
|
||||||
|
->setResultType(CustomerQuery::RESULT_TYPE_OBJECTS)
|
||||||
|
->setOrderBy('name')
|
||||||
|
;
|
||||||
|
|
||||||
|
if (null !== ($order = $paramFetcher->get('order'))) {
|
||||||
|
$query->setOrder($order);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== ($orderBy = $paramFetcher->get('orderBy'))) {
|
||||||
|
$query->setOrderBy($orderBy);
|
||||||
|
}
|
||||||
|
|
||||||
if (null !== ($visible = $paramFetcher->get('visible'))) {
|
if (null !== ($visible = $paramFetcher->get('visible'))) {
|
||||||
$query->setVisibility($visible);
|
$query->setVisibility($visible);
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\API;
|
namespace App\API;
|
||||||
|
|
||||||
use App\Entity\Project;
|
|
||||||
use App\Repository\ProjectRepository;
|
use App\Repository\ProjectRepository;
|
||||||
use App\Repository\Query\ProjectQuery;
|
use App\Repository\Query\ProjectQuery;
|
||||||
use FOS\RestBundle\Controller\Annotations as Rest;
|
use FOS\RestBundle\Controller\Annotations as Rest;
|
||||||
@@ -58,6 +57,8 @@ class ProjectController extends BaseApiController
|
|||||||
* )
|
* )
|
||||||
* @Rest\QueryParam(name="customer", requirements="\d+", strict=true, nullable=true, description="Customer ID to filter projects")
|
* @Rest\QueryParam(name="customer", requirements="\d+", strict=true, nullable=true, description="Customer ID to filter projects")
|
||||||
* @Rest\QueryParam(name="visible", requirements="\d+", strict=true, nullable=true, description="Visibility status to filter projects (1=visible, 2=hidden, 3=both)")
|
* @Rest\QueryParam(name="visible", requirements="\d+", strict=true, nullable=true, description="Visibility status to filter projects (1=visible, 2=hidden, 3=both)")
|
||||||
|
* @Rest\QueryParam(name="order", requirements="ASC|DESC", strict=true, nullable=true, description="The result order (allowed values: 'ASC', 'DESC')")
|
||||||
|
* @Rest\QueryParam(name="orderBy", requirements="id|name", strict=true, nullable=true, description="The field by which results will be ordered (allowed values: 'id', 'name')")
|
||||||
*
|
*
|
||||||
* @param ParamFetcherInterface $paramFetcher
|
* @param ParamFetcherInterface $paramFetcher
|
||||||
* @return Response
|
* @return Response
|
||||||
@@ -65,7 +66,18 @@ class ProjectController extends BaseApiController
|
|||||||
public function cgetAction(ParamFetcherInterface $paramFetcher)
|
public function cgetAction(ParamFetcherInterface $paramFetcher)
|
||||||
{
|
{
|
||||||
$query = new ProjectQuery();
|
$query = new ProjectQuery();
|
||||||
$query->setResultType(ProjectQuery::RESULT_TYPE_OBJECTS);
|
$query
|
||||||
|
->setResultType(ProjectQuery::RESULT_TYPE_OBJECTS)
|
||||||
|
->setOrderBy('name')
|
||||||
|
;
|
||||||
|
|
||||||
|
if (null !== ($order = $paramFetcher->get('order'))) {
|
||||||
|
$query->setOrder($order);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== ($orderBy = $paramFetcher->get('orderBy'))) {
|
||||||
|
$query->setOrderBy($orderBy);
|
||||||
|
}
|
||||||
|
|
||||||
if (null !== ($customer = $paramFetcher->get('customer'))) {
|
if (null !== ($customer = $paramFetcher->get('customer'))) {
|
||||||
$query->setCustomer($customer);
|
$query->setCustomer($customer);
|
||||||
|
|||||||
@@ -395,13 +395,12 @@ class Timesheet
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null === $this->getBegin() && null !== $this->getEnd()) {
|
if (null === $this->getBegin()) {
|
||||||
$context->buildViolation('You must submit a begin date before an end date is added.')
|
$context->buildViolation('You must submit a begin date.')
|
||||||
->atPath('begin')
|
->atPath('begin')
|
||||||
->setTranslationDomain('validators')
|
->setTranslationDomain('validators')
|
||||||
->addViolation();
|
->addViolation();
|
||||||
}
|
} else {
|
||||||
|
|
||||||
if (null !== $this->getBegin() && null !== $this->getEnd() && $this->getEnd()->getTimestamp() < $this->getBegin()->getTimestamp()) {
|
if (null !== $this->getBegin() && null !== $this->getEnd() && $this->getEnd()->getTimestamp() < $this->getBegin()->getTimestamp()) {
|
||||||
$context->buildViolation('End date must not be earlier then start date.')
|
$context->buildViolation('End date must not be earlier then start date.')
|
||||||
->atPath('end')
|
->atPath('end')
|
||||||
@@ -415,6 +414,6 @@ class Timesheet
|
|||||||
->setTranslationDomain('validators')
|
->setTranslationDomain('validators')
|
||||||
->addViolation();
|
->addViolation();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,12 +15,15 @@ use App\Form\Type\ProjectType;
|
|||||||
use App\Form\Type\YesNoType;
|
use App\Form\Type\YesNoType;
|
||||||
use App\Repository\CustomerRepository;
|
use App\Repository\CustomerRepository;
|
||||||
use App\Repository\ProjectRepository;
|
use App\Repository\ProjectRepository;
|
||||||
|
use App\Repository\Query\ProjectQuery;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
|
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\Form\FormEvent;
|
||||||
|
use Symfony\Component\Form\FormEvents;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,11 +67,7 @@ class ActivityEditForm extends AbstractType
|
|||||||
'data' => $customer ? $customer : null,
|
'data' => $customer ? $customer : null,
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'mapped' => false,
|
'mapped' => false,
|
||||||
'api_data' => [
|
'project_enabled' => true,
|
||||||
'select' => 'project',
|
|
||||||
'route' => 'get_projects',
|
|
||||||
'route_params' => ['customer' => '-s-']
|
|
||||||
],
|
|
||||||
])
|
])
|
||||||
->add('project', ProjectType::class, [
|
->add('project', ProjectType::class, [
|
||||||
'label' => 'label.project',
|
'label' => 'label.project',
|
||||||
@@ -76,7 +75,27 @@ class ActivityEditForm extends AbstractType
|
|||||||
'query_builder' => function (ProjectRepository $repo) use ($project, $customer) {
|
'query_builder' => function (ProjectRepository $repo) use ($project, $customer) {
|
||||||
return $repo->builderForEntityType($project, $customer);
|
return $repo->builderForEntityType($project, $customer);
|
||||||
},
|
},
|
||||||
])
|
]);
|
||||||
|
|
||||||
|
// replaces the project select after submission, to make sure only projects for the selected customer are displayed
|
||||||
|
$builder->addEventListener(
|
||||||
|
FormEvents::PRE_SUBMIT,
|
||||||
|
function (FormEvent $event) use ($project) {
|
||||||
|
$data = $event->getData();
|
||||||
|
if (!isset($data['customer']) || empty($data['customer'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$event->getForm()->add('project', ProjectType::class, [
|
||||||
|
'group_by' => null,
|
||||||
|
'query_builder' => function (ProjectRepository $repo) use ($data, $project) {
|
||||||
|
return $repo->builderForEntityType($project, $data['customer']);
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder
|
||||||
->add('fixedRate', MoneyType::class, [
|
->add('fixedRate', MoneyType::class, [
|
||||||
'label' => 'label.fixed_rate',
|
'label' => 'label.fixed_rate',
|
||||||
'required' => false,
|
'required' => false,
|
||||||
@@ -93,7 +112,7 @@ class ActivityEditForm extends AbstractType
|
|||||||
])
|
])
|
||||||
;
|
;
|
||||||
|
|
||||||
if ($entry->getId() === null) {
|
if (null === $entry->getId()) {
|
||||||
$builder->add('create_more', CheckboxType::class, [
|
$builder->add('create_more', CheckboxType::class, [
|
||||||
'label' => 'label.create_more',
|
'label' => 'label.create_more',
|
||||||
'required' => false,
|
'required' => false,
|
||||||
|
|||||||
@@ -55,8 +55,12 @@ class SelectWithApiDataExtension extends AbstractTypeExtension
|
|||||||
|
|
||||||
$apiData = $options['api_data'];
|
$apiData = $options['api_data'];
|
||||||
|
|
||||||
|
if (!is_array($apiData)) {
|
||||||
|
throw new \InvalidArgumentException('Option "api_data" must be an array for form "' . $form->getName() . '"');
|
||||||
|
}
|
||||||
|
|
||||||
if (!isset($apiData['select'])) {
|
if (!isset($apiData['select'])) {
|
||||||
throw new \InvalidArgumentException('Missing "select" option for "api_data" option for form "' . $form->getName() . '"');
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($apiData['route'])) {
|
if (!isset($apiData['route'])) {
|
||||||
@@ -67,8 +71,13 @@ class SelectWithApiDataExtension extends AbstractTypeExtension
|
|||||||
$apiData['route_params'] = [];
|
$apiData['route_params'] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$formPrefix = $form->getParent()->getName();
|
||||||
|
if (!empty($formPrefix)) {
|
||||||
|
$formPrefix .= '_';
|
||||||
|
}
|
||||||
|
|
||||||
$view->vars['attr'] = array_merge($view->vars['attr'], [
|
$view->vars['attr'] = array_merge($view->vars['attr'], [
|
||||||
'data-related-select' => $form->getParent()->getName() . '_' . $apiData['select'],
|
'data-related-select' => $formPrefix . $apiData['select'],
|
||||||
'data-api-url' => $this->router->generate($apiData['route'], $apiData['route_params']),
|
'data-api-url' => $this->router->generate($apiData['route'], $apiData['route_params']),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,12 +96,14 @@ class TimesheetEditForm extends AbstractType
|
|||||||
'html5' => false,
|
'html5' => false,
|
||||||
'format' => 'yyyy-MM-dd HH:mm',
|
'format' => 'yyyy-MM-dd HH:mm',
|
||||||
'with_seconds' => false,
|
'with_seconds' => false,
|
||||||
'attr' => ['autocomplete' => 'off', 'data-datetimepicker' => 'on'],
|
'attr' => ['autocomplete' => 'off', 'data-datetimepicker' => 'on', 'placeholder' => 'yyyy-MM-dd HH:mm'],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($options['duration_only']) {
|
if ($options['duration_only']) {
|
||||||
$builder->add('duration', DurationType::class);
|
$builder->add('duration', DurationType::class, [
|
||||||
|
'required' => false,
|
||||||
|
]);
|
||||||
} else {
|
} else {
|
||||||
$builder->add('end', DateTimeType::class, [
|
$builder->add('end', DateTimeType::class, [
|
||||||
'label' => 'label.end',
|
'label' => 'label.end',
|
||||||
@@ -110,7 +112,7 @@ class TimesheetEditForm extends AbstractType
|
|||||||
'html5' => false,
|
'html5' => false,
|
||||||
'format' => 'yyyy-MM-dd HH:mm',
|
'format' => 'yyyy-MM-dd HH:mm',
|
||||||
'with_seconds' => false,
|
'with_seconds' => false,
|
||||||
'attr' => ['autocomplete' => 'off', 'data-datetimepicker' => 'on'],
|
'attr' => ['autocomplete' => 'off', 'data-datetimepicker' => 'on', 'placeholder' => 'yyyy-MM-dd HH:mm'],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,11 +133,7 @@ class TimesheetEditForm extends AbstractType
|
|||||||
'required' => false,
|
'required' => false,
|
||||||
'placeholder' => null === $customer ? '' : null,
|
'placeholder' => null === $customer ? '' : null,
|
||||||
'mapped' => false,
|
'mapped' => false,
|
||||||
'api_data' => [
|
'project_enabled' => true,
|
||||||
'select' => 'project',
|
|
||||||
'route' => 'get_projects',
|
|
||||||
'route_params' => ['customer' => '-s-']
|
|
||||||
],
|
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
$projectOptions['group_by'] = null;
|
$projectOptions['group_by'] = null;
|
||||||
@@ -149,25 +147,41 @@ class TimesheetEditForm extends AbstractType
|
|||||||
|
|
||||||
$builder
|
$builder
|
||||||
->add('project', ProjectType::class, array_merge($projectOptions, [
|
->add('project', ProjectType::class, array_merge($projectOptions, [
|
||||||
|
'activity_enabled' => true,
|
||||||
// documentation is for NelmioApiDocBundle
|
// documentation is for NelmioApiDocBundle
|
||||||
'documentation' => [
|
'documentation' => [
|
||||||
'type' => 'integer',
|
'type' => 'integer',
|
||||||
'description' => 'Project ID',
|
'description' => 'Project ID',
|
||||||
],
|
],
|
||||||
'required' => true,
|
|
||||||
'query_builder' => function (ProjectRepository $repo) use ($project, $customer) {
|
'query_builder' => function (ProjectRepository $repo) use ($project, $customer) {
|
||||||
return $repo->builderForEntityType($project, $customer);
|
return $repo->builderForEntityType($project, $customer);
|
||||||
},
|
},
|
||||||
'api_data' => [
|
])
|
||||||
'select' => 'activity',
|
);
|
||||||
'route' => 'get_activities',
|
|
||||||
'route_params' => ['project' => '-s-']
|
// replaces the project select after submission, to make sure only projects for the selected customer are displayed
|
||||||
],
|
$builder->addEventListener(
|
||||||
]));
|
FormEvents::PRE_SUBMIT,
|
||||||
|
function (FormEvent $event) use ($project) {
|
||||||
|
$data = $event->getData();
|
||||||
|
if (!isset($data['customer']) || empty($data['customer'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$event->getForm()->add('project', ProjectType::class, [
|
||||||
|
'activity_enabled' => true,
|
||||||
|
'group_by' => null,
|
||||||
|
'query_builder' => function (ProjectRepository $repo) use ($data, $project) {
|
||||||
|
return $repo->builderForEntityType($project, $data['customer']);
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
$builder
|
$builder
|
||||||
->add('activity', ActivityType::class, [
|
->add('activity', ActivityType::class, [
|
||||||
// documentation is for NelmioApiDocBundle
|
// documentation is for NelmioApiDocBundle
|
||||||
|
'placeholder' => null,
|
||||||
'documentation' => [
|
'documentation' => [
|
||||||
'type' => 'integer',
|
'type' => 'integer',
|
||||||
'description' => 'Activity ID',
|
'description' => 'Activity ID',
|
||||||
@@ -176,6 +190,27 @@ class TimesheetEditForm extends AbstractType
|
|||||||
return $repo->builderForEntityType($activity, $project);
|
return $repo->builderForEntityType($activity, $project);
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
;
|
||||||
|
|
||||||
|
// replaces the activity select after submission, to make sure only activities for the selected project are displayed
|
||||||
|
$builder->addEventListener(
|
||||||
|
FormEvents::PRE_SUBMIT,
|
||||||
|
function (FormEvent $event) use ($activity) {
|
||||||
|
$data = $event->getData();
|
||||||
|
if (!isset($data['project']) || empty($data['project'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$event->getForm()->add('activity', ActivityType::class, [
|
||||||
|
'placeholder' => null,
|
||||||
|
'query_builder' => function (ActivityRepository $repo) use ($data, $activity) {
|
||||||
|
return $repo->builderForEntityType($activity, $data['project']);
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$builder
|
||||||
->add('description', TextareaType::class, [
|
->add('description', TextareaType::class, [
|
||||||
'label' => 'label.description',
|
'label' => 'label.description',
|
||||||
'required' => false,
|
'required' => false,
|
||||||
@@ -196,41 +231,6 @@ class TimesheetEditForm extends AbstractType
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
$builder->get('customer')->addEventListener(
|
|
||||||
FormEvents::POST_SUBMIT,
|
|
||||||
function (FormEvent $event) {
|
|
||||||
$customer = $event->getForm()->getData();
|
|
||||||
$event->getForm()->getParent()->add('project', ProjectType::class, [
|
|
||||||
'required' => true,
|
|
||||||
'placeholder' => '',
|
|
||||||
'label' => 'label.project',
|
|
||||||
'query_builder' => function (ProjectRepository $repo) use ($customer) {
|
|
||||||
return $repo->builderForEntityType(null, $customer);
|
|
||||||
},
|
|
||||||
'api_data' => [
|
|
||||||
'select' => 'activity',
|
|
||||||
'route' => 'get_activities',
|
|
||||||
'route_params' => ['project' => '-s-']
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
|
|
||||||
$builder->get('project')->addEventListener(
|
|
||||||
FormEvents::POST_SUBMIT,
|
|
||||||
function (FormEvent $event) {
|
|
||||||
$project = $event->getForm()->getData();
|
|
||||||
$event->getForm()->getParent()->add('activity', ActivityType::class, [
|
|
||||||
'label' => 'label.activity',
|
|
||||||
'query_builder' => function (ActivityRepository $repo) use ($project) {
|
|
||||||
return $repo->builderForEntityType(null, $project);
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($options['include_user']) {
|
if ($options['include_user']) {
|
||||||
$builder->add('user', UserType::class);
|
$builder->add('user', UserType::class);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,9 @@
|
|||||||
namespace App\Form\Toolbar;
|
namespace App\Form\Toolbar;
|
||||||
|
|
||||||
use App\Form\Type\ActivityType;
|
use App\Form\Type\ActivityType;
|
||||||
|
use App\Form\Type\BeginDateType;
|
||||||
use App\Form\Type\CustomerType;
|
use App\Form\Type\CustomerType;
|
||||||
|
use App\Form\Type\EndDateType;
|
||||||
use App\Form\Type\PageSizeType;
|
use App\Form\Type\PageSizeType;
|
||||||
use App\Form\Type\ProjectType;
|
use App\Form\Type\ProjectType;
|
||||||
use App\Form\Type\UserRoleType;
|
use App\Form\Type\UserRoleType;
|
||||||
@@ -23,8 +25,6 @@ use App\Repository\Query\ActivityQuery;
|
|||||||
use App\Repository\Query\CustomerQuery;
|
use App\Repository\Query\CustomerQuery;
|
||||||
use App\Repository\Query\ProjectQuery;
|
use App\Repository\Query\ProjectQuery;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
|
||||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\Form\FormEvent;
|
use Symfony\Component\Form\FormEvent;
|
||||||
use Symfony\Component\Form\FormEvents;
|
use Symfony\Component\Form\FormEvents;
|
||||||
@@ -65,6 +65,8 @@ abstract class AbstractToolbarForm extends AbstractType
|
|||||||
{
|
{
|
||||||
$builder->add('customer', CustomerType::class, [
|
$builder->add('customer', CustomerType::class, [
|
||||||
'required' => false,
|
'required' => false,
|
||||||
|
'project_enabled' => true,
|
||||||
|
'project_visibility' => ProjectQuery::SHOW_BOTH,
|
||||||
'query_builder' => function (CustomerRepository $repo) {
|
'query_builder' => function (CustomerRepository $repo) {
|
||||||
$query = new CustomerQuery();
|
$query = new CustomerQuery();
|
||||||
$query->setVisibility(CustomerQuery::SHOW_BOTH); // this field is the reason for the query here
|
$query->setVisibility(CustomerQuery::SHOW_BOTH); // this field is the reason for the query here
|
||||||
@@ -114,14 +116,8 @@ abstract class AbstractToolbarForm extends AbstractType
|
|||||||
*/
|
*/
|
||||||
protected function addStartDateChoice(FormBuilderInterface $builder)
|
protected function addStartDateChoice(FormBuilderInterface $builder)
|
||||||
{
|
{
|
||||||
$builder->add('begin', DateType::class, [
|
$builder->add('begin', BeginDateType::class, [
|
||||||
'label' => 'label.begin',
|
|
||||||
'widget' => 'single_text',
|
|
||||||
'html5' => false,
|
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'format' => DateType::HTML5_FORMAT,
|
|
||||||
'attr' => ['autocomplete' => 'off', 'data-datepicker' => 'on'],
|
|
||||||
'empty_data' => (new \DateTime('first day of this month'))->format('Y-M-d')
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,14 +126,8 @@ abstract class AbstractToolbarForm extends AbstractType
|
|||||||
*/
|
*/
|
||||||
protected function addEndDateChoice(FormBuilderInterface $builder)
|
protected function addEndDateChoice(FormBuilderInterface $builder)
|
||||||
{
|
{
|
||||||
$builder->add('end', DateType::class, [
|
$builder->add('end', EndDateType::class, [
|
||||||
'label' => 'label.end',
|
|
||||||
'widget' => 'single_text',
|
|
||||||
'html5' => false,
|
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'format' => DateType::HTML5_FORMAT,
|
|
||||||
'attr' => ['autocomplete' => 'off', 'data-datepicker' => 'on'],
|
|
||||||
'empty_data' => (new \DateTime('last day of this month'))->format('Y-M-d')
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,10 +136,11 @@ abstract class AbstractToolbarForm extends AbstractType
|
|||||||
*/
|
*/
|
||||||
protected function addProjectChoice(FormBuilderInterface $builder)
|
protected function addProjectChoice(FormBuilderInterface $builder)
|
||||||
{
|
{
|
||||||
$builder->add('project', ChoiceType::class, [
|
$builder->add('project', ProjectType::class, [
|
||||||
'group_by' => null,
|
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'label' => 'label.project',
|
'activity_enabled' => true,
|
||||||
|
'activity_visibility' => ActivityQuery::SHOW_BOTH,
|
||||||
|
'choices' => [],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$builder->addEventListener(
|
$builder->addEventListener(
|
||||||
@@ -163,6 +154,8 @@ abstract class AbstractToolbarForm extends AbstractType
|
|||||||
$event->getForm()->add('project', ProjectType::class, [
|
$event->getForm()->add('project', ProjectType::class, [
|
||||||
'group_by' => null,
|
'group_by' => null,
|
||||||
'required' => false,
|
'required' => false,
|
||||||
|
'activity_enabled' => true,
|
||||||
|
'activity_visibility' => ActivityQuery::SHOW_BOTH,
|
||||||
'query_builder' => function (ProjectRepository $repo) use ($data) {
|
'query_builder' => function (ProjectRepository $repo) use ($data) {
|
||||||
$query = new ProjectQuery();
|
$query = new ProjectQuery();
|
||||||
$query->setCustomer($data['customer']);
|
$query->setCustomer($data['customer']);
|
||||||
|
|||||||
51
src/Form/Type/BeginDateType.php
Normal file
51
src/Form/Type/BeginDateType.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?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\Form\Type;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||||
|
use Symfony\Component\OptionsResolver\Options;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom form field type to display the begin-date input field.
|
||||||
|
*/
|
||||||
|
class BeginDateType extends AbstractType
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'label' => 'label.begin',
|
||||||
|
'widget' => 'single_text',
|
||||||
|
'html5' => false,
|
||||||
|
'format' => DateType::HTML5_FORMAT,
|
||||||
|
'empty_data' => (new \DateTime('first day of this month'))->format('Y-M-d')
|
||||||
|
]);
|
||||||
|
|
||||||
|
$resolver->setDefault('attr', function (Options $options) {
|
||||||
|
return [
|
||||||
|
'autocomplete' => 'off',
|
||||||
|
'data-datepicker' => 'on',
|
||||||
|
'placeholder' => $options['format'],
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getParent()
|
||||||
|
{
|
||||||
|
return DateType::class;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,8 +11,10 @@ namespace App\Form\Type;
|
|||||||
|
|
||||||
use App\Entity\Customer;
|
use App\Entity\Customer;
|
||||||
use App\Repository\CustomerRepository;
|
use App\Repository\CustomerRepository;
|
||||||
|
use App\Repository\Query\ProjectQuery;
|
||||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\OptionsResolver\Options;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,8 +34,22 @@ class CustomerType extends AbstractType
|
|||||||
'query_builder' => function (CustomerRepository $repo) {
|
'query_builder' => function (CustomerRepository $repo) {
|
||||||
return $repo->builderForEntityType(null);
|
return $repo->builderForEntityType(null);
|
||||||
},
|
},
|
||||||
|
'project_enabled' => false,
|
||||||
|
'project_visibility' => ProjectQuery::SHOW_VISIBLE,
|
||||||
//'attr' => ['class' => 'selectpicker', 'data-size' => 10, 'data-live-search' => true, 'data-width' => '100%']
|
//'attr' => ['class' => 'selectpicker', 'data-size' => 10, 'data-live-search' => true, 'data-width' => '100%']
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$resolver->setDefault('api_data', function (Options $options) {
|
||||||
|
if (true === $options['project_enabled']) {
|
||||||
|
return [
|
||||||
|
'select' => 'project',
|
||||||
|
'route' => 'get_projects',
|
||||||
|
'route_params' => ['customer' => '-s-', 'orderBy' => 'name', 'visible' => $options['project_visibility']],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
51
src/Form/Type/EndDateType.php
Normal file
51
src/Form/Type/EndDateType.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?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\Form\Type;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||||
|
use Symfony\Component\OptionsResolver\Options;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom form field type to display the end-date input field.
|
||||||
|
*/
|
||||||
|
class EndDateType extends AbstractType
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'label' => 'label.end',
|
||||||
|
'widget' => 'single_text',
|
||||||
|
'html5' => false,
|
||||||
|
'format' => DateType::HTML5_FORMAT,
|
||||||
|
'empty_data' => (new \DateTime('last day of this month'))->format('Y-M-d')
|
||||||
|
]);
|
||||||
|
|
||||||
|
$resolver->setDefault('attr', function (Options $options) {
|
||||||
|
return [
|
||||||
|
'autocomplete' => 'off',
|
||||||
|
'data-datepicker' => 'on',
|
||||||
|
'placeholder' => $options['format'],
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getParent()
|
||||||
|
{
|
||||||
|
return DateType::class;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,8 +11,10 @@ namespace App\Form\Type;
|
|||||||
|
|
||||||
use App\Entity\Project;
|
use App\Entity\Project;
|
||||||
use App\Repository\ProjectRepository;
|
use App\Repository\ProjectRepository;
|
||||||
|
use App\Repository\Query\ActivityQuery;
|
||||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\OptionsResolver\Options;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,8 +59,22 @@ class ProjectType extends AbstractType
|
|||||||
'query_builder' => function (ProjectRepository $repo) {
|
'query_builder' => function (ProjectRepository $repo) {
|
||||||
return $repo->builderForEntityType(null);
|
return $repo->builderForEntityType(null);
|
||||||
},
|
},
|
||||||
|
'activity_enabled' => false,
|
||||||
|
'activity_visibility' => ActivityQuery::SHOW_VISIBLE,
|
||||||
//'attr' => ['class' => 'selectpicker', 'data-size' => 10, 'data-live-search' => true, 'data-width' => '100%']
|
//'attr' => ['class' => 'selectpicker', 'data-size' => 10, 'data-live-search' => true, 'data-width' => '100%']
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$resolver->setDefault('api_data', function (Options $options) {
|
||||||
|
if (true === $options['activity_enabled']) {
|
||||||
|
return [
|
||||||
|
'select' => 'activity',
|
||||||
|
'route' => 'get_activities',
|
||||||
|
'route_params' => ['project' => '-s-', 'orderBy' => 'name', 'visible' => $options['activity_visibility']],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ class ActivityRepository extends AbstractRepository
|
|||||||
$query->setResultType(ActivityQuery::RESULT_TYPE_QUERYBUILDER);
|
$query->setResultType(ActivityQuery::RESULT_TYPE_QUERYBUILDER);
|
||||||
$query->setProject($project);
|
$query->setProject($project);
|
||||||
$query->setOrderGlobalsFirst(true);
|
$query->setOrderGlobalsFirst(true);
|
||||||
|
$query->setOrderBy('name');
|
||||||
|
|
||||||
if (null === $activity && $project === null) {
|
if (null === $activity && $project === null) {
|
||||||
$query->setGlobalsOnly(true);
|
$query->setGlobalsOnly(true);
|
||||||
|
|||||||
@@ -85,11 +85,11 @@ class ProjectRepository extends AbstractRepository
|
|||||||
/**
|
/**
|
||||||
* Returns a query builder that is used for ProjectType and your own 'query_builder' option.
|
* Returns a query builder that is used for ProjectType and your own 'query_builder' option.
|
||||||
*
|
*
|
||||||
* @param Project|null $entity
|
* @param Project|int|null $entity
|
||||||
* @param Customer|null $customer
|
* @param Customer|int|null $customer
|
||||||
* @return array|QueryBuilder|Pagerfanta
|
* @return array|QueryBuilder|Pagerfanta
|
||||||
*/
|
*/
|
||||||
public function builderForEntityType(Project $entity = null, Customer $customer = null)
|
public function builderForEntityType($entity = null, $customer = null)
|
||||||
{
|
{
|
||||||
$query = new ProjectQuery();
|
$query = new ProjectQuery();
|
||||||
$query->setHiddenEntity($entity);
|
$query->setHiddenEntity($entity);
|
||||||
@@ -121,7 +121,6 @@ class ProjectRepository extends AbstractRepository
|
|||||||
}
|
}
|
||||||
$qb->andWhere('p.visible = 1');
|
$qb->andWhere('p.visible = 1');
|
||||||
|
|
||||||
/** @var Project $entity */
|
|
||||||
$entity = $query->getHiddenEntity();
|
$entity = $query->getHiddenEntity();
|
||||||
if (null !== $entity) {
|
if (null !== $entity) {
|
||||||
$qb->orWhere('p.id = :project')->setParameter('project', $entity);
|
$qb->orWhere('p.id = :project')->setParameter('project', $entity);
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ class Duration
|
|||||||
*/
|
*/
|
||||||
public function format($seconds, $format = self::FORMAT_NO_SECONDS)
|
public function format($seconds, $format = self::FORMAT_NO_SECONDS)
|
||||||
{
|
{
|
||||||
|
if (null === $seconds) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
$hour = floor($seconds / 3600);
|
$hour = floor($seconds / 3600);
|
||||||
$minute = floor(($seconds / 60) % 60);
|
$minute = floor(($seconds / 60) % 60);
|
||||||
|
|
||||||
|
|||||||
@@ -94,7 +94,8 @@ class ProjectControllerTest extends APIControllerBaseTest
|
|||||||
|
|
||||||
public function getCollectionTestData()
|
public function getCollectionTestData()
|
||||||
{
|
{
|
||||||
yield ['/api/projects', [], [[true, 1], [false, 3], [false, 1]]];
|
// if you wonder why: SQLite does case-sensitive ordering, so "Title" > "fifth”
|
||||||
|
yield ['/api/projects', [], [[true, 1], [false, 1], [false, 3]]];
|
||||||
yield ['/api/projects', ['customer' => '1'], [[true, 1], [false, 1]]];
|
yield ['/api/projects', ['customer' => '1'], [[true, 1], [false, 1]]];
|
||||||
yield ['/api/projects', ['customer' => '1', 'visible' => VisibilityQuery::SHOW_VISIBLE], [[true, 1], [false, 1]]];
|
yield ['/api/projects', ['customer' => '1', 'visible' => VisibilityQuery::SHOW_VISIBLE], [[true, 1], [false, 1]]];
|
||||||
yield ['/api/projects', ['customer' => '1', 'visible' => VisibilityQuery::SHOW_BOTH], [[true, 1], [false, 1], [false, 1]]];
|
yield ['/api/projects', ['customer' => '1', 'visible' => VisibilityQuery::SHOW_BOTH], [[true, 1], [false, 1], [false, 1]]];
|
||||||
|
|||||||
@@ -161,13 +161,16 @@ class ExtensionsTest extends TestCase
|
|||||||
$sut = $this->getSut($this->localeEn, 'XX');
|
$sut = $this->getSut($this->localeEn, 'XX');
|
||||||
$this->assertEquals('02:37 h', $sut->duration($record->getDuration()));
|
$this->assertEquals('02:37 h', $sut->duration($record->getDuration()));
|
||||||
|
|
||||||
// test negative duratiobn
|
// test negative duration
|
||||||
$sut = $this->getSut($this->localeEn, 'XX');
|
$sut = $this->getSut($this->localeEn, 'XX');
|
||||||
$this->assertEquals('?', $sut->duration('-1'));
|
$this->assertEquals('?', $sut->duration('-1'));
|
||||||
|
|
||||||
// test zero duration
|
// test zero duration
|
||||||
$sut = $this->getSut($this->localeEn, 'XX');
|
$sut = $this->getSut($this->localeEn, 'XX');
|
||||||
$this->assertEquals('00:00 h', $sut->duration('0'));
|
$this->assertEquals('00:00 h', $sut->duration('0'));
|
||||||
|
|
||||||
|
$sut = $this->getSut($this->localeEn, 'XX');
|
||||||
|
$this->assertNull($sut->duration(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getTimesheet($seconds)
|
protected function getTimesheet($seconds)
|
||||||
|
|||||||
Reference in New Issue
Block a user