diff --git a/.changeset/clever-fans-tickle.md b/.changeset/clever-fans-tickle.md deleted file mode 100644 index e2b548b6e1..0000000000 --- a/.changeset/clever-fans-tickle.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Prepend locale for redirected urls in tests. -More info: https://github.com/amannn/next-intl/issues/1335 diff --git a/.changeset/empty-ties-rhyme.md b/.changeset/empty-ties-rhyme.md new file mode 100644 index 0000000000..df486e6816 --- /dev/null +++ b/.changeset/empty-ties-rhyme.md @@ -0,0 +1,5 @@ +--- +"@bigcommerce/catalyst-core": minor +--- + +Add orders for customer account. Now customer can open orders history or move to specific order details. diff --git a/.changeset/stupid-drinks-report.md b/.changeset/forty-actors-notice.md similarity index 57% rename from .changeset/stupid-drinks-report.md rename to .changeset/forty-actors-notice.md index 7a781c3a78..d404642a05 100644 --- a/.changeset/stupid-drinks-report.md +++ b/.changeset/forty-actors-notice.md @@ -2,4 +2,4 @@ "@bigcommerce/catalyst-core": patch --- -Add additional IP address header +Remove webpack chunk plugin diff --git a/.changeset/heavy-toes-own.md b/.changeset/heavy-toes-own.md deleted file mode 100644 index ad850f915d..0000000000 --- a/.changeset/heavy-toes-own.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Add missing metadata in account settings page. diff --git a/.changeset/odd-jokes-love.md b/.changeset/odd-jokes-love.md deleted file mode 100644 index 4e017b2ad5..0000000000 --- a/.changeset/odd-jokes-love.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": minor ---- - -Pass customer ip address into requests that don't rely on cached values. diff --git a/.changeset/orange-cheetahs-explode.md b/.changeset/orange-cheetahs-explode.md new file mode 100644 index 0000000000..deb0dc80a5 --- /dev/null +++ b/.changeset/orange-cheetahs-explode.md @@ -0,0 +1,5 @@ +--- +"@bigcommerce/catalyst-core": patch +--- + +Added aria label for compare button diff --git a/.changeset/quick-owls-relate.md b/.changeset/quick-owls-relate.md new file mode 100644 index 0000000000..3787c3389e --- /dev/null +++ b/.changeset/quick-owls-relate.md @@ -0,0 +1,5 @@ +--- +"@bigcommerce/catalyst-core": minor +--- + +Uses the API responses to show better errors when adding a product to the cart. diff --git a/.changeset/slimy-months-hunt.md b/.changeset/slimy-months-hunt.md new file mode 100644 index 0000000000..5b782ab352 --- /dev/null +++ b/.changeset/slimy-months-hunt.md @@ -0,0 +1,5 @@ +--- +"@bigcommerce/catalyst-core": patch +--- + +UX improvements for account pages diff --git a/.changeset/slow-houses-smell.md b/.changeset/slow-houses-smell.md new file mode 100644 index 0000000000..621e4fbe8b --- /dev/null +++ b/.changeset/slow-houses-smell.md @@ -0,0 +1,5 @@ +--- +"@bigcommerce/create-catalyst": patch +--- + +Adds a platform check to check if a command is installed. diff --git a/.changeset/strange-beers-sin.md b/.changeset/strange-beers-sin.md new file mode 100644 index 0000000000..f8633a39e3 --- /dev/null +++ b/.changeset/strange-beers-sin.md @@ -0,0 +1,5 @@ +--- +"@bigcommerce/catalyst-core": patch +--- + +Added localization to hardcoded strings diff --git a/.changeset/strange-beers-smile.md b/.changeset/strange-beers-smile.md deleted file mode 100644 index 7a75904381..0000000000 --- a/.changeset/strange-beers-smile.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Get lossy image from API instead of setting param in code diff --git a/.changeset/thick-donuts-hope.md b/.changeset/thick-donuts-hope.md deleted file mode 100644 index cb6db20ef0..0000000000 --- a/.changeset/thick-donuts-hope.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": minor ---- - -wraps header and footer in suspense boundaries diff --git a/.changeset/honest-files-fail.md b/.changeset/translations-patch-e75fb980.md similarity index 100% rename from .changeset/honest-files-fail.md rename to .changeset/translations-patch-e75fb980.md diff --git a/.changeset/twenty-walls-flash.md b/.changeset/twenty-walls-flash.md deleted file mode 100644 index 7817ed1f84..0000000000 --- a/.changeset/twenty-walls-flash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-client": minor ---- - -Add the ability to hook into the fetchOptions before the request is sent. diff --git a/.changeset/wicked-papayas-smoke.md b/.changeset/wicked-papayas-smoke.md new file mode 100644 index 0000000000..70ca23a0d2 --- /dev/null +++ b/.changeset/wicked-papayas-smoke.md @@ -0,0 +1,5 @@ +--- +"@bigcommerce/catalyst-core": minor +--- + +If a string is not provided in the selected locale, the translation system will fallback to "en" for that specific entry. diff --git a/.env.example b/.env.example index b0f4ee4647..bacc5581f5 100644 --- a/.env.example +++ b/.env.example @@ -2,13 +2,9 @@ # The control panel URL is of the form `https://store-{hash}.mybigcommerce.com`. BIGCOMMERCE_STORE_HASH= -# The access token from a store-level API account. The only scope required to run Catalyst is Carts `manage`. -# See https://developer.bigcommerce.com/docs/start/authentication/api-accounts#store-level-api-accounts -BIGCOMMERCE_ACCESS_TOKEN= - -# A bearer token that authorizes server-to-server requests to the GraphQL Storefront API -# See https://developer.bigcommerce.com/docs/rest-authentication/tokens/customer-impersonation-token -BIGCOMMERCE_CUSTOMER_IMPERSONATION_TOKEN= +# A JWT Token for accessing the Storefront API. Enables server-to-server requests if allowed_cors_origins is omitted. +# See https://developer.bigcommerce.com/docs/rest-authentication/tokens#storefront-tokens +BIGCOMMERCE_STOREFRONT_TOKEN= # The Channel ID for the selling channel being serviced by this Catalyst storefront. # Channel ID 1 will allow you to load the same data being used on the default Stencil storefront on your store, diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ea1a103dcf..870d5ddca4 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -17,3 +17,6 @@ updates: update-types: ['version-update:semver-major'] - dependency-name: 'react-day-picker' update-types: ['version-update:semver-major'] + # We are using the latest pre-releases for react and react-dom. + - dependency-name: 'react' + - dependency-name: 'react-dom' diff --git a/.github/workflows/basic.yml b/.github/workflows/basic.yml index d97c4bb023..965c030e6e 100644 --- a/.github/workflows/basic.yml +++ b/.github/workflows/basic.yml @@ -13,7 +13,8 @@ env: TURBO_TEAM: ${{ vars.TURBO_TEAM }} TURBO_REMOTE_CACHE_SIGNATURE_KEY: ${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }} BIGCOMMERCE_STORE_HASH: ${{ secrets.BIGCOMMERCE_STORE_HASH }} - BIGCOMMERCE_CUSTOMER_IMPERSONATION_TOKEN: ${{ secrets.BIGCOMMERCE_CUSTOMER_IMPERSONATION_TOKEN }} + BIGCOMMERCE_STOREFRONT_TOKEN: ${{ secrets.BIGCOMMERCE_STOREFRONT_TOKEN }} + BIGCOMMERCE_CHANNEL_ID: ${{ secrets.BIGCOMMERCE_CHANNEL_ID }} jobs: lint-typecheck: diff --git a/.github/workflows/changesets-release.yml b/.github/workflows/changesets-release.yml index 1de90fcd92..2f6f7f6938 100644 --- a/.github/workflows/changesets-release.yml +++ b/.github/workflows/changesets-release.yml @@ -29,6 +29,8 @@ jobs: - name: Build Packages run: pnpm --filter "./packages/**" build + env: + CLI_SEGMENT_WRITE_KEY: ${{ secrets.CLI_SEGMENT_WRITE_KEY }} - name: Create Release Pull Request or Publish to npm id: changesets diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml index b54553523b..56aaa8253f 100644 --- a/.github/workflows/regression-tests.yml +++ b/.github/workflows/regression-tests.yml @@ -140,7 +140,7 @@ jobs: - name: Run Playwright tests run: | cd core - npx playwright test tests/ui/ --project=tests-chromium + npx playwright test tests/ui/ --project=tests-chromium - uses: actions/upload-artifact@v4 if: failure() @@ -151,7 +151,7 @@ jobs: - name: Send slack notification uses: slackapi/slack-github-action@v1.26.0 - if: ${{ steps.pr_details.outputs.draft != 'true' && failure() }} + if: ${{ steps.pr_details.outputs.draft != 'true' && steps.pr_details.outputs.pr && failure() }} env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} with: @@ -213,7 +213,7 @@ jobs: - name: Send slack notification uses: slackapi/slack-github-action@v1.26.0 - if: ${{ steps.pr_details.outputs.draft != 'true' && failure() }} + if: ${{ steps.pr_details.outputs.draft != 'true' && steps.pr_details.outputs.pr && failure() }} env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} with: diff --git a/SECURITY.md b/SECURITY.md index bc6e641af8..0095d631d7 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,8 +1,11 @@ # Reporting security issues -If you have found a security vulnerability in an active open-source repository created and owned by BigCommerce, please report it to our [public bug bounty program](https://bugcrowd.com/bigcommerce). If you would prefer to submit via email, please send your report to [security@bigcommerce.com](mailto:security@bigcommerce.com) +BigCommerce is dedicated to the responsible disclosure of security vulnerabilities. +If you have found a security vulnerability in an active open-source repository created and owned by BigCommerce, please report it to our [public bug bounty program](https://bugcrowd.com/bigcommerce). If you would prefer to submit via email, please send your report to [security@bigcommerce.com](mailto:security@bigcommerce.com). -*Note: Only submissions to our bounty program on BugCrowd will be eligible for bounties. Bounty eligibility and amounts are determined according to the program guidelines.* +We ask that you **do not** open a public GitHub issue to report security concerns. -Please ***do not*** use public issues to report security vulnerabilities. +_Note: Only submissions to our bounty program on BugCrowd will be eligible for bounties. Bounty eligibility and amounts are determined according to the program guidelines._ -Bugs in 3rd-party modules should be reported to those modules’ maintainers. +_Note: Bugs in 3rd-party modules and/or dependencies should be reported to the owners/maintainers or those modules and/or dependencies, BigCommerce has no control or authority over third party content._ + +Thank you in advance for collaborating with us to help protect us and our customers. diff --git a/core/.env.example b/core/.env.example index e962f421a2..0be7bd5998 100644 --- a/core/.env.example +++ b/core/.env.example @@ -6,13 +6,9 @@ MAKESWIFT_SITE_API_KEY= # The control panel URL is of the form `https://store-{hash}.mybigcommerce.com`. BIGCOMMERCE_STORE_HASH= -# The access token from a store-level API account. The only scope required to run Catalyst is Carts `manage`. -# See https://developer.bigcommerce.com/docs/start/authentication/api-accounts#store-level-api-accounts -BIGCOMMERCE_ACCESS_TOKEN= - -# A bearer token that authorizes server-to-server requests to the GraphQL Storefront API -# See https://developer.bigcommerce.com/docs/rest-authentication/tokens/customer-impersonation-token -BIGCOMMERCE_CUSTOMER_IMPERSONATION_TOKEN= +# A JWT Token for accessing the Storefront API. Enables server-to-server requests if allowed_cors_origins is omitted. +# See https://developer.bigcommerce.com/docs/rest-authentication/tokens#storefront-tokens +BIGCOMMERCE_STOREFRONT_TOKEN= # The Channel ID for the selling channel being serviced by this Catalyst storefront. # Channel ID 1 will allow you to load the same data being used on the default Stencil storefront on your store, diff --git a/core/.gitignore b/core/.gitignore index d96c1399f9..26db3633a1 100644 --- a/core/.gitignore +++ b/core/.gitignore @@ -43,3 +43,6 @@ client/generated # secrets .catalyst + +# Build config +build-config.json diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index d869619dcf..6f5f12b686 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -1,5 +1,160 @@ # Changelog +## 0.21.0 + +### Minor Changes + +- [#1631](https://github.com/bigcommerce/catalyst/pull/1631) [`58d9e7c`](https://github.com/bigcommerce/catalyst/commit/58d9e7ccb7915593cd012cce6d9f4bdf66cb381f) Thanks [@deini](https://github.com/deini)! - fetch available locales at build time + +### Patch Changes + +- [#1636](https://github.com/bigcommerce/catalyst/pull/1636) [`23abacf`](https://github.com/bigcommerce/catalyst/commit/23abacfb8ff4ff9d269e51821a6a992a9cb2d4f5) Thanks [@chanceaclark](https://github.com/chanceaclark)! - Remove console.error when falling back to defaultChannelId + +- [#1636](https://github.com/bigcommerce/catalyst/pull/1636) [`23abacf`](https://github.com/bigcommerce/catalyst/commit/23abacfb8ff4ff9d269e51821a6a992a9cb2d4f5) Thanks [@chanceaclark](https://github.com/chanceaclark)! - Clean up login error handling. + +- Updated dependencies [[`23abacf`](https://github.com/bigcommerce/catalyst/commit/23abacfb8ff4ff9d269e51821a6a992a9cb2d4f5)]: + - @bigcommerce/catalyst-client@0.14.0 + +## 0.20.0 + +### Minor Changes + +- [#1623](https://github.com/bigcommerce/catalyst/pull/1623) [`16e3a76`](https://github.com/bigcommerce/catalyst/commit/16e3a763571324dccd9031a79e400409eff9ee0c) Thanks [@chanceaclark](https://github.com/chanceaclark)! - Next 15 upgrade + +### Patch Changes + +- [#1629](https://github.com/bigcommerce/catalyst/pull/1629) [`72a30a8`](https://github.com/bigcommerce/catalyst/commit/72a30a84193f7ed8a09b770d16dd2c9a8a7d1347) Thanks [@deini](https://github.com/deini)! - Use Typescript on Next Config + +- [#1618](https://github.com/bigcommerce/catalyst/pull/1618) [`d60e916`](https://github.com/bigcommerce/catalyst/commit/d60e916661385fab211f7e8b1342dbda2fd504b9) Thanks [@bc-svc-local](https://github.com/bc-svc-local)! - Update translations. + +- Updated dependencies [[`16e3a76`](https://github.com/bigcommerce/catalyst/commit/16e3a763571324dccd9031a79e400409eff9ee0c)]: + - @bigcommerce/catalyst-client@0.13.0 + +## 0.19.0 + +### Minor Changes + +- [#1262](https://github.com/bigcommerce/catalyst/pull/1262) [`0c2023b`](https://github.com/bigcommerce/catalyst/commit/0c2023bae650039cd79ba51b1161b5c8c16f0b8d) Thanks [@chanceaclark](https://github.com/chanceaclark)! - Removes all usages of the customer impersonation token. Also updates the docs to correspond with the Storefront API Token. + +- [#1262](https://github.com/bigcommerce/catalyst/pull/1262) [`0c2023b`](https://github.com/bigcommerce/catalyst/commit/0c2023bae650039cd79ba51b1161b5c8c16f0b8d) Thanks [@chanceaclark](https://github.com/chanceaclark)! - Allows the ability to consume a [storefront token](https://developer.bigcommerce.com/docs/rest-authentication/tokens#storefront-tokens). This new token will allow Catalyst to create `customerAccessToken`'s whenever a user logs into their account. This change doesn't include consuming the either token, only adding the ability to pass it in. + +### Patch Changes + +- Updated dependencies [[`0c2023b`](https://github.com/bigcommerce/catalyst/commit/0c2023bae650039cd79ba51b1161b5c8c16f0b8d), [`0c2023b`](https://github.com/bigcommerce/catalyst/commit/0c2023bae650039cd79ba51b1161b5c8c16f0b8d)]: + - @bigcommerce/catalyst-client@0.12.0 + +## 0.18.1 + +### Patch Changes + +- [#1525](https://github.com/bigcommerce/catalyst/pull/1525) [`e751319`](https://github.com/bigcommerce/catalyst/commit/e751319728359a2e72d48072a4b68055ed4dbb1e) Thanks [@bc-alexsaiannyi](https://github.com/bc-alexsaiannyi)! - fix warning for using the same keys on items + +- [#1521](https://github.com/bigcommerce/catalyst/pull/1521) [`fd83a78`](https://github.com/bigcommerce/catalyst/commit/fd83a78f94b170dcf6e8aed14c61e3791b64c5de) Thanks [@bc-yevhenii-buliuk](https://github.com/bc-yevhenii-buliuk)! - fix styles for active account tab + +- [#1520](https://github.com/bigcommerce/catalyst/pull/1520) [`c898792`](https://github.com/bigcommerce/catalyst/commit/c898792a0ed3ee9849cdfeda7018245e491e8016) Thanks [@bc-alexsaiannyi](https://github.com/bc-alexsaiannyi)! - improve error message on reset password page + +- [#1524](https://github.com/bigcommerce/catalyst/pull/1524) [`f08883c`](https://github.com/bigcommerce/catalyst/commit/f08883c8fa559f0b6015321e2396606d77fa0ad6) Thanks [@bc-alexsaiannyi](https://github.com/bc-alexsaiannyi)! - improve behaviour for change password page for logged in user + +- [#1529](https://github.com/bigcommerce/catalyst/pull/1529) [`22426b2`](https://github.com/bigcommerce/catalyst/commit/22426b256e29b6c3dd145fd6df9ed57c5a99bd75) Thanks [@bc-yevhenii-buliuk](https://github.com/bc-yevhenii-buliuk)! - fix validation message for email on account settings page + +- [#1516](https://github.com/bigcommerce/catalyst/pull/1516) [`41270c2`](https://github.com/bigcommerce/catalyst/commit/41270c29a6e21217622c29b18e91f9a24d58ea8b) Thanks [@bc-svc-local](https://github.com/bc-svc-local)! - Update translations. + +- [#1534](https://github.com/bigcommerce/catalyst/pull/1534) [`de48618`](https://github.com/bigcommerce/catalyst/commit/de486186acfec2604d749b9f6d2b4656a9e9280a) Thanks [@bc-svc-local](https://github.com/bc-svc-local)! - Update translations. + +## 0.18.0 + +### Minor Changes + +- [#1491](https://github.com/bigcommerce/catalyst/pull/1491) [`313a591`](https://github.com/bigcommerce/catalyst/commit/313a5913181a144b53cb12208132f4a9924e2256) Thanks [@jorgemoya](https://github.com/jorgemoya)! - Bump `next-intl` which includes [some minor changes and updated APIs](<(https://next-intl-docs.vercel.app/blog/next-intl-3-22)>): + + - Use new `createNavigation` api. + - Pass `locale` to redirects. + - `setRequestLocale` is no longer unstable. + +### Patch Changes + +- [#1505](https://github.com/bigcommerce/catalyst/pull/1505) [`691ec2b`](https://github.com/bigcommerce/catalyst/commit/691ec2bcbb8839446463e292856080cc9b16c584) Thanks [@bc-alexsaiannyi](https://github.com/bc-alexsaiannyi)! - update login page & error message styles + +- [#1506](https://github.com/bigcommerce/catalyst/pull/1506) [`ac83d3e`](https://github.com/bigcommerce/catalyst/commit/ac83d3eb98e19307a3a82fa94c222cff3c0806f0) Thanks [@bc-yevhenii-buliuk](https://github.com/bc-yevhenii-buliuk)! - remove unnecessary fields from account settings form and update confirmation message + +- [#1499](https://github.com/bigcommerce/catalyst/pull/1499) [`b5aea9b`](https://github.com/bigcommerce/catalyst/commit/b5aea9b36159d11a77d090fee62cb1736bc794be) Thanks [@jorgemoya](https://github.com/jorgemoya)! - Bumps next-intl to fix issue with hashes and query params in urls. + +- [#1511](https://github.com/bigcommerce/catalyst/pull/1511) [`370d0b1`](https://github.com/bigcommerce/catalyst/commit/370d0b18f0f47100d7e520fcf9f209f6e41f34e9) Thanks [@bc-alexsaiannyi](https://github.com/bc-alexsaiannyi)! - update styles for reset password validation + +- [#1454](https://github.com/bigcommerce/catalyst/pull/1454) [`53599e6`](https://github.com/bigcommerce/catalyst/commit/53599e6e02988ab63d158c5c9f587669a5581402) Thanks [@bc-yevhenii-buliuk](https://github.com/bc-yevhenii-buliuk)! - remove unnecessary fields from create account form + +- [#1487](https://github.com/bigcommerce/catalyst/pull/1487) [`a22233f`](https://github.com/bigcommerce/catalyst/commit/a22233f8fc94c5ad602fa734cadbb892af34fe6b) Thanks [@bc-svc-local](https://github.com/bc-svc-local)! - Update translations. + +## 0.17.1 + +### Patch Changes + +- Updated dependencies [[`d4120d3`](https://github.com/bigcommerce/catalyst/commit/d4120d39c10398e842a7ebe14ada685ec8aae3a8)]: + - @bigcommerce/catalyst-client@0.11.0 + +## 0.17.0 + +### Minor Changes + +- [#1401](https://github.com/bigcommerce/catalyst/pull/1401) [`3095002`](https://github.com/bigcommerce/catalyst/commit/3095002d7a10b9c4058016076deb7a45fc8ae7bb) Thanks [@bookernath](https://github.com/bookernath)! - Add dynamic robots.txt from control panel settings + +### Patch Changes + +- [#1477](https://github.com/bigcommerce/catalyst/pull/1477) [`79e705f`](https://github.com/bigcommerce/catalyst/commit/79e705f151a733a811effed40757030aba6b6300) Thanks [@deini](https://github.com/deini)! - Breadcrumbs for top level category pages are no longer rendered + +- [#1467](https://github.com/bigcommerce/catalyst/pull/1467) [`e763a83`](https://github.com/bigcommerce/catalyst/commit/e763a83bcd4b8b5311586247291338eb65fbc476) Thanks [@deini](https://github.com/deini)! - Fixes an issue when a numeric product option set to a minimum <= 0 breaks the counter component. + +- [#1459](https://github.com/bigcommerce/catalyst/pull/1459) [`b4485c7`](https://github.com/bigcommerce/catalyst/commit/b4485c76de8c83546c68a7b50fcb7991603dbf6e) Thanks [@chanceaclark](https://github.com/chanceaclark)! - Updates the with-routes middleware to fallback on locale based rewrite logic if the redirect is a dynamic entity redirect. + +- [#1469](https://github.com/bigcommerce/catalyst/pull/1469) [`8e9e7f3`](https://github.com/bigcommerce/catalyst/commit/8e9e7f3d40545004b080146b4dbb42f4ac7cf17c) Thanks [@chanceaclark](https://github.com/chanceaclark)! - Fixes the product quantity reseting back to the previous value when adjusting the quantity fails. + +- [#1476](https://github.com/bigcommerce/catalyst/pull/1476) [`d47e3ac`](https://github.com/bigcommerce/catalyst/commit/d47e3aceb244713bc996287319357e6af3d865ed) Thanks [@deini](https://github.com/deini)! - adds an empty state to category pages + +- [#1458](https://github.com/bigcommerce/catalyst/pull/1458) [`3d67f8d`](https://github.com/bigcommerce/catalyst/commit/3d67f8d0d1776d747e9aa485b0b29a738eeacf3c) Thanks [@chanceaclark](https://github.com/chanceaclark)! - Add no-store to mutations that are rate limited. + +- [#1453](https://github.com/bigcommerce/catalyst/pull/1453) [`1c8b042`](https://github.com/bigcommerce/catalyst/commit/1c8b04278074eb55358a5515f330a011de9561b5) Thanks [@bc-svc-local](https://github.com/bc-svc-local)! - Update translations. + +- Updated dependencies [[`2d1526a`](https://github.com/bigcommerce/catalyst/commit/2d1526a50402b2eb677abd55f19fb904234d1a84)]: + - @bigcommerce/catalyst-client@0.10.0 + +## 0.16.0 + +### Minor Changes + +- [#1410](https://github.com/bigcommerce/catalyst/pull/1410) [`53cca82`](https://github.com/bigcommerce/catalyst/commit/53cca82611272fc3be24505b7c6d5866f10c87fd) Thanks [@bookernath](https://github.com/bookernath)! - Move /reset page to /login/forgot-password in order to reduce top-level routes. + +- [#1384](https://github.com/bigcommerce/catalyst/pull/1384) [`17692ca`](https://github.com/bigcommerce/catalyst/commit/17692caa3ff9b25180359d8a020470ece3e589f6) Thanks [@chanceaclark](https://github.com/chanceaclark)! - Pass customer ip address into requests that don't rely on cached values. + +- [#1388](https://github.com/bigcommerce/catalyst/pull/1388) [`a309a4d`](https://github.com/bigcommerce/catalyst/commit/a309a4dd47083a58c998a4f6d169185177cca571) Thanks [@deini](https://github.com/deini)! - wraps header and footer in suspense boundaries + +### Patch Changes + +- [#1374](https://github.com/bigcommerce/catalyst/pull/1374) [`1f76f61`](https://github.com/bigcommerce/catalyst/commit/1f76f615b38bb41db770653bd8e7947cd6361b18) Thanks [@jorgemoya](https://github.com/jorgemoya)! - Prepend locale for redirected urls in tests. + More info: https://github.com/amannn/next-intl/issues/1335 + +- [#1373](https://github.com/bigcommerce/catalyst/pull/1373) [`971033f`](https://github.com/bigcommerce/catalyst/commit/971033fc63181bad15aa46abb65b0d44501922c9) Thanks [@jorgemoya](https://github.com/jorgemoya)! - Add missing metadata in account settings page. + +- [#1370](https://github.com/bigcommerce/catalyst/pull/1370) [`655d518`](https://github.com/bigcommerce/catalyst/commit/655d518b2fd662614539467fff940b2b5ff78567) Thanks [@bc-svc-local](https://github.com/bc-svc-local)! - Update translations. + +- [#1446](https://github.com/bigcommerce/catalyst/pull/1446) [`ba4820b`](https://github.com/bigcommerce/catalyst/commit/ba4820bf6dd36d0155028ad3db094bd9745d5d94) Thanks [@deini](https://github.com/deini)! - Fixes a bug where product variant was not reliably being selected on PDP when using pre-selected options. + +- [#1391](https://github.com/bigcommerce/catalyst/pull/1391) [`4d64c31`](https://github.com/bigcommerce/catalyst/commit/4d64c31d4765dd72c81c1836b66aa1d7cb34b5f5) Thanks [@bookernath](https://github.com/bookernath)! - Get lossy image from API instead of setting param in code + +- [#1389](https://github.com/bigcommerce/catalyst/pull/1389) [`a4eaff6`](https://github.com/bigcommerce/catalyst/commit/a4eaff6bb2520f748630e24a6a28ca31cd2eb2c3) Thanks [@bookernath](https://github.com/bookernath)! - Add additional IP address header + +- [#1402](https://github.com/bigcommerce/catalyst/pull/1402) [`6e75ef5`](https://github.com/bigcommerce/catalyst/commit/6e75ef5097e0f3227c04ac0d9d7bbc484513bcce) Thanks [@bc-yevhenii-buliuk](https://github.com/bc-yevhenii-buliuk)! - fixing the problem with submitting the password change form + +- [#1407](https://github.com/bigcommerce/catalyst/pull/1407) [`ac9832f`](https://github.com/bigcommerce/catalyst/commit/ac9832fcc61f01413a5b8f101f5f27c53ca1fce5) Thanks [@bc-svc-local](https://github.com/bc-svc-local)! - Update translations. + +- [#1392](https://github.com/bigcommerce/catalyst/pull/1392) [`76227ac`](https://github.com/bigcommerce/catalyst/commit/76227ac06bb349f604f1d2d4a9b68e7d0869eba4) Thanks [@bc-svc-local](https://github.com/bc-svc-local)! - Update translations. + +- [#1424](https://github.com/bigcommerce/catalyst/pull/1424) [`4874add`](https://github.com/bigcommerce/catalyst/commit/4874addfbdde90ac45aa57c10767587ba4c50735) Thanks [@bc-svc-local](https://github.com/bc-svc-local)! - Update translations. + +- [#1445](https://github.com/bigcommerce/catalyst/pull/1445) [`ba3f513`](https://github.com/bigcommerce/catalyst/commit/ba3f513ac4242ce6883ad6ab635d38156a271ca9) Thanks [@deini](https://github.com/deini)! - Adds optimistic updates to all "Add to cart" buttons. This change makes the UI feel snappier and give quick feedback on user interaction. + +- Updated dependencies [[`17692ca`](https://github.com/bigcommerce/catalyst/commit/17692caa3ff9b25180359d8a020470ece3e589f6)]: + - @bigcommerce/catalyst-client@0.9.0 + ## 0.15.0 ### Minor Changes diff --git a/core/app/[locale]/(default)/(auth)/change-password/_actions/change-password.ts b/core/app/[locale]/(default)/(auth)/change-password/_actions/change-password.ts index 3b224c6719..c59383109d 100644 --- a/core/app/[locale]/(default)/(auth)/change-password/_actions/change-password.ts +++ b/core/app/[locale]/(default)/(auth)/change-password/_actions/change-password.ts @@ -54,6 +54,9 @@ export const changePassword = async (_previousState: unknown, formData: FormData newPassword: parsedData.newPassword, }, }, + fetchOptions: { + cache: 'no-store', + }, }); const result = response.data.customer.resetPassword; diff --git a/core/app/[locale]/(default)/(auth)/change-password/_components/change-password-form.tsx b/core/app/[locale]/(default)/(auth)/change-password/_components/change-password-form.tsx index b4a238b03f..c69f2d8206 100644 --- a/core/app/[locale]/(default)/(auth)/change-password/_components/change-password-form.tsx +++ b/core/app/[locale]/(default)/(auth)/change-password/_components/change-password-form.tsx @@ -1,8 +1,8 @@ 'use client'; import { useTranslations } from 'next-intl'; -import { ChangeEvent, useRef, useState } from 'react'; -import { useFormState, useFormStatus } from 'react-dom'; +import { ChangeEvent, useActionState, useRef, useState } from 'react'; +import { useFormStatus } from 'react-dom'; import { Button } from '~/components/ui/button'; import { @@ -48,7 +48,7 @@ export const ChangePasswordForm = ({ customerId, customerToken }: Props) => { const form = useRef(null); const router = useRouter(); - const [state, formAction] = useFormState(changePassword, { + const [state, formAction] = useActionState(changePassword, { status: 'idle', message: '', }); @@ -127,7 +127,7 @@ export const ChangePasswordForm = ({ customerId, customerToken }: Props) => { /> value !== newPassword} > {t('confirmPasswordValidationMessage')} diff --git a/core/app/[locale]/(default)/(auth)/change-password/page.tsx b/core/app/[locale]/(default)/(auth)/change-password/page.tsx index 5bdc9cbe22..455422fdb9 100644 --- a/core/app/[locale]/(default)/(auth)/change-password/page.tsx +++ b/core/app/[locale]/(default)/(auth)/change-password/page.tsx @@ -1,5 +1,4 @@ -import { useTranslations } from 'next-intl'; -import { getTranslations } from 'next-intl/server'; +import { getLocale, getTranslations } from 'next-intl/server'; import { redirect } from '~/i18n/routing'; @@ -14,26 +13,25 @@ export async function generateMetadata() { } interface Props { - searchParams: { + searchParams: Promise<{ c?: string; t?: string; - }; + }>; } -export default function ChangePassword({ searchParams }: Props) { - const t = useTranslations('ChangePassword'); - - const customerId = searchParams.c; - const customerToken = searchParams.t; +export default async function ChangePassword({ searchParams }: Props) { + const { c: customerId, t: customerToken } = await searchParams; + const t = await getTranslations('ChangePassword'); + const locale = await getLocale(); if (!customerId || !customerToken) { - redirect('/login'); + redirect({ href: '/login', locale }); } if (customerId && customerToken) { return (
-

