From f6088ace996071e2cbb3b9476212e4bf4a4d3189 Mon Sep 17 00:00:00 2001 From: Simon Kern <14980558+skjerns@users.noreply.github.com> Date: Fri, 30 Oct 2026 06:25:54 +0100 Subject: [PATCH 1/2] feat(filter): support filtering cards by creator in store Assisted-by: ClaudeCode:claude-opus-5-5 Signed-off-by: Simon Kern <14980558+skjerns@users.noreply.github.com> --- src/helpers/cardFilters.js | 17 ++++++++++++++++ src/helpers/cardFilters.spec.js | 36 +++++++++++++++++++++++++++++++++ src/stores/board.js | 4 ++-- src/stores/card.js | 7 ++++++- 4 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 src/helpers/cardFilters.js create mode 100644 src/helpers/cardFilters.spec.js diff --git a/src/helpers/cardFilters.js b/src/helpers/cardFilters.js new file mode 100644 index 0000000000..ca628813cd --- /dev/null +++ b/src/helpers/cardFilters.js @@ -0,0 +1,17 @@ +/** + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +/** + * Check whether a card was created by one of the given users. + * The card owner is either a user id or a user object. + * + * @param {object} card the card to check + * @param {string[]} uids the user ids to match against + * @return {boolean} + */ +export function cardCreatedByAny(card, uids) { + const ownerUid = card.owner?.uid ?? card.owner + return uids.includes(ownerUid) +} diff --git a/src/helpers/cardFilters.spec.js b/src/helpers/cardFilters.spec.js new file mode 100644 index 0000000000..f8c760056a --- /dev/null +++ b/src/helpers/cardFilters.spec.js @@ -0,0 +1,36 @@ +/** + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { describe, expect, it } from '@jest/globals' +import { cardCreatedByAny } from './cardFilters.js' + +describe('cardCreatedByAny', () => { + it('matches an owner given as user id', () => { + expect(cardCreatedByAny({ owner: 'alice' }, ['alice'])).toBe(true) + }) + + it('matches an owner given as user object', () => { + expect(cardCreatedByAny({ owner: { uid: 'alice', displayname: 'Alice' } }, ['alice'])).toBe(true) + }) + + it('matches if the owner is any of several users', () => { + expect(cardCreatedByAny({ owner: 'bob' }, ['alice', 'bob'])).toBe(true) + expect(cardCreatedByAny({ owner: { uid: 'bob' } }, ['alice', 'bob'])).toBe(true) + }) + + it('does not match other owners', () => { + expect(cardCreatedByAny({ owner: 'carol' }, ['alice', 'bob'])).toBe(false) + expect(cardCreatedByAny({ owner: { uid: 'carol' } }, ['alice', 'bob'])).toBe(false) + }) + + it('does not match a card without owner', () => { + expect(cardCreatedByAny({ owner: null }, ['alice'])).toBe(false) + expect(cardCreatedByAny({}, ['alice'])).toBe(false) + }) + + it('does not match when no users are given', () => { + expect(cardCreatedByAny({ owner: 'alice' }, [])).toBe(false) + }) +}) diff --git a/src/stores/board.js b/src/stores/board.js index d0f2d2b25e..651fc006e0 100644 --- a/src/stores/board.js +++ b/src/stores/board.js @@ -25,7 +25,7 @@ export const useBoardStore = defineStore('board', { showArchived: false, viewModeByBoard: {}, assignableUsers: [], - filter: { tags: [], users: [], due: '', unassigned: false, completed: 'both' }, + filter: { tags: [], users: [], creators: [], due: '', unassigned: false, completed: 'both' }, boardFilter: BOARD_FILTERS.ALL, boards: loadState('deck', 'initialBoards', {}), }), @@ -145,7 +145,7 @@ export const useBoardStore = defineStore('board', { localStorage.setItem(`deck.viewMode.${this.currentBoard.id}`, mode) }, async loadBoardById(boardId) { - this.filter = { tags: [], users: [], due: '', unassigned: false, completed: 'both' } + this.filter = { tags: [], users: [], creators: [], due: '', unassigned: false, completed: 'both' } this.setCurrentBoard(null) const board = await apiClient.loadById(boardId) this.setCurrentBoard(board) diff --git a/src/stores/card.js b/src/stores/card.js index 666b042a0e..38a090e766 100644 --- a/src/stores/card.js +++ b/src/stores/card.js @@ -5,6 +5,7 @@ import moment from 'moment' import { defineStore } from 'pinia' +import { cardCreatedByAny } from '../helpers/cardFilters.js' import { CardApi } from '../services/CardApi.js' import { useBoardStore } from './board.js' import { useSettingsStore } from './settings.js' @@ -20,7 +21,7 @@ export const useCardStore = defineStore('card', { getters: { cardsByStack(state) { return (id) => state.cards.filter((card) => { - const { tags, users, due, unassigned, completed } = useBoardStore().filter + const { tags, users, creators, due, unassigned, completed } = useBoardStore().filter if (completed === 'open' && card.done !== null) { return false } @@ -49,6 +50,10 @@ export const useCardStore = defineStore('card', { } } + if (creators.length > 0 && !cardCreatedByAny(card, creators)) { + return false + } + if (unassigned && card.assignedUsers.length > 0) { return false } From 26f5681d22a72e2ed77092b1042d31a2fc9c3d96 Mon Sep 17 00:00:00 2001 From: Simon Kern <14980558+skjerns@users.noreply.github.com> Date: Fri, 30 Oct 2026 06:25:54 +0100 Subject: [PATCH 2/2] feat(filter): add "Filter by creator" section to board filter Assisted-by: ClaudeCode:claude-opus-5-5 Signed-off-by: Simon Kern <14980558+skjerns@users.noreply.github.com> --- src/components/Controls.vue | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/components/Controls.vue b/src/components/Controls.vue index 2c09e1fbe7..b0e4febb9a 100644 --- a/src/components/Controls.vue +++ b/src/components/Controls.vue @@ -146,6 +146,22 @@ :hideStatus="true" /> {{ user.displayname }} +