mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
541 lines
22 KiB
JavaScript
541 lines
22 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";
|
|
import { darkBlue, darkBrown, darkGreen, lightBlue, lightBrown, lightGreen } from './colors';
|
|
|
|
const debug = process.env.NODE_ENV !== 'production'
|
|
|
|
const store = createStore({
|
|
strict: debug,
|
|
state: {
|
|
persons: [],
|
|
households: [],
|
|
courses: [],
|
|
relationships: [],
|
|
links: [],
|
|
whitelistIds: [],
|
|
personLoadedIds: [],
|
|
householdLoadingIds: [],
|
|
courseLoadedIds: [],
|
|
relationshipLoadedIds: [],
|
|
excludedNodesIds: [],
|
|
updateHack: 0
|
|
},
|
|
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) {
|
|
return state.links
|
|
},
|
|
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)
|
|
},
|
|
|
|
countLinksByNode: (state) => (node_id) => {
|
|
let array = []
|
|
state.links.filter(link => ! link.id.startsWith('relationship'))
|
|
.forEach(link => {
|
|
if (link.from === node_id || link.to === node_id) {
|
|
if (state.excludedNodesIds.indexOf(splitId(link.id, 'link')) === -1) {
|
|
array.push(link)
|
|
}
|
|
//console.log(link.id, state.excludedNodesIds.indexOf(splitId(link.id, 'link')))
|
|
}
|
|
})
|
|
//console.log('count links', array.length, array.map(i => i.id))
|
|
return array.length
|
|
},
|
|
|
|
getParticipationsByCourse: (state) => (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) => {
|
|
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 is a little bit mysterious :
|
|
* The 2 previous getters return complete array, but folded (missing) persons are not taken into consideration and are not displayed (!?!)
|
|
* This getter compare input array (participations|members) to personLoadedIds array
|
|
* and return complete array with folded persons taken into consideration
|
|
*
|
|
* @param state
|
|
* @param array - An array of persons from course or household.
|
|
* This array is dirty, melting persons adapted (or not) to vis, with _id and _label.
|
|
* @return array - An array of persons mapped and taken in state.persons
|
|
*/
|
|
getPersonsGroup: (state) => (array) => {
|
|
let group = []
|
|
array.forEach(item => {
|
|
let id = splitId(item.person.id, 'id')
|
|
if (state.personLoadedIds.includes(id)) {
|
|
group.push(state.persons.filter(person => person._id === id)[0])
|
|
}
|
|
})
|
|
//console.log('array', array.map(item => item.person.id))
|
|
//console.log('get persons group', group.map(f => f.id))
|
|
return group
|
|
},
|
|
|
|
|
|
},
|
|
mutations: {
|
|
addPerson(state, [person, options]) {
|
|
let age = getAge(person)
|
|
age = (age === '')? '' : ' - ' + age
|
|
|
|
let debug = ''
|
|
/// Debug mode: uncomment to display person_id on visgraph
|
|
//debug = `\nid ${person.id}`
|
|
|
|
person.group = person.type
|
|
person._id = person.id
|
|
person.id = `person_${person.id}`
|
|
person.label = `*${person.text}${person.deathdate ? ' (‡)' : ''}*\n_${getGender(person.gender)}${age}_${debug}`
|
|
person.folded = false
|
|
// folded is used for missing persons
|
|
if (options.folded) {
|
|
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) {
|
|
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) {
|
|
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) {
|
|
relationship.group = relationship.type
|
|
relationship._id = relationship.id
|
|
relationship.id = `relationship_${relationship.id}`
|
|
state.relationships.push(relationship)
|
|
},
|
|
addLink(state, link) {
|
|
state.links.push(link)
|
|
},
|
|
updateLink(state, link) {
|
|
//console.log('updateLink', link)
|
|
let link_ = {
|
|
from: `person_${link.fromPerson.id}`,
|
|
to: `person_${link.toPerson.id}`,
|
|
id: 'relationship_' + splitId(link.id,'id')
|
|
+ '-person_' + link.fromPerson.id + '-person_' + link.toPerson.id,
|
|
arrows: getRelationshipDirection(link),
|
|
color: lightGreen,
|
|
font: { color: darkGreen },
|
|
dashes: true,
|
|
label: getRelationshipLabel(link),
|
|
title: getRelationshipTitle(link),
|
|
relation: link.relation,
|
|
reverse: link.reverse
|
|
}
|
|
// find row position and replace by updatedLink
|
|
state.links.splice(
|
|
state.links.findIndex(item => item.id === link_.id), 1, link_
|
|
)
|
|
},
|
|
removeLink(state, link_id) {
|
|
state.links = state.links.filter(l => l.id !== link_id)
|
|
},
|
|
|
|
//// 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) {
|
|
//console.log('unfoldPerson', person)
|
|
person.label = person._label
|
|
delete person._label
|
|
delete person.title
|
|
person.folded = false
|
|
},
|
|
|
|
//// force update hack
|
|
updateHack(state) {
|
|
state.updateHack = state.updateHack + 1
|
|
}
|
|
},
|
|
actions: {
|
|
/**
|
|
* Expand loop (steps 1->10), always start from a person.
|
|
* Fetch household, courses, relationships, and others persons.
|
|
* These persons are "missing" and will be first display in fold mode.
|
|
*
|
|
* 1) Add a new person
|
|
* @param object
|
|
* @param person
|
|
*/
|
|
addPerson({ commit, dispatch }, person) {
|
|
commit('markPersonLoaded', person.id)
|
|
commit('addPerson', [person, { folded: false }])
|
|
commit('updateHack')
|
|
dispatch('fetchInfoForPerson', person)
|
|
},
|
|
|
|
/**
|
|
* 2) Fetch infos for this person (hub)
|
|
* @param object
|
|
* @param person
|
|
*/
|
|
fetchInfoForPerson({ dispatch }, person) {
|
|
// TODO enfants hors ménages
|
|
// example: household 61
|
|
//console.log(person.text, 'household', person.current_household_id)
|
|
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)
|
|
// DISABLED: in init or expand loop, layer is uncheck when added
|
|
//commit('addExcludedNode', household.id)
|
|
//commit('updateHack')
|
|
dispatch('addLinkFromPersonsToHousehold', household)
|
|
commit('updateHack')
|
|
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.id}`,
|
|
id: `${household.id}-person_${m.person.id}`,
|
|
arrows: 'from',
|
|
color: lightBrown,
|
|
font: { color: darkBrown },
|
|
dashes: (getHouseholdWidth(m) === 1)? [0,4] : false, //edge style: [dash, gap, dash, gap]
|
|
//label: getHouseholdLabel(m),
|
|
width: getHouseholdWidth(m),
|
|
})
|
|
if (!getters.isPersonLoaded(m.person.id)) {
|
|
dispatch('addMissingPerson', [m.person, household])
|
|
}
|
|
})
|
|
},
|
|
|
|
/**
|
|
* 5) Fetch AccompanyingCourses for the person
|
|
* @param object
|
|
* @param person
|
|
*/
|
|
fetchCoursesByPerson({ commit, dispatch }, person) {
|
|
getCoursesByPerson(person)
|
|
.then(courses => new Promise(resolve => {
|
|
dispatch('addCourses', courses)
|
|
resolve()
|
|
}))
|
|
},
|
|
|
|
/**
|
|
* 6) Add each distinct course (a person can have multiple courses)
|
|
* @param object
|
|
* @param courses
|
|
*/
|
|
addCourses({ commit, getters, dispatch }, courses) {
|
|
let currentCourses = courses.filter(c => c.closingDate === null)
|
|
currentCourses.forEach(course => {
|
|
//console.log(' isCourseLoaded ?', getters.isCourseLoaded(course.id))
|
|
if (! getters.isCourseLoaded(course.id)) {
|
|
commit('markCourseLoaded', course.id)
|
|
commit('addCourse', course)
|
|
commit('addExcludedNode', course.id) // in init or expand loop, layer is uncheck when added
|
|
dispatch('addLinkFromPersonsToCourse', course)
|
|
commit('updateHack')
|
|
}
|
|
})
|
|
},
|
|
|
|
/**
|
|
* 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: `accompanying_period_${splitId(course.id,'id')}-person_${p.person.id}`,
|
|
arrows: 'from',
|
|
color: lightBlue,
|
|
font: { color: darkBlue },
|
|
})
|
|
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 => {
|
|
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)) {
|
|
commit('markRelationshipLoaded', relationship.id)
|
|
commit('addRelationship', relationship)
|
|
dispatch('addLinkFromRelationship', relationship)
|
|
commit('updateHack')
|
|
}
|
|
})
|
|
},
|
|
|
|
/**
|
|
* 10) Add an edge for each relationship (person -> person)
|
|
* @param object
|
|
* @param relationship
|
|
*/
|
|
addLinkFromRelationship({ commit, getters, dispatch }, relationship) {
|
|
//console.log('-> addLink from person', relationship.fromPerson.id, 'to person', relationship.toPerson.id)
|
|
commit('addLink', {
|
|
from: `person_${relationship.fromPerson.id}`,
|
|
to: `person_${relationship.toPerson.id}`,
|
|
id: 'relationship_' + splitId(relationship.id,'id')
|
|
+ '-person_' + relationship.fromPerson.id + '-person_' + relationship.toPerson.id,
|
|
arrows: getRelationshipDirection(relationship),
|
|
color: lightGreen,
|
|
font: { color: darkGreen },
|
|
dashes: true,
|
|
label: getRelationshipLabel(relationship),
|
|
title: getRelationshipTitle(relationship),
|
|
relation: relationship.relation,
|
|
reverse: relationship.reverse
|
|
})
|
|
for (let person of [relationship.fromPerson, relationship.toPerson]) {
|
|
if (!getters.isPersonLoaded(person.id)) {
|
|
dispatch('addMissingPerson', [person, relationship])
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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)) {
|
|
// in init or expand loop, exclude too missing persons if parent have been excluded
|
|
commit('addExcludedNode', person.id)
|
|
}
|
|
commit('updateHack')
|
|
},
|
|
|
|
/**
|
|
* ==================================================================
|
|
* 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.getPersonsGroup(participations)
|
|
.forEach(person => {
|
|
if (person.folded === true) {
|
|
//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.getPersonsGroup(members)
|
|
.forEach(person => {
|
|
if (person.folded === true) {
|
|
//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'
|
|
}
|
|
}
|
|
let group = getters.getPersonsGroup(personGroup())
|
|
if (action === 'add') {
|
|
commit('addExcludedNode', id)
|
|
group.forEach(person => {
|
|
// countLinks < 2 but parent has just already been added !
|
|
if (!getters.isInWhitelist(person.id) && getters.countLinksByNode(person.id) < 1) {
|
|
commit('addExcludedNode', person.id)
|
|
}
|
|
})
|
|
}
|
|
if (action === 'remove') {
|
|
commit('removeExcludedNode', id)
|
|
group.forEach(person => {
|
|
commit('removeExcludedNode', person.id)
|
|
})
|
|
}
|
|
commit('updateHack')
|
|
},
|
|
|
|
}
|
|
})
|
|
|
|
export { store }
|