Advertisement

#32 Using Our List View And Tab Components To Display Account Details

In this article we will finally start displaying some useful information on our account details page. Along with displaying the cash, which is a new property, associated with the account we will also show the securities and deposits. We will leverage the tabs and list view components that we created in previous videos to give us a more polished and compact way of displaying both the deposits and the securities.

Code Snippets

account-model.ts (1:31)

...
export interface IAccountModelConfig {
    cash: number;
    ...
}

export class AccountModel {
    private _cash = 0;
    ...
    public get cash() {
        return this._cash;
    }
    public set cash(value: number) {
        this._cash = value;
    }
    ...
    constructor(config: IAccountModelConfig) {
        this._cash = config.cash;
        ...
    }
}
src ⟩ store ⟩ account-model.ts

account-initial-state.ts (2:30)

...
if (process.env.NODE_ENV === "development") {
    index = createAccount(index, { cash: 5684, name: "name-1" });
    index = createAccount(index, { cash: 0, name: "name-2" });
}
...
src ⟩ store ⟩ account-initial-state.ts

functions.ts (3:07)

export function currencyFormatter(minimumFractionDigits: number = 2, maximumFractionDigits: number = 2) {
    return new Intl.NumberFormat("en-US", {
        currency: "USD",
        maximumFractionDigits,
        minimumFractionDigits,
        style: "currency",
    });
}
src ⟩ functions.ts

AccountsDetails.vue (4:27)

<script lang="ts">...
import { currencyFormatter } from "@/functions";

import {
    AccountDepositModel,
    AccountSecurityModel,
    ...,
} from "@/store";
...
export default class AccountsDetails extends Vue {
    ...
    private readonly currency = currencyFormatter();
    ...
    private accountDeposits: AccountDepositModel[] = [];
    private accountSecurities: AccountSecurityModel[] = [];
    private cash = 0;
    private depositTotal = 0;
    ...
    private get profit() {
        return this.value - (this.depositTotal - this.cash);
    }

    private get roi() {
        const roi = this.depositTotal === 0 ? 0 : (this.profit / this.depositTotal) * 100;
        return `${roi.toFixed(2)} %`;
    }

    private get value() {
        return this.accountSecurities.reduce((prev, cur) => prev + cur.value, 0);
    }
    ...
    private load() {
        const account = this.getterAccount(this.id);
        this.accountDeposits = account.deposits;
        this.accountSecurities = account.securities;
        this.cash = account.cash;
        this.name = account.name;
        this.depositTotal = account.deposits.reduce((prev, cur) => prev + cur.amount, 0);
    }
    ...
}
</script>
src ⟩ views ⟩ AccountsDetails.vue

AccountsDetails.vue (8:58)

<template lang="pug">
div
    form
        div.inputs
            ...
            label Cash
            input(
                type="number"
                v-model="cash")
        ...
    div(class="account-stats")
        div
            label Value
            div {{currency.format(value)}}
        div
            label Total Deposits
            div {{currency.format(depositTotal)}}
        div
            label Profit
            div {{currency.format(profit)}}
        div
            label ROI
            div {{roi}}
</template>
src ⟩ views ⟩ AccountsDetails.vue

AccountsDetails.vue (11:43)

<style lang="sass" scoped>
...
.account-stats
    display: flex
    flex-wrap: wrap
    border: 1px solid $light-gray
    padding-top: 0.75rem

    > *
        flex: 1
        min-width: 150px
        text-align: center
        margin-bottom: 0.75rem
</style>
src ⟩ views ⟩ AccountsDetails.vue

Advertisement

ListView.vue (14:38)

<script lang="tsx">
...
export default class ListView extends Vue {
    @Prop() private readonly headings?: string[];
    ...
    private render() {
        ...
        return (
            <ul>
                ...
                {this.headings && (
                    <li class="list-item-content">
                        {this.headings.map((x) => (
                            <div class="list-item-text list-item-heading">{x}</div>
                        ))}
                        <div style="opacity: 0;">&rang;</div>
                    </li>
                )}
                ...
            </ul>
        );
    }
}
</script>
src ⟩ components ⟩ ListView.vue

AccountsDetails.vue (17:14)

<script lang="tsx">
...
import ListView from "@/components/ListView.vue";
import { TabContainer } from "@/components/tabs";
...
@Component({
    components: {
        ...,
        ListView,
        TabContainer,
    },
})
export default class AccountsDetails extends Vue {
    ...
    private readonly accountSecurityHeadings = ["Symbol", "Value", "Shares"];
    private readonly accountDepositHeadings = ["Date", "Amount"];
    ...
    private readonly tabs = ["Securities", "Deposits"];
    ...
    private handleClickAccountDeposit(deposit: AccountDepositModel) {
        console.log(deposit);
    }

    private handleClickAccountSecurity(accountSecurity: AccountSecurityModel) {
        console.log(accountSecurity);
    }

    private handleClickCreateAccountDeposit() {
        console.log("handleClickCreateAccountDeposit");
    }

    private handleClickCreateAccountSecurity() {
        console.log("handleClickCreateAccountSecurity");
    }
    ...
    private renderDeposit(accountDeposit: AccountDepositModel) {
        return (
            <div class="account-deposit">
                <span>{accountDeposit.date.toDateString()}</span>
                <span>{this.currency.format(accountDeposit.amount)}</span>
            </div>
        );
    }

    private renderSecurity(accountSecurity: AccountSecurityModel) {
        return (
            <div class="account-security">
                <span>{accountSecurity.security.symbol}</span>
                <span>{this.currency.format(accountSecurity.value)}</span>
                <span>{accountSecurity.shares}</span>
            </div>
        );
    }
}
</script>
src ⟩ views ⟩ AccountsDetails.vue

AccountsDetails.vue (21:42)

<template lang="pug">
div
    ...
    TabContainer(v-bind:tabs="tabs")
        ListView(
            v-bind:headings="accountSecurityHeadings"
            v-bind:items="accountSecurities"
            v-bind:onClick="handleClickAccountSecurity"
            v-bind:onClickCreate="handleClickCreateAccountSecurity"
            v-bind:renderFn="renderSecurity")
        ListView(
            v-bind:headings="accountDepositHeadings"
            v-bind:items="accountDeposits"
            v-bind:onClick="handleClickAccountDeposit"
            v-bind:onClickCreate="handleClickCreateAccountDeposit"
            v-bind:renderFn="renderDeposit")
</template>
src ⟩ views ⟩ AccountsDetails.vue

AccountsDetails.vue (23:50)

<style lang="sass" scoped>
...
.account-deposit, .account-security
    display: flex

    > *
        flex: 1
</style>
src ⟩ views ⟩ AccountsDetails.vue

ListView.vue (24:33)

<style lang="sass" scoped>
...
.list-item-heading
    font-weight: 600
...
</style>
src ⟩ components ⟩ ListView.vue

Exciton Interactive LLC
Advertisement