migrate transaction category store to composition API and typescript
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { CategoryType } from '@/core/category.ts';
|
||||
import { TransactionType } from '@/core/transaction.ts';
|
||||
import { type TransactionCategoriesWithVisibleCount, TransactionCategory } from '@/models/transaction_category.ts';
|
||||
|
||||
export function setCategoryModelByAnotherCategory(category, category2) {
|
||||
export function setCategoryModelByAnotherCategory(category: TransactionCategory, category2: TransactionCategory): void {
|
||||
category.id = category2.id;
|
||||
category.type = category2.type;
|
||||
category.parentId = category2.parentId;
|
||||
@@ -12,7 +13,7 @@ export function setCategoryModelByAnotherCategory(category, category2) {
|
||||
category.visible = !category2.hidden;
|
||||
}
|
||||
|
||||
export function transactionTypeToCategoryType(transactionType) {
|
||||
export function transactionTypeToCategoryType(transactionType: TransactionType): CategoryType | null {
|
||||
if (transactionType === TransactionType.Income) {
|
||||
return CategoryType.Income;
|
||||
} else if (transactionType === TransactionType.Expense) {
|
||||
@@ -24,7 +25,7 @@ export function transactionTypeToCategoryType(transactionType) {
|
||||
}
|
||||
}
|
||||
|
||||
export function categoryTypeToTransactionType(categoryType) {
|
||||
export function categoryTypeToTransactionType(categoryType: CategoryType): TransactionType | null {
|
||||
if (categoryType === CategoryType.Income) {
|
||||
return TransactionType.Income;
|
||||
} else if (categoryType === CategoryType.Expense) {
|
||||
@@ -36,14 +37,20 @@ export function categoryTypeToTransactionType(categoryType) {
|
||||
}
|
||||
}
|
||||
|
||||
export function getTransactionPrimaryCategoryName(categoryId, allCategories) {
|
||||
export function getTransactionPrimaryCategoryName(categoryId: string, allCategories: TransactionCategory[]): string {
|
||||
if (!allCategories) {
|
||||
return '';
|
||||
}
|
||||
|
||||
for (let i = 0; i < allCategories.length; i++) {
|
||||
for (let j = 0; j < allCategories[i].subCategories.length; j++) {
|
||||
const subCategory = allCategories[i].subCategories[j];
|
||||
const subCategoryList = allCategories[i].secondaryCategories;
|
||||
|
||||
if (!subCategoryList) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let j = 0; j < subCategoryList.length; j++) {
|
||||
const subCategory = subCategoryList[j];
|
||||
if (subCategory.id === categoryId) {
|
||||
return allCategories[i].name;
|
||||
}
|
||||
@@ -53,14 +60,20 @@ export function getTransactionPrimaryCategoryName(categoryId, allCategories) {
|
||||
return '';
|
||||
}
|
||||
|
||||
export function getTransactionSecondaryCategoryName(categoryId, allCategories) {
|
||||
export function getTransactionSecondaryCategoryName(categoryId: string, allCategories: TransactionCategory[]): string {
|
||||
if (!allCategories) {
|
||||
return '';
|
||||
}
|
||||
|
||||
for (let i = 0; i < allCategories.length; i++) {
|
||||
for (let j = 0; j < allCategories[i].subCategories.length; j++) {
|
||||
const subCategory = allCategories[i].subCategories[j];
|
||||
const subCategoryList = allCategories[i].secondaryCategories;
|
||||
|
||||
if (!subCategoryList) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let j = 0; j < subCategoryList.length; j++) {
|
||||
const subCategory = subCategoryList[j];
|
||||
if (subCategory.id === categoryId) {
|
||||
return subCategory.name;
|
||||
}
|
||||
@@ -70,12 +83,12 @@ export function getTransactionSecondaryCategoryName(categoryId, allCategories) {
|
||||
return '';
|
||||
}
|
||||
|
||||
export function allTransactionCategoriesWithVisibleCount(allTransactionCategories, allowCategoryTypes) {
|
||||
const ret = {};
|
||||
export function allTransactionCategoriesWithVisibleCount(allTransactionCategories: Record<number, TransactionCategory[]>, allowCategoryTypes: Record<number, boolean>): Record<number, TransactionCategoriesWithVisibleCount> {
|
||||
const ret: Record<string, TransactionCategoriesWithVisibleCount> = {};
|
||||
const hasAllowCategoryTypes = allowCategoryTypes
|
||||
&& (allowCategoryTypes[CategoryType.Income.toString()]
|
||||
|| allowCategoryTypes[CategoryType.Expense.toString()]
|
||||
|| allowCategoryTypes[CategoryType.Transfer.toString()]);
|
||||
&& (allowCategoryTypes[CategoryType.Income]
|
||||
|| allowCategoryTypes[CategoryType.Expense]
|
||||
|| allowCategoryTypes[CategoryType.Transfer]);
|
||||
|
||||
const allCategoryTypes = [ CategoryType.Income, CategoryType.Expense, CategoryType.Transfer ];
|
||||
|
||||
@@ -90,10 +103,10 @@ export function allTransactionCategoriesWithVisibleCount(allTransactionCategorie
|
||||
continue;
|
||||
}
|
||||
|
||||
const allCategories = allTransactionCategories[categoryType];
|
||||
const allSubCategories = {};
|
||||
const allVisibleSubCategoryCounts = {};
|
||||
const allFirstVisibleSubCategoryIndexes = {};
|
||||
const allCategories: TransactionCategory[] = allTransactionCategories[categoryType];
|
||||
const allSubCategories: Record<string, TransactionCategory[]> = {};
|
||||
const allVisibleSubCategoryCounts: Record<string, number> = {};
|
||||
const allFirstVisibleSubCategoryIndexes: Record<string, number> = {};
|
||||
let allVisibleCategoryCount = 0;
|
||||
let firstVisibleCategoryIndex = -1;
|
||||
|
||||
@@ -108,12 +121,12 @@ export function allTransactionCategoriesWithVisibleCount(allTransactionCategorie
|
||||
}
|
||||
}
|
||||
|
||||
if (category.subCategories) {
|
||||
if (category.secondaryCategories) {
|
||||
let visibleSubCategoryCount = 0;
|
||||
let firstVisibleSubCategoryIndex = -1;
|
||||
|
||||
for (let k = 0; k < category.subCategories.length; k++) {
|
||||
const subCategory = category.subCategories[k];
|
||||
for (let k = 0; k < category.secondaryCategories.length; k++) {
|
||||
const subCategory = category.secondaryCategories[k];
|
||||
|
||||
if (!subCategory.hidden) {
|
||||
visibleSubCategoryCount++;
|
||||
@@ -124,16 +137,16 @@ export function allTransactionCategoriesWithVisibleCount(allTransactionCategorie
|
||||
}
|
||||
}
|
||||
|
||||
if (category.subCategories.length > 0) {
|
||||
allSubCategories[category.id] = category.subCategories;
|
||||
if (category.secondaryCategories.length > 0) {
|
||||
allSubCategories[category.id] = category.secondaryCategories;
|
||||
allVisibleSubCategoryCounts[category.id] = visibleSubCategoryCount;
|
||||
allFirstVisibleSubCategoryIndexes[category.id] = firstVisibleSubCategoryIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret[categoryType.toString()] = {
|
||||
type: categoryType.toString(),
|
||||
ret[categoryType] = {
|
||||
type: categoryType,
|
||||
allCategories: allCategories,
|
||||
allVisibleCategoryCount: allVisibleCategoryCount,
|
||||
firstVisibleCategoryIndex: firstVisibleCategoryIndex,
|
||||
@@ -146,9 +159,9 @@ export function allTransactionCategoriesWithVisibleCount(allTransactionCategorie
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function allVisiblePrimaryTransactionCategoriesByType(allTransactionCategories, categoryType) {
|
||||
export function allVisiblePrimaryTransactionCategoriesByType(allTransactionCategories: Record<number, TransactionCategory[]>, categoryType: number): TransactionCategory[] {
|
||||
const allCategories = allTransactionCategories[categoryType];
|
||||
const visibleCategories = [];
|
||||
const visibleCategories: TransactionCategory[] = [];
|
||||
|
||||
if (!allCategories) {
|
||||
return visibleCategories;
|
||||
@@ -167,14 +180,14 @@ export function allVisiblePrimaryTransactionCategoriesByType(allTransactionCateg
|
||||
return visibleCategories;
|
||||
}
|
||||
|
||||
export function getFinalCategoryIdsByFilteredCategoryIds(allTransactionCategoriesMap, filteredCategoryIds) {
|
||||
export function getFinalCategoryIdsByFilteredCategoryIds(allTransactionCategoriesMap: Record<number, TransactionCategory>, filteredCategoryIds: Record<string, boolean>): string {
|
||||
let finalCategoryIds = '';
|
||||
|
||||
if (!allTransactionCategoriesMap) {
|
||||
return finalCategoryIds;
|
||||
}
|
||||
|
||||
for (let categoryId in allTransactionCategoriesMap) {
|
||||
for (const categoryId in allTransactionCategoriesMap) {
|
||||
if (!Object.prototype.hasOwnProperty.call(allTransactionCategoriesMap, categoryId)) {
|
||||
continue;
|
||||
}
|
||||
@@ -195,7 +208,7 @@ export function getFinalCategoryIdsByFilteredCategoryIds(allTransactionCategorie
|
||||
return finalCategoryIds;
|
||||
}
|
||||
|
||||
export function isSubCategoryIdAvailable(categories, categoryId) {
|
||||
export function isSubCategoryIdAvailable(categories: TransactionCategory[], categoryId: string): boolean {
|
||||
if (!categories || !categories.length) {
|
||||
return false;
|
||||
}
|
||||
@@ -207,8 +220,14 @@ export function isSubCategoryIdAvailable(categories, categoryId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let j = 0; j < primaryCategory.subCategories.length; j++) {
|
||||
const secondaryCategory = primaryCategory.subCategories[j];
|
||||
const subCategoryList = primaryCategory.secondaryCategories;
|
||||
|
||||
if (!subCategoryList) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let j = 0; j < subCategoryList.length; j++) {
|
||||
const secondaryCategory = subCategoryList[j];
|
||||
|
||||
if (secondaryCategory.hidden) {
|
||||
continue;
|
||||
@@ -223,7 +242,7 @@ export function isSubCategoryIdAvailable(categories, categoryId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function getFirstAvailableCategoryId(categories) {
|
||||
export function getFirstAvailableCategoryId(categories: TransactionCategory[]): string {
|
||||
if (!categories || !categories.length) {
|
||||
return '';
|
||||
}
|
||||
@@ -235,8 +254,14 @@ export function getFirstAvailableCategoryId(categories) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let j = 0; j < primaryCategory.subCategories.length; j++) {
|
||||
const secondaryCategory = primaryCategory.subCategories[j];
|
||||
const subCategoryList = primaryCategory.secondaryCategories;
|
||||
|
||||
if (!subCategoryList) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let j = 0; j < subCategoryList.length; j++) {
|
||||
const secondaryCategory = subCategoryList[j];
|
||||
|
||||
if (secondaryCategory.hidden) {
|
||||
continue;
|
||||
@@ -249,7 +274,7 @@ export function getFirstAvailableCategoryId(categories) {
|
||||
return '';
|
||||
}
|
||||
|
||||
export function getFirstAvailableSubCategoryId(categories, categoryId) {
|
||||
export function getFirstAvailableSubCategoryId(categories: TransactionCategory[], categoryId: string): string {
|
||||
if (!categories || !categories.length) {
|
||||
return '';
|
||||
}
|
||||
@@ -261,8 +286,14 @@ export function getFirstAvailableSubCategoryId(categories, categoryId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let j = 0; j < primaryCategory.subCategories.length; j++) {
|
||||
const secondaryCategory = primaryCategory.subCategories[j];
|
||||
const subCategoryList = primaryCategory.secondaryCategories;
|
||||
|
||||
if (!subCategoryList) {
|
||||
return '';
|
||||
}
|
||||
|
||||
for (let j = 0; j < subCategoryList.length; j++) {
|
||||
const secondaryCategory = subCategoryList[j];
|
||||
|
||||
if (secondaryCategory.hidden) {
|
||||
continue;
|
||||
@@ -277,7 +308,7 @@ export function getFirstAvailableSubCategoryId(categories, categoryId) {
|
||||
return '';
|
||||
}
|
||||
|
||||
export function isNoAvailableCategory(categories, showHidden) {
|
||||
export function isNoAvailableCategory(categories: TransactionCategory[], showHidden: boolean): boolean {
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
if (showHidden || !categories[i].hidden) {
|
||||
return false;
|
||||
@@ -287,7 +318,7 @@ export function isNoAvailableCategory(categories, showHidden) {
|
||||
return true;
|
||||
}
|
||||
|
||||
export function getAvailableCategoryCount(categories, showHidden) {
|
||||
export function getAvailableCategoryCount(categories: TransactionCategory[], showHidden: boolean): number {
|
||||
let count = 0;
|
||||
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
@@ -299,7 +330,7 @@ export function getAvailableCategoryCount(categories, showHidden) {
|
||||
return count;
|
||||
}
|
||||
|
||||
export function getFirstShowingId(categories, showHidden) {
|
||||
export function getFirstShowingId(categories: TransactionCategory[], showHidden: boolean): string | null {
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
if (showHidden || !categories[i].hidden) {
|
||||
return categories[i].id;
|
||||
@@ -309,7 +340,7 @@ export function getFirstShowingId(categories, showHidden) {
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getLastShowingId(categories, showHidden) {
|
||||
export function getLastShowingId(categories: TransactionCategory[], showHidden: boolean): string | null {
|
||||
for (let i = categories.length - 1; i >= 0; i--) {
|
||||
if (showHidden || !categories[i].hidden) {
|
||||
return categories[i].id;
|
||||
@@ -319,8 +350,8 @@ export function getLastShowingId(categories, showHidden) {
|
||||
return null;
|
||||
}
|
||||
|
||||
export function hasAnyAvailableCategory(allTransactionCategories, showHidden) {
|
||||
for (let type in allTransactionCategories) {
|
||||
export function hasAnyAvailableCategory(allTransactionCategories: Record<number, TransactionCategoriesWithVisibleCount>, showHidden: boolean): boolean {
|
||||
for (const type in allTransactionCategories) {
|
||||
if (!Object.prototype.hasOwnProperty.call(allTransactionCategories, type)) {
|
||||
continue;
|
||||
}
|
||||
@@ -341,10 +372,10 @@ export function hasAnyAvailableCategory(allTransactionCategories, showHidden) {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function hasAvailableCategory(allTransactionCategories, showHidden) {
|
||||
const result = {};
|
||||
export function hasAvailableCategory(allTransactionCategories: Record<number, TransactionCategoriesWithVisibleCount>, showHidden: boolean): Record<number, boolean> {
|
||||
const result: Record<number, boolean> = {};
|
||||
|
||||
for (let type in allTransactionCategories) {
|
||||
for (const type in allTransactionCategories) {
|
||||
if (!Object.prototype.hasOwnProperty.call(allTransactionCategories, type)) {
|
||||
continue;
|
||||
}
|
||||
@@ -361,19 +392,19 @@ export function hasAvailableCategory(allTransactionCategories, showHidden) {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function selectSubCategories(filterCategoryIds, category, value) {
|
||||
if (!category || !category.subCategories || !category.subCategories.length) {
|
||||
export function selectSubCategories(filterCategoryIds: Record<string, boolean>, category: TransactionCategory, value: boolean): void {
|
||||
if (!category || !category.secondaryCategories || !category.secondaryCategories.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < category.subCategories.length; i++) {
|
||||
const subCategory = category.subCategories[i];
|
||||
for (let i = 0; i < category.secondaryCategories.length; i++) {
|
||||
const subCategory = category.secondaryCategories[i];
|
||||
filterCategoryIds[subCategory.id] = value;
|
||||
}
|
||||
}
|
||||
|
||||
export function selectAll(filterCategoryIds, allTransactionCategoriesMap) {
|
||||
for (let categoryId in filterCategoryIds) {
|
||||
export function selectAll(filterCategoryIds: Record<string, boolean>, allTransactionCategoriesMap: Record<string, TransactionCategory>): void {
|
||||
for (const categoryId in filterCategoryIds) {
|
||||
if (!Object.prototype.hasOwnProperty.call(filterCategoryIds, categoryId)) {
|
||||
continue;
|
||||
}
|
||||
@@ -386,8 +417,8 @@ export function selectAll(filterCategoryIds, allTransactionCategoriesMap) {
|
||||
}
|
||||
}
|
||||
|
||||
export function selectNone(filterCategoryIds, allTransactionCategoriesMap) {
|
||||
for (let categoryId in filterCategoryIds) {
|
||||
export function selectNone(filterCategoryIds: Record<string, boolean>, allTransactionCategoriesMap: Record<string, TransactionCategory>): void {
|
||||
for (const categoryId in filterCategoryIds) {
|
||||
if (!Object.prototype.hasOwnProperty.call(filterCategoryIds, categoryId)) {
|
||||
continue;
|
||||
}
|
||||
@@ -400,8 +431,8 @@ export function selectNone(filterCategoryIds, allTransactionCategoriesMap) {
|
||||
}
|
||||
}
|
||||
|
||||
export function selectInvert(filterCategoryIds, allTransactionCategoriesMap) {
|
||||
for (let categoryId in filterCategoryIds) {
|
||||
export function selectInvert(filterCategoryIds: Record<string, boolean>, allTransactionCategoriesMap: Record<string, TransactionCategory>): void {
|
||||
for (const categoryId in filterCategoryIds) {
|
||||
if (!Object.prototype.hasOwnProperty.call(filterCategoryIds, categoryId)) {
|
||||
continue;
|
||||
}
|
||||
@@ -414,13 +445,13 @@ export function selectInvert(filterCategoryIds, allTransactionCategoriesMap) {
|
||||
}
|
||||
}
|
||||
|
||||
export function isCategoryOrSubCategoriesAllChecked(category, filterCategoryIds) {
|
||||
if (!category.subCategories) {
|
||||
export function isCategoryOrSubCategoriesAllChecked(category: TransactionCategory, filterCategoryIds: Record<string, boolean>): boolean {
|
||||
if (!category.secondaryCategories || category.secondaryCategories.length < 1) {
|
||||
return !filterCategoryIds[category.id];
|
||||
}
|
||||
|
||||
for (let i = 0; i < category.subCategories.length; i++) {
|
||||
const subCategory = category.subCategories[i];
|
||||
for (let i = 0; i < category.secondaryCategories.length; i++) {
|
||||
const subCategory = category.secondaryCategories[i];
|
||||
if (filterCategoryIds[subCategory.id]) {
|
||||
return false;
|
||||
}
|
||||
@@ -429,9 +460,13 @@ export function isCategoryOrSubCategoriesAllChecked(category, filterCategoryIds)
|
||||
return true;
|
||||
}
|
||||
|
||||
export function isSubCategoriesAllChecked(category, filterCategoryIds) {
|
||||
for (let i = 0; i < category.subCategories.length; i++) {
|
||||
const subCategory = category.subCategories[i];
|
||||
export function isSubCategoriesAllChecked(category: TransactionCategory, filterCategoryIds: Record<string, boolean>): boolean {
|
||||
if (!category.secondaryCategories || category.secondaryCategories.length < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < category.secondaryCategories.length; i++) {
|
||||
const subCategory = category.secondaryCategories[i];
|
||||
if (filterCategoryIds[subCategory.id]) {
|
||||
return false;
|
||||
}
|
||||
@@ -440,15 +475,19 @@ export function isSubCategoriesAllChecked(category, filterCategoryIds) {
|
||||
return true;
|
||||
}
|
||||
|
||||
export function isSubCategoriesHasButNotAllChecked(category, filterCategoryIds) {
|
||||
export function isSubCategoriesHasButNotAllChecked(category: TransactionCategory, filterCategoryIds: Record<string, boolean>): boolean {
|
||||
let checkedCount = 0;
|
||||
|
||||
for (let i = 0; i < category.subCategories.length; i++) {
|
||||
const subCategory = category.subCategories[i];
|
||||
if (!category.secondaryCategories || category.secondaryCategories.length < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < category.secondaryCategories.length; i++) {
|
||||
const subCategory = category.secondaryCategories[i];
|
||||
if (!filterCategoryIds[subCategory.id]) {
|
||||
checkedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return checkedCount > 0 && checkedCount < category.subCategories.length;
|
||||
return checkedCount > 0 && checkedCount < category.secondaryCategories.length;
|
||||
}
|
||||
+2
-2
@@ -445,8 +445,8 @@ export default {
|
||||
removeUnusedTransactionPicture: (req: TransactionPictureUnusedDeleteRequest): ApiResponsePromise<boolean> => {
|
||||
return axios.post<ApiResponse<boolean>>('v1/transaction/pictures/remove_unused.json', req);
|
||||
},
|
||||
getAllTransactionCategories: (): ApiResponsePromise<TransactionCategoryInfoResponse[]> => {
|
||||
return axios.get<ApiResponse<TransactionCategoryInfoResponse[]>>('v1/transaction/categories/list.json');
|
||||
getAllTransactionCategories: (): ApiResponsePromise<Record<number, TransactionCategoryInfoResponse[]>> => {
|
||||
return axios.get<ApiResponse<Record<number, TransactionCategoryInfoResponse[]>>>('v1/transaction/categories/list.json');
|
||||
},
|
||||
getTransactionCategory: (req: { id: string }): ApiResponsePromise<TransactionCategoryInfoResponse> => {
|
||||
return axios.get<ApiResponse<TransactionCategoryInfoResponse>>('v1/transaction/categories/get.json?id=' + req.id);
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
isSubCategoryIdAvailable,
|
||||
getFirstAvailableCategoryId,
|
||||
getFirstAvailableSubCategoryId
|
||||
} from './category.js';
|
||||
} from './category.ts';
|
||||
|
||||
function getDisplayAmount(amount, currency, hideAmount, formatAmountWithCurrencyFunc) {
|
||||
if (hideAmount) {
|
||||
|
||||
@@ -1,3 +1,125 @@
|
||||
import { CategoryType } from '@/core/category.ts';
|
||||
import { DEFAULT_CATEGORY_ICON_ID } from '@/consts/icon.ts';
|
||||
import { DEFAULT_CATEGORY_COLOR } from '@/consts/color.ts';
|
||||
|
||||
export class TransactionCategory implements TransactionCategoryInfoResponse {
|
||||
public id: string;
|
||||
public name: string;
|
||||
public parentId: string;
|
||||
public type: number;
|
||||
public icon: string;
|
||||
public color: string;
|
||||
public comment: string;
|
||||
public displayOrder: number;
|
||||
public visible: boolean;
|
||||
public secondaryCategories?: TransactionCategory[];
|
||||
|
||||
private constructor(id: string, name: string, parentId: string, type: number, icon: string, color: string, comment: string, displayOrder: number, visible: boolean, secondaryCategories?: TransactionCategory[]) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.parentId = parentId;
|
||||
this.type = type;
|
||||
this.icon = icon;
|
||||
this.color = color;
|
||||
this.comment = comment;
|
||||
this.displayOrder = displayOrder;
|
||||
this.visible = visible;
|
||||
|
||||
if (secondaryCategories) {
|
||||
this.secondaryCategories = secondaryCategories;
|
||||
} else if (!secondaryCategories && (!parentId || parentId === '0')) {
|
||||
this.secondaryCategories = [];
|
||||
}
|
||||
}
|
||||
|
||||
get hidden(): boolean {
|
||||
return !this.visible;
|
||||
}
|
||||
|
||||
get subCategories(): TransactionCategoryInfoResponse[] | undefined {
|
||||
if (typeof this.secondaryCategories === 'undefined') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const ret: TransactionCategoryInfoResponse[] = [];
|
||||
|
||||
if (this.secondaryCategories) {
|
||||
for (const subCategory of this.secondaryCategories) {
|
||||
ret.push(subCategory);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public toCreateRequest(clientSessionId: string): TransactionCategoryCreateRequest {
|
||||
return {
|
||||
name: this.name,
|
||||
type: this.type,
|
||||
parentId: this.parentId,
|
||||
icon: this.icon,
|
||||
color: this.color,
|
||||
comment: this.comment,
|
||||
clientSessionId: clientSessionId
|
||||
};
|
||||
}
|
||||
|
||||
public toModifyRequest(): TransactionCategoryModifyRequest {
|
||||
return {
|
||||
id: this.id,
|
||||
name: this.name,
|
||||
parentId: this.parentId,
|
||||
icon: this.icon,
|
||||
color: this.color,
|
||||
comment: this.comment,
|
||||
hidden: !this.visible
|
||||
};
|
||||
}
|
||||
|
||||
public static of(categoryResponse: TransactionCategoryInfoResponse): TransactionCategory {
|
||||
return new TransactionCategory(
|
||||
categoryResponse.id,
|
||||
categoryResponse.name,
|
||||
categoryResponse.parentId,
|
||||
categoryResponse.type,
|
||||
categoryResponse.icon,
|
||||
categoryResponse.color,
|
||||
categoryResponse.comment,
|
||||
categoryResponse.displayOrder,
|
||||
!categoryResponse.hidden,
|
||||
categoryResponse.subCategories ? TransactionCategory.ofMany(categoryResponse.subCategories) : undefined
|
||||
);
|
||||
}
|
||||
|
||||
public static ofMany(categoryResponses: TransactionCategoryInfoResponse[]): TransactionCategory[] {
|
||||
const tags: TransactionCategory[] = [];
|
||||
|
||||
for (const tagResponse of categoryResponses) {
|
||||
tags.push(TransactionCategory.of(tagResponse));
|
||||
}
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
public static ofMap(categoriesByType: Record<number, TransactionCategoryInfoResponse[]>): Record<number, TransactionCategory[]> {
|
||||
const ret: Record<number, TransactionCategory[]> = {};
|
||||
|
||||
for (const categoryType in categoriesByType) {
|
||||
if (!Object.prototype.hasOwnProperty.call(categoriesByType, categoryType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret[categoryType] = TransactionCategory.ofMany(categoriesByType[categoryType]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static createNewCategory(type?: CategoryType, parentId?: string): TransactionCategory {
|
||||
return new TransactionCategory('', '', parentId || '0', type || CategoryType.Income, DEFAULT_CATEGORY_ICON_ID, DEFAULT_CATEGORY_COLOR, '', 0, true);
|
||||
}
|
||||
}
|
||||
|
||||
export interface TransactionCategoryCreateRequest {
|
||||
readonly name: string;
|
||||
readonly type: number;
|
||||
@@ -61,3 +183,13 @@ export interface TransactionCategoryInfoResponse {
|
||||
readonly hidden: boolean;
|
||||
readonly subCategories?: TransactionCategoryInfoResponse[];
|
||||
}
|
||||
|
||||
export interface TransactionCategoriesWithVisibleCount {
|
||||
readonly type: number;
|
||||
readonly allCategories: TransactionCategory[];
|
||||
readonly allVisibleCategoryCount: number;
|
||||
readonly firstVisibleCategoryIndex: number;
|
||||
readonly allSubCategories: Record<string, TransactionCategory[]>;
|
||||
readonly allVisibleSubCategoryCounts: Record<string, number>;
|
||||
readonly allFirstVisibleSubCategoryIndexes: Record<string, number>;
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import { defineStore } from 'pinia';
|
||||
import { useSettingsStore } from './setting.ts';
|
||||
import { useUserStore } from './user.ts';
|
||||
import { useAccountsStore } from './account.js';
|
||||
import { useTransactionCategoriesStore } from './transactionCategory.js';
|
||||
import { useTransactionCategoriesStore } from './transactionCategory.ts';
|
||||
import { useTransactionTagsStore } from './transactionTag.ts';
|
||||
import { useTransactionTemplatesStore } from './transactionTemplate.js';
|
||||
import { useTransactionsStore } from './transaction.js';
|
||||
|
||||
@@ -3,7 +3,7 @@ import { defineStore } from 'pinia';
|
||||
import { useSettingsStore } from './setting.ts';
|
||||
import { useUserStore } from './user.ts';
|
||||
import { useAccountsStore } from './account.js';
|
||||
import { useTransactionCategoriesStore } from './transactionCategory.js';
|
||||
import { useTransactionCategoriesStore } from './transactionCategory.ts';
|
||||
import { useExchangeRatesStore } from './exchangeRates.ts';
|
||||
|
||||
import { DateRangeScene, DateRange } from '@/core/datetime';
|
||||
@@ -43,7 +43,7 @@ import {
|
||||
} from '@/lib/account.js';
|
||||
import {
|
||||
getFinalCategoryIdsByFilteredCategoryIds
|
||||
} from '@/lib/category.js';
|
||||
} from '@/lib/category.ts';
|
||||
import {
|
||||
sortStatisticsItems
|
||||
} from '@/lib/statistics.ts';
|
||||
|
||||
@@ -3,7 +3,7 @@ import { defineStore } from 'pinia';
|
||||
import { useSettingsStore } from './setting.ts';
|
||||
import { useUserStore } from './user.ts';
|
||||
import { useAccountsStore } from './account.js';
|
||||
import { useTransactionCategoriesStore } from './transactionCategory.js';
|
||||
import { useTransactionCategoriesStore } from './transactionCategory.ts';
|
||||
import { useOverviewStore } from './overview.ts';
|
||||
import { useStatisticsStore } from './statistics.js';
|
||||
import { useExchangeRatesStore } from './exchangeRates.ts';
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
} from '@/lib/datetime.ts';
|
||||
import { getAmountWithDecimalNumberCount } from '@/lib/numeral.ts';
|
||||
import { getCurrencyFraction } from '@/lib/currency.ts';
|
||||
import { getFirstAvailableCategoryId } from '@/lib/category.js';
|
||||
import { getFirstAvailableCategoryId } from '@/lib/category.ts';
|
||||
|
||||
const emptyTransactionResult = {
|
||||
items: [],
|
||||
|
||||
@@ -1,533 +0,0 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
import { CategoryType } from '@/core/category.ts';
|
||||
import { DEFAULT_CATEGORY_ICON_ID } from '@/consts/icon.ts';
|
||||
import { DEFAULT_CATEGORY_COLOR } from '@/consts/color.ts';
|
||||
import { isEquals } from '@/lib/common.ts';
|
||||
import services from '@/lib/services.ts';
|
||||
import logger from '@/lib/logger.ts';
|
||||
|
||||
function loadTransactionCategoryList(state, allCategories) {
|
||||
state.allTransactionCategories = allCategories;
|
||||
state.allTransactionCategoriesMap = {};
|
||||
|
||||
for (let categoryType in allCategories) {
|
||||
if (!Object.prototype.hasOwnProperty.call(allCategories, categoryType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const categories = allCategories[categoryType];
|
||||
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
const category = categories[i];
|
||||
state.allTransactionCategoriesMap[category.id] = category;
|
||||
|
||||
for (let j = 0; j < category.subCategories.length; j++) {
|
||||
const subCategory = category.subCategories[j];
|
||||
state.allTransactionCategoriesMap[subCategory.id] = subCategory;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addCategoryToTransactionCategoryList(state, category) {
|
||||
let categoryList = null;
|
||||
|
||||
if (!category.parentId || category.parentId === '0') {
|
||||
categoryList = state.allTransactionCategories[category.type];
|
||||
} else if (state.allTransactionCategoriesMap[category.parentId]) {
|
||||
categoryList = state.allTransactionCategoriesMap[category.parentId].subCategories;
|
||||
}
|
||||
|
||||
if (categoryList) {
|
||||
categoryList.push(category);
|
||||
}
|
||||
|
||||
state.allTransactionCategoriesMap[category.id] = category;
|
||||
}
|
||||
|
||||
function updateCategoryInTransactionCategoryList(state, category, oldCategory) {
|
||||
if (oldCategory && category.parentId !== oldCategory.parentId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let categoryList = null;
|
||||
|
||||
if (!category.parentId || category.parentId === '0') {
|
||||
categoryList = state.allTransactionCategories[category.type];
|
||||
} else if (state.allTransactionCategoriesMap[category.parentId]) {
|
||||
categoryList = state.allTransactionCategoriesMap[category.parentId].subCategories;
|
||||
}
|
||||
|
||||
if (categoryList) {
|
||||
for (let i = 0; i < categoryList.length; i++) {
|
||||
if (categoryList[i].id === category.id) {
|
||||
if (!category.parentId || category.parentId === '0') {
|
||||
category.subCategories = categoryList[i].subCategories;
|
||||
}
|
||||
|
||||
categoryList.splice(i, 1, category);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state.allTransactionCategoriesMap[category.id] = category;
|
||||
return true;
|
||||
}
|
||||
|
||||
function updateCategoryDisplayOrderInCategoryList(state, { category, from, to }) {
|
||||
let categoryList = null;
|
||||
|
||||
if (!category.parentId || category.parentId === '0') {
|
||||
categoryList = state.allTransactionCategories[category.type];
|
||||
} else if (state.allTransactionCategoriesMap[category.parentId]) {
|
||||
categoryList = state.allTransactionCategoriesMap[category.parentId].subCategories;
|
||||
}
|
||||
|
||||
if (categoryList) {
|
||||
categoryList.splice(to, 0, categoryList.splice(from, 1)[0]);
|
||||
}
|
||||
}
|
||||
|
||||
function updateCategoryVisibilityInTransactionCategoryList(state, { category, hidden }) {
|
||||
if (state.allTransactionCategoriesMap[category.id]) {
|
||||
state.allTransactionCategoriesMap[category.id].hidden = hidden;
|
||||
}
|
||||
}
|
||||
|
||||
function removeCategoryFromTransactionCategoryList(state, category) {
|
||||
let categoryList = null;
|
||||
|
||||
if (!category.parentId || category.parentId === '0') {
|
||||
categoryList = state.allTransactionCategories[category.type];
|
||||
} else if (state.allTransactionCategoriesMap[category.parentId]) {
|
||||
categoryList = state.allTransactionCategoriesMap[category.parentId].subCategories;
|
||||
}
|
||||
|
||||
if (categoryList) {
|
||||
for (let i = 0; i < categoryList.length; i++) {
|
||||
if (categoryList[i].id === category.id) {
|
||||
categoryList.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state.allTransactionCategoriesMap[category.id] && state.allTransactionCategoriesMap[category.id].subCategories) {
|
||||
const subCategories = state.allTransactionCategoriesMap[category.id].subCategories;
|
||||
|
||||
for (let i = 0; i < subCategories.length; i++) {
|
||||
const subCategory = subCategories[i];
|
||||
if (state.allTransactionCategoriesMap[subCategory.id]) {
|
||||
delete state.allTransactionCategoriesMap[subCategory.id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state.allTransactionCategoriesMap[category.id]) {
|
||||
delete state.allTransactionCategoriesMap[category.id];
|
||||
}
|
||||
}
|
||||
|
||||
export const useTransactionCategoriesStore = defineStore('transactionCategories', {
|
||||
state: () => ({
|
||||
allTransactionCategories: {},
|
||||
allTransactionCategoriesMap: {},
|
||||
transactionCategoryListStateInvalid: true,
|
||||
}),
|
||||
actions: {
|
||||
generateNewTransactionCategoryModel(type, parentId) {
|
||||
return {
|
||||
type: type || CategoryType.Income,
|
||||
name: '',
|
||||
parentId: parentId || '0',
|
||||
icon: DEFAULT_CATEGORY_ICON_ID,
|
||||
color: DEFAULT_CATEGORY_COLOR,
|
||||
comment: '',
|
||||
visible: true
|
||||
};
|
||||
},
|
||||
updateTransactionCategoryListInvalidState(invalidState) {
|
||||
this.transactionCategoryListStateInvalid = invalidState;
|
||||
},
|
||||
resetTransactionCategories() {
|
||||
this.allTransactionCategories = {};
|
||||
this.allTransactionCategoriesMap = {};
|
||||
this.transactionCategoryListStateInvalid = true;
|
||||
},
|
||||
loadAllCategories({ force }) {
|
||||
const self = this;
|
||||
|
||||
if (!force && !self.transactionCategoryListStateInvalid) {
|
||||
return new Promise((resolve) => {
|
||||
resolve(self.allTransactionCategories);
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getAllTransactionCategories().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to retrieve category list' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data.result[CategoryType.Income]) {
|
||||
data.result[CategoryType.Income] = [];
|
||||
}
|
||||
|
||||
if (!data.result[CategoryType.Expense]) {
|
||||
data.result[CategoryType.Expense] = [];
|
||||
}
|
||||
|
||||
if (!data.result[CategoryType.Transfer]) {
|
||||
data.result[CategoryType.Transfer] = [];
|
||||
}
|
||||
|
||||
for (let categoryType in data.result) {
|
||||
if (!Object.prototype.hasOwnProperty.call(data.result, categoryType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const categories = data.result[categoryType];
|
||||
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
const category = categories[i];
|
||||
|
||||
if (!category.subCategories) {
|
||||
category.subCategories = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (self.transactionCategoryListStateInvalid) {
|
||||
self.updateTransactionCategoryListInvalidState(false);
|
||||
}
|
||||
|
||||
if (force && data.result && isEquals(self.allTransactionCategories, data.result)) {
|
||||
reject({ message: 'Category list is up to date' });
|
||||
return;
|
||||
}
|
||||
|
||||
loadTransactionCategoryList(self, data.result);
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
if (force) {
|
||||
logger.error('failed to force load category list', error);
|
||||
} else {
|
||||
logger.error('failed to load category list', error);
|
||||
}
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to retrieve category list' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
getCategory({ categoryId }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getTransactionCategory({
|
||||
id: categoryId
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to retrieve category' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to load category info', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to retrieve category' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
saveCategory({ category, isEdit, clientSessionId }) {
|
||||
const self = this;
|
||||
|
||||
const submitCategory = {
|
||||
type: category.type,
|
||||
name: category.name,
|
||||
parentId: category.parentId,
|
||||
icon: category.icon,
|
||||
color: category.color,
|
||||
comment: category.comment
|
||||
};
|
||||
|
||||
if (clientSessionId) {
|
||||
submitCategory.clientSessionId = clientSessionId;
|
||||
}
|
||||
|
||||
if (isEdit) {
|
||||
submitCategory.id = category.id;
|
||||
submitCategory.hidden = !category.visible;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let promise = null;
|
||||
|
||||
if (!submitCategory.id) {
|
||||
promise = services.addTransactionCategory(submitCategory);
|
||||
} else {
|
||||
promise = services.modifyTransactionCategory(submitCategory);
|
||||
}
|
||||
|
||||
promise.then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
if (!submitCategory.id) {
|
||||
reject({ message: 'Unable to add category' });
|
||||
} else {
|
||||
reject({ message: 'Unable to save category' });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data.result.subCategories) {
|
||||
data.result.subCategories = [];
|
||||
}
|
||||
|
||||
if (!submitCategory.id) {
|
||||
addCategoryToTransactionCategoryList(self, data.result);
|
||||
} else {
|
||||
const result = updateCategoryInTransactionCategoryList(self, data.result, self.allTransactionCategoriesMap[submitCategory.id]);
|
||||
|
||||
if (!result && !self.transactionCategoryListStateInvalid) {
|
||||
self.updateTransactionCategoryListInvalidState(true);
|
||||
}
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to save category', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
if (!submitCategory.id) {
|
||||
reject({ message: 'Unable to add category' });
|
||||
} else {
|
||||
reject({ message: 'Unable to save category' });
|
||||
}
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
addCategories({ categories }) {
|
||||
const self = this;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.addTransactionCategoryBatch({
|
||||
categories: categories
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to add preset categories' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self.transactionCategoryListStateInvalid) {
|
||||
self.updateTransactionCategoryListInvalidState(true);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to add preset categories', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to add preset categories' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
changeCategoryDisplayOrder({ categoryId, from, to }) {
|
||||
const self = this;
|
||||
const category = self.allTransactionCategoriesMap[categoryId];
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!category) {
|
||||
reject({ message: 'Unable to move category' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!category.parentId || category.parentId === '0') {
|
||||
if (!self.allTransactionCategories[category.type] ||
|
||||
!self.allTransactionCategories[category.type][to]) {
|
||||
reject({ message: 'Unable to move category' });
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!self.allTransactionCategoriesMap[category.parentId].subCategories ||
|
||||
!self.allTransactionCategoriesMap[category.parentId].subCategories[to]) {
|
||||
reject({ message: 'Unable to move category' });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!self.transactionCategoryListStateInvalid) {
|
||||
self.updateTransactionCategoryListInvalidState(true);
|
||||
}
|
||||
|
||||
updateCategoryDisplayOrderInCategoryList(self, {
|
||||
category: category,
|
||||
from: from,
|
||||
to: to
|
||||
});
|
||||
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
updateCategoryDisplayOrders({ type, parentId }) {
|
||||
const self = this;
|
||||
const newDisplayOrders = [];
|
||||
|
||||
let categoryList = null;
|
||||
|
||||
if (!parentId || parentId === '0') {
|
||||
categoryList = self.allTransactionCategories[type];
|
||||
} else if (self.allTransactionCategoriesMap[parentId]) {
|
||||
categoryList = self.allTransactionCategoriesMap[parentId].subCategories;
|
||||
}
|
||||
|
||||
if (categoryList) {
|
||||
for (let i = 0; i < categoryList.length; i++) {
|
||||
newDisplayOrders.push({
|
||||
id: categoryList[i].id,
|
||||
displayOrder: i + 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.moveTransactionCategory({
|
||||
newDisplayOrders: newDisplayOrders
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to move category' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.transactionCategoryListStateInvalid) {
|
||||
self.updateTransactionCategoryListInvalidState(false);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to save categories display order', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to move category' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
hideCategory({ category, hidden }) {
|
||||
const self = this;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.hideTransactionCategory({
|
||||
id: category.id,
|
||||
hidden: hidden
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
if (hidden) {
|
||||
reject({ message: 'Unable to hide this category' });
|
||||
} else {
|
||||
reject({ message: 'Unable to unhide this category' });
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
updateCategoryVisibilityInTransactionCategoryList(self, {
|
||||
category: category,
|
||||
hidden: hidden
|
||||
});
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to change category visibility', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
if (hidden) {
|
||||
reject({ message: 'Unable to hide this category' });
|
||||
} else {
|
||||
reject({ message: 'Unable to unhide this category' });
|
||||
}
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
deleteCategory({ category, beforeResolve }) {
|
||||
const self = this;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.deleteTransactionCategory({
|
||||
id: category.id
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to delete this category' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (beforeResolve) {
|
||||
beforeResolve(() => {
|
||||
removeCategoryFromTransactionCategoryList(self, category);
|
||||
});
|
||||
} else {
|
||||
removeCategoryFromTransactionCategoryList(self, category);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to delete category', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to delete this category' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,518 @@
|
||||
import { ref } from 'vue';
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
import type { BeforeResolveFunction } from '@/core/base.ts';
|
||||
|
||||
import { CategoryType } from '@/core/category.ts';
|
||||
|
||||
import {
|
||||
type TransactionCategoryInfoResponse,
|
||||
type TransactionCategoryCreateBatchRequest,
|
||||
type TransactionCategoryNewDisplayOrderRequest,
|
||||
TransactionCategory,
|
||||
} from '@/models/transaction_category.ts';
|
||||
|
||||
import { isEquals } from '@/lib/common.ts';
|
||||
import services, { type ApiResponsePromise } from '@/lib/services.ts';
|
||||
import logger from '@/lib/logger.ts';
|
||||
|
||||
export const useTransactionCategoriesStore = defineStore('transactionCategories', () =>{
|
||||
const allTransactionCategories = ref<Record<number, TransactionCategory[]>>({});
|
||||
const allTransactionCategoriesMap = ref<Record<string, TransactionCategory>>({});
|
||||
const transactionCategoryListStateInvalid = ref<boolean>(true);
|
||||
|
||||
function loadTransactionCategoryList(allCategories: Record<number, TransactionCategory[]>): void {
|
||||
allTransactionCategories.value = allCategories;
|
||||
allTransactionCategoriesMap.value = {};
|
||||
|
||||
for (const categoryType in allCategories) {
|
||||
if (!Object.prototype.hasOwnProperty.call(allCategories, categoryType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const categories = allCategories[categoryType];
|
||||
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
const category = categories[i];
|
||||
allTransactionCategoriesMap.value[category.id] = category;
|
||||
|
||||
if (!category.secondaryCategories) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let j = 0; j < category.secondaryCategories.length; j++) {
|
||||
const subCategory = category.secondaryCategories[j];
|
||||
allTransactionCategoriesMap.value[subCategory.id] = subCategory;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addCategoryToTransactionCategoryList(category: TransactionCategory): void {
|
||||
let categoryList: TransactionCategory[] | undefined = undefined;
|
||||
|
||||
if (!category.parentId || category.parentId === '0') {
|
||||
categoryList = allTransactionCategories.value[category.type];
|
||||
} else if (allTransactionCategoriesMap.value[category.parentId]) {
|
||||
categoryList = allTransactionCategoriesMap.value[category.parentId].secondaryCategories;
|
||||
}
|
||||
|
||||
if (categoryList) {
|
||||
categoryList.push(category);
|
||||
}
|
||||
|
||||
allTransactionCategoriesMap.value[category.id] = category;
|
||||
}
|
||||
|
||||
function updateCategoryInTransactionCategoryList(category: TransactionCategory, oldCategory: TransactionCategory): boolean {
|
||||
if (oldCategory && category.parentId !== oldCategory.parentId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let categoryList: TransactionCategory[] | undefined = undefined;
|
||||
|
||||
if (!category.parentId || category.parentId === '0') {
|
||||
categoryList = allTransactionCategories.value[category.type];
|
||||
} else if (allTransactionCategoriesMap.value[category.parentId]) {
|
||||
categoryList = allTransactionCategoriesMap.value[category.parentId].secondaryCategories;
|
||||
}
|
||||
|
||||
if (categoryList) {
|
||||
for (let i = 0; i < categoryList.length; i++) {
|
||||
if (categoryList[i].id === category.id) {
|
||||
if (!category.parentId || category.parentId === '0') {
|
||||
category.secondaryCategories = categoryList[i].secondaryCategories;
|
||||
}
|
||||
|
||||
categoryList.splice(i, 1, category);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allTransactionCategoriesMap.value[category.id] = category;
|
||||
return true;
|
||||
}
|
||||
|
||||
function updateCategoryDisplayOrderInCategoryList(params: { category: TransactionCategory, from: number, to: number }): void {
|
||||
let categoryList: TransactionCategory[] | undefined = undefined;
|
||||
|
||||
if (!params.category.parentId || params.category.parentId === '0') {
|
||||
categoryList = allTransactionCategories.value[params.category.type];
|
||||
} else if (allTransactionCategoriesMap.value[params.category.parentId]) {
|
||||
categoryList = allTransactionCategoriesMap.value[params.category.parentId].secondaryCategories;
|
||||
}
|
||||
|
||||
if (categoryList) {
|
||||
categoryList.splice(params.to, 0, categoryList.splice(params.from, 1)[0]);
|
||||
}
|
||||
}
|
||||
|
||||
function updateCategoryVisibilityInTransactionCategoryList(params: { category: TransactionCategory, hidden: boolean }): void {
|
||||
if (allTransactionCategoriesMap.value[params.category.id]) {
|
||||
allTransactionCategoriesMap.value[params.category.id].visible = !params.hidden;
|
||||
}
|
||||
}
|
||||
|
||||
function removeCategoryFromTransactionCategoryList(category: TransactionCategory): void {
|
||||
let categoryList: TransactionCategory[] | undefined = undefined;
|
||||
|
||||
if (!category.parentId || category.parentId === '0') {
|
||||
categoryList = allTransactionCategories.value[category.type];
|
||||
} else if (allTransactionCategoriesMap.value[category.parentId]) {
|
||||
categoryList = allTransactionCategoriesMap.value[category.parentId].secondaryCategories;
|
||||
}
|
||||
|
||||
if (categoryList) {
|
||||
for (let i = 0; i < categoryList.length; i++) {
|
||||
if (categoryList[i].id === category.id) {
|
||||
categoryList.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allTransactionCategoriesMap.value[category.id] && allTransactionCategoriesMap.value[category.id].secondaryCategories) {
|
||||
const subCategoryList = allTransactionCategoriesMap.value[category.id].secondaryCategories;
|
||||
|
||||
if (subCategoryList) {
|
||||
for (let i = 0; i < subCategoryList.length; i++) {
|
||||
const subCategory = subCategoryList[i];
|
||||
if (allTransactionCategoriesMap.value[subCategory.id]) {
|
||||
delete allTransactionCategoriesMap.value[subCategory.id];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allTransactionCategoriesMap.value[category.id]) {
|
||||
delete allTransactionCategoriesMap.value[category.id];
|
||||
}
|
||||
}
|
||||
|
||||
function updateTransactionCategoryListInvalidState(invalidState: boolean): void {
|
||||
transactionCategoryListStateInvalid.value = invalidState;
|
||||
}
|
||||
|
||||
function resetTransactionCategories(): void {
|
||||
allTransactionCategories.value = {};
|
||||
allTransactionCategoriesMap.value = {};
|
||||
transactionCategoryListStateInvalid.value = true;
|
||||
}
|
||||
|
||||
function loadAllCategories(params: { force?: boolean }): Promise<Record<number, TransactionCategory[]>> {
|
||||
if (!params.force && !transactionCategoryListStateInvalid.value) {
|
||||
return new Promise((resolve) => {
|
||||
resolve(allTransactionCategories.value);
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getAllTransactionCategories().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to retrieve category list' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data.result[CategoryType.Income]) {
|
||||
data.result[CategoryType.Income] = [];
|
||||
}
|
||||
|
||||
if (!data.result[CategoryType.Expense]) {
|
||||
data.result[CategoryType.Expense] = [];
|
||||
}
|
||||
|
||||
if (!data.result[CategoryType.Transfer]) {
|
||||
data.result[CategoryType.Transfer] = [];
|
||||
}
|
||||
|
||||
if (transactionCategoryListStateInvalid.value) {
|
||||
updateTransactionCategoryListInvalidState(false);
|
||||
}
|
||||
|
||||
const transactionCategories = TransactionCategory.ofMap(data.result);
|
||||
|
||||
if (params.force && data.result && isEquals(allTransactionCategories.value, transactionCategories)) {
|
||||
reject({ message: 'Category list is up to date' });
|
||||
return;
|
||||
}
|
||||
|
||||
loadTransactionCategoryList(transactionCategories);
|
||||
|
||||
resolve(transactionCategories);
|
||||
}).catch(error => {
|
||||
if (params.force) {
|
||||
logger.error('failed to force load category list', error);
|
||||
} else {
|
||||
logger.error('failed to load category list', error);
|
||||
}
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to retrieve category list' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getCategory(params: { categoryId: string }): Promise<TransactionCategory> {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.getTransactionCategory({
|
||||
id: params.categoryId
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to retrieve category' });
|
||||
return;
|
||||
}
|
||||
|
||||
const transactionCategory = TransactionCategory.of(data.result);
|
||||
|
||||
resolve(transactionCategory);
|
||||
}).catch(error => {
|
||||
logger.error('failed to load category info', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to retrieve category' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function saveCategory(params: { category: TransactionCategory, isEdit: boolean, clientSessionId: string }): Promise<TransactionCategory> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let promise: ApiResponsePromise<TransactionCategoryInfoResponse>;
|
||||
|
||||
if (!params.isEdit) {
|
||||
promise = services.addTransactionCategory(params.category.toCreateRequest(params.clientSessionId));
|
||||
} else {
|
||||
promise = services.modifyTransactionCategory(params.category.toModifyRequest());
|
||||
}
|
||||
|
||||
promise.then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
if (!params.isEdit) {
|
||||
reject({ message: 'Unable to add category' });
|
||||
} else {
|
||||
reject({ message: 'Unable to save category' });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const transactionCategory = TransactionCategory.of(data.result);
|
||||
|
||||
if (!params.isEdit) {
|
||||
addCategoryToTransactionCategoryList(transactionCategory);
|
||||
} else {
|
||||
const result = updateCategoryInTransactionCategoryList(transactionCategory, allTransactionCategoriesMap.value[params.category.id]);
|
||||
|
||||
if (!result && !transactionCategoryListStateInvalid.value) {
|
||||
updateTransactionCategoryListInvalidState(true);
|
||||
}
|
||||
}
|
||||
|
||||
resolve(transactionCategory);
|
||||
}).catch(error => {
|
||||
logger.error('failed to save category', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
if (!params.isEdit) {
|
||||
reject({ message: 'Unable to add category' });
|
||||
} else {
|
||||
reject({ message: 'Unable to save category' });
|
||||
}
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function addCategories(req: TransactionCategoryCreateBatchRequest): Promise<Record<number, TransactionCategory[]>> {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.addTransactionCategoryBatch(req).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to add preset categories' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!transactionCategoryListStateInvalid.value) {
|
||||
updateTransactionCategoryListInvalidState(true);
|
||||
}
|
||||
|
||||
const transactionCategories = TransactionCategory.ofMap(data.result);
|
||||
|
||||
resolve(transactionCategories);
|
||||
}).catch(error => {
|
||||
logger.error('failed to add preset categories', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to add preset categories' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function changeCategoryDisplayOrder(params: { categoryId: string, from: number, to: number }): Promise<void> {
|
||||
const category = allTransactionCategoriesMap.value[params.categoryId];
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!category) {
|
||||
reject({ message: 'Unable to move category' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!category.parentId || category.parentId === '0') {
|
||||
if (!allTransactionCategories.value[category.type] ||
|
||||
!allTransactionCategories.value[category.type][params.to]) {
|
||||
reject({ message: 'Unable to move category' });
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const subCategoryList = allTransactionCategoriesMap.value[category.parentId].secondaryCategories;
|
||||
|
||||
if (!subCategoryList || !subCategoryList[params.to]) {
|
||||
reject({ message: 'Unable to move category' });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!transactionCategoryListStateInvalid.value) {
|
||||
updateTransactionCategoryListInvalidState(true);
|
||||
}
|
||||
|
||||
updateCategoryDisplayOrderInCategoryList({
|
||||
category: category,
|
||||
from: params.from,
|
||||
to: params.to
|
||||
});
|
||||
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function updateCategoryDisplayOrders(params: { type: CategoryType, parentId: string }): Promise<boolean> {
|
||||
const newDisplayOrders: TransactionCategoryNewDisplayOrderRequest[] = [];
|
||||
|
||||
let categoryList: TransactionCategory[] | undefined = undefined;
|
||||
|
||||
if (!params.parentId || params.parentId === '0') {
|
||||
categoryList = allTransactionCategories.value[params.type];
|
||||
} else if (allTransactionCategoriesMap.value[params.parentId]) {
|
||||
categoryList = allTransactionCategoriesMap.value[params.parentId].secondaryCategories;
|
||||
}
|
||||
|
||||
if (categoryList) {
|
||||
for (let i = 0; i < categoryList.length; i++) {
|
||||
newDisplayOrders.push({
|
||||
id: categoryList[i].id,
|
||||
displayOrder: i + 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
services.moveTransactionCategory({
|
||||
newDisplayOrders: newDisplayOrders
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to move category' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (transactionCategoryListStateInvalid.value) {
|
||||
updateTransactionCategoryListInvalidState(false);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to save categories display order', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to move category' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function hideCategory(params: { category: TransactionCategory, hidden: boolean }): Promise<boolean> {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.hideTransactionCategory({
|
||||
id: params.category.id,
|
||||
hidden: params.hidden
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
if (params.hidden) {
|
||||
reject({ message: 'Unable to hide this category' });
|
||||
} else {
|
||||
reject({ message: 'Unable to unhide this category' });
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
updateCategoryVisibilityInTransactionCategoryList({
|
||||
category: params.category,
|
||||
hidden: params.hidden
|
||||
});
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to change category visibility', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
if (params.hidden) {
|
||||
reject({ message: 'Unable to hide this category' });
|
||||
} else {
|
||||
reject({ message: 'Unable to unhide this category' });
|
||||
}
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function deleteCategory(params: { category: TransactionCategory, beforeResolve: BeforeResolveFunction }): Promise<boolean> {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.deleteTransactionCategory({
|
||||
id: params.category.id
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to delete this category' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (params.beforeResolve) {
|
||||
params.beforeResolve(() => {
|
||||
removeCategoryFromTransactionCategoryList(params.category);
|
||||
});
|
||||
} else {
|
||||
removeCategoryFromTransactionCategoryList(params.category);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to delete category', error);
|
||||
|
||||
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else if (!error.processed) {
|
||||
reject({ message: 'Unable to delete this category' });
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
// states
|
||||
allTransactionCategories,
|
||||
allTransactionCategoriesMap,
|
||||
transactionCategoryListStateInvalid,
|
||||
// functions
|
||||
updateTransactionCategoryListInvalidState,
|
||||
resetTransactionCategories,
|
||||
loadAllCategories,
|
||||
getCategory,
|
||||
saveCategory,
|
||||
addCategories,
|
||||
changeCategoryDisplayOrder,
|
||||
updateCategoryDisplayOrders,
|
||||
hideCategory,
|
||||
deleteCategory
|
||||
};
|
||||
});
|
||||
@@ -241,7 +241,7 @@ import { mapStores } from 'pinia';
|
||||
import { useRootStore } from '@/stores/index.js';
|
||||
import { useSettingsStore } from '@/stores/setting.ts';
|
||||
import { useUserStore } from '@/stores/user.ts';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
import { useExchangeRatesStore } from '@/stores/exchangeRates.ts';
|
||||
|
||||
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
|
||||
|
||||
@@ -198,13 +198,13 @@ import PresetDialog from './list/dialogs/PresetDialog.vue';
|
||||
import { useDisplay } from 'vuetify';
|
||||
|
||||
import { mapStores } from 'pinia';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
|
||||
import { CategoryType } from '@/core/category.ts';
|
||||
import {
|
||||
isNoAvailableCategory,
|
||||
getAvailableCategoryCount
|
||||
} from '@/lib/category.js';
|
||||
} from '@/lib/category.ts';
|
||||
import { getNavSideBarOuterHeight } from '@/lib/ui/desktop.ts';
|
||||
|
||||
import {
|
||||
|
||||
@@ -95,16 +95,18 @@
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
|
||||
import { CategoryType } from '@/core/category.ts';
|
||||
import { ALL_CATEGORY_ICONS } from '@/consts/icon.ts';
|
||||
import { ALL_CATEGORY_COLORS } from '@/consts/color.ts';
|
||||
import { TransactionCategory } from '@/models/transaction_category.ts';
|
||||
|
||||
import { generateRandomUUID } from '@/lib/misc.ts';
|
||||
import {
|
||||
setCategoryModelByAnotherCategory,
|
||||
allVisiblePrimaryTransactionCategoriesByType
|
||||
} from '@/lib/category.js';
|
||||
} from '@/lib/category.ts';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
@@ -115,15 +117,12 @@ export default {
|
||||
'open'
|
||||
],
|
||||
data() {
|
||||
const transactionCategoriesStore = useTransactionCategoriesStore();
|
||||
const newTransactionCategory = transactionCategoriesStore.generateNewTransactionCategoryModel();
|
||||
|
||||
return {
|
||||
showState: false,
|
||||
editCategoryId: null,
|
||||
clientSessionId: '',
|
||||
loading: false,
|
||||
category: newTransactionCategory,
|
||||
category: TransactionCategory.createNewCategory(),
|
||||
submitting: false,
|
||||
resolve: null,
|
||||
reject: null
|
||||
@@ -176,7 +175,7 @@ export default {
|
||||
self.loading = true;
|
||||
self.submitting = false;
|
||||
|
||||
const newTransactionCategory = self.transactionCategoriesStore.generateNewTransactionCategoryModel();
|
||||
const newTransactionCategory = TransactionCategory.createNewCategory();
|
||||
setCategoryModelByAnotherCategory(self.category, newTransactionCategory);
|
||||
|
||||
if (options.id) {
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
|
||||
import { CategoryType } from '@/core/category.ts';
|
||||
import { categorizedArrayToPlainArray } from '@/lib/common.ts';
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useSettingsStore } from '@/stores/setting.ts';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
import { useTransactionsStore } from '@/stores/transaction.js';
|
||||
import { useStatisticsStore } from '@/stores/statistics.js';
|
||||
|
||||
@@ -162,7 +162,7 @@ import {
|
||||
isCategoryOrSubCategoriesAllChecked,
|
||||
isSubCategoriesAllChecked,
|
||||
isSubCategoriesHasButNotAllChecked
|
||||
} from '@/lib/category.js';
|
||||
} from '@/lib/category.ts';
|
||||
|
||||
import {
|
||||
mdiSelectAll,
|
||||
@@ -187,9 +187,9 @@ export default {
|
||||
return {
|
||||
loading: true,
|
||||
expandCategoryTypes: [
|
||||
CategoryType.Income.toString(),
|
||||
CategoryType.Expense.toString(),
|
||||
CategoryType.Transfer.toString()
|
||||
CategoryType.Income,
|
||||
CategoryType.Expense,
|
||||
CategoryType.Transfer
|
||||
],
|
||||
filterCategoryIds: {},
|
||||
showHidden: false,
|
||||
@@ -388,11 +388,11 @@ export default {
|
||||
},
|
||||
getCategoryTypeName(categoryType) {
|
||||
switch (categoryType) {
|
||||
case CategoryType.Income.toString():
|
||||
case CategoryType.Income:
|
||||
return this.$t('Income Categories');
|
||||
case CategoryType.Expense.toString():
|
||||
case CategoryType.Expense:
|
||||
return this.$t('Expense Categories');
|
||||
case CategoryType.Transfer.toString():
|
||||
case CategoryType.Transfer:
|
||||
return this.$t('Transfer Categories');
|
||||
default:
|
||||
return this.$t('Transaction Categories');
|
||||
|
||||
@@ -322,7 +322,7 @@ import { mapStores } from 'pinia';
|
||||
import { useSettingsStore } from '@/stores/setting.ts';
|
||||
import { useUserStore } from '@/stores/user.ts';
|
||||
import { useAccountsStore } from '@/stores/account.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
import { useStatisticsStore } from '@/stores/statistics.js';
|
||||
|
||||
import { DateRangeScene, DateRange } from '@/core/datetime.ts';
|
||||
|
||||
@@ -591,7 +591,7 @@ import { mapStores } from 'pinia';
|
||||
import { useSettingsStore } from '@/stores/setting.ts';
|
||||
import { useUserStore } from '@/stores/user.ts';
|
||||
import { useAccountsStore } from '@/stores/account.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
import { useTransactionTagsStore } from '@/stores/transactionTag.ts';
|
||||
import { useTransactionsStore } from '@/stores/transaction.js';
|
||||
import { useTransactionTemplatesStore } from '@/stores/transactionTemplate.js';
|
||||
@@ -627,7 +627,7 @@ import {
|
||||
import {
|
||||
categoryTypeToTransactionType,
|
||||
transactionTypeToCategoryType
|
||||
} from '@/lib/category.js';
|
||||
} from '@/lib/category.ts';
|
||||
import { getUnifiedSelectedAccountsCurrencyOrDefaultCurrency } from '@/lib/account.js';
|
||||
import { getTransactionDisplayAmount } from '@/lib/transaction.js';
|
||||
import { isDataImportingEnabled } from '@/lib/server_settings.ts';
|
||||
|
||||
@@ -175,7 +175,7 @@ import { mapStores } from 'pinia';
|
||||
import { useSettingsStore } from '@/stores/setting.ts';
|
||||
import { useUserStore } from '@/stores/user.ts';
|
||||
import { useAccountsStore } from '@/stores/account.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
import { useTransactionTagsStore } from '@/stores/transactionTag.ts';
|
||||
import { useExchangeRatesStore } from '@/stores/exchangeRates.ts';
|
||||
|
||||
@@ -187,7 +187,7 @@ import {
|
||||
getTransactionPrimaryCategoryName,
|
||||
getTransactionSecondaryCategoryName,
|
||||
getFirstAvailableCategoryId
|
||||
} from '@/lib/category.js';
|
||||
} from '@/lib/category.ts';
|
||||
|
||||
import {
|
||||
mdiPound
|
||||
|
||||
@@ -394,7 +394,7 @@ import { mapStores } from 'pinia';
|
||||
import { useSettingsStore } from '@/stores/setting.ts';
|
||||
import { useUserStore } from '@/stores/user.ts';
|
||||
import { useAccountsStore } from '@/stores/account.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
import { useTransactionTagsStore } from '@/stores/transactionTag.ts';
|
||||
import { useTransactionsStore } from '@/stores/transaction.js';
|
||||
import { useTransactionTemplatesStore } from '@/stores/transactionTemplate.js';
|
||||
@@ -421,7 +421,7 @@ import {
|
||||
getTransactionPrimaryCategoryName,
|
||||
getTransactionSecondaryCategoryName,
|
||||
getFirstAvailableCategoryId
|
||||
} from '@/lib/category.js';
|
||||
} from '@/lib/category.ts';
|
||||
import { setTransactionModelByTransaction } from '@/lib/transaction.js';
|
||||
import {
|
||||
isTransactionPicturesEnabled,
|
||||
|
||||
@@ -600,7 +600,7 @@ import { mapStores } from 'pinia';
|
||||
import { useSettingsStore } from '@/stores/setting.ts';
|
||||
import { useUserStore } from '@/stores/user.ts';
|
||||
import { useAccountsStore } from '@/stores/account.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
import { useTransactionTagsStore } from '@/stores/transactionTag.ts';
|
||||
import { useTransactionsStore } from '@/stores/transaction.js';
|
||||
import { useOverviewStore } from '@/stores/overview.ts';
|
||||
@@ -628,7 +628,7 @@ import {
|
||||
getTransactionPrimaryCategoryName,
|
||||
getTransactionSecondaryCategoryName,
|
||||
getFirstAvailableCategoryId
|
||||
} from '@/lib/category.js';
|
||||
} from '@/lib/category.ts';
|
||||
|
||||
import {
|
||||
mdiFilterOutline,
|
||||
|
||||
@@ -179,7 +179,7 @@ import { mapStores } from 'pinia';
|
||||
import { useRootStore } from '@/stores/index.js';
|
||||
import { useSettingsStore } from '@/stores/setting.ts';
|
||||
import { useUserStore } from '@/stores/user.ts';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
import { useExchangeRatesStore } from '@/stores/exchangeRates.ts';
|
||||
|
||||
import { CategoryType } from '@/core/category.ts';
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
<template #default>
|
||||
<div class="grid grid-cols-2">
|
||||
<div class="list-item-subitem no-chevron">
|
||||
<a class="item-link" href="#" @click="category.showIconSelectionSheet = true">
|
||||
<a class="item-link" href="#" @click="showIconSelectionSheet = true">
|
||||
<div class="item-content">
|
||||
<div class="item-inner">
|
||||
<div class="item-header">
|
||||
@@ -100,12 +100,12 @@
|
||||
|
||||
<icon-selection-sheet :all-icon-infos="allCategoryIcons"
|
||||
:color="category.color"
|
||||
v-model:show="category.showIconSelectionSheet"
|
||||
v-model:show="showIconSelectionSheet"
|
||||
v-model="category.icon"
|
||||
></icon-selection-sheet>
|
||||
</div>
|
||||
<div class="list-item-subitem no-chevron">
|
||||
<a class="item-link" href="#" @click="category.showColorSelectionSheet = true">
|
||||
<a class="item-link" href="#" @click="showColorSelectionSheet = true">
|
||||
<div class="item-content">
|
||||
<div class="item-inner">
|
||||
<div class="item-header">
|
||||
@@ -121,7 +121,7 @@
|
||||
</a>
|
||||
|
||||
<color-selection-sheet :all-color-infos="allCategoryColors"
|
||||
v-model:show="category.showColorSelectionSheet"
|
||||
v-model:show="showColorSelectionSheet"
|
||||
v-model="category.color"
|
||||
></color-selection-sheet>
|
||||
</div>
|
||||
@@ -147,17 +147,19 @@
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
|
||||
import { CategoryType } from '@/core/category.ts';
|
||||
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 {
|
||||
setCategoryModelByAnotherCategory,
|
||||
allVisiblePrimaryTransactionCategoriesByType
|
||||
} from '@/lib/category.js';
|
||||
} from '@/lib/category.ts';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
@@ -165,19 +167,17 @@ export default {
|
||||
'f7router'
|
||||
],
|
||||
data() {
|
||||
const transactionCategoriesStore = useTransactionCategoriesStore();
|
||||
const query = this.f7route.query;
|
||||
const newTransactionCategory = transactionCategoriesStore.generateNewTransactionCategoryModel(parseInt(query.type), query.parentId);
|
||||
newTransactionCategory.showIconSelectionSheet = false;
|
||||
newTransactionCategory.showColorSelectionSheet = false;
|
||||
|
||||
return {
|
||||
editCategoryId: null,
|
||||
clientSessionId: '',
|
||||
loading: false,
|
||||
loadingError: null,
|
||||
category: newTransactionCategory,
|
||||
category: TransactionCategory.createNewCategory(parseInt(query.type), query.parentId),
|
||||
showPrimaryCategorySheet: false,
|
||||
showIconSelectionSheet: false,
|
||||
showColorSelectionSheet: false,
|
||||
submitting: false
|
||||
};
|
||||
},
|
||||
|
||||
@@ -88,14 +88,14 @@
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
|
||||
import { CategoryType } from '@/core/category.ts';
|
||||
import {
|
||||
isNoAvailableCategory,
|
||||
getFirstShowingId,
|
||||
getLastShowingId
|
||||
} from '@/lib/category.js';
|
||||
} from '@/lib/category.ts';
|
||||
import { onSwipeoutDeleted } from '@/lib/ui/mobile.ts';
|
||||
|
||||
export default {
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
|
||||
import { CategoryType } from '@/core/category.ts';
|
||||
import { getObjectOwnFieldCount, categorizedArrayToPlainArray } from '@/lib/common.ts';
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
import { useSettingsStore } from '@/stores/setting.ts';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
import { useTransactionsStore } from '@/stores/transaction.js';
|
||||
import { useStatisticsStore } from '@/stores/statistics.js';
|
||||
|
||||
@@ -158,7 +158,7 @@ import {
|
||||
isCategoryOrSubCategoriesAllChecked,
|
||||
isSubCategoriesAllChecked,
|
||||
isSubCategoriesHasButNotAllChecked
|
||||
} from '@/lib/category.js';
|
||||
} from '@/lib/category.ts';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
@@ -357,11 +357,11 @@ export default {
|
||||
},
|
||||
getCategoryTypeName(categoryType) {
|
||||
switch (categoryType) {
|
||||
case CategoryType.Income.toString():
|
||||
case CategoryType.Income:
|
||||
return this.$t('Income Categories');
|
||||
case CategoryType.Expense.toString():
|
||||
case CategoryType.Expense:
|
||||
return this.$t('Expense Categories');
|
||||
case CategoryType.Transfer.toString():
|
||||
case CategoryType.Transfer:
|
||||
return this.$t('Transfer Categories');
|
||||
default:
|
||||
return this.$t('Transaction Categories');
|
||||
|
||||
@@ -328,7 +328,7 @@ import { mapStores } from 'pinia';
|
||||
import { useSettingsStore } from '@/stores/setting.ts';
|
||||
import { useUserStore } from '@/stores/user.ts';
|
||||
import { useAccountsStore } from '@/stores/account.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
import { useStatisticsStore } from '@/stores/statistics.js';
|
||||
|
||||
import { DateRangeScene, DateRange } from '@/core/datetime.ts';
|
||||
|
||||
@@ -434,7 +434,7 @@ import { useSettingsStore } from '@/stores/setting.ts';
|
||||
import { useEnvironmentsStore } from '@/stores/environment.ts';
|
||||
import { useUserStore } from '@/stores/user.ts';
|
||||
import { useAccountsStore } from '@/stores/account.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
import { useTransactionTagsStore } from '@/stores/transactionTag.ts';
|
||||
import { useTransactionsStore } from '@/stores/transaction.js';
|
||||
import { useTransactionTemplatesStore } from '@/stores/transactionTemplate.js';
|
||||
@@ -463,7 +463,7 @@ import {
|
||||
getTransactionPrimaryCategoryName,
|
||||
getTransactionSecondaryCategoryName,
|
||||
getFirstAvailableCategoryId
|
||||
} from '@/lib/category.js';
|
||||
} from '@/lib/category.ts';
|
||||
import { setTransactionModelByTransaction } from '@/lib/transaction.js';
|
||||
import {
|
||||
isTransactionPicturesEnabled,
|
||||
|
||||
@@ -524,7 +524,7 @@ import { mapStores } from 'pinia';
|
||||
import { useSettingsStore } from '@/stores/setting.ts';
|
||||
import { useUserStore } from '@/stores/user.ts';
|
||||
import { useAccountsStore } from '@/stores/account.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js';
|
||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||
import { useTransactionTagsStore } from '@/stores/transactionTag.ts';
|
||||
import { useTransactionsStore } from '@/stores/transaction.js';
|
||||
|
||||
@@ -548,7 +548,7 @@ import {
|
||||
getDateRangeByDateType,
|
||||
getDateRangeByBillingCycleDateType
|
||||
} from '@/lib/datetime.ts';
|
||||
import { categoryTypeToTransactionType, transactionTypeToCategoryType } from '@/lib/category.js';
|
||||
import { categoryTypeToTransactionType, transactionTypeToCategoryType } from '@/lib/category.ts';
|
||||
import { getUnifiedSelectedAccountsCurrencyOrDefaultCurrency } from '@/lib/account.js';
|
||||
import { getTransactionDisplayAmount } from '@/lib/transaction.js';
|
||||
import { onSwipeoutDeleted, scrollToSelectedItem } from '@/lib/ui/mobile.ts';
|
||||
|
||||
Reference in New Issue
Block a user