├── .babelrc ├── .circleci ├── bin │ ├── calibre-deploy.sh │ ├── docker-tags │ └── should-run-snyk.sh └── config.yml ├── .dockerignore ├── .editorconfig ├── .env.example ├── .env.prod ├── .eslintignore ├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md └── pull_request_template.md ├── .gitignore ├── .graphqlrc ├── .husky ├── .gitignore └── commit-msg ├── .nvmrc ├── .prettierrc ├── .reaction └── project-hooks │ ├── README.md │ ├── post-build │ ├── post-project-start │ ├── post-system-start │ └── pre-build ├── .snyk ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── apiUtils ├── headerLanguage.js ├── localeMiddleware.js └── redirect.js ├── bin ├── fix-volumes ├── package-link ├── package-unlink ├── package-update ├── setup ├── start └── wait-for.sh ├── components ├── AccountDropdown │ ├── AccountDropdown.js │ └── index.js ├── Breadcrumbs │ ├── Breadcrumbs.js │ └── index.js ├── Cart │ ├── Cart.js │ ├── CartToggle.js │ └── index.js ├── CartItems │ ├── CartItems.js │ └── index.js ├── CartPopover │ ├── CartPopover.js │ └── index.js ├── CatalogGridItem │ ├── CatalogGridItem.js │ └── index.js ├── CheckoutActions │ ├── CheckoutActions.js │ └── index.js ├── CheckoutButtons │ ├── CheckoutButtons.js │ └── index.js ├── CheckoutSummary │ ├── CheckoutSummary.js │ └── index.js ├── Divider │ ├── Divider.js │ └── index.js ├── Entry │ ├── ChangePassword.js │ ├── Entry.js │ ├── EntryModal.js │ ├── ForgotPassword.js │ ├── Login.js │ ├── ResetPassword.js │ ├── SignUp.js │ └── index.js ├── Footer │ ├── Footer.js │ ├── Footer.test.js │ └── index.js ├── Header │ ├── Header.tsx │ └── index.ts ├── Layout │ ├── Layout.js │ └── index.js ├── Link │ ├── Link.js │ └── index.js ├── LocaleDropdown │ ├── LocaleDropdown.js │ └── index.js ├── MediaGallery │ ├── MediaGallery.js │ └── index.js ├── MediaGalleryItem │ ├── MediaGalleryItem.js │ └── index.js ├── MiniCart │ ├── MiniCart.js │ └── index.js ├── NavigationDesktop │ ├── NavigationDesktop.js │ ├── NavigationItemDesktop.js │ └── index.js ├── NavigationMobile │ ├── NavigationItemMobile.js │ ├── NavigationMobile.js │ ├── NavigationSubMenuMobile.js │ ├── NavigationToggleMobile.js │ └── index.js ├── OrderCard │ ├── OrderCard.js │ └── index.js ├── OrderCardFulfillmentGroup │ ├── OrderCardFulfillmentGroup.js │ └── index.js ├── OrderCardHeader │ ├── OrderCardHeader.js │ └── index.js ├── OrderCardStatusBadge │ ├── OrderCardStatusBadge.js │ └── index.js ├── OrderCardSummary │ ├── OrderCardSummary.js │ └── index.js ├── OrderFulfillmentGroup │ ├── OrderFulfillmentGroup.js │ └── index.js ├── OrderSummary │ ├── OrderSummary.js │ └── index.js ├── PageLoading │ ├── PageLoading.js │ └── index.js ├── PageSizeSelector │ ├── PageSizeSelector.js │ └── index.js ├── PageStepper │ ├── PageStepper.js │ └── index.js ├── ProductDetail │ ├── ProductDetail.js │ └── index.js ├── ProductDetailAddToCart │ ├── ProductDetailAddToCart.js │ └── index.js ├── ProductDetailDescription │ ├── ProductDetailDescription.js │ └── index.js ├── ProductDetailOption │ ├── ProductDetailOption.js │ ├── index.js │ └── styles.js ├── ProductDetailOptionsList │ ├── ProductDetailOptionsList.js │ └── index.js ├── ProductDetailPrice │ ├── ProductDetailPrice.js │ └── index.js ├── ProductDetailTitle │ ├── ProductDetailTitle.js │ └── index.js ├── ProductDetailVendor │ ├── ProductDetailVendor.js │ └── index.js ├── ProductGrid │ ├── ProductGrid.js │ ├── ProductGridEmptyMessage.js │ └── index.js ├── ProductGridHero │ ├── ProductGridHero.js │ └── index.js ├── ProductGridTitle │ ├── ProductGridTitle.js │ └── index.js ├── Profile │ ├── Profile.js │ ├── ProfileContainer.js │ ├── index.js │ └── viewer.gql ├── ProfileAddressBook │ ├── ProfileAddressBook.js │ └── index.js ├── ProfileMenu │ ├── ProfileMenu.js │ └── index.js ├── ProfileOrders │ ├── ProfileOrders.js │ └── index.js ├── ProgressiveImage │ ├── ProgressiveImage.js │ └── index.js ├── Select │ ├── Select.js │ └── index.js ├── Social │ ├── FacebookSocial.js │ ├── TwitterSocial.js │ └── index.js ├── SortBySelector │ ├── SortBySelector.js │ └── index.js ├── StripeCard │ ├── .gitignore │ ├── StripeCard.js │ ├── StripeInput.js │ ├── hooks │ │ ├── createStripePaymentIntent.gql │ │ └── useStripePaymentIntent.js │ ├── index.js │ ├── package.json │ └── provider │ │ └── StripeWrapper.js ├── VariantItem │ ├── VariantItem.js │ └── index.js └── VariantList │ ├── VariantList.js │ └── index.js ├── config.js ├── containers ├── address │ ├── withAddressBook.js │ └── withAddressValidation.js ├── cart │ └── withCart.js ├── catalog │ ├── catalogItems.gql │ └── withCatalogItems.js └── order │ ├── withOrder.js │ └── withOrders.js ├── context ├── AuthContext.js ├── CartContext.js ├── ContextProviders.js ├── LocaleContext.js ├── RoutingContext.js ├── ShopContext.js ├── TagsContext.js └── UIContext.js ├── custom ├── README.md ├── analytics │ ├── index.js │ ├── provider.example.js │ └── segment.js ├── componentsContext.js ├── favicons.js ├── paymentMethods.js └── reactionTheme.js ├── docker-compose.dev.yml ├── docker-compose.yml ├── docs ├── README.md ├── architecture │ ├── README.md │ └── decisions │ │ └── README.md ├── cart.md ├── components │ └── breadcrumbs.md ├── page-architecture.md ├── static-assets.md ├── tags.md ├── testing.md ├── theming.md └── tracking-events.md ├── hocs ├── inject.js └── withTranslation.js ├── hooks ├── address │ ├── mutations.gql │ ├── query.gql │ ├── useAddAccountAddressBookEntry.js │ ├── useAddressValidation.js │ ├── useRemoveAccountAddressBookEntry.js │ └── useUpdateAccountAddressBookEntry.js ├── availablePaymentMethods │ ├── availablePaymentMethods.gql │ └── useAvailablePaymentMethods.js ├── cart │ ├── fragments.gql │ ├── mutations.gql │ ├── queries.gql │ └── useCart.js ├── globalStores │ ├── useAuthStore.js │ ├── useCartStore.js │ ├── useRoutingStore.js │ └── useUIStore.js ├── orders │ ├── fragments.gql │ ├── placeOrder.gql │ ├── queries.gql │ ├── useOrder.js │ └── useOrders.js ├── shop │ └── useShop.js ├── useStores.js ├── useTags.js ├── useTranslation.js └── viewer │ ├── useViewer.js │ └── viewer.gql ├── lib ├── accountsServer.js ├── apollo │ ├── apolloClient.js │ ├── omitVariableTypenameLink.js │ └── withApollo.js ├── tracking │ ├── constants.js │ ├── dispatch.js │ ├── track.js │ ├── trackCartItems.js │ ├── trackCheckout.js │ ├── trackCheckoutStep.js │ ├── trackOrder.js │ ├── trackProduct.js │ ├── trackProductClicked.js │ ├── trackProductListViewed.js │ └── utils │ │ ├── getCartItemTrackingData.js │ │ ├── getCartItemTrackingData.test.js │ │ ├── getProductListTrackingData.js │ │ ├── getProductListTrackingData.test.js │ │ ├── getProductTrackingData.js │ │ ├── getProductTrackingData.test.js │ │ ├── getVariantTrackingData.js │ │ └── getVariantTrackingData.test.js └── utils │ ├── SharedPropTypes.js │ ├── __mocks__ │ └── productData.mock.js │ ├── calculateRemainderDue.js │ ├── calculateRemainderDue.test.js │ ├── cartItemsConnectionToArray.js │ ├── cartItemsConnectionToArray.test.js │ ├── decoding.js │ ├── encoding.js │ ├── executionEnv.js │ ├── hashPassword.js │ ├── isProductBestseller.js │ ├── isProductBestseller.test.js │ ├── isProductLowQuantity.js │ ├── isProductLowQuantity.test.js │ ├── isProductOnSale.js │ ├── isProductOnSale.test.js │ ├── locales.json │ ├── pageSizes.js │ ├── pagination.js │ ├── pagination.test.js │ ├── priceByCurrencyCode.js │ ├── priceByCurrencyCode.test.js │ ├── relayConnectionToArray.js │ ├── relayConnectionToArray.test.js │ ├── variantById.js │ ├── variantById.test.js │ └── withLocales.js ├── next-env.d.ts ├── next.config.js ├── package.json ├── pages ├── [lang] │ ├── cart.js │ ├── cart │ │ ├── checkout.js │ │ └── login.js │ ├── checkout │ │ └── order.js │ ├── index.js │ ├── login.js │ ├── product │ │ └── [...slugOrId].js │ ├── profile │ │ ├── address.js │ │ └── orders.js │ └── tag │ │ └── [slug].js ├── _app.js ├── _document.js ├── _error.js └── api │ ├── detectLanguage.js │ ├── detectLanguage │ └── [...slug].js │ └── sitemap.js ├── public ├── favicons │ ├── apple-touch-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── manifest.json │ └── safari-pinned-tab.svg └── images │ └── placeholder.gif ├── setupTests.js ├── staticUtils ├── catalog │ ├── catalogItemProduct.js │ └── fetchCatalogProduct.js ├── graphQLRequest.js ├── shop │ ├── fetchPrimaryShop.js │ └── primaryShop.js ├── tag │ ├── fetchTag.js │ └── tag.js ├── tags │ ├── fetchAllTags.js │ └── tags.js └── translations │ └── fetchTranslations.js ├── translations ├── config.js ├── i18nRouter.js ├── isLocale.js └── locales │ ├── de │ ├── common.js │ ├── index.js │ └── productDetail.js │ ├── en │ ├── common.js │ ├── index.js │ └── productDetail.js │ └── index.js ├── tsconfig.json ├── types ├── material.d.ts └── reaction.d.ts └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "next/babel" 4 | ] 5 | } -------------------------------------------------------------------------------- /.circleci/bin/calibre-deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | URL=$1 6 | LOCATION=$2 7 | 8 | # Run One Off Test 9 | npx calibre@2.0.2 test create $URL --location=$LOCATION 10 | 11 | # Run Snapshot 12 | # California Snapshot Only (Be more generic as we add more site locations to track) 13 | if [ $LOCATION = "California" ] 14 | then 15 | npx calibre@2.0.2 site create-snapshot --site reaction-core-"$(echo $LOCATION | tr '[A-Z]' '[a-z]')" 16 | else 17 | echo "No Snapshot Configured for Location" 18 | fi 19 | -------------------------------------------------------------------------------- /.circleci/bin/docker-tags: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # outputs each tag we're attaching to this docker image 3 | set -e 4 | 5 | SHA=$1 6 | BRANCH=$2 7 | SHA1=$(git rev-parse --verify "${SHA}") 8 | 9 | # Echo to stderr 10 | echoerr() { echo "$@" 1>&2; } 11 | 12 | # Ensure that git SHA was provided 13 | if [[ -x "${SHA1}" ]]; then 14 | echoerr "Error, no git SHA provided" 15 | exit 1; 16 | fi 17 | 18 | # tag with the branch 19 | if [[ -n "${BRANCH}" ]]; then 20 | echo "${BRANCH}" 21 | fi 22 | 23 | # Tag with each git tag 24 | git show-ref --tags -d | grep "^${SHA1}" | sed -e 's,.* refs/tags/,,' -e 's/\^{}//' 2> /dev/null \ 25 | | xargs -I % \ 26 | echo "%" 27 | 28 | # Tag with latest if certain conditions are met 29 | if [[ "$BRANCH" == "trunk" ]]; then 30 | # Check to see if we have a valid `vX.X.X` tag and assign to CURRENT_TAG 31 | CURRENT_TAG=$( \ 32 | git show-ref --tags -d \ 33 | | grep "^${SHA1}" \ 34 | | sed -e 's,.* refs/tags/,,' -e 's/\^{}//' \ 35 | | grep "^v[0-9]\\+\\.[0-9]\\+\\.[0-9]\\+$" \ 36 | | sort \ 37 | ) 38 | 39 | # Find the highest tagged version number 40 | HIGHEST_TAG=$(git --no-pager tag | grep "^v[0-9]\\+\\.[0-9]\\+\\.[0-9]\\+$" | sort -r | head -n 1) 41 | 42 | # We tag :latest only if 43 | # 1. We have a current tag 44 | # 2. The current tag is equal to the highest tag, OR the highest tag does not exist 45 | if [[ -n "${CURRENT_TAG}" ]]; then 46 | if [[ "${CURRENT_TAG}" == "${HIGHEST_TAG}" ]] || [[ -z "${HIGHEST_TAG}" ]]; then 47 | echo "latest" 48 | fi 49 | fi 50 | fi 51 | -------------------------------------------------------------------------------- /.circleci/bin/should-run-snyk.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Please Use Google Shell Style: https://google.github.io/styleguide/shell.xml 4 | 5 | # ---- Start unofficial bash strict mode boilerplate 6 | # http://redsymbol.net/articles/unofficial-bash-strict-mode/ 7 | set -o errexit # always exit on error 8 | set -o errtrace # trap errors in functions as well 9 | set -o pipefail # don't ignore exit codes when piping output 10 | set -o posix # more strict failures in subshells 11 | # set -x # enable debugging 12 | 13 | IFS=$'\n\t' 14 | # ---- End unofficial bash strict mode boilerplate 15 | 16 | validate_env() { 17 | declare -a missing 18 | for var in "$@"; do 19 | if [[ -z "${!var}" ]]; then 20 | echo "⚠️ ERROR: Missing required environment variable: ${var}" 1>&2 21 | missing+=("${var}") 22 | fi 23 | done 24 | if [[ -n "${missing[*]}" ]]; then 25 | exit 1 26 | fi 27 | } 28 | 29 | main() { 30 | validate_env CIRCLE_COMPARE_URL DOCKER_REPOSITORY 31 | if [[ -z "${CIRCLE_PULL_REQUEST}" ]]; then 32 | echo "NO: Not a PR. Skipping Snyk." 33 | exit 34 | fi 35 | # Determine PR number from pull request link 36 | CIRCLE_PR_NUMBER="${CIRCLE_PR_NUMBER:-${CIRCLE_PULL_REQUEST##*/}}" 37 | PATH="${PATH}:${CIRCLE_WORKING_DIRECTORY}/node_modules/.bin" 38 | if [[ -v CIRCLE_PR_NUMBER ]] && [ -n ${CIRCLE_PR_NUMBER} ]; then 39 | # Get PR from github API 40 | url="https://api.github.com/repos/${DOCKER_REPOSITORY}/pulls/${CIRCLE_PR_NUMBER}" 41 | # Determine target/base branch from API response 42 | TARGET_BRANCH=$(curl --silent --location --fail --show-error "${url}" | 43 | jq -r '.base.ref') 44 | fi 45 | if [[ -z "${TARGET_BRANCH}" || ${TARGET_BRANCH} == "null" ]]; then 46 | echo "NO: Not a PR. Skipping Snyk." 47 | exit 48 | fi 49 | # If target branch does not exist or is trunk, run snyk tests 50 | if [[ ${TARGET_BRANCH} == "trunk" ]] || [[ -z "${TARGET_BRANCH/[ ]*\n/}" ]]; then 51 | echo "YES: always run when targeting trunk" 52 | exit 53 | fi 54 | # If package.json is different from the base branch, run snyk 55 | if git diff "$(basename "${CIRCLE_COMPARE_URL}")" package.json | grep -q diff; then 56 | echo "YES: package.json different. Running Snyk." 57 | exit 58 | fi 59 | echo "NO: package.json identical to target branch. Skipping Snyk." 60 | exit 61 | } 62 | 63 | main "$@" 64 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.js] 15 | max_line_length = 120 16 | indent_brace_style = 1TBS 17 | spaces_around_operators = true 18 | quote_type = double 19 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | CANONICAL_URL=http://localhost:4000 2 | BUILD_GRAPHQL_URL=http://localhost:3000/graphql 3 | EXTERNAL_GRAPHQL_URL=http://localhost:3000/graphql 4 | INTERNAL_GRAPHQL_URL=http://api.reaction.localhost:3000/graphql 5 | PORT=4000 6 | SEGMENT_ANALYTICS_SKIP_MINIMIZE=true 7 | SEGMENT_ANALYTICS_WRITE_KEY=ENTER_KEY_HERE 8 | SESSION_MAX_AGE_MS=2592000000 9 | SESSION_SECRET=CHANGEME 10 | STRIPE_PUBLIC_API_KEY=ENTER_STRIPE_PUBLIC_KEY_HERE -------------------------------------------------------------------------------- /.env.prod: -------------------------------------------------------------------------------- 1 | CANONICAL_URL=http://localhost:4000 2 | BUILD_GRAPHQL_URL=http://localhost:3000/graphql 3 | EXTERNAL_GRAPHQL_URL=http://localhost:3000/graphql 4 | INTERNAL_GRAPHQL_URL=http://api.reaction.localhost:3000/graphql 5 | PORT=4000 6 | SEGMENT_ANALYTICS_SKIP_MINIMIZE=true 7 | SEGMENT_ANALYTICS_WRITE_KEY=ENTER_KEY_HERE 8 | SESSION_MAX_AGE_MS=2592000000 9 | SESSION_SECRET=CHANGEME 10 | STRIPE_PUBLIC_API_KEY=ENTER_STRIPE_PUBLIC_KEY_HERE -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/* 2 | node_modules/* 3 | reports/* 4 | /**/*.d.ts 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | Type: **breaking|critical|major|minor** 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 1. Go to '...' 15 | 2. Click on '....' 16 | 3. Scroll down to '....' 17 | 4. See error 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **Desktop (please complete the following information):** 26 | - OS: [e.g. iOS] 27 | - Browser [e.g. chrome, safari] 28 | - Version [e.g. 22] 29 | 30 | **Smartphone (please complete the following information):** 31 | - Device: [e.g. iPhone6] 32 | - OS: [e.g. iOS8.1] 33 | - Browser [e.g. stock browser, safari] 34 | - Version [e.g. 22] 35 | 36 | **Additional context** 37 | Add any other context about the problem here. 38 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Resolves #issueNumber 2 | Impact: **breaking|critical|major|minor** 3 | Type: **feature|bugfix|performance|test|style|refactor|docs|chore** 4 | 5 | ## Issue 6 | 7 | Description of the issue this PR is solving, why it's happening, and how to reproduce it. This may differ from the original ticket as you now have more information at your disposal. 8 | 9 | ## Solution 10 | 11 | Summarize your solution to the problem. Please include short descriptions of any solutions you tested before arriving at your final solution. This will help reviewers know why you decided to solve this problem in this particular way and will speed up the review process. 12 | 13 | If you're solving a UIX related issue, please attach screen-caps or gifs showing how your solution differs from the issue. 14 | 15 | ## Breaking changes 16 | 17 | If you have a breaking changes, list them here, otherwise list none. 18 | 19 | Examples of breaking changes include changing file names, moving files, deleting files, renaming functions or exports, or changes to code which might cause previous versions of Reaction or third-party code not to work as expected. 20 | 21 | Note any work that you did to mitigate the effect of any breaking changes such as creating migrations, deprecation warnings, etc. 22 | 23 | ## Testing 24 | 25 | 1. List the steps needed for testing your change in this section. 26 | 2. Assume that testers already know how to start the app, and do the basic setup tasks. 27 | 3. Be detailed enough that someone can work through it without being too granular 28 | 29 | This project uses [semantic-release](https://semantic-release.gitbook.io/semantic-release/), please use their [commit message format.](https://semantic-release.gitbook.io/semantic-release/#commit-message-format). 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .fileStorage/ 3 | .vscode 4 | .idea 5 | .c9 6 | .env* 7 | !.env.example* 8 | !.env.prod* 9 | *.csv 10 | *.dat 11 | *.gz 12 | *.log 13 | *.out 14 | *.pid 15 | *.seed 16 | *.sublime-project 17 | *.sublime-workspace 18 | browser.config.js 19 | 20 | lib-cov 21 | logs 22 | node_modules 23 | npm-debug.log 24 | pids 25 | results 26 | allure-results 27 | package-lock.json 28 | 29 | .reaction/config.json 30 | 31 | .next/* 32 | src/.next/* 33 | build 34 | /reports 35 | 36 | docker-compose.override.yml 37 | 38 | # Yalc 39 | .yalc/ 40 | yalc.lock 41 | yalc-packages 42 | -------------------------------------------------------------------------------- /.graphqlrc: -------------------------------------------------------------------------------- 1 | { 2 | "request": { 3 | "url": "http://localhost:3000/graphql" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx commitlint -e -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 12.14.1 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "tabWidth": 2, 4 | "semi": true, 5 | "singleQuote": false, 6 | "bracketSpacing": true, 7 | "arrowParens": "always" 8 | } 9 | -------------------------------------------------------------------------------- /.reaction/project-hooks/README.md: -------------------------------------------------------------------------------- 1 | This directory contains project hooks for the Reaction development environment. 2 | They are invoked from the reaction-next build tools which operate across all 3 | projects to aid with orchestration. 4 | 5 | These hooks provide means for developers of an application to ensure that the 6 | application configure itself without higher-level coordination outside of the 7 | project. 8 | 9 | ## Included Hooks 10 | 11 | | Name | Description | 12 | | -------------------- | ---------------------------------------------------- | 13 | | `pre-build` | Invoked before Docker build. | 14 | | `post-build` | Invoked after Docker build and before project start. | 15 | | `post-project-start` | Invoked after project start. | 16 | | `post-system-start` | Invoked after entire system start. | 17 | 18 | More information can be found in the header documentation of each script. 19 | 20 | ## General Best Practices 21 | 22 | ### Check that Services Are Available Before Using Them 23 | 24 | The `post-project-start` and `post-system-start` hooks are called after the 25 | services are started with Docker. Though they have been started, it's possible 26 | that they will not yet be available to perform any script actions. This depends 27 | on the startup time for you application. 28 | 29 | This can lead to race conditions if you try to use a service in a hook script. 30 | 31 | Always check that any service is available before using it. 32 | 33 | Tools like [await](https://github.com/betalo-sweden/await) can help. 34 | 35 | ### Keep Hook Scripts Lightweight 36 | 37 | It can be tempting to add code directly to the hook script to directly perform 38 | a task. 39 | 40 | It is better to create a script in your project and call it from the hook. This 41 | keeps the scripted action reusable and available outside of the hook context. 42 | -------------------------------------------------------------------------------- /.reaction/project-hooks/post-build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Post Build Hook 3 | # Invoked by the reaction-next project bootstrapping process. 4 | # 5 | # Invoked after Docker build. 6 | # Perform any actions here that are required after docker-compose build but 7 | # before the project is started. 8 | # 9 | # Important Notes: 10 | # 11 | # - Expect that services are NOT running at this time. 12 | # - Do not assume that this hook script will run from this local directory. 13 | # The $__dir var is provided for convenience and may be used to invoke other 14 | # scripts. 15 | # - It is good practice to keep this script lightweight and invoke setup 16 | # scripts in your project. 17 | 18 | __current_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 19 | __root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" 20 | __root_name=$(basename "${__root_dir}") 21 | 22 | echo "${__root_name} post-build script invoked." 2>&1 23 | -------------------------------------------------------------------------------- /.reaction/project-hooks/post-project-start: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Post Project Start Hook 3 | # Invoked by the reaction-next project bootstrapping process. 4 | # 5 | # Invoked after this service is started. Can be used for project specific 6 | # actions that should be performed after the project is running, like database 7 | # setup, migrations, seeds, etc. Do not depend on other projects in this hook! 8 | # 9 | # Important Notes: 10 | # 11 | # - The hook runs after all Docker Compose services in THIS project are 12 | # started. Though started, there is no guarantee that these services are 13 | # ready (i.e. that they will respond to requests.) It is your responsibility 14 | # to test that services are available before using them to avoid race 15 | # conditions. 16 | # - Do not assume that this hook script will run from this local directory. 17 | # The $__dir var is provided for convenience and may be used to invoke other 18 | # scripts. 19 | # - It is good practice to keep this script lightweight and invoke setup 20 | # scripts in your project. 21 | 22 | __current_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 23 | __root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" 24 | __root_name=$(basename "${__root_dir}") 25 | 26 | echo "${__root_name} post-project-start script invoked." 2>&1 27 | -------------------------------------------------------------------------------- /.reaction/project-hooks/post-system-start: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Post System Start Hook 3 | # Invoked by the reaction-next project bootstrapping process. 4 | # 5 | # Invoked after all services in the system have been started. 6 | # 7 | # Important Notes: 8 | # 9 | # - The hook runs after all Docker Compose services in ALL projects are 10 | # started. Though started, there is no guarantee that these services are 11 | # ready (i.e. that they will respond to requests.) It is your responsibility 12 | # to test that services are available before using them to avoid race 13 | # conditions. 14 | # - Do not assume that this hook script will run from this local directory. 15 | # The $__dir var is provided for convenience and may be used to invoke other 16 | # scripts. 17 | # - It is good practice to keep this script lightweight and invoke setup 18 | # scripts in your project. 19 | 20 | __current_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 21 | __root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" 22 | __root_name=$(basename "${__root_dir}") 23 | 24 | echo "${__root_name} post-system-start script invoked." 2>&1 25 | -------------------------------------------------------------------------------- /.reaction/project-hooks/pre-build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Pre Build Hook 3 | # Invoked by the reaction-next project bootstrapping process. 4 | # 5 | # Invoked before Docker build. 6 | # Perform any actions here that are required before docker-compose build. For 7 | # example, copying values from .env.example to .env. 8 | # 9 | # Important Notes: 10 | # 11 | # - Expect that services are NOT running at this time. 12 | # - Do not assume that this hook script will run from this local directory. 13 | # The $__dir var is provided for convenience and may be used to invoke other 14 | # scripts. 15 | # - It is good practice to keep this script lightweight and invoke setup 16 | # scripts in your project. 17 | 18 | __current_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 19 | __root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" 20 | __root_name=$(basename "${__root_dir}") 21 | 22 | echo "${__root_name} post-project-start script invoked." 2>&1 23 | 24 | "${__root_dir}/bin/setup" 25 | -------------------------------------------------------------------------------- /.snyk: -------------------------------------------------------------------------------- 1 | # Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. 2 | version: v1.13.5 3 | patch: {} 4 | # ignores vulnerabilities until expiry date; change duration by modifying expiry date 5 | ignore: 6 | 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12-alpine 2 | 3 | ARG NEXTJS_DOTENV 4 | 5 | ENV NEXTJS_DOTENV=$NEXTJS_DOTENV 6 | 7 | # hadolint ignore=DL3018 8 | RUN apk --no-cache add bash curl less tini vim make 9 | SHELL ["/bin/bash", "-o", "pipefail", "-o", "errexit", "-u", "-c"] 10 | 11 | WORKDIR /usr/local/src/app 12 | ENV PATH=$PATH:/usr/local/src/app/node_modules/.bin 13 | 14 | # Allow yarn/npm to create ./node_modules 15 | RUN chown node:node . 16 | 17 | # Copy specific things so that we can keep the image as small as possible 18 | # without relying on each repo to include a .dockerignore file. 19 | COPY --chown=node:node ./ ./ 20 | 21 | USER node 22 | 23 | # Install ALL dependencies. We need devDependencies for the build command. 24 | RUN yarn install --production=false --frozen-lockfile --ignore-scripts --non-interactive --no-cache 25 | 26 | ENV BUILD_ENV=production NODE_ENV=production 27 | 28 | # hadolint ignore=SC2046 29 | RUN export $(grep -v '^#' .env.${NEXTJS_DOTENV:-prod} | xargs -0) && yarn build 30 | 31 | # Install only prod dependencies now that we've built, to make the image smaller 32 | RUN rm -rf node_modules/* 33 | RUN yarn install --production=true --frozen-lockfile --ignore-scripts --non-interactive 34 | 35 | # If any Node flags are needed, they can be set in the NODE_OPTIONS env variable. 36 | CMD ["tini", "--", "yarn", "start"] 37 | LABEL com.reactioncommerce.name="example-storefront" 38 | -------------------------------------------------------------------------------- /apiUtils/headerLanguage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Look up the accepted languages in the HTTP headers 3 | * 4 | * @param {Object} req - the current request 5 | * @returns {Array} A list of accepted languages 6 | */ 7 | export default function headerLookup(req) { 8 | let found; 9 | 10 | if (typeof req !== "undefined") { 11 | const { headers } = req; 12 | if (!headers) return found; 13 | 14 | const locales = []; 15 | const acceptLanguage = headers["accept-language"]; 16 | 17 | if (acceptLanguage) { 18 | const languages = []; let index; let match; 19 | const rgx = /(([a-z]{2})-?([A-Z]{2})?)\s*;?\s*(q=([0-9.]+))?/gi; 20 | 21 | do { 22 | match = rgx.exec(acceptLanguage); 23 | if (match) { 24 | const lng = match[1]; const weight = match[5] || "1"; const priority = Number(weight); 25 | if (lng && !isNaN(priority)) { 26 | languages.push({ lng, priority }); 27 | } 28 | } 29 | } while (match); 30 | 31 | languages.sort((langA, langB) => langB.priority - langA.priority); 32 | 33 | for (index = 0; index < languages.length; index += 1) { 34 | locales.push(languages[index].lng); 35 | } 36 | 37 | if (locales.length) found = locales; 38 | } 39 | } 40 | 41 | return found; 42 | } 43 | -------------------------------------------------------------------------------- /apiUtils/localeMiddleware.js: -------------------------------------------------------------------------------- 1 | import headerLanguage from "./headerLanguage"; 2 | import redirect from "./redirect"; 3 | 4 | export default (req, res) => { 5 | const { 6 | query: { slug, ...rest } 7 | } = req; 8 | 9 | const fallback = "de"; 10 | const allowedLocales = [ 11 | { name: "de-DE", locale: "de" }, 12 | { name: "de", locale: "de" }, 13 | { name: "en-AU", locale: "en" }, 14 | { name: "en-IN", locale: "en" }, 15 | { name: "en-CA", locale: "en" }, 16 | { name: "en-NZ", locale: "en" }, 17 | { name: "en-US", locale: "en" }, 18 | { name: "en-ZA", locale: "en" }, 19 | { name: "en-GB", locale: "en" }, 20 | { name: "en", locale: "en" } 21 | ]; 22 | 23 | const detections = headerLanguage(req); 24 | 25 | let found; 26 | 27 | if (detections && detections.length) { 28 | detections.forEach((language) => { 29 | if (found || typeof language !== "string") return; 30 | 31 | const lookedUpLocale = allowedLocales.find((allowedLocale) => allowedLocale.name === language); 32 | 33 | if (lookedUpLocale) { 34 | found = lookedUpLocale.locale; 35 | } 36 | }); 37 | } 38 | 39 | if (!found) { 40 | found = fallback; 41 | } 42 | 43 | const queryPart = rest ? (`?${Object.keys(rest).map((k) => `${k}=${rest[k]}`).join("&")}`) : ""; 44 | 45 | if (slug) { 46 | return redirect(res, 302, `/${found}${slug ? `/${slug.join("/")}` : ""}${queryPart}`); 47 | } 48 | 49 | return redirect(res, 302, `/${found}${queryPart}`); 50 | }; 51 | -------------------------------------------------------------------------------- /apiUtils/redirect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Redirects to a new given location 3 | * 4 | * @param {Object} res - response 5 | * @param {String} statusCode - HTTP response status code 6 | * @param {String} location - the new location 7 | * @returns {Object} the updated response object 8 | */ 9 | export default function redirect(res, statusCode, location) { 10 | if (!res) { 11 | throw new Error("Response object required"); 12 | } 13 | 14 | if (!statusCode) { 15 | throw new Error("Status code required"); 16 | } 17 | 18 | if (!location) { 19 | throw new Error("Location required"); 20 | } 21 | 22 | res.statusCode = statusCode; 23 | res.setHeader("Location", location); 24 | res.end(); 25 | } 26 | -------------------------------------------------------------------------------- /bin/fix-volumes: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Please Use Google Shell Style: https://google.github.io/styleguide/shell.xml 4 | 5 | # ---- Start unofficial bash strict mode boilerplate 6 | # http://redsymbol.net/articles/unofficial-bash-strict-mode/ 7 | set -o errexit # always exit on error 8 | set -o errtrace # trap errors in functions as well 9 | set -o pipefail # don't ignore exit codes when piping output 10 | set -o posix # more strict failures in subshells 11 | # set -x # enable debugging 12 | 13 | IFS=$'\n\t' 14 | # ---- End unofficial bash strict mode boilerplate 15 | cd "$(dirname "${BASH_SOURCE[0]}")/.." 16 | docker-compose run --entrypoint=./.reaction/fix-volumes.sh web --force 17 | -------------------------------------------------------------------------------- /bin/package-unlink: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Please Use Google Shell Style: https://google.github.io/styleguide/shell.xml 4 | 5 | # ---- Start unofficial bash strict mode boilerplate 6 | # http://redsymbol.net/articles/unofficial-bash-strict-mode/ 7 | set -o errexit # always exit on error 8 | set -o errtrace # trap errors in functions as well 9 | set -o pipefail # don't ignore exit codes when piping output 10 | set -o posix # more strict failures in subshells 11 | # set -x # enable debugging 12 | 13 | # ---- End unofficial bash strict mode boilerplate 14 | 15 | package_name=$1 16 | 17 | # validate input 18 | IFS='/' 19 | read -a pkgarr <<< "$1" 20 | org_name=${pkgarr[0]} 21 | 22 | #check if the organization name is valid by cross referencing with node_modules 23 | is_package=$(find node_modules -type d -name $org_name) 24 | if [ -z "$is_package" ]; then 25 | echo "$org_name does not exist" 26 | exit 0 27 | fi 28 | 29 | IFS="$(printf "\n\t")" 30 | 31 | # Unlink the yalc dependency, remove the package drectory and npm install 32 | echo "Unlinking package from Example Storefront..." 33 | docker-compose exec web sh -c "cd /usr/local/src/app && yalc remove ${package_name}" 34 | docker-compose exec web sh -c "rm -rf /usr/local/src/app/node_modules/${package_name} && npm i" 35 | docker-compose exec web sh -c "cd /usr/local/src/app && yalc update" 36 | 37 | # Fool nodemon into thinking something has changed so that it restarts. 38 | # Touch first file found in /pages with .js extension 39 | echo "Restarting Example Storefront..." 40 | docker-compose exec web sh -c "touch -c $(ls ./pages/*.js | head -n1)" 41 | -------------------------------------------------------------------------------- /bin/package-update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Please Use Google Shell Style: https://google.github.io/styleguide/shell.xml 4 | 5 | # ---- Start unofficial bash strict mode boilerplate 6 | # http://redsymbol.net/articles/unofficial-bash-strict-mode/ 7 | set -o errexit # always exit on error 8 | set -o errtrace # trap errors in functions as well 9 | set -o pipefail # don't ignore exit codes when piping output 10 | set -o posix # more strict failures in subshells 11 | # set -x # enable debugging 12 | 13 | IFS="$(printf "\n\t")" 14 | # ---- End unofficial bash strict mode boilerplate 15 | 16 | # Unlink the yalc dependency, remove the package drectory and npm install 17 | echo "Updating package from Example Storefront..." 18 | docker-compose exec web sh -c "cd /usr/local/src/app && yalc update" 19 | 20 | # Fool nodemon into thinking something has changed so that it restarts. 21 | # Touch first file found in /src with .js extension 22 | echo "Restarting Example Storefront..." 23 | docker-compose exec web sh -c "touch -c $(ls ./src/*.js | head -n1)" 24 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cd "$(dirname "${BASH_SOURCE[0]}")/.." 4 | env_file=./.env 5 | env_example_file=./.env.example 6 | 7 | function main { 8 | set -e 9 | 10 | add_new_env_vars 11 | } 12 | 13 | function add_new_env_vars { 14 | # create .env and set perms if it does not exist 15 | [ ! -f "${env_file}" ] && { touch "${env_file}" ; chmod 0600 "${env_file}" ; } 16 | 17 | export IFS=$'\n' 18 | for var in $(cat "${env_example_file}"); do 19 | key="${var%%=*}" # get var key 20 | var=$(command echo "$var") # generate dynamic values 21 | 22 | # If .env doesn't contain this env key, add it 23 | if ! $(grep -qLE "^$key=" "${env_file}"); then 24 | echo "Adding $key to .env" 25 | echo "$var" >> "${env_file}" 26 | fi 27 | done 28 | } 29 | 30 | main 31 | -------------------------------------------------------------------------------- /bin/start: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -o errexit # always exit on error 4 | set -o pipefail # don't ignore exit codes when piping output 5 | # set -x # enable debugging 6 | 7 | IFS="$(printf "\n\t")" 8 | 9 | cd "$(dirname "$0")/.." 10 | yarn install 11 | 12 | yarn start:dev -------------------------------------------------------------------------------- /bin/wait-for.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | TIMEOUT=15 4 | QUIET=0 5 | 6 | echoerr() { 7 | if [ "$QUIET" -ne 1 ]; then printf "%s\n" "$*" 1>&2; fi 8 | } 9 | 10 | usage() { 11 | exitcode="$1" 12 | cat << USAGE >&2 13 | Usage: 14 | $cmdname host:port [-t timeout] [-- command args] 15 | -q | --quiet Do not output any status messages 16 | -t TIMEOUT | --timeout=timeout Timeout in seconds, zero for no timeout 17 | -- COMMAND ARGS Execute command with args after the test finishes 18 | USAGE 19 | exit "$exitcode" 20 | } 21 | 22 | wait_for() { 23 | for i in `seq $TIMEOUT` ; do 24 | nc -z "$HOST" "$PORT" > /dev/null 2>&1 25 | 26 | result=$? 27 | if [ $result -eq 0 ] ; then 28 | if [ $# -gt 0 ] ; then 29 | exec "$@" 30 | fi 31 | exit 0 32 | fi 33 | sleep 1 34 | done 35 | echo "Operation timed out" >&2 36 | exit 1 37 | } 38 | 39 | while [ $# -gt 0 ] 40 | do 41 | case "$1" in 42 | *:* ) 43 | HOST=$(printf "%s\n" "$1"| cut -d : -f 1) 44 | PORT=$(printf "%s\n" "$1"| cut -d : -f 2) 45 | shift 1 46 | ;; 47 | -q | --quiet) 48 | QUIET=1 49 | shift 1 50 | ;; 51 | -t) 52 | TIMEOUT="$2" 53 | if [ "$TIMEOUT" = "" ]; then break; fi 54 | shift 2 55 | ;; 56 | --timeout=*) 57 | TIMEOUT="${1#*=}" 58 | shift 1 59 | ;; 60 | --) 61 | shift 62 | break 63 | ;; 64 | --help) 65 | usage 0 66 | ;; 67 | *) 68 | echoerr "Unknown argument: $1" 69 | usage 1 70 | ;; 71 | esac 72 | done 73 | 74 | if [ "$HOST" = "" -o "$PORT" = "" ]; then 75 | echoerr "Error: you need to provide a host and port to test." 76 | usage 2 77 | fi 78 | 79 | wait_for "$@" 80 | -------------------------------------------------------------------------------- /components/AccountDropdown/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./AccountDropdown"; 2 | -------------------------------------------------------------------------------- /components/Breadcrumbs/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Breadcrumbs"; 2 | -------------------------------------------------------------------------------- /components/Cart/Cart.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import PropTypes from "prop-types"; 3 | import inject from "hocs/inject"; 4 | import { withStyles } from "@material-ui/core/styles"; 5 | import Drawer from "@material-ui/core/Drawer"; 6 | 7 | const styles = () => ({ 8 | cart: { 9 | width: "90vw" 10 | } 11 | }); 12 | 13 | class Cart extends Component { 14 | static propTypes = { 15 | classes: PropTypes.object, 16 | uiStore: PropTypes.shape({ 17 | closeCart: PropTypes.func 18 | }).isRequired 19 | }; 20 | 21 | static defaultProps = { 22 | classes: {} 23 | }; 24 | 25 | handleClose = () => { 26 | this.props.uiStore.closeCart(); 27 | }; 28 | 29 | render() { 30 | const { classes, uiStore } = this.props; 31 | return ( 32 | 33 |
Cart Component
34 |
35 | ); 36 | } 37 | } 38 | 39 | export default withStyles(styles)(inject("uiStore")(Cart)); 40 | -------------------------------------------------------------------------------- /components/Cart/CartToggle.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import PropTypes from "prop-types"; 3 | import IconButton from "@material-ui/core/IconButton"; 4 | import CartIcon from "mdi-material-ui/Cart"; 5 | 6 | class CartToggle extends Component { 7 | static propTypes = { 8 | onClick: PropTypes.func 9 | }; 10 | 11 | render() { 12 | return ( 13 | 14 | 15 | 16 | ); 17 | } 18 | } 19 | 20 | export default CartToggle; 21 | -------------------------------------------------------------------------------- /components/Cart/index.js: -------------------------------------------------------------------------------- 1 | export { default as Cart } from "./Cart"; 2 | export { default as CartToggle } from "./CartToggle"; 3 | -------------------------------------------------------------------------------- /components/CartItems/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CartItems"; 2 | -------------------------------------------------------------------------------- /components/CartPopover/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CartPopover"; 2 | -------------------------------------------------------------------------------- /components/CatalogGridItem/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CatalogGridItem"; 2 | -------------------------------------------------------------------------------- /components/CheckoutActions/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CheckoutActions"; 2 | -------------------------------------------------------------------------------- /components/CheckoutButtons/CheckoutButtons.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import PropTypes from "prop-types"; 3 | import Button from "@reactioncommerce/components/Button/v1"; 4 | import Router from "translations/i18nRouter"; 5 | 6 | export default class CheckoutButtons extends Component { 7 | static propTypes = { 8 | /** 9 | * Set to `true` to prevent the button from calling `onClick` when clicked 10 | */ 11 | isDisabled: PropTypes.bool, 12 | /** 13 | * The NextJS route name for the primary checkout button. 14 | */ 15 | primaryButtonRoute: PropTypes.string, 16 | /** 17 | * Text to display inside the button 18 | */ 19 | primaryButtonText: PropTypes.string, 20 | /** 21 | * className for primary checkout button 22 | */ 23 | primaryClassName: PropTypes.string 24 | } 25 | 26 | static defaultProps = { 27 | primaryButtonRoute: "/cart/checkout", 28 | primaryButtonText: "Checkout" 29 | }; 30 | 31 | handleOnClick = () => { 32 | const { primaryButtonRoute } = this.props; 33 | Router.push(primaryButtonRoute); 34 | } 35 | 36 | render() { 37 | const { 38 | isDisabled, 39 | primaryClassName, 40 | primaryButtonText 41 | } = this.props; 42 | 43 | return ( 44 | 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /components/CheckoutButtons/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CheckoutButtons"; 2 | -------------------------------------------------------------------------------- /components/CheckoutSummary/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CheckoutSummary"; 2 | -------------------------------------------------------------------------------- /components/Divider/Divider.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import PropTypes from "prop-types"; 3 | import { withStyles } from "@material-ui/core/styles"; 4 | import Typography from "@material-ui/core/Typography"; 5 | 6 | const styles = (theme) => ({ 7 | container: { 8 | display: "flex", 9 | alignItems: "center" 10 | }, 11 | label: { 12 | flex: 0, 13 | flexBasis: "auto", 14 | textTransform: "uppercase", 15 | fontWeight: theme.typography.fontWeightBold, 16 | fontSize: "0.7rem", 17 | paddingRight: theme.spacing(), 18 | paddingLeft: theme.spacing(), 19 | letterSpacing: "0.1rem" 20 | }, 21 | item: { 22 | flex: "1 1 auto", 23 | border: 0, 24 | borderTop: "1px solid", 25 | borderColor: theme.palette.reaction.borderColor, 26 | marginTop: theme.spacing(2), 27 | marginBottom: theme.spacing(2) 28 | } 29 | }); 30 | 31 | /** 32 | * A divider for variant options 33 | * @export 34 | * @class Divider 35 | */ 36 | class Divider extends Component { 37 | static propTypes = { 38 | classes: PropTypes.object.isRequired, 39 | label: PropTypes.string 40 | } 41 | 42 | render() { 43 | const { classes: { container, item, label: labelClass }, label } = this.props; 44 | 45 | return ( 46 |
47 |
48 | {!!label && {label}} 49 |
50 |
51 | ); 52 | } 53 | } 54 | 55 | export default withStyles(styles)(Divider); 56 | -------------------------------------------------------------------------------- /components/Divider/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Divider"; 2 | -------------------------------------------------------------------------------- /components/Entry/EntryModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import { makeStyles } from "@material-ui/core/styles"; 4 | import Modal from "@material-ui/core/Modal"; 5 | import useStores from "hooks/useStores"; 6 | import Login from "../Entry/Login"; 7 | import SignUp from "../Entry/SignUp"; 8 | import ChangePassword from "../Entry/ChangePassword"; 9 | import ForgotPassword from "../Entry/ForgotPassword"; 10 | import ResetPassword from "../Entry/ResetPassword"; 11 | 12 | const useStyles = makeStyles((theme) => ({ 13 | paper: { 14 | position: "absolute", 15 | width: 380, 16 | backgroundColor: theme.palette.background.paper, 17 | border: "2px solid #000", 18 | boxShadow: theme.shadows[5], 19 | padding: theme.spacing(2, 4, 3), 20 | top: "50%", 21 | left: "50%", 22 | transform: "translate(-50%, -50%)", 23 | display: "flex", 24 | alignItems: "center", 25 | justifyContent: "center" 26 | } 27 | })); 28 | 29 | const EntryModal = ({ onClose, resetToken }) => { 30 | const classes = useStyles(); 31 | const { uiStore } = useStores(); 32 | 33 | const { entryModal, setEntryModal } = uiStore; 34 | 35 | const openModal = (value) => { 36 | setEntryModal(value); 37 | }; 38 | 39 | const closeModal = () => { 40 | setEntryModal(null); 41 | onClose(); 42 | }; 43 | 44 | // eslint-disable-next-line react/no-multi-comp 45 | const getModalComponent = () => { 46 | let comp = Login; 47 | if (entryModal === "signup") { 48 | comp = SignUp; 49 | } else if (entryModal === "change-password") { 50 | comp = ChangePassword; 51 | } else if (entryModal === "forgot-password") { 52 | comp = ForgotPassword; 53 | } else if (entryModal === "reset-password") { 54 | comp = ResetPassword; 55 | } 56 | return React.createElement(comp, { closeModal, openModal, resetToken }); 57 | }; 58 | 59 | return ( 60 | 61 |
{getModalComponent()}
62 |
63 | ); 64 | }; 65 | 66 | EntryModal.propTypes = { 67 | onClose: PropTypes.func, 68 | resetToken: PropTypes.string 69 | }; 70 | 71 | EntryModal.defaultProps = { 72 | onClose: () => null 73 | }; 74 | 75 | export default EntryModal; 76 | -------------------------------------------------------------------------------- /components/Entry/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Entry"; 2 | -------------------------------------------------------------------------------- /components/Footer/Footer.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import { withStyles } from "@material-ui/core/styles"; 4 | import Typography from "@material-ui/core/Typography"; 5 | 6 | const date = new Date(); 7 | 8 | const styles = (theme) => ({ 9 | footer: { 10 | alignItems: "center", 11 | display: "flex", 12 | justifyContent: "center", 13 | marginBottom: theme.spacing(2) 14 | } 15 | }); 16 | 17 | const Footer = ({ ...props }) => ( 18 | 23 | ); 24 | 25 | Footer.propTypes = { 26 | classes: PropTypes.object 27 | }; 28 | 29 | export default withStyles(styles, { name: "SkFooter" })(Footer); 30 | -------------------------------------------------------------------------------- /components/Footer/Footer.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, screen } from "@testing-library/react"; 3 | import Footer from "./Footer"; 4 | 5 | test("Renders the footer", () => { 6 | render(