import { createStore } from 'vuex' import { getHouseholdByPerson, getCoursesByPerson, getRelationshipsByPerson } from './api' import { getHouseholdLabel, getHouseholdWidth, getRelationshipLabel, getRelationshipTitle, getRelationshipDirection, splitId, getGender, getAge } from './vis-network' import {visMessages} from "./i18n"; const debug = process.env.NODE_ENV !== 'production' const store = createStore({ strict: debug, state: { persons: [], households: [], courses: [], relationships: [], links: [], whitelistIds: [], personLoadedIds: [], householdLoadingIds: [], courseLoadedIds: [], relationshipLoadedIds: [], excludedNodesIds: [] }, getters: { nodes(state) { let nodes = [] state.persons.forEach(p => { nodes.push(p) }) state.households.forEach(h => { nodes.push(h) }) state.courses.forEach(c => { nodes.push(c) }) // except excluded nodes (unchecked layers) state.excludedNodesIds.forEach(excluded => { nodes = nodes.filter(n => n.id !== excluded) }) return nodes }, edges(state) { let edges = [] state.links.forEach(l => { edges.push(l) }) return edges }, isInWhitelist: (state) => (person_id) => { return state.whitelistIds.includes(person_id) }, isHouseholdLoading: (state) => (household_id) => { return state.householdLoadingIds.includes(household_id) }, isCourseLoaded: (state) => (course_id) => { return state.courseLoadedIds.includes(course_id) }, isRelationshipLoaded: (state) => (relationship_id) => { return state.relationshipLoadedIds.includes(relationship_id) }, isPersonLoaded: (state) => (person_id) => { return state.personLoadedIds.includes(person_id) }, isExcludedNode: (state) => (id) => { return state.excludedNodesIds.includes(id) }, getParticipationsByCourse: (state) => (course_id) => { //console.log('getParticipationsByCourse', course_id) const course = state.courses.filter(c => c.id === course_id)[0] const currentParticipations = course.participations.filter(p => p.endDate === null) console.log('get persons in', course_id, currentParticipations.map(p => p.person.id), 'with folded', currentParticipations.filter(p => p.person.folded === true).map(p => p.person.id)) return currentParticipations }, getMembersByHousehold: (state) => (household_id) => { //console.log('getMembersByHousehold', household_id) const household = state.households.filter(h => h.id === household_id)[0] const currentMembers = household.members.filter(m => household.current_members_id.includes(m.id)) console.log('get persons in', household_id, currentMembers.map(m => m.person.id), 'with folded', currentMembers.filter(m => m.person.folded === true).map(m => m.person.id)) return currentMembers }, /** * This getter compare input array (participations|members) to personLoadedIds array * It return an array of folded persons */ getFoldedPersons: (state) => (array) => { let folded = [] array.forEach(item => { let id = (typeof item.person._id !== 'undefined') ? item.person._id : item.person.id if (state.personLoadedIds.includes(id)) { folded.push(state.persons.filter(person => person._id === id)[0]) } }) //console.log('array', array.map(r => r.person.id)) console.log('get FoldedPersons', folded.map(f => f.id)) return folded }, }, mutations: { addPerson(state, [person, options]) { //console.log('+ addPerson', person.id) person.group = person.type person._id = person.id person.id = `person_${person.id}` person.label = `*${person.text}*\n_${getGender(person.gender)} - ${getAge(person.birthdate)}_` person.folded = false if (options.folded) { // used by missing persons person.title = visMessages.fr.visgraph.click_to_expand person._label = person.label // keep label person.label = null person.folded = true } state.persons.push(person) }, addHousehold(state, household) { //console.log('+ addHousehold', household.id) household.group = household.type household._id = household.id household.label = `${visMessages.fr.visgraph.Household} n° ${household.id}` household.id = `household_${household.id}` state.households.push(household) }, addCourse(state, course) { //console.log('+ addCourse', course.id) course.group = course.type course._id = course.id course.label = `${visMessages.fr.visgraph.Course} n° ${course.id}` course.id = `accompanying_period_${course.id}` state.courses.push(course) }, addRelationship(state, relationship) { //console.log('+ addRelationship', relationship.id) relationship.group = relationship.type relationship._id = relationship.id relationship.id = `relationship_${relationship.id}` state.relationships.push(relationship) }, addLink(state, link) { //console.log('+ addLink from', link.from, 'to', link.to) state.links.push(link) }, //// id markers markInWhitelist(state, person) { state.whitelistIds.push(person.id) }, markPersonLoaded(state, id) { state.personLoadedIds.push(id) }, unmarkPersonLoaded(state, id) { state.personLoadedIds = state.personLoadedIds.filter(i => i !== id) }, markHouseholdLoading(state, id) { //console.log('..loading household', id) state.householdLoadingIds.push(id) }, unmarkHouseholdLoading(state, id) { state.householdLoadingIds = state.householdLoadingIds.filter(i => i !== id) }, markCourseLoaded(state, id) { state.courseLoadedIds.push(id) }, unmarkCourseLoaded(state, id) { state.courseLoadedIds = state.courseLoadedIds.filter(i => i !== id) }, markRelationshipLoaded(state, id) { state.relationshipLoadedIds.push(id) }, unmarkRelationshipLoaded(state, id) { state.relationshipLoadedIds = state.relationshipLoadedIds.filter(i => i !== id) }, //// excluded addExcludedNode(state, id) { console.log('==> exclude list: +', id) state.excludedNodesIds.push(id) }, removeExcludedNode(state, id) { console.log('<== exclude list: -', id) state.excludedNodesIds = state.excludedNodesIds.filter(e => e !== id) }, //// unfold unfoldPerson(state, person) { if (person.folded === true) { console.log('unfoldPerson', person) person.label = person._label delete person._label delete person.title person.folded = false } else { console.log('person is not folded', person) //throw 'person is not folded' } } }, actions: { /** * 1) Add a person in state * @param object * @param person */ addPerson({ commit, dispatch }, person) { commit('markPersonLoaded', person.id) commit('addPerson', [person, { folded: false }]) dispatch('fetchInfoForPerson', person) }, /** * 2) Fetch infos for this person (hub) * @param object * @param person */ fetchInfoForPerson({ dispatch }, person) { if (null !== person.current_household_id) { dispatch('fetchHouseholdForPerson', person) } dispatch('fetchCoursesByPerson', person) dispatch('fetchRelationshipByPerson', person) }, /** * 3) Fetch person current household (if it is not already loading) * check first isHouseholdLoading to fetch household once * @param object * @param person */ fetchHouseholdForPerson({ commit, getters, dispatch }, person) { //console.log(' isHouseholdLoading ?', getters.isHouseholdLoading(person.current_household_id)) if (! getters.isHouseholdLoading(person.current_household_id)) { commit('markHouseholdLoading', person.current_household_id) getHouseholdByPerson(person) .then(household => new Promise(resolve => { commit('addHousehold', household) //dispatch('addExcludedNode', household.id) // layer uncheck when added dispatch('addLinkFromPersonsToHousehold', household) resolve() }) ).catch( () => { commit('unmarkHouseholdLoading', person.current_household_id) }) } }, /** * 4) Add an edge for each household member (household -> person) * @param object * @param household */ addLinkFromPersonsToHousehold({ commit, getters, dispatch }, household) { let members = getters.getMembersByHousehold(household.id) console.log('add link for', members.length, 'members') members.forEach(m => { commit('addLink', { from: `${m.person.type}_${m.person.id}`, to: `household_${m.person.current_household_id}`, id: `p${m.person.id}-h${m.person.current_household_id}`, arrows: 'from', color: 'pink', font: { color: '#D04A60' }, label: getHouseholdLabel(m), width: getHouseholdWidth(m), }) if (!getters.isPersonLoaded(m.person.id)) { console.log('addMissingPerson from household', household.id) dispatch('addMissingPerson', [m.person, household]) } }) }, /** * 5) Fetch AccompanyingCourses for the person * @param object * @param person */ fetchCoursesByPerson({ commit, dispatch }, person) { //console.log('fetchCoursesByPerson', person) getCoursesByPerson(person) .then(courses => new Promise(resolve => { //console.log('fetch courses', courses.length) dispatch('addCourses', courses) resolve() })) }, /** * 6) Add each distinct course * @param object * @param courses */ addCourses({ commit, getters, dispatch }, courses) { //console.log('addCourse', courses) let currentCourses = courses.filter(c => c.closingDate === null) currentCourses.forEach(course => { //console.log(' isCourseLoaded ?', getters.isCourseLoaded(course.id)) if (! getters.isCourseLoaded(course.id)) { //console.log('course', course.id) commit('markCourseLoaded', course.id) commit('addCourse', course) commit('addExcludedNode', course.id) // init: layer uncheck when added dispatch('addLinkFromPersonsToCourse', course) } }) }, /** * 7) Add an edge for each course participation (course <- person) * @param object * @param course */ addLinkFromPersonsToCourse({ commit, getters, dispatch }, course) { const participations = getters.getParticipationsByCourse(course.id) console.log('add link for', participations.length, 'participations') participations.forEach(p => { //console.log(p.person.id) commit('addLink', { from: `${p.person.type}_${p.person.id}`, to: `${course.id}`, id: `p${p.person.id}-c`+ splitId(course.id,'id'), arrows: 'from', color: 'orange', font: { color: 'darkorange' }, }) if (!getters.isPersonLoaded(p.person.id)) { dispatch('addMissingPerson', [p.person, course]) } }) }, /** * 8) Fetch Relationship * @param object * @param person */ fetchRelationshipByPerson({ dispatch }, person) { //console.log('fetchRelationshipByPerson', person) getRelationshipsByPerson(person) .then(relationships => new Promise(resolve => { //console.log('fetch relationships', relationships.length) dispatch('addRelationships', relationships) resolve() })) }, /** * 9) Add each distinct relationship * @param object * @param relationships */ addRelationships({ commit, getters, dispatch }, relationships) { relationships.forEach(relationship => { //console.log(' isRelationshipLoaded ?', getters.isRelationshipLoaded(relationship.id)) if (! getters.isRelationshipLoaded(relationship.id)) { //console.log('relationship', relationship.id) commit('markRelationshipLoaded', relationship.id) commit('addRelationship', relationship) dispatch('addLinkFromRelationship', relationship) } }) }, /** * 10) Add an edge for each relationship (person -> person) * @param object * @param r (relationship) */ addLinkFromRelationship({ commit, getters, dispatch }, r) { //console.log('-> addLink from person', r.fromPerson.id, 'to person', r.toPerson.id) commit('addLink', { from: `person_${r.fromPerson.id}`, to: `person_${r.toPerson.id}`, id: 'r' + splitId(r.id,'id') + '_p' + r.fromPerson.id + '_p' + r.toPerson.id, arrows: getRelationshipDirection(r), color: 'lightblue', font: { color: '#33839d' }, dashes: true, //physics: false, label: getRelationshipLabel(r), title: getRelationshipTitle(r), }) for (let person of [r.fromPerson, r.toPerson]) { if (!getters.isPersonLoaded(person.id)) { dispatch('addMissingPerson', [person, r]) } } }, /** * Add missing person. node is displayed without label (folded). * We stop here and listen on events to unfold person and expand its fetch infos * @param object * @param array */ addMissingPerson({ commit, getters, dispatch }, [person, parent]) { console.log('! add missing Person', person.id) commit('markPersonLoaded', person.id) commit('addPerson', [person, { folded: true }]) if (getters.isExcludedNode(parent.id)) { commit('addExcludedNode', person.id) // init: exclude missing persons if parent is excluded } }, /** * ============================================================================= * * Triggered by a vis-network event when clicking on a Course Node. * Each folded node is unfold, then expanded with fetch infos * @param object * @param course */ unfoldPersonsByCourse({ getters, commit, dispatch }, course) { const participations = getters.getParticipationsByCourse(course.id) getters.getFoldedPersons(participations) .forEach(person => { console.log('-=. unfold and expand person', person.id) commit('unfoldPerson', person) dispatch('fetchInfoForPerson', person) }) }, /** * Triggered by a vis-network event when clicking on a Household Node. * Each folded node is unfold, then expanded with fetch infos * @param object * @param household */ unfoldPersonsByHousehold({ getters, commit, dispatch }, household) { const members = getters.getMembersByHousehold(household.id) getters.getFoldedPersons(members) .forEach(person => { console.log('-=. unfold and expand person', person.id) commit('unfoldPerson', person) dispatch('fetchInfoForPerson', person) }) }, /** * ============================================================================= * * For an excluded node, add|remove relative persons excluded too * @param object * @param array (add|remove action, id) */ excludedNode({ getters, commit }, [action, id]) { const personGroup = () => { switch (splitId(id, 'type')) { case 'accompanying_period': return getters.getParticipationsByCourse(id) case 'household': return getters.getMembersByHousehold(id) default: throw 'undefined case with this id' } } if (action === 'add') { commit('addExcludedNode', id) getters.getFoldedPersons(personGroup()) .forEach(person => { if (!getters.isInWhitelist(person.id)) { commit('addExcludedNode', person.id) } }) } if (action === 'remove') { commit('removeExcludedNode', id) getters.getFoldedPersons(personGroup()) .forEach(person => { commit('removeExcludedNode', person.id) }) } }, } }) export { store }