diff --git a/frontend/src/i18n/lang/en.json b/frontend/src/i18n/lang/en.json index 3b1121b85..711d50778 100644 --- a/frontend/src/i18n/lang/en.json +++ b/frontend/src/i18n/lang/en.json @@ -807,7 +807,9 @@ "overdue": "Show overdue tasks", "fromuntil": "Tasks from {from} until {until}", "select": "Select a date range", - "noTasks": "Nothing to do — Have a nice day!" + "noTasks": "Nothing to do — Have a nice day!", + "filterByLabel": "Filtering by label {label}", + "clearLabelFilter": "Clear label filter" }, "detail": { "chooseDueDate": "Click here to set a due date", diff --git a/frontend/src/views/Home.vue b/frontend/src/views/Home.vue index fbbdc35c3..98cd1a1ce 100644 --- a/frontend/src/views/Home.vue +++ b/frontend/src/views/Home.vue @@ -38,14 +38,17 @@ diff --git a/frontend/src/views/tasks/ShowTasks.vue b/frontend/src/views/tasks/ShowTasks.vue index 742bd853c..469d5d91a 100644 --- a/frontend/src/views/tasks/ShowTasks.vue +++ b/frontend/src/views/tasks/ShowTasks.vue @@ -6,6 +6,31 @@

{{ pageTitle }}

+ + + + + + + +

(), { showNulls: false, showOverdue: false, dateFrom: undefined, dateTo: undefined, + labelIds: undefined, }) const emit = defineEmits<{ 'tasksLoaded': true, + 'clearLabelFilter': void, }>() const authStore = useAuthStore() const taskStore = useTaskStore() const projectStore = useProjectStore() +const labelStore = useLabelStore() const route = useRoute() const router = useRouter() @@ -120,6 +154,15 @@ setTimeout(() => showNothingToDo.value = true, 100) const showAll = computed(() => typeof props.dateFrom === 'undefined' || typeof props.dateTo === 'undefined') +const filteredLabels = computed(() => { + if (!props.labelIds || props.labelIds.length === 0) { + return [] + } + return props.labelIds + .map(id => labelStore.getLabelById(Number(id))) + .filter(label => label !== null && label !== undefined) +}) + const pageTitle = computed(() => { // We need to define "key" because it is the first parameter in the array and we need the second const predefinedRange = Object.entries(DATE_RANGES) @@ -177,6 +220,10 @@ function setShowNulls(show: boolean) { }) } +function clearLabelFilter() { + emit('clearLabelFilter') +} + async function loadPendingTasks(from: Date|string, to: Date|string) { // FIXME: HACK! This should never happen. // Since this route is authentication only, users would get an error message if they access the page unauthenticated. @@ -207,6 +254,12 @@ async function loadPendingTasks(from: Date|string, to: Date|string) { } } + // Add label filtering + if (props.labelIds && props.labelIds.length > 0) { + const labelFilter = `labels in ${props.labelIds.join(', ')}` + params.filter += params.filter ? ` && ${labelFilter}` : labelFilter + } + let projectId = null const filterId = authStore.settings.frontendSettings.filterIdUsedOnOverview if (showAll.value && filterId && typeof projectStore.projects[filterId] !== 'undefined') { @@ -246,4 +299,25 @@ watchEffect(() => setTitle(pageTitle.value)) margin: 3rem auto 0; display: block; } + +.label-filter-info { + margin-block-end: 1rem; + + .clear-filter-button { + margin-inline-start: auto; + padding: 0.25rem 0.5rem; + + &:hover { + color: var(--danger); + } + } + + :deep(.message.info) { + width: 100%; + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + } +}