1
0
Fork 0
mirror of https://gitlab.com/bramw/baserow.git synced 2025-04-04 21:25:24 +00:00

Merge branch 'style-dashboard-charts' into 'develop'

Enhance dashboard charts design

See merge request 
This commit is contained in:
Jonathan Adeline 2025-03-10 05:50:54 +00:00
commit 38c77ab190
11 changed files with 279 additions and 138 deletions
enterprise/web-frontend/modules/baserow_enterprise
assets/scss/components
dashboard/components/widget
web-frontend/modules

View file

@ -1,3 +1,25 @@
.chart {
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;
}

View file

@ -1,3 +1,15 @@
.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;
}
}

View file

@ -1,5 +1,20 @@
<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>
<script>
@ -50,6 +65,36 @@ export default {
display: true,
align: 'start',
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({
data: seriesData,
label,
backgroundColor: this.chartColors[index],
...this.chartColors[index],
})
}
return {
@ -107,7 +152,7 @@ export default {
datasets.push({
data: seriesData,
label,
backgroundColor: this.chartColors[index],
...this.chartColors[index],
})
}
return {
@ -124,7 +169,33 @@ export default {
})
},
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: {

View file

@ -1,38 +1,48 @@
<template>
<div class="dashboard-chart-widget">
<div class="widget-header">
<div class="widget-header__main">
<div class="widget-header__title-wrapper">
<div class="widget-header__title">{{ widget.title }}</div>
<div
v-if="dataSourceMisconfigured"
class="widget-header__fix-configuration"
>
<svg
width="5"
height="6"
viewBox="0 0 5 6"
fill="none"
xmlns="http://www.w3.org/2000/svg"
<div
class="dashboard-chart-widget"
:class="{
'dashboard-chart-widget--with-header-description': widget.description,
}"
>
<template v-if="!loading">
<div
class="widget__header"
:class="{
'widget__header--edit-mode': editMode,
}"
>
<div class="widget__header-main">
<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" />
</svg>
{{ $t('widget.fixConfiguration') }}
</div>
<div v-if="widget.description" class="widget__header-description">
{{ widget.description }}
</div>
</div>
<div v-if="widget.description" class="widget-header__description">
{{ widget.description }}
</div>
<WidgetContextMenu
v-if="isEditMode"
:widget="widget"
:dashboard="dashboard"
@delete-widget="$emit('delete-widget', $event)"
></WidgetContextMenu>
</div>
<WidgetContextMenu
v-if="isEditMode"
:widget="widget"
:dashboard="dashboard"
@delete-widget="$emit('delete-widget', $event)"
></WidgetContextMenu>
</div>
<Chart :data-source="dataSource" :data-source-data="dataForDataSource">
</Chart>
<div class="dashboard-chart-widget__content widget__content">
<Chart :data-source="dataSource" :data-source-data="dataForDataSource">
</Chart>
</div>
</template>
<div v-else class="dashboard-chart-widget__loading loading-spinner"></div>
</div>
</template>
@ -57,6 +67,16 @@ export default {
required: false,
default: '',
},
loading: {
type: Boolean,
required: false,
default: false,
},
editMode: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
dataSource() {

View file

@ -7,6 +7,6 @@
@import 'dashboard_widget';
@import 'dashboard_summary_widget';
@import 'create_widget_button';
@import 'widget_header';
@import 'widget';
@import 'widget_settings_base_form';
@import 'create_widget_modal';

View file

@ -1,5 +1,5 @@
.dashboard-summary-widget {
padding: 0 24px 24px;
// nothing
}
.dashboard-summary-widget__summary {
@ -7,7 +7,6 @@
font-size: 40px;
font-weight: 600;
line-height: 40px;
margin-top: 16px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@ -16,3 +15,11 @@
.dashboard-summary-widget__summary--misconfigured {
color: #cdcecf;
}
.dashboard-summary-widget__loading {
height: 120px;
.dashboard-summary-widget--with-header-description & {
height: 141.8px;
}
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -12,14 +12,12 @@
</div>
<component
:is="widgetComponent(widget.type)"
v-if="isLoading === false"
:dashboard="dashboard"
:widget="widget"
:store-prefix="storePrefix"
:loading="isLoading"
:edit-mode="isEditMode"
/>
<div v-else>
<div class="dashboard-widget__loading"></div>
</div>
</div>
</template>

View file

@ -1,45 +1,46 @@
<template>
<div class="dashboard-summary-widget">
<div class="widget-header">
<div class="widget-header__main">
<div class="widget-header__title-wrapper">
<div class="widget-header__title">{{ widget.title }}</div>
<div
v-if="dataSourceMisconfigured"
class="widget-header__fix-configuration"
>
<svg
width="5"
height="6"
viewBox="0 0 5 6"
fill="none"
xmlns="http://www.w3.org/2000/svg"
<div
class="dashboard-summary-widget"
:class="{
'dashboard-summary-widget--with-header-description': widget.description,
}"
>
<template v-if="!loading">
<div class="widget__header widget__header--no-border">
<div class="widget__header-main">
<div class="widget__header-title-wrapper">
<div class="widget__header-title">{{ widget.title }}</div>
<Badge
v-if="dataSourceMisconfigured"
color="red"
indicator
rounded
>{{ $t('widget.fixConfiguration') }}</Badge
>
<circle cx="2.5" cy="3" r="2.5" fill="#FF5A44" />
</svg>
{{ $t('widget.fixConfiguration') }}
</div>
<div v-if="widget.description" class="widget__header-description">
{{ widget.description }}
</div>
</div>
<div v-if="widget.description" class="widget-header__description">
{{ widget.description }}
</div>
<WidgetContextMenu
v-if="isEditMode"
:widget="widget"
:dashboard="dashboard"
@delete-widget="$emit('delete-widget', $event)"
></WidgetContextMenu>
</div>
<WidgetContextMenu
v-if="isEditMode"
:widget="widget"
:dashboard="dashboard"
@delete-widget="$emit('delete-widget', $event)"
></WidgetContextMenu>
</div>
<div
class="dashboard-summary-widget__summary"
:class="{
'dashboard-summary-widget__summary--misconfigured':
dataSourceMisconfigured,
}"
>
{{ result }}
</div>
<div
class="widget__content dashboard-summary-widget__summary"
:class="{
'dashboard-summary-widget__summary--misconfigured':
dataSourceMisconfigured,
}"
>
{{ result }}
</div>
</template>
<div v-else class="dashboard-summary-widget__loading loading-spinner"></div>
</div>
</template>
@ -63,6 +64,11 @@ export default {
required: false,
default: '',
},
loading: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
dataSource() {

View file

@ -1,11 +1,11 @@
<template>
<div ref="contextButton" class="widget-header__context-menu">
<div ref="contextButton" class="widget__header-context-menu">
<ButtonIcon
icon="iconoir-more-vert"
type="secondary"
size="regular"
@click.stop="
$refs.context.toggle($refs.contextButton, 'bottom', 'right', 8, 0)
$refs.context.toggle($refs.contextButton, 'bottom', 'right', 8, -8)
"
></ButtonIcon>
<WidgetContext