diff --git a/.changeset/bitter-comics-cry.md b/.changeset/bitter-comics-cry.md deleted file mode 100644 index be97dc329c..0000000000 --- a/.changeset/bitter-comics-cry.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -"@bigcommerce/catalyst-core": minor ---- - -## New - -This refactor optimizes search PLP for caching and the eventual use of dynamicIO. With these changes we leverage data caching to hit mostly cache data for guest shoppers in different locales and with different currencies. - -## Key modifications include: - -- We don't stream in Search page data, instead it's a blocking call to get page data. -- Our query functions now take in all params required for fetching, instead of accessing dynamic variables internally. This is important to serialize arguments if we want to eventually `use cache`. -- Use `Streamable.from` to generate our streaming props that are passed to our UI components. -- Remove use of nuqs' `createSearchParamsCache` in favor of nuqs' `createLoader`. - -## Migration instructions: - -- Update `/(facted)/search/page.tsx` - - For this page we are now doing a blocking request for brand page data. Instead of having functions that each would read from props, we share streamable functions that can be passed to our UI components. We still stream in filter and product data. diff --git a/.changeset/breezy-olives-own.md b/.changeset/breezy-olives-own.md deleted file mode 100644 index 6ad261403c..0000000000 --- a/.changeset/breezy-olives-own.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - - - Update the account pages to match the style of VIBES and remain consistent with the rest of Catalyst. - - Updated OrderDetails line items styling to display cost of each item and the selected `productOptions` - - Created OrderDetails skeletons - - Updated /account/orders/[id] to use `Streamable` - -## Migration - -1. Copy all changes in the `/core/vibes/soul` directory -2. Copy all changes in the `/core/app/[locale]/(default)/account` directory diff --git a/.changeset/bumpy-waves-behave.md b/.changeset/bumpy-waves-behave.md deleted file mode 100644 index 33d13838ab..0000000000 --- a/.changeset/bumpy-waves-behave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Refactors redirecting to checkout as a route. This will enable session syncing to happen through a redirect using the sites and routes API. diff --git a/.changeset/chubby-coats-cross.md b/.changeset/chubby-coats-cross.md deleted file mode 100644 index 736176e19b..0000000000 --- a/.changeset/chubby-coats-cross.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": minor ---- - -Adds a new analytics provider meant to replace the other provider. This provider is built being framework agnostic but exposes a react provider to use within context. The initial implementation comes with a Google Analytics provider with some basic events to get started. We need to add some other events around starting checkout, banners, consent loading, and search. This change is additive only so no migration is needed until consumption. diff --git a/.changeset/clean-humans-wonder.md b/.changeset/clean-humans-wonder.md deleted file mode 100644 index 7de27af34d..0000000000 --- a/.changeset/clean-humans-wonder.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Revert UI changes for product form since streaming in fields causes an issue with the form. diff --git a/.changeset/cold-poets-refuse.md b/.changeset/cold-poets-refuse.md deleted file mode 100644 index 443d2b3967..0000000000 --- a/.changeset/cold-poets-refuse.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Refactors the sign in functionality to use two separate providers instead of one. This is some work needed to be done in order to provide a better API for session syncing so it shouldn't effect any existing functionality. diff --git a/.changeset/config.json b/.changeset/config.json index 6e6e393892..b326eacda5 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -13,5 +13,5 @@ }, "baseBranch": "canary", "updateInternalDependencies": "patch", - "ignore": [] + "ignore": ["@bigcommerce/catalyst"] } diff --git a/.changeset/cuddly-seals-exist.md b/.changeset/cuddly-seals-exist.md deleted file mode 100644 index 803c29e2f8..0000000000 --- a/.changeset/cuddly-seals-exist.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Translate home breadcrumb in Contact Us page. diff --git a/.changeset/dry-frogs-pump.md b/.changeset/dry-frogs-pump.md deleted file mode 100644 index 791860beb1..0000000000 --- a/.changeset/dry-frogs-pump.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@bigcommerce/catalyst-client": minor ---- - -GQL requests that respond as `200` but have an `errors` field will now be properly handled by the client and throw a proper `BigCommerceGQLError` response with the message reason from the API. This will provide a more detailed description of why the GQL request errored out. - -API errors will still be handled and attribute the errored status as the message with this change as `BigCommerceAPIError`. diff --git a/.changeset/eighty-bushes-cut.md b/.changeset/eighty-bushes-cut.md deleted file mode 100644 index 0d1801147f..0000000000 --- a/.changeset/eighty-bushes-cut.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Add textarea field to product details form. diff --git a/.changeset/eighty-nails-juggle.md b/.changeset/eighty-nails-juggle.md deleted file mode 100644 index 5864cefd8a..0000000000 --- a/.changeset/eighty-nails-juggle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Remove unused variants collection from query for PDP diff --git a/.changeset/empty-dodos-try.md b/.changeset/empty-dodos-try.md deleted file mode 100644 index 44857cea15..0000000000 --- a/.changeset/empty-dodos-try.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Revert auth route reorganization to fix regression with /login/token endpoint diff --git a/.changeset/fair-badgers-try.md b/.changeset/fair-badgers-try.md deleted file mode 100644 index 747d82fe7f..0000000000 --- a/.changeset/fair-badgers-try.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -## New - -This refactor optimizes PDP for caching and the eventual use of dynamicIO. With these changes we leverage data caching to hit mostly cache data for guest shoppers in different locales and with different currencies. - -## Key modifications include: - -- Split queries into four: - - Page Metadata (metadata fields that only depend on locale) - - Product (for fields that only depend on locale) - - Streamable Product (for fields that depend on locale and variant selection) - - Product Pricing and Related Products (for fields that require locale, variant selection, and currency -- in this case, pricing and related products) -- We don't stream in Product data, instead it's a blocking call that will redirect to `notFound` when product is not found. -- Our query functions now take in all params required for fetching, instead of accessing dynamic variables internally. This is important to serialize arguments if we want to eventually `use cache`. -- Use `Streamable.from` to generate our streaming props that are passed to our UI components. -- Update UI components to allow streaming product options before streaming in buy button. - -## Migration instructions: - -- Update `/product/[slug]/page.tsx` - - For this page we are now doing a blocking request that is simplified for metadata and as a base product. Instead of having functions that each would read from props, we share streamable functions that can be passed to our UI components. -- Update `/product/[slug]/page-data.tsx` - - Expect our requests to be simplified/merged, essentially replacing what we had before for new requests and functions. -- Update`/product/[slug]/_components`. - - Similar to `page.tsx` and `page.data`, expect changes in the fragments defined and how we pass streamable functions to UI components. -- Update `/vibes/soul/product-detail/index.tsx` & `/vibes/soul/product-detail/product-detail-form.tsx` - - Minor changes to allow streaming in data. diff --git a/.changeset/fast-walls-shout.md b/.changeset/fast-walls-shout.md deleted file mode 100644 index 02b06ac4ac..0000000000 --- a/.changeset/fast-walls-shout.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": minor ---- - -Removes the old analytics provider in favor of the provider that fetches the configuration from the GraphQL API. diff --git a/.changeset/fifty-dodos-breathe.md b/.changeset/fifty-dodos-breathe.md deleted file mode 100644 index 68c2c6535a..0000000000 --- a/.changeset/fifty-dodos-breathe.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Fixes types for signIn credentials and improves error handling for registering a customer. diff --git a/.changeset/fine-files-enjoy.md b/.changeset/fine-files-enjoy.md deleted file mode 100644 index a69f0246d2..0000000000 --- a/.changeset/fine-files-enjoy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Disable PPR in Compare page due to an issue of Next.js and PPR, which causes the products to be removed once one is added to cart. More info: https://github.com/vercel/next.js/issues/59407. diff --git a/.changeset/five-seals-chew.md b/.changeset/five-seals-chew.md deleted file mode 100644 index 95742f1a17..0000000000 --- a/.changeset/five-seals-chew.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Add result type to all `generateMetadata`. diff --git a/.changeset/flat-kiwis-design.md b/.changeset/flat-kiwis-design.md deleted file mode 100644 index 56b4001b48..0000000000 --- a/.changeset/flat-kiwis-design.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Properly handle the auth error when login is invalid. diff --git a/.changeset/forty-pants-pump.md b/.changeset/forty-pants-pump.md deleted file mode 100644 index fe2a3554cb..0000000000 --- a/.changeset/forty-pants-pump.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Fix style override issues with the latest version of the Tailwind bump. Changes should be easily rebasable. diff --git a/.changeset/fruity-dingos-sip.md b/.changeset/fruity-dingos-sip.md deleted file mode 100644 index f27b60a444..0000000000 --- a/.changeset/fruity-dingos-sip.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Memoize `GetCartCountQuery` using React.js `cache()` so that it only hits the GraphQL API once per render, instead of twice. diff --git a/.changeset/fruity-dodos-behave.md b/.changeset/fruity-dodos-behave.md deleted file mode 100644 index d856e2eec1..0000000000 --- a/.changeset/fruity-dodos-behave.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -"@bigcommerce/catalyst-core": minor ---- - -## New - -This refactor optimizes home page, header, and footer for caching and the eventual use of dynamicIO. With these changes we leverage data caching to hit mostly cache data for guest shoppers in different locales and with different currencies. - -## Key modifications include: - -- Header and Footer now have a blocking request for the shared data that is the same for all users. -- Data that can change for logged in users is now a separate request. -- Our query functions now take in all params required for fetching, instead of accessing dynamic variables internally. This is important to serialize arguments if we want to eventually `use cache`. -- Dynamic fetches (using customerAccessToken or preferred currency) are now all streaming queries. -- Use `Streamable.from` to generate our streaming props that are passed to our UI components. -- Update Header UI component to allow streaming in of currencies data. - -## Migration instructions: - -- Renamed `/app/[locale]/(default)/query.ts` to `/app/[locale]/(default)/page-data.ts`, include page query on this page. -- Updated `/app/[locale]/(default)/page.ts` to use `Streamable.from` pattern. -- Split data that can vary by user from `core/components/footer/fragment.ts` and `core/components/header/fragment.ts` -- Updated `core/components/header/index.tsx` and `core/components/footer/index.tsx` to fetch shared data in a blocking request and pass data that varies by customer as streamable data. Updated to use the new `Streamable.from` pattern. diff --git a/.changeset/fuzzy-beds-lick.md b/.changeset/fuzzy-beds-lick.md deleted file mode 100644 index 99597b6611..0000000000 --- a/.changeset/fuzzy-beds-lick.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Rename some GQL query/mutations/fragments to standardized naming. diff --git a/.changeset/gentle-seals-sing.md b/.changeset/gentle-seals-sing.md deleted file mode 100644 index 780f260c50..0000000000 --- a/.changeset/gentle-seals-sing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/create-catalyst": patch ---- - -Add es-419 to list of allowed locale for selecting in CLI diff --git a/.changeset/gold-ducks-tie.md b/.changeset/gold-ducks-tie.md deleted file mode 100644 index cb5d2df7c1..0000000000 --- a/.changeset/gold-ducks-tie.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": minor ---- - -Add currency selector to header diff --git a/.changeset/good-actors-return.md b/.changeset/good-actors-return.md deleted file mode 100644 index 68c9dcf08d..0000000000 --- a/.changeset/good-actors-return.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Add persistent cart support diff --git a/.changeset/happy-moles-thank.md b/.changeset/happy-moles-thank.md deleted file mode 100644 index 00603d2418..0000000000 --- a/.changeset/happy-moles-thank.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": minor ---- - -Add Wishlist account pages and public wishlist page diff --git a/.changeset/healthy-ears-dream.md b/.changeset/healthy-ears-dream.md deleted file mode 100644 index 5e0c4e6d9b..0000000000 --- a/.changeset/healthy-ears-dream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": minor ---- - -Properly handle `BigCommerceGQLError` in actions, by returning the error messages from the request. diff --git a/.changeset/hip-countries-tap.md b/.changeset/hip-countries-tap.md deleted file mode 100644 index 423f20cba6..0000000000 --- a/.changeset/hip-countries-tap.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Add discounts summary item to Cart. diff --git a/.changeset/itchy-ways-do.md b/.changeset/itchy-ways-do.md deleted file mode 100644 index ecd55fa50f..0000000000 --- a/.changeset/itchy-ways-do.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-client": minor ---- - -Add an `errorPolicy` option for GQL requests. Accepts `none`, `ignore`, `all`. Defaults to `none` which throws an error if there are GQL errors, `ignore` returns the data without error object, and `all` returns both data and errors. diff --git a/.changeset/khaki-rice-design.md b/.changeset/khaki-rice-design.md deleted file mode 100644 index b9f93526a6..0000000000 --- a/.changeset/khaki-rice-design.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Deduplicate default image in the image gallery in PDP. diff --git a/.changeset/late-ends-nail.md b/.changeset/late-ends-nail.md deleted file mode 100644 index 6c4fe02c54..0000000000 --- a/.changeset/late-ends-nail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": minor ---- - -Add the ability to redirect from the login page. Developers can now append a relative path to the `?redirectTo=` query param on the `/login` page. When a shopper successfully logs in, it'll redirect them to the given relative path. Defaults to `/account/orders` to prevent a breaking change. diff --git a/.changeset/legal-ends-judge.md b/.changeset/legal-ends-judge.md deleted file mode 100644 index 51882753ae..0000000000 --- a/.changeset/legal-ends-judge.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -"@bigcommerce/catalyst-core": minor ---- - -In order to maintain parity with Stencil's 404 page, we wanted to allow the user to search from the 404 page. Since the search included with the header component is fully featured, we included a CTA to open the same search that you get when clicking the search icon in the header. - -**Migration** -Most changes are additive, so they should hopefully be easy to resolve if flagged for merge conflicts. Change #3 below replaces the Search state with the new search context, be sure to pay attention to the new - -1. This change adds a new directory under `core/` called `context/` containing a `search-context.tsx` file. Since this is a new file, there shouldn't be any merge conflicts -2. `SearchProvider` is imported into `core/app/providers` and replaces the React fragment (`<>`) that currently wraps `` and `{children}` -3. In `core/vibes/soul/primitives/navigation`, replace `useState` with `useSearch` imported from the new context file, and update the dependency arrays for the `useEffect`'s in the `Navigation component` -4. Add search `Button` that calls `setIsSearchOpen(true)` to the `NotFound` component in `core/vibes/sections/not-found/index.tsx` diff --git a/.changeset/little-cars-repeat.md b/.changeset/little-cars-repeat.md deleted file mode 100644 index e0b0dcb32a..0000000000 --- a/.changeset/little-cars-repeat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Re-apply auth grouping approach with middleware exemption to preserve functionality of /login/token endpoint for Customer Login API diff --git a/.changeset/little-hornets-stand.md b/.changeset/little-hornets-stand.md deleted file mode 100644 index ed18a47a66..0000000000 --- a/.changeset/little-hornets-stand.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -"@bigcommerce/catalyst-client": patch -"@bigcommerce/catalyst-core": patch ---- - -Update GQL client and auth middleware to handle invalid tokens and invalidate session. - -### Summary - -This will ensure that if a user is logged out elsewhere, they will be redirected to the /login page when they try to access a protected route. - -Previously, the pages would 404 which is misleading. - -### Migration - -1. Copy all changes from the `/core/client` directory and the `/packages/client` directory -2. Copy logic from withAuth middleware to handle the `?invalidate-session` query param -3. Copy translation values -4. Copy all changes from the `/core/app/[locale]/(default)/account/` directory server actions -5. Copy all changes from the `/core/app/[locale]/(default)/checkout/route.ts` file diff --git a/.changeset/loud-breads-jog.md b/.changeset/loud-breads-jog.md deleted file mode 100644 index 541006acb6..0000000000 --- a/.changeset/loud-breads-jog.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": minor ---- - -Fetches the stores URLs on build which can remove the need of setting NEXT_PUBLIC_BIGCOMMERCE_CDN_HOSTNAME. The environment variable is still provided in case customization is needed. diff --git a/.changeset/mean-geese-retire.md b/.changeset/mean-geese-retire.md deleted file mode 100644 index b942d57fb4..0000000000 --- a/.changeset/mean-geese-retire.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@bigcommerce/eslint-config-catalyst": patch -"@bigcommerce/catalyst-client": patch -"@bigcommerce/catalyst-core": minor ---- - -Manual changes on a dependency bumps. diff --git a/.changeset/nasty-parks-nail.md b/.changeset/nasty-parks-nail.md deleted file mode 100644 index b03545af39..0000000000 --- a/.changeset/nasty-parks-nail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Moves some auth related route handlers under the (auth) route group. This is to cleanup some of the routing. diff --git a/.changeset/new-books-drop.md b/.changeset/new-books-drop.md deleted file mode 100644 index 7d0a14be34..0000000000 --- a/.changeset/new-books-drop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -fix: localized home page routes are rewritten to the "catch all" page diff --git a/.changeset/ninety-files-begin.md b/.changeset/ninety-files-begin.md deleted file mode 100644 index fa5c5f3512..0000000000 --- a/.changeset/ninety-files-begin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Pass in currency code to quick search results. diff --git a/.changeset/pink-islands-explain.md b/.changeset/pink-islands-explain.md deleted file mode 100644 index 84c9ee9c9f..0000000000 --- a/.changeset/pink-islands-explain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@bigcommerce/catalyst-core': patch ---- - -fix: switching locales redirects user to the home page diff --git a/.changeset/plenty-buttons-know.md b/.changeset/plenty-buttons-know.md deleted file mode 100644 index 5aa6b02b24..0000000000 --- a/.changeset/plenty-buttons-know.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Check for `error.type` instead of `error.name` auth error in Login, since `error.name` gets minified in production and the check never returns `true`. Additionally, add a check for the `cause.err` to be of type `BigcommerceGQLError`. - -Migration: - -- Change `error.name === 'CallbackRouteError'` to `error.type === 'CallbackRouteError'` check in the error handling of the login action and include `error.cause.err instanceof BigCommerceGQLError`. diff --git a/.changeset/plenty-jokes-learn.md b/.changeset/plenty-jokes-learn.md deleted file mode 100644 index d7db753358..0000000000 --- a/.changeset/plenty-jokes-learn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Remove explicit locale override in Link component that was appending default locale to links even with the 'as-needed' mode. diff --git a/.changeset/popular-ducks-bake.md b/.changeset/popular-ducks-bake.md deleted file mode 100644 index d6244797a6..0000000000 --- a/.changeset/popular-ducks-bake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Use `setRequestLocale` only where needed diff --git a/.changeset/pretty-coats-accept.md b/.changeset/pretty-coats-accept.md deleted file mode 100644 index 21e8043e68..0000000000 --- a/.changeset/pretty-coats-accept.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/create-catalyst": minor ---- - -Expands the supported Node.js version of the `create-catalyst` CLI to `^20` and `^22`. The version of Node.js required to run the CLI is not necessarily tied to the version of Node.js required to run Catalyst; the CLI requires at least version 18 to run because it depends on global Fetch API support being enabled by default. More context in [#2296](https://github.com/bigcommerce/catalyst/pull/2296). diff --git a/.changeset/puny-radios-show.md b/.changeset/puny-radios-show.md deleted file mode 100644 index d9dd4478e7..0000000000 --- a/.changeset/puny-radios-show.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Remove featured products panel from 404 page, allowing the page to be static in preparation for adding a search box diff --git a/.changeset/quiet-impalas-hear.md b/.changeset/quiet-impalas-hear.md deleted file mode 100644 index a5d5d14a45..0000000000 --- a/.changeset/quiet-impalas-hear.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Dedupe requests in "webpages" by properly caching/memoizing the fetch function per page render. diff --git a/.changeset/red-otters-wave.md b/.changeset/red-otters-wave.md deleted file mode 100644 index a295344da0..0000000000 --- a/.changeset/red-otters-wave.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@bigcommerce/catalyst-core": minor ---- - -Implement the new analytics provider, utilizing the GoogleAnalytics provider as the first analytics solution. - - -Most changes are additive so merge conflicts should be easy to resolve. In order to use the new provider from the previous provider, if it's already not setup in the BigCommerce control panel for checkout analytics, you'll need to add the GA4 property ID. This will automatically be used by the new GoogleAnalytics provider. - diff --git a/.changeset/rotten-pans-sneeze.md b/.changeset/rotten-pans-sneeze.md deleted file mode 100644 index f6e7642c94..0000000000 --- a/.changeset/rotten-pans-sneeze.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Fix an issue with orders with deleted products throwing an error and stopping page render by settings the errorPolicy for requests to ignore errors and update Soul components to render the products without using links for these cases. diff --git a/.changeset/salty-laws-hunt.md b/.changeset/salty-laws-hunt.md deleted file mode 100644 index bb76a00c22..0000000000 --- a/.changeset/salty-laws-hunt.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -"@bigcommerce/catalyst-core": minor ---- - -## New - -This refactor optimizes category PLP for caching and the eventual use of dynamicIO. With these changes we leverage data caching to hit mostly cache data for guest shoppers in different locales and with different currencies. - -## Key modifications include: - -- We don't stream in Category page data, instead it's a blocking call that will redirect to `notFound` when category is not found. Same for metadata. -- Our query functions now take in all params required for fetching, instead of accessing dynamic variables internally. This is important to serialize arguments if we want to eventually `use cache`. -- Use `Streamable.from` to generate our streaming props that are passed to our UI components. -- Remove use of nuqs' `createSearchParamsCache` in favor of nuqs' `createLoader`. - -## Migration instructions: - -- Update `/(facted)/category/[slug]/page.tsx` - - For this page we are now doing a blocking request for category page data. Instead of having functions that each would read from props, we share streamable functions that can be passed to our UI components. We still stream in filter and product data. -- Update `/(facted)/category/[slug]/page-data.tsx` - - Request now accept `customerAccessToken` as a prop instead of calling internally. -- Update`/(facted)/category/[slug]/fetch-compare-products.ts` - - Request now accept `customerAccessToken` as a prop instead of calling internally. -- Update `/(faceted)/fetch-faceted-search.ts` - - Request now accept `customerAccessToken` and `currencyCode` as a prop instead of calling internally. diff --git a/.changeset/shiny-insects-swim.md b/.changeset/shiny-insects-swim.md deleted file mode 100644 index e1898ded00..0000000000 --- a/.changeset/shiny-insects-swim.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Split coupon discounts and regular discounts from summary items, use total `cart.discountedAmount` for discounts. diff --git a/.changeset/silver-numbers-divide.md b/.changeset/silver-numbers-divide.md deleted file mode 100644 index 7d52830cf0..0000000000 --- a/.changeset/silver-numbers-divide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Set currency on cart at creation time diff --git a/.changeset/six-schools-roll.md b/.changeset/six-schools-roll.md deleted file mode 100644 index 257209d04f..0000000000 --- a/.changeset/six-schools-roll.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Upgrade `next-intl` to v4 and add strong types for translated messages via TypeScript type augmentation. diff --git a/.changeset/slick-clocks-lick.md b/.changeset/slick-clocks-lick.md deleted file mode 100644 index a90a158f06..0000000000 --- a/.changeset/slick-clocks-lick.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Remove unused dependencies. diff --git a/.changeset/slick-turtles-cry.md b/.changeset/slick-turtles-cry.md deleted file mode 100644 index 5388cab4ad..0000000000 --- a/.changeset/slick-turtles-cry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": minor ---- - -Adds the ability to redirect after logout. diff --git a/.changeset/slimy-trains-rhyme.md b/.changeset/slimy-trains-rhyme.md deleted file mode 100644 index 57cad70084..0000000000 --- a/.changeset/slimy-trains-rhyme.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Refactors the add to cart logic to handle some shared functionality like revalidating the tags and setting the cart state. diff --git a/.changeset/slow-dodos-ring.md b/.changeset/slow-dodos-ring.md deleted file mode 100644 index ed7a765631..0000000000 --- a/.changeset/slow-dodos-ring.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Add the `store_hash` `` element to better support merchants. This enabled BigCommerce to identify the store more easily. diff --git a/.changeset/slow-dolls-allow.md b/.changeset/slow-dolls-allow.md deleted file mode 100644 index 9a6a0b2c88..0000000000 --- a/.changeset/slow-dolls-allow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Add coupon code form to Cart page. diff --git a/.changeset/smooth-flowers-joke.md b/.changeset/smooth-flowers-joke.md deleted file mode 100644 index d2952c677e..0000000000 --- a/.changeset/smooth-flowers-joke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Applied streamable pattern to Cart. diff --git a/.changeset/some-aliens-spend.md b/.changeset/some-aliens-spend.md deleted file mode 100644 index 8fd0842657..0000000000 --- a/.changeset/some-aliens-spend.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Use `setRequestLocale` in all pages and layouts and pass `locale` parameter to `getTranslations` in all `generateMetadata` to maximize static rendering. This is part of the ongoing work in preparation of enabling PPR and `dynamicIO` for all routes. diff --git a/.changeset/sweet-lions-kneel.md b/.changeset/sweet-lions-kneel.md deleted file mode 100644 index 6682fa724e..0000000000 --- a/.changeset/sweet-lions-kneel.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@bigcommerce/create-catalyst": minor -"@bigcommerce/catalyst": minor ---- - -Support node 22. diff --git a/.changeset/swift-spiders-divide.md b/.changeset/swift-spiders-divide.md deleted file mode 100644 index 329f3c3bc2..0000000000 --- a/.changeset/swift-spiders-divide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Clean up 'en' dictionary. diff --git a/.changeset/swift-tips-wave.md b/.changeset/swift-tips-wave.md deleted file mode 100644 index 3129696f36..0000000000 --- a/.changeset/swift-tips-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-client": patch ---- - -Add GraphQL operation name and type to GraphQL URL as query parameters to improve server logging of GraphQL operations diff --git a/.changeset/ten-pots-vanish.md b/.changeset/ten-pots-vanish.md deleted file mode 100644 index fcb7282834..0000000000 --- a/.changeset/ten-pots-vanish.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": minor ---- - -Sets a default session when any user first visits the page. diff --git a/.changeset/thin-mugs-lay.md b/.changeset/thin-mugs-lay.md deleted file mode 100644 index f422d22a54..0000000000 --- a/.changeset/thin-mugs-lay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Add shipping selection to checkout. diff --git a/.changeset/tidy-lobsters-compete.md b/.changeset/tidy-lobsters-compete.md deleted file mode 100644 index 6ede567345..0000000000 --- a/.changeset/tidy-lobsters-compete.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Remove unecessary `fetchOptions` in object that has nothing to do with a client request. diff --git a/.changeset/tidy-stingrays-search.md b/.changeset/tidy-stingrays-search.md deleted file mode 100644 index d2fac1b509..0000000000 --- a/.changeset/tidy-stingrays-search.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -"@bigcommerce/catalyst-core": major ---- - -Introduce Soul VIBE UI library to the repository. - -- Added a collection of reusable primitives with modern styles -- Prebuilt sections and page templates that are easy to use -- Fast performance and modern patterns leveraging the latest features of Next.js -- Easy customization to best represent your brand -- Utilize @conform-to/react for progressively enhanced HTML forms - -Join the discussion [here](https://github.com/bigcommerce/catalyst/discussions/1861) for more details of this major milestone for Catalyst! diff --git a/.changeset/tiny-wombats-turn.md b/.changeset/tiny-wombats-turn.md deleted file mode 100644 index 3a3f6af82f..0000000000 --- a/.changeset/tiny-wombats-turn.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Add wishlist support to product display pages - -## Migration -- Ensure WishlistButton component is passed to additionalActions prop on ProductDetail -- Ensure WishlistButtonForm is used on product page diff --git a/.changeset/translations-patch-1685d885.md b/.changeset/translations-patch-1685d885.md deleted file mode 100644 index ad17b2636a..0000000000 --- a/.changeset/translations-patch-1685d885.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Update translations. diff --git a/.changeset/translations-patch-23b9a635.md b/.changeset/translations-patch-23b9a635.md deleted file mode 100644 index ad17b2636a..0000000000 --- a/.changeset/translations-patch-23b9a635.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Update translations. diff --git a/.changeset/translations-patch-2523d61f.md b/.changeset/translations-patch-2523d61f.md deleted file mode 100644 index ad17b2636a..0000000000 --- a/.changeset/translations-patch-2523d61f.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Update translations. diff --git a/.changeset/translations-patch-3dd52131.md b/.changeset/translations-patch-3dd52131.md deleted file mode 100644 index ad17b2636a..0000000000 --- a/.changeset/translations-patch-3dd52131.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Update translations. diff --git a/.changeset/translations-patch-44f249be.md b/.changeset/translations-patch-44f249be.md deleted file mode 100644 index ad17b2636a..0000000000 --- a/.changeset/translations-patch-44f249be.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Update translations. diff --git a/.changeset/translations-patch-4ddf0bc6.md b/.changeset/translations-patch-4ddf0bc6.md deleted file mode 100644 index ad17b2636a..0000000000 --- a/.changeset/translations-patch-4ddf0bc6.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Update translations. diff --git a/.changeset/translations-patch-63d5963b.md b/.changeset/translations-patch-63d5963b.md deleted file mode 100644 index ad17b2636a..0000000000 --- a/.changeset/translations-patch-63d5963b.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Update translations. diff --git a/.changeset/translations-patch-6ff30da1.md b/.changeset/translations-patch-6ff30da1.md deleted file mode 100644 index ad17b2636a..0000000000 --- a/.changeset/translations-patch-6ff30da1.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Update translations. diff --git a/.changeset/translations-patch-8fd0f039.md b/.changeset/translations-patch-8fd0f039.md deleted file mode 100644 index ad17b2636a..0000000000 --- a/.changeset/translations-patch-8fd0f039.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Update translations. diff --git a/.changeset/translations-patch-98b49268.md b/.changeset/translations-patch-98b49268.md deleted file mode 100644 index ad17b2636a..0000000000 --- a/.changeset/translations-patch-98b49268.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Update translations. diff --git a/.changeset/translations-patch-c6b7a7c7.md b/.changeset/translations-patch-c6b7a7c7.md deleted file mode 100644 index ad17b2636a..0000000000 --- a/.changeset/translations-patch-c6b7a7c7.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Update translations. diff --git a/.changeset/translations-patch-df0fb0f0.md b/.changeset/translations-patch-df0fb0f0.md deleted file mode 100644 index ad17b2636a..0000000000 --- a/.changeset/translations-patch-df0fb0f0.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Update translations. diff --git a/.changeset/translations-patch-e6b1d72a.md b/.changeset/translations-patch-e6b1d72a.md deleted file mode 100644 index ad17b2636a..0000000000 --- a/.changeset/translations-patch-e6b1d72a.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Update translations. diff --git a/.changeset/translations-patch-fcb1634a.md b/.changeset/translations-patch-fcb1634a.md deleted file mode 100644 index ad17b2636a..0000000000 --- a/.changeset/translations-patch-fcb1634a.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Update translations. diff --git a/.changeset/warm-drinks-think.md b/.changeset/warm-drinks-think.md deleted file mode 100644 index 5377f7c407..0000000000 --- a/.changeset/warm-drinks-think.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Dedupe requests in various pages by properly caching/memoizing the function per page render. diff --git a/.changeset/weak-nails-rule.md b/.changeset/weak-nails-rule.md deleted file mode 100644 index 288b3e77c0..0000000000 --- a/.changeset/weak-nails-rule.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Disable prefetch for the `/logout` link. diff --git a/.changeset/whole-papers-scream.md b/.changeset/whole-papers-scream.md deleted file mode 100644 index e8d6a150db..0000000000 --- a/.changeset/whole-papers-scream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Add date field to product details form. diff --git a/.changeset/whole-signs-shine.md b/.changeset/whole-signs-shine.md deleted file mode 100644 index fae7653ac5..0000000000 --- a/.changeset/whole-signs-shine.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -"@bigcommerce/catalyst-core": minor ---- - -## New - -This refactor optimizes brand PLP for caching and the eventual use of dynamicIO. With these changes we leverage data caching to hit mostly cache data for guest shoppers in different locales and with different currencies. - -## Key modifications include: - -- We don't stream in Brand page data, instead it's a blocking call that will redirect to `notFound` when brand is not found. Same for metadata. -- Our query functions now take in all params required for fetching, instead of accessing dynamic variables internally. This is important to serialize arguments if we want to eventually `use cache`. -- Use `Streamable.from` to generate our streaming props that are passed to our UI components. -- Remove use of nuqs' `createSearchParamsCache` in favor of nuqs' `createLoader`. - -## Migration instructions: - -- Update `/(facted)/brand/[slug]/page.tsx` - - For this page we are now doing a blocking request for brand page data. Instead of having functions that each would read from props, we share streamable functions that can be passed to our UI components. We still stream in filter and product data. -- Update `/(facted)/brand/[slug]/page-data.tsx` - - Request now accept `customerAccessToken` as a prop instead of calling internally. diff --git a/.changeset/wild-days-bake.md b/.changeset/wild-days-bake.md deleted file mode 100644 index a03280de75..0000000000 --- a/.changeset/wild-days-bake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": minor ---- - -Adds support for nested web page children / trees. Restructure web page routing to support a layout file. diff --git a/.changeset/witty-suns-explode.md b/.changeset/witty-suns-explode.md deleted file mode 100644 index 0b002608f8..0000000000 --- a/.changeset/witty-suns-explode.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@bigcommerce/catalyst-core": patch ---- - -Adds `Streamable.from` and uses it wherever we were unintentionally executing an async function in a React Server Component. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 654e154809..4fa108d4f5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -15,7 +15,6 @@ updates: update-types: ['version-update:semver-major'] - dependency-name: 'eslint' update-types: ['version-update:semver-major'] - - dependency-name: 'react-day-picker' - update-types: ['version-update:semver-major'] - # Tailwind upgrade deserves it's own PR and changelog + # Disabling tailwind due to browser compatibility constraints. - dependency-name: 'tailwindcss' + update-types: ['version-update:semver-major'] diff --git a/.github/workflows/.lighthouserc-desktop.json b/.github/workflows/.lighthouserc-desktop.json deleted file mode 100644 index fc47cda37f..0000000000 --- a/.github/workflows/.lighthouserc-desktop.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "ci": { - "collect": { - "settings": { - "preset": "desktop" - } - } - } -} diff --git a/.github/workflows/changesets-release.yml b/.github/workflows/changesets-release.yml index f56e214f16..251741f70a 100644 --- a/.github/workflows/changesets-release.yml +++ b/.github/workflows/changesets-release.yml @@ -4,6 +4,7 @@ on: push: branches: - canary + - integrations/makeswift concurrency: ${{ github.workflow }}-${{ github.ref }} @@ -37,6 +38,8 @@ jobs: uses: changesets/action@v1 with: publish: pnpm exec changeset publish + title: "Version Packages (`${{ github.ref_name }}`)" + commit: "Version Packages (`${{ github.ref_name }}`)" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/lighthouseCommentMaker.js b/.github/workflows/lighthouseCommentMaker.js deleted file mode 100644 index d6fafd1e82..0000000000 --- a/.github/workflows/lighthouseCommentMaker.js +++ /dev/null @@ -1,61 +0,0 @@ -// @ts-check - -/** - * @typedef {Object} Summary - * @prop {number} performance - * @prop {number} accessibility - * @prop {number} best-practices - * @prop {number} seo - * @prop {number} pwa - */ - -/** - * @typedef {Object} Manifest - * @prop {string} url - * @prop {boolean} isRepresentativeRun - * @prop {string} htmlPath - * @prop {string} jsonPath - * @prop {Summary} summary - */ - -/** - * @typedef {Object} LighthouseOutputs - * @prop {Record} links - * @prop {Manifest[]} manifest - * @prop {string} preset - */ - -const formatScore = (/** @type { number } */ score) => Math.round(score * 100); -const emojiScore = (/** @type { number } */ score) => - score >= 0.9 ? '๐ŸŸข' : score >= 0.5 ? '๐ŸŸ ' : '๐Ÿ”ด'; - -const scoreRow = ( - /** @type { string } */ label, - /** @type { number } */ score -) => `| ${emojiScore(score)} ${label} | ${formatScore(score)} |`; - -/** - * @param {LighthouseOutputs} lighthouseOutputs - */ -function makeComment(lighthouseOutputs) { - const { summary } = lighthouseOutputs.manifest[2]; - const [[testedUrl, reportUrl]] = Object.entries(lighthouseOutputs.links); - const preset = lighthouseOutputs.preset; - - const comment = `We ran Lighthouse against the changes on a ${preset} and produced this [report](${reportUrl}). Here's the summary: - -| Category | Score | -| -------- | ----- | -${scoreRow('Performance', summary.performance)} -${scoreRow('Accessibility', summary.accessibility)} -${scoreRow('Best practices', summary['best-practices'])} -${scoreRow('SEO', summary.seo)} - -`; - - return comment; -} - -module.exports = ({ lighthouseOutputs }) => { - return makeComment(lighthouseOutputs); -}; diff --git a/.github/workflows/pull-request-data.js b/.github/workflows/pull-request-data.js deleted file mode 100644 index 14d941f02c..0000000000 --- a/.github/workflows/pull-request-data.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = async ({ github, context, core }) => { - const result = await github.rest.repos.listPullRequestsAssociatedWithCommit({ - owner: context.repo.owner, - repo: context.repo.repo, - commit_sha: context.sha, - }); - - core.setOutput('pr', result?.data[0]?.number || ''); - core.setOutput('title', result?.data[0]?.title || ''); - core.setOutput('url', result?.data[0]?.html_url || ''); - core.setOutput('draft', `${result?.data[0]?.draft}` || 'true'); -} diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml index 84d3e0d72f..afcbc46c2f 100644 --- a/.github/workflows/regression-tests.yml +++ b/.github/workflows/regression-tests.yml @@ -5,100 +5,40 @@ on: states: ['success'] env: - PLAYWRIGHT_TEST_BASE_URL: ${{ github.event.deployment_status.target_url }} VERCEL_PROTECTION_BYPASS: ${{ secrets.VERCEL_AUTOMATION_BYPASS_SECRET }} - BIGCOMMERCE_ACCESS_TOKEN: ${{ secrets.BIGCOMMERCE_ACCESS_TOKEN }} - BIGCOMMERCE_STORE_HASH: ${{ secrets.BIGCOMMERCE_STORE_HASH }} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event.deployment_status.target_url }} + cancel-in-progress: true jobs: - generate-lighthouse-audit: - name: Lighthouse Audit - timeout-minutes: 30 - runs-on: ubuntu-latest + unlighthouse-audit: if: ${{ contains(fromJson('["Production โ€“ catalyst-canary", "Preview โ€“ catalyst-canary"]'), github.event.deployment_status.environment) }} + name: Unlighthouse Audit - ${{ matrix.device }} + runs-on: ubuntu-latest + strategy: + matrix: + device: [desktop, mobile] + concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event.deployment_status.target_url }}-${{ matrix.device }} + cancel-in-progress: true steps: - name: Checkout code uses: actions/checkout@v4 - - - name: Pull Request Details - id: pr_details - uses: actions/github-script@v7 with: - script: | - const prData = require('./.github/workflows/pull-request-data.js'); - await prData({ github, context, core }); + fetch-depth: 0 - - name: Lighthouse house audit on desktop - id: lighthouse_audit_desktop - uses: treosh/lighthouse-ci-action@v11 - with: - urls: | - ${{ github.event.deployment_status.target_url }} - configPath: '.github/workflows/.lighthouserc-desktop.json' - temporaryPublicStorage: true - runs: 3 - - - name: Lighthouse audit on mobile - id: lighthouse_audit_mobile - uses: treosh/lighthouse-ci-action@v11 - with: - urls: | - ${{ github.event.deployment_status.target_url }} - temporaryPublicStorage: true - runs: 3 - - - uses: pnpm/action-setup@v3 - - name: Format lighthouse score on desktop - id: format_lighthouse_score_desktop - uses: actions/github-script@v7 - with: - script: | - const lighthouseCommentMaker = require('./.github/workflows/lighthouseCommentMaker.js'); - - const lighthouseOutputs = { - manifest: ${{ steps.lighthouse_audit_desktop.outputs.manifest }}, - links: ${{ steps.lighthouse_audit_desktop.outputs.links }}, - preset: "desktop" - }; - - const comment = lighthouseCommentMaker({ lighthouseOutputs }); - core.setOutput("comment", comment); - - - name: Format lighthouse score on mobile - id: format_lighthouse_score_mobile - uses: actions/github-script@v7 - with: - script: | - const lighthouseCommentMaker = require('./.github/workflows/lighthouseCommentMaker.js'); - - const lighthouseOutputs = { - manifest: ${{ steps.lighthouse_audit_mobile.outputs.manifest }}, - links: ${{ steps.lighthouse_audit_mobile.outputs.links }}, - preset: "mobile" - }; + - name: Install Dependencies + run: npm install @unlighthouse/cli puppeteer -g - const comment = lighthouseCommentMaker({ lighthouseOutputs }); - core.setOutput("comment", comment); + - name: Unlighthouse audit on ${{ matrix.device }} + run: unlighthouse-ci --site ${{ github.event.deployment_status.target_url }} --${{ matrix.device }} --disable-robots-txt --extra-headers x-vercel-protection-bypass=$VERCEL_PROTECTION_BYPASS,x-vercel-set-bypass-cookie=true - - name: Add comment to PR - id: comment_to_pr - uses: marocchino/sticky-pull-request-comment@v2.9.0 - if: ${{ steps.pr_details.outputs.pr }} + - name: Upload ${{ matrix.device }} audit + if: failure() || success() + uses: actions/upload-artifact@v4 with: - recreate: true - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - number: ${{ steps.pr_details.outputs.pr }} - header: lighthouse - message: | - # โšก๏ธ๐Ÿ  Lighthouse report - - *Lighthouse ran against ${{ github.event.deployment_status.target_url }}* - - ## ๐Ÿ–ฅ๏ธ Desktop - - ${{ steps.format_lighthouse_score_desktop.outputs.comment }} - - ## ๐Ÿ“ฑ Mobile - - ${{ steps.format_lighthouse_score_mobile.outputs.comment }} + name: unlighthouse-${{ matrix.device }}-report + path: './.unlighthouse/' + include-hidden-files: 'true' diff --git a/.gitignore b/.gitignore index 99e43d16d4..96fa153af9 100644 --- a/.gitignore +++ b/.gitignore @@ -8,10 +8,14 @@ dist .catalyst .env .env*.local +.env*.test test-results/ playwright-report/ playwright/.cache/ +.tests bigcommerce.graphql bigcommerce-graphql.d.ts .DS_Store coverage/ +.history +.unlighthouse diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 86b3056677..7fcebb1c7f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,42 +1,78 @@ # Contributing to Catalyst + Thanks for showing interest in contributing! The following is a set of guidelines for contributing to Catalyst. These are just guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request. -#### Table of Contents +## Repository Structure + +Catalyst is a monorepo that contains the code for the Catalyst Next.js application inside of `core/`, and supporting packages such as the GraphQL API client and the `create-catalyst` CLI in `packages/`. + +The default branch for this repository is called `canary`. This is the primary development branch where active development takes place, including the introduction of new features, bug fixes, and other changes before they are released in stable versions. + +To contribute to the `canary` branch, you can create a new branch off of `canary` and submit a PR against that branch. + +## Makeswift Integration + +In addition to `canary`, we also maintain the `integrations/makeswift` branch, which contains additional code required to integrate with [Makeswift](https://www.makeswift.com). + +To contribute to the `integrations/makeswift` branch, you can create a new branch off of `integrations/makeswift` and submit a PR against that branch. + +### Pulling updates from `canary` into `integrations/makeswift` + +We aim to keep `integrations/makeswift` in sync with `canary`. To do this, we frequently pull the latest code from `canary` into `integrations/makeswift`. + +To pull the latest code from `canary` into `integrations/makeswift`, you can follow these steps: + +1. Fetch the latest code from `canary`: + + ```bash + git checkout canary + git pull + ``` + +2. Fetch the latest code from `integrations/makeswift`: + + ```bash + git checkout integrations/makeswift + git pull + ``` + +3. Checkout a new branch from `integrations/makeswift`: -How Can I Contribute? - * [Pull Requests](#pull-requests) - * [Issues / Bugs](#issues--bugs) - * [Other Ways to Contribute](#other-ways-to-contribute) + ```bash + git checkout -b {new-branch-name} + ``` -Styleguides - * [Git Commit Messages](#git-commit-messages) +4. Merge `canary` into the new branch: -## Pull Requests + ```bash + git merge canary + ``` -First ensure that your feature isn't already being developed or considered (see open PRs and issues). -If it is, please consider contributing to those initiatives. +5. After resolving any merge conflicts, open a new PR in GitHub to merge your new branch into `integrations/makeswift`. This PR should be code reviewed and approved before the next steps. -## Issues / Bugs - -* Please include a clear, specific title and replicable description. +6. Rebase the changes onto the local `integrations/makeswift` branch in order to make the branches 1-1 and keep a linear commit history. -* Please include your environment, OS, and any exceptions/backtraces that occur. The more -information that is given, the more likely we can debug and fix the issue. + ```bash + git checkout integrations/makeswift + git rebase {new-branch-name} + ``` -**If you find a security bug, please do not post as an issue. Send directly to security@bigcommerce.com -instead.** +7. Push the changes up to GitHub, which will automatically close the open PR from step 6. + ```bash + git push + ``` ## Other Ways to Contribute -* Consider reporting bugs, contributing to test coverage, or helping spread the word about Catalyst. +- Consider reporting bugs, contributing to test coverage, or helping spread the word about Catalyst. ## Git Commit Messages -* Use the present tense ("Add feature" not "Added feature") -* Use the imperative mood ("Move cursor to..." not "Moves cursor to...") -* Limit the first line to 72 characters or less -* Reference pull requests and external links liberally +- Use the present tense ("Add feature" not "Added feature") +- Use the imperative mood ("Move cursor to..." not "Moves cursor to...") +- Limit the first line to 72 characters or less +- Reference pull requests and external links liberally -Thank you again for your interest in contributing to Catalyst! \ No newline at end of file +Thank you again for your interest in contributing to Catalyst! diff --git a/README.md b/README.md index cb9e550764..e1507051ed 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ [![MIT License](https://img.shields.io/github/license/bigcommerce/catalyst)](LICENSE.md) [![Lighthouse Report](https://github.com/bigcommerce/catalyst/actions/workflows/lighthouse.yml/badge.svg)](https://github.com/bigcommerce/catalyst/actions/workflows/lighthouse.yml) [![Lint, Typecheck, gql.tada](https://github.com/bigcommerce/catalyst/actions/workflows/basic.yml/badge.svg)](https://github.com/bigcommerce/catalyst/actions/workflows/basic.yml) +[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/bigcommerce/catalyst) diff --git a/core/.env.test.example b/core/.env.test.example new file mode 100644 index 0000000000..c5b0287fae --- /dev/null +++ b/core/.env.test.example @@ -0,0 +1,40 @@ +# This file should hold any environment variables specific to test execution. +# Any overlapping variables in here will overwrite variables from the other .env files. +# For example, if BIGCOMMERCE_CHANNEL_ID=1 in .env.local, but BIGCOMMERCE_CHANNEL_ID=5 in this file, the test environment will use BIGCOMMERCE_CHANNEL_ID=5 + +# Access token client ID and client secret are required for JWT login tests. If not provided, tests that require these will be skipped. +BIGCOMMERCE_CLIENT_ID= +BIGCOMMERCE_CLIENT_SECRET= + +# Base URL for all tests. This should always be set. It is recommended to run tests against a production build. +# If not set or undefined, it will default to 'http://localhost:3000' +PLAYWRIGHT_TEST_BASE_URL=http://localhost:3000 + +# If true, no tests will execute that require modifying/deleting any data on your store. +# This is particularly useful if you want to run the tests against a live store and avoid test data being visible to any customers. +# If this is false, you should only run tests against a staging store where test data is okay. +TESTS_READ_ONLY=false + +# Locale to use during test execution +TESTS_LOCALE=en + +# The fallback locale to use in the occurence of missing translation keys in the selected TESTS_LOCALE +# This should match the default locale you have set for your storefront +TESTS_FALLBACK_LOCALE=en + +# An existing customer account to use for all tests - use this if you don't want tests to create a new customer account. +# If TESTS_READ_ONLY is `true`, then any test requiring a customer will be skipped if these values are not set. +TEST_CUSTOMER_ID=1 +TEST_CUSTOMER_EMAIL=example@test.com +TEST_CUSTOMER_PASSWORD=Password1 + +# Used to create customer accounts for testing purposes if no existing customer account is provided +BIGCOMMERCE_ACCESS_TOKEN= + +# The default product ID to use for tests. This needs to be a product without variants (simple) +# This is optional, but recommended. If left empty, tests that require a product will be skipped if TESTS_READ_ONLY is true +DEFAULT_PRODUCT_ID= + +# The default product ID to use for tests that require a complex product. This product must have at least 1 variant. +# This is optional, but recommended. If left empty, tests that require a complex product will be skipped if TESTS_READ_ONLY is true +DEFAULT_COMPLEX_PRODUCT_ID= diff --git a/core/.eslintrc.cjs b/core/.eslintrc.cjs index 83a5b2d58a..70238b3e39 100644 --- a/core/.eslintrc.cjs +++ b/core/.eslintrc.cjs @@ -47,6 +47,11 @@ const config = { importNames: ['redirect', 'permanentRedirect', 'useRouter', 'usePathname'], message: 'Please import from `~/i18n/routing` instead.', }, + { + name: '@playwright/test', + importNames: ['expect', 'test'], + message: 'Please import from `~/tests/fixtures` instead.', + }, ], }, ], @@ -57,10 +62,31 @@ const config = { }, ], }, + overrides: [ + { + files: ['**/*.spec.ts', '**/*.test.ts'], + rules: { + '@typescript-eslint/no-restricted-imports': [ + 'error', + { + paths: [ + { + name: 'next-intl/server', + importNames: ['getTranslations', 'getFormatter'], + message: + 'Please import `getTranslations` from `~/tests/lib/i18n` and `getFormatter` from `~/tests/lib/formatter` instead.', + }, + ], + }, + ], + }, + }, + ], ignorePatterns: [ 'client/generated/**/*.ts', 'playwright-report/**', 'test-results/**', + '.tests/**', '**/google_analytics4.js', ], }; diff --git a/core/.gitignore b/core/.gitignore index 87ba66d856..255bc859a8 100644 --- a/core/.gitignore +++ b/core/.gitignore @@ -10,6 +10,7 @@ /test-results/ /playwright-report/ /playwright/.cache/ +/.tests # next.js /.next/ diff --git a/core/app/[locale]/(default)/(auth)/login/page.tsx b/core/app/[locale]/(default)/(auth)/login/page.tsx index 1239e5e268..aadfd2308c 100644 --- a/core/app/[locale]/(default)/(auth)/login/page.tsx +++ b/core/app/[locale]/(default)/(auth)/login/page.tsx @@ -43,24 +43,30 @@ export default async function Login({ params, searchParams }: Props) { -
-

