From 72f883096936fcc30da7422519a466111ffe8b2e Mon Sep 17 00:00:00 2001 From: Coner Murphy Date: Tue, 28 Feb 2023 13:20:43 +0000 Subject: [PATCH 1/8] feat: started restructure of package --- packages/index.ts | 40 ++++++++++++++++++++++++++++- packages/pricing-table/src/index.js | 6 +---- types/additional.d.ts | 4 +-- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/packages/index.ts b/packages/index.ts index 22bf869..a3911fc 100644 --- a/packages/index.ts +++ b/packages/index.ts @@ -1 +1,39 @@ -export { SalablePricingTable } from './pricing-table/src'; +import { SalablePricingTable } from './pricing-table/src'; + +class Config { + protected _apiKey; + + constructor(config: { apiKey: string }) { + this._apiKey = config.apiKey; + } + + get apiKey() { + return this._apiKey; + } +} + +export class Salable { + protected config; + + constructor(config: { apiKey: string }) { + this.config = new Config(config); + } + + init() { + const requiredFields: { [k: string]: { [k: string]: string } } = { + apiKey: { + value: this.config.apiKey, + }, + }; + + Object.keys(requiredFields).forEach((key) => { + const field = requiredFields[key]; + + if (!field.value) throw Error(`Salable - Missing Property: ${key}`); + }); + } + + public PricingTale = new SalablePricingTable(); +} + +window.Salable = Salable; diff --git a/packages/pricing-table/src/index.js b/packages/pricing-table/src/index.js index 11d3b5a..2c36b58 100644 --- a/packages/pricing-table/src/index.js +++ b/packages/pricing-table/src/index.js @@ -1,5 +1 @@ -import { SalablePricingTable } from './salable.js'; - -window.SalablePricingTable = SalablePricingTable; - -export { SalablePricingTable }; +export { SalablePricingTable } from './salable.js'; diff --git a/types/additional.d.ts b/types/additional.d.ts index 347af8b..af5d983 100644 --- a/types/additional.d.ts +++ b/types/additional.d.ts @@ -1,8 +1,8 @@ -import { SalablePricingTable } from "@/packages"; +import { Salable } from "@/packages"; declare global { interface Window { - SalablePricingTable: typeof SalablePricingTable; + Salable: typeof Salable; } } From a16a267af9276b5a073f7742f1712bfca6359788 Mon Sep 17 00:00:00 2001 From: Joshua Okoro Date: Thu, 2 Mar 2023 11:25:06 -0600 Subject: [PATCH 2/8] chore: structure folder and files --- .eslintrc.json | 22 +- .prettierignore | 11 + .prettierrc | 8 + README.md | 74 ++-- docs/salable-logo.png | Bin 0 -> 67052 bytes .../example => examples}/404.html | 0 examples/available-plans.html | 35 ++ .../example => examples}/favicon.ico | Bin .../index.html => examples/pricing-table.html | 18 +- package.json | 1 + packages/index.ts | 39 --- packages/pricing-table/package.json | 14 - packages/pricing-table/src/index.js | 1 - rollup.config.mjs | 12 +- src/base.ts | 55 +++ src/constants.ts | 2 + src/globals.d.ts | 9 + src/index.ts | 8 + {packages => src}/pricing-table/README.md | 0 .../src => src/pricing-table}/css/main.css | 8 +- .../pricing-table}/css/themes/dark.css | 0 .../pricing-table}/css/themes/light.css | 0 src/pricing-table/index.js | 1 + .../pricing-table}/lottie/dots-left-blue.json | 126 +++---- .../lottie/dots-left-white.json | 132 ++++---- .../src => src/pricing-table}/salable.js | 318 +++++------------- src/utils/errors.ts | 5 + tsconfig.json | 13 +- types/additional.d.ts | 2 +- 29 files changed, 412 insertions(+), 502 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 docs/salable-logo.png rename {packages/pricing-table/example => examples}/404.html (100%) create mode 100644 examples/available-plans.html rename {packages/pricing-table/example => examples}/favicon.ico (100%) rename packages/pricing-table/example/index.html => examples/pricing-table.html (58%) delete mode 100644 packages/index.ts delete mode 100644 packages/pricing-table/package.json delete mode 100644 packages/pricing-table/src/index.js create mode 100644 src/base.ts create mode 100644 src/constants.ts create mode 100644 src/globals.d.ts create mode 100644 src/index.ts rename {packages => src}/pricing-table/README.md (100%) rename {packages/pricing-table/src => src/pricing-table}/css/main.css (99%) rename {packages/pricing-table/src => src/pricing-table}/css/themes/dark.css (100%) rename {packages/pricing-table/src => src/pricing-table}/css/themes/light.css (100%) create mode 100644 src/pricing-table/index.js rename {packages/pricing-table/src => src/pricing-table}/lottie/dots-left-blue.json (55%) rename {packages/pricing-table/src => src/pricing-table}/lottie/dots-left-white.json (52%) rename {packages/pricing-table/src => src/pricing-table}/salable.js (73%) create mode 100644 src/utils/errors.ts diff --git a/.eslintrc.json b/.eslintrc.json index 77872d9..15d133e 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,17 +1,7 @@ { "parser": "@typescript-eslint/parser", - "extends": [ - "prettier", - "plugin:jest/recommended", - "plugin:import/recommended" - ], - "plugins": [ - "@typescript-eslint", - "jest", - "prettier", - "unused-imports", - "import" - ], + "extends": ["prettier", "plugin:jest/recommended", "plugin:import/recommended"], + "plugins": ["@typescript-eslint", "jest", "prettier", "unused-imports", "import"], "overrides": [ { "files": ["*.ts", "*.tsx"], @@ -35,8 +25,12 @@ { "trailingComma": "es5", "singleQuote": true, - "printWidth": 80, - "endOfLine": "auto" + "printWidth": 120, + "endOfLine": "auto", + "semi": true, + "arrowParens": "always", + "bracketSpacing": false, + "tabWidth": 2 } ], "import/no-unresolved": [ diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..88b211b --- /dev/null +++ b/.prettierignore @@ -0,0 +1,11 @@ +coverage +node_modules +dist +build +coverage +.github +.idea +.vscode +.cache +.yalc +package-lock.json \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..393d6f0 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +{ + "singleQuote": true, + "semi": true, + "arrowParens": "always", + "printWidth": 120, + "bracketSpacing": false, + "tabWidth": 2 +} diff --git a/README.md b/README.md index dc67d83..b6bbf51 100644 --- a/README.md +++ b/README.md @@ -1,53 +1,51 @@ -# @salable/js-sdk +

+ + + +
+

-## Packages Included +
-- [@salable/pricing-table](https://github.com/Salable/js-sdk/tree/main/packages/pricing-table) +
-## Development +--- -### Initial Setup +## Overview -To set up the JS SDK on your local machine for development, perform the following steps. +Salable JS is a JavaScript library for building and managing a SaaS application's products, plans, subscriptions, and payment. -1. Clone the repo to your local machine -2. Run `npm i` at the root of the project +## Getting Started -_Note: You don't need to run `npm i` per package. Because this project uses NPM workspaces, you only ever need to run it at root._ +### Installation -### Adding Packages +You can include Salable JS in your project by importing the SalableJS npm module or loading SalableJS with a script tag -If you want to add a new package, perform the following steps. +### Install SalableJS with npm -1. Create a new folder inside the `packages` directory named as the name of the package. (e.g. pricing-table) -2. Inside the new package folder, create `package.json` and `README.md` files. - - Use the template for `package.json` below, replacing information where required. -3. Create a `src` folder in the root of your new package folder and inside it put a `index.ts` or `index.js` file depending on if you're using TS or not. - - From the `index.{js|ts}` file, export your package's files. This file is the entry point for Rollup to build from. -4. Update `./packages/index.ts` to include your new package index file to ensure it is included in the output build. - - Export your new package from `./packages/index.ts` with a name based on your package name. -5. Update this `README.md` file to include your new package in the list of packages at the top fo this file. -6. To install/uninstall NPM packages to your new package, use the following commands and replace information where required. _NOTE: For your package name, use the directory name inside `./packages`_ - - **Install:** `npm install -w ` - - **Uninstall:** `npm uninstall -w ` +```bash +npm instal @salable/salable-js +``` + +Once the installation is done, import the package and create and instance. + +```typescript +import {Salable} from '@salable/salable-js'; +const salable = new Salable('{your-api-key-here}'); +``` + +### Install SalableJS with script tag -#### `package.json` example +```html + ``` -#### Example +## License + +This project is licensed under the **MIT license**. -For an example package, see the `pricing-table` package/directory inside `./packages`. +See [LICENSE](https://github.com/Salable/js-sdk/tree/main/packages/salable-js/LICENSE) for more information. diff --git a/docs/salable-logo.png b/docs/salable-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..3413ad546003ea3272a94d17050557f17ec502ab GIT binary patch literal 67052 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 literal 0 HcmV?d00001 diff --git a/packages/pricing-table/example/404.html b/examples/404.html similarity index 100% rename from packages/pricing-table/example/404.html rename to examples/404.html diff --git a/examples/available-plans.html b/examples/available-plans.html new file mode 100644 index 0000000..1159083 --- /dev/null +++ b/examples/available-plans.html @@ -0,0 +1,35 @@ + + + + Salable pricing table library + + + +

Salable JS

+ +
+

Available Plans

+
+
+ +
+ + + diff --git a/packages/pricing-table/example/favicon.ico b/examples/favicon.ico similarity index 100% rename from packages/pricing-table/example/favicon.ico rename to examples/favicon.ico diff --git a/packages/pricing-table/example/index.html b/examples/pricing-table.html similarity index 58% rename from packages/pricing-table/example/index.html rename to examples/pricing-table.html index 1a5ef7e..29fcbc7 100644 --- a/packages/pricing-table/example/index.html +++ b/examples/pricing-table.html @@ -15,24 +15,24 @@

Salable pricing table js library

diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..e2ffef9 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,31 @@ +/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ +export default { + // rootDir: ".", + preset: 'ts-jest', + testEnvironment: 'node', + collectCoverageFrom: [ + '**/*.{ts,js}', + '!**/node_modules/**', + '!**/dist/**', + '!**/*.test.[jt]s?(x)', + '!**/index.[jt]s?(x)', + '!**/lib/**', + ], + transformIgnorePatterns: ['/node_modules/'], + coverageThreshold: { + global: { + branches: 0, + functions: 0, + lines: 0, + statements: 0, + }, + }, + transform: { + '^.+\\.ts?$': 'ts-jest', + }, + transformIgnorePatterns: ['node_modules/(?!(utilities))'], + moduleDirectories: ['node_modules', ''], + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.ts$': '$1', + }, +}; diff --git a/package-lock.json b/package-lock.json index b186a58..17ffcdb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,9 +19,10 @@ "@rollup/plugin-json": "^6.0.0", "@rollup/plugin-node-resolve": "^15.0.1", "@rollup/plugin-replace": "^5.0.1", + "@salable/node-sdk": "file:.yalc/@salable/node-sdk", "@semantic-release/changelog": "^6.0.1", "@semantic-release/git": "^10.0.1", - "@types/jest": "^29.0.3", + "@types/jest": "^29.4.0", "@types/node": "^18.11.9", "@typescript-eslint/eslint-plugin": "^5.37.0", "commitizen": "^4.2.5", @@ -35,6 +36,7 @@ "eslint-plugin-unused-imports": "^2.0.0", "husky": "^8.0.0", "jest": "^29.0.3", + "jest-fetch-mock": "^3.0.3", "lint-staged": "^13.0.3", "prettier": "^2.7.1", "rollup": "^3.2.5", @@ -48,6 +50,11 @@ "typescript": "^4.8.3" } }, + ".yalc/@salable/node-sdk": { + "version": "1.6.1", + "dev": true, + "license": "MIT" + }, "cdn": { "name": "@salable/cdn", "version": "1.0.3", @@ -1939,6 +1946,10 @@ "resolved": "cdn", "link": true }, + "node_modules/@salable/node-sdk": { + "resolved": ".yalc/@salable/node-sdk", + "link": true + }, "node_modules/@semantic-release/changelog": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@semantic-release/changelog/-/changelog-6.0.2.tgz", @@ -2261,9 +2272,9 @@ } }, "node_modules/@types/jest": { - "version": "29.2.5", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.5.tgz", - "integrity": "sha512-H2cSxkKgVmqNHXP7TC2L/WUorrZu8ZigyRywfVzv6EyBlxj39n4C00hjXYQWsbwqgElaj/CiAeSRmk5GoaKTgw==", + "version": "29.4.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.4.0.tgz", + "integrity": "sha512-VaywcGQ9tPorCX/Jkkni7RWGFfI11whqzs8dvxF41P17Z+z872thvEvlIbznjPJ02kl1HMX3LmLOonsj2n7HeQ==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -5586,6 +5597,15 @@ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" }, + "node_modules/cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "dev": true, + "dependencies": { + "node-fetch": "2.6.7" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -8436,6 +8456,16 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-fetch-mock": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz", + "integrity": "sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==", + "dev": true, + "dependencies": { + "cross-fetch": "^3.0.4", + "promise-polyfill": "^8.1.3" + } + }, "node_modules/jest-get-type": { "version": "29.2.0", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.2.0.tgz", @@ -12976,6 +13006,12 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, + "node_modules/promise-polyfill": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.3.0.tgz", + "integrity": "sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==", + "dev": true + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -16460,6 +16496,9 @@ } } }, + "@salable/node-sdk": { + "version": "file:.yalc/@salable/node-sdk" + }, "@semantic-release/changelog": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@semantic-release/changelog/-/changelog-6.0.2.tgz", @@ -16739,9 +16778,9 @@ } }, "@types/jest": { - "version": "29.2.5", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.5.tgz", - "integrity": "sha512-H2cSxkKgVmqNHXP7TC2L/WUorrZu8ZigyRywfVzv6EyBlxj39n4C00hjXYQWsbwqgElaj/CiAeSRmk5GoaKTgw==", + "version": "29.4.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.4.0.tgz", + "integrity": "sha512-VaywcGQ9tPorCX/Jkkni7RWGFfI11whqzs8dvxF41P17Z+z872thvEvlIbznjPJ02kl1HMX3LmLOonsj2n7HeQ==", "dev": true, "requires": { "expect": "^29.0.0", @@ -19641,6 +19680,15 @@ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" }, + "cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "dev": true, + "requires": { + "node-fetch": "2.6.7" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -21737,6 +21785,16 @@ "jest-util": "^29.3.1" } }, + "jest-fetch-mock": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz", + "integrity": "sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==", + "dev": true, + "requires": { + "cross-fetch": "^3.0.4", + "promise-polyfill": "^8.1.3" + } + }, "jest-get-type": { "version": "29.2.0", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.2.0.tgz", @@ -24991,6 +25049,12 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, + "promise-polyfill": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.3.0.tgz", + "integrity": "sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==", + "dev": true + }, "prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", diff --git a/package.json b/package.json index be1ba88..7c2899d 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "scripts": { "prepare": "husky install", "commit": "cz", + "test": "jest", "build": "rollup -c", "build:cdn": "cd cdn && npm run build && cd ..", "preversion": "npm run build", @@ -34,7 +35,7 @@ "@rollup/plugin-replace": "^5.0.1", "@semantic-release/changelog": "^6.0.1", "@semantic-release/git": "^10.0.1", - "@types/jest": "^29.0.3", + "@types/jest": "^29.4.0", "@types/node": "^18.11.9", "@typescript-eslint/eslint-plugin": "^5.37.0", "commitizen": "^4.2.5", @@ -48,6 +49,7 @@ "eslint-plugin-unused-imports": "^2.0.0", "husky": "^8.0.0", "jest": "^29.0.3", + "jest-fetch-mock": "^3.0.3", "lint-staged": "^13.0.3", "prettier": "^2.7.1", "rollup": "^3.2.5", diff --git a/src/base.ts b/src/base.ts index 90f1212..c2c2434 100644 --- a/src/base.ts +++ b/src/base.ts @@ -1,4 +1,3 @@ -import 'isomorphic-fetch'; import {SALABLE_BASE_URL} from './constants'; import {MissingPropertyError} from './utils/errors'; @@ -6,7 +5,7 @@ export class BaseResource { protected _apiKey; protected _request; protected _createElWithClass; - protected _apiDomain; + protected _apiDomain: string; constructor(apiKey: string) { this._apiKey = (apiKey || '').trim(); diff --git a/src/constants.ts b/src/constants.ts index 1f3b7d8..1b0d2f8 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,2 +1,3 @@ -export const SALABLE_BASE_URL = - process.env.SALABLE_BASE_URL || 'https://gsn58u22o0.execute-api.us-east-1.amazonaws.com/dev'; +export const SALABLE_BASE_URL = 'https://gsn58u22o0.execute-api.us-east-1.amazonaws.com/dev'; +// export const SALABLE_BASE_CDN = `https://cdn.salable.org`; +export const SALABLE_BASE_CDN = `http://127.0.0.1:5500`; diff --git a/src/index.ts b/src/index.ts index 307b53c..6d99a18 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,29 @@ -import {BaseResource} from './base'; -import {SalablePricingTable} from './pricing-table'; +import {ChangePlan, IChangePlan, ChangePlanCallbackProp} from './resources/change-plan'; +import {SalableAvailablePlans} from './resources/available-plans'; +import {SalableBase} from './resources/base'; -export class Salable extends BaseResource { - public PricingTable = new SalablePricingTable(this._apiKey); +interface IAvailablePlans { + apiKey: string; + granteeID: string; + memberID: string; + productID: string; + subscriptionID?: string; +} + +export class Salable extends SalableBase { + _changePlan = new ChangePlan(this._apiKey); + + public availablePlans = (params: Omit) => { + return new SalableAvailablePlans({...params, apiKey: this._apiKey}); + }; + + public changePlanSync = (params: IChangePlan) => { + return this._changePlan.upgradeOrDowngradePlanSync(params); + }; + + public changePlanAsync = (params: IChangePlan, callback?: ChangePlanCallbackProp) => { + return this._changePlan.upgradeOrDowngradePlanAsync(params, callback); + }; } window.Salable = Salable; diff --git a/src/resources/available-plans.js b/src/resources/available-plans.js new file mode 100644 index 0000000..a4227a8 --- /dev/null +++ b/src/resources/available-plans.js @@ -0,0 +1,637 @@ +import {MissingPropertyError} from '../utils/errors'; +import {SalableBase} from './base'; +import {ChangePlan} from './change-plan'; + +export class SalableAvailablePlans extends SalableBase { + _granteeID; + _memberID; + _productID; + _subscriptionID; + _element; + _callback; + + constructor(params) { + super(params.apiKey); + this._granteeID = params.granteeID; + this._memberID = params.memberID; + this._productID = params.productID; + this._subscriptionID = params.subscriptionID; + this._element = null; + const envConfig = { + availablePlansTableNode: this._element, + }; + const checkoutConfig = { + member: params.memberID, + customer: { + email: params.granteeID, + }, + }; + this.envConfig = envConfig; + this.checkoutConfig = checkoutConfig; + this.initialisers = new Initialisers(this._apiKey, envConfig, checkoutConfig); + } + + mount(element, callback) { + (async () => { + if (!element) MissingPropertyError('element'); + + const classPrefix = 'salable'; + const availablePlansTable = element; + const bodyMovinScript = document.createElement('script'); + bodyMovinScript.src = 'https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.7.7/lottie.min.js'; + bodyMovinScript.integrity = + 'sha512-HDCfX3BneBQMfloBfluMQe6yio+OfXnbKAbI0SnfcZ4YfZL670nc52Aue1bBhgXa+QdWsBdhMVR2hYROljf+Fg=='; + bodyMovinScript.crossOrigin = 'anonymous'; + bodyMovinScript.defer = true; + bodyMovinScript.id = 'SalableLottieCdn'; + document.body.appendChild(bodyMovinScript); + + this.initialisers.createCssStyleSheetLink( + `${this.initialisers.getCdnDomain()}/latest/css/main.css`, + 'SalableCssMain' + ); + this.initialisers.createCssStyleSheetLink( + `${this.initialisers.getCdnDomain()}/latest/css/themes/${this.envConfig.theme ?? 'light'}.css`, + `SalableCss${ + this.envConfig.theme ? this.envConfig.theme[0].toUpperCase() + this.envConfig.theme.substr(1) : 'Light' + }` + ); + + const availablePlansTableContainerEl = this.initialisers.createElWithClass( + 'div', + `${classPrefix}-pricing-table-container` + ); + availablePlansTable.appendChild(availablePlansTableContainerEl); + + availablePlansTableContainerEl.setAttribute('data-interval', 'month'); + + if (this.envConfig.globalPlanOptions?.cta?.visibility === 'hidden') + availablePlansTableContainerEl.classList.add('salable-global-cta-hidden'); + if (this.envConfig.state) + availablePlansTableContainerEl.classList.add(`salable-pricing-table-state-${this.envConfig.state}`); + + const loadingEl = this.initialisers.createElWithClass('div', `${classPrefix}-loading`); + availablePlansTableContainerEl.appendChild(loadingEl); + + bodyMovinScript.addEventListener('load', () => { + this.initialisers.createInlineScript( + this.initialisers.createLottieAnimation( + `document.getElementsByClassName('salable-loading')[0]`, + `${this.initialisers.getCdnDomain()}/latest/lottie/dots-left-${ + this.envConfig.theme === 'dark' ? 'white' : 'blue' + }.json` + ), + availablePlansTableContainerEl, + 'SalableLottiePricingTableLoadingAnimation' + ); + }); + const [productResult, subscriptionResult] = await Promise.allSettled([ + this._request( + encodeURI( + `/products/${this._productID}?expand=[plans, currencies.currency, organisationPaymentIntegration, plans.currencies.currency, plans.features.feature, plans.features.enumValue]` + ) + ), + this._subscriptionID ? this._request(`/subscriptions/${this._subscriptionID}`) : null, + ]) + .then((res) => res) + .catch((error) => error); + + const handleError = (err) => { + throw new Error(`SalableJS - ${err}`); + }; + + if (productResult.status === 'rejected') { + const error = productResult.reason; + handleError(error); + } + + // process subscription + if (subscriptionResult.status === 'rejected') { + const error = subscriptionResult.reason; + handleError(error); + } + + if (productResult.status !== 'rejected') { + const productData = productResult.value; + const subscriptionData = subscriptionResult.value; + const defaultCurrency = productData?.currencies?.find((c) => c.defaultCurrency); + + const plans = productData.plans + .filter((p) => p.active && p.planType !== 'bespoke' && p.status === 'ACTIVE') + .sort((a, b) => { + if (a.pricingType === 'free' && a.planType !== 'Coming soon') return -1; + if (a.planType === 'Coming soon') return 1; + const priceA = a.currencies.find((c) => c.currencyUuid === defaultCurrency.currencyUuid)?.price || 0; + const priceB = b.currencies.find((c) => c.currencyUuid === defaultCurrency.currencyUuid)?.price || 0; + return priceA - priceB; + }); + + const currentPlanIndex = plans.findIndex((plan) => plan.uuid === subscriptionData.planUuid) ?? -1; + + if (plans.filter((p) => p.interval === 'month').length) { + this.initialisers.createPlansPerInterval({ + interval: 'month', + plans: plans.filter( + (p) => p.interval === 'month' || (p.pricingType === 'free' && p.planType === 'Standard') + ), + availablePlansTableContainerEl, + classPrefix, + envConfig: this.envConfig, + checkoutConfig: this.checkoutConfig, + plansContainerEl: this.initialisers.createElWithClass( + 'div', + `${classPrefix}-plans-container ${classPrefix}-plans-container-month` + ), + defaultCurrency, + subscriptionData, + currentPlanIndex, + callback, + }); + } + + if (plans.filter((p) => p.interval === 'year' && p.pricingType === 'paid').length) { + this.initialisers.createPlansPerInterval({ + interval: 'year', + plans: plans.filter( + (p) => p.interval === 'year' || (p.pricingType === 'free' && p.planType === 'Standard') + ), + availablePlansTableContainerEl, + classPrefix, + envConfig: this.envConfig, + checkoutConfig: this.checkoutConfig, + plansContainerEl: this.initialisers.createElWithClass( + 'div', + `${classPrefix}-plans-container ${classPrefix}-plans-container-year` + ), + defaultCurrency, + subscriptionData, + currentPlanIndex, + plans, + callback, + }); + } + + if ( + plans.filter((p) => p.interval === 'year' && p.pricingType === 'paid').length && + plans.filter((p) => p.interval === 'month').length + ) { + const monthlyEl = document.querySelector(`.${classPrefix}-plans-container-month`); + const yearEl = document.querySelector(`.${classPrefix}-plans-container-year`); + const plansIntervalToggleEl = this.initialisers.createAvailablePlansTableIntervalToggle(classPrefix); + document + .querySelectorAll(`.${classPrefix}-plans-container`)[0] + .parentNode.insertBefore( + plansIntervalToggleEl, + document.querySelectorAll(`.${classPrefix}-plans-container`)[0] + ); + yearEl.style.display = 'none'; + plansIntervalToggleEl.addEventListener('click', () => { + if (yearEl.style.display === 'none') { + availablePlansTableContainerEl.setAttribute('data-interval', 'year'); + monthlyEl.style.display = 'none'; + yearEl.style.display = 'flex'; + document + .querySelector(`.${classPrefix}-plans-interval-toggle-label-year`) + .classList.add(`${classPrefix}-plans-interval-toggle-active`); + document + .querySelector(`.${classPrefix}-plans-interval-toggle-label-month`) + .classList.remove(`${classPrefix}-plans-interval-toggle-active`); + } else { + availablePlansTableContainerEl.setAttribute('data-interval', 'month'); + monthlyEl.style.display = 'flex'; + yearEl.style.display = 'none'; + document + .querySelector(`.${classPrefix}-plans-interval-toggle-label-year`) + .classList.remove(`${classPrefix}-plans-interval-toggle-active`); + document + .querySelector(`.${classPrefix}-plans-interval-toggle-label-month`) + .classList.add(`${classPrefix}-plans-interval-toggle-active`); + } + }); + } + + loadingEl.style.display = 'none'; + + if (this.envConfig.stylingOptions && Object.keys(this.envConfig.stylingOptions).length) { + const stylingOptionsKeyValues = Object.entries(this.envConfig.stylingOptions); + const root = document.querySelector(':root'); + + if (stylingOptionsKeyValues) { + for (const option of stylingOptionsKeyValues) { + root.style.setProperty(`--${option[0].split('_').join('-')}`, option[1]); + } + } + } + } + })(); + } +} + +class Initialisers { + envConfig; + checkoutConfig; + _apiKey; + + constructor(apiKey, envConfig, checkoutConfig) { + this._apiKey = apiKey; + this.envConfig = envConfig; + this.checkoutConfig = checkoutConfig; + } + + createElWithClass(type, className) { + const el = document.createElement(type); + el.className = className; + return el; + } + + createAvailablePlansTableIntervalToggle(classPrefix) { + const plansIntervalToggleEl = this.createElWithClass('button', `${classPrefix}-plans-interval-toggle`); + const plansIntervalToggleMonthLabel = this.createElWithClass( + 'span', + `${classPrefix}-plans-interval-toggle-label ${classPrefix}-plans-interval-toggle-label-month ${classPrefix}-plans-interval-toggle-active` + ); + const plansIntervalToggleYearLabel = this.createElWithClass( + 'span', + `${classPrefix}-plans-interval-toggle-label ${classPrefix}-plans-interval-toggle-label-year` + ); + plansIntervalToggleMonthLabel.innerText = 'Monthly Plans'; + plansIntervalToggleYearLabel.innerText = 'Yearly Plans'; + plansIntervalToggleEl.appendChild(plansIntervalToggleMonthLabel); + plansIntervalToggleEl.appendChild(plansIntervalToggleYearLabel); + + return plansIntervalToggleEl; + } + + createPlanPrice(classPrefix, plan, planEl, defaultCurrency) { + const planPriceEl = this.createElWithClass('div', `${classPrefix}-plan-price`); + if (plan.pricingType === 'free') { + planPriceEl.innerText = 'Free'; + } else { + if (plan.currencies?.length) { + const matchedCurrency = plan.currencies.find((c) => { + if (this.envConfig.currency) return c.currency.shortName === this.envConfig.currency; + return c.currency.uuid === defaultCurrency.currencyUuid; + }); + if (this.envConfig.currency && !matchedCurrency) + throw Error('Salable pricing table - currency provided does not exist on product'); + if (matchedCurrency) { + const price = (matchedCurrency.price / 100).toFixed(2); + planPriceEl.innerText = `${matchedCurrency.currency.symbol}${ + price.toString().includes('.00') ? price.replace('.00', '') : price + }`; + } + const planPriceIntervalEl = this.createElWithClass('span', `${classPrefix}-plan-price-interval`); + planPriceIntervalEl.innerText = `per ${plan.licenseType !== 'metered' ? plan.interval : 'unit'}`; + planPriceEl.appendChild(planPriceIntervalEl); + } + } + if (plan.planType === 'Coming soon') { + planPriceEl.innerText = 'Coming soon'; + } + planEl.appendChild(planPriceEl); + } + + createPlanHeading(classPrefix, plan) { + const planHeadingEl = this.createElWithClass('h3', `${classPrefix}-plan-heading`); + planHeadingEl.innerText = plan.displayName; + return planHeadingEl; + } + + createFeatureIcon(classPrefix, feature) { + return feature.value === 'false' ? '✗' : '✔'; + } + + createFeatureLabel(classPrefix) { + const featureLabelEl = this.createElWithClass('span', `${classPrefix}-feature-list-item-label`); + return featureLabelEl; + } + + createFeatureValue(classPrefix) { + const featureValueEl = this.createElWithClass('span', `${classPrefix}-feature-list-item-value`); + return featureValueEl; + } + + createPlansFeaturesList(classPrefix, plan) { + const planFeaturesEl = this.createElWithClass('ul', `${classPrefix}-feature-list`); + for (const feature of plan.features.filter((p) => p.feature.visibility === 'public')) { + const featureEl = this.createElWithClass('li', `${classPrefix}-feature-list-item`); + const featureLabelEl = this.createFeatureLabel(classPrefix); + featureLabelEl.innerText = feature.feature.displayName; + featureEl.appendChild(featureLabelEl); + const getValueText = (value) => { + switch (value.toString()) { + case '-1': + return 'Unlimited'; + case 'true': + return this.createFeatureIcon(classPrefix, feature); + case 'false': + return this.createFeatureIcon(classPrefix, feature); + default: + return feature?.feature.valueType === 'enum' && feature.enumValue.name ? feature.enumValue.name : value; + } + }; + const featureValueEl = this.createFeatureValue(classPrefix); + featureValueEl.innerHTML = getValueText(feature.value); + featureEl.appendChild(featureValueEl); + planFeaturesEl.appendChild(featureEl); + } + return planFeaturesEl; + } + + createPlanCta({ + classPrefix, + envConfig, + plan, + planIndex, + buttonTextDefaults, + availablePlansTableContainerEl, + subscriptionData, + currentPlanIndex, + plans, + callback, + }) { + const planCtaEl = this.createElWithClass( + 'button', + `${classPrefix}-plan-button${plan.planType === 'Coming soon' ? ' salable-plan-button-coming-soon' : ''}` + ); + const planCtaText = (plan, envConfig, buttonTextDefaults) => { + switch (true) { + case envConfig.individualPlanOptions?.[plan?.uuid]?.cta?.text !== undefined: + return envConfig.individualPlanOptions[plan.uuid].cta.text; + case envConfig.globalPlanOptions?.cta?.text?.[plan.planType.toLowerCase()] !== undefined: + return envConfig.globalPlanOptions.cta.text[plan.planType.toLowerCase()]; + case buttonTextDefaults?.[plan?.planType] !== undefined: + if (plan?.planType === 'Standard') return buttonTextDefaults?.[plan?.planType]?.[plan?.pricingType]; + return buttonTextDefaults?.[plan.planType]; + default: + return 'Upgrade'; + } + }; + + const planCtaElText = planCtaText(plan, envConfig, buttonTextDefaults); + + const planCtaElId = `button-${planIndex}-cta`; + const planCtaElInnerSpan = this.createElWithClass('span', 'salable-plan-button-span'); + planCtaEl.appendChild(planCtaElInnerSpan); + planCtaEl.classList.add('salable-plan-button-free'); + planCtaEl.id = planCtaElId; + + if (subscriptionData?.planUuid === plan.uuid) { + planCtaEl.setAttribute('disabled', 'true'); + planCtaEl.classList.add('salable-disabled'); + planCtaEl.classList.add('salable-plan-button-disabled'); + planCtaElInnerSpan.innerText = 'Current Plan'; + } else { + planCtaElInnerSpan.innerText = planIndex > currentPlanIndex ? planCtaElText : 'Downgrade'; + } + + planCtaEl.id = planCtaElId; + + // Execute function to upgrade or downgrade user's plan + planCtaEl.addEventListener('click', async (event) => { + event.preventDefault(); + + // Get Salable Plan buttons from DOM and disable them + const planCTAs = Array.from(document.querySelectorAll('.salable-plan-button')); + let buttonIndex = 0; + for (const cta of planCTAs) { + if (buttonIndex !== planIndex) { + cta.classList.add('salable-plan-button-disabled'); + } + cta.classList.add('salable-disabled'); + cta.setAttribute('disabled', 'disabled'); + buttonIndex += 1; + } + + const currentButtonTextHolder = planCtaElInnerSpan.innerText; + + // Add loading animation + planCtaElInnerSpan.innerText = ''; + const lottie = this.createLottieAnimation( + `document.querySelector('#${planCtaElId} .salable-plan-button-span')`, + `${this.getCdnDomain()}/latest/lottie/dots-left-white.json` + ); + this.createInlineScript(lottie, availablePlansTableContainerEl, `SalableLottie${planCtaElId}LoadingAnimation`); + + // Make an API call to upgrade or downgrade user's plan + try { + const changePlan = new ChangePlan(this._apiKey); + const {message} = await changePlan.upgradeOrDowngradePlanSync({ + planID: plan.uuid, + subscriptionID: subscriptionData.uuid, + }); + if (callback) { + callback({message}, undefined); + } + } catch (error) { + planCtaElInnerSpan.innerText = currentButtonTextHolder; + for (const cta of planCTAs.filter((b) => b.id !== `button-${currentPlanIndex}-cta`)) { + cta.classList.remove('salable-plan-button-disabled'); + cta.classList.remove('salable-disabled'); + cta.removeAttribute('disabled', true); + } + if (callback) { + callback(undefined, {message: error.message}); + } + return; + } + + planCtaEl.removeAttribute('disabled'); + + const newCurrentPlanIndex = plans.findIndex((DPlan) => DPlan.uuid === plan.uuid) ?? -1; + + planCtaElInnerSpan.innerText = + planIndex > newCurrentPlanIndex && planIndex !== newCurrentPlanIndex ? planCtaElText : 'Downgrade'; + buttonIndex = 0; + for (const cta of planCTAs) { + cta.classList.remove('salable-plan-button-disabled'); + cta.classList.remove('salable-disabled'); + cta.removeAttribute('disabled', true); + + const buttonHTML = cta.getElementsByClassName(`${classPrefix}-plan-button-span`); + buttonHTML[0].innerHTML = + buttonIndex > newCurrentPlanIndex && buttonIndex !== newCurrentPlanIndex ? planCtaElText : 'Downgrade'; + + if (buttonIndex === newCurrentPlanIndex) { + buttonHTML[0].innerHTML = 'Current Plan'; + cta.classList.add('salable-plan-button-disabled'); + cta.classList.add('salable-disabled'); + cta.setAttribute('disabled', true); + } + buttonIndex += 1; + } + }); + return planCtaEl; + } + + createPlan(classPrefix) { + const planEl = this.createElWithClass('div', `${classPrefix}-plan`); + return planEl; + } + + createPlansPerInterval({ + interval, + plans, + availablePlansTableContainerEl, + classPrefix, + envConfig, + plansContainerEl, + defaultCurrency, + subscriptionData, + currentPlanIndex, + callback, + }) { + const buttonTextDefaults = { + Standard: { + free: 'Create license', + paid: 'Upgrade', + }, + 'Coming soon': 'Contact us', + enterprise: 'Contact us', + }; + + let planIndex = 0; + for (const plan of plans) { + const planEl = this.createPlan(classPrefix); + + const planHeadingEl = this.createPlanHeading(classPrefix, plan, planEl); + planEl.appendChild(planHeadingEl); + this.createPlanPrice(classPrefix, plan, planEl, defaultCurrency); + + const planFeaturesEl = this.createPlansFeaturesList(classPrefix, plan); + planEl.appendChild(planFeaturesEl); + + if (envConfig?.individualPlanOptions?.[plan?.uuid]?.cta?.visibility !== 'hidden') { + if (envConfig?.globalPlanOptions?.cta?.visibility !== 'hidden') { + const planCtaEl = this.createPlanCta({ + classPrefix, + envConfig, + plan, + planIndex, + buttonTextDefaults, + availablePlansTableContainerEl, + interval, + subscriptionData, + currentPlanIndex, + plans, + callback, + }); + if (this.envConfig.state === 'preview') { + if (plan.pricingType === 'free' && plan.planType !== 'Coming soon') { + this.createTooltip( + planCtaEl, + planEl, + 'Free licenses cannot be created when the pricing table is in preview state', + `${planCtaEl.id}Tooltip` + ); + } else { + planEl.appendChild(planCtaEl); + } + } else { + planEl.appendChild(planCtaEl); + } + } + } + + // Add Plan to pricing table + plansContainerEl.appendChild(planEl); + availablePlansTableContainerEl.appendChild(plansContainerEl); + planIndex++; + } + } + + createCssStyleSheetLink(link, id) { + const head = document.getElementsByTagName('head')[0]; + const linkStylesheet = document.createElement('link'); + linkStylesheet.setAttribute('href', link); + linkStylesheet.setAttribute('rel', 'stylesheet'); + if (id) linkStylesheet.id = id; + head.appendChild(linkStylesheet); + } + + createInlineScript(script, sibling, id) { + const inlineScript = document.createElement('script'); + inlineScript.textContent = script; + if (id) inlineScript.id = id; + sibling.insertBefore(inlineScript, null); + } + + createLottieAnimation(element, lottieFilePath) { + return ` + bodymovin.loadAnimation({ + container: ${element}, + path: '${lottieFilePath}', + renderer: 'svg', + loop: true, + autoplay: true, + }); + `; + } + + removeLottieAnimation(element, lottieFilePath) { + return ` + bodymovin.destroy({ + container: ${element}, + path: '${lottieFilePath}', + renderer: 'svg', + loop: true, + autoplay: false, + }); + `; + } + + queryParametersFactory(queryParams) { + let paramsStr = ''; + + const allowedQueryParams = [ + 'customerCountry', + 'customerEmail', + 'customerPostcode', + 'member', + 'couponCode', + 'marketingConsent', + 'vatCity', + 'vatCompanyName', + 'vatCountry', + 'vatNumber', + 'vatPostcode', + 'vatState', + 'vatStreet', + ]; + + for (const key of Object.keys(queryParams)) { + if (allowedQueryParams.includes(key)) { + if (!queryParams[key]) break; + switch (key) { + case 'marketingConsent': + paramsStr += `&${key}=${queryParams[key] ? '1' : '0'}`; + break; + default: + paramsStr += `&${key}=${queryParams[key]}`; + break; + } + } + } + + return paramsStr; + } + + createTooltip(el, elParent, tooltipText, id) { + const toolTipElHolder = this.createElWithClass('div', 'salable-tooltip-holder'); + const toolTipEl = this.createElWithClass('span', 'salable-tooltip'); + toolTipEl.innerText = tooltipText; + toolTipEl.id = id; + toolTipEl.setAttribute('role', 'tooltip'); + toolTipElHolder.appendChild(toolTipEl); + el.setAttribute('aria-describedby', id); + toolTipElHolder.appendChild(el); + elParent.appendChild(toolTipElHolder); + + el.addEventListener('mouseover', () => toolTipEl.classList.add('salable-tooltip-visible')); + el.addEventListener('mouseleave', () => toolTipEl.classList.remove('salable-tooltip-visible')); + } + + getCdnDomain() { + return `https://cdn.salable.${this.envConfig.environment === 'stg' ? 'org' : 'app'}`; + } +} diff --git a/src/resources/base.ts b/src/resources/base.ts new file mode 100644 index 0000000..8e71e4c --- /dev/null +++ b/src/resources/base.ts @@ -0,0 +1,58 @@ +import {SALABLE_BASE_URL, SALABLE_BASE_CDN} from '../constants'; +import {MissingPropertyError} from '../utils/errors'; + +export class SalableBase { + protected _apiKey: string; + // protected _request; + // protected _handlePromiseAllResults; + // protected _createElWithClass; + protected _apiDomain: string; + protected _cdnDomain: string; + + constructor(apiKey: string) { + this._apiKey = (apiKey || '').trim(); + this._apiDomain = SALABLE_BASE_URL; + this._cdnDomain = SALABLE_BASE_CDN; + + if (new.target === SalableBase) { + throw new Error('You cannot instantiate an abstract class!'); + } + + if (!apiKey) MissingPropertyError('apiKey'); + } + + protected _request = async (endpoint: string, options?: RequestInit): Promise => { + const url = `${this._apiDomain}${endpoint}`; + const headers = { + 'Content-Type': 'application/json', + 'x-api-key': this._apiKey, + }; + + const config = { + ...options, + headers, + }; + + return fetch(url, config).then(async (response) => { + if (response.ok) { + return response.json() as Promise; + } + const error = (await response.json()) as {error: string}; + throw new Error(error?.error ?? response.statusText); + }); + }; + + /** + * Create a HTML element in the document along with the class + * + * @export createElWithClass + * @param {string} type + * @param {string} className + * @return {HTMLElement} + */ + protected _createElWithClass = (type: string, className?: string) => { + const el = document.createElement(type); + el.className = className || ''; + return el; + }; +} diff --git a/src/resources/change-plan.ts b/src/resources/change-plan.ts new file mode 100644 index 0000000..51ade21 --- /dev/null +++ b/src/resources/change-plan.ts @@ -0,0 +1,66 @@ +import {MissingPropertyError} from '../utils/errors'; +import {SalableBase} from './base'; + +export interface IChangePlan { + planID: string; + subscriptionID: string; +} + +export type ChangePlanCallbackProp = ( + success?: { + message: string; + }, + error?: string +) => void; + +export class ChangePlan extends SalableBase { + protected _defaultErrorMessage: string; + constructor(apiKey: string) { + super(apiKey); + this._defaultErrorMessage = 'Unknown error while changing plan. Please try again later'; + } + + upgradeOrDowngradePlanSync = async ({planID, subscriptionID}: IChangePlan) => { + if (typeof planID !== 'string' || !planID.trim()) { + MissingPropertyError('planID'); + } + if (typeof subscriptionID !== 'string' || !subscriptionID.trim()) { + MissingPropertyError('subscriptionID'); + } + try { + await this._request(`/subscriptions/${subscriptionID}/updateplan/${planID}`, { + method: 'PUT', + }); + return {message: 'Plan changed successfully'}; + } catch (error) { + if (error instanceof Error) { + throw new Error(error.message); + } + throw new Error(this._defaultErrorMessage); + } + }; + upgradeOrDowngradePlanAsync = ({planID, subscriptionID}: IChangePlan, callback?: ChangePlanCallbackProp) => { + if (typeof planID !== 'string' || !planID.trim()) { + MissingPropertyError('planID'); + } + if (typeof subscriptionID !== 'string' || !subscriptionID.trim()) { + MissingPropertyError('subscriptionID'); + } + this._request(`/subscriptions/${subscriptionID}/updateplan/${planID}`, { + method: 'PUT', + }) + .then(() => { + if (callback) { + callback({message: 'Plan changed successfully'}, undefined); + } + }) + .catch((error) => { + if (callback) { + if (error instanceof Error) { + callback(undefined, error.message); + } + callback(undefined, this._defaultErrorMessage); + } + }); + }; +} diff --git a/tsconfig.json b/tsconfig.json index e356343..4eddb5e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,7 @@ "strict": true, "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, + "experimentalDecorators": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, @@ -17,6 +18,14 @@ "outDir": "dist", "types": ["node"] }, - "include": ["src", "./types/additional.d.ts", "src/index.ts", "cdn", "src/pricing-table", "src/globals.d.ts", "docs"], - "exclude": ["node_modules", "lib", "jest.setup.ts", "dist", "cdk.out", "package.json"] + "include": [ + "src", + "./types/additional.d.ts", + "src/index.ts", + "cdn", + "src/resources/pricing-table", + "src/globals.d.ts", + "docs" + ], + "exclude": ["node_modules", "lib", "jest.setup.ts", "dist", "cdk.out", "package.json", "src/**/__tests__"] } From 3ff397f793ce7d0b6ae610052f143ea0b1882a8b Mon Sep 17 00:00:00 2001 From: Joshua Okoro Date: Thu, 16 Mar 2023 08:20:03 -0500 Subject: [PATCH 6/8] feat: migrate available plan source code to typescript --- examples/available-plans.html | 1 - src/base.ts | 54 -- src/index.ts | 10 +- src/interfaces/index.ts | 2 + src/interfaces/product.interface.ts | 109 ++++ src/interfaces/subscription.interface.ts | 14 + src/resources/available-plans.js | 637 ----------------------- src/resources/available-plans.ts | 459 ++++++++++++++++ src/resources/base.ts | 31 +- src/resources/change-plan.ts | 3 + src/resources/element-generator.ts | 50 ++ src/resources/plans-table.ts | 182 +++++++ 12 files changed, 833 insertions(+), 719 deletions(-) delete mode 100644 src/base.ts create mode 100644 src/interfaces/index.ts create mode 100644 src/interfaces/product.interface.ts create mode 100644 src/interfaces/subscription.interface.ts delete mode 100644 src/resources/available-plans.js create mode 100644 src/resources/available-plans.ts create mode 100644 src/resources/element-generator.ts create mode 100644 src/resources/plans-table.ts diff --git a/examples/available-plans.html b/examples/available-plans.html index edbb627..8664e54 100644 --- a/examples/available-plans.html +++ b/examples/available-plans.html @@ -19,7 +19,6 @@

Available Plans

-