Remove vuex
This commit is contained in:
parent
8b0e368d58
commit
c693625e34
@ -14,8 +14,7 @@
|
|||||||
"postcss": "^8.4.6",
|
"postcss": "^8.4.6",
|
||||||
"tailwindcss": "^3.0.18",
|
"tailwindcss": "^3.0.18",
|
||||||
"vue": "^3.2.25",
|
"vue": "^3.2.25",
|
||||||
"vue-router": "^4.0.12",
|
"vue-router": "^4.0.12"
|
||||||
"vuex": "^4.0.2"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^2.0.0",
|
"@vitejs/plugin-vue": "^2.0.0",
|
||||||
|
9
web/src/@types/shims-vuex.d.ts
vendored
9
web/src/@types/shims-vuex.d.ts
vendored
@ -1,9 +0,0 @@
|
|||||||
import { ComponentCustomProperties } from 'vue'
|
|
||||||
import { State } from '../store'
|
|
||||||
import { Store } from 'vuex'
|
|
||||||
|
|
||||||
declare module '@vue/runtime-core' {
|
|
||||||
interface ComponentCustomProperties {
|
|
||||||
$store: Store<State>
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +1,28 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { mapState } from "pinia";
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
import { LOGOUT } from "./store/mutation-types";
|
import { LOGOUT } from "./store/mutation-types";
|
||||||
import { useBudgetsStore } from "./stores/budgets";
|
import { useBudgetsStore } from "./stores/budget";
|
||||||
|
import { useAccountStore } from "./stores/budget-account";
|
||||||
import { useSessionStore } from "./stores/session";
|
import { useSessionStore } from "./stores/session";
|
||||||
import { useSettingsStore } from "./stores/settings";
|
import { useSettingsStore } from "./stores/settings";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
computed: {
|
computed: {
|
||||||
loggedIn() {
|
...mapState(useBudgetsStore, ["CurrentBudgetName"]),
|
||||||
return this.$store.state.Session.Token;
|
...mapState(useSettingsStore, ["ExpandMenu", "ShowMenu"]),
|
||||||
}
|
...mapState(useSessionStore, ["LoggedIn"]),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
logout() {
|
logout() {
|
||||||
this.$store.commit(LOGOUT);
|
useSessionStore().logout();
|
||||||
this.$router.push("/login")
|
this.$router.push("/login")
|
||||||
},
|
},
|
||||||
toggleMenu() {
|
toggleMenu() {
|
||||||
this.$store.commit("toggleMenu");
|
useSettingsStore().toggleMenu();
|
||||||
},
|
},
|
||||||
toggleMenuSize() {
|
toggleMenuSize() {
|
||||||
this.$store.commit("toggleMenuSize");
|
useSettingsStore().toggleMenuSize();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeCreate() {
|
beforeCreate() {
|
||||||
@ -36,12 +38,19 @@ export default defineComponent({
|
|||||||
sessionStore.User = restoredState.Session.User;
|
sessionStore.User = restoredState.Session.User;
|
||||||
sessionStore.Token = restoredState.Session.Token;
|
sessionStore.Token = restoredState.Session.Token;
|
||||||
for (const budget of restoredState.Budgets || []) {
|
for (const budget of restoredState.Budgets || []) {
|
||||||
|
console.log("UIAE", sessionStore.Budgets, budget)
|
||||||
sessionStore.Budgets.set(budget[0], budget[1]);
|
sessionStore.Budgets.set(budget[0], budget[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const budgetsStore = useBudgetsStore();
|
const budgetsStore = useBudgetsStore();
|
||||||
budgetsStore.CurrentBudgetID = restoredState.CurrentBudgetID;
|
budgetsStore.CurrentBudgetID = restoredState.CurrentBudgetID;
|
||||||
|
|
||||||
|
const accountStore = useAccountStore();
|
||||||
|
accountStore.CurrentAccountID = restoredState.CurrentAccountID;
|
||||||
|
for (const account of restoredState.Accounts || []) {
|
||||||
|
accountStore.Accounts.set(account[0], account[1]);
|
||||||
|
}
|
||||||
|
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
settingsStore.ShowMenu = restoredState.ShowMenu;
|
settingsStore.ShowMenu = restoredState.ShowMenu;
|
||||||
settingsStore.ExpandMenu = restoredState.ExpandMenu;
|
settingsStore.ExpandMenu = restoredState.ExpandMenu;
|
||||||
@ -55,18 +64,18 @@ export default defineComponent({
|
|||||||
<span class="flex-1 font-bold text-5xl -my-3 hidden md:inline" @click="toggleMenuSize">≡</span>
|
<span class="flex-1 font-bold text-5xl -my-3 hidden md:inline" @click="toggleMenuSize">≡</span>
|
||||||
<span class="flex-1 font-bold text-5xl -my-3 md:hidden" @click="toggleMenu">≡</span>
|
<span class="flex-1 font-bold text-5xl -my-3 md:hidden" @click="toggleMenu">≡</span>
|
||||||
|
|
||||||
<span class="flex-1">{{ $store.getters.CurrentBudgetName }}</span>
|
<span class="flex-1">{{ CurrentBudgetName }}</span>
|
||||||
|
|
||||||
<div class="flex flex-1 flex-row justify-end -mx-4">
|
<div class="flex flex-1 flex-row justify-end -mx-4">
|
||||||
<router-link class="mx-4" v-if="loggedIn" to="/dashboard">Dashboard</router-link>
|
<router-link class="mx-4" v-if="LoggedIn" to="/dashboard">Dashboard</router-link>
|
||||||
<router-link class="mx-4" v-if="!loggedIn" to="/login">Login</router-link>
|
<router-link class="mx-4" v-if="!LoggedIn" to="/login">Login</router-link>
|
||||||
<a class="mx-4" v-if="loggedIn" @click="logout">Logout</a>
|
<a class="mx-4" v-if="LoggedIn" @click="logout">Logout</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col md:flex-row flex-1">
|
<div class="flex flex-col md:flex-row flex-1">
|
||||||
<div
|
<div
|
||||||
:class="[$store.state.ExpandMenu ? 'md:w-72' : 'md:w-36', $store.state.ShowMenu ? '' : 'hidden']"
|
:class="[ExpandMenu ? 'md:w-72' : 'md:w-36', ShowMenu ? '' : 'hidden']"
|
||||||
class="md:block flex-shrink-0 w-full"
|
class="md:block flex-shrink-0 w-full"
|
||||||
>
|
>
|
||||||
<router-view name="sidebar"></router-view>
|
<router-view name="sidebar"></router-view>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Card from '../components/Card.vue';
|
import Card from '../components/Card.vue';
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
import { NEW_BUDGET } from "../store/action-types";
|
import { useBudgetsStore } from '../stores/budget';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
@ -13,7 +13,7 @@ export default defineComponent({
|
|||||||
components: { Card },
|
components: { Card },
|
||||||
methods: {
|
methods: {
|
||||||
saveBudget() {
|
saveBudget() {
|
||||||
this.$store.dispatch(NEW_BUDGET, this.$data.budgetName);
|
useBudgetsStore().NewBudget(this.$data.budgetName);
|
||||||
this.$data.dialog = false;
|
this.$data.dialog = false;
|
||||||
},
|
},
|
||||||
newBudget() {
|
newBudget() {
|
||||||
|
@ -3,7 +3,10 @@ import App from './App.vue'
|
|||||||
import './index.css'
|
import './index.css'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
import { SET_CURRENT_ACCOUNT, SET_CURRENT_BUDGET } from './store/action-types'
|
import { useBudgetsStore } from './stores/budget';
|
||||||
|
import { useSessionStore } from './stores/session';
|
||||||
|
import { useSettingsStore } from './stores/settings';
|
||||||
|
import { useAccountStore } from './stores/budget-account'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
app.use(router)
|
app.use(router)
|
||||||
@ -11,10 +14,30 @@ app.use(createPinia())
|
|||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|
||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from, next) => {
|
||||||
await store.dispatch(SET_CURRENT_BUDGET, to.params.budgetid);
|
const budgetStore = useBudgetsStore();
|
||||||
await store.dispatch(SET_CURRENT_ACCOUNT, {
|
await budgetStore.SetCurrentBudget((<string>to.params.budgetid));
|
||||||
accountid: to.params.accountid,
|
|
||||||
budgetid: to.params.budgetid
|
const accountStore = useAccountStore();
|
||||||
});
|
await accountStore.SetCurrentAccount((<string>to.params.budgetid), (<string>to.params.accountid));
|
||||||
next();
|
next();
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function saveStateToLocalStorage() {
|
||||||
|
const sessionStore = useSessionStore();
|
||||||
|
const budgetStore = useBudgetsStore();
|
||||||
|
const accountStore = useAccountStore();
|
||||||
|
const settingsStore = useSettingsStore();
|
||||||
|
let persistedState = {
|
||||||
|
Session: sessionStore,
|
||||||
|
CurrentBudgetID: budgetStore.CurrentBudgetID,
|
||||||
|
CurrentAccountID: accountStore.CurrentAccountID,
|
||||||
|
ExpandMenu: settingsStore.ExpandMenu,
|
||||||
|
ShowMenu: settingsStore.ShowMenu
|
||||||
|
}
|
||||||
|
localStorage.setItem('store', JSON.stringify(persistedState));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
useSettingsStore().$subscribe(() => saveStateToLocalStorage);
|
||||||
|
useBudgetsStore().$subscribe(() => saveStateToLocalStorage);
|
||||||
|
useSessionStore().$subscribe(() => saveStateToLocalStorage);
|
@ -1,8 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { mapState } from "pinia";
|
||||||
import { defineComponent } from "vue"
|
import { defineComponent } from "vue"
|
||||||
import Autocomplete, { Suggestion } from '../components/Autocomplete.vue'
|
import Autocomplete, { Suggestion } from '../components/Autocomplete.vue'
|
||||||
import Currency from "../components/Currency.vue";
|
import Currency from "../components/Currency.vue";
|
||||||
import TransactionRow from "../components/TransactionRow.vue";
|
import TransactionRow from "../components/TransactionRow.vue";
|
||||||
|
import { useAccountStore } from "../stores/budget-account";
|
||||||
|
import { useSessionStore } from "../stores/session";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
@ -16,6 +19,9 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
components: { Autocomplete, Currency, TransactionRow },
|
components: { Autocomplete, Currency, TransactionRow },
|
||||||
props: ["budgetid", "accountid"],
|
props: ["budgetid", "accountid"],
|
||||||
|
computed: {
|
||||||
|
...mapState(useAccountStore, ["CurrentAccount", "TransactionsList"]),
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
saveTransaction(e : MouseEvent) {
|
saveTransaction(e : MouseEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -31,7 +37,7 @@ export default defineComponent({
|
|||||||
amount: this.$data.Amount,
|
amount: this.$data.Amount,
|
||||||
state: "Uncleared"
|
state: "Uncleared"
|
||||||
}),
|
}),
|
||||||
headers: this.$store.getters.AuthHeaders,
|
headers: useSessionStore().AuthHeaders,
|
||||||
})
|
})
|
||||||
.then(x => x.json());
|
.then(x => x.json());
|
||||||
},
|
},
|
||||||
@ -40,10 +46,10 @@ export default defineComponent({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<h1>{{ $store.getters.CurrentAccount.Name }}</h1>
|
<h1>{{ CurrentAccount?.Name }}</h1>
|
||||||
<p>
|
<p>
|
||||||
Current Balance:
|
Current Balance:
|
||||||
<Currency :value="$store.getters.CurrentAccount.Balance" />
|
<Currency :value="CurrentAccount?.Balance" />
|
||||||
</p>
|
</p>
|
||||||
<table>
|
<table>
|
||||||
<tr class="font-bold">
|
<tr class="font-bold">
|
||||||
@ -76,7 +82,7 @@ export default defineComponent({
|
|||||||
</td>
|
</td>
|
||||||
<td style="width: 20px;"></td>
|
<td style="width: 20px;"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<TransactionRow v-for="(transaction, index) in $store.getters.Transactions"
|
<TransactionRow v-for="(transaction, index) in TransactionsList"
|
||||||
:transaction="transaction"
|
:transaction="transaction"
|
||||||
:index="index" />
|
:index="index" />
|
||||||
</table>
|
</table>
|
||||||
|
@ -1,10 +1,19 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { mapState } from "pinia"
|
||||||
import { defineComponent } from "vue"
|
import { defineComponent } from "vue"
|
||||||
import Currency from "../components/Currency.vue"
|
import Currency from "../components/Currency.vue"
|
||||||
|
import { useBudgetsStore } from "../stores/budget"
|
||||||
|
import { useAccountStore } from "../stores/budget-account"
|
||||||
|
import { useSettingsStore } from "../stores/settings"
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: ["budgetid", "accountid"],
|
props: ["budgetid", "accountid"],
|
||||||
components: { Currency }
|
components: { Currency },
|
||||||
|
computed: {
|
||||||
|
...mapState(useSettingsStore, ["ExpandMenu"]),
|
||||||
|
...mapState(useBudgetsStore, ["CurrentBudgetName", "CurrentBudgetID"]),
|
||||||
|
...mapState(useAccountStore, ["OnBudgetAccounts", "OnBudgetAccountsBalance", "OffBudgetAccounts", "OffBudgetAccountsBalance"])
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -12,44 +21,44 @@ export default defineComponent({
|
|||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<span class="m-1 p-1 px-3 text-xl">
|
<span class="m-1 p-1 px-3 text-xl">
|
||||||
<router-link to="/dashboard">⌂</router-link>
|
<router-link to="/dashboard">⌂</router-link>
|
||||||
{{$store.getters.CurrentBudgetName}}
|
{{CurrentBudgetName}}
|
||||||
</span>
|
</span>
|
||||||
<span class="bg-orange-200 rounded-lg m-1 p-1 px-3 flex flex-col">
|
<span class="bg-orange-200 rounded-lg m-1 p-1 px-3 flex flex-col">
|
||||||
<router-link :to="'/budget/'+budgetid+'/budgeting'">Budget</router-link><br />
|
<router-link :to="'/budget/'+budgetid+'/budgeting'">Budget</router-link><br />
|
||||||
<!--<router-link :to="'/budget/'+$store.getters.CurrentBudgetID+'/reports'">Reports</router-link>-->
|
<!--<router-link :to="'/budget/'+CurrentBudgetID+'/reports'">Reports</router-link>-->
|
||||||
<!--<router-link :to="'/budget/'+$store.getters.CurrentBudgetID+'/all-accounts'">All Accounts</router-link>-->
|
<!--<router-link :to="'/budget/'+CurrentBudgetID+'/all-accounts'">All Accounts</router-link>-->
|
||||||
</span>
|
</span>
|
||||||
<li class="bg-orange-200 rounded-lg m-1 p-1 px-3">
|
<li class="bg-orange-200 rounded-lg m-1 p-1 px-3">
|
||||||
<div class="flex flex-row justify-between font-bold">
|
<div class="flex flex-row justify-between font-bold">
|
||||||
<span>On-Budget Accounts</span>
|
<span>On-Budget Accounts</span>
|
||||||
<Currency :class="$store.state.ExpandMenu?'md:inline':'md:hidden'" :value="$store.getters.OnBudgetAccountsBalance" />
|
<Currency :class="ExpandMenu?'md:inline':'md:hidden'" :value="OnBudgetAccountsBalance" />
|
||||||
</div>
|
</div>
|
||||||
<div v-for="account in $store.getters.OnBudgetAccounts" class="flex flex-row justify-between">
|
<div v-for="account in OnBudgetAccounts" class="flex flex-row justify-between">
|
||||||
<router-link :to="'/budget/'+budgetid+'/account/'+account.ID">{{account.Name}}</router-link>
|
<router-link :to="'/budget/'+budgetid+'/account/'+account.ID">{{account.Name}}</router-link>
|
||||||
<Currency :class="$store.state.ExpandMenu?'md:inline':'md:hidden'" :value="account.Balance" />
|
<Currency :class="ExpandMenu?'md:inline':'md:hidden'" :value="account.Balance" />
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="bg-red-200 rounded-lg m-1 p-1 px-3">
|
<li class="bg-red-200 rounded-lg m-1 p-1 px-3">
|
||||||
<div class="flex flex-row justify-between font-bold">
|
<div class="flex flex-row justify-between font-bold">
|
||||||
<span>Off-Budget Accounts</span>
|
<span>Off-Budget Accounts</span>
|
||||||
<Currency :class="$store.state.ExpandMenu?'md:inline':'md:hidden'" :value="$store.getters.OffBudgetAccountsBalance" />
|
<Currency :class="ExpandMenu?'md:inline':'md:hidden'" :value="OffBudgetAccountsBalance" />
|
||||||
</div>
|
</div>
|
||||||
<div v-for="account in $store.getters.OffBudgetAccounts" class="flex flex-row justify-between">
|
<div v-for="account in OffBudgetAccounts" class="flex flex-row justify-between">
|
||||||
<router-link :to="'/budget/'+budgetid+'/account/'+account.ID">{{account.Name}}</router-link>
|
<router-link :to="'/budget/'+budgetid+'/account/'+account.ID">{{account.Name}}</router-link>
|
||||||
<Currency :class="$store.state.ExpandMenu?'md:inline':'md:hidden'" :value="account.Balance" />
|
<Currency :class="ExpandMenu?'md:inline':'md:hidden'" :value="account.Balance" />
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="bg-red-200 rounded-lg m-1 p-1 px-3">
|
<li class="bg-red-200 rounded-lg m-1 p-1 px-3">
|
||||||
Closed Accounts
|
Closed Accounts
|
||||||
</li>
|
</li>
|
||||||
<!--<li>
|
<!--<li>
|
||||||
<router-link :to="'/budget/'+$store.getters.CurrentBudgetID+'/accounts'">Edit accounts</router-link>
|
<router-link :to="'/budget/'+CurrentBudgetID+'/accounts'">Edit accounts</router-link>
|
||||||
</li>-->
|
</li>-->
|
||||||
<li class="bg-red-200 rounded-lg m-1 p-1 px-3">
|
<li class="bg-red-200 rounded-lg m-1 p-1 px-3">
|
||||||
+ Add Account
|
+ Add Account
|
||||||
</li>
|
</li>
|
||||||
<li class="bg-red-200 rounded-lg m-1 p-1 px-3">
|
<li class="bg-red-200 rounded-lg m-1 p-1 px-3">
|
||||||
<router-link :to="'/budget/'+$store.getters.CurrentBudgetID+'/settings'">Budget-Settings</router-link>
|
<router-link :to="'/budget/'+CurrentBudgetID+'/settings'">Budget-Settings</router-link>
|
||||||
</li>
|
</li>
|
||||||
<!--<li><router-link to="/admin">Admin</router-link></li>-->
|
<!--<li><router-link to="/admin">Admin</router-link></li>-->
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,51 +1,53 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { mapState } from "pinia";
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
import { FETCH_MONTH_BUDGET } from "../store/action-types";
|
|
||||||
import { TITLE } from "../store/mutation-types";
|
|
||||||
import Currency from "../components/Currency.vue";
|
import Currency from "../components/Currency.vue";
|
||||||
|
import { useBudgetsStore } from "../stores/budget";
|
||||||
|
import { Category, useAccountStore } from "../stores/budget-account";
|
||||||
|
|
||||||
interface Date {
|
interface Date {
|
||||||
Year:Number,
|
Year: Number,
|
||||||
Month:Number,
|
Month: Number,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$store.commit(TITLE, "Budget for " + this.month + " " + this.year);
|
document.title = "Budgeteer - Budget for " + this.month + " " + this.year;
|
||||||
return this.$store.dispatch(FETCH_MONTH_BUDGET, { budgetid: this.budgetid, year: this.year, month: this.month });
|
return useAccountStore().FetchMonthBudget(this.budgetid, this.year, this.month);
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
year() {
|
year() {
|
||||||
return this.$store.dispatch(FETCH_MONTH_BUDGET, { budgetid: this.budgetid, year: this.year, month: this.month });
|
return useAccountStore().FetchMonthBudget(this.budgetid, this.year, this.month);
|
||||||
},
|
},
|
||||||
month() {
|
month() {
|
||||||
return this.$store.dispatch(FETCH_MONTH_BUDGET, { budgetid: this.budgetid, year: this.year, month: this.month });
|
return useAccountStore().FetchMonthBudget(this.budgetid, this.year, this.month);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
props: ["budgetid", "year", "month"],
|
props: ["budgetid", "year", "month"],
|
||||||
computed: {
|
computed: {
|
||||||
Categories() {
|
...mapState(useBudgetsStore, ["CurrentBudgetID"]),
|
||||||
return this.$store.getters.Categories(this.year, this.month);
|
Categories() : IterableIterator<Category> | undefined {
|
||||||
|
return useAccountStore().Categories(this.year, this.month);
|
||||||
},
|
},
|
||||||
previous() : Date {
|
previous(): Date {
|
||||||
return {
|
return {
|
||||||
Year: new Date(this.year, this.month - 1, 1).getFullYear(),
|
Year: new Date(this.year, this.month - 1, 1).getFullYear(),
|
||||||
Month: new Date(this.year, this.month - 1, 1).getMonth(),
|
Month: new Date(this.year, this.month - 1, 1).getMonth(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
current() : Date {
|
current(): Date {
|
||||||
return {
|
return {
|
||||||
Year: new Date().getFullYear(),
|
Year: new Date().getFullYear(),
|
||||||
Month: new Date().getMonth(),
|
Month: new Date().getMonth(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
selected() : Date {
|
selected(): Date {
|
||||||
return {
|
return {
|
||||||
Year: this.year,
|
Year: this.year,
|
||||||
Month: Number(this.month) + 1
|
Month: Number(this.month) + 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
next() : Date {
|
next(): Date {
|
||||||
return {
|
return {
|
||||||
Year: new Date(this.year, Number(this.month) + 1, 1).getFullYear(),
|
Year: new Date(this.year, Number(this.month) + 1, 1).getFullYear(),
|
||||||
Month: new Date(this.year, Number(this.month) + 1, 1).getMonth(),
|
Month: new Date(this.year, Number(this.month) + 1, 1).getMonth(),
|
||||||
@ -61,13 +63,17 @@ export default defineComponent({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<h1>
|
<h1>Budget for {{ selected.Month }}/{{ selected.Year }}</h1>
|
||||||
Budget for {{selected.Month}}/{{selected.Year}}
|
|
||||||
</h1>
|
|
||||||
<div>
|
<div>
|
||||||
<router-link :to="'/budget/'+$store.getters.CurrentBudget.ID +'/budgeting/' + previous.Year + '/' + previous.Month">Previous Month</router-link> -
|
<router-link
|
||||||
<router-link :to="'/budget/'+$store.getters.CurrentBudget.ID +'/budgeting/' + current.Year + '/' + current.Month">Current Month</router-link> -
|
:to="'/budget/' + CurrentBudgetID + '/budgeting/' + previous.Year + '/' + previous.Month"
|
||||||
<router-link :to="'/budget/'+$store.getters.CurrentBudget.ID +'/budgeting/' + next.Year + '/' + next.Month">Next Month</router-link>
|
>Previous Month</router-link>-
|
||||||
|
<router-link
|
||||||
|
:to="'/budget/' + CurrentBudgetID + '/budgeting/' + current.Year + '/' + current.Month"
|
||||||
|
>Current Month</router-link>-
|
||||||
|
<router-link
|
||||||
|
:to="'/budget/' + CurrentBudgetID + '/budgeting/' + next.Year + '/' + next.Month"
|
||||||
|
>Next Month</router-link>
|
||||||
</div>
|
</div>
|
||||||
<table class="container col-lg-12" id="content">
|
<table class="container col-lg-12" id="content">
|
||||||
<tr>
|
<tr>
|
||||||
@ -81,14 +87,22 @@ export default defineComponent({
|
|||||||
<th>Available</th>
|
<th>Available</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-for="category in Categories">
|
<tr v-for="category in Categories">
|
||||||
<td>{{category.Group}}</td>
|
<td>{{ category.Group }}</td>
|
||||||
<td>{{category.Name}}</td>
|
<td>{{ category.Name }}</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td class="text-right"><Currency :value="category.AvailableLastMonth" /></td>
|
<td class="text-right">
|
||||||
<td class="text-right"><Currency :value="category.Assigned" /></td>
|
<Currency :value="category.AvailableLastMonth" />
|
||||||
<td class="text-right"><Currency :value="category.Activity" /></td>
|
</td>
|
||||||
<td class="text-right"><Currency :value="category.Available" /></td>
|
<td class="text-right">
|
||||||
|
<Currency :value="category.Assigned" />
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<Currency :value="category.Activity" />
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<Currency :value="category.Available" />
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</template>
|
</template>
|
@ -2,17 +2,22 @@
|
|||||||
import NewBudget from '../dialogs/NewBudget.vue';
|
import NewBudget from '../dialogs/NewBudget.vue';
|
||||||
import Card from '../components/Card.vue';
|
import Card from '../components/Card.vue';
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
import { mapState } from 'pinia';
|
||||||
|
import { useSessionStore } from '../stores/session';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: ["budgetid"],
|
props: ["budgetid"],
|
||||||
components: { NewBudget, Card }
|
components: { NewBudget, Card },
|
||||||
|
computed: {
|
||||||
|
...mapState(useSessionStore, ["BudgetsList"]),
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<h1>Budgets</h1>
|
<h1>Budgets</h1>
|
||||||
<div class="grid md:grid-cols-2 gap-6">
|
<div class="grid md:grid-cols-2 gap-6">
|
||||||
<Card v-for="budget in $store.getters.Budgets">
|
<Card v-for="budget in BudgetsList">
|
||||||
<router-link v-bind:to="'/budget/'+budget.ID+'/budgeting'" class="contents">
|
<router-link v-bind:to="'/budget/'+budget.ID+'/budgeting'" class="contents">
|
||||||
<!--<svg class="w-24"></svg>-->
|
<!--<svg class="w-24"></svg>-->
|
||||||
<p class="w-24 text-center text-6xl"></p>
|
<p class="w-24 text-center text-6xl"></p>
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { TITLE } from "../store/mutation-types";
|
|
||||||
import { LOGIN } from '../store/action-types'
|
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
|
import { useSessionStore } from "../stores/session";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
@ -15,12 +14,12 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$store.commit(TITLE, "Login");
|
document.title = "Budgeteer - Login";
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
formSubmit(e : MouseEvent) {
|
formSubmit(e : MouseEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.$store.dispatch(LOGIN, this.$data.login)
|
useSessionStore().login(this.$data.login)
|
||||||
.then(x => {
|
.then(x => {
|
||||||
this.$data.error = "";
|
this.$data.error = "";
|
||||||
this.$router.replace("/dashboard");
|
this.$router.replace("/dashboard");
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { REGISTER } from "../store/action-types";
|
import { useSessionStore } from '../stores/session';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
@ -15,11 +15,11 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
formSubmit (e) {
|
formSubmit (e : FormDataEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.$store.dispatch(REGISTER, this.$data.login)
|
useSessionStore().register(this.$data.login)
|
||||||
.then(() => this.$data.error = "")
|
.then(() => this.$data.error = "")
|
||||||
.catch(() => this.$data.error = ["Something went wrong!"]);
|
.catch(() => this.$data.error = "Something went wrong!");
|
||||||
|
|
||||||
// TODO display invalidCredentials
|
// TODO display invalidCredentials
|
||||||
// TODO redirect to dashboard on success
|
// TODO redirect to dashboard on success
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from "vue"
|
import { defineComponent } from "vue"
|
||||||
import { IMPORT_YNAB } from "../store/action-types";
|
import { useAPI } from "../stores/api";
|
||||||
import { TITLE } from "../store/mutation-types"
|
import { useBudgetsStore } from "../stores/budget";
|
||||||
|
import { useSessionStore } from "../stores/session";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
data() {
|
data() {
|
||||||
@ -16,7 +17,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$store.commit(TITLE, "Settings")
|
document.title = "Budgeteer - Settings";
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
gotAssignments(e : Event) {
|
gotAssignments(e : Event) {
|
||||||
@ -30,22 +31,21 @@ export default defineComponent({
|
|||||||
this.$data.transactionsFile = input.files[0];
|
this.$data.transactionsFile = input.files[0];
|
||||||
},
|
},
|
||||||
deleteBudget() {
|
deleteBudget() {
|
||||||
fetch("/api/v1/budget/" + this.$store.getters.CurrentBudget.ID, {
|
const currentBudgetID = useBudgetsStore().CurrentBudgetID;
|
||||||
method: "DELETE",
|
if (currentBudgetID == null)
|
||||||
headers: {
|
return;
|
||||||
'Authorization': 'Bearer ' + this.$store.state.Session.Token
|
|
||||||
},
|
const api = useAPI();
|
||||||
});
|
api.DELETE("/api/v1/budget/" + currentBudgetID);
|
||||||
this.$store.commit("deleteBudget", this.$store.getters.CurrentBudget.ID)
|
|
||||||
|
const budgetStore = useSessionStore();
|
||||||
|
budgetStore.Budgets.delete(currentBudgetID);
|
||||||
this.$router.push("/")
|
this.$router.push("/")
|
||||||
},
|
},
|
||||||
clearBudget() {
|
clearBudget() {
|
||||||
fetch("/api/v1/budget/" + this.$store.getters.CurrentBudget.ID + "/settings/clear", {
|
const currentBudgetID = useBudgetsStore().CurrentBudgetID;
|
||||||
method: "POST",
|
const api = useAPI();
|
||||||
headers: {
|
api.POST("/api/v1/budget/" + currentBudgetID + "/settings/clear", null)
|
||||||
'Authorization': 'Bearer ' + this.$store.state.Session.Token
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
cleanNegative() {
|
cleanNegative() {
|
||||||
// <a href="/budget/{{.Budget.ID}}/settings/clean-negative">Fix all historic negative category-balances</a>
|
// <a href="/budget/{{.Budget.ID}}/settings/clean-negative">Fix all historic negative category-balances</a>
|
||||||
@ -57,7 +57,8 @@ export default defineComponent({
|
|||||||
let formData = new FormData();
|
let formData = new FormData();
|
||||||
formData.append("transactions", this.$data.transactionsFile);
|
formData.append("transactions", this.$data.transactionsFile);
|
||||||
formData.append("assignments", this.$data.assignmentsFile);
|
formData.append("assignments", this.$data.assignmentsFile);
|
||||||
this.$store.dispatch(IMPORT_YNAB, formData);
|
const budgetStore = useBudgetsStore();
|
||||||
|
budgetStore.ImportYNAB(formData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
export const IMPORT_YNAB = "YNAB import";
|
|
||||||
export const GET = "GET";
|
|
||||||
export const POST = "POST";
|
|
||||||
export const NEW_BUDGET = "New budget";
|
|
||||||
export const SET_CURRENT_BUDGET = "Set current budget";
|
|
||||||
export const SET_CURRENT_ACCOUNT = "Set current account";
|
|
||||||
export const FETCH_BUDGET = "Fetch budget";
|
|
||||||
export const FETCH_MONTH_BUDGET = "Fetch budget for month";
|
|
||||||
export const LOGIN = 'Log in';
|
|
||||||
export const REGISTER = 'Register';
|
|
||||||
export const FETCH_ACCOUNT = "Fetch account";
|
|
@ -1,152 +0,0 @@
|
|||||||
import { Module } from "vuex";
|
|
||||||
import { FETCH_ACCOUNT, FETCH_BUDGET, FETCH_MONTH_BUDGET, SET_CURRENT_ACCOUNT } from "../action-types";
|
|
||||||
import { LOGOUT, TITLE } from "../mutation-types";
|
|
||||||
|
|
||||||
export interface BudgetState {
|
|
||||||
Accounts: Map<string, Account>,
|
|
||||||
CurrentAccountID?: string,
|
|
||||||
Categories: Map<string, Category>,
|
|
||||||
Months: Map<number, Map<number, Map<string, Category>>>,
|
|
||||||
Transactions: [],
|
|
||||||
Assignments: []
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Account {
|
|
||||||
ID: string
|
|
||||||
OnBudget: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Category {
|
|
||||||
Group: string
|
|
||||||
Name: string
|
|
||||||
AvailableLastMonth: number
|
|
||||||
Assigned: number
|
|
||||||
Activity: number
|
|
||||||
Available: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export const budgetStore : Module<BudgetState, any> = {
|
|
||||||
state: {
|
|
||||||
Accounts: new Map<string, Account>(),
|
|
||||||
CurrentAccountID: undefined,
|
|
||||||
Months: new Map<number, Map<number, Map<string, Category>>>(),
|
|
||||||
Categories: new Map<string, Category>(),
|
|
||||||
Transactions: [],
|
|
||||||
Assignments: []
|
|
||||||
},
|
|
||||||
|
|
||||||
mutations: {
|
|
||||||
initializeStore(state) {
|
|
||||||
const store = localStorage.getItem("store");
|
|
||||||
if (!store)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const restoredState = JSON.parse(store);
|
|
||||||
if (!restoredState)
|
|
||||||
return;
|
|
||||||
|
|
||||||
state.CurrentAccountID = restoredState.CurrentAccountID;
|
|
||||||
|
|
||||||
for (const account of restoredState.Accounts || []) {
|
|
||||||
state.Accounts.set(account[0], account[1]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[LOGOUT](state) {
|
|
||||||
state.Accounts.clear();
|
|
||||||
state.Categories.clear();
|
|
||||||
state.Transactions = [];
|
|
||||||
state.Assignments = [];
|
|
||||||
},
|
|
||||||
addAccount(state, account) {
|
|
||||||
state.Accounts.set(account.ID, account);
|
|
||||||
},
|
|
||||||
addCategory(state, category) {
|
|
||||||
state.Categories.set(category.ID, category);
|
|
||||||
},
|
|
||||||
addCategoriesForMonth(state, {year, month, categories}) {
|
|
||||||
const yearMap = state.Months.get(year) || new Map<number, Map<string, Category>>();
|
|
||||||
state.Months.set(year, yearMap);
|
|
||||||
|
|
||||||
const monthMap = yearMap.get(month) || new Map<string, Category>();
|
|
||||||
yearMap.set(month, monthMap);
|
|
||||||
|
|
||||||
for (const category of categories){
|
|
||||||
monthMap.set(category.ID, category);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setCurrentAccountID(state, accountid) {
|
|
||||||
state.CurrentAccountID = accountid;
|
|
||||||
},
|
|
||||||
setTransactions(state, transactions) {
|
|
||||||
state.Transactions = transactions;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getters: {
|
|
||||||
Accounts(state) {
|
|
||||||
return state.Accounts.values();
|
|
||||||
},
|
|
||||||
Categories: (state) => (year : number, month : number) => {
|
|
||||||
const yearMap = state.Months.get(year);
|
|
||||||
return yearMap?.get(month)?.values();
|
|
||||||
},
|
|
||||||
CurrentAccount(state) : Account | undefined {
|
|
||||||
if (state.CurrentAccountID == null)
|
|
||||||
return undefined;
|
|
||||||
return state.Accounts.get(state.CurrentAccountID);
|
|
||||||
},
|
|
||||||
OnBudgetAccounts(state) {
|
|
||||||
return Array.from(state.Accounts.values()).filter(x => x.OnBudget);
|
|
||||||
},
|
|
||||||
OnBudgetAccountsBalance(state, getters){
|
|
||||||
return getters.OnBudgetAccounts.reduce((prev, curr) => prev + Number(curr.Balance), 0);
|
|
||||||
},
|
|
||||||
OffBudgetAccounts(state) {
|
|
||||||
return Array.from(state.Accounts.values()).filter(x => !x.OnBudget);
|
|
||||||
},
|
|
||||||
OffBudgetAccountsBalance(state, getters){
|
|
||||||
return getters.OffBudgetAccounts.reduce((prev, curr) => prev + Number(curr.Balance), 0);
|
|
||||||
},
|
|
||||||
Transactions(state) {
|
|
||||||
return (state.Transactions || []);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
async [SET_CURRENT_ACCOUNT]({ state, commit, dispatch, getters }, { budgetid, accountid }) {
|
|
||||||
if (budgetid == null)
|
|
||||||
return
|
|
||||||
|
|
||||||
commit("setCurrentAccountID", accountid);
|
|
||||||
if (accountid == null)
|
|
||||||
return
|
|
||||||
|
|
||||||
commit(TITLE, getters.CurrentAccount.Name);
|
|
||||||
await dispatch(FETCH_ACCOUNT, accountid)
|
|
||||||
},
|
|
||||||
async [FETCH_ACCOUNT]({ state, commit, rootState }, accountid) {
|
|
||||||
const result = await fetch("/api/v1/account/" + accountid + "/transactions", {
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Bearer ' + rootState.Session.Token
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const response = await result.json();
|
|
||||||
commit("setTransactions", response.Transactions);
|
|
||||||
},
|
|
||||||
async [FETCH_BUDGET]({ state, commit, dispatch, rootState }, budgetid) {
|
|
||||||
const result = await dispatch("GET", { path: "/budget/" + budgetid });
|
|
||||||
const response = await result.json();
|
|
||||||
for (const account of response.Accounts || []) {
|
|
||||||
commit("addAccount", account);
|
|
||||||
}
|
|
||||||
for (const category of response.Categories || []) {
|
|
||||||
commit("addCategory", category);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async [FETCH_MONTH_BUDGET]({state, commit, dispatch, rootState }, {budgetid, month, year}) {
|
|
||||||
const result = await dispatch("GET", { path: "/budget/" + budgetid + "/" + year + "/" + month});
|
|
||||||
const response = await result.json();
|
|
||||||
commit("addCategoriesForMonth", {year, month, categories: response.Categories})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
export const store = createStore<State>({
|
|
||||||
actions: {
|
|
||||||
/*async fetchDashboard ({state, commit, rootState}) {
|
|
||||||
const response = await fetch("/api/v1/dashboard", {
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Bearer ' + rootState.Session.Token
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const data = await response.json();
|
|
||||||
commit("setBudgets", data.Budgets);
|
|
||||||
},*/
|
|
||||||
},
|
|
||||||
plugins: [createLogger()],
|
|
||||||
modules: {
|
|
||||||
budget: budgetStore
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
store.subscribe((mutation, state) => {
|
|
||||||
|
|
||||||
const sessionStore = useSessionStore();
|
|
||||||
let persistedState = {
|
|
||||||
Session: sessionStore,
|
|
||||||
// Accounts: [...state.Accounts],
|
|
||||||
CurrentBudgetID: state.CurrentBudgetID,
|
|
||||||
//CurrentAccountID: state.CurrentAccountID,
|
|
||||||
ExpandMenu: state.ExpandMenu,
|
|
||||||
ShowMenu: state.ShowMenu
|
|
||||||
}
|
|
||||||
localStorage.setItem("store", JSON.stringify(persistedState));
|
|
||||||
})
|
|
@ -1,3 +0,0 @@
|
|||||||
export const LOGIN_SUCCESS = '✔ Logged in';
|
|
||||||
export const LOGOUT = 'Log out';
|
|
||||||
export const TITLE = 'Update title';
|
|
28
web/src/stores/api.ts
Normal file
28
web/src/stores/api.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { useSessionStore } from "./session";
|
||||||
|
|
||||||
|
export const useAPI = defineStore("api", {
|
||||||
|
actions: {
|
||||||
|
GET(path : string) {
|
||||||
|
const sessionStore = useSessionStore();
|
||||||
|
return fetch("/api/v1" + path, {
|
||||||
|
headers: sessionStore.AuthHeaders,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
POST(path : string, body : FormData | string | null) {
|
||||||
|
const sessionStore = useSessionStore();
|
||||||
|
return fetch("/api/v1" + path, {
|
||||||
|
method: "POST",
|
||||||
|
headers: sessionStore.AuthHeaders,
|
||||||
|
body: body,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
DELETE(path : string) {
|
||||||
|
const sessionStore = useSessionStore();
|
||||||
|
return fetch("/api/v1" + path, {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: sessionStore.AuthHeaders,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
111
web/src/stores/budget-account.ts
Normal file
111
web/src/stores/budget-account.ts
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import { defineStore } from "pinia"
|
||||||
|
import { FETCH_ACCOUNT } from "../store/action-types";
|
||||||
|
import { useAPI } from "./api";
|
||||||
|
import { useSessionStore } from "./session";
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
Accounts: Map<string, Account>,
|
||||||
|
CurrentAccountID: string | null,
|
||||||
|
Categories: Map<string, Category>,
|
||||||
|
Months: Map<number, Map<number, Map<string, Category>>>,
|
||||||
|
Transactions: [],
|
||||||
|
Assignments: []
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Account {
|
||||||
|
ID: string
|
||||||
|
Name: string
|
||||||
|
OnBudget: boolean
|
||||||
|
Balance: Number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Category {
|
||||||
|
ID: string
|
||||||
|
Group: string
|
||||||
|
Name: string
|
||||||
|
AvailableLastMonth: number
|
||||||
|
Assigned: number
|
||||||
|
Activity: number
|
||||||
|
Available: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAccountStore = defineStore("budget/account", {
|
||||||
|
state: (): State => ({
|
||||||
|
Accounts: new Map<string, Account>(),
|
||||||
|
CurrentAccountID: null,
|
||||||
|
Months: new Map<number, Map<number, Map<string, Category>>>(),
|
||||||
|
Categories: new Map<string, Category>(),
|
||||||
|
Transactions: [],
|
||||||
|
Assignments: []
|
||||||
|
}),
|
||||||
|
getters: {
|
||||||
|
AccountsList(state) {
|
||||||
|
return state.Accounts.values();
|
||||||
|
},
|
||||||
|
Categories: (state) => (year : number, month : number) => {
|
||||||
|
const yearMap = state.Months.get(year);
|
||||||
|
return yearMap?.get(month)?.values();
|
||||||
|
},
|
||||||
|
CurrentAccount(state) : Account | undefined {
|
||||||
|
if (state.CurrentAccountID == null)
|
||||||
|
return undefined;
|
||||||
|
|
||||||
|
return state.Accounts.get(state.CurrentAccountID);
|
||||||
|
},
|
||||||
|
OnBudgetAccounts(state) {
|
||||||
|
return Array.from(state.Accounts.values()).filter(x => x.OnBudget);
|
||||||
|
},
|
||||||
|
OnBudgetAccountsBalance(state) : Number {
|
||||||
|
return this.OnBudgetAccounts.reduce((prev, curr) => prev + Number(curr.Balance), 0);
|
||||||
|
},
|
||||||
|
OffBudgetAccounts(state) {
|
||||||
|
return Array.from(state.Accounts.values()).filter(x => !x.OnBudget);
|
||||||
|
},
|
||||||
|
OffBudgetAccountsBalance(state) : Number {
|
||||||
|
return this.OffBudgetAccounts.reduce((prev, curr) => prev + Number(curr.Balance), 0);
|
||||||
|
},
|
||||||
|
Transactions(state) {
|
||||||
|
return (state.Transactions || []);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
async SetCurrentAccount(budgetid : string, accountid : string) {
|
||||||
|
if (budgetid == null)
|
||||||
|
return
|
||||||
|
|
||||||
|
this.CurrentAccountID = accountid;
|
||||||
|
if (this.CurrentAccount == undefined)
|
||||||
|
return
|
||||||
|
|
||||||
|
useSessionStore().setTitle(this.CurrentAccount.Name);
|
||||||
|
await this.FetchAccount(accountid);
|
||||||
|
},
|
||||||
|
async FetchAccount(accountid : string) {
|
||||||
|
const api = useAPI();
|
||||||
|
const result = await api.GET("/api/v1/account/" + accountid + "/transactions");
|
||||||
|
const response = await result.json();
|
||||||
|
this.Transactions = response.Transactions;
|
||||||
|
},
|
||||||
|
async FetchMonthBudget(budgetid : string, month : number, year : number) {
|
||||||
|
const api = useAPI();
|
||||||
|
const result = await api.GET("/budget/" + budgetid + "/" + year + "/" + month);
|
||||||
|
const response = await result.json();
|
||||||
|
this.addCategoriesForMonth(year, month, response.Categories);
|
||||||
|
},
|
||||||
|
addCategoriesForMonth(year : number, month : number, categories : Category[]) : void {
|
||||||
|
const yearMap = this.Months.get(year) || new Map<number, Map<string, Category>>();
|
||||||
|
this.Months.set(year, yearMap);
|
||||||
|
|
||||||
|
const monthMap = yearMap.get(month) || new Map<string, Category>();
|
||||||
|
yearMap.set(month, monthMap);
|
||||||
|
|
||||||
|
for (const category of categories){
|
||||||
|
monthMap.set(category.ID, category);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
logout() {
|
||||||
|
this.$reset()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
@ -1,5 +1,6 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { useAPI } from "./api";
|
import { useAPI } from "./api";
|
||||||
|
import { useAccountStore } from "./budget-account";
|
||||||
import { Budget, useSessionStore } from "./session";
|
import { Budget, useSessionStore } from "./session";
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
@ -8,35 +9,29 @@ interface State {
|
|||||||
|
|
||||||
export const useBudgetsStore = defineStore('budget', {
|
export const useBudgetsStore = defineStore('budget', {
|
||||||
state: (): State => ({
|
state: (): State => ({
|
||||||
CurrentBudgetID: null
|
CurrentBudgetID: null,
|
||||||
}),
|
}),
|
||||||
actions: {
|
|
||||||
setCurrentBudgetID(budgetid : string) {
|
|
||||||
this.CurrentBudgetID = budgetid;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
getters: {
|
getters: {
|
||||||
CurrentBudget() : Budget | undefined {
|
CurrentBudget(): Budget | undefined {
|
||||||
if (this.CurrentBudgetID == null)
|
if (this.CurrentBudgetID == null)
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
||||||
const sessionStore = useSessionStore();
|
const sessionStore = useSessionStore();
|
||||||
return sessionStore.Budgets.get(this.CurrentBudgetID);
|
return sessionStore.Budgets.get(this.CurrentBudgetID);
|
||||||
},
|
},
|
||||||
CurrentBudgetName() : string {
|
CurrentBudgetName(state): string {
|
||||||
return this.CurrentBudget?.Name ?? "";
|
return this.CurrentBudget?.Name ?? "";
|
||||||
},
|
},
|
||||||
CurrentBudgetID() : string | undefined {
|
},
|
||||||
return this.CurrentBudgetID;
|
actions: {
|
||||||
},
|
ImportYNAB(formData: FormData) {
|
||||||
ImportYNAB(formData) {
|
|
||||||
const api = useAPI();
|
const api = useAPI();
|
||||||
return api.POST(
|
return api.POST(
|
||||||
"/budget/" + this.CurrentBudgetID + "/import/ynab",
|
"/budget/" + this.CurrentBudgetID + "/import/ynab",
|
||||||
formData
|
formData,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
async NewBudget(budgetName) {
|
async NewBudget(budgetName: string): Promise<void> {
|
||||||
const api = useAPI();
|
const api = useAPI();
|
||||||
const result = await api.POST(
|
const result = await api.POST(
|
||||||
"/budget/new",
|
"/budget/new",
|
||||||
@ -47,13 +42,24 @@ export const useBudgetsStore = defineStore('budget', {
|
|||||||
const sessionStore = useSessionStore();
|
const sessionStore = useSessionStore();
|
||||||
sessionStore.Budgets.set(response.ID, response);
|
sessionStore.Budgets.set(response.ID, response);
|
||||||
},
|
},
|
||||||
async SetCurrentBudget(budgetid : string) {
|
async SetCurrentBudget(budgetid: string): Promise<void> {
|
||||||
this.CurrentBudgetID = budgetid;
|
this.CurrentBudgetID = budgetid;
|
||||||
|
|
||||||
if (budgetid == null)
|
if (budgetid == null)
|
||||||
return
|
return
|
||||||
|
|
||||||
await dispatch(FETCH_BUDGET, budgetid)
|
await this.FetchBudget(budgetid);
|
||||||
|
},
|
||||||
|
async FetchBudget(budgetid: string) {
|
||||||
|
const api = useAPI();
|
||||||
|
const result = await api.GET("/budget/" + budgetid);
|
||||||
|
const response = await result.json();
|
||||||
|
for (const account of response.Accounts || []) {
|
||||||
|
useAccountStore().Accounts.set(account.ID, account);
|
||||||
|
}
|
||||||
|
for (const category of response.Categories || []) {
|
||||||
|
useAccountStore().Categories.set(category.ID, category);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
@ -20,35 +20,9 @@ export const useSessionStore = defineStore('session', {
|
|||||||
Budgets: new Map<string, Budget>(),
|
Budgets: new Map<string, Budget>(),
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
Budgets(): IterableIterator<Budget> {
|
BudgetsList: (state) => state.Budgets.values(),
|
||||||
return this.Budgets.values();
|
AuthHeaders: (state) => ({'Authorization': 'Bearer ' + state.Token}),
|
||||||
},
|
LoggedIn: (state) => state.Token != null,
|
||||||
AuthHeaders(): HeadersInit {
|
|
||||||
return {
|
|
||||||
'Authorization': 'Bearer ' + this.Token
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/*// must define return type because of using `this`
|
|
||||||
fullUserDetails (state): FullUserDetails {
|
|
||||||
// import from other stores
|
|
||||||
const authPreferencesStore = useAuthPreferencesStore()
|
|
||||||
const authEmailStore = useAuthEmailStore()
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
// other getters now on `this`
|
|
||||||
fullName: this.fullName,
|
|
||||||
...authPreferencesStore.$state,
|
|
||||||
...authEmailStore.details
|
|
||||||
}
|
|
||||||
|
|
||||||
// alternative if other modules are still in Vuex
|
|
||||||
// return {
|
|
||||||
// ...state,
|
|
||||||
// fullName: this.fullName,
|
|
||||||
// ...vuexStore.state.auth.preferences,
|
|
||||||
// ...vuexStore.getters['auth/email'].details
|
|
||||||
// }
|
|
||||||
}*/
|
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
setTitle(title : string) {
|
setTitle(title : string) {
|
||||||
|
@ -1583,7 +1583,7 @@
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
prettier "^1.18.2 || ^2.0.0"
|
prettier "^1.18.2 || ^2.0.0"
|
||||||
|
|
||||||
"@vue/devtools-api@^6.0.0-beta.11", "@vue/devtools-api@^6.0.0-beta.18":
|
"@vue/devtools-api@^6.0.0-beta.18":
|
||||||
version "6.0.0-beta.21.1"
|
version "6.0.0-beta.21.1"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.0.0-beta.21.1.tgz#f1410f53c42aa67fa3b01ca7bdba891f69d7bc97"
|
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.0.0-beta.21.1.tgz#f1410f53c42aa67fa3b01ca7bdba891f69d7bc97"
|
||||||
integrity sha512-FqC4s3pm35qGVeXRGOjTsRzlkJjrBLriDS9YXbflHLsfA9FrcKzIyWnLXoNm+/7930E8rRakXuAc2QkC50swAw==
|
integrity sha512-FqC4s3pm35qGVeXRGOjTsRzlkJjrBLriDS9YXbflHLsfA9FrcKzIyWnLXoNm+/7930E8rRakXuAc2QkC50swAw==
|
||||||
@ -8230,13 +8230,6 @@ vue@^3.2.25:
|
|||||||
"@vue/server-renderer" "3.2.29"
|
"@vue/server-renderer" "3.2.29"
|
||||||
"@vue/shared" "3.2.29"
|
"@vue/shared" "3.2.29"
|
||||||
|
|
||||||
vuex@^4.0.2:
|
|
||||||
version "4.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/vuex/-/vuex-4.0.2.tgz#f896dbd5bf2a0e963f00c67e9b610de749ccacc9"
|
|
||||||
integrity sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==
|
|
||||||
dependencies:
|
|
||||||
"@vue/devtools-api" "^6.0.0-beta.11"
|
|
||||||
|
|
||||||
watchpack-chokidar2@^2.0.1:
|
watchpack-chokidar2@^2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957"
|
resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user