@riosst100/pwa-marketplace 1.3.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/package.json +1 -1
  2. package/src/componentOverrideMapping.js +6 -0
  3. package/src/components/OperatingHours/operatingHours.js +7 -9
  4. package/src/components/Pagination/index.js +27 -29
  5. package/src/components/SellerDetail/sellerDetail.js +2 -13
  6. package/src/components/SellerInformation/sellerInformation.js +2 -1
  7. package/src/components/SellerLocation/sellerLocation.js +1 -1
  8. package/src/components/SellerReview/sellerReview.js +18 -10
  9. package/src/components/SellerReviewItem/sellerReviewItem.js +105 -20
  10. package/src/components/ShopByCategory/index.js +2 -0
  11. package/src/components/ShopByCategory/shopByCategory.js +69 -0
  12. package/src/components/ShopByCategory/shopByCategory.module.css +58 -0
  13. package/src/components/ShopByCategory/shopByCategory.shimmer.js +24 -0
  14. package/src/components/SubCategory/subCategory.js +31 -0
  15. package/src/overwrites/peregrine/lib/talons/Breadcrumbs/useBreadcrumbs.js +100 -0
  16. package/src/overwrites/peregrine/lib/talons/MegaMenu/megaMenu.gql.js +70 -0
  17. package/src/overwrites/peregrine/lib/talons/MegaMenu/useMegaMenu.js +199 -0
  18. package/src/overwrites/peregrine/lib/talons/RootComponents/Category/categoryContent.gql.js +70 -0
  19. package/src/overwrites/peregrine/lib/talons/RootComponents/Category/useCategoryContent.js +141 -0
  20. package/src/overwrites/venia-ui/lib/RootComponents/Category/categoryContent.js +222 -0
  21. package/src/overwrites/venia-ui/lib/components/Adapter/adapter.js +5 -2
  22. package/src/overwrites/venia-ui/lib/components/FilterModal/FilterList/filterDefault.js +84 -0
  23. package/src/overwrites/venia-ui/lib/components/FilterSidebar/filterSidebar.js +157 -0
  24. package/src/overwrites/venia-ui/lib/components/MegaMenu/megaMenu.js +2 -1
  25. package/src/overwrites/venia-ui/lib/components/MegaMenu/megaMenuItem.js +1 -0
  26. package/src/overwrites/venia-ui/lib/components/MegaMenu/shopByColumn.js +75 -0
  27. package/src/overwrites/venia-ui/lib/components/MegaMenu/shopByColumn.module.css +28 -0
  28. package/src/overwrites/venia-ui/lib/components/MegaMenu/submenu.js +31 -0
  29. package/src/overwrites/venia-ui/lib/components/MegaMenu/submenuColumn.js +1 -0
  30. package/src/overwrites/venia-ui/lib/components/SearchBar/searchField.js +1 -1
  31. package/src/talons/MegaMenu/megaMenu.gql.js +70 -0
  32. package/src/talons/Seller/seller.gql.js +0 -36
  33. package/src/talons/SellerReview/sellerReview.gql.js +53 -0
  34. package/src/talons/SellerReview/useSellerReview.js +49 -0
  35. package/src/talons/ShopByCategory/index.js +1 -0
  36. package/src/talons/ShopByCategory/shopByCategory.gql.js +38 -0
  37. package/src/talons/ShopByCategory/useShopByCategory.js +69 -0
  38. package/src/talons/SubCategory/subCategory.gql.js +15 -0
  39. package/src/talons/SubCategory/useSubCategory.js +52 -0
