diff --git a/.czrc b/.czrc
new file mode 100644
index 000000000..21edebed7
--- /dev/null
+++ b/.czrc
@@ -0,0 +1,7 @@
+{
+ "path": "cz-conventional-changelog",
+ "disableScopeLowerCase": false,
+ "disableSubjectLowerCase": false,
+ "maxHeaderWidth": 200,
+ "maxLineWidth": 200
+}
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..8f85e5b67
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,17 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.{js,jsx,ts,tsx}]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+trim_trailing_whitespace = true
+insert_final_newline = true
+max_line_length = 150
diff --git a/.eslintignore b/.eslintignore
old mode 100644
new mode 100755
index 5a5053203..dfae701cb
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,2 +1,3 @@
**/node_modules/**/*
-**/lib/*
\ No newline at end of file
+**/lib/*
+packages/composables/nuxt/plugin.js
\ No newline at end of file
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 000000000..340875a7c
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,61 @@
+const { resolve } = require('path');
+const airBnb = require('./eslint/airBnB/extends');
+const eslintExtends = require('./eslint/extends');
+const eslintRules = require('./eslint/rules');
+const jest = require('./eslint/jest/extends');
+const jestRules = require('./eslint/jest/rules');
+const importEsLint = require('./eslint/import/plugin');
+const importEsLintExtends = require('./eslint/import/extends');
+const importEsLintRules = require('./eslint/import/rules');
+const importEsLintSettings = require('./eslint/import/settings');
+const typescript = require('./eslint/typescript/plugin');
+const typescriptExtends = require('./eslint/typescript/extends');
+const typescriptRules = require('./eslint/typescript/rules');
+const vue = require('./eslint/vue/plugin');
+const vueExtends = require('./eslint/vue/extends');
+
+module.exports = {
+ env: {
+ jest: true,
+ node: true,
+ browser: true,
+ es6: true,
+ commonjs: true,
+ },
+ parser: 'vue-eslint-parser',
+ parserOptions: {
+ parser: '@typescript-eslint/parser',
+ project: [
+ resolve(__dirname, './tsconfig.base.json'),
+ resolve(__dirname, './packages/api-client/tsconfig.json'),
+ resolve(__dirname, './packages/composables/tsconfig.json'),
+ resolve(__dirname, './packages/theme/tsconfig.json')
+ ],
+ tsconfigRootDir: __dirname,
+ extraFileExtensions: ['.vue'],
+ ecmaVersion: 2021,
+ sourceType: 'module',
+ },
+ plugins: [
+ ...typescript,
+ ...vue,
+ ...importEsLint,
+ ],
+ extends: [
+ ...eslintExtends,
+ ...airBnb,
+ ...typescriptExtends,
+ ...vueExtends,
+ ...importEsLintExtends,
+ ...jest,
+ ],
+ rules: {
+ ...eslintRules,
+ ...typescriptRules,
+ ...importEsLintRules,
+ ...jestRules,
+ },
+ settings: {
+ ...importEsLintSettings,
+ }
+}
diff --git a/.eslintrc.json b/.eslintrc.json
deleted file mode 100644
index cd081619e..000000000
--- a/.eslintrc.json
+++ /dev/null
@@ -1,137 +0,0 @@
-{
- "root": true,
- "extends": [
- "eslint:recommended",
- "plugin:@typescript-eslint/eslint-recommended",
- "plugin:@typescript-eslint/recommended"
- ],
- "parser": "vue-eslint-parser",
- "parserOptions": {
- "parser": "@typescript-eslint/parser",
- "ecmaVersion": 2017,
- "sourceType": "module"
- },
- "plugins": ["vue", "@typescript-eslint"],
- "env": {
- "browser": true,
- "commonjs": true,
- "node": true,
- "jest": true
- },
- "globals": {
- "Promise": true,
- "process": true,
- "console": true,
- "Set": true,
- "Intl": true
- },
- "rules": {
- "eqeqeq": 2,
- "no-use-before-define": [
- 2,
- {
- "functions": false
- }
- ],
- "no-undef": 2,
- "no-unused-vars": 2,
- "brace-style": 2,
- "no-mixed-spaces-and-tabs": 2,
- "key-spacing": 2,
- "comma-spacing": 2,
- "array-bracket-spacing": 2,
- "space-in-parens": 2,
- "no-trailing-spaces": 2,
- "comma-dangle": 2,
- "comma-style": 2,
- "space-infix-ops": 2,
- "keyword-spacing": 2,
- "space-before-blocks": 2,
- "spaced-comment": 2,
- "no-multiple-empty-lines": [
- 2,
- {
- "max": 1
- }
- ],
- "complexity": 2,
- "max-depth": [
- 2,
- {
- "max": 3
- }
- ],
- "default-case": 0,
- "dot-notation": 2,
- "no-alert": 2,
- "no-empty-function": 0,
- "no-eval": 2,
- "no-extend-native": 2,
- "no-extra-bind": 2,
- "no-implicit-coercion": 2,
- "no-multi-spaces": 2,
- "no-useless-return": 2,
- "no-console": 0,
- "global-require": 1,
- "camelcase": 2,
- "computed-property-spacing": 2,
- "consistent-this": 2,
- "func-call-spacing": 2,
- "func-names": 2,
- "func-name-matching": 2,
- "func-style": [
- 2,
- "declaration",
- {
- "allowArrowFunctions": true
- }
- ],
- "indent": [
- 2,
- 2,
- {
- "SwitchCase": 1
- }
- ],
- "line-comment-position": 2,
- "linebreak-style": 2,
- "lines-around-comment": 2,
- "max-statements-per-line": 2,
- "no-lonely-if": 2,
- "prefer-const": 2,
- "no-mixed-operators": 2,
- "no-multi-assign": 2,
- "no-unneeded-ternary": 2,
- "object-property-newline": [
- 2,
- {
- "allowAllPropertiesOnSameLine": true
- }
- ],
- "operator-linebreak": 2,
- "quote-props": [2, "as-needed"],
- "quotes": [2, "single"],
- "semi": 2,
- "semi-spacing": 2,
- "one-var": [2, "never"],
- "eol-last": 2,
- "newline-after-var": 0,
- "no-var": 2,
- "@typescript-eslint/no-empty-function": 0,
- "no-case-declarations": 0,
- "@typescript-eslint/no-var-requires": 0,
- "@typescript-eslint/no-explicit-any": 0,
- "@typescript-eslint/explicit-function-return-type": 0,
- "@typescript-eslint/no-unused-vars": 2,
- "@typescript-eslint/ban-ts-ignore": 0
- },
- "overrides": [
- {
- "files": "*.ts",
- "rules": {
- "no-undef": "off",
- "no-unused-vars": "off"
- }
- }
- ]
-}
diff --git a/.huskyrc b/.huskyrc
new file mode 100644
index 000000000..9fce2c796
--- /dev/null
+++ b/.huskyrc
@@ -0,0 +1,7 @@
+{
+ "hooks": {
+ "prepare-commit-msg": "npx exec < /dev/tty && git cz --hook || true",
+ "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
+ "pre-commit": "lerna run --parallel precommit"
+ }
+}
diff --git a/.ncurc.json b/.ncurc.json
new file mode 100644
index 000000000..428197364
--- /dev/null
+++ b/.ncurc.json
@@ -0,0 +1,5 @@
+{
+ "reject": [
+ "husky"
+ ]
+}
diff --git a/README.md b/README.md
index 0c9f7efd1..826e1ae43 100644
--- a/README.md
+++ b/README.md
@@ -1,1137 +1,37 @@
-# Vue Storefront Next for Magento 2
+
-* [Introduction](#introduction)
-* [Getting Started](#getting-started)
- - [Installation](#installation)
- - [With Vue Storefront CLI (recommended)](#with-vue-storefront-cli-recommended)
- - [Manual installation](#manual-installation)
-* [API Client](#api-client)
- - [Configuration](#configuration)
- - [Overriding and extending API Client methods](#overriding-and-extending-api-client-methods)
- - [Methods](#methods)
-* [Composables](#composables)
- - [ `useCart()` ](#usecart)
- - [ `useCategory()` ](#usecategory)
- - [ `useCheckout()` ](#usecheckout)
- - [ `useConfig()` ](#useconfig)
- - [ `useLocale()` ](#uselocale)
- - [ `usePage()` ](#usepage)
- - [ `useProduct()` ](#useproduct)
- - [ `useRouter()` ](#userouter)
- - [ `useUser()` ](#useuser)
- - [ `useUserOrders()` ](#useuserorders)
- - [ `useWishlist()` ](#usewishlist)
-* [Getters](#getters)
- - [Cardgetters](#cardgetters)
- - [Categorygetters](#categorygetters)
- - [Checkoutgetters](#checkoutgetters)
- - [Ordergetters](#ordergetters)
- - [Productgetters](#productgetters)
- - [Usergetters](#usergetters)
- - [Wishlistgetters](#wishlistgetters)
+## Vue Storefront 2 integration with Magento (WIP)
-## Introduction
+This project is a Magento 2 integration for Vue Storefront 2.
+This integration is being developed by superheroes from Cyberfuze, [Ecritel](https://www.ecritel.com/) and [Leonex](https://www.leonex.de/) ❤️
-This is a documentation for Vue Storefront integration with Magento 2.
+**We're aiming to release beta version the latest end of April 2021**. Stay tuned!
-This integration is currently a **work in progress** and not ready for production usage.
+## How to start if you want to try out the integration
-## Getting Started
-
-* [Installation](#installation)
- - [With Vue Storefront CLI (recommended)](#with-vue-storefront-cli-recommended)
- - [Manual installation](#manual-installation)
-
----
-
-### Installation
-
-#### With Vue Storefront CLI (recommended)
-
-This is the easiest and fastest way of bootstrapping new Vue Storefront project. With Vue Storefront CLI you can generate preconfigured, working boilerplate shop in one minute!
-
-During the installation process you will be asked questions about:
-
-* eCommerce platform you want to use
-* CMS platform
-* Whether you want or doesn't want to generate a working UI layer based on Storefront UI
-
-Based on provided answers Vue Storefront CLI will generate a project already integrated with the services that you have chosen.
-
-#### Manual installation
-
-First, install the packages:
-
-```shell script
-npm install --save @vue-storefront/magento @vue-storefront/magento-api
-
-# OR
-
-yarn add @vue-storefront/magento @vue-storefront/magento-api
-
-```
-Once packages are installed you need to invoke the `setup method that will configure your Commercetools integration before using any other method from the integration. You can read how to configure it [here](#api-client).
-
-```javascript
-import { setup } from '@vue-storefront/magento-api'
-
-setup({
- // configuration of your eCommerce integration
-})
-```
-
-In the next chapters, you will learn in-depth about Commercetools API Client and Composition API functions.
-
-## API Client
-
-* [Configuration](#configuration)
-* [Overriding and extending API Client methods](#overriding-and-extending-api-client-methods)
-* [Methods](#methods)
-
----
-
-API Client is a data layer of your eCommerce integration. It provides a friendly abstraction layer over network calls to your eCommerce platform.
-
-It expresses each network request as a declarative method like getProduct or getCategory. By having this additional layer we can hide implementation details of how we get the data which gives you freedom of introducing major changes in this layer without influencing other parts of the app.
-
-API Client by itself is a Vanilla JavaScript application and it doesn't require any frontend framework to run. It's usually not used directly in the UI and is responsible only for providing data to Composition Functions.
-
-### Configuration
-
-``` javascript
-import {
- setup
-} from '@vue-storefront/magento-api';
-
-export default async ({
- app
-}) => {
-
- const config = {
- storage: app.$cookies,
- api: {
- uri: process.env.API_URL
- },
- tax: {
- displayCartSubtotalIncludingTax: true
- },
- externalCheckout: {
- enabled: true,
- cmsUrl: 'https://your-magento-instance.tld/vue/cart/sync/',
- stores: {
- default: {
- cmsUrl: 'https://your-magento-instance.tld/vue/cart/sync/'
- }
- }
- },
- websites: {
- base: {
- code: 'base',
- defaultStoreGroup: 'main_website_store',
- storeGroups: {
- // eslint-disable-next-line @typescript-eslint/camelcase,camelcase
- main_website_store: {
- code: 'main_website_store',
- defaultStore: 'default',
- stores: {
- default: {
- code: 'default'
- },
- de: {
- code: 'de'
- },
- fr: {
- code: 'fr'
- }
- }
- }
- }
- }
- },
- defaultStore: 'default'
- };
-
- await setup(config);
-};
-```
-
-`setup` accepts the following config:
-
-``` typescript
-interface ApiClientSettings {
- storage: Storage;
- api?: {
- uri: string;
- };
- tax: {
- displayCartSubtotalIncludingTax: boolean;
- };
- externalCheckout: {
- enabled: boolean;
- cmsUrl: string;
- stores: Record;
- };
- websites: Record;
- defaultStore: string;
-}
-
-type ExternalCheckoutStore = {
- cmsUrl: string;
-}
-
-interface Storage {
- set: (
- name: string,
- value: any,
- ) => void;
- get: (name: string) => any;
- remove: (name: string) => any;
- removeAll: () => void;
-}
-
-type Website = {
- code: string;
- defaultStoreGroup: string;
- storeGroups: Record;
-}
-
-type StoreGroup = {
- code: string;
- defaultStore: string;
- stores: Record;
- website?: Website;
-}
-
-type Store = {
- code: string;
- storeGroup?: StoreGroup;
-}
-```
-
-### Overriding and extending API Client methods
-
-One of the biggest benefits of using headless architecture is the flexibility of combining multiple specialized third-party services in one application. It gives you a freedom of replacing parts of your app that are not meeting your expectations with other ones that do. For example if you don't like your eCommerce default CMS you can easily replace it with Storyblok.
-
-The main purpose of introducing the API Client into Vue Storefront architecture was to make such changes as simple as possible and let people apply them without introducing breaking changes to other parts of the app
-
-For example: If you want to change the way you're getting eCommerce data and switch from REST API to GraphQL or fetch product reviews from external service you can easily do that without introducing breaking changes in the UI layer or composition API functions **as long as your method will keep the same interface**
-
-You can override any of API Client methods with `override function. It accepts an object where you can put functions you want to override.
-
-``` javascript
-import {
- override
-} from '@vue-storefront/{platform}-api'
-
-override({
- async getProduct(params) {
- // new getProduct
- },
-})
-```
-
-### Methods
-
-You can find detailed information about all API Client methods here
-
-## Composables
-
-* [ `useCart()` ](#usecart)
-* [ `useCategory()` ](#usecategory)
-* [ `useCheckout()` ](#usecheckout)
-* [ `useConfig()` ](#useconfig)
-* [ `useLocale()` ](#uselocale)
-* [ `usePage()` ](#usepage)
-* [ `useProduct()` ](#useproduct)
-* [ `useRouter()` ](#userouter)
-* [ `useUser()` ](#useuser)
-* [ `useUserOrders()` ](#useuserorders)
-* [ `useWishlist()` ](#usewishlist)
-
-### useCart
-
-`useCart` composition API function is responsible, as its name suggests, for interactions with the cart in your eCommerce. This function returns following values:
-
-``` typescript
-interface UseCart
-<
- CART,
- CART_ITEM,
- PRODUCT,
- COUPON
-> {
- cart: ComputedProperty;
- addItem: (product: PRODUCT, quantity: number) => Promise;
- isOnCart: (product: PRODUCT) => boolean;
- removeItem: (product: CART_ITEM,) => Promise;
- updateItemQty: (product: CART_ITEM, quantity?: number) => Promise;
- clear: () => Promise;
- coupon: ComputedProperty;
- applyCoupon: (coupon: string) => Promise;
- removeCoupon: () => Promise;
- load: () => Promise;
- loading: ComputedProperty;
-}
-```
-
-* `cart` - Returns the Items in the Cart as a `computed` property
-* `isOnCart` - Function that takes in a `product` and returns `true` or `false`
-* `addItem` - Function that takes in a `product` and its `quantaty` and adds it to the cart
-* `load` - Function that loads the current `cart
-* `removeItem` - Function that takes in a `product` and removes it from the `cart
-* `clear` - Function that clears cart
-* `updateItemQty` - Function that takes in a `product` and its new `quantaty` and updates it accordingly
-* `coupon` - Returns applied coupon codes as a `computed` property
-* `applyCoupon` - Function that takes in a `coupon` and applies it to the cart
-* `removeCoupon` - Function that removes all applied coupons
-* `loading` - Returns `true` / `false` as `computed` property
-
-#### Cart initialization
-
-Cart composable is a service designed for supporting a single cart and access it everywhere with ease. Initialization of a cart requires using `load()` when calling `useCart()` for the first time. Keep in mind that upon execution of `load` , the cart will get loaded only once, if a wishlist has already been loaded, nothing happens.
-
-``` javascript
-import {
- onSSR
-} from '@vue-storefront/core';
-import {
- useCart
-} from '@vue-storefront/magento';
-
-export default {
- setup() {
- const {
- cart,
- isOnCart,
- addItem,
- load,
- removeItem,
- clear,
- updateItemQty,
- coupon,
- applyCoupon,
- removeCoupon,
- loading
- } = useCart();
-
- onSSR(async () => {
- await load();
- });
-
- return {
- cart,
- isOnCart,
- addItem,
- removeItem,
- clear,
- updateItemQty,
- coupon,
- applyCoupon,
- removeCoupon,
- loading,
- };
- }
-};
-```
-
-### useCategory
-
-`useCategory` composition API function is responsible, as its name suggests, for interactions with the categories in your eCommerce. This function returns following values:
-
-``` typescript
-interface UseCategory
-<
- CATEGORY
-> {
- categories: ComputedProperty;
- search: (params: {
- [x: string]: any;
- }) => Promise;
- loading: ComputedProperty;
-}
-```
-
-* `categories` - Returns all `categories` as a `computed` property
-* `search` - Function that takes `categorySearchParams` as input and fill the `categories
-* `loading` - Returns the current state of `search` as `computed` property
-
-``` javascript
-import {
- onSSR
-} from '@vue-storefront/core';
-import {
- useCategory
-} from '@vue-storefront/magento';
-
-export default {
- setup(props) {
- const {
- categories,
- search,
- loading
- } = useCategory();
-
- onSSR(async () => {
- let searchParameters = {};
- if (props.categoryId) {
- searchParameters = {
- ids: {
- eq: props.categoryId
- }
- };
- } else {
- searchParameters = getCategorySearchParameters(context);
- }
- await search(searchParameters);
- });
-
- return {
- categories,
- loading
- };
- }
-};
-```
-
-### useCheckout
-
-`useCheckout` composition API function is responsible, as its name suggests, for interactions with the checkout in your eCommerce. This function returns following values:
-
-> This composable however needs to be impelemented in the `core` and then ported to magento.
-
-``` typescript
-interface UseCheckout
-<
- PAYMENT_METHODS,
- SHIPPING_METHODS,
- PERSONAL_DETAILS,
- SHIPPING_DETAILS,
- SHIPPING_METHOD,
- BILLING_DETAILS,
- CHOOSEN_PAYMENT_METHOD,
- CHOOSEN_SHIPPING_METHOD,
- PLACE_ORDER
-> {
- chosenShippingMethod: Ref;
- shippingDetails: Ref;
- billingDetails: Ref;
- chosenPaymentMethod: Ref;
- placeOrder: (params: any) => Promise;
- loadDetails: (params: any) => Promise;
- paymentMethods: Ref;
- personalDetails: Ref;
- loading: Readonly[>>;
- shippingMethods: Ref
-}
-```
-
-* `chosenShippingMethod` - Returns the `chosenShippingMethod
-* `shippingDetails` - Returns the `shippingDetails`
-* `billingDetails` - Returns the `billingDetails`
-* `chosenPaymentMethod` - Returns the `chosenPaymentMethod`
-* `chosenShippingMethod` - Returns the `chosenShippingMethod`
-* `placeOrder` - Function to `placeOrder`
-* `loadDetails` - Function to `loadDetails`
-* `paymentMethods` - Returns the `paymentMethods`
-* `personalDetails` - Returns the `personalDetails`
-* `loading` - Returns the `loading` state
-* `shippingMethods` - Returns the `shippingMethods`
-
-``` javascript
-import {
- useCheckout
-} from '@vue-storefront/magento';
-
-export default {
- setup() {
- const {
- chosenShippingMethod,
- shippingDetails,
- billingDetails,
- chosenPaymentMethod,
- placeOrder,
- loadDetails,
- paymentMethods,
- personalDetails,
- loading,
- shippingMethods
- } = useCheckout();
-
- return {
- chosenShippingMethod,
- shippingDetails,
- billingDetails,
- chosenPaymentMethod,
- placeOrder,
- loadDetails,
- paymentMethods,
- personalDetails,
- loading,
- shippingMethods
- };
- }
-};
-```
-
-### useConfig
-
-useConfig` composition API function is responsible, as its name suggests, for interactions with the configuration in your eCommerce. This function returns following values:
-
-``` typescript
-interface UseConfig {
- config: ComputedProperty;
- loadConfig: () => Promise;
- loading: ComputedProperty;
-}
-```
-
-* `config` - Returns the loaded `config` as `computed` property
-* `loadConfig` - Function to load the `config`
-* `loading` - Return state of `loadConfig` Function as `computed` property
-
-``` javascript
-import {
- onSSR
-} from '@vue-storefront/core';
-import {
- useConfig
-} from '@vue-storefront/magento';
-
-export default {
- setup() {
- const {
- config,
- loadConfig,
- loading
- } = useConfig();
-
- onSSR(async () => {
- await loadConfig();
- })
-
- return {
- config,
- loading
- };
- }
-};
-```
-
-### useLocale
-
-`useLocale` composition API function is responsible, as its name suggests, for interactions with the locales in your eCommerce. This function returns following values:
-
-``` typescript
-interface UseLocale {
- availableLocales: ComputedProperty;
- availableCountries: ComputedProperty;
- availableCurrencies: ComputedProperty;
- country: ComputedProperty;
- currency: ComputedProperty;
- loadAvailableLocales: () => Promise;
- loadAvailableCountries: () => Promise;
- loadAvailableCurrencies: () => Promise;
- loading: ComputedProperty;
- locale: ComputedProperty;
- setLocale: (locale: AgnosticLocale) => Promise;
- setCountry: (country: AgnosticCountry) => Promise;
- setCurrency: (currency: AgnosticCurrency) => Promise;
-}
-```
-
-* `availableLocales` - Returns all `availableLocales` as `computed` property
-* `availableCountries` - Returns all `availableCountries` as `computed` property
-* `availableCurrencies` - Returns all `availableCurrencies` as `computed` property
-* `country` - Returns all `country` s as `computed` property
-* `currency` - Returns all `currency` s as `computed` property
-* `loadAvailableLocales` - Function that loads available locales
-* `loadAvailableCountries` - Function that loads available countries
-* `loadAvailableCurrencies` - Function that loads available currencies
-* `loading` - Returns the `loading` state as `computed` property
-* `setLocale` - Function that takes a `locale` as param and sets the `locale` property
-* `setCountry` - Function that takes a `country` as param and sets the `country` property
-* `setCurrency` - Function that takes a `currency` as param and sets the `currency` property
-
-``` javascript
-import {
- useLocale
-} from '@vue-storefront/magento';
-export default {
- setup(props, context) {
- const {
- $router,
- $route
- } = context.root;
- const {
- locale,
- ...fields
- } = useLocale();
- const setCookie = context.root.$i18n.setLocaleCookie;
- const isLangModalOpen = ref(false);
-
- const handleChangeLang = ({
- name
- }) => {
- if (name === locale.value) {
- isLangModalOpen.value = false;
- return;
- }
- locale.value = name;
- setCookie(name);
- $router.go({
- path: $route.fullPath,
- force: true
- });
- };
-
- return {
- handleChangeLang,
- locale,
- isLangModalOpen,
- ...fields
- };
- }
-}
-```
-
-### usePage
-
-`usePage` composition API function is responsible, as its name suggests, for interactions with the pages in your eCommerce. This function returns following values:
-
-``` typescript
-interface UsePage {
- loadPage: (identifier: string) => Promise;
- page: Readonly][>>;
- loading: Readonly][>>;
-}
-```
-
-* `loadPage` - Function that takes an `identifier` and loads the `page`
-* `page` - Returns the `page` data
-* `loading` - indicates the State of `loadPage`
-
-``` javascript
-import {
- usePage
-} from '@vue-storefront/magento';
-import {
- onSSR
-} from '@vue-storefront/core';
-
-export default {
- setup(props) {
- const {
- page,
- loadPage,
- loading
- } = usePage('cmsPage');
-
- onSSR(async () => {
- await loadPage(props.identifier);
- });
-
- return {
- page,
- loading
- };
- }
-}
-```
-
-### useProduct
-
-`useProduct` composition API function is responsible, as its name suggests, for interactions with the products in your eCommerce. This function returns following values:
-
-``` typescript
-interface UseProduct {
- products: ComputedProperty;
- totalProducts: ComputedProperty;
- availableFilters: ComputedProperty;
- search: (params: {
- perPage?: number;
- page?: number;
- sort?: any;
- term?: any;
- filters?: PRODUCT_FILTERS;
- [x: string]: any;
- }) => Promise;
- availableSortingOptions: ComputedProperty;
- loading: ComputedProperty;
- [x: string]: any;
-}
-```
-
-* `products` - Returns `products` as a `computed` property
-* `totalProducts` - Returns the number of `totalProducts` as a `computed` property
-* `availableFilters` - Returns all `availableFilters` as a `computed` property
-* `search` - Function that takes in `perPage` , `page` , `sort` , `term` , `filters` and an `array of String` as optional
-
- params and gets the `products` accordingly
-
-* `availableSortingOptions` - Return all `availableSortingOptions` as a `computed` property
-* `loading` - Retruns the `loading` state of `search`
-
-``` javascript
-import {
- useProduct
-} from '@vue-storefront/magento';
-import {
- onSSR
-} from '@vue-storefront/core';
-
-export default {
- setup(props) {
- const path = context.root.$route.path;
- let urlKey = path.split('/').pop();
- const {
- loading: productLoading,
- products,
- search
- } = useProduct('products__' + urlKey);
- onSSR(async () => {
- urlKey = urlKey.replace(config.value.product_url_suffix, '');
- // eslint-disable-next-line @typescript-eslint/camelcase,camelcase
- await search({
- filter: {
- url_key: {
- eq: urlKey
- }
- },
- queryType: 'DETAIL'
- });
- });
-
- return {
- productLoading,
- products
- }
- }
-}
-```
-
-### useRouter
-
-`useRouter` composition API function is responsible, as its name suggests, for interactions with the routes in your eCommerce. This function returns following values:
-
-``` typescript
-interface UseRouter {
- search: (url: string) => Promise;
- route: Readonly][>>;
- loading: Readonly][>>;
-}
-```
-
-* `search` - Function that takes in a `url` and fills the `route` property
-* `route` - Returns the current Page
-* `loading` - Returns state of `search`
-
-``` javascript
-import {
- onSSR
-} from '@vue-storefront/core';
-import {
- useRouter
-} from '@vue-storefront/magento';
-import {
- computed
-} from '@vue/composition-api';
-
-export default {
- setup(props, context) {
- const {
- path
- } = context.root.$route;
- const {
- loading,
- search,
- route
- } = useRouter('router:' + path);
- onSSR(async () => {
- await search(path);
- if (route.value.data.urlResolver === null) {
- context.root.$nuxt.error({
- statusCode: 404,
- message: 'Page not found'
- });
- }
- });
-
- const routeType = computed(() => {
- if (loading.value || route.value.data.urlResolver === null) {
- return {};
- }
- return route.value.data.urlResolver;
- });
-
- return {
- loading,
- routeType
- };
- }
-};
-```
-
-### useUser
-
-`useUser` composition API function is responsible, as its name suggests, for interactions with the user in your
- eCommerce. This function returns following values:
-
-
-``` typescript
-interface UseUser
-<
- USER,
- UPDATE_USER_PARAMS
-> {
- user: ComputedProperty;
- updateUser: (params: UPDATE_USER_PARAMS) => Promise;
- register: (user: {
- email: string;
- password: string;
- firstName?: string;
- lastName?: string;
- [x: string]: any;
- }) => Promise;
- login: (user: {
- username: string;
- password: string;
- [x: string]: any;
- }) => Promise;
- logout: () => Promise;
- changePassword: (
- currentPassword: string,
- newPassword: string) => Promise;
- refreshUser: () => Promise;
- isAuthenticated: Ref;
- loading: ComputedProperty;
-}
-```
-
-* `user` - Returns the current `User` as `computed` property
-* `updateUser` - Function that takes the `currentUser` and the data to be updated as params and updates the `user
-
-` property
-
-* `register` - Function that takes in an `email` , `password` as params and `firstName` , `lastName` as optional params
-
- and registers a new `User` and sets the `user`
-* `login` - Function that takes in the `username` and `password` of a `User` and sets `user`
-* `logout` - Function that logs out the current `user`
-* `changePassword` - Function that takes in the `currentPassword` and `newPassword` and updates the `user`
-* `refreshUser` - Function to refresh `user`
-* `isAuthenticated` - Return `boolean` if `user` exists
-* `loading` - Returns the current state of the above functions as `computed` property
-
-``` javascript
-import {
- useUser
-} from '@vue-storefront/magento';
-
-export default {
- setup(props, {
- root
- }) {
- const {
- isAuthenticated
- } = useUser();
- const onAccountClicked = () => {
- isAuthenticated && isAuthenticated.value ? root.$router.push('/my-account') : toggleLoginModal();
- };
-
- return {
- onAccountClicked
- }
- }
-}
-```
-
-### useUserOrders
-
-`useUserOrders` composition API function is responsible, as its name suggests, for interactions with the orders in your
- eCommerce. This function returns following values:
-
-``` typescript
-interface UseUserOrders {
- orders: ComputedProperty;
- totalOrders: ComputedProperty;
- searchOrders: (params?: {
- id?: any;
- page?: number;
- perPage?: number;
- [x: string]: any;
- }) => Promise;
- loading: ComputedProperty;
-}
-```
-
-* `orders` - Returns an `array` of orders for the current User as a `computed` property
-* `totalOrder` - Returns the total `number` of `orders` for the current User
-* `searchOrders` - Function that takes an `id` , `page` and `perPage` as optional values and sets the `orders array`
-* `loading` - Returns the current state of the `searchOrders` function
-
-``` javascript
-import {
- onSSR
-} from '@vue-storefront/core';
-import {
- useUserOrders
-} from '@vue-storefront/magento';
-import {
- computed
-} from '@vue/composition-api';
-
-export default {
- setup() {
- const {
- orders,
- searchOrders
- } = useUserOrders();
-
- onSSR(async () => {
- await searchOrders();
- });
-
- return {
- orders: computed(() => orders ? orders.value : [])
- }
- }
-}
-```
-
-### useWishlist
-
-`useWishlist` composition API function is responsible, as its name suggests, for interactions with the user in your
- eCommerce. This function returns following values:
-
-
-``` typescript
- interface UseWishlist
-<
- WISHLIST,
- WISHLIST_ITEM,
- PRODUCT,
-> {
- wishlist: ComputedProperty;
- addItem: (product: PRODUCT) => Promise;
- isOnWishlist: (product: PRODUCT) => boolean;
- removeItem: (product: WISHLIST_ITEM,) => Promise;
- clear: () => Promise;
- load: () => Promise;
- loading: ComputedProperty;
-}
-```
-
-* `wishlist` - Returns the current `whishlist` as `computed` property
-* `addItem` - Function that takes in a product and adds it to the `wishlist`
-* `isOnWishlist` - Function that takes in `product` and checks it against the `wishlist` and returns a `boolean`
-* `removeItem` - Function that takes in a `product` of the `wishlist` and removes it
-* `clear` - Function that clears out all `products` from the `wishlist`
-* `load` - Function that loads the `wishlist`
-* `loading` - Indicated the state of the above functions and returns a `boolean` as `computed` property
-
-``` javascript
-import {
- onSSR
-} from '@vue-storefront/core';
-import {
- useWishlist
-} from '@vue-storefront/magento';
-export default {
- setup(props, {
- root
- }) {
- const {
- load
- } = useWishlist();
- onSSR(async () => {
- await load();
- });
- }
-}
-```
-
-## Getters
-
-* [Cardgetters](#cardgetters)
-* [Categorygetters](#categorygetters)
-* [Checkoutgetters](#checkoutgetters)
-* [Ordergetters](#ordergetters)
-* [Productgetters](#productgetters)
-* [Usergetters](#usergetters)
-* [Wishlistgetters](#wishlistgetters)
-
-### Cardgetters
-
-``` typescript
-interface CartGetters {
- getItems: (cart: CART) => CART_ITEM[];
- getItemName: (cartItem: CART_ITEM) => string;
- getItemImage: (cartItem: CART_ITEM) => string;
- getItemPrice: (cartItem: CART_ITEM) => AgnosticPrice;
- getItemQty: (cartItem: CART_ITEM) => number;
- getItemAttributes: (cartItem: CART_ITEM, filters?: Array) => Record;
- getItemSku: (cartItem: CART_ITEM) => string;
- getTotals: (cart: CART) => AgnosticTotals;
- getShippingPrice: (cart: CART) => number;
- getTotalItems: (cart: CART) => number;
- getFormattedPrice: (price: number) => string;
- [getterName: string]: (element: any, options?: any) => unknown;
-}
```
-
-* `getItems` - Function that takes in a `cart` and returns an `array` of `CartItems`
-* `getItemName` - Function that takes in a `cartItem` and returns its `name`
-* `getItemPrice` - Function that takes in a `cartItem` and returns its `regular price` and optional its `special price`
-* `getItemQty` - Function that takes in a `cartItem` and returns its `quantity`
-* `getItemAttributes` - Function that takes in a `cartItem` and optional `filters` and returns the attribute as `value`
- and `label`, optional the `name`
-* `getItemSku` - Function that takes in a `cartItem` and returns its `sku`
-* `getTotals` - Function that takes in a `cart` and returns the `total` and `subtotal
-* `getShippingPrice` - Function that takes in a `cart` and retruns the total `shippingprice`
-* `getTotalItems` - Function that takes in a `cart` and returns the amount of `cartItems`
-* `getFormattedPrice` - Function that takes in `price` and returns it as a formatted `string`
-
-### Categorygetters
-
-``` typescript
-interface CategoryGetters {
- getTree: (category: CATEGORY) => AgnosticCategoryTree | null;
- getBreadcrumbs?: (category: CATEGORY) => AgnosticBreadcrumb[];
- [getterName: string]: any;
-}
+yarn global add @vue-storefront/cli
```
-
-* `getTree` - Function that takes in a `Category` and returns a `label`, an optional `slug` and an `array` of `items
-` that contains the same information and is recursive
-* `getBreadcrumbs` - Function that takes in a Category and returns an `array` of `text` and `links`
-
-### Checkoutgetters
-
-``` typescript
-interface CheckoutGetters {
- getShippingMethodId: (shippingMethod: SHIPPING_METHOD) => string;
- getShippingMethodName: (shippingMethod: SHIPPING_METHOD) => string;
- getShippingMethodDescription: (shippingMethod: SHIPPING_METHOD) => string;
- getShippingMethodPrice: (shippingMethod: SHIPPING_METHOD) => number;
- getFormattedPrice: (price: number) => string;
- [getterName: string]: (element: any, options?: any) => unknown;
-}
```
-
-* `getShippingMethodId` - Function that takes in a `shippingMethod` and returns its `id`
-* `getShippingMethodName` - Function that takes in a `shippingMethod` and returns its `name`
-* `getShippingMethodDescription` - Function that takes in a `shippingMethod` and returns its `description`
-* `getShippingMethdPrice` - Function that takes in a `shippingMethod` and returns the `price` to use it
-* `getFormattedPrice` - Function that takes in a `price` and returns it formatted.
-
-### Ordergetters
-
-``` typescript
-interface UserOrderGetters {
- getDate: (order: ORDER) => string;
- getId: (order: ORDER) => string;
- getStatus: (order: ORDER) => string;
- getPrice: (order: ORDER) => number;
- getItems: (order: ORDER) => ORDER_ITEM[];
- getItemSku: (item: ORDER_ITEM) => string;
- getItemName: (item: ORDER_ITEM) => string;
- getItemQty: (item: ORDER_ITEM) => number;
- getFormattedPrice: (price: number) => string;
- [getterName: string]: (element: any, options?: any) => unknown;
-}
-```
-
-* `getDate` - Function that takes in an `order` and returns the `date` it has been issued
-* `getId` - Function that takes in an `order` and returns its `id`
-* `getStatus` - Function that takes in an `order` and returns its current `status`
-* `getPrice` - Function that takes in an `order` and returns its `total price`
-* `getItems` - Function that takes in an `order` and returns an `array` of `items` in the `order`
-* `getItemSku` - Function that takes in an `orderItem` and returns its `sku`
-* `getItemName` - Function that takes in an `orderItem` and returns its `name`
-* `getItemQty` - Function that takes in an `orderItem` and returns its `quantity` in the `order`
-* `getFormattedPrice` - Function that takes in a `price` and returns it formatted
-
-### Productgetters
-
-``` typescript
-interface ProductGetters {
- getName: (product: PRODUCT) => string;
- getSlug: (product: PRODUCT) => string;
- getPrice: (product: PRODUCT) => AgnosticPrice;
- getGallery: (product: PRODUCT) => AgnosticMediaGalleryItem[];
- getCoverImage: (product: PRODUCT) => string;
- getFiltered: (products: PRODUCT[], filters?: PRODUCT_FILTER) =>
- PRODUCT[];
- getAttributes: (products: PRODUCT[] | PRODUCT, filters?: Array) => Record;
- getDescription: (product: PRODUCT) => string;
- getCategoryIds: (product: PRODUCT) => string[];
- getId: (product: PRODUCT) => string;
- getFormattedPrice: (price: number) => string;
- getBreadcrumbs?: (product: PRODUCT) => AgnosticBreadcrumb[];
- [getterName: string]: any;
-}
+vsf init && cd && yarn && yarn dev
```
-* `getName` - Function that takes in a `product` and returns its `name`
-* `getSlug` - Function that takes in a `product` and returns its `slug`
-* `getPrice` - Function that takes in a `product` and returns a `regular price` and `special price`
-* `getGallery` - Function that takes in a `product` and returns an `array` of `small`, `normal` and `big` image paths
-* `getCoverImage` - Function that takes in a `product` and returns the `path` to its `coverImage`
-* `getFiltered` - Function that takes in an `array`of `products`, optional `filters` and returns a `productsArray`
-* `getAttributes` - Function that takes in an `array` of `products` or a single `product`, optional an `array` of
- `attributes` as `filters` and returns the `productAttributes`
-* `getDescription` - Function that takes in a `product` and returns its `description`
-* `getCategoryIds`- Function that takes in `product` and returns an `array` with its `categoryIds`
-* `getId` - Function that takes in a `product` and returns its `id`
-* `getFormattedPrice` - Function that takes in the `productPrice` and returns it formatted
-* `getBreadcrumbs` - Function that takes in a `product` and returns its `breadcrumbItems`
-
-### Usergetters
+## How to start if you want to contribute?
-``` typescript
-interface UserGetters {
- getFirstName: (customer: USER) => string;
- getLastName: (customer: USER) => string;
- getFullName: (customer: USER) => string;
- [getterName: string]: (element: any, options?: any) => unknown;
-}
-```
-
-* `getFirstName` - Function that takes in a `user` and returns its `firstname`
-* `getLastName` - Function that takes in a `user` and returns its `lastname`
-* `getFullName` - Function that takes in a `user` and returns the `fullname`
-
-### Wishlistgetters
-
-``` typescript
-interface WishlistGetters {
- getItems: (wishlist: WISHLIST) => WISHLIST_ITEM[];
- getItemName: (wishlistItem: WISHLIST_ITEM) => string;
- getItemImage: (wishlistItem: WISHLIST_ITEM) => string;
- getItemPrice: (wishlistItem: WISHLIST_ITEM) => AgnosticPrice;
- getItemAttributes: (wishlistItem: WISHLIST_ITEM, filters?: Array) => Record;
- getItemSku: (wishlistItem: WISHLIST_ITEM) => string;
- getTotals: (wishlist: WISHLIST) => AgnosticTotals;
- getTotalItems: (wishlist: WISHLIST) => number;
- getFormattedPrice: (price: number) => string;
- [getterName: string]: (element: any, options?: any) => unknown;
-}
-```
+Want to contribute? Ping us on `magento2-vsf2` channel on [our Discord](discord.vuestorefront.io)!
-* `getItems` - Function that takes in a `wishlist` and returns an `array` of `wishlistItems`
-* `getItemName` - Function that takes in a `wishlistItem` and returns its `name`
-* `getItemImage` - Function that takes in a `wishlistItem` and returns its `imagePath`
-* `getItemPrice` - Function that takes in a `wishlistItem` and returns the `regular price` and `special price`
-* `getItemAttributes` - Function that takes in a `wishlistItem` and an optional `array` of filters and returns the
- `attributes`
-* `getItemSku` - Function that takes in a `wishlistItem` and returns its `sku`
-* `getTotals` - Function that takes in a `whishlistItem` and returns the `total` and `suptotal`
-* `getTotalItems` - Function that takes in a `wishlist` and returns the number of `items`
-* `getFormattedPrice` - Function that takes in a `price` and returns it formatted
+1. CLone the repo
+2. Run `yarn` to install dependencies
+3. Build dependencies `yarn build:api-client && yarn build:composables`
+4. Run `yarn dev:theme` to run theme. You can find other commands in `package.json`
+5. If you need HMR on Api Client/Composables run `yarn dev:api-client` or `yarn dev:composables` on a separate terminal window.
-## Setter
+## Resources
-### Cartsetter
+- [Vue Storefront Documentation](https://docs.vuestorefront.io/v2/)
+- [Magento 2 integration Documentation (WIP)](https://docs.vuestorefront.io/magento)
+- [Community Chat](https://discord.vuestorefront.io)
-`setCart` - Function that takes in new `cart` and sets it as default.
+## Support
+If you have any questions about this integration we will be happy to answer them on `magento2-vsf2` channel on [our Discord](discord.vuestorefront.io).
diff --git a/commitlint.config.js b/commitlint.config.js
new file mode 100644
index 000000000..28fe5c5bf
--- /dev/null
+++ b/commitlint.config.js
@@ -0,0 +1 @@
+module.exports = {extends: ['@commitlint/config-conventional']}
diff --git a/eslint/airBnB/extends.js b/eslint/airBnB/extends.js
new file mode 100644
index 000000000..08f12a0e6
--- /dev/null
+++ b/eslint/airBnB/extends.js
@@ -0,0 +1,4 @@
+module.exports = [
+ 'airbnb-base',
+ 'airbnb-typescript/base',
+];
diff --git a/eslint/extends.js b/eslint/extends.js
new file mode 100644
index 000000000..be09e6a4c
--- /dev/null
+++ b/eslint/extends.js
@@ -0,0 +1,3 @@
+module.exports = [
+ 'eslint:recommended',
+]
diff --git a/eslint/import/extends.js b/eslint/import/extends.js
new file mode 100644
index 000000000..8567e14f3
--- /dev/null
+++ b/eslint/import/extends.js
@@ -0,0 +1,5 @@
+module.exports = [
+ 'plugin:import/errors',
+ 'plugin:import/warnings',
+ 'plugin:import/typescript',
+];
diff --git a/eslint/import/plugin.js b/eslint/import/plugin.js
new file mode 100644
index 000000000..6abd08078
--- /dev/null
+++ b/eslint/import/plugin.js
@@ -0,0 +1,3 @@
+module.exports = [
+ 'import',
+];
diff --git a/eslint/import/rules.js b/eslint/import/rules.js
new file mode 100644
index 000000000..89a041281
--- /dev/null
+++ b/eslint/import/rules.js
@@ -0,0 +1,16 @@
+module.exports = {
+ 'import/no-extraneous-dependencies': ['error', { devDependencies: true }],
+ 'import/no-unresolved': 'off',
+ 'import/named': 'off',
+ 'import/export': 'off',
+ 'import/no-cycle': 'off',
+ 'import/prefer-default-export': 'off', // Allow single Named-export
+ 'import/no-extraneous-dependencies': 'off',
+ 'import/extensions': ['error', 'always', {
+ js: 'never',
+ mjs: 'never',
+ jsx: 'never',
+ ts: 'never',
+ tsx: 'never',
+ }],
+};
diff --git a/eslint/import/settings.js b/eslint/import/settings.js
new file mode 100644
index 000000000..b017cdd08
--- /dev/null
+++ b/eslint/import/settings.js
@@ -0,0 +1,16 @@
+module.exports = {
+ 'import/extensions': [
+ '.js',
+ '.jsx',
+ '.mjs',
+ '.ts',
+ '.tsx',
+ '.vue',
+ ],
+ 'import/resolver': {
+ node: {
+ extensions: ['.js', '.jsx', '.ts', '.tsx', '.vue'],
+ },
+ typescript: {},
+ },
+}
diff --git a/eslint/jest/extends.js b/eslint/jest/extends.js
new file mode 100644
index 000000000..2c761d028
--- /dev/null
+++ b/eslint/jest/extends.js
@@ -0,0 +1,3 @@
+module.exports = [
+ 'plugin:jest/recommended',
+]
diff --git a/eslint/jest/plugin.js b/eslint/jest/plugin.js
new file mode 100644
index 000000000..b58603375
--- /dev/null
+++ b/eslint/jest/plugin.js
@@ -0,0 +1,3 @@
+module.exports = [
+ 'jest',
+];
diff --git a/eslint/jest/rules.js b/eslint/jest/rules.js
new file mode 100644
index 000000000..5382e236c
--- /dev/null
+++ b/eslint/jest/rules.js
@@ -0,0 +1,8 @@
+module.exports = {
+ 'jest/no-focused-tests': 'error',
+ 'jest/no-identical-title': 'error',
+ 'jest/valid-expect': 'error',
+ 'jest/no-disabled-tests': 'warn',
+ 'jest/prefer-to-have-length': 'warn',
+};
+
diff --git a/eslint/rules.js b/eslint/rules.js
new file mode 100644
index 000000000..0f521f9aa
--- /dev/null
+++ b/eslint/rules.js
@@ -0,0 +1,33 @@
+module.exports = {
+ 'no-console': (process.env.NODE_ENV === 'production') ? 'error' : 'off',
+ 'no-debugger': (process.env.NODE_ENV === 'production') ? 'error' : 'off',
+ 'prefer-promise-reject-errors': 'off',
+ 'no-shadow': 'off',
+ 'no-redeclare': 'off',
+ 'no-unused-vars': 'off',
+ 'no-case-declarations': 0,
+ quotes: ['warn', 'single'],
+ 'no-unused-expressions': ['warn', {
+ 'allowShortCircuit': true,
+ 'allowTernary': true,
+ }],
+ 'max-len': ['error', {
+ code: 150,
+ ignoreComments: true,
+ ignoreUrls: true,
+ ignoreTemplateLiterals: true,
+ ignoreRegExpLiterals: true,
+ ignoreStrings: true,
+ }],
+ 'class-methods-use-this': 'off',
+ 'no-new': 0,
+ 'no-param-reassign': ['error', {
+ props: true,
+ ignorePropertyModificationsFor: [
+ 'state', // for vuex state
+ 'acc', // for reduce accumulators
+ 'e', // for e.returnvalue
+ 'options', // for decorators
+ ],
+ }],
+};
diff --git a/eslint/typescript/extends.js b/eslint/typescript/extends.js
new file mode 100644
index 000000000..1a19879f0
--- /dev/null
+++ b/eslint/typescript/extends.js
@@ -0,0 +1,3 @@
+module.exports = [
+ 'plugin:@typescript-eslint/recommended-requiring-type-checking',
+]
diff --git a/eslint/typescript/plugin.js b/eslint/typescript/plugin.js
new file mode 100644
index 000000000..ad6d684ef
--- /dev/null
+++ b/eslint/typescript/plugin.js
@@ -0,0 +1,3 @@
+module.exports = [
+ '@typescript-eslint',
+];
diff --git a/eslint/typescript/rules.js b/eslint/typescript/rules.js
new file mode 100644
index 000000000..448431485
--- /dev/null
+++ b/eslint/typescript/rules.js
@@ -0,0 +1,33 @@
+module.exports = {
+ '@typescript-eslint/no-shadow': ['error'],
+ '@typescript-eslint/no-redeclare': ['error'],
+ '@typescript-eslint/indent': ['warn', 2],
+ '@typescript-eslint/explicit-function-return-type': 'off',
+ '@typescript-eslint/no-unsafe-member-access': 'off',
+ '@typescript-eslint/no-unsafe-call': 'off',
+ '@typescript-eslint/no-unsafe-assignment': 'off',
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
+ '@typescript-eslint/no-unused-vars': ['error', {
+ 'ignoreRestSiblings': true,
+ 'argsIgnorePattern': '^_',
+ 'caughtErrorsIgnorePattern': '^ignore',
+ }],
+ '@typescript-eslint/restrict-template-expressions': 0,
+ '@typescript-eslint/naming-convention': [
+ 'error',
+ {
+ selector: 'variable',
+ format: ['camelCase', 'PascalCase', 'UPPER_CASE'],
+ },
+ {
+ selector: 'function',
+ format: ['camelCase', 'PascalCase'],
+ },
+ {
+ selector: 'typeLike',
+ format: ['PascalCase', 'UPPER_CASE'],
+ },
+ ],
+ '@typescript-eslint/no-unsafe-return': 0,
+ '@typescript-eslint/no-unnecessary-type-assertion': 0,
+}
diff --git a/eslint/unicorn/extends.js b/eslint/unicorn/extends.js
new file mode 100644
index 000000000..6c765078f
--- /dev/null
+++ b/eslint/unicorn/extends.js
@@ -0,0 +1,3 @@
+module.exports = [
+ 'plugin:unicorn/recommended',
+]
diff --git a/eslint/unicorn/plugin.js b/eslint/unicorn/plugin.js
new file mode 100644
index 000000000..5a251ef45
--- /dev/null
+++ b/eslint/unicorn/plugin.js
@@ -0,0 +1,3 @@
+module.exports = [
+ 'unicorn',
+];
diff --git a/eslint/unicorn/rules.js b/eslint/unicorn/rules.js
new file mode 100644
index 000000000..67f741479
--- /dev/null
+++ b/eslint/unicorn/rules.js
@@ -0,0 +1,32 @@
+module.exports = {
+ 'unicorn/no-null': 0,
+ 'unicorn/no-reduce': 0,
+ 'unicorn/no-array-for-each': 0,
+ 'unicorn/prevent-abbreviations': 0,
+ 'unicorn/no-object-as-default-parameter': 0,
+ 'unicorn/no-abusive-eslint-disable': 0,
+ 'unicorn/consistent-function-scoping': 0,
+ 'unicorn/consistent-destructuring': 0,
+ 'unicorn/filename-case': [
+ 0,
+ {
+ cases: {
+ kebabCase: false,
+ pascalCase: true,
+ camelCase: true
+ },
+ // ignore UPPER_CASE markdown filenames
+ ignore: [/^[A-Z](([\dA-Z]+_)*[\dA-Z]+)?\.(mdx|d\.ts)?$/]
+ }
+ ],
+ 'unicorn/no-new-array': 'off',
+ 'unicorn/no-array-reduce': 'off',
+ 'unicorn/prefer-array-some': 'off',
+ 'unicorn/catch-error-name': [
+ 2,
+ {
+ name: 'error',
+ ignore: ['^e(rr)?$'],
+ },
+ ],
+};
diff --git a/eslint/vue/extends.js b/eslint/vue/extends.js
new file mode 100644
index 000000000..56f101ee3
--- /dev/null
+++ b/eslint/vue/extends.js
@@ -0,0 +1,6 @@
+module.exports = [
+ 'plugin:vue/base',
+ 'plugin:vue/essential',
+ 'plugin:vue/recommended',
+ 'plugin:vue/strongly-recommended',
+]
diff --git a/eslint/vue/plugin.js b/eslint/vue/plugin.js
new file mode 100644
index 000000000..841e71fba
--- /dev/null
+++ b/eslint/vue/plugin.js
@@ -0,0 +1,3 @@
+module.exports = [
+ 'vue',
+];
diff --git a/jest.base.config.js b/jest.base.config.js
index d246220ff..7c70a6d19 100644
--- a/jest.base.config.js
+++ b/jest.base.config.js
@@ -1,13 +1,58 @@
// For a detailed explanation regarding each configuration property, visit:
// https://jestjs.io/docs/en/configuration.html
-
+'use strict'
module.exports = {
+ globals: {
+ __DEV__: true,
+ },
+ // noStackTrace: true,
+ // bail: true,
+ // cache: false,
+ // verbose: true,
+ // watch: true,
+ coverageReporters: ['lcov'],
+
+ coverageThreshold: {
+ global: {
+ // branches: 50,
+ // functions: 50,
+ // lines: 50,
+ // statements: 50
+ },
+ },
+
+ coveragePathIgnorePatterns: ['/node_modules/', '.d.ts$', '/__mocks__/'],
+
+ testEnvironment: 'jest-environment-jsdom-sixteen',
+
transform: {
'^.+\\.(ts)$': 'ts-jest'
},
+
coverageDirectory: './coverage/',
+
collectCoverageFrom: [
'src/**/*.ts'
],
- collectCoverage: true
+
+ setupFiles: [
+ 'jest-date-mock',
+ 'jest-localstorage-mock'
+ ],
+
+ transformIgnorePatterns: [
+ 'node_modules',
+ '/node_modules',
+ ],
+
+ testMatch: ['/**/__tests__/**/*spec.[jt]s?(x)'],
+
+ watchPlugins: [
+ 'jest-watch-typeahead/filename',
+ 'jest-watch-typeahead/testname',
+ ['jest-watch-toggle-config', { setting: 'verbose' }],
+ ['jest-watch-toggle-config', { setting: 'collectCoverage' }],
+ ['jest-watch-toggle-config', { setting: 'notify' }],
+ ['jest-watch-toggle-config', { setting: 'bail' }],
+ ],
};
diff --git a/lerna.json b/lerna.json
new file mode 100644
index 000000000..deb185c20
--- /dev/null
+++ b/lerna.json
@@ -0,0 +1,5 @@
+{
+ "useWorkspaces": true,
+ "npmClient": "yarn",
+ "version": "independent"
+}
diff --git a/package.json b/package.json
new file mode 100644
index 000000000..74eb3d129
--- /dev/null
+++ b/package.json
@@ -0,0 +1,86 @@
+{
+ "name": "vue-storefront",
+ "private": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.x"
+ },
+ "scripts": {
+ "build": "yarn build:api-client && yarn build:composables && yarn build:theme",
+ "build:api-client": "cd packages/api-client && yarn build",
+ "build:composables": "cd packages/composables && yarn build",
+ "build:theme": "cd packages/theme && yarn build",
+ "dev": "concurrently \"yarn:dev:*\"",
+ "dev:api-client": "cd packages/api-client && yarn dev",
+ "dev:composables": "cd packages/composables && yarn dev",
+ "dev:theme": "cd packages/theme && yarn dev",
+ "test": "yarn test:api-client && yarn test:composables && yarn test:theme",
+ "test:api-client": "cd packages/api-client && yarn test --passWithNoTests",
+ "test:composables": "cd packages/composables && yarn test --passWithNoTests",
+ "test:theme": "cd packages/theme && yarn test --passWithNoTests",
+ "start": "cd packages/theme && yarn start",
+ "lint": "eslint . --ext .ts,.vue --fix"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.13.14",
+ "@commitlint/cli": "^12.1.1",
+ "@commitlint/config-conventional": "^12.1.1",
+ "@commitlint/config-lerna-scopes": "^12.1.1",
+ "@manypkg/cli": "^0.17.0",
+ "@types/graphql": "^14.5.0",
+ "@types/jest": "^26.0.22",
+ "@types/jsdom": "^16.2.10",
+ "@types/node": "^14.14.37",
+ "@types/webpack": "^5.28.0",
+ "@typescript-eslint/eslint-plugin": "^4.21.0",
+ "@typescript-eslint/parser": "^4.21.0",
+ "@vue/eslint-config-typescript": "^7.0.0",
+ "babel-eslint": "^10.1.0",
+ "concurrently": "^6.0.1",
+ "ejs": "^3.1.6",
+ "eslint": "7.23.0",
+ "eslint-config-airbnb-base": "^14.2.1",
+ "eslint-config-airbnb-typescript": "^12.3.1",
+ "eslint-config-prettier": "^8.1.0",
+ "eslint-config-standard": "^16.0.2",
+ "eslint-import-resolver-typescript": "^2.4.0",
+ "eslint-plugin-cypress": "^2.11.2",
+ "eslint-plugin-import": "^2.22.1",
+ "eslint-plugin-jest": "^24.3.4",
+ "eslint-plugin-node": "^11.1.0",
+ "eslint-plugin-standard": "^4.1.0",
+ "eslint-plugin-unicorn": "29.0.0",
+ "eslint-plugin-vue": "^7.8.0",
+ "husky": "^4.3.8",
+ "jest": "^26.6.3",
+ "jest-date-mock": "^1.0.8",
+ "jest-environment-jsdom-sixteen": "^1.0.3",
+ "jest-junit": "^12.0.0",
+ "jest-localstorage-mock": "^2.4.9",
+ "jest-silent-reporter": "^0.5.0",
+ "jest-transform-stub": "^2.0.0",
+ "jest-watch-lerna-packages": "^1.1.0",
+ "jest-watch-toggle-config": "^2.0.1",
+ "jest-watch-typeahead": "^0.6.2",
+ "jest-watch-yarn-workspaces": "^1.1.0",
+ "lerna": "^4.0.0",
+ "lint-staged": "^10.5.4",
+ "majestic": "^1.8.0",
+ "npm-check-updates": "^11.3.0",
+ "rimraf": "^3.0.2",
+ "rollup": "^2.44.0",
+ "rollup-plugin-terser": "^7.0.2",
+ "rollup-plugin-typescript2": "^0.30.0",
+ "ts-jest": "^26.5.4",
+ "ts-node": "^9.1.1",
+ "tslib": "^2.2.0",
+ "typescript": "^4.2.4",
+ "vue-eslint-parser": "^7.6.0"
+ },
+ "workspaces": [
+ "packages/*"
+ ],
+ "lint-staged": {
+ "*.{js,ts,vue}": "eslint --fix"
+ }
+}
diff --git a/packages/api-client/.editorconfig b/packages/api-client/.editorconfig
index 5d1263484..7ac5caaf7 100644
--- a/packages/api-client/.editorconfig
+++ b/packages/api-client/.editorconfig
@@ -1,13 +1,15 @@
-# editorconfig.org
-root = true
-
[*]
+charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
-charset = utf-8
-trim_trailing_whitespace = true
insert_final_newline = true
+trim_trailing_whitespace = true
-[*.md]
-trim_trailing_whitespace = false
+[*.{js,jsx,ts,tsx}]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+trim_trailing_whitespace = true
+insert_final_newline = true
+max_line_length = 150
diff --git a/packages/api-client/.gitignore b/packages/api-client/.gitignore
index 7c4bc8070..77e5a4f9f 100644
--- a/packages/api-client/.gitignore
+++ b/packages/api-client/.gitignore
@@ -1,3 +1,4 @@
lib
node_modules
coverage
+server
diff --git a/packages/api-client/.graphqlconfig.example b/packages/api-client/.graphqlconfig.example
new file mode 100644
index 000000000..5fe0c4917
--- /dev/null
+++ b/packages/api-client/.graphqlconfig.example
@@ -0,0 +1,12 @@
+{
+ "name": "Magento",
+ "schemaPath": "./schema.graphql",
+ "extensions": {
+ "endpoints": {
+ "API": {
+ "url": "https://vsf-m2.site-builder.app/graphql",
+ "introspect": true
+ }
+ }
+ }
+}
diff --git a/packages/api-client/.lintstagedrc b/packages/api-client/.lintstagedrc
new file mode 100644
index 000000000..d95e0101c
--- /dev/null
+++ b/packages/api-client/.lintstagedrc
@@ -0,0 +1,5 @@
+{
+ "*.{js,jsx,ts,tsx,vue}": [
+ "eslint --fix"
+ ],
+}
diff --git a/packages/api-client/codegen.yml b/packages/api-client/codegen.yml
new file mode 100644
index 000000000..8268fbf91
--- /dev/null
+++ b/packages/api-client/codegen.yml
@@ -0,0 +1,12 @@
+overwrite: true
+schema: "schema.graphql"
+documents: "src/**/*.graphql"
+generates:
+ src/types/GraphQL.ts:
+ plugins:
+ - "typescript"
+ - "typescript-operations"
+ config:
+ skipTypename: true
+ namingConvention:
+ default: change-case-all#pascalCase
diff --git a/packages/api-client/package.json b/packages/api-client/package.json
index ea2dbf27e..28e677a67 100644
--- a/packages/api-client/package.json
+++ b/packages/api-client/package.json
@@ -1,33 +1,51 @@
{
"name": "@vue-storefront/magento-api",
- "version": "0.0.1",
+ "version": "1.0.0-beta.1",
"sideEffects": false,
+ "server": "server/index.js",
"main": "lib/index.cjs.js",
"module": "lib/index.es.js",
"types": "lib/index.d.ts",
"scripts": {
- "build": "rm -rf lib && rollup -c",
- "dev": "rollup -c -w",
- "test": "cross-env APP_ENV=test jest --rootDir ."
+ "build": "rimraf lib server && rollup -c",
+ "dev": "rimraf lib server && rollup -c -w",
+ "graphql:codegen": "graphql-codegen --config codegen.yml",
+ "lint": "eslint ./src --ext .ts,.vue",
+ "lint:fix": "eslint ./src --ext .ts --fix",
+ "prepublish": "yarn build",
+ "test": "jest",
+ "update:check": "ncu",
+ "update:update": "ncu -u"
},
"dependencies": {
- "@vue-storefront/core": "^2.2.1",
- "apollo-cache-inmemory": "^1.6.3",
- "apollo-client": "^2.6.4",
- "apollo-link": "^1.2.13",
- "apollo-link-context": "^1.0.19",
- "apollo-link-http": "^1.5.16",
+ "@apollo/client": "^3.3.15",
+ "@vue-storefront/core": "^2.3.0-rc.3",
+ "apollo-cache-inmemory": "^1.6.6",
+ "apollo-client": "^2.6.10",
+ "apollo-link": "^1.2.14",
+ "apollo-link-context": "^1.0.20",
"apollo-link-error": "^1.1.13",
- "graphql": "^14.5.8",
- "graphql-tag": "^2.10.1",
- "isomorphic-fetch": "^2.2.1"
+ "apollo-link-http": "^1.5.17",
+ "apollo-link-retry": "^2.2.16",
+ "graphql": "^15.5.0",
+ "graphql-tag": "^2.11.0",
+ "isomorphic-fetch": "^3.0.0"
},
"devDependencies": {
- "@rollup/plugin-json": "^4.0.3",
- "apollo-link-schema": "^1.2.4",
- "graphql-tools": "^4.0.6",
+ "@graphql-codegen/cli": "1.21.3",
+ "@graphql-codegen/typescript": "1.21.1",
+ "@graphql-codegen/typescript-operations": "^1.17.15",
+ "@rollup/plugin-json": "^4.1.0",
+ "@types/isomorphic-fetch": "^0.0.35",
+ "apollo-link-schema": "^1.2.5",
+ "graphql-tools": "^7.0.4",
"jest-transform-graphql": "^2.1.0",
- "rollup-plugin-graphql": "^0.1.0"
+ "node-fetch": "^2.6.1",
+ "rollup": "^2.45.2",
+ "rollup-plugin-graphql": "^0.1.0",
+ "rollup-plugin-typescript2": "^0.30.0",
+ "typescript": "^4.2.4",
+ "webpack": "^4.46.0"
},
"files": [
"lib/**/*"
diff --git a/packages/api-client/partial-types.js b/packages/api-client/partial-types.js
index e01f614d5..a0cf22972 100644
--- a/packages/api-client/partial-types.js
+++ b/packages/api-client/partial-types.js
@@ -1,7 +1,7 @@
const fetch = require('node-fetch');
const fs = require('fs');
-fetch('https://demo.site-builder.app//graphql', {
+fetch('https://demo-magento2.storefrontcloud.io/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
diff --git a/packages/api-client/rollup.config.js b/packages/api-client/rollup.config.js
index 35c10eaa9..c0f240182 100644
--- a/packages/api-client/rollup.config.js
+++ b/packages/api-client/rollup.config.js
@@ -1,9 +1,34 @@
-import pkg from './package.json';
-import { generateBaseConfig } from '../../rollup.base.config';
import graphql from 'rollup-plugin-graphql';
+import typescript from 'rollup-plugin-typescript2';
import json from '@rollup/plugin-json';
+import pkg from './package.json';
+import { generateBaseConfig } from '../../rollup.base.config';
-const baseConfig = generateBaseConfig(pkg);
-baseConfig.plugins.push(graphql(), json());
+const server = {
+ input: 'src/index.server.ts',
+ output: [
+ {
+ file: pkg.server,
+ format: 'cjs',
+ sourcemap: true,
+ },
+ ],
+ external: [
+ '@apollo/client/utilities',
+ ...Object.keys(pkg.dependencies || {}),
+ ...Object.keys(pkg.peerDependencies || {}),
+ ],
+ plugins: [
+ typescript({
+ // eslint-disable-next-line global-require
+ typescript: require('typescript'),
+ }),
+ graphql(),
+ json(),
+ ],
+};
-export default baseConfig;
+export default [
+ generateBaseConfig(pkg),
+ server,
+];
diff --git a/packages/api-client/src/api/cart/query.ts b/packages/api-client/src/api/cart/query.ts
index 94625b712..0fb01526c 100644
--- a/packages/api-client/src/api/cart/query.ts
+++ b/packages/api-client/src/api/cart/query.ts
@@ -2,9 +2,9 @@ import gql from 'graphql-tag';
import {cartFragment} from '../../fragments';
export default gql`
- query cart($cartId: String!) {
- cart(cart_id:$cartId) {
- ${cartFragment}
- }
- }
+ query cart($cartId: String!) {
+ cart(cart_id:$cartId) {
+ ${cartFragment}
+ }
+ }
`;
diff --git a/packages/api-client/src/api/customerOrders/index.ts b/packages/api-client/src/api/customerOrders/index.ts
index e4a42013b..5858bd554 100644
--- a/packages/api-client/src/api/customerOrders/index.ts
+++ b/packages/api-client/src/api/customerOrders/index.ts
@@ -6,7 +6,7 @@ const orders = async ({ client }): Promise> => {
+const deleteCustomerAddress = async ({ client }, input: {id: number}): Promise> => {
return await client.mutate({
mutation: DeleteCustomerAddress,
variables: { input }
diff --git a/packages/api-client/src/api/generateCustomerToken/index.ts b/packages/api-client/src/api/generateCustomerToken/index.ts
index b00c1dd4f..41317498c 100644
--- a/packages/api-client/src/api/generateCustomerToken/index.ts
+++ b/packages/api-client/src/api/generateCustomerToken/index.ts
@@ -10,11 +10,11 @@ const generateCustomerToken = async ({ client }, email: string, password: string
email,
password
},
- fetchPolicy: 'no-cache',
+ fetchPolicy: 'no-cache'
});
return response;
- //return response.data.changeCustomerPassword;
+ // return response.data.changeCustomerPassword;
};
export default generateCustomerToken;
diff --git a/packages/api-client/src/api/getMenuCategory/index.ts b/packages/api-client/src/api/getMenuCategory/index.ts
new file mode 100644
index 000000000..5fbcd1eed
--- /dev/null
+++ b/packages/api-client/src/api/getMenuCategory/index.ts
@@ -0,0 +1,38 @@
+import { CustomQuery } from '@vue-storefront/core';
+import { Context } from '../../types/context';
+import { CategoryFilterInput, CategoryProducts } from '../../types/GraphQL';
+import { BaseQuery } from './query';
+
+export default async function getMenuCategory(
+ context: Context,
+ params: CategoryFilterInput,
+ customQuery?: CustomQuery,
+): Promise {
+ const defaultVariables = params ? {
+ ids: params.ids,
+ name: params.name,
+ url_key: params.url_key,
+ } : { };
+
+ const { categories } = context.extendQuery(customQuery,
+ {
+ categories: {
+ query: BaseQuery,
+ variables: defaultVariables,
+ },
+ });
+
+ try {
+ const { data } = await context.client.query({
+ query: BaseQuery,
+ variables: categories.variables,
+ // temporary, seems like bug in apollo:
+ // @link: https://github.com/apollographql/apollo-client/issues/3234
+ fetchPolicy: 'no-cache',
+ });
+
+ return data;
+ } catch (error) {
+ throw error.graphQLErrors?.[0] || error.networkError?.result || error;
+ }
+}
diff --git a/packages/api-client/src/api/getMenuCategory/query.ts b/packages/api-client/src/api/getMenuCategory/query.ts
new file mode 100644
index 000000000..d8f3a3f8b
--- /dev/null
+++ b/packages/api-client/src/api/getMenuCategory/query.ts
@@ -0,0 +1,30 @@
+import gql from 'graphql-tag';
+import { categoryDataFragment, categoryUrlData } from '../../fragments';
+
+export const BaseQuery = gql`
+ query GetMenuCategory($filters: CategoryFilterInput = {}){
+ categories(filters: $filters) {
+ items {
+ children {
+ ${categoryDataFragment}
+ ${categoryUrlData}
+ children_count
+ children {
+ ${categoryDataFragment}
+ ${categoryUrlData}
+ children_count
+ }
+ }
+ children_count
+ ${categoryDataFragment}
+ ${categoryUrlData}
+ }
+ page_info{
+ current_page
+ page_size
+ total_pages
+ }
+ total_count
+ }
+ }
+`;
diff --git a/packages/api-client/src/api/index.ts b/packages/api-client/src/api/index.ts
index 1d7037303..0a6fc4652 100644
--- a/packages/api-client/src/api/index.ts
+++ b/packages/api-client/src/api/index.ts
@@ -1,29 +1,32 @@
-export { default as products } from './products';
-export { default as storeConfig } from './storeConfig';
-export { default as categoryList} from './categoryList';
-export { default as urlResolver} from './urlResolver';
-export { default as cmsPage} from './cmsPage';
-export { default as createEmptyCart} from './createEmptyCart';
-export { default as cart} from './cart';
-export { default as addSimpleProductsToCart} from './addSimpleProductsToCart';
-export { default as addConfigurableProductsToCart} from './addConfigurableProductsToCart';
-export { default as updateCartItems} from './updateCartItems';
-export { default as removeItemFromCart} from './removeItemFromCart';
-export { default as applyCouponToCart} from './applyCouponToCart';
-export { default as generateCustomerToken} from './generateCustomerToken';
-export { default as customer} from './customer';
-export { default as mergeCarts} from './mergeCarts';
-export { default as customerCart} from './customerCart';
-export { default as createCustomer} from './createCustomer';
-export { default as changeCustomerPassword} from './changeCustomerPassword';
-export { default as revokeCustomerToken} from './revokeCustomerToken';
-export { default as updateCustomer} from './updateCustomer';
-export { default as wishlist } from './wishlist';
+export { default as addConfigurableProductsToCart } from './addConfigurableProductsToCart';
+export { default as addSimpleProductsToCart } from './addSimpleProductsToCart';
+export { default as applyCouponToCart } from './applyCouponToCart';
+export { default as cart } from './cart';
+export { default as categoryList } from './categoryList';
+export { default as changeCustomerPassword } from './changeCustomerPassword';
+export { default as cmsPage } from './cmsPage';
+export { default as createCustomer } from './createCustomer';
+export { default as createCustomerAddress } from './createCustomerAddress';
+export { default as createEmptyCart } from './createEmptyCart';
+export { default as customer } from './customer';
+export { default as customerCart } from './customerCart';
+export { default as customerOrders } from './customerOrders';
+export { default as generateCustomerToken } from './generateCustomerToken';
+export { default as mergeCarts } from './mergeCarts';
+export { default as getMenuCategory } from './getMenuCategory';
export { default as placeOrder } from './placeOrder';
+export { default as products } from './products';
+export { default as removeCouponFromCart } from './removeCouponFromCart';
+export { default as removeItemFromCart } from './removeItemFromCart';
+export { default as revokeCustomerToken } from './revokeCustomerToken';
export { default as setBillingAddressOnCart } from './setBillingAddressOnCart';
export { default as setGuestEmailOnCart } from './setGuestEmailOnCart';
export { default as setPaymentMethodOnCart } from './setPaymentMethodOnCart';
export { default as setShippingAddressesOnCart } from './setShippingAddressesOnCart';
export { default as setShippingMethodsOnCart } from './setShippingMethodsOnCart';
+export { default as storeConfig } from './storeConfig';
+export { default as updateCartItems } from './updateCartItems';
+export { default as updateCustomer } from './updateCustomer';
export { default as updateCustomerAddress } from './updateCustomerAddress';
-export { default as customerOrders } from './customerOrders'
+export { default as urlResolver } from './urlResolver';
+export { default as wishlist } from './wishlist';
diff --git a/packages/api-client/src/api/placeOrder/mutation.ts b/packages/api-client/src/api/placeOrder/mutation.ts
index 50d0bdc09..5c28a23f6 100644
--- a/packages/api-client/src/api/placeOrder/mutation.ts
+++ b/packages/api-client/src/api/placeOrder/mutation.ts
@@ -3,10 +3,10 @@ import gql from 'graphql-tag';
export default gql`
mutation placeOrder($input: PlaceOrderInput) {
placeOrder(input: $input) {
- order {
- order_id
- order_number
- }
+ order {
+ order_id
+ order_number
+ }
}
}
`;
diff --git a/packages/api-client/src/api/products/index.ts b/packages/api-client/src/api/products/index.ts
index b4614e5aa..e500a2b22 100644
--- a/packages/api-client/src/api/products/index.ts
+++ b/packages/api-client/src/api/products/index.ts
@@ -1,6 +1,10 @@
+import gql from 'graphql-tag';
+import { ApolloQueryResult } from 'apollo-client';
+import { CustomQuery } from '@vue-storefront/core';
import { ProductAttributeFilterInput, ProductAttributeSortInput, Products } from '../../types/GraphQL';
import { detailQuery, listQuery } from './query';
-import { ApolloQueryResult } from 'apollo-client';
+import { Context } from '../../types/context';
+import { GetProductSearchParams } from '../../types/API';
const enum ProductsQueryType {
list = 'LIST',
@@ -13,25 +17,46 @@ type Variables = {
search?: string;
filter?: ProductAttributeFilterInput;
sort?: ProductAttributeSortInput;
-}
+};
+
+const getProduct = async (
+ context: Context,
+ searchParams?: GetProductSearchParams,
+ customQuery?: CustomQuery,
+): Promise> => {
+ const defaultParams = {
+ pageSize: 20,
+ currentPage: 1,
+ queryType: ProductsQueryType.list,
+ ...searchParams,
+ };
+
+ const query = defaultParams.queryType === ProductsQueryType.list ? listQuery : detailQuery;
+
+ const variables: Variables = {
+ pageSize: defaultParams.pageSize,
+ currentPage: defaultParams.currentPage,
+ };
+
+ if (defaultParams.search) variables.search = defaultParams.search;
+
+ if (defaultParams.filter) variables.filter = defaultParams.filter;
+
+ if (defaultParams.sort) variables.sort = defaultParams.sort;
+
+ const { products } = context.extendQuery(
+ customQuery, {
+ products: {
+ query,
+ variables: defaultParams,
+ },
+ },
+ );
-const getProduct = async({ client },
- pageSize = 20,
- currentPage = 1,
- queryType: ProductsQueryType = ProductsQueryType.list,
- search?: string,
- filter?: ProductAttributeFilterInput,
- sort?: ProductAttributeSortInput): Promise> => {
- const query = queryType === ProductsQueryType.list ? listQuery : detailQuery;
-
- const variables: Variables = { pageSize, currentPage };
- if (search) variables.search = search;
- if (filter) variables.filter = filter;
- if (sort) variables.sort = sort;
-
- return await client.query({
- query,
- variables
+ return context.client.query({
+ query: gql`${products.query}`,
+ variables: products.variables,
+ fetchPolicy: 'no-cache',
});
};
diff --git a/packages/api-client/src/api/products/query.ts b/packages/api-client/src/api/products/query.ts
index 73b29495f..c9ab07318 100644
--- a/packages/api-client/src/api/products/query.ts
+++ b/packages/api-client/src/api/products/query.ts
@@ -1,7 +1,8 @@
import gql from 'graphql-tag';
-import {productFragment} from '../../fragments';
+import { productFragment } from '../../fragments';
+
export const detailQuery = gql`
- query products($search: String, $filter: ProductAttributeFilterInput, $pageSize: Int = 20, $currentPage: Int = 1, $sort: ProductAttributeSortInput) {
+ query products($search: String = "", $filter: ProductAttributeFilterInput, $pageSize: Int = 20, $currentPage: Int = 1, $sort: ProductAttributeSortInput) {
products(search: $search, filter: $filter, pageSize: $pageSize, currentPage: $currentPage, sort: $sort) {
items {
id
@@ -93,7 +94,7 @@ export const detailQuery = gql`
`;
export const listQuery = gql`
- query products($search: String, $filter: ProductAttributeFilterInput, $pageSize: Int = 20, $currentPage: Int = 1, $sort: ProductAttributeSortInput) {
+ query products($search: String = "", $filter: ProductAttributeFilterInput, $pageSize: Int = 20, $currentPage: Int = 1, $sort: ProductAttributeSortInput) {
products(search: $search, filter: $filter, pageSize: $pageSize, currentPage: $currentPage, sort: $sort) {
aggregations {
attribute_code,
diff --git a/packages/api-client/src/api/setShippingAddressesOnCart/mutation.ts b/packages/api-client/src/api/setShippingAddressesOnCart/mutation.ts
index 25f7e044b..d6c8a8e00 100644
--- a/packages/api-client/src/api/setShippingAddressesOnCart/mutation.ts
+++ b/packages/api-client/src/api/setShippingAddressesOnCart/mutation.ts
@@ -1,11 +1,21 @@
import gql from 'graphql-tag';
-import { cartFragment } from '../../fragments';
export default gql`
- mutation setShippingMethodsOnCart($input: SetShippingMethodsOnCartInput) {
- setShippingMethodsOnCart(input: $input) {
+ mutation setShippingAddressesOnCart($input: setShippingAddressesOnCartInput) {
+ setShippingAddressesOnCart(input: $input) {
cart {
- ${cartFragment}
+ shipping_addresses {
+ selected_shipping_method {
+ carrier_code
+ carrier_title
+ method_code
+ method_title
+ amount {
+ value
+ currency
+ }
+ }
+ }
}
}
}
diff --git a/packages/api-client/src/fragments/index.ts b/packages/api-client/src/fragments/index.ts
index 14316c5ba..055b079f8 100644
--- a/packages/api-client/src/fragments/index.ts
+++ b/packages/api-client/src/fragments/index.ts
@@ -3,17 +3,6 @@ export const productFragment = `
type_id
sku
name
- stock_status
- only_x_left_in_stock
- description {
- html
- }
- short_description {
- html
- }
- categories {
- id
- }
price_range {
minimum_price {
final_price {
@@ -26,26 +15,36 @@ export const productFragment = `
}
}
}
- small_image {
- url
- }
image {
url
}
thumbnail {
- url
+ url
}
url_rewrites {
- url
- parameters {
+ url
+ parameters {
+ name
+ value
+ }
+ }
+ categories {
+ id
name
- value
- }
+ url_suffix
+ url_path
+ breadcrumbs {
+ category_name,
+ category_url_path
+ }
}
`;
export const cartFragment = `
id
+ applied_coupons {
+ code
+ }
prices {
subtotal_excluding_tax {
value
@@ -81,6 +80,18 @@ export const cartFragment = `
method_title
}
}
+ billing_address {
+ firstname
+ lastname
+ street
+ city
+ postcode
+ telephone
+ country {
+ code
+ label
+ }
+ }
items {
id
product {
@@ -97,14 +108,6 @@ export const cartFragment = `
value
}
}
- ... on ConfigurableCartItem {
- configurable_options {
- id
- value_id,
- option_label
- value_label
- }
- }
quantity
}
total_quantity
@@ -135,3 +138,22 @@ export const customerFragment = `
telephone
}
`;
+
+export const categoryDataFragment = `
+ canonical_url
+ image
+ include_in_menu
+ is_anchor
+ level
+ name
+ path
+ path_in_store
+ position
+ uid
+`;
+
+export const categoryUrlData = `
+ url_key
+ url_path
+ url_suffix
+`;
diff --git a/packages/api-client/src/helpers.ts b/packages/api-client/src/helpers.ts
index 2f57c3db5..3551eba09 100644
--- a/packages/api-client/src/helpers.ts
+++ b/packages/api-client/src/helpers.ts
@@ -1,39 +1,40 @@
-import { Config } from './types/setup';
-import createMagentoConnection from './link';
import ApolloClient from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
+import { Config } from './types/setup';
+import createMagentoConnection from './link';
const onSetup = (settings: Config): {
config: Config;
client: ApolloClient
} => {
const defaultSettings = {
- api: "https://demo.site-builder.app/graphql",
+ api: '',
tax: {
- displayCartSubtotalIncludingTax: true
+ displayCartSubtotalIncludingTax: true,
},
externalCheckout: {
- enable: false
- }
+ enable: false,
+ },
};
const config = {
- ...defaultSettings, ...settings
+ ...defaultSettings,
+ ...settings,
} as any;
if (settings.client) {
return {
client: settings.client,
- config
+ config,
};
}
if (settings.customOptions && settings.customOptions.link) {
return {
client: new ApolloClient({
- cache: new InMemoryCache(), ...settings.customOptions
+ cache: new InMemoryCache({ addTypename: false }), ...settings.customOptions,
}),
- config
+ config,
};
}
@@ -41,10 +42,10 @@ const onSetup = (settings: Config): {
return {
config,
- client
+ client,
};
};
export {
- onSetup
+ onSetup,
};
diff --git a/packages/api-client/src/helpers/apiClient/defaultSettings.ts b/packages/api-client/src/helpers/apiClient/defaultSettings.ts
new file mode 100644
index 000000000..0d8b48530
--- /dev/null
+++ b/packages/api-client/src/helpers/apiClient/defaultSettings.ts
@@ -0,0 +1,41 @@
+import { ClientConfig } from '../../types/setup';
+
+export const defaultSettings: ClientConfig = {
+ api: 'https://demo-magento2.storefrontcloud.io/graphql',
+ cookies: {
+ currencyCookieName: 'vsf-currency',
+ countryCookieName: 'vsf-country',
+ localeCookieName: 'vsf-locale',
+ cartCookieName: 'vsf-cart',
+ customerCookieName: 'vsf-customer',
+ storeCookieName: 'vsf-store',
+ },
+ currency: 'USD',
+ defaultStore: 'default',
+ tax: {
+ displayCartSubtotalIncludingTax: true,
+ },
+ websites: {
+ base: {
+ code: 'base',
+ defaultStoreGroup: 'main_website_store',
+ storeGroups: {
+ main_website_store: {
+ code: 'main_website_store',
+ defaultStore: 'default',
+ stores: {
+ default: { code: 'default' },
+ },
+ },
+ },
+ },
+ },
+ state: {
+ getCartId: () => '',
+ setCartId: () => {},
+ getCustomerToken: () => '',
+ setCustomerToken: () => {},
+ getStore: () => '',
+ setStore: () => {},
+ },
+};
diff --git a/packages/api-client/src/helpers/magentoLink/graphQl.ts b/packages/api-client/src/helpers/magentoLink/graphQl.ts
new file mode 100644
index 000000000..13c987cc5
--- /dev/null
+++ b/packages/api-client/src/helpers/magentoLink/graphQl.ts
@@ -0,0 +1,68 @@
+/* eslint-disable @typescript-eslint/restrict-template-expressions */
+import ApolloClient from 'apollo-client';
+import fetch from 'isomorphic-fetch';
+import { ApolloLink } from 'apollo-link';
+import { createHttpLink } from 'apollo-link-http';
+import { InMemoryCache } from 'apollo-cache-inmemory';
+import { Logger } from '@vue-storefront/core';
+import { onError } from 'apollo-link-error';
+import { RetryLink } from 'apollo-link-retry';
+import { setContext } from 'apollo-link-context';
+import { handleRetry } from './linkHandlers';
+import { Config } from '../../types/setup';
+
+const createErrorHandler = () => onError(({
+ graphQLErrors,
+ networkError,
+}) => {
+ if (graphQLErrors) {
+ graphQLErrors.forEach(({
+ message,
+ locations,
+ path,
+ }) => {
+ if (!message.includes('Resource Owner Password Credentials Grant')) {
+ if (!locations) {
+ Logger.error(`[GraphQL error]: Message: ${message}, Path: ${path}`);
+ return;
+ }
+
+ const parsedLocations = locations.map(({
+ column,
+ line,
+ }) => `[column: ${column}, line: ${line}]`);
+
+ Logger.error(`[GraphQL error]: Message: ${message}, Location: ${parsedLocations.join(', ')}, Path: ${path}`);
+ }
+ });
+ }
+
+ if (networkError) {
+ Logger.error(`[Network error]: ${networkError}`);
+ }
+});
+
+export const apolloLinkFactory = (settings: Config, handlers?: {
+ authLink?: ApolloLink;
+}) => {
+ const baseAuthLink = handlers?.authLink || setContext((apolloReq, { headers }) => ({
+ headers: {
+ ...headers,
+ },
+ }));
+ const httpLink = createHttpLink({ uri: settings.api, fetch });
+
+ const onErrorLink = createErrorHandler();
+
+ const errorRetry = new RetryLink({
+ attempts: handleRetry(),
+ delay: () => 0,
+ });
+
+ return ApolloLink.from([onErrorLink, errorRetry, baseAuthLink.concat(httpLink)]);
+};
+
+export const apolloClientFactory = (customOptions: Record) => new ApolloClient({
+ cache: new InMemoryCache(),
+ ...customOptions,
+});
diff --git a/packages/api-client/src/helpers/magentoLink/index.ts b/packages/api-client/src/helpers/magentoLink/index.ts
new file mode 100644
index 000000000..7c5efe35b
--- /dev/null
+++ b/packages/api-client/src/helpers/magentoLink/index.ts
@@ -0,0 +1,18 @@
+import { Logger } from '@vue-storefront/core';
+import { Config } from '../../types/setup';
+import { apolloLinkFactory } from './graphQl';
+import { authLinkFactory } from './linkHandlers';
+
+export const createMagentoConnection = (settings: Config): any => {
+ Logger.debug('createMagentoConnection');
+
+ const authLink = authLinkFactory({ state: settings.state });
+
+ const apolloLink = apolloLinkFactory(settings, {
+ authLink,
+ });
+
+ return {
+ apolloLink,
+ };
+};
diff --git a/packages/api-client/src/helpers/magentoLink/linkHandlers.ts b/packages/api-client/src/helpers/magentoLink/linkHandlers.ts
new file mode 100644
index 000000000..dfc65989c
--- /dev/null
+++ b/packages/api-client/src/helpers/magentoLink/linkHandlers.ts
@@ -0,0 +1,36 @@
+import { Logger } from '@vue-storefront/core';
+import { setContext } from 'apollo-link-context';
+import { ConfigState } from '../../types/setup';
+
+export const handleRetry = () => (count, operation, error) => {
+ if (count > 3) {
+ return false;
+ }
+
+ if (error?.result?.message === 'invalid_token') {
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+ Logger.debug(`Apollo retry-link, the operation (${operation.operationName}) sent with wrong token, creating a new one... (attempt: ${count})`);
+ return true;
+ }
+
+ return false;
+};
+
+export const authLinkFactory = ({ state }: {
+ state: ConfigState;
+}) => setContext((apolloReq, { headers }) => {
+ Logger.debug('Apollo authLinkFactory', apolloReq.operationName);
+
+ const token: string = state.getCustomerToken();
+
+ if (token) {
+ Logger.debug('Apollo authLinkFactory, finished, token: ', token);
+ }
+
+ return {
+ headers: {
+ ...headers,
+ ...(token ? { authorization: `Bearer ${token}` } : {}),
+ },
+ };
+});
diff --git a/packages/api-client/src/index.server.ts b/packages/api-client/src/index.server.ts
new file mode 100644
index 000000000..9d3e87497
--- /dev/null
+++ b/packages/api-client/src/index.server.ts
@@ -0,0 +1,94 @@
+/* istanbul ignore file */
+import { ApiClientExtension, apiClientFactory } from '@vue-storefront/core';
+import * as api from './api';
+import { ClientInstance, Config } from './types/setup';
+import { createMagentoConnection } from './helpers/magentoLink';
+import { defaultSettings } from './helpers/apiClient/defaultSettings';
+import { apolloClientFactory } from './helpers/magentoLink/graphQl';
+
+const onCreate = (settings: Config): { config: Config; client: ClientInstance } => {
+ const config = {
+ ...defaultSettings,
+ ...settings,
+ state: settings.state || defaultSettings.state,
+ } as unknown as Config;
+
+ if (settings.client) {
+ return {
+ client: settings.client,
+ config,
+ };
+ }
+
+ if (settings.customOptions && settings.customOptions.link) {
+ return {
+ client: apolloClientFactory(settings.customOptions),
+ config,
+ };
+ }
+
+ const { apolloLink } = createMagentoConnection(config);
+
+ const client = apolloClientFactory({
+ link: apolloLink,
+ ...settings.customOptions,
+ });
+
+ return {
+ config,
+ client,
+ };
+};
+
+const tokenExtension: ApiClientExtension = {
+ name: 'tokenExtension',
+ hooks: (req, res) => ({
+ beforeCreate: ({ configuration }) => {
+ const cartCookieName = configuration.cookies?.cartCookieName || defaultSettings.cookies.cartCookieName;
+ const customerCookieName = configuration.cookies?.customerCookieName || defaultSettings.cookies.customerCookieName;
+ const storeCookieName = configuration.cookies?.storeCookieName || defaultSettings.cookies.storeCookieName;
+
+ return {
+ ...configuration,
+ state: {
+ getCartId: () => req.cookies[cartCookieName],
+ setCartId: (id) => {
+ if (!id) {
+ req.cookies[cartCookieName];
+ return;
+ }
+ res.cookie(cartCookieName, JSON.stringify(id));
+ },
+ getCustomerToken: () => req.cookies[customerCookieName],
+ setCustomerToken: (token) => {
+ if (!token) {
+ // eslint-disable-next-line no-param-reassign
+ delete req.cookies[customerCookieName];
+ return;
+ }
+ res.cookie(customerCookieName, JSON.stringify(token));
+ },
+ getStore: () => req.cookies[storeCookieName],
+ setStore: (id) => {
+ if (!id) {
+ // eslint-disable-next-line no-param-reassign
+ delete req.cookies[storeCookieName];
+ return;
+ }
+ res.cookie(storeCookieName, JSON.stringify(id));
+ },
+ },
+ };
+ },
+ }),
+};
+
+const { createApiClient } = apiClientFactory({
+ onCreate,
+ api,
+ extensions: [tokenExtension],
+});
+
+export {
+ createApiClient,
+};
diff --git a/packages/api-client/src/index.ts b/packages/api-client/src/index.ts
index 6ac380acf..d065f333e 100644
--- a/packages/api-client/src/index.ts
+++ b/packages/api-client/src/index.ts
@@ -1,16 +1,83 @@
-import * as api from './api';
-import { apiClientFactory } from '@vue-storefront/core';
-export * from './types';
-import { onSetup } from './helpers';
-
-const { createApiClient } = apiClientFactory({
- tag: 'ma',
- onSetup,
- api: {
- ...api
- }
-});
-
+export * from './types/setup';
+export * from './types/API';
+export * from './types/context';
export {
- createApiClient
-};
+ Cart as CartInterface,
+ Customer as CustomerInterface,
+ ProductInterface,
+ AddSimpleProductsToCartInput,
+ AddConfigurableProductsToCartOutput,
+ AddConfigurableProductsToCartInput,
+ Products,
+ AddSimpleProductsToCartOutput,
+ Aggregation,
+ AggregationOption,
+ AppliedCoupon,
+ ApplyCouponToCartInput,
+ ApplyCouponToCartOutput,
+ Breadcrumb,
+ CategoryFilterInput,
+ Order,
+ CategoryInterface,
+ CategoryProducts,
+ CustomerAddressInput,
+ CustomerInput,
+ CmsBlock,
+ CmsPage,
+ PlaceOrderInput,
+ PlaceOrderOutput,
+ ComplexTextValue,
+ CustomerAddress,
+ CustomerToken,
+ EntityUrl,
+ Money,
+ FixedProductTax,
+ HttpQueryParameter,
+ MediaGalleryInterface,
+ PriceRange,
+ ProductAttributeFilterInput,
+ ProductAttributeSortInput,
+ ProductDiscount,
+ ProductImage,
+ ProductLinks,
+ SetBillingAddressOnCartInput,
+ SetBillingAddressOnCartOutput,
+ SetGuestEmailOnCartInput,
+ SetGuestEmailOnCartOutput,
+ SetPaymentMethodOnCartInput,
+ SetPaymentMethodOnCartOutput,
+ SetShippingAddressesOnCartInput,
+ SetShippingAddressesOnCartOutput,
+ SetShippingMethodsOnCartInput,
+ SetShippingMethodsOnCartOutput,
+ ProductLinksInterface,
+ ProductPrice,
+ ProductStockStatus,
+ RemoveCouponFromCartInput,
+ RemoveCouponFromCartOutput,
+ RemoveItemFromCartInput,
+ RemoveItemFromCartOutput,
+ SearchResultPageInfo,
+ SelectedShippingMethod,
+ SortEnum,
+ SortField,
+ SortFields,
+ StoreConfig,
+ TierPrice,
+ UpdateCartItemsInput,
+ UpdateCartItemsOutput,
+ UrlRewrite,
+ Wishlist,
+ CartItemInterface,
+ CategoryTree,
+ cmsPageQuery,
+ createEmptyCartMutation,
+ categoryList,
+ customerQuery,
+ cartQuery,
+ customerOrdersQuery,
+ Discount,
+ storeConfigQuery,
+ urlResolver,
+ wishlistOutput,
+} from './types/GraphQL';
diff --git a/packages/api-client/src/link.ts b/packages/api-client/src/link.ts
index 78ba84eb8..4cb55f5e8 100644
--- a/packages/api-client/src/link.ts
+++ b/packages/api-client/src/link.ts
@@ -1,5 +1,4 @@
import { createHttpLink } from 'apollo-link-http';
-import { Config } from './types/setup';
import fetch from 'isomorphic-fetch';
import ApolloClient from 'apollo-client';
import { ApolloLink } from 'apollo-link';
@@ -7,40 +6,39 @@ import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemo
import { setContext } from 'apollo-link-context';
import { onError } from 'apollo-link-error';
import { Logger } from '@vue-storefront/core';
+import { Config } from './types/setup';
import introspectionQueryResultData from '../fragmentTypes.json';
-const createErrorHandler = () => {
- return onError(({
- graphQLErrors,
- networkError
- }) => {
- if (graphQLErrors) {
- graphQLErrors.map(({
- message,
- locations,
- path
- }) => {
- if (!message.includes('Resource Owner Password Credentials Grant')) {
- if (!locations) {
- Logger.error(`[GraphQL error]: Message: ${message}, Path: ${path}`);
- return;
- }
+const createErrorHandler = () => onError(({
+ graphQLErrors,
+ networkError,
+}) => {
+ if (graphQLErrors) {
+ graphQLErrors.map(({
+ message,
+ locations,
+ path,
+ }) => {
+ if (!message.includes('Resource Owner Password Credentials Grant')) {
+ if (!locations) {
+ Logger.error(`[GraphQL error]: Message: ${message}, Path: ${path}`);
+ return;
+ }
- const parsedLocations = locations.map(({
- column,
- line
- }) => `[column: ${column}, line: ${line}]`);
+ const parsedLocations = locations.map(({
+ column,
+ line,
+ }) => `[column: ${column}, line: ${line}]`);
- Logger.error(`[GraphQL error]: Message: ${message}, Location: ${parsedLocations.join(', ')}, Path: ${path}`);
- }
- });
- }
+ Logger.error(`[GraphQL error]: Message: ${message}, Location: ${parsedLocations.join(', ')}, Path: ${path}`);
+ }
+ });
+ }
- if (networkError) {
- Logger.error(`[Network error]: ${networkError}`);
- }
- });
-};
+ if (networkError) {
+ Logger.error(`[Network error]: ${networkError}`);
+ }
+});
const createMagentoConnection = (settings: Config): ApolloClient => {
const apiState = settings.state;
@@ -50,30 +48,36 @@ const createMagentoConnection = (settings: Config): ApolloClient => {
uri: settings.api,
fetch,
headers: {
- Store: storeCode
- }
+ Store: storeCode,
+ },
});
+
const onErrorLink = createErrorHandler();
+
const authLink = setContext((_, { headers }) => {
const token = apiState.getCustomerToken();
if (token) {
return {
headers: {
...headers,
- authorization: `Bearer ${token}`
- }
+ authorization: `Bearer ${token}`,
+ },
};
}
return { headers };
});
+
const link: ApolloLink = ApolloLink.from([authLink, onErrorLink, httpLink]);
+
const fragmentMatcher = new IntrospectionFragmentMatcher({
- introspectionQueryResultData
+ introspectionQueryResultData,
});
+
const cache = new InMemoryCache({ fragmentMatcher });
+
return new ApolloClient({
cache,
- link
+ link,
});
};
diff --git a/packages/api-client/src/types/API.ts b/packages/api-client/src/types/API.ts
new file mode 100644
index 000000000..e71b33dd6
--- /dev/null
+++ b/packages/api-client/src/types/API.ts
@@ -0,0 +1,102 @@
+import { ApolloQueryResult } from 'apollo-client';
+import { ExecutionResult } from 'graphql';
+import {
+ AddConfigurableProductsToCartInput,
+ AddConfigurableProductsToCartOutput,
+ AddSimpleProductsToCartInput,
+ AddSimpleProductsToCartOutput,
+ AppliedCoupon, ApplyCouponToCartInput,
+ ApplyCouponToCartOutput,
+ Cart as CartInterface,
+ CartItemInterface,
+ cartQuery,
+ CategoryFilterInput,
+ categoryList, CategoryProducts,
+ CategoryTree,
+ cmsPageQuery,
+ createEmptyCartMutation,
+ Customer as CustomerInterface,
+ CustomerInput,
+ customerQuery,
+ CustomerToken,
+ ProductAttributeFilterInput,
+ ProductAttributeSortInput,
+ ProductInterface,
+ Products,
+ RemoveItemFromCartInput,
+ RemoveItemFromCartOutput,
+ storeConfigQuery,
+ UpdateCartItemsInput,
+ UpdateCartItemsOutput,
+ urlResolver,
+} from './GraphQL';
+
+export type Cart = CartInterface;
+export type CartItem = CartItemInterface;
+export type Category = CategoryTree;
+export type CategoryFilter = CategoryFilterInput;
+export type Coupon = AppliedCoupon;
+export type Customer = CustomerInterface;
+export type CustomerUpdateParameters = CustomerInput;
+export type Product = ProductInterface;
+export type ProductAttributeFilter = ProductAttributeFilterInput;
+export type ShippingMethod = Record;
+export type Wishlist = Record;
+
+export const enum ProductsQueryType {
+ list = 'LIST',
+ detail = 'DETAIL',
+}
+
+export type GetProductSearchParams = {
+ pageSize?: number;
+ currentPage?: number;
+ queryType?: ProductsQueryType;
+ search?: string;
+ filter?: ProductAttributeFilterInput;
+ sort?: ProductAttributeSortInput;
+};
+
+export interface MagentoApiMethods {
+ categoryList(categoryFilter?: CategoryFilterInput): Promise>;
+
+ urlResolver(url: string): Promise>;
+
+ products(searchParams: GetProductSearchParams): Promise>;
+
+ storeConfig(): Promise>;
+
+ cmsPage(indentifier: string): Promise>;
+
+ createEmptyCart(): Promise>;
+
+ getMenuCategory(): Promise>;
+
+ cart(cartId: string): Promise>;
+
+ addSimpleProductsToCart(input: AddSimpleProductsToCartInput): Promise>;
+
+ addConfigurableProductsToCart(input: AddConfigurableProductsToCartInput): Promise>;
+
+ updateCartItems(input: UpdateCartItemsInput): Promise>;
+
+ removeItemFromCart(input: RemoveItemFromCartInput): Promise>;
+
+ applyCouponToCart(input: ApplyCouponToCartInput): Promise>;
+
+ generateCustomerToken(email: string, password: string): Promise>;
+
+ customer(): Promise>;
+
+ mergeCarts(sourceCartId: string, destinationCartId: string): Promise>;
+
+ customerCart(): Promise>;
+
+ createCustomer(input: CustomerInput): Promise;
+
+ changeCustomerPassword(currentPassword: string, newPassword: string): Promise;
+
+ revokeCustomerToken(): Promise;
+
+ updateCustomer(input: CustomerInput): Promise;
+}
diff --git a/packages/api-client/src/types/GraphQL.ts b/packages/api-client/src/types/GraphQL.ts
index 101fc0146..2bcb29656 100644
--- a/packages/api-client/src/types/GraphQL.ts
+++ b/packages/api-client/src/types/GraphQL.ts
@@ -1,5 +1,5 @@
-import exp from "constants";
-import { cart } from "../api";
+import exp from 'constants';
+import { cart } from '../api';
/* Category */
export type categoryList = [CategoryTree]
@@ -571,7 +571,7 @@ export type createEmptyCartMutation = {
};
export type Cart = {
- applied_coupons: [AppliedCoupon]
+ applied_coupons: AppliedCoupon[]
available_payment_methods: [AvailablePaymentMethod]
billing_address: BillingCartAddress
email: string
@@ -598,26 +598,26 @@ type BillingCartAddress = CartAddressInterface;
export type SelectedShippingMethod = {
amount: Money,
base_amount: Money,
- carrier_code: String,
- carrier_title: String,
- method_code: String,
- method_title: String
+ carrier_code: string,
+ carrier_title: string,
+ method_code: string,
+ method_title: string
}
type CartItemQuantity = {
- cart_item_id: Number,
- quantity: Number
+ cart_item_id: number,
+ quantity: number
}
type AvailableShippingMethod = {
amount: Money,
- available: Boolean,
+ available: boolean,
base_amount: Money,
- carrier_code: String,
- carrier_title: String,
- error_message: String,
- method_code: String,
- method_title: String,
+ carrier_code: string,
+ carrier_title: string,
+ error_message: string,
+ method_code: string,
+ method_title: string,
price_excl_tax: Money,
price_incl_tax: Money
}
@@ -626,9 +626,9 @@ type ShippingCartAddress = {
available_shipping_methods: [AvailableShippingMethod],
cart_items: [CartItemQuantity],
cart_items_v2: CartItemInterface,
- items_weight: Number,
+ items_weight: number,
selected_shipping_method: SelectedShippingMethod,
- pickup_location_code: String
+ pickup_location_code: string
}
interface CartAddressInterface {
@@ -656,8 +656,8 @@ type CartAddressRegion = {
// CUSTOM
type CustNote = {
amount: Money,
- customer_note: String,
- font: String
+ customer_note: string,
+ font: string
}
export interface CartItemInterface {
@@ -676,7 +676,7 @@ type CartItemPrices = {
total_item_discount: Money
}
-type Discount = {
+export type Discount = {
amount: Money
label: string
}
@@ -817,12 +817,12 @@ export type customerQuery = {
}
type CustomerOrder = {
- created_at: String,
- grand_total: Number,
- id: Number,
- increment_id: String,
- order_number: String,
- status: String
+ created_at: string,
+ grand_total: number,
+ id: number,
+ increment_id: string,
+ order_number: string,
+ status: string
}
export type customerOrdersQuery = {
@@ -838,7 +838,7 @@ export type Customer = {
email: string
firstname: string
gender: number
- is_subscribed: Boolean
+ is_subscribed: boolean
lastname: string
middlename: string
prefix: string
@@ -851,8 +851,8 @@ export type CustomerAddress = {
city: string
company: string
country_code: CountryCodeEnum
- default_billing: Boolean
- default_shipping: Boolean
+ default_billing: boolean
+ default_shipping: boolean
extension_attributes: [CustomerAddressAttribute]
fax: string
firstname: string
@@ -1191,39 +1191,38 @@ type CustomerAddressRegionInput = {
}
export type CustomerAddressInput = {
- city: String
- company: String
+ city: string
+ company: string
country_code: CountryCodeEnum
country_id: CountryCodeEnum
custom_attributes: [CustomerAddressAttributeInput]
- default_billing: Boolean
- default_shipping: Boolean
- fax: String
- firstname: String
- lastname: String
- middlename: String
- postcode: String
- prefix: String
+ default_billing: boolean
+ default_shipping: boolean
+ fax: string
+ firstname: string
+ lastname: string
+ middlename: string
+ postcode: string
+ prefix: string
region: CustomerAddressRegionInput
- street: [String]
- suffix: String
- telephone: String
- vat_id: String
+ street: [string]
+ suffix: string
+ telephone: string
+ vat_id: string
}
-
/* SetBillingAddressOnCart*/
type CartAddressInput = {
city: string,
company: string,
- country_code: String
- firstname: String
- lastname: String
- postcode: String
- region: String
+ country_code: string
+ firstname: string
+ lastname: string
+ postcode: string
+ region: string
save_in_address_book: boolean
- street: [String],
- telephone: String
+ street: [string],
+ telephone: string
}
type BillingAddressInput = {
@@ -1242,16 +1241,16 @@ export type SetBillingAddressOnCartOutput = {
cart: CartItemPrices
}
-/*SetShippingAddressesOnCart*/
+/* SetShippingAddressesOnCart*/
type ShippingAddressInput = {
address: CartAddressInput,
customer_address_id: number,
- customer_notes: String,
- pickup_location_code: String
+ customer_notes: string,
+ pickup_location_code: string
}
export type SetShippingAddressesOnCartInput = {
- cart_id: String,
+ cart_id: string,
shipping_addresses: ShippingAddressInput
}
@@ -1259,34 +1258,34 @@ export type SetShippingAddressesOnCartOutput = {
cart: Cart
}
-/*SetGuestEmailOnCart*/
+/* SetGuestEmailOnCart*/
export type SetGuestEmailOnCartInput = {
- cart_id: String,
- email: String
+ cart_id: string,
+ email: string
}
export type SetGuestEmailOnCartOutput = {
cart: Cart
}
-/*PlaceOrder*/
+/* PlaceOrder*/
export type PlaceOrderInput = {
- cart_id: String,
- delivery_date: String
+ cart_id: string,
+ delivery_date: string
}
export type PlaceOrderOutput = {
order: Order
}
-/*SetShippingMethodsOnCart*/
+/* SetShippingMethodsOnCart*/
type ShippingMethodInput = {
carrier_code: string,
method_code: string
}
export type SetShippingMethodsOnCartInput = {
- cart_id: String,
+ cart_id: string,
shipping_methods: ShippingMethodInput
}
diff --git a/packages/api-client/src/types/context.ts b/packages/api-client/src/types/context.ts
new file mode 100644
index 000000000..7660e7b76
--- /dev/null
+++ b/packages/api-client/src/types/context.ts
@@ -0,0 +1,5 @@
+import { ApiClientMethods, IntegrationContext } from '@vue-storefront/core';
+import { ClientInstance, Config } from './setup';
+import { MagentoApiMethods } from './API';
+
+export type Context = IntegrationContext>;
diff --git a/packages/api-client/src/types/index.ts b/packages/api-client/src/types/index.ts
deleted file mode 100644
index 9bb1d6cee..000000000
--- a/packages/api-client/src/types/index.ts
+++ /dev/null
@@ -1,155 +0,0 @@
-import {
- AddSimpleProductsToCartInput,
- AddSimpleProductsToCartOutput,
- cartQuery,
- CategoryFilterInput,
- categoryList,
- CategoryTree,
- cmsPageQuery,
- createEmptyCartMutation,
- ProductAttributeFilterInput,
- ProductAttributeSortInput,
- ProductInterface,
- Products,
- storeConfigQuery,
- urlResolver,
- Cart as CartInterface,
- CartItemInterface,
- UpdateCartItemsInput,
- UpdateCartItemsOutput,
- RemoveItemFromCartInput,
- RemoveItemFromCartOutput,
- ApplyCouponToCartInput,
- ApplyCouponToCartOutput,
- AppliedCoupon,
- AddConfigurableProductsToCartInput,
- AddConfigurableProductsToCartOutput,
- CustomerToken,
- customerQuery,
- CustomerInput,
- Customer as CustomerInterface
-} from './GraphQL';
-import {ApolloQueryResult} from 'apollo-client';
-import {ExecutionResult} from 'graphql';
-
-export type Cart = CartInterface;
-export type Wishlist = {}
-export type Product = ProductInterface;
-export type Category = CategoryTree;
-export type CategoryFilter = CategoryFilterInput
-export type ShippingMethod = {}
-export type CartItem = CartItemInterface;
-export type Coupon = AppliedCoupon;
-export type Customer = CustomerInterface;
-export type CustomerUpdateParameters = CustomerInput;
-
-export type ProductAttributeFilter = ProductAttributeFilterInput;
-
-export const enum ProductsQueryType {
- list = 'LIST',
- detail = 'DETAIL',
-}
-
-export interface ApiClientMethods {
- categoryList(categoryFilter?: CategoryFilterInput): Promise>;
- urlResolver(url: string): Promise>;
- products(pageSize: number,
- currentPage: number,
- queryType: ProductsQueryType,
- search?: string,
- filter?: ProductAttributeFilterInput,
- sort?: ProductAttributeSortInput): Promise>;
- storeConfig(): Promise>;
- cmsPage(indentifier: string): Promise>;
- createEmptyCart(): Promise>;
- cart(cartId: string): Promise>;
- addSimpleProductsToCart(input: AddSimpleProductsToCartInput): Promise>;
- addConfigurableProductsToCart(input: AddConfigurableProductsToCartInput): Promise>;
- updateCartItems(input: UpdateCartItemsInput): Promise>;
- removeItemFromCart(input: RemoveItemFromCartInput): Promise>;
- applyCouponToCart(input: ApplyCouponToCartInput): Promise>;
- generateCustomerToken(email: string, password: string): Promise>;
- customer(): Promise>;
- mergeCarts(sourceCartId: string, destinationCartId: string): Promise>;
- customerCart(): Promise>;
- createCustomer(input: CustomerInput): Promise;
- changeCustomerPassword(currentPassword: string, newPassword: string): Promise;
- revokeCustomerToken(): Promise;
- updateCustomer(input: CustomerInput): Promise;
-}
-
-export interface ApiClientSettings {
- storage: Storage;
- api?: {
- uri: string;
- };
- tax: {
- displayCartSubtotalIncludingTax: boolean;
- };
- externalCheckout: {
- enabled: boolean;
- cmsUrl: string;
- stores: Record;
- };
- websites: Record;
- defaultStore: string;
- overrides: {
- categoryList?(categoryFilter?: CategoryFilterInput): Promise>;
- urlResolver?(url: string): Promise>;
- products?(pageSize: number,
- currentPage: number,
- queryType: ProductsQueryType,
- search?: string,
- filter?: ProductAttributeFilterInput,
- sort?: ProductAttributeSortInput): Promise>;
- storeConfig?(): Promise>;
- cmsPage?(indentifier: string): Promise>;
- createEmptyCart?(): Promise>;
- cart?(cartId: string): Promise>;
- addSimpleProductsToCart?(input: AddSimpleProductsToCartInput): Promise>;
- addConfigurableProductsToCart?(input: AddConfigurableProductsToCartInput): Promise>;
- updateCartItems?(input: UpdateCartItemsInput): Promise>;
- removeItemFromCart?(input: RemoveItemFromCartInput): Promise>;
- applyCouponToCart?(input: ApplyCouponToCartInput): Promise>;
- generateCustomerToken?(email: string, password: string): Promise>;
- customer?(): Promise>;
- mergeCarts?(sourceCartId: string, destinationCartId: string): Promise>;
- customerCart?(): Promise>;
- createCustomer?(input: CustomerInput): Promise;
- changeCustomerPassword?(currentPassword: string, newPassword: string): Promise;
- revokeCustomerToken(): Promise;
- updateCustomer?(input: CustomerInput): Promise;
- };
-}
-
-type ExternalCheckoutStore = {
- cmsUrl: string;
-}
-
-export interface Storage {
- set: (
- name: string,
- value: any,
- ) => void;
- get: (name: string) => any;
- remove: (name: string) => any;
- removeAll: () => void;
-}
-
-export type Website = {
- code: string;
- defaultStoreGroup: string;
- storeGroups: Record;
-}
-
-export type StoreGroup = {
- code: string;
- defaultStore: string;
- stores: Record;
- website?: Website;
-}
-
-export type Store = {
- code: string;
- storeGroup?: StoreGroup;
-}
diff --git a/packages/api-client/src/types/setup.ts b/packages/api-client/src/types/setup.ts
index c42ba814b..d43e81aac 100644
--- a/packages/api-client/src/types/setup.ts
+++ b/packages/api-client/src/types/setup.ts
@@ -1,23 +1,94 @@
import ApolloClient, { ApolloClientOptions } from 'apollo-client';
-import { Storage, Website } from './index';
+import { MagentoApiMethods } from './API';
+
+export interface Storage {
+ set: (
+ name: string,
+ value: any
+ ) => void;
+ get: (name: string) => any;
+ remove: (name: string) => any;
+ removeAll: () => void;
+}
+
+export type Website = {
+ code: string;
+ defaultStoreGroup: string;
+ storeGroups: Record;
+};
export interface LocaleItem {
name: string;
label: string;
}
-export interface Config {
- client?: ApolloClient;
+type ExternalCheckoutStore = {
+ cmsUrl: string;
+};
+
+export type StoreGroup = {
+ code: string;
+ defaultStore: string;
+ stores: Record;
+ website?: Website;
+};
+
+export type Store = {
+ code: string;
+ storeGroup?: StoreGroup;
+};
+
+export interface ApiClientSettings {
+ storage: Storage;
+ api?: {
+ uri: string;
+ };
+ tax: {
+ displayCartSubtotalIncludingTax: boolean;
+ };
+ externalCheckout: {
+ enabled: boolean;
+ cmsUrl: string;
+ stores: Record;
+ };
+ websites: Record;
+ defaultStore: string;
+ overrides: MagentoApiMethods;
+}
+
+export type ConfigState = {
+ getCartId(): string;
+ setCartId(id: string): void;
+ getCustomerToken(): string;
+ setCustomerToken(token: string): void;
+ getStore(): string;
+ setStore(id: string): void;
+};
+
+export interface ClientConfig {
api: string;
customOptions?: ApolloClientOptions;
currency: string;
websites: Record;
defaultStore: string;
- storage: Storage;
- state: any;
tax: {
displayCartSubtotalIncludingTax: boolean;
};
+ cookies: {
+ currencyCookieName: string;
+ countryCookieName: string;
+ localeCookieName: string;
+ cartCookieName: string;
+ customerCookieName: string;
+ storeCookieName: string;
+ },
+ state: ConfigState;
+}
+
+export interface Config extends ClientConfig {
+ client?: ApolloClient;
+ storage: Storage;
}
-export default Storage;
+export interface ClientInstance extends ApolloClient {
+}
diff --git a/packages/composables/.editorconfig b/packages/composables/.editorconfig
new file mode 100644
index 000000000..7ac5caaf7
--- /dev/null
+++ b/packages/composables/.editorconfig
@@ -0,0 +1,15 @@
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.{js,jsx,ts,tsx}]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+trim_trailing_whitespace = true
+insert_final_newline = true
+max_line_length = 150
diff --git a/packages/composables/.lintstagedrc b/packages/composables/.lintstagedrc
new file mode 100644
index 000000000..d95e0101c
--- /dev/null
+++ b/packages/composables/.lintstagedrc
@@ -0,0 +1,5 @@
+{
+ "*.{js,jsx,ts,tsx,vue}": [
+ "eslint --fix"
+ ],
+}
diff --git a/packages/composables/nuxt/defaultConfig.js b/packages/composables/nuxt/defaultConfig.js
new file mode 100644
index 000000000..2decfc05b
--- /dev/null
+++ b/packages/composables/nuxt/defaultConfig.js
@@ -0,0 +1,10 @@
+export default {
+ cookies: {
+ currencyCookieName: 'vsf-currency',
+ countryCookieName: 'vsf-country',
+ localeCookieName: 'vsf-locale',
+ cartCookieName: 'vsf-cart',
+ customerCookieName: 'vsf-customer',
+ storeCookieName: 'vsf-store',
+ },
+};
diff --git a/packages/composables/nuxt/helpers/consts.js b/packages/composables/nuxt/helpers/consts.js
deleted file mode 100644
index 5c5511d8a..000000000
--- a/packages/composables/nuxt/helpers/consts.js
+++ /dev/null
@@ -1,34 +0,0 @@
-//import defaultConfig from '@vue-storefront/commercetools/nuxt/defaultConfig';
-
-/*
-const getLocaleSettings = (moduleOptions, app) => {
- let localeSettings = {};
-
- if (moduleOptions.cookies) {
- localeSettings = {
- locale: app.$cookies.get(moduleOptions.cookies.localeCookieName),
- country: app.$cookies.get(moduleOptions.cookies.currencyCookieName),
- currency: app.$cookies.get(moduleOptions.cookies.countryCookieName)
- };
- }
-
- return {
- locale: app.i18n.locale || (localeSettings.locale || moduleOptions.locale || defaultConfig.locale),
- country: localeSettings.country || moduleOptions.country || defaultConfig.country,
- currency: localeSettings.currency || moduleOptions.currency || defaultConfig.currency
- };
-};
-
-export const mapConfigToSetupObject = ({ moduleOptions, app, additionalProperties = {} }) => {
- return {
- ...defaultConfig,
- ...moduleOptions,
- ...additionalProperties,
- ...getLocaleSettings(moduleOptions, app)
- };
-};
-*/
-
-export const M2_CART_ID_COOKIE_NAME = 'M2_CART_ID';
-export const M2_CUSTOMER_TOKEN_COOKIE_NAME = 'M2_CUSTOMER_TOKEN';
-export const M2_STORE_COOKIE_NAME = 'M2_STORE';
\ No newline at end of file
diff --git a/packages/composables/nuxt/helpers/index.js b/packages/composables/nuxt/helpers/index.js
index ea1c2d415..92769dc4f 100644
--- a/packages/composables/nuxt/helpers/index.js
+++ b/packages/composables/nuxt/helpers/index.js
@@ -1,64 +1,26 @@
-import {
- M2_CART_ID_COOKIE_NAME,
- M2_CUSTOMER_TOKEN_COOKIE_NAME,
- M2_STORE_COOKIE_NAME
-} from './consts';
+import defaultConfig from '@vue-storefront/magento/nuxt/defaultConfig';
-const loadState = (app) => {
- let cartId = app.$cookies.get(M2_CART_ID_COOKIE_NAME);
- let customerToken = app.$cookies.get(M2_CUSTOMER_TOKEN_COOKIE_NAME);
- let store = app.$cookies.get(M2_STORE_COOKIE_NAME);
+export const getLocaleSettings = (app, moduleOptions) => {
+ let localeSettings = {};
- const getCartId = () => {
- return cartId;
+ if (moduleOptions.cookies) {
+ localeSettings = {
+ locale: app.$cookies.get(moduleOptions.cookies.localeCookieName),
+ country: app.$cookies.get(moduleOptions.cookies.currencyCookieName),
+ currency: app.$cookies.get(moduleOptions.cookies.countryCookieName),
};
-
- const setCartId = (id) => {
- if (!id) {
- app.$cookies.remove(M2_CART_ID_COOKIE_NAME);
- return;
- }
- app.$cookies.set(M2_CART_ID_COOKIE_NAME, id);
- cartId = id;
- }
-
- const getCustomerToken = () => {
- return customerToken;
- }
-
- const setCustomerToken = (token) => {
- if(!token) {
- app.$cookies.remove(M2_CUSTOMER_TOKEN_COOKIE_NAME);
- return;
- }
- app.$cookies.set(M2_CUSTOMER_TOKEN_COOKIE_NAME, token);
- customerToken = token;
- }
-
- const getStore = () => {
- //return store;
- return 'ru';
- }
-
- const setStore = (id) => {
- if (!id) {
- app.$cookies.remove(M2_STORE_COOKIE_NAME);
- return;
- }
- app.$cookies.set(M2_STORE_COOKIE_NAME, id);
- store = id;
- }
-
- return {
- getCartId,
- setCartId,
- getCustomerToken,
- setCustomerToken,
- getStore,
- setStore
- }
-}
-
-export {
- loadState
-}
\ No newline at end of file
+ }
+
+ return {
+ locale: app.i18n.locale || (localeSettings.locale || moduleOptions.locale || defaultConfig.locale),
+ country: localeSettings.country || moduleOptions.country || defaultConfig.country,
+ currency: localeSettings.currency || moduleOptions.currency || defaultConfig.currency,
+ };
+};
+
+export const mapConfigToSetupObject = ({ moduleOptions, app, additionalProperties = {} }) => ({
+ ...defaultConfig,
+ ...moduleOptions,
+ ...additionalProperties,
+ ...getLocaleSettings(app, moduleOptions),
+});
diff --git a/packages/composables/nuxt/index.js b/packages/composables/nuxt/index.js
index 3d8c03079..05255ef1d 100644
--- a/packages/composables/nuxt/index.js
+++ b/packages/composables/nuxt/index.js
@@ -1,8 +1,48 @@
import path from 'path';
+const mapI18nSettings = (i18n) => ({
+ locale: i18n.defaultLocale,
+ currency: i18n.currency,
+ country: i18n.country,
+ acceptLanguage: i18n.locales.map(({ code }) => code),
+ countries: i18n.countries,
+ currencies: i18n.currencies,
+ locales: i18n.locales.map(({ label, code }) => ({ name: code, label })),
+});
+
+const isNuxtI18nUsed = (moduleOptions) => moduleOptions.i18n && moduleOptions.i18n.useNuxtI18nConfig;
+
+const getMissingFields = (options) => [
+ 'locale',
+ 'currency',
+ 'country',
+ 'acceptLanguage',
+ 'countries',
+ 'currencies',
+ 'locales',
+].filter((o) => options[o] === undefined);
+
export default function (moduleOptions) {
+ const options = isNuxtI18nUsed(moduleOptions)
+ ? {
+ ...moduleOptions,
+ ...mapI18nSettings(this.options.i18n),
+ }
+ : moduleOptions;
+
+ const missingFields = getMissingFields(options);
+
+ if (missingFields.length > 0) {
+ throw new Error(`Please provide missing i18n fields: (${missingFields.join(', ')})`);
+ }
+
+ this.extendBuild((config) => {
+ // eslint-disable-next-line no-param-reassign
+ config.resolve.alias['@vue-storefront/magento-api$'] = require.resolve('@vue-storefront/magento-api');
+ });
+
this.addPlugin({
src: path.resolve(__dirname, './plugin.js'),
- options: moduleOptions
+ options,
});
-}
\ No newline at end of file
+}
diff --git a/packages/composables/nuxt/plugin.js b/packages/composables/nuxt/plugin.js
index d7163b324..f32656484 100644
--- a/packages/composables/nuxt/plugin.js
+++ b/packages/composables/nuxt/plugin.js
@@ -1,43 +1,58 @@
-import { integrationPlugin } from '@vue-storefront/magento';
-import {
- loadState
-} from '@vue-storefront/magento/nuxt/helpers';
+import { integrationPlugin } from '@vue-storefront/core'
+import { mapConfigToSetupObject } from '@vue-storefront/magento/nuxt/helpers';
+import defaultConfig from '@vue-storefront/magento/nuxt/defaultConfig';
const moduleOptions = JSON.parse('<%= JSON.stringify(options) %>');
-// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
export default integrationPlugin(({ app, integration }) => {
- const settings = {
- api: 'https://miuz.cyberfuze.com/graphql',
- websites: {
- base: {
- code: 'base',
- defaultStoreGroup: 'main_website_store',
- storeGroups: {
- // eslint-disable-next-line @typescript-eslint/camelcase,camelcase
- main_website_store: {
- code: 'main_website_store',
- defaultStore: 'default',
- stores: {
- default: {
- code: 'default'
- },
- ru: {
- code: 'ru'
- },
- en: {
- code: 'us'
- }
- }
- }
- }
- }
- },
- defaultStore: 'default'
+ const cartCookieName = moduleOptions.cookies?.cartCookieName || defaultConfig.cookies.cartCookieName;
+ const customerCookieName = moduleOptions.cookies?.customerCookieName || defaultConfig.cookies.customerCookieName;
+ const storeCookieName = moduleOptions.cookies?.storeCookieName || defaultConfig.cookies.storeCookieName;
+
+ const getCartId = () => app.$cookies.get(cartCookieName);
+
+ const setCartId = (id) => {
+ if (!id) {
+ app.$cookies.remove(cartCookieName);
+ return;
+ }
+ app.$cookies.set(cartCookieName, id);
+ };
+
+ const getCustomerToken = () => app.$cookies.get(customerCookieName);
+
+ const setCustomerToken = (token) => {
+ if (!token) {
+ app.$cookies.remove(customerCookieName);
+ return;
+ }
+ app.$cookies.set(customerCookieName, token);
+ };
+
+ const getStore = () => app.$cookies.get(storeCookieName);
+
+ const setStore = (id) => {
+ if (!id) {
+ app.$cookies.remove(storeCookieName);
+ return;
+ }
+ app.$cookies.set(storeCookieName, id);
};
-
- const state = loadState(app, settings);
+ const settings = mapConfigToSetupObject({
+ moduleOptions,
+ app,
+ additionalProperties: {
+ state: {
+ getCartId,
+ setCartId,
+ getCustomerToken,
+ setCustomerToken,
+ getStore,
+ setStore,
+ },
+ }
+ });
- integration.configure({ ...moduleOptions, ...settings, state });
+ integration.configure('magento', settings);
});
diff --git a/packages/composables/package.json b/packages/composables/package.json
index 46cc4267d..7a0ccb6f7 100644
--- a/packages/composables/package.json
+++ b/packages/composables/package.json
@@ -1,24 +1,32 @@
{
"name": "@vue-storefront/magento",
- "version": "0.0.1",
+ "version": "1.0.0-beta.1",
"sideEffects": false,
"main": "lib/index.cjs.js",
"module": "lib/index.es.js",
+ "tsModule": "src/index.ts",
"types": "lib/index.d.ts",
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w",
- "test": "jest"
+ "test": "jest",
+ "lint:fix": "eslint ./src --ext .ts,.vue --fix",
+ "prepublish": "yarn build",
+ "update:check": "ncu",
+ "update:update": "ncu -u"
},
"dependencies": {
- "@vue-storefront/magento-api": "^0.0.1",
- "@vue-storefront/core": "^2.2.1",
+ "@vue-storefront/magento-api": "^1.0.0-beta.1",
+ "@vue-storefront/core": "^2.3.0-rc.3",
"vue": "^2.6.x"
},
"devDependencies": {
- "@vue/test-utils": "^1.0.0-beta.30",
+ "@vue/test-utils": "^1.1.4",
"vue-template-compiler": "^2.6.x"
},
+ "peerDependencies": {
+ "@vue/composition-api": "1.0.0-beta.21"
+ },
"files": [
"lib/**/*"
],
diff --git a/packages/composables/rollup.config.js b/packages/composables/rollup.config.js
index e834ad807..10dc02f5e 100644
--- a/packages/composables/rollup.config.js
+++ b/packages/composables/rollup.config.js
@@ -1,4 +1,4 @@
import pkg from './package.json';
-import { generateBaseConfig } from '../rollup.base.config';
+import { generateBaseConfig } from '../../rollup.base.config';
export default generateBaseConfig(pkg);
diff --git a/packages/composables/src/composables/getters/_utils.ts b/packages/composables/src/composables/getters/_utils.ts
new file mode 100644
index 000000000..1170ad53a
--- /dev/null
+++ b/packages/composables/src/composables/getters/_utils.ts
@@ -0,0 +1,24 @@
+import { AgnosticAttribute } from '@vue-storefront/core';
+import { Product, ConfigurableOption } from '@vue-storefront/magento-api';
+
+export const getAttributeValue = (attribute) => attribute.values;
+
+export const formatAttributeList = (attributes: ConfigurableOption[]): AgnosticAttribute[] => attributes.map((attr) => {
+ const attrValue = getAttributeValue(attr);
+ return {
+ name: attr.attribute_code,
+ value: attrValue,
+ label: attr.label,
+ };
+});
+
+export const getVariantByAttributes = (products: Product[], attributes: any): Product => {
+ if (!products || products.length === 0) {
+ return null;
+ }
+
+ const configurationKeys = Object.keys(attributes);
+
+ return products[0].configurable_children.find((product) => configurationKeys
+ .every((attrName) => product[attrName] && product[attrName] === attributes[attrName])) || products[0].configurable_children[0];
+};
diff --git a/packages/composables/src/composables/getters/cartGetters.ts b/packages/composables/src/composables/getters/cartGetters.ts
index feb6526a0..9c484e573 100644
--- a/packages/composables/src/composables/getters/cartGetters.ts
+++ b/packages/composables/src/composables/getters/cartGetters.ts
@@ -1,5 +1,8 @@
-import { CartGetters, AgnosticPrice, AgnosticTotals, AgnosticAttribute, AgnosticCoupon, AgnosticDiscount } from '@vue-storefront/core';
-import { Cart, CartItem } from '../../types';
+import {
+ CartGetters, AgnosticPrice, AgnosticTotals, AgnosticAttribute, AgnosticCoupon, AgnosticDiscount,
+} from '@vue-storefront/core';
+import { Discount, Cart } from '@vue-storefront/magento-api';
+import { CartItem } from '../../types';
import productGetters from './productGetters';
// import { settings } from '@vue-storefront/magento-api';
@@ -14,34 +17,28 @@ export const getCartItems = (cart: Cart): CartItem[] => {
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export const getCartItemName = (product: CartItem): string => {
- return productGetters.getName(product.product);
-};
+export const getCartItemName = (product: CartItem): string => productGetters.getName(product.product);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export const getCartItemImage = (product: CartItem): string => {
- return productGetters.getCoverImage(product.product);
-};
+export const getCartItemImage = (product: CartItem): string => productGetters.getCoverImage(product.product);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const getCartItemPrice = (product: CartItem): AgnosticPrice => {
if (!product || !product.prices) {
return {
regular: 0,
- special: 0
- }
- }
-
- const regular = product.prices.row_total.value;
- let special = null;
- const discount = product.prices.total_item_discount.value;
- if (discount > 0) {
- special = regular - discount;
+ special: 0,
+ };
}
return {
- regular: regular,
- special: special
+ regular: product.product?.regular_price || 0,
+ special: product.product?.price_range?.minimum_price?.final_price?.value || 0,
+ // @ts-ignore
+ credit: Math.round(product.product?.price_range?.minimum_price?.final_price?.value / 10),
+ installment: Math.round((product.product?.price_range?.minimum_price?.final_price?.value * 1.1046) / 10),
+ discountPercentage: 100 - Math.round((product.product?.price_range?.minimum_price?.final_price?.value / product.product?.regular_price) * 100),
+ total: product.prices?.row_total?.value,
};
};
@@ -50,7 +47,7 @@ export const getCartItemQty = (product: CartItem): number => product.quantity;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const getCartItemAttributes = (product: CartItem, filterByAttributeName?: Array): Record => {
const attributes = {};
- if (!product.configurable_options) {
+ if (!product || !product.configurable_options) {
return attributes;
}
for (const option of product.configurable_options) {
@@ -66,26 +63,16 @@ export const getCartItemSku = (product: CartItem): string => {
return product.product.sku;
};
-export const getCartTotals = (cart: Cart): AgnosticTotals => {
- if (!cart || !cart.prices.grand_total || !cart.prices.subtotal_including_tax || !cart.prices.subtotal_excluding_tax) {
- return {
- total: 0,
- subtotal: 0
- };
- }
-
- const totals = {
- subtotal: 0,
- total: cart.prices.grand_total.value
- };
+const calculateDiscounts = (discounts: Discount[]): number => discounts.reduce((a, b) => Number.parseFloat(a) + Number.parseFloat(b.amount.value), 0);
- if (settings.tax.displayCartSubtotalIncludingTax) {
- totals.subtotal = cart.prices.subtotal_including_tax.value;
- } else {
- totals.subtotal = cart.prices.subtotal_excluding_tax.value;
- }
+export const getCartTotals = (cart: Cart): AgnosticTotals => {
+ if (!cart || !cart.prices) return {} as AgnosticTotals;
- return totals;
+ return {
+ total: cart.prices.grand_total.value,
+ subtotal: cart.prices.subtotal_including_tax.value,
+ special: (cart.prices.discounts) ? calculateDiscounts(cart.prices.discounts) : 0,
+ } as AgnosticTotals;
};
export const getCartShippingPrice = (cart: Cart): number => {
@@ -110,12 +97,10 @@ export const getCartTotalItems = (cart: Cart): number => {
return cart.total_quantity;
};
-export const getFormattedPrice = (price: number) => {
- return productGetters.getFormattedPrice(price);
-};
+export const getFormattedPrice = (price: number) => productGetters.getFormattedPrice(price);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export const getCoupons = (cart: Cart): AgnosticCoupon[] => [];
+export const getCoupons = (cart: Cart): AgnosticCoupon[] => cart.applied_coupons as AgnosticCoupon[] || [];
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const getDiscounts = (cart: Cart): AgnosticDiscount[] => [];
@@ -131,9 +116,9 @@ const cartGetters: CartGetters = {
getTotals: getCartTotals,
getShippingPrice: getCartShippingPrice,
getTotalItems: getCartTotalItems,
- getFormattedPrice: getFormattedPrice,
- getCoupons: getCoupons,
- getDiscounts: getDiscounts
+ getFormattedPrice,
+ getCoupons,
+ getDiscounts,
};
export default cartGetters;
diff --git a/packages/composables/src/composables/getters/categoryGetters.ts b/packages/composables/src/composables/getters/categoryGetters.ts
index 1fb017252..f805ea97e 100644
--- a/packages/composables/src/composables/getters/categoryGetters.ts
+++ b/packages/composables/src/composables/getters/categoryGetters.ts
@@ -1,11 +1,11 @@
-import {CategoryGetters, AgnosticCategoryTree, AgnosticBreadcrumb} from '@vue-storefront/core';
-import {Category} from '@vue-storefront/magento-api';
+import { CategoryGetters, AgnosticCategoryTree, AgnosticBreadcrumb } from '@vue-storefront/core';
+import { Category } from '@vue-storefront/magento-api';
const buildTree = (rootCategory: Category): AgnosticCategoryTree => ({
label: rootCategory.name,
- slug: '/' + rootCategory.url_path + rootCategory.url_suffix,
+ slug: `/${rootCategory.url_path}${rootCategory.url_suffix}`,
items: rootCategory.children.map(buildTree),
- isCurrent: false
+ isCurrent: false,
});
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -17,22 +17,22 @@ export const getCategoryTree = (category: Category): AgnosticCategoryTree | null
};
export const getCategoryBreadcrumbs = (category: Category): AgnosticBreadcrumb[] => {
+ let breadcrumbs = [];
+
if (!category) {
return [];
}
- let breadcrumbs = [];
- if (category.breadcrumbs !== null) {
- breadcrumbs = category.breadcrumbs.map((breadcrumb) => {
- return {
- text: breadcrumb.category_name,
- link: '/' + breadcrumb.category_url_path + category.url_suffix
- } as AgnosticBreadcrumb;
- });
+
+ if (Array.isArray(category?.breadcrumbs)) {
+ breadcrumbs = category.breadcrumbs.map((breadcrumb) => ({
+ text: breadcrumb.category_name,
+ link: `/${breadcrumb.category_url_path}${category.url_suffix || ''}`,
+ } as AgnosticBreadcrumb));
}
breadcrumbs.push({
text: category.name,
- link: '/' + category.url_path + category.url_suffix
+ link: `/${category.url_path}${category.url_suffix || ''}`,
} as AgnosticBreadcrumb);
return breadcrumbs;
@@ -40,7 +40,7 @@ export const getCategoryBreadcrumbs = (category: Category): AgnosticBreadcrumb[]
const categoryGetters: CategoryGetters = {
getTree: getCategoryTree,
- getBreadCrumbs: getCategoryBreadcrumbs
+ getBreadCrumbs: getCategoryBreadcrumbs,
};
export default categoryGetters;
diff --git a/packages/composables/src/composables/getters/checkoutGetters.ts b/packages/composables/src/composables/getters/checkoutGetters.ts
index b79a17991..b854df3e9 100644
--- a/packages/composables/src/composables/getters/checkoutGetters.ts
+++ b/packages/composables/src/composables/getters/checkoutGetters.ts
@@ -1,5 +1,5 @@
-import { CheckoutGetters} from '@vue-storefront/core';
-import { ShippingMethod } from '@vue-storefront/magento-api/src/types';
+import { CheckoutGetters } from '@vue-storefront/core';
+import { ShippingMethod } from '@vue-storefront/magento-api';
import productGetters from './productGetters';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -14,16 +14,14 @@ export const getShippingMethodDescription = (shippingMethod: ShippingMethod): st
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const getShippingMethodPrice = (shippingMethod: ShippingMethod): number => 0;
-export const getFormattedPrice = (price: number) => {
- return productGetters.getFormattedPrice(price);
-};
+export const getFormattedPrice = (price: number) => productGetters.getFormattedPrice(price);
const checkoutGetters: CheckoutGetters = {
getShippingMethodId,
getShippingMethodName,
getShippingMethodDescription,
getFormattedPrice,
- getShippingMethodPrice
+ getShippingMethodPrice,
};
export default checkoutGetters;
diff --git a/packages/composables/src/composables/getters/facetGetters.ts b/packages/composables/src/composables/getters/facetGetters.ts
index e763ca2fb..b50e8725c 100644
--- a/packages/composables/src/composables/getters/facetGetters.ts
+++ b/packages/composables/src/composables/getters/facetGetters.ts
@@ -8,52 +8,46 @@ import {
AgnosticFacet,
} from '@vue-storefront/core';
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
-const getAll = (searchData, criteria?: string[]): AgnosticFacet[] => [];
+import {
+ buildBreadcrumbs,
+ buildFacets,
+ reduceForGroupedFacets,
+ reduceForFacets,
+} from '../useFacet/_utils';
+
+import {
+ SearchData,
+} from '../../types';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-const getGrouped = (searchData, criteria?: string[]): AgnosticGroupedFacet[] => {
- if (!searchData.data.availableFilters)
- return [];
-
- const grouped = searchData.data.availableFilters.map(item => {
- return {
- id: item.attribute_code,
- label: item.default_frontend_label,
- options: item.options.map(option => {
- return {
- type: '',
- id: option.value,
- value: option.label,
- selected: (searchData?.input?.filters &&
- searchData?.input?.filters[item.attribute_code] &&
- searchData?.input?.filters[item.attribute_code].includes(option.value)) ? true : false
- }
- })
- }
- });
- return grouped;
-};
+const getAll = (searchData: SearchData, criteria?: string[]): AgnosticFacet[] => buildFacets(searchData, reduceForFacets, criteria);
+
+const getGrouped = (searchData, criteria?: string[]): AgnosticGroupedFacet[] => buildFacets(searchData, reduceForGroupedFacets, criteria)
+ .filter((facet) => facet.options.length > 0);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const getSortOptions = (searchData): AgnosticSort => {
- if(!searchData.data)
- return {options: [], selected: ''} as AgnosticSort;
-
- return { options: searchData.data.availableSortingOptions, selected: searchData.input.sort };
+ if (!searchData || !searchData.data || !searchData.data.availableSortingOptions) {
+ return {
+ options: [],
+ selected: '',
+ } as AgnosticSort;
+ }
+
+ return {
+ options: searchData.data.availableSortingOptions,
+ selected: searchData.input.sort,
+ };
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const getCategoryTree = (searchData): AgnosticCategoryTree => {
- console.log(searchData);
- const buildTree = (category: any): AgnosticCategoryTree => {
- return {
- label: category.name,
- slug: category.url_path,
- items: (category.children) ? category.children.map(buildTree) : [],
- isCurrent: false
- };
- };
+ const buildTree = (category: any): AgnosticCategoryTree => ({
+ label: category.name,
+ slug: category.url_path,
+ items: (category.children) ? category.children.map(buildTree) : [],
+ isCurrent: (category.name === searchData.data.category.name),
+ });
if (!searchData.data || !searchData.data.category.children) {
return {} as AgnosticCategoryTree;
@@ -64,6 +58,9 @@ const getCategoryTree = (searchData): AgnosticCategoryTree => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const getProducts = (searchData): any => {
+ if (!searchData || !searchData.data || !searchData.data.items) {
+ return [];
+ }
return searchData.data.items;
};
@@ -73,7 +70,7 @@ const getPagination = (searchData): AgnosticPagination => ({
totalPages: (searchData.data) ? Math.ceil(searchData.data.total / searchData.input.itemsPerPage) : 0,
totalItems: (searchData.data) ? searchData.data.total : 0,
itemsPerPage: searchData.input.itemsPerPage,
- pageOptions: [10, 50, 100]
+ pageOptions: [10, 50, 100],
});
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -86,7 +83,7 @@ const facetGetters: FacetsGetters = {
getProducts,
getCategoryTree,
getBreadcrumbs,
- getPagination
+ getPagination,
};
export default facetGetters;
diff --git a/packages/composables/src/composables/getters/index.ts b/packages/composables/src/composables/getters/index.ts
index 2f3b74177..df4646352 100644
--- a/packages/composables/src/composables/getters/index.ts
+++ b/packages/composables/src/composables/getters/index.ts
@@ -1,24 +1,12 @@
/* istanbul ignore file */
-import cartGetters from './cartGetters';
-import categoryGetters from './categoryGetters';
-import checkoutGetters from './checkoutGetters';
-import productGetters from './productGetters';
-import userGetters from './userGetters';
-import orderGetters from './orderGetters';
-import wishlistGetters from './wishlistGetters';
-import facetGetters from './facetGetters';
-import userShippingGetters from './userShippingGetters';
-import userBillingGetters from './userBillingGetters';
-
-export {
- cartGetters,
- categoryGetters,
- checkoutGetters,
- productGetters,
- userGetters,
- orderGetters,
- wishlistGetters,
- facetGetters,
- userShippingGetters,
- userBillingGetters
-};
+export { default as cartGetters } from './cartGetters';
+export { default as categoryGetters } from './categoryGetters';
+export { default as checkoutGetters } from './checkoutGetters';
+export { default as productGetters } from './productGetters';
+export { default as userGetters } from './userGetters';
+export { default as orderGetters } from './orderGetters';
+export { default as wishlistGetters } from './wishlistGetters';
+export { default as facetGetters } from './facetGetters';
+export { default as userShippingGetters } from './userShippingGetters';
+export { default as userBillingGetters } from './userBillingGetters';
+export { default as reviewGetters } from './reviewGetters';
diff --git a/packages/composables/src/composables/getters/orderGetters.ts b/packages/composables/src/composables/getters/orderGetters.ts
index 47857d6b6..80ce82e01 100644
--- a/packages/composables/src/composables/getters/orderGetters.ts
+++ b/packages/composables/src/composables/getters/orderGetters.ts
@@ -1,34 +1,33 @@
/* istanbul ignore file */
-
import { UserOrderGetters } from '@vue-storefront/core';
import { Order, OrderItem } from '../../types';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export const getDate = (order: Order): string => '123';
+export const getDate = (order: any): string => order?.created_at || '123';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export const getId = (order: Order): string => '123';
+export const getId = (order: any): string => order?.id || Math.floor(Math.random() * 100);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export const getStatus = (order: Order): string => '';
+export const getStatus = (order: any): string => order?.status || 'Failed';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export const getPrice = (order: Order): number | null => 0;
+export const getPrice = (order: any): number | null => order?.grand_total || 0;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export const getItems = (order: Order): OrderItem[] => [];
+export const getItems = (order: any): any[] => order?.items || [];
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export const getItemSku = (item: OrderItem): string => '';
+export const getItemSku = (item: any): string => item?.product_sku || 0;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export const getItemName = (item: OrderItem): string => '';
+export const getItemName = (item: any): string => item?.product_name || 0;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export const getItemQty = (item: OrderItem): number => 0;
+export const getItemQty = (item: any): number => item?.quantity_ordered || 0;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export const getItemPrice = (item: any): number => item?.price?.current || 0;
+export const getItemPrice = (item: any): number => item?.product_sale_price?.value || 0;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const getFormattedPrice = (price: number) => String(price);
@@ -43,7 +42,7 @@ const orderGetters: UserOrderGetters = {
getItemName,
getItemQty,
getItemPrice,
- getFormattedPrice
+ getFormattedPrice,
};
export default orderGetters;
diff --git a/packages/composables/src/composables/getters/productGetters.ts b/packages/composables/src/composables/getters/productGetters.ts
index b0173d9e4..c32ab1f8e 100644
--- a/packages/composables/src/composables/getters/productGetters.ts
+++ b/packages/composables/src/composables/getters/productGetters.ts
@@ -3,46 +3,45 @@ import {
AgnosticBreadcrumb,
AgnosticMediaGalleryItem,
AgnosticPrice,
- ProductGetters
+ ProductGetters,
} from '@vue-storefront/core';
-import {
- CategoryInterface as Category,
- ProductInterface as Product
+import {
+ Category,
+ Product,
} from '@vue-storefront/magento-api';
import categoryGetters from './categoryGetters';
-type ProductVariantFilters = any
-
-// Product
+type ProductVariantFilters = any;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const getProductName = (product: Product): string => {
if (!product) {
return '';
}
+
return product.name;
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const getProductSlug = (product: Product, category?: Category): string => {
const rewrites = product.url_rewrites;
- let url = '/p/' + product.sku;
+ let url = `/p/${product.sku}`;
if (!rewrites || rewrites.length === 0) {
return url;
}
- url = '/' + rewrites[0].url;
+ url = `/${rewrites[0].url}`;
loopOuter:
- for (let i = 0; i < rewrites.length; i++) {
+ for (let i = 0; i < rewrites.length; i += 1) {
const rewrite = rewrites[i];
if (category && category.id && rewrite.parameters) {
- for (let j = 0; j < rewrite.parameters.length; j++) {
+ for (let j = 0; j < rewrite.parameters.length; j += 1) {
const parameter = rewrite.parameters[j];
// eslint-disable-next-line max-depth
- if (parameter.name === 'category' && parseInt(parameter.value) === category.id) {
- url = '/' + rewrite.url;
+ if (parameter.name === 'category' && parseInt(parameter.value, 10) === category.id) {
+ url = `/${rewrite.url}`;
break loopOuter;
}
}
@@ -56,33 +55,36 @@ export const getProductSlug = (product: Product, category?: Category): string =>
export const getProductPrice = (product: Product): AgnosticPrice => {
let regular = 0;
let special = null;
- if (product.price_range) {
+
+ if (product?.price_range) {
regular = product.price_range.minimum_price.regular_price.value;
const final = product.price_range.minimum_price.final_price.value;
+
if (final < regular) {
special = final;
}
}
return {
- regular: regular,
- special: special
+ regular,
+ special,
};
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const getProductGallery = (product: Product): AgnosticMediaGalleryItem[] => {
const images = [];
- if (!product.media_gallery) {
+
+ if (!product?.media_gallery) {
return images;
}
- for (let i = 0; i < product.media_gallery.length; i++) {
+ for (let i = 0; i < product.media_gallery.length; i += 1) {
const galleryItem = product.media_gallery[i];
images.push({
small: galleryItem.url,
normal: galleryItem.url,
- big: galleryItem.url
+ big: galleryItem.url,
});
}
@@ -91,48 +93,45 @@ export const getProductGallery = (product: Product): AgnosticMediaGalleryItem[]
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const getProductCoverImage = (product: Product): string => {
- if (!product) {
+ if (!product || !product.image) {
return null;
}
- if (!product.small_image) {
- return '';
- }
-
- return product.small_image.url;
+ return product.image.url;
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export const getProductFiltered = (products: Product[], filters: ProductVariantFilters | any = {}): Product[] => {
- return products;
-};
+export const getProductFiltered = (products: Product[], filters: ProductVariantFilters | any = {}): Product[] => products;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const getProductAttributes = (products: Product[] | Product, filterByAttributeName?: string[]): Record => {
- if (!products || !products.configurable_options) {
+ if (!products || !products?.configurable_options) {
return {};
}
+
const attributes = {};
const configurableOptions = products.configurable_options;
- for (let i = 0; i < configurableOptions.length; i++) {
+
+ for (let i = 0; i < configurableOptions.length; i += 1) {
const option = configurableOptions[i];
attributes[option.attribute_code] = {
name: option.attribute_code,
label: option.label,
value: option.values.map((value) => {
- let obj = {};
- obj[value.value_index] = value.value_label;
+ const obj = {};
+ obj[value.value_index] = value.label;
return obj;
- })
+ }),
} as AgnosticAttribute;
}
return attributes;
};
export const getProductDescription = (product: Product): string => {
- if (!product || !product.description) {
+ if (!product || !product?.description) {
return '';
}
+
return product.description.html;
};
@@ -146,35 +145,41 @@ export const getProductShortDescription = (product: Product): string => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const getProductCategoryIds = (product: Product): string[] => {
const categoryIds = [];
- if (!product.categories) {
+
+ if (!product?.categories) {
return categoryIds;
}
- for (const category of product.categories) {
- categoryIds.push(category.id);
- }
- return categoryIds;
+
+ return product.categories.map((category) => category.id);
};
export const getProductCategory = (product: Product, currentUrlPath: string): Category | null => {
- if (!product.categories || product.categories.length === 0) {
+ if (!product?.categories || product?.categories.length === 0) {
return null;
}
+
const categories = currentUrlPath.split('/');
categories.pop();
+
if (categories.length === 0) {
return null;
- } else {
- const categoryPath = categories.join('/');
- for (const category of product.categories) {
- if ('/' + category.url_path === categoryPath) {
- return category;
- }
+ }
+
+ const categoryPath = categories.join('/');
+
+ for (const category of product.categories) {
+ if (`/${category.url_path}` === categoryPath) {
+ return category;
}
}
+
return null;
};
export const getProductId = (product: Product): string => product.id;
+
+export const getProductSku = (product: Product): string => product.sku;
+
export const getProductTypeId = (product: Product): string => product.type_id;
export const getFormattedPrice = (price: number) => {
@@ -187,17 +192,19 @@ export const getFormattedPrice = (price: number) => {
const country = 'en';
const currency = 'USD';
- return new Intl.NumberFormat(`${ locale }-${ country }`, {
+ return new Intl.NumberFormat(`${locale}-${country}`, {
style: 'currency',
- currency
+ currency,
}).format(price);
};
export const getProductBreadcrumbs = (product: Product, category?: Category): AgnosticBreadcrumb[] => {
+ let breadcrumbs = [];
+
if (!product) {
- return [];
+ return breadcrumbs;
}
- let breadcrumbs = [];
+
if (category) {
breadcrumbs = categoryGetters.getBreadCrumbs(category) as AgnosticBreadcrumb[];
}
@@ -205,14 +212,14 @@ export const getProductBreadcrumbs = (product: Product, category?: Category): Ag
breadcrumbs.push({
text: getProductName(product),
route: {
- path: getProductSlug(product)
- }
+ path: getProductSlug(product),
+ },
});
return breadcrumbs;
};
-export const getProductWishlistState = (product: Product): boolean => product.isOnWishlist;
+export const getProductWishlistState = (product: Product): boolean => product?.isOnWishlist;
export const getProductTotalReviews = (): number => 0;
@@ -232,13 +239,13 @@ const productGetters: ProductGetters = {
getCategoryIds: getProductCategoryIds,
getCategory: getProductCategory,
getId: getProductId,
- getFormattedPrice: getFormattedPrice,
+ getFormattedPrice,
getBreadcrumbs: getProductBreadcrumbs,
getTypeId: getProductTypeId,
getWishlistState: getProductWishlistState,
-
getTotalReviews: getProductTotalReviews,
getAverageRating: getProductAverageRating,
+ getProductSku,
};
export default productGetters;
diff --git a/packages/composables/src/composables/getters/reviewGetters.ts b/packages/composables/src/composables/getters/reviewGetters.ts
new file mode 100644
index 000000000..928258dbc
--- /dev/null
+++ b/packages/composables/src/composables/getters/reviewGetters.ts
@@ -0,0 +1,51 @@
+/* istanbul ignore file */
+import { ReviewGetters, AgnosticRateCount } from '@vue-storefront/core';
+
+// TODO: Replace with GraphQL types when they get updated
+type Review = any;
+type ReviewItem = any;
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+export const getItems = (review: Review): ReviewItem[] => [];
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+export const getReviewId = (item: ReviewItem): string => '';
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+export const getReviewAuthor = (item: ReviewItem): string => '';
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+export const getReviewMessage = (item: ReviewItem): string => '';
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+export const getReviewRating = (item: ReviewItem): number => 0;
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+export const getReviewDate = (item: ReviewItem): string => '';
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+export const getTotalReviews = (review: Review): number => 0;
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+export const getAverageRating = (review: Review): number => 0;
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+export const getRatesCount = (review: Review): AgnosticRateCount[] => [];
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+export const getReviewsPage = (review: Review): number => 1;
+
+const reviewGetters: ReviewGetters = {
+ getItems,
+ getReviewId,
+ getReviewAuthor,
+ getReviewMessage,
+ getReviewRating,
+ getReviewDate,
+ getTotalReviews,
+ getAverageRating,
+ getRatesCount,
+ getReviewsPage,
+};
+
+export default reviewGetters;
diff --git a/packages/composables/src/composables/getters/userBillingGetters.ts b/packages/composables/src/composables/getters/userBillingGetters.ts
index fd3983ac9..a7f6703d0 100644
--- a/packages/composables/src/composables/getters/userBillingGetters.ts
+++ b/packages/composables/src/composables/getters/userBillingGetters.ts
@@ -7,27 +7,27 @@ const userBillingGetters: UserBillingGetters = {
}
const entries = Object.entries(criteria);
- return billing.addresses.filter(address => entries.every(([key, value]) => address[key] === value));
+ return billing.addresses.filter((address) => entries.every(([key, value]) => address[key] === value));
},
- getDefault: billing => billing.addresses.find(({ isDefault }) => isDefault),
- getTotal: billing => billing.addresses.length,
+ getDefault: (billing) => billing.addresses.find(({ isDefault }) => isDefault),
+ getTotal: (billing) => billing.addresses.length,
- getPostCode: address => address?.postalCode || '',
- getStreetName: address => address?.streetName || '',
- getStreetNumber: address => address?.streetNumber || '',
- getCity: address => address?.city || '',
- getFirstName: address => address?.firstName || '',
- getLastName: address => address?.lastName || '',
- getCountry: address => address?.country || '',
- getPhone: address => address?.phone || '',
- getEmail: address => address?.email || '',
- getProvince: address => address?.state || '',
- getCompanyName: address => address?.company || '',
+ getPostCode: (address) => address?.postalCode || '',
+ getStreetName: (address) => address?.streetName || '',
+ getStreetNumber: (address) => address?.streetNumber || '',
+ getCity: (address) => address?.city || '',
+ getFirstName: (address) => address?.firstName || '',
+ getLastName: (address) => address?.lastName || '',
+ getCountry: (address) => address?.country || '',
+ getPhone: (address) => address?.phone || '',
+ getEmail: (address) => address?.email || '',
+ getProvince: (address) => address?.state || '',
+ getCompanyName: (address) => address?.company || '',
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- getTaxNumber: address => '',
- getId: address => address?.id || '',
- getApartmentNumber: address => address?.apartment || '',
- isDefault: address => address?.isDefault || false
+ getTaxNumber: (address) => '',
+ getId: (address) => address?.id || '',
+ getApartmentNumber: (address) => address?.apartment || '',
+ isDefault: (address) => address?.isDefault || false,
};
export default userBillingGetters;
diff --git a/packages/composables/src/composables/getters/userGetters.ts b/packages/composables/src/composables/getters/userGetters.ts
index a05592d87..1ac616bf9 100644
--- a/packages/composables/src/composables/getters/userGetters.ts
+++ b/packages/composables/src/composables/getters/userGetters.ts
@@ -1,6 +1,5 @@
/* istanbul ignore file */
-
-import { UserGetters} from '@vue-storefront/core';
+import { UserGetters } from '@vue-storefront/core';
import { User } from '../../types';
export const getUserFirstName = (user: User): string => {
@@ -8,20 +7,16 @@ export const getUserFirstName = (user: User): string => {
return user ? user.firstname : '';
};
-export const getUserLastName = (user: User): string => {
- return user ? user.lastname : '';
-};
-export const getUserEmail = (user: User): string => {
- return user ? user.email : '';
-};
+export const getUserLastName = (user: User): string => (user ? user.lastname : '');
+export const getUserEmail = (user: User): string => (user ? user.email : '');
-export const getUserFullName = (user: User): string => user ? `${user.firstName} ${user.lastName}` : '';
+export const getUserFullName = (user: User): string => (user ? `${user.firstName} ${user.lastName}` : '');
const userGetters: UserGetters = {
getFirstName: getUserFirstName,
getLastName: getUserLastName,
getEmailAddress: getUserEmail,
- getFullName: getUserFullName
+ getFullName: getUserFullName,
};
export default userGetters;
diff --git a/packages/composables/src/composables/getters/userShippingGetters.ts b/packages/composables/src/composables/getters/userShippingGetters.ts
index 584132f21..12841636b 100644
--- a/packages/composables/src/composables/getters/userShippingGetters.ts
+++ b/packages/composables/src/composables/getters/userShippingGetters.ts
@@ -2,36 +2,35 @@ import { UserShippingGetters } from '@vue-storefront/core';
const userShippingGetters: UserShippingGetters = {
getAddresses: (shipping, criteria?: Record) => {
- console.log(shipping);
- if (!shipping || !shipping.addresses)
- return [] as Record;
-
+ console.log(shipping);
+ if (!shipping || !shipping.addresses) return [] as Record;
+
if (!criteria || !Object.keys(criteria).length) {
return shipping.addresses;
}
-
+
const entries = Object.entries(criteria);
- return shipping.addresses.filter(address => entries.every(([key, value]) => address[key] === value));
+ return shipping.addresses.filter((address) => entries.every(([key, value]) => address[key] === value));
},
- getDefault: shipping => shipping.addresses.find(({ isDefault }) => isDefault),
- getTotal: shipping => shipping.addresses.length,
+ getDefault: (shipping) => shipping.addresses.find(({ isDefault }) => isDefault),
+ getTotal: (shipping) => shipping.addresses.length,
- getPostCode: address => address?.postalCode || '',
- getStreetName: address => address?.streetName || '',
- getStreetNumber: address => address?.streetNumber || '',
- getCity: address => address?.city || '',
- getFirstName: address => address?.firstName || '',
- getLastName: address => address?.lastName || '',
- getCountry: address => address?.country || '',
- getPhone: address => address?.phone || '',
- getEmail: address => address?.email || '',
- getProvince: address => address?.state || '',
- getCompanyName: address => address?.company || '',
+ getPostCode: (address) => address?.postalCode || '',
+ getStreetName: (address) => address?.streetName || '',
+ getStreetNumber: (address) => address?.streetNumber || '',
+ getCity: (address) => address?.city || '',
+ getFirstName: (address) => address?.firstName || '',
+ getLastName: (address) => address?.lastName || '',
+ getCountry: (address) => address?.country || '',
+ getPhone: (address) => address?.phone || '',
+ getEmail: (address) => address?.email || '',
+ getProvince: (address) => address?.state || '',
+ getCompanyName: (address) => address?.company || '',
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- getTaxNumber: address => '',
- getId: address => address?.id || '',
- getApartmentNumber: address => address?.apartment || '',
- isDefault: address => address?.isDefault || false
+ getTaxNumber: (address) => '',
+ getId: (address) => address?.id || '',
+ getApartmentNumber: (address) => address?.apartment || '',
+ isDefault: (address) => address?.isDefault || false,
};
export default userShippingGetters;
diff --git a/packages/composables/src/composables/getters/wishlistGetters.ts b/packages/composables/src/composables/getters/wishlistGetters.ts
index 534c581a5..f453a0628 100644
--- a/packages/composables/src/composables/getters/wishlistGetters.ts
+++ b/packages/composables/src/composables/getters/wishlistGetters.ts
@@ -1,16 +1,16 @@
/* istanbul ignore file */
-
import {
WishlistGetters,
AgnosticPrice,
- AgnosticTotals
+ AgnosticTotals,
} from '@vue-storefront/core';
-import { WishlistProduct } from './../../types';
+import { WishlistOutput } from '@vue-storefront/magento-api';
+import { WishlistProduct } from '../../types';
-type Wishlist = {};
+type Wishlist = WishlistOutput;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export const getWishlistItems = (wishlist: Wishlist): WishlistProduct[] => [];
+export const getWishlistItems = (wishlist: Wishlist): WishlistProduct[] => wishlist.items;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const getWishlistItemName = (product: WishlistProduct): string => '';
@@ -25,7 +25,7 @@ export const getWishlistItemPrice = (product: WishlistProduct): AgnosticPrice =>
export const getWishlistItemQty = (product: WishlistProduct): number => 1;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export const getWishlistItemAttributes = (product: WishlistProduct, filterByAttributeName?: string[]) => ({'': ''});
+export const getWishlistItemAttributes = (product: WishlistProduct, filterByAttributeName?: string[]) => ({ '': '' });
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const getWishlistItemSku = (product: WishlistProduct): string => '';
@@ -37,7 +37,7 @@ export const getWishlistTotals = (wishlist: Wishlist): AgnosticTotals => ({ tota
export const getWishlistShippingPrice = (wishlist: Wishlist): number => 0;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export const getWishlistTotalItems = (wishlist: Wishlist): number => 0;
+export const getWishlistTotalItems = (wishlist: Wishlist): number => ((wishlist) ? wishlist.items_count : 0);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const getFormattedPrice = (price: number): string => '';
@@ -53,7 +53,7 @@ const wishlistGetters: WishlistGetters = {
getItemAttributes: getWishlistItemAttributes,
getItemSku: getWishlistItemSku,
getTotalItems: getWishlistTotalItems,
- getFormattedPrice
+ getFormattedPrice,
};
export default wishlistGetters;
diff --git a/packages/composables/src/composables/useBilling/index.ts b/packages/composables/src/composables/useBilling/index.ts
index 336816de8..8694c4ab2 100644
--- a/packages/composables/src/composables/useBilling/index.ts
+++ b/packages/composables/src/composables/useBilling/index.ts
@@ -1,28 +1,38 @@
import { Context } from '@vue-storefront/core';
-import { useBillingFactory, UseBillingParams } from '../../factories/useBillingFactory'
+import { useBillingFactory, UseBillingParams } from '../../factories/useBillingFactory';
import { Address } from '../../types';
-
-let details = {};
+import useCart from '../useCart';
const params: UseBillingParams = {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- load: async (context: Context, { customQuery }) => {
- console.log('[Magento] loadBilling');
- if (!context.cart.cart?.value?.billing_address) {
- await context.cart.load({ customQuery });
- }
- return context.cart.cart.value.billing_address;
- },
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ provide() {
+ return {
+ cart: useCart(),
+ };
+ },
+
+ load: async (context: Context, { customQuery }) => {
+ console.log('[Magento] loadBilling');
+ if (!context.cart.cart?.value?.billing_address) {
+ await context.cart.load({ customQuery });
+ }
+ return context.cart.cart.value.billing_address;
+ },
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- save: async (context: Context, { billingDetails, customQuery }) => {
- console.log('[Magento] setBillingAddress');
- const { id } = context.cart.cart.value;
- const setBillingAddressOnCartResponse = await context.$ma.api.setBillingAddressOnCart({
- cart_id: id,
- billing_address: {
- address: {
- /*
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ save: async (context: Context, { billingDetails, customQuery }) => {
+ console.log('[Magento] setBillingAddress');
+ const id = context.$magento.config.state.getCartId();
+ console.log(id);
+ console.log(typeof id);
+
+ // const { id } = context.cart.cart.value;
+ const setBillingAddressOnCartResponse = await context.$magento.api.setBillingAddressOnCart({
+ cart_id: id,
+ billing_address: {
+ address: {
+
+ /*
firstname: "Bob",
lastname: "Roll",
company: "Magento",
@@ -34,13 +44,13 @@ const params: UseBillingParams = {
telephone: "8675309",
save_in_address_book: false
*/
- ...billingDetails
- },
- same_as_shipping: false
- }
- });
- return setBillingAddressOnCartResponse.data.setBillingAddressOnCart.cart.billing_address;
- }
+ ...billingDetails,
+ },
+ same_as_shipping: true,
+ },
+ });
+ return setBillingAddressOnCartResponse.data.setBillingAddressOnCart.cart.billing_address;
+ },
};
export default useBillingFactory(params);
diff --git a/packages/composables/src/composables/useCart/index.ts b/packages/composables/src/composables/useCart/index.ts
index 8336e2936..7ec5baedf 100644
--- a/packages/composables/src/composables/useCart/index.ts
+++ b/packages/composables/src/composables/useCart/index.ts
@@ -1,17 +1,25 @@
/* istanbul ignore file */
-/* eslint-disable @typescript-eslint/camelcase */
-/* eslint-disable camelcase */
-import { useCartFactory, UseCartFactoryParams, Context } from '@vue-storefront/core';
-import { Cart, CartItem, Coupon, Product } from '../../types';
+/* eslint-disable no-param-reassign */
+import {
+ useCartFactory,
+ UseCartFactoryParams,
+ Context,
+} from '@vue-storefront/core';
+import {
+ Cart,
+ CartItem,
+ Coupon,
+ Product,
+} from '../../types';
const params: UseCartFactoryParams = {
load: async (context: Context) => {
- const apiState = context.$ma.config.state;
- // is user authincated.
+ const apiState = context.$magento.config.state;
+ // is user authenticated.
if (apiState.getCustomerToken()) {
try {
// get cart ID
- const result = await context.$ma.api.customerCart();
+ const result = await context.$magento.api.customerCart();
return result.data.customerCart;
} catch (e) {
// Signed up user don't have a cart.
@@ -20,105 +28,144 @@ const params: UseCartFactoryParams = {
return await params.load(context, {});
}
}
-
+
// if guest user have a cart ID
let cartId = apiState.getCartId();
if (!cartId) {
- const result = await context.$ma.api.createEmptyCart();
+ const result = await context.$magento.api.createEmptyCart();
cartId = result.data.createEmptyCart;
apiState.setCartId(cartId);
}
try {
- const cartResponse = await context.$ma.api.cart(cartId);
- //console.log(cartResponse);
+ const cartResponse = await context.$magento.api.cart(cartId);
+ // console.log(cartResponse);
return cartResponse.data.cart;
} catch (e) {
apiState.setCartId(null);
return await params.load(context, {});
}
},
- addItem: async (context: Context, { currentCart, product, quantity }) => {
+ addItem: async (context: Context, {
+ product,
+ quantity,
+ }) => {
+ const apiState = context.$magento.config.state;
+ let currentCartId = apiState.getCartId();
+
+ if (!currentCartId) {
+ await params.load(context, {});
+ currentCartId = apiState.getCartId();
+ }
+
+ if (!product) {
+ return;
+ }
+
+ product.type_id = 'simple';
switch (product.type_id) {
case 'simple':
- const response = await context.$ma.api.addSimpleProductsToCart({
- cart_id: currentCart.id,
+ const simpleProduct = await context.$magento.api.addSimpleProductsToCart({
+ cart_id: currentCartId,
cart_items: [
{
data: {
- quantity: quantity,
- sku: product.sku
- }
- }
- ]
+ quantity,
+ sku: product.sku,
+ },
+ },
+ ],
});
- return response.data.addSimpleProductsToCart.cart;
+ return simpleProduct.data.addSimpleProductsToCart.cart;
case 'configurable':
- const configurableResponse = await context.$ma.api.addConfigurableProductsToCart({
- cart_id: currentCart.id,
+ const configurableProduct = await context.$magento.api.addConfigurableProductsToCart({
+ cart_id: currentCartId,
cart_items: [
{
parent_sku: product.sku,
variant_sku: product.variant_sku,
data: {
- quantity: quantity,
- sku: product.variant_sku
- }
- }
- ]
+ quantity,
+ sku: product.variant_sku,
+ },
+ },
+ ],
});
- return configurableResponse.data.addConfigurableProductsToCart.cart;
+ return configurableProduct.data.addConfigurableProductsToCart.cart;
default:
// todo implement other options
throw new Error(`Product Type ${product.type_id} not supported in add to cart yet`);
}
},
- removeItem: async (context: Context, { currentCart, product }) => {
- const response = await context.$ma.api.removeItemFromCart({
+ removeItem: async (context: Context, {
+ currentCart,
+ product,
+ }) => {
+ // @TODO, why i can't just get the item??
+ const item = currentCart.items.find((cartItem) => cartItem.product.id === product.id);
+ if (!item) {
+ return;
+ }
+
+ const response = await context.$magento.api.removeItemFromCart({
cart_id: currentCart.id,
- cart_item_id: product.id
+ cart_item_id: item.id,
});
+
return response.data.removeItemFromCart.cart;
},
- updateItemQty: async (context: Context, { currentCart, product, quantity }) => {
- const response = await context.$ma.api.updateCartItems({
+ updateItemQty: async (context: Context, {
+ currentCart,
+ product,
+ quantity,
+ }) => {
+ const response = await context.$magento.api.updateCartItems({
cart_id: currentCart.id,
cart_items: [
{
cart_item_id: product.id,
- quantity: quantity
- }
- ]
+ quantity,
+ },
+ ],
});
- return response.data.updateCartItems.cart;
+ return { updatedCart: response.data.updateCartItems.cart };
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
clear: async (context: Context, { currentCart }) => {
- context.$ma.config.state.setCartId(null);
- return await params.load(context, {});
+ context.$magento.config.state.setCartId(null);
+ return params.load(context, {});
},
- applyCoupon: async (context: Context, { currentCart, couponCode }) => {
- const response = await context.$ma.api.applyCouponToCart({
+ applyCoupon: async (context: Context, {
+ currentCart,
+ couponCode,
+ }) => {
+ const response = await context.$magento.api.applyCouponToCart({
cart_id: currentCart.id,
- coupon_code: couponCode
+ coupon_code: couponCode,
});
- return {updatedCart: response.data.applyCouponToCart.cart, updatedCoupon: { code: couponCode }};
+ return {
+ updatedCart: response.data.applyCouponToCart.cart,
+ updatedCoupon: { code: couponCode },
+ };
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- removeCoupon: async (context: Context, { currentCart }) => {
- const response = await context.$ma.api.removeCouponFromCart({
+ removeCoupon: async (context: Context, {
+ currentCart,
+ coupon,
+ }) => {
+ const response = await context.$magento.api.removeCouponFromCart({
cart_id: currentCart.id,
});
- return { updatedCart: response.data.removeCouponFromCart.cart, updatedCoupon: { code: ''}};
+ return {
+ updatedCart: response.data.removeCouponFromCart.cart,
+ updatedCoupon: { code: '' },
+ };
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- isOnCart: (context: Context, { currentCart, product }) => {
- // @TODO: Check if this is the format.
- return currentCart.items.find((item) => item.id === product.id);
- }
+ isInCart: (context: Context, { currentCart, product }) => currentCart.items.find((cartItem) => cartItem.product.id === product.id),
};
export default useCartFactory(params);
diff --git a/packages/composables/src/composables/useCategory/index.ts b/packages/composables/src/composables/useCategory/index.ts
index beffec3b2..b2f1d260c 100644
--- a/packages/composables/src/composables/useCategory/index.ts
+++ b/packages/composables/src/composables/useCategory/index.ts
@@ -4,9 +4,9 @@ import { UseCategory, Category, CategorySearchParams } from '../../types';
const factoryParams = {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
categorySearch: async (context: Context, params) => {
- const categoryResponse = await context.$ma.api.categoryList(params);
+ const categoryResponse = await context.$magento.api.categoryList(params);
return categoryResponse.data.categoryList;
- }
+ },
};
const useCategory: (id: string) => UseCategory = useCategoryFactory(factoryParams);
diff --git a/packages/composables/src/composables/useCheckout/index.ts b/packages/composables/src/composables/useCheckout/index.ts
index 50918620c..9174fa1d8 100644
--- a/packages/composables/src/composables/useCheckout/index.ts
+++ b/packages/composables/src/composables/useCheckout/index.ts
@@ -1,49 +1,56 @@
/* istanbul ignore file */
-
import { computed, Ref, ref } from '@vue/composition-api';
import { ShippingMethod, User, UserAddress } from '../../types';
const PAYMENT_METHODS_MOCK = [{
label: 'Visa Debit',
- value: 'debit'
+ value: 'debit',
}, {
label: 'MasterCard',
- value: 'mastercard'
+ value: 'mastercard',
}, {
label: 'Visa Electron',
- value: 'electron'
+ value: 'electron',
}, {
label: 'Cash on delivery',
- value: 'cash'
+ value: 'cash',
}, {
label: 'Check',
- value: 'check'
+ value: 'check',
}];
-const paymentMethods: Ref = ref(PAYMENT_METHODS_MOCK);
-const shippingMethods: Ref = ref([]);
-const personalDetails: Ref = ref({});
-const shippingDetails: Ref = ref({});
-const billingDetails: Ref = ref({});
-const chosenPaymentMethod: Ref = ref('');
-const chosenShippingMethod: Ref = ref({});
+const paymentMethods = ref(PAYMENT_METHODS_MOCK);
+const shippingMethods = ref([]);
+const personalDetails = ref({});
+const shippingDetails = ref({});
+const billingDetails = ref({});
+const chosenPaymentMethod = ref('');
+const chosenShippingMethod = ref({});
const placeOrder = async () => {};
const loadDetails = async () => {};
// @todo CHECKOUT
-const useCheckout: () => { chosenShippingMethod: Ref; shippingDetails: Ref; billingDetails: Ref; chosenPaymentMethod: Ref; placeOrder: () => Promise; loadDetails: () => Promise; paymentMethods: Ref; personalDetails: Ref; loading: Readonly][>>; shippingMethods: Ref } = () => {
-
- return {
- paymentMethods,
- shippingMethods,
- personalDetails,
- shippingDetails,
- billingDetails,
- chosenPaymentMethod,
- chosenShippingMethod,
- placeOrder,
- loadDetails,
- loading: computed(() => false)
- };
-};
+const useCheckout: () => {
+ chosenShippingMethod: Ref;
+ shippingDetails: Ref;
+ billingDetails: Ref;
+ chosenPaymentMethod: Ref;
+ placeOrder: () => Promise;
+ loadDetails: () => Promise;
+ paymentMethods: Ref;
+ personalDetails: Ref;
+ loading: Readonly][>>;
+ shippingMethods: Ref;
+} = () => ({
+ paymentMethods,
+ shippingMethods,
+ personalDetails,
+ shippingDetails,
+ billingDetails,
+ chosenPaymentMethod,
+ chosenShippingMethod,
+ placeOrder,
+ loadDetails,
+ loading: computed(() => false),
+});
export default useCheckout;
diff --git a/packages/composables/src/composables/useConfig/index.ts b/packages/composables/src/composables/useConfig/index.ts
index c6c7e16bc..81f74df24 100644
--- a/packages/composables/src/composables/useConfig/index.ts
+++ b/packages/composables/src/composables/useConfig/index.ts
@@ -4,10 +4,10 @@ import { useConfigFactory } from '../../factories/useConfigFactory';
const useConfig: (cacheId: string) => UseConfig = useConfigFactory({
loadConfig: async (context: Context) => {
- const result = await context.$ma.api.storeConfig();
+ const result = await context.$magento.api.storeConfig();
return result.data.storeConfig || {};
- }
+ },
});
export default useConfig;
diff --git a/packages/composables/src/composables/useFacet/_utils.ts b/packages/composables/src/composables/useFacet/_utils.ts
new file mode 100644
index 000000000..216836be0
--- /dev/null
+++ b/packages/composables/src/composables/useFacet/_utils.ts
@@ -0,0 +1,76 @@
+import { SearchData } from '../../types';
+
+const buildBreadcrumbsList = (rootCat, bc) => {
+ const newBc = [...bc, {
+ text: rootCat.name,
+ link: rootCat.slug,
+ }];
+ return rootCat.parent ? buildBreadcrumbsList(rootCat.parent, newBc) : newBc;
+};
+
+export const buildBreadcrumbs = (rootCat) => buildBreadcrumbsList(rootCat, [])
+ .reverse()
+ .reduce((prev, curr, index) => ([
+ ...prev,
+ {
+ ...curr,
+ link: `${prev[index - 1]?.link || ''}/${curr.link}`,
+ }]),
+ []);
+
+const filterFacets = (criteria) => (f) => (criteria ? criteria.includes(f) : true);
+
+const getFacetTypeByCode = (code) => {
+ if (code === 'type_of_stones') {
+ return 'radio';
+ }
+ return 'checkbox';
+};
+
+const createFacetsFromOptions = (facets, filters, filterKey) => {
+ const options = facets[filterKey]?.options || [];
+ const selectedList = filters && filters[filterKey] ? filters[filterKey] : [];
+
+ return options
+ .map(({
+ label,
+ value,
+ }) => ({
+ type: getFacetTypeByCode(facets[filterKey]?.attribute_code),
+ id: value,
+ attrName: label,
+ value,
+ selected: selectedList.includes(value),
+ count: null,
+ }));
+};
+
+export const reduceForFacets = (facets, filters) => (prev, curr) => ([
+ ...prev,
+ ...createFacetsFromOptions(facets, filters, curr),
+]);
+
+export const reduceForGroupedFacets = (facets, filters) => (prev, curr) => ([
+ ...prev,
+ {
+ id: facets[curr].attribute_code,
+ label: facets[curr].default_frontend_label,
+ options: createFacetsFromOptions(facets, filters, curr),
+ count: null,
+ },
+]);
+
+export const buildFacets = (searchData: SearchData, reduceFn, criteria?: string[]) => {
+ if (!searchData.data) {
+ return [];
+ }
+
+ const {
+ data: { availableFilters: facets },
+ input: { filters },
+ } = searchData;
+
+ return Object.keys(facets || [])
+ .filter(filterFacets(criteria))
+ .reduce(reduceFn(facets, filters), []);
+};
diff --git a/packages/composables/src/composables/useFacet/index.ts b/packages/composables/src/composables/useFacet/index.ts
index 449e2fbf0..8c6beec52 100644
--- a/packages/composables/src/composables/useFacet/index.ts
+++ b/packages/composables/src/composables/useFacet/index.ts
@@ -1,105 +1,91 @@
-import { Context, useFacetFactory, FacetSearchResult, ProductsSearchParams } from '@vue-storefront/core';
-import { CategoryFilterInput, ProductAttributeFilterInput } from '@vue-storefront/magento-api'
+import {
+ Context, useFacetFactory, FacetSearchResult, ProductsSearchParams,
+} from '@vue-storefront/core';
const availableSortingOptions = [{
value: 'name',
- label: 'Name'
+ label: 'Name',
}, {
value: 'price-up',
- label: 'Price from low to high'
+ label: 'Price from low to high',
}, {
value: 'price-down',
- label: 'Price from high to low'
+ label: 'Price from high to low',
}];
+const constructFilterObject = (inputFilters: Object) => {
+ const filter = {};
-const constructFilterObject = (obj: Object) => {
- /*
- return Object.entries(obj)
- .reduce((prev, [value, options]) => {
- if (value !== 'price') {
- prev[value] = { in: options.map(({ value }) => value.toString()) };
- } else {
- const val = options[0].value.split('_');
- prev[value] = {
- from: decodeURIComponent(val[0].toString()),
- to: decodeURIComponent(val[1].toString())
- };
+ Object.keys(inputFilters).forEach((key) => {
+ if (key === 'price') {
+ const price = { from: 0, to: 0 };
+ const [priceFrom, priceTo] = inputFilters[key].split('-');
+
+ price.from = priceFrom;
+
+ if (priceTo) {
+ price.to = priceTo;
}
- // eslint-disable-next-line camelcase,@typescript-eslint/camelcase
- return prev;
- }, {});
- */
- let filters = {};
- console.log(Object.entries(obj));
- for (const [key, value] of Object.entries(obj)) {
- if(value.length === 0)
- return;
- if (!filters[key]) {
- filters[key] = {
- "in": value.join(''),
- "scope": "catalog"
- };
+
+ filter[key] = price;
+ } else if (typeof inputFilters[key] === 'string') {
+ filter[key] = { finset: [inputFilters[key]] };
} else {
- filters[key].in = filters[key].in + "," + value.join('');
+ filter[key] = { finset: inputFilters[key] };
}
- }
+ });
- return filters;
+ return filter;
};
const factoryParams = {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
search: async (context: Context, params: FacetSearchResult) => {
- const itemsPerPage = params.input.itemsPerPage;
- const categoryParams: CategoryFilterInput = {
- filters: {
- url_path: {
- eq: params.input.categorySlug
- }
- }
- };
+ const itemsPerPage = (params.input.itemsPerPage) ? params.input.itemsPerPage : 20;
+ const inputFilters = (params.input.filters) ? params.input.filters : {};
- const categoryResponse = await context.$ma.api.categoryList(
- categoryParams.perPage,
- categoryParams.page,
- categoryParams.filter,
- categoryParams.search,
- categoryParams.sort,
- );
-
- // What happen if not exsits?
- const category = categoryResponse.data.categoryList[0];
- const inputFilters = params.input.filters;
-
- const productParams: ProductAttributeFilterInput = {
+ // ref for filters
+ // async for filters
+ // if ref for filters: don't run again.
+
+ // ref for products
+ // async for products
+
+ const productParams: ProductsSearchParams = {
filter: {
- category_id: {
- eq: category.id
- }
+ category_ids: {
+ eq: params.input.categorySlug,
+ },
+ type_id: {
+ eq: 'configurable',
+ },
+ ...constructFilterObject(inputFilters),
},
perPage: itemsPerPage,
offset: (params.input.page - 1) * itemsPerPage,
- page: params.input.page
+ page: params.input.page,
+ search: (params.input.term) ? params.input.term : '',
};
-
- const productResponse = await context.$ma.api.getProduct(
+
+ const productResponse = await context.$magento.api.products(
productParams.perPage,
productParams.page,
- Object.assign(productParams.filter, constructFilterObject(params.input.filters)),
+ productParams.filter,
productParams.queryType,
productParams.search,
- productParams.sort
+ productParams.sort,
);
const data = {
items: productResponse?.data?.products?.items || [],
- total: productResponse?.data?.products?.total_count?.value || 0,
+ total: productResponse?.data?.products?.total_count || 0,
availableFilters: productResponse?.data?.products?.attribute_metadata,
- category: category,
- availableSortingOptions
+ category: { id: params.input.categorySlug },
+ availableSortingOptions,
+ perPageOptions: [10, 20, 50],
+ itemsPerPage,
};
-
+
return data;
},
};
diff --git a/packages/composables/src/composables/useMakeOrder/index.ts b/packages/composables/src/composables/useMakeOrder/index.ts
index 430f42838..815936ebc 100644
--- a/packages/composables/src/composables/useMakeOrder/index.ts
+++ b/packages/composables/src/composables/useMakeOrder/index.ts
@@ -1,24 +1,24 @@
+import { Context } from '@vue-storefront/core';
import { Order, UseMakeOrder } from '../../types';
-import { Context } from '@vue-storefront/core'
import { useMakeOrderFactory } from '../../factories/useMakeOrderFactory';
import useCart from '../useCart';
const factoryParams = {
- provide() {
- return {
- cart: useCart()
- };
- },
-
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- make: async (context: Context, { customQuery }): Promise => {
- console.log('[Magento] Make Order');
- const { id } = context.cart.cart.value;
- const placeOrderResponse = await context.$ma.api.placeOrder({cart_id: id});
- return placeOrderResponse.data.order;
- }
+ provide() {
+ return {
+ cart: useCart(),
+ };
+ },
+
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ make: async (context: Context, { customQuery }): Promise => {
+ console.log('[Magento] Make Order');
+ const { id } = context.cart.cart.value;
+ const placeOrderResponse = await context.$magento.api.placeOrder({ cart_id: id });
+ return placeOrderResponse.data.order;
+ },
};
const useMakeOrder: () => UseMakeOrder = useMakeOrderFactory(factoryParams);
-export default useMakeOrder;
\ No newline at end of file
+export default useMakeOrder;
diff --git a/packages/composables/src/composables/useMenuCategory/index.ts b/packages/composables/src/composables/useMenuCategory/index.ts
new file mode 100644
index 000000000..61584f6a7
--- /dev/null
+++ b/packages/composables/src/composables/useMenuCategory/index.ts
@@ -0,0 +1,23 @@
+/* eslint-disable @typescript-eslint/return-await */
+import {
+ Context,
+ useCategoryFactory,
+ UseCategoryFactoryParams,
+} from '@vue-storefront/core';
+import { MenuCategory, CategoryFilter } from '@vue-storefront/magento-api';
+
+const factoryParams: UseCategoryFactoryParams = {
+ categorySearch: async (context: Context, parameters) => {
+ const { customQuery, ...searchParams } = parameters;
+ const {
+ $magento: {
+ api,
+ },
+ } = context;
+ const { categories: { items } } = await api.getMenuCategories(searchParams, customQuery);
+
+ return items.filter((i) => i.include_in_menu);
+ },
+};
+
+export default useCategoryFactory(factoryParams);
diff --git a/packages/composables/src/composables/usePage/index.ts b/packages/composables/src/composables/usePage/index.ts
index dbc213259..66e41d8a1 100644
--- a/packages/composables/src/composables/usePage/index.ts
+++ b/packages/composables/src/composables/usePage/index.ts
@@ -3,10 +3,10 @@ import { Page, UsePage } from '../../types';
import { usePageFactory } from '../../factories/usePageFactory';
const usePage: (cacheId: string) => UsePage = usePageFactory({
- loadPage: async (context: Context, identifer: string) => {
- const result = await context.$ma.api.cmsPage(identifer);
+ loadPage: async (context: Context, identifier: string) => {
+ const result = await context.$magento.api.cmsPage(identifier);
return result.data.cmsPage;
- }
+ },
});
export default usePage;
diff --git a/packages/composables/src/composables/useProduct/index.ts b/packages/composables/src/composables/useProduct/index.ts
index 32eba034a..8526f0081 100644
--- a/packages/composables/src/composables/useProduct/index.ts
+++ b/packages/composables/src/composables/useProduct/index.ts
@@ -1,45 +1,35 @@
import {
- ProductAttributeFilter,
- ProductAttributeSortInput,
- Products,
- ProductsQueryType
+ Products,
+ GetProductSearchParams,
} from '@vue-storefront/magento-api';
-import { Context } from '@vue-storefront/core';
-import { useProductFactory, ProductsSearchParams } from '@vue-storefront/core';
+import { Context, useProductFactory, ProductsSearchParams } from '@vue-storefront/core';
+
import { UseProduct } from '../../types';
const availableSortingOptions = [{
value: 'latest',
- label: 'Latest'
+ label: 'Latest',
}, {
value: 'price-up',
- label: 'Price from low to high'
+ label: 'Price from low to high',
}, {
value: 'price-down',
- label: 'Price from high to low'
+ label: 'Price from high to low',
}];
-const productsSearch = async (context: Context, params: {
- search?: string;
- filter?: ProductAttributeFilter;
- pageSize?: number;
- currentPage?: number;
- sort?: ProductAttributeSortInput;
- queryType?: ProductsQueryType;
-}) => {
- const productResults = await context.$ma.api.products(params.pageSize, params.currentPage, params.queryType, params.search, params.filter, params.sort);
+const productsSearch = async (context: Context, params: GetProductSearchParams) => {
+ const productResults = await context.$magento.api.products(params);
return {
data: productResults.data.products,
total: productResults.data.products.total_count,
availableFilters: productResults.data.products.aggregations,
- availableSortingOptions
+ availableSortingOptions,
};
-
};
const useProduct: (cacheId: string) => UseProduct]