mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-20 14:54:15 +01:00
(feat) add custom banner on events
This commit is contained in:
parent
c194ac9b31
commit
0f6f763814
@ -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, eventsSettings } from '../../models/setting';
|
||||
|
||||
declare const Application: IApplication;
|
||||
|
||||
interface EventsEditorialBlockProps {
|
||||
onError: (message: string) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* This component displays to Users (public view) the editorial block (= banner) associated to events.
|
||||
*/
|
||||
export const EventsEditorialBlock: React.FC<EventsEditorialBlockProps> = ({ onError }) => {
|
||||
// Stores banner retrieved from API
|
||||
const [banner, setBanner] = useState<Record<string, SettingValue>>({});
|
||||
|
||||
// Retrieve the settings related to the Events Banner from the API
|
||||
useEffect(() => {
|
||||
SettingAPI.query(eventsSettings)
|
||||
.then(settings => {
|
||||
setBanner({ ...SettingLib.bulkMapToObject(settings) });
|
||||
})
|
||||
.catch(onError);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
{banner.events_banner_active &&
|
||||
<EditorialBlock
|
||||
text={banner.events_banner_text}
|
||||
cta={banner.events_banner_cta_active && banner.events_banner_cta_label}
|
||||
url={banner.events_banner_cta_active && banner.events_banner_cta_url} />
|
||||
}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const EventsEditorialBlockWrapper: React.FC<EventsEditorialBlockProps> = (props) => {
|
||||
return (
|
||||
<Loader>
|
||||
<EventsEditorialBlock {...props} />
|
||||
</Loader>
|
||||
);
|
||||
};
|
||||
|
||||
Application.Components.component('eventsEditorialBlock', react2angular(EventsEditorialBlockWrapper, ['onError']));
|
@ -0,0 +1,82 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { IApplication } from '../../models/application';
|
||||
import { Loader } from '../base/loader';
|
||||
import { react2angular } from 'react2angular';
|
||||
import { ErrorBoundary } from '../base/error-boundary';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useForm, SubmitHandler } from 'react-hook-form';
|
||||
import { FabButton } from '../base/fab-button';
|
||||
import { EditorialKeys, EditorialBlockForm } from '../editorial-block/editorial-block-form';
|
||||
import SettingAPI from '../../api/setting';
|
||||
import SettingLib from '../../lib/setting';
|
||||
import { SettingName, SettingValue, eventsSettings } from '../../models/setting';
|
||||
|
||||
declare const Application: IApplication;
|
||||
|
||||
interface EventsSettingsProps {
|
||||
onError: (message: string) => void,
|
||||
onSuccess: (message: string) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* Events settings
|
||||
*/
|
||||
export const EventsSettings: React.FC<EventsSettingsProps> = ({ onError, onSuccess }) => {
|
||||
const { t } = useTranslation('admin');
|
||||
const { register, control, formState, handleSubmit, reset } = useForm<Record<SettingName, SettingValue>>();
|
||||
|
||||
/** Link Events Banner Setting Names to generic keys expected by the Editorial Form */
|
||||
const bannerKeys: Record<EditorialKeys, SettingName> = {
|
||||
active_text_block: 'events_banner_active',
|
||||
text_block: 'events_banner_text',
|
||||
active_cta: 'events_banner_cta_active',
|
||||
cta_label: 'events_banner_cta_label',
|
||||
cta_url: 'events_banner_cta_url'
|
||||
};
|
||||
|
||||
/** Callback triggered when the form is submitted: save the settings */
|
||||
const onSubmit: SubmitHandler<Record<SettingName, SettingValue>> = (data) => {
|
||||
SettingAPI.bulkUpdate(SettingLib.objectToBulkMap(data)).then(() => {
|
||||
onSuccess(t('app.admin.events_settings.update_success'));
|
||||
}, reason => {
|
||||
onError(reason);
|
||||
});
|
||||
};
|
||||
|
||||
/** On component mount, fetch existing Events Banner Settings from API, and populate form with these values. */
|
||||
useEffect(() => {
|
||||
SettingAPI.query(eventsSettings)
|
||||
.then(settings => reset(SettingLib.bulkMapToObject(settings)))
|
||||
.catch(onError);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="events-settings">
|
||||
<header>
|
||||
<h2>{t('app.admin.events_settings.title')}</h2>
|
||||
<FabButton onClick={handleSubmit(onSubmit)} className='save-btn is-main'>{t('app.admin.events_settings.save')}</FabButton>
|
||||
</header>
|
||||
<form className="events-settings-content">
|
||||
<div className="settings-section">
|
||||
<EditorialBlockForm register={register}
|
||||
control={control}
|
||||
formState={formState}
|
||||
keys={bannerKeys}
|
||||
info={t('app.admin.events_settings.generic_text_block_info')} />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const EventsSettingsWrapper: React.FC<EventsSettingsProps> = (props) => {
|
||||
return (
|
||||
<Loader>
|
||||
<ErrorBoundary>
|
||||
<EventsSettings {...props} />
|
||||
</ErrorBoundary>
|
||||
</Loader>
|
||||
);
|
||||
};
|
||||
|
||||
Application.Components.component('eventsSettings', react2angular(EventsSettingsWrapper, ['onError', 'onSuccess']));
|
@ -55,6 +55,20 @@ Application.Controllers.controller('AdminEventsController', ['$scope', '$state',
|
||||
function ($scope, $state, dialogs, $uibModal, growl, AuthService, Event, Category, EventTheme, AgeRange, PriceCategory, eventsPromise, categoriesPromise, themesPromise, ageRangesPromise, priceCategoriesPromise, _t, Member, uiTourService, settingsPromise) {
|
||||
/* PUBLIC SCOPE */
|
||||
|
||||
/**
|
||||
* Callback triggered by react components
|
||||
*/
|
||||
$scope.onSuccess = function (message) {
|
||||
growl.success(message);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback triggered by react components
|
||||
*/
|
||||
$scope.onError = function (message) {
|
||||
growl.error(message);
|
||||
};
|
||||
|
||||
// By default, the pagination mode is activated to limit the page size
|
||||
$scope.paginateActive = true;
|
||||
|
||||
|
@ -13,10 +13,17 @@
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
|
||||
Application.Controllers.controller('EventsController', ['$scope', '$state', 'Event', 'categoriesPromise', 'themesPromise', 'ageRangesPromise',
|
||||
function ($scope, $state, Event, categoriesPromise, themesPromise, ageRangesPromise) {
|
||||
Application.Controllers.controller('EventsController', ['$scope', '$state', 'Event', 'categoriesPromise', 'themesPromise', 'ageRangesPromise', 'growl',
|
||||
function ($scope, $state, Event, categoriesPromise, themesPromise, ageRangesPromise, growl) {
|
||||
/* PUBLIC SCOPE */
|
||||
|
||||
/**
|
||||
* Callback triggered by react components
|
||||
*/
|
||||
$scope.onError = function (message) {
|
||||
growl.error(message);
|
||||
};
|
||||
|
||||
// The events displayed on the page
|
||||
$scope.events = [];
|
||||
|
||||
|
@ -257,6 +257,14 @@ export const machinesSettings = [
|
||||
'machines_banner_cta_url'
|
||||
] as const;
|
||||
|
||||
export const eventsSettings = [
|
||||
'events_banner_active',
|
||||
'events_banner_text',
|
||||
'events_banner_cta_active',
|
||||
'events_banner_cta_label',
|
||||
'events_banner_cta_url'
|
||||
] as const;
|
||||
|
||||
export const allSettings = [
|
||||
...homePageSettings,
|
||||
...privacyPolicySettings,
|
||||
@ -284,7 +292,8 @@ export const allSettings = [
|
||||
...displaySettings,
|
||||
...storeSettings,
|
||||
...trainingsSettings,
|
||||
...machinesSettings
|
||||
...machinesSettings,
|
||||
...eventsSettings
|
||||
] as const;
|
||||
|
||||
export type SettingName = typeof allSettings[number];
|
||||
|
@ -50,6 +50,7 @@
|
||||
@import "modules/events/event";
|
||||
@import "modules/events/event-form";
|
||||
@import "modules/events/update-recurrent-modal";
|
||||
@import "modules/events/events-settings.scss";
|
||||
@import "modules/form/abstract-form-item";
|
||||
@import "modules/form/form-input";
|
||||
@import "modules/form/form-multi-file-upload";
|
||||
|
@ -0,0 +1,21 @@
|
||||
.events-settings {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding-bottom: 6rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
& > header {
|
||||
padding-bottom: 0;
|
||||
@include header($sticky: true);
|
||||
gap: 2.4rem;
|
||||
}
|
||||
|
||||
&-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3.2rem;
|
||||
|
||||
.settings-section { @include layout-settings; }
|
||||
.save-btn { align-self: flex-start; }
|
||||
}
|
||||
}
|
@ -28,15 +28,19 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12" ng-if="isAuthorized('admin')">
|
||||
<uib-tabset justified="true" active="tabs.active">
|
||||
<uib-tab heading="{{ 'app.admin.events.events_monitoring' | translate }}" index="0">
|
||||
<uib-tab heading="{{ 'app.admin.events.settings' | translate }}" index="0">
|
||||
<events-settings on-error="onError" on-success="onSuccess"></events-settings>
|
||||
</uib-tab>
|
||||
|
||||
<uib-tab heading="{{ 'app.admin.events.events_monitoring' | translate }}" index="1">
|
||||
<ng-include src="'/admin/events/monitoring.html'"></ng-include>
|
||||
</uib-tab>
|
||||
|
||||
<uib-tab heading="{{ 'app.admin.events.manage_filters' | translate }}" index="1">
|
||||
<uib-tab heading="{{ 'app.admin.events.manage_filters' | translate }}" index="2">
|
||||
<ng-include src="'/admin/events/filters.html'"></ng-include>
|
||||
</uib-tab>
|
||||
|
||||
<uib-tab heading="{{ 'app.admin.events.manage_prices_categories' | translate }}" index="2" class="prices-tab">
|
||||
<uib-tab heading="{{ 'app.admin.events.manage_prices_categories' | translate }}" index="3" class="prices-tab">
|
||||
<ng-include src="'/admin/events/prices.html'"></ng-include>
|
||||
</uib-tab>
|
||||
</uib-tabset>
|
||||
@ -47,5 +51,3 @@
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
|
||||
|
@ -18,7 +18,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<section class="m-lg">
|
||||
<events-editorial-block on-error="onError"></events-editorial-block>
|
||||
|
||||
<div class="row m-b-md">
|
||||
<div class="col-md-3 m-b" ng-show="categories.length > 0">
|
||||
|
@ -188,6 +188,11 @@ module SettingsHelper
|
||||
trainings_banner_cta_active
|
||||
trainings_banner_cta_label
|
||||
trainings_banner_cta_url
|
||||
events_banner_active
|
||||
events_banner_text
|
||||
events_banner_cta_active
|
||||
events_banner_cta_label
|
||||
events_banner_cta_url
|
||||
].freeze
|
||||
end
|
||||
# rubocop:enable Metrics/ModuleLength
|
||||
|
@ -45,7 +45,8 @@ class SettingPolicy < ApplicationPolicy
|
||||
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 trainings_banner_active trainings_banner_text trainings_banner_cta_active trainings_banner_cta_label
|
||||
trainings_banner_cta_url]
|
||||
trainings_banner_cta_url events_banner_active events_banner_text events_banner_cta_active events_banner_cta_label
|
||||
events_banner_cta_url]
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -500,6 +500,7 @@ en:
|
||||
update_success: "The trainings settings were successfully updated"
|
||||
#events tracking and management
|
||||
events:
|
||||
settings: "Settings"
|
||||
events_monitoring: "Events monitoring"
|
||||
manage_filters: "Manage filters"
|
||||
fablab_events: "Fablab events"
|
||||
@ -586,6 +587,16 @@ en:
|
||||
no_reservations_for_now: "No reservation for now."
|
||||
back_to_monitoring: "Back to monitoring"
|
||||
canceled: "Canceled"
|
||||
events_settings:
|
||||
title: "Settings"
|
||||
generic_text_block: "Editorial text block"
|
||||
generic_text_block_info: "Displays an editorial block above the list of events visible to members."
|
||||
generic_text_block_switch: "Display editorial block"
|
||||
cta_switch: "Display a button"
|
||||
cta_label: "Button label"
|
||||
cta_url: "url"
|
||||
save: "Save"
|
||||
update_success: "The events settings were successfully updated"
|
||||
#subscriptions, prices, credits and coupons management
|
||||
pricing:
|
||||
pricing_management: "Pricing management"
|
||||
|
Loading…
x
Reference in New Issue
Block a user