From 24f4acc1d3b0af33b4e6739134a3eccbf6787d53 Mon Sep 17 00:00:00 2001 From: Dobie Wollert Date: Sun, 19 Jul 2015 21:31:10 -0400 Subject: [PATCH] latest changes --- app/controllers/devices.js | 132 +++++++++++++++ app/model/device.js | 16 ++ app/views/index.jade | 4 + cli.js | 17 ++ config/routes.js | 10 ++ public/js/app.js | 13 ++ public/js/controllers.js | 251 ++++++++++++++++++++++++++++ public/js/directives.js | 10 +- public/js/services.js | 14 ++ public/partials/clients/edit.html | 10 +- public/partials/devices/add.html | 80 +++++++++ public/partials/devices/edit.html | 80 +++++++++ public/partials/devices/index.html | 41 +++++ public/partials/schedule/index.html | 4 +- public/partials/schedule/pms.html | 2 +- 15 files changed, 671 insertions(+), 13 deletions(-) create mode 100644 app/controllers/devices.js create mode 100644 app/model/device.js create mode 100644 cli.js create mode 100644 public/partials/devices/add.html create mode 100644 public/partials/devices/edit.html create mode 100644 public/partials/devices/index.html diff --git a/app/controllers/devices.js b/app/controllers/devices.js new file mode 100644 index 0000000..c3bae86 --- /dev/null +++ b/app/controllers/devices.js @@ -0,0 +1,132 @@ +var mongoose = require('mongoose'), + Device = mongoose.model('Device'); + +var _ = require('lodash'); +var md5 = require('MD5'); + +var log = require('log4node'); + +exports.index = function(req, res) { + log.info('devices.index'); + var query = Device.find({ deleted: false }) + .exec(function(err, results) { + if (err) { + res.json(500, err); + } else { + res.json(results); + } + }); +}; + +exports.get = function(req, res, next) { + var id = req.param('device_id'); + + log.info("devices.get %s", id); + Device.findById(id) + .exec(function(err, device) { + if (err) return next(err); + if (!device) return next(new Error('failed to load device ' + id)); + + res.json(device); + }); +}; + +exports.deviceTypes = function(req, res, next) { + log.info("devices.deviceTypes"); + + var query = Device.find({ deleted: false }) + .select('deviceType') + .exec(function(err, results) { + if (err) { + res.json(500, err); + } else { + res.json(_.uniq(_.pluck(results, 'deviceType'))); + } + }); +}; + +exports.makes = function(req, res, next) { + log.info("devices.makes"); + + var query = Device.find({ deleted: false }) + .select('make') + .exec(function(err, results) { + if (err) { + res.json(500, err); + } else { + res.json(_.uniq(_.pluck(results, 'make'))); + } + }); +}; + +exports.models = function(req, res, next) { + log.info("devices.models"); + + var query = Device.find({ deleted: false }) + .select('model') + .exec(function(err, results) { + if (err) { + res.json(500, err); + } else { + res.json(_.uniq(_.pluck(results, 'model'))); + } + }); +}; + +exports.create = function(req, res, next) { + log.info("devices.create %j", res.body); + + var device = new Device({ + deviceType: req.body.deviceType, + make: req.body.make, + model: req.body.model, + technicalData: req.body.technicalData, + links: req.body.links, + partsRecommended: req.body.partsRecommended, + images: req.body.images + }); + + return device.save(function(err) { + if (err) + log.error("Error: %s", err); + return res.json(device); + }); +}; + +exports.update = function(req, res, next) { + var id = req.param('device_id'); + log.info('devices.update %s %j', id, req.body); + + return Device.findById(id, function(err, device) { + device.deviceType = req.body.deviceType; + device.make = req.body.make; + device.model = req.body.model; + device.technicalData = req.body.technicalData; + device.links = req.body.links; + device.partsRecommended = req.body.partsRecommended; + device.images = req.body.images; + + return device.save(function(err) { + if (err) + log.error("Error: %s", err); + return res.json(device); + }); + }); +}; + +exports.upload = function(req, res, next) { + var path = req.files.file.path; + + fs.readFile(path, function(err, data) { + var hash = md5(data); + + fs.writeFile('/srv/biomed-site/images/devices/' + hash, data, function(err) { + if (err) + log.error("Error: %s", err); + + return res.json({ + filename: hash + }); + }); + }); +}; diff --git a/app/model/device.js b/app/model/device.js new file mode 100644 index 0000000..244aff5 --- /dev/null +++ b/app/model/device.js @@ -0,0 +1,16 @@ +var mongoose = require('mongoose') + Schema = mongoose.Schema, + ObjectId = Schema.ObjectId; + +var deviceSchema = new Schema({ + deviceType: String, + make: String, + model: String, + technicalData: String, + links: String, + partsRecommended: String, + images: [{ type: String }], + deleted: { type: Boolean, default: false } +}); + +module.exports = mongoose.model('Device', deviceSchema); diff --git a/app/views/index.jade b/app/views/index.jade index 854ecf8..e847859 100644 --- a/app/views/index.jade +++ b/app/views/index.jade @@ -60,6 +60,10 @@ html(lang="en", ng-app="biomed", ng-controller="PageCtrl") a(href='/accounting') | Accounting + li(data-match-route='/devices.*') + a(href='/devices') + | Devices + li(data-match-route='/posts.*', ng-show="accountHasPermission('system.admin')") a(href='/posts') | Posts diff --git a/cli.js b/cli.js new file mode 100644 index 0000000..580befb --- /dev/null +++ b/cli.js @@ -0,0 +1,17 @@ +var fs = require('fs'); + +var env = 'prod', + config = require('./config/config')[env], + mongoose = require('mongoose'), + Promise = require('bluebird'); + +// bootstrap db connection +mongoose.set('debug', config.debug); +mongoose.connect(config.database); + +// bootstrap model +var modelPath = __dirname + '/app/model' +fs.readdirSync(modelPath).forEach(function (file) { + require(modelPath + '/' + file) +}) + diff --git a/config/routes.js b/config/routes.js index 47498a0..7297478 100644 --- a/config/routes.js +++ b/config/routes.js @@ -58,6 +58,16 @@ module.exports = function(app, auth, piler, calendar, directory, config) { app.post('/api/workorders/:workorder_id', workorders.update); app.del('/api/workorders/:workorder_id', workorders.destroy); + var devices = require('../app/controllers/devices'); + app.get('/api/devices', devices.index); + app.get('/api/devices/deviceTypes', devices.deviceTypes); + app.get('/api/devices/makes', devices.makes); + app.get('/api/devices/models', devices.models); + app.post('/api/devices/images', devices.upload); + app.get('/api/devices/:device_id', devices.get); + app.post('/api/devices', devices.create); + app.post('/api/devices/:device_id', devices.update); + var pms = require('../app/controllers/pms'); app.get('/api/pms', pms.index); diff --git a/public/js/app.js b/public/js/app.js index 1f358cc..1ad432e 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -61,6 +61,19 @@ angular.module('biomed', ['biomed.filters', 'biomed.services', 'biomed.directive templateUrl: '/partials/clients/edit.html', controller: "ClientEditCtrl" }) + .when('/devices', { + templateUrl: '/partials/devices/index.html', + controller: "DeviceIndexCtrl", + reloadOnSearch: false + }) + .when('/devices/add', { + templateUrl: '/partials/devices/add.html', + controller: "DeviceAddCtrl" + }) + .when('/devices/:id', { + templateUrl: '/partials/devices/edit.html', + controller: "DeviceEditCtrl" + }) .when('/accounting', { templateUrl: '/partials/accounting/index.html', controller: "AccountingIndexCtrl", diff --git a/public/js/controllers.js b/public/js/controllers.js index 20ef6bd..74e658a 100644 --- a/public/js/controllers.js +++ b/public/js/controllers.js @@ -705,6 +705,257 @@ angular.module('biomed') } }) +.controller("DeviceIndexCtrl", function($scope, $filter, $routeParams, Devices, LocationBinder) { + $scope.loading = true; + + var allData = Devices.index(function() { + $scope.loading = false; + $scope.filter(); + }); + + var filteredData = []; + var index = 0; + var initialPageSize = 100; + var pageSize = 5; + + $scope.canLoad = true; + + $scope.$watch('query', function() { + $scope.filter(); + }); + + LocationBinder($scope, ['query']); + + $scope.filter = function() { + filteredData = $filter('orderBy')($filter('filter')(allData, $scope.query), $scope.sort.column, $scope.sort.descending); + index = initialPageSize; + $scope.canLoad = true; + $scope.devices = filteredData.slice(0, initialPageSize); + }; + + $scope.addItems = function() { + $scope.devices = $scope.devices.concat(filteredData.slice(index, index + pageSize)); + index += pageSize; + $scope.canLoad = index < filteredData.length; + } + + $scope.sort = { + column: 'deviceType', + descending: false + }; + + $scope.selectedCls = function(column) { + return column == $scope.sort.column && 'sort-' + $scope.sort.descending; + } + + $scope.changeSorting = function(column) { + var sort = $scope.sort; + if (sort.column == column) { + sort.descending = !sort.descending; + } else { + sort.column = column; + sort.descending = false; + } + + $scope.filter(); + }; +}) + +.controller("DeviceAddCtrl", function($scope, Devices, $location, $filter) { + $scope.model = {}; + + $scope.deviceTypes = Devices.deviceTypes(); + $scope.deviceMakes = Devices.makes(); + + $scope.deviceTypeOpts = { + containerCssClass: 'input-xxlarge', + placeholder: 'Choose a Device Type', + query: function(query) { + var data = $filter('filter')($scope.deviceTypes, query.term); + var results = []; + data.forEach(function(item) { + results.push({id: item, text: item}); + }); + query.callback({results: results }); + }, + createSearchChoice: function(term) { + return { id: term, text: term }; + } + }; + + $scope.makeOpts = { + containerCssClass: 'input-xxlarge', + placeholder: 'Choose a Device Make', + query: function(query) { + var data = $filter('filter')($scope.deviceMakes, query.term); + var results = []; + data.forEach(function(item) { + results.push({id: item, text: item}); + }); + query.callback({results: results }); + }, + createSearchChoice: function(term) { + return { id: term, text: term }; + } + }; + + var images = {}; + + $scope.imageOpts = { + options: { + url: '/api/devices/images', + addRemoveLinks: true + }, + eventHandlers: { + success: function(file, response) { + file.filename = response.filename; + + if (images[file.filename]) { + images[file.filename]++; + this.removeFile(file); + } else { + images[file.filename] = 1; + } + }, + removedfile: function(file) { + images[file.filename]--; + + if (images[file.filename] <= 0) { + delete images[file.filename]; + } + } + } + }; + + $scope.$watch('deviceTypePicker', function() { + if ($scope.deviceTypePicker) { + $scope.model.deviceType = $scope.deviceTypePicker.id; + } else { + $scope.model.deviceType = null; + } + }); + + $scope.$watch('makePicker', function() { + if ($scope.makePicker) { + $scope.model.make = $scope.makePicker.id; + } else { + $scope.model.make = null; + } + }); + + $scope.save = function() { + $scope.model.images = Object.keys(images); + + Devices.create($scope.model, function(result) { +// $location.path("/devices/" + result._id); + $location.path("/devices/"); + }); + }; +}) + +.controller("DeviceEditCtrl", function($scope, Devices, $location, $filter, $routeParams) { + var images = {}; + + $scope.model = Devices.get($routeParams, function() { + $scope.loading = false; + + $scope.existingImages = $scope.model.images; + if ($scope.model.images) { + for (var i = 0; i < $scope.model.images.length; i++) { + images[$scope.model.images[i]] = 1; + } + } + + $scope.deviceTypePicker = {id: $scope.model.deviceType, text: $scope.model.deviceType}; + $scope.makePicker = {id: $scope.model.make, text: $scope.model.make}; + }); + + $scope.deviceTypes = Devices.deviceTypes(); + $scope.deviceMakes = Devices.makes(); + + $scope.deviceTypeOpts = { + containerCssClass: 'input-xxlarge', + placeholder: 'Choose a Device Type', + query: function(query) { + var data = $filter('filter')($scope.deviceTypes, query.term); + var results = []; + data.forEach(function(item) { + results.push({id: item, text: item}); + }); + query.callback({results: results }); + }, + createSearchChoice: function(term) { + return { id: term, text: term }; + } + }; + + $scope.makeOpts = { + containerCssClass: 'input-xxlarge', + placeholder: 'Choose a Device Make', + query: function(query) { + var data = $filter('filter')($scope.deviceMakes, query.term); + var results = []; + data.forEach(function(item) { + results.push({id: item, text: item}); + }); + query.callback({results: results }); + }, + createSearchChoice: function(term) { + return { id: term, text: term }; + } + }; + + $scope.imageOpts = { + options: { + url: '/api/devices/images', + addRemoveLinks: true + }, + eventHandlers: { + success: function(file, response) { + file.filename = response.filename; + + if (images[file.filename]) { + images[file.filename]++; + this.removeFile(file); + } else { + images[file.filename] = 1; + } + }, + removedfile: function(file) { + images[file.filename]--; + + if (images[file.filename] <= 0) { + delete images[file.filename]; + } + } + } + }; + + $scope.$watch('deviceTypePicker', function() { + if ($scope.deviceTypePicker) { + $scope.model.deviceType = $scope.deviceTypePicker.id; + } else { + $scope.model.deviceType = null; + } + }); + + $scope.$watch('makePicker', function() { + if ($scope.makePicker) { + $scope.model.make = $scope.makePicker.id; + } else { + $scope.model.make = null; + } + }); + + $scope.save = function() { + $scope.model.images = Object.keys(images); + + Devices.update({id: $scope.model._id}, $scope.model, function(result) { + $location.path("/devices/"); + }); + }; +}) + .controller("AccountingIndexCtrl", function($scope, $filter, $routeParams, Workorders, LocationBinder) { $scope.loading = true; diff --git a/public/js/directives.js b/public/js/directives.js index 3ec6128..363025c 100644 --- a/public/js/directives.js +++ b/public/js/directives.js @@ -807,7 +807,8 @@ angular.module('biomed.directives', []) return { scope: { dropzone: '=', - existing: '=' + existing: '=', + prefix: '@' }, controller: function($scope, $element, $attrs) { var config, dropzone; @@ -821,13 +822,16 @@ angular.module('biomed.directives', []) $scope.$watch('existing', function() { var existing = $scope.existing; - console.log(dropzone); + var prefix = "http://atlanticbiomedical.com/images/"; + if ($scope.prefix) { + prefix = prefix + $scope.prefix; + } if (existing) { for (var i = 0; i < existing.length; i++) { var file = { name: existing[i], size: 0, accepted: true, filename: existing[i] }; dropzone.options.addedfile.call(dropzone, file); - dropzone.options.thumbnail.call(dropzone, file, "http://atlanticbiomedical.com/images/" + existing[i]); + dropzone.options.thumbnail.call(dropzone, file, prefix + existing[i]); dropzone.files.push(file); } } diff --git a/public/js/services.js b/public/js/services.js index 652a691..f71f4f5 100644 --- a/public/js/services.js +++ b/public/js/services.js @@ -14,6 +14,20 @@ angular.module('biomed.services', []) isUnique: { method: 'GET', params: { cmd: 'isUnique' } }, }); }) +.factory("Devices", function($resource) { + return $resource('/api/devices/:id/:cmd', + { id: "@id", cmd: "@cmd" }, + { + index: { method: 'GET', params: {}, isArray: true }, + deviceTypes: { method: 'GET', params: { cmd: 'deviceTypes' }, isArray: true }, + makes: { method: 'GET', params: { cmd: 'makes' }, isArray: true }, + models: { method: 'GET', params: { cmd: 'models' }, isArray: true }, + get: { method: 'GET', params: { id: 0} }, + create: { method: 'POST', params: {} }, + update: { method: 'POST', params: { id: 0} }, + destroy: { method: 'DELETE', params: { id: 0 } }, + }); +}) .factory("Posts", function($resource) { return $resource('/api/posts/:id', { id: "@id" }, diff --git a/public/partials/clients/edit.html b/public/partials/clients/edit.html index 8465eca..7ccaaeb 100644 --- a/public/partials/clients/edit.html +++ b/public/partials/clients/edit.html @@ -6,8 +6,8 @@

