1
0
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:
Karen 2023-01-26 11:11:38 +01:00 committed by Sylvain
parent c194ac9b31
commit 0f6f763814
12 changed files with 216 additions and 9 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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