feat: Add possibility to clone cards when cloning a board

Signed-off-by: Max Bachhuber <max.bachhuber@bahuma.io>

Adjust BoardServiceTest to new dependencies

Signed-off-by: Max Bachhuber <max.bachhuber@bahuma.io>

Add BoardCloneModal vue component to frontend. Adjust BoardApi and store to support clone options

Signed-off-by: Max Bachhuber <max.bachhuber@bahuma.io>

Add license and credits

Signed-off-by: Max Bachhuber <max.bachhuber@bahuma.io>

Fix PHP code style

Signed-off-by: Max Bachhuber <max.bachhuber@bahuma.io>

Change default clone settings

Signed-off-by: Max Bachhuber <max.bachhuber@bahuma.io>

Add accordion for advanced settings

Signed-off-by: Max Bachhuber <max.bachhuber@bahuma.io>

Fix bug which caused board to be cloned when clicking out of the modal

Signed-off-by: Max Bachhuber <max.bachhuber@bahuma.io>

Change wording of clone options

Signed-off-by: Max Bachhuber <max.bachhuber@bahuma.io>

fix: Rebase failures

Signed-off-by: Julius Härtl <jus@bitgrid.net>

update cloneBoards phpdoc

make error message clear

SPDX Header BoardCloneModal.vue

Signed-off-by: grnd-alt <salimbelakkaf@outlook.de>
This commit is contained in:
Max Bachhuber
2021-11-16 01:35:09 +01:00
committed by Julius Knorr
parent bdaf28eef4
commit f2c30afe8a
12 changed files with 406 additions and 108 deletions

View File

@@ -12,7 +12,10 @@
:force-display-actions="isTouchDevice"
@click="onNavigate"
@undo="unDelete">
<NcAppNavigationIconBullet slot="icon" :color="board.color" />
<template #icon>
<NcAppNavigationIconBullet :color="board.color" />
<BoardCloneModal v-if="cloneModalOpen" :board-title="board.title" @close="onCloseCloneModal" />
</template>
<template #counter>
<AccountIcon v-if="board.acl.length > 0" />
@@ -33,7 +36,7 @@
</NcActionButton>
<NcActionButton v-if="canCreate && !board.archived"
:close-after-click="true"
@click="actionClone">
@click="showCloneModal">
<template #icon>
<CloneIcon :size="20" decorative />
</template>
@@ -157,6 +160,7 @@ import { loadState } from '@nextcloud/initial-state'
import { emit } from '@nextcloud/event-bus'
import isTouchDevice from '../../mixins/isTouchDevice.js'
import BoardCloneModal from './BoardCloneModal.vue'
const canCreateState = loadState('deck', 'canCreate')
@@ -174,6 +178,7 @@ export default {
CloneIcon,
CloseIcon,
CheckIcon,
BoardCloneModal,
},
directives: {
ClickOutside,
@@ -201,6 +206,7 @@ export default {
isDueSubmenuActive: false,
updateDueSetting: null,
canCreate: canCreateState,
cloneModalOpen: false,
}
},
computed: {
@@ -349,6 +355,26 @@ export default {
})
}
},
showCloneModal() {
this.cloneModalOpen = true
},
async onCloseCloneModal(data) {
this.cloneModalOpen = false
if (data) {
this.loading = true
try {
const newBoard = await this.$store.dispatch('cloneBoard', {
boardData: this.board,
settings: data,
})
this.loading = false
this.$router.push({ name: 'board', params: { id: newBoard.id } })
} catch (e) {
OC.Notification.showTemporary(t('deck', 'An error occurred'))
console.error(e)
}
}
},
},
}
</script>

View File

