491 lines
19 KiB
JavaScript

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}${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}${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 }