diff --git a/src/components/tasks/partials/projectSearch.vue b/src/components/tasks/partials/projectSearch.vue index 72ff6d1f4..07548c8f1 100644 --- a/src/components/tasks/partials/projectSearch.vue +++ b/src/components/tasks/partials/projectSearch.vue @@ -37,6 +37,10 @@ const props = defineProps({ type: Object as PropType, required: false, }, + savedFiltersOnly: { + type: Boolean, + default: false, + }, }) const emit = defineEmits(['update:modelValue']) @@ -57,6 +61,12 @@ function findProjects(query: string) { if (query === '') { select(null) } + + if (props.savedFiltersOnly) { + foundProjects.value = projectStore.searchSavedFilter(query) + return + } + foundProjects.value = projectStore.searchProject(query) } diff --git a/src/i18n/lang/en.json b/src/i18n/lang/en.json index aec9ede75..bd87f8f1a 100644 --- a/src/i18n/lang/en.json +++ b/src/i18n/lang/en.json @@ -86,7 +86,8 @@ "language": "Language", "defaultProject": "Default Project", "timezone": "Time Zone", - "overdueTasksRemindersTime": "Overdue tasks reminder email time" + "overdueTasksRemindersTime": "Overdue tasks reminder email time", + "filterUsedOnOverview": "Saved filter used on the overview page" }, "totp": { "title": "Two Factor Authentication", diff --git a/src/modelTypes/IUserSettings.ts b/src/modelTypes/IUserSettings.ts index 9e3b3fb4b..18430439c 100644 --- a/src/modelTypes/IUserSettings.ts +++ b/src/modelTypes/IUserSettings.ts @@ -9,6 +9,7 @@ export interface IFrontendSettings { playSoundWhenDone: boolean quickAddMagicMode: PrefixMode colorSchema: BasicColorSchema + filterIdUsedOnOverview: IProject['id'] | null } export interface IUserSettings extends IAbstract { diff --git a/src/stores/projects.ts b/src/stores/projects.ts index 0ed9c9f7e..3f509a4f9 100644 --- a/src/stores/projects.ts +++ b/src/stores/projects.ts @@ -17,6 +17,7 @@ import type {MaybeRef} from '@vueuse/core' import ProjectModel from '@/models/project' import {success} from '@/message' import {useBaseStore} from '@/stores/base' +import {getSavedFilterIdFromProjectId} from '@/services/savedFilter' const {remove, search, update} = createNewIndexer('projects', ['title', 'description']) @@ -62,6 +63,16 @@ export const useProjectStore = defineStore('project', () => { || [] } }) + + const searchSavedFilter = computed(() => { + return (query: string, includeArchived = false) => { + return search(query) + ?.filter(value => getSavedFilterIdFromProjectId(value) > 0) + .map(id => projects.value[id]) + .filter(project => project.isArchived === includeArchived) + || [] + } + }) function setIsLoading(newIsLoading: boolean) { isLoading.value = newIsLoading @@ -191,6 +202,7 @@ export const useProjectStore = defineStore('project', () => { getChildProjects, findProjectByExactname, searchProject, + searchSavedFilter, setProject, setProjects, diff --git a/src/stores/tasks.ts b/src/stores/tasks.ts index 3166e353f..3b103af9b 100644 --- a/src/stores/tasks.ts +++ b/src/stores/tasks.ts @@ -29,6 +29,7 @@ import {useKanbanStore} from '@/stores/kanban' import {useBaseStore} from '@/stores/base' import ProjectUserService from '@/services/projectUsers' import {useAuthStore} from '@/stores/auth' +import TaskCollectionService from '@/services/taskCollection' interface MatchedAssignee extends IUser { match: string, @@ -123,12 +124,17 @@ export const useTaskStore = defineStore('task', () => { }) } - async function loadTasks(params) { - const taskService = new TaskService() + async function loadTasks(params, projectId: IProject['id'] | null = null) { const cancel = setModuleLoading(setIsLoading) try { - tasks.value = await taskService.getAll({}, params) + if (projectId === null) { + const taskService = new TaskService() + tasks.value = await taskService.getAll({}, params) + } else { + const taskCollectionService = new TaskCollectionService() + tasks.value = await taskCollectionService.getAll({projectId}, params) + } baseStore.setHasTasks(tasks.value.length > 0) return tasks.value } finally { diff --git a/src/views/tasks/ShowTasks.vue b/src/views/tasks/ShowTasks.vue index 93d166ba5..1048ff2b6 100644 --- a/src/views/tasks/ShowTasks.vue +++ b/src/views/tasks/ShowTasks.vue @@ -59,6 +59,7 @@ import LlamaCool from '@/assets/llama-cool.svg?component' import type {ITask} from '@/modelTypes/ITask' import {useAuthStore} from '@/stores/auth' import {useTaskStore} from '@/stores/tasks' +import {useProjectStore} from '@/stores/projects' const authStore = useAuthStore() const taskStore = useTaskStore() @@ -69,6 +70,8 @@ const {t} = useI18n({useScope: 'global'}) const tasks = ref([]) const showNothingToDo = ref(false) +const projectStore = useProjectStore() + setTimeout(() => showNothingToDo.value = true, 100) // Linting disabled because we explicitely enabled destructuring in vite's config, this will work. @@ -178,6 +181,11 @@ async function loadPendingTasks(from: string, to: string) { params.filterComparator.push('greater') } } + + if (authStore.settings.frontendSettings.filterIdUsedOnOverview && typeof projectStore.projects[authStore.settings.frontendSettings.filterIdUsedOnOverview] !== 'undefined') { + tasks.value = await taskStore.loadTasks(params, authStore.settings.frontendSettings.filterIdUsedOnOverview) + return + } tasks.value = await taskStore.loadTasks(params) } diff --git a/src/views/user/settings/General.vue b/src/views/user/settings/General.vue index 9e53d0c5d..cde9f9fc3 100644 --- a/src/views/user/settings/General.vue +++ b/src/views/user/settings/General.vue @@ -18,6 +18,12 @@ +
+ + +