@evershop/evershop 1.0.0-beta.2 → 1.0.0-beta.3

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 (121) hide show
  1. package/README.md +34 -51
  2. package/package.json +1 -1
  3. package/src/lib/components/Area.js +3 -3
  4. package/src/lib/components/form/Form.js +19 -5
  5. package/src/lib/components/form/fields/Hidden.js +13 -9
  6. package/src/lib/components/form/fields/Radio.js +1 -1
  7. package/src/modules/base/graphql/types/Country/Country.graphql +2 -0
  8. package/src/modules/base/graphql/types/Country/Country.resolvers.js +11 -1
  9. package/src/modules/catalog/components/product/list/List.js +18 -2
  10. package/src/modules/catalog/pages/admin/attributeEdit/index.js +2 -2
  11. package/src/modules/catalog/pages/admin/attributeEdit+attributeNew/General.js +1 -1
  12. package/src/modules/catalog/pages/admin/attributeGrid/rows/GroupRow.js +2 -1
  13. package/src/modules/catalog/pages/frontStore/categoryView/CategoryView.js +22 -0
  14. package/src/modules/catalog/pages/frontStore/categoryView/Filter.js +51 -48
  15. package/src/modules/catalog/pages/frontStore/categoryView/Filter.scss +43 -8
  16. package/src/modules/catalog/pages/frontStore/categoryView/General.js +12 -40
  17. package/src/modules/catalog/pages/frontStore/categoryView/General.scss +5 -0
  18. package/src/modules/catalog/pages/frontStore/categoryView/Pagination.js +2 -2
  19. package/src/modules/catalog/pages/frontStore/categoryView/Products.js +5 -5
  20. package/src/modules/catalog/{components/product/list → pages/frontStore/categoryView}/Sorting.js +31 -17
  21. package/src/modules/catalog/pages/frontStore/categoryView/[index]filters.js +0 -1
  22. package/src/modules/catalog/pages/frontStore/homepage/FeaturedProducts.js +1 -1
  23. package/src/modules/checkout/api/frontStore/addToCart/{addToCart.js → [detectCurrentCart]addToCart.js} +1 -1
  24. package/src/modules/checkout/api/frontStore/addToCart/[tokenVerify]detectCurrentCart[auth].js +3 -0
  25. package/src/modules/checkout/api/frontStore/checkoutPlaceOrder/route +1 -1
  26. package/src/modules/checkout/api/frontStore/checkoutSetContactInfo/saveContactInfo.js +4 -4
  27. package/src/modules/checkout/api/frontStore/checkoutSetPaymentInfo/savePaymentInfo.js +26 -4
  28. package/src/modules/checkout/api/frontStore/checkoutSetShipmentInfo/saveShipmentInfo.js +4 -4
  29. package/src/modules/checkout/components/frontStore/checkout/payment/paymentStep/StepContent.js +43 -83
  30. package/src/modules/checkout/components/frontStore/checkout/shipment/StepContent.js +1 -2
  31. package/src/modules/checkout/pages/admin/all/CheckoutMenuGroup.js +1 -1
  32. package/src/modules/checkout/pages/admin/orderEdit/Payment.js +8 -1
  33. package/src/modules/checkout/pages/admin/orderEdit/payment/Total.js +1 -0
  34. package/src/modules/checkout/pages/frontStore/all/[tokenVerify]detectCurrentCart[auth].js +7 -1
  35. package/src/modules/checkout/pages/frontStore/cart/ShoppingCart.js +1 -0
  36. package/src/modules/checkout/pages/frontStore/checkout/Checkout.js +1 -1
  37. package/src/modules/checkout/pages/frontStore/checkout/PaymentStep.js +1 -2
  38. package/src/modules/checkout/pages/frontStore/checkoutSuccess/CustomerInfo.js +1 -1
  39. package/src/modules/checkout/services/cart/Cart.js +130 -106
  40. package/src/modules/checkout/services/cart/DataObject.js +37 -11
  41. package/src/modules/checkout/services/cart/Item.js +153 -115
  42. package/src/modules/checkout/services/orderCreator.js +2 -0
  43. package/src/modules/checkout/services/toPrice.js +1 -0
  44. package/src/modules/cms/api/admin/imageUpload/[context]multerFile[auth].js +1 -1
  45. package/src/modules/cms/pages/admin/all/CmsMenuGroup.js +1 -1
  46. package/src/modules/cms/pages/frontStore/all/Meta.js +1 -0
  47. package/src/modules/cms/pages/frontStore/all/MobileMenu.js +2 -2
  48. package/src/modules/cod/api/frontStore/codCapturePayment/[bodyParser]capture.js +51 -0
  49. package/src/modules/cod/api/frontStore/codCapturePayment/bodyParser.js +5 -0
  50. package/src/modules/cod/api/frontStore/codCapturePayment/route +2 -0
  51. package/src/modules/cod/api/frontStore/paymentMethods/[validateCart]registerCod[sendMethods].js +14 -0
  52. package/src/modules/cod/bootstrap.js +20 -0
  53. package/src/modules/cod/components/CODLogo.js +5 -0
  54. package/src/modules/cod/graphql/types/CODSetting/CODSetting.graphql +5 -0
  55. package/src/modules/cod/graphql/types/CODSetting/CODSetting.resolvers.js +20 -0
  56. package/src/modules/cod/pages/admin/orderEdit/CaptureButton.js +54 -0
  57. package/src/modules/cod/pages/admin/paymentSetting/CODSetting.js +59 -0
  58. package/src/modules/cod/pages/frontStore/checkout/CashOnDelivery.js +70 -0
  59. package/src/modules/customer/components/Address/AddressForm/AddressForm.js +106 -0
  60. package/src/modules/customer/components/Address/AddressForm/AddressFormLoadingSkeleton.js +18 -0
  61. package/src/modules/customer/components/Address/AddressForm/AddressFormLoadingSkeleton.scss +27 -0
  62. package/src/modules/customer/components/Address/AddressForm/Country.js +36 -0
  63. package/src/modules/customer/components/Address/AddressForm/Index.js +35 -0
  64. package/src/modules/customer/components/Address/AddressForm/NameAndTelephone.js +43 -0
  65. package/src/modules/customer/components/Address/AddressForm/Province.js +42 -0
  66. package/src/modules/customer/components/Address/AddressForm/ProvinceAndPostcode.js +52 -0
  67. package/src/modules/customer/{pages/frontStore/address → components/Address}/AddressSummary.js +1 -1
  68. package/src/modules/customer/pages/admin/all/CustomerMenuGroup.js +1 -1
  69. package/src/modules/customer/pages/frontStore/all/UserIcon.js +1 -1
  70. package/src/modules/customer/pages/frontStore/checkout/CustomerInfoStep.js +7 -2
  71. package/src/modules/graphql/api/frontStore/graphql/[bodyParser]graphql.js +8 -0
  72. package/src/modules/paypal/api/frontStore/paymentMethods/[validateCart]registerPaypal[sendMethods].js +21 -0
  73. package/src/modules/paypal/api/frontStore/paypalAuthorizePayment/[bodyParser]authorize.js +76 -0
  74. package/src/modules/paypal/api/frontStore/paypalAuthorizePayment/bodyParser.js +5 -0
  75. package/src/modules/paypal/api/frontStore/paypalAuthorizePayment/route +2 -0
  76. package/src/modules/paypal/api/frontStore/paypalCapturePayment/[bodyParser]capture.js +74 -0
  77. package/src/modules/paypal/api/frontStore/paypalCapturePayment/bodyParser.js +5 -0
  78. package/src/modules/paypal/api/frontStore/paypalCapturePayment/route +2 -0
  79. package/src/modules/paypal/api/frontStore/paypalCreateOrder/[bodyParser]createOrder.js +168 -0
  80. package/src/modules/paypal/api/frontStore/paypalCreateOrder/bodyParser.js +5 -0
  81. package/src/modules/paypal/api/frontStore/paypalCreateOrder/route +2 -0
  82. package/src/modules/paypal/api/frontStore/paypalGetAccessToken/[bodyParser]getAccessToken.js +66 -0
  83. package/src/modules/paypal/api/frontStore/paypalGetAccessToken/bodyParser.js +5 -0
  84. package/src/modules/paypal/api/frontStore/paypalGetAccessToken/route +2 -0
  85. package/src/modules/paypal/bootstrap.js +20 -0
  86. package/src/modules/paypal/components/PaypalLogo.js +5 -0
  87. package/src/modules/paypal/graphql/types/PaypalSetting/PaypalSetting.graphql +9 -0
  88. package/src/modules/paypal/graphql/types/PaypalSetting/PaypalSetting.resolvers.js +90 -0
  89. package/src/modules/paypal/migration/Version-1.0.0.js +7 -0
  90. package/src/modules/paypal/pages/admin/paymentSetting/PaypalSetting.js +119 -0
  91. package/src/modules/paypal/pages/frontStore/checkout/Paypal.js +137 -0
  92. package/src/modules/paypal/pages/frontStore/paypalCancel/index.js +29 -0
  93. package/src/modules/paypal/pages/frontStore/paypalCancel/route +2 -0
  94. package/src/modules/paypal/pages/frontStore/paypalReturn/Error.js +13 -0
  95. package/src/modules/paypal/pages/frontStore/paypalReturn/index.js +51 -0
  96. package/src/modules/paypal/pages/frontStore/paypalReturn/route +2 -0
  97. package/src/modules/paypal/services/getApiBaseUrl.js +5 -0
  98. package/src/modules/promotion/bootstrap.js +67 -66
  99. package/src/modules/promotion/pages/admin/all/CouponMenuGroup.js +1 -1
  100. package/src/modules/promotion/services/{couponValidator.js → CouponValidator.js} +0 -0
  101. package/src/modules/promotion/services/{discountCalculator.js → DiscountCalculator.js} +18 -17
  102. package/src/modules/setting/graphql/types/ShippingSetting/ShippingSetting.graphql +1 -1
  103. package/src/modules/setting/graphql/types/ShippingSetting/ShippingSetting.resolvers.js +5 -5
  104. package/src/modules/setting/pages/admin/paymentSetting/PaymentSetting.js +1 -1
  105. package/src/modules/setting/pages/admin/shippingSetting/ShippingSetting.js +4 -4
  106. package/src/modules/setting/pages/admin/storeSetting/StoreSetting.js +6 -6
  107. package/src/modules/stripe/bootstrap.js +20 -0
  108. package/src/modules/stripe/components/StripeLogo.js +5 -0
  109. package/src/modules/stripe/pages/frontStore/checkout/CheckoutForm.js +20 -26
  110. package/src/modules/stripe/pages/frontStore/checkout/Stripe.js +87 -0
  111. package/src/modules/checkout/components/frontStore/checkout/address/AddressBook.js +0 -82
  112. package/src/modules/checkout/components/frontStore/checkout/address/NewBillingAddressForm.js +0 -36
  113. package/src/modules/checkout/components/frontStore/checkout/address/NewShippingAddressForm.js +0 -38
  114. package/src/modules/checkout/components/frontStore/checkout/address/UseShippingAddress.js +0 -64
  115. package/src/modules/customer/pages/admin/customerGrid/NewCustomertButton.js +0 -17
  116. package/src/modules/customer/pages/admin/customerNew/index.js +0 -8
  117. package/src/modules/customer/pages/admin/customerNew/route +0 -2
  118. package/src/modules/customer/pages/frontStore/address/AddressForm.js +0 -238
  119. package/src/modules/stripe/index.js +0 -1
  120. package/src/modules/stripe/pages/frontStore/checkout/PaymentFormContext.js +0 -40
  121. package/src/modules/stripe/pages/frontStore/checkout/PaymentFormCustom.js +0 -71
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
2
2
  <p align="center">
