added daterange-picker and localized date-inputs (#540)
This commit is contained in:
62
src/Form/Model/DateRange.php
Normal file
62
src/Form/Model/DateRange.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?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\Model;
|
||||
|
||||
use DateTime;
|
||||
|
||||
class DateRange
|
||||
{
|
||||
/**
|
||||
* @var DateTime
|
||||
*/
|
||||
protected $begin;
|
||||
/**
|
||||
* @var DateTime
|
||||
*/
|
||||
protected $end;
|
||||
|
||||
/**
|
||||
* @return DateTime
|
||||
*/
|
||||
public function getBegin(): ?DateTime
|
||||
{
|
||||
return $this->begin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime $begin
|
||||
* @return DateRange
|
||||
*/
|
||||
public function setBegin(DateTime $begin)
|
||||
{
|
||||
$this->begin = $begin;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DateTime
|
||||
*/
|
||||
public function getEnd(): ?DateTime
|
||||
{
|
||||
return $this->end;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime $end
|
||||
* @return DateRange
|
||||
*/
|
||||
public function setEnd(DateTime $end)
|
||||
{
|
||||
$this->end = $end;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ namespace App\Form;
|
||||
use App\Entity\Timesheet;
|
||||
use App\Form\Type\ActivityType;
|
||||
use App\Form\Type\CustomerType;
|
||||
use App\Form\Type\DateTimePickerType;
|
||||
use App\Form\Type\DurationType;
|
||||
use App\Form\Type\ProjectType;
|
||||
use App\Form\Type\UserType;
|
||||
@@ -19,7 +20,6 @@ use App\Repository\ActivityRepository;
|
||||
use App\Repository\CustomerRepository;
|
||||
use App\Repository\ProjectRepository;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
@@ -90,13 +90,8 @@ class TimesheetEditForm extends AbstractType
|
||||
}
|
||||
|
||||
if (null === $end || !$options['duration_only']) {
|
||||
$builder->add('begin', DateTimeType::class, [
|
||||
$builder->add('begin', DateTimePickerType::class, [
|
||||
'label' => 'label.begin',
|
||||
'widget' => 'single_text',
|
||||
'html5' => false,
|
||||
'format' => 'yyyy-MM-dd HH:mm',
|
||||
'with_seconds' => false,
|
||||
'attr' => ['autocomplete' => 'off', 'data-datetimepicker' => 'on', 'placeholder' => 'yyyy-MM-dd HH:mm'],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -105,14 +100,8 @@ class TimesheetEditForm extends AbstractType
|
||||
'required' => false,
|
||||
]);
|
||||
} else {
|
||||
$builder->add('end', DateTimeType::class, [
|
||||
$builder->add('end', DateTimePickerType::class, [
|
||||
'label' => 'label.end',
|
||||
'widget' => 'single_text',
|
||||
'required' => false,
|
||||
'html5' => false,
|
||||
'format' => 'yyyy-MM-dd HH:mm',
|
||||
'with_seconds' => false,
|
||||
'attr' => ['autocomplete' => 'off', 'data-datetimepicker' => 'on', 'placeholder' => 'yyyy-MM-dd HH:mm'],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,9 +10,8 @@
|
||||
namespace App\Form\Toolbar;
|
||||
|
||||
use App\Form\Type\ActivityType;
|
||||
use App\Form\Type\BeginDateType;
|
||||
use App\Form\Type\CustomerType;
|
||||
use App\Form\Type\EndDateType;
|
||||
use App\Form\Type\DateRangeType;
|
||||
use App\Form\Type\PageSizeType;
|
||||
use App\Form\Type\ProjectType;
|
||||
use App\Form\Type\UserRoleType;
|
||||
@@ -115,20 +114,11 @@ abstract class AbstractToolbarForm extends AbstractType
|
||||
/**
|
||||
* @param FormBuilderInterface $builder
|
||||
*/
|
||||
protected function addStartDateChoice(FormBuilderInterface $builder)
|
||||
protected function addDateRangeChoice(FormBuilderInterface $builder, $allowEmpty = true)
|
||||
{
|
||||
$builder->add('begin', BeginDateType::class, [
|
||||
'required' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FormBuilderInterface $builder
|
||||
*/
|
||||
protected function addEndDateChoice(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('end', EndDateType::class, [
|
||||
$builder->add('daterange', DateRangeType::class, [
|
||||
'required' => false,
|
||||
'allow_empty' => $allowEmpty,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,7 @@ class InvoiceToolbarForm extends AbstractToolbarForm
|
||||
{
|
||||
$this->addTemplateChoice($builder);
|
||||
$this->addUserChoice($builder);
|
||||
$this->addStartDateChoice($builder);
|
||||
$this->addEndDateChoice($builder);
|
||||
$this->addDateRangeChoice($builder);
|
||||
$this->addCustomerChoice($builder);
|
||||
$this->addProjectChoice($builder);
|
||||
$this->addActivityChoice($builder);
|
||||
|
||||
@@ -24,8 +24,7 @@ class TimesheetAdminToolbarForm extends TimesheetToolbarForm
|
||||
$this->addTimesheetStateChoice($builder);
|
||||
$this->addPageSizeChoice($builder);
|
||||
$this->addUserChoice($builder);
|
||||
$this->addStartDateChoice($builder);
|
||||
$this->addEndDateChoice($builder);
|
||||
$this->addDateRangeChoice($builder);
|
||||
$this->addCustomerChoice($builder);
|
||||
$this->addProjectChoice($builder);
|
||||
$this->addActivityChoice($builder);
|
||||
|
||||
@@ -26,8 +26,7 @@ class TimesheetToolbarForm extends AbstractToolbarForm
|
||||
{
|
||||
$this->addTimesheetStateChoice($builder);
|
||||
$this->addPageSizeChoice($builder);
|
||||
$this->addStartDateChoice($builder);
|
||||
$this->addEndDateChoice($builder);
|
||||
$this->addDateRangeChoice($builder);
|
||||
$this->addCustomerChoice($builder);
|
||||
$this->addProjectChoice($builder);
|
||||
$this->addActivityChoice($builder);
|
||||
|
||||
@@ -9,33 +9,50 @@
|
||||
|
||||
namespace App\Form\Type;
|
||||
|
||||
use App\Utils\LocaleSettings;
|
||||
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.
|
||||
* Custom form field type to display the date input fields.
|
||||
*/
|
||||
class BeginDateType extends AbstractType
|
||||
class DatePickerType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* @var LocaleSettings
|
||||
*/
|
||||
protected $localeSettings;
|
||||
|
||||
/**
|
||||
* @param LocaleSettings $localeSettings
|
||||
*/
|
||||
public function __construct(LocaleSettings $localeSettings)
|
||||
{
|
||||
$this->localeSettings = $localeSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$pickerFormat = $this->localeSettings->getDatePickerFormat();
|
||||
$dateFormat = $this->localeSettings->getDateTypeFormat();
|
||||
|
||||
$resolver->setDefaults([
|
||||
'label' => 'label.begin',
|
||||
'widget' => 'single_text',
|
||||
'html5' => false,
|
||||
'format' => DateType::HTML5_FORMAT,
|
||||
'format' => $dateFormat,
|
||||
'format_picker' => $pickerFormat,
|
||||
]);
|
||||
|
||||
$resolver->setDefault('attr', function (Options $options) {
|
||||
return [
|
||||
'autocomplete' => 'off',
|
||||
'data-datepicker' => 'on',
|
||||
'placeholder' => $options['format'],
|
||||
'data-format' => $options['format_picker'],
|
||||
];
|
||||
});
|
||||
}
|
||||
154
src/Form/Type/DateRangeType.php
Normal file
154
src/Form/Type/DateRangeType.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?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 App\Form\Model\DateRange;
|
||||
use App\Utils\LocaleSettings;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\CallbackTransformer;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Custom form field type to select a date range
|
||||
*/
|
||||
class DateRangeType extends AbstractType
|
||||
{
|
||||
public const DATE_SPACER = ' - ';
|
||||
|
||||
/**
|
||||
* @var LocaleSettings
|
||||
*/
|
||||
protected $localeSettings;
|
||||
|
||||
/**
|
||||
* @param LocaleSettings $localeSettings
|
||||
*/
|
||||
public function __construct(LocaleSettings $localeSettings)
|
||||
{
|
||||
$this->localeSettings = $localeSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$pickerFormat = $this->localeSettings->getDatePickerFormat();
|
||||
$dateFormat = $this->localeSettings->getDateFormat();
|
||||
|
||||
$resolver->setDefaults([
|
||||
'model_timezone' => null,
|
||||
'view_timezone' => null,
|
||||
'label' => 'label.daterange',
|
||||
'format' => $dateFormat,
|
||||
'separator' => self::DATE_SPACER,
|
||||
'format_picker' => $pickerFormat,
|
||||
'allow_empty' => true,
|
||||
]);
|
||||
|
||||
$resolver->setDefault('attr', function (Options $options) {
|
||||
return [
|
||||
'autocomplete' => 'off',
|
||||
'placeholder' => $options['format_picker'] . $options['separator'] . $options['format_picker'],
|
||||
'data-format' => $options['format_picker'],
|
||||
'data-separator' => $options['separator'],
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
protected function formatToPattern(string $format, string $separator)
|
||||
{
|
||||
$pattern = str_replace('d', '[0-9]{2}', $format);
|
||||
$pattern = str_replace('m', '[0-9]{2}', $pattern);
|
||||
$pattern = str_replace('Y', '[0-9]{4}', $pattern);
|
||||
|
||||
return '/^' . $pattern . $separator . $pattern . '$/';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$formatDate = $options['format'];
|
||||
$separator = $options['separator'];
|
||||
$allowEmpty = $options['allow_empty'];
|
||||
$pattern = $this->formatToPattern($formatDate, $separator);
|
||||
|
||||
$builder->addModelTransformer(new CallbackTransformer(
|
||||
function (DateRange $range) use ($formatDate, $separator) {
|
||||
if (null === $range->getBegin()) {
|
||||
return '';
|
||||
}
|
||||
$display = $range->getBegin()->format($formatDate);
|
||||
if (null !== $range->getEnd()) {
|
||||
$display .= $separator . $range->getEnd()->format($formatDate);
|
||||
}
|
||||
|
||||
return $display;
|
||||
},
|
||||
function ($dates) use ($formatDate, $pattern, $separator, $allowEmpty) {
|
||||
$range = new DateRange();
|
||||
|
||||
if (empty($dates) && $allowEmpty) {
|
||||
return $range;
|
||||
}
|
||||
|
||||
if (preg_match($pattern, $dates) !== 1) {
|
||||
throw new TransformationFailedException('Invalid date range given');
|
||||
}
|
||||
|
||||
$values = explode($separator, $dates);
|
||||
|
||||
if (count($values) !== 2) {
|
||||
throw new TransformationFailedException('Invalid date range given');
|
||||
}
|
||||
|
||||
$begin = \DateTime::createFromFormat($formatDate, $values[0]);
|
||||
if ($begin === false) {
|
||||
throw new TransformationFailedException('Invalid begin date given');
|
||||
}
|
||||
$range->setBegin($begin);
|
||||
|
||||
$end = \DateTime::createFromFormat($formatDate, $values[1]);
|
||||
if ($end === false) {
|
||||
throw new TransformationFailedException('Invalid end date given');
|
||||
}
|
||||
$range->setEnd($end);
|
||||
|
||||
if ($begin->getTimestamp() > $end->getTimestamp()) {
|
||||
throw new TransformationFailedException('Begin date must be before end date');
|
||||
}
|
||||
|
||||
return $range;
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return TextType::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBlockPrefix()
|
||||
{
|
||||
return 'daterange';
|
||||
}
|
||||
}
|
||||
70
src/Form/Type/DateTimePickerType.php
Normal file
70
src/Form/Type/DateTimePickerType.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?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 App\Utils\LocaleSettings;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Custom form field type to display the date-time input fields.
|
||||
*/
|
||||
class DateTimePickerType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* @var LocaleSettings
|
||||
*/
|
||||
protected $localeSettings;
|
||||
|
||||
/**
|
||||
* @param LocaleSettings $localeSettings
|
||||
*/
|
||||
public function __construct(LocaleSettings $localeSettings)
|
||||
{
|
||||
$this->localeSettings = $localeSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$dateTimePicker = $this->localeSettings->getDateTimePickerFormat();
|
||||
$dateTimeFormat = $this->localeSettings->getDateTimeTypeFormat();
|
||||
|
||||
$resolver->setDefaults([
|
||||
'label' => 'label.begin',
|
||||
'widget' => 'single_text',
|
||||
'html5' => false,
|
||||
'format' => $dateTimeFormat,
|
||||
'format_picker' => $dateTimePicker,
|
||||
'with_seconds' => false,
|
||||
]);
|
||||
|
||||
$resolver->setDefault('attr', function (Options $options) {
|
||||
return [
|
||||
'data-datetimepicker' => 'on',
|
||||
'autocomplete' => 'off',
|
||||
'placeholder' => $options['format'],
|
||||
'data-format' => $options['format_picker'],
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return DateTimeType::class;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
<?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,
|
||||
]);
|
||||
|
||||
$resolver->setDefault('attr', function (Options $options) {
|
||||
return [
|
||||
'autocomplete' => 'off',
|
||||
'data-datepicker' => 'on',
|
||||
'placeholder' => $options['format'],
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return DateType::class;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user