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

Back in stock feature #1211

Merged
merged 58 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
6c9dbc8
Add back in stock
Jan 17, 2024
a4a03e7
CURB-3886 Some fixes and rework.
Feb 2, 2024
82f2238
CURB-3886 Remove / rename backinstock constants
Feb 7, 2024
f074f26
CURB-3886 Rename productCode to productId
Feb 7, 2024
327ac83
CURB-3886 Refactor back-in-stock components folder
Feb 7, 2024
42e6939
CURB-3886 Refactor theme subscribers
Feb 7, 2024
891140c
CURB-3886 Refactor gmd nav_menu portals
Feb 7, 2024
80de111
CURB-3886 Refactor back on stock portal constants
Feb 7, 2024
d5df4e5
CURB-3886 Move groupedLabels in BackInStockSubscriptionsProvider
Feb 7, 2024
b3cab93
CURB-3886 Refactor backinstock actions.
Feb 7, 2024
78add06
CURB-3886 Remove back in stock typo
Feb 7, 2024
e0b4ffc
CURB-3886 Remove typo
Feb 12, 2024
6fe5cb7
CURB-3886 Refactor imports
Feb 12, 2024
123f4f1
CURB-3886 Refactor ui-shared backinstock component
Feb 12, 2024
2f915e7
CURB-3886 Implement withCurrentProduct in BackInStockButtonPortal
Feb 13, 2024
7c74927
CURB-3886 Merge ios/gmd characteristics
Feb 13, 2024
7c3b099
CURB-3886 Move characteristics button
Feb 13, 2024
6fdeba7
CURB-3886 Refactor Characteristics Button
Feb 13, 2024
92f6d92
CURB-3886 Add fallback icon
Feb 13, 2024
b643dcf
CURB-3886 Refactor CharacteristicsButton connector
Feb 13, 2024
24ab274
CURB-3886 Refactor NavDrawer backInStockButton
Feb 13, 2024
d517773
CURB-3886 Small improvements
Feb 14, 2024
b12d903
CURB-3886 Refactor getProductType
Feb 14, 2024
0b2ce6e
CURB-3886 Refactor backInStock index
Feb 14, 2024
1170838
CURB-3886 Improve useBackInStockSubscriptionsContext documentation
Feb 14, 2024
39e71f6
CURB-3886 Rename useBackInStockSubscriptions
Feb 14, 2024
033c189
CURB-3886 Refactor getSubscriptionByVariant
Feb 14, 2024
4308849
CURB-3886 Refactor getSubscriptionByCharacteristics
Feb 14, 2024
3b3e09e
Merge branch 'master' into CURB-3886_add_back_in_stock
Feb 14, 2024
08752d1
CURB-3886 Refactor grantPermissions and grantPushPermissions
Feb 14, 2024
2f64985
CURB-3886 Update translations
Feb 14, 2024
0248e8c
CURB-3886 Update SheetItem propTypes
Feb 16, 2024
153f580
CURB-3886 GetIsBackInStockEnabled based on theme setting
Feb 16, 2024
5b244b4
CURB-3886 Review Feedback
Feb 26, 2024
676e335
CURB-3886 Review Feedback
Feb 26, 2024
ca927b7
Merge remote-tracking branch 'origin/CURB-3886_add_back_in_stock' int…
Feb 26, 2024
2390f1f
CURB-3886 Improved NavDrawer Item styling for long labels
fkloes Feb 26, 2024
5b06eda
CURB-3886 Refactor backInStock actions
Feb 26, 2024
eb37479
CURB-3886 Typo in extension-config.json
Feb 27, 2024
06a25d0
CURB-3886 Adjust some imports
Feb 27, 2024
f65ce41
CURB-3886 Adjust translations
Feb 27, 2024
3e79373
Merge remote-tracking branch 'origin/CURB-3886_add_back_in_stock' int…
Feb 27, 2024
387c2ef
CURB-3886 Review Feedback. Rename BackInStockButtonPortal
Feb 27, 2024
dc58d5d
CURB-3886 Review Feedback.
Feb 27, 2024
8deb0bf
CURB-3886 Review Feedback. Move onclick action in BackInStockButton
Feb 27, 2024
29feed1
CURB-3886 Review Feedback. Adjust constants
Feb 27, 2024
c4ea41d
CURB-3886 Fetch only active / triggered subscriptions
Feb 27, 2024
058e925
CURB-3886 Even more review feedback
Feb 27, 2024
9ed45f7
CURB-3886 Adjust translations
Feb 27, 2024
ca8935f
CURB-3886 Some fixes
Feb 27, 2024
9377dac
CURB-3886 Back in Stock feature index
Feb 27, 2024
c6c6f09
CURB-3886 Adjust translations
Feb 28, 2024
fb2ac45
CURB-3886 Clear console errors
Feb 28, 2024
c589d38
CURB-3886 More styling
Feb 29, 2024
78083eb
CURB-3886 Fix makeGetProductByCharacteristics
Mar 1, 2024
e6374bb
CURB-3886 Fix close icon on subscription list
Mar 1, 2024
69e866b
CURB-3886 Final review fixes ???
Mar 1, 2024
d88cb86
CURB-3886 Fixed some unit tests
fkloes Mar 1, 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
113 changes: 113 additions & 0 deletions libraries/engage/back-in-stock/actions/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { PipelineRequest } from '@shopgate/engage/core';
import {
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 '../constants';
import {
SHOPGATE_USER_ADD_BACK_IN_STOCK_SUBSCRIPTION,
SHOPGATE_USER_DELETE_BACK_IN_STOCK_SUBSCRIPTION,
SHOPGATE_USER_GET_BACK_IN_STOCK_SUBSCRIPTIONS,
} from '../constants/Pipelines';

/**
* Fetch Back in Stock Subscriptions
* @returns {Function}
*/
export const fetchBackInStockSubscriptions = () => async (dispatch) => {
dispatch({ type: FETCH_BACK_IN_STOCK_SUBSCRIPTIONS });

try {
const { subscriptions } =
fkloes marked this conversation as resolved.
Show resolved Hide resolved
await new PipelineRequest(SHOPGATE_USER_GET_BACK_IN_STOCK_SUBSCRIPTIONS)
.setInput({
SaschaKrist marked this conversation as resolved.
Show resolved Hide resolved
limit: 100,
})
.dispatch();

dispatch({
type: FETCH_BACK_IN_STOCK_SUBSCRIPTIONS_SUCCESS,
subscriptions,
});

return subscriptions;
} catch (error) {
dispatch({
type: FETCH_BACK_IN_STOCK_SUBSCRIPTIONS_ERROR,
error,
});

return null;
}
};

/**
* 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 }) => async (dispatch) => {
dispatch({ type: ADD_BACK_IN_STOCK_SUBSCRIPTION });

try {
const { subscriptions } =
await new PipelineRequest(SHOPGATE_USER_ADD_BACK_IN_STOCK_SUBSCRIPTION)
.setInput({
productCode: productId,
})
.dispatch();

dispatch({
type: ADD_BACK_IN_STOCK_SUBSCRIPTION_SUCCESS,
subscriptions,
});
return subscriptions;
} catch (error) {
dispatch({
type: ADD_BACK_IN_STOCK_SUBSCRIPTION_ERROR,
error,
});

return null;
}
};

/**
* 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 }) => async (dispatch) => {
dispatch({ type: REMOVE_BACK_IN_STOCK_SUBSCRIPTION });

try {
const { subscriptions } =
await new PipelineRequest(SHOPGATE_USER_DELETE_BACK_IN_STOCK_SUBSCRIPTION)
.setInput({
subscriptionCode,
})
.dispatch();

dispatch({
type: REMOVE_BACK_IN_STOCK_SUBSCRIPTION_SUCCESS,
subscriptions,
});
return subscriptions;
} catch (error) {
dispatch({
type: REMOVE_BACK_IN_STOCK_SUBSCRIPTION_ERROR,
error,
});

return null;
}
};

Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React 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';
import { i18n } from '@shopgate/engage/core';
import styles from './style';

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 {Object} props.subscription The subscription
* @param {Function} props.onClick Action to subscribe the product
* @return {JSX}
*/
const BackInStockButton = ({
onClick,
isLinkToBackInStockEnabled = false,
subscription,
}) => {
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={onClick} 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 = {
onClick: PropTypes.func.isRequired,
isLinkToBackInStockEnabled: PropTypes.bool,
subscription: PropTypes.shape(),
};

BackInStockButton.defaultProps = {
isLinkToBackInStockEnabled: false,
subscription: null,
};

export default BackInStockButton;
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
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({
display: 'inline',
alignItems: 'center',
textAlign: 'end',
lineHeight: '20px',
}).toString(),
backInStockMessage: css({
marginLeft: '4px',
verticalAlign: 'middle',
fontSize: '0.875rem',
}).toString(),
buttonText: css({
marginLeft: '4px',
fontSize: '0.875rem',
}).toString(),
icon: css({
verticalAlign: 'middle',
display: 'inline',
}).toString(),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { connect } from 'react-redux';
import {
getIsBackInStockEnabled,
makeGetSubscriptionByProduct,
} from '@shopgate/engage/back-in-stock';
import { addBackInStockSubscription } from '@shopgate/engage/back-in-stock/actions';
import {
getProductAvailability,
makeGetProductType,
} from '@shopgate/engage/product';
import { grantPushPermissions } from '@shopgate/engage/core';

/**
* @return {Object} The extended component props.
*/
const makeMapStateToProps = () => {
const getProductType = makeGetProductType();
const getSubscriptionByProduct = makeGetSubscriptionByProduct();
return (state, props) => ({
subscription: getSubscriptionByProduct(state, props),
productType: getProductType(state, props),
stock: getProductAvailability(state, props),
isBackInStockEnabled: getIsBackInStockEnabled(state, props),
});
};

const mapDispatchToProps = {
addBackInStockSubscription,
grantPushPermissions,
};

export default connect(makeMapStateToProps, mapDispatchToProps);
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { Portal } from '@shopgate/engage/components';
import { AVAILABILITY_STATE_OK } from '@shopgate/engage/product';
import {
BackInStockButton,
PRODUCT_BACK_IN_STOCK,
PRODUCT_BACK_IN_STOCK_AFTER,
PRODUCT_BACK_IN_STOCK_BEFORE,
} from '@shopgate/engage/back-in-stock';

import { withCurrentProduct } from '@shopgate/engage/core';
import connect from './connector';

/**
* The BackInStockButtonPortal component.
* @param {Object} props The component props.
* @param {boolean} props.isBackInStockEnabled Whether the back in stock feature is enabled
* @param {string} props.productId The product id
* @param {string} props.variantId The variant id
* @param {string} props.productType The product type
* @param {Object} props.stock The product stock info
* @param {Function} props.addBackInStockSubscription Add product to back in stock list
* @param {Function} props.grantPushPermissions Request / Set push permission
* @param {Object} props.subscription The subscription
* @return {JSX}
*/
const BackInStockButtonPortal = ({
productType,
stock,
productId,
variantId,
addBackInStockSubscription,
isBackInStockEnabled,
grantPushPermissions,
subscription,
}) => {
const showBackInStock = productType !== 'parent' &&
SaschaKrist marked this conversation as resolved.
Show resolved Hide resolved
SaschaKrist marked this conversation as resolved.
Show resolved Hide resolved
productType !== null &&
stock?.state !== AVAILABILITY_STATE_OK &&
isBackInStockEnabled;

return (
<Fragment>
<Portal name={PRODUCT_BACK_IN_STOCK_BEFORE} />
<Portal name={PRODUCT_BACK_IN_STOCK}>
{showBackInStock &&
<BackInStockButton
subscription={subscription}
isLinkToBackInStockEnabled
onClick={async () => {
SaschaKrist marked this conversation as resolved.
Show resolved Hide resolved
const allowed = await grantPushPermissions();
if (allowed) {
addBackInStockSubscription({ productId: variantId ?? productId });
}
}}
/>}
</Portal>
<Portal name={PRODUCT_BACK_IN_STOCK_AFTER} />
</Fragment>
);
};

BackInStockButtonPortal.propTypes = {
addBackInStockSubscription: PropTypes.func.isRequired,
grantPushPermissions: PropTypes.func.isRequired,
isBackInStockEnabled: PropTypes.bool.isRequired,
productId: PropTypes.string.isRequired,
productType: PropTypes.string,
stock: PropTypes.shape(),
subscription: PropTypes.shape(),
variantId: PropTypes.string,
};

BackInStockButtonPortal.defaultProps = {
subscription: null,
productType: null,
variantId: null,
stock: null,
};

export default withCurrentProduct(connect(BackInStockButtonPortal));
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { connect } from 'react-redux';
import {
getIsBackInStockEnabled,
makeGetSubscriptionByCharacteristics,
} from '@shopgate/engage/back-in-stock';
import {
getVariantAvailabilityByCharacteristics,
} from '@shopgate/pwa-common-commerce/product';
import { addBackInStockSubscription } from '@shopgate/engage/back-in-stock/actions';
import grantPushPermissions from '@shopgate/engage/core/actions/grantPushPermissions';
import {
makeGetProductByCharacteristics,
} from '@shopgate/engage/product';

/**
* @return {Object} The extended component props.
*/
const makeMapStateToProps = () => {
const getProductByCharacteristics = makeGetProductByCharacteristics();
SaschaKrist marked this conversation as resolved.
Show resolved Hide resolved
const getSubscriptionByCharacteristics = makeGetSubscriptionByCharacteristics();

return (state, props) => {
const variant = getProductByCharacteristics(state, props) || {};
return ({
variant,
availability: getVariantAvailabilityByCharacteristics(state, props),
subscription: getSubscriptionByCharacteristics(state, props),
isBackInStockEnabled: getIsBackInStockEnabled(state, props),
});
};
};

const mapDispatchToProps = {
addBackInStockSubscription,
grantPushPermissions,

};

export default connect(makeMapStateToProps, mapDispatchToProps);
Loading