mirror of
				https://gitlab.com/Chill-Projet/chill-bundles.git
				synced 2025-11-04 11:18:25 +00:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			454-evalua
			...
			449-scope-
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 9db1a960db | 
							
								
								
									
										6
									
								
								.changes/unreleased/UX-20251030-153126.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/UX-20251030-153126.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
kind: UX
 | 
			
		||||
body: Remove the label if there is only one scope and no scope picking field is displayed.
 | 
			
		||||
time: 2025-10-30T15:31:26.807444365+01:00
 | 
			
		||||
custom:
 | 
			
		||||
    Issue: "449"
 | 
			
		||||
    SchemaChange: No schema change
 | 
			
		||||
@@ -1,6 +0,0 @@
 | 
			
		||||
kind: UX
 | 
			
		||||
body: Expand timeSpent choices for evaluation document and translate them to user locale or fallback 'fr'
 | 
			
		||||
time: 2025-10-30T18:09:19.373907522+01:00
 | 
			
		||||
custom:
 | 
			
		||||
    Issue: ""
 | 
			
		||||
    SchemaChange: No schema change
 | 
			
		||||
@@ -18,6 +18,7 @@ use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
 | 
			
		||||
use Chill\DocStoreBundle\Form\CollectionStoredObjectType;
 | 
			
		||||
use Chill\MainBundle\Entity\Center;
 | 
			
		||||
use Chill\MainBundle\Entity\Location;
 | 
			
		||||
use Chill\MainBundle\Entity\Scope;
 | 
			
		||||
use Chill\MainBundle\Entity\User;
 | 
			
		||||
use Chill\MainBundle\Form\Type\ChillDateType;
 | 
			
		||||
use Chill\MainBundle\Form\Type\CommentType;
 | 
			
		||||
@@ -47,6 +48,7 @@ use Symfony\Component\Form\FormEvent;
 | 
			
		||||
use Symfony\Component\Form\FormEvents;
 | 
			
		||||
use Symfony\Component\OptionsResolver\OptionsResolver;
 | 
			
		||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
 | 
			
		||||
use Symfony\Component\Security\Core\Security;
 | 
			
		||||
 | 
			
		||||
