From 16a6cb59dc7707e5b68e2473b94291890eba9bca Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Wed, 25 Oct 2023 09:54:46 +0200 Subject: [PATCH] Add Vuex `line` module --- lib/www/client/source/src/store/index.js | 2 + .../source/src/store/modules/line/actions.js | 117 ++++++++++++++++++ .../source/src/store/modules/line/getters.js | 14 +++ .../source/src/store/modules/line/index.js | 6 + .../src/store/modules/line/mutations.js | 49 ++++++++ .../source/src/store/modules/line/state.js | 8 ++ 6 files changed, 196 insertions(+) create mode 100644 lib/www/client/source/src/store/modules/line/actions.js create mode 100644 lib/www/client/source/src/store/modules/line/getters.js create mode 100644 lib/www/client/source/src/store/modules/line/index.js create mode 100644 lib/www/client/source/src/store/modules/line/mutations.js create mode 100644 lib/www/client/source/src/store/modules/line/state.js diff --git a/lib/www/client/source/src/store/index.js b/lib/www/client/source/src/store/index.js index 2c9116f..a01cf15 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 line from './modules/line' import notify from './modules/notify' Vue.use(Vuex) @@ -19,6 +20,7 @@ export default new Vuex.Store({ project, event, label, + line, notify } }) diff --git a/lib/www/client/source/src/store/modules/line/actions.js b/lib/www/client/source/src/store/modules/line/actions.js new file mode 100644 index 0000000..2431a80 --- /dev/null +++ b/lib/www/client/source/src/store/modules/line/actions.js @@ -0,0 +1,117 @@ + +/** Fetch lines from server + */ +async function refreshLines ({commit, dispatch, state, rootState}) { + + if (state.loading) { + commit('abortLinesLoading'); + } + + commit('setLinesLoading'); + const pid = rootState.project.projectId; + const url = `/project/${pid}/line`; + const init = { + signal: state.loading.signal + }; + const res = await dispatch('api', [url, init]); + + if (res) { + commit('setLines', res); + commit('setLinesTimestamp'); + } + commit('clearLinesLoading'); +} + +/** Return a subset of lines from state.lines + */ +async function getLines ({commit, dispatch, state}, [projectId, {line, fsp, lsp, incr, sortBy, sortDesc, itemsPerPage, page, text}]) { + let filteredLines = [...state.lines]; + + if (sortBy) { + + sortBy.forEach( (key, idx) => { + filteredLines.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) { + filteredLines.reverse(); + } + }); + + } + + if (line) { + filteredLines = filteredLines.filter( line => line.line == line ); + } + + if (fsp) { + filteredLines = filteredLines.filter( line => line.fsp == fsp ); + } + + if (lsp) { + filteredLines = filteredLines.filter( line => line.lsp == lsp ); + } + + if (text) { + const numberFilter = (value, search, item) => { + return value == search; + }; + + const textFilter = (value, search, item) => { + return String(value).toLowerCase().includes(search.toLowerCase()); + }; + + const incrFilter = (value, search, item) => { + const inc = /^(incr(ement)?|↑|\+)/i; + const dec = /^(decr(ement)?|↓|-)/i; + return (inc.test(search) && value) || (dec.test(search) && !value) + } + + const searchFunctions = { + line: numberFilter, + fsp: numberFilter, + lsp: numberFilter, + remarks: textFilter, + incr: incrFilter, + ntba: (value, search, item) => text.toLowerCase() == "ntba" && value + }; + + filteredLines = filteredLines.filter ( line => { + for (let key in searchFunctions) { + const fn = searchFunctions[key]; + if (fn(line[key], text, line)) { + return true; + } + } + return false; + }); + } + + const count = filteredLines.length; + + if (itemsPerPage && itemsPerPage > 0) { + const offset = (page > 0) + ? (page-1) * itemsPerPage + : 0; + + filteredLines = filteredLines.slice(offset, offset+itemsPerPage); + } + + return {lines: filteredLines, count}; +} + +export default { refreshLines, getLines }; diff --git a/lib/www/client/source/src/store/modules/line/getters.js b/lib/www/client/source/src/store/modules/line/getters.js new file mode 100644 index 0000000..2c95bd0 --- /dev/null +++ b/lib/www/client/source/src/store/modules/line/getters.js @@ -0,0 +1,14 @@ + +function lines (state) { + return state.lines; +} + +function lineCount (state) { + return state.lines?.length ?? 0; +} + +function linesLoading (state) { + return !!state.loading; +} + +export default { lines, lineCount, linesLoading }; diff --git a/lib/www/client/source/src/store/modules/line/index.js b/lib/www/client/source/src/store/modules/line/index.js new file mode 100644 index 0000000..dae701e --- /dev/null +++ b/lib/www/client/source/src/store/modules/line/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/line/mutations.js b/lib/www/client/source/src/store/modules/line/mutations.js new file mode 100644 index 0000000..2a272c1 --- /dev/null +++ b/lib/www/client/source/src/store/modules/line/mutations.js @@ -0,0 +1,49 @@ + +function setLines (state, lines) { + // We don't need or want the events array to be reactive, since + // it can be tens of thousands of items long. + state.lines = Object.freeze(lines); +} + +function setLinesLoading (state, abortController = new AbortController()) { + state.loading = abortController; +} + +// This assumes that we know any transactions have finished or we +// don't care about aborting. +function clearLinesLoading (state) { + state.loading = null; +} + +function setLinesTimestamp (state, timestamp = new Date()) { + // NOTE: There is no `modified_on` property in the lines + // result or in the database schema, but we could perhaps add + // one. + if (timestamp === true) { + const tstamp = state.lines + .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 setLinesETag (state, etag) { + state.etag = etag; +} + +function abortLinesLoading (state) { + if (state.loading) { + state.loading.abort(); + } + state.loading = null; +} + +export default { + setLines, + setLinesLoading, + clearLinesLoading, + setLinesTimestamp, + setLinesETag +}; diff --git a/lib/www/client/source/src/store/modules/line/state.js b/lib/www/client/source/src/store/modules/line/state.js new file mode 100644 index 0000000..3b00c95 --- /dev/null +++ b/lib/www/client/source/src/store/modules/line/state.js @@ -0,0 +1,8 @@ +const state = () => ({ + lines: Object.freeze([]), + loading: null, + timestamp: null, + etag: null, +}); + +export default state;