3
- <h1 align="center">Lightweight, modular, extendable React ecommerce platform.</h1>
3
+ <h1 align="center">Lightweight And Extendable React E-commerce Platform</h1>
4
4
  </p>
5
5
  <p align="center">
6
6
  <a href="https://evershop.io/docs/development/getting-started/introduction">Documentation</a>
@@ -15,49 +15,36 @@
15
15
  </a>
16
16
  </p>
17
17
 
18
- ## Component-Level Data Fetching (Server Side Props)
19
-
20
- ```javascript
21
- import React from 'react';
22
- import Products from './components/Products';
23
-
24
- export default function NewArrival({ products }) {
25
- return <div>
26
- <h2>New Arrival</h2>
27
- <Products products={products}/>
28
- </div>
29
- }
30
-
31
- // The GraphQL query result will be passed to the page component as props
32
- export const query = `
33
- query NewArrival {
34
- products {
35
- name
36
- price
37
- image {
38
- alt
39
- url
40
- }
41
- url
42
- }
43
- }
44
- `
45
- ```
18
+ ## Introduction
19
+
20
+ EverShop is a GraphQL Based and React ecommerce platform with essential commerce features. Built with React, modular and fully customizable.
21
+
22
+ ## Features
23
+ - Catalog management
24
+ - Product management
25
+ - Category management
26
+ - Attribute and attribute group
27
+ - Variant management
28
+ - Custom options
29
+ - Product layered navigation
30
+ - Order management
31
+ - Customer management
32
+ - Customer group
33
+ - Customer address
34
+ - Login, register and my account
35
+ - Advanced coupon management
36
+ - Online payment methods
37
+ - Stripe
38
+ - Paypal
46
39
 
