Advertisement

#34 Creating And Updating Deposits In The Vuex Store

In this article we will add the ability to create and edit deposits. In addition to being able to change the date and amount of a deposit we will also allow transferring a deposit between accounts.

Code Snippets

store-constants.ts (1:11)

...
...
export const ACTION_ADD_ACCOUNT_DEPOSIT = "ACTION_ADD_ACCOUNT_DEPOSIT";
...
export const ACTION_UPDATE_ACCOUNT_DEPOSIT = "ACTION_UPDATE_ACCOUNT_DEPOSIT";
...
export const MUTATION_ADD_ACCOUNT_DEPOSIT = "MUTATION_ADD_ACCOUNT";
...
export const MUTATION_UPDATE_ACCOUNT_DEPOSIT = "MUTATION_UPDATE_ACCOUNT_DEPOSIT";
...
src ⟩ store ⟩ store-constants.ts

account-types.ts (2:14)

...
export type PayloadAddAccountDeposit = AccountDepositModel;
...
export type PayloadUpdateAccountDeposit = AccountDepositModel;
...
src ⟩ store ⟩ account-types.ts

functions.ts (3:06)

...
interface ISortOptions {
    descending?: boolean;
}
...
export function add<T extends IStateItem>(
    ...
    func: (item: T) => string | number,
    options?: ISortOptions,
) {
    ...
    state.items = sort([...state.items, item], func, options);
    ...
}
...
function sortAscending(a: string | number, b: string | number) {
    if (a < b) {
        return -1;
    }
    if (a > b) {
        return 1;
    }
    return 0;
}

function sortDescending(a: string | number, b: string | number) {
    if (a > b) {
        return -1;
    }
    if (a < b) {
        return 1;
    }
    return 0;
}

export function sort<T>(items: T[], func: (item: T) => string | number, options?: ISortOptions) {
    return items.sort((a, b) => {
        const funcA = func(a);
        const funcB = func(b);
        let valueA: string | number;
        let valueB: string | number;
        if (typeof funcA === "string" && typeof funcB === "string") {
            valueA = funcA.toUpperCase();
            valueB = funcB.toUpperCase();
        } else if (
            (typeof funcA === "number" && typeof funcB === "number") ||
            (typeof funcA === "bigint" && typeof funcB === "bigint")
        ) {
            valueA = funcA;
            valueB = funcB;
        } else {
            throw new Error(
                "The result of calling the provided function must be to convert items into string or numbers.",
            );
        }
        if (
            typeof options === "undefined" ||
            typeof options.descending === "undefined" ||
            options.descending === false
        ) {
            return sortAscending(valueA, valueB);
        } else {
            return sortDescending(valueA, valueB);
        }
    });
}
...
src ⟩ store ⟩ functions.ts

Advertisement

account-module.ts (9:37)

...
import moment from "moment";

import {
    ...
    ACTION_ADD_ACCOUNT_DEPOSIT,
    ...
    ACTION_UPDATE_ACCOUNT_DEPOSIT,
    ...
    MUTATION_ADD_ACCOUNT_DEPOSIT,
    ...
    MUTATION_UPDATE_ACCOUNT_DEPOSIT,
    ...
} from "@/store/store-constants";

