-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1217 from shopgate/CURB-3885-back-in-stock-feature
Implemented Back-In-Stock reminder
- Loading branch information
Showing
103 changed files
with
1,829 additions
and
831 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import { PipelineRequest } from '@shopgate/engage/core'; | ||
import { | ||
SHOPGATE_USER_ADD_BACK_IN_STOCK_SUBSCRIPTION, | ||
SHOPGATE_USER_DELETE_BACK_IN_STOCK_SUBSCRIPTION, | ||
SHOPGATE_USER_GET_BACK_IN_STOCK_SUBSCRIPTIONS, | ||
ADD_BACK_IN_STOCK_SUBSCRIPTION, | ||
ADD_BACK_IN_STOCK_SUBSCRIPTION_ERROR, | ||
ADD_BACK_IN_STOCK_SUBSCRIPTION_SUCCESS, | ||
FETCH_BACK_IN_STOCK_SUBSCRIPTIONS, | ||
FETCH_BACK_IN_STOCK_SUBSCRIPTIONS_ERROR, | ||
FETCH_BACK_IN_STOCK_SUBSCRIPTIONS_SUCCESS, | ||
REMOVE_BACK_IN_STOCK_SUBSCRIPTION, | ||
REMOVE_BACK_IN_STOCK_SUBSCRIPTION_ERROR, | ||
REMOVE_BACK_IN_STOCK_SUBSCRIPTION_SUCCESS, | ||
} from '@shopgate/engage/back-in-stock/constants'; | ||
|
||
/** | ||
* Fetch Back in Stock Subscriptions | ||
* @returns {Function} | ||
*/ | ||
export const fetchBackInStockSubscriptions = () => (dispatch) => { | ||
dispatch({ type: FETCH_BACK_IN_STOCK_SUBSCRIPTIONS }); | ||
|
||
const request = new PipelineRequest(SHOPGATE_USER_GET_BACK_IN_STOCK_SUBSCRIPTIONS) | ||
.setInput({ | ||
limit: 100, | ||
filters: { | ||
status: { $in: ['active', 'triggered'] }, | ||
}, | ||
}) | ||
.setRetries(0) | ||
.dispatch(); | ||
|
||
request | ||
.then(({ subscriptions }) => { | ||
dispatch({ | ||
type: FETCH_BACK_IN_STOCK_SUBSCRIPTIONS_SUCCESS, | ||
subscriptions, | ||
}); | ||
}) | ||
.catch((error) => { | ||
dispatch({ | ||
type: FETCH_BACK_IN_STOCK_SUBSCRIPTIONS_ERROR, | ||
error, | ||
}); | ||
}); | ||
|
||
return request; | ||
}; | ||
|
||
/** | ||
* Add a Back in Stock Subscription | ||
* @param {Object} props Props. | ||
* @param {string} props.productId The product for which the subscription should be added | ||
* @returns {Function} | ||
*/ | ||
export const addBackInStockSubscription = ({ productId }) => (dispatch) => { | ||
dispatch({ type: ADD_BACK_IN_STOCK_SUBSCRIPTION }); | ||
const request = new PipelineRequest(SHOPGATE_USER_ADD_BACK_IN_STOCK_SUBSCRIPTION) | ||
.setInput({ | ||
productCode: productId, | ||
}) | ||
.setRetries(0) | ||
.dispatch(); | ||
|
||
request | ||
.then(({ subscriptions }) => { | ||
dispatch({ | ||
type: ADD_BACK_IN_STOCK_SUBSCRIPTION_SUCCESS, | ||
subscriptions, | ||
}); | ||
}) | ||
.catch((error) => { | ||
dispatch({ | ||
type: ADD_BACK_IN_STOCK_SUBSCRIPTION_ERROR, | ||
error, | ||
}); | ||
}); | ||
|
||
return request; | ||
}; | ||
|
||
/** | ||
* Remove a Back in Stock Subscription | ||
* @param {Object} props Props. | ||
* @param {string} props.subscriptionCode The subscription which should be deleted | ||
* @returns {Function} | ||
*/ | ||
export const removeBackInStockSubscription = ({ subscriptionCode }) => (dispatch) => { | ||
dispatch({ type: REMOVE_BACK_IN_STOCK_SUBSCRIPTION }); | ||
const request = new PipelineRequest(SHOPGATE_USER_DELETE_BACK_IN_STOCK_SUBSCRIPTION) | ||
.setInput({ | ||
subscriptionCode, | ||
}) | ||
.setRetries(0) | ||
.dispatch(); | ||
|
||
request | ||
.then(({ subscriptions }) => { | ||
dispatch({ | ||
type: REMOVE_BACK_IN_STOCK_SUBSCRIPTION_SUCCESS, | ||
subscriptions, | ||
}); | ||
}) | ||
.catch((error) => { | ||
dispatch({ | ||
type: REMOVE_BACK_IN_STOCK_SUBSCRIPTION_ERROR, | ||
error, | ||
}); | ||
}); | ||
|
||
return request; | ||
}; | ||
|
10 changes: 10 additions & 0 deletions
10
libraries/engage/back-in-stock/components/BackInStockButton/connector.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { connect } from 'react-redux'; | ||
import { addBackInStockSubscription } from '@shopgate/engage/back-in-stock/actions'; | ||
import grantPushPermissions from '@shopgate/engage/core/actions/grantPushPermissions'; | ||
|
||
const mapDispatchToProps = { | ||
addBackInStockSubscription, | ||
grantPushPermissions, | ||
}; | ||
|
||
export default connect(null, mapDispatchToProps); |
95 changes: 95 additions & 0 deletions
95
libraries/engage/back-in-stock/components/BackInStockButton/index.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import React, { useCallback } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { themeConfig } from '@shopgate/engage'; | ||
import { | ||
Link, | ||
CheckedIcon, | ||
NotificationIcon, | ||
} from '@shopgate/engage/components'; | ||
import { BACK_IN_STOCK_PATTERN } from '@shopgate/engage/back-in-stock/constants'; | ||
import { i18n } from '@shopgate/engage/core'; | ||
import styles from './style'; | ||
import connect from './connector'; | ||
|
||
const { colors } = themeConfig; | ||
/** | ||
* This component renders a button to subscribe a product or a hint | ||
* that the product is already subscribed | ||
* @param {Object} props The component props | ||
* @param {boolean} props.isLinkToBackInStockEnabled Whether the link to the back in | ||
* stock page is active | ||
* @param {boolean} props.stopPropagation Stop event propagation | ||
* @param {string} props.productId The product id | ||
* @param {Object} props.subscription The subscription | ||
* @param {Function} props.addBackInStockSubscription Add product to back in stock list | ||
* @param {Function} props.grantPushPermissions Request / Set push permission | ||
* @return {JSX} | ||
*/ | ||
const BackInStockButton = ({ | ||
productId, | ||
isLinkToBackInStockEnabled = false, | ||
subscription, | ||
stopPropagation = false, | ||
addBackInStockSubscription, | ||
grantPushPermissions, | ||
}) => { | ||
const handleClick = useCallback(async (event) => { | ||
if (stopPropagation) { | ||
event.stopPropagation(); | ||
} | ||
const allowed = await grantPushPermissions({ | ||
rationaleModal: { | ||
message: 'permissions.back_in_stock_push_notifications.message', | ||
confirm: 'permissions.back_in_stock_push_notifications.confirm', | ||
dismiss: 'permissions.back_in_stock_push_notifications.dismiss', | ||
}, | ||
}); | ||
if (allowed) { | ||
addBackInStockSubscription({ productId }); | ||
} | ||
}, [addBackInStockSubscription, grantPushPermissions, productId, stopPropagation]); | ||
|
||
if (subscription?.status === 'active') { | ||
return ( | ||
<Link | ||
href={BACK_IN_STOCK_PATTERN} | ||
disabled={!isLinkToBackInStockEnabled} | ||
className={styles.backInStockMessageContainer} | ||
tag="span" | ||
> | ||
<CheckedIcon color={colors.success} className={styles.icon} /> | ||
<span className={styles.backInStockMessage}>{i18n.text('back_in_stock.we_will_remind_you')}</span> | ||
</Link> | ||
); | ||
} | ||
|
||
return ( | ||
<div> | ||
{/* eslint-disable-next-line max-len */} | ||
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid,jsx-a11y/interactive-supports-focus,jsx-a11y/click-events-have-key-events */} | ||
<a role="button" onClick={handleClick} className={styles.button}> | ||
<div className={styles.buttonContent}> | ||
<NotificationIcon color={colors.primary} /> | ||
<span className={styles.buttonText}>{i18n.text('back_in_stock.get_notified')}</span> | ||
</div> | ||
</a> | ||
</div>); | ||
}; | ||
|
||
BackInStockButton.propTypes = { | ||
addBackInStockSubscription: PropTypes.func.isRequired, | ||
grantPushPermissions: PropTypes.func.isRequired, | ||
isLinkToBackInStockEnabled: PropTypes.bool, | ||
productId: PropTypes.string, | ||
stopPropagation: PropTypes.bool, | ||
subscription: PropTypes.shape(), | ||
}; | ||
|
||
BackInStockButton.defaultProps = { | ||
stopPropagation: false, | ||
isLinkToBackInStockEnabled: false, | ||
subscription: null, | ||
productId: null, | ||
}; | ||
|
||
export default connect(BackInStockButton); |
33 changes: 33 additions & 0 deletions
33
libraries/engage/back-in-stock/components/BackInStockButton/style.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { css } from 'glamor'; | ||
import { themeConfig } from '@shopgate/engage'; | ||
|
||
export default { | ||
button: css({ | ||
color: themeConfig.colors.primary, | ||
}).toString(), | ||
buttonContent: css({ | ||
display: 'flex', | ||
alignItems: 'center', | ||
}).toString(), | ||
backInStockMessageContainer: css({ | ||
lineHeight: '20px', | ||
display: 'flex', | ||
alignItems: 'center', | ||
textAlign: 'end', | ||
width: 'auto', | ||
}).toString(), | ||
backInStockMessage: css({ | ||
marginLeft: '4px', | ||
verticalAlign: 'middle', | ||
fontSize: '0.875rem', | ||
}).toString(), | ||
buttonText: css({ | ||
marginLeft: '4px', | ||
fontSize: '0.875rem', | ||
lineHeight: '16.5px', | ||
}).toString(), | ||
icon: css({ | ||
verticalAlign: 'middle', | ||
display: 'inline', | ||
}).toString(), | ||
}; |
27 changes: 27 additions & 0 deletions
27
libraries/engage/back-in-stock/components/CharacteristicsButton/connector.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { connect } from 'react-redux'; | ||
import { | ||
getIsBackInStockEnabled, | ||
makeGetSubscriptionByCharacteristics, | ||
} from '@shopgate/engage/back-in-stock/selectors'; | ||
import { | ||
makeGetProductByCharacteristics, | ||
} from '@shopgate/engage/product'; | ||
|
||
/** | ||
* @return {Object} The extended component props. | ||
*/ | ||
const makeMapStateToProps = () => { | ||
const getProductByCharacteristics = makeGetProductByCharacteristics({ strict: true }); | ||
const getSubscriptionByCharacteristics = makeGetSubscriptionByCharacteristics(); | ||
|
||
return (state, props) => { | ||
const variant = getProductByCharacteristics(state, props) || {}; | ||
return ({ | ||
variant, | ||
subscription: getSubscriptionByCharacteristics(state, props), | ||
isBackInStockEnabled: getIsBackInStockEnabled(state, props), | ||
}); | ||
}; | ||
}; | ||
|
||
export default connect(makeMapStateToProps); |
52 changes: 52 additions & 0 deletions
52
libraries/engage/back-in-stock/components/CharacteristicsButton/index.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { BackInStockButton } from '@shopgate/engage/back-in-stock/components'; | ||
import { withCurrentProduct } from '@shopgate/engage/core'; | ||
import connect from './connector'; | ||
|
||
/** | ||
* The CharacteristicsButton component. | ||
* @param {Object} props The component props. | ||
* @param {boolean} props.isBackInStockEnabled Whether the back in stock feature is enabled | ||
* @param {Object} props.variant The variant for this entry | ||
* @param {Object} props.subscription The subscription | ||
* @return {JSX} | ||
*/ | ||
const CharacteristicsButton = ({ | ||
isBackInStockEnabled, | ||
subscription, | ||
variant, | ||
}) => { | ||
const productIsNotAVariant = !variant; | ||
const featureIsNotEnabled = !isBackInStockEnabled; | ||
const productIsNotAvailable = variant?.stock?.quantity === 0 && | ||
variant?.stock?.ignoreQuantity === false; | ||
|
||
if (productIsNotAVariant || featureIsNotEnabled || !productIsNotAvailable) return null; | ||
|
||
return ( | ||
<div style={{ | ||
display: 'flex', | ||
justifyContent: 'end', | ||
}} | ||
> | ||
<BackInStockButton | ||
subscription={subscription} | ||
stopPropagation | ||
productId={variant.id} | ||
/> | ||
</div>); | ||
}; | ||
|
||
CharacteristicsButton.propTypes = { | ||
isBackInStockEnabled: PropTypes.bool.isRequired, | ||
subscription: PropTypes.shape(), | ||
variant: PropTypes.shape(), | ||
}; | ||
|
||
CharacteristicsButton.defaultProps = { | ||
variant: {}, | ||
subscription: null, | ||
}; | ||
|
||
export default withCurrentProduct(connect(CharacteristicsButton)); |
22 changes: 22 additions & 0 deletions
22
libraries/engage/back-in-stock/components/ProductInfoBackInStockButton/connector.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { connect } from 'react-redux'; | ||
import { | ||
getIsBackInStockEnabled, | ||
makeGetSubscriptionByProduct, | ||
} from '@shopgate/engage/back-in-stock/selectors'; | ||
import { | ||
getProduct, | ||
} from '@shopgate/engage/product'; | ||
|
||
/** | ||
* @return {Object} The extended component props. | ||
*/ | ||
const makeMapStateToProps = () => { | ||
const getSubscriptionByProduct = makeGetSubscriptionByProduct(); | ||
return (state, props) => ({ | ||
subscription: getSubscriptionByProduct(state, props), | ||
isBackInStockEnabled: getIsBackInStockEnabled(state, props), | ||
product: getProduct(state, props), | ||
}); | ||
}; | ||
|
||
export default connect(makeMapStateToProps); |
Oops, something went wrong.