{{master.name}}

{{master.identifier}}

- Create new Workorder - Create new Meeting + Work Order + Meeting
@@ -305,9 +305,7 @@ Purchase Date Warranty Expiration PM Test - Room # - PO Number - Move to + Location @@ -322,8 +320,6 @@ {{tag.data.deviceWarrantyExpiration}} {{tag.data.test}} {{tag.data.roomNumber}} - {{tag.data.poNumber}} - {{tag.data.MoveTo}} diff --git a/public/partials/devices/add.html b/public/partials/devices/add.html new file mode 100644 index 0000000..a9cf62f --- /dev/null +++ b/public/partials/devices/add.html @@ -0,0 +1,80 @@ + +
+

New Device

+
+ +
+
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+ +
+ +
+ +
+
+
diff --git a/public/partials/devices/edit.html b/public/partials/devices/edit.html new file mode 100644 index 0000000..d648af0 --- /dev/null +++ b/public/partials/devices/edit.html @@ -0,0 +1,80 @@ + +
+

Edit Device

+
+ +
+
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+ +
+ +
+ +
+
+
diff --git a/public/partials/devices/index.html b/public/partials/devices/index.html new file mode 100644 index 0000000..7e4fe94 --- /dev/null +++ b/public/partials/devices/index.html @@ -0,0 +1,41 @@ + +
+

Devices

+
+ +
+
+
+ Create new Device +
+ Search: +
+ + +
+
+
+ + + + + + + + + + + + + + + + + + + +
Device TypeMakeModel
There is no information to display.
{{device.deviceType}}{{device.make}}{{device.model}}Edit
+
+
diff --git a/public/partials/schedule/index.html b/public/partials/schedule/index.html index 58b589c..6935fd5 100644 --- a/public/partials/schedule/index.html +++ b/public/partials/schedule/index.html @@ -8,8 +8,8 @@
- Create new Workorder - Create new Meeting + Work Order + Meeting
Group:
diff --git a/public/partials/schedule/pms.html b/public/partials/schedule/pms.html index 5f44d6d..e833f3e 100644 --- a/public/partials/schedule/pms.html +++ b/public/partials/schedule/pms.html @@ -9,7 +9,7 @@