Compare commits

...

192 Commits

Author SHA1 Message Date
3aea3f502b WIP add devs files for working with caddy / mercure 2025-07-16 10:56:52 +02:00
def75cec6c WIP first try for sending update command for ticket 2025-07-16 10:56:35 +02:00
41896b1dd4 First basic configuration for mercure 2025-07-14 17:31:24 +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
399 changed files with 35518 additions and 24058 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

@@ -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

13
.env
View File

@@ -16,9 +16,6 @@ APP_ENV=prod
APP_SECRET=!ChangeMeInAppEnv!
###< symfony/framework-bundle ###
## Wopi server for editing documents online
EDITOR_SERVER=http://collabora:9980
# must be manually set in .env.local
# ADMIN_PASSWORD=
@@ -92,3 +89,13 @@ 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/mercure-bundle ###
# See https://symfony.com/doc/current/mercure.html#configuration
# The URL of the Mercure hub, used by the app to publish updates (can be a local URL)
MERCURE_URL=https://example.com/.well-known/mercure
# The public URL of the Mercure hub, used by the browser to connect
MERCURE_PUBLIC_URL=https://example.com/.well-known/mercure
# The secret used to sign the JWTs
MERCURE_JWT_SECRET="!ChangeThisMercureHubJWTSecretKey!"
###< symfony/mercure-bundle ###

File diff suppressed because it is too large Load Diff

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

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

View File

@@ -32,3 +32,9 @@ services:
hostname: my-rabbit
volumes:
- ./docker/rabbitmq/data:/var/lib/rabbitmq
###> symfony/mercure-bundle ###
mercure:
ports:
- "127.0.0.1:8043:443"
###< symfony/mercure-bundle ###

View File

@@ -50,7 +50,36 @@ services:
timeout: 30s
retries: 3
###> symfony/mercure-bundle ###
mercure:
image: dunglas/mercure
restart: unless-stopped
environment:
# Uncomment the following line to disable HTTPS,
#SERVER_NAME: ':80'
MERCURE_PUBLISHER_JWT_KEY: '!ChangeThisMercureHubJWTSecretKey!'
MERCURE_SUBSCRIBER_JWT_KEY: '!ChangeThisMercureHubJWTSecretKey!'
# Set the URL of your Symfony project (without trailing slash!) as value of the cors_origins directive
MERCURE_EXTRA_DIRECTIVES: |
cors_origins http://chill-bundles.wip https://chill-bundles.wip
# Comment the following line to disable the development mode
command: /usr/bin/caddy run --config /etc/caddy/dev.Caddyfile
healthcheck:
test: [ "CMD", "curl", "-f", "https://localhost/healthz" ]
timeout: 5s
retries: 5
start_period: 60s
volumes:
- mercure_data:/data
- mercure_config:/config
###< symfony/mercure-bundle ###
volumes:
###> doctrine/doctrine-bundle ###
database_data:
###< doctrine/doctrine-bundle ###
###< doctrine/doctrine-bundle ###
###> symfony/mercure-bundle ###
mercure_data:
mercure_config:
###< symfony/mercure-bundle ###

View File

@@ -55,6 +55,7 @@
"symfony/http-foundation": "^5.4",
"symfony/intl": "^5.4",
"symfony/mailer": "^5.4",
"symfony/mercure-bundle": "^0.3.9",
"symfony/messenger": "^5.4",
"symfony/mime": "^5.4",
"symfony/monolog-bundle": "^3.5",
@@ -133,6 +134,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

@@ -35,6 +35,8 @@ 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],
Symfony\Bundle\MercureBundle\MercureBundle::class => ['all' => true],
];

View File

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

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

@@ -0,0 +1,8 @@
mercure:
hubs:
default:
url: '%env(MERCURE_URL)%'
public_url: '%env(MERCURE_PUBLIC_URL)%'
jwt:
secret: '%env(MERCURE_JWT_SECRET)%'
publish: '*'

View File

@@ -17,3 +17,8 @@ when@dev:
defaults:
template: '@ChillMain/Dev/dev.assets.test2.html.twig'
dev_mercure:
path: /_dev/mercure
controller: Symfony\Bundle\FrameworkBundle\Controller\TemplateController
defaults:
template: '@ChillMain/Dev/dev.mercure.html.twig'

View File

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

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

@@ -11,6 +11,7 @@
"@hotwired/stimulus": "^3.0.0",
"@luminateone/eslint-baseline": "^1.0.9",
"@symfony/stimulus-bridge": "^3.2.0",
"@symfony/ux-translator": "file:vendor/symfony/ux-translator/assets",
"@symfony/webpack-encore": "^4.1.0",
"@tsconfig/node20": "^20.1.4",
"@types/dompurify": "^3.0.5",
@@ -79,12 +80,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>

20
resources/dev.Caddyfile Normal file
View File

@@ -0,0 +1,20 @@
{
# Désactive les redirections automatiques HTTP -> HTTPS
# auto_https off
# Désactive le port 80 par défaut
# default_bind :8080
}
localhost:8043 {
mercure {
# Publisher JWT key
publisher_jwt !ChangeThisMercureHubJWTSecretKey!
# Subscriber JWT key
subscriber_jwt !ChangeThisMercureHubJWTSecretKey!
cors_origins http://chill-bundles.wip https://chill-bundles.wip
ui
demo
}
respond "Not Found" 404
}

View File

@@ -10,10 +10,7 @@
/>
</div>
<div
v-if="
getContext === 'accompanyingCourse' &&
suggestedEntities.length > 0
"
v-if="getContext === 'accompanyingCourse' && suggestedEntities.length > 0"
>
<ul class="list-suggest add-items inline">
<li

View File

