Newer
Older
Sébastien DA ROCHA
committed
const feature = {
namespaced: true,
state: {
attachmentFormset: [],
clickedFeatures: [],
Sébastien DA ROCHA
committed
features: [],
Sébastien DA ROCHA
committed
form: null,
linkedFormset: [], //* used to edit in feature_edit
linked_features: [], //* used to display in feature_detail
massMode: 'edit-status',
Sébastien DA ROCHA
committed
},
mutations: {
SET_FEATURES(state, features) {
state.features = features.sort((a, b) => {
return new Date(b.created_on) - new Date(a.created_on); // sort features chronologically
});
Sébastien DA ROCHA
committed
},
SET_FEATURES_COUNT(state, features_count) {
state.features_count = features_count;
},
Sébastien DA ROCHA
committed
UPDATE_FORM(state, payload) {
state.form = payload;
},
INIT_FORM(state) {
state.form = {
title: state.currentFeature.title,
description: { value: state.currentFeature.description },
status: { value: state.currentFeature.status },
};
},
UPDATE_FORM_FIELD(state, field) {
if (state.form[field.name].value !== undefined) {
state.form[field.name].value = field.value;
} else {
state.form[field.name] = field.value;
}
Sébastien DA ROCHA
committed
UPDATE_EXTRA_FORM(state, extra_form) {
const index = state.extra_forms.findIndex(el => el.label === extra_form.label);
Sébastien DA ROCHA
committed
if (index !== -1) {
state.extra_forms[index] = extra_form;
Sébastien DA ROCHA
committed
}
},
SET_EXTRA_FORMS(state, extra_forms) {
state.extra_forms = extra_forms;
Sébastien DA ROCHA
committed
},
CLEAR_EXTRA_FORM(state) {
ADD_ATTACHMENT_FORM(state, attachmentFormset) {
state.attachmentFormset = [...state.attachmentFormset, attachmentFormset];
Sébastien DA ROCHA
committed
},
UPDATE_ATTACHMENT_FORM(state, payload) {
const index = state.attachmentFormset.findIndex((el) => el.dataKey === payload.dataKey);
if (index !== -1) {
state.attachmentFormset[index] = payload;
}
Sébastien DA ROCHA
committed
},
REMOVE_ATTACHMENT_FORM(state, payload) {
state.attachmentFormset = state.attachmentFormset.filter(form => form.dataKey !== payload);
},
CLEAR_ATTACHMENT_FORM(state) {
state.attachmentFormset = [];
},

Timothee P
committed
ADD_LINKED_FORM(state, linkedFormset) {
state.linkedFormset = [...state.linkedFormset, linkedFormset];
Sébastien DA ROCHA
committed
},
UPDATE_LINKED_FORM(state, payload) {
const index = state.linkedFormset.findIndex((el) => el.dataKey === payload.dataKey);
if (index !== -1) {
state.linkedFormset[index] = payload;
}
Sébastien DA ROCHA
committed
},
REMOVE_LINKED_FORM(state, payload) {
state.linkedFormset = state.linkedFormset.filter(form => form.dataKey !== payload);
},
SET_LINKED_FEATURES(state, payload) {
state.linked_features = payload;
},

Timothee P
committed
CLEAR_LINKED_FORM(state) {
state.linkedFormset = [];
},
ADD_ATTACHMENT_TO_DELETE(state, attachementId) {
state.attachmentsToDelete.push(attachementId);
},
REMOVE_ATTACHMENTS_ID_TO_DELETE(state, attachementId) {
state.attachmentsToDelete = state.attachmentsToDelete.filter(el => el !== attachementId);
},
UPDATE_CHECKED_FEATURES(state, checkedFeatures) {
state.checkedFeatures = checkedFeatures;
},
UPDATE_CLICKED_FEATURES(state, clickedFeatures) {
state.clickedFeatures = clickedFeatures;
},
TOGGLE_MASS_MODE(state, payload) {
state.massMode = payload;
},
Sébastien DA ROCHA
committed
},
Sébastien DA ROCHA
committed
getters: {
},
Sébastien DA ROCHA
committed
actions: {
async GET_PROJECT_FEATURES({ commit, dispatch, rootState }, {
project_slug,
feature_type__slug,
ordering,
search,
limit,
}) {
dispatch('CANCEL_CURRENT_SEARCH_REQUEST', null, { root: true });
const cancelToken = axios.CancelToken.source();
commit('SET_CANCELLABLE_SEARCH_REQUEST', cancelToken, { root: true });
commit('SET_FEATURES', []);
commit('SET_FEATURES_COUNT', 0);
let url = `${rootState.configuration.VUE_APP_DJANGO_API_BASE}v2/features/?project__slug=${project_slug}`;
if (feature_type__slug) {
url = url.concat('', `${url.includes('?') ? '&' : '?'}feature_type__slug=${feature_type__slug}`);
}
if (ordering) {
url = url.concat('', `${url.includes('?') ? '&' : '?'}ordering=${ordering}`);
}
url = url.concat('', `${url.includes('?') ? '&' : '?'}title__icontains=${search}`);
url = url.concat('', `${url.includes('?') ? '&' : '?'}limit=${limit}`);
if (geojson) {
url = url.concat('', '&output=geojson');
}
try {
const response = await axios.get(url, { cancelToken: cancelToken.token });
if (response.status === 200 && response.data) {
const features = response.data.features;
commit('SET_FEATURES_COUNT', features_count);
if (error.message) {
console.error(error);
}
throw error; // 'throw' instead of 'return', in order to pass inside the 'catch' error instead of 'then', to avoid initiating map in another component after navigation
Sébastien DA ROCHA
committed
},
GET_PROJECT_FEATURE({ commit, dispatch, rootState }, { project_slug, feature_id }) {
dispatch('CANCEL_CURRENT_SEARCH_REQUEST', null, { root: true });
const cancelToken = axios.CancelToken.source();
commit('SET_CANCELLABLE_SEARCH_REQUEST', cancelToken, { root: true });
const url = `${rootState.configuration.VUE_APP_DJANGO_API_BASE}v2/features/${feature_id}/?project__slug=${project_slug}`;
.get(url, { cancelToken: cancelToken.token })
if (response.status === 200 && response.data) {
commit('SET_CURRENT_FEATURE', response.data);
console.error('Error while getting feature for id = ', feature_id, error);
Sébastien DA ROCHA
committed
SEND_FEATURE({ state, rootState, commit, dispatch }, { routeName, query, extraForms }) {
function redirect(featureName, response) {
// when modifying more than 2 features, exit this function (to avoid conflict with next feature call to GET_PROJECT_FEATURE)
if (routeName === 'editer-attribut-signalement') return response;
let newQuery = { ...query }; // create a copy of query from the route to avoid redundant navigation error since the router object would be modified
commit(
'DISPLAY_MESSAGE',
{
comment: routeName === 'ajouter-signalement' ?
'Le signalement a été crée' :
level: 'positive'
},
{ root: true },
);
const slug_type_signal = rootState['feature-type'].current_feature_type_slug;
const project = rootState.projects.project;
if (routeName === 'ajouter-signalement' && !query.ordering) {
newQuery = {
ordering: project.feature_browsing_default_sort,
offset: 0,// if feature was just created, in both ordering it would be the first in project features list
};
if (project.feature_browsing_default_filter === 'feature_type_slug') {
newQuery['feature_type_slug'] = slug_type_signal;
}
if (query && query.ordering === '-updated_on') { // if the list is ordered by update time
newQuery.offset = 0;// it would be in first position (else, if ordered by creation, the position won't change anyway)
// in fast edition avoid redundant navigation if query didn't change
if (routeName === 'details-signalement-filtre' && parseInt(query.offset) === parseInt(newQuery.offset)) {
return 'reloadPage';
}
router.push({
name: 'details-signalement-filtre',
params: { slug_type_signal },
query: newQuery,
});
return response;
async function handleOtherForms(featureId, featureName, response) {
await dispatch('SEND_ATTACHMENTS', featureId);
await dispatch('PUT_LINKED_FEATURES', featureId);
return redirect(featureName, response);

Timothee P
committed
function createGeojson() { //* prepare feature data to send
const extraFormObject = {}; //* prepare an object to be flatten in properties of geojson
for (const field of extraForms || state.extra_forms) { // use extraForms from argument if defined, overiding data from the store, in order to not use mutation (in case of multiple features)

Timothee P
committed
if (field.value !== null) extraFormObject[field.name] = field.value;

Timothee P
committed
}
return {
id: state.form.feature_id || state.currentFeature.feature_id,
geometry: state.form.geometry || state.form.geom ||
state.currentFeature.geometry || state.currentFeature.geom,
title: state.form.title,
description: state.form.description.value,
status: state.form.status.value,
project: rootState.projects.project.slug,
feature_type: rootState['feature-type'].current_feature_type_slug,

Timothee P
committed
...extraFormObject
}

Timothee P
committed
const geojson = createGeojson();
let url = `${rootState.configuration.VUE_APP_DJANGO_API_BASE}v2/features/`;
if (routeName !== 'ajouter-signalement') {

Timothee P
committed
url += `${geojson.id}/?
feature_type__slug=${rootState['feature-type'].current_feature_type_slug}
&project__slug=${rootState.projects.project.slug}`;
//* postOrPutFeature function from service featureAPI could be used here, but because configuration is in store,
//* projectBase would need to be sent with each function which imply to modify all function from this service,
//* which could create regression
return axios({
url,
method: routeName === 'ajouter-signalement' ? 'POST' : 'PUT',
data: geojson
}).then((response) => {
if ((response.status === 200 || response.status === 201) && response.data) {
const featureId = response.data.id;
const featureName = response.data.properties.title;
if (state.attachmentFormset.length > 0 ||
state.linkedFormset.length > 0 ||
state.attachmentsToDelete.length > 0) {
return handleOtherForms(featureId, featureName, response);
return redirect(featureName, response);
.catch((error) => {
if (error.message === 'Network Error' || !rootState.isOnline) {
let arraysOffline = [];
const localStorageArray = localStorage.getItem('geocontrib_offline');
if (localStorageArray) {
arraysOffline = JSON.parse(localStorageArray);
project: rootState.projects.project.slug,
type: routeName === 'ajouter-signalement' ? 'post' : 'put',

Timothee P
committed
featureId: geojson.id,
geojson: geojson
};
arraysOffline.push(updateMsg);
localStorage.setItem('geocontrib_offline', JSON.stringify(arraysOffline));
slug_type_signal: rootState['feature-type'].current_feature_type_slug
},
});
}
else {
console.error('Error while sending feature', error);
Sébastien DA ROCHA
committed
throw error;
}
throw error;
});
Sébastien DA ROCHA
committed
},
async SEND_ATTACHMENTS({ state, rootState, dispatch }, featureId) {
const DJANGO_API_BASE = rootState.configuration.VUE_APP_DJANGO_API_BASE;
function addFile(attachment, attchmtId) {
formdata.append('file', attachment.fileToImport, attachment.fileToImport.name);
return axios
.put(`${DJANGO_API_BASE}features/${featureId}/attachments/${attchmtId}/upload-file/`, formdata)
.then((response) => {
return response;
})
.catch((error) => {
console.error(error);
});
}
function putOrPostAttachement(attachment) {
formdata.append('title', attachment.title);
formdata.append('info', attachment.info);
let url = `${DJANGO_API_BASE}features/${featureId}/attachments/`;
data: formdata
}).then((response) => {
if (response && (response.status === 200 || response.status === 201) && attachment.fileToImport) {
return addFile(attachment, response.data.id);
}
.catch((error) => {
console.error(error);
return error;
});
function deleteAttachement(attachmentsId, featureId) {
attachmentsId: attachmentsId,
featureId: featureId
};
return dispatch('DELETE_ATTACHMENTS', payload)
.then((response) => response);
}
const promisesResult = await Promise.all([
...state.attachmentFormset.map((attachment) => putOrPostAttachement(attachment)),
...state.attachmentsToDelete.map((attachmentsId) => deleteAttachement(attachmentsId, featureId))
state.attachmentsToDelete = [];
return promisesResult;
},
DELETE_ATTACHMENTS({ commit }, payload) {
const url = `${this.state.configuration.VUE_APP_DJANGO_API_BASE}features/${payload.featureId}/attachments/${payload.attachmentsId}/`;
return axios
.delete(url)
.then((response) => {
if (response && response.status === 204) {
commit('REMOVE_ATTACHMENTS_ID_TO_DELETE', payload.attachmentsId);
return response;
}
})
.catch((error) => {
console.error(error);
PUT_LINKED_FEATURES({ state, rootState }, featureId) {
.put(`${rootState.configuration.VUE_APP_DJANGO_API_BASE}features/${featureId}/feature-links/`, state.linkedFormset)
.then((response) => {
if (response.status === 200 && response.data) {
}
})
.catch((error) => {
throw error;
});
},

Timothee P
committed
DELETE_FEATURE({ rootState }, payload) {
const { feature_id, noFeatureType } = payload;
let url = `${rootState.configuration.VUE_APP_DJANGO_API_BASE}v2/features/${feature_id}/?` +

Timothee P
committed
`project__slug=${rootState.projects.project.slug}`;
url +=`&feature_type__slug=${rootState['feature-type'].current_feature_type_slug}`;
return axios
.delete(url)
.then((response) => response)
.catch(() => {
return false;
});
INIT_EXTRA_FORMS({ state, rootGetters, commit }) {
const feature = state.currentFeature;
const featureType = rootGetters['feature-type/feature_type'];
function findCurrentValue(label) {
const field = feature.feature_data.find((el) => el.label === label);
return field ? field.value : null;
}
if (featureType && featureType.customfield_set) {
//* retrieve 'name', 'options', 'position' from current feature_type data to display in the form
const extraForm = rootGetters['feature-type/feature_type'].customfield_set.map((field) => {
return {
...field,
//* add value field to extra forms from feature_type and existing values if feature is defined
value:
feature && feature.feature_data
? findCurrentValue(field.label)
: null,
};
});
commit('SET_EXTRA_FORMS', extraForm);
}
Sébastien DA ROCHA
committed
},
Sébastien DA ROCHA
committed