I have a kanban board that displays issues in a project made up of three lists, Open, In Progress, and Completed.
I am using vue draggable in order to drag and drop issues between the three lists.
I fetch the issues from an api call using axios and I also update each issues status(i.e Open, In Progress, Closed) with a post request as well.
The issue is that when I drag an issue, lets say from Open and drop it in In Progress, the issue returns to the Open column in the kanban board. I have to refresh the page for the issue to move to the In Progress column. This means that the update request is working.
But how can I make the issue move without refreshing the page?
KanBanBoard.vue
<template>
<v-container>
<v-row wrap>
<v-col xl="4" lg="4" md="4" sm="4" xs="12">
<v-card>
<v-card-title class="blue lighten-3">
<span class="white--text">Open</span>
</v-card-title>
<v-divider horizontal></v-divider>
<v-card-text class="blue lighten-3">
<draggable class="list-group kanban-column" :list="Open" group="tasks" :move="onDrop">
<v-card
class="#f4f5fa"
style="height:auto; margin-top:10px"
v-for="issue in Open"
:key="issue"
align-center
elevation="3"
>
<v-card-text>
<v-row dense style="width:auto">
<router-link
class="d-flex align-center text-decoration-none grey--text"
style="font-size:18px;"
:to="{ name: 'IssuePage', params: { id: issue.id, issue } }"
>
{{ issue.title }}
</router-link>
</v-row>
<v-row dense>
<v-col>
<v-chip
class="ma-2"
color="red"
outlined
style="position:relative; right:10px;top:10px; height:min-content"
>
{{ issue.issueSeverity }}
</v-chip>
</v-col>
<v-col>
<v-chip
class="ma-2"
color="green"
outlined
style="position:relative; right:83px; top:10px;height:min-content"
>
{{ issue.issueType }}
</v-chip>
</v-col>
</v-row>
</v-card-text>
</v-card>
</draggable>
</v-card-text>
</v-card>
</v-col>
<v-col xl="4" lg="4" md="4" sm="4" xs="12">
<v-card>
<v-card-title class="light-green lighten-3">
<span class="white--text">In Progress</span>
</v-card-title>
<v-divider horizontal></v-divider>
<v-card-text class="light-green lighten-3">
<draggable class="list-group kanban-column" :list="InProgress" group="tasks" :move="onDrop">
<v-card
class="#f4f5fa"
style="height:auto; margin-top:10px"
v-for="issue in InProgress"
:key="issue"
align-center
elevation="3"
>
<v-card-text>
<v-row dense style="width:auto">
<router-link
class="d-flex align-center text-decoration-none grey--text"
style="font-size:18px;"
:to="{ name: 'IssuePage', params: { id: issue.id, issue } }"
>
{{ issue.title }}
</router-link>
</v-row>
<v-row>
<v-col>
<v-chip
class="ma-2"
color="red"
outlined
style="position:relative; right:10px;top:10px; height:min-content"
>
{{ issue.issueSeverity }}
</v-chip>
</v-col>
<v-col>
<v-chip
class="ma-2"
color="green"
outlined
style="position:relative; right:83px; top:10px;height:min-content"
>
{{ issue.issueType }}
</v-chip>
</v-col>
</v-row>
</v-card-text>
</v-card>
</draggable>
</v-card-text>
</v-card>
</v-col>
<v-col xl="4" lg="4" md="4" sm="4" xs="12">
<v-card>
<v-card-title class="orange lighten-3">
<span class="white--text">Completed</span>
</v-card-title>
<v-divider horizontal></v-divider>
<v-card-text class="orange lighten-3">
<draggable class="list-group kanban-column" :list="Completed" group="tasks" :move="onDrop">
<v-card
class="#f4f5fa"
style="height:auto; margin-top:10px"
v-for="issue in Completed"
:key="issue"
align-center
elevation="3"
>
<v-card-text>
<v-row dense style="width:auto">
<router-link
class="d-flex align-center text-decoration-none grey--text"
style="font-size:18px;"
:to="{ name: 'IssuePage', params: { id: issue.id, issue } }"
>
{{ issue.title }}
</router-link>
</v-row>
<v-row dense>
<v-col>
<v-chip
class="ma-2"
color="red"
outlined
style="position:relative; right:10px;top:10px; height:min-content"
>
{{ issue.issueSeverity }}
</v-chip>
</v-col>
<v-col>
<v-chip
class="ma-2"
color="green"
outlined
style="position:relative; right:83px; top:10px;height:min-content"
>
{{ issue.issueType }}
</v-chip>
</v-col>
</v-row>
</v-card-text>
</v-card>
</draggable>
</v-card-text>
</v-card>
</v-col>
</v-row>
</v-container>
</template>
<script>
import draggable from 'vuedraggable'
import { mapGetters, mapActions } from 'vuex'
export default {
props: ['project_id'],
components: {
draggable,
},
computed: {
...mapGetters(['Open','InProgress', 'Completed', 'Statuses', 'Types', 'Severities']),
},
watch: {
project_id() {
this.fetchIssuesofProject(this.project_id)
this.test()
},
},
methods: {
...mapActions(['fetchIssuesofProject', 'updateIssueStatus']),
onDrop(evt) {
const movedIssue = evt.draggedContext.element
var StatusTag = evt.relatedContext.element.issueStatus
var TypeTag = evt.relatedContext.element.issueType
var SeverityTag = evt.relatedContext.element.issueSeverity
var status = this.Statuses.find(st => st.title == StatusTag)
var type = this.Types.find(tp => tp.title == TypeTag)
var severity = this.Severities.find(sv => sv.title == SeverityTag)
const updateIssue = {
id: movedIssue.id,
created: movedIssue.created,
title: movedIssue.title,
description: movedIssue.description,
time_estimate: movedIssue.time_estimate,
userid: 'f3260d22-8b5b-4c40-be1e-d93ba732c576',
projectid: movedIssue.projectid,
issueTypeId: type.id,
issueStatusId: status.id,
issueSeverityId: severity.id,
}
//console.log(updateIssue)
this.updateIssueStatus(updateIssue)
},
test() {
console.log(this.projectid)
},
},
created() {
this.test(), this.fetchIssuesofProject(this.project_id), this.getIssueStatus()
},
}
</script>
<style scoped>
hr {
margin-top: 0.1px;
margin-bottom: 0.1px;
border: 1;
border-top: 1px solid rgba(0, 0, 0, 0.1);
}
</style>
Kanban.js (vuex)
import axios from 'axios'
const state = {
Issues: [],
}
const getters = {
Open: (state) => state.Issues.filter(x => x.issueStatus == 'Open'),
InProgress: (state) => state.Issues.filter(x => x.issueStatus == 'In Progress'),
Completed: (state) => state.Issues.filter(x => x.issueStatus == 'Closed'),
}
const actions = {
async fetchIssuesofProject({ commit }, projectid) {
const response = await axios.get('https://fadiserver.herokuapp.com/api/v1/my-issues-titles/?projectid=' + projectid).catch(error => {
console.log(error)
})
commit('setIssues', response.data)
},
async updateIssueStatus({ commit }, issue) {
const response = await axios.post('https://fadiserver.herokuapp.com/api/v1/my-issues/?id=' + issue.id, issue).catch(error => {
console.log(error)
})
commit('updateIssueStatus', response.data)
},
}
const mutations = {
setIssues: (state, Issues) => (state.Issues = Issues),
updateIssueStatus: (state, Issue) => {
const index = state.Issues.findIndex(is => is.id == Issue.id)
if(index !== -1){
state.Issue.splice(index, 1, Issue)
}
}
}
export default {
state,
getters,
actions,
mutations
}
If you see mapGetters without equivelent mapActions, it is because it is called in another component, so no need to worry about them