mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-29 21:17:17 +00:00
Merge pull request #7211 from jibees/4207-add-query-params-in-url
Orders list : save filters params
This commit is contained in:
@@ -8,6 +8,7 @@ angular.module("ofn.admin", [
|
||||
"admin.dropdown",
|
||||
"admin.products",
|
||||
"admin.taxons",
|
||||
"infinite-scroll"
|
||||
"infinite-scroll",
|
||||
"admin.orders"
|
||||
]).config ($httpProvider) ->
|
||||
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"
|
||||
|
||||
@@ -110,5 +110,8 @@
|
||||
// foundation
|
||||
//= require ../shared/mm-foundation-tpls-0.9.0-20180826174721.min.js
|
||||
|
||||
// LocalStorage
|
||||
//= require ../shared/angular-local-storage.js
|
||||
|
||||
// requires the rest of the JS code in this folder
|
||||
//= require_tree .
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
angular.module("ofn.admin").directive "select2WatchNgModel", () ->
|
||||
restrict: 'E'
|
||||
scope: true
|
||||
require: "ngModel"
|
||||
link: (scope, element, attrs, ngModel) ->
|
||||
ngModel.$render = () ->
|
||||
newValue = ngModel.$viewValue;
|
||||
element.children(".select2").select2("val", newValue)
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module("admin.orders").controller "ordersCtrl", ($scope, $timeout, RequestMonitor, Orders, SortOptions, $window, $filter) ->
|
||||
angular.module("admin.orders").controller "ordersCtrl", ($scope, $timeout, RequestMonitor, Orders, SortOptions, $window, $filter, $location, KeyValueMapStore) ->
|
||||
$scope.RequestMonitor = RequestMonitor
|
||||
$scope.pagination = Orders.pagination
|
||||
$scope.orders = Orders.all
|
||||
@@ -15,39 +15,54 @@ angular.module("admin.orders").controller "ordersCtrl", ($scope, $timeout, Reque
|
||||
$scope.poll = 0
|
||||
$scope.rowStatus = {}
|
||||
|
||||
KeyValueMapStore.localStorageKey = 'ordersFilters'
|
||||
KeyValueMapStore.storableKeys = ["q", "sorting", "page", "per_page"]
|
||||
|
||||
$scope.initialise = ->
|
||||
unless KeyValueMapStore.restoreValues($scope)
|
||||
$scope.setDefaults()
|
||||
|
||||
$scope.fetchResults()
|
||||
|
||||
$scope.setDefaults = ->
|
||||
$scope.per_page = 15
|
||||
$scope.q = {
|
||||
completed_at_not_null: true
|
||||
}
|
||||
|
||||
$scope.clearFilters = () ->
|
||||
KeyValueMapStore.clearKeyValueMap()
|
||||
$scope.setDefaults()
|
||||
$scope.fetchResults()
|
||||
|
||||
$scope.fetchResults = (page=1) ->
|
||||
startDateWithTime = $scope.appendStringIfNotEmpty($scope['q']['completed_at_gteq'], ' 00:00:00')
|
||||
endDateWithTime = $scope.appendStringIfNotEmpty($scope['q']['completed_at_lteq'], ' 23:59:59')
|
||||
startDateWithTime = $scope.appendStringIfNotEmpty($scope.q?.completed_at_gteq, ' 00:00:00')
|
||||
endDateWithTime = $scope.appendStringIfNotEmpty($scope.q?.completed_at_lteq, ' 23:59:59')
|
||||
|
||||
$scope.resetSelected()
|
||||
params = {
|
||||
'q[completed_at_gteq]': startDateWithTime,
|
||||
'q[completed_at_lteq]': endDateWithTime,
|
||||
'q[state_eq]': $scope['q']['state_eq'],
|
||||
'q[number_cont]': $scope['q']['number_cont'],
|
||||
'q[email_cont]': $scope['q']['email_cont'],
|
||||
'q[bill_address_firstname_start]': $scope['q']['bill_address_firstname_start'],
|
||||
'q[bill_address_lastname_start]': $scope['q']['bill_address_lastname_start'],
|
||||
'q[state_eq]': $scope.q?.state_eq,
|
||||
'q[number_cont]': $scope.q?.number_cont,
|
||||
'q[email_cont]': $scope.q?.email_cont,
|
||||
'q[bill_address_firstname_start]': $scope.q?.bill_address_firstname_start,
|
||||
'q[bill_address_lastname_start]': $scope.q?.bill_address_lastname_start,
|
||||
# Set default checkbox values to null. See: https://github.com/openfoodfoundation/openfoodnetwork/pull/3076#issuecomment-440010498
|
||||
'q[completed_at_not_null]': $scope['q']['completed_at_not_null'] || null,
|
||||
'q[distributor_id_in][]': $scope['q']['distributor_id_in'],
|
||||
'q[order_cycle_id_in][]': $scope['q']['order_cycle_id_in'],
|
||||
'q[completed_at_not_null]': $scope.q?.completed_at_not_null || null,
|
||||
'q[distributor_id_in][]': $scope.q?.distributor_id_in,
|
||||
'q[order_cycle_id_in][]': $scope.q?.order_cycle_id_in,
|
||||
'q[s]': $scope.sorting || 'completed_at desc',
|
||||
shipping_method_id: $scope.shipping_method_id,
|
||||
shipping_method_id: $scope.q?.shipping_method_id,
|
||||
per_page: $scope.per_page,
|
||||
page: page
|
||||
}
|
||||
KeyValueMapStore.setStoredValues($scope)
|
||||
RequestMonitor.load(Orders.index(params).$promise)
|
||||
|
||||
$scope.appendStringIfNotEmpty = (baseString, stringToAppend) ->
|
||||
return baseString unless baseString
|
||||
return baseString if baseString.endsWith(stringToAppend)
|
||||
|
||||
baseString + stringToAppend
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
angular.module("admin.orders", ['admin.indexUtils', 'ngResource', 'mm.foundation'])
|
||||
angular.module("admin.orders", ['admin.indexUtils', 'ngResource', 'mm.foundation', "OFNShared"])
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
angular.module("admin.indexUtils").factory 'KeyValueMapStore', (localStorageService)->
|
||||
new class KeyValueMapStore
|
||||
localStorageKey: ''
|
||||
storableKeys: []
|
||||
|
||||
constructor: ->
|
||||
localStorageService.setStorageType("sessionStorage")
|
||||
|
||||
getStoredKeyValueMap: ->
|
||||
localStorageService.get(@localStorageKey) || {}
|
||||
|
||||
setStoredValues: (source) ->
|
||||
keyValueMap = {}
|
||||
for key in @storableKeys
|
||||
keyValueMap[key] = source[key]
|
||||
localStorageService.set(@localStorageKey, keyValueMap)
|
||||
|
||||
restoreValues: (target) ->
|
||||
storedKeyValueMap = @getStoredKeyValueMap()
|
||||
|
||||
return false if _.isEmpty(storedKeyValueMap)
|
||||
|
||||
for k,v of storedKeyValueMap
|
||||
target[k] = v
|
||||
|
||||
return true
|
||||
|
||||
clearKeyValueMap: () ->
|
||||
localStorageService.remove(@localStorageKey)
|
||||
@@ -2,10 +2,13 @@ angular.module("admin.utils").directive "datepicker", ($window, $timeout) ->
|
||||
require: "ngModel"
|
||||
link: (scope, element, attrs, ngModel) ->
|
||||
$timeout ->
|
||||
flatpickr(element, Object.assign(
|
||||
flapickrInstance = flatpickr(element, Object.assign(
|
||||
{},
|
||||
$window.FLATPICKR_DATE_DEFAULT, {
|
||||
onOpen: (selectedDates, dateStr, instance) ->
|
||||
instance.setDate(ngModel.$modelValue)
|
||||
}
|
||||
));
|
||||
ngModel.$render = () ->
|
||||
newValue = ngModel.$viewValue;
|
||||
flapickrInstance?.setDate(newValue)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
window.OFNShared = angular.module("OFNShared", [
|
||||
"mm.foundation"
|
||||
"mm.foundation",
|
||||
"LocalStorageModule"
|
||||
]).config ($httpProvider) ->
|
||||
$httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#table-filter {
|
||||
|
||||
.field {
|
||||
input[type="text"], input[type="phone"],
|
||||
input[type="email"], input[type="number"],
|
||||
@@ -9,6 +8,9 @@
|
||||
}
|
||||
|
||||
.actions {
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
column-gap: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,22 +10,23 @@
|
||||
= text_field_tag "q[completed_at_lteq]", nil, class: 'datepicker', datepicker: 'q.completed_at_lteq', 'ng-model' => 'q.completed_at_lteq', :placeholder => t(:stop)
|
||||
.field
|
||||
= label_tag nil, t(:status)
|
||||
= select_tag("q[state_eq]",
|
||||
options_for_select(Spree::Order.state_machines[:state].states.collect {|s| [t("spree.order_state.#{s.name}"), s.value]}),
|
||||
{include_blank: true, class: 'select2', 'ng-model' => 'q.state_eq'})
|
||||
%select2-watch-ng-model{'ng-model': 'q.state_eq'}
|
||||
= select_tag("q[state_eq]",
|
||||
options_for_select(Spree::Order.state_machines[:state].states.collect {|s| [t("spree.order_state.#{s.name}"), s.value]}),
|
||||
{include_blank: true, class: 'select2', 'ng-model' => 'q.state_eq'})
|
||||
.four.columns
|
||||
.field
|
||||
= label_tag nil, t(:order_number)
|
||||
= label_tag "q_number_cont", t(:order_number)
|
||||
= text_field_tag "q[number_cont]", nil, "ng-model" => "q.number_cont", "ng-keypress" => "$event.keyCode === 13 && fetchResults()"
|
||||
.field
|
||||
= label_tag nil, t(:email)
|
||||
= email_field_tag "q[email_cont", nil, "ng-model" => "q.email_cont", "ng-keypress" => "$event.keyCode === 13 && fetchResults()"
|
||||
= label_tag "q_email_cont", t(:email)
|
||||
= email_field_tag "q[email_cont]", nil, "ng-model" => "q.email_cont", "ng-keypress" => "$event.keyCode === 13 && fetchResults()"
|
||||
.four.columns
|
||||
.field
|
||||
= label_tag nil, t(:first_name_begins_with)
|
||||
= label_tag "q_bill_address_firstname_start", t(:first_name_begins_with)
|
||||
= text_field_tag "q[bill_address_firstname_start]", nil, size: 25, "ng-model" => "q.bill_address_firstname_start", "ng-keypress" => "$event.keyCode === 13 && fetchResults()"
|
||||
.field
|
||||
= label_tag nil, t(:last_name_begins_with)
|
||||
= label_tag "q_bill_address_lastname_start", t(:last_name_begins_with)
|
||||
= text_field_tag "q[bill_address_lastname_start]", nil, size: 25, "ng-model" => "q.bill_address_lastname_start", "ng-keypress" => "$event.keyCode === 13 && fetchResults()"
|
||||
.omega.four.columns
|
||||
.field.checkbox
|
||||
@@ -34,21 +35,26 @@
|
||||
= t(:show_only_complete_orders)
|
||||
.field
|
||||
= label_tag nil, t(:shipping_method)
|
||||
= select_tag("shipping_method_id",
|
||||
%select2-watch-ng-model{'ng-model': 'q.shipping_method_id'}
|
||||
= select_tag("q[shipping_method_id]",
|
||||
options_for_select(Spree::ShippingMethod.managed_by(spree_current_user).collect {|s| [t("spree.shipping_method_names.#{s.name}"), s.id]}),
|
||||
{include_blank: true, class: 'select2', 'ng-model' => 'shipping_method_id'})
|
||||
{include_blank: true, class: 'select2', 'ng-model': 'q.shipping_method_id'})
|
||||
.field-block.alpha.eight.columns
|
||||
= label_tag nil, t(:distributors)
|
||||
= select_tag("q[distributor_id_in]",
|
||||
options_for_select(Enterprise.is_distributor.managed_by(spree_current_user).map {|e| [e.name, e.id]}, params[:distributor_ids]),
|
||||
{class: "select2 fullwidth", multiple: true, 'ng-model' => 'q.distributor_id_in'})
|
||||
%select2-watch-ng-model{'ng-model': 'q.distributor_id_in'}
|
||||
= select_tag("q[distributor_id_in]",
|
||||
options_for_select(Enterprise.is_distributor.managed_by(spree_current_user).map {|e| [e.name, e.id]}, params[:distributor_ids]),
|
||||
{class: "select2 fullwidth", multiple: true, 'ng-model' => 'q.distributor_id_in'})
|
||||
.field-block.omega.eight.columns
|
||||
= label_tag nil, t(:order_cycles)
|
||||
= select_tag("q[order_cycle_id_in]",
|
||||
options_for_select(OrderCycle.managed_by(spree_current_user).where('order_cycles.orders_close_at is not null').order('order_cycles.orders_close_at DESC').map {|oc| [oc.name, oc.id]}, params[:order_cycle_ids]),
|
||||
{class: "select2 fullwidth", multiple: true, 'ng-model' => 'q.order_cycle_id_in'})
|
||||
%select2-watch-ng-model{'ng-model': 'q.order_cycle_id_in'}
|
||||
= select_tag("q[order_cycle_id_in]",
|
||||
options_for_select(OrderCycle.managed_by(spree_current_user).where('order_cycles.orders_close_at is not null').order('order_cycles.orders_close_at DESC').map {|oc| [oc.name, oc.id]}, params[:order_cycle_ids]),
|
||||
{class: "select2 fullwidth", multiple: true, 'ng-model' => 'q.order_cycle_id_in'})
|
||||
.clearfix
|
||||
.actions.filter-actions
|
||||
%div
|
||||
%a.button.icon-search{'ng-click' => 'fetchResults()'}
|
||||
= t(:filter_results)
|
||||
%a.button.icon-search{'ng-click' => 'fetchResults()'}
|
||||
= t(:filter_results)
|
||||
%a.button{'ng-click' => 'clearFilters()', "id": "clear_filters_button"}
|
||||
= t(:clear_filters)
|
||||
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
= csrf_meta_tags
|
||||
|
||||
- content_for :main_ng_app_name do
|
||||
= "ofn.admin"
|
||||
|
||||
- content_for :page_actions do
|
||||
- if can?(:fire, @order)
|
||||
%li= event_links
|
||||
= render partial: 'spree/admin/shared/order_links'
|
||||
- if can?(:admin, Spree::Order)
|
||||
%li= button_link_to t(:back_to_orders_list), admin_orders_path, :icon => 'icon-arrow-left'
|
||||
%li{"ng-controller" => "ordersCtrl"}
|
||||
%a.button.icon-arrow-left{icon: 'icon-arrow-left', ng: { href: admin_orders_path }}
|
||||
= t(:back_to_orders_list)
|
||||
|
||||
= render partial: "spree/admin/shared/order_page_title"
|
||||
= render partial: "spree/admin/shared/order_tabs", locals: { current: 'Order Details' }
|
||||
@@ -17,7 +23,7 @@
|
||||
|
||||
= admin_inject_shops(module: 'admin.orders')
|
||||
= admin_inject_order_cycles
|
||||
%div{"ng-app" => "admin.orders", "ng-controller" => "orderCtrl", "ofn-distributor-id" => @order.distributor_id, "ofn-order-cycle-id" => @order.order_cycle_id}
|
||||
%div{"ng-controller" => "orderCtrl", "ofn-distributor-id" => @order.distributor_id, "ofn-order-cycle-id" => @order.order_cycle_id}
|
||||
|
||||
= render :partial => 'add_product' if can?(:update, @order)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
= render partial: 'spree/admin/shared/order_sub_menu'
|
||||
|
||||
- content_for :main_ng_app_name do
|
||||
= "admin.orders"
|
||||
= "ofn.admin"
|
||||
|
||||
- content_for :main_ng_ctrl_name do
|
||||
= "ordersCtrl"
|
||||
|
||||
@@ -333,6 +333,7 @@ en:
|
||||
no_pending_payments: "No pending payments"
|
||||
invalid_payment_state: "Invalid payment state: %{state}"
|
||||
filter_results: Filter Results
|
||||
clear_filters: Clear filters
|
||||
quantity: Quantity
|
||||
pick_up: Pick up
|
||||
ok: Ok
|
||||
|
||||
@@ -111,4 +111,97 @@ feature '
|
||||
expect(page).to have_current_path spree.edit_admin_order_path(incomplete_order)
|
||||
end
|
||||
end
|
||||
|
||||
context "test the 'Only show the complete orders' checkbox" do
|
||||
scenario "display or not incomplete order" do
|
||||
incomplete_order = create(:order, distributor: distributor, order_cycle: order_cycle)
|
||||
complete_order = create(
|
||||
:order,
|
||||
distributor: distributor,
|
||||
order_cycle: order_cycle,
|
||||
user: user,
|
||||
state: 'complete',
|
||||
payment_state: 'balance_due',
|
||||
completed_at: 1.day.ago
|
||||
)
|
||||
login_as_admin_and_visit spree.admin_orders_path
|
||||
expect(page).to have_content complete_order.number
|
||||
expect(page).to have_no_content incomplete_order.number
|
||||
|
||||
uncheck 'Only show complete orders'
|
||||
page.find('a.icon-search').click
|
||||
|
||||
expect(page).to have_content complete_order.number
|
||||
expect(page).to have_content incomplete_order.number
|
||||
end
|
||||
end
|
||||
|
||||
context "save the filter params" do
|
||||
let!(:shipping_method) { create(:shipping_method, name: "UPS Ground") }
|
||||
let!(:user) { create(:user, email: 'an@email.com') }
|
||||
let!(:order) do
|
||||
create(
|
||||
:order,
|
||||
distributor: distributor,
|
||||
order_cycle: order_cycle,
|
||||
user: user,
|
||||
number: "R123456",
|
||||
state: 'complete',
|
||||
payment_state: 'balance_due',
|
||||
completed_at: 1.day.ago
|
||||
)
|
||||
end
|
||||
before :each do
|
||||
login_as_admin_and_visit spree.admin_orders_path
|
||||
|
||||
# Specify each filters
|
||||
uncheck 'Only show complete orders'
|
||||
fill_in "Invoice number", with: "R123456"
|
||||
select2_select order_cycle.name, from: 'q_order_cycle_id_in'
|
||||
select2_select distributor.name, from: 'q_distributor_id_in'
|
||||
select2_select shipping_method.name, from: 'q_shipping_method_id'
|
||||
select2_select "complete", from: 'q_state_eq'
|
||||
fill_in "Email", with: user.email
|
||||
fill_in "First name begins with", with: "J"
|
||||
fill_in "Last name begins with", with: "D"
|
||||
find('#q_completed_at_gteq').click
|
||||
select_date_from_datepicker Time.zone.at(1.week.ago)
|
||||
find('#q_completed_at_lteq').click
|
||||
select_date_from_datepicker Time.zone.now
|
||||
|
||||
page.find('a.icon-search').click
|
||||
end
|
||||
|
||||
scenario "when reloading the page" do
|
||||
page.driver.refresh
|
||||
|
||||
# Check every filters to be equal
|
||||
expect(find_field("Only show complete orders")).not_to be_checked
|
||||
expect(find_field("Invoice number").value).to eq "R123456"
|
||||
expect(find("#s2id_q_shipping_method_id").text).to eq shipping_method.name
|
||||
expect(find("#s2id_q_state_eq").text).to eq "complete"
|
||||
expect(find("#s2id_q_distributor_id_in").text).to eq distributor.name
|
||||
expect(find("#s2id_q_order_cycle_id_in").text).to eq order_cycle.name
|
||||
expect(find_field("Email").value).to eq user.email
|
||||
expect(find_field("First name begins with").value).to eq "J"
|
||||
expect(find_field("Last name begins with").value).to eq "D"
|
||||
expect(find("#q_completed_at_gteq").value).to eq 1.week.ago.strftime("%Y-%m-%d")
|
||||
expect(find("#q_completed_at_lteq").value).to eq Time.zone.now.strftime("%Y-%m-%d")
|
||||
end
|
||||
|
||||
scenario "and clear filters" do
|
||||
find("a#clear_filters_button").click
|
||||
expect(find_field("Only show complete orders")).to be_checked
|
||||
expect(find_field("Invoice number").value).to eq ""
|
||||
expect(find("#s2id_q_shipping_method_id").text).to be_empty
|
||||
expect(find("#s2id_q_state_eq").text).to be_empty
|
||||
expect(find("#s2id_q_distributor_id_in").text).to be_empty
|
||||
expect(find("#s2id_q_order_cycle_id_in").text).to be_empty
|
||||
expect(find_field("Email").value).to be_empty
|
||||
expect(find_field("First name begins with").value).to be_empty
|
||||
expect(find_field("Last name begins with").value).to be_empty
|
||||
expect(find("#q_completed_at_gteq").value).to be_empty
|
||||
expect(find("#q_completed_at_lteq").value).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
describe "Test KeyValueMapStore service", ->
|
||||
|
||||
KeyValueMapStore = null
|
||||
|
||||
beforeEach ->
|
||||
module "ofn.admin"
|
||||
|
||||
beforeEach inject (_KeyValueMapStore_) ->
|
||||
KeyValueMapStore = _KeyValueMapStore_
|
||||
|
||||
it "set and restore filters", ->
|
||||
KeyValueMapStore.localStorageKey = 'localStorageKey'
|
||||
KeyValueMapStore.storableKeys = ["a", "b", "c"]
|
||||
source =
|
||||
a: "1",
|
||||
b: "2",
|
||||
d: "4"
|
||||
KeyValueMapStore.setStoredValues(source)
|
||||
source = {}
|
||||
restored = KeyValueMapStore.restoreValues(source)
|
||||
expect(restored).toEqual true
|
||||
expect(source).toEqual {a: '1', b: '2'}
|
||||
|
||||
it "clear filters", ->
|
||||
KeyValueMapStore.storageKey = 'localStorageKey'
|
||||
KeyValueMapStore.storableFilters = ["a", "b", "c"]
|
||||
source =
|
||||
a: "1",
|
||||
b: "2",
|
||||
d: "4"
|
||||
KeyValueMapStore.setStoredValues(source)
|
||||
KeyValueMapStore.clearKeyValueMap()
|
||||
source = {}
|
||||
restored = KeyValueMapStore.restoreValues(source)
|
||||
expect(restored).toEqual false
|
||||
expect(source).toEqual {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user