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;
+ }
+}