Mm 50735 (#22626)
* add shipping address to self hosted signup model * purchase modals allow scroll * fix z-index issues with payment modal dropdowns
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
379dbb1ca8
Коммит
9105077ee1
@@ -21,10 +21,11 @@ type BootstrapSelfHostedSignupResponseInternal struct {
|
||||
|
||||
// email contained in token, so not in the request body.
|
||||
type SelfHostedCustomerForm struct {
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
BillingAddress *Address `json:"billing_address"`
|
||||
Organization string `json:"organization"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
BillingAddress *Address `json:"billing_address"`
|
||||
ShippingAddress *Address `json:"shipping_address"`
|
||||
Organization string `json:"organization"`
|
||||
}
|
||||
|
||||
type SelfHostedConfirmPaymentMethodRequest struct {
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
.shipping-address-section {
|
||||
display: flex;
|
||||
align-content: flex-start;
|
||||
padding-bottom: 24px;
|
||||
font-weight: normal;
|
||||
|
||||
button.no-style {
|
||||
padding-left: 0;
|
||||
border: none;
|
||||
background: transparent;
|
||||
outline: unset;
|
||||
text-align: left;
|
||||
|
||||
&:focus {
|
||||
outline: unset;
|
||||
}
|
||||
}
|
||||
|
||||
#address-same-than-billing-address {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.Form-checkbox-label {
|
||||
padding-left: 12px;
|
||||
cursor: default;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.billing_address_btn_text {
|
||||
color: var(--center-channel-color);
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {useIntl} from 'react-intl';
|
||||
|
||||
import './choose_different_shipping.scss';
|
||||
|
||||
interface Props {
|
||||
shippingIsSame: boolean;
|
||||
setShippingIsSame: (different: boolean) => void;
|
||||
}
|
||||
export default function ChooseDifferentShipping(props: Props) {
|
||||
const intl = useIntl();
|
||||
const toggle = () => props.setShippingIsSame(!props.shippingIsSame);
|
||||
|
||||
return (
|
||||
<div className='shipping-address-section'>
|
||||
<input
|
||||
id='address-same-than-billing-address'
|
||||
className='Form-checkbox-input'
|
||||
name='terms'
|
||||
type='checkbox'
|
||||
checked={props.shippingIsSame}
|
||||
onChange={toggle}
|
||||
/>
|
||||
<span className='Form-checkbox-label'>
|
||||
<button
|
||||
onClick={toggle}
|
||||
type='button'
|
||||
className='no-style'
|
||||
>
|
||||
<span className='billing_address_btn_text'>
|
||||
{intl.formatMessage({
|
||||
id: 'admin.billing.subscription.complianceScreenShippingSameAsBilling',
|
||||
defaultMessage:
|
||||
'My shipping address is the same as my billing address',
|
||||
})}
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
$dropdown_input_index: 999999;
|
||||
|
||||
.DropdownInput {
|
||||
z-index: 999999;
|
||||
z-index: $dropdown_input_index;
|
||||
|
||||
&.Input_container {
|
||||
margin-top: 20px;
|
||||
@@ -37,7 +39,7 @@
|
||||
}
|
||||
|
||||
.DropdownInput__option > div {
|
||||
z-index: 999999;
|
||||
z-index: $dropdown_input_index;
|
||||
padding: 10px 24px;
|
||||
cursor: pointer;
|
||||
line-height: 16px;
|
||||
@@ -51,3 +53,33 @@
|
||||
.DropdownInput__option.focused > div {
|
||||
background-color: rgba(var(--center-channel-color-rgb), 0.08);
|
||||
}
|
||||
|
||||
.second-dropdown-sibling-wrapper {
|
||||
.DropdownInput {
|
||||
z-index: $dropdown_input_index - 1;
|
||||
}
|
||||
|
||||
.DropdownInput__option > div {
|
||||
z-index: $dropdown_input_index - 1;
|
||||
}
|
||||
}
|
||||
|
||||
.third-dropdown-sibling-wrapper {
|
||||
.DropdownInput {
|
||||
z-index: $dropdown_input_index - 2;
|
||||
}
|
||||
|
||||
.DropdownInput__option > div {
|
||||
z-index: $dropdown_input_index - 2;
|
||||
}
|
||||
}
|
||||
|
||||
.fourth-dropdown-sibling-wrapper {
|
||||
.DropdownInput {
|
||||
z-index: $dropdown_input_index - 3;
|
||||
}
|
||||
|
||||
.DropdownInput__option > div {
|
||||
z-index: $dropdown_input_index - 3;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,25 +61,27 @@ const AddressForm = (props: AddressFormProps) => {
|
||||
{...props.title}
|
||||
/>
|
||||
</div>
|
||||
<DropdownInput
|
||||
onChange={handleCountryChange}
|
||||
value={
|
||||
props.address.country ? {value: props.address.country, label: props.address.country} : undefined
|
||||
}
|
||||
options={COUNTRIES.map((country) => ({
|
||||
value: country.name,
|
||||
label: country.name,
|
||||
}))}
|
||||
legend={formatMessage({
|
||||
id: 'payment_form.country',
|
||||
defaultMessage: 'Country',
|
||||
})}
|
||||
placeholder={formatMessage({
|
||||
id: 'payment_form.country',
|
||||
defaultMessage: 'Country',
|
||||
})}
|
||||
name={'billing_dropdown'}
|
||||
/>
|
||||
<div className='third-dropdown-sibling-wrapper'>
|
||||
<DropdownInput
|
||||
onChange={handleCountryChange}
|
||||
value={
|
||||
props.address.country ? {value: props.address.country, label: props.address.country} : undefined
|
||||
}
|
||||
options={COUNTRIES.map((country) => ({
|
||||
value: country.name,
|
||||
label: country.name,
|
||||
}))}
|
||||
legend={formatMessage({
|
||||
id: 'payment_form.country',
|
||||
defaultMessage: 'Country',
|
||||
})}
|
||||
placeholder={formatMessage({
|
||||
id: 'payment_form.country',
|
||||
defaultMessage: 'Country',
|
||||
})}
|
||||
name={'billing_dropdown'}
|
||||
/>
|
||||
</div>
|
||||
<div className='form-row'>
|
||||
<Input
|
||||
name='address'
|
||||
@@ -122,7 +124,7 @@ const AddressForm = (props: AddressFormProps) => {
|
||||
/>
|
||||
</div>
|
||||
<div className='form-row'>
|
||||
<div className='form-row-third-1 selector'>
|
||||
<div className='form-row-third-1 selector fourth-dropdown-sibling-wrapper'>
|
||||
<StateSelector
|
||||
country={props.address.country}
|
||||
state={props.address.state}
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
.form-row-third-1 {
|
||||
.DropdownInput {
|
||||
z-index: 99999;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
@@ -37,7 +36,6 @@
|
||||
|
||||
.DropdownInput {
|
||||
position: relative;
|
||||
z-index: 999999;
|
||||
height: 36px;
|
||||
margin-bottom: 24px;
|
||||
font-weight: normal;
|
||||
|
||||
@@ -251,7 +251,7 @@ export default class PaymentForm extends React.PureComponent<Props, State> {
|
||||
/>
|
||||
</div>
|
||||
<div className='form-row'>
|
||||
<div className='form-row-third-1 selector'>
|
||||
<div className='form-row-third-1 selector second-dropdown-sibling-wrapper'>
|
||||
<StateSelector
|
||||
country={this.state.country}
|
||||
state={this.state.state}
|
||||
|
||||
@@ -2,42 +2,8 @@
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
|
||||
.shipping-address-section {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
padding: 0 96px;
|
||||
padding-bottom: 28px;
|
||||
font-weight: normal;
|
||||
|
||||
button.no-style {
|
||||
padding-left: 0;
|
||||
background: transparent;
|
||||
outline: unset;
|
||||
|
||||
&:focus {
|
||||
outline: unset;
|
||||
}
|
||||
}
|
||||
|
||||
#address-same-than-billing-address {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.Form-checkbox-label {
|
||||
padding-left: 12px;
|
||||
cursor: default;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.billing_address_btn_text {
|
||||
color: var(--center-channel-color);
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-weight: normal;
|
||||
}
|
||||
& &__purchase-body {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
>div {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import React, {ReactNode} from 'react';
|
||||
import {FormattedMessage, injectIntl, IntlShape} from 'react-intl';
|
||||
|
||||
import classnames from 'classnames';
|
||||
import {Stripe, StripeCardElementChangeEvent} from '@stripe/stripe-js';
|
||||
import {loadStripe} from '@stripe/stripe-js/pure'; // https://github.com/stripe/stripe-js#importing-loadstripe-without-side-effects
|
||||
import {Elements} from '@stripe/react-stripe-js';
|
||||
@@ -812,7 +813,7 @@ class PurchaseModal extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={this.state.processing ? 'processing' : ''}>
|
||||
<div className={classnames('PurchaseModal__purchase-body', {processing: this.state.processing})}>
|
||||
<div className='LHS'>
|
||||
<h2 className='title'>{title}</h2>
|
||||
<UpgradeSvg
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import {COUNTRIES} from 'utils/countries';
|
||||
|
||||
import DropdownInput from 'components/dropdown_input';
|
||||
import Input from 'components/widgets/inputs/input/input';
|
||||
import StateSelector from 'components/payment_form/state_selector';
|
||||
|
||||
interface Props {
|
||||
type: 'shipping' | 'billing';
|
||||
testPrefix?: string;
|
||||
|
||||
country: string;
|
||||
changeCountry: (option: {value: string}) => void;
|
||||
|
||||
address: string;
|
||||
changeAddress: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
|
||||
address2: string;
|
||||
changeAddress2: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
|
||||
city: string;
|
||||
changeCity: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
|
||||
state: string;
|
||||
changeState: (postalCode: string) => void;
|
||||
|
||||
postalCode: string;
|
||||
changePostalCode: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
}
|
||||
export default function Address(props: Props) {
|
||||
const testPrefix = props.testPrefix || 'selfHostedPurchase';
|
||||
const intl = useIntl();
|
||||
let countrySelectorId = `${testPrefix}CountrySelector`;
|
||||
let stateSelectorId = `${testPrefix}StateSelector`;
|
||||
if (props.type === 'shipping') {
|
||||
countrySelectorId += '_Shipping';
|
||||
stateSelectorId += '_Shipping';
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className={classNames({'third-dropdown-sibling-wrapper': props.type === 'shipping'})}>
|
||||
<DropdownInput
|
||||
testId={countrySelectorId}
|
||||
onChange={props.changeCountry}
|
||||
value={
|
||||
props.country ? {value: props.country, label: props.country} : undefined
|
||||
}
|
||||
options={COUNTRIES.map((country) => ({
|
||||
value: country.name,
|
||||
label: country.name,
|
||||
}))}
|
||||
legend={intl.formatMessage({
|
||||
id: 'payment_form.country',
|
||||
defaultMessage: 'Country',
|
||||
})}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'payment_form.country',
|
||||
defaultMessage: 'Country',
|
||||
})}
|
||||
name={'billing_dropdown'}
|
||||
/>
|
||||
</div>
|
||||
<div className='form-row'>
|
||||
<Input
|
||||
name='address'
|
||||
type='text'
|
||||
value={props.address}
|
||||
onChange={props.changeAddress}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'payment_form.address',
|
||||
defaultMessage: 'Address',
|
||||
})}
|
||||
required={true}
|
||||
/>
|
||||
</div>
|
||||
<div className='form-row'>
|
||||
<Input
|
||||
name='address2'
|
||||
type='text'
|
||||
value={props.address2}
|
||||
onChange={props.changeAddress2}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'payment_form.address_2',
|
||||
defaultMessage: 'Address 2',
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
<div className='form-row'>
|
||||
<Input
|
||||
name='city'
|
||||
type='text'
|
||||
value={props.city}
|
||||
onChange={props.changeCity}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'payment_form.city',
|
||||
defaultMessage: 'City',
|
||||
})}
|
||||
required={true}
|
||||
/>
|
||||
</div>
|
||||
<div className='form-row'>
|
||||
<div className={classNames('form-row-third-1', {'second-dropdown-sibling-wrapper': props.type === 'billing', 'fourth-dropdown-sibling-wrapper': props.type === 'shipping'})}>
|
||||
<StateSelector
|
||||
testId={stateSelectorId}
|
||||
country={props.country}
|
||||
state={props.state}
|
||||
onChange={props.changeState}
|
||||
/>
|
||||
</div>
|
||||
<div className='form-row-third-2'>
|
||||
<Input
|
||||
name='postalCode'
|
||||
type='text'
|
||||
value={props.postalCode}
|
||||
onChange={props.changePostalCode}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'payment_form.zipcode',
|
||||
defaultMessage: 'Zip/Postal Code',
|
||||
})}
|
||||
required={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -310,6 +310,15 @@ describe('SelfHostedPurchaseModal :: canSubmit', () => {
|
||||
state: 'string',
|
||||
country: 'string',
|
||||
postalCode: '12345',
|
||||
|
||||
shippingSame: true,
|
||||
shippingAddress: '',
|
||||
shippingAddress2: '',
|
||||
shippingCity: '',
|
||||
shippingState: '',
|
||||
shippingCountry: '',
|
||||
shippingPostalCode: '',
|
||||
|
||||
cardName: 'string',
|
||||
organization: 'string',
|
||||
agreedTerms: true,
|
||||
@@ -361,6 +370,21 @@ describe('SelfHostedPurchaseModal :: canSubmit', () => {
|
||||
expect(canSubmit(state, SelfHostedSignupProgress.CREATED_CUSTOMER)).toBe(false);
|
||||
expect(canSubmit(state, SelfHostedSignupProgress.CREATED_INTENT)).toBe(false);
|
||||
});
|
||||
|
||||
it('if shipping address different and is not filled, can not submit', () => {
|
||||
const state = makeHappyPathState();
|
||||
state.shippingSame = false;
|
||||
expect(canSubmit(state, SelfHostedSignupProgress.START)).toBe(false);
|
||||
|
||||
state.shippingAddress = 'more shipping info';
|
||||
state.shippingAddress2 = 'more shipping info';
|
||||
state.shippingCity = 'more shipping info';
|
||||
state.shippingState = 'more shipping info';
|
||||
state.shippingCountry = 'more shipping info';
|
||||
state.shippingPostalCode = 'more shipping info';
|
||||
expect(canSubmit(state, SelfHostedSignupProgress.START)).toBe(true);
|
||||
});
|
||||
|
||||
it('if card number missing and card has not been confirmed, can not submit', () => {
|
||||
const state = makeHappyPathState();
|
||||
state.cardFilled = false;
|
||||
|
||||
@@ -26,8 +26,6 @@ import {GlobalState} from 'types/store';
|
||||
import {isModalOpen} from 'selectors/views/modals';
|
||||
import {isDevModeEnabled} from 'selectors/general';
|
||||
|
||||
import {COUNTRIES} from 'utils/countries';
|
||||
|
||||
import {
|
||||
ModalIdentifiers,
|
||||
StatTypes,
|
||||
@@ -35,8 +33,6 @@ import {
|
||||
} from 'utils/constants';
|
||||
|
||||
import CardInput, {CardInputType} from 'components/payment_form/card_input';
|
||||
import StateSelector from 'components/payment_form/state_selector';
|
||||
import DropdownInput from 'components/dropdown_input';
|
||||
import BackgroundSvg from 'components/common/svg_images_components/background_svg';
|
||||
import UpgradeSvg from 'components/common/svg_images_components/upgrade_svg';
|
||||
|
||||
@@ -47,6 +43,7 @@ import RootPortal from 'components/root_portal';
|
||||
import useLoadStripe from 'components/common/hooks/useLoadStripe';
|
||||
import useControlSelfHostedPurchaseModal from 'components/common/hooks/useControlSelfHostedPurchaseModal';
|
||||
import useFetchStandardAnalytics from 'components/common/hooks/useFetchStandardAnalytics';
|
||||
import ChooseDifferentShipping from 'components/choose_different_shipping';
|
||||
|
||||
import {ValueOf} from '@mattermost/types/utilities';
|
||||
import {UserProfile} from '@mattermost/types/users';
|
||||
@@ -64,6 +61,7 @@ import SuccessPage from './success_page';
|
||||
import SelfHostedCard from './self_hosted_card';
|
||||
import StripeProvider from './stripe_provider';
|
||||
import Terms from './terms';
|
||||
import Address from './address';
|
||||
import useNoEscape from './useNoEscape';
|
||||
|
||||
import {SetPrefix, UnionSetActions} from './types';
|
||||
@@ -73,12 +71,24 @@ import './self_hosted_purchase_modal.scss';
|
||||
import {STORAGE_KEY_PURCHASE_IN_PROGRESS} from './constants';
|
||||
|
||||
export interface State {
|
||||
|
||||
// billing address
|
||||
address: string;
|
||||
address2: string;
|
||||
city: string;
|
||||
state: string;
|
||||
country: string;
|
||||
postalCode: string;
|
||||
|
||||
// shipping address
|
||||
shippingSame: boolean;
|
||||
shippingAddress: string;
|
||||
shippingAddress2: string;
|
||||
shippingCity: string;
|
||||
shippingState: string;
|
||||
shippingCountry: string;
|
||||
shippingPostalCode: string;
|
||||
|
||||
cardName: string;
|
||||
organization: string;
|
||||
agreedTerms: boolean;
|
||||
@@ -113,6 +123,15 @@ export function makeInitialState(): State {
|
||||
state: '',
|
||||
country: '',
|
||||
postalCode: '',
|
||||
|
||||
shippingSame: true,
|
||||
shippingAddress: '',
|
||||
shippingAddress2: '',
|
||||
shippingCity: '',
|
||||
shippingState: '',
|
||||
shippingCountry: '',
|
||||
shippingPostalCode: '',
|
||||
|
||||
cardName: '',
|
||||
organization: '',
|
||||
agreedTerms: false,
|
||||
@@ -170,8 +189,18 @@ const simpleSetters: Array<Extract<keyof State, string>> = [
|
||||
'address2',
|
||||
'city',
|
||||
'country',
|
||||
'postalCode',
|
||||
'state',
|
||||
'postalCode',
|
||||
|
||||
// shipping address
|
||||
'shippingSame',
|
||||
'shippingAddress',
|
||||
'shippingAddress2',
|
||||
'shippingCity',
|
||||
'shippingState',
|
||||
'shippingCountry',
|
||||
'shippingPostalCode',
|
||||
|
||||
'agreedTerms',
|
||||
'cardFilled',
|
||||
'cardName',
|
||||
@@ -220,7 +249,7 @@ export function canSubmit(state: State, progress: ValueOf<typeof SelfHostedSignu
|
||||
return false;
|
||||
}
|
||||
|
||||
const validAddress = Boolean(
|
||||
let validAddress = Boolean(
|
||||
state.organization &&
|
||||
state.address &&
|
||||
state.city &&
|
||||
@@ -228,6 +257,16 @@ export function canSubmit(state: State, progress: ValueOf<typeof SelfHostedSignu
|
||||
state.postalCode &&
|
||||
state.country,
|
||||
);
|
||||
if (!state.shippingSame) {
|
||||
validAddress = validAddress && Boolean(
|
||||
state.shippingAddress &&
|
||||
state.shippingCity &&
|
||||
state.shippingState &&
|
||||
state.shippingPostalCode &&
|
||||
state.shippingCountry,
|
||||
|
||||
);
|
||||
}
|
||||
const validCard = Boolean(
|
||||
state.cardName &&
|
||||
state.cardFilled,
|
||||
@@ -366,16 +405,25 @@ export default function SelfHostedPurchaseModal(props: Props) {
|
||||
try {
|
||||
const [firstName, lastName] = inferNames(user, state.cardName);
|
||||
|
||||
const billingAddress = {
|
||||
city: state.city,
|
||||
country: state.country,
|
||||
line1: state.address,
|
||||
line2: state.address2,
|
||||
postal_code: state.postalCode,
|
||||
state: state.state,
|
||||
};
|
||||
signupCustomerResult = await Client4.createCustomerSelfHostedSignup({
|
||||
first_name: firstName,
|
||||
last_name: lastName,
|
||||
billing_address: {
|
||||
city: state.city,
|
||||
country: state.country,
|
||||
line1: state.address,
|
||||
line2: state.address2,
|
||||
postal_code: state.postalCode,
|
||||
state: state.state,
|
||||
billing_address: billingAddress,
|
||||
shipping_address: state.shippingSame ? billingAddress : {
|
||||
city: state.shippingCity,
|
||||
country: state.shippingCountry,
|
||||
line1: state.shippingAddress,
|
||||
line2: state.shippingAddress2,
|
||||
postal_code: state.shippingPostalCode,
|
||||
state: state.shippingState,
|
||||
},
|
||||
organization: state.organization,
|
||||
});
|
||||
@@ -586,99 +634,76 @@ export default function SelfHostedPurchaseModal(props: Props) {
|
||||
defaultMessage='Billing address'
|
||||
/>
|
||||
</div>
|
||||
<DropdownInput
|
||||
testId='selfHostedPurchaseCountrySelector'
|
||||
onChange={(option: {value: string}) => {
|
||||
<Address
|
||||
type='billing'
|
||||
country={state.country}
|
||||
changeCountry={(option) => {
|
||||
dispatch({type: 'set_country', data: option.value});
|
||||
}}
|
||||
value={
|
||||
state.country ? {value: state.country, label: state.country} : undefined
|
||||
}
|
||||
options={COUNTRIES.map((country) => ({
|
||||
value: country.name,
|
||||
label: country.name,
|
||||
}))}
|
||||
legend={intl.formatMessage({
|
||||
id: 'payment_form.country',
|
||||
defaultMessage: 'Country',
|
||||
})}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'payment_form.country',
|
||||
defaultMessage: 'Country',
|
||||
})}
|
||||
name={'billing_dropdown'}
|
||||
address={state.address}
|
||||
changeAddress={(e) => {
|
||||
dispatch({type: 'set_address', data: e.target.value});
|
||||
}}
|
||||
address2={state.address2}
|
||||
changeAddress2={(e) => {
|
||||
dispatch({type: 'set_address2', data: e.target.value});
|
||||
}}
|
||||
city={state.city}
|
||||
changeCity={(e) => {
|
||||
dispatch({type: 'set_city', data: e.target.value});
|
||||
}}
|
||||
state={state.state}
|
||||
changeState={(state: string) => {
|
||||
dispatch({type: 'set_state', data: state});
|
||||
}}
|
||||
postalCode={state.postalCode}
|
||||
changePostalCode={(e) => {
|
||||
dispatch({type: 'set_postalCode', data: e.target.value});
|
||||
}}
|
||||
/>
|
||||
<div className='form-row'>
|
||||
<Input
|
||||
name='address'
|
||||
type='text'
|
||||
value={state.address}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch({type: 'set_address', data: e.target.value});
|
||||
}}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'payment_form.address',
|
||||
defaultMessage: 'Address',
|
||||
})}
|
||||
required={true}
|
||||
/>
|
||||
</div>
|
||||
<div className='form-row'>
|
||||
<Input
|
||||
name='address2'
|
||||
type='text'
|
||||
value={state.address2}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch({type: 'set_address2', data: e.target.value});
|
||||
}}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'payment_form.address_2',
|
||||
defaultMessage: 'Address 2',
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
<div className='form-row'>
|
||||
<Input
|
||||
name='city'
|
||||
type='text'
|
||||
value={state.city}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch({type: 'set_city', data: e.target.value});
|
||||
}}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'payment_form.city',
|
||||
defaultMessage: 'City',
|
||||
})}
|
||||
required={true}
|
||||
/>
|
||||
</div>
|
||||
<div className='form-row'>
|
||||
<div className='form-row-third-1'>
|
||||
<StateSelector
|
||||
testId='selfHostedPurchaseStateSelector'
|
||||
country={state.country}
|
||||
state={state.state}
|
||||
onChange={(state: string) => {
|
||||
dispatch({type: 'set_state', data: state});
|
||||
<ChooseDifferentShipping
|
||||
shippingIsSame={state.shippingSame}
|
||||
setShippingIsSame={(val: boolean) => {
|
||||
dispatch({type: 'set_shippingSame', data: val});
|
||||
}}
|
||||
/>
|
||||
{!state.shippingSame && (
|
||||
<>
|
||||
<div className='section-title'>
|
||||
<FormattedMessage
|
||||
id='payment_form.shipping_address'
|
||||
defaultMessage='Shipping Address'
|
||||
/>
|
||||
</div>
|
||||
<Address
|
||||
type='shipping'
|
||||
country={state.shippingCountry}
|
||||
changeCountry={(option) => {
|
||||
dispatch({type: 'set_shippingCountry', data: option.value});
|
||||
}}
|
||||
address={state.shippingAddress}
|
||||
changeAddress={(e) => {
|
||||
dispatch({type: 'set_shippingAddress', data: e.target.value});
|
||||
}}
|
||||
address2={state.shippingAddress2}
|
||||
changeAddress2={(e) => {
|
||||
dispatch({type: 'set_shippingAddress2', data: e.target.value});
|
||||
}}
|
||||
city={state.shippingCity}
|
||||
changeCity={(e) => {
|
||||
dispatch({type: 'set_shippingCity', data: e.target.value});
|
||||
}}
|
||||
state={state.shippingState}
|
||||
changeState={(state: string) => {
|
||||
dispatch({type: 'set_shippingState', data: state});
|
||||
}}
|
||||
postalCode={state.shippingPostalCode}
|
||||
changePostalCode={(e) => {
|
||||
dispatch({type: 'set_shippingPostalCode', data: e.target.value});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className='form-row-third-2'>
|
||||
<Input
|
||||
name='postalCode'
|
||||
type='text'
|
||||
value={state.postalCode}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch({type: 'set_postalCode', data: e.target.value});
|
||||
}}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'payment_form.zipcode',
|
||||
defaultMessage: 'Zip/Postal Code',
|
||||
})}
|
||||
required={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<Terms
|
||||
agreed={state.agreedTerms}
|
||||
setAgreed={(data: boolean) => {
|
||||
|
||||
@@ -4,19 +4,20 @@
|
||||
|
||||
.form-view {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex-direction: row;
|
||||
flex-grow: 1;
|
||||
flex-wrap: wrap;
|
||||
align-content: top;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
padding: 77px 107px;
|
||||
color: var(--center-channel-color);
|
||||
font-family: "Open Sans";
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
|
||||
.title {
|
||||
font-size: 22px;
|
||||
@@ -39,14 +40,12 @@
|
||||
margin-right: 16px;
|
||||
|
||||
.DropdownInput {
|
||||
z-index: 99999;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.DropdownInput {
|
||||
position: relative;
|
||||
z-index: 999999;
|
||||
height: 36px;
|
||||
margin-bottom: 24px;
|
||||
|
||||
@@ -517,6 +516,9 @@
|
||||
}
|
||||
|
||||
input[type=checkbox] {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
flex-shrink: 0;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
|
||||
@@ -4378,6 +4378,7 @@
|
||||
"payment_form.no_billing_address": "No billing address added",
|
||||
"payment_form.no_credit_card": "No credit card added",
|
||||
"payment_form.saved_payment_method": "Saved Payment Method",
|
||||
"payment_form.shipping_address": "Shipping Address",
|
||||
"payment_form.zipcode": "Zip/Postal Code",
|
||||
"payment.card_number": "Card Number",
|
||||
"payment.field_required": "This field is required",
|
||||
|
||||
@@ -18,6 +18,7 @@ export interface SelfHostedSignupForm {
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
billing_address: Address;
|
||||
shipping_address: Address;
|
||||
organization: string;
|
||||
}
|
||||
|
||||
|
||||
Ссылка в новой задаче
Block a user