Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add payment activity widget emoji survey #8506

Merged
merged 77 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
90f6994
payments overview widget
Mar 4, 2024
e4027d0
add unicode emoji
Mar 5, 2024
2fdb2b3
add unit tests
Mar 5, 2024
2dd5b9a
ui tests
Mar 6, 2024
b0c9405
add changelog
Mar 6, 2024
53f7ec4
fix code reviews
Mar 8, 2024
7854b84
fix responsive component
Mar 11, 2024
4eaf649
hide feature under feature flag
Mar 11, 2024
85a3162
set more descriptive names for the ratings
Mar 11, 2024
bba57a2
separate external and internal dependencies
Mar 11, 2024
93f520a
adjust emoji size
Mar 12, 2024
1bb9d42
fix close button
Mar 13, 2024
7581bda
center emoji
Mar 14, 2024
4dcbec0
add text padding
Mar 14, 2024
eec84c4
remove ununsed var
Mar 14, 2024
da56928
add paddings
Mar 14, 2024
f6bef71
add close button on survey ends
Mar 15, 2024
c13b7bb
Remove duplicate var declaration post-merge
Jinksi Mar 28, 2024
38c0f38
Reorder imports
Jinksi Mar 28, 2024
0b807c8
Move survey to be child of PaymentActivity component
Jinksi Mar 28, 2024
3c40202
Remove Card wrap
Jinksi Mar 28, 2024
93b6d1a
Fix merge missing changes in survey controller
Jinksi Mar 28, 2024
707f249
Merge branch 'develop' into add/8484-payment-activity-widget-survey
Jinksi Apr 2, 2024
29c573e
Update snapshot tests
Jinksi Apr 3, 2024
8ca2833
Update TS with Rating type etc
Jinksi Apr 3, 2024
4e2fbf4
Rename plural to singular Emoticon
Jinksi Apr 3, 2024
4e63d0e
Rename survey class to `wcpay-payments-activity__survey`
Jinksi Apr 3, 2024
e7ac70d
Update styles and markup
Jinksi Apr 3, 2024
4f5ace7
Rename status -> responseStatus
Jinksi Apr 3, 2024
16dc85d
Rename disclaimer class
Jinksi Apr 3, 2024
38dab2c
Use WP icon for close button
Jinksi Apr 3, 2024
0b69a97
Merge branch 'develop' into add/8484-payment-activity-widget-survey
Jinksi Apr 3, 2024
f6d5318
Reduce to single call to context hook
Jinksi Apr 4, 2024
4607805
Update survey text and alignment to match design
Jinksi Apr 4, 2024
964ef07
Show error notice when survey post response fails
Jinksi Apr 4, 2024
b9034f3
Use a more descriptive option key for submitted survey
Jinksi Apr 4, 2024
364f0cb
Refactor context TS
Jinksi Apr 4, 2024
ba26daa
Simplify css for both survey and confirmation message
Jinksi Apr 4, 2024
4040dd9
Move CardFooter to Survey component
Jinksi Apr 4, 2024
da393e2
Update tests
Jinksi Apr 4, 2024
e7f8c09
Fix PaymentActivity test
Jinksi Apr 4, 2024
477fec4
Refactor Emoticon onClick to consolidate logic
Jinksi Apr 4, 2024
686da84
Prefer implicit types to improve readability
Jinksi Apr 4, 2024
e381c24
Merge branch 'develop' into add/8484-payment-activity-widget-survey
Jinksi Apr 4, 2024
584b00f
Merge branch 'develop' into add/8484-payment-activity-widget-survey
Jinksi Apr 8, 2024
22cba56
Merge branch 'develop' into add/8484-payment-activity-widget-survey
Jinksi Apr 8, 2024
8915377
Render survey only when has at least one payment
Jinksi Apr 8, 2024
de06a8c
Submit WCPay version number with response
Jinksi Apr 9, 2024
aea55e1
Disable close button if submission in progress
Jinksi Apr 9, 2024
5ffaa5b
Merge branch 'develop' into add/8484-payment-activity-widget-survey
Jinksi Apr 9, 2024
64daee3
Update links to use the woocommerce.com domain (#8574)
vladolaru Apr 9, 2024
bdb09b2
Jetpack Connection Manager Updates (#8584)
dmallory42 Apr 9, 2024
9c32c43
Clear WP cache on writes inside Database_Cache (#8601)
marcinbot Apr 9, 2024
ea68a19
chore: remove unused code which confirmed payment in legacy UPE (#8577)
timur27 Apr 9, 2024
fec1630
Added support to Cartes Bancaires (#8568)
gpressutto5 Apr 9, 2024
197c0d2
Remove @wordpress/data dependency in the email input iframe file (#8561)
hsingyuc Apr 10, 2024
b4e6ff7
Adds check to see if WC()->session is set in WooPay_Utilities::should…
billrobbins Apr 10, 2024
032d9f2
Add Multi-Currency Support to Page Caching via Cookies (#8534)
rafaelzaleski Apr 10, 2024
ba727d0
Reporting: Add UI for Payment Data Highlights tiles in Payment Activi…
nagpai Apr 10, 2024
59332ad
Prevent WooPay order webhook from being registered if account is unde…
ricardo Apr 10, 2024
09d274a
Fix account tools Finish setup button: point to Stripe KYC flow inste…
oaratovskyi Apr 10, 2024
3f4cd6e
Revert account rejected check before registering WooPay order webhook…
ricardo Apr 10, 2024
c999d21
Ensure “Proceed to Checkout” Button’s Loading Spinner Doesn’t Affect …
lovo-h Apr 10, 2024
207e0ca
Implement session cookie for WooPay checkout flow (#8570)
cesarcosta99 Apr 10, 2024
cc05f78
Fix tests after merge
Jinksi Apr 11, 2024
1fdb16d
Merge branch 'develop' into add/8484-payment-activity-widget-survey
Jinksi Apr 11, 2024
b41bd3a
Merge branch 'develop' into add/8484-payment-activity-widget-survey
Jinksi Apr 12, 2024
3666e0d
Merge branch 'develop' into add/8484-payment-activity-widget-survey
Apr 12, 2024
ad76ea2
Merge branch 'develop' into add/8484-payment-activity-widget-survey
Jinksi Apr 17, 2024
152a819
Bring survey into new `payment-activity` dir
Jinksi Apr 17, 2024
96d81d7
Fix missing jest mocks
Jinksi Apr 17, 2024
8a912c3
Add survey integration tests for payment-activity component
Jinksi Apr 17, 2024
c20d8de
Use @include breakpoint for media query
Jinksi Apr 18, 2024
ce69a28
Add space above Emoticon declaration
Jinksi Apr 18, 2024
824c8d9
Remove simplify inner container max-width styling
Jinksi Apr 18, 2024
959210d
Define emoticons as React Elements rather than strings to avoid `dang…
Jinksi Apr 18, 2024
e046bc3
Merge branch 'develop' into add/8484-payment-activity-widget-survey
Jinksi Apr 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog/add-8197-overview-survey
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: add

Add User Satisfaction Survey for Payments Overview Widget
12 changes: 11 additions & 1 deletion client/components/payment-activity/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,23 @@
import * as React from 'react';
import { Card, CardBody, CardHeader } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import interpolateComponents from '@automattic/interpolate-components';

/**
* Internal dependencies
*/

import EmptyStateAsset from 'assets/images/payment-activity-empty-state.svg?asset';
import interpolateComponents from '@automattic/interpolate-components';
import PaymentActivityData from './payment-activity-data';
import Survey from './survey';
import { WcPayOverviewSurveyContextProvider } from './survey/context';
import './style.scss';

const PaymentActivity: React.FC = () => {
const { lifetimeTPV } = wcpaySettings;
const hasAtLeastOnePayment = lifetimeTPV > 0;
const isOverviewSurveySubmitted =
wcpaySettings.isOverviewSurveySubmitted ?? false;

return (
<Card>
Expand Down Expand Up @@ -50,6 +54,12 @@ const PaymentActivity: React.FC = () => {
</div>
) }
</CardBody>

{ ! isOverviewSurveySubmitted && hasAtLeastOnePayment && (
<WcPayOverviewSurveyContextProvider>
<Survey />
</WcPayOverviewSurveyContextProvider>
) }
</Card>
);
};
Expand Down
84 changes: 84 additions & 0 deletions client/components/payment-activity/survey/context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**
* External dependencies
*/
import React, { createContext, useState, useCallback, useContext } from 'react';
import apiFetch from '@wordpress/api-fetch';
import { useDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import { NAMESPACE } from 'data/constants';
import type { OverviewSurveyFields } from './types';

type ResponseStatus = 'pending' | 'resolved' | 'error';

const useContextValue = ( initialState: OverviewSurveyFields = {} ) => {
const [ surveySubmitted, setSurveySubmitted ] = useState( false );
const [ responseStatus, setResponseStatus ] = useState< ResponseStatus >(
'resolved'
);
const [ surveyAnswers, setSurveyAnswers ] = useState( initialState );

const { createErrorNotice } = useDispatch( 'core/notices' );

const submitSurvey = useCallback(
async ( answers: OverviewSurveyFields ) => {
setResponseStatus( 'pending' );
try {
await apiFetch( {
path: `${ NAMESPACE }/upe_survey/payments-overview`,
method: 'POST',
data: answers,
} );
setSurveySubmitted( true );
setResponseStatus( 'resolved' );
} catch ( e ) {
setResponseStatus( 'error' );
setSurveySubmitted( false );
createErrorNotice(
__(
'An error occurred while submitting the survey. Please try again.',
'woocommerce-payments'
)
);
}
},
[ setResponseStatus, setSurveySubmitted, createErrorNotice ]
);

return {
setSurveySubmitted: submitSurvey,
responseStatus,
surveySubmitted,
surveyAnswers,
setSurveyAnswers,
};
};

type ContextValue = ReturnType< typeof useContextValue >;

const WcPayOverviewSurveyContext = createContext< ContextValue | null >( null );

export const WcPayOverviewSurveyContextProvider: React.FC< {
initialData?: OverviewSurveyFields;
} > = ( { children, initialData } ) => {
return (
<WcPayOverviewSurveyContext.Provider
value={ useContextValue( initialData ) }
>
{ children }
</WcPayOverviewSurveyContext.Provider>
);
};

export const useOverviewSurveyContext = (): ContextValue => {
const context = useContext( WcPayOverviewSurveyContext );
if ( ! context ) {
throw new Error( 'An error occurred when using survey context' );
}
return context;
};

export default WcPayOverviewSurveyContext;
49 changes: 49 additions & 0 deletions client/components/payment-activity/survey/emoticon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* External dependencies
*/
import React from 'react';
import classNames from 'classnames';

/**
* Internal dependencies
*/
import type { Rating } from './types';

const emoticons: Record< Rating, React.ReactElement > = {
'very-unhappy': <>&#128542;</>,
unhappy: <>&#129764;</>,
neutral: <>&#128529;</>,
happy: <>&#128578;</>,
'very-happy': <>&#128525;</>,
};

interface Props {
rating: Rating;
onClick: ( event: React.MouseEvent< HTMLButtonElement > ) => void;
disabled: boolean;
isSelected: boolean;
}

const Emoticon: React.FC< Props > = ( {
rating,
onClick,
disabled,
isSelected,
} ) => {
return (
<button
disabled={ disabled }
type="button"
onClick={ onClick }
className={ classNames( 'components-button', 'has-icon', {
selected: isSelected,
} ) }
>
<span role="img" aria-label={ rating }>
{ emoticons[ rating ] }
</span>
</button>
);
};

export default Emoticon;
199 changes: 199 additions & 0 deletions client/components/payment-activity/survey/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/** @format **/

/**
* External dependencies
*/
import React from 'react';
import { HorizontalRule } from '@wordpress/primitives';
import { Button, CardFooter, TextareaControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { createInterpolateElement, useState } from '@wordpress/element';
import { Icon, closeSmall } from '@wordpress/icons';

/**
* Internal dependencies.
*/
import type { Rating } from './types';
import { useOverviewSurveyContext } from './context';
import Emoticon from './emoticon';
import './style.scss';

const Survey: React.FC = () => {
const {
responseStatus,
surveySubmitted,
surveyAnswers,
setSurveyAnswers,
setSurveySubmitted,
} = useOverviewSurveyContext();

const [ showComponent, setShowComponent ] = useState( true );

const currentRating = surveyAnswers.rating;
const ratingWithComment: Rating[] = [
'very-unhappy',
'unhappy',
'neutral',
];
const ratings: Rating[] = [
'very-unhappy',
'unhappy',
'neutral',
'happy',
'very-happy',
];
const showComment =
currentRating && ratingWithComment.includes( currentRating );
const disableForm = 'pending' === responseStatus;

const setReviewRating = function ( value?: Rating ) {
const answers = {
...surveyAnswers,
rating: value,
};
setSurveyAnswers( answers );

// If the user selects a rating that does not require a comment, submit the survey immediately.
if ( value && ! ratingWithComment.includes( value ) ) {
setSurveySubmitted( answers );
}
};

if ( ! showComponent ) {
return null;
}

if ( surveySubmitted ) {
return (
<CardFooter size="small">
<div className="wcpay-payments-activity__survey">
<div className="survey_container">
<span role="img" aria-label="Thank you!">
🙌
</span>
{ __(
'We appreciate your feedback!',
'woocommerce-payments'
) }
</div>

<div className="close_container">
<button
type="button"
className="components-button has-icon"
aria-label="Close dialog"
onClick={ () => {
setShowComponent( false );
} }
>
<Icon icon={ closeSmall } size={ 28 } />
</button>
</div>
</div>
</CardFooter>
);
}

return (
<CardFooter size="small">
<div className="wcpay-payments-activity__survey">
<div className="survey_container">
{ __(
'Are those metrics helpful?',
'woocommerce-payments'
) }

<div className="survey_container__emoticons">
{ ratings.map( ( rating ) => (
<Emoticon
key={ rating }
disabled={ disableForm }
rating={ rating }
onClick={ () => setReviewRating( rating ) }
isSelected={ rating === currentRating }
/>
) ) }
</div>
</div>

{ showComment && (
<>
<div className="close_container">
<button
type="button"
className="components-button has-icon"
aria-label="Close dialog"
onClick={ () => {
setReviewRating( undefined );
} }
disabled={ disableForm }
>
<Icon icon={ closeSmall } size={ 28 } />
</button>
</div>

<HorizontalRule />

<div className="comment_container">
<TextareaControl
label={ __(
'Why do you feel that way? (optional)',
'woocommerce-payments'
) }
onChange={ ( text ) => {
setSurveyAnswers( ( prev ) => ( {
...prev,
comments: text,
} ) );
} }
value={ surveyAnswers.comments ?? '' }
readOnly={ disableForm }
/>
<p className="comment_container__disclaimer">
{ createInterpolateElement(
__(
'Your feedback will be only be shared with WooCommerce and treated pursuant to our <a>privacy policy</a>.',
'woocommerce-payments'
),
{
a: (
// eslint-disable-next-line jsx-a11y/anchor-has-content
<a
href="https://automattic.com/privacy/"
target="_blank"
rel="noreferrer"
/>
),
}
) }
</p>
</div>

<div className="wcpay-confirmation-modal__footer">
<Button
variant={ 'tertiary' }
disabled={ disableForm }
onClick={ () => {
setReviewRating( undefined );
} }
>
{ __( 'Cancel', 'woocommerce-payments' ) }
</Button>
<Button
variant={ 'primary' }
isBusy={ disableForm }
disabled={ disableForm }
onClick={ () =>
setSurveySubmitted( surveyAnswers )
}
>
{ __( 'Send', 'woocommerce-payments' ) }
</Button>
</div>
</>
) }
</div>
</CardFooter>
);
};
export default Survey;
Loading
Loading