From 9813c5d27b0b9843875e215fe47488c0cd79a8ac Mon Sep 17 00:00:00 2001 From: Sylvain Date: Mon, 30 Nov 2020 16:52:55 +0100 Subject: [PATCH] style payment modal --- .../src/javascript/components/fab-modal.tsx | 33 +++++++- .../src/javascript/components/stripe-form.tsx | 9 ++- .../javascript/components/stripe-modal.tsx | 81 ++++++++++--------- app/frontend/src/stylesheets/application.scss | 1 + .../src/stylesheets/modules/fab-modal.scss | 6 +- .../src/stylesheets/modules/stripe-modal.scss | 68 ++++++++++++++++ app/themes/casemate/style.scss.erb | 2 + 7 files changed, 155 insertions(+), 45 deletions(-) create mode 100644 app/frontend/src/stylesheets/modules/stripe-modal.scss diff --git a/app/frontend/src/javascript/components/fab-modal.tsx b/app/frontend/src/javascript/components/fab-modal.tsx index 723dd31c1..0b5a12956 100644 --- a/app/frontend/src/javascript/components/fab-modal.tsx +++ b/app/frontend/src/javascript/components/fab-modal.tsx @@ -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 = ({ title, isOpen, toggleModal, children, confirmButton }) => { +export const FabModal: React.FC = ({ 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 = ({ 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 (
@@ -49,8 +73,9 @@ export const FabModal: React.FC = ({ title, isOpen, toggleModal,
- + {hasCloseButton() &&} {hasConfirmButton() && {confirmButton}} + {hasCustomFooter() && customFooter}
diff --git a/app/frontend/src/javascript/components/stripe-form.tsx b/app/frontend/src/javascript/components/stripe-form.tsx index 10c785a75..050bc2bf3 100644 --- a/app/frontend/src/javascript/components/stripe-form.tsx +++ b/app/frontend/src/javascript/components/stripe-form.tsx @@ -6,9 +6,14 @@ interface StripeFormProps { onSubmit: () => void, onSuccess: (paymentMethod: PaymentMethod) => void, onError: (message: string) => void, + className?: string, } -export const StripeForm: React.FC = ({ 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 = ({ onSubmit, onSuccess, onError, children, className }) => { const stripe = useStripe(); const elements = useElements(); @@ -56,7 +61,7 @@ export const StripeForm: React.FC = ({ onSubmit, onSuccess, onE }; return ( -
+ {children} diff --git a/app/frontend/src/javascript/components/stripe-modal.tsx b/app/frontend/src/javascript/components/stripe-modal.tsx index ef048479e..42b05c396 100644 --- a/app/frontend/src/javascript/components/stripe-modal.tsx +++ b/app/frontend/src/javascript/components/stripe-modal.tsx @@ -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 = ({ 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 ( - +
+ + powered by stripe + mastercard + visa +
); } @@ -122,35 +122,40 @@ const StripeModal: React.FC = ({ isOpen, toggleModal, afterSuc return ( -
- - - - - {hasCgv() &&
- - -
} -
-
- {hasErrors() &&
- {errors} -
} - {isPaymentSchedule() &&
-

{ t('app.shared.stripe.payment_schedule', { DEADLINES: schedule.items.length }) }

-
} -
- - powered by stripe - mastercard - visa -
-
-
+ + + + + {hasErrors() &&
+ {errors} +
} + {hasCgv() &&
+ + +
} + {isPaymentSchedule() &&
+ +

{ t('app.shared.stripe.payment_schedule', { DEADLINES: schedule.items.length }) }

+
} +
+ +
+
); } diff --git a/app/frontend/src/stylesheets/application.scss b/app/frontend/src/stylesheets/application.scss index 922f87a61..0097bf975 100644 --- a/app/frontend/src/stylesheets/application.scss +++ b/app/frontend/src/stylesheets/application.scss @@ -24,5 +24,6 @@ @import "modules/fab-modal"; @import "modules/payment-schedule-summary"; @import "modules/wallet-info"; +@import "modules/stripe-modal"; @import "app.responsive"; diff --git a/app/frontend/src/stylesheets/modules/fab-modal.scss b/app/frontend/src/stylesheets/modules/fab-modal.scss index 1e8d565ab..7eb775fd2 100644 --- a/app/frontend/src/stylesheets/modules/fab-modal.scss +++ b/app/frontend/src/stylesheets/modules/fab-modal.scss @@ -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; diff --git a/app/frontend/src/stylesheets/modules/stripe-modal.scss b/app/frontend/src/stylesheets/modules/stripe-modal.scss new file mode 100644 index 000000000..7a414fc54 --- /dev/null +++ b/app/frontend/src/stylesheets/modules/stripe-modal.scss @@ -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; + } +} diff --git a/app/themes/casemate/style.scss.erb b/app/themes/casemate/style.scss.erb index e4d2235a8..5333982c8 100644 --- a/app/themes/casemate/style.scss.erb +++ b/app/themes/casemate/style.scss.erb @@ -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; }