mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-18 07:52:23 +01:00
(feat) per training settings for auto cancel
This commit is contained in:
parent
e2edbb419a
commit
c804c84113
@ -26,6 +26,7 @@ class API::SettingsController < API::ApiController
|
||||
authorize Setting
|
||||
|
||||
@settings = []
|
||||
updated_settings = []
|
||||
may_transaction params[:transactional] do
|
||||
params[:settings].each do |setting|
|
||||
next if !setting[:name] || !setting[:value]
|
||||
@ -34,8 +35,9 @@ class API::SettingsController < API::ApiController
|
||||
if !SettingService.update_allowed?(db_setting)
|
||||
db_setting.errors.add(:-, "#{I18n.t("settings.#{setting[:name]}")}: #{I18n.t('settings.locked_setting')}")
|
||||
elsif db_setting.save
|
||||
unless db_setting.value == setting[:value]
|
||||
db_setting.history_values.create(value: setting[:value], invoicing_profile: current_user.invoicing_profile)
|
||||
if db_setting.value != setting[:value] &&
|
||||
db_setting.history_values.create(value: setting[:value], invoicing_profile: current_user.invoicing_profile)
|
||||
updated_settings.push(db_setting)
|
||||
end
|
||||
end
|
||||
|
||||
@ -43,7 +45,7 @@ class API::SettingsController < API::ApiController
|
||||
may_rollback(params[:transactional]) if db_setting.errors.keys.count.positive?
|
||||
end
|
||||
end
|
||||
SettingService.run_after_update(@settings)
|
||||
SettingService.run_after_update(updated_settings)
|
||||
end
|
||||
|
||||
def show
|
||||
|
@ -77,6 +77,7 @@ class API::TrainingsController < API::ApiController
|
||||
def training_params
|
||||
params.require(:training)
|
||||
.permit(:id, :name, :description, :machine_ids, :plan_ids, :nb_total_places, :public_page, :disabled,
|
||||
:auto_cancel, :auto_cancel_threshold, :auto_cancel_deadline,
|
||||
training_image_attributes: %i[id attachment], machine_ids: [], plan_ids: [],
|
||||
advanced_accounting_attributes: %i[code analytical_section])
|
||||
end
|
||||
|
@ -38,13 +38,13 @@ export const TrainingForm: React.FC<TrainingFormProps> = ({ action, training, on
|
||||
const { t } = useTranslation('admin');
|
||||
|
||||
const [machineModule, setMachineModule] = useState<Setting>(null);
|
||||
const [isActiveCancellation, setIsActiveCancellation] = useState<boolean>(false);
|
||||
const [isActiveAccounting, setIsActiveAccounting] = useState<boolean>(false);
|
||||
const [isActiveAuthorizationValidity, setIsActiveAuthorizationValidity] = useState<boolean>(false);
|
||||
const [isActiveValidationRule, setIsActiveValidationRule] = useState<boolean>(false);
|
||||
|
||||
const { handleSubmit, register, control, setValue, formState } = useForm<Training>({ defaultValues: { ...training } });
|
||||
const output = useWatch<Training>({ control });
|
||||
const isActiveCancellation = useWatch({ control, name: 'auto_cancel' }) as boolean;
|
||||
|
||||
useEffect(() => {
|
||||
SettingAPI.get('machines_module').then(setMachineModule).catch(onError);
|
||||
@ -79,13 +79,6 @@ export const TrainingForm: React.FC<TrainingFormProps> = ({ action, training, on
|
||||
}).catch(error => onError(error));
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback triggered when the auto cancellation switch has changed.
|
||||
*/
|
||||
const toggleCancellationSwitch = (value: boolean) => {
|
||||
setIsActiveCancellation(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback triggered when the authorisation validity switch has changed.
|
||||
*/
|
||||
@ -178,15 +171,15 @@ export const TrainingForm: React.FC<TrainingFormProps> = ({ action, training, on
|
||||
<p className="description">{t('app.admin.training_form.automatic_cancellation_info')}</p>
|
||||
</header>
|
||||
<div className="content">
|
||||
<FormSwitch id="active_cancellation" control={control}
|
||||
onChange={toggleCancellationSwitch} formState={formState}
|
||||
<FormSwitch id="auto_cancel" control={control}
|
||||
formState={formState}
|
||||
defaultValue={isActiveCancellation}
|
||||
label={t('app.admin.training_form.automatic_cancellation_switch')} />
|
||||
{isActiveCancellation && <>
|
||||
<FormInput register={register}
|
||||
type="number"
|
||||
step={1}
|
||||
id="auto_cancellation_threshold"
|
||||
id="auto_cancel_threshold"
|
||||
formState={formState}
|
||||
rules={{ required: isActiveCancellation }}
|
||||
nullable
|
||||
@ -194,7 +187,7 @@ export const TrainingForm: React.FC<TrainingFormProps> = ({ action, training, on
|
||||
<FormInput register={register}
|
||||
type="number"
|
||||
step={1}
|
||||
id="auto_cancellation_deadline"
|
||||
id="auto_cancel_deadline"
|
||||
formState={formState}
|
||||
rules={{ required: isActiveCancellation }}
|
||||
nullable
|
||||
|
@ -143,8 +143,11 @@ export const Trainings: React.FC<TrainingsProps> = ({ onError, onSuccess }) => {
|
||||
|
||||
<div className='cancel'>
|
||||
<span>{t('app.admin.trainings.cancellation')}</span>
|
||||
<p>5 {t('app.admin.trainings.cancellation_minimum')}<span>|</span>48 {t('app.admin.trainings.cancellation_deadline')}
|
||||
</p>
|
||||
{(training.auto_cancel && <p>
|
||||
{t('app.admin.trainings.cancellation_minimum', { ATTENDEES: training.auto_cancel_threshold })}
|
||||
<span>|</span>
|
||||
{t('app.admin.trainings.cancellation_deadline', { DEADLINE: training.auto_cancel_deadline })}
|
||||
</p>) || <p>---</p>}
|
||||
</div>
|
||||
|
||||
<div className='capacity'>
|
||||
|
@ -14,6 +14,9 @@ export interface Training {
|
||||
disabled?: boolean,
|
||||
plan_ids?: number[],
|
||||
training_image_attributes?: FileType,
|
||||
auto_cancel: boolean,
|
||||
auto_cancel_threshold: number,
|
||||
auto_cancel_deadline: number,
|
||||
availabilities?: Array<{
|
||||
id: number,
|
||||
start_at: TDateISO,
|
||||
|
@ -203,6 +203,11 @@ class Setting < ApplicationRecord
|
||||
last_value&.created_at
|
||||
end
|
||||
|
||||
def previous_value
|
||||
previous_value = history_values.order(HistoryValue.arel_table['created_at'].desc).limit(2).last
|
||||
previous_value&.value
|
||||
end
|
||||
|
||||
def previous_update
|
||||
previous_value = history_values.order(HistoryValue.arel_table['created_at'].desc).limit(2).last
|
||||
previous_value&.created_at
|
||||
|
@ -5,12 +5,14 @@
|
||||
# so this service provides a wrapper around these operations.
|
||||
class SettingService
|
||||
class << self
|
||||
# @param setting [Setting]
|
||||
def update_allowed?(setting)
|
||||
return false if Rails.application.secrets.locked_settings.include? setting.name
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
# @param settings [Array<Setting>]
|
||||
def run_after_update(settings)
|
||||
update_theme_stylesheet(settings)
|
||||
update_home_stylesheet(settings)
|
||||
@ -20,11 +22,13 @@ class SettingService
|
||||
export_projects_to_openlab(settings)
|
||||
validate_admins(settings)
|
||||
update_accounting_line(settings)
|
||||
update_trainings_auto_cancel(settings)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# rebuild the theme stylesheet
|
||||
# @param settings [Array<Setting>]
|
||||
def update_theme_stylesheet(settings)
|
||||
return unless (%w[main_color secondary_color] & settings.map(&:name)).count.positive?
|
||||
|
||||
@ -32,6 +36,7 @@ class SettingService
|
||||
end
|
||||
|
||||
# rebuild the home page stylesheet
|
||||
# @param settings [Array<Setting>]
|
||||
def update_home_stylesheet(settings)
|
||||
return unless settings.any? { |s| s.name == 'home_css' }
|
||||
|
||||
@ -39,6 +44,7 @@ class SettingService
|
||||
end
|
||||
|
||||
# notify about a change in privacy policy
|
||||
# @param settings [Array<Setting>]
|
||||
def notify_privacy_update(settings)
|
||||
return unless settings.any? { |s| s.name == 'privacy_body' }
|
||||
|
||||
@ -47,6 +53,7 @@ class SettingService
|
||||
end
|
||||
|
||||
# sync all objects on stripe
|
||||
# @param settings [Array<Setting>]
|
||||
def sync_stripe_objects(settings)
|
||||
return unless (%w[stripe_secret_key online_payment_module] & settings.map(&:name)).count.positive?
|
||||
|
||||
@ -55,6 +62,7 @@ class SettingService
|
||||
end
|
||||
|
||||
# generate the statistics since the last update
|
||||
# @param settings [Array<Setting>]
|
||||
def build_stats(settings)
|
||||
return unless settings.any? { |s| s.name == 'statistics_module' && s.value == 'true' }
|
||||
|
||||
@ -63,6 +71,7 @@ class SettingService
|
||||
end
|
||||
|
||||
# export projects to openlab
|
||||
# @param settings [Array<Setting>]
|
||||
def export_projects_to_openlab(settings)
|
||||
return unless (%w[openlab_app_id openlab_app_secret] & settings.map(&:name)).count.positive? &&
|
||||
Setting.get('openlab_app_id').present? && Setting.get('openlab_app_secret').present?
|
||||
@ -71,16 +80,33 @@ class SettingService
|
||||
end
|
||||
|
||||
# automatically validate the admins
|
||||
# @param settings [Array<Setting>]
|
||||
def validate_admins(settings)
|
||||
return unless settings.any? { |s| s.name == 'user_validation_required' && s.value == 'true' }
|
||||
|
||||
User.admins.each { |admin| admin.update(validated_at: Time.current) if admin.validated_at.nil? }
|
||||
end
|
||||
|
||||
# rebuild accounting lines
|
||||
# @param settings [Array<Setting>]
|
||||
def update_accounting_line(settings)
|
||||
return unless settings.any? { |s| s.name.match(/^accounting_/) || s.name == 'advanced_accounting' }
|
||||
|
||||
AccountingWorker.perform_async(:all)
|
||||
end
|
||||
|
||||
# update tranings auto_cancel parameters
|
||||
# @param settings [Array<Setting>]
|
||||
def update_trainings_auto_cancel(settings)
|
||||
return unless settings.any? { |s| s.name.match(/^trainings_auto_cancel/) }
|
||||
|
||||
tac = settings.find { |s| s.name == 'trainings_auto_cancel' }
|
||||
threshold = settings.find { |s| s.name == 'trainings_auto_cancel_threshold' }
|
||||
deadline = settings.find { |s| s.name == 'trainings_auto_cancel_deadline' }
|
||||
|
||||
Training.find_each do |t|
|
||||
TrainingService.update_auto_cancel(t, tac, threshold, deadline)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -34,6 +34,29 @@ class TrainingService
|
||||
end
|
||||
end
|
||||
|
||||
# update the given training, depending on the provided settings
|
||||
# @param training [Training]
|
||||
# @param auto_cancel [Setting,NilClass]
|
||||
# @param threshold [Setting,NilClass]
|
||||
# @param deadline [Setting,NilClass]
|
||||
def update_auto_cancel(training, auto_cancel, threshold, deadline)
|
||||
previous_auto_cancel = auto_cancel.nil? ? Setting.find_by(name: 'trainings_auto_cancel').value : auto_cancel.previous_value
|
||||
previous_threshold = threshold.nil? ? Setting.find_by(name: 'trainings_auto_cancel_threshold').value : threshold.previous_value
|
||||
previous_deadline = deadline.nil? ? Setting.find_by(name: 'trainings_auto_cancel_deadline').value : deadline.previous_value
|
||||
is_default = training.auto_cancel.to_s == previous_auto_cancel &&
|
||||
[nil, previous_threshold].include?(training.auto_cancel_threshold.to_s) &&
|
||||
[nil, previous_deadline].include?(training.auto_cancel_deadline.to_s)
|
||||
|
||||
return unless is_default
|
||||
|
||||
# update parameters if the given training is default
|
||||
params = {}
|
||||
params[:auto_cancel] = auto_cancel.value unless auto_cancel.nil?
|
||||
params[:auto_cancel_threshold] = threshold.value unless threshold.nil?
|
||||
params[:auto_cancel_deadline] = deadline.value unless deadline.nil?
|
||||
training.update(params)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# @param trainings [ActiveRecord::Relation<Training>]
|
||||
|
@ -1,6 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
json.extract! training, :id, :name, :description, :machine_ids, :nb_total_places, :public_page, :disabled, :slug
|
||||
json.extract! training, :id, :name, :description, :machine_ids, :nb_total_places, :public_page, :disabled, :slug,
|
||||
:auto_cancel, :auto_cancel_threshold, :auto_cancel_deadline
|
||||
if training.training_image
|
||||
json.training_image_attributes do
|
||||
json.id training.training_image.id
|
||||
|
@ -437,8 +437,8 @@ en:
|
||||
name: "Training name"
|
||||
associated_machines: "Associated machines"
|
||||
cancellation: "Cancellation (attendees | deadline)"
|
||||
cancellation_minimum: "minimum"
|
||||
cancellation_deadline: "h"
|
||||
cancellation_minimum: "{ATTENDEES} minimum"
|
||||
cancellation_deadline: "{DEADLINE} h"
|
||||
capacity: "Capacity (max. attendees)"
|
||||
authorisation: "Time-limited authorisation"
|
||||
period_MONTH: "{MONTH} {MONTH, plural, one{month} other{months}}"
|
||||
|
Loading…
x
Reference in New Issue
Block a user