1
.gitignore
vendored
1
.gitignore
vendored
@@ -13,6 +13,5 @@ tests/integration/vendor/
|
||||
tests/integration/composer.lock
|
||||
vendor/
|
||||
*.lock
|
||||
package-lock.json
|
||||
|
||||
\.idea/
|
||||
|
||||
@@ -72,10 +72,6 @@ input.input-inline {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#app img {
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
#searchbox {
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ class CardController extends Controller {
|
||||
* @param int $order
|
||||
* @return \OCP\AppFramework\Db\Entity
|
||||
*/
|
||||
public function create($title, $stackId, $type, $order = 999) {
|
||||
public function create($title, $stackId, $type = 'plain', $order = 999) {
|
||||
return $this->cardService->create($title, $stackId, $type, $order, $this->userId);
|
||||
}
|
||||
|
||||
|
||||
17991
package-lock.json
generated
Normal file
17991
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
53
package.json
53
package.json
@@ -26,18 +26,19 @@
|
||||
"test:coverage": "jest --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.0.0",
|
||||
"@babel/polyfill": "^7.4.4",
|
||||
"nextcloud-axios": "^0.2.0",
|
||||
"nextcloud-server": "^0.15.9",
|
||||
"nextcloud-server": "^0.15.10",
|
||||
"nextcloud-vue": "^0.12.1",
|
||||
"nextcloud-vue-collections": "^0.5.2",
|
||||
"vue": "^2.5.16",
|
||||
"vue": "^2.6.7",
|
||||
"vue-click-outside": "^1.0.7",
|
||||
"vue-color": "^2.7.0",
|
||||
"vue-infinite-loading": "^2.4.1",
|
||||
"vue-router": "^3.0.1",
|
||||
"vue-easymde": "^0.1.6",
|
||||
"vue-infinite-loading": "^2.4.4",
|
||||
"vue-router": "^3.1.2",
|
||||
"vue-smooth-dnd": "^0.8.0",
|
||||
"vuex": "^3.0.1",
|
||||
"vuex": "^3.1.1",
|
||||
"vuex-router-sync": "^5.0.0"
|
||||
},
|
||||
"browserslist": [
|
||||
@@ -48,42 +49,42 @@
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.1.2",
|
||||
"@babel/core": "^7.5.5",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
|
||||
"@babel/preset-env": "^7.1.0",
|
||||
"@vue/test-utils": "^1.0.0-beta.25",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"babel-jest": "^24.0.0",
|
||||
"babel-loader": "^8.0.4",
|
||||
"@babel/preset-env": "^7.5.5",
|
||||
"@vue/test-utils": "^1.0.0-beta.29",
|
||||
"babel-eslint": "^10.0.2",
|
||||
"babel-jest": "^24.8.0",
|
||||
"babel-loader": "^8.0.6",
|
||||
"css-loader": "^3.0.0",
|
||||
"eslint": "^4.19.1",
|
||||
"eslint-config-standard": "^11.0.0",
|
||||
"eslint-friendly-formatter": "^4.0.1",
|
||||
"eslint-loader": "^2.1.1",
|
||||
"eslint-plugin-import": "^2.13.0",
|
||||
"eslint-plugin-node": "^8.0.0",
|
||||
"eslint-plugin-promise": "^4.0.1",
|
||||
"eslint-loader": "^2.2.1",
|
||||
"eslint-plugin-import": "^2.18.2",
|
||||
"eslint-plugin-node": "^8.0.1",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-standard": "^3.1.0",
|
||||
"eslint-plugin-vue": "^4.5.0",
|
||||
"extract-text-webpack-plugin": "^3.0.2",
|
||||
"file-loader": "^4.0.0",
|
||||
"jest": "^24.0.0",
|
||||
"jest": "^24.8.0",
|
||||
"jest-serializer-vue": "^2.0.2",
|
||||
"mini-css-extract-plugin": "^0.8.0",
|
||||
"node-sass": "^4.10.0",
|
||||
"node-sass": "^4.12.0",
|
||||
"prettier-eslint": "^9.0.0",
|
||||
"raw-loader": "^3.0.0",
|
||||
"sass-loader": "^7.1.0",
|
||||
"sass-loader": "^7.2.0",
|
||||
"stylelint": "^8.4.0",
|
||||
"stylelint-config-recommended-scss": "^3.2.0",
|
||||
"stylelint-config-recommended-scss": "^3.3.0",
|
||||
"stylelint-webpack-plugin": "^0.10.5",
|
||||
"vue-jest": "^3.0.0",
|
||||
"vue-loader": "^15.4.2",
|
||||
"vue-jest": "^3.0.4",
|
||||
"vue-loader": "^15.7.1",
|
||||
"vue-style-loader": "^4.1.1",
|
||||
"vue-template-compiler": "^2.5.16",
|
||||
"webpack": "^4.23.1",
|
||||
"webpack-cli": "^3.1.2",
|
||||
"webpack-merge": "^4.1.2"
|
||||
"vue-template-compiler": "^2.6.10",
|
||||
"webpack": "^4.39.1",
|
||||
"webpack-cli": "^3.3.6",
|
||||
"webpack-merge": "^4.2.1"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
|
||||
16
src/App.vue
16
src/App.vue
@@ -74,7 +74,7 @@ export default {
|
||||
}),
|
||||
// TODO: properly handle sidebar showing for route subview and board sidebar
|
||||
sidebarRouterView() {
|
||||
console.log(this.$route)
|
||||
// console.log(this.$route)
|
||||
return this.$route.name === 'card' || this.$route.name === 'board.details'
|
||||
},
|
||||
sidebarShown() {
|
||||
@@ -119,12 +119,10 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.deck-main {
|
||||
bottom: 0;
|
||||
overflow: auto;
|
||||
position: absolute;
|
||||
top: 44px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<style>
|
||||
#content * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -60,7 +60,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
updateColor() {
|
||||
this.$emit('input', this.color)
|
||||
this.$emit('input', this.color.hex)
|
||||
},
|
||||
hidePicker() {
|
||||
this.showFullPicker = false
|
||||
|
||||
@@ -32,21 +32,24 @@
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="board" class="crumb svg">
|
||||
<div class="board-bullet" />
|
||||
<a href="#todo">{{ board.title }}</a>
|
||||
<span style="display: inline;" class="icon-shared" />
|
||||
<div :style="{backgroundColor: '#' + board.color}" class="board-bullet" />
|
||||
<a href="#">{{ board.title }}</a>
|
||||
<router-link :to="{name: 'board.details'}" class="icon-shared" />
|
||||
</div>
|
||||
<div v-if="board" class="board-actions">
|
||||
<div id="stack-add">
|
||||
<form>
|
||||
<label for="new-stack-input-main" class="hidden-visually">Add a new stack</label>
|
||||
<input id="new-stack-input-main" type="text" class="no-close"
|
||||
placeholder="Add a new stack">
|
||||
<input class="icon-confirm" type="button" title="Submit">
|
||||
<input id="new-stack-input-main" v-model="newStackTitle" type="text"
|
||||
class="no-close"
|
||||
placeholder="Add a new stack" @keyup.enter="clickAddNewStack()">
|
||||
<input class="icon-confirm" type="button" title="Submit"
|
||||
@click="clickAddNewStack()">
|
||||
</form>
|
||||
</div>
|
||||
<div class="board-action-buttons">
|
||||
<button title="Show archived cards" class="icon icon-archive" />
|
||||
<button :style="archivStyle" title="Show archived cards" class="icon icon-archive"
|
||||
@click="toggleShowArchived" />
|
||||
<button :class="[(compactMode ? 'icon-toggle-compact-collapsed' : 'icon-toggle-compact-expanded')]" title="Toggle compact mode" class="icon"
|
||||
@click="toggleCompactMode" />
|
||||
<router-link v-tooltip="t('deck', 'Board settings')" :to="{name: 'board.details'}" class="icon-settings-dark"
|
||||
@@ -68,10 +71,24 @@ export default {
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
newStackTitle: '',
|
||||
stack: '',
|
||||
showArchived: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
compactMode: state => state.compactMode
|
||||
})
|
||||
}),
|
||||
archivStyle() {
|
||||
|
||||
if (this.showArchived === true) {
|
||||
return 'opacity: 1.0'
|
||||
}
|
||||
return 'opacity: 0.3'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleNav() {
|
||||
@@ -82,6 +99,16 @@ export default {
|
||||
},
|
||||
toggleCompactMode() {
|
||||
this.$store.dispatch('toggleCompactMode')
|
||||
},
|
||||
toggleShowArchived() {
|
||||
this.$store.dispatch('toggleShowArchived')
|
||||
this.showArchived = !this.showArchived
|
||||
},
|
||||
clickAddNewStack() {
|
||||
this.stack = { title: this.newStackTitle }
|
||||
this.$store.dispatch('createStack', this.stack)
|
||||
this.newStackTitle = ''
|
||||
this.stack = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,19 +26,7 @@
|
||||
<div v-if="board" class="board">
|
||||
<container lock-axix="y" orientation="horizontal" @drop="onDropStack">
|
||||
<draggable v-for="stack in stacksByBoard" :key="stack.id" class="stack">
|
||||
<h3>{{ stack.title }}</h3>
|
||||
<container :get-child-payload="payloadForCard(stack.id)" group-name="stack" @drop="($event) => onDropCard(stack.id, $event)">
|
||||
<draggable v-for="card in cardsByStack(stack.id)" :key="card.id">
|
||||
<card-item v-if="card" :id="card.id" />
|
||||
</draggable>
|
||||
</container>
|
||||
<div class="card create">
|
||||
<div title="Add card">
|
||||
<i class="icon icon-add" />
|
||||
<span class="hidden-visually">Add card</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<stack :stack="stack" />
|
||||
</draggable>
|
||||
</container>
|
||||
</div>
|
||||
@@ -55,15 +43,15 @@
|
||||
import { Container, Draggable } from 'vue-smooth-dnd'
|
||||
import { mapState } from 'vuex'
|
||||
import Controls from '../Controls'
|
||||
import CardItem from '../cards/CardItem'
|
||||
import Stack from './Stack'
|
||||
|
||||
export default {
|
||||
name: 'Board',
|
||||
components: {
|
||||
CardItem,
|
||||
Controls,
|
||||
Container,
|
||||
Draggable
|
||||
Draggable,
|
||||
Stack
|
||||
},
|
||||
inject: [
|
||||
'boardApi'
|
||||
@@ -81,17 +69,21 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
board: state => state.currentBoard
|
||||
board: state => state.currentBoard,
|
||||
showArchived: state => state.showArchived
|
||||
}),
|
||||
stacksByBoard() {
|
||||
return this.$store.getters.stacksByBoard(this.board.id)
|
||||
},
|
||||
cardsByStack() {
|
||||
return (id) => this.$store.getters.cardsByStack(id)
|
||||
}
|
||||
/* cardsByStack() {
|
||||
return (id) => this.$store.getters.cardsByStack(id)
|
||||
} */
|
||||
},
|
||||
watch: {
|
||||
'$route': 'fetchData'
|
||||
id: 'fetchData',
|
||||
showArchived() {
|
||||
this.fetchData()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData()
|
||||
@@ -102,22 +94,22 @@ export default {
|
||||
.then((board) => {
|
||||
this.$store.dispatch('setCurrentBoard', board)
|
||||
this.$store.dispatch('loadStacks', board)
|
||||
this.$store.dispatch('setAssignableUsers', board.users)
|
||||
this.loading = false
|
||||
console.log(board)
|
||||
this.$store.state.labels = board.labels
|
||||
})
|
||||
},
|
||||
onDropStack({ removedIndex, addedIndex }) {
|
||||
this.$store.dispatch('orderStack', { stack: this.stacksByBoard[removedIndex], removedIndex, addedIndex })
|
||||
},
|
||||
onDropCard({ removedIndex, addedIndex }) {
|
||||
/* onDropCard({ removedIndex, addedIndex }) {
|
||||
|
||||
},
|
||||
payloadForCard(stackId) {
|
||||
}, */
|
||||
/* payloadForCard(stackId) {
|
||||
return index => {
|
||||
return this.cardsByStack(stackId)[index]
|
||||
}
|
||||
},
|
||||
}, */
|
||||
createStack() {
|
||||
let newStack = {
|
||||
title: 'FooBar',
|
||||
@@ -126,6 +118,9 @@ export default {
|
||||
}
|
||||
this.$store.dispatch('createStack', newStack)
|
||||
}
|
||||
/* deleteStack(stack) {
|
||||
this.$store.dispatch('deleteStack', stack)
|
||||
} */
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -146,6 +141,7 @@ export default {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
.smooth-dnd-container.vertical {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -157,6 +153,6 @@ export default {
|
||||
|
||||
.smooth-dnd-container.vertical .smooth-dnd-draggable-wrapper {
|
||||
height: auto;
|
||||
}
|
||||
} */
|
||||
|
||||
</style>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<app-sidebar
|
||||
<app-sidebar v-if="board != null"
|
||||
:actions="[]"
|
||||
:title="board.title"
|
||||
@close="closeSidebar">
|
||||
@@ -35,7 +35,7 @@
|
||||
</AppSidebarTab>
|
||||
|
||||
<AppSidebarTab name="Deleted items" icon="icon-delete">
|
||||
<DeletedTabSidebard :board="board" />
|
||||
<DeletedTabSidebar :board="board" />
|
||||
</AppSidebarTab>
|
||||
|
||||
<AppSidebarTab name="Timeline" icon="icon-activity">
|
||||
@@ -49,7 +49,7 @@
|
||||
import { mapState } from 'vuex'
|
||||
import SharingTabSidebard from './SharingTabSidebard'
|
||||
import TagsTabSidebard from './TagsTabSidebard'
|
||||
import DeletedTabSidebard from './DeletedTabSidebard'
|
||||
import DeletedTabSidebar from './DeletedTabSidebar'
|
||||
import TimelineTabSidebard from './TimelineTabSidebard'
|
||||
import { AppSidebar, AppSidebarTab } from 'nextcloud-vue'
|
||||
|
||||
@@ -60,9 +60,15 @@ export default {
|
||||
AppSidebarTab,
|
||||
SharingTabSidebard,
|
||||
TagsTabSidebard,
|
||||
DeletedTabSidebard,
|
||||
DeletedTabSidebar,
|
||||
TimelineTabSidebard
|
||||
},
|
||||
props: {
|
||||
id: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
board: state => state.currentBoard,
|
||||
|
||||
128
src/components/board/DeletedTabSidebar.vue
Normal file
128
src/components/board/DeletedTabSidebar.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
<h3>{{ t('deck', 'Deleted stacks') }}</h3>
|
||||
<ul>
|
||||
<li v-for="deletedStack in deletedStacks" :key="deletedStack.id">
|
||||
|
||||
<span class="icon icon-deck" />
|
||||
<span class="title">{{ deletedStack.title }}</span>
|
||||
<button
|
||||
:title="t('settings', 'Undo')"
|
||||
class="app-navigation-entry-deleted-button icon-history"
|
||||
@click="stackUndoDelete(deletedStack)" />
|
||||
|
||||
<!-- <span class="live-relative-timestamp" data-timestamp="{{ deletedStack.deletedAt*1000 }}">{{deletedStack.deletedAt | relativeDateFilter }}</span>
|
||||
<a @click="stackUndoDelete(deletedStack)"><span class="icon icon-history"></span></a> -->
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>{{ t('deck', 'Deleted cards') }}</h3>
|
||||
<ul>
|
||||
<li v-for="deletedCard in deletedCards" :key="deletedCard.id">
|
||||
<div class="icon icon-deck" />
|
||||
<span class="title">{{ deletedCard.title }}</span>
|
||||
<button
|
||||
:title="t('settings', 'Undo')"
|
||||
class="app-navigation-entry-deleted-button icon-history"
|
||||
@click="cardUndoDelete(deletedCard)" />
|
||||
</li>
|
||||
|
||||
<!-- <li ng-repeat="deletedCard in cardservice.deleted">
|
||||
<span class="icon icon-deck"></span>
|
||||
<span class="title">{{deletedCard.title}} ({{stackservice.tryAllThenDeleted(deletedCard.stackId).title}})</span>
|
||||
<span class="live-relative-timestamp" data-timestamp="{{ deletedCard.deletedAt*1000 }}">{{deletedCard.deletedAt | relativeDateFilter }}</span>
|
||||
<a ng-click="cardOrCardAndStackUndoDelete(deletedCard)">
|
||||
<span class="icon icon-history"></span>
|
||||
</a>
|
||||
</li> -->
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
export default {
|
||||
name: 'DeletedTabSidebar',
|
||||
components: {
|
||||
|
||||
},
|
||||
props: {
|
||||
board: {
|
||||
type: Object,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isLoading: false,
|
||||
copiedDeletedStack: null,
|
||||
copiedDeletedCard: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
deletedStacks: state => state.stack.deletedStacks,
|
||||
deletedCards: state => state.stack.deletedCards
|
||||
})
|
||||
|
||||
},
|
||||
created() {
|
||||
this.getData()
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
this.isLoading = true
|
||||
this.$store.dispatch('deletedItems', this.board.id).then(response => {
|
||||
this.isLoading = false
|
||||
})
|
||||
},
|
||||
stackUndoDelete(deletedStack) {
|
||||
this.copiedDeletedStack = Object.assign({}, deletedStack)
|
||||
this.copiedDeletedStack.deletedAt = 0
|
||||
this.$store.dispatch('stackUndoDelete', this.copiedDeletedStack)
|
||||
this.getData()
|
||||
},
|
||||
cardUndoDelete(deletedCard) {
|
||||
this.copiedDeletedCard = Object.assign({}, deletedCard)
|
||||
this.copiedDeletedCard.deletedAt = 0
|
||||
this.$store.dispatch('cardUndoDelete', this.copiedDeletedCard)
|
||||
this.getData()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
* {
|
||||
flex-basis: 44px;
|
||||
}
|
||||
|
||||
.title {
|
||||
flex-grow: 2;
|
||||
}
|
||||
.live-relative-timestamp {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
* {
|
||||
flex-basis: 44px;
|
||||
}
|
||||
|
||||
.title {
|
||||
flex-grow: 2;
|
||||
}
|
||||
.live-relative-timestamp {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,26 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
deleted
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'DeletedTabSidebard',
|
||||
components: {
|
||||
|
||||
},
|
||||
props: {
|
||||
board: {
|
||||
type: Object,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<multiselect v-model="addAcl" :options="sharees" label="label"
|
||||
<multiselect v-model="addAcl" :options="unallocatedSharees" label="label"
|
||||
@input="clickAddAcl" @search-change="asyncFind">
|
||||
<template #option="scope">
|
||||
{{ scope.option.label }}
|
||||
@@ -23,34 +23,36 @@
|
||||
{{ acl.participant.displayname }}
|
||||
</span>
|
||||
|
||||
<input :checked="acl.permissionEdit" type="checkbox" @click="clickEditAcl(acl)">
|
||||
<label for="checkbox">{{ t('deck', 'Edit') }}</label>
|
||||
|
||||
<input :checked="acl.permissionShare" type="checkbox" @click="clickShareAcl(acl)">
|
||||
<label for="checkbox">{{ t('deck', 'Share') }}</label>
|
||||
|
||||
<input :checked="acl.permissionManage" type="checkbox" @click="clickManageAcl(acl)">
|
||||
<label for="checkbox">{{ t('deck', 'Manage') }}</label>
|
||||
|
||||
<button v-tooltip="t('deck', 'Delete')" class="icon-delete" @click="clickDeleteAcl(acl)" />
|
||||
|
||||
<Actions>
|
||||
<ActionCheckbox :checked="acl.permissionEdit" @change="clickEditAcl(acl)">{{ t('deck', 'Can edit') }}</ActionCheckbox>
|
||||
</Actions>
|
||||
<Actions>
|
||||
<ActionCheckbox :checked="acl.permissionShare" @change="clickShareAcl(acl)">{{ t('deck', 'Can share') }}</ActionCheckbox>
|
||||
<ActionCheckbox :checked="acl.permissionManage" @change="clickManageAcl(acl)">{{ t('deck', 'Can manage') }}</ActionCheckbox>
|
||||
<ActionButton icon="icon-delete" @click="clickDeleteAcl(acl)">{{ t('deck', 'Delete') }}</ActionButton>
|
||||
</Actions>
|
||||
</li>
|
||||
</ul>
|
||||
<CollaborationView />
|
||||
|
||||
<collection-list v-if="board.id" :id="`${board.id}`" :name="board.title"
|
||||
type="deck" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Avatar, Multiselect } from 'nextcloud-vue'
|
||||
import CollaborationView from '../CollaborationView'
|
||||
import { Avatar, Multiselect, Actions, ActionButton, ActionCheckbox } from 'nextcloud-vue'
|
||||
import { CollectionList } from 'nextcloud-vue-collections'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'SharingTabSidebard',
|
||||
components: {
|
||||
Avatar,
|
||||
Actions,
|
||||
ActionButton,
|
||||
ActionCheckbox,
|
||||
Multiselect,
|
||||
CollaborationView
|
||||
CollectionList
|
||||
},
|
||||
props: {
|
||||
board: {
|
||||
@@ -68,7 +70,14 @@ export default {
|
||||
computed: {
|
||||
...mapGetters({
|
||||
sharees: 'sharees'
|
||||
})
|
||||
}),
|
||||
unallocatedSharees() {
|
||||
return this.sharees.filter((sharee) => {
|
||||
return Object.values(this.board.acl).findIndex((acl) => {
|
||||
return acl.participant.uid === sharee.value.shareWith
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
asyncFind(query) {
|
||||
@@ -108,3 +117,16 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
#shareWithList {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
#shareWithList li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.username {
|
||||
padding: 12px 9px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
172
src/components/board/Stack.vue
Normal file
172
src/components/board/Stack.vue
Normal file
@@ -0,0 +1,172 @@
|
||||
<!--
|
||||
- @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
|
||||
-
|
||||
- @author Julius Härtl <jus@bitgrid.net>
|
||||
- @author Jakob Röhrl <jakob.roehrl@web.de>
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- GNU Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="stack">
|
||||
<div class="stack--header">
|
||||
<transition name="fade" mode="out-in">
|
||||
<h3 v-if="!editing" @click="startEditing(stack)">{{ stack.title }}</h3>
|
||||
<form v-else>
|
||||
<input v-model="copiedStack.title" type="text" autofocus>
|
||||
<input type="button" class="icon-confirm" @click="finishedEdit(stack)">
|
||||
</form>
|
||||
</transition>
|
||||
<Actions>
|
||||
<ActionButton icon="icon-delete" @click="deleteStack(stack)">{{ t('deck', 'Delete stack') }}</ActionButton>
|
||||
</Actions>
|
||||
</div>
|
||||
|
||||
<!-- <container :get-child-payload="payloadForCard(stack.id)" group-name="stack" @drop="($event) => onDropCard(stack.id, $event)"> -->
|
||||
<container :get-child-payload="payloadForCard(stack.id)" group-name="stack" @drop="onDropCard">
|
||||
<draggable v-for="card in cardsByStack(stack.id)" :key="card.id">
|
||||
<card-item v-if="card" :id="card.id" />
|
||||
</draggable>
|
||||
</container>
|
||||
|
||||
<form class="stack--card-add" @submit.prevent="clickAddCard()">
|
||||
<label for="new-stack-input-main" class="hidden-visually">Add a new card</label>
|
||||
<input id="new-stack-input-main" v-model="newCardTitle" type="text"
|
||||
class="no-close"
|
||||
placeholder="Add a new card">
|
||||
<input class="icon-confirm" type="button" title="Submit">
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { Container, Draggable } from 'vue-smooth-dnd'
|
||||
import { Actions } from 'nextcloud-vue/dist/Components/Actions'
|
||||
import { ActionButton } from 'nextcloud-vue/dist/Components/ActionButton'
|
||||
import CardItem from '../cards/CardItem'
|
||||
|
||||
export default {
|
||||
name: 'Stack',
|
||||
components: {
|
||||
Actions,
|
||||
ActionButton,
|
||||
CardItem,
|
||||
Container,
|
||||
Draggable
|
||||
},
|
||||
|
||||
props: {
|
||||
stack: {
|
||||
type: Object,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editing: false,
|
||||
copiedStack: '',
|
||||
newCardTitle: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cardsByStack() {
|
||||
return (id) => this.$store.getters.cardsByStack(id)
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
onDropCard({ removedIndex, addedIndex }) {
|
||||
},
|
||||
payloadForCard(stackId) {
|
||||
return index => {
|
||||
return this.cardsByStack(stackId)[index]
|
||||
}
|
||||
},
|
||||
deleteStack(stack) {
|
||||
this.$store.dispatch('deleteStack', stack)
|
||||
},
|
||||
startEditing(stack) {
|
||||
this.copiedStack = Object.assign({}, stack)
|
||||
this.editing = true
|
||||
},
|
||||
finishedEdit(stack) {
|
||||
if (this.copiedStack.title !== stack.title) {
|
||||
this.$store.dispatch('updateStack', this.copiedStack)
|
||||
}
|
||||
this.editing = false
|
||||
},
|
||||
clickAddCard() {
|
||||
let newCard = {
|
||||
title: this.newCardTitle,
|
||||
stackId: this.stack.id,
|
||||
boardId: this.stack.boardId
|
||||
}
|
||||
this.$store.dispatch('addCard', newCard)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
$stack-spacing: 10px;
|
||||
$stack-width: 300px;
|
||||
|
||||
.stack {
|
||||
width: $stack-width;
|
||||
padding: $stack-spacing;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.smooth-dnd-container.vertical {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.smooth-dnd-container.vertical > .smooth-dnd-draggable-wrapper {
|
||||
overflow: initial;
|
||||
}
|
||||
|
||||
.smooth-dnd-container.vertical .smooth-dnd-draggable-wrapper {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.stack--header {
|
||||
display: flex;
|
||||
|
||||
h3, form {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
|
||||
input[type=text] {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.stack--card-add {
|
||||
display: flex;
|
||||
|
||||
input[type=text] {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -21,45 +21,194 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<app-sidebar
|
||||
:actions="[]"
|
||||
title="andy-yeo-1387151-unsplash.jpg"
|
||||
subtitle="4,3 MB, last edited 19 days ago"
|
||||
<app-sidebar v-if="currentCard !== null && copiedCard !== null"
|
||||
:actions="toolbarActions"
|
||||
:title="currentCard.title"
|
||||
:subtitle="subtitle"
|
||||
@close="closeSidebar">
|
||||
<template #action />
|
||||
<AppSidebarTab name="Description" icon="icon-description">
|
||||
this is the description tab
|
||||
<AppSidebarTab :order="0" name="Details" icon="icon-home">
|
||||
|
||||
<p>Tags</p>
|
||||
<multiselect v-model="allLabels" :multiple="true" :options="currentBoard.labels"
|
||||
:taggable="true" label="title"
|
||||
track-by="id" @select="addLabelToCard" @remove="removeLabelFromCard">
|
||||
<template #option="scope">
|
||||
<span>{{ scope.option.title }}</span>
|
||||
</template>
|
||||
</multiselect>
|
||||
|
||||
<p>Assign to user</p>
|
||||
<multiselect v-model="assignedUsers" :multiple="true" :options="assignableUsers"
|
||||
label="displayname"
|
||||
track-by="primaryKey"
|
||||
@select="assignUserToCard" @remove="removeUserFromCard">
|
||||
<template #option="scope">
|
||||
{{ scope.option.displayname }}
|
||||
</template>
|
||||
</multiselect>
|
||||
|
||||
<p>Due to</p>
|
||||
|
||||
<DatetimePicker v-model="copiedCard.duedate" type="datetime" lang="en"
|
||||
format="YYYY-MM-DD HH:mm" confirm @change="setDue()" />
|
||||
<button v-tooltip="t('deck', 'Delete')" v-if="copiedCard.duedate" class="icon-delete"
|
||||
@click="removeDue()" />
|
||||
|
||||
<markdown-editor ref="markdownEditor" v-model="desc" :configs="{autofocus: true, autosave: {enabled: true, uniqueId: 'unique'}, toolbar: false}" />
|
||||
</AppSidebarTab>
|
||||
<AppSidebarTab name="Timeline" icon="icon-activity">
|
||||
<AppSidebarTab :order="1" name="Attachments" icon="icon-files-dark">
|
||||
{{ currentCard.attachments }}
|
||||
<button class="icon-upload" @click="clickAddNewAttachmment()">
|
||||
{{ t('deck', 'Upload attachment') }}
|
||||
</button>
|
||||
</AppSidebarTab>
|
||||
<AppSidebarTab :order="2" name="Timeline" icon="icon-activity">
|
||||
this is the activity tab
|
||||
</AppSidebarTab>
|
||||
<AppSidebarTab name="Attachments" icon="icon-files-dark">
|
||||
this is the files tab
|
||||
</AppSidebarTab>
|
||||
</app-sidebar>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { AppSidebar, AppSidebarTab } from 'nextcloud-vue'
|
||||
import { AppSidebar, AppSidebarTab, Multiselect, DatetimePicker } from 'nextcloud-vue'
|
||||
import { mapState } from 'vuex'
|
||||
import markdownEditor from 'vue-easymde/src/markdown-editor'
|
||||
|
||||
export default {
|
||||
name: 'CardSidebar',
|
||||
components: {
|
||||
AppSidebar,
|
||||
AppSidebarTab
|
||||
AppSidebarTab,
|
||||
Multiselect,
|
||||
DatetimePicker,
|
||||
markdownEditor
|
||||
},
|
||||
props: {
|
||||
id: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
assignedUsers: null,
|
||||
addedLabelToCard: null,
|
||||
isLoading: false,
|
||||
copiedCard: null,
|
||||
allLabels: null,
|
||||
desc: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
currentBoard: state => state.currentBoard,
|
||||
assignableUsers: state => state.assignableUsers
|
||||
}),
|
||||
currentCard() {
|
||||
return this.$store.getters.cardById(this.id)
|
||||
},
|
||||
subtitle() {
|
||||
let lastModified = this.currentCard.lastModified
|
||||
let createdAt = this.currentCard.createdAt
|
||||
|
||||
return t('deck', 'Modified') + ': ' + lastModified + ' ' + t('deck', 'Created') + ': ' + createdAt
|
||||
},
|
||||
toolbarActions() {
|
||||
return [
|
||||
{
|
||||
action: () => {
|
||||
|
||||
},
|
||||
icon: 'icon-archive-dark',
|
||||
text: t('deck', 'Assign to me')
|
||||
},
|
||||
{
|
||||
action: () => {
|
||||
|
||||
},
|
||||
icon: 'icon-archive',
|
||||
text: t('deck', (this.showArchived ? 'Unarchive card' : 'Archive card'))
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'currentCard': {
|
||||
immediate: true,
|
||||
handler() {
|
||||
this.copiedCard = JSON.parse(JSON.stringify(this.currentCard))
|
||||
this.allLabels = this.currentCard.labels
|
||||
this.assignedUsers = this.currentCard.assignedUsers.map((item) => item.participant)
|
||||
this.desc = this.currentCard.description
|
||||
}
|
||||
},
|
||||
desc() {
|
||||
this.copiedCard.description = this.desc
|
||||
this.saveDesc()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setDue() {
|
||||
this.$store.dispatch('updateCardDue', this.copiedCard)
|
||||
},
|
||||
removeDue() {
|
||||
this.copiedCard.duedate = null
|
||||
this.$store.dispatch('updateCardDue', this.copiedCard)
|
||||
},
|
||||
saveDesc() {
|
||||
this.$store.dispatch('updateCardDesc', this.copiedCard)
|
||||
},
|
||||
|
||||
closeSidebar() {
|
||||
this.$router.push({ name: 'board' })
|
||||
},
|
||||
|
||||
assignUserToCard(user) {
|
||||
this.copiedCard.newUserUid = user.uid
|
||||
this.$store.dispatch('assignCardToUser', this.copiedCard)
|
||||
},
|
||||
|
||||
removeUserFromCard(user) {
|
||||
this.copiedCard.removeUserUid = user.uid
|
||||
this.$store.dispatch('removeUserFromCard', this.copiedCard)
|
||||
},
|
||||
|
||||
addLabelToCard(newLabel) {
|
||||
this.copiedCard.labels.push(newLabel)
|
||||
let data = {
|
||||
card: this.copiedCard,
|
||||
labelId: newLabel.id
|
||||
}
|
||||
this.$store.dispatch('addLabel', data)
|
||||
},
|
||||
|
||||
removeLabelFromCard(removedLabel) {
|
||||
|
||||
let removeIndex = this.copiedCard.labels.findIndex((label) => {
|
||||
return label.id === removedLabel.id
|
||||
})
|
||||
if (removeIndex !== -1) {
|
||||
this.copiedCard.labels.splice(removeIndex, 1)
|
||||
}
|
||||
|
||||
let data = {
|
||||
card: this.copiedCard,
|
||||
labelId: removedLabel.id
|
||||
}
|
||||
this.$store.dispatch('removeLabel', data)
|
||||
},
|
||||
clickAddNewAttachmment() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
<style>
|
||||
@import "~easymde/dist/easymde.min.css";
|
||||
.editor-preview,
|
||||
.editor-statusbar {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -22,16 +22,21 @@
|
||||
|
||||
<template>
|
||||
<div class="badges">
|
||||
<div v-if="true" class="card-files icon icon-files-dark" />
|
||||
<div v-if="true" class="card-comments icon icon-comment" />
|
||||
<div v-if="true" :class="{'icon-calendar': true, 'icon-calendar-dark': false}" class="due icon now">
|
||||
<span>Now</span>
|
||||
<div v-if="card.attachments" class="card-files icon icon-files-dark" />
|
||||
|
||||
<div v-if="card.description" class="card-comments icon icon-filetype-text" />
|
||||
|
||||
<div v-if="card.duedate" :class="dueIcon">
|
||||
<span>{{ dueTimeDiff }}</span>
|
||||
</div>
|
||||
<div v-if="true" class="card-tasks icon icon-checkmark">
|
||||
|
||||
<div v-if="card.description && card.description.match(/\[\s*\]/g)" class="card-tasks icon icon-checkmark">
|
||||
<!-- TODO: get checkbox values -->
|
||||
<span>0/0</span>
|
||||
</div>
|
||||
<div v-if="true" class="card-assigned-users">
|
||||
<avatar user="admin" />
|
||||
|
||||
<div v-if="card.assignedUsers" class="card-assigned-users">
|
||||
<avatar v-for="user in card.assignedUsers" :key="user.id" :user="user.participant.primaryKey" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -50,6 +55,43 @@ export default {
|
||||
computed: {
|
||||
compactMode() {
|
||||
return false
|
||||
},
|
||||
dueIcon() {
|
||||
let timeInHours = Math.round((Date.parse(this.card.duedate) - Date.now()) / 1000 / 60 / 60 / 24)
|
||||
|
||||
if (timeInHours === 1) {
|
||||
return 'icon-calendar-dark due icon next'
|
||||
}
|
||||
if (timeInHours === 0) {
|
||||
return 'icon-calendar-dark due icon now'
|
||||
}
|
||||
if (timeInHours < 0) {
|
||||
return 'icon-calendar-dark due icon overdue'
|
||||
}
|
||||
},
|
||||
dueTimeDiff() {
|
||||
let unit = 'Minutes'
|
||||
let timeInMin = (Date.parse(this.card.duedate) - Date.now()) / 60000
|
||||
|
||||
if (timeInMin > 59) {
|
||||
timeInMin /= 60
|
||||
unit = 'Hours'
|
||||
}
|
||||
|
||||
if (timeInMin > 23) {
|
||||
timeInMin /= 24
|
||||
unit = 'Days'
|
||||
}
|
||||
|
||||
if (timeInMin > 355) {
|
||||
timeInMin /= 355
|
||||
unit = 'Years'
|
||||
}
|
||||
|
||||
return Math.round(timeInMin) + ' ' + unit
|
||||
},
|
||||
card() {
|
||||
return this.$store.getters.cardById(this.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,28 +22,37 @@
|
||||
|
||||
<template>
|
||||
<div :class="{'compact': compactMode}" tag="div" class="card"
|
||||
@click="openCard">
|
||||
@click.self="openCard">
|
||||
<div class="card-upper">
|
||||
<h3 @click.stop="startEditing">{{ card.title }}</h3>
|
||||
<h3 @click.stop="startEditing(card)">{{ card.title }}</h3>
|
||||
<transition name="fade" mode="out-in">
|
||||
<form v-if="editing">
|
||||
<input :value="card.title" type="text" autofocus>
|
||||
<input type="button" class="icon-confirm" @click.stop="editing=false">
|
||||
<input v-model="copiedCard.title" type="text" autofocus>
|
||||
<input type="button" class="icon-confirm" @click="finishedEdit(card)">
|
||||
</form>
|
||||
<action v-if="!editing" :actions="visibilityPopover" @click.stop="" />
|
||||
|
||||
<Actions @click.stop.prevent>
|
||||
<ActionButton icon="icon-user" @click="assignCardToMe()">{{ t('deck', 'Assign to me') }}</ActionButton>
|
||||
<ActionButton icon="icon-archive" @click="archiveUnarchiveCard()">{{ t('deck', (showArchived ? 'Unarchive card' : 'Archive card')) }}</ActionButton>
|
||||
<ActionButton icon="icon-delete" @click="deleteCard()">{{ t('deck', 'Delete card') }}</ActionButton>
|
||||
<ActionButton icon="icon-settings-dark" @click="setCurrentCard()">{{ t('deck', 'Card details') }}</ActionButton>
|
||||
</Actions>
|
||||
|
||||
</transition>
|
||||
</div>
|
||||
<ul class="labels">
|
||||
<li v-for="label in labels" :key="label.id" :style="labelStyle(label)"><span>{{ label.title }}</span></li>
|
||||
<ul class="labels" @click="openCard">
|
||||
<li v-for="label in card.labels" :key="label.id" :style="labelStyle(label)"><span>{{ label.title }}</span></li>
|
||||
</ul>
|
||||
<div v-show="!compactMode" class="card-controls compact-item">
|
||||
<card-badges />
|
||||
<div v-show="!compactMode" class="card-controls compact-item" @click="openCard">
|
||||
<card-badges :id="id" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { PopoverMenu, Action } from 'nextcloud-vue'
|
||||
import { PopoverMenu } from 'nextcloud-vue'
|
||||
import { Actions } from 'nextcloud-vue/dist/Components/Actions'
|
||||
import { ActionButton } from 'nextcloud-vue/dist/Components/ActionButton'
|
||||
import ClickOutside from 'vue-click-outside'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
@@ -53,7 +62,7 @@ import Color from '../../mixins/color'
|
||||
|
||||
export default {
|
||||
name: 'CardItem',
|
||||
components: { PopoverMenu, CardBadges, LabelTag, Action },
|
||||
components: { PopoverMenu, CardBadges, LabelTag, Actions, ActionButton },
|
||||
directives: {
|
||||
ClickOutside
|
||||
},
|
||||
@@ -67,12 +76,14 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
menuOpened: false,
|
||||
editing: false
|
||||
editing: false,
|
||||
copiedCard: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
compactMode: state => state.compactMode
|
||||
compactMode: state => state.compactMode,
|
||||
showArchived: state => state.showArchived
|
||||
}),
|
||||
card() {
|
||||
return this.$store.getters.cardById(this.id)
|
||||
@@ -80,12 +91,6 @@ export default {
|
||||
menu() {
|
||||
return []
|
||||
},
|
||||
labels() {
|
||||
return [
|
||||
{ id: 1, title: 'ToDo', color: 'aa0000' },
|
||||
{ id: 2, title: 'Done', color: '33ff33' }
|
||||
]
|
||||
},
|
||||
labelStyle() {
|
||||
return (label) => {
|
||||
return {
|
||||
@@ -93,25 +98,11 @@ export default {
|
||||
color: this.textColor(label.color)
|
||||
}
|
||||
}
|
||||
},
|
||||
visibilityPopover() {
|
||||
return [
|
||||
{
|
||||
action: () => {},
|
||||
icon: 'icon-archive-dark',
|
||||
text: t('deck', 'Archive card')
|
||||
},
|
||||
{
|
||||
action: () => {},
|
||||
icon: 'icon-settings-dark',
|
||||
text: t('deck', 'Card details')
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openCard() {
|
||||
this.$router.push({ name: 'card', params: { cardId: 123 } })
|
||||
this.$router.push({ name: 'card', params: { cardId: this.id } })
|
||||
},
|
||||
togglePopoverMenu() {
|
||||
this.menuOpened = !this.menuOpened
|
||||
@@ -119,9 +110,33 @@ export default {
|
||||
hidePopoverMenu() {
|
||||
this.menuOpened = false
|
||||
},
|
||||
startEditing() {
|
||||
startEditing(card) {
|
||||
this.copiedCard = Object.assign({}, card)
|
||||
this.editing = true
|
||||
},
|
||||
finishedEdit(card) {
|
||||
if (this.copiedCard.title !== card.title) {
|
||||
this.$store.dispatch('updateCard', this.copiedCard)
|
||||
}
|
||||
this.editing = false
|
||||
},
|
||||
deleteCard() {
|
||||
this.$store.dispatch('deleteCard', this.card)
|
||||
},
|
||||
archiveUnarchiveCard() {
|
||||
this.copiedCard = Object.assign({}, this.card)
|
||||
this.copiedCard.archived = !this.copiedCard.archived
|
||||
this.$store.dispatch('archiveUnarchiveCard', this.copiedCard)
|
||||
},
|
||||
assignCardToMe() {
|
||||
this.copiedCard = Object.assign({}, this.card)
|
||||
this.copiedCard.newUserUid = this.card.owner.uid
|
||||
this.$store.dispatch('assignCardToUser', this.copiedCard)
|
||||
},
|
||||
setCurrentCard() {
|
||||
this.$store.dispatch('setCurrentCard', this.card)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -153,8 +168,9 @@ export default {
|
||||
display: flex;
|
||||
padding: 5px 7px;
|
||||
position: absolute;
|
||||
width: calc(100% - 14px);
|
||||
input[type=text] {
|
||||
width: 100%;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -102,7 +102,8 @@ export default {
|
||||
]),
|
||||
isAdmin() {
|
||||
// eslint-disable-next-line
|
||||
return oc_isadmin
|
||||
//return oc_isadmin
|
||||
return OC.isUserAdmin()
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
|
||||
@@ -63,11 +63,10 @@ export default {
|
||||
this.editing = true
|
||||
},
|
||||
createBoard(e) {
|
||||
console.log(this.color)
|
||||
const title = e.currentTarget.childNodes[0].value
|
||||
this.$store.dispatch('createBoard', {
|
||||
title: title,
|
||||
color: this.color.hex.substring(1)
|
||||
color: this.color.substring(1)
|
||||
})
|
||||
this.editing = false
|
||||
},
|
||||
|
||||
@@ -27,8 +27,14 @@
|
||||
<a href="#">
|
||||
{{ board.title }}
|
||||
</a>
|
||||
|
||||
<div v-if="actions.length > 0" class="app-navigation-entry-utils">
|
||||
<ul>
|
||||
<li class="app-navigation-entry-utils-menu-button">
|
||||
<button v-if="board.acl.length === 0" class="icon-shared" style="opacity: 0.3"
|
||||
@click="showSidebar" />
|
||||
<button v-else class="icon-shared" @click="showSidebar" />
|
||||
</li>
|
||||
<li class="app-navigation-entry-utils-menu-button">
|
||||
<button v-click-outside="hideMenu" @click="showMenu" />
|
||||
</li>
|
||||
@@ -212,6 +218,11 @@ export default {
|
||||
},
|
||||
cancelEdit(e) {
|
||||
this.editing = false
|
||||
},
|
||||
showSidebar() {
|
||||
const route = this.routeTo
|
||||
route.name = 'board.details'
|
||||
this.$router.push(route)
|
||||
}
|
||||
},
|
||||
inject: [
|
||||
|
||||
@@ -86,6 +86,13 @@ export default new Router({
|
||||
components: {
|
||||
default: Boards,
|
||||
sidebar: BoardSidebar
|
||||
},
|
||||
props: {
|
||||
default: (route) => {
|
||||
return {
|
||||
id: parseInt(route.params.id, 10)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -93,6 +100,13 @@ export default new Router({
|
||||
name: 'card',
|
||||
components: {
|
||||
sidebar: CardSidebar
|
||||
},
|
||||
props: {
|
||||
sidebar: (route) => {
|
||||
return {
|
||||
id: parseInt(route.params.cardId, 10)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
167
src/services/CardApi.js
Normal file
167
src/services/CardApi.js
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* @copyright Copyright (c) 2018 Michael Weimann <mail@michael-weimann.eu>
|
||||
*
|
||||
* @author Michael Weimann <mail@michael-weimann.eu>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
import axios from 'nextcloud-axios'
|
||||
|
||||
export class CardApi {
|
||||
|
||||
url(url) {
|
||||
url = `/apps/deck${url}`
|
||||
return OC.generateUrl(url)
|
||||
}
|
||||
|
||||
addCard(card) {
|
||||
return axios.post(this.url(`/cards`), card)
|
||||
.then(
|
||||
(response) => {
|
||||
return Promise.resolve(response.data)
|
||||
},
|
||||
(err) => {
|
||||
return Promise.reject(err)
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
deleteCard(cardId) {
|
||||
return axios.delete(this.url(`/cards/${cardId}`))
|
||||
.then(
|
||||
(response) => {
|
||||
return Promise.resolve(response.data)
|
||||
},
|
||||
(err) => {
|
||||
return Promise.reject(err)
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
updateCard(card) {
|
||||
return axios.put(this.url(`/cards/${card.id}`), card)
|
||||
.then(
|
||||
(response) => {
|
||||
return Promise.resolve(response.data)
|
||||
},
|
||||
(err) => {
|
||||
return Promise.reject(err)
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
assignUser(card) {
|
||||
return axios.post(this.url(`/cards/${card.id}/assign`), { userId: card.newUserUid })
|
||||
.then(
|
||||
(response) => {
|
||||
return Promise.resolve(response.data)
|
||||
},
|
||||
(err) => {
|
||||
return Promise.reject(err)
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
removeUser(card) {
|
||||
return axios.delete(this.url(`/cards/${card.id}/assign/${card.removeUserUid}`))
|
||||
.then(
|
||||
(response) => {
|
||||
return Promise.resolve(response.data)
|
||||
},
|
||||
(err) => {
|
||||
return Promise.reject(err)
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
archiveCard(card) {
|
||||
return axios.put(this.url(`/cards/${card.id}/archive`))
|
||||
.then(
|
||||
(response) => {
|
||||
return Promise.resolve(response.data)
|
||||
},
|
||||
(err) => {
|
||||
return Promise.reject(err)
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
unArchiveCard(card) {
|
||||
return axios.put(this.url(`/cards/${card.id}/unarchive`))
|
||||
.then(
|
||||
(response) => {
|
||||
return Promise.resolve(response.data)
|
||||
},
|
||||
(err) => {
|
||||
return Promise.reject(err)
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
assignLabelToCard(data) {
|
||||
return axios.post(this.url(`/cards/${data.card.id}/label/${data.labelId}`))
|
||||
.then(
|
||||
(response) => {
|
||||
return Promise.resolve(response.data)
|
||||
},
|
||||
(err) => {
|
||||
return Promise.reject(err)
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
removeLabelFromCard(data) {
|
||||
return axios.delete(this.url(`/cards/${data.card.id}/label/${data.labelId}`))
|
||||
.then(
|
||||
(response) => {
|
||||
return Promise.resolve(response.data)
|
||||
},
|
||||
(err) => {
|
||||
return Promise.reject(err)
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -44,6 +44,51 @@ export class StackApi {
|
||||
})
|
||||
}
|
||||
|
||||
deletedStacks(boardId) {
|
||||
return axios.get(this.url(`/${boardId}/stacks/deleted`))
|
||||
.then(
|
||||
(response) => {
|
||||
return Promise.resolve(response.data)
|
||||
},
|
||||
(err) => {
|
||||
return Promise.reject(err)
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
deletedCards(boardId) {
|
||||
return axios.get(this.url(`/${boardId}/cards/deleted`))
|
||||
.then(
|
||||
(response) => {
|
||||
return Promise.resolve(response.data)
|
||||
},
|
||||
(err) => {
|
||||
return Promise.reject(err)
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
loadArchivedStacks(boardId) {
|
||||
return axios.get(this.url(`/stacks/${boardId}/archived`))
|
||||
.then(
|
||||
(response) => {
|
||||
return Promise.resolve(response.data)
|
||||
},
|
||||
(err) => {
|
||||
return Promise.reject(err)
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Stack} stack
|
||||
* @returns {Promise}
|
||||
@@ -78,4 +123,34 @@ export class StackApi {
|
||||
})
|
||||
}
|
||||
|
||||
deleteStack(stackId) {
|
||||
return axios.delete(this.url(`/stacks/${stackId}`))
|
||||
.then(
|
||||
(response) => {
|
||||
return Promise.resolve(response.data)
|
||||
},
|
||||
(err) => {
|
||||
return Promise.reject(err)
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
updateStack(stack) {
|
||||
return axios.put(this.url(`/stacks/${stack.id}`), stack)
|
||||
.then(
|
||||
(response) => {
|
||||
return Promise.resolve(response.data)
|
||||
},
|
||||
(err) => {
|
||||
return Promise.reject(err)
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
return Promise.reject(err)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,8 +20,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import { CardApi } from './../services/CardApi'
|
||||
import Vue from 'vue'
|
||||
|
||||
const apiClient = new CardApi()
|
||||
|
||||
export default {
|
||||
state: {
|
||||
cards: []
|
||||
@@ -35,6 +38,9 @@ export default {
|
||||
}
|
||||
},
|
||||
mutations: {
|
||||
clearCards(state) {
|
||||
state.cards = []
|
||||
},
|
||||
addCard(state, card) {
|
||||
let existingIndex = state.cards.findIndex(_card => _card.id === card.id)
|
||||
if (existingIndex !== -1) {
|
||||
@@ -43,8 +49,125 @@ export default {
|
||||
} else {
|
||||
state.cards.push(card)
|
||||
}
|
||||
},
|
||||
deleteCard(state, card) {
|
||||
let existingIndex = state.cards.findIndex(_card => _card.id === card.id)
|
||||
if (existingIndex !== -1) {
|
||||
state.cards.splice(existingIndex, 1)
|
||||
}
|
||||
},
|
||||
updateTitle(state, card) {
|
||||
let existingIndex = state.cards.findIndex(_card => _card.id === card.id)
|
||||
if (existingIndex !== -1) {
|
||||
state.cards[existingIndex].title = card.title
|
||||
}
|
||||
},
|
||||
assignCardToUser(state, user) {
|
||||
let existingIndex = state.cards.findIndex(_card => _card.id === user.cardId)
|
||||
if (existingIndex !== -1) {
|
||||
state.cards[existingIndex].assignedUsers.push(user)
|
||||
}
|
||||
},
|
||||
removeUserFromCard(state, user) {
|
||||
let existingIndex = state.cards.findIndex(_card => _card.id === user.cardId)
|
||||
if (existingIndex !== -1) {
|
||||
let foundIndex = state.cards[existingIndex].assignedUsers.findIndex(_user => _user.id === user.id)
|
||||
if (foundIndex !== -1) {
|
||||
state.cards[existingIndex].assignedUsers.splice(foundIndex, 1)
|
||||
}
|
||||
}
|
||||
},
|
||||
updateCardDesc(state, card) {
|
||||
let existingIndex = state.cards.findIndex(_card => _card.id === card.id)
|
||||
if (existingIndex !== -1) {
|
||||
state.cards[existingIndex].description = card.description
|
||||
}
|
||||
},
|
||||
updateCardDue(state, card) {
|
||||
let existingIndex = state.cards.findIndex(_card => _card.id === card.id)
|
||||
if (existingIndex !== -1) {
|
||||
state.cards[existingIndex].duedate = card.duedate
|
||||
}
|
||||
},
|
||||
updateCardLabels(state, card) {
|
||||
let existingIndex = state.cards.findIndex(_card => _card.id === card.id)
|
||||
if (existingIndex !== -1) {
|
||||
let existingCard = state.cards.find(_card => _card.id === card.id)
|
||||
existingCard.labels = card.labels
|
||||
}
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
addCard({ commit }, card) {
|
||||
apiClient.addCard(card)
|
||||
.then((createdCard) => {
|
||||
commit('addCard', createdCard)
|
||||
})
|
||||
},
|
||||
updateCard({ commit }, card) {
|
||||
apiClient.updateCard(card)
|
||||
.then((updatedCard) => {
|
||||
commit('updateTitle', updatedCard)
|
||||
})
|
||||
},
|
||||
deleteCard({ commit }, card) {
|
||||
apiClient.deleteCard(card.id)
|
||||
.then((card) => {
|
||||
commit('deleteCard', card)
|
||||
})
|
||||
},
|
||||
archiveUnarchiveCard({ commit }, card) {
|
||||
let call = 'archiveCard'
|
||||
if (card.archived === false) {
|
||||
call = 'unArchiveCard'
|
||||
}
|
||||
|
||||
apiClient[call](card)
|
||||
.then((card) => {
|
||||
commit('deleteCard', card)
|
||||
})
|
||||
},
|
||||
assignCardToUser({ commit }, card) {
|
||||
apiClient.assignUser(card)
|
||||
.then((user) => {
|
||||
commit('assignCardToUser', user)
|
||||
})
|
||||
},
|
||||
removeUserFromCard({ commit }, card) {
|
||||
apiClient.removeUser(card)
|
||||
.then((user) => {
|
||||
commit('removeUserFromCard', user)
|
||||
})
|
||||
},
|
||||
addLabel({ commit }, data) {
|
||||
apiClient.assignLabelToCard(data)
|
||||
.then(() => {
|
||||
commit('updateCardLabels', data.card)
|
||||
})
|
||||
},
|
||||
removeLabel({ commit }, data) {
|
||||
apiClient.removeLabelFromCard(data)
|
||||
.then(() => {
|
||||
commit('updateCardLabels', data.card)
|
||||
})
|
||||
},
|
||||
cardUndoDelete({ commit }, card) {
|
||||
apiClient.updateCard(card)
|
||||
.then((card) => {
|
||||
commit('addCard', card)
|
||||
})
|
||||
},
|
||||
updateCardDesc({ commit }, card) {
|
||||
apiClient.updateCard(card)
|
||||
.then((updatedCard) => {
|
||||
commit('updateCardDesc', updatedCard)
|
||||
})
|
||||
},
|
||||
updateCardDue({ commit }, card) {
|
||||
apiClient.updateCard(card)
|
||||
.then((card) => {
|
||||
commit('updateCardDue', card)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,12 +46,15 @@ export default new Vuex.Store({
|
||||
},
|
||||
strict: debug,
|
||||
state: {
|
||||
showArchived: false,
|
||||
navShown: true,
|
||||
compactMode: false,
|
||||
sidebarShown: false,
|
||||
currentBoard: null,
|
||||
currentCard: null,
|
||||
boards: [],
|
||||
sharees: [],
|
||||
assignableUsers: [],
|
||||
boardFilter: BOARD_FILTERS.ALL
|
||||
},
|
||||
getters: {
|
||||
@@ -90,6 +93,9 @@ export default new Vuex.Store({
|
||||
}
|
||||
},
|
||||
mutations: {
|
||||
toggleShowArchived(state) {
|
||||
state.showArchived = !state.showArchived
|
||||
},
|
||||
/**
|
||||
* Adds or replaces a board in the store.
|
||||
* Matches a board by it's id.
|
||||
@@ -131,8 +137,12 @@ export default new Vuex.Store({
|
||||
setBoards(state, boards) {
|
||||
state.boards = boards
|
||||
},
|
||||
setSharees(state, sharees) {
|
||||
state.sharees = sharees
|
||||
setSharees(state, shareesUsersAndGroups) {
|
||||
state.sharees = shareesUsersAndGroups.users
|
||||
state.sharees.push(...shareesUsersAndGroups.groups)
|
||||
},
|
||||
setAssignableUsers(state, users) {
|
||||
state.assignableUsers = users
|
||||
},
|
||||
setBoardFilter(state, filter) {
|
||||
state.boardFilter = filter
|
||||
@@ -140,6 +150,9 @@ export default new Vuex.Store({
|
||||
setCurrentBoard(state, board) {
|
||||
state.currentBoard = board
|
||||
},
|
||||
setCurrentCard(state, card) {
|
||||
state.currentCard = card
|
||||
},
|
||||
|
||||
// label mutators
|
||||
removeLabelFromCurrentBoard(state, labelId) {
|
||||
@@ -166,8 +179,8 @@ export default new Vuex.Store({
|
||||
},
|
||||
|
||||
// acl mutators
|
||||
addAclToCurrentBoard(state, acl) {
|
||||
console.log(state.currentBoard)
|
||||
addAclToCurrentBoard(state, createdAcl) {
|
||||
Vue.set(state.currentBoard.acl, createdAcl.id, createdAcl)
|
||||
},
|
||||
updateAclFromCurrentBoard(state, acl) {
|
||||
for (var acl_ in state.currentBoard.acl) {
|
||||
@@ -178,15 +191,24 @@ export default new Vuex.Store({
|
||||
}
|
||||
},
|
||||
deleteAclFromCurrentBoard(state, acl) {
|
||||
for (var acl_ in state.currentBoard.acl) {
|
||||
if (state.currentBoard.acl[acl_].participant.uid === acl.participant.uid) {
|
||||
delete state.currentBoard.acl[acl_]
|
||||
let removeIndex = -1
|
||||
for (var index in state.currentBoard.acl) {
|
||||
var attr = state.currentBoard.acl[index]
|
||||
if (acl.id === attr.id) {
|
||||
removeIndex = index
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (removeIndex > -1) {
|
||||
Vue.delete(state.currentBoard.acl, removeIndex)
|
||||
}
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
toggleShowArchived({ commit }) {
|
||||
commit('toggleShowArchived')
|
||||
},
|
||||
/**
|
||||
* @param commit
|
||||
* @param state
|
||||
@@ -244,9 +266,10 @@ export default new Vuex.Store({
|
||||
params.append('itemType', 0)
|
||||
params.append('itemType', 1)
|
||||
axios.get(OC.linkToOCS('apps/files_sharing/api/v1') + 'sharees', { params }).then((response) => {
|
||||
commit('setSharees', response.data.ocs.data.users)
|
||||
commit('setSharees', response.data.ocs.data)
|
||||
})
|
||||
},
|
||||
|
||||
setBoardFilter({ commmit }, filter) {
|
||||
commmit('setBoardFilter', filter)
|
||||
},
|
||||
@@ -262,6 +285,12 @@ export default new Vuex.Store({
|
||||
setCurrentBoard({ commit }, board) {
|
||||
commit('setCurrentBoard', board)
|
||||
},
|
||||
setAssignableUsers({ commit }, board) {
|
||||
commit('setAssignableUsers', board)
|
||||
},
|
||||
setCurrentCard({ commit }, card) {
|
||||
commit('setCurrentCard', card)
|
||||
},
|
||||
|
||||
// label actions
|
||||
removeLabelFromCurrentBoard({ commit }, label) {
|
||||
@@ -285,11 +314,11 @@ export default new Vuex.Store({
|
||||
},
|
||||
|
||||
// acl actions
|
||||
addAclToCurrentBoard({ commit }, acl) {
|
||||
acl.boardId = this.state.currentBoard.id
|
||||
apiClient.addAcl(acl)
|
||||
.then((acl) => {
|
||||
commit('addAclToCurrentBoard', acl)
|
||||
addAclToCurrentBoard({ commit }, newAcl) {
|
||||
newAcl.boardId = this.state.currentBoard.id
|
||||
apiClient.addAcl(newAcl)
|
||||
.then((returnAcl) => {
|
||||
commit('addAclToCurrentBoard', returnAcl)
|
||||
})
|
||||
},
|
||||
updateAclFromCurrentBoard({ commit }, acl) {
|
||||
|
||||
@@ -28,7 +28,9 @@ const apiClient = new StackApi()
|
||||
|
||||
export default {
|
||||
state: {
|
||||
stacks: []
|
||||
stacks: [],
|
||||
deletedStacks: [],
|
||||
deletedCards: []
|
||||
},
|
||||
getters: {
|
||||
stacksByBoard: state => (id) => {
|
||||
@@ -51,6 +53,29 @@ export default {
|
||||
for (let i = 0; i < newOrder.length; i++) {
|
||||
newOrder[i].order = parseInt(i)
|
||||
}
|
||||
},
|
||||
deleteStack(state, stack) {
|
||||
|
||||
let existingIndex = state.stacks.findIndex(_stack => _stack.id === stack.id)
|
||||
if (existingIndex !== -1) {
|
||||
state.stacks.splice(existingIndex, 1)
|
||||
}
|
||||
},
|
||||
updateStack(state, stack) {
|
||||
let existingIndex = state.stacks.findIndex(_stack => _stack.id === stack.id)
|
||||
if (existingIndex !== -1) {
|
||||
state.stacks[existingIndex].title = stack.title
|
||||
}
|
||||
},
|
||||
setDeletedStacks(state, delStacks) {
|
||||
state.deletedStacks = []
|
||||
if (delStacks.length > 0) {
|
||||
state.deletedStacks = delStacks
|
||||
}
|
||||
},
|
||||
setDeletedCards(state, delCards) {
|
||||
state.deletedCards = []
|
||||
state.deletedCards = delCards
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
@@ -64,7 +89,12 @@ export default {
|
||||
})
|
||||
},
|
||||
loadStacks({ commit }, board) {
|
||||
apiClient.loadStacks(board.id)
|
||||
commit('clearCards')
|
||||
let call = 'loadStacks'
|
||||
if (this.state.showArchived === true) {
|
||||
call = 'loadArchivedStacks'
|
||||
}
|
||||
apiClient[call](board.id)
|
||||
.then((stacks) => {
|
||||
for (let i in stacks) {
|
||||
let stack = stacks[i]
|
||||
@@ -77,10 +107,40 @@ export default {
|
||||
})
|
||||
},
|
||||
createStack({ commit }, stack) {
|
||||
stack.boardId = this.state.currentBoard.id
|
||||
apiClient.createStack(stack)
|
||||
.then((createdStack) => {
|
||||
commit('addStack', createdStack)
|
||||
})
|
||||
},
|
||||
deleteStack({ commit }, stack) {
|
||||
apiClient.deleteStack(stack.id)
|
||||
.then((stack) => {
|
||||
commit('deleteStack', stack)
|
||||
})
|
||||
},
|
||||
updateStack({ commit }, stack) {
|
||||
apiClient.updateStack(stack)
|
||||
.then((stack) => {
|
||||
commit('updateStack', stack)
|
||||
})
|
||||
},
|
||||
deletedItems({ commit }, boardId) {
|
||||
apiClient.deletedStacks(boardId)
|
||||
.then((deletedStacks) => {
|
||||
commit('setDeletedStacks', deletedStacks)
|
||||
})
|
||||
apiClient.deletedCards(boardId)
|
||||
.then((deletedCards) => {
|
||||
commit('setDeletedCards', deletedCards)
|
||||
})
|
||||
},
|
||||
stackUndoDelete({ commit }, stack) {
|
||||
apiClient.updateStack(stack)
|
||||
.then((stack) => {
|
||||
commit('addStack', stack)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ module.exports = {
|
||||
output: {
|
||||
path: path.resolve(__dirname, './js'),
|
||||
publicPath: '/js/',
|
||||
filename: 'deck.js'
|
||||
filename: 'deck.js',
|
||||
jsonpFunction: 'webpackJsonpOCADeck'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
|
||||
Reference in New Issue
Block a user