Merge pull request 'Use vue's Composition API in components' (#11) from vue-composition into master
All checks were successful
continuous-integration/drone/push Build is passing
ci/woodpecker/push/woodpecker Pipeline was successful

Reviewed-on: #11
This commit is contained in:
Jan Bader 2022-02-15 10:13:47 +01:00
commit 368ac7f15d
12 changed files with 168 additions and 173 deletions

View File

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, PropType } from "vue" import { defineComponent, PropType, ref, watch } from "vue"
import { GET } from "../api"; import { GET } from "../api";
import { useBudgetsStore } from "../stores/budget"; import { useBudgetsStore } from "../stores/budget";
@ -14,50 +14,44 @@ interface Data {
Suggestions: Suggestion[] Suggestions: Suggestion[]
} }
export default defineComponent({ const props = defineProps<{
data() { modelValue: Suggestion | undefined,
return {
Selected: undefined,
SearchQuery: this.modelValue || "",
Suggestions: new Array<Suggestion>(),
} as Data
},
props: {
modelValue: Object as PropType<Suggestion>,
type: String type: String
}, }>();
watch: {
SearchQuery() { const Selected = ref<Suggestion | undefined>(props.modelValue || undefined);
this.load(this.$data.SearchQuery); const SearchQuery = ref(props.modelValue?.Name || "");
} const Suggestions = ref<Array<Suggestion>>([]);
}, const emit = defineEmits(["update:modelValue"]);
methods: { watch(SearchQuery, () => {
saveTransaction(e : MouseEvent) { load(SearchQuery.value);
});
function saveTransaction(e: MouseEvent) {
e.preventDefault(); e.preventDefault();
}, };
load(text : String) { function load(text: String) {
this.$emit('update:modelValue', {ID: null, Name: text}); emit('update:modelValue', { ID: null, Name: text });
if (text == "") { if (text == "") {
this.$data.Suggestions = []; Suggestions.value = [];
return; return;
} }
const budgetStore = useBudgetsStore(); const budgetStore = useBudgetsStore();
GET("/budget/" + budgetStore.CurrentBudgetID + "/autocomplete/" + this.type + "?s=" + text) GET("/budget/" + budgetStore.CurrentBudgetID + "/autocomplete/" + props.type + "?s=" + text)
.then(x => x.json()) .then(x => x.json())
.then(x => { .then(x => {
let suggestions = x || []; let suggestions = x || [];
if (suggestions.length > 10) { if (suggestions.length > 10) {
suggestions = suggestions.slice(0, 10); suggestions = suggestions.slice(0, 10);
} }
this.$data.Suggestions = suggestions; Suggestions.value = suggestions;
}); });
}, };
keypress(e : KeyboardEvent) { function keypress(e: KeyboardEvent) {
console.log(e.key); console.log(e.key);
if (e.key == "Enter") { if (e.key == "Enter") {
const selected = this.$data.Suggestions[0]; const selected = Suggestions.value[0];
this.selectElement(selected); selectElement(selected);
const el = (<HTMLInputElement>e.target); const el = (<HTMLInputElement>e.target);
const inputElements = Array.from(el.ownerDocument.querySelectorAll('input:not([disabled]):not([readonly])')); const inputElements = Array.from(el.ownerDocument.querySelectorAll('input:not([disabled]):not([readonly])'));
const currentIndex = inputElements.indexOf(el); const currentIndex = inputElements.indexOf(el);
@ -65,37 +59,43 @@ export default defineComponent({
(<HTMLInputElement>nextElement).focus(); (<HTMLInputElement>nextElement).focus();
} }
}, };
selectElement(element : Suggestion) { function selectElement(element: Suggestion) {
this.$data.Selected = element; Selected.value = element;
this.$data.Suggestions = []; Suggestions.value = [];
this.$emit('update:modelValue', element); emit('update:modelValue', element);
}, };
select(e : MouseEvent) { function select(e: MouseEvent) {
const target = (<HTMLInputElement>e.target); const target = (<HTMLInputElement>e.target);
const valueAttribute = target.attributes.getNamedItem("value"); const valueAttribute = target.attributes.getNamedItem("value");
let selectedID = ""; let selectedID = "";
if (valueAttribute != null) if (valueAttribute != null)
selectedID = valueAttribute.value; selectedID = valueAttribute.value;
const selected = this.$data.Suggestions.filter(x => x.ID == selectedID)[0]; const selected = Suggestions.value.filter(x => x.ID == selectedID)[0];
this.selectElement(selected); selectElement(selected);
}, };
clear() { function clear() {
this.$data.Selected = undefined; Selected.value = undefined;
this.$emit('update:modelValue', {ID: null, Name: this.$data.SearchQuery}); emit('update:modelValue', { ID: null, Name: SearchQuery.value });
} };
}
})
</script> </script>
<template> <template>
<div> <div>
<input class="border-b-2 border-black" @keypress="keypress" v-if="Selected == undefined" v-model="SearchQuery" /> <input
class="border-b-2 border-black"
@keypress="keypress"
v-if="Selected == undefined"
v-model="SearchQuery"
/>
<span @click="clear" v-if="Selected != undefined" class="bg-gray-300">{{ Selected.Name }}</span> <span @click="clear" v-if="Selected != undefined" class="bg-gray-300">{{ Selected.Name }}</span>
<div v-if="Suggestions.length > 0" class="absolute bg-gray-400 w-64 p-2"> <div v-if="Suggestions.length > 0" class="absolute bg-gray-400 w-64 p-2">
<span v-for="suggestion in Suggestions" class="block" @click="select" :value="suggestion.ID"> <span
{{suggestion.Name}} v-for="suggestion in Suggestions"
</span> class="block"
@click="select"
:value="suggestion.ID"
>{{ suggestion.Name }}</span>
</div> </div>
</div> </div>
</template> </template>

View File

@ -1,9 +1,4 @@
<script lang="ts"> <script lang="ts" setup>
import { defineComponent } from "vue";
export default defineComponent({
})
</script> </script>
<template> <template>

View File

@ -1,19 +1,15 @@
<script lang="ts"> <script lang="ts" setup>
import { defineComponent } from "vue"; import { computed } from 'vue';
export default defineComponent({ const props = defineProps<{ value: number | undefined }>();
props: ["value"],
computed: { const internalValue = computed(() => Number(props.value ?? 0));
formattedValue() {
return Number(this.value).toLocaleString(undefined, { const formattedValue = computed(() => internalValue.value.toLocaleString(undefined, {
minimumFractionDigits: 2, minimumFractionDigits: 2,
}); }));
}
}
})
</script> </script>
<template> <template>
<span class="text-right" :class="value < 0 ? 'negative' : ''">{{formattedValue}} </span> <span class="text-right" :class="internalValue < 0 ? 'negative' : ''">{{ formattedValue }} </span>
</template> </template>

View File

@ -1,16 +1,15 @@
<script lang="ts"> <script lang="ts" setup>
import { mapState } from "pinia"; import { computed } from "vue";
import { defineComponent } from "vue";
import { useBudgetsStore } from "../stores/budget"; import { useBudgetsStore } from "../stores/budget";
import { Transaction } from "../stores/budget-account";
import Currency from "./Currency.vue"; import Currency from "./Currency.vue";
export default defineComponent({ const props = defineProps<{
props: [ "transaction", "index" ], transaction: Transaction,
components: { Currency }, index: number,
computed: { }>();
...mapState(useBudgetsStore, ["CurrentBudgetID"])
} const CurrentBudgetID = computed(()=> useBudgetsStore().CurrentBudgetID);
})
</script> </script>
<template> <template>

View File

@ -1,26 +1,17 @@
<script lang="ts"> <script lang="ts" setup>
import Card from '../components/Card.vue'; import Card from '../components/Card.vue';
import { defineComponent } from "vue"; import { ref } from "vue";
import { useBudgetsStore } from '../stores/budget'; import { useBudgetsStore } from '../stores/budget';
export default defineComponent({ const dialog = ref(false);
data() { const budgetName = ref("");
return { function saveBudget() {
dialog: false, useBudgetsStore().NewBudget(budgetName.value);
budgetName: "" dialog.value = false;
} };
}, function newBudget() {
components: { Card }, dialog.value = true;
methods: { };
saveBudget() {
useBudgetsStore().NewBudget(this.$data.budgetName);
this.$data.dialog = false;
},
newBudget() {
this.$data.dialog = true;
}
}
})
</script> </script>
<template> <template>

View File

@ -33,8 +33,8 @@ function saveTransaction(e: MouseEvent) {
} }
const accountStore = useAccountStore(); const accountStore = useAccountStore();
const CurrentAccount = accountStore.CurrentAccount; const CurrentAccount = computed(() => accountStore.CurrentAccount);
const TransactionsList = accountStore.TransactionsList; const TransactionsList = computed(() => accountStore.TransactionsList);
</script> </script>
<template> <template>

View File

@ -1,8 +1,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted } from 'vue';
import { useSessionStore } from '../stores/session';
onMounted(() => { onMounted(() => {
document.title = "Budgeteer - Admin"; useSessionStore().setTitle("Admin");
}) })
</script> </script>

