Skip to content

Commit

Permalink
Revert front end validation and implement backend validation for chan…
Browse files Browse the repository at this point in the history
…ges in datetime order cycle values [OFN-11613]
  • Loading branch information
wandji20 committed Jul 24, 2024
1 parent 44dc034 commit 30c3e8f
Show file tree
Hide file tree
Showing 13 changed files with 108 additions and 153 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ angular.module('admin.orderCycles')

$scope.submit = ($event, destination) ->
$event.preventDefault()
$scope.order_cycle?.trigger_action = $($event.target).data('trigger-action');
$scope.order_cycle?.confirm = $($event.target).data('confirm');
StatusMessage.display 'progress', t('js.saving')
OrderCycle.update(destination, $scope.order_cycle_form)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ angular.module('admin.orderCycles').controller "AdminSimpleEditOrderCycleCtrl",

$scope.submit = ($event, destination) ->
$event.preventDefault()
$scope.order_cycle?.trigger_action = $($event.target).data('trigger-action');
$scope.order_cycle?.confirm = $($event.target).data('confirm');
StatusMessage.display 'progress', t('js.saving')
OrderCycle.mirrorIncomingToOutgoingProducts()
OrderCycle.update(destination, $scope.order_cycle_form) if OrderCycle.confirmNoDistributors()
Original file line number Diff line number Diff line change
Expand Up @@ -161,14 +161,25 @@ angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, $
StatusMessage.display('failure', t('js.order_cycles.create_failure'))

update: (destination, form) ->
oc = new OrderCycleResource({order_cycle: this.dataForSubmit()})
oc = new OrderCycleResource({
order_cycle: this.dataForSubmit(),
confirm: this.order_cycle.confirm,
trigger_action: this.order_cycle.trigger_action
})
oc.$update {order_cycle_id: this.order_cycle.id, reloading: (if destination? then 1 else 0)}, (data) =>
# Hide all confirmation buttons in warning modal
$('#linked-order-warning-modal .modal-actions button.secondary').css({ display: 'none' })
# Show the appropriate confirmation button, open warning modal, and return
if data.trigger_action
StatusMessage.display 'notice', "You have unsaved changes"
$("#linked-order-warning-modal button[data-trigger-action=#{data.trigger_action}]").css({ display: 'block' });
$('.warning-modal button.modal-target-trigger').trigger('click');
return;

form.$setPristine() if form
if destination?
$window.location = destination
else
if ($window.adminOrderCycleUpdateCallback)
adminOrderCycleUpdateCallback(data.order_cycle);
StatusMessage.display 'success', t('js.order_cycles.update_success')
, (response) ->
if response.data.errors?
Expand Down
13 changes: 13 additions & 0 deletions app/components/modal_component/modal_component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@
max-width: 100%;
height: auto;
}

.flex-column {
display: flex;
flex-direction: column;
}

.gap-1 {
gap: 1rem;
}

.gap-2 {
gap: 2rem;
}
}

/* prevent arrow on selected admin menu item appearing above modal */
Expand Down
28 changes: 21 additions & 7 deletions app/controllers/admin/order_cycles_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class OrderCyclesController < Admin::ResourceController
before_action :remove_protected_attrs, only: [:update]
before_action :require_order_cycle_set_params, only: [:bulk_update]
around_action :protect_invalid_destroy, only: :destroy
before_action :verify_datetime_change, only: :update

def index
respond_to do |format|
Expand Down Expand Up @@ -70,12 +71,7 @@ def update
respond_to do |format|
flash[:success] = t('.success') if params[:reloading] == '1'
format.html { redirect_to_after_update_path }
format.json {
render json: { success: true, order_cycle: {
orders_open_at: @order_cycle.orders_open_at&.strftime('%Y-%m-%d %H:%M'),
orders_close_at: @order_cycle.orders_close_at&.strftime('%Y-%m-%d %H:%M')
} }
}
format.json { render json: { success: true } }
end
elsif request.format.html?
render :checkout_options
Expand Down Expand Up @@ -240,7 +236,7 @@ def protect_invalid_destroy
else
begin
yield
rescue ActiveRecord::InvalidForeignKey
rescue ActiveRecord::InvalidForeignKey, ActiveRecord::DeleteRestrictionError
redirect_to main_app.admin_order_cycles_url
flash[:error] = I18n.t('admin.order_cycles.destroy_errors.orders_present')
end
Expand Down Expand Up @@ -299,5 +295,23 @@ def order_cycle_bulk_params
collection_attributes: [:id] + PermittedAttributes::OrderCycle.basic_attributes
).to_h.with_indifferent_access
end

