mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 06:37:23 +01:00
WIP(teams): more partial fixing
This commit is contained in:
@@ -9,11 +9,6 @@
|
|||||||
]"
|
]"
|
||||||
@click="castEnd($event, task)"
|
@click="castEnd($event, task)"
|
||||||
>
|
>
|
||||||
<approval-header
|
|
||||||
v-if="task.group.id"
|
|
||||||
:task="task"
|
|
||||||
:group="group"
|
|
||||||
/>
|
|
||||||
<div
|
<div
|
||||||
class="d-flex"
|
class="d-flex"
|
||||||
:class="{'task-not-scoreable': showTaskLockIcon }"
|
:class="{'task-not-scoreable': showTaskLockIcon }"
|
||||||
@@ -894,14 +889,12 @@ import lockIcon from '@/assets/svg/lock.svg';
|
|||||||
import menuIcon from '@/assets/svg/menu.svg';
|
import menuIcon from '@/assets/svg/menu.svg';
|
||||||
import markdownDirective from '@/directives/markdown';
|
import markdownDirective from '@/directives/markdown';
|
||||||
import scoreTask from '@/mixins/scoreTask';
|
import scoreTask from '@/mixins/scoreTask';
|
||||||
import approvalHeader from './approvalHeader';
|
|
||||||
import approvalFooter from './approvalFooter';
|
import approvalFooter from './approvalFooter';
|
||||||
import MenuDropdown from '../ui/customMenuDropdown';
|
import MenuDropdown from '../ui/customMenuDropdown';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
approvalFooter,
|
approvalFooter,
|
||||||
approvalHeader,
|
|
||||||
MenuDropdown,
|
MenuDropdown,
|
||||||
},
|
},
|
||||||
directives: {
|
directives: {
|
||||||
@@ -1055,6 +1048,7 @@ export default {
|
|||||||
if (this.isGroupTask) {
|
if (this.isGroupTask) {
|
||||||
if (this.isOpenTask) return false;
|
if (this.isOpenTask) return false;
|
||||||
if (this.task.group.assignedUsers.indexOf(this.user._id) !== -1) return false;
|
if (this.task.group.assignedUsers.indexOf(this.user._id) !== -1) return false;
|
||||||
|
if (this.teamManagerAccess && this.task.completed) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -447,13 +447,6 @@ export async function cron (options = {}) {
|
|||||||
task.checklist.forEach(i => { i.completed = false; });
|
task.checklist.forEach(i => { i.completed = false; });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task.group && task.group.approval && task.group.approval.approved) {
|
|
||||||
task.group.approval.approved = false;
|
|
||||||
task.group.approval.dateApproved = null;
|
|
||||||
task.group.approval.requested = false;
|
|
||||||
task.group.approval.requestedDate = null;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
resetHabitCounters(user, tasksByType, now, daysMissed);
|
resetHabitCounters(user, tasksByType, now, daysMissed);
|
||||||
@@ -464,12 +457,6 @@ export async function cron (options = {}) {
|
|||||||
if (task.up === false || task.down === false) {
|
if (task.up === false || task.down === false) {
|
||||||
task.value = Math.abs(task.value) < 0.1 ? 0 : task.value /= 2;
|
task.value = Math.abs(task.value) < 0.1 ? 0 : task.value /= 2;
|
||||||
}
|
}
|
||||||
if (task.group && task.group.approval && task.group.approval.approved) {
|
|
||||||
task.group.approval.approved = false;
|
|
||||||
task.group.approval.dateApproved = null;
|
|
||||||
task.group.approval.requested = false;
|
|
||||||
task.group.approval.requestedDate = null;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Finished tallying
|
// Finished tallying
|
||||||
|
|||||||
@@ -6,11 +6,6 @@ const SHARED_COMPLETION = {
|
|||||||
every: 'allAssignedCompletion',
|
every: 'allAssignedCompletion',
|
||||||
};
|
};
|
||||||
|
|
||||||
async function _completeMasterTask (masterTask) {
|
|
||||||
masterTask.completed = true;
|
|
||||||
await masterTask.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function _deleteUnfinishedTasks (groupMemberTask) {
|
async function _deleteUnfinishedTasks (groupMemberTask) {
|
||||||
await Tasks.Task.deleteMany({
|
await Tasks.Task.deleteMany({
|
||||||
'group.taskId': groupMemberTask.group.taskId,
|
'group.taskId': groupMemberTask.group.taskId,
|
||||||
@@ -21,33 +16,11 @@ async function _deleteUnfinishedTasks (groupMemberTask) {
|
|||||||
}).exec();
|
}).exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function _evaluateAllAssignedCompletion (masterTask) {
|
|
||||||
let completions;
|
|
||||||
if (masterTask.group.approval && masterTask.group.approval.required) {
|
|
||||||
completions = await Tasks.Task.countDocuments({
|
|
||||||
'group.taskId': masterTask._id,
|
|
||||||
'group.approval.approved': true,
|
|
||||||
}).exec();
|
|
||||||
} else {
|
|
||||||
completions = await Tasks.Task.countDocuments({
|
|
||||||
'group.taskId': masterTask._id,
|
|
||||||
completed: true,
|
|
||||||
}).exec();
|
|
||||||
}
|
|
||||||
if (completions >= masterTask.group.assignedUsers.length) {
|
|
||||||
await _completeMasterTask(masterTask);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSharedCompletion (masterTask, groupMemberTask) {
|
async function handleSharedCompletion (masterTask, groupMemberTask) {
|
||||||
if (masterTask.type !== 'todo') return;
|
if (masterTask.type === 'reward') return;
|
||||||
|
if (masterTask.type === 'todo') await _deleteUnfinishedTasks(groupMemberTask);
|
||||||
if (masterTask.group.sharedCompletion === SHARED_COMPLETION.single) {
|
masterTask.completed = groupMemberTask.completed;
|
||||||
await _deleteUnfinishedTasks(groupMemberTask);
|
await masterTask.save();
|
||||||
await _completeMasterTask(masterTask);
|
|
||||||
} else if (masterTask.group.sharedCompletion === SHARED_COMPLETION.every) {
|
|
||||||
await _evaluateAllAssignedCompletion(masterTask);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
} from './utils';
|
} from './utils';
|
||||||
import { model as Challenge } from '../../models/challenge';
|
import { model as Challenge } from '../../models/challenge';
|
||||||
import { model as Group } from '../../models/group';
|
import { model as Group } from '../../models/group';
|
||||||
import { model as User } from '../../models/user';
|
|
||||||
import * as Tasks from '../../models/task';
|
import * as Tasks from '../../models/task';
|
||||||
import apiError from '../apiError';
|
import apiError from '../apiError';
|
||||||
import {
|
import {
|
||||||
@@ -336,69 +335,18 @@ async function scoreTask (user, task, direction, req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task.group.approval.required && !task.group.approval.approved) {
|
let localTask;
|
||||||
const fields = requiredGroupFields.concat(' managers');
|
|
||||||
const group = await Group.getGroup({ user, groupId: task.group.id, fields });
|
|
||||||
|
|
||||||
const managerIds = Object.keys(group.managers);
|
if (task.group.id && !task.userId && task.group.assignedUsers.length > 0) {
|
||||||
managerIds.push(group.leader);
|
// Task is being scored from team board, and a user copy should exist
|
||||||
|
if (!task.group.assignedUsers.includes(user._id)) {
|
||||||
if (managerIds.indexOf(user._id) !== -1) {
|
throw new BadRequest('Task has not been assigned to this user.');
|
||||||
task.group.approval.approved = true;
|
|
||||||
task.group.approval.requested = true;
|
|
||||||
task.group.approval.requestedDate = new Date();
|
|
||||||
} else {
|
|
||||||
if (task.group.approval.requested) {
|
|
||||||
return {
|
|
||||||
task,
|
|
||||||
requiresApproval: true,
|
|
||||||
message: res.t('taskRequiresApproval'),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
task.group.approval.requested = true;
|
|
||||||
task.group.approval.requestedDate = new Date();
|
|
||||||
|
|
||||||
const managers = await User.find({ _id: managerIds }, 'notifications preferences').exec(); // Use this method so we can get access to notifications
|
|
||||||
|
|
||||||
// @TODO: we can use the User.pushNotification function because
|
|
||||||
// we need to ensure notifications are translated
|
|
||||||
const managerPromises = [];
|
|
||||||
managers.forEach(manager => {
|
|
||||||
manager.addNotification('GROUP_TASK_APPROVAL', {
|
|
||||||
message: res.t('userHasRequestedTaskApproval', {
|
|
||||||
user: user.profile.name,
|
|
||||||
taskName: task.text,
|
|
||||||
}, manager.preferences.language),
|
|
||||||
groupId: group._id,
|
|
||||||
// user task id, used to match the notification when the task is approved
|
|
||||||
taskId: task._id,
|
|
||||||
userId: user._id,
|
|
||||||
groupTaskId: task.group.taskId, // the original task id
|
|
||||||
direction,
|
|
||||||
});
|
|
||||||
managerPromises.push(manager.save());
|
|
||||||
});
|
|
||||||
|
|
||||||
managerPromises.push(task.save());
|
|
||||||
await Promise.all(managerPromises);
|
|
||||||
|
|
||||||
return {
|
|
||||||
task,
|
|
||||||
requiresApproval: true,
|
|
||||||
message: res.t('taskApprovalHasBeenRequested'),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (task.group.approval.required && task.group.approval.approved) {
|
localTask = await Tasks.Task.findOne(
|
||||||
const notificationIndex = user.notifications.findIndex(notification => notification
|
{ userId: user._id, 'group.taskId': task._id },
|
||||||
&& notification.data && notification.data.task
|
).exec();
|
||||||
&& notification.data.task._id === task._id && notification.type === 'GROUP_TASK_APPROVED');
|
if (!localTask) throw new NotFound('Task not found.');
|
||||||
|
|
||||||
if (notificationIndex !== -1) {
|
|
||||||
user.notifications.splice(notificationIndex, 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const wasCompleted = task.completed;
|
const wasCompleted = task.completed;
|
||||||
@@ -411,26 +359,30 @@ async function scoreTask (user, task, direction, req, res) {
|
|||||||
|
|
||||||
// If a todo was completed or uncompleted move it in or out of the user.tasksOrder.todos list
|
// If a todo was completed or uncompleted move it in or out of the user.tasksOrder.todos list
|
||||||
// TODO move to common code?
|
// TODO move to common code?
|
||||||
let pullTask = false;
|
let pullTask;
|
||||||
let pushTask = false;
|
let pushTask;
|
||||||
if (task.type === 'todo') {
|
if (task.type === 'todo') {
|
||||||
if (!wasCompleted && task.completed) {
|
if (!wasCompleted && task.completed) {
|
||||||
// @TODO: mongoose's push and pull should be atomic and help with
|
// @TODO: mongoose's push and pull should be atomic and help with
|
||||||
// our concurrency issues. If not, we need to use this update $pull and $push
|
// our concurrency issues. If not, we need to use this update $pull and $push
|
||||||
pullTask = true;
|
pullTask = localTask ? localTask._id : task._id;
|
||||||
// user.tasksOrder.todos.pull(task._id);
|
|
||||||
} else if (
|
} else if (
|
||||||
wasCompleted
|
wasCompleted
|
||||||
&& !task.completed
|
&& !task.completed
|
||||||
&& user.tasksOrder.todos.indexOf(task._id) === -1
|
&& user.tasksOrder.todos.indexOf(task._id) === -1
|
||||||
) {
|
) {
|
||||||
pushTask = true;
|
pushTask = localTask ? localTask._id : task._id;
|
||||||
// user.tasksOrder.todos.push(task._id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setNextDue(task, user);
|
setNextDue(task, user);
|
||||||
|
|
||||||
|
if (localTask) {
|
||||||
|
localTask.completed = task.completed;
|
||||||
|
localTask.value = task.value + delta;
|
||||||
|
await localTask.save();
|
||||||
|
}
|
||||||
|
|
||||||
taskScoredWebhook.send(user, {
|
taskScoredWebhook.send(user, {
|
||||||
task,
|
task,
|
||||||
direction,
|
direction,
|
||||||
@@ -514,8 +466,8 @@ export async function scoreTasks (user, taskScorings, req, res) {
|
|||||||
const pushIDs = [];
|
const pushIDs = [];
|
||||||
|
|
||||||
returnDatas.forEach(returnData => {
|
returnDatas.forEach(returnData => {
|
||||||
if (returnData.pushTask === true) pushIDs.push(returnData.task._id);
|
if (returnData.pushTask) pushIDs.push(returnData.pushTask);
|
||||||
if (returnData.pullTask === true) pullIDs.push(returnData.task._id);
|
if (returnData.pullTask) pullIDs.push(returnData.pullTask);
|
||||||
});
|
});
|
||||||
|
|
||||||
const moveUpdateObject = {};
|
const moveUpdateObject = {};
|
||||||
@@ -532,13 +484,6 @@ export async function scoreTasks (user, taskScorings, req, res) {
|
|||||||
handleChallengeTask(data.task, data.delta, data.direction);
|
handleChallengeTask(data.task, data.delta, data.direction);
|
||||||
handleGroupTask(data.task, data.delta, data.direction);
|
handleGroupTask(data.task, data.delta, data.direction);
|
||||||
|
|
||||||
// Handle group tasks that require approval
|
|
||||||
if (data.requiresApproval === true) {
|
|
||||||
return {
|
|
||||||
id: data.task._id, message: data.message, requiresApproval: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return { id: data.task._id, delta: data.delta, _tmp: data._tmp };
|
return { id: data.task._id, delta: data.delta, _tmp: data._tmp };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user