View File

@ -1,4 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from "vue";
import Currency from "../components/Currency.vue" import Currency from "../components/Currency.vue"
import { useBudgetsStore } from "../stores/budget" import { useBudgetsStore } from "../stores/budget"
import { useAccountStore } from "../stores/budget-account" import { useAccountStore } from "../stores/budget-account"
@ -9,17 +10,17 @@ const props = defineProps<{
accountid: string, accountid: string,
}>(); }>();
const ExpandMenu = useSettingsStore().Menu.Expand; const ExpandMenu = computed(() => useSettingsStore().Menu.Expand);
const budgetStore = useBudgetsStore(); const budgetStore = useBudgetsStore();
const CurrentBudgetName = budgetStore.CurrentBudgetName; const CurrentBudgetName = computed(() => budgetStore.CurrentBudgetName);
const CurrentBudgetID = budgetStore.CurrentBudgetID; const CurrentBudgetID = computed(() => budgetStore.CurrentBudgetID);
const accountStore = useAccountStore(); const accountStore = useAccountStore();
const OnBudgetAccounts = accountStore.OnBudgetAccounts; const OnBudgetAccounts = computed(() => accountStore.OnBudgetAccounts);
const OffBudgetAccounts = accountStore.OffBudgetAccounts; const OffBudgetAccounts = computed(() => accountStore.OffBudgetAccounts);
const OnBudgetAccountsBalance = accountStore.OnBudgetAccountsBalance; const OnBudgetAccountsBalance = computed(() => accountStore.OnBudgetAccountsBalance);
const OffBudgetAccountsBalance = accountStore.OffBudgetAccountsBalance; const OffBudgetAccountsBalance = computed(() => accountStore.OffBudgetAccountsBalance);
</script> </script>
<template> <template>

