web/storagenode: held returned block added
WHAT: held information block on payout page WHY: to show held and held disposed amounts Change-Id: I34b8f17993f93d7fdbc65021d0a088c8a5490f8d
This commit is contained in:
parent
84b6b91ee1
commit
92a336cb5a
120
web/storagenode/src/app/components/payments/TotalHeldArea.vue
Normal file
120
web/storagenode/src/app/components/payments/TotalHeldArea.vue
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
// Copyright (C) 2020 Storj Labs, Inc.
|
||||||
|
// See LICENSE for copying information.
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<section class="total-held-area">
|
||||||
|
<div class="total-held-area__united-info-area">
|
||||||
|
<div class="total-held-area__united-info-area__item">
|
||||||
|
<p class="total-held-area__united-info-area__item__label">Held Amount Rate</p>
|
||||||
|
<p class="total-held-area__united-info-area__item__amount">{{ heldPercentage }}%</p>
|
||||||
|
</div>
|
||||||
|
<div class="total-held-area__united-info-area__item align-center">
|
||||||
|
<p class="total-held-area__united-info-area__item__label">Total Held Amount</p>
|
||||||
|
<p class="total-held-area__united-info-area__item__amount">{{ totalHeldAndPaid.held | centsToDollars }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="total-held-area__united-info-area__item align-end">
|
||||||
|
<p class="total-held-area__united-info-area__item__label">Total Held Returned</p>
|
||||||
|
<p class="total-held-area__united-info-area__item__amount">{{ totalHeldAndPaid.disposed | centsToDollars }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="total-held-area__info-area">
|
||||||
|
<SingleInfo width="100%" label="Held Amount Rate" :value="heldPercentage + '%'" />
|
||||||
|
<SingleInfo width="100%" label="Total Held Amount" :value="totalHeldAndPaid.held | centsToDollars" />
|
||||||
|
<SingleInfo width="100%" label="Total Held Returned" :value="totalHeldAndPaid.disposed | centsToDollars" />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Vue } from 'vue-property-decorator';
|
||||||
|
|
||||||
|
import SingleInfo from '@/app/components/payments/SingleInfo.vue';
|
||||||
|
|
||||||
|
import { TotalHeldAndPaid } from '@/storagenode/payouts/payouts';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: {
|
||||||
|
SingleInfo,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export default class TotalPayoutArea extends Vue {
|
||||||
|
public get totalHeldAndPaid(): TotalHeldAndPaid {
|
||||||
|
return this.$store.state.payoutModule.totalHeldAndPaid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get heldPercentage(): string {
|
||||||
|
return this.$store.state.payoutModule.heldPercentage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.total-held-area {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&__united-info-area {
|
||||||
|
width: calc(100% - 60px);
|
||||||
|
padding: 24px 30px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
background: var(--block-background-color);
|
||||||
|
border: 1px solid var(--block-border-color);
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
|
&__item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
color: var(--regular-text-color);
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
font-family: 'font_regular', sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__amount {
|
||||||
|
font-family: 'font_medium', sans-serif;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__info-area {
|
||||||
|
display: none;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-center {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-end {
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 780px) {
|
||||||
|
|
||||||
|
.total-held-area {
|
||||||
|
|
||||||
|
&__united-info-area {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__info-area {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.info-container {
|
||||||
|
width: 100% !important;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -4,7 +4,8 @@
|
|||||||
import {
|
import {
|
||||||
EstimatedPayout,
|
EstimatedPayout,
|
||||||
PayoutPeriod,
|
PayoutPeriod,
|
||||||
SatelliteHeldHistory, SatellitePayoutForPeriod,
|
SatelliteHeldHistory,
|
||||||
|
SatellitePayoutForPeriod,
|
||||||
TotalHeldAndPaid,
|
TotalHeldAndPaid,
|
||||||
TotalPaystubForPeriod,
|
TotalPaystubForPeriod,
|
||||||
} from '@/storagenode/payouts/payouts';
|
} from '@/storagenode/payouts/payouts';
|
||||||
|
@ -27,10 +27,13 @@
|
|||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<section class="payout-area-container__held-info-area">
|
<section class="payout-area-container__held-info-area">
|
||||||
<SingleInfo v-if="selectedSatellite" width="48%" label="Held Amount Rate" :value="heldPercentage + '%'" />
|
<TotalHeldArea v-if="isSatelliteSelected" />
|
||||||
<SingleInfo width="48%" label="Total Held Amount" :value="totalHeld | centsToDollars" />
|
<div class="row" v-else >
|
||||||
|
<SingleInfo width="48%" label="Total Held Amount" :value="totalHeldAndPaid.held | centsToDollars" />
|
||||||
|
<SingleInfo width="48%" label="Total Held Returned" :value="totalHeldAndPaid.disposed | centsToDollars" />
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<HeldProgress v-if="selectedSatellite" class="payout-area-container__process-area" />
|
<HeldProgress v-if="isSatelliteSelected" class="payout-area-container__process-area" />
|
||||||
<HeldHistoryArea />
|
<HeldHistoryArea />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -45,6 +48,7 @@ import HeldHistoryTable from '@/app/components/payments/HeldHistoryMonthlyBreakd
|
|||||||
import HeldProgress from '@/app/components/payments/HeldProgress.vue';
|
import HeldProgress from '@/app/components/payments/HeldProgress.vue';
|
||||||
import PayoutHistoryTable from '@/app/components/payments/PayoutHistoryTable.vue';
|
import PayoutHistoryTable from '@/app/components/payments/PayoutHistoryTable.vue';
|
||||||
import SingleInfo from '@/app/components/payments/SingleInfo.vue';
|
import SingleInfo from '@/app/components/payments/SingleInfo.vue';
|
||||||
|
import TotalHeldArea from '@/app/components/payments/TotalHeldArea.vue';
|
||||||
import SatelliteSelection from '@/app/components/SatelliteSelection.vue';
|
import SatelliteSelection from '@/app/components/SatelliteSelection.vue';
|
||||||
|
|
||||||
import BackArrowIcon from '@/../static/images/notifications/backArrow.svg';
|
import BackArrowIcon from '@/../static/images/notifications/backArrow.svg';
|
||||||
@ -54,11 +58,11 @@ import { NODE_ACTIONS } from '@/app/store/modules/node';
|
|||||||
import { NOTIFICATIONS_ACTIONS } from '@/app/store/modules/notifications';
|
import { NOTIFICATIONS_ACTIONS } from '@/app/store/modules/notifications';
|
||||||
import { PAYOUT_ACTIONS } from '@/app/store/modules/payout';
|
import { PAYOUT_ACTIONS } from '@/app/store/modules/payout';
|
||||||
import { NotificationsCursor } from '@/app/types/notifications';
|
import { NotificationsCursor } from '@/app/types/notifications';
|
||||||
import { SatelliteInfo } from '@/storagenode/dashboard';
|
import { PayoutPeriod, TotalHeldAndPaid } from '@/storagenode/payouts/payouts';
|
||||||
import { PayoutPeriod } from '@/storagenode/payouts/payouts';
|
|
||||||
|
|
||||||
@Component ({
|
@Component ({
|
||||||
components: {
|
components: {
|
||||||
|
TotalHeldArea,
|
||||||
PayoutHistoryTable,
|
PayoutHistoryTable,
|
||||||
HeldHistoryArea,
|
HeldHistoryArea,
|
||||||
HeldProgress,
|
HeldProgress,
|
||||||
@ -110,20 +114,15 @@ export default class PayoutArea extends Vue {
|
|||||||
await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, false);
|
await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get totalHeld(): number {
|
public get totalHeldAndPaid(): TotalHeldAndPaid {
|
||||||
return this.$store.state.payoutModule.totalHeldAndPaid.held;
|
return this.$store.state.payoutModule.totalHeldAndPaid;
|
||||||
}
|
|
||||||
|
|
||||||
public get heldPercentage(): number {
|
|
||||||
return this.$store.state.payoutModule.heldPercentage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* selectedSatellite - current selected satellite from store.
|
* Indicates if satellite is selected.
|
||||||
* @return SatelliteInfo - current selected satellite
|
|
||||||
*/
|
*/
|
||||||
public get selectedSatellite(): SatelliteInfo {
|
public get isSatelliteSelected(): boolean {
|
||||||
return this.$store.state.node.selectedSatellite.id;
|
return !!this.$store.state.node.selectedSatellite.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get payoutPeriods(): PayoutPeriod[] {
|
public get payoutPeriods(): PayoutPeriod[] {
|
||||||
@ -222,6 +221,12 @@ export default class PayoutArea extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 890px) {
|
@media screen and (max-width: 890px) {
|
||||||
|
|
||||||
.payout-area-container {
|
.payout-area-container {
|
||||||
@ -259,5 +264,9 @@ export default class PayoutArea extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -172,6 +172,7 @@ export class TotalPaystubForPeriod {
|
|||||||
export class TotalHeldAndPaid {
|
export class TotalHeldAndPaid {
|
||||||
public held: number = 0;
|
public held: number = 0;
|
||||||
public paid: number = 0;
|
public paid: number = 0;
|
||||||
|
public disposed: number = 0;
|
||||||
// TODO: remove
|
// TODO: remove
|
||||||
public currentMonthEarnings: number = 0;
|
public currentMonthEarnings: number = 0;
|
||||||
|
|
||||||
@ -181,6 +182,7 @@ export class TotalHeldAndPaid {
|
|||||||
paystubs.forEach(paystub => {
|
paystubs.forEach(paystub => {
|
||||||
this.held += this.convertToCents(paystub.held - paystub.disposed);
|
this.held += this.convertToCents(paystub.held - paystub.disposed);
|
||||||
this.paid += this.convertToCents(paystub.paid);
|
this.paid += this.convertToCents(paystub.paid);
|
||||||
|
this.disposed += this.convertToCents(paystub.disposed);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
// Copyright (C) 2020 Storj Labs, Inc.
|
||||||
|
// See LICENSE for copying information.
|
||||||
|
|
||||||
|
import Vuex from 'vuex';
|
||||||
|
|
||||||
|
import TotalHeldArea from '@/app/components/payments/TotalHeldArea.vue';
|
||||||
|
|
||||||
|
import { makeNodeModule, NODE_MUTATIONS } from '@/app/store/modules/node';
|
||||||
|
import { makePayoutModule, PAYOUT_MUTATIONS } from '@/app/store/modules/payout';
|
||||||
|
import { PayoutHttpApi } from '@/storagenode/api/payout';
|
||||||
|
import { SNOApi } from '@/storagenode/api/storagenode';
|
||||||
|
import { Paystub, TotalHeldAndPaid } from '@/storagenode/payouts/payouts';
|
||||||
|
import { PayoutService } from '@/storagenode/payouts/service';
|
||||||
|
import { Metric, Satellite, Stamp } from '@/storagenode/satellite';
|
||||||
|
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 payoutService = new PayoutService(payoutApi);
|
||||||
|
const payoutModule = makePayoutModule(payoutApi, payoutService);
|
||||||
|
const nodeApi = new SNOApi();
|
||||||
|
const nodeModule = makeNodeModule(nodeApi);
|
||||||
|
|
||||||
|
const store = new Vuex.Store({ modules: { payoutModule, node: nodeModule }});
|
||||||
|
|
||||||
|
describe('TotalHeldArea', (): void => {
|
||||||
|
it('renders correctly', (): void => {
|
||||||
|
const wrapper = shallowMount(TotalHeldArea, {
|
||||||
|
store,
|
||||||
|
localVue,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders correctly with actual values', async (): Promise<void> => {
|
||||||
|
const wrapper = shallowMount(TotalHeldArea, {
|
||||||
|
store,
|
||||||
|
localVue,
|
||||||
|
});
|
||||||
|
|
||||||
|
const testJoinAt = new Date(Date.UTC(2018, 0, 30));
|
||||||
|
|
||||||
|
const satelliteInfo = new Satellite(
|
||||||
|
'3',
|
||||||
|
[new Stamp()],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
111,
|
||||||
|
222,
|
||||||
|
50,
|
||||||
|
70,
|
||||||
|
new Metric(1, 1, 1, 0, 1),
|
||||||
|
new Metric(2, 1, 1, 0, 1),
|
||||||
|
testJoinAt,
|
||||||
|
);
|
||||||
|
const paystub = new Paystub();
|
||||||
|
paystub.held = 600000;
|
||||||
|
paystub.disposed = 100000;
|
||||||
|
paystub.paid = 1000000;
|
||||||
|
|
||||||
|
const totalHeldAndPaid = new TotalHeldAndPaid([paystub]);
|
||||||
|
|
||||||
|
await store.commit(NODE_MUTATIONS.SELECT_SATELLITE, satelliteInfo);
|
||||||
|
|
||||||
|
await store.commit(PAYOUT_MUTATIONS.SET_TOTAL, totalHeldAndPaid);
|
||||||
|
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,49 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`TotalHeldArea renders correctly 1`] = `
|
||||||
|
<section class="total-held-area">
|
||||||
|
<div class="total-held-area__united-info-area">
|
||||||
|
<div class="total-held-area__united-info-area__item">
|
||||||
|
<p class="total-held-area__united-info-area__item__label">Held Amount Rate</p>
|
||||||
|
<p class="total-held-area__united-info-area__item__amount">0%</p>
|
||||||
|
</div>
|
||||||
|
<div class="total-held-area__united-info-area__item align-center">
|
||||||
|
<p class="total-held-area__united-info-area__item__label">Total Held Amount</p>
|
||||||
|
<p class="total-held-area__united-info-area__item__amount">$0.00</p>
|
||||||
|
</div>
|
||||||
|
<div class="total-held-area__united-info-area__item align-end">
|
||||||
|
<p class="total-held-area__united-info-area__item__label">Total Held Returned</p>
|
||||||
|
<p class="total-held-area__united-info-area__item__amount">$0.00</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="total-held-area__info-area">
|
||||||
|
<singleinfo-stub width="100%" label="Held Amount Rate" value="0%"></singleinfo-stub>
|
||||||
|
<singleinfo-stub width="100%" label="Total Held Amount" value="$0.00"></singleinfo-stub>
|
||||||
|
<singleinfo-stub width="100%" label="Total Held Returned" value="$0.00"></singleinfo-stub>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`TotalHeldArea renders correctly with actual values 1`] = `
|
||||||
|
<section class="total-held-area">
|
||||||
|
<div class="total-held-area__united-info-area">
|
||||||
|
<div class="total-held-area__united-info-area__item">
|
||||||
|
<p class="total-held-area__united-info-area__item__label">Held Amount Rate</p>
|
||||||
|
<p class="total-held-area__united-info-area__item__amount">0%</p>
|
||||||
|
</div>
|
||||||
|
<div class="total-held-area__united-info-area__item align-center">
|
||||||
|
<p class="total-held-area__united-info-area__item__label">Total Held Amount</p>
|
||||||
|
<p class="total-held-area__united-info-area__item__amount">$0.50</p>
|
||||||
|
</div>
|
||||||
|
<div class="total-held-area__united-info-area__item align-end">
|
||||||
|
<p class="total-held-area__united-info-area__item__label">Total Held Returned</p>
|
||||||
|
<p class="total-held-area__united-info-area__item__amount">$0.10</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="total-held-area__info-area">
|
||||||
|
<singleinfo-stub width="100%" label="Held Amount Rate" value="0%"></singleinfo-stub>
|
||||||
|
<singleinfo-stub width="100%" label="Total Held Amount" value="$0.50"></singleinfo-stub>
|
||||||
|
<singleinfo-stub width="100%" label="Total Held Returned" value="$0.10"></singleinfo-stub>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
`;
|
@ -64,6 +64,7 @@ describe('mutations', (): void => {
|
|||||||
|
|
||||||
expect(state.payoutModule.totalHeldAndPaid.held).toBe(50);
|
expect(state.payoutModule.totalHeldAndPaid.held).toBe(50);
|
||||||
expect(state.payoutModule.totalHeldAndPaid.paid).toBe(100);
|
expect(state.payoutModule.totalHeldAndPaid.paid).toBe(100);
|
||||||
|
expect(state.payoutModule.totalHeldAndPaid.disposed).toBe(10);
|
||||||
expect(state.payoutModule.currentMonthEarnings).toBe(22);
|
expect(state.payoutModule.currentMonthEarnings).toBe(22);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -246,6 +247,7 @@ describe('actions', () => {
|
|||||||
it('success get total', async (): Promise<void> => {
|
it('success get total', async (): Promise<void> => {
|
||||||
const paystub = new Paystub();
|
const paystub = new Paystub();
|
||||||
paystub.held = 100000;
|
paystub.held = 100000;
|
||||||
|
paystub.disposed = 50000;
|
||||||
paystub.paid = 200000;
|
paystub.paid = 200000;
|
||||||
|
|
||||||
jest.spyOn(payoutApi, 'getPaystubsForPeriod').mockReturnValue(
|
jest.spyOn(payoutApi, 'getPaystubsForPeriod').mockReturnValue(
|
||||||
@ -254,8 +256,9 @@ describe('actions', () => {
|
|||||||
|
|
||||||
await store.dispatch(PAYOUT_ACTIONS.GET_TOTAL);
|
await store.dispatch(PAYOUT_ACTIONS.GET_TOTAL);
|
||||||
|
|
||||||
expect(state.payoutModule.totalHeldAndPaid.held).toBe(10);
|
expect(state.payoutModule.totalHeldAndPaid.held).toBe(5);
|
||||||
expect(state.payoutModule.totalHeldAndPaid.paid).toBe(20);
|
expect(state.payoutModule.totalHeldAndPaid.paid).toBe(20);
|
||||||
|
expect(state.payoutModule.totalHeldAndPaid.disposed).toBe(5);
|
||||||
expect(state.payoutModule.currentMonthEarnings).toBe(0);
|
expect(state.payoutModule.currentMonthEarnings).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -266,7 +269,7 @@ describe('actions', () => {
|
|||||||
await store.dispatch(PAYOUT_ACTIONS.GET_TOTAL);
|
await store.dispatch(PAYOUT_ACTIONS.GET_TOTAL);
|
||||||
expect(true).toBe(false);
|
expect(true).toBe(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
expect(state.payoutModule.totalHeldAndPaid.held).toBe(10);
|
expect(state.payoutModule.totalHeldAndPaid.held).toBe(5);
|
||||||
expect(state.payoutModule.totalHeldAndPaid.paid).toBe(20);
|
expect(state.payoutModule.totalHeldAndPaid.paid).toBe(20);
|
||||||
expect(state.payoutModule.currentMonthEarnings).toBe(0);
|
expect(state.payoutModule.currentMonthEarnings).toBe(0);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user