diff --git a/src/scripts/js/es6/common/vuecomponents/App.js b/src/scripts/js/es6/common/vuecomponents/App.js index 550b74d..dd060c2 100644 --- a/src/scripts/js/es6/common/vuecomponents/App.js +++ b/src/scripts/js/es6/common/vuecomponents/App.js @@ -13,8 +13,10 @@ const TEMPLATE =` :project="project" :selectedIds="selectedIds" :canChangeSelectionCB="canChangeSelectionCB" + :componentState="initialTableState" @selectItemsChanged="onSelectItemsChanged" @isInitialized="onTableInitialized" + @componentStateChanged="onTableStateChanged" />
@@ -27,6 +29,23 @@ const TEMPLATE =`
`; +class ComponentState { + /** + * Serializable state of this component. + * + * @param {Object} tableState + */ + constructor(tableState) { + this.tableState = tableState; + } +} +/** + * Component wrapping a table for selecting attract_task/asset/shot nodes, and a editor to edit the selected node(s). + * Selected row filters and visible columns are stored in localStorage per project/context. This makes the settings + * sticky between sessions in the same browser. + * Selected nodes are stored in window.history. This makes it possible to move back/forward in browser and the selection + * will change accordingly. + */ Vue.component('attract-app', { template: TEMPLATE, mixins: [BrowserHistoryState], @@ -98,7 +117,22 @@ Vue.component('attract-app', { return `/attract/${projectUrl}/${this.contextType}/${selected._id}`; } } - + }, + stateStorageKey() { + return `attract.${this.projectId}.${this.contextType}`; + }, + initialAppState() { + let stateJsonStr; + try { + stateJsonStr = localStorage.getItem(this.stateStorageKey); + } catch (error) { + // Log and ignore. + console.warn('Unable to restore state:', error); + } + return stateJsonStr ? JSON.parse(stateJsonStr) : undefined; + }, + initialTableState() { + return this.initialAppState ? this.initialAppState.tableState : undefined; } }, watch: { @@ -127,6 +161,20 @@ Vue.component('attract-app', { onTableInitialized() { this.isTableInited = true; }, + /** + * Save table state to localStorage per project and context + * @param {Object} newState + */ + onTableStateChanged(newState) { + let appState = new ComponentState(newState); + let stateJsonStr = JSON.stringify(appState); + try { + localStorage.setItem(this.stateStorageKey, stateJsonStr); + } catch (error) { + // Log and ignore. + console.warn('Unable to save state:', error); + } + }, canChangeSelectionCB() { if(this.isEditing) { let retval = confirm("You have unsaved data. Do you want to discard it?"); diff --git a/src/scripts/js/es6/common/vuecomponents/assetstable/Table.js b/src/scripts/js/es6/common/vuecomponents/assetstable/Table.js index f035459..f7ab580 100644 --- a/src/scripts/js/es6/common/vuecomponents/assetstable/Table.js +++ b/src/scripts/js/es6/common/vuecomponents/assetstable/Table.js @@ -1,7 +1,7 @@ let PillarTable = pillar.vuecomponents.table.PillarTable; import {AssetColumnFactory} from './columns/AssetColumnFactory' import {AssetRowsSource} from './rows/AssetRowsSource' -import {RowFilter} from '../attracttable/filter/RowFilter' +import {RowFilter} from '../attracttable/rows/filter/RowFilter' const TEMPLATE =`
@@ -41,6 +41,18 @@ let AssetsTable = Vue.component('attract-assets-table', { return { columnFactory: new AssetColumnFactory(this.project), rowsSource: new AssetRowsSource(this.project._id), + rowFilterConfig: {validStatuses: this.getValidStatuses()} + } + }, + methods: { + getValidStatuses() { + for (const it of this.project.node_types) { + if(it.name === 'attract_asset'){ + return it.dyn_schema.status.allowed; + } + } + console.warn('Did not find allowed statuses for node type attract_shot'); + return []; } }, components: { diff --git a/src/scripts/js/es6/common/vuecomponents/attracttable/filter/RowFilter.js b/src/scripts/js/es6/common/vuecomponents/attracttable/filter/RowFilter.js deleted file mode 100644 index eeeb095..0000000 --- a/src/scripts/js/es6/common/vuecomponents/attracttable/filter/RowFilter.js +++ /dev/null @@ -1,98 +0,0 @@ -let RowFilterBase = pillar.vuecomponents.table.filter.RowFilter; -const TEMPLATE =` -
- - - - -
    -
  • - Status: -
  • -
  • - Final -
  • -
  • - Approved -
  • -
  • - Cbb -
  • -
  • - Review -
  • -
  • - In Progress -
  • -
  • - Todo -
  • -
  • - On Hold -
  • -
-
-
-`; - -let RowFilter = { - extends: RowFilterBase, - template: TEMPLATE, - props: { - rowObjects: Array - }, - data() { - return { - showAssetStatus: { - todo: true, - in_progress: true, - on_hold: true, - approved: true, - cbb: true, - final: true, - review: true, - }, - } - }, - computed: { - nameQueryLoweCase() { - return this.nameQuery.toLowerCase(); - }, - visibleRowObjects() { - return this.rowObjects.filter((row) => { - if (!this.hasShowStatus(row)) return false; - return this.filterByName(row); - }); - } - }, - methods: { - hasShowStatus(rowObject) { - let status = rowObject.getProperties().status; - return !(this.showAssetStatus[status] === false); // To handle invalid statuses - }, - }, -}; - -export { RowFilter } diff --git a/src/scripts/js/es6/common/vuecomponents/attracttable/rows/AttractRowBase.js b/src/scripts/js/es6/common/vuecomponents/attracttable/rows/AttractRowBase.js index ce45608..6deca7a 100644 --- a/src/scripts/js/es6/common/vuecomponents/attracttable/rows/AttractRowBase.js +++ b/src/scripts/js/es6/common/vuecomponents/attracttable/rows/AttractRowBase.js @@ -9,6 +9,10 @@ class AttractRowBase extends RowBase { onRowUpdated(event) { this.underlyingObject = event.detail; } + + getStatus() { + return this.underlyingObject.properties.status; + } } export { AttractRowBase } diff --git a/src/scripts/js/es6/common/vuecomponents/attracttable/rows/filter/RowFilter.js b/src/scripts/js/es6/common/vuecomponents/attracttable/rows/filter/RowFilter.js new file mode 100644 index 0000000..60b66ce --- /dev/null +++ b/src/scripts/js/es6/common/vuecomponents/attracttable/rows/filter/RowFilter.js @@ -0,0 +1,68 @@ +let NameFilter = pillar.vuecomponents.table.rows.filter.NameFilter; +let StatusFilter = pillar.vuecomponents.table.rows.filter.StatusFilter; + +const TEMPLATE =` +
+ + +
+`; + +let RowFilter = { + template: TEMPLATE, + props: { + rowObjects: Array, + componentState: Object, + config: Object + }, + data() { + return { + availableStatuses: this.config.validStatuses, + nameFilteredRowObjects: [], + nameFilterState: (this.componentState || {}).nameFilter, + statusFilterState: (this.componentState || {}).statusFilter, + } + }, + methods: { + onNameFiltered(visibleRowObjects) { + this.nameFilteredRowObjects = visibleRowObjects; + }, + onNameFilterStateChanged(stateObj) { + this.nameFilterState = stateObj; + }, + onStatusFilterStateChanged(stateObj) { + this.statusFilterState = stateObj; + } + }, + computed: { + currentComponentState() { + return { + nameFilter: this.nameFilterState, + statusFilter: this.statusFilterState, + }; + } + }, + watch: { + currentComponentState(newValue) { + this.$emit('componentStateChanged', newValue); + } + }, + components: { + 'name-filter': NameFilter, + 'status-filter': StatusFilter + } +}; + +export { RowFilter } + diff --git a/src/scripts/js/es6/common/vuecomponents/shotstable/Table.js b/src/scripts/js/es6/common/vuecomponents/shotstable/Table.js index 7c3f94d..6f6bcd0 100644 --- a/src/scripts/js/es6/common/vuecomponents/shotstable/Table.js +++ b/src/scripts/js/es6/common/vuecomponents/shotstable/Table.js @@ -1,7 +1,7 @@ let PillarTable = pillar.vuecomponents.table.PillarTable; import {ShotsColumnFactory} from './columns/ShotsColumnFactory' import {ShotRowsSource} from './rows/ShotRowsSource' -import {RowFilter} from '../attracttable/filter/RowFilter' +import {RowFilter} from '../attracttable/rows/filter/RowFilter' let ShotsTable = Vue.component('attract-shots-table', { extends: PillarTable, @@ -12,6 +12,18 @@ let ShotsTable = Vue.component('attract-shots-table', { return { columnFactory: new ShotsColumnFactory(this.project), rowsSource: new ShotRowsSource(this.project._id), + rowFilterConfig: {validStatuses: this.getValidStatuses()} + } + }, + methods: { + getValidStatuses() { + for (const it of this.project.node_types) { + if(it.name === 'attract_shot'){ + return it.dyn_schema.status.allowed; + } + } + console.warn('Did not find allowed statuses for node type attract_shot'); + return []; } }, components: { diff --git a/src/scripts/js/es6/common/vuecomponents/taskstable/Table.js b/src/scripts/js/es6/common/vuecomponents/taskstable/Table.js index 269d81f..a937b44 100644 --- a/src/scripts/js/es6/common/vuecomponents/taskstable/Table.js +++ b/src/scripts/js/es6/common/vuecomponents/taskstable/Table.js @@ -1,7 +1,7 @@ let PillarTable = pillar.vuecomponents.table.PillarTable; import {TasksColumnFactory} from './columns/TasksColumnFactory' import {TaskRowsSource} from './rows/TaskRowsSource' -import {RowFilter} from '../attracttable/filter/RowFilter' +import {RowFilter} from '../attracttable/rows/filter/RowFilter' const TEMPLATE =`
@@ -41,6 +41,18 @@ let TasksTable = Vue.component('attract-tasks-table', { return { columnFactory: new TasksColumnFactory(this.project), rowsSource: new TaskRowsSource(this.project._id), + rowFilterConfig: {validStatuses: this.getValidStatuses()} + } + }, + methods: { + getValidStatuses() { + for (const it of this.project.node_types) { + if(it.name === 'attract_task'){ + return it.dyn_schema.status.allowed; + } + } + console.warn('Did not find allowed statuses for node type attract_task'); + return []; } }, components: {