diff --git a/CHANGELOG.md b/CHANGELOG.md index 568224874..233c1f51b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ and this project adheres to * [main] Add mainLocation field to User entity and add it in user form type * [course list in person context] show full username/label for ref * [accompanying period work] remove the possibility to generate document from an accompanying period work +* vuejs: add validation on required fields for AddPerson, Address and Location components +* vuejs: treat 422 validation errors in locations and AddPerson components ## Test releases diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/App.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/App.vue index 2fb9d022d..4809a5fae 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/App.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/App.vue @@ -11,7 +11,7 @@ import Location from './components/Location.vue'; export default { name: "App", - props: ['hasSocialIssues', 'hasLocation', 'hasPerson'], + props: ['hasSocialIssues', 'hasLocation', 'hasPerson'], components: { ConcernedGroups, SocialIssuesAcc, diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue index b8249ccc5..91c5db839 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location.vue @@ -24,7 +24,7 @@ v-model="location" > - + diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location/NewLocation.vue b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location/NewLocation.vue index 35bf9a065..c9a1c233c 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location/NewLocation.vue +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location/NewLocation.vue @@ -18,15 +18,6 @@ @@ -23,7 +25,7 @@ import VueMultiselect from 'vue-multiselect'; export default { name: 'CountrySelection', components: { VueMultiselect }, - props: ['context', 'entity'], + props: ['context', 'entity', 'flag', 'checkErrors'], emits: ['getCities'], data() { return { @@ -34,14 +36,13 @@ export default { }, computed: { sortedCountries() { - //console.log('sorted countries'); 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').filter(c => c.countryCode !== 'BE')) return sortedCountries; - } + }, }, mounted() { this.init(); @@ -50,6 +51,7 @@ export default { init() { if (this.value !== undefined) { this.selectCountry(this.value); + this.flag.dirty = false; } }, selectCountryByCode(countryCode) { @@ -62,7 +64,13 @@ export default { //console.log('select country', value); this.entity.selected.country = value; this.$emit('getCities', value); - } + this.checkErrors(); + }, + remove() { + this.flag.dirty = true; + this.entity.selected.country = null; + this.checkErrors(); + }, } }; diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/EditPane.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/EditPane.vue index af1263b43..486d28e73 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/EditPane.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/EditPane.vue @@ -7,6 +7,12 @@ Loading... +
+ +
+

{{ $t('select_an_address_title') }}

