migrate transaction category edit page to composition API and typescript

This commit is contained in:
MaysWind
2025-01-18 23:47:49 +08:00
parent f5f8b9a145
commit 965be837a3
4 changed files with 346 additions and 337 deletions
+129 -159
View File
@@ -1,10 +1,10 @@
<template>
<f7-page @page:afterin="onPageAfterIn">
<f7-navbar>
<f7-nav-left :back-link="$t('Back')"></f7-nav-left>
<f7-nav-title :title="$t(title)"></f7-nav-title>
<f7-nav-left :back-link="tt('Back')"></f7-nav-left>
<f7-nav-title :title="tt(title)"></f7-nav-title>
<f7-nav-right>
<f7-link :class="{ 'disabled': inputIsEmpty || submitting }" :text="$t(saveButtonTitle)" @click="save"></f7-link>
<f7-link :class="{ 'disabled': inputIsEmpty || submitting }" :text="tt(saveButtonTitle)" @click="save"></f7-link>
</f7-nav-right>
</f7-navbar>
@@ -57,15 +57,15 @@
<f7-list-input
type="text"
clear-button
:label="$t('Category Name')"
:placeholder="$t('Your category name')"
:label="tt('Category Name')"
:placeholder="tt('Your category name')"
v-model:value="category.name"
></f7-list-input>
<f7-list-item
link="#" no-chevron
class="list-item-with-header-and-title"
:header="$t('Primary Category')"
:header="tt('Primary Category')"
:title="getPrimaryCategoryName(category.parentId)"
@click="showPrimaryCategorySheet = true"
v-if="editCategoryId && category.parentId && category.parentId !== '0'"
@@ -87,7 +87,7 @@
<div class="item-content">
<div class="item-inner">
<div class="item-header">
<span>{{ $t('Category Icon') }}</span>
<span>{{ tt('Category Icon') }}</span>
</div>
<div class="item-title">
<div class="list-item-custom-title no-padding">
@@ -98,7 +98,7 @@
</div>
</a>
<icon-selection-sheet :all-icon-infos="allCategoryIcons"
<icon-selection-sheet :all-icon-infos="ALL_CATEGORY_ICONS"
:color="category.color"
v-model:show="showIconSelectionSheet"
v-model="category.icon"
@@ -109,7 +109,7 @@
<div class="item-content">
<div class="item-inner">
<div class="item-header">
<span>{{ $t('Category Color') }}</span>
<span>{{ tt('Category Color') }}</span>
</div>
<div class="item-title">
<div class="list-item-custom-title no-padding">
@@ -120,7 +120,7 @@
</div>
</a>
<color-selection-sheet :all-color-infos="allCategoryColors"
<color-selection-sheet :all-color-infos="ALL_CATEGORY_COLORS"
v-model:show="showColorSelectionSheet"
v-model="category.color"
></color-selection-sheet>
@@ -129,15 +129,15 @@
</template>
</f7-list-item>
<f7-list-item :title="$t('Visible')" v-if="editCategoryId">
<f7-list-item :title="tt('Visible')" v-if="editCategoryId">
<f7-toggle :checked="category.visible" @toggle:change="category.visible = $event"></f7-toggle>
</f7-list-item>
<f7-list-input
type="textarea"
style="height: auto"
:label="$t('Description')"
:placeholder="$t('Your category description (optional)')"
:label="tt('Description')"
:placeholder="tt('Your category description (optional)')"
v-textarea-auto-size
v-model:value="category.comment"
></f7-list-input>
@@ -145,8 +145,14 @@
</f7-page>
</template>
<script>
import { mapStores } from 'pinia';
<script setup lang="ts">
import { ref } from 'vue';
import type { Router } from 'framework7/types';
import { useI18n } from '@/locales/helpers.ts';
import { useI18nUIComponents, showLoading, hideLoading } from '@/lib/ui/mobile.ts';
import { useCategoryEditPageBase } from '@/views/base/categories/CategoryEditPageBase.ts';
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
import { CategoryType } from '@/core/category.ts';
@@ -154,157 +160,121 @@ import { ALL_CATEGORY_ICONS } from '@/consts/icon.ts';
import { ALL_CATEGORY_COLORS } from '@/consts/color.ts';
import { TransactionCategory } from '@/models/transaction_category.ts';
import { getNameByKeyValue } from '@/lib/common.ts';
import { generateRandomUUID } from '@/lib/misc.ts';
import { allVisiblePrimaryTransactionCategoriesByType } from '@/lib/category.ts';
export default {
props: [
'f7route',
'f7router'
],
data() {
const query = this.f7route.query;
const props = defineProps<{
f7route: Router.Route;
f7router: Router.Router;
}>();
return {
editCategoryId: null,
clientSessionId: '',
loading: false,
loadingError: null,
category: TransactionCategory.createNewCategory(parseInt(query.type), query.parentId),
showPrimaryCategorySheet: false,
showIconSelectionSheet: false,
showColorSelectionSheet: false,
submitting: false
};
},
computed: {
...mapStores(useTransactionCategoriesStore),
allAvailableCategories() {
return allVisiblePrimaryTransactionCategoriesByType(this.transactionCategoriesStore.allTransactionCategories, this.category.type);
},
title() {
if (!this.editCategoryId) {
if (this.category.parentId === '0') {
return 'Add Primary Category';
} else {
return 'Add Secondary Category';
}
} else {
return 'Edit Category';
}
},
saveButtonTitle() {
if (!this.editCategoryId) {
return 'Add';
} else {
return 'Save';
}
},
allCategoryIcons() {
return ALL_CATEGORY_ICONS;
},
allCategoryColors() {
return ALL_CATEGORY_COLORS;
},
inputIsEmpty() {
return !!this.inputEmptyProblemMessage;
},
inputEmptyProblemMessage() {
if (!this.category.name) {
return 'Category name cannot be blank';
} else {
return null;
}
}
},
created() {
const self = this;
const query = self.f7route.query;
const query = props.f7route.query;
if (!query.id && !query.parentId) {
self.$toast('Parameter Invalid');
self.loadingError = 'Parameter Invalid';
const { tt } = useI18n();
const { showAlert, showToast, routeBackOnError } = useI18nUIComponents();
const {
editCategoryId,
clientSessionId,
loading,
submitting,
category,
allAvailableCategories,
title,
saveButtonTitle,
inputEmptyProblemMessage,
inputIsEmpty
} = useCategoryEditPageBase(query['type'] ? parseInt(query['type']) as CategoryType : undefined, query['parentId']);
const transactionCategoriesStore = useTransactionCategoriesStore();
const loadingError = ref<unknown | null>(null);
const showPrimaryCategorySheet = ref<boolean>(false);
const showIconSelectionSheet = ref<boolean>(false);
const showColorSelectionSheet = ref<boolean>(false);
function getPrimaryCategoryName(parentId: string): string | null {
return TransactionCategory.findNameById(allAvailableCategories.value, parentId);
}
function init(): void {
if (!query['id'] && !query['parentId']) {
showToast('Parameter Invalid');
loadingError.value = 'Parameter Invalid';
return;
}
if (query['id']) {
loading.value = true;
editCategoryId.value = query['id'];
transactionCategoriesStore.getCategory({
categoryId: editCategoryId.value
}).then(response => {
category.value.from(response);
loading.value = false;
}).catch(error => {
if (error.processed) {
loading.value = false;
} else {
loadingError.value = error;
showToast(error.message || error);
}
});
} else if (query['parentId']) {
const categoryType = query['type'] ? parseInt(query['type']) as CategoryType : undefined;
if (categoryType !== CategoryType.Income &&
categoryType !== CategoryType.Expense &&
categoryType !== CategoryType.Transfer) {
showToast('Parameter Invalid');
loadingError.value = 'Parameter Invalid';
return;
}
if (query.id) {
self.loading = true;
self.editCategoryId = query.id;
self.transactionCategoriesStore.getCategory({
categoryId: self.editCategoryId
}).then(category => {
self.category.from(category);
self.loading = false;
}).catch(error => {
if (error.processed) {
self.loading = false;
} else {
self.loadingError = error;
self.$toast(error.message || error);
}
});
} else if (query.parentId) {
const categoryType = parseInt(query.type);
if (categoryType !== CategoryType.Income &&
categoryType !== CategoryType.Expense &&
categoryType !== CategoryType.Transfer) {
self.$toast('Parameter Invalid');
self.loadingError = 'Parameter Invalid';
return;
}
self.clientSessionId = generateRandomUUID();
self.loading = false;
}
},
methods: {
onPageAfterIn() {
this.$routeBackOnError(this.f7router, 'loadingError');
},
save() {
const self = this;
const router = self.f7router;
const problemMessage = self.inputEmptyProblemMessage;
if (problemMessage) {
self.$alert(problemMessage);
return;
}
self.submitting = true;
self.$showLoading(() => self.submitting);
self.transactionCategoriesStore.saveCategory({
category: self.category,
isEdit: !!self.editCategoryId,
clientSessionId: self.clientSessionId
}).then(() => {
self.submitting = false;
self.$hideLoading();
if (!self.editCategoryId) {
self.$toast('You have added a new category');
} else {
self.$toast('You have saved this category');
}
router.back();
}).catch(error => {
self.submitting = false;
self.$hideLoading();
if (!error.processed) {
self.$toast(error.message || error);
}
});
},
getPrimaryCategoryName(parentId) {
return getNameByKeyValue(this.allAvailableCategories, parentId, 'id', 'name');
}
clientSessionId.value = generateRandomUUID();
loading.value = false;
}
}
function save(): void {
const router = props.f7router;
const problemMessage = inputEmptyProblemMessage.value;
if (problemMessage) {
showAlert(problemMessage);
return;
}
submitting.value = true;
showLoading(() => submitting.value);
transactionCategoriesStore.saveCategory({
category: category.value,
isEdit: !!editCategoryId.value,
clientSessionId: clientSessionId.value
}).then(() => {
submitting.value = false;
hideLoading();
if (!editCategoryId.value) {
showToast('You have added a new category');
} else {
showToast('You have saved this category');
}
router.back();
}).catch(error => {
submitting.value = false;
hideLoading();
if (!error.processed) {
showToast(error.message || error);
}
});
}
function onPageAfterIn(): void {
routeBackOnError(props.f7router, loadingError);
}
init();
</script>