From 680e376ed1cd346717939ab0c0342d62d9374fc0 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Wed, 25 Oct 2023 09:57:05 +0200 Subject: [PATCH] Add Vuex `sequence` module --- lib/www/client/source/src/store/index.js | 2 + .../src/store/modules/sequence/actions.js | 122 ++++++++++++++++++ .../src/store/modules/sequence/getters.js | 14 ++ .../src/store/modules/sequence/index.js | 6 + .../src/store/modules/sequence/mutations.js | 49 +++++++ .../src/store/modules/sequence/state.js | 8 ++ 6 files changed, 201 insertions(+) create mode 100644 lib/www/client/source/src/store/modules/sequence/actions.js create mode 100644 lib/www/client/source/src/store/modules/sequence/getters.js create mode 100644 lib/www/client/source/src/store/modules/sequence/index.js create mode 100644 lib/www/client/source/src/store/modules/sequence/mutations.js create mode 100644 lib/www/client/source/src/store/modules/sequence/state.js diff --git a/lib/www/client/source/src/store/index.js b/lib/www/client/source/src/store/index.js index 5b6b7f2..672200a 100644 --- a/lib/www/client/source/src/store/index.js +++ b/lib/www/client/source/src/store/index.js @@ -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 sequence from './modules/sequence' import plan from './modules/plan' import line from './modules/line' import notify from './modules/notify' @@ -21,6 +22,7 @@ export default new Vuex.Store({ project, event, label, + sequence, plan, line, notify diff --git a/lib/www/client/source/src/store/modules/sequence/actions.js b/lib/www/client/source/src/store/modules/sequence/actions.js new file mode 100644 index 0000000..f4eec87 --- /dev/null +++ b/lib/www/client/source/src/store/modules/sequence/actions.js @@ -0,0 +1,122 @@ + +/** Fetch sequences from server + */ +async function refreshSequences ({commit, dispatch, state, rootState}) { + + if (state.loading) { + commit('abortSequencesLoading'); + } + + commit('setSequencesLoading'); + const pid = rootState.project.projectId; + const url = `/project/${pid}/sequence?files=true`; + const init = { + signal: state.loading.signal + }; + const res = await dispatch('api', [url, init]); + + if (res) { + commit('setSequences', res); + commit('setSequencesTimestamp'); + } + commit('clearSequencesLoading'); +} + +/** Return a subset of sequences from state.sequences + */ +async function getSequences ({commit, dispatch, state}, [projectId, {sequence, date0, date1, sortBy, sortDesc, itemsPerPage, page, text}]) { + let filteredSequences = [...state.sequences]; + + if (sortBy) { + + sortBy.forEach( (key, idx) => { + filteredSequences.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) { + filteredSequences.reverse(); + } + }); + + } + + if (sequence) { + filteredSequences = filteredSequences.filter( sequence => sequence.sequence == sequence ); + } + + if (date0 && date1) { + filteredSequences = filteredSequences.filter( sequence => + (sequence.ts0_final ?? sequence.ts0)?.substr(0, 10) >= date0 && + (sequence.ts1_final ?? sequence.ts1)?.substr(0, 10) <= date1 + ); + } else if (date0) { + filteredSequences = filteredSequences.filter( sequence => (sequence.ts0_final ?? sequence.ts0)?.substr(0, 10) == date0 || (sequence.ts1_final ?? sequence.ts1)?.substr(0, 10) ); + } + + if (text) { + const tstampFilter = (value, search, item) => { + return search?.length >= 5 && textFilter(value, search, item); + }; + + const numberFilter = (value, search, item) => { + return value == search; + }; + + const textFilter = (value, search, item) => { + return String(value).toLowerCase().includes(search.toLowerCase()); + }; + + const searchFunctions = { + ts0: tstampFilter, + ts1: tstampFilter, + ts0_final: tstampFilter, + ts1_final: tstampFilter, + sequence: numberFilter, + line: numberFilter, + fsp: numberFilter, + lsp: numberFilter, + fsp_final: numberFilter, + fsp_final: numberFilter, + remarks: textFilter, + remarks_final: textFilter + }; + + filteredSequences = filteredSequences.filter ( sequence => { + for (let key in searchFunctions) { + const fn = searchFunctions[key]; + if (fn(sequence[key], text, sequence)) { + return true; + } + } + return false; + }); + } + + const count = filteredSequences.length; + + if (itemsPerPage && itemsPerPage > 0) { + const offset = (page > 0) + ? (page-1) * itemsPerPage + : 0; + + filteredSequences = filteredSequences.slice(offset, offset+itemsPerPage); + } + + return {sequences: filteredSequences, count}; +} + +export default { refreshSequences, getSequences }; diff --git a/lib/www/client/source/src/store/modules/sequence/getters.js b/lib/www/client/source/src/store/modules/sequence/getters.js new file mode 100644 index 0000000..3a24627 --- /dev/null +++ b/lib/www/client/source/src/store/modules/sequence/getters.js @@ -0,0 +1,14 @@ + +function sequences (state) { + return state.sequences; +} + +function sequenceCount (state) { + return state.sequences?.length ?? 0; +} + +function sequencesLoading (state) { + return !!state.loading; +} + +export default { sequences, sequenceCount, sequencesLoading }; diff --git a/lib/www/client/source/src/store/modules/sequence/index.js b/lib/www/client/source/src/store/modules/sequence/index.js new file mode 100644 index 0000000..dae701e --- /dev/null +++ b/lib/www/client/source/src/store/modules/sequence/index.js @@ -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 }; diff --git a/lib/www/client/source/src/store/modules/sequence/mutations.js b/lib/www/client/source/src/store/modules/sequence/mutations.js new file mode 100644 index 0000000..dad1b7a --- /dev/null +++ b/lib/www/client/source/src/store/modules/sequence/mutations.js @@ -0,0 +1,49 @@ + +function setSequences (state, sequences) { + // We don't need or want the events array to be reactive, since + // it can be tens of thousands of items long. + state.sequences = Object.freeze(sequences); +} + +function setSequencesLoading (state, abortController = new AbortController()) { + state.loading = abortController; +} + +// This assumes that we know any transactions have finished or we +// don't care about aborting. +function clearSequencesLoading (state) { + state.loading = null; +} + +function setSequencesTimestamp (state, timestamp = new Date()) { + // NOTE: There is no `modified_on` property in the sequences + // result or in the database schema, but we should probably add + // one. + if (timestamp === true) { + const tstamp = state.sequences + .map( event => event.modified_on ) + .reduce( (acc, cur) => acc > cur ? acc : cur ); + state.timestamp = tstamp ? new Date(tstamp) : new Date(); + } else { + state.timestamp = timestamp; + } +} + +function setSequencesETag (state, etag) { + state.etag = etag; +} + +function abortSequencesLoading (state) { + if (state.loading) { + state.loading.abort(); + } + state.loading = null; +} + +export default { + setSequences, + setSequencesLoading, + clearSequencesLoading, + setSequencesTimestamp, + setSequencesETag +}; diff --git a/lib/www/client/source/src/store/modules/sequence/state.js b/lib/www/client/source/src/store/modules/sequence/state.js new file mode 100644 index 0000000..0e3ea9e --- /dev/null +++ b/lib/www/client/source/src/store/modules/sequence/state.js @@ -0,0 +1,8 @@ +const state = () => ({ + sequences: Object.freeze([]), + loading: null, + timestamp: null, + etag: null, +}); + +export default state;