1
0
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:
Sylvain 2022-10-12 16:48:39 +02:00
parent e851188354
commit 11c8b25357
16 changed files with 91 additions and 43 deletions

View File

@ -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

View File

@ -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>

View File

@ -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;
}
}
});

View File

@ -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';
};
}
]);

View File

@ -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);

View File

@ -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;
};

View File

@ -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 = [

View File

@ -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 }
}
})

View File

@ -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 @@
}
}
}
}
}

View File

@ -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>

View File

@ -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

View File

@ -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
##

View File

@ -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 %>";

View File

@ -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"

View File

@ -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"

View File

@ -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)