Compare commits
14 Commits
feature/ad
...
vue3-remov
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
981d1cbd38 | ||
|
|
85af54cd6f | ||
|
|
f3a8de2c48 | ||
|
|
1d7bf30e8f | ||
|
|
ee10097c24 | ||
|
|
03d2bd945c | ||
|
|
1110a4f125 | ||
|
|
f4acf38035 | ||
|
|
30e74a7919 | ||
|
|
b59b2edb37 | ||
|
|
8e805dbec3 | ||
|
|
0f1d1181e3 | ||
|
|
c32115dea6 | ||
|
|
452dc5f230 |
@@ -84,7 +84,7 @@ describe('Card', function () {
|
|||||||
cy.get('.modal-mask.card-selector .multiselect-list').should('be.visible').click()
|
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('.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').contains('Create card').should('be.visible').click()
|
||||||
cy.wait('@save', { timeout: 7000 })
|
cy.wait('@save', { timeout: 7000 })
|
||||||
|
|
||||||
cy.reload()
|
cy.reload()
|
||||||
@@ -187,7 +187,7 @@ describe('Card', function () {
|
|||||||
cy.get('.file-picker__main').should('be.visible')
|
cy.get('.file-picker__main').should('be.visible')
|
||||||
cy.get('.file-picker__main [data-filename="welcome.txt"]', { timeout: 30000 }).should('be.visible')
|
cy.get('.file-picker__main [data-filename="welcome.txt"]', { timeout: 30000 }).should('be.visible')
|
||||||
.click()
|
.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').contains('welcome')
|
||||||
cy.get('.attachment-list .filename .extension').contains('txt')
|
cy.get('.attachment-list .filename .extension').contains('txt')
|
||||||
})
|
})
|
||||||
@@ -302,8 +302,10 @@ describe('Card', function () {
|
|||||||
.first().click()
|
.first().click()
|
||||||
cy.get(`.card:contains("${newCardTitle}")`).should('be.visible').click()
|
cy.get(`.card:contains("${newCardTitle}")`).should('be.visible').click()
|
||||||
|
|
||||||
cy.get('#app-sidebar-vue [data-test="tag-selector"] .vs__dropdown-toggle').should('be.visible').click()
|
cy.get('#app-sidebar-vue [data-test="tag-selector"] .vs__dropdown-toggle .vs__actions').should('be.visible').click()
|
||||||
cy.get('.vs__dropdown-menu .tag:contains("Action needed")').should('be.visible').click()
|
cy.get('.vs__dropdown-menu .tag:contains("Action needed")').should('be.visible').click()
|
||||||
|
cy.get('.vs__dropdown-menu .tag:contains("Later")').should('not.exist')
|
||||||
|
cy.get('#app-sidebar-vue [data-test="tag-selector"] .vs__dropdown-toggle .vs__actions').should('be.visible').click()
|
||||||
cy.get('.vs__dropdown-menu .tag:contains("Later")').should('be.visible').click()
|
cy.get('.vs__dropdown-menu .tag:contains("Later")').should('be.visible').click()
|
||||||
|
|
||||||
cy.get('.vs__selected .tag:contains("Action needed")').should('be.visible')
|
cy.get('.vs__selected .tag:contains("Action needed")').should('be.visible')
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ describe('Deck dashboard', function() {
|
|||||||
|
|
||||||
const defaultBoard = 'Welcome to Nextcloud Deck!'
|
const defaultBoard = 'Welcome to Nextcloud Deck!'
|
||||||
|
|
||||||
cy.get('.app-navigation-entry-wrapper[icon=icon-deck]')
|
cy.get('#deck-navigation-all')
|
||||||
.find('ul.app-navigation-entry__children .app-navigation-entry:contains(' + defaultBoard + ')')
|
.find('ul.app-navigation-entry__children .app-navigation-entry:contains(' + defaultBoard + ')')
|
||||||
.first()
|
.first()
|
||||||
.contains(defaultBoard)
|
.contains(defaultBoard)
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ Cypress.Commands.add('createExampleBoard', ({ user, board }) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Cypress.Commands.add('getNavigationEntry', (boardTitle) => {
|
Cypress.Commands.add('getNavigationEntry', (boardTitle) => {
|
||||||
return cy.get('.app-navigation-entry-wrapper[icon=icon-deck]')
|
return cy.get('#deck-navigation-all')
|
||||||
.find('ul.app-navigation-entry__children .app-navigation-entry:contains(' + boardTitle + ')')
|
.find('ul.app-navigation-entry__children .app-navigation-entry:contains(' + boardTitle + ')')
|
||||||
.find('a.app-navigation-entry-link')
|
.find('a.app-navigation-entry-link')
|
||||||
})
|
})
|
||||||
|
|||||||
13089
package-lock.json
generated
13089
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
27
package.json
27
package.json
@@ -35,7 +35,7 @@
|
|||||||
"@nextcloud/auth": "^2.4.0",
|
"@nextcloud/auth": "^2.4.0",
|
||||||
"@nextcloud/axios": "^2.5.1",
|
"@nextcloud/axios": "^2.5.1",
|
||||||
"@nextcloud/capabilities": "^1.2.0",
|
"@nextcloud/capabilities": "^1.2.0",
|
||||||
"@nextcloud/dialogs": "^6.0.1",
|
"@nextcloud/dialogs": "^7.0.0-rc.1",
|
||||||
"@nextcloud/event-bus": "^3.3.2",
|
"@nextcloud/event-bus": "^3.3.2",
|
||||||
"@nextcloud/files": "^3.10.1",
|
"@nextcloud/files": "^3.10.1",
|
||||||
"@nextcloud/initial-state": "^2.2.0",
|
"@nextcloud/initial-state": "^2.2.0",
|
||||||
@@ -43,7 +43,10 @@
|
|||||||
"@nextcloud/moment": "^1.3.2",
|
"@nextcloud/moment": "^1.3.2",
|
||||||
"@nextcloud/notify_push": "^1.3.0",
|
"@nextcloud/notify_push": "^1.3.0",
|
||||||
"@nextcloud/router": "^3.0.1",
|
"@nextcloud/router": "^3.0.1",
|
||||||
"@nextcloud/vue": "^8.25.1",
|
"@nextcloud/vue": "^9.0.0-rc.3",
|
||||||
|
"@vue/compiler-sfc": "^3.5.13",
|
||||||
|
"@vue/vue3-jest": "^29.2.6",
|
||||||
|
"@vueuse/core": "^13.1.0",
|
||||||
"blueimp-md5": "^2.19.0",
|
"blueimp-md5": "^2.19.0",
|
||||||
"chroma-js": "^3.1.2",
|
"chroma-js": "^3.1.2",
|
||||||
"dompurify": "^3.2.5",
|
"dompurify": "^3.2.5",
|
||||||
@@ -54,16 +57,14 @@
|
|||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"p-queue": "^8.0.1",
|
"p-queue": "^8.0.1",
|
||||||
"url-search-params-polyfill": "^8.2.5",
|
"url-search-params-polyfill": "^8.2.5",
|
||||||
"vue": "^2.7.15",
|
"v3-infinite-loading": "^1.3.2",
|
||||||
"vue-at": "^2.5.1",
|
"vue": "^3.5.13",
|
||||||
"vue-click-outside": "^1.1.0",
|
"vue-click-outside": "^1.1.0",
|
||||||
"vue-easymde": "^2.0.0",
|
"vue-easymde": "^2.0.0",
|
||||||
"vue-infinite-loading": "^2.4.5",
|
|
||||||
"vue-material-design-icons": "^5.3.1",
|
"vue-material-design-icons": "^5.3.1",
|
||||||
"vue-router": "^3.6.5",
|
"vue-router": "^4.5.0",
|
||||||
"vue-smooth-dnd": "^0.8.1",
|
"vue3-smooth-dnd": "^0.0.6",
|
||||||
"vuex": "^3.6.2",
|
"vuex": "^4.1.0"
|
||||||
"vuex-router-sync": "^5.0.0"
|
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"extends @nextcloud/browserslist-config"
|
"extends @nextcloud/browserslist-config"
|
||||||
@@ -78,17 +79,15 @@
|
|||||||
"@nextcloud/cypress": "^1.0.0-beta.13",
|
"@nextcloud/cypress": "^1.0.0-beta.13",
|
||||||
"@nextcloud/eslint-config": "^8.4.2",
|
"@nextcloud/eslint-config": "^8.4.2",
|
||||||
"@nextcloud/stylelint-config": "^3.0.1",
|
"@nextcloud/stylelint-config": "^3.0.1",
|
||||||
"@nextcloud/webpack-vue-config": "^6.3.0",
|
"@nextcloud/webpack-vue-config": "github:nextcloud-libraries/webpack-vue-config#vue3",
|
||||||
"@relative-ci/agent": "^4.3.1",
|
"@relative-ci/agent": "^4.2.14",
|
||||||
"@vue/test-utils": "^2.4.6",
|
"@vue/test-utils": "^2.4.6",
|
||||||
"@vue/vue2-jest": "^29.2.6",
|
|
||||||
"cypress": "^13.17.0",
|
"cypress": "^13.17.0",
|
||||||
"eslint-plugin-cypress": "^3.6.0",
|
"eslint-plugin-cypress": "^3.6.0",
|
||||||
"eslint-webpack-plugin": "^5.0.1",
|
"eslint-webpack-plugin": "^5.0.1",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"jest-serializer-vue": "^3.1.0",
|
"jest-serializer-vue": "^3.1.0",
|
||||||
"stylelint-webpack-plugin": "^5.0.1",
|
"stylelint-webpack-plugin": "^5.0.1"
|
||||||
"vue-template-compiler": "^2.7.16"
|
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"moduleFileExtensions": [
|
"moduleFileExtensions": [
|
||||||
|
|||||||
213
src/App.vue
213
src/App.vue
@@ -1,15 +1,51 @@
|
|||||||
<!--
|
<!--
|
||||||
- SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
- SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
|
||||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
-->
|
-->
|
||||||
|
<script>
|
||||||
|
import { NcAppContent, NcContent, NcModal } from '@nextcloud/vue'
|
||||||
|
import CardMoveDialog from './CardMoveDialog.vue'
|
||||||
|
import AppNavigation from './components/navigation/AppNavigation.vue'
|
||||||
|
import KeyboardShortcuts from './components/KeyboardShortcuts.vue'
|
||||||
|
import { BoardApi } from './services/BoardApi.js'
|
||||||
|
|
||||||
|
const boardApi = new BoardApi()
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'App',
|
||||||
|
components: {
|
||||||
|
NcContent,
|
||||||
|
AppNavigation,
|
||||||
|
NcAppContent,
|
||||||
|
KeyboardShortcuts,
|
||||||
|
CardMoveDialog,
|
||||||
|
NcModal,
|
||||||
|
},
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
boardApi,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
cardDetailsInModal() {
|
||||||
|
return this.$store.getters.config('cardDetailsInModal')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
hideModal() {
|
||||||
|
this.$router.push({ name: 'board' })
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NcContent app-name="deck" :class="{ 'nav-hidden': !navShown, 'sidebar-hidden': !sidebarRouterView }">
|
<NcContent app-name="deck">
|
||||||
<AppNavigation />
|
<AppNavigation />
|
||||||
<NcAppContent :allow-swipe-navigation="false">
|
<NcAppContent :allow-swipe-navigation="false">
|
||||||
<router-view />
|
<router-view />
|
||||||
</NcAppContent>
|
</NcAppContent>
|
||||||
|
|
||||||
<div v-if="$route.params.id || $route.params.cardId">
|
<div v-if="$route.params.id || $route.params.cardId">
|
||||||
<NcModal v-if="cardDetailsInModal && $route.params.cardId"
|
<NcModal v-if="cardDetailsInModal && $route.params.cardId"
|
||||||
:name="t('deck', 'Card details')"
|
:name="t('deck', 'Card details')"
|
||||||
@@ -21,179 +57,10 @@
|
|||||||
<router-view name="sidebar" />
|
<router-view name="sidebar" />
|
||||||
</div>
|
</div>
|
||||||
</NcModal>
|
</NcModal>
|
||||||
|
|
||||||
<router-view name="sidebar" :visible="!cardDetailsInModal || !$route.params.cardId" />
|
<router-view name="sidebar" :visible="!cardDetailsInModal || !$route.params.cardId" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<KeyboardShortcuts />
|
<KeyboardShortcuts />
|
||||||
<CardMoveDialog />
|
<CardMoveDialog />
|
||||||
</NcContent>
|
</NcContent>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
|
||||||
import { mapState } from 'vuex'
|
|
||||||
import AppNavigation from './components/navigation/AppNavigation.vue'
|
|
||||||
import KeyboardShortcuts from './components/KeyboardShortcuts.vue'
|
|
||||||
import { NcModal, NcContent, NcAppContent, isMobile } from '@nextcloud/vue'
|
|
||||||
import { BoardApi } from './services/BoardApi.js'
|
|
||||||
import { emit, subscribe } from '@nextcloud/event-bus'
|
|
||||||
import { loadState } from '@nextcloud/initial-state'
|
|
||||||
import CardMoveDialog from './CardMoveDialog.vue'
|
|
||||||
|
|
||||||
const boardApi = new BoardApi()
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'App',
|
|
||||||
components: {
|
|
||||||
CardMoveDialog,
|
|
||||||
AppNavigation,
|
|
||||||
NcModal,
|
|
||||||
NcContent,
|
|
||||||
NcAppContent,
|
|
||||||
KeyboardShortcuts,
|
|
||||||
},
|
|
||||||
mixins: [isMobile],
|
|
||||||
provide() {
|
|
||||||
return {
|
|
||||||
boardApi,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
addButton: {
|
|
||||||
icon: 'icon-add',
|
|
||||||
classes: [],
|
|
||||||
text: t('deck', 'Add board'),
|
|
||||||
edit: {
|
|
||||||
text: t('deck', 'Add board'),
|
|
||||||
action: () => {
|
|
||||||
},
|
|
||||||
reset: () => {
|
|
||||||
},
|
|
||||||
},
|
|
||||||
action: () => {
|
|
||||||
this.addButton.classes.push('editing')
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapState({
|
|
||||||
navShown: state => state.navShown,
|
|
||||||
sidebarShownState: state => state.sidebarShown,
|
|
||||||
currentBoard: state => state.currentBoard,
|
|
||||||
}),
|
|
||||||
// TODO: properly handle sidebar showing for route subview and board sidebar
|
|
||||||
sidebarRouterView() {
|
|
||||||
// console.log(this.$route)
|
|
||||||
return this.$route.name === 'card' || this.$route.name === 'board.details'
|
|
||||||
},
|
|
||||||
sidebarShown() {
|
|
||||||
return this.sidebarRouterView || this.sidebarShownState
|
|
||||||
},
|
|
||||||
cardDetailsInModal: {
|
|
||||||
get() {
|
|
||||||
return this.$store.getters.config('cardDetailsInModal')
|
|
||||||
},
|
|
||||||
set(newValue) {
|
|
||||||
this.$store.dispatch('setConfig', { cardDetailsInModal: newValue })
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
const initialState = loadState('deck', 'initialBoards', null)
|
|
||||||
if (initialState !== null) {
|
|
||||||
this.$store.dispatch('loadBoards')
|
|
||||||
}
|
|
||||||
this.$store.dispatch('loadSharees')
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
// Set navigation to initial state and update in case it gets toggled
|
|
||||||
emit('toggle-navigation', { open: !this.isMobile && this.navShown, _initial: true })
|
|
||||||
this.$nextTick(() => {
|
|
||||||
subscribe('navigation-toggled', (navState) => {
|
|
||||||
this.$store.dispatch('toggleNav', navState.open)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
hideModal() {
|
|
||||||
this.$router.push({ name: 'board' })
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
|
|
||||||
#content-vue {
|
|
||||||
#app-content {
|
|
||||||
transition: margin-left 100ms ease;
|
|
||||||
position: relative;
|
|
||||||
overflow-x: hidden;
|
|
||||||
align-items: stretch;
|
|
||||||
}
|
|
||||||
|
|
||||||
#app-sidebar {
|
|
||||||
transition: max-width 100ms ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.nav-hidden {
|
|
||||||
#app-content {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.sidebar-hidden {
|
|
||||||
#app-sidebar {
|
|
||||||
max-width: 0;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '../css/print';
|
|
||||||
|
|
||||||
.icon-activity {
|
|
||||||
background-image: url(../img/activity-dark.svg);
|
|
||||||
|
|
||||||
body[data-theme-dark] & {
|
|
||||||
background-image: url(../img/activity.svg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatardiv.circles {
|
|
||||||
background: var(--color-primary-element);
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-circles {
|
|
||||||
background-image: url(../img/circles-dark.svg);
|
|
||||||
opacity: 1;
|
|
||||||
background-size: 20px;
|
|
||||||
background-position: center center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-circles-white, .icon-circles.icon-white {
|
|
||||||
background-image: url(../img/circles.svg);
|
|
||||||
opacity: 1;
|
|
||||||
background-size: 20px;
|
|
||||||
background-position: center center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-colorpicker {
|
|
||||||
background-image: url('../img/color_picker.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.v-select {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal__card {
|
|
||||||
width: 100%;
|
|
||||||
min-width: 100%;
|
|
||||||
height: calc(100% - 20px);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -22,10 +22,10 @@
|
|||||||
label="title" />
|
label="title" />
|
||||||
</div>
|
</div>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<NcButton :disabled="!isBoardAndStackChoosen" type="secondary" @click="moveCard">
|
<NcButton :disabled="!isBoardAndStackChoosen" variant="secondary" @click="moveCard">
|
||||||
{{ t('deck', 'Move card') }}
|
{{ t('deck', 'Move card') }}
|
||||||
</NcButton>
|
</NcButton>
|
||||||
<NcButton :disabled="!isBoardAndStackChoosen" type="primary" @click="cloneCard">
|
<NcButton :disabled="!isBoardAndStackChoosen" variant="primary" @click="cloneCard">
|
||||||
{{ t('deck', 'Copy card') }}
|
{{ t('deck', 'Copy card') }}
|
||||||
</NcButton>
|
</NcButton>
|
||||||
</template>
|
</template>
|
||||||
@@ -72,7 +72,7 @@ export default {
|
|||||||
mounted() {
|
mounted() {
|
||||||
subscribe('deck:card:show-move-dialog', this.openModal)
|
subscribe('deck:card:show-move-dialog', this.openModal)
|
||||||
},
|
},
|
||||||
destroyed() {
|
unmounted() {
|
||||||
unsubscribe('deck:card:show-move-dialog', this.openModal)
|
unsubscribe('deck:card:show-move-dialog', this.openModal)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@@ -10,9 +10,15 @@
|
|||||||
:key="activity.activity_id"
|
:key="activity.activity_id"
|
||||||
:activity="activity" />
|
:activity="activity" />
|
||||||
<InfiniteLoading :identifier="objectId" @infinite="infiniteHandler" @change="changeObject">
|
<InfiniteLoading :identifier="objectId" @infinite="infiniteHandler" @change="changeObject">
|
||||||
<div slot="spinner" class="icon-loading" />
|
<template #spinner>
|
||||||
<div slot="no-more" />
|
<div class="icon-loading" />
|
||||||
<div slot="no-results" />
|
</template>
|
||||||
|
<template #no-more>
|
||||||
|
<div />
|
||||||
|
</template>
|
||||||
|
<template #no-results>
|
||||||
|
<div />
|
||||||
|
</template>
|
||||||
</InfiniteLoading>
|
</InfiniteLoading>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -21,7 +27,7 @@
|
|||||||
import axios from '@nextcloud/axios'
|
import axios from '@nextcloud/axios'
|
||||||
import { generateOcsUrl } from '@nextcloud/router'
|
import { generateOcsUrl } from '@nextcloud/router'
|
||||||
import ActivityEntry from './ActivityEntry.vue'
|
import ActivityEntry from './ActivityEntry.vue'
|
||||||
import InfiniteLoading from 'vue-infinite-loading'
|
// import InfiniteLoading from 'v3-infinite-loading'
|
||||||
|
|
||||||
const ACTIVITY_FETCH_LIMIT = 50
|
const ACTIVITY_FETCH_LIMIT = 50
|
||||||
|
|
||||||
@@ -29,7 +35,6 @@ export default {
|
|||||||
name: 'ActivityList',
|
name: 'ActivityList',
|
||||||
components: {
|
components: {
|
||||||
ActivityEntry,
|
ActivityEntry,
|
||||||
InfiniteLoading,
|
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
filter: {
|
filter: {
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
:title="t('deck', 'Apply filter')"
|
:title="t('deck', 'Apply filter')"
|
||||||
:aria-label="t('deck', 'Apply filter')"
|
:aria-label="t('deck', 'Apply filter')"
|
||||||
class="filter-button"
|
class="filter-button"
|
||||||
:type="isFilterActive ? 'primary' : 'tertiary'">
|
:variant="isFilterActive ? 'primary' : 'tertiary'">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<FilterIcon v-if="isFilterActive" :size="20" decorative />
|
<FilterIcon v-if="isFilterActive" :size="20" decorative />
|
||||||
<FilterOffIcon v-else :size="20" decorative />
|
<FilterOffIcon v-else :size="20" decorative />
|
||||||
@@ -231,12 +231,16 @@
|
|||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
<NcActionButton v-if="compactMode"
|
<NcActionButton v-if="compactMode"
|
||||||
@click="toggleCompactMode">
|
@click="toggleCompactMode">
|
||||||
<ArrowExpandVerticalIcon slot="icon" :size="20" decorative />
|
<template #icon>
|
||||||
|
<ArrowExpandVerticalIcon :size="20" decorative />
|
||||||
|
</template>
|
||||||
{{ t('deck', 'Toggle compact mode') }}
|
{{ t('deck', 'Toggle compact mode') }}
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
<NcActionButton v-else
|
<NcActionButton v-else
|
||||||
@click="toggleCompactMode">
|
@click="toggleCompactMode">
|
||||||
<ArrowCollapseVerticalIcon slot="icon" :size="20" decorative />
|
<template #icon>
|
||||||
|
<ArrowCollapseVerticalIcon :size="20" decorative />
|
||||||
|
</template>
|
||||||
{{ t('deck', 'Toggle compact mode') }}
|
{{ t('deck', 'Toggle compact mode') }}
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
<NcActionButton @click="toggleShowCardCover">
|
<NcActionButton @click="toggleShowCardCover">
|
||||||
@@ -274,6 +278,7 @@ import SessionList from './SessionList.vue'
|
|||||||
import { isNotifyPushEnabled } from '../sessions.js'
|
import { isNotifyPushEnabled } from '../sessions.js'
|
||||||
import CreateNewCardCustomPicker from '../views/CreateNewCardCustomPicker.vue'
|
import CreateNewCardCustomPicker from '../views/CreateNewCardCustomPicker.vue'
|
||||||
import { getCurrentUser } from '@nextcloud/auth'
|
import { getCurrentUser } from '@nextcloud/auth'
|
||||||
|
import { onClickOutside } from '@vueuse/core'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Controls',
|
name: 'Controls',
|
||||||
@@ -359,6 +364,11 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
created() {
|
||||||
|
onClickOutside(() => {
|
||||||
|
this.hideAddStack()
|
||||||
|
})
|
||||||
|
},
|
||||||
beforeMount() {
|
beforeMount() {
|
||||||
subscribe('deck:board:show-new-card', this.clickShowAddCardModel)
|
subscribe('deck:board:show-new-card', this.clickShowAddCardModel)
|
||||||
subscribe('deck:board:toggle-filter-popover', this.triggerOpenFilters)
|
subscribe('deck:board:toggle-filter-popover', this.triggerOpenFilters)
|
||||||
@@ -366,7 +376,7 @@ export default {
|
|||||||
subscribe('deck:board:toggle-filter-by-me', this.triggerFilterByMe)
|
subscribe('deck:board:toggle-filter-by-me', this.triggerFilterByMe)
|
||||||
|
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeUnmount() {
|
||||||
unsubscribe('deck:board:show-new-card', this.clickShowAddCardModel)
|
unsubscribe('deck:board:show-new-card', this.clickShowAddCardModel)
|
||||||
unsubscribe('deck:board:toggle-filter-popover', this.triggerOpenFilters)
|
unsubscribe('deck:board:toggle-filter-popover', this.triggerOpenFilters)
|
||||||
unsubscribe('deck:board:clear-filter', this.triggerClearFilter)
|
unsubscribe('deck:board:clear-filter', this.triggerClearFilter)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
<!-- :style="{top:cardTop, left:cardLeft}" -->
|
<!-- :style="{top:cardTop, left:cardLeft}" -->
|
||||||
<div v-if="card && selector"
|
<div v-if="card && selector"
|
||||||
ref="shortcutModal"
|
ref="shortcutModal"
|
||||||
v-click-outside="close"
|
|
||||||
class="keyboard-shortcuts__modal"
|
class="keyboard-shortcuts__modal"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@keydown.esc="close">
|
@keydown.esc="close">
|
||||||
@@ -18,6 +17,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import DueDateSelector from './card/DueDateSelector.vue'
|
import DueDateSelector from './card/DueDateSelector.vue'
|
||||||
|
import { onClickOutside } from '@vueuse/core'
|
||||||
import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus'
|
import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus'
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import TagSelector from './card/TagSelector.vue'
|
import TagSelector from './card/TagSelector.vue'
|
||||||
@@ -50,8 +50,9 @@ export default {
|
|||||||
subscribe('deck:card:show-assignment-selector', this.handleShowAssignemnt)
|
subscribe('deck:card:show-assignment-selector', this.handleShowAssignemnt)
|
||||||
subscribe('deck:card:show-due-date-selector', this.handleShowDueDate)
|
subscribe('deck:card:show-due-date-selector', this.handleShowDueDate)
|
||||||
subscribe('deck:card:show-label-selector', this.handleShowLabel)
|
subscribe('deck:card:show-label-selector', this.handleShowLabel)
|
||||||
|
onClickOutside(this.close)
|
||||||
},
|
},
|
||||||
destroyed() {
|
unmounted() {
|
||||||
document.removeEventListener('keydown', this.onKeydown)
|
document.removeEventListener('keydown', this.onKeydown)
|
||||||
unsubscribe('deck:card:show-assignment-selector', this.handleShowAssignemnt)
|
unsubscribe('deck:card:show-assignment-selector', this.handleShowAssignemnt)
|
||||||
unsubscribe('deck:card:show-due-date-selector', this.handleShowDueDate)
|
unsubscribe('deck:card:show-due-date-selector', this.handleShowDueDate)
|
||||||
|
|||||||
@@ -29,12 +29,12 @@
|
|||||||
{{ t('deck', 'Create a new list to add cards to this board') }}
|
{{ t('deck', 'Create a new list to add cards to this board') }}
|
||||||
<form @submit.prevent="addNewStack()">
|
<form @submit.prevent="addNewStack()">
|
||||||
<NcTextField ref="newStackInput"
|
<NcTextField ref="newStackInput"
|
||||||
|
v-model="newStackTitle"
|
||||||
:disable="loading"
|
:disable="loading"
|
||||||
:value.sync="newStackTitle"
|
|
||||||
:placeholder="t('deck', 'List name')"
|
:placeholder="t('deck', 'List name')"
|
||||||
type="text" />
|
type="text" />
|
||||||
<NcButton type="secondary"
|
<NcButton variant="secondary"
|
||||||
native-type="submit"
|
type="submit"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
:title="t('deck', 'Add list')">
|
:title="t('deck', 'Add list')">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
@@ -63,7 +63,10 @@
|
|||||||
data-click-closes-sidebar="true"
|
data-click-closes-sidebar="true"
|
||||||
data-dragscroll-enabled
|
data-dragscroll-enabled
|
||||||
class="stack-draggable-wrapper">
|
class="stack-draggable-wrapper">
|
||||||
<Stack :stack="stack" :dragging="draggingStack" data-click-closes-sidebar="true" />
|
<Stack :stack="stack"
|
||||||
|
:dragging="draggingStack"
|
||||||
|
data-click-closes-sidebar="true"
|
||||||
|
@open-card="openCard" />
|
||||||
</Draggable>
|
</Draggable>
|
||||||
</Container>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
@@ -82,7 +85,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { Container, Draggable } from 'vue-smooth-dnd'
|
import { Container, Draggable } from 'vue3-smooth-dnd'
|
||||||
import { mapState, mapGetters } from 'vuex'
|
import { mapState, mapGetters } from 'vuex'
|
||||||
import Controls from '../Controls.vue'
|
import Controls from '../Controls.vue'
|
||||||
import DeckIcon from '../icons/DeckIcon.vue'
|
import DeckIcon from '../icons/DeckIcon.vue'
|
||||||
@@ -165,14 +168,14 @@ export default {
|
|||||||
created() {
|
created() {
|
||||||
this.session = createSession(this.id)
|
this.session = createSession(this.id)
|
||||||
this.fetchData()
|
this.fetchData()
|
||||||
this.$root.$on('open-card', (cardId) => {
|
|
||||||
this.localModal = cardId
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeUnmount() {
|
||||||
this.session.close()
|
this.session.close()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
openCard(cardId) {
|
||||||
|
|
||||||
|
},
|
||||||
async fetchData() {
|
async fetchData() {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
try {
|
try {
|
||||||
@@ -253,8 +256,8 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../css/animations';
|
@use '../../css/animations';
|
||||||
@import '../../css/variables';
|
@use '../../css/variables';
|
||||||
|
|
||||||
form {
|
form {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -282,7 +285,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.board {
|
.board {
|
||||||
padding-left: $board-spacing;
|
padding-left: variables.$board-spacing;
|
||||||
position: relative;
|
position: relative;
|
||||||
max-height: calc(100% - var(--default-clickable-area));
|
max-height: calc(100% - var(--default-clickable-area));
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -313,8 +316,8 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
// Margin left instead of padidng to avoid jumps on dropping a card
|
// Margin left instead of padidng to avoid jumps on dropping a card
|
||||||
margin-left: $stack-spacing;
|
margin-left: variables.$stack-spacing;
|
||||||
padding-right: $stack-spacing;
|
padding-right: variables.$stack-spacing;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding-top: 15px;
|
padding-top: 15px;
|
||||||
|
|||||||
@@ -94,7 +94,7 @@
|
|||||||
non-drag-area-selector=".dragDisabled"
|
non-drag-area-selector=".dragDisabled"
|
||||||
:drag-handle-selector="dragHandleSelector"
|
:drag-handle-selector="dragHandleSelector"
|
||||||
data-dragscroll-enabled
|
data-dragscroll-enabled
|
||||||
@should-accept-drop="canEdit"
|
:should-accept-drop="() => canEdit"
|
||||||
@drag-start="draggingCard = true"
|
@drag-start="draggingCard = true"
|
||||||
@drag-end="draggingCard = false"
|
@drag-end="draggingCard = false"
|
||||||
@drop="($event) => onDropCard(stack.id, $event)">
|
@drop="($event) => onDropCard(stack.id, $event)">
|
||||||
@@ -102,7 +102,10 @@
|
|||||||
<transition :appear="animate && !card.animated && (card.animated=true)"
|
<transition :appear="animate && !card.animated && (card.animated=true)"
|
||||||
:appear-class="'zoom-appear-class'"
|
:appear-class="'zoom-appear-class'"
|
||||||
:appear-active-class="'zoom-appear-active-class'">
|
:appear-active-class="'zoom-appear-active-class'">
|
||||||
<CardItem :id="card.id" ref="card" :dragging="draggingCard" />
|
<CardItem :id="card.id"
|
||||||
|
ref="card"
|
||||||
|
:dragging="draggingCard"
|
||||||
|
@open-card="openCard" />
|
||||||
</transition>
|
</transition>
|
||||||
</Draggable>
|
</Draggable>
|
||||||
</Container>
|
</Container>
|
||||||
@@ -136,7 +139,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import ClickOutside from 'vue-click-outside'
|
import ClickOutside from 'vue-click-outside'
|
||||||
import { mapGetters, mapState } from 'vuex'
|
import { mapGetters, mapState } from 'vuex'
|
||||||
import { Container, Draggable } from 'vue-smooth-dnd'
|
import { Container, Draggable } from 'vue3-smooth-dnd'
|
||||||
import ArchiveIcon from 'vue-material-design-icons/Archive.vue'
|
import ArchiveIcon from 'vue-material-design-icons/Archive.vue'
|
||||||
import CardPlusOutline from 'vue-material-design-icons/CardPlusOutline.vue'
|
import CardPlusOutline from 'vue-material-design-icons/CardPlusOutline.vue'
|
||||||
import { NcActions, NcActionButton, NcModal } from '@nextcloud/vue'
|
import { NcActions, NcActionButton, NcModal } from '@nextcloud/vue'
|
||||||
@@ -171,6 +174,9 @@ export default {
|
|||||||
default: undefined,
|
default: undefined,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
emits: [
|
||||||
|
'open-card',
|
||||||
|
],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
editing: false,
|
editing: false,
|
||||||
@@ -233,6 +239,9 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
openCard(cardId) {
|
||||||
|
this.$emit('open-card', cardId)
|
||||||
|
},
|
||||||
stopCardCreation(e) {
|
stopCardCreation(e) {
|
||||||
// For some reason the submit event triggers a MouseEvent that is bubbling to the outside
|
// For some reason the submit event triggers a MouseEvent that is bubbling to the outside
|
||||||
// so we have to ignore it
|
// so we have to ignore it
|
||||||
@@ -362,10 +371,10 @@ export default {
|
|||||||
|
|
||||||
@use 'sass:math';
|
@use 'sass:math';
|
||||||
|
|
||||||
@import './../../css/variables';
|
@use './../../css/variables';
|
||||||
|
|
||||||
.stack {
|
.stack {
|
||||||
width: $stack-width + $stack-spacing * 3;
|
width: variables.$stack-width + variables.$stack-spacing * 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stack__header {
|
.stack__header {
|
||||||
@@ -373,8 +382,8 @@ export default {
|
|||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
padding-left: $card-spacing;
|
padding-left: variables.$card-spacing;
|
||||||
padding-right: $card-spacing;
|
padding-right: variables.$card-spacing;
|
||||||
margin: 6px;
|
margin: 6px;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
@@ -418,7 +427,7 @@ export default {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
max-width: calc($stack-width - 60px);
|
max-width: calc(variables.$stack-width - 60px);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
padding: 4px 4px;
|
padding: 4px 4px;
|
||||||
font-size: var(--default-font-size);
|
font-size: var(--default-font-size);
|
||||||
@@ -459,8 +468,8 @@ export default {
|
|||||||
|
|
||||||
form {
|
form {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-left: $stack-spacing;
|
margin-left: variables.$stack-spacing;
|
||||||
margin-right: $stack-spacing;
|
margin-right: variables.$stack-spacing;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: 2px solid var(--color-border-maxcontrast);
|
border: 2px solid var(--color-border-maxcontrast);
|
||||||
border-radius: var(--border-radius-large);
|
border-radius: var(--border-radius-large);
|
||||||
|
|||||||
@@ -3,15 +3,16 @@
|
|||||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
<!-- eslint-disable vue/no-v-model-argument -->
|
||||||
<template>
|
<template>
|
||||||
<NcAppSidebar v-if="currentBoard && currentCard"
|
<NcAppSidebar v-if="currentBoard && currentCard"
|
||||||
ref="cardSidebar"
|
ref="cardSidebar"
|
||||||
|
v-model:name-editable="isEditingTitle"
|
||||||
:active="tabId"
|
:active="tabId"
|
||||||
:name="displayTitle"
|
:name="displayTitle"
|
||||||
:subname="subtitle"
|
:subname="subtitle"
|
||||||
:subtitle="subtitleTooltip"
|
:subtitle="subtitleTooltip"
|
||||||
:name-editable.sync="isEditingTitle"
|
@update:name="value => titleEditing = value"
|
||||||
@update:name="(value) => titleEditing = value"
|
|
||||||
@dismiss-editing="titleEditing = currentCard.title"
|
@dismiss-editing="titleEditing = currentCard.title"
|
||||||
@submit-name="handleSubmitTitle"
|
@submit-name="handleSubmitTitle"
|
||||||
@opened="focusHeader"
|
@opened="focusHeader"
|
||||||
@@ -24,35 +25,69 @@
|
|||||||
{{ t('deck', 'Open in bigger view') }}
|
{{ t('deck', 'Open in bigger view') }}
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
|
|
||||||
<CardMenuEntries :card="currentCard" :hide-details-entry="true" />
|
<NcActionButton v-if="canEdit && !isCurrentUserAssigned"
|
||||||
|
icon="icon-user"
|
||||||
|
:close-after-click="true"
|
||||||
|
@click="assignCardToMe()">
|
||||||
|
{{ t('deck', 'Assign to me') }}
|
||||||
|
</NcActionButton>
|
||||||
|
<NcActionButton v-if="canEdit && isCurrentUserAssigned"
|
||||||
|
icon="icon-user"
|
||||||
|
:close-after-click="true"
|
||||||
|
@click="unassignCardFromMe()">
|
||||||
|
{{ t('deck', 'Unassign myself') }}
|
||||||
|
</NcActionButton>
|
||||||
|
<NcActionButton v-if="canEdit"
|
||||||
|
icon="icon-checkmark"
|
||||||
|
:close-after-click="true"
|
||||||
|
@click="changeCardDoneStatus()">
|
||||||
|
{{ currentCard.done ? t('deck', 'Mark as not done') : t('deck', 'Mark as done') }}
|
||||||
|
</NcActionButton>
|
||||||
|
<NcActionButton v-if="canEdit"
|
||||||
|
icon="icon-external"
|
||||||
|
:close-after-click="true"
|
||||||
|
@click="openCardMoveDialog">
|
||||||
|
{{ t('deck', 'Move/copy card') }}
|
||||||
|
</NcActionButton>
|
||||||
|
<NcActionButton v-for="action in cardActions"
|
||||||
|
:key="action.label"
|
||||||
|
:close-after-click="true"
|
||||||
|
:icon="action.icon"
|
||||||
|
@click="action.callback(cardRichObject)">
|
||||||
|
{{ action.label }}
|
||||||
|
</NcActionButton>
|
||||||
|
<NcActionButton v-if="canEditBoard" :close-after-click="true" @click="archiveUnarchiveCard()">
|
||||||
|
<template #icon>
|
||||||
|
<ArchiveIcon :size="20" decorative />
|
||||||
|
</template>
|
||||||
|
{{ currentCard.archived ? t('deck', 'Unarchive card') : t('deck', 'Archive card') }}
|
||||||
|
</NcActionButton>
|
||||||
|
<NcActionButton v-if="canEdit"
|
||||||
|
icon="icon-delete"
|
||||||
|
:close-after-click="true"
|
||||||
|
@click="deleteCard()">
|
||||||
|
{{ t('deck', 'Delete card') }}
|
||||||
|
</NcActionButton>
|
||||||
</template>
|
</template>
|
||||||
<template #description>
|
<template #description>
|
||||||
<NcReferenceList v-if="currentCard.referenceData"
|
<NcReferenceList v-if="currentCard.referenceData" :text="currentCard.title" :interactive="false" />
|
||||||
:text="currentCard.title"
|
|
||||||
:interactive="false" />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<NcAppSidebarTab id="details"
|
<NcAppSidebarTab id="details" :order="0" :name="t('deck', 'Details')">
|
||||||
:order="0"
|
|
||||||
:name="t('deck', 'Details')">
|
|
||||||
<CardSidebarTabDetails :card="currentCard" />
|
<CardSidebarTabDetails :card="currentCard" />
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<HomeIcon :size="20" />
|
<HomeIcon :size="20" />
|
||||||
</template>
|
</template>
|
||||||
</NcAppSidebarTab>
|
</NcAppSidebarTab>
|
||||||
|
|
||||||
<NcAppSidebarTab id="attachments"
|
<NcAppSidebarTab id="attachments" :order="1" :name="t('deck', 'Attachments')">
|
||||||
:order="1"
|
|
||||||
:name="t('deck', 'Attachments')">
|
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<AttachmentIcon :size="20" />
|
<AttachmentIcon :size="20" />
|
||||||
</template>
|
</template>
|
||||||
<CardSidebarTabAttachments :card="currentCard" />
|
<CardSidebarTabAttachments :card="currentCard" />
|
||||||
</NcAppSidebarTab>
|
</NcAppSidebarTab>
|
||||||
|
|
||||||
<NcAppSidebarTab id="comments"
|
<NcAppSidebarTab id="comments" :order="2" :name="t('deck', 'Comments')">
|
||||||
:order="2"
|
|
||||||
:name="t('deck', 'Comments')">
|
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<CommentIcon :size="20" />
|
<CommentIcon :size="20" />
|
||||||
</template>
|
</template>
|
||||||
@@ -73,7 +108,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { NcActionButton, NcAppSidebar, NcAppSidebarTab } from '@nextcloud/vue'
|
import { NcActionButton, NcAppSidebar, NcAppSidebarTab } from '@nextcloud/vue'
|
||||||
import { NcReferenceList } from '@nextcloud/vue/dist/Components/NcRichText.js'
|
import { NcReferenceList } from '@nextcloud/vue/components/NcRichText'
|
||||||
import { getCapabilities } from '@nextcloud/capabilities'
|
import { getCapabilities } from '@nextcloud/capabilities'
|
||||||
import { mapState, mapGetters } from 'vuex'
|
import { mapState, mapGetters } from 'vuex'
|
||||||
import CardSidebarTabDetails from './CardSidebarTabDetails.vue'
|
import CardSidebarTabDetails from './CardSidebarTabDetails.vue'
|
||||||
@@ -87,9 +122,15 @@ import HomeIcon from 'vue-material-design-icons/Home.vue'
|
|||||||
import CommentIcon from 'vue-material-design-icons/Comment.vue'
|
import CommentIcon from 'vue-material-design-icons/Comment.vue'
|
||||||
import ActivityIcon from 'vue-material-design-icons/LightningBolt.vue'
|
import ActivityIcon from 'vue-material-design-icons/LightningBolt.vue'
|
||||||
|
|
||||||
import { showError, showWarning } from '@nextcloud/dialogs'
|
import { showError, showWarning, showUndo } from '@nextcloud/dialogs'
|
||||||
import { getLocale } from '@nextcloud/l10n'
|
import { getLocale } from '@nextcloud/l10n'
|
||||||
import CardMenuEntries from '../cards/CardMenuEntries.vue'
|
import { emit } from '@nextcloud/event-bus'
|
||||||
|
|
||||||
|
import ArchiveIcon from 'vue-material-design-icons/Archive.vue'
|
||||||
|
import { getCurrentUser } from '@nextcloud/auth'
|
||||||
|
import { generateUrl } from '@nextcloud/router'
|
||||||
|
|
||||||
|
import '@nextcloud/dialogs/style.css'
|
||||||
|
|
||||||
const capabilities = getCapabilities()
|
const capabilities = getCapabilities()
|
||||||
|
|
||||||
@@ -108,7 +149,7 @@ export default {
|
|||||||
AttachmentIcon,
|
AttachmentIcon,
|
||||||
CommentIcon,
|
CommentIcon,
|
||||||
HomeIcon,
|
HomeIcon,
|
||||||
CardMenuEntries,
|
ArchiveIcon,
|
||||||
},
|
},
|
||||||
mixins: [relativeDate],
|
mixins: [relativeDate],
|
||||||
props: {
|
props: {
|
||||||
@@ -140,8 +181,17 @@ export default {
|
|||||||
isFullApp: (state) => state.isFullApp,
|
isFullApp: (state) => state.isFullApp,
|
||||||
currentBoard: (state) => state.currentBoard,
|
currentBoard: (state) => state.currentBoard,
|
||||||
hasCardSaveError: (state) => state.hasCardSaveError,
|
hasCardSaveError: (state) => state.hasCardSaveError,
|
||||||
|
showArchived: (state) => state.showArchived,
|
||||||
}),
|
}),
|
||||||
...mapGetters(['canEdit', 'assignables', 'cardActions', 'stackById']),
|
...mapGetters([
|
||||||
|
'canEdit',
|
||||||
|
'assignables',
|
||||||
|
'cardActions',
|
||||||
|
'stackById',
|
||||||
|
'isArchived',
|
||||||
|
'boards',
|
||||||
|
'boardById',
|
||||||
|
]),
|
||||||
currentCard() {
|
currentCard() {
|
||||||
return this.$store.getters.cardById(this.id)
|
return this.$store.getters.cardById(this.id)
|
||||||
},
|
},
|
||||||
@@ -168,6 +218,31 @@ export default {
|
|||||||
return reference ? reference.openGraphObject.name : this.currentCard.title
|
return reference ? reference.openGraphObject.name : this.currentCard.title
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
canEdit() {
|
||||||
|
return !this.currentCard.archived
|
||||||
|
},
|
||||||
|
canEditBoard() {
|
||||||
|
if (this.currentBoard) {
|
||||||
|
return this.$store.getters.canEdit
|
||||||
|
}
|
||||||
|
const board = this.$store.getters.boards.find((item) => item.id === this.currentCard.boardId)
|
||||||
|
return !!board?.permissions?.PERMISSION_EDIT
|
||||||
|
},
|
||||||
|
isCurrentUserAssigned() {
|
||||||
|
return this.currentCard.assignedUsers.find((item) => item.type === 0 && item.participant.uid === getCurrentUser()?.uid)
|
||||||
|
},
|
||||||
|
boardId() {
|
||||||
|
return this.card?.boardId ? this.currentCard.boardId : Number(this.$route.params.id)
|
||||||
|
},
|
||||||
|
cardRichObject() {
|
||||||
|
return {
|
||||||
|
id: '' + this.currentCard.id,
|
||||||
|
name: this.currentCard.title,
|
||||||
|
boardname: this.boardById(this.boardId)?.title,
|
||||||
|
stackname: this.stackById(this.currentCard.stackId)?.title,
|
||||||
|
link: window.location.protocol + '//' + window.location.host + generateUrl('/apps/deck/') + `card/${this.currentCard.id}`,
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
currentCard() {
|
currentCard() {
|
||||||
@@ -216,12 +291,46 @@ export default {
|
|||||||
formatDate(timestamp) {
|
formatDate(timestamp) {
|
||||||
return moment.unix(timestamp).locale(this.locale).format('LLLL')
|
return moment.unix(timestamp).locale(this.locale).format('LLLL')
|
||||||
},
|
},
|
||||||
|
deleteCard() {
|
||||||
|
this.$store.dispatch('deleteCard', this.currentCard)
|
||||||
|
const undoCard = { ...this.currentCard, deletedAt: 0 }
|
||||||
|
showUndo(t('deck', 'Card deleted'), () => this.$store.dispatch('cardUndoDelete', undoCard))
|
||||||
|
if (this.$router.currentRoute.name === 'card') {
|
||||||
|
this.$router.push({ name: 'board' })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
changeCardDoneStatus() {
|
||||||
|
this.$store.dispatch('changeCardDoneStatus', { ...this.currentCard, done: !this.currentCard.done })
|
||||||
|
},
|
||||||
|
archiveUnarchiveCard() {
|
||||||
|
this.$store.dispatch('archiveUnarchiveCard', { ...this.currentCard, archived: !this.currentCard.archived })
|
||||||
|
},
|
||||||
|
assignCardToMe() {
|
||||||
|
this.$store.dispatch('assignCardToUser', {
|
||||||
|
card: this.currentCard,
|
||||||
|
assignee: {
|
||||||
|
userId: getCurrentUser()?.uid,
|
||||||
|
type: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
unassignCardFromMe() {
|
||||||
|
this.$store.dispatch('removeUserFromCard', {
|
||||||
|
card: this.currentCard,
|
||||||
|
assignee: {
|
||||||
|
userId: getCurrentUser()?.uid,
|
||||||
|
type: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openCardMoveDialog() {
|
||||||
|
emit('deck:card:show-move-dialog', this.currentCard)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
||||||
section.app-sidebar__tab--active {
|
section.app-sidebar__tab--active {
|
||||||
min-height: auto;
|
min-height: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -273,6 +382,7 @@ section.app-sidebar__tab--active {
|
|||||||
z-index: 100;
|
z-index: 100;
|
||||||
background-color: var(--color-main-background);
|
background-color: var(--color-main-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-sidebar-tabs__nav {
|
.app-sidebar-tabs__nav {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 87px;
|
top: 87px;
|
||||||
@@ -285,10 +395,10 @@ section.app-sidebar__tab--active {
|
|||||||
overflow: initial;
|
overflow: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
#emptycontent, .emptycontent {
|
#emptycontent,
|
||||||
|
.emptycontent {
|
||||||
margin-top: 88px;
|
margin-top: 88px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -23,11 +23,11 @@
|
|||||||
:key="comment.id"
|
:key="comment.id"
|
||||||
:comment="comment"
|
:comment="comment"
|
||||||
@doReload="loadComments" />
|
@doReload="loadComments" />
|
||||||
<InfiniteLoading :identifier="card.id" @infinite="infiniteHandler">
|
<!-- <InfiniteLoading :identifier="card.id" @infinite="infiniteHandler">
|
||||||
<div slot="spinner" class="icon-loading" />
|
<div slot="spinner" class="icon-loading" />
|
||||||
<div slot="no-more" />
|
<div slot="no-more" />
|
||||||
<div slot="no-results" />
|
<div slot="no-results" />
|
||||||
</InfiniteLoading>
|
</InfiniteLoading> -->
|
||||||
</ul>
|
</ul>
|
||||||
<div v-else-if="isLoading" class="icon icon-loading" />
|
<div v-else-if="isLoading" class="icon icon-loading" />
|
||||||
<div v-else class="emptycontent">
|
<div v-else class="emptycontent">
|
||||||
@@ -42,7 +42,7 @@ import { mapState, mapGetters } from 'vuex'
|
|||||||
import { NcAvatar } from '@nextcloud/vue'
|
import { NcAvatar } from '@nextcloud/vue'
|
||||||
import CommentItem from './CommentItem.vue'
|
import CommentItem from './CommentItem.vue'
|
||||||
import CommentForm from './CommentForm.vue'
|
import CommentForm from './CommentForm.vue'
|
||||||
import InfiniteLoading from 'vue-infinite-loading'
|
// import InfiniteLoading from 'v3-infinite-loading'
|
||||||
import { getCurrentUser } from '@nextcloud/auth'
|
import { getCurrentUser } from '@nextcloud/auth'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -51,7 +51,7 @@ export default {
|
|||||||
NcAvatar,
|
NcAvatar,
|
||||||
CommentItem,
|
CommentItem,
|
||||||
CommentForm,
|
CommentForm,
|
||||||
InfiniteLoading,
|
// InfiniteLoading,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
card: {
|
card: {
|
||||||
|
|||||||
@@ -2,8 +2,13 @@
|
|||||||
- SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
|
- SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
|
||||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<div>
|
||||||
|
THIS HAS AT
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- <template>
|
||||||
<div class="comment-form">
|
<div class="comment-form">
|
||||||
<form @submit.prevent="submit">
|
<form @submit.prevent="submit">
|
||||||
<At ref="at"
|
<At ref="at"
|
||||||
@@ -191,4 +196,4 @@ export default {
|
|||||||
.atwho-li--avatar {
|
.atwho-li--avatar {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
</style>
|
</style> -->
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ export default {
|
|||||||
mounted() {
|
mounted() {
|
||||||
this.setupEditor()
|
this.setupEditor()
|
||||||
},
|
},
|
||||||
async beforeDestroy() {
|
async beforeUnmount() {
|
||||||
await this.destroyEditor()
|
await this.destroyEditor()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@@ -4,8 +4,12 @@
|
|||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<CardDetailEntry :label="t('deck', 'Assign a due date to this card…')" data-test="due-date-selector">
|
<CardDetailEntry :label="t('deck', 'Assign a due date to this card…')" data-test="due-date-selector">
|
||||||
<Calendar v-if="!card.done" slot="icon" :size="20" />
|
<template v-if="!card.done" #icon>
|
||||||
<CalendarCheck v-else slot="icon" :size="20" />
|
<Calendar :size="20" />
|
||||||
|
</template>
|
||||||
|
<template v-else #icon>
|
||||||
|
<CalendarCheck :size="20" />
|
||||||
|
</template>
|
||||||
<template v-if="!card.done && !card.archived">
|
<template v-if="!card.done && !card.archived">
|
||||||
<NcDateTimePickerNative v-if="duedate"
|
<NcDateTimePickerNative v-if="duedate"
|
||||||
id="card-duedate-picker"
|
id="card-duedate-picker"
|
||||||
@@ -15,7 +19,7 @@
|
|||||||
type="datetime-local" />
|
type="datetime-local" />
|
||||||
<NcActions v-if="canEdit"
|
<NcActions v-if="canEdit"
|
||||||
:menu-title="!duedate ? t('deck', 'Add due date') : null"
|
:menu-title="!duedate ? t('deck', 'Add due date') : null"
|
||||||
type="tertiary"
|
variant="tertiary"
|
||||||
data-cy-due-date-actions>
|
data-cy-due-date-actions>
|
||||||
<template v-if="!duedate" #icon>
|
<template v-if="!duedate" #icon>
|
||||||
<Plus :size="20" />
|
<Plus :size="20" />
|
||||||
@@ -48,7 +52,7 @@
|
|||||||
</NcActions>
|
</NcActions>
|
||||||
|
|
||||||
<NcButton v-if="!card.done"
|
<NcButton v-if="!card.done"
|
||||||
type="secondary"
|
variant="secondary"
|
||||||
class="completed-button"
|
class="completed-button"
|
||||||
@click="changeCardDoneStatus()">
|
@click="changeCardDoneStatus()">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
@@ -69,14 +73,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="due-actions">
|
<div class="due-actions">
|
||||||
<NcButton v-if="!card.archived"
|
<NcButton v-if="!card.archived"
|
||||||
type="tertiary"
|
variant="tertiary"
|
||||||
:name="t('deck', 'Not done')"
|
:name="t('deck', 'Not done')"
|
||||||
@click="changeCardDoneStatus()">
|
@click="changeCardDoneStatus()">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<ClearIcon :size="20" />
|
<ClearIcon :size="20" />
|
||||||
</template>
|
</template>
|
||||||
</NcButton>
|
</NcButton>
|
||||||
<NcButton type="secondary" @click="archiveUnarchiveCard()">
|
<NcButton variant="secondary" @click="archiveUnarchiveCard()">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<ArchiveIcon :size="20" />
|
<ArchiveIcon :size="20" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<div class="selector-wrapper--icon">
|
<div class="selector-wrapper--icon">
|
||||||
<TagMultiple :size="20" />
|
<TagMultiple :size="20" />
|
||||||
</div>
|
</div>
|
||||||
<NcSelect :value="assignedLabels"
|
<NcSelect v-model="assignedLabels"
|
||||||
class="selector-wrapper--selector"
|
class="selector-wrapper--selector"
|
||||||
:multiple="true"
|
:multiple="true"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../css/variables';
|
@use '../../css/variables';
|
||||||
|
|
||||||
.card-cover {
|
.card-cover {
|
||||||
height: 90px;
|
height: 90px;
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ import CardMenu from './CardMenu.vue'
|
|||||||
import CardCover from './CardCover.vue'
|
import CardCover from './CardCover.vue'
|
||||||
import DueDate from './badges/DueDate.vue'
|
import DueDate from './badges/DueDate.vue'
|
||||||
import { getCurrentUser } from '@nextcloud/auth'
|
import { getCurrentUser } from '@nextcloud/auth'
|
||||||
|
import { emit } from '@nextcloud/event-bus'
|
||||||
|
|
||||||
const TITLE_EDITING_STATE = {
|
const TITLE_EDITING_STATE = {
|
||||||
OFF: 0,
|
OFF: 0,
|
||||||
@@ -120,6 +121,7 @@ export default {
|
|||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
emits: ['open-card'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
highlight: false,
|
highlight: false,
|
||||||
@@ -226,8 +228,9 @@ export default {
|
|||||||
this.$router.push({ name: 'card', params: { id: boardId, cardId: this.card.id } }).catch(() => {})
|
this.$router.push({ name: 'card', params: { id: boardId, cardId: this.card.id } }).catch(() => {})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
emit('open-card', {
|
||||||
this.$root.$emit('open-card', this.card.id)
|
cardId: this.card.id,
|
||||||
|
})
|
||||||
},
|
},
|
||||||
triggerEditTitle() {
|
triggerEditTitle() {
|
||||||
this.editingTitle = TITLE_EDITING_STATE.PENDING
|
this.editingTitle = TITLE_EDITING_STATE.PENDING
|
||||||
@@ -318,8 +321,8 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import './../../css/animations';
|
@use './../../css/animations';
|
||||||
@import './../../css/variables';
|
@use './../../css/variables';
|
||||||
|
|
||||||
@mixin dark-card {
|
@mixin dark-card {
|
||||||
border: 2px solid var(--color-border-dark);
|
border: 2px solid var(--color-border-dark);
|
||||||
@@ -331,8 +334,8 @@ export default {
|
|||||||
border-radius: var(--border-radius-large);
|
border-radius: var(--border-radius-large);
|
||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
background-color: var(--color-main-background);
|
background-color: var(--color-main-background);
|
||||||
margin-bottom: $card-spacing;
|
margin-bottom: variables.$card-spacing;
|
||||||
padding: var(--default-grid-baseline) $card-padding;
|
padding: var(--default-grid-baseline) variables.$card-padding;
|
||||||
border: 2px solid var(--color-border-dark);
|
border: 2px solid var(--color-border-dark);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -470,7 +473,7 @@ export default {
|
|||||||
width: 32px;
|
width: 32px;
|
||||||
}
|
}
|
||||||
&.has-labels {
|
&.has-labels {
|
||||||
padding-bottom: $card-padding;
|
padding-bottom: variables.$card-padding;
|
||||||
}
|
}
|
||||||
.labels {
|
.labels {
|
||||||
height: 6px;
|
height: 6px;
|
||||||
|
|||||||
@@ -6,40 +6,190 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="card" class="card-menu" @click.stop.prevent>
|
<div v-if="card" class="card-menu" @click.stop.prevent>
|
||||||
<NcButton v-if="card.referenceData"
|
<NcButton v-if="card.referenceData"
|
||||||
type="tertiary"
|
variant="tertiary"
|
||||||
:title="t('deck','Open link')"
|
:title="t('deck', 'Open link')"
|
||||||
@click="openLink">
|
@click="openLink">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<LinkIcon :size="20" />
|
<LinkIcon :size="20" />
|
||||||
</template>
|
</template>
|
||||||
</NcButton>
|
</NcButton>
|
||||||
<NcActions>
|
<NcActions>
|
||||||
<CardMenuEntries :card="card" @edit-title="editTitle" />
|
<NcActionButton v-if="!hideDetailsEntry" :close-after-click="true" @click="openCard">
|
||||||
|
<template #icon>
|
||||||
|
<CardBulletedIcon icon :size="20" decorative />
|
||||||
|
</template>
|
||||||
|
{{ t('deck', 'Card details') }}
|
||||||
|
</NcActionButton>
|
||||||
|
<NcActionButton v-if="canEdit" :close-after-click="true" @click="editTitle">
|
||||||
|
<template #icon>
|
||||||
|
<PencilIcon :size="20" decorative />
|
||||||
|
</template>
|
||||||
|
{{ t('deck', 'Edit title') }}
|
||||||
|
</NcActionButton>
|
||||||
|
<NcActionButton v-if="canEdit && !isCurrentUserAssigned"
|
||||||
|
icon="icon-user"
|
||||||
|
:close-after-click="true"
|
||||||
|
@click="assignCardToMe()">
|
||||||
|
{{ t('deck', 'Assign to me') }}
|
||||||
|
</NcActionButton>
|
||||||
|
<NcActionButton v-if="canEdit && isCurrentUserAssigned"
|
||||||
|
icon="icon-user"
|
||||||
|
:close-after-click="true"
|
||||||
|
@click="unassignCardFromMe()">
|
||||||
|
{{ t('deck', 'Unassign myself') }}
|
||||||
|
</NcActionButton>
|
||||||
|
<NcActionButton v-if="canEdit"
|
||||||
|
icon="icon-checkmark"
|
||||||
|
:close-after-click="true"
|
||||||
|
@click="changeCardDoneStatus()">
|
||||||
|
{{ card.done ? t('deck', 'Mark as not done') : t('deck', 'Mark as done') }}
|
||||||
|
</NcActionButton>
|
||||||
|
<NcActionButton v-if="canEdit"
|
||||||
|
icon="icon-external"
|
||||||
|
:close-after-click="true"
|
||||||
|
@click="openCardMoveDialog">
|
||||||
|
{{ t('deck', 'Move/copy card') }}
|
||||||
|
</NcActionButton>
|
||||||
|
<NcActionButton v-for="action in cardActions"
|
||||||
|
:key="action.label"
|
||||||
|
:close-after-click="true"
|
||||||
|
:icon="action.icon"
|
||||||
|
@click="action.callback(cardRichObject)">
|
||||||
|
{{ action.label }}
|
||||||
|
</NcActionButton>
|
||||||
|
<NcActionButton v-if="canEditBoard" :close-after-click="true" @click="archiveUnarchiveCard()">
|
||||||
|
<template #icon>
|
||||||
|
<ArchiveIcon :size="20" decorative />
|
||||||
|
</template>
|
||||||
|
{{ card.archived ? t('deck', 'Unarchive card') : t('deck', 'Archive card') }}
|
||||||
|
</NcActionButton>
|
||||||
|
<NcActionButton v-if="canEdit"
|
||||||
|
icon="icon-delete"
|
||||||
|
:close-after-click="true"
|
||||||
|
@click="deleteCard()">
|
||||||
|
{{ t('deck', 'Delete card') }}
|
||||||
|
</NcActionButton>
|
||||||
</NcActions>
|
</NcActions>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { NcActions, NcButton } from '@nextcloud/vue'
|
import { NcActions, NcButton, NcActionButton } from '@nextcloud/vue'
|
||||||
import LinkIcon from 'vue-material-design-icons/Link.vue'
|
import LinkIcon from 'vue-material-design-icons/Link.vue'
|
||||||
import CardMenuEntries from './CardMenuEntries.vue'
|
import ArchiveIcon from 'vue-material-design-icons/Archive.vue'
|
||||||
|
import { getCurrentUser } from '@nextcloud/auth'
|
||||||
|
import CardBulletedIcon from 'vue-material-design-icons/CardBulleted.vue'
|
||||||
|
import PencilIcon from 'vue-material-design-icons/Pencil.vue'
|
||||||
|
import { mapGetters, mapState } from 'vuex'
|
||||||
|
import { showUndo } from '@nextcloud/dialogs'
|
||||||
|
import { generateUrl } from '@nextcloud/router'
|
||||||
|
|
||||||
|
import '@nextcloud/dialogs/style.css'
|
||||||
|
import { emit } from '@nextcloud/event-bus'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CardMenu',
|
name: 'CardMenu',
|
||||||
components: { NcActions, NcButton, LinkIcon, CardMenuEntries },
|
components: { NcActions, NcButton, LinkIcon, NcActionButton, PencilIcon, CardBulletedIcon, ArchiveIcon },
|
||||||
props: {
|
props: {
|
||||||
card: {
|
card: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
hideDetailsEntry: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
emits: ['edit-title'],
|
emits: ['edit-title'],
|
||||||
|
computed: {
|
||||||
|
...mapGetters([
|
||||||
|
'isArchived',
|
||||||
|
'boards',
|
||||||
|
'cardActions',
|
||||||
|
'stackById',
|
||||||
|
'boardById',
|
||||||
|
]),
|
||||||
|
...mapState({
|
||||||
|
showArchived: state => state.showArchived,
|
||||||
|
currentBoard: state => state.currentBoard,
|
||||||
|
}),
|
||||||
|
canEdit() {
|
||||||
|
return !this.card.archived
|
||||||
|
},
|
||||||
|
canEditBoard() {
|
||||||
|
if (this.currentBoard) {
|
||||||
|
return this.$store.getters.canEdit
|
||||||
|
}
|
||||||
|
const board = this.$store.getters.boards.find((item) => item.id === this.card.boardId)
|
||||||
|
return !!board?.permissions?.PERMISSION_EDIT
|
||||||
|
},
|
||||||
|
isCurrentUserAssigned() {
|
||||||
|
return this.card.assignedUsers.find((item) => item.type === 0 && item.participant.uid === getCurrentUser()?.uid)
|
||||||
|
},
|
||||||
|
boardId() {
|
||||||
|
return this.card?.boardId ? this.card.boardId : Number(this.$route.params.id)
|
||||||
|
},
|
||||||
|
cardRichObject() {
|
||||||
|
return {
|
||||||
|
id: '' + this.card.id,
|
||||||
|
name: this.card.title,
|
||||||
|
boardname: this.boardById(this.boardId)?.title,
|
||||||
|
stackname: this.stackById(this.card.stackId)?.title,
|
||||||
|
link: window.location.protocol + '//' + window.location.host + generateUrl('/apps/deck/') + `card/${this.card.id}`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
openLink() {
|
openLink() {
|
||||||
window.open(this.card?.referenceData?.openGraphObject?.link)
|
window.open(this.card?.referenceData?.openGraphObject?.link)
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
editTitle(id) {
|
openCard() {
|
||||||
this.$emit('edit-title', id)
|
const boardId = this.card?.boardId ? this.card.boardId : this.$route?.params.id ?? this.currentBoard.id
|
||||||
|
|
||||||
|
if (this.$router) {
|
||||||
|
this.$router?.push({ name: 'card', params: { id: boardId, cardId: this.card.id } }).catch(() => { })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$root.$emit('open-card', this.card.id)
|
||||||
|
},
|
||||||
|
editTitle() {
|
||||||
|
this.$emit('edit-title', this.card.id)
|
||||||
|
},
|
||||||
|
deleteCard() {
|
||||||
|
this.$store.dispatch('deleteCard', this.card)
|
||||||
|
const undoCard = { ...this.card, deletedAt: 0 }
|
||||||
|
showUndo(t('deck', 'Card deleted'), () => this.$store.dispatch('cardUndoDelete', undoCard))
|
||||||
|
if (this.$router.currentRoute.name === 'card') {
|
||||||
|
this.$router.push({ name: 'board' })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
changeCardDoneStatus() {
|
||||||
|
this.$store.dispatch('changeCardDoneStatus', { ...this.card, done: !this.card.done })
|
||||||
|
},
|
||||||
|
archiveUnarchiveCard() {
|
||||||
|
this.$store.dispatch('archiveUnarchiveCard', { ...this.card, archived: !this.card.archived })
|
||||||
|
},
|
||||||
|
assignCardToMe() {
|
||||||
|
this.$store.dispatch('assignCardToUser', {
|
||||||
|
card: this.card,
|
||||||
|
assignee: {
|
||||||
|
userId: getCurrentUser()?.uid,
|
||||||
|
type: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
unassignCardFromMe() {
|
||||||
|
this.$store.dispatch('removeUserFromCard', {
|
||||||
|
card: this.card,
|
||||||
|
assignee: {
|
||||||
|
userId: getCurrentUser()?.uid,
|
||||||
|
type: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openCardMoveDialog() {
|
||||||
|
emit('deck:card:show-move-dialog', this.card)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,187 +0,0 @@
|
|||||||
<!--
|
|
||||||
- SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
|
||||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<NcActionButton v-if="!hideDetailsEntry" :close-after-click="true" @click="openCard">
|
|
||||||
<CardBulletedIcon slot="icon" :size="20" decorative />
|
|
||||||
{{ t('deck', 'Card details') }}
|
|
||||||
</NcActionButton>
|
|
||||||
<NcActionButton v-if="canEdit" :close-after-click="true" @click="editTitle">
|
|
||||||
<template #icon>
|
|
||||||
<PencilIcon :size="20" decorative />
|
|
||||||
</template>
|
|
||||||
{{ t('deck', 'Edit title') }}
|
|
||||||
</NcActionButton>
|
|
||||||
<NcActionButton v-if="canEdit && !isCurrentUserAssigned"
|
|
||||||
icon="icon-user"
|
|
||||||
:close-after-click="true"
|
|
||||||
@click="assignCardToMe()">
|
|
||||||
{{ t('deck', 'Assign to me') }}
|
|
||||||
</NcActionButton>
|
|
||||||
<NcActionButton v-if="canEdit && isCurrentUserAssigned"
|
|
||||||
icon="icon-user"
|
|
||||||
:close-after-click="true"
|
|
||||||
@click="unassignCardFromMe()">
|
|
||||||
{{ t('deck', 'Unassign myself') }}
|
|
||||||
</NcActionButton>
|
|
||||||
<NcActionButton v-if="canEdit"
|
|
||||||
icon="icon-checkmark"
|
|
||||||
:close-after-click="true"
|
|
||||||
@click="changeCardDoneStatus()">
|
|
||||||
{{ card.done ? t('deck', 'Mark as not done') : t('deck', 'Mark as done') }}
|
|
||||||
</NcActionButton>
|
|
||||||
<NcActionButton v-if="canEdit"
|
|
||||||
icon="icon-external"
|
|
||||||
:close-after-click="true"
|
|
||||||
@click="openCardMoveDialog">
|
|
||||||
{{ t('deck', 'Move/copy card') }}
|
|
||||||
</NcActionButton>
|
|
||||||
<NcActionButton v-for="action in cardActions"
|
|
||||||
:key="action.label"
|
|
||||||
:close-after-click="true"
|
|
||||||
:icon="action.icon"
|
|
||||||
@click="action.callback(cardRichObject)">
|
|
||||||
{{ action.label }}
|
|
||||||
</NcActionButton>
|
|
||||||
<NcActionButton v-if="canEditBoard" :close-after-click="true" @click="archiveUnarchiveCard()">
|
|
||||||
<template #icon>
|
|
||||||
<ArchiveIcon :size="20" decorative />
|
|
||||||
</template>
|
|
||||||
{{ card.archived ? t('deck', 'Unarchive card') : t('deck', 'Archive card') }}
|
|
||||||
</NcActionButton>
|
|
||||||
<NcActionButton v-if="canEdit"
|
|
||||||
icon="icon-delete"
|
|
||||||
:close-after-click="true"
|
|
||||||
@click="deleteCard()">
|
|
||||||
{{ t('deck', 'Delete card') }}
|
|
||||||
</NcActionButton>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import { NcActionButton } from '@nextcloud/vue'
|
|
||||||
import { mapGetters, mapState } from 'vuex'
|
|
||||||
import ArchiveIcon from 'vue-material-design-icons/Archive.vue'
|
|
||||||
import CardBulletedIcon from 'vue-material-design-icons/CardBulleted.vue'
|
|
||||||
import PencilIcon from 'vue-material-design-icons/Pencil.vue'
|
|
||||||
import { generateUrl } from '@nextcloud/router'
|
|
||||||
import { getCurrentUser } from '@nextcloud/auth'
|
|
||||||
import { showUndo } from '@nextcloud/dialogs'
|
|
||||||
|
|
||||||
import '@nextcloud/dialogs/style.css'
|
|
||||||
import { emit } from '@nextcloud/event-bus'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'CardMenuEntries',
|
|
||||||
components: { NcActionButton, ArchiveIcon, CardBulletedIcon, PencilIcon },
|
|
||||||
props: {
|
|
||||||
card: {
|
|
||||||
type: Object,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
hideDetailsEntry: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
emits: ['edit-title'],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
modalShow: false,
|
|
||||||
selectedBoard: '',
|
|
||||||
selectedStack: '',
|
|
||||||
stacksFromBoard: [],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapGetters([
|
|
||||||
'isArchived',
|
|
||||||
'boards',
|
|
||||||
'cardActions',
|
|
||||||
'stackById',
|
|
||||||
'boardById',
|
|
||||||
]),
|
|
||||||
...mapState({
|
|
||||||
showArchived: state => state.showArchived,
|
|
||||||
currentBoard: state => state.currentBoard,
|
|
||||||
}),
|
|
||||||
canEdit() {
|
|
||||||
return !this.card.archived
|
|
||||||
},
|
|
||||||
canEditBoard() {
|
|
||||||
if (this.currentBoard) {
|
|
||||||
return this.$store.getters.canEdit
|
|
||||||
}
|
|
||||||
const board = this.$store.getters.boards.find((item) => item.id === this.card.boardId)
|
|
||||||
return !!board?.permissions?.PERMISSION_EDIT
|
|
||||||
},
|
|
||||||
isCurrentUserAssigned() {
|
|
||||||
return this.card.assignedUsers.find((item) => item.type === 0 && item.participant.uid === getCurrentUser()?.uid)
|
|
||||||
},
|
|
||||||
boardId() {
|
|
||||||
return this.card?.boardId ? this.card.boardId : Number(this.$route.params.id)
|
|
||||||
},
|
|
||||||
cardRichObject() {
|
|
||||||
return {
|
|
||||||
id: '' + this.card.id,
|
|
||||||
name: this.card.title,
|
|
||||||
boardname: this.boardById(this.boardId)?.title,
|
|
||||||
stackname: this.stackById(this.card.stackId)?.title,
|
|
||||||
link: window.location.protocol + '//' + window.location.host + generateUrl('/apps/deck/') + `card/${this.card.id}`,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
openCard() {
|
|
||||||
const boardId = this.card?.boardId ? this.card.boardId : this.$route?.params.id ?? this.currentBoard.id
|
|
||||||
|
|
||||||
if (this.$router) {
|
|
||||||
this.$router?.push({ name: 'card', params: { id: boardId, cardId: this.card.id } }).catch(() => {})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$root.$emit('open-card', this.card.id)
|
|
||||||
},
|
|
||||||
editTitle() {
|
|
||||||
this.$emit('edit-title', this.card.id)
|
|
||||||
},
|
|
||||||
deleteCard() {
|
|
||||||
this.$store.dispatch('deleteCard', this.card)
|
|
||||||
const undoCard = { ...this.card, deletedAt: 0 }
|
|
||||||
showUndo(t('deck', 'Card deleted'), () => this.$store.dispatch('cardUndoDelete', undoCard))
|
|
||||||
if (this.$router.currentRoute.name === 'card') {
|
|
||||||
this.$router.push({ name: 'board' })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
changeCardDoneStatus() {
|
|
||||||
this.$store.dispatch('changeCardDoneStatus', { ...this.card, done: !this.card.done })
|
|
||||||
},
|
|
||||||
archiveUnarchiveCard() {
|
|
||||||
this.$store.dispatch('archiveUnarchiveCard', { ...this.card, archived: !this.card.archived })
|
|
||||||
},
|
|
||||||
assignCardToMe() {
|
|
||||||
this.$store.dispatch('assignCardToUser', {
|
|
||||||
card: this.card,
|
|
||||||
assignee: {
|
|
||||||
userId: getCurrentUser()?.uid,
|
|
||||||
type: 0,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
unassignCardFromMe() {
|
|
||||||
this.$store.dispatch('removeUserFromCard', {
|
|
||||||
card: this.card,
|
|
||||||
assignee: {
|
|
||||||
userId: getCurrentUser()?.uid,
|
|
||||||
type: 0,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
openCardMoveDialog() {
|
|
||||||
emit('deck:card:show-move-dialog', this.card)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -151,16 +151,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { NcModal, Tooltip } from '@nextcloud/vue'
|
import { NcModal } from '@nextcloud/vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'HelpModal',
|
name: 'HelpModal',
|
||||||
components: {
|
components: {
|
||||||
NcModal,
|
NcModal,
|
||||||
},
|
},
|
||||||
directives: {
|
|
||||||
Tooltip,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -13,12 +13,12 @@
|
|||||||
</NcColorPicker>
|
</NcColorPicker>
|
||||||
<form @submit.prevent.stop="createBoard">
|
<form @submit.prevent.stop="createBoard">
|
||||||
<NcTextField ref="inputField"
|
<NcTextField ref="inputField"
|
||||||
|
v-model="value"
|
||||||
:disable="loading"
|
:disable="loading"
|
||||||
:value.sync="value"
|
|
||||||
:placeholder="t('deck', 'Board name')"
|
:placeholder="t('deck', 'Board name')"
|
||||||
type="text"
|
type="text"
|
||||||
required />
|
required />
|
||||||
<NcButton type="tertiary"
|
<NcButton variant="tertiary"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
:title="t('deck', 'Cancel edit')"
|
:title="t('deck', 'Cancel edit')"
|
||||||
@click.stop.prevent="cancelEdit">
|
@click.stop.prevent="cancelEdit">
|
||||||
@@ -26,8 +26,8 @@
|
|||||||
<CloseIcon :size="20" />
|
<CloseIcon :size="20" />
|
||||||
</template>
|
</template>
|
||||||
</NcButton>
|
</NcButton>
|
||||||
<NcButton type="tertiary"
|
<NcButton variant="tertiary"
|
||||||
native-type="submit"
|
type="submit"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
:title="t('deck', 'Save board')">
|
:title="t('deck', 'Save board')">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
@@ -77,6 +77,7 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
async createBoard(e) {
|
async createBoard(e) {
|
||||||
|
alert('createBoard called')
|
||||||
this.loading = true
|
this.loading = true
|
||||||
const title = this.value.trim()
|
const title = this.value.trim()
|
||||||
await this.$store.dispatch('createBoard', {
|
await this.$store.dispatch('createBoard', {
|
||||||
@@ -88,6 +89,7 @@ export default {
|
|||||||
this.color = randomColor()
|
this.color = randomColor()
|
||||||
},
|
},
|
||||||
cancelEdit(e) {
|
cancelEdit(e) {
|
||||||
|
alert('cancelEdit called')
|
||||||
this.editing = false
|
this.editing = false
|
||||||
this.color = randomColor()
|
this.color = randomColor()
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<AccountIcon v-if="board.acl.length > 0" />
|
<AccountIcon v-if="board.acl.length > 0" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="!deleted" slot="actions">
|
<template v-if="!deleted" #actions>
|
||||||
<template v-if="!isDueSubmenuActive">
|
<template v-if="!isDueSubmenuActive">
|
||||||
<NcActionButton icon="icon-info"
|
<NcActionButton icon="icon-info"
|
||||||
:close-after-click="true"
|
:close-after-click="true"
|
||||||
@@ -129,17 +129,17 @@
|
|||||||
:placeholder="t('deck', 'Board name')"
|
:placeholder="t('deck', 'Board name')"
|
||||||
type="text"
|
type="text"
|
||||||
required />
|
required />
|
||||||
<NcButton type="tertiary"
|
<NcButton variant="tertiary"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
native-type="submit"
|
type="submit"
|
||||||
:title="t('deck', 'Cancel edit')"
|
:title="t('deck', 'Cancel edit')"
|
||||||
@click.stop.prevent="cancelEdit">
|
@click.stop.prevent="cancelEdit">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<CloseIcon :size="20" />
|
<CloseIcon :size="20" />
|
||||||
</template>
|
</template>
|
||||||
</NcButton>
|
</NcButton>
|
||||||
<NcButton type="tertiary"
|
<NcButton variant="tertiary"
|
||||||
native-type="submit"
|
type="submit"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
:title="t('deck', 'Save board')">
|
:title="t('deck', 'Save board')">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
@@ -152,7 +152,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { NcAppNavigationIconBullet, NcAppNavigationItem, NcColorPicker, NcButton, NcTextField, NcActionButton } from '@nextcloud/vue'
|
import { NcAppNavigationIconBullet, NcAppNavigationItem, NcColorPicker, NcButton, NcTextField, NcActionButton, NcLoadingIcon } from '@nextcloud/vue'
|
||||||
import ClickOutside from 'vue-click-outside'
|
import ClickOutside from 'vue-click-outside'
|
||||||
import ArchiveIcon from 'vue-material-design-icons/Archive.vue'
|
import ArchiveIcon from 'vue-material-design-icons/Archive.vue'
|
||||||
import CloneIcon from 'vue-material-design-icons/ContentDuplicate.vue'
|
import CloneIcon from 'vue-material-design-icons/ContentDuplicate.vue'
|
||||||
@@ -186,6 +186,7 @@ export default {
|
|||||||
CheckIcon,
|
CheckIcon,
|
||||||
BoardCloneModal,
|
BoardCloneModal,
|
||||||
BoardExportModal,
|
BoardExportModal,
|
||||||
|
NcLoadingIcon,
|
||||||
},
|
},
|
||||||
directives: {
|
directives: {
|
||||||
ClickOutside,
|
ClickOutside,
|
||||||
|
|||||||
@@ -31,10 +31,6 @@ export default {
|
|||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
id: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
text: {
|
text: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
|
|||||||
@@ -5,16 +5,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<NcDialog :name="t('deck', 'Clone {boardTitle}', {boardTitle: boardTitle})" :show="true" @close="close(false)">
|
<NcDialog :name="t('deck', 'Clone {boardTitle}', {boardTitle: boardTitle})" :show="true" @close="close(false)">
|
||||||
<div class="modal__content">
|
<div class="modal__content">
|
||||||
<NcCheckboxRadioSwitch :checked.sync="withCards">
|
<NcCheckboxRadioSwitch v-model="withCards">
|
||||||
{{ t('deck', 'Clone cards') }}
|
{{ t('deck', 'Clone cards') }}
|
||||||
</NcCheckboxRadioSwitch>
|
</NcCheckboxRadioSwitch>
|
||||||
<NcCheckboxRadioSwitch v-if="withCards" :checked.sync="withAssignments">
|
<NcCheckboxRadioSwitch v-if="withCards" v-model="withAssignments">
|
||||||
{{ t('deck', 'Clone assignments') }}
|
{{ t('deck', 'Clone assignments') }}
|
||||||
</NcCheckboxRadioSwitch>
|
</NcCheckboxRadioSwitch>
|
||||||
<NcCheckboxRadioSwitch v-if="withCards" :checked.sync="withLabels">
|
<NcCheckboxRadioSwitch v-if="withCards" v-model="withLabels">
|
||||||
{{ t('deck', 'Clone labels') }}
|
{{ t('deck', 'Clone labels') }}
|
||||||
</NcCheckboxRadioSwitch>
|
</NcCheckboxRadioSwitch>
|
||||||
<NcCheckboxRadioSwitch v-if="withCards" :checked.sync="withDueDate">
|
<NcCheckboxRadioSwitch v-if="withCards" v-model="withDueDate">
|
||||||
{{ t('deck', 'Clone due dates') }}
|
{{ t('deck', 'Clone due dates') }}
|
||||||
</NcCheckboxRadioSwitch>
|
</NcCheckboxRadioSwitch>
|
||||||
<div v-if="withCards" class="accordion" :class="{ 'is-open': accordionOpen }">
|
<div v-if="withCards" class="accordion" :class="{ 'is-open': accordionOpen }">
|
||||||
@@ -25,10 +25,10 @@
|
|||||||
{{ t('deck', 'Advanced options') }}
|
{{ t('deck', 'Advanced options') }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="accordionOpen" class="accordion__content">
|
<div v-if="accordionOpen" class="accordion__content">
|
||||||
<NcCheckboxRadioSwitch v-if="withCards" :checked.sync="moveCardsToLeftStack">
|
<NcCheckboxRadioSwitch v-if="withCards" v-model="moveCardsToLeftStack">
|
||||||
{{ t('deck', 'Move all cards to the first list') }}
|
{{ t('deck', 'Move all cards to the first list') }}
|
||||||
</NcCheckboxRadioSwitch>
|
</NcCheckboxRadioSwitch>
|
||||||
<NcCheckboxRadioSwitch v-if="withCards" :checked.sync="restoreArchivedCards">
|
<NcCheckboxRadioSwitch v-if="withCards" v-model="restoreArchivedCards">
|
||||||
{{ t('deck', 'Restore archived cards') }}
|
{{ t('deck', 'Restore archived cards') }}
|
||||||
</NcCheckboxRadioSwitch>
|
</NcCheckboxRadioSwitch>
|
||||||
</div>
|
</div>
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
<NcButton @click="cancel">
|
<NcButton @click="cancel">
|
||||||
{{ t('deck', 'Cancel') }}
|
{{ t('deck', 'Cancel') }}
|
||||||
</NcButton>
|
</NcButton>
|
||||||
<NcButton type="primary" @click="save">
|
<NcButton variant="primary" @click="save">
|
||||||
{{ t('deck', 'Clone') }}
|
{{ t('deck', 'Clone') }}
|
||||||
</NcButton>
|
</NcButton>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -5,13 +5,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<NcDialog :name="t('deck', 'Export {boardTitle}', {boardTitle: boardTitle})" @update:open="close">
|
<NcDialog :name="t('deck', 'Export {boardTitle}', {boardTitle: boardTitle})" @update:open="close">
|
||||||
<div class="modal__content">
|
<div class="modal__content">
|
||||||
<NcCheckboxRadioSwitch :checked.sync="exportFormat"
|
<NcCheckboxRadioSwitch v-model="exportFormat"
|
||||||
value="json"
|
value="json"
|
||||||
type="radio"
|
type="radio"
|
||||||
name="board_export_format">
|
name="board_export_format">
|
||||||
{{ t('deck', 'Export as JSON') }}
|
{{ t('deck', 'Export as JSON') }}
|
||||||
</NcCheckboxRadioSwitch>
|
</NcCheckboxRadioSwitch>
|
||||||
<NcCheckboxRadioSwitch :checked.sync="exportFormat"
|
<NcCheckboxRadioSwitch v-model="exportFormat"
|
||||||
value="csv"
|
value="csv"
|
||||||
type="radio"
|
type="radio"
|
||||||
name="board_export_format">
|
name="board_export_format">
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
<NcButton @click="close">
|
<NcButton @click="close">
|
||||||
{{ t('deck', 'Cancel') }}
|
{{ t('deck', 'Cancel') }}
|
||||||
</NcButton>
|
</NcButton>
|
||||||
<NcButton type="primary" @click="exportBoard">
|
<NcButton variant="primary" @click="exportBoard">
|
||||||
{{ t('deck', 'Export') }}
|
{{ t('deck', 'Export') }}
|
||||||
</NcButton>
|
</NcButton>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import './../../css/variables';
|
@use './../../css/variables';
|
||||||
|
|
||||||
.overview-wrapper {
|
.overview-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -162,16 +162,16 @@ export default {
|
|||||||
overflow-x: scroll;
|
overflow-x: scroll;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
padding-left: $board-spacing;
|
padding-left: variables.$board-spacing;
|
||||||
padding-right: $board-spacing;
|
padding-right: variables.$board-spacing;
|
||||||
|
|
||||||
.dashboard-column {
|
.dashboard-column {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-width: $stack-width;
|
min-width: variables.$stack-width;
|
||||||
width: $stack-width;
|
width: variables.$stack-width;
|
||||||
margin-left: $stack-spacing;
|
margin-left: variables.$stack-spacing;
|
||||||
margin-right: $stack-spacing;
|
margin-right: variables.$stack-spacing;
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: var(--default-font-size);
|
font-size: var(--default-font-size);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="searchQuery!==''" class="global-search">
|
<div v-if="searchQuery !== ''" class="global-search">
|
||||||
<h2>
|
<h2>
|
||||||
<NcRichText :text="t('deck', 'Search for {searchQuery} in all boards')" :arguments="queryStringArgs" />
|
<NcRichText :text="t('deck', 'Search for {searchQuery} in all boards')" :arguments="queryStringArgs" />
|
||||||
<div v-if="loading" class="icon-loading-small" />
|
<div v-if="loading" class="icon-loading-small" />
|
||||||
@@ -19,13 +19,13 @@
|
|||||||
:key="card.id"
|
:key="card.id"
|
||||||
:standalone="true" />
|
:standalone="true" />
|
||||||
<Placeholder v-if="loading" />
|
<Placeholder v-if="loading" />
|
||||||
<InfiniteLoading :identifier="searchQuery" @infinite="infiniteHandler">
|
<!-- <InfiniteLoading :identifier="searchQuery" @infinite="infiniteHandler">
|
||||||
<div slot="spinner" />
|
<div slot="spinner" />
|
||||||
<div slot="no-more" />
|
<div slot="no-more" />
|
||||||
<div slot="no-results">
|
<div slot="no-results">
|
||||||
{{ t('deck', 'No results found') }}
|
{{ t('deck', 'No results found') }}
|
||||||
</div>
|
</div>
|
||||||
</InfiniteLoading>
|
</InfiniteLoading> -->
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<p>{{ t('deck', 'No results found') }}</p>
|
<p>{{ t('deck', 'No results found') }}</p>
|
||||||
@@ -39,7 +39,7 @@ import CardItem from '../cards/CardItem.vue'
|
|||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import axios from '@nextcloud/axios'
|
import axios from '@nextcloud/axios'
|
||||||
import { generateOcsUrl } from '@nextcloud/router'
|
import { generateOcsUrl } from '@nextcloud/router'
|
||||||
import InfiniteLoading from 'vue-infinite-loading'
|
// import InfiniteLoading from 'v3-infinite-loading'
|
||||||
import Placeholder from './Placeholder.vue'
|
import Placeholder from './Placeholder.vue'
|
||||||
import { NcActions, NcActionButton, NcRichText } from '@nextcloud/vue'
|
import { NcActions, NcActionButton, NcRichText } from '@nextcloud/vue'
|
||||||
|
|
||||||
@@ -70,7 +70,14 @@ function search({ query, cursor }) {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'GlobalSearchResults',
|
name: 'GlobalSearchResults',
|
||||||
components: { CardItem, InfiniteLoading, NcRichText, Placeholder, NcActions, NcActionButton },
|
components: {
|
||||||
|
CardItem,
|
||||||
|
// InfiniteLoading,
|
||||||
|
NcRichText,
|
||||||
|
Placeholder,
|
||||||
|
NcActions,
|
||||||
|
NcActionButton,
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
results: [],
|
results: [],
|
||||||
@@ -155,11 +162,11 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../css/variables';
|
@use '../../css/variables';
|
||||||
|
|
||||||
.global-search {
|
.global-search {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: $board-spacing + $stack-spacing;
|
padding: variables.$board-spacing + variables.$stack-spacing;
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
min-height: 35vh;
|
min-height: 35vh;
|
||||||
@@ -175,6 +182,7 @@ export default {
|
|||||||
top: 10px;
|
top: 10px;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-wrapper {
|
.search-wrapper {
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -182,13 +190,14 @@ export default {
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 > div {
|
h2>div {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
||||||
&.icon-loading-small {
|
&.icon-loading-small {
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h2:deep(span) {
|
h2:deep(span) {
|
||||||
background-color: var(--color-background-dark);
|
background-color: var(--color-background-dark);
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
@@ -199,13 +208,14 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
||||||
& > div {
|
&>div {
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:deep(.card) {
|
&:deep(.card) {
|
||||||
width: $stack-width;
|
width: variables.$stack-width;
|
||||||
margin-right: $stack-spacing;
|
margin-right: variables.$stack-spacing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -58,18 +58,18 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../css/variables';
|
@use '../../css/variables';
|
||||||
$clickable-area: var(--default-clickable-area);
|
$clickable-area: var(--default-clickable-area);
|
||||||
|
|
||||||
.card--placeholder {
|
.card--placeholder {
|
||||||
width: $stack-width;
|
width: variables.$stack-width;
|
||||||
margin-right: $stack-spacing;
|
margin-right: variables.$stack-spacing;
|
||||||
padding: $card-padding;
|
padding: variables.$card-padding;
|
||||||
transition: box-shadow 0.1s ease-in-out;
|
transition: box-shadow 0.1s ease-in-out;
|
||||||
box-shadow: 0 0 2px 0 var(--color-box-shadow);
|
box-shadow: 0 0 2px 0 var(--color-box-shadow);
|
||||||
border-radius: var(--border-radius-large);
|
border-radius: var(--border-radius-large);
|
||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
margin-bottom: $card-spacing;
|
margin-bottom: variables.$card-spacing;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,38 +3,34 @@
|
|||||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Vue from 'vue'
|
|
||||||
|
|
||||||
import './../css/collections.css'
|
import './../css/collections.css'
|
||||||
import FileSharingPicker from './views/FileSharingPicker.js'
|
import FileSharingPicker from './views/FileSharingPicker.js'
|
||||||
import { buildSelector } from './helpers/selector.js'
|
import { buildSelector } from './helpers/selector.js'
|
||||||
|
|
||||||
import './shared-init.js'
|
import './shared-init.js'
|
||||||
|
|
||||||
Vue.prototype.t = t
|
export function initCollections() {
|
||||||
Vue.prototype.n = n
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
Vue.prototype.OC = OC
|
if (OCA.Sharing && OCA.Sharing.ShareSearch) {
|
||||||
|
OCA.Sharing.ShareSearch.addNewResult(FileSharingPicker)
|
||||||
|
}
|
||||||
|
|
||||||
window.addEventListener('DOMContentLoaded', () => {
|
window.OCP.Collaboration.registerType('deck', {
|
||||||
if (OCA.Sharing && OCA.Sharing.ShareSearch) {
|
action: () => {
|
||||||
OCA.Sharing.ShareSearch.addNewResult(FileSharingPicker)
|
const BoardSelector = () => import('./BoardSelector.vue')
|
||||||
}
|
return buildSelector(BoardSelector)
|
||||||
|
},
|
||||||
|
typeString: t('deck', 'Link to a board'),
|
||||||
|
typeIconClass: 'icon-deck',
|
||||||
|
})
|
||||||
|
|
||||||
window.OCP.Collaboration.registerType('deck', {
|
window.OCP.Collaboration.registerType('deck-card', {
|
||||||
action: () => {
|
action: () => {
|
||||||
const BoardSelector = () => import('./BoardSelector.vue')
|
const CardSelector = () => import('./CardSelector.vue')
|
||||||
return buildSelector(BoardSelector)
|
return buildSelector(CardSelector)
|
||||||
},
|
},
|
||||||
typeString: t('deck', 'Link to a board'),
|
typeString: t('deck', 'Link to a card'),
|
||||||
typeIconClass: 'icon-deck',
|
typeIconClass: 'icon-deck',
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
}
|
||||||
window.OCP.Collaboration.registerType('deck-card', {
|
|
||||||
action: () => {
|
|
||||||
const CardSelector = () => import('./CardSelector.vue')
|
|
||||||
return buildSelector(CardSelector)
|
|
||||||
},
|
|
||||||
typeString: t('deck', 'Link to a card'),
|
|
||||||
typeIconClass: 'icon-deck',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { registerWidget, registerCustomPickerElement, NcCustomPickerRenderResult } from '@nextcloud/vue/dist/Functions/registerReference.js'
|
import { registerWidget, registerCustomPickerElement, NcCustomPickerRenderResult } from '@nextcloud/vue'
|
||||||
import { translate, translatePlural } from '@nextcloud/l10n'
|
import { translate, translatePlural } from '@nextcloud/l10n'
|
||||||
|
|
||||||
import './shared-init.js'
|
import './shared-init.js'
|
||||||
|
|||||||
48
src/main.js
48
src/main.js
@@ -2,11 +2,10 @@
|
|||||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
import Vue from 'vue'
|
import { createApp } from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router.js'
|
import router from './router.js'
|
||||||
import store from './store/main.js'
|
import store from './store/main.js'
|
||||||
import { sync } from 'vuex-router-sync'
|
|
||||||
import { translate, translatePlural } from '@nextcloud/l10n'
|
import { translate, translatePlural } from '@nextcloud/l10n'
|
||||||
import { showError } from '@nextcloud/dialogs'
|
import { showError } from '@nextcloud/dialogs'
|
||||||
import { subscribe } from '@nextcloud/event-bus'
|
import { subscribe } from '@nextcloud/event-bus'
|
||||||
@@ -14,27 +13,33 @@ import ClickOutside from 'vue-click-outside'
|
|||||||
import './shared-init.js'
|
import './shared-init.js'
|
||||||
import './models/index.js'
|
import './models/index.js'
|
||||||
import './sessions.js'
|
import './sessions.js'
|
||||||
|
import { initCollections } from './init-collections.js'
|
||||||
|
|
||||||
// the server snap.js conflicts with vertical scrolling so we disable it
|
// the server snap.js conflicts with vertical scrolling so we disable it
|
||||||
document.body.setAttribute('data-snap-ignore', 'true')
|
document.body.setAttribute('data-snap-ignore', 'true')
|
||||||
|
|
||||||
sync(store, router)
|
const app = createApp(App)
|
||||||
|
|
||||||
Vue.prototype.t = translate
|
app.config.globalProperties.t = translate
|
||||||
Vue.prototype.n = translatePlural
|
app.config.globalProperties.n = translatePlural
|
||||||
|
app.config.globalProperties.OC = OC
|
||||||
|
|
||||||
Vue.directive('click-outside', ClickOutside)
|
initCollections({ t, n, OC })
|
||||||
|
|
||||||
Vue.directive('focus', {
|
app.directive('click-outside', ClickOutside)
|
||||||
inserted(el) {
|
|
||||||
|
app.directive('focus', {
|
||||||
|
mounted(el) {
|
||||||
el.focus()
|
el.focus()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
Vue.config.errorHandler = (err, vm, info) => {
|
app.config.errorHandler = (err, vm, info) => {
|
||||||
if (err.response && err.response.data.message) {
|
if (err.response && err.response.data.message) {
|
||||||
const errorMessage = t('deck', 'Something went wrong')
|
const errorMessage = translate('deck', 'Something went wrong')
|
||||||
showError(`${errorMessage}: ${err.response.data.status} ${err.response.data.message}`)
|
showError(
|
||||||
|
`${errorMessage}: ${err.response.data.status} ${err.response.data.message}`,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
console.error(err)
|
console.error(err)
|
||||||
}
|
}
|
||||||
@@ -47,16 +52,14 @@ window.addEventListener('DOMContentLoaded', () => {
|
|||||||
window.OCA.Files = {}
|
window.OCA.Files = {}
|
||||||
}
|
}
|
||||||
// register unused client for the sidebar to have access to its parser methods
|
// register unused client for the sidebar to have access to its parser methods
|
||||||
Object.assign(window.OCA.Files, { App: { fileList: { filesClient: OC.Files.getClient() } } }, window.OCA.Files)
|
Object.assign(
|
||||||
|
window.OCA.Files,
|
||||||
|
{ App: { fileList: { filesClient: OC.Files.getClient() } } },
|
||||||
|
window.OCA.Files,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
/* eslint-disable-next-line no-new */
|
app.mixin({
|
||||||
new Vue({
|
|
||||||
el: '#content',
|
|
||||||
// eslint-disable-next-line vue/match-component-file-name
|
|
||||||
name: 'Deck',
|
|
||||||
router,
|
|
||||||
store,
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
time: Date.now(),
|
time: Date.now(),
|
||||||
@@ -75,7 +78,7 @@ new Vue({
|
|||||||
this.time = Date.now()
|
this.time = Date.now()
|
||||||
}, 1000)
|
}, 1000)
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeUnmount() {
|
||||||
clearInterval(this.interval)
|
clearInterval(this.interval)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -86,9 +89,12 @@ new Vue({
|
|||||||
this.$store.commit('setSearchQuery', '')
|
this.$store.commit('setSearchQuery', '')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
render: h => h(App),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
app.use(router)
|
||||||
|
app.use(store)
|
||||||
|
app.mount('#content')
|
||||||
|
|
||||||
if (!window.OCA.Deck) {
|
if (!window.OCA.Deck) {
|
||||||
window.OCA.Deck = {}
|
window.OCA.Deck = {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ export default {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$set(this.uploadQueue, file.name, { name: file.name, progress: 0 })
|
this.uploadQueue[file.name] = { name: file.name, progress: 0 }
|
||||||
|
|
||||||
const bodyFormData = new FormData()
|
const bodyFormData = new FormData()
|
||||||
bodyFormData.append('cardId', this.cardId)
|
bodyFormData.append('cardId', this.cardId)
|
||||||
bodyFormData.append('type', type)
|
bodyFormData.append('type', type)
|
||||||
@@ -39,7 +40,7 @@ export default {
|
|||||||
onUploadProgress: (e) => {
|
onUploadProgress: (e) => {
|
||||||
const percentCompleted = Math.round((e.loaded * 100) / e.total)
|
const percentCompleted = Math.round((e.loaded * 100) / e.total)
|
||||||
console.debug(percentCompleted)
|
console.debug(percentCompleted)
|
||||||
this.$set(this.uploadQueue[file.name], 'progress', percentCompleted)
|
this.uploadQueue[file.name].progress = percentCompleted
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -50,7 +51,7 @@ export default {
|
|||||||
showError(err.response.data ? err.response.data.message : 'Failed to upload file')
|
showError(err.response.data ? err.response.data.message : 'Failed to upload file')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.$delete(this.uploadQueue, file.name)
|
delete this.uploadQueue[file.name]
|
||||||
})
|
})
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Vue from 'vue'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import Router from 'vue-router'
|
|
||||||
import { generateUrl, getRootUrl } from '@nextcloud/router'
|
import { generateUrl, getRootUrl } from '@nextcloud/router'
|
||||||
import { BOARD_FILTERS } from './store/main.js'
|
import { BOARD_FILTERS } from './store/main.js'
|
||||||
import Boards from './components/boards/Boards.vue'
|
import Boards from './components/boards/Boards.vue'
|
||||||
@@ -14,17 +13,14 @@ import BoardSidebar from './components/board/BoardSidebar.vue'
|
|||||||
import CardSidebar from './components/card/CardSidebar.vue'
|
import CardSidebar from './components/card/CardSidebar.vue'
|
||||||
import Overview from './components/overview/Overview.vue'
|
import Overview from './components/overview/Overview.vue'
|
||||||
|
|
||||||
Vue.use(Router)
|
|
||||||
|
|
||||||
// We apply a dynamic base URL depending on the URL used in the browser
|
// We apply a dynamic base URL depending on the URL used in the browser
|
||||||
const baseUrl = generateUrl('/apps/deck/')
|
const baseUrl = generateUrl('/apps/deck/')
|
||||||
const webRootWithIndexPHP = getRootUrl() + '/index.php'
|
const webRootWithIndexPHP = getRootUrl() + '/index.php'
|
||||||
const doesURLContainIndexPHP = window.location.pathname.startsWith(webRootWithIndexPHP)
|
const doesURLContainIndexPHP = window.location.pathname.startsWith(webRootWithIndexPHP)
|
||||||
const currentBaseUrl = doesURLContainIndexPHP ? baseUrl : baseUrl.replace('/index.php/', '/')
|
const currentBaseUrl = doesURLContainIndexPHP ? baseUrl : baseUrl.replace('/index.php/', '/')
|
||||||
|
|
||||||
const router = new Router({
|
const router = createRouter({
|
||||||
mode: 'history',
|
history: createWebHistory(currentBaseUrl),
|
||||||
base: currentBaseUrl,
|
|
||||||
linkActiveClass: 'active',
|
linkActiveClass: 'active',
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { AttachmentApi } from './../services/AttachmentApi.js'
|
import { AttachmentApi } from './../services/AttachmentApi.js'
|
||||||
import Vue from 'vue'
|
|
||||||
|
|
||||||
const apiClient = new AttachmentApi()
|
const apiClient = new AttachmentApi()
|
||||||
|
|
||||||
@@ -24,20 +23,20 @@ export default {
|
|||||||
mutations: {
|
mutations: {
|
||||||
createAttachment(state, { cardId, attachment }) {
|
createAttachment(state, { cardId, attachment }) {
|
||||||
if (typeof state.attachments[cardId] === 'undefined') {
|
if (typeof state.attachments[cardId] === 'undefined') {
|
||||||
Vue.set(state.attachments, cardId, [attachment])
|
state.attachments[cardId] = [attachment]
|
||||||
} else {
|
} else {
|
||||||
state.attachments[cardId].push(attachment)
|
state.attachments[cardId].push(attachment)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
createAttachments(state, { cardId, attachments }) {
|
createAttachments(state, { cardId, attachments }) {
|
||||||
Vue.set(state.attachments, cardId, attachments)
|
state.attachments[cardId] = attachments
|
||||||
},
|
},
|
||||||
|
|
||||||
updateAttachment(state, { cardId, attachment }) {
|
updateAttachment(state, { cardId, attachment }) {
|
||||||
const existingIndex = state.attachments[attachment.cardId].findIndex(a => a.id === attachment.id && a.type === attachment.type)
|
const existingIndex = state.attachments[attachment.cardId].findIndex(a => a.id === attachment.id && a.type === attachment.type)
|
||||||
if (existingIndex !== -1) {
|
if (existingIndex !== -1) {
|
||||||
Vue.set(state.attachments[cardId], existingIndex, attachment)
|
state.attachments[cardId][existingIndex] = attachment
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
import { CardApi } from './../services/CardApi.js'
|
import { CardApi } from './../services/CardApi.js'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import Vue from 'vue'
|
|
||||||
|
|
||||||
const apiClient = new CardApi()
|
const apiClient = new CardApi()
|
||||||
|
|
||||||
@@ -190,7 +189,7 @@ export default {
|
|||||||
const existingIndex = state.cards.findIndex(_card => _card.id === card.id)
|
const existingIndex = state.cards.findIndex(_card => _card.id === card.id)
|
||||||
if (existingIndex !== -1) {
|
if (existingIndex !== -1) {
|
||||||
const existingCard = state.cards.find(_card => _card.id === card.id)
|
const existingCard = state.cards.find(_card => _card.id === card.id)
|
||||||
Vue.set(state.cards, existingIndex, Object.assign({}, existingCard, card))
|
state.cards[existingIndex] = { ...existingCard, ...card }
|
||||||
} else {
|
} else {
|
||||||
state.cards.push(card)
|
state.cards.push(card)
|
||||||
}
|
}
|
||||||
@@ -204,15 +203,15 @@ export default {
|
|||||||
updateCard(state, card) {
|
updateCard(state, card) {
|
||||||
const existingIndex = state.cards.findIndex(_card => _card.id === card.id)
|
const existingIndex = state.cards.findIndex(_card => _card.id === card.id)
|
||||||
if (existingIndex !== -1) {
|
if (existingIndex !== -1) {
|
||||||
Vue.set(state.cards, existingIndex, Object.assign({}, state.cards[existingIndex], card))
|
state.cards[existingIndex] = { ...state.cards[existingIndex], ...card }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateCardsReorder(state, cards) {
|
updateCardsReorder(state, cards) {
|
||||||
for (const newCard of cards) {
|
for (const newCard of cards) {
|
||||||
const existingIndex = state.cards.findIndex(_card => _card.id === newCard.id)
|
const existingIndex = state.cards.findIndex(_card => _card.id === newCard.id)
|
||||||
if (existingIndex !== -1) {
|
if (existingIndex !== -1) {
|
||||||
Vue.set(state.cards[existingIndex], 'order', newCard.order)
|
state.cards[existingIndex].order = newCard.order
|
||||||
Vue.set(state.cards[existingIndex], 'stackId', newCard.stackId)
|
state.cards[existingIndex].stackId = newCard.stackId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -234,26 +233,26 @@ export default {
|
|||||||
updateCardProperty(state, { card, property }) {
|
updateCardProperty(state, { card, property }) {
|
||||||
const existingIndex = state.cards.findIndex(_card => _card.id === card.id)
|
const existingIndex = state.cards.findIndex(_card => _card.id === card.id)
|
||||||
if (existingIndex !== -1) {
|
if (existingIndex !== -1) {
|
||||||
Vue.set(state.cards[existingIndex], property, card[property])
|
state.cards[existingIndex][property] = card[property]
|
||||||
Vue.set(state.cards[existingIndex], 'lastModified', Date.now() / 1000)
|
state.cards[existingIndex].lastModifiedBy = Date.now() / 1000
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cardSetAttachmentCount(state, { cardId, count }) {
|
cardSetAttachmentCount(state, { cardId, count }) {
|
||||||
const existingIndex = state.cards.findIndex(_card => _card.id === cardId)
|
const existingIndex = state.cards.findIndex(_card => _card.id === cardId)
|
||||||
if (existingIndex !== -1) {
|
if (existingIndex !== -1) {
|
||||||
Vue.set(state.cards[existingIndex], 'attachmentCount', count)
|
state.cards[existingIndex].attachmentCount = count
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cardIncreaseAttachmentCount(state, cardId) {
|
cardIncreaseAttachmentCount(state, cardId) {
|
||||||
const existingIndex = state.cards.findIndex(_card => _card.id === cardId)
|
const existingIndex = state.cards.findIndex(_card => _card.id === cardId)
|
||||||
if (existingIndex !== -1) {
|
if (existingIndex !== -1) {
|
||||||
Vue.set(state.cards[existingIndex], 'attachmentCount', state.cards[existingIndex].attachmentCount + 1)
|
state.cards[existingIndex].attachmentCount = state.cards[existingIndex].attachmentCount + 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cardDecreaseAttachmentCount(state, cardId) {
|
cardDecreaseAttachmentCount(state, cardId) {
|
||||||
const existingIndex = state.cards.findIndex(_card => _card.id === cardId)
|
const existingIndex = state.cards.findIndex(_card => _card.id === cardId)
|
||||||
if (existingIndex !== -1) {
|
if (existingIndex !== -1) {
|
||||||
Vue.set(state.cards[existingIndex], 'attachmentCount', state.cards[existingIndex].attachmentCount - 1)
|
state.cards[existingIndex].attachmentCount = state.cards[existingIndex].attachmentCount - 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
addNewCard(state, card) {
|
addNewCard(state, card) {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { CommentApi } from '../services/CommentApi.js'
|
import { CommentApi } from '../services/CommentApi.js'
|
||||||
import Vue from 'vue'
|
|
||||||
|
|
||||||
const apiClient = new CommentApi()
|
const apiClient = new CommentApi()
|
||||||
|
|
||||||
@@ -34,10 +33,10 @@ export default {
|
|||||||
},
|
},
|
||||||
addComments(state, { comments, cardId }) {
|
addComments(state, { comments, cardId }) {
|
||||||
if (state.comments[cardId] === undefined) {
|
if (state.comments[cardId] === undefined) {
|
||||||
Vue.set(state.comments, cardId, {
|
state.comments[cardId] = {
|
||||||
hasMore: comments.length > 0,
|
hasMore: comments.length >= 0,
|
||||||
comments: [...comments],
|
comments: [...comments],
|
||||||
})
|
}
|
||||||
} else {
|
} else {
|
||||||
const newComments = comments.filter((comment) => {
|
const newComments = comments.filter((comment) => {
|
||||||
return state.comments[cardId].comments.findIndex((item) => item.id === comment.id) === -1
|
return state.comments[cardId].comments.findIndex((item) => item.id === comment.id) === -1
|
||||||
@@ -59,11 +58,11 @@ export default {
|
|||||||
},
|
},
|
||||||
markCommentsAsRead(state, cardId) {
|
markCommentsAsRead(state, cardId) {
|
||||||
state.comments[cardId].comments.forEach(_comment => {
|
state.comments[cardId].comments.forEach(_comment => {
|
||||||
Vue.set(_comment, 'isUnread', false)
|
_comment.isUnread = false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
setReplyTo(state, comment) {
|
setReplyTo(state, comment) {
|
||||||
Vue.set(state, 'replyTo', comment)
|
state.replyTo = comment
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
|||||||
@@ -6,8 +6,7 @@
|
|||||||
import 'url-search-params-polyfill'
|
import 'url-search-params-polyfill'
|
||||||
|
|
||||||
import { loadState } from '@nextcloud/initial-state'
|
import { loadState } from '@nextcloud/initial-state'
|
||||||
import Vue from 'vue'
|
import { createStore } from 'vuex'
|
||||||
import Vuex from 'vuex'
|
|
||||||
import axios from '@nextcloud/axios'
|
import axios from '@nextcloud/axios'
|
||||||
import { generateOcsUrl, generateUrl } from '@nextcloud/router'
|
import { generateOcsUrl, generateUrl } from '@nextcloud/router'
|
||||||
import { BoardApi } from '../services/BoardApi.js'
|
import { BoardApi } from '../services/BoardApi.js'
|
||||||
@@ -18,7 +17,6 @@ import comment from './comment.js'
|
|||||||
import trashbin from './trashbin.js'
|
import trashbin from './trashbin.js'
|
||||||
import attachment from './attachment.js'
|
import attachment from './attachment.js'
|
||||||
import overview from './overview.js'
|
import overview from './overview.js'
|
||||||
Vue.use(Vuex)
|
|
||||||
|
|
||||||
const apiClient = new BoardApi()
|
const apiClient = new BoardApi()
|
||||||
const debug = process.env.NODE_ENV !== 'production'
|
const debug = process.env.NODE_ENV !== 'production'
|
||||||
@@ -29,7 +27,7 @@ export const BOARD_FILTERS = {
|
|||||||
SHARED: 'shared',
|
SHARED: 'shared',
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new Vuex.Store({
|
const store = createStore({
|
||||||
modules: {
|
modules: {
|
||||||
actions,
|
actions,
|
||||||
stack,
|
stack,
|
||||||
@@ -130,10 +128,10 @@ export default new Vuex.Store({
|
|||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
setFullApp(state, isFullApp) {
|
setFullApp(state, isFullApp) {
|
||||||
Vue.set(state, 'isFullApp', isFullApp)
|
state.isFullApp = isFullApp
|
||||||
},
|
},
|
||||||
setHasCardSaveError(state, hasCardSaveError) {
|
setHasCardSaveError(state, hasCardSaveError) {
|
||||||
Vue.set(state, 'hasCardSaveError', hasCardSaveError)
|
state.hasCardSaveError = hasCardSaveError
|
||||||
},
|
},
|
||||||
SET_CONFIG(state, { key, value }) {
|
SET_CONFIG(state, { key, value }) {
|
||||||
const [scope, id, configKey] = key.split(':', 3)
|
const [scope, id, configKey] = key.split(':', 3)
|
||||||
@@ -145,11 +143,11 @@ export default new Vuex.Store({
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (indexExisting > -1) {
|
if (indexExisting > -1) {
|
||||||
Vue.set(state.boards[indexExisting].settings, configKey, value)
|
state.boards[indexExisting].settings[configKey] = value
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
Vue.set(state.config, key, value)
|
state.config[key] = value
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setSearchQuery(state, searchQuery) {
|
setSearchQuery(state, searchQuery) {
|
||||||
@@ -162,7 +160,7 @@ export default new Vuex.Store({
|
|||||||
Object.keys(filter).forEach((key) => {
|
Object.keys(filter).forEach((key) => {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'due':
|
case 'due':
|
||||||
Vue.set(state.filter, key, filter.due)
|
state.filter[key] = filter.due
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
filter[key].forEach((item) => {
|
filter[key].forEach((item) => {
|
||||||
@@ -189,7 +187,7 @@ export default new Vuex.Store({
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (indexExisting > -1) {
|
if (indexExisting > -1) {
|
||||||
Vue.set(state.boards, indexExisting, board)
|
state.boards[indexExisting] = board
|
||||||
} else {
|
} else {
|
||||||
state.boards.push(board)
|
state.boards.push(board)
|
||||||
}
|
}
|
||||||
@@ -201,7 +199,7 @@ export default new Vuex.Store({
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (indexExisting > -1) {
|
if (indexExisting > -1) {
|
||||||
Vue.set(state.boards, indexExisting, board)
|
state.boards[indexExisting] = board
|
||||||
} else {
|
} else {
|
||||||
state.boards.push(board)
|
state.boards.push(board)
|
||||||
}
|
}
|
||||||
@@ -234,7 +232,7 @@ export default new Vuex.Store({
|
|||||||
state.boards = boards
|
state.boards = boards
|
||||||
},
|
},
|
||||||
setSharees(state, shareesUsersAndGroups) {
|
setSharees(state, shareesUsersAndGroups) {
|
||||||
Vue.set(state, 'sharees', shareesUsersAndGroups.exact.users)
|
state.sharees = shareesUsersAndGroups.exact.users
|
||||||
state.sharees.push(...shareesUsersAndGroups.exact.groups)
|
state.sharees.push(...shareesUsersAndGroups.exact.groups)
|
||||||
state.sharees.push(...shareesUsersAndGroups.exact.circles)
|
state.sharees.push(...shareesUsersAndGroups.exact.circles)
|
||||||
|
|
||||||
@@ -284,7 +282,7 @@ export default new Vuex.Store({
|
|||||||
updateAclFromCurrentBoard(state, acl) {
|
updateAclFromCurrentBoard(state, acl) {
|
||||||
for (const acl_ in state.currentBoard.acl) {
|
for (const acl_ in state.currentBoard.acl) {
|
||||||
if (state.currentBoard.acl[acl_].participant.uid === acl.participant.uid) {
|
if (state.currentBoard.acl[acl_].participant.uid === acl.participant.uid) {
|
||||||
Vue.set(state.currentBoard.acl, acl_, acl)
|
state.currentBoard.acl[acl_] = acl
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -300,7 +298,7 @@ export default new Vuex.Store({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (removeIndex > -1) {
|
if (removeIndex > -1) {
|
||||||
Vue.delete(state.currentBoard.acl, removeIndex)
|
state.currentBoard.acl.splice(removeIndex, 1)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
TOGGLE_SHORTCUT_LOCK(state, lock) {
|
TOGGLE_SHORTCUT_LOCK(state, lock) {
|
||||||
@@ -531,3 +529,5 @@ export default new Vuex.Store({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export default store
|
||||||
|
|||||||
@@ -3,10 +3,7 @@
|
|||||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Vue from 'vue'
|
|
||||||
import Vuex from 'vuex'
|
|
||||||
import { OverviewApi } from '../services/OverviewApi.js'
|
import { OverviewApi } from '../services/OverviewApi.js'
|
||||||
Vue.use(Vuex)
|
|
||||||
|
|
||||||
const apiClient = new OverviewApi()
|
const apiClient = new OverviewApi()
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Vue from 'vue'
|
|
||||||
import { StackApi } from './../services/StackApi.js'
|
import { StackApi } from './../services/StackApi.js'
|
||||||
import applyOrderToArray from './../helpers/applyOrderToArray.js'
|
import applyOrderToArray from './../helpers/applyOrderToArray.js'
|
||||||
|
|
||||||
@@ -26,7 +25,7 @@ export default {
|
|||||||
const existingIndex = state.stacks.findIndex(_stack => _stack.id === stack.id)
|
const existingIndex = state.stacks.findIndex(_stack => _stack.id === stack.id)
|
||||||
if (existingIndex !== -1) {
|
if (existingIndex !== -1) {
|
||||||
const existingStack = state.stacks.find(_stack => _stack.id === stack.id)
|
const existingStack = state.stacks.find(_stack => _stack.id === stack.id)
|
||||||
Vue.set(state.stacks, existingIndex, Object.assign({}, existingStack, stack))
|
state.stacks[existingIndex] = { ...existingStack, ...stack }
|
||||||
} else {
|
} else {
|
||||||
state.stacks.push(stack)
|
state.stacks.push(stack)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import DeckIcon from '../components/icons/DeckIcon.vue'
|
|||||||
import { BoardApi } from './../services/BoardApi.js'
|
import { BoardApi } from './../services/BoardApi.js'
|
||||||
import store from './../store/main.js'
|
import store from './../store/main.js'
|
||||||
|
|
||||||
import NcUserBubble from '@nextcloud/vue/dist/Components/NcUserBubble.js'
|
import { NcUserBubble } from '@nextcloud/vue'
|
||||||
|
|
||||||
import moment from '@nextcloud/moment'
|
import moment from '@nextcloud/moment'
|
||||||
import { generateUrl } from '@nextcloud/router'
|
import { generateUrl } from '@nextcloud/router'
|
||||||
|
|||||||
@@ -82,7 +82,7 @@
|
|||||||
{{ t('deck', 'Cancel') }}
|
{{ t('deck', 'Cancel') }}
|
||||||
</NcButton>
|
</NcButton>
|
||||||
<NcButton :disabled="loading || !isBoardAndStackChoosen"
|
<NcButton :disabled="loading || !isBoardAndStackChoosen"
|
||||||
type="primary"
|
variant="primary"
|
||||||
@click="createCard">
|
@click="createCard">
|
||||||
{{ t('deck', 'Create card') }}
|
{{ t('deck', 'Create card') }}
|
||||||
</NcButton>
|
</NcButton>
|
||||||
@@ -212,8 +212,8 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
beforeMount() {
|
beforeMount() {
|
||||||
this.$set(this.card, 'title', this.title)
|
this.card.title = this.title
|
||||||
this.$set(this.card, 'description', this.description)
|
this.card.description = this.description
|
||||||
this.fetchBoards()
|
this.fetchBoards()
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const isDevServer = process.env.WEBPACK_SERVE
|
|||||||
|
|
||||||
webpackConfig.entry = {
|
webpackConfig.entry = {
|
||||||
...webpackConfig.entry,
|
...webpackConfig.entry,
|
||||||
collections: path.join(__dirname, 'src', 'init-collections.js'),
|
// collections: path.join(__dirname, 'src', 'init-collections.js'),
|
||||||
dashboard: path.join(__dirname, 'src', 'init-dashboard.js'),
|
dashboard: path.join(__dirname, 'src', 'init-dashboard.js'),
|
||||||
calendar: path.join(__dirname, 'src', 'init-calendar.js'),
|
calendar: path.join(__dirname, 'src', 'init-calendar.js'),
|
||||||
talk: path.join(__dirname, 'src', 'init-talk.js'),
|
talk: path.join(__dirname, 'src', 'init-talk.js'),
|
||||||
@@ -22,6 +22,9 @@ if (isDevServer) {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
webpackConfig.output.clean = {
|
||||||
|
keep: /\webpack-stats\.json$/,
|
||||||
|
}
|
||||||
webpackConfig.stats = {
|
webpackConfig.stats = {
|
||||||
context: path.resolve(__dirname, 'src'),
|
context: path.resolve(__dirname, 'src'),
|
||||||
assets: true,
|
assets: true,
|
||||||
@@ -30,7 +33,5 @@ if (isDevServer) {
|
|||||||
modules: true,
|
modules: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Workaround for https://github.com/nextcloud/webpack-vue-config/pull/432 causing problems with nextcloud-vue-collections
|
|
||||||
webpackConfig.resolve.alias = {}
|
|
||||||
|
|
||||||
module.exports = webpackConfig
|
module.exports = webpackConfig
|
||||||
|
|||||||
Reference in New Issue
Block a user