Created
May 6, 2022 15:40
-
-
Save ikraamg/3fa914a5a536597ac23e41ae2ec6fe22 to your computer and use it in GitHub Desktop.
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
diff --git a/package.json b/package.json | |
index 4b6438c..55622ef 100644 | |
--- a/package.json | |
+++ b/package.json | |
@@ -22,6 +22,7 @@ | |
"@stripe/terminal-js": "^0.9.0", | |
"babel-plugin-styled-components": "^1.10.7", | |
"copy-to-clipboard": "^3.3.1", | |
+ "date-fns": "^2.28.0", | |
"dotenv": "^10.0.0", | |
"humps": "^2.0.1", | |
"magic-sdk": "^8.0.0", | |
diff --git a/src/components/Generic/Ticker/index.jsx b/src/components/Generic/Ticker/index.jsx | |
new file mode 100644 | |
index 0000000..3bbc45a | |
--- /dev/null | |
+++ b/src/components/Generic/Ticker/index.jsx | |
@@ -0,0 +1,16 @@ | |
+import { useTicker } from '~/hooks/components/use-ticker' | |
+import { Text } from 'theme-ui' | |
+ | |
+export const Ticker = ({ futureDate, styles }) => { | |
+ const { days, hours, minutes, seconds } = useTicker(futureDate) | |
+ const tickerContents = ( | |
+ <> | |
+ {days > 0 && `${days} days, `} | |
+ {hours > 0 && `${hours} hours, `} | |
+ {minutes > 0 && `${minutes} minutes, `} | |
+ {`${seconds} seconds`} | |
+ </> | |
+ ) | |
+ | |
+ return <Text sx={styles}>{tickerContents}</Text> | |
+} | |
diff --git a/src/hooks/actions/use-cart.js b/src/hooks/actions/use-cart.js | |
index 1341e0e..50506bb 100644 | |
--- a/src/hooks/actions/use-cart.js | |
+++ b/src/hooks/actions/use-cart.js | |
@@ -67,6 +67,11 @@ export function useCart() { | |
[dispatch] | |
) | |
+ const crossSell = useMemo( | |
+ () => bindActionCreators(actions.crossSell, dispatch), | |
+ [dispatch] | |
+ ) | |
+ | |
return { | |
cart, | |
addPromoCode, | |
@@ -80,6 +85,7 @@ export function useCart() { | |
modifyGiftCards, | |
removeFromCart, | |
removePromoCode, | |
- subscribeProduct | |
+ subscribeProduct, | |
+ crossSell, | |
} | |
} | |
diff --git a/src/hooks/components/use-ticker.js b/src/hooks/components/use-ticker.js | |
new file mode 100644 | |
index 0000000..82a8254 | |
--- /dev/null | |
+++ b/src/hooks/components/use-ticker.js | |
@@ -0,0 +1,29 @@ | |
+import { useEffect, useState } from "react"; | |
+import { intervalToDuration, isBefore } from 'date-fns'; | |
+ | |
+export const useTicker = (futureDate) => { | |
+ const [now, setNow] = useState(new Date()); | |
+ | |
+ useEffect(() => { | |
+ const interval = setInterval(() => { | |
+ setNow(new Date()); | |
+ }, 1000); | |
+ | |
+ return () => { | |
+ clearInterval(interval); | |
+ }; | |
+ }, [futureDate]); | |
+ | |
+ const isTimeUp = isBefore(futureDate, now); | |
+ | |
+ if (isTimeUp) { | |
+ return { days: 0, hours: 0, minutes: 0, seconds: 0, isTimeUp }; | |
+ } | |
+ | |
+ let { days, hours, minutes, seconds } = intervalToDuration({ | |
+ start: now, | |
+ end: futureDate | |
+ }); | |
+ | |
+ return { days, hours, minutes, seconds, isTimeUp }; | |
+}; | |
\ No newline at end of file | |
diff --git a/src/redux/actions/cart/cross-sell.js b/src/redux/actions/cart/cross-sell.js | |
new file mode 100644 | |
index 0000000..be506df | |
--- /dev/null | |
+++ b/src/redux/actions/cart/cross-sell.js | |
@@ -0,0 +1,33 @@ | |
+import { | |
+ CROSS_SELL_ERROR, | |
+ CROSS_SELL_SUCCESS, | |
+ CROSS_SELL_REQUEST | |
+} from '~/redux/actions/types' | |
+ | |
+export const crossSellRequest = lineItemsAttributes => ({ | |
+ type: CROSS_SELL_REQUEST, | |
+ data: lineItemsAttributes | |
+}) | |
+ | |
+export const crossSellSuccess = (response) => ({ | |
+ type: CROSS_SELL_SUCCESS, | |
+ data: response | |
+}) | |
+ | |
+export const crossSellError = (error, meta = {}) => ({ | |
+ type: CROSS_SELL_ERROR, | |
+ error: true, | |
+ meta, | |
+ payload: error | |
+}) | |
+ | |
+export const crossSell = (cart, lineItemsAttributes) => | |
+ async (dispatch, { api }) => { | |
+ try { | |
+ dispatch(crossSellRequest(lineItemsAttributes)) | |
+ const response = await api.crossSell(cart, lineItemsAttributes) | |
+ dispatch(crossSellSuccess(response)) | |
+ } catch (error) { | |
+ dispatch(crossSellError(error)) | |
+ } | |
+ } | |
diff --git a/src/redux/actions/cart/index.js b/src/redux/actions/cart/index.js | |
index a4ac1dc..30b602e 100644 | |
--- a/src/redux/actions/cart/index.js | |
+++ b/src/redux/actions/cart/index.js | |
@@ -16,3 +16,4 @@ export * from './get-user-wallet' | |
export * from './update-order-addresses' | |
export * from './update-order-delivery' | |
export * from './update-order-payment' | |
+export * from './cross-sell' | |
diff --git a/src/redux/actions/types.js b/src/redux/actions/types.js | |
index 2fad0de..d5490f0 100644 | |
--- a/src/redux/actions/types.js | |
+++ b/src/redux/actions/types.js | |
@@ -108,6 +108,9 @@ export const UPDATE_ORDER_PAYMENT_ERROR = 'UPDATE_ORDER_PAYMENT_ERROR' | |
export const SEED_NOTIFICATIONS = 'SEED_NOTIFICATIONS' | |
export const ADD_NOTIFICATION = 'ADD_NOTIFICATION' | |
export const REMOVE_NOTIFICATION = 'REMOVE_NOTIFICATION' | |
+export const CROSS_SELL_ERROR = 'CROSS_SELL_ERROR' | |
+export const CROSS_SELL_SUCCESS = 'CROSS_SELL_SUCCESS' | |
+export const CROSS_SELL_REQUEST = 'CROSS_SELL_REQUEST' | |
const status = { | |
Idle: 'idle', | |
diff --git a/src/redux/reducers/cart.js b/src/redux/reducers/cart.js | |
index 6ed44b4..e4c9309 100644 | |
--- a/src/redux/reducers/cart.js | |
+++ b/src/redux/reducers/cart.js | |
@@ -40,7 +40,10 @@ import { | |
UPDATE_ORDER_DELIVERY_ERROR, | |
UPDATE_ORDER_PAYMENT_REQUEST, | |
UPDATE_ORDER_PAYMENT_SUCCESS, | |
- UPDATE_ORDER_PAYMENT_ERROR | |
+ UPDATE_ORDER_PAYMENT_ERROR, | |
+ CROSS_SELL_ERROR, | |
+ CROSS_SELL_SUCCESS, | |
+ CROSS_SELL_REQUEST, | |
} from '~/redux/actions/types' | |
import initialState from '~/redux/store/initial-state' | |
@@ -54,6 +57,7 @@ const cart = (state = initialState.cart, action) => { | |
case UPDATE_ORDER_ADDRESSES_REQUEST: | |
case UPDATE_ORDER_DELIVERY_REQUEST: | |
case UPDATE_ORDER_PAYMENT_REQUEST: | |
+ case CROSS_SELL_REQUEST: | |
return { | |
...state, | |
isFetching: true, | |
@@ -80,6 +84,7 @@ const cart = (state = initialState.cart, action) => { | |
case UPDATE_ORDER_ADDRESSES_SUCCESS: | |
case UPDATE_ORDER_DELIVERY_SUCCESS: | |
case UPDATE_ORDER_PAYMENT_SUCCESS: | |
+ case CROSS_SELL_SUCCESS: | |
return { | |
...state, | |
isFetching: false, | |
@@ -103,6 +108,7 @@ const cart = (state = initialState.cart, action) => { | |
case UPDATE_ORDER_ADDRESSES_ERROR: | |
case UPDATE_ORDER_DELIVERY_ERROR: | |
case UPDATE_ORDER_PAYMENT_ERROR: | |
+ case CROSS_SELL_ERROR: | |
return { | |
...state, | |
error: action.payload, | |
diff --git a/src/redux/reducers/cart.spec.js b/src/redux/reducers/cart.spec.js | |
index 190f8a2..53c1c34 100644 | |
--- a/src/redux/reducers/cart.spec.js | |
+++ b/src/redux/reducers/cart.spec.js | |
@@ -40,7 +40,10 @@ import { | |
UPDATE_ORDER_DELIVERY_ERROR, | |
UPDATE_ORDER_PAYMENT_REQUEST, | |
UPDATE_ORDER_PAYMENT_SUCCESS, | |
- UPDATE_ORDER_PAYMENT_ERROR | |
+ UPDATE_ORDER_PAYMENT_ERROR, | |
+ CROSS_SELL_ERROR, | |
+ CROSS_SELL_SUCCESS, | |
+ CROSS_SELL_REQUEST | |
} from '~/redux/actions/types' | |
import initialState from '~/redux/store/initial-state' | |
import { mockedFullCart } from '~/__mocks__/acs/cart_full' | |
@@ -726,4 +729,50 @@ describe('cart reducer', () => { | |
error | |
}) | |
}) | |
+ | |
+ it('should handle CROSS_SELL_REQUEST', () => { | |
+ expect( | |
+ cart( | |
+ {}, | |
+ { | |
+ type: CROSS_SELL_REQUEST | |
+ } | |
+ ) | |
+ ).toEqual({ | |
+ isFetching: true, | |
+ error: null | |
+ }) | |
+ }) | |
+ | |
+ it('should handle CROSS_SELL_SUCCESS', () => { | |
+ expect( | |
+ cart( | |
+ {}, | |
+ { | |
+ type: CROSS_SELL_SUCCESS, | |
+ data: {} | |
+ } | |
+ ) | |
+ ).toEqual({ | |
+ data: {}, | |
+ isFetching: false, | |
+ error: null | |
+ }) | |
+ }) | |
+ | |
+ it('should handle CROSS_SELL_ERROR', () => { | |
+ expect( | |
+ cart( | |
+ {}, | |
+ { | |
+ type: CROSS_SELL_ERROR, | |
+ error: true, | |
+ payload: error | |
+ } | |
+ ) | |
+ ).toEqual({ | |
+ isFetching: false, | |
+ error | |
+ }) | |
+ }) | |
}) | |
diff --git a/src/services/api/index.js b/src/services/api/index.js | |
index b24f5fe..1c6d4c0 100644 | |
--- a/src/services/api/index.js | |
+++ b/src/services/api/index.js | |
@@ -386,6 +386,25 @@ class Api { | |
const response = await axiosClient.post(url, props, config) | |
return response.data | |
} | |
+ | |
+ async crossSell(cart, lineItemsAttributes) { | |
+ const url = `/api/orders/${cart.number}/cross_sell` | |
+ const config = { | |
+ transformRequest: [ | |
+ (data, headers) => { | |
+ headers['X-Spree-Order-Token'] = cart.token | |
+ return JSON.stringify(data) | |
+ } | |
+ ] | |
+ } | |
+ const payload = { | |
+ order: { | |
+ line_items_attributes: lineItemsAttributes | |
+ } | |
+ } | |
+ const response = await axiosClient.put(url, payload, config) | |
+ return response.data | |
+ } | |
} | |
const api = new Api() | |
diff --git a/src/services/api/index.test.js b/src/services/api/index.test.js | |
index b4f722a..53f4933 100644 | |
--- a/src/services/api/index.test.js | |
+++ b/src/services/api/index.test.js | |
@@ -103,6 +103,26 @@ describe('api', () => { | |
}) | |
}) | |
+ describe('crossSell', () => { | |
+ const cart = { number: 'CART-ID', token: "test" } | |
+ const lineItemsAttributes = [{ "sku": "product-4", "quantity": 8 }] | |
+ const payload = { order: { line_items_attributes: lineItemsAttributes } } | |
+ | |
+ it('puts the request to the cart endpoint', () => { | |
+ axios.put = jest.fn().mockImplementation(() => { | |
+ return {} | |
+ }) | |
+ | |
+ api.crossSell(cart, lineItemsAttributes) | |
+ | |
+ expect(axios.put).toHaveBeenCalledWith( | |
+ `/api/orders/${cart.number}/cross_sell`, payload, { | |
+ transformRequest: expect.any(Array) | |
+ } | |
+ ) | |
+ }) | |
+ }) | |
+ | |
describe('getStates', () => { | |
it('calls the states endpoint', async () => { | |
const iso = 'US' | |
diff --git a/yarn.lock b/yarn.lock | |
index dda9b99..389878a 100644 | |
--- a/yarn.lock | |
+++ b/yarn.lock | |
@@ -3519,6 +3519,11 @@ data-urls@^2.0.0: | |
whatwg-mimetype "^2.3.0" | |
whatwg-url "^8.0.0" | |
+date-fns@^2.28.0: | |
+ version "2.28.0" | |
+ resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2" | |
+ integrity sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw== | |
+ | |
debug@4, debug@^4.1.0: | |
version "4.3.1" | |
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment