Add Vuex plan module

This commit is contained in:
D. Berge
2023-10-25 09:56:05 +02:00
parent 16a6cb59dc
commit a26974670a
6 changed files with 208 additions and 0 deletions

View File

@@ -7,6 +7,7 @@ import snack from './modules/snack'
import project from './modules/project'
import event from './modules/event'
import label from './modules/label'
import plan from './modules/plan'
import line from './modules/line'
import notify from './modules/notify'
@@ -20,6 +21,7 @@ export default new Vuex.Store({
project,
event,
label,
plan,
line,
notify
}

View File

@@ -0,0 +1,114 @@
/** Fetch sequences from server
*/
async function refreshPlan ({commit, dispatch, state, rootState}) {
if (state.loading) {
commit('abortPlanLoading');
}
commit('setPlanLoading');
const pid = rootState.project.projectId;
const url = `/project/${pid}/plan`;
const init = {
signal: state.loading.signal
};
const res = await dispatch('api', [url, init]);
if (res) {
commit('setPlan', res);
commit('setPlanTimestamp');
}
commit('clearPlanLoading');
}
/** Return a subset of sequences from state.sequences
*/
async function getPlannedSequences ({commit, dispatch, state}, [projectId, {sequence, date0, date1, sortBy, sortDesc, itemsPerPage, page, text}]) {
let filteredPlannedSequences = [...state.sequences];
if (sortBy) {
sortBy.forEach( (key, idx) => {
filteredPlannedSequences.sort( (el0, el1) => {
const a = el0?.[key];
const b = el1?.[key];
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else if (a == b) {
return 0;
} else if (a && !b) {
return 1;
} else if (!a && b) {
return -1;
} else {
return 0;
}
});
if (sortDesc && sortDesc[idx] === true) {
filteredPlannedSequences.reverse();
}
});
}
if (sequence) {
filteredPlannedSequences = filteredPlannedSequences.filter( sequence => sequence.sequence == sequence );
}
if (date0 && date1) {
filteredPlannedSequences = filteredPlannedSequences.filter( sequence =>
sequence.ts0.substr(0, 10) >= date0 && sequence.ts1.substr(0, 10) <= date1
);
} else if (date0) {
filteredPlannedSequences = filteredPlannedSequences.filter( sequence => sequence.ts0.substr(0, 10) == date0 || sequence.ts1.substr(0, 10) );
}
if (text) {
const tstampFilter = (value, search, item) => {
return textFilter(value.toISOString(), search, item);
};
const numberFilter = (value, search, item) => {
return value == search;
};
const textFilter = (value, search, item) => {
return String(value).toLowerCase().includes(search.toLowerCase());
};
const searchFunctions = {
sequence: numberFilter,
line: numberFilter,
remarks: textFilter,
ts0: tstampFilter,
ts1: tstampFilter
};
filteredPlannedSequences = filteredPlannedSequences.filter ( sequence => {
for (let key in searchFunctions) {
const fn = searchFunctions[key];
if (fn(sequence[key], text, sequence)) {
return true;
}
}
return false;
});
}
const count = filteredPlannedSequences.length;
if (itemsPerPage && itemsPerPage > 0) {
const offset = (page > 0)
? (page-1) * itemsPerPage
: 0;
filteredPlannedSequences = filteredPlannedSequences.slice(offset, offset+itemsPerPage);
}
return {sequences: filteredPlannedSequences, count};
}
export default { refreshPlan, getPlannedSequences };

View File

@@ -0,0 +1,18 @@
function planRemarks (state) {
return state.remarks;
}
function plannedSequences (state) {
return state.sequences;
}
function plannedSequenceCount (state) {
return state.sequences?.length ?? 0;
}
function plannedSequencesLoading (state) {
return !!state.loading;
}
export default { planRemarks, plannedSequences, plannedSequenceCount, plannedSequencesLoading };

View File

@@ -0,0 +1,6 @@
import state from './state'
import getters from './getters'
import actions from './actions'
import mutations from './mutations'
export default { state, getters, actions, mutations };

View File

@@ -0,0 +1,59 @@
function transform (item) {
item.ts0 = new Date(item.ts0);
item.ts1 = new Date(item.ts1);
return item;
}
// ATTENTION: This relies on the new planner endpoint
// as per issue #281.
function setPlan (state, plan) {
// We don't need or want the planned sequences array to be reactive
state.sequences = Object.freeze(plan.sequences.map(transform));
state.remarks = plan.remarks;
}
function setPlanLoading (state, abortController = new AbortController()) {
state.loading = abortController;
}
// This assumes that we know any transactions have finished or we
// don't care about aborting.
function clearPlanLoading (state) {
state.loading = null;
}
function setPlanTimestamp (state, timestamp = new Date()) {
// NOTE: There is no `modified_on` property in the plan
// result or in the database schema, but we should probably add
// one.
if (timestamp === true) {
const tstamp = state.plan
.map( item => item.modified_on )
.reduce( (acc, cur) => acc > cur ? acc : cur );
state.timestamp = tstamp ? new Date(tstamp) : new Date();
} else {
state.timestamp = timestamp;
}
}
function setPlanETag (state, etag) {
state.etag = etag;
}
function abortPlanLoading (state) {
if (state.loading) {
state.loading.abort();
}
state.loading = null;
}
export default {
setPlan,
setPlanLoading,
clearPlanLoading,
setPlanTimestamp,
setPlanETag
};

View File

@@ -0,0 +1,9 @@
const state = () => ({
sequences: Object.freeze([]),
remarks: null,
loading: null,
timestamp: null,
etag: null,
});
export default state;