mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-20 14:54:15 +01:00
style payment modal
This commit is contained in:
parent
f5709ef60e
commit
9813c5d27b
@ -11,16 +11,26 @@ import { CustomAssetName } from '../models/custom-asset';
|
||||
|
||||
Modal.setAppElement('body');
|
||||
|
||||
export enum ModalSize {
|
||||
small = 'sm',
|
||||
medium = 'md',
|
||||
large = 'lg'
|
||||
}
|
||||
|
||||
interface FabModalProps {
|
||||
title: string,
|
||||
isOpen: boolean,
|
||||
toggleModal: () => void,
|
||||
confirmButton?: ReactNode
|
||||
confirmButton?: ReactNode,
|
||||
closeButton?: boolean,
|
||||
className?: string,
|
||||
width?: ModalSize,
|
||||
customFooter?: ReactNode
|
||||
}
|
||||
|
||||
const blackLogoFile = CustomAssetAPI.get(CustomAssetName.LogoBlackFile);
|
||||
|
||||
export const FabModal: React.FC<FabModalProps> = ({ title, isOpen, toggleModal, children, confirmButton }) => {
|
||||
export const FabModal: React.FC<FabModalProps> = ({ title, isOpen, toggleModal, children, confirmButton, className, width = 'sm', closeButton, customFooter }) => {
|
||||
const { t } = useTranslation('shared');
|
||||
const blackLogo = blackLogoFile.read();
|
||||
|
||||
@ -31,9 +41,23 @@ export const FabModal: React.FC<FabModalProps> = ({ title, isOpen, toggleModal,
|
||||
return confirmButton !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should we display the close button?
|
||||
*/
|
||||
const hasCloseButton = (): boolean => {
|
||||
return closeButton;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there's a custom footer
|
||||
*/
|
||||
const hasCustomFooter = (): boolean => {
|
||||
return customFooter !== undefined;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen}
|
||||
className="fab-modal"
|
||||
className={`fab-modal fab-modal-${width} ${className}`}
|
||||
overlayClassName="fab-modal-overlay"
|
||||
onRequestClose={toggleModal}>
|
||||
<div className="fab-modal-header">
|
||||
@ -49,8 +73,9 @@ export const FabModal: React.FC<FabModalProps> = ({ title, isOpen, toggleModal,
|
||||
</div>
|
||||
<div className="fab-modal-footer">
|
||||
<Loader>
|
||||
<button className="modal-btn--close" onClick={toggleModal}>{t('app.shared.buttons.close')}</button>
|
||||
{hasCloseButton() &&<button className="modal-btn--close" onClick={toggleModal}>{t('app.shared.buttons.close')}</button>}
|
||||
{hasConfirmButton() && <span className="modal-btn--confirm">{confirmButton}</span>}
|
||||
{hasCustomFooter() && customFooter}
|
||||
</Loader>
|
||||
</div>
|
||||
</Modal>
|
||||
|
@ -6,9 +6,14 @@ interface StripeFormProps {
|
||||
onSubmit: () => void,
|
||||
onSuccess: (paymentMethod: PaymentMethod) => void,
|
||||
onError: (message: string) => void,
|
||||
className?: string,
|
||||
}
|
||||
|
||||
export const StripeForm: React.FC<StripeFormProps> = ({ onSubmit, onSuccess, onError, children }) => {
|
||||
/**
|
||||
* A form component to collect the credit card details and to create the payment method on Stripe.
|
||||
* The form validation button must be created elsewhere, using the attribute form="stripe-form".
|
||||
*/
|
||||
export const StripeForm: React.FC<StripeFormProps> = ({ onSubmit, onSuccess, onError, children, className }) => {
|
||||
|
||||
const stripe = useStripe();
|
||||
const elements = useElements();
|
||||
@ -56,7 +61,7 @@ export const StripeForm: React.FC<StripeFormProps> = ({ onSubmit, onSuccess, onE
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} id="stripe-form">
|
||||
<form onSubmit={handleSubmit} id="stripe-form" className={className}>
|
||||
<CardElement options={cardOptions} />
|
||||
{children}
|
||||
</form>
|
||||
|
@ -8,7 +8,7 @@ import { Loader } from './loader';
|
||||
import { IApplication } from '../models/application';
|
||||
import { StripeElements } from './stripe-elements';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FabModal } from './fab-modal';
|
||||
import { FabModal, ModalSize } from './fab-modal';
|
||||
import { PaymentMethod } from '@stripe/stripe-js';
|
||||
import { WalletInfo } from './wallet-info';
|
||||
import { Reservation } from '../models/reservation';
|
||||
@ -93,16 +93,16 @@ const StripeModal: React.FC<StripeModalProps> = ({ isOpen, toggleModal, afterSuc
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the form submission button. This button will be shown into the modal footer.
|
||||
* Return the logos, shown in the modal footer.
|
||||
*/
|
||||
const submitButton = (): ReactNode => {
|
||||
const logoFooter = (): ReactNode => {
|
||||
return (
|
||||
<button type="submit"
|
||||
disabled={submitState}
|
||||
form="stripe-form"
|
||||
className="validate-btn">
|
||||
{t('app.shared.stripe.confirm_payment_of_', { AMOUNT: formatPrice(remainingPrice) })}
|
||||
</button>
|
||||
<div className="stripe-modal-icons">
|
||||
<i className="fa fa-lock fa-2x m-r-sm pos-rlt" />
|
||||
<img src={stripeLogo} alt="powered by stripe" />
|
||||
<img src={mastercardLogo} alt="mastercard" />
|
||||
<img src={visaLogo} alt="visa" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -122,35 +122,40 @@ const StripeModal: React.FC<StripeModalProps> = ({ isOpen, toggleModal, afterSuc
|
||||
|
||||
|
||||
return (
|
||||
<div className="stripe-modal">
|
||||
<FabModal title={t('app.shared.stripe.online_payment')} isOpen={isOpen} toggleModal={toggleModal} confirmButton={submitButton()}>
|
||||
<WalletInfo reservation={reservation} currentUser={currentUser} wallet={wallet} price={price} />
|
||||
<StripeElements>
|
||||
<StripeForm onSubmit={handleSubmit} onSuccess={handleFormSuccess} onError={handleFormError}>
|
||||
{hasCgv() && <div className="terms-of-sales">
|
||||
<input type="checkbox" id="acceptToS" name="acceptCondition" checked={tos} onChange={toggleTos} required />
|
||||
<label htmlFor="acceptToS">{ t('app.shared.stripe.i_have_read_and_accept_') }
|
||||
<a href={cgv.custom_asset_file_attributes.attachment_url} target="_blank">
|
||||
{ t('app.shared.stripe._the_general_terms_and_conditions') }
|
||||
</a>
|
||||
</label>
|
||||
</div>}
|
||||
</StripeForm>
|
||||
</StripeElements>
|
||||
{hasErrors() && <div className="stripe-errors">
|
||||
{errors}
|
||||
</div>}
|
||||
{isPaymentSchedule() && <div className="payment-schedule-info">
|
||||
<p>{ t('app.shared.stripe.payment_schedule', { DEADLINES: schedule.items.length }) }</p>
|
||||
</div>}
|
||||
<div className="stripe-modal-icons">
|
||||
<i className="fa fa-lock fa-2x m-r-sm pos-rlt" />
|
||||
<img src={stripeLogo} alt="powered by stripe" />
|
||||
<img src={mastercardLogo} alt="mastercard" />
|
||||
<img src={visaLogo} alt="visa" />
|
||||
</div>
|
||||
</FabModal>
|
||||
</div>
|
||||
<FabModal title={t('app.shared.stripe.online_payment')}
|
||||
isOpen={isOpen}
|
||||
toggleModal={toggleModal}
|
||||
width={ModalSize.medium}
|
||||
closeButton={false}
|
||||
customFooter={logoFooter()}
|
||||
className="stripe-modal">
|
||||
<WalletInfo reservation={reservation} currentUser={currentUser} wallet={wallet} price={price} />
|
||||
<StripeElements>
|
||||
<StripeForm onSubmit={handleSubmit} onSuccess={handleFormSuccess} onError={handleFormError} className="stripe-form">
|
||||
{hasErrors() && <div className="stripe-errors">
|
||||
{errors}
|
||||
</div>}
|
||||
{hasCgv() && <div className="terms-of-sales">
|
||||
<input type="checkbox" id="acceptToS" name="acceptCondition" checked={tos} onChange={toggleTos} required />
|
||||
<label htmlFor="acceptToS">{ t('app.shared.stripe.i_have_read_and_accept_') }
|
||||
<a href={cgv.custom_asset_file_attributes.attachment_url} target="_blank">
|
||||
{ t('app.shared.stripe._the_general_terms_and_conditions') }
|
||||
</a>
|
||||
</label>
|
||||
</div>}
|
||||
{isPaymentSchedule() && <div className="payment-schedule-info">
|
||||
<i className="fa fa-warning" />
|
||||
<p>{ t('app.shared.stripe.payment_schedule', { DEADLINES: schedule.items.length }) }</p>
|
||||
</div>}
|
||||
</StripeForm>
|
||||
<button type="submit"
|
||||
disabled={submitState}
|
||||
form="stripe-form"
|
||||
className="validate-btn">
|
||||
{t('app.shared.stripe.confirm_payment_of_', { AMOUNT: formatPrice(remainingPrice) })}
|
||||
</button>
|
||||
</StripeElements>
|
||||
</FabModal>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -24,5 +24,6 @@
|
||||
@import "modules/fab-modal";
|
||||
@import "modules/payment-schedule-summary";
|
||||
@import "modules/wallet-info";
|
||||
@import "modules/stripe-modal";
|
||||
|
||||
@import "app.responsive";
|
||||
|
@ -19,11 +19,14 @@
|
||||
animation: 0.15s linear fadeIn;
|
||||
}
|
||||
|
||||
.fab-modal-sm { width: 340px; }
|
||||
.fab-modal-md { width: 440px; }
|
||||
.fab-modal-lg { width: 600px; }
|
||||
|
||||
.fab-modal {
|
||||
animation: 0.3s ease-out slideInFromTop;
|
||||
position: relative;
|
||||
top: 90px;
|
||||
width: 340px;
|
||||
margin: auto;
|
||||
opacity: 1;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
|
||||
@ -58,6 +61,7 @@
|
||||
}
|
||||
|
||||
.fab-modal-footer {
|
||||
position: relative;
|
||||
padding: 15px;
|
||||
text-align: right;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
|
68
app/frontend/src/stylesheets/modules/stripe-modal.scss
Normal file
68
app/frontend/src/stylesheets/modules/stripe-modal.scss
Normal file
@ -0,0 +1,68 @@
|
||||
.stripe-modal {
|
||||
.fab-modal-content {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.stripe-form {
|
||||
background-color: #f4f3f3;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px 6px 0 0;
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
|
||||
padding: 15px;
|
||||
|
||||
.stripe-errors {
|
||||
height: 20px;
|
||||
padding: 4px 0;
|
||||
color: #9e2146;
|
||||
margin-bottom: 1.2em;
|
||||
}
|
||||
}
|
||||
.terms-of-sales {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
font-size: 1.4rem;
|
||||
font-weight: 600;
|
||||
input {
|
||||
display: inline;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
label {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
.stripe-modal-icons {
|
||||
text-align: center;
|
||||
|
||||
.fa.fa-lock {
|
||||
top: 7px;
|
||||
color: #9edd78;
|
||||
}
|
||||
|
||||
img {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.payment-schedule-info {
|
||||
border: 1px solid #faebcc;
|
||||
border-radius: 4px;
|
||||
padding: 15px;
|
||||
background-color: #fcf8e3;
|
||||
color: #8a6d3b;
|
||||
|
||||
.fa.fa-warning {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
p {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
.validate-btn {
|
||||
width: 100%;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 0 0 6px 6px;
|
||||
border-top: 0;
|
||||
padding: 16px;
|
||||
color: #fff;
|
||||
background-color: #1d98ec;
|
||||
}
|
||||
}
|
@ -94,6 +94,7 @@ a.project-author,
|
||||
a.dsq-brlink,
|
||||
.alert a,
|
||||
.about-fablab a,
|
||||
.terms-of-sales a,
|
||||
a.collected-infos {
|
||||
color: $primary;
|
||||
}
|
||||
@ -110,6 +111,7 @@ a.dsq-brlink:hover,
|
||||
.payment-schedule-summary .view-full-schedule:hover,
|
||||
.alert a:hover,
|
||||
.about-fablab a:hover,
|
||||
.terms-of-sales a:hover,
|
||||
a.collected-infos:hover {
|
||||
color: $primary-dark;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user