{t('heading')}

+

{t('heading')}

); diff --git a/core/app/[locale]/(default)/(auth)/login/_actions/login.ts b/core/app/[locale]/(default)/(auth)/login/_actions/login.ts index ec5830fdfb..6c89c0969c 100644 --- a/core/app/[locale]/(default)/(auth)/login/_actions/login.ts +++ b/core/app/[locale]/(default)/(auth)/login/_actions/login.ts @@ -1,12 +1,15 @@ 'use server'; -import { isRedirectError } from 'next/dist/client/components/redirect'; +import { unstable_rethrow as rethrow } from 'next/navigation'; +import { getLocale } from 'next-intl/server'; import { Credentials, signIn } from '~/auth'; import { redirect } from '~/i18n/routing'; export const login = async (_previousState: unknown, formData: FormData) => { try { + const locale = await getLocale(); + const credentials = Credentials.parse({ email: formData.get('email'), password: formData.get('password'), @@ -19,12 +22,10 @@ export const login = async (_previousState: unknown, formData: FormData) => { redirect: false, }); - redirect('/account'); + redirect({ href: '/account/', locale }); + // redirect({ href: process.env.STENCIL_URL + '/account.php?action=order_status', locale }); } catch (error: unknown) { - // We need to throw this error to trigger the redirect as Next.js uses error boundaries to redirect. - if (isRedirectError(error)) { - throw error; - } + rethrow(error); return { status: 'error', diff --git a/core/app/[locale]/(default)/(auth)/login/_components/login-form.tsx b/core/app/[locale]/(default)/(auth)/login/_components/login-form.tsx index 17058eb694..3e25dcf235 100644 --- a/core/app/[locale]/(default)/(auth)/login/_components/login-form.tsx +++ b/core/app/[locale]/(default)/(auth)/login/_components/login-form.tsx @@ -1,8 +1,8 @@ 'use client'; import { useTranslations } from 'next-intl'; -import { ChangeEvent, useState } from 'react'; -import { useFormState, useFormStatus } from 'react-dom'; +import { ChangeEvent, useActionState, useState } from 'react'; +import { useFormStatus } from 'react-dom'; import { Breadcrumbs as ComponentsBreadcrumbs } from '~/components/ui/breadcrumbs'; import { Link } from '~/components/link'; @@ -41,7 +41,7 @@ export const LoginForm = () => { const [isEmailValid, setIsEmailValid] = useState(true); const [isPasswordValid, setIsPasswordValid] = useState(true); - const [state, formAction] = useFormState(login, { status: 'idle' }); + const [state, formAction] = useActionState(login, { status: 'idle' }); const { accountState } = useAccountStatusContext(); const isFormInvalid = state?.status === 'error'; @@ -75,7 +75,7 @@ export const LoginForm = () => {

{t('Form.error')}

)} -
+ Email Address: @@ -90,14 +90,14 @@ export const LoginForm = () => { /> {t('Form.enterEmailMessage')} - Password: + Password: { /> {t('Form.entePasswordMessage')} -
+
- {t('Form.resetPassword')} + {t('Form.forgotPassword')}
diff --git a/core/app/[locale]/(default)/(auth)/reset/_actions/reset-password.ts b/core/app/[locale]/(default)/(auth)/login/forgot-password/_actions/reset-password.ts similarity index 70% rename from core/app/[locale]/(default)/(auth)/reset/_actions/reset-password.ts rename to core/app/[locale]/(default)/(auth)/login/forgot-password/_actions/reset-password.ts index 025ccd2a5a..228a7415bf 100644 --- a/core/app/[locale]/(default)/(auth)/reset/_actions/reset-password.ts +++ b/core/app/[locale]/(default)/(auth)/login/forgot-password/_actions/reset-password.ts @@ -10,6 +10,22 @@ const ResetPasswordSchema = z.object({ email: z.string().email(), }); +const processZodErrors = (err: z.ZodError) => { + const { fieldErrors, formErrors } = err.flatten((issue: z.ZodIssue) => ({ + message: issue.message, + })); + + if (formErrors.length > 0) { + return formErrors.join('\n'); + } + + return Object.entries(fieldErrors) + .map(([, errorList]) => { + return `${errorList?.map(({ message }) => message).join('\n')}`; + }) + .join('\n'); +}; + const ResetPasswordMutation = graphql(` mutation ResetPassword($input: RequestResetPasswordInput!, $reCaptcha: ReCaptchaV2Input) { customer { @@ -37,7 +53,7 @@ export const resetPassword = async ({ path, reCaptchaToken, }: SubmitResetPasswordForm) => { - const t = await getTranslations('Reset'); + const t = await getTranslations('Login.ForgotPassword'); try { const parsedData = ResetPasswordSchema.parse({ @@ -53,6 +69,9 @@ export const resetPassword = async ({ }, ...(reCaptchaToken && { reCaptchaV2: { token: reCaptchaToken } }), }, + fetchOptions: { + cache: 'no-store', + }, }); const result = response.data.customer.requestResetPassword; @@ -66,7 +85,14 @@ export const resetPassword = async ({ error: result.errors.map((error) => error.message).join('\n'), }; } catch (error: unknown) { - if (error instanceof Error || error instanceof z.ZodError) { + if (error instanceof z.ZodError) { + return { + status: 'error', + error: processZodErrors(error), + }; + } + + if (error instanceof Error) { return { status: 'error', error: error.message }; } diff --git a/core/app/[locale]/(default)/(auth)/reset/_components/reset-password-form/fragment.ts b/core/app/[locale]/(default)/(auth)/login/forgot-password/_components/reset-password-form/fragment.ts similarity index 100% rename from core/app/[locale]/(default)/(auth)/reset/_components/reset-password-form/fragment.ts rename to core/app/[locale]/(default)/(auth)/login/forgot-password/_components/reset-password-form/fragment.ts diff --git a/core/app/[locale]/(default)/(auth)/reset/_components/reset-password-form/index.tsx b/core/app/[locale]/(default)/(auth)/login/forgot-password/_components/reset-password-form/index.tsx similarity index 87% rename from core/app/[locale]/(default)/(auth)/reset/_components/reset-password-form/index.tsx rename to core/app/[locale]/(default)/(auth)/login/forgot-password/_components/reset-password-form/index.tsx index c1e662ec1c..eb0c370e1b 100644 --- a/core/app/[locale]/(default)/(auth)/reset/_components/reset-password-form/index.tsx +++ b/core/app/[locale]/(default)/(auth)/login/forgot-password/_components/reset-password-form/index.tsx @@ -34,7 +34,7 @@ interface FormStatus { } const SubmitButton = () => { - const t = useTranslations('Reset.Form'); + const t = useTranslations('Login.ForgotPassword.Form'); const { pending } = useFormStatus(); @@ -52,7 +52,7 @@ const SubmitButton = () => { }; export const ResetPasswordForm = ({ reCaptchaSettings }: Props) => { - const t = useTranslations('Reset.Form'); + const t = useTranslations('Login.ForgotPassword.Form'); const form = useRef(null); const [formStatus, setFormStatus] = useState(null); @@ -80,7 +80,7 @@ export const ResetPasswordForm = ({ reCaptchaSettings }: Props) => { }; const handleEmailValidation = (e: ChangeEvent) => { - const validationStatus = e.target.validity.valueMissing; + const validationStatus = e.target.validity.valueMissing || e.target.validity.typeMismatch; setIsEmailValid(!validationStatus); }; @@ -122,12 +122,12 @@ export const ResetPasswordForm = ({ reCaptchaSettings }: Props) => { return ( <> {formStatus?.status === 'error' && ( - +

{formStatus.message}

)} -

{t('description')}

+

{t('description')}

@@ -144,11 +144,17 @@ export const ResetPasswordForm = ({ reCaptchaSettings }: Props) => { /> {t('emailValidationMessage')} + + {t('emailValidationMessage')} + {reCaptchaSettings?.isEnabledOnStorefront && ( @@ -159,7 +165,7 @@ export const ResetPasswordForm = ({ reCaptchaSettings }: Props) => { sitekey={reCaptchaSettings.siteKey} /> {!isReCaptchaValid && ( - + {t('recaptchaText')} )} diff --git a/core/app/[locale]/(default)/(auth)/reset/page.tsx b/core/app/[locale]/(default)/(auth)/login/forgot-password/page.tsx similarity index 57% rename from core/app/[locale]/(default)/(auth)/reset/page.tsx rename to core/app/[locale]/(default)/(auth)/login/forgot-password/page.tsx index 3608b38cce..8021665c7c 100644 --- a/core/app/[locale]/(default)/(auth)/reset/page.tsx +++ b/core/app/[locale]/(default)/(auth)/login/forgot-password/page.tsx @@ -7,7 +7,13 @@ import { bypassReCaptcha } from '~/lib/bypass-recaptcha'; import { ResetPasswordForm } from './_components/reset-password-form'; import { ResetPasswordFormFragment } from './_components/reset-password-form/fragment'; - +import { Forgotpasswordbreadcrumb as ComponentsBreadcrumbs } from '~/components/ui/breadcrumbs/forgotpasswordbreadcrumbs'; +const Forgotpasswordbreadcrumb: any = [ + { + label: 'Forgot Password', + href: '/login/forgot-password/', + }, +]; const ResetPageQuery = graphql( ` query ResetPageQuery { @@ -24,7 +30,7 @@ const ResetPageQuery = graphql( ); export async function generateMetadata() { - const t = await getTranslations('Reset'); + const t = await getTranslations('Login.ForgotPassword'); return { title: t('title'), @@ -32,17 +38,22 @@ export async function generateMetadata() { } export default async function Reset() { - const t = await getTranslations('Reset'); + const t = await getTranslations('Login.ForgotPassword'); const { data } = await client.fetch({ document: ResetPageQuery, fetchOptions: { next: { revalidate } }, }); + const recaptchaSettings = await bypassReCaptcha(data.site.settings?.reCaptcha); + return ( -
+
+
+ +

{t('heading')}

- + < ResetPasswordForm reCaptchaSettings={recaptchaSettings} />
); } diff --git a/core/app/[locale]/(default)/(auth)/login/page.tsx b/core/app/[locale]/(default)/(auth)/login/page.tsx index f27a88bdb6..03ea9cc122 100644 --- a/core/app/[locale]/(default)/(auth)/login/page.tsx +++ b/core/app/[locale]/(default)/(auth)/login/page.tsx @@ -1,13 +1,16 @@ -import { useTranslations } from 'next-intl'; -import { getTranslations, unstable_setRequestLocale } from 'next-intl/server'; +import { getTranslations, setRequestLocale } from 'next-intl/server'; import { Breadcrumbs as ComponentsBreadcrumbs } from '~/components/ui/breadcrumbs'; import { Link } from '~/components/link'; import { Button } from '~/components/ui/button'; -import { locales, LocaleType } from '~/i18n/routing'; +import { locales } from '~/i18n/routing'; import { LoginForm } from './_components/login-form'; -export async function generateMetadata() { +export async function generateMetadata({ params }: Props) { + const { locale } = await params; + + setRequestLocale(locale); + const t = await getTranslations('Login'); return { @@ -16,13 +19,15 @@ export async function generateMetadata() { } interface Props { - params: { locale: LocaleType }; + params: Promise<{ locale: string }>; } -export default function Login({ params: { locale } }: Props) { - unstable_setRequestLocale(locale); +export default async function Login({ params }: Props) { + const { locale } = await params; - const t = useTranslations('Login'); + setRequestLocale(locale); + + const t = await getTranslations('Login'); const breadcrumbs: any = [ { @@ -33,12 +38,14 @@ export default function Login({ params: { locale } }: Props) { return (
+
+

Sign in

-
+

{t('CreateAccount.heading')}

diff --git a/core/app/[locale]/(default)/(auth)/register/_actions/login.ts b/core/app/[locale]/(default)/(auth)/register/_actions/login.ts index 4342822917..f073752180 100644 --- a/core/app/[locale]/(default)/(auth)/register/_actions/login.ts +++ b/core/app/[locale]/(default)/(auth)/register/_actions/login.ts @@ -1,12 +1,15 @@ 'use server'; -import { isRedirectError } from 'next/dist/client/components/redirect'; +import { unstable_rethrow as rethrow } from 'next/navigation'; +import { getLocale } from 'next-intl/server'; import { Credentials, signIn } from '~/auth'; import { redirect } from '~/i18n/routing'; export const login = async (formData: FormData) => { try { + const locale = await getLocale(); + const credentials = Credentials.parse({ email: formData.get('customer-email'), password: formData.get('customer-password'), @@ -19,11 +22,9 @@ export const login = async (formData: FormData) => { redirect: false, }); - redirect('/account'); + redirect({ href: '/account', locale }); } catch (error: unknown) { - if (isRedirectError(error)) { - throw error; - } + rethrow(error); return { status: 'error', diff --git a/core/app/[locale]/(default)/(auth)/register/_actions/register-customer.ts b/core/app/[locale]/(default)/(auth)/register/_actions/register-customer.ts index fb564facea..9207237435 100644 --- a/core/app/[locale]/(default)/(auth)/register/_actions/register-customer.ts +++ b/core/app/[locale]/(default)/(auth)/register/_actions/register-customer.ts @@ -43,7 +43,7 @@ interface RegisterCustomerForm { } const isRegisterCustomerInput = (data: unknown): data is RegisterCustomerInput => { - if (typeof data === 'object' && data !== null && 'email' in data && 'address' in data) { + if (typeof data === 'object' && data !== null && 'email' in data) { return true; } @@ -71,6 +71,9 @@ export const registerCustomer = async ({ formData, reCaptchaToken }: RegisterCus input: parsedData, ...(reCaptchaToken && { reCaptchaV2: { token: reCaptchaToken } }), }, + fetchOptions: { + cache: 'no-store', + }, }); const result = response.data.customer.registerCustomer; diff --git a/core/app/[locale]/(default)/(auth)/register/_components/register-customer-form.tsx b/core/app/[locale]/(default)/(auth)/register/_components/register-customer-form.tsx index 3e0032c11c..177feb24c6 100644 --- a/core/app/[locale]/(default)/(auth)/register/_components/register-customer-form.tsx +++ b/core/app/[locale]/(default)/(auth)/register/_components/register-customer-form.tsx @@ -1,10 +1,10 @@ 'use client'; - + import { useTranslations } from 'next-intl'; import { ChangeEvent, MouseEvent, useRef, useState } from 'react'; import { useFormStatus } from 'react-dom'; import ReCaptcha from 'react-google-recaptcha'; - + import { useAccountStatusContext } from '~/app/[locale]/(default)/account/(tabs)/_components/account-status-provider'; import { ExistingResultType } from '~/client/util'; import { @@ -34,23 +34,22 @@ import { import { Button } from '~/components/ui/button'; import { Field, Form, FormSubmit } from '~/components/ui/form'; import { Message } from '~/components/ui/message'; - + import { login } from '../_actions/login'; import { registerCustomer } from '../_actions/register-customer'; import { getRegisterCustomerQuery } from '../page-data'; -import { log } from 'console'; - + interface FormStatus { status: 'success' | 'error'; message: string; } - + type CustomerFields = ExistingResultType['customerFields']; type AddressFields = ExistingResultType['addressFields']; type Countries = ExistingResultType['countries']; type CountryCode = Countries[number]['code']; type CountryStates = Countries[number]['statesOrProvinces']; - + interface RegisterCustomerProps { addressFields: AddressFields; countries: Countries; @@ -65,17 +64,17 @@ interface RegisterCustomerProps { siteKey: string; }; } - + interface SumbitMessages { messages: { submit: string; submitting: string; }; } - + const SubmitButton = ({ messages }: SumbitMessages) => { const { pending } = useFormStatus(); - + return ( ); }; - + export const RegisterCustomerForm = ({ addressFields, countries, @@ -97,7 +96,7 @@ export const RegisterCustomerForm = ({ }: RegisterCustomerProps) => { const form = useRef(null); const [formStatus, setFormStatus] = useState(null); - + const [textInputValid, setTextInputValid] = useState>({}); const [passwordValid, setPassswordValid] = useState>({ [FieldNameToFieldId.password]: true, @@ -110,21 +109,21 @@ export const RegisterCustomerForm = ({ const [picklistValid, setPicklistValid] = useState>({}); const [checkboxesValid, setCheckboxesValid] = useState>({}); const [multiTextValid, setMultiTextValid] = useState>({}); - + const reCaptchaRef = useRef(null); const [reCaptchaToken, setReCaptchaToken] = useState(''); const [isReCaptchaValid, setReCaptchaValid] = useState(true); - + const { setAccountState } = useAccountStatusContext(); - + const t = useTranslations('Register.Form'); - + const handleTextInputValidation = (e: ChangeEvent) => { const fieldId = Number(e.target.id.split('-')[1]); - + const validityState = e.target.validity; const validationStatus = validityState.valueMissing || validityState.typeMismatch; - + setTextInputValid({ ...textInputValid, [fieldId]: !validationStatus }); }; const handleNumbersInputValidation = createNumbersInputValidationHandler( @@ -138,38 +137,38 @@ export const RegisterCustomerForm = ({ const handleDatesValidation = createDatesValidationHandler(setDatesValid, datesValid); const handlePasswordValidation = (e: ChangeEvent) => { const fieldId = e.target.id.split('-')[1] ?? ''; - + switch (FieldNameToFieldId[Number(fieldId)]) { case 'password': { setPassswordValid((prevState) => ({ ...prevState, [fieldId]: !e.target.validity.valueMissing, })); - + return; } - + case 'confirmPassword': { const confirmPassword = e.target.value; const field = customerFields.find( ({ entityId }) => entityId === Number(FieldNameToFieldId.password), ); - + if (!isAddressOrAccountFormField(field)) { return; } - + const passwordFieldName = createFieldName(field, 'customer'); const password = new FormData(e.target.form ?? undefined).get(passwordFieldName); - + setPassswordValid((prevState) => ({ ...prevState, [fieldId]: password === confirmPassword && !e.target.validity.valueMissing, })); - + return; } - + default: { setPassswordValid((prevState) => ({ ...prevState, @@ -178,10 +177,10 @@ export const RegisterCustomerForm = ({ } } }; - + const handleCountryChange = (value: string) => { const states = countries.find(({ code }) => code === value)?.statesOrProvinces; - + setCountryStates(states ?? []); }; const handleRadioButtonsChange = createRadioButtonsValidationHandler( @@ -204,59 +203,59 @@ export const RegisterCustomerForm = ({ validateCheckboxFields(form.current); } }; - + const onReCaptchaChange = (token: string | null) => { if (!token) { setReCaptchaValid(false); - + return; } - + setReCaptchaToken(token); setReCaptchaValid(true); }; - + const onSubmit = async (formData: FormData) => { if (formData.get('customer-password') !== formData.get('customer-confirmPassword')) { setFormStatus({ status: 'error', message: t('confirmPassword'), }); - + window.scrollTo({ top: 0, behavior: 'smooth', }); - + return; } - + if (reCaptchaSettings?.isEnabledOnStorefront && !reCaptchaToken) { setReCaptchaValid(false); - + return; } - + setReCaptchaValid(true); - + const submit = await registerCustomer({ formData, reCaptchaToken }); - + if (submit.status === 'success') { setAccountState({ status: 'success' }); - + await login(formData); } - + if (submit.status === 'error') { setFormStatus({ status: 'error', message: submit.error ?? '' }); } - + window.scrollTo({ top: 0, behavior: 'smooth', }); }; - + return ( <> {formStatus && ( @@ -265,8 +264,8 @@ export const RegisterCustomerForm = ({ )} -
- +
+ {customerFields .filter((field) => !CUSTOMER_FIELDS_TO_EXCLUDE.includes(field.entityId)) .map((field) => { @@ -285,7 +284,7 @@ export const RegisterCustomerForm = ({ /> ); - + case 'MultilineTextFormField': { return ( @@ -298,7 +297,7 @@ export const RegisterCustomerForm = ({ ); } - + case 'NumberFormField': { return ( @@ -311,7 +310,7 @@ export const RegisterCustomerForm = ({ ); } - + case 'DateFormField': { return ( @@ -325,7 +324,7 @@ export const RegisterCustomerForm = ({ ); } - + case 'RadioButtonsFormField': { return ( @@ -338,7 +337,7 @@ export const RegisterCustomerForm = ({ ); } - + case 'PicklistFormField': { return ( @@ -352,7 +351,7 @@ export const RegisterCustomerForm = ({ ); } - + case 'CheckboxesFormField': { return ( @@ -366,7 +365,7 @@ export const RegisterCustomerForm = ({ ); } - + case 'PasswordFormField': { return ( @@ -379,12 +378,12 @@ export const RegisterCustomerForm = ({ ); } - + default: return null; } })} - {addressFields.map((field) => { + {addressFields.map((field) => { const fieldId = field.entityId; const fieldName = createFieldName(field, 'address'); @@ -501,6 +500,7 @@ export const RegisterCustomerForm = ({ } field={field} name={fieldName} + options={countryStates.map(({ name }) => { return { entityId: name, label: name }; })} @@ -528,7 +528,7 @@ export const RegisterCustomerForm = ({ })}
- + {reCaptchaSettings?.isEnabledOnStorefront && ( )}
- - - - +
+ + + +
+ ); -}; - - \ No newline at end of file +}; \ No newline at end of file diff --git a/core/app/[locale]/(default)/(auth)/register/page-data.ts b/core/app/[locale]/(default)/(auth)/register/page-data.ts index 977aa45959..919742b8fa 100644 --- a/core/app/[locale]/(default)/(auth)/register/page-data.ts +++ b/core/app/[locale]/(default)/(auth)/register/page-data.ts @@ -1,6 +1,6 @@ import { cache } from 'react'; -import { getSessionCustomerId } from '~/auth'; +import { getSessionCustomerAccessToken } from '~/auth'; import { client } from '~/client'; import { graphql, VariablesOf } from '~/client/graphql'; import { FormFieldsFragment } from '~/components/form-fields/fragment'; @@ -69,7 +69,7 @@ interface Props { } export const getRegisterCustomerQuery = cache(async ({ address, customer }: Props = {}) => { - const customerId = await getSessionCustomerId(); + const customerAccessToken = await getSessionCustomerAccessToken(); const response = await client.fetch({ document: RegisterCustomerQuery, @@ -80,7 +80,7 @@ export const getRegisterCustomerQuery = cache(async ({ address, customer }: Prop customerSortBy: customer?.sortBy, }, fetchOptions: { cache: 'no-store' }, - customerId, + customerAccessToken, }); const addressFields = response.data.site.settings?.formFields.shippingAddress; @@ -89,7 +89,7 @@ export const getRegisterCustomerQuery = cache(async ({ address, customer }: Prop const countries = response.data.geography.countries; const defaultCountry = response.data.site.settings?.contact?.country; - const reCaptchaSettings = bypassReCaptcha(response.data.site.settings?.reCaptcha); + const reCaptchaSettings = await bypassReCaptcha(response.data.site.settings?.reCaptcha); if (!addressFields || !customerFields || !countries) { return null; diff --git a/core/app/[locale]/(default)/(auth)/register/page.tsx b/core/app/[locale]/(default)/(auth)/register/page.tsx index 8b8a6bc5eb..90786d3696 100644 --- a/core/app/[locale]/(default)/(auth)/register/page.tsx +++ b/core/app/[locale]/(default)/(auth)/register/page.tsx @@ -47,6 +47,8 @@ export default async function Register() { reCaptchaSettings, } = registerCustomerData; + const reCaptcha = await bypassReCaptcha(reCaptchaSettings); + const { code = FALLBACK_COUNTRY.code, entityId = FALLBACK_COUNTRY.entityId, @@ -54,21 +56,23 @@ export default async function Register() { } = countries.find(({ name }) => name === defaultCountry) || {}; return ( -
+
+
-

{t('heading')}

+ className="login-div login-breadcrumb mx-auto px-[1px]" + breadcrumbs={breadcrumbs} + /> +
+

{t('heading')}

); } -export const runtime = 'edge'; +export const runtime = 'edge'; \ No newline at end of file diff --git a/core/app/[locale]/(default)/(faceted)/_components/ProductLimitSelector.tsx b/core/app/[locale]/(default)/(faceted)/_components/ProductLimitSelector.tsx index 822816b7e2..d27ed48028 100644 --- a/core/app/[locale]/(default)/(faceted)/_components/ProductLimitSelector.tsx +++ b/core/app/[locale]/(default)/(faceted)/_components/ProductLimitSelector.tsx @@ -1,41 +1,57 @@ - +// ProductCountFilter.tsx "use client"; import React, { useState, useEffect } from 'react'; -import { ProductCard } from '~/components/product-card'; -import { fetchFacetedSearch } from '../fetch-faceted-search'; -import { storeProductLimitInCookies } from '../_actions/store-prod-data-limit'; -interface ProductLimitSelectorProps { - initialProducts: any[]; - categoryId: number; - searchParams: Record; -} - -export function ProductLimitSelector({ initialProducts, categoryId, searchParams }: ProductLimitSelectorProps) { - const [productLimit, setProductLimit] = useState(50); - const [products, setProducts] = useState(initialProducts); - - const handleLimitChange = async (event: React.ChangeEvent) => { - const newLimit = Number(event.target.value); - storeProductLimitInCookies(newLimit) - setProductLimit(newLimit); - - - const search = await fetchFacetedSearch({ ...searchParams, category: categoryId, limit: newLimit }); - setProducts(search.products.items); +import { useRouter, useSearchParams } from 'next/navigation'; + +const ProductCountFilter = () => { + const router = useRouter(); + const searchParams = useSearchParams(); + const [productsPerPage, setProductsPerPage] = useState('20'); + const [isClient, setIsClient] = useState(false); + + useEffect(() => { + setIsClient(true); + const limit = searchParams.get('limit'); + if (limit) { + setProductsPerPage(limit); + } + }, [searchParams]); + + const handleCountChange = (event: React.ChangeEvent) => { + const newCount = event.target.value; + setProductsPerPage(newCount); + + const params = new URLSearchParams(searchParams.toString()); + params.set('limit', newCount); + params.set('page', '1'); + + router.push(`${window.location.pathname}?${params.toString()}`); }; + if (!isClient) { + return ( +
+ 20 Products +
+ ); + } + return ( -
- {productLimit} - + + + + + - -
); -} +}; + +export default ProductCountFilter; \ No newline at end of file diff --git a/core/app/[locale]/(default)/(faceted)/_components/faceted-search.tsx b/core/app/[locale]/(default)/(faceted)/_components/faceted-search.tsx index 98fa69ac1d..1809864cbb 100644 --- a/core/app/[locale]/(default)/(faceted)/_components/faceted-search.tsx +++ b/core/app/[locale]/(default)/(faceted)/_components/faceted-search.tsx @@ -24,10 +24,11 @@ export const FacetedSearch = ({ {children} - +
+
); }; diff --git a/core/app/[locale]/(default)/(faceted)/_components/facets.tsx b/core/app/[locale]/(default)/(faceted)/_components/facets.tsx index 96cc0a0f94..167934cf3f 100644 --- a/core/app/[locale]/(default)/(faceted)/_components/facets.tsx +++ b/core/app/[locale]/(default)/(faceted)/_components/facets.tsx @@ -105,7 +105,7 @@ export const Facets = ({ facets, pageType }: Props) => { onCheckedChange={submitForm} value={brand.entityId} /> -