// Code goes here
angular.module('brokenResize', ['ionic'])
.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('wodetail', {
url: '/wodetail',
templateUrl: 'wodetail.html',
controller: 'WoDetailCtrl'
});
$urlRouterProvider.otherwise('/wodetail');
})
.run(function ($ionicPlatform, $rootScope, $location) {
$ionicPlatform.ready(function() {
// Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
// for form inputs)
if (window.cordova && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
cordova.plugins.Keyboard.disableScroll(true);
}
if (window.StatusBar) {
// org.apache.cordova.statusbar required
StatusBar.styleDefault();
}
});
})
.controller('WoDetailCtrl', function($scope, Controller, $state, $ionicPopover) {
var wo = {"_id":5000,"costSummary":"$325.59","customer":{"_id":"55ce79b5925f5f880cac4abe","name":"Microsoft","__v":0,"status":"active","site":{"list":[{"name":"Shipping Office","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ae8","inspections":[],"equipment":[]},{"name":"Shipping Office 2","address1":"1400 Old Dominion Dr","city":"Reston","state":"MD","zip":"20164","_id":"55ce79b5925f5f880cac4ae7","inspections":[],"equipment":[]},{"name":"Shipping Office 3","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ae6","inspections":[],"equipment":[]},{"name":"Shipping Office 4","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ae5","inspections":[],"equipment":[]},{"name":"Shipping Office 5","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ae4","inspections":[],"equipment":[]},{"name":"Shipping Office 6","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ae3","inspections":[],"equipment":[]},{"name":"Shipping Office 7","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ae2","inspections":[],"equipment":[]},{"name":"Shipping Office 8","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ae1","inspections":[],"equipment":[]},{"name":"Shipping Office 9","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ae0","inspections":[],"equipment":[]},{"name":"Shipping Office 10","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4adf","inspections":[],"equipment":[]},{"name":"Shipping Office 11","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ade","inspections":[],"equipment":[]},{"name":"Shipping Office 12","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4add","inspections":[],"equipment":[]},{"name":"Shipping Office 13","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4adc","inspections":[],"equipment":[]},{"name":"Shipping Office 14","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4adb","inspections":[],"equipment":[]},{"name":"Shipping Office 15","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ada","inspections":[],"equipment":[]},{"name":"Shipping Office 16","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ad9","inspections":[],"equipment":[]},{"name":"Shipping Office 17","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ad8","inspections":[],"equipment":[]},{"name":"Shipping Office 18","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ad7","inspections":[],"equipment":[]},{"name":"Shipping Office 19","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ad6","inspections":[],"equipment":[]},{"name":"Shipping Office 20","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ad5","inspections":[],"equipment":[]},{"name":"Shipping Office 21","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ad4","inspections":[],"equipment":[]},{"name":"Shipping Office 22","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ad3","inspections":[],"equipment":[]},{"name":"Shipping Office 23","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ad2","inspections":[],"equipment":[]},{"name":"Shipping Office 24","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ad1","inspections":[],"equipment":[]},{"name":"Shipping Office 25","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ad0","inspections":[],"equipment":[]},{"name":"Shipping Office 26","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4acf","inspections":[],"equipment":[]},{"name":"Shipping Office 27","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ace","inspections":[],"equipment":[]},{"name":"Shipping Office 28","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4acd","inspections":[],"equipment":[]},{"name":"Shipping Office 29","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4acc","inspections":[],"equipment":[]},{"name":"Shipping Office 30","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4acb","inspections":[],"equipment":[]},{"name":"Shipping Office 31","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4aca","inspections":[],"equipment":[]},{"name":"Shipping Office 32","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ac9","inspections":[],"equipment":[]},{"name":"Shipping Office 33","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ac8","inspections":[],"equipment":[]},{"name":"Shipping Office 34","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ac7","inspections":[],"equipment":[]},{"name":"Shipping Office 35","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ac6","inspections":[],"equipment":[]},{"name":"Shipping Office 36","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ac5","inspections":[],"equipment":[]},{"name":"Shipping Office 37","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ac4","inspections":[],"equipment":[]},{"name":"Shipping Office 38","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ac3","inspections":[],"equipment":[]},{"name":"Shipping Office 39","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ac2","inspections":[],"equipment":[]}]},"contact":{"list":[{"name":"Dave Bryant","phone":"+1 (703) 471-1316","email":"dbryant@microsoft.com","_id":"55ce79b5925f5f880cac4ac1"},{"name":"Adam Smith","phone":"+1 (703) 282-4493","email":"asmith@microsoft.com","_id":"55ce79b5925f5f880cac4ac0"},{"name":"Bill Roberts","phone":"+1 (703) 882-9943","email":"broberts@microsoft.com","_id":"55ce79b5925f5f880cac4abf"}]}},"site":{"name":"Shipping Office","address1":"1400 Old Dominion Dr","city":"Reston","state":"VA","zip":"20164","_id":"55ce79b5925f5f880cac4ae8","inspections":[],"equipment":[]},"contact":{"name":"Adam Smith","phone":"+1 (703) 282-4493","email":"asmith@microsoft.com","_id":"55ce79b5925f5f880cac4ac0"},"description":"test","__v":0,"data":{"parts":[{"price":{"discount":0.1,"final":6.84},"fromDb":{"number":"V50073SBI-101","description":"3/4 X 1/2 ADAPTER ELBOW","listPrice":1.52,"_id":"55d88154f052d5f800adec34"},"description":"3/4 X 1/2 ADAPTER ELBOW","quantity":5}],"labor":[{"price":{"discount":0.25,"final":318.75},"tech":{"_id":"55ce79b5925f5f880cac4afa","provider":"local","email":"test@test.com","userType":"employee","company":{"name":"Freedom Fire Protection LLC","phone":{"primary":"(703) 754-2445"},"address":{"line1":"3510 Mountain Road","city":"Haymarket","state":"VA","zip":"20169"}},"__v":0,"role":"tech","name":{"first":"Test","last":"User","full":"Test User"}},"type":{"name":"Technician","amount":85,"_id":"55ce79b5925f5f880cac4aed"},"hours":5}]},"salesman":{"name":"test"},"invoice":{"to":"test","invoiced":false},"po":{"number":"test"},"discount":{"percentage":0},"pricing":{"name":"T&M","amount":"$0.00"},"woType":{"name":"Install"},"tech":{"_id":"55ce79b5925f5f880cac4afa","email":"test@test.com","userType":"employee","company":{"name":"Freedom Fire Protection LLC","phone":{"primary":"(703) 754-2445"},"address":{"line1":"3510 Mountain Road","city":"Haymarket","state":"VA","zip":"20169"}},"role":"tech","name":{"first":"Test","last":"User","full":"Test User"}},"scheduled":{},"status":{"name":"Returned"},"createDateTime":"2015-08-18T03:01:11.249Z"};
// Local methods
var methods = Controller.woDetail($scope);
// Scope models
$scope.wo = wo;
$scope.partSearch = {
description: ''
};
$scope.switch = {
accordion: {
customer: false,
details: false,
parts: false,
labor: false,
signatures: false
}
};
$scope.bak = {
signature: {}
};
// Scope methods
$scope.createPopover = function (state, scope) {
$ionicPopover.fromTemplateUrl('popover-' + state + '.html', {
scope: scope
}).then(function (popover) {
$scope[state + 'Popover'] = popover;
$scope.openPopover = function ($event) {
$scope[state + 'Popover'].show($event);
};
$scope.closePopover = function () {
$scope[state + 'Popover'].hide();
};
});
};
// Constructors
methods.initialWoData();
$scope.createPopover('wodetail', $scope);
for (var method in methods) {
$scope[method] = methods[method];
}
})
.factory('Controller', function ($timeout, $state, $rootScope, $ionicPopover) {
return {
// WoDetailCtrl service
woDetail: function ($scope) {
var ctrl = this;
ctrl.scope = $scope || {};
return {
initialWoData: function () {
if (!ctrl.scope.wo.data) {
ctrl.scope.wo.data = {
parts: [],
labor: []
};
}
},
partSearchModalOpen: function () {
if (ctrl.scope.partSearch.description) {
ctrl.scope.partSearchModal.show();
} else {
ctrl.scope.switch.partSearchRequired = true;
}
},
partSearchVal: function () {
if (ctrl.scope.partSearch.description) {
ctrl.scope.switch.partSearchRequired = false;
}
},
addPart: function (part) {
var p = {price: {discount: 0}};
ctrl.scope.switch.addingPart = true;
$timeout(function () {
ctrl.scope.switch.addingPart = false;
}, 100);
p.fromDb = part;
p.description = part.description;
ctrl.scope.partSearchModal.hide();
$timeout(function () {
ctrl.scope.wo.data.parts.push(p);
}, 50);
},
backupSignature: function (type) {
if (ctrl.scope.wo.data.signature &&
ctrl.scope.wo.data.signature[type] &&
ctrl.scope.wo.data.signature[type].signature) {
ctrl.scope.bak.signature[type] = ctrl.scope.wo.data.signature[type].signature;
}
ctrl.scope.switch.signature[type] = !ctrl.scope.switch.signature[type];
if (ctrl.scope.wo.data.signature &&
ctrl.scope.wo.data.signature[type] &&
ctrl.scope.wo.data.signature[type].signature) {
ctrl.scope.wo.data.signature[type].signature = null;
}
},
undoSignature: function (type) {
ctrl.scope.wo.data.signature[type].signature = ctrl.scope.bak.signature[type];
ctrl.scope.bak.signature[type] = null;
ctrl.scope.switch.signature[type] = !ctrl.scope.switch.signature[type];
},
setSignature: function (type) {
ctrl.scope.switch.signature[type] = !ctrl.scope.switch.signature[type];
ctrl.scope.bak.signature[type] = null;
},
expandCollapse: function () {
ctrl.scope.switch.accordion.expanded = !ctrl.scope.switch.accordion.expanded;
if (ctrl.scope.switch.accordion.expanded) {
for (var group in ctrl.scope.switch.accordion) {
ctrl.scope.closePopover();
ctrl.scope.switch.accordion[group] = true;
}
} else {
for (var group in ctrl.scope.switch.accordion) {
ctrl.scope.closePopover();
ctrl.scope.switch.accordion[group] = false;
}
}
}
};
}
};
})
.directive('accordion', function ($ionicScrollDelegate) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
if (scope.$parent.$eval(attrs.accordion) === undefined) {
scope.$parent.$eval(attrs.accordion + '= false');
}
element.on('touchend', function () {
scope.$apply(function () {
scope.switch.accordion[attrs.accordion] = !scope.switch.accordion[attrs.accordion];
$ionicScrollDelegate.resize();
});
});
}
}
})
.directive('formatDate', function () {
return {
restrict: 'A',
scope: {
date: '=ngModel'
},
link: function (scope, element, attrs) {
scope.date = new Date(scope.date);
}
};
});
ion-nav-bar.bar-royal
ion-nav-buttons(side='right')
button.button.button-icon.icon.ion-android-more-vertical(ng-click='openPopover($event);')
ion-view.igray-bg(title='Work Order {{wo._id}}')
ion-content
ion-list
ion-item.igreen-bg.item-button-right.accordion-button
| Customer information
button.button.button-positive(accordion='customer')
i.icon(ng-class='switch.accordion.customer ? "ion-chevron-up" : "ion-chevron-down"')
ion-item.item-input.item-accordion.animate-me(ng-if='switch.accordion.customer')
label.input-label(for='wodetail-customer') Customer
input.input-right#wodetail-customer(type='text',
ng-model='wo.customer.name')
ion-item.item-input.item-accordion.animate-me(ng-if='switch.accordion.customer')
label.input-label(for='wodetail-address1') Address 1
input.input-right#wodetail-address1(type='text',
ng-model='wo.site.address1')
ion-item.item-input.item-accordion.animate-me(ng-if='switch.accordion.customer')
label.input-label(for='wodetail-address2') Address 2
input.input-right#wodetail-address2(type='text',
ng-model='wo.site.address2')
ion-item.item-input.item-accordion.animate-me(ng-if='switch.accordion.customer')
label.input-label(for='wodetail-city') City
input.input-right#wodetail-city(type='text',
ng-model='wo.site.city')
ion-item.item-input.item-select.item-accordion.animate-me(ng-if='switch.accordion.customer')
label.input-label(for='wodetail-state') State
select#wodetail-state(ng-model='wo.site.state',
name='siteState',
ng-options='state as state for state in \
["AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DC", "DE", "FL", "GA",\
"HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MD",\
"MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ",\
"NM", "NY", "NC", "ND", "OH", "OK", "OR", "PA", "RI", "SC",\
"SD", "TN", "TX", "UT", "VT", "VA", "WA", "WV", "WI", "WY"]')
ion-item.igreen-bg.item-button-right.accordion-button
| Work Order Details
button.button.button-positive(accordion='details')
i.icon(ng-class='switch.accordion.details ? "ion-chevron-up" : "ion-chevron-down"')
ion-item.item-input.item-accordion.item-stacked-label.white-bg.textarea-bordered.animate-me(ng-if='switch.accordion.details')
label.input-label(for='wodetail-description') Description
textarea#wodetail-description(ng-model='wo.description')
ion-item.item-input.item-select.item-accordion.animate-me(ng-if='switch.accordion.details')
label.input-label(for='wodetail-type') Type
select#wodetail-type(ng-model='wo.woType.name',
name='siteState',
ng-options='type.name for type in config.woTypes track by $index')
ion-item.item-input.item-select.item-accordion.animate-me(ng-if='switch.accordion.details')
label.input-label(for='wodetail-status') Status
select#wodetail-status(ng-model='wo.status.name',
name='siteState',
ng-options='status.name for status in config.statuses track by $index')
ion-item.item-input.item-accordion.animate-me(ng-if='switch.accordion.details')
label.input-label(for='wodetail-salesman') Salesman
input.input-right#wodetail-salesman(type='text',
ng-model='wo.salesman.name')
ion-item.item-input.item-accordion.animate-me(ng-if='switch.accordion.details')
label.input-label(for='wodetail-invoice') Invoice to
input.input-right#wodetail-invoice(type='text',
ng-model='wo.invoice.to')
ion-item.item-input-mimick.item-accordion.animate-me(ng-if='switch.accordion.details')
label.input-label Cost summary
span.pull-right {{wo.costSummary}}
ion-item.igreen-bg.item-button-right.accordion-button
| Parts
button.button.button-positive.accordion-button-switchable.animate-me(ng-click='switch.partSearch = !switch.partSearch;',
ng-if='switch.accordion.parts',
ng-init='switch.partSearch = false;')
i.icon(ng-class='switch.partSearch ? "ion-close" : "ion-plus"')
button.button.button-positive(accordion='parts')
i.icon(ng-class='switch.accordion.parts ? "ion-chevron-up" : "ion-chevron-down"')
ion-item.search-parts.item-input.item-accordion.noborder.animate-me(ng-if='switch.partSearch &&\
switch.accordion.parts;',
style='margin: -2px;',
animation-stop='!switch.accordion.parts',
animation-stop-on='leave')
i.icon.ion-search.placeholder-icon
input(type='text',
ng-model='partSearch.description',
placeholder='Search',
style='margin-right: 5px;',
ng-change='partSearchVal();',
ng-class='{"has-error": switch.partSearchRequired}')
button.button.button-calm.part-search-button(style='border: none; border-radius: 0;',
ng-click='partSearchModalOpen();') Find
.form-error.white-bg(ng-if='switch.partSearchRequired') This field is required
ion-item.item-table.item-table-header.item-accordion.animate-me(ng-if='switch.accordion.parts')
.row(style='text-align: center;')
.col
strong Part
.col
strong Quantity
.col
strong Discount
.col
strong Final price
.item-parent
ion-item.item-part.item-table.item-accordion.animate-me(ng-repeat='part in wo.data.parts',
animation-stop='switch.accordion.parts && !switch.addingPart',
animation-stop-on='enter')
.row.item-table-body.parts(ng-if='switch.accordion.parts')
.col
span {{part.description}}
.col
input(type='number',
ng-model='part.quantity',
ng-change='calculator.part(part, wo);')
.col
select.item-select-mimick(ng-model='part.price.discount',
ng-options='discount.value as discount.name for discount in \
discountPercentages',
ng-change='calculator.part(part, wo);')
.col
span {{part.price.final | currency:'$'}}
ion-option-button.button-energized.icon.ion-trash-a(ng-click='wo.data.parts.splice($index, 1);\
summarizeCost(wo);')
ion-item.item-table.item-accordion(style='padding-left: 16px;',
ng-if='wo.data.parts.length < 1 && switch.accordion.parts')
span.input-label Click the add parts button above to add a part
ion-item.igreen-bg.item-button-right.accordion-button
| Labor
button.button.button-positive.accordion-button-switchable.animate-me(ng-click='wo.data.labor.push({price: {discount: 0}});',
ng-if='switch.accordion.labor',
ng-init='switch.laborSearch = false;')
i.icon(ng-class='switch.laborSearch ? "ion-close" : "ion-plus"')
button.button.button-positive(accordion='labor')
i.icon(ng-class='switch.accordion.labor ? "ion-chevron-up" : "ion-chevron-down"')
.form-error.white-bg(ng-if='switch.laborSearchRequired') This field is required
ion-item.item-table.item-table-header.item-accordion.animate-me(ng-if='switch.accordion.labor')
.row(style='text-align: center;')
.col
strong Employee
.col
strong Type
.col
strong Hours
.col
strong Hours Type
.col
strong Discount
.col
strong Final price
.item-parent
ion-item.item-labor.item-table.item-accordion.animate-me(ng-repeat='labor in wo.data.labor',
animation-stop='switch.accordion.labor && !switch.addingLabor',
animation-stop-on='enter')
.row.item-table-body.labor(ng-if='switch.accordion.labor')
.col
select.item-select-mimick(ng-model='labor.tech',
ng-options='tech.name.full for tech in config.techs track by tech._id')
option(value='',
disabled,
selected) Choose
.col
select.item-select-mimick(ng-model='labor.type',
ng-options='type.name for type in config.labor.laborTypes track by type._id',
ng-change='calculator.labor(labor, wo);')
option(value='',
disabled,
selected) Choose
.col
input(type='number',
ng-model='labor.hours.number',
ng-change='calculator.labor(labor, wo);')
.col
select.item-select-mimick(ng-model='labor.hours.type',
ng-options='rate.name for rate in config.labor.rates track by rate._id',
ng-change='calculator.labor(labor, wo);')
option(value='',
disabled,
selected) Choose
.col
select.item-select-mimick(ng-model='labor.price.discount',
ng-options='discount.value as discount.name for discount in \
discountPercentages',
ng-change='calculator.labor(labor, wo);')
.col
span {{labor.price.final | currency:'$'}}
ion-option-button.button-energized.icon.ion-trash-a(ng-click='wo.data.labor.splice($index, 1);\
summarizeCost(wo);')
ion-item.item-table.item-accordion(style='padding-left: 16px;',
ng-if='wo.data.labor.length < 1 && switch.accordion.labor')
span.input-label Click the add labor button above to add labor item
ion-item.igreen-bg.item-button-right.accordion-button
| Signatures
button.button.button-positive(accordion='signatures')
i.icon(ng-class='switch.accordion.signatures ? "ion-chevron-up" : "ion-chevron-down"')
ion-item.item-table-header.item-accordion.animate-me(ng-if='switch.accordion.signatures')
strong Tech
ion-item.item-input.item-accordion.item-stacked-label.white-bg.animate-me(ng-if='switch.accordion.signatures')
label.input-label(for='#wodetail-printname-tech') Print Name
input#wodetail-printname-tech(type='text',
ng-model='wo.data.signature.tech.print')
ion-item.item-input.item-accordion.item-stacked-label.white-bg.animate-me(ng-if='switch.accordion.signatures')
label.input-label(for='#wodetail-date-tech') Date
input#wodetail-date-tech(type='date',
date='{{wo.data.signature.tech.date}}',
ng-model='wo.data.signature.tech.date',
format-date='')
ion-item.item-input.item-button-right.item-signature-label.item-accordion.white-bg.animate-me(ng-if='switch.accordion.signatures')
label.input-label Signature
button.button.button-light.signature-button-left(type='button',
ng-class='{"disabled": !bak.signature.tech}',
ng-click='undoSignature("tech")')
i.icon.ion-reply
button.button.button-light(type='button',
ng-click='setSignature("tech")'
ng-show='switch.signature.tech')
i.icon.ion-checkmark
button.button.button-light(type='button',
ng-init='switch.signature.tech = false;'
ng-click='backupSignature("tech")',
ng-hide='switch.signature.tech')
i.icon.ion-edit
ion-item.item-input.item-signature.item-accordion.item-stacked-label.white-bg.animate-me(ng-if='switch.accordion.signatures')
.canvas-signature(signature-pad='tech',
signature-pad-model='$parent.$parent.$parent.wo.data.signature.tech.signature',
ng-show='switch.signature.tech')
.signature-image(ng-hide='switch.signature.tech',
ng-class='{"signature-disabled": !wo.data.signature.tech.signature}')
img(ng-src='{{wo.data.signature.tech.signature}}',
style='width: 100%; opacity: .2;',
ng-show='wo.data.signature.tech.signature')
ion-item.item-table-header.item-accordion.animate-me(ng-if='switch.accordion.signatures')
strong Customer
ion-item.item-input.item-accordion.item-stacked-label.white-bg.animate-me(ng-if='switch.accordion.signatures')
label.input-label(for='#wodetail-customer-printname') Print Name
input#wodetail-customer-printname(type='text',
ng-model='wo.data.signature.customer.print')
ion-item.item-input.item-accordion.item-stacked-label.white-bg.animate-me(ng-if='switch.accordion.signatures')
label(for='wodetail-customer-date') Date
input#wodetail-customer-date(type='date',
date='{{wo.data.signature.customer.date}}',
ng-model='wo.data.signature.customer.date',
format-date='')
ion-item.item-input.item-button-right.item-signature-label.item-accordion.white-bg.animate-me(ng-if='switch.accordion.signatures')
label.input-label Signature
button.button.button-light.signature-button-left(type='button',
ng-class='{"disabled": !bak.signature.customer}',
ng-click='undoSignature("customer")')
i.icon.ion-reply
button.button.button-light(type='button',
ng-click='setSignature("customer")'
ng-show='switch.signature.customer')
i.icon.ion-checkmark
button.button.button-light(type='button',
ng-init='switch.signature.customer = false;'
ng-click='backupSignature("customer")',
ng-hide='switch.signature.customer')
i.icon.ion-edit
ion-item.item-input.item-signature.item-accordion.item-stacked-label.white-bg.animate-me(ng-if='switch.accordion.signatures')
.canvas-signature(signature-pad='customer',
signature-pad-model='$parent.$parent.$parent.wo.data.signature.customer.signature',
ng-show='switch.signature.customer')
.signature-image(ng-hide='switch.signature.customer',
ng-class='{"signature-disabled": !wo.data.signature.customer.signature}')
img(ng-src='{{wo.data.signature.customer.signature}}',
style='width: 100%; opacity: .2;',
ng-show='wo.data.signature.customer.signature')
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="http://code.ionicframework.com/1.1.0/css/ionic.min.css" />
<script src="http://code.ionicframework.com/1.1.0/js/ionic.bundle.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body class="platform-android" ng-app="brokenResize">
<ion-nav-view></ion-nav-view>
</body>
</html>
<ion-popover-view>
<ion-content>
<ion-list>
<ion-item ng-click="saveAndSend()" class="item-icon-left"><i class="icon ion-upload"></i>Save Changes</ion-item>
<ion-item ng-click="expandCollapse()" class="item-icon-left"><i ng-class="switch.accordion.expanded ? "ion-minus" : "ion-plus"" class="icon"></i>{{switch.accordion.expanded ? 'Collapse' : 'Expand'}}</ion-item>
</ion-list>
</ion-content>
</ion-popover-view>
// Custom variables
$igray: #f3f3f4;
$inavy: #2f4050;
$igreen: #1ab394;
$idefault: #c2c2c2;
// Styles
.igray-bg {
background: $igray;
}
.inavy-bg {
background: $inavy;
color: white;
}
.igreen-bg, .igreen-bg .item-content {
background: $igreen;
color: white;
}
.white-bg {
background: white;
}
@media screen and (min-device-aspect-ratio: 3/4) {
@media (min-height: 700px) {
.form-login {
margin: 6% 10% 0 10%;
}
.logo {
margin: 10% 33% 0 33%;
}
}
@media (max-height: 699px) {
.form-login {
margin: 4% 10% 0 10%;
}
.logo {
margin: 3% 35% 0 35%;
}
}
}
@media screen and (max-device-aspect-ratio: 3/4) {
@media (min-height: 700px) {
.form-login {
margin: 10% 10% 0 10%;
}
.logo {
margin: 30% 25% 0 25%;
}
}
@media (max-height: 699px) {
.form-login {
margin: 20% 10% 0 10%;
}
.logo {
margin: 30% 15% 0 15%;
}
}
}
.logo img {
width: 100%;
}
#logout {
text-align: center;
margin: 10%;
}
#logout h1 {
font-style: italic;
}
.input-right {
text-align: right;
}
.list .item.item-accordion {
line-height: 38px;
padding-top: 0;
padding-bottom: 0;
transition: 0.09s all linear;
}
.item-accordion label {
line-height: inherit;
}
.item-accordion .row,
.item-accordion .row .col {
padding: 0;
}
.list .item.item-accordion.ng-enter,
.list .item.item-accordion.ng-enter.ng-enter-active,
.list .item.item-accordion.ng-leave,
.list .item.item-accordion.ng-leave-active {
line-height: 0px;
}
.accordion-button button {
border-color: white !important;
}
.accordion-button-switchable {
margin-right: 60px;
}
.pull-right {
float: right;
}
.pull-left {
float: left;
}
.block {
display: block;
}
.item-input-mimick {
padding-top: 8px !important;
padding-bottom: 8px !important;
}
.item-input-mimick span {
font-size: 14px;
}
.item-table:not(:first-child) {
border-top: 1px solid #ddd;
}
.item-table .item-content:not(.item-part-empty) {
padding: 0;
}
.item-table-body {
padding: 0 10px;
}
.item-table-body .col:not(:first-child) {
text-align: center;
}
.item-table-body .col {
padding-top: 5px;
}
.item-table-body .col:not(:last-child) {
border-right: 1px solid #ddd;
}
.item-table-body select {
padding: 0;
width: 100%;
border: none;
background: #fff;
color: #333;
text-indent: .01px;
text-overflow: '';
white-space: nowrap;
font-size: 14px;
cursor: pointer;
direction: rtl;
}
.item-table-body select {
text-indent: 41%;
}
.item-table-body input {
width: 100%;
text-align: center;
}
.item-table-body .input-label {
text-align: left;
}
.item-table-body .col {
overflow: hidden;
}
.item-table-header {
background: $idefault;
border: none !important;
}
.item-table {
padding: 0;
border: none;
}
.table-row .list {
display: flex;
}
.table-row .item {
flex: 1;
width: 100%;
display: block;
}
.item-table-body .col:first-child span {
margin-left: 16px;
}
.noborder {
border: none;
}
.search-parts.ng-enter.ng-enter-active {
animation-name: slideInRight;
animation-duration: 0.6s;
animation-fill-mode: both;
}
.search-parts.ng-leave {
animation-name: slideOutRight;
animation-duration: 0.6s;
animation-fill-mode: both;
}
.button-with-arrow {
float: right;
}
.button-with-arrow span {
position: absolute;
right: 16px;
}
#add-part-button.ng-enter.ng-enter-active,
#add-labor-button.ng-enter.ng-enter-active {
animation-name: fadeIn;
animation-duration: 0.6s;
animation-fill-mode: both;
}
#add-part-button.ng-leave,
#add-labor-button.ng-leave {
animation-name: fadeOut;
animation-duration: 0.6s;
animation-fill-mode: both;
}
input.has-error {
border-bottom: 2px solid red;
}
.form-error {
padding: 2px 0 2px 16px;
color: red;
}
.item-part.ng-enter.ng-enter-active,
.item-labor.ng-enter.ng-enter-active {
animation-name: slideInRight;
animation-duration: 0.6s;
animation-fill-mode: both;
line-height: 38px !important;
}
.item-part.ng-leave,
.item-labor.ng-leave {
animation-name: slideOutRight;
animation-duration: 0.6s;
animation-fill-mode: both;
line-height: 38px !important;
}
.part-search-button {
padding: 0 25px;
}
.canvas-signature {
margin-bottom: 3px;
}
.canvas-signature canvas {
border: 1px solid #ddd;
}
.item-signature {
border-top: none;
}
.signature-image {
height: 247px;
border: 1px solid #ddd;
padding: 0;
margin-right: 16px;
margin-bottom: 16px;
}
.signature-disabled {
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAChJREFUeNpiPHPmDAMMGBsbw9lMDDgA6RKM%2F%2F%2F%2Fh3POnj1LCzsAAgwAQtYIcFfEyzkAAAAASUVORK5CYII%3D');
}
.signature-button-left {
margin-right: 60px;
}
.platform-android .popover .item.activated {
background-color: #D9D9D9;
}
.item.textarea-bordered {
padding-right: 16px;
}
.item.textarea-bordered textarea {
border: 1px solid #ddd;
border-radius: 0;
margin-bottom: 16px;
padding: 10px;
}
.nodisplay {
display: none;
}
<ion-nav-bar class="bar-royal">
<ion-nav-buttons side="right">
<button ng-click="openPopover($event);" class="button button-icon icon ion-android-more-vertical"></button>
</ion-nav-buttons>
<ion-view title="Work Order {{wo._id}}" class="igray-bg">
<ion-content>
<ion-list>
<ion-item class="igreen-bg item-button-right accordion-button">Customer information
<button accordion="customer" class="button button-positive"><i ng-class="switch.accordion.customer ? "ion-chevron-up" : "ion-chevron-down"" class="icon"></i></button>
</ion-item>
<ion-item ng-if="switch.accordion.customer" class="item-input item-accordion animate-me">
<label for="wodetail-customer" class="input-label">Customer</label>
<input id="wodetail-customer" type="text" ng-model="wo.customer.name" class="input-right"/>
</ion-item>
<ion-item ng-if="switch.accordion.customer" class="item-input item-accordion animate-me">
<label for="wodetail-address1" class="input-label">Address 1</label>
<input id="wodetail-address1" type="text" ng-model="wo.site.address1" class="input-right"/>
</ion-item>
<ion-item ng-if="switch.accordion.customer" class="item-input item-accordion animate-me">
<label for="wodetail-address2" class="input-label">Address 2</label>
<input id="wodetail-address2" type="text" ng-model="wo.site.address2" class="input-right"/>
</ion-item>
<ion-item ng-if="switch.accordion.customer" class="item-input item-accordion animate-me">
<label for="wodetail-city" class="input-label">City</label>
<input id="wodetail-city" type="text" ng-model="wo.site.city" class="input-right"/>
</ion-item>
<ion-item ng-if="switch.accordion.customer" class="item-input item-select item-accordion animate-me">
<label for="wodetail-state" class="input-label">State</label>
<select id="wodetail-state" ng-model="wo.site.state" name="siteState" ng-options="state as state for state in ["AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DC", "DE", "FL", "GA", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "OH", "OK", "OR", "PA", "RI", "SC", "SD", "TN", "TX", "UT", "VT", "VA", "WA", "WV", "WI", "WY"]"></select>
</ion-item>
<ion-item class="igreen-bg item-button-right accordion-button">Work Order Details
<button accordion="details" class="button button-positive"><i ng-class="switch.accordion.details ? "ion-chevron-up" : "ion-chevron-down"" class="icon"></i></button>
</ion-item>
<ion-item ng-if="switch.accordion.details" class="item-input item-accordion item-stacked-label white-bg textarea-bordered animate-me">
<label for="wodetail-description" class="input-label">Description</label>
<textarea id="wodetail-description" ng-model="wo.description"></textarea>
</ion-item>
<ion-item ng-if="switch.accordion.details" class="item-input item-select item-accordion animate-me">
<label for="wodetail-type" class="input-label">Type</label>
<select id="wodetail-type" ng-model="wo.woType.name" name="siteState" ng-options="type.name for type in config.woTypes track by $index"></select>
</ion-item>
<ion-item ng-if="switch.accordion.details" class="item-input item-select item-accordion animate-me">
<label for="wodetail-status" class="input-label">Status</label>
<select id="wodetail-status" ng-model="wo.status.name" name="siteState" ng-options="status.name for status in config.statuses track by $index"></select>
</ion-item>
<ion-item ng-if="switch.accordion.details" class="item-input item-accordion animate-me">
<label for="wodetail-salesman" class="input-label">Salesman</label>
<input id="wodetail-salesman" type="text" ng-model="wo.salesman.name" class="input-right"/>
</ion-item>
<ion-item ng-if="switch.accordion.details" class="item-input item-accordion animate-me">
<label for="wodetail-invoice" class="input-label">Invoice to</label>
<input id="wodetail-invoice" type="text" ng-model="wo.invoice.to" class="input-right"/>
</ion-item>
<ion-item ng-if="switch.accordion.details" class="item-input-mimick item-accordion animate-me">
<label class="input-label">Cost summary</label><span class="pull-right">{{wo.costSummary}}</span>
</ion-item>
<ion-item class="igreen-bg item-button-right accordion-button">Parts
<button ng-click="switch.partSearch = !switch.partSearch;" ng-if="switch.accordion.parts" ng-init="switch.partSearch = false;" class="button button-positive accordion-button-switchable animate-me"><i ng-class="switch.partSearch ? "ion-close" : "ion-plus"" class="icon"></i></button>
<button accordion="parts" class="button button-positive"><i ng-class="switch.accordion.parts ? "ion-chevron-up" : "ion-chevron-down"" class="icon"></i></button>
</ion-item>
<ion-item ng-if="switch.partSearch && switch.accordion.parts;" style="margin: -2px;" animation-stop="!switch.accordion.parts" animation-stop-on="leave" class="search-parts item-input item-accordion noborder animate-me"><i class="icon ion-search placeholder-icon"></i>
<input type="text" ng-model="partSearch.description" placeholder="Search" style="margin-right: 5px;" ng-change="partSearchVal();" ng-class="{"has-error": switch.partSearchRequired}"/>
<button style="border: none; border-radius: 0;" ng-click="partSearchModalOpen();" class="button button-calm part-search-button">Find</button>
</ion-item>
<div ng-if="switch.partSearchRequired" class="form-error white-bg">This field is required</div>
<ion-item ng-if="switch.accordion.parts" class="item-table item-table-header item-accordion animate-me">
<div style="text-align: center;" class="row">
<div class="col"><strong>Part</strong></div>
<div class="col"><strong>Quantity</strong></div>
<div class="col"><strong>Discount</strong></div>
<div class="col"><strong>Final price</strong></div>
</div>
</ion-item>
<div class="item-parent">
<ion-item ng-repeat="part in wo.data.parts" animation-stop="switch.accordion.parts && !switch.addingPart" animation-stop-on="enter" class="item-part item-table item-accordion animate-me">
<div ng-if="switch.accordion.parts" class="row item-table-body parts">
<div class="col"><span>{{part.description}}</span></div>
<div class="col">
<input type="number" ng-model="part.quantity" ng-change="calculator.part(part, wo);"/>
</div>
<div class="col">
<select ng-model="part.price.discount" ng-options="discount.value as discount.name for discount in discountPercentages" ng-change="calculator.part(part, wo);" class="item-select-mimick"></select>
</div>
<div class="col"><span>{{part.price.final | currency:'$'}}</span></div>
</div>
<ion-option-button ng-click="wo.data.parts.splice($index, 1); summarizeCost(wo);" class="button-energized icon ion-trash-a"></ion-option-button>
</ion-item>
<ion-item style="padding-left: 16px;" ng-if="wo.data.parts.length < 1 && switch.accordion.parts" class="item-table item-accordion"><span class="input-label">Click the add parts button above to add a part</span></ion-item>
</div>
<ion-item class="igreen-bg item-button-right accordion-button">Labor
<button ng-click="wo.data.labor.push({price: {discount: 0}});" ng-if="switch.accordion.labor" ng-init="switch.laborSearch = false;" class="button button-positive accordion-button-switchable animate-me"><i ng-class="switch.laborSearch ? "ion-close" : "ion-plus"" class="icon"></i></button>
<button accordion="labor" class="button button-positive"><i ng-class="switch.accordion.labor ? "ion-chevron-up" : "ion-chevron-down"" class="icon"></i></button>
</ion-item>
<div ng-if="switch.laborSearchRequired" class="form-error white-bg">This field is required</div>
<ion-item ng-if="switch.accordion.labor" class="item-table item-table-header item-accordion animate-me">
<div style="text-align: center;" class="row">
<div class="col"><strong>Employee</strong></div>
<div class="col"><strong>Type</strong></div>
<div class="col"><strong>Hours</strong></div>
<div class="col"><strong>Hours Type</strong></div>
<div class="col"><strong>Discount</strong></div>
<div class="col"><strong>Final price</strong></div>
</div>
</ion-item>
<div class="item-parent">
<ion-item ng-repeat="labor in wo.data.labor" animation-stop="switch.accordion.labor && !switch.addingLabor" animation-stop-on="enter" class="item-labor item-table item-accordion animate-me">
<div ng-if="switch.accordion.labor" class="row item-table-body labor">
<div class="col">
<select ng-model="labor.tech" ng-options="tech.name.full for tech in config.techs track by tech._id" class="item-select-mimick">
<option value="" disabled="disabled" selected="selected">Choose</option>
</select>
</div>
<div class="col">
<select ng-model="labor.type" ng-options="type.name for type in config.labor.laborTypes track by type._id" ng-change="calculator.labor(labor, wo);" class="item-select-mimick">
<option value="" disabled="disabled" selected="selected">Choose</option>
</select>
</div>
<div class="col">
<input type="number" ng-model="labor.hours.number" ng-change="calculator.labor(labor, wo);"/>
</div>
<div class="col">
<select ng-model="labor.hours.type" ng-options="rate.name for rate in config.labor.rates track by rate._id" ng-change="calculator.labor(labor, wo);" class="item-select-mimick">
<option value="" disabled="disabled" selected="selected">Choose</option>
</select>
</div>
<div class="col">
<select ng-model="labor.price.discount" ng-options="discount.value as discount.name for discount in discountPercentages" ng-change="calculator.labor(labor, wo);" class="item-select-mimick"></select>
</div>
<div class="col"><span>{{labor.price.final | currency:'$'}}</span></div>
</div>
<ion-option-button ng-click="wo.data.labor.splice($index, 1); summarizeCost(wo);" class="button-energized icon ion-trash-a"></ion-option-button>
</ion-item>
<ion-item style="padding-left: 16px;" ng-if="wo.data.labor.length < 1 && switch.accordion.labor" class="item-table item-accordion"><span class="input-label">Click the add labor button above to add labor item</span></ion-item>
</div>
<ion-item class="igreen-bg item-button-right accordion-button">Signatures
<button accordion="signatures" class="button button-positive"><i ng-class="switch.accordion.signatures ? "ion-chevron-up" : "ion-chevron-down"" class="icon"></i></button>
</ion-item>
<ion-item ng-if="switch.accordion.signatures" class="item-table-header item-accordion animate-me"><strong>Tech</strong></ion-item>
<ion-item ng-if="switch.accordion.signatures" class="item-input item-accordion item-stacked-label white-bg animate-me">
<label for="#wodetail-printname-tech" class="input-label">Print Name</label>
<input id="wodetail-printname-tech" type="text" ng-model="wo.data.signature.tech.print"/>
</ion-item>
<ion-item ng-if="switch.accordion.signatures" class="item-input item-accordion item-stacked-label white-bg animate-me">
<label for="#wodetail-date-tech" class="input-label">Date</label>
<input id="wodetail-date-tech" type="date" date="{{wo.data.signature.tech.date}}" ng-model="wo.data.signature.tech.date" format-date=""/>
</ion-item>
<ion-item ng-if="switch.accordion.signatures" class="item-input item-button-right item-signature-label item-accordion white-bg animate-me">
<label class="input-label">Signature</label>
<button type="button" ng-class="{"disabled": !bak.signature.tech}" ng-click="undoSignature("tech")" class="button button-light signature-button-left"><i class="icon ion-reply"></i></button>
<button type="button" ng-click="setSignature("tech")" ng-show="switch.signature.tech" class="button button-light"><i class="icon ion-checkmark"></i></button>
<button type="button" ng-init="switch.signature.tech = false;" ng-click="backupSignature("tech")" ng-hide="switch.signature.tech" class="button button-light"><i class="icon ion-edit"></i></button>
</ion-item>
<ion-item ng-if="switch.accordion.signatures" class="item-input item-signature item-accordion item-stacked-label white-bg animate-me">
<div signature-pad="tech" signature-pad-model="$parent.$parent.$parent.wo.data.signature.tech.signature" ng-show="switch.signature.tech" class="canvas-signature"></div>
<div ng-hide="switch.signature.tech" ng-class="{"signature-disabled": !wo.data.signature.tech.signature}" class="signature-image"><img ng-src="{{wo.data.signature.tech.signature}}" style="width: 100%; opacity: .2;" ng-show="wo.data.signature.tech.signature"/></div>
</ion-item>
<ion-item ng-if="switch.accordion.signatures" class="item-table-header item-accordion animate-me"><strong>Customer</strong></ion-item>
<ion-item ng-if="switch.accordion.signatures" class="item-input item-accordion item-stacked-label white-bg animate-me">
<label for="#wodetail-customer-printname" class="input-label">Print Name</label>
<input id="wodetail-customer-printname" type="text" ng-model="wo.data.signature.customer.print"/>
</ion-item>
<ion-item ng-if="switch.accordion.signatures" class="item-input item-accordion item-stacked-label white-bg animate-me">
<label for="wodetail-customer-date">Date</label>
<input id="wodetail-customer-date" type="date" date="{{wo.data.signature.customer.date}}" ng-model="wo.data.signature.customer.date" format-date=""/>
</ion-item>
<ion-item ng-if="switch.accordion.signatures" class="item-input item-button-right item-signature-label item-accordion white-bg animate-me">
<label class="input-label">Signature</label>
<button type="button" ng-class="{"disabled": !bak.signature.customer}" ng-click="undoSignature("customer")" class="button button-light signature-button-left"><i class="icon ion-reply"></i></button>
<button type="button" ng-click="setSignature("customer")" ng-show="switch.signature.customer" class="button button-light"><i class="icon ion-checkmark"></i></button>
<button type="button" ng-init="switch.signature.customer = false;" ng-click="backupSignature("customer")" ng-hide="switch.signature.customer" class="button button-light"><i class="icon ion-edit"></i></button>
</ion-item>
<ion-item ng-if="switch.accordion.signatures" class="item-input item-signature item-accordion item-stacked-label white-bg animate-me">
<div signature-pad="customer" signature-pad-model="$parent.$parent.$parent.wo.data.signature.customer.signature" ng-show="switch.signature.customer" class="canvas-signature"></div>
<div ng-hide="switch.signature.customer" ng-class="{"signature-disabled": !wo.data.signature.customer.signature}" class="signature-image"><img ng-src="{{wo.data.signature.customer.signature}}" style="width: 100%; opacity: .2;" ng-show="wo.data.signature.customer.signature"/></div>
</ion-item>
</ion-list>
</ion-content>
</ion-view>
</ion-nav-bar>