mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-16 22:27:26 +01:00
New client group plan (#8948)
* Added stripe payment for group plan * Began adding amazon * Added amazon payments for group * Added get group plans route * Added group plan nav * Added initial task page * Added create and edit group plans * Added initial approval header and footer * Added assignment and approved requirement * Added minor text fixes * Added inital approval flow * Added approval modal * Removed always true * Added more styles for filters * Added search * Added env vars * Fixed router issues * Added env to social login * Fixed merge conflict
This commit is contained in:
99
website/client/components/tasks/approvalFooter.vue
Normal file
99
website/client/components/tasks/approvalFooter.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<template lang="pug">
|
||||
div
|
||||
approval-modal(:task='task')
|
||||
.claim-bottom-message.col-12
|
||||
.task-unclaimed(v-if='!approvalRequested && !multipleApprovalsRequested')
|
||||
| {{ message }}
|
||||
a(@click='claim()', v-if='!userIsAssigned') Claim
|
||||
a(@click='unassign()', v-if='userIsAssigned') Remove Claim
|
||||
.row.task-single-approval(v-if='approvalRequested')
|
||||
.col-6.text-center
|
||||
a(@click='approve()') Approve Task
|
||||
.col-6.text-center
|
||||
a Needs work
|
||||
.text-center.task-multi-approval(v-if='multipleApprovalsRequested')
|
||||
a(@click='showRequests()') View Requests
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.task-unclaimed a {
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'client/libs/store';
|
||||
import approvalModal from './approvalModal';
|
||||
|
||||
export default {
|
||||
props: ['task', 'group'],
|
||||
components: {
|
||||
approvalModal,
|
||||
},
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
userIsAssigned () {
|
||||
return this.task.group.assignedUsers && this.task.group.assignedUsers.indexOf(this.user._id) !== -1;
|
||||
},
|
||||
message () {
|
||||
let assignedUsers = this.task.group.assignedUsers;
|
||||
let assignedUsersLength = assignedUsers.length;
|
||||
|
||||
if (assignedUsersLength === 1 && !this.userIsAssigned) {
|
||||
return `Assigned to ${assignedUsers}`;
|
||||
} else if (assignedUsersLength > 1 && !this.userIsAssigned) {
|
||||
return `Assigned to ${assignedUsersLength} members`;
|
||||
} else if (assignedUsersLength > 1 && this.userIsAssigned) {
|
||||
return `Assigned to you and ${assignedUsersLength} members`;
|
||||
} else if (this.userIsAssigned) {
|
||||
return 'You are assigned to this task';
|
||||
} else if (assignedUsersLength === 0) {
|
||||
return 'This task is unassigned';
|
||||
}
|
||||
},
|
||||
approvalRequested () {
|
||||
if (this.task.approvals && this.task.approvals.length === 1) return true;
|
||||
},
|
||||
multipleApprovalsRequested () {
|
||||
if (this.task.approvals && this.task.approvals.length > 1) return true;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
claim () {
|
||||
if (!confirm('Are you sure you want to claim this task?')) return;
|
||||
this.$store.dispatch('tasks:assignTask', {
|
||||
taskId: this.task._id,
|
||||
userId: this.user._id,
|
||||
});
|
||||
this.task.group.assignedUsers.push(this.user._id);
|
||||
// @TODO: Reload user tasks?
|
||||
},
|
||||
unassign () {
|
||||
if (!confirm('Are you sure you want to unclaim this task?')) return;
|
||||
this.$store.dispatch('tasks:unassignTask', {
|
||||
taskId: this.task._id,
|
||||
userId: this.user._id,
|
||||
});
|
||||
let index = this.task.group.assignedUsers.indexOf(this.user._id);
|
||||
this.task.group.assignedUsers.splice(index, 1);
|
||||
// @TODO: Reload user tasks?
|
||||
},
|
||||
approve () {
|
||||
if (!confirm('Are you sure you want to approve this task?')) return;
|
||||
let userIdToApprove = this.task.group.assignedUsers[0];
|
||||
this.$store.dispatch('tasks:unassignTask', {
|
||||
taskId: this.task._id,
|
||||
userId: userIdToApprove,
|
||||
});
|
||||
this.task.group.assignedUsers.splice(0, 1);
|
||||
this.task.approvals.splice(0, 1);
|
||||
},
|
||||
reject () {
|
||||
|
||||
},
|
||||
showRequests () {
|
||||
this.$root.$emit('show::modal', 'approval-modal');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
38
website/client/components/tasks/approvalHeader.vue
Normal file
38
website/client/components/tasks/approvalHeader.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template lang="pug">
|
||||
.claim-bottom-message.col-12.text-center(v-if='task.approvals && task.approvals.length > 0', :class="{approval: userIsAdmin}")
|
||||
.task-unclaimed
|
||||
| {{ message }}
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.approval {
|
||||
background: #24cc8f;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'client/libs/store';
|
||||
export default {
|
||||
props: ['task', 'group'],
|
||||
computed: {
|
||||
...mapState({user: 'user.data'}),
|
||||
message () {
|
||||
let approvals = this.task.approvals;
|
||||
let approvalsLength = approvals.length;
|
||||
let userIsRequesting = this.task.group.approvals && this.task.group.approvals.indexOf(this.user._id) !== -1;
|
||||
|
||||
if (approvalsLength === 1 && !userIsRequesting) {
|
||||
return `${approvals[0].userId.profile.name} requests approval`;
|
||||
} else if (approvalsLength > 1 && !userIsRequesting) {
|
||||
return `${approvalsLength} request approval`;
|
||||
} else if (approvalsLength === 1 && userIsRequesting) {
|
||||
return 'You are requesting approval';
|
||||
}
|
||||
},
|
||||
userIsAdmin () {
|
||||
return this.group.leader.id === this.user._id;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
44
website/client/components/tasks/approvalModal.vue
Normal file
44
website/client/components/tasks/approvalModal.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template lang="pug">
|
||||
b-modal#approval-modal(title="Approve Task", size='md', :hide-footer="true")
|
||||
.modal-body
|
||||
.row.approval(v-for='(approval, index) in task.approvals')
|
||||
.col-8
|
||||
strong {{approval.userId.profile.name}}
|
||||
.col-2
|
||||
button.btn.btn-primary(@click='approve(index)') Approve
|
||||
.modal-footer
|
||||
button.btn.btn-secondary(@click='close()') {{$t('close')}}
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.row.approval {
|
||||
padding-top: 1em;
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
|
||||
export default {
|
||||
props: ['task'],
|
||||
components: {
|
||||
bModal,
|
||||
},
|
||||
methods: {
|
||||
approve (index) {
|
||||
if (!confirm('Are you sure you want to approve this task?')) return;
|
||||
let userIdToApprove = this.task.group.assignedUsers[index];
|
||||
this.$store.dispatch('tasks:unassignTask', {
|
||||
taskId: this.task._id,
|
||||
userId: userIdToApprove,
|
||||
});
|
||||
this.task.group.assignedUsers.splice(index, 1);
|
||||
this.task.approvals.splice(index, 1);
|
||||
},
|
||||
close () {
|
||||
this.$root.$emit('hide::modal', 'approval-modal');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -16,6 +16,7 @@
|
||||
v-if="filterTask(task)",
|
||||
:isUser="isUser",
|
||||
@editTask="editTask",
|
||||
:group='group',
|
||||
)
|
||||
template(v-if="isUser === true && type === 'reward' && activeFilter.label !== 'custom'")
|
||||
.reward-items
|
||||
@@ -169,7 +170,7 @@ export default {
|
||||
bModal,
|
||||
shopItem,
|
||||
},
|
||||
props: ['type', 'isUser', 'searchText', 'selectedTags', 'taskListOverride'],
|
||||
props: ['type', 'isUser', 'searchText', 'selectedTags', 'taskListOverride', 'group'], // @TODO: maybe we should store the group on state?
|
||||
data () {
|
||||
const types = Object.freeze({
|
||||
habit: {
|
||||
@@ -227,6 +228,7 @@ export default {
|
||||
userPreferences: 'user.data.preferences',
|
||||
}),
|
||||
taskList () {
|
||||
// @TODO: This should not default to user's tasks. It should require that you pass options in
|
||||
if (this.taskListOverride) return this.taskListOverride;
|
||||
return this.tasks[`${this.type}s`];
|
||||
},
|
||||
@@ -265,6 +267,8 @@ export default {
|
||||
combinedTasksHeights += el.offsetHeight;
|
||||
});
|
||||
|
||||
if (!this.$refs.columnBackground) return;
|
||||
|
||||
const rewardsList = taskListEl.getElementsByClassName('reward-items')[0];
|
||||
if (rewardsList) {
|
||||
combinedTasksHeights += rewardsList.offsetHeight;
|
||||
|
||||
@@ -1,277 +1,281 @@
|
||||
<template lang="pug">
|
||||
.task.d-flex(:class="{'task-not-scoreable': isUser !== true}")
|
||||
// Habits left side control
|
||||
.left-control.d-flex.align-items-center.justify-content-center(v-if="task.type === 'habit'", :class="controlClass.up")
|
||||
.task-control.habit-control(:class="controlClass.up + '-control-habit'", @click="isUser ? score('up') : null")
|
||||
.svg-icon.positive(v-html="icons.positive")
|
||||
// Dailies and todos left side control
|
||||
.left-control.d-flex.justify-content-center(v-if="task.type === 'daily' || task.type === 'todo'", :class="controlClass")
|
||||
.task-control.daily-todo-control(:class="controlClass + '-control-daily-todo'", @click="isUser ? score(task.completed ? 'down' : 'up') : null")
|
||||
.svg-icon.check(v-html="icons.check", :class="{'display-check-icon': task.completed}")
|
||||
// Task title, description and icons
|
||||
.task-content(:class="contentClass")
|
||||
.task-clickable-area(@click="edit($event, task)")
|
||||
h3.task-title(:class="{ 'has-notes': task.notes }", v-markdown="task.text")
|
||||
.task-notes.small-text(v-markdown="task.notes")
|
||||
.checklist(v-if="task.checklist && task.checklist.length > 0")
|
||||
label.custom-control.custom-checkbox.checklist-item(
|
||||
v-for="item in task.checklist", :class="{'checklist-item-done': item.completed}",
|
||||
)
|
||||
input.custom-control-input(type="checkbox", :checked="item.completed", @change="toggleChecklistItem(item)")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description {{ item.text }}
|
||||
.icons.small-text.d-flex.align-items-center
|
||||
.d-flex.align-items-center(v-if="task.type === 'todo' && task.date", :class="{'due-overdue': isDueOverdue}")
|
||||
.svg-icon.calendar(v-html="icons.calendar")
|
||||
span {{dueIn}}
|
||||
.icons-right.d-flex.justify-content-end
|
||||
.d-flex.align-items-center(v-if="showStreak")
|
||||
.svg-icon.streak(v-html="icons.streak")
|
||||
span(v-if="task.type === 'daily'") {{task.streak}}
|
||||
span(v-if="task.type === 'habit'")
|
||||
span.m-0(v-if="task.up") +{{task.counterUp}}
|
||||
span.m-0(v-if="task.up && task.down") |
|
||||
span.m-0(v-if="task.down") -{{task.counterDown}}
|
||||
.d-flex.align-items-center(v-if="task.challenge && task.challenge.id")
|
||||
.svg-icon.challenge(v-html="icons.challenge")
|
||||
b-popover.tags-popover.no-span-margin(
|
||||
:triggers="['hover']",
|
||||
:placement="'bottom'",
|
||||
:popover-style="{'max-width': '1000px'}",
|
||||
.task
|
||||
approval-header(:task='task', v-if='this.task.group.id', :group='group')
|
||||
.d-flex(:class="{'task-not-scoreable': isUser !== true}")
|
||||
// Habits left side control
|
||||
.left-control.d-flex.align-items-center.justify-content-center(v-if="task.type === 'habit'", :class="controlClass.up")
|
||||
.task-control.habit-control(:class="controlClass.up + '-control-habit'", @click="isUser ? score('up') : null")
|
||||
.svg-icon.positive(v-html="icons.positive")
|
||||
// Dailies and todos left side control
|
||||
.left-control.d-flex.justify-content-center(v-if="task.type === 'daily' || task.type === 'todo'", :class="controlClass")
|
||||
.task-control.daily-todo-control(:class="controlClass + '-control-daily-todo'", @click="isUser ? score(task.completed ? 'down' : 'up') : null")
|
||||
.svg-icon.check(v-html="icons.check", :class="{'display-check-icon': task.completed}")
|
||||
// Task title, description and icons
|
||||
.task-content(:class="contentClass")
|
||||
.task-clickable-area(@click="edit($event, task)")
|
||||
h3.task-title(:class="{ 'has-notes': task.notes }", v-markdown="task.text")
|
||||
.task-notes.small-text(v-markdown="task.notes")
|
||||
.checklist(v-if="task.checklist && task.checklist.length > 0")
|
||||
label.custom-control.custom-checkbox.checklist-item(
|
||||
v-for="item in task.checklist", :class="{'checklist-item-done': item.completed}",
|
||||
)
|
||||
.d-flex.align-items-center(slot="content")
|
||||
.tags-popover-title(v-once) {{ `${$t('tags')}:` }}
|
||||
.tag-label(v-for="tag in getTagsFor(task)") {{tag}}
|
||||
.d-flex.align-items-center(v-if="task.tags && task.tags.length > 0")
|
||||
.svg-icon.tags(v-html="icons.tags")
|
||||
input.custom-control-input(type="checkbox", :checked="item.completed", @change="toggleChecklistItem(item)")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description {{ item.text }}
|
||||
.icons.small-text.d-flex.align-items-center
|
||||
.d-flex.align-items-center(v-if="task.type === 'todo' && task.date", :class="{'due-overdue': isDueOverdue}")
|
||||
.svg-icon.calendar(v-html="icons.calendar")
|
||||
span {{dueIn}}
|
||||
.icons-right.d-flex.justify-content-end
|
||||
.d-flex.align-items-center(v-if="showStreak")
|
||||
.svg-icon.streak(v-html="icons.streak")
|
||||
span(v-if="task.type === 'daily'") {{task.streak}}
|
||||
span(v-if="task.type === 'habit'")
|
||||
span.m-0(v-if="task.up") +{{task.counterUp}}
|
||||
span.m-0(v-if="task.up && task.down") |
|
||||
span.m-0(v-if="task.down") -{{task.counterDown}}
|
||||
.d-flex.align-items-center(v-if="task.challenge && task.challenge.id")
|
||||
.svg-icon.challenge(v-html="icons.challenge")
|
||||
b-popover.tags-popover.no-span-margin(
|
||||
:triggers="['hover']",
|
||||
:placement="'bottom'",
|
||||
:popover-style="{'max-width': '1000px'}",
|
||||
)
|
||||
.d-flex.align-items-center(slot="content")
|
||||
.tags-popover-title(v-once) {{ `${$t('tags')}:` }}
|
||||
.tag-label(v-for="tag in getTagsFor(task)") {{tag}}
|
||||
.d-flex.align-items-center(v-if="task.tags && task.tags.length > 0")
|
||||
.svg-icon.tags(v-html="icons.tags")
|
||||
|
||||
// Habits right side control
|
||||
.right-control.d-flex.align-items-center.justify-content-center(v-if="task.type === 'habit'", :class="controlClass.down")
|
||||
.task-control.habit-control(:class="controlClass.down + '-control-habit'", @click="isUser ? score('down') : null")
|
||||
.svg-icon.negative(v-html="icons.negative")
|
||||
// Rewards right side control
|
||||
.right-control.d-flex.align-items-center.justify-content-center.reward-control(v-if="task.type === 'reward'", :class="controlClass", @click="isUser ? score('down') : null")
|
||||
.svg-icon(v-html="icons.gold")
|
||||
.small-text {{task.value}}
|
||||
// Habits right side control
|
||||
.right-control.d-flex.align-items-center.justify-content-center(v-if="task.type === 'habit'", :class="controlClass.down")
|
||||
.task-control.habit-control(:class="controlClass.down + '-control-habit'", @click="isUser ? score('down') : null")
|
||||
.svg-icon.negative(v-html="icons.negative")
|
||||
// Rewards right side control
|
||||
.right-control.d-flex.align-items-center.justify-content-center.reward-control(v-if="task.type === 'reward'", :class="controlClass", @click="isUser ? score('down') : null")
|
||||
.svg-icon(v-html="icons.gold")
|
||||
.small-text {{task.value}}
|
||||
approval-footer(:task='task', v-if='this.task.group.id', :group='group')
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
.task {
|
||||
margin-bottom: 8px;
|
||||
box-shadow: 0 2px 2px 0 rgba($black, 0.16), 0 1px 4px 0 rgba($black, 0.12);
|
||||
background: transparent;
|
||||
border-radius: 2px;
|
||||
z-index: 9;
|
||||
position: relative;
|
||||
.task {
|
||||
margin-bottom: 8px;
|
||||
box-shadow: 0 2px 2px 0 rgba($black, 0.16), 0 1px 4px 0 rgba($black, 0.12);
|
||||
background: transparent;
|
||||
border-radius: 2px;
|
||||
z-index: 9;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 1px 8px 0 rgba($black, 0.12), 0 4px 4px 0 rgba($black, 0.16);
|
||||
&:hover {
|
||||
box-shadow: 0 1px 8px 0 rgba($black, 0.12), 0 4px 4px 0 rgba($black, 0.16);
|
||||
|
||||
.left-control, .right-control, .task-content {
|
||||
border-color: $purple-500;
|
||||
.left-control, .right-control, .task-content {
|
||||
border-color: $purple-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.task-title {
|
||||
padding-bottom: 8px;
|
||||
color: $gray-10;
|
||||
font-weight: normal;
|
||||
margin-bottom: 0px;
|
||||
.task-title {
|
||||
padding-bottom: 8px;
|
||||
color: $gray-10;
|
||||
font-weight: normal;
|
||||
margin-bottom: 0px;
|
||||
|
||||
&.has-notes {
|
||||
padding-bottom: 0px;
|
||||
&.has-notes {
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.task-notes {
|
||||
color: $gray-100;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.task-content {
|
||||
padding: 8px;
|
||||
flex-grow: 1;
|
||||
cursor: pointer;
|
||||
background: $white;
|
||||
border: 1px solid transparent;
|
||||
|
||||
&.no-right-border {
|
||||
border-right: none !important;
|
||||
.task-notes {
|
||||
color: $gray-100;
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.checklist {
|
||||
margin-bottom: 2px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.task-content {
|
||||
padding: 8px;
|
||||
flex-grow: 1;
|
||||
cursor: pointer;
|
||||
background: $white;
|
||||
border: 1px solid transparent;
|
||||
|
||||
.checklist-item {
|
||||
color: $gray-50;
|
||||
font-size: 14px;
|
||||
line-height: 1.43;
|
||||
margin-bottom: 10px;
|
||||
min-height: 0px;
|
||||
width: 100%;
|
||||
&.no-right-border {
|
||||
border-right: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
&-done {
|
||||
.checklist {
|
||||
margin-bottom: 2px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.checklist-item {
|
||||
color: $gray-50;
|
||||
font-size: 14px;
|
||||
line-height: 1.43;
|
||||
margin-bottom: 10px;
|
||||
min-height: 0px;
|
||||
width: 100%;
|
||||
|
||||
&-done {
|
||||
color: $gray-300;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.custom-control-indicator {
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.custom-control-description {
|
||||
margin-left: 6px;
|
||||
padding-top: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.icons {
|
||||
margin-top: 4px;
|
||||
color: $gray-300;
|
||||
text-decoration: line-through;
|
||||
font-style: normal;
|
||||
|
||||
&-right {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-control-indicator {
|
||||
.icons-right .svg-icon {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.icons span {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.no-span-margin span {
|
||||
margin-left: 0px !important;
|
||||
}
|
||||
|
||||
.svg-icon.streak {
|
||||
width: 11.6px;
|
||||
height: 7.1px;
|
||||
}
|
||||
|
||||
.tags.svg-icon, .calendar.svg-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.tags:hover {
|
||||
color: $purple-500;
|
||||
}
|
||||
|
||||
.due-overdue {
|
||||
color: $red-50;
|
||||
}
|
||||
|
||||
.calendar.svg-icon {
|
||||
margin-right: 2px;
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.custom-control-description {
|
||||
margin-left: 6px;
|
||||
padding-top: 0px;
|
||||
.challenge.svg-icon {
|
||||
width: 14px;
|
||||
height: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.icons {
|
||||
margin-top: 4px;
|
||||
color: $gray-300;
|
||||
font-style: normal;
|
||||
|
||||
&-right {
|
||||
flex-grow: 1;
|
||||
.check.svg-icon {
|
||||
width: 12.3px;
|
||||
height: 9.8px;
|
||||
margin: 9px 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.icons-right .svg-icon {
|
||||
margin-left: 8px;
|
||||
}
|
||||
.left-control, .right-control {
|
||||
width: 40px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.icons span {
|
||||
margin-left: 4px;
|
||||
}
|
||||
.left-control {
|
||||
border-top-left-radius: 2px;
|
||||
border-bottom-left-radius: 2px;
|
||||
min-height: 60px;
|
||||
border: 1px solid transparent;
|
||||
border-right: none;
|
||||
|
||||
.no-span-margin span {
|
||||
margin-left: 0px !important;
|
||||
}
|
||||
& + .task-content {
|
||||
border-left: none;
|
||||
}
|
||||
}
|
||||
|
||||
.svg-icon.streak {
|
||||
width: 11.6px;
|
||||
height: 7.1px;
|
||||
}
|
||||
|
||||
.tags.svg-icon, .calendar.svg-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.tags:hover {
|
||||
color: $purple-500;
|
||||
}
|
||||
|
||||
.due-overdue {
|
||||
color: $red-50;
|
||||
}
|
||||
|
||||
.calendar.svg-icon {
|
||||
margin-right: 2px;
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.challenge.svg-icon {
|
||||
width: 14px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.check.svg-icon {
|
||||
width: 12.3px;
|
||||
height: 9.8px;
|
||||
margin: 9px 8px;
|
||||
}
|
||||
|
||||
.left-control, .right-control {
|
||||
width: 40px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.left-control {
|
||||
border-top-left-radius: 2px;
|
||||
border-bottom-left-radius: 2px;
|
||||
min-height: 60px;
|
||||
border: 1px solid transparent;
|
||||
border-right: none;
|
||||
|
||||
& + .task-content {
|
||||
.right-control {
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
min-height: 56px;
|
||||
border: 1px solid transparent;
|
||||
border-left: none;
|
||||
}
|
||||
}
|
||||
|
||||
.right-control {
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
min-height: 56px;
|
||||
border: 1px solid transparent;
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.task-control, .reward-control {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.task-not-scoreable {
|
||||
.task-control, .reward-control {
|
||||
cursor: default !important;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.svg-icon.check {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
.task-not-scoreable {
|
||||
.task-control, .reward-control {
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
.daily-todo-control {
|
||||
margin-top: 16px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.reward-control {
|
||||
flex-direction: column;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 4px;
|
||||
|
||||
.svg-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
.svg-icon.check {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.small-text {
|
||||
margin-top: 4px;
|
||||
color: $yellow-10;
|
||||
.daily-todo-control {
|
||||
margin-top: 16px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.reward-control {
|
||||
flex-direction: column;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 4px;
|
||||
|
||||
.svg-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.small-text {
|
||||
margin-top: 4px;
|
||||
color: $yellow-10;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss"> // not working as scoped css
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
<style lang="scss">
|
||||
// not working as scoped css
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
.tags-popover {
|
||||
// TODO fix padding, see https://github.com/bootstrap-vue/bootstrap-vue/issues/559#issuecomment-311150335
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tags-popover {
|
||||
// TODO fix padding, see https://github.com/bootstrap-vue/bootstrap-vue/issues/559#issuecomment-311150335
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tags-popover-title {
|
||||
margin-right: 4px;
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
.tags-popover-title {
|
||||
margin-right: 4px;
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.tag-label {
|
||||
display: block;
|
||||
float: left;
|
||||
margin-left: 4px;
|
||||
border-radius: 100px;
|
||||
background-color: $gray-50;
|
||||
padding: 4px 10px;
|
||||
color: $gray-300;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tag-label {
|
||||
display: block;
|
||||
float: left;
|
||||
margin-left: 4px;
|
||||
border-radius: 100px;
|
||||
background-color: $gray-50;
|
||||
padding: 4px 10px;
|
||||
color: $gray-300;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
@@ -292,16 +296,20 @@ import checkIcon from 'assets/svg/check.svg';
|
||||
import bPopover from 'bootstrap-vue/lib/components/popover';
|
||||
import markdownDirective from 'client/directives/markdown';
|
||||
import notifications from 'client/mixins/notifications';
|
||||
import approvalHeader from './approvalHeader';
|
||||
import approvalFooter from './approvalFooter';
|
||||
|
||||
export default {
|
||||
mixins: [notifications],
|
||||
components: {
|
||||
bPopover,
|
||||
approvalFooter,
|
||||
approvalHeader,
|
||||
},
|
||||
directives: {
|
||||
markdown: markdownDirective,
|
||||
},
|
||||
props: ['task', 'isUser'],
|
||||
props: ['task', 'isUser', 'group'], // @TODO: maybe we should store the group on state?
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
|
||||
@@ -1,126 +1,149 @@
|
||||
<template lang="pug">
|
||||
form(
|
||||
v-if="task",
|
||||
@submit.stop.prevent="submit()",
|
||||
)
|
||||
b-modal#task-modal(
|
||||
size="sm",
|
||||
@hidden="cancel()",
|
||||
form(
|
||||
v-if="task",
|
||||
@submit.stop.prevent="submit()",
|
||||
)
|
||||
.task-modal-header(
|
||||
slot="modal-header",
|
||||
:class="[cssClass]",
|
||||
b-modal#task-modal(
|
||||
size="sm",
|
||||
@hidden="cancel()",
|
||||
)
|
||||
h1 {{ title }}
|
||||
.form-group
|
||||
label(v-once) {{ `${$t('title')}*` }}
|
||||
input.form-control(type='text', :class="[`${cssClass}-modal-input`]", required, v-model="task.text")
|
||||
.form-group
|
||||
label(v-once) {{ $t('notes') }}
|
||||
textarea.form-control(:class="[`${cssClass}-modal-input`]", v-model="task.notes", rows="3")
|
||||
.task-modal-content
|
||||
.option(v-if="task.type === 'reward'")
|
||||
label(v-once) {{ $t('cost') }}
|
||||
input(type="number", v-model="task.value", required, min="0")
|
||||
.option(v-if="['daily', 'todo'].indexOf(task.type) > -1")
|
||||
label(v-once) {{ $t('checklist') }}
|
||||
br
|
||||
.inline-edit-input-group.checklist-group.input-group(v-for="(item, $index) in task.checklist")
|
||||
input.inline-edit-input.checklist-item.form-control(type="text", :value="item.text")
|
||||
span.input-group-btn(@click="removeChecklistItem($index)")
|
||||
.svg-icon.destroy-icon(v-html="icons.destroy")
|
||||
input.inline-edit-input.checklist-item.form-control(type="text", :placeholder="$t('newChecklistItem')", @keydown.enter="addChecklistItem($event)", v-model="newChecklistItem")
|
||||
.d-flex.justify-content-center(v-if="task.type === 'habit'")
|
||||
.option-item(:class="optionClass(task.up === true)", @click="task.up = !task.up")
|
||||
.option-item-box
|
||||
.task-control.habit-control(:class="controlClass.up + '-control-habit'")
|
||||
.svg-icon.positive(v-html="icons.positive")
|
||||
.option-item-label(v-once) {{ $t('positive') }}
|
||||
.option-item(:class="optionClass(task.down === true)", @click="task.down = !task.down")
|
||||
.option-item-box
|
||||
.task-control.habit-control(:class="controlClass.down + '-control-habit'")
|
||||
.svg-icon.negative(v-html="icons.negative")
|
||||
.option-item-label(v-once) {{ $t('negative') }}
|
||||
template(v-if="task.type !== 'reward'")
|
||||
label(v-once)
|
||||
span.float-left {{ $t('difficulty') }}
|
||||
.svg-icon.info-icon(v-html="icons.information")
|
||||
.d-flex.justify-content-center
|
||||
.option-item(:class="optionClass(task.priority === 0.1)", @click="task.priority = 0.1")
|
||||
.task-modal-header(
|
||||
slot="modal-header",
|
||||
:class="[cssClass]",
|
||||
)
|
||||
h1 {{ title }}
|
||||
.form-group
|
||||
label(v-once) {{ `${$t('title')}*` }}
|
||||
input.form-control(type='text', :class="[`${cssClass}-modal-input`]", required, v-model="task.text")
|
||||
.form-group
|
||||
label(v-once) {{ $t('notes') }}
|
||||
textarea.form-control(:class="[`${cssClass}-modal-input`]", v-model="task.notes", rows="3")
|
||||
.task-modal-content
|
||||
.option(v-if="task.type === 'reward'")
|
||||
label(v-once) {{ $t('cost') }}
|
||||
input(type="number", v-model="task.value", required, min="0")
|
||||
.option(v-if="['daily', 'todo'].indexOf(task.type) > -1")
|
||||
label(v-once) {{ $t('checklist') }}
|
||||
br
|
||||
.inline-edit-input-group.checklist-group.input-group(v-for="(item, $index) in task.checklist")
|
||||
input.inline-edit-input.checklist-item.form-control(type="text", :value="item.text")
|
||||
span.input-group-btn(@click="removeChecklistItem($index)")
|
||||
.svg-icon.destroy-icon(v-html="icons.destroy")
|
||||
input.inline-edit-input.checklist-item.form-control(type="text", :placeholder="$t('newChecklistItem')", @keydown.enter="addChecklistItem($event)", v-model="newChecklistItem")
|
||||
.d-flex.justify-content-center(v-if="task.type === 'habit'")
|
||||
.option-item(:class="optionClass(task.up === true)", @click="task.up = !task.up")
|
||||
.option-item-box
|
||||
.svg-icon.difficulty-trivial-icon(v-html="icons.difficultyTrivial")
|
||||
.option-item-label(v-once) {{ $t('trivial') }}
|
||||
.option-item(:class="optionClass(task.priority === 1)", @click="task.priority = 1")
|
||||
.task-control.habit-control(:class="controlClass.up + '-control-habit'")
|
||||
.svg-icon.positive(v-html="icons.positive")
|
||||
.option-item-label(v-once) {{ $t('positive') }}
|
||||
.option-item(:class="optionClass(task.down === true)", @click="task.down = !task.down")
|
||||
.option-item-box
|
||||
.svg-icon.difficulty-normal-icon(v-html="icons.difficultyNormal")
|
||||
.option-item-label(v-once) {{ $t('easy') }}
|
||||
.option-item(:class="optionClass(task.priority === 1.5)", @click="task.priority = 1.5")
|
||||
.option-item-box
|
||||
.svg-icon.difficulty-medium-icon(v-html="icons.difficultyMedium")
|
||||
.option-item-label(v-once) {{ $t('medium') }}
|
||||
.option-item(:class="optionClass(task.priority === 2)", @click="task.priority = 2")
|
||||
.option-item-box
|
||||
.svg-icon.difficulty-hard-icon(v-html="icons.difficultyHard")
|
||||
.option-item-label(v-once) {{ $t('hard') }}
|
||||
.option(v-if="task.type === 'todo'")
|
||||
label(v-once) {{ $t('dueDate') }}
|
||||
datepicker(v-model="task.date")
|
||||
.option(v-if="task.type === 'daily'")
|
||||
label(v-once) {{ $t('startDate') }}
|
||||
datepicker(v-model="task.startDate")
|
||||
.option(v-if="task.type === 'daily'")
|
||||
label(v-once) {{ $t('repeats') }}
|
||||
b-dropdown(:text="$t(task.frequency)")
|
||||
b-dropdown-item(v-for="frequency in ['daily', 'weekly', 'monthly', 'yearly']", :key="frequency", @click="task.frequency = frequency", :class="{active: task.frequency === frequency}")
|
||||
| {{ $t(frequency) }}
|
||||
label(v-once) {{ $t('repeatEvery') }}
|
||||
input.form-control(type="number", v-model="task.everyX", min="0", required)
|
||||
| {{ repeatSuffix }}
|
||||
br
|
||||
template(v-if="task.frequency === 'weekly'")
|
||||
.form-check-inline.weekday-check(
|
||||
v-for="(day, dayNumber) in ['su','m','t','w','th','f','s']",
|
||||
:key="dayNumber",
|
||||
)
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox", v-model="task.repeat[day]")
|
||||
.task-control.habit-control(:class="controlClass.down + '-control-habit'")
|
||||
.svg-icon.negative(v-html="icons.negative")
|
||||
.option-item-label(v-once) {{ $t('negative') }}
|
||||
template(v-if="task.type !== 'reward'")
|
||||
label(v-once)
|
||||
span.float-left {{ $t('difficulty') }}
|
||||
.svg-icon.info-icon(v-html="icons.information")
|
||||
.d-flex.justify-content-center
|
||||
.option-item(:class="optionClass(task.priority === 0.1)", @click="task.priority = 0.1")
|
||||
.option-item-box
|
||||
.svg-icon.difficulty-trivial-icon(v-html="icons.difficultyTrivial")
|
||||
.option-item-label(v-once) {{ $t('trivial') }}
|
||||
.option-item(:class="optionClass(task.priority === 1)", @click="task.priority = 1")
|
||||
.option-item-box
|
||||
.svg-icon.difficulty-normal-icon(v-html="icons.difficultyNormal")
|
||||
.option-item-label(v-once) {{ $t('easy') }}
|
||||
.option-item(:class="optionClass(task.priority === 1.5)", @click="task.priority = 1.5")
|
||||
.option-item-box
|
||||
.svg-icon.difficulty-medium-icon(v-html="icons.difficultyMedium")
|
||||
.option-item-label(v-once) {{ $t('medium') }}
|
||||
.option-item(:class="optionClass(task.priority === 2)", @click="task.priority = 2")
|
||||
.option-item-box
|
||||
.svg-icon.difficulty-hard-icon(v-html="icons.difficultyHard")
|
||||
.option-item-label(v-once) {{ $t('hard') }}
|
||||
.option(v-if="task.type === 'todo'")
|
||||
label(v-once) {{ $t('dueDate') }}
|
||||
datepicker(v-model="task.date")
|
||||
.option(v-if="task.type === 'daily'")
|
||||
label(v-once) {{ $t('startDate') }}
|
||||
datepicker(v-model="task.startDate")
|
||||
.option(v-if="task.type === 'daily'")
|
||||
label(v-once) {{ $t('repeats') }}
|
||||
b-dropdown(:text="$t(task.frequency)")
|
||||
b-dropdown-item(v-for="frequency in ['daily', 'weekly', 'monthly', 'yearly']", :key="frequency", @click="task.frequency = frequency", :class="{active: task.frequency === frequency}")
|
||||
| {{ $t(frequency) }}
|
||||
label(v-once) {{ $t('repeatEvery') }}
|
||||
input.form-control(type="number", v-model="task.everyX", min="0", required)
|
||||
| {{ repeatSuffix }}
|
||||
br
|
||||
template(v-if="task.frequency === 'weekly'")
|
||||
.form-check-inline.weekday-check(
|
||||
v-for="(day, dayNumber) in ['su','m','t','w','th','f','s']",
|
||||
:key="dayNumber",
|
||||
)
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox", v-model="task.repeat[day]")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(v-once) {{ weekdaysMin(dayNumber) }}
|
||||
template(v-if="task.frequency === 'monthly'")
|
||||
label.custom-control.custom-radio
|
||||
input.custom-control-input(type='radio', v-model="repeatsOn", value="dayOfMonth")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(v-once) {{ weekdaysMin(dayNumber) }}
|
||||
template(v-if="task.frequency === 'monthly'")
|
||||
label.custom-control.custom-radio
|
||||
input.custom-control-input(type='radio', v-model="repeatsOn", value="dayOfMonth")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description {{ $t('dayOfMonth') }}
|
||||
label.custom-control.custom-radio
|
||||
input.custom-control-input(type='radio', v-model="repeatsOn", value="dayOfWeek")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description {{ $t('dayOfWeek') }}
|
||||
|
||||
.option
|
||||
label(v-once) {{ $t('tags') }}
|
||||
.category-wrap(@click="showTagsSelect = !showTagsSelect")
|
||||
span.category-select(v-if='task.tags && task.tags.length === 0') {{$t('none')}}
|
||||
span.category-select(v-else) {{getTagsFor(task)[0]}}
|
||||
.category-box(v-if="showTagsSelect")
|
||||
.form-check(
|
||||
v-for="tag in user.tags",
|
||||
:key="tag.id",
|
||||
)
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox", :value="tag.id", v-model="task.tags")
|
||||
span.custom-control-description {{ $t('dayOfMonth') }}
|
||||
label.custom-control.custom-radio
|
||||
input.custom-control-input(type='radio', v-model="repeatsOn", value="dayOfWeek")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(v-once) {{ tag.name }}
|
||||
button.btn.btn-primary(@click="showTagsSelect = !showTagsSelect") {{$t('close')}}
|
||||
.option(v-if="task.type === 'habit'")
|
||||
label(v-once) {{ $t('resetStreak') }}
|
||||
b-dropdown(:text="$t(task.frequency)")
|
||||
b-dropdown-item(v-for="frequency in ['daily', 'weekly', 'monthly']", :key="frequency", @click="task.frequency = frequency", :class="{active: task.frequency === frequency}")
|
||||
| {{ $t(frequency) }}
|
||||
span.custom-control-description {{ $t('dayOfWeek') }}
|
||||
|
||||
.task-modal-footer(slot="modal-footer")
|
||||
button.btn.btn-primary(type="submit", v-once) {{ $t('save') }}
|
||||
span.cancel-task-btn(v-once, v-if="purpose === 'create'", @click="cancel()") {{ $t('cancel') }}
|
||||
span.delete-task-btn(v-once, v-else, @click="destroy()") {{ $t('delete') }}
|
||||
.option
|
||||
label(v-once) {{ $t('tags') }}
|
||||
.category-wrap(@click="showTagsSelect = !showTagsSelect")
|
||||
span.category-select(v-if='task.tags && task.tags.length === 0') {{$t('none')}}
|
||||
span.category-select(v-else) {{getTagsFor(task)[0]}}
|
||||
.category-box(v-if="showTagsSelect")
|
||||
.form-check(
|
||||
v-for="tag in user.tags",
|
||||
:key="tag.id",
|
||||
)
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox", :value="tag.id", v-model="task.tags")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(v-once) {{ tag.name }}
|
||||
button.btn.btn-primary(@click="showTagsSelect = !showTagsSelect") {{$t('close')}}
|
||||
.option(v-if="task.type === 'habit'")
|
||||
label(v-once) {{ $t('resetStreak') }}
|
||||
b-dropdown(:text="$t(task.frequency)")
|
||||
b-dropdown-item(v-for="frequency in ['daily', 'weekly', 'monthly']", :key="frequency", @click="task.frequency = frequency", :class="{active: task.frequency === frequency}")
|
||||
| {{ $t(frequency) }}
|
||||
|
||||
.option.group-options(v-if='groupId')
|
||||
label(v-once) Assigned To
|
||||
.category-wrap(@click="showAssignedSelect = !showAssignedSelect")
|
||||
span.category-select(v-if='assignedMembers && assignedMembers.length === 0') {{$t('none')}}
|
||||
span.category-select(v-else)
|
||||
span(v-for='memberId in assignedMembers') {{memberNamesById[memberId]}}
|
||||
.category-box(v-if="showAssignedSelect")
|
||||
.form-check(
|
||||
v-for="member in members",
|
||||
:key="member._id",
|
||||
)
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox", :value="member._id", v-model="assignedMembers", @change='toggleAssignment(member._id)')
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(v-once) {{ member.profile.name }}
|
||||
button.btn.btn-primary(@click="showAssignedSelect = !showAssignedSelect") {{$t('close')}}
|
||||
|
||||
.option.group-options(v-if='groupId')
|
||||
label(v-once) Needs Approval
|
||||
toggle-switch(:label='""',
|
||||
:checked="requiresApproval",
|
||||
@change="updateRequiresApproval")
|
||||
|
||||
.task-modal-footer(slot="modal-footer")
|
||||
button.btn.btn-primary(type="submit", v-once) {{ $t('save') }}
|
||||
span.cancel-task-btn(v-once, v-if="purpose === 'create'", @click="cancel()") {{ $t('cancel') }}
|
||||
span.delete-task-btn(v-once, v-else, @click="destroy()") {{ $t('delete') }}
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@@ -325,6 +348,7 @@ import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
import { mapGetters, mapActions, mapState } from 'client/libs/store';
|
||||
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
|
||||
import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item';
|
||||
import toggleSwitch from 'client/components/ui/toggleSwitch';
|
||||
import Datepicker from 'vuejs-datepicker';
|
||||
import moment from 'moment';
|
||||
import uuid from 'uuid';
|
||||
@@ -344,11 +368,13 @@ export default {
|
||||
bDropdown,
|
||||
bDropdownItem,
|
||||
Datepicker,
|
||||
toggleSwitch,
|
||||
},
|
||||
props: ['task', 'purpose', 'challengeId'], // purpose is either create or edit, task is the task created or edited
|
||||
props: ['task', 'purpose', 'challengeId', 'groupId'], // purpose is either create or edit, task is the task created or edited
|
||||
data () {
|
||||
return {
|
||||
showTagsSelect: false,
|
||||
showAssignedSelect: false,
|
||||
newChecklistItem: null,
|
||||
icons: Object.freeze({
|
||||
information: informationIcon,
|
||||
@@ -360,8 +386,31 @@ export default {
|
||||
positive: positiveIcon,
|
||||
destroy: deleteIcon,
|
||||
}),
|
||||
requiresApproval: false, // We can't set task.group fields so we use this field to toggle
|
||||
members: [],
|
||||
memberNamesById: {},
|
||||
assignedMembers: [],
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
async task () {
|
||||
if (this.groupId && this.task.group && this.task.group.approval.required) {
|
||||
this.requiresApproval = true;
|
||||
}
|
||||
|
||||
if (this.groupId) {
|
||||
let members = await this.$store.dispatch('members:getGroupMembers', {
|
||||
groupId: this.groupId,
|
||||
includeAllPublicFields: true,
|
||||
});
|
||||
this.members = members;
|
||||
this.members.forEach(member => {
|
||||
this.memberNamesById[member._id] = member.profile.name;
|
||||
});
|
||||
this.assignedMembers = this.task.group.assignedUsers;
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
getTaskClasses: 'tasks:getTaskClasses',
|
||||
@@ -457,10 +506,21 @@ export default {
|
||||
tasks: [this.task],
|
||||
});
|
||||
this.$emit('taskCreated', this.task);
|
||||
} else if (this.groupId) {
|
||||
this.$store.dispatch('tasks:createGroupTasks', {
|
||||
groupId: this.groupId,
|
||||
tasks: [this.task],
|
||||
});
|
||||
this.$emit('taskCreated', this.task);
|
||||
} else {
|
||||
this.createTask(this.task);
|
||||
}
|
||||
} else {
|
||||
if (this.groupId) {
|
||||
this.task.group.assignedUsers = this.assignedMembers;
|
||||
this.task.requiresApproval = this.requiresApproval;
|
||||
}
|
||||
|
||||
this.saveTask(this.task);
|
||||
this.$emit('taskEdited', this.task);
|
||||
}
|
||||
@@ -474,6 +534,24 @@ export default {
|
||||
this.showTagsSelect = false;
|
||||
this.$emit('cancel');
|
||||
},
|
||||
updateRequiresApproval (newValue) {
|
||||
let truthy = true;
|
||||
if (!newValue) truthy = false; // This return undefined instad of false
|
||||
this.requiresApproval = truthy;
|
||||
},
|
||||
async toggleAssignment (memberId) {
|
||||
if (this.assignedMembers.indexOf(memberId) === -1) {
|
||||
await this.$store.dispatch('tasks:unassignTask', {
|
||||
taskId: this.task._id,
|
||||
userId: this.user._id,
|
||||
});
|
||||
return;
|
||||
}
|
||||
await this.$store.dispatch('tasks:assignTask', {
|
||||
taskId: this.task._id,
|
||||
userId: this.user._id,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user