mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-18 07:52:23 +01:00
(feat) hide the store to members/visitors
This commit is contained in:
parent
e851188354
commit
11c8b25357
@ -26,13 +26,13 @@ class API::SettingsController < API::ApiController
|
||||
authorize Setting
|
||||
|
||||
@settings = []
|
||||
may_transaction(params[:transactional]) do
|
||||
may_transaction params[:transactional] do
|
||||
params[:settings].each do |setting|
|
||||
next if !setting[:name] || !setting[:value]
|
||||
|
||||
db_setting = Setting.find_or_initialize_by(name: setting[:name])
|
||||
if !SettingService.before_update(db_setting)
|
||||
db_setting.errors[:-] << I18n.t("settings.#{setting[:name]}") + ': ' + I18n.t('settings.locked_setting')
|
||||
db_setting.errors.add(:-, "#{I18n.t("settings.#{setting[:name]}")}: #{I18n.t('settings.locked_setting')}")
|
||||
elsif db_setting.save
|
||||
db_setting.history_values.create(value: setting[:value], invoicing_profile: current_user.invoicing_profile)
|
||||
SettingService.after_update(db_setting)
|
||||
@ -66,7 +66,7 @@ class API::SettingsController < API::ApiController
|
||||
first_val = setting.history_values.order(created_at: :asc).limit(1).first
|
||||
new_val = HistoryValue.create!(
|
||||
setting_id: setting.id,
|
||||
value: first_val.value,
|
||||
value: first_val&.value,
|
||||
invoicing_profile_id: current_user.invoicing_profile.id
|
||||
)
|
||||
SettingService.after_update(setting)
|
||||
@ -84,11 +84,9 @@ class API::SettingsController < API::ApiController
|
||||
end
|
||||
|
||||
# run the given block in a transaction if `should` is true. Just run it normally otherwise
|
||||
def may_transaction(should)
|
||||
def may_transaction(should, &block)
|
||||
if should == 'true'
|
||||
ActiveRecord::Base.transaction do
|
||||
yield
|
||||
end
|
||||
ActiveRecord::Base.transaction(&block)
|
||||
else
|
||||
yield
|
||||
end
|
||||
|
@ -11,6 +11,7 @@ import { FabButton } from '../base/fab-button';
|
||||
import SettingAPI from '../../api/setting';
|
||||
import SettingLib from '../../lib/setting';
|
||||
import { SettingName, SettingValue, storeSettings } from '../../models/setting';
|
||||
import { FormSwitch } from '../form/form-switch';
|
||||
|
||||
declare const Application: IApplication;
|
||||
|
||||
@ -29,7 +30,7 @@ export const StoreSettings: React.FC<StoreSettingsProps> = ({ onError, onSuccess
|
||||
useEffect(() => {
|
||||
SettingAPI.query(storeSettings)
|
||||
.then(settings => {
|
||||
const data = SettingLib.mapToBulkObject(settings);
|
||||
const data = SettingLib.bulkMapToObject(settings);
|
||||
reset(data);
|
||||
})
|
||||
.catch(onError);
|
||||
@ -39,7 +40,7 @@ export const StoreSettings: React.FC<StoreSettingsProps> = ({ onError, onSuccess
|
||||
* Callback triggered when the form is submitted: save the settings
|
||||
*/
|
||||
const onSubmit: SubmitHandler<Record<SettingName, SettingValue>> = (data) => {
|
||||
SettingAPI.bulkUpdate(SettingLib.bulkObjectToMap(data)).then(() => {
|
||||
SettingAPI.bulkUpdate(SettingLib.objectToBulkMap(data)).then(() => {
|
||||
onSuccess(t('app.admin.store_settings.update_success'));
|
||||
}, reason => {
|
||||
onError(reason);
|
||||
@ -52,16 +53,25 @@ export const StoreSettings: React.FC<StoreSettingsProps> = ({ onError, onSuccess
|
||||
<h2>{t('app.admin.store_settings.title')}</h2>
|
||||
</header>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<p>{t('app.admin.store_settings.withdrawal_instructions')}</p>
|
||||
<FabAlert level="warning">
|
||||
<HtmlTranslate trKey="app.admin.store_settings.withdrawal_info" />
|
||||
</FabAlert>
|
||||
<FormRichText control={control}
|
||||
heading
|
||||
bulletList
|
||||
link
|
||||
limit={400}
|
||||
id="store_withdrawal_instructions" />
|
||||
<div className="setting-section">
|
||||
<p className="section-title">{t('app.admin.store_settings.withdrawal_instructions')}</p>
|
||||
<FabAlert level="warning">
|
||||
<HtmlTranslate trKey="app.admin.store_settings.withdrawal_info" />
|
||||
</FabAlert>
|
||||
<FormRichText control={control}
|
||||
heading
|
||||
bulletList
|
||||
link
|
||||
limit={400}
|
||||
id="store_withdrawal_instructions" />
|
||||
</div>
|
||||
<div className="setting-section">
|
||||
<p className="section-title">{t('app.admin.store_settings.store_hidden_title')}</p>
|
||||
<FabAlert level="warning">
|
||||
<HtmlTranslate trKey="app.admin.store_settings.store_hidden_info" />
|
||||
</FabAlert>
|
||||
<FormSwitch control={control} id="store_hidden" label={t('app.admin.store_settings.store_hidden')} />
|
||||
</div>
|
||||
<FabButton type='submit' className='save-btn'>{t('app.admin.store_settings.save')}</FabButton>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -357,6 +357,7 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco
|
||||
} else {
|
||||
// user is not logged in
|
||||
openLoginModal(trans.$to().name, trans.$to().params);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -14,6 +14,20 @@
|
||||
* Navigation controller. List the links availables in the left navigation pane and their icon.
|
||||
*/
|
||||
Application.Controllers.controller('MainNavController', ['$scope', 'settingsPromise', function ($scope, settingsPromise) {
|
||||
/**
|
||||
* Returns the current state of the public registration setting (allowed/blocked).
|
||||
*/
|
||||
$scope.registrationEnabled = function () {
|
||||
return settingsPromise.public_registrations === 'true';
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the store should be hidden to members/visitors
|
||||
*/
|
||||
$scope.storeHidden = function () {
|
||||
return settingsPromise.store_hidden === 'true';
|
||||
};
|
||||
|
||||
// Common links (public application)
|
||||
$scope.navLinks = [
|
||||
{
|
||||
@ -57,7 +71,8 @@ Application.Controllers.controller('MainNavController', ['$scope', 'settingsProm
|
||||
state: 'app.public.store',
|
||||
linkText: 'app.public.common.fablab_store',
|
||||
linkIcon: 'cart-plus',
|
||||
class: 'store-link'
|
||||
class: 'store-link',
|
||||
authorizedRoles: $scope.storeHidden() ? ['admin', 'manager'] : undefined
|
||||
},
|
||||
{ class: 'menu-spacer' },
|
||||
{
|
||||
@ -160,12 +175,5 @@ Application.Controllers.controller('MainNavController', ['$scope', 'settingsProm
|
||||
authorizedRoles: ['admin']
|
||||
}
|
||||
].filter(Boolean).concat(Fablab.adminNavLinks);
|
||||
|
||||
/**
|
||||
* Returns the current state of the public registration setting (allowed/blocked).
|
||||
*/
|
||||
$scope.registrationEnabled = function () {
|
||||
return settingsPromise.public_registrations === 'true';
|
||||
};
|
||||
}
|
||||
]);
|
||||
|
@ -14,7 +14,19 @@ export default class ParsingLib {
|
||||
for (const item of value) {
|
||||
parsedValue.push(ParsingLib.parse(item));
|
||||
}
|
||||
} else if (ParsingLib.isBoolean(value)) {
|
||||
} else {
|
||||
parsedValue = ParsingLib.simpleParse(value);
|
||||
}
|
||||
return parsedValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Try to parse the given value to get the value with the matching type.
|
||||
* Arrays are not supported.
|
||||
*/
|
||||
static simpleParse = (value: string): baseType => {
|
||||
let parsedValue: baseType = value;
|
||||
if (ParsingLib.isBoolean(value)) {
|
||||
parsedValue = (value === 'true');
|
||||
} else if (ParsingLib.isInteger(value)) {
|
||||
parsedValue = parseInt(value, 10);
|
||||
|
@ -1,24 +1,25 @@
|
||||
import { SettingName, SettingValue } from '../models/setting';
|
||||
import ParsingLib from './parsing';
|
||||
|
||||
export default class SettingLib {
|
||||
/**
|
||||
* Convert the provided data to a map, as expected by BulkUpdate
|
||||
*/
|
||||
static bulkObjectToMap = (data: Record<SettingName, SettingValue>): Map<SettingName, SettingValue> => {
|
||||
const res = new Map<SettingName, SettingValue>();
|
||||
static objectToBulkMap = (data: Record<SettingName, SettingValue>): Map<SettingName, string> => {
|
||||
const res = new Map<SettingName, string>();
|
||||
for (const key in data) {
|
||||
res.set(key as SettingName, data[key]);
|
||||
res.set(key as SettingName, `${data[key]}`);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the provided map to a simple javascript object
|
||||
* Convert the provided map to a simple javascript object, usable by react-hook-form
|
||||
*/
|
||||
static mapToBulkObject = (data: Map<SettingName, SettingValue>): Record<SettingName, SettingValue> => {
|
||||
static bulkMapToObject = (data: Map<SettingName, string>): Record<SettingName, SettingValue> => {
|
||||
const res = {} as Record<SettingName, SettingValue>;
|
||||
data.forEach((value, key) => {
|
||||
res[key] = value;
|
||||
res[key] = ParsingLib.simpleParse(value);
|
||||
});
|
||||
return res;
|
||||
};
|
||||
|
@ -214,7 +214,8 @@ export const displaySettings = [
|
||||
] as const;
|
||||
|
||||
export const storeSettings = [
|
||||
'store_withdrawal_instructions'
|
||||
'store_withdrawal_instructions',
|
||||
'store_hidden'
|
||||
] as const;
|
||||
|
||||
export const allSettings = [
|
||||
|
@ -28,7 +28,7 @@ angular.module('application.router', ['ui.router'])
|
||||
logoBlackFile: ['CustomAsset', function (CustomAsset) { return CustomAsset.get({ name: 'logo-black-file' }).$promise; }],
|
||||
sharedTranslations: ['Translations', function (Translations) { return Translations.query(['app.shared', 'app.public.common']).$promise; }],
|
||||
modulesPromise: ['Setting', function (Setting) { return Setting.query({ names: "['machines_module', 'spaces_module', 'plans_module', 'invoicing_module', 'wallet_module', 'statistics_module', 'trainings_module', 'public_agenda_module', 'store_module']" }).$promise; }],
|
||||
settingsPromise: ['Setting', function (Setting) { return Setting.query({ names: "['public_registrations']" }).$promise; }]
|
||||
settingsPromise: ['Setting', function (Setting) { return Setting.query({ names: "['public_registrations', 'store_hidden']" }).$promise; }]
|
||||
},
|
||||
onEnter: ['$rootScope', 'logoFile', 'logoBlackFile', 'modulesPromise', 'CSRF', function ($rootScope, logoFile, logoBlackFile, modulesPromise, CSRF) {
|
||||
// Retrieve Anti-CSRF tokens from cookies
|
||||
@ -634,6 +634,9 @@ angular.module('application.router', ['ui.router'])
|
||||
controller: 'StoreController'
|
||||
}
|
||||
},
|
||||
data: {
|
||||
authorizedRoles: Fablab.storeHidden ? ['admin', 'manager'] : undefined
|
||||
},
|
||||
params: {
|
||||
categoryTypeUrl: { dynamic: true, raw: true, type: 'path', value: null, squash: true },
|
||||
category: { dynamic: true, type: 'path', raw: true, value: null, squash: true },
|
||||
@ -642,7 +645,8 @@ angular.module('application.router', ['ui.router'])
|
||||
is_active: { dynamic: true, type: 'query', value: 'true', squash: true },
|
||||
is_available: { dynamic: true, type: 'query', value: 'false', squash: true },
|
||||
page: { dynamic: true, type: 'query', value: '1', squash: true },
|
||||
sort: { dynamic: true, type: 'query' }
|
||||
sort: { dynamic: true, type: 'query' },
|
||||
authorizedRoles: { dynamic: true, raw: true }
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -11,8 +11,13 @@
|
||||
grid-column: 2 / -2;
|
||||
}
|
||||
form {
|
||||
grid-column: 2 / 7;
|
||||
p { @include title-base; }
|
||||
grid-column: 1 / -1;
|
||||
display: flex;
|
||||
.setting-section {
|
||||
margin: 1rem 2rem;
|
||||
width: 50%;
|
||||
}
|
||||
.section-title { @include title-base; }
|
||||
.save-btn {
|
||||
background-color: var(--main);
|
||||
color: var(--gray-soft-lightest);
|
||||
@ -24,4 +29,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +67,7 @@
|
||||
<i class="fa fa-question-circle-o"></i> <span>{{ linkName }}</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="{{navLink.class}}" ng-repeat="navLink in navLinks">
|
||||
<li class="{{navLink.class}}" ng-repeat="navLink in navLinks" ng-if="!navLink.authorizedRoles || isAuthorized(navLink.authorizedRoles)">
|
||||
<a ng-click="toggleNavSize($event)" ui-sref="{{navLink.state}}" ui-sref-active="active" class="auto" data-toggle="class:nav-off-screen" data-target="#nav" ng-if="navLink.state">
|
||||
<i class="fa fa-{{navLink.linkIcon}} fa-lg"></i>
|
||||
<span>{{navLink.linkText | translate}}</span>
|
||||
|
@ -154,7 +154,8 @@ class Setting < ApplicationRecord
|
||||
user_validation_required_list
|
||||
show_username_in_admin_list
|
||||
store_module
|
||||
store_withdrawal_instructions] }
|
||||
store_withdrawal_instructions
|
||||
store_hidden] }
|
||||
# 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
|
||||
|
@ -42,7 +42,7 @@ class SettingPolicy < ApplicationPolicy
|
||||
payment_gateway payzen_endpoint payzen_public_key public_agenda_module renew_pack_threshold statistics_module
|
||||
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
|
||||
user_validation_required user_validation_required_list store_module store_withdrawal_instructions]
|
||||
user_validation_required user_validation_required_list store_module store_withdrawal_instructions store_hidden]
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -44,6 +44,7 @@
|
||||
Fablab.trackingId = "<%= Setting.get('tracking_id') %>";
|
||||
Fablab.adminSysId = parseInt("<%= User.adminsys&.id %>", 10);
|
||||
Fablab.activeProviderType = "<%= AuthProvider.active&.providable_type %>";
|
||||
Fablab.storeHidden = ('<%= Setting.get('store_hidden') %>' === 'true');
|
||||
|
||||
// i18n stuff
|
||||
Fablab.locale = "<%= Rails.application.secrets.app_locale %>";
|
||||
|
@ -2106,5 +2106,8 @@ en:
|
||||
title: 'Settings'
|
||||
withdrawal_instructions: 'Product withdrawal instructions'
|
||||
withdrawal_info: "This text is displayed on the checkout page to inform the client about the products withdrawal method"
|
||||
store_hidden_title: "Store publicly available"
|
||||
store_hidden_info: "You can hide the store to the eyes of the members and the visitors."
|
||||
store_hidden: "Hide the store"
|
||||
save: "Save"
|
||||
update_success: "The settings were successfully updated"
|
||||
|
@ -620,3 +620,4 @@ en:
|
||||
show_username_in_admin_list: "Show the username in the admin's members list"
|
||||
store_module: "Store module"
|
||||
store_withdrawal_instructions: "Withdrawal instructions"
|
||||
store_hidden: "Store hidden to the public"
|
||||
|
@ -991,6 +991,8 @@ Setting.set('show_username_in_admin_list', false) unless Setting.find_by(name: '
|
||||
|
||||
Setting.set('store_module', false) unless Setting.find_by(name: 'store_module').try(:value)
|
||||
|
||||
Setting.set('store_hidden', true) unless Setting.find_by(name: 'store_hidden').try(:value)
|
||||
|
||||
if StatisticCustomAggregation.count.zero?
|
||||
# available reservations hours for machines
|
||||
machine_hours = StatisticType.find_by(key: 'hour', statistic_index_id: 2)
|
||||
|
Loading…
x
Reference in New Issue
Block a user