web/satellite: updated project dashboard data cards
Updated cards to show access grants count, buckets count, team size and billing status for current project Issue: https://github.com/storj/storj/issues/5865 Change-Id: I7e8d3aa3e05548d593576c3a0633e9eb51f38cff
This commit is contained in:
parent
083b3d6fc1
commit
277a4c6aa3
@ -217,6 +217,8 @@ async function onProjectSelected(projectID: string): Promise<void> {
|
||||
billingStore.getProjectUsageAndChargesCurrentRollup(),
|
||||
projectsStore.getProjectLimits(projectID),
|
||||
bucketsStore.getBuckets(FIRST_PAGE, projectID),
|
||||
agStore.getAccessGrants(FIRST_PAGE, projectID),
|
||||
pmStore.getProjectMembers(FIRST_PAGE, projectID),
|
||||
]);
|
||||
} catch (error) {
|
||||
await notify.error(error.message, AnalyticsErrorEventSource.NAVIGATION_PROJECT_SELECTION);
|
||||
|
@ -3,7 +3,10 @@
|
||||
|
||||
<template>
|
||||
<div class="info-container">
|
||||
<h2 class="info-container__title">{{ title }}</h2>
|
||||
<div class="info-container__header">
|
||||
<component :is="icon" />
|
||||
<h2 class="info-container__header__title">{{ title }}</h2>
|
||||
</div>
|
||||
<VLoader v-if="isDataFetching" height="40px" width="40px" />
|
||||
<template v-else>
|
||||
<p class="info-container__subtitle">{{ subtitle }}</p>
|
||||
@ -14,9 +17,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { VueConstructor } from 'vue';
|
||||
|
||||
import VLoader from '@/components/common/VLoader.vue';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
icon: VueConstructor,
|
||||
isDataFetching: boolean,
|
||||
title: string,
|
||||
subtitle: string,
|
||||
@ -34,30 +40,40 @@ const props = withDefaults(defineProps<{
|
||||
padding: 24px;
|
||||
width: calc(100% - 48px);
|
||||
font-family: 'font_regular', sans-serif;
|
||||
background-color: #fff;
|
||||
background-color: var(--c-white);
|
||||
box-shadow: 0 0 32px rgb(0 0 0 / 4%);
|
||||
border-radius: 10px;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
font-size: 18px;
|
||||
line-height: 27px;
|
||||
color: #000;
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
:deep(path) {
|
||||
fill: var(--c-black);
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
font-size: 18px;
|
||||
line-height: 27px;
|
||||
color: var(--c-black);
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&__subtitle {
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
color: #000;
|
||||
color: var(--c-grey-6);
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
&__value {
|
||||
font-family: 'font_bold', sans-serif;
|
||||
font-size: 36px;
|
||||
line-height: 47px;
|
||||
font-size: 28px;
|
||||
line-height: 36px;
|
||||
letter-spacing: -0.02em;
|
||||
color: #000;
|
||||
color: var(--c-black);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -106,42 +106,58 @@
|
||||
<LimitsArea :is-loading="isDataFetching" />
|
||||
<div class="project-dashboard__info">
|
||||
<InfoContainer
|
||||
title="Billing"
|
||||
:subtitle="status"
|
||||
:value="centsToDollars(estimatedCharges)"
|
||||
:is-data-fetching="isDataFetching"
|
||||
:icon="BucketsIcon"
|
||||
title="Buckets"
|
||||
:subtitle="`Last update ${now}`"
|
||||
:value="bucketsCount.toString()"
|
||||
:is-data-fetching="areBucketsFetching"
|
||||
>
|
||||
<template #side-value>
|
||||
<p class="project-dashboard__info__label">Will be charged during next billing period</p>
|
||||
<router-link :to="RouteConfig.Buckets.path" class="project-dashboard__info__link">
|
||||
Go to buckets →
|
||||
</router-link>
|
||||
</template>
|
||||
</InfoContainer>
|
||||
<InfoContainer
|
||||
title="Objects"
|
||||
:subtitle="`Updated ${now}`"
|
||||
:value="limits.objectCount.toString()"
|
||||
:icon="GrantsIcon"
|
||||
title="Access Grants"
|
||||
:subtitle="`Last update ${now}`"
|
||||
:value="accessGrantsCount.toString()"
|
||||
:is-data-fetching="isDataFetching"
|
||||
>
|
||||
<template #side-value>
|
||||
<p class="project-dashboard__info__label" aria-roledescription="total-storage">
|
||||
Total of {{ usedLimitFormatted(limits.storageUsed) }}
|
||||
<router-link :to="RouteConfig.AccessGrants.path" class="project-dashboard__info__link">
|
||||
Access management →
|
||||
</router-link>
|
||||
</template>
|
||||
</InfoContainer>
|
||||
<InfoContainer
|
||||
:icon="TeamIcon"
|
||||
title="Users"
|
||||
:subtitle="`Last update ${now}`"
|
||||
:value="teamSize.toString()"
|
||||
:is-data-fetching="isDataFetching"
|
||||
>
|
||||
<template #side-value>
|
||||
<p class="project-dashboard__info__link" @click="onInviteUsersClick">
|
||||
Invite project users →
|
||||
</p>
|
||||
</template>
|
||||
</InfoContainer>
|
||||
<InfoContainer
|
||||
title="Segments"
|
||||
:subtitle="`Updated ${now}`"
|
||||
:value="limits.segmentCount.toString()"
|
||||
:icon="BillingIcon"
|
||||
title="Billing"
|
||||
:subtitle="status"
|
||||
:value="isProAccount ? centsToDollars(estimatedCharges) : 'Free'"
|
||||
:is-data-fetching="isDataFetching"
|
||||
>
|
||||
<template #side-value>
|
||||
<a
|
||||
<router-link
|
||||
:to="RouteConfig.Account.with(RouteConfig.Billing.with(RouteConfig.BillingOverview)).path"
|
||||
class="project-dashboard__info__link"
|
||||
href="https://docs.storj.io/dcs/billing-payment-and-accounts-1/pricing#segments"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Learn more ->
|
||||
</a>
|
||||
Go to billing →
|
||||
</router-link>
|
||||
</template>
|
||||
</InfoContainer>
|
||||
</div>
|
||||
@ -171,6 +187,8 @@ import { useAppStore } from '@/store/modules/appStore';
|
||||
import { useBucketsStore } from '@/store/modules/bucketsStore';
|
||||
import { useProjectsStore } from '@/store/modules/projectsStore';
|
||||
import { useConfigStore } from '@/store/modules/configStore';
|
||||
import { useProjectMembersStore } from '@/store/modules/projectMembersStore';
|
||||
import { useAccessGrantsStore } from '@/store/modules/accessGrantsStore';
|
||||
import { centsToDollars } from '@/utils/strings';
|
||||
|
||||
import VLoader from '@/components/common/VLoader.vue';
|
||||
@ -188,6 +206,10 @@ import LimitsArea from '@/components/project/dashboard/LimitsArea.vue';
|
||||
|
||||
import NewProjectIcon from '@/../static/images/project/newProject.svg';
|
||||
import InfoIcon from '@/../static/images/project/infoIcon.svg';
|
||||
import BucketsIcon from '@/../static/images/navigation/buckets.svg';
|
||||
import GrantsIcon from '@/../static/images/navigation/accessGrants.svg';
|
||||
import TeamIcon from '@/../static/images/navigation/users.svg';
|
||||
import BillingIcon from '@/../static/images/navigation/billing.svg';
|
||||
|
||||
const configStore = useConfigStore();
|
||||
const bucketsStore = useBucketsStore();
|
||||
@ -195,6 +217,8 @@ const appStore = useAppStore();
|
||||
const billingStore = useBillingStore();
|
||||
const usersStore = useUsersStore();
|
||||
const projectsStore = useProjectsStore();
|
||||
const pmStore = useProjectMembersStore();
|
||||
const agStore = useAccessGrantsStore();
|
||||
const notify = useNotify();
|
||||
const router = useRouter();
|
||||
|
||||
@ -304,12 +328,33 @@ const bucketWasCreated = computed((): boolean => {
|
||||
});
|
||||
|
||||
/**
|
||||
* get selected project from store
|
||||
* Get selected project from store.
|
||||
*/
|
||||
const selectedProject = computed((): Project => {
|
||||
return projectsStore.state.selectedProject;
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns current team size from store.
|
||||
*/
|
||||
const teamSize = computed((): number => {
|
||||
return pmStore.state.page.totalCount;
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns access grants count from store.
|
||||
*/
|
||||
const accessGrantsCount = computed((): number => {
|
||||
return agStore.state.page.totalCount;
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns access grants count from store.
|
||||
*/
|
||||
const bucketsCount = computed((): number => {
|
||||
return bucketsStore.state.page.totalCount;
|
||||
});
|
||||
|
||||
/**
|
||||
* Hides server-side encryption banner.
|
||||
*/
|
||||
@ -332,6 +377,13 @@ function onUpgradeClick(): void {
|
||||
appStore.updateActiveModal(MODALS.upgradeAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on invite users CTA click logic.
|
||||
*/
|
||||
function onInviteUsersClick(): void {
|
||||
appStore.updateActiveModal(MODALS.addTeamMember);
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds on create project button click logic.
|
||||
*/
|
||||
@ -407,6 +459,8 @@ onMounted(async (): Promise<void> => {
|
||||
window.addEventListener('resize', recalculateChartWidth);
|
||||
recalculateChartWidth();
|
||||
|
||||
const FIRST_PAGE = 1;
|
||||
|
||||
try {
|
||||
const now = new Date();
|
||||
const past = new Date();
|
||||
@ -434,6 +488,8 @@ onMounted(async (): Promise<void> => {
|
||||
projectsStore.getDailyProjectData({ since: past, before: now }),
|
||||
billingStore.getProjectUsageAndChargesCurrentRollup(),
|
||||
billingStore.getCoupon(),
|
||||
pmStore.getProjectMembers(FIRST_PAGE, projectID),
|
||||
agStore.getAccessGrants(FIRST_PAGE, projectID),
|
||||
]);
|
||||
} catch (error) {
|
||||
await notify.error(error.message, AnalyticsErrorEventSource.PROJECT_DASHBOARD_PAGE);
|
||||
@ -441,8 +497,6 @@ onMounted(async (): Promise<void> => {
|
||||
isDataFetching.value = false;
|
||||
}
|
||||
|
||||
const FIRST_PAGE = 1;
|
||||
|
||||
try {
|
||||
await bucketsStore.getBuckets(FIRST_PAGE, projectID);
|
||||
|
||||
@ -497,7 +551,7 @@ onBeforeUnmount((): void => {
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
margin: 63px -8px 14px;
|
||||
margin: 44px -8px 14px;
|
||||
|
||||
> * {
|
||||
margin: 2px 8px;
|
||||
@ -640,24 +694,36 @@ onBeforeUnmount((): void => {
|
||||
flex-wrap: wrap;
|
||||
|
||||
.info-container {
|
||||
width: calc((100% - 32px) / 3);
|
||||
width: calc((100% - 32px) / 4);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&__label,
|
||||
@media screen and (max-width: 1060px) {
|
||||
|
||||
> .info-container {
|
||||
width: calc((100% - 16px) / 2);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
|
||||
> .info-container {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&__link {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
&__link {
|
||||
color: var(--c-black);
|
||||
cursor: pointer;
|
||||
text-decoration: underline !important;
|
||||
text-underline-position: under;
|
||||
|
||||
&:visited {
|
||||
color: #000;
|
||||
color: var(--c-black);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -734,20 +800,6 @@ onBeforeUnmount((): void => {
|
||||
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) {
|
||||
@ -762,15 +814,6 @@ onBeforeUnmount((): void => {
|
||||
&__charts__container:first-child {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
&__info {
|
||||
margin-top: 32px;
|
||||
|
||||
> .info-container {
|
||||
width: 100%;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user