mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-07 01:54:16 +01:00
fix pending traning modal
This commit is contained in:
parent
f9a8453b70
commit
4ecdf431d5
@ -65,10 +65,12 @@ const MachineCardComponent: React.FC<MachineCardProps> = ({ user, machine, onSho
|
|||||||
<i className="fas fa-bookmark" />
|
<i className="fas fa-bookmark" />
|
||||||
{t('app.public.machine_card.book')}
|
{t('app.public.machine_card.book')}
|
||||||
</ReserveButton>}
|
</ReserveButton>}
|
||||||
<button onClick={handleShowMachine} className="show-button">
|
<span>
|
||||||
<i className="fas fa-eye" />
|
<button onClick={handleShowMachine} className="show-button">
|
||||||
{t('app.public.machine_card.consult')}
|
<i className="fas fa-eye" />
|
||||||
</button>
|
{t('app.public.machine_card.consult')}
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import moment from 'moment';
|
||||||
import { FabModal } from '../base/fab-modal';
|
import { FabModal } from '../base/fab-modal';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { HtmlTranslate } from '../base/html-translate';
|
import { HtmlTranslate } from '../base/html-translate';
|
||||||
|
import { IFablab } from '../../models/fablab';
|
||||||
|
|
||||||
|
declare var Fablab: IFablab;
|
||||||
|
|
||||||
interface PendingTrainingModalProps {
|
interface PendingTrainingModalProps {
|
||||||
isOpen: boolean,
|
isOpen: boolean,
|
||||||
@ -15,8 +19,10 @@ export const PendingTrainingModal: React.FC<PendingTrainingModalProps> = ({ isOp
|
|||||||
/**
|
/**
|
||||||
* Return the formatted localized date for the given date
|
* Return the formatted localized date for the given date
|
||||||
*/
|
*/
|
||||||
const formatDate = (date: Date): string => {
|
const formatDateTime = (date: Date): string => {
|
||||||
return Intl.DateTimeFormat().format(date);
|
const day = Intl.DateTimeFormat().format(moment(date).toDate());
|
||||||
|
const time = Intl.DateTimeFormat(Fablab.intl_locale, { hour: 'numeric', minute: 'numeric' }).format(moment(date).toDate());
|
||||||
|
return t('app.logged.pending_training_modal.DATE_TIME', { DATE: day, TIME:time });
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -25,7 +31,7 @@ export const PendingTrainingModal: React.FC<PendingTrainingModalProps> = ({ isOp
|
|||||||
toggleModal={toggleModal}
|
toggleModal={toggleModal}
|
||||||
closeButton={true}>
|
closeButton={true}>
|
||||||
<p>{t('app.logged.pending_training_modal.wait_for_validated')}</p>
|
<p>{t('app.logged.pending_training_modal.wait_for_validated')}</p>
|
||||||
<p><HtmlTranslate trKey="app.logged.pending_training_modal.training_will_occur_DATE_html" options={{DATE: formatDate(nextReservation)}} /></p>
|
<p><HtmlTranslate trKey="app.logged.pending_training_modal.training_will_occur_DATE_html" options={{ DATE: formatDateTime(nextReservation) }} /></p>
|
||||||
</FabModal>
|
</FabModal>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { BaseSyntheticEvent, useState } from 'react';
|
||||||
import { PendingTrainingModal } from './pending-training-modal';
|
import { PendingTrainingModal } from './pending-training-modal';
|
||||||
import MachineAPI from '../../api/machine';
|
import MachineAPI from '../../api/machine';
|
||||||
import { Machine } from '../../models/machine';
|
import { Machine } from '../../models/machine';
|
||||||
@ -20,17 +20,27 @@ interface ReserveButtonProps {
|
|||||||
*/
|
*/
|
||||||
export const ReserveButton: React.FC<ReserveButtonProps> = ({ currentUser, machineId, onLoginRequested, onLoadingStart, onLoadingEnd, onError, onReserveMachine, className, children }) => {
|
export const ReserveButton: React.FC<ReserveButtonProps> = ({ currentUser, machineId, onLoginRequested, onLoadingStart, onLoadingEnd, onError, onReserveMachine, className, children }) => {
|
||||||
|
|
||||||
|
const [machine, setMachine] = useState<Machine>(null);
|
||||||
const [pendingTraining, setPendingTraining] = useState<boolean>(false);
|
const [pendingTraining, setPendingTraining] = useState<boolean>(false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback triggered when the user clicks on the 'reserve' button.
|
* Callback triggered when the user clicks on the 'reserve' button.
|
||||||
* We load the full machine data, then we check if the user has passed the training for it (if it's needed)
|
|
||||||
*/
|
*/
|
||||||
const handleClick = (user?: User): void => {
|
const handleClick = (event: BaseSyntheticEvent): void => {
|
||||||
|
event.preventDefault();
|
||||||
|
getMachine(currentUser);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We load the full machine data, including data on the current user status for this machine.
|
||||||
|
* Then we check if the user has passed the training for it (if it's needed)
|
||||||
|
*/
|
||||||
|
const getMachine = (user: User): void => {
|
||||||
if (onLoadingStart) onLoadingStart();
|
if (onLoadingStart) onLoadingStart();
|
||||||
|
|
||||||
MachineAPI.get(machineId)
|
MachineAPI.get(machineId)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
|
setMachine(data);
|
||||||
checkTraining(data, user);
|
checkTraining(data, user);
|
||||||
if (onLoadingEnd) onLoadingEnd();
|
if (onLoadingEnd) onLoadingEnd();
|
||||||
})
|
})
|
||||||
@ -38,7 +48,14 @@ export const ReserveButton: React.FC<ReserveButtonProps> = ({ currentUser, machi
|
|||||||
onError(error);
|
onError(error);
|
||||||
if (onLoadingEnd) onLoadingEnd();
|
if (onLoadingEnd) onLoadingEnd();
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open/closes the alert modal informing the user about his pending training
|
||||||
|
*/
|
||||||
|
const togglePendingTrainingModal = (): void => {
|
||||||
|
setPendingTraining(!pendingTraining);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check that the current user has passed the required training before allowing him to book
|
* Check that the current user has passed the required training before allowing him to book
|
||||||
@ -47,7 +64,7 @@ export const ReserveButton: React.FC<ReserveButtonProps> = ({ currentUser, machi
|
|||||||
// if there's no user currently logged, trigger the logging process
|
// if there's no user currently logged, trigger the logging process
|
||||||
if (!user) {
|
if (!user) {
|
||||||
onLoginRequested()
|
onLoginRequested()
|
||||||
.then(user => handleClick(user))
|
.then(user => getMachine(user))
|
||||||
.catch(error => onError(error));
|
.catch(error => onError(error));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -58,17 +75,22 @@ export const ReserveButton: React.FC<ReserveButtonProps> = ({ currentUser, machi
|
|||||||
return onReserveMachine(machineId);
|
return onReserveMachine(machineId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if a user is authenticated and have booked a training for this machine, tell him that he must wait
|
// if there's an authenticated user, and he booked a training for this machine, tell him that he must wait
|
||||||
// for an admin to validate the training before he can book the reservation
|
// for an admin to validate the training before he can book the reservation
|
||||||
if (machine.current_user_next_training_reservation) {
|
if (machine.current_user_next_training_reservation) {
|
||||||
return setPendingTraining(true);
|
return setPendingTraining(true);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button onClick={() => handleClick(currentUser)} className={className}>
|
<span>
|
||||||
{children}
|
<button onClick={handleClick} className={className}>
|
||||||
<PendingTrainingModal isOpen={pendingTraining} />
|
{children}
|
||||||
</button>
|
</button>
|
||||||
|
<PendingTrainingModal isOpen={pendingTraining}
|
||||||
|
toggleModal={togglePendingTrainingModal}
|
||||||
|
nextReservation={machine?.current_user_next_training_reservation?.slots_attributes[0]?.start_at} />
|
||||||
|
</span>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
background: rgba(29, 29, 29, 0.5);
|
background: rgba(29, 29, 29, 0.5);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
margin: -1px;
|
margin: -1px;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.loading::after {
|
&.loading::after {
|
||||||
@ -25,9 +26,9 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 41%;
|
width: 100%;
|
||||||
font-size: 4em;
|
font-size: 4em;
|
||||||
top: 35%;
|
top: 110px;
|
||||||
color: white;
|
color: white;
|
||||||
animation: spin 2s linear infinite;
|
animation: spin 2s linear infinite;
|
||||||
}
|
}
|
||||||
@ -83,7 +84,10 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #000;
|
color: #000;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
line-height: 1.8rem;
|
height: 4em;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.machine-actions {
|
.machine-actions {
|
||||||
@ -95,31 +99,36 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
& > button {
|
& > span {
|
||||||
border: none !important;
|
|
||||||
padding: 15px 12px;
|
|
||||||
display: block;
|
|
||||||
width: 50%;
|
width: 50%;
|
||||||
background-color: #fbfbfb;
|
|
||||||
margin-bottom: 0;
|
|
||||||
font-weight: normal;
|
|
||||||
text-align: center;
|
|
||||||
white-space: nowrap;
|
|
||||||
vertical-align: middle;
|
|
||||||
touch-action: manipulation;
|
|
||||||
cursor: pointer;
|
|
||||||
background-image: none;
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 1.5;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
& > i {
|
& > button {
|
||||||
margin-right: 5px;
|
border: none !important;
|
||||||
|
padding: 15px 12px;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #fbfbfb;
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-weight: normal;
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: middle;
|
||||||
|
touch-action: manipulation;
|
||||||
|
cursor: pointer;
|
||||||
|
background-image: none;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.5;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
& > i {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.reserve-button {
|
||||||
|
border-right: 1px solid #dddddd !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.reserve-button {
|
|
||||||
border-right: 1px solid #dddddd !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,6 +170,7 @@ en:
|
|||||||
machine_reservation: "Machine reservation"
|
machine_reservation: "Machine reservation"
|
||||||
wait_for_validated: "You must wait for your training is being validated by the FabLab team to book this machine."
|
wait_for_validated: "You must wait for your training is being validated by the FabLab team to book this machine."
|
||||||
training_will_occur_DATE_html: "Your training will occur at <strong>{DATE}</strong>"
|
training_will_occur_DATE_html: "Your training will occur at <strong>{DATE}</strong>"
|
||||||
|
DATE_TIME: "{DATE} {TIME}"
|
||||||
#book a training
|
#book a training
|
||||||
trainings_reserve:
|
trainings_reserve:
|
||||||
trainings_planning: "Trainings planning"
|
trainings_planning: "Trainings planning"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user