# Check that order cycle datetime values changed if it has existing orders
def verify_datetime_change
return unless params[:order_cycle][:confirm]
return unless @order_cycle.orders.exists?
return if same_dates(@order_cycle.orders_open_at&.to_s,
order_cycle_params[:orders_open_at]) &&
same_dates(@order_cycle.orders_close_at&.to_s, order_cycle_params[:orders_close_at])

render json: { trigger_action: params[:order_cycle][:trigger_action] }
end

def same_dates(string1, string2)
false unless string1 && string2

DateTime.parse(string1).strftime('%Y-%m-%d %H:%M') ==
DateTime.parse(string2).strftime('%Y-%m-%d %H:%M')
end
end
end
1 change: 1 addition & 0 deletions app/models/order_cycle.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class OrderCycle < ApplicationRecord
where incoming: false
}, class_name: "Exchange", dependent: :destroy

has_many :orders, class_name: 'Spree::Order', dependent: :restrict_with_exception
has_many :suppliers, -> { distinct }, source: :sender, through: :cached_incoming_exchanges
has_many :distributors, -> { distinct }, source: :receiver, through: :cached_outgoing_exchanges
has_many :order_cycle_schedules, dependent: :destroy
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.modal-body.flex-column-gap-1
%h6
= t('admin.order_cycles.edit.linked_schedule_warning_modal.title')
%div{ style: 'font-size: 1rem;' }
= t('admin.order_cycles.edit.linked_schedule_warning_modal.content')
%p.modal-actions.justify-end.gap-1
%button.button.secondary{ "ng-click": "submit($event, null)", type: "button", style: "display: none;", data: { action: 'click->modal#close', 'trigger-action': 'save' } }
= t('admin.order_cycles.edit.linked_schedule_warning_modal.proceed')
%button.button.secondary{ "ng-click": "submit($event, '#{main_app.admin_order_cycle_incoming_path(@order_cycle)}')", type: "button", style: "display: none;", data: { action: 'click->modal#close', 'trigger-action': 'saveAndNext' } }
= t('admin.order_cycles.edit.linked_schedule_warning_modal.proceed')
%button.button.secondary{ "ng-click": "submit($event, '#{main_app.admin_order_cycles_path}')", type: "button", style: "display: none;", data: { action: 'click->modal#close', 'trigger-action': 'saveAndBack' } }
= t('admin.order_cycles.edit.linked_schedule_warning_modal.proceed')
%button.button.primary{ type: "button", 'data-action': 'click->modal#close' }
= t('admin.order_cycles.edit.linked_schedule_warning_modal.cancel')
4 changes: 2 additions & 2 deletions app/views/admin/order_cycles/_name_and_timing_form.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
= f.label :orders_open_at, t('.orders_open')
.omega.six.columns.fullwidth_inputs
- if viewing_as_coordinator_of?(@order_cycle)
= f.text_field :orders_open_at, data: { controller: "flatpickr", "flatpickr-enable-time-value": true, action: 'order-cycle#toggleSaveBtns', 'order-cycle-target': 'input' }, 'ng-model' => 'order_cycle.orders_open_at', 'ng-if' => 'loaded()', 'change-warning' => 'order_cycle', class: "datetimepicker"
= f.text_field :orders_open_at, data: { controller: "flatpickr", "flatpickr-enable-time-value": true }, 'ng-model' => 'order_cycle.orders_open_at', 'ng-if' => 'loaded()', 'change-warning' => 'order_cycle', class: "datetimepicker"
- else
{{ order_cycle.orders_open_at }}