@@ -0,0 +1,100 @@
1
+ import { useMemo } from 'react';
2
+ import { useQuery } from '@apollo/client';
3
+ import { useInternalLink } from '@magento/peregrine/lib/hooks/useInternalLink';
4
+
5
+ import mergeOperations from '@magento/peregrine/lib/util/shallowMerge';
6
+
7
+ import DEFAULT_OPERATIONS from '@magento/peregrine/lib/talons/Breadcrumbs/breadcrumbs.gql';
8
+
9
+ // Just incase the data is unsorted, lets sort it.
10
+
11
+ /* CUSTOM: Fix sorting */
12
+ const sortCrumbs = (a, b) => a.category_level - b.category_level;
13
+
14
+ // Generates the path for the category.
15
+ const getPath = (path, suffix) => {
16
+ if (path) {
17
+ return `/${path}${suffix || ''}`;
18
+ }
19
+
20
+ // If there is no path this is just a dead link.
21
+ return '#';
22
+ };
23
+
24
+ /**
25
+ * Returns props necessary to render a Breadcrumbs component.
26
+ *
27
+ * @param {object} props
28
+ * @param {object} props.query - the breadcrumb query
29
+ * @param {string} props.categoryId - the id of the category for which to generate breadcrumbs
30
+ * @return {{
31
+ * currentCategory: string,
32
+ * currentCategoryPath: string,
33
+ * isLoading: boolean,
34
+ * normalizedData: array,
35
+ * handleClick: function
36
+ * }}
37
+ */
38
+ export const useBreadcrumbs = props => {
39
+ const { categoryId } = props;
40
+
41
+ const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations);
42
+ const { getBreadcrumbsQuery, getStoreConfigQuery } = operations;
43
+
44
+ const { data, loading, error } = useQuery(getBreadcrumbsQuery, {
45
+ variables: { category_id: categoryId },
46
+ fetchPolicy: 'cache-and-network',
47
+ nextFetchPolicy: 'cache-first'
48
+ });
49
+
50
+ const { data: storeConfigData } = useQuery(getStoreConfigQuery, {
51
+ fetchPolicy: 'cache-and-network'
52
+ });
53
+
54
+ const categoryUrlSuffix = useMemo(() => {
55
+ if (storeConfigData) {
56
+ return storeConfigData.storeConfig.category_url_suffix;
57
+ }
58
+ }, [storeConfigData]);
59
+
60
+ // When we have breadcrumb data sort and normalize it for easy rendering.
61
+ const normalizedData = useMemo(() => {
62
+ if (!loading && data && data.categories.items.length) {
63
+ const breadcrumbData = data.categories.items[0].breadcrumbs;
64
+
65
+ return (
66
+ breadcrumbData &&
67
+ breadcrumbData
68
+ .map(category => ({
69
+ category_level: category.category_level,
70
+ text: category.category_name,
71
+ path: getPath(
72
+ category.category_url_path,
73
+ categoryUrlSuffix
74
+ )
75
+ }))
76
+ .sort(sortCrumbs)
77
+ );
78
+ }
79
+ }, [categoryUrlSuffix, data, loading]);
80
+
81
+ const { setShimmerType } = useInternalLink('category');
82
+
83
+ return {
84
+ currentCategory:
85
+ (data &&
86
+ data.categories.items.length &&
87
+ data.categories.items[0].name) ||
88
+ '',
89
+ currentCategoryPath:
90
+ (data &&
91
+ data.categories.items.length &&
92
+ `${data.categories.items[0].url_path}${categoryUrlSuffix ||
93
+ ''}`) ||
94
+ '#',
95
+ isLoading: loading,
96
+ hasError: !!error,
97
+ normalizedData: normalizedData || [],
98
+ handleClick: setShimmerType
99
+ };
100
+ };
@@ -0,0 +1,70 @@
1
+ import { gql } from '@apollo/client';
2
+ export const GET_STORE_CONFIG_DATA = gql`
3
+ query GetStoreConfigForMegaMenu {
4
+ # eslint-disable-next-line @graphql-eslint/require-id-when-available
5
+ storeConfig {
6
+ store_code
7
+ category_url_suffix
8
+ }
9
+ }
10
+ `;
11
+
12
+ export const GET_MEGA_MENU = gql`
13
+ query getMegaMenu {
14
+ # eslint-disable-next-line @graphql-eslint/require-id-when-available
15
+ categoryList {
16
+ uid
17
+ name
18
+ # eslint-disable-next-line @graphql-eslint/require-id-when-available
19
+ children {
20
+ uid
21
+ include_in_menu
22
+ name
23
+ position
24
+ url_path
25
+ shop_by {
26
+ name
27
+ items {
28
+ name
29
+ url_path
30
+ }
31
+ }
32
+ # eslint-disable-next-line @graphql-eslint/require-id-when-available
33
+ children {
34
+ uid
35
+ include_in_menu
36
+ name
37
+ position
38
+ url_path
39
+ shop_by {
40
+ name
41
+ items {
42
+ name
43
+ url_path
44
+ }
45
+ }
46
+ # eslint-disable-next-line @graphql-eslint/require-id-when-available
47
+ children {
48
+ uid
49
+ include_in_menu
50
+ name
51
+ position
52
+ url_path
53
+ shop_by {
54
+ name
55
+ items {
56
+ name
57
+ url_path
58
+ }
59
+ }
60
+ }
61
+ }
62
+ }
63
+ }
64
+ }
65
+ `;
66
+
67
+ export default {
68
+ getMegaMenuQuery: GET_MEGA_MENU,
69
+ getStoreConfigQuery: GET_STORE_CONFIG_DATA
70
+ };
@@ -0,0 +1,199 @@
1
+ import { useMemo, useState, useCallback, useEffect } from 'react';
2
+ import { useLocation } from 'react-router-dom';
3
+ import { useInternalLink } from '../../hooks/useInternalLink';
4
+
5
+ import { useQuery } from '@apollo/client';
6
+ import { useEventListener } from '../../hooks/useEventListener';
7
+
8
+ import mergeOperations from '../../util/shallowMerge';
9
+ import DEFAULT_OPERATIONS from './megaMenu.gql';
10
+
11
+ /**
12
+ * The useMegaMenu talon complements the MegaMenu component.
13
+ *
14
+ * @param {Object} props
15
+ * @param {*} props.operations GraphQL operations used by talons
16
+ * @param {React.RefObject} props.mainNavRef Reference to main navigation DOM node
17
+ *
18
+ * @return {MegaMenuTalonProps}
19
+ */
20
+ export const useMegaMenu = (props = {}) => {
21
+ const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations);
22
+ const { getMegaMenuQuery, getStoreConfigQuery } = operations;
23
+
24
+ const location = useLocation();
25
+ const [activeCategoryId, setActiveCategoryId] = useState(null);
26
+ const [subMenuState, setSubMenuState] = useState(false);
27
+ const [disableFocus, setDisableFocus] = useState(false);
28
+
29
+ const { data: storeConfigData } = useQuery(getStoreConfigQuery, {
30
+ fetchPolicy: 'cache-and-network'
31
+ });
32
+
33
+ const { data } = useQuery(getMegaMenuQuery, {
34
+ fetchPolicy: 'cache-and-network',
35
+ nextFetchPolicy: 'cache-first'
36
+ });
37
+
38
+ const categoryUrlSuffix = useMemo(() => {
39
+ if (storeConfigData) {
40
+ return storeConfigData.storeConfig.category_url_suffix;
41
+ }
42
+ }, [storeConfigData]);
43
+
44
+ /**
45
+ * Check if category should be visible on the storefront.
46
+ *
47
+ * @param {MegaMenuCategory} category
48
+ * @returns {boolean}
49
+ */
50
+ const shouldRenderMegaMenuItem = category => {
51
+ return !!category.include_in_menu;
52
+ };
53
+
54
+ /**
55
+ * Check if category is the active category based on the current location.
56
+ *
57
+ * @param {MegaMenuCategory} category
58
+ * @returns {boolean}
59
+ */
60
+
61
+ const isActive = useCallback(
62
+ ({ url_path }) => {
63
+ if (!url_path) return false;
64
+
65
+ const categoryUrlPath = `/${url_path}${categoryUrlSuffix || ''}`;
66
+
67
+ return location.pathname === categoryUrlPath;
68
+ },
69
+ [location.pathname, categoryUrlSuffix]
70
+ );
71
+
72
+ /**
73
+ * Recursively map data returned by GraphQL query.
74
+ *
75
+ * @param {MegaMenuCategory} category
76
+ * @param {array} - path from the given category to the first level category
77
+ * @param {boolean} isRoot - describes is category a root category
78
+ * @return {MegaMenuCategory}
79
+ */
80
+ const processData = useCallback(
81
+ (category, path = [], isRoot = true) => {
82
+ if (!category) {
83
+ return;
84
+ }
85
+
86
+ const megaMenuCategory = Object.assign({}, category);
87
+
88
+ if (!isRoot) {
89
+ megaMenuCategory.path = [...path, category.uid];
90
+ }
91
+
92
+ megaMenuCategory.isActive = isActive(megaMenuCategory);
93
+
94
+ if (megaMenuCategory.children) {
95
+ megaMenuCategory.children = [...megaMenuCategory.children]
96
+ .filter(category => shouldRenderMegaMenuItem(category))
97
+ .sort((a, b) => (a.position > b.position ? 1 : -1))
98
+ .map(child =>
99
+ processData(child, megaMenuCategory.path, false)
100
+ );
101
+ }
102
+
103
+ return megaMenuCategory;
104
+ },
105
+ [isActive]
106
+ );
107
+
108
+ const megaMenuData = useMemo(() => {
109
+ return data ? processData(data.categoryList[0]) : {};
110
+ }, [data, processData]);
111
+
112
+ const findActiveCategory = useCallback(
113
+ (pathname, category) => {
114
+ if (isActive(category)) {
115
+ return category;
116
+ }
117
+
118
+ if (category.children) {
119
+ return category.children.find(category =>
120
+ findActiveCategory(pathname, category)
121
+ );
122
+ }
123
+ },
124
+ [isActive]
125
+ );
126
+
127
+ const handleClickOutside = e => {
128
+ if (!props.mainNavRef.current.contains(e.target)) {
129
+ setSubMenuState(false);
130
+ setDisableFocus(true);
131
+ }
132
+ };
133
+
134
+ useEventListener(globalThis, 'keydown', handleClickOutside);
135
+
136
+ const handleSubMenuFocus = useCallback(() => {
137
+ setSubMenuState(true);
138
+ }, [setSubMenuState]);
139
+
140
+ useEffect(() => {
141
+ const activeCategory = findActiveCategory(
142
+ location.pathname,
143
+ megaMenuData
144
+ );
145
+
146
+ if (activeCategory) {
147
+ setActiveCategoryId(activeCategory.path[0]);
148
+ } else {
149
+ setActiveCategoryId(null);
150
+ }
151
+ }, [findActiveCategory, location.pathname, megaMenuData]);
152
+
153
+ /**
154
+ * Sets next root component to show proper loading effect
155
+ *
156
+ * @returns {void}
157
+ */
158
+ const { setShimmerType } = useInternalLink('category');
159
+
160
+ return {
161
+ megaMenuData,
162
+ activeCategoryId,
163
+ categoryUrlSuffix,
164
+ handleClickOutside,
165
+ subMenuState,
166
+ disableFocus,
167
+ handleSubMenuFocus,
168
+ handleNavigate: setShimmerType
169
+ };
170
+ };
171
+
172
+ /** JSDocs type definitions */
173
+
174
+ /**
175
+ * @typedef {Object} MegaMenuTalonProps
176
+ *
177
+ * @property {MegaMenuCategory} megaMenuData - The Object with categories contains only categories
178
+ * with the include_in_menu = 1 flag. The categories are sorted
179
+ * based on the field position.
180
+ * @property {String} activeCategoryId returns the currently selected category uid.
181
+ * @property {String} categoryUrlSuffix store's category url suffix to construct category URL
182
+ * @property {Function} handleClickOutside function to handle mouse/key events.
183
+ * @property {Boolean} subMenuState maintaining sub-menu open/close state
184
+ * @property {Boolean} disableFocus state to disable focus
185
+ * @property {Function} handleSubMenuFocus toggle function to handle sub-menu focus
186
+ * @property {function} handleNavigate - callback to fire on link click
187
+ */
188
+
189
+ /**
190
+ * Object type returned by the {@link useMegaMenu} talon.
191
+ * @typedef {Object} MegaMenuCategory
192
+ *
193
+ * @property {String} uid - uid of the category
194
+ * @property {int} include_in_menu - describes if category should be included in menu
195
+ * @property {String} name - name of the category
196
+ * @property {int} position - value used for sorting
197
+ * @property {String} url_path - URL path for a category
198
+ * @property {MegaMenuCategory} children - child category
199
+ */
@@ -0,0 +1,70 @@
1
+ import { gql } from '@apollo/client';
2
+
3
+ export const GET_PRODUCT_FILTERS_BY_CATEGORY = gql`
4
+ query getProductFiltersByCategory(
5
+ $categoryIdFilter: FilterEqualTypeInput!
6
+ ) {
7
+ products(filter: { category_uid: $categoryIdFilter }) {
8
+ aggregations {
9
+ label
10
+ count
11
+ attribute_code
12
+ options {
13
+ label
14
+ value
15
+ }
16
+ position
17
+ }
18
+ }
19
+ }
20
+ `;
21
+
22
+ export const GET_CATEGORY_CONTENT = gql`
23
+ query getCategoryData($id: String!) {
24
+ categories(filters: { category_uid: { in: [$id] } }) {
25
+ # eslint-disable-next-line @graphql-eslint/require-id-when-available
26
+ items {
27
+ uid
28
+ name
29
+ description
30
+ url_key
31
+ url_path
32
+ parent {
33
+ uid
34
+ name
35
+ position
36
+ url_path
37
+ # eslint-disable-next-line @graphql-eslint/require-id-when-available
38
+ }
39
+ children {
40
+ uid
41
+ name
42
+ position
43
+ url_path
44
+ # eslint-disable-next-line @graphql-eslint/require-id-when-available
45
+ }
46
+ }
47
+ }
48
+ }
49
+ `;
50
+
51
+ export const GET_CATEGORY_AVAILABLE_SORT_METHODS = gql`
52
+ query getCategoryAvailableSortMethods(
53
+ $categoryIdFilter: FilterEqualTypeInput!
54
+ ) {
55
+ products(filter: { category_uid: $categoryIdFilter }) {
56
+ sort_fields {
57
+ options {
58
+ label
59
+ value
60
+ }
61
+ }
62
+ }
63
+ }
64
+ `;
65
+
66
+ export default {
67
+ getCategoryContentQuery: GET_CATEGORY_CONTENT,
68
+ getProductFiltersByCategoryQuery: GET_PRODUCT_FILTERS_BY_CATEGORY,
69
+ getCategoryAvailableSortMethodsQuery: GET_CATEGORY_AVAILABLE_SORT_METHODS
70
+ };
@@ -0,0 +1,141 @@
1
+ import { useEffect } from 'react';
2
+ import { useLazyQuery, useQuery } from '@apollo/client';
3
+
4
+ import mergeOperations from '@magento/peregrine/lib/util/shallowMerge';
5
+ import { useEventingContext } from '@magento/peregrine/lib/context/eventing';
6
+
7
+ import DEFAULT_OPERATIONS from './categoryContent.gql';
8
+
9
+ /**
10
+ * Returns props necessary to render the categoryContent component.
11
+ *
12
+ * @param {object} props.data - The results of a getCategory GraphQL query.
13
+ *
14
+ * @returns {object} result
15
+ * @returns {string} result.categoryDescription - This category's description.
16
+ * @returns {string} result.categoryName - This category's name.
17
+ * @returns {object} result.filters - The filters object.
18
+ * @returns {object} result.items - The items in this category.
19
+ * @returns {number} result.totalPagesFromData - The total amount of pages for the query.
20
+ */
21
+ export const useCategoryContent = props => {
22
+ const { categoryId, data, pageSize = 6 } = props;
23
+
24
+ const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations);
25
+
26
+ const {
27
+ getCategoryContentQuery,
28
+ getProductFiltersByCategoryQuery,
29
+ getCategoryAvailableSortMethodsQuery
30
+ } = operations;
31
+
32
+ const placeholderItems = Array.from({ length: pageSize }).fill(null);
33
+
34
+ const [getFilters, { data: filterData }] = useLazyQuery(
35
+ getProductFiltersByCategoryQuery,
36
+ {
37
+ fetchPolicy: 'cache-and-network',
38
+ nextFetchPolicy: 'cache-first'
39
+ }
40
+ );
41
+
42
+ const [getSortMethods, { data: sortData }] = useLazyQuery(
43
+ getCategoryAvailableSortMethodsQuery,
44
+ {
45
+ fetchPolicy: 'cache-and-network',
46
+ nextFetchPolicy: 'cache-first'
47
+ }
48
+ );
49
+
50
+ const { data: categoryData, loading: categoryLoading } = useQuery(
51
+ getCategoryContentQuery,
52
+ {
53
+ fetchPolicy: 'cache-and-network',
54
+ nextFetchPolicy: 'cache-first',
55
+ skip: !categoryId,
56
+ variables: {
57
+ id: categoryId
58
+ }
59
+ }
60
+ );
61
+
62
+ console.log(categoryData)
63
+
64
+ const [, { dispatch }] = useEventingContext();
65
+
66
+ useEffect(() => {
67
+ if (categoryId) {
68
+ getFilters({
69
+ variables: {
70
+ categoryIdFilter: {
71
+ eq: categoryId
72
+ }
73
+ }
74
+ });
75
+ }
76
+ }, [categoryId, getFilters]);
77
+
78
+ useEffect(() => {
79
+ if (categoryId) {
80
+ getSortMethods({
81
+ variables: {
82
+ categoryIdFilter: {
83
+ in: categoryId
84
+ }
85
+ }
86
+ });
87
+ }
88
+ }, [categoryId, getSortMethods]);
89
+
90
+ const filters = filterData ? filterData.products.aggregations : null;
91
+ const items = data ? data.products.items : placeholderItems;
92
+ const children =
93
+ categoryData && categoryData.categories.items.length
94
+ ? categoryData.categories.items[0].children
95
+ : null;
96
+ const parent =
97
+ categoryData && categoryData.categories.items.length
98
+ ? categoryData.categories.items[0].parent
99
+ : null;
100
+ const totalPagesFromData = data
101
+ ? data.products.page_info.total_pages
102
+ : null;
103
+ const totalCount = data ? data.products.total_count : null;
104
+ const categoryName =
105
+ categoryData && categoryData.categories.items.length
106
+ ? categoryData.categories.items[0].name
107
+ : null;
108
+ const categoryDescription =
109
+ categoryData && categoryData.categories.items.length
110
+ ? categoryData.categories.items[0].description
111
+ : null;
112
+ const availableSortMethods = sortData
113
+ ? sortData.products.sort_fields.options
114
+ : null;
115
+
116
+ useEffect(() => {
117
+ if (!categoryLoading && categoryData.categories.items.length > 0) {
118
+ dispatch({
119
+ type: 'CATEGORY_PAGE_VIEW',
120
+ payload: {
121
+ id: categoryData.categories.items[0].uid,
122
+ name: categoryData.categories.items[0].name,
123
+ url_key: categoryData.categories.items[0].url_key,
124
+ url_path: categoryData.categories.items[0].url_path
125
+ }
126
+ });
127
+ }
128
+ }, [categoryData, dispatch, categoryLoading]);
129
+
130
+ return {
131
+ availableSortMethods,
132
+ categoryName,
133
+ categoryDescription,
134
+ filters,
135
+ items,
136
+ totalCount,
137
+ totalPagesFromData,
138
+ children,
139
+ parent
140
+ };
141
+ };