scripts/apps/dashboard/workspace-tasks/tasks.ts
import _ from 'lodash';
import moment from 'moment';
import './styles/tasks.scss';
import {gettext} from 'core/utils';
TasksService.$inject = ['desks', '$rootScope', 'api', 'datetimeHelper'];
function TasksService(desks, $rootScope, api, datetimeHelper) {
this.statuses = [
{_id: 'todo', name: gettext('To Do')},
{_id: 'in_progress', name: gettext('In Progress')},
{_id: 'done', name: gettext('Done')},
];
this.save = function(orig, task) {
if (task.task.due_time) {
task.task.due_date = datetimeHelper.mergeDateTime(task.task.due_date, task.task.due_time);
}
delete task.task.due_time;
if (!task.task.user) {
delete task.task.user;
}
return api('tasks').save(orig, task)
.then((result) => result);
};
this.buildFilter = function(status) {
var filters = [];
var self = this;
if (desks.getCurrentDeskId()) {
// desk filter
filters.push({term: {'task.desk': desks.getCurrentDeskId()}});
} else {
// user filter
filters.push({term: {'task.user': $rootScope.currentUser._id}});
}
// status filter
if (status) {
filters.push({term: {'task.status': status}});
} else {
var allStatuses = [];
_.each(self.statuses, (s) => {
allStatuses.push({term: {'task.status': s._id}});
});
filters.push({or: allStatuses});
}
var andFilter = {and: filters};
return andFilter;
};
this.fetch = function(status, filter = this.buildFilter(status)) {
return api('tasks').query({
source: {
size: 200,
sort: [{_updated: 'desc'}],
filter: filter,
},
});
};
}
TasksController.$inject = ['$scope', '$timeout', 'api', 'notify', 'desks', 'tasks', '$filter', 'archiveService'];
function TasksController($scope, $timeout, api, notify, desks, tasks, $filter, archiveService) {
var KANBAN_VIEW = 'kanban',
timeout;
$scope.selected = {};
$scope.newTask = null;
$scope.tasks = null;
$scope.view = KANBAN_VIEW;
$scope.statuses = tasks.statuses;
$scope.activeStatus = $scope.statuses[0]._id;
$scope.$watch(() => desks.getCurrentDeskId(), (desk) => {
if (desk) {
fetchTasks();
fetchStages();
fetchPublished();
fetchScheduled();
}
});
/**
* Fetch stages of current desk
*/
function fetchStages() {
desks.fetchDeskStages(desks.getCurrentDeskId()).then((stages) => {
$scope.stages = stages;
});
}
/**
* Fetch items for current desk which are not published or spiked
*/
function fetchTasks() {
$timeout.cancel(timeout);
timeout = $timeout(() => {
var filter = {bool: {
must: {
term: {'task.desk': desks.getCurrentDeskId()},
},
must_not: {
terms: {state: ['published', 'spiked', 'corrected', 'killed', 'recalled']},
},
}};
var source = {source: {
size: 200,
sort: [{_updated: 'desc'}],
filter: filter,
}};
api.query('tasks', source).then((result) => {
$scope.stageItems = _.groupBy(result._items, (item) => item.task.stage);
});
}, 300, false);
}
/**
* Fetch content published from a desk
*/
function fetchPublished() {
var filter = {bool: {
must: {
term: {'task.desk': desks.getCurrentDeskId()},
},
}};
api.query('published', {source: {filter: filter}})
.then((results) => {
$scope.published = results;
});
}
/**
* Fetch templates scheduled for today on current desk
*/
function fetchScheduled() {
var startTime = moment().hours(0)
.minutes(0)
.seconds(0);
var endTime = moment().hours(23)
.minutes(59)
.seconds(59);
var filter = {
schedule_desk: desks.getCurrentDeskId(),
next_run: {$gte: toServerTime(startTime), $lte: toServerTime(endTime)},
};
api.query('content_templates', {where: filter, sort: 'next_run'}).then((results) => {
$scope.scheduled = results;
});
/**
* Get UTC datetime matching server format for given moment date object
*
* @param {Moment} d
* @return {string}
*/
function toServerTime(d) {
d.milliseconds(0); // set it to zero so it will match in replace
return d.toISOString().replace('.000Z', '+0000');
}
}
$scope.preview = function(item) {
$scope.selected.preview = item;
};
$scope.create = function() {
$scope.newTask = {};
archiveService.addTaskToArticle($scope.newTask, desks.getCurrentDesk());
var taskDate = new Date();
$scope.newTask.task.due_date = $filter('formatDateTimeString')(taskDate);
$scope.newTask.task.due_time = $filter('formatDateTimeString')(taskDate, 'HH:mm:ss');
};
$scope.save = function() {
tasks.save({}, $scope.newTask)
.then((result) => {
notify.success(gettext('Item saved.'));
$scope.close();
});
};
$scope.close = function() {
$scope.newTask = null;
};
$scope.setView = function(view) {
if ($scope.view !== view) {
$scope.view = view;
$scope.tasks = null;
fetchTasks();
}
};
$scope.selectStatus = function(status) {
if ($scope.activeStatus !== status) {
$scope.activeStatus = status;
$scope.tasks = null;
fetchTasks();
}
};
$scope.$on('task:new', fetchTasks);
$scope.$on('task:stage', (event, data) => {
var deskId = desks.getCurrentDeskId();
if (deskId === data.old_desk || deskId === data.new_desk) {
fetchTasks();
}
});
}
TaskPreviewDirective.$inject = ['tasks', 'desks', 'notify', '$filter'];
function TaskPreviewDirective(tasks, desks, notify, $filter) {
var promise = desks.initialize();
return {
templateUrl: 'scripts/apps/dashboard/workspace-tasks/views/task-preview.html',
scope: {
item: '=',
close: '&onclose',
},
link: function(scope) {
var _orig;
scope.task = null;
scope.task_details = null;
scope.editmode = false;
promise.then(() => {
scope.desks = desks.deskLookup;
scope.users = desks.userLookup;
});
scope.$watch('item._id', (val) => {
if (val) {
scope.reset();
}
});
scope.save = function() {
scope.task.task = _.extend(scope.task.task, scope.task_details);
tasks.save(_orig, scope.task)
.then((result) => {
notify.success(gettext('Item saved.'));
scope.editmode = false;
});
};
scope.edit = function() {
scope.editmode = true;
};
scope.reset = function() {
scope.editmode = false;
scope.task = _.create(scope.item);
scope.task_details = _.extend({}, scope.item.task);
scope.task_details.due_date = scope.task_details.due_date ?
$filter('formatDateTimeString')(scope.task_details.due_date) : null;
scope.task_details.due_time = scope.task_details.due_time ?
$filter('formatDateTimeString')(scope.task_details.due_time, 'HH:mm:ss') : null;
_orig = scope.item;
};
},
};
}
TaskKanbanBoardDirective.$inject = [];
function TaskKanbanBoardDirective() {
return {
templateUrl: 'scripts/apps/dashboard/workspace-tasks/views/kanban-board.html',
scope: {
items: '=',
label: '@',
cssClass: '@',
selected: '=',
},
link: function(scope) {
scope.preview = function(item) {
if (scope.selected) {
scope.selected.preview = item;
}
};
},
};
}
AssigneeViewDirective.$inject = ['desks'];
function AssigneeViewDirective(desks) {
var promise = desks.initialize();
return {
templateUrl: 'scripts/apps/dashboard/workspace-tasks/views/assignee-view.html',
scope: {
task: '=',
name: '=',
},
link: function(scope) {
promise.then(function setItemAssigne() {
var task = angular.extend({desk: null, user: null}, scope.task);
var desk = desks.deskLookup[task.desk] || {};
var user = desks.userLookup[task.user] || {};
scope.deskName = desk.name || null;
scope.userName = user.display_name || null;
scope.user = user || null;
});
},
};
}
// todo(petr): move to desks module
StagesCtrlFactory.$inject = ['desks'];
function StagesCtrlFactory(desks) {
var promise = desks.initialize();
return function StagesCtrl($scope) {
var self = this;
promise.then(() => {
self.stages = null;
self.selected = null;
// select a stage as active
self.select = function(stage) {
var stageId = stage ? stage._id : null;
self.selected = stage || null;
desks.setCurrentStageId(stageId);
};
// reload list of stages
self.reload = function(deskId) {
self.stages = deskId ? desks.deskStages[deskId] : null;
self.select(_.find(self.stages, {_id: desks.activeStageId}));
};
$scope.$watch(() => desks.getCurrentDeskId(), () => {
self.reload(desks.getCurrentDeskId());
});
});
};
}
function DeskStagesDirective() {
return {
templateUrl: 'scripts/apps/dashboard/workspace-tasks/views/desk-stages.html',
};
}
angular.module('superdesk.apps.workspace.tasks', ['superdesk.apps.workspace.menu'])
.factory('StagesCtrl', StagesCtrlFactory)
.directive('sdTaskPreview', TaskPreviewDirective)
.directive('sdAssigneeView', AssigneeViewDirective)
.directive('sdDeskStages', DeskStagesDirective)
.directive('sdTaskKanbanBoard', TaskKanbanBoardDirective)
.controller('TasksController', TasksController)
.service('tasks', TasksService)
.config(['superdeskProvider', 'workspaceMenuProvider', function(superdesk, workspaceMenuProvider) {
superdesk.activity('/workspace/tasks', {
label: gettext('Workspace'),
controller: TasksController,
templateUrl: 'scripts/apps/dashboard/workspace-tasks/views/workspace-tasks.html',
topTemplateUrl: 'scripts/apps/dashboard/views/workspace-topnav.html',
sideTemplateUrl: 'scripts/apps/workspace/views/workspace-sidenav.html',
filters: [{action: 'view', type: 'task'}],
});
superdesk.activity('pick.task', {
label: gettext('Pick task'),
icon: 'pick',
controller: ['data', 'superdesk',
/**
* Open given item using sidebar authoring
*
* @param {Object} data
* @param {Object} superdesk service
* @return {Promise}
*/
function pickTask(data, superdeskService) {
return superdeskService.intent('edit', 'item', data.item);
},
],
filters: [{action: superdesk.ACTION_EDIT, type: 'task'}],
});
workspaceMenuProvider.item({
href: '/workspace/tasks',
icon: 'tasks',
label: gettext('Tasks'),
shortcut: 'alt+t',
order: 500,
if: 'privileges.tasks && workspaceConfig.tasks',
});
}]);