import { ... sort, ... } from "@/store/functions";
...
import {
    ...
    PayloadAddAccountDeposit,
    ...
    PayloadUpdateAccountDeposit,
} from "@/store/account-types";
...
export const accountActions: StoreActionTree = {
    ...
    [ACTION_ADD_ACCOUNT_DEPOSIT](this, { commit }, payload: PayloadAddAccountDeposit) {
        commit(MUTATION_ADD_ACCOUNT_DEPOSIT, payload);
    },
    ...
    [ACTION_UPDATE_ACCOUNT_DEPOSIT](this, { commit }, payload: PayloadUpdateAccountDeposit) {
        commit(MUTATION_UPDATE_ACCOUNT_DEPOSIT, payload);
    },
};
...
export const accountMutations: StoreMutationTree = {
    ...
    [MUTATION_ADD_ACCOUNT_DEPOSIT](storeState, payload: PayloadAddAccountDeposit) {
        add(storeState[STATE_ACCOUNTS_DEPOSITS], payload, (x) => moment(x.date).valueOf(), { descending: true });
    },
    ...
    [MUTATION_UPDATE_ACCOUNT_DEPOSIT](storeState, payload: PayloadUpdateAccountDeposit) {
        const state = storeState[STATE_ACCOUNTS_DEPOSITS];
        const deposit = findById(state, payload.id)!;

        storeActionValidator
            .begin()
            .while(StoreActions.Updating)
            .throwIf(deposit)
            .isUndefined(undefinedMessage("deposit", payload.id, state.index));

        deposit.accountId = payload.accountId;
        deposit.amount = payload.amount;
        deposit.date = payload.date;

        state.items = sort(state.items, (x) => moment(x.date).valueOf(), { descending: true });
    },
};
...
src ⟩ store ⟩ account-module.ts

AccountsDeposit.vue (15:37)

<script lang="ts">
...
import {
    ...
    ACTION_ADD_ACCOUNT_DEPOSIT,
    ...
    ACTION_UPDATE_ACCOUNT_DEPOSIT,
    ...
    PayloadAddAccountDeposit,
    ...
    PayloadUpdateAccountDeposit,
} from "@/store";
...
export default class AccountsDeposit extends Vue {
    @Action(ACTION_ADD_ACCOUNT_DEPOSIT) private readonly addDeposit!: ActionFn<PayloadAddAccountDeposit>;
    ...
    @Action(ACTION_UPDATE_ACCOUNT_DEPOSIT) private readonly updateDeposit!: ActionFn<PayloadUpdateAccountDeposit>;
    ...
    private save() {
        const deposit = new AccountDepositModel({
            accountId: this.accountId,
            amount: this.amount,
            date: moment(this.date).toDate(),
            id: this.id,
        });
        switch (this.id) {
            case 0:
                this.addDeposit(deposit);
                return;
            default:
                this.updateDeposit(deposit);
                return;
        }
    }
}
</script>
src ⟩ views ⟩ AccountsDeposit.vue

AccountsDeposit.vue (19:56)

<script lang="ts">
...
import {
    ...
    AccountModel,
    ...
    GETTER_ACCOUNTS,
    ...
} from "@/store";
...
export default class AccountsDeposit extends Vue {
    ...
    @Getter(GETTER_ACCOUNTS) private readonly accounts!: AccountModel[];
    ...
    private accountName = "";
    ...
    private transfer = false;
    ...
    private load(deposit: AccountDepositModel) {
        ...
        this.accountName = deposit.account.name;
        ...
    }
    ...
    private toggleTransfer() {
        this.transfer = !this.transfer;
    }
}
</script>
src ⟩ views ⟩ AccountsDeposit.vue

AccountsDeposit.vue (21:55)

<template lang="pug">
form
    div.inputs
        h1 Deposit
        label
            span Account 
            small
                a(v-on:click.prevent="toggleTransfer")
                    span(v-if="!transfer") Transfer
                    span(v-if="transfer") Cancel
       div.transfer-inputs
            div.transfer-from
                label(v-if="transfer") From
                div.input {{accountName}}
            div.transfer-to(v-if="transfer")
                label To
                select(
                    v-model="accountId")
                    option(
                        v-for="account in accounts"
                        v-bind:key="account.id"
                        v-bind:value="account.id") {{account.name}}
        ...
    ...
</template>
src ⟩ views ⟩ AccountsDeposit.vue

AccountsDeposit.vue (26:15)

<style lang="sass" scoped>
...
.transfer-inputs
    display: flex

    > * 
        flex: 1

    .transfer-from
        margin-right: 0.125rem

    .transfer-to
        margin-left: 0.125rem
</style>
src ⟩ views ⟩ AccountsDeposit.vue

Exciton Interactive LLC
Advertisement