mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-30 19:52:20 +01:00
(ui) Training form
This commit is contained in:
parent
17c70e0c81
commit
7edec2bd69
@ -26,7 +26,7 @@ export const AdvancedAccountingForm = <TFieldValues extends FieldValues>({ regis
|
||||
return (
|
||||
<div className="advanced-accounting-form">
|
||||
{isEnabled && <div>
|
||||
<h4>{t('app.admin.advanced_accounting_form.title')}</h4>
|
||||
<p className='title'>{t('app.admin.advanced_accounting_form.title')}</p>
|
||||
<FormInput register={register}
|
||||
id="advanced_accounting_attributes.code"
|
||||
label={t('app.admin.advanced_accounting_form.code')} />
|
||||
|
@ -20,6 +20,7 @@ import { SelectOption } from '../../models/select';
|
||||
import SettingAPI from '../../api/setting';
|
||||
import { Setting } from '../../models/setting';
|
||||
import { AdvancedAccountingForm } from '../accounting/advanced-accounting-form';
|
||||
import { FabPanel } from '../base/fab-panel';
|
||||
|
||||
declare const Application: IApplication;
|
||||
|
||||
@ -34,13 +35,19 @@ interface TrainingFormProps {
|
||||
* Form to edit or create trainings
|
||||
*/
|
||||
export const TrainingForm: React.FC<TrainingFormProps> = ({ action, training, onError, onSuccess }) => {
|
||||
const { t } = useTranslation('admin');
|
||||
|
||||
const [machineModule, setMachineModule] = useState<Setting>(null);
|
||||
const [isActiveCancellation, setIsActiveCancellation] = useState<boolean>(false);
|
||||
const [isActiveTextBlock, setIsActiveTextBlock] = useState<boolean>(false);
|
||||
const [isActiveCta, setIsActiveCta] = useState<boolean>(false);
|
||||
const [isActiveAccounting, setIsActiveAccounting] = useState<boolean>(false);
|
||||
const { handleSubmit, register, control, setValue, formState } = useForm<Training>({ defaultValues: { ...training } });
|
||||
const output = useWatch<Training>({ control });
|
||||
const { t } = useTranslation('admin');
|
||||
|
||||
useEffect(() => {
|
||||
SettingAPI.get('machines_module').then(setMachineModule).catch(onError);
|
||||
SettingAPI.get('advanced_accounting').then(res => setIsActiveAccounting(res.value === 'true')).catch(onError);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
@ -71,50 +78,175 @@ export const TrainingForm: React.FC<TrainingFormProps> = ({ action, training, on
|
||||
}).catch(error => onError(error));
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback triggered when the auto cancellation switch has changed.
|
||||
*/
|
||||
const toggleCancellationSwitch = (value: boolean) => {
|
||||
setIsActiveCancellation(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<HTMLInputElement>): void => {
|
||||
console.log('cta label:', event.target.value);
|
||||
};
|
||||
/**
|
||||
* Callback triggered when the cta url has changed.
|
||||
*/
|
||||
const handleCtaUrlChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
console.log('cta url:', event.target.value);
|
||||
};
|
||||
|
||||
// regular expression to validate the input fields
|
||||
const urlRegex = /^(https?:\/\/)([^.]+)\.(.{2,30})(\/.*)*\/?$/;
|
||||
|
||||
return (
|
||||
<form className="training-form" onSubmit={handleSubmit(onSubmit)}>
|
||||
<FormInput register={register} id="name"
|
||||
formState={formState}
|
||||
rules={{ required: true }}
|
||||
label={t('app.admin.training_form.name')} />
|
||||
<FormImageUpload setValue={setValue}
|
||||
register={register}
|
||||
control={control}
|
||||
formState={formState}
|
||||
rules={{ required: true }}
|
||||
id="training_image_attributes"
|
||||
accept="image/*"
|
||||
defaultImage={output.training_image_attributes}
|
||||
label={t('app.admin.training_form.illustration')} />
|
||||
<FormRichText control={control}
|
||||
id="description"
|
||||
rules={{ required: true }}
|
||||
label={t('app.admin.training_form.description')}
|
||||
limit={null}
|
||||
heading bulletList blockquote link video image />
|
||||
{machineModule?.value === 'true' && <FormMultiSelect control={control}
|
||||
id="machine_ids"
|
||||
formState={formState}
|
||||
label={t('app.admin.training_form.associated_machines')}
|
||||
tooltip={t('app.admin.training_form.associated_machines_help')}
|
||||
loadOptions={loadMachines} />}
|
||||
<FormInput register={register}
|
||||
<FabPanel>
|
||||
<p className="title">{t('app.admin.training_form.description')}</p>
|
||||
<FormInput register={register} id="name"
|
||||
formState={formState}
|
||||
rules={{ required: true }}
|
||||
label={t('app.admin.training_form.name')} />
|
||||
<FormImageUpload setValue={setValue}
|
||||
register={register}
|
||||
control={control}
|
||||
formState={formState}
|
||||
rules={{ required: true }}
|
||||
id="training_image_attributes"
|
||||
accept="image/*"
|
||||
defaultImage={output.training_image_attributes}
|
||||
label={t('app.admin.training_form.illustration')} />
|
||||
<FormRichText control={control}
|
||||
id="description"
|
||||
rules={{ required: true }}
|
||||
label={t('app.admin.training_form.description')}
|
||||
limit={null}
|
||||
heading bulletList blockquote link />
|
||||
</FabPanel>
|
||||
|
||||
<FabPanel>
|
||||
<p className="title">{t('app.admin.training_form.settings')}</p>
|
||||
{machineModule?.value === 'true' &&
|
||||
<FormMultiSelect control={control}
|
||||
id="machine_ids"
|
||||
formState={formState}
|
||||
label={t('app.admin.training_form.associated_machines')}
|
||||
tooltip={t('app.admin.training_form.associated_machines_help')}
|
||||
loadOptions={loadMachines} />}
|
||||
<FormInput register={register}
|
||||
type="number"
|
||||
id="nb_total_places"
|
||||
formState={formState}
|
||||
nullable
|
||||
label={t('app.admin.training_form.default_seats')} />
|
||||
<FormSwitch control={control}
|
||||
id="public_page"
|
||||
defaultValue={true}
|
||||
label={t('app.admin.training_form.public_page')}
|
||||
tooltip={t('app.admin.training_form.public_help')} />
|
||||
<FormSwitch control={control}
|
||||
id="disabled"
|
||||
label={t('app.admin.training_form.disable_training')}
|
||||
tooltip={t('app.admin.training_form.disabled_help')} />
|
||||
<AdvancedAccountingForm register={register} onError={onError} />
|
||||
<FabButton type="submit" className="is-info submit-btn">
|
||||
<FormSwitch control={control}
|
||||
id="public_page"
|
||||
defaultValue={true}
|
||||
label={t('app.admin.training_form.public_page')}
|
||||
tooltip={t('app.admin.training_form.public_help')} />
|
||||
<FormSwitch control={control}
|
||||
id="disabled"
|
||||
label={t('app.admin.training_form.disable_training')}
|
||||
tooltip={t('app.admin.training_form.disabled_help')} />
|
||||
</FabPanel>
|
||||
|
||||
<FabPanel>
|
||||
<p className="title">
|
||||
{t('app.admin.training_form.automatic_cancellation')}
|
||||
<div className="fab-tooltip">
|
||||
<span className="trigger"><i className="fa fa-question-circle" /></span>
|
||||
<div className="content">{t('app.admin.training_form.automatic_cancellation_info')}</div>
|
||||
</div>
|
||||
</p>
|
||||
|
||||
<FormSwitch id="active_cancellation" control={control}
|
||||
onChange={toggleCancellationSwitch} formState={formState}
|
||||
defaultValue={isActiveCancellation}
|
||||
label={t('app.admin.training_form.automatic_cancellation_switch')} />
|
||||
{isActiveCancellation && <>
|
||||
<FormInput register={register}
|
||||
type="number"
|
||||
step={1}
|
||||
id="auto_cancellation_threshold"
|
||||
formState={formState}
|
||||
rules={{ required: isActiveCancellation }}
|
||||
nullable
|
||||
label={t('app.admin.training_form.automatic_cancellation_threshold')} />
|
||||
<FormInput register={register}
|
||||
type="number"
|
||||
step={1}
|
||||
id="auto_cancellation_deadline"
|
||||
formState={formState}
|
||||
rules={{ required: isActiveCancellation }}
|
||||
nullable
|
||||
label={t('app.admin.training_form.automatic_cancellation_deadline')} />
|
||||
</>}
|
||||
</FabPanel>
|
||||
|
||||
<FabPanel>
|
||||
<p className="title">
|
||||
{t('app.admin.training_form.generic_text_block')}
|
||||
<div className="fab-tooltip">
|
||||
<span className="trigger"><i className="fa fa-question-circle" /></span>
|
||||
<div className="content">{t('app.admin.training_form.generic_text_block_info')}</div>
|
||||
</div>
|
||||
</p>
|
||||
|
||||
<FormSwitch id="active_text_block" control={control}
|
||||
onChange={toggleTextBlockSwitch} formState={formState}
|
||||
defaultValue={isActiveTextBlock}
|
||||
label={t('app.admin.training_form.generic_text_block_switch')} />
|
||||
|
||||
<FormRichText id="text_block"
|
||||
control={control}
|
||||
heading
|
||||
limit={280}
|
||||
disabled={!isActiveTextBlock} />
|
||||
|
||||
{isActiveTextBlock && <>
|
||||
<FormSwitch id="active_cta" control={control}
|
||||
onChange={toggleTextBlockCta} formState={formState}
|
||||
label={t('app.admin.training_form.cta_switch')} />
|
||||
|
||||
{isActiveCta && <>
|
||||
<FormInput id="cta_label"
|
||||
register={register}
|
||||
rules={{ required: true }}
|
||||
onChange={handleCtaLabelChange}
|
||||
maxLength={40}
|
||||
label={t('app.admin.training_form.cta_label')} />
|
||||
<FormInput id="cta_url"
|
||||
register={register}
|
||||
rules={{ required: true, pattern: urlRegex }}
|
||||
onChange={handleCtaUrlChange}
|
||||
label={t('app.admin.training_form.cta_url')} />
|
||||
</>}
|
||||
</>}
|
||||
</FabPanel>
|
||||
|
||||
{isActiveAccounting &&
|
||||
<FabPanel>
|
||||
<AdvancedAccountingForm register={register} onError={onError} />
|
||||
</FabPanel>
|
||||
}
|
||||
|
||||
<FabButton type="submit" className="fab-button save-btn is-main">
|
||||
{t('app.admin.training_form.ACTION_training', { ACTION: action })}
|
||||
</FabButton>
|
||||
</form>
|
||||
|
@ -79,7 +79,7 @@ export const TrainingsSettings: React.FC<TrainingsSettingsProps> = () => {
|
||||
<h2>{t('app.admin.trainings_settings.title')}</h2>
|
||||
</header>
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="trainings-settings-content">
|
||||
<div className="setting-section">
|
||||
<div className="settings-section">
|
||||
<p className="section-title">{t('app.admin.trainings_settings.automatic_cancellation')}</p>
|
||||
<FabAlert level="warning">
|
||||
{t('app.admin.trainings_settings.automatic_cancellation_info')}
|
||||
@ -108,7 +108,7 @@ export const TrainingsSettings: React.FC<TrainingsSettingsProps> = () => {
|
||||
</>}
|
||||
</div>
|
||||
|
||||
<div className="setting-section">
|
||||
<div className="settings-section">
|
||||
<p className="section-title">{t('app.admin.trainings_settings.automatic_cancellation')}</p>
|
||||
<FabAlert level="warning">
|
||||
{t('app.admin.trainings_settings.generic_text_block_info')}
|
||||
@ -121,6 +121,7 @@ export const TrainingsSettings: React.FC<TrainingsSettingsProps> = () => {
|
||||
|
||||
<FormRichText id="text_block"
|
||||
control={control}
|
||||
heading
|
||||
limit={280}
|
||||
disabled={!isActiveTextBlock} />
|
||||
|
||||
|
129
app/frontend/src/javascript/components/trainings/trainings.tsx
Normal file
129
app/frontend/src/javascript/components/trainings/trainings.tsx
Normal file
@ -0,0 +1,129 @@
|
||||
import * as React 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 { FabButton } from '../base/fab-button';
|
||||
import Select from 'react-select';
|
||||
import { SelectOption } from '../../models/select';
|
||||
import { PencilSimple, Trash } from 'phosphor-react';
|
||||
|
||||
declare const Application: IApplication;
|
||||
|
||||
interface TrainingsProps {
|
||||
onError: (message: string) => void,
|
||||
onSuccess: (message: string) => void,
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin list of trainings
|
||||
*/
|
||||
export const Trainings: React.FC<TrainingsProps> = () => {
|
||||
const { t } = useTranslation('admin');
|
||||
|
||||
/** Goto new training page */
|
||||
const newTraining = (): void => {
|
||||
window.location.href = '/#!/admin/trainings/new';
|
||||
};
|
||||
|
||||
// Styles the React-select component
|
||||
const customStyles = {
|
||||
control: base => ({
|
||||
...base,
|
||||
width: '20ch',
|
||||
border: 'none',
|
||||
backgroundColor: 'transparent'
|
||||
}),
|
||||
indicatorSeparator: () => ({
|
||||
display: 'none'
|
||||
})
|
||||
};
|
||||
|
||||
/** Creates filtering options to the react-select format */
|
||||
const buildFilterOptions = (): Array<SelectOption<any>> => {
|
||||
return [
|
||||
{ value: 'all', label: t('app.admin.trainings.status_all') },
|
||||
{ value: 'enabled', label: t('app.admin.trainings.status_enabled') },
|
||||
{ value: 'disabled', label: t('app.admin.trainings.status_disabled') }
|
||||
];
|
||||
};
|
||||
|
||||
/** Handel filter change */
|
||||
const onFilterChange = (option: SelectOption<any>) => {
|
||||
console.log(option);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='trainings'>
|
||||
<header>
|
||||
<h2>{t('app.admin.trainings.trainings')}</h2>
|
||||
<div className='grpBtn'>
|
||||
<FabButton className="main-action-btn" onClick={newTraining}>{t('app.admin.trainings.add_a_new_training')}</FabButton>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="trainings-content">
|
||||
<div className='display'>
|
||||
<div className='filter'>
|
||||
<p>{t('app.admin.trainings.filter_status')}</p>
|
||||
<Select
|
||||
options={buildFilterOptions()}
|
||||
onChange={evt => onFilterChange(evt)}
|
||||
styles={customStyles} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='trainings-list'>
|
||||
{/* map
|
||||
.is-override si l'item a des paramètres d'annulation auto spécifiques
|
||||
*/}
|
||||
<div className='trainings-list-item'>
|
||||
<div className='name'>
|
||||
<span>{t('app.admin.trainings.name')}</span>
|
||||
<p>All you can learn : super training</p>
|
||||
</div>
|
||||
<div className='machines'>
|
||||
<span>{t('app.admin.trainings.associated_machines')}</span>
|
||||
<p>Découpeuse laser, Découpeuse vinyle, Shopbot / Grande fraiseuse, Petite Fraiseuse, Imprimante 3D</p>
|
||||
</div>
|
||||
<div className='cancel'>
|
||||
<span>{t('app.admin.trainings.cancellation')}</span>
|
||||
<p>5 {t('app.admin.trainings.cancellation_minimum')} <span>|</span> 48 {t('app.admin.trainings.cancellation_deadline')}
|
||||
{/* si l'item a des paramètres d'annulation auto spécifiques */}
|
||||
{false && <span className='override'> ({t('app.admin.trainings.cancellation_override')})</span> }
|
||||
</p>
|
||||
</div>
|
||||
<div className='capacity'>
|
||||
<span>{t('app.admin.trainings.capacity')}</span>
|
||||
<p>10</p>
|
||||
</div>
|
||||
|
||||
<div className='actions'>
|
||||
<div className='grpBtn'>
|
||||
<FabButton className='edit-btn'>
|
||||
<PencilSimple size={20} weight="fill" />
|
||||
</FabButton>
|
||||
<FabButton className='delete-btn'>
|
||||
<Trash size={20} weight="fill" />
|
||||
</FabButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const TrainingsWrapper: React.FC<TrainingsProps> = (props) => {
|
||||
return (
|
||||
<Loader>
|
||||
<ErrorBoundary>
|
||||
<Trainings {...props} />
|
||||
</ErrorBoundary>
|
||||
</Loader>
|
||||
);
|
||||
};
|
||||
|
||||
Application.Components.component('trainings', react2angular(TrainingsWrapper, ['onError', 'onSuccess']));
|
@ -141,6 +141,7 @@
|
||||
@import "modules/supporting-documents/supporting-documents-types-list";
|
||||
@import "modules/trainings/training-form";
|
||||
@import "modules/trainings/trainings-settings";
|
||||
@import "modules/trainings/trainings";
|
||||
@import "modules/user/avatar";
|
||||
@import "modules/user/avatar-input";
|
||||
@import "modules/user/change-password";
|
||||
|
@ -24,3 +24,7 @@
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.advanced-accounting-form {
|
||||
.title { @include text-base(600); }
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
.fab-panel {
|
||||
background-color: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
|
||||
margin: 30px 30px 30px 0;
|
||||
min-height: 1px;
|
||||
@ -14,8 +14,8 @@
|
||||
background-color: #f4f3f3;
|
||||
padding: 18px 15px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
border-top-right-radius: 3px;
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: var(--border-radius);
|
||||
border-top-left-radius: var(--border-radius);
|
||||
|
||||
&.small {
|
||||
padding: 24px;
|
||||
@ -29,6 +29,7 @@
|
||||
&.no-header {
|
||||
padding: 15px;
|
||||
background-color: #f4f3f3;
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,11 +6,12 @@
|
||||
.content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
display: none;
|
||||
width: max-content;
|
||||
max-width: min(75vw, 65ch);
|
||||
max-width: min(75vw, 30ch);
|
||||
padding: 1rem;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: var(--information-lightest);
|
||||
color: var(--information);
|
||||
border: 1px solid var(--information);
|
||||
|
@ -5,10 +5,13 @@
|
||||
&-header {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
align-items: center;
|
||||
margin-bottom: 0.8rem;
|
||||
position: relative;
|
||||
& > *:not(:first-child) {
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
|
||||
p {
|
||||
@include text-sm;
|
||||
margin: 0;
|
||||
|
@ -2,10 +2,6 @@
|
||||
.form-item-header {
|
||||
margin-bottom: 0;
|
||||
|
||||
& > *:not(:first-child) {
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
|
||||
.fab-tooltip .content {
|
||||
max-width: min(75vw, 30ch);
|
||||
}
|
||||
|
@ -1,18 +1,3 @@
|
||||
@mixin btn {
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
background: none;
|
||||
border: none;
|
||||
&:active {
|
||||
color: currentColor;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
margin: 2.4rem 0;
|
||||
padding: 0.4rem 0.8rem;
|
||||
|
@ -1,5 +1,24 @@
|
||||
// hide Angular tag for the grid
|
||||
training-form { display: contents; }
|
||||
|
||||
.training-form {
|
||||
.submit-btn {
|
||||
float: right;
|
||||
grid-column: 2/-2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2.4rem;
|
||||
|
||||
.fab-panel {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
.title {
|
||||
@include text-base(600);
|
||||
margin-bottom: 2.4rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.fab-tooltip { margin-left: 1.6rem; }
|
||||
}
|
||||
}
|
||||
.save-btn {
|
||||
align-self: flex-start;
|
||||
}
|
||||
}
|
||||
|
@ -16,9 +16,9 @@
|
||||
@include grid-col(2);
|
||||
gap: 2.4rem 3.2rem;
|
||||
|
||||
.setting-section { grid-column: 1 / -1; }
|
||||
.settings-section { grid-column: 1 / -1; }
|
||||
@media (min-width: 1024px) {
|
||||
.setting-section { grid-column: span 1; }
|
||||
.settings-section { grid-column: span 1; }
|
||||
}
|
||||
.section-title { @include title-base; }
|
||||
.save-btn {
|
||||
|
127
app/frontend/src/stylesheets/modules/trainings/trainings.scss
Normal file
127
app/frontend/src/stylesheets/modules/trainings/trainings.scss
Normal file
@ -0,0 +1,127 @@
|
||||
.trainings {
|
||||
max-width: 1600px;
|
||||
margin: 0 auto;
|
||||
padding-bottom: 6rem;
|
||||
display: grid;
|
||||
gap: 2.4rem;
|
||||
|
||||
header {
|
||||
@include header();
|
||||
padding-bottom: 1.6rem;
|
||||
.grpBtn {
|
||||
display: flex;
|
||||
& > *:not(:first-child) { margin-left: 2.4rem; }
|
||||
}
|
||||
}
|
||||
|
||||
&-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.6rem;
|
||||
|
||||
.display {
|
||||
padding: 0.8rem 2.4rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
background-color: var(--gray-soft);
|
||||
border-radius: var(--border-radius);
|
||||
|
||||
.filter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
p { margin: 0 0.8rem 0 0; }
|
||||
}
|
||||
}
|
||||
|
||||
.trainings-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.6rem;
|
||||
|
||||
&-item {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
justify-content: space-between;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1.6rem;
|
||||
padding: 1.6rem;
|
||||
border: 1px solid var(--gray-soft-dark);
|
||||
border-radius: var(--border-radius);
|
||||
background-color: var(--gray-soft-lightest);
|
||||
&.is-override {
|
||||
border-color: var(--gray-hard-darkest);
|
||||
}
|
||||
span {
|
||||
@include text-xs;
|
||||
color: var(--gray-hard-light);
|
||||
}
|
||||
p {
|
||||
margin: 0;
|
||||
@include text-base(600);
|
||||
span { @include text-base(400); }
|
||||
}
|
||||
|
||||
.name,
|
||||
.machines { grid-column: 1 / -1; }
|
||||
.machines {
|
||||
overflow: hidden ;
|
||||
p { @extend .text-ellipsis; }
|
||||
}
|
||||
.cancel {
|
||||
.override {
|
||||
@include text-sm;
|
||||
color: var(--alert-light);
|
||||
}
|
||||
}
|
||||
.actions {
|
||||
grid-column-end: -1;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
.grpBtn {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
border-radius: var(--border-radius-sm);
|
||||
button {
|
||||
@include btn;
|
||||
border-radius: 0;
|
||||
color: var(--gray-soft-lightest);
|
||||
&:hover { opacity: 0.75; }
|
||||
}
|
||||
.edit-btn {background: var(--gray-hard-darkest) }
|
||||
.delete-btn {background: var(--main) }
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
grid-template-columns: 2fr 1fr max-content;
|
||||
.name { grid-area: 1/1/2/2; }
|
||||
.machines { grid-area: 2/1/3/2; }
|
||||
.actions { grid-row: 1/3; }
|
||||
}
|
||||
@media (min-width: 1440px) {
|
||||
grid-template-columns: 1fr 40ch repeat(2, 1fr) max-content;
|
||||
align-items: center;
|
||||
.name,
|
||||
.machines,
|
||||
.cancel,
|
||||
.capacity,
|
||||
.actions { grid-area: auto; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.trainings-container {
|
||||
max-width: 1600px;
|
||||
margin: 0 auto;
|
||||
padding: 2.4rem 0 6rem;
|
||||
@include grid-col(12);
|
||||
gap: 2.4rem 3.2rem;
|
||||
.alert {
|
||||
margin: 0;
|
||||
grid-column: 2/-2;
|
||||
}
|
||||
}
|
@ -17,4 +17,20 @@
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
// tmp: move to a dedicated button ss
|
||||
@mixin btn {
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
background: none;
|
||||
border: none;
|
||||
&:active {
|
||||
color: currentColor;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
@ -10,22 +10,9 @@
|
||||
<h1>{{ training.name }}</h1>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md">
|
||||
<section class="heading-actions wrapper">
|
||||
<div class="btn btn-lg btn-block btn-default rounded m-t-xs" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<div class="row no-gutter">
|
||||
<div class="col-sm-12 col-md-12 col-lg-9 b-r-lg nopadding">
|
||||
<div class="panel panel-default bg-light m-lg">
|
||||
<div class="panel-body m-r">
|
||||
<training-form action="'update'" training="training" on-error="onError" on-success="onSuccess"></training-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="trainings-container">
|
||||
<training-form action="'update'" training="training" on-error="onError" on-success="onSuccess"></training-form>
|
||||
</div>
|
||||
|
@ -37,6 +37,8 @@
|
||||
<trainings-settings on-error="onError" on-success="on-success"></trainings-settings>
|
||||
</uib-tab>
|
||||
<uib-tab heading="{{ 'app.admin.trainings.trainings' | translate }}" index="0" class="manage-trainings">
|
||||
<trainings on-error="onError" on-success="on-success"></trainings>
|
||||
|
||||
<div class="m-t m-b">
|
||||
<button type="button" class="btn btn-warning" ui-sref="app.admin.trainings_new" ng-show="isAuthorized('admin')">
|
||||
<i class="fa fa-plus m-r"></i>
|
||||
@ -57,7 +59,7 @@
|
||||
<tr>
|
||||
<th style="width:20%" translate>{{ 'app.admin.trainings.name' }}</th>
|
||||
<th ng-if="enableMachinesModule" style="width:40%" translate>{{ 'app.admin.trainings.associated_machines' }}</th>
|
||||
<th style="width:20%" translate>{{ 'app.admin.trainings.number_of_tickets' }}</th>
|
||||
<th style="width:20%" translate>{{ 'app.admin.trainings.capacity' }}</th>
|
||||
<th style="width:20%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -5,35 +5,18 @@
|
||||
<a ng-click="cancel()"><i class="fas fa-long-arrow-alt-left "></i></a>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-md-8 b-l b-r">
|
||||
<div class="col-md-11 b-l">
|
||||
<section class="heading-title">
|
||||
<h1 translate>{{ 'app.admin.trainings_new.add_a_new_training' }}</h1>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<div class="row no-gutter" >
|
||||
|
||||
<div class="col-md-9 b-r nopadding">
|
||||
|
||||
<div class="alert alert-warning m-lg" role="alert">
|
||||
{{ 'app.admin.trainings_new.beware_when_creating_a_training_its_reservation_prices_are_initialized_to_zero' | translate }}
|
||||
{{ 'app.admin.trainings_new.dont_forget_to_change_them_before_creating_slots_for_this_training' | translate }}
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default bg-light m-lg">
|
||||
<div class="panel-body m-r">
|
||||
<training-form action="'create'" on-error="onError" on-success="onSuccess"></training-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<!-- <button class="btn">TEST</button> -->
|
||||
<div class="trainings-container" >
|
||||
<div class="alert alert-warning" role="alert">
|
||||
{{ 'app.admin.trainings_new.beware_when_creating_a_training_its_reservation_prices_are_initialized_to_zero' | translate }}
|
||||
{{ 'app.admin.trainings_new.dont_forget_to_change_them_before_creating_slots_for_this_training' | translate }}
|
||||
</div>
|
||||
<training-form action="'create'" on-error="onError" on-success="onSuccess"></training-form>
|
||||
</div>
|
||||
|
@ -27,7 +27,7 @@ en:
|
||||
confirm: "Confirm"
|
||||
deleted: "The machine category has been successfully deleted."
|
||||
unable_to_delete: "Unable to delete the machine category: "
|
||||
confirm_delete_supporting_documents_type: "Do you really want to remove this machine category?"
|
||||
confirm_machine_category: "Do you really want to remove this machine category?"
|
||||
machine_form:
|
||||
name: "Name"
|
||||
illustration: "Visual"
|
||||
@ -44,11 +44,13 @@ en:
|
||||
create_success: "The machine was created successfully"
|
||||
update_success: "The machine was updated successfully"
|
||||
training_form:
|
||||
description: "Description"
|
||||
name: "Name"
|
||||
illustration: "Illustration"
|
||||
description: "Description"
|
||||
add_a_new_training: "Add a new training"
|
||||
validate_your_training: "Validate your training"
|
||||
settings: "Settings"
|
||||
associated_machines: "Associated machines"
|
||||
associated_machines_help: "If you associate a machine to this training, the members will need to successfully pass this training before being able to reserve the machine."
|
||||
default_seats: "Default number of seats"
|
||||
@ -56,6 +58,17 @@ en:
|
||||
public_help: "When unchecked, this option will prevent the training from appearing in the trainings list."
|
||||
disable_training: "Disable the training"
|
||||
disabled_help: "When disabled, the training won't be reservable and won't appear by default in the trainings list."
|
||||
automatic_cancellation: "Automatic cancellation"
|
||||
automatic_cancellation_info: "Activating this option will override the general cancellation settings for this training."
|
||||
automatic_cancellation_switch: "Activate automatic cancellation for this training"
|
||||
automatic_cancellation_threshold: "Minimum number of registrations to maintain a session"
|
||||
automatic_cancellation_deadline: "Deadline, in hours, before automatic cancellation"
|
||||
generic_text_block: "Generic text block"
|
||||
generic_text_block_info: "Displays an editorial block above the training description visible to members."
|
||||
generic_text_block_switch: "Display editorial block"
|
||||
cta_switch: "Display a button"
|
||||
cta_label: "Button label"
|
||||
cta_url: "url"
|
||||
ACTION_training: "{ACTION, select, create{Create} other{Update}} the training"
|
||||
create_success: "The training was created successfully"
|
||||
update_success: "The training was updated successfully"
|
||||
@ -391,9 +404,13 @@ en:
|
||||
plan_session: "Schedule a new session"
|
||||
trainings: "Trainings"
|
||||
add_a_new_training: "Add a new training"
|
||||
name: "Name"
|
||||
name: "Training name"
|
||||
associated_machines: "Associated machines"
|
||||
number_of_tickets: "Number of tickets"
|
||||
cancellation: "Cancellation (attendees | deadline)"
|
||||
cancellation_minimum: "minimum"
|
||||
cancellation_deadline: "h"
|
||||
cancellation_override: "Override"
|
||||
capacity: "Capacity (max. attendees)"
|
||||
select_a_training: "Select a training"
|
||||
training: "Training"
|
||||
date: "Date"
|
||||
@ -415,6 +432,7 @@ en:
|
||||
unable_to_delete_the_training_because_some_users_already_booked_it: "Unable to delete the training because some users already booked it."
|
||||
confirmation_required: "Confirmation required"
|
||||
do_you_really_want_to_delete_this_training: "Do you really want to delete this training?"
|
||||
filter_status: "Filter:"
|
||||
status_enabled: "Enabled"
|
||||
status_disabled: "Disabled"
|
||||
status_all: "All"
|
||||
@ -428,7 +446,7 @@ en:
|
||||
title: "Settings"
|
||||
automatic_cancellation: "Trainings automatic cancellation"
|
||||
automatic_cancellation_info: "If this setting is activated, a minimum number of participants will be required to maintain a session. You will be notified by email if a session is cancelled. If the wallet is enabled, credit notes and refunds will be automatic, otherwise you will have to generate credit notes and make refunds manually."
|
||||
automatic_cancellation_switch: "Activate automatic cancellation of the trainings"
|
||||
automatic_cancellation_switch: "Activate automatic cancellation for all the trainings"
|
||||
automatic_cancellation_threshold: "Minimum number of registrations to maintain a session"
|
||||
automatic_cancellation_deadline: "Deadline, in hours, before automatic cancellation"
|
||||
generic_text_block: "Generic text block"
|
||||
|
Loading…
x
Reference in New Issue
Block a user