1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-17 06:52:27 +01:00

WIP: fab-input component

This commit is contained in:
Sylvain 2021-03-24 17:31:50 +01:00
parent 5b854ea831
commit abd6ecabc3
8 changed files with 212 additions and 20 deletions

View File

@ -11,7 +11,7 @@ export default class SettingAPI {
async query (names: Array<SettingName>): Promise<Map<SettingName, any>> {
const res: AxiosResponse = await apiClient.get(`/api/settings/?names=[${names.join(',')}]`);
return res?.data;
return SettingAPI.toSettingsMap(res?.data);
}
static get (name: SettingName): IWrapPromise<Setting> {
@ -23,5 +23,16 @@ export default class SettingAPI {
const api = new SettingAPI();
return wrapPromise(api.query(names));
}
private
static toSettingsMap(data: Object): Map<SettingName, any> {
const dataArray: Array<Array<string | any>> = Object.entries(data);
const map = new Map();
dataArray.forEach(item => {
map.set(SettingName[item[0]], item[1]);
});
return map;
}
}

View File

@ -0,0 +1,63 @@
/**
* This component is a template for an input component that wraps the application style
*/
import React, { ReactNode, SyntheticEvent, useCallback } from 'react';
import { debounce as _debounce } from 'lodash';
interface FabInputProps {
id: string,
onChange?: (event: SyntheticEvent) => void,
value: any,
icon?: ReactNode,
addOn?: ReactNode,
addOnClassName?: string,
className?: string,
disabled?: boolean,
required?: boolean,
debounce?: number,
type?: 'text' | 'date' | 'password' | 'url' | 'time' | 'tel' | 'search' | 'number' | 'month' | 'email' | 'datetime-local' | 'week',
}
export const FabInput: React.FC<FabInputProps> = ({ id, onChange, value, icon, className, disabled, type, required, debounce, addOn, addOnClassName }) => {
/**
* Check if the current component was provided an icon to display
*/
const hasIcon = (): boolean => {
return !!icon;
}
/**
* Check if the current component was provided an add-on element to display, at the end of the input
*/
const hasAddOn = (): boolean => {
return !!addOn;
}
/**
* Debounced (ie. temporised) version of the 'on change' callback.
*
*/
const handler = useCallback(_debounce(onChange, debounce), []);
/**
* Handle the action of the button
*/
const handleChange = (e: SyntheticEvent): void => {
if (typeof onChange === 'function') {
handler(e);
}
}
return (
<div className={`fab-input ${className ? className : ''}`}>
{hasIcon() && <span className="fab-input--icon">{icon}</span>}
<input id={id} type={type} className="fab-input--input" value={value} onChange={handleChange} disabled={disabled} required={required} />
{hasAddOn() && <span className={`fab-input--addon ${addOnClassName ? addOnClassName : ''}`}>{addOn}</span>}
</div>
);
}
FabInput.defaultProps = { type: 'text', debounce: 0 };

View File

@ -10,6 +10,8 @@ import { IApplication } from '../models/application';
import { useTranslation } from 'react-i18next';
import { FabModal, ModalSize } from './fab-modal';
import { User } from '../models/user';
import { Gateway } from '../models/gateway';
import { StripeKeysForm } from './stripe-keys-form';
declare var Application: IApplication;
@ -35,12 +37,22 @@ const SelectGatewayModal: React.FC<SelectGatewayModalModalProps> = ({ isOpen, to
toggleModal();
}
/**
* Save the gateway provided by the target input into the component state
*/
const setGateway = (event: BaseSyntheticEvent) => {
const gateway = event.target.value;
setSelectedGateway(gateway);
setPreventConfirmGateway(!gateway);
}
/**
* Check if any payment gateway was selected
*/
const hasSelectedGateway = (): boolean => {
return selectedGateway !== '';
}
return (
<FabModal title={t('app.admin.invoices.payment.gateway_modal.select_gateway_title')}
isOpen={isOpen}
@ -51,18 +63,19 @@ const SelectGatewayModal: React.FC<SelectGatewayModalModalProps> = ({ isOpen, to
confirmButton={t('app.admin.invoices.payment.gateway_modal.confirm_button')}
onConfirm={onGatewayConfirmed}
preventConfirm={preventConfirmGateway}>
<p className="info-gateway">
{!hasSelectedGateway() && <p className="info-gateway">
{t('app.admin.invoices.payment.gateway_modal.gateway_info')}
</p>
</p>}
<label htmlFor="gateway">{t('app.admin.invoices.payment.gateway_modal.select_gateway')}</label>
<select id="gateway" className="select-gateway" onChange={setGateway} value={selectedGateway}>
<option />
<option value="stripe">{t('app.admin.invoices.payment.gateway_modal.stripe')}</option>
<option value="payzen">{t('app.admin.invoices.payment.gateway_modal.payzen')}</option>
<option value={Gateway.Stripe}>{t('app.admin.invoices.payment.gateway_modal.stripe')}</option>
<option value={Gateway.PayZen}>{t('app.admin.invoices.payment.gateway_modal.payzen')}</option>
</select>
{selectedGateway === Gateway.Stripe && <StripeKeysForm param={'lorem ipsum'} />}
</FabModal>
);
}
};
const SelectGatewayModalWrapper: React.FC<SelectGatewayModalModalProps> = ({ isOpen, toggleModal, currentUser }) => {
return (

View File

@ -2,11 +2,12 @@
* Form to set the stripe's public and private keys
*/
import React, { useEffect, useState } from 'react';
import React, { ReactNode, useEffect, useState } from 'react';
import { Loader } from './loader';
import { useTranslation } from 'react-i18next';
import SettingAPI from '../api/setting';
import { SettingName } from '../models/setting';
import { FabInput } from './fab-input';
interface StripeKeysFormProps {
@ -19,7 +20,11 @@ const StripeKeysFormComponent: React.FC<StripeKeysFormProps> = ({ param }) => {
const { t } = useTranslation('admin');
const [publicKey, setPublicKey] = useState<string>('');
const [publicKeyAddOn, setPublicKeyAddOn] = useState<ReactNode>(null);
const [publicKeyAddOnClassName, setPublicKeyAddOnClassName] = useState<string>('');
const [secretKey, setSecretKey] = useState<string>('');
const [secretKeyAddOn, setSecretKeyAddOn] = useState<ReactNode>(null);
const [secretKeyAddOnClassName, setSecretKeyAddOnClassName] = useState<string>('');
useEffect(() => {
const keys = stripeKeys.read();
@ -31,17 +36,27 @@ const StripeKeysFormComponent: React.FC<StripeKeysFormProps> = ({ param }) => {
// see StripeKeysModalController
// from app/frontend/src/javascript/controllers/admin/invoices.js
const testPublicKey = () => {
setPublicKeyAddOnClassName('key-valid');
setPublicKeyAddOn(<i className="fa fa-check" />);
}
return (
<div>
<div className="stripe-keys-form">
<div className="stripe-keys-info" dangerouslySetInnerHTML={{__html: t('app.admin.invoices.payment.stripe_keys_info_html')}} />
<form name="stripeKeysForm">
<div className="row m-md">
<label htmlFor="stripe_public_key"
className="control-label">{ t('app.admin.invoices.payment.public_key') } *</label>
<div className="input-group">
<span className="input-group-addon"><i className="fa fa-info" /></span>
<div className="stripe-public-input">
<label htmlFor="stripe_public_key">{ t('app.admin.invoices.payment.public_key') } *</label>
<FabInput id="stripe_public_key"
icon={<i className="fa fa-info" />}
value={publicKey}
onChange={testPublicKey}
addOn={publicKeyAddOn}
addOnClassName={publicKeyAddOnClassName}
required />
<div className="key-input">
<span className="key-input__icon"><i className="fa fa-info" /></span>
<input type="text"
className="form-control"
id="stripe_public_key"
value={publicKey}
ng-model-options='{ debounce: 200 }'
@ -55,13 +70,11 @@ const StripeKeysFormComponent: React.FC<StripeKeysFormProps> = ({ param }) => {
</span>
</div>
</div>
<div className="row m-md">
<label htmlFor="stripe_secret_key"
className="control-label">{ t('app.admin.invoices.payment.secret_key') } *</label>
<div className="input-group">
<span className="input-group-addon"><i className="fa fa-key" /></span>
<div className="stripe-secret-input">
<label htmlFor="stripe_secret_key">{ t('app.admin.invoices.payment.secret_key') } *</label>
<div className="key-input">
<span className="key-input__icon"><i className="fa fa-key" /></span>
<input type="text"
className="form-control"
id="stripe_secret_key"
value={secretKey}
ng-model-options='{ debounce: 200 }'

View File

@ -0,0 +1,5 @@
export enum Gateway {
Stripe = 'stripe',
PayZen = 'payzen',
}

View File

@ -22,6 +22,7 @@
@import "modules/stripe";
@import "modules/tour";
@import "modules/fab-modal";
@import "modules/fab-input";
@import "modules/fab-button";
@import "modules/payment-schedule-summary";
@import "modules/wallet-info";
@ -34,5 +35,6 @@
@import "modules/payment-schedule-dashboard";
@import "modules/plan-card";
@import "modules/select-gateway-modal";
@import "modules/stripe-keys-form";
@import "app.responsive";

View File

@ -0,0 +1,63 @@
.fab-input {
position: relative;
display: table;
border-collapse: separate;
&--icon {
min-width: 40px;
padding: 6px 12px;
font-size: 16px;
font-weight: 400;
line-height: 1;
color: #555;
text-align: center;
background-color: #eee;
border: 1px solid #c4c4c4;
border-radius: 4px 0 0 4px;
width: 1%;
white-space: nowrap;
vertical-align: middle;
display: table-cell;
&:first-child {
border-right: 0;
}
}
&--input {
display: block;
width: 100%;
height: 38px;
padding: 6px 12px;
font-size: 16px;
line-height: 1.5;
color: #555555;
background-color: #fff;
background-image: none;
border: 1px solid #c4c4c4;
border-radius: 0;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .08);
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
}
&--addon {
padding: 6px 12px;
font-size: 16px;
font-weight: 400;
line-height: 1;
color: #555;
text-align: center;
background-color: #eee;
border: 1px solid #c4c4c4;
border-radius: 0 4px 4px 0;
width: 1%;
white-space: nowrap;
vertical-align: middle;
display: table-cell;
&:last-child, &:not(:first-child) {
border-left: 0;
}
}
}

View File

@ -0,0 +1,22 @@
.stripe-keys-form {
& {
margin-top: 1em;
}
.stripe-keys-info {
border: 1px solid #bce8f1;
border-radius: 4px;
color: #31708f;
background-color: #d9edf7;
padding: 15px;
}
.stripe-public-input, .stripe-secret-input {
display: block;
margin: 7px;
.key-valid {
background-color: #7bca38;
}
}
}