Expand All @@ -24,7 +24,7 @@
= f.label :orders_close, t('.orders_close')
.six.columns.omega.fullwidth_inputs
- if viewing_as_coordinator_of?(@order_cycle)
= f.text_field :orders_close_at, data: { controller: "flatpickr", "flatpickr-enable-time-value": true, action: 'order-cycle#toggleSaveBtns', 'order-cycle-target': 'input' }, 'ng-model' => 'order_cycle.orders_close_at', 'ng-if' => 'loaded()', 'change-warning' => 'order_cycle', class: "datetimepicker"
= f.text_field :orders_close_at, data: { controller: "flatpickr", "flatpickr-enable-time-value": true }, 'ng-model' => 'order_cycle.orders_close_at', 'ng-if' => 'loaded()', 'change-warning' => 'order_cycle', class: "datetimepicker"
- else
{{ order_cycle.orders_close_at }}

Expand Down
50 changes: 15 additions & 35 deletions app/views/admin/order_cycles/edit.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -15,46 +15,26 @@
= t :edit_order_cycle
- ng_controller = @order_cycle.simple? ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl'
- has_scheduled_order = @order_cycle.schedules.exists?
= admin_inject_order_cycle_instance(@order_cycle)
= form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.orderCycles', 'ng-controller' => ng_controller, name: 'order_cycle_form', data: { controller: 'modal modal-link order-cycle', "modal-link-target-value": "linked-schedule-warning-modal", 'order-cycle-has-schedule-value': has_scheduled_order, 'order-cycle-init-vals-value': { 'order_cycle[orders_open_at]': @order_cycle.orders_open_at&.strftime('%Y-%m-%d %H:%M'), 'order_cycle[orders_close_at]': @order_cycle.orders_close_at&.strftime('%Y-%m-%d %H:%M') } } } do |f|
= form_for [main_app, :admin, @order_cycle], :url => '', :html => {:class => 'ng order_cycle', 'ng-app' => 'admin.orderCycles', 'ng-controller' => ng_controller, name: 'order_cycle_form'} do |f|
%save-bar{ dirty: "order_cycle_form.$dirty", persist: "true" }
%div#form-actions
%input.red{ type: "button", value: t('.save'), "ng-click": "submit($event, null)", "ng-disabled": "!order_cycle_form.$dirty || order_cycle_form.$invalid" }
- if @order_cycle.simple?
%input.red{ type: "button", value: t('.save_and_back_to_list'), "ng-click": "submit($event, '#{main_app.admin_order_cycles_path}')", "ng-disabled": "!order_cycle_form.$dirty || order_cycle_form.$invalid" }
- else
%input.red{ type: "button", value: t('.save_and_next'), "ng-click": "submit($event, '#{main_app.admin_order_cycle_incoming_path(@order_cycle)}')", "ng-disabled": "!order_cycle_form.$dirty || order_cycle_form.$invalid" }
%input{ type: "button", value: t('.next'), "ng-click": "cancel('#{main_app.admin_order_cycle_incoming_path(@order_cycle)}')", "ng-disabled": "order_cycle_form.$dirty" }
%input{ type: "button", "ng-value": "order_cycle_form.$dirty ? '#{t('.cancel')}' : '#{t('.back_to_list')}'", "ng-click": "cancel('#{main_app.admin_order_cycles_path}')" }
%div#modal-actions{style: "display: none;"}
%input.red{ type: "button", value: t('.save'), "ng-disabled": "!order_cycle_form.$dirty || order_cycle_form.$invalid", data: { 'action': 'click->modal-link#open click->order-cycle#updateModalConfirmButton', 'target': 'save'} }
- if @order_cycle.simple?
%input.red{ type: "button", value: t('.save_and_back_to_list'), "ng-disabled": "!order_cycle_form.$dirty || order_cycle_form.$invalid", data: { 'action': 'click->modal-link#open click->order-cycle#updateModalConfirmButton', 'target': 'saveAndBack'} }
- else
%input.red{ type: "button", value: t('.save_and_next'), "ng-disabled": "!order_cycle_form.$dirty || order_cycle_form.$invalid", data: { 'action': 'click->modal-link#open click->order-cycle#updateModalConfirmButton', 'target': 'saveAndNext'} }
%input{ type: "button", value: t('.next'), "ng-click": "cancel('#{main_app.admin_order_cycle_incoming_path(@order_cycle)}')", "ng-disabled": "order_cycle_form.$dirty" }
%input{ type: "button", "ng-value": "order_cycle_form.$dirty ? '#{t('.cancel')}' : '#{t('.back_to_list')}'", "ng-click": "cancel('#{main_app.admin_order_cycles_path}')" }
%input.red{ type: "button", value: t('.save'), "ng-click": "submit($event, null)", "ng-disabled": "!order_cycle_form.$dirty || order_cycle_form.$invalid", data: { confirm: "true", 'trigger-action': 'save' } }
- if @order_cycle.simple?
%input.red{ type: "button", value: t('.save_and_back_to_list'), "ng-click": "submit($event, '#{main_app.admin_order_cycles_path}')", "ng-disabled": "!order_cycle_form.$dirty || order_cycle_form.$invalid", data: { confirm: "true", 'trigger-action': 'saveAndBack' } }
- else
%input.red{ type: "button", value: t('.save_and_next'), "ng-click": "submit($event, '#{main_app.admin_order_cycle_incoming_path(@order_cycle)}')", "ng-disabled": "!order_cycle_form.$dirty || order_cycle_form.$invalid", data: { confirm: "true", 'trigger-action': 'saveAndNext' } }
%input{ type: "button", value: t('.next'), "ng-click": "cancel('#{main_app.admin_order_cycle_incoming_path(@order_cycle)}')", "ng-disabled": "order_cycle_form.$dirty" }
%input{ type: "button", "ng-value": "order_cycle_form.$dirty ? '#{t('.cancel')}' : '#{t('.back_to_list')}'", "ng-click": "cancel('#{main_app.admin_order_cycles_path}')" }

