Compare commits

..

224 Commits

Author SHA1 Message Date
4f51ef81ad Add resources and examples for chill:main:ticket_motives_import command
- Added a `README.md` file in `resources/ticket_motives_import/` to explain the command's usage.
- Included a sample `motives.yaml` file with predefined ticket motives for importing.
2025-09-29 13:05:42 +02:00
4637dc692c Add OverrideTranslationCommand for generating customized translation catalogues
- Introduced a Symfony console command `chill:main:override_translation` to apply YAML-defined translation overrides.
- Added an example configuration file in `resources/translation_override/` to illustrate usage.
- Updated service definitions to register the new command.
2025-09-29 13:05:32 +02:00
38935edb93 Merge branch '71-fix-bug-add-urgent-on-init-modal-add-config-homepage' into 'ticket-app-master'
Correction de bugs, ajout champs urgents dans la modal d'initialisation du ticket et ajout d'un configuration pour l'affichage des tabs dans la homepage

See merge request Chill-Projet/chill-bundles!884
2025-09-22 09:23:30 +00:00
Boris Waaub
e1ef65d4ca Correction de bugs, ajout champs urgents dans la modal d'initialisation du ticket et ajout d'un configuration pour l'affichage des tabs dans la homepage 2025-09-22 09:23:30 +00:00
ec9d0be70b Merge branch '71-task-feature-and-bug-by-status-for-boris' into 'ticket-app-master'
Misc: homepage widget with tickets, and improvements in ticket list

See merge request Chill-Projet/chill-bundles!879
2025-09-16 11:16:57 +00:00
Boris Waaub
0ba2cbc1e8 Misc: homepage widget with tickets, and improvements in ticket list 2025-09-16 11:16:57 +00:00
e87429933a Merge branch 'ticket/filter-ticket-by-id' into 'ticket-app-master'
Add ticket filtering "byTicketId"

See merge request Chill-Projet/chill-bundles!882
2025-09-15 09:17:23 +00:00
8e2e676e3d Add ticket filtering "byTicketId" 2025-09-15 11:11:40 +02:00
e12ad563a3 Merge branch '1604-by-creator-and-by-user-assign-selector-for-ticket-list' into 'ticket-app-master'
[Frontend] Ajouter les sélecteur "par créateur", et "par utilisateur assigné"

See merge request Chill-Projet/chill-bundles!876
2025-09-09 08:24:08 +00:00
Boris Waaub
711aa8db9b [Frontend] Ajouter les sélecteur "par créateur", et "par utilisateur assigné" 2025-09-09 08:24:08 +00:00
e78d44953f Merge branch 'ticket/improve-ticket-list' into 'ticket-app-master'
Fix bugs in api endpoint to filter tickets, and add parameters byAddresseeGroup and byCreator

See merge request Chill-Projet/chill-bundles!875
2025-09-08 14:18:02 +00:00
18f67801c7 Fix bugs in api endpoint to filter tickets, and add parameters byAddresseeGroup and byCreator 2025-09-08 14:18:02 +00:00
c815e6bc69 Merge branch 'master' into ticket-app-master 2025-09-08 16:13:02 +02:00
807f2711fe Merge branch 'fix-and-change-from-board-78' into 'ticket-app-master'
Améliorations liées au board 78

See merge request Chill-Projet/chill-bundles!873
2025-09-08 12:19:49 +00:00
Boris Waaub
cd594cd580 Améliorations liées au board 78 2025-09-08 12:19:49 +00:00
fb6b26bfb5 fix type hinting 2025-09-05 18:37:36 +02:00
c5cedb8bd6 fix cs 2025-09-05 18:34:37 +02:00
2665e43a61 Merge branch 'master' into ticket-app-master
# Conflicts:
#	.eslint-baseline.json
#	src/Bundle/ChillMainBundle/Entity/User.php
#	src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue
#	src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue
#	src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue
#	src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue
#	src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CountrySelection.vue
#	src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/EditPane.vue
#	src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/ShowPane.vue
#	src/Bundle/ChillThirdPartyBundle/translations/messages.fr.yml
2025-09-05 18:32:01 +02:00
25561cdf63 Add an importer for motives 2025-09-02 10:16:54 +02:00
10b73e06e1 Merge branch 'enhance-multiple-tasks-from-board-78' into 'ticket-app-master'
Améliorations du dernier MR multiple-tasks-from-board-78

See merge request Chill-Projet/chill-bundles!870
2025-09-01 13:35:15 +00:00
Boris Waaub
e7c04e34a9 Améliorations du dernier MR multiple-tasks-from-board-78 2025-09-01 13:35:15 +00:00
164beee7c6 Merge branch 'multiple-tasks-from-board-78' into 'ticket-app-master'
Merge request contenant différentes tâches provenant du board 78

See merge request Chill-Projet/chill-bundles!864
2025-08-18 15:32:00 +00:00
Boris Waaub
4d96eb9457 Merge request contenant différentes tâches provenant du board 78 2025-08-18 15:31:59 +00:00
fe2eba3b29 Merge branch '1249-implement-app-vue-with-tickets-list' into 'ticket-app-master'
Implémenter une app vue avec la liste des tickets attribués

See merge request Chill-Projet/chill-bundles!858
2025-07-18 16:06:17 +00:00
Boris Waaub
61d1232e31 Implémenter une app vue avec la liste des tickets attribués 2025-07-18 16:06:16 +00:00
6594d4f6a6 Merge branch 'ticket/add-files-to-motives' into 'ticket-app-master'
[Ticket]  Add documents to Motive

See merge request Chill-Projet/chill-bundles!862
2025-07-18 14:55:12 +00:00
1a66a9e864 [Ticket] Add documents to Motive 2025-07-18 14:55:12 +00:00
1b74c119dc Merge branch 'ticket/supplementary-comments-on-motive' into 'ticket-app-master'
Add `supplementaryComments` property to Motive entity, update fixtures and types

See merge request Chill-Projet/chill-bundles!861
2025-07-18 13:18:44 +00:00
14d88810f3 Merge branch 'ticket/add-filter-by-addressee-on-ticket-api' into 'ticket-app-master'
[Ticket] add filter by addressee on ticket list api

See merge request Chill-Projet/chill-bundles!860
2025-07-18 11:40:48 +00:00
445a2c9358 [Ticket] add filter by addressee on ticket list api 2025-07-18 11:40:48 +00:00
c8baf0a8aa Merge branch 'ticket/add-filters-to-list' into 'ticket-app-master'
Ticket: ajout de paramètres à la requête de liste de tickets

See merge request Chill-Projet/chill-bundles!857
2025-07-16 13:39:27 +00:00
faed443a96 Ticket: ajout de paramètres à la requête de liste de tickets 2025-07-16 13:39:27 +00:00
bbf387d96f Merge branch '1243-display-ticket-list' into 'ticket-app-master'
Créer un composant pour afficher une liste des tickets

See merge request Chill-Projet/chill-bundles!849
2025-07-16 09:04:58 +00:00
Boris Waaub
b7c9b60744 Créer un composant pour afficher une liste des tickets 2025-07-16 09:04:57 +00:00
2bd303bbbe Add supplementaryComments property to Motive entity, update fixtures and types 2025-07-11 16:00:58 +02:00
c5e6122d2c Add deleted boolean property to Ticket type definition 2025-07-11 14:59:34 +02:00
088b876e20 Merge branch 'ticket/alter-comments-on-ticket' into 'ticket-app-master'
Tickets: edit comments and mark them as deleted

See merge request Chill-Projet/chill-bundles!854
2025-07-11 12:56:19 +00:00
3400656d7c Tickets: edit comments and mark them as deleted 2025-07-11 12:56:19 +00:00
568c8be7fd Update baseline for eslint 2025-07-09 21:57:56 +02:00
538ecc42ea Update .editorconfig for correct formatting rules in file patterns 2025-07-09 21:57:38 +02:00
15d26d4b06 Refactor selectItsMe and removeEntity to improve type annotations and code readability in PickEntity.vue 2025-07-09 21:57:28 +02:00
d8bd9bd7cd Restore defaults and behaviour with pick entity on PickEntity.vue 2025-07-09 18:08:04 +02:00
dcdfba5ccd eslint fixes 2025-07-09 17:46:36 +02:00
0204bdd38d Restore features after merging 2025-07-09 17:46:16 +02:00
392fd01b56 Merge branch 'master' into ticket-app-master
# Conflicts:
#	src/Bundle/ChillMainBundle/Export/Formatter/CSVFormatter.php
#	src/Bundle/ChillMainBundle/Export/Formatter/CSVListFormatter.php
#	src/Bundle/ChillMainBundle/Export/Formatter/SpreadsheetListFormatter.php
#	src/Bundle/ChillMainBundle/Resources/public/vuejs/PickEntity/PickEntity.vue
#	src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/GeographicalUnitStatAggregator.php
#	src/Bundle/ChillPersonBundle/Resources/public/types.ts
#	src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons.vue
2025-07-09 13:44:23 +02:00
35844f3b73 Merge branch 'ticket/list-add-opening-state' into 'ticket-app-master'
Ajout du statut opening / closed pour la liste des tickets

See merge request Chill-Projet/chill-bundles!850
2025-07-08 13:48:53 +00:00
7506b918d7 Ajout du statut opening / closed pour la liste des tickets 2025-07-08 13:48:43 +00:00
cfba291f2c Merge branch 'ticket-app-master' into 'ticket-app-master'
Fixes réunion 7/7

See merge request Chill-Projet/chill-bundles!852
2025-07-07 15:35:20 +00:00
borisw
04438c09d3 FIX: 1403 - Ajout de la gestion du pluriel pour l'état des usagers dans l'historique des tickets et mise à jour des traductions associées. 2025-07-07 17:14:31 +02:00
2a54d1b909 Merge branch '1405-refactor-to-get-thirdparty' into 'ticket-app-master'
Refactor third-party type imports and update related components

See merge request Chill-Projet/chill-bundles!851
2025-07-07 14:56:35 +00:00
borisw
628eeac5e0 Merge branch 'ticket-app-master' into 1405-refactor-to-get-thirdparty 2025-07-07 16:54:54 +02:00
a2263b3fa1 Fix incorrect alias in ThirdPartyRepository query builder expressions 2025-07-07 16:49:56 +02:00
74796d0fb0 Remove unused @symfony/ux-translator dependency and adjust specs-build script. 2025-07-07 16:49:56 +02:00
c19481e40a Fix incorrect alias in ThirdPartyRepository query builder expressions 2025-07-07 16:37:46 +02:00
borisw
6eeb717b1a Refactor third-party type imports and update related components
- Changed import path for ThirdParty type in TypeThirdParty.vue and updated its usage.
- Refactored PersonText.vue to import Person and AltName types from ChillPersonAssets.
- Updated types.ts in ChillThirdPartyBundle to include a new 'type' field in the Thirdparty interface.
- Modified TicketBundle types to accommodate Thirdparty type in CallerState.
- Adjusted AddresseeSelectorComponent.vue to use 'thirdparty' instead of 'third_party'.
- Refined BannerComponent.vue to improve readability and maintainability.
- Updated CallerSelectorComponent.vue to reflect changes in entity types.
- Enhanced TicketHistoryListComponent.vue to handle both Person and Thirdparty types.
- Refactored TicketHistoryPersonComponent.vue to accept both Person and Thirdparty entities.
2025-07-07 16:35:31 +02:00
beb7c462da Remove unused @symfony/ux-translator dependency and adjust specs-build script. 2025-07-07 16:03:01 +02:00
borisw
dbf363a9e8 Ajouter le fichier de configuration Prettier avec des paramètres de formatage 2025-07-07 15:27:40 +02:00
64a2f7c9ed Fix definition for Ticket and SimpleTicket 2025-07-07 14:05:26 +02:00
f26d9739c8 Merge branch 'ticket/option-one-multi-person-entity-per-ticket' into 'ticket-app-master'
Add phone number parsing functionality

See merge request Chill-Projet/chill-bundles!848
2025-07-04 13:36:55 +00:00
afa5edc1d8 Inject personPerTicket parameter into EditTicketController and expose it to the frontend via edit.html.twig. Refactor related type definitions. 2025-07-04 15:33:03 +02:00
42d6c9e672 Add SetPersonCommandConstraint and its validator with test coverage for ChillTicketBundle 2025-07-04 15:33:02 +02:00
2b22d4cb7c Add configuration for ChillTicketBundle parameters: add an option to set one / multi Person entities per ticket 2025-07-04 15:33:02 +02:00
c8e5d0eb37 fix rector 2025-07-04 14:35:46 +02:00
2bf8ad5d6c Merge branch 'ticket/list-tickets' into 'ticket-app-master'
Ajout d'une liste de tickets

See merge request Chill-Projet/chill-bundles!847
2025-07-04 09:00:12 +00:00
11698a52e3 Ajout d'une liste de tickets 2025-07-04 09:00:12 +00:00
70955573e8 Merge branch '1344-1246-1257-afficher-patient-suggérés-et-selecteur-urgent' into 'ticket-app-master'
Afficher les patients suggérés et ajouter un sélecteur urgent/non urgent

See merge request Chill-Projet/chill-bundles!841
2025-07-04 07:45:34 +00:00
Boris Waaub
3df4043eb9 Afficher les patients suggérés et ajouter un sélecteur urgent/non urgent 2025-07-04 07:45:33 +00:00
06e8264dde Merge branch 'refs/heads/master' into ticket-app-master
# Conflicts:
#	src/Bundle/ChillPersonBundle/Resources/public/types.ts
#	src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriod/SetReferrer.vue
2025-07-02 17:28:59 +02:00
b451d2c4a3 Merge branch 'task/1245-backend-cr-er-un-point-d-api-de-suggestion-des-usagers-person-pour-un-ticket' into 'ticket-app-master'
Créer un point d'api de suggestion des usagers pour un ticket

See merge request Chill-Projet/chill-bundles!845
2025-07-01 12:38:03 +00:00
4f93150874 Créer un point d'api de suggestion des usagers pour un ticket 2025-07-01 12:38:02 +00:00
0566ab0910 Merge branch '1241-add-feature-close-open-ticket' into 'ticket-app-master'
Add feature open and close ticket

See merge request Chill-Projet/chill-bundles!835
2025-06-24 10:44:04 +00:00
Boris Waaub
f4eeee1598 Add feature open and close ticket 2025-06-24 10:44:03 +00:00
33cf16fc13 Merge branch 'task/1255-backend-cr-er-un-point-d-api-pour-enregistrer-le-fait-que-le-ticket-est-urgent-ou-non' into 'ticket-app-master'
Record that a ticket can be in emergency, or not

See merge request Chill-Projet/chill-bundles!840
2025-06-24 10:42:51 +00:00
0a331aab37 Record that a ticket can be in emergency, or not 2025-06-24 10:42:51 +00:00
d43b739654 Merge branch 'task/1240-impl-menter-un-backend-pour-cloturer-puis-r-ouvrir-le-ticket' into 'ticket-app-master'
Add api endpoint to open and close ticket

See merge request Chill-Projet/chill-bundles!839
2025-06-20 15:42:44 +00:00
c72432efae Add api endpoint to open and close ticket 2025-06-20 15:42:43 +00:00
95975fae55 fix cs 2025-06-20 17:35:19 +02:00
95a7efa138 Merge branch 'master' into ticket-app-master 2025-06-20 17:35:06 +02:00
45e193ff6d Merge remote-tracking branch 'origin/master' into ticket-app-master 2025-06-20 12:53:20 +02:00
dfc146ff3f Merge remote-tracking branch 'origin/ticket-app-master' into ticket-app-master 2025-06-20 12:45:33 +02:00
b41fcf66a9 Merge branch '1277-refacto-use-symfony-translation' into 'ticket-app-master'
1277 refacto use symfony translation

See merge request Chill-Projet/chill-bundles!836
2025-06-16 10:59:42 +00:00
Boris Waaub
a8dd1b3548 1277 refacto use symfony translation 2025-06-16 10:59:42 +00:00
2b99a480ac Add StateHistory and StateEnum entities to track ticket state changes
Integrated the `StateHistory` entity to manage state transitions in tickets and the `StateEnum` for defining state values (`open`, `closed`). Updated `Ticket` to handle associations with state histories and provide state management methods. Added migration for `state_history` table and extended `TicketTest` for state-related tests.
2025-06-03 12:19:52 +02:00
7633e587bb Expand and refine development guidelines
Added detailed setup instructions, including Docker and asset management steps. Updated guidelines on testing structure, code quality tools, debugging, and deployment processes. Enhanced clarity and streamlined processes for developers. Updated `TicketTest` with additional tests for `externalRef`.
2025-06-02 16:02:33 +02:00
fc61dfdf3a Fix CS and add more comments within ticket bundle 2025-06-02 15:51:11 +02:00
f1a5b5c49e add info for junie 2025-06-02 15:32:52 +02:00
ec685dcd47 Merge branch 'prepare-junie' into ticket-app-master 2025-06-02 15:27:38 +02:00
631ae3eedd first impl for junie guildelines 2025-06-02 15:26:58 +02:00
440a7837ac clean phpstan baseline 2025-06-02 15:26:58 +02:00
e0abf34784 Remove unnecessary files 2025-06-02 11:24:59 +02:00
377ae9a9dc Merge remote-tracking branch 'origin/master' into ticket-app-master 2025-05-30 14:53:44 +02:00
034dc30e30 Merge branch 'master' into ticket-app-master 2025-05-30 13:58:45 +02:00
d615111a0f Merge remote-tracking branch 'origin/ticket-app-master' into ticket-app-master 2025-05-30 13:58:28 +02:00
ffb756c712 Merge remote-tracking branch 'origin/master' into ticket-app-master 2025-05-30 13:36:20 +02:00
69daccb860 Merge remote-tracking branch 'origin/master' into ticket-app-master 2025-05-30 12:47:37 +02:00
16435423cf Replace node-sass with sass in package.json
Updated the dependency from node-sass to sass to ensure compatibility with modern tooling and resolve deprecation warnings. This change aligns with recommended practices for Sass-related workflows.
2025-05-27 15:40:00 +02:00
697b4ab436 fix compilation errors 2025-05-27 15:39:48 +02:00
67d804e28e fix compilation errors 2025-05-27 12:00:49 +02:00
cf41fa9574 fix compilation errors 2025-05-27 11:59:52 +02:00
b8b325f7d7 Add path mapping for ChillPersonAssets in tsconfig.json
This update introduces a new path alias, "ChillPersonAssets/*", to the tsconfig.json file. It allows TypeScript to resolve imports for assets within the ChillPersonBundle more efficiently.
2025-05-27 11:59:36 +02:00
e97bd8c4ef doc for creating a new bundle: add route 2025-05-27 11:59:29 +02:00
e28d7df533 Add routing to ticket bundle 2025-05-27 11:56:35 +02:00
4b20b1bc01 Merge remote-tracking branch 'refs/remotes/origin/master' into ticket-app-master 2025-05-27 11:33:22 +02:00
b15733076c Add TicketBundle to the build of open api specs 2025-05-27 10:25:10 +02:00
25be5c9ea3 Automatic eslint fixes 2025-05-27 10:21:25 +02:00
b035020c6f Clarify and expand "create a new bundle" documentation
Rewrote the "create a new bundle" guide for clarity and completeness. Added detailed steps for creating a bundle class, registering namespaces in `composer.json`, updating configuration files, and dumping autoload. These changes aim to make the instructions easier to follow for new developers.
2025-05-27 10:20:55 +02:00
128101dc46 Add ChillTicketBundle to project configuration
ChillTicketBundle is now registered in `composer.json`, `bundles.php`, and `doctrine_migrations_chill.yaml`. This integration ensures its autoloading, enables its functionality across environments, and sets up its migration paths.
2025-05-27 09:59:51 +02:00
5f2711023e Merge branch 'refs/heads/master' into ticket-app-master
# Conflicts:
#	composer.json
#	config/bundles.php
#	config/packages/doctrine_migrations_chill.yaml
#	package.json
#	src/Bundle/ChillMainBundle/DataFixtures/ORM/LoadUserGroup.php
#	src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php
#	src/Bundle/ChillMainBundle/Entity/UserGroup.php
#	src/Bundle/ChillMainBundle/Resources/public/chill/js/date.ts
#	src/Bundle/ChillMainBundle/Resources/public/lib/download-report/download-report.js
#	src/Bundle/ChillMainBundle/Resources/public/module/ckeditor5/editor_config.ts
#	src/Bundle/ChillMainBundle/Resources/public/module/ckeditor5/index.ts
#	src/Bundle/ChillMainBundle/Resources/public/page/export/download-export.js
#	src/Bundle/ChillMainBundle/Resources/public/types.ts
#	src/Bundle/ChillMainBundle/Resources/views/Dev/dev.assets.html.twig
#	src/Bundle/ChillMainBundle/Templating/Entity/UserGroupRender.php
#	src/Bundle/ChillMainBundle/chill.api.specs.yaml
#	src/Bundle/ChillMainBundle/chill.webpack.config.js
#	src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Comment.vue
#	src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue
#	src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Resources.vue
#	src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Resources/WriteComment.vue
#	src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue
#	src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/FormEvaluation.vue
#	src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/components/Household.vue
#	src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/components/MemberDetails.vue
#	src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/components/PersonComment.vue
#	src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons.vue
#	src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/Entity/PersonRenderBox.vue
#	src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/Entity/PersonText.vue
#	src/Bundle/ChillPersonBundle/Resources/public/vuejs/_js/i18n.ts
#	tests/app/config/bootstrap.php
2025-05-27 09:37:04 +02:00
bdf2ed4bbd Fix typo 2025-05-27 09:35:49 +02:00
1df542603e Refactor export download script to use ES6 and webpack
The export download script was refactored to use ES6 syntax and webpack's modular system. This included separating out the download script into its own file for better organization, removing globally-scoped JavaScript, and adding the new download script as a webpack entry point. Also, the import method for the 'mime' library was adjusted to use ES6 syntax.
2024-06-04 21:51:42 +02:00
80bcc68ce5 WIP temporarily force extension to xlsx 2024-06-04 15:40:05 +02:00
154fc3e2f6 Increase size between user-groups in AddresseeSelectorComponent.vue 2024-06-04 11:59:26 +02:00
e45af94c78 Update ticket history interface and functionality
Reworked the ticket history interface and added new functionalities. The history interface now supports multiple patients and shows changes in patients' state. Additionally, new ticket creation is now displayed in the history line, along with the creator information. Some minor textual changes were made to reflect support for multiple patients. Implemented code cleanup and removed debug statements for a cleaner codebase.
2024-06-03 23:25:53 +02:00
166a6fde20 Add feature to set concerned persons in a ticket
This commit adds the functionality to set and change the concerned persons in a ticket within the ChillTicketBundle. New vuejs components, serializers, and store modules have been introduced to achieve this. Moreover, necessary changes have been made in existing components and store index to support this functionality.
2024-06-03 22:30:12 +02:00
631f047338 Manage the persons' assocation with ticket through SetPersonsCommand and dedicated handler 2024-06-03 13:53:28 +02:00
a777588bb8 fixup! Add TicketListController test 2024-06-03 13:26:01 +02:00
ca78d112c2 Add chill_ticket.yaml to configure routes
This commit adds a new file, chill_ticket.yaml, under the tests/app/config/routes directory in the ChillTicketBundle. This file is used to define the routes for the ChillTicket application.
2024-06-03 13:23:21 +02:00
bcfd317d83 Remove ChillEventBundle and refactor framework.yaml configurations
This commit involves the deletion of ChillEventBundle from the bundles configuration. Additionally, test framework configurations are handled in a consolidated manner by moving assets configurations (json_manifest_path) from test/framework.yaml to framework.yaml. The obsolete test/framework.yaml has been deleted as it is no longer needed.
2024-06-03 13:23:09 +02:00
348740f073 Add TicketListController test
A new file, TicketListControllerTest.php, has been added to the test suite. This file includes tests to ensure that the TicketList controller is working as expected, including checking the successful response of the 'GET' request.
2024-06-03 13:22:46 +02:00
0d74f0980f Multiple fixes and improvements 2024-06-03 12:50:29 +02:00
be19dc00db Replace person-render-box with on-the-fly in BannerComponent
This change replaces the usage of person-render-box component with the on-the-fly component in the BannerComponent.vue file of ChillTicketBundle. Also, OnTheFly component is now being imported. This update simplifies the structure and improves the readability of the code.
2024-06-03 11:20:50 +02:00
643028ffd6 Update styles and markup for badges and ticket events
Updated the SCSS for badge components, including the introduction of a margin and specific font-weight for user-group badges. Additionally, changes have been made to the TicketApp store to comment out a specific condition. A few updates were made to the twig files in the ChillTicketBundle and ChillMainBundle to reflect these style changes.
2024-06-03 11:13:49 +02:00
ac4e2e5bf2 Update time calculations in BannerComponent and TicketNormalizer
Refactored BannerComponent to use an imported date function, improved time calculation logic, and enhanced code readability. Also, updated TicketNormalizer to properly normalize various datetime and user-related fields. A new date validation check has been added in the date utility file.
2024-06-01 00:31:39 +02:00
498572b96e Refactor addressee history management
This commit refactors the management of addressees history in the ticketing system. Instead of individual addition and removal events for addressees, a new event 'addressees_state' is introduced to capture the state of addressees at any given point in the history. The structure and logic of normalized tickets have been adjusted accordingly.
2024-05-31 23:43:32 +02:00
d2a61ce69b Add return path support for ticket creation and editing
This commit introduces support for a return path query parameter during ticket creation and editing operations. This enables a more user-friendly redirection after a form submission operation where the return path provides more context on the next page. The return path is now also considered in the Vue.js components, with necessary checks and validations. A 'Cancel' button has been added to the ActionToolbarComponent.vue where the return path exists.
2024-05-31 22:23:13 +02:00
a9c0567ee1 add script to run php-cs-fixer 2024-05-31 22:22:49 +02:00
76cec5b5a8 fix indentation 2024-05-31 22:22:34 +02:00
efe8a67697 Upgrade CKEditor and refactor configuration with use of typescript 2024-05-31 21:46:19 +02:00
26dfa9b028 Add ticket listing and related enhancements
Added a new functionality for listing tickets with the ability for the user to order the list. A method was added to the User class to identify if an object is an instance of User. Similarly, a method was added to the UserGroup class. User.php, UserGroup.php, TicketRepository.php, and TicketRepositoryInterface.php were updated. A new TicketListController, MotiveRepository, and SectionMenuBuilder were created. Translations were included, and services.yaml was updated.
2024-05-31 12:32:01 +02:00
50025044d3 Add UserGroupRender and Interface for UserGroup templating
A new UserGroupRender class was added to manage the templating logic for UserGroup entities. The UserGroupRenderInterface was also created, extending the ChillEntityRenderInterface. Additionally, a Twig template for rendering user groups was added.
2024-05-31 12:31:44 +02:00
e6202a2e34 Remove unnecessary fields and methods from Ticket entity
The "updatedAt" field and "getUpdatedAt" method, as well as the "createdBy" field and the "getCreatedBy" method have been removed from the Ticket entity. These fields and related methods were not necessary and their removal simplifies the entity structure.
2024-05-30 16:06:34 +02:00
b863bd967d Update address list import to latest compiled addresses
The import of the address list has been upgraded to use the latest version of the compiled addresses from Belgian-best-address. In the AddressReferenceBEFromBestAddress class, the RELEASE constant has been updated to point to the v1.1.1 tag.
2024-05-30 16:01:34 +02:00
e65bcf7275 Restore feature to see chill assets style preview in dev environment
This commit introduces a new dev.assets.html.twig file and updates the chill.yaml file to add new paths for the SASS Assets tests.
2024-05-28 16:33:13 +02:00
e00ece4200 Update form builder parameter in SearchController
Changed the first argument in the `createNamedBuilder` method from `null` to an empty string. This adjustment ensures the form factory correctly creates the builder in the SearchController.
2024-05-28 15:58:17 +02:00
640fd71402 merge ticket-app-master and fix rector / cs 2024-05-28 15:54:52 +02:00
aae50ca290 Merge branch 'ticket-app-master' into chill-bundles-ticket-app-adaptations 2024-05-28 15:08:59 +02:00
1fa483598b Merge branch 'upgrade-sf5' into ticket-app-master 2024-05-28 14:59:25 +02:00
e4b6a468f8 adding fixtures for ticket in every environment 2024-05-28 13:47:58 +02:00
Boris Waaub
66c7758023 Adapt module name 2024-05-22 11:17:07 +02:00
Boris Waaub
4750d2c24e Adapt module name 2024-05-22 11:16:18 +02:00
Boris Waaub
ca05e3d979 Layout adaptation 2024-05-22 11:12:22 +02:00
Boris Waaub
a20f9b4f86 Generalize ticket actions 2024-05-22 00:38:47 +02:00
Boris Waaub
c73c1eb8d5 Rename "appelant" by "patient" 2024-05-21 22:24:30 +02:00
Boris Waaub
8778bb0731 Use colors and badges for history and banner 2024-05-21 22:22:33 +02:00
Boris Waaub
c7d20eebc5 chore: Remove unused code in AddresseeSelectorComponent.vue 2024-05-21 20:53:15 +02:00
Boris Waaub
b9e130c159 Use suggestion for user asignee 2024-05-21 20:44:23 +02:00
Boris Waaub
3e8bc94af3 Remove user object display 2024-05-21 18:14:11 +02:00
Boris Waaub
0c914c9f9f Remove "remove_addressee" history line 2024-05-21 17:32:40 +02:00
Boris Waaub
580a60c939 Add user_group for returning type 2024-05-21 17:32:05 +02:00
Boris Waaub
4996ac3b7c Adapt layout action toolbar 2024-05-21 15:22:13 +02:00
Boris Waaub
2a23bf19cb use record_actions sticky-form-buttons 2024-05-21 10:53:25 +02:00
Boris Waaub
650d2596d9 Update ticket display to use ticket ID instead of external reference 2024-05-21 09:54:06 +02:00
Boris Waaub
2bdd5a329e Merge branch 'ticket-app-master' of gitlab.com:boriswa/chill-bundles into ticket-app-master 2024-05-21 09:53:32 +02:00
78d1776733 Add functionality to find a caller by phone number
Added a new method in PersonRepository to allow querying people by phone number. Also, a new REST API endpoint "/public/api/1.0/ticket/find-caller" was introduced and it can find a caller by their phone number. Accompanied this feature addition with corresponding test cases.
2024-05-17 13:14:26 +02:00
66dc603c85 fix cs with new version of php-cs-fixer 2024-05-17 12:20:33 +02:00
3a8154ecce Replace PhoneNumberUtil with PhonenumberHelper
The PhoneNumberUtil has been replaced with PhonenumberHelper in AssociateByPhonenumberCommandHandler and its test class. The purpose of this change is to improve phone number parsing which is now delegated to the PhonenumberHelper class in the Chill\MainBundle\Phonenumber namespace. As a consequence, the related dependencies in both the service and the test class have been updated accordingly.
2024-05-17 12:17:00 +02:00
c81828e04f Add phone number parsing functionality
Added a new method 'parse' in the PhonenumberHelper class in ChillMainBundle to sanitize and parse phone numbers. This method specifically handles phone numbers that start with '00', '+' or '0'. Associated unit tests for this new method were also added in PhonenumberHelperTest.php.
2024-05-17 12:16:28 +02:00
Boris Waaub
ec17dd7de2 Merge branch 'master' of https://gitlab.com/Chill-Projet/chill-bundles into ticket-app-master 2024-05-13 16:08:19 +02:00
76c076a5f3 Merge branch 'ticket-app-create-template' into 'ticket-app-master'
Mise à jour des messages de l'interface utilisateur pour inclure les...

