Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add warning modal when changing dates for order cycle with linked orders [OFN-11613] #12653

Merged
merged 7 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,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,7 +161,11 @@ 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) =>
form.$setPristine() if form
if destination?
Expand All @@ -171,6 +175,8 @@ angular.module('admin.orderCycles').factory 'OrderCycle', ($resource, $window, $
, (response) ->
if response.data.errors?
StatusMessage.display('failure', response.data.errors[0])
else if (response.data.trigger_action)
StatusMessage.display('notice', t('js.order_cycles.unsaved_changes'), response.data.trigger_action)
else
StatusMessage.display('failure', t('js.order_cycles.update_failure'))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ angular.module("admin.utils").factory "StatusMessage", ->

statusMessage:
text: ""
style: {}
style: {},
type: null,
actionName: null

invalidMessage: ""

Expand All @@ -23,11 +25,15 @@ angular.module("admin.utils").factory "StatusMessage", ->
active: ->
@statusMessage.text != ''

display: (type, text) ->
display: (type, text, actionName = null) ->
@statusMessage.text = text
@statusMessage.type = type
@statusMessage.actionName = actionName
@statusMessage.style = @types[type].style
null

clear: ->
@statusMessage.text = ''
@statusMessage.style = {}
@statusMessage.type = null
@statusMessage.actionName = null
2 changes: 1 addition & 1 deletion app/assets/javascripts/templates/admin/save_bar.html.haml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#save-bar.animate-show{ "ng-show": 'dirty || persist || StatusMessage.active()' }
.container
.seven.columns.alpha
%h5#status-message{ "ng-show": "StatusMessage.invalidMessage == ''", "ng-style": 'StatusMessage.statusMessage.style' }
%h5#status-message{ "ng-show": "StatusMessage.invalidMessage == ''", "ng-style": 'StatusMessage.statusMessage.style', data: { 'order-cycle-form-target': 'statusMessage' }, "ng-attr-data-type": "{{StatusMessage.statusMessage.type}}", "ng-attr-data-action-name": "{{StatusMessage.statusMessage.actionName}}" }
{{ StatusMessage.statusMessage.text || " " }}
%h5#status-message{ style: 'color: #C85136', "ng-show": "StatusMessage.invalidMessage !== ''" }
{{ StatusMessage.invalidMessage || " " }}
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
20 changes: 19 additions & 1 deletion 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 @@ -235,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 @@ -294,5 +295,22 @@ 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, order_cycle_params[:orders_open_at]) &&
same_dates(@order_cycle.orders_close_at, order_cycle_params[:orders_close_at])

render json: { trigger_action: params[:order_cycle][:trigger_action] },
status: :unprocessable_entity
end

def same_dates(date, string)
false unless date && string

DateTime.parse(string).to_fs(:short) == date.to_fs(:short)
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
dacook marked this conversation as resolved.
Show resolved Hide resolved
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 @@
.flex-column-gap-1
%h6
= t('.title')
%div{ style: 'font-size: 1rem;' }
= t('.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('.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('.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('.proceed')
%button.button.primary{ type: "button", 'data-action': 'click->modal#close' }
= t('.cancel')
29 changes: 18 additions & 11 deletions app/views/admin/order_cycles/edit.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,25 @@

- ng_controller = @order_cycle.simple? ? 'AdminSimpleEditOrderCycleCtrl' : 'AdminEditOrderCycleCtrl'
= 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'} do |f|
%div{ data: { controller: 'order-cycle-form' } }
= 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" }
%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}')" }

%save-bar{ dirty: "order_cycle_form.$dirty", persist: "true" }
%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" }
= render 'simple_form', f: f
- 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}')" }
= render 'form', f: f

- if @order_cycle.simple?
= render 'simple_form', f: f
- else
= render 'form', f: f
%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'
3 changes: 3 additions & 0 deletions app/webpacker/controllers/mixins/useOpenAndCloseAsAModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export const useOpenAndCloseAsAModal = (controller) => {
}.bind(controller),

close: function (_event, remove = false) {
// Only execute close if there is an open modal
if (!document.querySelector("body").classList.contains('modal-open')) return;

this.modalTarget.classList.remove("in");
this.backgroundTarget.classList.remove("in");
document.querySelector("body").classList.remove("modal-open");
Expand Down
44 changes: 44 additions & 0 deletions app/webpacker/controllers/order_cycle_form_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Controller } from "stimulus";

export default class extends Controller {
static targets = ['statusMessage']
connect() {
this.observer = new MutationObserver(this.updateCallback);
wandji20 marked this conversation as resolved.
Show resolved Hide resolved
this.observer.observe(
this.statusMessageTarget,
{ attributes: true, attributeOldValue: true, attributeFilter: ['data-type'] }
);
}

// Callback to trigger warning modal
updateCallback(mutationsList) {
const newDataType = document.getElementById('status-message').getAttribute('data-type');
const actionName = document.getElementById('status-message').getAttribute('data-action-name');
if(!actionName) return;

for(let mutation of mutationsList) {
if (mutation.type === 'attributes' && mutation.attributeName === 'data-type') {
// Only trigger warning modal when notice display (notice) is preceeded by progress display (progress)
if(mutation.oldValue === 'progress' && newDataType === 'notice') {
// Hide all confirmation buttons in warning modal
document.querySelectorAll(
'#linked-order-warning-modal .modal-actions button.secondary'
).forEach((node) => {
node.style.display = 'none';
});
// Show the appropriate confirmation button, open warning modal, and return
document.querySelectorAll(
`#linked-order-warning-modal button[data-trigger-action=${actionName}]`
).forEach((node) => {
node.style.display = 'block';
})
document.querySelector('.warning-modal button.modal-target-trigger').click();
}
}
}
}

disconnect() {
this.observer.disconnect();
}
}
4 changes: 4 additions & 0 deletions app/webpacker/css/admin/order_cycles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,7 @@ form.order_cycle {
}
}
}

#linked-order-warning-modal .reveal-modal{
width: 28rem;
}
6 changes: 6 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1506,6 +1506,11 @@ en:
choose_products_from: "Choose Products From:"
re_notify_producers: Re notify producers
notify_producers_tip: This will send an email to each producer with the list of their orders.
date_time_warning_modal_content:
title: 'Orders are linked to this order cycle.'
content: 'If you wish to create a new order cycle, it is recommended to duplicate the order cycle first and then change the dates.'
proceed: 'Proceed anyway'
cancel: 'Cancel'
incoming:
incoming: "Incoming"
supplier: "Supplier"
Expand Down Expand Up @@ -3698,6 +3703,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using
This will set stock level to zero on all products for this
enterprise that are not present in the uploaded file.
order_cycles:
unsaved_changes: "You have unsaved changes"
create_failure: "Failed to create order cycle"
update_success: 'Your order cycle has been updated.'
update_failure: "Failed to update order cycle"
Expand Down
Loading
Loading