mirror of
				https://gitlab.com/Chill-Projet/chill-bundles.git
				synced 2025-11-04 03:08:25 +00:00 
			
		
		
		
	Compare commits
	
		
			69 Commits
		
	
	
		
			366-pick-u
			...
			321-text-e
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						b5c9e65986
	
				 | 
					
					
						|||
| 
						
						
							
						
						8b2af35e97
	
				 | 
					
					
						|||
| dc44c46667 | |||
| ba571c1a69 | |||
| 6a364705f2 | |||
| b6d454691a | |||
| 6d7a6932a9 | |||
| 
						 | 
					2faf194b15 | ||
| f207599d86 | |||
| b0959f8cc5 | |||
| 4c5dee5f0a | |||
| f6c98aa0d5 | |||
| 6d13d184d5 | |||
| af36eccfaf | |||
| 483a20a43f | |||
| 6d8e2ad825 | |||
| 86388a63a8 | |||
| 
						 | 
					5ea55ebfe5 | ||
| f97dc8f931 | |||
| 
						 | 
					a9c3aab528 | ||
| 1181377bd6 | |||
| 
						 | 
					2275b7c560 | ||
| 4a8d298ae5 | |||
| 
						
						
							
						
						3e7f03d331
	
				 | 
					
					
						|||
| 
						
						
							
						
						b830952b9e
	
				 | 
					
					
						|||
| ad17313c61 | |||
| 
						
						
							
						
						620515ad15
	
				 | 
					
					
						|||
| 50c377ee22 | |||
| cc7e7a90ee | |||
| 
						
						
							
						
						1d4ef19051
	
				 | 
					
					
						|||
| 
						
						
							
						
						8337a724d1
	
				 | 
					
					
						|||
| 8ca377d5d4 | |||
| 224e0bae43 | |||
| 3aa4fac80d | |||
| 
						 | 
					a7517eb647 | ||
| e278e636e0 | |||
| 
						 | 
					40e373a9c7 | ||
| 1c1f418b18 | |||
| bf0e14b43a | |||
| 
						
						
							
						
						203a098054
	
				 | 
					
					
						|||
| 
						
						
							
						
						d58acff541
	
				 | 
					
					
						|||
| 
						
						
							
						
						5858e05a42
	
				 | 
					
					
						|||
| 
						
						
							
						
						b9b4fafe14
	
				 | 
					
					
						|||
| 2dcce7b826 | |||
| dcd1777a70 | |||
| a6eb28175a | |||
| 7d78512823 | |||
| d0cd4792d6 | |||
| 6d196ead94 | |||
| 4047d5fd5b | |||
| 9aac80d834 | |||
| 7560dc57c6 | |||
| 10314845f6 | |||
| 9b84bc4d69 | |||
| a2fcf039be | |||
| b4d887a372 | |||
| 0aaa7122da | |||
| 1bc7f85874 | |||
| 1d2fd000aa | |||
| 506df432b0 | |||
| c32c18b0e2 | |||
| 321d569ee9 | |||
| cd40eb3932 | |||
| f0f2531fa3 | |||
| 183a220e7b | |||
| 9df127a82c | |||
| 04a1412562 | |||
| 3aef0a185e | |||
| 578bce31b9 | 
							
								
								
									
										6
									
								
								.changes/unreleased/DX-20250430-144550.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/DX-20250430-144550.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
kind: DX
 | 
			
		||||
body: Remove dead code for wopi-link module
 | 
			
		||||
time: 2025-04-30T14:45:50.406111606+02:00
 | 
			
		||||
custom:
 | 
			
		||||
    Issue: "352"
 | 
			
		||||
    SchemaChange: No schema change
 | 
			
		||||
							
								
								
									
										7
									
								
								.changes/unreleased/Feature-20250424-142211.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.changes/unreleased/Feature-20250424-142211.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
kind: Feature
 | 
			
		||||
body: Add the document file name to the document title when a user upload a document,
 | 
			
		||||
  unless there is already a document title.
 | 
			
		||||
time: 2025-04-24T14:22:11.800975422+02:00
 | 
			
		||||
custom:
 | 
			
		||||
  Issue: "377"
 | 
			
		||||
  SchemaChange: No schema change
 | 
			
		||||
							
								
								
									
										6
									
								
								.changes/unreleased/Feature-20250520-095628.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/Feature-20250520-095628.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
kind: Feature
 | 
			
		||||
body: Add desactivation date for social action and issue csv export
 | 
			
		||||
time: 2025-05-20T09:56:28.108941934+02:00
 | 
			
		||||
custom:
 | 
			
		||||
    Issue: ""
 | 
			
		||||
    SchemaChange: No schema change
 | 
			
		||||
							
								
								
									
										7
									
								
								.changes/unreleased/Fixed-20250424-133943.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.changes/unreleased/Fixed-20250424-133943.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
kind: Fixed
 | 
			
		||||
body: trying to prevent bug of typeerror in doc-history + improved display of document
 | 
			
		||||
  history
 | 
			
		||||
time: 2025-04-24T13:39:43.878468232+02:00
 | 
			
		||||
custom:
 | 
			
		||||
  Issue: "376"
 | 
			
		||||
  SchemaChange: No schema change
 | 
			
		||||
							
								
								
									
										7
									
								
								.changes/unreleased/Fixed-20250424-163746.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.changes/unreleased/Fixed-20250424-163746.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
kind: Fixed
 | 
			
		||||
body: Display previous participation in acc course work even if the person has left
 | 
			
		||||
  the acc course
 | 
			
		||||
time: 2025-04-24T16:37:46.970203594+02:00
 | 
			
		||||
custom:
 | 
			
		||||
  Issue: "381"
 | 
			
		||||
  SchemaChange: No schema change
 | 
			
		||||
							
								
								
									
										6
									
								
								.changes/unreleased/Fixed-20250505-102715.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/Fixed-20250505-102715.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
kind: Fixed
 | 
			
		||||
body: Fix display of text in calendar events
 | 
			
		||||
time: 2025-05-05T10:27:15.461493066+02:00
 | 
			
		||||
custom:
 | 
			
		||||
    Issue: "372"
 | 
			
		||||
    SchemaChange: No schema change
 | 
			
		||||
							
								
								
									
										6
									
								
								.changes/unreleased/Fixed-20250514-145339.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/Fixed-20250514-145339.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
kind: Fixed
 | 
			
		||||
body: Add missing translation for user_group.no_user_groups
 | 
			
		||||
time: 2025-05-14T14:53:39.53927329+02:00
 | 
			
		||||
custom:
 | 
			
		||||
    Issue: ""
 | 
			
		||||
    SchemaChange: No schema change
 | 
			
		||||
							
								
								
									
										6
									
								
								.changes/unreleased/UX-20250423-172624.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/UX-20250423-172624.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
kind: UX
 | 
			
		||||
body: Remove default filter in_progress for the page 'my tasks'; Allows for new tasks to be displayed upon opening of the page
 | 
			
		||||
time: 2025-04-23T17:26:24.45777387+02:00
 | 
			
		||||
custom:
 | 
			
		||||
    Issue: "374"
 | 
			
		||||
    SchemaChange: No schema change
 | 
			
		||||
							
								
								
									
										19
									
								
								.changes/v3.11.0.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								.changes/v3.11.0.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
## v3.11.0 - 2025-04-17
 | 
			
		||||
### Feature
 | 
			
		||||