class ActivityType extends AbstractType
 | 
			
		||||
{
 | 
			
		||||
@@ -60,6 +62,7 @@ class ActivityType extends AbstractType
 | 
			
		||||
        protected array $timeChoices,
 | 
			
		||||
        protected SocialIssueRender $socialIssueRender,
 | 
			
		||||
        protected SocialActionRender $socialActionRender,
 | 
			
		||||
        private readonly Security $security,
 | 
			
		||||
    ) {
 | 
			
		||||
        if (!$tokenStorage->getToken()->getUser() instanceof User) {
 | 
			
		||||
            throw new \RuntimeException('you should have a valid user');
 | 
			
		||||
@@ -87,10 +90,22 @@ class ActivityType extends AbstractType
 | 
			
		||||
        $activityType = $options['activityType'];
 | 
			
		||||
 | 
			
		||||
        if (null !== $options['data']->getPerson()) {
 | 
			
		||||
 | 
			
		||||
            $reachableScopes = array_values(
 | 
			
		||||
                array_filter(
 | 
			
		||||
                    $this->authorizationHelper->getReachableScopes(
 | 
			
		||||
                        $this->security->getUser(),
 | 
			
		||||
                        ActivityVoter::CREATE === (string) $options['role'] ? ActivityVoter::CREATE_PERSON : (string) $options['role'],
 | 
			
		||||
                        $options['center']
 | 
			
		||||
                    ),
 | 
			
		||||
                    static fn (Scope $s) => $s->isActive()
 | 
			
		||||
                )
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            $builder->add('scope', ScopePickerType::class, [
 | 
			
		||||
                'center' => $options['center'],
 | 
			
		||||
                'role' => ActivityVoter::CREATE === (string) $options['role'] ? ActivityVoter::CREATE_PERSON : (string) $options['role'],
 | 
			
		||||
                'reachable_scopes' => $reachableScopes,
 | 
			
		||||
                'required' => true,
 | 
			
		||||
                'label' => count($reachableScopes) > 1 ? 'Scope' : false,
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,9 +14,11 @@ namespace Chill\DocStoreBundle\Form;
 | 
			
		||||
use Chill\DocStoreBundle\Entity\Document;
 | 
			
		||||
use Chill\DocStoreBundle\Entity\DocumentCategory;
 | 
			
		||||
use Chill\DocStoreBundle\Entity\PersonDocument;
 | 
			
		||||
use Chill\MainBundle\Entity\Scope;
 | 
			
		||||
use Chill\MainBundle\Form\Type\ChillDateType;
 | 
			
		||||
use Chill\MainBundle\Form\Type\ChillTextareaType;
 | 
			
		||||
use Chill\MainBundle\Form\Type\ScopePickerType;
 | 
			
		||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
 | 
			
		||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcher;
 | 
			
		||||
use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher;
 | 
			
		||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
 | 
			
		||||
@@ -27,10 +29,11 @@ use Symfony\Component\Form\AbstractType;
 | 
			
		||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
 | 
			
		||||
use Symfony\Component\Form\FormBuilderInterface;
 | 
			
		||||
use Symfony\Component\OptionsResolver\OptionsResolver;
 | 
			
		||||
use Symfony\Component\Security\Core\Security;
 | 
			
		||||
 | 
			
		||||
class PersonDocumentType extends AbstractType
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(private readonly TranslatableStringHelperInterface $translatableStringHelper, private readonly ScopeResolverDispatcher $scopeResolverDispatcher, private readonly ParameterBagInterface $parameterBag, private readonly CenterResolverDispatcher $centerResolverDispatcher) {}
 | 
			
		||||
    public function __construct(private readonly TranslatableStringHelperInterface $translatableStringHelper, private readonly Security $security, private readonly AuthorizationHelperInterface $authorizationHelper, private readonly ScopeResolverDispatcher $scopeResolverDispatcher, private readonly ParameterBagInterface $parameterBag, private readonly CenterResolverDispatcher $centerResolverDispatcher) {}
 | 
			
		||||
 | 
			
		||||
    public function buildForm(FormBuilderInterface $builder, array $options)
 | 
			
		||||
    {
 | 
			
		||||
@@ -56,9 +59,19 @@ class PersonDocumentType extends AbstractType
 | 
			
		||||
            ]);
 | 
			
		||||
 | 
			
		||||
        if ($isScopeConcerned && $this->parameterBag->get('chill_main')['acl']['form_show_scopes']) {
 | 
			
		||||
            $reachableScopes = array_values(
 | 
			
		||||
                array_filter(
 | 
			
		||||
                    $this->authorizationHelper->getReachableScopes(
 | 
			
		||||
                        $this->security->getUser(),
 | 
			
		||||
                        $options['role'],
 | 
			
		||||
                        $this->centerResolverDispatcher->resolveCenter($document)
 | 
			
		||||
                    ),
 | 
			
		||||
                    static fn (Scope $s) => $s->isActive()
 | 
			
		||||
                )
 | 
			
		||||
            );
 | 
			
		||||
            $builder->add('scope', ScopePickerType::class, [
 | 
			
		||||
                'center' => $this->centerResolverDispatcher->resolveCenter($document),
 | 
			
		||||
                'role' => $options['role'],
 | 
			
		||||
                'reachable_scopes' => $reachableScopes,
 | 
			
		||||
                'label' => count($reachableScopes) > 1 ? 'Scope' : false,
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -40,14 +40,12 @@ use Symfony\Component\Security\Core\Security;
 | 
			
		||||
class ScopePickerType extends AbstractType
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private readonly AuthorizationHelperInterface $authorizationHelper,
 | 
			
		||||
        private readonly Security $security,
 | 
			
		||||
        private readonly TranslatableStringHelperInterface $translatableStringHelper,
 | 
			
		||||
    ) {}
 | 
			
		||||
 | 
			
		||||
    public function buildForm(FormBuilderInterface $builder, array $options)
 | 
			
		||||
    {
 | 
			
		||||
        $items = array_values(
 | 
			
		||||
        /*        $items = array_values(
 | 
			
		||||
                    array_filter(
 | 
			
		||||
                        $this->authorizationHelper->getReachableScopes(
 | 
			
		||||
                            $this->security->getUser(),
 | 
			
		||||
@@ -56,25 +54,25 @@ class ScopePickerType extends AbstractType
 | 
			
		||||
                        ),
 | 
			
		||||
                        static fn (Scope $s) => $s->isActive()
 | 
			
		||||
                    )
 | 
			
		||||
        );
 | 
			
		||||
                );*/
 | 
			
		||||
 | 
			
		||||
        if (0 === \count($items)) {
 | 
			
		||||
        if (0 === \count($options['reachable_scopes'])) {
 | 
			
		||||
            throw new \RuntimeException('no scopes are reachable. This form should not be shown to user');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (1 !== \count($items)) {
 | 
			
		||||
        if (1 !== \count($options['reachable_scopes'])) {
 | 
			
		||||
            $builder->add('scope', EntityType::class, [
 | 
			
		||||
                'class' => Scope::class,
 | 
			
		||||
                'placeholder' => 'Choose the circle',
 | 
			
		||||
                'choice_label' => fn (Scope $c) => $this->translatableStringHelper->localize($c->getName()),
 | 
			
		||||
                'choices' => $items,
 | 
			
		||||
                'choices' => $options['reachable_scopes'],
 | 
			
		||||
            ]);
 | 
			
		||||
            $builder->setDataMapper(new ScopePickerDataMapper());
 | 
			
		||||
        } else {
 | 
			
		||||
            $builder->add('scope', HiddenType::class, [
 | 
			
		||||
                'data' => $items[0]->getId(),
 | 
			
		||||
                'data' => $options['reachable_scopes'][0]->getId(),
 | 
			
		||||
            ]);
 | 
			
		||||
            $builder->setDataMapper(new ScopePickerDataMapper($items[0]));
 | 
			
		||||
            $builder->setDataMapper(new ScopePickerDataMapper($options['reachable_scopes'][0]));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -86,11 +84,13 @@ class ScopePickerType extends AbstractType
 | 
			
		||||
    public function configureOptions(OptionsResolver $resolver)
 | 
			
		||||
    {
 | 
			
		||||
        $resolver
 | 
			
		||||
            ->setRequired('reachable_scopes')
 | 
			
		||||
            ->setAllowedTypes('reachable_scopes', ['array']);
 | 
			
		||||
        // create `center` option
 | 
			
		||||
            ->setRequired('center')
 | 
			
		||||
            ->setAllowedTypes('center', [Center::class, 'array', 'null'])
 | 
			
		||||
        //            ->setRequired('center')
 | 
			
		||||
        //            ->setAllowedTypes('center', [Center::class, 'array', 'null'])
 | 
			
		||||
        // create ``role` option
 | 
			
		||||
            ->setRequired('role')
 | 
			
		||||
            ->setAllowedTypes('role', ['string']);
 | 
			
		||||
        //            ->setRequired('role')
 | 
			
		||||
        //            ->setAllowedTypes('role', ['string']);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,6 @@ declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Form\Type;
 | 
			
		||||
 | 
			
		||||
use Chill\MainBundle\Entity\Center;
 | 
			
		||||
use Chill\MainBundle\Entity\Scope;
 | 
			
		||||
use Chill\MainBundle\Entity\User;
 | 
			
		||||
use Chill\MainBundle\Form\Type\ScopePickerType;
 | 
			
		||||
@@ -42,8 +41,7 @@ final class ScopePickerTypeTest extends TypeTestCase
 | 
			
		||||
    public function estBuildOneScopeIsSuccessful()
 | 
			
		||||
    {
 | 
			
		||||
        $form = $this->factory->create(ScopePickerType::class, null, [
 | 
			
		||||
            'center' => new Center(),
 | 
			
		||||
            'role' => 'ONE_SCOPE',
 | 
			
		||||
            'reachable_scopes' => [new Scope()],
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        $view = $form->createView();
 | 
			
		||||
@@ -54,8 +52,7 @@ final class ScopePickerTypeTest extends TypeTestCase
 | 
			
		||||
    public function testBuildThreeScopesIsSuccessful()
 | 
			
		||||
    {
 | 
			
		||||
        $form = $this->factory->create(ScopePickerType::class, null, [
 | 
			
		||||
            'center' => new Center(),
 | 
			
		||||
            'role' => 'THREE_SCOPE',
 | 
			
		||||
            'reachable_scopes' => [new Scope(), new Scope(), new Scope()],
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        $view = $form->createView();
 | 
			
		||||
@@ -66,8 +63,7 @@ final class ScopePickerTypeTest extends TypeTestCase
 | 
			
		||||
    public function testBuildTwoScopesIsSuccessful()
 | 
			
		||||
    {
 | 
			
		||||
        $form = $this->factory->create(ScopePickerType::class, null, [
 | 
			
		||||
            'center' => new Center(),
 | 
			
		||||
            'role' => 'TWO_SCOPE',
 | 
			
		||||
            'reachable_scopes' => [new Scope(), new Scope()],
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        $view = $form->createView();
 | 
			
		||||
 
 | 
			
		||||
@@ -127,20 +127,6 @@ duration:
 | 
			
		||||
            few {# minutes}
 | 
			
		||||
            other {# minutes}
 | 
			
		||||
        }
 | 
			
		||||
    hour: >-
 | 
			
		||||
        {h, plural,
 | 
			
		||||
            =0 {Aucune durée}
 | 
			
		||||
            one {# heure}
 | 
			
		||||
            few {# heures}
 | 
			
		||||
            other {# heures}
 | 
			
		||||
        }
 | 
			
		||||
    day: >-
 | 
			
		||||
        {d, plural,
 | 
			
		||||
            =0 {Aucune durée}
 | 
			
		||||
            one {# jour}
 | 
			
		||||
            few {# jours}
 | 
			
		||||
            other {# jours}
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
filter_order:
 | 
			
		||||
    by_date:
 | 
			
		||||
 
 | 
			
		||||
@@ -67,117 +67,37 @@ const store = useStore();
 | 
			
		||||
 | 
			
		||||
const $toast = useToast();
 | 
			
		||||
 | 
			
		||||
const timeSpentValues = [
 | 
			
		||||
    60,
 | 
			
		||||
    120,
 | 
			
		||||
    180,
 | 
			
		||||
    240,
 | 
			
		||||
    300,
 | 
			
		||||
    600,
 | 
			
		||||
    900,
 | 
			
		||||
    1200,
 | 
			
		||||
    1500,
 | 
			
		||||
    1800,
 | 
			
		||||
    2700,
 | 
			
		||||
    3600,
 | 
			
		||||
    4500,
 | 
			
		||||
    5400,
 | 
			
		||||
    6300,
 | 
			
		||||
    7200,
 | 
			
		||||
    9000,
 | 
			
		||||
    10800,
 | 
			
		||||
    12600,
 | 
			
		||||
    14400,
 | 
			
		||||
    16200,
 | 
			
		||||
    18000,
 | 
			
		||||
    19800,
 | 
			
		||||
    21600,
 | 
			
		||||
    23400,
 | 
			
		||||
    25200,
 | 
			
		||||
    27000,
 | 
			
		||||
    28800,
 | 
			
		||||
    43200,
 | 
			
		||||
    57600,
 | 
			
		||||
    72000,
 | 
			
		||||
    86400,
 | 
			
		||||
    100800,
 | 
			
		||||
    115200,
 | 
			
		||||
    129600,
 | 
			
		||||
    144000, // goes from 1 minute to 40 hours
 | 
			
		||||
const timeSpentChoices = [
 | 
			
		||||
    { text: "1 minute", value: 60 },
 | 
			
		||||
    { text: "2 minutes", value: 120 },
 | 
			
		||||
    { text: "3 minutes", value: 180 },
 | 
			
		||||
    { text: "4 minutes", value: 240 },
 | 
			
		||||
    { text: "5 minutes", value: 300 },
 | 
			
		||||
    { text: "10 minutes", value: 600 },
 | 
			
		||||
    { text: "15 minutes", value: 900 },
 | 
			
		||||
    { text: "20 minutes", value: 1200 },
 | 
			
		||||
    { text: "25 minutes", value: 1500 },
 | 
			
		||||
    { text: "30 minutes", value: 1800 },
 | 
			
		||||
    { text: "45 minutes", value: 2700 },
 | 
			
		||||
    { text: "1 hour", value: 3600 },
 | 
			
		||||
    { text: "1 hour 15 minutes", value: 4500 },
 | 
			
		||||
    { text: "1 hour 30 minutes", value: 5400 },
 | 
			
		||||
    { text: "1 hour 45 minutes", value: 6300 },
 | 
			
		||||
    { text: "2 hours", value: 7200 },
 | 
			
		||||
    { text: "2 hours 30 minutes", value: 9000 },
 | 
			
		||||
    { text: "3 hours", value: 10800 },
 | 
			
		||||
    { text: "3 hours 30 minutes", value: 12600 },
 | 
			
		||||
    { text: "4 hours", value: 14400 },
 | 
			
		||||
    { text: "4 hours 30 minutes", value: 16200 },
 | 
			
		||||
    { text: "5 hours", value: 18000 },
 | 
			
		||||
    { text: "5 hours 30 minutes", value: 19800 },
 | 
			
		||||
    { text: "6 hours", value: 21600 },
 | 
			
		||||
    { text: "6 hours 30 minutes", value: 23400 },
 | 
			
		||||
    { text: "7 hours", value: 25200 },
 | 
			
		||||
    { text: "7 hours 30 minutes", value: 27000 },
 | 
			
		||||
    { text: "8 hours", value: 28800 },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const formatDuration = (seconds, locale) => {
 | 
			
		||||
    const currentLocale = locale || navigator.language || "fr";
 | 
			
		||||
 | 
			
		||||
    const totalHours = Math.floor(seconds / 3600);
 | 
			
		||||
    const remainingMinutes = Math.floor((seconds % 3600) / 60);
 | 
			
		||||
 | 
			
		||||
    if (totalHours >= 8) {
 | 
			
		||||
        const days = Math.floor(totalHours / 8);
 | 
			
		||||
        const remainingHours = totalHours % 8;
 | 
			
		||||
 | 
			
		||||
        const parts = [];
 | 
			
		||||
 | 
			
		||||
        if (days > 0) {
 | 
			
		||||
            parts.push(
 | 
			
		||||
                new Intl.NumberFormat(currentLocale, {
 | 
			
		||||
                    style: "unit",
 | 
			
		||||
                    unit: "day",
 | 
			
		||||
                    unitDisplay: "long",
 | 
			
		||||
                }).format(days),
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (remainingHours > 0) {
 | 
			
		||||
            parts.push(
 | 
			
		||||
                new Intl.NumberFormat(currentLocale, {
 | 
			
		||||
                    style: "unit",
 | 
			
		||||
                    unit: "hour",
 | 
			
		||||
                    unitDisplay: "long",
 | 
			
		||||
                }).format(remainingHours),
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return parts.join(" ");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // For less than 8 hours, use hour and minute format
 | 
			
		||||
    const parts = [];
 | 
			
		||||
 | 
			
		||||
    if (totalHours > 0) {
 | 
			
		||||
        parts.push(
 | 
			
		||||
            new Intl.NumberFormat(currentLocale, {
 | 
			
		||||
                style: "unit",
 | 
			
		||||
                unit: "hour",
 | 
			
		||||
                unitDisplay: "long",
 | 
			
		||||
            }).format(totalHours),
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (remainingMinutes > 0) {
 | 
			
		||||
        parts.push(
 | 
			
		||||
            new Intl.NumberFormat(currentLocale, {
 | 
			
		||||
                style: "unit",
 | 
			
		||||
                unit: "minute",
 | 
			
		||||
                unitDisplay: "long",
 | 
			
		||||
            }).format(remainingMinutes),
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    console.log(parts);
 | 
			
		||||
    console.log(parts.join(" "));
 | 
			
		||||
 | 
			
		||||
    return parts.join(" ");
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const timeSpentChoices = computed(() => {
 | 
			
		||||
    const locale = "fr";
 | 
			
		||||
    return timeSpentValues.map((value) => ({
 | 
			
		||||
        text: formatDuration(value, locale),
 | 
			
		||||
        value: parseInt(value),
 | 
			
		||||
    }));
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const startDate = computed({
 | 
			
		||||
    get() {
 | 
			
		||||
        return props.evaluation.startDate;
 | 
			
		||||
@@ -274,7 +194,7 @@ function updateWarningInterval(value) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function updateTimeSpent(value) {
 | 
			
		||||
    timeSpent.value = parseInt(value);
 | 
			
		||||
    timeSpent.value = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function updateComment(value) {
 | 
			
		||||
 
 | 
			
		||||
@@ -216,29 +216,9 @@
 | 
			
		||||
 | 
			
		||||
                                    {% if e.timeSpent is not null and e.timeSpent > 0 %}
 | 
			
		||||
                                        <li>
 | 
			
		||||
                                            {% set totalHours = (e.timeSpent / 3600)|round(0, 'floor') %}
 | 
			
		||||
                                            {% set totalMinutes = ((e.timeSpent % 3600) / 60)|round(0, 'floor') %}
 | 
			
		||||
 | 
			
		||||
                                            <span class="item-key">{{ 'accompanying_course_work.timeSpent'|trans ~ ' : ' }}</span>
 | 
			
		||||
 | 
			
		||||
                                            {% if totalHours >= 8 %}
 | 
			
		||||
                                                {% set days = (totalHours / 8)|round(0, 'floor') %}
 | 
			
		||||
                                                {% set remainingHours = totalHours % 8 %}
 | 
			
		||||
 | 
			
		||||
                                                {% if days > 0 %}
 | 
			
		||||
                                                    {{ 'duration.day'|trans({ '{d}' : days }) }}
 | 
			
		||||
                                                {% endif %}
 | 
			
		||||
                                                {% if remainingHours > 0 %}
 | 
			
		||||
                                                    {{ 'duration.hour'|trans({ '{h}' : remainingHours }) }}
 | 
			
		||||
                                                {% endif %}
 | 
			
		||||
                                            {% else %}
 | 
			
		||||
                                                {% if totalHours > 0 %}
 | 
			
		||||
                                                    {{ 'duration.hour'|trans({ '{h}' : totalHours }) }}
 | 
			
		||||
                                                {% endif %}
 | 
			
		||||
                                                {% if totalMinutes > 0 %}
 | 
			
		||||
                                                    {{ 'duration.minute'|trans({ '{m}' : totalMinutes }) }}
 | 
			
		||||
                                                {% endif %}
 | 
			
		||||
                                            {% endif %}
 | 
			
		||||
                                            {% set minutes = (e.timeSpent / 60) %}
 | 
			
		||||
                                            <span
 | 
			
		||||
                                                class="item-key">{{ 'accompanying_course_work.timeSpent'|trans ~ ' : ' }}</span> {{ 'duration.minute'|trans({ '{m}' : minutes }) }}
 | 
			
		||||
                                        </li>
 | 
			
		||||
                                    {% elseif displayContent is defined and displayContent == 'long' %}
 | 
			
		||||
                                        <li>
 | 
			
		||||
 
 | 
			
		||||
@@ -167,10 +167,20 @@ final readonly class PersonContext implements PersonContextInterface
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($this->isScopeNecessary($entity)) {
 | 
			
		||||
            $reachableScopes = array_values(
 | 
			
		||||
                array_filter(
 | 
			
		||||
                    $this->authorizationHelper->getReachableScopes(
 | 
			
		||||
                        $this->security->getUser(),
 | 
			
		||||
                        PersonDocumentVoter::CREATE,
 | 
			
		||||
                        $this->centerResolverManager->resolveCenters($entity)
 | 
			
		||||
                    ),
 | 
			
		||||
                    static fn (Scope $s) => $s->isActive()
 | 
			
		||||
                )
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            $builder->add('scope', ScopePickerType::class, [
 | 
			
		||||
                'center' => $this->centerResolverManager->resolveCenters($entity),
 | 
			
		||||
                'role' => PersonDocumentVoter::CREATE,
 | 
			
		||||
                'label' => 'Scope',
 | 
			
		||||
                'reachable_scopes' => $reachableScopes,
 | 
			
		||||
                'label' => count($reachableScopes) > 1 ? 'Scope' : false,
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -11,11 +11,13 @@ declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Chill\TaskBundle\Form;
 | 
			
		||||
 | 
			
		||||
use Chill\MainBundle\Entity\Scope;
 | 
			
		||||
use Chill\MainBundle\Form\Type\ChillDateType;
 | 
			
		||||
use Chill\MainBundle\Form\Type\ChillTextareaType;
 | 
			
		||||
use Chill\MainBundle\Form\Type\DateIntervalType;
 | 
			
		||||
use Chill\MainBundle\Form\Type\ScopePickerType;
 | 
			
		||||
use Chill\MainBundle\Form\Type\UserPickerType;
 | 
			
		||||
use Chill\MainBundle\Security\Authorization\AuthorizationHelperInterface;
 | 
			
		||||
use Chill\MainBundle\Security\Resolver\CenterResolverDispatcherInterface;
 | 
			
		||||
use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher;
 | 
			
		||||
use Chill\TaskBundle\Security\Authorization\TaskVoter;
 | 
			
		||||
@@ -24,10 +26,18 @@ use Symfony\Component\Form\AbstractType;
 | 
			
		||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
 | 
			
		||||
use Symfony\Component\Form\FormBuilderInterface;
 | 
			
		||||
use Symfony\Component\OptionsResolver\OptionsResolver;
 | 
			
		||||
use Symfony\Component\Security\Core\Security;
 | 
			
		||||
use Symfony\Contracts\Translation\TranslatorInterface;
 | 
			
		||||
 | 
			
		||||
class SingleTaskType extends AbstractType
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(private readonly ParameterBagInterface $parameterBag, private readonly CenterResolverDispatcherInterface $centerResolverDispatcher, private readonly ScopeResolverDispatcher $scopeResolverDispatcher) {}
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private readonly ParameterBagInterface $parameterBag,
 | 
			
		||||
        private readonly CenterResolverDispatcherInterface $centerResolverDispatcher,
 | 
			
		||||
        private readonly Security $security,
 | 
			
		||||
        private readonly AuthorizationHelperInterface $authorizationHelper,
 | 
			
		||||
        private readonly ScopeResolverDispatcher $scopeResolverDispatcher,
 | 
			
		||||
    ) {}
 | 
			
		||||
 | 
			
		||||
    public function buildForm(FormBuilderInterface $builder, array $options)
 | 
			
		||||
    {
 | 
			
		||||
@@ -62,11 +72,22 @@ class SingleTaskType extends AbstractType
 | 
			
		||||
            ]);
 | 
			
		||||
 | 
			
		||||
        if ($isScopeConcerned && $this->parameterBag->get('chill_main')['acl']['form_show_scopes']) {
 | 
			
		||||
            $reachableScopes = array_values(
 | 
			
		||||
                array_filter(
 | 
			
		||||
                    $this->authorizationHelper->getReachableScopes(
 | 
			
		||||
                        $this->security->getUser(),
 | 
			
		||||
                        $options['role'],
 | 
			
		||||
                        $center
 | 
			
		||||
                    ),
 | 
			
		||||
                    static fn (Scope $s) => $s->isActive()
 | 
			
		||||
                )
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            $builder
 | 
			
		||||
                ->add('circle', ScopePickerType::class, [
 | 
			
		||||
                    'center' => $center,
 | 
			
		||||
                    'role' => $options['role'],
 | 
			
		||||
                    'reachable_scopes' => $reachableScopes,
 | 
			
		||||
                    'required' => true,
 | 
			
		||||
                    'label' => count($reachableScopes) > 1 ? 'Scope' : false,
 | 
			
		||||
                ]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user