- if @order_cycle.simple?
= render 'simple_form', f: f
- else
= render 'form', f: f

- if has_scheduled_order
= render ModalComponent.new(id: "linked-schedule-warning-modal", close_button: false) do
.content
.modal-body
%h6
= t('admin.order_cycles.edit.linked_schedule_warning_modal.title')
%div{ style: 'font-size: 1rem;' }
= t('admin.order_cycles.edit.linked_schedule_warning_modal.content')
%p.modal-actions.justify-end
%button.button.secondary#modal-confirm{ type: "button", 'data-action': 'click->modal#close', style: 'display: none;', data: { 'order-cycle-target': 'modalConfirm', request: 'save' }, "ng-click": "submit($event, null)" }
= t('admin.order_cycles.edit.linked_schedule_warning_modal.proceed')
%button.button.secondary#modal-confirm{ type: "button", 'data-action': 'click->modal#close', style: 'display: none;', data: { 'order-cycle-target': 'modalConfirm', request: 'saveAndNext' }, "ng-click": "submit($event, '#{main_app.admin_order_cycle_incoming_path(@order_cycle)}')" }
= t('admin.order_cycles.edit.linked_schedule_warning_modal.proceed')
%button.button.secondary#modal-confirm{ type: "button", 'data-action': 'click->modal#close', style: 'display: none;', data: { 'order-cycle-target': 'modalConfirm', request: 'saveAndBack' }, "ng-click": "submit($event, '#{main_app.admin_order_cycles_path}')" }
= t('admin.order_cycles.edit.linked_schedule_warning_modal.proceed')
%button.button.primary{ type: "button", 'data-action': 'click->modal#close' }
= t('admin.order_cycles.edit.linked_schedule_warning_modal.cancel')
- if @order_cycle.orders.exists?
%div.warning-modal{ data: { controller: 'modal modal-link', 'modal-link-target-value': "linked-order-warning-modal" } }
%button.modal-target-trigger{ type: 'button', data: { 'action': 'modal-link#open' }, style: 'display: none;' }
= render ModalComponent.new(id: "linked-order-warning-modal", close_button: false) do
.content.flex-column.gap-2
= render 'date_time_warning_modal_content'
52 changes: 0 additions & 52 deletions app/webpacker/controllers/order_cycle_controller.js

This file was deleted.

24 changes: 3 additions & 21 deletions app/webpacker/css/admin/order_cycles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -63,24 +63,6 @@ form.order_cycle {
}
}

#linked-schedule-warning-modal {
.reveal-modal {
width: 28rem;

.content {
display: flex;
flex-direction: column;
gap: 2rem;

.modal-body {
display: flex;
flex-direction: column;
gap: 1rem;
}

.modal-actions {
gap: 1rem;
}
}
}
}
#linked-order-warning-modal .reveal-modal{
width: 28rem;
}
Loading

0 comments on commit 30c3e8f

Please sign in to comment.