refactoring AddAddress component

This commit is contained in:
Mathieu Jaumotte 2021-05-11 17:01:18 +02:00
parent 41254db72f
commit 7aed8e83ea
14 changed files with 506 additions and 255 deletions

View File

@ -1,9 +1,23 @@
<template>
<add-address></add-address>
<div v-if="address.address">
{{ address.address.street }}, {{ address.address.streetNumber }}
</div>
<div v-if="address.city">
{{ address.city.code }} {{ address.city.name }}
</div>
<div v-if="address.country">
{{ address.country.name }}
</div>
<add-address
@addNewAddress="addNewAddress"
ref="addAddress">
</add-address>
</template>
<script>
import { mapState } from 'vuex'
import { mapState } from 'vuex';
import AddAddress from '../_components/AddAddress.vue';
@ -12,8 +26,18 @@ export default {
components: {
AddAddress
},
computed: mapState([
'address'
])
computed: {
address() {
return this.$store.state.address;
}
},
methods: {
addNewAddress({ address, modal }) {
console.log('@@@ CLICK button addNewAdress', address);
this.$store.dispatch('addAddress', address.selected);
//this.$refs.addAddress.resetAll(); // to cast child method
modal.showModal = false;
}
}
};
</script>

View File

@ -1,14 +0,0 @@
/*
* Endpoint chill_main_address_reference_api_show
* method GET, get AddressReference Object
* @returns {Promise} a promise containing all AddressReference object
*
*/
export const getReferenceAddress = () => {
const url = `/api/1.0/main/address-reference.json`;
return fetch(url)
.then(response => {
if (response.ok) { return response.json(); }
throw Error('Error with request resource response');
});
};

View File

@ -1,20 +1,16 @@
import { createApp } from 'vue'
import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n'
import { addressMessages } from './js/i18n'
import { getDataPromise } from './store'
import { store } from './store'
import App from './App.vue';
getDataPromise.then(store => {
const i18n = _createI18n(addressMessages);
const i18n = _createI18n(addressMessages);
const app = createApp({
template: `<app></app>`,
})
.use(store)
.use(i18n)
.component('app', App)
.mount('#address');
});
const app = createApp({
template: `<app></app>`,
})
.use(store)
.use(i18n)
.component('app', App)
.mount('#address');

View File