47
40
  ## Demo
48
41
 
49
42
  Explore our demo store.
50
43
 
51
44
  <p align="center">
52
- <!--img alt="EverShop Admin Demo" width="950" src="https://raw.githubusercontent.com/evershopcommerce/evershop/dev/.github/images/evershop-backend-demo.png"/>
53
- </p-->
54
- <p align="left">
55
- <a href="https://demo.evershop.io/" target="_blank">
56
- <img alt="evershop-store-demo" height="35" alt="EverShop Store Demo" src="https://raw.githubusercontent.com/evershopcommerce/evershop/dev/.github/images/evershop-store-front-demo.png"/>
57
- </a>
45
+ <img alt="EverShop Admin Demo" width="950" src="https://raw.githubusercontent.com/evershopcommerce/evershop/dev/.github/images/evershop-backend-demo.png"/>
58
46
  </p>
59
-
60
- <p align="left">
47
+ <p align="center">
61
48
  <a href="https://demo.evershop.io/admin" target="_blank">
62
49
  <img alt="evershop-backend-demo" height="35" alt="EverShop Admin Demo" src="https://raw.githubusercontent.com/evershopcommerce/evershop/dev/.github/images/evershop-admin-demo.png"/>
63
50
  </a>
@@ -66,10 +53,14 @@ Explore our demo store.
66
53
 
