Skip to content

Commit c366500

Browse files
feature: Make views in summary selectable
1 parent a5a9e70 commit c366500

3 files changed

Lines changed: 239 additions & 26 deletions

File tree

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
<template lang="pug">
2+
div
3+
h5 {{ type_title }}
4+
div(v-if="type == 'top_apps'")
5+
aw-summary(:fields="top_apps",
6+
:namefunc="e => e.data.app",
7+
:colorfunc="e => e.data.app",
8+
with_limit)
9+
div(v-if="type == 'top_titles'")
10+
aw-summary(:fields="top_titles",
11+
:namefunc="e => e.data.title",
12+
:colorfunc="e => e.data.app",
13+
with_limit)
14+
div(v-if="type == 'top_domains'")
15+
aw-summary(:fields="top_domains",
16+
:namefunc="e => e.data.$domain",
17+
:colorfunc="e => e.data.$domain",
18+
with_limit)
19+
div(v-if="type == 'top_urls'")
20+
aw-summary(:fields="top_urls",
21+
:namefunc="e => e.data.url",
22+
:colorfunc="e => e.data.$domain",
23+
with_limit)
24+
div(v-if="type == 'top_editor_files'")
25+
aw-summary(:fields="$store.state.activity_daily.editor.top_files",
26+
:namefunc="top_editor_files_namefunc",
27+
:colorfunc="e => e.data.language",
28+
with_limit)
29+
div(v-if="type == 'top_editor_languages'")
30+
aw-summary(:fields="$store.state.activity_daily.editor.top_languages",
31+
:namefunc="e => e.data.language",
32+
:colorfunc="e => e.data.language",
33+
with_limit)
34+
div(v-if="type == 'top_editor_projects'")
35+
aw-summary(:fields="$store.state.activity_daily.editor.top_projects",
36+
:namefunc="top_editor_projects_namefunc",
37+
:colorfunc="e => e.data.language",
38+
with_limit)
39+
div(v-if="type == 'top_categories'")
40+
aw-summary(:fields="top_categories",
41+
:namefunc="e => e.data['$category'].join(' > ')",
42+
:colorfunc="e => e.data['$category'].join(' > ')",
43+
with_limit)
44+
div(v-if="type == 'category_tree'")
45+
aw-categorytree(:events="top_categories")
46+
div(v-if="type == 'category_sunburst'")
47+
aw-sunburst-categories(:data="top_categories_hierarchy", style="height: 20em")
48+
49+
b-dropdown.vis-style-dropdown-btn(size="sm" variant="outline-secondary")
50+
template(v-slot:button-content)
51+
icon(name="cog")
52+
b-dropdown-item(v-for="t in types" :key="t" variant="outline-secondary" @click="$emit('onTypeChange', id, t)" v-bind:disabled="!get_type_available(t)")
53+
| {{ get_type_title(t) }}
54+
</template>
55+
56+
<style scoped lang="scss">
57+
.vis-style-dropdown-btn {
58+
position: absolute;
59+
bottom: 0;
60+
right: 0.5em;
61+
62+
background-color: #fff;
63+
}
64+
</style>
65+
66+
<script>
67+
// TODO: Move this somewhere else
68+
import { build_category_hierarchy } from '~/util/classes';
69+
function pick_subname_as_name(c) {
70+
c.name = c.subname;
71+
c.children = c.children.map(pick_subname_as_name);
72+
return c;
73+
}
74+
75+
export default {
76+
name: 'aw-selectable-vis',
77+
props: {
78+
id: Number,
79+
type: String,
80+
},
81+
data: function() {
82+
return {
83+
types: [
84+
'top_apps',
85+
'top_titles',
86+
'top_domains',
87+
'top_urls',
88+
'top_categories',
89+
'category_tree',
90+
'category_sunburst',
91+
'top_editor_files',
92+
'top_editor_languages',
93+
'top_editor_projects',
94+
],
95+
// TODO: Move this function somewhere else
96+
top_editor_files_namefunc: e => {
97+
let f = e.data.file || '';
98+
f = f.split('/');
99+
f = f[f.length - 1];
100+
return f;
101+
},
102+
// TODO: Move this function somewhere else
103+
top_editor_projects_namefunc: e => {
104+
let f = e.data.project || '';
105+
f = f.split('/');
106+
f = f[f.length - 1];
107+
return f;
108+
},
109+
};
110+
},
111+
computed: {
112+
top_apps: function() {
113+
return this.$store.state.activity_daily.window.top_apps;
114+
},
115+
top_titles: function() {
116+
return this.$store.state.activity_daily.window.top_titles;
117+
},
118+
top_domains: function() {
119+
return this.$store.state.activity_daily.browser.top_domains;
120+
},
121+
top_urls: function() {
122+
return this.$store.state.activity_daily.browser.top_urls;
123+
},
124+
top_categories: function() {
125+
return this.$store.state.activity_daily.category.top;
126+
},
127+
top_categories_hierarchy: function() {
128+
if (this.top_categories) {
129+
const categories = this.top_categories.map(c => {
130+
return { name: c.data.$category, size: c.duration };
131+
});
132+
133+
return {
134+
name: 'All',
135+
children: build_category_hierarchy(categories).map(c => pick_subname_as_name(c)),
136+
};
137+
} else {
138+
return null;
139+
}
140+
},
141+
type_title: function() {
142+
return this.get_type_title(this.type);
143+
},
144+
},
145+
methods: {
146+
get_type_available: function(type) {
147+
if (type === 'top_apps' || type === 'top_titles') {
148+
return this.$store.state.activity_daily.window.available;
149+
} else if (type === 'top_domains' || type === 'top_urls') {
150+
return this.$store.state.activity_daily.browser.available;
151+
} else if (
152+
type === 'top_editor_files' ||
153+
type === 'top_editor_languages' ||
154+
type === 'top_editor_projects'
155+
) {
156+
return this.$store.state.activity_daily.editor.available;
157+
} else if (
158+
type === 'top_categories' ||
159+
type === 'category_tree' ||
160+
type === 'category_sunburst'
161+
) {
162+
return this.$store.state.activity_daily.category.available;
163+
} else {
164+
console.error('Unknown type available: ', type);
165+
return false;
166+
}
167+
},
168+
get_type_title: function(type) {
169+
if (type === 'top_apps') {
170+
return 'Top Applications';
171+
} else if (type === 'top_titles') {
172+
return 'Top Window Titles';
173+
} else if (type === 'top_domains') {
174+
return 'Top Browser Domains';
175+
} else if (type === 'top_urls') {
176+
return 'Top Browser URLs';
177+
} else if (type === 'top_editor_files') {
178+
return 'Top Editor Files';
179+
} else if (type === 'top_editor_languages') {
180+
return 'Top Editor Languages';
181+
} else if (type === 'top_editor_projects') {
182+
return 'Top Editor Projects';
183+
} else if (type === 'top_categories') {
184+
return 'Top Categories';
185+
} else if (type === 'category_tree') {
186+
return 'Category Tree';
187+
} else if (type === 'category_sunburst') {
188+
return 'Category Sunburst';
189+
} else {
190+
console.error('Unknown type: ', type);
191+
return 'Unknown';
192+
}
193+
},
194+
},
195+
};
196+
</script>