@ -1,77 +1,64 @@
import 'es6-promise/auto';
import { createStore } from 'vuex';
import { getReferenceAddress } from '../api';
import { app } from '../index';
//import { getReferenceAddress } from '../api';
//import { app } from '../index';
const debug = process.env.NODE_ENV !== 'production';
const getDataPromise = getReferenceAddress()
.then(referenceAddresses => new Promise((resolve, reject) => {
const store = createStore({
strict: debug,
state: {
address: {},
errorMsg: {}
},
getters: {
/*
getCountries: state => state.countries,
getSelectedCountry: state => state.selectedCountry,
getCities: state => state.selectedCountry.countryCode === undefined ?
state.cities :
state.cities.filter(c => c.country == state.selectedCountry.countryCode),
getReferenceAddresses: state => state.referenceAddresses, //TODO filter as a function of city
getMapCenter: state => state.addressMap.center,
*/
},
mutations: {
addAddress(state, address) {
console.log('@M addAddress address', address);
state.address = address;
},
/*
setSelectedCountry(state, value) {
state.selectedCountry = value;
},
setSelectedCity(state, value) {
state.selectedCity = value;
},
setSelectedAddress(state, value) {
state.selectedAddress = value;
state.addressMap.center = value.point.coordinates.reverse();
//app.$refs.addressMap.updateMap(); //TODO how to update the map view? -> action avec dispatch
},
setMapCenter(state, value) {
state.addressMap.center = value;
},
setIsNoAddress(state, value) {
state.form.isNoAddress = value;
}
*/
},
actions: {
addAddress({ commit }, payload) {
console.log('@A addAddress payload', payload);
commit('addAddress', payload);
},
/*
updateMapCenter({ commit }, payload) {
commit('updateMapCenter', payload);
},
*/
}
});
const store = createStore({
strict: debug,
state: {
referenceAddresses: referenceAddresses.results, //TODO-> move to data
countries: [
{id: 1, name: 'France', countryCode: 'FR'},
{id: 2, name: 'Belgium', countryCode: 'BE'}
], //TODO fetch countries from CHILL API
cities: [
{id: 1, name: 'Bruxelles', code: '1000', country: 'BE'},
{id: 2, name: 'Aisne', code: '85045', country: 'FR'},
{id: 3, name: 'Saint-Gervais', code: '85230', country: 'FR'}
], //TODO fetch postal codes from CHILL API
selectedCountry: {id: 1, name: 'France', countryCode: 'FR'},
selectedCity: null,
selectedAddress: null,
addressMap: {
center : [48.8589, 2.3469], // Note: LeafletJs demands [lat, lon] cfr https://macwright.com/lonlat/
zoom: 12
},
form: {
isNoAddress: false,
floor: null,
corridor: null,
steps: null
}
},
getters: {
getCountries: state => state.countries,
getSelectedCountry: state => state.selectedCountry,
getCities: state => state.selectedCountry.countryCode === undefined ?
state.cities :
state.cities.filter(c => c.country == state.selectedCountry.countryCode),
getReferenceAddresses: state => state.referenceAddresses, //TODO filter as a function of city
getMapCenter: state => state.addressMap.center,
},
mutations: {
setSelectedCountry(state, value) {
state.selectedCountry = value;
},
setSelectedCity(state, value) {
state.selectedCity = value;
},
setSelectedAddress(state, value) {
state.selectedAddress = value;
state.addressMap.center = value.point.coordinates.reverse();
//app.$refs.addressMap.updateMap(); //TODO how to update the map view? -> action avec dispatch
},
setMapCenter(state, value) {
state.addressMap.center = value;
},
setIsNoAddress(state, value) {
state.form.isNoAddress = value;
}
},
actions: {
updateMapCenter({ commit }, payload) {
commit('updateMapCenter', payload);
},
}
});
resolve(store);
}));
export { getDataPromise };
export { store };

View File

@ -0,0 +1,43 @@
/*
* Endpoint countries GET
*/
const fetchCountries = () => {
console.log('<<< fetching countries');
return [
{id: 1, name: 'France', countryCode: 'FR'},
{id: 2, name: 'Belgium', countryCode: 'BE'}
];
};
/*
* Endpoint cities GET
*/
const fetchCities = (country) => {
console.log('<<< fetching cities for', country);
return [
{id: 1, name: 'Bruxelles', code: '1000', country: 'BE'},
{id: 2, name: 'Aisne', code: '85045', country: 'FR'},
{id: 3, name: 'Saint-Gervais', code: '85230', country: 'FR'}
];
};
/*
* Endpoint chill_main_address_reference_api_show
* method GET, get AddressReference Object
* @returns {Promise} a promise containing all AddressReference object
*/
const fetchReferenceAddresses = (city) => {
console.log('<<< fetching references addresses for', city);
const url = `/api/1.0/main/address-reference.json`;
return fetch(url)
.then(response => {
if (response.ok) { return response.json(); }
throw Error('Error with request resource response');
});
};
export {
fetchCountries,
fetchCities,
fetchReferenceAddresses
};

View File