67
54
  Email: demo@gmail.com<br/>
68
55
  Password: 123456
69
- <!--p align="center">
56
+ <p align="center">
70
57
  <img alt="EverShop Store Demo" width="950" src="https://raw.githubusercontent.com/evershopcommerce/evershop/dev/.github/images/evershop-product-detail.png"/>
71
- </p-->
72
-
58
+ </p>
59
+ <p align="center">
60
+ <a href="https://demo.evershop.io/" target="_blank">
61
+ <img alt="evershop-store-demo" height="35" alt="EverShop Store Demo" src="https://raw.githubusercontent.com/evershopcommerce/evershop/dev/.github/images/evershop-store-front-demo.png"/>
62
+ </a>
63
+ </p>
73
64
 
74
65
  ## Quick Start
75
66
 
@@ -85,22 +76,14 @@ npx create-evershop-app my-app --playAround
85
76
 
86
77
  - [Theme development](https://evershop.io/docs/development/theme/theme-overview).
87
78
 
88
- ## Features
89
- - Catalog management(with product attribute, custom option and variants)
90
- - Order management
91
- - Customer management
92
- - Coupon management
93
- - Tax
94
- - Online payment (For now using Stripe)
95
- - Basic CMS pages management
96
- - Easy to customize by developing extensions
79
+
97
80
 
98
81
  ## Support
99
82
 
100
83
  If you like my work, feel free to:
101
84
 
102
- - ⭐ this repository. It is a big motivation for me to continue working on this project.
103
- - [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)][tweet] about EverShop
85
+ - ⭐ this repository. It helps.
86
+ - [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)][tweet] about EverShop. Please accept my gratitude.
104
87
 
105
88
  [tweet]: https://twitter.com/intent/tweet?url=https%3A%2F%2Fgithub.com%2Fevershopcommerce%2Fevershop&text=Awesome%20React%20Ecommerce%20Project&hashtags=react,ecommerce,expressjs,graphql
106
89
  ### Ask a question about EverShop
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evershop/evershop",
3
- "version": "1.0.0-beta.2",
3
+ "version": "1.0.0-beta.3",
4
4
  "description": "The React Ecommerce platform. Built with React and MySQL. Open-source and free. Fast and customizable.",