@@ -39,17 +39,11 @@
<option selected disabled value="">
{{ trans(ACTIVITY_CHOOSE_LOCATION_TYPE) }}
</option>
<option
v-for="t in locationTypes"
:value="t"
:key="t.id"
>
<option v-for="t in locationTypes" :value="t" :key="t.id">
{{ localizeString(t.title) }}
</option>
</select>
<label>{{
trans(ACTIVITY_LOCATION_FIELDS_TYPE)
}}</label>
<label>{{ trans(ACTIVITY_LOCATION_FIELDS_TYPE) }}</label>
</div>
<div class="form-floating mb-3">
@@ -108,10 +102,7 @@
</form>
</template>
<template #footer>
<button
class="btn btn-save"
@click.prevent="saveNewLocation"
>
<button class="btn btn-save" @click.prevent="saveNewLocation">
{{ trans(SAVE) }}
</button>
</template>
@@ -244,8 +235,7 @@ export default {
},
hasPhonenumber1() {
return (
this.selected.phonenumber1 !== null &&
this.selected.phonenumber1 !== ""
this.selected.phonenumber1 !== null && this.selected.phonenumber1 !== ""
);
},
showAddAddress() {

View File

@@ -49,9 +49,7 @@
</div>
<div class="col-8">
<div v-if="actionIsLoading === true">
<i
class="chill-green fa fa-circle-o-notch fa-spin fa-lg"
></i>
<i class="chill-green fa fa-circle-o-notch fa-spin fa-lg"></i>
</div>
<span
@@ -64,8 +62,7 @@
<template
v-else-if="
socialActionsList.length > 0 &&
(socialIssuesSelected.length ||
socialActionsSelected.length)
(socialIssuesSelected.length || socialActionsSelected.length)
"
>
<div
@@ -88,9 +85,7 @@
</template>
<span
v-else-if="
actionAreLoaded && socialActionsList.length === 0
"
v-else-if="actionAreLoaded && socialActionsList.length === 0"
class="inline-choice chill-no-data-statement mt-3"
>
{{ trans(ACTIVITY_SOCIAL_ACTION_LIST_EMPTY) }}
@@ -169,8 +164,7 @@ export default {
/* 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.socialIssuesList.filter((i) => i.id === issue.id).length !== 1
) {
this.$store.commit("addIssueInList", issue);
}

View File

@@ -10,9 +10,7 @@
:value="issue"
/>
<label class="form-check-label" :for="issue.id">
<span class="badge bg-chill-l-gray text-dark">{{
issue.text
}}</span>
<span class="badge bg-chill-l-gray text-dark">{{ issue.text }}</span>
</label>
</div>
</span>

View File

@@ -68,9 +68,7 @@ export type EventInputCalendarRange = EventInput & {
export function isEventInputCalendarRange(
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

@@ -61,22 +61,14 @@
<label class="input-group-text" for="slotDuration"
>Durée des créneaux</label
>
<select
v-model="slotDuration"
id="slotDuration"
class="form-select"
>
<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"
>
<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>
@@ -92,11 +84,7 @@
<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"
>
<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>
@@ -124,9 +112,7 @@
v-model="hideWeekends"
/>
</span>
<label
for="showHideWE"
class="form-check-label input-group-text"
<label for="showHideWE" class="form-check-label input-group-text"
>Week-ends</label
>
</div>
@@ -142,9 +128,7 @@
<b v-else-if="arg.event.extendedProps.is === 'range'"
>{{ arg.timeText }}
{{ arg.event.extendedProps.locationName }}
<small>{{
arg.event.extendedProps.userLabel
}}</small></b
<small>{{ arg.event.extendedProps.userLabel }}</small></b
>
<b v-else-if="arg.event.extendedProps.is === 'current'"
>{{ arg.timeText }} {{ $t("current_selected") }}
@@ -152,9 +136,7 @@
<b v-else-if="arg.event.extendedProps.is === 'local'">{{
arg.event.title
}}</b>
<b v-else
>{{ arg.timeText }} {{ $t("current_selected") }}
</b>
<b v-else>{{ arg.timeText }} {{ $t("current_selected") }} </b>
</span>
</template>
</FullCalendar>
@@ -268,9 +250,7 @@ export default {
this.$store.state.activity.endDate !== null)
) {
if (
!window.confirm(
this.$t("change_main_user_will_reset_event_data"),
)
!window.confirm(this.$t("change_main_user_will_reset_event_data"))
) {
return;
}
@@ -278,13 +258,9 @@ export default {
// 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),
);
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.$data.previousUser.push(this.$store.getters.getMainUser);
}
}
@@ -314,8 +290,7 @@ export default {
// show an alert if changing mainUser
if (
(this.$store.getters.getMainUser !== null &&
this.$store.state.me.id !==
this.$store.getters.getMainUser.id) ||
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"))) {
@@ -359,9 +334,7 @@ export default {
this.$store.getters.getMainUser.id
) {
if (
!window.confirm(
this.$t("this_calendar_range_will_change_main_user"),
)
!window.confirm(this.$t("this_calendar_range_will_change_main_user"))
) {
return;
}

View File

@@ -4,18 +4,9 @@
{{ 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"
/>
<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>
@@ -69,8 +60,7 @@ export default {
computed: {
style() {
return {
backgroundColor: this.$store.getters.getUserData(this.user)
.mainColor,
backgroundColor: this.$store.getters.getUserData(this.user).mainColor,
};
},
rangeShow: {
@@ -81,9 +71,7 @@ export default {
});
},
get() {
return this.$store.getters.isRangeShownOnCalendarForUser(
this.user,
);
return this.$store.getters.isRangeShownOnCalendarForUser(this.user);
},
},
remoteShow: {
@@ -94,9 +82,7 @@ export default {
});
},
get() {
return this.$store.getters.isRemoteShownOnCalendarForUser(
this.user,
);
return this.$store.getters.isRemoteShownOnCalendarForUser(this.user);
},
},
},

View File

@@ -22,33 +22,25 @@
</button>
<ul class="dropdown-menu" aria-labelledby="btnGroupDrop1">
<li v-if="status !== Statuses.ACCEPTED">
<a
class="dropdown-item"
@click="changeStatus(Statuses.ACCEPTED)"
><i class="fa fa-check" aria-hidden="true"></i>
{{ $t("Accept") }}</a
<a class="dropdown-item" @click="changeStatus(Statuses.ACCEPTED)"
><i class="fa fa-check" aria-hidden="true"></i> {{ $t("Accept") }}</a
>
</li>
<li v-if="status !== Statuses.DECLINED">
<a
class="dropdown-item"
@click="changeStatus(Statuses.DECLINED)"
><i class="fa fa-times" aria-hidden="true"></i>
{{ $t("Decline") }}</a
<a class="dropdown-item" @click="changeStatus(Statuses.DECLINED)"
><i class="fa fa-times" aria-hidden="true"></i> {{ $t("Decline") }}</a
>
</li>
<li v-if="status !== Statuses.TENTATIVELY_ACCEPTED">
<a
class="dropdown-item"
@click="changeStatus(Statuses.TENTATIVELY_ACCEPTED)"
><i class="fa fa-question"></i>
{{ $t("Tentatively_accept") }}</a
><i class="fa fa-question"></i> {{ $t("Tentatively_accept") }}</a
>
</li>
<li v-if="status !== Statuses.PENDING">
<a class="dropdown-item" @click="changeStatus(Statuses.PENDING)"
><i class="fa fa-hourglass-o"></i>
{{ $t("Set_pending") }}</a
><i class="fa fa-hourglass-o"></i> {{ $t("Set_pending") }}</a
>
</li>
</ul>
@@ -91,9 +83,7 @@ export default defineComponent({
},
},
emits: {
statusChanged(
payload: "accepted" | "declined" | "pending" | "tentative",
) {
statusChanged(payload: "accepted" | "declined" | "pending" | "tentative") {
return true;
},
},

View File

@@ -23,22 +23,14 @@
<label class="input-group-text" for="slotDuration"
>Durée des créneaux</label
>
<select
v-model="slotDuration"
id="slotDuration"
class="form-select"
>
<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"
>
<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>
@@ -54,11 +46,7 @@
<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"
>
<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>
@@ -86,9 +74,7 @@
v-model="showWeekends"
/>
</span>
<label
for="showHideWE"
class="form-check-label input-group-text"
<label for="showHideWE" class="form-check-label input-group-text"
>Week-ends</label
>
</div>
@@ -98,16 +84,12 @@
<FullCalendar :options="calendarOptions" ref="calendarRef">
<template v-slot:eventContent="{ event }: { event: EventApi }">
<span :class="eventClasses">
<b v-if="event.extendedProps.is === 'remote'">{{
event.title
}}</b>
<b v-if="event.extendedProps.is === 'remote'">{{ event.title }}</b>
<b v-else-if="event.extendedProps.is === 'range'"
>{{ formatDate(event.startStr) }} -
{{ event.extendedProps.locationName }}</b
>
<b v-else-if="event.extendedProps.is === 'local'">{{
event.title
}}</b>
<b v-else-if="event.extendedProps.is === 'local'">{{ event.title }}</b>
<b v-else>no 'is'</b>
<a
v-if="event.extendedProps.is === 'range'"
@@ -126,11 +108,7 @@
<h6 class="chill-red">{{ $t("copy_range_from_to") }}</h6>
</div>
<div class="col-xs-12 col-sm-9 col-md-2">
<select
v-model="dayOrWeek"
id="dayOrWeek"
class="form-select"
>
<select v-model="dayOrWeek" id="dayOrWeek" class="form-select">
<option value="day">{{ $t("from_day_to_day") }}</option>
<option value="week">
{{ $t("from_week_to_week") }}
@@ -139,27 +117,16 @@
</div>
<template v-if="dayOrWeek === 'day'">
<div class="col-xs-12 col-sm-3 col-md-3">
<input
class="form-control"
type="date"
v-model="copyFrom"
/>
<input class="form-control" type="date" v-model="copyFrom" />
</div>
<div class="col-xs-12 col-sm-1 col-md-1 copy-chevron">
<i class="fa fa-angle-double-right"></i>
</div>
<div class="col-xs-12 col-sm-3 col-md-3">
<input
class="form-control"
type="date"
v-model="copyTo"
/>
<input class="form-control" type="date" v-model="copyTo" />
</div>
<div class="col-xs-12 col-sm-5 col-md-1">
<button
class="btn btn-action float-end"
@click="copyDay"
>
<button class="btn btn-action float-end" @click="copyDay">
{{ $t("copy_range") }}
</button>
</div>
@@ -171,11 +138,7 @@
id="copyFromWeek"
class="form-select"
>
<option
v-for="w in lastWeeks"
:value="w.value"
:key="w.value"
>
<option v-for="w in lastWeeks" :value="w.value" :key="w.value">
{{ w.text }}
</option>
</select>
@@ -184,25 +147,14 @@
<i class="fa fa-angle-double-right"></i>
</div>
<div class="col-xs-12 col-sm-3 col-md-3">
<select
v-model="copyToWeek"
id="copyToWeek"
class="form-select"
>
<option
v-for="w in nextWeeks"
:value="w.value"
:key="w.value"
>
<select v-model="copyToWeek" id="copyToWeek" class="form-select">
<option v-for="w in nextWeeks" :value="w.value" :key="w.value">
{{ w.text }}
</option>
</select>
</div>
<div class="col-xs-12 col-sm-5 col-md-1">
<button
class="btn btn-action float-end"
@click="copyWeek"
>
<button class="btn btn-action float-end" @click="copyWeek">
{{ $t("copy_range") }}
</button>
</div>

View File

@@ -41,9 +41,7 @@ const futureStore = function (): Promise<Store<State>> {
});
store.commit("me/setWhoAmi", user, { root: true });
store
.dispatch("locations/getLocations", null, { root: true })
.then((_) => {
store.dispatch("locations/getLocations", null, { root: true }).then((_) => {
return store.dispatch("locations/getCurrentLocation", null, {
root: true,
});

View File

@@ -29,10 +29,7 @@ export default {
(state: CalendarLocalsState) =>
({ start, end }: { start: Date; end: Date }): boolean => {
for (const range of state.localsLoaded) {
if (
start.getTime() === range.start &&
end.getTime() === range.end
) {
if (start.getTime() === range.start && end.getTime() === range.end) {
return true;
}
}
@@ -54,10 +51,7 @@ export default {
});
state.key = state.key + toAdd.length;
},
addLoaded(
state: CalendarLocalsState,
payload: { start: Date; end: Date },
) {
addLoaded(state: CalendarLocalsState, payload: { start: Date; end: Date }) {
state.localsLoaded.push({
start: payload.start.getTime(),
end: payload.end.getTime(),
@@ -85,11 +79,7 @@ export default {
end: end,
});
return fetchCalendarLocalForUser(
ctx.rootGetters["me/getMe"],
start,
end,
)
return fetchCalendarLocalForUser(ctx.rootGetters["me/getMe"], start, end)
.then((remotes: CalendarLight[]) => {
// to be add when reactivity problem will be solve ?
//ctx.commit('addRemotes', remotes);

View File

@@ -40,10 +40,7 @@ export default {
(state: CalendarRangesState) =>
({ start, end }: { start: Date; end: Date }): boolean => {
for (const range of state.rangesLoaded) {
if (
start.getTime() === range.start &&
end.getTime() === range.end
) {
if (start.getTime() === range.start && end.getTime() === range.end) {
return true;
}
}
@@ -110,9 +107,7 @@ export default {
state: CalendarRangesState,
externalEvents: (EventInput & { id: string })[],
) {
const toAdd = externalEvents.filter(
(r) => !state.rangesIndex.has(r.id),
);
const toAdd = externalEvents.filter((r) => !state.rangesIndex.has(r.id));
toAdd.forEach((r) => {
state.rangesIndex.add(r.id);
@@ -120,10 +115,7 @@ export default {
});
state.key = state.key + toAdd.length;
},
addLoaded(
state: CalendarRangesState,
payload: { start: Date; end: Date },
) {
addLoaded(state: CalendarRangesState, payload: { start: Date; end: Date }) {
state.rangesLoaded.push({
start: payload.start.getTime(),
end: payload.end.getTime(),
@@ -142,17 +134,12 @@ export default {
},
removeRange(state: CalendarRangesState, calendarRangeId: number) {
const found = state.ranges.find(
(r) =>
r.calendarRangeId === calendarRangeId && r.is === "range",
(r) => r.calendarRangeId === calendarRangeId && r.is === "range",
);
if (found !== undefined) {
state.ranges = state.ranges.filter(
(r) =>
!(
r.calendarRangeId === calendarRangeId &&
r.is === "range"
),
(r) => !(r.calendarRangeId === calendarRangeId && r.is === "range"),
);
if (typeof found.id === "string") {
@@ -211,11 +198,7 @@ export default {
},
createRange(
ctx: Context,
{
start,
end,
location,
}: { start: Date; end: Date; location: Location },
{ start, end, location }: { start: Date; end: Date; location: Location },
): Promise<null> {
const url = `/api/1.0/calendar/calendar-range.json?`;
@@ -240,11 +223,7 @@ export default {
},
} as CalendarRangeCreate;
return makeFetch<CalendarRangeCreate, CalendarRange>(
"POST",
url,
body,
)
return makeFetch<CalendarRangeCreate, CalendarRange>("POST", url, body)
.then((newRange) => {
ctx.commit("addRange", newRange);
@@ -281,11 +260,7 @@ export default {
},
} as CalendarRangeEdit;
return makeFetch<CalendarRangeEdit, CalendarRange>(
"PATCH",
url,
body,
)
return makeFetch<CalendarRangeEdit, CalendarRange>("PATCH", url, body)
.then((range) => {
ctx.commit("updateRange", range);
return Promise.resolve(null);
@@ -310,11 +285,7 @@ export default {
},
} as CalendarRangeEdit;
return makeFetch<CalendarRangeEdit, CalendarRange>(
"PATCH",
url,
body,
)
return makeFetch<CalendarRangeEdit, CalendarRange>("PATCH", url, body)
.then((range) => {
ctx.commit("updateRange", range);
return Promise.resolve(null);
@@ -334,20 +305,14 @@ export default {
for (const r of rangesToCopy) {
const start = new Date(ISOToDatetime(r.start) as Date);
start.setFullYear(
to.getFullYear(),
to.getMonth(),
to.getDate(),
);
start.setFullYear(to.getFullYear(), to.getMonth(), to.getDate());
const end = new Date(ISOToDatetime(r.end) as Date);
end.setFullYear(to.getFullYear(), to.getMonth(), to.getDate());
const location = ctx.rootGetters["locations/getLocationById"](
r.locationId,
);
promises.push(
ctx.dispatch("createRange", { start, end, location }),
);
promises.push(ctx.dispatch("createRange", { start, end, location }));
}
return Promise.all(promises).then(() => Promise.resolve(null));
@@ -369,9 +334,7 @@ export default {
r.locationId,
);
promises.push(
ctx.dispatch("createRange", { start, end, location }),
);
promises.push(ctx.dispatch("createRange", { start, end, location }));
}
return Promise.all(promises).then(() => Promise.resolve(null));

View File

@@ -29,10 +29,7 @@ export default {
(state: CalendarRemotesState) =>
({ start, end }: { start: Date; end: Date }): boolean => {
for (const range of state.remotesLoaded) {
if (
start.getTime() === range.start &&
end.getTime() === range.end
) {
if (start.getTime() === range.start && end.getTime() === range.end) {
return true;
}
}
@@ -85,11 +82,7 @@ export default {
end: end,
});
return fetchCalendarRemoteForUser(
ctx.rootGetters["me/getMe"],
start,
end,
)
return fetchCalendarRemoteForUser(ctx.rootGetters["me/getMe"], start, end)
.then((remotes: CalendarRemote[]) => {
// to be add when reactivity problem will be solve ?
//ctx.commit('addRemotes', remotes);

View File

@@ -112,11 +112,8 @@ export default {
results.forEach((i) => {
if (!users.some((j) => i.user.id === j.id)) {
let ratio = Math.floor(
users.length / COLORS.length,
);
let colorIndex =
users.length - ratio * COLORS.length;
let ratio = Math.floor(users.length / COLORS.length);
let colorIndex = users.length - ratio * COLORS.length;
users.push({
id: i.user.id,
username: i.user.username,
@@ -153,45 +150,29 @@ export default {
(me) =>
new Promise((resolve, reject) => {
this.users.logged = me;
let currentUser = users.find(
(u) => u.id === me.id,
);
let currentUser = users.find((u) => u.id === me.id);
this.value = currentUser;
fetchCalendar(currentUser.id).then(
(calendar) =>
new Promise(
(resolve, reject) => {
let results =
calendar.results;
let events =
results.map(
(i) => ({
start: i
.startDate
.datetime,
end: i
.endDate
.datetime,
}),
);
let calendarEventsCurrentUser =
{
new Promise((resolve, reject) => {
let results = calendar.results;
let events = results.map((i) => ({
start: i.startDate.datetime,
end: i.endDate.datetime,
}));
let calendarEventsCurrentUser = {
events: events,
color: "darkblue",
id: 1000,
editable: false,
};
this.calendarEvents.user =
calendarEventsCurrentUser;
this.calendarEvents.user = calendarEventsCurrentUser;
this.selectUsers(
currentUser,
);
this.selectUsers(currentUser);
resolve();
},
),
}),
);
resolve();
@@ -209,9 +190,7 @@ export default {
return `${value.username}`;
},
coloriseSelectedValues() {
let tags = document.querySelectorAll(
"div.multiselect__tags-wrap",
)[0];
let tags = document.querySelectorAll("div.multiselect__tags-wrap")[0];
if (tags.hasChildNodes()) {
let children = tags.childNodes;
@@ -232,8 +211,8 @@ export default {
},
selectEvents() {
let selectedUsersId = this.users.selected.map((a) => a.id);
this.calendarEvents.selected = this.calendarEvents.loaded.filter(
(a) => selectedUsersId.includes(a.id),
this.calendarEvents.selected = this.calendarEvents.loaded.filter((a) =>
selectedUsersId.includes(a.id),
);
},
selectUsers(value) {
@@ -243,9 +222,7 @@ export default {
this.updateEventsSource();
},
unSelectUsers(value) {
this.users.selected = this.users.selected.filter(
(a) => a.id != value.id,
);
this.users.selected = this.users.selected.filter((a) => a.id != value.id);
this.selectEvents();
this.updateEventsSource();
},

View File

@@ -20,10 +20,7 @@
</option>
<template v-for="t in templates" :key="t.id">
<option :value="t.id">
{{
localizeString(t.name) ||
"Aucun nom défini"
}}
{{ localizeString(t.name) || "Aucun nom défini" }}
</option>
</template>
</select>
@@ -31,9 +28,7 @@
v-if="canGenerate"
class="btn btn-update btn-sm change-icon"
:href="buildUrlGenerate"
@click.prevent="
clickGenerate($event, buildUrlGenerate)
"
@click.prevent="clickGenerate($event, buildUrlGenerate)"
><i class="fa fa-fw fa-cog"
/></a>
<a

View File

@@ -13,8 +13,9 @@ const startApp = (
const inputTitle = collectionEntry?.querySelector("input[type='text']");
const input_stored_object: HTMLInputElement | null =
divElement.querySelector("input[data-stored-object]");
const input_stored_object: HTMLInputElement | null = divElement.querySelector(
"input[data-stored-object]",
);
if (null === input_stored_object) {
throw new Error("input to stored object not found");
}
@@ -53,9 +54,7 @@ const startApp = (
console.log("version added", stored_object_version);
this.$data.existingDoc = stored_object;
this.$data.existingDoc.currentVersion = stored_object_version;
input_stored_object.value = JSON.stringify(
this.$data.existingDoc,
);
input_stored_object.value = JSON.stringify(this.$data.existingDoc);
if (this.$data.inputTitle) {
if (!this.$data.inputTitle?.value) {
this.$data.inputTitle.value = file_name;

View File

@@ -49,9 +49,7 @@
<li v-if="isHistoryViewable">
<history-button
:stored-object="props.storedObject"
:can-edit="
canEdit && props.storedObject._permissions.canEdit
"
:can-edit="canEdit && props.storedObject._permissions.canEdit"
></history-button>
</li>
</ul>
@@ -129,9 +127,7 @@ const props = withDefaults(defineProps<DocumentActionButtonsGroupConfig>(), {
canDownload: true,
canConvertPdf: true,
returnPath:
window.location.pathname +
window.location.search +
window.location.hash,
window.location.pathname + window.location.search + window.location.hash,
});
/**

View File

@@ -29,9 +29,7 @@
</modal>
</teleport>
<div class="col-12 m-auto sticky-top">
<div
class="row justify-content-center border-bottom pdf-tools d-md-none"
>
<div class="row justify-content-center border-bottom pdf-tools d-md-none">
<div class="col-5 text-center turn-page">
<select
class="form-select form-select-sm"
@@ -92,10 +90,7 @@
v-if="signature.zones.length === 1 && signedState !== 'signed'"
class="col-5 p-0 text-center turnSignature"
>
<button
class="btn btn-light btn-sm"
@click="goToSignatureZoneUnique"
>
<button class="btn btn-light btn-sm" @click="goToSignatureZoneUnique">
{{ trans(SIGNATURES_GO_TO_SIGNATURE_UNIQUE) }}
</button>
</div>
@@ -150,10 +145,7 @@
:title="trans(SIGNATURES_ADD_SIGN_ZONE)"
>
<template v-if="canvasEvent === 'add'">
<div
class="spinner-border spinner-border-sm"
role="status"
>
<div class="spinner-border spinner-border-sm" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</template>
@@ -207,10 +199,7 @@
v-if="signature.zones.length === 1 && signedState !== 'signed'"
class="col-4 d-xl-none text-center turnSignature p-0"
>
<button
class="btn btn-light btn-sm"
@click="goToSignatureZoneUnique"
>
<button class="btn btn-light btn-sm" @click="goToSignatureZoneUnique">
{{ trans(SIGNATURES_GO_TO_SIGNATURE_UNIQUE) }}
</button>
</div>
@@ -238,10 +227,7 @@
v-if="signature.zones.length === 1 && signedState !== 'signed'"
class="col-4 d-none d-xl-flex p-0 text-center turnSignature"
>
<button
class="btn btn-light btn-sm"
@click="goToSignatureZoneUnique"
>
<button class="btn btn-light btn-sm" @click="goToSignatureZoneUnique">
{{ trans(SIGNATURES_GO_TO_SIGNATURE_UNIQUE) }}
</button>
</div>
@@ -299,10 +285,7 @@
</template>
<template v-else>
{{ trans(SIGNATURES_CLICK_ON_DOCUMENT) }}
<div
class="spinner-border spinner-border-sm"
role="status"
>
<div class="spinner-border spinner-border-sm" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</template>
@@ -562,14 +545,8 @@ const addCanvasEvents = () => {
);
});
} else {
const canvas = document.querySelectorAll(
"canvas",
)[0] as HTMLCanvasElement;
canvas.addEventListener(
"pointerup",
(e) => canvasClick(e, canvas),
false,
);
const canvas = document.querySelectorAll("canvas")[0] as HTMLCanvasElement;
canvas.addEventListener("pointerup", (e) => canvasClick(e, canvas), false);
}
};
@@ -605,11 +582,7 @@ const hitSignature = (
scaleYToCanvas(zone.y, canvas.height, zone.PDFPage.height) <
xy[1] &&
xy[1] <
scaleYToCanvas(
zone.height - zone.y,
canvas.height,
zone.PDFPage.height,
) +
scaleYToCanvas(zone.height - zone.y, canvas.height, zone.PDFPage.height) +
zone.PDFPage.height * zoom.value;
const selectZone = async (z: SignatureZone, canvas: HTMLCanvasElement) => {
@@ -625,8 +598,7 @@ const selectZoneEvent = (e: PointerEvent, canvas: HTMLCanvasElement) =>
signature.zones
.filter(
(z) =>
(z.PDFPage.index + 1 === getCanvasId(canvas) &&
multiPage.value) ||
(z.PDFPage.index + 1 === getCanvasId(canvas) && multiPage.value) ||
(z.PDFPage.index + 1 === page.value && !multiPage.value),
)
.map((z) => {

View File

@@ -153,12 +153,10 @@ const handleFile = async (file: File): Promise<void> => {
</p>
<!-- todo i18n -->
<p v-if="has_existing_doc">
Déposez un document ou cliquez ici pour remplacer le document
existant
Déposez un document ou cliquez ici pour remplacer le document existant
</p>
<p v-else>
Déposez un document ou cliquez ici pour ouvrir le navigateur de
fichier
Déposez un document ou cliquez ici pour ouvrir le navigateur de fichier
</p>
</div>
<div v-else class="waiting">

View File

@@ -35,9 +35,7 @@ async function download_and_open(event: Event): Promise<void> {
if (null === state.content) {
event.preventDefault();
const raw = await download_doc(
build_convert_link(props.storedObject.uuid),
);
const raw = await download_doc(build_convert_link(props.storedObject.uuid));
state.content = window.URL.createObjectURL(raw);
button.href = window.URL.createObjectURL(raw);

View File

@@ -42,9 +42,7 @@ const editionUntilFormatted = computed<string>(() => {
<modal v-if="state.modalOpened" @close="state.modalOpened = false">
<template v-slot:body>
<div class="desktop-edit">
<p class="center">
Veuillez enregistrer vos modifications avant le
</p>
<p class="center">Veuillez enregistrer vos modifications avant le</p>
<p>
<strong>{{ editionUntilFormatted }}</strong>
</p>
@@ -57,23 +55,21 @@ const editionUntilFormatted = computed<string>(() => {
<p>
<small
>Le document peut être édité uniquement en utilisant
Libre Office.</small
>Le document peut être édité uniquement en utilisant Libre
Office.</small
>
</p>
<p>
<small
>En cas d'échec lors de l'enregistrement, sauver le
document sur le poste de travail avant de le déposer
à nouveau ici.</small
>En cas d'échec lors de l'enregistrement, sauver le document sur
le poste de travail avant de le déposer à nouveau ici.</small
>
</p>
<p>
<small
>Vous pouvez naviguez sur d'autres pages pendant
l'édition.</small
>Vous pouvez naviguez sur d'autres pages pendant l'édition.</small
>
</p>
</div>

View File

@@ -95,10 +95,7 @@ async function download_and_open(): Promise<void> {
let raw;
try {
raw = await download_and_decrypt_doc(
props.storedObject,
props.atVersion,
);
raw = await download_and_decrypt_doc(props.storedObject, props.atVersion);
} catch (e) {
console.error("error while downloading and decrypting document");
console.error(e);

View File

@@ -49,8 +49,7 @@ const isRestored = computed<boolean>(
);
const isDuplicated = computed<boolean>(
() =>
props.version.version === 0 && null !== props.version["from-restored"],
() => props.version.version === 0 && null !== props.version["from-restored"],
);
const classes = computed<{
@@ -70,16 +69,9 @@ const classes = computed<{
<div :class="classes">
<div
class="col-12 tags"
v-if="
isCurrent ||
isKeptBeforeConversion ||
isRestored ||
isDuplicated
"
>
<span class="badge bg-success" v-if="isCurrent"
>Version actuelle</span
v-if="isCurrent || isKeptBeforeConversion || isRestored || isDuplicated"
>
<span class="badge bg-success" v-if="isCurrent">Version actuelle</span>
<span class="badge bg-info" v-if="isKeptBeforeConversion"
>Conservée avant conversion dans un autre format</span
>
@@ -96,21 +88,17 @@ const classes = computed<{
<span
><strong>&nbsp;#{{ version.version + 1 }}&nbsp;</strong></span
>
<template
v-if="version.createdBy !== null && version.createdAt !== null"
<template v-if="version.createdBy !== null && version.createdAt !== null"
><strong v-if="version.version == 0">créé par</strong
><strong v-else>modifié par</strong>
<span class="badge-user"
><UserRenderBoxBadge
:user="version.createdBy"
></UserRenderBoxBadge
><UserRenderBoxBadge :user="version.createdBy"></UserRenderBoxBadge
></span>
<strong>à</strong>
{{
$d(ISOToDatetime(version.createdAt.datetime8601), "long")
}}</template
><template
v-if="version.createdBy === null && version.createdAt !== null"
><template v-if="version.createdBy === null && version.createdAt !== null"
><strong v-if="version.version == 0">Créé le</strong
><strong v-else>modifié le</strong>
{{

View File

@@ -2,9 +2,7 @@
<a
:class="Object.assign(props.classes, { btn: true })"
@click="beforeLeave($event)"
:href="
build_wopi_editor_link(props.storedObject.uuid, props.returnPath)
"
:href="build_wopi_editor_link(props.storedObject.uuid, props.returnPath)"
>
<i class="fa fa-paragraph"></i>
Editer en ligne

View File

@@ -145,9 +145,7 @@ async function download_info_link(
function build_wopi_editor_link(uuid: string, returnPath?: string) {
if (returnPath === undefined) {
returnPath =
window.location.pathname +
window.location.search +
window.location.hash;
window.location.pathname + window.location.search + window.location.hash;
}
return (
@@ -186,10 +184,7 @@ async function download_and_decrypt_doc(
) {
downloadInfo = storedObject._links.downloadLink;
} else {
downloadInfo = await download_info_link(
storedObject,
atVersionToDownload,
);
downloadInfo = await download_info_link(storedObject, atVersionToDownload);
}
const rawResponse = await window.fetch(downloadInfo.url);
@@ -244,10 +239,7 @@ async function download_doc_as_pdf(storedObject: StoredObject): Promise<Blob> {
}
if (storedObject.currentVersion?.type === "application/pdf") {
return download_and_decrypt_doc(
storedObject,
storedObject.currentVersion,
);
return download_and_decrypt_doc(storedObject, storedObject.currentVersion);
}
const convertLink = build_convert_link(storedObject.uuid);

View File

@@ -613,4 +613,14 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter
return $this;
}
/**
* Check if the current object is an instance of User.
*
* @return bool returns true if the current object is an instance of User, false otherwise
*/
public function isUser(): bool
{
return true;
}
}

View File

@@ -27,6 +27,8 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Contracts\Translation\TranslatableInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
// command to get the report with curl : curl --user "center a_social:password" "http://localhost:8000/fr/exports/generate/count_person?export[filters][person_gender_filter][enabled]=&export[filters][person_nationality_filter][enabled]=&export[filters][person_nationality_filter][form][nationalities]=&export[aggregators][person_nationality_aggregator][order]=1&export[aggregators][person_nationality_aggregator][form][group_by_level]=country&export[submit]=&export[_token]=RHpjHl389GrK-bd6iY5NsEqrD5UKOTHH40QKE9J1edU" --globoff
/**
* Create a CSV List for the export.
*/

View File

@@ -76,6 +76,24 @@ final class PhonenumberHelper implements PhoneNumberHelperInterface
->formatOutOfCountryCallingNumber($phoneNumber, $this->config['default_carrier_code']);
}
/**
* @throws NumberParseException
*/
public function parse(string $phoneNumber): PhoneNumber
{
$sanitizedPhoneNumber = $phoneNumber;
if (str_starts_with($sanitizedPhoneNumber, '00')) {
$sanitizedPhoneNumber = '+'.substr($sanitizedPhoneNumber, 2, null);
}
if (!str_starts_with($sanitizedPhoneNumber, '+') && !str_starts_with($sanitizedPhoneNumber, '0')) {
$sanitizedPhoneNumber = '+'.$sanitizedPhoneNumber;
}
return $this->phoneNumberUtil->parse($sanitizedPhoneNumber, $this->config['default_carrier_code']);
}
/**
* Get type (mobile, landline, ...) for phone number.
*/