* ([#365](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/365)) Add counters of actions and activities, with 2 boxes to (1) show the number of active actions on total actions and (2) show the number of activities in a accompanying period, and pills in menus for showing the number of active actions and the number of activities.
 | 
			
		||||
* ([#364](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/364)) Added a second phone number "telephone2" to the thirdParty entity. Adapted twig templates and vuejs apps to handle this phone number
 | 
			
		||||
 | 
			
		||||
  **Schema Change**: Add columns or tables
 | 
			
		||||
* Signature: add a button to go directly to the signature zone, even if there is only one
 | 
			
		||||
### Fixed
 | 
			
		||||
* Fixed wrong translations in the on-the-fly for creation of thirdParty
 | 
			
		||||
* Fixed update of phone number in on-the-fly edition of thirdParty
 | 
			
		||||
* Fixed closing of modal when editing thirdParty in accompanying course works
 | 
			
		||||
* Shorten the delay between two execution of AccompanyingPeriodStepChangeCronjob, to ensure at least one execution in a day
 | 
			
		||||
* ([#102](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/102)) Fix display of title in document list
 | 
			
		||||
* When cleaning the old stored object versions, do not throw an error if the stored object is not found on disk
 | 
			
		||||
* Add consistent log prefix and key to logs when stale workflows are automatically canceled
 | 
			
		||||
* ([#380](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/380)) Remove the "not null" validation constraint on recently added properties on HouseholdComposition
 | 
			
		||||
 | 
			
		||||
### DX
 | 
			
		||||
* Add new chill-col style for displaying title and aside in a flex table
 | 
			
		||||
@@ -220,6 +220,7 @@ framework:
 | 
			
		||||
                        - attenteModification
 | 
			
		||||
                        - attenteMiseEnForme
 | 
			
		||||
                        - attenteValidationMiseEnForme
 | 
			
		||||
                        - attenteSignature
 | 
			
		||||
                        - attenteVisa
 | 
			
		||||
                        - postSignature
 | 
			
		||||
                        - attenteTraitement
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@
 | 
			
		||||
    "@hotwired/stimulus": "^3.0.0",
 | 
			
		||||
    "@luminateone/eslint-baseline": "^1.0.9",
 | 
			
		||||
    "@symfony/stimulus-bridge": "^3.2.0",
 | 
			
		||||
    "@symfony/ux-translator": "file:vendor/symfony/ux-translator/assets",
 | 
			
		||||
    "@symfony/webpack-encore": "^4.1.0",
 | 
			
		||||
    "@tsconfig/node20": "^20.1.4",
 | 
			
		||||
    "@types/dompurify": "^3.0.5",
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Chill\ActivityBundle\Menu;
 | 
			
		||||
 | 
			
		||||
use Chill\ActivityBundle\Entity\Activity;
 | 
			
		||||
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
 | 
			
		||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
 | 
			
		||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
 | 
			
		||||
@@ -23,22 +24,30 @@ use Symfony\Contracts\Translation\TranslatorInterface;
 | 
			
		||||
 */
 | 
			
		||||
class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(protected Security $security, protected TranslatorInterface $translator) {}
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        protected Security $security,
 | 
			
		||||
        protected TranslatorInterface $translator,
 | 
			
		||||
        private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry,
 | 
			
		||||
    ) {}
 | 
			
		||||
 | 
			
		||||
    public function buildMenu($menuId, MenuItem $menu, array $parameters)
 | 
			
		||||
    {
 | 
			
		||||
        $period = $parameters['accompanyingCourse'];
 | 
			
		||||
 | 
			
		||||
        $activities = $this->managerRegistry->getManager()->getRepository(Activity::class)->findBy(
 | 
			
		||||
            ['accompanyingPeriod' => $period]
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if (
 | 
			
		||||
            AccompanyingPeriod::STEP_DRAFT !== $period->getStep()
 | 
			
		||||
            && $this->security->isGranted(ActivityVoter::SEE, $period)
 | 
			
		||||
        ) {
 | 
			
		||||
            $menu->addChild($this->translator->trans('Activity'), [
 | 
			
		||||
            $menu->addChild($this->translator->trans('Activities'), [
 | 
			
		||||
                'route' => 'chill_activity_activity_list',
 | 
			
		||||
                'routeParameters' => [
 | 
			
		||||
                    'accompanying_period_id' => $period->getId(),
 | 
			
		||||
                ], ])
 | 
			
		||||
                ->setExtras(['order' => 40]);
 | 
			
		||||
                ->setExtras(['order' => 40, 'counter' => count($activities) > 0 ? count($activities) : null]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Chill\ActivityBundle\Menu;
 | 
			
		||||
 | 
			
		||||
use Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface;
 | 
			
		||||
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
 | 
			
		||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
 | 
			
		||||
use Chill\PersonBundle\Entity\Person;
 | 
			
		||||
@@ -23,13 +24,20 @@ use Symfony\Contracts\Translation\TranslatorInterface;
 | 
			
		||||
 */
 | 
			
		||||
final readonly class PersonMenuBuilder implements LocalMenuBuilderInterface
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(private AuthorizationCheckerInterface $authorizationChecker, private TranslatorInterface $translator) {}
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private readonly ActivityACLAwareRepositoryInterface $activityACLAwareRepository,
 | 
			
		||||
        private AuthorizationCheckerInterface $authorizationChecker,
 | 
			
		||||
        private TranslatorInterface $translator,
 | 
			
		||||
    ) {}
 | 
			
		||||
 | 
			
		||||
    public function buildMenu($menuId, MenuItem $menu, array $parameters)
 | 
			
		||||
    {
 | 
			
		||||
        /** @var Person $person */
 | 
			
		||||
        $person = $parameters['person'];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $count = $this->activityACLAwareRepository->countByPerson($person, ActivityVoter::SEE);
 | 
			
		||||
 | 
			
		||||
        if ($this->authorizationChecker->isGranted(ActivityVoter::SEE, $person)) {
 | 
			
		||||
            $menu->addChild(
 | 
			
		||||
                $this->translator->trans('Activities'),
 | 
			
		||||
@@ -38,7 +46,7 @@ final readonly class PersonMenuBuilder implements LocalMenuBuilderInterface
 | 
			
		||||
                    'routeParameters' => ['person_id' => $person->getId()],
 | 
			
		||||
                ]
 | 
			
		||||
            )
 | 
			
		||||
                ->setExtra('order', 201);
 | 
			
		||||
                ->setExtras(['order' => 201, 'counter' => $count > 0 ? $count : null]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -120,3 +120,34 @@ li.document-list-item {
 | 
			
		||||
        vertical-align: baseline;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.badge-activity-type-simple {
 | 
			
		||||
    @extend .badge;
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    margin: 0.2rem 0;
 | 
			
		||||
    padding-left: 0;
 | 
			
		||||
    padding-right: 0.5rem;
 | 
			
		||||
 | 
			
		||||
    border-left: 20px groove #9acd32;
 | 
			
		||||
    border-radius: $badge-border-radius;
 | 
			
		||||
 | 
			
		||||
    color: black;
 | 
			
		||||
    font-weight: normal;
 | 
			
		||||
    font-size: unset;
 | 
			
		||||
    max-width: 100%;
 | 
			
		||||
    background-color: $gray-100;
 | 
			
		||||
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    text-overflow: ellipsis;
 | 
			
		||||
    text-indent: 5px hanging;
 | 
			
		||||
    text-align: left;
 | 
			
		||||
 | 
			
		||||
    &::before {
 | 
			
		||||
        margin-right: 3px;
 | 
			
		||||
        position: relative;
 | 
			
		||||
        left: -0.5px;
 | 
			
		||||
        font-family: ForkAwesome;
 | 
			
		||||
        content: '\f04b';
 | 
			
		||||
        color: #9acd32;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ import Location from "./components/Location.vue";
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    name: "App",
 | 
			
		||||
    props: ["hasSocialIssues", "hasLocation", "hasPerson"],
 | 
			
		||||
    props: ["hasSocialIssues", "hasLocation", "hasPerson", "isSimpleEditor"],
 | 
			
		||||
    components: {
 | 
			
		||||
        ConcernedGroups,
 | 
			
		||||
        SocialIssuesAcc,
 | 
			
		||||
 
 | 
			
		||||
@@ -14,18 +14,21 @@ const i18n = _createI18n(activityMessages);
 | 
			
		||||
const hasSocialIssues = document.querySelector("#social-issues-acc") !== null;
 | 
			
		||||
const hasLocation = document.querySelector("#location") !== null;
 | 
			
		||||
const hasPerson = document.querySelector("#add-persons") !== null;
 | 
			
		||||
const isSimpleEditor = true;
 | 
			
		||||
 | 
			
		||||
const app = createApp({
 | 
			
		||||
  template: `<app
 | 
			
		||||
       :hasSocialIssues="hasSocialIssues"
 | 
			
		||||
       :hasLocation="hasLocation"
 | 
			
		||||
       :hasPerson="hasPerson"
 | 
			
		||||
       :isSimpleEditor = "isSimpleEditor"
 | 
			
		||||
    ></app>`,
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      hasSocialIssues,
 | 
			
		||||
      hasLocation,
 | 
			
		||||
      hasPerson,
 | 
			
		||||
      isSimpleEditor
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
})
 | 
			
		||||
 
 | 
			
		||||
@@ -126,4 +126,4 @@
 | 
			
		||||
 | 
			
		||||
{% block css %}
 | 
			
		||||
    {{ encore_entry_link_tags('mod_pickentity_type') }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,44 +13,44 @@
 | 
			
		||||
{% endif %}
 | 
			
		||||
 | 
			
		||||
<div class="item-row">
 | 
			
		||||
    <div class="item-col" style="width: unset">
 | 
			
		||||
        {% if document.isPending %}
 | 
			
		||||
            <div class="badge text-bg-info" data-docgen-is-pending="{{ document.id }}">{{ 'docgen.Doc generation is pending'|trans }}</div>
 | 
			
		||||
        {% elseif document.isFailure %}
 | 
			
		||||
            <div class="badge text-bg-warning">{{ 'docgen.Doc generation failed'|trans }}</div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
 | 
			
		||||
        <div>
 | 
			
		||||
            {% if activity.accompanyingPeriod is not null and context == 'person' %}
 | 
			
		||||
                <span class="badge bg-primary">
 | 
			
		||||
                        <i class="fa fa-random"></i> {{ activity.accompanyingPeriod.id }}
 | 
			
		||||
                    </span> 
 | 
			
		||||
    <div class="item-two-col-grid">
 | 
			
		||||
        <div class="title">
 | 
			
		||||
            {% if document.isPending %}
 | 
			
		||||
                <div class="badge text-bg-info" data-docgen-is-pending="{{ document.id }}">{{ 'docgen.Doc generation is pending'|trans }}</div>
 | 
			
		||||
            {% elseif document.isFailure %}
 | 
			
		||||
                <div class="badge text-bg-warning">{{ 'docgen.Doc generation failed'|trans }}</div>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            <div class="badge-activity-type">
 | 
			
		||||
                <span class="title_label"></span>
 | 
			
		||||
                <span class="title_action">
 | 
			
		||||
                    {{ activity.type.name | localize_translatable_string }}
 | 
			
		||||
 | 
			
		||||
            <div>
 | 
			
		||||
                <div>
 | 
			
		||||
                    <div class="badge-activity-type-simple">
 | 
			
		||||
                        {{ activity.type.name | localize_translatable_string }}
 | 
			
		||||
                    </div>
 | 
			
		||||
                    {% if activity.emergency %}
 | 
			
		||||
                        <span class="badge bg-danger rounded-pill fs-6 float-end">{{ 'Emergency'|trans|upper }}</span>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                    </span>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="denomination h2">
 | 
			
		||||
            {{ document.title|chill_print_or_message("No title") }}
 | 
			
		||||
        </div>
 | 
			
		||||
        {% if document.hasTemplate %}
 | 
			
		||||
            <div>
 | 
			
		||||
                <p>{{ document.template.name|localize_translatable_string }}</p>
 | 
			
		||||
            <div class="denomination h2">
 | 
			
		||||
                {{ document.title|chill_print_or_message("No title") }}
 | 
			
		||||
            </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="item-col">
 | 
			
		||||
        <div class="container">
 | 
			
		||||
            {% if document.hasTemplate %}
 | 
			
		||||
                <div>
 | 
			
		||||
                    <p>{{ document.template.name|localize_translatable_string }}</p>
 | 
			
		||||
                </div>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="aside">
 | 
			
		||||
            <div class="dates row text-end">
 | 
			
		||||
                <span>{{ document.createdAt|format_date('short') }}</span>
 | 
			
		||||
            </div>
 | 
			
		||||
            {% if activity.accompanyingPeriod is not null and context == 'person' %}
 | 
			
		||||
                <div class="text-end">
 | 
			
		||||
                    <span class="badge bg-primary">
 | 
			
		||||
                        <i class="fa fa-random"></i> {{ activity.accompanyingPeriod.id }}
 | 
			
		||||
                    </span> 
 | 
			
		||||
                </div>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
@import '~ChillPersonAssets/chill/scss/mixins.scss';
 | 
			
		||||
@import '~ChillMainAssets/module/bootstrap/shared';
 | 
			
		||||
@import '~ChillPersonAssets/chill/scss/mixins.scss';
 | 
			
		||||
@import 'bootstrap/scss/_badge.scss';
 | 
			
		||||
 | 
			
		||||
.badge-calendar {
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
@@ -23,3 +24,35 @@
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.badge-calendar-simple {
 | 
			
		||||
    @extend .badge;
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    margin: 0.2rem 0;
 | 
			
		||||
    padding-left: 0;
 | 
			
		||||
    padding-right: 0.5rem;
 | 
			
		||||
 | 
			
		||||
    border-left: 20px groove $chill-l-gray;
 | 
			
		||||
    border-radius: $badge-border-radius;
 | 
			
		||||
 | 
			
		||||
    max-width: 100%;
 | 
			
		||||
    background-color: $gray-100;
 | 
			
		||||
 | 
			
		||||
    color: black;
 | 
			
		||||
    font-weight: normal;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    font-weight: normal;
 | 
			
		||||
    font-size: unset;
 | 
			
		||||
    text-overflow: ellipsis;
 | 
			
		||||
    text-indent: 5px hanging;
 | 
			
		||||
    text-align: left;
 | 
			
		||||
 | 
			
		||||
    &::before {
 | 
			
		||||
        margin-right: 3px;
 | 
			
		||||
        position: relative;
 | 
			
		||||
        left: -0.5px;
 | 
			
		||||
        font-family: ForkAwesome;
 | 
			
		||||
        content: '\f04b';
 | 
			
		||||
        color: $chill-l-gray;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ div.calendar-list {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    & > a.calendar-list__global {
 | 
			
		||||
        display: inline-block;;
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
        padding: 0.2rem;
 | 
			
		||||
        min-width: 2rem;
 | 
			
		||||
        border: 1px solid var(--bs-chill-blue);
 | 
			
		||||
 
 | 
			
		||||
@@ -96,23 +96,23 @@
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <FullCalendar :options="calendarOptions" ref="calendarRef">
 | 
			
		||||
        <template v-slot:eventContent="{ arg }: { arg: { event: EventApi } }">
 | 
			
		||||
            <span :class="eventClasses(arg.event)">
 | 
			
		||||
                <b v-if="arg.event.extendedProps.is === 'remote'">{{
 | 
			
		||||
                    arg.event.title
 | 
			
		||||
        <template v-slot:eventContent="{ event }">
 | 
			
		||||
            <span :class="eventClasses(event)">
 | 
			
		||||
                <b v-if="event.extendedProps.is === 'remote'">{{
 | 
			
		||||
                    event.title
 | 
			
		||||
                }}</b>
 | 
			
		||||
                <b v-else-if="arg.event.extendedProps.is === 'range'"
 | 
			
		||||
                    >{{ arg.event.startStr }} -
 | 
			
		||||
                    {{ arg.event.extendedProps.locationName }}</b
 | 
			
		||||
                <b v-else-if="event.extendedProps.is === 'range'"
 | 
			
		||||
                    >{{ formatDate(event.startStr) }} -
 | 
			
		||||
                    {{ event.extendedProps.locationName }}</b
 | 
			
		||||
                >
 | 
			
		||||
                <b v-else-if="arg.event.extendedProps.is === 'local'">{{
 | 
			
		||||
                    arg.event.title
 | 
			
		||||
                <b v-else-if="event.extendedProps.is === 'local'">{{
 | 
			
		||||
                    event.title
 | 
			
		||||
                }}</b>
 | 
			
		||||
                <b v-else>no 'is'</b>
 | 
			
		||||
                <a
 | 
			
		||||
                    v-if="arg.event.extendedProps.is === 'range'"
 | 
			
		||||
                    v-if="event.extendedProps.is === 'range'"
 | 
			
		||||
                    class="fa fa-fw fa-times delete"
 | 
			
		||||
                    @click.prevent="onClickDelete(arg.event)"
 | 
			
		||||
                    @click.prevent="onClickDelete(event)"
 | 
			
		||||
                >
 | 
			
		||||
                </a>
 | 
			
		||||
            </span>
 | 
			
		||||
@@ -221,13 +221,12 @@ import type {
 | 
			
		||||
    DatesSetArg,
 | 
			
		||||
    EventInput,
 | 
			
		||||
} from "@fullcalendar/core";
 | 
			
		||||
import { reactive, computed, ref, onMounted } from "vue";
 | 
			
		||||
import { computed, ref, onMounted } from "vue";
 | 
			
		||||
import { useStore } from "vuex";
 | 
			
		||||
import { key } from "./store";
 | 
			
		||||
import FullCalendar from "@fullcalendar/vue3";
 | 
			
		||||
import frLocale from "@fullcalendar/core/locales/fr";
 | 
			
		||||
import interactionPlugin, {
 | 
			
		||||
    DropArg,
 | 
			
		||||
    EventResizeDoneArg,
 | 
			
		||||
} from "@fullcalendar/interaction";
 | 
			
		||||
import timeGridPlugin from "@fullcalendar/timegrid";
 | 
			
		||||
@@ -237,19 +236,13 @@ import {
 | 
			
		||||
    EventDropArg,
 | 
			
		||||
    EventClickArg,
 | 
			
		||||
} from "@fullcalendar/core";
 | 
			
		||||
import {
 | 
			
		||||
    dateToISO,
 | 
			
		||||
    ISOToDate,
 | 
			
		||||
} from "../../../../../ChillMainBundle/Resources/public/chill/js/date";
 | 
			
		||||
import { dateToISO, ISOToDate } from "ChillMainAssets/chill/js/date";
 | 
			
		||||
import VueMultiselect from "vue-multiselect";
 | 
			
		||||
import { Location } from "../../../../../ChillMainBundle/Resources/public/types";
 | 
			
		||||
import { Location } from "ChillMainAssets/types";
 | 
			
		||||
import EditLocation from "./Components/EditLocation.vue";
 | 
			
		||||
import { useI18n } from "vue-i18n";
 | 
			
		||||
 | 
			
		||||
const store = useStore(key);
 | 
			
		||||
 | 
			
		||||
const { t } = useI18n();
 | 
			
		||||
 | 
			
		||||
const showWeekends = ref(false);
 | 
			
		||||
const slotDuration = ref("00:15:00");
 | 
			
		||||
const slotMinTime = ref("09:00:00");
 | 
			
		||||
@@ -301,6 +294,11 @@ const nextWeeks = computed((): Weeks[] =>
 | 
			
		||||
    }),
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const formatDate = (datetime: string) => {
 | 
			
		||||
    console.log(typeof datetime);
 | 
			
		||||
    return ISOToDate(datetime);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const baseOptions = ref<CalendarOptions>({
 | 
			
		||||
    locale: frLocale,
 | 
			
		||||
    plugins: [interactionPlugin, timeGridPlugin],
 | 
			
		||||
@@ -353,7 +351,7 @@ const pickedLocation = computed<Location | null>({
 | 
			
		||||
 * return the show classes for the event
 | 
			
		||||
 * @param arg
 | 
			
		||||
 */
 | 
			
		||||
const eventClasses = function (arg: EventApi): object {
 | 
			
		||||
const eventClasses = function (): object {
 | 
			
		||||
    return { calendarRangeItems: true };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -431,7 +429,6 @@ function onEventDropOrResize(payload: EventDropArg | EventResizeDoneArg) {
 | 
			
		||||
    if (payload.event.extendedProps.is !== "range") {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    const changedEvent = payload.event;
 | 
			
		||||
 | 
			
		||||
    store.dispatch("calendarRanges/patchRangeTime", {
 | 
			
		||||
        calendarRangeId: payload.event.extendedProps.calendarRangeId,
 | 
			
		||||
 
 | 
			
		||||
@@ -6,50 +6,48 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<div class="item-row">
 | 
			
		||||
    <div class="item-col" style="width: unset">
 | 
			
		||||
        {% if document.storedObject.isPending %}
 | 
			
		||||
            <div class="badge text-bg-info" data-docgen-is-pending="{{ document.storedObject.id }}">{{ 'docgen.Doc generation is pending'|trans }}</div>
 | 
			
		||||
        {% elseif document.storedObject.isFailure %}
 | 
			
		||||
            <div class="badge text-bg-warning">{{ 'docgen.Doc generation failed'|trans }}</div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
 | 
			
		||||
        <div>
 | 
			
		||||
            {% if c.accompanyingPeriod is not null and context == 'person' %}
 | 
			
		||||
                <span class="badge bg-primary">
 | 
			
		||||
                        <i class="fa fa-random"></i> {{ c.accompanyingPeriod.id }}
 | 
			
		||||
                    </span> 
 | 
			
		||||
    <div class="item-two-col-grid">
 | 
			
		||||
        <div class="title">
 | 
			
		||||
            {% if document.storedObject.isPending %}
 | 
			
		||||
                <div class="badge text-bg-info" data-docgen-is-pending="{{ document.storedObject.id }}">{{ 'docgen.Doc generation is pending'|trans }}</div>
 | 
			
		||||
            {% elseif document.storedObject.isFailure %}
 | 
			
		||||
                <div class="badge text-bg-warning">{{ 'docgen.Doc generation failed'|trans }}</div>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
 | 
			
		||||
            <span class="badge-calendar">
 | 
			
		||||
                    <span class="title_label"></span>
 | 
			
		||||
                    <span class="title_action">
 | 
			
		||||
                        {{ 'Calendar'|trans }}
 | 
			
		||||
                        {% if c.endDate.diff(c.startDate).days >= 1 %}
 | 
			
		||||
                            {{ c.startDate|format_datetime('short', 'short') }}
 | 
			
		||||
                            - {{ c.endDate|format_datetime('short', 'short') }}
 | 
			
		||||
                        {% else %}
 | 
			
		||||
                            {{ c.startDate|format_datetime('short', 'short') }}
 | 
			
		||||
                            - {{ c.endDate|format_datetime('none', 'short') }}
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    </span>
 | 
			
		||||
                </span>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="denomination h2">
 | 
			
		||||
            {{ document.storedObject.title|chill_print_or_message("No title") }}
 | 
			
		||||
        </div>
 | 
			
		||||
        {% if document.storedObject.hasTemplate %}
 | 
			
		||||
            <div>
 | 
			
		||||
                <p>{{ document.storedObject.template.name|localize_translatable_string }}</p>
 | 
			
		||||
            </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="item-col">
 | 
			
		||||
        <div class="container">
 | 
			
		||||
                <span class="badge-calendar-simple">
 | 
			
		||||
                    {{ 'Calendar'|trans }}
 | 
			
		||||
                    {% if c.endDate.diff(c.startDate).days >= 1 %}
 | 
			
		||||
                        {{ c.startDate|format_datetime('short', 'short') }}
 | 
			
		||||
                        - {{ c.endDate|format_datetime('short', 'short') }}
 | 
			
		||||
                    {% else %}
 | 
			
		||||
                        {{ c.startDate|format_datetime('short', 'short') }}
 | 
			
		||||
                        - {{ c.endDate|format_datetime('none', 'short') }}
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                </span>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div class="denomination h2">
 | 
			
		||||
                {{ document.storedObject.title|chill_print_or_message("No title") }}
 | 
			
		||||
            </div>
 | 
			
		||||
            {% if document.storedObject.hasTemplate %}
 | 
			
		||||
                <div>
 | 
			
		||||
                    <p>{{ document.storedObject.template.name|localize_translatable_string }}</p>
 | 
			
		||||
                </div>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="aside">
 | 
			
		||||
            <div class="dates row text-end">
 | 
			
		||||
                <span>{{ document.storedObject.createdAt|format_date('short') }}</span>
 | 
			
		||||
            </div>
 | 
			
		||||
            {% if c.accompanyingPeriod is not null and context == 'person' %}
 | 
			
		||||
                <div class="text-end">
 | 
			
		||||
                    <span class="badge bg-primary">
 | 
			
		||||
                        <i class="fa fa-random"></i> {{ c.accompanyingPeriod.id }}
 | 
			
		||||
                    </span> 
 | 
			
		||||
                </div>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,9 @@ const startApp = (
 | 
			
		||||
    collectionEntry: null | HTMLLIElement,
 | 
			
		||||
): void => {
 | 
			
		||||
    console.log("app started", divElement);
 | 
			
		||||
 | 
			
		||||
    const inputTitle = collectionEntry?.querySelector("input[type='text']");
 | 
			
		||||
 | 
			
		||||
    const input_stored_object: HTMLInputElement | null =
 | 
			
		||||
        divElement.querySelector("input[data-stored-object]");
 | 
			
		||||
    if (null === input_stored_object) {
 | 
			
		||||
@@ -26,9 +29,10 @@ const startApp = (
 | 
			
		||||
    const app = createApp({
 | 
			
		||||
        template:
 | 
			
		||||
            '<drop-file-widget :existingDoc="this.$data.existingDoc" :allowRemove="true" @addDocument="this.addDocument" @removeDocument="removeDocument"></drop-file-widget>',
 | 
			
		||||
        data(vm) {
 | 
			
		||||
        data() {
 | 
			
		||||
            return {
 | 
			
		||||
                existingDoc: existingDoc,
 | 
			
		||||
                inputTitle: inputTitle,
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
        components: {
 | 
			
		||||
@@ -38,10 +42,13 @@ const startApp = (
 | 
			
		||||
            addDocument: function ({
 | 
			
		||||
                stored_object,
 | 
			
		||||
                stored_object_version,
 | 
			
		||||
                file_name,
 | 
			
		||||
            }: {
 | 
			
		||||
                stored_object: StoredObject;
 | 
			
		||||
                stored_object_version: StoredObjectVersion;
 | 
			
		||||
                file_name: string;
 | 
			
		||||
            }): void {
 | 
			
		||||
                stored_object.title = file_name;
 | 
			
		||||
                console.log("object added", stored_object);
 | 
			
		||||
                console.log("version added", stored_object_version);
 | 
			
		||||
                this.$data.existingDoc = stored_object;
 | 
			
		||||
@@ -49,6 +56,11 @@ const startApp = (
 | 
			
		||||
                input_stored_object.value = JSON.stringify(
 | 
			
		||||
                    this.$data.existingDoc,
 | 
			
		||||
                );
 | 
			
		||||
                if (this.$data.inputTitle) {
 | 
			
		||||
                    if (!this.$data.inputTitle?.value) {
 | 
			
		||||
                        this.$data.inputTitle.value = file_name;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            removeDocument: function (object: StoredObject): void {
 | 
			
		||||
                console.log("catch remove document", object);
 | 
			
		||||
 
 | 
			
		||||
@@ -2,26 +2,28 @@
 | 
			
		||||
    <teleport to="body">
 | 
			
		||||
        <modal v-if="modalOpen" @close="modalOpen = false">
 | 
			
		||||
            <template v-slot:header>
 | 
			
		||||
                <h2>{{ $t("signature_confirmation") }}</h2>
 | 
			
		||||
                <h2>{{ trans(SIGNATURES_SIGNATURE_CONFIRMATION) }}</h2>
 | 
			
		||||
            </template>
 | 
			
		||||
            <template v-slot:body>
 | 
			
		||||
                <div class="signature-modal-body text-center" v-if="loading">
 | 
			
		||||
                    <p>{{ $t("electronic_signature_in_progress") }}</p>
 | 
			
		||||
                    <p>
 | 
			
		||||
                        {{ trans(SIGNATURES_ELECTRONIC_SIGNATURE_IN_PROGRESS) }}
 | 
			
		||||
                    </p>
 | 
			
		||||
                    <div class="loading">
 | 
			
		||||
                        <i
 | 
			
		||||
                            class="fa fa-circle-o-notch fa-spin fa-3x"
 | 
			
		||||
                            :title="$t('loading')"
 | 
			
		||||
                            :title="trans(SIGNATURES_LOADING)"
 | 
			
		||||
                        ></i>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="signature-modal-body text-center" v-else>
 | 
			
		||||
                    <p>{{ $t("you_are_going_to_sign") }}</p>
 | 
			
		||||
                    <p>{{ $t("are_you_sure") }}</p>
 | 
			
		||||
                    <p>{{ trans(SIGNATURES_YOU_ARE_GOING_TO_SIGN) }}</p>
 | 
			
		||||
                    <p>{{ trans(SIGNATURES_ARE_YOU_SURE) }}</p>
 | 
			
		||||
                </div>
 | 
			
		||||
            </template>
 | 
			
		||||
            <template v-slot:footer>
 | 
			
		||||
                <button class="btn btn-action" @click.prevent="confirmSign">
 | 
			
		||||
                    {{ $t("yes") }}
 | 
			
		||||
                    {{ trans(SIGNATURES_YES) }}
 | 
			
		||||
                </button>
 | 
			
		||||
            </template>
 | 
			
		||||
        </modal>
 | 
			
		||||
@@ -82,28 +84,39 @@
 | 
			
		||||
                        @change="toggleMultiPage"
 | 
			
		||||
                    />
 | 
			
		||||
                    <label class="form-check-label" for="checkboxMulti">
 | 
			
		||||
                        {{ $t("all_pages") }}
 | 
			
		||||
                        {{ trans(SIGNATURES_ALL_PAGES) }}
 | 
			
		||||
                    </label>
 | 
			
		||||
                </template>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div
 | 
			
		||||
                v-if="signature.zones.length > 0"
 | 
			
		||||
                v-if="signature.zones.length === 1 && signedState !== 'signed'"
 | 
			
		||||
                class="col-5 p-0 text-center turnSignature"
 | 
			
		||||
            >
 | 
			
		||||
                <button
 | 
			
		||||
                    :disabled="isFirstSignatureZone"
 | 
			
		||||
                    class="btn btn-light btn-sm"
 | 
			
		||||
                    @click="goToSignatureZoneUnique"
 | 
			
		||||
                >
 | 
			
		||||
                    {{ trans(SIGNATURES_GO_TO_SIGNATURE_UNIQUE) }}
 | 
			
		||||
                </button>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div
 | 
			
		||||
                v-if="signature.zones.length > 1"
 | 
			
		||||
                class="col-5 p-0 text-center turnSignature"
 | 
			
		||||
            >
 | 
			
		||||
                <button
 | 
			
		||||
                    :disabled="isFirstSignatureZone()"
 | 
			
		||||
                    class="btn btn-light btn-sm"
 | 
			
		||||
                    @click="turnSignature(-1)"
 | 
			
		||||
                >
 | 
			
		||||
                    {{ $t("last_zone") }}
 | 
			
		||||
                    {{ trans(SIGNATURES_LAST_ZONE) }}
 | 
			
		||||
                </button>
 | 
			
		||||
                <span>|</span>
 | 
			
		||||
                <button
 | 
			
		||||
                    :disabled="isLastSignatureZone"
 | 
			
		||||
                    :disabled="isLastSignatureZone()"
 | 
			
		||||
                    class="btn btn-light btn-sm"
 | 
			
		||||
                    @click="turnSignature(1)"
 | 
			
		||||
                >
 | 
			
		||||
                    {{ $t("next_zone") }}
 | 
			
		||||
                    {{ trans(SIGNATURES_NEXT_ZONE) }}
 | 
			
		||||
                </button>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="col text-end" v-if="signedState !== 'signed'">
 | 
			
		||||
@@ -112,9 +125,9 @@
 | 
			
		||||
                    :hidden="!userSignatureZone"
 | 
			
		||||
                    @click="undoSign"
 | 
			
		||||
                    v-if="signature.zones.length > 1"
 | 
			
		||||
                    :title="$t('choose_another_signature')"
 | 
			
		||||
                    :title="trans(SIGNATURES_CHOOSE_ANOTHER_SIGNATURE)"
 | 
			
		||||
                >
 | 
			
		||||
                    {{ $t("another_zone") }}
 | 
			
		||||
                    {{ trans(SIGNATURES_ANOTHER_ZONE) }}
 | 
			
		||||
                </button>
 | 
			
		||||
                <button
 | 
			
		||||
                    class="btn btn-misc btn-sm"
 | 
			
		||||
@@ -122,7 +135,7 @@
 | 
			
		||||
                    @click="undoSign"
 | 
			
		||||
                    v-else
 | 
			
		||||
                >
 | 
			
		||||
                    {{ $t("cancel") }}
 | 
			
		||||
                    {{ trans(SIGNATURES_CANCEL) }}
 | 
			
		||||
                </button>
 | 
			
		||||
                <button
 | 
			
		||||
                    v-if="userSignatureZone === null"
 | 
			
		||||
@@ -134,7 +147,7 @@
 | 
			
		||||
                        active: canvasEvent === 'add',
 | 
			
		||||
                    }"
 | 
			
		||||
                    @click="toggleAddZone()"
 | 
			
		||||
                    :title="$t('add_sign_zone')"
 | 
			
		||||
                    :title="trans(SIGNATURES_ADD_SIGN_ZONE)"
 | 
			
		||||
                >
 | 
			
		||||
                    <template v-if="canvasEvent === 'add'">
 | 
			
		||||
                        <div
 | 
			
		||||
@@ -186,48 +199,70 @@
 | 
			
		||||
                        @change="toggleMultiPage"
 | 
			
		||||
                    />
 | 
			
		||||
                    <label class="form-check-label" for="checkboxMulti">
 | 
			
		||||
                        {{ $t("see_all_pages") }}
 | 
			
		||||
                        {{ trans(SIGNATURES_SEE_ALL_PAGES) }}
 | 
			
		||||
                    </label>
 | 
			
		||||
                </template>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div
 | 
			
		||||
                v-if="signature.zones.length > 0 && signedState !== 'signed'"
 | 
			
		||||
                v-if="signature.zones.length === 1 && signedState !== 'signed'"
 | 
			
		||||
                class="col-4 d-xl-none text-center turnSignature p-0"
 | 
			
		||||
            >
 | 
			
		||||
                <button
 | 
			
		||||
                    :disabled="!hasSignatureZoneSelected"
 | 
			
		||||
                    class="btn btn-light btn-sm"
 | 
			
		||||
                    @click="turnSignature(-1)"
 | 
			
		||||
                    @click="goToSignatureZoneUnique"
 | 
			
		||||
                >
 | 
			
		||||
                    {{ $t("last_zone") }}
 | 
			
		||||
                </button>
 | 
			
		||||
                <span>|</span>
 | 
			
		||||
                <button
 | 
			
		||||
                    :disabled="isLastSignatureZone"
 | 
			
		||||
                    class="btn btn-light btn-sm"
 | 
			
		||||
                    @click="turnSignature(1)"
 | 
			
		||||
                >
 | 
			
		||||
                    {{ $t("next_zone") }}
 | 
			
		||||
                    {{ trans(SIGNATURES_GO_TO_SIGNATURE_UNIQUE) }}
 | 
			
		||||
                </button>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div
 | 
			
		||||
                v-if="signature.zones.length > 0 && signedState !== 'signed'"
 | 
			
		||||
                class="col-4 d-none d-xl-flex p-0 text-center turnSignature"
 | 
			
		||||
                v-if="signature.zones.length > 1 && signedState !== 'signed'"
 | 
			
		||||
                class="col-4 d-xl-none text-center turnSignature p-0"
 | 
			
		||||
            >
 | 
			
		||||
                <button
 | 
			
		||||
                    :disabled="isFirstSignatureZone"
 | 
			
		||||
                    :disabled="isFirstSignatureZone()"
 | 
			
		||||
                    class="btn btn-light btn-sm"
 | 
			
		||||
                    @click="turnSignature(-1)"
 | 
			
		||||
                >
 | 
			
		||||
                    {{ $t("last_sign_zone") }}
 | 
			
		||||
                    {{ trans(SIGNATURES_LAST_ZONE) }}
 | 
			
		||||
                </button>
 | 
			
		||||
                <span>|</span>
 | 
			
		||||
                <button
 | 
			
		||||
                    :disabled="isLastSignatureZone"
 | 
			
		||||
                    :disabled="isLastSignatureZone()"
 | 
			
		||||
                    class="btn btn-light btn-sm"
 | 
			
		||||
                    @click="turnSignature(1)"
 | 
			
		||||
                >
 | 
			
		||||
                    {{ $t("next_sign_zone") }}
 | 
			
		||||
                    {{ trans(SIGNATURES_NEXT_ZONE) }}
 | 
			
		||||
                </button>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div
 | 
			
		||||
                v-if="signature.zones.length === 1 && signedState !== 'signed'"
 | 
			
		||||
                class="col-4 d-none d-xl-flex p-0 text-center turnSignature"
 | 
			
		||||
            >
 | 
			
		||||
                <button
 | 
			
		||||
                    class="btn btn-light btn-sm"
 | 
			
		||||
                    @click="goToSignatureZoneUnique"
 | 
			
		||||
                >
 | 
			
		||||
                    {{ trans(SIGNATURES_GO_TO_SIGNATURE_UNIQUE) }}
 | 
			
		||||
                </button>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div
 | 
			
		||||
                v-if="signature.zones.length > 1 && signedState !== 'signed'"
 | 
			
		||||
                class="col-4 d-none d-xl-flex p-0 text-center turnSignature"
 | 
			
		||||
            >
 | 
			
		||||
                <button
 | 
			
		||||
                    :disabled="isFirstSignatureZone()"
 | 
			
		||||
                    class="btn btn-light btn-sm"
 | 
			
		||||
                    @click="turnSignature(-1)"
 | 
			
		||||
                >
 | 
			
		||||
                    {{ trans(SIGNATURES_LAST_SIGN_ZONE) }}
 | 
			
		||||
                </button>
 | 
			
		||||
                <span>|</span>
 | 
			
		||||
                <button
 | 
			
		||||
                    :disabled="isLastSignatureZone()"
 | 
			
		||||
                    class="btn btn-light btn-sm"
 | 
			
		||||
                    @click="turnSignature(1)"
 | 
			
		||||
                >
 | 
			
		||||
                    {{ trans(SIGNATURES_NEXT_SIGN_ZONE) }}
 | 
			
		||||
                </button>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="col text-end" v-if="signedState !== 'signed'">
 | 
			
		||||
@@ -237,7 +272,7 @@
 | 
			
		||||
                    @click="undoSign"
 | 
			
		||||
                    v-if="signature.zones.length > 1"
 | 
			
		||||
                >
 | 
			
		||||
                    {{ $t("choose_another_signature") }}
 | 
			
		||||
                    {{ trans(SIGNATURES_CHOOSE_ANOTHER_SIGNATURE) }}
 | 
			
		||||
                </button>
 | 
			
		||||
                <button
 | 
			
		||||
                    class="btn btn-misc btn-sm"
 | 
			
		||||
@@ -245,7 +280,7 @@
 | 
			
		||||
                    @click="undoSign"
 | 
			
		||||
                    v-else
 | 
			
		||||
                >
 | 
			
		||||
                    {{ $t("cancel") }}
 | 
			
		||||
                    {{ trans(SIGNATURES_CANCEL) }}
 | 
			
		||||
                </button>
 | 
			
		||||
                <button
 | 
			
		||||
                    v-if="userSignatureZone === null"
 | 
			
		||||
@@ -257,13 +292,13 @@
 | 
			
		||||
                        active: canvasEvent === 'add',
 | 
			
		||||
                    }"
 | 
			
		||||
                    @click="toggleAddZone()"
 | 
			
		||||
                    :title="$t('add_sign_zone')"
 | 
			
		||||
                    :title="trans(SIGNATURES_ADD_SIGN_ZONE)"
 | 
			
		||||
                >
 | 
			
		||||
                    <template v-if="canvasEvent !== 'add'">
 | 
			
		||||
                        {{ $t("add_zone") }}
 | 
			
		||||
                        {{ trans(SIGNATURES_ADD_ZONE) }}
 | 
			
		||||
                    </template>
 | 
			
		||||
                    <template v-else>
 | 
			
		||||
                        {{ $t("click_on_document") }}
 | 
			
		||||
                        {{ trans(SIGNATURES_CLICK_ON_DOCUMENT) }}
 | 
			
		||||
                        <div
 | 
			
		||||
                            class="spinner-border spinner-border-sm"
 | 
			
		||||
                            role="status"
 | 
			
		||||
@@ -297,10 +332,10 @@
 | 
			
		||||
                    v-if="signedState !== 'signed'"
 | 
			
		||||
                    :href="getReturnPath()"
 | 
			
		||||
                >
 | 
			
		||||
                    {{ $t("cancel") }}
 | 
			
		||||
                    {{ trans(SIGNATURES_CANCEL) }}
 | 
			
		||||
                </a>
 | 
			
		||||
                <a class="btn btn-misc" v-else :href="getReturnPath()">
 | 
			
		||||
                    {{ $t("return") }}
 | 
			
		||||
                    {{ trans(SIGNATURES_RETURN) }}
 | 
			
		||||
                </a>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="col text-end" v-if="signedState !== 'signed'">
 | 
			
		||||
@@ -309,7 +344,7 @@
 | 
			
		||||
                    :disabled="!userSignatureZone"
 | 
			
		||||
                    @click="sign"
 | 
			
		||||
                >
 | 
			
		||||
                    {{ $t("sign") }}
 | 
			
		||||
                    {{ trans(SIGNATURES_SIGN) }}
 | 
			
		||||
                </button>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="col-4" v-else></div>
 | 
			
		||||
@@ -318,7 +353,7 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { ref, Ref, computed } from "vue";
 | 
			
		||||
import { ref, Ref } from "vue";
 | 
			
		||||
import { useToast } from "vue-toast-notification";
 | 
			
		||||
import "vue-toast-notification/dist/theme-sugar.css";
 | 
			
		||||
import {
 | 
			
		||||
@@ -329,13 +364,38 @@ import {
 | 
			
		||||
    SignedState,
 | 
			
		||||
    ZoomLevel,
 | 
			
		||||
} from "../../types";
 | 
			
		||||
import { makeFetch } from "../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
 | 
			
		||||
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
 | 
			
		||||
import * as pdfjsLib from "pdfjs-dist";
 | 
			
		||||
import {
 | 
			
		||||
    PDFDocumentProxy,
 | 
			
		||||
    PDFPageProxy,
 | 
			
		||||
} from "pdfjs-dist/types/src/display/api";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    SIGNATURES_YES,
 | 
			
		||||
    SIGNATURES_ARE_YOU_SURE,
 | 
			
		||||
    SIGNATURES_YOU_ARE_GOING_TO_SIGN,
 | 
			
		||||
    SIGNATURES_SIGNATURE_CONFIRMATION,
 | 
			
		||||
    SIGNATURES_SIGN,
 | 
			
		||||
    SIGNATURES_CHOOSE_ANOTHER_SIGNATURE,
 | 
			
		||||
    SIGNATURES_CANCEL,
 | 
			
		||||
    SIGNATURES_LAST_SIGN_ZONE,
 | 
			
		||||
    SIGNATURES_NEXT_SIGN_ZONE,
 | 
			
		||||
    SIGNATURES_ADD_SIGN_ZONE,
 | 
			
		||||
    SIGNATURES_CLICK_ON_DOCUMENT,
 | 
			
		||||
    SIGNATURES_LAST_ZONE,
 | 
			
		||||
    SIGNATURES_NEXT_ZONE,
 | 
			
		||||
    SIGNATURES_ADD_ZONE,
 | 
			
		||||
    SIGNATURES_ANOTHER_ZONE,
 | 
			
		||||
    SIGNATURES_ELECTRONIC_SIGNATURE_IN_PROGRESS,
 | 
			
		||||
    SIGNATURES_LOADING,
 | 
			
		||||
    SIGNATURES_RETURN,
 | 
			
		||||
    SIGNATURES_SEE_ALL_PAGES,
 | 
			
		||||
    SIGNATURES_ALL_PAGES,
 | 
			
		||||
    SIGNATURES_GO_TO_SIGNATURE_UNIQUE,
 | 
			
		||||
    trans,
 | 
			
		||||
} from "translator";
 | 
			
		||||
 | 
			
		||||
// @ts-ignore incredible but the console.log is needed
 | 
			
		||||
import * as PdfWorker from "pdfjs-dist/build/pdf.worker.mjs";
 | 
			
		||||
console.log(PdfWorker);
 | 
			
		||||
@@ -416,19 +476,15 @@ const $toast = useToast();
 | 
			
		||||
const signature = window.signature;
 | 
			
		||||
 | 
			
		||||
const isFirstSignatureZone = () =>
 | 
			
		||||
    userSignatureZone.value?.index ? userSignatureZone.value.index < 1 : false;
 | 
			
		||||
    userSignatureZone.value?.index != null
 | 
			
		||||
        ? userSignatureZone.value.index < 1
 | 
			
		||||
        : false;
 | 
			
		||||
 | 
			
		||||
const isLastSignatureZone = () =>
 | 
			
		||||
    userSignatureZone.value?.index
 | 
			
		||||
        ? userSignatureZone.value.index >= signature.zones.length - 1
 | 
			
		||||
        : false;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Return true if the user has selected a user zone (existing on the doc or created by the user)
 | 
			
		||||
 */
 | 
			
		||||
const hasSignatureZoneSelected = computed<boolean>(
 | 
			
		||||
    () => userSignatureZone.value !== null,
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const setZoomLevel = async (zoomLevel: string) => {
 | 
			
		||||
    zoom.value = Number.parseFloat(zoomLevel);
 | 
			
		||||
    await resetPages();
 | 
			
		||||
@@ -600,6 +656,15 @@ const turnPage = async (upOrDown: number) => {
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const selectZoneInCanvas = (signatureZone: SignatureZone) => {
 | 
			
		||||
    page.value = signatureZone.PDFPage.index + 1;
 | 
			
		||||
    const canvas = getCanvas(signatureZone.PDFPage.index + 1);
 | 
			
		||||
    selectZone(signatureZone, canvas);
 | 
			
		||||
    canvas.scrollIntoView();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const goToSignatureZoneUnique = () => selectZoneInCanvas(signature.zones[0]);
 | 
			
		||||
 | 
			
		||||
const turnSignature = async (upOrDown: number) => {
 | 
			
		||||
    let zoneIndex = userSignatureZone.value?.index ?? -1;
 | 
			
		||||
    if (zoneIndex < -1) {
 | 
			
		||||
@@ -612,10 +677,7 @@ const turnSignature = async (upOrDown: number) => {
 | 
			
		||||
    }
 | 
			
		||||
    let currentZone = signature.zones[zoneIndex];
 | 
			
		||||
    if (currentZone) {
 | 
			
		||||
        page.value = currentZone.PDFPage.index + 1;
 | 
			
		||||
        const canvas = getCanvas(currentZone.PDFPage.index + 1);
 | 
			
		||||
        selectZone(currentZone, canvas);
 | 
			
		||||
        canvas.scrollIntoView();
 | 
			
		||||
        selectZoneInCanvas(currentZone);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@ const emit =
 | 
			
		||||
            {
 | 
			
		||||
                stored_object_version: StoredObjectVersionCreated,
 | 
			
		||||
                stored_object: StoredObject,
 | 
			
		||||
                file_name: string,
 | 
			
		||||
            },
 | 
			
		||||
        ) => void
 | 
			
		||||
    >();
 | 
			
		||||
@@ -114,7 +115,21 @@ const handleFile = async (file: File): Promise<void> => {
 | 
			
		||||
        persisted: false,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    emit("addDocument", { stored_object, stored_object_version });
 | 
			
		||||
    const fileName = file.name;
 | 
			
		||||
    let file_name = "Nouveau document";
 | 
			
		||||
    const file_name_split = fileName.split(".");
 | 
			
		||||
    if (file_name_split.length > 1) {
 | 
			
		||||
        const extension = file_name_split
 | 
			
		||||
            ? file_name_split[file_name_split.length - 1]
 | 
			
		||||
            : "";
 | 
			
		||||
        file_name = fileName.replace(extension, "").slice(0, -1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    emit("addDocument", {
 | 
			
		||||
        stored_object,
 | 
			
		||||
        stored_object_version,
 | 
			
		||||
        file_name: file_name,
 | 
			
		||||
    });
 | 
			
		||||
    uploading.value = false;
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ const emit = defineEmits<{
 | 
			
		||||
        {
 | 
			
		||||
            stored_object: StoredObject,
 | 
			
		||||
            stored_object_version: StoredObjectVersion,
 | 
			
		||||
            file_name: string,
 | 
			
		||||
        },
 | 
			
		||||
    ): void;
 | 
			
		||||
    (e: "removeDocument"): void;
 | 
			
		||||
@@ -42,14 +43,16 @@ const buttonState = computed<"add" | "replace">(() => {
 | 
			
		||||
function onAddDocument({
 | 
			
		||||
    stored_object,
 | 
			
		||||
    stored_object_version,
 | 
			
		||||
    file_name,
 | 
			
		||||
}: {
 | 
			
		||||
    stored_object: StoredObject;
 | 
			
		||||
    stored_object_version: StoredObjectVersion;
 | 
			
		||||
    file_name: string;
 | 
			
		||||
}): void {
 | 
			
		||||
    const message =
 | 
			
		||||
        buttonState.value === "add" ? "Document ajouté" : "Document remplacé";
 | 
			
		||||
    $toast.success(message);
 | 
			
		||||
    emit("addDocument", { stored_object_version, stored_object });
 | 
			
		||||
    emit("addDocument", { stored_object_version, stored_object, file_name });
 | 
			
		||||
    state.showModal = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@ const emit = defineEmits<{
 | 
			
		||||
        {
 | 
			
		||||
            stored_object: StoredObject,
 | 
			
		||||
            stored_object_version: StoredObjectVersion,
 | 
			
		||||
            file_name: string,
 | 
			
		||||
        },
 | 
			
		||||
    ): void;
 | 
			
		||||
    (e: "removeDocument"): void;
 | 
			
		||||
@@ -53,11 +54,13 @@ const dav_link_href = computed<string | undefined>(() => {
 | 
			
		||||
const onAddDocument = ({
 | 
			
		||||
    stored_object,
 | 
			
		||||
    stored_object_version,
 | 
			
		||||
    file_name,
 | 
			
		||||
}: {
 | 
			
		||||
    stored_object: StoredObject;
 | 
			
		||||
    stored_object_version: StoredObjectVersion;
 | 
			
		||||
    file_name: string;
 | 
			
		||||
}): void => {
 | 
			
		||||
    emit("addDocument", { stored_object, stored_object_version });
 | 
			
		||||
    emit("addDocument", { stored_object, stored_object_version, file_name });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const onRemoveDocument = (e: Event): void => {
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,7 @@ const onRestored = ({
 | 
			
		||||
<template>
 | 
			
		||||
    <template v-if="props.versions.length > 0">
 | 
			
		||||
        <div class="container">
 | 
			
		||||
            <template v-for="v in props.versions">
 | 
			
		||||
            <template v-for="v in props.versions" :key="v.id">
 | 
			
		||||
                <history-button-list-item
 | 
			
		||||
                    :version="v"
 | 
			
		||||
                    :can-edit="canEdit"
 | 
			
		||||
 
 | 
			
		||||
@@ -32,13 +32,17 @@ const onRestore = ({
 | 
			
		||||
    emit("restoreVersion", { newVersion });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const isKeptBeforeConversion = computed<boolean>(() =>
 | 
			
		||||
    props.version["point-in-times"].reduce(
 | 
			
		||||
        (accumulator: boolean, pit: StoredObjectPointInTime) =>
 | 
			
		||||
            accumulator || "keep-before-conversion" === pit.reason,
 | 
			
		||||
        false,
 | 
			
		||||
    ),
 | 
			
		||||
);
 | 
			
		||||
const isKeptBeforeConversion = computed<boolean>(() => {
 | 
			
		||||
    if ("point-in-times" in props.version) {
 | 
			
		||||
        return props.version["point-in-times"].reduce(
 | 
			
		||||
            (accumulator: boolean, pit: StoredObjectPointInTime) =>
 | 
			
		||||
                accumulator || "keep-before-conversion" === pit.reason,
 | 
			
		||||
            false,
 | 
			
		||||
        );
 | 
			
		||||
    } else {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const isRestored = computed<boolean>(
 | 
			
		||||
    () => props.version.version > 0 && null !== props.version["from-restored"],
 | 
			
		||||
@@ -90,11 +94,11 @@ const classes = computed<{
 | 
			
		||||
        <div class="col-12">
 | 
			
		||||
            <file-icon :type="version.type"></file-icon>
 | 
			
		||||
            <span
 | 
			
		||||
                ><strong>#{{ version.version + 1 }}</strong></span
 | 
			
		||||
                ><strong> #{{ version.version + 1 }} </strong></span
 | 
			
		||||
            >
 | 
			
		||||
            <template
 | 
			
		||||
                v-if="version.createdBy !== null && version.createdAt !== null"
 | 
			
		||||
                ><strong v-if="version.version == 0">Créé par</strong
 | 
			
		||||
                ><strong v-if="version.version == 0">créé par</strong
 | 
			
		||||
                ><strong v-else>modifié par</strong>
 | 
			
		||||
                <span class="badge-user"
 | 
			
		||||
                    ><UserRenderBoxBadge
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@ License * along with this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
{{ encore_entry_link_tags("mod_document_action_buttons_group") }}
 | 
			
		||||
{% endblock %} {% block content %}
 | 
			
		||||
 | 
			
		||||
<div class="col-md-10 col-xxl">
 | 
			
		||||
<div class="document-list">
 | 
			
		||||
    <h1>
 | 
			
		||||
        {{ 'Documents for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }}
 | 
			
		||||
    </h1>
 | 
			
		||||
 
 | 
			
		||||
@@ -3,54 +3,56 @@
 | 
			
		||||
{% import '@ChillPerson/Macro/updatedBy.html.twig' as mmm %}
 | 
			
		||||
 | 
			
		||||
<div class="item-row">
 | 
			
		||||
    <div class="item-col" style="width: unset">
 | 
			
		||||
        {% if document.object.isPending %}
 | 
			
		||||
            <div class="badge text-bg-info" data-docgen-is-pending="{{ document.object.id }}">{{ 'docgen.Doc generation is pending'|trans }}</div>
 | 
			
		||||
        {% elseif document.object.isFailure %}
 | 
			
		||||
            <div class="badge text-bg-warning">{{ 'docgen.Doc generation failed'|trans }}</div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    <!-- person document or accompanying course document -->
 | 
			
		||||
    <div class="item-two-col-grid">
 | 
			
		||||
        <div class="title">
 | 
			
		||||
            {% if document.object.isPending %}
 | 
			
		||||
                <div class="badge text-bg-info" data-docgen-is-pending="{{ document.object.id }}">{{ 'docgen.Doc generation is pending'|trans }}</div>
 | 
			
		||||
            {% elseif document.object.isFailure %}
 | 
			
		||||
                <div class="badge text-bg-warning">{{ 'docgen.Doc generation failed'|trans }}</div>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
 | 
			
		||||
        {% if context == 'person' and accompanyingCourse is defined %}
 | 
			
		||||
            <div>
 | 
			
		||||
                    <span class="badge bg-primary">
 | 
			
		||||
                        <i class="fa fa-random"></i> {{ accompanyingCourse.id }}
 | 
			
		||||
                    </span> 
 | 
			
		||||
            <div class="denomination h2">
 | 
			
		||||
                {{ document.title|chill_print_or_message("No title") }}
 | 
			
		||||
            </div>
 | 
			
		||||
        {% elseif context == 'accompanying-period' and person is defined %}
 | 
			
		||||
            <div>
 | 
			
		||||
                    <span class="badge bg-primary">
 | 
			
		||||
                        {{  'Document from person %name%'|trans({ '%name%': document.person|chill_entity_render_string }) }}
 | 
			
		||||
                    </span> 
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        <div class="denomination h2">
 | 
			
		||||
            {{ document.title|chill_print_or_message("No title") }}
 | 
			
		||||
        </div>
 | 
			
		||||
        {% if document.object.type is not empty %}
 | 
			
		||||
            <div>
 | 
			
		||||
                {{ mm.mimeIcon(document.object.type) }}
 | 
			
		||||
            </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        <div>
 | 
			
		||||
            <p>{{ document.category.name|localize_translatable_string }}</p>
 | 
			
		||||
        </div>
 | 
			
		||||
        {% if document.object.hasTemplate %}
 | 
			
		||||
            <div>
 | 
			
		||||
                <p>{{ document.object.template.name|localize_translatable_string }}</p>
 | 
			
		||||
            </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="item-col">
 | 
			
		||||
        <div class="container">
 | 
			
		||||
            {% if document.date is not null %}
 | 
			
		||||
                <div class="dates row text-end">
 | 
			
		||||
                    <span>{{ document.date|format_date('short') }}</span>
 | 
			
		||||
            {% if document.object.type is not empty %}
 | 
			
		||||
                <div>
 | 
			
		||||
                    {{ mm.mimeIcon(document.object.type) }}
 | 
			
		||||
                </div>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            {% if document.category %}
 | 
			
		||||
                <div>
 | 
			
		||||
                    <p>{{ document.category.name|localize_translatable_string }}</p>
 | 
			
		||||
                </div>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            {% if document.object.hasTemplate %}
 | 
			
		||||
                <div>
 | 
			
		||||
                    <p>{{ document.object.template.name|localize_translatable_string }}</p>
 | 
			
		||||
                </div>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        </div>
 | 
			
		||||
        {% if document.date is not null %}
 | 
			
		||||
            <div class="aside">
 | 
			
		||||
                <div class="dates row text-end">
 | 
			
		||||
                    <span>{{ document.date|format_date('short') }}</span>
 | 
			
		||||
                </div>
 | 
			
		||||
                {% if context == 'person' and accompanyingCourse is defined %}
 | 
			
		||||
                    <div class="text-end">
 | 
			
		||||
                        <span class="badge bg-primary">
 | 
			
		||||
                            <i class="fa fa-random"></i> {{ accompanyingCourse.id }}
 | 
			
		||||
                        </span> 
 | 
			
		||||
                    </div>
 | 
			
		||||
                {% elseif context == 'accompanying-period' and person is defined %}
 | 
			
		||||
                    <div class="text-end">
 | 
			
		||||
                        <span class="badge bg-primary">
 | 
			
		||||
                             {{ document.person|chill_entity_render_string }}
 | 
			
		||||
                        </span> 
 | 
			
		||||
                    </div>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
            </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
{% if document.description is not empty %}
 | 
			
		||||
    <div class="item-row">
 | 
			
		||||
 
 | 
			
		||||
@@ -62,7 +62,15 @@ final readonly class RemoveOldVersionMessageHandler implements MessageHandlerInt
 | 
			
		||||
 | 
			
		||||
        $storedObject = $storedObjectVersion->getStoredObject();
 | 
			
		||||
 | 
			
		||||
        $this->storedObjectManager->delete($storedObjectVersion);
 | 
			
		||||
        if ($this->storedObjectManager->exists($storedObjectVersion)) {
 | 
			
		||||
            $this->storedObjectManager->delete($storedObjectVersion);
 | 
			
		||||
        } else {
 | 
			
		||||
            $this->logger->notice(
 | 
			
		||||
                self::LOG_PREFIX.'Stored object version does not exists any more.',
 | 
			
		||||
                ['storedObjectVersionName' => $storedObjectVersion->getFilename()],
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // to ensure an immediate deletion
 | 
			
		||||
        $this->entityManager->remove($storedObjectVersion);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -44,6 +44,7 @@ class RemoveOldVersionMessageHandlerTest extends TestCase
 | 
			
		||||
        $entityManager->expects($this->once())->method('clear');
 | 
			
		||||
 | 
			
		||||
        $storedObjectManager = $this->createMock(StoredObjectManagerInterface::class);
 | 
			
		||||
        $storedObjectManager->expects($this->once())->method('exists')->willReturn(true);
 | 
			
		||||
        $storedObjectManager->expects($this->once())->method('delete')->with($this->identicalTo($version));
 | 
			
		||||
 | 
			
		||||
        $handler = new RemoveOldVersionMessageHandler($storedObjectVersionRepository, new NullLogger(), $entityManager, $storedObjectManager, new MockClock());
 | 
			
		||||
@@ -51,6 +52,29 @@ class RemoveOldVersionMessageHandlerTest extends TestCase
 | 
			
		||||
        $handler(new RemoveOldVersionMessage(1));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testInvokeForVersionNotExisting(): void
 | 
			
		||||
    {
 | 
			
		||||
        $object = new StoredObject();
 | 
			
		||||
        $version = $object->registerVersion();
 | 
			
		||||
        $storedObjectVersionRepository = $this->createMock(StoredObjectVersionRepository::class);
 | 
			
		||||
        $storedObjectVersionRepository->expects($this->once())->method('find')
 | 
			
		||||
            ->with($this->identicalTo(1))
 | 
			
		||||
            ->willReturn($version);
 | 
			
		||||
 | 
			
		||||
        $entityManager = $this->createMock(EntityManagerInterface::class);
 | 
			
		||||
        $entityManager->expects($this->once())->method('remove')->with($this->identicalTo($version));
 | 
			
		||||
        $entityManager->expects($this->once())->method('flush');
 | 
			
		||||
        $entityManager->expects($this->once())->method('clear');
 | 
			
		||||
 | 
			
		||||
        $storedObjectManager = $this->createMock(StoredObjectManagerInterface::class);
 | 
			
		||||
        $storedObjectManager->expects($this->once())->method('exists')->willReturn(false);
 | 
			
		||||
        $storedObjectManager->expects($this->never())->method('delete')->with($this->identicalTo($version));
 | 
			
		||||
 | 
			
		||||
        $handler = new RemoveOldVersionMessageHandler($storedObjectVersionRepository, new NullLogger(), $entityManager, $storedObjectManager, new MockClock());
 | 
			
		||||
 | 
			
		||||
        $handler(new RemoveOldVersionMessage(1));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testInvokeWithStoredObjectToDelete(): void
 | 
			
		||||
    {
 | 
			
		||||
        $object = new StoredObject();
 | 
			
		||||
@@ -123,6 +147,6 @@ class DummyStoredObjectManager implements StoredObjectManagerInterface
 | 
			
		||||
 | 
			
		||||
    public function exists(StoredObject|StoredObjectVersion $document): bool
 | 
			
		||||
    {
 | 
			
		||||
        throw new \RuntimeException();
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -99,3 +99,30 @@ CHILL_ACCOMPANYING_COURSE_DOCUMENT_UPDATE: Modifier un document
 | 
			
		||||
entity_display_title:
 | 
			
		||||
    Document (n°%doc%): "Document (n°%doc%)"
 | 
			
		||||
    Doc for evaluation (n°%eval%): Document de l'évaluation n°%eval%
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# SIGNATURES
 | 
			
		||||
 | 
			
		||||
signatures:
 | 
			
		||||
    yes: Oui
 | 
			
		||||
    are_you_sure: Êtes-vous sûr·e?
 | 
			
		||||
    you_are_going_to_sign: Vous allez signer le document
 | 
			
		||||
    signature_confirmation: Confirmation de la signature
 | 
			
		||||
    sign: Signer
 | 
			
		||||
    choose_another_signature: Choisir une autre zone
 | 
			
		||||
    cancel: Annuler
 | 
			
		||||
    last_sign_zone: Zone de signature précédente
 | 
			
		||||
    next_sign_zone: Zone de signature suivante
 | 
			
		||||
    add_sign_zone: Ajouter une zone de signature
 | 
			
		||||
    click_on_document: Cliquer sur le document
 | 
			
		||||
    last_zone: Zone précédente
 | 
			
		||||
    next_zone: Zone suivante
 | 
			
		||||
    add_zone: Ajouter une zone
 | 
			
		||||
    another_zone: Autre zone
 | 
			
		||||
    electronic_signature_in_progress: Signature électronique en cours...
 | 
			
		||||
    loading: Chargement...
 | 
			
		||||
    remove_sign_zone: Enlever la zone
 | 
			
		||||
    return: Retour
 | 
			
		||||
    see_all_pages: Voir toutes les pages
 | 
			
		||||
    all_pages: Toutes les pages
 | 
			
		||||
    go_to_signature_unique: Aller à la zone de signature
 | 
			
		||||
 
 | 
			
		||||
@@ -66,11 +66,8 @@ class EntityToJsonTransformer implements DataTransformerInterface
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function denormalizeOne(array|string $item)
 | 
			
		||||
    private function denormalizeOne(array $item)
 | 
			
		||||
    {
 | 
			
		||||
        if ('me' === $item) {
 | 
			
		||||
            return $item;
 | 
			
		||||
        }
 | 
			
		||||
        if (!\array_key_exists('type', $item)) {
 | 
			
		||||
            throw new TransformationFailedException('the key "type" is missing on element');
 | 
			
		||||
        }
 | 
			
		||||
@@ -101,6 +98,5 @@ class EntityToJsonTransformer implements DataTransformerInterface
 | 
			
		||||
                'json',
 | 
			
		||||
                $context,
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,82 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Chill is a software for social workers
 | 
			
		||||
 *
 | 
			
		||||
 * For the full copyright and license information, please view
 | 
			
		||||
 * the LICENSE file that was distributed with this source code.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace Chill\MainBundle\Form\Type;
 | 
			
		||||
 | 
			
		||||
use Chill\MainBundle\Entity\User;
 | 
			
		||||
use Chill\MainBundle\Form\Type\DataTransformer\EntityToJsonTransformer;
 | 
			
		||||
use Symfony\Component\Form\AbstractType;
 | 
			
		||||
use Symfony\Component\Form\FormBuilderInterface;
 | 
			
		||||
use Symfony\Component\Form\FormInterface;
 | 
			
		||||
use Symfony\Component\Form\FormView;
 | 
			
		||||
use Symfony\Component\OptionsResolver\OptionsResolver;
 | 
			
		||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
 | 
			
		||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
 | 
			
		||||
use Symfony\Component\Serializer\SerializerInterface;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Pick user dymically, using vuejs module "AddPerson".
 | 
			
		||||
 *
 | 
			
		||||
 * Possible options:
 | 
			
		||||
 *
 | 
			
		||||
 * - `multiple`: pick one or more users
 | 
			
		||||
 * - `suggested`: a list of suggested users
 | 
			
		||||
 * - `suggest_myself`: append the current user to the list of suggested
 | 
			
		||||
 * - `as_id`: only the id will be set in the returned data
 | 
			
		||||
 * - `submit_on_adding_new_entity`: the browser will immediately submit the form when new users are checked
 | 
			
		||||
 */
 | 
			
		||||
class PickUserOrMeDynamicType extends AbstractType
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private readonly DenormalizerInterface $denormalizer,
 | 
			
		||||
        private readonly SerializerInterface $serializer,
 | 
			
		||||
        private readonly NormalizerInterface $normalizer,
 | 
			
		||||
    ) {}
 | 
			
		||||
 | 
			
		||||
    public function buildForm(FormBuilderInterface $builder, array $options)
 | 
			
		||||
    {
 | 
			
		||||
        $builder->addViewTransformer(new EntityToJsonTransformer($this->denormalizer, $this->serializer, $options['multiple'], 'user'));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function buildView(FormView $view, FormInterface $form, array $options)
 | 
			
		||||
    {
 | 
			
		||||
        $view->vars['multiple'] = $options['multiple'];
 | 
			
		||||
        $view->vars['types'] = ['user'];
 | 
			
		||||
        $view->vars['uniqid'] = uniqid('pick_user_or_me_dyn');
 | 
			
		||||
        $view->vars['suggested'] = [];
 | 
			
		||||
        $view->vars['as_id'] = true === $options['as_id'] ? '1' : '0';
 | 
			
		||||
        $view->vars['submit_on_adding_new_entity'] = true === $options['submit_on_adding_new_entity'] ? '1' : '0';
 | 
			
		||||
 | 
			
		||||
        foreach ($options['suggested'] as $user) {
 | 
			
		||||
            $view->vars['suggested'][] = $this->normalizer->normalize($user, 'json', ['groups' => 'read']);
 | 
			
		||||
        }
 | 
			
		||||
        //        $user = /* should come from context */ $options['context'];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function configureOptions(OptionsResolver $resolver)
 | 
			
		||||
    {
 | 
			
		||||
        $resolver
 | 
			
		||||
            ->setDefault('multiple', false)
 | 
			
		||||
            ->setAllowedTypes('multiple', ['bool'])
 | 
			
		||||
            ->setDefault('compound', false)
 | 
			
		||||
            ->setDefault('suggested', [])
 | 
			
		||||
            // if set to true, only the id will be set inside the content. The denormalization will not work.
 | 
			
		||||
            ->setDefault('as_id', false)
 | 
			
		||||
            ->setAllowedTypes('as_id', ['bool'])
 | 
			
		||||
            ->setDefault('submit_on_adding_new_entity', false)
 | 
			
		||||
            ->setAllowedTypes('submit_on_adding_new_entity', ['bool']);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getBlockPrefix()
 | 
			
		||||
    {
 | 
			
		||||
        return 'pick_entity_dynamic';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -33,6 +33,8 @@
 | 
			
		||||
 | 
			
		||||
@import './scss/hover.scss';
 | 
			
		||||
 | 
			
		||||
@import './scss/comment-editor.scss';
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *    BASE LAYOUT POSITION
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,39 @@
 | 
			
		||||
.comment-container {
 | 
			
		||||
    margin-top: 1.5rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.toggle-button {
 | 
			
		||||
    background-color: white;
 | 
			
		||||
    font-size: .8rem;
 | 
			
		||||
    text-decoration: none;
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    bottom: -10px;
 | 
			
		||||
    left: 20px;
 | 
			
		||||
    padding: 2px 6px;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    z-index: 10;
 | 
			
		||||
    transition: left 0.1s ease-in-out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rich-editor-active .toggle-button {
 | 
			
		||||
    bottom: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.editor-wrapper textarea {
 | 
			
		||||
    resize: vertical;
 | 
			
		||||
    min-height: 100px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.editor-container {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.editor-wrapper {
 | 
			
		||||
    position: relative;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.hidden-textarea {
 | 
			
		||||
    display: none;
 | 
			
		||||
}
 | 
			
		||||
@@ -25,7 +25,34 @@ div.flex-table {
 | 
			
		||||
            div.item-col:last-child {
 | 
			
		||||
                display: flex;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            div.item-two-col-grid {
 | 
			
		||||
                display: grid;
 | 
			
		||||
                width: 100%;
 | 
			
		||||
                justify-content: stretch;
 | 
			
		||||
 | 
			
		||||
                @include media-breakpoint-up(lg) {
 | 
			
		||||
                    grid-template-areas:
 | 
			
		||||
                        "title aside";
 | 
			
		||||
                    grid-template-columns: 1fr minmax(8rem, 1fr);
 | 
			
		||||
                    column-gap: 0.5em;
 | 
			
		||||
                }
 | 
			
		||||
                @include media-breakpoint-down(lg) {
 | 
			
		||||
                    grid-template-areas:
 | 
			
		||||
                        "aside"
 | 
			
		||||
                        "title";
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                & > div.title {
 | 
			
		||||
                    grid-area: title;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                & > div.aside {
 | 
			
		||||
                    grid-area: aside;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    h2, h3, h4, dl, p {
 | 
			
		||||
 
 | 
			
		||||
@@ -8,10 +8,10 @@ import {
 | 
			
		||||
    Heading,
 | 
			
		||||
    Link,
 | 
			
		||||
    List,
 | 
			
		||||
} from "ckeditor5";
 | 
			
		||||
import coreTranslations from "ckeditor5/translations/fr.js";
 | 
			
		||||
} from 'ckeditor5';
 | 
			
		||||
import coreTranslations from 'ckeditor5/translations/fr.js';
 | 
			
		||||
 | 
			
		||||
import "ckeditor5/ckeditor5.css";
 | 
			
		||||
import 'ckeditor5/ckeditor5.css';
 | 
			
		||||
 | 
			
		||||
import "./index.scss";
 | 
			
		||||
 | 
			
		||||
@@ -41,6 +41,8 @@ export default {
 | 
			
		||||
            "redo",
 | 
			
		||||
        ],
 | 
			
		||||
    },
 | 
			
		||||
    translations: [coreTranslations],
 | 
			
		||||
    translations: [
 | 
			
		||||
        coreTranslations
 | 
			
		||||
    ],
 | 
			
		||||
    licenseKey: "GPL",
 | 
			
		||||
};
 | 
			
		||||
} ;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,23 @@
 | 
			
		||||
import config from "./editor_config";
 | 
			
		||||
import { ClassicEditor } from "ckeditor5";
 | 
			
		||||
import App from "../../vuejs/CommentEditor/App.vue"
 | 
			
		||||
import { createApp, reactive } from "vue";
 | 
			
		||||
 | 
			
		||||
const ckeditorFields: NodeListOf<HTMLTextAreaElement> =
 | 
			
		||||
    document.querySelectorAll("textarea[ckeditor]");
 | 
			
		||||
ckeditorFields.forEach((field: HTMLTextAreaElement): void => {
 | 
			
		||||
    ClassicEditor.create(field, config).catch((error) => {
 | 
			
		||||
        console.error(error.stack);
 | 
			
		||||
        throw error;
 | 
			
		||||
    });
 | 
			
		||||
    document.querySelectorAll("[id^='comment-app']");
 | 
			
		||||
 | 
			
		||||
const globalState = reactive({
 | 
			
		||||
    isSimple: localStorage.getItem('editorMode') === 'simple'
 | 
			
		||||
});
 | 
			
		||||
window.addEventListener('storage', () => {
 | 
			
		||||
    globalState.isSimple = localStorage.getItem('editorMode') === 'simple';
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
ckeditorFields.forEach((field: HTMLTextAreaElement): void => {
 | 
			
		||||
    const app = createApp(App,{
 | 
			
		||||
        fieldName: field.dataset.fieldName,
 | 
			
		||||
        template: `<app></app>`
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    app.provide('globalState', globalState)
 | 
			
		||||
        .component("app", App)
 | 
			
		||||
        .mount(field);
 | 
			
		||||
});
 | 
			
		||||
//Fields.push.apply(Fields, document.querySelectorAll('.cf-fields textarea'));
 | 
			
		||||
 
 | 
			
		||||
@@ -10,13 +10,12 @@ let appsPerInput = new Map();
 | 
			
		||||
 | 
			
		||||
function loadDynamicPicker(element) {
 | 
			
		||||
  let apps = element.querySelectorAll('[data-module="pick-dynamic"]');
 | 
			
		||||
  let suggested;
 | 
			
		||||
  let as_id;
 | 
			
		||||
  let submit_on_adding_new_entity;
 | 
			
		||||
  let label;
 | 
			
		||||
 | 
			
		||||
  apps.forEach(function (el) {
 | 
			
		||||
    let suggested;
 | 
			
		||||
    let as_id;
 | 
			
		||||
    let submit_on_adding_new_entity;
 | 
			
		||||
    let label;
 | 
			
		||||
    let isCurrentUserPicker;
 | 
			
		||||
    const isMultiple = parseInt(el.dataset.multiple) === 1,
 | 
			
		||||
      uniqId = el.dataset.uniqid,
 | 
			
		||||
      input = element.querySelector(
 | 
			
		||||
@@ -27,13 +26,12 @@ function loadDynamicPicker(element) {
 | 
			
		||||
        ? JSON.parse(input.value)
 | 
			
		||||
        : input.value === "[]" || input.value === ""
 | 
			
		||||
          ? null
 | 
			
		||||
          : [JSON.parse(input.value)];
 | 
			
		||||
    suggested = JSON.parse(el.dataset.suggested);
 | 
			
		||||
    as_id = parseInt(el.dataset.asId) === 1;
 | 
			
		||||
    submit_on_adding_new_entity =
 | 
			
		||||
      parseInt(el.dataset.submitOnAddingNewEntity) === 1;
 | 
			
		||||
    label = el.dataset.label;
 | 
			
		||||
    isCurrentUserPicker = uniqId.startsWith("pick_user_or_me_dyn");
 | 
			
		||||
          : [JSON.parse(input.value)],
 | 
			
		||||
      suggested = JSON.parse(el.dataset.suggested),
 | 
			
		||||
      as_id = parseInt(el.dataset.asId) === 1,
 | 
			
		||||
      submit_on_adding_new_entity =
 | 
			
		||||
        parseInt(el.dataset.submitOnAddingNewEntity) === 1,
 | 
			
		||||
      label = el.dataset.label;
 | 
			
		||||
 | 
			
		||||
    if (!isMultiple) {
 | 
			
		||||
      if (input.value === "[]") {
 | 
			
		||||
@@ -50,7 +48,6 @@ function loadDynamicPicker(element) {
 | 
			
		||||
        ':uniqid="uniqid" ' +
 | 
			
		||||
        ':suggested="notPickedSuggested" ' +
 | 
			
		||||
        ':label="label" ' +
 | 
			
		||||
        ':isCurrentUserPicker="isCurrentUserPicker" ' +
 | 
			
		||||
        '@addNewEntity="addNewEntity" ' +
 | 
			
		||||
        '@removeEntity="removeEntity" ' +
 | 
			
		||||
        '@addNewEntityProcessEnded="addNewEntityProcessEnded"' +
 | 
			
		||||
@@ -68,7 +65,6 @@ function loadDynamicPicker(element) {
 | 
			
		||||
          as_id,
 | 
			
		||||
          submit_on_adding_new_entity,
 | 
			
		||||
          label,
 | 
			
		||||
          isCurrentUserPicker,
 | 
			
		||||
        };
 | 
			
		||||
      },
 | 
			
		||||
      computed: {
 | 
			
		||||
@@ -97,8 +93,7 @@ function loadDynamicPicker(element) {
 | 
			
		||||
                const ids = this.picked.map((el) => el.id);
 | 
			
		||||
                input.value = ids.join(",");
 | 
			
		||||
              }
 | 
			
		||||
              console.log(this.picked);
 | 
			
		||||
              // console.log(entity);
 | 
			
		||||
              console.log(entity);
 | 
			
		||||
            }
 | 
			
		||||
          } else {
 | 
			
		||||
            if (
 | 
			
		||||
 
 | 
			
		||||
@@ -1,45 +0,0 @@
 | 
			
		||||
import { createApp } from "vue";
 | 
			
		||||
import OpenWopiLink from "ChillMainAssets/vuejs/_components/OpenWopiLink";
 | 
			
		||||
import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n";
 | 
			
		||||
 | 
			
		||||
const i18n = _createI18n({});
 | 
			
		||||
 | 
			
		||||
//TODO move to chillDocStore or ChillWopi
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
tags to load module:
 | 
			
		||||
 | 
			
		||||
<span data-module="wopi-link"
 | 
			
		||||
    data-wopi-url="{{ path('chill_wopi_file_edit', {'fileId': document.uuid}) }}"
 | 
			
		||||
    data-doc-type="{{ document.type|e('html_attr') }}"
 | 
			
		||||
    data-options="{{ options|json_encode }}"
 | 
			
		||||
        ></span>
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
window.addEventListener("DOMContentLoaded", function (e) {
 | 
			
		||||
  document
 | 
			
		||||
    .querySelectorAll('span[data-module="wopi-link"]')
 | 
			
		||||
    .forEach(function (el) {
 | 
			
		||||
      createApp({
 | 
			
		||||
        template:
 | 
			
		||||
          '<open-wopi-link :wopiUrl="wopiUrl" :type="type" :options="options"></open-wopi-link>',
 | 
			
		||||
        components: {
 | 
			
		||||
          OpenWopiLink,
 | 
			
		||||
        },
 | 
			
		||||
        data() {
 | 
			
		||||
          return {
 | 
			
		||||
            wopiUrl: el.dataset.wopiUrl,
 | 
			
		||||
            type: el.dataset.docType,
 | 
			
		||||
            options:
 | 
			
		||||
              el.dataset.options !== "null"
 | 
			
		||||
                ? JSON.parse(el.dataset.options)
 | 
			
		||||
                : {},
 | 
			
		||||
          };
 | 
			
		||||
        },
 | 
			
		||||
      })
 | 
			
		||||
        .use(i18n)
 | 
			
		||||
        .mount(el);
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
@@ -0,0 +1,36 @@
 | 
			
		||||
<template>
 | 
			
		||||
    <div>
 | 
			
		||||
        <div>
 | 
			
		||||
            <comment-editor
 | 
			
		||||
                :isSimple="globalState.isSimple"
 | 
			
		||||
                :fieldName="fieldName"
 | 
			
		||||
                @toggle="toggleEditorMode"
 | 
			
		||||
                ></comment-editor>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { defineComponent, inject } from 'vue';
 | 
			
		||||
import CommentEditor from "../CommentEditor/component/CommentEditor.vue";
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
    name: "App",
 | 
			
		||||
    components: { CommentEditor },
 | 
			
		||||
    props: {
 | 
			
		||||
        fieldName: String
 | 
			
		||||
    },
 | 
			
		||||
    setup() {
 | 
			
		||||
        const globalState = inject('globalState');
 | 
			
		||||
        const toggleEditorMode = () => {
 | 
			
		||||
            globalState.isSimple = !globalState.isSimple;
 | 
			
		||||
            localStorage.setItem('editorMode', globalState.isSimple ? 'simple' : 'rich');
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            globalState,
 | 
			
		||||
            toggleEditorMode,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
@@ -0,0 +1,58 @@
 | 
			
		||||
<template>
 | 
			
		||||
    <div :class="{'editor-container': true, 'rich-editor-active': !isSimple}">
 | 
			
		||||
        <div v-if="!isSimple" class="editor-wrapper">
 | 
			
		||||
            <ckeditor
 | 
			
		||||
                :name="fieldName"
 | 
			
		||||
                :editor="classicEditor"
 | 
			
		||||
                :config="editorConfig"
 | 
			
		||||
                v-model.lazy="content"
 | 
			
		||||
                tag-name="textarea"
 | 
			
		||||
            />
 | 
			
		||||
        </div>
 | 
			
		||||
        <div v-else class="editor-wrapper">
 | 
			
		||||
            <textarea
 | 
			
		||||
                v-model.lazy="content"
 | 
			
		||||
                :name="fieldName"
 | 
			
		||||
                class="form-control"
 | 
			
		||||
            ></textarea>
 | 
			
		||||
        </div>
 | 
			
		||||
        <a @click="toggleSimpleEditor" class="toggle-button">{{ isSimple ? "rich" : "simple" }}</a>
 | 
			
		||||
    </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { defineComponent, ref, computed, toRefs } from 'vue';
 | 
			
		||||
import { Ckeditor } from "@ckeditor/ckeditor5-vue";
 | 
			
		||||
import classicEditorConfig from "ChillMainAssets/module/ckeditor5/editor_config";
 | 
			
		||||
import { ClassicEditor } from "ckeditor5";
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
    name: "CommentEditor",
 | 
			
		||||
    components: {
 | 
			
		||||
        ckeditor: Ckeditor,
 | 
			
		||||
    },
 | 
			
		||||
    props: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        isSimple: Boolean,
 | 
			
		||||
        fieldName: String,
 | 
			
		||||
    },
 | 
			
		||||
    setup(props, { emit }) {
 | 
			
		||||
        const { isSimple } = toRefs(props);
 | 
			
		||||
        const content = ref("");
 | 
			
		||||
        const classicEditor = ClassicEditor;
 | 
			
		||||
        const editorConfig = classicEditorConfig;
 | 
			
		||||
 | 
			
		||||
        const toggleSimpleEditor = () => {
 | 
			
		||||
            emit("toggle");
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            isSimple,
 | 
			
		||||
            content,
 | 
			
		||||
            classicEditor,
 | 
			
		||||
            editorConfig,
 | 
			
		||||
            toggleSimpleEditor,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
@@ -0,0 +1,14 @@
 | 
			
		||||
import {personMessages} from "ChillPersonAssets/vuejs/_js/i18n";
 | 
			
		||||
import {calendarUserSelectorMessages} from "ChillCalendarAssets/vuejs/_components/CalendarUserSelector/js/i18n";
 | 
			
		||||
import {activityMessages} from "ChillActivityAssets/vuejs/Activity/i18n";
 | 
			
		||||
 | 
			
		||||
const appMessages = {
 | 
			
		||||
  fr: {
 | 
			
		||||
    mode: {
 | 
			
		||||
      simple: "Editeur simple",
 | 
			
		||||
      rich: "Editeur riche"
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export { appMessages };
 | 
			
		||||
@@ -1,25 +1,10 @@
 | 
			
		||||
<template>
 | 
			
		||||
    <ul :class="listClasses" v-if="picked.length && displayPicked">
 | 
			
		||||
        <li v-for="p in picked" @click="removeEntity(p)" :key="p.type + p.id">
 | 
			
		||||
            <span
 | 
			
		||||
                v-if="'me' === p"
 | 
			
		||||
                class="chill_denomination current-user updatedBy"
 | 
			
		||||
                >{{ trans(USER_CURRENT_USER) }}</span
 | 
			
		||||
            >
 | 
			
		||||
            <span v-else class="chill_denomination">{{ p.text }}</span>
 | 
			
		||||
            <span class="chill_denomination">{{ p.text }}</span>
 | 
			
		||||
        </li>
 | 
			
		||||
    </ul>
 | 
			
		||||
    <ul class="record_actions">
 | 
			
		||||
        <li v-if="isCurrentUserPicker" class="btn btn-sm btn-misc">
 | 
			
		||||
            <label class="flex items-center gap-2">
 | 
			
		||||
                <input
 | 
			
		||||
                    ref="itsMeCheckbox"
 | 
			
		||||
                    :type="multiple ? 'checkbox' : 'radio'"
 | 
			
		||||
                    @change="selectItsMe"
 | 
			
		||||
                />
 | 
			
		||||
                {{ trans(USER_CURRENT_USER) }}
 | 
			
		||||
            </label>
 | 
			
		||||
        </li>
 | 
			
		||||
        <li class="add-persons">
 | 
			
		||||
            <add-persons
 | 
			
		||||
                :options="addPersonsOptions"
 | 
			
		||||
@@ -39,83 +24,119 @@
 | 
			
		||||
    </ul>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
import { ref, computed } from "vue";
 | 
			
		||||
import AddPersons from "ChillPersonAssets/vuejs/_components/AddPersons.vue"; // eslint-disable-line
 | 
			
		||||
<script>
 | 
			
		||||
import AddPersons from "ChillPersonAssets/vuejs/_components/AddPersons.vue";
 | 
			
		||||
import { appMessages } from "./i18n";
 | 
			
		||||
import { trans, USER_CURRENT_USER } from "translator";
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
    multiple: Boolean,
 | 
			
		||||
    types: Array,
 | 
			
		||||
    picked: Array,
 | 
			
		||||
    uniqid: String,
 | 
			
		||||
    removableIfSet: { type: Boolean, default: true },
 | 
			
		||||
    displayPicked: { type: Boolean, default: true },
 | 
			
		||||
    suggested: { type: Array, default: () => [] },
 | 
			
		||||
    label: String,
 | 
			
		||||
    isCurrentUserPicker: { type: Boolean, default: false },
 | 
			
		||||
});
 | 
			
		||||
export default {
 | 
			
		||||
    name: "PickEntity",
 | 
			
		||||
    props: {
 | 
			
		||||
        multiple: {
 | 
			
		||||
            type: Boolean,
 | 
			
		||||
            required: true,
 | 
			
		||||
        },
 | 
			
		||||
        types: {
 | 
			
		||||
            type: Array,
 | 
			
		||||
            required: true,
 | 
			
		||||
        },
 | 
			
		||||
        picked: {
 | 
			
		||||
            required: true,
 | 
			
		||||
        },
 | 
			
		||||
        uniqid: {
 | 
			
		||||
            type: String,
 | 
			
		||||
            required: true,
 | 
			
		||||
        },
 | 
			
		||||
        removableIfSet: {
 | 
			
		||||
            type: Boolean,
 | 
			
		||||
            default: true,
 | 
			
		||||
        },
 | 
			
		||||
        displayPicked: {
 | 
			
		||||
            // display picked entities.
 | 
			
		||||
            type: Boolean,
 | 
			
		||||
            default: true,
 | 
			
		||||
        },
 | 
			
		||||
        suggested: {
 | 
			
		||||
            type: Array,
 | 
			
		||||
            default: [],
 | 
			
		||||
        },
 | 
			
		||||
        label: {
 | 
			
		||||
            type: String,
 | 
			
		||||
            required: false,
 | 
			
		||||
        },
 | 
			
		||||
    },
 | 
			
		||||
    emits: ["addNewEntity", "removeEntity", "addNewEntityProcessEnded"],
 | 
			
		||||
    components: {
 | 
			
		||||
        AddPersons,
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            key: "",
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
        addPersonsOptions() {
 | 
			
		||||
            return {
 | 
			
		||||
                uniq: !this.multiple,
 | 
			
		||||
                type: this.types,
 | 
			
		||||
                priority: null,
 | 
			
		||||
                button: {
 | 
			
		||||
                    size: "btn-sm",
 | 
			
		||||
                    class: "btn-submit",
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
        translatedListOfTypes() {
 | 
			
		||||
            if (this.label !== "") {
 | 
			
		||||
                return this.label;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits([
 | 
			
		||||
    "addNewEntity",
 | 
			
		||||
    "removeEntity",
 | 
			
		||||
    "addNewEntityProcessEnded",
 | 
			
		||||
]);
 | 
			
		||||
            let trans = [];
 | 
			
		||||
            this.types.forEach((t) => {
 | 
			
		||||
                if (this.$props.multiple) {
 | 
			
		||||
                    trans.push(appMessages.fr.pick_entity[t].toLowerCase());
 | 
			
		||||
                } else {
 | 
			
		||||
                    trans.push(
 | 
			
		||||
                        appMessages.fr.pick_entity[t + "_one"].toLowerCase(),
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
const itsMeCheckbox = ref(null);
 | 
			
		||||
const addPersons = ref(null);
 | 
			
		||||
 | 
			
		||||
const addPersonsOptions = computed(() => ({
 | 
			
		||||
    uniq: !props.multiple,
 | 
			
		||||
    type: props.types,
 | 
			
		||||
    priority: null,
 | 
			
		||||
    button: { size: "btn-sm", class: "btn-submit" },
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const translatedListOfTypes = computed(() => {
 | 
			
		||||
    if (props.label) return props.label;
 | 
			
		||||
    let trans = props.types.map((t) =>
 | 
			
		||||
        props.multiple
 | 
			
		||||
            ? appMessages.fr.pick_entity[t].toLowerCase()
 | 
			
		||||
            : appMessages.fr.pick_entity[t + "_one"].toLowerCase(),
 | 
			
		||||
    );
 | 
			
		||||
    return props.multiple
 | 
			
		||||
        ? appMessages.fr.pick_entity.modal_title + trans.join(", ")
 | 
			
		||||
        : appMessages.fr.pick_entity.modal_title_one + trans.join(", ");
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const listClasses = computed(() => ({
 | 
			
		||||
    "list-suggest": true,
 | 
			
		||||
    "remove-items": props.removableIfSet,
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const selectItsMe = (event) =>
 | 
			
		||||
    event.target.checked ? addNewSuggested("me") : removeEntity("me");
 | 
			
		||||
 | 
			
		||||
const addNewSuggested = (entity) => {
 | 
			
		||||
    emit("addNewEntity", { entity });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const addNewEntity = ({ selected, modal }) => {
 | 
			
		||||
    selected.forEach((item) => emit("addNewEntity", { entity: item.result }));
 | 
			
		||||
    addPersons.value?.resetSearch();
 | 
			
		||||
    modal.showModal = false;
 | 
			
		||||
    emit("addNewEntityProcessEnded");
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const removeEntity = (entity) => {
 | 
			
		||||
    if (!props.removableIfSet) return;
 | 
			
		||||
    if (entity === "me" && itsMeCheckbox.value) {
 | 
			
		||||
        itsMeCheckbox.value.checked = false;
 | 
			
		||||
    }
 | 
			
		||||
    emit("removeEntity", { entity });
 | 
			
		||||
            if (this.$props.multiple) {
 | 
			
		||||
                return (
 | 
			
		||||
                    appMessages.fr.pick_entity.modal_title + trans.join(", ")
 | 
			
		||||
                );
 | 
			
		||||
            } else {
 | 
			
		||||
                return (
 | 
			
		||||
                    appMessages.fr.pick_entity.modal_title_one +
 | 
			
		||||
                    trans.join(", ")
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        listClasses() {
 | 
			
		||||
            return {
 | 
			
		||||
                "list-suggest": true,
 | 
			
		||||
                "remove-items": this.$props.removableIfSet,
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        addNewSuggested(entity) {
 | 
			
		||||
            this.$emit("addNewEntity", { entity: entity });
 | 
			
		||||
        },
 | 
			
		||||
        addNewEntity({ selected, modal }) {
 | 
			
		||||
            selected.forEach((item) => {
 | 
			
		||||
                this.$emit("addNewEntity", { entity: item.result });
 | 
			
		||||
            }, this);
 | 
			
		||||
            this.$refs.addPersons.resetSearch(); // to cast child method
 | 
			
		||||
            modal.showModal = false;
 | 
			
		||||
            this.$emit("addNewEntityProcessEnded");
 | 
			
		||||
        },
 | 
			
		||||
        removeEntity(entity) {
 | 
			
		||||
            if (!this.$props.removableIfSet) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            this.$emit("removeEntity", { entity: entity });
 | 
			
		||||
        },
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.current-user {
 | 
			
		||||
    color: var(--bs-body-color);
 | 
			
		||||
    background-color: var(--bs-chill-l-gray) !important;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -26,9 +26,9 @@
 | 
			
		||||
                trans(THIRDPARTY_CONTACT_OF)
 | 
			
		||||
            }}</span>
 | 
			
		||||
            <span v-else-if="props.entity.kind === 'company'">{{
 | 
			
		||||
                trans(THIRDPARTY_A_CONTACT)
 | 
			
		||||
                trans(THIRDPARTY_A_COMPANY)
 | 
			
		||||
            }}</span>
 | 
			
		||||
            <span v-else>{{ $t("thirdparty.contact") }}</span>
 | 
			
		||||
            <span v-else>{{ trans(THIRDPARTY_A_CONTACT) }}</span>
 | 
			
		||||
        </template>
 | 
			
		||||
    </span>
 | 
			
		||||
 | 
			
		||||
@@ -54,6 +54,7 @@ import {
 | 
			
		||||
    ACCEPTED_USERS,
 | 
			
		||||
    THIRDPARTY_A_CONTACT,
 | 
			
		||||
    THIRDPARTY_CONTACT_OF,
 | 
			
		||||
    THIRDPARTY_A_COMPANY,
 | 
			
		||||
    PERSON,
 | 
			
		||||
    THIRDPARTY,
 | 
			
		||||
} from "translator";
 | 
			
		||||
 
 | 
			
		||||
@@ -1,214 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
    <a
 | 
			
		||||
        v-if="isOpenDocument"
 | 
			
		||||
        class="btn"
 | 
			
		||||
        :class="[
 | 
			
		||||
            isChangeIcon ? 'change-icon' : '',
 | 
			
		||||
            isChangeClass ? options.changeClass : 'btn-wopilink',
 | 
			
		||||
        ]"
 | 
			
		||||
        @click="openModal"
 | 
			
		||||
    >
 | 
			
		||||
        <i v-if="isChangeIcon" class="fa me-2" :class="options.changeIcon"></i>
 | 
			
		||||
 | 
			
		||||
        <span v-if="!noText">
 | 
			
		||||
            {{ trans(WOPI_ONLINE_EDIT_DOCUMENT) }}
 | 
			
		||||
        </span>
 | 
			
		||||
    </a>
 | 
			
		||||
 | 
			
		||||
    <teleport to="body">
 | 
			
		||||
        <div class="wopi-frame" v-if="isOpenDocument">
 | 
			
		||||
            <modal
 | 
			
		||||
                v-if="modal.showModal"
 | 
			
		||||
                :modalDialogClass="modal.modalDialogClass"
 | 
			
		||||
                :hideFooter="true"
 | 
			
		||||
                @close="modal.showModal = false"
 | 
			
		||||
            >
 | 
			
		||||
                <template #header>
 | 
			
		||||
                    <img class="logo" :src="logo" height="45" />
 | 
			
		||||
                    <span class="ms-auto me-3">
 | 
			
		||||
                        <span v-if="options.title">{{ options.title }}</span>
 | 
			
		||||
                    </span>
 | 
			
		||||
                </template>
 | 
			
		||||
 | 
			
		||||
                <template #body>
 | 
			
		||||
                    <div v-if="loading" class="loading">
 | 
			
		||||
                        <i
 | 
			
		||||
                            class="fa fa-circle-o-notch fa-spin fa-3x"
 | 
			
		||||
                            :title="trans(WOPI_LOADING)"
 | 
			
		||||
                        ></i>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <iframe :src="this.wopiUrl" @load="loaded"></iframe>
 | 
			
		||||
                </template>
 | 
			
		||||
            </modal>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div v-else>
 | 
			
		||||
            <Modal
 | 
			
		||||
                v-if="modal.showModal"
 | 
			
		||||
                modalDialogClass="modal-sm"
 | 
			
		||||
                @close="modal.showModal = false"
 | 
			
		||||
            >
 | 
			
		||||
                <template v-slot:header>
 | 
			
		||||
                    <h3>{{ trans(WOPI_INVALID_TITLE) }}</h3>
 | 
			
		||||
                </template>
 | 
			
		||||
                <template v-slot:body>
 | 
			
		||||
                    <div class="alert alert-warning">
 | 
			
		||||
                        {{ trans(WOPI_ONLINE_EDIT_DOCUMENT) }}
 | 
			
		||||
                    </div>
 | 
			
		||||
                </template>
 | 
			
		||||
            </Modal>
 | 
			
		||||
        </div>
 | 
			
		||||
    </teleport>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
import { ref, computed } from "vue";
 | 
			
		||||
import {
 | 
			
		||||
    trans,
 | 
			
		||||
    WOPI_ONLINE_EDIT_DOCUMENT,
 | 
			
		||||
    WOPI_INVALID_TITLE,
 | 
			
		||||
    WOPI_LOADING,
 | 
			
		||||
} from "translator";
 | 
			
		||||
import Modal from "ChillMainAssets/vuejs/_components/Modal";
 | 
			
		||||
import logo from "ChillMainAssets/chill/img/logo-chill-sans-slogan_white.png";
 | 
			
		||||
 | 
			
		||||
// Props
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
    wopiUrl: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        required: true,
 | 
			
		||||
    },
 | 
			
		||||
    type: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        required: true,
 | 
			
		||||
    },
 | 
			
		||||
    options: {
 | 
			
		||||
        type: Object,
 | 
			
		||||
        required: false,
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// data
 | 
			
		||||
const modal = ref({
 | 
			
		||||
    showModal: false,
 | 
			
		||||
    modalDialogClass: "modal-fullscreen",
 | 
			
		||||
});
 | 
			
		||||
const loading = ref(false);
 | 
			
		||||
 | 
			
		||||
// MIME types
 | 
			
		||||
const mime = [
 | 
			
		||||
    // TODO temporary hardcoded. to be replaced by twig extension or a collabora server query
 | 
			
		||||
    "application/clarisworks",
 | 
			
		||||
    "application/coreldraw",
 | 
			
		||||
    "application/macwriteii",
 | 
			
		||||
    "application/msword",
 | 
			
		||||
    "application/pdf",
 | 
			
		||||
    "application/vnd.lotus-1-2-3",
 | 
			
		||||
    "application/vnd.ms-excel",
 | 
			
		||||
    "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
 | 
			
		||||
    "application/vnd.ms-excel.sheet.macroEnabled.12",
 | 
			
		||||
    "application/vnd.ms-excel.template.macroEnabled.12",
 | 
			
		||||
    "application/vnd.ms-powerpoint",
 | 
			
		||||
    "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
 | 
			
		||||
    "application/vnd.ms-powerpoint.template.macroEnabled.12",
 | 
			
		||||
    "application/vnd.ms-visio.drawing",
 | 
			
		||||
    "application/vnd.ms-word.document.macroEnabled.12",
 | 
			
		||||
    "application/vnd.ms-word.template.macroEnabled.12",
 | 
			
		||||
    "application/vnd.ms-works",
 | 
			
		||||
    "application/vnd.oasis.opendocument.chart",
 | 
			
		||||
    "application/vnd.oasis.opendocument.formula",
 | 
			
		||||
    "application/vnd.oasis.opendocument.graphics",
 | 
			
		||||
    "application/vnd.oasis.opendocument.graphics-flat-xml",
 | 
			
		||||
    "application/vnd.oasis.opendocument.graphics-template",
 | 
			
		||||
    "application/vnd.oasis.opendocument.presentation",
 | 
			
		||||
    "application/vnd.oasis.opendocument.presentation-flat-xml",
 | 
			
		||||
    "application/vnd.oasis.opendocument.presentation-template",
 | 
			
		||||
    "application/vnd.oasis.opendocument.spreadsheet",
 | 
			
		||||
    "application/vnd.oasis.opendocument.spreadsheet-flat-xml",
 | 
			
		||||
    "application/vnd.oasis.opendocument.spreadsheet-template",
 | 
			
		||||
    "application/vnd.oasis.opendocument.text",
 | 
			
		||||
    "application/vnd.oasis.opendocument.text-flat-xml",
 | 
			
		||||
    "application/vnd.oasis.opendocument.text-master",
 | 
			
		||||
    "application/vnd.oasis.opendocument.text-master-template",
 | 
			
		||||
    "application/vnd.oasis.opendocument.text-template",
 | 
			
		||||
    "application/vnd.oasis.opendocument.text-web",
 | 
			
		||||
    "application/vnd.openxmlformats-officedocument.presentationml.presentation",
 | 
			
		||||
    "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
 | 
			
		||||
    "application/vnd.openxmlformats-officedocument.presentationml.template",
 | 
			
		||||
    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
 | 
			
		||||
    "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
 | 
			
		||||
    "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
 | 
			
		||||
    "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
 | 
			
		||||
    "application/vnd.sun.xml.calc",
 | 
			
		||||
    "application/vnd.sun.xml.calc.template",
 | 
			
		||||
    "application/vnd.sun.xml.chart",
 | 
			
		||||
    "application/vnd.sun.xml.draw",
 | 
			
		||||
    "application/vnd.sun.xml.draw.template",
 | 
			
		||||
    "application/vnd.sun.xml.impress",
 | 
			
		||||
    "application/vnd.sun.xml.impress.template",
 | 
			
		||||
    "application/vnd.sun.xml.math",
 | 
			
		||||
    "application/vnd.sun.xml.writer",
 | 
			
		||||
    "application/vnd.sun.xml.writer.global",
 | 
			
		||||
    "application/vnd.sun.xml.writer.template",
 | 
			
		||||
    "application/vnd.visio",
 | 
			
		||||
    "application/vnd.visio2013",
 | 
			
		||||
    "application/vnd.wordperfect",
 | 
			
		||||
    "application/x-abiword",
 | 
			
		||||
    "application/x-aportisdoc",
 | 
			
		||||
    "application/x-dbase",
 | 
			
		||||
    "application/x-dif-document",
 | 
			
		||||
    "application/x-fictionbook+xml",
 | 
			
		||||
    "application/x-gnumeric",
 | 
			
		||||
    "application/x-hwp",
 | 
			
		||||
    "application/x-iwork-keynote-sffkey",
 | 
			
		||||
    "application/x-iwork-numbers-sffnumbers",
 | 
			
		||||
    "application/x-iwork-pages-sffpages",
 | 
			
		||||
    "application/x-mspublisher",
 | 
			
		||||
    "application/x-mswrite",
 | 
			
		||||
    "application/x-pagemaker",
 | 
			
		||||
    "application/x-sony-bbeb",
 | 
			
		||||
    "application/x-t602",
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
// Computed
 | 
			
		||||
const isOpenDocument = computed(() => mime.includes(props.type));
 | 
			
		||||
 | 
			
		||||
const noText = computed(() => props.options?.noText === true);
 | 
			
		||||
 | 
			
		||||
const isChangeIcon = computed(() => !!props.options?.changeIcon);
 | 
			
		||||
 | 
			
		||||
const isChangeClass = computed(() => !!props.options?.changeClass);
 | 
			
		||||
 | 
			
		||||
// Methods
 | 
			
		||||
const openModal = () => {
 | 
			
		||||
    loading.value = true;
 | 
			
		||||
    modal.value.showModal = true;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const loaded = () => {
 | 
			
		||||
    loading.value = false;
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss">
 | 
			
		||||
div.wopi-frame {
 | 
			
		||||
    div.modal-header {
 | 
			
		||||
        border-bottom: 0;
 | 
			
		||||
        background-color: var(--bs-primary);
 | 
			
		||||
        color: white;
 | 
			
		||||
    }
 | 
			
		||||
    div.modal-body {
 | 
			
		||||
        padding: 0;
 | 
			
		||||
        overflow-y: unset !important;
 | 
			
		||||
        iframe {
 | 
			
		||||
            height: 100%;
 | 
			
		||||
            width: 100%;
 | 
			
		||||
        }
 | 
			
		||||
        div.loading {
 | 
			
		||||
            position: absolute;
 | 
			
		||||
            color: var(--bs-chill-gray);
 | 
			
		||||
            top: calc(50% - 30px);
 | 
			
		||||
            left: calc(50% - 30px);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@@ -54,6 +54,11 @@ const messages = {
 | 
			
		||||
            residential_address: "Adresse de résidence",
 | 
			
		||||
            located_at: "réside chez",
 | 
			
		||||
        },
 | 
			
		||||
        comment: {
 | 
			
		||||
            label: "Commentaire",
 | 
			
		||||
            editor_simple: "Simple",
 | 
			
		||||
            editor_rich: "Riche"
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -136,6 +136,59 @@
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<h2>Fix the title in the flex table</h2>
 | 
			
		||||
 | 
			
		||||
<p>This will fix the layout of the row, with a "title" element, and an aside element. Using <code>css grid</code>, this is quite safe and won't overflow</p>
 | 
			
		||||
 | 
			
		||||
<xmp>
 | 
			
		||||
    <div class="flex-table">
 | 
			
		||||
        <div class="item-bloc">
 | 
			
		||||
            <div class="item-row">
 | 
			
		||||
                <div class="item-two-col-grid">
 | 
			
		||||
                    <div class="title">This is my title</div>
 | 
			
		||||
                    <div class="aside">Aside value</div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="item-bloc">
 | 
			
		||||
            <div class="item-row">
 | 
			
		||||
                <div class="item-two-col-grid">
 | 
			
		||||
                    <div class="title">
 | 
			
		||||
                        <div><h3>This is my title, which can be very long and take a lot of place. But it is wrapped successfully, and won't disturb the placement of the aside block</h3></div>
 | 
			
		||||
                        <div>This is a second line</div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="aside">Aside value</div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</xmp>
 | 
			
		||||
 | 
			
		||||
    <p>will render:</p>
 | 
			
		||||
 | 
			
		||||
    <div class="flex-table">
 | 
			
		||||
        <div class="item-bloc">
 | 
			
		||||
            <div class="item-row">
 | 
			
		||||
                <div class="item-two-col-grid">
 | 
			
		||||
                    <div class="title">This is my title</div>
 | 
			
		||||
                    <div class="aside">Aside value</div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="item-bloc">
 | 
			
		||||
            <div class="item-row">
 | 
			
		||||
                <div class="item-two-col-grid">
 | 
			
		||||
                    <div class="title">
 | 
			
		||||
                        <div><h3>This is my title, which can be very long and take a lot of place. But it is wrapped successfully, and won't disturb the placement of the aside block</h3></div>
 | 
			
		||||
                        <div>This is a second line</div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="aside">Aside value</div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<h2>Wrap-list</h2>
 | 
			
		||||
<p>Une liste inline qui s'aligne, puis glisse sous son titre.</p>
 | 
			
		||||
<div class="wrap-list debug">
 | 
			
		||||
@@ -392,4 +445,12 @@ Toutes les classes btn-* de bootstrap sont fonctionnelles
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div class="row">
 | 
			
		||||
    <h1>Badges</h1>
 | 
			
		||||
 | 
			
		||||
    <span class="badge-accompanying-work-type-simple">Action d'accompagnement</span>
 | 
			
		||||
    <span class="badge-activity-type-simple">Type d'échange</span>
 | 
			
		||||
    <span class="badge-calendar-simple">Rendez-vous</span>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -214,7 +214,9 @@
 | 
			
		||||
 | 
			
		||||
{% block private_comment_widget %}
 | 
			
		||||
    {% for entry in form %}
 | 
			
		||||
        {{ form_widget(entry) }}
 | 
			
		||||
        <div id="comment-app-{{ form.vars.id }}" data-field-name="{{ form.vars.full_name }}">
 | 
			
		||||
            {{ form_widget(entry, { attr: { ckeditor: 'true' } }) }}
 | 
			
		||||
        </div>
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
@@ -224,7 +226,9 @@
 | 
			
		||||
 | 
			
		||||
{% block comment_widget %}
 | 
			
		||||
    {% for entry in form %}
 | 
			
		||||
        {{ form_widget(entry) }}
 | 
			
		||||
        <div id="comment-app-{{ form.vars.id }}" data-field-name="{{ form.vars.full_name }}">
 | 
			
		||||
            {{ form_widget(entry, { attr: { ckeditor: 'true' } }) }}
 | 
			
		||||
        </div>
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
{% endblock comment_widget %}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,6 @@
 | 
			
		||||
    {{ encore_entry_script_tags('mod_pickentity_type') }}
 | 
			
		||||
    {{ encore_entry_script_tags('mod_entity_workflow_subscribe') }}
 | 
			
		||||
    {{ encore_entry_script_tags('page_workflow_show') }}
 | 
			
		||||
    {{ encore_entry_script_tags('mod_wopi_link') }}
 | 
			
		||||
    {{ encore_entry_script_tags('mod_document_action_buttons_group') }}
 | 
			
		||||
    {{ encore_entry_script_tags('mod_workflow_attachment') }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
@@ -19,7 +18,6 @@
 | 
			
		||||
    {{ encore_entry_link_tags('mod_pickentity_type') }}
 | 
			
		||||
    {{ encore_entry_link_tags('mod_entity_workflow_subscribe') }}
 | 
			
		||||
    {{ encore_entry_link_tags('page_workflow_show') }}
 | 
			
		||||
    {{ encore_entry_link_tags('mod_wopi_link') }}
 | 
			
		||||
    {{ encore_entry_link_tags('mod_document_action_buttons_group') }}
 | 
			
		||||
    {{ encore_entry_link_tags('mod_workflow_attachment') }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,8 @@ use Symfony\Component\Workflow\Transition;
 | 
			
		||||
#[AsMessageHandler]
 | 
			
		||||
final readonly class CancelStaleWorkflowHandler
 | 
			
		||||
{
 | 
			
		||||
    private const LOG_PREFIX = '[CancelStaleWorkflowHandler] ';
 | 
			
		||||
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        private EntityWorkflowRepository $workflowRepository,
 | 
			
		||||
        private Registry $registry,
 | 
			
		||||
@@ -40,13 +42,13 @@ final readonly class CancelStaleWorkflowHandler
 | 
			
		||||
 | 
			
		||||
        $workflow = $this->workflowRepository->find($message->getWorkflowId());
 | 
			
		||||
        if (null === $workflow) {
 | 
			
		||||
            $this->logger->alert('Workflow was not found!', [$workflowId]);
 | 
			
		||||
            $this->logger->alert(self::LOG_PREFIX.'Workflow was not found!', ['entityWorkflowId' => $workflowId]);
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (false === $workflow->isStaledAt($olderThanDate)) {
 | 
			
		||||
            $this->logger->alert('Workflow has transitioned in the meantime.', [$workflowId]);
 | 
			
		||||
            $this->logger->alert(self::LOG_PREFIX.'Workflow has transitioned in the meantime.', ['entityWorkflowId' => $workflowId]);
 | 
			
		||||
 | 
			
		||||
            throw new UnrecoverableMessageHandlingException('the workflow is not staled any more');
 | 
			
		||||
        }
 | 
			
		||||
@@ -67,14 +69,14 @@ final readonly class CancelStaleWorkflowHandler
 | 
			
		||||
                    'transitionAt' => $this->clock->now(),
 | 
			
		||||
                    'transition' => $transition->getName(),
 | 
			
		||||
                ]);
 | 
			
		||||
                $this->logger->info('EntityWorkflow has been cancelled automatically.', [$workflowId]);
 | 
			
		||||
                $this->logger->info(self::LOG_PREFIX.'EntityWorkflow has been cancelled automatically.', ['entityWorkflowId' => $workflowId]);
 | 
			
		||||
                $transitionApplied = true;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!$transitionApplied) {
 | 
			
		||||
            $this->logger->error('No valid transition found for EntityWorkflow.', [$workflowId]);
 | 
			
		||||
            $this->logger->error(self::LOG_PREFIX.'No valid transition found for EntityWorkflow.', ['entityWorkflowId' => $workflowId]);
 | 
			
		||||
            throw new UnrecoverableMessageHandlingException(sprintf('No valid transition found for EntityWorkflow %d.', $workflowId));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -86,10 +86,6 @@ module.exports = function (encore, entries) {
 | 
			
		||||
    "mod_entity_workflow_pick",
 | 
			
		||||
    __dirname + "/Resources/public/module/entity-workflow-pick/index.js",
 | 
			
		||||
  );
 | 
			
		||||
  encore.addEntry(
 | 
			
		||||
    "mod_wopi_link",
 | 
			
		||||
    __dirname + "/Resources/public/module/wopi-link/index.js",
 | 
			
		||||
  );
 | 
			
		||||
  encore.addEntry(
 | 
			
		||||
    "mod_pick_postal_code",
 | 
			
		||||
    __dirname + "/Resources/public/module/pick-postal-code/index.js",
 | 
			
		||||
 
 | 
			
		||||
@@ -49,7 +49,6 @@ Name: Nom
 | 
			
		||||
Label: Nom
 | 
			
		||||
 | 
			
		||||
user:
 | 
			
		||||
    current_user: Utilisateur courant
 | 
			
		||||
    profile:
 | 
			
		||||
        title: Mon profil
 | 
			
		||||
        Phonenumber successfully updated!: Numéro de téléphone mis à jour!
 | 
			
		||||
@@ -60,6 +59,7 @@ user_group:
 | 
			
		||||
    inactive: Inactif
 | 
			
		||||
    with_users: Membres
 | 
			
		||||
    no_users: Aucun utilisateur associé
 | 
			
		||||
    no_user_groups: Aucune groupe d'utilisateurs
 | 
			
		||||
    no_admin_users: Aucun administrateur
 | 
			
		||||
    Label: Nom du groupe
 | 
			
		||||
    BackgroundColor: Couleur de fond du badge
 | 
			
		||||
@@ -113,6 +113,8 @@ Any comment: Aucun commentaire
 | 
			
		||||
# comment embeddable
 | 
			
		||||
No comment associated: Aucun commentaire
 | 
			
		||||
private comment: Notes privées
 | 
			
		||||
comment_public: Note
 | 
			
		||||
comment_private: Note privée
 | 
			
		||||
 | 
			
		||||
#pagination
 | 
			
		||||
Previous: Précédent
 | 
			
		||||
@@ -740,6 +742,7 @@ export:
 | 
			
		||||
        id: Identifiant de l'action
 | 
			
		||||
        social_issue_id: Identifiant de la problématique sociale
 | 
			
		||||
        social_issue: Problématique sociale
 | 
			
		||||
        desactivation_date: Date de désactivation
 | 
			
		||||
        social_issue_ordering: Ordre de la problématique sociale
 | 
			
		||||
        action_label: Action d'accompagnement
 | 
			
		||||
        action_ordering: Ordre
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@ readonly class AccompanyingPeriodStepChangeCronjob implements CronJobInterface
 | 
			
		||||
    {
 | 
			
		||||
        $now = $this->clock->now();
 | 
			
		||||
 | 
			
		||||
        if (null !== $cronJobExecution && $now->sub(new \DateInterval('P1D')) < $cronJobExecution->getLastStart()) {
 | 
			
		||||
        if (null !== $cronJobExecution && $now->sub(new \DateInterval('PT23H45M')) < $cronJobExecution->getLastStart()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -204,20 +204,24 @@ final class AccompanyingCourseController extends \Symfony\Bundle\FrameworkBundle
 | 
			
		||||
            ['date' => 'DESC', 'id' => 'DESC'],
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $activities = \array_slice($activities, 0, 3);
 | 
			
		||||
 | 
			
		||||
        $works = $this->workRepository->findByAccompanyingPeriod(
 | 
			
		||||
            $accompanyingCourse,
 | 
			
		||||
            ['startDate' => 'DESC', 'endDate' => 'DESC'],
 | 
			
		||||
            3
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $counters = [
 | 
			
		||||
            'activities' => count($activities),
 | 
			
		||||
            'openWorks' => count($accompanyingCourse->getOpenWorks()),
 | 
			
		||||
            'works' => count($works),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        return $this->render('@ChillPerson/AccompanyingCourse/index.html.twig', [
 | 
			
		||||
            'accompanyingCourse' => $accompanyingCourse,
 | 
			
		||||
            'withoutHousehold' => $withoutHousehold,
 | 
			
		||||
            'participationsByHousehold' => $accompanyingCourse->actualParticipationsByHousehold(),
 | 
			
		||||
            'works' => $works,
 | 
			
		||||
            'activities' => $activities,
 | 
			
		||||
            'works' => \array_slice($works, 0, 3),
 | 
			
		||||
            'activities' => \array_slice($activities, 0, 3),
 | 
			
		||||
            'counters' => $counters,
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -511,6 +511,14 @@ class AccompanyingPeriod implements
 | 
			
		||||
        return $this->getParticipationsContainsPerson($person)->count() > 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getOpenWorks(): Collection
 | 
			
		||||
    {
 | 
			
		||||
        return $this->getWorks()->filter(
 | 
			
		||||
            static fn (AccompanyingPeriodWork $work): bool => null === $work->getEndDate()
 | 
			
		||||
                    or $work->getEndDate() > new \DateTimeImmutable('today')
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Open a new participation for a person.
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -58,13 +58,11 @@ class HouseholdComposition implements TrackCreationInterface, TrackUpdateInterfa
 | 
			
		||||
    #[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: true, options: ['default' => null])]
 | 
			
		||||
    private ?int $numberOfChildren = null;
 | 
			
		||||
 | 
			
		||||
    #[Assert\NotNull]
 | 
			
		||||
    #[Assert\GreaterThanOrEqual(0, groups: ['Default', 'household_composition'])]
 | 
			
		||||
    #[Serializer\Groups(['docgen:read'])]
 | 
			
		||||
    #[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: true, options: ['default' => null])]
 | 
			
		||||
    private ?int $numberOfDependents = null;
 | 
			
		||||
 | 
			
		||||
    #[Assert\NotNull]
 | 
			
		||||
    #[Assert\GreaterThanOrEqual(0, groups: ['Default', 'household_composition'])]
 | 
			
		||||
    #[Serializer\Groups(['docgen:read'])]
 | 
			
		||||
    #[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: true, options: ['default' => null])]
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ use Doctrine\ORM\Mapping as ORM;
 | 
			
		||||
class MaritalStatus
 | 
			
		||||
{
 | 
			
		||||
    #[ORM\Id]
 | 
			
		||||
    #[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 7)]
 | 
			
		||||
    #[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 15)]
 | 
			
		||||
    private ?string $id;
 | 
			
		||||
 | 
			
		||||
    #[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON)]
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@ namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters;
 | 
			
		||||
 | 
			
		||||
use Chill\MainBundle\Export\FilterInterface;
 | 
			
		||||
use Chill\MainBundle\Form\Type\PickRollingDateType;
 | 
			
		||||
use Chill\MainBundle\Form\Type\PickUserOrMeDynamicType;
 | 
			
		||||
use Chill\MainBundle\Form\Type\PickUserDynamicType;
 | 
			
		||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
 | 
			
		||||
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
 | 
			
		||||
use Chill\PersonBundle\Export\Declarations;
 | 
			
		||||
@@ -66,7 +66,7 @@ class ReferrerFilter implements FilterInterface
 | 
			
		||||
    public function buildForm(FormBuilderInterface $builder)
 | 
			
		||||
    {
 | 
			
		||||
        $builder
 | 
			
		||||
            ->add('accepted_referrers', PickUserOrMeDynamicType::class, [
 | 
			
		||||
            ->add('accepted_referrers', PickUserDynamicType::class, [
 | 
			
		||||
                'multiple' => true,
 | 
			
		||||
            ])
 | 
			
		||||
            ->add('date_calc', PickRollingDateType::class, [
 | 
			
		||||
 
 | 
			
		||||
@@ -71,7 +71,7 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
 | 
			
		||||
                    ->setExtras(['order' => 30]);
 | 
			
		||||
             */
 | 
			
		||||
 | 
			
		||||
            $menu->addChild($this->translator->trans('Accompanying Course Comment'), [
 | 
			
		||||
            $menu->addChild($this->translator->trans('Accompanying Course Comments'), [
 | 
			
		||||
                'route' => 'chill_person_accompanying_period_comment_list',
 | 
			
		||||
                'routeParameters' => [
 | 
			
		||||
                    'accompanying_period_id' => $period->getId(),
 | 
			
		||||
@@ -80,12 +80,15 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($this->security->isGranted(AccompanyingPeriodWorkVoter::SEE, $period)) {
 | 
			
		||||
            $menu->addChild($this->translator->trans('Accompanying Course Action'), [
 | 
			
		||||
            $menu->addChild($this->translator->trans('Accompanying Course Actions'), [
 | 
			
		||||
                'route' => 'chill_person_accompanying_period_work_list',
 | 
			
		||||
                'routeParameters' => [
 | 
			
		||||
                    'id' => $period->getId(),
 | 
			
		||||
                ], ])
 | 
			
		||||
                ->setExtras(['order' => 40]);
 | 
			
		||||
                ->setExtras([
 | 
			
		||||
                    'order' => 40,
 | 
			
		||||
                    'counter' => count($period->getWorks()) > 0 ? count($period->getWorks()) : null,
 | 
			
		||||
                ]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $workflow = $this->registry->get($period, 'accompanying_period_lifecycle');
 | 
			
		||||
 
 | 
			
		||||
@@ -304,5 +304,14 @@ div#dashboards {
 | 
			
		||||
                margin: 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        div.count-item {
 | 
			
		||||
            font-size: 3rem;
 | 
			
		||||
            text-align: center;
 | 
			
		||||
        }
 | 
			
		||||
        div.count-item-label {
 | 
			
		||||
            font-size: 90%;
 | 
			
		||||
            font-variant: all-small-caps;
 | 
			
		||||
            text-align: center;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,36 @@
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.badge-accompanying-work-type-simple {
 | 
			
		||||
    @extend .badge;
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    margin: 0.2rem 0;
 | 
			
		||||
    padding-left: 0;
 | 
			
		||||
    padding-right: 0.5rem;
 | 
			
		||||
 | 
			
		||||
    border-left: 20px groove $orange;
 | 
			
		||||
    border-radius: $badge-border-radius;
 | 
			
		||||
 | 
			
		||||
    max-width: 100%;
 | 
			
		||||
    background-color: $gray-100;
 | 
			
		||||
 | 
			
		||||
    color: black;
 | 
			
		||||
    font-weight: normal;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    text-overflow: ellipsis;
 | 
			
		||||
    text-indent: 5px hanging;
 | 
			
		||||
    text-align: left;
 | 
			
		||||
 | 
			
		||||
    &::before {
 | 
			
		||||
        margin-right: 3px;
 | 
			
		||||
        position: relative;
 | 
			
		||||
        left: -0.5px;
 | 
			
		||||
        font-family: ForkAwesome;
 | 
			
		||||
        content: '\f04b';
 | 
			
		||||
        color: #e2793d;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// AccompanyingCourse Work Pages
 | 
			
		||||
div.accompanying-course-work {
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -61,15 +61,15 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { ClassicEditor } from "ckeditor5";
 | 
			
		||||
import { Ckeditor } from "@ckeditor/ckeditor5-vue";
 | 
			
		||||
import {ClassicEditor} from "ckeditor5";
 | 
			
		||||
import {Ckeditor} from "@ckeditor/ckeditor5-vue";
 | 
			
		||||
import classicEditorConfig from "ChillMainAssets/module/ckeditor5/editor_config";
 | 
			
		||||
import { mapState } from "vuex";
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: "Comment",
 | 
			
		||||
  components: {
 | 
			
		||||
    ckeditor: Ckeditor,
 | 
			
		||||
    ckeditor: Ckeditor
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
 
 | 
			
		||||
@@ -204,7 +204,8 @@ export default {
 | 
			
		||||
      } else if (payload.type === "thirdparty") {
 | 
			
		||||
        body.name = payload.data.text;
 | 
			
		||||
        body.email = payload.data.email;
 | 
			
		||||
        body.telephone = payload.data.phonenumber;
 | 
			
		||||
        body.telephone = payload.data.telephone;
 | 
			
		||||
        body.telephone2 = payload.data.telephone2;
 | 
			
		||||
        body.address = { id: payload.data.address.address_id };
 | 
			
		||||
 | 
			
		||||
        makeFetch(
 | 
			
		||||
 
 | 
			
		||||
@@ -385,7 +385,8 @@ export default {
 | 
			
		||||
      } else if (payload.type === "thirdparty") {
 | 
			
		||||
        body.name = payload.data.text;
 | 
			
		||||
        body.email = payload.data.email;
 | 
			
		||||
        body.telephone = payload.data.phonenumber;
 | 
			
		||||
        body.telephone = payload.data.telephone;
 | 
			
		||||
        body.telephone2 = payload.data.telephone2;
 | 
			
		||||
        if (payload.data.address) {
 | 
			
		||||
          body.address = { id: payload.data.address.address_id };
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -194,6 +194,7 @@ export default {
 | 
			
		||||
        body.name = payload.data.name;
 | 
			
		||||
        body.email = payload.data.email;
 | 
			
		||||
        body.telephone = payload.data.telephone;
 | 
			
		||||
        body.telephone2 = payload.data.telephone2;
 | 
			
		||||
        body.address = payload.data.address
 | 
			
		||||
          ? { id: payload.data.address.address_id }
 | 
			
		||||
          : null;
 | 
			
		||||
 
 | 
			
		||||
@@ -39,15 +39,15 @@
 | 
			
		||||
<script>
 | 
			
		||||
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
 | 
			
		||||
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
 | 
			
		||||
import { Ckeditor } from "@ckeditor/ckeditor5-vue";
 | 
			
		||||
import { Ckeditor }from "@ckeditor/ckeditor5-vue";
 | 
			
		||||
import classicEditorConfig from "ChillMainAssets/module/ckeditor5/editor_config";
 | 
			
		||||
import { ClassicEditor } from "ckeditor5";
 | 
			
		||||
import {ClassicEditor} from "ckeditor5";
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: "WriteComment",
 | 
			
		||||
  components: {
 | 
			
		||||
    Modal,
 | 
			
		||||
    ckeditor: Ckeditor,
 | 
			
		||||
    ckeditor: Ckeditor
 | 
			
		||||
  },
 | 
			
		||||
  props: ["resource"],
 | 
			
		||||
  emits: ["updateComment"],
 | 
			
		||||
 
 | 
			
		||||
@@ -46,8 +46,7 @@
 | 
			
		||||
      <label class="col-form-label">{{ $t("comments") }}</label>
 | 
			
		||||
      <ckeditor
 | 
			
		||||
        v-model="note"
 | 
			
		||||
        :editor="classicEditor"
 | 
			
		||||
        :config="editorConfig"
 | 
			
		||||
        :editor="classicEditor" :config="editorConfig"
 | 
			
		||||
        tag-name="textarea"
 | 
			
		||||
      ></ckeditor>
 | 
			
		||||
    </div>
 | 
			
		||||
@@ -208,6 +207,29 @@
 | 
			
		||||
            </label>
 | 
			
		||||
          </div>
 | 
			
		||||
        </li>
 | 
			
		||||
        <li
 | 
			
		||||
          v-for="p in getPreviousPersons"
 | 
			
		||||
          :key="p.id"
 | 
			
		||||
          class="alert alert-danger"
 | 
			
		||||
        >
 | 
			
		||||
          <div class="form-check">
 | 
			
		||||
            <input
 | 
			
		||||
              v-model="personsPicked"
 | 
			
		||||
              :value="p.id"
 | 
			
		||||
              type="checkbox"
 | 
			
		||||
              class="me-2 form-check-input"
 | 
			
		||||
              :id="'person_check' + p.id"
 | 
			
		||||
            />
 | 
			
		||||
            <label :for="'person_check' + p.id" class="form-check-label">
 | 
			
		||||
              <person-text :person="p"></person-text>
 | 
			
		||||
            </label>
 | 
			
		||||
          </div>
 | 
			
		||||
          <span
 | 
			
		||||
            ><i class="fa fa-warning"></i> {{
 | 
			
		||||
              $t("warning_previous_persons")
 | 
			
		||||
            }}</span
 | 
			
		||||
          >
 | 
			
		||||
        </li>
 | 
			
		||||
      </ul>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
@@ -440,7 +462,7 @@
 | 
			
		||||
import { mapState, mapGetters } from "vuex";
 | 
			
		||||
import { Ckeditor } from "@ckeditor/ckeditor5-vue";
 | 
			
		||||
import classicEditorConfig from "ChillMainAssets/module/ckeditor5/editor_config";
 | 
			
		||||
import { ClassicEditor } from "ckeditor5";
 | 
			
		||||
import {ClassicEditor} from "ckeditor5";
 | 
			
		||||
import AddResult from "./components/AddResult.vue";
 | 
			
		||||
import AddEvaluation from "./components/AddEvaluation.vue";
 | 
			
		||||
import AddPersons from "ChillPersonAssets/vuejs/_components/AddPersons.vue";
 | 
			
		||||
@@ -497,6 +519,8 @@ const i18n = {
 | 
			
		||||
      notification_notify_referrer: "Notifier le référent",
 | 
			
		||||
      notification_notify_any: "Notifier d'autres utilisateurs",
 | 
			
		||||
      notification_send: "Envoyer une notification",
 | 
			
		||||
      warning_previous_persons:
 | 
			
		||||
        "Cet usager n'est désormais plus concerné par le parcours, bien qu'il ait été associé à l'action par le passé.",
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
@@ -583,6 +607,7 @@ export default {
 | 
			
		||||
      "hasHandlingThirdParty",
 | 
			
		||||
      "hasThirdParties",
 | 
			
		||||
      "hasReferrers",
 | 
			
		||||
      "getPreviousPersons",
 | 
			
		||||
    ]),
 | 
			
		||||
    classicEditor: () => ClassicEditor,
 | 
			
		||||
    editorConfig: () => classicEditorConfig,
 | 
			
		||||
@@ -745,7 +770,8 @@ export default {
 | 
			
		||||
      let body = { type: payload.type };
 | 
			
		||||
      body.name = payload.data.text;
 | 
			
		||||
      body.email = payload.data.email;
 | 
			
		||||
      body.telephone = payload.data.phonenumber;
 | 
			
		||||
      body.telephone = payload.data.telephone;
 | 
			
		||||
      body.telephone2 = payload.data.telephone2;
 | 
			
		||||
      body.address = { id: payload.data.address.address_id };
 | 
			
		||||
 | 
			
		||||
      makeFetch(
 | 
			
		||||
@@ -755,7 +781,9 @@ export default {
 | 
			
		||||
      )
 | 
			
		||||
        .then((response) => {
 | 
			
		||||
          this.$store.dispatch("updateThirdParty", response);
 | 
			
		||||
          this.$refs.onTheFly.closeModal();
 | 
			
		||||
          for (let otf of this.$refs.onTheFly) {
 | 
			
		||||
            otf.closeModal();
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
        .catch((error) => {
 | 
			
		||||
          if (error.name === "ValidationException") {
 | 
			
		||||
 
 | 
			
		||||
@@ -273,7 +273,7 @@
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { ISOToDatetime } from "ChillMainAssets/chill/js/date";
 | 
			
		||||
import { Ckeditor } from "@ckeditor/ckeditor5-vue";
 | 
			
		||||
import {Ckeditor} from "@ckeditor/ckeditor5-vue";
 | 
			
		||||
import { ClassicEditor } from "ckeditor5";
 | 
			
		||||
import classicEditorConfig from "ChillMainAssets/module/ckeditor5/editor_config";
 | 
			
		||||
import { mapState } from "vuex";
 | 
			
		||||
@@ -535,11 +535,11 @@ export default {
 | 
			
		||||
        title: title,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    addDocument({ stored_object, stored_object_version }) {
 | 
			
		||||
    addDocument({ stored_object, stored_object_version, file_name }) {
 | 
			
		||||
      let document = {
 | 
			
		||||
        type: "accompanying_period_work_evaluation_document",
 | 
			
		||||
        storedObject: stored_object,
 | 
			
		||||
        title: "Nouveau document",
 | 
			
		||||
        title: file_name,
 | 
			
		||||
      };
 | 
			
		||||
      this.$store.commit("addDocument", {
 | 
			
		||||
        key: this.evaluation.key,
 | 
			
		||||
 
 | 
			
		||||
@@ -87,6 +87,11 @@ const store = createStore({
 | 
			
		||||
 | 
			
		||||
      return [];
 | 
			
		||||
    },
 | 
			
		||||
    getPreviousPersons(state) {
 | 
			
		||||
      return state.personsPicked.filter(
 | 
			
		||||
        (p) => !state.personsReachables.map((pr) => pr.id).includes(p.id),
 | 
			
		||||
      );
 | 
			
		||||
    },
 | 
			
		||||
    buildPayload(state) {
 | 
			
		||||
      return {
 | 
			
		||||
        type: "accompanying_period_work",
 | 
			
		||||
@@ -607,8 +612,7 @@ const store = createStore({
 | 
			
		||||
    submit({ getters, state, commit }, callback) {
 | 
			
		||||
      let payload = getters.buildPayload,
 | 
			
		||||
        params = new URLSearchParams({ entity_version: state.version }),
 | 
			
		||||
        url = `/api/1.0/person/accompanying-course/work/${state.work.id}.json?${params}`,
 | 
			
		||||
        errors = [];
 | 
			
		||||
        url = `/api/1.0/person/accompanying-course/work/${state.work.id}.json?${params}`;
 | 
			
		||||
      commit("setIsPosting", true);
 | 
			
		||||
 | 
			
		||||
      // console.log('the social action', payload);
 | 
			
		||||
 
 | 
			
		||||
@@ -30,8 +30,7 @@
 | 
			
		||||
 | 
			
		||||
    <div class="item-row comment">
 | 
			
		||||
      <ckeditor
 | 
			
		||||
        :editor="classicEditor"
 | 
			
		||||
        :config="editorConfig"
 | 
			
		||||
        :editor="classicEditor" :config="editorConfig"
 | 
			
		||||
        v-model="comment"
 | 
			
		||||
        tag-name="textarea"
 | 
			
		||||
      />
 | 
			
		||||
@@ -103,9 +102,9 @@ div.participation-details {
 | 
			
		||||
<script>
 | 
			
		||||
import { mapGetters } from "vuex";
 | 
			
		||||
import PersonRenderBox from "ChillPersonAssets/vuejs/_components/Entity/PersonRenderBox.vue";
 | 
			
		||||
import { Ckeditor } from "@ckeditor/ckeditor5-vue";
 | 
			
		||||
import {Ckeditor} from "@ckeditor/ckeditor5-vue";
 | 
			
		||||
import classicEditorConfig from "ChillMainAssets/module/ckeditor5/editor_config";
 | 
			
		||||
import { ClassicEditor } from "ckeditor5";
 | 
			
		||||
import {ClassicEditor} from "ckeditor5";
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: "MemberDetails",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,25 +1,25 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <ckeditor
 | 
			
		||||
    name="content"
 | 
			
		||||
    :placeholder="
 | 
			
		||||
    <ckeditor
 | 
			
		||||
        name="content"
 | 
			
		||||
        :placeholder="
 | 
			
		||||
      $t('household_members_editor.positioning.comment_placeholder')
 | 
			
		||||
    "
 | 
			
		||||
    :editor="editor"
 | 
			
		||||
    :config="editorConfig"
 | 
			
		||||
    v-model="content"
 | 
			
		||||
    tag-name="textarea"
 | 
			
		||||
  />
 | 
			
		||||
        :editor="editor"
 | 
			
		||||
        :config="editorConfig"
 | 
			
		||||
        v-model="content"
 | 
			
		||||
        tag-name="textarea"
 | 
			
		||||
    />
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { Ckeditor } from "@ckeditor/ckeditor5-vue";
 | 
			
		||||
import  { Ckeditor } from "@ckeditor/ckeditor5-vue";
 | 
			
		||||
import classicEditorConfig from "ChillMainAssets/module/ckeditor5/editor_config";
 | 
			
		||||
import { ClassicEditor } from "ckeditor5";
 | 
			
		||||
import {ClassicEditor} from "ckeditor5";
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: "PersonComment.vue",
 | 
			
		||||
  components: {
 | 
			
		||||
    ckeditor: Ckeditor,
 | 
			
		||||
    ckeditor: Ckeditor
 | 
			
		||||
  },
 | 
			
		||||
  props: ["conc"],
 | 
			
		||||
  computed: {
 | 
			
		||||
 
 | 
			
		||||
@@ -201,7 +201,7 @@
 | 
			
		||||
        {% endif %}
 | 
			
		||||
 | 
			
		||||
        {% if accompanyingCourse.step != 'DRAFT' %}
 | 
			
		||||
            <div class="mbloc col col-sm-6 col-lg-4">
 | 
			
		||||
            <div class="mbloc col col-sm-6 col-lg-8 col-xxl-4">
 | 
			
		||||
                <div class="notification-counter">
 | 
			
		||||
                    <h4 class="item-key">{{ 'notification.Notifications'|trans }}</h4>
 | 
			
		||||
                    {% set notif_counter = chill_count_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod', accompanyingCourse.id) %}
 | 
			
		||||
@@ -238,6 +238,31 @@
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
 | 
			
		||||
        {% if counters.activities > 0 %}
 | 
			
		||||
            <div class="mbloc col col-sm-6 col-lg-4">
 | 
			
		||||
                <div class="count-activities">
 | 
			
		||||
                    <div class="count-item">{{ counters.activities }}</div>
 | 
			
		||||
                    <div class="count-item-label">
 | 
			
		||||
                        {% if counters.activities == 1 %}
 | 
			
		||||
                            {{ 'Activity'|trans }}
 | 
			
		||||
                        {% else %}
 | 
			
		||||
                            {{ 'Activities'|trans }}
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
 | 
			
		||||
        {% if counters.works > 0 %}
 | 
			
		||||
            <div class="mbloc col col-sm-6 col-lg-4">
 | 
			
		||||
                <div class="count-works">
 | 
			
		||||
                    <div class="count-item">{{ counters.openWorks }} / {{ counters.works }}</div>
 | 
			
		||||
                    <div class="count-item-label">{{ 'accompanying_course_work.On-going works over total'|trans }}</div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="social-actions my-4">
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ L'usager {{ oldPersonLocation|chill_entity_render_string }} a déménagé.
 | 
			
		||||
Son adresse était utilisée pour localiser le parcours n°{{ period.id }}, dont vous êtes
 | 
			
		||||
le référent.
 | 
			
		||||
 | 
			
		||||
En conséquence de ce déménage, le parcours est toujours localisé à cette adresse, mais à l'aide d'une
 | 
			
		||||
En conséquence de ce déménagement, le parcours est toujours localisé à cette adresse, mais à l'aide d'une
 | 
			
		||||
adresse temporaire.
 | 
			
		||||
 | 
			
		||||
Si vous continuez à suivre le parcours, vous pouvez le localiser à nouveau auprès de l'adresse de
 | 
			
		||||
 
 | 
			
		||||
@@ -5,44 +5,49 @@
 | 
			
		||||
{% set w = document.accompanyingPeriodWorkEvaluation.accompanyingPeriodWork %}
 | 
			
		||||
 | 
			
		||||
<div class="item-row">
 | 
			
		||||
    <div class="item-col" style="width: unset">
 | 
			
		||||
        {% if document.storedObject.isPending %}
 | 
			
		||||
            <div class="badge text-bg-info" data-docgen-is-pending="{{ document.storedObject.id }}">{{ 'docgen.Doc generation is pending'|trans }}</div>
 | 
			
		||||
        {% elseif document.storedObject.isFailure %}
 | 
			
		||||
            <div class="badge text-bg-warning">{{ 'docgen.Doc generation failed'|trans }}</div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        <div>
 | 
			
		||||
            {% if context == 'person' %}
 | 
			
		||||
                <span class="badge bg-primary">
 | 
			
		||||
                        <i class="fa fa-random"></i> {{ w.accompanyingPeriod.id }}
 | 
			
		||||
                    </span> 
 | 
			
		||||
    <!-- evaluation document -->
 | 
			
		||||
    <div class="item-two-col-grid" style="width: unset">
 | 
			
		||||
        <div class="title">
 | 
			
		||||
            {% if document.storedObject.isPending %}
 | 
			
		||||
                <div class="badge text-bg-info" data-docgen-is-pending="{{ document.storedObject.id }}">{{ 'docgen.Doc generation is pending'|trans }}</div>
 | 
			
		||||
            {% elseif document.storedObject.isFailure %}
 | 
			
		||||
                <div class="badge text-bg-warning">{{ 'docgen.Doc generation failed'|trans }}</div>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            <div class="badge-accompanying-work-type">
 | 
			
		||||
                <span class="title_label"></span>
 | 
			
		||||
                <span class="title_action">{{ w.socialAction|chill_entity_render_string }} > {{ document.accompanyingPeriodWorkEvaluation.evaluation.title|localize_translatable_string }}</span>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="denomination h2">
 | 
			
		||||
            {{ document.title|chill_print_or_message("No title") }}
 | 
			
		||||
        </div>
 | 
			
		||||
        {% if document.storedObject.type is not empty %}
 | 
			
		||||
            <div>
 | 
			
		||||
                {{ mm.mimeIcon(document.storedObject.type) }}
 | 
			
		||||
                <div>
 | 
			
		||||
                    <div class="badge-accompanying-work-type-simple">
 | 
			
		||||
                        {{ w.socialAction|chill_entity_render_string }} > {{ document.accompanyingPeriodWorkEvaluation.evaluation.title|localize_translatable_string }}
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="denomination h2">
 | 
			
		||||
                {{ document.title|chill_print_or_message("No title") }}
 | 
			
		||||
            </div>
 | 
			
		||||
            {% if document.storedObject.type is not empty %}
 | 
			
		||||
                <div>
 | 
			
		||||
                    {{ mm.mimeIcon(document.storedObject.type) }}
 | 
			
		||||
                </div>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            {% if document.storedObject.hasTemplate %}
 | 
			
		||||
                <div>
 | 
			
		||||
                    <p>{{ document.storedObject.template.name|localize_translatable_string }}</p>
 | 
			
		||||
                </div>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        </div>
 | 
			
		||||
        {% if document.storedObject.createdAt is not null %}
 | 
			
		||||
            <div class="aside">
 | 
			
		||||
                <div class="dates row text-end">
 | 
			
		||||
                    <span>{{ document.storedObject.createdAt|format_date('short') }}</span>
 | 
			
		||||
                </div>
 | 
			
		||||
                {% if context == 'person' %}
 | 
			
		||||
                    <div class="text-end">
 | 
			
		||||
                        <span class="badge bg-primary">
 | 
			
		||||
                            <i class="fa fa-random"></i> {{ w.accompanyingPeriod.id }}
 | 
			
		||||
                        </span> 
 | 
			
		||||
                    </div>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
            </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        {% if document.storedObject.hasTemplate %}
 | 
			
		||||
            <div>
 | 
			
		||||
                <p>{{ document.storedObject.template.name|localize_translatable_string }}</p>
 | 
			
		||||
            </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="item-col">
 | 
			
		||||
        <div class="container">
 | 
			
		||||
            <div class="dates row text-end">
 | 
			
		||||
                <span>{{ document.storedObject.createdAt|format_date('short') }}</span>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -30,9 +30,6 @@ final readonly class SocialActionCSVExportService
 | 
			
		||||
        private TranslatorInterface $translator,
 | 
			
		||||
    ) {}
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param list<SocialAction> $actions
 | 
			
		||||
     */
 | 
			
		||||
    public function generateCsv(array $actions): Writer
 | 
			
		||||
    {
 | 
			
		||||
        // CSV headers
 | 
			
		||||
@@ -84,7 +81,8 @@ final readonly class SocialActionCSVExportService
 | 
			
		||||
            'action_id' => $action->getId(),
 | 
			
		||||
            'social_issue_id' => $action->getIssue()?->getId(),
 | 
			
		||||
            'problematique_label' => null !== $action->getIssue() ? $this->socialIssueRender->renderString($action->getIssue(), []) : null,
 | 
			
		||||
            'social_issue_ordering' => null !== $action->getIssue() ? $action->getIssue()->getOrdering() : null,
 | 
			
		||||
            'desactivation_date' => $action->getDesactivationDate()?->format('Y-m-d'),
 | 
			
		||||
            'social_issue_ordering' => $action->getIssue()?->getOrdering(),
 | 
			
		||||
            'action_label' => $this->socialActionRender->renderString($action, []),
 | 
			
		||||
            'action_ordering' => $action->getOrdering(),
 | 
			
		||||
            'goal_label' => null !== $goal ? $this->stringHelper->localize($goal->getTitle()) : null,
 | 
			
		||||
 
 | 
			
		||||
@@ -44,6 +44,7 @@ readonly class SocialIssueCSVExportService
 | 
			
		||||
                    'Id',
 | 
			
		||||
                    'Label',
 | 
			
		||||
                    'Social issue',
 | 
			
		||||
                    'export.social_action_list.desactivation_date',
 | 
			
		||||
                    'socialIssue.isParent?',
 | 
			
		||||
                    'socialIssue.Parent id',
 | 
			
		||||
                ]
 | 
			
		||||
@@ -66,6 +67,7 @@ readonly class SocialIssueCSVExportService
 | 
			
		||||
                'id' => $issue->getId(),
 | 
			
		||||
                'label' => $this->stringHelper->localize($issue->getTitle()),
 | 
			
		||||
                'title' => $this->socialIssueRender->renderString($issue, []),
 | 
			
		||||
                'export.social_action_list.desactivation_date' => $issue->getDesactivationDate()?->format('Y-m-d'),
 | 
			
		||||
                'isParent' => $issue->hasChildren() ? 'X' : '',
 | 
			
		||||
                'parent_id' => null !== $issue->getParent() ? $issue->getParent()->getId() : '',
 | 
			
		||||
            ];
 | 
			
		||||
 
 | 
			
		||||
@@ -52,6 +52,7 @@ class SocialActionCsvExporterTest extends TestCase
 | 
			
		||||
        // Création d'une instance réelle de SocialAction sans objectifs ni résultats
 | 
			
		||||
        $actionWithoutGoalsOrResults = new SocialAction();
 | 
			
		||||
        $actionWithoutGoalsOrResults->setIssue($socialIssue);
 | 
			
		||||
        $actionWithoutGoalsOrResults->setDesactivationDate(new \DateTime('2025-05-21'));
 | 
			
		||||
        $actionWithoutGoalsOrResults->setTitle(['fr' => 'Action without goals or results']);
 | 
			
		||||
 | 
			
		||||
        // Création d'une instance réelle de SocialAction avec des objectifs et des résultats
 | 
			
		||||
@@ -61,6 +62,7 @@ class SocialActionCsvExporterTest extends TestCase
 | 
			
		||||
 | 
			
		||||
        $actionWithGoalsAndResults = new SocialAction();
 | 
			
		||||
        $actionWithGoalsAndResults->setIssue($socialIssue);
 | 
			
		||||
        $actionWithGoalsAndResults->setDesactivationDate(new \DateTime('2025-05-21'));
 | 
			
		||||
        $actionWithGoalsAndResults->setTitle(['fr' => 'Action with goals and results']);
 | 
			
		||||
        $actionWithGoalsAndResults->addGoal($goalWithResult);
 | 
			
		||||
 | 
			
		||||
@@ -68,6 +70,7 @@ class SocialActionCsvExporterTest extends TestCase
 | 
			
		||||
        $goalWithoutResult = new Goal();
 | 
			
		||||
        $actionWithGoalsNoResults = new SocialAction();
 | 
			
		||||
        $actionWithGoalsNoResults->setIssue($socialIssue);
 | 
			
		||||
        $actionWithGoalsNoResults->setDesactivationDate(new \DateTime('2025-05-21'));
 | 
			
		||||
        $actionWithGoalsNoResults->setTitle(['fr' => 'Action with goals and no results']);
 | 
			
		||||
        $actionWithGoalsNoResults->addGoal($goalWithoutResult);
 | 
			
		||||
 | 
			
		||||
@@ -76,6 +79,7 @@ class SocialActionCsvExporterTest extends TestCase
 | 
			
		||||
        $resultWithNoAction->setTitle(['fr' => 'Result without objectives']);
 | 
			
		||||
        $actionWithResultsNoGoals = new SocialAction();
 | 
			
		||||
        $actionWithResultsNoGoals->setIssue($socialIssue);
 | 
			
		||||
        $actionWithResultsNoGoals->setDesactivationDate(new \DateTime('2025-05-21'));
 | 
			
		||||
        $actionWithResultsNoGoals->setTitle(['fr' => 'Action with results and no goals']);
 | 
			
		||||
        $actionWithResultsNoGoals->addResult($resultWithNoAction);
 | 
			
		||||
 | 
			
		||||
@@ -91,11 +95,11 @@ class SocialActionCsvExporterTest extends TestCase
 | 
			
		||||
        $this->assertStringContainsString('Action with results and no goals', $content);
 | 
			
		||||
 | 
			
		||||
        self::assertEquals(<<<'CSV'
 | 
			
		||||
        export.social_action_list.action_id,export.social_action_list.social_issue_id,export.social_action_list.problematique_label,export.social_action_list.social_issue_ordering,export.social_action_list.action_label,export.social_action_list.action_ordering,export.social_action_list.goal_label,export.social_action_list.goal_id,export.social_action_list.goal_result_label,export.social_action_list.goal_result_id,export.social_action_list.result_without_goal_label,export.social_action_list.result_without_goal_id,export.social_action_list.evaluation_title,export.social_action_list.evaluation_id,export.social_action_list.evaluation_url,export.social_action_list.evaluation_delay_month,export.social_action_list.evaluation_delay_week,export.social_action_list.evaluation_delay_day
 | 
			
		||||
        ,,"Issue Title",0,"Action with goals and results",0,"not found",,"not found",,,,,,,,,
 | 
			
		||||
        ,,"Issue Title",0,"Action without goals or results",0,,,,,,,,,,,,
 | 
			
		||||
        ,,"Issue Title",0,"Action with goals and no results",0,"not found",,,,,,,,,,,
 | 
			
		||||
        ,,"Issue Title",0,"Action with results and no goals",0,,,,,"Result without objectives",,,,,,,
 | 
			
		||||
        export.social_action_list.action_id,export.social_action_list.social_issue_id,export.social_action_list.problematique_label,export.social_action_list.desactivation_date,export.social_action_list.social_issue_ordering,export.social_action_list.action_label,export.social_action_list.action_ordering,export.social_action_list.goal_label,export.social_action_list.goal_id,export.social_action_list.goal_result_label,export.social_action_list.goal_result_id,export.social_action_list.result_without_goal_label,export.social_action_list.result_without_goal_id,export.social_action_list.evaluation_title,export.social_action_list.evaluation_id,export.social_action_list.evaluation_url,export.social_action_list.evaluation_delay_month,export.social_action_list.evaluation_delay_week,export.social_action_list.evaluation_delay_day
 | 
			
		||||
        ,,"Issue Title",2025-05-21,0,"Action with goals and results",0,"not found",,"not found",,,,,,,,,
 | 
			
		||||
        ,,"Issue Title",2025-05-21,0,"Action without goals or results",0,,,,,,,,,,,,
 | 
			
		||||
        ,,"Issue Title",2025-05-21,0,"Action with goals and no results",0,"not found",,,,,,,,,,,
 | 
			
		||||
        ,,"Issue Title",2025-05-21,0,"Action with results and no goals",0,,,,,"Result without objectives",,,,,,,
 | 
			
		||||
 | 
			
		||||
        CSV, $content);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,43 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Chill is a software for social workers
 | 
			
		||||
 *
 | 
			
		||||
 * For the full copyright and license information, please view
 | 
			
		||||
 * the LICENSE file that was distributed with this source code.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace Chill\Migrations\Person;
 | 
			
		||||
 | 
			
		||||
use Doctrine\DBAL\Schema\Schema;
 | 
			
		||||
use Doctrine\Migrations\AbstractMigration;
 | 
			
		||||
 | 
			
		||||
final class Version20250514115009 extends AbstractMigration
 | 
			
		||||
{
 | 
			
		||||
    public function getDescription(): string
 | 
			
		||||
    {
 | 
			
		||||
        return 'Allow more characters for maritalstatus id';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function up(Schema $schema): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->addSql(<<<'SQL'
 | 
			
		||||
            ALTER TABLE chill_person_marital_status ALTER id TYPE VARCHAR(15)
 | 
			
		||||
        SQL);
 | 
			
		||||
        $this->addSql(<<<'SQL'
 | 
			
		||||
            ALTER TABLE chill_person_person ALTER maritalstatus_id TYPE VARCHAR(15)
 | 
			
		||||
        SQL);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function down(Schema $schema): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->addSql(<<<'SQL'
 | 
			
		||||
            ALTER TABLE chill_person_person ALTER maritalStatus_id TYPE VARCHAR(7)
 | 
			
		||||
        SQL);
 | 
			
		||||
        $this->addSql(<<<'SQL'
 | 
			
		||||
            ALTER TABLE chill_person_marital_status ALTER id TYPE VARCHAR(7)
 | 
			
		||||
        SQL);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -804,7 +804,7 @@ person_admin:
 | 
			
		||||
 | 
			
		||||
# specific to accompanying period
 | 
			
		||||
accompanying_period:
 | 
			
		||||
    deleted: Parcours d'accompagnment supprimé
 | 
			
		||||
    deleted: Parcours d'accompagnement supprimé
 | 
			
		||||
    dates: Période
 | 
			
		||||
    dates_from_%opening_date%: Ouvert depuis le %opening_date%
 | 
			
		||||
    dates_from_%opening_date%_to_%closing_date%: Ouvert du %opening_date% au %closing_date%
 | 
			
		||||
@@ -843,6 +843,7 @@ accompanying_course:
 | 
			
		||||
    administrative_location: Localisation administrative
 | 
			
		||||
    comment is pinned: Le commentaire est épinglé
 | 
			
		||||
    comment is unpinned: Le commentaire est désépinglé
 | 
			
		||||
 | 
			
		||||
show: Montrer
 | 
			
		||||
hide: Masquer
 | 
			
		||||
closed periods: parcours clôturés
 | 
			
		||||
@@ -851,6 +852,7 @@ Social work configuration: Gestion des actions d'accompagnement social
 | 
			
		||||
 | 
			
		||||
# Accompanying Course comments
 | 
			
		||||
Accompanying Course Comment: Commentaire
 | 
			
		||||
Accompanying Course Comments: Commentaires
 | 
			
		||||
Accompanying Course Comment list: Commentaires du parcours
 | 
			
		||||
pinned: épinglé
 | 
			
		||||
Pin comment: Épingler
 | 
			
		||||
@@ -919,6 +921,7 @@ accompanying_course_work:
 | 
			
		||||
    date_filter: Filtrer par date
 | 
			
		||||
    types_filter: Filtrer par type d'action
 | 
			
		||||
    user_filter: Filtrer par intervenant
 | 
			
		||||
    On-going works over total: Actions en cours / Actions du parcours
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
 
 | 
			
		||||
@@ -624,7 +624,7 @@ final class SingleTaskController extends AbstractController
 | 
			
		||||
            ->addCheckbox('status', $statuses, $statuses, $statusTrans);
 | 
			
		||||
 | 
			
		||||
        $states = $this->singleTaskStateRepository->findAllExistingStates();
 | 
			
		||||
        $checked = array_values(array_filter($states, fn (string $state) => !in_array($state, ['closed', 'canceled', 'validated'], true)));
 | 
			
		||||
        $checked = array_values(array_filter($states, fn (string $state) => !in_array($state, ['in_progress', 'closed', 'canceled', 'validated'], true)));
 | 
			
		||||
 | 
			
		||||
        if ([] !== $states) {
 | 
			
		||||
            $filterBuilder
 | 
			
		||||
 
 | 
			
		||||
@@ -65,6 +65,7 @@ class ThirdpartyCSVExportController extends AbstractController
 | 
			
		||||
                    'Name',
 | 
			
		||||
                    'Profession',
 | 
			
		||||
                    'Telephone',
 | 
			
		||||
                    'Telephone2',
 | 
			
		||||
                    'Email',
 | 
			
		||||
                    'Address',
 | 
			
		||||
                    'Comment',
 | 
			
		||||
@@ -76,6 +77,7 @@ class ThirdpartyCSVExportController extends AbstractController
 | 
			
		||||
                    'Contact name',
 | 
			
		||||
                    'Contact  firstname',
 | 
			
		||||
                    'Contact phone',
 | 
			
		||||
                    'Contact phone2',
 | 
			
		||||
                    'Contact email',
 | 
			
		||||
                    'Contact address',
 | 
			
		||||
                    'Contact profession',
 | 
			
		||||
 
 | 
			
		||||
@@ -209,6 +209,11 @@ class ThirdParty implements TrackCreationInterface, TrackUpdateInterface, \Strin
 | 
			
		||||
    #[PhonenumberConstraint(type: 'any')]
 | 
			
		||||
    private ?PhoneNumber $telephone = null;
 | 
			
		||||
 | 
			
		||||
    #[Groups(['read', 'write', 'docgen:read', 'docgen:read:3party:parent'])]
 | 
			
		||||
    #[ORM\Column(name: 'telephone2', type: 'phone_number', nullable: true)]
 | 
			
		||||
    #[PhonenumberConstraint(type: 'any')]
 | 
			
		||||
    private ?PhoneNumber $telephone2 = null;
 | 
			
		||||
 | 
			
		||||
    #[ORM\Column(name: 'types', type: \Doctrine\DBAL\Types\Types::JSON, nullable: true)]
 | 
			
		||||
    private ?array $thirdPartyTypes = [];
 | 
			
		||||
 | 
			
		||||
@@ -429,6 +434,11 @@ class ThirdParty implements TrackCreationInterface, TrackUpdateInterface, \Strin
 | 
			
		||||
        return $this->telephone;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getTelephone2(): ?PhoneNumber
 | 
			
		||||
    {
 | 
			
		||||
        return $this->telephone2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get type.
 | 
			
		||||
     */
 | 
			
		||||
@@ -712,6 +722,13 @@ class ThirdParty implements TrackCreationInterface, TrackUpdateInterface, \Strin
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setTelephone2(?PhoneNumber $telephone2 = null): self
 | 
			
		||||
    {
 | 
			
		||||
        $this->telephone2 = $telephone2;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set type.
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
@@ -59,6 +59,10 @@ class ThirdPartyType extends AbstractType
 | 
			
		||||
                'label' => 'Phonenumber',
 | 
			
		||||
                'required' => false,
 | 
			
		||||
            ])
 | 
			
		||||
            ->add('telephone2', ChillPhoneNumberType::class, [
 | 
			
		||||
                'label' => 'telephone2',
 | 
			
		||||
                'required' => false,
 | 
			
		||||
            ])
 | 
			
		||||
            ->add('email', EmailType::class, [
 | 
			
		||||
                'required' => false,
 | 
			
		||||
            ])
 | 
			
		||||
 
 | 
			
		||||
@@ -42,6 +42,7 @@ class ThirdPartyRepository implements ObjectRepository
 | 
			
		||||
            parent.name AS name,
 | 
			
		||||
            parent.profession AS profession,
 | 
			
		||||
            parent.telephone AS telephone,
 | 
			
		||||
            parent.telephone2 AS telephone2,
 | 
			
		||||
            parent.email AS email,
 | 
			
		||||
            CONCAT_WS(' ', parent_address.street, parent_address.streetnumber, parent_postal.code, parent_postal.label) AS address,
 | 
			
		||||
            parent.comment AS comment,
 | 
			
		||||
@@ -55,6 +56,7 @@ class ThirdPartyRepository implements ObjectRepository
 | 
			
		||||
            contact.name AS contact_name,
 | 
			
		||||
            contact.firstname AS contact_firstname,
 | 
			
		||||
            contact.telephone AS contact_phone,
 | 
			
		||||
            contact.telephone2 AS contact_phone2,
 | 
			
		||||
            contact.email AS contact_email,
 | 
			
		||||
            contact.profession AS contact_profession,
 | 
			
		||||
            CONCAT_WS(' ', contact_address.street, contact_address.streetnumber, contact_postal.code, contact_postal.label) AS contact_address
 | 
			
		||||
 
 | 
			
		||||
@@ -91,6 +91,18 @@
 | 
			
		||||
                                                    }}</a
 | 
			
		||||
                                                >
 | 
			
		||||
                                            </li>
 | 
			
		||||
                                            <li v-if="thirdparty.telephone2">
 | 
			
		||||
                                                <i class="fa fa-li fa-mobile" />
 | 
			
		||||
                                                <a
 | 
			
		||||
                                                    :href="
 | 
			
		||||
                                                        'tel: ' +
 | 
			
		||||
                                                        thirdparty.telephone2
 | 
			
		||||
                                                    "
 | 
			
		||||
                                                    >{{
 | 
			
		||||
                                                        thirdparty.telephone2
 | 
			
		||||
                                                    }}</a
 | 
			
		||||
                                                >
 | 
			
		||||
                                            </li>
 | 
			
		||||
                                            <li v-if="thirdparty.email">
 | 
			
		||||
                                                <i
 | 
			
		||||
                                                    class="fa fa-li fa-envelope-o"
 | 
			
		||||
@@ -121,6 +133,12 @@
 | 
			
		||||
                                        thirdparty.telephone
 | 
			
		||||
                                    }}</a>
 | 
			
		||||
                                </li>
 | 
			
		||||
                                <li v-if="thirdparty.telephone2">
 | 
			
		||||
                                    <i class="fa fa-li fa-mobile" />
 | 
			
		||||
                                    <a :href="'tel: ' + thirdparty.telephone2"
 | 
			
		||||
                                        >{{ thirdparty.telephone2 }}
 | 
			
		||||
                                    </a>
 | 
			
		||||
                                </li>
 | 
			
		||||
                                <li v-if="thirdparty.email">
 | 
			
		||||
                                    <i class="fa fa-li fa-envelope-o" />
 | 
			
		||||
                                    <a :href="'mailto: ' + thirdparty.email">{{
 | 
			
		||||
 
 | 
			
		||||
@@ -223,6 +223,19 @@
 | 
			
		||||
            />
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="input-group mb-3">
 | 
			
		||||
            <span class="input-group-text" id="phonenumber2"
 | 
			
		||||
                ><i class="fa fa-fw fa-phone"
 | 
			
		||||
            /></span>
 | 
			
		||||
            <input
 | 
			
		||||
                class="form-control form-control-lg"
 | 
			
		||||
                v-model="thirdparty.telephone2"
 | 
			
		||||
                :placeholder="$t('thirdparty.phonenumber2')"
 | 
			
		||||
                :aria-label="$t('thirdparty.phonenumber2')"
 | 
			
		||||
                aria-describedby="phonenumber2"
 | 
			
		||||
            />
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div v-if="parent">
 | 
			
		||||
            <div class="input-group mb-3">
 | 
			
		||||
                <span class="input-group-text" id="comment"
 | 
			
		||||
@@ -263,6 +276,7 @@ export default {
 | 
			
		||||
                firstname: "",
 | 
			
		||||
                name: "",
 | 
			
		||||
                telephone: "",
 | 
			
		||||
                telephone2: "",
 | 
			
		||||
                civility: null,
 | 
			
		||||
                profession: "",
 | 
			
		||||
            },
 | 
			
		||||
@@ -368,9 +382,11 @@ export default {
 | 
			
		||||
        addQueryItem(field, queryItem) {
 | 
			
		||||
            switch (field) {
 | 
			
		||||
                case "name":
 | 
			
		||||
                    this.thirdparty.name
 | 
			
		||||
                        ? (this.thirdparty.name += ` ${queryItem}`)
 | 
			
		||||
                        : (this.thirdparty.name = queryItem);
 | 
			
		||||
                    if (this.thirdparty.name) {
 | 
			
		||||
                        this.thirdparty.name += ` ${queryItem}`;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        this.thirdparty.name = queryItem;
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case "firstName":
 | 
			
		||||
                    this.thirdparty.firstname = queryItem;
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ const thirdpartyMessages = {
 | 
			
		||||
      name: "Dénomination",
 | 
			
		||||
      email: "Courriel",
 | 
			
		||||
      phonenumber: "Téléphone",
 | 
			
		||||
      phonenumber2: "Autre numéro de téléphone",
 | 
			
		||||
      comment: "Commentaire",
 | 
			
		||||
      profession: "Qualité",
 | 
			
		||||
      civility: "Civilité",
 | 
			
		||||
 
 | 
			
		||||
@@ -115,6 +115,10 @@
 | 
			
		||||
                            {% else %}
 | 
			
		||||
                                <span class="chill-no-data-statement">{{ 'thirdparty.No_phonenumber'|trans }}</span>
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                            {% if thirdparty.telephone2 is not null %}
 | 
			
		||||
                                {% if thirdparty.telephone is not null %}, {% endif %}
 | 
			
		||||
                                <a href="{{ 'tel:' ~ thirdparty.telephone2|phone_number_format('E164') }}">{{ thirdparty.telephone2|chill_format_phonenumber }}</a>
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                        </li>
 | 
			
		||||
                        <li><i class="fa fa-li fa-envelope-o"></i>
 | 
			
		||||
                            <a href="{{ 'mailto:' ~ thirdparty.email }}">
 | 
			
		||||
@@ -135,8 +139,14 @@
 | 
			
		||||
                    }) }}
 | 
			
		||||
                </li>
 | 
			
		||||
                <li><i class="fa fa-li fa-phone"></i>
 | 
			
		||||
                    {% if thirdparty.telephone %}
 | 
			
		||||
                        <a href="{{ 'tel:' ~ thirdparty.telephone|phone_number_format('E164') }}">{{ thirdparty.telephone|chill_format_phonenumber }}</a>
 | 
			
		||||
                    {% if thirdparty.telephone or thirdparty.telephone2 %}
 | 
			
		||||
                        {% if thirdparty.telephone is not null %}
 | 
			
		||||
                            <a href="{{ 'tel:' ~ thirdparty.telephone|phone_number_format('E164') }}">{{ thirdparty.telephone|chill_format_phonenumber }}</a>
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                        {% if thirdparty.telephone2 is not null %}
 | 
			
		||||
                            {% if thirdparty.telephone is not null %}, {% endif %}
 | 
			
		||||
                            <a href="{{ 'tel:' ~ thirdparty.telephone2|phone_number_format('E164') }}">{{ thirdparty.telephone2|chill_format_phonenumber }}</a>
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    {% else %}
 | 
			
		||||
                        <span class="chill-no-data-statement">{{ 'thirdparty.No_phonenumber'|trans }}</span>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@
 | 
			
		||||
{{ form_row(form.typesAndCategories) }}
 | 
			
		||||
 | 
			
		||||
{{ form_row(form.telephone) }}
 | 
			
		||||
{{ form_row(form.telephone2) }}
 | 
			
		||||
{{ form_row(form.email) }}
 | 
			
		||||
 | 
			
		||||
{% if form.contactDataAnonymous is defined %}
 | 
			
		||||
 
 | 
			
		||||
@@ -24,17 +24,24 @@
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="row">
 | 
			
		||||
            <div class="form-group col-md-5 mb-3">
 | 
			
		||||
            <div class="form-group col-md-6 mb-3">
 | 
			
		||||
                {{ form_widget(form.telephone) }}
 | 
			
		||||
                {{ form_errors(form.telephone) }}
 | 
			
		||||
                {{ form_label(form.telephone) }}
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="form-group col-md-5 mb-3">
 | 
			
		||||
            <div class="form-group col-md-6 mb-3">
 | 
			
		||||
                {{ form_widget(form.telephone2) }}
 | 
			
		||||
                {{ form_errors(form.telephone2) }}
 | 
			
		||||
                {{ form_label(form.telephone2) }}
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="row">
 | 
			
		||||
            <div class="form-group col-md-6 mb-3">
 | 
			
		||||
                {{ form_widget(form.email) }}
 | 
			
		||||
                {{ form_errors(form.email) }}
 | 
			
		||||
                {{ form_label(form.email) }}
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="form-group col-md-2 mb-3">
 | 
			
		||||
            <div class="form-group col-md-6 mb-3">
 | 
			
		||||
                {{ form_widget(form.contactDataAnonymous) }}
 | 
			
		||||
                {{ form_label(form.contactDataAnonymous) }}
 | 
			
		||||
                {{ form_errors(form.contactDataAnonymous) }}
 | 
			
		||||
 
 | 
			
		||||
@@ -76,6 +76,18 @@
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    </dd>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    <dt>{{ 'Phonenumber2'|trans }}</dt>
 | 
			
		||||
                    <dd>
 | 
			
		||||
                        {% if thirdParty.telephone2 == null %}
 | 
			
		||||
                            <span class="chill-no-data-statement">{{ 'thirdparty.No_phonenumber'|trans }}</span>
 | 
			
		||||
                        {%  else %}
 | 
			
		||||
                            <a href="{{ 'tel:' ~ thirdParty.telephone2|phone_number_format('E164') }}">
 | 
			
		||||
                                {{ thirdParty.telephone2|chill_format_phonenumber }}
 | 
			
		||||
                            </a>
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    </dd>
 | 
			
		||||
 | 
			
		||||
                    <dt>{{ 'email'|trans }}<dt>
 | 
			
		||||
                    <dd>
 | 
			
		||||
                        {% if thirdParty.email == null %}
 | 
			
		||||
 
 | 
			
		||||
@@ -55,6 +55,7 @@ class ThirdPartyNormalizer implements NormalizerAwareInterface, NormalizerInterf
 | 
			
		||||
            'profession' => $this->normalizer->normalize($thirdParty->getProfession(), $format, $context),
 | 
			
		||||
            'address' => $this->normalizer->normalize($thirdParty->getAddress(), $format, ['address_rendering' => 'short']),
 | 
			
		||||
            'telephone' => $this->normalizer->normalize($thirdParty->getTelephone(), $format, $context),
 | 
			
		||||
            'telephone2' => $this->normalizer->normalize($thirdParty->getTelephone2(), $format, $context),
 | 
			
		||||
            'email' => $thirdParty->getEmail(),
 | 
			
		||||
            'isChild' => $thirdParty->isChild(),
 | 
			
		||||
            'parent' => $this->normalizer->normalize($thirdParty->getParent(), $format, $context),
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,8 @@ components:
 | 
			
		||||
          type: string
 | 
			
		||||
        telephone:
 | 
			
		||||
          type: string
 | 
			
		||||
        telephone2:
 | 
			
		||||
          type: string
 | 
			
		||||
        address:
 | 
			
		||||
          $ref: "#/components/schemas/Address"
 | 
			
		||||
    Address:
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,34 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Chill is a software for social workers
 | 
			
		||||
 *
 | 
			
		||||
 * For the full copyright and license information, please view
 | 
			
		||||
 * the LICENSE file that was distributed with this source code.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace Chill\Migrations\ThirdParty;
 | 
			
		||||
 | 
			
		||||
use Doctrine\DBAL\Schema\Schema;
 | 
			
		||||
use Doctrine\Migrations\AbstractMigration;
 | 
			
		||||
 | 
			
		||||
final class Version20250325085950 extends AbstractMigration
 | 
			
		||||
{
 | 
			
		||||
    public function getDescription(): string
 | 
			
		||||
    {
 | 
			
		||||
        return 'Add a second telephone number to ThirdParty';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function up(Schema $schema): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->addSql('ALTER TABLE chill_3party.third_party ADD telephone2 VARCHAR(35) DEFAULT NULL');
 | 
			
		||||
        $this->addSql('COMMENT ON COLUMN chill_3party.third_party.telephone2 IS \'(DC2Type:phone_number)\'');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function down(Schema $schema): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->addSql('ALTER TABLE chill_3party.third_party DROP telephone2');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -4,6 +4,7 @@ third parties: tiers
 | 
			
		||||
firstname: Prénom
 | 
			
		||||
name: Nom
 | 
			
		||||
telephone: Téléphone
 | 
			
		||||
telephone2: Autre numéro de téléphone
 | 
			
		||||
adress: Adresse
 | 
			
		||||
email: Courriel
 | 
			
		||||
comment: Commentaire
 | 
			
		||||
@@ -39,7 +40,7 @@ thirdparty.A contact: Une personne physique
 | 
			
		||||
thirdparty.contact: Personne physique
 | 
			
		||||
thirdparty.Contact of: Contact de
 | 
			
		||||
thirdparty.a_company_explanation: >-
 | 
			
		||||
    Les personnes morales peuvent compter un ou plusieurs contacts, interne à l'instution. Il est également possible de
 | 
			
		||||
    Les personnes morales peuvent compter un ou plusieurs contacts, interne à l'institution. Il est également possible de
 | 
			
		||||
    leur associer un acronyme, et le nom d'un service.
 | 
			
		||||
thirdparty.a_contact_explanation: >-
 | 
			
		||||
    Les personnes physiques ne disposent pas d'acronyme, de service, ou de contacts sous-jacents. Il est possible de leur
 | 
			
		||||
@@ -149,6 +150,8 @@ Contact id: Identifiant du contact
 | 
			
		||||
Contact name: Nom du contact
 | 
			
		||||
Contact  firstname: Prénom du contact
 | 
			
		||||
Contact phone: Téléphone du contact
 | 
			
		||||
Contact phone2: Autre téléphone du contact
 | 
			
		||||
Telephone2: Autre téléphone
 | 
			
		||||
Contact email: Courrier électronique du contact
 | 
			
		||||
Contact address: Adresse du contact
 | 
			
		||||
Contact profession: Profession du contact
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user