diff --git a/website/client/components/group-plans/billing.vue b/website/client/components/group-plans/billing.vue index 3a07f8a673..f9b886d54b 100644 --- a/website/client/components/group-plans/billing.vue +++ b/website/client/components/group-plans/billing.vue @@ -24,6 +24,8 @@ li {{ $t('gemCapExtra') }} {{group.purchased.plan.consecutive.gemCapExtra}} li {{ $t('mysticHourglasses') }} {{group.purchased.plan.consecutive.trinkets}} .col-12.col-md-6.offset-md-3 + button.btn.btn-success(class='btn-success', v-if='group.purchased.plan.dateTerminated', @click='upgradeGroup()') + | {{ $t('upgrade') }} .btn.btn-primary(v-if='!group.purchased.plan.dateTerminated && group.purchased.plan.paymentMethod === "Stripe"', @click='showStripeEdit({groupId: group.id})') {{ $t('subUpdateCard') }} .btn.btn-sm.btn-danger(v-if='!group.purchased.plan.dateTerminated', @@ -69,6 +71,10 @@ export default { let group = await this.$store.dispatch('guilds:getGroup', {groupId: this.groupId}); this.group = Object.assign({}, group); }, + upgradeGroup () { + this.$store.state.upgradingGroup = this.group; + this.$router.push('/group-plans'); + }, }, }; diff --git a/website/client/components/group-plans/taskInformation.vue b/website/client/components/group-plans/taskInformation.vue index 6aac845515..d71374b774 100644 --- a/website/client/components/group-plans/taskInformation.vue +++ b/website/client/components/group-plans/taskInformation.vue @@ -70,6 +70,7 @@ :groupId="groupId", v-on:taskCreated='taskCreated', v-on:taskEdited='taskEdited', + v-on:taskDestroyed='taskDestroyed' ) .row task-column.col-12.col-sm-6.col-3( @@ -380,6 +381,7 @@ export default { }); }, taskCreated (task) { + task.group.id = this.group._id; this.tasksByType[task.type].push(task); }, taskEdited (task) { @@ -388,6 +390,12 @@ export default { }); this.tasksByType[task.type].splice(index, 1, task); }, + taskDestroyed (task) { + let index = findIndex(this.tasksByType[task.type], (taskItem) => { + return taskItem._id === task._id; + }); + this.tasksByType[task.type].splice(index, 1); + }, cancelTaskModal () { this.editingTask = null; this.creatingTask = null; diff --git a/website/client/components/tasks/approvalFooter.vue b/website/client/components/tasks/approvalFooter.vue index 62e1a2e2d2..a654bb90bd 100644 --- a/website/client/components/tasks/approvalFooter.vue +++ b/website/client/components/tasks/approvalFooter.vue @@ -76,24 +76,36 @@ export default { }, }, methods: { - claim () { + async claim () { if (!confirm('Are you sure you want to claim this task?')) return; + + let taskId = this.task._id; + // If we are on the user task + if (this.task.userId) { + taskId = this.task.group.taskId; + } + this.$store.dispatch('tasks:assignTask', { - taskId: this.task._id, + taskId, userId: this.user._id, }); this.task.group.assignedUsers.push(this.user._id); - // @TODO: Reload user tasks? }, - unassign () { + async unassign () { if (!confirm('Are you sure you want to unclaim this task?')) return; + + let taskId = this.task._id; + // If we are on the user task + if (this.task.userId) { + taskId = this.task.group.taskId; + } + this.$store.dispatch('tasks:unassignTask', { - taskId: this.task._id, + taskId, 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; diff --git a/website/client/components/tasks/column.vue b/website/client/components/tasks/column.vue index e0ef503daf..bac00c97db 100644 --- a/website/client/components/tasks/column.vue +++ b/website/client/components/tasks/column.vue @@ -35,9 +35,9 @@ h3(v-once) {{$t('theseAreYourTasks', {taskType: $t(types[type].label)})}} .small-text {{$t(`${type}sDesc`)}} .sortable-tasks( - ref="tasksList", - v-sortable='activeFilters[type].label !== "scheduled"', - @onsort='sorted', + ref="tasksList", + v-sortable='activeFilters[type].label !== "scheduled"', + @onsort='sorted', data-sortableId ) task( @@ -441,20 +441,32 @@ export default { }), async sorted (data) { const filteredList = this.taskList; - const taskIdToMove = filteredList[data.oldIndex]._id; + const taskToMove = filteredList[data.oldIndex]; + const taskIdToMove = taskToMove._id; + let originTasks = this.tasks[`${this.type}s`]; + if (this.taskListOverride) originTasks = this.taskListOverride; // Server const taskIdToReplace = filteredList[data.newIndex]; - const newIndexOnServer = this.tasks[`${this.type}s`].findIndex(taskId => taskId === taskIdToReplace); - let newOrder = await this.$store.dispatch('tasks:move', { - taskId: taskIdToMove, - position: newIndexOnServer, - }); - this.user.tasksOrder[`${this.type}s`] = newOrder; + const newIndexOnServer = originTasks.findIndex(taskId => taskId === taskIdToReplace); + + let newOrder; + if (taskToMove.group.id) { + newOrder = await this.$store.dispatch('tasks:moveGroupTask', { + taskId: taskIdToMove, + position: newIndexOnServer, + }); + } else { + newOrder = await this.$store.dispatch('tasks:move', { + taskId: taskIdToMove, + position: newIndexOnServer, + }); + } + if (!this.taskListOverride) this.user.tasksOrder[`${this.type}s`] = newOrder; // Client - const deleted = this.tasks[`${this.type}s`].splice(data.oldIndex, 1); - this.tasks[`${this.type}s`].splice(data.newIndex, 0, deleted[0]); + const deleted = originTasks.splice(data.oldIndex, 1); + originTasks.splice(data.newIndex, 0, deleted[0]); }, async moveTo (task, where) { // where is 'top' or 'bottom' const taskIdToMove = task._id; diff --git a/website/client/components/tasks/taskModal.vue b/website/client/components/tasks/taskModal.vue index 9cc58c07fd..ab4683d924 100644 --- a/website/client/components/tasks/taskModal.vue +++ b/website/client/components/tasks/taskModal.vue @@ -13,6 +13,7 @@ type="text", :class="[`${cssClass}-modal-input`]", required, v-model="task.text", autofocus, spellcheck="true", + :disabled="groupAccessRequiredAndOnPersonalPage" ) .form-group label(v-once) {{ $t('notes') }} @@ -596,6 +597,10 @@ export default { user: 'user.data', dayMapping: 'constants.DAY_MAPPING', }), + groupAccessRequiredAndOnPersonalPage () { + if (!this.groupId && this.task.group.id) return true; + return false; + }, checklistEnabled () { return ['daily', 'todo'].indexOf(this.task.type) > -1 && !this.isOriginalChallengeTask; }, diff --git a/website/client/store/actions/tasks.js b/website/client/store/actions/tasks.js index fa8a3567aa..ed3eb41c63 100644 --- a/website/client/store/actions/tasks.js +++ b/website/client/store/actions/tasks.js @@ -218,3 +218,8 @@ export async function move (store, payload) { let response = await axios.post(`/api/v3/tasks/${payload.taskId}/move/to/${payload.position}`); return response.data.data; } + +export async function moveGroupTask (store, payload) { + let response = await axios.post(`/api/v3/group-tasks/${payload.taskId}/move/to/${payload.position}`); + return response.data.data; +} diff --git a/website/server/controllers/api-v3/groups.js b/website/server/controllers/api-v3/groups.js index 4de8c39afb..ec7c8e549e 100644 --- a/website/server/controllers/api-v3/groups.js +++ b/website/server/controllers/api-v3/groups.js @@ -567,13 +567,13 @@ api.joinGroup = { if (group.memberCount === 0) group.leader = user._id; // If new user is only member -> set as leader - group.memberCount += 1; - if (group.hasNotCancelled()) { await payments.addSubToGroupUser(user, group); await group.updateGroupPlan(); } + group.memberCount += 1; + let promises = [group.save(), user.save()]; if (inviter) {