@ -5,7 +5,7 @@
<teleport to="body">
<modal v-if="modal.showModal"
:modalDialogClass="modal.modalDialogClass"
v-bind:modalDialogClass="modal.modalDialogClass"
@close="modal.showModal = false">
<template v-slot:header>
@ -13,56 +13,67 @@
</template>
<template v-slot:body>
<h4>{{ $t('select_an_address') }}</h4>
<label for="isNoAddress">
<input type="checkbox"
name="isNoAddress"
v-bind:placeholder="$t('isNoAddress')"
v-model="isNoAddress"
v-bind:value="value"/>
{{ $t('isNoAddress') }}
</label>
<div class="address_form__fields__isNoAddress">
<input type="checkbox" name="isNoAddress" :placeholder="$t('isNoAddress')" v-model="isNoAddress"/>
<label for="isNoAddress">{{ $t('isNoAddress') }}</label>
</div>
<country-selection
v-bind:address="address"
v-bind:getCities="getCities">
</country-selection>
<city-selection
v-bind:address="address"
v-bind:getReferenceAddresses="getReferenceAddresses">
</city-selection>
<address-selection
v-bind:address="address"
v-bind:updateMapCenter="updateMapCenter">
</address-selection>
<address-map
v-bind:address="address"
ref="addressMap">
</address-map>
<!--div class="address_form__fields__isNoAddress"></div>
<div class="address_form__select">
<div class="address_form__select__header">
<h4>{{ $t('select_an_address') }}</h4>
</div>
<div class="address_form__select__left">
<country-selection>
</country-selection>
<city-selection>
</city-selection>
<address-selection>
</address-selection>
</div>
<div class="address_form__map">
<address-map ref="addressMap">
</address-map>
</div>
</div>
<div v-if="!isNoAddress" class="address_form__fields">
<div class="address_form__fields">
<div class="address_form__fields__header">
<h4>{{ $t('fill_an_address') }}</h4>
</div>
<div class="address_form__fields__left">
<input type="text" name="floor" :placeholder="$t('floor')" v-model="floor"/>
<input type="text" name="corridor" :placeholder="$t('corridor')" v-model="corridor"/>
<input type="text" name="steps" :placeholder="$t('steps')" v-model="steps"/>
<input type="text" name="flat" :placeholder="$t('flat')" v-model="flat"/>
</div>
<div class="address_form__fields__right">
<input type="text" name="buildingName" :placeholder="$t('buildingName')" v-model="buildingName"/>
<input type="text" name="extra" :placeholder="$t('extra')" v-model="extra"/>
<input type="text" name="distribution" :placeholder="$t('distribution')" v-model="distribution"/>
</div>
</div>
</div-->
<address-more
v-if="!isNoAddress"
v-bind:address="address">
</address-more>
</template>
<template v-slot:footer>
<button class="sc-button green" @click="addAddresses">
<button class="sc-button green"
@click.prevent="$emit('addNewAddress', { address, modal })">
<i class="fa fa-plus fa-fw"></i>{{ $t('action.add')}}
</button>
</template>
@ -72,13 +83,13 @@
</template>
<script>
import { mapState } from 'vuex';
import Modal from './Modal';
import CountrySelection from './CountrySelection';
import CitySelection from './CitySelection';
import AddressSelection from './AddressSelection';
import AddressMap from './AddressMap';
import { fetchCountries, fetchCities, fetchReferenceAddresses } from '../_api/AddAddress'
import CountrySelection from './AddAddress/CountrySelection';
import CitySelection from './AddAddress/CitySelection';
import AddressSelection from './AddAddress/AddressSelection';
import AddressMap from './AddAddress/AddressMap';
import AddressMore from './AddAddress/AddressMore'
export default {
name: 'AddAddresses',
@ -87,40 +98,113 @@ export default {
CountrySelection,
CitySelection,
AddressSelection,
AddressMap
AddressMap,
AddressMore
},
props: [
],
emits: ['addNewAddress'],
data() {
return {
modal: {
showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-xl"
}
},
address: {
loaded: {
countries: [],
cities: [],
addresses: [],
},
selected: {
country: {},
city: {},
address: {},
},
addressMap: {
center : [48.8589, 2.3469], // Note: LeafletJs demands [lat, lon] cfr https://macwright.com/lonlat/
zoom: 12
},
isNoAddress: false,
floor: null,
corridor: null,
steps: null,
floor: null,
flat: null,
buildingName: null,
extra: null,
distribution: null,
},
errorMsg: {}
}
},
computed: {
...mapState(['add_addresses']), //TODO: is it useful?
isNoAddress: {
set(v) {
this.$store.commit('setIsNoAddress', v);
set(value) {
console.log('value', value);
this.address.isNoAddress = value;
},
get() {
return this.$store.state.form.isNoAddress; //TODO could be this.add_address.form.isNoAddress
return this.address.isNoAddress;
}
},
//TODO define setter and getter for form input (floor, steps, ...)
}
},
mounted() {
this.getCountries();
},
methods: {
openModal() {
this.modal.showModal = true;
this.resetAll();
//this.$nextTick(function() {
// this.$refs.search.focus(); // positionner le curseur à l'ouverture de la modale
//})
},
addAddresses() {
console.log('@@@ CLICK button addAddresses')
this.selected.forEach(function(item) {
//console.log('# dispatch action for each item', item);
// this.$store.dispatch('addParticipation', item); //TODO
}, this
);
this.modal.showModal = false;
getCountries() {
console.log('getCountries');
this.address.loaded.countries = fetchCountries(); // à remplacer par
//fetchCountries().then(countries => new Promise((resolve, reject) => {
// this.address.loaded.countries = countries;
// resolve()
//}))
//.catch((error) => {
// this.errorMsg.push(error.message);
//});
},
getCities(country) {
console.log('getCities for', country.name);
this.address.loaded.cities = fetchCities(); // à remplacer par
//fetchCities(country).then(cities => new Promise((resolve, reject) => {
// this.address.loaded.cities = cities;
// resolve()
//}))
//.catch((error) => {
// this.errorMsg.push(error.message);
//});
},
getReferenceAddresses(city) {
console.log('getReferenceAddresses for', city.name);
fetchReferenceAddresses(city)
.then(addresses => new Promise((resolve, reject) => {
console.log('addresses', addresses);
this.address.loaded.addresses = addresses.results;
resolve();
}))
.catch((error) => {
this.errorMsg.push(error.message);
});
},
updateMapCenter(point) {
console.log('point', point);
this.address.addressMap.center[0] = point.coordinates[1];
this.address.addressMap.center[1] = point.coordinates[0];
this.$refs.addressMap.update();
},
resetAll() {
console.log('reset all selected');
this.address.selected.address = {};
this.address.selected.city = {};
this.address.selected.country = {};
}
}
}

