WIP: Split out admin angularjs

This commit is contained in:
Rob H
2014-05-02 21:38:39 +10:00
parent 5a2a43a060
commit 6ba0d6c5f9
29 changed files with 270 additions and 298 deletions

View File

@@ -1,3 +1,3 @@
Admin = angular.module("ofn.admin", ["ngResource", "ofn.shared_services", "ofn.shared_directives"]).config ($httpProvider) ->
$httpProvider.defaults.headers.common["X-CSRF-Token"] = $("meta[name=csrf-token]").attr("content")
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"
window.Admin = angular.module("ofn.admin", ["ngResource","ofn.dropdown"]).config ($httpProvider) ->
$httpProvider.defaults.headers.common["X-CSRF-Token"] = $("meta[name=csrf-token]").attr("content")
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"

View File

@@ -14,5 +14,6 @@
//= require admin/spree_core
//= require admin/spree_auth
//= require admin/spree_promo
//= require ./admin
//= require_tree .

View File

@@ -1,104 +1,3 @@
Admin.value "blankOption", ->
{ id: "0", name: "All" }
Admin.directive "ofnLineItemUpdAttr", [
"switchClass", "pendingChanges"
(switchClass, pendingChanges) ->
require: "ngModel"
link: (scope, element, attrs, ngModel) ->
attrName = attrs.ofnLineItemUpdAttr
element.dbValue = scope.$eval(attrs.ngModel)
scope.$watch ->
scope.$eval(attrs.ngModel)
, (value) ->
if ngModel.$dirty
if value == element.dbValue
pendingChanges.remove(scope.line_item.id, attrName)
switchClass( element, "", ["update-pending", "update-error", "update-success"], false )
else
changeObj =
lineItem: scope.line_item
element: element
attrName: attrName
url: "/api/orders/#{scope.line_item.order.number}/line_items/#{scope.line_item.id}?line_item[#{attrName}]=#{value}"
pendingChanges.add(scope.line_item.id, attrName, changeObj)
switchClass( element, "update-pending", ["update-error", "update-success"], false )
]
Admin.directive "ofnConfirmModelChange", (ofnConfirmHandler,$timeout) ->
restrict: "A"
link: (scope, element, attrs) ->
handler = ofnConfirmHandler scope, -> scope.fetchOrders()
scope.$watch attrs.ngModel, (oldValue,newValue) ->
handler() unless oldValue == undefined || newValue == oldValue
Admin.directive "ofnConfirmLinkPath", (ofnConfirmHandler) ->
restrict: "A"
scope:
path: "@ofnConfirmLinkPath"
link: (scope, element, attrs) ->
element.click ofnConfirmHandler scope, ->
window.location = scope.path
Admin.factory "ofnConfirmHandler", (pendingChanges, $compile, $q) ->
return (scope, callback) ->
template = "<div id='dialog-div' style='padding: 10px'><h6>Unsaved changes currently exist, save now or ignore?</h6></div>"
dialogDiv = $compile(template)(scope)
return ->
if pendingChanges.changeCount(pendingChanges.pendingChanges) > 0
dialogDiv.dialog
dialogClass: "no-close"
resizable: false
height: 140
modal: true
buttons:
"SAVE": ->
dialogDiv = $(this)
$q.all(pendingChanges.submitAll()).then ->
callback()
dialogDiv.dialog "close"
"IGNORE": ->
callback()
$(this).dialog "close"
scope.$apply()
dialogDiv.dialog "open"
else
callback()
Admin.factory "pendingChanges",[
"dataSubmitter"
(dataSubmitter) ->
pendingChanges: {}
add: (id, attrName, changeObj) ->
@pendingChanges["#{id}"] = {} unless @pendingChanges.hasOwnProperty("#{id}")
@pendingChanges["#{id}"]["#{attrName}"] = changeObj
removeAll: ->
@pendingChanges = {}
remove: (id, attrName) ->
if @pendingChanges.hasOwnProperty("#{id}")
delete @pendingChanges["#{id}"]["#{attrName}"]
delete @pendingChanges["#{id}"] if @changeCount( @pendingChanges["#{id}"] ) < 1
submitAll: ->
all = []
for id,lineItem of @pendingChanges
for attrName,changeObj of lineItem
all.push @submit(id, attrName, changeObj)
all
submit: (id, attrName, change) ->
dataSubmitter(change).then (data) =>
@remove id, attrName
change.element.dbValue = data["#{attrName}"]
changeCount: (lineItem) ->
Object.keys(lineItem).length
]
Admin.controller "AdminOrderMgmtCtrl", [
"$scope", "$http", "dataFetcher", "blankOption", "pendingChanges"
($scope, $http, dataFetcher, blankOption, pendingChanges) ->
@@ -281,50 +180,6 @@ Admin.controller "AdminOrderMgmtCtrl", [
$scope.quickSearch = ""
]
Admin.filter "selectFilter", (blankOption) ->
return (lineItems,selectedSupplier,selectedDistributor,selectedOrderCycle) ->
filtered = []
filtered.push lineItem for lineItem in lineItems when (angular.equals(selectedSupplier,"0") || lineItem.supplier.id == selectedSupplier) &&
(angular.equals(selectedDistributor,"0") || lineItem.order.distributor.id == selectedDistributor) &&
(angular.equals(selectedOrderCycle,"0") || lineItem.order.order_cycle.id == selectedOrderCycle)
filtered
Admin.filter "variantFilter", ->
return (lineItems,selectedUnitsProduct,selectedUnitsVariant,sharedResource) ->
filtered = []
filtered.push lineItem for lineItem in lineItems when (angular.equals(selectedUnitsProduct,{}) ||
(lineItem.units_product.id == selectedUnitsProduct.id && (sharedResource || lineItem.units_variant.id == selectedUnitsVariant.id ) ) )
filtered
Admin.factory "dataSubmitter", [
"$http", "$q", "switchClass"
($http, $q, switchClass) ->
return (changeObj) ->
deferred = $q.defer()
$http.put(changeObj.url).success((data) ->
switchClass changeObj.element, "update-success", ["update-pending", "update-error"], 3000
deferred.resolve data
).error ->
switchClass changeObj.element, "update-error", ["update-pending", "update-success"], false
deferred.reject()
deferred.promise
]
Admin.factory "switchClass", [
"$timeout"
($timeout) ->
return (element,classToAdd,removeClasses,timeout) ->
$timeout.cancel element.timeout if element.timeout
element.removeClass className for className in removeClasses
element.addClass classToAdd
intRegex = /^\d+$/
if timeout && intRegex.test(timeout)
element.timeout = $timeout(->
element.removeClass classToAdd
, timeout, true)
]
daysFromToday = (days) ->
now = new Date
now.setHours(0)

View File

@@ -1,97 +1,3 @@
Admin.factory "Taxons", ($resource) ->
resource = $resource "/admin/taxons/search"
return {
findByIDs: (ids) ->
resource.get { ids: ids }
findByTerm: (term) ->
resource.get { q: term }
cleanTaxons: (data) ->
data['taxons'].map (result) -> result
}
Admin.directive "ofnTaxonAutocomplete", (Taxons) ->
# Adapted from Spree's existing taxon autocompletion
require: "ngModel"
link: (scope,element,attrs,ngModel) ->
setTimeout ->
element.select2
placeholder: Spree.translations.taxon_placeholder
multiple: true
initSelection: (element, callback) ->
Taxons.findByIDs(element.val()).$promise.then (result) ->
callback Taxons.cleanTaxons(result)
query: (query) ->
Taxons.findByTerm(query.term).$promise.then (result) ->
query.callback { results: Taxons.cleanTaxons(result) }
formatResult: (taxon) ->
taxon.pretty_name
formatSelection: (taxon) ->
taxon.pretty_name
element.on "change", ->
scope.$apply ->
ngModel.$setViewValue element.val()
Admin.directive "ofnDecimal", ->
require: "ngModel"
link: (scope, element, attrs, ngModel) ->
numRegExp = /^\d+(\.\d+)?$/
element.bind "blur", ->
scope.$apply ngModel.$setViewValue(ngModel.$modelValue)
ngModel.$render()
ngModel.$parsers.push (viewValue) ->
return viewValue + ".0" if viewValue.indexOf(".") == -1 if angular.isString(viewValue) and numRegExp.test(viewValue)
viewValue
Admin.directive "ofnTrackProduct", ['$parse', ($parse) ->
require: "ngModel"
link: (scope, element, attrs, ngModel) ->
ngModel.$parsers.push (viewValue) ->
if ngModel.$dirty
parsedPropertyName = $parse(attrs.ofnTrackProduct)
addDirtyProperty scope.dirtyProducts, scope.product.id, parsedPropertyName, viewValue
scope.displayDirtyProducts()
viewValue
]
Admin.directive "ofnTrackVariant", ['$parse', ($parse) ->
require: "ngModel"
link: (scope, element, attrs, ngModel) ->
ngModel.$parsers.push (viewValue) ->
dirtyVariants = {}
dirtyVariants = scope.dirtyProducts[scope.product.id].variants if scope.dirtyProducts.hasOwnProperty(scope.product.id) and scope.dirtyProducts[scope.product.id].hasOwnProperty("variants")
if ngModel.$dirty
parsedPropertyName = $parse(attrs.ofnTrackVariant)
addDirtyProperty dirtyVariants, scope.variant.id, parsedPropertyName, viewValue
addDirtyProperty scope.dirtyProducts, scope.product.id, $parse("variants"), dirtyVariants
scope.displayDirtyProducts()
viewValue
]
Admin.directive "ofnToggleVariants", ->
link: (scope, element, attrs) ->
if scope.displayProperties[scope.product.id].showVariants
element.removeClass "icon-chevron-right"
element.addClass "icon-chevron-down"
else
element.removeClass "icon-chevron-down"
element.addClass "icon-chevron-right"
element.on "click", ->
scope.$apply ->
if scope.displayProperties[scope.product.id].showVariants
scope.displayProperties[scope.product.id].showVariants = false
element.removeClass "icon-chevron-down"
element.addClass "icon-chevron-right"
else
scope.displayProperties[scope.product.id].showVariants = true
element.removeClass "icon-chevron-right"
element.addClass "icon-chevron-down"
Admin.controller "AdminProductEditCtrl", [
"$scope", "$timeout", "$http", "dataFetcher"
($scope, $timeout, $http, dataFetcher) ->
@@ -516,12 +422,6 @@ Admin.controller "AdminProductEditCtrl", [
Object.keys($scope.dirtyProducts).length
]
Admin.filter "rangeArray", ->
return (input,start,end) ->
input.push(i) for i in [start..end]
input
filterSubmitProducts = (productsToFilter) ->
filteredProducts = []
if productsToFilter instanceof Object

View File

@@ -0,0 +1,7 @@
Admin.directive "ofnConfirmLinkPath", (ofnConfirmHandler) ->
restrict: "A"
scope:
path: "@ofnConfirmLinkPath"
link: (scope, element, attrs) ->
element.click ofnConfirmHandler scope, ->
window.location = scope.path

View File

@@ -0,0 +1,6 @@
Admin.directive "ofnConfirmModelChange", (ofnConfirmHandler,$timeout) ->
restrict: "A"
link: (scope, element, attrs) ->
handler = ofnConfirmHandler scope, -> scope.fetchOrders()
scope.$watch attrs.ngModel, (oldValue,newValue) ->
handler() unless oldValue == undefined || newValue == oldValue

View File

@@ -0,0 +1,9 @@
Admin.directive "datepicker", ->
require: "ngModel"
link: (scope, element, attrs, ngModel) ->
element.datepicker
dateFormat: "yy-mm-dd"
onSelect: (dateText, inst) ->
scope.$apply (scope) ->
# Fires ngModel.$parsers
ngModel.$setViewValue dateText

View File

@@ -0,0 +1,11 @@
Admin.directive "datetimepicker", ->
require: "ngModel"
link: (scope, element, attrs, ngModel) ->
element.datetimepicker
dateFormat: "yy-mm-dd"
timeFormat: "HH:mm:ss"
stepMinute: 15
onSelect: (dateText, inst) ->
scope.$apply (scope) ->
# Fires ngModel.$parsers
ngModel.$setViewValue dateText

View File

@@ -0,0 +1,11 @@
Admin.directive "ofnDecimal", ->
require: "ngModel"
link: (scope, element, attrs, ngModel) ->
numRegExp = /^\d+(\.\d+)?$/
element.bind "blur", ->
scope.$apply ngModel.$setViewValue(ngModel.$modelValue)
ngModel.$render()
ngModel.$parsers.push (viewValue) ->
return viewValue + ".0" if viewValue.indexOf(".") == -1 if angular.isString(viewValue) and numRegExp.test(viewValue)
viewValue

View File

@@ -0,0 +1,23 @@
Admin.directive "ofnLineItemUpdAttr", [
"switchClass", "pendingChanges"
(switchClass, pendingChanges) ->
require: "ngModel"
link: (scope, element, attrs, ngModel) ->
attrName = attrs.ofnLineItemUpdAttr
element.dbValue = scope.$eval(attrs.ngModel)
scope.$watch ->
scope.$eval(attrs.ngModel)
, (value) ->
if ngModel.$dirty
if value == element.dbValue
pendingChanges.remove(scope.line_item.id, attrName)
switchClass( element, "", ["update-pending", "update-error", "update-success"], false )
else
changeObj =
lineItem: scope.line_item
element: element
attrName: attrName
url: "/api/orders/#{scope.line_item.order.number}/line_items/#{scope.line_item.id}?line_item[#{attrName}]=#{value}"
pendingChanges.add(scope.line_item.id, attrName, changeObj)
switchClass( element, "update-pending", ["update-error", "update-success"], false )
]

View File

@@ -0,0 +1,9 @@
Admin.directive "ofnSelect2MinSearch", ->
require: 'ngModel'
link: (scope, element, attrs, ngModel) ->
element.select2
minimumResultsForSearch: attrs.ofnSelect2MinSearch
ngModel.$formatters.push (value) ->
if (value)
element.select2('val', value);

View File

@@ -0,0 +1,21 @@
Admin.directive "ofnTaxonAutocomplete", (Taxons) ->
# Adapted from Spree's existing taxon autocompletion
require: "ngModel"
link: (scope,element,attrs,ngModel) ->
setTimeout ->
element.select2
placeholder: Spree.translations.taxon_placeholder
multiple: true
initSelection: (element, callback) ->
Taxons.findByIDs(element.val()).$promise.then (result) ->
callback Taxons.cleanTaxons(result)
query: (query) ->
Taxons.findByTerm(query.term).$promise.then (result) ->
query.callback { results: Taxons.cleanTaxons(result) }
formatResult: (taxon) ->
taxon.pretty_name
formatSelection: (taxon) ->
taxon.pretty_name
element.on "change", ->
scope.$apply ->
ngModel.$setViewValue element.val()

View File

@@ -0,0 +1,11 @@
Admin.directive "ofnToggleColumn", ->
link: (scope, element, attrs) ->
element.addClass "selected" if scope.column.visible
element.click "click", ->
scope.$apply ->
if scope.column.visible
scope.column.visible = false
element.removeClass "selected"
else
scope.column.visible = true
element.addClass "selected"

View File

@@ -0,0 +1,18 @@
Admin.directive "ofnToggleVariants", ->
link: (scope, element, attrs) ->
if scope.displayProperties[scope.product.id].showVariants
element.removeClass "icon-chevron-right"
element.addClass "icon-chevron-down"
else
element.removeClass "icon-chevron-down"
element.addClass "icon-chevron-right"
element.on "click", ->
scope.$apply ->
if scope.displayProperties[scope.product.id].showVariants
scope.displayProperties[scope.product.id].showVariants = false
element.removeClass "icon-chevron-down"
element.addClass "icon-chevron-right"
else
scope.displayProperties[scope.product.id].showVariants = true
element.removeClass "icon-chevron-right"
element.addClass "icon-chevron-down"

View File

@@ -0,0 +1,10 @@
Admin.directive "ofnTrackProduct", ['$parse', ($parse) ->
require: "ngModel"
link: (scope, element, attrs, ngModel) ->
ngModel.$parsers.push (viewValue) ->
if ngModel.$dirty
parsedPropertyName = $parse(attrs.ofnTrackProduct)
addDirtyProperty scope.dirtyProducts, scope.product.id, parsedPropertyName, viewValue
scope.displayDirtyProducts()
viewValue
]

View File

@@ -0,0 +1,13 @@
Admin.directive "ofnTrackVariant", ['$parse', ($parse) ->
require: "ngModel"
link: (scope, element, attrs, ngModel) ->
ngModel.$parsers.push (viewValue) ->
dirtyVariants = {}
dirtyVariants = scope.dirtyProducts[scope.product.id].variants if scope.dirtyProducts.hasOwnProperty(scope.product.id) and scope.dirtyProducts[scope.product.id].hasOwnProperty("variants")
if ngModel.$dirty
parsedPropertyName = $parse(attrs.ofnTrackVariant)
addDirtyProperty dirtyVariants, scope.variant.id, parsedPropertyName, viewValue
addDirtyProperty scope.dirtyProducts, scope.product.id, $parse("variants"), dirtyVariants
scope.displayDirtyProducts()
viewValue
]

View File

@@ -0,0 +1,4 @@
Admin.filter "rangeArray", ->
return (input,start,end) ->
input.push(i) for i in [start..end]
input

View File

@@ -0,0 +1,7 @@
Admin.filter "selectFilter", (blankOption) ->
return (lineItems,selectedSupplier,selectedDistributor,selectedOrderCycle) ->
filtered = []
filtered.push lineItem for lineItem in lineItems when (angular.equals(selectedSupplier,"0") || lineItem.supplier.id == selectedSupplier) &&
(angular.equals(selectedDistributor,"0") || lineItem.order.distributor.id == selectedDistributor) &&
(angular.equals(selectedOrderCycle,"0") || lineItem.order.order_cycle.id == selectedOrderCycle)
filtered

View File

@@ -0,0 +1,6 @@
Admin.filter "variantFilter", ->
return (lineItems,selectedUnitsProduct,selectedUnitsVariant,sharedResource) ->
filtered = []
filtered.push lineItem for lineItem in lineItems when (angular.equals(selectedUnitsProduct,{}) ||
(lineItem.units_product.id == selectedUnitsProduct.id && (sharedResource || lineItem.units_variant.id == selectedUnitsVariant.id ) ) )
filtered

View File

@@ -0,0 +1,2 @@
Admin.value "blankOption", ->
{ id: "0", name: "All" }

View File

@@ -0,0 +1,24 @@
Admin.factory "ofnConfirmHandler", (pendingChanges, $compile, $q) ->
return (scope, callback) ->
template = "<div id='dialog-div' style='padding: 10px'><h6>Unsaved changes currently exist, save now or ignore?</h6></div>"
dialogDiv = $compile(template)(scope)
return ->
if pendingChanges.changeCount(pendingChanges.pendingChanges) > 0
dialogDiv.dialog
dialogClass: "no-close"
resizable: false
height: 140
modal: true
buttons:
"SAVE": ->
dialogDiv = $(this)
$q.all(pendingChanges.submitAll()).then ->
callback()
dialogDiv.dialog "close"
"IGNORE": ->
callback()
$(this).dialog "close"
scope.$apply()
dialogDiv.dialog "open"
else
callback()

View File

@@ -1,6 +1,4 @@
sharedServicesModule = angular.module("ofn.shared_services", [])
sharedServicesModule.factory "dataFetcher", [
Admin.factory "dataFetcher", [
"$http", "$q"
($http, $q) ->
return (dataLocation) ->

View File

@@ -0,0 +1,13 @@
Admin.factory "dataSubmitter", [
"$http", "$q", "switchClass"
($http, $q, switchClass) ->
return (changeObj) ->
deferred = $q.defer()
$http.put(changeObj.url).success((data) ->
switchClass changeObj.element, "update-success", ["update-pending", "update-error"], 3000
deferred.resolve data
).error ->
switchClass changeObj.element, "update-error", ["update-pending", "update-success"], false
deferred.reject()
deferred.promise
]

View File

@@ -0,0 +1,32 @@
Admin.factory "pendingChanges",[
"dataSubmitter"
(dataSubmitter) ->
pendingChanges: {}
add: (id, attrName, changeObj) ->
@pendingChanges["#{id}"] = {} unless @pendingChanges.hasOwnProperty("#{id}")
@pendingChanges["#{id}"]["#{attrName}"] = changeObj
removeAll: ->
@pendingChanges = {}
remove: (id, attrName) ->
if @pendingChanges.hasOwnProperty("#{id}")
delete @pendingChanges["#{id}"]["#{attrName}"]
delete @pendingChanges["#{id}"] if @changeCount( @pendingChanges["#{id}"] ) < 1
submitAll: ->
all = []
for id,lineItem of @pendingChanges
for attrName,changeObj of lineItem
all.push @submit(id, attrName, changeObj)
all
submit: (id, attrName, change) ->
dataSubmitter(change).then (data) =>
@remove id, attrName
change.element.dbValue = data["#{attrName}"]
changeCount: (lineItem) ->
Object.keys(lineItem).length
]

View File

@@ -0,0 +1,13 @@
Admin.factory "switchClass", [
"$timeout"
($timeout) ->
return (element,classToAdd,removeClasses,timeout) ->
$timeout.cancel element.timeout if element.timeout
element.removeClass className for className in removeClasses
element.addClass classToAdd
intRegex = /^\d+$/
if timeout && intRegex.test(timeout)
element.timeout = $timeout(->
element.removeClass classToAdd
, timeout, true)
]

View File

@@ -0,0 +1,13 @@
Admin.factory "Taxons", ($resource) ->
resource = $resource "/admin/taxons/search"
return {
findByIDs: (ids) ->
resource.get { ids: ids }
findByTerm: (term) ->
resource.get { q: term }
cleanTaxons: (data) ->
data['taxons'].map (result) -> result
}

View File

@@ -1,45 +0,0 @@
sharedDirectivesModule = angular.module("ofn.shared_directives", [])
sharedDirectivesModule.directive "datetimepicker", ->
require: "ngModel"
link: (scope, element, attrs, ngModel) ->
element.datetimepicker
dateFormat: "yy-mm-dd"
timeFormat: "HH:mm:ss"
stepMinute: 15
onSelect: (dateText, inst) ->
scope.$apply (scope) ->
# Fires ngModel.$parsers
ngModel.$setViewValue dateText
sharedDirectivesModule.directive "datepicker", ->
require: "ngModel"
link: (scope, element, attrs, ngModel) ->
element.datepicker
dateFormat: "yy-mm-dd"
onSelect: (dateText, inst) ->
scope.$apply (scope) ->
# Fires ngModel.$parsers
ngModel.$setViewValue dateText
sharedDirectivesModule.directive "ofnSelect2MinSearch", ->
require: 'ngModel'
link: (scope, element, attrs, ngModel) ->
element.select2
minimumResultsForSearch: attrs.ofnSelect2MinSearch
ngModel.$formatters.push (value) ->
if (value)
element.select2('val', value);
sharedDirectivesModule.directive "ofnToggleColumn", ->
link: (scope, element, attrs) ->
element.addClass "selected" if scope.column.visible
element.click "click", ->
scope.$apply ->
if scope.column.visible
scope.column.visible = false
element.removeClass "selected"
else
scope.column.visible = true
element.addClass "selected"

View File

@@ -4,7 +4,7 @@
= render :partial => 'spree/admin/shared/order_sub_menu'
%div{ 'ng-app' => 'ofn.bulk_order_management', 'ng-controller' => 'AdminOrderMgmtCtrl', 'ng-init' => "initialise('#{@spree_api_key}');loading=true;" }
%div{ 'ng-app' => 'ofn.admin', 'ng-controller' => 'AdminOrderMgmtCtrl', 'ng-init' => "initialise('#{@spree_api_key}');loading=true;" }
%div{ 'ng-show' => '!spree_api_key_ok' }
{{ api_error_msg }}
.filters{ :class => "sixteen columns alpha" }

View File

@@ -13,7 +13,7 @@
%div{ 'ng-app' => 'ofn.bulk_product_edit', 'ng-controller' => 'AdminProductEditCtrl', 'ng-init' => "initialise('#{@spree_api_key}');loading=true;" }
%div{ 'ng-app' => 'ofn.admin', 'ng-controller' => 'AdminProductEditCtrl', 'ng-init' => "initialise('#{@spree_api_key}');loading=true;" }
%div{ 'ng-show' => '!spree_api_key_ok' }
{{ api_error_msg }}
%div.option_tab_titles{ :class => "sixteen columns alpha" }