Add treeView to visualize changes in transaction
Test: N/A Change-Id: Ida61733f1cc73b3bee4d3963b1bc17dfe2b626fc
This commit is contained in:
@@ -57,7 +57,7 @@ import LocalStore from './localstore.js'
|
|||||||
import DataAdb from './DataAdb.vue'
|
import DataAdb from './DataAdb.vue'
|
||||||
import FileType from './mixins/FileType.js'
|
import FileType from './mixins/FileType.js'
|
||||||
|
|
||||||
const APP_NAME = "Winscope";
|
const APP_NAME = "Winscope"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'app',
|
name: 'app',
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
:filter="hierarchyFilter"
|
:filter="hierarchyFilter"
|
||||||
:flattened="store.flattened"
|
:flattened="store.flattened"
|
||||||
:items-clickable="true"
|
:items-clickable="true"
|
||||||
|
:useGlobalCollapsedState="true"
|
||||||
ref="hierarchy"
|
ref="hierarchy"
|
||||||
/>
|
/>
|
||||||
</md-card>
|
</md-card>
|
||||||
@@ -52,6 +53,7 @@
|
|||||||
:item="selectedTree"
|
:item="selectedTree"
|
||||||
:filter="propertyFilter"
|
:filter="propertyFilter"
|
||||||
:collapseChildren="true"
|
:collapseChildren="true"
|
||||||
|
:useGlobalCollapsedState="true"
|
||||||
/>
|
/>
|
||||||
</md-card>
|
</md-card>
|
||||||
</md-card-content>
|
</md-card-content>
|
||||||
|
|||||||
@@ -31,7 +31,11 @@
|
|||||||
</md-table-toolbar>
|
</md-table-toolbar>
|
||||||
|
|
||||||
<div class="scrollBody" ref="tableBody">
|
<div class="scrollBody" ref="tableBody">
|
||||||
<md-table-row v-for="transaction in filteredData" :key="transaction.timestamp">
|
<md-table-row
|
||||||
|
v-for="transaction in filteredData"
|
||||||
|
:key="transaction.timestamp"
|
||||||
|
@click="transactionSelected(transaction)"
|
||||||
|
>
|
||||||
<md-table-cell>{{transaction.time}}</md-table-cell>
|
<md-table-cell>{{transaction.time}}</md-table-cell>
|
||||||
|
|
||||||
<div v-if="transaction.type == 'transaction'">
|
<div v-if="transaction.type == 'transaction'">
|
||||||
@@ -49,9 +53,24 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</md-table>
|
</md-table>
|
||||||
|
|
||||||
|
<md-card class="changes">
|
||||||
|
<md-content md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
|
||||||
|
<h2 class="md-title" style="flex: 1">Changes</h2>
|
||||||
|
</md-content>
|
||||||
|
<div class="changes-content">
|
||||||
|
<tree-view :item="selectedTree" :useGlobalCollapsedState="true" />
|
||||||
|
</div>
|
||||||
|
</md-card>
|
||||||
|
|
||||||
</md-card-content>
|
</md-card-content>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
import TreeView from './TreeView.vue';
|
||||||
|
|
||||||
|
import { transform_json } from './transform.js';
|
||||||
|
import { stableIdCompatibilityFixup } from './utils/utils.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'transactionsview',
|
name: 'transactionsview',
|
||||||
props: ['data'],
|
props: ['data'],
|
||||||
@@ -71,6 +90,7 @@ export default {
|
|||||||
transactionTypes: Array.from(transactionTypes),
|
transactionTypes: Array.from(transactionTypes),
|
||||||
selectedTransactionTypes: [],
|
selectedTransactionTypes: [],
|
||||||
searchInput: "",
|
searchInput: "",
|
||||||
|
selectedTree: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -95,6 +115,47 @@ export default {
|
|||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
removeNullFields(changeObject) {
|
||||||
|
for (const key in changeObject) {
|
||||||
|
if (changeObject[key] === null) {
|
||||||
|
delete changeObject[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changeObject;
|
||||||
|
},
|
||||||
|
transactionSelected(transaction) {
|
||||||
|
let obj = this.removeNullFields(transaction.obj);
|
||||||
|
let name = transaction.type;
|
||||||
|
|
||||||
|
if (transaction.type == "transaction") {
|
||||||
|
name = "changes";
|
||||||
|
obj = {};
|
||||||
|
|
||||||
|
const [surfaceChanges, displayChanges] =
|
||||||
|
this.aggregateTransactions(transaction.transactions);
|
||||||
|
|
||||||
|
for (const changeId in surfaceChanges) {
|
||||||
|
this.removeNullFields(surfaceChanges[changeId]);
|
||||||
|
}
|
||||||
|
for (const changeId in displayChanges) {
|
||||||
|
this.removeNullFields(displayChanges[changeId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(surfaceChanges).length > 0) {
|
||||||
|
obj.surfaceChanges = surfaceChanges;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(displayChanges).length > 0) {
|
||||||
|
obj.displayChanges = displayChanges;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const transactionUnique = transaction.timestamp;
|
||||||
|
this.selectedTree = transform_json(obj, name, transactionUnique, {
|
||||||
|
formatter: () => {}
|
||||||
|
});
|
||||||
|
},
|
||||||
filterTransactions(condition) {
|
filterTransactions(condition) {
|
||||||
return (entry) => {
|
return (entry) => {
|
||||||
if (entry.type == "transaction") {
|
if (entry.type == "transaction") {
|
||||||
@@ -110,7 +171,27 @@ export default {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
summarizeTranscations(transactions) {
|
mergeChanges(a, b) {
|
||||||
|
const res = {};
|
||||||
|
|
||||||
|
for (const key in a) {
|
||||||
|
if (a[key] !== null && a.hasOwnProperty(key)) {
|
||||||
|
res[key] = a[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key in b) {
|
||||||
|
if (b[key] !== null && key !== 'id' && b.hasOwnProperty(key)) {
|
||||||
|
if (res.hasOwnProperty(key)) {
|
||||||
|
throw new Error(`Merge failed – key '${key}' already present`);
|
||||||
|
}
|
||||||
|
res[key] = b[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
aggregateTransactions(transactions) {
|
||||||
const surfaceChanges = {};
|
const surfaceChanges = {};
|
||||||
const displayChanges = {};
|
const displayChanges = {};
|
||||||
|
|
||||||
@@ -120,12 +201,12 @@ export default {
|
|||||||
switch (transaction.type) {
|
switch (transaction.type) {
|
||||||
case "surfaceChange":
|
case "surfaceChange":
|
||||||
surfaceChanges[obj.id] =
|
surfaceChanges[obj.id] =
|
||||||
Object.assign(surfaceChanges[obj.id] ?? {}, obj);
|
this.mergeChanges(surfaceChanges[obj.id] ?? {}, obj);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "displayChange":
|
case "displayChange":
|
||||||
displayChanges[obj.id] =
|
displayChanges[obj.id] =
|
||||||
Object.assign(displayChanges[obj.id] ?? {}, obj);
|
this.mergeChanges(displayChanges[obj.id] ?? {}, obj);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -133,31 +214,50 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const summary = [];
|
return [surfaceChanges, displayChanges];
|
||||||
|
},
|
||||||
|
summarizeTranscations(transactions) {
|
||||||
|
const ids = {
|
||||||
|
"surfaceChange": new Set(),
|
||||||
|
"displayChange": new Set(),
|
||||||
|
};
|
||||||
|
|
||||||
const surfaceChangesId = Object.keys(surfaceChanges);
|
for (const transaction of transactions) {
|
||||||
if (surfaceChangesId.length > 0) {
|
ids[transaction.type].add(transaction.obj.id);
|
||||||
summary.push(`surfaceChanges: ${surfaceChangesId.join(', ')}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const displayChangesIds = Object.keys(displayChanges);
|
const summary = [];
|
||||||
if (displayChangesIds.length > 0) {
|
|
||||||
summary.push(`displayChanges: ${displayChangesIds.join(', ')}`);
|
if (ids.surfaceChange.size > 0) {
|
||||||
|
summary.push(`surfaceChanges: ${[...ids.surfaceChange].join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ids.displayChange.size > 0) {
|
||||||
|
summary.push(`displayChanges: ${[...ids.displayChange].join(', ')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return summary.join(" | ");
|
return summary.join(" | ");
|
||||||
}
|
},
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
'tree-view': TreeView,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* .transaction-table {
|
.container {
|
||||||
width: 100%;
|
display: flex;
|
||||||
} */
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transaction-table,
|
||||||
|
.changes {
|
||||||
|
flex: 1;
|
||||||
|
margin: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.scrollBody {
|
.scrollBody {
|
||||||
/* width: 100%; */
|
|
||||||
max-height: 75vh;
|
max-height: 75vh;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,7 @@
|
|||||||
:initial-depth="depth + 1"
|
:initial-depth="depth + 1"
|
||||||
:collapse="collapseChildren"
|
:collapse="collapseChildren"
|
||||||
:collapseChildren="collapseChildren"
|
:collapseChildren="collapseChildren"
|
||||||
|
:useGlobalCollapsedState="useGlobalCollapsedState"
|
||||||
ref="children"
|
ref="children"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -98,6 +99,14 @@ export default {
|
|||||||
"initial-depth",
|
"initial-depth",
|
||||||
"collapse",
|
"collapse",
|
||||||
"collapseChildren",
|
"collapseChildren",
|
||||||
|
// Allows collapse state to be tracked by Vuex so that collapse state of
|
||||||
|
// items with same stableId can remain consisten accross time and easily
|
||||||
|
// toggled from anywhere in the app.
|
||||||
|
// Should be true if you are using the same TreeView to display multiple
|
||||||
|
// trees throughout the component's lifetime to make sure same nodes are
|
||||||
|
// toggled when switching back and forth between trees.
|
||||||
|
// If true, requires all nodes in tree to have a stableId.
|
||||||
|
"useGlobalCollapsedState",
|
||||||
],
|
],
|
||||||
data() {
|
data() {
|
||||||
const isCollapsedByDefault = this.collapse ?? false;
|
const isCollapsedByDefault = this.collapse ?? false;
|
||||||
@@ -106,14 +115,19 @@ export default {
|
|||||||
isChildSelected: false,
|
isChildSelected: false,
|
||||||
clickTimeout: null,
|
clickTimeout: null,
|
||||||
isCollapsedByDefault,
|
isCollapsedByDefault,
|
||||||
|
localCollapsedState: isCollapsedByDefault,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setCollapseValue(isCollapsed) {
|
setCollapseValue(isCollapsed) {
|
||||||
this.$store.commit('setCollapsedState', {
|
if (this.useGlobalCollapsedState) {
|
||||||
item: this.item,
|
this.$store.commit('setCollapsedState', {
|
||||||
isCollapsed,
|
item: this.item,
|
||||||
});
|
isCollapsed,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.localCollapsedState = isCollapsed;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
toggleTree() {
|
toggleTree() {
|
||||||
this.setCollapseValue(!this.isCollapsed);
|
this.setCollapseValue(!this.isCollapsed);
|
||||||
@@ -236,8 +250,12 @@ export default {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.$store.getters.collapsedStateStoreFor(this.item) ??
|
if (this.useGlobalCollapsedState) {
|
||||||
|
return this.$store.getters.collapsedStateStoreFor(this.item) ??
|
||||||
this.isCollapsedByDefault;
|
this.isCollapsedByDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.localCollapsedState;
|
||||||
},
|
},
|
||||||
isSelected() {
|
isSelected() {
|
||||||
return this.selected === this.item;
|
return this.selected === this.item;
|
||||||
|
|||||||
Reference in New Issue
Block a user