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:
parent
5b854ea831
commit
abd6ecabc3
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
63
app/frontend/src/javascript/components/fab-input.tsx
Normal file
63
app/frontend/src/javascript/components/fab-input.tsx
Normal 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 };
|
||||
|
@ -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 (
|
||||
|
@ -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 }'
|
||||
|
5
app/frontend/src/javascript/models/gateway.ts
Normal file
5
app/frontend/src/javascript/models/gateway.ts
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
export enum Gateway {
|
||||
Stripe = 'stripe',
|
||||
PayZen = 'payzen',
|
||||
}
|
@ -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";
|
||||
|
63
app/frontend/src/stylesheets/modules/fab-input.scss
Normal file
63
app/frontend/src/stylesheets/modules/fab-input.scss
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
22
app/frontend/src/stylesheets/modules/stripe-keys-form.scss
Normal file
22
app/frontend/src/stylesheets/modules/stripe-keys-form.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user