web/storagenode: held history monthly breakdown
Change-Id: Ia60070853e160d2a56fc420c02fe6b6955fb4b0f
This commit is contained in:
parent
e48177ce81
commit
ddd261703c
@ -464,7 +464,7 @@ export default class EstimationArea extends Vue {
|
||||
padding: 0 16px;
|
||||
width: calc(100% - 32px);
|
||||
height: 36px;
|
||||
background: var(--estimation-table-header-color);
|
||||
background: var(--table-header-color);
|
||||
|
||||
&__text {
|
||||
font-weight: 500;
|
||||
|
@ -0,0 +1,68 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<section class="held-history-container">
|
||||
<div class="held-history-container__header">
|
||||
<p class="held-history-container__header__title">Held Amount History</p>
|
||||
</div>
|
||||
<div class="held-history-container__divider"></div>
|
||||
<HeldHistoryMonthlyBreakdownTable />
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import HeldHistoryMonthlyBreakdownTable from '@/app/components/payments/HeldHistoryMonthlyBreakdownTable.vue';
|
||||
|
||||
import { PAYOUT_ACTIONS } from '@/app/store/modules/payout';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
HeldHistoryMonthlyBreakdownTable,
|
||||
},
|
||||
})
|
||||
export default class HeldHistoryArea extends Vue {
|
||||
/**
|
||||
* Lifecycle hook before component render.
|
||||
* Fetches held history information.
|
||||
*/
|
||||
public beforeMount(): void {
|
||||
this.$store.dispatch(PAYOUT_ACTIONS.GET_HELD_HISTORY);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.held-history-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 28px 40px 10px 40px;
|
||||
background: var(--block-background-color);
|
||||
border: 1px solid var(--block-border-color);
|
||||
box-sizing: border-box;
|
||||
border-radius: 12px;
|
||||
margin: 12px 0 50px;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
font-size: 18px;
|
||||
color: var(--regular-text-color);
|
||||
}
|
||||
}
|
||||
|
||||
&__divider {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
margin-top: 18px;
|
||||
background-color: #eaeaea;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,159 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="held-history-table-container--large">
|
||||
<div class="held-history-table-container--large__labels-area">
|
||||
<div class="column justify-start column-1">
|
||||
<p class="held-history-table-container--large__labels-area__text">Satellite</p>
|
||||
</div>
|
||||
<div class="column justify-end column-2">
|
||||
<p class="held-history-table-container--large__labels-area__text">Month 1-3</p>
|
||||
</div>
|
||||
<div class="column justify-end column-3">
|
||||
<p class="held-history-table-container--large__labels-area__text">Month 4-6</p>
|
||||
</div>
|
||||
<div class="column justify-end column-4">
|
||||
<p class="held-history-table-container--large__labels-area__text">Month 7-9</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="item in monthlyBreakdown" class="held-history-table-container--large__info-area" :key="item.satelliteID">
|
||||
<div class="justify-start column-1">
|
||||
<p class="held-history-table-container--large__info-area__text">{{ item.satelliteName }}</p>
|
||||
<p class="held-history-table-container--large__info-area__months">{{ item.age }} month</p>
|
||||
</div>
|
||||
<div class="column justify-end column-2">
|
||||
<p class="held-history-table-container--large__info-area__text">{{ item.firstPeriod | centsToDollars }}</p>
|
||||
</div>
|
||||
<div class="column justify-end column-3">
|
||||
<p class="held-history-table-container--large__info-area__text">{{ item.secondPeriod | centsToDollars }}</p>
|
||||
</div>
|
||||
<div class="column justify-end column-4">
|
||||
<p class="held-history-table-container--large__info-area__text">{{ item.thirdPeriod | centsToDollars }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="held-history-table-container--small">
|
||||
<HeldHistoryMonthlyBreakdownTableSmall
|
||||
v-for="item in monthlyBreakdown"
|
||||
:held-history-item="item"
|
||||
:key="item.satelliteID"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import HeldHistoryMonthlyBreakdownTableSmall from '@/app/components/payments/HeldHistoryMonthlyBreakdownTableSmall.vue';
|
||||
|
||||
import { HeldHistoryMonthlyBreakdownItem } from '@/app/types/payout';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
HeldHistoryMonthlyBreakdownTableSmall,
|
||||
},
|
||||
})
|
||||
export default class HeldHistoryMonthlyBreakdownTable extends Vue {
|
||||
/**
|
||||
* Returns list of satellite held history items by periods from store.
|
||||
*/
|
||||
public get monthlyBreakdown(): HeldHistoryMonthlyBreakdownItem[] {
|
||||
return this.$store.state.payoutModule.heldHistory.monthlyBreakdown;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.held-history-table-container--large {
|
||||
|
||||
&__labels-area {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 17px;
|
||||
padding: 0 16px;
|
||||
width: calc(100% - 32px);
|
||||
height: 36px;
|
||||
background: var(--table-header-color);
|
||||
|
||||
&__text {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
font-size: 14px;
|
||||
color: #909bad;
|
||||
}
|
||||
}
|
||||
|
||||
&__info-area {
|
||||
padding: 11px 16px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
min-height: 34px;
|
||||
height: auto;
|
||||
border-bottom: 1px solid rgba(169, 181, 193, 0.3);
|
||||
|
||||
&:last-of-type {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&__text {
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-size: 14px;
|
||||
color: var(--regular-text-color);
|
||||
max-width: 100%;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
&__months {
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-size: 11px;
|
||||
color: #9b9db1;
|
||||
margin-top: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.held-history-table-container--small {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.column {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.justify-start {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.justify-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.column-1 {
|
||||
width: 37%;
|
||||
}
|
||||
|
||||
.column-2,
|
||||
.column-3,
|
||||
.column-4 {
|
||||
width: 21%;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
|
||||
.held-history-table-container--large {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.held-history-table-container--small {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,160 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="held-history-table-container--small__item">
|
||||
<div class="held-history-table-container--small__item__satellite-info">
|
||||
<div>
|
||||
<p class="held-history-table-container--small__item__satellite-info__name">{{ heldHistoryItem.satelliteName }}</p>
|
||||
<p class="held-history-table-container--small__item__satellite-info__months">{{ heldHistoryItem.age }} month</p>
|
||||
</div>
|
||||
<div class="held-history-table-container--small__item__satellite-info__button">
|
||||
<div class="icon hide" @click="hide" v-if="isExpanded">
|
||||
<blue-hide-icon></blue-hide-icon>
|
||||
</div>
|
||||
<div class="icon expand" @click="expand" v-else>
|
||||
<blue-expand-icon></blue-expand-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<transition name="fade">
|
||||
<div class="held-history-table-container--small__item__held-info" v-if="isExpanded">
|
||||
<div class="held-history-table-container--small__item__held-info__item">
|
||||
<p class="held-history-table-container--small__item__held-info__item__label">Month 1-3</p>
|
||||
<p class="held-history-table-container--small__item__held-info__item__value">{{ heldHistoryItem.firstPeriod | centsToDollars }}</p>
|
||||
</div>
|
||||
<div class="held-history-table-container--small__item__held-info__item">
|
||||
<p class="held-history-table-container--small__item__held-info__item__label">Month 4-6</p>
|
||||
<p class="held-history-table-container--small__item__held-info__item__value">{{ heldHistoryItem.secondPeriod | centsToDollars }}</p>
|
||||
</div>
|
||||
<div class="held-history-table-container--small__item__held-info__item">
|
||||
<p class="held-history-table-container--small__item__held-info__item__label">Month 7-9</p>
|
||||
<p class="held-history-table-container--small__item__held-info__item__value">{{ heldHistoryItem.thirdPeriod | centsToDollars }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
|
||||
import BlueHideIcon from '@/../static/images/common/BlueMinus.svg';
|
||||
import BlueExpandIcon from '@/../static/images/common/BluePlus.svg';
|
||||
|
||||
import { HeldHistoryMonthlyBreakdownItem } from '@/app/types/payout';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
BlueExpandIcon,
|
||||
BlueHideIcon,
|
||||
},
|
||||
})
|
||||
export default class HeldHistoryMonthlyBreakdownTableSmall extends Vue {
|
||||
@Prop({default: () => new HeldHistoryMonthlyBreakdownItem()})
|
||||
public readonly heldHistoryItem: HeldHistoryMonthlyBreakdownItem;
|
||||
|
||||
/**
|
||||
* Indicates if held info should be rendered.
|
||||
*/
|
||||
public isExpanded: boolean = false;
|
||||
|
||||
/**
|
||||
* Shows held info.
|
||||
*/
|
||||
public expand(): void {
|
||||
this.isExpanded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides held info.
|
||||
*/
|
||||
public hide(): void {
|
||||
this.isExpanded = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.held-history-table-container--small__item {
|
||||
padding: 12px;
|
||||
width: calc(100% - 24px);
|
||||
|
||||
&__satellite-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
&__name {
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-size: 14px;
|
||||
color: var(--regular-text-color);
|
||||
max-width: calc(100% - 40px);
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
&__months {
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-size: 11px;
|
||||
color: #9b9db1;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
&__button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
min-width: 30px;
|
||||
min-height: 30px;
|
||||
background: var(--expand-button-background-color);
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&__held-info {
|
||||
margin-top: 16px;
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&__label {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
color: #909bad;
|
||||
}
|
||||
|
||||
&__value {
|
||||
font-family: 'font_regular', sans-serif;
|
||||
color: var(--regular-text-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
@ -1,126 +0,0 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="held-history-table-container__labels-area">
|
||||
<div class="column justify-start column-1">
|
||||
<p class="held-history-table-container__labels-area__text">Satellite</p>
|
||||
</div>
|
||||
<div class="column justify-start column-2">
|
||||
<p class="held-history-table-container__labels-area__text">Month 1-3</p>
|
||||
</div>
|
||||
<div class="column justify-start column-3">
|
||||
<p class="held-history-table-container__labels-area__text">Month 4-6</p>
|
||||
</div>
|
||||
<div class="column justify-end column-4">
|
||||
<p class="held-history-table-container__labels-area__text">Month 7-9</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="item in data" class="held-history-table-container__info-area">
|
||||
<div class="column justify-start column-1">
|
||||
<p class="held-history-table-container__info-area__text">{{ item.satelliteName }}</p>
|
||||
<p class="held-history-table-container__info-area__text">{{ item.satelliteAge }}</p>
|
||||
</div>
|
||||
<div class="column justify-start column-2">
|
||||
<p class="held-history-table-container__info-area__text">{{ item.firstPeriodAmount }}</p>
|
||||
</div>
|
||||
<div class="column justify-start column-3">
|
||||
<p class="held-history-table-container__info-area__text">{{ item.secondPeriodAmount }}</p>
|
||||
</div>
|
||||
<div class="column justify-end column-4">
|
||||
<p class="held-history-table-container__info-area__text">{{ item.thirdPeriodAmount }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
class HeldHistoryTableRow {
|
||||
public constructor(
|
||||
public satelliteName: string = '',
|
||||
public satelliteAge: string = '',
|
||||
public firstPeriodAmount: string = '',
|
||||
public secondPeriodAmount: string = '',
|
||||
public thirdPeriodAmount: string = '',
|
||||
) {}
|
||||
}
|
||||
|
||||
@Component
|
||||
export default class HeldHistoryTable extends Vue {
|
||||
public data: HeldHistoryTableRow[] = [
|
||||
new HeldHistoryTableRow('us-central-1', '17 month', '$0.0005', '$0.0005', '$0.0005'),
|
||||
new HeldHistoryTableRow('europe-west-1', '7 month', '$0.0005', '$0.0005', '$0.0005'),
|
||||
new HeldHistoryTableRow('europe-west-1', '3 month', '$0.0005', '$0.0005', '$0.0005'),
|
||||
];
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.held-history-table-container {
|
||||
|
||||
&__labels-area {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 17px;
|
||||
padding: 0 16px;
|
||||
width: calc(100% - 32px);
|
||||
height: 36px;
|
||||
background: #f9fafc;
|
||||
|
||||
&__text {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
font-size: 14px;
|
||||
color: #909bad;
|
||||
}
|
||||
}
|
||||
|
||||
&__info-area {
|
||||
padding: 0 16px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 56px;
|
||||
border-bottom: 1px solid #a9b5c1;
|
||||
|
||||
&:last-of-type {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&__text {
|
||||
font-family: 'font_regular', sans-serif;
|
||||
font-size: 14px;
|
||||
color: #535f77;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.column {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.justify-start {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.justify-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.column-1 {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.column-2,
|
||||
.column-3,
|
||||
.column-4 {
|
||||
width: 13%;
|
||||
}
|
||||
</style>
|
@ -2,6 +2,7 @@
|
||||
// See LICENSE for copying information.
|
||||
|
||||
import {
|
||||
HeldHistory,
|
||||
HeldInfo,
|
||||
PaymentInfoParameters,
|
||||
PayoutApi,
|
||||
@ -17,12 +18,14 @@ export const PAYOUT_MUTATIONS = {
|
||||
SET_RANGE: 'SET_RANGE',
|
||||
SET_TOTAL: 'SET_TOTAL',
|
||||
SET_HELD_PERCENT: 'SET_HELD_PERCENT',
|
||||
SET_HELD_HISTORY: 'SET_HELD_HISTORY',
|
||||
};
|
||||
|
||||
export const PAYOUT_ACTIONS = {
|
||||
GET_HELD_INFO: 'GET_HELD_INFO',
|
||||
SET_PERIODS_RANGE: 'SET_PERIODS_RANGE',
|
||||
GET_TOTAL: 'GET_TOTAL',
|
||||
GET_HELD_HISTORY: 'GET_HELD_HISTORY',
|
||||
};
|
||||
|
||||
export const BANDWIDTH_DOWNLOAD_PRICE_PER_TB = 2000;
|
||||
@ -52,6 +55,9 @@ export function makePayoutModule(api: PayoutApi) {
|
||||
[PAYOUT_MUTATIONS.SET_HELD_PERCENT](state: PayoutState, heldPercentage: number): void {
|
||||
state.heldPercentage = heldPercentage;
|
||||
},
|
||||
[PAYOUT_MUTATIONS.SET_HELD_HISTORY](state: PayoutState, heldHistory: HeldHistory): void {
|
||||
state.heldHistory = heldHistory;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
[PAYOUT_ACTIONS.GET_HELD_INFO]: async function ({commit, state, rootState}: any, satelliteId: string = ''): Promise<void> {
|
||||
@ -99,6 +105,11 @@ export function makePayoutModule(api: PayoutApi) {
|
||||
[PAYOUT_ACTIONS.SET_PERIODS_RANGE]: function ({commit}: any, periodRange: PayoutInfoRange): void {
|
||||
commit(PAYOUT_MUTATIONS.SET_RANGE, periodRange);
|
||||
},
|
||||
[PAYOUT_ACTIONS.GET_HELD_HISTORY]: async function ({commit}: any): Promise<void> {
|
||||
const heldHistory = await api.getHeldHistory();
|
||||
|
||||
commit(PAYOUT_MUTATIONS.SET_HELD_HISTORY, heldHistory);
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -83,6 +83,7 @@ export class PayoutState {
|
||||
public totalEarnings: number = 0,
|
||||
public currentMonthEarnings: number = 0,
|
||||
public heldPercentage: number = 0,
|
||||
public heldHistory: HeldHistory = new HeldHistory(),
|
||||
) {}
|
||||
}
|
||||
|
||||
@ -107,4 +108,34 @@ export interface PayoutApi {
|
||||
* @throws Error
|
||||
*/
|
||||
getTotal(paymentInfoParameters: PaymentInfoParameters): Promise<TotalPayoutInfo>;
|
||||
|
||||
/**
|
||||
* Fetches held history for all satellites.
|
||||
* @throws Error
|
||||
*/
|
||||
getHeldHistory(): Promise<HeldHistory>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds held history information for all satellites.
|
||||
*/
|
||||
export class HeldHistory {
|
||||
public constructor(
|
||||
public monthlyBreakdown: HeldHistoryMonthlyBreakdownItem[] = [],
|
||||
) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains held amounts of satellite grouped by periods.
|
||||
*/
|
||||
export class HeldHistoryMonthlyBreakdownItem {
|
||||
public constructor(
|
||||
public satelliteID: string = '',
|
||||
public satelliteName: string = '',
|
||||
public age: number = 1,
|
||||
public firstPeriod: number = 0,
|
||||
public secondPeriod: number = 0,
|
||||
public thirdPeriod: number = 0,
|
||||
public fourthPeriod: number = 0,
|
||||
) {}
|
||||
}
|
||||
|
@ -29,13 +29,7 @@
|
||||
<SingleInfo width="48%" label="Total Held Amount" :value="totalHeld | centsToDollars" />
|
||||
</section>
|
||||
<HeldProgress v-if="selectedSatellite" class="payout-area-container__process-area" />
|
||||
<!-- <section class="payout-area-container__held-history-container">-->
|
||||
<!-- <div class="payout-area-container__held-history-container__header">-->
|
||||
<!-- <p class="payout-area-container__held-history-container__header__title">Held Amount history</p>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="payout-area-container__held-history-container__divider"></div>-->
|
||||
<!-- <HeldHistoryTable />-->
|
||||
<!-- </section>-->
|
||||
<HeldHistoryArea />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -44,7 +38,8 @@
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import EstimationArea from '@/app/components/payments/EstimationArea.vue';
|
||||
import HeldHistoryTable from '@/app/components/payments/HeldHistoryTable.vue';
|
||||
import HeldHistoryArea from '@/app/components/payments/HeldHistoryArea.vue';
|
||||
import HeldHistoryTable from '@/app/components/payments/HeldHistoryMonthlyBreakdownTable.vue';
|
||||
import HeldProgress from '@/app/components/payments/HeldProgress.vue';
|
||||
import SingleInfo from '@/app/components/payments/SingleInfo.vue';
|
||||
import SatelliteSelection from '@/app/components/SatelliteSelection.vue';
|
||||
@ -59,6 +54,7 @@ import { SatelliteInfo } from '@/storagenode/dashboard';
|
||||
|
||||
@Component ({
|
||||
components: {
|
||||
HeldHistoryArea,
|
||||
HeldProgress,
|
||||
HeldHistoryTable,
|
||||
SingleInfo,
|
||||
@ -181,37 +177,6 @@ export default class PayoutArea extends Vue {
|
||||
&__process-area {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
&__held-history-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 28px 40px 10px 40px;
|
||||
background: #fff;
|
||||
border: 1px solid #eaeaea;
|
||||
box-sizing: border-box;
|
||||
border-radius: 12px;
|
||||
margin: 12px 0 50px;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
&__title {
|
||||
font-family: 'font_medium', sans-serif;
|
||||
font-size: 18px;
|
||||
color: #535f77;
|
||||
}
|
||||
}
|
||||
|
||||
&__divider {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
margin-top: 18px;
|
||||
background-color: #eaeaea;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.additional-text {
|
||||
|
@ -1,7 +1,14 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
import { HeldInfo, PaymentInfoParameters, PayoutApi, TotalPayoutInfo } from '@/app/types/payout';
|
||||
import {
|
||||
HeldHistory,
|
||||
HeldHistoryMonthlyBreakdownItem,
|
||||
HeldInfo,
|
||||
PaymentInfoParameters,
|
||||
PayoutApi,
|
||||
TotalPayoutInfo,
|
||||
} from '@/app/types/payout';
|
||||
import { HttpClient } from '@/storagenode/utils/httpClient';
|
||||
|
||||
/**
|
||||
@ -99,6 +106,39 @@ export class PayoutHttpApi implements PayoutApi {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch total payout information.
|
||||
*
|
||||
* @returns total payout information
|
||||
* @throws Error
|
||||
*/
|
||||
public async getHeldHistory(): Promise<HeldHistory> {
|
||||
const path = `${this.ROOT_PATH}/heldhistory/`;
|
||||
|
||||
const response = await this.client.get(path);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('can not get held history information');
|
||||
}
|
||||
|
||||
const data: any = await response.json() || [];
|
||||
|
||||
// TODO: this will be changed with adding 'all stats' held history.
|
||||
const monthlyBreakdown = data.map((historyItem: any) => {
|
||||
return new HeldHistoryMonthlyBreakdownItem(
|
||||
historyItem.satelliteID,
|
||||
historyItem.satelliteName,
|
||||
historyItem.age,
|
||||
historyItem.firstPeriod / this.PRICE_DIVIDER,
|
||||
historyItem.secondPeriod / this.PRICE_DIVIDER,
|
||||
historyItem.thirdPeriod / this.PRICE_DIVIDER,
|
||||
historyItem.fourthPeriod / this.PRICE_DIVIDER,
|
||||
);
|
||||
});
|
||||
|
||||
return new HeldHistory(monthlyBreakdown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch total payout information depends on month.
|
||||
*
|
||||
|
3
web/storagenode/static/images/common/BlueMinus.svg
Normal file
3
web/storagenode/static/images/common/BlueMinus.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="2" viewBox="0 0 12 2" fill="none">
|
||||
<rect x="12" y="0.142578" width="1.71429" height="12" rx="0.857143" transform="rotate(90 12 0.142578)" fill="#2683FF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 224 B |
4
web/storagenode/static/images/common/BluePlus.svg
Normal file
4
web/storagenode/static/images/common/BluePlus.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none">
|
||||
<rect x="5.14258" width="1.71429" height="12" rx="0.857143" fill="#2683FF"/>
|
||||
<rect x="12" y="5.14258" width="1.71429" height="12" rx="0.857143" transform="rotate(90 12 5.14258)" fill="#2683FF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 305 B |
@ -23,7 +23,7 @@
|
||||
--page-number-color: #354049;
|
||||
--refresh-button-background-color: white;
|
||||
--refresh-button-border-color: #e8e8e8;
|
||||
--estimation-table-header-color: #f9fafc;
|
||||
--table-header-color: #f9fafc;
|
||||
--estimation-table-total-container-color: rgba(0, 117, 255, 0.05);
|
||||
--month-label-color: #535f77;
|
||||
--month-active-background-color: #f1f4f9;
|
||||
@ -42,6 +42,7 @@
|
||||
--egress-font-color: #2e5f46;
|
||||
--egress-tooltip-info-background-color: rgba(211, 242, 204, 0.3);
|
||||
--disk-stat-chart-text-color: #657284;
|
||||
--expand-button-background-color: rgba(226, 236, 247, 0.45);
|
||||
--tooltip-background-path: url('../../static/images/tooltipBack.png');
|
||||
--tooltip-arrow-path: url('../../static/images/tooltipArrow.png');
|
||||
--info-image-arrow-middle-path: url('../../static/images/Message.png');
|
||||
@ -71,7 +72,7 @@
|
||||
--page-number-color: #354049;
|
||||
--refresh-button-background-color: #494c57;
|
||||
--refresh-button-border-color: #212329;
|
||||
--estimation-table-header-color: #3e414d;
|
||||
--table-header-color: #3e414d;
|
||||
--estimation-table-total-container-color: #5e6c80;
|
||||
--month-label-color: #4f97f7;
|
||||
--month-active-background-color: #3f4352;
|
||||
@ -90,6 +91,7 @@
|
||||
--egress-font-color: white;
|
||||
--egress-tooltip-info-background-color: #212329;
|
||||
--disk-stat-chart-text-color: white;
|
||||
--expand-button-background-color: #31343d;
|
||||
--tooltip-background-path: url('../../static/images/tooltipBackDark.png');
|
||||
--tooltip-arrow-path: url('../../static/images/tooltipArrowDark.png');
|
||||
--info-image-arrow-middle-path: url('../../static/images/MessageDark.png');
|
||||
|
@ -1,8 +1,7 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
import Vue, { VNode } from 'vue';
|
||||
import { DirectiveBinding } from 'vue/types/options';
|
||||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
|
||||
import EstimationPeriodDropdown from '@/app/components/payments/EstimationPeriodDropdown.vue';
|
||||
|
@ -0,0 +1,40 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
import Vuex from 'vuex';
|
||||
|
||||
import HeldHistoryMonthlyBreakdownTable from '@/app/components/payments/HeldHistoryMonthlyBreakdownTable.vue';
|
||||
|
||||
import { makePayoutModule, PAYOUT_MUTATIONS } from '@/app/store/modules/payout';
|
||||
import { HeldHistory, HeldHistoryMonthlyBreakdownItem } from '@/app/types/payout';
|
||||
import { PayoutHttpApi } from '@/storagenode/api/payout';
|
||||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
localVue.use(Vuex);
|
||||
|
||||
localVue.filter('centsToDollars', (cents: number): string => {
|
||||
return `$${(cents / 100).toFixed(2)}`;
|
||||
});
|
||||
|
||||
const payoutApi = new PayoutHttpApi();
|
||||
const payoutModule = makePayoutModule(payoutApi);
|
||||
|
||||
const store = new Vuex.Store({ modules: { payoutModule }});
|
||||
|
||||
describe('HeldHistoryMonthlyBreakdownTable', (): void => {
|
||||
it('renders correctly with actual values', async (): Promise<void> => {
|
||||
const wrapper = shallowMount(HeldHistoryMonthlyBreakdownTable, {
|
||||
store,
|
||||
localVue,
|
||||
});
|
||||
|
||||
await store.commit(PAYOUT_MUTATIONS.SET_HELD_HISTORY, new HeldHistory([
|
||||
new HeldHistoryMonthlyBreakdownItem('1', 'name1', 1, 50000, 0, 0, 0),
|
||||
new HeldHistoryMonthlyBreakdownItem('2', 'name2', 5, 50000, 422280, 0, 0),
|
||||
new HeldHistoryMonthlyBreakdownItem('3', 'name3', 6, 50000, 7333880, 7852235, 0),
|
||||
]));
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
@ -0,0 +1,46 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
import HeldHistoryMonthlyBreakdownTableSmall from '@/app/components/payments/HeldHistoryMonthlyBreakdownTableSmall.vue';
|
||||
|
||||
import { HeldHistoryMonthlyBreakdownItem } from '@/app/types/payout';
|
||||
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
|
||||
localVue.filter('centsToDollars', (cents: number): string => {
|
||||
return `$${(cents / 100).toFixed(2)}`;
|
||||
});
|
||||
|
||||
describe('HeldHistoryMonthlyBreakdownTableSmall', (): void => {
|
||||
it('renders correctly with actual values', async (): Promise<void> => {
|
||||
const wrapper = shallowMount(HeldHistoryMonthlyBreakdownTableSmall, {
|
||||
propsData: {
|
||||
heldHistoryItem: new HeldHistoryMonthlyBreakdownItem(
|
||||
'1',
|
||||
'name1',
|
||||
6,
|
||||
50000,
|
||||
7333880,
|
||||
7852235,
|
||||
0,
|
||||
),
|
||||
},
|
||||
localVue,
|
||||
});
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
|
||||
wrapper.find('.expand').trigger('click');
|
||||
|
||||
await localVue.nextTick();
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
|
||||
wrapper.find('.hide').trigger('click');
|
||||
|
||||
await localVue.nextTick();
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
@ -0,0 +1,72 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`HeldHistoryMonthlyBreakdownTable renders correctly with actual values 1`] = `
|
||||
<div>
|
||||
<div class="held-history-table-container--large">
|
||||
<div class="held-history-table-container--large__labels-area">
|
||||
<div class="column justify-start column-1">
|
||||
<p class="held-history-table-container--large__labels-area__text">Satellite</p>
|
||||
</div>
|
||||
<div class="column justify-end column-2">
|
||||
<p class="held-history-table-container--large__labels-area__text">Month 1-3</p>
|
||||
</div>
|
||||
<div class="column justify-end column-3">
|
||||
<p class="held-history-table-container--large__labels-area__text">Month 4-6</p>
|
||||
</div>
|
||||
<div class="column justify-end column-4">
|
||||
<p class="held-history-table-container--large__labels-area__text">Month 7-9</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="held-history-table-container--large__info-area">
|
||||
<div class="justify-start column-1">
|
||||
<p class="held-history-table-container--large__info-area__text">name1</p>
|
||||
<p class="held-history-table-container--large__info-area__months">1 month</p>
|
||||
</div>
|
||||
<div class="column justify-end column-2">
|
||||
<p class="held-history-table-container--large__info-area__text">$500.00</p>
|
||||
</div>
|
||||
<div class="column justify-end column-3">
|
||||
<p class="held-history-table-container--large__info-area__text">$0.00</p>
|
||||
</div>
|
||||
<div class="column justify-end column-4">
|
||||
<p class="held-history-table-container--large__info-area__text">$0.00</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="held-history-table-container--large__info-area">
|
||||
<div class="justify-start column-1">
|
||||
<p class="held-history-table-container--large__info-area__text">name2</p>
|
||||
<p class="held-history-table-container--large__info-area__months">5 month</p>
|
||||
</div>
|
||||
<div class="column justify-end column-2">
|
||||
<p class="held-history-table-container--large__info-area__text">$500.00</p>
|
||||
</div>
|
||||
<div class="column justify-end column-3">
|
||||
<p class="held-history-table-container--large__info-area__text">$4222.80</p>
|
||||
</div>
|
||||
<div class="column justify-end column-4">
|
||||
<p class="held-history-table-container--large__info-area__text">$0.00</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="held-history-table-container--large__info-area">
|
||||
<div class="justify-start column-1">
|
||||
<p class="held-history-table-container--large__info-area__text">name3</p>
|
||||
<p class="held-history-table-container--large__info-area__months">6 month</p>
|
||||
</div>
|
||||
<div class="column justify-end column-2">
|
||||
<p class="held-history-table-container--large__info-area__text">$500.00</p>
|
||||
</div>
|
||||
<div class="column justify-end column-3">
|
||||
<p class="held-history-table-container--large__info-area__text">$73338.80</p>
|
||||
</div>
|
||||
<div class="column justify-end column-4">
|
||||
<p class="held-history-table-container--large__info-area__text">$78522.35</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="held-history-table-container--small">
|
||||
<heldhistorymonthlybreakdowntablesmall-stub heldhistoryitem="[object Object]"></heldhistorymonthlybreakdowntablesmall-stub>
|
||||
<heldhistorymonthlybreakdowntablesmall-stub heldhistoryitem="[object Object]"></heldhistorymonthlybreakdowntablesmall-stub>
|
||||
<heldhistorymonthlybreakdowntablesmall-stub heldhistoryitem="[object Object]"></heldhistorymonthlybreakdowntablesmall-stub>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -0,0 +1,71 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`HeldHistoryMonthlyBreakdownTableSmall renders correctly with actual values 1`] = `
|
||||
<div class="held-history-table-container--small__item">
|
||||
<div class="held-history-table-container--small__item__satellite-info">
|
||||
<div>
|
||||
<p class="held-history-table-container--small__item__satellite-info__name">name1</p>
|
||||
<p class="held-history-table-container--small__item__satellite-info__months">6 month</p>
|
||||
</div>
|
||||
<div class="held-history-table-container--small__item__satellite-info__button">
|
||||
<div class="icon expand">
|
||||
<blue-expand-icon-stub></blue-expand-icon-stub>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<transition-stub name="fade">
|
||||
<!---->
|
||||
</transition-stub>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`HeldHistoryMonthlyBreakdownTableSmall renders correctly with actual values 2`] = `
|
||||
<div class="held-history-table-container--small__item">
|
||||
<div class="held-history-table-container--small__item__satellite-info">
|
||||
<div>
|
||||
<p class="held-history-table-container--small__item__satellite-info__name">name1</p>
|
||||
<p class="held-history-table-container--small__item__satellite-info__months">6 month</p>
|
||||
</div>
|
||||
<div class="held-history-table-container--small__item__satellite-info__button">
|
||||
<div class="icon hide">
|
||||
<blue-hide-icon-stub></blue-hide-icon-stub>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<transition-stub name="fade">
|
||||
<div class="held-history-table-container--small__item__held-info">
|
||||
<div class="held-history-table-container--small__item__held-info__item">
|
||||
<p class="held-history-table-container--small__item__held-info__item__label">Month 1-3</p>
|
||||
<p class="held-history-table-container--small__item__held-info__item__value">$500.00</p>
|
||||
</div>
|
||||
<div class="held-history-table-container--small__item__held-info__item">
|
||||
<p class="held-history-table-container--small__item__held-info__item__label">Month 4-6</p>
|
||||
<p class="held-history-table-container--small__item__held-info__item__value">$73338.80</p>
|
||||
</div>
|
||||
<div class="held-history-table-container--small__item__held-info__item">
|
||||
<p class="held-history-table-container--small__item__held-info__item__label">Month 7-9</p>
|
||||
<p class="held-history-table-container--small__item__held-info__item__value">$78522.35</p>
|
||||
</div>
|
||||
</div>
|
||||
</transition-stub>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`HeldHistoryMonthlyBreakdownTableSmall renders correctly with actual values 3`] = `
|
||||
<div class="held-history-table-container--small__item">
|
||||
<div class="held-history-table-container--small__item__satellite-info">
|
||||
<div>
|
||||
<p class="held-history-table-container--small__item__satellite-info__name">name1</p>
|
||||
<p class="held-history-table-container--small__item__satellite-info__months">6 month</p>
|
||||
</div>
|
||||
<div class="held-history-table-container--small__item__satellite-info__button">
|
||||
<div class="icon expand">
|
||||
<blue-expand-icon-stub></blue-expand-icon-stub>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<transition-stub name="fade">
|
||||
<!---->
|
||||
</transition-stub>
|
||||
</div>
|
||||
`;
|
@ -1,26 +0,0 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
import {
|
||||
HeldInfo,
|
||||
PaymentInfoParameters,
|
||||
PayoutApi,
|
||||
TotalPayoutInfo,
|
||||
} from '@/app/types/payout';
|
||||
|
||||
/**
|
||||
* Mock for PayoutApi.
|
||||
*/
|
||||
export class PayoutApiMock implements PayoutApi {
|
||||
public getHeldInfoByMonth(paymentInfoParameters: PaymentInfoParameters): Promise<HeldInfo> {
|
||||
return Promise.resolve(new HeldInfo());
|
||||
}
|
||||
|
||||
public getHeldInfoByPeriod(paymentInfoParameters: PaymentInfoParameters): Promise<HeldInfo> {
|
||||
return Promise.resolve(new HeldInfo());
|
||||
}
|
||||
|
||||
public getTotal(paymentInfoParameters: PaymentInfoParameters): Promise<TotalPayoutInfo> {
|
||||
return Promise.resolve(new TotalPayoutInfo());
|
||||
}
|
||||
}
|
@ -5,7 +5,14 @@ import Vuex from 'vuex';
|
||||
|
||||
import { makeNodeModule } from '@/app/store/modules/node';
|
||||
import { makePayoutModule, PAYOUT_ACTIONS, PAYOUT_MUTATIONS } from '@/app/store/modules/payout';
|
||||
import { HeldInfo, PayoutInfoRange, PayoutPeriod, TotalPayoutInfo } from '@/app/types/payout';
|
||||
import {
|
||||
HeldHistory,
|
||||
HeldHistoryMonthlyBreakdownItem,
|
||||
HeldInfo,
|
||||
PayoutInfoRange,
|
||||
PayoutPeriod,
|
||||
TotalPayoutInfo,
|
||||
} from '@/app/types/payout';
|
||||
import { getHeldPercentage, getMonthsBeforeNow } from '@/app/utils/payout';
|
||||
import { PayoutHttpApi } from '@/storagenode/api/payout';
|
||||
import { SNOApi } from '@/storagenode/api/storagenode';
|
||||
@ -69,6 +76,19 @@ describe('mutations', (): void => {
|
||||
|
||||
expect(state.payoutModule.heldPercentage).toBe(expectedHeldPercentage);
|
||||
});
|
||||
|
||||
it('sets held history', (): void => {
|
||||
const testHeldHistory = new HeldHistory([
|
||||
new HeldHistoryMonthlyBreakdownItem('1', 'name1', 1, 50000, 0, 0, 0),
|
||||
new HeldHistoryMonthlyBreakdownItem('2', 'name2', 5, 50000, 422280, 0, 0),
|
||||
new HeldHistoryMonthlyBreakdownItem('3', 'name3', 6, 50000, 7333880, 7852235, 0),
|
||||
]);
|
||||
|
||||
store.commit(PAYOUT_MUTATIONS.SET_HELD_HISTORY, testHeldHistory);
|
||||
|
||||
expect(state.payoutModule.heldHistory.monthlyBreakdown.length).toBe(testHeldHistory.monthlyBreakdown.length);
|
||||
expect(state.payoutModule.heldHistory.monthlyBreakdown[1].satelliteName).toBe(testHeldHistory.monthlyBreakdown[1].satelliteName);
|
||||
});
|
||||
});
|
||||
|
||||
describe('actions', () => {
|
||||
@ -168,6 +188,33 @@ describe('actions', () => {
|
||||
expect(state.payoutModule.periodRange.start.period).toBe('2020-02');
|
||||
expect(state.payoutModule.periodRange.end.period).toBe('2020-03');
|
||||
});
|
||||
|
||||
it('success get held history', async (): Promise<void> => {
|
||||
jest.spyOn(payoutApi, 'getHeldHistory').mockReturnValue(
|
||||
Promise.resolve(new HeldHistory([
|
||||
new HeldHistoryMonthlyBreakdownItem('1', 'name1', 1, 50000, 0, 0, 0),
|
||||
new HeldHistoryMonthlyBreakdownItem('2', 'name2', 5, 50000, 422280, 0, 0),
|
||||
new HeldHistoryMonthlyBreakdownItem('3', 'name3', 6, 50000, 7333880, 7852235, 0),
|
||||
])),
|
||||
);
|
||||
|
||||
await store.dispatch(PAYOUT_ACTIONS.GET_HELD_HISTORY);
|
||||
|
||||
expect(state.payoutModule.heldHistory.monthlyBreakdown.length).toBe(3);
|
||||
expect(state.payoutModule.heldHistory.monthlyBreakdown[1].satelliteName).toBe('name2');
|
||||
});
|
||||
|
||||
it('get total throws an error when api call fails', async (): Promise<void> => {
|
||||
jest.spyOn(payoutApi, 'getHeldHistory').mockImplementation(() => { throw new Error(); });
|
||||
|
||||
try {
|
||||
await store.dispatch(PAYOUT_ACTIONS.GET_HELD_HISTORY);
|
||||
expect(true).toBe(false);
|
||||
} catch (error) {
|
||||
expect(state.payoutModule.heldHistory.monthlyBreakdown.length).toBe(3);
|
||||
expect(state.payoutModule.heldHistory.monthlyBreakdown[1].satelliteName).toBe('name2');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('utils functions', (): void => {
|
||||
|
Loading…
Reference in New Issue
Block a user