5
5
  "files": [
6
6
  "bin",
@@ -34,7 +34,7 @@ function Area(props) {
34
34
 
35
35
  return (
36
36
  <WrapperComponent {...areaWrapperProps}>
37
- {areaComponents.map((w) => {
37
+ {areaComponents.map((w, index) => {
38
38
  const C = w.component.default;
39
39
  const id = w.id;
40
40
  const propsMap = context.propsMap;
@@ -48,8 +48,8 @@ function Area(props) {
48
48
  if (w.props) {
49
49
  Object.assign(componentProps, w.props);
50
50
  }
51
- if (typeof C === 'string') return <C key={w.id} {...componentProps} />;
52
- return <C key={w.id} areaProps={props} {...componentProps} />;
51
+ if (typeof C === 'string') return <C key={index} {...componentProps} />;
52
+ return <C key={index} areaProps={props} {...componentProps} />;
53
53
  })}
54
54
  </WrapperComponent>
55
55
  );
@@ -50,10 +50,22 @@ export function Form(props) {
50
50
  const errors = {};
51
51
  fields.forEach((f) => {
52
52
  f.validationRules.forEach((r) => {
53
- const rule = validator.getRule(r);
54
- if (rule === undefined) return;
55
- if (!rule.handler.call(fields, f.value)) {
56
- errors[f.name] = rule.errorMessage;
53
+ let rule;
54
+ // Check if r is a string or an object
55
+ if (typeof r === 'string') {
56
+ rule = r;
57
+ } else {
58
+ rule = r.rule;
59
+ }
60
+
61
+ const ruleValidator = validator.getRule(rule);
62
+ if (ruleValidator === undefined) return;
63
+ if (!ruleValidator.handler.call(fields, f.value)) {
64
+ if (r.message) {
65
+ errors[f.name] = r.message;
66
+ } else {
67
+ errors[f.name] = ruleValidator.errorMessage;
68
+ }
57
69
  }
58
70
  });
59
71
  });
@@ -119,7 +131,9 @@ export function Form(props) {
119
131
  // Get the first element with the name of the field with error
120
132
  const firstElementWithError = document.getElementsByName(firstFieldWithError)[0];
121
133
  // Focus on the first element with error
122
- firstElementWithError.focus();
134
+ if (firstElementWithError) {
135
+ firstElementWithError.focus();
136
+ }
123
137
  }
124
138
  } catch (error) {
125
139
  if (onError) {
@@ -1,16 +1,20 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import React from 'react';
3
+ import Error from './Error';
3
4
 
4
- export function Hidden({ name, value }) {
5
+ export function Hidden({ name, value, error }) {
5
6
  return (
6
- <input
7
- type="text"
8
- id={name}
9
- name={name}
10
- value={value}
11
- readOnly
12
- style={{ display: 'none' }}
13
- />
7
+ <>
8
+ {error && <Error error={error} />}
9
+ <input
10
+ type="text"
11
+ id={name}
12
+ name={name}
13
+ value={value}
14
+ readOnly
15
+ style={{ display: 'none' }}
16
+ />
17
+ </>
14
18
  );
15
19
  }
16
20
 
@@ -66,7 +66,7 @@ Radio.propTypes = {
66
66
  value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
67
67
  text: PropTypes.string
68
68
  })).isRequired,
69
- value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired
69
+ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
70
70
  };
71
71
 
72
72
  Radio.defaultProps = {
@@ -1,8 +1,10 @@
1
1
  type Country {
2
2
  name: String!
3
3
  code: String!
4
+ provinces: [Province]
4
5
  }
5
6
 
6
7
  extend type Query {
7
8
  countries (countries: [String]) : [Country]
9
+ allowedCountries : [Country]
8
10
  }
@@ -1,4 +1,6 @@
1
- const { contries } = require("../../../../../lib/locale/countries")
1
+ const { contries } = require("../../../../../lib/locale/countries");
2
+ const { getSetting } = require("../../../../setting/services/setting");
3
+ const { provinces } = require("../../../../../lib/locale/provinces");
2
4
 
3
5
  module.exports = {
4
6
  Query: {
@@ -9,6 +11,11 @@ module.exports = {
9
11
  } else {
10
12
  return contries.filter((c) => list.includes(c.code));
11
13
  }
14
+ },
15
+ allowedCountries: async () => {
16
+ const allowedCountries = await getSetting("allowedCountries", '["US"]');
17
+ let list = JSON.parse(allowedCountries);
18
+ return contries.filter((c) => list.includes(c.code));
12
19
  }
13
20
  },
14
21
  Country: {
@@ -26,6 +33,9 @@ module.exports = {
26
33
  } else {
27
34
  return country;
28
35
  }
36
+ },
37
+ provinces: (country) => {
38
+ return provinces.filter((p) => p.countryCode === country.code);
29
39
  }
30
40
  }
31
41
  }
@@ -6,7 +6,7 @@ import { Price } from './item/Price';
6
6
  import Area from '../../../../../lib/components/Area';
7
7
  import { get } from '../../../../../lib/util/get';
8
8
 
9
- export default function ProductList({ products = [] }) {
9
+ export default function ProductList({ products = [], countPerRow = 3 }) {
10
10
  if (products.length === 0) {
11
11
  return (
12
12
  <div className="product-list">
@@ -14,8 +14,24 @@ export default function ProductList({ products = [] }) {
14
14
  </div>
15
15
  );
16
16
  }
17
+
18
+ let className;
19
+ switch (countPerRow) {
20
+ case 3:
21
+ className = 'grid grid-cols-2 md:grid-cols-3 gap-2';
22
+ break;
23
+ case 4:
24
+ className = 'grid grid-cols-2 md:grid-cols-4 gap-2';
25
+ break;
26
+ case 5:
27
+ className = 'grid grid-cols-2 md:grid-cols-5 gap-2';
28
+ break;
29
+ default:
30
+ className = 'grid grid-cols-2 md:grid-cols-3 gap-2';
31
+ }
32
+
17
33
  return (
18
- <div className="grid grid-cols-2 md:grid-cols-4 gap-3">
34
+ <div className={className}>
19
35
  {
20
36
  products.map((p) => (
21
37
  <Area
@@ -15,8 +15,8 @@ module.exports = async (request, response, delegate, next) => {
15
15
  } else {
16
16
  setContextValue(request, 'attributeId', attribute.attribute_id);
17
17
  setContextValue(request, 'pageInfo', {
18
- title: attribute.name,
19
- description: attribute.name
18
+ title: attribute.attribute_name,
19
+ description: attribute.attribute_name
20
20
  });
21
21
  next();
22
22
  }
@@ -57,7 +57,7 @@ function Groups({ groups, addGroupUrl }) {
57
57
  <div className='grid gap-2 grid-cols-2'>
58
58
  <div>
59
59
  <Select
60
- name='groups'
60
+ name='groups[]'
61
61
  options={data.attributeGroups}
62
62
  hideSelectedOptions={true}
63
63
  isMulti={true}
@@ -21,11 +21,12 @@ export default function GroupRow({ groups, saveAttributeGroupUrl }) {
21
21
  onSuccess={() => {
22
22
  location.reload();
23
23
  }}
24
+ isJSON={true}
24
25
  >
25
26
  <Field
26
27
  formId="group-edit"
27
28
  type="text"
28
- name="groupName"
29
+ name="group_name"
29
30
  value={group.groupName}
30
31
  />
31
32
  <Field
@@ -0,0 +1,22 @@
1
+ import React from 'react';
2
+ import Area from '../../../../../lib/components/Area';
3
+
4
+ export default function CategoryView() {
5
+ return (
6
+ <div className="page-width grid grid-cols-1 md:grid-cols-4 gap-2">
7
+ <Area
8
+ id="leftColumn"
9
+ className="md:col-span-1"
10
+ />
11
+ <Area
12
+ id="rightColumn"
13
+ className="md:col-span-3"
14
+ />
15
+ </div>
16
+ );
17
+ }
18
+
19
+ export const layout = {
20
+ areaId: 'content',
21
+ sortOrder: 10
22
+ }
@@ -2,7 +2,6 @@ import PropTypes from 'prop-types';
2
2
  import React, { useState } from 'react';
3
3
  import Area from '../../../../../lib/components/Area';
4
4
  import { get } from '../../../../../lib/util/get';
5
- import Sorting from '../../../components/product/list/Sorting';
6
5
  import './Filter.scss';
7
6
 
8
7
  function Price({
@@ -109,10 +108,16 @@ function Attributes({ currentFilters, attributes, updateFilter }) {
109
108
  e.preventDefault();
110
109
  if (isChecked === true) {
111
110
  updateFilter(
112
- currentFilters.filter(
113
- (f) => f.key !== attributeCode
114
- || (f.key === attributeCode && parseInt(f.value, 10) !== parseInt(optionId, 10))
115
- )
111
+ currentFilters.map(
112
+ (f) => {
113
+ if (f.key !== attributeCode) {
114
+ return f
115
+ } else {
116
+ const value = f.value.split(',').filter((v) => parseInt(v) !== parseInt(optionId));
117
+ return { key: attributeCode, value: value.join(',') };
118
+ }
119
+ }
120
+ ).filter((f) => f.value !== '')
116
121
  );
117
122
  } else {
118
123
  updateFilter(currentFilters.concat({ key: attributeCode, value: optionId }));
@@ -124,21 +129,31 @@ function Attributes({ currentFilters, attributes, updateFilter }) {
124
129
  {attributes.map((a) => (
125
130
  <div key={a.attributeCode}>
126
131
  <div className="filter-item-title"><span className="font-medium">{a.attributeName}</span></div>
127
- <ul className="flex flex-wrap filter-option-list">
132
+ <ul className="filter-option-list">
128
133
  {a.options.map((o) => {
129
134
  const isChecked = currentFilters.find(
130
135
  (f) => f.key === a.attributeCode
131
- && parseInt(f.value, 10) === parseInt(o.optionId, 10)
136
+ && f.value.split(',').includes(o.optionId.toString())
132
137
  );
133
138
  return (
134
139
  <li key={o.optionId} className="mt-05 mr-05">
135
- <button
136
- type="button"
137
- className={isChecked ? 'checked text-center filter-option link-button' : 'text-center filter-option link-button'}
138
- onClick={(e) => onChange(e, a.attributeCode, o.optionId, !!isChecked)}
139
- >
140
- {o.optionText}
141
- </button>
140
+ <a href="#" className='flex justify-start items-center' onClick={(e) => onChange(e, a.attributeCode, o.optionId, !!isChecked)}>
141
+ {isChecked && <svg width="24px" height="24px" viewBox="0 0 24 24" >
142
+ <g stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
143
+ <g fill="#212121" fillRule="nonzero">
144
+ <path d="M18,3 C19.6568542,3 21,4.34314575 21,6 L21,18 C21,19.6568542 19.6568542,21 18,21 L6,21 C4.34314575,21 3,19.6568542 3,18 L3,6 C3,4.34314575 4.34314575,3 6,3 L18,3 Z M16.4696699,7.96966991 L10,14.4393398 L7.53033009,11.9696699 C7.23743687,11.6767767 6.76256313,11.6767767 6.46966991,11.9696699 C6.1767767,12.2625631 6.1767767,12.7374369 6.46966991,13.0303301 L9.46966991,16.0303301 C9.76256313,16.3232233 10.2374369,16.3232233 10.5303301,16.0303301 L17.5303301,9.03033009 C17.8232233,8.73743687 17.8232233,8.26256313 17.5303301,7.96966991 C17.2374369,7.6767767 16.7625631,7.6767767 16.4696699,7.96966991 Z" ></path>
145
+ </g>
146
+ </g>
147
+ </svg>}
148
+ {!isChecked && <svg width="24px" height="24px" viewBox="0 0 24 24">
149
+ <g stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
150
+ <g fill="#212121" fillRule="nonzero">
151
+ <path d="M5.75,3 L18.25,3 C19.7687831,3 21,4.23121694 21,5.75 L21,18.25 C21,19.7687831 19.7687831,21 18.25,21 L5.75,21 C4.23121694,21 3,19.7687831 3,18.25 L3,5.75 C3,4.23121694 4.23121694,3 5.75,3 Z M5.75,4.5 C5.05964406,4.5 4.5,5.05964406 4.5,5.75 L4.5,18.25 C4.5,18.9403559 5.05964406,19.5 5.75,19.5 L18.25,19.5 C18.9403559,19.5 19.5,18.9403559 19.5,18.25 L19.5,5.75 C19.5,5.05964406 18.9403559,4.5 18.25,4.5 L5.75,4.5 Z"></path>
152
+ </g>
153
+ </g>
154
+ </svg>}
155
+ <span className='filter-option'>{o.optionText}</span>
156
+ </a>
142
157
  </li>
143
158
  );
144
159
  })}
@@ -162,8 +177,7 @@ Attributes.propTypes = {
162
177
  };
163
178
 
164
179
  export default function Filter({ category: { availableFilters, products: { currentFilters } } }) {
165
-
166
- const [filtering, setFiltering] = useState(false);
180
+ const [isOpen, setIsOpen] = useState(false);
167
181
 
168
182
  const updateFilter = (newFilters) => {
169
183
  const currentUrl = window.location.href;
@@ -189,30 +203,14 @@ export default function Filter({ category: { availableFilters, products: { curre
189
203
  };
190
204
 
191
205
  return (
192
- <div className="product-filter-tool">
193
- <div className="page-width flex justify-between">
194
- <div className="self-center">
195
- <div className="filter-heading">
196
- {filtering === false && (
197
- <button type="button" className="link-button flex justify-start space-x-05" onClick={(e) => { e.preventDefault(); setFiltering(true); }}>
198
- <svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
199
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" />
200
- </svg>
201
- <span className="font-light">FILTER BY</span>
202
- </button>
203
- )}
204
- {filtering === true && (
205
- <div className="close-filter mb-1">
206
- <button type="button" onClick={(e) => { e.preventDefault(); setFiltering(false); }} className="link-button">
207
- <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
208
- <path fillRule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clipRule="evenodd" />
209
- </svg>
210
- </button>
211
- </div>
212
- )}
213
- </div>
214
- {filtering === true && (
215
- <div className="filter-table md:grid md:grid-cols-4 gap-2">
206
+ <>
207
+ <div className={`product-filter-tool hidden md:block ${isOpen ? 'opening' : 'closed'}`}>
208
+ <div className="">
209
+ <div className="self-center">
210
+ <div className="filter-heading">
211
+ <span className="font-bold ">SHOP BY</span>
212
+ </div>
213
+ <div className='mt-1 grid grid-cols-1 gap-2'>
216
214
  <Area
217
215
  id="productFilter"
218
216
  updateFilter={updateFilter}
@@ -229,20 +227,25 @@ export default function Filter({ category: { availableFilters, products: { curre
229
227
  ]}
230
228
  />
231
229
  </div>
232
- )}
233
- </div>
234
- {filtering === false && (
235
- <div>
236
- <Sorting currentFilters={currentFilters} />
237
230
  </div>
238
- )}
231
+ </div>
232
+ <a className='filter-closer flex md:hidden' href="#" onClick={(e) => { e.preventDefault(); setIsOpen(!isOpen) }}>
233
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-6 h-6">
234
+ <path strokeLinecap="round" strokeLinejoin="round" d="M10.5 6h9.75M10.5 6a1.5 1.5 0 11-3 0m3 0a1.5 1.5 0 10-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-9.75 0h9.75" />
235
+ </svg>
236
+ </a>
239
237
  </div>
240
- </div>
238
+ <a className='filter-opener flex md:hidden' href="#" onClick={(e) => { e.preventDefault(); setIsOpen(!isOpen) }}>
239
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-6 h-6">
240
+ <path strokeLinecap="round" strokeLinejoin="round" d="M10.5 6h9.75M10.5 6a1.5 1.5 0 11-3 0m3 0a1.5 1.5 0 10-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-9.75 0h9.75" />
241
+ </svg>
242
+ </a>
243
+ </>
241
244
  );
242
245
  }
243
246
 
244
247
  export const layout = {
245
- areaId: "content",
248
+ areaId: "leftColumn",
246
249
  sortOrder: 1
247
250
  }
248
251
 
@@ -1,8 +1,6 @@
1
1
  .product-filter-tool {
2
2
  padding-top: 1rem;
3
3
  padding-bottom: 1rem;
4
- border-top: 1px solid var(--divider);
5
- border-bottom: 1px solid var(--divider);
6
4
  margin-bottom: 1.5rem;
7
5
  .filter-item-title {
8
6
  margin-bottom: 0.5rem;
@@ -16,13 +14,8 @@
16
14
  }
17
15
  }
18
16
  .filter-option {
19
- border: 1px solid var(--divider);
20
- display: block;
21
- padding: 0.5rem 1.5rem;
17
+ padding: 0rem 0.8rem;
22
18
  border-radius: 2px;
23
- &.checked {
24
- border: 2px solid var(--text);
25
- }
26
19
  }
27
20
 
28
21
  .filter-heading {
@@ -97,3 +90,45 @@ input[type="range"]:nth-child(1)::-webkit-slider-thumb {
97
90
  float: right;
98
91
  margin-right: -5px;
99
92
  }
93
+
94
+ .filter-opener, .filter-closer {
95
+ position: fixed;
96
+ bottom: 20px;
97
+ left: 20px;
98
+ width: 35px;
99
+ height: 35px;
100
+ }
101
+
102
+ .filter-opener, .filter-closer {
103
+ z-index: 998;
104
+ background: #fff;
105
+ border-radius: 50%;
106
+ box-shadow: 0 0 10px rgba(0,0,0,0.2);
107
+ justify-content: center;
108
+ align-items: center;
109
+ svg {
110
+ width: 20px;
111
+ height: 20px;
112
+ }
113
+ }
114
+
115
+ @media (max-width: 639px) {
116
+ .product-filter-tool.opening {
117
+ display: block;
118
+ position: fixed;
119
+ // Make full screen
120
+ top: 0;
121
+ left: 0;
122
+ right: 0;
123
+ bottom: 0;
124
+ z-index: 999;
125
+ background-color: #fff;
126
+ padding: 20px;
127
+ box-sizing: border-box;
128
+ }
129
+
130
+ .filter-opener,
131
+ .filter-closer {
132
+ display: flex;
133
+ }
134
+ }