mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-21 15:54:22 +01:00
(feat) add a custom banner for trainings
This commit is contained in:
parent
06a93391a2
commit
0778616345
@ -13,7 +13,7 @@ import { User } from '../../models/user';
|
|||||||
import { EditorialBlock } from '../editorial-block/editorial-block';
|
import { EditorialBlock } from '../editorial-block/editorial-block';
|
||||||
import SettingAPI from '../../api/setting';
|
import SettingAPI from '../../api/setting';
|
||||||
import SettingLib from '../../lib/setting';
|
import SettingLib from '../../lib/setting';
|
||||||
import { SettingValue, machineBannerSettings } from '../../models/setting';
|
import { SettingValue, machinesSettings } from '../../models/setting';
|
||||||
|
|
||||||
declare const Application: IApplication;
|
declare const Application: IApplication;
|
||||||
|
|
||||||
@ -46,16 +46,7 @@ export const MachinesList: React.FC<MachinesListProps> = ({ onError, onSuccess,
|
|||||||
|
|
||||||
const [banner, setBanner] = useState<Record<string, SettingValue>>({});
|
const [banner, setBanner] = useState<Record<string, SettingValue>>({});
|
||||||
|
|
||||||
// fetch Banner text and button from API
|
// retrieve the full list of machines and the machines Banner on component mount
|
||||||
const fetchBanner = async () => {
|
|
||||||
SettingAPI.query(machineBannerSettings)
|
|
||||||
.then(settings => {
|
|
||||||
setBanner({ ...SettingLib.bulkMapToObject(settings) });
|
|
||||||
})
|
|
||||||
.catch(onError);
|
|
||||||
};
|
|
||||||
|
|
||||||
// retrieve the full list of machines on component mount
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
MachineAPI.index()
|
MachineAPI.index()
|
||||||
.then(data => setAllMachines(data))
|
.then(data => setAllMachines(data))
|
||||||
@ -63,7 +54,11 @@ export const MachinesList: React.FC<MachinesListProps> = ({ onError, onSuccess,
|
|||||||
MachineCategoryAPI.index()
|
MachineCategoryAPI.index()
|
||||||
.then(data => setMachineCategories(data))
|
.then(data => setMachineCategories(data))
|
||||||
.catch(e => onError(e));
|
.catch(e => onError(e));
|
||||||
fetchBanner();
|
SettingAPI.query(machinesSettings)
|
||||||
|
.then(settings => {
|
||||||
|
setBanner({ ...SettingLib.bulkMapToObject(settings) });
|
||||||
|
})
|
||||||
|
.catch(onError);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// filter the machines shown when the full list was retrieved
|
// filter the machines shown when the full list was retrieved
|
||||||
|
@ -9,7 +9,7 @@ import { FabButton } from '../base/fab-button';
|
|||||||
import { EditorialKeys, EditorialBlockForm } from '../editorial-block/editorial-block-form';
|
import { EditorialKeys, EditorialBlockForm } from '../editorial-block/editorial-block-form';
|
||||||
import SettingAPI from '../../api/setting';
|
import SettingAPI from '../../api/setting';
|
||||||
import SettingLib from '../../lib/setting';
|
import SettingLib from '../../lib/setting';
|
||||||
import { SettingName, SettingValue, machineBannerSettings } from '../../models/setting';
|
import { SettingName, SettingValue, machinesSettings } from '../../models/setting';
|
||||||
|
|
||||||
declare const Application: IApplication;
|
declare const Application: IApplication;
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ export const MachinesSettings: React.FC<MachinesSettingsProps> = ({ onError, onS
|
|||||||
|
|
||||||
/** On component mount, fetch existing Machines Banner Settings from API, and populate form with these values. */
|
/** On component mount, fetch existing Machines Banner Settings from API, and populate form with these values. */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
SettingAPI.query(machineBannerSettings)
|
SettingAPI.query(machinesSettings)
|
||||||
.then(settings => reset(SettingLib.bulkMapToObject(settings)))
|
.then(settings => reset(SettingLib.bulkMapToObject(settings)))
|
||||||
.catch(onError);
|
.catch(onError);
|
||||||
}, []);
|
}, []);
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { IApplication } from '../../models/application';
|
||||||
|
import { react2angular } from 'react2angular';
|
||||||
|
import { Loader } from '../base/loader';
|
||||||
|
import { EditorialBlock } from '../editorial-block/editorial-block';
|
||||||
|
import SettingAPI from '../../api/setting';
|
||||||
|
import SettingLib from '../../lib/setting';
|
||||||
|
import { SettingValue, trainingsSettings } from '../../models/setting';
|
||||||
|
|
||||||
|
declare const Application: IApplication;
|
||||||
|
|
||||||
|
interface TrainingEditorialBlockProps {
|
||||||
|
onError: (message: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component displays to Users the editorial block (banner) associated to trainings.
|
||||||
|
*/
|
||||||
|
export const TrainingEditorialBlock: React.FC<TrainingEditorialBlockProps> = ({ onError }) => {
|
||||||
|
// Store Banner retrieved from API
|
||||||
|
const [banner, setBanner] = useState<Record<string, SettingValue>>({});
|
||||||
|
|
||||||
|
// Retrieve the settings related to the Trainings Banner from the API
|
||||||
|
useEffect(() => {
|
||||||
|
SettingAPI.query(trainingsSettings)
|
||||||
|
.then(settings => {
|
||||||
|
setBanner({ ...SettingLib.bulkMapToObject(settings) });
|
||||||
|
})
|
||||||
|
.catch(onError);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{banner.trainings_banner_active &&
|
||||||
|
<EditorialBlock
|
||||||
|
text={banner.trainings_banner_text}
|
||||||
|
cta={banner.trainings_banner_cta_active && banner.trainings_banner_cta_label}
|
||||||
|
url={banner.trainings_banner_cta_active && banner.trainings_banner_cta_url} />
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const TrainingEditorialBlockWrapper: React.FC<TrainingEditorialBlockProps> = (props) => {
|
||||||
|
return (
|
||||||
|
<Loader>
|
||||||
|
<TrainingEditorialBlock {...props} />
|
||||||
|
</Loader>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Application.Components.component('trainingEditorialBlock', react2angular(TrainingEditorialBlockWrapper, ['onError']));
|
@ -9,8 +9,8 @@ import { useForm, SubmitHandler, useWatch } from 'react-hook-form';
|
|||||||
import { FormSwitch } from '../form/form-switch';
|
import { FormSwitch } from '../form/form-switch';
|
||||||
import { FormInput } from '../form/form-input';
|
import { FormInput } from '../form/form-input';
|
||||||
import { FabButton } from '../base/fab-button';
|
import { FabButton } from '../base/fab-button';
|
||||||
import { EditorialBlockForm } from '../editorial-block/editorial-block-form';
|
import { EditorialKeys, EditorialBlockForm } from '../editorial-block/editorial-block-form';
|
||||||
import { SettingName, SettingValue, trainingSettings } from '../../models/setting';
|
import { SettingName, SettingValue, trainingsSettings } from '../../models/setting';
|
||||||
import SettingAPI from '../../api/setting';
|
import SettingAPI from '../../api/setting';
|
||||||
import SettingLib from '../../lib/setting';
|
import SettingLib from '../../lib/setting';
|
||||||
|
|
||||||
@ -32,8 +32,17 @@ export const TrainingsSettings: React.FC<TrainingsSettingsProps> = ({ onError, o
|
|||||||
const isActiveAuthorizationValidity = useWatch({ control, name: 'trainings_authorization_validity' }) as boolean;
|
const isActiveAuthorizationValidity = useWatch({ control, name: 'trainings_authorization_validity' }) as boolean;
|
||||||
const isActiveInvalidationRule = useWatch({ control, name: 'trainings_invalidation_rule' }) as boolean;
|
const isActiveInvalidationRule = useWatch({ control, name: 'trainings_invalidation_rule' }) as boolean;
|
||||||
|
|
||||||
|
/** Link Trainings Banner Settings to generic keys expected by the Editorial Form */
|
||||||
|
const bannerKeys: Record<EditorialKeys, SettingName> = {
|
||||||
|
active_text_block: 'trainings_banner_active',
|
||||||
|
text_block: 'trainings_banner_text',
|
||||||
|
active_cta: 'trainings_banner_cta_active',
|
||||||
|
cta_label: 'trainings_banner_cta_label',
|
||||||
|
cta_url: 'trainings_banner_cta_url'
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
SettingAPI.query(trainingSettings)
|
SettingAPI.query(trainingsSettings)
|
||||||
.then(settings => {
|
.then(settings => {
|
||||||
const data = SettingLib.bulkMapToObject(settings);
|
const data = SettingLib.bulkMapToObject(settings);
|
||||||
reset(data);
|
reset(data);
|
||||||
@ -63,6 +72,7 @@ export const TrainingsSettings: React.FC<TrainingsSettingsProps> = ({ onError, o
|
|||||||
<EditorialBlockForm register={register}
|
<EditorialBlockForm register={register}
|
||||||
control={control}
|
control={control}
|
||||||
formState={formState}
|
formState={formState}
|
||||||
|
keys={bannerKeys}
|
||||||
info={t('app.admin.trainings_settings.generic_text_block_info')} />
|
info={t('app.admin.trainings_settings.generic_text_block_info')} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -14,6 +14,10 @@ import type { Machine } from '../../models/machine';
|
|||||||
import TrainingAPI from '../../api/training';
|
import TrainingAPI from '../../api/training';
|
||||||
import MachineAPI from '../../api/machine';
|
import MachineAPI from '../../api/machine';
|
||||||
import { EditDestroyButtons } from '../base/edit-destroy-buttons';
|
import { EditDestroyButtons } from '../base/edit-destroy-buttons';
|
||||||
|
import { EditorialBlock } from '../editorial-block/editorial-block';
|
||||||
|
import { SettingValue, trainingsSettings } from '../../models/setting';
|
||||||
|
import SettingAPI from '../../api/setting';
|
||||||
|
import SettingLib from '../../lib/setting';
|
||||||
|
|
||||||
declare const Application: IApplication;
|
declare const Application: IApplication;
|
||||||
|
|
||||||
@ -31,6 +35,7 @@ export const Trainings: React.FC<TrainingsProps> = ({ onError, onSuccess }) => {
|
|||||||
const [trainings, setTrainings] = useState<Array<Training>>([]);
|
const [trainings, setTrainings] = useState<Array<Training>>([]);
|
||||||
const [machines, setMachines] = useState<Array<Machine>>([]);
|
const [machines, setMachines] = useState<Array<Machine>>([]);
|
||||||
const [filter, setFilter] = useState<boolean>(null);
|
const [filter, setFilter] = useState<boolean>(null);
|
||||||
|
const [banner, setBanner] = useState<Record<string, SettingValue>>({});
|
||||||
|
|
||||||
// Styles the React-select component
|
// Styles the React-select component
|
||||||
const customStyles = {
|
const customStyles = {
|
||||||
@ -45,7 +50,13 @@ export const Trainings: React.FC<TrainingsProps> = ({ onError, onSuccess }) => {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// At component mount, fetch Banner and Machines from API
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
SettingAPI.query(trainingsSettings)
|
||||||
|
.then(settings => {
|
||||||
|
setBanner({ ...SettingLib.bulkMapToObject(settings) });
|
||||||
|
})
|
||||||
|
.catch(onError);
|
||||||
MachineAPI.index({ disabled: false })
|
MachineAPI.index({ disabled: false })
|
||||||
.then(setMachines)
|
.then(setMachines)
|
||||||
.catch(onError);
|
.catch(onError);
|
||||||
@ -114,6 +125,12 @@ export const Trainings: React.FC<TrainingsProps> = ({ onError, onSuccess }) => {
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
{banner.trainings_banner_active &&
|
||||||
|
<EditorialBlock
|
||||||
|
text={banner.trainings_banner_text}
|
||||||
|
cta={banner.trainings_banner_cta_active && banner.trainings_banner_cta_label}
|
||||||
|
url={banner.trainings_banner_cta_active && banner.trainings_banner_cta_url} />
|
||||||
|
}
|
||||||
<div className="trainings-content">
|
<div className="trainings-content">
|
||||||
<div className='display'>
|
<div className='display'>
|
||||||
<div className='filter'>
|
<div className='filter'>
|
||||||
|
@ -17,8 +17,8 @@
|
|||||||
/**
|
/**
|
||||||
* Public listing of the trainings
|
* Public listing of the trainings
|
||||||
*/
|
*/
|
||||||
Application.Controllers.controller('TrainingsController', ['$scope', '$state', 'trainingsPromise',
|
Application.Controllers.controller('TrainingsController', ['$scope', '$state', 'trainingsPromise', 'growl',
|
||||||
function ($scope, $state, trainingsPromise) {
|
function ($scope, $state, trainingsPromise, growl) {
|
||||||
// List of trainings
|
// List of trainings
|
||||||
$scope.trainings = trainingsPromise;
|
$scope.trainings = trainingsPromise;
|
||||||
|
|
||||||
@ -31,6 +31,13 @@ Application.Controllers.controller('TrainingsController', ['$scope', '$state', '
|
|||||||
* Callback for the 'show' button
|
* Callback for the 'show' button
|
||||||
*/
|
*/
|
||||||
$scope.showTraining = function (training) { $state.go('app.public.training_show', { id: training.slug }); };
|
$scope.showTraining = function (training) { $state.go('app.public.training_show', { id: training.slug }); };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback triggered by react components
|
||||||
|
*/
|
||||||
|
$scope.onError = function (message) {
|
||||||
|
growl.error(message);
|
||||||
|
};
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -233,7 +233,7 @@ export const storeSettings = [
|
|||||||
'store_hidden'
|
'store_hidden'
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const trainingSettings = [
|
export const trainingsSettings = [
|
||||||
'trainings_auto_cancel',
|
'trainings_auto_cancel',
|
||||||
'trainings_auto_cancel_threshold',
|
'trainings_auto_cancel_threshold',
|
||||||
'trainings_auto_cancel_deadline',
|
'trainings_auto_cancel_deadline',
|
||||||
@ -241,9 +241,15 @@ export const trainingSettings = [
|
|||||||
'trainings_authorization_validity_duration',
|
'trainings_authorization_validity_duration',
|
||||||
'trainings_invalidation_rule',
|
'trainings_invalidation_rule',
|
||||||
'trainings_invalidation_rule_period'
|
'trainings_invalidation_rule_period'
|
||||||
|
'trainings_auto_cancel_deadline',
|
||||||
|
'trainings_banner_active',
|
||||||
|
'trainings_banner_text',
|
||||||
|
'trainings_banner_cta_active',
|
||||||
|
'trainings_banner_cta_label',
|
||||||
|
'trainings_banner_cta_url'
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const machineBannerSettings = [
|
export const machinesSettings = [
|
||||||
'machines_banner_active',
|
'machines_banner_active',
|
||||||
'machines_banner_text',
|
'machines_banner_text',
|
||||||
'machines_banner_cta_active',
|
'machines_banner_cta_active',
|
||||||
@ -277,8 +283,8 @@ export const allSettings = [
|
|||||||
...poymentSettings,
|
...poymentSettings,
|
||||||
...displaySettings,
|
...displaySettings,
|
||||||
...storeSettings,
|
...storeSettings,
|
||||||
...trainingSettings,
|
...trainingsSettings,
|
||||||
...machineBannerSettings
|
...machinesSettings
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type SettingName = typeof allSettings[number];
|
export type SettingName = typeof allSettings[number];
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<section class="m-lg">
|
<section class="m-lg">
|
||||||
<editorial-block></editorial-block>
|
<training-editorial-block on-error="onError"></training-editorial-block>
|
||||||
|
|
||||||
<div class="row" ng-repeat="training in (trainings.length/3 | array)">
|
<div class="row" ng-repeat="training in (trainings.length/3 | array)">
|
||||||
|
|
||||||
|
193
app/helpers/settings_helper.rb
Normal file
193
app/helpers/settings_helper.rb
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Helpers methods listing all the settings used in setting.rb
|
||||||
|
# The following list contains all the settings that can be customized from the Fab-manager's UI.
|
||||||
|
# A few of them that are system settings, that should not be updated manually (uuid, origin...).
|
||||||
|
|
||||||
|
# rubocop:disable Metrics/ModuleLength
|
||||||
|
module SettingsHelper
|
||||||
|
# WARNING: when adding a new key, you may also want to add it in:
|
||||||
|
# - config/locales/en.yml#settings
|
||||||
|
# - app/frontend/src/javascript/models/setting.ts#SettingName
|
||||||
|
# - db/seeds.rb (to set the default value)
|
||||||
|
# - app/policies/setting_policy.rb#public_whitelist (if the setting can be read by anyone)
|
||||||
|
# - test/fixtures/settings.yml (for backend testing)
|
||||||
|
# - test/fixtures/history_values.yml (example value for backend testing)
|
||||||
|
# - test/frontend/__fixtures__/settings.ts (example value for frontend testing)
|
||||||
|
SETTINGS = %w[
|
||||||
|
about_title
|
||||||
|
about_body
|
||||||
|
about_contacts
|
||||||
|
privacy_draft
|
||||||
|
privacy_body
|
||||||
|
privacy_dpo
|
||||||
|
twitter_name
|
||||||
|
home_blogpost
|
||||||
|
machine_explications_alert
|
||||||
|
training_explications_alert
|
||||||
|
training_information_message
|
||||||
|
subscription_explications_alert
|
||||||
|
invoice_logo
|
||||||
|
invoice_reference
|
||||||
|
invoice_code-active
|
||||||
|
invoice_code-value
|
||||||
|
invoice_order-nb
|
||||||
|
invoice_VAT-active
|
||||||
|
invoice_VAT-rate
|
||||||
|
invoice_VAT-rate_Machine
|
||||||
|
invoice_VAT-rate_Training
|
||||||
|
invoice_VAT-rate_Space
|
||||||
|
invoice_VAT-rate_Event
|
||||||
|
invoice_VAT-rate_Subscription
|
||||||
|
invoice_VAT-rate_Product
|
||||||
|
invoice_text
|
||||||
|
invoice_legals
|
||||||
|
booking_window_start
|
||||||
|
booking_window_end
|
||||||
|
booking_move_enable
|
||||||
|
booking_move_delay
|
||||||
|
booking_cancel_enable
|
||||||
|
booking_cancel_delay
|
||||||
|
main_color
|
||||||
|
secondary_color
|
||||||
|
fablab_name
|
||||||
|
name_genre
|
||||||
|
reminder_enable
|
||||||
|
reminder_delay
|
||||||
|
event_explications_alert
|
||||||
|
space_explications_alert
|
||||||
|
visibility_yearly
|
||||||
|
visibility_others
|
||||||
|
reservation_deadline
|
||||||
|
display_name_enable
|
||||||
|
machines_sort_by
|
||||||
|
accounting_sales_journal_code
|
||||||
|
accounting_payment_card_code
|
||||||
|
accounting_payment_card_label
|
||||||
|
accounting_payment_card_journal_code
|
||||||
|
accounting_payment_wallet_code
|
||||||
|
accounting_payment_wallet_label
|
||||||
|
accounting_payment_wallet_journal_code
|
||||||
|
accounting_payment_other_code
|
||||||
|
accounting_payment_other_label
|
||||||
|
accounting_payment_other_journal_code
|
||||||
|
accounting_wallet_code
|
||||||
|
accounting_wallet_label
|
||||||
|
accounting_wallet_journal_code
|
||||||
|
accounting_VAT_code
|
||||||
|
accounting_VAT_label
|
||||||
|
accounting_VAT_journal_code
|
||||||
|
accounting_subscription_code
|
||||||
|
accounting_subscription_label
|
||||||
|
accounting_Machine_code
|
||||||
|
accounting_Machine_label
|
||||||
|
accounting_Training_code
|
||||||
|
accounting_Training_label
|
||||||
|
accounting_Event_code
|
||||||
|
accounting_Event_label
|
||||||
|
accounting_Space_code
|
||||||
|
accounting_Space_label
|
||||||
|
accounting_Product_code
|
||||||
|
accounting_Product_label
|
||||||
|
hub_last_version
|
||||||
|
hub_public_key
|
||||||
|
fab_analytics
|
||||||
|
link_name
|
||||||
|
home_content
|
||||||
|
home_css
|
||||||
|
origin
|
||||||
|
uuid
|
||||||
|
phone_required
|
||||||
|
tracking_id
|
||||||
|
book_overlapping_slots
|
||||||
|
slot_duration
|
||||||
|
events_in_calendar
|
||||||
|
spaces_module
|
||||||
|
plans_module
|
||||||
|
invoicing_module
|
||||||
|
facebook_app_id
|
||||||
|
twitter_analytics
|
||||||
|
recaptcha_site_key
|
||||||
|
recaptcha_secret_key
|
||||||
|
feature_tour_display
|
||||||
|
email_from
|
||||||
|
disqus_shortname
|
||||||
|
allowed_cad_extensions
|
||||||
|
allowed_cad_mime_types
|
||||||
|
openlab_app_id
|
||||||
|
openlab_app_secret
|
||||||
|
openlab_default
|
||||||
|
online_payment_module
|
||||||
|
stripe_public_key
|
||||||
|
stripe_secret_key
|
||||||
|
stripe_currency
|
||||||
|
invoice_prefix
|
||||||
|
confirmation_required
|
||||||
|
wallet_module
|
||||||
|
statistics_module
|
||||||
|
upcoming_events_shown
|
||||||
|
payment_schedule_prefix
|
||||||
|
trainings_module
|
||||||
|
address_required
|
||||||
|
accounting_Error_code
|
||||||
|
accounting_Error_label
|
||||||
|
payment_gateway
|
||||||
|
payzen_username
|
||||||
|
payzen_password
|
||||||
|
payzen_endpoint
|
||||||
|
payzen_public_key
|
||||||
|
payzen_hmac
|
||||||
|
payzen_currency
|
||||||
|
public_agenda_module
|
||||||
|
renew_pack_threshold
|
||||||
|
pack_only_for_subscription
|
||||||
|
overlapping_categories
|
||||||
|
extended_prices_in_same_day
|
||||||
|
public_registrations
|
||||||
|
accounting_Pack_code
|
||||||
|
accounting_Pack_label
|
||||||
|
facebook
|
||||||
|
twitter
|
||||||
|
viadeo
|
||||||
|
linkedin
|
||||||
|
instagram
|
||||||
|
youtube
|
||||||
|
vimeo
|
||||||
|
dailymotion
|
||||||
|
github
|
||||||
|
echosciences
|
||||||
|
pinterest
|
||||||
|
lastfm
|
||||||
|
flickr
|
||||||
|
machines_module
|
||||||
|
user_change_group
|
||||||
|
user_validation_required
|
||||||
|
user_validation_required_list
|
||||||
|
show_username_in_admin_list
|
||||||
|
store_module
|
||||||
|
store_withdrawal_instructions
|
||||||
|
store_hidden
|
||||||
|
advanced_accounting
|
||||||
|
external_id
|
||||||
|
prevent_invoices_zero
|
||||||
|
invoice_VAT-name
|
||||||
|
trainings_auto_cancel
|
||||||
|
trainings_auto_cancel_threshold
|
||||||
|
trainings_auto_cancel_deadline
|
||||||
|
trainings_authorization_validity
|
||||||
|
trainings_authorization_validity_duration
|
||||||
|
trainings_invalidation_rule
|
||||||
|
trainings_invalidation_rule_period
|
||||||
|
machines_banner_active
|
||||||
|
machines_banner_text
|
||||||
|
machines_banner_cta_active
|
||||||
|
machines_banner_cta_label
|
||||||
|
machines_banner_cta_url
|
||||||
|
trainings_banner_active
|
||||||
|
trainings_banner_text
|
||||||
|
trainings_banner_cta_active
|
||||||
|
trainings_banner_cta_label
|
||||||
|
trainings_banner_cta_url
|
||||||
|
].freeze
|
||||||
|
end
|
||||||
|
# rubocop:enable Metrics/ModuleLength
|
@ -6,186 +6,12 @@
|
|||||||
# A full history of the previous values is kept in database with the date and the author of the change
|
# A full history of the previous values is kept in database with the date and the author of the change
|
||||||
# after_update callback is handled by SettingService
|
# after_update callback is handled by SettingService
|
||||||
class Setting < ApplicationRecord
|
class Setting < ApplicationRecord
|
||||||
|
include SettingsHelper
|
||||||
|
|
||||||
has_many :history_values, dependent: :destroy
|
has_many :history_values, dependent: :destroy
|
||||||
# The following list contains all the settings that can be customized from the Fab-manager's UI.
|
|
||||||
# A few of them that are system settings, that should not be updated manually (uuid, origin...).
|
# The full list of settings is declared in SettingsHelper
|
||||||
validates :name, inclusion:
|
validates :name, inclusion: { in: SETTINGS }
|
||||||
{ in: %w[about_title
|
|
||||||
about_body
|
|
||||||
about_contacts
|
|
||||||
privacy_draft
|
|
||||||
privacy_body
|
|
||||||
privacy_dpo
|
|
||||||
twitter_name
|
|
||||||
home_blogpost
|
|
||||||
machine_explications_alert
|
|
||||||
training_explications_alert
|
|
||||||
training_information_message
|
|
||||||
subscription_explications_alert
|
|
||||||
invoice_logo
|
|
||||||
invoice_reference
|
|
||||||
invoice_code-active
|
|
||||||
invoice_code-value
|
|
||||||
invoice_order-nb
|
|
||||||
invoice_VAT-active
|
|
||||||
invoice_VAT-rate
|
|
||||||
invoice_VAT-rate_Machine
|
|
||||||
invoice_VAT-rate_Training
|
|
||||||
invoice_VAT-rate_Space
|
|
||||||
invoice_VAT-rate_Event
|
|
||||||
invoice_VAT-rate_Subscription
|
|
||||||
invoice_VAT-rate_Product
|
|
||||||
invoice_text
|
|
||||||
invoice_legals
|
|
||||||
booking_window_start
|
|
||||||
booking_window_end
|
|
||||||
booking_move_enable
|
|
||||||
booking_move_delay
|
|
||||||
booking_cancel_enable
|
|
||||||
booking_cancel_delay
|
|
||||||
main_color
|
|
||||||
secondary_color
|
|
||||||
fablab_name
|
|
||||||
name_genre
|
|
||||||
reminder_enable
|
|
||||||
reminder_delay
|
|
||||||
event_explications_alert
|
|
||||||
space_explications_alert
|
|
||||||
visibility_yearly
|
|
||||||
visibility_others
|
|
||||||
reservation_deadline
|
|
||||||
display_name_enable
|
|
||||||
machines_sort_by
|
|
||||||
accounting_sales_journal_code
|
|
||||||
accounting_payment_card_code
|
|
||||||
accounting_payment_card_label
|
|
||||||
accounting_payment_card_journal_code
|
|
||||||
accounting_payment_wallet_code
|
|
||||||
accounting_payment_wallet_label
|
|
||||||
accounting_payment_wallet_journal_code
|
|
||||||
accounting_payment_other_code
|
|
||||||
accounting_payment_other_label
|
|
||||||
accounting_payment_other_journal_code
|
|
||||||
accounting_wallet_code
|
|
||||||
accounting_wallet_label
|
|
||||||
accounting_wallet_journal_code
|
|
||||||
accounting_VAT_code
|
|
||||||
accounting_VAT_label
|
|
||||||
accounting_VAT_journal_code
|
|
||||||
accounting_subscription_code
|
|
||||||
accounting_subscription_label
|
|
||||||
accounting_Machine_code
|
|
||||||
accounting_Machine_label
|
|
||||||
accounting_Training_code
|
|
||||||
accounting_Training_label
|
|
||||||
accounting_Event_code
|
|
||||||
accounting_Event_label
|
|
||||||
accounting_Space_code
|
|
||||||
accounting_Space_label
|
|
||||||
accounting_Product_code
|
|
||||||
accounting_Product_label
|
|
||||||
hub_last_version
|
|
||||||
hub_public_key
|
|
||||||
fab_analytics
|
|
||||||
link_name
|
|
||||||
home_content
|
|
||||||
home_css
|
|
||||||
origin
|
|
||||||
uuid
|
|
||||||
phone_required
|
|
||||||
tracking_id
|
|
||||||
book_overlapping_slots
|
|
||||||
slot_duration
|
|
||||||
events_in_calendar
|
|
||||||
spaces_module
|
|
||||||
plans_module
|
|
||||||
invoicing_module
|
|
||||||
facebook_app_id
|
|
||||||
twitter_analytics
|
|
||||||
recaptcha_site_key
|
|
||||||
recaptcha_secret_key
|
|
||||||
feature_tour_display
|
|
||||||
email_from
|
|
||||||
disqus_shortname
|
|
||||||
allowed_cad_extensions
|
|
||||||
allowed_cad_mime_types
|
|
||||||
openlab_app_id
|
|
||||||
openlab_app_secret
|
|
||||||
openlab_default
|
|
||||||
online_payment_module
|
|
||||||
stripe_public_key
|
|
||||||
stripe_secret_key
|
|
||||||
stripe_currency
|
|
||||||
invoice_prefix
|
|
||||||
confirmation_required
|
|
||||||
wallet_module
|
|
||||||
statistics_module
|
|
||||||
upcoming_events_shown
|
|
||||||
payment_schedule_prefix
|
|
||||||
trainings_module
|
|
||||||
address_required
|
|
||||||
accounting_Error_code
|
|
||||||
accounting_Error_label
|
|
||||||
payment_gateway
|
|
||||||
payzen_username
|
|
||||||
payzen_password
|
|
||||||
payzen_endpoint
|
|
||||||
payzen_public_key
|
|
||||||
payzen_hmac
|
|
||||||
payzen_currency
|
|
||||||
public_agenda_module
|
|
||||||
renew_pack_threshold
|
|
||||||
pack_only_for_subscription
|
|
||||||
overlapping_categories
|
|
||||||
extended_prices_in_same_day
|
|
||||||
public_registrations
|
|
||||||
accounting_Pack_code
|
|
||||||
accounting_Pack_label
|
|
||||||
facebook
|
|
||||||
twitter
|
|
||||||
viadeo
|
|
||||||
linkedin
|
|
||||||
instagram
|
|
||||||
youtube
|
|
||||||
vimeo
|
|
||||||
dailymotion
|
|
||||||
github
|
|
||||||
echosciences
|
|
||||||
pinterest
|
|
||||||
lastfm
|
|
||||||
flickr
|
|
||||||
machines_module
|
|
||||||
user_change_group
|
|
||||||
user_validation_required
|
|
||||||
user_validation_required_list
|
|
||||||
show_username_in_admin_list
|
|
||||||
store_module
|
|
||||||
store_withdrawal_instructions
|
|
||||||
store_hidden
|
|
||||||
advanced_accounting
|
|
||||||
external_id
|
|
||||||
prevent_invoices_zero
|
|
||||||
invoice_VAT-name
|
|
||||||
trainings_auto_cancel
|
|
||||||
trainings_auto_cancel_threshold
|
|
||||||
trainings_auto_cancel_deadline
|
|
||||||
trainings_authorization_validity
|
|
||||||
trainings_authorization_validity_duration
|
|
||||||
trainings_invalidation_rule
|
|
||||||
trainings_invalidation_rule_period
|
|
||||||
machines_banner_active
|
|
||||||
machines_banner_text
|
|
||||||
machines_banner_cta_active
|
|
||||||
machines_banner_cta_label
|
|
||||||
machines_banner_cta_url] }
|
|
||||||
# WARNING: when adding a new key, you may also want to add it in:
|
|
||||||
# - config/locales/en.yml#settings
|
|
||||||
# - app/frontend/src/javascript/models/setting.ts#SettingName
|
|
||||||
# - db/seeds/settings.rb (to set the default value)
|
|
||||||
# - app/policies/setting_policy.rb#public_whitelist (if the setting can be read by anyone)
|
|
||||||
# - test/fixtures/settings.yml (for backend testing)
|
|
||||||
# - test/fixtures/history_values.yml (example value for backend testing)
|
|
||||||
# - test/frontend/__fixtures__/settings.ts (example value for frontend testing)
|
|
||||||
|
|
||||||
def value
|
def value
|
||||||
last_value = history_values.order(HistoryValue.arel_table['created_at'].desc).limit(1).first
|
last_value = history_values.order(HistoryValue.arel_table['created_at'].desc).limit(1).first
|
||||||
|
@ -43,7 +43,9 @@ class SettingPolicy < ApplicationPolicy
|
|||||||
pack_only_for_subscription overlapping_categories public_registrations facebook twitter viadeo linkedin instagram
|
pack_only_for_subscription overlapping_categories public_registrations facebook twitter viadeo linkedin instagram
|
||||||
youtube vimeo dailymotion github echosciences pinterest lastfm flickr machines_module user_change_group
|
youtube vimeo dailymotion github echosciences pinterest lastfm flickr machines_module user_change_group
|
||||||
user_validation_required user_validation_required_list store_module store_withdrawal_instructions store_hidden
|
user_validation_required user_validation_required_list store_module store_withdrawal_instructions store_hidden
|
||||||
external_id machines_banner_active machines_banner_text machines_banner_cta_active machines_banner_cta_label machines_banner_cta_url]
|
external_id machines_banner_active machines_banner_text machines_banner_cta_active machines_banner_cta_label
|
||||||
|
machines_banner_cta_url trainings_banner_active trainings_banner_text trainings_banner_cta_active trainings_banner_cta_label
|
||||||
|
trainings_banner_cta_url]
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -777,6 +777,36 @@ export const settings: Array<Setting> = [
|
|||||||
value: 'https://www.sleede.com/',
|
value: 'https://www.sleede.com/',
|
||||||
last_update: '2022-12-23T14:39:12+0100',
|
last_update: '2022-12-23T14:39:12+0100',
|
||||||
localized: 'Url'
|
localized: 'Url'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'trainings_banner_active',
|
||||||
|
value: 'true',
|
||||||
|
last_update: '2022-12-23T14:39:12+0100',
|
||||||
|
localized: 'Custom banner is active'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'trainings_banner_text',
|
||||||
|
value: 'Test for Banner Content in Trainings',
|
||||||
|
last_update: '2022-12-23T14:39:12+0100',
|
||||||
|
localized: 'Text of the custom banner'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'trainings_banner_cta_active',
|
||||||
|
value: 'true',
|
||||||
|
last_update: '2022-12-23T14:39:12+0100',
|
||||||
|
localized: 'Custom banner has a button'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'trainings_banner_cta_label',
|
||||||
|
value: 'Test for Banner Button in Trainings',
|
||||||
|
last_update: '2022-12-23T14:39:12+0100',
|
||||||
|
localized: 'Label'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'trainings_banner_cta_url',
|
||||||
|
value: 'https://www.sleede.com/',
|
||||||
|
last_update: '2022-12-23T14:39:12+0100',
|
||||||
|
localized: 'Url'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
import { render, screen, waitFor } from '@testing-library/react';
|
||||||
|
import { TrainingEditorialBlock } from '../../../../app/frontend/src/javascript/components/trainings/training-editorial-block';
|
||||||
|
|
||||||
|
// Trainings Editorial Block
|
||||||
|
describe('Trainings Editorial Block', () => {
|
||||||
|
test('should render a banner', async () => {
|
||||||
|
const onError = jest.fn();
|
||||||
|
|
||||||
|
render(<TrainingEditorialBlock onError={onError} />);
|
||||||
|
|
||||||
|
await waitFor(() => screen.getByText('Test for Banner Content in Trainings'));
|
||||||
|
await waitFor(() => screen.getByText('Test for Banner Button in Trainings'));
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user