Commit 4e1f962e authored by tobinski's avatar tobinski
Browse files

Merge branch 'refactor-store' into 1-add-match-view

parents b1aeec0d a604d943
......@@ -10,8 +10,16 @@
<script>
import Navbar from "@/components/Navbar";
import {mapActions} from 'vuex';
export default {
components: {Navbar}
components: {Navbar},
methods: {
...mapActions('provider', ['list']),
},
mounted() {
this.list();
}
}
</script>
......
<template>
<div class="edit-form" v-if="list.length > 0">
<div class="edit-form" v-if="list.length > 0" ref="editform">
<div class="toggle" @click="toggle">
<b-icon scale="1.5" icon="arrow-bar-up"/>
<b-badge :variant="hasError ? 'danger': 'secondary'" class="toggle-badge" v-if="toEdit > 0">{{toEdit}}</b-badge>
......@@ -13,7 +13,7 @@
<b-button @click="preDestroy" :disabled="!canDestroy" size="sm" v-b-tooltip.hover title="Destroy a concordance. Remove relations between the persons">
<b-icon icon="trash"></b-icon>
</b-button>
<b-button @click="preSave(editlist)" :disabled="!canSave" size="sm" v-b-tooltip.hover title="Save edited concordance as shown below.">
<b-button @click="preSave(list)" :disabled="!canSave" size="sm" v-b-tooltip.hover title="Save edited concordance as shown below.">
<b-icon icon="check"></b-icon>
</b-button>
<b-button @click="preClear" :disabled="!canClear" size="sm" v-b-tooltip.hover title="Cancel editing">
......@@ -24,8 +24,8 @@
</template>
<b-list-group flush>
<template v-for="(concordance) in list">
<b-list-group-item v-for="(person, key) in concordance.persons" :key="key">
<template v-for="(concordance, k1) in list">
<b-list-group-item v-for="(person, key) in concordance.persons" :key="`${k1}${key}`">
<div class="d-flex justify-content-between group-row" :class="person.error ? 'has-error':''">
<div>{{person.first_name}}</div>
<div>{{person.last_name}}</div>
......@@ -49,6 +49,7 @@
<script>
import {mapGetters, mapMutations, mapActions} from 'vuex';
import checks from '../plugins/checks';
import {Getters} from "@/store/concordance/type";
export default {
name: "EditForm",
data() {
......@@ -57,7 +58,7 @@
}
},
computed: {
...mapGetters(['editList']),
...mapGetters('concordance', [Getters.EDIT_LIST]),
/**
* Return the number of persons in the edit list
* @return number
......@@ -105,8 +106,8 @@
}
},
methods: {
...mapActions(['updateConcordance', 'mergeConcordance', 'destroyConcordance', 'updateMergeConcordances']),
...mapMutations(['removePerson', 'clearEditList']),
...mapActions('concordance', ['destroy', 'merge']),
...mapMutations('concordance', ['removePersonEditList', 'clearEditList']),
/**
* Toggle the state of the edit box
*/
......@@ -128,7 +129,7 @@
* Dynamically set a height if we change the number of persons in the edit box
*/
setOpenHeight() {
const height = document.getElementsByClassName('edit-form')[0].clientHeight;
const height = this.$refs.editform ? this.$refs.editform.clientHeight : 0;
const wHeight = window.innerHeight;
document.getElementsByClassName('edit-box')[0].style.top = wHeight - height + 'px';
},
......@@ -148,7 +149,7 @@
// don't update
return;
}
this.updateMergeConcordances();
this.merge();
setImmediate(this.toggle);
},
/**
......@@ -163,8 +164,12 @@
* @param person
*/
preRemovePerson(person) {
this.removePerson(person);
setImmediate(this.setOpenHeight);
this.removePersonEditList(person);
if(this.editList.length === 0) {
setImmediate(this.toggle)
} else {
setImmediate(this.setOpenHeight);
}
},
/**
* Add a guard before we destroy a concordance
......@@ -186,7 +191,7 @@
centered: true,
});
if (destroy) {
this.destroyConcordance(this.editList[0].id);
this.destroy(this.editList[0].id);
setImmediate(this.toggle);
}
}
......
<template>
<div>
<div class="d-flex justify-content-between mb-4" v-if="!isNew">
<h3>{{match.name}}</h3>
<h3>{{single.name}}</h3>
<b-button size="sm" @click="showConfig = !showConfig"><b-icon icon="gear"></b-icon></b-button>
</div>
<transition name="fade" duration="300" >
......@@ -27,13 +27,13 @@
id="group-name"
label="Name for the matching"
label-for="name">
<b-input name="name" id="name" v-model="match.name" :disabled="!isNew"></b-input>
<b-input name="name" id="name" v-model="single.name" :disabled="!isNew"></b-input>
</b-form-group>
</b-col>
</b-row>
<b-row v-for="(field, index) in match.parameters" :key="index">
<b-row v-for="(field, index) in single.parameters" :key="index">
<div class="col-11">
<MatchField v-model="match.parameters[index]" :disabled="!isNew"/>
<MatchField v-model="single.parameters[index]" :disabled="!isNew"/>
</div>
<div class="col-1 d-flex align-items-center justify-content-end" v-if="isNew">
<b-button @click="removeField(index)">
......@@ -51,7 +51,7 @@
<script>
import MatchField from "@/components/MatchField";
import FormContainer from "@/components/FormContainer";
import {mapActions, mapGetters} from 'vuex';
import {mapActions, mapGetters, mapMutations} from 'vuex';
export default {
name: "MatchForm",
components: {MatchField, FormContainer},
......@@ -71,32 +71,32 @@
} else {
this.showConfig = true;
}
// load data from store
this.getMatch(this.$route.params.id);
},
computed: {
...mapGetters(['match']),
...mapGetters('match', ['single']),
},
methods: {
...mapActions(['createMatch', 'updateMatch', 'runTestMatch', "getMatch"]),
...mapActions('match', {'new': 'new', 'singleAction': 'single'}),
...mapActions('proposal', ['test']),
/**
* Save
**/
async preSave() {
await this.createMatch({
name: this.match.name,
parameters: this.match.parameters,
await this.new({
name: this.single.name,
parameters: this.single.parameters,
});
// todo: navigate
},
/**
* Run a test match
* todo: pagination!
*/
preTest() {
this.runTestMatch(
this.test(
{
name: this.match.name,
parameters: this.match.parameters,
name: this.single.name,
parameters: this.single.parameters,
}
)
},
......@@ -105,13 +105,13 @@
* @param index
*/
removeField(index) {
this.match.parameters.splice(index,1);
this.single.parameters.splice(index,1);
},
/**
* Add single search field
*/
addField() {
this.match.parameters.push({
this.single.parameters.push({
field: null,
operator: 'must',
option1: '',
......
......@@ -2,8 +2,8 @@
<div>
<b-table
id="my-table"
:busy.sync="isBusy"
:items="matchList"
:busy.sync="loading"
:items="list"
:fields="fields" >
<template v-slot:cell(id)="id">
<!-- `data.value` is the value after formatted by the Formatter -->
......@@ -18,12 +18,11 @@
</template>
<script>
import {mapGetters, mapActions} from 'vuex'
import {mapGetters} from 'vuex'
export default {
name: "MatchList",
data () {
return {
isBusy: false,
fields: [
{
key: 'id',
......@@ -48,14 +47,8 @@
]
}
},
mounted() {
this.getMatches();
},
methods: {
...mapActions(['getMatches']),
},
computed: {
...mapGetters(['matchList'])
...mapGetters('match', ['list', 'loading'])
},
filters: {
date: function(d) {
......
<template>
<b-pagination v-model="current"
:total-rows="total"
:per-page="size"
first-text="First"
prev-text="Prev"
next-text="Next"
last-text="Last"
@change="change"
v-if="total > size">
</b-pagination>
</template>
<script>
export default {
name: "Pagination",
props: {
get: {
type: Function,
required: true
}
},
data() {
return {
total: 0,
size: 20,
current: 1,
}
},
methods: {
// trigger the callback function
async change(value) {
// if we trigger the function from an external component
if (typeof value === "undefined") {
value = this.current;
}
const from = (value - 1) * this.size;
this.total = await this.get({from, size: this.size})
}
}
}
</script>
<style scoped>
</style>
<template>
<div>
<transition-group name="fade" mode="out-in">
<div v-for="(ml, index) in proposalList" :key="index" class="mb-4">
<div v-for="(ml, index) in list" :key="index" class="mb-4">
<b-card no-body header-bg-variant="primary" border-variant="primary" header-text-variant="light" class="mb-0 rounded-top rounded-bottom-0 card-master">
<template v-slot:header>
<div class="action-header">
......@@ -46,58 +46,55 @@
</b-list-group-item>
</b-list-group>
</b-card>
<b-btn-group>
<b-button @click="preSave(ml)">Save</b-button>
</b-btn-group>
</div>
</transition-group>
<b-pagination v-model="currentPage"
:total-rows="rows"
:per-page="perPage"
first-text="First"
prev-text="Prev"
next-text="Next"
last-text="Last"
@change="paginate">
</b-pagination>
</div>
</template>
<script>
import {mapGetters, mapActions} from 'vuex';
import {mapGetters, mapActions, mapMutations} from 'vuex';
export default {
name: "ProposalList",
data: function() {
return {
id: null,
currentPage: 0,
// todo: this is hardcoded and not good!
rows: 200,
perPage: 20,
matches: [],
}
},
mounted() {
// check if new
this.id = this.$route.params.id;
if (this.id !== 'new') {
// load data from store
this.getProposals({id: this.id, pagination: {start: 0, limit: 20}});
}
},
computed: {
...mapGetters(['proposalList']),
...mapGetters('proposal', ['list']),
proposals() {
return this.proposalList.map((p) => {
return this.list.map((p) => {
p.base.match = true;
p.base.persons.forEach((person) => person.match = true);
p.proposals.forEach((prop) => {
prop.match = false;
prop.persons.forEach((pp) => pp.match = false)
})
})
}
},
methods: {
...mapActions(['getProposals']),
...mapActions('proposal', ['accept', 'reject']),
...mapActions('concordance', ['update', 'merge']),
...mapMutations('concordance', ['edit']),
...mapMutations('proposal', ['remove']),
preSave(proposal) {
// merge concordances
const matches = proposal.proposals.filter((c) => c.match);
// merge two concordances
if(matches.length > 0) {
this.edit(proposal.base);
for (const c of matches) {
this.edit(c)
}
this.merge()
}
// save all refused
const refused = proposal.proposals.filter((c) => !c.match);
if(refused.length > 0) {
// save in base
refused.push(proposal.base);
for(const r of refused) {
r.refused_matches.push(...refused.filter(c => c.id !== r.id).map(c => c.id));
this.update(r);
}
}
this.remove(proposal);
},
changeProposal(proposal, $event) {
if($event) {
proposal.persons.forEach((p) => {
......@@ -107,12 +104,8 @@
proposal.persons.forEach((p) => {
p.match = false;
})
}
},
paginate() {
this.getProposals({id: this.id, pagination: {start: (this.currentPage * this.perPage), limit: this.perPage}});
}
}
}
</script>
......
<template>
<div>
<transition-group name="fade" mode="out-in">
<b-card no-body v-for="(item, index) in searchResultList" :key="index" class="mb-4">
<b-card no-body v-for="(item, index) in list" :key="index" class="mb-4">
<template v-slot:header>
<div class="action-header">
<h6 class="mb-0">Concordance #{{item.id}}</h6>
......@@ -30,10 +30,10 @@
export default {
name: "ResultList",
computed: {
...mapGetters(["searchResultList"])
...mapGetters('concordance', ["list"])
},
methods: {
...mapMutations(['edit']),
...mapMutations('concordance',['edit']),
preEdit(item) {
// hook
this.edit(item);
......
......@@ -51,7 +51,7 @@
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import { mapGetters, mapMutations } from 'vuex'
import FormContainer from "@/components/FormContainer";
export default {
name: "SearchForm",
......@@ -66,13 +66,8 @@
year2: 0,
}
},
mounted() {
// load providers if not yet loaded
// todo: check if loaded in store. Do this while initializing the app
this.providers();
},
computed: {
...mapGetters(['providerList']),
...mapGetters('provider', {providerList: 'list'}),
providerOptions() {
const list = this.providerList.map((i) => {
const text = i.provider_details.find((pd) => pd.fallback).name;
......@@ -84,7 +79,7 @@
}
},
methods: {
...mapActions(['search', "providers"]),
...mapMutations("concordance", {'queryMutation': 'query'}),
preSearch(e) {
e.preventDefault();
const query = {
......@@ -94,11 +89,9 @@
year1: this.year1,
year2: this.year2,
};
const pagination = {
start: 0,
limit: 20,
};
this.search({search: query, pagination})
// set query in store
this.queryMutation(query);
this.$emit("search", query);
},
toggleFilter() {
this.showFilter = !this.showFilter;
......
import {Module} from 'vuex';
import {api, Concordance, Pagination, Person, SearchQuery} from '@/store';
import {Actions, Getters, Mutations} from '@/store/concordance/type';
import {TmpPagination} from '@/store/proposal';
// State of the concordance
export interface ConcordanceState {
search: SearchQuery;
loading: boolean;
list: Concordance[];
editList: Concordance[];
}
// SearchQueryKeys
export enum SearchQueryKeys {
query = 'query',
length = 'length',
provider = 'provider',
year1 ='year1',
year2 = 'year2',
}
const concordance: Module<ConcordanceState, any> = {
namespaced: true,
state: {
loading: false,
search: {
query: '',
length: 0,
provider: '',
year1: -1,
year2: -1,
},
list: [],
editList: [],
},
getters: {
/**
* Get the list of proposals
* @param state
*/
[Getters.LIST]: state => {
return state.list;
},
/**
* Get the state of loading
* @param state
*/
[Getters.LOADING]: state => {
return state.loading;
},
/**
* The edit list of concordance
* @param state
*/
[Getters.EDIT_LIST]: state => {
return state.editList;
},
},
mutations: {
/**
* Save search query
* @param state
* @param query
*/
[Mutations.QUERY](state, query: SearchQuery) {
state.search.query = query.query;
state.search.length = query.length;
state.search.provider = query.provider;
state.search.year1 = query.year1;
state.search.year2 = query.year2;
},
/**
* Commit list of concordances
* @param state
* @param data
*/
[Mutations.LIST](state, data: []) {
state.list = data;
},
/**
* init loading
* @param state
*/
[Mutations.START_LOAD]: (state) => {
state.loading = true;
},
/**
* finish loading
* @param state
*/
[Mutations.FINISH_LOAD]: (state) => {
state.loading = false;
},
/**
* Put a concordance into the edit list
* @param state
* @param concordance
*/
[Mutations.EDIT_LIST](state, concordance: Concordance) {
const index = state.list.findIndex((v: any) => v.id === concordance.id);
state.list.splice(index, 1);
state.editList.push(concordance);
},
/**
* Clear edit state
* @param state
*/
[Mutations.CLEAR_EDIT_LIST](state) {
state.editList = [];
},
/**
* Remove a person from the edit array of concordances
* todo: refactor, this looks too complicated
* @param state
* @param person
*/
[Mutations.REMOVE_PERSON_EDIT_LIST](state, person: Person) {
const editData = state.editList;
for (let i = editData.length - 1; i >= 0; i -= 1) {
const persons = editData[i].persons;
for (let x = persons.length - 1; x >= 0; x -= 1) {
if(persons[x] === person) {
persons.splice(x, 1);
}
}