{t('CreateAccount.title')}

-

{t('CreateAccount.accountBenefits')}

-
    -
  • {t('CreateAccount.fastCheckout')}
  • -
  • {t('CreateAccount.multipleAddresses')}
  • -
  • {t('CreateAccount.ordersHistory')}
  • -
  • {t('CreateAccount.ordersTracking')}
  • -
  • {t('CreateAccount.wishlists')}
  • -
- - {t('CreateAccount.cta')} - +
+

+ {t('CreateAccount.title')} +

+
+

{t('CreateAccount.accountBenefits')}

+
    +
  • {t('CreateAccount.fastCheckout')}
  • +
  • {t('CreateAccount.multipleAddresses')}
  • +
  • {t('CreateAccount.ordersHistory')}
  • +
  • {t('CreateAccount.ordersTracking')}
  • +
  • {t('CreateAccount.wishlists')}
  • +
+ + {t('CreateAccount.cta')} + +
diff --git a/core/app/[locale]/(default)/(auth)/register/_actions/prefixes.ts b/core/app/[locale]/(default)/(auth)/register/_actions/prefixes.ts new file mode 100644 index 0000000000..239c5a3924 --- /dev/null +++ b/core/app/[locale]/(default)/(auth)/register/_actions/prefixes.ts @@ -0,0 +1,2 @@ +export const ADDRESS_FIELDS_NAME_PREFIX = 'customAddress_'; +export const CUSTOMER_FIELDS_NAME_PREFIX = 'customCustomer_'; diff --git a/core/app/[locale]/(default)/(faceted)/brand/[slug]/page.tsx b/core/app/[locale]/(default)/(faceted)/brand/[slug]/page.tsx index 2104fd0ba3..e4440f56ae 100644 --- a/core/app/[locale]/(default)/(faceted)/brand/[slug]/page.tsx +++ b/core/app/[locale]/(default)/(faceted)/brand/[slug]/page.tsx @@ -145,9 +145,10 @@ export default async function Brand(props: Props) { }); const streamableTotalCount = Streamable.from(async () => { + const format = await getFormatter(); const search = await streamableFacetedSearch; - return search.products.collectionInfo?.totalItems ?? 0; + return format.number(search.products.collectionInfo?.totalItems ?? 0); }); const streamablePagination = Streamable.from(async () => { diff --git a/core/app/[locale]/(default)/(faceted)/category/[slug]/page.tsx b/core/app/[locale]/(default)/(faceted)/category/[slug]/page.tsx index f434cbdf1f..e3ccba4bf3 100644 --- a/core/app/[locale]/(default)/(faceted)/category/[slug]/page.tsx +++ b/core/app/[locale]/(default)/(faceted)/category/[slug]/page.tsx @@ -158,9 +158,10 @@ export default async function Category(props: Props) { }); const streamableTotalCount = Streamable.from(async () => { + const format = await getFormatter(); const search = await streamableFacetedSearch; - return search.products.collectionInfo?.totalItems ?? 0; + return format.number(search.products.collectionInfo?.totalItems ?? 0); }); const streamablePagination = Streamable.from(async () => { diff --git a/core/app/[locale]/(default)/(faceted)/fetch-faceted-search.ts b/core/app/[locale]/(default)/(faceted)/fetch-faceted-search.ts index 4fd4363e5c..2a08b5c370 100644 --- a/core/app/[locale]/(default)/(faceted)/fetch-faceted-search.ts +++ b/core/app/[locale]/(default)/(faceted)/fetch-faceted-search.ts @@ -330,7 +330,7 @@ export const PublicSearchParamsSchema = z.object({ }); const AttributeKey = z.custom<`attr_${string}`>((val) => { - return typeof val === 'string' ? /^attr_\w+$/.test(val) : false; + return typeof val === 'string' ? /^attr_.+$/.test(val) : false; }); export const PublicToPrivateParams = PublicSearchParamsSchema.catchall(SearchParamToArray.nullish()) diff --git a/core/app/[locale]/(default)/(faceted)/search/page.tsx b/core/app/[locale]/(default)/(faceted)/search/page.tsx index a41770f23c..7b1c0d1ca3 100644 --- a/core/app/[locale]/(default)/(faceted)/search/page.tsx +++ b/core/app/[locale]/(default)/(faceted)/search/page.tsx @@ -137,16 +137,17 @@ export default async function Search(props: Props) { }); const streamableTotalCount = Streamable.from(async () => { + const format = await getFormatter(); const searchParams = await props.searchParams; const searchTerm = typeof searchParams.term === 'string' ? searchParams.term : ''; if (!searchTerm) { - return 0; + return format.number(0); } const search = await streamableFacetedSearch; - return search.products.collectionInfo?.totalItems ?? 0; + return format.number(search.products.collectionInfo?.totalItems ?? 0); }); const streamableEmptyStateTitle = Streamable.from(async () => { diff --git a/core/app/[locale]/(default)/account/addresses/page.tsx b/core/app/[locale]/(default)/account/addresses/page.tsx index a4b3f4b470..49c02451a6 100644 --- a/core/app/[locale]/(default)/account/addresses/page.tsx +++ b/core/app/[locale]/(default)/account/addresses/page.tsx @@ -4,7 +4,6 @@ import { getTranslations, setRequestLocale } from 'next-intl/server'; import { Address, AddressListSection } from '@/vibes/soul/sections/address-list-section'; import { - fieldToFieldNameTransformer, formFieldTransformer, injectCountryCodeOptions, } from '~/data-transformers/form-field-transformer'; @@ -92,14 +91,6 @@ export default async function Addresses({ params, searchParams }: Props) { return injectCountryCodeOptions(field, countries ?? []); }) - .filter(exists) - .map((field) => { - if (Array.isArray(field)) { - return field.map(fieldToFieldNameTransformer); - } - - return fieldToFieldNameTransformer(field); - }) .filter(exists); return ( @@ -110,7 +101,9 @@ export default async function Addresses({ params, searchParams }: Props) { createLabel={t('create')} deleteLabel={t('delete')} editLabel={t('edit')} - fields={[...fields, { name: 'id', type: 'hidden' }]} + emptyStateTitle={t('EmptyState.title')} + fields={[...fields, { name: 'id', type: 'hidden', label: 'ID' }]} + minimumAddressCount={0} setDefaultLabel={t('setDefault')} showAddFormLabel={t('cta')} title={t('title')} diff --git a/core/app/[locale]/(default)/account/layout.tsx b/core/app/[locale]/(default)/account/layout.tsx index 210c81ec4b..5aa581dd47 100644 --- a/core/app/[locale]/(default)/account/layout.tsx +++ b/core/app/[locale]/(default)/account/layout.tsx @@ -20,10 +20,10 @@ export default async function Layout({ children, params }: Props) { sidebar={ diff --git a/core/app/[locale]/(default)/account/orders/page.tsx b/core/app/[locale]/(default)/account/orders/page.tsx index 37e7130ecd..65d4167426 100644 --- a/core/app/[locale]/(default)/account/orders/page.tsx +++ b/core/app/[locale]/(default)/account/orders/page.tsx @@ -50,6 +50,8 @@ export default async function Orders({ params, searchParams }: Props) { return ( 0) { - return submission.reply({ formErrors: result.errors.map((error) => error.message) }); + return { + lastResult: submission.reply({ formErrors: result.errors.map((error) => error.message) }), + }; } - return submission.reply(); + return { + lastResult: submission.reply(), + successMessage: t('passwordUpdated'), + }; } catch (error) { // eslint-disable-next-line no-console console.error(error); if (error instanceof BigCommerceGQLError) { - return submission.reply({ - formErrors: error.errors.map(({ message }) => message), - }); + return { + lastResult: submission.reply({ + formErrors: error.errors.map(({ message }) => message), + }), + }; } if (error instanceof Error) { - return submission.reply({ formErrors: [error.message] }); + return { + lastResult: submission.reply({ formErrors: [error.message] }), + }; } - return submission.reply({ formErrors: [t('somethingWentWrong')] }); + return { lastResult: submission.reply({ formErrors: [t('somethingWentWrong')] }) }; } }; diff --git a/core/app/[locale]/(default)/account/settings/_actions/update-customer.ts b/core/app/[locale]/(default)/account/settings/_actions/update-customer.ts index ebee64099f..8d678d562c 100644 --- a/core/app/[locale]/(default)/account/settings/_actions/update-customer.ts +++ b/core/app/[locale]/(default)/account/settings/_actions/update-customer.ts @@ -79,7 +79,7 @@ export const updateCustomer: UpdateAccountAction = async (prevState, formData) = return { account: submission.value, - successMessage: t('passwordUpdated'), + successMessage: t('settingsUpdated'), lastResult: submission.reply(), }; } catch (error) { diff --git a/core/app/[locale]/(default)/account/wishlists/[id]/_components/wishlist-actions.tsx b/core/app/[locale]/(default)/account/wishlists/[id]/_components/wishlist-actions.tsx index c1cadfa3a6..f4d748291d 100644 --- a/core/app/[locale]/(default)/account/wishlists/[id]/_components/wishlist-actions.tsx +++ b/core/app/[locale]/(default)/account/wishlists/[id]/_components/wishlist-actions.tsx @@ -16,11 +16,13 @@ interface Props { isMobileUser: Streamable; shareLabel: string; shareCloseLabel: string; + shareCopyLabel: string; shareModalTitle: string; shareSuccessMessage: string; shareCopiedMessage: string; shareDisabledTooltip: string; menuActions: WishlistAction[]; + actionsTitle?: string; } export const WishlistActions = ({ @@ -28,11 +30,13 @@ export const WishlistActions = ({ isMobileUser, shareLabel, shareCloseLabel, + shareCopyLabel, shareModalTitle, shareSuccessMessage, shareCopiedMessage, shareDisabledTooltip, menuActions, + actionsTitle, }: Props) => { const { publicUrl } = wishlist; @@ -42,11 +46,12 @@ export const WishlistActions = ({
-
+
{publicUrl != null && publicUrl !== '' && ( )} - +
@@ -71,7 +76,7 @@ export function WishlistActionsSkeleton() {
-
+
diff --git a/core/app/[locale]/(default)/account/wishlists/[id]/_components/wishlist-analytics-provider.tsx b/core/app/[locale]/(default)/account/wishlists/[id]/_components/wishlist-analytics-provider.tsx index 951a83c60d..763ce72ade 100644 --- a/core/app/[locale]/(default)/account/wishlists/[id]/_components/wishlist-analytics-provider.tsx +++ b/core/app/[locale]/(default)/account/wishlists/[id]/_components/wishlist-analytics-provider.tsx @@ -25,7 +25,7 @@ export function WishlistAnalyticsProvider( props: PropsWithChildren<{ data: Streamable }>, ) { return ( - }> + ); diff --git a/core/app/[locale]/(default)/account/wishlists/[id]/page-data.ts b/core/app/[locale]/(default)/account/wishlists/[id]/page-data.ts index 33592f260e..ae14ab4173 100644 --- a/core/app/[locale]/(default)/account/wishlists/[id]/page-data.ts +++ b/core/app/[locale]/(default)/account/wishlists/[id]/page-data.ts @@ -3,7 +3,6 @@ import { cache } from 'react'; import { getSessionCustomerAccessToken } from '~/auth'; import { client } from '~/client'; import { graphql } from '~/client/graphql'; -import { revalidate } from '~/client/revalidate-target'; import { TAGS } from '~/client/tags'; import { WishlistPaginatedItemsFragment } from '~/components/wishlist/fragment'; import { getPreferredCurrencyCode } from '~/lib/currency'; @@ -47,7 +46,7 @@ export const getCustomerWishlist = cache(async (entityId: number, pagination: Pa document: WishlistDetailsQuery, variables: { ...paginationArgs, currencyCode, entityId }, customerAccessToken, - fetchOptions: { next: { revalidate, tags: [TAGS.customer] } }, + fetchOptions: { cache: 'no-store', next: { tags: [TAGS.customer] } }, }); const wishlist = response.data.customer?.wishlists.edges?.[0]?.node; diff --git a/core/app/[locale]/(default)/account/wishlists/[id]/page.tsx b/core/app/[locale]/(default)/account/wishlists/[id]/page.tsx index 4fab50e45a..6ea2baa869 100644 --- a/core/app/[locale]/(default)/account/wishlists/[id]/page.tsx +++ b/core/app/[locale]/(default)/account/wishlists/[id]/page.tsx @@ -1,5 +1,4 @@ import { removeEdgesAndNodes } from '@bigcommerce/catalyst-client'; -import { notFound } from 'next/navigation'; import { getFormatter, getTranslations, setRequestLocale } from 'next-intl/server'; import { SearchParams } from 'nuqs'; import { createSearchParamsCache, parseAsInteger, parseAsString } from 'nuqs/server'; @@ -10,6 +9,7 @@ import { Wishlist, WishlistDetails } from '@/vibes/soul/sections/wishlist-detail import { ExistingResultType } from '~/client/util'; import { defaultPageInfo, pageInfoTransformer } from '~/data-transformers/page-info-transformer'; import { wishlistDetailsTransformer } from '~/data-transformers/wishlists-transformer'; +import { redirect } from '~/i18n/routing'; import { isMobileUser } from '~/lib/user-agent'; import { removeWishlistItem } from '../_actions/remove-wishlist-item'; @@ -38,6 +38,7 @@ async function getWishlist( t: ExistingResultType>, pt: ExistingResultType>, searchParamsPromise: Promise, + locale: string, ): Promise { const entityId = Number(id); const searchParamsParsed = searchParamsCache.parse(await searchParamsPromise); @@ -45,7 +46,7 @@ async function getWishlist( const wishlist = await getCustomerWishlist(entityId, searchParamsParsed); if (!wishlist) { - return notFound(); + return redirect({ href: '/account/wishlists/', locale }); } return wishlistDetailsTransformer(wishlist, t, pt, formatter); @@ -100,6 +101,7 @@ export default async function WishlistPage({ params, searchParams }: Props) { return ( getPaginationInfo(id, searchParams))} prevHref="/account/wishlists" removeAction={removeWishlistItem} - wishlist={Streamable.from(() => getWishlist(id, t, pt, searchParams))} + removeButtonTitle={t('removeButtonTitle')} + wishlist={Streamable.from(() => getWishlist(id, t, pt, searchParams, locale))} /> ); diff --git a/core/app/[locale]/(default)/account/wishlists/_components/wishlist-actions-menu.tsx b/core/app/[locale]/(default)/account/wishlists/_components/wishlist-actions-menu.tsx index 9f08a16253..6fc56ff205 100644 --- a/core/app/[locale]/(default)/account/wishlists/_components/wishlist-actions-menu.tsx +++ b/core/app/[locale]/(default)/account/wishlists/_components/wishlist-actions-menu.tsx @@ -37,18 +37,19 @@ interface WishlistMenuAction extends WishlistActionBase { export type WishlistAction = WishlistModalAction | WishlistMenuAction; interface Props { + actionsTitle?: string; share?: { wishlistName: string; label: string; publicUrl: string; modalTitle: string; - modalCloseLabel: string; copiedMessage: string; isMobileUser: boolean; isPublic: boolean; successMessage: string; disabledTooltip: string; closeLabel: string; + copyLabel: string; }; items: WishlistAction[]; } @@ -84,13 +85,19 @@ function getShareMenuItemProps( label: share.label, disabled: !share.isPublic, key, - modal: getShareWishlistModal(share.modalTitle, share.modalCloseLabel, share.publicUrl, () => { - void copyToClipboard(share.publicUrl); - }), + modal: getShareWishlistModal( + share.modalTitle, + share.copyLabel, + share.closeLabel, + share.publicUrl, + () => { + void copyToClipboard(share.publicUrl); + }, + ), }; } -export const WishlistActionsMenu = ({ items, share }: Props) => { +export const WishlistActionsMenu = ({ actionsTitle, items, share }: Props) => { const [state, dispatch] = useReducer(reducer, {}); const shareModalKey = 'share-dropdown-modal'; const getShareUrl = (publicUrl: string) => String(new URL(publicUrl, window.location.origin)); @@ -149,12 +156,14 @@ export const WishlistActionsMenu = ({ items, share }: Props) => { size="small" variant="tertiary" > - + + {actionsTitle} + {modals.map(({ key, modal: { formAction: action, ...modalProps } }) => ( void | Promise, @@ -102,7 +103,7 @@ export const getShareWishlistModal = ( title, buttons: [ { type: 'cancel', label: closeLabel }, - { label: 'Copy', variant: 'primary', action }, + { label: copyLabel, variant: 'primary', action }, ], }); diff --git a/core/app/[locale]/(default)/account/wishlists/page-data.ts b/core/app/[locale]/(default)/account/wishlists/page-data.ts index fb5964855e..02a1c3f7b1 100644 --- a/core/app/[locale]/(default)/account/wishlists/page-data.ts +++ b/core/app/[locale]/(default)/account/wishlists/page-data.ts @@ -3,7 +3,6 @@ import { cache } from 'react'; import { getSessionCustomerAccessToken } from '~/auth'; import { client } from '~/client'; import { graphql } from '~/client/graphql'; -import { revalidate } from '~/client/revalidate-target'; import { TAGS } from '~/client/tags'; import { WishlistsFragment } from '~/components/wishlist/fragment'; import { getPreferredCurrencyCode } from '~/lib/currency'; @@ -42,7 +41,7 @@ export const getCustomerWishlists = cache(async ({ limit = 9, before, after }: P document: WishlistsPageQuery, variables: { ...paginationArgs, currencyCode }, customerAccessToken, - fetchOptions: { next: { revalidate, tags: [TAGS.customer] } }, + fetchOptions: { cache: 'no-store', next: { tags: [TAGS.customer] } }, }); const wishlists = response.data.customer?.wishlists; diff --git a/core/app/[locale]/(default)/account/wishlists/page.tsx b/core/app/[locale]/(default)/account/wishlists/page.tsx index 8f01be8037..5fba791cfa 100644 --- a/core/app/[locale]/(default)/account/wishlists/page.tsx +++ b/core/app/[locale]/(default)/account/wishlists/page.tsx @@ -74,7 +74,6 @@ export default async function Wishlists({ params, searchParams }: Props) { emptyStateCallToAction={ } - emptyStateSubtitle={t('noWishlistsSubtitle')} emptyStateTitle={t('noWishlists')} emptyWishlistStateText={t('emptyWishlist')} itemActions={{ @@ -85,6 +84,7 @@ export default async function Wishlists({ params, searchParams }: Props) { return ( getPaginationInfo(searchParams))} - placeholderCount={1} title={t('title')} viewWishlistLabel={t('viewWishlist')} wishlists={Streamable.from(() => listWishlists(searchParams, t))} diff --git a/core/app/[locale]/(default)/cart/_components/cart-analytics-provider.tsx b/core/app/[locale]/(default)/cart/_components/cart-analytics-provider.tsx index 923c9072b3..a9f7736d0c 100644 --- a/core/app/[locale]/(default)/cart/_components/cart-analytics-provider.tsx +++ b/core/app/[locale]/(default)/cart/_components/cart-analytics-provider.tsx @@ -26,7 +26,7 @@ export function CartAnalyticsProvider( props: PropsWithChildren<{ data: Streamable }>, ) { return ( - }> + ); diff --git a/core/app/[locale]/(default)/cart/page-data.ts b/core/app/[locale]/(default)/cart/page-data.ts index 93b9d963de..7c6733d455 100644 --- a/core/app/[locale]/(default)/cart/page-data.ts +++ b/core/app/[locale]/(default)/cart/page-data.ts @@ -1,7 +1,9 @@ +import { cache } from 'react'; + import { getSessionCustomerAccessToken } from '~/auth'; import { client } from '~/client'; -import { FragmentOf, graphql, VariablesOf } from '~/client/graphql'; -import { getShippingZones } from '~/client/management/get-shipping-zones'; +import { graphql, VariablesOf } from '~/client/graphql'; +import { revalidate } from '~/client/revalidate-target'; import { TAGS } from '~/client/tags'; export const PhysicalItemFragment = graphql(` @@ -262,18 +264,34 @@ export const getCart = async (variables: Variables) => { return data; }; -export const getShippingCountries = async (geography: FragmentOf) => { - const hasAccessToken = Boolean(process.env.BIGCOMMERCE_ACCESS_TOKEN); - const shippingZones = hasAccessToken ? await getShippingZones() : []; - const countries = geography.countries ?? []; - - const uniqueCountryZones = new Set( - shippingZones.map((zone) => zone.locations.map((location) => location.country_iso2)).flat(), - ); - - return countries.filter((countryDetails) => { - const isCountryInTheList = uniqueCountryZones.has(countryDetails.code); +const SupportedShippingDestinationsQuery = graphql(` + query SupportedShippingDestinations { + site { + settings { + shipping { + supportedShippingDestinations { + countries { + entityId + code + name + statesOrProvinces { + entityId + name + abbreviation + } + } + } + } + } + } + } +`); - return isCountryInTheList || !hasAccessToken; +export const getShippingCountries = cache(async () => { + const { data } = await client.fetch({ + document: SupportedShippingDestinationsQuery, + fetchOptions: { next: { revalidate } }, }); -}; + + return data.site.settings?.shipping?.supportedShippingDestinations.countries ?? []; +}); diff --git a/core/app/[locale]/(default)/cart/page.tsx b/core/app/[locale]/(default)/cart/page.tsx index 64e7ae148e..bf7e3e8d61 100644 --- a/core/app/[locale]/(default)/cart/page.tsx +++ b/core/app/[locale]/(default)/cart/page.tsx @@ -134,7 +134,7 @@ export default async function Cart({ params }: Props) { checkout?.shippingConsignments?.find((consignment) => consignment.selectedShippingOption) || checkout?.shippingConsignments?.[0]; - const shippingCountries = await getShippingCountries(data.geography); + const shippingCountries = await getShippingCountries(); const countries = shippingCountries.map((country) => ({ value: country.code, diff --git a/core/app/[locale]/(default)/checkout/route.ts b/core/app/[locale]/(default)/checkout/route.ts index 5a05ea19ef..b99f40bd7e 100644 --- a/core/app/[locale]/(default)/checkout/route.ts +++ b/core/app/[locale]/(default)/checkout/route.ts @@ -7,12 +7,15 @@ import { getChannelIdFromLocale } from '~/channels.config'; import { client } from '~/client'; import { graphql } from '~/client/graphql'; import { redirect } from '~/i18n/routing'; +import { getVisitIdCookie, getVisitorIdCookie } from '~/lib/analytics/bigcommerce'; import { getCartId } from '~/lib/cart'; const CheckoutRedirectMutation = graphql(` - mutation CheckoutRedirectMutation($cartId: String!) { + mutation CheckoutRedirectMutation($cartId: String!, $visitId: UUID, $visitorId: UUID) { cart { - createCartRedirectUrls(input: { cartEntityId: $cartId }) { + createCartRedirectUrls( + input: { cartEntityId: $cartId, visitId: $visitId, visitorId: $visitorId } + ) { errors { ... on NotFoundError { __typename @@ -36,10 +39,13 @@ export async function GET(req: NextRequest, { params }: { params: Promise<{ loca return redirect({ href: '/cart', locale }); } + const visitId = await getVisitIdCookie(); + const visitorId = await getVisitorIdCookie(); + try { const { data } = await client.fetch({ document: CheckoutRedirectMutation, - variables: { cartId }, + variables: { cartId, visitId, visitorId }, fetchOptions: { cache: 'no-store' }, customerAccessToken, channelId, diff --git a/core/app/[locale]/(default)/compare/_components/compare-analytics-provider.tsx b/core/app/[locale]/(default)/compare/_components/compare-analytics-provider.tsx index eb2bd57367..c03f2fdd53 100644 --- a/core/app/[locale]/(default)/compare/_components/compare-analytics-provider.tsx +++ b/core/app/[locale]/(default)/compare/_components/compare-analytics-provider.tsx @@ -25,7 +25,7 @@ export function CompareAnalyticsProvider( props: PropsWithChildren<{ data: Streamable }>, ) { return ( - }> + ); diff --git a/core/app/[locale]/(default)/compare/page-data.ts b/core/app/[locale]/(default)/compare/page-data.ts index ccc79dd6d0..9b0acac0f7 100644 --- a/core/app/[locale]/(default)/compare/page-data.ts +++ b/core/app/[locale]/(default)/compare/page-data.ts @@ -1,85 +1,77 @@ import { removeEdgesAndNodes } from '@bigcommerce/catalyst-client'; import { cache } from 'react'; -import { getSessionCustomerAccessToken } from '~/auth'; import { client } from '~/client'; import { graphql } from '~/client/graphql'; import { revalidate } from '~/client/revalidate-target'; +import { CurrencyCode } from '~/components/header/fragment'; import { ProductCardFragment } from '~/components/product-card/fragment'; -import { getPreferredCurrencyCode } from '~/lib/currency'; export const MAX_COMPARE_LIMIT = 10; -export const CompareCardFragment = graphql( +const ComparedProductsQuery = graphql( ` - fragment CompareCardFragment on Product { - ...ProductCardFragment - description - sku - weight { - value - unit - } - condition - customFields { - edges { - node { - entityId - name - value - } - } - } - productOptions(first: 1) { - edges { - node { - entityId - } - } - } - inventory { - isInStock - } - availabilityV2 { - status - } - } - `, - [ProductCardFragment], -); - -const ComparePageQuery = graphql( - ` - query ComparePageQuery($entityIds: [Int!], $first: Int, $currencyCode: currencyCode) { + query ComparedProductsQuery($entityIds: [Int!], $first: Int, $currencyCode: currencyCode) { site { products(entityIds: $entityIds, first: $first) { edges { node { - ...CompareCardFragment + ...ProductCardFragment description + sku + weight { + value + unit + } + condition + customFields { + edges { + node { + entityId + name + value + } + } + } + productOptions(first: 1) { + edges { + node { + entityId + } + } + } + inventory { + isInStock + } + availabilityV2 { + status + } } } } } } `, - [CompareCardFragment], + [ProductCardFragment], ); -export const getCompareData = cache(async (productIds: number[] = []) => { - const customerAccessToken = await getSessionCustomerAccessToken(); - const currencyCode = await getPreferredCurrencyCode(); +export const getComparedProducts = cache( + async (productIds: number[] = [], currencyCode?: CurrencyCode, customerAccessToken?: string) => { + if (productIds.length === 0) { + return []; + } - const { data } = await client.fetch({ - document: ComparePageQuery, - variables: { - entityIds: productIds, - first: productIds.length ? MAX_COMPARE_LIMIT : 0, - currencyCode, - }, - customerAccessToken, - fetchOptions: customerAccessToken ? { cache: 'no-store' } : { next: { revalidate } }, - }); + const { data } = await client.fetch({ + document: ComparedProductsQuery, + variables: { + entityIds: productIds, + first: productIds.length ? MAX_COMPARE_LIMIT : 0, + currencyCode, + }, + customerAccessToken, + fetchOptions: customerAccessToken ? { cache: 'no-store' } : { next: { revalidate } }, + }); - return removeEdgesAndNodes(data.site.products); -}); + return removeEdgesAndNodes(data.site.products); + }, +); diff --git a/core/app/[locale]/(default)/compare/page.tsx b/core/app/[locale]/(default)/compare/page.tsx index b31b8ad09b..13d8e868d3 100644 --- a/core/app/[locale]/(default)/compare/page.tsx +++ b/core/app/[locale]/(default)/compare/page.tsx @@ -1,17 +1,17 @@ import { removeEdgesAndNodes } from '@bigcommerce/catalyst-client'; import { Metadata } from 'next'; import { getFormatter, getTranslations, setRequestLocale } from 'next-intl/server'; -import { cache } from 'react'; import * as z from 'zod'; import { Streamable } from '@/vibes/soul/lib/streamable'; -import { CompareProduct } from '@/vibes/soul/primitives/compare-card'; import { CompareSection } from '@/vibes/soul/sections/compare-section'; +import { getSessionCustomerAccessToken } from '~/auth'; import { pricesTransformer } from '~/data-transformers/prices-transformer'; +import { getPreferredCurrencyCode } from '~/lib/currency'; import { addToCart } from './_actions/add-to-cart'; import { CompareAnalyticsProvider } from './_components/compare-analytics-provider'; -import { getCompareData } from './page-data'; +import { getComparedProducts } from './page-data'; const CompareParamsSchema = z.object({ ids: z @@ -30,49 +30,6 @@ const CompareParamsSchema = z.object({ .transform((value) => value?.map((id) => parseInt(id, 10))), }); -const getProducts = cache(async (productIds: number[] = []): Promise => { - const t = await getTranslations('Compare'); - - const products = await getCompareData(productIds); - const format = await getFormatter(); - - return products.map((product) => ({ - id: product.entityId.toString(), - title: product.name, - href: product.path, - image: product.defaultImage - ? { src: product.defaultImage.url, alt: product.defaultImage.altText } - : undefined, - price: pricesTransformer(product.prices, format), - subtitle: product.brand?.name ?? undefined, - rating: product.reviewSummary.averageRating, - description:
, - customFields: [ - { name: t('sku'), value: product.sku }, - { name: t('weight'), value: `${product.weight?.value} ${product.weight?.unit}` }, - ...removeEdgesAndNodes(product.customFields).map(({ name, value }) => ({ name, value })), - ], - hasVariants: removeEdgesAndNodes(product.productOptions).length > 0, - isPreorder: product.availabilityV2.status === 'Preorder', - disabled: product.availabilityV2.status === 'Unavailable' || !product.inventory.isInStock, - })); -}); - -const getAnalyticsData = async (productIds: number[] = []) => { - const products = await getCompareData(productIds); - - return products.map((product) => { - return { - id: product.entityId, - name: product.name, - sku: product.sku, - brand: product.brand?.name ?? '', - price: product.prices?.price.value ?? 0, - currency: product.prices?.price.currencyCode ?? '', - }; - }); -}; - interface Props { params: Promise<{ locale: string }>; searchParams: Promise<{ @@ -97,12 +54,63 @@ export default async function Compare(props: Props) { const t = await getTranslations('Compare'); - const searchParams = await props.searchParams; - const parsed = CompareParamsSchema.parse(searchParams); - const productIds = parsed.ids?.filter((id) => !Number.isNaN(id)); + const streamableProducts = Streamable.from(async () => { + const customerAccessToken = await getSessionCustomerAccessToken(); + const currencyCode = await getPreferredCurrencyCode(); + + const searchParams = await props.searchParams; + const parsed = CompareParamsSchema.parse(searchParams); + const productIds = parsed.ids?.filter((id) => !Number.isNaN(id)); + + const products = await getComparedProducts(productIds, currencyCode, customerAccessToken); + const format = await getFormatter(); + + return products.map((product) => ({ + id: product.entityId.toString(), + title: product.name, + href: product.path, + image: product.defaultImage + ? { src: product.defaultImage.url, alt: product.defaultImage.altText } + : undefined, + price: pricesTransformer(product.prices, format), + subtitle: product.brand?.name ?? undefined, + rating: product.reviewSummary.averageRating, + description:
, + customFields: [ + { name: t('sku'), value: product.sku }, + { name: t('weight'), value: `${product.weight?.value} ${product.weight?.unit}` }, + ...removeEdgesAndNodes(product.customFields).map(({ name, value }) => ({ name, value })), + ], + hasVariants: removeEdgesAndNodes(product.productOptions).length > 0, + isPreorder: product.availabilityV2.status === 'Preorder', + disabled: product.availabilityV2.status === 'Unavailable' || !product.inventory.isInStock, + })); + }); + + const streamableAnalyticsData = Streamable.from(async () => { + const customerAccessToken = await getSessionCustomerAccessToken(); + const currencyCode = await getPreferredCurrencyCode(); + + const searchParams = await props.searchParams; + const parsed = CompareParamsSchema.parse(searchParams); + const productIds = parsed.ids?.filter((id) => !Number.isNaN(id)); + + const products = await getComparedProducts(productIds, currencyCode, customerAccessToken); + + return products.map((product) => { + return { + id: product.entityId, + name: product.name, + sku: product.sku, + brand: product.brand?.name ?? '', + price: product.prices?.price.value ?? 0, + currency: product.prices?.price.currencyCode ?? '', + }; + }); + }); return ( - getAnalyticsData(productIds))}> + getProducts(productIds))} + products={streamableProducts} ratingLabel={t('rating')} title={t('title')} viewOptionsLabel={t('viewOptions')} diff --git a/core/app/[locale]/(default)/product/[slug]/_components/product-analytics-provider/index.tsx b/core/app/[locale]/(default)/product/[slug]/_components/product-analytics-provider.tsx similarity index 97% rename from core/app/[locale]/(default)/product/[slug]/_components/product-analytics-provider/index.tsx rename to core/app/[locale]/(default)/product/[slug]/_components/product-analytics-provider.tsx index 59b65be7b0..4daf4d56cd 100644 --- a/core/app/[locale]/(default)/product/[slug]/_components/product-analytics-provider/index.tsx +++ b/core/app/[locale]/(default)/product/[slug]/_components/product-analytics-provider.tsx @@ -24,7 +24,7 @@ export function ProductAnalyticsProvider( props: PropsWithChildren<{ data: Streamable }>, ) { return ( - }> + ); diff --git a/core/app/[locale]/(default)/product/[slug]/_components/product-review-schema/product-review-schema.tsx b/core/app/[locale]/(default)/product/[slug]/_components/product-review-schema/product-review-schema.tsx index 199bda4f6c..15f1cd00c4 100644 --- a/core/app/[locale]/(default)/product/[slug]/_components/product-review-schema/product-review-schema.tsx +++ b/core/app/[locale]/(default)/product/[slug]/_components/product-review-schema/product-review-schema.tsx @@ -1,3 +1,4 @@ +import DOMPurify from 'isomorphic-dompurify'; import { useFormatter } from 'next-intl'; import { Product as ProductSchemaType, WithContext } from 'schema-dts'; @@ -39,7 +40,7 @@ export const ProductReviewSchema = ({ reviews, productId }: Props) => { return (