@@ -25,6 +31,8 @@ @@ -33,13 +41,17 @@ v-bind:context="context" v-bind:focusOnAddress="focusOnAddress" v-bind:updateMapCenter="updateMapCenter" + v-bind:flag="flag" + v-bind:checkErrors="checkErrors" @getReferenceAddresses="$emit('getReferenceAddresses', selected.city)"> + v-bind:updateMapCenter="updateMapCenter" + v-bind:flag="flag" + v-bind:checkErrors="checkErrors">
@@ -99,7 +111,9 @@ export default { 'flag', 'entity', 'errorMsg', - 'insideModal' + 'insideModal', + 'errors', + 'checkErrors', ], emits: ['getCities', 'getReferenceAddresses'], data() { @@ -128,7 +142,7 @@ export default { get() { return this.entity.selected.isNoAddress; } - } + }, }, methods: { focusOnAddress() { diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/components/OnTheFly.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/components/OnTheFly.vue index d4342fdee..8c93eec71 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/components/OnTheFly.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/components/OnTheFly.vue @@ -90,7 +90,7 @@ export default { OnTheFlyThirdparty, OnTheFlyCreate }, - props: ['type', 'id', 'action', 'buttonText', 'displayBadge', 'parent'], + props: ['type', 'id', 'action', 'buttonText', 'displayBadge', 'parent', 'canCloseModal'], emits: ['saveFormOnTheFly'], data() { return { @@ -162,7 +162,20 @@ export default { return 'entity-' + this.type + ' badge-' + this.type; } }, + watch: { + canCloseModal: { + handler: function(val, oldVal) { + if (val) { + this.closeModal(); + } + }, + deep: true + } + }, methods: { + closeModal() { + this.modal.showModal = false; + }, openModal() { //console.log('## OPEN ON THE FLY MODAL'); //console.log('## type:', this.type, ', action:', this.action); @@ -200,8 +213,6 @@ export default { // pass datas to parent this.$emit('saveFormOnTheFly', { type: type, data: data }); - - this.modal.showModal = false; }, buildLocation(id, type) { if (type === 'person') { diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php index 73b83f246..91e430a2f 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person.php @@ -151,7 +151,6 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI * @var DateTime * * @ORM\Column(type="date", nullable=true) - * @Assert\Date * @Birthdate */ private $birthdate; @@ -259,7 +258,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI * @var string * * @ORM\Column(type="string", length=255) - * @Assert\NotBlank + * @Assert\NotBlank(message="The firstname cannot be empty") * @Assert\Length( * max=255, * ) @@ -282,7 +281,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI * @var string * * @ORM\Column(type="string", length=9, nullable=true) - * @Assert\NotNull + * @Assert\NotNull(message="The gender must be set") */ private $gender; @@ -326,7 +325,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI * @var string * * @ORM\Column(type="string", length=255) - * @Assert\NotBlank + * @Assert\NotBlank(message="The lastname cannot be empty") * @Assert\Length( * max=255, * ) diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons.vue index f118671ed..59647e160 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons.vue @@ -66,9 +66,10 @@
+ @saveFormOnTheFly="saveFormOnTheFly" + :canCloseModal="canCloseOnTheFlyModal">
@@ -91,8 +92,7 @@ import Modal from 'ChillMainAssets/vuejs/_components/Modal'; import OnTheFly from 'ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue'; import PersonSuggestion from './AddPersons/PersonSuggestion'; import { searchEntities } from 'ChillPersonAssets/vuejs/_api/AddPersons'; -import { postPerson } from "ChillPersonAssets/vuejs/_api/OnTheFly"; -import { postThirdparty } from "ChillThirdPartyAssets/vuejs/_api/OnTheFly"; +import { makeFetch } from 'ChillMainAssets/lib/api/apiMethods'; export default { name: 'AddPersons', @@ -120,7 +120,8 @@ export default { suggested: [], selected: [], priorSuggestion: {} - } + }, + canCloseOnTheFlyModal: false } }, computed: { @@ -267,22 +268,36 @@ export default { saveFormOnTheFly({ type, data }) { console.log('saveFormOnTheFly from addPersons, type', type, ', data', data); if (type === 'person') { - console.log('type person with', data); - postPerson(data) - .then(person => new Promise((resolve, reject) => { - console.log('onthefly create: post person', person); - this.newPriorSuggestion(person); - resolve(); - })); + makeFetch('POST', '/api/1.0/person/person.json', data) + .then(response => { + this.newPriorSuggestion(response); + this.canCloseOnTheFlyModal = true; + }) + .catch((error) => { + if (error.name === 'ValidationException') { + for (let v of error.violations) { + this.$toast.open({message: v }); + } + } else { + this.$toast.open({message: 'An error occurred'}); + } + }) } else if (type === 'thirdparty') { - console.log('type thirdparty with', data); - postThirdparty(data) - .then(thirdparty => new Promise((resolve, reject) => { - console.log('onthefly create: post thirdparty', thirdparty); - this.newPriorSuggestion(thirdparty); - resolve(); - })); + makeFetch('POST', '/api/1.0/thirdparty/thirdparty.json', data) + .then(response => { + this.newPriorSuggestion(response); + this.canCloseOnTheFlyModal = true; + }) + .catch((error) => { + if (error.name === 'ValidationException') { + for (let v of error.violations) { + this.$toast.open({message: v }); + } + } else { + this.$toast.open({message: 'An error occurred'}); + } + }) } } }, diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/OnTheFly/Person.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/OnTheFly/Person.vue index 799e5651b..9543da3bb 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/OnTheFly/Person.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/OnTheFly/Person.vue @@ -22,24 +22,45 @@
- +
- +
- +
- @@ -62,8 +83,8 @@
@@ -71,8 +92,8 @@
@@ -80,11 +101,17 @@
+
+ +
+ @@ -108,6 +135,7 @@ export default { config: { altNames: [] }, + errors: [] } }, computed: { @@ -183,6 +211,18 @@ export default { } }, methods: { + checkErrors(e) { + this.errors = []; + if (!this.person.lastName) { + this.errors.push("Le nom ne doit pas être vide."); + } + if (!this.person.firstName) { + this.errors.push("Le prénom ne doit pas être vide."); + } + if (!this.person.gender) { + this.errors.push("Le genre doit être renseigné"); + } + }, loadData() { getPerson(this.id) .then(person => new Promise((resolve, reject) => { diff --git a/src/Bundle/ChillPersonBundle/translations/validators.fr.yml b/src/Bundle/ChillPersonBundle/translations/validators.fr.yml index b421e9f43..96cfd565f 100644 --- a/src/Bundle/ChillPersonBundle/translations/validators.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/validators.fr.yml @@ -16,6 +16,9 @@ The birthdate must be before %date%: La date de naissance doit être avant le %d 'Invalid phone number: it should begin with the international prefix starting with "+", hold only digits and be smaller than 20 characters. Ex: +33623456789': 'Numéro de téléphone invalide: il doit commencer par le préfixe international précédé de "+", ne comporter que des chiffres et faire moins de 20 caractères. Ex: +33623456789' 'The email is not valid': 'Le courriel n''est pas valide' Two addresses has the same validFrom date: La date de validité est identique à celle d'une autre adresse +The firstname cannot be empty: Le prénom ne peut pas être vide +The lastname cannot be empty: Le nom de famille ne peut pas être vide +The gender must be set: Le genre doit être renseigné #export list You must select at least one element: Vous devez sélectionner au moins un élément