See merge request Chill-Projet/chill-bundles!689
2024-05-13 13:34:43 +00:00
Boris Waaub
f0045edd6c FIX: Ouvert depuis 2024-05-13 12:33:11 +02:00
Boris Waaub
d00b76ffcd $tc n'est plus supporté pour i18n composition api, il faut utiliser $t.
FIX: Person PersonRenderBox
2024-05-13 12:16:07 +02:00
Boris Waaub
8991f0ef3f Modification i18n 2024-05-13 12:00:11 +02:00
Boris Waaub
d6f5eae0c9 Rendre les commentaire markdown 2024-05-13 11:59:50 +02:00
Boris Waaub
821fce3dd8 $tc n'est plus supporté pour i18n composiontion api, il faut utiliser $t.
Source : https://github.com/intlify/vue-cli-plugin-i18n/issues/214
i18n composion api : https://vue-i18n.intlify.dev/api/composition
2024-05-13 11:38:28 +02:00
Boris Waaub
1d33ae1e39 use ckeditor 2024-05-08 18:03:50 +02:00
Boris Waaub
19af0feb57 Use PersonRenderBox 2024-05-08 17:54:03 +02:00
Boris Waaub
1c09e9a692 Merge branch 'ticket-app-master' into ticket-app-create-template 2024-05-08 16:05:35 +02:00
Boris Waaub
d72e748388 Merge branch 'ticket-app-master' of https://gitlab.com/boriswa/chill-bundles into ticket-app-master 2024-05-08 16:02:09 +02:00
Boris Waaub
ab850b7b70 Fusionner les utilisateurs/goupes en une "Card" 2024-05-06 20:07:15 +02:00
Boris Waaub
3f9745d8cf Use teleport for banner 2024-05-06 18:03:04 +02:00
Boris Waaub
473765366a Add tranfert with AddPerson 2024-05-06 16:38:56 +02:00
Boris Waaub
6500c24a7f Déplacer le répertoire translation dans source 2024-05-02 14:10:22 +02:00
Boris Waaub
1d00457141 Ajouter les propriétés createdAt et updatedBy à l'interface Ticket 2024-05-02 14:09:52 +02:00
Boris Waaub
eb0bf56cff Add user group addressee 2024-05-02 13:18:45 +02:00
Boris Waaub
7b8cd90cf1 Add user store 2024-05-02 12:03:10 +02:00
Boris Waaub
a27d92aba0 Add comment and motive 2024-05-02 00:50:33 +02:00
Boris Waaub
85bdfb9e21 Remove banner component 2024-05-01 22:04:07 +02:00
Boris Waaub
4cffcf4de1 Use translate in setup 2024-05-01 22:03:36 +02:00
Boris Waaub
b2587a688f Déplacer le composant banner dans twig 2024-05-01 15:51:12 +02:00
Boris Waaub
c9f0e9843b Déplacer le composant banner dans twig 2024-05-01 15:49:32 +02:00
Boris Waaub
b40ad9e445 Mise à jour des messages de l'interface utilisateur pour inclure les fonctionnalités de commentaire, de motif et de transfert 2024-04-25 11:16:08 +02:00
Boris Waaub
3e10e47e29 Merge branch 'ticket-app-master' into ticket-app-create-template 2024-04-25 10:37:42 +02:00
Boris Waaub
2a1963e993 Mise à jour de l'interface utilisateur pour le composant ActionToolbarComponent 2024-04-25 10:36:45 +02:00
34c171659b Merge branch 'ticket-app/backend-3' into 'ticket-app-master'
Add functionality to set addressees for a ticket

See merge request Chill-Projet/chill-bundles!683
2024-04-24 16:50:29 +00:00
2d8b960d9e Re-open the same ticket if a ticket already exists with the same externalRef, instead of creating a new one 2024-04-24 18:48:00 +02:00
831ae03431 Merge branch 'ticket-app/backend-2' into 'ticket-app-master'
Add functionality to add comments to tickets

See merge request Chill-Projet/chill-bundles!681
2024-04-23 21:42:07 +00:00
45828174d1 Add addressee history to ticket serialization
This update extends the tickets serialization and normalisation process to include addressee history. With the changes, AddresseeHistory class now also keeps track of who removed an addressee. Additional types, tests and interfaces have been introduced to support this change.
2024-04-23 23:39:01 +02:00
ed45f14a45 Add tracking of addressee history in ticket system
The updates introduce tracking for the history of addressees in the ticket system, both when added and when removed. The user who removed an addressee is now recorded. The changes also ensure these updated aspects are correctly normalized and users can see them in the ticket history. A new database migration file was created for the changes.
2024-04-23 23:38:34 +02:00
fa67835690 Add functionality to add single addressee to tickets
This update introduces a new feature allowing end-users to add a single addressee to a ticket without removing the existing ones. This was achieved by adding a new API endpoint and updating the SetAddresseesController to handle the addition of a single addressee. Accompanying tests have also been provided to ensure the new feature works as expected.
2024-04-23 23:00:12 +02:00
b434d38091 Add functionality to set addressees for a ticket
This update includes the implementation of methods to add and retrieve addressee history in the Ticket entity, a handler for addressee setting command, denormalizer for transforming request data to SetAddresseesCommand, and corresponding tests. Additionally, it adds a SetAddresseesController for handling addressee related requests and updates the API specifications.
2024-04-23 22:50:51 +02:00
Boris Waaub
800a952532 Add base template 2024-04-23 20:41:32 +02:00
9f355032a8 Create a "do not exclude" validation constraint for user groups 2024-04-22 12:41:43 +02:00
0bc6e62d4d Add fixtures for UserGroup 2024-04-22 12:01:49 +02:00
46fb1c04b5 Add color and exclusion fields to UserGroup
This commit introduces new fields to the UserGroup entity, specifically background color, foreground color, and an exclusion key. These have been implemented both in the PHP entity and TypeScript interface definitions. Additionally, a Doctrine migration has been created to reflect these changes on the database side.
2024-04-22 12:01:28 +02:00
3b2c3d1464 Merge branch 'ticket-app-create-store' into 'ticket-app-master'
Create vuex store

See merge request Chill-Projet/chill-bundles!678
2024-04-22 08:29:56 +00:00
Boris Waaub
0bd6038160 Merge branch chill-bundles:master into ticket-app-master 2024-04-19 15:54:24 +00:00
Boris Waaub
baab8e94ce Add ticket to storeand catch error with toast in component 2024-04-19 17:46:12 +02:00
e2deb55fdb Create api endpoint for listing user-group 2024-04-19 15:34:43 +02:00
Boris Waaub
2cdfb50058 Mise en œuvre de la fonctionnalité de remplacement du motif du ticket
La validation introduit plusieurs fonctionnalités liées à la gestion du motif du ticket dans le bundle Chill-TicketBundle :
- Ajoute la possibilité de remplacer le motif d'un ticket par un nouveau.
- Fournit des fonctionnalités de gestion de l'historique des motifs du ticket.
- Implémente les modifications pertinentes au niveau du contrôleur, du gestionnaire d'actions et de l'entité.
- Intègre de nouvelles points d'API et met à jour le fichier de spécification de l'API pour la nouvelle fonctionnalité.
- Inclut des tests pour garantir le bon fonctionnement de la nouvelle fonctionnalité.
2024-04-19 14:12:09 +02:00
39d701feb2 Serialize ticket's Comment 2024-04-18 22:10:56 +02:00
613ee8b186 Add functionality to add comments to tickets
A new controller, 'AddCommentController', has been added. This controller implements the 'AddCommentCommandHandler', allowing users to add comments to tickets. Additionally, corresponding test cases were implemented. The Ticket entity was also updated to accept and manage comments. API endpoint specs were updated to reflect these changes.
2024-04-18 21:57:55 +02:00
56a1a488de Return the content of the ticket on replace motive POST request 2024-04-18 15:44:05 +02:00
3f789ad0f4 Merge branch 'ticket-app/create-entities' into 'ticket-app-master'
Add phone number search function to PersonACLAwareRepository

See merge request Chill-Projet/chill-bundles!677
2024-04-18 11:21:46 +00:00
467bea7cde Serialization of tickets with history 2024-04-18 13:13:09 +02:00
670b8eb82b Implement functionality to replace ticket's motive
The commit introduces several features related to ticket motive management in the Chill-TicketBundle:
- Adds capability to replace a ticket's motive with a new one.
- Provides ticket motive history management features.
- Implements relevant changes in Controller, Action Handler, and Entity levels.
- Incorporates new API endpoints and updates the API specification file for the new feature.
- Includes tests to ensure the new functionality works as expected.
2024-04-18 13:13:08 +02:00
a9760b323f Add ChillTicketBundle to configuration and autoload-dev
The commit includes the ChillTicketBundle in the bundles configuration file for testing. Additionally, the autoload-dev directive in the composer.json file was updated to include the "App" namespace for testing purposes. This ensures that the tests related to the "App" namespace are correctly autoloaded.
2024-04-18 13:13:08 +02:00
71a3a1924a Add Motive API and related fixtures to ChillTicketBundle
This update introduces the Motive API Controller to the ChillTicket bundle with its corresponding service configuration. Also included are related data fixtures for loading motive information. The motive entity has been updated to improve its serialization properties and new types were added to the TypeScript definitions of the bundle.
2024-04-18 13:13:07 +02:00
ecdc1e25bf Layout of banner for ticket 2024-04-18 13:13:07 +02:00
dd37427be1 Bootstrap ticket layout and vue app to edit ticket 2024-04-18 13:13:07 +02:00
c8467df1b1 fixup! Rename Command directory to Action to avoid confusion with symfony commands 2024-04-18 13:13:06 +02:00
4c89a954fa Refactor test, fixing the constructor 2024-04-18 13:13:05 +02:00
7c1f3b114d Rename Command directory to Action to avoid confusion with symfony commands 2024-04-18 13:13:05 +02:00
36bc4dab24 Configure a testsuite for TicketBundle 2024-04-18 13:13:04 +02:00
4b30d92282 Add ticket creation and associating by phone number functionality
This update introduces new features allowing the creation of tickets and associating them with a phone number. Specifically, relevant commands and their handlers have been created along with corresponding tests. An endpoint for ticket creation has also been set up, and the ViewTicketController has been renamed and refactored to EditTicketController to better reflect its function.
2024-04-18 13:13:04 +02:00
75fbec5489 Create entities and doctrine mapping for ticket 2024-04-18 13:13:03 +02:00
912fdd6349 Add phone number search function to PersonACLAwareRepository
A new function, findByPhone, has been added to the PersonACLAwareRepository. This function allows searching for people based on their phone numbers. Changes also reflect in the PersonACLAwareRepositoryInterface, and new test cases have been added to the PersonACLAwareRepositoryTest.
2024-04-16 14:41:55 +02:00
5832542978 load also tests for ticket bundle 2024-04-16 14:41:39 +02:00
5c3585a1ed Fix loading of environment variable in bootstrap process 2024-04-16 14:41:29 +02:00
a2f1e20ddf Fix cs 2024-04-15 15:49:47 +02:00
4d67702a76 Bootstrap loading of controllers and routes for ticket bundle 2024-04-15 15:48:25 +02:00
18e442db29 Merge branch 'ticket-app-init' into 'ticket-app-master'
Add ChillTicketBundle webpack configuration

See merge request Chill-Projet/chill-bundles!673
2024-04-15 12:44:21 +00:00
Boris Waaub
deb3d92189 Add ChillTicketBundle webpack configuration 2024-04-15 14:34:09 +02:00
a59ea7db31 Compiles with ticket bundle 2024-04-15 13:48:49 +02:00
a738b0cac9 Initialize ChillTicketBundle 2024-04-15 13:22:36 +02:00
695 changed files with 42764 additions and 34263 deletions

View File

@@ -0,0 +1,6 @@
kind: Feature
body: |
Upgrade import of address list to the last version of compiled addresses of belgian-best-address
time: 2024-05-30T16:00:03.440767606+02:00
custom:
Issue: ""

View File

@@ -0,0 +1,6 @@
kind: Feature
body: |
Upgrade CKEditor and refactor configuration with use of typescript
time: 2024-05-31T19:02:42.776662753+02:00
custom:
Issue: ""

View File

@@ -0,0 +1,6 @@
kind: Feature
body: Add a command to generate a list of permissions
time: 2025-09-04T18:10:32.334524026+02:00
custom:
Issue: ""
SchemaChange: No schema change

View File

@@ -1,7 +0,0 @@
kind: Fixed
body: |
Associate activity's creator as a participant by default, and retro-actively append the creator to each activity
time: 2025-11-18T14:05:59.904993123+01:00
custom:
Issue: "466"
SchemaChange: Add columns or tables

View File

@@ -1,6 +0,0 @@
kind: UX
body: Alphabetically order userJobs and mainLocations within user creation form
time: 2025-11-19T15:37:06.393470745+01:00
custom:
Issue: "470"
SchemaChange: No schema change

View File

@@ -1,6 +0,0 @@
kind: UX
body: Change position and color of confirm parcours button
time: 2025-11-24T15:11:15.960279853+01:00
custom:
Issue: "437"
SchemaChange: No schema change

View File

