├── .gitignore ├── .npmignore ├── README.md ├── lerna-debug.log ├── lerna.json ├── package-lock.json ├── package.json └── packages ├── components ├── .eslintignore ├── .gitignore ├── README.md ├── __tests__ │ └── components.test.js ├── package.json ├── src │ ├── index.ts │ ├── store │ │ ├── carts │ │ │ └── index.ts │ │ ├── collections │ │ │ └── index.ts │ │ ├── customers │ │ │ ├── index.ts │ │ │ ├── mutations.ts │ │ │ └── queries.ts │ │ ├── gift-cards │ │ │ ├── index.ts │ │ │ └── queries.ts │ │ ├── index.ts │ │ ├── line-items │ │ │ ├── index.ts │ │ │ └── mutations.ts │ │ ├── order-edits │ │ │ ├── index.ts │ │ │ ├── mutations.ts │ │ │ └── queries.ts │ │ ├── orders │ │ │ ├── index.ts │ │ │ ├── mutations.ts │ │ │ └── queries.ts │ │ ├── payment-collections │ │ │ ├── index.ts │ │ │ ├── mutations.ts │ │ │ └── queries.ts │ │ ├── product-types │ │ │ ├── index.ts │ │ │ └── queries.ts │ │ ├── products │ │ │ └── index.ts │ │ ├── regions │ │ │ ├── index.ts │ │ │ └── queries.ts │ │ ├── return-reasons │ │ │ ├── index.ts │ │ │ └── queries.ts │ │ ├── returns │ │ │ ├── index.ts │ │ │ └── mutations.ts │ │ ├── shipping-options │ │ │ ├── index.ts │ │ │ └── queries.ts │ │ └── swaps │ │ │ ├── index.ts │ │ │ ├── mutations.ts │ │ │ └── queries.ts │ └── util │ │ ├── makeProps.ts │ │ └── propsFactory.ts └── tsconfig.json └── core ├── .eslintignore ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README copy.md ├── README.md ├── __tests__ └── core.test.js ├── mocks ├── data │ ├── fixtures.json │ └── index.ts ├── handlers │ ├── admin.ts │ ├── index.ts │ └── store.ts └── server.ts ├── package copy.json ├── package-lock.json ├── package.json ├── src ├── composables │ ├── index.ts │ ├── store │ │ ├── carts │ │ │ ├── index.ts │ │ │ ├── mutations.ts │ │ │ └── queries.ts │ │ ├── collections │ │ │ ├── index.ts │ │ │ └── queries.ts │ │ ├── customers │ │ │ ├── index.ts │ │ │ ├── mutations.ts │ │ │ └── queries.ts │ │ ├── gift-cards │ │ │ ├── index.ts │ │ │ └── queries.ts │ │ ├── index.ts │ │ ├── line-items │ │ │ ├── index.ts │ │ │ └── mutations.ts │ │ ├── order-edits │ │ │ ├── index.ts │ │ │ ├── mutations.ts │ │ │ └── queries.ts │ │ ├── orders │ │ │ ├── index.ts │ │ │ ├── mutations.ts │ │ │ └── queries.ts │ │ ├── payment-collections │ │ │ ├── index.ts │ │ │ ├── mutations.ts │ │ │ └── queries.ts │ │ ├── product-types │ │ │ ├── index.ts │ │ │ └── queries.ts │ │ ├── products │ │ │ ├── index.ts │ │ │ └── queries.ts │ │ ├── regions │ │ │ ├── index.ts │ │ │ └── queries.ts │ │ ├── return-reasons │ │ │ ├── index.ts │ │ │ └── queries.ts │ │ ├── returns │ │ │ ├── index.ts │ │ │ └── mutations.ts │ │ ├── shipping-options │ │ │ ├── index.ts │ │ │ └── queries.ts │ │ └── swaps │ │ │ ├── index.ts │ │ │ ├── mutations.ts │ │ │ └── queries.ts │ └── utils │ │ ├── buildOptions.ts │ │ ├── index.ts │ │ └── queryKeysFactory.ts ├── helpers │ └── index.ts ├── index.ts ├── injectionSymbols.ts ├── medusaVueClient.ts ├── types.ts └── useApi.ts ├── tests ├── composables │ └── store │ │ ├── carts │ │ ├── mutations.test.ts │ │ └── queries.test.ts │ │ ├── collections │ │ └── queries.test.ts │ │ ├── customers │ │ ├── mutations.test.ts │ │ └── queries.test.ts │ │ ├── gift_cards │ │ └── queries.test.ts │ │ ├── line-items │ │ └── mutations.test.ts │ │ ├── order-edits │ │ ├── mutations.test.ts │ │ └── queries.test.ts │ │ ├── orders │ │ ├── mutations.test.ts │ │ └── queries.test.ts │ │ ├── payment-collections │ │ ├── mutations.test.ts │ │ └── queries.test.ts │ │ ├── product-types │ │ └── queries.test.ts │ │ ├── products │ │ └── queries.test.ts │ │ ├── regions │ │ └── queries.test.ts │ │ ├── return-reasons │ │ └── queries.test.ts │ │ ├── returns │ │ └── mutations.test.ts │ │ ├── shipping-options │ │ └── queries.test.ts │ │ └── swaps │ │ ├── mutations.test.ts │ │ └── queries.test.ts ├── setup.ts └── utils.ts ├── tsconfig.json └── vitest.config.ts /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .npmrc 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /lib 2 | node_modules 3 | .DS_store 4 | .env* 5 | /*.js 6 | !index.js 7 | test/ 8 | public/ 9 | .storybook/ 10 | mocks/ 11 | stories/ 12 | yarn.lock 13 | .npmrc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Medusa Vue 2 | 3 | Vue 3 composables and components for seamless and streamlined interaction with a [Medusa](https://github.com/medusajs/medusa). 4 | 5 | If you're building a custom vue based storefront that consumes a medusa backend and find yourself wishing you had something nice at hands like `medusa-react` to streamline your data management - this might be your library! 6 | 7 | ## Installation 8 | 9 | The library uses [@tanstack/vue-query](https://tanstack.com/query/v4/docs/vue/overview) under the hood. 10 | 11 | For the core composables run: 12 | 13 | ```bash 14 | npm install @medusa-vue/core 15 | # or 16 | yarn add @medusa-vue/core 17 | ``` 18 | 19 | For the components (WIP :construction_worker:): 20 | 21 | ```bash 22 | npm install @medusa-vue/components 23 | # or 24 | yarn add @medusa-vue/components 25 | ``` 26 | 27 | ## Quick Start 28 | 29 | In order to use the composables exposed by this library you need to register it as a plugin in your main file before mounting your app. The plugin takes a config object that needs at least a `baseUrl` where it can reach your server. Optionally, it allows you to pass additional props to configure both the underlying `medusa-js` and the `vue-query` client. Here's the complete interface. Refer to [these](https://docs.medusajs.com/js-client/overview/) and [these](https://tanstack.com/query/v4/docs/vue/overview) docs, respectively to get an idea on how the parts work together. 30 | 31 | ```ts 32 | interface MedusaVueClientProps { 33 | baseUrl: string; 34 | maxRetries?: number; 35 | /** 36 | * Authentication token 37 | */ 38 | apiKey?: string; 39 | /** 40 | * PublishableApiKey identifier that defines the scope of resources 41 | * available within the request 42 | */ 43 | publishableApiKey?: string; 44 | 45 | queryClientProviderProps?: VueQueryPluginOptions; 46 | } 47 | ``` 48 | 49 | Plug it in: 50 | 51 | ```ts 52 | // main.ts 53 | import { createApp } from 'vue'; 54 | import App from './App.vue'; 55 | 56 | import { createMedusaVueClient } from '@medusa-vue/core'; 57 | 58 | const client = createMedusaVueClient({ 59 | baseUrl: '', 60 | }); 61 | 62 | const app = createApp(App); 63 | 64 | app.use(client).mount('#app'); 65 | ``` 66 | 67 | ### Queries 68 | 69 | [Queries](https://tanstack.com/query/v4/docs/vue/guides/queries) simply wrap around vue-query's `useQuery` hook to fetch some data from your medusa server 70 | 71 | ```vue 72 | // ./my-product-list.vue 73 | 78 | 79 | 84 | ``` 85 | 86 | **_Note_**: If you've worked with @medusajs/medusa-react you might be used to being able to destructure the recordset returned by the server directly, i.e. `const { products } = useProducts()`. This is however not possible with vue due to the way it's reactive system works. 87 | 88 | ### Mutations 89 | 90 | [Mutations](https://react-query.tanstack.com/guides/mutations#_top) wrap around vue-query's `useMutation` to mutate data and perform server-side effects on your medusa server. If you are not entirely familiar with this idea of "mutations", creating a cart would be a mutation because it creates a cart in your server (and database). Mutations also have to be invoked imperatively, meaning that calling for the mutation to take action, you will have to call a `mutate()` function returned from mutation hooks. 91 | 92 | ```vue 93 | 101 | 102 | 107 | ``` 108 | 109 | The mutation hooks will return exactly what vue-query's [`useMutation`](https://tanstack.com/query/v4/docs/vue/guides/mutations) returns. In addition, the options you pass in to the hooks will be passed along to `useMutation`. 110 | 111 | ### Components 112 | 113 | **_NOTE_**: This is still work in progress and new components will gradually be added!:construction_worker: 114 | 115 | If you prefer declarative templates, `@medusa-vue/components` provided (almost) renderless components to use directly in your template and provide data through `slot-props`. This allows for extremely streamlinend and declarative templating: 116 | 117 | ```vue 118 | 121 | 122 | 129 | ``` 130 | 131 | The component also allows to pass down the laoding indicating component via a slot: 132 | 133 | ```vue 134 | 137 | 138 | 149 | ``` 150 | 151 | ### Utilities 152 | 153 | A set of utility functions are also exposed from the library to make your life easier when dealing with displaying money amounts 154 | 155 | #### `formatVariantPrice()` 156 | 157 | - `formatVariantPrice(params: FormatVariantPriceParams): string` 158 | 159 | ```typescript 160 | type FormatVariantPriceParams = { 161 | variant: ProductVariantInfo; 162 | region: RegionInfo; 163 | includeTaxes?: boolean; 164 | minimumFractionDigits?: number; 165 | maximumFractionDigits?: number; 166 | locale?: string; 167 | }; 168 | 169 | type ProductVariantInfo = Pick; 170 | 171 | type RegionInfo = { 172 | currency_code: string; 173 | tax_code: string; 174 | tax_rate: number; 175 | }; 176 | ``` 177 | 178 | Given a variant and region, will return a string representing the localized amount (i.e: `$19.50`) 179 | 180 | The behavior of minimumFractionDigits and maximumFractionDigits is the same as the one explained by MDN [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat). In fact, in order to convert the decimal amount, we use the browser's `Intl.NumberFormat` method. 181 | 182 | #### `computeVariantPrice()` 183 | 184 | - `computeVariantPrice(params: ComputeVariantPriceParams): number` 185 | 186 | ```typescript 187 | type ComputeVariantPriceParams = { 188 | variant: ProductVariantInfo; 189 | region: RegionInfo; 190 | includeTaxes?: boolean; 191 | }; 192 | ``` 193 | 194 | Determines a variant's price based on the region provided. Returns a decimal number representing the amount. 195 | 196 | #### `formatAmount()` 197 | 198 | - `formatAmount(params: FormatAmountParams): string` 199 | 200 | ```typescript 201 | type FormatAmountParams = { 202 | amount: number; 203 | region: RegionInfo; 204 | includeTaxes?: boolean; 205 | minimumFractionDigits?: number; 206 | maximumFractionDigits?: number; 207 | locale?: string; 208 | }; 209 | ``` 210 | 211 | Returns a localized string based on the input params representing the amount (i.e: "$10.99"). 212 | 213 | #### `computeAmount()` 214 | 215 | - `computeAmount(params: ComputeAmountParams): number` 216 | 217 | ```typescript 218 | type ComputeAmountParams = { 219 | amount: number; 220 | region: RegionInfo; 221 | includeTaxes?: boolean; 222 | }; 223 | ``` 224 | 225 | Takes an integer amount, a region, and includeTaxes boolean. Returns a decimal amount including (or excluding) taxes. 226 | 227 | ### Credits 228 | 229 | Based on and inspired by [medusa-react](https://www.npmjs.com/package/medusa-react). 230 | Keep up the good work! :beers: 231 | -------------------------------------------------------------------------------- /lerna-debug.log: -------------------------------------------------------------------------------- 1 | 31 error Error: Command failed with exit code 1: npm install --package-lock-only --ignore-scripts 2 | 31 error npm ERR! code E404 3 | 31 error npm ERR! 404 Not Found - GET https://registry.npmjs.org/@size-limit%2fpreset-small-dist - Not found 4 | 31 error npm ERR! 404 5 | 31 error npm ERR! 404 '@size-limit/preset-small-dist@^6.0.4' is not in this registry. 6 | 31 error npm ERR! 404 7 | 31 error npm ERR! 404 Note that you can also install from a 8 | 31 error npm ERR! 404 tarball, folder, http url, or git url. 9 | 31 error 10 | 31 error npm ERR! A complete log of this run can be found in: 11 | 31 error npm ERR! /Users/ulikrause/.npm/_logs/2023-01-19T16_54_22_946Z-debug-0.log 12 | 31 error at makeError (/Users/ulikrause/code/@medusa-vue/node_modules/execa/lib/error.js:60:11) 13 | 31 error at handlePromise (/Users/ulikrause/code/@medusa-vue/node_modules/execa/index.js:118:26) 14 | 31 error at processTicksAndRejections (node:internal/process/task_queues:96:5) 15 | 31 error at async Promise.all (index 0) 16 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "node_modules/lerna/schemas/lerna-schema.json", 3 | "useWorkspaces": true, 4 | "version": "0.4.2", 5 | "packages": [ 6 | "packages/*" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@medusa-vue/monorepo", 3 | "version": "0.0.0", 4 | "publishConfig": { 5 | "access": "public" 6 | }, 7 | "scripts": {}, 8 | "workspaces": [ 9 | "packages/*" 10 | ], 11 | "devDependencies": { 12 | "lerna": "^6.4.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/components/.eslintignore: -------------------------------------------------------------------------------- 1 | src/types.ts -------------------------------------------------------------------------------- /packages/components/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | node_modules 4 | .cache 5 | dist 6 | -------------------------------------------------------------------------------- /packages/components/README.md: -------------------------------------------------------------------------------- 1 | # Medusa Vue 2 | 3 | Vue 3 composables and components for seamless and streamlined interaction with a [Medusa](https://github.com/medusajs/medusa). 4 | 5 | If you're building a custom vue based storefront that consumes a medusa backend and find yourself wishing you had something nice at hands like `medusa-react` to streamline your data management - this might be your library! 6 | 7 | ## Installation 8 | 9 | The library uses [@tanstack/vue-query](https://tanstack.com/query/v4/docs/vue/overview) under the hood. 10 | 11 | For the core composables run: 12 | 13 | ```bash 14 | npm install @medusa-vue/core 15 | # or 16 | yarn add @medusa-vue/core 17 | ``` 18 | 19 | For the components (WIP :construction_worker:): 20 | 21 | ```bash 22 | npm install @medusa-vue/components 23 | # or 24 | yarn add @medusa-vue/components 25 | ``` 26 | 27 | ## Quick Start 28 | 29 | In order to use the composables exposed by this library you need to register it as a plugin in your main file before mounting your app. The plugin takes a config object that needs at least a `baseUrl` where it can reach your server. Optionally, it allows you to pass additional props to configure both the underlying `medusa-js` and the `vue-query` client. Here's the complete interface. Refer to [these](https://docs.medusajs.com/js-client/overview/) and [these](https://tanstack.com/query/v4/docs/vue/overview) docs, respectively to get an idea on how the parts work together. 30 | 31 | ```ts 32 | interface MedusaVueClientProps { 33 | baseUrl: string; 34 | maxRetries?: number; 35 | /** 36 | * Authentication token 37 | */ 38 | apiKey?: string; 39 | /** 40 | * PublishableApiKey identifier that defines the scope of resources 41 | * available within the request 42 | */ 43 | publishableApiKey?: string; 44 | 45 | queryClientProviderProps?: VueQueryPluginOptions; 46 | } 47 | ``` 48 | 49 | Plug it in: 50 | 51 | ```ts 52 | // main.ts 53 | import { createApp } from 'vue'; 54 | import App from './App.vue'; 55 | 56 | import { createMedusaVueClient } from '@medusa-vue/core'; 57 | 58 | const client = createMedusaVueClient({ 59 | baseUrl: '', 60 | }); 61 | 62 | const app = createApp(App); 63 | 64 | app.use(client).mount('#app'); 65 | ``` 66 | 67 | The hooks exposed by `medusa-vue` fall into two main categories: queries and mutations. 68 | 69 | ### Queries 70 | 71 | [Queries](https://tanstack.com/query/v4/docs/vue/guides/queries) simply wrap around vue-query's `useQuery` hook to fetch some data from your medusa server 72 | 73 | ```vue 74 | // ./my-product-list.vue 75 | 82 | 83 | 88 | ``` 89 | 90 | **_Note_**: If you've worked with @medusajs/medusa-react you might be used to being able to destructure the recordset returned by the server, i.e. `const { products } = useProducts()`. This is however not possible with vue due to the way it's reactive system works. 91 | 92 | ### Mutations 93 | 94 | [Mutations](https://react-query.tanstack.com/guides/mutations#_top) wrap around vue-query's `useMutation` to mutate data and perform server-side effects on your medusa server. If you are not entirely familiar with this idea of "mutations", creating a cart would be a mutation because it creates a cart in your server (and database). Mutations also have to be invoked imperatively, meaning that calling for the mutation to take action, you will have to call a `mutate()` function returned from mutation hooks. 95 | 96 | ```vue 97 | 105 | 106 | 111 | ``` 112 | 113 | The mutation hooks will return exactly what vue-query's [`useMutation`](https://tanstack.com/query/v4/docs/vue/guides/mutations) returns. In addition, the options you pass in to the hooks will be passed along to `useMutation`. 114 | 115 | ### Components 116 | 117 | **_NOTE_**: This is still work in progress and new components will gradually be added!:construction_worker: 118 | 119 | If you prefer declarative templates, `@medusa-vue/components` provided (almost) renderless components to use directly in your template and provide data through `slot-props`. This allows for extremely streamlinend and declarative templating: 120 | 121 | ```vue 122 | 125 | 126 | 133 | ``` 134 | 135 | The component also allows to pass down the laoding indicating component via a slot: 136 | 137 | ```vue 138 | 141 | 142 | 153 | ``` 154 | 155 | ### Utilities 156 | 157 | A set of utility functions are also exposed from the library to make your life easier when dealing with displaying money amounts 158 | 159 | #### `formatVariantPrice()` 160 | 161 | - `formatVariantPrice(params: FormatVariantPriceParams): string` 162 | 163 | ```typescript 164 | type FormatVariantPriceParams = { 165 | variant: ProductVariantInfo; 166 | region: RegionInfo; 167 | includeTaxes?: boolean; 168 | minimumFractionDigits?: number; 169 | maximumFractionDigits?: number; 170 | locale?: string; 171 | }; 172 | 173 | type ProductVariantInfo = Pick; 174 | 175 | type RegionInfo = { 176 | currency_code: string; 177 | tax_code: string; 178 | tax_rate: number; 179 | }; 180 | ``` 181 | 182 | Given a variant and region, will return a string representing the localized amount (i.e: `$19.50`) 183 | 184 | The behavior of minimumFractionDigits and maximumFractionDigits is the same as the one explained by MDN [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat). In fact, in order to convert the decimal amount, we use the browser's `Intl.NumberFormat` method. 185 | 186 | #### `computeVariantPrice()` 187 | 188 | - `computeVariantPrice(params: ComputeVariantPriceParams): number` 189 | 190 | ```typescript 191 | type ComputeVariantPriceParams = { 192 | variant: ProductVariantInfo; 193 | region: RegionInfo; 194 | includeTaxes?: boolean; 195 | }; 196 | ``` 197 | 198 | Determines a variant's price based on the region provided. Returns a decimal number representing the amount. 199 | 200 | #### `formatAmount()` 201 | 202 | - `formatAmount(params: FormatAmountParams): string` 203 | 204 | ```typescript 205 | type FormatAmountParams = { 206 | amount: number; 207 | region: RegionInfo; 208 | includeTaxes?: boolean; 209 | minimumFractionDigits?: number; 210 | maximumFractionDigits?: number; 211 | locale?: string; 212 | }; 213 | ``` 214 | 215 | Returns a localized string based on the input params representing the amount (i.e: "$10.99"). 216 | 217 | #### `computeAmount()` 218 | 219 | - `computeAmount(params: ComputeAmountParams): number` 220 | 221 | ```typescript 222 | type ComputeAmountParams = { 223 | amount: number; 224 | region: RegionInfo; 225 | includeTaxes?: boolean; 226 | }; 227 | ``` 228 | 229 | Takes an integer amount, a region, and includeTaxes boolean. Returns a decimal amount including (or excluding) taxes. 230 | 231 | ### Credits 232 | 233 | Based on and inspired by [medusa-react](https://www.npmjs.com/package/medusa-react). 234 | Keep up the good work! :beers: 235 | -------------------------------------------------------------------------------- /packages/components/__tests__/components.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const components = require('..'); 4 | const assert = require('assert').strict; 5 | 6 | assert.strictEqual(components(), 'Hello from components'); 7 | console.info("components tests passed"); 8 | -------------------------------------------------------------------------------- /packages/components/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@medusa-vue/components", 3 | "version": "0.4.2", 4 | "description": "Vue renderless components to interact with @medusa-js api", 5 | "keywords": [ 6 | "vue", 7 | "compontents", 8 | "api", 9 | "query", 10 | "@medusa" 11 | ], 12 | "author": "Ulrich Krause ", 13 | "homepage": "https://github.com/raukaute/medusa-vue", 14 | "license": "MIT", 15 | "main": "dist/index.js", 16 | "directories": { 17 | "lib": "dist", 18 | "test": "__tests__" 19 | }, 20 | "files": [ 21 | "dist" 22 | ], 23 | "publishConfig": { 24 | "access": "public" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "git+https://github.com/raukaute/medusa-vue.git" 29 | }, 30 | "scripts": { 31 | "start": "dts watch", 32 | "build": "dts build", 33 | "test": "vitest", 34 | "coverage": "vitest run --coverage", 35 | "lint": "dts lint", 36 | "size": "size-limit", 37 | "analyze": "size-limit --why" 38 | }, 39 | "bugs": { 40 | "url": "https://github.com/raukaute/medusa-vue/issues" 41 | }, 42 | "dependencies": { 43 | "@medusa-vue/core": "^0.4.2" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/components/src/index.ts: -------------------------------------------------------------------------------- 1 | import { UseProducts } from './store/products'; 2 | 3 | export { UseProducts }; 4 | -------------------------------------------------------------------------------- /packages/components/src/store/carts/index.ts: -------------------------------------------------------------------------------- 1 | import { useGetCart } from '@medusa-vue/core'; 2 | import { 3 | AllowedComponentProps, 4 | ComponentCustomProps, 5 | UnwrapRef, 6 | VNode, 7 | VNodeProps, 8 | defineComponent, 9 | h, 10 | reactive, 11 | } from 'vue'; 12 | 13 | import { makeTagProps } from '../../util/makeProps'; 14 | import type { TagProps } from '../../util/makeProps'; 15 | 16 | export interface UseMedusaGetCartComponentProps extends TagProps {} 17 | 18 | export const UseMedusaGetCartImplementation = /*#__PURE__*/ defineComponent({ 19 | name: 'UseMedusaGetCart', 20 | props: { 21 | id: { 22 | type: String, 23 | required: true, 24 | }, 25 | ...makeTagProps(), 26 | }, 27 | 28 | setup(props, { slots }) { 29 | const data = useGetCart(props.id); 30 | 31 | return () => { 32 | const slotProps = reactive(data); 33 | const children = slots.default && slots.default(slotProps); 34 | const fallback = slots.fallback && slots.fallback(); 35 | const component = props.tag ? h(props.tag, {}, children) : children; 36 | 37 | return (data.isLoading.value && fallback) || component; 38 | }; 39 | }, 40 | }); 41 | 42 | export const UseGetCart: _UseMedusaGetCartI = 43 | UseMedusaGetCartImplementation as any; 44 | 45 | export interface _UseMedusaGetCartI { 46 | new (): { 47 | $props: AllowedComponentProps & 48 | ComponentCustomProps & 49 | VNodeProps & 50 | UseMedusaGetCartComponentProps; 51 | 52 | $slots: { 53 | default?: (data: UnwrapRef>) => VNode[]; 54 | fallback?: () => VNode[]; 55 | }; 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /packages/components/src/store/collections/index.ts: -------------------------------------------------------------------------------- 1 | import { useCollection, useCollections } from '@medusa-vue/core'; 2 | import { 3 | AllowedComponentProps, 4 | ComponentCustomProps, 5 | UnwrapRef, 6 | VNode, 7 | VNodeProps, 8 | defineComponent, 9 | h, 10 | reactive, 11 | } from 'vue'; 12 | 13 | import { makeTagProps } from '../../util/makeProps'; 14 | import type { TagProps } from '../../util/makeProps'; 15 | 16 | export interface MedusaVueComponentProps extends TagProps {} 17 | 18 | export interface UseMedusaUseCollectionsComponentProps 19 | extends MedusaVueComponentProps {} 20 | 21 | export const UseMedusaGetCollectionsImplementation = 22 | /*#__PURE__*/ defineComponent({ 23 | name: 'UseMedusaGetCollections', 24 | props: { 25 | id: { 26 | type: String, 27 | required: true, 28 | }, 29 | ...makeTagProps(), 30 | }, 31 | 32 | setup(props, { slots }) { 33 | const data = useCollections(); 34 | 35 | return () => { 36 | const slotProps = reactive(data); 37 | const children = slots.default && slots.default(slotProps); 38 | const fallback = slots.fallback && slots.fallback(); 39 | const component = props.tag ? h(props.tag, {}, children) : children; 40 | 41 | return (data.isLoading.value && fallback) || component; 42 | }; 43 | }, 44 | }); 45 | 46 | export const UseGetCollections: _UseMedusaGetCollectionsI = 47 | UseMedusaGetCollectionsImplementation as any; 48 | 49 | export interface _UseMedusaGetCollectionsI { 50 | new (): { 51 | $props: AllowedComponentProps & 52 | ComponentCustomProps & 53 | VNodeProps & 54 | UseMedusaUseCollectionsComponentProps; 55 | 56 | $slots: { 57 | default?: (data: UnwrapRef>) => VNode[]; 58 | fallback?: () => VNode[]; 59 | }; 60 | }; 61 | } 62 | -------------------------------------------------------------------------------- /packages/components/src/store/customers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./queries" 2 | export * from "./mutations" 3 | -------------------------------------------------------------------------------- /packages/components/src/store/customers/mutations.ts: -------------------------------------------------------------------------------- 1 | import { 2 | StoreCustomersRes, 3 | StorePostCustomersCustomerReq, 4 | StorePostCustomersReq, 5 | } from '@medusajs/medusa'; 6 | import { useMutation, UseMutationOptions } from '@tanstack/vue-query'; 7 | import { useMedusa } from '../../../useApi'; 8 | 9 | export const useCreateCustomer = ( 10 | options?: UseMutationOptions< 11 | StoreCustomersRes, 12 | Error, 13 | StorePostCustomersReq, 14 | unknown 15 | > 16 | ) => { 17 | const { client } = useMedusa(); 18 | return useMutation( 19 | (data: StorePostCustomersReq) => client.customers.create(data), 20 | options 21 | ); 22 | }; 23 | 24 | export const useUpdateMe = ( 25 | options?: UseMutationOptions< 26 | StoreCustomersRes, 27 | Error, 28 | { id: string } & StorePostCustomersCustomerReq, 29 | unknown 30 | > 31 | ) => { 32 | const { client } = useMedusa(); 33 | return useMutation( 34 | ({ id, ...data }: { id: string } & StorePostCustomersCustomerReq) => 35 | client.customers.update(data), 36 | options 37 | ); 38 | }; 39 | -------------------------------------------------------------------------------- /packages/components/src/store/customers/queries.ts: -------------------------------------------------------------------------------- 1 | import { 2 | StoreCustomersListOrdersRes, 3 | StoreCustomersRes, 4 | StoreGetCustomersCustomerOrdersParams, 5 | } from '@medusajs/medusa'; 6 | import { useQuery } from '@tanstack/vue-query'; 7 | import { Response } from '@medusajs/medusa-js'; 8 | import { useMedusa } from '../../../useApi'; 9 | import { UseQueryOptionsWrapper } from '../../../types'; 10 | import { queryKeysFactory } from '../../utils/index'; 11 | 12 | const CUSTOMERS_QUERY_KEY = `customers` as const; 13 | 14 | export const customerKeys = { 15 | ...queryKeysFactory(CUSTOMERS_QUERY_KEY), 16 | orders: (id: string) => [...customerKeys.detail(id), 'orders'] as const, 17 | }; 18 | 19 | type CustomerQueryKey = typeof customerKeys; 20 | 21 | export const useMeCustomer = ( 22 | options?: UseQueryOptionsWrapper< 23 | Response, 24 | Error, 25 | ReturnType 26 | > 27 | ) => { 28 | const { client } = useMedusa(); 29 | const { data, ...rest } = useQuery( 30 | customerKeys.detail('me'), 31 | () => client.customers.retrieve(), 32 | options 33 | ); 34 | return { data, ...rest } as const; 35 | }; 36 | 37 | export const useCustomerOrders = ( 38 | query: StoreGetCustomersCustomerOrdersParams = { limit: 10, offset: 0 }, 39 | options?: UseQueryOptionsWrapper< 40 | Response, 41 | Error, 42 | ReturnType 43 | > 44 | ) => { 45 | const { client } = useMedusa(); 46 | const { data, ...rest } = useQuery( 47 | customerKeys.orders('me'), 48 | () => client.customers.listOrders(query), 49 | options 50 | ); 51 | 52 | return { data, ...rest } as const; 53 | }; 54 | -------------------------------------------------------------------------------- /packages/components/src/store/gift-cards/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./queries" 2 | -------------------------------------------------------------------------------- /packages/components/src/store/gift-cards/queries.ts: -------------------------------------------------------------------------------- 1 | import { StoreGiftCardsRes } from '@medusajs/medusa'; 2 | import { Response } from '@medusajs/medusa-js'; 3 | import { useQuery } from '@tanstack/vue-query'; 4 | import { UseQueryOptionsWrapper } from '../../../types'; 5 | import { useMedusa } from '../../../useApi'; 6 | import { queryKeysFactory } from '../../utils/index'; 7 | 8 | const GIFT_CARDS_QUERY_KEY = `gift_cards` as const; 9 | 10 | export const giftCardKeys = queryKeysFactory(GIFT_CARDS_QUERY_KEY); 11 | 12 | type GiftCardQueryKey = typeof giftCardKeys; 13 | 14 | export const useGiftCard = ( 15 | id: string, 16 | options?: UseQueryOptionsWrapper< 17 | Response, 18 | Error, 19 | ReturnType 20 | > 21 | ) => { 22 | const { client } = useMedusa(); 23 | const { data, ...rest } = useQuery( 24 | giftCardKeys.detail(id), 25 | () => client.giftCards.retrieve(id), 26 | options 27 | ); 28 | return { data, ...rest } as const; 29 | }; 30 | -------------------------------------------------------------------------------- /packages/components/src/store/index.ts: -------------------------------------------------------------------------------- 1 | export * from './carts/'; 2 | export * from './collections/'; 3 | export * from './customers/'; 4 | export * from './gift-cards/'; 5 | export * from './line-items/'; 6 | export * from './orders/'; 7 | export * from './order-edits/'; 8 | export * from './payment-collections/'; 9 | export * from './products/'; 10 | export * from './product-types/'; 11 | export * from './regions/'; 12 | export * from './return-reasons/'; 13 | export * from './returns/'; 14 | export * from './shipping-options/'; 15 | export * from './swaps/'; 16 | -------------------------------------------------------------------------------- /packages/components/src/store/line-items/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./mutations" 2 | -------------------------------------------------------------------------------- /packages/components/src/store/line-items/mutations.ts: -------------------------------------------------------------------------------- 1 | import { 2 | StoreCartsRes, 3 | StorePostCartsCartLineItemsReq, 4 | StorePostCartsCartLineItemsItemReq, 5 | } from '@medusajs/medusa'; 6 | import { useMutation, UseMutationOptions } from '@tanstack/vue-query'; 7 | import { useMedusa } from '../../../useApi'; 8 | 9 | export const useCreateLineItem = ( 10 | cartId: string, 11 | options?: UseMutationOptions< 12 | StoreCartsRes, 13 | Error, 14 | StorePostCartsCartLineItemsReq, 15 | unknown 16 | > 17 | ) => { 18 | const { client } = useMedusa(); 19 | return useMutation( 20 | (data: StorePostCartsCartLineItemsReq) => 21 | client.carts.lineItems.create(cartId, data), 22 | options 23 | ); 24 | }; 25 | 26 | export const useUpdateLineItem = ( 27 | cartId: string, 28 | options?: UseMutationOptions< 29 | StoreCartsRes, 30 | Error, 31 | StorePostCartsCartLineItemsItemReq & { lineId: string }, 32 | unknown 33 | > 34 | ) => { 35 | const { client } = useMedusa(); 36 | return useMutation( 37 | ({ 38 | lineId, 39 | ...data 40 | }: StorePostCartsCartLineItemsItemReq & { lineId: string }) => 41 | client.carts.lineItems.update(cartId, lineId, data), 42 | options 43 | ); 44 | }; 45 | 46 | export const useDeleteLineItem = ( 47 | cartId: string, 48 | options?: UseMutationOptions< 49 | StoreCartsRes, 50 | Error, 51 | { lineId: string }, 52 | unknown 53 | > 54 | ) => { 55 | const { client } = useMedusa(); 56 | return useMutation( 57 | ({ lineId }: { lineId: string }) => 58 | client.carts.lineItems.delete(cartId, lineId), 59 | options 60 | ); 61 | }; 62 | -------------------------------------------------------------------------------- /packages/components/src/store/order-edits/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./queries" 2 | export * from './mutations' -------------------------------------------------------------------------------- /packages/components/src/store/order-edits/mutations.ts: -------------------------------------------------------------------------------- 1 | import { 2 | useMutation, 3 | UseMutationOptions, 4 | useQueryClient, 5 | } from '@tanstack/vue-query'; 6 | import { Response } from '@medusajs/medusa-js'; 7 | 8 | import { 9 | StoreOrderEditsRes, 10 | StorePostOrderEditsOrderEditDecline, 11 | } from '@medusajs/medusa'; 12 | 13 | import { buildOptions } from '../../utils/buildOptions'; 14 | import { useMedusa } from '../../../useApi'; 15 | import { orderEditQueryKeys } from './queries'; 16 | 17 | export const useDeclineOrderEdit = ( 18 | id: string, 19 | options?: UseMutationOptions< 20 | Response, 21 | Error, 22 | StorePostOrderEditsOrderEditDecline, 23 | unknown 24 | > 25 | ) => { 26 | const { client } = useMedusa(); 27 | const queryClient = useQueryClient(); 28 | 29 | return useMutation( 30 | (payload: StorePostOrderEditsOrderEditDecline) => 31 | client.orderEdits.decline(id, payload), 32 | buildOptions( 33 | queryClient, 34 | [orderEditQueryKeys.lists(), orderEditQueryKeys.detail(id)], 35 | options 36 | ) 37 | ); 38 | }; 39 | 40 | export const useCompleteOrderEdit = ( 41 | id: string, 42 | options?: UseMutationOptions< 43 | Response, 44 | Error, 45 | void, 46 | unknown 47 | > 48 | ) => { 49 | const { client } = useMedusa(); 50 | const queryClient = useQueryClient(); 51 | 52 | return useMutation( 53 | () => client.orderEdits.complete(id), 54 | buildOptions( 55 | queryClient, 56 | [orderEditQueryKeys.lists(), orderEditQueryKeys.detail(id)], 57 | options 58 | ) 59 | ); 60 | }; 61 | -------------------------------------------------------------------------------- /packages/components/src/store/order-edits/queries.ts: -------------------------------------------------------------------------------- 1 | import { queryKeysFactory } from '../../utils'; 2 | import { StoreOrderEditsRes } from '@medusajs/medusa'; 3 | import { useQuery } from '@tanstack/vue-query'; 4 | import { useMedusa } from '../../../useApi'; 5 | import { UseQueryOptionsWrapper } from '../../../types'; 6 | import { Response } from '@medusajs/medusa-js'; 7 | 8 | const ORDER_EDITS_QUERY_KEY = `orderEdit` as const; 9 | 10 | export const orderEditQueryKeys = queryKeysFactory< 11 | typeof ORDER_EDITS_QUERY_KEY 12 | >(ORDER_EDITS_QUERY_KEY); 13 | 14 | type OrderQueryKey = typeof orderEditQueryKeys; 15 | 16 | export const useOrderEdit = ( 17 | id: string, 18 | options?: UseQueryOptionsWrapper< 19 | Response, 20 | Error, 21 | ReturnType 22 | > 23 | ) => { 24 | const { client } = useMedusa(); 25 | const { data, ...rest } = useQuery( 26 | orderEditQueryKeys.detail(id), 27 | () => client.orderEdits.retrieve(id), 28 | options 29 | ); 30 | 31 | return { data, ...rest } as const; 32 | }; 33 | -------------------------------------------------------------------------------- /packages/components/src/store/orders/index.ts: -------------------------------------------------------------------------------- 1 | export * from './queries'; 2 | export * from './mutations'; 3 | -------------------------------------------------------------------------------- /packages/components/src/store/orders/mutations.ts: -------------------------------------------------------------------------------- 1 | import { 2 | StorePostCustomersCustomerAcceptClaimReq, 3 | StorePostCustomersCustomerOrderClaimReq, 4 | } from '@medusajs/medusa'; 5 | import { Response } from '@medusajs/medusa-js'; 6 | import { 7 | UseMutationOptions, 8 | useMutation, 9 | useQueryClient, 10 | } from '@tanstack/vue-query'; 11 | import { orderKeys } from './queries'; 12 | import { useMedusa } from '../../../useApi'; 13 | import { buildOptions } from '../../utils/buildOptions'; 14 | 15 | export const useRequestOrderAccess = ( 16 | options?: UseMutationOptions< 17 | Response<{}>, 18 | Error, 19 | StorePostCustomersCustomerOrderClaimReq, 20 | unknown 21 | > 22 | ) => { 23 | const { client } = useMedusa(); 24 | const queryClient = useQueryClient(); 25 | 26 | return useMutation( 27 | (payload: StorePostCustomersCustomerOrderClaimReq) => 28 | client.orders.requestCustomerOrders(payload), 29 | buildOptions(queryClient, [orderKeys.all], options) 30 | ); 31 | }; 32 | export const useGrantOrderAccess = ( 33 | options?: UseMutationOptions< 34 | Response<{}>, 35 | Error, 36 | StorePostCustomersCustomerAcceptClaimReq, 37 | unknown 38 | > 39 | ) => { 40 | const { client } = useMedusa(); 41 | const queryClient = useQueryClient(); 42 | 43 | return useMutation( 44 | (payload: StorePostCustomersCustomerAcceptClaimReq) => 45 | client.orders.confirmRequest(payload), 46 | buildOptions(queryClient, [orderKeys.all], options) 47 | ); 48 | }; 49 | -------------------------------------------------------------------------------- /packages/components/src/store/orders/queries.ts: -------------------------------------------------------------------------------- 1 | import { queryKeysFactory } from '../../utils/index'; 2 | import { StoreOrdersRes, StoreGetOrdersParams } from '@medusajs/medusa'; 3 | import { useQuery } from '@tanstack/vue-query'; 4 | import { useMedusa } from '../../../useApi'; 5 | import { UseQueryOptionsWrapper } from '../../../types'; 6 | import { Response } from '@medusajs/medusa-js'; 7 | 8 | const ORDERS_QUERY_KEY = `orders` as const; 9 | 10 | export const orderKeys = { 11 | ...queryKeysFactory( 12 | ORDERS_QUERY_KEY 13 | ), 14 | cart: (cartId: string) => [...orderKeys.details(), 'cart', cartId] as const, 15 | }; 16 | 17 | type OrderQueryKey = typeof orderKeys; 18 | 19 | export const useOrder = ( 20 | id: string, 21 | options?: UseQueryOptionsWrapper< 22 | Response, 23 | Error, 24 | ReturnType 25 | > 26 | ) => { 27 | const { client } = useMedusa(); 28 | const { data, ...rest } = useQuery( 29 | orderKeys.detail(id), 30 | () => client.orders.retrieve(id), 31 | options 32 | ); 33 | 34 | return { data, ...rest } as const; 35 | }; 36 | 37 | export const useCartOrder = ( 38 | cartId: string, 39 | options?: UseQueryOptionsWrapper< 40 | Response, 41 | Error, 42 | ReturnType 43 | > 44 | ) => { 45 | const { client } = useMedusa(); 46 | const { data, ...rest } = useQuery( 47 | orderKeys.cart(cartId), 48 | () => client.orders.retrieveByCartId(cartId), 49 | options 50 | ); 51 | 52 | return { data, ...rest } as const; 53 | }; 54 | 55 | export const useOrders = ( 56 | query: StoreGetOrdersParams, 57 | options?: UseQueryOptionsWrapper< 58 | Response, 59 | Error, 60 | ReturnType 61 | > 62 | ) => { 63 | const { client } = useMedusa(); 64 | const { data, ...rest } = useQuery( 65 | orderKeys.list(query), 66 | () => client.orders.lookupOrder(query), 67 | options 68 | ); 69 | 70 | return { data, ...rest } as const; 71 | }; 72 | -------------------------------------------------------------------------------- /packages/components/src/store/payment-collections/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./queries" 2 | export * from "./mutations" 3 | -------------------------------------------------------------------------------- /packages/components/src/store/payment-collections/mutations.ts: -------------------------------------------------------------------------------- 1 | import { 2 | useMutation, 3 | UseMutationOptions, 4 | useQueryClient, 5 | } from '@tanstack/vue-query'; 6 | import { Response } from '@medusajs/medusa-js'; 7 | 8 | import { 9 | StorePaymentCollectionsRes, 10 | StorePostPaymentCollectionsBatchSessionsReq, 11 | StorePostPaymentCollectionsBatchSessionsAuthorizeReq, 12 | StorePaymentCollectionSessionsReq, 13 | StorePaymentCollectionsSessionRes, 14 | } from '@medusajs/medusa'; 15 | 16 | import { buildOptions } from '../../utils/buildOptions'; 17 | import { useMedusa } from '../../../useApi'; 18 | import { paymentCollectionQueryKeys } from './queries'; 19 | 20 | export const useManageMultiplePaymentSessions = ( 21 | id: string, 22 | options?: UseMutationOptions< 23 | Response, 24 | Error, 25 | StorePostPaymentCollectionsBatchSessionsReq, 26 | unknown 27 | > 28 | ) => { 29 | const { client } = useMedusa(); 30 | const queryClient = useQueryClient(); 31 | 32 | return useMutation( 33 | (payload: StorePostPaymentCollectionsBatchSessionsReq) => 34 | client.paymentCollections.managePaymentSessionsBatch(id, payload), 35 | buildOptions( 36 | queryClient, 37 | [ 38 | paymentCollectionQueryKeys.lists(), 39 | paymentCollectionQueryKeys.detail(id), 40 | ], 41 | options 42 | ) 43 | ); 44 | }; 45 | 46 | export const useManagePaymentSession = ( 47 | id: string, 48 | options?: UseMutationOptions< 49 | Response, 50 | Error, 51 | StorePaymentCollectionSessionsReq, 52 | unknown 53 | > 54 | ) => { 55 | const { client } = useMedusa(); 56 | const queryClient = useQueryClient(); 57 | 58 | return useMutation( 59 | (payload: StorePaymentCollectionSessionsReq) => 60 | client.paymentCollections.managePaymentSession(id, payload), 61 | buildOptions( 62 | queryClient, 63 | [ 64 | paymentCollectionQueryKeys.lists(), 65 | paymentCollectionQueryKeys.detail(id), 66 | ], 67 | options 68 | ) 69 | ); 70 | }; 71 | 72 | export const useAuthorizePaymentSession = ( 73 | id: string, 74 | options?: UseMutationOptions< 75 | Response, 76 | Error, 77 | string, 78 | unknown 79 | > 80 | ) => { 81 | const { client } = useMedusa(); 82 | const queryClient = useQueryClient(); 83 | 84 | return useMutation( 85 | (session_id: string) => 86 | client.paymentCollections.authorizePaymentSession(id, session_id), 87 | buildOptions( 88 | queryClient, 89 | [ 90 | paymentCollectionQueryKeys.lists(), 91 | paymentCollectionQueryKeys.detail(id), 92 | ], 93 | options 94 | ) 95 | ); 96 | }; 97 | 98 | export const useAuthorizePaymentSessionsBatch = ( 99 | id: string, 100 | options?: UseMutationOptions< 101 | Response, 102 | Error, 103 | StorePostPaymentCollectionsBatchSessionsAuthorizeReq, 104 | unknown 105 | > 106 | ) => { 107 | const { client } = useMedusa(); 108 | const queryClient = useQueryClient(); 109 | 110 | return useMutation( 111 | payload => 112 | client.paymentCollections.authorizePaymentSessionsBatch(id, payload), 113 | buildOptions( 114 | queryClient, 115 | [ 116 | paymentCollectionQueryKeys.lists(), 117 | paymentCollectionQueryKeys.detail(id), 118 | ], 119 | options 120 | ) 121 | ); 122 | }; 123 | 124 | export const usePaymentCollectionRefreshPaymentSession = ( 125 | id: string, 126 | options?: UseMutationOptions< 127 | Response, 128 | Error, 129 | string, 130 | unknown 131 | > 132 | ) => { 133 | const { client } = useMedusa(); 134 | const queryClient = useQueryClient(); 135 | 136 | return useMutation( 137 | (session_id: string) => 138 | client.paymentCollections.refreshPaymentSession(id, session_id), 139 | buildOptions( 140 | queryClient, 141 | [ 142 | paymentCollectionQueryKeys.lists(), 143 | paymentCollectionQueryKeys.detail(id), 144 | ], 145 | options 146 | ) 147 | ); 148 | }; 149 | -------------------------------------------------------------------------------- /packages/components/src/store/payment-collections/queries.ts: -------------------------------------------------------------------------------- 1 | import { queryKeysFactory } from '../../utils'; 2 | import { StorePaymentCollectionsRes } from '@medusajs/medusa'; 3 | import { useQuery } from '@tanstack/vue-query'; 4 | import { useMedusa } from '../../../useApi'; 5 | import { UseQueryOptionsWrapper } from '../../../types'; 6 | import { Response } from '@medusajs/medusa-js'; 7 | 8 | const PAYMENT_COLLECTION_QUERY_KEY = `paymentCollection` as const; 9 | 10 | export const paymentCollectionQueryKeys = queryKeysFactory< 11 | typeof PAYMENT_COLLECTION_QUERY_KEY 12 | >(PAYMENT_COLLECTION_QUERY_KEY); 13 | 14 | type PaymentCollectionKey = typeof paymentCollectionQueryKeys; 15 | 16 | export const usePaymentCollection = ( 17 | id: string, 18 | options?: UseQueryOptionsWrapper< 19 | Response, 20 | Error, 21 | ReturnType 22 | > 23 | ) => { 24 | const { client } = useMedusa(); 25 | const { data, ...rest } = useQuery( 26 | paymentCollectionQueryKeys.detail(id), 27 | () => client.paymentCollections.retrieve(id), 28 | options 29 | ); 30 | 31 | return { data, ...rest } as const; 32 | }; 33 | -------------------------------------------------------------------------------- /packages/components/src/store/product-types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./queries" 2 | -------------------------------------------------------------------------------- /packages/components/src/store/product-types/queries.ts: -------------------------------------------------------------------------------- 1 | import { 2 | StoreGetProductTypesParams, 3 | StoreProductTypesListRes, 4 | } from '@medusajs/medusa'; 5 | import { Response } from '@medusajs/medusa-js'; 6 | import { useQuery } from '@tanstack/vue-query'; 7 | import { useMedusa } from '../../../useApi'; 8 | import { UseQueryOptionsWrapper } from '../../../types'; 9 | import { queryKeysFactory } from '../../utils'; 10 | 11 | const PRODUCT_TYPES_QUERY_KEY = `product_types` as const; 12 | 13 | export const productTypeKeys = queryKeysFactory(PRODUCT_TYPES_QUERY_KEY); 14 | 15 | type ProductTypesQueryKeys = typeof productTypeKeys; 16 | 17 | export const useProductTypes = ( 18 | query?: StoreGetProductTypesParams, 19 | options?: UseQueryOptionsWrapper< 20 | Response, 21 | Error, 22 | ReturnType 23 | > 24 | ) => { 25 | const { client } = useMedusa(); 26 | const { data, ...rest } = useQuery( 27 | productTypeKeys.list(query), 28 | () => client.productTypes.list(query), 29 | options 30 | ); 31 | return { data, ...rest } as const; 32 | }; 33 | -------------------------------------------------------------------------------- /packages/components/src/store/products/index.ts: -------------------------------------------------------------------------------- 1 | import { useProducts } from '@medusa-vue/core'; 2 | import { 3 | AllowedComponentProps, 4 | ComponentCustomProps, 5 | UnwrapRef, 6 | VNode, 7 | VNodeProps, 8 | defineComponent, 9 | h, 10 | reactive, 11 | } from 'vue'; 12 | import { makeTagProps } from '../../util/makeProps'; 13 | import type { TagProps } from '../../util/makeProps'; 14 | 15 | export interface MedusaVueComponentProps extends TagProps {} 16 | 17 | export interface UseMedusaProductsComponentProps 18 | extends MedusaVueComponentProps {} 19 | 20 | export const UseMedusaProductsImplementation = /*#__PURE__*/ defineComponent({ 21 | name: 'UseMedusaProducts', 22 | props: { 23 | ...makeTagProps(), 24 | }, 25 | 26 | setup(props, { slots }) { 27 | const data = useProducts(); 28 | 29 | return () => { 30 | const slotProps = reactive(data); 31 | const children = slots.default && slots.default(slotProps); 32 | const fallback = slots.fallback && slots.fallback(); 33 | const component = props.tag ? h(props.tag, {}, children) : children; 34 | 35 | return (data.isLoading.value && fallback) || component; 36 | }; 37 | }, 38 | }); 39 | 40 | export const UseProducts: _UseMedusaProductsI = 41 | UseMedusaProductsImplementation as any; 42 | 43 | export interface _UseMedusaProductsI { 44 | new (): { 45 | $props: AllowedComponentProps & 46 | ComponentCustomProps & 47 | VNodeProps & 48 | UseMedusaProductsComponentProps; 49 | 50 | $slots: { 51 | default?: (data: UnwrapRef>) => VNode[]; 52 | fallback?: () => VNode[]; 53 | }; 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /packages/components/src/store/regions/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./queries" 2 | -------------------------------------------------------------------------------- /packages/components/src/store/regions/queries.ts: -------------------------------------------------------------------------------- 1 | import { queryKeysFactory } from '../../utils/index'; 2 | import { UseQueryOptionsWrapper } from '../../../types'; 3 | import { useQuery } from '@tanstack/vue-query'; 4 | import { useMedusa } from '../../../useApi'; 5 | import { StoreRegionsRes, StoreRegionsListRes } from '@medusajs/medusa'; 6 | import { Response } from '@medusajs/medusa-js'; 7 | 8 | const REGIONS_QUERY_KEY = `regions` as const; 9 | 10 | const regionsKey = queryKeysFactory(REGIONS_QUERY_KEY); 11 | 12 | type RegionQueryType = typeof regionsKey; 13 | 14 | export const useRegions = ( 15 | options?: UseQueryOptionsWrapper< 16 | Response, 17 | Error, 18 | ReturnType 19 | > 20 | ) => { 21 | const { client } = useMedusa(); 22 | const { data, ...rest } = useQuery( 23 | regionsKey.lists(), 24 | () => client.regions.list(), 25 | options 26 | ); 27 | 28 | return { data, ...rest } as const; 29 | }; 30 | 31 | export const useRegion = ( 32 | id: string, 33 | options?: UseQueryOptionsWrapper< 34 | Response, 35 | Error, 36 | ReturnType 37 | > 38 | ) => { 39 | const { client } = useMedusa(); 40 | const { data, ...rest } = useQuery( 41 | regionsKey.detail(id), 42 | () => client.regions.retrieve(id), 43 | options 44 | ); 45 | return { data, ...rest } as const; 46 | }; 47 | -------------------------------------------------------------------------------- /packages/components/src/store/return-reasons/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./queries" 2 | -------------------------------------------------------------------------------- /packages/components/src/store/return-reasons/queries.ts: -------------------------------------------------------------------------------- 1 | import { queryKeysFactory } from '../../utils/index'; 2 | import { 3 | StoreReturnReasonsListRes, 4 | StoreReturnReasonsRes, 5 | } from '@medusajs/medusa'; 6 | import { Response } from '@medusajs/medusa-js'; 7 | import { useQuery } from '@tanstack/vue-query'; 8 | import { useMedusa } from '../../../useApi'; 9 | import { UseQueryOptionsWrapper } from '../../../types'; 10 | 11 | const RETURNS_REASONS_QUERY_KEY = `return_reasons` as const; 12 | 13 | const returnReasonsKey = queryKeysFactory(RETURNS_REASONS_QUERY_KEY); 14 | 15 | type ReturnReasonsQueryKey = typeof returnReasonsKey; 16 | 17 | export const useReturnReasons = ( 18 | options?: UseQueryOptionsWrapper< 19 | Response, 20 | Error, 21 | ReturnType 22 | > 23 | ) => { 24 | const { client } = useMedusa(); 25 | const { data, ...rest } = useQuery( 26 | returnReasonsKey.lists(), 27 | () => client.returnReasons.list(), 28 | options 29 | ); 30 | return { data, ...rest } as const; 31 | }; 32 | 33 | export const useReturnReason = ( 34 | id: string, 35 | options?: UseQueryOptionsWrapper< 36 | Response, 37 | Error, 38 | ReturnType 39 | > 40 | ) => { 41 | const { client } = useMedusa(); 42 | const { data, ...rest } = useQuery( 43 | returnReasonsKey.detail(id), 44 | () => client.returnReasons.retrieve(id), 45 | options 46 | ); 47 | return { data, ...rest } as const; 48 | }; 49 | -------------------------------------------------------------------------------- /packages/components/src/store/returns/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./mutations" 2 | -------------------------------------------------------------------------------- /packages/components/src/store/returns/mutations.ts: -------------------------------------------------------------------------------- 1 | import { StoreReturnsRes, StorePostReturnsReq } from '@medusajs/medusa'; 2 | import { useMutation, UseMutationOptions } from '@tanstack/vue-query'; 3 | import { useMedusa } from '../../../useApi'; 4 | 5 | export const useCreateReturn = ( 6 | options?: UseMutationOptions< 7 | StoreReturnsRes, 8 | Error, 9 | StorePostReturnsReq, 10 | unknown 11 | > 12 | ) => { 13 | const { client } = useMedusa(); 14 | return useMutation( 15 | (data: StorePostReturnsReq) => client.returns.create(data), 16 | options 17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /packages/components/src/store/shipping-options/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./queries" 2 | -------------------------------------------------------------------------------- /packages/components/src/store/shipping-options/queries.ts: -------------------------------------------------------------------------------- 1 | import { queryKeysFactory } from '../../utils/index'; 2 | import { UseQueryOptionsWrapper } from '../../../types'; 3 | import { useQuery } from '@tanstack/vue-query'; 4 | import { useMedusa } from '../../../useApi'; 5 | import { 6 | StoreShippingOptionsListRes, 7 | StoreGetShippingOptionsParams, 8 | } from '@medusajs/medusa'; 9 | import { Response } from '@medusajs/medusa-js'; 10 | 11 | const SHIPPING_OPTION_QUERY_KEY = `shipping_options` as const; 12 | 13 | const shippingOptionKey = { 14 | ...queryKeysFactory(SHIPPING_OPTION_QUERY_KEY), 15 | cart: (cartId: string) => [...shippingOptionKey.all, 'cart', cartId] as const, 16 | }; 17 | 18 | type ShippingOptionQueryKey = typeof shippingOptionKey; 19 | 20 | export const useShippingOptions = ( 21 | query?: StoreGetShippingOptionsParams, 22 | options?: UseQueryOptionsWrapper< 23 | Response, 24 | Error, 25 | ReturnType 26 | > 27 | ) => { 28 | const { client } = useMedusa(); 29 | const { data, ...rest } = useQuery( 30 | shippingOptionKey.list(query), 31 | async () => client.shippingOptions.list(query), 32 | options 33 | ); 34 | return { data, ...rest } as const; 35 | }; 36 | 37 | export const useCartShippingOptions = ( 38 | cartId: string, 39 | options?: UseQueryOptionsWrapper< 40 | Response, 41 | Error, 42 | ReturnType 43 | > 44 | ) => { 45 | const { client } = useMedusa(); 46 | const { data, ...rest } = useQuery( 47 | shippingOptionKey.cart(cartId), 48 | async () => client.shippingOptions.listCartOptions(cartId), 49 | options 50 | ); 51 | return { data, ...rest } as const; 52 | }; 53 | -------------------------------------------------------------------------------- /packages/components/src/store/swaps/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./queries" 2 | export * from "./mutations" 3 | -------------------------------------------------------------------------------- /packages/components/src/store/swaps/mutations.ts: -------------------------------------------------------------------------------- 1 | import { StoreSwapsRes, StorePostSwapsReq } from '@medusajs/medusa'; 2 | import { useMutation, UseMutationOptions } from '@tanstack/vue-query'; 3 | import { useMedusa } from '../../../useApi'; 4 | 5 | export const useCreateSwap = ( 6 | options?: UseMutationOptions 7 | ) => { 8 | const { client } = useMedusa(); 9 | return useMutation( 10 | (data: StorePostSwapsReq) => client.swaps.create(data), 11 | options 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /packages/components/src/store/swaps/queries.ts: -------------------------------------------------------------------------------- 1 | import { queryKeysFactory } from '../../utils/index'; 2 | import { StoreSwapsRes } from '@medusajs/medusa'; 3 | import { Response } from '@medusajs/medusa-js'; 4 | import { useQuery } from '@tanstack/vue-query'; 5 | import { useMedusa } from '../../../useApi'; 6 | import { UseQueryOptionsWrapper } from '../../../types'; 7 | 8 | const SWAPS_QUERY_KEY = `swaps` as const; 9 | 10 | const swapKey = { 11 | ...queryKeysFactory(SWAPS_QUERY_KEY), 12 | cart: (cartId: string) => [...swapKey.all, 'cart', cartId] as const, 13 | }; 14 | 15 | type SwapQueryKey = typeof swapKey; 16 | 17 | export const useCartSwap = ( 18 | cartId: string, 19 | options?: UseQueryOptionsWrapper< 20 | Response, 21 | Error, 22 | ReturnType 23 | > 24 | ) => { 25 | const { client } = useMedusa(); 26 | const { data, ...rest } = useQuery( 27 | swapKey.cart(cartId), 28 | () => client.swaps.retrieveByCartId(cartId), 29 | options 30 | ); 31 | 32 | return { data, ...rest } as const; 33 | }; 34 | -------------------------------------------------------------------------------- /packages/components/src/util/makeProps.ts: -------------------------------------------------------------------------------- 1 | // Utilities 2 | import { propsFactory } from './propsFactory'; 3 | 4 | // Types 5 | export interface TagProps { 6 | tag: string; 7 | } 8 | 9 | // Composables 10 | export const makeTagProps = propsFactory( 11 | { 12 | tag: { 13 | type: String, 14 | default: 'div', 15 | }, 16 | }, 17 | 'tag' 18 | ); 19 | -------------------------------------------------------------------------------- /packages/components/src/util/propsFactory.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Borrowed from vuetify 3 | * see: https://github.com/vuetifyjs/vuetify/blob/next/packages/vuetify/src/util/propsFactory.ts 4 | */ 5 | 6 | import type { ComponentObjectPropsOptions, Prop, PropType } from 'vue'; 7 | 8 | /** 9 | * Creates a factory function for props definitions. 10 | * This is used to define props in a composable then override 11 | * default values in an implementing component. 12 | * 13 | * @example Simplified signature 14 | * (props: Props) => (defaults?: Record) => Props 15 | * 16 | * @example Usage 17 | * const makeProps = propsFactory({ 18 | * foo: String, 19 | * }) 20 | * 21 | * defineComponent({ 22 | * props: { 23 | * ...makeProps({ 24 | * foo: 'a', 25 | * }), 26 | * }, 27 | * setup (props) { 28 | * // would be "string | undefined", now "string" because a default has been provided 29 | * props.foo 30 | * }, 31 | * } 32 | */ 33 | 34 | export function propsFactory( 35 | props: PropsOptions, 36 | source: string 37 | ) { 38 | return = {}>( 39 | defaults?: Defaults 40 | ): AppendDefault => { 41 | return Object.keys(props).reduce((obj, prop) => { 42 | const isObjectDefinition = 43 | typeof props[prop] === 'object' && 44 | props[prop] != null && 45 | !Array.isArray(props[prop]); 46 | const definition = isObjectDefinition 47 | ? props[prop] 48 | : { type: props[prop] }; 49 | 50 | if (defaults && prop in defaults) { 51 | obj[prop] = { 52 | ...definition, 53 | default: defaults[prop], 54 | }; 55 | } else { 56 | obj[prop] = definition; 57 | } 58 | 59 | if (source && !obj[prop].source) { 60 | obj[prop].source = source; 61 | } 62 | 63 | return obj; 64 | }, {}); 65 | }; 66 | } 67 | 68 | type AppendDefault< 69 | T extends ComponentObjectPropsOptions, 70 | D extends PartialKeys 71 | > = { 72 | [P in keyof T]-?: unknown extends D[P] 73 | ? T[P] 74 | : T[P] extends Record 75 | ? Omit & { 76 | type: PropType>; 77 | default: MergeDefault; 78 | } 79 | : { 80 | type: PropType>; 81 | default: MergeDefault; 82 | }; 83 | }; 84 | 85 | type MergeDefault = unknown extends D 86 | ? InferPropType 87 | : NonNullable> | D; 88 | 89 | /** 90 | * Like `Partial` but doesn't care what the value is 91 | */ 92 | type PartialKeys = { [P in keyof T]?: unknown }; 93 | 94 | // Copied from Vue 95 | type InferPropType = T extends null 96 | ? any // null & true would fail to infer 97 | : T extends { type: null | true } 98 | ? any // As TS issue https://github.com/Microsoft/TypeScript/issues/14829 // somehow `ObjectConstructor` when inferred from { (): T } becomes `any` // `BooleanConstructor` when inferred from PropConstructor(with PropMethod) becomes `Boolean` 99 | : T extends ObjectConstructor | { type: ObjectConstructor } 100 | ? Record 101 | : T extends BooleanConstructor | { type: BooleanConstructor } 102 | ? boolean 103 | : T extends Prop 104 | ? unknown extends V 105 | ? D 106 | : V 107 | : T; 108 | -------------------------------------------------------------------------------- /packages/components/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src", "types", "node_modules/@types"], 3 | "compilerOptions": { 4 | "rootDir": "./src", 5 | "outDir": "./lib", 6 | "module": "esnext", 7 | "lib": ["dom", "esnext"], 8 | "importHelpers": true, 9 | "declaration": true, 10 | "sourceMap": true, 11 | "strict": true, 12 | "noImplicitReturns": true, 13 | "noFallthroughCasesInSwitch": true, 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true, 16 | "moduleResolution": "node", 17 | "esModuleInterop": true, 18 | "skipLibCheck": true, 19 | "forceConsistentCasingInFileNames": true, 20 | "noEmit": true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/core/.eslintignore: -------------------------------------------------------------------------------- 1 | src/types.ts -------------------------------------------------------------------------------- /packages/core/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | node_modules 4 | .cache 5 | dist 6 | -------------------------------------------------------------------------------- /packages/core/CHANGELOG.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raukaute/medusa-vue/97500e8e2c3ff63ec91a80acdb0a9e61032c8e4c/packages/core/CHANGELOG.md -------------------------------------------------------------------------------- /packages/core/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Ulrich Krause 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/core/README copy.md: -------------------------------------------------------------------------------- 1 | # Medusa React 2 | 3 | A React library providing a set of components, utilities, and hooks for interacting seamlessly with a Medusa backend and building custom React storefronts. 4 | 5 | ## Installation 6 | 7 | The library uses [react-query](https://react-query.tanstack.com/overview) as a solution for server-side state management and lists the library as a peer dependency. 8 | 9 | In order to install the package, run the following 10 | 11 | ```bash 12 | npm install medusa-react react-query @medusajs/medusa 13 | # or 14 | yarn add medusa-react react-query @medusajs/medusa 15 | ``` 16 | 17 | ## Quick Start 18 | 19 | In order to use the hooks exposed by medusa-react, you will need to include the `MedusaProvider` somewhere up in your component tree. The `MedusaProvider` takes a `baseUrl` prop which should point to your Medusa server. Under the hood, `medusa-react` uses the `medusa-js` client library (built on top of axios) to interact with your server. 20 | 21 | In addition, because medusa-react is built on top of react-query, you can pass an object representing react-query's [QueryClientProvider](https://react-query.tanstack.com/reference/QueryClientProvider#_top) props, which will be passed along by `MedusaProvider`. 22 | 23 | ```jsx 24 | // App.tsx 25 | 26 | import * as React from "react" 27 | import { QueryClient } from "react-query" 28 | import { MedusaProvider } from "medusa-react" 29 | import MyStorefront from "./my-storefront" 30 | 31 | // Your react-query's query client config 32 | const queryClient = new QueryClient({ 33 | defaultOptions: { 34 | queries: { 35 | refetchOnWindowFocus: false, 36 | staleTime: 30000, 37 | retry: 1, 38 | }, 39 | }, 40 | }) 41 | 42 | const App = () => { 43 | return ( 44 | 48 | 49 | 50 | ) 51 | } 52 | 53 | export default App 54 | ``` 55 | 56 | The hooks exposed by `medusa-react` fall into two main categories: queries and mutations. 57 | 58 | ### Queries 59 | 60 | [Queries](https://react-query.tanstack.com/guides/queries#_top) simply wrap around react-query's `useQuery` hook to fetch some data from your medusa server 61 | 62 | ```jsx 63 | // ./my-storefront.tsx 64 | import * as React from "react" 65 | import { useProducts } from "medusa-react" 66 | 67 | const MyStorefront = () => { 68 | const { products, isLoading } = useProducts() 69 | 70 | return isLoading ? ( 71 | 72 | ) : ( 73 | products.map((product) => ) 74 | ) 75 | } 76 | ``` 77 | 78 | In general, the queries will return everything react-query returns from [`useQuery`](https://react-query.tanstack.com/reference/useQuery#_top) except the `data` field, which will be flattened out. In addition, you can also access the HTTP response object returned from the `medusa-js` client including things like `status`, `headers`, etc. 79 | 80 | So, in other words, we can express what the above query returns as the following: 81 | 82 | ```typescript 83 | import { UseQueryResult } from "react-query" 84 | 85 | // This is what a Medusa server returns when you hit the GET /store/products endpoint 86 | type ProductsResponse = { 87 | products: Product[] 88 | limit: number 89 | offset: number 90 | } 91 | 92 | // UseProductsQuery refers to what's returned by the useProducts hook 93 | type UseProductsQuery = ProductsResponse & 94 | Omit & { 95 | response: { 96 | status: number 97 | statusText: string 98 | headers: Record & { 99 | "set-cookie"?: string[] 100 | } 101 | config: any 102 | request?: any 103 | } 104 | } 105 | 106 | // More generally ... 107 | 108 | type QueryReturnType = APIResponse & 109 | Omit & { 110 | response: { 111 | status: number 112 | statusText: string 113 | headers: Record & { 114 | "set-cookie"?: string[] 115 | } 116 | config: any 117 | request?: any 118 | } 119 | } 120 | ``` 121 | 122 | ### Mutations 123 | 124 | [Mutations](https://react-query.tanstack.com/guides/mutations#_top) wrap around react-query's `useMutation` to mutate data and perform server-side effects on your medusa server. If you are not entirely familiar with this idea of "mutations", creating a cart would be a mutation because it creates a cart in your server (and database). Mutations also have to be invoked imperatively, meaning that calling for the mutation to take action, you will have to call a `mutate()` function returned from mutation hooks. 125 | 126 | ```jsx 127 | import * as React from "react" 128 | import { useCreateCart } from "medusa-react" 129 | 130 | const CreateCartButton = () => { 131 | const createCart = useCreateCart() 132 | const handleClick = () => { 133 | createCart.mutate({}) // create an empty cart 134 | } 135 | 136 | return ( 137 | 140 | ) 141 | } 142 | ``` 143 | 144 | The mutation hooks will return exactly what react-query's [`useMutation`](https://react-query.tanstack.com/reference/useMutation#_top) returns. In addition, the options you pass in to the hooks will be passed along to `useMutation`. 145 | 146 | ### Utilities 147 | 148 | A set of utility functions are also exposed from the library to make your life easier when dealing with displaying money amounts 149 | 150 | #### `formatVariantPrice()` 151 | 152 | - `formatVariantPrice(params: FormatVariantPriceParams): string` 153 | 154 | ```typescript 155 | type FormatVariantPriceParams = { 156 | variant: ProductVariantInfo 157 | region: RegionInfo 158 | includeTaxes?: boolean 159 | minimumFractionDigits?: number 160 | maximumFractionDigits?: number 161 | locale?: string 162 | } 163 | 164 | type ProductVariantInfo = Pick 165 | 166 | type RegionInfo = { 167 | currency_code: string 168 | tax_code: string 169 | tax_rate: number 170 | } 171 | ``` 172 | 173 | Given a variant and region, will return a string representing the localized amount (i.e: `$19.50`) 174 | 175 | The behavior of minimumFractionDigits and maximumFractionDigits is the same as the one explained by MDN [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat). In fact, in order to convert the decimal amount, we use the browser's `Intl.NumberFormat` method. 176 | 177 | #### `computeVariantPrice()` 178 | 179 | - `computeVariantPrice(params: ComputeVariantPriceParams): number` 180 | 181 | ```typescript 182 | type ComputeVariantPriceParams = { 183 | variant: ProductVariantInfo 184 | region: RegionInfo 185 | includeTaxes?: boolean 186 | } 187 | ``` 188 | 189 | Determines a variant's price based on the region provided. Returns a decimal number representing the amount. 190 | 191 | #### `formatAmount()` 192 | 193 | - `formatAmount(params: FormatAmountParams): string` 194 | 195 | ```typescript 196 | type FormatAmountParams = { 197 | amount: number 198 | region: RegionInfo 199 | includeTaxes?: boolean 200 | minimumFractionDigits?: number 201 | maximumFractionDigits?: number 202 | locale?: string 203 | } 204 | ``` 205 | 206 | Returns a localized string based on the input params representing the amount (i.e: "$10.99"). 207 | 208 | #### `computeAmount()` 209 | 210 | - `computeAmount(params: ComputeAmountParams): number` 211 | 212 | ```typescript 213 | type ComputeAmountParams = { 214 | amount: number 215 | region: RegionInfo 216 | includeTaxes?: boolean 217 | } 218 | ``` 219 | 220 | Takes an integer amount, a region, and includeTaxes boolean. Returns a decimal amount including (or excluding) taxes. 221 | 222 | ### Context Providers (Experimental) 223 | 224 | In order to make building custom storefronts easier, we also expose a `SessionCartProvider` and a `CartProvider` . At first, the two sound very similar to each other, however, the main distinction between the two is that the `SessionCartProvider` never interacts with your medusa server. 225 | 226 | The main goal behind the provider is to manage the state related to your users' cart experience. In other words, the provider keeps track of the items users add to their cart and help you interact with those items through a set of helpful methods like `addItem`, `updateQuantity`, `removeItem` , etc. 227 | 228 | On the other hand the `CartProvider` makes use of some of the hooks already exposed by `medusa-react` to help you create a cart (on the medusa backend), start the checkout flow, authorize payment sessions, etc. It also manages one single global piece of state which represents a cart, exactly like the one created on your medusa backend. 229 | 230 | You can think of a `sessionCart` as a purely client-side lightweight cart, in other words, just a javascript object living in your browser, whereas `cart` is the entity which you have stored in your database. 231 | 232 | ### SessionCart 233 | 234 | The first step to using the `SessionCartProvider` is by inserting it somewhere up in your component tree. 235 | 236 | ```jsx 237 | // App.tsx 238 | 239 | import * as React from "react" 240 | import { QueryClient } from "react-query" 241 | import { MedusaProvider, SessionCartProvider } from "medusa-react" 242 | import MyStorefront from "./my-storefront" 243 | 244 | // Your react-query's query client config 245 | const queryClient = new QueryClient({ 246 | defaultOptions: { 247 | queries: { 248 | refetchOnWindowFocus: false, 249 | staleTime: 30000, 250 | retry: 1, 251 | }, 252 | }, 253 | }) 254 | 255 | const App = () => { 256 | return ( 257 | 261 | 262 | 263 | 264 | 265 | ) 266 | } 267 | 268 | export default App 269 | ``` 270 | 271 | ### Cart 272 | 273 | ```jsx 274 | // App.tsx 275 | 276 | import * as React from "react" 277 | import { QueryClient } from "react-query" 278 | import { MedusaProvider, CartProvider } from "medusa-react" 279 | import MyStorefront from "./my-storefront" 280 | 281 | // Your react-query's query client config 282 | const queryClient = new QueryClient({ 283 | defaultOptions: { 284 | queries: { 285 | refetchOnWindowFocus: false, 286 | staleTime: 30000, 287 | retry: 1, 288 | }, 289 | }, 290 | }) 291 | 292 | const App = () => { 293 | return ( 294 | 298 | 299 | 300 | 301 | 302 | ) 303 | } 304 | 305 | export default App 306 | ``` 307 | -------------------------------------------------------------------------------- /packages/core/README.md: -------------------------------------------------------------------------------- 1 | # Medusa Vue 2 | 3 | Vue 3 composables and components for seamless and streamlined interaction with a [Medusa](https://github.com/medusajs/medusa). 4 | 5 | If you're building a custom vue based storefront that consumes a medusa backend and find yourself wishing you had something nice at hands like `medusa-react` to streamline your data management - this might be your library! 6 | 7 | ## Installation 8 | 9 | The library uses [@tanstack/vue-query](https://tanstack.com/query/v4/docs/vue/overview) under the hood. 10 | 11 | For the core composables run: 12 | 13 | ```bash 14 | npm install @medusa-vue/core 15 | # or 16 | yarn add @medusa-vue/core 17 | ``` 18 | 19 | For the components (WIP :construction_worker:): 20 | 21 | ```bash 22 | npm install @medusa-vue/components 23 | # or 24 | yarn add @medusa-vue/components 25 | ``` 26 | 27 | ## Quick Start 28 | 29 | In order to use the composables exposed by this library you need to register it as a plugin in your main file before mounting your app. The plugin takes a config object that needs at least a `baseUrl` where it can reach your server. Optionally, it allows you to pass additional props to configure both the underlying `medusa-js` and the `vue-query` client. Here's the complete interface. Refer to [these](https://docs.medusajs.com/js-client/overview/) and [these](https://tanstack.com/query/v4/docs/vue/overview) docs, respectively to get an idea on how the parts work together. 30 | 31 | ```ts 32 | interface MedusaVueClientProps { 33 | baseUrl: string; 34 | maxRetries?: number; 35 | /** 36 | * Authentication token 37 | */ 38 | apiKey?: string; 39 | /** 40 | * PublishableApiKey identifier that defines the scope of resources 41 | * available within the request 42 | */ 43 | publishableApiKey?: string; 44 | 45 | queryClientProviderProps?: VueQueryPluginOptions; 46 | } 47 | ``` 48 | 49 | Plug it in: 50 | 51 | ```ts 52 | // main.ts 53 | import { createApp } from 'vue'; 54 | import App from './App.vue'; 55 | 56 | import { createMedusaVueClient } from '@medusa-vue/core'; 57 | 58 | const client = createMedusaVueClient({ 59 | baseUrl: '', 60 | }); 61 | 62 | const app = createApp(App); 63 | 64 | app.use(client).mount('#app'); 65 | ``` 66 | 67 | The hooks exposed by `medusa-vue` fall into two main categories: queries and mutations. 68 | 69 | ### Queries 70 | 71 | [Queries](https://tanstack.com/query/v4/docs/vue/guides/queries) simply wrap around vue-query's `useQuery` hook to fetch some data from your medusa server 72 | 73 | ```vue 74 | // ./my-product-list.vue 75 | 82 | 83 | 88 | ``` 89 | 90 | **_Note_**: If you've worked with @medusajs/medusa-react you might be used to being able to destructure the recordset returned by the server, i.e. `const { products } = useProducts()`. This is however not possible with vue due to the way it's reactive system works. 91 | 92 | ### Mutations 93 | 94 | [Mutations](https://react-query.tanstack.com/guides/mutations#_top) wrap around vue-query's `useMutation` to mutate data and perform server-side effects on your medusa server. If you are not entirely familiar with this idea of "mutations", creating a cart would be a mutation because it creates a cart in your server (and database). Mutations also have to be invoked imperatively, meaning that calling for the mutation to take action, you will have to call a `mutate()` function returned from mutation hooks. 95 | 96 | ```vue 97 | 105 | 106 | 111 | ``` 112 | 113 | The mutation hooks will return exactly what vue-query's [`useMutation`](https://tanstack.com/query/v4/docs/vue/guides/mutations) returns. In addition, the options you pass in to the hooks will be passed along to `useMutation`. 114 | 115 | ### Components 116 | 117 | **_NOTE_**: This is still work in progress and new components will gradually be added!:construction_worker: 118 | 119 | If you prefer declarative templates, `@medusa-vue/components` provided (almost) renderless components to use directly in your template and provide data through `slot-props`. This allows for extremely streamlinend and declarative templating: 120 | 121 | ```vue 122 | 125 | 126 | 133 | ``` 134 | 135 | The component also allows to pass down the laoding indicating component via a slot: 136 | 137 | ```vue 138 | 141 | 142 | 153 | ``` 154 | 155 | ### Utilities 156 | 157 | A set of utility functions are also exposed from the library to make your life easier when dealing with displaying money amounts 158 | 159 | #### `formatVariantPrice()` 160 | 161 | - `formatVariantPrice(params: FormatVariantPriceParams): string` 162 | 163 | ```typescript 164 | type FormatVariantPriceParams = { 165 | variant: ProductVariantInfo; 166 | region: RegionInfo; 167 | includeTaxes?: boolean; 168 | minimumFractionDigits?: number; 169 | maximumFractionDigits?: number; 170 | locale?: string; 171 | }; 172 | 173 | type ProductVariantInfo = Pick; 174 | 175 | type RegionInfo = { 176 | currency_code: string; 177 | tax_code: string; 178 | tax_rate: number; 179 | }; 180 | ``` 181 | 182 | Given a variant and region, will return a string representing the localized amount (i.e: `$19.50`) 183 | 184 | The behavior of minimumFractionDigits and maximumFractionDigits is the same as the one explained by MDN [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat). In fact, in order to convert the decimal amount, we use the browser's `Intl.NumberFormat` method. 185 | 186 | #### `computeVariantPrice()` 187 | 188 | - `computeVariantPrice(params: ComputeVariantPriceParams): number` 189 | 190 | ```typescript 191 | type ComputeVariantPriceParams = { 192 | variant: ProductVariantInfo; 193 | region: RegionInfo; 194 | includeTaxes?: boolean; 195 | }; 196 | ``` 197 | 198 | Determines a variant's price based on the region provided. Returns a decimal number representing the amount. 199 | 200 | #### `formatAmount()` 201 | 202 | - `formatAmount(params: FormatAmountParams): string` 203 | 204 | ```typescript 205 | type FormatAmountParams = { 206 | amount: number; 207 | region: RegionInfo; 208 | includeTaxes?: boolean; 209 | minimumFractionDigits?: number; 210 | maximumFractionDigits?: number; 211 | locale?: string; 212 | }; 213 | ``` 214 | 215 | Returns a localized string based on the input params representing the amount (i.e: "$10.99"). 216 | 217 | #### `computeAmount()` 218 | 219 | - `computeAmount(params: ComputeAmountParams): number` 220 | 221 | ```typescript 222 | type ComputeAmountParams = { 223 | amount: number; 224 | region: RegionInfo; 225 | includeTaxes?: boolean; 226 | }; 227 | ``` 228 | 229 | Takes an integer amount, a region, and includeTaxes boolean. Returns a decimal amount including (or excluding) taxes. 230 | 231 | ### Credits 232 | 233 | Based on and inspired by [medusa-react](https://www.npmjs.com/package/medusa-react). 234 | Keep up the good work! :beers: 235 | -------------------------------------------------------------------------------- /packages/core/__tests__/core.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const core = require('..'); 4 | const assert = require('assert').strict; 5 | 6 | assert.strictEqual(core(), 'Hello from core'); 7 | console.info("core tests passed"); 8 | -------------------------------------------------------------------------------- /packages/core/mocks/data/index.ts: -------------------------------------------------------------------------------- 1 | import data from "./fixtures.json" 2 | 3 | const resources = data["resources"] 4 | 5 | type Resources = typeof resources 6 | 7 | type ResourcesWithKey = { 8 | [K in keyof T]: { [_ in Entity]: K } & T[K] 9 | } 10 | 11 | type KeyedResources = ResourcesWithKey<"entity", Resources> 12 | 13 | export const fixtures = { 14 | get( 15 | entity: Entity 16 | ): Omit { 17 | return resources[entity as string] 18 | }, 19 | list( 20 | entity: Entity, 21 | number = 2 22 | ): Omit[] { 23 | return Array(number) 24 | .fill(null) 25 | .map((_) => fixtures.get(entity)) 26 | }, 27 | } as const 28 | -------------------------------------------------------------------------------- /packages/core/mocks/handlers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./store" 2 | export * from "./admin" 3 | -------------------------------------------------------------------------------- /packages/core/mocks/handlers/store.ts: -------------------------------------------------------------------------------- 1 | import { fixtures } from '../data'; 2 | import { rest } from 'msw'; 3 | 4 | export const storeHandlers = [ 5 | rest.get('/store/products', (req, res, ctx) => { 6 | const limit = parseInt(req.url.searchParams.get('limit') || '2'); 7 | const offset = parseInt(req.url.searchParams.get('offset') || '0'); 8 | return res( 9 | ctx.status(200), 10 | ctx.json({ 11 | products: fixtures.list('product', limit), 12 | offset, 13 | limit, 14 | }) 15 | ); 16 | }), 17 | 18 | rest.get('/store/products/:id', (req, res, ctx) => { 19 | return res( 20 | ctx.status(200), 21 | ctx.json({ 22 | product: fixtures.get('product'), 23 | }) 24 | ); 25 | }), 26 | 27 | rest.get('/store/product-types', (req, res, ctx) => { 28 | const limit = parseInt(req.url.searchParams.get('limit') || '2'); 29 | const offset = parseInt(req.url.searchParams.get('offset') || '0'); 30 | return res( 31 | ctx.status(200), 32 | ctx.json({ 33 | product_types: fixtures.list('product_type'), 34 | offset, 35 | limit, 36 | }) 37 | ); 38 | }), 39 | 40 | rest.get('/store/collections/', (req, res, ctx) => { 41 | return res( 42 | ctx.status(200), 43 | ctx.json({ 44 | collections: fixtures.list('product_collection'), 45 | }) 46 | ); 47 | }), 48 | 49 | rest.get('/store/collections/:id', (req, res, ctx) => { 50 | return res( 51 | ctx.status(200), 52 | ctx.json({ 53 | collection: fixtures.get('product_collection'), 54 | }) 55 | ); 56 | }), 57 | 58 | rest.get('/store/regions/', (req, res, ctx) => { 59 | return res( 60 | ctx.status(200), 61 | ctx.json({ 62 | regions: fixtures.list('region'), 63 | }) 64 | ); 65 | }), 66 | 67 | rest.get('/store/regions/:id', (req, res, ctx) => { 68 | return res( 69 | ctx.status(200), 70 | ctx.json({ 71 | region: fixtures.get('region'), 72 | }) 73 | ); 74 | }), 75 | 76 | rest.get('/store/gift-cards/:id', (req, res, ctx) => { 77 | return res( 78 | ctx.status(200), 79 | ctx.json({ 80 | gift_card: fixtures.get('gift_card'), 81 | }) 82 | ); 83 | }), 84 | 85 | rest.post('/store/order-edits/:id/decline', (req, res, ctx) => { 86 | return res( 87 | ctx.status(200), 88 | ctx.json({ 89 | order_edit: { 90 | ...fixtures.get('store_order_edit'), 91 | declined_reason: (req.body as any).declined_reason, 92 | status: 'declined', 93 | }, 94 | }) 95 | ); 96 | }), 97 | 98 | rest.post('/store/order-edits/:id/complete', (req, res, ctx) => { 99 | return res( 100 | ctx.status(200), 101 | ctx.json({ 102 | order_edit: { 103 | ...fixtures.get('store_order_edit'), 104 | status: 'confirmed', 105 | }, 106 | }) 107 | ); 108 | }), 109 | 110 | rest.get('/store/orders/:id', (req, res, ctx) => { 111 | return res( 112 | ctx.status(200), 113 | ctx.json({ 114 | order: fixtures.get('order'), 115 | }) 116 | ); 117 | }), 118 | 119 | rest.get('/store/orders/cart/:id', (req, res, ctx) => { 120 | return res( 121 | ctx.status(200), 122 | ctx.json({ 123 | order: fixtures.get('order'), 124 | }) 125 | ); 126 | }), 127 | 128 | rest.get('/store/orders/', (req, res, ctx) => { 129 | return res( 130 | ctx.status(200), 131 | ctx.json({ 132 | orders: fixtures.get('order'), 133 | }) 134 | ); 135 | }), 136 | 137 | rest.post('/store/orders/customer/confirm', (req, res, ctx) => { 138 | return res(ctx.status(200)); 139 | }), 140 | 141 | rest.post('/store/orders/batch/customer/token', (req, res, ctx) => { 142 | return res(ctx.status(200)); 143 | }), 144 | 145 | rest.get('/store/return-reasons/', (req, res, ctx) => { 146 | return res( 147 | ctx.status(200), 148 | ctx.json({ 149 | return_reasons: fixtures.list('return_reason'), 150 | }) 151 | ); 152 | }), 153 | 154 | rest.get('/store/return-reasons/:id', (req, res, ctx) => { 155 | return res( 156 | ctx.status(200), 157 | ctx.json({ 158 | return_reason: fixtures.get('return_reason'), 159 | }) 160 | ); 161 | }), 162 | 163 | rest.get('/store/shipping-options/:cart_id', (req, res, ctx) => { 164 | return res( 165 | ctx.status(200), 166 | ctx.json({ 167 | shipping_options: fixtures.list('shipping_option'), 168 | }) 169 | ); 170 | }), 171 | 172 | rest.get('/store/shipping-options/', (req, res, ctx) => { 173 | return res( 174 | ctx.status(200), 175 | ctx.json({ 176 | shipping_options: fixtures.list('shipping_option', 5), 177 | }) 178 | ); 179 | }), 180 | 181 | rest.get('/store/swaps/:cart_id', (req, res, ctx) => { 182 | return res( 183 | ctx.status(200), 184 | ctx.json({ 185 | swap: fixtures.get('swap'), 186 | }) 187 | ); 188 | }), 189 | 190 | rest.get('/store/customers/me', (req, res, ctx) => { 191 | return res( 192 | ctx.status(200), 193 | ctx.json({ 194 | customer: fixtures.get('customer'), 195 | }) 196 | ); 197 | }), 198 | 199 | rest.get('/store/customers/me/orders', (req, res, ctx) => { 200 | return res( 201 | ctx.status(200), 202 | ctx.json({ 203 | orders: fixtures.list('order', 5), 204 | limit: 5, 205 | offset: 0, 206 | }) 207 | ); 208 | }), 209 | 210 | rest.post('/store/customers/', (req, res, ctx) => { 211 | const body = req.body as Record; 212 | const dummyCustomer = fixtures.get('customer'); 213 | const customer = { 214 | ...dummyCustomer, 215 | ...body, 216 | }; 217 | 218 | return res( 219 | ctx.status(200), 220 | ctx.json({ 221 | customer, 222 | }) 223 | ); 224 | }), 225 | 226 | rest.post('/store/customers/me', (req, res, ctx) => { 227 | const body = req.body as Record; 228 | const dummyCustomer = fixtures.get('customer'); 229 | const customer = { 230 | ...dummyCustomer, 231 | ...body, 232 | }; 233 | 234 | return res( 235 | ctx.status(200), 236 | ctx.json({ 237 | customer, 238 | }) 239 | ); 240 | }), 241 | 242 | rest.get('/store/carts/:id', (req, res, ctx) => { 243 | return res( 244 | ctx.status(200), 245 | ctx.json({ 246 | cart: fixtures.get('cart'), 247 | }) 248 | ); 249 | }), 250 | 251 | rest.post('/store/returns/', (req, res, ctx) => { 252 | const { items, ...body } = req.body as Record; 253 | const ret = fixtures.get('return'); 254 | const item = ret.items[0]; 255 | ret.items = items.map(i => ({ ...i, ...item })); 256 | 257 | return res( 258 | ctx.status(200), 259 | ctx.json({ 260 | return: { 261 | ...ret, 262 | ...body, 263 | }, 264 | }) 265 | ); 266 | }), 267 | 268 | rest.post('/store/swaps/', (req, res, ctx) => { 269 | const { additional_items, return_items, ...body } = req.body as Record< 270 | string, 271 | any 272 | >; 273 | const swap = fixtures.get('swap'); 274 | const additional_item = swap.additional_items[0]; 275 | swap.additional_items = additional_items.map(i => ({ 276 | ...i, 277 | ...additional_item, 278 | })); 279 | const return_item = swap.return_order.items[0]; 280 | swap.return_order.items = return_items.map(i => ({ 281 | ...i, 282 | ...return_item, 283 | })); 284 | 285 | return res( 286 | ctx.status(200), 287 | ctx.json({ 288 | swap: { 289 | ...swap, 290 | ...body, 291 | }, 292 | }) 293 | ); 294 | }), 295 | 296 | rest.post('/store/carts/:id/line-items', (req, res, ctx) => { 297 | const { id } = req.params; 298 | const { quantity, variant_id } = req.body as Record; 299 | const item = fixtures.get('line_item'); 300 | return res( 301 | ctx.status(200), 302 | ctx.json({ 303 | cart: { 304 | ...fixtures.get('cart'), 305 | id, 306 | items: [ 307 | { 308 | ...item, 309 | quantity, 310 | variant_id, 311 | }, 312 | ], 313 | }, 314 | }) 315 | ); 316 | }), 317 | 318 | rest.post('/store/carts/:id/line-items/:line_id', (req, res, ctx) => { 319 | const { id, line_id } = req.params; 320 | const { quantity } = req.body as Record; 321 | const item = fixtures.get('line_item'); 322 | return res( 323 | ctx.status(200), 324 | ctx.json({ 325 | cart: { 326 | ...fixtures.get('cart'), 327 | id, 328 | items: [ 329 | { 330 | ...item, 331 | id: line_id, 332 | quantity, 333 | }, 334 | ], 335 | }, 336 | }) 337 | ); 338 | }), 339 | 340 | rest.delete('/store/carts/:id/line-items/:line_id', (req, res, ctx) => { 341 | return res( 342 | ctx.status(200), 343 | ctx.json({ 344 | cart: fixtures.get('cart'), 345 | }) 346 | ); 347 | }), 348 | 349 | rest.post('/store/carts/', (req, res, ctx) => { 350 | return res( 351 | ctx.status(200), 352 | ctx.json({ 353 | cart: fixtures.get('cart'), 354 | }) 355 | ); 356 | }), 357 | 358 | rest.post('/store/carts/:id', (req, res, ctx) => { 359 | const { id } = req.params; 360 | const body = req.body as Record; 361 | return res( 362 | ctx.status(200), 363 | ctx.json({ 364 | cart: { 365 | ...fixtures.get('cart'), 366 | id, 367 | ...body, 368 | }, 369 | }) 370 | ); 371 | }), 372 | 373 | rest.post('/store/carts/:id/complete', (req, res, ctx) => { 374 | const { id } = req.params; 375 | return res( 376 | ctx.status(200), 377 | ctx.json({ 378 | type: 'order', 379 | data: fixtures.get('order'), 380 | }) 381 | ); 382 | }), 383 | 384 | rest.post('/store/carts/:id/payment-sessions', (req, res, ctx) => { 385 | const { id } = req.params; 386 | return res( 387 | ctx.status(200), 388 | ctx.json({ 389 | cart: { 390 | ...fixtures.get('cart'), 391 | id, 392 | }, 393 | }) 394 | ); 395 | }), 396 | 397 | rest.post( 398 | '/store/carts/:id/payment-sessions/:provider_id', 399 | (req, res, ctx) => { 400 | const { id } = req.params; 401 | return res( 402 | ctx.status(200), 403 | ctx.json({ 404 | cart: { 405 | ...fixtures.get('cart'), 406 | id, 407 | }, 408 | }) 409 | ); 410 | } 411 | ), 412 | 413 | rest.post( 414 | '/store/carts/:id/payment-sessions/:provider_id/refresh', 415 | (req, res, ctx) => { 416 | const { id } = req.params; 417 | return res( 418 | ctx.status(200), 419 | ctx.json({ 420 | cart: { 421 | ...fixtures.get('cart'), 422 | id, 423 | }, 424 | }) 425 | ); 426 | } 427 | ), 428 | 429 | rest.post('/store/carts/:id/payment-session', (req, res, ctx) => { 430 | const { id } = req.params; 431 | return res( 432 | ctx.status(200), 433 | ctx.json({ 434 | cart: { 435 | ...fixtures.get('cart'), 436 | id, 437 | }, 438 | }) 439 | ); 440 | }), 441 | 442 | rest.delete( 443 | '/store/carts/:id/payment-sessions/:provider_id', 444 | (req, res, ctx) => { 445 | const { id } = req.params; 446 | return res( 447 | ctx.status(200), 448 | ctx.json({ 449 | cart: { 450 | ...fixtures.get('cart'), 451 | id, 452 | }, 453 | }) 454 | ); 455 | } 456 | ), 457 | 458 | rest.post('/store/carts/:id/shipping-methods', (req, res, ctx) => { 459 | const { id } = req.params; 460 | return res( 461 | ctx.status(200), 462 | ctx.json({ 463 | cart: { 464 | ...fixtures.get('cart'), 465 | id, 466 | }, 467 | }) 468 | ); 469 | }), 470 | 471 | rest.get('/store/payment-collections/:id', (req, res, ctx) => { 472 | const { id } = req.params; 473 | return res( 474 | ctx.status(200), 475 | ctx.json({ 476 | payment_collection: { 477 | ...fixtures.get('payment_collection'), 478 | id, 479 | }, 480 | }) 481 | ); 482 | }), 483 | 484 | rest.post( 485 | '/store/payment-collections/:id/sessions/batch', 486 | (req, res, ctx) => { 487 | const { id } = req.params; 488 | return res( 489 | ctx.status(200), 490 | ctx.json({ 491 | payment_collection: { 492 | ...fixtures.get('payment_collection'), 493 | id, 494 | }, 495 | }) 496 | ); 497 | } 498 | ), 499 | 500 | rest.post( 501 | '/store/payment-collections/:id/sessions/batch/authorize', 502 | (req, res, ctx) => { 503 | const { id } = req.params; 504 | return res( 505 | ctx.status(207), 506 | ctx.json({ 507 | payment_collection: { 508 | ...fixtures.get('payment_collection'), 509 | id, 510 | }, 511 | }) 512 | ); 513 | } 514 | ), 515 | 516 | rest.post('/store/payment-collections/:id/sessions', (req, res, ctx) => { 517 | const { id } = req.params; 518 | return res( 519 | ctx.status(200), 520 | ctx.json({ 521 | payment_collection: { 522 | ...fixtures.get('payment_collection'), 523 | id, 524 | }, 525 | }) 526 | ); 527 | }), 528 | 529 | rest.post( 530 | '/store/payment-collections/:id/sessions/:session_id', 531 | (req, res, ctx) => { 532 | const { id, session_id } = req.params; 533 | const payCol: any = { ...fixtures.get('payment_collection') }; 534 | 535 | payCol.payment_sessions[0].id = `new_${session_id}`; 536 | const session = { 537 | payment_session: payCol.payment_sessions[0], 538 | }; 539 | 540 | return res( 541 | ctx.status(200), 542 | ctx.json({ 543 | ...session, 544 | }) 545 | ); 546 | } 547 | ), 548 | 549 | rest.post( 550 | '/store/payment-collections/:id/sessions/:session_id/authorize', 551 | (req, res, ctx) => { 552 | const { session_id } = req.params; 553 | 554 | const session = fixtures.get('payment_collection').payment_sessions[0]; 555 | return res( 556 | ctx.status(200), 557 | ctx.json({ 558 | payment_collection: { 559 | ...session, 560 | id: session_id, 561 | }, 562 | }) 563 | ); 564 | } 565 | ), 566 | ]; 567 | -------------------------------------------------------------------------------- /packages/core/mocks/server.ts: -------------------------------------------------------------------------------- 1 | import { setupServer } from "msw/node" 2 | import { storeHandlers, adminHandlers } from "./handlers" 3 | 4 | export const server = setupServer(...storeHandlers, ...adminHandlers) 5 | -------------------------------------------------------------------------------- /packages/core/package copy.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "license": "MIT", 4 | "main": "dist/index.js", 5 | "typings": "dist/index.d.ts", 6 | "engines": { 7 | "node": ">=10" 8 | }, 9 | "scripts": { 10 | "start": "dts watch", 11 | "build": "dts build", 12 | "prepare": "dts build", 13 | "test": "vitest", 14 | "coverage": "vitest run --coverage", 15 | "lint": "dts lint", 16 | "size": "size-limit", 17 | "analyze": "size-limit --why" 18 | }, 19 | "peerDependencies": { 20 | "@medusajs/medusa": "^1.7.3", 21 | "vue": "^3.2.38" 22 | }, 23 | "husky": { 24 | "hooks": { 25 | "pre-commit": "tsdx lint" 26 | } 27 | }, 28 | "name": "medusa-vue", 29 | "author": "Ulrich Kraue (@raukaute)", 30 | "module": "dist/medusa-vue.esm.js", 31 | "size-limit": [ 32 | { 33 | "path": "dist/medusa-vue.cjs.production.min.js", 34 | "limit": "10 KB" 35 | }, 36 | { 37 | "path": "dist/medusa-vue.esm.js", 38 | "limit": "10 KB" 39 | } 40 | ], 41 | "devDependencies": { 42 | "@babel/core": "^7.16.0", 43 | "@size-limit/preset-small-lib": "^6.0.4", 44 | "@types/lodash": "^4.14.177", 45 | "@vitejs/plugin-vue": "^4.0.0", 46 | "@vue/test-utils": "^2.2.7", 47 | "axios": "^0.24.0", 48 | "babel-loader": "^8.2.3", 49 | "dts-cli": "^1.5.1", 50 | "husky": "^7.0.4", 51 | "jsdom": "^21.0.0", 52 | "msw": "^0.35.0", 53 | "size-limit": "^6.0.4", 54 | "tslib": "^2.3.1", 55 | "vitest": "^0.27.1", 56 | "vue": "^3.2.38" 57 | }, 58 | "dependencies": { 59 | "@medusajs/medusa-js": "^1.3.3", 60 | "@tanstack/vue-query": "^4.14.5", 61 | "lodash": "^4.17.21", 62 | "lodash-es": "^4.17.21" 63 | }, 64 | "msw": { 65 | "workerDirectory": "public" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@medusa-vue/core", 3 | "version": "0.4.2", 4 | "license": "MIT", 5 | "main": "dist/index.js", 6 | "module": "dist/core.esm.js", 7 | "typings": "dist/index.d.ts", 8 | "description": "Vue composables to interact with @medusa api", 9 | "keywords": [ 10 | "vue", 11 | "composables", 12 | "query", 13 | "@medusa" 14 | ], 15 | "author": "Ulrich Krause 20 | ) => { 21 | const { client } = useMedusa(); 22 | return useMutation( 23 | (data?: StorePostCartReq | undefined) => client.carts.create(data), 24 | options 25 | ); 26 | }; 27 | 28 | export const useUpdateCart = ( 29 | cartId: string, 30 | options?: UseMutationOptions< 31 | StoreCartsRes, 32 | Error, 33 | StorePostCartsCartReq, 34 | unknown 35 | > 36 | ) => { 37 | const { client } = useMedusa(); 38 | return useMutation( 39 | (data: StorePostCartsCartReq) => client.carts.update(cartId, data), 40 | options 41 | ); 42 | }; 43 | 44 | export const useCompleteCart = ( 45 | cartId: string, 46 | options?: UseMutationOptions 47 | ) => { 48 | const { client } = useMedusa(); 49 | 50 | return useMutation(() => client.carts.complete(cartId), options); 51 | }; 52 | 53 | export const useCreatePaymentSession = ( 54 | cartId: string, 55 | options?: UseMutationOptions 56 | ) => { 57 | const { client } = useMedusa(); 58 | return useMutation(() => client.carts.createPaymentSessions(cartId), options); 59 | }; 60 | 61 | export const useUpdatePaymentSession = ( 62 | cartId: string, 63 | options?: UseMutationOptions< 64 | StoreCartsRes, 65 | Error, 66 | { provider_id: string } & StorePostCartsCartPaymentSessionUpdateReq, 67 | unknown 68 | > 69 | ) => { 70 | const { client } = useMedusa(); 71 | return useMutation( 72 | ({ data, provider_id }) => 73 | client.carts.updatePaymentSession(cartId, provider_id, { data }), 74 | options 75 | ); 76 | }; 77 | 78 | type RefreshPaymentSessionMutationData = { 79 | provider_id: string; 80 | }; 81 | 82 | export const useRefreshPaymentSession = ( 83 | cartId: string, 84 | options?: UseMutationOptions< 85 | StoreCartsRes, 86 | Error, 87 | RefreshPaymentSessionMutationData, 88 | unknown 89 | > 90 | ) => { 91 | const { client } = useMedusa(); 92 | return useMutation( 93 | ({ provider_id }: RefreshPaymentSessionMutationData) => 94 | client.carts.refreshPaymentSession(cartId, provider_id), 95 | options 96 | ); 97 | }; 98 | 99 | type SetPaymentSessionMutationData = { provider_id: string }; 100 | 101 | export const useSetPaymentSession = ( 102 | cartId: string, 103 | options?: UseMutationOptions< 104 | StoreCartsRes, 105 | Error, 106 | SetPaymentSessionMutationData, 107 | unknown 108 | > 109 | ) => { 110 | const { client } = useMedusa(); 111 | return useMutation( 112 | (data: StorePostCartsCartPaymentSessionReq) => 113 | client.carts.setPaymentSession(cartId, data), 114 | options 115 | ); 116 | }; 117 | 118 | export const useAddShippingMethodToCart = ( 119 | cartId: string, 120 | options?: UseMutationOptions< 121 | StoreCartsRes, 122 | Error, 123 | StorePostCartsCartShippingMethodReq, 124 | unknown 125 | > 126 | ) => { 127 | const { client } = useMedusa(); 128 | return useMutation( 129 | (data: StorePostCartsCartShippingMethodReq) => 130 | client.carts.addShippingMethod(cartId, data), 131 | options 132 | ); 133 | }; 134 | 135 | type DeletePaymentSessionMutationData = { 136 | provider_id: string; 137 | }; 138 | 139 | export const useDeletePaymentSession = ( 140 | cartId: string, 141 | options?: UseMutationOptions< 142 | StoreCartsRes, 143 | Error, 144 | DeletePaymentSessionMutationData, 145 | unknown 146 | > 147 | ) => { 148 | const { client } = useMedusa(); 149 | return useMutation( 150 | ({ provider_id }: DeletePaymentSessionMutationData) => 151 | client.carts.deletePaymentSession(cartId, provider_id), 152 | options 153 | ); 154 | }; 155 | 156 | export const useStartCheckout = ( 157 | options?: UseMutationOptions< 158 | StoreCartsRes['cart'], 159 | Error, 160 | StorePostCartReq, 161 | unknown 162 | > 163 | ) => { 164 | const { client } = useMedusa(); 165 | const mutation = useMutation(async (data?: StorePostCartReq) => { 166 | const { cart } = await client.carts.create(data); 167 | const res = await client.carts.createPaymentSessions(cart.id); 168 | return res.cart; 169 | }, options); 170 | 171 | return mutation; 172 | }; 173 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/carts/queries.ts: -------------------------------------------------------------------------------- 1 | import { queryKeysFactory } from '../../utils/index'; 2 | import { StoreCartsRes } from '@medusajs/medusa'; 3 | import { Response } from '@medusajs/medusa-js'; 4 | import { useQuery } from '@tanstack/vue-query'; 5 | import { useMedusa } from '../../../useApi'; 6 | import { UseQueryOptionsWrapper } from '../../../types'; 7 | 8 | const CARTS_QUERY_KEY = `carts` as const; 9 | 10 | export const cartKeys = queryKeysFactory(CARTS_QUERY_KEY); 11 | type CartQueryKey = typeof cartKeys; 12 | 13 | export const useGetCart = ( 14 | id: string, 15 | options?: UseQueryOptionsWrapper< 16 | Response, 17 | Error, 18 | ReturnType 19 | > 20 | ) => { 21 | const { client } = useMedusa(); 22 | const { data, ...rest } = useQuery( 23 | cartKeys.detail(id), 24 | () => client.carts.retrieve(id), 25 | options 26 | ); 27 | return { data, ...rest } as const; 28 | }; 29 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/collections/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./queries" 2 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/collections/queries.ts: -------------------------------------------------------------------------------- 1 | import { 2 | StoreCollectionsListRes, 3 | StoreCollectionsRes, 4 | StoreGetCollectionsParams, 5 | } from '@medusajs/medusa'; 6 | import { Response } from '@medusajs/medusa-js'; 7 | import { useQuery } from '@tanstack/vue-query'; 8 | import { useMedusa } from '../../../useApi'; 9 | import { UseQueryOptionsWrapper } from '../../../types'; 10 | import { queryKeysFactory } from '../../utils/index'; 11 | 12 | const COLLECTIONS_QUERY_KEY = `collections` as const; 13 | 14 | export const collectionKeys = queryKeysFactory(COLLECTIONS_QUERY_KEY); 15 | 16 | type CollectionQueryKey = typeof collectionKeys; 17 | 18 | export const useCollection = ( 19 | id: string, 20 | options?: UseQueryOptionsWrapper< 21 | Response, 22 | Error, 23 | ReturnType 24 | > 25 | ) => { 26 | const { client } = useMedusa(); 27 | const { data, ...rest } = useQuery( 28 | collectionKeys.detail(id), 29 | () => client.collections.retrieve(id), 30 | options 31 | ); 32 | return { data, ...rest } as const; 33 | }; 34 | 35 | export const useCollections = ( 36 | query?: StoreGetCollectionsParams, 37 | options?: UseQueryOptionsWrapper< 38 | Response, 39 | Error, 40 | ReturnType 41 | > 42 | ) => { 43 | const { client } = useMedusa(); 44 | const { data, ...rest } = useQuery( 45 | collectionKeys.list(query), 46 | () => client.collections.list(query), 47 | options 48 | ); 49 | return { data, ...rest } as const; 50 | }; 51 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/customers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./queries" 2 | export * from "./mutations" 3 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/customers/mutations.ts: -------------------------------------------------------------------------------- 1 | import { 2 | StoreCustomersRes, 3 | StorePostCustomersCustomerReq, 4 | StorePostCustomersReq, 5 | } from '@medusajs/medusa'; 6 | import { useMutation, UseMutationOptions } from '@tanstack/vue-query'; 7 | import { useMedusa } from '../../../useApi'; 8 | 9 | export const useCreateCustomer = ( 10 | options?: UseMutationOptions< 11 | StoreCustomersRes, 12 | Error, 13 | StorePostCustomersReq, 14 | unknown 15 | > 16 | ) => { 17 | const { client } = useMedusa(); 18 | return useMutation( 19 | (data: StorePostCustomersReq) => client.customers.create(data), 20 | options 21 | ); 22 | }; 23 | 24 | export const useUpdateMe = ( 25 | options?: UseMutationOptions< 26 | StoreCustomersRes, 27 | Error, 28 | { id: string } & StorePostCustomersCustomerReq, 29 | unknown 30 | > 31 | ) => { 32 | const { client } = useMedusa(); 33 | return useMutation( 34 | ({ id, ...data }: { id: string } & StorePostCustomersCustomerReq) => 35 | client.customers.update(data), 36 | options 37 | ); 38 | }; 39 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/customers/queries.ts: -------------------------------------------------------------------------------- 1 | import { 2 | StoreCustomersListOrdersRes, 3 | StoreCustomersRes, 4 | StoreGetCustomersCustomerOrdersParams, 5 | } from '@medusajs/medusa'; 6 | import { useQuery } from '@tanstack/vue-query'; 7 | import { Response } from '@medusajs/medusa-js'; 8 | import { useMedusa } from '../../../useApi'; 9 | import { UseQueryOptionsWrapper } from '../../../types'; 10 | import { queryKeysFactory } from '../../utils/index'; 11 | 12 | const CUSTOMERS_QUERY_KEY = `customers` as const; 13 | 14 | export const customerKeys = { 15 | ...queryKeysFactory(CUSTOMERS_QUERY_KEY), 16 | orders: (id: string) => [...customerKeys.detail(id), 'orders'] as const, 17 | }; 18 | 19 | type CustomerQueryKey = typeof customerKeys; 20 | 21 | export const useMeCustomer = ( 22 | options?: UseQueryOptionsWrapper< 23 | Response, 24 | Error, 25 | ReturnType 26 | > 27 | ) => { 28 | const { client } = useMedusa(); 29 | const { data, ...rest } = useQuery( 30 | customerKeys.detail('me'), 31 | () => client.customers.retrieve(), 32 | options 33 | ); 34 | return { data, ...rest } as const; 35 | }; 36 | 37 | export const useCustomerOrders = ( 38 | query: StoreGetCustomersCustomerOrdersParams = { limit: 10, offset: 0 }, 39 | options?: UseQueryOptionsWrapper< 40 | Response, 41 | Error, 42 | ReturnType 43 | > 44 | ) => { 45 | const { client } = useMedusa(); 46 | const { data, ...rest } = useQuery( 47 | customerKeys.orders('me'), 48 | () => client.customers.listOrders(query), 49 | options 50 | ); 51 | 52 | return { data, ...rest } as const; 53 | }; 54 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/gift-cards/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./queries" 2 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/gift-cards/queries.ts: -------------------------------------------------------------------------------- 1 | import { StoreGiftCardsRes } from '@medusajs/medusa'; 2 | import { Response } from '@medusajs/medusa-js'; 3 | import { useQuery } from '@tanstack/vue-query'; 4 | import { UseQueryOptionsWrapper } from '../../../types'; 5 | import { useMedusa } from '../../../useApi'; 6 | import { queryKeysFactory } from '../../utils/index'; 7 | 8 | const GIFT_CARDS_QUERY_KEY = `gift_cards` as const; 9 | 10 | export const giftCardKeys = queryKeysFactory(GIFT_CARDS_QUERY_KEY); 11 | 12 | type GiftCardQueryKey = typeof giftCardKeys; 13 | 14 | export const useGiftCard = ( 15 | id: string, 16 | options?: UseQueryOptionsWrapper< 17 | Response, 18 | Error, 19 | ReturnType 20 | > 21 | ) => { 22 | const { client } = useMedusa(); 23 | const { data, ...rest } = useQuery( 24 | giftCardKeys.detail(id), 25 | () => client.giftCards.retrieve(id), 26 | options 27 | ); 28 | return { data, ...rest } as const; 29 | }; 30 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/index.ts: -------------------------------------------------------------------------------- 1 | export * from './carts/'; 2 | export * from './collections/'; 3 | export * from './customers/'; 4 | export * from './gift-cards/'; 5 | export * from './line-items/'; 6 | export * from './orders/'; 7 | export * from './order-edits/'; 8 | export * from './payment-collections/'; 9 | export * from './products/'; 10 | export * from './product-types/'; 11 | export * from './regions/'; 12 | export * from './return-reasons/'; 13 | export * from './returns/'; 14 | export * from './shipping-options/'; 15 | export * from './swaps/'; 16 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/line-items/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./mutations" 2 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/line-items/mutations.ts: -------------------------------------------------------------------------------- 1 | import { 2 | StoreCartsRes, 3 | StorePostCartsCartLineItemsReq, 4 | StorePostCartsCartLineItemsItemReq, 5 | } from '@medusajs/medusa'; 6 | import { useMutation, UseMutationOptions } from '@tanstack/vue-query'; 7 | import { useMedusa } from '../../../useApi'; 8 | 9 | export const useCreateLineItem = ( 10 | cartId: string, 11 | options?: UseMutationOptions< 12 | StoreCartsRes, 13 | Error, 14 | StorePostCartsCartLineItemsReq, 15 | unknown 16 | > 17 | ) => { 18 | const { client } = useMedusa(); 19 | return useMutation( 20 | (data: StorePostCartsCartLineItemsReq) => 21 | client.carts.lineItems.create(cartId, data), 22 | options 23 | ); 24 | }; 25 | 26 | export const useUpdateLineItem = ( 27 | cartId: string, 28 | options?: UseMutationOptions< 29 | StoreCartsRes, 30 | Error, 31 | StorePostCartsCartLineItemsItemReq & { lineId: string }, 32 | unknown 33 | > 34 | ) => { 35 | const { client } = useMedusa(); 36 | return useMutation( 37 | ({ 38 | lineId, 39 | ...data 40 | }: StorePostCartsCartLineItemsItemReq & { lineId: string }) => 41 | client.carts.lineItems.update(cartId, lineId, data), 42 | options 43 | ); 44 | }; 45 | 46 | export const useDeleteLineItem = ( 47 | cartId: string, 48 | options?: UseMutationOptions< 49 | StoreCartsRes, 50 | Error, 51 | { lineId: string }, 52 | unknown 53 | > 54 | ) => { 55 | const { client } = useMedusa(); 56 | return useMutation( 57 | ({ lineId }: { lineId: string }) => 58 | client.carts.lineItems.delete(cartId, lineId), 59 | options 60 | ); 61 | }; 62 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/order-edits/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./queries" 2 | export * from './mutations' -------------------------------------------------------------------------------- /packages/core/src/composables/store/order-edits/mutations.ts: -------------------------------------------------------------------------------- 1 | import { 2 | useMutation, 3 | UseMutationOptions, 4 | useQueryClient, 5 | } from '@tanstack/vue-query'; 6 | import { Response } from '@medusajs/medusa-js'; 7 | 8 | import { 9 | StoreOrderEditsRes, 10 | StorePostOrderEditsOrderEditDecline, 11 | } from '@medusajs/medusa'; 12 | 13 | import { buildOptions } from '../../utils/buildOptions'; 14 | import { useMedusa } from '../../../useApi'; 15 | import { orderEditQueryKeys } from './queries'; 16 | 17 | export const useDeclineOrderEdit = ( 18 | id: string, 19 | options?: UseMutationOptions< 20 | Response, 21 | Error, 22 | StorePostOrderEditsOrderEditDecline, 23 | unknown 24 | > 25 | ) => { 26 | const { client } = useMedusa(); 27 | const queryClient = useQueryClient(); 28 | 29 | return useMutation( 30 | (payload: StorePostOrderEditsOrderEditDecline) => 31 | client.orderEdits.decline(id, payload), 32 | buildOptions( 33 | queryClient, 34 | [orderEditQueryKeys.lists(), orderEditQueryKeys.detail(id)], 35 | options 36 | ) 37 | ); 38 | }; 39 | 40 | export const useCompleteOrderEdit = ( 41 | id: string, 42 | options?: UseMutationOptions< 43 | Response, 44 | Error, 45 | void, 46 | unknown 47 | > 48 | ) => { 49 | const { client } = useMedusa(); 50 | const queryClient = useQueryClient(); 51 | 52 | return useMutation( 53 | () => client.orderEdits.complete(id), 54 | buildOptions( 55 | queryClient, 56 | [orderEditQueryKeys.lists(), orderEditQueryKeys.detail(id)], 57 | options 58 | ) 59 | ); 60 | }; 61 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/order-edits/queries.ts: -------------------------------------------------------------------------------- 1 | import { queryKeysFactory } from '../../utils'; 2 | import { StoreOrderEditsRes } from '@medusajs/medusa'; 3 | import { useQuery } from '@tanstack/vue-query'; 4 | import { useMedusa } from '../../../useApi'; 5 | import { UseQueryOptionsWrapper } from '../../../types'; 6 | import { Response } from '@medusajs/medusa-js'; 7 | 8 | const ORDER_EDITS_QUERY_KEY = `orderEdit` as const; 9 | 10 | export const orderEditQueryKeys = queryKeysFactory< 11 | typeof ORDER_EDITS_QUERY_KEY 12 | >(ORDER_EDITS_QUERY_KEY); 13 | 14 | type OrderQueryKey = typeof orderEditQueryKeys; 15 | 16 | export const useOrderEdit = ( 17 | id: string, 18 | options?: UseQueryOptionsWrapper< 19 | Response, 20 | Error, 21 | ReturnType 22 | > 23 | ) => { 24 | const { client } = useMedusa(); 25 | const { data, ...rest } = useQuery( 26 | orderEditQueryKeys.detail(id), 27 | () => client.orderEdits.retrieve(id), 28 | options 29 | ); 30 | 31 | return { data, ...rest } as const; 32 | }; 33 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/orders/index.ts: -------------------------------------------------------------------------------- 1 | export * from './queries'; 2 | export * from './mutations'; 3 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/orders/mutations.ts: -------------------------------------------------------------------------------- 1 | import { 2 | StorePostCustomersCustomerAcceptClaimReq, 3 | StorePostCustomersCustomerOrderClaimReq, 4 | } from '@medusajs/medusa'; 5 | import { Response } from '@medusajs/medusa-js'; 6 | import { 7 | UseMutationOptions, 8 | useMutation, 9 | useQueryClient, 10 | } from '@tanstack/vue-query'; 11 | import { orderKeys } from './queries'; 12 | import { useMedusa } from '../../../useApi'; 13 | import { buildOptions } from '../../utils/buildOptions'; 14 | 15 | export const useRequestOrderAccess = ( 16 | options?: UseMutationOptions< 17 | Response<{}>, 18 | Error, 19 | StorePostCustomersCustomerOrderClaimReq, 20 | unknown 21 | > 22 | ) => { 23 | const { client } = useMedusa(); 24 | const queryClient = useQueryClient(); 25 | 26 | return useMutation( 27 | (payload: StorePostCustomersCustomerOrderClaimReq) => 28 | client.orders.requestCustomerOrders(payload), 29 | buildOptions(queryClient, [orderKeys.all], options) 30 | ); 31 | }; 32 | export const useGrantOrderAccess = ( 33 | options?: UseMutationOptions< 34 | Response<{}>, 35 | Error, 36 | StorePostCustomersCustomerAcceptClaimReq, 37 | unknown 38 | > 39 | ) => { 40 | const { client } = useMedusa(); 41 | const queryClient = useQueryClient(); 42 | 43 | return useMutation( 44 | (payload: StorePostCustomersCustomerAcceptClaimReq) => 45 | client.orders.confirmRequest(payload), 46 | buildOptions(queryClient, [orderKeys.all], options) 47 | ); 48 | }; 49 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/orders/queries.ts: -------------------------------------------------------------------------------- 1 | import { queryKeysFactory } from '../../utils/index'; 2 | import { StoreOrdersRes, StoreGetOrdersParams } from '@medusajs/medusa'; 3 | import { useQuery } from '@tanstack/vue-query'; 4 | import { useMedusa } from '../../../useApi'; 5 | import { UseQueryOptionsWrapper } from '../../../types'; 6 | import { Response } from '@medusajs/medusa-js'; 7 | 8 | const ORDERS_QUERY_KEY = `orders` as const; 9 | 10 | export const orderKeys = { 11 | ...queryKeysFactory( 12 | ORDERS_QUERY_KEY 13 | ), 14 | cart: (cartId: string) => [...orderKeys.details(), 'cart', cartId] as const, 15 | }; 16 | 17 | type OrderQueryKey = typeof orderKeys; 18 | 19 | export const useOrder = ( 20 | id: string, 21 | options?: UseQueryOptionsWrapper< 22 | Response, 23 | Error, 24 | ReturnType 25 | > 26 | ) => { 27 | const { client } = useMedusa(); 28 | const { data, ...rest } = useQuery( 29 | orderKeys.detail(id), 30 | () => client.orders.retrieve(id), 31 | options 32 | ); 33 | 34 | return { data, ...rest } as const; 35 | }; 36 | 37 | export const useCartOrder = ( 38 | cartId: string, 39 | options?: UseQueryOptionsWrapper< 40 | Response, 41 | Error, 42 | ReturnType 43 | > 44 | ) => { 45 | const { client } = useMedusa(); 46 | const { data, ...rest } = useQuery( 47 | orderKeys.cart(cartId), 48 | () => client.orders.retrieveByCartId(cartId), 49 | options 50 | ); 51 | 52 | return { data, ...rest } as const; 53 | }; 54 | 55 | export const useOrders = ( 56 | query: StoreGetOrdersParams, 57 | options?: UseQueryOptionsWrapper< 58 | Response, 59 | Error, 60 | ReturnType 61 | > 62 | ) => { 63 | const { client } = useMedusa(); 64 | const { data, ...rest } = useQuery( 65 | orderKeys.list(query), 66 | () => client.orders.lookupOrder(query), 67 | options 68 | ); 69 | 70 | return { data, ...rest } as const; 71 | }; 72 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/payment-collections/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./queries" 2 | export * from "./mutations" 3 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/payment-collections/mutations.ts: -------------------------------------------------------------------------------- 1 | import { 2 | useMutation, 3 | UseMutationOptions, 4 | useQueryClient, 5 | } from '@tanstack/vue-query'; 6 | import { Response } from '@medusajs/medusa-js'; 7 | 8 | import { 9 | StorePaymentCollectionsRes, 10 | StorePostPaymentCollectionsBatchSessionsReq, 11 | StorePostPaymentCollectionsBatchSessionsAuthorizeReq, 12 | StorePaymentCollectionSessionsReq, 13 | StorePaymentCollectionsSessionRes, 14 | } from '@medusajs/medusa'; 15 | 16 | import { buildOptions } from '../../utils/buildOptions'; 17 | import { useMedusa } from '../../../useApi'; 18 | import { paymentCollectionQueryKeys } from './queries'; 19 | 20 | export const useManageMultiplePaymentSessions = ( 21 | id: string, 22 | options?: UseMutationOptions< 23 | Response, 24 | Error, 25 | StorePostPaymentCollectionsBatchSessionsReq, 26 | unknown 27 | > 28 | ) => { 29 | const { client } = useMedusa(); 30 | const queryClient = useQueryClient(); 31 | 32 | return useMutation( 33 | (payload: StorePostPaymentCollectionsBatchSessionsReq) => 34 | client.paymentCollections.managePaymentSessionsBatch(id, payload), 35 | buildOptions( 36 | queryClient, 37 | [ 38 | paymentCollectionQueryKeys.lists(), 39 | paymentCollectionQueryKeys.detail(id), 40 | ], 41 | options 42 | ) 43 | ); 44 | }; 45 | 46 | export const useManagePaymentSession = ( 47 | id: string, 48 | options?: UseMutationOptions< 49 | Response, 50 | Error, 51 | StorePaymentCollectionSessionsReq, 52 | unknown 53 | > 54 | ) => { 55 | const { client } = useMedusa(); 56 | const queryClient = useQueryClient(); 57 | 58 | return useMutation( 59 | (payload: StorePaymentCollectionSessionsReq) => 60 | client.paymentCollections.managePaymentSession(id, payload), 61 | buildOptions( 62 | queryClient, 63 | [ 64 | paymentCollectionQueryKeys.lists(), 65 | paymentCollectionQueryKeys.detail(id), 66 | ], 67 | options 68 | ) 69 | ); 70 | }; 71 | 72 | export const useAuthorizePaymentSession = ( 73 | id: string, 74 | options?: UseMutationOptions< 75 | Response, 76 | Error, 77 | string, 78 | unknown 79 | > 80 | ) => { 81 | const { client } = useMedusa(); 82 | const queryClient = useQueryClient(); 83 | 84 | return useMutation( 85 | (session_id: string) => 86 | client.paymentCollections.authorizePaymentSession(id, session_id), 87 | buildOptions( 88 | queryClient, 89 | [ 90 | paymentCollectionQueryKeys.lists(), 91 | paymentCollectionQueryKeys.detail(id), 92 | ], 93 | options 94 | ) 95 | ); 96 | }; 97 | 98 | export const useAuthorizePaymentSessionsBatch = ( 99 | id: string, 100 | options?: UseMutationOptions< 101 | Response, 102 | Error, 103 | StorePostPaymentCollectionsBatchSessionsAuthorizeReq, 104 | unknown 105 | > 106 | ) => { 107 | const { client } = useMedusa(); 108 | const queryClient = useQueryClient(); 109 | 110 | return useMutation( 111 | payload => 112 | client.paymentCollections.authorizePaymentSessionsBatch(id, payload), 113 | buildOptions( 114 | queryClient, 115 | [ 116 | paymentCollectionQueryKeys.lists(), 117 | paymentCollectionQueryKeys.detail(id), 118 | ], 119 | options 120 | ) 121 | ); 122 | }; 123 | 124 | export const usePaymentCollectionRefreshPaymentSession = ( 125 | id: string, 126 | options?: UseMutationOptions< 127 | Response, 128 | Error, 129 | string, 130 | unknown 131 | > 132 | ) => { 133 | const { client } = useMedusa(); 134 | const queryClient = useQueryClient(); 135 | 136 | return useMutation( 137 | (session_id: string) => 138 | client.paymentCollections.refreshPaymentSession(id, session_id), 139 | buildOptions( 140 | queryClient, 141 | [ 142 | paymentCollectionQueryKeys.lists(), 143 | paymentCollectionQueryKeys.detail(id), 144 | ], 145 | options 146 | ) 147 | ); 148 | }; 149 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/payment-collections/queries.ts: -------------------------------------------------------------------------------- 1 | import { queryKeysFactory } from '../../utils'; 2 | import { StorePaymentCollectionsRes } from '@medusajs/medusa'; 3 | import { useQuery } from '@tanstack/vue-query'; 4 | import { useMedusa } from '../../../useApi'; 5 | import { UseQueryOptionsWrapper } from '../../../types'; 6 | import { Response } from '@medusajs/medusa-js'; 7 | 8 | const PAYMENT_COLLECTION_QUERY_KEY = `paymentCollection` as const; 9 | 10 | export const paymentCollectionQueryKeys = queryKeysFactory< 11 | typeof PAYMENT_COLLECTION_QUERY_KEY 12 | >(PAYMENT_COLLECTION_QUERY_KEY); 13 | 14 | type PaymentCollectionKey = typeof paymentCollectionQueryKeys; 15 | 16 | export const usePaymentCollection = ( 17 | id: string, 18 | options?: UseQueryOptionsWrapper< 19 | Response, 20 | Error, 21 | ReturnType 22 | > 23 | ) => { 24 | const { client } = useMedusa(); 25 | const { data, ...rest } = useQuery( 26 | paymentCollectionQueryKeys.detail(id), 27 | () => client.paymentCollections.retrieve(id), 28 | options 29 | ); 30 | 31 | return { data, ...rest } as const; 32 | }; 33 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/product-types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./queries" 2 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/product-types/queries.ts: -------------------------------------------------------------------------------- 1 | import { 2 | StoreGetProductTypesParams, 3 | StoreProductTypesListRes, 4 | } from '@medusajs/medusa'; 5 | import { Response } from '@medusajs/medusa-js'; 6 | import { useQuery } from '@tanstack/vue-query'; 7 | import { useMedusa } from '../../../useApi'; 8 | import { UseQueryOptionsWrapper } from '../../../types'; 9 | import { queryKeysFactory } from '../../utils'; 10 | 11 | const PRODUCT_TYPES_QUERY_KEY = `product_types` as const; 12 | 13 | export const productTypeKeys = queryKeysFactory(PRODUCT_TYPES_QUERY_KEY); 14 | 15 | type ProductTypesQueryKeys = typeof productTypeKeys; 16 | 17 | export const useProductTypes = ( 18 | query?: StoreGetProductTypesParams, 19 | options?: UseQueryOptionsWrapper< 20 | Response, 21 | Error, 22 | ReturnType 23 | > 24 | ) => { 25 | const { client } = useMedusa(); 26 | const { data, ...rest } = useQuery( 27 | productTypeKeys.list(query), 28 | () => client.productTypes.list(query), 29 | options 30 | ); 31 | return { data, ...rest } as const; 32 | }; 33 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/products/index.ts: -------------------------------------------------------------------------------- 1 | export * from './queries'; 2 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/products/queries.ts: -------------------------------------------------------------------------------- 1 | import { Response } from '@medusajs/medusa-js'; 2 | import { 3 | StoreGetProductsParams, 4 | StoreProductsListRes, 5 | StoreProductsRes, 6 | } from '@medusajs/medusa'; 7 | import { useQuery } from '@tanstack/vue-query'; 8 | import { useMedusa } from '../../../useApi'; 9 | 10 | import { UseQueryOptionsWrapper } from '../../../types'; 11 | import { queryKeysFactory } from '../../utils/index'; 12 | 13 | const PRODUCTS_QUERY_KEY = `products` as const; 14 | 15 | export const productKeys = queryKeysFactory< 16 | typeof PRODUCTS_QUERY_KEY, 17 | StoreGetProductsParams 18 | >(PRODUCTS_QUERY_KEY); 19 | type ProductQueryKey = typeof productKeys; 20 | 21 | export const useProducts = ( 22 | query?: StoreGetProductsParams, 23 | options?: UseQueryOptionsWrapper< 24 | Response, 25 | Error, 26 | ReturnType 27 | > 28 | ) => { 29 | const { client } = useMedusa(); 30 | const { data, ...rest } = useQuery( 31 | productKeys.list(query), 32 | () => client.products.list(query), 33 | options 34 | ); 35 | return { data, ...rest } as const; 36 | }; 37 | 38 | export const useProduct = ( 39 | id: string, 40 | options?: UseQueryOptionsWrapper< 41 | Response, 42 | Error, 43 | ReturnType 44 | > 45 | ) => { 46 | const { client } = useMedusa(); 47 | const { data, ...rest } = useQuery( 48 | productKeys.detail(id), 49 | () => client.products.retrieve(id), 50 | options 51 | ); 52 | 53 | return { data, ...rest } as const; 54 | }; 55 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/regions/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./queries" 2 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/regions/queries.ts: -------------------------------------------------------------------------------- 1 | import { queryKeysFactory } from '../../utils/index'; 2 | import { UseQueryOptionsWrapper } from '../../../types'; 3 | import { useQuery } from '@tanstack/vue-query'; 4 | import { useMedusa } from '../../../useApi'; 5 | import { StoreRegionsRes, StoreRegionsListRes } from '@medusajs/medusa'; 6 | import { Response } from '@medusajs/medusa-js'; 7 | 8 | const REGIONS_QUERY_KEY = `regions` as const; 9 | 10 | const regionsKey = queryKeysFactory(REGIONS_QUERY_KEY); 11 | 12 | type RegionQueryType = typeof regionsKey; 13 | 14 | export const useRegions = ( 15 | options?: UseQueryOptionsWrapper< 16 | Response, 17 | Error, 18 | ReturnType 19 | > 20 | ) => { 21 | const { client } = useMedusa(); 22 | const { data, ...rest } = useQuery( 23 | regionsKey.lists(), 24 | () => client.regions.list(), 25 | options 26 | ); 27 | 28 | return { data, ...rest } as const; 29 | }; 30 | 31 | export const useRegion = ( 32 | id: string, 33 | options?: UseQueryOptionsWrapper< 34 | Response, 35 | Error, 36 | ReturnType 37 | > 38 | ) => { 39 | const { client } = useMedusa(); 40 | const { data, ...rest } = useQuery( 41 | regionsKey.detail(id), 42 | () => client.regions.retrieve(id), 43 | options 44 | ); 45 | return { data, ...rest } as const; 46 | }; 47 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/return-reasons/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./queries" 2 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/return-reasons/queries.ts: -------------------------------------------------------------------------------- 1 | import { queryKeysFactory } from '../../utils/index'; 2 | import { 3 | StoreReturnReasonsListRes, 4 | StoreReturnReasonsRes, 5 | } from '@medusajs/medusa'; 6 | import { Response } from '@medusajs/medusa-js'; 7 | import { useQuery } from '@tanstack/vue-query'; 8 | import { useMedusa } from '../../../useApi'; 9 | import { UseQueryOptionsWrapper } from '../../../types'; 10 | 11 | const RETURNS_REASONS_QUERY_KEY = `return_reasons` as const; 12 | 13 | const returnReasonsKey = queryKeysFactory(RETURNS_REASONS_QUERY_KEY); 14 | 15 | type ReturnReasonsQueryKey = typeof returnReasonsKey; 16 | 17 | export const useReturnReasons = ( 18 | options?: UseQueryOptionsWrapper< 19 | Response, 20 | Error, 21 | ReturnType 22 | > 23 | ) => { 24 | const { client } = useMedusa(); 25 | const { data, ...rest } = useQuery( 26 | returnReasonsKey.lists(), 27 | () => client.returnReasons.list(), 28 | options 29 | ); 30 | return { data, ...rest } as const; 31 | }; 32 | 33 | export const useReturnReason = ( 34 | id: string, 35 | options?: UseQueryOptionsWrapper< 36 | Response, 37 | Error, 38 | ReturnType 39 | > 40 | ) => { 41 | const { client } = useMedusa(); 42 | const { data, ...rest } = useQuery( 43 | returnReasonsKey.detail(id), 44 | () => client.returnReasons.retrieve(id), 45 | options 46 | ); 47 | return { data, ...rest } as const; 48 | }; 49 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/returns/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./mutations" 2 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/returns/mutations.ts: -------------------------------------------------------------------------------- 1 | import { StoreReturnsRes, StorePostReturnsReq } from '@medusajs/medusa'; 2 | import { useMutation, UseMutationOptions } from '@tanstack/vue-query'; 3 | import { useMedusa } from '../../../useApi'; 4 | 5 | export const useCreateReturn = ( 6 | options?: UseMutationOptions< 7 | StoreReturnsRes, 8 | Error, 9 | StorePostReturnsReq, 10 | unknown 11 | > 12 | ) => { 13 | const { client } = useMedusa(); 14 | return useMutation( 15 | (data: StorePostReturnsReq) => client.returns.create(data), 16 | options 17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/shipping-options/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./queries" 2 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/shipping-options/queries.ts: -------------------------------------------------------------------------------- 1 | import { queryKeysFactory } from '../../utils/index'; 2 | import { UseQueryOptionsWrapper } from '../../../types'; 3 | import { useQuery } from '@tanstack/vue-query'; 4 | import { useMedusa } from '../../../useApi'; 5 | import { 6 | StoreShippingOptionsListRes, 7 | StoreGetShippingOptionsParams, 8 | } from '@medusajs/medusa'; 9 | import { Response } from '@medusajs/medusa-js'; 10 | 11 | const SHIPPING_OPTION_QUERY_KEY = `shipping_options` as const; 12 | 13 | const shippingOptionKey = { 14 | ...queryKeysFactory(SHIPPING_OPTION_QUERY_KEY), 15 | cart: (cartId: string) => [...shippingOptionKey.all, 'cart', cartId] as const, 16 | }; 17 | 18 | type ShippingOptionQueryKey = typeof shippingOptionKey; 19 | 20 | export const useShippingOptions = ( 21 | query?: StoreGetShippingOptionsParams, 22 | options?: UseQueryOptionsWrapper< 23 | Response, 24 | Error, 25 | ReturnType 26 | > 27 | ) => { 28 | const { client } = useMedusa(); 29 | const { data, ...rest } = useQuery( 30 | shippingOptionKey.list(query), 31 | async () => client.shippingOptions.list(query), 32 | options 33 | ); 34 | return { data, ...rest } as const; 35 | }; 36 | 37 | export const useCartShippingOptions = ( 38 | cartId: string, 39 | options?: UseQueryOptionsWrapper< 40 | Response, 41 | Error, 42 | ReturnType 43 | > 44 | ) => { 45 | const { client } = useMedusa(); 46 | const { data, ...rest } = useQuery( 47 | shippingOptionKey.cart(cartId), 48 | async () => client.shippingOptions.listCartOptions(cartId), 49 | options 50 | ); 51 | return { data, ...rest } as const; 52 | }; 53 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/swaps/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./queries" 2 | export * from "./mutations" 3 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/swaps/mutations.ts: -------------------------------------------------------------------------------- 1 | import { StoreSwapsRes, StorePostSwapsReq } from '@medusajs/medusa'; 2 | import { useMutation, UseMutationOptions } from '@tanstack/vue-query'; 3 | import { useMedusa } from '../../../useApi'; 4 | 5 | export const useCreateSwap = ( 6 | options?: UseMutationOptions 7 | ) => { 8 | const { client } = useMedusa(); 9 | return useMutation( 10 | (data: StorePostSwapsReq) => client.swaps.create(data), 11 | options 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /packages/core/src/composables/store/swaps/queries.ts: -------------------------------------------------------------------------------- 1 | import { queryKeysFactory } from '../../utils/index'; 2 | import { StoreSwapsRes } from '@medusajs/medusa'; 3 | import { Response } from '@medusajs/medusa-js'; 4 | import { useQuery } from '@tanstack/vue-query'; 5 | import { useMedusa } from '../../../useApi'; 6 | import { UseQueryOptionsWrapper } from '../../../types'; 7 | 8 | const SWAPS_QUERY_KEY = `swaps` as const; 9 | 10 | const swapKey = { 11 | ...queryKeysFactory(SWAPS_QUERY_KEY), 12 | cart: (cartId: string) => [...swapKey.all, 'cart', cartId] as const, 13 | }; 14 | 15 | type SwapQueryKey = typeof swapKey; 16 | 17 | export const useCartSwap = ( 18 | cartId: string, 19 | options?: UseQueryOptionsWrapper< 20 | Response, 21 | Error, 22 | ReturnType 23 | > 24 | ) => { 25 | const { client } = useMedusa(); 26 | const { data, ...rest } = useQuery( 27 | swapKey.cart(cartId), 28 | () => client.swaps.retrieveByCartId(cartId), 29 | options 30 | ); 31 | 32 | return { data, ...rest } as const; 33 | }; 34 | -------------------------------------------------------------------------------- /packages/core/src/composables/utils/buildOptions.ts: -------------------------------------------------------------------------------- 1 | import { QueryClient, QueryKey, UseMutationOptions } from '@tanstack/vue-query'; 2 | 3 | export const buildOptions = < 4 | TData, 5 | TError, 6 | TVariables, 7 | TContext, 8 | TKey extends Array 9 | >( 10 | queryClient: QueryClient, 11 | queryKey?: TKey[] | TKey, 12 | options?: UseMutationOptions 13 | ): UseMutationOptions => { 14 | return { 15 | ...options, 16 | onSuccess: (...args) => { 17 | if (options?.onSuccess) { 18 | return options.onSuccess(...args); 19 | } 20 | 21 | if (queryKey !== undefined) { 22 | if (queryKey.filter(Array.isArray).length > 0) { 23 | queryKey.forEach(key => queryClient.invalidateQueries(key)); 24 | } else { 25 | queryClient.invalidateQueries(queryKey); 26 | } 27 | } 28 | }, 29 | }; 30 | }; 31 | -------------------------------------------------------------------------------- /packages/core/src/composables/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './queryKeysFactory'; 2 | -------------------------------------------------------------------------------- /packages/core/src/composables/utils/queryKeysFactory.ts: -------------------------------------------------------------------------------- 1 | import { TQueryKey } from '../../types'; 2 | 3 | export const queryKeysFactory = < 4 | T, 5 | TListQueryType = any, 6 | TDetailQueryType = string 7 | >( 8 | globalKey: T 9 | ) => { 10 | const queryKeyFactory: TQueryKey = { 11 | all: [globalKey], 12 | lists: () => [...queryKeyFactory.all, 'list'], 13 | list: (query?: TListQueryType) => [...queryKeyFactory.lists(), { query }], 14 | details: () => [...queryKeyFactory.all, 'detail'], 15 | detail: (id: TDetailQueryType) => [...queryKeyFactory.details(), id], 16 | }; 17 | return queryKeyFactory; 18 | }; 19 | -------------------------------------------------------------------------------- /packages/core/src/helpers/index.ts: -------------------------------------------------------------------------------- 1 | import { isEmpty } from 'lodash'; 2 | import { RegionInfo, ProductVariantInfo } from '../types'; 3 | 4 | type FormatVariantPriceParams = { 5 | variant: ProductVariantInfo; 6 | region: RegionInfo; 7 | includeTaxes?: boolean; 8 | minimumFractionDigits?: number; 9 | maximumFractionDigits?: number; 10 | locale?: string; 11 | }; 12 | 13 | /** 14 | * Takes a product variant and a region, and converts the variant's price to a localized decimal format 15 | */ 16 | export const formatVariantPrice = ({ 17 | variant, 18 | region, 19 | includeTaxes = true, 20 | ...rest 21 | }: FormatVariantPriceParams) => { 22 | const amount = computeVariantPrice({ variant, region, includeTaxes }); 23 | 24 | return convertToLocale({ 25 | amount, 26 | currency_code: region?.currency_code, 27 | ...rest, 28 | }); 29 | }; 30 | 31 | type ComputeVariantPriceParams = { 32 | variant: ProductVariantInfo; 33 | region: RegionInfo; 34 | includeTaxes?: boolean; 35 | }; 36 | 37 | /** 38 | * Takes a product variant and region, and returns the variant price as a decimal number 39 | * @param params.variant - product variant 40 | * @param params.region - region 41 | * @param params.includeTaxes - whether to include taxes or not 42 | */ 43 | export const computeVariantPrice = ({ 44 | variant, 45 | region, 46 | includeTaxes = true, 47 | }: ComputeVariantPriceParams) => { 48 | const amount = getVariantPrice(variant, region); 49 | 50 | return computeAmount({ 51 | amount, 52 | region, 53 | includeTaxes, 54 | }); 55 | }; 56 | 57 | /** 58 | * Finds the price amount correspoding to the region selected 59 | * @param variant - the product variant 60 | * @param region - the region 61 | * @returns - the price's amount 62 | */ 63 | export const getVariantPrice = ( 64 | variant: ProductVariantInfo, 65 | region: RegionInfo 66 | ) => { 67 | let price = variant?.prices?.find( 68 | p => p.currency_code.toLowerCase() === region?.currency_code?.toLowerCase() 69 | ); 70 | 71 | return price?.amount || 0; 72 | }; 73 | 74 | type ComputeAmountParams = { 75 | amount: number; 76 | region: RegionInfo; 77 | includeTaxes?: boolean; 78 | }; 79 | 80 | /** 81 | * Takes an amount, a region, and returns the amount as a decimal including or excluding taxes 82 | */ 83 | export const computeAmount = ({ 84 | amount, 85 | region, 86 | includeTaxes = true, 87 | }: ComputeAmountParams) => { 88 | const toDecimal = convertToDecimal(amount, region); 89 | 90 | const taxRate = includeTaxes ? getTaxRate(region) : 0; 91 | 92 | const amountWithTaxes = toDecimal * (1 + taxRate); 93 | 94 | return amountWithTaxes; 95 | }; 96 | 97 | type FormatAmountParams = { 98 | amount: number; 99 | region: RegionInfo; 100 | includeTaxes?: boolean; 101 | minimumFractionDigits?: number; 102 | maximumFractionDigits?: number; 103 | locale?: string; 104 | }; 105 | 106 | /** 107 | * Takes an amount and a region, and converts the amount to a localized decimal format 108 | */ 109 | export const formatAmount = ({ 110 | amount, 111 | region, 112 | includeTaxes = true, 113 | ...rest 114 | }: FormatAmountParams) => { 115 | const taxAwareAmount = computeAmount({ 116 | amount, 117 | region, 118 | includeTaxes, 119 | }); 120 | return convertToLocale({ 121 | amount: taxAwareAmount, 122 | currency_code: region.currency_code, 123 | ...rest, 124 | }); 125 | }; 126 | 127 | // we should probably add a more extensive list 128 | const noDivisionCurrencies = ['krw', 'jpy', 'vnd']; 129 | 130 | const convertToDecimal = (amount: number, region: RegionInfo) => { 131 | const divisor = noDivisionCurrencies.includes( 132 | region?.currency_code?.toLowerCase() 133 | ) 134 | ? 1 135 | : 100; 136 | 137 | return Math.floor(amount) / divisor; 138 | }; 139 | 140 | const getTaxRate = (region?: RegionInfo) => { 141 | return region && !isEmpty(region) ? region?.tax_rate / 100 : 0; 142 | }; 143 | 144 | const convertToLocale = ({ 145 | amount, 146 | currency_code, 147 | minimumFractionDigits, 148 | maximumFractionDigits, 149 | locale = 'en-US', 150 | }: ConvertToLocaleParams) => { 151 | return currency_code && !isEmpty(currency_code) 152 | ? new Intl.NumberFormat(locale, { 153 | style: 'currency', 154 | currency: currency_code, 155 | minimumFractionDigits, 156 | maximumFractionDigits, 157 | }).format(amount) 158 | : amount.toString(); 159 | }; 160 | 161 | type ConvertToLocaleParams = { 162 | amount: number; 163 | currency_code: string; 164 | minimumFractionDigits?: number; 165 | maximumFractionDigits?: number; 166 | locale?: string; 167 | }; 168 | -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './composables/'; 2 | export * from './helpers'; 3 | 4 | export { createMedusaVueClient } from './medusaVueClient'; 5 | -------------------------------------------------------------------------------- /packages/core/src/injectionSymbols.ts: -------------------------------------------------------------------------------- 1 | import { InjectionKey } from 'vue'; 2 | import Medusa from '@medusajs/medusa-js'; 3 | 4 | export const medusaKey = Symbol('medusa-client') as InjectionKey<{ 5 | client: Medusa; 6 | }>; 7 | -------------------------------------------------------------------------------- /packages/core/src/medusaVueClient.ts: -------------------------------------------------------------------------------- 1 | import { VueQueryPlugin, VueQueryPluginOptions } from '@tanstack/vue-query'; 2 | import Medusa from '@medusajs/medusa-js'; 3 | import { App } from 'vue'; 4 | 5 | import { medusaKey } from './injectionSymbols'; 6 | 7 | interface MedusaVueClientProps { 8 | baseUrl: string; 9 | maxRetries?: number; 10 | /** 11 | * Authentication token 12 | */ 13 | apiKey?: string; 14 | /** 15 | * PublishableApiKey identifier that defines the scope of resources 16 | * available within the request 17 | */ 18 | publishableApiKey?: string; 19 | 20 | queryClientProviderProps?: VueQueryPluginOptions; 21 | } 22 | 23 | export const createMedusaVueClient = (options: MedusaVueClientProps) => { 24 | const medusaVueClient = { 25 | install: (app: App) => { 26 | const medusa = new Medusa({ 27 | baseUrl: options.baseUrl, 28 | apiKey: options.apiKey, 29 | publishableApiKey: options.publishableApiKey, 30 | maxRetries: options.maxRetries || 1, 31 | }); 32 | 33 | const defaultVueQueryPluginOptions: VueQueryPluginOptions = { 34 | queryClientConfig: { 35 | defaultOptions: { 36 | queries: { 37 | cacheTime: 500, 38 | refetchOnWindowFocus: false, 39 | staleTime: 1000 * 60 * 60 * 24, 40 | retry: 1, 41 | }, 42 | }, 43 | }, 44 | }; 45 | 46 | app.provide(medusaKey, { client: medusa }); 47 | 48 | app.use( 49 | VueQueryPlugin, 50 | options.queryClientProviderProps || defaultVueQueryPluginOptions 51 | ); 52 | }, 53 | }; 54 | 55 | return medusaVueClient; 56 | }; 57 | -------------------------------------------------------------------------------- /packages/core/src/types.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ProductVariant as ProductVariantEntity, 3 | Region, 4 | StoreCartsRes, 5 | } from '@medusajs/medusa'; 6 | import { QueryKey, UseQueryOptions } from '@tanstack/vue-query'; 7 | 8 | export type UseQueryOptionsWrapper< 9 | // Return type of queryFn 10 | TQueryFn = unknown, 11 | // Type thrown in case the queryFn rejects 12 | E = Error, 13 | // Query key type 14 | TQueryKey extends QueryKey = QueryKey 15 | > = Omit< 16 | UseQueryOptions, 17 | 'queryKey' | 'queryFn' | 'select' | 'refetchInterval' 18 | >; 19 | 20 | // Choose only a subset of the type Region to allow for some flexibility 21 | export type RegionInfo = Pick< 22 | Region, 23 | 'currency_code' | 'tax_code' | 'tax_rate' 24 | >; 25 | export type ProductVariant = ConvertDateToString< 26 | Omit 27 | >; 28 | export type ProductVariantInfo = Pick; 29 | 30 | type ConvertDateToString = { 31 | [P in keyof T]: T[P] extends Date ? Date | string : T[P]; 32 | }; 33 | 34 | export type Cart = StoreCartsRes['cart']; 35 | 36 | export type TQueryKey = { 37 | all: [TKey]; 38 | lists: () => [...TQueryKey['all'], 'list']; 39 | list: ( 40 | query?: TListQuery 41 | ) => [ 42 | ...ReturnType['lists']>, 43 | { query: TListQuery | undefined } 44 | ]; 45 | details: () => [...TQueryKey['all'], 'detail']; 46 | detail: ( 47 | id: TDetailQuery 48 | ) => [...ReturnType['details']>, TDetailQuery]; 49 | }; 50 | -------------------------------------------------------------------------------- /packages/core/src/useApi.ts: -------------------------------------------------------------------------------- 1 | import { inject } from 'vue'; 2 | import { medusaKey } from './injectionSymbols'; 3 | import Medusa from '@medusajs/medusa-js'; 4 | 5 | /** 6 | * Returns the medusa Client. 7 | */ 8 | export function useMedusa(): { client: Medusa } { 9 | return inject(medusaKey)!; 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/tests/composables/store/carts/mutations.test.ts: -------------------------------------------------------------------------------- 1 | import { fixtures } from '../../../../mocks/data'; 2 | import { 3 | useAddShippingMethodToCart, 4 | useCompleteCart, 5 | useCreateCart, 6 | useCreatePaymentSession, 7 | useDeletePaymentSession, 8 | useRefreshPaymentSession, 9 | useSetPaymentSession, 10 | useUpdateCart, 11 | useUpdatePaymentSession, 12 | } from '../../../../src'; 13 | import { createWrapperComponent, waitFor } from '../../../utils'; 14 | 15 | describe('useCreateCart hook', () => { 16 | test('creates a cart', async () => { 17 | const { vm } = createWrapperComponent(() => useCreateCart()); 18 | 19 | vm.mutate({}); 20 | 21 | await waitFor(() => vm.isSuccess); 22 | 23 | expect(vm.data?.response.status).toEqual(200); 24 | expect(vm.data?.cart).toEqual(fixtures.get('cart')); 25 | }); 26 | }); 27 | 28 | describe('useUpdateCart hook', () => { 29 | test('updates a cart', async () => { 30 | const { vm } = createWrapperComponent(() => useUpdateCart('some-cart')); 31 | 32 | vm.mutate({ 33 | email: 'new@email.com', 34 | }); 35 | 36 | await waitFor(() => vm.isSuccess); 37 | 38 | expect(vm.data?.response.status).toEqual(200); 39 | expect(vm.data?.cart).toEqual({ 40 | ...fixtures.get('cart'), 41 | id: 'some-cart', 42 | email: 'new@email.com', 43 | }); 44 | }); 45 | }); 46 | 47 | describe('useCompleteCart hook', () => { 48 | test('completes a cart', async () => { 49 | const { vm } = createWrapperComponent(() => useCompleteCart('test-cart')); 50 | 51 | vm.mutate(); 52 | 53 | await waitFor(() => vm.isSuccess); 54 | 55 | expect(vm.data?.response.status).toEqual(200); 56 | expect(vm.data?.type).toEqual('order'); 57 | expect(vm.data?.data).toEqual(fixtures.get('order')); 58 | }); 59 | }); 60 | 61 | describe('useCreatePaymentSession hook', () => { 62 | test('creates a payment session', async () => { 63 | const { vm } = createWrapperComponent(() => 64 | useCreatePaymentSession('test-cart') 65 | ); 66 | 67 | vm.mutate(); 68 | 69 | await waitFor(() => vm.isSuccess); 70 | 71 | expect(vm.data?.response.status).toEqual(200); 72 | expect(vm.data?.cart).toEqual({ 73 | ...fixtures.get('cart'), 74 | id: 'test-cart', 75 | }); 76 | }); 77 | }); 78 | 79 | describe('useUpdatePaymentSession hook', () => { 80 | test('updates a payment session', async () => { 81 | const { vm } = createWrapperComponent(() => 82 | useUpdatePaymentSession('test-cart') 83 | ); 84 | 85 | vm.mutate({ 86 | data: {}, 87 | provider_id: 'stripe', 88 | }); 89 | 90 | await waitFor(() => vm.isSuccess); 91 | 92 | expect(vm.data?.response.status).toEqual(200); 93 | expect(vm.data?.cart).toEqual({ 94 | ...fixtures.get('cart'), 95 | id: 'test-cart', 96 | }); 97 | }); 98 | }); 99 | 100 | describe('useRefreshPaymentSession hook', () => { 101 | test('refreshes a payment session', async () => { 102 | const { vm } = createWrapperComponent(() => 103 | useRefreshPaymentSession('test-cart') 104 | ); 105 | 106 | vm.mutate({ 107 | provider_id: 'stripe', 108 | }); 109 | 110 | await waitFor(() => vm.isSuccess); 111 | 112 | expect(vm.data?.response.status).toEqual(200); 113 | expect(vm.data?.cart).toEqual({ 114 | ...fixtures.get('cart'), 115 | id: 'test-cart', 116 | }); 117 | }); 118 | }); 119 | 120 | describe('useSetPaymentSession hook', () => { 121 | test('sets a payment session', async () => { 122 | const { vm } = createWrapperComponent(() => 123 | useSetPaymentSession('test-cart') 124 | ); 125 | 126 | vm.mutate({ 127 | provider_id: 'stripe', 128 | }); 129 | 130 | await waitFor(() => vm.isSuccess); 131 | 132 | expect(vm.data?.response.status).toEqual(200); 133 | expect(vm.data?.cart).toEqual({ 134 | ...fixtures.get('cart'), 135 | id: 'test-cart', 136 | }); 137 | }); 138 | }); 139 | 140 | describe('useDeletePaymentSession hook', () => { 141 | test('deletes a payment session', async () => { 142 | const { vm } = createWrapperComponent(() => 143 | useDeletePaymentSession('test-cart') 144 | ); 145 | 146 | vm.mutate({ 147 | provider_id: 'stripe', 148 | }); 149 | 150 | await waitFor(() => vm.isSuccess); 151 | 152 | expect(vm.data?.response.status).toEqual(200); 153 | expect(vm.data?.cart).toEqual({ 154 | ...fixtures.get('cart'), 155 | id: 'test-cart', 156 | }); 157 | }); 158 | }); 159 | 160 | describe('useAddShippingMethodToCart hook', () => { 161 | test('adds a shipping method to a cart', async () => { 162 | const { vm } = createWrapperComponent(() => 163 | useAddShippingMethodToCart('test-cart') 164 | ); 165 | 166 | vm.mutate({ 167 | option_id: 'test-option', 168 | }); 169 | 170 | await waitFor(() => vm.isSuccess); 171 | 172 | expect(vm.data?.response.status).toEqual(200); 173 | expect(vm.data?.cart).toEqual({ 174 | ...fixtures.get('cart'), 175 | id: 'test-cart', 176 | }); 177 | }); 178 | }); 179 | -------------------------------------------------------------------------------- /packages/core/tests/composables/store/carts/queries.test.ts: -------------------------------------------------------------------------------- 1 | import { fixtures } from '../../../../mocks/data'; 2 | import { useGetCart } from '../../../../src'; 3 | import { createWrapperComponent, waitFor } from '../../../utils'; 4 | 5 | describe('useGetCart hook', () => { 6 | test('returns a cart', async () => { 7 | const cart = fixtures.get('cart'); 8 | const { vm } = createWrapperComponent(() => useGetCart(cart.id)); 9 | 10 | await waitFor(() => vm.isSuccess); 11 | 12 | expect(vm.data?.response.status).toEqual(200); 13 | expect(vm.data?.cart).toEqual(cart); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/core/tests/composables/store/collections/queries.test.ts: -------------------------------------------------------------------------------- 1 | import { fixtures } from '../../../../mocks/data'; 2 | import { useCollections, useCollection } from '../../../../src'; 3 | import { createWrapperComponent, waitFor } from '../../../utils'; 4 | 5 | describe('useCollections hook', () => { 6 | test('returns a list of collections', async () => { 7 | const { vm } = createWrapperComponent(() => useCollections()); 8 | 9 | await waitFor(() => vm.isSuccess); 10 | 11 | expect(vm.data?.response.status).toEqual(200); 12 | expect(vm.data?.collections).toEqual(fixtures.list('product_collection')); 13 | }); 14 | }); 15 | 16 | describe('useCollection hook', () => { 17 | test('returns a collection', async () => { 18 | const collection = fixtures.get('product_collection'); 19 | const { vm } = createWrapperComponent(() => useCollection(collection.id)); 20 | 21 | await waitFor(() => vm.isSuccess); 22 | 23 | expect(vm.data?.response.status).toEqual(200); 24 | expect(vm.data?.collection).toEqual(collection); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /packages/core/tests/composables/store/customers/mutations.test.ts: -------------------------------------------------------------------------------- 1 | import { fixtures } from '../../../../mocks/data'; 2 | import { useCreateCustomer, useUpdateMe } from '../../../../src/'; 3 | import { createWrapperComponent, waitFor } from '../../../utils'; 4 | 5 | describe('useCreateCustomer hook', () => { 6 | test('creates a new customer', async () => { 7 | const customer = { 8 | first_name: 'john', 9 | last_name: 'wick', 10 | email: 'johnwick@medusajs.com', 11 | password: 'supersecret', 12 | phone: '111111', 13 | }; 14 | 15 | const { vm } = createWrapperComponent(() => useCreateCustomer()); 16 | 17 | vm.mutate(customer); 18 | 19 | await waitFor(() => vm.isSuccess); 20 | 21 | expect(vm.data?.response.status).toEqual(200); 22 | expect(vm.data?.customer).toEqual({ 23 | ...fixtures.get('customer'), 24 | ...customer, 25 | }); 26 | }); 27 | }); 28 | 29 | describe('useUpdateMe hook', () => { 30 | test('updates current customer', async () => { 31 | const customer = { 32 | first_name: 'lebron', 33 | last_name: 'james', 34 | email: 'lebronjames@medusajs.com', 35 | password: 'supersecret', 36 | phone: '111111', 37 | }; 38 | 39 | const { vm } = createWrapperComponent(() => useUpdateMe()); 40 | 41 | vm.mutate({ 42 | id: 'cus_test', 43 | ...customer, 44 | }); 45 | 46 | await waitFor(() => vm.isSuccess); 47 | 48 | expect(vm.data?.response.status).toEqual(200); 49 | expect(vm.data?.customer).toEqual({ 50 | ...fixtures.get('customer'), 51 | ...customer, 52 | }); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /packages/core/tests/composables/store/customers/queries.test.ts: -------------------------------------------------------------------------------- 1 | import { rest } from 'msw'; 2 | import { fixtures } from '../../../../mocks/data'; 3 | import { server } from '../../../../mocks/server'; 4 | import { useCustomerOrders, useMeCustomer } from '../../../../src'; 5 | import { createWrapperComponent, waitFor } from '../../../utils'; 6 | 7 | describe('useMeCustomer hook', () => { 8 | test('returns customer', async () => { 9 | const { vm } = createWrapperComponent(() => useMeCustomer()); 10 | 11 | await waitFor(() => vm.isSuccess); 12 | 13 | expect(vm.data?.response.status).toEqual(200); 14 | expect(vm.data?.customer).toEqual(fixtures.get('customer')); 15 | }); 16 | }); 17 | 18 | describe('useCustomerOrders hook', () => { 19 | test("returns customer's orders", async () => { 20 | const orders = fixtures.list('order', 5); 21 | 22 | const { vm } = createWrapperComponent(() => useCustomerOrders()); 23 | 24 | await waitFor(() => vm.isSuccess); 25 | 26 | expect(vm.data?.response.status).toEqual(200); 27 | expect(vm.data?.orders).toEqual(orders); 28 | expect(vm.data?.limit).toEqual(5); 29 | expect(vm.data?.offset).toEqual(0); 30 | }); 31 | 32 | test("propagates query params and returns customer's orders", async () => { 33 | const orders = fixtures.list('order'); 34 | 35 | server.use( 36 | rest.get('/store/customers/me/orders', (req, res, ctx) => { 37 | const limit = req.url.searchParams.get('limit'); 38 | const offset = req.url.searchParams.get('offset'); 39 | const expand = req.url.searchParams.get('expand'); 40 | const fields = req.url.searchParams.get('fields'); 41 | expect({ 42 | limit, 43 | offset, 44 | expand, 45 | fields, 46 | }).toEqual({ 47 | limit: '2', 48 | offset: '5', 49 | expand: 'relation_1,relation_2', 50 | fields: 'field_1,field_2', 51 | }); 52 | return res( 53 | ctx.status(200), 54 | ctx.json({ 55 | orders, 56 | limit: 2, 57 | offset: 5, 58 | }) 59 | ); 60 | }) 61 | ); 62 | 63 | const { vm } = createWrapperComponent(() => 64 | useCustomerOrders({ 65 | limit: 2, 66 | offset: 5, 67 | expand: 'relation_1,relation_2', 68 | fields: 'field_1,field_2', 69 | }) 70 | ); 71 | 72 | await waitFor(() => vm.isSuccess); 73 | 74 | expect(vm.data?.response.status).toEqual(200); 75 | expect(vm.data?.orders).toEqual(orders); 76 | expect(vm.data?.limit).toEqual(2); 77 | expect(vm.data?.offset).toEqual(5); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /packages/core/tests/composables/store/gift_cards/queries.test.ts: -------------------------------------------------------------------------------- 1 | import { fixtures } from '../../../../mocks/data'; 2 | import { useGiftCard } from '../../../../src/'; 3 | import { createWrapperComponent, waitFor } from '../../../utils'; 4 | 5 | describe('useGiftCard hook', () => { 6 | test('returns a gift card', async () => { 7 | const giftCard = fixtures.get('gift_card'); 8 | const { vm } = createWrapperComponent(() => useGiftCard(giftCard.id)); 9 | 10 | await waitFor(() => vm.isSuccess); 11 | 12 | expect(vm.data?.response.status).toEqual(200); 13 | expect(vm.data?.gift_card).toEqual(giftCard); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/core/tests/composables/store/line-items/mutations.test.ts: -------------------------------------------------------------------------------- 1 | import { fixtures } from '../../../../mocks/data'; 2 | import { 3 | useCreateLineItem, 4 | useDeleteLineItem, 5 | useUpdateLineItem, 6 | } from '../../../../src/'; 7 | import { createWrapperComponent, waitFor } from '../../../utils'; 8 | 9 | describe('useCreateLineItem hook', () => { 10 | test('creates a line item', async () => { 11 | const lineItem = { 12 | variant_id: 'test-variant', 13 | quantity: 1, 14 | }; 15 | 16 | const { vm } = createWrapperComponent(() => useCreateLineItem('test-cart')); 17 | 18 | vm.mutate(lineItem); 19 | 20 | await waitFor(() => vm.isSuccess); 21 | 22 | expect(vm.data?.response.status).toEqual(200); 23 | expect(vm.data?.cart.items).toEqual( 24 | expect.arrayContaining([ 25 | expect.objectContaining({ 26 | ...lineItem, 27 | }), 28 | ]) 29 | ); 30 | }); 31 | }); 32 | 33 | describe('useUpdateLineItem hook', () => { 34 | test('updates a line item', async () => { 35 | const lineItem = { 36 | lineId: 'some-item-id', 37 | quantity: 3, 38 | }; 39 | 40 | const { vm } = createWrapperComponent(() => useUpdateLineItem('test-cart')); 41 | 42 | vm.mutate(lineItem); 43 | 44 | await waitFor(() => vm.isSuccess); 45 | 46 | expect(vm.data?.response.status).toEqual(200); 47 | expect(vm.data?.cart.items).toEqual( 48 | expect.arrayContaining([ 49 | expect.objectContaining({ 50 | id: lineItem.lineId, 51 | quantity: lineItem.quantity, 52 | }), 53 | ]) 54 | ); 55 | }); 56 | }); 57 | 58 | describe('useDeleteLineItem hook', () => { 59 | test('deletes a line item', async () => { 60 | const lineItem = { 61 | lineId: 'some-item-id', 62 | }; 63 | 64 | const { vm } = createWrapperComponent(() => useDeleteLineItem('test-cart')); 65 | 66 | vm.mutate(lineItem); 67 | 68 | await waitFor(() => vm.isSuccess); 69 | 70 | expect(vm.data?.response.status).toEqual(200); 71 | expect(vm.data?.cart).toEqual(fixtures.get('cart')); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /packages/core/tests/composables/store/order-edits/mutations.test.ts: -------------------------------------------------------------------------------- 1 | import { useCompleteOrderEdit, useDeclineOrderEdit } from '../../../../src/'; 2 | import { createWrapperComponent, waitFor } from '../../../utils'; 3 | 4 | describe('useDeclineOrderEdit hook', () => { 5 | test('decline an order edit', async () => { 6 | const declineBody = { 7 | declined_reason: 'Wrong color', 8 | }; 9 | 10 | const { vm } = createWrapperComponent(() => 11 | useDeclineOrderEdit('store_order_edit') 12 | ); 13 | 14 | vm.mutate(declineBody); 15 | 16 | await waitFor(() => vm.isSuccess); 17 | 18 | expect(vm.data?.response.status).toEqual(200); 19 | expect(vm.data?.order_edit).toEqual( 20 | expect.objectContaining({ 21 | status: 'declined', 22 | ...declineBody, 23 | }) 24 | ); 25 | }); 26 | }); 27 | 28 | describe('useCompleteOrderEdit hook', () => { 29 | test('complete an order edit', async () => { 30 | const { vm } = createWrapperComponent(() => 31 | useCompleteOrderEdit('store_order_edit') 32 | ); 33 | 34 | vm.mutate(); 35 | 36 | await waitFor(() => vm.isSuccess); 37 | 38 | expect(vm.data?.response.status).toEqual(200); 39 | expect(vm.data?.order_edit).toEqual( 40 | expect.objectContaining({ 41 | status: 'confirmed', 42 | }) 43 | ); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /packages/core/tests/composables/store/order-edits/queries.test.ts: -------------------------------------------------------------------------------- 1 | import { fixtures } from '../../../../mocks/data'; 2 | import { createWrapperComponent, waitFor } from '../../../utils'; 3 | import { useOrderEdit } from '../../../../src'; 4 | 5 | describe('useOrderEdit hook', () => { 6 | test('returns an order', async () => { 7 | const store_order_edit = fixtures.get('store_order_edit'); 8 | 9 | const { vm } = createWrapperComponent(() => 10 | useOrderEdit(store_order_edit.id) 11 | ); 12 | 13 | await waitFor(() => vm.isSuccess); 14 | 15 | expect(vm.data?.response.status).toEqual(200); 16 | expect(vm.data?.order_edit).toEqual(store_order_edit); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /packages/core/tests/composables/store/orders/mutations.test.ts: -------------------------------------------------------------------------------- 1 | import { useRequestOrderAccess, useGrantOrderAccess } from '../../../../src/'; 2 | 3 | import { createWrapperComponent, waitFor } from '../../../utils'; 4 | 5 | describe('useGrantOrderAccess hook', () => { 6 | test('Grant access to token', async () => { 7 | const { vm } = createWrapperComponent(() => useGrantOrderAccess()); 8 | 9 | vm.mutate({ token: 'store_order_edit' }); 10 | 11 | await waitFor(() => vm.isSuccess); 12 | 13 | expect(vm.data?.response.status).toEqual(200); 14 | }); 15 | }); 16 | 17 | describe('useRequestOrderAccess hook', () => { 18 | test('Requests access to ids', async () => { 19 | const { vm } = createWrapperComponent(() => useRequestOrderAccess()); 20 | 21 | vm.mutate({ order_ids: [''] }); 22 | 23 | await waitFor(() => vm.isSuccess); 24 | 25 | expect(vm.data?.response.status).toEqual(200); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /packages/core/tests/composables/store/orders/queries.test.ts: -------------------------------------------------------------------------------- 1 | import { useOrders } from '../../../../src/'; 2 | import { rest } from 'msw'; 3 | import { server } from '../../../../mocks/server'; 4 | import { useCartOrder, useOrder } from '../../../../src/'; 5 | import { fixtures } from '../../../../mocks/data'; 6 | import { createWrapperComponent, waitFor } from '../../../utils'; 7 | 8 | describe('useOrder hook', () => { 9 | test('returns an order', async () => { 10 | const order = fixtures.get('order'); 11 | 12 | const { vm } = createWrapperComponent(() => useOrder(order.id)); 13 | 14 | await waitFor(() => vm.isSuccess); 15 | 16 | expect(vm.data?.response.status).toEqual(200); 17 | expect(vm.data?.order).toEqual(order); 18 | }); 19 | }); 20 | 21 | describe('useCartOrder hook', () => { 22 | test('returns a cart order', async () => { 23 | const order = fixtures.get('order'); 24 | 25 | const { vm } = createWrapperComponent(() => useCartOrder('test_cart')); 26 | 27 | await waitFor(() => vm.isSuccess); 28 | 29 | expect(vm.data?.response.status).toEqual(200); 30 | expect(vm.data?.order).toEqual(order); 31 | }); 32 | }); 33 | 34 | describe('useOrders hook', () => { 35 | test('propagates the query params and returns an order', async () => { 36 | const order = fixtures.get('order'); 37 | const displayId = 400, 38 | emailParam = 'customer@test.com'; 39 | 40 | server.use( 41 | rest.get('/store/orders', (req, res, ctx) => { 42 | const display_id = req.url.searchParams.get('display_id'); 43 | const email = req.url.searchParams.get('email'); 44 | expect({ 45 | display_id, 46 | email, 47 | }).toEqual({ 48 | email: emailParam, 49 | display_id: displayId.toString(), 50 | }); 51 | return res( 52 | ctx.status(200), 53 | ctx.json({ 54 | order, 55 | }) 56 | ); 57 | }) 58 | ); 59 | 60 | const { vm } = createWrapperComponent(() => 61 | useOrders({ 62 | display_id: displayId, 63 | email: emailParam, 64 | }) 65 | ); 66 | 67 | await waitFor(() => vm.isSuccess); 68 | 69 | expect(vm.data?.response.status).toEqual(200); 70 | expect(vm.data?.order).toEqual(order); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /packages/core/tests/composables/store/payment-collections/mutations.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | useManageMultiplePaymentSessions, 3 | useManagePaymentSession, 4 | useAuthorizePaymentSession, 5 | useAuthorizePaymentSessionsBatch, 6 | usePaymentCollectionRefreshPaymentSession, 7 | } from '../../../../src'; 8 | 9 | import { createWrapperComponent, waitFor } from '../../../utils'; 10 | 11 | describe('useManageMultiplePaymentSessions hook', () => { 12 | test('Manage multiple payment sessions of a payment collection', async () => { 13 | const { vm } = createWrapperComponent(() => 14 | useManageMultiplePaymentSessions('payment_collection_id') 15 | ); 16 | 17 | vm.mutate({ 18 | sessions: [ 19 | { 20 | provider_id: 'manual', 21 | amount: 900, 22 | }, 23 | ], 24 | }); 25 | 26 | await waitFor(() => vm.isSuccess); 27 | 28 | expect(vm.data?.response.status).toEqual(200); 29 | expect(vm.data?.payment_collection).toEqual( 30 | expect.objectContaining({ 31 | id: 'payment_collection_id', 32 | amount: 900, 33 | }) 34 | ); 35 | }); 36 | }); 37 | 38 | describe('useManagePaymentSession hook', () => { 39 | test('Manage payment session of a payment collection', async () => { 40 | const { vm } = createWrapperComponent(() => 41 | useManagePaymentSession('payment_collection_id') 42 | ); 43 | 44 | vm.mutate({ 45 | provider_id: 'manual', 46 | }); 47 | 48 | await waitFor(() => vm.isSuccess); 49 | 50 | expect(vm.data?.response.status).toEqual(200); 51 | expect(vm.data?.payment_collection).toEqual( 52 | expect.objectContaining({ 53 | id: 'payment_collection_id', 54 | amount: 900, 55 | }) 56 | ); 57 | }); 58 | }); 59 | 60 | describe('useAuthorizePaymentSession hook', () => { 61 | test('Authorize a payment session of a Payment Collection', async () => { 62 | const { vm } = createWrapperComponent(() => 63 | useAuthorizePaymentSession('payment_collection_id') 64 | ); 65 | 66 | vm.mutate('123'); 67 | 68 | await waitFor(() => vm.isSuccess); 69 | 70 | expect(vm.data?.response.status).toEqual(200); 71 | 72 | expect(vm.data?.payment_collection).toEqual( 73 | expect.objectContaining({ 74 | id: '123', 75 | amount: 900, 76 | }) 77 | ); 78 | }); 79 | }); 80 | 81 | describe('authorizePaymentSessionsBatch hook', () => { 82 | test('Authorize all payment sessions of a Payment Collection', async () => { 83 | const { vm } = createWrapperComponent(() => 84 | useAuthorizePaymentSessionsBatch('payment_collection_id') 85 | ); 86 | 87 | vm.mutate({ 88 | session_ids: ['abc'], 89 | }); 90 | 91 | await waitFor(() => vm.isSuccess); 92 | 93 | expect(vm.data?.response.status).toEqual(207); 94 | 95 | expect(vm.data?.payment_collection).toEqual( 96 | expect.objectContaining({ 97 | id: 'payment_collection_id', 98 | payment_sessions: expect.arrayContaining([ 99 | expect.objectContaining({ 100 | amount: 900, 101 | }), 102 | ]), 103 | }) 104 | ); 105 | }); 106 | }); 107 | 108 | describe('usePaymentCollectionRefreshPaymentSession hook', () => { 109 | test('Refresh a payment sessions of a Payment Collection', async () => { 110 | const { vm } = createWrapperComponent(() => 111 | usePaymentCollectionRefreshPaymentSession('payment_collection_id') 112 | ); 113 | 114 | vm.mutate('session_id'); 115 | 116 | await waitFor(() => vm.isSuccess); 117 | 118 | expect(vm.data?.response.status).toEqual(200); 119 | expect(vm.data?.payment_session).toEqual( 120 | expect.objectContaining({ 121 | id: 'new_session_id', 122 | amount: 900, 123 | }) 124 | ); 125 | }); 126 | }); 127 | -------------------------------------------------------------------------------- /packages/core/tests/composables/store/payment-collections/queries.test.ts: -------------------------------------------------------------------------------- 1 | import { fixtures } from '../../../../mocks/data'; 2 | import { createWrapperComponent, waitFor } from '../../../utils'; 3 | import { usePaymentCollection } from '../../../../src/'; 4 | 5 | describe('usePaymentCollection hook', () => { 6 | test('returns a payment collection', async () => { 7 | const payment_collection = fixtures.get('payment_collection'); 8 | 9 | const { vm } = createWrapperComponent(() => 10 | usePaymentCollection(payment_collection.id) 11 | ); 12 | 13 | await waitFor(() => vm.isSuccess); 14 | 15 | expect(vm.data?.response.status).toEqual(200); 16 | expect(vm.data?.payment_collection).toEqual(payment_collection); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /packages/core/tests/composables/store/product-types/queries.test.ts: -------------------------------------------------------------------------------- 1 | import { fixtures } from '../../../../mocks/data'; 2 | import { useProductTypes } from '../../../../src/'; 3 | import { createWrapperComponent, waitFor } from '../../../utils'; 4 | 5 | describe('useProductTypes hook', () => { 6 | test('returns product types', async () => { 7 | const productTypes = fixtures.list('product_type'); 8 | 9 | const { vm } = createWrapperComponent(() => useProductTypes()); 10 | 11 | await waitFor(() => vm.isSuccess); 12 | 13 | expect(vm.data?.response.status).toEqual(200); 14 | expect(vm.data?.product_types).toEqual(productTypes); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /packages/core/tests/composables/store/products/queries.test.ts: -------------------------------------------------------------------------------- 1 | import { fixtures } from '../../../../mocks/data'; 2 | import { useProduct, useProducts } from '../../../../src'; 3 | import { createWrapperComponent, waitFor } from '../../../utils'; 4 | 5 | describe('useProducts hook', () => { 6 | test('gets a list of products', async () => { 7 | const { vm } = createWrapperComponent(() => useProducts()); 8 | 9 | await waitFor(() => vm.isSuccess); 10 | 11 | expect(vm.data?.response.status).toEqual(200); 12 | expect(vm.data?.products).toEqual(fixtures.list('product')); 13 | }); 14 | 15 | test('gets a list of products based on limit and offset', async () => { 16 | const { vm } = createWrapperComponent(() => 17 | useProducts({ 18 | limit: 2, 19 | offset: 5, 20 | }) 21 | ); 22 | 23 | await waitFor(() => vm.isSuccess); 24 | 25 | expect(vm.data?.response.status).toEqual(200); 26 | expect(vm.data?.products).toEqual(fixtures.list('product')); 27 | expect(vm.data?.limit).toEqual(2); 28 | expect(vm.data?.offset).toEqual(5); 29 | }); 30 | }); 31 | 32 | describe('useProducts hook', () => { 33 | test('success', async () => { 34 | const { vm } = createWrapperComponent(() => 35 | useProduct('prod_01F0YESHQ27Y31CAMD0NV6W9YP') 36 | ); 37 | 38 | await waitFor(() => vm.isSuccess); 39 | 40 | expect(vm.data?.response.status).toEqual(200); 41 | expect(vm.data?.product).toEqual(fixtures.get('product')); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /packages/core/tests/composables/store/regions/queries.test.ts: -------------------------------------------------------------------------------- 1 | import { fixtures } from '../../../../mocks/data'; 2 | import { useRegion, useRegions } from '../../../../src'; 3 | import { createWrapperComponent, waitFor } from '../../../utils'; 4 | 5 | describe('useRegions hook', () => { 6 | test('success', async () => { 7 | const regions = fixtures.list('region'); 8 | 9 | const { vm } = createWrapperComponent(() => useRegions()); 10 | 11 | expect(vm.data?.regions).toBeUndefined(); 12 | 13 | await waitFor(() => vm.isSuccess); 14 | 15 | expect(vm.data?.regions).toEqual(regions); 16 | }); 17 | }); 18 | 19 | describe('useRegion hook', () => { 20 | test('success', async () => { 21 | const region = fixtures.get('region'); 22 | 23 | const { vm } = createWrapperComponent(() => useRegion(region.id)); 24 | 25 | expect(vm.data?.region).toBeUndefined(); 26 | 27 | await waitFor(() => vm.isSuccess); 28 | 29 | expect(vm.data?.region).toEqual(region); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /packages/core/tests/composables/store/return-reasons/queries.test.ts: -------------------------------------------------------------------------------- 1 | import { fixtures } from '../../../../mocks/data'; 2 | import { createWrapperComponent, waitFor } from '../../../utils'; 3 | import { useReturnReason, useReturnReasons } from '../../../../src/'; 4 | 5 | describe('useReturnReasons hook', () => { 6 | test('returns a list of return reasons', async () => { 7 | const return_reasons = fixtures.list('return_reason'); 8 | 9 | const { vm } = createWrapperComponent(() => useReturnReasons()); 10 | 11 | await waitFor(() => vm.isSuccess); 12 | 13 | expect(vm.data?.response.status).toEqual(200); 14 | expect(vm.data?.return_reasons).toEqual(return_reasons); 15 | }); 16 | }); 17 | 18 | describe('useReturnReason hook', () => { 19 | test('returns a return reason', async () => { 20 | const return_reason = fixtures.get('return_reason'); 21 | 22 | const { vm } = createWrapperComponent(() => 23 | useReturnReason(return_reason.id) 24 | ); 25 | 26 | await waitFor(() => vm.isSuccess); 27 | 28 | expect(vm.data?.response.status).toEqual(200); 29 | expect(vm.data?.return_reason).toEqual(return_reason); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /packages/core/tests/composables/store/returns/mutations.test.ts: -------------------------------------------------------------------------------- 1 | import { useCreateReturn } from '../../../../src'; 2 | import { fixtures } from '../../../../mocks/data'; 3 | import { createWrapperComponent, waitFor } from '../../../utils'; 4 | 5 | describe('useCreateReturn hook', () => { 6 | test('creates a return', async () => { 7 | const ret = { 8 | order_id: 'order_38', 9 | items: [ 10 | { 11 | item_id: 'test-item', 12 | quantity: 1, 13 | }, 14 | ], 15 | }; 16 | 17 | const { vm } = createWrapperComponent(() => useCreateReturn()); 18 | 19 | vm.mutate(ret); 20 | 21 | await waitFor(() => vm.isSuccess); 22 | 23 | expect(vm.data?.response.status).toEqual(200); 24 | expect(vm.data?.return).toEqual( 25 | expect.objectContaining({ 26 | ...fixtures.get('return'), 27 | order_id: ret.order_id, 28 | }) 29 | ); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /packages/core/tests/composables/store/shipping-options/queries.test.ts: -------------------------------------------------------------------------------- 1 | import { rest } from 'msw'; 2 | import { server } from '../../../../mocks/server'; 3 | import { useShippingOptions, useCartShippingOptions } from '../../../../src/'; 4 | import { fixtures } from '../../../../mocks/data'; 5 | import { createWrapperComponent, waitFor } from '../../../utils'; 6 | 7 | describe('useShippingOptions hook', () => { 8 | test('returns a list of shipping options', async () => { 9 | const shippingOptions = fixtures.list('shipping_option', 5); 10 | 11 | const { vm } = createWrapperComponent(() => useShippingOptions()); 12 | 13 | await waitFor(() => vm.isSuccess); 14 | 15 | expect(vm.data?.response.status).toEqual(200); 16 | expect(vm.data?.shipping_options).toEqual(shippingOptions); 17 | }); 18 | 19 | test('when shipping options params are provided, then they should be sent as query params', async () => { 20 | const shippingOptions = fixtures.list('shipping_option'); 21 | 22 | server.use( 23 | rest.get('/store/shipping-options/', (req, res, ctx) => { 24 | const product_ids = req.url.searchParams.get('product_ids'); 25 | const is_return = req.url.searchParams.get('is_return'); 26 | const region_id = req.url.searchParams.get('region_id'); 27 | 28 | expect({ 29 | product_ids, 30 | is_return, 31 | region_id, 32 | }).toEqual({ 33 | product_ids: '1,2,3', 34 | is_return: 'false', 35 | region_id: 'test-region', 36 | }); 37 | 38 | return res( 39 | ctx.status(200), 40 | ctx.json({ 41 | shipping_options: fixtures.list('shipping_option'), 42 | }) 43 | ); 44 | }) 45 | ); 46 | 47 | const { vm } = createWrapperComponent(() => 48 | useShippingOptions({ 49 | product_ids: '1,2,3', 50 | is_return: 'false', 51 | region_id: 'test-region', 52 | }) 53 | ); 54 | 55 | await waitFor(() => vm.isSuccess); 56 | 57 | expect(vm.data?.response.status).toEqual(200); 58 | expect(vm.data?.shipping_options).toEqual(shippingOptions); 59 | }); 60 | }); 61 | 62 | describe('useCartShippingOptions hook', () => { 63 | test('success', async () => { 64 | const cartShippingOptions = fixtures.list('shipping_option'); 65 | 66 | const { vm } = createWrapperComponent(() => 67 | useCartShippingOptions('cart_test') 68 | ); 69 | 70 | await waitFor(() => vm.isSuccess); 71 | 72 | expect(vm.data?.response.status).toEqual(200); 73 | expect(vm.data?.shipping_options).toEqual(cartShippingOptions); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /packages/core/tests/composables/store/swaps/mutations.test.ts: -------------------------------------------------------------------------------- 1 | import { useCreateSwap } from '../../../../src/'; 2 | import { fixtures } from '../../../../mocks/data'; 3 | import { createWrapperComponent, waitFor } from '../../../utils'; 4 | 5 | describe('useCreateSwap hook', () => { 6 | test('creates a return', async () => { 7 | const swap = { 8 | order_id: 'order_test', 9 | additional_items: [ 10 | { 11 | variant_id: 'new-item', 12 | quantity: 1, 13 | }, 14 | ], 15 | return_items: [ 16 | { 17 | item_id: 'return-item', 18 | quantity: 1, 19 | }, 20 | ], 21 | }; 22 | 23 | const { vm } = createWrapperComponent(() => useCreateSwap()); 24 | 25 | vm.mutate(swap); 26 | 27 | await waitFor(() => vm.isSuccess); 28 | 29 | expect(vm.data?.response.status).toEqual(200); 30 | expect(vm.data?.swap).toEqual( 31 | expect.objectContaining({ 32 | ...fixtures.get('swap'), 33 | order_id: swap.order_id, 34 | }) 35 | ); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /packages/core/tests/composables/store/swaps/queries.test.ts: -------------------------------------------------------------------------------- 1 | import { useCartSwap } from '../../../../src/'; 2 | 3 | import { fixtures } from '../../../../mocks/data'; 4 | import { createWrapperComponent, waitFor } from '../../../utils'; 5 | 6 | describe('useCartSwap hook', () => { 7 | test('returns a swap', async () => { 8 | const swap = fixtures.get('swap'); 9 | 10 | const { vm } = createWrapperComponent(() => useCartSwap('cart_test')); 11 | 12 | await waitFor(() => vm.isSuccess); 13 | 14 | expect(vm.data?.response.status).toEqual(200); 15 | expect(vm.data?.swap).toEqual(swap); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /packages/core/tests/setup.ts: -------------------------------------------------------------------------------- 1 | import { afterAll, afterEach, beforeAll } from 'vitest'; 2 | import { server } from '../mocks/server'; 3 | 4 | // console.log('beforeAll', beforeAll); 5 | beforeAll(() => server.listen()); 6 | 7 | // // Close server after all tests 8 | afterAll(() => server.close()); 9 | 10 | // // Reset handlers after each test `important for test isolation` 11 | afterEach(() => server.resetHandlers()); 12 | -------------------------------------------------------------------------------- /packages/core/tests/utils.ts: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import { defineComponent, watch } from 'vue'; 3 | import { createMedusaVueClient } from '../src'; 4 | 5 | const createTestMedusaVueClient = () => { 6 | const client = createMedusaVueClient({ 7 | baseUrl: '/', 8 | queryClientProviderProps: {}, 9 | }); 10 | 11 | return client; 12 | }; 13 | 14 | export const createWrapperComponent = (dataFn: () => T) => { 15 | const client = createTestMedusaVueClient(); 16 | 17 | const wrapper = mount( 18 | defineComponent({ 19 | setup() { 20 | const data = dataFn(); 21 | 22 | return data; 23 | }, 24 | 25 | template: '
', 26 | }), 27 | { 28 | global: { 29 | plugins: [client], 30 | }, 31 | } 32 | ); 33 | 34 | return wrapper; 35 | }; 36 | 37 | export const waitFor = (condition: () => any) => { 38 | let manuallyResolve: (val?: unknown) => void; 39 | const promise = new Promise(resolve => (manuallyResolve = resolve)); 40 | 41 | watch( 42 | () => condition(), 43 | () => { 44 | if (condition()) { 45 | manuallyResolve(); 46 | } 47 | } 48 | ); 49 | 50 | return promise; 51 | }; 52 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src", "types", "node_modules/@types"], 3 | "compilerOptions": { 4 | "rootDir": "./src", 5 | "outDir": "./lib", 6 | "module": "esnext", 7 | "lib": ["dom", "esnext"], 8 | "importHelpers": true, 9 | "declaration": true, 10 | "sourceMap": true, 11 | "strict": true, 12 | "noImplicitReturns": true, 13 | "noFallthroughCasesInSwitch": true, 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true, 16 | "moduleResolution": "node", 17 | "esModuleInterop": true, 18 | "skipLibCheck": true, 19 | "forceConsistentCasingInFileNames": true, 20 | "noEmit": true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/core/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | import Vue from '@vitejs/plugin-vue'; 4 | 5 | export default defineConfig({ 6 | plugins: [Vue()], 7 | test: { 8 | globals: true, 9 | environment: 'jsdom', 10 | setupFiles: ['./tests/setup.ts'], 11 | root: '.', 12 | }, 13 | }); 14 | --------------------------------------------------------------------------------