View File

@ -1,13 +1,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, defineProps, onMounted, PropType, watch, watchEffect } from "vue"; import { computed, defineProps, onMounted, watchEffect } from "vue";
import Currency from "../components/Currency.vue"; import Currency from "../components/Currency.vue";
import { useBudgetsStore } from "../stores/budget"; import { useBudgetsStore } from "../stores/budget";
import { useAccountStore } from "../stores/budget-account"; import { useAccountStore } from "../stores/budget-account";
import { useSessionStore } from "../stores/session";
interface Date {
Year: number,
Month: number,
}
const props = defineProps<{ const props = defineProps<{
budgetid: string, budgetid: string,
@ -22,6 +18,7 @@ const categoriesForMonth = useAccountStore().CategoriesForMonth;
const Categories = computed(() => { const Categories = computed(() => {
return [...categoriesForMonth(selected.value.Year, selected.value.Month)]; return [...categoriesForMonth(selected.value.Year, selected.value.Month)];
}); });
const previous = computed(() => ({ const previous = computed(() => ({
Year: new Date(selected.value.Year, selected.value.Month - 1, 1).getFullYear(), Year: new Date(selected.value.Year, selected.value.Month - 1, 1).getFullYear(),
Month: new Date(selected.value.Year, selected.value.Month - 1, 1).getMonth(), Month: new Date(selected.value.Year, selected.value.Month - 1, 1).getMonth(),
@ -44,9 +41,9 @@ watchEffect(() => {
return useAccountStore().FetchMonthBudget(props.budgetid ?? "", Number(props.year), Number(props.month)); return useAccountStore().FetchMonthBudget(props.budgetid ?? "", Number(props.year), Number(props.month));
}); });
/*{{define "title"}} onMounted(() => {
{{printf "Budget for %s %d" .Date.Month .Date.Year}} useSessionStore().setTitle("Budget for " + selected.value.Month + "/" + selected.value.Year);
{{end}}*/ })
</script> </script>
<template> <template>

View File

@ -7,7 +7,7 @@ const error = ref("");
const login = ref({ user: "", password: "" }); const login = ref({ user: "", password: "" });
onMounted(() => { onMounted(() => {
document.title = "Budgeteer - Login"; useSessionStore().setTitle("Login");
}); });
function formSubmit(e: MouseEvent) { function formSubmit(e: MouseEvent) {

View File

@ -10,7 +10,7 @@ const assignmentsFile = ref<File | undefined>(undefined);
const filesIncomplete = computed(() => transactionsFile.value == undefined || assignmentsFile.value == undefined); const filesIncomplete = computed(() => transactionsFile.value == undefined || assignmentsFile.value == undefined);
onMounted(() => { onMounted(() => {
document.title = "Budgeteer - Settings"; useSessionStore().setTitle("Settings");
}); });
function gotAssignments(e: Event) { function gotAssignments(e: Event) {

View File

@ -11,11 +11,24 @@ interface State {
Assignments: [] Assignments: []
} }
export interface Transaction {
ID: string,
Date: string,
TransferAccount: string,
CategoryGroup: string,
Category:string,
Memo: string,
Status: string,
GroupID: string,
Payee: string,
Amount: number,
}
export interface Account { export interface Account {
ID: string ID: string
Name: string Name: string
OnBudget: boolean OnBudget: boolean
Balance: Number Balance: number
} }
export interface Category { export interface Category {
@ -42,9 +55,10 @@ export const useAccountStore = defineStore("budget/account", {
return [...state.Accounts.values()]; return [...state.Accounts.values()];
}, },
CategoriesForMonth: (state) => (year: number, month: number) => { CategoriesForMonth: (state) => (year: number, month: number) => {
console.log("MTH", state.Months)
const yearMap = state.Months.get(year); const yearMap = state.Months.get(year);
return [ ...yearMap?.get(month)?.values() || [] ]; const monthMap = yearMap?.get(month);
console.log("MTH", monthMap)
return [...monthMap?.values() || []];
}, },
CurrentAccount(state): Account | undefined { CurrentAccount(state): Account | undefined {
if (state.CurrentAccountID == null) if (state.CurrentAccountID == null)
@ -55,13 +69,13 @@ export const useAccountStore = defineStore("budget/account", {
OnBudgetAccounts(state) { OnBudgetAccounts(state) {
return [...state.Accounts.values()].filter(x => x.OnBudget); return [...state.Accounts.values()].filter(x => x.OnBudget);
}, },
OnBudgetAccountsBalance(state) : Number { OnBudgetAccountsBalance(state) : number {
return this.OnBudgetAccounts.reduce((prev, curr) => prev + Number(curr.Balance), 0); return this.OnBudgetAccounts.reduce((prev, curr) => prev + Number(curr.Balance), 0);
}, },
OffBudgetAccounts(state) { OffBudgetAccounts(state) {
return [...state.Accounts.values()].filter(x => !x.OnBudget); return [...state.Accounts.values()].filter(x => !x.OnBudget);
}, },
OffBudgetAccountsBalance(state) : Number { OffBudgetAccountsBalance(state) : number {
return this.OffBudgetAccounts.reduce((prev, curr) => prev + Number(curr.Balance), 0); return this.OffBudgetAccounts.reduce((prev, curr) => prev + Number(curr.Balance), 0);
}, },
TransactionsList(state) { TransactionsList(state) {
@ -91,15 +105,16 @@ export const useAccountStore = defineStore("budget/account", {
this.addCategoriesForMonth(year, month, response.Categories); this.addCategoriesForMonth(year, month, response.Categories);
}, },
addCategoriesForMonth(year: number, month: number, categories: Category[]): void { addCategoriesForMonth(year: number, month: number, categories: Category[]): void {
const yearMap = this.Months.get(year) || new Map<number, Map<string, Category>>(); this.$patch((state) => {
this.Months.set(year, yearMap); const yearMap = state.Months.get(year) || new Map<number, Map<string, Category>>();
const monthMap = yearMap.get(month) || new Map<string, Category>(); const monthMap = yearMap.get(month) || new Map<string, Category>();
yearMap.set(month, monthMap);
for (const category of categories) { for (const category of categories) {
monthMap.set(category.ID, category); monthMap.set(category.ID, category);
} }
yearMap.set(month, monthMap);
state.Months.set(year, yearMap);
});
}, },
logout() { logout() {
this.$reset() this.$reset()