support adding / renaming / deleting / changing display order for tag group on mobile version
This commit is contained in:
@@ -44,6 +44,7 @@ import CategoryEditPage from '@/views/mobile/categories/EditPage.vue';
|
||||
import CategoryPresetPage from '@/views/mobile/categories/PresetPage.vue';
|
||||
|
||||
import TagListPage from '@/views/mobile/tags/ListPage.vue';
|
||||
import TagGroupListPage from '@/views/mobile/tags/GroupListPage.vue';
|
||||
|
||||
import TemplateListPage from '@/views/mobile/templates/ListPage.vue';
|
||||
|
||||
@@ -324,6 +325,11 @@ const routes: Router.RouteParameters[] = [
|
||||
async: asyncResolve(TagListPage),
|
||||
beforeEnter: [checkLogin]
|
||||
},
|
||||
{
|
||||
path: '/tag/group/list',
|
||||
async: asyncResolve(TagGroupListPage),
|
||||
beforeEnter: [checkLogin]
|
||||
},
|
||||
{
|
||||
path: '/template/list',
|
||||
async: asyncResolve(TemplateListPage),
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<f7-page @page:afterin="onPageAfterIn">
|
||||
<f7-navbar>
|
||||
<f7-nav-left :back-link="tt('Back')" v-if="!displayOrderModified"></f7-nav-left>
|
||||
<f7-nav-left v-else-if="displayOrderModified">
|
||||
<f7-link icon-f7="xmark" :class="{ 'disabled': displayOrderSaving }" @click="cancelSort"></f7-link>
|
||||
</f7-nav-left>
|
||||
<f7-nav-title :title="tt('Transaction Tag Groups')"></f7-nav-title>
|
||||
<f7-nav-right class="navbar-compact-icons">
|
||||
<f7-link icon-f7="checkmark_alt" :class="{ 'disabled': displayOrderSaving || !displayOrderModified }" @click="saveSortResult"></f7-link>
|
||||
</f7-nav-right>
|
||||
</f7-navbar>
|
||||
|
||||
<f7-list strong inset dividers class="tag-group-item-list margin-top skeleton-text" v-if="loading">
|
||||
<f7-list-item :key="itemIdx" v-for="itemIdx in [ 1, 2, 3 ]">
|
||||
<template #title>
|
||||
<div class="display-flex">
|
||||
<div class="transaction-tag-group-list-item-content list-item-valign-middle padding-inline-start-half">Tag Group Name</div>
|
||||
</div>
|
||||
</template>
|
||||
</f7-list-item>
|
||||
</f7-list>
|
||||
|
||||
<f7-list strong inset dividers class="tag-group-item-list margin-top" v-if="!loading && tagGroups.length < 1">
|
||||
<f7-list-item :title="tt('No available tag group')"></f7-list-item>
|
||||
</f7-list>
|
||||
|
||||
<f7-list strong inset dividers sortable sortable-enabled
|
||||
class="tag-group-item-list margin-top"
|
||||
@sortable:sort="onSort"
|
||||
v-if="!loading">
|
||||
<f7-list-item :id="getTagGroupDomId(tagGroup)"
|
||||
:key="tagGroup.id"
|
||||
v-for="tagGroup in tagGroups">
|
||||
<template #title>
|
||||
<div class="display-flex">
|
||||
<div class="transaction-tag-group-list-item-content list-item-valign-middle padding-inline-start-half">{{ tagGroup.name }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</f7-list-item>
|
||||
</f7-list>
|
||||
</f7-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
import type { Router } from 'framework7/types';
|
||||
|
||||
import { useI18n } from '@/locales/helpers.ts';
|
||||
import { useI18nUIComponents, showLoading, hideLoading } from '@/lib/ui/mobile.ts';
|
||||
|
||||
import { useTransactionTagsStore } from '@/stores/transactionTag.ts';
|
||||
|
||||
import { TransactionTagGroup } from '@/models/transaction_tag_group.ts';
|
||||
|
||||
const props = defineProps<{
|
||||
f7router: Router.Router;
|
||||
}>();
|
||||
|
||||
const { tt } = useI18n();
|
||||
const { showToast, routeBackOnError } = useI18nUIComponents();
|
||||
|
||||
const transactionTagsStore = useTransactionTagsStore();
|
||||
|
||||
const loading = ref<boolean>(true);
|
||||
const loadingError = ref<unknown | null>(null);
|
||||
const displayOrderModified = ref<boolean>(false);
|
||||
const displayOrderSaving = ref<boolean>(false);
|
||||
|
||||
const tagGroups = computed<TransactionTagGroup[]>(() => transactionTagsStore.allTransactionTagGroups);
|
||||
|
||||
function getTagGroupDomId(tagGroup: TransactionTagGroup): string {
|
||||
return 'tagGroup_' + tagGroup.id;
|
||||
}
|
||||
|
||||
function parseTagGroupIdFromDomId(domId: string): string | null {
|
||||
if (!domId || domId.indexOf('tagGroup_') !== 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return domId.substring(9); // tagGroup_
|
||||
}
|
||||
|
||||
function init(): void {
|
||||
loading.value = true;
|
||||
|
||||
transactionTagsStore.loadAllTagGroups({
|
||||
force: false
|
||||
}).then(() => {
|
||||
loading.value = false;
|
||||
}).catch(error => {
|
||||
if (error.processed) {
|
||||
loading.value = false;
|
||||
} else {
|
||||
loadingError.value = error;
|
||||
showToast(error.message || error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function saveSortResult(): void {
|
||||
if (!displayOrderModified.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
displayOrderSaving.value = true;
|
||||
showLoading();
|
||||
|
||||
transactionTagsStore.updateTagGroupDisplayOrders().then(() => {
|
||||
displayOrderSaving.value = false;
|
||||
hideLoading();
|
||||
|
||||
displayOrderModified.value = false;
|
||||
}).catch(error => {
|
||||
displayOrderSaving.value = false;
|
||||
hideLoading();
|
||||
|
||||
if (!error.processed) {
|
||||
showToast(error.message || error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function cancelSort(): void {
|
||||
if (!displayOrderModified.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
displayOrderSaving.value = true;
|
||||
showLoading();
|
||||
|
||||
transactionTagsStore.loadAllTagGroups({
|
||||
force: false
|
||||
}).then(() => {
|
||||
displayOrderSaving.value = false;
|
||||
hideLoading();
|
||||
|
||||
displayOrderModified.value = false;
|
||||
}).catch(error => {
|
||||
displayOrderSaving.value = false;
|
||||
hideLoading();
|
||||
|
||||
if (!error.processed) {
|
||||
showToast(error.message || error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onSort(event: { el: { id: string }, from: number, to: number }): void {
|
||||
if (!event || !event.el || !event.el.id) {
|
||||
showToast('Unable to move tag group');
|
||||
return;
|
||||
}
|
||||
|
||||
const id = parseTagGroupIdFromDomId(event.el.id);
|
||||
|
||||
if (!id) {
|
||||
showToast('Unable to move tag group');
|
||||
return;
|
||||
}
|
||||
|
||||
transactionTagsStore.changeTagGroupDisplayOrder({
|
||||
tagGroupId: id,
|
||||
from: event.from,
|
||||
to: event.to
|
||||
}).then(() => {
|
||||
displayOrderModified.value = true;
|
||||
}).catch(error => {
|
||||
showToast(error.message || error);
|
||||
});
|
||||
}
|
||||
|
||||
function onPageAfterIn(): void {
|
||||
if (transactionTagsStore.transactionTagGroupListStateInvalid && !loading.value) {
|
||||
transactionTagsStore.loadAllTagGroups({}).catch(error => {
|
||||
if (!error.processed) {
|
||||
showToast(error.message || error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
routeBackOnError(props.f7router, loadingError);
|
||||
}
|
||||
|
||||
init();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.transaction-tag-group-list-item-content {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
@@ -12,7 +12,7 @@
|
||||
</f7-link>
|
||||
</f7-nav-title>
|
||||
<f7-nav-right class="navbar-compact-icons">
|
||||
<f7-link icon-f7="ellipsis" :class="{ 'disabled': hasEditingTag || !tags.length || sortable }" @click="showMoreActionSheet = true"></f7-link>
|
||||
<f7-link icon-f7="ellipsis" :class="{ 'disabled': hasEditingTag || sortable }" @click="showMoreActionSheet = true"></f7-link>
|
||||
<f7-link icon-f7="plus" :class="{ 'disabled': hasEditingTag }" v-if="!sortable" @click="add"></f7-link>
|
||||
<f7-link icon-f7="checkmark_alt" :class="{ 'disabled': displayOrderSaving || !displayOrderModified || hasEditingTag }" @click="saveSortResult" v-else-if="sortable"></f7-link>
|
||||
</f7-nav-right>
|
||||
@@ -178,6 +178,17 @@
|
||||
</f7-popup>
|
||||
|
||||
<f7-actions close-by-outside-click close-on-escape :opened="showMoreActionSheet" @actions:closed="showMoreActionSheet = false">
|
||||
<f7-actions-group>
|
||||
<f7-actions-button @click="addTagGroup">{{ tt('Add Tag Group') }}</f7-actions-button>
|
||||
<f7-actions-button @click="renameTagGroup"
|
||||
v-if="activeTagGroupId && activeTagGroupId !== DEFAULT_TAG_GROUP_ID">{{ tt('Rename Tag Group') }}</f7-actions-button>
|
||||
<f7-actions-button color="red" :class="{ 'disabled': tags && tags.length > 0 }"
|
||||
@click="removeTagGroup"
|
||||
v-if="activeTagGroupId && activeTagGroupId !== DEFAULT_TAG_GROUP_ID">{{ tt('Delete Tag Group') }}</f7-actions-button>
|
||||
</f7-actions-group>
|
||||
<f7-actions-group>
|
||||
<f7-actions-button @click="changeTagGroupDisplayOrder">{{ tt('Change Group Display Order') }}</f7-actions-button>
|
||||
</f7-actions-group>
|
||||
<f7-actions-group>
|
||||
<f7-actions-button @click="setSortable()">{{ tt('Sort') }}</f7-actions-button>
|
||||
<f7-actions-button v-if="!showHidden" @click="showHidden = true">{{ tt('Show Hidden Transaction Tags') }}</f7-actions-button>
|
||||
@@ -211,7 +222,9 @@ import { useTagListPageBase } from '@/views/base/tags/TagListPageBase.ts';
|
||||
import { useTransactionTagsStore } from '@/stores/transactionTag.ts';
|
||||
|
||||
import { TextDirection } from '@/core/text.ts';
|
||||
import { DEFAULT_TAG_GROUP_ID } from '@/consts/tag.ts';
|
||||
|
||||
import { TransactionTagGroup } from '@/models/transaction_tag_group.ts';
|
||||
import { TransactionTag } from '@/models/transaction_tag.ts';
|
||||
|
||||
import { scrollToSelectedItem } from '@/lib/ui/common.ts';
|
||||
@@ -222,7 +235,7 @@ const props = defineProps<{
|
||||
}>();
|
||||
|
||||
const { tt, getCurrentLanguageTextDirection } = useI18n();
|
||||
const { showAlert, showToast, routeBackOnError } = useI18nUIComponents();
|
||||
const { showAlert, showConfirm, showPrompt, showToast, routeBackOnError } = useI18nUIComponents();
|
||||
|
||||
const {
|
||||
activeTagGroupId,
|
||||
@@ -510,6 +523,94 @@ function cancelSort(): void {
|
||||
});
|
||||
}
|
||||
|
||||
function addTagGroup(): void {
|
||||
showPrompt(tt('New Tag Group Name'), '', (value: string) => {
|
||||
showLoading();
|
||||
|
||||
transactionTagsStore.saveTagGroup({
|
||||
tagGroup: TransactionTagGroup.createNewTagGroup(value)
|
||||
}).then(tagGroup => {
|
||||
hideLoading();
|
||||
activeTagGroupId.value = tagGroup.id;
|
||||
}).catch(error => {
|
||||
hideLoading();
|
||||
|
||||
if (!error.processed) {
|
||||
showToast(error.message || error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function renameTagGroup(): void {
|
||||
const tagGroup = transactionTagsStore.allTransactionTagGroupsMap[activeTagGroupId.value];
|
||||
|
||||
if (!tagGroup) {
|
||||
showToast('Unable to rename this tag group');
|
||||
return;
|
||||
}
|
||||
|
||||
showPrompt(tt('Rename Tag Group'), tagGroup.name || '', (value: string) => {
|
||||
showLoading();
|
||||
|
||||
const newTagGroup = tagGroup.clone();
|
||||
newTagGroup.name = value;
|
||||
|
||||
transactionTagsStore.saveTagGroup({
|
||||
tagGroup: newTagGroup
|
||||
}).then(() => {
|
||||
hideLoading();
|
||||
}).catch(error => {
|
||||
hideLoading();
|
||||
|
||||
if (!error.processed) {
|
||||
showToast(error.message || error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function removeTagGroup(): void {
|
||||
const tagGroup = transactionTagsStore.allTransactionTagGroupsMap[activeTagGroupId.value];
|
||||
|
||||
if (!tagGroup) {
|
||||
showToast('Unable to delete this tag group');
|
||||
return;
|
||||
}
|
||||
|
||||
const currentTagGroupIndex = allTagGroupsWithDefault.value.findIndex(group => group.id === tagGroup.id);
|
||||
|
||||
showConfirm('Are you sure you want to delete this tag group?', () => {
|
||||
showLoading();
|
||||
|
||||
transactionTagsStore.deleteTagGroup({
|
||||
tagGroup: tagGroup
|
||||
}).then(() => {
|
||||
hideLoading();
|
||||
|
||||
if (allTagGroupsWithDefault.value[currentTagGroupIndex]) {
|
||||
const newActiveTagGroup = allTagGroupsWithDefault.value[currentTagGroupIndex];
|
||||
activeTagGroupId.value = newActiveTagGroup ? newActiveTagGroup.id : DEFAULT_TAG_GROUP_ID;
|
||||
} else if (allTagGroupsWithDefault.value[currentTagGroupIndex - 1]) {
|
||||
const newActiveTagGroup = allTagGroupsWithDefault.value[currentTagGroupIndex - 1];
|
||||
activeTagGroupId.value = newActiveTagGroup ? newActiveTagGroup.id : DEFAULT_TAG_GROUP_ID;
|
||||
} else {
|
||||
activeTagGroupId.value = DEFAULT_TAG_GROUP_ID;
|
||||
}
|
||||
}).catch(error => {
|
||||
hideLoading();
|
||||
|
||||
if (!error.processed) {
|
||||
showToast(error.message || error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function changeTagGroupDisplayOrder(): void {
|
||||
props.f7router.navigate('/tag/group/list');
|
||||
}
|
||||
|
||||
function onSort(event: { el: { id: string }, from: number, to: number }): void {
|
||||
if (!event || !event.el || !event.el.id) {
|
||||
showToast('Unable to move tag');
|
||||
|
||||
Reference in New Issue
Block a user