Skip to content

Commit b7ff2d6

Browse files
committed
Add top sites pie chart to dashboard hourly view
1 parent e0db2d7 commit b7ff2d6

File tree

10 files changed

+233
-16
lines changed

10 files changed

+233
-16
lines changed

dist/PromoClearYouTube.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/dark.js

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/dashboard.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/general.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/src/block.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/src/dashboard.js

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package-lock.json

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"dependencies": {
1313
"@vueuse/core": "^10.11.1",
1414
"chart.js": "^4.4.7",
15+
"chartjs-plugin-datalabels": "^2.2.0",
1516
"d3": "^7.9.0",
1617
"date-fns": "^2.30.0",
1718
"rollup-plugin-copy": "^3.5.0",

src/components/Dashboad.vue

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,11 @@
3838
<NotesView v-if="chart == TypeOfChart.Notes" />
3939
<BlankView v-if="chart == TypeOfChart.Blank" />
4040
</div>
41-
<div class="tab-items">
42-
<TabList :type="TypeOfList.Dashboard" :showAllStats="false" v-if="chart == TypeOfChart.Horly" />
41+
<div v-if="chart == TypeOfChart.Horly">
42+
<TopSitesPieChart />
43+
<div class="tab-items">
44+
<TabList :type="TypeOfList.Dashboard" :showAllStats="false" />
45+
</div>
4346
</div>
4447
</template>
4548

@@ -56,6 +59,7 @@ import HourlyChart from './HourlyChart.vue';
5659
import NotesView from './NotesView.vue';
5760
import BlankView from './BlankView.vue';
5861
import TabList from '../components/TabList.vue';
62+
import TopSitesPieChart from './TopSitesPieChart.vue';
5963
import { TypeOfList } from '../utils/enums';
6064
import { onMounted, ref } from 'vue';
6165
import Browser from 'webextension-polyfill';
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
<template>
2+
<div class="top-sites-chart">
3+
<h3 class="chart-title">Top 4 Sites</h3>
4+
<div class="chart-layout">
5+
<div class="chart-container">
6+
<Doughnut :data="data" :options="options" v-if="data != undefined" />
7+
</div>
8+
<div class="sites-list" v-if="topSites && topSites.length > 0">
9+
<div v-for="(site, index) in topSites" :key="index" class="site-item">
10+
<div class="site-color" :style="{ backgroundColor: chartColors[index] }"></div>
11+
<div class="site-info">
12+
<div class="site-url">{{ site.url }}</div>
13+
<div class="site-time">{{ convertSummaryTimeToString(site.summaryTime) }}</div>
14+
</div>
15+
</div>
16+
</div>
17+
</div>
18+
</div>
19+
</template>
20+
21+
<script lang="ts">
22+
export default {
23+
name: 'TopSitesPieChart',
24+
};
25+
</script>
26+
27+
<script lang="ts" setup>
28+
import { Doughnut } from 'vue-chartjs';
29+
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js';
30+
import ChartDataLabels from 'chartjs-plugin-datalabels';
31+
import { convertSummaryTimeToString } from '../utils/converter';
32+
import { injectStorage } from '../storage/inject-storage';
33+
import { onMounted, ref, watch } from 'vue';
34+
import { DARK_MODE_DEFAULT, StorageParams } from '../storage/storage-params';
35+
import { useTodayTabListSummary } from '../functions/useTodayTabListSummary';
36+
import { SortingBy } from '../utils/enums';
37+
import { Tab } from '../entity/tab';
38+
39+
const settingsStorage = injectStorage();
40+
const darkMode = ref(false);
41+
const data = ref();
42+
const options = ref();
43+
const isLoading = ref(true);
44+
const topSites = ref<Tab[]>([]);
45+
46+
// Chart colors
47+
const chartColors = [
48+
'#5668e2',
49+
'#8a56e2',
50+
'#cf56e2',
51+
'#e256ae',
52+
];
53+
54+
// Process tabs to get top 4 sites and combine the rest as "Others"
55+
async function processTabsData() {
56+
isLoading.value = true;
57+
58+
// Get today's tab list summary
59+
const tabSummary = await useTodayTabListSummary(SortingBy.UsageTime);
60+
61+
if (!tabSummary || !tabSummary.tabs || tabSummary.tabs.length === 0) {
62+
isLoading.value = false;
63+
return;
64+
}
65+
66+
const tabs = tabSummary.tabs;
67+
const summaryTime = tabSummary.summaryTime || 0;
68+
69+
// Extract top 4 sites
70+
const top4Sites = tabs.slice(0, 4);
71+
72+
// Prepare data for chart - only top 4 sites
73+
const labels = top4Sites.map(tab => tab.url);
74+
const timeValues = top4Sites.map(tab => tab.summaryTime);
75+
76+
// Save top sites for the list
77+
topSites.value = top4Sites;
78+
79+
// Set chart data
80+
setupChartData(labels, timeValues);
81+
isLoading.value = false;
82+
}
83+
84+
function setupChartData(labels: string[], timeValues: number[]) {
85+
data.value = {
86+
labels: labels,
87+
datasets: [
88+
{
89+
borderWidth: 2,
90+
borderColor: darkMode.value ? '#303030' : '#fff',
91+
color: '#fff',
92+
backgroundColor: chartColors,
93+
data: timeValues,
94+
},
95+
],
96+
};
97+
98+
options.value = {
99+
responsive: true,
100+
maintainAspectRatio: false,
101+
layout: {
102+
padding: 0,
103+
},
104+
plugins: {
105+
legend: {
106+
display: false, // Hide the legend
107+
},
108+
tooltip: {
109+
callbacks: {
110+
label: function (context: any) {
111+
return context.label + ': ' + convertSummaryTimeToString(context.raw);
112+
},
113+
},
114+
},
115+
// Add datalabels plugin configuration
116+
datalabels: {
117+
color: '#fff',
118+
font: {
119+
weight: 'bold',
120+
size: 12,
121+
},
122+
formatter: (value: number, ctx: any) => {
123+
const label = ctx.chart.data.labels[ctx.dataIndex];
124+
return label;
125+
},
126+
display: function(context: any) {
127+
// Only display labels for segments that are large enough
128+
return context.dataset.data[context.dataIndex] / context.dataset.data.reduce((a: number, b: number) => a + b, 0) > 0.05;
129+
},
130+
},
131+
},
132+
};
133+
}
134+
135+
onMounted(async () => {
136+
darkMode.value = await settingsStorage.getValue(StorageParams.DARK_MODE, DARK_MODE_DEFAULT);
137+
ChartJS.register(ArcElement, Tooltip, Legend, ChartDataLabels);
138+
await processTabsData();
139+
});
140+
</script>
141+
142+
<style scoped>
143+
.top-sites-chart {
144+
margin: 20px auto;
145+
width: 100%;
146+
}
147+
148+
.chart-title {
149+
text-align: center;
150+
margin-bottom: 15px;
151+
font-size: 18px;
152+
font-weight: 500;
153+
}
154+
155+
.chart-layout {
156+
display: flex;
157+
align-items: center;
158+
justify-content: space-between;
159+
}
160+
161+
.chart-container {
162+
height: 300px;
163+
width: 50%;
164+
}
165+
166+
.sites-list {
167+
width: 45%;
168+
padding-left: 20px;
169+
}
170+
171+
.site-item {
172+
display: flex;
173+
align-items: center;
174+
margin-bottom: 15px;
175+
}
176+
177+
.site-color {
178+
width: 15px;
179+
height: 15px;
180+
border-radius: 50%;
181+
margin-right: 10px;
182+
}
183+
184+
.site-info {
185+
flex-grow: 1;
186+
}
187+
188+
.site-url {
189+
font-weight: 500;
190+
margin-bottom: 3px;
191+
}
192+
193+
.site-time {
194+
font-size: 14px;
195+
color: #666;
196+
}
197+
</style>

0 commit comments

Comments
 (0)