Skip to content

Instantly share code, notes, and snippets.

@SariSaar
Last active February 20, 2024 08:56
Show Gist options
  • Save SariSaar/5bf9a1b5272976c82a080986f7617a0b to your computer and use it in GitHub Desktop.
Save SariSaar/5bf9a1b5272976c82a080986f7617a0b to your computer and use it in GitHub Desktop.
Code examples for building a shopping cart in Sharetribe Web Template – Adding items to cart
import React from 'react';
import css from '../AddToCartButton/AddToCartButton.module.css';
import { Button } from '..';
const AddToCartButton = props => {
const {
listing,
count,
incrementCart,
isListingPage,
buttonLabel,
cartLabel,
isBooking = false,
showProductOrderForm = true,
isOwnListing = false,
} = props;
if (isBooking || !showProductOrderForm) {
return null;
}
const increaseCount = () => incrementCart(1);
const decreaseCount = () => incrementCart(-1);
const currentStock = listing?.currentStock?.attributes.quantity;
const isMaxItems = currentStock <= count;
if (!count || (count === 0 && currentStock > 0)) {
return (
<Button
onClick={() => {
if (!isOwnListing) increaseCount();
}}
>
{buttonLabel}
</Button>
);
} else {
return (
<div>
{isListingPage && (cartLabel)}
<div className={css.buttonContainer}>
<Button className={css.smallButton} onClick={decreaseCount}>
-
</Button>
<span className={css.countContainer}>{count}</span>
<Button className={css.smallButton} disabled={isMaxItems} onClick={increaseCount}>
+
</Button>
</div>
</div>
);
}
};
AddToCartButton.defaultProps = {
isListingPage: false,
};
export default AddToCartButton;
.smallButton {
height: 15px;
width: 35px;
margin: 10px;
}
.buttonContainer {
display: flex;
flex-direction: row;
justify-content: center;
}
.countContainer {
align-self: center;
}
{
"AddToCartButton.cartLabel": "Cart:",
"AddToCartButton.label": "Add to cart"
}
/**
* This is a simplified version of the toggleCart function
*/
import { fetchCurrentUser, fetchCurrentUserHasOrdersSuccess, currentUserShowSuccess } from '../../ducks/user.duck';
...
// Add or remove items from cart
export const toggleCart = (listingId, authorId, increment = 1) => (dispatch, getState, sdk) => {
const currentUser = getState().user.currentUser;
const cart = currentUser.attributes.profile.privateData?.cart || [];
// Cart as object with author ids as keys
let newCart = getNewCart(cart, authorId, listingId, increment);
sdk.currentUser
.updateProfile(
{
privateData: {
cart: newCart,
},
},
{ expand: true }
)
.then(resp => {
const entities = denormalisedResponseEntities(resp);
if (entities.length !== 1) {
throw new Error('Expected a resource in the sdk.currentUser.updateProfile response');
}
const currentUser = entities[0];
// Update current user in state.user.currentUser through user.duck.js
dispatch(currentUserShowSuccess(currentUser));
})
.catch(e => {
console.log('e', e)
});
};
const listingIsInCart = (cart, authorId, listingId) => {
if (!cart || !cart[authorId]) {
return false;
}
return Object.keys(cart[authorId]).includes(listingId);
};
const getNewCart = (cart, authorId, listingId, increment) => {
const authorInCart = Object.keys(cart).includes(authorId);
const isListingInCart = listingIsInCart(cart, authorId, listingId);
const newCount = ((cart[authorId] && cart[authorId][listingId]?.count) || 0) + increment;
// Increment an existing listing
if (authorInCart && isListingInCart && newCount > 0) {
return {
...cart,
[authorId]: {
...cart[authorId],
[listingId]: {
count: newCount,
},
},
};
// Remove an existing listing from cart
} else if (authorInCart && isListingInCart && newCount <= 0) {
const newCart = { ...cart };
delete newCart[authorId][listingId];
// If the listing was the author's last one, remove the author as well
if (Object.keys(newCart[authorId]).length == 0) {
delete newCart[authorId];
}
return newCart;
// Add new listing to an existing author
} else if (authorInCart && !isListingInCart) {
return {
...cart,
[authorId]: {
...cart[authorId],
[listingId]: {
count: increment,
},
},
};
// Add new listing and a new author
} else {
return {
...cart,
[authorId]: {
[listingId]: {
count: increment,
},
},
};
}
};
export const handleAddToCart = parameters => value => {
const { currentUser, routes, history, location, onToggleCart, listingId, authorId } = parameters;
if (currentUser === null) {
const state = { from: `${location.pathname}${location.search}${location.hash}` };
history.push(createResourceLocatorString('LoginPage', routes, {}, {}), state);
} else {
onToggleCart(listingId, authorId, value);
}
};
import {
...,
handleAddToCart
} from './ListingPage.shared';
import {
...,
toggleCart,
} from './ListingPage.duck';
export const ListingPageComponent = props => {
...
const {
...,
onToggleCart,
} = props;
...
// Cart functionality – add this within the component but before the return statement
const onAddToCart = handleAddToCart({
...commonParams,
location,
currentUser,
onToggleCart,
listingId: listingId.uuid,
authorId: currentAuthor.id.uuid,
});
const cart = currentUser?.attributes?.profile?.privateData?.cart;
const listingCartCount =
(cart &&
cart[(currentAuthor?.id.uuid)] &&
cart[(currentAuthor?.id.uuid)][(currentListing?.id?.uuid)]?.count) ||
0;
const cartProps = {
listing: currentListing,
count: listingCartCount,
incrementCart: onAddToCart,
isListingPage: true,
isOwnListing,
};
// cart functionality ends
...
<OrderPanel
...
cartProps={cartProps} // Add cartProps to OrderPanel
/>
...
const mapDispatchToProps = dispatch => ({
...,
onToggleCart: (listingId, authorId, increment) =>
dispatch(toggleCart(listingId, authorId, increment)),
});
import {
...
AddToCartButton,
} from '../../components';
...
const OrderPanel = props => {
const {
...
cartProps,
} = props;
...
) : showProductOrderForm ? (
<AddToCartButton
showProductOrderForm={showProductOrderForm}
isBooking={isBooking}
buttonLabel={<FormattedMessage id="AddToCartButton.label" />}
cartLabel={<FormattedMessage id="AddToCartButton.cartLabel" />}
{...cartProps}
/>
) : showInquiryForm ? (
...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment