Merge pull request #7211 from jibees/4207-add-query-params-in-url

Orders list : save filters params
This commit is contained in:
Pau Pérez Fabregat
2021-06-18 11:19:26 +02:00
committed by GitHub
15 changed files with 247 additions and 41 deletions

View File

@@ -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, */*"

View File

@@ -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 .

View File

@@ -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)

View File

@@ -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

View File

@@ -1 +1 @@
angular.module("admin.orders", ['admin.indexUtils', 'ngResource', 'mm.foundation'])
angular.module("admin.orders", ['admin.indexUtils', 'ngResource', 'mm.foundation', "OFNShared"])

View File

@@ -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)

View File

@@ -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)

View File

@@ -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, */*"

View File

@@ -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;
}
}
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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 {}