Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions src/components/Controls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,22 @@
:hideStatus="true" /> {{ user.displayname }}</label>
</div>

<h3>{{ t('deck', 'Filter by creator') }}</h3>
<div v-for="user in board.users" :key="user.uid" class="filter--item">
<input
:id="'creator-' + user.uid"
v-model="filter.creators"
type="checkbox"
class="checkbox"
:value="user.uid"
@change="setFilter">
<label :for="'creator-' + user.uid"><NcAvatar
:user="user.uid"
:size="24"
:disableMenu="true"
:hideStatus="true" /> {{ user.displayname }}</label>
</div>

<h3>{{ t('deck', 'Filter by status') }}</h3>
<div class="filter--item">
<input
Expand Down Expand Up @@ -401,7 +417,7 @@ export default {
newStackTitle: '',
stack: '',
isAddStackVisible: false,
filter: { tags: [], users: [], due: '', unassigned: false, completed: 'both' },
filter: { tags: [], users: [], creators: [], due: '', unassigned: false, completed: 'both' },
showAddCardModal: false,
defaultPageTitle: false,
isNotifyPushEnabled: isNotifyPushEnabled(),
Expand Down Expand Up @@ -440,7 +456,7 @@ export default {
},

isFilterActive() {
return this.filter.tags.length !== 0 || this.filter.users.length !== 0 || this.filter.due !== '' || this.filter.completed !== 'both'
return this.filter.tags.length !== 0 || this.filter.users.length !== 0 || this.filter.creators.length !== 0 || this.filter.due !== '' || this.filter.completed !== 'both'
},

labelsSorted() {
Expand Down Expand Up @@ -567,7 +583,7 @@ export default {
},

clearFilter() {
const filterReset = { tags: [], users: [], due: '', unassigned: false, completed: 'both' }
const filterReset = { tags: [], users: [], creators: [], due: '', unassigned: false, completed: 'both' }
this.setFilterInStore({ ...filterReset })
this.filter = filterReset
},
Expand Down
17 changes: 17 additions & 0 deletions src/helpers/cardFilters.js
Original file line number Diff line number Diff line change
@@ -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)
}
36 changes: 36 additions & 0 deletions src/helpers/cardFilters.spec.js
Original file line number Diff line number Diff line change
@@ -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)
})
})
4 changes: 2 additions & 2 deletions src/stores/board.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', {}),
}),
Expand Down Expand Up @@ -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)
Expand Down
7 changes: 6 additions & 1 deletion src/stores/card.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down