@@ -0,0 +1,124 @@
<!--
- SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
<NcDialog :name="t('deck', 'Clone {boardTitle}', {boardTitle: boardTitle})" :show="true" @close="close(false)">
<div class="modal__content">
<NcCheckboxRadioSwitch :checked.sync="withCards">
{{ t('deck', 'Clone cards') }}
</NcCheckboxRadioSwitch>
<NcCheckboxRadioSwitch v-if="withCards" :checked.sync="withAssignments">
{{ t('deck', 'Clone assignments') }}
</NcCheckboxRadioSwitch>
<NcCheckboxRadioSwitch v-if="withCards" :checked.sync="withLabels">
{{ t('deck', 'Clone labels') }}
</NcCheckboxRadioSwitch>
<NcCheckboxRadioSwitch v-if="withCards" :checked.sync="withDueDate">
{{ t('deck', 'Clone due dates') }}
</NcCheckboxRadioSwitch>
<div v-if="withCards" class="accordion" :class="{ 'is-open': accordionOpen }">
<div class="accordion__toggle" @click="accordionOpen = !accordionOpen">
<span class="accordion__toggle__icon">
</span>
{{ t('deck', 'Advanced options') }}
</div>
<div v-if="accordionOpen" class="accordion__content">
<NcCheckboxRadioSwitch v-if="withCards" :checked.sync="moveCardsToLeftStack">
{{ t('deck', 'Move all cards to the first list') }}
</NcCheckboxRadioSwitch>
<NcCheckboxRadioSwitch v-if="withCards" :checked.sync="restoreArchivedCards">
{{ t('deck', 'Restore archived cards') }}
</NcCheckboxRadioSwitch>
</div>
</div>
</div>
<template #actions>
<NcButton @click="cancel">
{{ t('deck', 'Cancel') }}
</NcButton>
<NcButton type="primary" @click="save">
{{ t('deck', 'Clone') }}
</NcButton>
</template>
</NcDialog>
</template>
<script>
import { NcButton, NcCheckboxRadioSwitch, NcDialog } from '@nextcloud/vue'
export default {
name: 'BoardCloneModal',
components: {
NcDialog,
NcCheckboxRadioSwitch,
NcButton,
},
props: {
boardTitle: {
type: String,
default: 'Board',
},
},
data() {
return {
withCards: false,
withAssignments: true,
withLabels: true,
withDueDate: true,
moveCardsToLeftStack: false,
restoreArchivedCards: false,
accordionOpen: false,
}
},
methods: {
close(data) {
this.$emit('close', data)
},
save() {
const data = {
withCards: this.withCards,
withAssignments: this.withAssignments,
withLabels: this.withLabels,
withDueDate: this.withDueDate,
moveCardsToLeftStack: this.moveCardsToLeftStack,
restoreArchivedCards: this.restoreArchivedCards,
}
this.close(data)
},
cancel() {
this.close(false)
},
},
}
</script>
<style scoped>
.modal__content {
margin: 20px;
}
.modal__title {
text-align: center;
}
.modal__buttons {
text-align: end;
margin-top: .5em;
}
.accordion__toggle {
margin: .5em 0;
cursor: pointer;
}
.accordion__toggle__icon {
display: inline-block;
}
.accordion.is-open .accordion__toggle__icon {
transform: rotate(90deg);
}
</style>

View File

@@ -123,9 +123,16 @@ export class BoardApi {
})
}
async cloneBoard(board) {
async cloneBoard(board, withCards = false, withAssignments = false, withLabels = false, withDueDate = false, moveCardsToLeftStack = false, restoreArchivedCards = false) {
try {
const response = await axios.post(this.url(`/boards/${board.id}/clone`))
const response = await axios.post(this.url(`/boards/${board.id}/clone`), {
withCards,
withAssignments,
withLabels,
withDueDate,
moveCardsToLeftStack,
restoreArchivedCards,
})
return response.data
} catch (err) {
return err

View File

@@ -398,9 +398,11 @@ export default new Vuex.Store({
return err
}
},
async cloneBoard({ commit }, boardData) {
async cloneBoard({ commit }, { boardData, settings }) {
const { withCards, withAssignments, withLabels, withDueDate, moveCardsToLeftStack, restoreArchivedCards } = settings
try {
const newBoard = await apiClient.cloneBoard(boardData)
const newBoard = await apiClient.cloneBoard(boardData, withCards, withAssignments, withLabels, withDueDate, moveCardsToLeftStack, restoreArchivedCards)
commit('cloneBoard', newBoard)
return newBoard
} catch (err) {