Advertisement

#42 Adding And Updating Portfolio Plans In The Vuex Store

In this article we will go through the process of updating our vuex store so that we will have the ability to add and update portfolio plans. The will give us the ability to add new plans as well as modify the name and category percentages of existing plans.

Code Snippets

store-types.ts (1:40)

...
import { IPortfolioGetters, IPortfolioState } from "@/store/portfolio-types";
...
export interface IStoreGetters extends IAccountGetters, IPortfolioGetters, ISecurityGetters {}
...
src ⟩ store ⟩ store-types.ts

portfolio-module.ts (2:30)

import {
    ...
    GETTER_SECURITY_CATEGORIES,
    ...
} from "@/store/store-constants";
...
export const portfolioGetters: StoreGetterTree = {
    [GETTER_PORTFOLIO_CATEGORIES]: (storeState, getters: IPortfolioGetters) => {
        return (planId: number) => {
            if (planId <= 0) {
                return getters[GETTER_SECURITY_CATEGORIES].map((x) => {
                    const portfolioCategory = new PortfolioCategoryModel({
                        categoryId: x.id,
                        id: x.id,
                        percent: 0,
                        planId: 0,
                    });
                    portfolioCategory.setCategory(x);
                    return portfolioCategory;
                });
            }
            ...
        };
    },
    ...
};
...
src ⟩ store ⟩ portfolio-module.ts

PlannerDetails.vue (6:00)

<script lang="ts">
...
import {
    ...
    GETTER_PORTFOLIO_CATEGORIES,
    ...
    GetterPortfolioCategories,
    ...
    PortfolioPlanModel,
} from "@/store";
...
export default class PlannerDetails extends Vue {
    ...
    @Getter(GETTER_PORTFOLIO_CATEGORIES) private readonly getterPortfolioCategories!: GetterPortfolioCategories;
    ...
    private mounted() {
        ...
        if (this.id <= 0) {
            this.portfolioCategories = this.getterPortfolioCategories(0);
            return;
        }
        ...
    }

    private save() {
        const plan = new PortfolioPlanModel({
            categories: this.portfolioCategories,
            id: this.id,
            name: this.name,
        });
        console.log(plan);
    }
}
</script>
src ⟩ views ⟩ PlannerDetails.vue

store-constants.ts (8:16)

...
export const ACTION_ADD_PORTFOLIO_PLAN = "ACTION_ADD_PORTFOLIO_PLAN";
...
export const ACTION_UPDATE_PORTFOLIO_PLAN = "ACTION_UPDATE_PORTFOLIO_PLAN";
...
export const MUTATION_ADD_PORTFOLIO_PLAN = "MUTATION_ADD_PORTFOLIO_PLAN";
...
export const MUTATION_UPDATE_PORTFOLIO_PLAN = "MUTATION_UPDATE_PORTFOLIO_PLAN";
...
src ⟩ store ⟩ store-constants.ts

portfolio-types.ts (9:09)

...
export type PayloadAddPortfolioPlan = PortfolioPlanModel;
export type PayloadUpdatePortfolioPlan = PortfolioPlanModel;
...
src ⟩ store ⟩ portfolio-types.ts

Advertisement

functions.ts (9:36)

...
export function add<T extends IStateItem>(...) {
    ...
    return item;
}
...
src ⟩ store ⟩ functions.ts

portfolio-module.ts (10:12)

import {
    ACTION_ADD_PORTFOLIO_PLAN,
    ACTION_UPDATE_PORTFOLIO_PLAN,
    ...
    MUTATION_ADD_PORTFOLIO_PLAN,
    MUTATION_UPDATE_PORTFOLIO_PLAN,
    ...
} from "@/store/store-constants";

