From 18d8d9acc82be3ac031f484da88074b456eec164 Mon Sep 17 00:00:00 2001 From: Luis Alvarez Date: Wed, 10 Mar 2021 23:07:53 -0600 Subject: [PATCH 01/21] Update to latest Next.js canary --- package.json | 5 +- yarn.lock | 326 +++++++++------------------------------------------ 2 files changed, 56 insertions(+), 275 deletions(-) diff --git a/package.json b/package.json index 4e8fa8d30..85810ca91 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "lodash.debounce": "^4.0.8", "lodash.random": "^3.2.0", "lodash.throttle": "^4.1.1", - "next": "^10.0.7", + "next": "^10.0.9-canary.5", "next-seo": "^4.11.0", "next-themes": "^0.0.4", "postcss": "^8.2.6", @@ -73,9 +73,6 @@ "prettier": "^2.2.1", "typescript": "^4.0.3" }, - "resolutions": { - "webpack": "5.11.1" - }, "husky": { "hooks": { "pre-commit": "lint-staged" diff --git a/yarn.lock b/yarn.lock index 7a1dce814..45181fd21 100644 --- a/yarn.lock +++ b/yarn.lock @@ -862,20 +862,20 @@ dependencies: webpack-bundle-analyzer "4.3.0" -"@next/env@10.0.7": - version "10.0.7" - resolved "https://registry.yarnpkg.com/@next/env/-/env-10.0.7.tgz#7b3e87a9029ca37491e2ec25c27593f0906725f9" - integrity sha512-/vnz2SL/mk3Tei58WfRtVnvz5xHmAqcBmZL5sTBEy1CZG6OtZGNx0qAFCjtVkeJ5m1Bh4Ut+WFh/RF333wx8Sg== +"@next/env@10.0.9-canary.5": + version "10.0.9-canary.5" + resolved "https://registry.yarnpkg.com/@next/env/-/env-10.0.9-canary.5.tgz#a1e1b490e4037840349ecaea8e63a8c0d0de5361" + integrity sha512-5Sos4xVdjckm2iJwRxLkshIzUMr32lDyYMnBXRJ7PhFbITrgXUrV0d4XKDken8Yu8L11QDvDUKnpqGeBhFaL/A== -"@next/polyfill-module@10.0.7": - version "10.0.7" - resolved "https://registry.yarnpkg.com/@next/polyfill-module/-/polyfill-module-10.0.7.tgz#ec45ec1f28f47beed15ed67dffc907edd7143094" - integrity sha512-HxqzRpoSgmZP0kRIWwH+e0SgtAXqJ0VkYtwWcsQFED8+xF4Eqn+7Twyp4uE6hutC8gr8IFSFqH+DEYhRtg1ltQ== +"@next/polyfill-module@10.0.9-canary.5": + version "10.0.9-canary.5" + resolved "https://registry.yarnpkg.com/@next/polyfill-module/-/polyfill-module-10.0.9-canary.5.tgz#d3eb63ece1aed00cf5ac77865553bb1b3d894ad6" + integrity sha512-CbnMly7rGj6MelgzrHxxTANO9whsq7VM4mAH/3lFbO9sNK9lWFuJaMAwYtVJfYGd8ySjDFdpQsGlpW2ovzJZBA== -"@next/react-dev-overlay@10.0.7": - version "10.0.7" - resolved "https://registry.yarnpkg.com/@next/react-dev-overlay/-/react-dev-overlay-10.0.7.tgz#5fe777011cab75ec09ad539ee61bb95ab5a2bdeb" - integrity sha512-yq71MDHVqN2N+IqOvZDiFsMpQrBcymrdpTx1ShhAADX7cWQvW4dhcIir4BbfrS10vS1LLz/3a8uKZkGdNoJj3w== +"@next/react-dev-overlay@10.0.9-canary.5": + version "10.0.9-canary.5" + resolved "https://registry.yarnpkg.com/@next/react-dev-overlay/-/react-dev-overlay-10.0.9-canary.5.tgz#e267c609bfff6f9664164d58346298e8768f99cb" + integrity sha512-d0cFQvLe28ujAbWdv5CpJo03dpqmyNhXqUn+Dkpeybo1IEV68Arr0Me++ofIJvUxZV3isDBKHoyN6xFmgkYgSQ== dependencies: "@babel/code-frame" "7.12.11" anser "1.4.9" @@ -889,10 +889,10 @@ stacktrace-parser "0.1.10" strip-ansi "6.0.0" -"@next/react-refresh-utils@10.0.7": - version "10.0.7" - resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-10.0.7.tgz#866ce30fe2f321e011255e81ed5d55eeda05894b" - integrity sha512-d/71vtQglv6m7sh4W1O9drc2hYti7UnAdEXfBLZAS354g2S80lvCRGIhbDrMx4w0rpShoxBIZboE2++LihAESg== +"@next/react-refresh-utils@10.0.9-canary.5": + version "10.0.9-canary.5" + resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-10.0.9-canary.5.tgz#5ece6594ec08b48bc319d8f171d9c46a7beb04ba" + integrity sha512-47Y2GO+PezgJF4t41/VOEmIpUe66bbzbh/jO+doldwoPiLPxrSfOMpAnlJLpm5keHquBJMCIQbwDtmSpNvPkTg== "@nodelib/fs.scandir@2.1.4": version "2.1.4" @@ -1492,19 +1492,6 @@ app-module-path@^2.2.0: resolved "https://registry.yarnpkg.com/app-module-path/-/app-module-path-2.2.0.tgz#641aa55dfb7d6a6f0a8141c4b9c0aa50b6c24dd5" integrity sha1-ZBqlXft9am8KgUHEucCqULbCTdU= -aproba@^1.0.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== - -are-we-there-yet@~1.1.2: - version "1.1.5" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" - integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" @@ -1517,11 +1504,6 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" -array-flatten@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-3.0.0.tgz#6428ca2ee52c7b823192ec600fa3ed2f157cd541" - integrity sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA== - array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" @@ -2027,11 +2009,6 @@ chokidar@3.5.1, chokidar@^3.4.3: optionalDependencies: fsevents "~2.3.1" -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - chrome-trace-event@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" @@ -2234,11 +2211,6 @@ console-browserify@^1.1.0: resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= - constant-case@3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.3.tgz#ac910a99caf3926ac5112f352e3af599d8c5fc0a" @@ -2465,6 +2437,13 @@ debounce@^1.2.0: resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.0.tgz#44a540abc0ea9943018dc0eaa95cce87f65cd131" integrity sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg== +debug@2: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + debug@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -2513,20 +2492,6 @@ decompress-response@^3.3.0: dependencies: mimic-response "^1.0.0" -decompress-response@^4.2.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986" - integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw== - dependencies: - mimic-response "^2.0.0" - -decompress-response@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" - integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== - dependencies: - mimic-response "^3.1.0" - dedent@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" @@ -2576,11 +2541,6 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= - depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -2620,11 +2580,6 @@ detect-indent@^6.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.0.0.tgz#0abd0f549f69fc6659a254fe96786186b6f528fd" integrity sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA== -detect-libc@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= - detective-amd@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/detective-amd/-/detective-amd-3.0.1.tgz#aca8eddb1f405821953faf4a893d9b9e0430b09e" @@ -2830,7 +2785,7 @@ emojis-list@^3.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== -end-of-stream@^1.1.0, end-of-stream@^1.4.1: +end-of-stream@^1.1.0: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== @@ -3006,11 +2961,6 @@ execa@^4.1.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" -expand-template@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" - integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== - external-editor@^3.0.3: version "3.1.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" @@ -3209,11 +3159,6 @@ fs-capacitor@^6.1.0: resolved "https://registry.yarnpkg.com/fs-capacitor/-/fs-capacitor-6.2.0.tgz#fa79ac6576629163cb84561995602d8999afb7f5" integrity sha512-nKcE1UduoSKX27NSZlg879LdQc94OtbOsEmKMN2MBNudXREvijRKx2GEBsTMTfws+BrbkJoEuynbGSVRSpauvw== -fs-constants@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" - integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== - fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" @@ -3239,20 +3184,6 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - gensync@^1.0.0-beta.1: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -3280,6 +3211,13 @@ get-intrinsic@^1.0.2: has "^1.0.3" has-symbols "^1.0.1" +get-orientation@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/get-orientation/-/get-orientation-1.1.2.tgz#20507928951814f8a91ded0a0e67b29dfab98947" + integrity sha512-/pViTfifW+gBbh/RnlFYHINvELT9Znt+SYyDKAUL6uV6By019AK/s+i9XP4jSwq7lwP38Fd8HVeTxym3+hkwmQ== + dependencies: + stream-parser "^0.3.1" + get-own-enumerable-property-symbols@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" @@ -3299,11 +3237,6 @@ get-stream@^5.0.0, get-stream@^5.1.0: dependencies: pump "^3.0.0" -github-from-package@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" - integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= - glob-parent@^5.1.0, glob-parent@~5.1.0: version "5.1.1" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" @@ -3468,11 +3401,6 @@ has-symbols@^1.0.1: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= - has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -4537,16 +4465,6 @@ mimic-response@^1.0.0, mimic-response@^1.0.1: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== -mimic-response@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43" - integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== - -mimic-response@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" - integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== - min-indent@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" @@ -4578,16 +4496,11 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5: +minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== -mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" - integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== - mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" @@ -4643,11 +4556,6 @@ nanoid@^3.1.16, nanoid@^3.1.20: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== -napi-build-utils@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" - integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== - native-url@0.3.4: version "0.3.4" resolved "https://registry.yarnpkg.com/native-url/-/native-url-0.3.4.tgz#29c943172aed86c63cee62c8c04db7f5756661f8" @@ -4678,17 +4586,17 @@ next-unused@^0.0.3: madge "^3.8.0" ts-loader "^7.0.0" -next@^10.0.7: - version "10.0.7" - resolved "https://registry.yarnpkg.com/next/-/next-10.0.7.tgz#442f8e1da7454de33b0bbcc1ce5684b923597ee6" - integrity sha512-We0utmwwfkvO12eLyUZd3tX9VLDE3FPpOaHpH3kqKdUTxJzUKt8FLBXCTm0mwsTKW5XColWG8mJvz2OLu3+3QA== +next@^10.0.9-canary.5: + version "10.0.9-canary.5" + resolved "https://registry.yarnpkg.com/next/-/next-10.0.9-canary.5.tgz#969dfb3b4646736db1144fb965d4af3e83fa6ff2" + integrity sha512-wIqEpQe9gKxwBZZHQgDGuztNlfv6/opIjf6nFYf7Iimw4SnNprPJWpEKrqF3JVxMoLawI6tafMwsYg1TQNZM7A== dependencies: "@babel/runtime" "7.12.5" "@hapi/accept" "5.0.1" - "@next/env" "10.0.7" - "@next/polyfill-module" "10.0.7" - "@next/react-dev-overlay" "10.0.7" - "@next/react-refresh-utils" "10.0.7" + "@next/env" "10.0.9-canary.5" + "@next/polyfill-module" "10.0.9-canary.5" + "@next/react-dev-overlay" "10.0.9-canary.5" + "@next/react-refresh-utils" "10.0.9-canary.5" "@opentelemetry/api" "0.14.0" ast-types "0.13.2" browserslist "4.16.1" @@ -4700,6 +4608,7 @@ next@^10.0.7: cssnano-simple "1.2.2" etag "1.8.1" find-cache-dir "3.3.1" + get-orientation "1.1.2" jest-worker "24.9.0" native-url "0.3.4" node-fetch "2.6.1" @@ -4718,9 +4627,7 @@ next@^10.0.7: styled-jsx "3.3.2" use-subscription "1.5.1" vm-browserify "1.1.2" - watchpack "2.0.0-beta.13" - optionalDependencies: - sharp "0.26.3" + watchpack "2.1.1" no-case@^3.0.3, no-case@^3.0.4: version "3.0.4" @@ -4730,18 +4637,6 @@ no-case@^3.0.3, no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" -node-abi@^2.7.0: - version "2.19.3" - resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.19.3.tgz#252f5dcab12dad1b5503b2d27eddd4733930282d" - integrity sha512-9xZrlyfvKhWme2EXFKQhZRp1yNWT/uI1luYPr3sFl+H4keYY4xR+1jO7mvTTijIsHf1M+QDe9uWuKeEpLInIlg== - dependencies: - semver "^5.4.1" - -node-addon-api@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.1.0.tgz#98b21931557466c6729e51cb77cd39c965f42239" - integrity sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw== - node-emoji@^1.8.1: version "1.10.0" resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da" @@ -4807,11 +4702,6 @@ node-source-walk@^4.0.0, node-source-walk@^4.2.0: dependencies: "@babel/parser" "^7.0.0" -noop-logger@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" - integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI= - normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -4851,16 +4741,6 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" -npmlog@^4.0.1, npmlog@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - nullthrows@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" @@ -5612,27 +5492,6 @@ postcss@^8.1.6, postcss@^8.2.1, postcss@^8.2.6: nanoid "^3.1.20" source-map "^0.6.1" -prebuild-install@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-6.0.0.tgz#669022bcde57c710a869e39c5ca6bf9cd207f316" - integrity sha512-h2ZJ1PXHKWZpp1caLw0oX9sagVpL2YTk+ZwInQbQ3QqNd4J03O6MpFNmMTJlkfgPENWqe5kP0WjQLqz5OjLfsw== - dependencies: - detect-libc "^1.0.3" - expand-template "^2.0.3" - github-from-package "0.0.0" - minimist "^1.2.3" - mkdirp-classic "^0.5.3" - napi-build-utils "^1.0.1" - node-abi "^2.7.0" - noop-logger "^0.1.1" - npmlog "^4.0.1" - pump "^3.0.0" - rc "^1.2.7" - simple-get "^3.0.3" - tar-fs "^2.0.0" - tunnel-agent "^0.6.0" - which-pm-runs "^1.0.0" - precinct@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/precinct/-/precinct-6.3.1.tgz#8ad735a8afdfc48b56ed39c9ad3bf999b6b928dc" @@ -5871,7 +5730,7 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.3.3, readable-stream@^2.3.6: +readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.3.3, readable-stream@^2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -5884,7 +5743,7 @@ readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0: +readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -6175,7 +6034,7 @@ serialize-javascript@^5.0.1: dependencies: randombytes "^2.1.0" -set-blocking@^2.0.0, set-blocking@~2.0.0: +set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= @@ -6203,22 +6062,6 @@ sha.js@^2.4.0, sha.js@^2.4.8: inherits "^2.0.1" safe-buffer "^5.0.1" -sharp@0.26.3: - version "0.26.3" - resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.26.3.tgz#9de8577a986b22538e6e12ced1f7e8a53f9728de" - integrity sha512-NdEJ9S6AMr8Px0zgtFo1TJjMK/ROMU92MkDtYn2BBrDjIx3YfH9TUyGdzPC+I/L619GeYQc690Vbaxc5FPCCWg== - dependencies: - array-flatten "^3.0.0" - color "^3.1.3" - detect-libc "^1.0.3" - node-addon-api "^3.0.2" - npmlog "^4.1.2" - prebuild-install "^6.0.0" - semver "^7.3.2" - simple-get "^4.0.0" - tar-fs "^2.1.1" - tunnel-agent "^0.6.0" - shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -6241,7 +6084,7 @@ shopify-buy@^2.11.0: resolved "https://registry.yarnpkg.com/shopify-buy/-/shopify-buy-2.11.0.tgz#0f7cb52741395e4ae778c336f32ddf3fe67c2f35" integrity sha512-bGjS1b/VCPvCjazSstlKwgLtK1WBotWom06/12loja8yfo/cWkLuJsakBbQe1uEIDiOLhKaR0M0CAXZFheYDug== -signal-exit@^3.0.0, signal-exit@^3.0.2: +signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== @@ -6251,29 +6094,6 @@ signedsource@^1.0.0: resolved "https://registry.yarnpkg.com/signedsource/-/signedsource-1.0.0.tgz#1ddace4981798f93bd833973803d80d52e93ad6a" integrity sha1-HdrOSYF5j5O9gzlzgD2A1S6TrWo= -simple-concat@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" - integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== - -simple-get@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.0.tgz#b45be062435e50d159540b576202ceec40b9c6b3" - integrity sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA== - dependencies: - decompress-response "^4.2.0" - once "^1.3.1" - simple-concat "^1.0.0" - -simple-get@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.0.tgz#73fa628278d21de83dadd5512d2cc1f4872bd675" - integrity sha512-ZalZGexYr3TA0SwySsr5HlgOOinS4Jsa8YB2GJ6lUNAazyAu4KG/VmzMTwAt2YVXzzVj8QmefmAonZIK2BSGcQ== - dependencies: - decompress-response "^6.0.0" - once "^1.3.1" - simple-concat "^1.0.0" - simple-swizzle@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" @@ -6428,6 +6248,13 @@ stream-http@^2.7.2: to-arraybuffer "^1.0.0" xtend "^4.0.0" +stream-parser@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/stream-parser/-/stream-parser-0.3.1.tgz#1618548694420021a1182ff0af1911c129761773" + integrity sha1-FhhUhpRCACGhGC/wrxkRwSl2F3M= + dependencies: + debug "2" + streamsearch@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" @@ -6457,7 +6284,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2", string-width@^2.1.1: +string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -6670,27 +6497,6 @@ tapable@^2.1.1, tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw== -tar-fs@^2.0.0, tar-fs@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" - integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== - dependencies: - chownr "^1.1.1" - mkdirp-classic "^0.5.2" - pump "^3.0.0" - tar-stream "^2.1.4" - -tar-stream@^2.1.4: - version "2.2.0" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" - integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== - dependencies: - bl "^4.0.3" - end-of-stream "^1.4.1" - fs-constants "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.1.1" - temp@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/temp/-/temp-0.4.0.tgz#671ad63d57be0fe9d7294664b3fc400636678a60" @@ -6845,13 +6651,6 @@ tty-browserify@0.0.0: resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" @@ -7026,15 +6825,7 @@ warning@^4.0.3: dependencies: loose-envify "^1.0.0" -watchpack@2.0.0-beta.13: - version "2.0.0-beta.13" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.0.0-beta.13.tgz#9d9b0c094b8402139333e04eb6194643c8384f55" - integrity sha512-ZEFq2mx/k5qgQwgi6NOm+2ImICb8ngAkA/rZ6oyXZ7SgPn3pncf+nfhYTCrs3lmHwOxnPtGLTOuFLfpSMh1VMA== - dependencies: - glob-to-regexp "^0.4.1" - graceful-fs "^4.1.2" - -watchpack@^2.0.0: +watchpack@2.1.1, watchpack@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.1.1.tgz#e99630550fca07df9f90a06056987baa40a689c7" integrity sha512-Oo7LXCmc1eE1AjyuSBmtC3+Wy4HcV8PxWh2kP6fOl8yTlNS7r0K9l1ao2lrrUza7V39Y3D/BbJgY8VeSlc5JKw== @@ -7138,13 +6929,6 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -wide-align@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== - dependencies: - string-width "^1.0.2 || 2" - word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" From 8dee3bdf94c673c1aeb1cc8a62b26bbe56e5fae3 Mon Sep 17 00:00:00 2001 From: Sam Ko Date: Mon, 15 Mar 2021 15:00:52 -0700 Subject: [PATCH 02/21] Update .env.template (#232) * Update .env.template * Update .env.template (framework/shopify/) --- .env.template | 4 ++-- framework/shopify/.env.template | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.env.template b/.env.template index 9b45afe4b..9d2f8f1bf 100644 --- a/.env.template +++ b/.env.template @@ -5,5 +5,5 @@ BIGCOMMERCE_STORE_API_TOKEN= BIGCOMMERCE_STORE_API_CLIENT_ID= BIGCOMMERCE_CHANNEL_ID= -SHOPIFY_STORE_DOMAIN= -SHOPIFY_STOREFRONT_ACCESS_TOKEN= +NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN= +NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN= diff --git a/framework/shopify/.env.template b/framework/shopify/.env.template index 24521c2a1..9dc3674b6 100644 --- a/framework/shopify/.env.template +++ b/framework/shopify/.env.template @@ -1,2 +1,2 @@ -SHOPIFY_STORE_DOMAIN= -SHOPIFY_STOREFRONT_ACCESS_TOKEN= +NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN= +NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN= From a85772a73efaad8b857b7f31349cd8400f9d779b Mon Sep 17 00:00:00 2001 From: Vinicius Zucatti Date: Fri, 19 Mar 2021 13:02:59 -0300 Subject: [PATCH 03/21] Click image in cart should go to product (#227) Co-authored-by: B --- components/cart/CartItem/CartItem.tsx | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/components/cart/CartItem/CartItem.tsx b/components/cart/CartItem/CartItem.tsx index cb7f8600e..e6820d32c 100644 --- a/components/cart/CartItem/CartItem.tsx +++ b/components/cart/CartItem/CartItem.tsx @@ -92,15 +92,18 @@ const CartItem = ({ })} {...rest} > -
- {item.variant.image!.altText} +
+ + closeSidebarIfPresent()} + className={s.productImage} + width={150} + height={150} + src={item.variant.image!.url} + alt={item.variant.image!.altText} + unoptimized + /> +
From b5ba5e76be75257248a4f8daded15e5a184eebb0 Mon Sep 17 00:00:00 2001 From: Joe Refoy Date: Sat, 20 Mar 2021 02:07:35 +1000 Subject: [PATCH 04/21] Fix yarn package installation command (#240) * Fix yarn package installation command * Update README.md Co-authored-by: B Co-authored-by: B --- framework/shopify/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/shopify/README.md b/framework/shopify/README.md index fc6a70ce3..eeae73afc 100644 --- a/framework/shopify/README.md +++ b/framework/shopify/README.md @@ -27,8 +27,8 @@ Collection of hooks and data fetching functions to integrate Shopify in a React 1. Install dependencies: ``` -yarn install shopify-buy -yarn install -D @types/shopify-buy +yarn add shopify-buy +yarn add @types/shopify-buy ``` 3. Environment variables need to be set: From f770ad7a91059a2ecfbb9de1bac111dfe7016124 Mon Sep 17 00:00:00 2001 From: Luis Alvarez D Date: Mon, 29 Mar 2021 17:03:45 -0600 Subject: [PATCH 05/21] Update master to work with multiple Vercel projects (#237) * Moved configs * Delete with-config.js * Added log for configs * Update TS config based on the selected provider * set tsconfig to bigcommerce * Updated readme instructions on provider config * Added new commerce variable * Change the default of updateTSConfig Co-authored-by: B --- .env.template | 3 ++ README.md | 21 +--------- commerce.config.json | 1 - framework/commerce/config.js | 66 +++++++++++++++++++++++++++++++ framework/commerce/with-config.js | 41 ------------------- next.config.js | 13 ++++-- 6 files changed, 80 insertions(+), 65 deletions(-) create mode 100644 framework/commerce/config.js delete mode 100644 framework/commerce/with-config.js diff --git a/.env.template b/.env.template index 9d2f8f1bf..9e42e2f31 100644 --- a/.env.template +++ b/.env.template @@ -1,3 +1,6 @@ +# Available providers: bigcommerce, shopify +COMMERCE_PROVIDER=bigcommerce + BIGCOMMERCE_STOREFRONT_API_URL= BIGCOMMERCE_STOREFRONT_API_TOKEN= BIGCOMMERCE_STORE_API_URL= diff --git a/README.md b/README.md index 885c95e85..cd4351911 100644 --- a/README.md +++ b/README.md @@ -71,25 +71,7 @@ Next.js Commerce provides a set of utilities and functions to create new provide ### How to change providers -First, update the provider selected in `commerce.config.json`: - -```json -{ - "provider": "bigcommerce", - "features": { - "wishlist": true - } -} -``` - -Then, change the paths defined in `tsconfig.json` and update the `@framework` paths to point to the right folder provider: - -```json -"@framework": ["framework/bigcommerce"], -"@framework/*": ["framework/bigcommerce/*"] -``` - -Make sure to add the environment variables required by the new provider. +Open `.env.local` and change the value of `COMMERCE_PROVIDER` to the provider you would like to use, then set the environment variables for that provider (use `.env.template` as the base). ### Features @@ -103,7 +85,6 @@ Every provider defines the features that it supports under `framework/{provider} - You'll see a config file like this: ```json { - "provider": "bigcommerce", "features": { "wishlist": false } diff --git a/commerce.config.json b/commerce.config.json index bef7db222..08ea78814 100644 --- a/commerce.config.json +++ b/commerce.config.json @@ -1,5 +1,4 @@ { - "provider": "bigcommerce", "features": { "wishlist": true, "customCheckout": false diff --git a/framework/commerce/config.js b/framework/commerce/config.js new file mode 100644 index 000000000..2dd3284bc --- /dev/null +++ b/framework/commerce/config.js @@ -0,0 +1,66 @@ +/** + * This file is expected to be used in next.config.js only + */ + +const path = require('path') +const fs = require('fs') +const merge = require('deepmerge') +const prettier = require('prettier') + +const PROVIDERS = ['bigcommerce', 'shopify'] + +function getProviderName() { + return ( + process.env.COMMERCE_PROVIDER || + (process.env.BIGCOMMERCE_STOREFRONT_API_URL + ? 'bigcommerce' + : process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN + ? 'shopify' + : null) + ) +} + +function withCommerceConfig(nextConfig = {}) { + const commerce = nextConfig.commerce || {} + const name = commerce.provider || getProviderName() + + if (!name) { + throw new Error( + `The commerce provider is missing, please add a valid provider name or its environment variables` + ) + } + if (!PROVIDERS.includes(name)) { + throw new Error( + `The commerce provider "${name}" can't be found, please use one of "${PROVIDERS.join( + ', ' + )}"` + ) + } + + const commerceNextConfig = require(path.join('../', name, 'next.config')) + const config = merge(commerceNextConfig, nextConfig) + + config.env = config.env || {} + + Object.entries(config.commerce.features).forEach(([k, v]) => { + if (v) config.env[`COMMERCE_${k.toUpperCase()}_ENABLED`] = true + }) + + // Update paths in `tsconfig.json` to point to the selected provider + if (config.commerce.updateTSConfig !== false) { + const tsconfigPath = path.join(process.cwd(), 'tsconfig.json') + const tsconfig = require(tsconfigPath) + + tsconfig.compilerOptions.paths['@framework'] = [`framework/${name}`] + tsconfig.compilerOptions.paths['@framework/*'] = [`framework/${name}/*`] + + fs.writeFileSync( + tsconfigPath, + prettier.format(JSON.stringify(tsconfig), { parser: 'json' }) + ) + } + + return config +} + +module.exports = { withCommerceConfig, getProviderName } diff --git a/framework/commerce/with-config.js b/framework/commerce/with-config.js deleted file mode 100644 index 1eb1acc19..000000000 --- a/framework/commerce/with-config.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * This file is expected to be used in next.config.js only - */ - -const merge = require('deepmerge') - -const PROVIDERS = ['bigcommerce', 'shopify'] - -function getProviderName() { - // TODO: OSOT. - return process.env.BIGCOMMERCE_STOREFRONT_API_URL ? 'bigcommerce' : null -} - -module.exports = (nextConfig = {}) => { - const commerce = nextConfig.commerce || {} - const name = commerce.provider || getProviderName() - - if (!name) { - throw new Error( - `The commerce provider is missing, please add a valid provider name or its environment variables` - ) - } - if (!PROVIDERS.includes(name)) { - throw new Error( - `The commerce provider "${name}" can't be found, please use one of "${PROVIDERS.join( - ', ' - )}"` - ) - } - - const commerceNextConfig = require(`../${name}/next.config`) - const config = merge(commerceNextConfig, nextConfig) - - config.env = config.env || {} - - Object.entries(config.commerce.features).forEach(([k, v]) => { - if (v) config.env[`COMMERCE_${k.toUpperCase()}_ENABLED`] = true - }) - - return config -} diff --git a/next.config.js b/next.config.js index 7e86695a0..c8694f1e0 100644 --- a/next.config.js +++ b/next.config.js @@ -1,8 +1,12 @@ const commerce = require('./commerce.config.json') -const withCommerceConfig = require('./framework/commerce/with-config') +const { + withCommerceConfig, + getProviderName, +} = require('./framework/commerce/config') -const isBC = commerce.provider === 'bigcommerce' -const isShopify = commerce.provider === 'shopify' +const provider = commerce.provider || getProviderName() +const isBC = provider === 'bigcommerce' +const isShopify = provider === 'shopify' module.exports = withCommerceConfig({ commerce, @@ -39,3 +43,6 @@ module.exports = withCommerceConfig({ ].filter((x) => x) }, }) + +// Don't delete this console log, useful to see the commerce config in Vercel deployments +console.log('next.config.js', JSON.stringify(module.exports, null, 2)) From 936f149fccdb5c0840600b6239b39c6e9380461c Mon Sep 17 00:00:00 2001 From: Luis Alvarez D Date: Thu, 8 Apr 2021 15:42:59 -0500 Subject: [PATCH 06/21] Update provider and commerce docs (#256) * Updating the docs for framework/commerce * Added more docs * Updated cart hooks docs * Updated docs for wishlist and Node.js * Added a note * Updated table of contents * Adding new provider docs * Updated core docs, main repo docs, and new provider docs * Updated bigcommerce docs * Updated shopify docs --- README.md | 59 +---- framework/bigcommerce/.env.template | 2 + framework/bigcommerce/README.md | 397 +++------------------------- framework/commerce/README.md | 334 +++++++++++++++++++++++ framework/commerce/new-provider.md | 239 +++++++++++++++++ framework/shopify/.env.template | 2 + framework/shopify/README.md | 163 +----------- 7 files changed, 640 insertions(+), 556 deletions(-) create mode 100644 framework/commerce/README.md create mode 100644 framework/commerce/new-provider.md diff --git a/README.md b/README.md index cd4351911..941b1699b 100644 --- a/README.md +++ b/README.md @@ -29,43 +29,10 @@ Next.js Commerce integrates out-of-the-box with BigCommerce and Shopify. We plan ## Considerations - `framework/commerce` contains all types, helpers and functions to be used as base to build a new **provider**. -- **Providers** live under `framework`'s root folder and they will extend Next.js Commerce types and functionality. -- **Features API** is to ensure feature parity between the UI and the Provider. The UI should update accordingly and no extra code should be bundled. All extra configuration for features will live under `features` in `commerce.config.json` and if needed it can also be accessed programatically. +- **Providers** live under `framework`'s root folder and they will extend Next.js Commerce types and functionality (`framework/commerce`). +- We have a **Features API** to ensure feature parity between the UI and the Provider. The UI should update accordingly and no extra code should be bundled. All extra configuration for features will live under `features` in `commerce.config.json` and if needed it can also be accessed programatically. - Each **provider** should add its corresponding `next.config.js` and `commerce.config.json` adding specific data related to the provider. For example in case of BigCommerce, the images CDN and additional API routes. - **Providers don't depend on anything that's specific to the application they're used in**. They only depend on `framework/commerce`, on their own framework folder and on some dependencies included in `package.json` -- We recommend that each **provider** ships with an `env.template` file and a `[readme.md](http://readme.md)` file. - -## Provider Structure - -Next.js Commerce provides a set of utilities and functions to create new providers. This is how a provider structure looks like. - -- `product` - - usePrice - - useSearch - - getProduct - - getAllProducts -- `wishlist` - - useWishlist - - useAddItem - - useRemoveItem -- `auth` - - useLogin - - useLogout - - useSignup -- `customer` - - useCustomer - - getCustomerId - - getCustomerWistlist -- `cart` - - useCart - - useAddItem - - useRemoveItem - - useUpdateItem -- `env.template` -- `provider.ts` -- `commerce.config.json` -- `next.config.js` -- `README.md` ## Configuration @@ -95,15 +62,9 @@ Every provider defines the features that it supports under `framework/{provider} ### How to create a new provider -We'd recommend to duplicate a provider folder and push your providers SDK. +Follow our docs for [Adding a new Commerce Provider](framework/commerce/new-provider.md). -If you succeeded building a provider, submit a PR so we can all enjoy it. - -## Work in progress - -We're using Github Projects to keep track of issues in progress and todo's. Here is our [Board](https://github.com/vercel/commerce/projects/1) - -People actively working on this project: @okbel & @lfades. +If you succeeded building a provider, submit a PR with a valid demo and we'll review it asap. ## Contribute @@ -113,11 +74,15 @@ Our commitment to Open Source can be found [here](https://vercel.com/oss). 2. Create a new branch `git checkout -b MY_BRANCH_NAME` 3. Install yarn: `npm install -g yarn` 4. Install the dependencies: `yarn` -5. Duplicate `.env.template` and rename it to `.env.local`. -6. Add proper store values to `.env.local`. +5. Duplicate `.env.template` and rename it to `.env.local` +6. Add proper store values to `.env.local` 7. Run `yarn dev` to build and watch for code changes -8. The development branch is `canary` (this is the branch pull requests should be made against). - On a release, `canary` branch is rebased into `master`. + +## Work in progress + +We're using Github Projects to keep track of issues in progress and todo's. Here is our [Board](https://github.com/vercel/commerce/projects/1) + +People actively working on this project: @okbel & @lfades. ## Troubleshoot diff --git a/framework/bigcommerce/.env.template b/framework/bigcommerce/.env.template index 43e85c046..2b91bc095 100644 --- a/framework/bigcommerce/.env.template +++ b/framework/bigcommerce/.env.template @@ -1,3 +1,5 @@ +COMMERCE_PROVIDER=bigcommerce + BIGCOMMERCE_STOREFRONT_API_URL= BIGCOMMERCE_STOREFRONT_API_TOKEN= BIGCOMMERCE_STORE_API_URL= diff --git a/framework/bigcommerce/README.md b/framework/bigcommerce/README.md index 2609b1544..7f62a5f3f 100644 --- a/framework/bigcommerce/README.md +++ b/framework/bigcommerce/README.md @@ -1,45 +1,34 @@ -# Table of Contents +# Bigcommerce Provider -- [BigCommerce Storefront Data Hooks](#bigcommerce-storefront-data-hooks) - - [Installation](#installation) - - [General Usage](#general-usage) - - [CommerceProvider](#commerceprovider) - - [useLogin hook](#uselogin-hook) - - [useLogout](#uselogout) - - [useCustomer](#usecustomer) - - [useSignup](#usesignup) - - [usePrice](#useprice) - - [Cart Hooks](#cart-hooks) - - [useCart](#usecart) - - [useAddItem](#useadditem) - - [useUpdateItem](#useupdateitem) - - [useRemoveItem](#useremoveitem) - - [Wishlist Hooks](#wishlist-hooks) - - [Product Hooks and API](#product-hooks-and-api) - - [useSearch](#usesearch) - - [getAllProducts](#getallproducts) - - [getProduct](#getproduct) - - [More](#more) +**Demo:** https://bigcommerce.demo.vercel.store/ -# BigCommerce Storefront Data Hooks +With the deploy button below you'll be able to have a [BigCommerce](https://www.bigcommerce.com/) account and a store that works with this starter: -> This project is under active development, new features and updates will be continuously added over time +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fcommerce&project-name=commerce&repo-name=commerce&demo-title=Next.js%20Commerce&demo-description=An%20all-in-one%20starter%20kit%20for%20high-performance%20e-commerce%20sites.&demo-url=https%3A%2F%2Fdemo.vercel.store&demo-image=https%3A%2F%2Fbigcommerce-demo-asset-ksvtgfvnd.vercel.app%2Fbigcommerce.png&integration-ids=oac_MuWZiE4jtmQ2ejZQaQ7ncuDT) -UI hooks and data fetching methods built from the ground up for e-commerce applications written in React, that use BigCommerce as a headless e-commerce platform. The package provides: +If you already have a BigCommerce account and want to use your current store, then copy the `.env.template` file in this directory to `.env.local` in the main directory (which will be ignored by Git): -- Code splitted hooks for data fetching using [SWR](https://swr.vercel.app/), and to handle common user actions -- Code splitted data fetching methods for initial data population and static generation of content -- Helpers to create the API endpoints that connect to the hooks, very well suited for Next.js applications - -## Installation - -To install: - -``` -yarn add storefront-data-hooks +```bash +cp framework/bigcommerce/.env.template .env.local ``` -After install, the first thing you do is: set your environment variables in `.env.local` +Then, set the environment variables in `.env.local` to match the ones from your store. + +## Contribute + +Our commitment to Open Source can be found [here](https://vercel.com/oss). + +If you find an issue with the provider or want a new feature, feel free to open a PR or [create a new issue](https://github.com/vercel/commerce/issues). + +## Troubleshoot + +
+I already own a BigCommerce store. What should I do? +
+First thing you do is: set your environment variables +
+
+.env.local ```sh BIGCOMMERCE_STOREFRONT_API_URL=<> @@ -50,331 +39,21 @@ BIGCOMMERCE_STORE_API_CLIENT_ID=<> BIGCOMMERCE_CHANNEL_ID=<> ``` -## General Usage +If your project was started with a "Deploy with Vercel" button, you can use Vercel's CLI to retrieve these credentials. -### CommerceProvider +1. Install Vercel CLI: `npm i -g vercel` +2. Link local instance with Vercel and Github accounts (creates .vercel file): `vercel link` +3. Download your environment variables: `vercel env pull .env.local` -This component is a provider pattern component that creates commerce context for it's children. It takes config values for the locale and an optional `fetcherRef` object for data fetching. +Next, you're free to customize the starter. More updates coming soon. Stay tuned. -```jsx -... -import { CommerceProvider } from '@bigcommerce/storefront-data-hooks' +
-const App = ({ locale = 'en-US', children }) => { - return ( - - {children} - - ) -} -... -``` - -### useLogin hook - -Hook for bigcommerce user login functionality, returns `login` function to handle user login. - -```jsx -... -import useLogin from '@bigcommerce/storefront-data-hooks/use-login' - -const LoginView = () => { - const login = useLogin() - - const handleLogin = async () => { - await login({ - email, - password, - }) - } - - return ( -
- {children} -
- ) -} -... -``` - -### useLogout - -Hook to logout user. - -```jsx -... -import useLogout from '@bigcommerce/storefront-data-hooks/use-logout' - -const LogoutLink = () => { - const logout = useLogout() - return ( - logout()}> - Logout - - ) -} -``` - -### useCustomer - -Hook for getting logged in customer data, and fetching customer info. - -```jsx -... -import useCustomer from '@bigcommerce/storefront-data-hooks/use-customer' -... - -const Profile = () => { - const { data } = useCustomer() - - if (!data) { - return null - } - - return ( -
Hello, {data.firstName}
- ) -} -``` - -### useSignup - -Hook for bigcommerce user signup, returns `signup` function to handle user signups. - -```jsx -... -import useSignup from '@bigcommerce/storefront-data-hooks/use-login' - -const SignupView = () => { - const signup = useSignup() - - const handleSignup = async () => { - await signup({ - email, - firstName, - lastName, - password, - }) - } - - return ( -
- {children} -
- ) -} -... -``` - -### usePrice - -Helper hook to format price according to commerce locale, and return discount if available. - -```jsx -import usePrice from '@bigcommerce/storefront-data-hooks/use-price' -... - const { price, discount, basePrice } = usePrice( - data && { - amount: data.cart_amount, - currencyCode: data.currency.code, - } - ) -... -``` - -## Cart Hooks - -### useCart - -Returns the current cart data for use - -```jsx -... -import useCart from '@bigcommerce/storefront-data-hooks/cart/use-cart' - -const countItem = (count: number, item: LineItem) => count + item.quantity - -const CartNumber = () => { - const { data } = useCart() - const itemsCount = data?.lineItems.reduce(countItem, 0) ?? 0 - - return itemsCount > 0 ? {itemsCount} : null -} -``` - -### useAddItem - -```jsx -... -import useAddItem from '@bigcommerce/storefront-data-hooks/cart/use-add-item' - -const AddToCartButton = ({ productId, variantId }) => { - const addItem = useAddItem() - - const addToCart = async () => { - await addItem({ - productId, - variantId, - }) - } - - return -} -... -``` - -### useUpdateItem - -```jsx -... -import useUpdateItem from '@bigcommerce/storefront-data-hooks/cart/use-update-item' - -const CartItem = ({ item }) => { - const [quantity, setQuantity] = useState(item.quantity) - const updateItem = useUpdateItem(item) - - const updateQuantity = async (e) => { - const val = e.target.value - await updateItem({ quantity: val }) - } - - return ( - - ) -} -... -``` - -### useRemoveItem - -Provided with a cartItemId, will remove an item from the cart: - -```jsx -... -import useRemoveItem from '@bigcommerce/storefront-data-hooks/cart/use-remove-item' - -const RemoveButton = ({ item }) => { - const removeItem = useRemoveItem() - - const handleRemove = async () => { - await removeItem({ id: item.id }) - } - - return -} -... -``` - -## Wishlist Hooks - -Wishlist hooks are similar to cart hooks. See the below example for how to use `useWishlist`, `useAddItem`, and `useRemoveItem`. - -```jsx -import useAddItem from '@bigcommerce/storefront-data-hooks/wishlist/use-add-item' -import useRemoveItem from '@bigcommerce/storefront-data-hooks/wishlist/use-remove-item' -import useWishlist from '@bigcommerce/storefront-data-hooks/wishlist/use-wishlist' - -const WishlistButton = ({ productId, variant }) => { - const addItem = useAddItem() - const removeItem = useRemoveItem() - const { data } = useWishlist() - const { data: customer } = useCustomer() - const itemInWishlist = data?.items?.find( - (item) => - item.product_id === productId && - item.variant_id === variant?.node.entityId - ) - - const handleWishlistChange = async (e) => { - e.preventDefault() - - if (!customer) { - return - } - - if (itemInWishlist) { - await removeItem({ id: itemInWishlist.id! }) - } else { - await addItem({ - productId, - variantId: variant?.node.entityId!, - }) - } - } - - return ( - - ) -} -``` - -## Product Hooks and API - -### useSearch - -`useSearch` handles searching the bigcommerce storefront product catalog by catalog, brand, and query string. - -```jsx -... -import useSearch from '@bigcommerce/storefront-data-hooks/products/use-search' - -const SearchPage = ({ searchString, category, brand, sortStr }) => { - const { data } = useSearch({ - search: searchString || '', - categoryId: category?.entityId, - brandId: brand?.entityId, - sort: sortStr || '', - }) - - return ( - - {data.products.map(({ node }) => ( - - ))} - - ) -} -``` - -### getAllProducts - -API function to retrieve a product list. - -```js -import { getConfig } from '@bigcommerce/storefront-data-hooks/api' -import getAllProducts from '@bigcommerce/storefront-data-hooks/api/operations/get-all-products' - -const { products } = await getAllProducts({ - variables: { field: 'featuredProducts', first: 6 }, - config, - preview, -}) -``` - -### getProduct - -API product to retrieve a single product when provided with the product -slug string. - -```js -import { getConfig } from '@bigcommerce/storefront-data-hooks/api' -import getProduct from '@bigcommerce/storefront-data-hooks/api/operations/get-product' - -const { product } = await getProduct({ - variables: { slug }, - config, - preview, -}) -``` - -## More - -Feel free to read through the source for more usage, and check the commerce vercel demo and commerce repo for usage examples: ([demo.vercel.store](https://demo.vercel.store/)) ([repo](https://github.com/vercel/commerce)) +
+BigCommerce shows a Coming Soon page and requests a Preview Code +
+After Email confirmation, Checkout should be manually enabled through BigCommerce platform. Look for "Review & test your store" section through BigCommerce's dashboard. +
+
+BigCommerce team has been notified and they plan to add more detailed about this subject. +
diff --git a/framework/commerce/README.md b/framework/commerce/README.md new file mode 100644 index 000000000..ecdebb8c0 --- /dev/null +++ b/framework/commerce/README.md @@ -0,0 +1,334 @@ +# Commerce Framework + +- [Commerce Framework](#commerce-framework) + - [Commerce Hooks](#commerce-hooks) + - [CommerceProvider](#commerceprovider) + - [Authentication Hooks](#authentication-hooks) + - [useSignup](#usesignup) + - [useLogin](#uselogin) + - [useLogout](#uselogout) + - [Customer Hooks](#customer-hooks) + - [useCustomer](#usecustomer) + - [Product Hooks](#product-hooks) + - [usePrice](#useprice) + - [useSearch](#usesearch) + - [Cart Hooks](#cart-hooks) + - [useCart](#usecart) + - [useAddItem](#useadditem) + - [useUpdateItem](#useupdateitem) + - [useRemoveItem](#useremoveitem) + - [Wishlist Hooks](#wishlist-hooks) + - [Commerce API](#commerce-api) + - [More](#more) + +The commerce framework ships multiple hooks and a Node.js API, both using an underlying headless e-commerce platform, which we call commerce providers. + +The core features are: + +- Code splitted hooks for data fetching using [SWR](https://swr.vercel.app/), and to handle common user actions +- A Node.js API for initial data population, static generation of content and for creating the API endpoints that connect to the hooks, if required. + +> 👩‍🔬 If you would like to contribute a new provider, check the docs for [Adding a new Commerce Provider](./new-provider.md). + +> 🚧 The core commerce framework is under active development, new features and updates will be continuously added over time. Breaking changes are expected while we finish the API. + +## Commerce Hooks + +A commerce hook is a [React hook](https://reactjs.org/docs/hooks-intro.html) that's connected to a commerce provider. They focus on user actions and data fetching of data that wasn't statically generated. + +Data fetching hooks use [SWR](https://swr.vercel.app/) underneath and you're welcome to use any of its [return values](https://swr.vercel.app/docs/options#return-values) and [options](https://swr.vercel.app/docs/options#options). For example, using the `useCustomer` hook: + +```jsx +const { data, isLoading, error } = useCustomer({ + swrOptions: { + revalidateOnFocus: true, + }, +}) +``` + +### CommerceProvider + +This component adds the provider config and handlers to the context of your React tree for it's children. You can optionally pass the `locale` to it: + +```jsx +import { CommerceProvider } from '@framework' + +const App = ({ locale = 'en-US', children }) => { + return {children} +} +``` + +## Authentication Hooks + +### useSignup + +Returns a _signup_ function that can be used to sign up the current visitor: + +```jsx +import useSignup from '@framework/auth/use-signup' + +const SignupView = () => { + const signup = useSignup() + + const handleSignup = async () => { + await signup({ + email, + firstName, + lastName, + password, + }) + } + + return
{children}
+} +``` + +### useLogin + +Returns a _login_ function that can be used to sign in the current visitor into an existing customer: + +```jsx +import useLogin from '@framework/auth/use-login' + +const LoginView = () => { + const login = useLogin() + const handleLogin = async () => { + await login({ + email, + password, + }) + } + + return
{children}
+} +``` + +### useLogout + +Returns a _logout_ function that signs out the current customer when called. + +```jsx +import useLogout from '@framework/auth/use-logout' + +const LogoutButton = () => { + const logout = useLogout() + return ( + + ) +} +``` + +## Customer Hooks + +### useCustomer + +Fetches and returns the data of the signed in customer: + +```jsx +import useCustomer from '@framework/customer/use-customer' + +const Profile = () => { + const { data, isLoading, error } = useCustomer() + + if (isLoading) return

Loading...

+ if (error) return

{error.message}

+ if (!data) return null + + return
Hello, {data.firstName}
+} +``` + +## Product Hooks + +### usePrice + +Helper hook to format price according to the commerce locale and currency code. It also handles discounts: + +```jsx +import useCart from '@framework/cart/use-cart' +import usePrice from '@framework/product/use-price' + +// ... +const { data } = useCart() +const { price, discount, basePrice } = usePrice( + data && { + amount: data.subtotalPrice, + currencyCode: data.currency.code, + // If `baseAmount` is used, a discount will be calculated + // baseAmount: number, + } +) +// ... +``` + +### useSearch + +Fetches and returns the products that match a set of filters: + +```jsx +import useSearch from '@framework/product/use-search' + +const SearchPage = ({ searchString, category, brand, sortStr }) => { + const { data } = useSearch({ + search: searchString || '', + categoryId: category?.entityId, + brandId: brand?.entityId, + sort: sortStr, + }) + + return ( + + {data.products.map((product) => ( + + ))} + + ) +} +``` + +## Cart Hooks + +### useCart + +Fetches and returns the data of the current cart: + +```jsx +import useCart from '@framework/cart/use-cart' + +const CartTotal = () => { + const { data, isLoading, isEmpty, error } = useCart() + + if (isLoading) return

Loading...

+ if (error) return

{error.message}

+ if (isEmpty) return

The cart is empty

+ + return

The cart total is {data.totalPrice}

+} +``` + +### useAddItem + +Returns a function that adds a new item to the cart when called, if this is the first item it will create the cart: + +```jsx +import { useAddItem } from '@framework/cart' + +const AddToCartButton = ({ productId, variantId }) => { + const addItem = useAddItem() + + const addToCart = async () => { + await addItem({ + productId, + variantId, + }) + } + + return +} +``` + +### useUpdateItem + +Returns a function that updates a current item in the cart when called, usually the quantity. + +```jsx +import { useUpdateItem } from '@framework/cart' + +const CartItemQuantity = ({ item }) => { + const [quantity, setQuantity] = useState(item.quantity) + const updateItem = useUpdateItem({ item }) + + const updateQuantity = async (e) => { + const val = e.target.value + + setQuantity(val) + await updateItem({ quantity: val }) + } + + return ( + + ) +} +``` + +If the `quantity` is lower than 1 the item will be removed from the cart. + +### useRemoveItem + +Returns a function that removes an item in the cart when called: + +```jsx +import { useRemoveItem } from '@framework/cart' + +const RemoveButton = ({ item }) => { + const removeItem = useRemoveItem() + const handleRemove = async () => { + await removeItem(item) + } + + return +} +``` + +## Wishlist Hooks + +Wishlist hooks work just like [cart hooks](#cart-hooks). Feel free to check how those work first. + +The example below shows how to use the `useWishlist`, `useAddItem` and `useRemoveItem` hooks: + +```jsx +import { useWishlist, useAddItem, useRemoveItem } from '@framework/wishlist' + +const WishlistButton = ({ productId, variant }) => { + const addItem = useAddItem() + const removeItem = useRemoveItem() + const { data, isLoading, isEmpty, error } = useWishlist() + + if (isLoading) return

Loading...

+ if (error) return

{error.message}

+ if (isEmpty) return

The wihslist is empty

+ + const { data: customer } = useCustomer() + const itemInWishlist = data?.items?.find( + (item) => item.product_id === productId && item.variant_id === variant.id + ) + + const handleWishlistChange = async (e) => { + e.preventDefault() + if (!customer) return + + if (itemInWishlist) { + await removeItem({ id: itemInWishlist.id }) + } else { + await addItem({ + productId, + variantId: variant.id, + }) + } + } + + return ( + + ) +} +``` + +## Commerce API + +While commerce hooks focus on client side data fetching and interactions, the commerce API focuses on static content generation for pages and API endpoints in a Node.js context. + +> The commerce API is currently going through a refactor in https://github.com/vercel/commerce/pull/252 - We'll update the docs once the API is released. + +## More + +Feel free to read through the source for more usage, and check the commerce vercel demo and commerce repo for usage examples: ([demo.vercel.store](https://demo.vercel.store/)) ([repo](https://github.com/vercel/commerce)) diff --git a/framework/commerce/new-provider.md b/framework/commerce/new-provider.md new file mode 100644 index 000000000..4051c0f01 --- /dev/null +++ b/framework/commerce/new-provider.md @@ -0,0 +1,239 @@ +# Adding a new Commerce Provider + +A commerce provider is a headless e-commerce platform that integrates with the [Commerce Framework](./README.md). Right now we have the following providers: + +- BigCommerce ([framework/bigcommerce](../bigcommerce)) +- Shopify ([framework/shopify](../shopify)) + +Adding a commerce provider means adding a new folder in `framework` with a folder structure like the next one: + +- `api` + - index.ts +- `product` + - usePrice + - useSearch + - getProduct + - getAllProducts +- `wishlist` + - useWishlist + - useAddItem + - useRemoveItem +- `auth` + - useLogin + - useLogout + - useSignup +- `customer` + - useCustomer + - getCustomerId + - getCustomerWistlist +- `cart` + - useCart + - useAddItem + - useRemoveItem + - useUpdateItem +- `env.template` +- `index.ts` +- `provider.ts` +- `commerce.config.json` +- `next.config.js` +- `README.md` + +`provider.ts` exports a provider object with handlers for the [Commerce Hooks](./README.md#commerce-hooks) and `api/index.ts` exports a Node.js provider for the [Commerce API](./README.md#commerce-api) + +> **Important:** We use TypeScript for every provider and expect its usage for every new one. + +The app imports from the provider directly instead of the core commerce folder (`framework/commerce`), but all providers are interchangeable and to achieve it every provider always has to implement the core types and helpers. + +The provider folder should only depend on `framework/commerce` and dependencies in the main `package.json`. In the future we'll move the `framework` folder to a package that can be shared easily for multiple apps. + +## Adding the provider hooks + +Using BigCommerce as an example. The first thing to do is export a `CommerceProvider` component that includes a `provider` object with all the handlers that can be used for hooks: + +```tsx +import type { ReactNode } from 'react' +import { + CommerceConfig, + CommerceProvider as CoreCommerceProvider, + useCommerce as useCoreCommerce, +} from '@commerce' +import { bigcommerceProvider, BigcommerceProvider } from './provider' + +export { bigcommerceProvider } +export type { BigcommerceProvider } + +export const bigcommerceConfig: CommerceConfig = { + locale: 'en-us', + cartCookie: 'bc_cartId', +} + +export type BigcommerceConfig = Partial + +export type BigcommerceProps = { + children?: ReactNode + locale: string +} & BigcommerceConfig + +export function CommerceProvider({ children, ...config }: BigcommerceProps) { + return ( + + {children} + + ) +} + +export const useCommerce = () => useCoreCommerce() +``` + +The exported types and components extend from the core ones exported by `@commerce`, which refers to `framework/commerce`. + +The `bigcommerceProvider` object looks like this: + +```tsx +import { handler as useCart } from './cart/use-cart' +import { handler as useAddItem } from './cart/use-add-item' +import { handler as useUpdateItem } from './cart/use-update-item' +import { handler as useRemoveItem } from './cart/use-remove-item' + +import { handler as useWishlist } from './wishlist/use-wishlist' +import { handler as useWishlistAddItem } from './wishlist/use-add-item' +import { handler as useWishlistRemoveItem } from './wishlist/use-remove-item' + +import { handler as useCustomer } from './customer/use-customer' +import { handler as useSearch } from './product/use-search' + +import { handler as useLogin } from './auth/use-login' +import { handler as useLogout } from './auth/use-logout' +import { handler as useSignup } from './auth/use-signup' + +import fetcher from './fetcher' + +export const bigcommerceProvider = { + locale: 'en-us', + cartCookie: 'bc_cartId', + fetcher, + cart: { useCart, useAddItem, useUpdateItem, useRemoveItem }, + wishlist: { + useWishlist, + useAddItem: useWishlistAddItem, + useRemoveItem: useWishlistRemoveItem, + }, + customer: { useCustomer }, + products: { useSearch }, + auth: { useLogin, useLogout, useSignup }, +} + +export type BigcommerceProvider = typeof bigcommerceProvider +``` + +The provider object, in this case `bigcommerceProvider`, has to match the `Provider` type defined in [framework/commerce](./index.ts). + +A hook handler, like `useCart`, looks like this: + +```tsx +import { useMemo } from 'react' +import { SWRHook } from '@commerce/utils/types' +import useCart, { UseCart, FetchCartInput } from '@commerce/cart/use-cart' +import { normalizeCart } from '../lib/normalize' +import type { Cart } from '../types' + +export default useCart as UseCart + +export const handler: SWRHook< + Cart | null, + {}, + FetchCartInput, + { isEmpty?: boolean } +> = { + fetchOptions: { + url: '/api/bigcommerce/cart', + method: 'GET', + }, + async fetcher({ input: { cartId }, options, fetch }) { + const data = cartId ? await fetch(options) : null + return data && normalizeCart(data) + }, + useHook: ({ useData }) => (input) => { + const response = useData({ + swrOptions: { revalidateOnFocus: false, ...input?.swrOptions }, + }) + + return useMemo( + () => + Object.create(response, { + isEmpty: { + get() { + return (response.data?.lineItems.length ?? 0) <= 0 + }, + enumerable: true, + }, + }), + [response] + ) + }, +} +``` + +In the case of data fetching hooks like `useCart` each handler has to implement the `SWRHook` type that's defined in the core types. For mutations it's the `MutationHook`, e.g for `useAddItem`: + +```tsx +import { useCallback } from 'react' +import type { MutationHook } from '@commerce/utils/types' +import { CommerceError } from '@commerce/utils/errors' +import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item' +import { normalizeCart } from '../lib/normalize' +import type { + Cart, + BigcommerceCart, + CartItemBody, + AddCartItemBody, +} from '../types' +import useCart from './use-cart' + +export default useAddItem as UseAddItem + +export const handler: MutationHook = { + fetchOptions: { + url: '/api/bigcommerce/cart', + method: 'POST', + }, + async fetcher({ input: item, options, fetch }) { + if ( + item.quantity && + (!Number.isInteger(item.quantity) || item.quantity! < 1) + ) { + throw new CommerceError({ + message: 'The item quantity has to be a valid integer greater than 0', + }) + } + + const data = await fetch({ + ...options, + body: { item }, + }) + + return normalizeCart(data) + }, + useHook: ({ fetch }) => () => { + const { mutate } = useCart() + + return useCallback( + async function addItem(input) { + const data = await fetch({ input }) + await mutate(data, false) + return data + }, + [fetch, mutate] + ) + }, +} +``` + +## Adding the Node.js provider API + +TODO + +> The commerce API is currently going through a refactor in https://github.com/vercel/commerce/pull/252 - We'll update the docs once the API is released. diff --git a/framework/shopify/.env.template b/framework/shopify/.env.template index 9dc3674b6..74f446835 100644 --- a/framework/shopify/.env.template +++ b/framework/shopify/.env.template @@ -1,2 +1,4 @@ +COMMERCE_PROVIDER=shopify + NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN= NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN= diff --git a/framework/shopify/README.md b/framework/shopify/README.md index eeae73afc..d67111a41 100644 --- a/framework/shopify/README.md +++ b/framework/shopify/README.md @@ -1,57 +1,28 @@ -## Table of Contents +## Shopify Provider -- [Getting Started](#getting-started) - - [Modifications](#modifications) - - [Adding item to Cart](#adding-item-to-cart) - - [Proceed to Checkout](#proceed-to-checkout) -- [General Usage](#general-usage) - - [CommerceProvider](#commerceprovider) - - [useCommerce](#usecommerce) -- [Hooks](#hooks) - - [usePrice](#useprice) - - [useAddItem](#useadditem) - - [useRemoveItem](#useremoveitem) - - [useUpdateItem](#useupdateitem) -- [APIs](#apis) - - [getProduct](#getproduct) - - [getAllProducts](#getallproducts) - - [getAllCollections](#getallcollections) - - [getAllPages](#getallpages) +**Demo:** https://shopify.demo.vercel.store/ -# Shopify Storefront Data Hooks +Before getting starter, a [Shopify](https://www.shopify.com/) account and store is required before using the provider. -Collection of hooks and data fetching functions to integrate Shopify in a React application. Designed to work with [Next.js Commerce](https://demo.vercel.store/). +Next, copy the `.env.template` file in this directory to `.env.local` in the main directory (which will be ignored by Git): -## Getting Started - -1. Install dependencies: - -``` -yarn add shopify-buy -yarn add @types/shopify-buy +```bash +cp framework/shopify/.env.template .env.local ``` -3. Environment variables need to be set: +Then, set the environment variables in `.env.local` to match the ones from your store. -``` -SHOPIFY_STORE_DOMAIN= -SHOPIFY_STOREFRONT_ACCESS_TOKEN= -NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN= -NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN= -``` +## Contribute -4. Point the framework to `shopify` by updating `tsconfig.json`: +Our commitment to Open Source can be found [here](https://vercel.com/oss). -``` -"@framework/*": ["framework/shopify/*"], -"@framework": ["framework/shopify"] -``` +If you find an issue with the provider or want a new feature, feel free to open a PR or [create a new issue](https://github.com/vercel/commerce/issues). -### Modifications +## Modifications These modifications are temporarily until contributions are made to remove them. -#### Adding item to Cart +### Adding item to Cart ```js // components/product/ProductView/ProductView.tsx @@ -72,7 +43,7 @@ const ProductView: FC = ({ product }) => { } ``` -#### Proceed to Checkout +### Proceed to Checkout ```js // components/cart/CartSidebarView/CartSidebarView.tsx @@ -88,114 +59,6 @@ const CartSidebarView: FC = () => { } ``` -## General Usage - -### CommerceProvider - -Provider component that creates the commerce context for children. - -```js -import { CommerceProvider } from '@framework' - -const App = ({ children }) => { - return {children} -} - -export default App -``` - -### useCommerce - -Returns the configs that are defined in the nearest `CommerceProvider`. Also provides access to Shopify's `checkout` and `shop`. - -```js -import { useCommerce } from 'nextjs-commerce-shopify' - -const { checkout, shop } = useCommerce() -``` - -- `checkout`: The information required to checkout items and pay ([Documentation](https://shopify.dev/docs/storefront-api/reference/checkouts/checkout)). -- `shop`: Represents a collection of the general settings and information about the shop ([Documentation](https://shopify.dev/docs/storefront-api/reference/online-store/shop/index)). - -## Hooks - -### usePrice - -Display the product variant price according to currency and locale. - -```js -import usePrice from '@framework/product/use-price' - -const { price } = usePrice({ - amount, -}) -``` - -Takes in either `amount` or `variant`: - -- `amount`: A price value for a particular item if the amount is known. -- `variant`: A shopify product variant. Price will be extracted from the variant. - -### useAddItem - -```js -import { useAddItem } from '@framework/cart' - -const AddToCartButton = ({ variantId, quantity }) => { - const addItem = useAddItem() - - const addToCart = async () => { - await addItem({ - variantId, - }) - } - - return -} -``` - -### useRemoveItem - -```js -import { useRemoveItem } from '@framework/cart' - -const RemoveButton = ({ item }) => { - const removeItem = useRemoveItem() - - const handleRemove = async () => { - await removeItem({ id: item.id }) - } - - return -} -``` - -### useUpdateItem - -```js -import { useUpdateItem } from '@framework/cart' - -const CartItem = ({ item }) => { - const [quantity, setQuantity] = useState(item.quantity) - const updateItem = useUpdateItem(item) - - const updateQuantity = async (e) => { - const val = e.target.value - await updateItem({ quantity: val }) - } - - return ( - - ) -} -``` - ## APIs Collections of APIs to fetch data from a Shopify store. From d838f34c73dc1fb6a44965750024d2cb934907be Mon Sep 17 00:00:00 2001 From: B Date: Tue, 13 Apr 2021 11:00:29 -0300 Subject: [PATCH 07/21] Variants Issue with Shopify Provider (#222) * changes * Progress * Normalized Products output * Progress * Restored Index Agnostic * Progress * Reordering * Moved normalizer to BC function * Removed Futures * More Types * More Types * More Types * Fix useCallback * Progress, Changes types, readme and restoring functionality * Changes * TS Issues * Changes * Normalizer * Normalizing more operations * Normalizing more operations * changes * Merge Issues * Cleanup * change * changes * index.ts broke my tree shaking * slug * Normalized Options and Swatches * Restored Add to cart * Correct Variant Added to Cart * Normalizing Cart Responses * Changes * changes breaking * Adding immutable normalizer for Product * Cart Normalized * changes * Progress * More updates * Removed some comments * Add loading state for data hooks * Bug fix * Changed the way isEmpty works * Improve navbar performance * Added useResponse hook * added useResponse to useWhishlist * Added husky and lint-staged * Ran prettier fix * Added more cart types * Moved types.d.ts to the commerce folder * Minor changes * Moved normalizer to happen after fetch * updated useCart types * Updated normalizer for useData * Added new normalizer for the cart to the UI * More corrections for useCart * Updated cart update hooks * Removed import * Progress * Switch away from global types * Making multiple changes * Improved types for operations * Moved and updated cart types * Updated the useAddItem and useRemoveItem hooks * Minor life improvement * Minor change * Implement Shopify Provider * Update README.md * Update README.md * normalizations & missing files * Update index.ts * fixes * Update normalize.ts * fix: cart error on first load * shopify checkout redirect & api handler * Update get-checkout-id.ts * userAvatar * Fix: color option * Update normalize.ts * changes * Update next.config.js * start customer auth & signup * Update config.ts * Login, Sign Up, Log Out, and checkout & customer association * Automatic login after sign-up * Update handle-login.ts * MOving stuff around and adding temporal new files * changes * Replace use-cart with the new hook * Removed old hook * Improved HookHandler type * Moved types * Simplified useData types * Updated Fetcher type * Moved SwrOptions type * Removed duplicated fetcher * Moved provider to its own file * Added proper type for fetch input * Revert "Merge branch 'agnostic' of https://github.com/vercel/commerce into agnostic" This reverts commit 23c8ed7c2d48d30e74ad94216f9910650fadf30c, reversing changes made to bf50965a39ef0b1b956461ebe62070809fbe1d63. * change readme * Revert "Merge branch 'master' of https://github.com/vercel/commerce into agnostic" This reverts commit bf50965a39ef0b1b956461ebe62070809fbe1d63, reversing changes made to 0dad4ddedbf0bff2d0b5800ca469fda0073889ea. * Revert "Revert "Merge branch 'agnostic' of https://github.com/vercel/commerce into agnostic"" This reverts commit c9a43f1bce0572d0eff41f3af893be8bdb00bedd. * align with upstream changes * Updated how the hook input is handled * Add more options to the hook handler * Final touches to the hook handler type * Moved useWishlist to use new handler * Move useCustomer to the new hook * Added a default fetcher * query all products for vendors & paths, improve search * Update use-search.tsx * fix cart after upstream changes * Shopify Provider (#186) * Start of Shopify provider * add missing comment to documentation * add missing env vars to documentation * update reference to types file * Moved useSearch to the new hook * Removed old use-data lib * Removed generics for result and body * Removed normalizr * Wishlist * New changes and initial Features API * Fixed some types * Fixed more types * fixes after upstream changes * Fixed product types * Fixed another product type * Updated type * Fixed remaining issues with types * Added a MutationHandler * Moved the handlers to each hook * Moved the fetcher to its own file * Moved handler to each hook * Added initial version of useAddItem * Added better mutation types, and moved some hooks * Removed use-cart-actions * Added initial version of useAddItem * Updated types * Update use-add-item.tsx * changes * Changes * Reordering and changes * Adding Features APO * Adding wishlist api * Implementing FeaturesAPI with Wishlist * Removing bug with touchstart * Adding tyni typing * moved use-remove-item * Removed MutationHandler type * Moved more hooks and updated types to make them smaller * Moved data hooks to new format * Removed no longer required types * Removed useResponse helper * Updated useData type * Moved wishlist use-add-item * Moved wishlist use-remove-item to provider * Moved use-login and use-logout * Update use-signup * Removed use-action helper * Moved auth & cart hooks + several fixes * Updated cart item, fixed deprecations * Update next.config.js * Updates to wishlist feature * Moved the features to be environment variable only * More changes for wishlist config * Disable wishlist * Removed useWishlistActions * Updated readme * updates * typos * Updated the way the provider config is set * Removed features.ts * Removed bootstrap.js * Aligned with upstream changes * Updates * shopify: changes * shopify: changes * Update next.config.js * Shopify Provider Updates (#209) * Implement Shopify Provider * Update README.md * Update README.md * normalizations & missing files * Update index.ts * fixes * Update normalize.ts * fix: cart error on first load * shopify checkout redirect & api handler * Update get-checkout-id.ts * Fix: color option * Update normalize.ts * changes * Update next.config.js * start customer auth & signup * Update config.ts * Login, Sign Up, Log Out, and checkout & customer association * Automatic login after sign-up * Update handle-login.ts * changes * Revert "Merge branch 'agnostic' of https://github.com/vercel/commerce into agnostic" This reverts commit 23c8ed7c2d48d30e74ad94216f9910650fadf30c, reversing changes made to bf50965a39ef0b1b956461ebe62070809fbe1d63. * change readme * Revert "Merge branch 'master' of https://github.com/vercel/commerce into agnostic" This reverts commit bf50965a39ef0b1b956461ebe62070809fbe1d63, reversing changes made to 0dad4ddedbf0bff2d0b5800ca469fda0073889ea. * Revert "Revert "Merge branch 'agnostic' of https://github.com/vercel/commerce into agnostic"" This reverts commit c9a43f1bce0572d0eff41f3af893be8bdb00bedd. * align with upstream changes * query all products for vendors & paths, improve search * Update use-search.tsx * fix cart after upstream changes * fixes after upstream changes * Moved handler to each hook * Added initial version of useAddItem * Updated types * Update use-add-item.tsx * Moved auth & cart hooks + several fixes * Updated cart item, fixed deprecations * Update next.config.js * Aligned with upstream changes * Updates * Update next.config.js * Updated the commerce config structure * Removed @framework imports within framework providers * Fixed types * changes * Adding extra config * Adding shopify commit * Adding env templates to the providers * Ignore some types * Adding link for Cart * Adding customCheckout * multiple changes to fix the wishlist * Shopify Provier Updates (#212) * changes * Adding shopify commit * Changed to query page by id * Fixed page query, Changed use-search GraphQl query * Update use-search.tsx * remove unused util * Changed cookie expiration * Update tsconfig.json Co-authored-by: okbel * Bump and adding dependency * Adding color checks * Now colors work with lighter colors * Stable commerce.config.json * Updated main readme * Readme changes * Default to bigcommerce * og config * Adding Shopify Config * Adding OG Card * Adding support for default option * Adding support for default options and removing the one that we do not need * Changes to Shopify * Shopify provider fixes (#225) * Changed to query page by id * Fixed page query, Changed use-search GraphQl query * Update use-search.tsx * remove unused util * Changed cookie expiration * Update tsconfig.json * Fix add to cart & prepare for user activation * Update helpers.ts * Update helpers.ts * Changes, fix Shopify GraphQL deprecations * Update checkout-to-cart.ts * Default to BigCommerce * Update index.ts * Fixed types * Delete commerce.config.json * Shopify provider fixes (#239) * Changed to query page by id * Fixed page query, Changed use-search GraphQl query * Update use-search.tsx * remove unused util * Changed cookie expiration * Update tsconfig.json * Fix add to cart & prepare for user activation * Update helpers.ts * Update helpers.ts * Changes, fix Shopify GraphQL deprecations * Update checkout-to-cart.ts * Default to BigCommerce * Update index.ts * Fixed types * Update customer-access-token-delete.ts * Fix item missing path * Update customer-access-token-delete.ts Co-authored-by: bc Co-authored-by: Luis Alvarez Co-authored-by: cond0r Co-authored-by: Peter Mekhaeil <4616064+petermekhaeil@users.noreply.github.com> --- .../product/ProductView/ProductView.tsx | 24 ++++---- components/product/helpers.ts | 6 +- framework/shopify/api/index.ts | 3 +- .../api/operations/get-all-collections.ts | 21 ------- framework/shopify/api/operations/get-page.ts | 25 -------- framework/shopify/auth/use-login.tsx | 9 +-- framework/shopify/auth/use-signup.tsx | 38 ++++++------ framework/shopify/cart/index.ts | 1 + framework/shopify/cart/use-add-item.tsx | 12 ++-- framework/shopify/cart/use-cart.tsx | 8 +-- framework/shopify/cart/use-remove-item.tsx | 13 ++-- framework/shopify/cart/use-update-item.tsx | 2 +- .../shopify/cart/utils/checkout-to-cart.ts | 42 ------------- framework/shopify/cart/utils/fetcher.ts | 31 ---------- framework/shopify/cart/utils/index.ts | 2 - framework/shopify/fetcher.ts | 9 ++- framework/shopify/index.tsx | 3 +- framework/shopify/product/use-search.tsx | 3 +- framework/shopify/provider.ts | 6 +- framework/shopify/types.ts | 6 +- .../{cart => }/utils/checkout-create.ts | 14 +++-- framework/shopify/utils/checkout-to-cart.ts | 48 +++++++++++++++ framework/shopify/utils/get-sort-variables.ts | 2 +- framework/shopify/utils/get-vendors.ts | 22 ++++--- .../utils/handle-account-activation.ts | 30 +++++++++ framework/shopify/utils/handle-login.ts | 43 ++++++------- framework/shopify/utils/index.ts | 5 ++ .../utils/mutations/checkout-create.ts | 5 +- .../utils/mutations/checkout-line-item-add.ts | 5 +- .../mutations/checkout-line-item-remove.ts | 5 +- .../mutations/checkout-line-item-update.ts | 5 +- .../mutations/customer-activate-by-url.ts | 19 ++++++ .../utils/mutations/customer-activate.ts | 19 ++++++ framework/shopify/utils/mutations/index.ts | 2 + framework/shopify/utils/normalize.ts | 61 +++++++++++-------- .../utils/queries/get-checkout-query.ts | 3 + framework/shopify/utils/storage.ts | 13 ---- framework/shopify/utils/throw-user-errors.ts | 38 ++++++++++++ pages/[...pages].tsx | 3 +- pages/search.tsx | 6 +- tsconfig.json | 4 +- 41 files changed, 326 insertions(+), 290 deletions(-) delete mode 100644 framework/shopify/api/operations/get-all-collections.ts delete mode 100644 framework/shopify/api/operations/get-page.ts delete mode 100644 framework/shopify/cart/utils/checkout-to-cart.ts delete mode 100644 framework/shopify/cart/utils/fetcher.ts delete mode 100644 framework/shopify/cart/utils/index.ts rename framework/shopify/{cart => }/utils/checkout-create.ts (62%) create mode 100644 framework/shopify/utils/checkout-to-cart.ts create mode 100644 framework/shopify/utils/handle-account-activation.ts create mode 100644 framework/shopify/utils/mutations/customer-activate-by-url.ts create mode 100644 framework/shopify/utils/mutations/customer-activate.ts delete mode 100644 framework/shopify/utils/storage.ts create mode 100644 framework/shopify/utils/throw-user-errors.ts diff --git a/components/product/ProductView/ProductView.tsx b/components/product/ProductView/ProductView.tsx index c0fdd32d2..05e7a1cee 100644 --- a/components/product/ProductView/ProductView.tsx +++ b/components/product/ProductView/ProductView.tsx @@ -1,23 +1,20 @@ import cn from 'classnames' import Image from 'next/image' import { NextSeo } from 'next-seo' -import { FC, useState } from 'react' +import { FC, useEffect, useState } from 'react' import s from './ProductView.module.css' - import { Swatch, ProductSlider } from '@components/product' import { Button, Container, Text, useUI } from '@components/ui' - import type { Product } from '@commerce/types' import usePrice from '@framework/product/use-price' import { useAddItem } from '@framework/cart' - import { getVariant, SelectedOptions } from '../helpers' import WishlistButton from '@components/wishlist/WishlistButton' interface Props { - className?: string children?: any product: Product + className?: string } const ProductView: FC = ({ product }) => { @@ -29,12 +26,18 @@ const ProductView: FC = ({ product }) => { }) const { openSidebar } = useUI() const [loading, setLoading] = useState(false) - const [choices, setChoices] = useState({ - size: null, - color: null, - }) + const [choices, setChoices] = useState({}) + + useEffect(() => { + // Selects the default option + product.variants[0].options?.forEach((v) => { + setChoices((choices) => ({ + ...choices, + [v.displayName.toLowerCase()]: v.values[0].label.toLowerCase(), + })) + }) + }, []) - // Select the correct variant based on choices const variant = getVariant(product, choices) const addToCart = async () => { @@ -143,7 +146,6 @@ const ProductView: FC = ({ product }) => { className={s.button} onClick={addToCart} loading={loading} - disabled={!variant && product.options.length > 0} > Add to Cart diff --git a/components/product/helpers.ts b/components/product/helpers.ts index 029476c92..a0ceb7aa5 100644 --- a/components/product/helpers.ts +++ b/components/product/helpers.ts @@ -1,9 +1,5 @@ import type { Product } from '@commerce/types' - -export type SelectedOptions = { - size: string | null - color: string | null -} +export type SelectedOptions = Record export function getVariant(product: Product, opts: SelectedOptions) { const variant = product.variants.find((variant) => { diff --git a/framework/shopify/api/index.ts b/framework/shopify/api/index.ts index 4f15cae15..387ed02fc 100644 --- a/framework/shopify/api/index.ts +++ b/framework/shopify/api/index.ts @@ -5,7 +5,6 @@ import { API_TOKEN, SHOPIFY_CHECKOUT_ID_COOKIE, SHOPIFY_CUSTOMER_TOKEN_COOKIE, - SHOPIFY_COOKIE_EXPIRE, } from '../const' if (!API_URL) { @@ -48,7 +47,7 @@ const config = new Config({ commerceUrl: API_URL, apiToken: API_TOKEN!, cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, - cartCookieMaxAge: SHOPIFY_COOKIE_EXPIRE, + cartCookieMaxAge: 60 * 60 * 24 * 30, fetch: fetchGraphqlApi, customerCookie: SHOPIFY_CUSTOMER_TOKEN_COOKIE, }) diff --git a/framework/shopify/api/operations/get-all-collections.ts b/framework/shopify/api/operations/get-all-collections.ts deleted file mode 100644 index 9cf216a91..000000000 --- a/framework/shopify/api/operations/get-all-collections.ts +++ /dev/null @@ -1,21 +0,0 @@ -import Client from 'shopify-buy' -import { ShopifyConfig } from '../index' - -type Options = { - config: ShopifyConfig -} - -const getAllCollections = async (options: Options) => { - const { config } = options - - const client = Client.buildClient({ - storefrontAccessToken: config.apiToken, - domain: config.commerceUrl, - }) - - const res = await client.collection.fetchAllWithProducts() - - return JSON.parse(JSON.stringify(res)) -} - -export default getAllCollections diff --git a/framework/shopify/api/operations/get-page.ts b/framework/shopify/api/operations/get-page.ts deleted file mode 100644 index 32acb7c8f..000000000 --- a/framework/shopify/api/operations/get-page.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Page } from '../../schema' -import { ShopifyConfig, getConfig } from '..' - -export type GetPageResult = T - -export type PageVariables = { - id: string -} - -async function getPage({ - url, - variables, - config, - preview, -}: { - url?: string - variables: PageVariables - config?: ShopifyConfig - preview?: boolean -}): Promise { - config = getConfig(config) - return {} -} - -export default getPage diff --git a/framework/shopify/auth/use-login.tsx b/framework/shopify/auth/use-login.tsx index 188dd54a2..7993822cd 100644 --- a/framework/shopify/auth/use-login.tsx +++ b/framework/shopify/auth/use-login.tsx @@ -10,7 +10,7 @@ import { MutationCheckoutCreateArgs, } from '../schema' import useLogin, { UseLogin } from '@commerce/auth/use-login' -import { setCustomerToken } from '../utils' +import { setCustomerToken, throwUserErrors } from '../utils' export default useLogin as UseLogin @@ -45,13 +45,8 @@ export const handler: MutationHook = { }, }) - const errors = customerAccessTokenCreate?.customerUserErrors + throwUserErrors(customerAccessTokenCreate?.customerUserErrors) - if (errors && errors.length) { - throw new ValidationError({ - message: getErrorMessage(errors[0]), - }) - } const customerAccessToken = customerAccessTokenCreate?.customerAccessToken const accessToken = customerAccessToken?.accessToken diff --git a/framework/shopify/auth/use-signup.tsx b/framework/shopify/auth/use-signup.tsx index 7f66448d3..9ca5c682f 100644 --- a/framework/shopify/auth/use-signup.tsx +++ b/framework/shopify/auth/use-signup.tsx @@ -1,15 +1,16 @@ import { useCallback } from 'react' import type { MutationHook } from '@commerce/utils/types' -import { CommerceError } from '@commerce/utils/errors' +import { CommerceError, ValidationError } from '@commerce/utils/errors' import useSignup, { UseSignup } from '@commerce/auth/use-signup' import useCustomer from '../customer/use-customer' -import { CustomerCreateInput } from '../schema' - import { - customerCreateMutation, - customerAccessTokenCreateMutation, -} from '../utils/mutations' -import handleLogin from '../utils/handle-login' + CustomerCreateInput, + Mutation, + MutationCustomerCreateArgs, +} from '../schema' + +import { customerCreateMutation } from '../utils/mutations' +import { handleAutomaticLogin, throwUserErrors } from '../utils' export default useSignup as UseSignup @@ -33,7 +34,11 @@ export const handler: MutationHook< 'A first name, last name, email and password are required to signup', }) } - const data = await fetch({ + + const { customerCreate } = await fetch< + Mutation, + MutationCustomerCreateArgs + >({ ...options, variables: { input: { @@ -45,19 +50,10 @@ export const handler: MutationHook< }, }) - try { - const loginData = await fetch({ - query: customerAccessTokenCreateMutation, - variables: { - input: { - email, - password, - }, - }, - }) - handleLogin(loginData) - } catch (error) {} - return data + throwUserErrors(customerCreate?.customerUserErrors) + await handleAutomaticLogin(fetch, { email, password }) + + return null }, useHook: ({ fetch }) => () => { const { revalidate } = useCustomer() diff --git a/framework/shopify/cart/index.ts b/framework/shopify/cart/index.ts index 3d288b1df..f6d36b443 100644 --- a/framework/shopify/cart/index.ts +++ b/framework/shopify/cart/index.ts @@ -1,3 +1,4 @@ export { default as useCart } from './use-cart' export { default as useAddItem } from './use-add-item' +export { default as useUpdateItem } from './use-update-item' export { default as useRemoveItem } from './use-remove-item' diff --git a/framework/shopify/cart/use-add-item.tsx b/framework/shopify/cart/use-add-item.tsx index d0f891148..cce0950e9 100644 --- a/framework/shopify/cart/use-add-item.tsx +++ b/framework/shopify/cart/use-add-item.tsx @@ -1,12 +1,15 @@ +import { useCallback } from 'react' import type { MutationHook } from '@commerce/utils/types' import { CommerceError } from '@commerce/utils/errors' import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item' import useCart from './use-cart' +import { + checkoutLineItemAddMutation, + getCheckoutId, + checkoutToCart, +} from '../utils' import { Cart, CartItemBody } from '../types' -import { checkoutLineItemAddMutation, getCheckoutId } from '../utils' -import { checkoutToCart } from './utils' import { Mutation, MutationCheckoutLineItemsAddArgs } from '../schema' -import { useCallback } from 'react' export default useAddItem as UseAddItem @@ -40,8 +43,7 @@ export const handler: MutationHook = { }, }) - // TODO: Fix this Cart type here - return checkoutToCart(checkoutLineItemsAdd) as any + return checkoutToCart(checkoutLineItemsAdd) }, useHook: ({ fetch }) => () => { const { mutate } = useCart() diff --git a/framework/shopify/cart/use-cart.tsx b/framework/shopify/cart/use-cart.tsx index d154bb837..5f77360bb 100644 --- a/framework/shopify/cart/use-cart.tsx +++ b/framework/shopify/cart/use-cart.tsx @@ -6,7 +6,7 @@ import useCommerceCart, { import { Cart } from '../types' import { SWRHook } from '@commerce/utils/types' -import { checkoutCreate, checkoutToCart } from './utils' +import { checkoutCreate, checkoutToCart } from '../utils' import getCheckoutQuery from '../utils/queries/get-checkout-query' export default useCommerceCart as UseCart @@ -22,11 +22,12 @@ export const handler: SWRHook< }, async fetcher({ input: { cartId: checkoutId }, options, fetch }) { let checkout + if (checkoutId) { const data = await fetch({ ...options, variables: { - checkoutId, + checkoutId: checkoutId, }, }) checkout = data.node @@ -36,8 +37,7 @@ export const handler: SWRHook< checkout = await checkoutCreate(fetch) } - // TODO: Fix this type - return checkoutToCart({ checkout } as any) + return checkoutToCart({ checkout }) }, useHook: ({ useData }) => (input) => { const response = useData({ diff --git a/framework/shopify/cart/use-remove-item.tsx b/framework/shopify/cart/use-remove-item.tsx index e2aef13d8..8db38eac2 100644 --- a/framework/shopify/cart/use-remove-item.tsx +++ b/framework/shopify/cart/use-remove-item.tsx @@ -1,23 +1,22 @@ import { useCallback } from 'react' - import type { MutationHookContext, HookFetcherContext, } from '@commerce/utils/types' - +import { RemoveCartItemBody } from '@commerce/types' import { ValidationError } from '@commerce/utils/errors' - import useRemoveItem, { RemoveItemInput as RemoveItemInputBase, UseRemoveItem, } from '@commerce/cart/use-remove-item' - import useCart from './use-cart' -import { checkoutLineItemRemoveMutation, getCheckoutId } from '../utils' -import { checkoutToCart } from './utils' +import { + checkoutLineItemRemoveMutation, + getCheckoutId, + checkoutToCart, +} from '../utils' import { Cart, LineItem } from '../types' import { Mutation, MutationCheckoutLineItemsRemoveArgs } from '../schema' -import { RemoveCartItemBody } from '@commerce/types' export type RemoveItemFn = T extends LineItem ? (input?: RemoveItemInput) => Promise diff --git a/framework/shopify/cart/use-update-item.tsx b/framework/shopify/cart/use-update-item.tsx index 666ce3d08..49dd6be14 100644 --- a/framework/shopify/cart/use-update-item.tsx +++ b/framework/shopify/cart/use-update-item.tsx @@ -13,7 +13,7 @@ import useUpdateItem, { import useCart from './use-cart' import { handler as removeItemHandler } from './use-remove-item' import type { Cart, LineItem, UpdateCartItemBody } from '../types' -import { checkoutToCart } from './utils' +import { checkoutToCart } from '../utils' import { getCheckoutId, checkoutLineItemUpdateMutation } from '../utils' import { Mutation, MutationCheckoutLineItemsUpdateArgs } from '../schema' diff --git a/framework/shopify/cart/utils/checkout-to-cart.ts b/framework/shopify/cart/utils/checkout-to-cart.ts deleted file mode 100644 index 03005f342..000000000 --- a/framework/shopify/cart/utils/checkout-to-cart.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Cart } from '../../types' -import { CommerceError, ValidationError } from '@commerce/utils/errors' - -import { - CheckoutLineItemsAddPayload, - CheckoutLineItemsRemovePayload, - CheckoutLineItemsUpdatePayload, - Maybe, -} from '../../schema' -import { normalizeCart } from '../../utils' - -export type CheckoutPayload = - | CheckoutLineItemsAddPayload - | CheckoutLineItemsUpdatePayload - | CheckoutLineItemsRemovePayload - -const checkoutToCart = (checkoutPayload?: Maybe): Cart => { - if (!checkoutPayload) { - throw new CommerceError({ - message: 'Invalid response from Shopify', - }) - } - - const checkout = checkoutPayload?.checkout - const userErrors = checkoutPayload?.userErrors - - if (userErrors && userErrors.length) { - throw new ValidationError({ - message: userErrors[0].message, - }) - } - - if (!checkout) { - throw new CommerceError({ - message: 'Invalid response from Shopify', - }) - } - - return normalizeCart(checkout) -} - -export default checkoutToCart diff --git a/framework/shopify/cart/utils/fetcher.ts b/framework/shopify/cart/utils/fetcher.ts deleted file mode 100644 index 6afb55f18..000000000 --- a/framework/shopify/cart/utils/fetcher.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { HookFetcherFn } from '@commerce/utils/types' -import { Cart } from '@commerce/types' -import { checkoutCreate, checkoutToCart } from '.' -import { FetchCartInput } from '@commerce/cart/use-cart' - -const fetcher: HookFetcherFn = async ({ - options, - input: { cartId: checkoutId }, - fetch, -}) => { - let checkout - - if (checkoutId) { - const data = await fetch({ - ...options, - variables: { - checkoutId, - }, - }) - checkout = data.node - } - - if (checkout?.completedAt || !checkoutId) { - checkout = await checkoutCreate(fetch) - } - - // TODO: Fix this type - return checkoutToCart({ checkout } as any) -} - -export default fetcher diff --git a/framework/shopify/cart/utils/index.ts b/framework/shopify/cart/utils/index.ts deleted file mode 100644 index 20d04955d..000000000 --- a/framework/shopify/cart/utils/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default as checkoutToCart } from './checkout-to-cart' -export { default as checkoutCreate } from './checkout-create' diff --git a/framework/shopify/fetcher.ts b/framework/shopify/fetcher.ts index 9c4fe9a9e..a69150503 100644 --- a/framework/shopify/fetcher.ts +++ b/framework/shopify/fetcher.ts @@ -2,9 +2,14 @@ import { Fetcher } from '@commerce/utils/types' import { API_TOKEN, API_URL } from './const' import { handleFetchResponse } from './utils' -const fetcher: Fetcher = async ({ method = 'POST', variables, query }) => { +const fetcher: Fetcher = async ({ + url = API_URL, + method = 'POST', + variables, + query, +}) => { return handleFetchResponse( - await fetch(API_URL, { + await fetch(url, { method, body: JSON.stringify({ query, variables }), headers: { diff --git a/framework/shopify/index.tsx b/framework/shopify/index.tsx index c26704771..5b25d6b21 100644 --- a/framework/shopify/index.tsx +++ b/framework/shopify/index.tsx @@ -28,8 +28,7 @@ export type ShopifyProps = { export function CommerceProvider({ children, ...config }: ShopifyProps) { return ( {children} diff --git a/framework/shopify/product/use-search.tsx b/framework/shopify/product/use-search.tsx index 425df9e83..bf812af3d 100644 --- a/framework/shopify/product/use-search.tsx +++ b/framework/shopify/product/use-search.tsx @@ -48,7 +48,8 @@ export const handler: SWRHook< edges = data.node?.products?.edges ?? [] if (brandId) { edges = edges.filter( - ({ node: { vendor } }: ProductEdge) => vendor === brandId + ({ node: { vendor } }: ProductEdge) => + vendor.replace(/\s+/g, '-').toLowerCase() === brandId ) } } else { diff --git a/framework/shopify/provider.ts b/framework/shopify/provider.ts index 383822baa..00db5c1d3 100644 --- a/framework/shopify/provider.ts +++ b/framework/shopify/provider.ts @@ -1,4 +1,4 @@ -import { SHOPIFY_CHECKOUT_ID_COOKIE, STORE_DOMAIN } from './const' +import { SHOPIFY_CHECKOUT_ID_COOKIE } from './const' import { handler as useCart } from './cart/use-cart' import { handler as useAddItem } from './cart/use-add-item' @@ -17,15 +17,11 @@ import fetcher from './fetcher' export const shopifyProvider = { locale: 'en-us', cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, - storeDomain: STORE_DOMAIN, fetcher, cart: { useCart, useAddItem, useUpdateItem, useRemoveItem }, customer: { useCustomer }, products: { useSearch }, auth: { useLogin, useLogout, useSignup }, - features: { - wishlist: false, - }, } export type ShopifyProvider = typeof shopifyProvider diff --git a/framework/shopify/types.ts b/framework/shopify/types.ts index c4e42b67d..e7bcb2476 100644 --- a/framework/shopify/types.ts +++ b/framework/shopify/types.ts @@ -7,13 +7,11 @@ export type ShopifyCheckout = { lineItems: CheckoutLineItem[] } -export interface Cart extends Core.Cart { - id: string +export type Cart = Core.Cart & { lineItems: LineItem[] } - export interface LineItem extends Core.LineItem { - options: any[] + options?: any[] } /** diff --git a/framework/shopify/cart/utils/checkout-create.ts b/framework/shopify/utils/checkout-create.ts similarity index 62% rename from framework/shopify/cart/utils/checkout-create.ts rename to framework/shopify/utils/checkout-create.ts index e950cc7e4..359d16315 100644 --- a/framework/shopify/cart/utils/checkout-create.ts +++ b/framework/shopify/utils/checkout-create.ts @@ -1,13 +1,17 @@ +import Cookies from 'js-cookie' + import { SHOPIFY_CHECKOUT_ID_COOKIE, SHOPIFY_CHECKOUT_URL_COOKIE, SHOPIFY_COOKIE_EXPIRE, -} from '../../const' +} from '../const' -import checkoutCreateMutation from '../../utils/mutations/checkout-create' -import Cookies from 'js-cookie' +import checkoutCreateMutation from './mutations/checkout-create' +import { CheckoutCreatePayload } from '../schema' -export const checkoutCreate = async (fetch: any) => { +export const checkoutCreate = async ( + fetch: any +): Promise => { const data = await fetch({ query: checkoutCreateMutation, }) @@ -20,7 +24,7 @@ export const checkoutCreate = async (fetch: any) => { expires: SHOPIFY_COOKIE_EXPIRE, } Cookies.set(SHOPIFY_CHECKOUT_ID_COOKIE, checkoutId, options) - Cookies.set(SHOPIFY_CHECKOUT_URL_COOKIE, checkout?.webUrl, options) + Cookies.set(SHOPIFY_CHECKOUT_URL_COOKIE, checkout.webUrl, options) } return checkout diff --git a/framework/shopify/utils/checkout-to-cart.ts b/framework/shopify/utils/checkout-to-cart.ts new file mode 100644 index 000000000..034ff11d7 --- /dev/null +++ b/framework/shopify/utils/checkout-to-cart.ts @@ -0,0 +1,48 @@ +import { Cart } from '../types' +import { CommerceError } from '@commerce/utils/errors' + +import { + CheckoutLineItemsAddPayload, + CheckoutLineItemsRemovePayload, + CheckoutLineItemsUpdatePayload, + CheckoutCreatePayload, + CheckoutUserError, + Checkout, + Maybe, +} from '../schema' + +import { normalizeCart } from './normalize' +import throwUserErrors from './throw-user-errors' + +export type CheckoutQuery = { + checkout: Checkout + checkoutUserErrors?: Array +} + +export type CheckoutPayload = + | CheckoutLineItemsAddPayload + | CheckoutLineItemsUpdatePayload + | CheckoutLineItemsRemovePayload + | CheckoutCreatePayload + | CheckoutQuery + +const checkoutToCart = (checkoutPayload?: Maybe): Cart => { + if (!checkoutPayload) { + throw new CommerceError({ + message: 'Missing checkout payload from response', + }) + } + + const checkout = checkoutPayload?.checkout + throwUserErrors(checkoutPayload?.checkoutUserErrors) + + if (!checkout) { + throw new CommerceError({ + message: 'Missing checkout object from response', + }) + } + + return normalizeCart(checkout) +} + +export default checkoutToCart diff --git a/framework/shopify/utils/get-sort-variables.ts b/framework/shopify/utils/get-sort-variables.ts index b8cdeec51..141d9a180 100644 --- a/framework/shopify/utils/get-sort-variables.ts +++ b/framework/shopify/utils/get-sort-variables.ts @@ -1,4 +1,4 @@ -const getSortVariables = (sort?: string, isCategory = false) => { +const getSortVariables = (sort?: string, isCategory: boolean = false) => { let output = {} switch (sort) { case 'price-asc': diff --git a/framework/shopify/utils/get-vendors.ts b/framework/shopify/utils/get-vendors.ts index f04483bb1..24843f177 100644 --- a/framework/shopify/utils/get-vendors.ts +++ b/framework/shopify/utils/get-vendors.ts @@ -2,13 +2,14 @@ import { ShopifyConfig } from '../api' import fetchAllProducts from '../api/utils/fetch-all-products' import getAllProductVendors from './queries/get-all-product-vendors-query' -export type BrandNode = { +export type Brand = { + entityId: string name: string path: string } export type BrandEdge = { - node: BrandNode + node: Brand } export type Brands = BrandEdge[] @@ -24,13 +25,16 @@ const getVendors = async (config: ShopifyConfig): Promise => { let vendorsStrings = vendors.map(({ node: { vendor } }) => vendor) - return [...new Set(vendorsStrings)].map((v) => ({ - node: { - entityId: v, - name: v, - path: `brands/${v}`, - }, - })) + return [...new Set(vendorsStrings)].map((v) => { + const id = v.replace(/\s+/g, '-').toLowerCase() + return { + node: { + entityId: id, + name: v, + path: `brands/${id}`, + }, + } + }) } export default getVendors diff --git a/framework/shopify/utils/handle-account-activation.ts b/framework/shopify/utils/handle-account-activation.ts new file mode 100644 index 000000000..d11f80ba1 --- /dev/null +++ b/framework/shopify/utils/handle-account-activation.ts @@ -0,0 +1,30 @@ +import { FetcherOptions } from '@commerce/utils/types' +import throwUserErrors from './throw-user-errors' + +import { + MutationCustomerActivateArgs, + MutationCustomerActivateByUrlArgs, +} from '../schema' +import { Mutation } from '../schema' +import { customerActivateByUrlMutation } from './mutations' + +const handleAccountActivation = async ( + fetch: (options: FetcherOptions) => Promise, + input: MutationCustomerActivateByUrlArgs +) => { + try { + const { customerActivateByUrl } = await fetch< + Mutation, + MutationCustomerActivateArgs + >({ + query: customerActivateByUrlMutation, + variables: { + input, + }, + }) + + throwUserErrors(customerActivateByUrl?.customerUserErrors) + } catch (error) {} +} + +export default handleAccountActivation diff --git a/framework/shopify/utils/handle-login.ts b/framework/shopify/utils/handle-login.ts index 77b6873e3..de86fa1d2 100644 --- a/framework/shopify/utils/handle-login.ts +++ b/framework/shopify/utils/handle-login.ts @@ -1,30 +1,12 @@ -import { ValidationError } from '@commerce/utils/errors' +import { FetcherOptions } from '@commerce/utils/types' +import { CustomerAccessTokenCreateInput } from '../schema' import { setCustomerToken } from './customer-token' - -const getErrorMessage = ({ - code, - message, -}: { - code: string - message: string -}) => { - switch (code) { - case 'UNIDENTIFIED_CUSTOMER': - message = 'Cannot find an account that matches the provided credentials' - break - } - return message -} +import { customerAccessTokenCreateMutation } from './mutations' +import throwUserErrors from './throw-user-errors' const handleLogin = (data: any) => { const response = data.customerAccessTokenCreate - const errors = response?.customerUserErrors - - if (errors && errors.length) { - throw new ValidationError({ - message: getErrorMessage(errors[0]), - }) - } + throwUserErrors(response?.customerUserErrors) const customerAccessToken = response?.customerAccessToken const accessToken = customerAccessToken?.accessToken @@ -36,4 +18,19 @@ const handleLogin = (data: any) => { return customerAccessToken } +export const handleAutomaticLogin = async ( + fetch: (options: FetcherOptions) => Promise, + input: CustomerAccessTokenCreateInput +) => { + try { + const loginData = await fetch({ + query: customerAccessTokenCreateMutation, + variables: { + input, + }, + }) + handleLogin(loginData) + } catch (error) {} +} + export default handleLogin diff --git a/framework/shopify/utils/index.ts b/framework/shopify/utils/index.ts index 2d59aa506..61e5975d7 100644 --- a/framework/shopify/utils/index.ts +++ b/framework/shopify/utils/index.ts @@ -4,6 +4,11 @@ export { default as getSortVariables } from './get-sort-variables' export { default as getVendors } from './get-vendors' export { default as getCategories } from './get-categories' export { default as getCheckoutId } from './get-checkout-id' +export { default as checkoutCreate } from './checkout-create' +export { default as checkoutToCart } from './checkout-to-cart' +export { default as handleLogin, handleAutomaticLogin } from './handle-login' +export { default as handleAccountActivation } from './handle-account-activation' +export { default as throwUserErrors } from './throw-user-errors' export * from './queries' export * from './mutations' export * from './normalize' diff --git a/framework/shopify/utils/mutations/checkout-create.ts b/framework/shopify/utils/mutations/checkout-create.ts index 912e1cbd2..ffbd555c7 100644 --- a/framework/shopify/utils/mutations/checkout-create.ts +++ b/framework/shopify/utils/mutations/checkout-create.ts @@ -3,9 +3,10 @@ import { checkoutDetailsFragment } from '../queries/get-checkout-query' const checkoutCreateMutation = /* GraphQL */ ` mutation { checkoutCreate(input: {}) { - userErrors { - message + checkoutUserErrors { + code field + message } checkout { ${checkoutDetailsFragment} diff --git a/framework/shopify/utils/mutations/checkout-line-item-add.ts b/framework/shopify/utils/mutations/checkout-line-item-add.ts index 67b9cf250..2282c4e26 100644 --- a/framework/shopify/utils/mutations/checkout-line-item-add.ts +++ b/framework/shopify/utils/mutations/checkout-line-item-add.ts @@ -3,9 +3,10 @@ import { checkoutDetailsFragment } from '../queries/get-checkout-query' const checkoutLineItemAddMutation = /* GraphQL */ ` mutation($checkoutId: ID!, $lineItems: [CheckoutLineItemInput!]!) { checkoutLineItemsAdd(checkoutId: $checkoutId, lineItems: $lineItems) { - userErrors { - message + checkoutUserErrors { + code field + message } checkout { ${checkoutDetailsFragment} diff --git a/framework/shopify/utils/mutations/checkout-line-item-remove.ts b/framework/shopify/utils/mutations/checkout-line-item-remove.ts index d967a5168..8dea4ce08 100644 --- a/framework/shopify/utils/mutations/checkout-line-item-remove.ts +++ b/framework/shopify/utils/mutations/checkout-line-item-remove.ts @@ -6,9 +6,10 @@ const checkoutLineItemRemoveMutation = /* GraphQL */ ` checkoutId: $checkoutId lineItemIds: $lineItemIds ) { - userErrors { - message + checkoutUserErrors { + code field + message } checkout { ${checkoutDetailsFragment} diff --git a/framework/shopify/utils/mutations/checkout-line-item-update.ts b/framework/shopify/utils/mutations/checkout-line-item-update.ts index 8edf17587..76254341e 100644 --- a/framework/shopify/utils/mutations/checkout-line-item-update.ts +++ b/framework/shopify/utils/mutations/checkout-line-item-update.ts @@ -3,9 +3,10 @@ import { checkoutDetailsFragment } from '../queries/get-checkout-query' const checkoutLineItemUpdateMutation = /* GraphQL */ ` mutation($checkoutId: ID!, $lineItems: [CheckoutLineItemUpdateInput!]!) { checkoutLineItemsUpdate(checkoutId: $checkoutId, lineItems: $lineItems) { - userErrors { - message + checkoutUserErrors { + code field + message } checkout { ${checkoutDetailsFragment} diff --git a/framework/shopify/utils/mutations/customer-activate-by-url.ts b/framework/shopify/utils/mutations/customer-activate-by-url.ts new file mode 100644 index 000000000..345d502bd --- /dev/null +++ b/framework/shopify/utils/mutations/customer-activate-by-url.ts @@ -0,0 +1,19 @@ +const customerActivateByUrlMutation = /* GraphQL */ ` + mutation customerActivateByUrl($activationUrl: URL!, $password: String!) { + customerActivateByUrl(activationUrl: $activationUrl, password: $password) { + customer { + id + } + customerAccessToken { + accessToken + expiresAt + } + customerUserErrors { + code + field + message + } + } + } +` +export default customerActivateByUrlMutation diff --git a/framework/shopify/utils/mutations/customer-activate.ts b/framework/shopify/utils/mutations/customer-activate.ts new file mode 100644 index 000000000..b1d057c69 --- /dev/null +++ b/framework/shopify/utils/mutations/customer-activate.ts @@ -0,0 +1,19 @@ +const customerActivateMutation = /* GraphQL */ ` + mutation customerActivate($id: ID!, $input: CustomerActivateInput!) { + customerActivate(id: $id, input: $input) { + customer { + id + } + customerAccessToken { + accessToken + expiresAt + } + customerUserErrors { + code + field + message + } + } + } +` +export default customerActivateMutation diff --git a/framework/shopify/utils/mutations/index.ts b/framework/shopify/utils/mutations/index.ts index 3a16d7cec..165fb192d 100644 --- a/framework/shopify/utils/mutations/index.ts +++ b/framework/shopify/utils/mutations/index.ts @@ -5,3 +5,5 @@ export { default as checkoutLineItemUpdateMutation } from './checkout-line-item- export { default as checkoutLineItemRemoveMutation } from './checkout-line-item-remove' export { default as customerAccessTokenCreateMutation } from './customer-access-token-create' export { default as customerAccessTokenDeleteMutation } from './customer-access-token-delete' +export { default as customerActivateMutation } from './customer-activate' +export { default as customerActivateByUrlMutation } from './customer-activate-by-url' diff --git a/framework/shopify/utils/normalize.ts b/framework/shopify/utils/normalize.ts index c9b428b37..25bdca053 100644 --- a/framework/shopify/utils/normalize.ts +++ b/framework/shopify/utils/normalize.ts @@ -33,7 +33,7 @@ const normalizeProductOption = ({ let output: any = { label: value, } - if (displayName === 'Color') { + if (displayName.match(/colou?r/gi)) { output = { ...output, hexColors: [value], @@ -54,21 +54,24 @@ const normalizeProductVariants = ({ edges }: ProductVariantConnection) => { return edges?.map( ({ node: { id, selectedOptions, sku, title, priceV2, compareAtPriceV2 }, - }) => ({ - id, - name: title, - sku: sku ?? id, - price: +priceV2.amount, - listPrice: +compareAtPriceV2?.amount, - requiresShipping: true, - options: selectedOptions.map(({ name, value }: SelectedOption) => - normalizeProductOption({ - id, - name, - values: [value], - }) - ), - }) + }) => { + return { + id, + name: title, + sku: sku ?? id, + price: +priceV2.amount, + listPrice: +compareAtPriceV2?.amount, + requiresShipping: true, + options: selectedOptions.map(({ name, value }: SelectedOption) => { + const options = normalizeProductOption({ + id, + name, + values: [value], + }) + return options + }), + } + } ) } @@ -96,7 +99,11 @@ export function normalizeProduct(productNode: ShopifyProduct): Product { price: money(priceRange?.minVariantPrice), images: normalizeProductImages(images), variants: variants ? normalizeProductVariants(variants) : [], - options: options ? options.map((o) => normalizeProductOption(o)) : [], + options: options + ? options + .filter((o) => o.name !== 'Title') // By default Shopify adds a 'Title' name when there's only one option. We don't need it. https://community.shopify.com/c/Shopify-APIs-SDKs/Adding-new-product-variant-is-automatically-adding-quot-Default/td-p/358095 + .map((o) => normalizeProductOption(o)) + : [], ...rest, } @@ -122,7 +129,7 @@ export function normalizeCart(checkout: Checkout): Cart { } function normalizeLineItem({ - node: { id, title, variant, quantity }, + node: { id, title, variant, quantity, ...rest }, }: CheckoutLineItemEdge): LineItem { return { id, @@ -135,18 +142,22 @@ function normalizeLineItem({ sku: variant?.sku ?? '', name: variant?.title!, image: { - url: variant?.image?.originalSrc, + url: variant?.image?.originalSrc ?? '/product-img-placeholder.svg', }, requiresShipping: variant?.requiresShipping ?? false, price: variant?.priceV2?.amount, listPrice: variant?.compareAtPriceV2?.amount, }, - path: '', + path: String(variant?.product?.handle), discounts: [], - options: [ - { - value: variant?.title, - }, - ], + options: + // By default Shopify adds a default variant with default names, we're removing it. https://community.shopify.com/c/Shopify-APIs-SDKs/Adding-new-product-variant-is-automatically-adding-quot-Default/td-p/358095 + variant?.title == 'Default Title' + ? [] + : [ + { + value: variant?.title, + }, + ], } } diff --git a/framework/shopify/utils/queries/get-checkout-query.ts b/framework/shopify/utils/queries/get-checkout-query.ts index 194e1619a..d8758e321 100644 --- a/framework/shopify/utils/queries/get-checkout-query.ts +++ b/framework/shopify/utils/queries/get-checkout-query.ts @@ -43,6 +43,9 @@ export const checkoutDetailsFragment = ` amount currencyCode } + product { + handle + } } quantity } diff --git a/framework/shopify/utils/storage.ts b/framework/shopify/utils/storage.ts deleted file mode 100644 index d46dadb21..000000000 --- a/framework/shopify/utils/storage.ts +++ /dev/null @@ -1,13 +0,0 @@ -export const getCheckoutIdFromStorage = (token: string) => { - if (window && window.sessionStorage) { - return window.sessionStorage.getItem(token) - } - - return null -} - -export const setCheckoutIdInStorage = (token: string, id: string | number) => { - if (window && window.sessionStorage) { - return window.sessionStorage.setItem(token, id + '') - } -} diff --git a/framework/shopify/utils/throw-user-errors.ts b/framework/shopify/utils/throw-user-errors.ts new file mode 100644 index 000000000..5488ba282 --- /dev/null +++ b/framework/shopify/utils/throw-user-errors.ts @@ -0,0 +1,38 @@ +import { ValidationError } from '@commerce/utils/errors' + +import { + CheckoutErrorCode, + CheckoutUserError, + CustomerErrorCode, + CustomerUserError, +} from '../schema' + +export type UserErrors = Array + +export type UserErrorCode = + | CustomerErrorCode + | CheckoutErrorCode + | null + | undefined + +const getCustomMessage = (code: UserErrorCode, message: string) => { + switch (code) { + case 'UNIDENTIFIED_CUSTOMER': + message = 'Cannot find an account that matches the provided credentials' + break + } + return message +} + +export const throwUserErrors = (errors?: UserErrors) => { + if (errors && errors.length) { + throw new ValidationError({ + errors: errors.map(({ code, message }) => ({ + code: code ?? 'validation_error', + message: getCustomMessage(code, message), + })), + }) + } +} + +export default throwUserErrors diff --git a/pages/[...pages].tsx b/pages/[...pages].tsx index 67adb6287..3f39845b5 100644 --- a/pages/[...pages].tsx +++ b/pages/[...pages].tsx @@ -25,8 +25,7 @@ export async function getStaticProps({ const pageItem = pages.find((p) => (p.url ? getSlug(p.url) === slug : false)) const data = pageItem && - // TODO: Shopify - Fix this type - (await getPage({ variables: { id: pageItem.id! } as any, config, preview })) + (await getPage({ variables: { id: pageItem.id! }, config, preview })) const page = data?.page if (!page) { diff --git a/pages/search.tsx b/pages/search.tsx index da2edccd8..4100108bc 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -75,10 +75,8 @@ export default function Search({ const { data } = useSearch({ search: typeof q === 'string' ? q : '', - // TODO: Shopify - Fix this type - categoryId: activeCategory?.entityId as any, - // TODO: Shopify - Fix this type - brandId: (activeBrand as any)?.entityId, + categoryId: activeCategory?.entityId, + brandId: activeBrand?.entityId, sort: typeof sort === 'string' ? sort : '', }) diff --git a/tsconfig.json b/tsconfig.json index 9e712fb18..e20f37099 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,8 +22,8 @@ "@components/*": ["components/*"], "@commerce": ["framework/commerce"], "@commerce/*": ["framework/commerce/*"], - "@framework": ["framework/bigcommerce"], - "@framework/*": ["framework/bigcommerce/*"] + "@framework": ["framework/shopify"], + "@framework/*": ["framework/shopify/*"] } }, "include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], From 0b715c2dd240652c9bcff2953e1c06b8bc8ae74a Mon Sep 17 00:00:00 2001 From: cond0r Date: Thu, 15 Apr 2021 18:35:31 +0300 Subject: [PATCH 08/21] Add product description html (#274) --- components/product/ProductView/ProductView.tsx | 2 +- framework/commerce/types.ts | 1 + framework/shopify/product/get-product.ts | 5 ++--- framework/shopify/utils/normalize.ts | 4 +++- framework/shopify/utils/queries/get-all-products-query.ts | 1 - 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/components/product/ProductView/ProductView.tsx b/components/product/ProductView/ProductView.tsx index 05e7a1cee..3b09fa39a 100644 --- a/components/product/ProductView/ProductView.tsx +++ b/components/product/ProductView/ProductView.tsx @@ -136,7 +136,7 @@ const ProductView: FC = ({ product }) => { ))}
- +
diff --git a/framework/commerce/types.ts b/framework/commerce/types.ts index a398070ac..86361fd9f 100644 --- a/framework/commerce/types.ts +++ b/framework/commerce/types.ts @@ -163,6 +163,7 @@ interface Entity { export interface Product extends Entity { name: string description: string + descriptionHtml?: string slug?: string path?: string images: ProductImage[] diff --git a/framework/shopify/product/get-product.ts b/framework/shopify/product/get-product.ts index 1f00288c7..1d861e1a1 100644 --- a/framework/shopify/product/get-product.ts +++ b/framework/shopify/product/get-product.ts @@ -21,11 +21,10 @@ const getProduct = async (options: { const { data }: GraphQLFetcherResult = await config.fetch(getProductQuery, { variables, }) - - const { productByHandle: product } = data + const { productByHandle } = data return { - product: product ? normalizeProduct(product) : null, + product: productByHandle ? normalizeProduct(productByHandle) : null, } } diff --git a/framework/shopify/utils/normalize.ts b/framework/shopify/utils/normalize.ts index 25bdca053..4ebc3a1ae 100644 --- a/framework/shopify/utils/normalize.ts +++ b/framework/shopify/utils/normalize.ts @@ -83,6 +83,7 @@ export function normalizeProduct(productNode: ShopifyProduct): Product { images, variants, description, + descriptionHtml, handle, priceRange, options, @@ -93,7 +94,6 @@ export function normalizeProduct(productNode: ShopifyProduct): Product { id, name, vendor, - description, path: `/${handle}`, slug: handle?.replace(/^\/+|\/+$/g, ''), price: money(priceRange?.minVariantPrice), @@ -104,6 +104,8 @@ export function normalizeProduct(productNode: ShopifyProduct): Product { .filter((o) => o.name !== 'Title') // By default Shopify adds a 'Title' name when there's only one option. We don't need it. https://community.shopify.com/c/Shopify-APIs-SDKs/Adding-new-product-variant-is-automatically-adding-quot-Default/td-p/358095 .map((o) => normalizeProductOption(o)) : [], + ...(description && { description }), + ...(descriptionHtml && { descriptionHtml }), ...rest, } diff --git a/framework/shopify/utils/queries/get-all-products-query.ts b/framework/shopify/utils/queries/get-all-products-query.ts index 5eb44c7a7..f48140d31 100644 --- a/framework/shopify/utils/queries/get-all-products-query.ts +++ b/framework/shopify/utils/queries/get-all-products-query.ts @@ -9,7 +9,6 @@ edges { title vendor handle - description priceRange { minVariantPrice { amount From a4f56d15496c837a73d83281cb0ff55fd95520e1 Mon Sep 17 00:00:00 2001 From: B Date: Wed, 21 Apr 2021 19:15:44 -0300 Subject: [PATCH 09/21] Delete ROADMAP.md --- docs/ROADMAP.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 docs/ROADMAP.md diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md deleted file mode 100644 index 437766dd8..000000000 --- a/docs/ROADMAP.md +++ /dev/null @@ -1 +0,0 @@ -# Roadmap From 8f260d66e7eac35d196d3e3a240101d73769fe5b Mon Sep 17 00:00:00 2001 From: Loan Laux Date: Fri, 23 Apr 2021 20:35:14 +0400 Subject: [PATCH 10/21] implement login Signed-off-by: Loan Laux --- .../api/cart/handlers/get-cart.ts | 10 +- .../reactioncommerce/api/checkout/index.ts | 6 +- framework/reactioncommerce/api/index.ts | 8 +- .../api/utils/fetch-graphql-api.ts | 1 - framework/reactioncommerce/auth/index.ts | 3 + framework/reactioncommerce/auth/use-login.tsx | 29 +- .../reactioncommerce/auth/use-signup.tsx | 4 +- .../cart/utils/checkout-create.ts | 4 +- framework/reactioncommerce/const.ts | 6 +- framework/reactioncommerce/fetcher.ts | 9 + framework/reactioncommerce/schema.d.ts | 11351 ++++---- framework/reactioncommerce/schema.graphql | 22610 ++++++++-------- .../reactioncommerce/utils/customer-token.ts | 14 +- .../utils/mutations/authenticate.ts | 15 + .../mutations/customer-access-token-create.ts | 16 - .../reactioncommerce/utils/mutations/index.ts | 2 +- next.config.js | 3 + yarn.lock | 2747 +- 18 files changed, 18596 insertions(+), 18242 deletions(-) create mode 100644 framework/reactioncommerce/auth/index.ts create mode 100644 framework/reactioncommerce/utils/mutations/authenticate.ts delete mode 100644 framework/reactioncommerce/utils/mutations/customer-access-token-create.ts diff --git a/framework/reactioncommerce/api/cart/handlers/get-cart.ts b/framework/reactioncommerce/api/cart/handlers/get-cart.ts index 0e6eb5c36..ca8d1d08e 100644 --- a/framework/reactioncommerce/api/cart/handlers/get-cart.ts +++ b/framework/reactioncommerce/api/cart/handlers/get-cart.ts @@ -9,16 +9,14 @@ import { import { normalizeCart } from '@framework/utils' // Return current cart info -const getCart: CartHandlers['getCart'] = async ({ - req: { +const getCart: CartHandlers['getCart'] = async ({ req, res, config }) => { + const { cookies: { [REACTION_ANONYMOUS_CART_TOKEN_COOKIE]: anonymousCartToken, [REACTION_CART_ID_COOKIE]: cartId, }, - }, - res, - config, -}) => { + } = req + let normalizedCart console.log('get-cart API') diff --git a/framework/reactioncommerce/api/checkout/index.ts b/framework/reactioncommerce/api/checkout/index.ts index de2cb835c..1fe5fb6fe 100644 --- a/framework/reactioncommerce/api/checkout/index.ts +++ b/framework/reactioncommerce/api/checkout/index.ts @@ -6,7 +6,7 @@ import createApiHandler, { import { REACTION_ANONYMOUS_CART_TOKEN_COOKIE, SHOPIFY_CHECKOUT_URL_COOKIE, - SHOPIFY_CUSTOMER_TOKEN_COOKIE, + REACTION_CUSTOMER_TOKEN_COOKIE, } from '../../const' import { getConfig } from '..' @@ -25,14 +25,14 @@ const checkoutApi: ReactionCommerceApiHandler = async ( const { cookies } = req const checkoutUrl = cookies[SHOPIFY_CHECKOUT_URL_COOKIE] - const customerCookie = cookies[SHOPIFY_CUSTOMER_TOKEN_COOKIE] + const customerCookie = cookies[REACTION_CUSTOMER_TOKEN_COOKIE] if (customerCookie) { try { await config.fetch(associateCustomerWithCheckoutMutation, { variables: { checkoutId: cookies[REACTION_ANONYMOUS_CART_TOKEN_COOKIE], - customerAccessToken: cookies[SHOPIFY_CUSTOMER_TOKEN_COOKIE], + customerAccessToken: cookies[REACTION_CUSTOMER_TOKEN_COOKIE], }, }) } catch (error) { diff --git a/framework/reactioncommerce/api/index.ts b/framework/reactioncommerce/api/index.ts index 2b7c47b89..2e3c44683 100644 --- a/framework/reactioncommerce/api/index.ts +++ b/framework/reactioncommerce/api/index.ts @@ -5,8 +5,8 @@ import { REACTION_ANONYMOUS_CART_TOKEN_COOKIE, REACTION_CART_ID_COOKIE, REACTION_EMPTY_DUMMY_CART_ID, - SHOPIFY_CUSTOMER_TOKEN_COOKIE, - SHOPIFY_COOKIE_EXPIRE, + REACTION_CUSTOMER_TOKEN_COOKIE, + REACTION_COOKIE_EXPIRE, SHOP_ID, } from '../const' @@ -45,9 +45,9 @@ const config = new Config({ cartCookie: REACTION_ANONYMOUS_CART_TOKEN_COOKIE, cartIdCookie: REACTION_CART_ID_COOKIE, dummyEmptyCartId: REACTION_EMPTY_DUMMY_CART_ID, - cartCookieMaxAge: SHOPIFY_COOKIE_EXPIRE, + cartCookieMaxAge: REACTION_COOKIE_EXPIRE, fetch: fetchGraphqlApi, - customerCookie: SHOPIFY_CUSTOMER_TOKEN_COOKIE, + customerCookie: REACTION_CUSTOMER_TOKEN_COOKIE, shopId: SHOP_ID, }) diff --git a/framework/reactioncommerce/api/utils/fetch-graphql-api.ts b/framework/reactioncommerce/api/utils/fetch-graphql-api.ts index dfbcf0343..6de009907 100644 --- a/framework/reactioncommerce/api/utils/fetch-graphql-api.ts +++ b/framework/reactioncommerce/api/utils/fetch-graphql-api.ts @@ -1,6 +1,5 @@ import type { GraphQLFetcher } from '@commerce/api' import fetch from './fetch' - import { API_URL } from '../../const' import { getError } from '../../utils/handle-fetch-response' diff --git a/framework/reactioncommerce/auth/index.ts b/framework/reactioncommerce/auth/index.ts new file mode 100644 index 000000000..36e757a89 --- /dev/null +++ b/framework/reactioncommerce/auth/index.ts @@ -0,0 +1,3 @@ +export { default as useLogin } from './use-login' +export { default as useLogout } from './use-logout' +export { default as useSignup } from './use-signup' diff --git a/framework/reactioncommerce/auth/use-login.tsx b/framework/reactioncommerce/auth/use-login.tsx index 188dd54a2..d5afeebb2 100644 --- a/framework/reactioncommerce/auth/use-login.tsx +++ b/framework/reactioncommerce/auth/use-login.tsx @@ -2,7 +2,7 @@ import { useCallback } from 'react' import type { MutationHook } from '@commerce/utils/types' import { CommerceError, ValidationError } from '@commerce/utils/errors' import useCustomer from '../customer/use-customer' -import createCustomerAccessTokenMutation from '../utils/mutations/customer-access-token-create' +import authenticateMutation from '../utils/mutations/authenticate' import { CustomerAccessTokenCreateInput, CustomerUserError, @@ -25,7 +25,7 @@ const getErrorMessage = ({ code, message }: CustomerUserError) => { export const handler: MutationHook = { fetchOptions: { - query: createCustomerAccessTokenMutation, + query: authenticateMutation, }, async fetcher({ input: { email, password }, options, fetch }) { if (!(email && password)) { @@ -35,25 +35,24 @@ export const handler: MutationHook = { }) } - const { customerAccessTokenCreate } = await fetch< - Mutation, - MutationCheckoutCreateArgs - >({ + console.log('querying API') + + const { authenticate } = await fetch({ ...options, variables: { - input: { email, password }, + serviceName: 'password', + params: { user: { email }, password }, }, }) - const errors = customerAccessTokenCreate?.customerUserErrors + // if (errors && errors.length) { + // throw new ValidationError({ + // message: getErrorMessage(errors[0].message), + // }) + // } + const accessToken = authenticate?.tokens?.accessToken - if (errors && errors.length) { - throw new ValidationError({ - message: getErrorMessage(errors[0]), - }) - } - const customerAccessToken = customerAccessTokenCreate?.customerAccessToken - const accessToken = customerAccessToken?.accessToken + console.log('accessToken', accessToken) if (accessToken) { setCustomerToken(accessToken) diff --git a/framework/reactioncommerce/auth/use-signup.tsx b/framework/reactioncommerce/auth/use-signup.tsx index 7f66448d3..0331acc56 100644 --- a/framework/reactioncommerce/auth/use-signup.tsx +++ b/framework/reactioncommerce/auth/use-signup.tsx @@ -7,7 +7,7 @@ import { CustomerCreateInput } from '../schema' import { customerCreateMutation, - customerAccessTokenCreateMutation, + authenticateMutation, } from '../utils/mutations' import handleLogin from '../utils/handle-login' @@ -47,7 +47,7 @@ export const handler: MutationHook< try { const loginData = await fetch({ - query: customerAccessTokenCreateMutation, + query: authenticateMutation, variables: { input: { email, diff --git a/framework/reactioncommerce/cart/utils/checkout-create.ts b/framework/reactioncommerce/cart/utils/checkout-create.ts index f072b5992..af031445d 100644 --- a/framework/reactioncommerce/cart/utils/checkout-create.ts +++ b/framework/reactioncommerce/cart/utils/checkout-create.ts @@ -1,7 +1,7 @@ import { REACTION_ANONYMOUS_CART_TOKEN_COOKIE, SHOPIFY_CHECKOUT_URL_COOKIE, - SHOPIFY_COOKIE_EXPIRE, + REACTION_COOKIE_EXPIRE, } from '../../const' import checkoutCreateMutation from '../../utils/mutations/checkout-create' @@ -22,7 +22,7 @@ export const checkoutCreate = async (fetch: any) => { if (checkoutId) { const options = { - expires: SHOPIFY_COOKIE_EXPIRE, + expires: REACTION_COOKIE_EXPIRE, } Cookies.set(REACTION_ANONYMOUS_CART_TOKEN_COOKIE, checkoutId, options) Cookies.set(SHOPIFY_CHECKOUT_URL_COOKIE, checkout?.webUrl, options) diff --git a/framework/reactioncommerce/const.ts b/framework/reactioncommerce/const.ts index 80090505f..c0faf6752 100644 --- a/framework/reactioncommerce/const.ts +++ b/framework/reactioncommerce/const.ts @@ -7,12 +7,12 @@ export const REACTION_EMPTY_DUMMY_CART_ID = 'DUMMY_EMPTY_CART_ID' export const SHOPIFY_CHECKOUT_URL_COOKIE = 'shopify_checkoutUrl' -export const SHOPIFY_CUSTOMER_TOKEN_COOKIE = 'shopify_customerToken' +export const REACTION_CUSTOMER_TOKEN_COOKIE = 'reaction_customerToken' export const STORE_DOMAIN = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN -export const SHOPIFY_COOKIE_EXPIRE = 30 +export const REACTION_COOKIE_EXPIRE = 30 export const API_URL = `http://127.0.0.1:3000/graphql` -export const SHOP_ID = 'cmVhY3Rpb24vc2hvcDplcnBESFlDdzc5cFRBV0FHUg==' +export const SHOP_ID = 'cmVhY3Rpb24vc2hvcDpIZGIycnRYTWVpbVRKbzZrcg==' diff --git a/framework/reactioncommerce/fetcher.ts b/framework/reactioncommerce/fetcher.ts index b4c57097b..4b774d081 100644 --- a/framework/reactioncommerce/fetcher.ts +++ b/framework/reactioncommerce/fetcher.ts @@ -2,6 +2,7 @@ import { FetcherError } from '@commerce/utils/errors' import type { Fetcher } from '@commerce/utils/types' import { handleFetchResponse } from './utils' import { API_URL } from './const' +import { getCustomerToken } from './utils' async function getText(res: Response) { try { @@ -28,12 +29,20 @@ const fetcher: Fetcher = async ({ }) => { // if no URL is passed but we have a `query` param, we assume it's GraphQL if (!url && query) { + const customerToken = getCustomerToken() + const authorizationHeader = {} + + if (customerToken) { + authorizationHeader['Authorization'] = `bearer ${customerToken}` + } + return handleFetchResponse( await fetch(API_URL, { method: 'POST', body: JSON.stringify({ query, variables }), headers: { 'Content-Type': 'application/json', + ...authorizationHeader, }, }) ) diff --git a/framework/reactioncommerce/schema.d.ts b/framework/reactioncommerce/schema.d.ts index 945444080..21b174606 100644 --- a/framework/reactioncommerce/schema.d.ts +++ b/framework/reactioncommerce/schema.d.ts @@ -13,8 +13,6 @@ export type Scalars = { Boolean: boolean Int: number Float: number - /** A string email address */ - Email: any /** * * An opaque string that identifies a particular result within a connection, @@ -29,910 +27,14 @@ export type Scalars = { * */ ConnectionLimitInt: any - /** A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the `date-time` format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar. */ - DateTime: any - /** An object with any fields */ - JSONObject: any /** A date string, such as 2007-12-03, compliant with the `full-date` format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar. */ Date: any -} - -/** Queries return all requested data, without any side effects */ -export type Query = { - __typename?: 'Query' - /** A test query */ - ping: Scalars['String'] - /** Returns the primary shop for the domain */ - primaryShop?: Maybe - /** Returns the ID of the primary shop for the domain */ - primaryShopId?: Maybe - /** Returns a shop by ID */ - shop?: Maybe - /** Returns a shop by slug */ - shopBySlug?: Maybe - shops?: Maybe - /** - * Returns app settings that are not shop specific. Plugins extend the GlobalSettings type to support - * whatever settings they need. - */ - globalSettings: GlobalSettings - /** - * Returns app settings for a specific shop. Plugins extend the ShopSettings type to support - * whatever settings they need. - */ - shopSettings: ShopSettings - /** - * Get a list of errors and suggested properly formatted addresses for an address. If no address - * validation service is active for the shop, this will return as if the address is valid even - * though no check actually occurred. - */ - addressValidation: AddressValidationResults - /** Get a full list of all registered address validation services */ - addressValidationServices: Array> - /** Returns a list of defined address validation rules for a shop */ - addressValidationRules: AddressValidationRuleConnection - /** SystemInformation object */ - systemInformation: SystemInformation - /** Retrieves a list of email templates */ - emailTemplates?: Maybe - /** Returns the account with the provided ID */ - account?: Maybe - /** Returns accounts optionally filtered by account groups */ - accounts: AccountConnection - /** Returns customer accounts */ - customers: AccountConnection - /** Returns the account for the authenticated user */ - viewer?: Maybe - /** Returns a single group by ID. */ - group?: Maybe - /** Returns a list of groups for the shop with ID `shopId`, as a Relay-compatible connection. */ - groups?: Maybe - /** Returns all pending staff member invitations */ - invitations: InvitationConnection - /** Returns a paged list of all roles associated with a shop */ - roles?: Maybe - /** Query for a single Product */ - product?: Maybe - /** Query for a list of Products */ - products?: Maybe - /** Gets items from a shop catalog */ - catalogItems?: Maybe - /** Gets product from catalog */ - catalogItemProduct?: Maybe - /** Returns a list of product in a tag */ - productsByTagId: TagProductConnection - /** Returns a tag from a provided tag ID or slug. Tags with isVisible set to false are excluded by default. */ - tag?: Maybe - /** Returns a paged list of tags for a shop. You must include a shopId when querying. */ - tags?: Maybe - /** - * Get the SimpleInventory info for a product configuration. Returns `null` if `updateSimpleInventory` - * has never been called for this product configuration. - */ - simpleInventory?: Maybe - /** Finds a cart by the cart ID and anonymous cart token. */ - anonymousCartByCartId?: Maybe - /** Find a cart for a given account ID. */ - accountCartByAccountId?: Maybe - /** Get an order by its ID */ - orderById?: Maybe - /** Get all orders for a single account, optionally limited to certain shop IDs and certain orderStatus */ - orders: OrderConnection - /** Get all orders for a single account, optionally limited to certain shop IDs and certain orderStatus */ - ordersByAccountId: OrdersByAccountIdConnection - /** Get an order by its reference ID (the ID shown to customers) */ - orderByReferenceId?: Maybe - /** Get refunds applied to an order by order ID */ - refunds?: Maybe>> - /** Get refunds applied to a specific payment by payment ID */ - refundsByPaymentId?: Maybe>> - /** - * Get a list of all payment methods available during a checkout. This may filter by auth, - * active/inactive, IP/region, shop, etc. To get the full list, use the `paymentMethods` - * query with proper authorization. - */ - availablePaymentMethods: Array> - /** Get a full list of all payment methods */ - paymentMethods: Array> - /** Gets discount codes */ - discountCodes?: Maybe - /** Get the full list of surcharges. */ - surcharges: SurchargeConnection - /** Get a single surcharge definition by its ID */ - surchargeById?: Maybe - /** Get a flat rate fulfillment method */ - flatRateFulfillmentMethod: FlatRateFulfillmentMethod - /** Get a flat rate fulfillment methods */ - flatRateFulfillmentMethods: FlatRateFulfillmentMethodConnection - /** Get the full list of flat rate fulfillment method restrictions. */ - getFlatRateFulfillmentRestrictions: FlatRateFulfillmentRestrictionConnection - /** Get a single flat rate fulfillment method restriction. */ - getFlatRateFulfillmentRestriction?: Maybe - /** List all tax codes supported by the current active tax service for the shop */ - taxCodes: Array> - /** Get a full list of all tax services for the shop */ - taxServices: Array> - /** Gets tax rates */ - taxRates?: Maybe - /** Returns a navigation tree by its ID in the specified language */ - navigationTreeById?: Maybe - /** Returns the navigation items for a shop */ - navigationItemsByShopId?: Maybe - /** Returns Sitemap object for a shop based on the handle param */ - sitemap?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryShopArgs = { - id: Scalars['ID'] -} - -/** Queries return all requested data, without any side effects */ -export type QueryShopBySlugArgs = { - slug: Scalars['String'] -} - -/** Queries return all requested data, without any side effects */ -export type QueryShopsArgs = { - shopIds?: Maybe>> - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe - sortOrder?: Maybe - sortBy?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryShopSettingsArgs = { - shopId: Scalars['ID'] -} - -/** Queries return all requested data, without any side effects */ -export type QueryAddressValidationArgs = { - address: AddressInput - shopId: Scalars['ID'] -} - -/** Queries return all requested data, without any side effects */ -export type QueryAddressValidationRulesArgs = { - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe - serviceNames?: Maybe>> - shopId: Scalars['ID'] - sortOrder?: Maybe - sortBy?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QuerySystemInformationArgs = { - shopId: Scalars['ID'] -} - -/** Queries return all requested data, without any side effects */ -export type QueryEmailTemplatesArgs = { - shopId: Scalars['ID'] - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryAccountArgs = { - id: Scalars['ID'] -} - -/** Queries return all requested data, without any side effects */ -export type QueryAccountsArgs = { - groupIds?: Maybe>> - notInAnyGroups?: Maybe - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe - sortOrder?: Maybe - sortBy?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryCustomersArgs = { - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe - sortOrder?: Maybe - sortBy?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryGroupArgs = { - id: Scalars['ID'] -} - -/** Queries return all requested data, without any side effects */ -export type QueryGroupsArgs = { - shopId: Scalars['ID'] - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe - sortOrder?: Maybe - sortBy?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryInvitationsArgs = { - shopIds?: Maybe>> - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe - sortOrder?: Maybe - sortBy?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryRolesArgs = { - shopId: Scalars['ID'] - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe - sortOrder?: Maybe - sortBy?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryProductArgs = { - productId: Scalars['ID'] - shopId: Scalars['ID'] -} - -/** Queries return all requested data, without any side effects */ -export type QueryProductsArgs = { - isArchived?: Maybe - isVisible?: Maybe - metafieldKey?: Maybe - metafieldValue?: Maybe - priceMax?: Maybe - priceMin?: Maybe - productIds?: Maybe>> - query?: Maybe - shopIds: Array> - tagIds?: Maybe>> - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe - sortOrder?: Maybe - sortBy?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryCatalogItemsArgs = { - shopIds: Array> - tagIds?: Maybe>> - booleanFilters?: Maybe>> - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe - sortOrder?: Maybe - sortByPriceCurrencyCode?: Maybe - sortBy?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryCatalogItemProductArgs = { - shopId?: Maybe - slugOrId?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryProductsByTagIdArgs = { - shopId: Scalars['ID'] - tagId: Scalars['ID'] - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryTagArgs = { - slugOrId: Scalars['String'] - shopId: Scalars['ID'] - shouldIncludeInvisible?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryTagsArgs = { - shopId: Scalars['ID'] - filter?: Maybe - excludedTagIds?: Maybe>> - isTopLevel?: Maybe - shouldIncludeDeleted?: Maybe - shouldIncludeInvisible?: Maybe - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe - sortOrder?: Maybe - sortBy?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QuerySimpleInventoryArgs = { - shopId: Scalars['ID'] - productConfiguration: ProductConfigurationInput -} - -/** Queries return all requested data, without any side effects */ -export type QueryAnonymousCartByCartIdArgs = { - cartId: Scalars['ID'] - cartToken: Scalars['String'] -} - -/** Queries return all requested data, without any side effects */ -export type QueryAccountCartByAccountIdArgs = { - accountId: Scalars['ID'] - shopId: Scalars['ID'] -} - -/** Queries return all requested data, without any side effects */ -export type QueryOrderByIdArgs = { - id: Scalars['ID'] - shopId: Scalars['ID'] - token?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryOrdersArgs = { - filters?: Maybe - shopIds?: Maybe>> - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe - sortOrder?: Maybe - sortBy?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryOrdersByAccountIdArgs = { - accountId: Scalars['ID'] - orderStatus?: Maybe>> - shopIds: Array> - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe - sortOrder?: Maybe - sortBy?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryOrderByReferenceIdArgs = { - id: Scalars['ID'] - shopId: Scalars['ID'] - token?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryRefundsArgs = { - orderId: Scalars['ID'] - shopId: Scalars['ID'] - token?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryRefundsByPaymentIdArgs = { - orderId: Scalars['ID'] - paymentId: Scalars['ID'] - shopId: Scalars['ID'] - token?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryAvailablePaymentMethodsArgs = { - shopId: Scalars['ID'] -} - -/** Queries return all requested data, without any side effects */ -export type QueryPaymentMethodsArgs = { - shopId: Scalars['ID'] -} - -/** Queries return all requested data, without any side effects */ -export type QueryDiscountCodesArgs = { - shopId: Scalars['ID'] - filters?: Maybe - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QuerySurchargesArgs = { - shopId: Scalars['ID'] - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe - sortOrder?: Maybe - sortBy?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QuerySurchargeByIdArgs = { - shopId: Scalars['ID'] - surchargeId: Scalars['ID'] -} - -/** Queries return all requested data, without any side effects */ -export type QueryFlatRateFulfillmentMethodArgs = { - methodId: Scalars['ID'] - shopId: Scalars['ID'] -} - -/** Queries return all requested data, without any side effects */ -export type QueryFlatRateFulfillmentMethodsArgs = { - shopId: Scalars['ID'] - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryGetFlatRateFulfillmentRestrictionsArgs = { - shopId: Scalars['ID'] - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe - sortOrder?: Maybe - sortBy?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryGetFlatRateFulfillmentRestrictionArgs = { - restrictionId: Scalars['ID'] - shopId: Scalars['ID'] -} - -/** Queries return all requested data, without any side effects */ -export type QueryTaxCodesArgs = { - shopId: Scalars['ID'] -} - -/** Queries return all requested data, without any side effects */ -export type QueryTaxServicesArgs = { - shopId: Scalars['ID'] -} - -/** Queries return all requested data, without any side effects */ -export type QueryTaxRatesArgs = { - shopId: Scalars['ID'] - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryNavigationTreeByIdArgs = { - id: Scalars['ID'] - language: Scalars['String'] - shopId: Scalars['ID'] - shouldIncludeSecondary?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QueryNavigationItemsByShopIdArgs = { - shopId: Scalars['ID'] - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe - sortOrder?: Maybe - sortBy?: Maybe -} - -/** Queries return all requested data, without any side effects */ -export type QuerySitemapArgs = { - handle: Scalars['String'] - shopUrl: Scalars['String'] -} - -/** Represents a Reaction shop */ -export type Shop = Node & { - __typename?: 'Shop' - /** The shop ID */ - _id: Scalars['ID'] - /** An the shop's default address */ - addressBook?: Maybe>> - /** Whether to allow user to checkout without creating an account */ - allowGuestCheckout?: Maybe - /** The base unit of length */ - baseUOL?: Maybe - /** The base unit of Measure */ - baseUOM?: Maybe - /** URLs for various shop assets in various sizes */ - brandAssets?: Maybe - /** The default shop currency */ - currency: Currency - /** Default parcel size for this shop */ - defaultParcelSize?: Maybe - /** Shop description */ - description?: Maybe - /** The shop's default email record */ - emails?: Maybe>> - /** Shop's keywords */ - keywords?: Maybe - /** Shop default language */ - language: Scalars['String'] - /** Shop name */ - name: Scalars['String'] - /** Returns URLs for shop logos */ - shopLogoUrls?: Maybe - /** Shop's type */ - shopType?: Maybe - /** Shop's slug */ - slug?: Maybe - /** Returns URLs for various storefront routes */ - storefrontUrls?: Maybe - /** Shop default timezone */ - timezone?: Maybe - /** The shop's units of length */ - unitsOfLength?: Maybe>> - /** The shop's units of measure */ - unitsOfMeasure?: Maybe>> - /** Returns a list of groups for this shop, as a Relay-compatible connection. */ - groups?: Maybe - /** Returns a list of roles for this shop, as a Relay-compatible connection. */ - roles?: Maybe - /** Returns a paged list of tags for this shop */ - tags?: Maybe - /** The default navigation tree for this shop */ - defaultNavigationTree?: Maybe - /** The ID of the shop's default navigation tree */ - defaultNavigationTreeId?: Maybe -} - -/** Represents a Reaction shop */ -export type ShopGroupsArgs = { - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe - sortOrder?: Maybe - sortBy?: Maybe -} - -/** Represents a Reaction shop */ -export type ShopRolesArgs = { - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe - sortOrder?: Maybe - sortBy?: Maybe -} - -/** Represents a Reaction shop */ -export type ShopTagsArgs = { - isTopLevel?: Maybe - shouldIncludeDeleted?: Maybe - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe - sortOrder?: Maybe - sortBy?: Maybe -} - -/** Represents a Reaction shop */ -export type ShopDefaultNavigationTreeArgs = { - language: Scalars['String'] - shouldIncludeSecondary?: Maybe -} - -/** Objects implementing the Node interface will always have an _id field that is globally unique. */ -export type Node = { - /** The ID of the object */ - _id: Scalars['ID'] -} - -/** Represents a physical or mailing address somewhere on Earth */ -export type Address = { - __typename?: 'Address' - /** The address ID */ - _id?: Maybe - /** The street address / first line */ - address1: Scalars['String'] - /** Optional second line */ - address2?: Maybe - /** City */ - city: Scalars['String'] - /** Optional company name, if it's a business address */ - company?: Maybe - /** Country */ - country: Scalars['String'] - /** - * The first name of a person at this address - * This is an optional field to support legacy and third party platforms - * We use fullName internally, and use first and last name fields to combine into a full name if needed - */ - firstName?: Maybe - /** The full name of a person at this address */ - fullName: Scalars['String'] - /** Is this the default address for billing purposes? */ - isBillingDefault?: Maybe - /** Is this a commercial address? */ - isCommercial: Scalars['Boolean'] - /** Is this the default address to use when selecting a shipping address at checkout? */ - isShippingDefault?: Maybe - /** - * The last name of a person at this address - * This is an optional field to support legacy and third party platforms - * We use fullName internally, and use first and last name fields to combine into a full name if needed - */ - lastName?: Maybe - /** Arbitrary additional metadata about this address */ - metafields?: Maybe>> - /** A phone number for someone at this address */ - phone: Scalars['String'] - /** Postal code */ - postal: Scalars['String'] - /** Region. For example, a U.S. state */ - region: Scalars['String'] -} - -/** User defined attributes */ -export type Metafield = { - __typename?: 'Metafield' - /** Field description */ - description?: Maybe - /** Field key */ - key?: Maybe - /** Field namespace */ - namespace?: Maybe - /** Field scope */ - scope?: Maybe - /** Field value */ - value?: Maybe - /** Field value type */ - valueType?: Maybe -} - -/** URLs for various shop assets in various sizes */ -export type ShopBrandAssets = { - __typename?: 'ShopBrandAssets' - /** URLs for the navigation bar brand logo image */ - navbarBrandImage?: Maybe - /** Internal navigation bar brand logo image ID */ - navbarBrandImageId?: Maybe -} - -/** A list of URLs for various sizes of an image */ -export type ImageSizes = { - __typename?: 'ImageSizes' - /** Use this URL to get a large resolution file for this image */ - large?: Maybe - /** Use this URL to get a medium resolution file for this image */ - medium?: Maybe - /** - * Use this URL to get this image with its original resolution as uploaded. This may not be - * the true original size if there is a hard cap on how big image files can be. - */ - original?: Maybe - /** Use this URL to get a small resolution file for this image */ - small?: Maybe - /** Use this URL to get a thumbnail resolution file for this image */ - thumbnail?: Maybe -} - -/** Represents one type of currency */ -export type Currency = Node & { - __typename?: 'Currency' - /** ID */ - _id: Scalars['ID'] - /** Currency code */ - code: Scalars['String'] - /** Decimal symbol */ - decimal?: Maybe - /** Format string */ - format: Scalars['String'] - /** Exchange rate from shop default currency, if known */ - rate?: Maybe - /** The decimal scale used by this currency */ - scale?: Maybe - /** Currency symbol */ - symbol: Scalars['String'] - /** Thousands separator symbol */ - thousand?: Maybe -} - -/** Parcel size */ -export type ShopParcelSize = { - __typename?: 'ShopParcelSize' - /** Parcel height */ - height?: Maybe - /** Parcel length */ - length?: Maybe - /** Parcel weight */ - weight?: Maybe - /** Parcel width */ - width?: Maybe -} - -/** A confirmable email record */ -export type EmailRecord = { - __typename?: 'EmailRecord' - /** The actual email address */ - address?: Maybe - /** The services provided by this address */ - provides?: Maybe - /** Has this address been verified? */ - verified?: Maybe -} - -/** Shop logo URLs */ -export type ShopLogoUrls = { - __typename?: 'ShopLogoUrls' - /** The primary logo URL for this shop. Setting this overrides any uploaded logo. */ - primaryShopLogoUrl?: Maybe -} - -/** Storefront route URLs */ -export type StorefrontUrls = { - __typename?: 'StorefrontUrls' - /** Storefront Account Profile URL (can include `:accountId` in string) */ - storefrontAccountProfileUrl?: Maybe - /** Storefront Home URL */ - storefrontHomeUrl?: Maybe - /** Storefront login URL */ - storefrontLoginUrl?: Maybe - /** Storefront single order URL (can include `:orderReferenceId` and `:orderToken` in string) */ - storefrontOrderUrl?: Maybe - /** Storefront orders URL (can include `:accountId` in string) */ - storefrontOrdersUrl?: Maybe -} - -/** Units of length */ -export type UnitOfLength = { - __typename?: 'UnitOfLength' - /** Whether this unit of length is the default */ - default?: Maybe - /** The name of the unit of length */ - label?: Maybe - /** Unit of length */ - uol?: Maybe -} - -/** Units of measure */ -export type UnitOfMeasure = { - __typename?: 'UnitOfMeasure' - /** Whether this unit of measure is the default */ - default?: Maybe - /** The name of the unit of measure */ - label?: Maybe - /** Unit of measure */ - uom?: Maybe -} - -/** The order in which the connection results should be sorted, based on the sortBy field. */ -export enum SortOrder { - /** ascending */ - Asc = 'asc', - /** descending */ - Desc = 'desc', -} - -/** The fields by which you are allowed to sort any query that returns an `GroupConnection` */ -export enum GroupSortByField { - /** Group ID */ - Id = '_id', - /** Date and time at which this group was created */ - CreatedAt = 'createdAt', - /** Group name */ - Name = 'name', - /** Date and time at which this group was last updated */ - UpdatedAt = 'updatedAt', -} - -/** - * Wraps a list of `Groups`, providing pagination cursors and information. - * - * For information about what Relay-compatible connections are and how to use them, see the following articles: - * - [Relay Connection Documentation](https://facebook.github.io/relay/docs/en/graphql-server-specification.html#connections) - * - [Relay Connection Specification](https://facebook.github.io/relay/graphql/connections.htm) - * - [Using Relay-style Connections With Apollo Client](https://www.apollographql.com/docs/react/recipes/pagination.html) - */ -export type GroupConnection = { - __typename?: 'GroupConnection' - /** The list of nodes that match the query, wrapped in an edge to provide a cursor string for each */ - edges?: Maybe>> - /** - * You can request the `nodes` directly to avoid the extra wrapping that `NodeEdge` has, - * if you know you will not need to paginate the results. - */ - nodes?: Maybe>> - /** Information to help a client request the next or previous page */ - pageInfo: PageInfo - /** The total number of nodes that match your query */ - totalCount: Scalars['Int'] -} - -/** A connection edge in which each node is a `Group` object */ -export type GroupEdge = NodeEdge & { - __typename?: 'GroupEdge' - /** The cursor that represents this node in the paginated results */ - cursor: Scalars['ConnectionCursor'] - /** The group */ - node?: Maybe -} - -/** - * Objects implementing the NodeEdge interface will always have a node and a cursor - * that represents that node for purposes of requesting paginated results. - */ -export type NodeEdge = { - /** The cursor that represents this node in the paginated results */ - cursor: Scalars['ConnectionCursor'] - /** The node itself */ - node?: Maybe -} - -/** Represents an account group */ -export type Group = Node & { - __typename?: 'Group' - /** The group ID */ - _id: Scalars['ID'] - /** The date and time at which this group was created */ - createdAt: Scalars['DateTime'] - /** The account that created this group */ - createdBy?: Maybe - /** A free text description of this group */ - description?: Maybe - /** A unique name for the group */ - name: Scalars['String'] - /** The shop to which this group belongs */ - shop?: Maybe - /** A unique URL-safe string representing this group */ - slug: Scalars['String'] - /** The date and time at which this group was last updated */ - updatedAt: Scalars['DateTime'] - /** A list of the account permissions implied by membership in this group */ - permissions?: Maybe>> + /** A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the `date-time` format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar. */ + DateTime: any + /** A string email address */ + Email: any + /** An object with any fields */ + JSONObject: any } /** Represents a single user account */ @@ -1000,6 +102,246 @@ export type AccountGroupsArgs = { sortBy?: Maybe } +/** + * Wraps a list of `Accounts`, providing pagination cursors and information. + * + * For information about what Relay-compatible connections are and how to use them, see the following articles: + * - [Relay Connection Documentation](https://facebook.github.io/relay/docs/en/graphql-server-specification.html#connections) + * - [Relay Connection Specification](https://facebook.github.io/relay/graphql/connections.htm) + * - [Using Relay-style Connections With Apollo Client](https://www.apollographql.com/docs/react/recipes/pagination.html) + */ +export type AccountConnection = { + __typename?: 'AccountConnection' + /** The list of nodes that match the query, wrapped in an edge to provide a cursor string for each */ + edges?: Maybe>> + /** + * You can request the `nodes` directly to avoid the extra wrapping that `NodeEdge` has, + * if you know you will not need to paginate the results. + */ + nodes?: Maybe>> + /** Information to help a client request the next or previous page */ + pageInfo: PageInfo + /** The total number of nodes that match your query */ + totalCount: Scalars['Int'] +} + +/** A connection edge in which each node is an `Account` object */ +export type AccountEdge = NodeEdge & { + __typename?: 'AccountEdge' + /** The cursor that represents this node in the paginated results */ + cursor: Scalars['ConnectionCursor'] + /** The account */ + node?: Maybe +} + +/** The fields by which you are allowed to sort any query that returns an `AccountConnection` */ +export enum AccountSortByField { + /** Account ID */ + Id = '_id', + /** Date and time at which this account was created */ + CreatedAt = 'createdAt', + /** Date and time at which this account was last updated */ + UpdatedAt = 'updatedAt', +} + +/** Defines a new Address and the account to which it should be added */ +export type AddAccountAddressBookEntryInput = { + /** The account ID */ + accountId: Scalars['ID'] + /** The address to add */ + address: AddressInput + /** An optional string identifying the mutation call, which will be returned in the response payload */ + clientMutationId?: Maybe +} + +/** The response from the `addAccountAddressBookEntry` mutation */ +export type AddAccountAddressBookEntryPayload = { + __typename?: 'AddAccountAddressBookEntryPayload' + /** The added address */ + address?: Maybe
+ /** The added address edge */ + addressEdge?: Maybe + /** The same string you sent with the mutation params, for matching mutation calls with their responses */ + clientMutationId?: Maybe +} + +/** Defines a new Email and the account to which it should be added */ +export type AddAccountEmailRecordInput = { + /** The account ID, which defaults to the viewer account */ + accountId?: Maybe + /** An optional string identifying the mutation call, which will be returned in the response payload */ + clientMutationId?: Maybe + /** The email address to add */ + email: Scalars['Email'] +} + +/** The response from the `addAccountEmailRecord` mutation */ +export type AddAccountEmailRecordPayload = { + __typename?: 'AddAccountEmailRecordPayload' + /** The account, with updated `emailRecords` */ + account?: Maybe + /** The same string you sent with the mutation params, for matching mutation calls with their responses */ + clientMutationId?: Maybe +} + +/** Defines a group and account that should be linked */ +export type AddAccountToGroupInput = { + /** The account ID */ + accountId: Scalars['ID'] + /** An optional string identifying the mutation call, which will be returned in the response payload */ + clientMutationId?: Maybe + /** The group ID */ + groupId: Scalars['ID'] +} + +/** The response from the `addAccountToGroup` mutation */ +export type AddAccountToGroupPayload = { + __typename?: 'AddAccountToGroupPayload' + /** The same string you sent with the mutation params, for matching mutation calls with their responses */ + clientMutationId?: Maybe + /** The updated group */ + group?: Maybe +} + +/** Input for the `addCartItems` mutation */ +export type AddCartItemsInput = { + /** The cart ID */ + cartId: Scalars['ID'] + /** If this cart is anonymous, provide the `cartToken` that was returned in the `CreateCartPayload` */ + cartToken?: Maybe + /** An optional string identifying the mutation call, which will be returned in the response payload */ + clientMutationId?: Maybe + /** Array of items to be added to the cart */ + items: Array> +} + +/** The payload returned from the `addCartItems` mutation call */ +export type AddCartItemsPayload = { + __typename?: 'AddCartItemsPayload' + /** + * The modified cart. You should check `incorrectPriceFailures` and `minOrderQuantityFailures` for + * information necessary to display errors to the shopper. Some items may not have been added. + */ + cart?: Maybe + /** The same string you sent with the mutation params, for matching mutation calls with their responses */ + clientMutationId?: Maybe + /** + * Clients should check to see if any items failed to be added due to the price not matching the current price. + * In general, a user interface should display the correct current prices to the shopper, confirm that they still + * want to add the items, and then call `createCart` or `addCartItems` to do so. + * + * Note that this field will always exist but may be an empty array if there were no failures of this type. + */ + incorrectPriceFailures: Array> + /** + * Clients should check to see if any items failed to be added due to quantity being below the minimum order + * quantity defined for the product variant. In general, a user interface should display the minimum order + * quantity to the shopper and allow them to add that quantity or greater. + * + * Note that this field will always exist but may be an empty array if there were no failures of this type. + */ + minOrderQuantityFailures: Array> +} + +/** Input for the addOrderFulfillmentGroup mutation */ +export type AddOrderFulfillmentGroupInput = { + /** An optional string identifying the mutation call, which will be returned in the response payload */ + clientMutationId?: Maybe + /** The order fulfillment group input, used to build the new group */ + fulfillmentGroup: OrderFulfillmentGroupExistingOrderInput + /** Optional list of order item IDs that should be moved from an existing group to the new group */ + moveItemIds?: Maybe>> + /** ID of the order that has the item you want to add the group to */ + orderId: Scalars['ID'] +} + +/** Response payload for the addOrderFulfillmentGroup mutation */ +export type AddOrderFulfillmentGroupPayload = { + __typename?: 'AddOrderFulfillmentGroupPayload' + /** The same string you sent with the mutation params, for matching mutation calls with their responses */ + clientMutationId?: Maybe + /** ID of the added fulfillment group */ + newFulfillmentGroupId: Scalars['ID'] + /** The updated order */ + order: Order +} + +/** Input for `addTag` mutation */ +export type AddTagInput = { + /** An optional string identifying the mutation call, which will be returned in the response payload */ + clientMutationId?: Maybe + /** Title to display to customers */ + displayTitle?: Maybe + /** Hero media URL */ + heroMediaUrl?: Maybe + /** Whether the tag is visible */ + isVisible: Scalars['Boolean'] + /** Tag metafields */ + metafields?: Maybe>> + /** Unique name of the tag */ + name: Scalars['String'] + /** The shop that owns the tag */ + shopId: Scalars['ID'] + /** The tag slug. If left blank, the name will be slugified and saved as the slug */ + slug?: Maybe +} + +/** Response payload for `addTag` mutation */ +export type AddTagPayload = { + __typename?: 'AddTagPayload' + /** The same string you sent with the mutation params, for matching mutation calls with their responses */ + clientMutationId?: Maybe + /** The shop that owns the tag */ + shopId: Scalars['ID'] + /** The newly-created tag */ + tag: Tag +} + +/** Represents a physical or mailing address somewhere on Earth */ +export type Address = { + __typename?: 'Address' + /** The address ID */ + _id?: Maybe + /** The street address / first line */ + address1: Scalars['String'] + /** Optional second line */ + address2?: Maybe + /** City */ + city: Scalars['String'] + /** Optional company name, if it's a business address */ + company?: Maybe + /** Country */ + country: Scalars['String'] + /** + * The first name of a person at this address + * This is an optional field to support legacy and third party platforms + * We use fullName internally, and use first and last name fields to combine into a full name if needed + */ + firstName?: Maybe + /** The full name of a person at this address */ + fullName: Scalars['String'] + /** Is this the default address for billing purposes? */ + isBillingDefault?: Maybe + /** Is this a commercial address? */ + isCommercial: Scalars['Boolean'] + /** Is this the default address to use when selecting a shipping address at checkout? */ + isShippingDefault?: Maybe + /** + * The last name of a person at this address + * This is an optional field to support legacy and third party platforms + * We use fullName internally, and use first and last name fields to combine into a full name if needed + */ + lastName?: Maybe + /** Arbitrary additional metadata about this address */ + metafields?: Maybe>> + /** A phone number for someone at this address */ + phone: Scalars['String'] + /** Postal code */ + postal: Scalars['String'] + /** Region. For example, a U.S. state */ + region: Scalars['String'] +} + /** * Wraps a list of `Addresses`, providing pagination cursors and information. * @@ -1032,351 +374,6 @@ export type AddressEdge = { node?: Maybe
} -/** - * Pagination information. When requesting pages of results, you can use endCursor or startCursor - * as your before or after parameters for the query you are paging. - */ -export type PageInfo = { - __typename?: 'PageInfo' - /** When paginating forwards, the cursor to continue. */ - endCursor?: Maybe - /** When paginating forwards, are there more items? */ - hasNextPage: Scalars['Boolean'] - /** When paginating backwards, are there more items? */ - hasPreviousPage: Scalars['Boolean'] - /** When paginating backwards, the cursor to continue. */ - startCursor?: Maybe -} - -/** The fields by which you are allowed to sort any query that returns an `RoleConnection` */ -export enum RoleSortByField { - /** Role ID */ - Id = '_id', - /** Role name */ - Name = 'name', -} - -/** - * Wraps a list of `Roles`, providing pagination cursors and information. - * - * For information about what Relay-compatible connections are and how to use them, see the following articles: - * - [Relay Connection Documentation](https://facebook.github.io/relay/docs/en/graphql-server-specification.html#connections) - * - [Relay Connection Specification](https://facebook.github.io/relay/graphql/connections.htm) - * - [Using Relay-style Connections With Apollo Client](https://www.apollographql.com/docs/react/recipes/pagination.html) - */ -export type RoleConnection = { - __typename?: 'RoleConnection' - /** The list of nodes that match the query, wrapped in an edge to provide a cursor string for each */ - edges?: Maybe>> - /** - * You can request the `nodes` directly to avoid the extra wrapping that `NodeEdge` has, - * if you know you will not need to paginate the results. - */ - nodes?: Maybe>> - /** Information to help a client request the next or previous page */ - pageInfo: PageInfo - /** The total number of nodes that match your query */ - totalCount: Scalars['Int'] -} - -/** A connection edge in which each node is a `Role` object */ -export type RoleEdge = NodeEdge & { - __typename?: 'RoleEdge' - /** The cursor that represents this node in the paginated results */ - cursor: Scalars['ConnectionCursor'] - /** The role */ - node?: Maybe -} - -/** Represents a named role */ -export type Role = Node & { - __typename?: 'Role' - /** The role ID */ - _id: Scalars['ID'] - /** A unique name for the role */ - name: Scalars['String'] -} - -/** The fields by which you are allowed to sort any query that returns a `TagConnection` */ -export enum TagSortByField { - /** Tag ID */ - Id = '_id', - /** Date and time the tag was created */ - CreatedAt = 'createdAt', - /** Tag name */ - Name = 'name', - /** Tag position */ - Position = 'position', - /** Date and time the tag was last updated */ - UpdatedAt = 'updatedAt', -} - -/** - * Wraps a list of `Tags`, providing pagination cursors and information. - * - * For information about what Relay-compatible connections are and how to use them, see the following articles: - * - [Relay Connection Documentation](https://facebook.github.io/relay/docs/en/graphql-server-specification.html#connections) - * - [Relay Connection Specification](https://facebook.github.io/relay/graphql/connections.htm) - * - [Using Relay-style Connections With Apollo Client](https://www.apollographql.com/docs/react/recipes/pagination.html) - */ -export type TagConnection = { - __typename?: 'TagConnection' - /** The list of nodes that match the query, wrapped in an edge to provide a cursor string for each */ - edges?: Maybe>> - /** - * You can request the `nodes` directly to avoid the extra wrapping that `NodeEdge` has, - * if you know you will not need to paginate the results. - */ - nodes?: Maybe>> - /** Information to help a client request the next or previous page */ - pageInfo: PageInfo - /** The total number of nodes that match your query */ - totalCount: Scalars['Int'] -} - -/** A connection edge in which each node is a `Tag` object */ -export type TagEdge = NodeEdge & { - __typename?: 'TagEdge' - /** The cursor that represents this node in the paginated results */ - cursor: Scalars['ConnectionCursor'] - /** The tag */ - node?: Maybe -} - -/** Represents a single tag */ -export type Tag = Node & - Deletable & { - __typename?: 'Tag' - /** The tag ID */ - _id: Scalars['ID'] - /** The date and time at which this tag was created */ - createdAt: Scalars['DateTime'] - /** A string of the title to be displayed on a Tag Listing Page */ - displayTitle?: Maybe - /** A list of the IDs of top products in this tag */ - featuredProductIds?: Maybe>> - /** A string containing the hero image url for a Tag Listing Page */ - heroMediaUrl?: Maybe - /** - * If `true`, this object should be considered deleted. Soft deleted objects are not - * returned in query results unless you explicitly ask for them. - */ - isDeleted: Scalars['Boolean'] - /** If `true`, this tag should be shown at the top level of the tag hierarchy */ - isTopLevel: Scalars['Boolean'] - /** If `true`, this tag's Tag Listing Page should be visible to the public */ - isVisible: Scalars['Boolean'] - /** Arbitrary additional metadata about this tag */ - metafields?: Maybe>> - /** The display name for the tag. This is unique within a given shop. */ - name: Scalars['String'] - /** The tag's position relative to other tags at the same level of the tag hierarchy */ - position?: Maybe - /** The shop to which this tag belongs */ - shop: Shop - /** A unique URL-safe string representing this tag for links */ - slug?: Maybe - /** A list of the IDs of tags that have this tag as their parent in the tag hierarchy, in the user-defined order */ - subTagIds: Array> - /** The date and time at which this tag was last updated */ - updatedAt: Scalars['DateTime'] - /** A paged list of tags that have this tag as their parent in the tag hierarchy. Currently only three levels are supported. */ - subTags?: Maybe - } - -/** Represents a single tag */ -export type TagSubTagsArgs = { - after?: Maybe - before?: Maybe - first?: Maybe - last?: Maybe - offset?: Maybe - sortOrder?: Maybe - sortBy?: Maybe -} - -/** Objects implementing the Deletable support soft deletion */ -export type Deletable = { - /** - * If true, this object should be considered deleted. Soft deleted objects are not - * returned in query results unless you explicitly ask for them. - */ - isDeleted: Scalars['Boolean'] -} - -/** Represents a navigation tree containing multiple levels of navigation items */ -export type NavigationTree = Node & { - __typename?: 'NavigationTree' - /** The navigation tree ID */ - _id: Scalars['ID'] - /** The draft navigation items that make up this tree */ - draftItems?: Maybe>> - /** Whether the navigation item has unpublished changes */ - hasUnpublishedChanges?: Maybe - /** The published navigation items that make up this tree */ - items?: Maybe>> - /** The name of the tree, for operator display purposes. Assumed to be in the primary shop's language */ - name: Scalars['String'] - /** The ID of the shop this navigation tree belongs to */ - shopId: Scalars['ID'] -} - -/** Represents a navigation item and its children in a tree */ -export type NavigationTreeItem = { - __typename?: 'NavigationTreeItem' - /** Whether the navigation item should display its children */ - expanded?: Maybe - /** Whether the navigation item should be hidden from customers */ - isPrivate?: Maybe - /** Whether the navigaton item is a secondary navigation item */ - isSecondary?: Maybe - /** Whether the navigation ttem should shown in query results for customers and admins */ - isVisible?: Maybe - /** The child navigation items */ - items?: Maybe>> - /** The navigation item */ - navigationItem: NavigationItem -} - -/** Represents a single navigation item */ -export type NavigationItem = Node & { - __typename?: 'NavigationItem' - /** The navigation item ID */ - _id: Scalars['ID'] - /** The date and time at which this navigation item was created */ - createdAt: Scalars['DateTime'] - /** The published data for this navigation item */ - data?: Maybe - /** The draft/unpublished data for this navigation item */ - draftData?: Maybe - /** Whether the navigation item has unpublished changes */ - hasUnpublishedChanges?: Maybe - /** An object storing additional metadata about the navigation item (such as its related tag) */ - metadata?: Maybe - /** The ID of the shop the navigation item belongs to */ - shopId: Scalars['ID'] -} - -/** Represents the data for a navigation item */ -export type NavigationItemData = { - __typename?: 'NavigationItemData' - /** CSS class names to add to the menu item for display */ - classNames?: Maybe - /** The content for the navigation item, in one or more languages */ - content?: Maybe>> - /** The translated content for a navigation item */ - contentForLanguage?: Maybe - /** Whether the provided URL is relative or external */ - isUrlRelative?: Maybe - /** Whether the navigation item should trigger a new tab/window to open when clicked */ - shouldOpenInNewWindow?: Maybe - /** The URL for the navigation item to link to */ - url?: Maybe -} - -/** Represents the translated content for a navigation item */ -export type NavigationItemContent = { - __typename?: 'NavigationItemContent' - /** The language of the piece of navigation content */ - language: Scalars['String'] - /** The translated value, in plain text or markdown */ - value?: Maybe -} - -/** - * Wraps a list of `Shops`, providing pagination cursors and information. - * - * For information about what Relay-compatible connections are and how to use them, see the following articles: - * - [Relay Connection Documentation](https://facebook.github.io/relay/docs/en/graphql-server-specification.html#connections) - * - [Relay Connection Specification](https://facebook.github.io/relay/graphql/connections.htm) - * - [Using Relay-style Connections With Apollo Client](https://www.apollographql.com/docs/react/recipes/pagination.html) - */ -export type ShopConnection = { - __typename?: 'ShopConnection' - /** The list of nodes that match the query, wrapped in an edge to provide a cursor string for each */ - edges?: Maybe>> - /** - * You can request the `nodes` directly to avoid the extra wrapping that `NodeEdge` has, - * if you know you will not need to paginate the results. - */ - nodes?: Maybe>> - /** Information to help a client request the next or previous page */ - pageInfo: PageInfo - /** The total number of nodes that match your query */ - totalCount: Scalars['Int'] -} - -/** A connection edge in which each node is an `Shop` object */ -export type ShopEdge = NodeEdge & { - __typename?: 'ShopEdge' - /** The cursor that represents this node in the paginated results */ - cursor: Scalars['ConnectionCursor'] - /** The Shop */ - node?: Maybe -} - -/** - * App settings that are not shop specific. Plugins extend the GlobalSettings type to support - * whatever settings they need. - */ -export type GlobalSettings = { - __typename?: 'GlobalSettings' - /** A fake setting necessary until some plugin extends this with a real setting */ - doNotUse?: Maybe -} - -/** - * App settings for a specific shop. Plugins extend the ShopSettings type to support - * whatever settings they need. - */ -export type ShopSettings = { - __typename?: 'ShopSettings' - /** A fake setting necessary until some plugin extends this with a real setting */ - doNotUse?: Maybe - /** - * If there is no known inventory for a product configuration, this setting determines - * whether that product configuration can be sold and should appear to be available. - */ - canSellVariantWithoutInventory: Scalars['Boolean'] - /** - * If `false` no defined shipping rates will be used when fulfillment - * quotes are requested for a cart or order. A quick way to disable the entire - * `reaction-shipping-rates` plugin temporarily. - */ - isShippingRatesFulfillmentEnabled?: Maybe - /** The default value to use for `taxCode` property of a product */ - defaultTaxCode?: Maybe - /** - * The name of the tax service to fall back to if the primary tax service is down. - * This will match the `name` field of one of the services returned by the `taxServices` - * query. - */ - fallbackTaxServiceName?: Maybe - /** - * The name of the tax service to use for calculating taxes on carts and orders. - * This will match the `name` field of one of the services returned by the `taxServices` - * query. - */ - primaryTaxServiceName?: Maybe - /** - * Whether a navigation item added to the navigation tree should be visible only to - * admins by default. - */ - shouldNavigationTreeItemsBeAdminOnly: Scalars['Boolean'] - /** - * Whether a navigation item added to the navigation tree should be - * public API/Storefront visible by default. - */ - shouldNavigationTreeItemsBePubliclyVisible: Scalars['Boolean'] - /** - * Whether a navigation item added to the navigation tree should be a secondary - * navigation item by default. - */ - shouldNavigationTreeItemsBeSecondaryNavOnly: Scalars['Boolean'] - /** This setting controls how often the sitemaps for the shop will be rebuilt */ - sitemapRefreshPeriod: Scalars['String'] -} - /** The details of an `Address` to be created or updated */ export type AddressInput = { /** The street address / first line */ @@ -1421,20 +418,25 @@ export type AddressInput = { region: Scalars['String'] } -/** User defined attributes. You can include only `key` and use these like tags, or also include a `value`. */ -export type MetafieldInput = { - /** Field description */ - description?: Maybe - /** Field key */ - key: Scalars['String'] - /** Field namespace */ - namespace?: Maybe - /** Field scope */ - scope?: Maybe - /** Field value */ - value?: Maybe - /** Field value type */ - valueType?: Maybe +/** A list of the possible types of `Address` */ +export enum AddressType { + /** Address can be used for payment transactions and invoicing */ + Billing = 'billing', + /** Address can be used as a mailing address for sending physical items */ + Shipping = 'shipping', +} + +/** Details about an error that was the result of validating an address that is invalid */ +export type AddressValidationError = { + __typename?: 'AddressValidationError' + /** A longer, detailed error message suitable for showing in the user interface */ + details?: Maybe + /** An identifier of the source of this error. These are not currently standardized. As long as your client understands it, any string is fine. */ + source?: Maybe + /** A short error message suitable for showing in the user interface */ + summary: Scalars['String'] + /** The error type. These are not currently standardized. As long as your client understands it, any string is fine. */ + type: Scalars['String'] } /** The response from `Query.addressValidation` */ @@ -1452,57 +454,27 @@ export type AddressValidationResults = { validationErrors: Array> } -/** An address suggestion returned from an address validation service */ -export type SuggestedAddress = { - __typename?: 'SuggestedAddress' - /** The street address / first line */ - address1: Scalars['String'] - /** Optional second line */ - address2?: Maybe - /** City */ - city: Scalars['String'] - /** Country */ - country: Scalars['String'] - /** Postal code */ - postal: Scalars['String'] - /** Region. For example, a U.S. state */ - region: Scalars['String'] -} - -/** Details about an error that was the result of validating an address that is invalid */ -export type AddressValidationError = { - __typename?: 'AddressValidationError' - /** A longer, detailed error message suitable for showing in the user interface */ - details?: Maybe - /** An identifier of the source of this error. These are not currently standardized. As long as your client understands it, any string is fine. */ - source?: Maybe - /** A short error message suitable for showing in the user interface */ - summary: Scalars['String'] - /** The error type. These are not currently standardized. As long as your client understands it, any string is fine. */ - type: Scalars['String'] -} - -/** A single registered address validation service */ -export type AddressValidationService = { - __typename?: 'AddressValidationService' - /** Human-readable name to show operators */ - displayName: Scalars['String'] - /** Unique name to serve as a key identifying this service */ - name: Scalars['String'] - /** An optional list of all country codes that this address service supports. Null means all countries. */ - supportedCountryCodes?: Maybe>> -} - -/** The fields by which you are allowed to sort any query that returns an `AddressValidationRuleConnection` */ -export enum AddressValidationRuleSortByField { - /** AddressValidationRule ID */ - Id = '_id', - /** Date and time at which the rule was created */ - CreatedAt = 'createdAt', - /** Service name */ - ServiceName = 'serviceName', - /** Date and time at which the rule was last updated */ - UpdatedAt = 'updatedAt', +/** + * An address validation rule specifies which validation services should run for + * which countries in each shop. + */ +export type AddressValidationRule = Node & { + __typename?: 'AddressValidationRule' + /** The rule ID */ + _id: Scalars['ID'] + /** Country codes for which this service is enabled */ + countryCodes?: Maybe>> + /** The date and time at which this rule was created */ + createdAt: Scalars['DateTime'] + /** + * The name of one of the installed validation services. Use `addressValidationServices` + * query to get a list with more details about all installed services. + */ + serviceName: Scalars['String'] + /** ID of the shop to which this rule applies */ + shopId: Scalars['ID'] + /** The date and time at which this rule was last updated */ + updatedAt: Scalars['DateTime'] } /** @@ -1537,265 +509,401 @@ export type AddressValidationRuleEdge = NodeEdge & { node?: Maybe } -/** - * An address validation rule specifies which validation services should run for - * which countries in each shop. - */ -export type AddressValidationRule = Node & { - __typename?: 'AddressValidationRule' - /** The rule ID */ - _id: Scalars['ID'] - /** Country codes for which this service is enabled */ - countryCodes?: Maybe>> - /** The date and time at which this rule was created */ - createdAt: Scalars['DateTime'] - /** - * The name of one of the installed validation services. Use `addressValidationServices` - * query to get a list with more details about all installed services. - */ - serviceName: Scalars['String'] - /** ID of the shop to which this rule applies */ - shopId: Scalars['ID'] - /** The date and time at which this rule was last updated */ - updatedAt: Scalars['DateTime'] -} - -/** Represents Reaction System Infomation */ -export type SystemInformation = { - __typename?: 'SystemInformation' - /** Core api version */ - apiVersion: Scalars['String'] - /** Mongo version */ - mongoVersion: DatabaseInformation - /** Plugins installed with name, version information */ - plugins?: Maybe>> -} - -/** Represents Mongo Database information */ -export type DatabaseInformation = { - __typename?: 'DatabaseInformation' - /** Version of database */ - version: Scalars['String'] -} - -/** Represents Reaction Plugin */ -export type Plugin = { - __typename?: 'Plugin' - /** Name of plugin */ - name: Scalars['String'] - /** Version of plugin */ - version?: Maybe -} - -/** - * Wraps a list of Templates, providing pagination cursors and information. - * - * For information about what Relay-compatible connections are and how to use them, see the following articles: - * - [Relay Connection Documentation](https://facebook.github.io/relay/docs/en/graphql-server-specification.html#connections) - * - [Relay Connection Specification](https://facebook.github.io/relay/graphql/connections.htm) - * - [Using Relay-style Connections With Apollo Client](https://www.apollographql.com/docs/react/recipes/pagination.html) - */ -export type TemplateConnection = { - __typename?: 'TemplateConnection' - /** The list of nodes that match the query, wrapped in an edge to provide a cursor string for each */ - edges?: Maybe>> - /** - * You can request the `nodes` directly to avoid the extra wrapping that `NodeEdge` has, - * if you know you will not need to paginate the results. - */ - nodes?: Maybe>> - /** Information to help a client request the next or previous page */ - pageInfo: PageInfo - /** The total number of nodes that match your query */ - totalCount: Scalars['Int'] -} - -/** A connection edge in which each node is a `Template` object */ -export type TemplateEdge = { - __typename?: 'TemplateEdge' - /** The cursor that represents this node in the paginated results */ - cursor: Scalars['ConnectionCursor'] - /** The email template */ - node?: Maybe