Merge branch 'master' into 2-0-stable

* master: (125 commits)
  Fix syntax error in GETTING_STARTED.md
  Fix syntax error in README.md
  Fix link syntax errors in REAME.md and GETTING_STARTED.md
  Style recently merged code
  Update gem i18n-js to pick up locale changes
  Fix embedded shopfront menu responsiveness
  Add communications links to README
  Fix script/setup by making it less clever
  Change import and reset logic to work with first page
  Move options to first page
  Fix wrong sort predicates in customer index
  Reset reverse when clicking another column to sort
  Move logic for toggling by column into SortOptions
  Generalize sorting through SortOptions service
  Remove unused sorting preferences in ColumnsCtrl
  Fix frontend sorting in "Bulk Order Management"
  Fix frontend sorting in "Customers" index
  Change sorting to be done in ascending order first
  Wrap rows in customer index with TBODY tag
  Update .rubocop_todo.yml
  ...
This commit is contained in:
Pau Perez
2018-07-27 10:21:47 +02:00
194 changed files with 2522 additions and 1711 deletions

View File

@@ -1,5 +1,6 @@
angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout, $http, $window, BulkProducts, DisplayProperties, dataFetcher, DirtyProducts, VariantUnitManager, StatusMessage, producers, Taxons, SpreeApiAuth, Columns, tax_categories) ->
$scope.loading = true
$scope.loadingAllPages = true
$scope.StatusMessage = StatusMessage
@@ -49,7 +50,10 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
$scope.fetchProducts = ->
$scope.loading = true
BulkProducts.fetch($scope.currentFilters).then ->
$scope.loadingAllPages = true
BulkProducts.fetch($scope.currentFilters, ->
$scope.loadingAllPages = false
).then ->
$scope.resetProducts()
$scope.loading = false

View File

@@ -1,4 +1,4 @@
angular.module("admin.customers").controller "customersCtrl", ($scope, $q, $filter, Customers, TagRuleResource, CurrentShop, RequestMonitor, Columns, pendingChanges, shops, availableCountries) ->
angular.module("admin.customers").controller "customersCtrl", ($scope, $q, $filter, Customers, TagRuleResource, CurrentShop, RequestMonitor, Columns, SortOptions, pendingChanges, shops, availableCountries) ->
$scope.shops = shops
$scope.availableCountries = availableCountries
$scope.RequestMonitor = RequestMonitor
@@ -6,6 +6,7 @@ angular.module("admin.customers").controller "customersCtrl", ($scope, $q, $filt
$scope.customerLimit = 20
$scope.customers = Customers.all
$scope.columns = Columns.columns
$scope.sorting = SortOptions
$scope.confirmRefresh = (event) ->
event.preventDefault() unless pendingChanges.unsavedCount() == 0 || confirm(t("unsaved_changes_warning"))

View File

@@ -1,4 +1,2 @@
angular.module("admin.indexUtils").controller "ColumnsCtrl", ($scope, Columns) ->
$scope.columns = Columns.columns
$scope.predicate = ""
$scope.reverse = false

View File

@@ -3,14 +3,18 @@ angular.module("admin.indexUtils").factory "PagedFetcher", (dataFetcher) ->
# Given a URL like http://example.com/foo?page=::page::&per_page=20
# And the response includes an attribute pages with the number of pages to fetch
# Fetch each page async, and call the processData callback with the resulting data
fetch: (url, processData) ->
fetch: (url, processData, onLastPageComplete) ->
dataFetcher(@urlForPage(url, 1)).then (data) =>
processData data
if data.pages > 1
for page in [2..data.pages]
dataFetcher(@urlForPage(url, page)).then (data) ->
lastPromise = dataFetcher(@urlForPage(url, page)).then (data) ->
processData data
onLastPageComplete && lastPromise.then onLastPageComplete
return
else
onLastPageComplete && onLastPageComplete()
urlForPage: (url, page) ->
url.replace("::page::", page)

View File

@@ -0,0 +1,8 @@
angular.module("admin.indexUtils").factory 'SortOptions', ->
new class SortOptions
predicate: ""
reverse: true
toggle: (predicate) ->
@reverse = (@predicate == predicate) && !@reverse
@predicate = predicate

View File

@@ -1,4 +1,4 @@
angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $http, $q, StatusMessage, Columns, Dereferencer, Orders, LineItems, Enterprises, OrderCycles, VariantUnitManager, RequestMonitor) ->
angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $http, $q, StatusMessage, Columns, SortOptions, Dereferencer, Orders, LineItems, Enterprises, OrderCycles, VariantUnitManager, RequestMonitor) ->
$scope.initialized = false
$scope.RequestMonitor = RequestMonitor
$scope.filteredLineItems = []
@@ -10,6 +10,7 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout,
$scope.selectedUnitsVariant = {}
$scope.sharedResource = false
$scope.columns = Columns.columns
$scope.sorting = SortOptions
$scope.confirmRefresh = ->
LineItems.allSaved() || confirm(t("unsaved_changes_warning"))

View File

