mirror of
https://gitlab.com/bramw/baserow.git
synced 2025-04-10 15:47:32 +00:00
Enhance dashboard charts design
This commit is contained in:
parent
1dc9a16030
commit
f113354cb8
11 changed files with 279 additions and 138 deletions
enterprise/web-frontend/modules/baserow_enterprise
assets/scss/components
dashboard/components/widget
web-frontend/modules
core/assets/scss/components/dashboard
dashboard/components/widget
|
@ -1,3 +1,25 @@
|
||||||
.chart {
|
.chart {
|
||||||
max-height: 320px;
|
max-height: 320px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chart__no-data {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
padding: 10px 0;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart__no-data-dashed-line {
|
||||||
|
border-top: 1px dashed $palette-neutral-200;
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart__no-data-plain-line {
|
||||||
|
border-top: 1px solid $palette-neutral-200;
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,15 @@
|
||||||
.dashboard-chart-widget {
|
.dashboard-chart-widget {
|
||||||
padding: 0 24px 24px;
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-chart-widget__content {
|
||||||
|
height: 280px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-chart-widget__loading {
|
||||||
|
height: 341px;
|
||||||
|
|
||||||
|
.dashboard-chart-widget--with-header-description & {
|
||||||
|
height: 362.8px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,20 @@
|
||||||
<template>
|
<template>
|
||||||
<Bar id="chart-id" :options="chartOptions" :data="chartData" class="chart" />
|
<Bar
|
||||||
|
v-if="chartData.datasets.length > 0"
|
||||||
|
id="chart-id"
|
||||||
|
:options="chartOptions"
|
||||||
|
:data="chartData"
|
||||||
|
class="chart"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div v-else class="chart__no-data">
|
||||||
|
<span class="chart__no-data-dashed-line"></span>
|
||||||
|
<span class="chart__no-data-dashed-line"></span>
|
||||||
|
<span class="chart__no-data-dashed-line"></span>
|
||||||
|
<span class="chart__no-data-dashed-line"></span>
|
||||||
|
<span class="chart__no-data-dashed-line"></span>
|
||||||
|
<span class="chart__no-data-plain-line"></span>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -50,6 +65,36 @@ export default {
|
||||||
display: true,
|
display: true,
|
||||||
align: 'start',
|
align: 'start',
|
||||||
position: 'bottom',
|
position: 'bottom',
|
||||||
|
labels: {
|
||||||
|
usePointStyle: true,
|
||||||
|
boxWidth: 14,
|
||||||
|
pointStyle: 'circle',
|
||||||
|
padding: 20,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
backgroundColor: '#202128',
|
||||||
|
padding: 10,
|
||||||
|
bodyFont: {
|
||||||
|
size: 12,
|
||||||
|
},
|
||||||
|
titleFont: {
|
||||||
|
size: 12,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
elements: {
|
||||||
|
bar: {
|
||||||
|
borderRadius: {
|
||||||
|
topLeft: 4,
|
||||||
|
topRight: 4,
|
||||||
|
bottomLeft: 0,
|
||||||
|
bottomRight: 0,
|
||||||
|
},
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#5190ef',
|
||||||
|
backgroundColor: '#5190ef',
|
||||||
|
hoverBackgroundColor: '#5190ef',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -88,7 +133,7 @@ export default {
|
||||||
datasets.push({
|
datasets.push({
|
||||||
data: seriesData,
|
data: seriesData,
|
||||||
label,
|
label,
|
||||||
backgroundColor: this.chartColors[index],
|
...this.chartColors[index],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
@ -107,7 +152,7 @@ export default {
|
||||||
datasets.push({
|
datasets.push({
|
||||||
data: seriesData,
|
data: seriesData,
|
||||||
label,
|
label,
|
||||||
backgroundColor: this.chartColors[index],
|
...this.chartColors[index],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
@ -124,7 +169,33 @@ export default {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
chartColors() {
|
chartColors() {
|
||||||
return ['#4E5CFE', '#2BC3F1', '#FFC744', '#E26AB0', '#3E4ACB']
|
return [
|
||||||
|
{
|
||||||
|
backgroundColor: '#5190ef',
|
||||||
|
borderColor: '#5190ef',
|
||||||
|
hoverBackgroundColor: '#5190ef',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: '#2BC3F1',
|
||||||
|
borderColor: '#2BC3F1',
|
||||||
|
hoverBackgroundColor: '#2BC3F1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: '#FFC744',
|
||||||
|
borderColor: '#FFC744',
|
||||||
|
hoverBackgroundColor: '#FFC744',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: '#E26AB0',
|
||||||
|
borderColor: '#E26AB0',
|
||||||
|
hoverBackgroundColor: '#E26AB0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
backgroundColor: '#3E4ACB',
|
||||||
|
borderColor: '#3E4ACB',
|
||||||
|
hoverBackgroundColor: '#3E4ACB',
|
||||||
|
},
|
||||||
|
]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -1,38 +1,48 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="dashboard-chart-widget">
|
<div
|
||||||
<div class="widget-header">
|
class="dashboard-chart-widget"
|
||||||
<div class="widget-header__main">
|
:class="{
|
||||||
<div class="widget-header__title-wrapper">
|
'dashboard-chart-widget--with-header-description': widget.description,
|
||||||
<div class="widget-header__title">{{ widget.title }}</div>
|
}"
|
||||||
<div
|
>
|
||||||
v-if="dataSourceMisconfigured"
|
<template v-if="!loading">
|
||||||
class="widget-header__fix-configuration"
|
<div
|
||||||
>
|
class="widget__header"
|
||||||
<svg
|
:class="{
|
||||||
width="5"
|
'widget__header--edit-mode': editMode,
|
||||||
height="6"
|
}"
|
||||||
viewBox="0 0 5 6"
|
>
|
||||||
fill="none"
|
<div class="widget__header-main">
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
<div class="widget__header-title-wrapper">
|
||||||
|
<div class="widget__header-title">{{ widget.title }}</div>
|
||||||
|
|
||||||
|
<Badge
|
||||||
|
v-if="dataSourceMisconfigured"
|
||||||
|
color="red"
|
||||||
|
size="small"
|
||||||
|
indicator
|
||||||
|
rounded
|
||||||
|
>{{ $t('widget.fixConfiguration') }}</Badge
|
||||||
>
|
>
|
||||||
<circle cx="2.5" cy="3" r="2.5" fill="#FF5A44" />
|
</div>
|
||||||
</svg>
|
<div v-if="widget.description" class="widget__header-description">
|
||||||
{{ $t('widget.fixConfiguration') }}
|
{{ widget.description }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="widget.description" class="widget-header__description">
|
<WidgetContextMenu
|
||||||
{{ widget.description }}
|
v-if="isEditMode"
|
||||||
</div>
|
:widget="widget"
|
||||||
|
:dashboard="dashboard"
|
||||||
|
@delete-widget="$emit('delete-widget', $event)"
|
||||||
|
></WidgetContextMenu>
|
||||||
</div>
|
</div>
|
||||||
<WidgetContextMenu
|
|
||||||
v-if="isEditMode"
|
<div class="dashboard-chart-widget__content widget__content">
|
||||||
:widget="widget"
|
<Chart :data-source="dataSource" :data-source-data="dataForDataSource">
|
||||||
:dashboard="dashboard"
|
</Chart>
|
||||||
@delete-widget="$emit('delete-widget', $event)"
|
</div>
|
||||||
></WidgetContextMenu>
|
</template>
|
||||||
</div>
|
<div v-else class="dashboard-chart-widget__loading loading-spinner"></div>
|
||||||
<Chart :data-source="dataSource" :data-source-data="dataForDataSource">
|
|
||||||
</Chart>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -57,6 +67,16 @@ export default {
|
||||||
required: false,
|
required: false,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
editMode: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
dataSource() {
|
dataSource() {
|
||||||
|
|
|
@ -7,6 +7,6 @@
|
||||||
@import 'dashboard_widget';
|
@import 'dashboard_widget';
|
||||||
@import 'dashboard_summary_widget';
|
@import 'dashboard_summary_widget';
|
||||||
@import 'create_widget_button';
|
@import 'create_widget_button';
|
||||||
@import 'widget_header';
|
@import 'widget';
|
||||||
@import 'widget_settings_base_form';
|
@import 'widget_settings_base_form';
|
||||||
@import 'create_widget_modal';
|
@import 'create_widget_modal';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
.dashboard-summary-widget {
|
.dashboard-summary-widget {
|
||||||
padding: 0 24px 24px;
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-summary-widget__summary {
|
.dashboard-summary-widget__summary {
|
||||||
|
@ -7,7 +7,6 @@
|
||||||
font-size: 40px;
|
font-size: 40px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
margin-top: 16px;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
@ -16,3 +15,11 @@
|
||||||
.dashboard-summary-widget__summary--misconfigured {
|
.dashboard-summary-widget__summary--misconfigured {
|
||||||
color: #cdcecf;
|
color: #cdcecf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dashboard-summary-widget__loading {
|
||||||
|
height: 120px;
|
||||||
|
|
||||||
|
.dashboard-summary-widget--with-header-description & {
|
||||||
|
height: 141.8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
.widget__header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: start;
|
||||||
|
gap: 7px;
|
||||||
|
padding: 20px 24px;
|
||||||
|
border-bottom: 1px solid $palette-neutral-200;
|
||||||
|
position: relative;
|
||||||
|
height: auto;
|
||||||
|
|
||||||
|
&--no-border {
|
||||||
|
border-bottom: none;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--edit-mode {
|
||||||
|
padding: 20px 55px 20px 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget__header-main {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget__header-context-menu {
|
||||||
|
margin-left: auto;
|
||||||
|
width: 46px;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget__header-title-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 14px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget__header-title {
|
||||||
|
color: $palette-neutral-1200;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget__header-description {
|
||||||
|
@extend %ellipsis;
|
||||||
|
|
||||||
|
color: #6a6b70;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget__content {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
|
@ -1,57 +0,0 @@
|
||||||
.widget-header {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: start;
|
|
||||||
gap: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.widget-header__main {
|
|
||||||
padding-top: 24px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.widget-header__context-menu {
|
|
||||||
flex-grow: 1;
|
|
||||||
text-align: right;
|
|
||||||
margin: 6px -14px 0 7px;
|
|
||||||
width: 46px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.widget-header__title-wrapper {
|
|
||||||
display: flex;
|
|
||||||
gap: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.widget-header__title {
|
|
||||||
color: $palette-neutral-1200;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 500;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.widget-header__description {
|
|
||||||
color: #6a6b70;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 400;
|
|
||||||
margin-top: 8px;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.widget-header__fix-configuration {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
color: #b23f30;
|
|
||||||
font-size: 10px;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 12px;
|
|
||||||
letter-spacing: 0.2px;
|
|
||||||
padding: 4px 8px;
|
|
||||||
border-radius: 80px;
|
|
||||||
background: #fff2f0;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
|
@ -12,14 +12,12 @@
|
||||||
</div>
|
</div>
|
||||||
<component
|
<component
|
||||||
:is="widgetComponent(widget.type)"
|
:is="widgetComponent(widget.type)"
|
||||||
v-if="isLoading === false"
|
|
||||||
:dashboard="dashboard"
|
:dashboard="dashboard"
|
||||||
:widget="widget"
|
:widget="widget"
|
||||||
:store-prefix="storePrefix"
|
:store-prefix="storePrefix"
|
||||||
|
:loading="isLoading"
|
||||||
|
:edit-mode="isEditMode"
|
||||||
/>
|
/>
|
||||||
<div v-else>
|
|
||||||
<div class="dashboard-widget__loading"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -1,45 +1,46 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="dashboard-summary-widget">
|
<div
|
||||||
<div class="widget-header">
|
class="dashboard-summary-widget"
|
||||||
<div class="widget-header__main">
|
:class="{
|
||||||
<div class="widget-header__title-wrapper">
|
'dashboard-summary-widget--with-header-description': widget.description,
|
||||||
<div class="widget-header__title">{{ widget.title }}</div>
|
}"
|
||||||
<div
|
>
|
||||||
v-if="dataSourceMisconfigured"
|
<template v-if="!loading">
|
||||||
class="widget-header__fix-configuration"
|
<div class="widget__header widget__header--no-border">
|
||||||
>
|
<div class="widget__header-main">
|
||||||
<svg
|
<div class="widget__header-title-wrapper">
|
||||||
width="5"
|
<div class="widget__header-title">{{ widget.title }}</div>
|
||||||
height="6"
|
|
||||||
viewBox="0 0 5 6"
|
<Badge
|
||||||
fill="none"
|
v-if="dataSourceMisconfigured"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
color="red"
|
||||||
|
indicator
|
||||||
|
rounded
|
||||||
|
>{{ $t('widget.fixConfiguration') }}</Badge
|
||||||
>
|
>
|
||||||
<circle cx="2.5" cy="3" r="2.5" fill="#FF5A44" />
|
</div>
|
||||||
</svg>
|
<div v-if="widget.description" class="widget__header-description">
|
||||||
{{ $t('widget.fixConfiguration') }}
|
{{ widget.description }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="widget.description" class="widget-header__description">
|
<WidgetContextMenu
|
||||||
{{ widget.description }}
|
v-if="isEditMode"
|
||||||
</div>
|
:widget="widget"
|
||||||
|
:dashboard="dashboard"
|
||||||
|
@delete-widget="$emit('delete-widget', $event)"
|
||||||
|
></WidgetContextMenu>
|
||||||
</div>
|
</div>
|
||||||
<WidgetContextMenu
|
<div
|
||||||
v-if="isEditMode"
|
class="widget__content dashboard-summary-widget__summary"
|
||||||
:widget="widget"
|
:class="{
|
||||||
:dashboard="dashboard"
|
'dashboard-summary-widget__summary--misconfigured':
|
||||||
@delete-widget="$emit('delete-widget', $event)"
|
dataSourceMisconfigured,
|
||||||
></WidgetContextMenu>
|
}"
|
||||||
</div>
|
>
|
||||||
<div
|
{{ result }}
|
||||||
class="dashboard-summary-widget__summary"
|
</div>
|
||||||
:class="{
|
</template>
|
||||||
'dashboard-summary-widget__summary--misconfigured':
|
<div v-else class="dashboard-summary-widget__loading loading-spinner"></div>
|
||||||
dataSourceMisconfigured,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
{{ result }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -63,6 +64,11 @@ export default {
|
||||||
required: false,
|
required: false,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
dataSource() {
|
dataSource() {
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div ref="contextButton" class="widget-header__context-menu">
|
<div ref="contextButton" class="widget__header-context-menu">
|
||||||
<ButtonIcon
|
<ButtonIcon
|
||||||
icon="iconoir-more-vert"
|
icon="iconoir-more-vert"
|
||||||
type="secondary"
|
type="secondary"
|
||||||
size="regular"
|
size="regular"
|
||||||
@click.stop="
|
@click.stop="
|
||||||
$refs.context.toggle($refs.contextButton, 'bottom', 'right', 8, 0)
|
$refs.context.toggle($refs.contextButton, 'bottom', 'right', 8, -8)
|
||||||
"
|
"
|
||||||
></ButtonIcon>
|
></ButtonIcon>
|
||||||
<WidgetContext
|
<WidgetContext
|
||||||
|
|
Loading…
Add table
Reference in a new issue