Files
deck/src/store/card.js
2025-09-10 09:44:36 +02:00

373 lines
12 KiB
JavaScript

/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { CardApi } from './../services/CardApi.js'
import moment from 'moment'
import Vue from 'vue'
const apiClient = new CardApi()
export default {
state: {
cards: [],
},
getters: {
cardsByStack: (state, getters, rootState) => (id) => {
return state.cards.filter((card) => {
const { tags, users, due, unassigned, completed } = rootState.filter
if (completed === 'open' && card.done !== null) { return false }
if (completed === 'completed' && card.done == null) { return false }
let allTagsMatch = true
let allUsersMatch = true
if (tags.length > 0) {
tags.forEach((tag) => {
if (card.labels.findIndex((l) => l.id === tag) === -1) {
allTagsMatch = false
}
})
if (!allTagsMatch) {
return false
}
}
if (users.length > 0) {
users.forEach((user) => {
if (!card?.assignedUsers || card.assignedUsers.findIndex((u) => u.participant.uid === user) === -1) {
allUsersMatch = false
}
})
if (!allUsersMatch) {
return false
}
}
if (unassigned && card.assignedUsers.length > 0) {
return false
}
if (due !== '') {
const datediffHour = ((new Date(card.duedate) - new Date()) / 3600 / 1000)
switch (due) {
case 'noDue':
return (card.duedate === null)
case 'overdue':
return (card.overdue === 3)
case 'dueToday':
return (card.overdue >= 2)
case 'dueWeek':
return (datediffHour <= 7 * 24 && card.duedate !== null)
case 'dueMonth':
return (datediffHour <= 30 * 24 && card.duedate !== null)
}
}
return true
})
.filter((card) => card.stackId === id)
.filter((card) => {
if (getters.getSearchQuery === '') {
return true
}
let hasMatch = true
const matches = getters.getSearchQuery.match(/(?:[^\s"]+|"[^"]*")+/g)
const filterOutQuotes = (q) => {
if (q[0] === '"' && q[q.length - 1] === '"') {
return q.slice(1, -1)
}
return q
}
for (const match of matches) {
let [filter, query] = match.indexOf(':') !== -1 ? match.split(/:(.*)/) : [null, match]
const isEmptyQuery = typeof query === 'undefined' || filterOutQuotes(query) === ''
if (filter === 'title') {
if (isEmptyQuery) {
continue
}
hasMatch = hasMatch && card.title.toLowerCase().includes(filterOutQuotes(query).toLowerCase())
} else if (filter === 'description') {
if (isEmptyQuery) {
hasMatch = hasMatch && !!card.description
continue
}
hasMatch = hasMatch && card.description.toLowerCase().includes(filterOutQuotes(query).toLowerCase())
} else if (filter === 'list') {
if (isEmptyQuery) {
continue
}
const stack = getters.stackById(card.stackId)
if (!stack) {
return false
}
hasMatch = hasMatch && stack.title.toLowerCase().includes(filterOutQuotes(query).toLowerCase())
} else if (filter === 'tag') {
if (isEmptyQuery) {
hasMatch = hasMatch && card.labels.length > 0
continue
}
hasMatch = hasMatch && card.labels.findIndex((label) => label.title.toLowerCase().includes(filterOutQuotes(query).toLowerCase())) !== -1
} else if (filter === 'date') {
const datediffHour = ((new Date(card.duedate) - new Date()) / 3600 / 1000)
query = filterOutQuotes(query)
switch (query) {
case 'overdue':
hasMatch = hasMatch && (card.overdue === 3)
break
case 'today':
hasMatch = hasMatch && (datediffHour > 0 && datediffHour <= 24 && card.duedate !== null)
break
case 'week':
hasMatch = hasMatch && (datediffHour > 0 && datediffHour <= 7 * 24 && card.duedate !== null)
break
case 'month':
hasMatch = hasMatch && (datediffHour > 0 && datediffHour <= 30 * 24 && card.duedate !== null)
break
case 'none':
hasMatch = hasMatch && (card.duedate === null)
break
}
if (card.duedate === null || !hasMatch) {
return false
}
const comparator = query[0] + (query[1] === '=' ? '=' : '')
const isValidComparator = ['<', '<=', '>', '>='].indexOf(comparator) !== -1
const parsedCardDate = moment(card.duedate)
const parsedDate = moment(query.slice(isValidComparator ? comparator.length : 0))
switch (comparator) {
case '<':
hasMatch = hasMatch && parsedCardDate.isBefore(parsedDate)
break
case '<=':
hasMatch = hasMatch && parsedCardDate.isSameOrBefore(parsedDate)
break
case '>':
hasMatch = hasMatch && parsedCardDate.isAfter(parsedDate)
break
case '>=':
hasMatch = hasMatch && parsedCardDate.isSameOrAfter(parsedDate)
break
default:
hasMatch = hasMatch && parsedCardDate.isSame(parsedDate)
break
}
} else if (filter === 'assigned') {
if (isEmptyQuery) {
hasMatch = hasMatch && card.assignedUsers.length > 0
continue
}
hasMatch = hasMatch && card.assignedUsers.findIndex((assignment) => {
return assignment.participant.primaryKey.toLowerCase() === filterOutQuotes(query).toLowerCase()
|| assignment.participant.displayname.toLowerCase() === filterOutQuotes(query).toLowerCase()
}) !== -1
} else {
hasMatch = hasMatch && (card.title.toLowerCase().includes(filterOutQuotes(match).toLowerCase())
|| card.description.toLowerCase().includes(filterOutQuotes(match).toLowerCase()) || card.id === parseInt(filterOutQuotes(match)))
}
if (!hasMatch) {
return false
}
}
return true
})
.sort((a, b) => a.order - b.order || a.createdAt - b.createdAt)
},
cardById: state => (id) => {
return state.cards.find((card) => card.id === id)
},
},
mutations: {
addCard(state, card) {
card.labels = card.labels || []
card.assignedUsers = card.assignedUsers || []
const existingIndex = state.cards.findIndex(_card => _card.id === card.id)
if (existingIndex !== -1) {
const existingCard = state.cards[existingIndex]
Vue.set(state.cards, existingIndex, Object.assign({}, existingCard, card))
} else {
state.cards.push(card)
}
},
deleteCard(state, card) {
const existingIndex = state.cards.findIndex(_card => _card.id === card.id)
if (existingIndex !== -1) {
state.cards.splice(existingIndex, 1)
}
},
updateCard(state, card) {
const existingIndex = state.cards.findIndex(_card => _card.id === card.id)
if (existingIndex !== -1) {
Vue.set(state.cards, existingIndex, Object.assign({}, state.cards[existingIndex], card))
}
},
updateCardsReorder(state, cards) {
for (const newCard of cards) {
const existingIndex = state.cards.findIndex(_card => _card.id === newCard.id)
if (existingIndex !== -1) {
Vue.set(state.cards[existingIndex], 'order', newCard.order)
Vue.set(state.cards[existingIndex], 'stackId', newCard.stackId)
}
}
},
assignCardToUser(state, user) {
const existingIndex = state.cards.findIndex(_card => _card.id === user.cardId)
if (existingIndex !== -1) {
state.cards[existingIndex].assignedUsers.push(user)
}
},
removeUserFromCard(state, user) {
const existingIndex = state.cards.findIndex(_card => _card.id === user.cardId)
if (existingIndex !== -1) {
const foundIndex = state.cards[existingIndex].assignedUsers.findIndex(_user => _user.id === user.id)
if (foundIndex !== -1) {
state.cards[existingIndex].assignedUsers.splice(foundIndex, 1)
}
}
},
updateCardProperty(state, { card, property }) {
const existingIndex = state.cards.findIndex(_card => _card.id === card.id)
if (existingIndex !== -1) {
Vue.set(state.cards[existingIndex], property, card[property])
Vue.set(state.cards[existingIndex], 'lastModified', Date.now() / 1000)
}
},
cardSetAttachmentCount(state, { cardId, count }) {
const existingIndex = state.cards.findIndex(_card => _card.id === cardId)
if (existingIndex !== -1) {
Vue.set(state.cards[existingIndex], 'attachmentCount', count)
}
},
cardIncreaseAttachmentCount(state, cardId) {
const existingIndex = state.cards.findIndex(_card => _card.id === cardId)
if (existingIndex !== -1) {
Vue.set(state.cards[existingIndex], 'attachmentCount', state.cards[existingIndex].attachmentCount + 1)
}
},
cardDecreaseAttachmentCount(state, cardId) {
const existingIndex = state.cards.findIndex(_card => _card.id === cardId)
if (existingIndex !== -1) {
Vue.set(state.cards[existingIndex], 'attachmentCount', state.cards[existingIndex].attachmentCount - 1)
}
},
addNewCard(state, card) {
state.cards.push(card)
},
setCards(state, cards) {
const deletedCards = state.cards.filter(_card => {
return cards.findIndex(c => _card.id === c.id) === -1
})
for (const card of deletedCards) {
this.commit('deleteCard', card)
}
for (const card of cards) {
this.commit('addCard', card)
}
},
},
actions: {
async cloneCard({ commit }, { cardId, targetStackId }) {
const createdCard = await apiClient.cloneCard(cardId, targetStackId)
commit('addCard', createdCard)
return createdCard
},
async addCard({ commit }, card) {
const createdCard = await apiClient.addCard(card)
commit('addCard', createdCard)
return createdCard
},
async updateCardTitle({ commit }, card) {
const updatedCard = await apiClient.updateCard(card)
commit('updateCardProperty', { property: 'title', card: updatedCard })
commit('updateCardProperty', { property: 'referenceData', card: updatedCard })
},
async moveCard({ commit }, card) {
const updatedCard = await apiClient.updateCard(card)
commit('deleteCard', updatedCard)
},
async reorderCard({ commit, getters }, card) {
let i = 0
const newCards = []
for (const c of getters.cardsByStack(card.stackId)) {
if (c.id === card.id) {
newCards.push(card)
}
if (i === card.order) {
i++
}
if (c.id !== card.id) {
newCards.push({ ...c, order: i++ })
}
}
newCards.push(card)
await commit('updateCardsReorder', newCards)
apiClient.reorderCard(card).then((cards) => {
commit('updateCardsReorder', Object.values(cards))
})
},
async deleteCard({ commit }, card) {
await apiClient.deleteCard(card.id)
commit('deleteCard', card)
commit('moveCardToTrash', card)
},
async archiveUnarchiveCard({ commit }, card) {
let call = 'archiveCard'
if (card.archived === false) {
call = 'unArchiveCard'
}
const updatedCard = await apiClient[call](card)
commit('updateCard', updatedCard)
},
async changeCardDoneStatus({ commit }, card) {
let call = 'markCardAsDone'
if (card.done === false) {
call = 'markCardAsUndone'
}
const updatedCard = await apiClient[call](card)
commit('updateCardProperty', { property: 'done', card: updatedCard })
},
async assignCardToUser({ commit }, { card, assignee }) {
const user = await apiClient.assignUser(card.id, assignee.userId, assignee.type)
commit('assignCardToUser', user)
},
async removeUserFromCard({ commit }, { card, assignee }) {
const user = await apiClient.removeUser(card.id, assignee.userId, assignee.type)
commit('removeUserFromCard', user)
},
async addLabel({ commit }, data) {
await apiClient.assignLabelToCard(data)
commit('updateCardProperty', { property: 'labels', card: data.card })
},
async removeLabel({ commit }, data) {
await apiClient.removeLabelFromCard(data)
commit('updateCardProperty', { property: 'labels', card: data.card })
},
async updateCardDesc({ commit }, card) {
const updatedCard = await apiClient.updateCard(card)
commit('updateCardProperty', { property: 'description', card: updatedCard })
},
async updateCardDue({ commit }, card) {
const updatedCard = await apiClient.updateCard(card)
commit('updateCardProperty', { property: 'duedate', card: updatedCard })
},
addCardData({ commit }, cardData) {
const card = { ...cardData }
commit('addStack', card.relatedStack)
commit('addBoard', card.relatedBoard)
delete card.relatedStack
delete card.relatedBoard
commit('addCard', card)
},
},
}