@@ -1,10 +0,0 @@
## v4.3.0 - 2025-09-08
### Feature
* ([#409](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/409)) Add 45 and 60 min calendar ranges
* Add a command to generate a list of permissions
* ([#412](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/412)) Add an absence end date
**Schema Change**: Add columns or tables
### Fixed
* fix date formatting in calendar range display
* Change route URL to avoid clash with person duplicate controller method

View File

@@ -1,8 +0,0 @@
## v4.4.0 - 2025-09-11
### Feature
* ([#359](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/359)) Allow the merge of two accompanying period works
* ([#369](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/369)) Duplication of a document to another accompanying period work evaluation
* ([#359](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/359)) Fusion of two accompanying period works
### Fixed
* Fix display of 'duplicate' and 'merge' buttons in CRUD templates
* Fix saving notification preferences in user's profile

View File

@@ -1,3 +0,0 @@
## v4.4.1 - 2025-09-11
### Fixed
* fix translations in duplicate evaluation document modal and realign close modal button

View File

@@ -1,3 +0,0 @@
## v4.4.2 - 2025-09-12
### Fixed
* Fix document generation and workflow generation do not work on accompanying period work documents

View File

@@ -1,13 +0,0 @@
## v4.5.0 - 2025-10-03
### Feature
* Only allow delete of attachment on workflows that are not final
* Move up signature buttons on index workflow page for easier access
* Filter out document from attachment list if it is the same as the workflow document
* Block edition on attached document on workflow, if the workflow is finalized or sent external
* Convert workflow's attached document to pdf while sending them external
* After a signature is canceled or rejected, going to a waiting page until the post-process routines apply a workflow transition
### Fixed
* ([#426](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/426)) Increased the number of required characters when setting a new password in Chill from 9 to 14 - GDPR compliance
* Fix permissions on storedObject which are subject by a workflow
### DX
* Introduce a WaitingScreen component to display a waiting screen

View File

@@ -1,4 +0,0 @@
## v4.5.1 - 2025-10-03
### Fixed
* Add missing javascript dependency
* Add exception handling for conversion of attachment on sending external, when documens are already in pdf

View File

@@ -1,14 +0,0 @@
## v4.6.0 - 2025-10-15
### Feature
* ([#423](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/423)) Create environment banner that can be activated and configured depending on the image deployed
* ([#394](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/394)) Only show active workflow on the page "my tracked workflow"
### Fixed
* Fix loading of classLists in SocialIssuesAcc.vue, ensure elements are present
* Fix the rendering of list of StoredObjectVersions, where there are kept version (before converting to pdf) and intermediate versions deleted
* ([#434](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/434)) Notification: fix editing of sent notification by removing form.addressesEmails, a field that no longer exists
* Fix loading of social issues and social actions within vue component
* ([#446](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/446)) Add unique condition on stored object filename, with cleaning step on existing duplicate filenames
**Schema Change**: Drop or rename table or columns, or enforce new constraint that must be manually fixed
* [workflow] take permissions into account to delete the workflow attachment
* ([#448](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/448)) Fix the execution of daily cronjob notification, when the previous last execution storage was invalid

View File

@@ -1,3 +0,0 @@
## v4.6.1 - 2025-10-27
### Fixed
* Fix export case where no 'reason' is picked within the PersonHavingActivityBetweenDateFilter.php

View File

@@ -1,21 +0,0 @@
## v4.7.0 - 2025-11-10
### Feature
* ([#385](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/385)) Create invitation list in user menu
* ([#404](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/404)) Add columns for comments linked to an activity in the activity list export
### Fixed
* ([#451](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/451)) Fix: display also social actions linked to parents of the selected social issue
* ([#453](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/453)) Fix: export actions and their results in csv even when action does not have any goals attached to it.
* Fix the possibility to delete a workflow
**Schema Change**: Drop or rename table or columns, or enforce new constraint that must be manually fixed
* ([#457](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/457)) Fix the fusion of thirdparty properties that are located in another schema than public for TO_ONE relations + add extra loop for MANY_TO_MANY relations where thirdparty is the source instead of the target
* ([#428](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/428)) Fix suggestion of referrer when creating notification for accompanyingPeriodWorkDocument
### DX
* Send notifications log to dedicated channel, if it exists
### UX
* ([#425](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/425)) Change the terms 'cercle' and 'centre' to 'service', and 'territoire' respectively.
* ([#542](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/542)) Improve the ux for selecting whether user wants to be notified of the final step of a workflow or all steps
* Expand timeSpent choices for evaluation document and translate them to user locale or fallback 'fr'
* ([#455](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/455)) Change the order of display for results and objectives in the social work/action form
* Wrap text when it is too long within badges

View File

@@ -1,9 +0,0 @@
## v4.8.0 - 2025-11-17
### Feature
* ([#461](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/461)) Make a calendar item on the 'mes rendez-vous' page clickable. Clicking will navigate to the edit page of the calendar item.
### Fixed
* ([#463](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/463)) Display calendar items for which an invite was accepted on the mes rendez-vous page
* Improve accessibility on login page
### UX
* ([#449](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/449)) Remove the label if there is only one scope and no scope picking field is displayed.

View File

@@ -1,6 +0,0 @@
## v4.8.1 - 2025-11-20
### Fixed
* Insert name of file as the document title when uploading
* Add missing path paramater 'id' for editing multiple participations
* ([#471](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/471)) Hide the display of inactive user groups in the api

View File

@@ -19,11 +19,11 @@ max_line_length = 80
[COMMIT_EDITMSG]
max_line_length = 0
[*.{js, vue, ts}]
[*.{js,vue,ts}]
indent_size = 2
indent_style = space
[.rst]
ident_size = 3
ident_style = space
[*.rst]
indent_size = 3
indent_style = space

4
.env
View File

@@ -92,7 +92,3 @@ REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT}
###> symfony/ovh-cloud-notifier ###
# OVHCLOUD_DSN=ovhcloud://APPLICATION_KEY:APPLICATION_SECRET@default?consumer_key=CONSUMER_KEY&service_name=SERVICE_NAME
###< symfony/ovh-cloud-notifier ###
###> symfony/loco-translation-provider ###
#LOCO_DSN=loco://API_KEY@default
###< symfony/loco-translation-provider ###

View File

@@ -234,12 +234,9 @@ This must be a decision made by a human, not by an AI. Every AI task must abort
#### Running Tests
The tests are run from the project's root (not from the bundle's root).
The tests are run from the project's root (not from the bundle's root: so, do not change the directory to any bundle directory before running tests).
```bash
# Run all tests
vendor/bin/phpunit
# Run a specific test file
vendor/bin/phpunit path/to/TestFile.php
@@ -247,9 +244,6 @@ vendor/bin/phpunit path/to/TestFile.php
vendor/bin/phpunit --filter methodName path/to/TestFile.php
```
When writing tests, only test specific files. Do not run all tests or the full
test suite.
#### Test Structure
Tests are organized by bundle and follow the same structure as the bundle itself:

4
.prettierrc Normal file
View File

@@ -0,0 +1,4 @@
{
"tabWidth": 2,
"useTabs": false
}

30
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,30 @@
{
// Use IntelliSense to learn about possible attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Chill Debug",
"type": "php",
"request": "launch",
"port": 9000,
"pathMappings": {
"/var/www/html": "${workspaceFolder}"
},
"preLaunchTask": "symfony"
},
{
"name": "Yarn Encore Dev (Watch)",
"type": "node-terminal",
"request": "launch",
"command": "yarn encore dev --watch",
"cwd": "${workspaceFolder}"
}
],
"compounds": [
{
"name": "Chill Debug + Yarn Encore Dev (Watch)",
"configurations": ["Chill Debug", "Yarn Encore Dev (Watch)"]
}
]
}

23
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,23 @@
{
"tasks": [
{
"type": "shell",
"command": "symfony",
"args": [
"server:start",
"--allow-http",
"--no-tls",
"--port=8000",
"--allow-all-ip",
"-d"
],
"label": "symfony"
},
{
"type": "shell",
"command": "yarn",
"args": ["encore", "dev", "--watch"],
"label": "webpack"
}
]
}

View File

@@ -6,111 +6,6 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
and is generated by [Changie](https://github.com/miniscruff/changie).
## v4.8.1 - 2025-11-20
### Fixed
* Insert name of file as the document title when uploading
* Add missing path paramater 'id' for editing multiple participations
* ([#471](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/471)) Hide the display of inactive user groups in the api
## v4.8.0 - 2025-11-17
### Feature
* ([#461](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/461)) Make a calendar item on the 'mes rendez-vous' page clickable. Clicking will navigate to the edit page of the calendar item.
### Fixed
* ([#463](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/463)) Display calendar items for which an invite was accepted on the mes rendez-vous page
* Improve accessibility on login page
### UX
* ([#449](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/449)) Remove the label if there is only one scope and no scope picking field is displayed.
## v4.7.0 - 2025-11-10
### Feature
* ([#385](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/385)) Create invitation list in user menu
* ([#404](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/404)) Add columns for comments linked to an activity in the activity list export
### Fixed
* ([#451](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/451)) Fix: display also social actions linked to parents of the selected social issue
* ([#453](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/453)) Fix: export actions and their results in csv even when action does not have any goals attached to it.
* Fix the possibility to delete a workflow
**Schema Change**: Drop or rename table or columns, or enforce new constraint that must be manually fixed
* ([#457](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/457)) Fix the fusion of thirdparty properties that are located in another schema than public for TO_ONE relations + add extra loop for MANY_TO_MANY relations where thirdparty is the source instead of the target
* ([#428](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/428)) Fix suggestion of referrer when creating notification for accompanyingPeriodWorkDocument
### DX
* Send notifications log to dedicated channel, if it exists
### UX
* ([#425](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/425)) Change the terms 'cercle' and 'centre' to 'service', and 'territoire' respectively.
* ([#542](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/542)) Improve the ux for selecting whether user wants to be notified of the final step of a workflow or all steps
* Expand timeSpent choices for evaluation document and translate them to user locale or fallback 'fr'
* ([#455](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/455)) Change the order of display for results and objectives in the social work/action form
* Wrap text when it is too long within badges
## v4.6.1 - 2025-10-27
### Fixed
* Fix export case where no 'reason' is picked within the PersonHavingActivityBetweenDateFilter.php
## v4.6.0 - 2025-10-15
### Feature
* ([#423](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/423)) Create environment banner that can be activated and configured depending on the image deployed
* ([#394](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/394)) Only show active workflow on the page "my tracked workflow"
### Fixed
* Fix loading of classLists in SocialIssuesAcc.vue, ensure elements are present
* Fix the rendering of list of StoredObjectVersions, where there are kept version (before converting to pdf) and intermediate versions deleted
* ([#434](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/434)) Notification: fix editing of sent notification by removing form.addressesEmails, a field that no longer exists
* Fix loading of social issues and social actions within vue component
* ([#446](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/446)) Add unique condition on stored object filename, with cleaning step on existing duplicate filenames
**Schema Change**: Drop or rename table or columns, or enforce new constraint that must be manually fixed
* [workflow] take permissions into account to delete the workflow attachment
* ([#448](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/448)) Fix the execution of daily cronjob notification, when the previous last execution storage was invalid
## v4.5.1 - 2025-10-03
### Fixed
* Add missing javascript dependency
* Add exception handling for conversion of attachment on sending external, when documens are already in pdf
## v4.5.0 - 2025-10-03
### Feature
* Only allow delete of attachment on workflows that are not final
* Move up signature buttons on index workflow page for easier access
* Filter out document from attachment list if it is the same as the workflow document
* Block edition on attached document on workflow, if the workflow is finalized or sent external
* Convert workflow's attached document to pdf while sending them external
* After a signature is canceled or rejected, going to a waiting page until the post-process routines apply a workflow transition
### Fixed
* ([#426](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/426)) Increased the number of required characters when setting a new password in Chill from 9 to 14 - GDPR compliance
* Fix permissions on storedObject which are subject by a workflow
### DX
* Introduce a WaitingScreen component to display a waiting screen
## v4.4.2 - 2025-09-12
### Fixed
* Fix document generation and workflow generation do not work on accompanying period work documents
## v4.4.1 - 2025-09-11
### Fixed
* fix translations in duplicate evaluation document modal and realign close modal button
## v4.4.0 - 2025-09-11
### Feature
* ([#359](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/359)) Allow the merge of two accompanying period works
* ([#369](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/369)) Duplication of a document to another accompanying period work evaluation
* ([#359](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/359)) Fusion of two accompanying period works
### Fixed
* Fix display of 'duplicate' and 'merge' buttons in CRUD templates
* Fix saving notification preferences in user's profile
## v4.3.0 - 2025-09-08
### Feature
* ([#409](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/409)) Add 45 and 60 min calendar ranges
* Add a command to generate a list of permissions
* ([#412](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/412)) Add an absence end date
**Schema Change**: Add columns or tables
### Fixed
* fix date formatting in calendar range display
* Change route URL to avoid clash with person duplicate controller method
## v4.2.1 - 2025-09-03
### Fixed
* Fix exports to work with DirectExportInterface

View File

@@ -54,7 +54,7 @@ Arborescence:
- person
- personvendee
- household_edit_metadata
- index.js
- index.ts
```
## Organisation des feuilles de styles

View File

@@ -14,7 +14,7 @@
"ext-openssl": "*",
"ext-redis": "*",
"ext-zlib": "*",
"champs-libres/wopi-bundle": "dev-symfony-v5@dev",
"champs-libres/wopi-bundle": "dev-master@dev",
"champs-libres/wopi-lib": "dev-master@dev",
"doctrine/data-fixtures": "^1.8",
"doctrine/doctrine-bundle": "^2.1",
@@ -54,7 +54,6 @@
"symfony/http-client": "^5.4",
"symfony/http-foundation": "^5.4",
"symfony/intl": "^5.4",
"symfony/loco-translation-provider": "^6.0",
"symfony/mailer": "^5.4",
"symfony/messenger": "^5.4",
"symfony/mime": "^5.4",
@@ -134,6 +133,7 @@
"Chill\\TaskBundle\\": "src/Bundle/ChillTaskBundle",
"Chill\\ThirdPartyBundle\\": "src/Bundle/ChillThirdPartyBundle",
"Chill\\WopiBundle\\": "src/Bundle/ChillWopiBundle/src",
"Chill\\TicketBundle\\": "src/Bundle/ChillTicketBundle/src",
"Chill\\Utils\\Rector\\": "utils/rector/src"
}
},

View File

@@ -2,6 +2,7 @@
return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
loophp\PsrHttpMessageBridgeBundle\PsrHttpMessageBridgeBundle::class => ['all' => true],
ChampsLibres\WopiBundle\WopiBundle::class => ['all' => true],
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
@@ -34,7 +35,7 @@ return [
Chill\ThirdPartyBundle\ChillThirdPartyBundle::class => ['all' => true],
Chill\BudgetBundle\ChillBudgetBundle::class => ['all' => true],
Chill\WopiBundle\ChillWopiBundle::class => ['all' => true],
Chill\TicketBundle\ChillTicketBundle::class => ['all' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
Symfony\UX\Translator\UxTranslatorBundle::class => ['all' => true],
loophp\PsrHttpMessageBridgeBundle\PsrHttpMessageBridgeBundle::class => ['all' => true],
];

View File

@@ -1,13 +1,6 @@
chill_main:
available_languages: [ '%env(resolve:LOCALE)%', 'en', 'nl' ]
available_languages: [ '%env(resolve:LOCALE)%', 'en' ]
available_countries: ['BE', 'FR']
top_banner:
visible: false
text:
fr: 'Vous travaillez actuellement avec la version de PRÉ-PRODUCTION.'
nl: 'Je werkt momenteel in de PRE-PRODUCTIE versie'
color: '#353535'
background_color: '#d8bb48'
notifications:
from_email: '%env(resolve:NOTIFICATION_FROM_EMAIL)%'
from_name: '%env(resolve:NOTIFICATION_FROM_NAME)%'

View File

@@ -1,2 +0,0 @@
chill_aside_activity:
show_concerned_persons_count: hidden

View File

@@ -1,5 +1,5 @@
chill_doc_store:
use_driver: openstack
use_driver: local_storage
local_storage:
storage_path: '%kernel.project_dir%/var/storage'
openstack:

View File

@@ -0,0 +1,5 @@
chill_ticket:
ticket:
person_per_ticket: one # One of "one"; "many"
response_time_exceeded_delay: PT12H

View File

@@ -14,6 +14,7 @@ doctrine_migrations:
'Chill\Migrations\Calendar': '@ChillCalendarBundle/migrations'
'Chill\Migrations\Budget': '@ChillBudgetBundle/migrations'
'Chill\Migrations\Report': '@ChillReportBundle/migrations'
'Chill\Migrations\Ticket': '@ChillTicketBundle/migrations'
all_or_nothing:
true

View File

@@ -1,12 +1,7 @@
# config/packages/translation.yaml
framework:
default_locale: '%env(resolve:LOCALE)%' # LOCALE=fr in .env
default_locale: en
translator:
default_path: '%kernel.project_dir%/translations'
fallbacks:
- '%env(resolve:LOCALE)%' # fr
- en
providers:
loco:
dsn: '%env(LOCO_DSN)%'
domains: [ 'messages' ]
locales: [ 'fr', 'nl' ]

View File

@@ -0,0 +1,4 @@
framework:
default_locale: '%env(resolve:LOCALE)%'
translator:
fallbacks: [ '%env(resolve:LOCALE)%' ]

View File

@@ -0,0 +1,2 @@
chill_ticket_bundle:
resource: '@ChillTicketBundle/config/routes.yaml'

View File

@@ -23,8 +23,8 @@ class "Document" {
- text description
- ArrayCollection_DocumentCategory categories
- varchar_150 content #link to openstack
- Territoire territoire
- Service service
- Center center
- Cercle cercle
- User user
- DateTime date # Creation date
}

View File

@@ -11,24 +11,94 @@
Create a new bundle
*******************
Create your own bundle is not a trivial task.
The easiest way to achieve this is seems to be :
1. Prepare a fresh installation of the chill project, in a new directory
2. Create a new bundle in this project, in the src directory
3. Initialize a git repository **at the root bundle**, and create your initial commit.
4. Register the bundle with composer/packagist. If you do not plan to distribute your bundle with packagist, you may use a custom repository for achieve this [#f1]_
5. Move to a development installation, made as described in the :ref:`installation-for-development` section, and add your new repository to the composer.json file
6. Work as :ref:`usual <editing-code-and-commiting>`
.. warning::
This part of the doc is not yet tested
TODO
Create a new directory with Bundle class
----------------------------------------
.. code-block:: bash
mkdir -p src/Bundle/ChillSomeBundle/src/config
mkdir -p src/Bundle/ChillSomeBundle/src/Controller
Add a bundle file
.. code-block:: php
<?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\SomeBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class ChillSomeBundle extends Bundle {}
And a route file:
.. code-block:: yaml
chill_ticket_controller:
resource: '@ChillTicketBundle/Controller/'
type: annotation
Register the new psr-4 namespace
--------------------------------
In composer.json, add the new psr4 namespace
.. code-block:: diff
{
"autoload": {
"psr-4": {
+ "Chill\\SomeBundle\\": "src/Bundle/ChillSomeBundle/src",
}
}
}
.. rubric:: Footnotes
Register the bundle
-------------------
Register in the file :code:`config/bundles.php`:
.. code-block:: php
Vendor\Bundle\YourBundle\YourBundle::class => ['all' => true],
And import routes in :code:`config/routes/chill_some_bundle.yaml`:
.. code-block:: yaml
chill_ticket_bundle:
resource: '@ChillSomeBundle/config/routes.yaml'
Add the doctrine_migration namespace
------------------------------------
Add the namespace to :code:`config/packages/doctrine_migrations_chill.yaml`
.. code-block:: diff
doctrine_migrations:
migrations_paths:
+ 'Chill\Some\Ticket': '@ChillSomeBundle/migrations'
Dump autoloading
----------------
.. code-block:: bash
symfony composer dump-autoload
.. [#f1] Be aware that we use the Affero GPL Licence, which ensure that all users must have access to derivative works done with this software.

View File

@@ -38,7 +38,7 @@ Certaines données sont historisées:
- les référents d'un parcours;
- les statuts d'un parcours;
- la liaison entre les territoires et les usagers;
- la liaison entre les centres et les usagers;
- etc.
Dans ces cas-là, Chill crée généralement deux colonnes, qui sont habituellement nommées :code:`startDate` et :code:`endDate`. Lorsque la colonne :code:`endDate` est à :code:`NULL`, cela signifie que la période n'est pas "fermée". La colonne :code:`startDate` n'est pas nullable.

View File

@@ -1,6 +1,6 @@
order,table_schema,table_name,commentaire
1,chill_3party,party_category,Catégorie de tiers
2,chill_3party,party_center,Association entre les tiers et les territoires (déprécié)
2,chill_3party,party_center,Association entre les tiers et les centres (déprécié)
3,chill_3party,party_profession,Profession du tiers (déprécié)
4,chill_3party,third_party,Tiers
5,chill_3party,thirdparty_category,association tiers - catégories
@@ -54,7 +54,7 @@ order,table_schema,table_name,commentaire
53,public,activitytpresence,Présence aux échanges
54,public,activitytype,Types d'échanges
55,public,activitytypecategory,Catégories de types d'échanges
56,public,centers,"Territoires (territoires, agences, etc.)"
56,public,centers,"Centres (territoires, agences, etc.)"
57,public,chill_activity_activity_chill_person_socialaction,
58,public,chill_activity_activity_chill_person_socialissue
59,public,chill_docgen_template,Gabarits de documents
@@ -111,7 +111,7 @@ order,table_schema,table_name,commentaire
110,public,chill_person_marital_status,Etats civils
111,public,chill_person_not_duplicate,
112,public,chill_person_person,Usagers
113,public,chill_person_person_center_history,Historique des territoires d'un usagers
113,public,chill_person_person_center_history,Historique des centres d'un usagers
114,public,chill_person_persons_to_addresses,Déprécié
115,public,chill_person_phone,Numéros d etéléphone supplémentaires d'un usager
116,public,chill_person_relations,Types de relations de filiation
@@ -142,7 +142,7 @@ order,table_schema,table_name,commentaire
141,public,permission_groups
142,public,permissionsgroup_rolescope
143,public,persons_spoken_languages
144,public,regroupment,Regroupement de territoires
144,public,regroupment,Regroupement de centres
145,public,regroupment_center,
146,public,role_scopes,
147,public,scopes,Services
1 order table_schema table_name commentaire
2 1 chill_3party party_category Catégorie de tiers
3 2 chill_3party party_center Association entre les tiers et les territoires (déprécié) Association entre les tiers et les centres (déprécié)
4 3 chill_3party party_profession Profession du tiers (déprécié)
5 4 chill_3party third_party Tiers
6 5 chill_3party thirdparty_category association tiers - catégories
54 53 public activitytpresence Présence aux échanges
55 54 public activitytype Types d'échanges
56 55 public activitytypecategory Catégories de types d'échanges
57 56 public centers Territoires (territoires, agences, etc.) Centres (territoires, agences, etc.)
58 57 public chill_activity_activity_chill_person_socialaction
59 58 public chill_activity_activity_chill_person_socialissue
60 59 public chill_docgen_template Gabarits de documents
111 110 public chill_person_marital_status Etats civils
112 111 public chill_person_not_duplicate
113 112 public chill_person_person Usagers
114 113 public chill_person_person_center_history Historique des territoires d'un usagers Historique des centres d'un usagers
115 114 public chill_person_persons_to_addresses Déprécié
116 115 public chill_person_phone Numéros d etéléphone supplémentaires d'un usager
117 116 public chill_person_relations Types de relations de filiation
142 141 public permission_groups
143 142 public permissionsgroup_rolescope
144 143 public persons_spoken_languages
145 144 public regroupment Regroupement de territoires Regroupement de centres
146 145 public regroupment_center
147 146 public role_scopes
148 147 public scopes Services

View File

@@ -1,419 +0,0 @@
============================================
Directives for creating new translation keys
============================================
These directives are meant to ensure better consistency across bundles, avoid duplication, and make keys more predictable.
General Principles
==================
1. **Use lowercase snake_case for all keys**
2. **Use dot-separated namespaces**
The dot is used to reflect:
- bundle
- feature
- sub-feature
- key type
3. **Do not use spaces in keys**
4. **Avoid duplicating the same text in multiple places**
When a translation is needed, try a search for the translation value first and see if it exists elsewhere
5. **If a key is used across multiple bundles, it must live in ChillMainBundle.**
6. **If a key is used across multiple bundles and is a generic term, it must be placed in the ``common`` namespace.**
Key Structure
=============
We use the following structure:
.. code-block:: text
<scope>.<feature>.<sub-feature>.<key-type>
Where:
* ``<>`` identifies the bundle or shared context
* ``<feature>`` identifies the part of the module using the translation
* ``<element>`` describes the text purpose
* ``<subelement>`` for a multi-level element ( eg. activity.export.person.count.description)
Examples of scopes
------------------
* ``activity`` — ChillActivityBundle
* ``person`` — ChillPersonBundle
* ``common`` — neutral shared translation values
Naming Scopes
=============
1. **Bundle-specific keys**
For most things inside a bundle:
.. code-block:: text
activity.<feature>.<element>
Example:
.. code-block:: text
activity.form.save
activity.list.title
activity.entity.type
activity.menu.activities
activity.controller.success_created
2. **Shared UI elements (buttons, labels, generic text)**
These belong in the ``common`` namespace in ChillMainBundle:
.. code-block:: text
common.save
common.delete
common.edit
common.filter
common.duration_time
Translation workflow
====================
Use the following workflow when deciding where a key belongs:
1. **Is this text used in more than one bundle?**
→ Place in ``main`` or ``common``
2. **Is this text generic UI (button, label, pagination, yes/no)?**
→ Place in ``common``
3. **Is this text specific to one bundle and one feature?**
→ Place in ``<bundle>.feature.<element>``
4. **Is this text related to an entity or value object?**
→ Place in ``<bundle>.entity.<entityname>.<field>``
5. **Is this text used in forms?**
``<bundle>.form.<field>`` or ``<bundle>.form.<action>``
6. **Is this text related to exports?**
``<bundle>.export.<feature>.<column>``
7. **Is it related to filtering, searching or parameters?**
``<bundle>.filter.<name>`` or
``<bundle>.filter.<feature>.<field>`` for nested filters
Examples based on translations within ChillActivityBundle
=========================================================
Below are concrete examples from ``ChillActivityBundle``,
refactored according to the guidelines.
General activity keys
---------------------
Instead of scattered keys like::
Show the activity
Edit the activity
Activity
Duration time
...
We use:
.. code-block:: text
activity.general.show
activity.general.edit
activity.general.title
activity.general.duration
activity.general.travel_time
activity.general.attendee
activity.general.remark
activity.general.no_comments
Forms
-----
Instead of keys like::
Activity creation
Save activity
Reset form
Choose a type
Use:
.. code-block:: text
activity.form.title_create
activity.form.save
activity.form.reset
activity.form.choose_type
activity.form.choose_duration
Long lists (like durations) should be grouped:
.. code-block:: text
activity.form.duration.5min
activity.form.duration.10min
activity.form.duration.15min
activity.form.duration.1h
activity.form.duration.1h30
activity.form.duration.2h
...
Entities
--------
Entity fields should follow:
.. code-block:: text
activity.entity.activity.date
activity.entity.activity.comment
activity.entity.activity.deleted
activity.entity.location.name
activity.entity.location.type
Controller messages
-------------------
Instead of strings as keys::
'Success : activity created!'
'The form is not valid. The activity has not been created !'
Use:
.. code-block:: text
activity.controller.success_created
activity.controller.error_invalid_create
activity.controller.success_updated
activity.controller.error_invalid_update
Roles
-----
Access control keys should be:
.. code-block:: text
activity.role.create
activity.role.update
activity.role.see
activity.role.see_details
activity.role.delete
activity.role.stats
activity.role.list
Admin
-----
.. code-block:: text
activity.admin.configuration
activity.admin.types
activity.admin.reasons
activity.admin.reason_category
activity.admin.presence
CRUD
----
.. code-block:: text
activity.crud.type.title_new
activity.crud.type.title_edit
activity.crud.presence.title_new
Activity Reason
---------------
.. code-block:: text
activity.reason.list
activity.reason.create
activity.reason.active
activity.reason.category
activity.reason.entity_title
Exports
-------
Group them logically:
.. code-block:: text
activity.export.person.count.title
activity.export.person.count.description
activity.export.person.count.header
activity.export.period.sum_duration.title
activity.export.period.sum_duration.description
activity.export.period.sum_duration.header
Filters
-------
Use hierarchical filters:
.. code-block:: text
activity.filter.by_reason
activity.filter.by_type
activity.filter.by_date
activity.filter.by_location
activity.filter.by_sent_received
activity.filter.by_user
Aggregators
-----------
.. code-block:: text
activity.aggregator.reason.by_category
activity.aggregator.reason.level
activity.aggregator.user.by_scope
activity.aggregator.user.by_job
Global/Shared Keys
==================
Keys like the following **must not be redeclared** in each bundle:
- First name
- Last name
- Username
- ID
- Type
- Duration
- Comment
- Date
- Location
- Present / Not present
- Add / Edit / Delete / Save / Update
These belong in ``common`` or ``main``:
.. code-block:: text
common.firstname
common.lastname
common.username
common.id
common.type
common.comment
common.date
common.location
common.present
common.absent
common.add
common.edit
common.delete
common.save
common.update
Naming directives summary
==========================
* **snake_case**
* **namespaced with dots**
* **bundle prefix for bundle-specific concepts**
* **common or main for shared concepts**
* **avoid free-floating keys (without namespace)**
* **reuse common keys wherever possible**
Migration Strategy (Optional)
=============================
To apply this structure progressively:
1. New keys must follow these guidelines.
2. Existing keys may remain as-is until refactored.
3. When refactoring:
- Move cross-bundle keys to ChillMainBundle and possible `common` namespace.
- Replace duplicated keys with shared ones.
===========================================
Avoiding duplicate translations
===========================================
1. Use Shared Namespaces
========================
Two namespaces must be used for shared translations:
* ``common.*`` — generic UI concepts (save, delete, date, name, etc.)
If a translation may be reused in multiple bundles, it must be placed
in the ``common`` namespace or in ChillMainBundle.
2. Bundle-Specific Keys
=======================
Keys belonging only to one bundle or one feature are namespaced inside that
bundle:
.. code-block:: text
activity.<feature>.<element>
person.<feature>.<element>
3. Search Before Creating
=========================
Before adding a new translation key, developers must:
1. For common translations like: "enregistrer/opslaan" look in the `common` namespace.
3. Search in Loco or translations for existing values.
If a suitable key exists, reuse it.
4. Only Create a New Key When Necessary
=======================================
Create a new key only when the text is:
* specific to the bundle
* specific to the feature
* not reusable elsewhere
6. Progressive Cleanup
======================
Old duplicates may remain temporarily. When updating code in an area, clean
duplicate values by moving them into ``common`` or ``main``.
General workflow
================
* **Reuse shared keys** within ``common`` namespace.
* **Search before creating** new keys.
* **Namespace bundle-specific keys** under their bundle.
* **Refactor progressively** when touching old code.

View File

@@ -1,148 +0,0 @@
=======================================================================
Managing translations within CHILL using Loco as a translation provider
=======================================================================
Within CHILL we make use of Symfony's translation component together with *Loco* as an external
translation provider. Using this setup centralise translations in a single online
location (Loco), while still allowing developers to create and update
translation keys locally in the project (YAML files).
Workflow
========
We use the following workflow:
* Developers create translation keys in YAML files inside each bundle.
* Keys are written in **English**.
* Application UI defaults to **French**, with **Dutch** as an additional locale (other languages can be added in the future).
* Loco acts as the central translation memory and synchronisation source.
* Loco Symfony package was installed so that built-in translation commands can be used to push/pull content
between Loco and the local project.
Translation directory structure
===============================
Each bundle contains its own ``translations`` directory, for example::
chill-bundles/
ChillCoreBundle/
translations/
messages.fr.yml
messages.nl.yml
ChillPersonBundle/
translations/
messages.fr.yml
messages.nl.yml
...
Configuration
=============
The translation configuration is defined in
``config/packages/translation.yaml``::
framework:
default_locale: '%env(resolve:LOCALE)%'
translator:
default_path: '%kernel.project_dir%/translations'
fallbacks:
- '%env(resolve:LOCALE)%'
- 'en'
providers:
loco:
dsn: '%env(LOCO_DSN)%'
domains: [ 'messages' ]
locales: [ 'fr', 'nl' ]
Note:
* ``en`` is the **source locale** in Loco.
* ``fr`` and ``nl`` are the **application locales**.
* ``domains: [messages]`` means only ``messages.*.yml`` files are pushed.
Environment variables
---------------------
In ``.env``::
LOCALE=fr
In ``.env.local``::
LOCO_DSN="loco://API_KEY@default"
Replace ``API_KEY`` with the key provided by Loco.
Working with Loco
=================
Loco shows all translation keys under three languages:
* **English (source)** — keys are listed but remain “untranslated”
* **French** — translated strings for French users
* **Dutch** — translated strings for Dutch users
Note: Don't add translations directly in the English column.
This column simply represents the *key*.
Pushing translations to Loco
============================
You can push local translations to Loco using:
.. code-block:: bash
symfony console translation:push loco --locales=fr --locales=nl --force
This will:
* Upload all French and Dutch translation values from ``*.fr.yml`` and
``*.nl.yml`` files
* Ensures Loco stays in sync with local YAML files
* Creates any missing keys in Loco
Pulling translations from Loco
==============================
When translators update strings in Loco, developers can fetch updates with:
.. code-block:: bash
symfony console translation:pull loco --locales=fr --locales=nl --force
This will:
* Download the latest French and Dutch translations
* Overwrite the local YAML files with Locos content
* Keep everything consistent across the team
Adding new translation keys (Developer workflow)
================================================
1. Add a new key directly in the appropriate YAML file, for example::
chill-bundles/ChillPersonBundle/translations/messages.fr.yml
Example key::
person.form.submit: "Envoyer"
2. Add Dutch translation as well if you can (otherwise leave empty to be translated within Loco later)::
person.form.submit: "Verzenden"
3. Run a push to send the new key to Loco:
.. code-block:: bash
symfony console translation:push loco --locales=fr --locales=nl --force
4. The key will now appear in Loco for translation management.
Note: English appears as “untranslated”, because it is merely the source language

View File

@@ -55,7 +55,6 @@
"@tsconfig/node20": "^20.1.4",
"@types/dompurify": "^3.0.5",
"@types/leaflet": "^1.9.3",
"@vueuse/core": "^13.9.0",
"bootstrap-icons": "^1.11.3",
"dropzone": "^5.7.6",
"es6-promise": "^4.2.8",
@@ -80,12 +79,12 @@
"dev": "encore dev",
"watch": "encore dev --watch",
"build": "encore production --progress",
"specs-build": "yaml-merge src/Bundle/ChillMainBundle/chill.api.specs.yaml src/Bundle/ChillPersonBundle/chill.api.specs.yaml src/Bundle/ChillCalendarBundle/chill.api.specs.yaml src/Bundle/ChillThirdPartyBundle/chill.api.specs.yaml src/Bundle/ChillDocStoreBundle/chill.api.specs.yaml> templates/api/specs.yaml",
"specs-build": "yaml-merge src/Bundle/ChillMainBundle/chill.api.specs.yaml src/Bundle/ChillPersonBundle/chill.api.specs.yaml src/Bundle/ChillCalendarBundle/chill.api.specs.yaml src/Bundle/ChillThirdPartyBundle/chill.api.specs.yaml src/Bundle/ChillDocStoreBundle/chill.api.specs.yaml src/Bundle/ChillTicketBundle/chill.api.specs.yaml> templates/api/specs.yaml",
"specs-validate": "swagger-cli validate templates/api/specs.yaml",
"specs-create-dir": "mkdir -p templates/api",
"specs": "yarn run specs-create-dir && yarn run specs-build && yarn run specs-validate",
"version": "node --version",
"eslint": "npx eslint-baseline --fix \"src/**/*.{js,ts,vue}\""
"eslint": "eslint-baseline --fix \"src/**/*.{js,ts,vue}\""
},
"private": true
}

View File

@@ -58,6 +58,10 @@
<!-- temporarily removed, the time to find a fix -->
<exclude>src/Bundle/ChillPersonBundle/Tests/Controller/PersonDuplicateControllerViewTest.php</exclude>
</testsuite>
<testsuite name="TicketBundle">
<directory suffix="Test.php">src/Bundle/ChillTicketBundle/tests/</directory>
</testsuite>
<!--
<testsuite name="ReportBundle">
<directory suffix="Test.php">src/Bundle/ChillReportBundle/Tests/</directory>

View File

@@ -0,0 +1,8 @@
In this directory, you find an example of file for the command `chill:main:ticket_motives_import`.
This file contains a list of ticket motives to import into the system. Each entry is a dictionary with two keys: `code` and `label`. The `code` key contains the unique code for the ticket motive, and the `label` key contains the human-readable label for the ticket motive.
The `stored_objects` key contains the documents that will be associated with the tickets. They must be found in the same directory.
The command `chill:main:ticket_motives_import` uses this file to import the specified ticket motives into the system.

View File

@@ -0,0 +1,136 @@
- label:
fr: Appel famille pour annonce de décès
urgent: false
supplementary_informations:
- label:
fr: Date du décès
- label:
fr: lieu du décès (domicile ou hôpital)
- label:
fr: nom de lhôpital
- label:
fr: service concerné
stored_objects:
- label:
fr: ☀️ De 07h à 21h
filename: 2_doc_20250402_Pelotons flux externes consolidés.pdf
- label:
fr: 🌙 De 21h à 07h du matin
filename: 3_doc_20250402_Pelotons flux externes consolidés.pdf
- label:
fr: 🗓️ Dimanches et jours fériés
filename: 4_doc_20250402_Pelotons flux externes consolidés.pdf
- label:
fr: 'Appel famille pour annonce absence : hospitalisation ou consultation'
urgent: false
supplementary_informations:
- label:
fr: Quel hôpital
- label:
fr: quel service
- label:
fr: pour quelles raisons
- label:
fr: 'consultation : date et heure'
- label:
fr: hospitalisation complète ou HDJ
stored_objects:
- label:
fr: ☀️ De 07h à 21h
filename: 5_doc_20250402_Pelotons flux externes consolidés.pdf
- label:
fr: 🌙 De 21h à 07h du matin
filename: 6_doc_20250402_Pelotons flux externes consolidés.pdf
- label:
fr: 🗓️ Dimanches et jours fériés
filename: 7_doc_20250402_Pelotons flux externes consolidés.pdf
- label:
fr: 'Appel famille pour annonce absence : interruption de prise en charge'
urgent: false
supplementary_informations:
- label:
fr: Pour quelles raisons ? Date
- label:
fr: durée
- label:
fr: accord médical ?
stored_objects:
- label:
fr: ☀️ De 07h à 21h
filename: 8_doc_20250402_Pelotons flux externes consolidés.pdf
- label:
fr: 🌙 De 21h à 07h du matin
filename: 9_doc_20250402_Pelotons flux externes consolidés.pdf
- label:
fr: 🗓️ Dimanches et jours fériés
filename: 10_doc_20250402_Pelotons flux externes consolidés.pdf
- label:
fr: 'Appel famille pour annonce absence : changement dadresse'
urgent: false
supplementary_informations:
- label:
fr:
- label:
fr: Pourquoi ? Pour combien de temps ? Besoin dun relais des soins ? Nouvelle adresse ?
stored_objects:
- label:
fr: ☀️ De 07h à 21h
filename: 11_doc_20250402_Pelotons flux externes consolidés.pdf
- label:
fr: 🌙 De 21h à 07h du matin
filename: 12_doc_20250402_Pelotons flux externes consolidés.pdf
- label:
fr: 🗓️ Dimanches et jours fériés
filename: 13_doc_20250402_Pelotons flux externes consolidés.pdf
- label:
fr: Appel famille pour altération de létat général du patient
urgent: true
supplementary_informations:
- label:
fr: Recherche des symptômes
- label:
fr: Attentes par rapport à la demande
stored_objects:
- label:
fr: ☀️ De 07h à 21h
filename: 14_doc_20250402_Pelotons flux externes consolidés.pdf
- label:
fr: 🌙 De 21h à 07h du matin
filename: 15_doc_20250402_Pelotons flux externes consolidés.pdf
- label:
fr: 🗓️ Dimanches et jours fériés
filename: 16_doc_20250402_Pelotons flux externes consolidés.pdf
- label:
fr: Appel famille pour prise en charge de la douleur
urgent: true
supplementary_informations:
- label:
fr: Localisation douleur
- label:
fr: Horaire dernier passage
- label:
fr: Traitements en cours
stored_objects:
- label:
fr: ☀️ De 07h à 21h
filename: 17_doc_20250402_Pelotons flux externes consolidés.pdf
- label:
fr: 🌙 De 21h à 07h du matin
filename: 18_doc_20250402_Pelotons flux externes consolidés.pdf
- label:
fr: 🗓️ Dimanches et jours fériés
filename: 19_doc_20250402_Pelotons flux externes consolidés.pdf
- label:
fr: Appel famille pour information sur la date de prise en charge
urgent: false
supplementary_informations: []
stored_objects:
- label:
fr: ☀️ De 07h à 21h
filename: 20_doc_20250402_Pelotons flux externes consolidés.pdf
- label:
fr: 🌙 De 21h à 07h du matin
filename: 21_doc_20250402_Pelotons flux externes consolidés.pdf
- label:
fr: 🗓️ Dimanches et jours fériés
filename: 22_doc_20250402_Pelotons flux externes consolidés.pdf

View File

@@ -0,0 +1,6 @@
In this directory, you find an example of file for the command `chill:main:override_translation`.
This file contains a list of translations to override in the translation catalogue. Each entry is a dictionary with two keys: `from` and `to`. The `from` key contains the original translation string, and the `to` key contains the replacement string.
The command `chill:main:override_translation` uses this file to generate a new translation catalogue with the specified overrides applied.

View File

@@ -0,0 +1,8 @@
- {from: "de l'usager", to: "du patient"}
- {from: "l'usager", to: "le patient"}
- {from: "L'usager", to: "Le patient"}
- {from: "d'usagers", to: "de patients"}
- {from: "usagers", to: "patients"}
- {from: "Usagers", to: "Patients"}
- {from: "usager", to: "patient"}
- {from: "Usager", to: "Patient"}

View File

@@ -382,7 +382,6 @@ final class ActivityController extends AbstractController
$entity = new Activity();
$entity->setUser($this->security->getUser());
$entity->addUser($this->security->getUser());
if ($person instanceof Person) {
$entity->setPerson($person);

View File

@@ -66,9 +66,6 @@ class ListActivityHelper
->leftJoin('activity.location', 'location')
->addSelect('location.name AS locationName')
->addSelect('activity.sentReceived')
->addSelect('activity.comment.comment AS commentText')
->addSelect('activity.comment.date AS commentDate')
->addSelect('JSON_BUILD_OBJECT(\'uid\', activity.comment.userId, \'d\', activity.comment.date) AS commentUser')
->addSelect('JSON_BUILD_OBJECT(\'uid\', IDENTITY(activity.createdBy), \'d\', activity.createdAt) AS createdBy')
->addSelect('activity.createdAt')
->addSelect('JSON_BUILD_OBJECT(\'uid\', IDENTITY(activity.updatedBy), \'d\', activity.updatedAt) AS updatedBy')
@@ -90,8 +87,6 @@ class ListActivityHelper
'createdAt', 'updatedAt' => $this->dateTimeHelper->getLabel($key),
'createdBy', 'updatedBy' => $this->userHelper->getLabel($key, $values, $key),
'date' => $this->dateTimeHelper->getLabel(self::MSG_KEY.$key),
'commentDate' => $this->dateTimeHelper->getLabel(self::MSG_KEY.'comment_date'),
'commentUser' => $this->userHelper->getLabel($key, $values, self::MSG_KEY.'comment_user'),
'attendeeName' => function ($value) {
if ('_header' === $value) {
return 'Attendee';
@@ -181,9 +176,6 @@ class ListActivityHelper
'usersNames',
'thirdPartiesIds',
'thirdPartiesNames',
'commentText',
'commentDate',
'commentUser',
'createdBy',
'createdAt',
'updatedBy',

View File

@@ -90,9 +90,7 @@ class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInt
public function getFormDefaultData(): array
{
return [
'reasons' => [],
];
return [];
}
public function describeAction($data, ExportGenerationContext $context): string|\Symfony\Contracts\Translation\TranslatableInterface|array

View File

@@ -42,8 +42,6 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
error_log('alterQuery called with data: '.json_encode(array_keys($data)));
// create a subquery for activity
$sqb = $qb->getEntityManager()->createQueryBuilder();
$sqb->select('1')
@@ -61,6 +59,7 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem
if (\in_array('activity', $qb->getAllAliases(), true)) {
$sqb->andWhere('activity_person_having_activity.id = activity.id');
}
if (isset($data['reasons']) && [] !== $data['reasons']) {
// add clause activity reason
$sqb->join('activity_person_having_activity.reasons', 'reasons_person_having_activity');
@@ -125,38 +124,12 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem
public function normalizeFormData(array $formData): array
{
$normalized = [
'date_from_rolling' => $formData['date_from_rolling']->normalize(),
'date_to_rolling' => $formData['date_to_rolling']->normalize(),
'reasons' => [],
];
if (isset($formData['reasons']) && [] !== $formData['reasons']) {
$normalized['reasons'] = array_map(
fn (ActivityReason $reason) => $reason->getId(),
$formData['reasons']
);
}
return $normalized;
return ['date_from_rolling' => $formData['date_from_rolling']->normalize(), 'date_to_rolling' => $formData['date_to_rolling']->normalize()];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
$denormalized = [
'date_from_rolling' => RollingDate::fromNormalized($formData['date_from_rolling']),
'date_to_rolling' => RollingDate::fromNormalized($formData['date_to_rolling']),
'reasons' => [],
];
if (isset($formData['reasons']) && [] !== $formData['reasons']) {
$denormalized['reasons'] = array_map(
fn ($id) => $this->activityReasonRepository->find($id),
$formData['reasons']
);
}
return $denormalized;
return ['date_from_rolling' => RollingDate::fromNormalized($formData['date_from_rolling']), 'date_to_rolling' => RollingDate::fromNormalized($formData['date_to_rolling'])];
}
public function getFormDefaultData(): array
@@ -170,12 +143,10 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem
public function describeAction($data, ExportGenerationContext $context): array
{
$reasons = $data['reasons'] ?? [];
return [
[] === $reasons ?
'export.filter.activity.describe_action_with_no_subject'
: 'export.filter.activity.describe_action_with_subject',
[] === $data['reasons'] ?
'export.filter.person_between_dates.describe_action_with_no_subject'
: 'export.filter.person_between_dates.describe_action_with_subject',
[
'date_from' => $this->rollingDateConverter->convert($data['date_from_rolling']),
'date_to' => $this->rollingDateConverter->convert($data['date_to_rolling']),
@@ -183,7 +154,7 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem
', ',
array_map(
fn (ActivityReason $r): string => '"'.$this->translatableStringHelper->localize($r->getName()).'"',
$reasons
$data['reasons']
)
),
],
@@ -197,7 +168,6 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem
public function validateForm($data, ExecutionContextInterface $context): void
{
error_log('validateForm called with data: '.json_encode(array_keys($data)));
if ($this->rollingDateConverter->convert($data['date_from_rolling'])
>= $this->rollingDateConverter->convert($data['date_to_rolling'])) {
$context->buildViolation('export.filter.activity.person_between_dates.date mismatch')

View File

@@ -88,8 +88,8 @@ class ActivityType extends AbstractType
if (null !== $options['data']->getPerson()) {
$builder->add('scope', ScopePickerType::class, [
'role' => ActivityVoter::CREATE === (string) $options['role'] ? ActivityVoter::CREATE_PERSON : (string) $options['role'],
'center' => $options['center'],
'role' => ActivityVoter::CREATE === (string) $options['role'] ? ActivityVoter::CREATE_PERSON : (string) $options['role'],
'required' => true,
]);
}

View File

@@ -1,7 +1,7 @@
<template>
<concerned-groups v-if="hasPerson" />
<social-issues-acc v-if="hasSocialIssues" />
<location v-if="hasLocation" />
<concerned-groups v-if="hasPerson" />
<social-issues-acc v-if="hasSocialIssues" />
<location v-if="hasLocation" />
</template>
<script>
@@ -10,12 +10,12 @@ import SocialIssuesAcc from "./components/SocialIssuesAcc.vue";
import Location from "./components/Location.vue";
export default {
name: "App",
props: ["hasSocialIssues", "hasLocation", "hasPerson"],
components: {
ConcernedGroups,
SocialIssuesAcc,
Location,
},
name: "App",
props: ["hasSocialIssues", "hasLocation", "hasPerson"],
components: {
ConcernedGroups,
SocialIssuesAcc,
Location,
},
};
</script>

View File

@@ -1,46 +1,43 @@
<template>
<teleport to="#add-persons" v-if="isComponentVisible">
<div class="flex-bloc concerned-groups" :class="getContext">
<persons-bloc
v-for="bloc in contextPersonsBlocs"
:key="bloc.key"
:bloc="bloc"
:bloc-width="getBlocWidth"
:set-persons-in-bloc="setPersonsInBloc"
/>
</div>
<div
v-if="
getContext === 'accompanyingCourse' &&
suggestedEntities.length > 0
"
<teleport to="#add-persons" v-if="isComponentVisible">
<div class="flex-bloc concerned-groups" :class="getContext">
<persons-bloc
v-for="bloc in contextPersonsBlocs"
:key="bloc.key"
:bloc="bloc"
:bloc-width="getBlocWidth"
:set-persons-in-bloc="setPersonsInBloc"
/>
</div>
<div
v-if="getContext === 'accompanyingCourse' && suggestedEntities.length > 0"
>
<ul class="list-suggest add-items inline">
<li
v-for="(p, i) in suggestedEntities"
@click="addSuggestedEntity(p)"
:key="`suggestedEntities-${i}`"
>
<ul class="list-suggest add-items inline">
<li
v-for="(p, i) in suggestedEntities"
@click="addSuggestedEntity(p)"
:key="`suggestedEntities-${i}`"
>
<person-text v-if="p.type === 'person'" :person="p" />
<span v-else>{{ p.text }}</span>
</li>
</ul>
</div>
<person-text v-if="p.type === 'person'" :person="p" />
<span v-else>{{ p.text }}</span>
</li>
</ul>
</div>
<ul class="record_actions">
<li class="add-persons">
<add-persons
:buttonTitle="trans(ACTIVITY_ADD_PERSONS)"
:modalTitle="trans(ACTIVITY_ADD_PERSONS)"
v-bind:key="addPersons.key"
v-bind:options="addPersonsOptions"
@addNewPersons="addNewPersons"
ref="addPersons"
>
</add-persons>
</li>
</ul>
</teleport>
<ul class="record_actions">
<li class="add-persons">
<add-persons
:buttonTitle="trans(ACTIVITY_ADD_PERSONS)"
:modalTitle="trans(ACTIVITY_ADD_PERSONS)"
v-bind:key="addPersons.key"
v-bind:options="addPersonsOptions"
@addNewPersons="addNewPersons"
ref="addPersons"
>
</add-persons>
</li>
</ul>
</teleport>
</template>
<script>
@@ -49,208 +46,208 @@ import AddPersons from "ChillPersonAssets/vuejs/_components/AddPersons.vue";
import PersonsBloc from "./ConcernedGroups/PersonsBloc.vue";
import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue";
import {
ACTIVITY_BLOC_PERSONS,
ACTIVITY_BLOC_PERSONS_ASSOCIATED,
ACTIVITY_BLOC_THIRDPARTY,
ACTIVITY_BLOC_USERS,
ACTIVITY_ADD_PERSONS,
trans,
ACTIVITY_BLOC_PERSONS,
ACTIVITY_BLOC_PERSONS_ASSOCIATED,
ACTIVITY_BLOC_THIRDPARTY,
ACTIVITY_BLOC_USERS,
ACTIVITY_ADD_PERSONS,
trans,
} from "translator";
export default {
name: "ConcernedGroups",
components: {
AddPersons,
PersonsBloc,
PersonText,
name: "ConcernedGroups",
components: {
AddPersons,
PersonsBloc,
PersonText,
},
setup() {
return {
trans,
ACTIVITY_ADD_PERSONS,
};
},
data() {
return {
personsBlocs: [
{
key: "persons",
title: trans(ACTIVITY_BLOC_PERSONS),
persons: [],
included: false,
},
{
key: "personsAssociated",
title: trans(ACTIVITY_BLOC_PERSONS_ASSOCIATED),
persons: [],
included: window.activity
? window.activity.activityType.personsVisible !== 0
: true,
},
{
key: "personsNotAssociated",
title: "activity.bloc_persons_not_associated",
persons: [],
included: window.activity
? window.activity.activityType.personsVisible !== 0
: true,
},
{
key: "thirdparty",
title: trans(ACTIVITY_BLOC_THIRDPARTY),
persons: [],
included: window.activity
? window.activity.activityType.thirdPartiesVisible !== 0
: true,
},
{
key: "users",
title: trans(ACTIVITY_BLOC_USERS),
persons: [],
included: window.activity
? window.activity.activityType.usersVisible !== 0
: true,
},
],
addPersons: {
key: "activity",
},
};
},
computed: {
isComponentVisible() {
return window.activity
? window.activity.activityType.personsVisible !== 0 ||
window.activity.activityType.thirdPartiesVisible !== 0 ||
window.activity.activityType.usersVisible !== 0
: true;
},
setup() {
return {
trans,
ACTIVITY_ADD_PERSONS,
};
...mapState({
persons: (state) => state.activity.persons,
thirdParties: (state) => state.activity.thirdParties,
users: (state) => state.activity.users,
accompanyingCourse: (state) => state.activity.accompanyingPeriod,
}),
...mapGetters(["suggestedEntities"]),
getContext() {
return this.accompanyingCourse ? "accompanyingCourse" : "person";
},
data() {
return {
personsBlocs: [
{
key: "persons",
title: trans(ACTIVITY_BLOC_PERSONS),
persons: [],
included: false,
},
{
key: "personsAssociated",
title: trans(ACTIVITY_BLOC_PERSONS_ASSOCIATED),
persons: [],
included: window.activity
? window.activity.activityType.personsVisible !== 0
: true,
},
{
key: "personsNotAssociated",
title: "activity.bloc_persons_not_associated",
persons: [],
included: window.activity
? window.activity.activityType.personsVisible !== 0
: true,
},
{
key: "thirdparty",
title: trans(ACTIVITY_BLOC_THIRDPARTY),
persons: [],
included: window.activity
? window.activity.activityType.thirdPartiesVisible !== 0
: true,
},
{
key: "users",
title: trans(ACTIVITY_BLOC_USERS),
persons: [],
included: window.activity
? window.activity.activityType.usersVisible !== 0
: true,
},
],
addPersons: {
key: "activity",
},
};
contextPersonsBlocs() {
return this.personsBlocs.filter((bloc) => bloc.included !== false);
},
computed: {
isComponentVisible() {
return window.activity
? window.activity.activityType.personsVisible !== 0 ||
window.activity.activityType.thirdPartiesVisible !== 0 ||
window.activity.activityType.usersVisible !== 0
: true;
},
...mapState({
persons: (state) => state.activity.persons,
thirdParties: (state) => state.activity.thirdParties,
users: (state) => state.activity.users,
accompanyingCourse: (state) => state.activity.accompanyingPeriod,
}),
...mapGetters(["suggestedEntities"]),
getContext() {
return this.accompanyingCourse ? "accompanyingCourse" : "person";
},
contextPersonsBlocs() {
return this.personsBlocs.filter((bloc) => bloc.included !== false);
},
addPersonsOptions() {
let optionsType = [];
if (window.activity) {
if (window.activity.activityType.personsVisible !== 0) {
optionsType.push("person");
}
if (window.activity.activityType.thirdPartiesVisible !== 0) {
optionsType.push("thirdparty");
}
if (window.activity.activityType.usersVisible !== 0) {
optionsType.push("user");
}
} else {
optionsType = ["person", "thirdparty", "user"];
}
return {
type: optionsType,
priority: null,
uniq: false,
button: {
size: "btn-sm",
},
};
},
getBlocWidth() {
return Math.round(100 / this.contextPersonsBlocs.length) + "%";
addPersonsOptions() {
let optionsType = [];
if (window.activity) {
if (window.activity.activityType.personsVisible !== 0) {
optionsType.push("person");
}
if (window.activity.activityType.thirdPartiesVisible !== 0) {
optionsType.push("thirdparty");
}
if (window.activity.activityType.usersVisible !== 0) {
optionsType.push("user");
}
} else {
optionsType = ["person", "thirdparty", "user"];
}
return {
type: optionsType,
priority: null,
uniq: false,
button: {
size: "btn-sm",
},
};
},
mounted() {
this.setPersonsInBloc();
getBlocWidth() {
return Math.round(100 / this.contextPersonsBlocs.length) + "%";
},
methods: {
setPersonsInBloc() {
let groups;
if (this.accompanyingCourse) {
groups = this.splitPersonsInGroups();
}
this.personsBlocs.forEach((bloc) => {
if (this.accompanyingCourse) {
switch (bloc.key) {
case "personsAssociated":
bloc.persons = groups.personsAssociated;
bloc.included = true;
break;
case "personsNotAssociated":
bloc.persons = groups.personsNotAssociated;
bloc.included = true;
break;
}
} else {
switch (bloc.key) {
case "persons":
bloc.persons = this.persons;
bloc.included = true;
break;
}
}
switch (bloc.key) {
case "thirdparty":
bloc.persons = this.thirdParties;
break;
case "users":
bloc.persons = this.users;
break;
}
}, groups);
},
splitPersonsInGroups() {
let personsAssociated = [];
let personsNotAssociated = this.persons;
let participations = this.getCourseParticipations();
this.persons.forEach((person) => {
participations.forEach((participation) => {
if (person.id === participation.id) {
//console.log(person.id);
personsAssociated.push(person);
personsNotAssociated = personsNotAssociated.filter(
(p) => p !== person,
);
}
});
});
return {
personsAssociated: personsAssociated,
personsNotAssociated: personsNotAssociated,
};
},
getCourseParticipations() {
let participations = [];
this.accompanyingCourse.participations.forEach((participation) => {
if (!participation.endDate) {
participations.push(participation.person);
}
});
return participations;
},
addNewPersons({ selected, modal }) {
console.log("@@@ CLICK button addNewPersons", selected);
selected.forEach((item) => {
this.$store.dispatch("addPersonsInvolved", item);
}, this);
this.$refs.addPersons.resetSearch(); // to cast child method
modal.showModal = false;
this.setPersonsInBloc();
},
addSuggestedEntity(person) {
this.$store.dispatch("addPersonsInvolved", {
result: person,
type: "person",
});
this.setPersonsInBloc();
},
},
mounted() {
this.setPersonsInBloc();
},
methods: {
setPersonsInBloc() {
let groups;
if (this.accompanyingCourse) {
groups = this.splitPersonsInGroups();
}
this.personsBlocs.forEach((bloc) => {
if (this.accompanyingCourse) {
switch (bloc.key) {
case "personsAssociated":
bloc.persons = groups.personsAssociated;
bloc.included = true;
break;
case "personsNotAssociated":
bloc.persons = groups.personsNotAssociated;
bloc.included = true;
break;
}
} else {
switch (bloc.key) {
case "persons":
bloc.persons = this.persons;
bloc.included = true;
break;
}
}
switch (bloc.key) {
case "thirdparty":
bloc.persons = this.thirdParties;
break;
case "users":
bloc.persons = this.users;
break;
}
}, groups);
},
splitPersonsInGroups() {
let personsAssociated = [];
let personsNotAssociated = this.persons;
let participations = this.getCourseParticipations();
this.persons.forEach((person) => {
participations.forEach((participation) => {
if (person.id === participation.id) {
//console.log(person.id);
personsAssociated.push(person);
personsNotAssociated = personsNotAssociated.filter(
(p) => p !== person,
);
}
});
});
return {
personsAssociated: personsAssociated,
personsNotAssociated: personsNotAssociated,
};
},
getCourseParticipations() {
let participations = [];
this.accompanyingCourse.participations.forEach((participation) => {
if (!participation.endDate) {
participations.push(participation.person);
}
});
return participations;
},
addNewPersons({ selected, modal }) {
console.log("@@@ CLICK button addNewPersons", selected);
selected.forEach((item) => {
this.$store.dispatch("addPersonsInvolved", item);
}, this);
this.$refs.addPersons.resetSearch(); // to cast child method
modal.showModal = false;
this.setPersonsInBloc();
},
addSuggestedEntity(person) {
this.$store.dispatch("addPersonsInvolved", {
result: person,
type: "person",
});
this.setPersonsInBloc();
},
},
};
</script>

View File

@@ -1,29 +1,29 @@
<template>
<li>
<span :title="person.text" @click.prevent="$emit('remove', person)">
<span class="chill_denomination">
<person-text :person="person" :is-cut="true" />
</span>
</span>
</li>
<li>
<span :title="person.text" @click.prevent="$emit('remove', person)">
<span class="chill_denomination">
<person-text :person="person" :is-cut="true" />
</span>
</span>
</li>
</template>
<script>
import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue";
export default {
name: "PersonBadge",
props: ["person"],
components: {
PersonText,
},
// computed: {
// textCutted() {
// let more = (this.person.text.length > 15) ?'…' : '';
// return this.person.text.slice(0,15) + more;
// }
// },
emits: ["remove"],
name: "PersonBadge",
props: ["person"],
components: {
PersonText,
},
// computed: {
// textCutted() {
// let more = (this.person.text.length > 15) ?'…' : '';
// return this.person.text.slice(0,15) + more;
// }
// },
emits: ["remove"],
};
</script>

View File

@@ -1,38 +1,38 @@
<template>
<div class="item-bloc" :style="{ 'flex-basis': blocWidth }">
<div class="item-row">
<div class="item-col">
<h4>{{ $t(bloc.title) }}</h4>
</div>
<div class="item-col">
<ul class="list-suggest remove-items">
<person-badge
v-for="person in bloc.persons"
:key="person.id"
:person="person"
@remove="removePerson"
/>
</ul>
</div>
</div>
<div class="item-bloc" :style="{ 'flex-basis': blocWidth }">
<div class="item-row">
<div class="item-col">
<h4>{{ $t(bloc.title) }}</h4>
</div>
<div class="item-col">
<ul class="list-suggest remove-items">
<person-badge
v-for="person in bloc.persons"
:key="person.id"
:person="person"
@remove="removePerson"
/>
</ul>
</div>
</div>
</div>
</template>
<script>
import PersonBadge from "./PersonBadge.vue";
export default {
name: "PersonsBloc",
components: {
PersonBadge,
},
props: ["bloc", "setPersonsInBloc", "blocWidth"],
methods: {
removePerson(item) {
console.log("@@ CLICK remove person: item", item);
this.$store.dispatch("removePersonInvolved", item);
this.setPersonsInBloc();
},
name: "PersonsBloc",
components: {
PersonBadge,
},
props: ["bloc", "setPersonsInBloc", "blocWidth"],
methods: {
removePerson(item) {
console.log("@@ CLICK remove person: item", item);
this.$store.dispatch("removePersonInvolved", item);
this.setPersonsInBloc();
},
},
};
</script>

View File

@@ -1,32 +1,32 @@
<template>
<teleport to="#location">
<div class="mb-3 row">
<label :class="locationClassList">
{{ trans(ACTIVITY_LOCATION) }}
</label>
<div class="col-sm-8">
<VueMultiselect
name="selectLocation"
id="selectLocation"
label="name"
track-by="id"
open-direction="top"
:multiple="false"
:searchable="true"
:placeholder="trans(ACTIVITY_CHOOSE_LOCATION)"
:custom-label="customLabel"
:select-label="trans(MULTISELECT_SELECT_LABEL)"
:deselect-label="trans(MULTISELECT_DESELECT_LABEL)"
:selected-label="trans(MULTISELECT_SELECTED_LABEL)"
:options="availableLocations"
group-values="locations"
group-label="locationGroup"
v-model="location"
/>
<new-location v-bind:available-locations="availableLocations" />
</div>
</div>
</teleport>
<teleport to="#location">
<div class="mb-3 row">
<label :class="locationClassList">
{{ trans(ACTIVITY_LOCATION) }}
</label>
<div class="col-sm-8">
<VueMultiselect
name="selectLocation"
id="selectLocation"
label="name"
track-by="id"
open-direction="top"
:multiple="false"
:searchable="true"
:placeholder="trans(ACTIVITY_CHOOSE_LOCATION)"
:custom-label="customLabel"
:select-label="trans(MULTISELECT_SELECT_LABEL)"
:deselect-label="trans(MULTISELECT_DESELECT_LABEL)"
:selected-label="trans(MULTISELECT_SELECTED_LABEL)"
:options="availableLocations"
group-values="locations"
group-label="locationGroup"
v-model="location"
/>
<new-location v-bind:available-locations="availableLocations" />
</div>
</div>
</teleport>
</template>
<script>
@@ -35,60 +35,60 @@ import VueMultiselect from "vue-multiselect";
import NewLocation from "./Location/NewLocation.vue";
import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
import {
trans,
ACTIVITY_LOCATION,
ACTIVITY_CHOOSE_LOCATION,
MULTISELECT_SELECT_LABEL,
MULTISELECT_DESELECT_LABEL,
MULTISELECT_SELECTED_LABEL,
trans,
ACTIVITY_LOCATION,
ACTIVITY_CHOOSE_LOCATION,
MULTISELECT_SELECT_LABEL,
MULTISELECT_DESELECT_LABEL,
MULTISELECT_SELECTED_LABEL,
} from "translator";
export default {
name: "Location",
components: {
NewLocation,
VueMultiselect,
name: "Location",
components: {
NewLocation,
VueMultiselect,
},
setup() {
return {
trans,
ACTIVITY_LOCATION,
ACTIVITY_CHOOSE_LOCATION,
MULTISELECT_SELECT_LABEL,
MULTISELECT_DESELECT_LABEL,
MULTISELECT_SELECTED_LABEL,
};
},
data() {
return {
locationClassList: `col-form-label col-sm-4 ${document.querySelector("input#chill_activitybundle_activity_location").getAttribute("required") ? "required" : ""}`,
};
},
computed: {
...mapState(["activity", "availableLocations"]),
...mapGetters(["suggestedEntities"]),
location: {
get() {
return this.activity.location;
},
set(value) {
this.$store.dispatch("updateLocation", value);
},
},
setup() {
return {
trans,
ACTIVITY_LOCATION,
ACTIVITY_CHOOSE_LOCATION,
MULTISELECT_SELECT_LABEL,
MULTISELECT_DESELECT_LABEL,
MULTISELECT_SELECTED_LABEL,
};
},
methods: {
labelAccompanyingCourseLocation(value) {
return `${value.address.text} (${localizeString(value.locationType.title)})`;
},
data() {
return {
locationClassList: `col-form-label col-sm-4 ${document.querySelector("input#chill_activitybundle_activity_location").getAttribute("required") ? "required" : ""}`,
};
},
computed: {
...mapState(["activity", "availableLocations"]),
...mapGetters(["suggestedEntities"]),
location: {
get() {
return this.activity.location;
},
set(value) {
this.$store.dispatch("updateLocation", value);
},
},
},
methods: {
labelAccompanyingCourseLocation(value) {
return `${value.address.text} (${localizeString(value.locationType.title)})`;
},
customLabel(value) {
return value.locationType
? value.name
? value.name === "__AccompanyingCourseLocation__"
? this.labelAccompanyingCourseLocation(value)
: `${value.name} (${localizeString(value.locationType.title)})`
: localizeString(value.locationType.title)
: "";
},
customLabel(value) {
return value.locationType
? value.name
? value.name === "__AccompanyingCourseLocation__"
? this.labelAccompanyingCourseLocation(value)
: `${value.name} (${localizeString(value.locationType.title)})`
: localizeString(value.locationType.title)
: "";
},
},
};
</script>

View File

@@ -1,123 +1,114 @@
<template>
<div>
<ul class="record_actions">
<li>
<a class="btn btn-sm btn-create" @click="openModal">
{{ trans(ACTIVITY_CREATE_NEW_LOCATION) }}
</a>
</li>
</ul>
<div>
<ul class="record_actions">
<li>
<a class="btn btn-sm btn-create" @click="openModal">
{{ trans(ACTIVITY_CREATE_NEW_LOCATION) }}
</a>
</li>
</ul>
<teleport to="body">
<modal
v-if="modal.showModal"
:modalDialogClass="modal.modalDialogClass"
@close="modal.showModal = false"
>
<template #header>
<h3 class="modal-title">
{{ trans(ACTIVITY_CREATE_NEW_LOCATION) }}
</h3>
</template>
<template #body>
<form>
<div class="alert alert-warning" v-if="errors.length">
<ul>
<li v-for="(e, i) in errors" :key="i">
{{ e }}
</li>
</ul>
</div>
<teleport to="body">
<modal
v-if="modal.showModal"
:modalDialogClass="modal.modalDialogClass"
@close="modal.showModal = false"
>
<template #header>
<h3 class="modal-title">
{{ trans(ACTIVITY_CREATE_NEW_LOCATION) }}
</h3>
</template>
<template #body>
<form>
<div class="alert alert-warning" v-if="errors.length">
<ul>
<li v-for="(e, i) in errors" :key="i">
{{ e }}
</li>
</ul>
</div>
<div class="form-floating mb-3">
<select
class="form-select form-select-lg"
id="type"
required
v-model="selectType"
>
<option selected disabled value="">
{{ trans(ACTIVITY_CHOOSE_LOCATION_TYPE) }}
</option>
<option
v-for="t in locationTypes"
:value="t"
:key="t.id"
>
{{ localizeString(t.title) }}
</option>
</select>
<label>{{
trans(ACTIVITY_LOCATION_FIELDS_TYPE)
}}</label>
</div>
<div class="form-floating mb-3">
<select
class="form-select form-select-lg"
id="type"
required
v-model="selectType"
>
<option selected disabled value="">
{{ trans(ACTIVITY_CHOOSE_LOCATION_TYPE) }}
</option>
<option v-for="t in locationTypes" :value="t" :key="t.id">
{{ localizeString(t.title) }}
</option>
</select>
<label>{{ trans(ACTIVITY_LOCATION_FIELDS_TYPE) }}</label>
</div>
<div class="form-floating mb-3">
<input
class="form-control form-control-lg"
id="name"
v-model="inputName"
placeholder
/>
<label for="name">{{
trans(ACTIVITY_LOCATION_FIELDS_NAME)
}}</label>
</div>
<div class="form-floating mb-3">
<input
class="form-control form-control-lg"
id="name"
v-model="inputName"
placeholder
/>
<label for="name">{{
trans(ACTIVITY_LOCATION_FIELDS_NAME)
}}</label>
</div>
<add-address
:context="addAddress.context"
:options="addAddress.options"
:addressChangedCallback="submitNewAddress"
v-if="showAddAddress"
ref="addAddress"
/>
<add-address
:context="addAddress.context"
:options="addAddress.options"
:addressChangedCallback="submitNewAddress"
v-if="showAddAddress"
ref="addAddress"
/>
<div class="form-floating mb-3" v-if="showContactData">
<input
class="form-control form-control-lg"
id="phonenumber1"
v-model="inputPhonenumber1"
placeholder
/>
<label for="phonenumber1">{{
trans(ACTIVITY_LOCATION_FIELDS_PHONENUMBER1)
}}</label>
</div>
<div class="form-floating mb-3" v-if="hasPhonenumber1">
<input
class="form-control form-control-lg"
id="phonenumber2"
v-model="inputPhonenumber2"
placeholder
/>
<label for="phonenumber2">{{
trans(ACTIVITY_LOCATION_FIELDS_PHONENUMBER2)
}}</label>
</div>
<div class="form-floating mb-3" v-if="showContactData">
<input
class="form-control form-control-lg"
id="email"
v-model="inputEmail"
placeholder
/>
<label for="email">{{
trans(ACTIVITY_LOCATION_FIELDS_EMAIL)
}}</label>
</div>
</form>
</template>
<template #footer>
<button
class="btn btn-save"
@click.prevent="saveNewLocation"
>
{{ trans(SAVE) }}
</button>
</template>
</modal>
</teleport>
</div>
<div class="form-floating mb-3" v-if="showContactData">
<input
class="form-control form-control-lg"
id="phonenumber1"
v-model="inputPhonenumber1"
placeholder
/>
<label for="phonenumber1">{{
trans(ACTIVITY_LOCATION_FIELDS_PHONENUMBER1)
}}</label>
</div>
<div class="form-floating mb-3" v-if="hasPhonenumber1">
<input
class="form-control form-control-lg"
id="phonenumber2"
v-model="inputPhonenumber2"
placeholder
/>
<label for="phonenumber2">{{
trans(ACTIVITY_LOCATION_FIELDS_PHONENUMBER2)
}}</label>
</div>
<div class="form-floating mb-3" v-if="showContactData">
<input
class="form-control form-control-lg"
id="email"
v-model="inputEmail"
placeholder
/>
<label for="email">{{
trans(ACTIVITY_LOCATION_FIELDS_EMAIL)
}}</label>
</div>
</form>
</template>
<template #footer>
<button class="btn btn-save" @click.prevent="saveNewLocation">
{{ trans(SAVE) }}
</button>
</template>
</modal>
</teleport>
</div>
</template>
<script>
@@ -128,237 +119,236 @@ import { getLocationTypes } from "../../api";
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
import {
SAVE,
ACTIVITY_LOCATION_FIELDS_EMAIL,
ACTIVITY_LOCATION_FIELDS_PHONENUMBER1,
ACTIVITY_LOCATION_FIELDS_PHONENUMBER2,
ACTIVITY_LOCATION_FIELDS_NAME,
ACTIVITY_LOCATION_FIELDS_TYPE,
ACTIVITY_CHOOSE_LOCATION_TYPE,
ACTIVITY_CREATE_NEW_LOCATION,
trans,
SAVE,
ACTIVITY_LOCATION_FIELDS_EMAIL,
ACTIVITY_LOCATION_FIELDS_PHONENUMBER1,
ACTIVITY_LOCATION_FIELDS_PHONENUMBER2,
ACTIVITY_LOCATION_FIELDS_NAME,
ACTIVITY_LOCATION_FIELDS_TYPE,
ACTIVITY_CHOOSE_LOCATION_TYPE,
ACTIVITY_CREATE_NEW_LOCATION,
trans,
} from "translator";
export default {
name: "NewLocation",
components: {
Modal,
AddAddress,
name: "NewLocation",
components: {
Modal,
AddAddress,
},
setup() {
return {
trans,
SAVE,
ACTIVITY_LOCATION_FIELDS_EMAIL,
ACTIVITY_LOCATION_FIELDS_PHONENUMBER1,
ACTIVITY_LOCATION_FIELDS_PHONENUMBER2,
ACTIVITY_LOCATION_FIELDS_NAME,
ACTIVITY_LOCATION_FIELDS_TYPE,
ACTIVITY_CHOOSE_LOCATION_TYPE,
ACTIVITY_CREATE_NEW_LOCATION,
};
},
props: ["availableLocations"],
data() {
return {
errors: [],
selected: {
type: null,
name: null,
addressId: null,
phonenumber1: null,
phonenumber2: null,
email: null,
},
locationTypes: [],
modal: {
showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-xl",
},
addAddress: {
options: {
button: {
text: {
create: "activity.create_address",
edit: "activity.edit_address",
},
size: "btn-sm",
},
title: {
create: "activity.create_address",
edit: "activity.edit_address",
},
},
context: {
target: {
//name, id
},
edit: false,
addressId: null,
defaults: window.addaddress,
},
},
};
},
computed: {
...mapState(["activity"]),
selectType: {
get() {
return this.selected.type;
},
set(value) {
this.selected.type = value;
},
},
setup() {
return {
trans,
SAVE,
ACTIVITY_LOCATION_FIELDS_EMAIL,
ACTIVITY_LOCATION_FIELDS_PHONENUMBER1,
ACTIVITY_LOCATION_FIELDS_PHONENUMBER2,
ACTIVITY_LOCATION_FIELDS_NAME,
ACTIVITY_LOCATION_FIELDS_TYPE,
ACTIVITY_CHOOSE_LOCATION_TYPE,
ACTIVITY_CREATE_NEW_LOCATION,
inputName: {
get() {
return this.selected.name;
},
set(value) {
this.selected.name = value;
},
},
inputEmail: {
get() {
return this.selected.email;
},
set(value) {
this.selected.email = value;
},
},
inputPhonenumber1: {
get() {
return this.selected.phonenumber1;
},
set(value) {
this.selected.phonenumber1 = value;
},
},
inputPhonenumber2: {
get() {
return this.selected.phonenumber2;
},
set(value) {
this.selected.phonenumber2 = value;
},
},
hasPhonenumber1() {
return (
this.selected.phonenumber1 !== null && this.selected.phonenumber1 !== ""
);
},
showAddAddress() {
let cond = false;
if (this.selected.type) {
if (this.selected.type.addressRequired !== "never") {
cond = true;
}
}
return cond;
},
showContactData() {
let cond = false;
if (this.selected.type) {
if (this.selected.type.contactData !== "never") {
cond = true;
}
}
return cond;
},
},
mounted() {
this.getLocationTypesList();
},
methods: {
localizeString,
checkForm() {
let cond = true;
this.errors = [];
if (!this.selected.type) {
this.errors.push("Type de localisation requis");
cond = false;
} else {
if (
this.selected.type.addressRequired === "required" &&
!this.selected.addressId
) {
this.errors.push("Adresse requise");
cond = false;
}
if (
this.selected.type.contactData === "required" &&
!this.selected.phonenumber1
) {
this.errors.push("Numéro de téléphone requis");
cond = false;
}
if (
this.selected.type.contactData === "required" &&
!this.selected.email
) {
this.errors.push("Adresse email requise");
cond = false;
}
}
return cond;
},
getLocationTypesList() {
getLocationTypes().then((results) => {
this.locationTypes = results.filter(
(t) => t.availableForUsers === true,
);
});
},
openModal() {
this.modal.showModal = true;
},
saveNewLocation() {
if (this.checkForm()) {
let body = {
type: "location",
name: this.selected.name,
locationType: {
id: this.selected.type.id,
type: "location-type",
},
phonenumber1: this.selected.phonenumber1,
phonenumber2: this.selected.phonenumber2,
email: this.selected.email,
};
},
props: ["availableLocations"],
data() {
return {
errors: [],
selected: {
type: null,
name: null,
addressId: null,
phonenumber1: null,
phonenumber2: null,
email: null,
if (this.selected.addressId) {
body = Object.assign(body, {
address: {
id: this.selected.addressId,
},
locationTypes: [],
modal: {
showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-xl",
},
addAddress: {
options: {
button: {
text: {
create: "activity.create_address",
edit: "activity.edit_address",
},
size: "btn-sm",
},
title: {
create: "activity.create_address",
edit: "activity.edit_address",
},
},
context: {
target: {
//name, id
},
edit: false,
addressId: null,
defaults: window.addaddress,
},
},
};
},
computed: {
...mapState(["activity"]),
selectType: {
get() {
return this.selected.type;
},
set(value) {
this.selected.type = value;
},
},
inputName: {
get() {
return this.selected.name;
},
set(value) {
this.selected.name = value;
},
},
inputEmail: {
get() {
return this.selected.email;
},
set(value) {
this.selected.email = value;
},
},
inputPhonenumber1: {
get() {
return this.selected.phonenumber1;
},
set(value) {
this.selected.phonenumber1 = value;
},
},
inputPhonenumber2: {
get() {
return this.selected.phonenumber2;
},
set(value) {
this.selected.phonenumber2 = value;
},
},
hasPhonenumber1() {
return (
this.selected.phonenumber1 !== null &&
this.selected.phonenumber1 !== ""
);
},
showAddAddress() {
let cond = false;
if (this.selected.type) {
if (this.selected.type.addressRequired !== "never") {
cond = true;
}
}
return cond;
},
showContactData() {
let cond = false;
if (this.selected.type) {
if (this.selected.type.contactData !== "never") {
cond = true;
}
}
return cond;
},
},
mounted() {
this.getLocationTypesList();
},
methods: {
localizeString,
checkForm() {
let cond = true;
this.errors = [];
if (!this.selected.type) {
this.errors.push("Type de localisation requis");
cond = false;
} else {
if (
this.selected.type.addressRequired === "required" &&
!this.selected.addressId
) {
this.errors.push("Adresse requise");
cond = false;
}
if (
this.selected.type.contactData === "required" &&
!this.selected.phonenumber1
) {
this.errors.push("Numéro de téléphone requis");
cond = false;
}
if (
this.selected.type.contactData === "required" &&
!this.selected.email
) {
this.errors.push("Adresse email requise");
cond = false;
}
}
return cond;
},
getLocationTypesList() {
getLocationTypes().then((results) => {
this.locationTypes = results.filter(
(t) => t.availableForUsers === true,
);
});
},
openModal() {
this.modal.showModal = true;
},
saveNewLocation() {
if (this.checkForm()) {
let body = {
type: "location",
name: this.selected.name,
locationType: {
id: this.selected.type.id,
type: "location-type",
},
phonenumber1: this.selected.phonenumber1,
phonenumber2: this.selected.phonenumber2,
email: this.selected.email,
};
if (this.selected.addressId) {
body = Object.assign(body, {
address: {
id: this.selected.addressId,
},
});
}
});
}
makeFetch("POST", "/api/1.0/main/location.json", body)
.then((response) => {
this.$store.dispatch("addAvailableLocationGroup", {
locationGroup: "Localisations nouvellement créées",
locations: [response],
});
this.$store.dispatch("updateLocation", response);
this.modal.showModal = false;
})
.catch((error) => {
if (error.name === "ValidationException") {
for (let v of error.violations) {
this.errors.push(v);
}
} else {
this.errors.push("An error occurred");
}
});
makeFetch("POST", "/api/1.0/main/location.json", body)
.then((response) => {
this.$store.dispatch("addAvailableLocationGroup", {
locationGroup: "Localisations nouvellement créées",
locations: [response],
});
this.$store.dispatch("updateLocation", response);
this.modal.showModal = false;
})
.catch((error) => {
if (error.name === "ValidationException") {
for (let v of error.violations) {
this.errors.push(v);
}
} else {
this.errors.push("An error occurred");
}
},
submitNewAddress(payload) {
this.selected.addressId = payload.addressId;
this.addAddress.context.addressId = payload.addressId;
this.addAddress.context.edit = true;
},
});
}
},
submitNewAddress(payload) {
this.selected.addressId = payload.addressId;
this.addAddress.context.addressId = payload.addressId;
this.addAddress.context.edit = true;
},
},
};
</script>

View File

@@ -1,103 +1,98 @@
<template>
<teleport to="#social-issues-acc">
<div class="mb-3 row">
<div class="col-4">
<label :class="socialIssuesClassList">{{
trans(ACTIVITY_SOCIAL_ISSUES)
}}</label>
</div>
<div class="col-8">
<check-social-issue
v-for="issue in socialIssuesList"
:key="issue.id"
:issue="issue"
:selection="socialIssuesSelected"
@updateSelected="updateIssuesSelected"
>
</check-social-issue>
<teleport to="#social-issues-acc">
<div class="mb-3 row">
<div class="col-4">
<label :class="socialIssuesClassList">{{
trans(ACTIVITY_SOCIAL_ISSUES)
}}</label>
</div>
<div class="col-8">
<check-social-issue
v-for="issue in socialIssuesList"
:key="issue.id"
:issue="issue"
:selection="socialIssuesSelected"
@updateSelected="updateIssuesSelected"
>
</check-social-issue>
<div class="my-3">
<VueMultiselect
name="otherIssues"
label="text"
track-by="id"
open-direction="bottom"
:close-on-select="true"
:preserve-search="false"
:reset-after="true"
:hide-selected="true"
:taggable="false"
:multiple="false"
:searchable="true"
:allow-empty="true"
:show-labels="false"
:loading="issueIsLoading"
:placeholder="trans(ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE)"
:options="socialIssuesOther"
@select="addIssueInList"
>
</VueMultiselect>
</div>
</div>
<div class="my-3">
<VueMultiselect
name="otherIssues"
label="text"
track-by="id"
open-direction="bottom"
:close-on-select="true"
:preserve-search="false"
:reset-after="true"
:hide-selected="true"
:taggable="false"
:multiple="false"
:searchable="true"
:allow-empty="true"
:show-labels="false"
:loading="issueIsLoading"
:placeholder="trans(ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE)"
:options="socialIssuesOther"
@select="addIssueInList"
>
</VueMultiselect>
</div>
</div>
</div>
<div class="mb-3 row">
<div class="col-4">
<label :class="socialActionsClassList">{{
trans(ACTIVITY_SOCIAL_ACTIONS)
}}</label>
</div>
<div class="col-8">
<div v-if="actionIsLoading === true">
<i class="chill-green fa fa-circle-o-notch fa-spin fa-lg"></i>
</div>
<div class="mb-3 row">
<div class="col-4">
<label :class="socialActionsClassList">{{
trans(ACTIVITY_SOCIAL_ACTIONS)
}}</label>
</div>
<div class="col-8">
<div v-if="actionIsLoading === true">
<i
class="chill-green fa fa-circle-o-notch fa-spin fa-lg"
></i>
</div>
<span
v-else-if="socialIssuesSelected.length === 0"
class="inline-choice chill-no-data-statement mt-3"
>
{{ trans(ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE) }}
</span>
<span
v-else-if="socialIssuesSelected.length === 0"
class="inline-choice chill-no-data-statement mt-3"
>
{{ trans(ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE) }}
</span>
<template
v-else-if="
socialActionsList.length > 0 &&
(socialIssuesSelected.length || socialActionsSelected.length)
"
>
<div
id="actionsList"
v-for="group in socialActionsList"
:key="group.issue"
>
<span class="badge bg-chill-l-gray text-dark">{{
group.issue
}}</span>
<check-social-action
v-for="action in group.actions"
:key="action.id"
:action="action"
:selection="socialActionsSelected"
@updateSelected="updateActionsSelected"
>
</check-social-action>
</div>
</template>
<template
v-else-if="
socialActionsList.length > 0 &&
(socialIssuesSelected.length ||
socialActionsSelected.length)
"
>
<div
id="actionsList"
v-for="group in socialActionsList"
:key="group.issue"
>
<span class="badge bg-chill-l-gray text-dark">{{
group.issue
}}</span>
<check-social-action
v-for="action in group.actions"
:key="action.id"
:action="action"
:selection="socialActionsSelected"
@updateSelected="updateActionsSelected"
>
</check-social-action>
</div>
</template>
<span
v-else-if="
actionAreLoaded && socialActionsList.length === 0
"
class="inline-choice chill-no-data-statement mt-3"
>
{{ trans(ACTIVITY_SOCIAL_ACTION_LIST_EMPTY) }}
</span>
</div>
</div>
</teleport>
<span
v-else-if="actionAreLoaded && socialActionsList.length === 0"
class="inline-choice chill-no-data-statement mt-3"
>
{{ trans(ACTIVITY_SOCIAL_ACTION_LIST_EMPTY) }}
</span>
</div>
</div>
</teleport>
</template>
<script>
@@ -106,175 +101,153 @@ import CheckSocialIssue from "./SocialIssuesAcc/CheckSocialIssue.vue";
import CheckSocialAction from "./SocialIssuesAcc/CheckSocialAction.vue";
import { getSocialIssues, getSocialActionByIssue } from "../api.js";
import {
ACTIVITY_SOCIAL_ACTION_LIST_EMPTY,
ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE,
ACTIVITY_SOCIAL_ACTIONS,
ACTIVITY_SOCIAL_ISSUES,
ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE,
trans,
ACTIVITY_SOCIAL_ACTION_LIST_EMPTY,
ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE,
ACTIVITY_SOCIAL_ACTIONS,
ACTIVITY_SOCIAL_ISSUES,
ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE,
trans,
} from "translator";
export default {
name: "SocialIssuesAcc",
components: {
CheckSocialIssue,
CheckSocialAction,
VueMultiselect,
name: "SocialIssuesAcc",
components: {
CheckSocialIssue,
CheckSocialAction,
VueMultiselect,
},
setup() {
return {
trans,
ACTIVITY_SOCIAL_ACTION_LIST_EMPTY,
ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE,
ACTIVITY_SOCIAL_ACTIONS,
ACTIVITY_SOCIAL_ISSUES,
ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE,
};
},
data() {
return {
issueIsLoading: false,
actionIsLoading: false,
actionAreLoaded: false,
socialIssuesClassList: `col-form-label ${document.querySelector("input#chill_activitybundle_activity_socialIssues").getAttribute("required") ? "required" : ""}`,
socialActionsClassList: `col-form-label ${document.querySelector("input#chill_activitybundle_activity_socialActions").getAttribute("required") ? "required" : ""}`,
};
},
computed: {
socialIssuesList() {
return this.$store.state.activity.accompanyingPeriod.socialIssues;
},
setup() {
return {
trans,
ACTIVITY_SOCIAL_ACTION_LIST_EMPTY,
ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE,
ACTIVITY_SOCIAL_ACTIONS,
ACTIVITY_SOCIAL_ISSUES,
ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE,
};
socialIssuesSelected() {
return this.$store.state.activity.socialIssues;
},
data() {
return {
issueIsLoading: false,
actionIsLoading: false,
actionAreLoaded: false,
socialIssuesClassList: {
"col-form-label": true,
required: false,
},
socialActionsClassList: {
"col-form-label": true,
required: false,
},
};
socialIssuesOther() {
return this.$store.state.socialIssuesOther;
},
computed: {
socialIssuesList() {
return this.$store.state.activity.accompanyingPeriod.socialIssues;
},
socialIssuesSelected() {
return this.$store.state.activity.socialIssues;
},
socialIssuesOther() {
return this.$store.state.socialIssuesOther;
},
socialActionsList() {
return this.$store.getters.socialActionsListSorted;
},
socialActionsSelected() {
return this.$store.state.activity.socialActions;
},
socialActionsList() {
return this.$store.getters.socialActionsListSorted;
},
mounted() {
/* Load classNames after element is present */
const socialActionsEl = document.querySelector(
"input#chill_activitybundle_activity_socialActions",
);
if (socialActionsEl && socialActionsEl.hasAttribute("required")) {
this.socialActionsClassList.required = true;
socialActionsSelected() {
return this.$store.state.activity.socialActions;
},
},
mounted() {
/* Load other issues in multiselect */
this.issueIsLoading = true;
this.actionAreLoaded = false;
getSocialIssues().then((response) => {
/* Add issues to the store */
this.$store.commit("updateIssuesOther", response);
/* Add in list the issues already associated (if not yet listed) */
this.socialIssuesSelected.forEach((issue) => {
if (
this.socialIssuesList.filter((i) => i.id === issue.id).length !== 1
) {
this.$store.commit("addIssueInList", issue);
}
});
const socialIssuesEl = document.querySelector(
"input#chill_activitybundle_activity_socialIssues",
);
if (socialIssuesEl && socialIssuesEl.hasAttribute("required")) {
this.socialIssuesClassList.required = true;
}
/* Remove from multiselect the issues that are not yet in the checkbox list */
this.socialIssuesList.forEach((issue) => {
this.$store.commit("removeIssueInOther", issue);
});
/* Load other issues in multiselect */
this.issueIsLoading = true;
this.actionAreLoaded = false;
/* Filter issues */
this.$store.commit("filterList", "issues");
getSocialIssues().then((response) => {
/* Add issues to the store */
this.$store.commit("updateIssuesOther", response);
/* Add in list the actions already associated (if not yet listed) */
this.socialActionsSelected.forEach((action) => {
this.$store.commit("addActionInList", action);
});
/* Add in list the issues already associated (if not yet listed) */
this.socialIssuesSelected.forEach((issue) => {
if (
this.socialIssuesList.filter((i) => i.id === issue.id)
.length !== 1
) {
this.$store.commit("addIssueInList", issue);
}
});
/* Filter actions */
this.$store.commit("filterList", "actions");
/* Remove from multiselect the issues that are not yet in the checkbox list */
this.socialIssuesList.forEach((issue) => {
this.$store.commit("removeIssueInOther", issue);
});
/* Filter issues */
this.$store.commit("filterList", "issues");
/* Add in list the actions already associated (if not yet listed) */
this.socialActionsSelected.forEach((action) => {
this.$store.commit("addActionInList", action);
});
/* Filter actions */
this.$store.commit("filterList", "actions");
this.issueIsLoading = false;
this.actionAreLoaded = true;
this.updateActionsList();
});
},
methods: {
/* When choosing an issue in multiselect, add it in checkboxes (as selected),
this.issueIsLoading = false;
this.actionAreLoaded = true;
this.updateActionsList();
});
},
methods: {
/* When choosing an issue in multiselect, add it in checkboxes (as selected),
remove it from multiselect, and add socialActions concerned
*/
addIssueInList(value) {
//console.log('addIssueInList', value);
this.$store.commit("addIssueInList", value);
this.$store.commit("removeIssueInOther", value);
this.$store.dispatch("addIssueSelected", value);
this.updateActionsList();
},
/* Update value for selected issues checkboxes
*/
updateIssuesSelected(issues) {
//console.log('updateIssuesSelected', issues);
this.$store.dispatch("updateIssuesSelected", issues);
this.updateActionsList();
},
/* Update value for selected actions checkboxes
*/
updateActionsSelected(actions) {
//console.log('updateActionsSelected', actions);
this.$store.dispatch("updateActionsSelected", actions);
},
/* Add socialActions concerned: after reset, loop on each issue selected
addIssueInList(value) {
//console.log('addIssueInList', value);
this.$store.commit("addIssueInList", value);
this.$store.commit("removeIssueInOther", value);
this.$store.dispatch("addIssueSelected", value);
this.updateActionsList();
},
/* Update value for selected issues checkboxes
*/
updateIssuesSelected(issues) {
//console.log('updateIssuesSelected', issues);
this.$store.dispatch("updateIssuesSelected", issues);
this.updateActionsList();
},
/* Update value for selected actions checkboxes
*/
updateActionsSelected(actions) {
//console.log('updateActionsSelected', actions);
this.$store.dispatch("updateActionsSelected", actions);
},
/* Add socialActions concerned: after reset, loop on each issue selected
to get social actions concerned
*/
updateActionsList() {
this.resetActionsList();
this.socialIssuesSelected.forEach((item) => {
this.actionIsLoading = true;
getSocialActionByIssue(item.id).then(
(actions) =>
new Promise((resolve) => {
actions.results.forEach((action) => {
this.$store.commit("addActionInList", action);
}, this);
updateActionsList() {
this.resetActionsList();
this.socialIssuesSelected.forEach((item) => {
this.actionIsLoading = true;
getSocialActionByIssue(item.id).then(
(actions) =>
new Promise((resolve) => {
actions.results.forEach((action) => {
this.$store.commit("addActionInList", action);
}, this);
this.$store.commit("filterList", "actions");
this.$store.commit("filterList", "actions");
this.actionIsLoading = false;
this.actionAreLoaded = true;
resolve();
}),
);
}, this);
},
/* Reset socialActions List: flush list and restore selected actions
*/
resetActionsList() {
this.$store.commit("resetActionsList");
this.actionAreLoaded = false;
this.socialActionsSelected.forEach((item) => {
this.$store.commit("addActionInList", item);
}, this);
},
this.actionIsLoading = false;
this.actionAreLoaded = true;
resolve();
}),
);
}, this);
},
/* Reset socialActions List: flush list and restore selected actions
*/
resetActionsList() {
this.$store.commit("resetActionsList");
this.actionAreLoaded = false;
this.socialActionsSelected.forEach((item) => {
this.$store.commit("addActionInList", item);
}, this);
},
},
};
</script>
@@ -284,18 +257,18 @@ export default {
@import "ChillMainAssets/chill/scss/chill_variables";
span.multiselect__single {
display: none !important;
display: none !important;
}
#actionsList {
border-radius: 0.5rem;
padding: 1rem;
margin: 0.5rem;
background-color: whitesmoke;
border-radius: 0.5rem;
padding: 1rem;
margin: 0.5rem;
background-color: whitesmoke;
}
span.badge {
margin-bottom: 0.5rem;
@include badge_social($social-issue-color);
margin-bottom: 0.5rem;
@include badge_social($social-issue-color);
}
</style>

View File

@@ -1,38 +1,38 @@
<template>
<span class="inline-choice">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
v-model="selected"
name="action"
:id="action.id"
:value="action"
/>
<label class="form-check-label" :for="action.id">
<span class="badge bg-light text-dark" :title="action.text">{{
action.text
}}</span>
</label>
</div>
</span>
<span class="inline-choice">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
v-model="selected"
name="action"
:id="action.id"
:value="action"
/>
<label class="form-check-label" :for="action.id">
<span class="badge bg-light text-dark" :title="action.text">{{
action.text
}}</span>
</label>
</div>
</span>
</template>
<script>
export default {
name: "CheckSocialAction",
props: ["action", "selection"],
emits: ["updateSelected"],
computed: {
selected: {
set(value) {
this.$emit("updateSelected", value);
},
get() {
return this.selection;
},
},
name: "CheckSocialAction",
props: ["action", "selection"],
emits: ["updateSelected"],
computed: {
selected: {
set(value) {
this.$emit("updateSelected", value);
},
get() {
return this.selection;
},
},
},
};
</script>
@@ -41,25 +41,13 @@ export default {
@import "ChillPersonAssets/chill/scss/mixins";
@import "ChillMainAssets/chill/scss/chill_variables";
span.badge {
@include badge_social($social-action-color);
font-size: 95%;
white-space: normal;
word-wrap: break-word;
word-break: break-word;
display: inline-block;
max-width: 100%;
margin-bottom: 5px;
margin-right: 1em;
text-align: left;
line-height: 1.2em;
&::before {
position: absolute;
left: 11px;
top: 0;
margin: 0 0.3em 0 -0.75em;
}
position: relative;
padding-left: 1.5em;
@include badge_social($social-action-color);
font-size: 95%;
margin-bottom: 5px;
margin-right: 1em;
max-width: 100%; /* Adjust as needed */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>

View File

@@ -1,38 +1,36 @@
<template>
<span class="inline-choice">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
v-model="selected"
name="issue"
:id="issue.id"
:value="issue"
/>
<label class="form-check-label" :for="issue.id">
<span class="badge bg-chill-l-gray text-dark">{{
issue.text
}}</span>
</label>
</div>
</span>
<span class="inline-choice">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
v-model="selected"
name="issue"
:id="issue.id"
:value="issue"
/>
<label class="form-check-label" :for="issue.id">
<span class="badge bg-chill-l-gray text-dark">{{ issue.text }}</span>
</label>
</div>
</span>
</template>
<script>
export default {
name: "CheckSocialIssue",
props: ["issue", "selection"],
emits: ["updateSelected"],
computed: {
selected: {
set(value) {
this.$emit("updateSelected", value);
},
get() {
return this.selection;
},
},
name: "CheckSocialIssue",
props: ["issue", "selection"],
emits: ["updateSelected"],
computed: {
selected: {
set(value) {
this.$emit("updateSelected", value);
},
get() {
return this.selection;
},
},
},
};
</script>
@@ -41,24 +39,9 @@ export default {
@import "ChillPersonAssets/chill/scss/mixins";
@import "ChillMainAssets/chill/scss/chill_variables";
span.badge {
@include badge_social($social-issue-color);
font-size: 95%;
white-space: normal;
word-wrap: break-word;
word-break: break-word;
display: inline-block;
max-width: 100%;
margin-bottom: 5px;
margin-right: 1em;
text-align: left;
&::before {
position: absolute;
left: 11px;
top: 0;
margin: 0 0.3em 0 -0.75em;
}
position: relative;
padding-left: 1.5em;
@include badge_social($social-issue-color);
font-size: 95%;
margin-bottom: 5px;
margin-right: 1em;
}
</style>

View File

@@ -1,50 +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\Migrations\Activity;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Migration fixing the automatic association of users to activities (exchanges).
*
* Originally, the user who created an exchange was not automatically associated
* to it (the "TMS" column), which led to incomplete data and biased statistics.
*
* This migration:
* - retroactively associates the creator of each exchange to the corresponding
* activity;
* - flags these backfilled associations with a temporary column so it is clear
* they were added by this data correction and can be safely cleaned up later.
*/
final class Version20251118124241 extends AbstractMigration
{
public function getDescription(): string
{
return 'Insert the creator of activity into the activity_user table';
}
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE activity_user ADD COLUMN by_migration BOOL DEFAULT FALSE');
$this->addSql("COMMENT ON COLUMN activity_user.by_migration IS 'For backup purpose - can be safely deleted after a while. See migration \\Chill\\Migrations\\Activity\\Version20251118124241'");
$this->addSql('INSERT INTO activity_user (activity_id, user_id, by_migration)
SELECT id, user_id, true FROM activity
ON CONFLICT DO NOTHING');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE activity_user DROP COLUMN by_migration');
}
}

View File

@@ -1,18 +0,0 @@
export:
filter:
activity:
course_having_activity_between_date:
Only course having an activity between from and to: Alleen trajecten met een activiteit tussen {from, date, short} en {to, date, short}
acp_by_activity_type:
'acp_containing_at_least_one_activitytypes': >-
Gefilterde trajecten: alleen die welke ten minste één activiteit bevatten van een van de volgende types: {activitytypes}
{has_date_after, select, 1 {, na {date_after, date}} other {}}
{has_date_before, select, 1 {, voor {date_before, date}} other {}}
describe_action_with_no_subject: >-
Gefilterd op persoon die een activiteit had tussen {date_from, date} en {date_to, date}
describe_action_with_subject: >-
Gefilterd op persoon die een activiteit had tussen {date_from, date} en {date_to, date}, en een van deze gekozen onderwerpen: {reasons}
activity:
title: Activiteit van {date, date, long} - {type}

View File

@@ -10,7 +10,7 @@ Attendee: Présence de l'usager
attendee: présence de l'usager
list_reasons: liste des sujets
user_username: nom de l'utilisateur
circle_name: nom du service
circle_name: nom du cercle
Remark: Commentaire
No comments: Aucun commentaire
Add a new activity: Ajouter une nouvel échange
@@ -20,7 +20,7 @@ not present: absent
Delete: Supprimer
Update: Mettre à jour
Update activity: Modifier l'échange
Scope: Service
Scope: Cercle
Activity data: Données de l'échange
Activity location: Localisation de l'échange
No reason associated: Aucun sujet
@@ -398,15 +398,13 @@ export:
sent received: Envoyé ou reçu
emergency: Urgence
accompanying course id: Identifiant du parcours
course circles: Services du parcours
course circles: Cercles du parcours
travelTime: Durée de déplacement
durationTime: Durée
id: Identifiant
List activities linked to an accompanying course: Liste les échanges liés à un parcours en fonction de différents filtres.
List activity linked to a course: Liste des échanges liés à un parcours
commentText: Commentaire
comment_date: Date de la dernière édition du commentaire
comment_user: Dernière édition par
filter:
activity:

View File

@@ -1,500 +1,234 @@
#general
Show the activity: Uitwisseling bekijken
Edit the activity: Uitwisseling bewerken
Activity: Uitwisseling
Show the activity: Toon activiteit
Edit the activity: Wijzig activiteit
Activity: Activiteit
Duration time: Duur
Duration Time: Duur
durationTime: duur
Travel time: Reisduur
Attendee: Aanwezigheid van de gebruiker
attendee: aanwezigheid van de gebruiker
list_reasons: lijst van onderwerpen
user_username: naam van de gebruiker
circle_name: naam van de dienst
durationTime: duur
Travel time: Duur van verplaatsing
Attendee: Aanwezigheden
attendee: aanwezigheden
list_reasons: Onderwerpen
user_username: gebruikersnaam
circle_name: naam kring
Remark: Opmerking
No comments: Geen opmerking
Add a new activity: Nieuwe uitwisseling toevoegen
Activity list: Lijst van uitwisselingen
No comments: Geen opmerkingen
Add a new activity: Voeg een nieuwe activiteit toe
Activity list: Lijst van activiteiten
present: aanwezig
not present: afwezig
Delete: Verwijderen
Update: Bijwerken
Update activity: Uitwisseling bewerken
Scope: Dienst
Activity data: Gegevens van de uitwisseling
Activity location: Locatie van de uitwisseling
Update activity: Activieit bijwerken
Scope: Werkingsgebied
Activity data: Gegevens activiteit
Activity location: Locatie activiteit
No reason associated: Geen onderwerp
No social issues associated: Geen sociale problematiek
No social actions associated: Geen begeleidingsactie
There isn't any activities.: Geen uitwisseling geregistreerd.
type_name: type van de uitwisseling
No social issues associated: Geen sociaal vraagstuk
No social actions associated: Geen maatschappelijke actie
There isn't any activities.: Er zijn geen activiteiten
type_name: Soort activiteit
person_firstname: voornaam
person_lastname: achternaam
person_id: identificatie van de gebruiker
Type: Type
person_lastname: familienaam
person_id: Identificatienummer persoon
Type: Soort
Invisible: Onzichtbaar
Optional: Optioneel
Required: Verplicht
Persons: Gebruikers
Persons: Personen
Users: Gebruikers
Emergency: Urgent
Emergency: Dringend
Sent received: Inkomend / Uitgaand
Sent: Verzenden
Received: Ontvangen
by: 'Door '
location: Plaats
Reasons: Onderwerpen
Private comment: Privé opmerking
sent: Verzonden
received: Ontvangen
#forms
Activity creation: Nieuwe uitwisseling
Create: Aanmaken
Back to the list: Terug naar de lijst
Save activity: Uitwisseling opslaan
Reset form: Formulier resetten
Choose the duration: Duur kiezen
Choose a type: Type kiezen
5 minutes: 5 minuten
10 minutes: 10 minuten
15 minutes: 15 minuten
20 minutes: 20 minuten
25 minutes: 25 minuten
30 minutes: 30 minuten
45 minutes: 45 minuten
1 hour: 1 uur
1 hour 15: 1 uur 15
1 hour 30: 1 uur 30
1 hour 45: 1 uur 45
2 hours: 2 uur
2 hours 15: 2 uur 15
2 hours 30: 2 uur 30
2 hours 45: 2 uur 45
3 hours: 3 uur
3 hours 30: 3 uur 30
4 hours: 4 uur
4 hours 30: 4 uur 30
5 hours: 5 uur
5 hours 30: 5 uur 30
6 hours: 6 uur
6 hours 30: 6 uur 30
7 hours: 7 uur
7 hours 30: 7 uur 30
8 hours: 8 uur
8 hours 30: 8 uur 30
9 hours: 9 uur
9 hours 30: 9 uur 30
10 hours: 10 uur
11 hours: 11 uur
12 hours: 12 uur
Concerned groups: Betrokken partijen bij de uitwisseling
Persons in accompanying course: Gebruikers van het traject
Third persons: Niet-prof. derden
Others persons: Gebruikers
Third parties: Professionele derden
Activity creation: Nouvel échange
Create: Créer
Back to the list: Retour à la liste
Save activity: Sauver l'échange
Reset form: Remise à zéro du formulaire
Choose the duration: Choisir la durée
Choose a type: Choisir un type
5 minutes: 5 minutes
10 minutes: 10 minutes
15 minutes: 15 minutes
20 minutes: 20 minutes
25 minutes: 25 minutes
30 minutes: 30 minutes
45 minutes: 45 minutes
1 hour: 1 heure
1 hour 15: 1 heure 15
1 hour 30: 1 heure 30
1 hour 45: 1 heure 45
2 hours: 2 heures
Concerned groups: Parties concernées
Persons in accompanying course: Usagers du parcours
Third persons: Tiers non-pro.
Others persons: Usagers
Third parties: Tiers professionnels
Users concerned: T(M)S
activity:
date: Datum van de uitwisseling
Insert a document: Document invoegen
Remove a document: Document verwijderen
comment: Opmerking
deleted: Uitwisseling verwijderd
errors: Het formulier bevat fouten
social_issues: Sociale problematieken
choose_other_social_issue: Andere sociale problematiek toevoegen...
social_actions: Begeleidingsacties
select_first_a_social_issue: Selecteer eerst een sociale problematiek
social_action_list_empty: Geen sociale actie beschikbaar
add_persons: Betrokken personen toevoegen
bloc_persons: Gebruikers
bloc_persons_associated: Gebruikers van het traject
bloc_persons_not_associated: Niet-prof. derden
bloc_thirdparty: Professionele derden
bloc_users: T(M)S
location: Locatie
choose_location: Kies een locatie
choose_location_type: Kies een type locatie
create_new_location: Nieuwe locatie aanmaken
location_fields:
name: Naam
type: Type
phonenumber1: Telefoon
phonenumber2: Andere telefoon
email: E-mailadres
create_address: Adres aanmaken
edit_address: Adres bewerken
No documents: Geen document
# activity filter in list page
activity_filter:
My activities: Mijn uitwisselingen (waar ik aan deelneem)
Types: Op type uitwisseling
Jobs: Op betrokken beroep
Insert a document: Insérer un document
Remove a document: Supprimer le document
comment: Commentaire
No documents: Aucun document
#timeline
'%user% has done an %activity_type%': '%user% heeft een uitwisseling van type "%activity_type%" uitgevoerd'
'%user% has done an %activity_type%': '%user% a effectué un échange de type "%activity_type%"'
#controller
'Success : activity created!': De uitwisseling is aangemaakt.
'The form is not valid. The activity has not been created !': Het formulier is ongeldig. De uitwisseling is niet aangemaakt.
'Success : activity updated!': De uitwisseling is bijgewerkt.
'The form is not valid. The activity has not been updated !': Het formulier is ongeldig. De uitwisseling is niet bijgewerkt.
'Success : activity created!': L'échange a été créé.
'The form is not valid. The activity has not been created !': Le formulaire est invalide. L'échange n'a pas été créé.
'Success : activity updated!': L'échange a été mis à jour.
'The form is not valid. The activity has not been updated !': Le formulaire est invalide. L'échange n'a pas été mis à jour.
# ROLES
CHILL_ACTIVITY_CREATE: Uitwisseling aanmaken
CHILL_ACTIVITY_UPDATE: Uitwisseling bewerken
CHILL_ACTIVITY_SEE: Uitwisseling bekijken
CHILL_ACTIVITY_SEE_DETAILS: Detail van uitwisselingen bekijken
CHILL_ACTIVITY_DELETE: Uitwisseling verwijderen
CHILL_ACTIVITY_STATS: Statistieken van uitwisselingen
CHILL_ACTIVITY_LIST: Lijst van uitwisselingen
CHILL_ACTIVITY_CREATE_PERSON: Uitwisseling aanmaken gekoppeld aan een gebruiker
CHILL_ACTIVITY_CREATE_ACCOMPANYING_COURSE: Uitwisseling aanmaken gekoppeld aan een traject
CHILL_ACTIVITY_FULL: Details bekijken, aanmaken, verwijderen en bijwerken van een uitwisseling
CHILL_ACTIVITY_CREATE: Créer un échange
CHILL_ACTIVITY_UPDATE: Modifier un échange
CHILL_ACTIVITY_SEE: Voir un échange
CHILL_ACTIVITY_SEE_DETAILS: Voir le détail des échanges
CHILL_ACTIVITY_DELETE: Supprimer un échange
CHILL_ACTIVITY_STATS: Statistique des échanges
CHILL_ACTIVITY_LIST: Liste des échanges
# admin
Activities: Uitwisselingen
Activity configuration: Configuratie van uitwisselingen
Activity configuration menu: Configuratie van uitwisselingen
Activity types: Types uitwisseling
Activity type configuration: Configuratie van categorieën van uitwisselingen
Activity Reasons: Onderwerpen van een uitwisseling
Activity Reasons Category: Categorieën van onderwerpen van uitwisselingen
Activity Types Categories: Categorieën van types uitwisseling
Activity Presences: Aanwezigheden bij uitwisselingen
Associated activity reason category is inactive: De gekoppelde onderwerpscategorie is inactief
Activities: Échanges
Activity configuration: Configuration des échanges
Activity configuration menu: Configuration des échanges
Activity types: Types d'échange
Activity type configuration: Configuration des categories d'échanges
Activity Reasons: Sujets d'un échange
Activity Reasons Category: Catégories de sujet d'échanges
Activity Types Categories: Catégories des types d'échanges
Activity Presences: Presences des échanges
# Crud
crud:
activity_type:
title_new: Nieuw type uitwisseling
title_edit: Type uitwisseling bewerken
activity_type_category:
title_new: Nieuwe categorie van type uitwisseling
title_edit: Categorie van type uitwisseling bewerken
activity_presence:
title_new: Nieuwe aanwezigheid bij uitwisselingen
title_edit: Aanwezigheid bij uitwisselingen bewerken
activity_type:
title_new: Nouveau type d'échange
title_edit: Edition d'un type d'échange
activity_type_category:
title_new: Nouvelle catégorie de type d'échange
title_edit: Edition d'une catégorie de type d'échange
# activity reason admin
ActivityReason list: Lijst van onderwerpen
Create a new activity reason: Nieuw onderwerp aanmaken
Active: Actief
Category: Categorie
ActivityReason creation: Nieuw onderwerp
ActivityReason edit: Onderwerp bewerken
ActivityReason: Onderwerp van uitwisseling
The entity is inactive and won't be proposed: Het onderwerp is inactief en zal niet worden voorgesteld
The entity is active and will be proposed: Het onderwerp is actief en zal worden voorgesteld
ActivityReason list: Liste des sujets
Create a new activity reason: Créer un nouveau sujet
Active: Actif
Category: Catégorie
ActivityReason creation: Nouveau sujet
ActivityReason edit: Modification d'un sujet
ActivityReason: Sujet d'échange
The entity is inactive and won't be proposed: Le sujet est inactif et ne sera pas proposé
The entity is active and will be proposed: Le sujet est actif et sera proposé
#activity reason category admin
ActivityReasonCategory list: Categorieën van onderwerpen
Create a new activity category reason: Nieuwe categorie aanmaken
ActivityReasonCategory creation: Nieuwe categorie van onderwerp
ActivityReasonCategory edit: Categorie van onderwerp bewerken
ActivityReasonCategory: Categorie van onderwerp van uitwisseling
ActivityReasonCategory is active and will be proposed: De categorie is actief en zal worden voorgesteld
ActivityReasonCategory is inactive and won't be proposed: De categorie is inactief en zal niet worden voorgesteld
#activity presence admin
ActivityPresence list: Lijst van aanwezigheden bij uitwisselingen
Create a new activity presence: Nieuwe "Aanwezigheid bij uitwisselingen" aanmaken
ActivityReasonCategory list: Catégories de sujets
Create a new activity category reason: Créer une nouvelle catégorie
ActivityReasonCategory creation: Nouvelle catégorie de sujet
ActivityReasonCategory edit: Modification d'une catégorie de sujet
ActivityReasonCategory: Catégorie de sujet d'échange
ActivityReasonCategory is active and will be proposed: La catégorie est active et sera proposée
ActivityReasonCategory is inactive and won't be proposed: La catégorie est inactive et ne sera pas proposée
# activity type type admin
ActivityType list: Types uitwisselingen
Create a new activity type: Nieuw type uitwisseling aanmaken
Persons visible: Zichtbaarheid van het veld Gebruikers
Persons label: Label van het veld Gebruikers
User visible: Zichtbaarheid van het veld Gebruiker
User label: Label van het veld Gebruiker
Date visible: Zichtbaarheid van het veld Datum
Date label: Label van het veld Datum
Location visible: Zichtbaarheid van het veld Plaats
Location label: Label van het veld Plaats
Third parties visible: Zichtbaarheid van het veld Derden
Third parties label: Label van het veld Derden
Duration time visible: Zichtbaarheid van het veld Duur
Duration time label: Label van het veld Duur
Travel time visible: Zichtbaarheid van het veld Reisduur
Travel time label: Label van het veld Reisduur
Attendee visible: Zichtbaarheid van het veld Aanwezigheid van de gebruiker
Attendee label: Label van het veld Aanwezigheid van de gebruiker
Reasons visible: Zichtbaarheid van het veld Onderwerp
Reasons label: Label van het veld Onderwerp
Comment visible: Zichtbaarheid van het veld Opmerking
Comment label: Label van het veld Opmerking
Private comment visible: Zichtbaarheid van het veld Privé Opmerking
Private comment label: Label van het veld Privé Opmerking
Emergency visible: Zichtbaarheid van het veld Urgent
Emergency label: Label van het veld Urgent
Accompanying period visible: Zichtbaarheid van het veld begeleidingstraject
Accompanying period label: Label van het veld begeleidingstraject
Social issues visible: Zichtbaarheid van het veld Sociale problematieken
Social issues label: Label van het veld Sociale problematieken
Social actions visible: Zichtbaarheid van het veld Sociale actie
Social actions label: Label van het veld Sociale actie
Users visible: Zichtbaarheid van het veld Gebruikers
Users label: Label van het veld Gebruikers
Sent received visible: Zichtbaarheid van het veld Inkomend / Uitgaand
Sent received label: Label van het veld Inkomend / Uitgaand
Documents visible: Zichtbaarheid van het veld Documenten
Documents label: Label van het veld Documenten
ActivityType list: Types d'échanges
Create a new activity type: Créer un nouveau type d'échange
Persons visible: Visibilité du champ Personnes
Persons label: Libellé du champ Personnes
User visible: Visibilité du champ Utilisateur
User label: Libellé du champ Utilisateur
Date visible: Visibilité du champ Date
Date label: Libellé du champ Date
Location visible: Visibilité du champ Lieu
Location label: Libellé du champ Lieu
Third parties visible: Visibilité du champ Tiers
Third parties label: Libellé du champ Tiers
Duration time visible: Visibilité du champ Durée
Duration time label: Libellé du champ Durée
Travel time visible: Visibilité du champ Durée de déplacement
Travel time label: Libellé du champ Durée de déplacement
Attendee visible: Visibilité du champ Présence de l'usager
Attendee label: Libellé du champ Présence de l'usager
Reasons visible: Visibilité du champ Sujet
Reasons label: Libellé du champ Sujet
Comment visible: Visibilité du champ Commentaire
Comment label: Libellé du champ Commentaire
Emergency visible: Visibilité du champ Urgent
Emergency label: Libellé du champ Urgent
Accompanying period visible: Visibilité du champ Période d'accompagnement
Accompanying period label: Libellé du champ Période d'accompagnement
Social issues visible: Visibilité du champ Problématiques sociales
Social issues label: Libellé du champ Problématiques sociales
Social actions visible: Visibilité du champ Action sociale
Social actions label: Libellé du champ Action sociale
Users visible: Visibilité du champ Utilisateurs
Users label: Libellé du champ Utilisateurs
Sent received visible: Visibilité du champ Entrant / Sortant
Sent received label: Libellé du champ Entrant / Sortant
Documents visible: Visibilité du champ Documents
Documents label: Libellé du champ Documents
# activity type category admin
ActivityTypeCategory list: Lijst van categorieën van types uitwisseling
Create a new activity type category: Nieuwe categorie van type uitwisseling aanmaken
Create a new activity in accompanying course: Uitwisseling aanmaken in het traject
ActivityTypeCategory list: Liste des catégories des types d'activité
Create a new activity type category: Créer une nouvelle catégorie de type d'échange
# activity delete
Remove activity: Uitwisseling verwijderen
Are you sure you want to remove the activity about "%name%" ?: Weet u zeker dat u een uitwisseling wilt verwijderen die betrekking heeft op "%name%"?
The activity has been successfully removed.: De uitwisseling is verwijderd.
Remove activity: Supprimer un échange
Are you sure you want to remove the activity about "%name%" ?: Êtes-vous sûr de vouloir supprimer un échange qui concerne "%name%" ?
The activity has been successfully removed.: L'échange a été supprimée.
# exports
Exports of activities linked to a person: Exports van uitwisselingen gekoppeld aan een gebruiker
Number of activities linked to a person: Aantal uitwisselingen gekoppeld aan een gebruiker
Count activities linked to a person: Aantal uitwisselingen
Count activities linked to a person by various parameters.: Telt het aantal geregistreerde uitwisselingen gekoppeld aan een gebruiker op basis van verschillende parameters.
Sum activity linked to a person duration: Duur van uitwisselingen
Sum activities linked to a person duration: Duur van uitwisselingen gekoppeld aan een gebruiker
Sum activities linked to a person duration by various parameters.: Telt de duur van uitwisselingen op basis van verschillende parameters.
List activity linked to a person: Uitwisselingen opsommen
List activities linked to a person: Lijst van uitwisselingen gekoppeld aan een gebruiker
List activities linked to a person description: Maakt de lijst van uitwisselingen op basis van verschillende parameters.
Exports of activities linked to an accompanying period: Exports van uitwisselingen gekoppeld aan een traject
Number of activities linked to an accompanying period: Aantal uitwisselingen gekoppeld aan een traject
Count activities linked to an accompanying period: Aantal uitwisselingen
Count activities linked to an accompanying period by various parameters.: Telt het aantal geregistreerde uitwisselingen gekoppeld aan een traject op basis van verschillende parameters.
Sum activity linked to an accompanying period duration: Som van de duur van uitwisselingen
Sum activities linked to an accompanying period duration: Som van de duur van uitwisselingen gekoppeld aan een traject
Sum activities linked to an accompanying period duration by various parameters.: Telt de duur van uitwisselingen op basis van verschillende parameters.
Sum activity linked to an accompanying period visit duration: Som van de reisduur van uitwisselingen
Sum activities linked to an accompanying period visit duration: Som van de reisduur van uitwisselingen gekoppeld aan een traject
Sum activities linked to an accompanying period visit duration by various parameters.: Telt de reisduur van uitwisselingen op basis van verschillende parameters.
Average activity linked to an accompanying period duration: Gemiddelde van de duur van uitwisselingen
Average activities linked to an accompanying period duration: Gemiddelde van de duur van uitwisselingen gekoppeld aan een traject
Average activities linked to an accompanying period duration by various parameters.: Gemiddelde van de duur van uitwisselingen op basis van verschillende parameters.
Average activity linked to an accompanying period visit duration: Gemiddelde van de reisduur van uitwisselingen
Average activities linked to an accompanying period visit duration: Gemiddelde van de reisduur van uitwisselingen gekoppeld aan een traject
Average activities linked to an accompanying period visit duration by various parameters.: Gemiddelde van de reisduur van uitwisselingen op basis van verschillende parameters.
Count activities: Nombre d'échanges
Count activities by various parameters.: Compte le nombre d'échanges enregistrées en fonction de différents paramètres.
Sum activity duration: Total de la durée des échanges
Sum activities duration by various parameters.: Additionne la durée des échanges en fonction de différents paramètres.
List activities: Liste les échanges
Number of activities: Nombre d'échanges
#filters
Filter by reason: Uitwisselingen filteren op onderwerp
'Filtered by reasons: only %list%': 'Gefilterd op onderwerp: alleen %list%'
'Filtered by activity type: only %list%': "Gefilterd op type uitwisseling: alleen %list%"
Filtered by date activity: Uitwisselingen filteren op datum
Activities after this date: Uitwisselingen na deze datum
Activities before this date: Uitwisselingen vóór deze datum
"Filtered by date of activity: only between %date_from% and %date_to%": "Gefilterd op datum van de uitwisseling: alleen tussen %date_from% en %date_to%"
This date should be after the date given in "Implied in an activity after this date" field: Deze datum moet later zijn dan de datum in het veld "uitwisselingen na deze datum"
Filter by reason: Filtrer par sujet d'activité
'Filtered by reasons: only %list%': 'Filtré par sujet: seulement %list%'
'Filtered by activity type: only %list%': "Filtré par type d'activity: uniquement %list%"
Filtered by date activity: Filtrer par date d'activité
Activities after this date: Activités après cette date
Activities before this date: Activités avant cette date
"Filtered by date of activity: only between %date_from% and %date_to%": "Filtré par date de l'activité: uniquement entre %date_from% et %date_to%"
This date should be after the date given in "Implied in an activity after this date" field: Cette date devrait être postérieure à la date donnée dans le champ "activités après cette date"
Filtered by person having an activity in a period: Uniquement les personnes ayant eu une activité dans la période donnée
Implied in an activity after this date: Impliqué dans une activité après cette date
Implied in an activity before this date: Impliqué dans une activité avant cette date
Filtered by person having an activity between %date_from% and %date_to% with reasons %reasons_name%: Filtré par personnes associées à une activité entre %date_from% et %date_to% avec les sujets %reasons_name%
Activity reasons for those activities: Sujets de ces activités
Filter by activity type: Uitwisselingen filteren op type
Filter activity by location: Uitwisselingen filteren op locatie
'Filtered activity by location: only %locations%': "Gefilterd op locatie: alleen %locations%"
Filter activity by locationtype: Uitwisselingen filteren op type locatie
'Filtered activity by locationtype: only %types%': "Gefilterd op type locatie: alleen %types%"
Accepted locationtype: Types locatie
Accepted users: TMS(en)
Filter activity by emergency: Uitwisselingen filteren op urgentie
'Filtered activity by emergency: only %emergency%': "Gefilterd op urgentie: alleen als %emergency%"
activity is emergency: de uitwisseling is urgent
activity is not emergency: de uitwisseling is niet urgent
Filter activity by sentreceived: Uitwisselingen filteren op verzonden/ontvangen
'Filtered activity by sentreceived: only %sentreceived%': "Gefilterd op verzonden/ontvangen: alleen %sentreceived%"
Accepted sentreceived: ''
Filter activity by linked socialaction: Uitwisselingen filteren op gekoppelde actie
'Filtered activity by linked socialaction: only %actions%': "Gefilterd op gekoppelde actie: alleen %actions%"
Filter activity by linked socialissue: Uitwisselingen filteren op gekoppelde problematiek
'Filtered activity by linked socialissue: only %issues%': "Gefilterd op gekoppelde problematiek: alleen %issues%"
Filter activity by user: Uitwisselingen filteren op hoofdgebruiker
Filter activity by users: Uitwisselingen filteren op deelnemende gebruiker
Filter activity by creator: Uitwisselingen filteren op aanmaker van de uitwisseling
'Filtered activity by user: only %users%': "Gefilterd op referent: alleen %users%"
'Filtered activity by users: only %users%': "Gefilterd op deelnemende gebruikers: alleen %users%"
'Filtered activity by creator: only %users%': "Gefilterd op aanmaker: alleen %users%"
Creators: Aanmakers
Accepted userscope: Diensten
Filter acp which has no activity: Trajecten filteren die geen uitwisseling hebben
Filtered acp which has no activities: Trajecten zonder gekoppelde uitwisseling filteren
Group acp by activity number: Trajecten groeperen op aantal uitwisselingen
Filter by activity type: Filtrer par type d'activité
#aggregators
Activity type: Type uitwisseling
Activity user: Gebruiker gekoppeld aan de uitwisseling
By reason: Op onderwerp
By category of reason: Op categorie van onderwerp
Reason's level: Niveau van het onderwerp
Group by reasons: Onderwerp van uitwisseling
Aggregate by activity user: Uitwisselingen groeperen op referent
Aggregate by activity users: Uitwisselingen groeperen op deelnemende gebruikers
Aggregate by activity type: Uitwisselingen groeperen op type
Aggregate by activity reason: Uitwisselingen groeperen op onderwerp
Activity type: Type d'activité
Activity user: Utilisateur lié à l'activity
By reason: Par sujet
By category of reason: Par catégorie de sujet
Reason's level: Niveau du sujet
Group by reasons: Sujet d'activité
Aggregate by activity user: Grouper par utilisateur lié à l'activité
Aggregate by activity type: Grouper par type d'activité
Aggregate by activity reason: Grouper par sujet de l'activité
Group activity by locationtype: Uitwisselingen groeperen op type locatie
Group activity by date: Uitwisselingen groeperen op datum
Frequency: Frequentie
by month: Per maand
by week: Per week
for week: Week
by year: Per jaar
in year: In
Group activity by creator: Uitwisselingen groeperen op aanmaker van de uitwisseling
Group activity by linked thirdparties: Uitwisselingen groeperen op betrokken derde
Accepted thirdparty: Betrokken derde
Group activity by linked socialaction: Uitwisselingen groeperen op gekoppelde actie
Group activity by linked socialissue: Uitwisselingen groeperen op gekoppelde problematiek
Group activity by userscope: Uitwisselingen groeperen op dienst van de aanmaker
Last activities: Les dernières activités
Last activities: De laatste uitwisselingen
See activity in accompanying course context: Voir l'activité dans le contexte du parcours d'accompagnement
See activity in accompanying course context: Uitwisseling bekijken in de context van het begeleidingstraject
You get notified of an activity which does not exists any more: Deze melding komt niet overeen met een geldige uitwisseling.
you are not allowed to see it details: De melding verwijst naar een uitwisseling waartoe u geen toegang hebt.
This is the minimal activity data: Uitwisseling nr.
You get notified of an activity which does not exists any more: Cette notification ne correspond pas à une activité valide.
you are not allowed to see it details: La notification fait référence à une activité à laquelle vous n'avez pas accès.
This is the minimal activity data: Activité n°
docgen:
Activity basic: Uitwisseling
A basic context for activity: Context voor uitwisselingen
Accompanying period with a list of activities: Begeleidingstraject met lijst van uitwisselingen
Accompanying period with a list of activities description: Deze context neemt de informatie van het traject over, en alle uitwisselingen voor een traject. De uitwisselingen worden niet gefilterd.
myActivitiesOnly: Alleen rekening houden met uitwisselingen waarin ik heb deelgenomen
myWorksOnly: Alleen rekening houden met begeleidingsacties waarvan ik referent ben
export:
export:
count_person_on_activity:
title: Aantal betrokken gebruikers bij uitwisselingen
description: Telt het aantal betrokken gebruikers bij uitwisselingen. Als een gebruiker aanwezig is in meerdere uitwisselingen, wordt hij slechts één keer geteld.
header: Aantal betrokken gebruikers bij uitwisselingen
count_household_on_activity:
title: Aantal betrokken huishoudens bij uitwisselingen
description: Telt het aantal betrokken huishoudens bij uitwisselingen. Als een huishouden aanwezig is in meerdere uitwisselingen, wordt het slechts één keer geteld. Gebruikers zonder huishouden worden niet geteld.
header: Aantal betrokken huishoudens bij uitwisselingen
count_household_on_activity_person:
title: Aantal betrokken huishoudens bij uitwisselingen
description: Telt het aantal betrokken huishoudens bij uitwisselingen. Als een huishouden aanwezig is in meerdere uitwisselingen, wordt het slechts één keer geteld. Gebruikers zonder huishouden worden niet geteld. Wanneer een gebruiker van huishouden verandert, wordt elk huishouden één keer geteld.
header: Aantal betrokken huishoudens bij uitwisselingen
list:
activity:
users name: Naam van de gebruikers
users ids: Identificatie van de gebruikers
third parties ids: Identificatie van de derden
persons ids: Identificatie van de gebruikers
persons name: Naam van de gebruikers
thirds parties: Derden
date: Datum van de uitwisseling
locationName: Locatie
sent received: Verzonden of ontvangen
emergency: Urgentie
accompanying course id: Identificatie van het traject
course circles: Diensten van het traject
travelTime: Reisduur
durationTime: Duur
id: Identificatie
List activities linked to an accompanying course: Somt uitwisselingen op gekoppeld aan een traject op basis van verschillende filters.
List activity linked to a course: Lijst van uitwisselingen gekoppeld aan een traject
commentText: Opmerking
comment_date: Datum van de laatste bewerking van de opmerking
comment_user: Laatste bewerking door
filter:
activity:
by_users_job:
Filter by users job: Uitwisselingen filteren op beroep van ten minste één deelnemende gebruiker
'Filtered activity by users job: only %jobs%': 'Gefilterd op beroep van ten minste één deelnemende gebruiker: alleen %jobs%'
by_users_scope:
Filter by users scope: Uitwisselingen filteren op dienst van ten minste één deelnemende gebruiker
'Filtered activity by users scope: only %scopes%': 'Gefilterd op dienst van ten minste één deelnemende gebruiker: alleen %scopes%'
course_having_activity_between_date:
Title: Trajecten filteren die een uitwisseling hebben ontvangen tussen twee data
Receiving an activity after: Die een uitwisseling hebben ontvangen na
Receiving an activity before: Die een uitwisseling hebben ontvangen vóór
acp_by_activity_type:
'activity after': Uitwisselingen na
activity after help: Indien leeg gelaten, wordt er geen rekening mee gehouden
activity before: Uitwisselingen vóór
activity before help: Indien leeg gelaten, wordt er geen rekening mee gehouden
person_between_dates:
Implied in an activity after this date: Betrokken bij een uitwisseling na deze datum
Implied in an activity before this date: Betrokken bij een uitwisseling vóór deze datum
Activity reasons for those activities: Onderwerpen van deze uitwisselingen
if no reasons: Als geen enkel onderwerp is aangevinkt, worden alle onderwerpen in aanmerking genomen
title: Gebruikers filteren die gekoppeld zijn geweest aan een uitwisseling tijdens de periode
date mismatch: De einddatum van de periode moet later zijn dan de startdatum
by_creator_scope:
Filter activity by user scope: Uitwisselingen filteren op dienst van de aanmaker van de uitwisseling
'Filtered activity by user scope: only %scopes%': "Gefilterd op dienst van de aanmaker van de uitwisseling: alleen %scopes%"
by_creator_job:
job_form_label: Beroepen
Filter activity by user job: Uitwisselingen filteren op beroep van de aanmaker van de uitwisseling
'Filtered activity by user job: only %jobs%': "Gefilterd op beroep van de aanmaker van de uitwisseling: alleen %jobs%"
by_persons:
Filter activity by persons: Uitwisselingen filteren op deelnemende gebruiker
'Filtered activity by persons: only %persons%': 'Uitwisselingen gefilterd op deelnemende gebruikers: alleen %persons%'
persons taking part on the activity: Gebruikers deelnemend aan de uitwisseling
by_sent_received:
Sent or received: Verzonden of ontvangen
is sent: verzonden
is received: ontvangen
by_presence:
Filter activity by activity presence: Uitwisselingen filteren op aanwezigheid van de gebruiker
presences: Aanwezigheden
'Filtered by activity presence: only %presences%': 'Gefilterd op aanwezigheid van de gebruiker: alleen %presences%'
aggregator:
person:
by_person:
title: Uitwisselingen groeperen op gebruiker (gebruikersdossier waarin de uitwisseling is geregistreerd)
person: Gebruiker
by_household:
title: Uitwisselingen groeperen op huishouden
household: Identificatie huishouden
acp:
by_activity_type:
title: Trajecten groeperen op type uitwisseling
after_date: Alleen uitwisselingen na deze datum
before_date: Alleen uitwisselingen vóór deze datum
activity_type: Types uitwisseling
activity:
by_sent_received:
Sent or received: Verzonden of ontvangen
is sent: verzonden
is received: ontvangen
Group activity by sentreceived: Uitwisselingen groeperen op verzonden / ontvangen
by_location:
Activity Location: Locatie van de uitwisseling
Title: Uitwisselingen groeperen op locatie van de uitwisseling
by_user_job:
Users 's job: Beroep van de gebruikers deelnemend aan de uitwisseling
Aggregate by users job: Uitwisselingen groeperen op beroep van de deelnemende gebruikers
by_user_scope:
Users 's scope: Hoofddienst van de gebruikers deelnemend aan de uitwisseling
Aggregate by users scope: Uitwisselingen groeperen op hoofddienst van de gebruiker
by_creator_scope:
Group activity by creator scope: Uitwisselingen groeperen op dienst van de aanmaker van de uitwisseling
Calc date: Berekeningsdatum van de dienst van de aanmaker van de uitwisseling
by_creator_job:
Group activity by creator job: Uitwisselingen groeperen op beroep van de aanmaker van de uitwisseling
Calc date: Berekeningsdatum van het beroep van de aanmaker van de uitwisseling
by_persons:
Group activity by persons: Uitwisselingen groeperen op deelnemende gebruiker
Persons: Deelnemende gebruikers
by_activity_presence:
Group activity by presence: Uitwisselingen groeperen op aanwezigheid van de gebruiker
header: Aanwezigheid van gebruiker(s)
generic_doc:
filter:
keys:
accompanying_period_activity_document: Document van uitwisselingen van trajecten
Activity basic: Echange
A basic context for activity: Contexte pour les activités

View File

@@ -25,7 +25,6 @@ final class ChillAsideActivityExtension extends Extension implements PrependExte
$config = $this->processConfiguration($configuration, $configs);
$container->setParameter('chill_aside_activity.form.time_duration', $config['form']['time_duration']);
$container->setParameter('chill_aside_activity.show_concerned_persons_count', 'visible' === $config['show_concerned_persons_count']);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../config'));
$loader->load('services.yaml');
@@ -39,24 +38,6 @@ final class ChillAsideActivityExtension extends Extension implements PrependExte
{
$this->prependRoute($container);
$this->prependCruds($container);
$this->prependTwigConfig($container);
}
protected function prependTwigConfig(ContainerBuilder $container)
{
// Get the configuration for this bundle
$chillAsideActivityConfig = $container->getExtensionConfig($this->getAlias());
$config = $this->processConfiguration($this->getConfiguration($chillAsideActivityConfig, $container), $chillAsideActivityConfig);
// Add configuration to twig globals
$twigConfig = [
'globals' => [
'chill_aside_activity_config' => [
'show_concerned_persons_count' => 'visible' === $config['show_concerned_persons_count'],
],
],
];
$container->prependExtensionConfig('twig', $twigConfig);
}
protected function prependCruds(ContainerBuilder $container)

View File

@@ -141,12 +141,6 @@ class Configuration implements ConfigurationInterface
->end()
->end()
->end()
->end()
->enumNode('show_concerned_persons_count')
->values(['hidden', 'visible'])
->defaultValue('hidden')
->info('Show the concerned persons count field in aside activity forms and views')
->end()
->end();
return $treeBuilder;

View File

@@ -62,10 +62,6 @@ class AsideActivity implements TrackCreationInterface, TrackUpdateInterface
#[ORM\ManyToOne(targetEntity: User::class)]
private User $updatedBy;
#[Assert\GreaterThanOrEqual(0)]
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: true)]
private ?int $concernedPersonsCount = 0;
public function getAgent(): ?User
{
return $this->agent;
@@ -190,16 +186,4 @@ class AsideActivity implements TrackCreationInterface, TrackUpdateInterface
return $this;
}
public function getConcernedPersonsCount(): ?int
{
return $this->concernedPersonsCount;
}
public function setConcernedPersonsCount(?int $concernedPersonsCount): self
{
$this->concernedPersonsCount = $concernedPersonsCount;
return $this;
}
}

View File

@@ -1,86 +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\AsideActivityBundle\Export\Aggregator;
use Chill\AsideActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class ByConcernedPersonsCountAggregator implements AggregatorInterface
{
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
{
$qb->addSelect('aside.concernedPersonsCount AS by_concerned_persons_count_aggregator')
->addGroupBy('by_concerned_persons_count_aggregator');
}
public function applyOn(): string
{
return Declarations::ASIDE_ACTIVITY_TYPE;
}
public function buildForm(FormBuilderInterface $builder): void
{
// No form needed
}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data): callable
{
return function ($value): string {
if ('_header' === $value) {
return 'export.aggregator.Concerned persons count';
}
if (null === $value) {
return 'export.aggregator.No concerned persons count specified';
}
return (string) $value;
};
}
public function getQueryKeys($data): array
{
return ['by_concerned_persons_count_aggregator'];
}
public function getTitle(): string
{
return 'export.aggregator.Group by concerned persons count';
}
}

View File

@@ -1,116 +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\AsideActivityBundle\Export\Export;
use Chill\AsideActivityBundle\Export\Declarations;
use Chill\AsideActivityBundle\Repository\AsideActivityRepository;
use Chill\AsideActivityBundle\Security\AsideActivityVoter;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Doctrine\ORM\Query;
use Symfony\Component\Form\FormBuilderInterface;
class SumConcernedPersonsCountAsideActivity implements ExportInterface, GroupedExportInterface
{
public function __construct(private readonly AsideActivityRepository $repository) {}
public function buildForm(FormBuilderInterface $builder) {}
public function getNormalizationVersion(): int
{
return 1;
}
public function normalizeFormData(array $formData): array
{
return [];
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return [];
}
public function getFormDefaultData(): array
{
return [];
}
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription(): string
{
return 'export.Sum concerned persons count for aside activities';
}
public function getGroup(): string
{
return 'export.Exports of aside activities';
}
public function getLabels($key, array $values, $data)
{
if ('export_sum_concerned_persons_count' !== $key) {
throw new \LogicException("the key {$key} is not used by this export");
}
$labels = array_combine($values, $values);
$labels['_header'] = $this->getTitle();
return static fn ($value) => $labels[$value];
}
public function getQueryKeys($data): array
{
return ['export_sum_concerned_persons_count'];
}
public function getResult($query, $data, \Chill\MainBundle\Export\ExportGenerationContext $context): array
{
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle(): string
{
return 'export.Sum concerned persons count for aside activities';
}
public function getType(): string
{
return Declarations::ASIDE_ACTIVITY_TYPE;
}
public function initiateQuery(array $requiredModifiers, array $acl, array $data, \Chill\MainBundle\Export\ExportGenerationContext $context): \Doctrine\ORM\QueryBuilder
{
$qb = $this->repository->createQueryBuilder('aside');
$qb->select('SUM(COALESCE(aside.concernedPersonsCount, 0)) as export_sum_concerned_persons_count');
return $qb;
}
public function requiredRole(): string
{
return AsideActivityVoter::STATS;
}
public function supportsModifiers(): array
{
return [
Declarations::ASIDE_ACTIVITY_TYPE,
];
}
}

View File

@@ -21,7 +21,6 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
@@ -30,13 +29,11 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
final class AsideActivityFormType extends AbstractType
{
private readonly array $timeChoices;
private readonly bool $showConcernedPersonsCount;
public function __construct(
ParameterBagInterface $parameterBag,
) {
$this->timeChoices = $parameterBag->get('chill_aside_activity.form.time_duration');
$this->showConcernedPersonsCount = $parameterBag->get('chill_aside_activity.show_concerned_persons_count');
}
public function buildForm(FormBuilderInterface $builder, array $options)
@@ -79,16 +76,6 @@ final class AsideActivityFormType extends AbstractType
->add('location', PickUserLocationType::class)
;
if ($this->showConcernedPersonsCount) {
$builder->add('concernedPersonsCount', IntegerType::class, [
'label' => 'Concerned persons count',
'required' => false,
'attr' => [
'min' => 0,
],
]);
}
foreach (['duration'] as $fieldName) {
$builder->get($fieldName)
->addModelTransformer($durationTimeTransformer);

View File

@@ -42,11 +42,6 @@
{%- if entity.location.name is defined -%}
<div><i class="fa fa-fw fa-map-marker"></i>{{ entity.location.name }}</div>
{%- endif -%}
{%- if entity.concernedPersonsCount > 0 -%}
<div><i class="fa fa-fw fa-user"></i>{{ entity.concernedPersonsCount }}</div>
{%- endif -%}
</div>
<div class="item-col" style="justify-content: flex-end;">
<div class="box">

View File

@@ -38,11 +38,6 @@
<dt class="inline">{{ 'Duration'|trans }}</dt>
<dd>{{ entity.duration|date('H:i') }}</dd>
{% if chill_aside_activity_config.show_concerned_persons_count == 'visible' %}
<dt class="inline">{{ 'Concerned persons count'|trans }}</dt>
<dd>{{ entity.concernedPersonsCount }}</dd>
{% endif %}
<dt class="inline">{{ 'Remark'|trans }}</dt>
{%- if entity.note is empty -%}
<dd>
@@ -60,6 +55,5 @@
</dl>
{% endblock %}
{% block content_view_actions_duplicate_link %}{% endblock %}
{% endembed %}
{% endblock %}

View File

@@ -1,49 +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\AsideActivityBundle\Tests\Export\Aggregator;
use Chill\AsideActivityBundle\Entity\AsideActivity;
use Chill\AsideActivityBundle\Export\Aggregator\ByConcernedPersonsCountAggregator;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
use Doctrine\ORM\EntityManagerInterface;
/**
* @internal
*
* @coversNothing
*/
class ByConcernedPersonsCountAggregatorTest extends AbstractAggregatorTest
{
public function getAggregator()
{
return new ByConcernedPersonsCountAggregator();
}
public static function getFormData(): array
{
return [
[],
];
}
public static function getQueryBuilders(): iterable
{
self::bootKernel();
$em = self::getContainer()->get(EntityManagerInterface::class);
return [
$em->createQueryBuilder()
->select('count(aside.id)')
->from(AsideActivity::class, 'aside'),
];
}
}

View File

@@ -1,50 +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\AsideActivityBundle\Tests\Export\Export;
use Chill\AsideActivityBundle\Export\Export\SumConcernedPersonsCountAsideActivity;
use Chill\AsideActivityBundle\Repository\AsideActivityRepository;
use Chill\MainBundle\Test\Export\AbstractExportTest;
/**
* @internal
*
* @coversNothing
*/
final class SumConcernedPersonsCountAsideActivityTest extends AbstractExportTest
{
protected function setUp(): void
{
self::bootKernel();
}
public function getExport()
{
$repository = self::getContainer()->get(AsideActivityRepository::class);
yield new SumConcernedPersonsCountAsideActivity($repository);
}
public static function getFormData(): array
{
return [
[],
];
}
public static function getModifiersCombination(): array
{
return [
['aside_activity'],
];
}
}

View File

@@ -20,10 +20,6 @@ services:
tags:
- { name: chill.export, alias: 'avg_aside_activity_duration' }
Chill\AsideActivityBundle\Export\Export\SumConcernedPersonsCountAsideActivity:
tags:
- { name: chill.export, alias: 'sum_aside_activity_concerned_persons_count' }
## Filters
chill.aside_activity.export.date_filter:
class: Chill\AsideActivityBundle\Export\Filter\ByDateFilter
@@ -74,7 +70,3 @@ services:
Chill\AsideActivityBundle\Export\Aggregator\ByLocationAggregator:
tags:
- { name: chill.export_aggregator, alias: 'aside_activity_location_aggregator' }
Chill\AsideActivityBundle\Export\Aggregator\ByConcernedPersonsCountAggregator:
tags:
- { name: chill.export_aggregator, alias: 'aside_activity_concerned_persons_count_aggregator' }

View File

@@ -27,7 +27,6 @@ Emergency: Urgent
by: "Par "
location: Lieu
Asideactivity location: Localisation de l'activité
Concerned persons count: Nombre d'usager concernés
# Crud
crud:
@@ -178,7 +177,7 @@ export:
agent_id: Utilisateur
creator_id: Créateur
main_scope: Service principal de l'utilisateur
main_center: Territoire principal de l'utilisateur
main_center: Centre principal de l'utilisateur
aside_activity_type: Catégorie d'activité annexe
date: Date
duration: Durée
@@ -191,7 +190,6 @@ export:
Count aside activities by various parameters.: Compte le nombre d'activités annexes selon divers critères
Average aside activities duration: Durée moyenne des activités annexes
Sum aside activities duration: Durée des activités annexes
Sum concerned persons count for aside activities: Nombre d'usager concernés par les activités annexes
filter:
Filter by aside activity date: Filtrer les activités annexes par date
Filter by aside activity type: Filtrer les activités annexes par type d'activité
@@ -212,8 +210,6 @@ export:
'Filtered by aside activity location: only %location%': "Filtré par localisation: uniquement %location%"
aggregator:
Group by aside activity type: Grouper les activités annexes par type d'activité
Group by concerned persons count: Grouper les activités annexes par nombre d'usagers conernés
Concerned persons count: Nombre d'usagers concernés
Aside activity type: Type d'activité annexe
by_user_job:
Aggregate by user job: Grouper les activités annexes par métier des utilisateurs

View File

@@ -165,60 +165,3 @@ Phonecall: "Telefoon oproep"
Aside activities: Nevenactiviteiten
Aside activity types: Types nevenactiviteiten
Aside activity type configuration: Configuratie categorieën nevenactiviteiten
# exports
export:
aside_activity:
List of aside activities: Lijst van nevenactiviteiten
createdAt: Aanmaak
updatedAt: Laatste update
agent_id: Gebruiker
creator_id: Aanmaker
main_scope: Hoofddienst van de gebruiker
main_center: Hoofdterritorium van de gebruiker
aside_activity_type: Categorie nevenactiviteit
date: Datum
duration: Duur
note: Notitie
id: Identificatie
location: Locatie
Exports of aside activities: Exports van nevenactiviteiten
Count aside activities: Aantal nevenactiviteiten
Count aside activities by various parameters.: Telt het aantal nevenactiviteiten volgens diverse criteria
Average aside activities duration: Gemiddelde duur van nevenactiviteiten
Sum aside activities duration: Duur van nevenactiviteiten
Sum concerned persons count for aside activities: Aantal betrokken gebruikers bij nevenactiviteiten
filter:
Filter by aside activity date: Nevenactiviteiten filteren op datum
Filter by aside activity type: Nevenactiviteiten filteren op type activiteit
'Filtered by aside activity type: only %type%': "Gefilterd op type nevenactiviteit: alleen %type%"
Filtered by aside activities between %dateFrom% and %dateTo%: Gefilterd op datum van nevenactiviteit, tussen %dateFrom% en %dateTo%
This date should be after the date given in "Implied in an aside activity after this date" field: Deze datum moet later zijn dan de datum in het veld "nevenactiviteiten na deze datum"
Aside activities after this date: Nevenactiviteiten na deze datum
Aside activities before this date: Nevenactiviteiten vóór deze datum
'Filtered aside activity by user: only %users%': "Gefilterd op gebruiker: alleen %users%"
Filter aside activity by user: Filteren op gebruiker
by_user_job:
'Filtered aside activities by user jobs: only %jobs%': "Gefilterd op beroep van gebruikers: alleen %jobs%"
Filter by user jobs: Nevenactiviteiten filteren op beroep van gebruikers
by_user_scope:
'Filtered aside activities by user scope: only %scopes%': "Gefilterd op dienst van gebruikers: alleen %scopes%"
Filter by user scope: Nevenactiviteiten filteren op dienst van gebruiker
Filter by aside activity location: Nevenactiviteiten filteren op locatie
'Filtered by aside activity location: only %location%': "Gefilterd op locatie: alleen %location%"
aggregator:
Group by aside activity type: Nevenactiviteiten groeperen op type activiteit
Group by concerned persons count: Nevenactiviteiten groeperen op aantal betrokken gebruikers
Concerned persons count: Aantal betrokken gebruikers
Aside activity type: Type nevenactiviteit
by_user_job:
Aggregate by user job: Nevenactiviteiten groeperen op beroep van gebruikers
by_user_scope:
Aggregate by user scope: Nevenactiviteiten groeperen op dienst van gebruikers
Aside activity location: Locatie van nevenactiviteiten
Group by aside activity location: Nevenactiviteiten groeperen op locatie
Aside activity localisation: Locatie
# ROLES
CHILL_ASIDE_ACTIVITY_STATS: Statistieken voor nevenactiviteiten

View File

@@ -74,42 +74,3 @@ The balance: Verschil tussen inkomsten en onkosten
Valid since %startDate% until %endDate%: Geldig sinds %startDate% tot %endDate%
Valid since %startDate%: Geldig sinds %startDate%
budget:
admin:
form:
Charge_kind_key: Identificatiesleutel
Resource_kind_key: Identificatiesleutel
This kind must contains only alphabeticals characters, and dashes. This string is in use during document generation. Changes may have side effect on document: Deze sleutel dient om het type last of inkomen te identificeren bij het genereren van documenten. Alleen alfanumerieke tekens zijn toegestaan. Het wijzigen van deze sleutel kan een effect hebben bij het genereren van nieuwe documenten.
# ROLES
Budget elements: Budget
CHILL_BUDGET_ELEMENT_CREATE: Inkomsten/last aanmaken
CHILL_BUDGET_ELEMENT_DELETE: Inkomsten/last verwijderen
CHILL_BUDGET_ELEMENT_SEE: Inkomstenen/lasten bekijken
CHILL_BUDGET_ELEMENT_UPDATE: Inkomsten/last bewerken
## admin
crud:
resource_kind:
title_new: Nieuw type inkomsten
title_edit: Type inkomsten bewerken
charge_kind:
title_new: Nieuw type last
title_edit: Type last bewerken
admin:
menu:
Resource types: Types inkomsten
Charge types: Types last
title:
Charge Type List: Lijst van types last
Resource Type List: Lijst van types inkomsten
Budget configuration: Configuratie van budgetelementen
new:
Create a new charge type: Nieuw type last aanmaken
Create a new resource type: Nieuw type inkomsten aanmaken
form:
Choose the type of resource: Kies een type inkomsten
Choose the type of charge: Kies een type last

View File

@@ -1,8 +1,2 @@
The amount cannot be empty: Het bedrag mag niet nul of leeg zijn
The budget element's end date must be after the start date: De einddatum moet later vallen dan de begindatum
budget:
admin:
form:
kind:
enkel_alphanumeriek
The amount cannot be empty: Le montant ne peut pas être vide ou égal à zéro
The budget element's end date must be after the start date: La date de fin doit être après la date de début

View File

@@ -12,7 +12,6 @@ declare(strict_types=1);
namespace Chill\CalendarBundle\Controller;
use Chill\CalendarBundle\Repository\CalendarRepository;
use Chill\CalendarBundle\Repository\InviteRepository;
use Chill\MainBundle\CRUD\Controller\ApiController;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Serializer\Model\Collection;
@@ -24,10 +23,7 @@ use Symfony\Component\Routing\Annotation\Route;
class CalendarAPIController extends ApiController
{
public function __construct(
private readonly CalendarRepository $calendarRepository,
private readonly InviteRepository $inviteRepository,
) {}
public function __construct(private readonly CalendarRepository $calendarRepository) {}
#[Route(path: '/api/1.0/calendar/calendar/by-user/{id}.{_format}', name: 'chill_api_single_calendar_list_by-user', requirements: ['_format' => 'json'])]
public function listByUser(User $user, Request $request, string $_format): JsonResponse
@@ -56,37 +52,16 @@ class CalendarAPIController extends ApiController
throw new BadRequestHttpException('dateTo not parsable');
}
// Get calendar items where user is the main user
$ownCalendars = $this->calendarRepository->findByUser(
$total = $this->calendarRepository->countByUser($user, $dateFrom, $dateTo);
$paginator = $this->getPaginatorFactory()->create($total);
$ranges = $this->calendarRepository->findByUser(
$user,
$dateFrom,
$dateTo
$dateTo,
$paginator->getItemsPerPage(),
$paginator->getCurrentPageFirstItemNumber()
);
// Get calendar items from accepted invites
$acceptedInvites = $this->inviteRepository->findAcceptedInvitesByUserAndDateRange($user, $dateFrom, $dateTo);
$inviteCalendars = array_map(fn ($invite) => $invite->getCalendar(), $acceptedInvites);
// Merge
$allCalendars = array_merge($ownCalendars, $inviteCalendars);
$uniqueCalendars = [];
$seenIds = [];
foreach ($allCalendars as $calendar) {
$id = $calendar->getId();
if (!in_array($id, $seenIds, true)) {
$seenIds[] = $id;
$uniqueCalendars[] = $calendar;
}
}
$total = count($uniqueCalendars);
$paginator = $this->getPaginatorFactory()->create($total);
$offset = $paginator->getCurrentPageFirstItemNumber();
$limit = $paginator->getItemsPerPage();
$ranges = array_slice($uniqueCalendars, $offset, $limit);
$collection = new Collection($ranges, $paginator);
return $this->json($collection, Response::HTTP_OK, [], ['groups' => ['calendar:light']]);

View File

@@ -13,7 +13,6 @@ namespace Chill\CalendarBundle\Controller;
use Chill\CalendarBundle\Entity\Calendar;
use Chill\CalendarBundle\Form\CalendarType;
use Chill\CalendarBundle\Form\CancelType;
use Chill\CalendarBundle\RemoteCalendar\Connector\RemoteCalendarConnectorInterface;
use Chill\CalendarBundle\Repository\CalendarACLAwareRepositoryInterface;
use Chill\CalendarBundle\Security\Voter\CalendarVoter;
@@ -31,7 +30,6 @@ use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Doctrine\ORM\EntityManagerInterface;
use http\Exception\UnexpectedValueException;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@@ -62,7 +60,6 @@ class CalendarController extends AbstractController
private readonly UserRepositoryInterface $userRepository,
private readonly TranslatorInterface $translator,
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry,
private readonly EntityManagerInterface $em,
) {}
/**
@@ -114,55 +111,6 @@ class CalendarController extends AbstractController
]);
}
#[Route(path: '/{_locale}/calendar/calendar/{id}/cancel', name: 'chill_calendar_calendar_cancel')]
public function cancelAction(Calendar $calendar, Request $request): Response
{
// Deal with sms being sent or not
// Communicate cancellation with the remote calendar.
$this->denyAccessUnlessGranted(CalendarVoter::EDIT, $calendar);
[$person, $accompanyingPeriod] = [$calendar->getPerson(), $calendar->getAccompanyingPeriod()];
$form = $this->createForm(CancelType::class, $calendar);
$form->add('submit', SubmitType::class);
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = '@ChillCalendar/Calendar/cancelCalendarByAccompanyingCourse.html.twig';
$redirectRoute = $this->generateUrl('chill_calendar_calendar_list_by_period', ['id' => $accompanyingPeriod->getId()]);
} elseif ($person instanceof Person) {
$view = '@ChillCalendar/Calendar/cancelCalendarByPerson.html.twig';
$redirectRoute = $this->generateUrl('chill_calendar_calendar_list_by_person', ['id' => $person->getId()]);
} else {
throw new \RuntimeException('nor person or accompanying period');
}
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->logger->notice('A calendar event has been cancelled', [
'by_user' => $this->getUser()->getUsername(),
'calendar_id' => $calendar->getId(),
]);
$calendar->setStatus($calendar::STATUS_CANCELED);
$calendar->setSmsStatus($calendar::SMS_CANCEL_PENDING);
$this->em->flush();
$this->addFlash('success', $this->translator->trans('chill_calendar.calendar_canceled'));
return new RedirectResponse($redirectRoute);
}
return $this->render($view, [
'calendar' => $calendar,
'form' => $form->createView(),
'accompanyingCourse' => $accompanyingPeriod,
'person' => $person,
]);
}
/**
* Edit a calendar item.
*/
@@ -318,7 +266,7 @@ class CalendarController extends AbstractController
}
if (!$this->getUser() instanceof User) {
throw new UnauthorizedHttpException('you are not a user');
throw new UnauthorizedHttpException('you are not an user');
}
$view = '@ChillCalendar/Calendar/listByUser.html.twig';

View File

@@ -1,58 +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\CalendarBundle\Controller;
use Chill\CalendarBundle\Entity\Calendar;
use Chill\CalendarBundle\Repository\InviteRepository;
use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepositoryInterface;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Symfony\Component\Routing\Annotation\Route;
class MyInvitationsController extends AbstractController
{
public function __construct(private readonly InviteRepository $inviteRepository, private readonly PaginatorFactory $paginator, private readonly DocGeneratorTemplateRepositoryInterface $docGeneratorTemplateRepository) {}
#[Route(path: '/{_locale}/calendar/invitations/my', name: 'chill_calendar_invitations_list_my')]
public function myInvitations(Request $request): Response
{
$this->denyAccessUnlessGranted('ROLE_USER');
$user = $this->getUser();
if (!$user instanceof User) {
throw new UnauthorizedHttpException('you are not a user');
}
$total = count($this->inviteRepository->findBy(['user' => $user]));
$paginator = $this->paginator->create($total);
$invitations = $this->inviteRepository->findBy(
['user' => $user],
['createdAt' => 'DESC'],
$paginator->getItemsPerPage(),
$paginator->getCurrentPageFirstItemNumber()
);
$view = '@ChillCalendar/Invitations/listByUser.html.twig';
return $this->render($view, [
'invitations' => $invitations,
'paginator' => $paginator,
'templates' => $this->docGeneratorTemplateRepository->findByEntity(Calendar::class),
]);
}
}

View File

@@ -35,7 +35,7 @@ class LoadCancelReason extends Fixture implements FixtureGroupInterface
$arr = [
['name' => CancelReason::CANCELEDBY_USER],
['name' => CancelReason::CANCELEDBY_PERSON],
['name' => CancelReason::CANCELEDBY_OTHER],
['name' => CancelReason::CANCELEDBY_DONOTCOUNT],
];
foreach ($arr as $a) {

View File

@@ -269,11 +269,6 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
return $this->cancelReason;
}
public function isCanceled(): bool
{
return null !== $this->cancelReason;
}
public function getCenters(): ?iterable
{
return match ($this->getContext()) {

View File

@@ -18,14 +18,14 @@ use Doctrine\ORM\Mapping as ORM;
#[ORM\Table(name: 'chill_calendar.cancel_reason')]
class CancelReason
{
final public const CANCELEDBY_OTHER = 'CANCELEDBY_OTHER';
final public const CANCELEDBY_DONOTCOUNT = 'CANCELEDBY_DONOTCOUNT';
final public const CANCELEDBY_PERSON = 'CANCELEDBY_PERSON';
final public const CANCELEDBY_USER = 'CANCELEDBY_USER';
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::BOOLEAN, options: ['default' => true])]
private bool $active = true;
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::BOOLEAN)]
private ?bool $active = null;
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 255)]
private ?string $canceledBy = null;

View File

@@ -15,7 +15,7 @@ use Chill\CalendarBundle\Entity\CancelReason;
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
@@ -28,14 +28,7 @@ class CancelReasonType extends AbstractType
->add('active', CheckboxType::class, [
'required' => false,
])
->add('canceledBy', ChoiceType::class, [
'choices' => [
'chill_calendar.canceled_by.user' => CancelReason::CANCELEDBY_USER,
'chill_calendar.canceled_by.person' => CancelReason::CANCELEDBY_PERSON,
'chill_calendar.canceled_by.other' => CancelReason::CANCELEDBY_OTHER,
],
'required' => true,
]);
->add('canceledBy', TextType::class);
}
public function configureOptions(OptionsResolver $resolver)

View File

@@ -1,42 +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\CalendarBundle\Form;
use Chill\CalendarBundle\Entity\Calendar;
use Chill\CalendarBundle\Entity\CancelReason;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class CancelType extends AbstractType
{
public function __construct(private readonly TranslatableStringHelperInterface $translatableStringHelper) {}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('cancelReason', EntityType::class, [
'class' => CancelReason::class,
'required' => true,
'choice_label' => fn (CancelReason $cancelReason) => $this->translatableStringHelper->localize($cancelReason->getName()),
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Calendar::class,
]);
}
}

View File

@@ -25,13 +25,6 @@ class UserMenuBuilder implements LocalMenuBuilderInterface
if ($this->security->isGranted('ROLE_USER')) {
$menu->addChild('My calendar list', [
'route' => 'chill_calendar_calendar_list_my',
])
->setExtras([
'order' => 8,
'icon' => 'tasks',
]);
$menu->addChild('invite.list.title', [
'route' => 'chill_calendar_invitations_list_my',
])
->setExtras([
'order' => 9,

View File

@@ -21,7 +21,6 @@ namespace Chill\CalendarBundle\Messenger\Doctrine;
use Chill\CalendarBundle\Entity\Calendar;
use Chill\CalendarBundle\Messenger\Message\CalendarMessage;
use Chill\CalendarBundle\Messenger\Message\CalendarRemovedMessage;
use Chill\MainBundle\Entity\User;
use Doctrine\ORM\Event\PostPersistEventArgs;
use Doctrine\ORM\Event\PostRemoveEventArgs;
use Doctrine\ORM\Event\PostUpdateEventArgs;
@@ -32,17 +31,6 @@ class CalendarEntityListener
{
public function __construct(private readonly MessageBusInterface $messageBus, private readonly Security $security) {}
private function getAuthenticatedUser(): User
{
$user = $this->security->getUser();
if (!$user instanceof User) {
throw new \LogicException('Expected an instance of User.');
}
return $user;
}
public function postPersist(Calendar $calendar, PostPersistEventArgs $args): void
{
if (!$calendar->preventEnqueueChanges) {
@@ -50,7 +38,7 @@ class CalendarEntityListener
new CalendarMessage(
$calendar,
CalendarMessage::CALENDAR_PERSIST,
$this->getAuthenticatedUser()
$this->security->getUser()
)
);
}
@@ -62,7 +50,7 @@ class CalendarEntityListener
$this->messageBus->dispatch(
new CalendarRemovedMessage(
$calendar,
$this->getAuthenticatedUser()
$this->security->getUser()
)
);
}
@@ -70,19 +58,12 @@ class CalendarEntityListener
public function postUpdate(Calendar $calendar, PostUpdateEventArgs $args): void
{
if ($calendar->getStatus() === $calendar::STATUS_CANCELED) {
$this->messageBus->dispatch(
new CalendarRemovedMessage(
$calendar,
$this->getAuthenticatedUser()
)
);
} elseif (!$calendar->preventEnqueueChanges) {
if (!$calendar->preventEnqueueChanges) {
$this->messageBus->dispatch(
new CalendarMessage(
$calendar,
CalendarMessage::CALENDAR_UPDATE,
$this->getAuthenticatedUser()
$this->security->getUser()
)
);
}

View File

@@ -70,8 +70,6 @@ class CalendarRemovedMessage
public function getRemoteId(): string
{
dump($this->remoteId);
return $this->remoteId;
}
}

View File

@@ -191,7 +191,6 @@ class CalendarRepository implements ObjectRepository
$qb->expr()->eq('c.mainUser', ':user'),
$qb->expr()->gte('c.startDate', ':startDate'),
$qb->expr()->lte('c.endDate', ':endDate'),
$qb->expr()->isNull('c.cancelReason'),
)
)
->setParameters([

View File

@@ -12,7 +12,6 @@ declare(strict_types=1);
namespace Chill\CalendarBundle\Repository;
use Chill\CalendarBundle\Entity\Invite;
use Chill\MainBundle\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository;
@@ -42,7 +41,7 @@ class InviteRepository implements ObjectRepository
/**
* @return array|Invite[]
*/
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null)
{
return $this->entityRepository->findBy($criteria, $orderBy, $limit, $offset);
}
@@ -52,52 +51,6 @@ class InviteRepository implements ObjectRepository
return $this->entityRepository->findOneBy($criteria);
}
/**
* Find accepted invites for a user within a date range.
*
* @return array|Invite[]
*/
public function findAcceptedInvitesByUserAndDateRange(User $user, \DateTimeImmutable $from, \DateTimeImmutable $to): array
{
return $this->buildAcceptedInviteByUserAndDateRangeQuery($user, $from, $to)
->getQuery()
->getResult();
}
/**
* Count accepted invites for a user within a date range.
*/
public function countAcceptedInvitesByUserAndDateRange(User $user, \DateTimeImmutable $from, \DateTimeImmutable $to): int
{
return $this->buildAcceptedInviteByUserAndDateRangeQuery($user, $from, $to)
->select('COUNT(c)')
->getQuery()
->getSingleScalarResult();
}
public function buildAcceptedInviteByUserAndDateRangeQuery(User $user, \DateTimeImmutable $from, \DateTimeImmutable $to)
{
$qb = $this->entityRepository->createQueryBuilder('i');
return $qb
->join('i.calendar', 'c')
->where(
$qb->expr()->andX(
$qb->expr()->eq('i.user', ':user'),
$qb->expr()->eq('i.status', ':status'),
$qb->expr()->gte('c.startDate', ':startDate'),
$qb->expr()->lte('c.endDate', ':endDate'),
$qb->expr()->isNull('c.cancelReason')
)
)
->setParameters([
'user' => $user,
'status' => Invite::ACCEPTED,
'startDate' => $from,
'endDate' => $to,
]);
}
public function getClassName(): string
{
return Invite::class;

View File

@@ -1,6 +1,5 @@
services:
Chill\CalendarBundle\Controller\:
autowire: true
autoconfigure: true
resource: '../../../Controller'
tags: ['controller.service_arguments']

View File

@@ -1,76 +1,74 @@
import { EventInput } from "@fullcalendar/core";
import {
DateTime,
Location,
User,
UserAssociatedInterface,
DateTime,
Location,
User,
UserAssociatedInterface,
} from "../../../ChillMainBundle/Resources/public/types";
import { Person } from "../../../ChillPersonBundle/Resources/public/types";
export interface CalendarRange {
id: number;
endDate: DateTime;
startDate: DateTime;
user: User;
location: Location;
createdAt: DateTime;
createdBy: User;
updatedAt: DateTime;
updatedBy: User;
id: number;
endDate: DateTime;
startDate: DateTime;
user: User;
location: Location;
createdAt: DateTime;
createdBy: User;
updatedAt: DateTime;
updatedBy: User;
}
export interface CalendarRangeCreate {
user: UserAssociatedInterface;
startDate: DateTime;
endDate: DateTime;
location: Location;
user: UserAssociatedInterface;
startDate: DateTime;
endDate: DateTime;
location: Location;
}
export interface CalendarRangeEdit {
startDate?: DateTime;
endDate?: DateTime;
location?: Location;
startDate?: DateTime;
endDate?: DateTime;
location?: Location;
}
export interface Calendar {
id: number;
id: number;
}
export interface CalendarLight {
id: number;
endDate: DateTime;
startDate: DateTime;
mainUser: User;
persons: Person[];
status: "valid" | "moved" | "canceled";
id: number;
endDate: DateTime;
startDate: DateTime;
mainUser: User;
persons: Person[];
status: "valid" | "moved" | "canceled";
}
export interface CalendarRemote {
id: number;
endDate: DateTime;
startDate: DateTime;
title: string;
isAllDay: boolean;
id: number;
endDate: DateTime;
startDate: DateTime;
title: string;
isAllDay: boolean;
}
export type EventInputCalendarRange = EventInput & {
id: string;
userId: number;
userLabel: string;
calendarRangeId: number;
locationId: number;
locationName: string;
start: string;
end: string;
is: "range";
id: string;
userId: number;
userLabel: string;
calendarRangeId: number;
locationId: number;
locationName: string;
start: string;
end: string;
is: "range";
};
export function isEventInputCalendarRange(
toBeDetermined: EventInputCalendarRange | EventInput,
toBeDetermined: EventInputCalendarRange | EventInput,
): toBeDetermined is EventInputCalendarRange {
return (
typeof toBeDetermined.is === "string" && toBeDetermined.is === "range"
);
return typeof toBeDetermined.is === "string" && toBeDetermined.is === "range";
}
export {};

View File

@@ -1,166 +1,146 @@
<template>
<teleport to="#mainUser">
<h2 class="chill-red">Utilisateur principal</h2>
<div>
<div>
<div v-if="null !== this.$store.getters.getMainUser">
<calendar-active :user="this.$store.getters.getMainUser" />
</div>
<pick-entity
:multiple="false"
:types="['user']"
:uniqid="'main_user_calendar'"
:picked="
null !== this.$store.getters.getMainUser
? [this.$store.getters.getMainUser]
: []
"
:removable-if-set="false"
:display-picked="false"
:suggested="this.suggestedUsers"
:label="'main_user'"
@add-new-entity="setMainUser"
/>
</div>
<teleport to="#mainUser">
<h2 class="chill-red">Utilisateur principal</h2>
<div>
<div>
<div v-if="null !== this.$store.getters.getMainUser">
<calendar-active :user="this.$store.getters.getMainUser" />
</div>
</teleport>
<pick-entity
:multiple="false"
:types="['user']"
:uniqid="'main_user_calendar'"
:picked="
null !== this.$store.getters.getMainUser
? [this.$store.getters.getMainUser]
: []
"
:removable-if-set="false"
:display-picked="false"
:suggested="this.suggestedUsers"
:label="'main_user'"
@add-new-entity="setMainUser"
/>
</div>
</div>
</teleport>
<concerned-groups />
<concerned-groups />
<teleport to="#schedule">
<div class="row mb-3" v-if="activity.startDate !== null">
<label class="col-form-label col-sm-4">Date</label>
<div class="col-sm-8">
{{ $d(activity.startDate, "long") }} -
{{ $d(activity.endDate, "hoursOnly") }}
<span v-if="activity.calendarRange === null"
>(Pas de plage de disponibilité sélectionnée)</span
>
<span v-else>(Une plage de disponibilité sélectionnée)</span>
</div>
</div>
</teleport>
<location />
<teleport to="#fullCalendar">
<div class="calendar-actives">
<template v-for="u in getActiveUsers" :key="u.id">
<calendar-active
:user="u"
:invite="this.$store.getters.getInviteForUser(u)"
/>
</template>
</div>
<div
class="display-options row justify-content-between"
style="margin-top: 1rem"
<teleport to="#schedule">
<div class="row mb-3" v-if="activity.startDate !== null">
<label class="col-form-label col-sm-4">Date</label>
<div class="col-sm-8">
{{ $d(activity.startDate, "long") }} -
{{ $d(activity.endDate, "hoursOnly") }}
<span v-if="activity.calendarRange === null"
>(Pas de plage de disponibilité sélectionnée)</span
>
<div class="col-sm-9 col-xs-12">
<div class="input-group mb-3">
<label class="input-group-text" for="slotDuration"
>Durée des créneaux</label
>
<select
v-model="slotDuration"
id="slotDuration"
class="form-select"
>
<option value="00:05:00">5 minutes</option>
<option value="00:10:00">10 minutes</option>
<option value="00:15:00">15 minutes</option>
<option value="00:30:00">30 minutes</option>
<option value="00:45:00">45 minutes</option>
<option value="00:60:00">60 minutes</option>
</select>
<label class="input-group-text" for="slotMinTime">De</label>
<select
v-model="slotMinTime"
id="slotMinTime"
class="form-select"
>
<option value="00:00:00">0h</option>
<option value="01:00:00">1h</option>
<option value="02:00:00">2h</option>
<option value="03:00:00">3h</option>
<option value="04:00:00">4h</option>
<option value="05:00:00">5h</option>
<option value="06:00:00">6h</option>
<option value="07:00:00">7h</option>
<option value="08:00:00">8h</option>
<option value="09:00:00">9h</option>
<option value="10:00:00">10h</option>
<option value="11:00:00">11h</option>
<option value="12:00:00">12h</option>
</select>
<label class="input-group-text" for="slotMaxTime">À</label>
<select
v-model="slotMaxTime"
id="slotMaxTime"
class="form-select"
>
<option value="12:00:00">12h</option>
<option value="13:00:00">13h</option>
<option value="14:00:00">14h</option>
<option value="15:00:00">15h</option>
<option value="16:00:00">16h</option>
<option value="17:00:00">17h</option>
<option value="18:00:00">18h</option>
<option value="19:00:00">19h</option>
<option value="20:00:00">20h</option>
<option value="21:00:00">21h</option>
<option value="22:00:00">22h</option>
<option value="23:00:00">23h</option>
<option value="23:59:59">24h</option>
</select>
</div>
</div>
<div class="col-sm-3 col-xs-12">
<div class="float-end">
<div class="form-check input-group">
<span class="input-group-text">
<input
id="showHideWE"
class="mt-0"
type="checkbox"
v-model="hideWeekends"
/>
</span>
<label
for="showHideWE"
class="form-check-label input-group-text"
>Week-ends</label
>
</div>
</div>
</div>
<span v-else>(Une plage de disponibilité sélectionnée)</span>
</div>
</div>
</teleport>
<location />
<teleport to="#fullCalendar">
<div class="calendar-actives">
<template v-for="u in getActiveUsers" :key="u.id">
<calendar-active
:user="u"
:invite="this.$store.getters.getInviteForUser(u)"
/>
</template>
</div>
<div
class="display-options row justify-content-between"
style="margin-top: 1rem"
>
<div class="col-sm-9 col-xs-12">
<div class="input-group mb-3">
<label class="input-group-text" for="slotDuration"
>Durée des créneaux</label
>
<select v-model="slotDuration" id="slotDuration" class="form-select">
<option value="00:05:00">5 minutes</option>
<option value="00:10:00">10 minutes</option>
<option value="00:15:00">15 minutes</option>
<option value="00:30:00">30 minutes</option>
</select>
<label class="input-group-text" for="slotMinTime">De</label>
<select v-model="slotMinTime" id="slotMinTime" class="form-select">
<option value="00:00:00">0h</option>
<option value="01:00:00">1h</option>
<option value="02:00:00">2h</option>
<option value="03:00:00">3h</option>
<option value="04:00:00">4h</option>
<option value="05:00:00">5h</option>
<option value="06:00:00">6h</option>
<option value="07:00:00">7h</option>
<option value="08:00:00">8h</option>
<option value="09:00:00">9h</option>
<option value="10:00:00">10h</option>
<option value="11:00:00">11h</option>
<option value="12:00:00">12h</option>
</select>
<label class="input-group-text" for="slotMaxTime">À</label>
<select v-model="slotMaxTime" id="slotMaxTime" class="form-select">
<option value="12:00:00">12h</option>
<option value="13:00:00">13h</option>
<option value="14:00:00">14h</option>
<option value="15:00:00">15h</option>
<option value="16:00:00">16h</option>
<option value="17:00:00">17h</option>
<option value="18:00:00">18h</option>
<option value="19:00:00">19h</option>
<option value="20:00:00">20h</option>
<option value="21:00:00">21h</option>
<option value="22:00:00">22h</option>
<option value="23:00:00">23h</option>
<option value="23:59:59">24h</option>
</select>
</div>
<FullCalendar ref="fullCalendar" :options="calendarOptions">
<template #eventContent="arg">
<span>
<b v-if="arg.event.extendedProps.is === 'remote'">{{
arg.event.title
}}</b>
<b v-else-if="arg.event.extendedProps.is === 'range'"
>{{ arg.timeText }}
{{ arg.event.extendedProps.locationName }}
<small>{{
arg.event.extendedProps.userLabel
}}</small></b
>
<b v-else-if="arg.event.extendedProps.is === 'current'"
>{{ arg.timeText }} {{ $t("current_selected") }}
</b>
<b v-else-if="arg.event.extendedProps.is === 'local'">{{
arg.event.title
}}</b>
<b v-else
>{{ arg.timeText }} {{ $t("current_selected") }}
</b>
</span>
</template>
</FullCalendar>
</teleport>
</div>
<div class="col-sm-3 col-xs-12">
<div class="float-end">
<div class="form-check input-group">
<span class="input-group-text">
<input
id="showHideWE"
class="mt-0"
type="checkbox"
v-model="hideWeekends"
/>
</span>
<label for="showHideWE" class="form-check-label input-group-text"
>Week-ends</label
>
</div>
</div>
</div>
</div>
<FullCalendar ref="fullCalendar" :options="calendarOptions">
<template #eventContent="arg">
<span>
<b v-if="arg.event.extendedProps.is === 'remote'">{{
arg.event.title
}}</b>
<b v-else-if="arg.event.extendedProps.is === 'range'"
>{{ arg.timeText }}
{{ arg.event.extendedProps.locationName }}
<small>{{ arg.event.extendedProps.userLabel }}</small></b
>
<b v-else-if="arg.event.extendedProps.is === 'current'"
>{{ arg.timeText }} {{ $t("current_selected") }}
</b>
<b v-else-if="arg.event.extendedProps.is === 'local'">{{
arg.event.title
}}</b>
<b v-else>{{ arg.timeText }} {{ $t("current_selected") }} </b>
</span>
</template>
</FullCalendar>
</teleport>
</template>
<script>
@@ -177,219 +157,210 @@ import PickEntity from "ChillMainAssets/vuejs/PickEntity/PickEntity.vue";
import { mapGetters, mapState } from "vuex";
export default {
name: "App",
components: {
ConcernedGroups,
Location,
FullCalendar,
CalendarActive,
PickEntity,
name: "App",
components: {
ConcernedGroups,
Location,
FullCalendar,
CalendarActive,
PickEntity,
},
data() {
return {
errorMsg: [],
showMyCalendar: false,
slotDuration: "00:05:00",
slotMinTime: "09:00:00",
slotMaxTime: "18:00:00",
hideWeekEnds: true,
previousUser: [],
};
},
computed: {
...mapGetters(["getMainUser"]),
...mapState(["activity"]),
events() {
return this.$store.getters.getEventSources;
},
data() {
return {
errorMsg: [],
showMyCalendar: false,
slotDuration: "00:05:00",
slotMinTime: "09:00:00",
slotMaxTime: "18:00:00",
hideWeekEnds: true,
previousUser: [],
};
calendarOptions() {
return {
locale: frLocale,
plugins: [
dayGridPlugin,
interactionPlugin,
timeGridPlugin,
dayGridPlugin,
listPlugin,
],
initialView: "timeGridWeek",
initialDate: this.$store.getters.getInitialDate,
eventSources: this.events,
selectable: true,
slotMinTime: this.slotMinTime,
slotMaxTime: this.slotMaxTime,
scrollTimeReset: false,
datesSet: this.onDatesSet,
select: this.onDateSelect,
eventChange: this.onEventChange,
eventClick: this.onEventClick,
selectMirror: true,
editable: true,
weekends: !this.hideWeekEnds,
headerToolbar: {
left: "prev,next today",
center: "title",
right: "timeGridWeek,timeGridDay,listWeek",
},
views: {
timeGrid: {
slotEventOverlap: false,
slotDuration: this.slotDuration,
},
},
};
},
computed: {
...mapGetters(["getMainUser"]),
...mapState(["activity"]),
events() {
return this.$store.getters.getEventSources;
},
calendarOptions() {
return {
locale: frLocale,
plugins: [
dayGridPlugin,
interactionPlugin,
timeGridPlugin,
dayGridPlugin,
listPlugin,
],
initialView: "timeGridWeek",
initialDate: this.$store.getters.getInitialDate,
eventSources: this.events,
selectable: true,
slotMinTime: this.slotMinTime,
slotMaxTime: this.slotMaxTime,
scrollTimeReset: false,
datesSet: this.onDatesSet,
select: this.onDateSelect,
eventChange: this.onEventChange,
eventClick: this.onEventClick,
selectMirror: true,
editable: true,
weekends: !this.hideWeekEnds,
headerToolbar: {
left: "prev,next today",
center: "title",
right: "timeGridWeek,timeGridDay,listWeek",
},
views: {
timeGrid: {
slotEventOverlap: false,
slotDuration: this.slotDuration,
},
},
};
},
getActiveUsers() {
const users = [];
for (const id of this.$store.state.currentView.users.keys()) {
users.push(this.$store.getters.getUserDataById(id).user);
}
return users;
},
suggestedUsers() {
const suggested = [];
this.$data.previousUser.forEach((u) => {
if (u.id !== this.$store.getters.getMainUser.id) {
suggested.push(u);
}
});
return suggested;
},
getActiveUsers() {
const users = [];
for (const id of this.$store.state.currentView.users.keys()) {
users.push(this.$store.getters.getUserDataById(id).user);
}
return users;
},
methods: {
setMainUser({ entity }) {
const user = entity;
console.log("setMainUser APP", entity);
suggestedUsers() {
const suggested = [];
if (
user.id !== this.$store.getters.getMainUser &&
(this.$store.state.activity.calendarRange !== null ||
this.$store.state.activity.startDate !== null ||
this.$store.state.activity.endDate !== null)
) {
if (
!window.confirm(
this.$t("change_main_user_will_reset_event_data"),
)
) {
return;
}
}
this.$data.previousUser.forEach((u) => {
if (u.id !== this.$store.getters.getMainUser.id) {
suggested.push(u);
}
});
// add the previous user, if any, in the previous user list (in use for suggestion)
if (null !== this.$store.getters.getMainUser) {
const suggestedUids = new Set(
this.$data.previousUser.map((u) => u.id),
);
if (!suggestedUids.has(this.$store.getters.getMainUser.id)) {
this.$data.previousUser.push(
this.$store.getters.getMainUser,
);
}
}
this.$store.dispatch("setMainUser", user);
this.$store.commit("showUserOnCalendar", {
user,
ranges: true,
remotes: true,
});
},
removeMainUser(user) {
console.log("removeMainUser APP", user);
window.alert(this.$t("main_user_is_mandatory"));
return;
},
onDatesSet(event) {
console.log("onDatesSet", event);
this.$store.dispatch("setCurrentDatesView", {
start: event.start,
end: event.end,
});
},
onDateSelect(payload) {
console.log("onDateSelect", payload);
// show an alert if changing mainUser
if (
(this.$store.getters.getMainUser !== null &&
this.$store.state.me.id !==
this.$store.getters.getMainUser.id) ||
this.$store.getters.getMainUser === null
) {
if (!window.confirm(this.$t("will_change_main_user_for_me"))) {
return;
} else {
this.$store.commit("showUserOnCalendar", {
user: this.$store.state.me,
remotes: true,
ranges: true,
});
}
}
this.$store.dispatch("setEventTimes", {
start: payload.start,
end: payload.end,
});
},
onEventChange(payload) {
console.log("onEventChange", payload);
if (this.$store.state.activity.calendarRange !== null) {
throw new Error(
"not allowed to edit a calendar associated with a calendar range",
);
}
this.$store.dispatch("setEventTimes", {
start: payload.event.start,
end: payload.event.end,
});
},
onEventClick(payload) {
if (payload.event.extendedProps.is !== "range") {
// do nothing when clicking on remote
return;
}
// show an alert if changing mainUser
if (
this.$store.getters.getMainUser !== null &&
payload.event.extendedProps.userId !==
this.$store.getters.getMainUser.id
) {
if (
!window.confirm(
this.$t("this_calendar_range_will_change_main_user"),
)
) {
return;
}
}
this.$store.dispatch("associateCalendarToRange", {
range: payload.event,
});
},
return suggested;
},
},
methods: {
setMainUser({ entity }) {
const user = entity;
console.log("setMainUser APP", entity);
if (
user.id !== this.$store.getters.getMainUser &&
(this.$store.state.activity.calendarRange !== null ||
this.$store.state.activity.startDate !== null ||
this.$store.state.activity.endDate !== null)
) {
if (
!window.confirm(this.$t("change_main_user_will_reset_event_data"))
) {
return;
}
}
// add the previous user, if any, in the previous user list (in use for suggestion)
if (null !== this.$store.getters.getMainUser) {
const suggestedUids = new Set(this.$data.previousUser.map((u) => u.id));
if (!suggestedUids.has(this.$store.getters.getMainUser.id)) {
this.$data.previousUser.push(this.$store.getters.getMainUser);
}
}
this.$store.dispatch("setMainUser", user);
this.$store.commit("showUserOnCalendar", {
user,
ranges: true,
remotes: true,
});
},
removeMainUser(user) {
console.log("removeMainUser APP", user);
window.alert(this.$t("main_user_is_mandatory"));
return;
},
onDatesSet(event) {
console.log("onDatesSet", event);
this.$store.dispatch("setCurrentDatesView", {
start: event.start,
end: event.end,
});
},
onDateSelect(payload) {
console.log("onDateSelect", payload);
// show an alert if changing mainUser
if (
(this.$store.getters.getMainUser !== null &&
this.$store.state.me.id !== this.$store.getters.getMainUser.id) ||
this.$store.getters.getMainUser === null
) {
if (!window.confirm(this.$t("will_change_main_user_for_me"))) {
return;
} else {
this.$store.commit("showUserOnCalendar", {
user: this.$store.state.me,
remotes: true,
ranges: true,
});
}
}
this.$store.dispatch("setEventTimes", {
start: payload.start,
end: payload.end,
});
},
onEventChange(payload) {
console.log("onEventChange", payload);
if (this.$store.state.activity.calendarRange !== null) {
throw new Error(
"not allowed to edit a calendar associated with a calendar range",
);
}
this.$store.dispatch("setEventTimes", {
start: payload.event.start,
end: payload.event.end,
});
},
onEventClick(payload) {
if (payload.event.extendedProps.is !== "range") {
// do nothing when clicking on remote
return;
}
// show an alert if changing mainUser
if (
this.$store.getters.getMainUser !== null &&
payload.event.extendedProps.userId !==
this.$store.getters.getMainUser.id
) {
if (
!window.confirm(this.$t("this_calendar_range_will_change_main_user"))
) {
return;
}
}
this.$store.dispatch("associateCalendarToRange", {
range: payload.event,
});
},
},
};
</script>
<style>
.calendar-actives {
display: flex;
flex-direction: row;
flex-wrap: wrap;
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.display-options {
margin-top: 1rem;
margin-top: 1rem;
}
/* for events which are range */
.fc-event.isrange {
border-width: 3px;
border-width: 3px;
}
</style>

View File

@@ -1,119 +1,105 @@
<template>
<div :style="style" class="calendar-active">
<span class="badge-user">
{{ user.text }}
<template v-if="invite !== null">
<i v-if="invite.status === 'accepted'" class="fa fa-check" />
<i
v-else-if="invite.status === 'declined'"
class="fa fa-times"
/>
<i
v-else-if="invite.status === 'pending'"
class="fa fa-question-o"
/>
<i
v-else-if="invite.status === 'tentative'"
class="fa fa-question"
/>
<span v-else="">{{ invite.status }}</span>
</template>
</span>
<span class="form-check-inline form-switch">
<input
class="form-check-input"
type="checkbox"
id="flexSwitchCheckDefault"
v-model="rangeShow"
/>
&nbsp;<label
class="form-check-label"
for="flexSwitchCheckDefault"
title="Disponibilités"
><i class="fa fa-calendar-check-o"
/></label>
</span>
<span class="form-check-inline form-switch">
<input
class="form-check-input"
type="checkbox"
id="flexSwitchCheckDefault"
v-model="remoteShow"
/>
&nbsp;<label
class="form-check-label"
for="flexSwitchCheckDefault"
title="Agenda"
><i class="fa fa-calendar"
/></label>
</span>
</div>
<div :style="style" class="calendar-active">
<span class="badge-user">
{{ user.text }}
<template v-if="invite !== null">
<i v-if="invite.status === 'accepted'" class="fa fa-check" />
<i v-else-if="invite.status === 'declined'" class="fa fa-times" />
<i v-else-if="invite.status === 'pending'" class="fa fa-question-o" />
<i v-else-if="invite.status === 'tentative'" class="fa fa-question" />
<span v-else="">{{ invite.status }}</span>
</template>
</span>
<span class="form-check-inline form-switch">
<input
class="form-check-input"
type="checkbox"
id="flexSwitchCheckDefault"
v-model="rangeShow"
/>
&nbsp;<label
class="form-check-label"
for="flexSwitchCheckDefault"
title="Disponibilités"
><i class="fa fa-calendar-check-o"
/></label>
</span>
<span class="form-check-inline form-switch">
<input
class="form-check-input"
type="checkbox"
id="flexSwitchCheckDefault"
v-model="remoteShow"
/>
&nbsp;<label
class="form-check-label"
for="flexSwitchCheckDefault"
title="Agenda"
><i class="fa fa-calendar"
/></label>
</span>
</div>
</template>
<script>
import { mapGetters } from "vuex";
export default {
name: "CalendarActive",
props: {
user: {
type: Object,
required: true,
},
invite: {
type: Object,
required: false,
default: null,
},
name: "CalendarActive",
props: {
user: {
type: Object,
required: true,
},
computed: {
style() {
return {
backgroundColor: this.$store.getters.getUserData(this.user)
.mainColor,
};
},
rangeShow: {
set(value) {
this.$store.commit("showUserOnCalendar", {
user: this.user,
ranges: value,
});
},
get() {
return this.$store.getters.isRangeShownOnCalendarForUser(
this.user,
);
},
},
remoteShow: {
set(value) {
this.$store.commit("showUserOnCalendar", {
user: this.user,
remotes: value,
});
},
get() {
return this.$store.getters.isRemoteShownOnCalendarForUser(
this.user,
);
},
},
invite: {
type: Object,
required: false,
default: null,
},
},
computed: {
style() {
return {
backgroundColor: this.$store.getters.getUserData(this.user).mainColor,
};
},
rangeShow: {
set(value) {
this.$store.commit("showUserOnCalendar", {
user: this.user,
ranges: value,
});
},
get() {
return this.$store.getters.isRangeShownOnCalendarForUser(this.user);
},
},
remoteShow: {
set(value) {
this.$store.commit("showUserOnCalendar", {
user: this.user,
remotes: value,
});
},
get() {
return this.$store.getters.isRemoteShownOnCalendarForUser(this.user);
},
},
},
};
</script>
<style scoped lang="scss">
.calendar-active {
margin: 0 0.25rem 0.25rem 0;
padding: 0.5rem;
margin: 0 0.25rem 0.25rem 0;
padding: 0.5rem;
border-radius: 0.5rem;
border-radius: 0.5rem;
color: var(--bs-blue);
color: var(--bs-blue);
& > .badge-user {
margin-right: 0.5rem;
}
& > .badge-user {
margin-right: 0.5rem;
}
}
</style>

View File

@@ -14,37 +14,37 @@ export { whoami } from "../../../../../ChillMainBundle/Resources/public/lib/api/
* @return Promise
*/
export const fetchCalendarRangeForUser = (
user: User,
start: Date,
end: Date,
user: User,
start: Date,
end: Date,
): Promise<CalendarRange[]> => {
const uri = `/api/1.0/calendar/calendar-range-available/${user.id}.json`;
const dateFrom = datetimeToISO(start);
const dateTo = datetimeToISO(end);
const uri = `/api/1.0/calendar/calendar-range-available/${user.id}.json`;
const dateFrom = datetimeToISO(start);
const dateTo = datetimeToISO(end);
return fetchResults<CalendarRange>(uri, { dateFrom, dateTo });
return fetchResults<CalendarRange>(uri, { dateFrom, dateTo });
};
export const fetchCalendarRemoteForUser = (
user: User,
start: Date,
end: Date,
user: User,
start: Date,
end: Date,
): Promise<CalendarRemote[]> => {
const uri = `/api/1.0/calendar/proxy/calendar/by-user/${user.id}/events`;
const dateFrom = datetimeToISO(start);
const dateTo = datetimeToISO(end);
const uri = `/api/1.0/calendar/proxy/calendar/by-user/${user.id}/events`;
const dateFrom = datetimeToISO(start);
const dateTo = datetimeToISO(end);
return fetchResults<CalendarRemote>(uri, { dateFrom, dateTo });
return fetchResults<CalendarRemote>(uri, { dateFrom, dateTo });
};
export const fetchCalendarLocalForUser = (
user: User,
start: Date,
end: Date,
user: User,
start: Date,
end: Date,
): Promise<CalendarLight[]> => {
const uri = `/api/1.0/calendar/calendar/by-user/${user.id}.json`;
const dateFrom = datetimeToISO(start);
const dateTo = datetimeToISO(end);
const uri = `/api/1.0/calendar/calendar/by-user/${user.id}.json`;
const dateFrom = datetimeToISO(start);
const dateTo = datetimeToISO(end);
return fetchResults<CalendarLight>(uri, { dateFrom, dateTo });
return fetchResults<CalendarLight>(uri, { dateFrom, dateTo });
};

View File

@@ -1,17 +1,17 @@
const COLORS = [
/* from https://colorbrewer2.org/#type=qualitative&scheme=Set3&n=12 */
"#8dd3c7",
"#ffffb3",
"#bebada",
"#fb8072",
"#80b1d3",
"#fdb462",
"#b3de69",
"#fccde5",
"#d9d9d9",
"#bc80bd",
"#ccebc5",
"#ffed6f",
/* from https://colorbrewer2.org/#type=qualitative&scheme=Set3&n=12 */
"#8dd3c7",
"#ffffb3",
"#bebada",
"#fb8072",
"#80b1d3",
"#fdb462",
"#b3de69",
"#fccde5",
"#d9d9d9",
"#bc80bd",
"#ccebc5",
"#ffed6f",
];
export { COLORS };

View File

@@ -1,117 +1,117 @@
import { COLORS } from "../const";
import { ISOToDatetime } from "../../../../../../ChillMainBundle/Resources/public/chill/js/date";
import {
DateTime,
User,
DateTime,
User,
} from "../../../../../../ChillMainBundle/Resources/public/types";
import { CalendarLight, CalendarRange, CalendarRemote } from "../../../types";
import type { EventInputCalendarRange } from "../../../types";
import { EventInput } from "@fullcalendar/core";
export interface UserData {
user: User;
calendarRanges: CalendarRange[];
calendarRangesLoaded: {}[];
remotes: CalendarRemote[];
remotesLoaded: {}[];
locals: CalendarRemote[];
localsLoaded: {}[];
mainColor: string;
user: User;
calendarRanges: CalendarRange[];
calendarRangesLoaded: {}[];
remotes: CalendarRemote[];
remotesLoaded: {}[];
locals: CalendarRemote[];
localsLoaded: {}[];
mainColor: string;
}
export const addIdToValue = (string: string, id: number): string => {
const array = string ? string.split(",") : [];
array.push(id.toString());
const str = array.join();
return str;
const array = string ? string.split(",") : [];
array.push(id.toString());
const str = array.join();
return str;
};
export const removeIdFromValue = (string: string, id: number) => {
let array = string.split(",");
array = array.filter((el) => el !== id.toString());
const str = array.join();
return str;
let array = string.split(",");
array = array.filter((el) => el !== id.toString());
const str = array.join();
return str;
};
/*
* Assign missing keys for the ConcernedGroups component
*/
export const mapEntity = (entity: EventInput): EventInput => {
const calendar = { ...entity };
Object.assign(calendar, { thirdParties: entity.professionals });
const calendar = { ...entity };
Object.assign(calendar, { thirdParties: entity.professionals });
if (entity.startDate !== null) {
calendar.startDate = ISOToDatetime(entity.startDate.datetime);
}
if (entity.endDate !== null) {
calendar.endDate = ISOToDatetime(entity.endDate.datetime);
}
if (entity.startDate !== null) {
calendar.startDate = ISOToDatetime(entity.startDate.datetime);
}
if (entity.endDate !== null) {
calendar.endDate = ISOToDatetime(entity.endDate.datetime);
}
if (entity.calendarRange !== null) {
calendar.calendarRange.calendarRangeId = entity.calendarRange.id;
calendar.calendarRange.id = `range_${entity.calendarRange.id}`;
}
if (entity.calendarRange !== null) {
calendar.calendarRange.calendarRangeId = entity.calendarRange.id;
calendar.calendarRange.id = `range_${entity.calendarRange.id}`;
}
return calendar;
return calendar;
};
export const createUserData = (user: User, colorIndex: number): UserData => {
const colorId = colorIndex % COLORS.length;
const colorId = colorIndex % COLORS.length;
return {
user: user,
calendarRanges: [],
calendarRangesLoaded: [],
remotes: [],
remotesLoaded: [],
locals: [],
localsLoaded: [],
mainColor: COLORS[colorId],
};
return {
user: user,
calendarRanges: [],
calendarRangesLoaded: [],
remotes: [],
remotesLoaded: [],
locals: [],
localsLoaded: [],
mainColor: COLORS[colorId],
};
};
// TODO move this function to a more global namespace, as it is also in use in MyCalendarRange app
export const calendarRangeToFullCalendarEvent = (
entity: CalendarRange,
entity: CalendarRange,
): EventInputCalendarRange => {
return {
id: `range_${entity.id}`,
title: "(" + entity.user.text + ")",
start: entity.startDate.datetime8601,
end: entity.endDate.datetime8601,
allDay: false,
userId: entity.user.id,
userLabel: entity.user.label,
calendarRangeId: entity.id,
locationId: entity.location.id,
locationName: entity.location.name,
is: "range",
};
return {
id: `range_${entity.id}`,
title: "(" + entity.user.text + ")",
start: entity.startDate.datetime8601,
end: entity.endDate.datetime8601,
allDay: false,
userId: entity.user.id,
userLabel: entity.user.label,
calendarRangeId: entity.id,
locationId: entity.location.id,
locationName: entity.location.name,
is: "range",
};
};
export const remoteToFullCalendarEvent = (
entity: CalendarRemote,
entity: CalendarRemote,
): EventInput & { id: string } => {
return {
id: `range_${entity.id}`,
title: entity.title,
start: entity.startDate.datetime8601,
end: entity.endDate.datetime8601,
allDay: entity.isAllDay,
is: "remote",
};
return {
id: `range_${entity.id}`,
title: entity.title,
start: entity.startDate.datetime8601,
end: entity.endDate.datetime8601,
allDay: entity.isAllDay,
is: "remote",
};
};
export const localsToFullCalendarEvent = (
entity: CalendarLight,
entity: CalendarLight,
): EventInput & { id: string; originId: number } => {
return {
id: `local_${entity.id}`,
title: entity.persons.map((p) => p.text).join(", "),
originId: entity.id,
start: entity.startDate.datetime8601,
end: entity.endDate.datetime8601,
allDay: false,
is: "local",
};
return {
id: `local_${entity.id}`,
title: entity.persons.map((p) => p.text).join(", "),
originId: entity.id,
start: entity.startDate.datetime8601,
end: entity.endDate.datetime8601,
allDay: false,
is: "local",
};
};

Some files were not shown because too many files have changed in this diff Show More