web/satellite: make project dashboard/overview responsive

This change modifies the new project dashboard to accommodate
small screen sizes.

Resolves https://github.com/storj/storj/issues/5036

Change-Id: I27a8acd144a24f21f0fa43808640ba5bca6b49a8
This commit is contained in:
Jeremy Wharton 2022-08-30 16:25:14 -05:00 committed by Storj Robot
parent c619ff45d8
commit 3342fbb918
8 changed files with 266 additions and 49 deletions

View File

@ -46,7 +46,7 @@ export default class VDateRangePicker extends Vue {
}
.mx-calendar {
width: 50%;
width: 100%;
}
.mx-date-row {
@ -107,4 +107,20 @@ export default class VDateRangePicker extends Vue {
height: 20px;
border-width: 4px 0 0 4px;
}
@media screen and (max-width: 768px) {
.range-selection__popup {
width: 320px !important;
}
.mx-range-wrapper {
flex-direction: column;
}
.mx-calendar + .mx-calendar {
border-left: none;
border-top: 1px solid #e8e8e8;
}
}
</style>

View File

@ -14,7 +14,7 @@
/>
</div>
<div v-if="buckets.length" class="buckets-container">
<SortingHeader />
<SortingHeader class="buckets-container__header" />
<VList
class="buckets-list"
:data-set="buckets"
@ -197,8 +197,8 @@ export default class BucketArea extends Vue {
.buckets-container {
background-color: #fff;
border-radius: 6px;
padding-bottom: 20px;
border-radius: 10px;
box-shadow: 0 0 32px rgb(0 0 0 / 4%);
}
.empty-search-result-area {
@ -207,7 +207,7 @@ export default class BucketArea extends Vue {
justify-content: center;
padding: 20px 0;
background-color: #fff;
border-radius: 6px;
border-radius: 10px;
&__title {
font-family: 'font_bold', sans-serif;
@ -217,10 +217,26 @@ export default class BucketArea extends Vue {
}
.buckets-list {
padding-top: 20px;
padding: 20px 0;
border-radius: 10px;
}
:deep(.pagination-container) {
padding-left: 0;
}
@media screen and (max-width: 960px) {
.buckets-container__header {
display: none;
}
.buckets-list {
padding: 0;
& > :deep(*:not(:first-child)) {
border-top: 1px solid #c7cdd2;
}
}
}
</style>

View File

@ -3,11 +3,30 @@
<template>
<div class="container">
<p class="container__item name" :title="itemData.name">{{ itemData.name }}</p>
<p class="container__item">{{ itemData.storage.toFixed(2) }}GB</p>
<p class="container__item">{{ itemData.egress.toFixed(2) }}GB</p>
<p class="container__item">{{ itemData.objectCount.toString() }}</p>
<p class="container__item">{{ itemData.segmentCount.toString() }}</p>
<div class="container__header" :title="itemData.name">
<BucketIcon class="container__header__icon" />
<p class="container__header__name">{{ itemData.name }}</p>
</div>
<div class="container__item">
<div class="container__item__inner">
<p class="container__item__inner__label">Storage</p>
<p class="container__item__inner__value">{{ itemData.storage.toFixed(2) }}GB</p>
</div>
<div class="container__item__inner">
<p class="container__item__inner__label">Bandwidth</p>
<p class="container__item__inner__value">{{ itemData.egress.toFixed(2) }}GB</p>
</div>
</div>
<div class="container__item">
<div class="container__item__inner">
<p class="container__item__inner__label">Objects</p>
<p class="container__item__inner__value">{{ itemData.objectCount.toString() }}</p>
</div>
<div class="container__item__inner">
<p class="container__item__inner__label">Segments</p>
<p class="container__item__inner__value">{{ itemData.segmentCount.toString() }}</p>
</div>
</div>
</div>
</template>
@ -16,8 +35,14 @@ import { Component, Prop, Vue } from 'vue-property-decorator';
import { Bucket } from '@/types/buckets';
import BucketIcon from '@/../static/images/project/bucket.svg';
// @vue/component
@Component
@Component({
components: {
BucketIcon,
}
})
export default class BucketItem extends Vue {
@Prop({default: () => new Bucket('', 0, 0, 0, 0, new Date(), new Date())})
private readonly itemData: Bucket;
@ -29,22 +54,81 @@ export default class BucketItem extends Vue {
padding: 20px 40px;
outline: none;
display: flex;
background: #fff;
margin-bottom: 1px;
width: calc(100% - 80px);
box-sizing: border-box;
font-family: 'font_medium', sans-serif;
font-size: 16px;
&__header {
width: 100%;
display: flex;
align-items: center;
&__icon {
display: none;
margin-right: 10px;
}
&__name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
&__item {
width: 20%;
font-family: 'font_medium', sans-serif;
font-size: 16px;
margin: 0;
display: flex;
width: 100%;
&__inner {
width: 50%;
&__label {
display: none;
margin-bottom: 4px;
font-family: 'font_regular', sans-serif;
font-size: 14px;
}
}
}
}
.name {
width: 40%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@media screen and (max-width: 960px) {
.container {
flex-wrap: wrap;
padding: 20px 24px;
&__header {
&__icon {
display: block;
}
&__name {
font-family: 'font_bold', sans-serif;
}
}
&__item {
width: 50%;
margin-top: 16px;
&__inner__label {
display: block;
}
}
}
}
@media screen and (max-width: 600px) {
.container {
flex-direction: column;
&__item {
width: 100%;
}
}
}
</style>

View File

@ -76,7 +76,8 @@ export default class NoBucketArea extends Vue {
font-size: 18px;
line-height: 26px;
color: #354049;
margin: 15px 0 30px;
margin: 15px 15px 30px;
text-align: center;
}
&__second-button {

View File

@ -2,7 +2,7 @@
// See LICENSE for copying information.
<template>
<div ref="dashboard" class="project-dashboard">
<div class="project-dashboard">
<h1 class="project-dashboard__title" aria-roledescription="title">Dashboard</h1>
<p class="project-dashboard__message">
Expect a delay of a few hours between network activity and the latest dashboard stats.
@ -70,7 +70,7 @@
</div>
</div>
<div class="project-dashboard__charts">
<div class="project-dashboard__charts__container">
<div ref="chartContainer" class="project-dashboard__charts__container">
<div class="project-dashboard__charts__container__header">
<h3 class="project-dashboard__charts__container__header__title">Storage</h3>
</div>
@ -144,7 +144,6 @@
</template>
</InfoContainer>
<InfoContainer
class="project-dashboard__info__middle"
title="Objects"
:subtitle="`Updated ${now}`"
:value="limits.objectCount.toString()"
@ -178,7 +177,7 @@
<p class="project-dashboard__stats-header__title">Buckets</p>
</div>
<VLoader v-if="areBucketsFetching" />
<BucketArea v-else />
<BucketArea v-else class="project-dashboard__bucket-area" />
</div>
</template>
@ -232,7 +231,7 @@ export default class NewProjectDashboard extends Vue {
public chartWidth = 0;
public $refs: {
dashboard: HTMLDivElement;
chartContainer: HTMLDivElement;
}
public readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
@ -290,9 +289,7 @@ export default class NewProjectDashboard extends Vue {
* Used container size recalculation for charts resizing.
*/
public recalculateChartWidth(): void {
// sixty pixels.
const additionalPaddingRight = 60;
this.chartWidth = this.$refs.dashboard.getBoundingClientRect().width / 2 - additionalPaddingRight;
this.chartWidth = this.$refs.chartContainer.getBoundingClientRect().width;
}
/**
@ -435,7 +432,6 @@ export default class NewProjectDashboard extends Vue {
<style scoped lang="scss">
.project-dashboard {
padding: 56px 55px 56px 40px;
height: calc(100% - 112px);
max-width: calc(100vw - 280px - 95px);
background-image: url('../../../../static/images/project/background.png');
background-position: top right;
@ -493,7 +489,12 @@ export default class NewProjectDashboard extends Vue {
display: flex;
align-items: center;
justify-content: space-between;
margin: 65px 0 16px;
flex-wrap: wrap;
margin: 63px -8px 14px;
> * {
margin: 2px 8px;
}
&__title {
font-family: 'font_Bold', sans-serif;
@ -516,9 +517,10 @@ export default class NewProjectDashboard extends Vue {
&__charts {
display: flex;
align-items: center;
justify-content: space-between;
&__container {
width: 100%;
width: calc((100% - 20px) / 2);
background-color: #fff;
box-shadow: 0 0 32px rgb(0 0 0 / 4%);
border-radius: 10px;
@ -607,19 +609,18 @@ export default class NewProjectDashboard extends Vue {
color: #000;
}
}
> *:first-child {
margin-right: 20px;
}
}
&__info {
display: flex;
align-items: center;
margin-top: 16px;
justify-content: space-between;
align-items: stretch;
flex-wrap: wrap;
&__middle {
margin: 0 16px;
.info-container {
width: calc((100% - 32px) / 3);
box-sizing: border-box;
}
&__label,
@ -639,6 +640,10 @@ export default class NewProjectDashboard extends Vue {
}
}
}
&__bucket-area {
margin-top: 0;
}
}
.new-project-button {
@ -663,6 +668,8 @@ export default class NewProjectDashboard extends Vue {
background: #56606d;
border-radius: 4px;
padding: 8px;
position: relative;
right: 25%;
}
:deep(.info__box__arrow) {
@ -672,10 +679,81 @@ export default class NewProjectDashboard extends Vue {
margin: 0 0 -2px 40px;
}
:deep(.range-selection__popup) {
z-index: 1;
}
@media screen and (max-width: 1280px) {
.project-dashboard {
max-width: calc(100vw - 86px - 95px);
}
}
@media screen and (max-width: 960px) {
:deep(.range-selection__popup) {
right: -148px;
}
}
@media screen and (max-width: 768px) {
.project-dashboard {
&__stats-header {
margin-bottom: 20px;
}
&__charts {
flex-direction: column;
&__container {
width: 100%;
}
&__container:first-child {
margin-right: 0;
margin-bottom: 22px;
}
}
&__info {
margin-top: 52px;
> .info-container {
width: calc((100% - 25px) / 2);
margin-bottom: 24px;
}
> .info-container:last-child {
width: 100%;
margin-bottom: 0;
}
}
}
:deep(.range-selection__popup) {
left: 0;
}
}
@media screen and (max-width: 480px) {
.project-dashboard {
&__charts__container:first-child {
margin-bottom: 20px;
}
&__info {
margin-top: 32px;
> .info-container {
width: 100%;
margin-bottom: 16px;
}
}
}
}
</style>

View File

@ -0,0 +1,3 @@
<svg width="16" height="18" viewBox="0 0 16 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.94232 0C12.3287 0 15.8846 1.41577 15.8846 3.16222C15.8846 3.18684 15.8839 3.21139 15.8825 3.23587L15.8846 3.23576L15.8812 3.25676C15.8747 3.34414 15.8594 3.43063 15.8355 3.5161L13.6784 15.7376C13.6578 16.9885 11.0975 18 7.94232 18C4.80603 18 2.25762 17.0006 2.20697 15.7601L2.20649 15.7376L0.0496288 3.51771C0.0253459 3.43129 0.00981089 3.34382 0.00338464 3.25544L0 3.23576L0.00211202 3.23587C0.000706552 3.21139 0 3.18684 0 3.16222C0 1.41577 3.5559 0 7.94232 0ZM7.94232 6.32444C5.57808 6.32444 3.45511 5.91314 2.00017 5.26045L3.5636 14.1197L3.81923 15.4762L3.84291 15.4946C3.9766 15.5948 4.18351 15.7078 4.45219 15.8167L4.50675 15.8385C5.37281 16.1771 6.60689 16.3821 7.94232 16.3821C9.28547 16.3821 10.5259 16.1747 11.392 15.8329C11.7041 15.7097 11.9368 15.5803 12.0734 15.4701L12.0845 15.4609L13.8846 5.26039C12.4297 5.91312 10.3066 6.32444 7.94232 6.32444ZM7.94232 1.61788C6.0014 1.61788 4.2014 1.921 2.92472 2.42932C2.37614 2.64773 1.96798 2.88827 1.73308 3.10939C1.71811 3.12348 1.70445 3.13691 1.69205 3.14963L1.68024 3.16207L1.70159 3.18448L1.73308 3.21505C1.96798 3.43617 2.37614 3.67671 2.92472 3.89513C4.2014 4.40344 6.0014 4.70656 7.94232 4.70656C9.88324 4.70656 11.6832 4.40344 12.9599 3.89513C13.5085 3.67671 13.9167 3.43617 14.1516 3.21505C14.1665 3.20096 14.1802 3.18753 14.1926 3.17481L14.2045 3.16207L14.1831 3.13996L14.1516 3.10939C13.9167 2.88827 13.5085 2.64773 12.9599 2.42932C11.6832 1.921 9.88324 1.61788 7.94232 1.61788Z" fill="#2C353A"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -8,7 +8,7 @@ exports[`BucketArea.vue renders correctly with bucket 1`] = `
<vheader-stub styletype="common" placeholder="Buckets" search="[Function]" class="buckets-header-component"></vheader-stub>
</div>
<div class="buckets-container">
<sortingheader-stub></sortingheader-stub>
<sortingheader-stub class="buckets-container__header"></sortingheader-stub>
<vlist-stub itemcomponent="[Function]" onitemclick="[Function]" dataset="[object Object]" class="buckets-list"></vlist-stub>
</div>
<!---->
@ -25,7 +25,7 @@ exports[`BucketArea.vue renders correctly with pagination 1`] = `
<vheader-stub styletype="common" placeholder="Buckets" search="[Function]" class="buckets-header-component"></vheader-stub>
</div>
<div class="buckets-container">
<sortingheader-stub></sortingheader-stub>
<sortingheader-stub class="buckets-container__header"></sortingheader-stub>
<vlist-stub itemcomponent="[Function]" onitemclick="[Function]" dataset="[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]" class="buckets-list"></vlist-stub>
</div>
<!---->

View File

@ -2,10 +2,29 @@
exports[`BucketItem.vue renders correctly 1`] = `
<div class="container">
<p title="name" class="container__item name">name</p>
<p class="container__item">1.00GB</p>
<p class="container__item">1.00GB</p>
<p class="container__item">1</p>
<p class="container__item">1</p>
<div title="name" class="container__header">
<bucketicon-stub class="container__header__icon"></bucketicon-stub>
<p class="container__header__name">name</p>
</div>
<div class="container__item">
<div class="container__item__inner">
<p class="container__item__inner__label">Storage</p>
<p class="container__item__inner__value">1.00GB</p>
</div>
<div class="container__item__inner">
<p class="container__item__inner__label">Bandwidth</p>
<p class="container__item__inner__value">1.00GB</p>
</div>
</div>
<div class="container__item">
<div class="container__item__inner">
<p class="container__item__inner__label">Objects</p>
<p class="container__item__inner__value">1</p>
</div>
<div class="container__item__inner">
<p class="container__item__inner__label">Segments</p>
<p class="container__item__inner__value">1</p>
</div>
</div>
</div>
`;