@@ -3,6 +3,7 @@ angular.module("admin.productImport").controller "ImportFormCtrl", ($scope, $htt
$scope.entries = {}
$scope.update_counts = {}
$scope.reset_counts = {}
$scope.importSettings = null
$scope.updates = {}
$scope.updated_total = 0
@@ -72,20 +73,21 @@ angular.module("admin.productImport").controller "ImportFormCtrl", ($scope, $htt
'end': end
'filepath': $scope.filepath
'settings': $scope.importSettings
).success((data, status, headers, config) ->
).success((data, status) ->
angular.merge($scope.entries, angular.fromJson(data['entries']))
$scope.sortUpdates(data['reset_counts'])
$scope.updateProgress()
).error((data, status, headers, config) ->
).error((data, status) ->
$scope.exception = data
console.error(data)
)
$scope.importSettings = null
$scope.getSettings = () ->
$scope.importSettings = ProductImportService.getSettings()
$scope.importSettings = {
reset_all_absent: document.getElementsByName('settings[reset_all_absent]')[0].value,
import_into: document.getElementsByName('settings[import_into]')[0].value
}
$scope.sortUpdates = (data) ->
angular.forEach data, (value, key) ->
@@ -104,7 +106,7 @@ angular.module("admin.productImport").controller "ImportFormCtrl", ($scope, $htt
'end': end
'filepath': $scope.filepath
'settings': $scope.importSettings
).success((data, status, headers, config) ->
).success((data, status) ->
$scope.sortResults(data['results'])
angular.forEach data['updated_ids'], (id) ->
@@ -114,7 +116,7 @@ angular.module("admin.productImport").controller "ImportFormCtrl", ($scope, $htt
$scope.update_errors.push(error)
$scope.updateProgress()
).error((data, status, headers, config) ->
).error((data, status) ->
$scope.exception = data
console.error(data)
)
@@ -129,10 +131,11 @@ angular.module("admin.productImport").controller "ImportFormCtrl", ($scope, $htt
$scope.updated_total += value
$scope.resetAbsent = () ->
return unless $scope.importSettings['reset_all_absent']
enterprises_to_reset = []
angular.forEach $scope.importSettings, (settings, enterprise) ->
if settings['reset_all_absent']
enterprises_to_reset.push(enterprise)
angular.forEach $scope.reset_counts, (count, enterprise_id) ->
enterprises_to_reset.push(enterprise_id)
if enterprises_to_reset.length && $scope.updated_ids.length
$http(
@@ -144,11 +147,9 @@ angular.module("admin.productImport").controller "ImportFormCtrl", ($scope, $htt
'reset_absent': true,
'updated_ids': $scope.updated_ids,
'enterprises_to_reset': enterprises_to_reset
).success((data, status, headers, config) ->
console.log(data)
).success((data, status) ->
$scope.updates.products_reset = data
).error((data, status, headers, config) ->
).error((data, status) ->
console.error(data)
)

View File

@@ -2,37 +2,24 @@ angular.module("admin.productImport").controller "ImportOptionsFormCtrl", ($scop
$scope.initForm = () ->
$scope.settings = {} if $scope.settings == undefined
$scope.settings[$scope.supplierId] = {
import_into: 'product_list'
defaults:
count_on_hand:
mode: 'overwrite_all'
on_hand:
mode: 'overwrite_all'
tax_category_id:
mode: 'overwrite_all'
shipping_category_id:
mode: 'overwrite_all'
available_on:
mode: 'overwrite_all'
$scope.settings = {
import_into: 'product_list',
reset_all_absent: false
}
$scope.import_into = 'product_list'
$scope.updateImportInto = () ->
$scope.import_into = $scope.settings[$scope.supplierId]['import_into']
$scope.$watch 'settings', (updated) ->
ProductImportService.updateSettings(updated)
, true
$scope.toggleResetAbsent = (id) ->
checked = $scope.settings[id]['reset_all_absent']
$scope.toggleResetAbsent = ->
checked = $scope.settings['reset_all_absent']
confirmed = confirm t('js.product_import.confirmation') if checked
if confirmed or !checked
ProductImportService.updateResetAbsent($scope.supplierId, $scope.reset_counts[$scope.supplierId], checked)
else
$scope.settings[id]['reset_all_absent'] = false
$scope.settings['reset_all_absent'] = false
$scope.resetTotal = ProductImportService.resetTotal

View File

@@ -8,7 +8,8 @@ angular.module("ofn.admin").factory "BulkProducts", (PagedFetcher, dataFetcher,
, ""
url = "/api/products/bulk_products?page=::page::;per_page=20;#{queryString}"
PagedFetcher.fetch url, (data) => @addProducts data.products
processData = (data) => @addProducts data.products
PagedFetcher.fetch url, processData, onComplete
cloneProduct: (product) ->
$http.post("/api/products/" + product.id + "/clone").success (data) =>
@@ -66,8 +67,13 @@ angular.module("ofn.admin").factory "BulkProducts", (PagedFetcher, dataFetcher,
variantUnitValue: (product, variant) ->
if variant.unit_value?
if product.variant_unit_scale
variant.unit_value / product.variant_unit_scale
@divideAsInteger variant.unit_value, product.variant_unit_scale
else
variant.unit_value
else
null
# forces integer division to avoid javascript floating point imprecision
# using one billion as the multiplier so that it works for numbers with up to 9 decimal places
divideAsInteger: (a, b) ->
(a * 1000000000) / (b * 1000000000)

View File

@@ -1,38 +1,43 @@
angular.module("admin.subscriptions").controller "DetailsController", ($scope, $http, CreditCardResource, StatusMessage) ->
angular.module("admin.subscriptions").controller "DetailsController", ($scope, $http, CustomerResource, StatusMessage) ->
$scope.cardRequired = false
$scope.registerNextCallback 'details', ->
$scope.subscription_form.$submitted = true
if $scope.subscription_details_form.$valid
$scope.subscription_form.$setPristine()
StatusMessage.clear()
$scope.setView('address')
else
StatusMessage.display 'failure', t('admin.subscriptions.details.invalid_error')
return unless $scope.validate()
$scope.subscription_form.$setPristine()
StatusMessage.clear()
$scope.setView('address')
$scope.$watch "subscription.customer_id", (newValue, oldValue) ->
return if !newValue?
$scope.loadAddresses(newValue) unless $scope.subscription.id?
$scope.loadCreditCards(newValue)
$scope.loadCustomer(newValue) unless $scope.subscription.id?
$scope.$watch "subscription.payment_method_id", (newValue, oldValue) ->
return if !newValue?
paymentMethod = ($scope.paymentMethods.filter (pm) -> pm.id == newValue)[0]
return unless paymentMethod?
if paymentMethod.type == "Spree::Gateway::StripeConnect"
$scope.cardRequired = true
else
$scope.cardRequired = false
$scope.subscription.credit_card_id = null
$scope.cardRequired = (paymentMethod.type == "Spree::Gateway::StripeConnect")
$scope.loadCustomer() if $scope.cardRequired && !$scope.customer
$scope.loadAddresses = (customer_id) ->
$http.get("/admin/customers/#{customer_id}/addresses")
.success (response) =>
delete response.bill_address.id
delete response.ship_address.id
angular.extend($scope.subscription.bill_address, response.bill_address)
angular.extend($scope.subscription.ship_address, response.ship_address)
$scope.shipAddressFromBilling() unless response.ship_address.address1?
$scope.loadCustomer = ->
params = { id: $scope.subscription.customer_id }
params.ams_prefix = 'subscription' unless $scope.subscription.id
$scope.customer = CustomerResource.get params, (response) ->
for address in ['bill_address','ship_address']
return unless response[address]
delete response[address].id
return if $scope.subscription[address].address1?
angular.extend($scope.subscription[address], response[address])
$scope.shipAddressFromBilling() unless response.ship_address?.address1?
$scope.loadCreditCards = (customer_id) ->
$scope.creditCards = CreditCardResource.index(customer_id: customer_id)
$scope.validate = ->
return true if $scope.subscription_details_form.$valid && $scope.creditCardOk()
StatusMessage.display 'failure', t('admin.subscriptions.details.invalid_error')
false
$scope.creditCardOk = ->
return true unless $scope.cardRequired
return false unless $scope.customer
return false unless $scope.customer.allow_charges
return false unless $scope.customer.default_card_present
true

View File

@@ -16,7 +16,7 @@ angular.module("admin.subscriptions").controller "OrdersPanelController", ($scop
oc = OrderCycles.byID[id]
return t('js.subscriptions.close_date_not_set') unless oc?.orders_close_at?
closes_at = moment(oc.orders_close_at)
text = if closes_at > moment() then t('js.subscriptions.closes') else t('js.subscription.closed')
text = if closes_at > moment() then t('js.subscriptions.closes') else t('js.subscriptions.closed')
"#{text} #{closes_at.fromNow()}"
$scope.stateText = (state) -> t("spree.order_state.#{state}")

View File

@@ -1,5 +0,0 @@
angular.module("admin.subscriptions").factory 'CreditCardResource', ($resource) ->
resource = $resource '/admin/customers/:customer_id/cards.json', {},
'index':
method: 'GET'
isArray: true

View File

@@ -0,0 +1,2 @@
angular.module("admin.subscriptions").factory 'CustomerResource', ($resource) ->
$resource '/admin/customers/:id.json'

View File

@@ -0,0 +1,17 @@
angular.module("admin.users").directive "resendUserEmailConfirmation", ($http) ->
template: "{{ 'js.admin.resend_user_email_confirmation.' + status | t }}"
scope:
email: "@resendUserEmailConfirmation"
link: (scope, element, attrs) ->
sent = false
scope.status = "resend"
element.bind "click", ->
return if sent
scope.status = "sending"
$http.post("/user/spree_user/confirmation", {spree_user: {email: scope.email}}).success (data) ->
sent = true
element.addClass "action--disabled"
scope.status = "done"
.error (data) ->
scope.status = "failed"

View File

@@ -0,0 +1,6 @@
angular.module("admin.utils").directive "textangularLinksTargetBlank", () ->
restrict: 'CA'
link: (scope, element, attrs) ->
setTimeout ->
element.find(".ta-editor").scope().defaultTagAttributes.a.target = '_blank'
, 500