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

Initial commit of Salesforce Commerce Cloud app. #3633

Merged
merged 2 commits into from
Jun 1, 2023

Conversation

jsdalton
Copy link
Member

@jsdalton jsdalton commented Jun 1, 2023

Note: Reopens #3620 to workaround permission issue.

Purpose

This app provides an integration with Salesforce Commerce Cloud storefronts to pick products and categories within the Contentful UI.

Approach

  • Created a new Salesforce Commerce Cloud app via npx create-contentful-app in the apps directory of the repository.
  • App uses components for Field, Configuration, and Dialog locations
  • Created an Sfcc utility to facilitate interaction with Salesforce Commerce Cloud apis.

Dependencies and/or References

https://developer.salesforce.com/docs/commerce/commerce-api/overview

Deployment

App is currently hosted on Contentful.

@jsdalton jsdalton requested a review from a team June 1, 2023 14:23
);
};

const CategorySearchResult = (props: SearchResultProps) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CategorySearchResult could be broken out into it's own file just to keep this file a bit more focused

headerHeight,
} from './SearchPicker';

const CategorySearchResults = (props: SearchResultsProps) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel it would be good to destructure the props here for consistency

headerHeight,
} from './SearchPicker';

const CategorySearchResults = (props: SearchResultsProps) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel it would be good to destructure the props here for consistency

Comment on lines +9 to +15
let description = 'Description';
if (product.shortDescription.default) {
description =
product.shortDescription.default.markup.length > descriptionLength
? `${product.shortDescription.default.markup.substring(0, descriptionLength)}...`
: product.shortDescription.default.markup;
}
Copy link
Contributor

@rusticpenguin rusticpenguin Jun 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let description = 'Description';
if (product.shortDescription.default) {
description =
product.shortDescription.default.markup.length > descriptionLength
? `${product.shortDescription.default.markup.substring(0, descriptionLength)}...`
: product.shortDescription.default.markup;
}
const formatShortDescription = (productMarkup?: string) => {
if (!productMarkup) {
return '';
}
if (productMarkup.length > descriptionLength) {
return `${productMarkup.substring(0, descriptionLength)}...`;
}
return productMarkup;
}
const description = formatShortDescription(product.shortDescription.default?.markup) || 'Description';

Changing this over to a const and using a function with a return value could help make this section a little faster to read

);
};

const ProductSearchResult = (props: SearchResultProps) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as before, I feel this would be nicer to move into it's own file for separation of concerns

stickyHeaderBreakpoint,
} from './SearchPicker';

const ProductSearchResults = (props: SearchResultsProps) => {
Copy link
Contributor

@rusticpenguin rusticpenguin Jun 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel it would be good to destructure the props here for consistency


return (
<Flex css={productWrapperStyles}>
<div
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of divs, could we use <box> and move a lot of the styling into the component?

}

// eslint-disable-next-line @typescript-eslint/no-empty-function
const noop = () => {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

love the declared noop

}
`;

const controlProps = { ...props, fieldType };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

beautifully written!

@@ -0,0 +1,292 @@
import React, { useState } from 'react';
Copy link
Contributor

@rusticpenguin rusticpenguin Jun 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we create a SearchBar folder, and break these out into separate files? This one is a big one!

Also similar comments as earlier with consistently destructing props when you're using them in the component (eg. SearchBar doesn't need to because it's just passing props, but the subcomponents who are using them would make it easier to read the file)

Comment on lines +21 to +27
const Component = useMemo(() => {
for (const [location, component] of Object.entries(ComponentLocationSettings)) {
if (sdk.location.is(location)) {
return component;
}
}
}, [sdk.location]);
Copy link
Contributor

@rusticpenguin rusticpenguin Jun 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like this may be better suited for a useEffect, since we want this hooked into the react lifecycle. (You may need to tweak the typing on useState)

Suggested change
const Component = useMemo(() => {
for (const [location, component] of Object.entries(ComponentLocationSettings)) {
if (sdk.location.is(location)) {
return component;
}
}
}, [sdk.location]);
const [Component, setComponent] = useState<ReactElement>();
useEffect(() => {
for (const [location, component] of Object.entries(ComponentLocationSettings)) {
if (sdk.location.is(location)) {
setComponent(component)
}
}
}, [sdk.location]);

headerHeight,
} from './SearchPicker';

const CategorySearchResults = (props: SearchResultsProps) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel it would be good to destructure the props here for consistency

}
}, 500);
return () => clearTimeout(timeOutId);
}, [fieldType, query, installParameters]);
Copy link
Contributor

@rusticpenguin rusticpenguin Jun 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw we setup the ReactQuery provider in the App.tsx, could we use the queryClient here instead?

margin-right: ${tokens.spacingM};
`;

const SelectItemAction = (props: { fieldValue?: string | string[] }) => {
Copy link
Contributor

@rusticpenguin rusticpenguin Jun 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel it would be good to destructure the props here for consistency, also creating an interface for the component

@@ -0,0 +1,173 @@
import React from 'react';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file could be broken up into it's own folder

};

return sdk.field.onValueChanged(valueChangeHandler);
}, [sdk.field]);
Copy link
Contributor

@rusticpenguin rusticpenguin Jun 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just learned this the other day! Check out: useResourceValue! This takes care of line 21 - 29

Copy link
Contributor

@rusticpenguin rusticpenguin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks amazing! All my comments are structure/code readability related. The actual logic is solid

Comment on lines +32 to +68
const queryArray: string[] = [];
if (props.fieldValue) {
props.fieldValue instanceof Array
? queryArray.push(...props.fieldValue)
: queryArray.push(props.fieldValue);
}

const client = new SfccClient(installParameters);
const currentItemQueries = useQueries({
queries: queryArray.map((id: string) => {
const [itemId, catalogId] = id.split(':');
return {
queryKey: ['itemInfo', itemId],
queryFn:
fieldType === 'product'
? () => client.fetchProduct(itemId)
: () => client.fetchCategory(itemId, catalogId),
};
}),
});

const queriesComplete = currentItemQueries.every((query) => query.isSuccess || query.isError);

useEffect(() => {
if (queriesComplete) {
const updatedData: any[] = [];
for (const query of currentItemQueries) {
if (query.isSuccess) {
updatedData.push(query.data);
}
}

if (updatedData.length) {
setCurrentData(updatedData);
}
}
}, [queriesComplete]);
Copy link
Contributor

@rusticpenguin rusticpenguin Jun 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel moving lines 32 - 68 into a hook would make this file easier to digest

Copy link
Contributor

@rusticpenguin rusticpenguin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created a ticket for feedback

Copy link
Contributor

@rusticpenguin rusticpenguin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created a ticket for feedback

@jsdalton jsdalton merged commit 359da57 into master Jun 1, 2023
@jsdalton jsdalton deleted the jgrahamdev/salesforce-app branch June 1, 2023 17:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants