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 {
|
||||
EstimatedPayout,
|
||||
PayoutPeriod,
|
||||
SatelliteHeldHistory, SatellitePayoutForPeriod,
|
||||
SatelliteHeldHistory,
|
||||
SatellitePayoutForPeriod,
|
||||
TotalHeldAndPaid,
|
||||
TotalPaystubForPeriod,
|
||||
} from '@/storagenode/payouts/payouts';
|
||||
|
@ -27,10 +27,13 @@
|
||||
</a>
|
||||
</p>
|
||||
<section class="payout-area-container__held-info-area">
|
||||
<SingleInfo v-if="selectedSatellite" width="48%" label="Held Amount Rate" :value="heldPercentage + '%'" />
|
||||
<SingleInfo width="48%" label="Total Held Amount" :value="totalHeld | centsToDollars" />
|
||||
<TotalHeldArea v-if="isSatelliteSelected" />
|
||||
<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>
|
||||
<HeldProgress v-if="selectedSatellite" class="payout-area-container__process-area" />
|
||||
<HeldProgress v-if="isSatelliteSelected" class="payout-area-container__process-area" />
|
||||
<HeldHistoryArea />
|
||||
</div>
|
||||
</div>
|
||||
@ -45,6 +48,7 @@ import HeldHistoryTable from '@/app/components/payments/HeldHistoryMonthlyBreakd
|
||||
import HeldProgress from '@/app/components/payments/HeldProgress.vue';
|
||||
import PayoutHistoryTable from '@/app/components/payments/PayoutHistoryTable.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 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 { PAYOUT_ACTIONS } from '@/app/store/modules/payout';
|
||||
import { NotificationsCursor } from '@/app/types/notifications';
|
||||
import { SatelliteInfo } from '@/storagenode/dashboard';
|
||||
import { PayoutPeriod } from '@/storagenode/payouts/payouts';
|
||||
import { PayoutPeriod, TotalHeldAndPaid } from '@/storagenode/payouts/payouts';
|
||||
|
||||
@Component ({
|
||||
components: {
|
||||
TotalHeldArea,
|
||||
PayoutHistoryTable,
|
||||
HeldHistoryArea,
|
||||
HeldProgress,
|
||||
@ -110,20 +114,15 @@ export default class PayoutArea extends Vue {
|
||||
await this.$store.dispatch(APPSTATE_ACTIONS.SET_LOADING, false);
|
||||
}
|
||||
|
||||
public get totalHeld(): number {
|
||||
return this.$store.state.payoutModule.totalHeldAndPaid.held;
|
||||
}
|
||||
|
||||
public get heldPercentage(): number {
|
||||
return this.$store.state.payoutModule.heldPercentage;
|
||||
public get totalHeldAndPaid(): TotalHeldAndPaid {
|
||||
return this.$store.state.payoutModule.totalHeldAndPaid;
|
||||
}
|
||||
|
||||
/**
|
||||
* selectedSatellite - current selected satellite from store.
|
||||
* @return SatelliteInfo - current selected satellite
|
||||
* Indicates if satellite is selected.
|
||||
*/
|
||||
public get selectedSatellite(): SatelliteInfo {
|
||||
return this.$store.state.node.selectedSatellite.id;
|
||||
public get isSatelliteSelected(): boolean {
|
||||
return !!this.$store.state.node.selectedSatellite.id;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
.payout-area-container {
|
||||
@ -259,5 +264,9 @@ export default class PayoutArea extends Vue {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.row {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -172,6 +172,7 @@ export class TotalPaystubForPeriod {
|
||||
export class TotalHeldAndPaid {
|
||||
public held: number = 0;
|
||||
public paid: number = 0;
|
||||
public disposed: number = 0;
|
||||
// TODO: remove
|
||||
public currentMonthEarnings: number = 0;
|
||||
|
||||
@ -181,6 +182,7 @@ export class TotalHeldAndPaid {
|
||||
paystubs.forEach(paystub => {
|
||||
this.held += this.convertToCents(paystub.held - paystub.disposed);
|
||||
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.paid).toBe(100);
|
||||
expect(state.payoutModule.totalHeldAndPaid.disposed).toBe(10);
|
||||
expect(state.payoutModule.currentMonthEarnings).toBe(22);
|
||||
});
|
||||
|
||||
@ -246,6 +247,7 @@ describe('actions', () => {
|
||||
it('success get total', async (): Promise<void> => {
|
||||
const paystub = new Paystub();
|
||||
paystub.held = 100000;
|
||||
paystub.disposed = 50000;
|
||||
paystub.paid = 200000;
|
||||
|
||||
jest.spyOn(payoutApi, 'getPaystubsForPeriod').mockReturnValue(
|
||||
@ -254,8 +256,9 @@ describe('actions', () => {
|
||||
|
||||
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.disposed).toBe(5);
|
||||
expect(state.payoutModule.currentMonthEarnings).toBe(0);
|
||||
});
|
||||
|
||||
@ -266,7 +269,7 @@ describe('actions', () => {
|
||||
await store.dispatch(PAYOUT_ACTIONS.GET_TOTAL);
|
||||
expect(true).toBe(false);
|
||||
} 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.currentMonthEarnings).toBe(0);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user