mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-19 13:54:25 +01:00
WIP: create react component to collect card data
This commit is contained in:
parent
0697d9cff1
commit
b88c1009db
17
app/frontend/src/javascript/api/setting.ts
Normal file
17
app/frontend/src/javascript/api/setting.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import apiClient from './api-client';
|
||||
import { AxiosResponse } from 'axios';
|
||||
import { Setting } from '../models/setting';
|
||||
import wrapPromise, { IWrapPromise } from '../lib/wrap-promise';
|
||||
|
||||
export default class SettingAPI {
|
||||
async get (name: string): Promise<Setting> {
|
||||
const res: AxiosResponse = await apiClient.get(`/api/settings/${name}`);
|
||||
return res?.data?.setting;
|
||||
}
|
||||
|
||||
static get (name: string): IWrapPromise<Setting> {
|
||||
const api = new SettingAPI();
|
||||
return wrapPromise(api.get(name));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* This is a compatibility wrapper to allow usage of stripe.js Elements inside of the angular.js app
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Elements } from '@stripe/react-stripe-js';
|
||||
import { react2angular } from 'react2angular';
|
||||
import { IApplication } from '../../models/application';
|
||||
import SettingAPI from '../../api/setting';
|
||||
import { loadStripe } from "@stripe/stripe-js";
|
||||
|
||||
declare var Application: IApplication;
|
||||
const stripePublicKey = SettingAPI.get('stripe_public_key');
|
||||
|
||||
const ElementsWrapper: React.FC = () => {
|
||||
const publicKey = stripePublicKey.read();
|
||||
const stripePromise = loadStripe(publicKey.value);
|
||||
|
||||
return (
|
||||
<Elements stripe={stripePromise} />
|
||||
);
|
||||
}
|
||||
|
||||
Application.Components.component('stripeElements', react2angular(ElementsWrapper));
|
@ -3,7 +3,7 @@
|
||||
*/
|
||||
import Switch from 'react-switch';
|
||||
import { react2angular } from 'react2angular';
|
||||
import { IApplication } from '../models/application';
|
||||
import { IApplication } from '../../models/application';
|
||||
|
||||
declare var Application: IApplication;
|
||||
|
@ -6,7 +6,7 @@ import React from 'react';
|
||||
import Modal from 'react-modal';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Loader } from './loader';
|
||||
import CustomAsset from '../api/custom-asset';
|
||||
import CustomAssetAPI from '../api/custom-asset';
|
||||
|
||||
Modal.setAppElement('body');
|
||||
|
||||
@ -16,7 +16,7 @@ interface FabModalProps {
|
||||
toggleModal: () => void
|
||||
}
|
||||
|
||||
const blackLogoFile = CustomAsset.get('logo-black-file');
|
||||
const blackLogoFile = CustomAssetAPI.get('logo-black-file');
|
||||
|
||||
export const FabModal: React.FC<FabModalProps> = ({ title, isOpen, toggleModal, children }) => {
|
||||
const { t } = useTranslation('shared');
|
||||
|
71
app/frontend/src/javascript/components/stripe-card.tsx
Normal file
71
app/frontend/src/javascript/components/stripe-card.tsx
Normal file
@ -0,0 +1,71 @@
|
||||
/**
|
||||
* This component enables the user to type his card data.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { CardElement, useElements, useStripe } from '@stripe/react-stripe-js';
|
||||
import { react2angular } from 'react2angular';
|
||||
import { Loader } from './loader';
|
||||
import { IApplication } from '../models/application';
|
||||
|
||||
|
||||
declare var Application: IApplication;
|
||||
|
||||
const StripeCard: React.FC = () => {
|
||||
|
||||
const stripe = useStripe();
|
||||
const elements = useElements();
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
// Stripe.js has not loaded yet
|
||||
if (!stripe || !elements) { return; }
|
||||
|
||||
const cardElement = elements.getElement(CardElement);
|
||||
|
||||
const { error, paymentMethod } = await stripe.createPaymentMethod({
|
||||
type: 'card',
|
||||
card: cardElement,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
console.log('[error]', error);
|
||||
} else {
|
||||
console.log('[PaymentMethod]', paymentMethod);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="stripe-card">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<CardElement
|
||||
options={{
|
||||
style: {
|
||||
base: {
|
||||
fontSize: '16px',
|
||||
color: '#424770',
|
||||
'::placeholder': { color: '#aab7c4' }
|
||||
},
|
||||
invalid: {
|
||||
color: '#9e2146',
|
||||
iconColor: '#9e2146'
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const StripeCardWrapper: React.FC = () => {
|
||||
return (
|
||||
<Loader>
|
||||
<StripeCard />
|
||||
</Loader>
|
||||
);
|
||||
}
|
||||
|
||||
Application.Components.component('stripeCard', react2angular(StripeCardWrapper));
|
@ -5,7 +5,6 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { react2angular } from 'react2angular';
|
||||
import moment from 'moment';
|
||||
import { IApplication } from '../models/application';
|
||||
import '../lib/i18n';
|
||||
import { IFilterService } from 'angular';
|
||||
|
@ -705,7 +705,10 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
|
||||
controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'cgv', 'Auth', 'Reservation', 'wallet', 'helpers', '$filter', 'coupon', 'cartItems', 'stripeKey', 'schedule',
|
||||
function ($scope, $uibModalInstance, $state, reservation, price, cgv, Auth, Reservation, wallet, helpers, $filter, coupon, cartItems, stripeKey, schedule) {
|
||||
// user wallet amount
|
||||
$scope.walletAmount = wallet.amount;
|
||||
$scope.wallet = wallet;
|
||||
|
||||
// Global price (total of all items)
|
||||
$scope.price = price.price;
|
||||
|
||||
// Price
|
||||
$scope.amount = helpers.getAmountToPay(price.price, wallet.amount);
|
||||
@ -800,6 +803,9 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
|
||||
* Callback to process the local payment, triggered on button click
|
||||
*/
|
||||
$scope.ok = function () {
|
||||
if ($scope.method.payment_method === 'stripe') {
|
||||
return payByStripe(reservation);
|
||||
}
|
||||
$scope.attempting = true;
|
||||
// save subscription (if there's only a subscription selected)
|
||||
if ($scope.reservation.slots_attributes.length === 0 && selectedPlan) {
|
||||
|
9
app/frontend/src/javascript/models/history-value.ts
Normal file
9
app/frontend/src/javascript/models/history-value.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export interface HistoryValue {
|
||||
id: number,
|
||||
value: string,
|
||||
created_at: Date
|
||||
user: {
|
||||
id: number,
|
||||
name: string
|
||||
}
|
||||
}
|
8
app/frontend/src/javascript/models/setting.ts
Normal file
8
app/frontend/src/javascript/models/setting.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { HistoryValue } from './history-value';
|
||||
|
||||
export interface Setting {
|
||||
name: string,
|
||||
value: string,
|
||||
last_update: Date,
|
||||
history: Array<HistoryValue>
|
||||
}
|
@ -10,9 +10,13 @@
|
||||
<form name="stripeForm" stripe:form cart-items="cartItems" on-payment-success="onPaymentSuccess" stripe-key="{{stripeKey}}" class="form-horizontal">
|
||||
<div class="panel-body">
|
||||
|
||||
<h3 class="m-t-xs" ng-if="walletAmount" ng-bind-html="'app.shared.wallet.you_have_amount_in_wallet' | translate:{ amount: numberFilter(walletAmount, 2), currency: currencySymbol }"></h3>
|
||||
<p ng-if="walletAmount > 0 && amount !== 0" class="text-italic" ng-bind-html="'app.shared.stripe.credit_amount_for_pay_reservation' | translate:{ amount: numberFilter(amount, 2), currency: currencySymbol }"></p>
|
||||
|
||||
<div class="row">
|
||||
<wallet-info current-user="currentUser"
|
||||
reservation="reservation"
|
||||
price="price"
|
||||
remaining-price="amount"
|
||||
wallet="wallet"/>
|
||||
</div>
|
||||
<div id="card-element"></div>
|
||||
<div id="card-errors" role="alert"></div>
|
||||
|
||||
|
@ -88,29 +88,31 @@
|
||||
|
||||
<%= flash_messages %>
|
||||
|
||||
<section class="vbox">
|
||||
<stripe-elements>
|
||||
<section class="vbox">
|
||||
|
||||
<header class="header header-md navbar navbar-fixed-top-xs">
|
||||
<div ui-view="header"></div>
|
||||
</header>
|
||||
<header class="header header-md navbar navbar-fixed-top-xs">
|
||||
<div ui-view="header"></div>
|
||||
</header>
|
||||
|
||||
<section ui-view="content">
|
||||
<section class="hbox stretch">
|
||||
<aside id="nav" class="aside-md bg-red hidden-print" ui-view="leftnav"></aside>
|
||||
<section ui-view="content">
|
||||
<section class="hbox stretch">
|
||||
<aside id="nav" class="aside-md bg-red hidden-print" ui-view="leftnav"></aside>
|
||||
|
||||
<section id="content">
|
||||
<section class="vbox">
|
||||
<section id="cookies-modal" ui-view="cookies">
|
||||
</section>
|
||||
<section id="content-main" class="scrollable" ui-view="main">
|
||||
<section id="content">
|
||||
<section class="vbox">
|
||||
<section id="cookies-modal" ui-view="cookies">
|
||||
</section>
|
||||
<section id="content-main" class="scrollable" ui-view="main">
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
</section> <!-- /.hbox -->
|
||||
</section>
|
||||
</section> <!-- /.hbox -->
|
||||
</section>
|
||||
|
||||
</section> <!-- /.vbox -->
|
||||
</section> <!-- /.vbox -->
|
||||
</stripe-elements>
|
||||
|
||||
<div class="app-generator">
|
||||
<span class="app-version" uib-tooltip="{{'app.public.common.version' | translate}} {{version.current}}" ng-if="currentUser && currentUser.role == 'admin'" ng-click="versionModal()">
|
||||
|
@ -41,6 +41,8 @@
|
||||
"@claviska/jquery-minicolors": "^2.3.5",
|
||||
"@fortawesome/fontawesome-free": "5.14.0",
|
||||
"@rails/webpacker": "5.2.1",
|
||||
"@stripe/react-stripe-js": "^1.1.2",
|
||||
"@stripe/stripe-js": "^1.11.0",
|
||||
"@types/react": "^16.9.53",
|
||||
"@types/react-dom": "^16.9.8",
|
||||
"@uirouter/angularjs": "0.4",
|
||||
|
12
yarn.lock
12
yarn.lock
@ -1121,6 +1121,18 @@
|
||||
webpack-cli "^3.3.12"
|
||||
webpack-sources "^1.4.3"
|
||||
|
||||
"@stripe/react-stripe-js@^1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@stripe/react-stripe-js/-/react-stripe-js-1.1.2.tgz#a7f5ef5b4d7dc7fa723501b706644414cfe6dcba"
|
||||
integrity sha512-07hu8RJXwWKGbvdvd1yt1cYvGtDB8jFX+q10f7FQuItUt9rlSo0am3WIx845iMHANiYgxyRb1PS201Yle9xxPQ==
|
||||
dependencies:
|
||||
prop-types "^15.7.2"
|
||||
|
||||
"@stripe/stripe-js@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@stripe/stripe-js/-/stripe-js-1.11.0.tgz#00e812d72a7760dae08237875066d263671478ee"
|
||||
integrity sha512-SDNZKuETBEVkernd1tq8tL6wNfVKrl24Txs3p+4NYxoaIbNaEO7mrln/2Y/WRcQBWjagvhDIM5I6+X1rfK0qhQ==
|
||||
|
||||
"@types/angular@^1.6.39":
|
||||
version "1.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/angular/-/angular-1.7.3.tgz#138c2f2f688e9dcb413c6052d9483d773ce7f627"
|
||||
|
Loading…
x
Reference in New Issue
Block a user