From a7f1d8b5a7fb109f7f52905a43bbb0c5f4283299 Mon Sep 17 00:00:00 2001 From: Chris Brame Date: Fri, 7 Sep 2018 00:55:31 -0400 Subject: [PATCH] feature(priorities) Customizable Priorities --- src/controllers/api/v1/tickets.js | 153 +++- src/controllers/settings.js | 48 +- src/helpers/viewdata/index.js | 23 + src/models/ticket.js | 2 +- src/models/ticketpriority.js | 19 +- src/models/tickettype.js | 26 + .../js/angularjs/controllers/settings.js | 559 ++++++++++++- .../js/angularjs/controllers/singleTicket.js | 4 +- src/public/js/modules/helpers.js | 6 + src/routes/index.js | 7 + src/sass/partials/common.sass | 21 +- src/sass/partials/ui.sass | 10 + src/settings/defaults.js | 19 +- .../partials/addPriorityToTypeWindow.hbs | 18 + src/views/partials/createPriorityWindow.hbs | 50 ++ src/views/partials/deletePriorityWindow.hbs | 31 + src/views/partials/deleteTicketTypeWindow.hbs | 22 +- src/views/partials/nav.hbs | 3 - src/views/settings.hbs | 746 +++++++++++++----- .../subviews/settings/settings-tickets.hbs | 474 ----------- src/views/tickets.hbs | 4 +- 21 files changed, 1496 insertions(+), 749 deletions(-) create mode 100644 src/views/partials/addPriorityToTypeWindow.hbs create mode 100644 src/views/partials/createPriorityWindow.hbs create mode 100644 src/views/partials/deletePriorityWindow.hbs delete mode 100644 src/views/subviews/settings/settings-tickets.hbs diff --git a/src/controllers/api/v1/tickets.js b/src/controllers/api/v1/tickets.js index 7d21c298d..390e4cd99 100644 --- a/src/controllers/api/v1/tickets.js +++ b/src/controllers/api/v1/tickets.js @@ -841,11 +841,17 @@ api_tickets.createType = function(req, res) { if (_.isUndefined(typeName) || typeName.length < 3) return res.status(400).json({success: false, error: 'Invalid Type Name!'}); var ticketTypeSchema = require('../../../models/tickettype'); - ticketTypeSchema.create({name: typeName}, function(err, ticketType) { + var ticketPrioritiesSchema = require('../../../models/ticketpriority'); + ticketPrioritiesSchema.find({default: true}, function(err, priorities) { if (err) return res.status(400).json({success: false, error: err.message}); + priorities = _.sortBy(priorities, 'migrationNum'); + + ticketTypeSchema.create({name: typeName, priorities: priorities}, function(err, ticketType) { + if (err) return res.status(400).json({success: false, error: err.message}); - return res.json({success: true, tickettype: ticketType}); - }) + return res.json({success: true, tickettype: ticketType}); + }) + }); }; /** @@ -883,6 +889,58 @@ api_tickets.updateType = function(req, res) { }); }; +api_tickets.typeAddPriority = function(req, res) { + var id = req.params.id; + var data = req.body; + if (!id || !data || !data.priority) + return res.status(400).json({success: false, error: 'Invalid request data'}); + + var ticketTypeSchema = require('../../../models/tickettype'); + ticketTypeSchema.getType(id, function(err, type) { + if (err) return res.status(400).json({success: false, error: err.message}); + + type.addPriority(data.priority, function(err, type) { + if (err) return res.status(400).json({success: false, error: err.message}); + + type.save(function(err, t) { + if (err) return res.status(400).json({success: false, error: err.message}); + + t.populate('priorities', function(err, tt) { + if (err) return res.status(400).json({success: false, error: err.message}); + + return res.json({success: true, type: tt}); + }); + }); + }); + }); +}; + +api_tickets.typeRemovePriority = function(req, res) { + var id = req.params.id; + var data = req.body; + if (!id || !data || !data.priority) + return res.status(400).json({success: false, error: 'Invalid request data'}); + + var ticketTypeSchema = require('../../../models/tickettype'); + ticketTypeSchema.getType(id, function(err, type) { + if (err) return res.status(400).json({success: false, error: err.message}); + + type.removePriority(data.priority, function(err, type) { + if (err) return res.status(400).json({success: false, error: err.message}); + + type.save(function(err, t) { + if (err) return res.status(400).json({success: false, error: err.message}); + + t.populate('priorities', function(err, tt) { + if (err) return res.status(400).json({success: false, error: err.message}); + + return res.json({success: true, type: tt}); + }); + }); + }); + }); +}; + /** * @api {delete} /api/v1/tickets/types/:id Delete Ticket Type * @apiName deleteType @@ -940,6 +998,95 @@ api_tickets.deleteType = function(req, res) { }); }; +api_tickets.createPriority = function(req, res) { + var data = req.body; + var pName = data.name; + var pOverdueIn = data.overdueIn; + var pHtmlColor = data.htmlColor; + + if (!pName) + return res.status(400).json({success: false, error: 'Invalid Request Data.'}); + + var ticketPrioritySchema = require('../../../models/ticketpriority'); + var P = new ticketPrioritySchema({ + name: pName, + overdueIn: pOverdueIn, + htmlColor: pHtmlColor + }); + + P.save(function(err, savedPriority) { + if (err) + return res.status(400).json({success: false, error: err.message}); + + return res.json({success: true, priority: savedPriority}); + }); +}; + +api_tickets.getPriorities = function(req, res) { + var ticketPrioritySchema = require('../../../models/ticketpriority'); + ticketPrioritySchema.find({}, function(err, priorities) { + if (err) return res.status(400).json({success: false, error: err.message}); + + priorities = _.sortBy(priorities, ['migrationNum', 'name']); + + return res.json({success: true, priorities: priorities}); + }); +}; + +api_tickets.updatePriority = function(req, res) { + var id = req.params.id; + var data = req.body; + if (_.isUndefined(id) || _.isNull(id) || _.isNull(data) || _.isUndefined(data)) + return res.status(400).json({success: false, error: 'Invalid Request Data'}); + + var ticketPrioritySchema = require('../../../models/ticketpriority'); + ticketPrioritySchema.findOne({_id: id}, function(err, priority) { + if (err) return res.status(400).json({success: false, error: err.message}); + + if (data.name) + priority.name = data.name; + if (data.htmlColor) + priority.htmlColor = data.htmlColor; + if (data.overdueIn) + priority.overdueIn = data.overdueIn; + + priority.save(function(err, p) { + if (err) return res.status(400).json({success: false, error: err.message}); + + return res.json({success: true, priority: p}); + }); + }); +}; + +api_tickets.deletePriority = function(req, res) { + var id = req.params.id; + var newPriority = req.body.newPriority; + if (!id || !newPriority) + return res.status(400).json({success: false, error: 'Invalid Request Data'}); + + async.series([ + function(next) { + var ticketSchema = require('../../../models/ticket'); + ticketSchema.updateMany({priority: id}, {priority: newPriority}, next); + }, + function(next) { + var ticketPrioritySchema = require('../../../models/ticketpriority'); + ticketPrioritySchema.findOne({_id: id}, function(err, priority) { + if (err) return next(err); + + if (priority.default) + return next('Unable to delete default priority: ' + priority.name); + + priority.remove(next); + }); + } + ], function(err) { + if (err) return res.status(400).json({success: false, error: err.message}); + + return res.json({success: true}); + }); +}; + /** * @api {get} /api/v1/tickets/stats Get Ticket Stats * @apiName getTicketStats diff --git a/src/controllers/settings.js b/src/controllers/settings.js index a04e26a77..bf4b85dff 100644 --- a/src/controllers/settings.js +++ b/src/controllers/settings.js @@ -40,7 +40,7 @@ settingsController.general = function(req, res) { getSettings(content, function(err) { if (err) return handleError(res, err); - return res.render('subviews/settings/settings-tickets', content); + return res.render('settings', content); }); }; @@ -59,7 +59,7 @@ settingsController.ticketSettings = function(req, res) { getSettings(content, function(err) { if (err) return handleError(res, err); - return res.render('subviews/settings/settings-tickets', content); + return res.render('settings', content); }); }; @@ -78,7 +78,7 @@ settingsController.mailerSettings = function(req, res) { getSettings(content, function(err) { if (err) return handleError(res, err); - return res.render('subviews/settings/settings-tickets', content); + return res.render('settings', content); }); }; @@ -97,7 +97,7 @@ settingsController.notificationsSettings = function(req, res) { getSettings(content, function(err) { if (err) return handleError(res, err); - return res.render('subviews/settings/settings-tickets', content); + return res.render('settings', content); }); }; @@ -116,7 +116,7 @@ settingsController.tpsSettings = function(req, res) { getSettings(content, function(err) { if (err) return handleError(res, err); - return res.render('subviews/settings/settings-tickets', content); + return res.render('settings', content); }); }; @@ -135,7 +135,7 @@ settingsController.legal = function(req, res) { getSettings(content, function(err) { if (err) return handleError(res, err); - return res.render('subviews/settings/settings-tickets', content); + return res.render('settings', content); }); }; @@ -208,36 +208,22 @@ function getSettings(content, callback) { if (err) return callback(err); content.data.ticketTypes = _.sortBy(types, function(o){ return o.name; }); + _.each(content.data.ticketTypes, function(type) { + type.priorities = _.sortBy(type.priorities, ['migrationNum', 'name']); + }); + + var ticketPrioritySchema = require('../models/ticketpriority'); + ticketPrioritySchema.getPriorities(function(err, priorities) { + if (err) return callback(err); + + content.data.priorities = _.sortBy(priorities, ['migrationNum', 'name']); - return callback(); + return callback(); + }); }); }); } -// settingsController.legal = function(req, res) { -// var content = {}; -// content.title = "Legal Settings"; -// content.nav = 'settings'; -// content.subnav = 'settings-legal'; -// -// content.data = {}; -// content.data.user = req.user; -// content.data.common = req.viewdata; -// -// settingSchema.getSettings(function(err, settings) { -// if (err) return handleError(res, 'Invalid Settings'); -// -// var s = {}; -// s.privacyPolicy = _.find(settings, function(x){return x.name === 'legal:privacypolicy'}); -// s.privacyPolicy = (s.privacyPolicy === undefined) ? {value: ''} : s.privacyPolicy; -// s.privacyPolicy.value = jsStringEscape(s.privacyPolicy.value); -// -// content.data.settings = s; -// -// return res.render('subviews/settings/legal', content); -// }); -// }; - settingsController.logs = function(req, res) { if (!checkPerms(req, 'settings:logs')) return res.redirect('/settings'); diff --git a/src/helpers/viewdata/index.js b/src/helpers/viewdata/index.js index 591861a2e..1e3add413 100644 --- a/src/helpers/viewdata/index.js +++ b/src/helpers/viewdata/index.js @@ -106,6 +106,15 @@ viewController.getData = function(request, cb) { return callback(); }); }, + function(callback) { + viewController.getPriorities(request, function(err, data) { + if (err) return callback(); + + viewdata.priorities = data; + + return callback(); + }); + }, function(callback) { viewController.getTags(request, function(err, data) { if (err) return callback(); @@ -329,6 +338,20 @@ viewController.getDefaultTicketType = function(request, callback) { }); }; +viewController.getPriorities = function(request, callback) { + var ticketPrioritySchema = require('../../models/ticketpriority'); + ticketPrioritySchema.getPriorities(function(err, priorities) { + if (err) { + winston.debug('Error viewController:getPriorities: ' + err); + return callback(err); + } + + priorities = _.sortBy(priorities, ['migrationNum', 'name']); + + return callback(null, priorities); + }); +}; + viewController.getTags = function(request, callback) { var tagSchema = require('../../models/tag'); diff --git a/src/models/ticket.js b/src/models/ticket.js index 091c7ad7b..69d9a2594 100644 --- a/src/models/ticket.js +++ b/src/models/ticket.js @@ -75,7 +75,7 @@ var ticketSchema = mongoose.Schema({ deleted: { type: Boolean, default: false, required: true, index: true }, type: { type: mongoose.Schema.Types.ObjectId, required: true, ref: 'tickettypes' }, status: { type: Number, default: 0, required: true, index: true }, - // priority: { type: Number, required: true }, + priority: { type: mongoose.Schema.Types.ObjectId, ref: 'priorities', required: true }, tags: [{ type: mongoose.Schema.Types.ObjectId, ref: 'tags' }], subject: { type: String, required: true }, diff --git a/src/models/ticketpriority.js b/src/models/ticketpriority.js index 252a1fc46..aa1a299d0 100644 --- a/src/models/ticketpriority.js +++ b/src/models/ticketpriority.js @@ -12,18 +12,29 @@ **/ -var mongoose = require('mongoose'); -var _ = require('lodash'); +var _ = require('lodash'); +var mongoose = require('mongoose'); +var moment = require('moment'); +var durationFormat = require('moment-duration-format'); var COLLECTION = 'priorities'; var prioritySchema = mongoose.Schema({ name: { type: String, required: true, unique: true }, - overdueIn: { type: Number, required: true, default: 2880}, // Minutes until overdue (48 Hours) - htmlColor: { type: String, default: '#29b955'}, + overdueIn: { type: Number, required: true, default: 2880 }, // Minutes until overdue (48 Hours) + htmlColor: { type: String, default: '#29b955' }, migrationNum: { type: Number, index: true }, //Needed to convert <1.0 priorities to new format. default: { type: Boolean } +}, { + toJSON: { + virtuals: true + } +}); + +prioritySchema.virtual('durationFormatted').get(function() { + var priority = this; + return moment.duration(priority.overdueIn, 'minutes').format('Y [year], M [month], d [day], h [hour], m [min]', {trim: 'both'}); }); prioritySchema.statics.getPriority = function(_id, callback) { diff --git a/src/models/tickettype.js b/src/models/tickettype.js index 684a60f35..9006e2be6 100644 --- a/src/models/tickettype.js +++ b/src/models/tickettype.js @@ -12,6 +12,7 @@ **/ +var _ = require('lodash'); var mongoose = require('mongoose'); var COLLECTION = 'tickettypes'; @@ -88,4 +89,29 @@ ticketTypeSchema.statics.getTypeByName = function(name, callback) { return q.exec(callback); }; +ticketTypeSchema.methods.addPriority = function(priorityId, callback) { + if (!priorityId) return callback({message: 'Invalid Priority Id'}); + + var self = this; + + if (!_.isArray(self.priorities)) + self.priorities = []; + + self.priorities.push(priorityId); + + return callback(null, self); +}; + +ticketTypeSchema.methods.removePriority = function(priorityId, callback) { + if (!priorityId) return callback({message: 'Invalid Priority Id'}); + + var self = this; + + self.priorities = _.reject(self.priorities, function(p) { + return p._id.toString() === priorityId.toString(); + }); + + return callback(null, self); +}; + module.exports = mongoose.model(COLLECTION, ticketTypeSchema); \ No newline at end of file diff --git a/src/public/js/angularjs/controllers/settings.js b/src/public/js/angularjs/controllers/settings.js index 5877ac769..82384f6b7 100644 --- a/src/public/js/angularjs/controllers/settings.js +++ b/src/public/js/angularjs/controllers/settings.js @@ -95,6 +95,8 @@ define(['angular', 'underscore', 'jquery', 'modules/helpers', 'modules/ui', 'uik } ]; + var privacyPolicyMDE = null; + $scope.init = function() { //Fix Inputs if input is preloaded with a value $timeout(function() { @@ -110,7 +112,7 @@ define(['angular', 'underscore', 'jquery', 'modules/helpers', 'modules/ui', 'uik var $privacyPolicy = $('#privacyPolicy'); if ($privacyPolicy.length > 0) { - var privacyPolicyMDE = new EasyMDE({ + privacyPolicyMDE = new EasyMDE({ element: $privacyPolicy[0], forceSync: true, minHeight: "220px", //Slighty smaller to adjust the scroll @@ -176,6 +178,8 @@ define(['angular', 'underscore', 'jquery', 'modules/helpers', 'modules/ui', 'uik $(vm).removeClass('active'); }); + $('.page-wrapper').scrollTop(0); + //Show Selected $target.addClass('active'); if (currentTarget.length > 0) { @@ -185,6 +189,12 @@ define(['angular', 'underscore', 'jquery', 'modules/helpers', 'modules/ui', 'uik if (settings === 'settings-tickets') { $target.find('ul>li[data-key]').first().addClass('active'); } + + if (settings === 'settings-legal') { + if (privacyPolicyMDE) { + privacyPolicyMDE.codemirror.refresh(); + } + } } } }; @@ -322,7 +332,24 @@ define(['angular', 'underscore', 'jquery', 'modules/helpers', 'modules/ui', 'uik 'Content-Type': 'application/json' } }).then(function successCallback() { - + if (!$scope.mailerCheckEnabled) { + UIkit.modal.confirm( + 'Settings will take affect after server restart.

Would you like to restart the server now?' + , function() { + $http.get( + '/api/v1/admin/restart' + ) + .success(function() { + }) + .error(function(err) { + helpers.hideLoader(); + $log.log('[trudesk:settings:mailerCheckSubmit] - Error: ' + err.error); + $log.error(err); + }); + }, { + labels: {'Ok': 'Yes', 'Cancel': 'No'}, confirmButtonClass: 'md-btn-primary' + }); + } }, function errorCallback(err) { helpers.UI.showSnackbar(err, true); }); @@ -615,11 +642,12 @@ define(['angular', 'underscore', 'jquery', 'modules/helpers', 'modules/ui', 'uik }; $scope.showDeleteTicketType = function(typeId, hasTickets) { - event.preventDefault(); if (hasTickets) { - var delTicketTypeModal = $('#deleteTicketTypeModal'); + var delTicketTypeModal = $('#deleteTicketTypeModal-' + typeId); if (delTicketTypeModal.length > 0) { UIkit.modal(delTicketTypeModal, {bgclose: false}).show(); + } else { + $log.log('Unable to locate modal window: #deleteTicketTypeModal' + typeId); } } else { $scope.submitDeleteTicketType(typeId, undefined); @@ -630,12 +658,12 @@ define(['angular', 'underscore', 'jquery', 'modules/helpers', 'modules/ui', 'uik if (event) event.preventDefault(); if (_.isUndefined(typeId) || typeId.length < 1) { - helpers.UI.showSnackbar('Unable to get tag ID', true); + helpers.UI.showSnackbar('Unable to get type ID', true); return true; } - var typeName = $('input#del_type_name').val(); - var newTypeId = $('form#deleteTicketTypeForm select[name="type"]').val(); + var typeName = $('input#del_type_name-' + typeId).val(); + var newTypeId = $('form#deleteTicketTypeForm-' + typeId + ' select[name="ticketType"]').val(); if (!newTypeId || newTypeId.length < 1) { helpers.UI.showSnackbar('Unable to get new ticket type. Aborting...', true); @@ -655,7 +683,7 @@ define(['angular', 'underscore', 'jquery', 'modules/helpers', 'modules/ui', 'uik if (response.data.success) { helpers.UI.showSnackbar('Successfully removed ticket type: ' + typeName, false); - return History.pushState(null, null, '/settings/tickettypes/'); + return History.pushState(null, null, '/settings/tickets/'); } }, function errorCallback(response) { if (!_.isUndefined(response.data.error.custom)) { @@ -668,6 +696,521 @@ define(['angular', 'underscore', 'jquery', 'modules/helpers', 'modules/ui', 'uik }); }; + $scope.editPriority = function(pId, $event) { + $event.preventDefault(); + var $viewBox = $('#view-p-' + pId); + var $editBox = $('#edit-p-' + pId); + if ($editBox.length > 0 && $viewBox.length > 0) { + $editBox.find('.uk-color-button').css({background: $editBox.find('input[name="p-' + pId + '-htmlColor"]').val()}); + $viewBox.addClass('hide'); + $editBox.removeClass('hide'); + } + }; + + $scope.cancelEditPriority = function(pId, $event) { + if ($event) + $event.preventDefault(); + var $viewBox = $('#view-p-' + pId); + var $editBox = $('#edit-p-' + pId); + if ($editBox.length > 0 && $viewBox.length > 0) { + if ($event) + $($event.currentTarget).parents('form').trigger('reset'); + $viewBox.removeClass('hide'); + $editBox.addClass('hide'); + } + }; + + $scope.editTicketTypePriority = function(typeId, id, $event) { + $event.preventDefault(); + var $viewBox = $('#t-' + typeId + '-view-p-' + id); + var $editBox = $('#t-' + typeId + '-edit-p-' + id); + if ($editBox.length > 0 && $viewBox.length > 0) { + $editBox.find('.uk-color-button').css({background: $editBox.find('input[name="p-' + id + '-htmlColor"]').val()}); + $viewBox.addClass('hide'); + $editBox.removeClass('hide'); + } + }; + + $scope.cancelEditTicketTypePriority = function(typeId, id, $event) { + if ($event) + $event.preventDefault(); + var $viewBox = $('#t-' + typeId + '-view-p-' + id); + var $editBox = $('#t-' + typeId + '-edit-p-' + id); + if ($editBox.length > 0 && $viewBox.length > 0) { + if ($event) + $($event.currentTarget).parents('form').trigger('reset'); + $viewBox.removeClass('hide'); + $editBox.addClass('hide'); + } + }; + + $scope.showAddPriorityToType = function(typeId, $event) { + if ($event) + $event.preventDefault(); + + //Dynamically load priorities.... + $http.get('/api/v1/tickets/type/' + typeId) + .success(function(response) { + if (response.success) { + var updatedType = response.type; + $http.get('/api/v1/tickets/priorities') + .success(function(response) { + var priorities = response.priorities; + var $addPriorityToTypeModal = $('#addPriorityTicketType-' + typeId); + var $pLoop = $addPriorityToTypeModal.find('.priority-loop'); + $pLoop.empty(); + + var html = ''; + _.each(priorities, function(priority) { + if (_.some(updatedType.priorities, priority)) { + html = ''; + html += '
\n' + + '
\n' + + '
' + priority.name + '
\n' + + '

SLA Overdue: ' + priority.durationFormatted + '

\n' + + '
\n' + + '
\n' + + ' check\n' + + '
\n' + + '
'; + + $pLoop.append(html); + } else { + html = ''; + html += '
\n' + + '
\n' + + '
' + priority.name + '
\n' + + '

SLA Overdue: ' + priority.durationFormatted + '

\n' + + '
\n' + + '
\n' + + ' Add\n' + + ' \n' + + '
\n' + + '
'; + + $pLoop.append(html); + } + }); + + var $injector = angular.injector(["ng", "trudesk"]); + $injector.invoke(["$compile", "$rootScope", function ($compile, $rootScope) { + var $scope = $pLoop.scope(); + $compile($pLoop)($scope || $rootScope); + $rootScope.$digest(); + }]); + + if ($addPriorityToTypeModal.length > 0) { + UIkit.modal($addPriorityToTypeModal, {bgclose: false}).show(); + } else { + $log.error('Unable to locate add priority modal.'); + } + }) + .error(function(error) { + $log.error(error); + helpers.UI.showSnackbar('Unable to load ticket type. Check Console.'); + }); + } else { + helpers.UI.showSnackbar('Unable to load ticket type. Check Console.'); + } + }) + .error(function(error) { + $log.error(error); + helpers.UI.showSnackbar('Unable to load ticket type. Check Console.'); + }); + }; + + $scope.priorityAddBtnClicked = function(typeId, pId, $event) { + $event.preventDefault(); + var $addButton = $($event.currentTarget); + if ($addButton.length < 1) + return false; + + $http.post('/api/v1/tickets/type/' + typeId + '/addpriority', { + priority: pId + }, { + 'Content-Type': 'application/json' + }).then(function success(response) { + var Type = response.data.type; + if (Type) { + //Update UI + var $typeContainer = $('div[data-ticket-type-id="' + typeId + '"]'); + if ($typeContainer.length > 0) { + var $prioritiesBox = $typeContainer.find('.priority-loop'); + var html = ''; + $prioritiesBox.empty(); + var sortedTypePriorities = _.sortBy(( _.sortBy(Type.priorities, 'name')), 'migrationNum'); + + _.each(sortedTypePriorities, function(priority) { + html = ''; + html += '
\n' + + '
\n' + + '
' + priority.name + '
\n' + + '

SLA Overdue: ' + priority.durationFormatted + '

\n' + + '
\n' + + '
\n' + + '
\n' + + ' Edit\n' + + ' Remove\n' + + '
\n' + + '
\n' + + '
\n' + + '
\n' + + '
\n' + + '
\n' + + '
\n' + + ' \n' + + ' \n' + + '
\n' + + '
\n' + + ' \n' + + ' \n' + + '
\n' + + '
\n' + + ' \n' + + '
\n' + + ' \n' + + ' \n' + + ' \n' + + '
\n' + + '
\n' + + '
\n' + + '
\n' + + ' Cancel\n' + + ' \n' + + '
\n' + + '
\n' + + '
\n' + + '
\n' + + '
'; + + $prioritiesBox.append(html); + }); + + //Bootstrap Angular dynamically... + var $injector = angular.injector(["ng", "trudesk"]); + $injector.invoke(["$compile", "$rootScope", function ($compile, $rootScope) { + var $scope = $prioritiesBox.scope(); + $compile($prioritiesBox)($scope || $rootScope); + $rootScope.$digest(); + }]); + + //Fix filled inputs + helpers.UI.inputs(); + helpers.UI.reRenderInputs(); + } + } + + + //Animation + $addButton.velocity({ + opacity: 0 + }, { + duration: 350, + complete: function() { + $addButton.addClass('hide'); + } + }); + + var $check = $addButton.siblings('i.material-icons'); + if ($check.length > 0) { + $check.velocity({ + opacity: 1 + }, { + delay: 360, + duration: 200, + begin: function() { + $check.show(); + } + }); + } + }, function errorCallback(err) { + $log.error(err); + helpers.UI.showSnackbar('Error: ' + err, true); + }); + }; + + $scope.removePriorityClicked = function(pId, $event) { + if ($event) + $event.preventDefault(); + + var deletePriorityModal = $('#deletePriorityModal-' + pId); + if (deletePriorityModal.length > 0) { + deletePriorityModal.find('form').trigger('reset'); + UIkit.modal(deletePriorityModal, {bgclose: false}).show(); + } else { + $log.error('Unable to locate delete priority modal.'); + } + }; + + $scope.submitRemovePriority = function(pId, $event) { + if ($event) + $event.preventDefault(); + + if (pId) { + var $form = $($event.currentTarget).parents('form'); + var selectedPriority = $form.find('select[name="priority"]').val(); + + $http.post('/api/v1/tickets/priority/' + pId + '/delete', { + newPriority: selectedPriority + }, { + headers: { + 'Content-Type': 'application/json' + } + }) + .then(function() { + var $body = $('body'); + // $body.find('div#view-p-' + pId).remove(); + // $body.find('div#edit-p-' + pId).remove(); + $body.find('div[data-pId="' + pId + '"]').remove(); + var deletePriorityModal = $('#deletePriorityModal-' + pId); + if (deletePriorityModal) { + UIkit.modal(deletePriorityModal).hide(); + } + }, function(err) { + $log.error(err); + helpers.UI.showSnackbar(err.data.error, true); + }) + } + }; + + $scope.submitRemoveTicketTypePriority = function(typeId, priorityId, $event) { + if ($event) + $event.preventDefault(); + + if (typeId && priorityId) { + $http.post('/api/v1/tickets/type/' + typeId + '/removepriority', { + priority: priorityId + }, { + headers: { + 'Content-Type': 'application/json' + } + }).then(function() { + helpers.UI.showSnackbar('Removed priority from type', false); + var $body = $('body'); + $body.find('div#t-' + typeId + '-view-p-' + priorityId).remove(); + $body.find('div#t-' + typeId + '-edit-p-' + priorityId).remove(); + }, function(err) { + $log.error(err); + helpers.UI.showSnackbar(err.data.error, true); + }); + } + }; + + $scope.showCreatePriorityWindow = function($event) { + if ($event) + $event.preventDefault(); + + var createPriorityModal = $('#createPriorityModal'); + if (createPriorityModal.length > 0) { + createPriorityModal.find('form').trigger('reset'); + helpers.UI.inputs(); + helpers.UI.reRenderInputs(); + createPriorityModal.find('form').trigger('reset'); + createPriorityModal.find('#generateHtmlColor').css({background: '#29B955'}); + UIkit.modal(createPriorityModal, {bgclose: false}).show(); + createPriorityModal.find('input[name="p-name"]').focus(); + } else { + $log.error('Unable to locate create priority modal.'); + } + }; + + $scope.createPrioritySubmit = function($event) { + $event.preventDefault(); + var $form = $($event.currentTarget); + if ($form) { + if (!$form.isValid(null, null, false)) { + return true; + } else { + var $priorityName = $form.find('input[name="p-name"]'); + var $priorityOverdueIn = $form.find('input[name="p-overdueIn"]'); + var $priorityHtmlColor = $form.find('input[name="p-htmlColor"]'); + if ($priorityName.length < 1 || $priorityOverdueIn.length < 1 || $priorityHtmlColor.length < 1) + return false; + + var name = $priorityName.val(); + var overdueIn = $priorityOverdueIn.val(); + var htmlColor = $priorityHtmlColor.val(); + + $http.post('/api/v1/tickets/priority/create', { + name: name, + overdueIn: overdueIn, + htmlColor: htmlColor + }, { + headers: { + 'Content-Type': 'application/json' + } + }).then(function successCallback(response) { + var savedPriority = response.data.priority; + if (savedPriority) { + var priorityLoop = $('body').find('.all-priorities-loop'); + if (priorityLoop) { + $http.get('/api/v1/tickets/priorities') + .success(function(pResponse) { + var priorities = pResponse.priorities; + var html = ''; + html += '
\n' + + '
\n' + + '
' + savedPriority.name + '
\n' + + '

SLA Overdue: ' + savedPriority.durationFormatted + '

\n' + + '
\n' + + '
\n' + + '
\n' + + ' Edit\n' + + ' Remove\n' + + '
\n' + + '
\n' + + '
\n' + + '
\n' + + '
\n' + + '
\n' + + '
\n' + + ' \n' + + ' \n' + + '
\n' + + '
\n' + + ' \n' + + ' \n' + + '
\n' + + '
\n' + + ' \n' + + '
\n' + + ' \n' + + ' \n' + + ' \n' + + '
\n' + + '
\n' + + '
\n' + + '
\n' + + ' Cancel\n' + + ' \n' + + '
\n' + + '
\n' + + '
\n' + + '
\n' + + '
\n' + + '
\n' + + '
\n' + + '
\n' + + ' \n' + + '
\n' + + '

Remove Priority

\n' + + ' Please select the priority you wish to reassign tickets to in order to delete the this priority.\n' + + '
\n' + + '
\n' + + '
\n' + + '
\n' + + ' \n' + + ' \n' + + '
\n' + + '
\n' + + '
\n' + + ' WARNING: This will change all tickets with a priority of: ' + savedPriority.name + ' to the selected priority above.
This is permanent!
\n' + + '
\n' + + ' \n' + + '
\n' + + '
\n' + + '
\n'; + + priorityLoop.append(html); + + helpers.UI.selectize(); + + //Bootstrap Angular dynamically... + var $injector = angular.injector(["ng", "trudesk"]); + $injector.invoke(["$compile", "$rootScope", function ($compile, $rootScope) { + var $scope = priorityLoop.scope(); + $compile(priorityLoop)($scope || $rootScope); + $rootScope.$digest(); + }]); + + helpers.UI.showSnackbar('Priority Created.', false); + var createPriorityModal = $('#createPriorityModal'); + if (createPriorityModal.length > 0) { + UIkit.modal(createPriorityModal).hide(); + } + }) + .error(function(errorResponse) { + $log.error(errorResponse); + helpers.UI.showSnackbar('Error: ' + errorResponse.data.error, true); + }); + } + } + }, function errorCallback(errorResponse) { + $log.error(errorResponse); + helpers.UI.showSnackbar('Error: ' + errorResponse.data.error, true); + }); + } + } + }; + + $scope.submitUpdatePriority = function($event) { + $event.preventDefault(); + var $form = $($event.currentTarget); + var priorityId = $form.attr('data-priority-id'); + var typeId = $form.parents('div[data-ticket-type-id]').attr('data-ticket-type-id'); + if ($form && priorityId) { + var $priorityNameInput = $form.find('input[name="p-' + priorityId + '-name"]'); + var $priorityHtmlColor = $form.find('input[name="p-' + priorityId + '-htmlColor"]'); + var $priorityOverdueIn = $form.find('input[name="p-' + priorityId + '-overdueIn"]'); + var priorityName = $priorityNameInput.val(); + var htmlColor = $priorityHtmlColor.val(); + var overdueIn = $priorityOverdueIn.val(); + + $http.put('/api/v1/tickets/priority/' + priorityId, { + name: priorityName, + htmlColor: htmlColor, + overdueIn: overdueIn + }, { + headers: { + 'Content-Type': 'application/json' + } + }).then(function successCallback(response) { + helpers.UI.showSnackbar('Priority updated successfully', false); + var $body = $('body'); + $body.find('.p-' + priorityId + '-name').css({color: htmlColor}).text(priorityName); + $body.find('.p-' + priorityId + '-overdueIn').text(response.data.priority.durationFormatted); + $body.find('input[name="p-' + priorityId + '-htmlColor"]').val(htmlColor); + if (typeId) + $scope.cancelEditTicketTypePriority(typeId, priorityId); + else + $scope.cancelEditPriority(priorityId); + }, function errorCallback(err) { + helpers.UI.showSnackbar(err, true); + }); + } + }; + + $scope.generateRandomColor = function(id, $event) { + $event.preventDefault(); + var $currentTarget = $($event.currentTarget); + if ($currentTarget.length > 0) { + var color = getRandomColor(); + $currentTarget.css({background: color}); + $currentTarget.next().find('input').val(color); + } + }; + + function getRandomColor() { + var letters = '0123456789ABCDEF'; + var color = '#'; + for (var i = 0; i < 6; i++) { + color += letters[Math.floor(Math.random() * 16)]; + } + return color; + } + $scope.editTag = function($event) { if (_.isNull($event.target) || _.isUndefined($event.target) || $event.target.tagName.toLowerCase() === 'label' || diff --git a/src/public/js/angularjs/controllers/singleTicket.js b/src/public/js/angularjs/controllers/singleTicket.js index 952aca7da..e15519092 100644 --- a/src/public/js/angularjs/controllers/singleTicket.js +++ b/src/public/js/angularjs/controllers/singleTicket.js @@ -173,7 +173,7 @@ define(['angular', 'underscore', 'jquery', 'uikit', 'modules/socket', 'modules/n $scope.priorities = _.sortBy($scope.priorities, 'name'); $scope.selected_priority = _.findWhere($scope.priorities, {_id: $scope.ticketPriority}); if (!$scope.selected_priority) { - alert('Selected Priority does not exit for this ticket type. Please select a new priority'); + UIkit.modal.alert('Selected Priority does not exit for this ticket type.

Please select a new priority'); } }); @@ -199,7 +199,7 @@ define(['angular', 'underscore', 'jquery', 'uikit', 'modules/socket', 'modules/n $scope.priorities = _.sortBy($scope.priorities, 'name'); $scope.selected_priority = _.findWhere($scope.priorities, {_id: $scope.ticketPriority}); if (_.isUndefined($scope.selected_priority)) { - alert('Selected Priority does not exit for this ticket type. Please select a new priority'); + UIkit.modal.alert('Selected Priority does not exit for this ticket type.

Please select a new priority'); } } }; diff --git a/src/public/js/modules/helpers.js b/src/public/js/modules/helpers.js index 2cc0ed99c..5869b7dde 100644 --- a/src/public/js/modules/helpers.js +++ b/src/public/js/modules/helpers.js @@ -397,6 +397,12 @@ function($, _, moment, UIkit, CountUp, Waves, Selectize, Snackbar, ROLES, Cookie }); }; + helpers.UI.reRenderInputs = function() { + $('.md-input').each(function() { + updateInput($(this)); + }); + }; + function updateInput(object) { // clear wrapper classes object.closest('.uk-input-group').removeClass('uk-input-group-danger uk-input-group-success uk-input-group-nocolor'); diff --git a/src/routes/index.js b/src/routes/index.js index f7981dd86..8a6ea1112 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -135,10 +135,17 @@ function mainRoutes(router, middleware, controllers) { router.get('/api/v1/tickets/search', middleware.api, controllers.api.tickets.search); router.post('/api/v1/tickets/create', middleware.api, controllers.api.tickets.create); router.get('/api/v1/tickets/type/:id', middleware.api, controllers.api.tickets.getType); + router.post('/api/v1/tickets/type/:id/removepriority', middleware.api, controllers.api.tickets.typeRemovePriority); + router.post('/api/v1/tickets/type/:id/addpriority', middleware.api, controllers.api.tickets.typeAddPriority); router.get('/api/v1/tickets/types', middleware.api, controllers.api.tickets.getTypes); router.post('/api/v1/tickets/types/create', middleware.api, controllers.api.tickets.createType); router.put('/api/v1/tickets/types/:id', middleware.api, controllers.api.tickets.updateType); router.delete('/api/v1/tickets/types/:id', middleware.api, controllers.api.tickets.deleteType); + router.post('/api/v1/tickets/priority/create', middleware.api, controllers.api.tickets.createPriority); + router.post('/api/v1/tickets/priority/:id/delete', middleware.api, controllers.api.tickets.deletePriority); + router.get('/api/v1/tickets/priorities', middleware.api, controllers.api.tickets.getPriorities); + router.put('/api/v1/tickets/priority/:id', middleware.api, controllers.api.tickets.updatePriority); + router.post('/api/v1/tickets/addtag', middleware.api, controllers.api.tickets.addTag); router.get('/api/v1/tickets/overdue', middleware.api, controllers.api.tickets.getOverdue); router.post('/api/v1/tickets/addcomment', middleware.api, controllers.api.tickets.postComment); diff --git a/src/sass/partials/common.sass b/src/sass/partials/common.sass index e41137fb1..7bc2d60dd 100644 --- a/src/sass/partials/common.sass +++ b/src/sass/partials/common.sass @@ -37,18 +37,31 @@ .nomargin-top, .nm-t margin-top: 0 !important -.marginright20 +.marginright20, +.mr-20 margin-right: 20px !important -.marginright30 +.marginright30, +.mr-30 margin-right: 30px !important -.marginleft5 +.marginleft5, +.ml-5 margin-left: 5px !important -.marginright5 +.marginright5, +.mr-5 margin-right: 5px !important +.mt-5 + margin-top: 5px !important +.mt-10 + margin-top: 10px !important .mb-10 margin-bottom: 10px !important .mb-5 margin-bottom: 5px !important +.mr-10 + margin-right: 10px !important +.mr-15 + margin-right: 15px !important + .padding-left-right-15 padding-left: 15px !important padding-right: 15px !important diff --git a/src/sass/partials/ui.sass b/src/sass/partials/ui.sass index 2c31d7db2..8ee2bf4d8 100644 --- a/src/sass/partials/ui.sass +++ b/src/sass/partials/ui.sass @@ -380,6 +380,16 @@ label.md-label color: white background: lighten($accent_color, 5%) +.uk-color-button + padding: 0 + float: left + display: inline-block + i.material-icons + font-size: 22px + vertical-align: middle + padding: 3px 4px + margin: 0 + .uk-tooltip-left:after display: block !important left: 100% diff --git a/src/settings/defaults.js b/src/settings/defaults.js index 437832530..afe1d879b 100644 --- a/src/settings/defaults.js +++ b/src/settings/defaults.js @@ -258,6 +258,7 @@ function addedDefaultPrioritesToTicketTypes(callback) { .catch(next); }, function(priorities, next) { + priorities = _.sortBy(priorities, 'migrationNum'); var ticketTypeSchema = require('../models/tickettype'); ticketTypeSchema.getTypes(function(err, types) { if (err) return next(err); @@ -267,14 +268,18 @@ function addedDefaultPrioritesToTicketTypes(callback) { if (!type.priorities) { type.priorities = []; prioritiesToAdd = _.map(priorities, '_id'); - } else { - _.each(priorities, function(priority) { - if (!_.find(type.priorities, {'_id': priority._id})) { - winston.debug('Adding default priority %s to ticket type %s', priority.name, type.name); - prioritiesToAdd.push(priority._id); - } - }); + } else if (type.priorities.length < 1) { + type.priorities = []; + prioritiesToAdd = _.map(priorities, '_id'); } + // } else { + // _.each(priorities, function(priority) { + // if (!_.find(type.priorities, {'_id': priority._id})) { + // winston.debug('Adding default priority %s to ticket type %s', priority.name, type.name); + // prioritiesToAdd.push(priority._id); + // } + // }); + // } if (prioritiesToAdd.length < 1) return done(); diff --git a/src/views/partials/addPriorityToTypeWindow.hbs b/src/views/partials/addPriorityToTypeWindow.hbs new file mode 100644 index 000000000..6047b9d70 --- /dev/null +++ b/src/views/partials/addPriorityToTypeWindow.hbs @@ -0,0 +1,18 @@ +
+
+
+ +
+

Add Priorities

+ Please select the priorities you wish to add to type: {{name}} +
+
+ +
+ + +
+
+
\ No newline at end of file diff --git a/src/views/partials/createPriorityWindow.hbs b/src/views/partials/createPriorityWindow.hbs new file mode 100644 index 000000000..72c5743fe --- /dev/null +++ b/src/views/partials/createPriorityWindow.hbs @@ -0,0 +1,50 @@ +
+
+
+
+

Create Priority

+ +
+ +
+
+ +
+
+ + +
+
+ + +
+
+ +
+ + + +
+
+
+ + +
+
+ +
+
\ No newline at end of file diff --git a/src/views/partials/deletePriorityWindow.hbs b/src/views/partials/deletePriorityWindow.hbs new file mode 100644 index 000000000..a01a335c0 --- /dev/null +++ b/src/views/partials/deletePriorityWindow.hbs @@ -0,0 +1,31 @@ +
+
+
+ +
+

Remove Priority

+ Please select the priority you wish to reassign tickets to in order to delete the this priority. +
+
+
+
+ + +
+
+
+ WARNING: This will change all tickets with a priority of: {{name}} to the selected priority above.
This is permanent!
+
+ +
+
+
\ No newline at end of file diff --git a/src/views/partials/deleteTicketTypeWindow.hbs b/src/views/partials/deleteTicketTypeWindow.hbs index 8c715b35a..8933f9c49 100644 --- a/src/views/partials/deleteTicketTypeWindow.hbs +++ b/src/views/partials/deleteTicketTypeWindow.hbs @@ -1,16 +1,18 @@ -
+
-
- + +
- There're currently tickets assigned to this Ticket Type. Please select the ticket type you wish to reassign those tickets to in order to delete the ticket type. +

Remove Ticket Type

+ Please select the ticket type you wish to reassign tickets to in order to delete the ticket type. +
- - + {{#each @root.data.common.ticketTypes}} + {{#isnot name ../name}} {{/isnot}} {{/each}} @@ -18,11 +20,11 @@
- WARNING: This will change all tickets with type: {{data.tickettype.name}} to the selected ticket type above.
This is permanent!
+ WARNING: This will change all tickets with type: {{name}} to the selected ticket type above.
This is permanent!
diff --git a/src/views/partials/nav.hbs b/src/views/partials/nav.hbs index 1845c7148..d21079813 100644 --- a/src/views/partials/nav.hbs +++ b/src/views/partials/nav.hbs @@ -113,13 +113,10 @@
  • styleLegal
  • -
  • styleTags
  • -
  • text_fieldsTicket Types
  • {{#canUser data.user "settings:logs"}}
  • remove_from_queueLogs
  • {{/canUser}} -
  • gavelLegal
  • {{/canUser}} diff --git a/src/views/settings.hbs b/src/views/settings.hbs index 5aec60832..d4ffaddb8 100644 --- a/src/views/settings.hbs +++ b/src/views/settings.hbs @@ -1,4 +1,4 @@ -
    -
    -
    -

    {{title}}

    +
    +
    +
    +

    Settings

    +
    +
    - -
    -
    -
    -
    -
    -
    Default Ticket Type
    -
    - Default ticket type for newly created tickets. -
    -
    -
    -
    - +
    +
      +
    • +
      +

      General

      +
      +
    • +
    • +
      +

      Tickets

      +
      +
    • +
    • +
      +

      Mailer

      +
      +
    • +
    • +
      +

      Notifications

      +
      +
    • +
    • +
      +

      Push Service

      +
      +
    • +
    • +
      +

      Legal

      +
      +
    • +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    Show Tour - Beta
    +
    + Tour the user interface on initial login. +
    +
    +
    + +
    + +
    -
    -
    -
    -
    -
    Show Tour - Beta
    -
    - Tour the user interface on initial login. -
    -
    -
    - -
    - +
    +
    +
    +
    Allow User Registration
    +
    + Allow users to create accounts on the login screen. +
    +
    +
    + +
    + +
    -
    -
    -
    -
    Show Overdue Tickets
    -
    - Flash tickets that have been idle for a length of time. (Configure Priorities) -
    -
    -
    - -
    - +
    +
    +
    +
    +
    + Default Ticket Type +
    +
    + Default ticket type for newly created tickets. +
    +
    +
    +
    + +
    -
    -
    -
    -
    -
    Allow Public Tickets
    -
    - Allow the creation of tickets by users that are unregistered. ({{data.common.hosturl}}/newissue) -
    -
    -
    - -
    - +
    +
    +
    +
    Allow Public Tickets
    +
    + Allow the creation of tickets by users that are unregistered. ({{data.common.hosturl}}/newissue) +
    +
    +
    + +
    + +
    -
    -
    -
    -
    -
    Allow User Registration
    -
    - Allow users to create accounts on the login screen. -
    -
    -
    - -
    - +
    +
    +
    +
    Show Overdue Tickets + help +
    +
    + Enable/Disable flashing of tickets based on SLA time of type priority. +
    +
    +
    + +
    + +
    -
    -
    -
    -
    -
    Mailer
    -
    - Preferences for trudesk to send email notifications to users. -
    -
    -
    - -
    - + +
    +
    +
    +
    Ticket Types
    +
    + Create / Modify Ticket Types +
    -
    -
    -
    -
    -
    -
    -
    - -
    +
    +
    + +
    +
    +
    +
    +
    +
    +
      + {{#forEach data.ticketTypes}} +
    • +
      +

      {{name}}

      +
      +
    • + {{/forEach}} +
    - - +
    + {{#each data.ticketTypes}} +
    +
    +

    General

    +
    +
    +
    +
    + + +
    + + + +
    +
    +
    + +
    +

    Priorities + help +

    +
    + +
    +
    +
    + {{#forEach priorities}} +
    +
    +
    {{name}}
    +

    SLA Overdue: {{durationFormat overdueIn 'minutes'}}

    +
    +
    +
    + Edit + Remove +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    + + +
    +
    + +
    + + + +
    +
    +
    +
    + Cancel + +
    +
    +
    +
    +
    + {{/forEach}} + + + + + + + + + + + + + + + + + + +
    +
    + +
    +

    Danger Zone

    +
    +
    +
    +
    Delete this type
    +

    Once you delete a ticket type, there is no going back. Please be certain.

    +
    +
    + +
    +
    +
    +
    +
    + {{> addPriorityToTypeWindow}} + {{> deleteTicketTypeWindow}} + {{/each}} +
    -
    - - -
    -
    - - -
    -
    - - +
    +
    + {{> createTicketTypeWindow}} +
    + + +
    +
    +
    +
    Ticket Priorities
    +
    + Ticket priorities set the level of SLAs for each ticket. +
    +
    +
    +
    +
    -
    - - +
    +
    +
    +
    +
    + {{#each data.priorities}} +
    +
    +
    {{name}}
    +

    SLA Overdue: {{durationFormat overdueIn 'minutes'}}

    +
    +
    +
    + Edit + Remove +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    + + +
    +
    + +
    + + + +
    +
    +
    +
    + Cancel + +
    +
    +
    +
    +
    + + {{> deletePriorityWindow}} + {{/each}} +
    +
    +
    -
    - - + {{> createPriorityWindow}} +
    + +
    +
    +
    +
    Ticket Tags
    +
    + Create / Modify Ticket Tags +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + +
    + +
    +
    -
    -
    -
    -
    Mailer Check
    -
    - Trudesk will periodically check a mailbox for emails to convert to tickets. (Over IMAP) - Settings are applied after server restart -
    -
    -
    - -
    - + +
    +
    +
    +
    +
    Mailer
    +
    + Preferences for trudesk to send email notifications to users. +
    -
    -
    -
    -
    -
    - - +
    + +
    +
    -
    - - +
    +
    +
    +
    +
    +
    +
    + +
    +
    + + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    + +
    + + +
    -
    - - +
    +
    +
    +
    +
    +
    +
    Mailer Check
    +
    + Trudesk will periodically check a mailbox for emails to convert to tickets. (Over IMAP) - Settings are applied after server restart +
    +
    +
    + +
    +
    -
    - - +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + +
    -
    - - +
    +
    +
    +
    + +
    + +
    + +
    +
    +
    +
    +
    TPS
    +
    + Trudesk Push Server authentication for push notifications. Get API Key +
    +
    +
    + +
    +
    -
    - +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    + +
    -
    +
    -
    TPS
    +
    Privacy Policy
    - Trudesk Push Server authentication for push notifications. Get API Key + Paste in HTML / Text of your privacy policy.
    - -
    - -
    -
    +
    -
    -
    - - -
    +
    - - +
    - +
    -
    diff --git a/src/views/subviews/settings/settings-tickets.hbs b/src/views/subviews/settings/settings-tickets.hbs deleted file mode 100644 index 90bf26d3d..000000000 --- a/src/views/subviews/settings/settings-tickets.hbs +++ /dev/null @@ -1,474 +0,0 @@ -
    -
    -
    -
    -

    Settings

    -
    - -
    - -
    -
      -
    • -
      -

      General

      -
      -
    • -
    • -
      -

      Tickets

      -
      -
    • -
    • -
      -

      Mailer

      -
      -
    • -
    • -
      -

      Notifications

      -
      -
    • -
    • -
      -

      Push Service

      -
      -
    • -
    • -
      -

      Legal

      -
      -
    • -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    Show Tour - Beta
    -
    - Tour the user interface on initial login. -
    -
    -
    - -
    - -
    -
    -
    -
    - -
    -
    -
    -
    Allow User Registration
    -
    - Allow users to create accounts on the login screen. -
    -
    -
    - -
    - -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    - Default Ticket Type -
    -
    - Default ticket type for newly created tickets. -
    -
    -
    -
    - -
    -
    -
    -
    - -
    -
    -
    -
    Allow Public Tickets
    -
    - Allow the creation of tickets by users that are unregistered. ({{data.common.hosturl}}/newissue) -
    -
    -
    - -
    - -
    -
    -
    -
    - -
    -
    -
    -
    Show Overdue Tickets
    -
    - Flash tickets that have been idle for a length of time. -
    -
    -
    - -
    - -
    -
    -
    -
    - -
    -
    -
    -
    Ticket Types
    -
    - Create / Modify Ticket Types -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
      - {{#forEach data.ticketTypes}} -
    • -
      -

      {{name}}

      -
      -
    • - {{/forEach}} -
    -
    -
    - {{#each data.ticketTypes}} -
    -
    -

    General

    -
    - -
    -
    -
    - - -
    - - - -
    -
    -
    - -
    -

    Priorities - help -

    -
    - -
    -
    -
    - {{#forEach priorities}} -
    -
    -
    {{name}}
    -

    SLA Overdue: {{durationFormat overdueIn 'minutes'}}

    -
    -
    -
    - Edit - Remove -
    -
    -
    - {{/forEach}} - - - - - - - - - - - - - - - - - - -
    -
    - -
    -

    Danger Zone

    -
    -
    -
    -
    Delete this type
    -

    Once you delete a ticket type, there is no going back. Please be certain.

    -
    -
    - -
    -
    -
    -
    -
    - {{/each}} -
    -
    -
    -
    - {{> createTicketTypeWindow}} -
    - -
    - -
    -
    -
    -
    -
    Mailer
    -
    - Preferences for trudesk to send email notifications to users. -
    -
    -
    - -
    - -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    - - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    - -
    - - -
    -
    -
    -
    -
    -
    -
    -
    -
    Mailer Check
    -
    - Trudesk will periodically check a mailbox for emails to convert to tickets. (Over IMAP) - Settings are applied after server restart -
    -
    -
    - -
    - -
    -
    -
    -
    -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - -
    -
    -
    -
    -
    -
    - -
    - -
    - -
    -
    -
    -
    -
    TPS
    -
    - Trudesk Push Server authentication for push notifications. Get API Key -
    -
    -
    - -
    - -
    -
    -
    -
    -
    -
    - - -
    -
    - - -
    -
    - -
    -
    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    -
    - -{{#contentFor 'js-plugins'}} - -{{/contentFor}} \ No newline at end of file diff --git a/src/views/tickets.hbs b/src/views/tickets.hbs index f5a294472..79bbd5f31 100644 --- a/src/views/tickets.hbs +++ b/src/views/tickets.hbs @@ -116,7 +116,7 @@ {{#each data.tickets}} {{#if_eq status compare=0}} - + @@ -144,7 +144,7 @@ {{/if_eq}} {{#is status 1}} - +