Add radar chart

This commit is contained in:
Jože Fortun
2023-04-18 15:37:41 +02:00
parent df6eaace7f
commit 68f570073a
17 changed files with 429 additions and 190 deletions
@@ -311,157 +311,6 @@
</template>
<template #additional-config="{ hasAxis, report }">
<hr v-if="!hasAxis">
<div
class="px-3"
>
<h5 class="mb-3">
{{ $t('edit.additionalConfig.legend.label') }}
</h5>
<b-row>
<b-col
cols="12"
md="6"
>
<b-form-group
:label="$t('edit.additionalConfig.legend.orientation.label')"
label-class="text-primary"
>
<b-form-select
v-model="report.legend.orientation"
:options="orientations"
/>
</b-form-group>
</b-col>
<b-col
cols="12"
md="6"
>
<b-form-group
:label="$t('edit.additionalConfig.legend.show')"
label-class="text-primary"
>
<c-input-checkbox
:value="!!report.legend.isHidden"
switch
invert
:labels="checkboxLabel"
@input="$set(report.legend,'isHidden', $event)"
/>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col
cols="12"
md="6"
>
<b-form-group
:label="$t('edit.additionalConfig.legend.align.label')"
label-class="text-primary"
>
<b-form-select
v-model="report.legend.align"
:options="alignments"
:disabled="!report.legend.position.isDefault"
/>
</b-form-group>
</b-col>
<b-col
cols="12"
md="6"
>
<b-form-group
:label="$t('edit.additionalConfig.legend.options.label')"
label-class="text-primary"
>
<b-form-checkbox
v-model="report.legend.isScrollable"
:disabled="report.legend.orientation !== 'horizontal'"
>
{{ $t('edit.additionalConfig.legend.scrollable') }}
</b-form-checkbox>
<b-form-checkbox
v-model="report.legend.position.isDefault"
>
{{ $t('edit.additionalConfig.legend.position.customize') }}
</b-form-checkbox>
</b-form-group>
</b-col>
</b-row>
<b-row
v-if="!report.legend.position.isDefault"
>
<b-col
cols="12"
md="6"
>
<b-form-group
:label="$t('edit.additionalConfig.legend.position.top')"
label-class="text-primary"
>
<b-input
v-model="report.legend.position.top"
/>
</b-form-group>
</b-col>
<b-col
cols="12"
md="6"
>
<b-form-group
:label="$t('edit.additionalConfig.legend.position.right')"
label-class="text-primary"
>
<b-input
v-model="report.legend.position.right"
/>
</b-form-group>
</b-col>
<b-col
cols="12"
md="6"
>
<b-form-group
:label="$t('edit.additionalConfig.legend.position.bottom')"
label-class="text-primary"
>
<b-input
v-model="report.legend.position.bottom"
/>
</b-form-group>
</b-col>
<b-col
cols="12"
md="6"
>
<b-form-group
:label="$t('edit.additionalConfig.legend.position.left')"
label-class="text-primary"
>
<b-input
v-model="report.legend.position.left"
/>
</b-form-group>
</b-col>
<b-col cols="12">
<small class="text-muted">
{{ $t('edit.additionalConfig.legend.valueRange') }}
</small>
</b-col>
</b-row>
</div>
<template v-if="!hasAxis">
<hr>
<div class="px-3">
@@ -603,13 +452,13 @@
<script>
import ReportEdit from './ReportEdit'
import ChartTranslator from 'corteza-webapp-compose/src/components/Chart/ChartTranslator'
import { compose, NoID } from '@cortezaproject/corteza-js'
import { compose } from '@cortezaproject/corteza-js'
import base from './base'
const ignoredCharts = [
'funnel',
'gauge',
'heatmap',
'radar',
]
export default {
@@ -626,13 +475,6 @@ export default {
extends: base,
props: {
chart: {
type: compose.Chart,
required: true,
},
},
data () {
return {
chartTypes: Object.values(compose.chartUtil.ChartType)
@@ -659,17 +501,6 @@ export default {
{ text: this.$t('edit.metric.lineTension.curvy'), value: 0.6 },
],
orientations: [
{ value: 'horizontal', text: this.$t('edit.additionalConfig.legend.orientation.horizontal') },
{ value: 'vertical', text: this.$t('edit.additionalConfig.legend.orientation.vertical') },
],
alignments: [
{ value: 'left', text: this.$t('edit.additionalConfig.legend.align.left') },
{ value: 'center', text: this.$t('edit.additionalConfig.legend.align.center') },
{ value: 'right', text: this.$t('edit.additionalConfig.legend.align.right') },
],
lineStyleOptions: [
{ value: '', text: this.$t('edit.metric.lineStyle.default') },
{ value: 'smooth', text: this.$t('edit.metric.lineStyle.smooth') },
@@ -688,12 +519,6 @@ export default {
}
},
computed: {
isNew () {
return this.chart.chartID === NoID
},
},
methods: {
hasRelativeDisplay: compose.chartUtil.hasRelativeDisplay,
@@ -0,0 +1,94 @@
<template>
<report-edit
:report.sync="editReport"
:modules="modules"
>
<template #metric-options="{ metric }">
<b-row>
<b-col
cols="12"
md="6"
>
<b-form-group
:label="$t('edit.metric.labelLabel')"
label-class="text-primary"
>
<b-input-group>
<b-form-input
v-model="metric.label"
/>
<b-input-group-append>
<chart-translator
:field.sync="metric.label"
:chart="chart"
:disabled="isNew"
:highlight-key="`metrics.${metric.metricID}.label`"
button-variant="light"
/>
</b-input-group-append>
</b-input-group>
</b-form-group>
</b-col>
</b-row>
</template>
<template #dimension-options-options="{ dimension }">
<b-form-checkbox
v-model="dimension.fixTooltips"
>
{{ $t('edit.metric.fixTooltips') }}
</b-form-checkbox>
</template>
<template #dimension-options="{ dimension }">
<b-row>
<b-col
cols="12"
md="6"
>
<b-form-group
:label="$t('edit.metric.radar.shape.label')"
label-class="text-primary"
>
<b-form-select
v-model="dimension.shape"
:options="shapeOptions"
/>
</b-form-group>
</b-col>
</b-row>
</template>
</report-edit>
</template>
<script>
import base from './base'
import ReportEdit from './ReportEdit'
import ChartTranslator from 'corteza-webapp-compose/src/components/Chart/ChartTranslator'
export default {
name: 'RadarChart',
components: {
ReportEdit,
ChartTranslator,
},
extends: base,
data () {
return {
shapeOptions: [
{ value: 'polygon', text: this.$t('edit.metric.radar.shape.polygon') },
{ value: 'circle', text: this.$t('edit.metric.radar.shape.circle') },
],
}
},
}
</script>
<style scoped lang="scss">
.cursor-pointer {
cursor: pointer;
}
</style>
@@ -247,7 +247,6 @@
<b-button
variant="link"
size="sm"
:disabled="true"
class="ml-auto px-0"
>
<font-awesome-icon
@@ -326,6 +325,159 @@
<hr>
</template>
<hr v-if="hasLegend">
<div
v-if="hasLegend"
class="px-3"
>
<h5 class="mb-3">
{{ $t('edit.additionalConfig.legend.label') }}
</h5>
<b-row>
<b-col
cols="12"
md="6"
>
<b-form-group
:label="$t('edit.additionalConfig.legend.orientation.label')"
label-class="text-primary"
>
<b-form-select
v-model="report.legend.orientation"
:options="orientations"
/>
</b-form-group>
</b-col>
<b-col
cols="12"
md="6"
>
<b-form-group
:label="$t('edit.additionalConfig.legend.show')"
label-class="text-primary"
>
<c-input-checkbox
:value="!!report.legend.isHidden"
switch
invert
:labels="checkboxLabel"
@input="$set(report.legend,'isHidden', $event)"
/>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col
cols="12"
md="6"
>
<b-form-group
:label="$t('edit.additionalConfig.legend.align.label')"
label-class="text-primary"
>
<b-form-select
v-model="report.legend.align"
:options="alignments"
:disabled="!report.legend.position.isDefault"
/>
</b-form-group>
</b-col>
<b-col
cols="12"
md="6"
>
<b-form-group
:label="$t('edit.additionalConfig.legend.options.label')"
label-class="text-primary"
>
<b-form-checkbox
v-model="report.legend.isScrollable"
:disabled="report.legend.orientation !== 'horizontal'"
>
{{ $t('edit.additionalConfig.legend.scrollable') }}
</b-form-checkbox>
<b-form-checkbox
v-model="report.legend.position.isDefault"
>
{{ $t('edit.additionalConfig.legend.position.customize') }}
</b-form-checkbox>
</b-form-group>
</b-col>
</b-row>
<b-row
v-if="!report.legend.position.isDefault"
>
<b-col
cols="12"
md="6"
>
<b-form-group
:label="$t('edit.additionalConfig.legend.position.top')"
label-class="text-primary"
>
<b-input
v-model="report.legend.position.top"
/>
</b-form-group>
</b-col>
<b-col
cols="12"
md="6"
>
<b-form-group
:label="$t('edit.additionalConfig.legend.position.right')"
label-class="text-primary"
>
<b-input
v-model="report.legend.position.right"
/>
</b-form-group>
</b-col>
<b-col
cols="12"
md="6"
>
<b-form-group
:label="$t('edit.additionalConfig.legend.position.bottom')"
label-class="text-primary"
>
<b-input
v-model="report.legend.position.bottom"
/>
</b-form-group>
</b-col>
<b-col
cols="12"
md="6"
>
<b-form-group
:label="$t('edit.additionalConfig.legend.position.left')"
label-class="text-primary"
>
<b-input
v-model="report.legend.position.left"
/>
</b-form-group>
</b-col>
<b-col cols="12">
<small class="text-muted">
{{ $t('edit.additionalConfig.legend.valueRange') }}
</small>
</b-col>
</b-row>
</div>
<slot
name="additional-config"
:report="editReport"
@@ -381,10 +533,25 @@ export default {
metricAggregates: aggregateFunctions.map(af => ({ ...af, text: this.$t(`edit.metric.function.${af.text}`) })),
dimensionModifiers: compose.chartUtil.dimensionFunctions.map(df => ({ ...df, text: this.$t(`edit.dimension.function.${df.text}`) })),
predefinedFilters: compose.chartUtil.predefinedFilters.map(pf => ({ ...pf, text: this.$t(`edit.filter.${pf.text}`) })),
alignments: [
{ value: 'left', text: this.$t('edit.additionalConfig.legend.align.left') },
{ value: 'center', text: this.$t('edit.additionalConfig.legend.align.center') },
{ value: 'right', text: this.$t('edit.additionalConfig.legend.align.right') },
],
orientations: [
{ value: 'horizontal', text: this.$t('edit.additionalConfig.legend.orientation.horizontal') },
{ value: 'vertical', text: this.$t('edit.additionalConfig.legend.orientation.vertical') },
],
}
},
computed: {
hasLegend () {
return !this.metrics.some(({ type }) => ['gauge'].includes(type))
},
defaultValueInputType () {
return ({ field }) => (this.module.fields.filter(f => f.name === field)[0] || {}).kind === 'DateTime' ? 'date' : 'text'
},
@@ -1,4 +1,6 @@
<script>
import { compose, NoID } from '@cortezaproject/corteza-js'
export default {
props: {
report: {
@@ -7,6 +9,11 @@ export default {
default: undefined,
},
chart: {
type: compose.Chart,
default: () => new compose.Chart(),
},
modules: {
type: Array,
required: true,
@@ -63,6 +70,10 @@ export default {
this.$emit('update:report', v)
},
},
isNew () {
return this.chart.chartID === NoID
},
},
}
</script>
@@ -1,9 +1,11 @@
import FunnelChart from './FunnelChart'
import GenericChart from './GenericChart'
import GaugeChart from './GaugeChart'
import RadarChart from './RadarChart'
export default {
FunnelChart,
GenericChart,
GaugeChart,
RadarChart,
}
+4 -2
View File
@@ -18,8 +18,9 @@ import {
BarChart,
PieChart,
GaugeChart,
HeatmapChart,
RadarChart,
FunnelChart,
ScatterChart,
} from 'echarts/charts'
import {
TitleComponent,
@@ -36,8 +37,9 @@ use([
LineChart,
PieChart,
GaugeChart,
HeatmapChart,
RadarChart,
FunnelChart,
ScatterChart,
CanvasRenderer,
TitleComponent,
GridComponent,
+2
View File
@@ -11,6 +11,8 @@ export function chartConstructor (c) {
return new compose.FunnelChart(c)
} else if (m.type === 'gauge') {
return new compose.GaugeChart(c)
} else if (m.type === 'radar') {
return new compose.RadarChart(c)
}
}
}
@@ -205,7 +205,7 @@
<!-- General report editing component -->
<component
:is="reportEditor"
v-if="editReport"
v-if="chart && editReport"
:report.sync="editReport"
:chart="chart"
:modules="modules"
@@ -454,10 +454,12 @@ export default {
if (this.chart instanceof compose.FunnelChart) {
return Reports.FunnelChart
}
if (this.chart instanceof compose.GaugeChart) {
} else if (this.chart instanceof compose.GaugeChart) {
return Reports.GaugeChart
} else if (this.chart instanceof compose.RadarChart) {
return Reports.RadarChart
}
return Reports.GenericChart
},
@@ -529,6 +531,10 @@ export default {
case 'funnel':
c = new compose.FunnelChart(c)
break
case 'radar':
c = new compose.RadarChart(c)
break
}
this.chart = c
this.onEditReport(0)
@@ -59,6 +59,12 @@
>
{{ $t('chart.addGauge') }}
</b-dropdown-item-button>
<b-dropdown-item-button
variant="dark"
@click="$router.push({ name: 'admin.charts.create', params: { category: 'radar' } })"
>
{{ $t('chart.addRadar') }}
</b-dropdown-item-button>
</b-dropdown>
<import
@@ -205,6 +211,10 @@ export default {
case 'funnel':
c = new compose.FunnelChart(c)
break
case 'radar':
c = new compose.RadarChart(c)
break
}
this.createChart(c).then((chart) => {
+2 -2
View File
@@ -15,7 +15,7 @@ import {
BarChart,
PieChart,
GaugeChart,
HeatmapChart,
RadarChart,
FunnelChart,
} from 'echarts/charts'
import {
@@ -32,7 +32,7 @@ use([
LineChart,
PieChart,
GaugeChart,
HeatmapChart,
RadarChart,
FunnelChart,
SVGRenderer,
TitleComponent,
+9 -3
View File
@@ -85,10 +85,11 @@ export default class FunnelChart extends BaseChart {
}
makeOptions (data: any) {
const { colorScheme, noAnimation = false, toolbox } = this.config
const { reports = [], colorScheme, noAnimation = false, toolbox } = this.config
const { saveAsImage } = toolbox || {}
const { labels, datasets = [], tooltip } = data
const { legend: l } = reports[0] || {}
const colors = getColorschemeColors(colorScheme)
const tooltipFormatter = `{b}<br />{c} ${tooltip.relative ? ' ({d}%)' : ''}`
@@ -115,8 +116,13 @@ export default class FunnelChart extends BaseChart {
appendToBody: true,
},
legend: {
show: true,
type: 'scroll',
show: !l?.isHidden,
type: l?.isScrollable ? 'scroll' : 'plain',
top: (l?.position?.isDefault ? undefined : l?.position?.top) || undefined,
right: (l?.position?.isDefault ? undefined : l?.position?.right) || undefined,
bottom: (l?.position?.isDefault ? undefined : l?.position?.bottom) || undefined,
left: (l?.position?.isDefault ? l?.align || 'center' : l?.position?.left) || 'auto',
orient: l?.orientation || 'horizontal'
},
series: datasets.map(({ data }: any) => {
return {
+1
View File
@@ -2,3 +2,4 @@ export { chartUtil } from './util'
export { default as Chart } from './chart'
export { default as FunnelChart } from './funnel'
export { default as GaugeChart } from './gauge'
export { default as RadarChart } from './radar'
+106
View File
@@ -0,0 +1,106 @@
import { BaseChart } from './base'
import {
Dimension,
Metric,
ChartType,
} from './util'
import { getColorschemeColors } from '../../../shared'
export default class RadarChart extends BaseChart {
mtrCheck ({ field, aggregate }: Metric) {
if (!field) {
throw new Error('notification.chart.invalidConfig.missingMetricsField')
}
if (field !== 'count' && !aggregate) {
throw new Error('notification.chart.invalidConfig.missingMetricsAggregate')
}
}
makeDataset (m: Metric, d: Dimension, data: Array<number|any>) {
return {
type: m.type,
label: m.label || m.field,
data,
}
}
makeOptions (data: any) {
const { reports = [], colorScheme, noAnimation = false, toolbox } = this.config
const { saveAsImage } = toolbox || {}
const { labels, datasets = [], dimension = {} } = data
const { legend: l } = reports[0] || {}
const labelFormatter = '{c}'
return {
color: getColorschemeColors(colorScheme),
animation: !noAnimation,
textStyle: {
fontFamily: 'Poppins-Regular',
},
toolbox: {
feature: {
saveAsImage: saveAsImage ? {
name: this.name
} : undefined,
},
},
legend: {
show: !l?.isHidden,
type: l?.isScrollable ? 'scroll' : 'plain',
top: (l?.position?.isDefault ? undefined : l?.position?.top) || undefined,
right: (l?.position?.isDefault ? undefined : l?.position?.right) || undefined,
bottom: (l?.position?.isDefault ? undefined : l?.position?.bottom) || undefined,
left: (l?.position?.isDefault ? l?.align || 'center' : l?.position?.left) || 'auto',
orient: l?.orientation || 'horizontal'
},
tooltip: {
show: true,
position: 'top',
appendToBody: true,
},
radar: {
shape: dimension.shape,
indicator: labels.map((name: string) => {
return { name }
}),
center: ['50%', '55%']
},
series: {
type: 'radar',
label: {
show: dimension.fixTooltips,
formatter: labelFormatter,
},
data: datasets.map(({ data: value, label: name }: any, index: number) => {
return { value, name }
}),
},
}
}
baseChartType (): string {
return 'radar'
}
async fetchReports (a: any) {
return super.fetchReports(a).then((rr: any) => {
return rr[0]
})
}
defMetrics (): Metric {
return Object.assign({}, {
type: ChartType.radar,
})
}
defDimension (): Dimension {
return Object.assign({}, {
shape: 'polygon',
fixTooltips: false,
conditions: {},
meta: {},
})
}
}
+1
View File
@@ -11,6 +11,7 @@ export enum ChartType {
doughnut='doughnut',
funnel = 'funnel',
gauge = 'gauge',
radar = 'radar',
scatter = 'scatter',
}
@@ -81,6 +81,7 @@ chart:
add: New Chart
addFunnel: Funnel chart
addGauge: Gauge chart
addRadar: Radar chart
addGeneric: Generic chart
createFailed: Could not create a chart
columns:
+6 -1
View File
@@ -66,7 +66,7 @@ edit:
fieldLabel: Field *
fieldPlaceholder: Select metric field
fillArea: Fill area below the line
fixTooltips: Always show tooltips
fixTooltips: Always show labels
options:
label: Options
function:
@@ -92,6 +92,11 @@ edit:
start: Start angle
end: End angle
rose: Rose
radar:
shape:
label: Radar shape
polygon: Polygon
circle: Circle
gaugeColor: Color
label: Metric
labelColor: Label color
@@ -76,7 +76,7 @@ chart:
tooltips:
name: Tooltips
show:
always: Always show tooltips
always: Always show labels
offset:
name: Offset
default: Default offset