import { add, ... } from "@/store/functions";
...
import { PortfolioPlanModel } from "@/store/portfolio-plan-model";
import {
    ...
    PayloadAddPortfolioPlan,
    PayloadUpdatePortfolioPlan,
} from "@/store/portfolio-types";
...
import { ..., StoreActionTree, ..., StoreMutationTree } from "@/store/store-types";
...
function totalPercent(plan: PortfolioPlanModel) {
    const total = plan.categories.reduce((pre, cur) => pre + cur.percent, 0);
    if (total !== 100) {
        console.log(`The total percent of the plan must be 100% but the current percent is '${total}%'`);
        return true;
    }
    return false;
}
...
export const portfolioActions: StoreActionTree = {
    [ACTION_ADD_PORTFOLIO_PLAN](this, { commit }, payload: PayloadAddPortfolioPlan) {
        if (totalPercent(payload)) {
            return;
        }
        commit(MUTATION_ADD_PORTFOLIO_PLAN, payload);
    },
    [ACTION_UPDATE_PORTFOLIO_PLAN](this, { commit }, payload: PayloadUpdatePortfolioPlan) {
        if (totalPercent(payload)) {
            return;
        }
        commit(MUTATION_UPDATE_PORTFOLIO_PLAN, payload);
    },
};
...
export const portfolioMutations: StoreMutationTree = {
    [MUTATION_ADD_PORTFOLIO_PLAN](storeState, payload: PayloadAddPortfolioPlan) {
        const plan = add(storeState[STATE_PORTFOLIO_PLANS], payload, (x) => x.name);
        payload.categories.forEach((x) => {
            x.planId = plan.id;
            add(storeState[STATE_PORTFOLIO_CATEGORIES], x);
        });
    },
    [MUTATION_UPDATE_PORTFOLIO_PLAN](storeState, payload: PayloadUpdatePortfolioPlan) {
        const portfolioCategories = storeState[STATE_PORTFOLIO_CATEGORIES].items.filter((x) => x.planId === payload.id);
        const portfolioPlanState = storeState[STATE_PORTFOLIO_PLANS];
        const plan = findById(portfolioPlanState, payload.id)!;

        storeActionValidator
            .begin()
            .while(StoreActions.Updating)
            .throwIf(plan)
            .isUndefined(undefinedMessage("portfolio plan", payload.id, portfolioPlanState.index));

        plan.name = payload.name;

        portfolioCategories.forEach((x) => {
            const category = plan.categories.find((y) => y.id === x.id)!;
            category.percent = x.percent;
        });
    },
};
src ⟩ store ⟩ portfolio-module.ts

store.ts (19:11)

...
import { portfolioActions, ..., portfolioMutations, ... } from "@/store/portfolio-module";
...
const actions = {
    ...accountActions,
    ...portfolioActions,
    ...routeActions,
    ...securitiesActions,
};
...
const mutations = {
    ...accountMutations,
    ...portfolioMutations,
    ...routeMutations,
    ...securitiesMutations,
};
...
src ⟩ store ⟩ store.ts

PlannerDetails.vue (19:42)

<script lang="ts">
...
import {
    ACTION_ADD_PORTFOLIO_PLAN,
    ACTION_UPDATE_PORTFOLIO_PLAN,
    ...
    PayloadAddPortfolioPlan,
    PayloadUpdatePortfolioPlan,
    ...
} from "@/store";
...
export default class PlannerDetails extends Vue {
    @Action(ACTION_ADD_PORTFOLIO_PLAN) private readonly addPlan!: ActionFn<PayloadAddPortfolioPlan>;
    ...
    @Action(ACTION_UPDATE_PORTFOLIO_PLAN) private readonly updatePlan!: ActionFn<PayloadUpdatePortfolioPlan>;
    ...
    private save() {
        ...
        switch (this.id) {
            case 0:
                this.addPlan(plan);
                break;
            default:
                this.updatePlan(plan);
                break;
        }
    }
}
</script>
src ⟩ views ⟩ PlannerDetails.vue

Exciton Interactive LLC
Advertisement