src/main.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Vue.component('error-boundary', () => import('./components/ErrorBoundary.vue'));
3737
Vue.component('input-timeinterval', () => import('./components/InputTimeInterval.vue'));
3838
Vue.component('aw-header', () => import('./components/Header.vue'));
3939
Vue.component('aw-devonly', () => import('./components/DevOnly.vue'));
40+
Vue.component('aw-selectable-vis', () => import('./components/SelectableVisualization.vue'));
4041

4142
// Visualization components
4243
Vue.component('aw-summary', () => import('./visualizations/Summary.vue'));

src/views/activity/daily/ActivityDailySummary.vue

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,8 @@
11
<template lang="pug">
22
div
33
div.row.mb-4
4-
div.col-md-4
5-
h5 Top Applications
6-
aw-summary(:fields="top_apps", :namefunc="e => e.data.app", :colorfunc="e => e.data.app", with_limit)
7-
8-
div.col-md-4
9-
h5 Top Window Titles
10-
aw-summary(:fields="top_titles", :namefunc="e => e.data.title", :colorfunc="e => e.data.app", with_limit)
11-
12-
div.col-md-4
13-
h5 Top Browser Domains
14-
aw-summary(:fields="top_domains", :namefunc="e => e.data.$domain", :colorfunc="e => e.data.$domain", with_limit)
15-
16-
div.row.mb-4
17-
div.col-md-4
18-
h5 Top categories
19-
aw-summary(:fields="top_categories", :namefunc="e => e.data['$category'].join(' > ')", :colorfunc="e => e.data['$category'].join(' > ')", with_limit)
20-
21-
div.col-md-4
22-
h5 Category Tree
23-
div(v-if="top_categories")
24-
aw-categorytree(:events="top_categories")
25-
26-
div.col-md-4
27-
h5 Category Sunburst
28-
div(v-if="top_categories")
29-
aw-sunburst-categories(:data="top_categories_hierarchy", style="height: 20em")
4+
div.col-md-4.padding(v-for="view, index in views")
5+
aw-selectable-vis(:id="index" :type="view" @onTypeChange="onTypeChange")
306

317
aw-devonly(v-if="periodLength === 'day'" reason="Not ready for production, still experimenting")
328
div.row.mb-4
@@ -35,6 +11,12 @@ div
3511

3612
</template>
3713

14+
<style scoped lang="scss">
15+
.padding {
16+
padding-top: 1em;
17+
}
18+
</style>
19+
3820
<script>
3921
import _ from 'lodash';
4022
import moment from 'moment';
@@ -91,6 +73,11 @@ export default {
9173
default: 'day',
9274
},
9375
},
76+
data: function() {
77+
return {
78+
views: this.loadSummaryFavoriteViews(),
79+
};
80+
},
9481
computed: {
9582
top_apps: function() {
9683
return this.$store.state.activity_daily.window.top_apps;
@@ -129,5 +116,34 @@ export default {
129116
];
130117
},
131118
},
119+
methods: {
120+
onTypeChange(id, type) {
121+
this.views[id] = type;
122+
// Needed to emit the change to the child component
123+
this.$set(this.views, this.views);
124+
this.saveSummaryFavoriteViews();
125+
},
126+
127+
saveSummaryFavoriteViews() {
128+
localStorage.activity_summary_favorite_views = JSON.stringify(this.views);
129+
console.log('Saved summary favorite types', localStorage.activity_summary_favorite_views);
130+
},
131+
132+
loadSummaryFavoriteViews() {
133+
const favorite_views = localStorage.activity_summary_favorite_views;
134+
if (favorite_views) {
135+
return JSON.parse(favorite_views);
136+
} else {
137+
return [
138+
'top_apps',
139+
'top_titles',
140+
'top_domains',
141+
'top_categories',
142+
'category_tree',
143+
'category_sunburst',
144+
];
145+
}
146+
},
147+
},
132148
};
133149
</script>

0 commit comments

Comments
 (0)