mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-29 05:00:43 +10:00
Merge pull request #524 from sharetribe/tos-page
Terms of Service and Privacy Policy
This commit is contained in:
commit
49c922f9e0
32 changed files with 932 additions and 357 deletions
|
|
@ -15,3 +15,4 @@ Documentation for specific topics can be found in the following files:
|
|||
- [CI](ci.md)
|
||||
- [Static pages](static-pages.md)
|
||||
- [Analytics](analytics.md)
|
||||
- [Terms of Service and Privacy Policy](terms-of-service-and-privacy-policy.md)
|
||||
|
|
|
|||
16
docs/terms-of-service-and-privacy-policy.md
Normal file
16
docs/terms-of-service-and-privacy-policy.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# Terms of Service and Privacy Policy
|
||||
|
||||
The Terms of Service and Privacy Policy of a marketplace are static
|
||||
components that can be edited to suit the marketplace.
|
||||
|
||||
## Terms of Service
|
||||
|
||||
Edit
|
||||
[src/components/TermsOfService/TermsOfService.js](../src/components/TermsOfService/TermsOfService.js)
|
||||
file to customise the default dummy content.
|
||||
|
||||
## Privacy Policy
|
||||
|
||||
Edit
|
||||
[src/components/PrivacyPolicy/PrivacyPolicy.js](../src/components/PrivacyPolicy/PrivacyPolicy.js)
|
||||
file to customise the default dummy content.
|
||||
|
|
@ -240,12 +240,12 @@ const Footer = props => {
|
|||
<div className={css.legalMatters}>
|
||||
<ul className={css.tosAndPrivacy}>
|
||||
<li>
|
||||
<NamedLink name="LandingPage" className={css.legalLink}>
|
||||
<NamedLink name="TermsOfServicePage" className={css.legalLink}>
|
||||
<FormattedMessage id="Footer.termsOfUse" />
|
||||
</NamedLink>
|
||||
</li>
|
||||
<li>
|
||||
<NamedLink name="LandingPage" className={css.legalLink}>
|
||||
<NamedLink name="PrivacyPolicyPage" className={css.legalLink}>
|
||||
<FormattedMessage id="Footer.privacyPolicy" />
|
||||
</NamedLink>
|
||||
</li>
|
||||
|
|
@ -261,10 +261,10 @@ const Footer = props => {
|
|||
<FormattedMessage id="Footer.copyright" />
|
||||
</ExternalLink>
|
||||
<div className={css.tosAndPrivacyMobile}>
|
||||
<NamedLink name="LandingPage" className={css.privacy}>
|
||||
<NamedLink name="PrivacyPolicyPage" className={css.privacy}>
|
||||
<FormattedMessage id="Footer.privacy" />
|
||||
</NamedLink>
|
||||
<NamedLink name="LandingPage" className={css.terms}>
|
||||
<NamedLink name="TermsOfServicePage" className={css.terms}>
|
||||
<FormattedMessage id="Footer.terms" />
|
||||
</NamedLink>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -26,3 +26,38 @@
|
|||
padding-left: 25vw;
|
||||
}
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow-x: auto;
|
||||
min-height: 48px;
|
||||
padding-top: 2px;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
min-height: 56px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
@media (--viewportLarge) {
|
||||
min-height: auto;
|
||||
flex-direction: column;
|
||||
margin-top: 28px;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.tab {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
height: 100%;
|
||||
margin-left: 16px;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
@media (--viewportLarge) {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,26 +5,35 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { TabNav } from '../../components';
|
||||
|
||||
import css from './LayoutWrapperSideNav.css';
|
||||
|
||||
const LayoutWrapperSideNav = props => {
|
||||
const { className, rootClassName, children } = props;
|
||||
const { className, rootClassName, children, tabs } = props;
|
||||
const classes = classNames(rootClassName || css.root, className);
|
||||
return <aside className={classes}>{children}</aside>;
|
||||
return (
|
||||
<aside className={classes}>
|
||||
{tabs ? <TabNav rootClassName={css.tabs} tabRootClassName={css.tab} tabs={tabs} /> : null}
|
||||
{children}
|
||||
</aside>
|
||||
);
|
||||
};
|
||||
|
||||
LayoutWrapperSideNav.defaultProps = {
|
||||
className: null,
|
||||
rootClassName: null,
|
||||
children: null,
|
||||
tabs: null,
|
||||
};
|
||||
|
||||
const { node, string } = PropTypes;
|
||||
const { node, string, array } = PropTypes;
|
||||
|
||||
LayoutWrapperSideNav.propTypes = {
|
||||
children: node.isRequired,
|
||||
children: node,
|
||||
className: string,
|
||||
rootClassName: string,
|
||||
tabs: array,
|
||||
};
|
||||
|
||||
export default LayoutWrapperSideNav;
|
||||
|
|
|
|||
26
src/components/PrivacyPolicy/PrivacyPolicy.css
Normal file
26
src/components/PrivacyPolicy/PrivacyPolicy.css
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
@import '../../marketplace.css';
|
||||
|
||||
.root {
|
||||
& p {
|
||||
@apply --marketplaceH4FontStyles;
|
||||
}
|
||||
& h2 {
|
||||
/* Adjust heading margins to work with the reduced body font size */
|
||||
margin: 23px 0 19px 0;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
margin: 24px 0 24px 0;
|
||||
}
|
||||
}
|
||||
|
||||
& .lastUpdated {
|
||||
@apply --marketplaceBodyFontStyles;
|
||||
margin-top: 0;
|
||||
margin-bottom: 55px;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
margin-top: 0;
|
||||
margin-bottom: 54px;
|
||||
}
|
||||
}
|
||||
}
|
||||
73
src/components/PrivacyPolicy/PrivacyPolicy.js
Normal file
73
src/components/PrivacyPolicy/PrivacyPolicy.js
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import css from './PrivacyPolicy.css';
|
||||
|
||||
const PrivacyPolicy = props => {
|
||||
const { rootClassName, className } = props;
|
||||
const classes = classNames(rootClassName || css.root, className);
|
||||
return (
|
||||
<div className={classes}>
|
||||
<p className={css.lastUpdated}>Last updated: October 30, 2017</p>
|
||||
|
||||
<p>
|
||||
Thank you for using Saunatime! Ut enim ad minim veniam, quis nostrud exercitation ullamco
|
||||
laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in
|
||||
voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
|
||||
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
</p>
|
||||
|
||||
<h2>1 Lorem ipsum dolor sit amet</h2>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
|
||||
laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in
|
||||
voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
|
||||
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
</p>
|
||||
|
||||
<h2>2 Sed ut perspiciatis unde</h2>
|
||||
<p>
|
||||
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque
|
||||
laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi
|
||||
architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit
|
||||
aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione
|
||||
voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet,
|
||||
consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et
|
||||
dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum
|
||||
exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi
|
||||
consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil
|
||||
molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?
|
||||
</p>
|
||||
|
||||
<h2>3 At vero eos et accusamus</h2>
|
||||
<p>
|
||||
At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium
|
||||
voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati
|
||||
cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id
|
||||
est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam
|
||||
libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod
|
||||
maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus.
|
||||
Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut
|
||||
et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a
|
||||
sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis
|
||||
doloribus asperiores repellat
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
PrivacyPolicy.defaultProps = {
|
||||
rootClassName: null,
|
||||
className: null,
|
||||
};
|
||||
|
||||
const { string } = PropTypes;
|
||||
|
||||
PrivacyPolicy.propTypes = {
|
||||
rootClassName: string,
|
||||
className: string,
|
||||
};
|
||||
|
||||
export default PrivacyPolicy;
|
||||
26
src/components/TermsOfService/TermsOfService.css
Normal file
26
src/components/TermsOfService/TermsOfService.css
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
@import '../../marketplace.css';
|
||||
|
||||
.root {
|
||||
& p {
|
||||
@apply --marketplaceH4FontStyles;
|
||||
}
|
||||
& h2 {
|
||||
/* Adjust heading margins to work with the reduced body font size */
|
||||
margin: 23px 0 19px 0;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
margin: 24px 0 24px 0;
|
||||
}
|
||||
}
|
||||
|
||||
& .lastUpdated {
|
||||
@apply --marketplaceBodyFontStyles;
|
||||
margin-top: 0;
|
||||
margin-bottom: 55px;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
margin-top: 0;
|
||||
margin-bottom: 54px;
|
||||
}
|
||||
}
|
||||
}
|
||||
73
src/components/TermsOfService/TermsOfService.js
Normal file
73
src/components/TermsOfService/TermsOfService.js
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import css from './TermsOfService.css';
|
||||
|
||||
const TermsOfService = props => {
|
||||
const { rootClassName, className } = props;
|
||||
const classes = classNames(rootClassName || css.root, className);
|
||||
return (
|
||||
<div className={classes}>
|
||||
<p className={css.lastUpdated}>Last updated: October 30, 2017</p>
|
||||
|
||||
<p>
|
||||
Thank you for using Saunatime! Ut enim ad minim veniam, quis nostrud exercitation ullamco
|
||||
laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in
|
||||
voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
|
||||
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
</p>
|
||||
|
||||
<h2>1 Lorem ipsum dolor sit amet</h2>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
|
||||
laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in
|
||||
voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
|
||||
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
</p>
|
||||
|
||||
<h2>2 Sed ut perspiciatis unde</h2>
|
||||
<p>
|
||||
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque
|
||||
laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi
|
||||
architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit
|
||||
aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione
|
||||
voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet,
|
||||
consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et
|
||||
dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum
|
||||
exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi
|
||||
consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil
|
||||
molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?
|
||||
</p>
|
||||
|
||||
<h2>3 At vero eos et accusamus</h2>
|
||||
<p>
|
||||
At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium
|
||||
voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati
|
||||
cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id
|
||||
est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam
|
||||
libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod
|
||||
maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus.
|
||||
Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut
|
||||
et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a
|
||||
sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis
|
||||
doloribus asperiores repellat
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
TermsOfService.defaultProps = {
|
||||
rootClassName: null,
|
||||
className: null,
|
||||
};
|
||||
|
||||
const { string } = PropTypes;
|
||||
|
||||
TermsOfService.propTypes = {
|
||||
rootClassName: string,
|
||||
className: string,
|
||||
};
|
||||
|
||||
export default TermsOfService;
|
||||
|
|
@ -76,6 +76,7 @@ export { default as OrderDetailsPanel } from './OrderDetailsPanel/OrderDetailsPa
|
|||
export { default as OrderDiscussionPanel } from './OrderDiscussionPanel/OrderDiscussionPanel';
|
||||
export { default as Page } from './Page/Page';
|
||||
export { default as PaginationLinks } from './PaginationLinks/PaginationLinks';
|
||||
export { default as PrivacyPolicy } from './PrivacyPolicy/PrivacyPolicy';
|
||||
export { default as Promised } from './Promised/Promised';
|
||||
export { default as ResponsiveImage } from './ResponsiveImage/ResponsiveImage';
|
||||
export { default as SaleDetailsPanel } from './SaleDetailsPanel/SaleDetailsPanel';
|
||||
|
|
@ -91,6 +92,7 @@ export {
|
|||
export { default as TabNav } from './TabNav/TabNav';
|
||||
export { default as TabNavHorizontal } from './TabNavHorizontal/TabNavHorizontal';
|
||||
export { default as Tabs } from './Tabs/Tabs';
|
||||
export { default as TermsOfService } from './TermsOfService/TermsOfService';
|
||||
export { default as TextInputField } from './TextInputField/TextInputField';
|
||||
export { default as Topbar } from './Topbar/Topbar';
|
||||
export { default as TopbarDesktop } from './TopbarDesktop/TopbarDesktop';
|
||||
|
|
|
|||
|
|
@ -50,6 +50,27 @@
|
|||
text-align: left;
|
||||
}
|
||||
|
||||
/* Terms of Service modal*/
|
||||
|
||||
.termsWrapper {
|
||||
width: 100%;
|
||||
padding-top: 40px;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
width: 571px;
|
||||
padding-top: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
.termsHeading {
|
||||
@apply --marketplaceH1FontStyles;
|
||||
margin: 0 0 19px 0;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
margin: 0 0 19px 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ================ Hide Top bar in screens smaller than 768px ================ */
|
||||
|
||||
.hideOnMobile {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { compose } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
|
|
@ -25,216 +25,244 @@ import {
|
|||
LayoutWrapperMain,
|
||||
LayoutWrapperFooter,
|
||||
Footer,
|
||||
Modal,
|
||||
TermsOfService,
|
||||
} from '../../components';
|
||||
import { LoginForm, SignupForm, TopbarContainer } from '../../containers';
|
||||
import { login, authenticationInProgress, signup } from '../../ducks/Auth.duck';
|
||||
import { isScrollingDisabled } from '../../ducks/UI.duck';
|
||||
import { sendVerificationEmail } from '../../ducks/user.duck';
|
||||
import { manageDisableScrolling } from '../../ducks/UI.duck';
|
||||
|
||||
import css from './AuthenticationPage.css';
|
||||
|
||||
export const AuthenticationPageComponent = props => {
|
||||
const {
|
||||
authInProgress,
|
||||
currentUser,
|
||||
intl,
|
||||
isAuthenticated,
|
||||
location,
|
||||
loginError,
|
||||
logoutError,
|
||||
scrollingDisabled,
|
||||
signupError,
|
||||
submitLogin,
|
||||
submitSignup,
|
||||
tab,
|
||||
sendVerificationEmailInProgress,
|
||||
sendVerificationEmailError,
|
||||
onResendVerificationEmail,
|
||||
} = props;
|
||||
const isLogin = tab === 'login';
|
||||
const from = location.state && location.state.from ? location.state.from : null;
|
||||
|
||||
const user = ensureCurrentUser(currentUser);
|
||||
const currentUserLoaded = !!user.id;
|
||||
|
||||
// We only want to show the email verification dialog in the signup
|
||||
// tab if the user isn't being redirected somewhere else
|
||||
// (i.e. `from` is present). We must also check the `emailVerified`
|
||||
// flag only when the current user is fully loaded.
|
||||
const showEmailVerification = !isLogin && currentUserLoaded && !user.attributes.emailVerified;
|
||||
|
||||
// Already authenticated, redirect away from auth page
|
||||
if (isAuthenticated && from) {
|
||||
return <Redirect to={from} />;
|
||||
} else if (isAuthenticated && currentUserLoaded && !showEmailVerification) {
|
||||
return <NamedRedirect name="LandingPage" />;
|
||||
export class AuthenticationPageComponent extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { tosModalOpen: false };
|
||||
}
|
||||
render() {
|
||||
const {
|
||||
authInProgress,
|
||||
currentUser,
|
||||
intl,
|
||||
isAuthenticated,
|
||||
location,
|
||||
loginError,
|
||||
logoutError,
|
||||
scrollingDisabled,
|
||||
signupError,
|
||||
submitLogin,
|
||||
submitSignup,
|
||||
tab,
|
||||
sendVerificationEmailInProgress,
|
||||
sendVerificationEmailError,
|
||||
onResendVerificationEmail,
|
||||
onManageDisableScrolling,
|
||||
} = this.props;
|
||||
const isLogin = tab === 'login';
|
||||
const from = location.state && location.state.from ? location.state.from : null;
|
||||
|
||||
/* eslint-disable no-console */
|
||||
if (loginError && console && console.error) {
|
||||
console.error(loginError);
|
||||
}
|
||||
if (signupError && console && console.error) {
|
||||
console.error(signupError);
|
||||
}
|
||||
/* eslint-enable no-console */
|
||||
const user = ensureCurrentUser(currentUser);
|
||||
const currentUserLoaded = !!user.id;
|
||||
|
||||
const loginErrorMessage = (
|
||||
<div className={css.error}>
|
||||
<FormattedMessage id="AuthenticationPage.loginFailed" />
|
||||
</div>
|
||||
);
|
||||
// We only want to show the email verification dialog in the signup
|
||||
// tab if the user isn't being redirected somewhere else
|
||||
// (i.e. `from` is present). We must also check the `emailVerified`
|
||||
// flag only when the current user is fully loaded.
|
||||
const showEmailVerification = !isLogin && currentUserLoaded && !user.attributes.emailVerified;
|
||||
|
||||
const signupErrorMessage = (
|
||||
<div className={css.error}>
|
||||
{isSignupEmailTakenError(signupError) ? (
|
||||
<FormattedMessage id="AuthenticationPage.signupFailedEmailAlreadyTaken" />
|
||||
) : (
|
||||
<FormattedMessage id="AuthenticationPage.signupFailed" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
// Already authenticated, redirect away from auth page
|
||||
if (isAuthenticated && from) {
|
||||
return <Redirect to={from} />;
|
||||
} else if (isAuthenticated && currentUserLoaded && !showEmailVerification) {
|
||||
return <NamedRedirect name="LandingPage" />;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-confusing-arrow
|
||||
const errorMessage = (error, message) => (error ? message : null);
|
||||
const loginOrSignupError = isLogin
|
||||
? errorMessage(loginError, loginErrorMessage)
|
||||
: errorMessage(signupError, signupErrorMessage);
|
||||
/* eslint-disable no-console */
|
||||
if (loginError && console && console.error) {
|
||||
console.error(loginError);
|
||||
}
|
||||
if (signupError && console && console.error) {
|
||||
console.error(signupError);
|
||||
}
|
||||
/* eslint-enable no-console */
|
||||
|
||||
const fromState = { state: from ? { from } : null };
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
text: (
|
||||
<h1 className={css.tab}>
|
||||
<FormattedMessage id="AuthenticationPage.signupLinkText" />
|
||||
</h1>
|
||||
),
|
||||
selected: !isLogin,
|
||||
linkProps: {
|
||||
name: 'SignupPage',
|
||||
to: fromState,
|
||||
},
|
||||
},
|
||||
{
|
||||
text: (
|
||||
<h1 className={css.tab}>
|
||||
<FormattedMessage id="AuthenticationPage.loginLinkText" />
|
||||
</h1>
|
||||
),
|
||||
selected: isLogin,
|
||||
linkProps: {
|
||||
name: 'LoginPage',
|
||||
to: fromState,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const formContent = (
|
||||
<div className={css.content}>
|
||||
<TabNavHorizontal className={css.tabs} tabs={tabs} />
|
||||
{loginOrSignupError}
|
||||
{isLogin ? (
|
||||
<LoginForm className={css.form} onSubmit={submitLogin} inProgress={authInProgress} />
|
||||
) : (
|
||||
<SignupForm className={css.form} onSubmit={submitSignup} inProgress={authInProgress} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
const name = user.attributes.profile.firstName;
|
||||
const email = <span className={css.email}>{user.attributes.email}</span>;
|
||||
|
||||
const resendEmailLink = (
|
||||
<InlineTextButton className={css.modalHelperLink} onClick={onResendVerificationEmail}>
|
||||
<FormattedMessage id="AuthenticationPage.resendEmailLinkText" />
|
||||
</InlineTextButton>
|
||||
);
|
||||
const fixEmailLink = (
|
||||
<NamedLink className={css.modalHelperLink} name="ContactDetailsPage">
|
||||
<FormattedMessage id="AuthenticationPage.fixEmailLinkText" />
|
||||
</NamedLink>
|
||||
);
|
||||
|
||||
const resendErrorTranslationId = isTooManyEmailVerificationRequestsError(
|
||||
sendVerificationEmailError
|
||||
)
|
||||
? 'AuthenticationPage.resendFailedTooManyRequests'
|
||||
: 'AuthenticationPage.resendFailed';
|
||||
const resendErrorMessage = sendVerificationEmailError ? (
|
||||
<p className={css.error}>
|
||||
<FormattedMessage id={resendErrorTranslationId} />
|
||||
</p>
|
||||
) : null;
|
||||
|
||||
const emailVerificationContent = (
|
||||
<div className={css.content}>
|
||||
<NamedLink className={css.verifyClose} name="ProfileSettingsPage">
|
||||
<span className={css.closeText}>
|
||||
<FormattedMessage id="AuthenticationPage.verifyEmailClose" />
|
||||
</span>
|
||||
<IconClose rootClassName={css.closeIcon} />
|
||||
</NamedLink>
|
||||
<IconEmailSent className={css.modalIcon} />
|
||||
<h1 className={css.modalTitle}>
|
||||
<FormattedMessage id="AuthenticationPage.verifyEmailTitle" values={{ name }} />
|
||||
</h1>
|
||||
<p className={css.modalMessage}>
|
||||
<FormattedMessage id="AuthenticationPage.verifyEmailText" values={{ email }} />
|
||||
</p>
|
||||
{resendErrorMessage}
|
||||
|
||||
<div className={css.bottomWrapper}>
|
||||
<p className={css.modalHelperText}>
|
||||
{sendVerificationEmailInProgress ? (
|
||||
<FormattedMessage id="AuthenticationPage.sendingEmail" />
|
||||
) : (
|
||||
<FormattedMessage id="AuthenticationPage.resendEmail" values={{ resendEmailLink }} />
|
||||
)}
|
||||
</p>
|
||||
<p className={css.modalHelperText}>
|
||||
<FormattedMessage id="AuthenticationPage.fixEmail" values={{ fixEmailLink }} />
|
||||
</p>
|
||||
const loginErrorMessage = (
|
||||
<div className={css.error}>
|
||||
<FormattedMessage id="AuthenticationPage.loginFailed" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
|
||||
const siteTitle = config.siteTitle;
|
||||
const schemaTitle = isLogin
|
||||
? intl.formatMessage({ id: 'AuthenticationPage.schemaTitleLogin' }, { siteTitle })
|
||||
: intl.formatMessage({ id: 'AuthenticationPage.schemaTitleSignup' }, { siteTitle });
|
||||
const signupErrorMessage = (
|
||||
<div className={css.error}>
|
||||
{isSignupEmailTakenError(signupError) ? (
|
||||
<FormattedMessage id="AuthenticationPage.signupFailedEmailAlreadyTaken" />
|
||||
) : (
|
||||
<FormattedMessage id="AuthenticationPage.signupFailed" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
const topbarClasses = classNames({
|
||||
[css.hideOnMobile]: showEmailVerification,
|
||||
});
|
||||
// eslint-disable-next-line no-confusing-arrow
|
||||
const errorMessage = (error, message) => (error ? message : null);
|
||||
const loginOrSignupError = isLogin
|
||||
? errorMessage(loginError, loginErrorMessage)
|
||||
: errorMessage(signupError, signupErrorMessage);
|
||||
|
||||
return (
|
||||
<Page
|
||||
logoutError={logoutError}
|
||||
title={schemaTitle}
|
||||
scrollingDisabled={scrollingDisabled}
|
||||
schema={{
|
||||
'@context': 'http://schema.org',
|
||||
'@type': 'WebPage',
|
||||
name: schemaTitle,
|
||||
}}
|
||||
>
|
||||
<LayoutSingleColumn>
|
||||
<LayoutWrapperTopbar>
|
||||
<TopbarContainer className={topbarClasses} />
|
||||
</LayoutWrapperTopbar>
|
||||
<LayoutWrapperMain className={css.layoutWrapperMain}>
|
||||
<div className={css.root}>
|
||||
{showEmailVerification ? emailVerificationContent : formContent}
|
||||
</div>
|
||||
</LayoutWrapperMain>
|
||||
<LayoutWrapperFooter>
|
||||
<Footer />
|
||||
</LayoutWrapperFooter>
|
||||
</LayoutSingleColumn>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
const fromState = { state: from ? { from } : null };
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
text: (
|
||||
<h1 className={css.tab}>
|
||||
<FormattedMessage id="AuthenticationPage.signupLinkText" />
|
||||
</h1>
|
||||
),
|
||||
selected: !isLogin,
|
||||
linkProps: {
|
||||
name: 'SignupPage',
|
||||
to: fromState,
|
||||
},
|
||||
},
|
||||
{
|
||||
text: (
|
||||
<h1 className={css.tab}>
|
||||
<FormattedMessage id="AuthenticationPage.loginLinkText" />
|
||||
</h1>
|
||||
),
|
||||
selected: isLogin,
|
||||
linkProps: {
|
||||
name: 'LoginPage',
|
||||
to: fromState,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const formContent = (
|
||||
<div className={css.content}>
|
||||
<TabNavHorizontal className={css.tabs} tabs={tabs} />
|
||||
{loginOrSignupError}
|
||||
{isLogin ? (
|
||||
<LoginForm className={css.form} onSubmit={submitLogin} inProgress={authInProgress} />
|
||||
) : (
|
||||
<SignupForm
|
||||
className={css.form}
|
||||
onSubmit={submitSignup}
|
||||
inProgress={authInProgress}
|
||||
onOpenTermsOfService={() => this.setState({ tosModalOpen: true })}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
const name = user.attributes.profile.firstName;
|
||||
const email = <span className={css.email}>{user.attributes.email}</span>;
|
||||
|
||||
const resendEmailLink = (
|
||||
<InlineTextButton className={css.modalHelperLink} onClick={onResendVerificationEmail}>
|
||||
<FormattedMessage id="AuthenticationPage.resendEmailLinkText" />
|
||||
</InlineTextButton>
|
||||
);
|
||||
const fixEmailLink = (
|
||||
<NamedLink className={css.modalHelperLink} name="ContactDetailsPage">
|
||||
<FormattedMessage id="AuthenticationPage.fixEmailLinkText" />
|
||||
</NamedLink>
|
||||
);
|
||||
|
||||
const resendErrorTranslationId = isTooManyEmailVerificationRequestsError(
|
||||
sendVerificationEmailError
|
||||
)
|
||||
? 'AuthenticationPage.resendFailedTooManyRequests'
|
||||
: 'AuthenticationPage.resendFailed';
|
||||
const resendErrorMessage = sendVerificationEmailError ? (
|
||||
<p className={css.error}>
|
||||
<FormattedMessage id={resendErrorTranslationId} />
|
||||
</p>
|
||||
) : null;
|
||||
|
||||
const emailVerificationContent = (
|
||||
<div className={css.content}>
|
||||
<NamedLink className={css.verifyClose} name="ProfileSettingsPage">
|
||||
<span className={css.closeText}>
|
||||
<FormattedMessage id="AuthenticationPage.verifyEmailClose" />
|
||||
</span>
|
||||
<IconClose rootClassName={css.closeIcon} />
|
||||
</NamedLink>
|
||||
<IconEmailSent className={css.modalIcon} />
|
||||
<h1 className={css.modalTitle}>
|
||||
<FormattedMessage id="AuthenticationPage.verifyEmailTitle" values={{ name }} />
|
||||
</h1>
|
||||
<p className={css.modalMessage}>
|
||||
<FormattedMessage id="AuthenticationPage.verifyEmailText" values={{ email }} />
|
||||
</p>
|
||||
{resendErrorMessage}
|
||||
|
||||
<div className={css.bottomWrapper}>
|
||||
<p className={css.modalHelperText}>
|
||||
{sendVerificationEmailInProgress ? (
|
||||
<FormattedMessage id="AuthenticationPage.sendingEmail" />
|
||||
) : (
|
||||
<FormattedMessage id="AuthenticationPage.resendEmail" values={{ resendEmailLink }} />
|
||||
)}
|
||||
</p>
|
||||
<p className={css.modalHelperText}>
|
||||
<FormattedMessage id="AuthenticationPage.fixEmail" values={{ fixEmailLink }} />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const siteTitle = config.siteTitle;
|
||||
const schemaTitle = isLogin
|
||||
? intl.formatMessage({ id: 'AuthenticationPage.schemaTitleLogin' }, { siteTitle })
|
||||
: intl.formatMessage({ id: 'AuthenticationPage.schemaTitleSignup' }, { siteTitle });
|
||||
|
||||
const topbarClasses = classNames({
|
||||
[css.hideOnMobile]: showEmailVerification,
|
||||
});
|
||||
|
||||
return (
|
||||
<Page
|
||||
logoutError={logoutError}
|
||||
title={schemaTitle}
|
||||
scrollingDisabled={scrollingDisabled}
|
||||
schema={{
|
||||
'@context': 'http://schema.org',
|
||||
'@type': 'WebPage',
|
||||
name: schemaTitle,
|
||||
}}
|
||||
>
|
||||
<LayoutSingleColumn>
|
||||
<LayoutWrapperTopbar>
|
||||
<TopbarContainer className={topbarClasses} />
|
||||
</LayoutWrapperTopbar>
|
||||
<LayoutWrapperMain className={css.layoutWrapperMain}>
|
||||
<div className={css.root}>
|
||||
{showEmailVerification ? emailVerificationContent : formContent}
|
||||
</div>
|
||||
<Modal
|
||||
id="AuthenticationPage.tos"
|
||||
isOpen={this.state.tosModalOpen}
|
||||
onClose={() => this.setState({ tosModalOpen: false })}
|
||||
onManageDisableScrolling={onManageDisableScrolling}
|
||||
>
|
||||
<div className={css.termsWrapper}>
|
||||
<h2 className={css.termsHeading}>
|
||||
<FormattedMessage id="AuthenticationPage.termsHeading" />
|
||||
</h2>
|
||||
<TermsOfService />
|
||||
</div>
|
||||
</Modal>
|
||||
</LayoutWrapperMain>
|
||||
<LayoutWrapperFooter>
|
||||
<Footer />
|
||||
</LayoutWrapperFooter>
|
||||
</LayoutSingleColumn>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AuthenticationPageComponent.defaultProps = {
|
||||
currentUser: null,
|
||||
|
|
@ -262,6 +290,7 @@ AuthenticationPageComponent.propTypes = {
|
|||
sendVerificationEmailInProgress: bool.isRequired,
|
||||
sendVerificationEmailError: propTypes.error,
|
||||
onResendVerificationEmail: func.isRequired,
|
||||
onManageDisableScrolling: func.isRequired,
|
||||
|
||||
// from withRouter
|
||||
location: shape({ state: object }).isRequired,
|
||||
|
|
@ -290,6 +319,8 @@ const mapDispatchToProps = dispatch => ({
|
|||
submitLogin: ({ email, password }) => dispatch(login(email, password)),
|
||||
submitSignup: params => dispatch(signup(params)),
|
||||
onResendVerificationEmail: () => dispatch(sendVerificationEmail()),
|
||||
onManageDisableScrolling: (componentId, disableScrolling) =>
|
||||
dispatch(manageDisableScrolling(componentId, disableScrolling)),
|
||||
});
|
||||
|
||||
// Note: it is important that the withRouter HOC is **outside** the
|
||||
|
|
|
|||
|
|
@ -82,6 +82,25 @@ exports[`AuthenticationPageComponent matches snapshot 1`] = `
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<InjectIntl(ModalComponent)
|
||||
id="AuthenticationPage.tos"
|
||||
isOpen={false}
|
||||
onClose={[Function]}
|
||||
onManageDisableScrolling={[Function]}
|
||||
>
|
||||
<div>
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="AuthenticationPage.termsHeading"
|
||||
values={Object {}}
|
||||
/>
|
||||
</h2>
|
||||
<TermsOfService
|
||||
className={null}
|
||||
rootClassName={null}
|
||||
/>
|
||||
</div>
|
||||
</InjectIntl(ModalComponent)>
|
||||
</LayoutWrapperMain>
|
||||
<LayoutWrapperFooter
|
||||
className={null}
|
||||
|
|
|
|||
|
|
@ -16,41 +16,6 @@
|
|||
box-shadow: none;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow-x: auto;
|
||||
min-height: 48px;
|
||||
padding-top: 2px;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
min-height: 56px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
@media (--viewportLarge) {
|
||||
min-height: auto;
|
||||
flex-direction: column;
|
||||
margin-top: 28px;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.tab {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
height: 100%;
|
||||
margin-left: 16px;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
@media (--viewportLarge) {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-top: 4px;
|
||||
margin-bottom: 19px;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import {
|
|||
LayoutWrapperFooter,
|
||||
Footer,
|
||||
Page,
|
||||
TabNav,
|
||||
UserNav,
|
||||
} from '../../components';
|
||||
import { ContactDetailsForm, TopbarContainer } from '../../containers';
|
||||
|
|
@ -84,9 +83,7 @@ export const ContactDetailsPageComponent = props => {
|
|||
/>
|
||||
<UserNav selectedPageName="ContactDetailsPage" />
|
||||
</LayoutWrapperTopbar>
|
||||
<LayoutWrapperSideNav>
|
||||
<TabNav rootClassName={css.tabs} tabRootClassName={css.tab} tabs={tabs} />
|
||||
</LayoutWrapperSideNav>
|
||||
<LayoutWrapperSideNav tabs={tabs} />
|
||||
<LayoutWrapperMain>
|
||||
<div className={css.content}>
|
||||
<h1 className={css.title}>
|
||||
|
|
|
|||
|
|
@ -27,38 +27,31 @@ exports[`ContactDetailsPage matches snapshot 1`] = `
|
|||
<LayoutWrapperSideNav
|
||||
className={null}
|
||||
rootClassName={null}
|
||||
>
|
||||
<TabNav
|
||||
className={null}
|
||||
rootClassName={null}
|
||||
tabClassName={null}
|
||||
tabRootClassName={null}
|
||||
tabs={
|
||||
Array [
|
||||
Object {
|
||||
"linkProps": Object {
|
||||
"name": "ContactDetailsPage",
|
||||
},
|
||||
"selected": true,
|
||||
"text": <FormattedMessage
|
||||
id="ContactDetailsPage.emailTabTitle"
|
||||
values={Object {}}
|
||||
/>,
|
||||
tabs={
|
||||
Array [
|
||||
Object {
|
||||
"linkProps": Object {
|
||||
"name": "ContactDetailsPage",
|
||||
},
|
||||
Object {
|
||||
"linkProps": Object {
|
||||
"name": "PasswordChangePage",
|
||||
},
|
||||
"selected": false,
|
||||
"text": <FormattedMessage
|
||||
id="ContactDetailsPage.passwordTabTitle"
|
||||
values={Object {}}
|
||||
/>,
|
||||
"selected": true,
|
||||
"text": <FormattedMessage
|
||||
id="ContactDetailsPage.emailTabTitle"
|
||||
values={Object {}}
|
||||
/>,
|
||||
},
|
||||
Object {
|
||||
"linkProps": Object {
|
||||
"name": "PasswordChangePage",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</LayoutWrapperSideNav>
|
||||
"selected": false,
|
||||
"text": <FormattedMessage
|
||||
id="ContactDetailsPage.passwordTabTitle"
|
||||
values={Object {}}
|
||||
/>,
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
<LayoutWrapperMain
|
||||
className={null}
|
||||
rootClassName={null}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ exports[`InboxPage matches snapshot 1`] = `
|
|||
<LayoutWrapperSideNav
|
||||
className={null}
|
||||
rootClassName={null}
|
||||
tabs={null}
|
||||
>
|
||||
<h1>
|
||||
<FormattedMessage
|
||||
|
|
@ -377,6 +378,7 @@ exports[`InboxPage matches snapshot 3`] = `
|
|||
<LayoutWrapperSideNav
|
||||
className={null}
|
||||
rootClassName={null}
|
||||
tabs={null}
|
||||
>
|
||||
<h1>
|
||||
<FormattedMessage
|
||||
|
|
|
|||
|
|
@ -16,41 +16,6 @@
|
|||
box-shadow: none;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow-x: auto;
|
||||
min-height: 48px;
|
||||
padding-top: 2px;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
min-height: 56px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
@media (--viewportLarge) {
|
||||
min-height: auto;
|
||||
flex-direction: column;
|
||||
margin-top: 28px;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.tab {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
height: 100%;
|
||||
margin-left: 16px;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
@media (--viewportLarge) {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-top: 4px;
|
||||
margin-bottom: 19px;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import {
|
|||
LayoutWrapperFooter,
|
||||
Footer,
|
||||
Page,
|
||||
TabNav,
|
||||
UserNav,
|
||||
} from '../../components';
|
||||
import { PasswordChangeForm, TopbarContainer } from '../../containers';
|
||||
|
|
@ -74,9 +73,7 @@ export const PasswordChangePageComponent = props => {
|
|||
/>
|
||||
<UserNav selectedPageName="PasswordChangePage" />
|
||||
</LayoutWrapperTopbar>
|
||||
<LayoutWrapperSideNav>
|
||||
<TabNav rootClassName={css.tabs} tabRootClassName={css.tab} tabs={tabs} />
|
||||
</LayoutWrapperSideNav>
|
||||
<LayoutWrapperSideNav tabs={tabs} />
|
||||
<LayoutWrapperMain>
|
||||
<div className={css.content}>
|
||||
<h1 className={css.title}>
|
||||
|
|
|
|||
|
|
@ -27,38 +27,31 @@ exports[`PasswordChangePage matches snapshot 1`] = `
|
|||
<LayoutWrapperSideNav
|
||||
className={null}
|
||||
rootClassName={null}
|
||||
>
|
||||
<TabNav
|
||||
className={null}
|
||||
rootClassName={null}
|
||||
tabClassName={null}
|
||||
tabRootClassName={null}
|
||||
tabs={
|
||||
Array [
|
||||
Object {
|
||||
"linkProps": Object {
|
||||
"name": "ContactDetailsPage",
|
||||
},
|
||||
"selected": false,
|
||||
"text": <FormattedMessage
|
||||
id="PasswordChangePage.emailTabTitle"
|
||||
values={Object {}}
|
||||
/>,
|
||||
tabs={
|
||||
Array [
|
||||
Object {
|
||||
"linkProps": Object {
|
||||
"name": "ContactDetailsPage",
|
||||
},
|
||||
Object {
|
||||
"linkProps": Object {
|
||||
"name": "PasswordChangePage",
|
||||
},
|
||||
"selected": true,
|
||||
"text": <FormattedMessage
|
||||
id="PasswordChangePage.passwordTabTitle"
|
||||
values={Object {}}
|
||||
/>,
|
||||
"selected": false,
|
||||
"text": <FormattedMessage
|
||||
id="PasswordChangePage.emailTabTitle"
|
||||
values={Object {}}
|
||||
/>,
|
||||
},
|
||||
Object {
|
||||
"linkProps": Object {
|
||||
"name": "PasswordChangePage",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</LayoutWrapperSideNav>
|
||||
"selected": true,
|
||||
"text": <FormattedMessage
|
||||
id="PasswordChangePage.passwordTabTitle"
|
||||
values={Object {}}
|
||||
/>,
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
<LayoutWrapperMain
|
||||
className={null}
|
||||
rootClassName={null}
|
||||
|
|
|
|||
15
src/containers/PrivacyPolicyPage/PrivacyPolicyPage.css
Normal file
15
src/containers/PrivacyPolicyPage/PrivacyPolicyPage.css
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
@import '../../marketplace.css';
|
||||
|
||||
.heading {
|
||||
margin: 5px 0 18px 0;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
margin: 2px 0 23px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
@media (--viewportLarge) {
|
||||
max-width: 563px;
|
||||
}
|
||||
}
|
||||
101
src/containers/PrivacyPolicyPage/PrivacyPolicyPage.js
Normal file
101
src/containers/PrivacyPolicyPage/PrivacyPolicyPage.js
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { compose } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { injectIntl, intlShape, FormattedMessage } from 'react-intl';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
import { isScrollingDisabled } from '../../ducks/UI.duck';
|
||||
import { TopbarContainer } from '../../containers';
|
||||
import {
|
||||
Page,
|
||||
LayoutSideNavigation,
|
||||
LayoutWrapperMain,
|
||||
LayoutWrapperSideNav,
|
||||
LayoutWrapperTopbar,
|
||||
LayoutWrapperFooter,
|
||||
PrivacyPolicy,
|
||||
Footer,
|
||||
} from '../../components';
|
||||
import config from '../../config';
|
||||
|
||||
import css from './PrivacyPolicyPage.css';
|
||||
|
||||
const PrivacyPolicyPageComponent = props => {
|
||||
const { logoutError, scrollingDisabled, intl } = props;
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
text: intl.formatMessage({ id: 'PrivacyPolicyPage.privacyTabTitle' }),
|
||||
selected: true,
|
||||
linkProps: {
|
||||
name: 'PrivacyPolicyPage',
|
||||
},
|
||||
},
|
||||
{
|
||||
text: intl.formatMessage({ id: 'PrivacyPolicyPage.tosTabTitle' }),
|
||||
selected: false,
|
||||
linkProps: {
|
||||
name: 'TermsOfServicePage',
|
||||
},
|
||||
},
|
||||
];
|
||||
const siteTitle = config.siteTitle;
|
||||
const schemaTitle = intl.formatMessage({ id: 'PrivacyPolicyPage.schemaTitle' }, { siteTitle });
|
||||
const schema = {
|
||||
'@context': 'http://schema.org',
|
||||
'@type': 'WebPage',
|
||||
name: schemaTitle,
|
||||
};
|
||||
return (
|
||||
<Page
|
||||
title={schemaTitle}
|
||||
logoutError={logoutError}
|
||||
scrollingDisabled={scrollingDisabled}
|
||||
schema={schema}
|
||||
>
|
||||
<LayoutSideNavigation>
|
||||
<LayoutWrapperTopbar>
|
||||
<TopbarContainer currentPage="PrivacyPolicyPage" />
|
||||
</LayoutWrapperTopbar>
|
||||
<LayoutWrapperSideNav tabs={tabs} />
|
||||
<LayoutWrapperMain>
|
||||
<div className={css.content}>
|
||||
<h1 className={css.heading}>
|
||||
<FormattedMessage id="PrivacyPolicyPage.heading" />
|
||||
</h1>
|
||||
<PrivacyPolicy />
|
||||
</div>
|
||||
</LayoutWrapperMain>
|
||||
<LayoutWrapperFooter>
|
||||
<Footer />
|
||||
</LayoutWrapperFooter>
|
||||
</LayoutSideNavigation>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
PrivacyPolicyPageComponent.defaultProps = {
|
||||
logoutError: null,
|
||||
};
|
||||
|
||||
const { bool } = PropTypes;
|
||||
|
||||
PrivacyPolicyPageComponent.propTypes = {
|
||||
logoutError: propTypes.error,
|
||||
scrollingDisabled: bool.isRequired,
|
||||
|
||||
// from injectIntl
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const { logoutError } = state.Auth;
|
||||
return {
|
||||
logoutError,
|
||||
scrollingDisabled: isScrollingDisabled(state),
|
||||
};
|
||||
};
|
||||
|
||||
const PrivacyPolicyPage = compose(connect(mapStateToProps), injectIntl)(PrivacyPolicyPageComponent);
|
||||
|
||||
export default PrivacyPolicyPage;
|
||||
|
|
@ -29,3 +29,20 @@
|
|||
.bottomWrapper {
|
||||
@apply --marketplaceModalBottomWrapper;
|
||||
}
|
||||
|
||||
.bottomWrapperText {
|
||||
@apply --marketplaceModalBottomWrapperText;
|
||||
}
|
||||
|
||||
.termsText {
|
||||
@apply --marketplaceModalHelperText;
|
||||
}
|
||||
|
||||
.termsLink {
|
||||
@apply --marketplaceModalHelperLink;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ export const Empty = {
|
|||
onSubmit(values) {
|
||||
console.log('sign up with form values:', values);
|
||||
},
|
||||
onOpenTermsOfService() {
|
||||
console.log('open terms of service');
|
||||
},
|
||||
},
|
||||
group: 'forms',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import * as validators from '../../util/validators';
|
|||
|
||||
import css from './SignupForm.css';
|
||||
|
||||
const KEY_CODE_ENTER = 13;
|
||||
|
||||
const SignupFormComponent = props => {
|
||||
const {
|
||||
rootClassName,
|
||||
|
|
@ -19,6 +21,7 @@ const SignupFormComponent = props => {
|
|||
inProgress,
|
||||
invalid,
|
||||
intl,
|
||||
onOpenTermsOfService,
|
||||
} = props;
|
||||
|
||||
// email
|
||||
|
|
@ -102,6 +105,24 @@ const SignupFormComponent = props => {
|
|||
const submitInProgress = submitting || inProgress;
|
||||
const submitDisabled = invalid || submitInProgress;
|
||||
|
||||
const handleTermsKeyUp = e => {
|
||||
// Allow click action with keyboard like with normal links
|
||||
if (e.keyCode === KEY_CODE_ENTER) {
|
||||
onOpenTermsOfService();
|
||||
}
|
||||
};
|
||||
const termsLink = (
|
||||
<span
|
||||
className={css.termsLink}
|
||||
onClick={onOpenTermsOfService}
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
onKeyUp={handleTermsKeyUp}
|
||||
>
|
||||
<FormattedMessage id="SignupForm.termsAndConditionsLinkText" />
|
||||
</span>
|
||||
);
|
||||
|
||||
return (
|
||||
<Form className={classes} onSubmit={handleSubmit}>
|
||||
<div>
|
||||
|
|
@ -145,6 +166,11 @@ const SignupFormComponent = props => {
|
|||
</div>
|
||||
|
||||
<div className={css.bottomWrapper}>
|
||||
<p className={css.bottomWrapperText}>
|
||||
<span className={css.termsText}>
|
||||
<FormattedMessage id="SignupForm.termsAndConditionsAcceptText" values={{ termsLink }} />
|
||||
</span>
|
||||
</p>
|
||||
<PrimaryButton
|
||||
className={css.submitButton}
|
||||
type="submit"
|
||||
|
|
@ -160,11 +186,15 @@ const SignupFormComponent = props => {
|
|||
|
||||
SignupFormComponent.defaultProps = { inProgress: false };
|
||||
|
||||
const { bool } = PropTypes;
|
||||
const { bool, func } = PropTypes;
|
||||
|
||||
SignupFormComponent.propTypes = {
|
||||
...formPropTypes,
|
||||
inProgress: bool,
|
||||
|
||||
onOpenTermsOfService: func.isRequired,
|
||||
|
||||
// from injectIntl
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@ import React from 'react';
|
|||
import { renderDeep } from '../../util/test-helpers';
|
||||
import SignupForm from './SignupForm';
|
||||
|
||||
const noop = () => null;
|
||||
|
||||
describe('SignupForm', () => {
|
||||
it('matches snapshot', () => {
|
||||
const tree = renderDeep(<SignupForm />);
|
||||
const tree = renderDeep(<SignupForm onOpenTermsOfService={noop} />);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -104,6 +104,28 @@ exports[`SignupForm matches snapshot 1`] = `
|
|||
<div
|
||||
className={undefined}
|
||||
>
|
||||
<p
|
||||
className={undefined}
|
||||
>
|
||||
<span
|
||||
className={undefined}
|
||||
>
|
||||
<span>
|
||||
By signing up you accept the
|
||||
<span
|
||||
className={undefined}
|
||||
onClick={[Function]}
|
||||
onKeyUp={[Function]}
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
>
|
||||
<span>
|
||||
terms and conditions
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</p>
|
||||
<button
|
||||
className=""
|
||||
disabled={true}
|
||||
|
|
|
|||
15
src/containers/TermsOfServicePage/TermsOfServicePage.css
Normal file
15
src/containers/TermsOfServicePage/TermsOfServicePage.css
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
@import '../../marketplace.css';
|
||||
|
||||
.heading {
|
||||
margin: 5px 0 18px 0;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
margin: 2px 0 23px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
@media (--viewportLarge) {
|
||||
max-width: 563px;
|
||||
}
|
||||
}
|
||||
103
src/containers/TermsOfServicePage/TermsOfServicePage.js
Normal file
103
src/containers/TermsOfServicePage/TermsOfServicePage.js
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { compose } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { injectIntl, intlShape, FormattedMessage } from 'react-intl';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
import { isScrollingDisabled } from '../../ducks/UI.duck';
|
||||
import { TopbarContainer } from '../../containers';
|
||||
import {
|
||||
Page,
|
||||
LayoutSideNavigation,
|
||||
LayoutWrapperMain,
|
||||
LayoutWrapperSideNav,
|
||||
LayoutWrapperTopbar,
|
||||
LayoutWrapperFooter,
|
||||
Footer,
|
||||
TermsOfService,
|
||||
} from '../../components';
|
||||
import config from '../../config';
|
||||
|
||||
import css from './TermsOfServicePage.css';
|
||||
|
||||
const TermsOfServicePageComponent = props => {
|
||||
const { logoutError, scrollingDisabled, intl } = props;
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
text: intl.formatMessage({ id: 'TermsOfServicePage.privacyTabTitle' }),
|
||||
selected: false,
|
||||
linkProps: {
|
||||
name: 'PrivacyPolicyPage',
|
||||
},
|
||||
},
|
||||
{
|
||||
text: intl.formatMessage({ id: 'TermsOfServicePage.tosTabTitle' }),
|
||||
selected: true,
|
||||
linkProps: {
|
||||
name: 'TermsOfServicePage',
|
||||
},
|
||||
},
|
||||
];
|
||||
const siteTitle = config.siteTitle;
|
||||
const schemaTitle = intl.formatMessage({ id: 'TermsOfServicePage.schemaTitle' }, { siteTitle });
|
||||
const schema = {
|
||||
'@context': 'http://schema.org',
|
||||
'@type': 'WebPage',
|
||||
name: schemaTitle,
|
||||
};
|
||||
return (
|
||||
<Page
|
||||
title={schemaTitle}
|
||||
logoutError={logoutError}
|
||||
scrollingDisabled={scrollingDisabled}
|
||||
schema={schema}
|
||||
>
|
||||
<LayoutSideNavigation>
|
||||
<LayoutWrapperTopbar>
|
||||
<TopbarContainer currentPage="TermsOfServicePage" />
|
||||
</LayoutWrapperTopbar>
|
||||
<LayoutWrapperSideNav tabs={tabs} />
|
||||
<LayoutWrapperMain>
|
||||
<div className={css.content}>
|
||||
<h1 className={css.heading}>
|
||||
<FormattedMessage id="TermsOfServicePage.heading" />
|
||||
</h1>
|
||||
<TermsOfService />
|
||||
</div>
|
||||
</LayoutWrapperMain>
|
||||
<LayoutWrapperFooter>
|
||||
<Footer />
|
||||
</LayoutWrapperFooter>
|
||||
</LayoutSideNavigation>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
TermsOfServicePageComponent.defaultProps = {
|
||||
logoutError: null,
|
||||
};
|
||||
|
||||
const { bool } = PropTypes;
|
||||
|
||||
TermsOfServicePageComponent.propTypes = {
|
||||
logoutError: propTypes.error,
|
||||
scrollingDisabled: bool.isRequired,
|
||||
|
||||
// from injectIntl
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const { logoutError } = state.Auth;
|
||||
return {
|
||||
logoutError,
|
||||
scrollingDisabled: isScrollingDisabled(state),
|
||||
};
|
||||
};
|
||||
|
||||
const TermsOfServicePage = compose(connect(mapStateToProps), injectIntl)(
|
||||
TermsOfServicePageComponent
|
||||
);
|
||||
|
||||
export default TermsOfServicePage;
|
||||
|
|
@ -31,6 +31,7 @@ export { default as PasswordResetForm } from './PasswordResetForm/PasswordResetF
|
|||
export { default as PasswordResetPage } from './PasswordResetPage/PasswordResetPage';
|
||||
export { default as PayoutDetailsForm } from './PayoutDetailsForm/PayoutDetailsForm';
|
||||
export { default as PayoutPreferencesPage } from './PayoutPreferencesPage/PayoutPreferencesPage';
|
||||
export { default as PrivacyPolicyPage } from './PrivacyPolicyPage/PrivacyPolicyPage';
|
||||
export { default as ProfilePage } from './ProfilePage/ProfilePage';
|
||||
export { default as ProfileSettingsForm } from './ProfileSettingsForm/ProfileSettingsForm';
|
||||
export { default as ProfileSettingsPage } from './ProfileSettingsPage/ProfileSettingsPage';
|
||||
|
|
@ -40,5 +41,6 @@ export { default as SignupForm } from './SignupForm/SignupForm';
|
|||
export { default as StaticPage } from './StaticPage/StaticPage';
|
||||
export { default as StripePaymentForm } from './StripePaymentForm/StripePaymentForm';
|
||||
export { default as StyleguidePage } from './StyleguidePage/StyleguidePage';
|
||||
export { default as TermsOfServicePage } from './TermsOfServicePage/TermsOfServicePage';
|
||||
export { default as TopbarContainer } from './TopbarContainer/TopbarContainer';
|
||||
export { default as TopbarSearchForm } from './TopbarSearchForm/TopbarSearchForm';
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {
|
|||
CheckoutPage,
|
||||
ContactDetailsPage,
|
||||
EditListingPage,
|
||||
EmailVerificationPage,
|
||||
InboxPage,
|
||||
LandingPage,
|
||||
ListingPage,
|
||||
|
|
@ -12,15 +13,16 @@ import {
|
|||
NotFoundPage,
|
||||
OrderPage,
|
||||
PasswordChangePage,
|
||||
PasswordResetPage,
|
||||
PasswordRecoveryPage,
|
||||
PasswordResetPage,
|
||||
PayoutPreferencesPage,
|
||||
PrivacyPolicyPage,
|
||||
ProfilePage,
|
||||
ProfileSettingsPage,
|
||||
SalePage,
|
||||
SearchPage,
|
||||
StyleguidePage,
|
||||
EmailVerificationPage,
|
||||
TermsOfServicePage,
|
||||
} from './containers';
|
||||
|
||||
// routeConfiguration needs to initialize containers first
|
||||
|
|
@ -241,6 +243,16 @@ const routeConfiguration = () => {
|
|||
name: 'PayoutPreferencesPage',
|
||||
component: props => <PayoutPreferencesPage {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/terms-of-service',
|
||||
name: 'TermsOfServicePage',
|
||||
component: props => <TermsOfServicePage {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/privacy-policy',
|
||||
name: 'PrivacyPolicyPage',
|
||||
component: props => <PrivacyPolicyPage {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/styleguide',
|
||||
name: 'Styleguide',
|
||||
|
|
|
|||
|
|
@ -8,12 +8,13 @@
|
|||
"AuthenticationPage.resendEmailLinkText": "Resend it",
|
||||
"AuthenticationPage.resendFailed": "Resending verification email failed. Please try again.",
|
||||
"AuthenticationPage.resendFailedTooManyRequests": "Resending verification email failed, since you’ve already been sent 5 verification emails. Please try again tomorrow.",
|
||||
"AuthenticationPage.schemaTitleSignup": "Sign up | {siteTitle}",
|
||||
"AuthenticationPage.schemaTitleLogin": "Log in | {siteTitle}",
|
||||
"AuthenticationPage.schemaTitleSignup": "Sign up | {siteTitle}",
|
||||
"AuthenticationPage.sendingEmail": "Sending verification email…",
|
||||
"AuthenticationPage.signupFailed": "Signup failed. Please make sure all the information you entered is correct and try again.",
|
||||
"AuthenticationPage.signupFailedEmailAlreadyTaken": "An account already exists with this email address. Try logging in instead.",
|
||||
"AuthenticationPage.signupLinkText": "Sign up",
|
||||
"AuthenticationPage.termsHeading": "Terms of Service",
|
||||
"AuthenticationPage.verifyEmailClose": "LATER",
|
||||
"AuthenticationPage.verifyEmailText": "Thanks for signing up! There's one quick step left. To be able to contact you, we need you to verify your email address. Please click the link we sent to {email}.",
|
||||
"AuthenticationPage.verifyEmailTitle": "{name}, check your inbox to verify your email",
|
||||
|
|
@ -23,11 +24,11 @@
|
|||
"BookingBreakdown.nightCount": "{count, number} {count, plural, one {night} other {nights}}",
|
||||
"BookingBreakdown.pricePerNight": "Price per night",
|
||||
"BookingBreakdown.providerTotalCanceled": "Total price",
|
||||
"BookingBreakdown.providerTotalDeclined": "You would have made",
|
||||
"BookingBreakdown.providerTotalDefault": "You'll make",
|
||||
"BookingBreakdown.providerTotalDelivered": "You made",
|
||||
"BookingBreakdown.providerTotalDeclined": "You would have made",
|
||||
"BookingBreakdown.subTotal": "Subtotal",
|
||||
"BookingBreakdown.refund": "Refund",
|
||||
"BookingBreakdown.subTotal": "Subtotal",
|
||||
"BookingBreakdown.total": "Total price",
|
||||
"BookingDatesForm.bookingEndTitle": "End date",
|
||||
"BookingDatesForm.bookingStartTitle": "Start date",
|
||||
|
|
@ -169,7 +170,7 @@
|
|||
"Footer.searchTurku": "Turku",
|
||||
"Footer.searchVantaa": "Vantaa",
|
||||
"Footer.terms": "Terms",
|
||||
"Footer.termsOfUse": "Terms of use",
|
||||
"Footer.termsOfUse": "Terms of Service",
|
||||
"Footer.toAboutPage": "About us",
|
||||
"Footer.toContactPage": "Contact",
|
||||
"Footer.toFAQPage": "FAQ",
|
||||
|
|
@ -266,13 +267,13 @@
|
|||
"OrderDetailsPanel.orderAutoDeclinedTitle": "You requested to book {listingLink}",
|
||||
"OrderDetailsPanel.orderCanceledStatus": "The request was cancelled on {transitionDate}",
|
||||
"OrderDetailsPanel.orderCanceledTitle": "You booked {listingLink}",
|
||||
"OrderDetailsPanel.orderDeclinedStatus": "Unfortunately {providerName} declined the request on {transitionDate}.",
|
||||
"OrderDetailsPanel.orderDeclinedTitle": "You requested to book {listingLink}",
|
||||
"OrderDetailsPanel.orderDeliveredStatus": " ",
|
||||
"OrderDetailsPanel.orderDeliveredTitle": "Booking period has ended",
|
||||
"OrderDetailsPanel.orderPreauthorizedStatus": "{providerName} has been notified about the booking request, so sit back and relax.",
|
||||
"OrderDetailsPanel.orderPreauthorizedSubtitle": "You have requested to book {listingLink}",
|
||||
"OrderDetailsPanel.orderPreauthorizedTitle": "Great success, {customerName}!",
|
||||
"OrderDetailsPanel.orderDeclinedStatus": "Unfortunately {providerName} declined the request on {transitionDate}.",
|
||||
"OrderDetailsPanel.orderDeclinedTitle": "You requested to book {listingLink}",
|
||||
"OrderPage.fetchOrderFailed": "Fetching order data failed.",
|
||||
"OrderPage.loadingData": "Loading order data.",
|
||||
"OrderPage.title": "Order details: {listingTitle}",
|
||||
|
|
@ -385,6 +386,10 @@
|
|||
"PayoutDetailsForm.streetAddressRequired": "This field is required",
|
||||
"PayoutDetailsForm.submitButtonText": "Save details & publish listing",
|
||||
"PayoutDetailsForm.title": "One more thing: payout preferences",
|
||||
"PrivacyPolicyPage.heading": "Saunatime Privacy Policy",
|
||||
"PrivacyPolicyPage.privacyTabTitle": "Privacy Policy",
|
||||
"PrivacyPolicyPage.schemaTitle": "Privacy Policy | {siteTitle}",
|
||||
"PrivacyPolicyPage.tosTabTitle": "Terms of Service",
|
||||
"ProfileSettingsForm.addYourProfilePicture": "+ Add your profile picture…",
|
||||
"ProfileSettingsForm.addYourProfilePictureMobile": "+ Add",
|
||||
"ProfileSettingsForm.changeAvatar": "Change",
|
||||
|
|
@ -408,23 +413,23 @@
|
|||
"SaleDetailsPanel.bannedUserDisplayName": "Banned user",
|
||||
"SaleDetailsPanel.bookingBreakdownTitle": "Booking breakdown",
|
||||
"SaleDetailsPanel.customerBannedStatus": "The user made the request, but was later banned.",
|
||||
"SaleDetailsPanel.declineSaleFailed": "Oops, declining failed. Please try again.",
|
||||
"SaleDetailsPanel.listingAcceptedTitle": "Woohoo! You accepted a request from {customerName} for {listingLink}",
|
||||
"SaleDetailsPanel.listingCanceledTitle": "{customerName} booked {listingLink}",
|
||||
"SaleDetailsPanel.listingDeliveredTitle": "{customerName} booked {listingLink}",
|
||||
"SaleDetailsPanel.listingDeclinedTitle": "{customerName} requested to book {listingLink}",
|
||||
"SaleDetailsPanel.listingDeliveredTitle": "{customerName} booked {listingLink}",
|
||||
"SaleDetailsPanel.listingRequestedTitle": "{customerName} has requested to book {listingLink}",
|
||||
"SaleDetailsPanel.declineSaleFailed": "Oops, declining failed. Please try again.",
|
||||
"SaleDetailsPanel.saleAcceptedStatus": "You accepted the request on {formattedDate}.",
|
||||
"SaleDetailsPanel.saleAutoDeclinedStatus": "You did not react to the request on time. The request expired on {formattedDate}.",
|
||||
"SaleDetailsPanel.saleCanceledStatus": "The request was cancelled on {formattedDate}.",
|
||||
"SaleDetailsPanel.saleDeliveredStatus": "You delivered the order on {formattedDate}.",
|
||||
"SaleDetailsPanel.saleDeclinedStatus": "You declined the request on {formattedDate}.",
|
||||
"SaleDetailsPanel.saleDeliveredStatus": "You delivered the order on {formattedDate}.",
|
||||
"SaleDetailsPanel.saleRequestedStatus": "{customerName} is waiting for your response.",
|
||||
"SalePage.acceptButton": "Accept",
|
||||
"SalePage.fetchSaleFailed": "Fetching sale data failed.",
|
||||
"SalePage.loadingData": "Loading sale data.",
|
||||
"SalePage.declineButton": "Decline",
|
||||
"SalePage.declineSaleFailed": "Declining sale failed.",
|
||||
"SalePage.fetchSaleFailed": "Fetching sale data failed.",
|
||||
"SalePage.loadingData": "Loading sale data.",
|
||||
"SalePage.title": "Sale details: {title}",
|
||||
"SearchMapInfoCard.noImage": "No image",
|
||||
"SearchPage.foundResults": "{count, number} {count, plural, one {sauna} other {saunas}} found",
|
||||
|
|
@ -433,8 +438,8 @@
|
|||
"SearchPage.loadingResults": "Loading search results…",
|
||||
"SearchPage.noResults": "Could not find any listings.",
|
||||
"SearchPage.openMapView": "Map",
|
||||
"SearchPage.schemaMapSearch": "map search",
|
||||
"SearchPage.schemaDescription": "Showing search results",
|
||||
"SearchPage.schemaMapSearch": "map search",
|
||||
"SearchPage.schemaTitle": "Search results for {searchAddress} | {siteTitle}",
|
||||
"SearchPage.searchError": "Search failed. Please try again.",
|
||||
"SearchResultsPanel.nextPage": "Next page",
|
||||
|
|
@ -455,8 +460,8 @@
|
|||
"SignupForm.passwordTooLong": "Password should be at most {maxLength} characters.",
|
||||
"SignupForm.passwordTooShort": "Password should be at least {minLength} characters.",
|
||||
"SignupForm.signUp": "Sign up",
|
||||
"SignupForm.stripeConnectedAccountAgreementLinkText": "Stripe Connected Account Agreement",
|
||||
"SignupForm.termsOfService": "By confirming I accept the terms and conditions and the {stripeConnectedAccountAgreementLink}",
|
||||
"SignupForm.termsAndConditionsAcceptText": "By signing up you accept the {termsLink}",
|
||||
"SignupForm.termsAndConditionsLinkText": "terms and conditions",
|
||||
"StripeBankAccountTokenInputField.accountNumber.inline": "account number",
|
||||
"StripeBankAccountTokenInputField.accountNumber.invalid": "Invalid {country} bank account number",
|
||||
"StripeBankAccountTokenInputField.accountNumber.label": "Bank account number",
|
||||
|
|
@ -509,6 +514,10 @@
|
|||
"StripePaymentForm.stripe.validation_error.missing": "There is no card on a customer that is being charged.",
|
||||
"StripePaymentForm.stripe.validation_error.processing_error": "An error occurred while processing the card.",
|
||||
"StripePaymentForm.submitPaymentInfo": "Send request",
|
||||
"TermsOfServicePage.heading": "Terms of Service",
|
||||
"TermsOfServicePage.privacyTabTitle": "Privacy Policy",
|
||||
"TermsOfServicePage.schemaTitle": "Terms of Service | {siteTitle}",
|
||||
"TermsOfServicePage.tosTabTitle": "Terms of Service",
|
||||
"Topbar.checkInbox": "Please check your inbox and verify your email {email}",
|
||||
"Topbar.closeVerifyEmailReminder": "Later",
|
||||
"Topbar.fixEmail": "Whoops, typo in your email? {fixEmailLink}",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue