190 lines
6.0 KiB
Vue
190 lines
6.0 KiB
Vue
<script lang="ts" setup>
|
||
import { computed, onMounted, ref, watchEffect } from "vue";
|
||
import Currency from "../components/Currency.vue";
|
||
import { useBudgetsStore } from "../stores/budget";
|
||
import { Category, useAccountStore } from "../stores/budget-account";
|
||
import { useSessionStore } from "../stores/session";
|
||
import Input from "../components/Input.vue";
|
||
import { POST } from "../api";
|
||
|
||
const props = defineProps<{
|
||
budgetid: string,
|
||
year: string,
|
||
month: string,
|
||
}>()
|
||
|
||
const budgetsStore = useBudgetsStore();
|
||
const CurrentBudgetID = computed(() => budgetsStore.CurrentBudgetID);
|
||
|
||
const accountStore = useAccountStore();
|
||
const categoriesForMonth = accountStore.CategoriesForMonthAndGroup;
|
||
|
||
function GetCategories(group: string) {
|
||
return [...categoriesForMonth(selected.value.Year, selected.value.Month, group)];
|
||
};
|
||
|
||
const groupsForMonth = accountStore.CategoryGroupsForMonth;
|
||
const GroupsForMonth = computed(() => {
|
||
return [...groupsForMonth(selected.value.Year, selected.value.Month)];
|
||
});
|
||
|
||
|
||
const previous = computed(() => ({
|
||
Year: new Date(selected.value.Year, selected.value.Month - 1, 1).getFullYear(),
|
||
Month: new Date(selected.value.Year, selected.value.Month - 1, 1).getMonth(),
|
||
}));
|
||
const current = computed(() => ({
|
||
Year: new Date().getFullYear(),
|
||
Month: new Date().getMonth(),
|
||
}));
|
||
const selected = computed(() => ({
|
||
Year: Number(props.year) ?? current.value.Year,
|
||
Month: Number(props.month ?? current.value.Month)
|
||
}));
|
||
const next = computed(() => ({
|
||
Year: new Date(selected.value.Year, Number(props.month) + 1, 1).getFullYear(),
|
||
Month: new Date(selected.value.Year, Number(props.month) + 1, 1).getMonth(),
|
||
}));
|
||
|
||
watchEffect(() => {
|
||
if (props.year != undefined && props.month != undefined)
|
||
return useAccountStore().FetchMonthBudget(props.budgetid ?? "", Number(props.year), Number(props.month));
|
||
});
|
||
|
||
onMounted(() => {
|
||
useSessionStore().setTitle("Budget for " + selected.value.Month + "/" + selected.value.Year);
|
||
})
|
||
|
||
|
||
const expandedGroups = ref<Map<string, boolean>>(new Map<string, boolean>())
|
||
|
||
function toggleGroup(group: { Name: string, Expand: boolean }) {
|
||
expandedGroups.value.set(group.Name, !(expandedGroups.value.get(group.Name) ?? group.Expand))
|
||
}
|
||
|
||
function getGroupState(group: { Name: string, Expand: boolean }): boolean {
|
||
return expandedGroups.value.get(group.Name) ?? group.Expand;
|
||
}
|
||
|
||
function assignedChanged(e : Event, category : Category){
|
||
const target = e.target as HTMLInputElement;
|
||
const value = target.valueAsNumber;
|
||
POST("/budget/"+CurrentBudgetID.value+"/category/" + category.ID + "/" + selected.value.Year + "/" + (selected.value.Month+1),
|
||
JSON.stringify({Assigned: category.Assigned}));
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<h1>Budget for {{ selected.Month + 1 }}/{{ selected.Year }}</h1>
|
||
<span>Available balance:
|
||
<Currency
|
||
:value="accountStore.GetIncomeAvailable(selected.Year, selected.Month)"
|
||
/>
|
||
</span><br />
|
||
<span>Budgeted this month:
|
||
<Currency
|
||
:value="accountStore.GetBudgeted(selected.Year, selected.Month).Assigned"
|
||
/>
|
||
</span><br />
|
||
<span>Deassigned this month:
|
||
<Currency
|
||
:value="accountStore.GetBudgeted(selected.Year, selected.Month).Deassigned"
|
||
/>
|
||
</span><br />
|
||
<span>Spent this month:
|
||
<Currency
|
||
:value="accountStore.GetBudgeted(selected.Year, selected.Month).Activity"
|
||
/>
|
||
</span><br />
|
||
<div>
|
||
<router-link
|
||
:to="'/budget/' + CurrentBudgetID + '/budgeting/' + previous.Year + '/' + 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"
|
||
>
|
||
>>
|
||
</router-link>
|
||
</div>
|
||
<div
|
||
id="content"
|
||
class="container col-lg-12 grid grid-cols-2 sm:grid-cols-4 lg:grid-cols-5"
|
||
>
|
||
<span class="hidden sm:block" />
|
||
<span class="hidden lg:block text-right">Leftover</span>
|
||
<span class="hidden sm:block text-right">Assigned</span>
|
||
<span class="hidden sm:block text-right">Activity</span>
|
||
<span class="hidden sm:block text-right">Available</span>
|
||
<template
|
||
v-for="group in GroupsForMonth"
|
||
:key="group.Name"
|
||
>
|
||
<span
|
||
class="text-lg font-bold mt-2"
|
||
@click="toggleGroup(group)"
|
||
>{{ (getGroupState(group) ? "−" : "+") + " " + group.Name }}</span>
|
||
<Currency
|
||
:value="group.AvailableLastMonth"
|
||
class="hidden lg:block mt-2"
|
||
positive-class="text-slate-500"
|
||
negative-class="text-red-700 dark:text-red-400"
|
||
/>
|
||
<Currency
|
||
:value="group.Assigned"
|
||
class="hidden sm:block mx-2 mt-2 text-right"
|
||
positive-class="text-slate-500"
|
||
negative-class="text-red-700 dark:text-red-400"
|
||
/>
|
||
<Currency
|
||
:value="group.Activity"
|
||
class="hidden sm:block mt-2"
|
||
positive-class="text-slate-500"
|
||
negative-class="text-red-700 dark:text-red-400"
|
||
/>
|
||
<Currency
|
||
:value="group.Available"
|
||
class="mt-2"
|
||
positive-class="text-slate-500"
|
||
negative-class="text-red-700 dark:text-red-400"
|
||
/>
|
||
<template
|
||
v-for="category in GetCategories(group.Name)"
|
||
:key="category.ID"
|
||
>
|
||
<div
|
||
v-if="getGroupState(group)"
|
||
class="contents"
|
||
>
|
||
<span
|
||
class="whitespace-nowrap overflow-hidden"
|
||
>{{ category.Name }}</span>
|
||
<Currency
|
||
:value="category.AvailableLastMonth"
|
||
class="hidden lg:block"
|
||
/>
|
||
<Input
|
||
v-model="category.Assigned"
|
||
type="number"
|
||
class="hidden sm:block mx-2 text-right"
|
||
@input="(evt) => assignedChanged(evt, category)"
|
||
/>
|
||
<Currency
|
||
:value="category.Activity"
|
||
class="hidden sm:block"
|
||
/>
|
||
<Currency
|
||
:value="accountStore.GetCategoryAvailable(category)"
|
||
/>
|
||
</div>
|
||
</template>
|
||
</template>
|
||
</div>
|
||
</template>
|