From 17c70e0c818865a770fba2612112ff1fe901b285 Mon Sep 17 00:00:00 2001 From: vincent Date: Fri, 6 Jan 2023 18:35:57 +0100 Subject: [PATCH] (ui) Trainings settings + style cleanup --- .../javascript/components/form/form-input.tsx | 6 +- .../trainings/trainings-settings.tsx | 164 ++++++++++++++++++ app/frontend/src/stylesheets/application.scss | 4 +- .../stylesheets/modules/store/_utilities.scss | 33 +--- .../modules/store/product-categories.scss | 4 + .../stylesheets/modules/store/products.scss | 4 + .../modules/trainings/trainings-settings.scss | 37 ++++ .../src/stylesheets/variables/layout.scss | 5 + .../templates/admin/trainings/index.html | 5 +- config/locales/app.admin.en.yml | 17 +- 10 files changed, 244 insertions(+), 35 deletions(-) create mode 100644 app/frontend/src/javascript/components/trainings/trainings-settings.tsx create mode 100644 app/frontend/src/stylesheets/modules/trainings/trainings-settings.scss create mode 100644 app/frontend/src/stylesheets/variables/layout.scss diff --git a/app/frontend/src/javascript/components/form/form-input.tsx b/app/frontend/src/javascript/components/form/form-input.tsx index 9507349bb..fe1fd8b1a 100644 --- a/app/frontend/src/javascript/components/form/form-input.tsx +++ b/app/frontend/src/javascript/components/form/form-input.tsx @@ -22,12 +22,13 @@ interface FormInputProps extends FormComponent) => void, nullable?: boolean, ariaLabel?: string, + maxLength?: number } /** * This component is a template for an input component to use within React Hook Form */ -export const FormInput = ({ id, register, label, tooltip, defaultValue, icon, className, rules, disabled, type, addOn, addOnAction, addOnClassName, addOnAriaLabel, placeholder, error, warning, formState, step, onChange, debounce, accept, nullable = false, ariaLabel }: FormInputProps) => { +export const FormInput = ({ id, register, label, tooltip, defaultValue, icon, className, rules, disabled, type, addOn, addOnAction, addOnClassName, addOnAriaLabel, placeholder, error, warning, formState, step, onChange, debounce, accept, nullable = false, ariaLabel, maxLength }: FormInputProps) => { /** * Debounced (ie. temporised) version of the 'on change' callback. */ @@ -70,7 +71,8 @@ export const FormInput = ({ id, re step={step} disabled={typeof disabled === 'function' ? disabled(id) : disabled} placeholder={placeholder} - accept={accept} /> + accept={accept} + maxLength={maxLength} /> {(type === 'file' && placeholder) && {placeholder}} {addOn && addOnAction && } {addOn && !addOnAction && {addOn}} diff --git a/app/frontend/src/javascript/components/trainings/trainings-settings.tsx b/app/frontend/src/javascript/components/trainings/trainings-settings.tsx new file mode 100644 index 000000000..cdc0f7e18 --- /dev/null +++ b/app/frontend/src/javascript/components/trainings/trainings-settings.tsx @@ -0,0 +1,164 @@ +import * as React from 'react'; +import { useState } 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 { FabAlert } from '../base/fab-alert'; +import { useForm, SubmitHandler } from 'react-hook-form'; +import { FormRichText } from '../form/form-rich-text'; +import { FormSwitch } from '../form/form-switch'; +import { FormInput } from '../form/form-input'; +import { FabButton } from '../base/fab-button'; + +declare const Application: IApplication; + +interface TrainingsSettingsProps { + onError: (message: string) => void, + onSuccess: (message: string) => void, +} + +/** + * Trainings settings + */ +export const TrainingsSettings: React.FC = () => { + const { t } = useTranslation('admin'); + const { register, control, formState, handleSubmit } = useForm(); + + // regular expression to validate the input fields + const urlRegex = /^(https?:\/\/)([^.]+)\.(.{2,30})(\/.*)*\/?$/; + + const [isActiveAutoCancellation, setIsActiveAutoCancellation] = useState(false); + const [isActiveTextBlock, setIsActiveTextBlock] = useState(false); + const [isActiveCta, setIsActiveCta] = useState(false); + + /** + * Callback triggered when the auto cancellation switch has changed. + */ + const toggleAutoCancellation = (value: boolean) => { + setIsActiveAutoCancellation(value); + }; + /** + * Callback triggered when the text block switch has changed. + */ + const toggleTextBlockSwitch = (value: boolean) => { + setIsActiveTextBlock(value); + }; + + /** + * Callback triggered when the CTA switch has changed. + */ + const toggleTextBlockCta = (value: boolean) => { + setIsActiveCta(value); + }; + + /** + * Callback triggered when the CTA label has changed. + */ + const handleCtaLabelChange = (event: React.ChangeEvent): void => { + console.log('cta label:', event.target.value); + }; + /** + * Callback triggered when the cta url has changed. + */ + const handleCtaUrlChange = (event: React.ChangeEvent): void => { + console.log('cta url:', event.target.value); + }; + + /** + * Callback triggered when the form is submitted: save the settings + */ + const onSubmit: SubmitHandler = (data) => { + console.log(data); + }; + + return ( +
+
+

{t('app.admin.trainings_settings.title')}

+
+
+
+

{t('app.admin.trainings_settings.automatic_cancellation')}

+ + {t('app.admin.trainings_settings.automatic_cancellation_info')} + + + + + {isActiveAutoCancellation && <> + + + } +
+ +
+

{t('app.admin.trainings_settings.automatic_cancellation')}

+ + {t('app.admin.trainings_settings.generic_text_block_info')} + + + + + + + {isActiveTextBlock && <> + + + {isActiveCta && <> + + + } + } +
+ + {t('app.admin.trainings_settings.save')} +
+
+ ); +}; + +const TrainingsSettingsWrapper: React.FC = (props) => { + return ( + + + + + + ); +}; + +Application.Components.component('trainingsSettings', react2angular(TrainingsSettingsWrapper, ['onError', 'onSuccess'])); diff --git a/app/frontend/src/stylesheets/application.scss b/app/frontend/src/stylesheets/application.scss index 4f509d421..e033ccf22 100644 --- a/app/frontend/src/stylesheets/application.scss +++ b/app/frontend/src/stylesheets/application.scss @@ -1,7 +1,8 @@ @import "variables/animations"; @import "variables/colors"; -@import "variables/typography"; @import "variables/decoration"; +@import "variables/layout"; +@import "variables/typography"; @import "app.functions"; @import "bootstrap_and_overrides"; @@ -139,6 +140,7 @@ @import "modules/supporting-documents/supporting-documents-type-form"; @import "modules/supporting-documents/supporting-documents-types-list"; @import "modules/trainings/training-form"; +@import "modules/trainings/trainings-settings"; @import "modules/user/avatar"; @import "modules/user/avatar-input"; @import "modules/user/change-password"; diff --git a/app/frontend/src/stylesheets/modules/store/_utilities.scss b/app/frontend/src/stylesheets/modules/store/_utilities.scss index 3799f785f..fb72ccd39 100644 --- a/app/frontend/src/stylesheets/modules/store/_utilities.scss +++ b/app/frontend/src/stylesheets/modules/store/_utilities.scss @@ -13,12 +13,6 @@ } } -@mixin grid-col($col-count) { - width: 100%; - display: grid; - grid-template-columns: repeat($col-count, minmax(0, 1fr)); -} - .back-btn { margin: 2.4rem 0; padding: 0.4rem 0.8rem; @@ -28,7 +22,7 @@ border-radius: var(--border-radius-sm); color: var(--gray-soft-lightest); i { margin-right: 0.8rem; } - + &:hover { color: var(--gray-soft-lightest); background-color: var(--gray-hard-lightest); @@ -47,27 +41,6 @@ } } -@mixin header { - padding: 2.4rem 0; - display: flex; - justify-content: space-between; - align-items: center; - .grpBtn { - display: flex; - & > *:not(:first-child) { margin-left: 2.4rem; } - } - h2 { - margin: 0; - @include title-lg; - color: var(--gray-hard-darkest) !important; - } - h3 { - margin: 0; - @include text-lg(600); - color: var(--gray-hard-darkest) !important; - } -} - // Custom scrollbar .u-scrollbar { &::-webkit-scrollbar-track @@ -75,13 +48,13 @@ border-radius: 6px; background-color: #d9d9d9; } - + &::-webkit-scrollbar { width: 12px; background-color: #ffffff; } - + &::-webkit-scrollbar-thumb { border-radius: 6px; diff --git a/app/frontend/src/stylesheets/modules/store/product-categories.scss b/app/frontend/src/stylesheets/modules/store/product-categories.scss index 698e17c8e..25c7462fa 100644 --- a/app/frontend/src/stylesheets/modules/store/product-categories.scss +++ b/app/frontend/src/stylesheets/modules/store/product-categories.scss @@ -8,6 +8,10 @@ header { @include header(); grid-column: 2 / -2; + .grpBtn { + display: flex; + & > *:not(:first-child) { margin-left: 2.4rem; } + } } .fab-alert { grid-column: 2 / -2; diff --git a/app/frontend/src/stylesheets/modules/store/products.scss b/app/frontend/src/stylesheets/modules/store/products.scss index f9a081626..fac50d2aa 100644 --- a/app/frontend/src/stylesheets/modules/store/products.scss +++ b/app/frontend/src/stylesheets/modules/store/products.scss @@ -9,6 +9,10 @@ @include header(); padding-bottom: 0; grid-column: 1 / -1; + .grpBtn { + display: flex; + & > *:not(:first-child) { margin-left: 2.4rem; } + } } } diff --git a/app/frontend/src/stylesheets/modules/trainings/trainings-settings.scss b/app/frontend/src/stylesheets/modules/trainings/trainings-settings.scss new file mode 100644 index 000000000..8ff9aba07 --- /dev/null +++ b/app/frontend/src/stylesheets/modules/trainings/trainings-settings.scss @@ -0,0 +1,37 @@ +.trainings-settings { + max-width: 1600px; + margin: 0 auto; + padding-bottom: 6rem; + @include grid-col(12); + gap: 3.2rem; + align-items: flex-start; + header { + @include header(); + padding-bottom: 0; + grid-column: 2 / -2; + } + + &-content { + grid-column: 2 / -2; + @include grid-col(2); + gap: 2.4rem 3.2rem; + + .setting-section { grid-column: 1 / -1; } + @media (min-width: 1024px) { + .setting-section { grid-column: span 1; } + } + .section-title { @include title-base; } + .save-btn { + grid-column: 1 / -1; + justify-self: flex-start; + background-color: var(--main); + color: var(--gray-soft-lightest); + border: none; + &:hover { + background-color: var(--main); + color: var(--gray-soft-lightest); + opacity: 0.75; + } + } + } +} \ No newline at end of file diff --git a/app/frontend/src/stylesheets/variables/layout.scss b/app/frontend/src/stylesheets/variables/layout.scss new file mode 100644 index 000000000..8685bdc9a --- /dev/null +++ b/app/frontend/src/stylesheets/variables/layout.scss @@ -0,0 +1,5 @@ +@mixin grid-col($col-count) { + width: 100%; + display: grid; + grid-template-columns: repeat($col-count, minmax(0, 1fr)); +} \ No newline at end of file diff --git a/app/frontend/templates/admin/trainings/index.html b/app/frontend/templates/admin/trainings/index.html index a809c83eb..c10b927d1 100644 --- a/app/frontend/templates/admin/trainings/index.html +++ b/app/frontend/templates/admin/trainings/index.html @@ -33,6 +33,9 @@
+ + +