View File

@@ -56,9 +56,7 @@ export const ISOToDatetime = (str: string | null): Date | null => {
[time, timezone] = times.split(times.charAt(8)),
[hours, minutes, seconds] = time.split(":").map((s) => parseInt(s));
if ("0000" === timezone) {
return new Date(
Date.UTC(year, month - 1, date, hours, minutes, seconds),
);
return new Date(Date.UTC(year, month - 1, date, hours, minutes, seconds));
}
return new Date(year, month - 1, date, hours, minutes, seconds);
@@ -154,9 +152,7 @@ export const intervalISOToDays = (str: string | null): number | null => {
vstring = "";
break;
default:
throw Error(
"this character should not appears: " + str.charAt(i),
);
throw Error("this character should not appears: " + str.charAt(i));
}
}

View File

@@ -126,7 +126,8 @@ function loadDynamicPicker(element) {
-1 ===
this.suggested.findIndex(
(e) => e.type === entity.type && e.id === entity.id,
)
) &&
"me" !== entity
) {
this.suggested.push(entity);
}

View File

@@ -11,6 +11,11 @@ export interface Civility {
// TODO
}
export interface Household {
type: "household";
id: number;
}
export interface Job {
id: number;
type: "user_job";
@@ -215,3 +220,61 @@ export interface ExportGeneration {
export interface PrivateCommentEmbeddable {
comments: Record<number, string>;
}
// API Exception types
export interface TransportExceptionInterface {
name: string;
}
export interface ValidationExceptionInterface
extends TransportExceptionInterface {
name: "ValidationException";
error: object;
violations: string[];
titles: string[];
propertyPaths: string[];
}
export interface AccessExceptionInterface extends TransportExceptionInterface {
name: "AccessException";
violations: string[];
}
export interface NotFoundExceptionInterface
extends TransportExceptionInterface {
name: "NotFoundException";
}
export interface ServerExceptionInterface extends TransportExceptionInterface {
name: "ServerException";
message: string;
code: number;
body: string;
}
export interface ConflictHttpExceptionInterface
extends TransportExceptionInterface {
name: "ConflictHttpException";
violations: string[];
}
export type ApiException =
| ValidationExceptionInterface
| AccessExceptionInterface
| NotFoundExceptionInterface
| ServerExceptionInterface
| ConflictHttpExceptionInterface;
export interface Modal {
showModal: boolean;
modalDialogClass: string;
}
export interface Selected {
result: UserGroupOrUser;
}
export interface addNewEntities {
selected: Selected[];
modal: Modal;
}

View File

@@ -144,10 +144,7 @@
</template>
<template #action>
<li v-if="!this.context.edit && this.useDatePane">
<button
class="btn btn-update change-icon"
@click="closeEditPane"
>
<button class="btn btn-update change-icon" @click="closeEditPane">
{{ $t("nav.next") }}
<i class="fa fa-fw fa-arrow-right" />
</button>
@@ -370,8 +367,7 @@ export default {
getTextTitle() {
if (
typeof this.options.title !== "undefined" &&
(this.options.title.edit !== null ||
this.options.title.create !== null)
(this.options.title.edit !== null || this.options.title.create !== null)
) {
return this.context.edit
? this.options.title.edit
@@ -489,10 +485,7 @@ export default {
if (!this.context.edit) {
this.context.edit = true;
this.context.addressId = params.addressId;
console.log(
"context is now edit, with address",
params.addressId,
);
console.log("context is now edit, with address", params.addressId);
}
}
},
@@ -619,9 +612,7 @@ export default {
? this.entity.address.confidential
: false;
this.entity.selected.isNoAddress =
this.context.edit && this.entity.address.text === ""
? true
: false;
this.context.edit && this.entity.address.text === "" ? true : false;
this.entity.selected.country = this.context.edit
? this.entity.address.country
@@ -716,8 +707,7 @@ export default {
// add the address reference, if any
if (this.entity.selected.address.addressReference !== undefined) {
newAddress = Object.assign(newAddress, {
addressReference:
this.entity.selected.address.addressReference,
addressReference: this.entity.selected.address.addressReference,
});
} else {
newAddress = Object.assign(newAddress, {
@@ -737,10 +727,7 @@ export default {
});
}
if (this.validTo && null !== this.entity.selected.valid.to) {
console.log(
"add validTo in fetch body",
this.entity.selected.valid.to,
);
console.log("add validTo in fetch body", this.entity.selected.valid.to);
newAddress = Object.assign(newAddress, {
validTo: {
datetime: `${this.entity.selected.valid.to.toISOString().split("T")[0]}T00:00:00+0100`,
@@ -752,10 +739,7 @@ export default {
newPostcode = Object.assign(newPostcode, {
country: { id: this.entity.selected.country.id },
}); //TODO why not assign postcodeBody here = Object.assign(postcodeBody, {'origin': 3}); ?
console.log(
"writeNew postcode is true! newPostcode: ",
newPostcode,
);
console.log("writeNew postcode is true! newPostcode: ", newPostcode);
newAddress = Object.assign(newAddress, {
newPostcode: newPostcode,
});

View File

@@ -72,10 +72,7 @@ export default {
this.entity.addressMap.zoom,
);
} else {
this.map.setView(
lonLatForLeaflet(this.addressPoint.coordinates),
15,
);
this.map.setView(lonLatForLeaflet(this.addressPoint.coordinates), 15);
}
this.map.scrollWheelZoom.disable();
@@ -105,9 +102,7 @@ export default {
},
update() {
if (this.marker && this.entity.addressMap.center) {
this.marker.setLatLng(
lonLatForLeaflet(this.entity.addressMap.center),
);
this.marker.setLatLng(lonLatForLeaflet(this.entity.addressMap.center));
this.map.panTo(lonLatForLeaflet(this.entity.addressMap.center));
}
},

View File

@@ -76,9 +76,7 @@ export default {
props: ["entity", "context", "updateMapCenter", "flag", "checkErrors"],
data() {
return {
value: this.context.edit
? this.entity.address.addressReference
: null,
value: this.context.edit ? this.entity.address.addressReference : null,
isLoading: false,
};
},
@@ -151,8 +149,7 @@ export default {
.then(
(addresses) =>
new Promise((resolve, reject) => {
this.entity.loaded.addresses =
addresses.results;
this.entity.loaded.addresses = addresses.results;
this.isLoading = false;
resolve();
}),
@@ -169,8 +166,7 @@ export default {
.then(
(addresses) =>
new Promise((resolve, reject) => {
this.entity.loaded.addresses =
addresses.results;
this.entity.loaded.addresses = addresses.results;
this.isLoading = false;
resolve();
}),

View File

@@ -136,9 +136,7 @@ export default {
},
methods: {
transName(value) {
return value.code && value.name
? `${value.name} (${value.code})`
: "";
return value.code && value.name ? `${value.name} (${value.code})` : "";
},
selectCity(value) {
console.log(value);
@@ -146,8 +144,7 @@ export default {
this.entity.selected.postcode.name = value.name;
this.entity.selected.postcode.code = value.code;
if (value.center) {
this.entity.selected.postcode.coordinates =
value.center.coordinates;
this.entity.selected.postcode.coordinates = value.center.coordinates;
}
this.entity.selected.writeNew.postcode = false;
this.$emit("getReferenceAddresses", value);
@@ -168,8 +165,7 @@ export default {
.then(
(cities) =>
new Promise((resolve, reject) => {
this.entity.loaded.cities =
cities.results.filter(
this.entity.loaded.cities = cities.results.filter(
(c) => c.origin !== 3,
); // filter out user-defined cities
this.isLoading = false;
@@ -188,8 +184,7 @@ export default {
.then(
(cities) =>
new Promise((resolve, reject) => {
this.entity.loaded.cities =
cities.results.filter(
this.entity.loaded.cities = cities.results.filter(
(c) => c.origin !== 3,
); // filter out user-defined cities
this.isLoading = false;

View File

@@ -42,12 +42,8 @@ export default {
sortedCountries() {
const countries = this.entity.loaded.countries;
let sortedCountries = [];
sortedCountries.push(
...countries.filter((c) => c.countryCode === "FR"),
);
sortedCountries.push(
...countries.filter((c) => c.countryCode === "BE"),
);
sortedCountries.push(...countries.filter((c) => c.countryCode === "FR"));
sortedCountries.push(...countries.filter((c) => c.countryCode === "BE"));
sortedCountries.push(
...countries
.filter((c) => c.countryCode !== "FR")

View File

@@ -1,9 +1,6 @@
<template>
<div v-if="insideModal === false" class="loading">
<i
v-if="flag.loading"
class="fa fa-circle-o-notch fa-spin fa-2x fa-fw"
/>
<i v-if="flag.loading" class="fa fa-circle-o-notch fa-spin fa-2x fa-fw" />
<span class="sr-only">{{ $t("loading") }}</span>
</div>
@@ -142,8 +139,7 @@ export default {
address["street"] = this.entity.selected.address.street
? this.entity.selected.address.street
: null;
address["streetNumber"] = this.entity.selected.address
.streetNumber
address["streetNumber"] = this.entity.selected.address.streetNumber
? this.entity.selected.address.streetNumber
: null;
address["floor"] = this.entity.selected.address.floor
@@ -158,12 +154,10 @@ export default {
address["flat"] = this.entity.selected.address.flat
? this.entity.selected.address.flat
: null;
address["buildingName"] = this.entity.selected.address
.buildingName
address["buildingName"] = this.entity.selected.address.buildingName
? this.entity.selected.address.buildingName
: null;
address["distribution"] = this.entity.selected.address
.distribution
address["distribution"] = this.entity.selected.address.distribution
? this.entity.selected.address.distribution
: null;
address["extra"] = this.entity.selected.address.extra

View File

@@ -2,10 +2,7 @@
<div class="address-form">
<!-- Not display in modal -->
<div v-if="insideModal === false" class="loading">
<i
v-if="flag.loading"
class="fa fa-circle-o-notch fa-spin fa-2x fa-fw"
/>
<i v-if="flag.loading" class="fa fa-circle-o-notch fa-spin fa-2x fa-fw" />
<span class="sr-only">Loading...</span>
</div>

View File

@@ -1,10 +1,7 @@
<template>
<div v-if="!onlyButton" class="mt-4 flex-grow-1">
<div class="loading">
<i
v-if="flag.loading"
class="fa fa-circle-o-notch fa-spin fa-2x fa-fw"
/>
<i v-if="flag.loading" class="fa fa-circle-o-notch fa-spin fa-2x fa-fw" />
<span class="sr-only">{{ $t("loading") }}</span>
</div>
@@ -45,9 +42,7 @@
name="button"
:title="$t(getTextButton)"
>
<span v-if="displayTextButton">{{
$t(getTextButton)
}}</span>
<span v-if="displayTextButton">{{ $t(getTextButton) }}</span>
</button>
</template>
</action-buttons>
@@ -60,9 +55,7 @@
:use-date-pane="useDatePane"
/>
<div
v-if="this.context.target.name === 'household' || this.context.edit"
>
<div v-if="this.context.target.name === 'household' || this.context.edit">
<action-buttons :options="this.options" :defaultz="this.defaultz">
<template #action>
<button
@@ -73,9 +66,7 @@
name="button"
:title="$t(getTextButton)"
>
<span v-if="displayTextButton">{{
$t(getTextButton)
}}</span>
<span v-if="displayTextButton">{{ $t(getTextButton) }}</span>
</button>
</template>
</action-buttons>
@@ -97,9 +88,7 @@
name="button"
:title="$t(getTextButton)"
>
<span v-if="displayTextButton">{{
$t(getTextButton)
}}</span>
<span v-if="displayTextButton">{{ $t(getTextButton) }}</span>
</button>
</template>
</action-buttons>
@@ -165,9 +154,7 @@ export default {
: this.defaultz.button.text.create;
},
getSuccessText() {
return this.context.edit
? "address_edit_success"
: "address_new_success";
return this.context.edit ? "address_edit_success" : "address_new_success";
},
onlyButton() {
return typeof this.options.onlyButton !== "undefined"

View File

@@ -1,9 +1,6 @@
<template>
<div v-if="insideModal === false" class="loading">
<i
v-if="flag.loading"
class="fa fa-circle-o-notch fa-spin fa-2x fa-fw"
/>
<i v-if="flag.loading" class="fa fa-circle-o-notch fa-spin fa-2x fa-fw" />
<span class="sr-only">{{ $t("loading") }}</span>
</div>

View File

@@ -86,10 +86,7 @@ onMounted(() => {
<template>
<div id="waiting-screen">
<div
v-if="isPending && isFetching"
class="alert alert-danger text-center"
>
<div v-if="isPending && isFetching" class="alert alert-danger text-center">
<div>
<p>
{{ trans(EXPORT_GENERATION_EXPORT_GENERATION_IS_PENDING) }}

View File

@@ -67,9 +67,7 @@
@click="selectTab('MyWorkflows')"
>
{{ $t("my_workflows.tab") }}
<tab-counter
:count="state.workflows.count + state.workflowsCc.count"
/>
<tab-counter :count="state.workflows.count + state.workflowsCc.count" />
</a>
</li>
<li class="nav-item loading ms-auto py-2" v-if="loading">

View File

@@ -25,11 +25,9 @@
</template>
<template #body>
<p class="news-date">
<time
class="createdBy"
datetime="{{item.startDate.datetime}}"
>{{ $d(newsItemStartDate(), "text") }}</time
>
<time class="createdBy" datetime="{{item.startDate.datetime}}">{{
$d(newsItemStartDate(), "text")
}}</time>
</p>
<div v-html="convertMarkdownToHtml(item.content)"></div>
</template>
@@ -93,10 +91,7 @@ const truncateContent = (content: string): string => {
// Truncate if amount of lines are too many
if (lines.length > props.maxLines && content.length < props.maxLength) {
const truncatedContent = lines
.slice(0, props.maxLines)
.join("\n")
.trim();
const truncatedContent = lines.slice(0, props.maxLines).join("\n").trim();
return truncatedContent + "...";
}
@@ -125,8 +120,7 @@ const truncateContent = (content: string): string => {
if (linkStartIndex !== -1) {
const linkEndIndex = content.indexOf(")", linkStartIndex);
const url = content.slice(linkStartIndex + 1, linkEndIndex);
truncatedContent =
truncatedContent.slice(0, linkStartIndex) + `(${url})`;
truncatedContent = truncatedContent.slice(0, linkStartIndex) + `(${url})`;
}
truncatedContent += "...";

View File

@@ -20,10 +20,7 @@
<th scope="col" />
</template>
<template #tbody>
<tr
v-for="(c, i) in accompanyingCourses.results"
:key="`course-${i}`"
>
<tr v-for="(c, i) in accompanyingCourses.results" :key="`course-${i}`">
<td>{{ $d(c.openingDate.datetime, "short") }}</td>
<td>
<span
@@ -37,11 +34,7 @@
</span>
</td>
<td>
<span
v-for="p in c.participations"
class="me-1"
:key="p.person.id"
>
<span v-for="p in c.participations" class="me-1" :key="p.person.id">
<on-the-fly
:type="p.person.type"
:id="p.person.id"
@@ -52,16 +45,12 @@
</span>
</td>
<td>
<span
v-if="c.emergency"
class="badge rounded-pill bg-danger me-1"
>{{ $t("emergency") }}</span
>
<span
v-if="c.confidential"
class="badge rounded-pill bg-danger"
>{{ $t("confidential") }}</span
>
<span v-if="c.emergency" class="badge rounded-pill bg-danger me-1">{{
$t("emergency")
}}</span>
<span v-if="c.confidential" class="badge rounded-pill bg-danger">{{
$t("confidential")
}}</span>
</td>
<td>
<a class="btn btn-sm btn-show" :href="getUrl(c)">

View File

@@ -27,9 +27,7 @@
:plural="counter.accompanyingCourses"
>
<template #n>
<span>{{
counter.accompanyingCourses
}}</span>
<span>{{ counter.accompanyingCourses }}</span>
</template>
</i18n-t>
</li>

View File

@@ -20,10 +20,7 @@
<th scope="col" />
</template>
<template #tbody>
<tr
v-for="(e, i) in evaluations.results"
:key="`evaluation-${i}`"
>
<tr v-for="(e, i) in evaluations.results" :key="`evaluation-${i}`">
<td>{{ $d(e.maxDate.datetime, "short") }}</td>
<td>
{{ localizeString(e.evaluation.title) }}
@@ -31,10 +28,7 @@
<td>
<span class="chill-entity entity-social-issue">
<span class="badge bg-chill-l-gray text-dark">
{{
e.accompanyingPeriodWork.socialAction.issue
.text
}}
{{ e.accompanyingPeriodWork.socialAction.issue.text }}
</span>
</span>
<h4 class="badge-title">
@@ -58,11 +52,7 @@
</span>
</td>
<td>
<div
class="btn-group-vertical"
role="group"
aria-label="Actions"
>
<div class="btn-group-vertical" role="group" aria-label="Actions">
<a class="btn btn-sm btn-show" :href="getUrl(e)">
{{
$t("show_entity", {
@@ -72,12 +62,7 @@
</a>
<a
class="btn btn-sm btn-show"
:href="
getUrl(
e.accompanyingPeriodWork
.accompanyingPeriod,
)
"
:href="getUrl(e.accompanyingPeriodWork.accompanyingPeriod)"
>
{{
$t("show_entity", {

View File

@@ -25,9 +25,7 @@
</td>
<td v-else />
<td>
<span class="outdated">{{
$d(t.endDate.datetime, "short")
}}</span>
<span class="outdated">{{ $d(t.endDate.datetime, "short") }}</span>
</td>
<td>{{ t.title }}</td>
<td>
@@ -59,10 +57,7 @@
<th scope="col" />
</template>
<template #tbody>
<tr
v-for="(t, i) in tasks.warning.results"
:key="`task-warning-${i}`"
>
<tr v-for="(t, i) in tasks.warning.results" :key="`task-warning-${i}`">
<td>
<span class="outdated">{{
$d(t.warningDate.datetime, "short")

View File

@@ -23,9 +23,7 @@
<td>
<div class="workflow">
<div class="breadcrumb">
<i
class="fa fa-circle me-1 text-chill-yellow mx-2"
/>
<i class="fa fa-circle me-1 text-chill-yellow mx-2" />
<span class="mx-2">{{ getStep(w) }}</span>
</div>
<span

View File

@@ -37,11 +37,7 @@
</h4>
</td>
<td>
<span
v-for="person in w.persons"
class="me-1"
:key="person.id"
>
<span v-for="person in w.persons" class="me-1" :key="person.id">
<on-the-fly
:type="person.type"
:id="person.id"
@@ -52,11 +48,7 @@
</span>
</td>
<td>
<div
class="btn-group-vertical"
role="group"
aria-label="Actions"
>
<div class="btn-group-vertical" role="group" aria-label="Actions">
<a class="btn btn-sm btn-update" :href="getUrl(w)">
{{
$t("show_entity", {

View File

@@ -10,7 +10,7 @@
v-model="radioType"
value="person"
/>
{{ $t("onthefly.create.person") }}
{{ trans(ONTHEFLY_CREATE_PERSON) }}
</label>
</a>
</li>
@@ -24,7 +24,7 @@
v-model="radioType"
value="thirdparty"
/>
{{ $t("onthefly.create.thirdparty") }}
{{ trans(ONTHEFLY_CREATE_THIRDPARTY) }}
</label>
</a>
</li>
@@ -46,64 +46,66 @@
/>
</div>
</template>
<script>
<script setup>
import { ref, computed, onMounted } from "vue";
import OnTheFlyPerson from "ChillPersonAssets/vuejs/_components/OnTheFly/Person.vue";
import OnTheFlyThirdparty from "ChillThirdPartyAssets/vuejs/_components/OnTheFly/ThirdParty.vue";
import {
trans,
ONTHEFLY_CREATE_PERSON,
ONTHEFLY_CREATE_THIRDPARTY,
} from "translator";
export default {
name: "OnTheFlyCreate",
props: ["action", "allowedTypes", "query"],
components: {
OnTheFlyPerson,
OnTheFlyThirdparty,
const props = defineProps({
action: String,
allowedTypes: Array,
query: String,
});
const type = ref(null);
const radioType = computed({
get: () => type.value,
set: (val) => {
type.value = val;
console.log("## type:", val, ", action:", props.action);
},
data() {
return {
type: null,
};
},
computed: {
radioType: {
set(type) {
this.type = type;
console.log("## type:", type, ", action:", this.action);
},
get() {
return this.type;
},
},
},
mounted() {
this.type =
this.allowedTypes.length === 1 &&
this.allowedTypes.includes("thirdparty")
});
const castPerson = ref(null);
const castThirdparty = ref(null);
onMounted(() => {
type.value =
props.allowedTypes.length === 1 && props.allowedTypes.includes("thirdparty")
? "thirdparty"
: "person";
},
methods: {
isActive(tab) {
return this.type === tab ? true : false;
},
castDataByType() {
switch (this.radioType) {
});
function isActive(tab) {
return type.value === tab;
}
function castDataByType() {
switch (radioType.value) {
case "person":
return this.$refs.castPerson.$data.person;
return castPerson.value.$data.person;
case "thirdparty":
let data = this.$refs.castThirdparty.$data.thirdparty;
let data = castThirdparty.value.$data.thirdparty;
if (data.address !== undefined && data.address !== null) {
data.address = { id: data.address.address_id };
} else {
data.address = null;
}
return data;
default:
throw Error("Invalid type of entity");
}
},
},
};
}
defineExpose({
castDataByType,
});
</script>
<style lang="css" scoped>

View File

@@ -9,7 +9,7 @@
class="btn btn-sm"
target="_blank"
:class="classAction"
:title="$t(titleAction)"
:title="trans(titleAction)"
@click="openModal"
>
{{ buttonText }}<span v-if="isDead"> ()</span>
@@ -23,10 +23,10 @@
>
<template #header>
<h3 v-if="parent" class="modal-title">
{{ $t(titleModal, { q: parent.text }) }}
{{ trans(titleModal, { q: parent.text }) }}
</h3>
<h3 v-else class="modal-title">
{{ $t(titleModal) }}
{{ trans(titleModal) }}
</h3>
</template>
@@ -38,7 +38,7 @@
ref="castPerson"
/>
<div v-if="hasResourceComment">
<h3>{{ $t("onthefly.resource_comment_title") }}</h3>
<h3>{{ trans(ONTHEFLY_RESOURCE_COMMENT_TITLE) }}</h3>
<blockquote class="chill-user-quote">
{{ parent.comment }}
</blockquote>
@@ -53,7 +53,7 @@
ref="castThirdparty"
/>
<div v-if="hasResourceComment">
<h3>{{ $t("onthefly.resource_comment_title") }}</h3>
<h3>{{ trans(ONTHEFLY_RESOURCE_COMMENT_TITLE) }}</h3>
<blockquote class="chill-user-quote">
{{ parent.comment }}
</blockquote>
@@ -82,65 +82,82 @@
<a
v-if="action === 'show'"
:href="buildLocation(id, type)"
:title="$t(titleMessage)"
:title="trans(titleMessage)"
class="btn btn-show"
>{{ $t(buttonMessage) }}
>{{ trans(buttonMessage) }}
</a>
<a v-else class="btn btn-save" @click="saveAction">
{{ $t("action.save") }}
{{ trans(ACTION_SAVE) }}
</a>
</template>
</modal>
</teleport>
</template>
<script>
<script setup>
import { ref, computed, defineEmits, defineProps } from "vue";
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
import OnTheFlyCreate from "./Create.vue";
import OnTheFlyPerson from "ChillPersonAssets/vuejs/_components/OnTheFly/Person.vue";
import OnTheFlyThirdparty from "ChillThirdPartyAssets/vuejs/_components/OnTheFly/ThirdParty.vue";
import {
trans,
ACTION_SHOW,
ACTION_EDIT,
ACTION_CREATE,
ACTION_ADDCONTACT,
ONTHEFLY_CREATE_TITLE_DEFAULT,
ONTHEFLY_CREATE_TITLE_PERSON,
ONTHEFLY_CREATE_TITLE_THIRDPARTY,
ONTHEFLY_SHOW_PERSON,
ONTHEFLY_SHOW_THIRDPARTY,
ONTHEFLY_EDIT_PERSON,
ONTHEFLY_EDIT_THIRDPARTY,
ONTHEFLY_ADDCONTACT_TITLE,
ACTION_REDIRECT_PERSON,
ACTION_REDIRECT_THIRDPARTY,
ONTHEFLY_SHOW_FILE_PERSON,
ONTHEFLY_SHOW_FILE_THIRDPARTY,
ONTHEFLY_SHOW_FILE_DEFAULT,
ONTHEFLY_RESOURCE_COMMENT_TITLE,
ACTION_SAVE,
} from "translator";
export default {
name: "OnTheFly",
components: {
Modal,
OnTheFlyPerson,
OnTheFlyThirdparty,
OnTheFlyCreate,
},
props: [
"type",
"id",
"action",
"buttonText",
"displayBadge",
"isDead",
"parent",
"allowedTypes",
"query",
],
emits: ["saveFormOnTheFly"],
data() {
return {
modal: {
const props = defineProps({
type: String,
id: [String, Number],
action: String,
buttonText: String,
displayBadge: Boolean,
isDead: Boolean,
parent: Object,
allowedTypes: Array,
query: String,
});
const emit = defineEmits(["saveFormOnTheFly"]);
const modal = ref({
showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-xl",
},
};
},
computed: {
hasResourceComment() {
});
const castPerson = ref();
const castThirdparty = ref();
const castNew = ref();
const hasResourceComment = computed(() => {
return (
typeof this.parent !== "undefined" &&
this.parent !== null &&
this.action === "show" &&
this.parent.type === "accompanying_period_resource" &&
this.parent.comment !== null &&
this.parent.comment !== ""
typeof props.parent !== "undefined" &&
props.parent !== null &&
props.action === "show" &&
props.parent.type === "accompanying_period_resource" &&
props.parent.comment !== null &&
props.parent.comment !== ""
);
},
classAction() {
switch (this.action) {
});
const classAction = computed(() => {
switch (props.action) {
case "show":
return "btn-show";
case "edit":
@@ -152,114 +169,125 @@ export default {
default:
return "";
}
},
titleAction() {
switch (this.action) {
});
const titleAction = computed(() => {
switch (props.action) {
case "show":
return "action.show";
return ACTION_SHOW;
case "edit":
return "action.edit";
return ACTION_EDIT;
case "create":
return "action.create";
return ACTION_CREATE;
case "addContact":
return "action.addContact";
return ACTION_ADDCONTACT;
default:
return "";
}
},
titleCreate() {
if (typeof this.allowedTypes === "undefined") {
return "onthefly.create.title.default";
});
const titleCreate = computed(() => {
if (typeof props.allowedTypes === "undefined") {
return ONTHEFLY_CREATE_TITLE_DEFAULT;
}
return this.allowedTypes.every((t) => t === "person")
? "onthefly.create.title.person"
: this.allowedTypes.every((t) => t === "thirdparty")
? "onthefly.create.title.thirdparty"
: "onthefly.create.title.default";
},
titleModal() {
switch (this.action) {
return props.allowedTypes.every((t) => t === "person")
? ONTHEFLY_CREATE_TITLE_PERSON
: props.allowedTypes.every((t) => t === "thirdparty")
? ONTHEFLY_CREATE_TITLE_THIRDPARTY
: ONTHEFLY_CREATE_TITLE_DEFAULT;
});
const titleModal = computed(() => {
switch (props.action) {
case "show":
return "onthefly.show." + this.type;
if (props.type == "person") {
return ONTHEFLY_SHOW_PERSON;
} else if (props.type == "thirdparty") {
return ONTHEFLY_SHOW_THIRDPARTY;
}
break;
case "edit":
return "onthefly.edit." + this.type;
if (props.type == "person") {
return ONTHEFLY_EDIT_PERSON;
} else if (props.type == "thirdparty") {
return ONTHEFLY_EDIT_THIRDPARTY;
}
break;
case "create":
return this.titleCreate;
return titleCreate.value;
case "addContact":
return "onthefly.addContact.title";
return ONTHEFLY_ADDCONTACT_TITLE;
default:
break;
}
return "";
});
const titleMessage = computed(() => {
switch (props.type) {
case "person":
return ACTION_REDIRECT_PERSON;
case "thirdparty":
return ACTION_REDIRECT_THIRDPARTY;
default:
return "";
}
},
titleMessage() {
switch (this.type) {
});
const buttonMessage = computed(() => {
switch (props.type) {
case "person":
return "action.redirect." + this.type;
return ONTHEFLY_SHOW_FILE_PERSON;
case "thirdparty":
return "action.redirect." + this.type;
return ONTHEFLY_SHOW_FILE_THIRDPARTY;
default:
return "";
return ONTHEFLY_SHOW_FILE_DEFAULT;
}
},
buttonMessage() {
switch (this.type) {
case "person":
return "onthefly.show.file_" + this.type;
case "thirdparty":
return "onthefly.show.file_" + this.type;
}
},
isDisplayBadge() {
return this.displayBadge === true && this.buttonText !== null;
},
badgeType() {
return "entity-" + this.type + " badge-" + this.type;
},
getReturnPath() {
});
const isDisplayBadge = computed(() => {
return props.displayBadge === true && props.buttonText !== null;
});
const badgeType = computed(() => {
return "entity-" + props.type + " badge-" + props.type;
});
const getReturnPath = computed(() => {
return `?returnPath=${window.location.pathname}${window.location.search}${window.location.hash}`;
},
},
methods: {
closeModal() {
this.modal.showModal = false;
},
openModal() {
// console.log('## OPEN ON THE FLY MODAL');
// console.log('## type:', this.type, ', action:', this.action);
this.modal.showModal = true;
this.$nextTick(function () {
//this.$refs.search.focus();
});
},
changeActionTo(action) {
this.$data.action = action;
},
saveAction() {
// console.log('saveAction button: create/edit action with', this.type);
let type = this.type,
});
function closeModal() {
modal.value.showModal = false;
}
function openModal() {
modal.value.showModal = true;
}
function changeActionTo(action) {
console.log(action);
// Not reactive in setup, but you can emit or use a ref if needed
}
function saveAction() {
let type = props.type,
data = {};
switch (type) {
case "person":
data = this.$refs.castPerson.$data.person;
console.log("person data are", data);
data = castPerson.value?.$data.person;
break;
case "thirdparty":
data = this.$refs.castThirdparty.$data.thirdparty;
/* never executed ? */
data = castThirdparty.value?.$data.thirdparty;
break;
default:
if (typeof this.type === "undefined") {
// action=create or addContact
// console.log('will rewrite data');
if (this.action === "addContact") {
if (typeof props.type === "undefined") {
if (props.action === "addContact") {
type = "thirdparty";
data = this.$refs.castThirdparty.$data.thirdparty;
// console.log('data original', data);
data = castThirdparty.value?.$data.thirdparty;
data.parent = {
type: "thirdparty",
id: this.parent.id,
id: props.parent.id,
};
data.civility =
data.civility !== null
@@ -268,16 +296,11 @@ export default {
id: data.civility.id,
}
: null;
data.profession =
data.profession !== "" ? data.profession : "";
data.profession = data.profession !== "" ? data.profession : "";
} else {
type = this.$refs.castNew.radioType;
data = this.$refs.castNew.castDataByType();
// console.log('type', type);
if (
typeof data.civility !== "undefined" &&
null !== data.civility
) {
type = castNew.value.radioType;
data = castNew.value.castDataByType();
if (typeof data.civility !== "undefined" && null !== data.civility) {
data.civility =
data.civility !== null
? {
@@ -290,34 +313,37 @@ export default {
typeof data.profession !== "undefined" &&
"" !== data.profession
) {
data.profession =
data.profession !== ""
? data.profession
: "";
data.profession = data.profession !== "" ? data.profession : "";
}
// console.log('onthefly data', data);
}
} else {
throw "error with object type";
}
}
// pass datas to parent
this.$emit("saveFormOnTheFly", { type: type, data: data });
},
buildLocation(id, type) {
emit("saveFormOnTheFly", { type: type, data: data });
}
function buildLocation(id, type) {
if (type === "person") {
// TODO i18n
return encodeURI(
`/fr/person/${id}/general${this.getReturnPath}`,
);
return encodeURI(`/fr/person/${id}/general${getReturnPath.value}`);
} else if (type === "thirdparty") {
return encodeURI(
`/fr/3party/3party/${id}/view${this.getReturnPath}`,
);
return encodeURI(`/fr/3party/3party/${id}/view${getReturnPath.value}`);
}
},
},
};
}
defineExpose({
openModal,
closeModal,
changeActionTo,
saveAction,
castPerson,
castThirdparty,
castNew,
hasResourceComment,
modal,
isDisplayBadge,
Modal,
});
</script>
<style lang="css" scoped>

View File

@@ -1,22 +1,34 @@
<template>
<ul :class="listClasses" v-if="picked.length && displayPicked">
<li v-for="p in picked" @click="removeEntity(p)" :key="p.type + p.id">
<div class="grey-card">
<ul :class="listClasses" v-if="picked.length > 0 && displayPicked">
<li
v-for="p in picked"
@click="removeEntity(p)"
:key="p === 'me' ? 'me' : p.type + p.id"
>
<span
v-if="'me' === p"
class="chill_denomination current-user updatedBy"
>{{ trans(USER_CURRENT_USER) }}</span
>
<span v-else class="chill_denomination">{{ p.text }}</span>
<span
v-else
:class="getBadgeClass(p)"
class="chill_denomination"
:style="getBadgeStyle(p)"
>
{{ p.text }}
</span>
</li>
</ul>
<ul class="record_actions">
<li v-if="isCurrentUserPicker" class="btn btn-sm btn-misc">
<label class="flex items-center gap-2">
<input
:checked="picked.indexOf('me') >= 0 ? true : null"
:checked="isMePicked"
ref="itsMeCheckbox"
:type="multiple ? 'checkbox' : 'radio'"
@change="selectItsMe"
@change="selectItsMe($event as InputEvent)"
/>
{{ trans(USER_CURRENT_USER) }}
</label>
@@ -27,94 +39,287 @@
:key="uniqid"
:buttonTitle="translatedListOfTypes"
:modalTitle="translatedListOfTypes"
ref="addPersons"
@addNewPersons="addNewEntity"
>
</add-persons>
</li>
</ul>
<ul class="list-suggest add-items inline">
<li v-for="s in suggested" :key="s.id" @click="addNewSuggested(s)">
<span>{{ s.text }}</span>
<ul class="badge-suggest add-items inline" style="float: right">
<li
v-for="s in suggested"
:key="s.type + s.id"
@click="addNewSuggested(s)"
>
<span :class="getBadgeClass(s)" :style="getBadgeStyle(s)">
{{ s.text }}
</span>
</li>
</ul>
</div>
</template>
<script setup>
import { ref, computed } from "vue";
import AddPersons from "ChillPersonAssets/vuejs/_components/AddPersons.vue"; // eslint-disable-line
import { appMessages } from "./i18n";
import { trans, USER_CURRENT_USER } from "translator";
<script lang="ts" setup>
import {
ref,
computed,
defineProps,
defineEmits,
defineComponent,
withDefaults,
} from "vue";
import AddPersons from "ChillPersonAssets/vuejs/_components/AddPersons.vue";
import {
Entities,
EntitiesOrMe,
EntityType,
SearchOptions,
} from "ChillPersonAssets/types";
import {
PICK_ENTITY_MODAL_TITLE,
PICK_ENTITY_USER,
PICK_ENTITY_USER_GROUP,
PICK_ENTITY_PERSON,
PICK_ENTITY_THIRDPARTY,
USER_CURRENT_USER,
trans,
} from "translator";
import { addNewEntities } from "ChillMainAssets/types";
const props = defineProps({
multiple: Boolean,
types: Array,
picked: Array,
uniqid: String,
removableIfSet: { type: Boolean, default: true },
displayPicked: { type: Boolean, default: true },
suggested: { type: Array, default: () => [] },
label: String,
isCurrentUserPicker: { type: Boolean, default: false },
defineComponent({
components: {
AddPersons,
},
});
const props = withDefaults(
defineProps<{
multiple: boolean;
types: EntityType[];
picked: EntitiesOrMe[];
uniqid: string;
removableIfSet?: boolean;
displayPicked?: boolean;
suggested?: Entities[];
label?: string;
isCurrentUserPicker?: boolean;
}>(),
{ isCurrentUserPicker: false, displayPicked: true, removableIfSet: true },
);
const emit = defineEmits([
"addNewEntity",
"removeEntity",
"addNewEntityProcessEnded",
]);
const emits = defineEmits<{
(e: "addNewEntity", payload: { entity: EntitiesOrMe }): void;
(e: "removeEntity", payload: { entity: EntitiesOrMe }): void;
(e: "addNewEntityProcessEnded"): void;
}>();
const itsMeCheckbox = ref(null);
const addPersons = ref(null);
const itsMeCheckbox = ref<null | HTMLInputElement>(null);
const addPersons = ref();
const addPersonsOptions = computed(() => ({
const addPersonsOptions = computed(
() =>
({
uniq: !props.multiple,
type: props.types,
priority: null,
button: { size: "btn-sm", class: "btn-submit" },
}));
button: {
size: "btn-sm",
class: "btn-submit",
},
}) as SearchOptions,
);
const isMePicked = computed<boolean>(() => props.picked.indexOf("me") >= 0);
const translatedListOfTypes = computed(() => {
if (props.label) return props.label;
let trans = props.types.map((t) =>
props.multiple
? appMessages.fr.pick_entity[t].toLowerCase()
: appMessages.fr.pick_entity[t + "_one"].toLowerCase(),
);
return props.multiple
? appMessages.fr.pick_entity.modal_title + trans.join(", ")
: appMessages.fr.pick_entity.modal_title_one + trans.join(", ");
if (props.label !== undefined && props.label !== "") {
return props.label;
}
const translatedTypes = props.types.map((type: EntityType) => {
switch (type) {
case "user":
return trans(PICK_ENTITY_USER, {
count: props.multiple ? 2 : 1,
});
case "person":
return trans(PICK_ENTITY_PERSON, {
count: props.multiple ? 2 : 1,
});
case "thirdparty":
return trans(PICK_ENTITY_THIRDPARTY, {
count: props.multiple ? 2 : 1,
});
case "user_group":
return trans(PICK_ENTITY_USER_GROUP, {
count: props.multiple ? 2 : 1,
});
}
});
return `${trans(PICK_ENTITY_MODAL_TITLE, {
count: props.multiple ? 2 : 1,
})} ${translatedTypes.join(", ")}`;
});
const listClasses = computed(() => ({
"list-suggest": true,
"badge-suggest": true,
"remove-items": props.removableIfSet,
inline: true,
}));
const selectItsMe = (event) =>
event.target.checked ? addNewSuggested("me") : removeEntity("me");
const addNewSuggested = (entity) => {
emit("addNewEntity", { entity });
const selectItsMe = (event: InputEvent): void => {
const target = event.target as HTMLInputElement;
if (target.checked) {
addNewSuggested("me");
} else {
removeEntity("me");
}
};
const addNewEntity = ({ selected, modal }) => {
selected.forEach((item) => emit("addNewEntity", { entity: item.result }));
function addNewSuggested(entity: EntitiesOrMe) {
emits("addNewEntity", { entity });
}
function addNewEntity({ selected }: addNewEntities) {
Object.values(selected).forEach((item) => {
emits("addNewEntity", { entity: item.result });
});
addPersons.value?.resetSearch();
modal.showModal = false;
emit("addNewEntityProcessEnded");
};
const removeEntity = (entity) => {
emits("addNewEntityProcessEnded");
}
const removeEntity = (entity: EntitiesOrMe): void => {
if (!props.removableIfSet) return;
if (entity === "me" && itsMeCheckbox.value) {
itsMeCheckbox.value.checked = false;
}
emit("removeEntity", { entity });
emits("removeEntity", { entity });
};
function getBadgeClass(entities: Entities) {
if (entities.type !== "user_group") {
return entities.type;
}
return "";
}
function getBadgeStyle(entities: Entities) {
if (entities.type === "user_group") {
return [
`ul.badge-suggest li > span {
color: ${entities.foregroundColor}!important;
border-bottom-color: ${entities.backgroundColor};
}`,
];
}
return [];
}
</script>
<style lang="scss" scoped>
.grey-card {
background: #f8f9fa;
border-radius: 8px;
padding: 1.5rem;
min-height: 160px;
}
.btn-check:checked + .btn,
:not(.btn-check) + .btn:active,
.btn:first-child:active,
.btn.active,
.btn.show {
color: white;
box-shadow: 0 0 0 0.2rem var(--bs-chill-green);
outline: 0;
}
.as-user-group {
display: inline-block;
}
ul.badge-suggest {
list-style-type: none;
padding-left: 0;
margin-bottom: 0px;
}
ul.badge-suggest li > span {
white-space: normal;
text-align: start;
margin-bottom: 3px;
}
ul.badge-suggest.inline li {
display: inline-block;
margin-right: 0.2em;
}
ul.badge-suggest.add-items li {
position: relative;
}
ul.badge-suggest.add-items li span {
cursor: pointer;
padding-left: 2rem;
}
ul.badge-suggest.add-items li span:hover {
color: #ced4da;
}
ul.badge-suggest.add-items li > span:before {
font: normal normal normal 13px ForkAwesome;
margin-right: 1.8em;
content: "\f067";
color: var(--bs-success);
position: absolute;
display: block;
top: 50%;
left: 0.75rem;
-webkit-transform: translateY(-50%);
-moz-transform: translateY(-50%);
-ms-transform: translateY(-50%);
transform: translateY(-50%);
}
ul.badge-suggest.remove-items li {
position: relative;
}
ul.badge-suggest.remove-items li span {
cursor: pointer;
padding-left: 2rem;
}
ul.badge-suggest.remove-items li span:hover {
color: #ced4da;
}
ul.badge-suggest.remove-items li > span:before {
font: normal normal normal 13px ForkAwesome;
margin-right: 1.8em;
content: "\f1f8";
color: var(--bs-danger);
position: absolute;
display: block;
top: 50%;
left: 0.75rem;
-webkit-transform: translateY(-50%);
-moz-transform: translateY(-50%);
-ms-transform: translateY(-50%);
transform: translateY(-50%);
}
ul.badge-suggest li > span {
margin: 0.2rem 0.1rem;
display: inline-block;
padding: 0 1em 0 2.2em !important;
background-color: #fff;
border: 1px solid #dee2e6;
border-bottom-width: 3px;
border-bottom-style: solid;
border-radius: 6px;
font-size: 0.75em;
font-weight: 700;
}
ul.badge-suggest li > span.person {
border-bottom-color: #43b29d;
}
ul.badge-suggest li > span.thirdparty {
border-bottom-color: rgb(198.9, 72, 98.1);
}
.current-user {
color: var(--bs-body-color);
background-color: var(--bs-chill-l-gray) !important;

View File

@@ -61,9 +61,7 @@ export default {
},
methods: {
transName(value) {
return value.code && value.name
? `${value.name} (${value.code})`
: "";
return value.code && value.name ? `${value.name} (${value.code})` : "";
},
selectCity(city) {
this.$emit("selectCity", city);
@@ -86,9 +84,7 @@ export default {
searchCities(query, this.country, controller)
.then((newCities) => {
this.cities = this.cities.filter(
(city) => city.id === this.picked,
);
this.cities = this.cities.filter((city) => city.id === this.picked);
newCities.forEach((item) => {
this.cities.push(item);
});

View File

@@ -10,9 +10,7 @@ const props = defineProps<SavedExportButtonsConfig>();
</script>
<template>
<generate-button
:saved-export-uuid="props.savedExportUuid"
></generate-button>
<generate-button :saved-export-uuid="props.savedExportUuid"></generate-button>
</template>
<style scoped lang="scss"></style>

View File

@@ -125,9 +125,7 @@ const onObjectNewStatusCallback = async function (): Promise<void> {
const onClickGenerate = async (): Promise<void> => {
emits("generate");
exportGeneration.value = await generateFromSavedExport(
props.savedExportUuid,
);
exportGeneration.value = await generateFromSavedExport(props.savedExportUuid);
onObjectNewStatusCallback();

View File

@@ -110,9 +110,7 @@ const filteredDocuments = computed<GenericDocForAccompanyingPeriod[]>(() => {
return false;
}
return needles.every((n: string) =>
title.toLowerCase().includes(n),
);
return needles.every((n: string) => title.toLowerCase().includes(n));
})
.filter((genericDoc: GenericDocForAccompanyingPeriod) => {
if (placesFilter.value.length === 0) {
@@ -153,8 +151,7 @@ const filteredDocuments = computed<GenericDocForAccompanyingPeriod[]>(() => {
aria-controls="filterOrderCollapse"
>
<strong
><i class="fa fa-fw fa-filter"></i>Filtrer la
liste</strong
><i class="fa fa-fw fa-filter"></i>Filtrer la liste</strong
>
</button>
</h2>
@@ -179,10 +176,7 @@ const filteredDocuments = computed<GenericDocForAccompanyingPeriod[]>(() => {
placeholder="Chercher dans la liste"
class="form-control"
/>
<button
type="submit"
class="btn btn-misc"
>
<button type="submit" class="btn btn-misc">
<i class="fa fa-search"></i>
</button>
</div>
@@ -190,9 +184,7 @@ const filteredDocuments = computed<GenericDocForAccompanyingPeriod[]>(() => {
</div>
<div class="row my-2">
<legend
class="col-form-label col-sm-4 required"
>
<legend class="col-form-label col-sm-4 required">
Date du document
</legend>
<div class="col-sm-8 pt-1">
@@ -218,15 +210,9 @@ const filteredDocuments = computed<GenericDocForAccompanyingPeriod[]>(() => {
</div>
<div class="row my-2">
<div class="col-sm-4 col-form-label">
Filtrer par
</div>
<div class="col-sm-4 col-form-label">Filtrer par</div>
<div class="col-sm-8 pt-2">
<div
class="form-check"
v-for="p in availablePlaces"
:key="p"
>
<div class="form-check" v-for="p in availablePlaces" :key="p">
<input
type="checkbox"
v-model="placesFilter"
@@ -234,9 +220,7 @@ const filteredDocuments = computed<GenericDocForAccompanyingPeriod[]>(() => {
class="form-check-input"
:value="p"
/>
<label class="form-check-label">{{
placeTrans(p)
}}</label>
<label class="form-check-label">{{ placeTrans(p) }}</label>
</div>
</div>
</div>

View File

@@ -81,16 +81,8 @@ const clickOnAddButton = () => {
rgba(25, 135, 84, 1) 0px,
rgba(25, 135, 84, 0) 9px
),
linear-gradient(
0deg,
rgba(25, 135, 84, 1) 0px,
rgba(25, 135, 84, 0) 9px
),
linear-gradient(
90deg,
rgba(25, 135, 84, 1) 0px,
rgba(25, 135, 84, 0) 9px
);
linear-gradient(0deg, rgba(25, 135, 84, 1) 0px, rgba(25, 135, 84, 0) 9px),
linear-gradient(90deg, rgba(25, 135, 84, 1) 0px, rgba(25, 135, 84, 0) 9px);
}
}
</style>

View File

@@ -97,8 +97,8 @@ defineExpose({ openModal, closeModal });
@click="onConfirm"
>
<template v-if="numberOfPicked > 1">
<i class="fa fa-plus"></i> Ajouter
{{ numberOfPicked }} pièces jointes
<i class="fa fa-plus"></i> Ajouter {{ numberOfPicked }} pièces
jointes
</template>
<template v-else>
<i class="fa fa-plus"></i> Ajouter une pièce jointe

View File

@@ -81,17 +81,12 @@ window.addEventListener("DOMContentLoaded", () => {
"this document is already attached to the workflow",
genericDoc,
);
this.$toast.error(
"Ce document est déjà attaché au workflow",
);
this.$toast.error("Ce document est déjà attaché au workflow");
return;
}
try {
const attachment = await create_attachment(
workflowId,
genericDoc,
);
const attachment = await create_attachment(workflowId, genericDoc);
this.$data.attachments.push(attachment);
} catch (error) {
console.error(error);

View File

@@ -1,7 +1,6 @@
<template>
<div v-if="props.address.isNoAddress" class="alert alert-info">
Cette adresse est incomplète. La position géographique est
approximative.
Cette adresse est incomplète. La position géographique est approximative.
</div>
<div
v-if="props.address.point !== null"
@@ -10,9 +9,7 @@
></div>
<p>
Voir sur
<a :href="makeUrlGoogleMap(props.address)" target="_blank"
>Google Maps</a
>
<a :href="makeUrlGoogleMap(props.address)" target="_blank">Google Maps</a>
<a :href="makeUrlOsm(props.address)" target="_blank">OSM</a>
</p>
</template>

View File

@@ -12,8 +12,7 @@
}"
>
<p v-if="props.address.refStatus === 'to_review'">
<i class="fa fa-warning"></i> L'adresse de référence a été
modifiée.
<i class="fa fa-warning"></i> L'adresse de référence a été modifiée.
</p>
<p v-if="props.address.refStatus === 'reviewed'">
L'adresse est conservée, mais diffère de l'adresse de référence.
@@ -21,8 +20,7 @@
<template
v-if="
props.address.addressReference.street !==
props.address.street ||
props.address.addressReference.street !== props.address.street ||
props.address.addressReference.streetNumber !==
props.address.streetNumber
"
@@ -30,22 +28,18 @@
<template v-if="props.address.country.code === 'BE'">
<div class="difference">
<span class="old"
>{{ props.address.street }}
{{ props.address.streetNumber }}</span
>{{ props.address.street }} {{ props.address.streetNumber }}</span
>
<span class="new"
>{{ props.address.addressReference.street }}
{{
props.address.addressReference.streetNumber
}}</span
{{ props.address.addressReference.streetNumber }}</span
>
</div>
</template>
<template v-else>
<div class="difference">
<span class="old"
>{{ props.address.streetNumber }}
{{ props.address.street }}</span
>{{ props.address.streetNumber }} {{ props.address.street }}</span
>
<span class="new"
>{{ props.address.addressReference.streetNumber }}
@@ -88,12 +82,8 @@
{{ props.address.point.coordinates[1] }}</span
>
<span class="new"
>{{
props.address.addressReference.point.coordinates[0]
}}
{{
props.address.addressReference.point.coordinates[1]
}}</span
>{{ props.address.addressReference.point.coordinates[0] }}
{{ props.address.addressReference.point.coordinates[1] }}</span
>
</div>
</template>
@@ -105,18 +95,12 @@
</button>
</li>
<li v-if="props.address.refStatus === 'to_review'">
<button
class="btn btn-sm btn-primary"
@click="keepCurrentAddress"
>
<button class="btn btn-sm btn-primary" @click="keepCurrentAddress">
Conserver
</button>
</li>
<li v-if="props.address.refStatus === 'reviewed'">
<button
class="btn btn-sm btn-primary"
@click="backToReview"
>
<button class="btn btn-sm btn-primary" @click="backToReview">
Ré-examiner
</button>
</li>

View File

@@ -32,10 +32,7 @@
</template>
</span>
<span
v-if="props.entity.type === 'user'"
class="badge rounded-pill bg-user"
>
<span v-if="props.entity.type === 'user'" class="badge rounded-pill bg-user">
{{ trans(ACCEPTED_USERS) }}
</span>

View File

@@ -49,10 +49,7 @@ onMounted(function () {
const storage = window.localStorage;
const savedKind = storage.getItem(EDITOR_MODE_KEY);
if (
null !== kind.value &&
(savedKind === "simple" || savedKind === "rich")
) {
if (null !== kind.value && (savedKind === "simple" || savedKind === "rich")) {
kind.value = savedKind;
}
@@ -76,10 +73,7 @@ onUnmounted(function () {
/>
</div>
<div v-else>
<textarea
v-model="value"
class="form-control simple-editor"
></textarea>
<textarea v-model="value" class="form-control simple-editor"></textarea>
</div>
<button :class="toggleButtonClass" type="button" @click="toggleEditor">

View File

@@ -5,10 +5,7 @@
<confidential :position-btn-far="true">
<template #confidential-content>
<div v-if="isMultiline === true">
<p
v-for="(l, i) in address.lines"
:key="`line-${i}`"
>
<p v-for="(l, i) in address.lines" :key="`line-${i}`">
{{ l }}
</p>
<p v-if="showButtonDetails">
@@ -22,10 +19,7 @@
<p v-if="'' !== address.text" class="street">
{{ address.text }}
</p>
<p
v-if="null !== address.postcode"
class="postcode"
>
<p v-if="null !== address.postcode" class="postcode">
{{ address.postcode.code }}
{{ address.postcode.name }}
</p>

View File

@@ -1,9 +1,28 @@
<template>
<i :class="gender.icon" />
<i :class="['fa', genderClass, 'px-1']" />
</template>
<script setup>
import { computed } from "vue";
const props = defineProps({
gender: Object,
gender: {
type: Object,
required: true,
},
});
const genderClass = computed(() => {
switch (props.gender.genderTranslation) {
case "woman":
return "fa-venus";
case "man":
return "fa-mars";
case "both":
return "fa-neuter";
case "unknown":
return "fa-genderless";
default:
return "fa-genderless";
}
});
</script>

View File

@@ -33,16 +33,11 @@
class="fa fa-circle me-1 text-chill-yellow"
>
</i>
<i
v-if="step.isFreezed"
class="fa fa-snowflake-o fa-sm me-1"
>
<i v-if="step.isFreezed" class="fa fa-snowflake-o fa-sm me-1">
</i>
{{ step.currentStep.text }}
</span>
<span v-if="j !== Object.keys(w.steps).length - 1">
</span>
<span v-if="j !== Object.keys(w.steps).length - 1"> </span>
</template>
</div>
<span

View File

@@ -31,12 +31,8 @@
:relatedEntityClass="props.relatedEntityClass"
:relatedEntityId="props.relatedEntityId"
:workflowsAvailables="props.workflowsAvailables"
:preventDefaultMoveToGenerate="
props.preventDefaultMoveToGenerate
"
:goToGenerateWorkflowPayload="
props.goToGenerateWorkflowPayload
"
:preventDefaultMoveToGenerate="props.preventDefaultMoveToGenerate"
:goToGenerateWorkflowPayload="props.goToGenerateWorkflowPayload"
:countExistingWorkflows="countWorkflows"
:embedded-within-list-modal="true"
@go-to-generate-workflow="goToGenerateWorkflow"

View File

@@ -42,21 +42,14 @@
data-bs-toggle="dropdown"
aria-expanded="false"
>
<span class="visually-hidden"
>Liste des workflows disponibles</span
>
<span class="visually-hidden">Liste des workflows disponibles</span>
</button>
<ul
class="dropdown-menu"
aria-labelledby="createWorkflowButton"
>
<ul class="dropdown-menu" aria-labelledby="createWorkflowButton">
<li v-for="w in props.workflowsAvailables" :key="w.name">
<button
class="dropdown-item"
type="button"
@click.prevent="
goToGenerateWorkflow($event, w.name)
"
@click.prevent="goToGenerateWorkflow($event, w.name)"
>
{{ w.text }}
</button>

View File

@@ -1,6 +1,6 @@
<template>
<transition name="modal">
<div class="modal-mask">
<div class="modal-mask" v-if="show">
<!-- :: styles bootstrap :: -->
<div
class="modal fade show"
@@ -8,7 +8,7 @@
aria-modal="true"
role="dialog"
>
<div class="modal-dialog" :class="props.modalDialogClass || {}">
<div class="modal-dialog" :class="modalDialogClass || {}">
<div class="modal-content">
<div class="modal-header">
<slot name="header"></slot>
@@ -23,10 +23,7 @@
<slot name="body"></slot>
</div>
<div class="modal-footer" v-if="!hideFooter">
<button
class="btn btn-cancel"
@click="emits('close')"
>
<button class="btn btn-cancel" @click="emits('close')">
{{ trans(MODAL_ACTION_CLOSE) }}
</button>
<slot name="footer"></slot>
@@ -53,14 +50,23 @@ import { trans, MODAL_ACTION_CLOSE } from "translator";
import { defineProps } from "vue";
export interface ModalProps {
modalDialogClass: object | null;
modalDialogClass: string;
hideFooter: boolean;
}
// Define the props
const props = withDefaults(defineProps<ModalProps>(), {
hideFooter: false,
modalDialogClass: null,
defineProps({
modalDialogClass: {
type: String,
default: "",
},
hideFooter: {
type: Boolean,
default: false,
},
show: {
type: Boolean,
default: true,
},
});
const emits = defineEmits<{

View File

@@ -78,8 +78,7 @@ export const multiSelectMessages = {
tag_placeholder: "Créer un nouvel élément",
select_label: '"Entrée" ou cliquez pour sélectionner',
deselect_label: '"Entrée" ou cliquez pour désélectionner',
select_group_label:
'Appuyer sur "Entrée" pour sélectionner ce groupe',
select_group_label: 'Appuyer sur "Entrée" pour sélectionner ce groupe',
deselect_group_label:
'Appuyer sur "Entrée" pour désélectionner ce groupe',
selected_label: "Sélectionné",

View File

@@ -0,0 +1,21 @@
<html>
<head>
<title>Test mercure</title>
</head>
<body>
<form method="GET">
<input type="text" name="eventSource" value="" />
<button type="submit" name="submit" value="Submit">Submit</button>
</form>
<script>
{% if app.request.query.has('eventSource') %}
const eventSource = new EventSource("{{ mercure(app.request.query.get('eventSource'))|escape('js') }}");
eventSource.onmessage = event => {
// Will be called every time an update is published by the server
console.log(JSON.parse(event.data));
}
{% endif %}
</script>
</body>
</html>

View File

@@ -69,6 +69,7 @@
</div>
{% endif %}
{% block wrapping_content %}
{% block content %}
<div class="col-8 main_search">
{% if app.user.isAbsent %}
@@ -99,6 +100,7 @@
{% include '@ChillMain/Homepage/index.html.twig' %}
{% endblock %}
{% endblock %}
</div>
{% endblock %}

View File

@@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\MainBundle\Tests\Phonenumber;
use Chill\MainBundle\Phonenumber\PhonenumberHelper;
use libphonenumber\PhoneNumber;
use libphonenumber\PhoneNumberUtil;
use Psr\Log\NullLogger;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
@@ -31,6 +32,7 @@ final class PhonenumberHelperTest extends KernelTestCase
public function testFormatPhonenumbers(string $defaultCarrierCode, string $phoneNumber, string $expected)
{
$util = PhoneNumberUtil::getInstance();
$subject = new PhonenumberHelper(
new ArrayAdapter(),
new ParameterBag([
@@ -70,4 +72,47 @@ final class PhonenumberHelperTest extends KernelTestCase
'00 33 6 23 12 45 54',
];
}
/**
* @dataProvider providePhoneNumbersToParse
*/
public function testParsePhonenumbers(string $defaultCarrierCode, string $phoneNumber, PhoneNumber $expected): void
{
$subject = new PhonenumberHelper(
new ArrayAdapter(),
new ParameterBag([
'chill_main.phone_helper' => [
'default_carrier_code' => $defaultCarrierCode,
],
]),
new NullLogger()
);
$actual = $subject->parse($phoneNumber);
self::assertTrue($expected->equals($actual));
}
public static function providePhoneNumbersToParse(): iterable
{
$util = PhoneNumberUtil::getInstance();
yield [
'FR',
'+32486544999',
$util->parse('+32486544999', 'FR'),
];
yield [
'FR',
'32486544999',
$util->parse('+32486544999', 'FR'),
];
yield [
'FR',
'0228858040',
$util->parse('+33228858040', 'FR'),
];
}
}

View File

@@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\MainBundle\Tests\Validation\Validator;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\UserGroup;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\MainBundle\Validation\Validator\UserGroupDoNotExclude;
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
/**
* @internal
*
* @coversNothing
*/
class UserGroupDoNotExcludeTest extends ConstraintValidatorTestCase
{
protected function createValidator()
{
return new UserGroupDoNotExclude(
new class () implements TranslatableStringHelperInterface {
public function localize(array $translatableStrings): ?string
{
return $translatableStrings['fr'];
}
}
);
}
public function testEmptyArrayIsValid(): void
{
$this->validator->validate([], new \Chill\MainBundle\Validation\Constraint\UserGroupDoNotExclude());
$this->assertNoViolation();
}
public function testMixedUserGroupAndUsersIsValid(): void
{
$this->validator->validate(
[new User(), new UserGroup()],
new \Chill\MainBundle\Validation\Constraint\UserGroupDoNotExclude()
);
$this->assertNoViolation();
}
public function testDifferentExcludeKeysIsValid(): void
{
$this->validator->validate(
[(new UserGroup())->setExcludeKey('A'), (new UserGroup())->setExcludeKey('B')],
new \Chill\MainBundle\Validation\Constraint\UserGroupDoNotExclude()
);
$this->assertNoViolation();
}
public function testMultipleGroupsWithEmptyExcludeKeyIsValid(): void
{
$this->validator->validate(
[(new UserGroup())->setExcludeKey(''), (new UserGroup())->setExcludeKey('')],
new \Chill\MainBundle\Validation\Constraint\UserGroupDoNotExclude()
);
$this->assertNoViolation();
}
public function testSameExclusionKeyWillRaiseError(): void
{
$this->validator->validate(
[
(new UserGroup())->setExcludeKey('A')->setLabel(['fr' => 'Group 1']),
(new UserGroup())->setExcludeKey('A')->setLabel(['fr' => 'Group 2']),
],
new \Chill\MainBundle\Validation\Constraint\UserGroupDoNotExclude()
);
$this->buildViolation('The groups {{ excluded_groups }} do exclude themselves. Please choose one between them')
->setParameter('excluded_groups', 'Group 1, Group 2')
->setCode('e16c8226-0090-11ef-8560-f7239594db09')
->assertRaised();
}
}

View File

@@ -10,6 +10,31 @@ servers:
components:
schemas:
Collection:
type: object
properties:
count:
type: number
format: u64
pagination:
type: object
properties:
first:
type: number
format: u64
items_per_page:
type: number
format: u64
next:
type: string
format: uri
nullable: true
previous:
type: string
format: uri
nullable: true
more:
type: boolean
EntityWorkflowAttachment:
type: object
properties:

View File

@@ -130,3 +130,58 @@ filter_order:
Search: Chercher dans la liste
By date: Filtrer par date
search_box: Filtrer par contenu
renderbox:
person: "Usager"
birthday:
man: "Né le"
woman: "Née le"
neutral: "Né·e le"
unknown: "Né·e le"
deathdate: "Date de décès"
household_without_address: "Le ménage de l'usager est sans adresse"
no_data: "Aucune information renseignée"
type:
thirdparty: "Tiers"
person: "Usager"
holder: "Titulaire"
years_old: >-
{n, plural,
=0 {0 an}
one {1 an}
other {# ans}
}
residential_address: "Adresse de résidence"
located_at: "réside chez"
household_number: "Ménage n°{number}"
current_members: "Membres actuels"
no_current_address: "Sans adresse actuellement"
new_household: "Nouveau ménage"
no_members_yet: "Aucun membre actuellement"
pick_entity:
add: "Ajouter"
modal_title: >-
{count, plural,
one {Indiquer un}
other {Ajouter des}
}
user: >-
{count, plural,
one {Utilisateur}
other {Utilisateurs}
}
user_group: >-
{count, plural,
one {Groupe d'utilisateur}
other {Groupes d'utilisateurs}
}
person: >-
{count, plural,
one {Usager}
other {Usagers}
}
thirdparty: >-
{count, plural,
one {Tiers}
other {Tiers}
}

View File

@@ -941,3 +941,34 @@ multiselect:
editor:
switch_to_simple: Éditeur simple
switch_to_complex: Éditeur riche
action:
actions: Actions
show: Voir
edit: Modifier
create: Créer
remove: Enlever
delete: Supprimer
save: Enregistrer
valid: Valider
valid_and_see: Valider et voir
add: Ajouter
show_modal: Ouvrir une modale
ok: OK
cancel: Annuler
close: Fermer
back: Retour
check_all: cocher tout
reset: réinitialiser
redirect:
person: Quitter la page et ouvrir la fiche de l'usager
thirdparty: Quitter la page et voir le tiers
refresh: Rafraîchir
addContact: Ajouter un contact
nav:
next: "Suivant"
previous: "Précédent"
top: "Haut"
bottom: "Bas"

View File

@@ -21,6 +21,8 @@ use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\ORM\Query;
use libphonenumber\PhoneNumber;
use libphonenumber\PhoneNumberFormat;
use Symfony\Component\Security\Core\Security;
final readonly class PersonACLAwareRepository implements PersonACLAwareRepositoryInterface
@@ -298,4 +300,27 @@ final readonly class PersonACLAwareRepository implements PersonACLAwareRepositor
\array_map(static fn (Center $c) => $c->getId(), $authorizedCenters)
);
}
public function findByPhone(PhoneNumber $phoneNumber, int $start = 0, int $limit = 20): array
{
$authorizedCenters = $this->authorizationHelper
->getReachableCenters($this->security->getUser(), PersonVoter::SEE);
if ([] === $authorizedCenters) {
return [];
}
$util = \libphonenumber\PhoneNumberUtil::getInstance();
return $this->em->createQuery(
'SELECT p FROM '.Person::class.' p LEFT JOIN p.otherPhoneNumbers opn JOIN p.centerCurrent pcc '.
'WHERE (p.phonenumber LIKE :phone OR p.mobilenumber LIKE :phone OR opn.phonenumber LIKE :phone) '.
'AND pcc.center IN (:centers)'
)
->setMaxResults($limit)
->setFirstResult($start)
->setParameter('phone', $util->format($phoneNumber, PhoneNumberFormat::E164))
->setParameter('centers', $authorizedCenters)
->getResult();
}
}

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