View File

@ -14,16 +14,10 @@ let map;
export default {
name: 'AddressMap',
data() {
},//TODO useful?
props: ['address'],
computed: {
center: {
set(value) {
this.$store.commit('setMapCenter', value);
},
get() {
return this.$store.state.addressMap.center;
}
center() {
return this.address.addressMap.center;
},
},
methods:{
@ -42,8 +36,8 @@ export default {
},
update() {
console.log('update map with : ', this.$store.getMapCenter())
map.setView(this.$store.getMapCenter(), 12);
console.log('update map with : ', this.address.addressMap.center)
map.setView(this.address.addressMap.center, 12);
}
},
mounted(){
@ -51,5 +45,3 @@ export default {
}
}
</script>

View File

@ -0,0 +1,112 @@
<template>
<div>
<h4>{{ $t('fill_an_address') }}</h4>
<input
type="text"
name="floor"
:placeholder="$t('floor')"
v-model="floor"/>
<input
type="text"
name="corridor"
:placeholder="$t('corridor')"
v-model="corridor"/>
<input
type="text"
name="steps"
:placeholder="$t('steps')"
v-model="steps"/>
<input
type="text"
name="flat"
:placeholder="$t('flat')"
v-model="flat"/>
<input
type="text"
name="buildingName"
:placeholder="$t('buildingName')"
v-model="buildingName"/>
<input
type="text"
name="extra"
:placeholder="$t('extra')"
v-model="extra"/>
<input
type="text"
name="distribution"
:placeholder="$t('distribution')"
v-model="distribution"/>
</div>
</template>
<script>
export default {
name: "AddressMore",
props: ['address'],
computed: {
floor: {
set(value) {
console.log('value', value);
this.address.floor = value;
},
get() {
return this.address.floor;
}
},
corridor: {
set(value) {
console.log('value', value);
this.address.corridor = value;
},
get() {
return this.address.corridor;
}
},
steps: {
set(value) {
console.log('value', value);
this.address.steps = value;
},
get() {
return this.address.steps;
}
},
flat: {
set(value) {
console.log('value', value);
this.address.flat = value;
},
get() {
return this.address.flat;
}
},
buildingName: {
set(value) {
console.log('value', value);
this.address.buildingName = value;
},
get() {
return this.address.buildingName;
}
},
extra: {
set(value) {
console.log('value', value);
this.address.extra = value;
},
get() {
return this.address.extra;
}
},
distribution: {
set(value) {
console.log('value', value);
this.address.distribution = value;
},
get() {
return this.address.distribution;
}
}
}
}
</script>

View File

@ -0,0 +1,38 @@
<template>
<div class="container">
<select
v-model="selected"
v-bind:placeholder="$t('select_address')">
<option
v-for="item in this.addresses"
v-bind:item="item"
v-bind:key="item.id"
v-bind:value="item">
{{ item.street }}, {{ item.streetNumber }}
</option>
</select>
</div>
</template>
<script>
export default {
name: 'AddressSelection',
props: ['address', 'updateMapCenter'],
computed: {
addresses() {
return this.address.loaded.addresses;
},
selected: {
set(value) {
console.log('selected value', value);
this.address.selected.address = value;
this.updateMapCenter(value.point);
},
get() {
return this.address.selected.address;
}
},
}
};
</script>

View File

@ -0,0 +1,38 @@
<template>
<div class="container">
<select
v-model="selected"
v-bind:placeholder="$t('select_city')">
<option
v-for="item in this.cities"
v-bind:item="item"
v-bind:key="item.id"
v-bind:value="item">
{{ item.code }}-{{ item.name }}
</option>
</select>
</div>
</template>
<script>
export default {
name: 'CitySelection',
props: ['address', 'getReferenceAddresses'],
computed: {
cities() {
return this.address.loaded.cities;
},
selected: {
set(value) {
console.log('selected value', value.name);
this.address.selected.city = value;
this.getReferenceAddresses(value);
},
get() {
return this.address.selected.city;
}
},
}
};
</script>

View File

@ -0,0 +1,38 @@
<template>
<div class="container">
<select
v-model="selected"
v-bind:placeholder="$t('select_country')">
<option
v-for="item in this.countries"
v-bind:item="item"
v-bind:key="item.id"
v-bind:value="item">
{{ item.name }}
</option>
</select>
</div>
</template>
<script>
export default {
name: 'CountrySelection',
props: ['address', 'getCities'],
computed: {
countries() {
return this.address.loaded.countries;
},
selected: {
set(value) {
console.log('selected value', value.name);
this.address.selected.country = value;
this.getCities(value);
},
get() {
return this.address.selected.country;
}
}
}
};
</script>

View File

@ -1,29 +0,0 @@
<template>
<div class="container">
<select v-model="selected" :placeholder="$t('select_address')">
<option v-for="item in this.addresses" v-bind:item="item" v-bind:key="item.id" v-bind:value="item">
{{ item.street }}, {{ item.streetNumber }}
</option>
</select>
</div>
</template>
<script>
export default {
name: 'AddressSelection',
data() {
},//TODO useful?
computed: {
addresses() { return this.$store.getters.getReferenceAddresses; },
selected: {
set(value) {
this.$store.commit('setSelectedAddress', value);
},
get() {
return this.$store.state.selectedAddress;
}
},
}
};
</script>

View File

@ -1,29 +0,0 @@
<template>
<div class="container">
<select v-model="selected" :placeholder="$t('select_city')">
<option v-for="item in this.cities" v-bind:item="item" v-bind:key="item.id" v-bind:value="item">
{{ item.code }}-{{ item.name }}
</option>
</select>
</div>
</template>
<script>
export default {
name: 'CitySelection',
data() {
},//TODO useful?
computed: {
cities() { return this.$store.getters.getCities; },
selected: {
set(value) {
this.$store.commit('setSelectedCity', value);
},
get() {
return this.$store.state.selectedCity;
}
},
}
};
</script>

View File

@ -1,29 +0,0 @@
<template>
<div class="container">
<select v-model="selected" :placeholder="$t('select_country')">
<option v-for="item in this.countries" v-bind:item="item" v-bind:key="item.id" v-bind:value="item">
{{ item.name }}
</option>
</select>
</div>
</template>
<script>
export default {
name: 'CountrySelection',
data() {
},//TODO useful?
computed: {
countries() { return this.$store.getters.getCountries; },
selected: {
set(value) {
this.$store.commit('setSelectedCountry', value); //TODO we should not change the state here (https://stackoverflow.com/questions/39299042/vuex-action-vs-mutations)
},
get() {
return this.$store.state.selectedCountry;
}
},
}
};
</script>