improved duration and minute selector (#2264)
* do not close modal if form is dirty * deprecated TimesheetConfiguration * inject timezone in form types * cleanup usage of UserDateTimeFactory * allow to configure increment steps for minutes * use 15 minutes step for datetimepicker in project edit form * use rounding rules for increments in minute select for begin and end * allow duration in multi user and admin timesheet forms * make dropdown values configurable
This commit is contained in:
@@ -14,6 +14,7 @@ use App\Entity\Customer;
|
||||
use App\Entity\Project;
|
||||
use App\Form\Type\ActivityType;
|
||||
use App\Form\Type\CustomerType;
|
||||
use App\Form\Type\DescriptionType;
|
||||
use App\Form\Type\ProjectType;
|
||||
use App\Form\Type\TagsType;
|
||||
use App\Repository\ActivityRepository;
|
||||
@@ -22,7 +23,6 @@ use App\Repository\ProjectRepository;
|
||||
use App\Repository\Query\ActivityFormTypeQuery;
|
||||
use App\Repository\Query\CustomerFormTypeQuery;
|
||||
use App\Repository\Query\ProjectFormTypeQuery;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
@@ -136,11 +136,15 @@ trait FormTrait
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.13
|
||||
*/
|
||||
protected function addDescription(FormBuilderInterface $builder)
|
||||
{
|
||||
@trigger_error('FormTrait::addDescription() is deprecated and will be removed with 2.0, use DescriptionType instead', E_USER_DEPRECATED);
|
||||
|
||||
$builder
|
||||
->add('description', TextareaType::class, [
|
||||
'label' => 'label.description',
|
||||
->add('description', DescriptionType::class, [
|
||||
'required' => false,
|
||||
'attr' => [
|
||||
'autofocus' => 'autofocus'
|
||||
|
||||
@@ -44,12 +44,20 @@ class ProjectEditForm extends AbstractType
|
||||
}
|
||||
}
|
||||
|
||||
$dateTimeOptions = [];
|
||||
$dateTimeOptions = [
|
||||
'model_timezone' => $options['timezone'],
|
||||
'view_timezone' => $options['timezone'],
|
||||
];
|
||||
// primarily for API usage, where we cannot use a user/locale specific format
|
||||
if (null !== $options['date_format']) {
|
||||
$dateTimeOptions['format'] = $options['date_format'];
|
||||
}
|
||||
|
||||
$timeIncrement = 1;
|
||||
if ($options['time_increment'] >= 1 && $options['time_increment'] <= 60) {
|
||||
$timeIncrement = $options['time_increment'];
|
||||
}
|
||||
|
||||
$builder
|
||||
->add('name', TextType::class, [
|
||||
'label' => 'label.name',
|
||||
@@ -68,14 +76,17 @@ class ProjectEditForm extends AbstractType
|
||||
->add('orderDate', DateTimePickerType::class, array_merge($dateTimeOptions, [
|
||||
'label' => 'label.orderDate',
|
||||
'required' => false,
|
||||
'time_increment' => $timeIncrement,
|
||||
]))
|
||||
->add('start', DateTimePickerType::class, array_merge($dateTimeOptions, [
|
||||
'label' => 'label.project_start',
|
||||
'required' => false,
|
||||
'time_increment' => $timeIncrement,
|
||||
]))
|
||||
->add('end', DateTimePickerType::class, array_merge($dateTimeOptions, [
|
||||
'label' => 'label.project_end',
|
||||
'required' => false,
|
||||
'time_increment' => $timeIncrement,
|
||||
]))
|
||||
->add('customer', CustomerType::class, [
|
||||
'placeholder' => (null === $id && null === $customer) ? '' : false,
|
||||
@@ -103,6 +114,8 @@ class ProjectEditForm extends AbstractType
|
||||
'currency' => Customer::DEFAULT_CURRENCY,
|
||||
'date_format' => null,
|
||||
'include_budget' => false,
|
||||
'timezone' => date_default_timezone_get(),
|
||||
'time_increment' => 1,
|
||||
'attr' => [
|
||||
'data-form-event' => 'kimai.projectUpdate'
|
||||
],
|
||||
|
||||
@@ -17,7 +17,7 @@ class TimesheetAdminEditForm extends TimesheetEditForm
|
||||
{
|
||||
$options['allow_begin_datetime'] = true;
|
||||
$options['allow_end_datetime'] = true;
|
||||
$options['allow_duration'] = false;
|
||||
$options['allow_duration'] = true;
|
||||
|
||||
parent::buildForm($builder, $options);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace App\Form;
|
||||
|
||||
use App\Entity\Timesheet;
|
||||
use App\Form\Type\DateTimePickerType;
|
||||
use App\Form\Type\DescriptionType;
|
||||
use App\Form\Type\DurationType;
|
||||
use App\Form\Type\FixedRateType;
|
||||
use App\Form\Type\HourlyRateType;
|
||||
@@ -19,7 +20,6 @@ use App\Form\Type\UserType;
|
||||
use App\Form\Type\YesNoType;
|
||||
use App\Repository\CustomerRepository;
|
||||
use App\Repository\ProjectRepository;
|
||||
use App\Timesheet\UserDateTimeFactory;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
@@ -41,21 +41,11 @@ class TimesheetEditForm extends AbstractType
|
||||
* @var ProjectRepository
|
||||
*/
|
||||
private $projects;
|
||||
/**
|
||||
* @var UserDateTimeFactory
|
||||
*/
|
||||
protected $dateTime;
|
||||
|
||||
/**
|
||||
* @param CustomerRepository $customer
|
||||
* @param ProjectRepository $project
|
||||
* @param UserDateTimeFactory $dateTime
|
||||
*/
|
||||
public function __construct(CustomerRepository $customer, ProjectRepository $project, UserDateTimeFactory $dateTime)
|
||||
public function __construct(CustomerRepository $customer, ProjectRepository $project)
|
||||
{
|
||||
$this->customers = $customer;
|
||||
$this->projects = $project;
|
||||
$this->dateTime = $dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +59,7 @@ class TimesheetEditForm extends AbstractType
|
||||
$currency = false;
|
||||
$begin = null;
|
||||
$customerCount = $this->customers->countCustomer(true);
|
||||
$timezone = $this->dateTime->getTimezone()->getName();
|
||||
$timezone = $options['timezone'];
|
||||
$isNew = true;
|
||||
|
||||
if (isset($options['data'])) {
|
||||
@@ -108,23 +98,15 @@ class TimesheetEditForm extends AbstractType
|
||||
}
|
||||
|
||||
if ($options['allow_begin_datetime']) {
|
||||
$this->addBegin($builder, $dateTimeOptions);
|
||||
$this->addBegin($builder, $dateTimeOptions, $options);
|
||||
}
|
||||
|
||||
if ($options['allow_end_datetime']) {
|
||||
$this->addEnd($builder, $dateTimeOptions, $options);
|
||||
}
|
||||
|
||||
if ($options['allow_duration']) {
|
||||
$this->addDuration($builder);
|
||||
} elseif ($options['allow_end_datetime']) {
|
||||
$this->addEnd($builder, $dateTimeOptions);
|
||||
}
|
||||
|
||||
if ($options['allow_begin_datetime'] && $options['allow_end_datetime']) {
|
||||
$builder->add('duration', DurationType::class, [
|
||||
'required' => false,
|
||||
'attr' => [
|
||||
'placeholder' => '00:00',
|
||||
'pattern' => '[0-9]{2,3}:[0-9]{2}'
|
||||
]
|
||||
]);
|
||||
$this->addDuration($builder, $options, (!$options['allow_begin_datetime'] || !$options['allow_end_datetime']), $isNew);
|
||||
}
|
||||
|
||||
if ($this->showCustomer($options, $isNew, $customerCount)) {
|
||||
@@ -133,7 +115,13 @@ class TimesheetEditForm extends AbstractType
|
||||
|
||||
$this->addProject($builder, $isNew, $project, $customer);
|
||||
$this->addActivity($builder, $activity, $project);
|
||||
$this->addDescription($builder);
|
||||
|
||||
$descriptionOptions = ['required' => false];
|
||||
if (!$isNew) {
|
||||
$descriptionOptions['attr'] = ['autofocus' => 'autofocus'];
|
||||
}
|
||||
$builder->add('description', DescriptionType::class, $descriptionOptions);
|
||||
|
||||
$this->addTags($builder);
|
||||
$this->addRates($builder, $currency, $options);
|
||||
$this->addUser($builder, $options);
|
||||
@@ -159,30 +147,58 @@ class TimesheetEditForm extends AbstractType
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function addBegin(FormBuilderInterface $builder, array $dateTimeOptions)
|
||||
protected function addBegin(FormBuilderInterface $builder, array $dateTimeOptions, array $options = [])
|
||||
{
|
||||
if ($options['begin_minutes'] >= 1 && $options['begin_minutes'] <= 60) {
|
||||
$dateTimeOptions['time_increment'] = $options['begin_minutes'];
|
||||
}
|
||||
|
||||
$builder->add('begin', DateTimePickerType::class, array_merge($dateTimeOptions, [
|
||||
'label' => 'label.begin'
|
||||
'label' => 'label.begin',
|
||||
]));
|
||||
}
|
||||
|
||||
protected function addEnd(FormBuilderInterface $builder, array $dateTimeOptions)
|
||||
protected function addEnd(FormBuilderInterface $builder, array $dateTimeOptions, array $options = [])
|
||||
{
|
||||
if ($options['end_minutes'] >= 1 && $options['end_minutes'] <= 60) {
|
||||
$dateTimeOptions['time_increment'] = (int) $options['end_minutes'];
|
||||
}
|
||||
|
||||
$builder->add('end', DateTimePickerType::class, array_merge($dateTimeOptions, [
|
||||
'label' => 'label.end',
|
||||
'required' => false,
|
||||
]));
|
||||
}
|
||||
|
||||
protected function addDuration(FormBuilderInterface $builder)
|
||||
protected function addDuration(FormBuilderInterface $builder, array $options, bool $forceApply = false, bool $autofocus = false)
|
||||
{
|
||||
$builder->add('duration', DurationType::class, [
|
||||
$durationOptions = [
|
||||
'required' => false,
|
||||
'docu_chapter' => 'timesheet.html#duration-format',
|
||||
'attr' => [
|
||||
'placeholder' => '00:00',
|
||||
]
|
||||
]);
|
||||
'placeholder' => '0:00',
|
||||
],
|
||||
];
|
||||
|
||||
if ($autofocus) {
|
||||
$durationOptions['attr']['autofocus'] = 'autofocus';
|
||||
}
|
||||
|
||||
$duration = $options['duration_minutes'];
|
||||
if ($duration !== null && (int) $duration > 0) {
|
||||
$durationOptions = array_merge($durationOptions, [
|
||||
'preset_minutes' => $duration
|
||||
]);
|
||||
}
|
||||
|
||||
$duration = $options['duration_hours'];
|
||||
if ($duration !== null && (int) $duration > 0) {
|
||||
$durationOptions = array_merge($durationOptions, [
|
||||
'preset_hours' => $duration,
|
||||
]);
|
||||
}
|
||||
|
||||
$builder->add('duration', DurationType::class, $durationOptions);
|
||||
|
||||
$builder->addEventListener(
|
||||
FormEvents::POST_SET_DATA,
|
||||
@@ -198,16 +214,17 @@ class TimesheetEditForm extends AbstractType
|
||||
// make sure that duration is mapped back to end field
|
||||
$builder->addEventListener(
|
||||
FormEvents::SUBMIT,
|
||||
function (FormEvent $event) {
|
||||
function (FormEvent $event) use ($forceApply) {
|
||||
/** @var Timesheet $data */
|
||||
$data = $event->getData();
|
||||
$duration = $data->getDuration();
|
||||
$end = null;
|
||||
if (null !== $duration) {
|
||||
// only apply the duration, if the end is not yet set
|
||||
// without that check, the end would be overwritten and the real end time would be lost
|
||||
if (($forceApply && null !== $duration) || (null !== $duration && null === $data->getEnd())) {
|
||||
$end = clone $data->getBegin();
|
||||
$end->modify('+ ' . $duration . 'seconds');
|
||||
$data->setEnd($end);
|
||||
}
|
||||
$data->setEnd($end);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -263,10 +280,15 @@ class TimesheetEditForm extends AbstractType
|
||||
'docu_chapter' => 'timesheet.html',
|
||||
'method' => 'POST',
|
||||
'date_format' => null,
|
||||
'timezone' => date_default_timezone_get(),
|
||||
'customer' => false, // for API usage
|
||||
'allow_begin_datetime' => true,
|
||||
'allow_end_datetime' => true,
|
||||
'allow_duration' => false,
|
||||
'duration_minutes' => null,
|
||||
'duration_hours' => 10,
|
||||
'begin_minutes' => 1,
|
||||
'end_minutes' => 1,
|
||||
'attr' => [
|
||||
'data-form-event' => 'kimai.timesheetUpdate',
|
||||
'data-msg-success' => 'action.update.success',
|
||||
|
||||
@@ -20,7 +20,6 @@ class TimesheetMultiUserEditForm extends TimesheetAdminEditForm
|
||||
{
|
||||
$options['allow_begin_datetime'] = true;
|
||||
$options['allow_end_datetime'] = true;
|
||||
$options['allow_duration'] = false;
|
||||
$options['include_user'] = false;
|
||||
|
||||
parent::buildForm($builder, $options);
|
||||
|
||||
@@ -32,7 +32,7 @@ class ExportToolbarForm extends AbstractToolbarForm
|
||||
if ($options['include_user']) {
|
||||
$this->addUsersChoice($builder);
|
||||
}
|
||||
$this->addDateRangeChoice($builder);
|
||||
$this->addDateRange($builder, ['timezone' => $options['timezone']]);
|
||||
$this->addCustomerMultiChoice($builder, ['start_date_param' => null, 'end_date_param' => null, 'ignore_date' => true], true);
|
||||
$this->addProjectMultiChoice($builder, ['ignore_date' => true], true, true);
|
||||
$this->addActivityMultiChoice($builder, [], true);
|
||||
@@ -64,6 +64,7 @@ class ExportToolbarForm extends AbstractToolbarForm
|
||||
'data_class' => ExportQuery::class,
|
||||
'csrf_protection' => false,
|
||||
'include_user' => true,
|
||||
'timezone' => date_default_timezone_get(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class InvoiceToolbarSimpleForm extends AbstractToolbarForm
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$this->addTemplateChoice($builder);
|
||||
$this->addDateRangeChoice($builder);
|
||||
$this->addDateRange($builder, ['timezone' => $options['timezone']]);
|
||||
$this->addCustomerChoice($builder, ['required' => true, 'start_date_param' => null, 'end_date_param' => null, 'ignore_date' => true, 'placeholder' => ''], true);
|
||||
$this->addProjectMultiChoice($builder, ['ignore_date' => true], false, true);
|
||||
$builder->add('markAsExported', CheckboxType::class, [
|
||||
@@ -63,6 +63,7 @@ class InvoiceToolbarSimpleForm extends AbstractToolbarForm
|
||||
'data_class' => InvoiceQuery::class,
|
||||
'csrf_protection' => false,
|
||||
'include_user' => true,
|
||||
'timezone' => date_default_timezone_get(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class TimesheetToolbarForm extends AbstractToolbarForm
|
||||
}
|
||||
|
||||
$this->addSearchTermInputField($builder);
|
||||
$this->addDateRangeChoice($builder);
|
||||
$this->addDateRange($builder, ['timezone' => $options['timezone']]);
|
||||
$this->addCustomerMultiChoice($builder, $newOptions, true);
|
||||
$this->addProjectMultiChoice($builder, $newOptions, true, true);
|
||||
$this->addActivityMultiChoice($builder, [], true);
|
||||
@@ -55,6 +55,7 @@ class TimesheetToolbarForm extends AbstractToolbarForm
|
||||
'csrf_protection' => false,
|
||||
'include_user' => false,
|
||||
'ignore_date' => true,
|
||||
'timezone' => date_default_timezone_get(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
namespace App\Form\Type;
|
||||
|
||||
use App\Timesheet\UserDateTimeFactory;
|
||||
use App\Utils\LocaleSettings;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||
@@ -23,12 +22,10 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
class DatePickerType extends AbstractType
|
||||
{
|
||||
private $localeSettings;
|
||||
private $dateTime;
|
||||
|
||||
public function __construct(LocaleSettings $localeSettings, UserDateTimeFactory $dateTime)
|
||||
public function __construct(LocaleSettings $localeSettings)
|
||||
{
|
||||
$this->localeSettings = $localeSettings;
|
||||
$this->dateTime = $dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,15 +35,14 @@ class DatePickerType extends AbstractType
|
||||
{
|
||||
$pickerFormat = $this->localeSettings->getDatePickerFormat();
|
||||
$dateFormat = $this->localeSettings->getDateTypeFormat();
|
||||
$timezone = $this->dateTime->getTimezone()->getName();
|
||||
|
||||
$resolver->setDefaults([
|
||||
'widget' => 'single_text',
|
||||
'html5' => false,
|
||||
'format' => $dateFormat,
|
||||
'format_picker' => $pickerFormat,
|
||||
'model_timezone' => $timezone,
|
||||
'view_timezone' => $timezone,
|
||||
'model_timezone' => date_default_timezone_get(),
|
||||
'view_timezone' => date_default_timezone_get(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
namespace App\Form\Type;
|
||||
|
||||
use App\Form\Model\DateRange;
|
||||
use App\Timesheet\UserDateTimeFactory;
|
||||
use App\Utils\LocaleSettings;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\CallbackTransformer;
|
||||
@@ -28,19 +27,11 @@ class DateRangeType extends AbstractType
|
||||
{
|
||||
public const DATE_SPACER = ' - ';
|
||||
|
||||
/**
|
||||
* @var LocaleSettings
|
||||
*/
|
||||
private $localeSettings;
|
||||
/**
|
||||
* @var UserDateTimeFactory
|
||||
*/
|
||||
private $dateFactory;
|
||||
|
||||
public function __construct(LocaleSettings $localeSettings, UserDateTimeFactory $dateTime)
|
||||
public function __construct(LocaleSettings $localeSettings)
|
||||
{
|
||||
$this->localeSettings = $localeSettings;
|
||||
$this->dateFactory = $dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,8 +117,7 @@ class DateRangeType extends AbstractType
|
||||
$formatDate = $options['format'];
|
||||
$separator = $options['separator'];
|
||||
$allowEmpty = $options['allow_empty'];
|
||||
//$timezone = new \DateTimeZone($options['timezone']);
|
||||
$timezone = $this->dateFactory->getTimezone();
|
||||
$timezone = new \DateTimeZone($options['timezone']);
|
||||
$pattern = $this->formatToPattern($formatDate, $separator);
|
||||
|
||||
$builder->addModelTransformer(new CallbackTransformer(
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
namespace App\Form\Type;
|
||||
|
||||
use App\API\BaseApiController;
|
||||
use App\Timesheet\UserDateTimeFactory;
|
||||
use App\Utils\LocaleSettings;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
|
||||
@@ -23,20 +22,11 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
*/
|
||||
class DateTimePickerType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* @var LocaleSettings
|
||||
*/
|
||||
protected $localeSettings;
|
||||
private $localeSettings;
|
||||
|
||||
/**
|
||||
* @var UserDateTimeFactory
|
||||
*/
|
||||
protected $dateTime;
|
||||
|
||||
public function __construct(LocaleSettings $localeSettings, UserDateTimeFactory $dateTime)
|
||||
public function __construct(LocaleSettings $localeSettings)
|
||||
{
|
||||
$this->localeSettings = $localeSettings;
|
||||
$this->dateTime = $dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,7 +36,6 @@ class DateTimePickerType extends AbstractType
|
||||
{
|
||||
$dateTimePicker = $this->localeSettings->getDateTimePickerFormat();
|
||||
$dateTimeFormat = $this->localeSettings->getDateTimeTypeFormat();
|
||||
$timezone = $this->dateTime->getTimezone()->getName();
|
||||
|
||||
$resolver->setDefaults([
|
||||
'documentation' => [
|
||||
@@ -60,8 +49,7 @@ class DateTimePickerType extends AbstractType
|
||||
'format' => $dateTimeFormat,
|
||||
'format_picker' => $dateTimePicker,
|
||||
'with_seconds' => false,
|
||||
'model_timezone' => $timezone,
|
||||
'view_timezone' => $timezone,
|
||||
'time_increment' => 1,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -72,6 +60,7 @@ class DateTimePickerType extends AbstractType
|
||||
'autocomplete' => 'off',
|
||||
'placeholder' => strtoupper($options['format']),
|
||||
'data-format' => $options['format_picker'],
|
||||
'data-time-picker-increment' => $options['time_increment'],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
35
src/Form/Type/DescriptionType.php
Normal file
35
src/Form/Type/DescriptionType.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class DescriptionType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'label' => 'label.description',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return TextareaType::class;
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,8 @@ use App\Validator\Constraints\Duration as DurationConstraint;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
@@ -29,9 +31,37 @@ class DurationType extends AbstractType
|
||||
$resolver->setDefaults([
|
||||
'label' => 'label.duration',
|
||||
'constraints' => [new DurationConstraint()],
|
||||
'preset_hours' => null,
|
||||
'preset_minutes' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
if ($options['preset_hours'] === null || $options['preset_minutes'] === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$intervalMinutes = (int) $options['preset_minutes'];
|
||||
$maxHours = (int) $options['preset_hours'];
|
||||
|
||||
if ($intervalMinutes < 1 || $maxHours < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
$maxMinutes = $maxHours * 60;
|
||||
$presets = [];
|
||||
|
||||
for ($minutes = $intervalMinutes; $minutes <= $maxMinutes; $minutes += $intervalMinutes) {
|
||||
$h = (int) ($minutes / 60);
|
||||
$m = $minutes % 60;
|
||||
$interval = new \DateInterval('PT' . $h . 'H' . $m . 'M');
|
||||
$presets[] = $interval->format('%h:%I');
|
||||
}
|
||||
|
||||
$view->vars['duration_presets'] = $presets;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
||||
64
src/Form/Type/MinuteIncrementType.php
Normal file
64
src/Form/Type/MinuteIncrementType.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?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\ChoiceType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Custom form field type to select the minute increment.
|
||||
*/
|
||||
class MinuteIncrementType extends AbstractType
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'deactivate' => true,
|
||||
]);
|
||||
|
||||
$resolver->setDefault('choices', function (Options $options) {
|
||||
$choices = ['increment_rounding' => null];
|
||||
|
||||
if ($options['deactivate']) {
|
||||
$choices['off'] = '0';
|
||||
}
|
||||
|
||||
$choices['1'] = '1';
|
||||
$choices['2'] = '2';
|
||||
$choices['3'] = '3';
|
||||
$choices['4'] = '4';
|
||||
$choices['5'] = '5';
|
||||
$choices['10'] = '10';
|
||||
$choices['15'] = '15';
|
||||
$choices['20'] = '20';
|
||||
$choices['25'] = '25';
|
||||
$choices['30'] = '30';
|
||||
$choices['45'] = '45';
|
||||
$choices['60'] = '60';
|
||||
$choices['90'] = '90';
|
||||
$choices['120'] = '120';
|
||||
|
||||
return $choices;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return ChoiceType::class;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user