Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
1ab813f
refactor(pinia): move remaining vuex state to pinia setting store
grnd-alt Aug 11, 2026
c8eccd4
refactor(vue3): use vue3 slot syntax
grnd-alt Aug 13, 2026
5ed968d
refactor(vue3): temporary vue3 compat build
grnd-alt Aug 13, 2026
1c87905
refactor(vue3): temporarily disable secondary webpack entrypoints
grnd-alt Aug 13, 2026
c1b8dfb
refactor(vue3): initialize vue3 app correctly
grnd-alt Aug 13, 2026
e167b03
refactor(vue3): replace beforeDestroy with beforeUnmount
grnd-alt Aug 13, 2026
dcd0c38
refactor(vue3): temporarily enable new vue3 render API in compat
grnd-alt Aug 13, 2026
fca6f5d
refactor(vue3): use vue3 modelValue syntax
grnd-alt Aug 14, 2026
2663aab
refactor(vue3): replace value.sync with v-model
grnd-alt Aug 17, 2026
e6eb2ba
refactore(vue3): use v-v-on-click-outside directive
grnd-alt Aug 17, 2026
4880fca
refactor(vue3): remove Vue.set usage
grnd-alt Aug 17, 2026
cde0d3f
refactor(vue3): replace deprecated vue-router next calls
grnd-alt Aug 17, 2026
600aa7e
refactor(vue3): convert CardSidebar and DueDate to composition API
grnd-alt Aug 18, 2026
165c335
refactor(vue3): disable vue3_compat watch_array flag
grnd-alt Aug 18, 2026
916bccb
refactor(vue3): rename CardMenuEntries.vue to NcActionsCardMenuEntrie…
grnd-alt Aug 19, 2026
0dbf925
refactor(vue3): align filter popover to nextcloud/vue@9
grnd-alt Aug 19, 2026
5918c1e
refactor(vue3): bring back globalSearchResults vertical scroll
grnd-alt Aug 19, 2026
b55bd44
refactor(vue3): replace vue instance emitters with nextcloud event bus
grnd-alt Aug 19, 2026
69af13a
refactor(vue3): convert alternative entrypoints to vue3
grnd-alt Aug 19, 2026
57708a2
refactor(vue3): remove @vue/compat
grnd-alt Aug 19, 2026
c9370ce
refactor(vue3): update scss @import to @use
grnd-alt Aug 19, 2026
1262875
refactor(vue3): cleanup transition remains
grnd-alt Aug 19, 2026
c8df521
refactor(vue3): lint fixes
grnd-alt Aug 19, 2026
ea76b1c
refactor(vue3): remove nextcloud/axios usage from cypress commands
grnd-alt Aug 19, 2026
5d8c708
refactor(vue3): replace .sync with v-model
grnd-alt Aug 20, 2026
a669401
fix(cypress): open dropdown before searching for option in dropdown
grnd-alt Aug 20, 2026
53ab126
fix(cypress): add data-cy identifier for app navigation categories
grnd-alt Aug 20, 2026
65a5d4f
refactor(vue3): replace this. calls from vue2
grnd-alt Aug 20, 2026
eef97d2
refactor(vue3): switch NcButton type prop to variant
grnd-alt Aug 20, 2026
00446cb
refactor(vue3): update cypress to v15
grnd-alt Sep 2, 2026
5dbaada
refactor(vue3): adapt vue3 refactor to cardFeatures.js cypress
grnd-alt Sep 2, 2026
a72e7f7
refactor(cypress): move to nextcloud/e2e-test-server
grnd-alt Sep 7, 2026
d11f9ed
fix(ci): install @vue/tsconfig
grnd-alt Sep 7, 2026
85cc47f
refactor(vue3): fix v-focus directive
grnd-alt Sep 8, 2026
a588f90
refactor(vue3): adjust cypress deckdashboard selector to new library
grnd-alt Sep 8, 2026
ac5fe4a
refactor(vue3): update eslint to vue3 config
grnd-alt Sep 8, 2026
2d88275
chore(tests): replace Cypress.env
grnd-alt Sep 8, 2026
c27ed86
refactor(vue3): replace :checked with :model-value
grnd-alt Sep 10, 2026
e47aa5c
refactor(vue3): clear comment form on submit
grnd-alt Sep 10, 2026
2ffe5f0
refactor(vue3): stop the global search from looping requests forever
Sep 14, 2026
73dd8e5
refactor(vue3): give standalone selector apps their global properties…
Sep 14, 2026
dcf69e2
refactor(vue3): mount the card selector as an async component again
Sep 14, 2026
cbb85bd
refactor(vue3): prefill the sidebar title field when renaming a card
Sep 14, 2026
f42089e
refactor(vue3): read hasCardSaveError from the store that actually ho…
Sep 14, 2026
2e1f674
refactor(vue3): only clear the comment form once the comment was created
Sep 14, 2026
708e3fe
refactor(vue3): paginate the activity list instead of draining it at …
Sep 14, 2026
9bc3bbd
refactor(vue3): pass the format options to useFormatTime, not to comp…
Sep 14, 2026
279c7e0
perf(vue3): drop three deep watchers that do not need to be deep
Sep 14, 2026
e866814
refactor(vue3): guard against String.match() returning null
Sep 14, 2026
b998b80
refactor(vue3): render the activity tabs again
Sep 14, 2026
27ac4fa
style(scss): fix linter error
blizzz Sep 14, 2026
2fe996e
fix(ui): hide duplicate modal close button
grnd-alt Sep 16, 2026
454ae76
fix(gantt): rerender view after patching bar duration calculation
grnd-alt Sep 16, 2026
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
3 changes: 2 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module.exports = {
root: true,
extends: [
'@nextcloud',
'@nextcloud/eslint-config/vue3',
],
rules: {
'jsdoc/require-param-description': ['off'],
Expand All @@ -10,5 +10,6 @@ module.exports = {
'jsdoc/no-undefined-types': ['off'],
'jsdoc/require-property-description': ['off'],
'import/no-named-as-default-member': ['off'],
'@typescript-eslint/no-unused-vars': ['off'],
},
}
1 change: 1 addition & 0 deletions cypress.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const { defineConfig } = require('cypress')
const cypressSplit = require('cypress-split')

module.exports = defineConfig({
allowCypressEnv: false,
projectId: '1s7wkc',
viewportWidth: 1280,
viewportHeight: 720,
Expand Down
5 changes: 3 additions & 2 deletions cypress/e2e/cardActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ const user = randUser()
const boardData = sampleBoard()

const auth = {
user: user.userId,
username: user.userId,
password: user.password,
}

const useModal = (useModal) => {
return cy.request({
method: 'POST',
url: `${Cypress.env('baseUrl')}/ocs/v2.php/apps/deck/api/v1.0/config/cardDetailsInModal?format=json`,
url: `${Cypress.expose('baseUrl')}/ocs/v2.php/apps/deck/api/v1.0/config/cardDetailsInModal?format=json`,
auth,
body: { value: useModal },
}).then((response) => {
Expand Down Expand Up @@ -92,6 +92,7 @@ describe('Card actions', function () {

cy.get('.app-sidebar-header .action-item__menutoggle').click()
cy.get('.v-popper__popper button:contains("Move/copy card")').click()
cy.get('.vs__dropdown-toggle span[title="MyTestBoard"]').should('be.visible').click()
cy.get('.vs__dropdown-menu span[title="MyTestBoard"]').should('be.visible').click()
cy.wait(3000) // wait for select component to load stacks
cy.get('[data-cy="select-stack"] .vs__dropdown-toggle').should('be.visible').click()
Expand Down
4 changes: 2 additions & 2 deletions cypress/e2e/cardColor.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ const user = randUser()
const boardData = sampleBoard()

const auth = {
user: user.userId,
username: user.userId,
password: user.password,
}

const useModal = (useModal) => {
return cy.request({
method: 'POST',
url: `${Cypress.env('baseUrl')}/ocs/v2.php/apps/deck/api/v1.0/config/cardDetailsInModal?format=json`,
url: `${Cypress.expose('baseUrl')}/ocs/v2.php/apps/deck/api/v1.0/config/cardDetailsInModal?format=json`,
auth,
body: { value: useModal },
}).then((response) => {
Expand Down
16 changes: 8 additions & 8 deletions cypress/e2e/cardFeatures.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ const user = randUser()
const boardData = sampleBoard()

const auth = {
user: user.userId,
username: user.userId,
password: user.password,
}

const useModal = (useModal) => {
return cy.request({
method: 'POST',
url: `${Cypress.env('baseUrl')}/ocs/v2.php/apps/deck/api/v1.0/config/cardDetailsInModal?format=json`,
url: `${Cypress.expose('baseUrl')}/ocs/v2.php/apps/deck/api/v1.0/config/cardDetailsInModal?format=json`,
auth,
body: { value: useModal },
}).then((response) => {
Expand All @@ -28,7 +28,7 @@ const useModal = (useModal) => {
const addCardsAtTop = (enabled) => {
return cy.request({
method: 'POST',
url: `${Cypress.env('baseUrl')}/ocs/v2.php/apps/deck/api/v1.0/config/stackAddCardAtTop?format=json`,
url: `${Cypress.expose('baseUrl')}/ocs/v2.php/apps/deck/api/v1.0/config/stackAddCardAtTop?format=json`,
auth,
body: { value: enabled },
}).then((response) => {
Expand Down Expand Up @@ -100,7 +100,7 @@ describe('Card', function () {
})

cy.get('[data-cy="navigation:settings"]').click()
cy.get('[data-cy="setting:add-card-at-top"] input[role="switch"]').check({ force: true })
cy.get('[data-cy="setting:add-card-at-top"]').click({ force: true })
cy.wait('@setCardPosition')
cy.visit(`/apps/deck/#/board/${boardId}`)

Expand All @@ -126,7 +126,7 @@ describe('Card', function () {
})

cy.get('[data-cy="navigation:settings"]').click()
cy.get('[data-cy="setting:add-card-at-top"] input[role="switch"]').uncheck({ force: true })
cy.get('[data-cy="setting:add-card-at-top"]').click({ force: true })
cy.wait('@setCardPosition')
cy.visit(`/apps/deck/#/board/${boardId}`)
cy.get('.board .stack').eq(0).within(() => {
Expand Down Expand Up @@ -160,7 +160,7 @@ describe('Card', function () {
cy.get('.modal-mask.card-selector .multiselect-list').should('be.visible').click()
cy.get('.vs__dropdown-menu span[title="TestList"]').should('be.visible').click()

cy.get('.modal-mask.card-selector button.button-vue--vue-primary').should('be.visible').click()
cy.get('.modal-mask.card-selector button.button-vue--primary').should('be.visible').click()
cy.wait('@save', { timeout: 7000 })

cy.reload()
Expand Down Expand Up @@ -263,7 +263,7 @@ describe('Card', function () {
cy.get('.file-picker__main').should('be.visible')
cy.get('.file-picker__main [data-filename="welcome.txt"]', { timeout: 30000 }).should('be.visible')
.click()
cy.get('.dialog__actions button.button-vue--vue-primary').click()
cy.get('.dialog__actions button.button-vue--primary').click()
cy.get('.attachment-list .filename').contains('welcome')
cy.get('.attachment-list .filename .extension').contains('txt')
})
Expand Down Expand Up @@ -307,7 +307,7 @@ describe('Card', function () {
cy.get('.reference-picker-modal--content .reference-picker .card-title').should('be.visible').click().type(newCardTitle)
cy.get('.reference-picker-modal--content .reference-picker .multiselect-board').should('be.visible').contains(boardData.title)
cy.get('.reference-picker-modal--content .reference-picker .multiselect-list').should('be.visible').contains(boardData.stacks[0].title)
cy.get('.reference-picker-modal--content .reference-picker button.button-vue--vue-primary').should('be.visible').click()
cy.get('.reference-picker-modal--content .reference-picker button.button-vue--primary').should('be.visible').click()
cy.wait('@save', { timeout: 7000 })
cy.get('.modal__card .ProseMirror').contains('/index.php/apps/deck/card/').should('have.length', 1)

Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/deckDashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe('Deck dashboard', function() {

const defaultBoard = 'Welcome to Nextcloud Deck!'

cy.get('.app-navigation-entry-wrapper[icon=icon-deck]')
cy.get('.app-navigation-entry-wrapper[data-cy-navigation-category="deck-navigation-all"]')
.find('ul.app-navigation-entry__children .app-navigation-entry:contains(' + defaultBoard + ')')
.first()
.contains(defaultBoard)
Expand Down
32 changes: 19 additions & 13 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import { addCommands } from '@nextcloud/cypress'
import axios from '@nextcloud/axios'
import { addCommands } from '@nextcloud/e2e-test-server/cypress'

addCommands()

const url = Cypress.config('baseUrl').replace(/\/index.php\/?$/g, '')
Cypress.env('baseUrl', url)
Cypress.expose('baseUrl', url)

// prepare main cypress window so we can use axios there
// and it will successfully fetch csrf tokens when needed.
Expand Down Expand Up @@ -57,12 +56,12 @@ Cypress.Commands.add('deckCreateList', ({ user, password }, title) => {

Cypress.Commands.add('createExampleBoard', ({ user, board }) => {
const auth = {
user: user.userId,
username: user.userId,
password: user.password,
}
cy.request({
method: 'POST',
url: `${Cypress.env('baseUrl')}/index.php/apps/deck/api/v1.0/boards`,
url: `${Cypress.expose('baseUrl')}/index.php/apps/deck/api/v1.0/boards`,
auth,
body: { title: board.title, color: board.color ?? 'ff0000' },
}).then((boardResponse) => {
Expand All @@ -72,7 +71,7 @@ Cypress.Commands.add('createExampleBoard', ({ user, board }) => {
const stack = board.stacks[stackIndex]
cy.request({
method: 'POST',
url: `${Cypress.env('baseUrl')}/index.php/apps/deck/api/v1.0/boards/${boardData.id}/stacks`,
url: `${Cypress.expose('baseUrl')}/index.php/apps/deck/api/v1.0/boards/${boardData.id}/stacks`,
auth,
body: { title: stack.title, order: 0 },
}).then((stackResponse) => {
Expand All @@ -81,7 +80,7 @@ Cypress.Commands.add('createExampleBoard', ({ user, board }) => {
const card = stack.cards[cardIndex]
cy.request({
method: 'POST',
url: `${Cypress.env('baseUrl')}/index.php/apps/deck/api/v1.0/boards/${boardData.id}/stacks/${stackData.id}/cards`,
url: `${Cypress.expose('baseUrl')}/index.php/apps/deck/api/v1.0/boards/${boardData.id}/stacks/${stackData.id}/cards`,
auth,
body: { title: card.title, description: card.description ?? '' },
})
Expand All @@ -93,7 +92,7 @@ Cypress.Commands.add('createExampleBoard', ({ user, board }) => {
})

Cypress.Commands.add('getNavigationEntry', (boardTitle) => {
return cy.get('.app-navigation-entry-wrapper[icon=icon-deck]')
return cy.get('[data-cy-navigation-category="deck-navigation-all"]').parent()
.find('ul.app-navigation-entry__children .app-navigation-entry:contains(' + boardTitle + ')')
.find('a.app-navigation-entry-link')
})
Expand All @@ -116,9 +115,16 @@ Cypress.Commands.add('shareBoardWithUi', (query, userId=query) => {
})

Cypress.Commands.add('setUserEmail', (user, value) => {
Cypress.log()
return axios.put(
`${url}/ocs/v2.php/cloud/users/${user.userId}`,
{ key: 'email', value },
)
return cy.request({
method: 'PUT',
url: `${url}/ocs/v2.php/cloud/users/${user.userId}`,
auth: {
username: user.userId,
password: user.password,
},
headers: {
'OCS-APIRequest': 'true',
},
body: { key: 'email', value },
})
})
2 changes: 1 addition & 1 deletion cypress/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { User } from '@nextcloud/cypress'
import { User } from '@nextcloud/e2e-test-server/cypress'

export const randHash = () => Math.random().toString(36).replace(/[^a-z]+/g, '').slice(0, 10)
export const randUser = () => new User(randHash(), randHash())
36 changes: 19 additions & 17 deletions lib/Controller/CardOcsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public function removeLabel(?int $boardId, int $cardId, int $labelId): DataRespo

#[NoAdminRequired]
#[PublicPage]
public function update(int $id, string $title, int $stackId, string $type, int $order, string $description, $duedate, $deletedAt, int $boardId, array|string|null $owner = null, $archived = null, $startdate = null): DataResponse {
public function update(int $id, string $title, int $stackId, string $type, int $order, string $description, $duedate, $deletedAt, ?int $boardId, array|string|null $owner = null, $archived = null, $startdate = null): DataResponse {
$done = array_key_exists('done', $this->request->getParams())
? new OptionalNullableValue($this->request->getParam('done', null))
: null;
Expand All @@ -135,22 +135,24 @@ public function update(int $id, string $title, int $stackId, string $type, int $
}
}

$localBoard = $this->boardService->find($boardId, false);
if ($localBoard->getExternalId()) {
return new DataResponse($this->externalBoardService->updateCardOnRemote(
$localBoard,
$id,
$title,
$stackId,
$type,
$owner,
$description,
$order,
$duedate,
$deletedAt,
$archived,
$done
));
if ($boardId) {
$localBoard = $this->boardService->find($boardId, false);
if ($localBoard->getExternalId()) {
return new DataResponse($this->externalBoardService->updateCardOnRemote(
$localBoard,
$id,
$title,
$stackId,
$type,
$owner,
$description,
$order,
$duedate,
$deletedAt,
$archived,
$done
));
}
}

return new DataResponse($this->cardService->update($id,
Expand Down
Loading
Loading