mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-30 19:52:20 +01:00
(ui) display trainings in list
This commit is contained in:
parent
117bd36caa
commit
305b5425bc
@ -28,4 +28,9 @@ export default class TrainingAPI {
|
||||
});
|
||||
return res?.data;
|
||||
}
|
||||
|
||||
static async destroy (trainingId: number): Promise<void> {
|
||||
const res: AxiosResponse<void> = await apiClient.delete(`/api/trainings/${trainingId}`);
|
||||
return res?.data;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,61 @@
|
||||
import { ReactNode, useState } from 'react';
|
||||
import * as React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FabButton } from './fab-button';
|
||||
import { FabModal } from './fab-modal';
|
||||
import { Trash } from 'phosphor-react';
|
||||
|
||||
interface DestroyButtonProps {
|
||||
onSuccess: (message: string) => void,
|
||||
onError: (message: string) => void,
|
||||
itemId: number,
|
||||
itemType: string,
|
||||
apiDestroy: (itemId: number) => Promise<void>,
|
||||
confirmationMessage?: string|ReactNode,
|
||||
className?: string,
|
||||
iconSize?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* This component shows a button.
|
||||
* When clicked, we show a modal dialog to ask the user for confirmation about the deletion of the provided item.
|
||||
*/
|
||||
export const DestroyButton: React.FC<DestroyButtonProps> = ({ onSuccess, onError, itemId, itemType, apiDestroy, confirmationMessage, className, iconSize = 24 }) => {
|
||||
const { t } = useTranslation('admin');
|
||||
|
||||
const [deletionModal, setDeletionModal] = useState<boolean>(false);
|
||||
|
||||
/**
|
||||
* Opens/closes the deletion modal
|
||||
*/
|
||||
const toggleDeletionModal = (): void => {
|
||||
setDeletionModal(!deletionModal);
|
||||
};
|
||||
|
||||
/**
|
||||
* The deletion has been confirmed by the user.
|
||||
* Call the API to trigger the deletion of the given item
|
||||
*/
|
||||
const onDeleteConfirmed = (): void => {
|
||||
apiDestroy(itemId).then(() => {
|
||||
onSuccess(t('app.admin.destroy_button.deleted', { TYPE: itemType }));
|
||||
}).catch((error) => {
|
||||
onError(t('app.admin.destroy_button.unable_to_delete', { TYPE: itemType }) + error);
|
||||
});
|
||||
toggleDeletionModal();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`destroy-button ${className || ''}`}>
|
||||
<FabButton type='button' className="destroy-button-cta" icon={<Trash size={iconSize} weight="fill" />} onClick={toggleDeletionModal} />
|
||||
<FabModal title={t('app.admin.destroy_button.delete_item', { TYPE: itemType })}
|
||||
isOpen={deletionModal}
|
||||
toggleModal={toggleDeletionModal}
|
||||
closeButton={true}
|
||||
confirmButton={t('app.admin.destroy_button.confirm_delete')}
|
||||
onConfirm={onDeleteConfirmed}>
|
||||
<span>{confirmationMessage || t('app.admin.destroy_button.delete_confirmation', { TYPE: itemType })}</span>
|
||||
</FabModal>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -5,9 +5,9 @@ import { useTranslation } from 'react-i18next';
|
||||
import { FabPopover } from '../../base/fab-popover';
|
||||
import { CreatePack } from './create-pack';
|
||||
import PrepaidPackAPI from '../../../api/prepaid-pack';
|
||||
import { DeletePack } from './delete-pack';
|
||||
import { EditPack } from './edit-pack';
|
||||
import FormatLib from '../../../lib/format';
|
||||
import { DestroyButton } from '../../base/destroy-button';
|
||||
|
||||
interface ConfigurePacksButtonProps {
|
||||
packsData: Array<PrepaidPack>,
|
||||
@ -76,7 +76,13 @@ export const ConfigurePacksButton: React.FC<ConfigurePacksButtonProps> = ({ pack
|
||||
{formatDuration(p.minutes)} - {FormatLib.price(p.amount)}
|
||||
<span className="pack-actions">
|
||||
<EditPack onSuccess={handleSuccess} onError={onError} pack={p} />
|
||||
<DeletePack onSuccess={handleSuccess} onError={onError} pack={p} />
|
||||
<DestroyButton onSuccess={handleSuccess}
|
||||
onError={onError}
|
||||
itemId={p.id}
|
||||
itemType={t('app.admin.configure_packs_button.pack')}
|
||||
apiDestroy={PrepaidPackAPI.destroy}
|
||||
iconSize={12}
|
||||
confirmationMessage={t('app.admin.configure_packs_button.delete_confirmation')} />
|
||||
</span>
|
||||
</li>)}
|
||||
</ul>
|
||||
|
@ -1,68 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
import * as React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FabButton } from '../../base/fab-button';
|
||||
import { FabModal } from '../../base/fab-modal';
|
||||
import { Loader } from '../../base/loader';
|
||||
import { PrepaidPack } from '../../../models/prepaid-pack';
|
||||
import PrepaidPackAPI from '../../../api/prepaid-pack';
|
||||
|
||||
interface DeletePackProps {
|
||||
onSuccess: (message: string) => void,
|
||||
onError: (message: string) => void,
|
||||
pack: PrepaidPack,
|
||||
}
|
||||
|
||||
/**
|
||||
* This component shows a button.
|
||||
* When clicked, we show a modal dialog to ask the user for confirmation about the deletion of the provided pack.
|
||||
*/
|
||||
const DeletePack: React.FC<DeletePackProps> = ({ onSuccess, onError, pack }) => {
|
||||
const { t } = useTranslation('admin');
|
||||
|
||||
const [deletionModal, setDeletionModal] = useState<boolean>(false);
|
||||
|
||||
/**
|
||||
* Opens/closes the deletion modal
|
||||
*/
|
||||
const toggleDeletionModal = (): void => {
|
||||
setDeletionModal(!deletionModal);
|
||||
};
|
||||
|
||||
/**
|
||||
* The deletion has been confirmed by the user.
|
||||
* Call the API to trigger the deletion of the temporary set plan-category
|
||||
*/
|
||||
const onDeleteConfirmed = (): void => {
|
||||
PrepaidPackAPI.destroy(pack.id).then(() => {
|
||||
onSuccess(t('app.admin.delete_pack.pack_deleted'));
|
||||
}).catch((error) => {
|
||||
onError(t('app.admin.delete_pack.unable_to_delete') + error);
|
||||
});
|
||||
toggleDeletionModal();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="delete-pack">
|
||||
<FabButton type='button' className="remove-pack-button" icon={<i className="fa fa-trash" />} onClick={toggleDeletionModal} />
|
||||
<FabModal title={t('app.admin.delete_pack.delete_pack')}
|
||||
isOpen={deletionModal}
|
||||
toggleModal={toggleDeletionModal}
|
||||
closeButton={true}
|
||||
confirmButton={t('app.admin.delete_pack.confirm_delete')}
|
||||
onConfirm={onDeleteConfirmed}>
|
||||
<span>{t('app.admin.delete_pack.delete_confirmation')}</span>
|
||||
</FabModal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const DeletePackWrapper: React.FC<DeletePackProps> = (props) => {
|
||||
return (
|
||||
<Loader>
|
||||
<DeletePack {...props} />
|
||||
</Loader>
|
||||
);
|
||||
};
|
||||
|
||||
export { DeletePackWrapper as DeletePack };
|
@ -1,15 +1,18 @@
|
||||
import * as React from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useEffect, 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 { useForm, SubmitHandler } from 'react-hook-form';
|
||||
import { useForm, SubmitHandler, useWatch } from 'react-hook-form';
|
||||
import { FormSwitch } from '../form/form-switch';
|
||||
import { FormInput } from '../form/form-input';
|
||||
import { FabButton } from '../base/fab-button';
|
||||
import { EditorialBlockForm } from '../editorial-block/editorial-block-form';
|
||||
import { SettingName, SettingValue, trainingSettings } from '../../models/setting';
|
||||
import SettingAPI from '../../api/setting';
|
||||
import SettingLib from '../../lib/setting';
|
||||
|
||||
declare const Application: IApplication;
|
||||
|
||||
@ -21,20 +24,22 @@ interface TrainingsSettingsProps {
|
||||
/**
|
||||
* Trainings settings
|
||||
*/
|
||||
export const TrainingsSettings: React.FC<TrainingsSettingsProps> = () => {
|
||||
export const TrainingsSettings: React.FC<TrainingsSettingsProps> = ({ onError, onSuccess }) => {
|
||||
const { t } = useTranslation('admin');
|
||||
const { register, control, formState, handleSubmit } = useForm();
|
||||
const { register, control, formState, handleSubmit, reset } = useForm<Record<SettingName, SettingValue>>();
|
||||
|
||||
const [isActiveAutoCancellation, setIsActiveAutoCancellation] = useState<boolean>(false);
|
||||
const isActiveAutoCancellation = useWatch({ control, name: 'trainings_auto_cancel' }) as boolean;
|
||||
const [isActiveAuthorizationValidity, setIsActiveAuthorizationValidity] = useState<boolean>(false);
|
||||
const [isActiveValidationRule, setIsActiveValidationRule] = useState<boolean>(false);
|
||||
|
||||
/**
|
||||
* Callback triggered when the auto cancellation switch has changed.
|
||||
*/
|
||||
const toggleAutoCancellation = (value: boolean) => {
|
||||
setIsActiveAutoCancellation(value);
|
||||
};
|
||||
useEffect(() => {
|
||||
SettingAPI.query(trainingSettings)
|
||||
.then(settings => {
|
||||
const data = SettingLib.bulkMapToObject(settings);
|
||||
reset(data);
|
||||
})
|
||||
.catch(onError);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Callback triggered when the authorisation validity switch has changed.
|
||||
@ -53,8 +58,12 @@ export const TrainingsSettings: React.FC<TrainingsSettingsProps> = () => {
|
||||
/**
|
||||
* Callback triggered when the form is submitted: save the settings
|
||||
*/
|
||||
const onSubmit: SubmitHandler<any> = (data) => {
|
||||
console.log(data);
|
||||
const onSubmit: SubmitHandler<Record<SettingName, SettingValue>> = (data) => {
|
||||
SettingAPI.bulkUpdate(SettingLib.objectToBulkMap(data)).then(() => {
|
||||
onSuccess(t('app.admin.trainings_settings.update_success'));
|
||||
}, reason => {
|
||||
onError(reason);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
@ -78,20 +87,20 @@ export const TrainingsSettings: React.FC<TrainingsSettingsProps> = () => {
|
||||
</header>
|
||||
|
||||
<div className="content">
|
||||
<FormSwitch id="active_auto_cancellation" control={control}
|
||||
onChange={toggleAutoCancellation} formState={formState}
|
||||
<FormSwitch id="trainings_auto_cancel" control={control}
|
||||
formState={formState}
|
||||
defaultValue={isActiveAutoCancellation}
|
||||
label={t('app.admin.trainings_settings.automatic_cancellation_switch')} />
|
||||
|
||||
{isActiveAutoCancellation && <>
|
||||
<FormInput id="auto_cancellation_threshold"
|
||||
<FormInput id="trainings_auto_cancel_threshold"
|
||||
type="number"
|
||||
register={register}
|
||||
rules={{ required: isActiveAutoCancellation, min: 0 }}
|
||||
step={1}
|
||||
formState={formState}
|
||||
label={t('app.admin.trainings_settings.automatic_cancellation_threshold')} />
|
||||
<FormInput id="auto_cancellation_deadline"
|
||||
<FormInput id="trainings_auto_cancel_deadline"
|
||||
type="number"
|
||||
register={register}
|
||||
rules={{ required: isActiveAutoCancellation, min: 1 }}
|
||||
|
@ -7,7 +7,13 @@ import { useTranslation } from 'react-i18next';
|
||||
import { FabButton } from '../base/fab-button';
|
||||
import Select from 'react-select';
|
||||
import { SelectOption } from '../../models/select';
|
||||
import { CalendarBlank, PencilSimple, Trash } from 'phosphor-react';
|
||||
import { CalendarBlank, PencilSimple } from 'phosphor-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import type { Training } from '../../models/training';
|
||||
import type { Machine } from '../../models/machine';
|
||||
import TrainingAPI from '../../api/training';
|
||||
import MachineAPI from '../../api/machine';
|
||||
import { DestroyButton } from '../base/destroy-button';
|
||||
|
||||
declare const Application: IApplication;
|
||||
|
||||
@ -19,9 +25,13 @@ interface TrainingsProps {
|
||||
/**
|
||||
* Admin list of trainings
|
||||
*/
|
||||
export const Trainings: React.FC<TrainingsProps> = () => {
|
||||
export const Trainings: React.FC<TrainingsProps> = ({ onError, onSuccess }) => {
|
||||
const { t } = useTranslation('admin');
|
||||
|
||||
const [trainings, setTrainings] = useState<Array<Training>>([]);
|
||||
const [machines, setMachines] = useState<Array<Machine>>([]);
|
||||
const [filter, setFilter] = useState<boolean>(null);
|
||||
|
||||
// Styles the React-select component
|
||||
const customStyles = {
|
||||
control: base => ({
|
||||
@ -35,18 +45,53 @@ export const Trainings: React.FC<TrainingsProps> = () => {
|
||||
})
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
MachineAPI.index({ disabled: false })
|
||||
.then(setMachines)
|
||||
.catch(onError);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
TrainingAPI.index({ disabled: filter })
|
||||
.then(setTrainings)
|
||||
.catch(onError);
|
||||
}, [filter]);
|
||||
|
||||
/** Creates filtering options to the react-select format */
|
||||
const buildFilterOptions = (): Array<SelectOption<any>> => {
|
||||
const buildFilterOptions = (): Array<SelectOption<boolean>> => {
|
||||
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') }
|
||||
{ value: null, label: t('app.admin.trainings.status_all') },
|
||||
{ value: false, label: t('app.admin.trainings.status_enabled') },
|
||||
{ value: true, label: t('app.admin.trainings.status_disabled') }
|
||||
];
|
||||
};
|
||||
|
||||
/** Handel filter change */
|
||||
const onFilterChange = (option: SelectOption<any>) => {
|
||||
console.log(option);
|
||||
const onFilterChange = (option: SelectOption<boolean>) => {
|
||||
setFilter(option.value);
|
||||
};
|
||||
|
||||
/**
|
||||
* List of machines names for teh given ids
|
||||
*/
|
||||
const machinesNames = (ids: Array<number>): string => {
|
||||
return machines.filter(m => ids.includes(m.id)).map(m => m.name).join(', ');
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* Check if the given training has associated non-disabled machines
|
||||
*/
|
||||
const hasMachines = (training: Training): boolean => {
|
||||
const activesMachines = machines.map(m => m.id);
|
||||
return training.machine_ids.filter(id => activesMachines.includes(id)).length > 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Redirect the user to the given training edition page
|
||||
*/
|
||||
const toTrainingEdit = (training: Training): void => {
|
||||
window.location.href = `/#!/admin/trainings/${training.id}/edit`;
|
||||
};
|
||||
|
||||
/** Link to calendar page */
|
||||
@ -84,61 +129,66 @@ export const Trainings: React.FC<TrainingsProps> = () => {
|
||||
{/* map
|
||||
ajouter la classe .is-override si l'item a au moins un réglage spécifique (différent des paramètres généraux)
|
||||
*/}
|
||||
<div className='trainings-list-item'>
|
||||
<div className='name'>
|
||||
<span>{t('app.admin.trainings.name')}</span>
|
||||
<p>All you can learn : super training</p>
|
||||
</div>
|
||||
{trainings.map(training => (
|
||||
<div className='trainings-list-item' key={training.id}>
|
||||
<div className='name'>
|
||||
<span>{t('app.admin.trainings.name')}</span>
|
||||
<p>{training.name}</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>
|
||||
{(hasMachines(training) && <div className='machines'>
|
||||
<span>{t('app.admin.trainings.associated_machines')}</span>
|
||||
<p>{machinesNames(training.machine_ids)}</p>
|
||||
</div>) || <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 un réglage spécifique (différent des paramètres généraux) */}
|
||||
{true && <span className='override'>{t('app.admin.trainings.override')}</span> }
|
||||
</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 un réglage spécifique (différent des paramètres généraux) */}
|
||||
{true && <span className='override'>{t('app.admin.trainings.override')}</span> }
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='capacity'>
|
||||
<span>{t('app.admin.trainings.capacity')}</span>
|
||||
<p>10</p>
|
||||
</div>
|
||||
<div className='capacity'>
|
||||
<span>{t('app.admin.trainings.capacity')}</span>
|
||||
<p>{training.nb_total_places}</p>
|
||||
</div>
|
||||
|
||||
<div className='authorisation'>
|
||||
<span>{t('app.admin.trainings.authorisation')}</span>
|
||||
<p>
|
||||
{t('app.admin.trainings.active_true')}
|
||||
<span>|</span>{t('app.admin.trainings.period_MONTH', { MONTH: 48 })}
|
||||
{/* si l'item a un réglage spécifique (différent des paramètres généraux) */}
|
||||
{true && <span className='override'>{t('app.admin.trainings.override')}</span> }
|
||||
</p>
|
||||
</div>
|
||||
<div className='authorisation'>
|
||||
<span>{t('app.admin.trainings.authorisation')}</span>
|
||||
<p>
|
||||
{t('app.admin.trainings.active_true')}
|
||||
<span>|</span>{t('app.admin.trainings.period_MONTH', { MONTH: 48 })}
|
||||
{/* si l'item a un réglage spécifique (différent des paramètres généraux) */}
|
||||
{true && <span className='override'>{t('app.admin.trainings.override')}</span> }
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='rule'>
|
||||
<span>{t('app.admin.trainings.validation_rule')}</span>
|
||||
<p>
|
||||
{t('app.admin.trainings.active_false')}
|
||||
<span>|</span>
|
||||
{/* si l'item a un réglage spécifique (différent des paramètres généraux) */}
|
||||
{true && <span className='override'>{t('app.admin.trainings.override')}</span> }
|
||||
</p>
|
||||
</div>
|
||||
<div className='rule'>
|
||||
<span>{t('app.admin.trainings.validation_rule')}</span>
|
||||
<p>
|
||||
{t('app.admin.trainings.active_false')}
|
||||
<span>|</span>
|
||||
{/* si l'item a un réglage spécifique (différent des paramètres généraux) */}
|
||||
{true && <span className='override'>{t('app.admin.trainings.override')}</span> }
|
||||
</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 className='actions'>
|
||||
<div className='grpBtn'>
|
||||
<FabButton className='edit-btn' onClick={() => toTrainingEdit(training)}>
|
||||
<PencilSimple size={20} weight="fill" />
|
||||
</FabButton>
|
||||
<DestroyButton onSuccess={onSuccess}
|
||||
className="delete-btn"
|
||||
onError={onError}
|
||||
itemId={training.id}
|
||||
itemType={t('app.admin.trainings.training')}
|
||||
apiDestroy={TrainingAPI.destroy} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -354,6 +354,20 @@ Application.Controllers.controller('TrainingsAdminController', ['$scope', '$stat
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows a success message forwarded from a child react component
|
||||
*/
|
||||
$scope.onSuccess = function (message) {
|
||||
growl.success(message);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback triggered by react components
|
||||
*/
|
||||
$scope.onError = function (message) {
|
||||
growl.error(message);
|
||||
};
|
||||
|
||||
/**
|
||||
* Setup the feature-tour for the admin/trainings page.
|
||||
* This is intended as a contextual help (when pressing F1)
|
||||
|
@ -233,6 +233,12 @@ export const storeSettings = [
|
||||
'store_hidden'
|
||||
] as const;
|
||||
|
||||
export const trainingSettings = [
|
||||
'trainings_auto_cancel',
|
||||
'trainings_auto_cancel_threshold',
|
||||
'trainings_auto_cancel_deadline'
|
||||
] as const;
|
||||
|
||||
export const allSettings = [
|
||||
...homePageSettings,
|
||||
...privacyPolicySettings,
|
||||
@ -258,7 +264,8 @@ export const allSettings = [
|
||||
...pricingSettings,
|
||||
...poymentSettings,
|
||||
...displaySettings,
|
||||
...storeSettings
|
||||
...storeSettings,
|
||||
...trainingSettings
|
||||
] as const;
|
||||
|
||||
export type SettingName = typeof allSettings[number];
|
||||
|
@ -23,6 +23,7 @@
|
||||
@import "modules/authentication-provider/openid-connect-data-mapping-form";
|
||||
@import "modules/authentication-provider/provider-form";
|
||||
@import "modules/authentication-provider/type-mapping-modal";
|
||||
@import "modules/base/destroy-button";
|
||||
@import "modules/base/editorial-block";
|
||||
@import "modules/base/fab-alert";
|
||||
@import "modules/base/fab-button";
|
||||
@ -94,7 +95,6 @@
|
||||
@import "modules/pricing/editable-price";
|
||||
@import "modules/pricing/machines/configure-packs-button";
|
||||
@import "modules/pricing/machines/create-pack";
|
||||
@import "modules/pricing/machines/delete-pack";
|
||||
@import "modules/pricing/machines/edit-pack";
|
||||
@import "modules/pricing/machines/machines-pricing";
|
||||
@import "modules/pricing/machines/pack-form";
|
||||
|
@ -0,0 +1,8 @@
|
||||
.destroy-button {
|
||||
display: inline;
|
||||
|
||||
& > button.destroy-button-cta {
|
||||
background-color: var(--alert);
|
||||
color: white;
|
||||
}
|
||||
}
|
@ -38,6 +38,7 @@
|
||||
}
|
||||
|
||||
.pack-actions button {
|
||||
min-height: unset;
|
||||
font-size: 10px;
|
||||
vertical-align: middle;
|
||||
line-height: 10px;
|
||||
|
@ -1,8 +0,0 @@
|
||||
.delete-pack {
|
||||
display: inline;
|
||||
|
||||
.remove-pack-button {
|
||||
background-color: #cb1117;
|
||||
color: white;
|
||||
}
|
||||
}
|
@ -26,7 +26,7 @@
|
||||
<div class="col-md-12">
|
||||
<uib-tabset justified="true" active="tabs.active">
|
||||
<uib-tab heading="{{ 'app.admin.trainings.trainings_settings' | translate }}" index="1" class="manage-trainings">
|
||||
<trainings-settings on-error="onError" on-success="on-success"></trainings-settings>
|
||||
<trainings-settings on-error="onError" on-success="onSuccess"></trainings-settings>
|
||||
</uib-tab>
|
||||
<uib-tab heading="{{ 'app.admin.trainings.all_trainings' | translate }}" index="0" class="manage-trainings">
|
||||
<trainings on-error="onError" on-success="on-success"></trainings>
|
||||
|
@ -165,7 +165,10 @@ class Setting < ApplicationRecord
|
||||
advanced_accounting
|
||||
external_id
|
||||
prevent_invoices_zero
|
||||
invoice_VAT-name] }
|
||||
invoice_VAT-name
|
||||
trainings_auto_cancel
|
||||
trainings_auto_cancel_threshold
|
||||
trainings_auto_cancel_deadline] }
|
||||
# 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
|
||||
|
@ -1,6 +1,12 @@
|
||||
en:
|
||||
app:
|
||||
admin:
|
||||
destroy_button:
|
||||
deleted: "The {TYPE} was successfully deleted."
|
||||
unable_to_delete: "Unable to delete the {TYPE}: "
|
||||
delete_item: "Delete the {TYPE}"
|
||||
confirm_delete: "Delete"
|
||||
delete_confirmation: "Are you sure you want to delete this {TYPE}?"
|
||||
machines:
|
||||
the_fablab_s_machines: "The FabLab's machines"
|
||||
all_machines: "All machines"
|
||||
@ -491,6 +497,7 @@ en:
|
||||
cta_label: "Button label"
|
||||
cta_url: "url"
|
||||
save: "Save"
|
||||
update_success: "The trainings settings were successfully updated"
|
||||
#events tracking and management
|
||||
events:
|
||||
events_monitoring: "Events monitoring"
|
||||
@ -669,9 +676,11 @@ en:
|
||||
machines: "Machines"
|
||||
price_updated: "Price successfully updated"
|
||||
configure_packs_button:
|
||||
pack: "prepaid pack"
|
||||
packs: "Prepaid packs"
|
||||
no_packs: "No packs for now"
|
||||
pack_DURATION: "{DURATION} hours"
|
||||
delete_confirmation: "Are you sure you want to delete this prepaid pack? This won't be possible if the pack was already bought by users."
|
||||
configure_extended_prices_button:
|
||||
extended_prices: "Extended prices"
|
||||
no_extended_prices: "No extended price for now"
|
||||
@ -695,12 +704,6 @@ en:
|
||||
new_pack_info: "A prepaid pack allows users to buy {TYPE, select, Machine{machine} Space{space} other{}} hours before booking any slots. These packs can provide discounts on volumes purchases."
|
||||
create_pack: "Create this pack"
|
||||
pack_successfully_created: "The new prepaid pack was successfully created."
|
||||
delete_pack:
|
||||
pack_deleted: "The prepaid pack was successfully deleted."
|
||||
unable_to_delete: "Unable to delete the prepaid pack: "
|
||||
delete_pack: "Delete the prepaid pack"
|
||||
confirm_delete: "Delete"
|
||||
delete_confirmation: "Are you sure you want to delete this prepaid pack? This won't be possible if the pack was already bought by users."
|
||||
edit_pack:
|
||||
edit_pack: "Edit the pack"
|
||||
confirm_changes: "Confirm changes"
|
||||
|
@ -664,6 +664,9 @@ en:
|
||||
external_id: "external identifier"
|
||||
prevent_invoices_zero: "prevent building invoices at 0"
|
||||
invoice_VAT-name: "VAT name"
|
||||
trainings_auto_cancel: "Trainings automatic cancellation"
|
||||
trainings_auto_cancel_threshold: "Minimum participants for automatic cancellation"
|
||||
trainings_auto_cancel_deadline: "Automatic cancellation deadline"
|
||||
#statuses of projects
|
||||
statuses:
|
||||
new: "New"
|
||||
|
Loading…
x
Reference in New Issue
Block a user