diff --git a/src/containers/ReviewForm/ReviewForm.css b/src/containers/ReviewForm/ReviewForm.css new file mode 100644 index 00000000..c37173ca --- /dev/null +++ b/src/containers/ReviewForm/ReviewForm.css @@ -0,0 +1,53 @@ +@import '../../marketplace.css'; + +.root { + display: flex; + flex-direction: column; + + /* Layout: size and positioning */ + width: 100%; + height: auto; + margin-top: 24px; +} + +.error { + color: var(--failColor); +} + +.errorPlaceholder { + @media (--viewportMedium) { + width: 100%; + height: 32px; + } +} + +.reviewRating { + margin-bottom: 18px; + + @media (--viewportMedium) { + margin-bottom: 16px; + } +} + +.reviewContent { + flex-shrink: 0; + margin-top: 24px; + margin-bottom: 24px; + + @media (--viewportMedium) { + margin-top: 30px; + } +} + +.submitButton { + margin-top: auto; + margin-bottom: 24px; + flex-shrink: 0; + + @media (--viewportMedium) { + display: inline-block; + width: 100%; + margin-top: 36px; + margin-bottom: 0; + } +} diff --git a/src/containers/ReviewForm/ReviewForm.example.js b/src/containers/ReviewForm/ReviewForm.example.js new file mode 100644 index 00000000..d5f7660a --- /dev/null +++ b/src/containers/ReviewForm/ReviewForm.example.js @@ -0,0 +1,14 @@ +/* eslint-disable no-console */ +import ReviewForm from './ReviewForm'; + +export const Empty = { + component: ReviewForm, + props: { + onSubmit: values => { + console.log('Submit ReviewForm with (unformatted) values:', values); + }, + reviewSent: false, + sendReviewInProgress: false, + }, + group: 'forms', +}; diff --git a/src/containers/ReviewForm/ReviewForm.js b/src/containers/ReviewForm/ReviewForm.js new file mode 100644 index 00000000..ea33ed66 --- /dev/null +++ b/src/containers/ReviewForm/ReviewForm.js @@ -0,0 +1,111 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { compose } from 'redux'; +import { reduxForm, propTypes as formPropTypes } from 'redux-form'; +import { intlShape, injectIntl, FormattedMessage } from 'react-intl'; +import classNames from 'classnames'; +import { isTransactionsTransitionAlreadyReviewed } from '../../util/errors'; +import * as propTypes from '../../util/propTypes'; +import { required } from '../../util/validators'; +import { FieldStarRating, Form, PrimaryButton, TextInputField } from '../../components'; + +import css from './ReviewForm.css'; + +const ReviewFormComponent = props => { + const { + className, + disabled, + handleSubmit, + intl, + form, + invalid, + submitting, + reviewSent, + sendReviewError, + sendReviewInProgress, + } = props; + + const reviewRating = intl.formatMessage({ id: 'ReviewForm.reviewRatingLabel' }); + const reviewRatingRequiredMessage = intl.formatMessage({ + id: 'ReviewForm.reviewRatingRequired', + }); + + const reviewContent = intl.formatMessage({ id: 'ReviewForm.reviewContentLabel' }); + const reviewContentPlaceholderMessage = intl.formatMessage({ + id: 'ReviewForm.reviewContentPlaceholder', + }); + const reviewContentRequiredMessage = intl.formatMessage({ + id: 'ReviewForm.reviewContentRequired', + }); + + const errorMessage = + sendReviewError && isTransactionsTransitionAlreadyReviewed(sendReviewError) ? ( +

+ +

+ ) : ( +

+ +

+ ); + const errorArea = sendReviewError ? errorMessage :

; + + const reviewSubmitMessage = intl.formatMessage({ + id: 'ReviewForm.reviewSubmit', + }); + + const classes = classNames(css.root, className); + const submitInProgress = submitting || sendReviewInProgress; + const submitDisabled = invalid || disabled || submitInProgress; + + return ( +

+ + + + + {errorArea} + + {reviewSubmitMessage} + + + ); +}; + +ReviewFormComponent.defaultProps = { className: null, sendReviewError: null }; + +const { bool, func, string } = PropTypes; + +ReviewFormComponent.propTypes = { + ...formPropTypes, + className: string, + intl: intlShape.isRequired, + onSubmit: func.isRequired, + reviewSent: bool.isRequired, + sendReviewError: propTypes.error, + sendReviewInProgress: bool.isRequired, +}; + +const formName = 'ReviewForm'; + +export default compose(reduxForm({ form: formName }), injectIntl)(ReviewFormComponent); diff --git a/src/containers/index.js b/src/containers/index.js index 07d94cbf..b2707ce1 100644 --- a/src/containers/index.js +++ b/src/containers/index.js @@ -35,6 +35,7 @@ export { default as PrivacyPolicyPage } from './PrivacyPolicyPage/PrivacyPolicyP export { default as ProfilePage } from './ProfilePage/ProfilePage'; export { default as ProfileSettingsForm } from './ProfileSettingsForm/ProfileSettingsForm'; export { default as ProfileSettingsPage } from './ProfileSettingsPage/ProfileSettingsPage'; +export { default as ReviewForm } from './ReviewForm/ReviewForm'; export { default as SalePage } from './SalePage/SalePage'; export { default as SearchPage } from './SearchPage/SearchPage'; export { default as SendMessageForm } from './SendMessageForm/SendMessageForm'; diff --git a/src/examples.js b/src/examples.js index a9a15c4e..2a3b3452 100644 --- a/src/examples.js +++ b/src/examples.js @@ -59,6 +59,7 @@ import * as LoginForm from './containers/LoginForm/LoginForm.example'; import * as PasswordRecoveryForm from './containers/PasswordRecoveryForm/PasswordRecoveryForm.example'; import * as PasswordResetForm from './containers/PasswordResetForm/PasswordResetForm.example'; import * as PayoutDetailsForm from './containers/PayoutDetailsForm/PayoutDetailsForm.example'; +import * as ReviewForm from './containers/ReviewForm/ReviewForm.example'; import * as SendMessageForm from './containers/SendMessageForm/SendMessageForm.example'; import * as SignupForm from './containers/SignupForm/SignupForm.example'; import * as StripePaymentForm from './containers/StripePaymentForm/StripePaymentForm.example'; @@ -116,6 +117,7 @@ export { PayoutDetailsForm, ResponsiveImage, ReviewRating, + ReviewForm, SelectField, SendMessageForm, SignupForm, diff --git a/src/translations/en.json b/src/translations/en.json index d152545d..e7eba216 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -442,6 +442,17 @@ "ProfileSettingsPage.title": "Profile settings", "ProfileSettingsPage.viewProfileLink": "View your profile", "ResponsiveImage.noImage": "No image", + "ReviewForm.reviewContentLabel": "Leave a review", + "ReviewForm.reviewContentPlaceholder": "Descripe your experience...", + "ReviewForm.reviewContentRequired": "Review message is required", + "ReviewForm.reviewRatingLabel": "Rate your experience", + "ReviewForm.reviewRatingRequired": "Review message is required", + "ReviewForm.reviewSubmit": "Publish review", + "ReviewForm.reviewSubmitAlreadySent": "Review already sent. Please refresh the page.", + "ReviewForm.reviewSubmitFailed": "Failed to sent a review. Please try again.", + "ReviewModal.description": "Reviews are an important part of the Saunatime community. Please share what went well and what could have been improved.", + "ReviewModal.later": "Later", + "ReviewModal.title": "Leave a review for {revieweeName}", "SaleDetailsPanel.acceptSaleFailed": "Oops, accepting failed. Please try again.", "SaleDetailsPanel.activityHeading": "Activity", "SaleDetailsPanel.bannedUserDisplayName": "Banned user",