Skip to content

Commit

Permalink
refactor(Charts): use echarts (frappe#1076)
Browse files Browse the repository at this point in the history
* refactor(SiteCharts): use echarts

* refactor(MarketplaceAppAnalytics): use echarts

* fix(Charts): not re-rendering when data changes

* refactor(SiteOverview): use echarts

* style(Charts): hover point color and empty state message

* refactor(ServerAnalytics): use echarts

- `LineChart` now takes array of colors
- datasets are now array too

* fix(Charts): modified chartTheme and dataset

Following 698efdf.

* chore(Charts): format time

* feat(SiteUptimeChart): loading and no data state

* fix(Charts): better limit text

* chore(Charts): remove frappe-charts

* fix: remove frappe-charts styles

* refactor(Charts): use Card's loading state

* feat(Charts): legend for multiline chart

* refactor(ServerAnalytics): use `FormControl` instead of select component

* fix(Card): control overflow via prop

* chore(Charts): remove unused component

* feat(ServerAnalytics): stack cpu usage chart

* feat(ServerAnalytics): display name/info on hover

* feat(ServerAnalytics): cpu chart is now shown in percent

* feat(ServerAnalytics): disk space chart is now shown in percent
  • Loading branch information
BreadGenie authored Oct 7, 2023
1 parent 275730c commit 6907a3e
Show file tree
Hide file tree
Showing 12 changed files with 522 additions and 395 deletions.
2 changes: 0 additions & 2 deletions dashboard/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ Technologies at the heart of dashboard:

4. [Feather Icons](https://feathericons.com/): Those Shiny & Crisp Open Source icons.

5. [Frappe Charts](https://frappe.io/charts): For those beautiful charts. [FrappeChart.vue](./src/components/FrappeChart.vue) is a wrapper Vue component which makes it easy to use Frappe Charts in any other component.

## Development

We use the vite's development server, gives us super-fast hot reload and more.
Expand Down
3 changes: 2 additions & 1 deletion dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
"@vueuse/core": "^10.3.0",
"core-js": "^3.6.4",
"dayjs": "^1.10.7",
"echarts": "^5.4.3",
"feather-icons": "^4.26.0",
"frappe-charts": "2.0.0-rc22",
"frappe-ui": "^0.1.0",
"fuse.js": "6.6.2",
"libarchive.js": "^1.3.0",
Expand All @@ -32,6 +32,7 @@
"register-service-worker": "^1.6.2",
"socket.io-client": "^4.5.1",
"vue": "^3.3.4",
"vue-echarts": "^6.6.1",
"vue-router": "^4.0.5"
},
"devDependencies": {
Expand Down
5 changes: 0 additions & 5 deletions dashboard/src/assets/style.css
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
@import 'frappe-ui/src/style.css';
@import 'frappe-charts/dist/frappe-charts.min.css';

@layer components {
.frappe-chart .line-vertical {
display: none;
}

/* Works on Firefox */
* {
scrollbar-width: thin;
Expand Down
66 changes: 0 additions & 66 deletions dashboard/src/components/FrappeChart.vue

This file was deleted.

214 changes: 214 additions & 0 deletions dashboard/src/components/charts/LineChart.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
<template>
<Card :title="title" class="h-80" :loading="loading" :stopOverflow="true">
<template #actions>
<slot name="actions"></slot>
</template>
<div
v-if="error || !data.datasets.length"
class="flex h-full items-center justify-center"
>
<ErrorMessage v-if="error" :message="error" />
<span v-else class="text-base text-gray-700">No data</span>
</div>
<VChart
v-else
autoresize
class="chart"
:option="options"
:init-options="initOptions"
/>
</Card>
</template>

<script setup>
import { ref, toRefs } from 'vue';
import { DateTime } from 'luxon';
import { use, graphic } from 'echarts/core';
import { SVGRenderer } from 'echarts/renderers';
import { LineChart } from 'echarts/charts';
import {
GridComponent,
LegendComponent,
TooltipComponent,
MarkLineComponent
} from 'echarts/components';
import VChart from 'vue-echarts';
import theme from '../../../tailwind.theme.json';
const props = defineProps({
title: {
type: String,
required: true
},
unit: {
type: String,
required: false,
default: () => ''
},
data: {
type: Object,
required: true,
default: () => ({ labels: [], datasets: [] })
},
type: {
type: String,
required: false,
default: () => 'category'
},
chartTheme: {
type: Array,
required: false,
default: () => [
theme.colors.red[500],
theme.colors.blue[500],
theme.colors.green[500],
theme.colors.purple[500],
theme.colors.yellow[500],
theme.colors.teal[500],
theme.colors.pink[500],
theme.colors.cyan[500]
]
},
loading: {
type: Boolean,
required: false,
default: () => false
},
error: {
type: String,
required: false,
default: () => ''
}
});
const { title, unit, data, type, chartTheme } = toRefs(props);
use([
SVGRenderer,
GridComponent,
LegendComponent,
LineChart,
TooltipComponent,
MarkLineComponent
]);
const initOptions = {
renderer: 'svg'
};
function formatBytes(bytes, decimals = 2, current = 0) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(Math.abs(bytes)) / Math.log(k));
return (
parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i + current]
);
}
function getUnit(value, seriesName) {
if (seriesName === 'bytes') return formatBytes(value);
else return `${+value.toFixed(2)} ${seriesName}`;
}
const options = ref({
tooltip: {
trigger: 'axis',
formatter: params => {
// for the dot to follow the same color as the line 🗿
let tooltip = `<p>${DateTime.fromSQL(
params[0].axisValueLabel
).toLocaleString(DateTime.DATETIME_MED)}</p>`;
params.forEach(({ value, seriesName }, i) => {
let colorSpan = color =>
'<span style="display:inline-block;margin-right:4px;border-radius:10px;width:10px;height:10px;background-color:' +
color +
'"></span>';
tooltip += `<p>${colorSpan(chartTheme.value[i])} ${getUnit(
value[1],
unit.value
)} - ${seriesName}</p>`;
});
return tooltip;
}
},
xAxis: {
type: type,
boundaryGap: false,
data: data.value.labels,
axisLine: {
show: false
},
axisTick: {
show: false
}
},
yAxis: {
type: 'value',
max: data.value.yMax,
axisLabel: {
formatter: value => {
if (unit.value === 'bytes') {
return formatBytes(value, 0);
} else {
if (value >= 1000000000) return `${value / 1000000000}B`;
else if (value >= 1000000) return `${value / 1000000}M`;
else if (value >= 1000) return `${value / 1000}K`;
return value;
}
}
}
},
labelLine: {
smooth: 0.2,
length: 10,
length2: 20
},
legend: {
top: 'bottom',
icon: 'circle',
show: data.value.datasets.length > 1
},
series: data.value.datasets.map((dataset, i) => {
return {
name: dataset.name || unit,
type: 'line',
stack: dataset.stack,
showSymbol: false,
data: dataset.dataset || dataset,
markLine: data.value.markLine,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
},
lineStyle: {
color: chartTheme.value[i]
},
itemStyle: {
color: chartTheme.value[i]
},
areaStyle: {
color: new graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: chartTheme.value[i]
},
{
offset: 1,
color: '#fff'
}
]),
opacity: 0.3
}
};
})
});
</script>
8 changes: 6 additions & 2 deletions dashboard/src/components/global/Card.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@
>
<LoadingText />
</div>
<div class="mt-4 flex-auto overflow-auto" v-else-if="$slots['default']">
<div
class="mt-4 flex-auto"
:class="{ 'overflow-auto': !stopOverflow }"
v-else-if="$slots['default']"
>
<slot></slot>
</div>
</div>
Expand All @@ -31,7 +35,7 @@
import { LoadingText } from 'frappe-ui';
export default {
name: 'Card',
props: ['title', 'subtitle', 'loading'],
props: ['title', 'subtitle', 'loading', 'stopOverflow'],
components: {
LoadingText
}
Expand Down
Loading

0 comments on commit 6907a3e

Please sign in to comment.