From fa8f0904c2f9d667641d0ea952de01f9dadcde91 Mon Sep 17 00:00:00 2001 From: Junaid Date: Wed, 10 Aug 2022 00:43:29 -0500 Subject: [PATCH] Initial commit --- .gitignore | 38 + .prettierignore | 1 + CHANGELOG.md | 239 + LICENSE.md | 661 ++ README.md | 94 + access.js | 191 + components/AppShell.js | 994 ++ components/ChannelGrid/dangerous/index.js | 143 + components/ChannelGrid/details/EditDetails.js | 156 + components/ChannelGrid/details/index.js | 95 + .../ChannelGrid/functions/EditFunction.js | 161 + components/ChannelGrid/functions/index.js | 54 + components/ChannelGrid/index.js | 67 + .../ChannelGrid/metafields/EditMetafield.js | 294 + components/ChannelGrid/metafields/index.js | 232 + components/ChannelGrid/orders/index.js | 280 + .../ChannelGrid/webhooks/EditWebhook.js | 294 + components/ChannelGrid/webhooks/index.js | 444 + components/CreateItemDrawer.js | 164 + .../CreateChannelView/ChannelForm.js | 154 + .../CreateViews/CreateChannelView/index.js | 247 + .../CreateMatchView/ChannelProductSearch.js | 557 ++ .../CreateMatchView/ChannelSelect.js | 123 + .../CreateViews/CreateMatchView/FindMatch.js | 227 + .../CreateMatchView/ShopProductSearch.js | 542 + .../CreateViews/CreateMatchView/ShopSelect.js | 123 + .../CreateViews/CreateMatchView/index.js | 274 + .../CreateOrderView/CartItemSelect.js | 473 + .../CreateOrderView/ChannelSelect.js | 123 + .../CreateOrderView/LineItemSelect.js | 434 + .../CreateViews/CreateOrderView/ShopSelect.js | 123 + .../CreateViews/CreateOrderView/index.js | 440 + .../CreateViews/CreateShopView/ShopForm.js | 78 + .../CreateViews/CreateShopView/index.js | 223 + components/OSOrders/editCartItem/index.js | 147 + components/OSOrders/editOrder/index.js | 539 + components/OSOrders/index.js | 425 + components/OSOrders/osOrderList.js | 959 ++ components/OSOrders/previousCarts/CartList.js | 111 + components/OSOrders/previousCarts/index.js | 37 + components/Patterns.js | 167 + .../ProductSearch/ChannelProductSearch.js | 35 + components/ProductSearch/ProductList.js | 85 + components/ProductSearch/Search.js | 234 + components/ProductSearch/ShopProductSearch.js | 62 + components/ProductSearch/editProduct/index.js | 139 + components/ProductSearch/index.js | 4 + components/ShopGrid/dangerous/index.js | 147 + components/ShopGrid/details/EditDetails.js | 157 + components/ShopGrid/details/index.js | 95 + components/ShopGrid/functions/EditFunction.js | 163 + components/ShopGrid/functions/index.js | 61 + components/ShopGrid/index.js | 71 + components/ShopGrid/links/EditLink.js | 375 + components/ShopGrid/links/index.js | 232 + components/ShopGrid/orders/index.js | 332 + components/ShopGrid/webhooks/EditWebhook.js | 297 + components/ShopGrid/webhooks/index.js | 442 + components/ShopOrders/OrderList.js | 284 + components/ShopOrders/index.js | 168 + example.env | 18 + graphql/cartItems.js | 34 + graphql/channels.js | 141 + graphql/lineItems.js | 34 + graphql/matches.js | 74 + graphql/orders.js | 214 + graphql/shops.js | 102 + jsconfig.json | 32 + keystone.js | 220 + lib/checkAuth.js | 25 + lib/findChannelItems.js | 109 + lib/findShopItems.js | 116 + lib/gqlFetcher.js | 4 + lib/mail.js | 61 + lib/placeMultipleOrders.js | 354 + lib/removeEmpty.js | 6 + lib/useSharedState.js | 9 + .../migration.sql | 322 + .../migration.sql | 29 + .../20210824002140_add_sku/migration.sql | 15 + .../migration.sql | 3 + .../migration.sql | 2 + .../migration.sql | 11 + .../migration.sql | 11 + .../migration.sql | 397 + .../migration.sql | 32 + .../migration.sql | 2 + .../migration.sql | 32 + .../migration.sql | 45 + .../migration.sql | 24 + .../migration.sql | 9 + .../migration.sql | 67 + .../migration.sql | 9 + .../migration.sql | 24 + .../20220525115231_rename_table/migration.sql | 27 + .../migration.sql | 12 + .../migration.sql | 56 + .../migration.sql | 40 + .../migration.sql | 39 + .../migration.sql | 14 + .../migration.sql | 36 + .../migration.sql | 24 + .../migration.sql | 21 + .../migration.sql | 32 + .../migration.sql | 2 + .../migration.sql | 9 + .../migration.sql | 4 + .../migration.sql | 29 + .../migration.sql | 14 + .../migration.sql | 30 + .../migration.sql | 8 + migrations/migration_lock.toml | 3 + mutations/addMatchToCart.js | 240 + mutations/addToCart.js | 70 + mutations/cancelOrder.js | 22 + mutations/cancelPurchase.js | 36 + mutations/getMatch.js | 262 + mutations/getMatchCount.js | 49 + mutations/index.js | 69 + mutations/matchOrder.js | 184 + mutations/overwriteMatch.js | 101 + mutations/placeOrders.js | 18 + mutations/redirectToInit.js | 11 + next.config.js | 12 + package.json | 49 + pages/_app.js | 79 + pages/_document.js | 50 + pages/api/create-purchase/[platform].js | 211 + pages/api/create-webhook/[platform].js | 93 + pages/api/delete-webhook/[platform].js | 68 + pages/api/get-webhooks/[platform].js | 72 + pages/api/graphql.js | 2 + pages/api/o-auth/channel/shopify/callback.js | 123 + pages/api/o-auth/channel/shopify/index.js | 57 + pages/api/o-auth/shop/shopify/callback.js | 124 + pages/api/o-auth/shop/shopify/index.js | 56 + pages/api/order/create.js | 60 + pages/api/search-orders/[platform].js | 201 + pages/api/search-products/[platform].js | 126 + pages/api/triggers/cancel-order/[platform].js | 35 + .../triggers/cancel-purchase/[platform].js | 46 + pages/api/triggers/create-order/[platform].js | 147 + .../triggers/create-tracking/[platform].js | 62 + pages/api/update-product/[platform].js | 62 + pages/channels.js | 67 + pages/index.js | 155 + pages/init.js | 215 + pages/products.js | 68 + pages/reset.js | 400 + pages/settings.js | 43 + pages/shops.js | 67 + pages/signin.js | 279 + pages/signup.js | 262 + primitives/cartItem.js | 141 + primitives/collapse.js | 9 + primitives/errorTooltip.js | 90 + primitives/lineItem.js | 118 + primitives/matchItem.js | 49 + primitives/option.js | 107 + primitives/order.js | 109 + primitives/product.js | 181 + primitives/quantityCounter.js | 106 + primitives/webhookSwitch.js | 119 + public/favicon/android-chrome-192x192.png | Bin 0 -> 8101 bytes public/favicon/android-chrome-512x512.png | Bin 0 -> 26951 bytes public/favicon/apple-touch-icon.png | Bin 0 -> 7586 bytes public/favicon/favicon-16x16.png | Bin 0 -> 1032 bytes public/favicon/favicon-32x32.png | Bin 0 -> 1608 bytes public/favicon/favicon.ico | Bin 0 -> 15086 bytes public/favicon/favicon.svg | 49 + public/favicon/safari-pinned-tab.svg | 22 + public/favicon/site.webmanifest | 19 + public/sw.js | 114 + public/sw.js.map | 1 + public/workbox-eac1af49.js | 2744 +++++ public/workbox-eac1af49.js.map | 1 + render.yaml | 24 + schema.graphql | 2228 +++++ schema.prisma | 301 + schemas/CartItem.js | 58 + schemas/Channel.js | 61 + schemas/ChannelItem.js | 50 + schemas/ChannelMetafield.js | 46 + schemas/LineItem.js | 52 + schemas/Link.js | 67 + schemas/Match.js | 45 + schemas/Order.js | 173 + schemas/Role.js | 48 + schemas/Shop.js | 60 + schemas/ShopItem.js | 45 + .../TrackingDetail/addShopTracking/shopify.js | 168 + schemas/TrackingDetail/index.js | 131 + schemas/User.js | 73 + schemas/apiKey.js | 63 + schemas/fields.js | 49 + schemas/trackingFields.js | 23 + svg/github.js | 16 + svg/graphql.js | 137 + svg/index.js | 3 + svg/linked-in.js | 12 + svg/logo.js | 113 + svg/logoIcon.js | 78 + theme/InputWrapperStyles.js | 41 + theme/extendTheme.js | 71 + theme/globalStyles.js | 62 + theme/styles.js | 29 + yarn.lock | 8865 +++++++++++++++++ 207 files changed, 39022 insertions(+) create mode 100644 .gitignore create mode 100644 .prettierignore create mode 100644 CHANGELOG.md create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 access.js create mode 100644 components/AppShell.js create mode 100644 components/ChannelGrid/dangerous/index.js create mode 100644 components/ChannelGrid/details/EditDetails.js create mode 100644 components/ChannelGrid/details/index.js create mode 100644 components/ChannelGrid/functions/EditFunction.js create mode 100644 components/ChannelGrid/functions/index.js create mode 100644 components/ChannelGrid/index.js create mode 100644 components/ChannelGrid/metafields/EditMetafield.js create mode 100644 components/ChannelGrid/metafields/index.js create mode 100644 components/ChannelGrid/orders/index.js create mode 100644 components/ChannelGrid/webhooks/EditWebhook.js create mode 100644 components/ChannelGrid/webhooks/index.js create mode 100644 components/CreateItemDrawer.js create mode 100644 components/CreateViews/CreateChannelView/ChannelForm.js create mode 100644 components/CreateViews/CreateChannelView/index.js create mode 100644 components/CreateViews/CreateMatchView/ChannelProductSearch.js create mode 100644 components/CreateViews/CreateMatchView/ChannelSelect.js create mode 100644 components/CreateViews/CreateMatchView/FindMatch.js create mode 100644 components/CreateViews/CreateMatchView/ShopProductSearch.js create mode 100644 components/CreateViews/CreateMatchView/ShopSelect.js create mode 100644 components/CreateViews/CreateMatchView/index.js create mode 100644 components/CreateViews/CreateOrderView/CartItemSelect.js create mode 100644 components/CreateViews/CreateOrderView/ChannelSelect.js create mode 100644 components/CreateViews/CreateOrderView/LineItemSelect.js create mode 100644 components/CreateViews/CreateOrderView/ShopSelect.js create mode 100644 components/CreateViews/CreateOrderView/index.js create mode 100644 components/CreateViews/CreateShopView/ShopForm.js create mode 100644 components/CreateViews/CreateShopView/index.js create mode 100644 components/OSOrders/editCartItem/index.js create mode 100644 components/OSOrders/editOrder/index.js create mode 100644 components/OSOrders/index.js create mode 100644 components/OSOrders/osOrderList.js create mode 100644 components/OSOrders/previousCarts/CartList.js create mode 100644 components/OSOrders/previousCarts/index.js create mode 100644 components/Patterns.js create mode 100644 components/ProductSearch/ChannelProductSearch.js create mode 100644 components/ProductSearch/ProductList.js create mode 100644 components/ProductSearch/Search.js create mode 100644 components/ProductSearch/ShopProductSearch.js create mode 100644 components/ProductSearch/editProduct/index.js create mode 100644 components/ProductSearch/index.js create mode 100644 components/ShopGrid/dangerous/index.js create mode 100644 components/ShopGrid/details/EditDetails.js create mode 100644 components/ShopGrid/details/index.js create mode 100644 components/ShopGrid/functions/EditFunction.js create mode 100644 components/ShopGrid/functions/index.js create mode 100644 components/ShopGrid/index.js create mode 100644 components/ShopGrid/links/EditLink.js create mode 100644 components/ShopGrid/links/index.js create mode 100644 components/ShopGrid/orders/index.js create mode 100644 components/ShopGrid/webhooks/EditWebhook.js create mode 100644 components/ShopGrid/webhooks/index.js create mode 100644 components/ShopOrders/OrderList.js create mode 100644 components/ShopOrders/index.js create mode 100644 example.env create mode 100644 graphql/cartItems.js create mode 100644 graphql/channels.js create mode 100644 graphql/lineItems.js create mode 100644 graphql/matches.js create mode 100644 graphql/orders.js create mode 100644 graphql/shops.js create mode 100644 jsconfig.json create mode 100644 keystone.js create mode 100644 lib/checkAuth.js create mode 100644 lib/findChannelItems.js create mode 100644 lib/findShopItems.js create mode 100644 lib/gqlFetcher.js create mode 100644 lib/mail.js create mode 100644 lib/placeMultipleOrders.js create mode 100644 lib/removeEmpty.js create mode 100644 lib/useSharedState.js create mode 100644 migrations/20210729011711_initial_migration/migration.sql create mode 100644 migrations/20210812071644_make_domain_unique/migration.sql create mode 100644 migrations/20210824002140_add_sku/migration.sql create mode 100644 migrations/20210824003114_add_more_fields_to_order/migration.sql create mode 100644 migrations/20210824011535_add_stocktrace_field/migration.sql create mode 100644 migrations/20220208174825_remove_api_keys_table/migration.sql create mode 100644 migrations/20220208175903_add_api_key_as_column_on_user/migration.sql create mode 100644 migrations/20220211032623_upgrade_to_keystone_6/migration.sql create mode 100644 migrations/20220211054832_new_migration/migration.sql create mode 100644 migrations/20220215181606_made_api_key_non_unique/migration.sql create mode 100644 migrations/20220216024920_change_endpoint_column_names/migration.sql create mode 100644 migrations/20220519231358_rename_columns_on_shop_and_channel_list/migration.sql create mode 100644 migrations/20220520001527_remove_outdated_columns/migration.sql create mode 100644 migrations/20220520062635_rename_label_to_key_for_channel_custom_input/migration.sql create mode 100644 migrations/20220520065253_update_tracking_fields/migration.sql create mode 100644 migrations/20220524134010_rename_order_placement_callback_to_update_order/migration.sql create mode 100644 migrations/20220525114607_turn_api_key_into_own_list/migration.sql create mode 100644 migrations/20220525115231_rename_table/migration.sql create mode 100644 migrations/20220526200426_remove_hardcoded_channel_fields/migration.sql create mode 100644 migrations/20220601143945_add_link_column/migration.sql create mode 100644 migrations/20220601144348_update_link_column/migration.sql create mode 100644 migrations/20220601173017_rename_custom_input_to_metafield/migration.sql create mode 100644 migrations/20220606193239_remove_linked_channel_and_migrate_to_links_instead/migration.sql create mode 100644 migrations/20220611212714_update_tracking_fields/migration.sql create mode 100644 migrations/20220630211041_add_tracking_details_list/migration.sql create mode 100644 migrations/20220630224212_move_tracking_field_to_cart_item/migration.sql create mode 100644 migrations/20220630224659_update_to_many_to_many/migration.sql create mode 100644 migrations/20220706213331_add_purchase_id_to_tracking_detail/migration.sql create mode 100644 migrations/20220708032112_rename_placement_endpoint_to_create_purchase_endpoint/migration.sql create mode 100644 migrations/20220708214042_add_link_order_match_order_and_process_order_to_order/migration.sql create mode 100644 migrations/20220711212650_rename_channel_values/migration.sql create mode 100644 migrations/20220711224819_rename_columns_for_shop_table/migration.sql create mode 100644 migrations/20220715234719_redo_webhook_architecture/migration.sql create mode 100644 migrations/20220724042318_move_update_order_endpoint_into_place_multiple_orders/migration.sql create mode 100644 migrations/migration_lock.toml create mode 100644 mutations/addMatchToCart.js create mode 100644 mutations/addToCart.js create mode 100644 mutations/cancelOrder.js create mode 100644 mutations/cancelPurchase.js create mode 100644 mutations/getMatch.js create mode 100644 mutations/getMatchCount.js create mode 100644 mutations/index.js create mode 100644 mutations/matchOrder.js create mode 100644 mutations/overwriteMatch.js create mode 100644 mutations/placeOrders.js create mode 100644 mutations/redirectToInit.js create mode 100644 next.config.js create mode 100644 package.json create mode 100644 pages/_app.js create mode 100644 pages/_document.js create mode 100644 pages/api/create-purchase/[platform].js create mode 100644 pages/api/create-webhook/[platform].js create mode 100644 pages/api/delete-webhook/[platform].js create mode 100644 pages/api/get-webhooks/[platform].js create mode 100644 pages/api/graphql.js create mode 100644 pages/api/o-auth/channel/shopify/callback.js create mode 100644 pages/api/o-auth/channel/shopify/index.js create mode 100644 pages/api/o-auth/shop/shopify/callback.js create mode 100644 pages/api/o-auth/shop/shopify/index.js create mode 100644 pages/api/order/create.js create mode 100644 pages/api/search-orders/[platform].js create mode 100644 pages/api/search-products/[platform].js create mode 100644 pages/api/triggers/cancel-order/[platform].js create mode 100644 pages/api/triggers/cancel-purchase/[platform].js create mode 100644 pages/api/triggers/create-order/[platform].js create mode 100644 pages/api/triggers/create-tracking/[platform].js create mode 100644 pages/api/update-product/[platform].js create mode 100644 pages/channels.js create mode 100644 pages/index.js create mode 100644 pages/init.js create mode 100644 pages/products.js create mode 100644 pages/reset.js create mode 100644 pages/settings.js create mode 100644 pages/shops.js create mode 100644 pages/signin.js create mode 100644 pages/signup.js create mode 100644 primitives/cartItem.js create mode 100644 primitives/collapse.js create mode 100644 primitives/errorTooltip.js create mode 100644 primitives/lineItem.js create mode 100644 primitives/matchItem.js create mode 100644 primitives/option.js create mode 100644 primitives/order.js create mode 100644 primitives/product.js create mode 100644 primitives/quantityCounter.js create mode 100644 primitives/webhookSwitch.js create mode 100644 public/favicon/android-chrome-192x192.png create mode 100644 public/favicon/android-chrome-512x512.png create mode 100644 public/favicon/apple-touch-icon.png create mode 100644 public/favicon/favicon-16x16.png create mode 100644 public/favicon/favicon-32x32.png create mode 100644 public/favicon/favicon.ico create mode 100644 public/favicon/favicon.svg create mode 100644 public/favicon/safari-pinned-tab.svg create mode 100644 public/favicon/site.webmanifest create mode 100644 public/sw.js create mode 100644 public/sw.js.map create mode 100644 public/workbox-eac1af49.js create mode 100644 public/workbox-eac1af49.js.map create mode 100644 render.yaml create mode 100644 schema.graphql create mode 100644 schema.prisma create mode 100644 schemas/CartItem.js create mode 100644 schemas/Channel.js create mode 100644 schemas/ChannelItem.js create mode 100644 schemas/ChannelMetafield.js create mode 100644 schemas/LineItem.js create mode 100644 schemas/Link.js create mode 100644 schemas/Match.js create mode 100644 schemas/Order.js create mode 100644 schemas/Role.js create mode 100644 schemas/Shop.js create mode 100644 schemas/ShopItem.js create mode 100644 schemas/TrackingDetail/addShopTracking/shopify.js create mode 100644 schemas/TrackingDetail/index.js create mode 100644 schemas/User.js create mode 100644 schemas/apiKey.js create mode 100644 schemas/fields.js create mode 100644 schemas/trackingFields.js create mode 100644 svg/github.js create mode 100644 svg/graphql.js create mode 100644 svg/index.js create mode 100644 svg/linked-in.js create mode 100644 svg/logo.js create mode 100644 svg/logoIcon.js create mode 100644 theme/InputWrapperStyles.js create mode 100644 theme/extendTheme.js create mode 100644 theme/globalStyles.js create mode 100644 theme/styles.js create mode 100644 yarn.lock diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8eacb15 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# vercel +.vercel + +# keystone +.keystone diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..482b53b --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +render.yaml \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..817443b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,239 @@ +# @keystone-6/example-next-lite + +## 0.0.1 + +### Patch Changes + +- Updated dependencies [[`7dddbe0fd`](https://github.com/keystonejs/keystone/commit/7dddbe0fd5b42a2596ba4dc0bbe1813cb54571c7), [`fb7844ab5`](https://github.com/keystonejs/keystone/commit/fb7844ab50c1d4a6d14b2ad46a568665f6661921), [`3c7a581c1`](https://github.com/keystonejs/keystone/commit/3c7a581c1e53ae49c9f74509de3927ebf2703bde), [`f4554980f`](https://github.com/keystonejs/keystone/commit/f4554980f6243a6545eee6c887d946ff25cd90e3)]: + - @keystone-6/core@1.0.0 + +## 4.0.0 + +### Major Changes + +- [#6957](https://github.com/keystonejs/keystone/pull/6957) [`de8cf44e7`](https://github.com/keystonejs/keystone/commit/de8cf44e7b328ab98e1466d7191d9ee65a57b02a) Thanks [@bladey](https://github.com/bladey)! - Update Node engines to support current Node LTS versions, currently versions 14 and 16. + +### Patch Changes + +- Updated dependencies [[`f2b41df9f`](https://github.com/keystonejs/keystone/commit/f2b41df9f77cf340e5e138cf60bacd6aec8e4548), [`04c54a4eb`](https://github.com/keystonejs/keystone/commit/04c54a4eb4aa6076cf87d441060eaa2091bc903b), [`748538649`](https://github.com/keystonejs/keystone/commit/748538649645d3b0ef32b0baba8fa310f2a493fe), [`4e96c23bb`](https://github.com/keystonejs/keystone/commit/4e96c23bb6c3a134f1324ec7879adac3abf90132), [`76ec35c97`](https://github.com/keystonejs/keystone/commit/76ec35c97a72dcb023e1b0da5b47e876896b6a03), [`760ae82ac`](https://github.com/keystonejs/keystone/commit/760ae82ac0fac5f73e123e2b36f7ba6320312ca6), [`0a7b75838`](https://github.com/keystonejs/keystone/commit/0a7b7583887e3811c23b0b74f4f97633fd484e08), [`622e57689`](https://github.com/keystonejs/keystone/commit/622e57689cf27dbecba7f64c02f0a3b6499d3218), [`bbedee845`](https://github.com/keystonejs/keystone/commit/bbedee84541d22c91a6816872902f6cce8e6aee3), [`82539faa5`](https://github.com/keystonejs/keystone/commit/82539faa53c495be1f5f470deb9eae9861cd31a0), [`760ae82ac`](https://github.com/keystonejs/keystone/commit/760ae82ac0fac5f73e123e2b36f7ba6320312ca6), [`96fd2e220`](https://github.com/keystonejs/keystone/commit/96fd2e22041de84a042f5a0df2cab75ba0dacc35), [`04c54a4eb`](https://github.com/keystonejs/keystone/commit/04c54a4eb4aa6076cf87d441060eaa2091bc903b), [`82539faa5`](https://github.com/keystonejs/keystone/commit/82539faa53c495be1f5f470deb9eae9861cd31a0), [`de8cf44e7`](https://github.com/keystonejs/keystone/commit/de8cf44e7b328ab98e1466d7191d9ee65a57b02a), [`7a7450009`](https://github.com/keystonejs/keystone/commit/7a7450009d68f70173a2af55eb3a845ea3799c99)]: + - @keystone-next/keystone@29.0.0 + +## 3.0.11 + +### Patch Changes + +- Updated dependencies [[`70eb86237`](https://github.com/keystonejs/keystone/commit/70eb86237bd3eafd36b0579f66ad3f1e173357b1), [`990b56291`](https://github.com/keystonejs/keystone/commit/990b56291e677077656b201b935086754c6257f1), [`b981f4c3e`](https://github.com/keystonejs/keystone/commit/b981f4c3ee135a1184188deb5ed8de22f718080c)]: + - @keystone-next/keystone@28.0.0 + +## 3.0.10 + +### Patch Changes + +- Updated dependencies [[`f3e8aac31`](https://github.com/keystonejs/keystone/commit/f3e8aac31efb3eb1573eb340e07a25920084a4aa), [`ddabdbd02`](https://github.com/keystonejs/keystone/commit/ddabdbd02230374ff921998f9d21c0ad7d32b226), [`71600965b`](https://github.com/keystonejs/keystone/commit/71600965b963e098ca77ae1261b850b9573c9f22), [`d9e1ba8fa`](https://github.com/keystonejs/keystone/commit/d9e1ba8fa23c0d9e902ef61167913ee92f5657cb), [`71600965b`](https://github.com/keystonejs/keystone/commit/71600965b963e098ca77ae1261b850b9573c9f22), [`5e61e0050`](https://github.com/keystonejs/keystone/commit/5e61e00503715f0f634d97e573926091a52661e6), [`f38772b27`](https://github.com/keystonejs/keystone/commit/f38772b27d3e9d157127dabfa40036462c235a9f), [`30fc08b51`](https://github.com/keystonejs/keystone/commit/30fc08b515e4f8851fd2583a265a813c683bf604), [`f683dcfe3`](https://github.com/keystonejs/keystone/commit/f683dcfe37d013b3d17f1fbad3df335b2f2ee51c), [`c0661b8ee`](https://github.com/keystonejs/keystone/commit/c0661b8ee9e16a1ffdd7fc77c9c56fead0efda36), [`db7f2311b`](https://github.com/keystonejs/keystone/commit/db7f2311bb2ff8e1e70350cd0f087439b8404a8a), [`023bc7a0b`](https://github.com/keystonejs/keystone/commit/023bc7a0b1e6fb0ebdc5055f0243d9dad255a667), [`cbb9df927`](https://github.com/keystonejs/keystone/commit/cbb9df927a0f106aaa35d107961a405b0d08a751), [`d1141ea82`](https://github.com/keystonejs/keystone/commit/d1141ea8235bca4ce88500991c24b962b06ade45), [`cbb9df927`](https://github.com/keystonejs/keystone/commit/cbb9df927a0f106aaa35d107961a405b0d08a751), [`ddabdbd02`](https://github.com/keystonejs/keystone/commit/ddabdbd02230374ff921998f9d21c0ad7d32b226), [`71600965b`](https://github.com/keystonejs/keystone/commit/71600965b963e098ca77ae1261b850b9573c9f22), [`d107a5bec`](https://github.com/keystonejs/keystone/commit/d107a5becdd16245caf208c3979965fa926e484c), [`44cbef543`](https://github.com/keystonejs/keystone/commit/44cbef5435081311acb9e68dd750f1ca289b8221), [`dcf5241d8`](https://github.com/keystonejs/keystone/commit/dcf5241d8e3e62b080842a5d4bfd47a7f2cce5ca)]: + - @keystone-next/keystone@27.0.0 + +## 3.0.9 + +### Patch Changes + +- [#6744](https://github.com/keystonejs/keystone/pull/6744) [`0ef1ee3cc`](https://github.com/keystonejs/keystone/commit/0ef1ee3ccd99f0f3e1f955f03d00b1a0f238c7cd) Thanks [@bladey](https://github.com/bladey)! - Renamed branch `master` to `main`. + +- Updated dependencies [[`73544fd19`](https://github.com/keystonejs/keystone/commit/73544fd19b865be9fbf3ea9ae68fae5f039eb13f), [`0ef1ee3cc`](https://github.com/keystonejs/keystone/commit/0ef1ee3ccd99f0f3e1f955f03d00b1a0f238c7cd), [`930b7129f`](https://github.com/keystonejs/keystone/commit/930b7129f37beb396bb8ecc8a8dc9f1b3615a7e0), [`fac96cbd1`](https://github.com/keystonejs/keystone/commit/fac96cbd14febcc01bdffbecd1aceee391f6a20a), [`3d289eb3d`](https://github.com/keystonejs/keystone/commit/3d289eb3d00c3e6a0c26ce962fb0f942a08c400a), [`bed3a560a`](https://github.com/keystonejs/keystone/commit/bed3a560a59d4fe787f3beebd65f8148453aae35), [`930b7129f`](https://github.com/keystonejs/keystone/commit/930b7129f37beb396bb8ecc8a8dc9f1b3615a7e0), [`6e4a0cf56`](https://github.com/keystonejs/keystone/commit/6e4a0cf56ce35b2446db7970763c55446de3db0e), [`d64bd4a7f`](https://github.com/keystonejs/keystone/commit/d64bd4a7f3da87e13e9cac41f0eb9757b771835f), [`abeceaf90`](https://github.com/keystonejs/keystone/commit/abeceaf902c231aabe9cf3a383ecf29c09b8f4dd), [`704f68b38`](https://github.com/keystonejs/keystone/commit/704f68b38f970860137380e21c36e04d2c51a7a4), [`576f341e6`](https://github.com/keystonejs/keystone/commit/576f341e61b31bbcf076ba70002d137c7b7ff9a9)]: + - @keystone-next/keystone@26.1.0 + +## 3.0.8 + +### Patch Changes + +- Updated dependencies [[`5c0163e09`](https://github.com/keystonejs/keystone/commit/5c0163e0973e5fee9b1e2c2b1f2834284858a509), [`7f5caff60`](https://github.com/keystonejs/keystone/commit/7f5caff60308112ded832db4703f33eaae00ce24), [`480c875d1`](https://github.com/keystonejs/keystone/commit/480c875d11700f9eb23f403a5bb277aa94c38ce7), [`3ece149e5`](https://github.com/keystonejs/keystone/commit/3ece149e53066661c57c56fdd1467003c5b11c06), [`d0e3c087e`](https://github.com/keystonejs/keystone/commit/d0e3c087e49310774b9538dfa5d2432c00381db0), [`21c5d1aa9`](https://github.com/keystonejs/keystone/commit/21c5d1aa964a19657d4ba7eb913e8ca292bf1714), [`8bbba49c7`](https://github.com/keystonejs/keystone/commit/8bbba49c74fd4b7cf2560613c9cf6bcaddb11a6f), [`42268ee72`](https://github.com/keystonejs/keystone/commit/42268ee72707e94a6197607d24534a438b748649), [`d9e18613a`](https://github.com/keystonejs/keystone/commit/d9e18613a4136f1c1201a197e47d9d4bde292cd2), [`e81947d6c`](https://github.com/keystonejs/keystone/commit/e81947d6ccb0b541387519898fdbbf09274d4c9f), [`5d3fc0b77`](https://github.com/keystonejs/keystone/commit/5d3fc0b77c92efc69d725f943626d8d76a28e799), [`3cfc2a383`](https://github.com/keystonejs/keystone/commit/3cfc2a3839142dd3ccdbf1dd86768257e9acc0dc), [`1da120a38`](https://github.com/keystonejs/keystone/commit/1da120a388a80585e897a06b81b027b7d8011902), [`499c00b44`](https://github.com/keystonejs/keystone/commit/499c00b44b4b378285ed21a385da799b4af0af82), [`eb1a89f3c`](https://github.com/keystonejs/keystone/commit/eb1a89f3c13d4e80516cc372cef3dc505ef864f3), [`4da935870`](https://github.com/keystonejs/keystone/commit/4da935870374414e83900949cc70fce0d4b6de19), [`1faddea9d`](https://github.com/keystonejs/keystone/commit/1faddea9d285c70d2d867958bc5ab2bbfb44dbd6), [`7de13bce3`](https://github.com/keystonejs/keystone/commit/7de13bce32630ee2478a9894e801020c520c64a9), [`271e5d97b`](https://github.com/keystonejs/keystone/commit/271e5d97bc2e4548ce039a568278f9f7569aa41a), [`0218a4215`](https://github.com/keystonejs/keystone/commit/0218a421576fb3ceb38eb5f38223a9ef0af4c4d2), [`273ee446a`](https://github.com/keystonejs/keystone/commit/273ee446a6d3e22c4d01c530d33282df362a6f1b), [`14bfa8a9b`](https://github.com/keystonejs/keystone/commit/14bfa8a9b33fae4c5eb3664ca23bb88850df5e50), [`8bbba49c7`](https://github.com/keystonejs/keystone/commit/8bbba49c74fd4b7cf2560613c9cf6bcaddb11a6f), [`a645861a9`](https://github.com/keystonejs/keystone/commit/a645861a9562748cf3e9786e37acea67c4a0cc17), [`581e130cf`](https://github.com/keystonejs/keystone/commit/581e130cf2a833c2b363801a32f4791bc1c7c62c), [`689d8ecaa`](https://github.com/keystonejs/keystone/commit/689d8ecaa9e93eedc80084aafc319a0396efc593), [`144f7f8e4`](https://github.com/keystonejs/keystone/commit/144f7f8e4e13ec547865927cb224fea7165b98b7), [`f963966ab`](https://github.com/keystonejs/keystone/commit/f963966ab138a315a8f18d83ed7a676f7423a51d), [`b76974736`](https://github.com/keystonejs/keystone/commit/b76974736132a71d693b3e325ffa009d395840a4), [`47c8b53ce`](https://github.com/keystonejs/keystone/commit/47c8b53ce44b7ad34ba40501a257a2b679cdee05), [`a95da1d81`](https://github.com/keystonejs/keystone/commit/a95da1d812574fd17d1fa8bc324415da558a9d9d), [`1b0a2f516`](https://github.com/keystonejs/keystone/commit/1b0a2f516d7d9ffce2e470dcd9ea870a3274500b), [`7621d0db7`](https://github.com/keystonejs/keystone/commit/7621d0db75033b68a510d5f6c9b03d9418980e73), [`67492f37d`](https://github.com/keystonejs/keystone/commit/67492f37dd9fbcd94234c15a072e9c826fa7a665), [`002e1d88b`](https://github.com/keystonejs/keystone/commit/002e1d88b0908c2e1215c1181724b2bc1cc57538), [`ca48072b4`](https://github.com/keystonejs/keystone/commit/ca48072b4d137e879e328c93b703a8364562db8a), [`10c61bd44`](https://github.com/keystonejs/keystone/commit/10c61bd44176ffa7d0e446c28fd9f12ed54790f0), [`1659e1fe5`](https://github.com/keystonejs/keystone/commit/1659e1fe5e0f394df058b3a773ea62bf392fa8db), [`3b9732acd`](https://github.com/keystonejs/keystone/commit/3b9732acd8cd597fa9c70128a2e7129ed02e6775), [`c2b124f8e`](https://github.com/keystonejs/keystone/commit/c2b124f8e4b283022ec473d9e5f32f37de639cf0), [`4048991ba`](https://github.com/keystonejs/keystone/commit/4048991ba7db234a694287000beaf2ea052cd24e), [`79e2cc3aa`](https://github.com/keystonejs/keystone/commit/79e2cc3aa79a90358a6ce1281a8ad5f5632ac185), [`1f952fb10`](https://github.com/keystonejs/keystone/commit/1f952fb10710b7fae6a88112310b25a09ab330ea), [`1b0a2f516`](https://github.com/keystonejs/keystone/commit/1b0a2f516d7d9ffce2e470dcd9ea870a3274500b), [`4e485a914`](https://github.com/keystonejs/keystone/commit/4e485a914cfbc6c4b5ef9eeca9157bf654469b2d), [`3ee4542a8`](https://github.com/keystonejs/keystone/commit/3ee4542a884d8135299178950ab47bb82907bcd9), [`e84f8f655`](https://github.com/keystonejs/keystone/commit/e84f8f6550cff4fbca69982e0371d787e67c8915), [`ca48072b4`](https://github.com/keystonejs/keystone/commit/ca48072b4d137e879e328c93b703a8364562db8a), [`e747ef6f3`](https://github.com/keystonejs/keystone/commit/e747ef6f31590799fa332e1f011b160a443fbeb4), [`5e62702ba`](https://github.com/keystonejs/keystone/commit/5e62702ba3934bf8effb5dce65466017dd868610), [`b00596d3f`](https://github.com/keystonejs/keystone/commit/b00596d3f8b64cddc46ec9e5e4e567dd67264253), [`80cd31303`](https://github.com/keystonejs/keystone/commit/80cd313033b339d90b5e640b252a357a4d60fbcd), [`c8aca958b`](https://github.com/keystonejs/keystone/commit/c8aca958b3650f10011370e0c00b01cb681bb212), [`232c512a0`](https://github.com/keystonejs/keystone/commit/232c512a05250cb8a9c26b70969afe4106e2f8ac), [`8631917d1`](https://github.com/keystonejs/keystone/commit/8631917d14778468652abb8eda06802d2469646c), [`b6c8c3bff`](https://github.com/keystonejs/keystone/commit/b6c8c3bff9d3d98f743c47c015ae27e63db0271e), [`bf5874411`](https://github.com/keystonejs/keystone/commit/bf58744118320493325b3b48aadd007e12d5c680), [`398c08529`](https://github.com/keystonejs/keystone/commit/398c085295d992658a9e7e22aae037f55528c258), [`47cee8c95`](https://github.com/keystonejs/keystone/commit/47cee8c952c1134e503bff54e61dcd48c76b5429), [`9f0a4cc1f`](https://github.com/keystonejs/keystone/commit/9f0a4cc1f6d5133e92a0d326e285152d18689173), [`838845298`](https://github.com/keystonejs/keystone/commit/8388452982277b10c65ff89be442464761a680a7), [`11fb46c91`](https://github.com/keystonejs/keystone/commit/11fb46c918e508cc182d5bd22f069b9329edadba)]: + - @keystone-next/keystone@26.0.0 + +## 3.0.7 + +### Patch Changes + +- [#6432](https://github.com/keystonejs/keystone/pull/6432) [`0a189d5d0`](https://github.com/keystonejs/keystone/commit/0a189d5d0e618ee5598e9beaccea0290d2a3f8d9) Thanks [@renovate](https://github.com/apps/renovate)! - Updated `typescript` dependency to `^4.4.2`. + +- Updated dependencies [[`2a901a121`](https://github.com/keystonejs/keystone/commit/2a901a1210a0b3de0ccd22ca93e9cbcc8ed0f951), [`3008c5110`](https://github.com/keystonejs/keystone/commit/3008c5110a0ebc524eb3609bd8ba901f664f83d3), [`3904a9cf7`](https://github.com/keystonejs/keystone/commit/3904a9cf73e16ef192faae833f2f39ed05f2d707), [`32f024738`](https://github.com/keystonejs/keystone/commit/32f0247384ecf3bce5c3ef14ad8d367c9888459f), [`2e3f3666b`](https://github.com/keystonejs/keystone/commit/2e3f3666b5340b8eb778104a1d4a3f4d52be6528), [`44f2ef60e`](https://github.com/keystonejs/keystone/commit/44f2ef60e29912f3c85b91fc704e09a7d5a15b22), [`9651aff8e`](https://github.com/keystonejs/keystone/commit/9651aff8eb9a51c0fbda6f51b1be0fedb07571da), [`9c5991f43`](https://github.com/keystonejs/keystone/commit/9c5991f43e8f909e576f6b51fd87aab3bbead504), [`069265b9c`](https://github.com/keystonejs/keystone/commit/069265b9cdd5898f4501535793f56debaa247c1c), [`4f36a81af`](https://github.com/keystonejs/keystone/commit/4f36a81afb03591354acc1d0141eff8fe54ff208), [`c76bfc0a2`](https://github.com/keystonejs/keystone/commit/c76bfc0a2ad5aeffb68b8d2006225f608e855a19), [`bc9088f05`](https://github.com/keystonejs/keystone/commit/bc9088f0574af27be6a068483a789a80f7a46a41), [`ee54522d5`](https://github.com/keystonejs/keystone/commit/ee54522d513a9376c1ed1e472a7ff91657e4e693), [`32f024738`](https://github.com/keystonejs/keystone/commit/32f0247384ecf3bce5c3ef14ad8d367c9888459f), [`bd120c7c2`](https://github.com/keystonejs/keystone/commit/bd120c7c296c9adaaefe9bf93cbb384cc7528715), [`595922b48`](https://github.com/keystonejs/keystone/commit/595922b48c909053fa9d34bb1c42177ad41c72d5), [`8f2786535`](https://github.com/keystonejs/keystone/commit/8f2786535272976678427fd13758e63b2c59d955), [`b3eefc1c3`](https://github.com/keystonejs/keystone/commit/b3eefc1c336a9a366c39f7aa2cf5251baaf843fd), [`0aa02a333`](https://github.com/keystonejs/keystone/commit/0aa02a333d989c30647cd10e25325d4d2db61be6), [`bf9b5605f`](https://github.com/keystonejs/keystone/commit/bf9b5605fc684975d9e2cad604c8e0d978eac40a), [`3957c0981`](https://github.com/keystonejs/keystone/commit/3957c098131b3b055cb94b07f1ce55ec82640908), [`af5e59bf4`](https://github.com/keystonejs/keystone/commit/af5e59bf4215aa297495ae603239b1e3510be39b), [`cbc5a68aa`](https://github.com/keystonejs/keystone/commit/cbc5a68aa7547ea55d1254ee5c3b1e543cdc78e2), [`32f024738`](https://github.com/keystonejs/keystone/commit/32f0247384ecf3bce5c3ef14ad8d367c9888459f), [`783290796`](https://github.com/keystonejs/keystone/commit/78329079606d74a2eedd63f96a985116bf0b449c), [`0a189d5d0`](https://github.com/keystonejs/keystone/commit/0a189d5d0e618ee5598e9beaccea0290d2a3f8d9), [`944bce1e8`](https://github.com/keystonejs/keystone/commit/944bce1e834be4d0f4c79f35cd53ccbabb92f555), [`e0f935eb2`](https://github.com/keystonejs/keystone/commit/e0f935eb2ef8ac311a43423c6691e56cd27b6bed), [`2324fa027`](https://github.com/keystonejs/keystone/commit/2324fa027a6c2beabef4724c69a9ad05338a0cf3), [`f2311781a`](https://github.com/keystonejs/keystone/commit/f2311781a990c0ccd3302ac8e7aa889138f70e47), [`88b03bd79`](https://github.com/keystonejs/keystone/commit/88b03bd79112c7d8f0d41c592c8bd4bb226f5f71), [`0aa02a333`](https://github.com/keystonejs/keystone/commit/0aa02a333d989c30647cd10e25325d4d2db61be6), [`5ceccd821`](https://github.com/keystonejs/keystone/commit/5ceccd821b513e2abec3eb24278e7c30bdcdf6d6), [`fd744dcaa`](https://github.com/keystonejs/keystone/commit/fd744dcaa513efb2a8ae954bb2d5d1fa7f0723d6), [`489e128fe`](https://github.com/keystonejs/keystone/commit/489e128fe0835968eda0908b199a8867c0e72a5b), [`bb0c6c626`](https://github.com/keystonejs/keystone/commit/bb0c6c62610eda20ae93a6b67185276bdbba3248)]: + - @keystone-next/keystone@25.0.0 + +## 3.0.6 + +### Patch Changes + +- Updated dependencies [[`e9f3c42d5`](https://github.com/keystonejs/keystone/commit/e9f3c42d5b9d42872cecbd18fbe9bf9d7d53ed82), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`1cbcf54cb`](https://github.com/keystonejs/keystone/commit/1cbcf54cb1206461866b582865e3b1a8fc728f18), [`a92169d04`](https://github.com/keystonejs/keystone/commit/a92169d04e5a1a98deb8e757b8eae3b06fc66450), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`b696a9579`](https://github.com/keystonejs/keystone/commit/b696a9579b503db86f42776381e247c4e1a7409f), [`f3014a627`](https://github.com/keystonejs/keystone/commit/f3014a627060c7cd86440a6937da5caecfd023a0), [`092df6678`](https://github.com/keystonejs/keystone/commit/092df6678cea18d639be16ad250ec4ecc9250f5a), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`6da56b80e`](https://github.com/keystonejs/keystone/commit/6da56b80e03c748a621afcca6c1ec2887fef7271), [`4f4f0351a`](https://github.com/keystonejs/keystone/commit/4f4f0351a056dea9d1614aa2a3a4789d66bb402d), [`697efa354`](https://github.com/keystonejs/keystone/commit/697efa354b1066b3d4b6eb757ca704b458f45e93), [`c7e331d90`](https://github.com/keystonejs/keystone/commit/c7e331d90a28b2ed8236100097cb8d34a11fabe2), [`3a7a06b2c`](https://github.com/keystonejs/keystone/commit/3a7a06b2cc6b5ea157d34d925b15494b471899eb), [`272b97b3a`](https://github.com/keystonejs/keystone/commit/272b97b3a10c0dfada782171d55ef7ac6f47c98f), [`78dac764e`](https://github.com/keystonejs/keystone/commit/78dac764e1860b33f9e2bd8cee6015abeaaa5ec4), [`399561b27`](https://github.com/keystonejs/keystone/commit/399561b2769ddd8f3d3fdf29838f5784404bb053), [`9d361c1c8`](https://github.com/keystonejs/keystone/commit/9d361c1c8625e1390f837b7318b63547d686a63b), [`0dcb1c95b`](https://github.com/keystonejs/keystone/commit/0dcb1c95b5200750cc8649485425f2ae40d023a3), [`94435ffee`](https://github.com/keystonejs/keystone/commit/94435ffee765824091899242e4a2f73c7356b524), [`5cd8ffd6c`](https://github.com/keystonejs/keystone/commit/5cd8ffd6cb822dbee8555b47846a5019c4d2b1c3), [`56044e2a4`](https://github.com/keystonejs/keystone/commit/56044e2a425f4256b66475fd3b1a6342cd6c3bf9), [`f46fd32b7`](https://github.com/keystonejs/keystone/commit/f46fd32b7047dbb5ea2566859f7ecee8db5b0b15), [`874f2c405`](https://github.com/keystonejs/keystone/commit/874f2c4058c9cf006213e84b9ffcf39c5bf144e8), [`8ea4eed55`](https://github.com/keystonejs/keystone/commit/8ea4eed55367aaa213f6b4ffb7473087498e39ae), [`e3fe6498d`](https://github.com/keystonejs/keystone/commit/e3fe6498dc36203d8080dff3c2e0c25f6c98733e), [`1030296d1`](https://github.com/keystonejs/keystone/commit/1030296d1f304dc44246e895089ac1f992e80590), [`3564b342d`](https://github.com/keystonejs/keystone/commit/3564b342d6dc2127ae591d7ac055af9eae90543c), [`8b2d179b2`](https://github.com/keystonejs/keystone/commit/8b2d179b2463d78b082182ca9afa8233109e0ba3), [`e3fefafcc`](https://github.com/keystonejs/keystone/commit/e3fefafcce6f8bf836c9bf0f4d931b8200ba41c7), [`4d9f89f88`](https://github.com/keystonejs/keystone/commit/4d9f89f884e2bf984fdd74ca2cbb7874b25b9cda), [`686c0f1c4`](https://github.com/keystonejs/keystone/commit/686c0f1c4a1feb609e1584aa71738709bbbf984e), [`d214e2f72`](https://github.com/keystonejs/keystone/commit/d214e2f72bae1c798e2415a38410d6063c333e2e), [`f5e64af37`](https://github.com/keystonejs/keystone/commit/f5e64af37df2eb460c89d89fa3c8924fb34970ed)]: + - @keystone-next/fields@14.0.0 + - @keystone-next/keystone@24.0.0 + +## 3.0.5 + +### Patch Changes + +- Updated dependencies [[`3f03b8c1f`](https://github.com/keystonejs/keystone/commit/3f03b8c1fa7005b37371e1cc401c3a03334a4f7a), [`ea0712aa2`](https://github.com/keystonejs/keystone/commit/ea0712aa22487325bd898818ea4fbca543c9dcf1), [`93f1e5d30`](https://github.com/keystonejs/keystone/commit/93f1e5d302701c610b6cba74e0c5c86a3ac8aacc), [`9e2deac5f`](https://github.com/keystonejs/keystone/commit/9e2deac5f340b4baeb03b01ae065f2bec5977523), [`7716315ea`](https://github.com/keystonejs/keystone/commit/7716315ea823dd91d17d54dcbb9155b5445cd956), [`a11e54d69`](https://github.com/keystonejs/keystone/commit/a11e54d692d3cec4ec2439cbf743b590688fb7d3), [`e5f61ad50`](https://github.com/keystonejs/keystone/commit/e5f61ad50133a328fcb32299b838fd9eac574c3f), [`e4e6cf9b5`](https://github.com/keystonejs/keystone/commit/e4e6cf9b59eec461d2b53acfa3b350e4f5a06fc4), [`2ef6fe82c`](https://github.com/keystonejs/keystone/commit/2ef6fe82cee6df7796935d35d1c12cab29aecc75), [`dd7e811e7`](https://github.com/keystonejs/keystone/commit/dd7e811e7ce084c1e832acefc6ed773af371ac9e), [`587a8d0b0`](https://github.com/keystonejs/keystone/commit/587a8d0b074ccecb239d120275359f72779f306f), [`597edbdd8`](https://github.com/keystonejs/keystone/commit/597edbdd81df80982dd3df3d9d600003ef8a15e9), [`1172e1853`](https://github.com/keystonejs/keystone/commit/1172e18531064df6412c06412e74da3b85740b35), [`fbe698461`](https://github.com/keystonejs/keystone/commit/fbe6984616de7a302db7c2b0082851db89c2e314), [`32e9879db`](https://github.com/keystonejs/keystone/commit/32e9879db9cfee77f067eb8105262df65bca6c06)]: + - @keystone-next/keystone@23.0.0 + - @keystone-next/fields@13.0.0 + +## 3.0.4 + +### Patch Changes + +- Updated dependencies [[`38b78f2ae`](https://github.com/keystonejs/keystone/commit/38b78f2aeaf4c5d8176a1751ad8cb5a7acce2790), [`139d7a8de`](https://github.com/keystonejs/keystone/commit/139d7a8def263d40c0d1d5353d2744842d9a0951), [`279403cb0`](https://github.com/keystonejs/keystone/commit/279403cb0b4bffb946763c9a7ef71be57478eeb3), [`253df44c2`](https://github.com/keystonejs/keystone/commit/253df44c2f8d6535a6425b2593eaed5380433d57), [`253df44c2`](https://github.com/keystonejs/keystone/commit/253df44c2f8d6535a6425b2593eaed5380433d57), [`f482db633`](https://github.com/keystonejs/keystone/commit/f482db6332e54a1d5cd469e2805b99b544208e83), [`c536b478f`](https://github.com/keystonejs/keystone/commit/c536b478fc89f2d933cddf8533e7d88030540a63)]: + - @keystone-next/fields@12.0.0 + - @keystone-next/keystone@22.0.0 + +## 3.0.3 + +### Patch Changes + +- Updated dependencies [[`03f535ba6`](https://github.com/keystonejs/keystone/commit/03f535ba6fa1a5e5f3027bcad761feb3fd94587b), [`03f535ba6`](https://github.com/keystonejs/keystone/commit/03f535ba6fa1a5e5f3027bcad761feb3fd94587b)]: + - @keystone-next/keystone@21.0.0 + - @keystone-next/fields@11.0.2 + +## 3.0.2 + +### Patch Changes + +- Updated dependencies [[`a3b07ea16`](https://github.com/keystonejs/keystone/commit/a3b07ea16ffc0f6741c0c0e5e281622a1831e0e7), [`7eabb4dee`](https://github.com/keystonejs/keystone/commit/7eabb4dee2552f7baf1e0024d82011b179d418d4), [`5227234a0`](https://github.com/keystonejs/keystone/commit/5227234a08edd99cd2795c8d888fbb3022810f54), [`a3b07ea16`](https://github.com/keystonejs/keystone/commit/a3b07ea16ffc0f6741c0c0e5e281622a1831e0e7), [`e4c19f808`](https://github.com/keystonejs/keystone/commit/e4c19f8086cc14f7f4a8ef390f1f4e1263004d40), [`4995c682d`](https://github.com/keystonejs/keystone/commit/4995c682dbdcfac2100de9fab98ba1e0e08cbcc2), [`a3b07ea16`](https://github.com/keystonejs/keystone/commit/a3b07ea16ffc0f6741c0c0e5e281622a1831e0e7), [`a3b07ea16`](https://github.com/keystonejs/keystone/commit/a3b07ea16ffc0f6741c0c0e5e281622a1831e0e7), [`a3b07ea16`](https://github.com/keystonejs/keystone/commit/a3b07ea16ffc0f6741c0c0e5e281622a1831e0e7), [`a3b07ea16`](https://github.com/keystonejs/keystone/commit/a3b07ea16ffc0f6741c0c0e5e281622a1831e0e7), [`a3b07ea16`](https://github.com/keystonejs/keystone/commit/a3b07ea16ffc0f6741c0c0e5e281622a1831e0e7), [`881c9ffb7`](https://github.com/keystonejs/keystone/commit/881c9ffb7c5941e9fb214ed955148d8ea567e65f), [`ef14e77ce`](https://github.com/keystonejs/keystone/commit/ef14e77cebc9420db8c7d29dfe61f02140f4a705), [`df7d7b6f6`](https://github.com/keystonejs/keystone/commit/df7d7b6f6f2830573393560f4a1ec35234889947), [`a3b07ea16`](https://github.com/keystonejs/keystone/commit/a3b07ea16ffc0f6741c0c0e5e281622a1831e0e7), [`84a5e7f3b`](https://github.com/keystonejs/keystone/commit/84a5e7f3bc3a29ff31d642831e7aaadfc8534ba1), [`a3b07ea16`](https://github.com/keystonejs/keystone/commit/a3b07ea16ffc0f6741c0c0e5e281622a1831e0e7), [`a3b07ea16`](https://github.com/keystonejs/keystone/commit/a3b07ea16ffc0f6741c0c0e5e281622a1831e0e7), [`a3b07ea16`](https://github.com/keystonejs/keystone/commit/a3b07ea16ffc0f6741c0c0e5e281622a1831e0e7), [`a3b07ea16`](https://github.com/keystonejs/keystone/commit/a3b07ea16ffc0f6741c0c0e5e281622a1831e0e7), [`97fd5e05d`](https://github.com/keystonejs/keystone/commit/97fd5e05d8681bae86001e6b7e8e3f36ebd639b7), [`a3b07ea16`](https://github.com/keystonejs/keystone/commit/a3b07ea16ffc0f6741c0c0e5e281622a1831e0e7)]: + - @keystone-next/keystone@20.0.0 + - @keystone-next/fields@11.0.0 + +## 3.0.1 + +### Patch Changes + +- [#5792](https://github.com/keystonejs/keystone/pull/5792) [`319c19bd5`](https://github.com/keystonejs/keystone/commit/319c19bd5f8e8c261a1aefb1997d66b2a136ae28) Thanks [@timleslie](https://github.com/timleslie)! - Changed the type of the `where` argument to `allItems` to `_allItemsMeta` from type `ItemWhereInput` to `ItemWhereInput! = {}`. + +* [#5850](https://github.com/keystonejs/keystone/pull/5850) [`5b02e8625`](https://github.com/keystonejs/keystone/commit/5b02e8625e18c8e79547d5caf8cacb5014ffee9d) Thanks [@timleslie](https://github.com/timleslie)! - The `AND` and `OR` operators of `ItemWhereInput` now accept non-null values, e.g. `[ItemWhereInput!]`, rather than `[ItemWhereInput]`. + +- [#5767](https://github.com/keystonejs/keystone/pull/5767) [`02af04c03`](https://github.com/keystonejs/keystone/commit/02af04c03c96c26c273cd49eda5b4a132e02a26a) Thanks [@timleslie](https://github.com/timleslie)! - Deprecated the `sortBy` GraphQL filter. Updated the `orderBy` GraphQL filter with an improved API. + + Previously a `User` list's `allUsers` query would have the argument: + + ```graphql + orderBy: String + ``` + + The new API gives it the argument: + + ```graphql + orderBy: [UserOrderByInput!]! = [] + ``` + + where + + ```graphql + input UserOrderByInput { + id: OrderDirection + name: OrderDirection + score: OrderDirection + } + + enum OrderDirection { + asc + desc + } + ``` + + Rather than writing `allUsers(orderBy: "name_ASC")` you now write `allUsers(orderBy: { name: asc })`. You can also now order by multiple fields, e.g. `allUsers(orderBy: [{ score: asc }, { name: asc }])`. Each `UserOrderByInput` must have exactly one key, or else an error will be returned. + +* [#5791](https://github.com/keystonejs/keystone/pull/5791) [`9de71a9fb`](https://github.com/keystonejs/keystone/commit/9de71a9fb0d3b7f5f05c0d908bebdb818723fd4b) Thanks [@timleslie](https://github.com/timleslie)! - Changed the return type of `allItems(...)` from `[User]` to `[User!]`, as this API can never have `null` items in the return array. + +- [#5769](https://github.com/keystonejs/keystone/pull/5769) [`08478b8a7`](https://github.com/keystonejs/keystone/commit/08478b8a7bb9fe5932c7f74f9f6d3af75a0a5394) Thanks [@timleslie](https://github.com/timleslie)! - The GraphQL query `_allMeta { count }` generated for each list has been deprecated in favour of a new query `Count`, which directy returns the count. + + A `User` list would have the following query added to the API: + + ```graphql + usersCount(where: UserWhereInput! = {}): Int + ``` + +- Updated dependencies [[`0eadba2ba`](https://github.com/keystonejs/keystone/commit/0eadba2badb13fc6a17f7e525d429494ca953481), [`f52079f0b`](https://github.com/keystonejs/keystone/commit/f52079f0bffc4cf2ab5e26e4c3654127b59d6078), [`b9c828fb0`](https://github.com/keystonejs/keystone/commit/b9c828fb0d6e587976dbd0dc4e87004bce3b2ef7), [`74bc77854`](https://github.com/keystonejs/keystone/commit/74bc778547623fe4ed3db97ed09384d9dc076372), [`a6a444acd`](https://github.com/keystonejs/keystone/commit/a6a444acd23f2590d9812872441cafb5d088c48e), [`29075e580`](https://github.com/keystonejs/keystone/commit/29075e58074672d90cfca84aba8dcedeecf243ca), [`59421c039`](https://github.com/keystonejs/keystone/commit/59421c0399368e56e46537c1c687daa27f5912d0), [`319c19bd5`](https://github.com/keystonejs/keystone/commit/319c19bd5f8e8c261a1aefb1997d66b2a136ae28), [`c6cd0a6bd`](https://github.com/keystonejs/keystone/commit/c6cd0a6bdc7ccb000c39fba0da31819e33d9e056), [`195d4fb12`](https://github.com/keystonejs/keystone/commit/195d4fb1218517d7b9a40d3bba1a087d40e6d1d6), [`1fe4753f3`](https://github.com/keystonejs/keystone/commit/1fe4753f3af28aa851e1f90d55937c940be5af1a), [`5b02e8625`](https://github.com/keystonejs/keystone/commit/5b02e8625e18c8e79547d5caf8cacb5014ffee9d), [`76cdb791b`](https://github.com/keystonejs/keystone/commit/76cdb791b1ab36d015e43b87deff52be2ea6b629), [`762f17823`](https://github.com/keystonejs/keystone/commit/762f1782334c9b7174c320182c753c215834ff7f), [`0617c81ea`](https://github.com/keystonejs/keystone/commit/0617c81eacc88e40bdd21bacab285d674b171a4a), [`02af04c03`](https://github.com/keystonejs/keystone/commit/02af04c03c96c26c273cd49eda5b4a132e02a26a), [`107eeb037`](https://github.com/keystonejs/keystone/commit/107eeb0374e214b69be3727ca955a9f76e1468bb), [`9de71a9fb`](https://github.com/keystonejs/keystone/commit/9de71a9fb0d3b7f5f05c0d908bebdb818723fd4b), [`08478b8a7`](https://github.com/keystonejs/keystone/commit/08478b8a7bb9fe5932c7f74f9f6d3af75a0a5394), [`7bda87ea7`](https://github.com/keystonejs/keystone/commit/7bda87ea7f11e0faceccc6ab3f715c72b07c129b), [`590bb1fe9`](https://github.com/keystonejs/keystone/commit/590bb1fe9254c2f8feff7e3a0e2e964610116f95), [`4b11c5ea8`](https://github.com/keystonejs/keystone/commit/4b11c5ea87b759c24bdbff9d18443bbc972757c0), [`38a177d61`](https://github.com/keystonejs/keystone/commit/38a177d6140874b29d3c09b5852dbfd787d5c429), [`bb4f4ac91`](https://github.com/keystonejs/keystone/commit/bb4f4ac91c3ed70393774f744075971453a12aba), [`19a756496`](https://github.com/keystonejs/keystone/commit/19a7564964d9dcdc94ecdda9c0a0e92c539eb309)]: + - @keystone-next/keystone@19.0.0 + - @keystone-next/fields@10.0.0 + +## 3.0.0 + +### Major Changes + +- [#5746](https://github.com/keystonejs/keystone/pull/5746) [`19750d2dc`](https://github.com/keystonejs/keystone/commit/19750d2dc5801cc8d2ffae1f50d1d5ca6ab9407d) Thanks [@timleslie](https://github.com/timleslie)! - Update Node.js dependency to `^12.20 || >= 14.13`. + +### Patch Changes + +- Updated dependencies [[`19750d2dc`](https://github.com/keystonejs/keystone/commit/19750d2dc5801cc8d2ffae1f50d1d5ca6ab9407d), [`016ccad82`](https://github.com/keystonejs/keystone/commit/016ccad82ed73898a64310506117c1cbae60a512), [`8da79e71a`](https://github.com/keystonejs/keystone/commit/8da79e71abb005eb755620fb3c8f82a3a2952152), [`e2232a553`](https://github.com/keystonejs/keystone/commit/e2232a5537620bd82983ba3f5cff124cec8facab)]: + - @keystone-next/fields@9.0.0 + - @keystone-next/keystone@18.0.0 + +## 2.0.2 + +### Patch Changes + +- [#5601](https://github.com/keystonejs/keystone/pull/5601) [`8b77b6971`](https://github.com/keystonejs/keystone/commit/8b77b697187f8d5eb73e862b2c07b8bfbb769e0b) Thanks [@renovate](https://github.com/apps/renovate)! - Updated Next.js dependency to `^10.2.0`. + +- Updated dependencies [[`1c0265171`](https://github.com/keystonejs/keystone/commit/1c0265171db2e334c25d014d855ec919c3d4782c), [`3d3894679`](https://github.com/keystonejs/keystone/commit/3d38946798650d117c39ce522987b169e616b2b9), [`8b77b6971`](https://github.com/keystonejs/keystone/commit/8b77b697187f8d5eb73e862b2c07b8bfbb769e0b), [`1043243ff`](https://github.com/keystonejs/keystone/commit/1043243ff5a22bb067cf4aa6e46d28a529203121)]: + - @keystone-next/keystone@17.1.0 + - @keystone-next/fields@8.1.0 + - @keystone-next/admin-ui@14.1.1 + +## 2.0.1 + +### Patch Changes + +- Updated dependencies [[`b7aeb232d`](https://github.com/keystonejs/keystone/commit/b7aeb232db43b32cae0bca3fcb74479d6834c587), [`b7aeb232d`](https://github.com/keystonejs/keystone/commit/b7aeb232db43b32cae0bca3fcb74479d6834c587), [`18ae28bde`](https://github.com/keystonejs/keystone/commit/18ae28bde943c140332ad5e0cd0b5238555fb1b8), [`f7d4c9b9f`](https://github.com/keystonejs/keystone/commit/f7d4c9b9f06cc3090b59d4b29e0907e9f3d1faee), [`fddeacf79`](https://github.com/keystonejs/keystone/commit/fddeacf79d25fea15be57d1a4ec16815bcdc4ab5), [`fbf5f77c5`](https://github.com/keystonejs/keystone/commit/fbf5f77c515b2413c4019b4a521dd4f4aa965276), [`dbc62ff7c`](https://github.com/keystonejs/keystone/commit/dbc62ff7c71ca4d4db1fab76f3e0ab729af5b80c), [`8577eb3ba`](https://github.com/keystonejs/keystone/commit/8577eb3baafe9cd61c48d89aca9eff252765e5a6), [`91e603d7a`](https://github.com/keystonejs/keystone/commit/91e603d7a686185c145bcbc445a27939f94aafa8), [`a6cdf3da8`](https://github.com/keystonejs/keystone/commit/a6cdf3da8a9b2ca943048fee6cacd376ea4aae50), [`a0c5aa307`](https://github.com/keystonejs/keystone/commit/a0c5aa30771d187253d0cfe24b4b686e136136cc), [`ddf51724a`](https://github.com/keystonejs/keystone/commit/ddf51724ab2043f395d1d197213748c06a5300b7), [`d216fd04c`](https://github.com/keystonejs/keystone/commit/d216fd04c92ec594fb9b448025fc3e23fe6dfdad), [`3e33cd3ff`](https://github.com/keystonejs/keystone/commit/3e33cd3ff46f824ec3516e5810a7e5027b332a5a), [`2df2fa021`](https://github.com/keystonejs/keystone/commit/2df2fa0213146adab79e5e17c60d43259041093d), [`49dd46843`](https://github.com/keystonejs/keystone/commit/49dd468435a96c537f5649aa2fd9e21103da40e1), [`f76938ac2`](https://github.com/keystonejs/keystone/commit/f76938ac223194ce401179fd9fa1226e11077277), [`74fed41e2`](https://github.com/keystonejs/keystone/commit/74fed41e23c3d5c6c073574c54ca339df2235351)]: + - @keystone-next/admin-ui@14.1.0 + - @keystone-next/fields@8.0.0 + - @keystone-next/keystone@17.0.0 + +## 2.0.0 + +### Major Changes + +- [#5397](https://github.com/keystonejs/keystone/pull/5397) [`a5627304b`](https://github.com/keystonejs/keystone/commit/a5627304b7921a0f1484d6d08330115d0edbb45b) Thanks [@bladey](https://github.com/bladey)! - Updated Node engine version to 12.x due to 10.x reaching EOL on 2021-04-30. + +### Patch Changes + +- [#5451](https://github.com/keystonejs/keystone/pull/5451) [`9e060fe83`](https://github.com/keystonejs/keystone/commit/9e060fe83459269bc5d257f31a23c164d2283624) Thanks [@JedWatson](https://github.com/JedWatson)! - With the goal of making the Lists API (i.e `context.lists.{List}`) more intuitive to use, the `resolveFields` option has been deprecated in favor of two new methods: + + (1) You can specify a string of fields to return with the new `query` option, when you want to query for resolved field values (including querying relationships and virtual fields). This replaces the `resolveFields: false` use case. + + For example, to query a Post you would now write: + + ```js + const [post] = await context.lists.Post.findMany({ + where: { slug }, + query: ` + title + content + image { + src + width + height + }`, + }); + ``` + + (2) Alternatively, there is a new set of APIs on `context.db.lists.{List}` which will return the unresolved item data from the database (but with read hooks applied), which can then be referenced directly or returned from a custom mutation or query in the GraphQL API to be handled by the Field resolvers. This replaces the `resolveFields: boolean` use case. + + For example, to query for the raw data stored in the database, you would write: + + ```js + const [post] = await context.db.lists.Post.findMany({ + where: { slug }, + }); + ``` + +* [#5388](https://github.com/keystonejs/keystone/pull/5388) [`29c9a36b7`](https://github.com/keystonejs/keystone/commit/29c9a36b70b7ba689b049ce6c1555806fbfc8e1e) Thanks [@timleslie](https://github.com/timleslie)! - Updated example project to use the new `db.provider` config option over the deprecated `db.adapter` option. + +- [#5366](https://github.com/keystonejs/keystone/pull/5366) [`115b06130`](https://github.com/keystonejs/keystone/commit/115b06130d801e00dec88935a5d400e71f089853) Thanks [@renovate](https://github.com/apps/renovate)! - Updated Next.js dependency to `^10.1.3`. + +- Updated dependencies [[`9e060fe83`](https://github.com/keystonejs/keystone/commit/9e060fe83459269bc5d257f31a23c164d2283624), [`3d3fb860f`](https://github.com/keystonejs/keystone/commit/3d3fb860faa303cbfe75eeb0855a8a575113320c), [`637ae05d3`](https://github.com/keystonejs/keystone/commit/637ae05d3f8a138902c2d03c5b342cb93c440767), [`d0adec53f`](https://github.com/keystonejs/keystone/commit/d0adec53ff20c2246dfe955b449b7c6e1afe96fb), [`c7aecec3c`](https://github.com/keystonejs/keystone/commit/c7aecec3c768eec742e0ce9c5506331e902e5124), [`588f31ddc`](https://github.com/keystonejs/keystone/commit/588f31ddce15ab752a987a1dc1429fa1d6f03d7c), [`781b3e5ab`](https://github.com/keystonejs/keystone/commit/781b3e5abcf9a8b6d29c86d6470adfd08b4413c8), [`49025d1ad`](https://github.com/keystonejs/keystone/commit/49025d1ad0d85c4f80e5430a365c4fc78db96c92), [`f059f6349`](https://github.com/keystonejs/keystone/commit/f059f6349bee3dce8bbf4a0584b235e97872851c), [`7498fcabb`](https://github.com/keystonejs/keystone/commit/7498fcabba3ef6b411dd3bf67a20821702442ebc), [`11f5bb631`](https://github.com/keystonejs/keystone/commit/11f5bb6316b90ec603aa034db1b9259c911204ed), [`d9e1acb30`](https://github.com/keystonejs/keystone/commit/d9e1acb30e384ce88e6681ba9d299d917dea97d9), [`24e62e29c`](https://github.com/keystonejs/keystone/commit/24e62e29c51c04448a272a25292251fc13e06d7a), [`fe55e9289`](https://github.com/keystonejs/keystone/commit/fe55e9289b898bdcb937eb5e981dba2bb58a672f), [`6861ecb40`](https://github.com/keystonejs/keystone/commit/6861ecb40345434f8d070950a3c8fb85f3d59994), [`5b2369077`](https://github.com/keystonejs/keystone/commit/5b2369077094dc5470ce8bfc5a7eaf142c04a818), [`a5627304b`](https://github.com/keystonejs/keystone/commit/a5627304b7921a0f1484d6d08330115d0edbb45b), [`202d362f3`](https://github.com/keystonejs/keystone/commit/202d362f38d0c8827263e6cd2d286d8dcbdd22ad), [`d0adec53f`](https://github.com/keystonejs/keystone/commit/d0adec53ff20c2246dfe955b449b7c6e1afe96fb), [`962cde7e3`](https://github.com/keystonejs/keystone/commit/962cde7e32ec7ce23d15180f315549f4f34069ee), [`f67497c1a`](https://github.com/keystonejs/keystone/commit/f67497c1a9dd7462e7d6564250712f5456dc5cb0), [`1d85d7ff4`](https://github.com/keystonejs/keystone/commit/1d85d7ff4e8d7795d6e0f82484cf7108d11925db), [`2bef01aaa`](https://github.com/keystonejs/keystone/commit/2bef01aaacd32eb746353bde11dd5e37c67fb43e), [`be60812f2`](https://github.com/keystonejs/keystone/commit/be60812f29d7768ce65a5f5e8c40597d4742c5d7), [`0e74d8123`](https://github.com/keystonejs/keystone/commit/0e74d81238d5d00cc3eb968c95c02f25cb3a5a78), [`be60812f2`](https://github.com/keystonejs/keystone/commit/be60812f29d7768ce65a5f5e8c40597d4742c5d7), [`76692d266`](https://github.com/keystonejs/keystone/commit/76692d26642eabf23d2ef038dec35d35d4e35d31), [`d7e8cad4f`](https://github.com/keystonejs/keystone/commit/d7e8cad4fca5d8ffefa235c2ff30ec8e2e0d6276), [`ecf07393a`](https://github.com/keystonejs/keystone/commit/ecf07393a19714f1686772bd082de7d229065aa2), [`ad1776b74`](https://github.com/keystonejs/keystone/commit/ad1776b7418b7a0d1c8e5def8d82051752c01aa9), [`309596591`](https://github.com/keystonejs/keystone/commit/3095965915adbb93ff6879d4e9bf3f0dd504708c), [`5b2369077`](https://github.com/keystonejs/keystone/commit/5b2369077094dc5470ce8bfc5a7eaf142c04a818), [`89b869e8d`](https://github.com/keystonejs/keystone/commit/89b869e8d492151449f2146108767a7e5e5ecdfa), [`58a793988`](https://github.com/keystonejs/keystone/commit/58a7939888ec84d0f089d77ca1ce9d94ef0d9a85), [`be60812f2`](https://github.com/keystonejs/keystone/commit/be60812f29d7768ce65a5f5e8c40597d4742c5d7), [`115b06130`](https://github.com/keystonejs/keystone/commit/115b06130d801e00dec88935a5d400e71f089853), [`a73aea7d7`](https://github.com/keystonejs/keystone/commit/a73aea7d78d4c520856f06f9d1b79efe4b36993b)]: + - @keystone-next/admin-ui@14.0.0 + - @keystone-next/keystone@16.0.0 + - @keystone-next/fields@7.0.0 + +## 1.0.4 + +### Patch Changes + +- Updated dependencies [[`901817fed`](https://github.com/keystonejs/keystone/commit/901817fedf4bcfb269416c3c68093ae0263f4d00), [`c28e765d1`](https://github.com/keystonejs/keystone/commit/c28e765d12655f802e324b82529fcf571d88c0c6), [`c28e765d1`](https://github.com/keystonejs/keystone/commit/c28e765d12655f802e324b82529fcf571d88c0c6), [`c28e765d1`](https://github.com/keystonejs/keystone/commit/c28e765d12655f802e324b82529fcf571d88c0c6), [`95fefaf81`](https://github.com/keystonejs/keystone/commit/95fefaf815204d6af6e407690f44750f500602e3), [`c28e765d1`](https://github.com/keystonejs/keystone/commit/c28e765d12655f802e324b82529fcf571d88c0c6), [`1a4db6c87`](https://github.com/keystonejs/keystone/commit/1a4db6c87c17706c8e5db2816e0a6b1b8f79e217), [`b40016301`](https://github.com/keystonejs/keystone/commit/b40016301dab71630068cc86c04828c5ee1683e8), [`5c4b48636`](https://github.com/keystonejs/keystone/commit/5c4b4863638cffa794dd1b02c445a87655a4178c), [`192393d0d`](https://github.com/keystonejs/keystone/commit/192393d0df67e123a694a42dd3f95ffa6d40042b), [`c28e765d1`](https://github.com/keystonejs/keystone/commit/c28e765d12655f802e324b82529fcf571d88c0c6), [`c28e765d1`](https://github.com/keystonejs/keystone/commit/c28e765d12655f802e324b82529fcf571d88c0c6), [`1886b4323`](https://github.com/keystonejs/keystone/commit/1886b43235e50bd2e070350d258f0a3145c19bbc), [`c28e765d1`](https://github.com/keystonejs/keystone/commit/c28e765d12655f802e324b82529fcf571d88c0c6), [`399e6db39`](https://github.com/keystonejs/keystone/commit/399e6db39c51cf9e8bbf3dde0887e5bf55dd1c4d), [`8665cfe66`](https://github.com/keystonejs/keystone/commit/8665cfe66016e0356681413e31f80a6d5586d364), [`1886b4323`](https://github.com/keystonejs/keystone/commit/1886b43235e50bd2e070350d258f0a3145c19bbc), [`399e6db39`](https://github.com/keystonejs/keystone/commit/399e6db39c51cf9e8bbf3dde0887e5bf55dd1c4d), [`d93bab17b`](https://github.com/keystonejs/keystone/commit/d93bab17b69c76e57580dc00e41314215da6d49b), [`5cd94b2a3`](https://github.com/keystonejs/keystone/commit/5cd94b2a32b3eddaf00ad77229f7e9664899c3b9), [`9e450d6b3`](https://github.com/keystonejs/keystone/commit/9e450d6b326e2ba5f46e49ecf53b6bd7a627e9ca), [`1e6d12f47`](https://github.com/keystonejs/keystone/commit/1e6d12f47076816d2a2441b42471176c5a7f2f8c), [`c28e765d1`](https://github.com/keystonejs/keystone/commit/c28e765d12655f802e324b82529fcf571d88c0c6), [`bc21855a7`](https://github.com/keystonejs/keystone/commit/bc21855a7ff6dd4dbc278b3e15c9157de765e6ba)]: + - @keystone-next/keystone@15.0.0 + - @keystone-next/admin-ui@13.0.0 + - @keystone-next/fields@6.0.0 diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..0ad25db --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..24215e6 --- /dev/null +++ b/README.md @@ -0,0 +1,94 @@ +

+ + + +

+ +
+ +**Openship is an operations and fulfillment platform that enables multi-channel fulfillment** + +[Website](https://openship.org) · [Documentation](https://docs.openship.org) · [Openship Cloud](https://openship.org/signup) + +
+ +## Running locally + +To get Openship running on your local machine: + +### Clone the repo + +``` +git clone https://github.com/openshiporg/openship +``` + +### Rename example.env to .env + +```shell +//.env +FRONTEND_URL=http://localhost:3000 +DATABASE_URL=postgresql://postgres:example@url:3000/postgres +SESSION_SECRET=please_change_me +``` + +Be sure to replace DATABASE_URL with a postgres connection string. + +You can run postgres locally or get a database online. + +> Railway offers a free, temporary [postgres database](https://railway.app/new/postgresql). + +### Start the application + +Run the following commands start up Openship: + +```js +$ cd openship +$ yarn install +$ yarn dev +``` + +### Openship: http://localhost:3000 + +Once the application is running, go to localhost:3000. If there are no users in the database, you'll be redirected to localhost:3000/init where you can create the admin user. + +### Keystone CMS: http://localhost:8000 + +Openship uses [Keystone.js](https://github.com/keystonejs/keystone) under the hood. Running Openship locally will give you access to the Keystone CMS at localhost:8000. It's a great way to see and interact with your database. + +## Deployment + +Under the hood, Openship is using Next.js, so naturally, it can be hosted anywhere that supports Node.js. Openship also requires a `postgres` database. + +### 1-Click Deployment + +These deployment services offer `Node.js` and `postgres` databases so Openship can be deployed in 1-click. + +#### Railway + +[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/new/template/31ZaPV?referralCode=fQpsld) + +#### Render + +[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/openshiporg/openship) + +### Next.js Deployment + +To deploy on platforms that don't support databases like [Netlify](https://netlify.com) and [Vercel](https://vercel.com), you'll need to pass a `postgres` connection string as the `DATABASE_URL` variable. + +#### Vercel + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fopenshiporg%2Fopenship&env=SESSION_SECRET,FRONTEND_URL,DATABASE_URL&envDescription=A%20postgres%20connection%20string%20is%20used%20for%20DATABASE_URL) + +#### Netlify + +[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/openshiporg/openship) + +## Credits + +Openship wouldn't be here without these great projects + +- [Next.js](https://nextjs.org/) +- [Keystone.js](https://keystonejs.com/) +- [Prisma](https://prisma.io/) +- [Mantine](https://mantine.dev/) +- [swr](https://swr.vercel.app/) diff --git a/access.js b/access.js new file mode 100644 index 0000000..6f8fc77 --- /dev/null +++ b/access.js @@ -0,0 +1,191 @@ +import { permissionsList } from "./schemas/fields"; +/* + The basic level of access to the system is being signed in as a valid user. This gives you access + to the Admin UI, access to your own User and Todo items, and read access to roles. +*/ +export const isSignedIn = ({ session }) => !!session; + +/* + Permissions are shorthand functions for checking that the current user's role has the specified + permission boolean set to true +*/ +// export const permissions = { +// // canCreateTodos: ({ session }: ListAccessArgs) => +// // !!session?.data.role?.canCreateTodos, +// // canManageAllTodos: ({ session }: ListAccessArgs) => +// // !!session?.data.role?.canManageAllTodos, +// canManageUsers: ({ session }: ListAccessArgs) => +// !!session?.data.role?.canManageUsers, +// canManageRoles: ({ session }: ListAccessArgs) => +// !!session?.data.role?.canManageRoles, +// canManageOrders: ({ session }: ListAccessArgs) => +// !!session?.data.role?.canManageOrders, +// canManageShops: ({ session }: ListAccessArgs) => +// !!session?.data.role?.canManageShops, +// canManageChannels: ({ session }: ListAccessArgs) => +// !!session?.data.role?.canManageChannels, +// canManageMatches: ({ session }: ListAccessArgs) => +// !!session?.data.role?.canManageMatches, +// }; + +export const permissions = Object.fromEntries( + permissionsList.map((permission) => [ + permission, + function ({ session }) { + return !!session?.data.role?.[permission]; + }, + ]) +); + +/* + Rules are logical functions that can be used for list access, and may return a boolean (meaning + all or no items are available) or a set of filters that limit the available items +*/ +export const rules = { + ownItem({ session }) { + if (!isSignedIn({ session })) { + return false; + } + // 2. If not, do they own this item? + return { user: { id: session?.itemId } }; + }, + canReadUsers: ({ session }) => { + if (!session) { + // No session? No Users. + return false; + } + if (permissions.canSeeOtherUsers({ session })) { + return true; // They can read everything! + } + // Can only see yourself + return { id: { equals: session.itemId } }; + }, + canUpdateUsers: ({ session }) => { + if (!session) { + // No session? No Users. + return false; + } + if (permissions.canManageUsers({ session })) { + return true; + } + // Can update yourself + return { id: { equals: session.itemId } }; + }, + canReadOrders: ({ session }) => { + if (!isSignedIn({ session })) { + return false; + } + if (permissions.canSeeOtherOrders({ session })) { + return true; // They can read everything! + } + // 2. If not, do they own this item? + return { user: { id: { equals: session.itemId } } }; + }, + canUpdateOrders: ({ session }) => { + if (!isSignedIn({ session })) { + return false; + } + // 1. Do they have the permission of canManageProducts + if (permissions.canManageOrders({ session })) { + return true; + } + // 2. If not, do they own this item? + return { user: { id: { equals: session?.itemId } } }; + }, + canReadShops: ({ session }) => { + if (!session) { + // No session? No Users. + return false; + } + // 1. Do they have the permission of canManageProducts + if (permissions.canSeeOtherShops({ session })) { + return true; // They can read everything! + } + // 2. If not, do they own this item? + return { user: { id: { equals: session.itemId } } }; + }, + canUpdateShops: ({ session }) => { + if (!session) { + // No session? No Users. + return false; + } + // 1. Do they have the permission of canManageProducts + if (permissions.canManageShops({ session })) { + return true; + } + // 2. If not, do they own this item? + return { user: { id: { equals: session.itemId } } }; + }, + canReadChannels: ({ session }) => { + if (!session) { + // No session? No Users. + return false; + } + // 1. Do they have the permission of canManageProducts + if (permissions.canSeeOtherChannels({ session })) { + return true; // They can read everything! + } + // 2. If not, do they own this item? + return { user: { id: { equals: session.itemId } } }; + }, + canUpdateChannels: ({ session }) => { + if (!session) { + // No session? No Users. + return false; + } + // 1. Do they have the permission of canManageProducts + if (permissions.canManageChannels({ session })) { + return true; + } + // 2. If not, do they own this item? + return { user: { id: { equals: session.itemId } } }; + }, + canReadMatches: ({ session }) => { + if (!session) { + // No session? No Users. + return false; + } + // 1. Do they have the permission of canManageProducts + if (permissions.canSeeOtherMatches({ session })) { + return true; // They can read everything! + } + // 2. If not, do they own this item? + return { user: { id: { equals: session.itemId } } }; + }, + canUpdateMatches: ({ session }) => { + if (!session) { + // No session? No Users. + return false; + } + // 1. Do they have the permission of canManageProducts + if (permissions.canManageMatches({ session })) { + return true; + } + // 2. If not, do they own this item? + return { user: { id: { equals: session.itemId } } }; + }, + canReadLinks: ({ session }) => { + if (!session) { + // No session? No Users. + return false; + } + // 1. Do they have the permission of canManageProducts + if (permissions.canSeeOtherLinks({ session })) { + return true; // They can read everything! + } + // 2. If not, do they own this item? + return { user: { id: { equals: session.itemId } } }; + }, + canUpdateLinks: ({ session }) => { + if (!session) { + // No session? No Users. + return false; + } + // 1. Do they have the permission of canManageProducts + if (permissions.canManageLinks({ session })) { + return true; + } + // 2. If not, do they own this item? + return { user: { id: { equals: session.itemId } } }; + }, +}; diff --git a/components/AppShell.js b/components/AppShell.js new file mode 100644 index 0000000..778f050 --- /dev/null +++ b/components/AppShell.js @@ -0,0 +1,994 @@ +import React, { useState } from "react"; +import { + createStyles, + Navbar, + UnstyledButton, + Tooltip, + Title, + Box, + Badge, + Group, + ActionIcon, + useMantineColorScheme, + useMantineTheme, + Divider, + Breadcrumbs, + Menu, + Skeleton, + Stack, + Popper, + Center, + Paper, + Code, + Button, + Text, + Burger, + Transition, +} from "@mantine/core"; + +import { + GlobeIcon, + IssueReopenedIcon, + WebhookIcon, + ContainerIcon, + SunIcon, + MoonIcon, + PackageIcon, + PlusIcon, + SignOutIcon, + KeyIcon, + CopyIcon, + SyncIcon, + CheckIcon, + ChevronDownIcon, +} from "@primer/octicons-react"; +import { LogoIconSVG } from "@svg"; +import { useRouter } from "next/router"; +import Link from "next/link"; +import { SHOPS_QUERY } from "@graphql/shops"; +import { CHANNELS_QUERY } from "@graphql/channels"; +import { gqlFetcher } from "@lib/gqlFetcher"; +import useSWR from "swr"; +import { CreateShopView } from "./CreateViews/CreateShopView"; +import { CreateChannelView } from "./CreateViews/CreateChannelView"; +import { CreateOrderView } from "./CreateViews/CreateOrderView"; +import { CreateMatchView } from "./CreateViews/CreateMatchView"; +import request, { gql } from "graphql-request"; +import { useClipboard, useClickOutside } from "@mantine/hooks"; +import { useNotifications } from "@mantine/notifications"; +import { useModals } from "@mantine/modals"; +import { useSharedState } from "@lib/useSharedState"; + +const HEADER_SIZE = 50; + +const useStyles = createStyles((theme, { opened }) => ({ + wrapper: { + display: "flex", + flex: 1, + }, + + aside: { + flex: "0 0 60px", + backgroundColor: + theme.colorScheme === "dark" ? theme.colors.dark[7] : theme.white, + display: "flex", + flexDirection: "column", + alignItems: "center", + borderRight: `1px solid ${ + theme.colorScheme === "dark" ? theme.colors.dark[7] : theme.colors.gray[3] + }`, + }, + + main: { + flex: 1, + backgroundColor: + theme.colorScheme === "dark" + ? theme.colors.dark[6] + : theme.colors.gray[0], + }, + + mainLink: { + width: 44, + height: 44, + borderRadius: theme.radius.md, + display: "flex", + alignItems: "center", + justifyContent: "center", + color: + theme.colorScheme === "dark" + ? theme.colors.dark[0] + : theme.colors.gray[7], + + "&:hover": { + backgroundColor: + theme.colorScheme === "dark" + ? theme.colors.dark[5] + : theme.colors.gray[0], + }, + }, + + mainLinkActive: { + "&, &:hover": { + backgroundColor: + theme.colorScheme === "dark" + ? theme.fn.rgba(theme.colors[theme.primaryColor][9], 0.25) + : theme.colors[theme.primaryColor][0], + color: + theme.colors[theme.primaryColor][theme.colorScheme === "dark" ? 4 : 7], + }, + }, + + title: { + boxSizing: "border-box", + fontFamily: `Greycliff CF, ${theme.fontFamily}`, + marginBottom: theme.spacing.xl, + backgroundColor: + theme.colorScheme === "dark" ? theme.colors.dark[7] : theme.white, + padding: theme.spacing.md, + paddingTop: 18, + height: 60, + borderBottom: `1px solid ${ + theme.colorScheme === "dark" ? theme.colors.dark[7] : theme.colors.gray[3] + }`, + }, + + logo: { + boxSizing: "border-box", + width: "100%", + display: "flex", + justifyContent: "center", + height: 60, + paddingTop: theme.spacing.md, + borderBottom: `1px solid ${ + theme.colorScheme === "dark" ? theme.colors.dark[7] : theme.colors.gray[3] + }`, + marginBottom: theme.spacing.xl, + }, + + link: { + boxSizing: "border-box", + display: "flex", + alignItems: "center", + textDecoration: "none", + borderTopRightRadius: theme.radius.sm, + borderBottomRightRadius: theme.radius.sm, + color: + theme.colorScheme === "dark" + ? theme.colors.dark[0] + : theme.colors.gray[7], + padding: `0 ${theme.spacing.md}px`, + marginTop: 2, + fontSize: theme.fontSizes.sm, + marginRight: theme.spacing.md, + height: 44, + lineHeight: "44px", + textTransform: "uppercase", + fontWeight: 700, + letterSpacing: 0.4, + cursor: "pointer", + "&:hover": { + backgroundColor: + theme.colorScheme === "dark" + ? theme.colors.dark[5] + : theme.colors.gray[1], + color: theme.colorScheme === "dark" ? theme.white : theme.black, + }, + }, + + linkActive: { + "&, &:hover": { + borderLeftColor: + theme.colors[theme.primaryColor][theme.colorScheme === "dark" ? 7 : 5], + backgroundImage: + theme.colorScheme === "dark" + ? `linear-gradient(to right, #1A1B1E, ${theme.colors.dark[6]})` + : `linear-gradient(to right, #fff, ${theme.colors.blue[2]})`, + color: theme.colors.blue[6], + border: `1px solid ${ + theme.colors[theme.colorScheme === "dark" ? "dark" : "blue"][ + theme.colorScheme === "dark" ? 5 : 1 + ] + }`, + borderLeft: "none", + }, + }, + + mobileLink: { + display: "block", + lineHeight: 1, + padding: "8px 12px", + borderRadius: theme.radius.sm, + textDecoration: "none", + color: + theme.colorScheme === "dark" + ? theme.colors.dark[0] + : theme.colors.gray[7], + fontSize: theme.fontSizes.sm, + textTransform: "uppercase", + fontWeight: 700, + letterSpacing: 0.3, + "&:hover": { + backgroundColor: + theme.colorScheme === "dark" + ? theme.colors.dark[6] + : theme.colors.gray[2], + }, + + [theme.fn.smallerThan("sm")]: { + borderRadius: 0, + padding: theme.spacing.md, + }, + }, + + mobileLinkActive: { + "&, &:hover": { + backgroundColor: theme.fn.variant({ + variant: "light", + color: theme.primaryColor, + }).background, + color: theme.fn.variant({ variant: "light", color: theme.primaryColor }) + .color, + }, + }, + + dropdown: { + position: "absolute", + top: HEADER_SIZE, + left: 0, + right: 0, + zIndex: 0, + borderTopRightRadius: 0, + borderTopLeftRadius: 0, + borderTopWidth: 0, + overflow: "hidden", + + [theme.fn.largerThan("sm")]: { + display: "none", + }, + }, +})); + +export const mainLinksMockdata = [ + { icon: GlobeIcon, label: "Orders", href: "/" }, + { icon: ContainerIcon, label: "Products", href: "/products" }, + { icon: PackageIcon, label: "Shops", href: "/shops" }, + { icon: WebhookIcon, label: "Channels", href: "/channels" }, +]; + +export function AppShell({ + loadingTabs, + data, + activeTab, + setActiveTab, + children, +}) { + const [navMode, setNavMode] = useState(null); + const [showModal, setShowModal] = useSharedState("createModal", false); + const router = useRouter(); + + const toggle = (value) => { + if (value === navMode) { + setNavMode(null); + } else { + setNavMode(value); + } + }; + + const ref = useClickOutside(() => setNavMode(null)); + + const { classes, cx } = useStyles({}); + + const { data: shopData } = useSWR(SHOPS_QUERY, gqlFetcher); + + const { data: channelData, error } = useSWR(CHANNELS_QUERY, gqlFetcher); + + const theme = useMantineTheme(); + + const { toggleColorScheme } = useMantineColorScheme(); + + const createModals = { + Order: ( + + ), + Shop: , + Channel: ( + + ), + Match: ( + + ), + }; + + const active = mainLinksMockdata.find( + (item) => item.href === router?.pathname + ); + + const mainLinks = mainLinksMockdata.map((link) => ( + + + setActive(link.label)} + component="a" + className={cx(classes.mainLink, { + [classes.mainLinkActive]: router?.pathname === link.href, + })} + my={3} + > + + + + + )); + + const links = data?.map(({ label, count }, index) => ( + { + event.preventDefault(); + setActiveTab(index); + }} + key={index} + > + {label} + {(count || count === 0) && {count}} + + )); + + const NavigationMap = { + primary: mainLinksMockdata.map((link, index) => ( + + + + {link.label} + + + )), + secondary: data.map((link, index) => ( + { + setActiveTab(index); + setNavMode(null); + }} + > + {link.label} + {(link?.count || link.count === 0) && ( + + {link.count} + + )} + + )), + }; + + const signOut = async () => { + await request( + "/api/graphql", + gql` + mutation { + endSession + } + ` + ); + router.push("/signin"); + }; + + return ( + + {createModals[showModal]} + + +
+
+ + + +
+ {mainLinks} + + + + + + + + + + {theme.colorScheme === "dark" ? : } + + + +
+ {(data || loadingTabs) && ( +
+ + {active?.label} + + + + {links} + {loadingTabs && ( + + + + + + )} +
+ )} +
+
+ + + +
+ +
+ + {/* + + {data && data[activeTab] && ( + + )} + */} + + {/* + + */} + {/* + {data && data[activeTab] && ( + + )} */} + + { + // setNavMode("primary"); + toggle("primary"); + }} + sx={{ + border: `1px solid ${ + theme.colors.gray[theme.colorScheme === "dark" ? 9 : 2] + }`, + boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)", + fontSize: "13px", + background: + theme.colorScheme === "dark" + ? theme.colors.dark[5] + : theme.fn.lighten(theme.colors.blueGray[0], 0.5), + color: + theme.colorScheme === "dark" + ? theme.colors.dark[0] + : theme.colors.blueGray[6], + }} + > + + + {data && data[activeTab] && ( + + )} + + {(styles) => ( + + {NavigationMap[navMode] && NavigationMap[navMode]} + + )} + + + + + + toggleColorScheme()} + size={28} + color={theme.colorScheme === "dark" ? "gray" : "dark"} + radius="sm" + sx={{ + border: `1px solid ${ + theme.colors.dark[theme.colorScheme === "dark" ? 1 : 8] + }`, + boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)", + }} + > + {theme.colorScheme === "dark" ? : } + + +
+ {/* + {data && data[activeTab] && ( + + )} + */} +
+
+ + {children} + +
+ ); +} + +function CreateModalButton({ + shopData, + setShowModal, + position = "right", + buttonColor, + buttonBorder, + buttonShadow, +}) { + const theme = useMantineTheme(); + + return ( + + + + } + styles={{ + item: { + color: + theme.colorScheme === "dark" + ? theme.colors.blueGray[2] + : theme.colors.blueGray[7], + textTransform: "uppercase", + fontWeight: 600, + letterSpacing: 0.4, + }, + }} + position={position} + > + Create a new + {[ + { mode: "Order", enabled: shopData?.shops.length > 0 }, + { mode: "Shop" }, + { mode: "Channel" }, + { mode: "Match" }, + ].map(({ mode, enabled = true }) => ( + setShowModal(mode)} + disabled={!enabled} + > + {mode} + + ))} + + ); +} + +function KeyPopper({ + shopData, + setShowModal, + position = "right", + buttonColor, + buttonBorder, +}) { + const [referenceElement, setReferenceElement] = useState(null); + const [visible, setVisible] = useState(false); + const theme = useMantineTheme(); + const clipboard = useClipboard({ timeout: 1000 }); + const notifications = useNotifications(); + const modals = useModals(); + + const { data, error, mutate } = useSWR( + gql` + query KEY_QUERY { + apiKeys { + id + } + } + `, + gqlFetcher + ); + + async function createKey() { + return await request( + "/api/graphql", + gql` + mutation { + createapiKey(data: {}) { + id + } + } + ` + ) + .then(async ({ createapiKey }) => { + await mutate(({ apiKeys }) => { + return { + apiKeys: [createapiKey], + }; + }, false); + notifications.showNotification({ + title: `API Key has been generated.`, + // message: JSON.stringify(data), + }); + }) + .catch((error) => { + // setLoading(false); + notifications.showNotification({ + title: error.response.errors[0].extensions.code, + message: error.response.errors[0].message, + color: "red", + }); + }); + } + + return ( + + setVisible((m) => !m)} + > + + + + + + {data?.apiKeys[0] ? ( + + + + {data.apiKeys[0].id} + clipboard.copy(data.apiKeys[0].id)} + > + {clipboard.copied ? ( + + ) : ( + + )} + + + + + + modals.openConfirmModal({ + title: ( + + Regenerate Key + + ), + centered: true, + children: ( + + Are you sure you want to regenerate this key? This + action will invalidate your previous key and any + applications using the previous key will need to be + updated. + + ), + labels: { + confirm: "Regenerate Key", + cancel: "No don't regenerate it", + }, + confirmProps: { color: "red" }, + // onCancel: () => console.log("Cancel"), + onConfirm: createKey, + }) + } + > + + + + ) : ( + + )} + + {/* setVisible((m) => !m)} + > + + */} + + + + ); +} diff --git a/components/ChannelGrid/dangerous/index.js b/components/ChannelGrid/dangerous/index.js new file mode 100644 index 0000000..d804a68 --- /dev/null +++ b/components/ChannelGrid/dangerous/index.js @@ -0,0 +1,143 @@ +import React, { useState } from "react"; +import { + Text, + useMantineTheme, + Group, + Box, + ActionIcon, + Input, + Button, + Loader, + Badge, + Stack, +} from "@mantine/core"; +import { XIcon } from "@primer/octicons-react"; +import { + CHANNELS_QUERY, + CREATE_CHANNEL_METAFIELD_MUTATION, + DELETE_CHANNEL_MUTATION, +} from "@graphql/channels"; +import { useNotifications } from "@mantine/notifications"; +import useSWR from "swr"; +import { gqlFetcher } from "@lib/gqlFetcher"; +import request from "graphql-request"; +import { useModals } from "@mantine/modals"; + +export const Dangerous = ({ channelId, name }) => { + const [loading, setLoading] = useState(false); + const { mutate: mutateChannels } = useSWR(CHANNELS_QUERY, gqlFetcher); + + const theme = useMantineTheme(); + const notifications = useNotifications(); + const modals = useModals(); + + const openDeleteModal = () => + modals.openConfirmModal({ + title: ( + + Delete {name} + + ), + closeOnConfirm: false, + centered: true, + children: ( + + Are you sure you want to delete this channel? This action is + destructive and will delete all data associated with this channel + including orders, links, matches, etc. + + ), + labels: { confirm: "Delete channel", cancel: "No don't delete it" }, + confirmProps: { color: "red", loading }, + // onCancel: () => console.log("Cancel"), + onConfirm: async () => + await request("/api/graphql", DELETE_CHANNEL_MUTATION, { + id: channelId, + }) + .then(async () => { + await mutateChannels(({ channels }) => { + const newData = []; + for (const item of channels) { + if (item.id !== channelId) { + newData.push(item); + } + } + return { + channels: newData, + }; + }, false); + modals.closeAll(); + notifications.showNotification({ + title: `Channel has been deleted.`, + // message: JSON.stringify(data), + }); + }) + .catch((error) => { + setLoading(false); + notifications.showNotification({ + title: error.response.errors[0].extensions.code, + message: error.response.errors[0].message, + color: "red", + }); + }), + }); + + return ( + <> + + Delete channel + + + + + + + Deleting the channel will permanently delete all data connected to + this shop including orders, links, matches, etc. This action is + irreversible. + + + + + + + ); +}; diff --git a/components/ChannelGrid/details/EditDetails.js b/components/ChannelGrid/details/EditDetails.js new file mode 100644 index 0000000..ab2e1e2 --- /dev/null +++ b/components/ChannelGrid/details/EditDetails.js @@ -0,0 +1,156 @@ +import React, { useState } from "react"; +import { + Text, + useMantineTheme, + Button, + Group, + Loader, + Input, + ActionIcon, +} from "@mantine/core"; +import useSWR from "swr"; +import { gqlFetcher } from "@lib/gqlFetcher"; +import { PencilIcon, XIcon } from "@primer/octicons-react"; +import request from "graphql-request"; +import { useNotifications } from "@mantine/notifications"; +import { CHANNELS_QUERY, UPDATE_CHANNEL_MUTATION } from "@graphql/channels"; + +export const EditDetails = ({ detail, channelId }) => { + const theme = useMantineTheme(); + const notifications = useNotifications(); + const [editMode, setEditMode] = useState(false); + const [loading, setLoading] = useState(false); + const [value, setValue] = useState(detail.value); + const { mutate: mutateChannels } = useSWR(CHANNELS_QUERY, gqlFetcher); + + return editMode ? ( + + setValue(event.target.value)} + styles={{ + wrapper: { + width: "100%", + }, + input: { + minHeight: 0, + height: 22, + }, + }} + autoFocus + /> + + + { + setEditMode(false); + setValue(detail.value); + }} + > + + + + + ) : ( + + + {detail.type === "password" + ? "·································" + : detail.value} + + setEditMode(true)} + ml="auto" + > + + + {/* */} + + ); +}; diff --git a/components/ChannelGrid/details/index.js b/components/ChannelGrid/details/index.js new file mode 100644 index 0000000..a6020b4 --- /dev/null +++ b/components/ChannelGrid/details/index.js @@ -0,0 +1,95 @@ +import React from "react"; +import { Paper, Text, useMantineTheme, Group, Box, Stack } from "@mantine/core"; +import { EditDetails } from "./EditDetails"; + +export const Details = ({ channelId, name, type, domain, accessToken }) => { + const theme = useMantineTheme(); + const details = [ + { + label: "Name", + value: name, + edit: true, + functionValue: "name", + }, + { + label: "Domain", + value: domain, + edit: true, + functionValue: "domain", + }, + { + label: "Access Token", + value: accessToken, + edit: true, + functionValue: "accessToken", + type: "password", + }, + { + label: "Type", + value: type, + transform: "uppercase", + }, + ]; + + return ( + + + + Channel Details + + + Edit details + + + + {details.map((detail) => ( + + + + {detail.label} + + + + {detail.edit ? ( + + ) : ( + + {detail.value} + + )} + + + ))} + + ); +}; diff --git a/components/ChannelGrid/functions/EditFunction.js b/components/ChannelGrid/functions/EditFunction.js new file mode 100644 index 0000000..9b05026 --- /dev/null +++ b/components/ChannelGrid/functions/EditFunction.js @@ -0,0 +1,161 @@ +import React, { useState } from "react"; +import { + Text, + useMantineTheme, + Button, + Group, + Badge, + TextInput, + Collapse as Coll, + Box, + Stack, +} from "@mantine/core"; +import useSWR from "swr"; +import { gqlFetcher } from "@lib/gqlFetcher"; +import request from "graphql-request"; +import { useNotifications } from "@mantine/notifications"; +import { CHANNELS_QUERY, UPDATE_CHANNEL_MUTATION } from "@graphql/channels"; + +export const EditFunction = ({ detail, channelId }) => { + const notifications = useNotifications(); + const theme = useMantineTheme(); + const [opened, setOpen] = useState(false); + const [loading, setLoading] = useState(false); + const [endpoint, setEndpoint] = useState(detail.value); + const { mutate: mutateChannels } = useSWR(CHANNELS_QUERY, gqlFetcher); + + + return ( + + + + + {detail.label} + + + {detail.description} + + + + setOpen((o) => !o)} + sx={{ + border: `1px solid ${ + theme.colors[detail.value ? "blue" : "green"][ + theme.colorScheme === "dark" ? 9 : 1 + ] + }`, + cursor: "pointer", + }} + > + {detail.value ? "Active" : "Activate"} + + + + +
{ + event.preventDefault(); + if (endpoint !== detail.value) { + setLoading(true); + let data = {}; + data[`${detail.functionValue}`] = endpoint; + await request("/api/graphql", UPDATE_CHANNEL_MUTATION, { + id: channelId, + data, + }) + .then(async ({ updateChannel }) => { + setLoading(false); + await mutateChannels(({ channels }) => { + const newData = []; + for (const item of channels) { + if (item.id === updateChannel.id) { + newData.push(updateChannel); + } else { + newData.push(item); + } + } + return { + channels: newData, + }; + }, false); + notifications.showNotification({ + title: `Endpoint has been updated.`, + // message: JSON.stringify(data), + }); + setOpen(false); + }) + .catch((error) => { + setLoading(false); + notifications.showNotification({ + title: error.response.errors[0].extensions.code, + message: error.response.errors[0].message, + color: "red", + }); + }); + } + }} + > + : } + rightSectionWidth={loading ? 26 : 70} + pb="sm" + px="sm" + id="endpoint" + spellcheck="false" + + rightSection={ + + } + size="sm" + value={endpoint} + onChange={(event) => setEndpoint(event.target.value)} + /> + +
+
+ ); +}; diff --git a/components/ChannelGrid/functions/index.js b/components/ChannelGrid/functions/index.js new file mode 100644 index 0000000..9a8dc82 --- /dev/null +++ b/components/ChannelGrid/functions/index.js @@ -0,0 +1,54 @@ +import React from "react"; +import { Group, Paper, Stack, Text, useMantineTheme } from "@mantine/core"; +import { useNotifications } from "@mantine/notifications"; +import { EditFunction } from "./EditFunction"; + +export const Functions = ({ + channelId, + searchProductsEndpoint, + createPurchaseEndpoint, +}) => { + const theme = useMantineTheme(); + const notifications = useNotifications(); + + const functions = [ + { + label: "Search Products", + description: "Endpoint to search channel products", + value: searchProductsEndpoint, + functionValue: "searchProductsEndpoint", + }, + { + label: "Create Purchase", + description: "Endpoint to create channel purchase orders", + value: createPurchaseEndpoint, + functionValue: "createPurchaseEndpoint", + }, + ]; + + return ( + + + + Advanced Settings + + + Control how Openship interacts with your channel + + + {functions.map((detail) => ( + + ))} + + ); +}; diff --git a/components/ChannelGrid/index.js b/components/ChannelGrid/index.js new file mode 100644 index 0000000..0d4356f --- /dev/null +++ b/components/ChannelGrid/index.js @@ -0,0 +1,67 @@ +import React from "react"; +import { Container, useMantineTheme, Group, Stack } from "@mantine/core"; +import { Details } from "./details"; +import { Functions } from "./functions"; +import { Orders } from "./orders"; +import { Metafields } from "./metafields"; +import { Dangerous } from "./dangerous"; +import { Webhooks } from "./webhooks"; + +export function ChannelGrid({ + id, + name, + type, + domain, + accessToken, + searchProductsEndpoint, + createPurchaseEndpoint, + getWebhooksEndpoint, + createWebhookEndpoint, + deleteWebhookEndpoint, + metafields, +}) { + const theme = useMantineTheme(); + + return ( + + {id && ( + + +
+ + + + + + + + + + )} + + ); +} diff --git a/components/ChannelGrid/metafields/EditMetafield.js b/components/ChannelGrid/metafields/EditMetafield.js new file mode 100644 index 0000000..9c74ef2 --- /dev/null +++ b/components/ChannelGrid/metafields/EditMetafield.js @@ -0,0 +1,294 @@ +import React, { useState } from "react"; +import { + Text, + useMantineTheme, + Button, + Group, + Loader, + Input, + ActionIcon, + Box, +} from "@mantine/core"; +import useSWR from "swr"; +import { gqlFetcher } from "@lib/gqlFetcher"; +import { PencilIcon, TrashIcon, XIcon } from "@primer/octicons-react"; +import request from "graphql-request"; +import { useNotifications } from "@mantine/notifications"; +import { + CHANNELS_QUERY, + DELETE_CHANNEL_METAFIELD_MUTATION, + UPDATE_CHANNEL_METAFIELD_MUTATION, +} from "@graphql/channels"; + +export const EditMetafield = ({ metafield, channelId }) => { + const theme = useMantineTheme(); + const notifications = useNotifications(); + const [editMode, setEditMode] = useState(false); + const [loading, setLoading] = useState(false); + const [value, setValue] = useState(metafield.value); + const [key, setKey] = useState(metafield.key); + const { mutate: mutateChannels } = useSWR(CHANNELS_QUERY, gqlFetcher); + + return editMode ? ( + + + {/* + {customInput.key} + */} + setKey(event.target.value)} + styles={{ + wrapper: { + width: "100%", + }, + input: { + minHeight: 0, + height: 22, + fontWeight: 600, + color: + theme.colors.blueGray[theme.colorScheme === "dark" ? 4 : 5], + }, + }} + ml={-2} + /> + + + setValue(event.target.value)} + styles={{ + wrapper: { + width: "100%", + }, + input: { + minHeight: 0, + height: 22, + }, + }} + autoFocus + /> + + + { + setEditMode(false); + setValue(metafield.value); + setKey(metafield.key); + }} + > + + + + + + ) : ( + + + + {metafield.key} + + + + + + {metafield.value} + + + {loading && } + { + event.preventDefault(); + if (value && key) { + setLoading(true); + await request( + "/api/graphql", + DELETE_CHANNEL_METAFIELD_MUTATION, + { + where: { id: metafield.id }, + } + ) + .then(async ({ deleteChannelMetafield }) => { + setLoading(false); + + await mutateChannels(({ channels }) => { + const newData = []; + for (const item of channels) { + if (item.id === channelId) { + const filteredChannel = { + ...item, + metafields: item.metafields.filter( + (c) => c.id !== deleteChannelMetafield.id + ), + }; + newData.push(filteredChannel); + } else { + newData.push(item); + } + } + return { + channels: newData, + }; + }, false); + notifications.showNotification({ + title: `Custom detail is deleted.`, + // message: JSON.stringify(data), + }); + }) + .catch((error) => { + setLoading(false); + notifications.showNotification({ + title: error.response.errors[0].extensions.code, + message: error.response.errors[0].message, + color: "red", + }); + }); + } + }} + > + + + + setEditMode(true)} + ml="auto" + radius={0} + sx={{ + borderTopRightRadius: theme.radius.sm, + borderBottomRightRadius: theme.radius.sm, + }} + > + + + + + + + ); +}; diff --git a/components/ChannelGrid/metafields/index.js b/components/ChannelGrid/metafields/index.js new file mode 100644 index 0000000..8184948 --- /dev/null +++ b/components/ChannelGrid/metafields/index.js @@ -0,0 +1,232 @@ +import React, { useState } from "react"; +import { + Paper, + Text, + useMantineTheme, + Group, + Box, + ActionIcon, + Collapse, + Input, + Button, + Loader, + Stack, +} from "@mantine/core"; +import { EditMetafield } from "./EditMetafield"; +import { PlusIcon, XIcon } from "@primer/octicons-react"; +import { + CHANNELS_QUERY, + CREATE_CHANNEL_METAFIELD_MUTATION, +} from "@graphql/channels"; +import { useNotifications } from "@mantine/notifications"; +import useSWR from "swr"; +import { gqlFetcher } from "@lib/gqlFetcher"; +import request from "graphql-request"; + +export const Metafields = ({ channelId, metafields }) => { + const theme = useMantineTheme(); + const [opened, setOpen] = useState(false); + + return ( + + + + + Metafields + + + Create channel-specific fields + + + setOpen(!opened)} + color="green" + size={28} + radius="sm" + ml="auto" + sx={{ + border: `1px solid ${ + theme.colors.green[theme.colorScheme === "dark" ? 9 : 1] + }`, + }} + mr={2} + > + + + + + + + {metafields.map((metafield) => ( + + ))} + + ); +}; + +const CreateMetafield = ({ channelId, setOpen, opened }) => { + const theme = useMantineTheme(); + const notifications = useNotifications(); + const [loading, setLoading] = useState(false); + const [value, setValue] = useState(""); + const [key, setKey] = useState(""); + const { mutate: mutateChannels } = useSWR(CHANNELS_QUERY, gqlFetcher); + + return ( + + + setKey(event.target.value)} + styles={{ + wrapper: { + width: "100%", + }, + input: { + minHeight: 0, + height: 22, + fontWeight: 600, + color: + theme.colors.blueGray[theme.colorScheme === "dark" ? 4 : 5], + }, + }} + ml={-2} + autoFocus={opened} + /> + + + setValue(event.target.value)} + styles={{ + wrapper: { + width: "100%", + }, + input: { + minHeight: 0, + height: 22, + }, + }} + /> + + {loading && } + + + { + setOpen(false); + setValue(""); + setKey(""); + }} + > + + + + + + ); +}; diff --git a/components/ChannelGrid/orders/index.js b/components/ChannelGrid/orders/index.js new file mode 100644 index 0000000..289b751 --- /dev/null +++ b/components/ChannelGrid/orders/index.js @@ -0,0 +1,280 @@ +import React, { useState } from "react"; +import { + Skeleton, + Paper, + Text, + Box, + useMantineTheme, + Divider, + Button, + Group, + Input, + Stack, +} from "@mantine/core"; +import { CHANNEL_ORDERS_QUERY } from "@graphql/orders"; +import useSWR from "swr"; +import { gqlFetcher } from "@lib/gqlFetcher"; +import { Collapse } from "@primitives/collapse"; +import { Order } from "@primitives/order"; +import { CartItem } from "@primitives/cartItem"; +import { ArrowRightIcon, SearchIcon } from "@primer/octicons-react"; + +export const Orders = ({ channelId }) => { + const theme = useMantineTheme(); + const [searchEntry, setSearchEntry] = useState(""); + + const { + data, + error, + mutate: mutateOrders, + } = useSWR( + [ + CHANNEL_ORDERS_QUERY, + JSON.stringify({ + where: { + cartItems: { + some: { channel: { id: { equals: channelId } } }, + }, + OR: [ + { orderName: { contains: searchEntry, mode: "insensitive" } }, + { first_name: { contains: searchEntry, mode: "insensitive" } }, + { last_name: { contains: searchEntry, mode: "insensitive" } }, + { streetAddress1: { contains: searchEntry, mode: "insensitive" } }, + { streetAddress2: { contains: searchEntry, mode: "insensitive" } }, + { city: { contains: searchEntry, mode: "insensitive" } }, + { state: { contains: searchEntry, mode: "insensitive" } }, + { zip: { contains: searchEntry, mode: "insensitive" } }, + ], + }, + cartItemsWhere: { channel: { id: { equals: channelId } } }, + // skip: parseInt(skip), + take: 5, + }), + ], + gqlFetcher + ); + if (error) return
Failed to load
; + + return ( + + + + Orders + + + Search orders from Openship + + + + + } + size="md" + onKeyPress={(e) => { + if (e.key === "Enter") { + setSearchEntry(e.target.value); + } + }} + /> + + {!data && ( + <> + + + + + )} + {data?.orders.length === 0 && ( + + No orders found + + )} + {data?.orders?.map((order) => { + const { + id, + orderId, + shop, + orderName, + first_name, + last_name, + streetAddress1, + streetAddress2, + city, + state, + zip, + createdAt, + cartItems, + lineItems, + orderError, + } = order; + return ( + + ( + <> + + + {" "} + {first_name} {last_name} +
+ {streetAddress1} {streetAddress2} +
+ {city} + {", "} + {state} {zip} + + } + /> +
+ {open && ( + <> + {cartItems?.length > 0 && ( + <> + + + + Cart Items + + {cartItems.map( + ({ + id: cartId, + name, + quantity, + price, + image, + productId, + variantId, + purchaseId, + lineItemId, + channel, + url: cartURL, + error, + status, + }) => ( + + + {cartURL && ( + + )} + + } + /> + + ) + )} + + + )} + + )} + + )} + /> +
+ ); + })} +
+ ); +}; diff --git a/components/ChannelGrid/webhooks/EditWebhook.js b/components/ChannelGrid/webhooks/EditWebhook.js new file mode 100644 index 0000000..8772b6e --- /dev/null +++ b/components/ChannelGrid/webhooks/EditWebhook.js @@ -0,0 +1,294 @@ +import React, { useState } from "react"; +import { + Text, + useMantineTheme, + Button, + Group, + Loader, + Input, + ActionIcon, + Box, +} from "@mantine/core"; +import useSWR from "swr"; +import { gqlFetcher } from "@lib/gqlFetcher"; +import { PencilIcon, TrashIcon, XIcon } from "@primer/octicons-react"; +import request from "graphql-request"; +import { useNotifications } from "@mantine/notifications"; +import { + CHANNELS_QUERY, + DELETE_CHANNEL_METAFIELD_MUTATION, + UPDATE_CHANNEL_METAFIELD_MUTATION, +} from "@graphql/channels"; + +export const EditWebhook = ({ metafield, channelId }) => { + const theme = useMantineTheme(); + const notifications = useNotifications(); + const [editMode, setEditMode] = useState(false); + const [loading, setLoading] = useState(false); + const [value, setValue] = useState(metafield.value); + const [key, setKey] = useState(metafield.key); + const { mutate: mutateChannels } = useSWR(CHANNELS_QUERY, gqlFetcher); + + return editMode ? ( + + + {/* + {customInput.key} + */} + setKey(event.target.value)} + styles={{ + wrapper: { + width: "100%", + }, + input: { + minHeight: 0, + height: 22, + fontWeight: 600, + color: + theme.colors.blueGray[theme.colorScheme === "dark" ? 4 : 5], + }, + }} + ml={-2} + /> + + + setValue(event.target.value)} + styles={{ + wrapper: { + width: "100%", + }, + input: { + minHeight: 0, + height: 22, + }, + }} + autoFocus + /> + + + { + setEditMode(false); + setValue(metafield.value); + setKey(metafield.key); + }} + > + + + + + + ) : ( + + + + {metafield.key} + + + + + + {metafield.value} + + + {loading && } + { + event.preventDefault(); + if (value && key) { + setLoading(true); + await request( + "/api/graphql", + DELETE_CHANNEL_METAFIELD_MUTATION, + { + where: { id: metafield.id }, + } + ) + .then(async ({ deleteChannelMetafield }) => { + setLoading(false); + + await mutateChannels(({ channels }) => { + const newData = []; + for (const item of channels) { + if (item.id === channelId) { + const filteredChannel = { + ...item, + metafields: item.metafields.filter( + (c) => c.id !== deleteChannelMetafield.id + ), + }; + newData.push(filteredChannel); + } else { + newData.push(item); + } + } + return { + channels: newData, + }; + }, false); + notifications.showNotification({ + title: `Custom detail is deleted.`, + // message: JSON.stringify(data), + }); + }) + .catch((error) => { + setLoading(false); + notifications.showNotification({ + title: error.response.errors[0].extensions.code, + message: error.response.errors[0].message, + color: "red", + }); + }); + } + }} + > + + + + setEditMode(true)} + ml="auto" + radius={0} + sx={{ + borderTopRightRadius: theme.radius.sm, + borderBottomRightRadius: theme.radius.sm, + }} + > + + + + + + + ); +}; diff --git a/components/ChannelGrid/webhooks/index.js b/components/ChannelGrid/webhooks/index.js new file mode 100644 index 0000000..0b2c6bf --- /dev/null +++ b/components/ChannelGrid/webhooks/index.js @@ -0,0 +1,444 @@ +import React, { useState } from "react"; +import { + Paper, + Text, + useMantineTheme, + Group, + Box, + ActionIcon, + Collapse, + Input, + Button, + Loader, + Table, + Code, + Divider, + TextInput, + Stack, +} from "@mantine/core"; +import { EditMetafield } from "./EditWebhook"; +import { GearIcon, PlusIcon, TrashIcon, XIcon } from "@primer/octicons-react"; +import { + CHANNELS_QUERY, + UPDATE_CHANNEL_MUTATION, +} from "@graphql/channels"; +import { useNotifications } from "@mantine/notifications"; +import useSWR from "swr"; +import { gqlFetcher } from "@lib/gqlFetcher"; +import request from "graphql-request"; + +export const Webhooks = ({ + channelId, + type, + domain, + accessToken, + getWebhooksEndpoint, + createWebhookEndpoint, + deleteWebhookEndpoint, +}) => { + const theme = useMantineTheme(); + const [opened, setOpen] = useState(false); + const [deleteLoading, setDeleteLoading] = useState(null); + const [createLoading, setCreateLoading] = useState(null); + const notifications = useNotifications(); + + const showToggle = ({ + opened, + getWebhooksEndpoint, + createWebhookEndpoint, + deleteWebhookEndpoint, + }) => { + if ( + !getWebhooksEndpoint || + !createWebhookEndpoint || + !deleteWebhookEndpoint + ) { + return true; + } + if (opened) { + return opened; + } + return false; + }; + + const params = new URLSearchParams({ + accessToken, + domain, + }).toString(); + + const url = `${getWebhooksEndpoint}?${params}`; + + const { data, error, mutate } = useSWR(url); + const existingWebhooks = [...(data?.webhooks ? data.webhooks : [])]; + + const recommendWebhooks = [ + { + callbackUrl: `/api/triggers/cancel-purchase/${type}`, + topic: "ORDER_CANCELLED", + }, + { + callbackUrl: `/api/triggers/create-tracking/${type}`, + topic: "TRACKING_CREATED", + }, + ].filter((item) => { + return ( + existingWebhooks.filter((existItem) => { + return ( + existItem.topic == item.topic && + existItem.callbackUrl == item.callbackUrl + ); + }).length == 0 + ); + }); + + const platformWebhookEndpoints = [ + { + label: "Get webhooks endpoint", + value: getWebhooksEndpoint, + functionValue: "getWebhooksEndpoint", + }, + { + label: "Create webhook endpoint", + value: createWebhookEndpoint, + functionValue: "createWebhookEndpoint", + }, + { + label: "Delete webhook endpoint", + value: deleteWebhookEndpoint, + functionValue: "deleteWebhookEndpoint", + }, + ]; + + return ( + + + + + Webhooks + + + Create platform webhooks to keep Openship in sync + + + {/* setOpen(!opened)} + color="green" + size={28} + radius="sm" + ml="auto" + sx={{ + border: `1px solid ${ + theme.colors.green[theme.colorScheme === "dark" ? 9 : 1] + }`, + }} + mr={2} + > + + */} + {getWebhooksEndpoint && createWebhookEndpoint && deleteWebhookEndpoint && ( + setOpen(!opened)} + color="gray" + size={28} + radius="sm" + ml="auto" + sx={{ + border: `1px solid ${ + theme.colors.green[theme.colorScheme === "dark" ? 9 : 1] + }`, + }} + mr={2} + > + + + )} + + + {/* */} + + + Webhook configuration + + {platformWebhookEndpoints.map((setting) => ( + + ))} + + + {getWebhooksEndpoint && createWebhookEndpoint && deleteWebhookEndpoint && ( + + + + + + + + + + {[...existingWebhooks, ...recommendWebhooks].map((webhook) => ( + + + + + + ))} + +
TopicURLActions
+ {webhook.topic} + + + {webhook.callbackUrl} + + + {webhook.id ? ( + { + event.preventDefault(); + setDeleteLoading(webhook.id); + const res = await fetch(`${deleteWebhookEndpoint}`, { + body: JSON.stringify({ + accessToken, + domain, + webhookId: webhook.id, + }), + headers: { + "Content-Type": "application/json", + }, + method: "POST", + }); + + const { error, success } = await res.json(); + setDeleteLoading(null); + + if (error) { + setDeleteLoading(null); + notifications.showNotification({ + title: `Webhook could not be deleted.`, + message: error, + color: "red", + }); + } else { + setDeleteLoading(null); + await mutate(); + notifications.showNotification({ + title: `Webhook has been deleted.`, + // message: JSON.stringify(data), + }); + } + }} + color="red" + size={20} + radius="sm" + ml="auto" + sx={{ + border: `1px solid ${ + theme.colors.red[theme.colorScheme === "dark" ? 9 : 1] + }`, + }} + mr={2} + loading={webhook.id === deleteLoading} + > + + + ) : ( + { + // setOpen(true); + // setCallbackUrl(webhook.callbackUrl); + // setTopic(webhook.topic); + // }} + onClick={async (event) => { + event.preventDefault(); + setCreateLoading(webhook.id); + const res = await fetch(`${createWebhookEndpoint}`, { + body: JSON.stringify({ + accessToken, + domain, + endpoint: webhook.callbackUrl, + topic: webhook.topic, + }), + headers: { + "Content-Type": "application/json", + }, + method: "POST", + }); + + const { error, success } = await res.json(); + setCreateLoading(null); + + if (error) { + setCreateLoading(null); + notifications.showNotification({ + title: `Webhook could not be created.`, + message: error, + color: "red", + }); + } else { + setCreateLoading(null); + await mutate(); + notifications.showNotification({ + title: `Webhook has been created.`, + // message: JSON.stringify(data), + }); + } + }} + color="blue" + size={20} + radius="sm" + ml="auto" + sx={{ + border: `1px solid ${ + theme.colors.blue[ + theme.colorScheme === "dark" ? 9 : 1 + ] + }`, + }} + mr={2} + loading={webhook.id === createLoading} + > + + + )} +
+ )} +
+ ); +}; + +function EditWebhookSettings({ setting, channelId, setOpen }) { + const notifications = useNotifications(); + const [loading, setLoading] = useState(false); + const [endpoint, setEndpoint] = useState(setting.value); + const { mutate: mutateChannels } = useSWR(CHANNELS_QUERY, gqlFetcher); + + return ( +
{ + event.preventDefault(); + if (endpoint !== setting.value) { + setLoading(true); + let data = {}; + data[`${setting.functionValue}`] = endpoint; + await request("/api/graphql", UPDATE_CHANNEL_MUTATION, { + id: channelId, + data, + }) + .then(async ({ updateChannel }) => { + setLoading(false); + await mutateChannels(({ channels }) => { + const newData = []; + for (const item of channels) { + if (item.id === updateChannel.id) { + newData.push(updateChannel); + } else { + newData.push(item); + } + } + return { + channels: newData, + }; + }, false); + notifications.showNotification({ + title: `Endpoint has been updated.`, + // message: JSON.stringify(data), + }); + // setOpen(false); + }) + .catch((error) => { + setLoading(false); + notifications.showNotification({ + title: error.response.errors[0].extensions.code, + message: error.response.errors[0].message, + color: "red", + }); + }); + } + }} + > + : } + rightSectionWidth={loading ? 26 : 70} + pb="sm" + px="sm" + id="endpoint" + spellcheck="false" + rightSection={ + + } + size="sm" + value={endpoint} + onChange={(event) => setEndpoint(event.target.value)} + /> + + ); +} diff --git a/components/CreateItemDrawer.js b/components/CreateItemDrawer.js new file mode 100644 index 0000000..df9823e --- /dev/null +++ b/components/CreateItemDrawer.js @@ -0,0 +1,164 @@ +import { useCallback, useMemo, useState } from "react" +import isDeepEqual from "fast-deep-equal" +import { Box } from "@keystone-ui/core" +import { Drawer } from "@keystone-ui/modals" +import { useToasts } from "@keystone-ui/toast" +import { LoadingDots } from "@keystone-ui/loading" + +import { gql, useMutation } from "../apollo" +import { useKeystone, useList } from "../context" + +import { Fields } from "../utils/Fields" +import { usePreventNavigation } from "../utils/usePreventNavigation" +import { GraphQLErrorNotice } from "./GraphQLErrorNotice" + +export function CreateItemDrawer({ listKey, onClose, onCreate }) { + const { createViewFieldModes } = useKeystone() + const list = useList(listKey) + + const toasts = useToasts() + + const [ + createItem, + { loading, error, data: returnedData } + ] = useMutation(gql`mutation($data: ${list.gqlNames.createInputName}!) { + item: ${list.gqlNames.createMutationName}(data: $data) { + id + label: ${list.labelField} + } +}`) + + const [value, setValue] = useState(() => { + const value = {} + Object.keys(list.fields).forEach(fieldPath => { + value[fieldPath] = { + kind: "value", + value: list.fields[fieldPath].controller.defaultValue + } + }) + return value + }) + + const invalidFields = useMemo(() => { + const invalidFields = new Set() + + Object.keys(value).forEach(fieldPath => { + const val = value[fieldPath].value + + const validateFn = list.fields[fieldPath].controller.validate + if (validateFn) { + const result = validateFn(val) + if (result === false) { + invalidFields.add(fieldPath) + } + } + }) + return invalidFields + }, [list, value]) + + const [forceValidation, setForceValidation] = useState(false) + + const data = {} + Object.keys(list.fields).forEach(fieldPath => { + const { controller } = list.fields[fieldPath] + const serialized = controller.serialize(value[fieldPath].value) + if ( + !isDeepEqual(serialized, controller.serialize(controller.defaultValue)) + ) { + Object.assign(data, serialized) + } + }) + + const shouldPreventNavigation = + !returnedData?.item && Object.keys(data).length !== 0 + + usePreventNavigation(shouldPreventNavigation) + + return ( + { + const newForceValidation = invalidFields.size !== 0 + setForceValidation(newForceValidation) + + if (newForceValidation) return + + createItem({ + variables: { + data + } + }) + .then(({ data }) => { + const label = data.item.label || data.item.id + onCreate({ id: data.item.id, label }) + toasts.addToast({ + title: label, + message: "Created Successfully", + tone: "positive" + }) + }) + .catch(() => {}) + } + }, + cancel: { + label: "Cancel", + action: () => { + if ( + !shouldPreventNavigation || + window.confirm( + "There are unsaved changes, are you sure you want to exit?" + ) + ) { + onClose() + } + } + } + }} + > + {createViewFieldModes.state === "error" && ( + + )} + {createViewFieldModes.state === "loading" && ( + + )} + {error && ( + + )} + + { + setValue(oldValues => getNewValue(oldValues)) + }, [])} + /> + + + ) +} \ No newline at end of file diff --git a/components/CreateViews/CreateChannelView/ChannelForm.js b/components/CreateViews/CreateChannelView/ChannelForm.js new file mode 100644 index 0000000..ebc2cf8 --- /dev/null +++ b/components/CreateViews/CreateChannelView/ChannelForm.js @@ -0,0 +1,154 @@ +import { formList, useForm } from "@mantine/form"; +import { + TextInput, + Button, + useMantineTheme, + Box, + Paper, + Text, + Stack, + Code, +} from "@mantine/core"; + +export const ChannelForm = ({ + label, + fields, + metafields, + handleSubmit, + buttonText, + loading, +}) => { + const theme = useMantineTheme(); + + const arrayToObject = (array, key) => + array.reduce( + (obj, item) => ({ + ...obj, + [item[key]]: "", + }), + {} + ); + + const form = useForm({ + initialValues: { + ...arrayToObject(fields, "name"), + ...(metafields && { + metafields: formList( + metafields.map(({ name }) => ({ key: name, value: "" })) + ), + }), + }, + }); + + return ( +
+ {fields.map(({ name, title, placeholder, rightSection }) => ( + + ))} + {metafields && ( + + + + {label} fields + + + {metafields.map( + ({ name, title, placeholder, rightSection }, index) => ( + + ) + )} + + )} + + + + + ); +}; diff --git a/components/CreateViews/CreateChannelView/index.js b/components/CreateViews/CreateChannelView/index.js new file mode 100644 index 0000000..442aa2a --- /dev/null +++ b/components/CreateViews/CreateChannelView/index.js @@ -0,0 +1,247 @@ +import { useState } from "react"; +import { Text, useMantineTheme, Box, Select, Modal } from "@mantine/core"; +import { mutate } from "swr"; +import { request } from "graphql-request"; +import { CHANNELS_QUERY, CREATE_CHANNEL_MUTATION } from "@graphql/channels"; +import { useNotifications } from "@mantine/notifications"; +import { useRouter } from "next/router"; +import { ChannelForm } from "./ChannelForm"; + +//hardcoded endpoints fetched through state type +const endpoints = { + shopify: { + searchProductsEndpoint: "/api/search-products/shopify", + createPurchaseEndpoint: "/api/create-purchase/shopify", + getWebhooksEndpoint: "/api/get-webhooks/shopify", + createWebhookEndpoint: "/api/create-webhook/shopify", + deleteWebhookEndpoint: "/api/delete-webhook/shopify", + }, + stockandtrace: { + searchProductsEndpoint: "/api/search-products/stockandtrace", + createPurchaseEndpoint: "/api/create-purchase/stockandtrace", + getWebhooksEndpoint: "/api/get-webhooks/shopify", + createWebhookEndpoint: "/api/create-webhook/shopify", + deleteWebhookEndpoint: "/api/delete-webhook/shopify", + }, + demo: { + domain: "https://openship-channel.vercel.app", + accessToken: "supersecret", + searchProductsEndpoint: + "https://openship-channel.vercel.app/api/search-products", + createPurchaseEndpoint: + "https://openship-channel.vercel.app/api/create-purchase", + }, +}; + +export const CreateChannelView = ({ showModal, setShowModal }) => { + const notifications = useNotifications(); + const router = useRouter(); + const theme = useMantineTheme(); + const [type, setType] = useState("shopify"); + const [loading, setLoading] = useState(false); + + const createChannel = async (values) => { + setLoading(true); + + const res = await request("/api/graphql", CREATE_CHANNEL_MUTATION, { + data: { + ...values, + }, + }) + .then(async () => { + await mutate(CHANNELS_QUERY); + + notifications.showNotification({ + title: `Channel has been added.`, + }); + setShowModal(false); + }) + .catch((error) => { + setLoading(false); + notifications.showNotification({ + title: error.response.errors[0].extensions.code, + message: error.response.errors[0].message, + color: "red", + }); + }); + }; + + const ChannelForms = { + shopify: { + label: "Shopify", + fields: [ + { + title: "URL", + name: "shop", + placeholder: "centralbikeshop", + rightSection: ".myshopify.com", + }, + ], + handleSubmit: (values) => + router.push(`/api/o-auth/channel/shopify?shop=${values.shop}`), + buttonText: "Connect Shopify", + }, + stockandtrace: { + label: "Stock & Trace", + fields: [ + { title: "Name", name: "name", placeholder: "Central Bike Shop" }, + { + title: "Domain", + name: "domain", + placeholder: "centralbikeshop.com", + }, + { + title: "Access Token", + name: "accessToken", + placeholder: "supersecret", + }, + ], + metafields: [ + { title: "Warehouse", name: "Warehouse", placeholder: "WH99" }, + { title: "Account", name: "Account", placeholder: "9837443" }, + { title: "Ship To", name: "Shipto", placeholder: "Supplier" }, + ], + handleSubmit: ({ metafields, ...values }) => + createChannel({ + type: "stockandtrace", + metafields: { create: metafields }, + ...values, + //hardcoded values + ...endpoints.stockandtrace, + }), + }, + shopifycustom: { + label: "Shopify Custom", + fields: [ + { title: "Name", name: "name", placeholder: "Central Bike Shop" }, + { title: "Domain", name: "domain", placeholder: "centralbikeshop.com" }, + { + title: "Access Token", + name: "accessToken", + placeholder: "supersecret", + }, + ], + handleSubmit: (values) => + createChannel({ + type: "shopify", + ...values, + ...endpoints.shopify, + }), + }, + custom: { + label: "Custom", + fields: [ + { title: "Name", name: "name", placeholder: "Central Bike Shop" }, + { title: "Domain", name: "domain", placeholder: "centralbikeshop.com" }, + { + title: "Access Token", + name: "accessToken", + placeholder: "supersecret", + }, + ], + handleSubmit: (values) => + createChannel({ + type: "custom", + ...values, + }), + }, + demo: { + label: "Demo", + fields: [ + { title: "Name", name: "name", placeholder: "Central Bike Shop" }, + ], + handleSubmit: (values) => + createChannel({ + type: "demo", + ...values, + ...endpoints.demo, + }), + }, + }; + + return ( + setShowModal(false)} + // size="xl" + title={ + + Create Channel + + } + > + } + variant="unstyled" + pt={4} + pb={6} + pl={12} + styles={{ + input: { height: 22, lineHeight: 0 }, + wrapper: { + background: + theme.colorScheme === "dark" ? theme.colors.gray[8] : "#fff", + }, + rightSection: { marginRight: 8 }, + }} + onKeyPress={(e) => { + if (e.key === "Enter") { + e.preventDefault(); + setSearchEntry(e.target.value); + setOpened(true); + } + }} + rightSectionWidth={100} + rightSection={{rightSection}} + /> + + + + + Results + + + + + + {data?.products?.map( + ({ title, image, price, productId, variantId }) => ( + { + form.addListItem("channelProducts", { + title, + image, + price, + quantity: 1, + productId, + variantId, + channel: { + id: channel.id, + name: channel.name, + }, + }); + setOpened(false); + }} + sx={{ cursor: "pointer" }} + > + + + + +
+ {title} + + {productId} | {variantId} + +
+
+
+
+ ) + )} +
+
+
+ {/* 0 && opened} + referenceElement={referenceElement} + transition="pop-top-left" + transitionDuration={200} + position="bottom" + // placement="bottom-start" + arrowStyle={{ + backgroundColor: + theme.colorScheme === "dark" + ? theme.colors.dark[5] + : theme.colors.gray[1], + }} + modifiers={[ + // { + // name: "preventOverflow", + // enabled: false, + // }, + // { + // name: "flip", + // enabled: dropdownPosition === "flip", + // }, + { + // @ts-ignore + name: "sameWidth", + enabled: true, + phase: "beforeWrite", + requires: ["computeStyles"], + fn: ({ state }) => { + // eslint-disable-next-line no-param-reassign + state.styles.popper.width = `${state.rects.reference.width}px`; + }, + effect: ({ state }) => { + // eslint-disable-next-line no-param-reassign + state.elements.popper.style.width = `${state.elements.reference.offsetWidth}px`; + }, + }, + // { + // // @ts-ignore + // name: "directionControl", + // enabled: true, + // phase: "main", + // fn: ({ state }) => { + // if (previousPlacement.current !== state.placement) { + // previousPlacement.current = state.placement; + + // const nextDirection = + // state.placement === "top" ? "column-reverse" : "column"; + + // if (direction !== nextDirection && switchDirectionOnFlip) { + // onDirectionChange && onDirectionChange(nextDirection); + // } + // } + // }, + // }, + {}, + ]} + > + + + {data?.products?.map( + ({ title, image, price, productId, variantId }) => ( + + form.addListItem("cartItems", { + title, + image, + price, + quantity: 1, + productId, + variantId, + channel, + }) + } + sx={{ cursor: "pointer" }} + > + + + + +
+ {title} + + {productId} | {variantId} + +
+
+
+
+ ) + )} +
+
+
*/} + + ); +} +function CartItemField({ + id, + image, + channel, + title, + productId, + variantId, + price, + quantity, + form, + index, +}) { + const handlers = useRef(); + const theme = useMantineTheme(); + + return ( + + + + + + + {channel?.name} + + {title} + + {productId} | {variantId} + + + + + ${(price * quantity).toFixed(2)} + + {quantity > 1 && ( + + + (${price} x {quantity}) + + + )} + + + + handlers.current.decrement()} + // disabled={value === min} + // className={classes.control} + onMouseDown={(event) => event.preventDefault()} + sx={{ + // backgroundColor: + // theme.colorScheme === "dark" + // ? theme.colors.dark[7] + // : theme.white, + border: `1px solid ${ + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.gray[3] + }`, + + "&:disabled": { + borderColor: + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.gray[3], + opacity: 0.8, + backgroundColor: "transparent", + }, + }} + > + + + + + handlers.current.increment()} + // disabled={value === min} + // className={classes.control} + onMouseDown={(event) => event.preventDefault()} + sx={{ + // backgroundColor: + // theme.colorScheme === "dark" + // ? theme.colors.dark[7] + // : theme.white, + border: `1px solid ${ + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.gray[3] + }`, + + "&:disabled": { + borderColor: + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.gray[3], + opacity: 0.8, + backgroundColor: "transparent", + }, + }} + > + + + + form.removeListItem("channelProducts", index) + } + onMouseDown={(event) => event.preventDefault()} + sx={{ + border: `1px solid ${ + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.red[3] + }`, + + "&:disabled": { + borderColor: + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.gray[3], + opacity: 0.8, + backgroundColor: "transparent", + }, + }} + ml={6} + > + + + + + + + + + + ); +} diff --git a/components/CreateViews/CreateMatchView/ChannelSelect.js b/components/CreateViews/CreateMatchView/ChannelSelect.js new file mode 100644 index 0000000..4bca1b5 --- /dev/null +++ b/components/CreateViews/CreateMatchView/ChannelSelect.js @@ -0,0 +1,123 @@ +import { Select, useMantineTheme } from "@mantine/core"; + +export function ChannelSelect({ channels, type = "lg", ...props }) { + const theme = useMantineTheme(); + + const styles = { + sm: { + root: { + position: "relative", + }, + + input: { + fontWeight: 600, + color: + theme.colorScheme === "light" + ? theme.colors.indigo[7] + : theme.colors.dark[0], + height: 28, + minHeight: 28, + paddingLeft: 13, + border: `1px solid ${ + theme.colors.blueGray[theme.colorScheme === "dark" ? 7 : 2] + }`, + boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)", + // fontSize: "16px !important", + background: + theme.colorScheme === "dark" + ? theme.colors.dark[5] + : theme.fn.lighten(theme.colors.blueGray[0], 0.5), + "&:focus, &:focus-within": { + outline: "none", + borderColor: `${ + theme.colors[theme.primaryColor][ + theme.colorScheme === "dark" ? 8 : 5 + ] + } !important`, + }, + }, + + required: { + display: "none", + // ":before": { marginLeft: "auto", content: '" required"' }, + }, + + error: { + fontSize: 14, + }, + item: { + fontWeight: 600, + marginTop: 3, + }, + }, + lg: { + root: { + position: "relative", + }, + + input: { + fontWeight: 600, + color: + theme.colorScheme === "light" + ? theme.colors.blue[7] + : theme.colors.dark[0], + height: "auto", + paddingTop: 18, + paddingLeft: 13, + border: `1px solid ${ + theme.colors.blueGray[theme.colorScheme === "dark" ? 7 : 2] + }`, + boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)", + // fontSize: "16px !important", + background: + theme.colorScheme === "dark" + ? theme.colors.dark[5] + : theme.fn.lighten(theme.colors.blueGray[0], 0.5), + "&:focus, &:focus-within": { + outline: "none", + borderColor: `${ + theme.colors[theme.primaryColor][ + theme.colorScheme === "dark" ? 8 : 5 + ] + } !important`, + }, + }, + + required: { + display: "none", + // ":before": { marginLeft: "auto", content: '" required"' }, + }, + + error: { + fontSize: 14, + }, + + label: { + position: "absolute", + pointerEvents: "none", + color: theme.colors.blueGray[theme.colorScheme === "dark" ? 2 : 6], + fontSize: theme.fontSizes.xs, + paddingLeft: 14, + paddingTop: 6, + zIndex: 1, + }, + item: { + fontWeight: 600, + marginTop: 3, + }, + }, + }; + + return ( + } + variant="unstyled" + pt={4} + pb={6} + pl={12} + styles={{ + input: { height: 22, lineHeight: 0 }, + wrapper: { + background: + theme.colorScheme === "dark" ? theme.colors.gray[8] : "#fff", + }, + rightSection: { marginRight: 8 }, + }} + onKeyPress={(e) => { + if (e.key === "Enter") { + e.preventDefault(); + setSearchEntry(e.target.value); + setOpened(true); + } + }} + rightSectionWidth={100} + rightSection={{rightSection}} + /> + + + + + Results + + + + + + {data?.products?.map( + ({ title, image, price, productId, variantId }) => ( + { + form.addListItem("shopProducts", { + title, + image, + price, + quantity: 1, + productId, + variantId, + shop: { + id: shop.id, + name: shop.name, + }, + }); + setOpened(false); + }} + sx={{ cursor: "pointer" }} + > + + + + +
+ {title} + + {productId} | {variantId} + +
+
+
+
+ ) + )} +
+
+
+ {form.values.shopProducts.length > 0 && ( + + )} + {/* 0 && opened} + referenceElement={referenceElement} + transition="pop-top-left" + transitionDuration={200} + position="bottom" + // placement="bottom-start" + arrowStyle={{ + backgroundColor: + theme.colorScheme === "dark" + ? theme.colors.dark[5] + : theme.colors.gray[1], + }} + modifiers={[ + { + // @ts-ignore + name: "sameWidth", + enabled: true, + phase: "beforeWrite", + requires: ["computeStyles"], + fn: ({ state }) => { + // eslint-disable-next-line no-param-reassign + state.styles.popper.width = `${state.rects.reference.width}px`; + }, + effect: ({ state }) => { + // eslint-disable-next-line no-param-reassign + state.elements.popper.style.width = `${state.elements.reference.offsetWidth}px`; + }, + }, + {}, + ]} + > + + + {data?.products?.map( + ({ title, image, price, productId, variantId }) => ( + { + form.addListItem("lineItems", { + title, + image, + price, + quantity: 1, + productId, + variantId, + }); + setOpened(false); + }} + sx={{ cursor: "pointer" }} + > + + + + +
+ {title} + + {productId} | {variantId} + +
+
+
+
+ ) + )} +
+
+
*/} + + ); +} + +function LineItemField({ + image, + title, + productId, + variantId, + price, + quantity, + form, + index, +}) { + const handlers = useRef(); + const theme = useMantineTheme(); + + return ( + + + + + + + {title} + + {productId} | {variantId} + + + + + ${(price * quantity).toFixed(2)} + + {quantity > 1 && ( + + + (${price} x {quantity}) + + + )} + + + + handlers.current.decrement()} + // disabled={value === min} + // className={classes.control} + onMouseDown={(event) => event.preventDefault()} + sx={{ + // backgroundColor: + // theme.colorScheme === "dark" + // ? theme.colors.dark[7] + // : theme.white, + border: `1px solid ${ + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.gray[3] + }`, + + "&:disabled": { + borderColor: + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.gray[3], + opacity: 0.8, + backgroundColor: "transparent", + }, + }} + > + + + + + handlers.current.increment()} + // disabled={value === min} + // className={classes.control} + onMouseDown={(event) => event.preventDefault()} + sx={{ + // backgroundColor: + // theme.colorScheme === "dark" + // ? theme.colors.dark[7] + // : theme.white, + border: `1px solid ${ + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.gray[3] + }`, + + "&:disabled": { + borderColor: + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.gray[3], + opacity: 0.8, + backgroundColor: "transparent", + }, + }} + > + + + form.removeListItem("shopProducts", index)} + onMouseDown={(event) => event.preventDefault()} + sx={{ + border: `1px solid ${ + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.red[3] + }`, + + "&:disabled": { + borderColor: + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.gray[3], + opacity: 0.8, + backgroundColor: "transparent", + }, + }} + ml={6} + > + + + + + + + + + + ); +} diff --git a/components/CreateViews/CreateMatchView/ShopSelect.js b/components/CreateViews/CreateMatchView/ShopSelect.js new file mode 100644 index 0000000..3a08613 --- /dev/null +++ b/components/CreateViews/CreateMatchView/ShopSelect.js @@ -0,0 +1,123 @@ +import { Select, useMantineTheme } from "@mantine/core"; + +export function ShopSelect({ shops, type = "lg", ...props }) { + const theme = useMantineTheme(); + + const styles = { + sm: { + root: { + position: "relative", + }, + + input: { + fontWeight: 600, + color: + theme.colorScheme === "light" + ? theme.colors.cyan[7] + : theme.colors.dark[0], + height: 28, + minHeight: 28, + paddingLeft: 13, + border: `1px solid ${ + theme.colors.blueGray[theme.colorScheme === "dark" ? 7 : 2] + }`, + boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)", + // fontSize: "16px !important", + background: + theme.colorScheme === "dark" + ? theme.colors.dark[5] + : theme.fn.lighten(theme.colors.blueGray[0], 0.5), + "&:focus, &:focus-within": { + outline: "none", + borderColor: `${ + theme.colors[theme.primaryColor][ + theme.colorScheme === "dark" ? 8 : 5 + ] + } !important`, + }, + }, + + required: { + display: "none", + // ":before": { marginLeft: "auto", content: '" required"' }, + }, + + error: { + fontSize: 14, + }, + item: { + fontWeight: 600, + marginTop: 3, + }, + }, + lg: { + root: { + position: "relative", + }, + + input: { + fontWeight: 600, + color: + theme.colorScheme === "light" + ? theme.colors.cyan[7] + : theme.colors.dark[0], + height: "auto", + paddingTop: 18, + paddingLeft: 13, + border: `1px solid ${ + theme.colors.blueGray[theme.colorScheme === "dark" ? 7 : 2] + }`, + boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)", + // fontSize: "16px !important", + background: + theme.colorScheme === "dark" + ? theme.colors.dark[5] + : theme.fn.lighten(theme.colors.blueGray[0], 0.5), + "&:focus, &:focus-within": { + outline: "none", + borderColor: `${ + theme.colors[theme.primaryColor][ + theme.colorScheme === "dark" ? 8 : 5 + ] + } !important`, + }, + }, + + required: { + display: "none", + // ":before": { marginLeft: "auto", content: '" required"' }, + }, + + error: { + fontSize: 14, + }, + + label: { + position: "absolute", + pointerEvents: "none", + color: theme.colors.blueGray[theme.colorScheme === "dark" ? 2 : 6], + fontSize: theme.fontSizes.xs, + paddingLeft: 14, + paddingTop: 6, + zIndex: 1, + }, + item: { + fontWeight: 600, + marginTop: 3, + }, + }, + }; + + return ( + } + variant="unstyled" + pt={4} + pb={6} + pl={12} + styles={{ + input: { height: 22, lineHeight: 0 }, + wrapper: { + background: + theme.colorScheme === "dark" ? theme.colors.gray[8] : "#fff", + }, + rightSection: { marginRight: 8 }, + }} + onKeyPress={(e) => { + if (e.key === "Enter") { + e.preventDefault(); + setSearchEntry(e.target.value); + setOpened(true); + } + }} + rightSectionWidth={100} + rightSection={{rightSection}} + /> + + 0 && opened} + referenceElement={referenceElement} + transition="pop-top-left" + transitionDuration={200} + position="bottom" + // placement="bottom-start" + arrowStyle={{ + backgroundColor: + theme.colorScheme === "dark" + ? theme.colors.dark[5] + : theme.colors.gray[1], + }} + modifiers={[ + // { + // name: "preventOverflow", + // enabled: false, + // }, + // { + // name: "flip", + // enabled: dropdownPosition === "flip", + // }, + { + // @ts-ignore + name: "sameWidth", + enabled: true, + phase: "beforeWrite", + requires: ["computeStyles"], + fn: ({ state }) => { + // eslint-disable-next-line no-param-reassign + state.styles.popper.width = `${state.rects.reference.width}px`; + }, + effect: ({ state }) => { + // eslint-disable-next-line no-param-reassign + state.elements.popper.style.width = `${state.elements.reference.offsetWidth}px`; + }, + }, + // { + // // @ts-ignore + // name: "directionControl", + // enabled: true, + // phase: "main", + // fn: ({ state }) => { + // if (previousPlacement.current !== state.placement) { + // previousPlacement.current = state.placement; + + // const nextDirection = + // state.placement === "top" ? "column-reverse" : "column"; + + // if (direction !== nextDirection && switchDirectionOnFlip) { + // onDirectionChange && onDirectionChange(nextDirection); + // } + // } + // }, + // }, + {}, + ]} + > + + + {data?.products?.map( + ({ title, image, price, productId, variantId }) => ( + + form.addListItem("cartItems", { + title, + image, + price, + quantity: 1, + productId, + variantId, + channel, + }) + } + sx={{ cursor: "pointer" }} + > + + + + +
+ {title} + + {productId} | {variantId} + +
+
+
+
+ ) + )} +
+
+
+ + ); +} +function CartItemField({ + id, + image, + channel, + title, + productId, + variantId, + price, + quantity, + form, + index, +}) { + const handlers = useRef(); + const theme = useMantineTheme(); + + return ( + + + + + + + {channel.name} + + {title} + + {productId} | {variantId} + + + + + ${(price * quantity).toFixed(2)} + + {quantity > 1 && ( + + + (${price} x {quantity}) + + + )} + + + + handlers.current.decrement()} + // disabled={value === min} + // className={classes.control} + onMouseDown={(event) => event.preventDefault()} + sx={{ + // backgroundColor: + // theme.colorScheme === "dark" + // ? theme.colors.dark[7] + // : theme.white, + border: `1px solid ${ + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.gray[3] + }`, + + "&:disabled": { + borderColor: + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.gray[3], + opacity: 0.8, + backgroundColor: "transparent", + }, + }} + > + + + + + handlers.current.increment()} + // disabled={value === min} + // className={classes.control} + onMouseDown={(event) => event.preventDefault()} + sx={{ + // backgroundColor: + // theme.colorScheme === "dark" + // ? theme.colors.dark[7] + // : theme.white, + border: `1px solid ${ + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.gray[3] + }`, + + "&:disabled": { + borderColor: + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.gray[3], + opacity: 0.8, + backgroundColor: "transparent", + }, + }} + > + + + form.removeListItem("cartItems", index)} + onMouseDown={(event) => event.preventDefault()} + sx={{ + border: `1px solid ${ + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.red[3] + }`, + + "&:disabled": { + borderColor: + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.gray[3], + opacity: 0.8, + backgroundColor: "transparent", + }, + }} + ml={6} + > + + + + + + + + + + ); +} diff --git a/components/CreateViews/CreateOrderView/ChannelSelect.js b/components/CreateViews/CreateOrderView/ChannelSelect.js new file mode 100644 index 0000000..14ae596 --- /dev/null +++ b/components/CreateViews/CreateOrderView/ChannelSelect.js @@ -0,0 +1,123 @@ +import { Select, useMantineTheme } from "@mantine/core"; + +export function ChannelSelect({ channels, type = "lg", ...props }) { + const theme = useMantineTheme(); + + const styles = { + sm: { + root: { + position: "relative", + }, + + input: { + fontWeight: 600, + color: + theme.colorScheme === "light" + ? theme.colors.blue[7] + : theme.colors.dark[0], + height: 28, + minHeight: 28, + paddingLeft: 13, + border: `1px solid ${ + theme.colors.blueGray[theme.colorScheme === "dark" ? 7 : 2] + }`, + boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)", + // fontSize: "16px !important", + background: + theme.colorScheme === "dark" + ? theme.colors.dark[5] + : theme.fn.lighten(theme.colors.blueGray[0], 0.5), + "&:focus, &:focus-within": { + outline: "none", + borderColor: `${ + theme.colors[theme.primaryColor][ + theme.colorScheme === "dark" ? 8 : 5 + ] + } !important`, + }, + }, + + required: { + display: "none", + // ":before": { marginLeft: "auto", content: '" required"' }, + }, + + error: { + fontSize: 14, + }, + item: { + fontWeight: 600, + marginTop: 3, + }, + }, + lg: { + root: { + position: "relative", + }, + + input: { + fontWeight: 600, + color: + theme.colorScheme === "light" + ? theme.colors.blue[7] + : theme.colors.dark[0], + height: "auto", + paddingTop: 18, + paddingLeft: 13, + border: `1px solid ${ + theme.colors.blueGray[theme.colorScheme === "dark" ? 7 : 2] + }`, + boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)", + // fontSize: "16px !important", + background: + theme.colorScheme === "dark" + ? theme.colors.dark[5] + : theme.fn.lighten(theme.colors.blueGray[0], 0.5), + "&:focus, &:focus-within": { + outline: "none", + borderColor: `${ + theme.colors[theme.primaryColor][ + theme.colorScheme === "dark" ? 8 : 5 + ] + } !important`, + }, + }, + + required: { + display: "none", + // ":before": { marginLeft: "auto", content: '" required"' }, + }, + + error: { + fontSize: 14, + }, + + label: { + position: "absolute", + pointerEvents: "none", + color: theme.colors.blueGray[theme.colorScheme === "dark" ? 2 : 6], + fontSize: theme.fontSizes.xs, + paddingLeft: 14, + paddingTop: 6, + zIndex: 1, + }, + item: { + fontWeight: 600, + marginTop: 3, + }, + }, + }; + + return ( + } + variant="unstyled" + pt={4} + pb={6} + pl={12} + styles={{ + input: { height: 22, lineHeight: 0 }, + wrapper: { + background: + theme.colorScheme === "dark" ? theme.colors.gray[8] : "#fff", + }, + }} + onKeyPress={(e) => { + if (e.key === "Enter") { + e.preventDefault(); + setSearchEntry(e.target.value); + setOpened(true); + } + }} + /> + + 0 && opened} + referenceElement={referenceElement} + transition="pop-top-left" + transitionDuration={200} + position="bottom" + // placement="bottom-start" + arrowStyle={{ + backgroundColor: + theme.colorScheme === "dark" + ? theme.colors.dark[5] + : theme.colors.gray[1], + }} + modifiers={[ + { + // @ts-ignore + name: "sameWidth", + enabled: true, + phase: "beforeWrite", + requires: ["computeStyles"], + fn: ({ state }) => { + // eslint-disable-next-line no-param-reassign + state.styles.popper.width = `${state.rects.reference.width}px`; + }, + effect: ({ state }) => { + // eslint-disable-next-line no-param-reassign + state.elements.popper.style.width = `${state.elements.reference.offsetWidth}px`; + }, + }, + {}, + ]} + > + + + {data?.products?.map( + ({ title, image, price, productId, variantId }) => ( + { + form.addListItem("lineItems", { + title, + image, + price, + quantity: 1, + productId, + variantId, + }); + setOpened(false); + }} + sx={{ cursor: "pointer" }} + > + + + + +
+ {title} + + {productId} | {variantId} + +
+
+
+
+ ) + )} +
+
+
+ + ); +} +function LineItemField({ + image, + title, + productId, + variantId, + price, + quantity, + form, + index, +}) { + const handlers = useRef(); + const theme = useMantineTheme(); + + return ( + + + + + + + {title} + + {productId} | {variantId} + + + + + ${(price * quantity).toFixed(2)} + + {quantity > 1 && ( + + + (${price} x {quantity}) + + + )} + + + + handlers.current.decrement()} + // disabled={value === min} + // className={classes.control} + onMouseDown={(event) => event.preventDefault()} + sx={{ + // backgroundColor: + // theme.colorScheme === "dark" + // ? theme.colors.dark[7] + // : theme.white, + border: `1px solid ${ + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.gray[3] + }`, + + "&:disabled": { + borderColor: + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.gray[3], + opacity: 0.8, + backgroundColor: "transparent", + }, + }} + > + + + + + handlers.current.increment()} + // disabled={value === min} + // className={classes.control} + onMouseDown={(event) => event.preventDefault()} + sx={{ + // backgroundColor: + // theme.colorScheme === "dark" + // ? theme.colors.dark[7] + // : theme.white, + border: `1px solid ${ + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.gray[3] + }`, + + "&:disabled": { + borderColor: + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.gray[3], + opacity: 0.8, + backgroundColor: "transparent", + }, + }} + > + + + form.removeListItem("lineItems", index)} + onMouseDown={(event) => event.preventDefault()} + sx={{ + border: `1px solid ${ + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.red[3] + }`, + + "&:disabled": { + borderColor: + theme.colorScheme === "dark" + ? "transparent" + : theme.colors.gray[3], + opacity: 0.8, + backgroundColor: "transparent", + }, + }} + ml={6} + > + + + + + + + + + + ); +} diff --git a/components/CreateViews/CreateOrderView/ShopSelect.js b/components/CreateViews/CreateOrderView/ShopSelect.js new file mode 100644 index 0000000..3a08613 --- /dev/null +++ b/components/CreateViews/CreateOrderView/ShopSelect.js @@ -0,0 +1,123 @@ +import { Select, useMantineTheme } from "@mantine/core"; + +export function ShopSelect({ shops, type = "lg", ...props }) { + const theme = useMantineTheme(); + + const styles = { + sm: { + root: { + position: "relative", + }, + + input: { + fontWeight: 600, + color: + theme.colorScheme === "light" + ? theme.colors.cyan[7] + : theme.colors.dark[0], + height: 28, + minHeight: 28, + paddingLeft: 13, + border: `1px solid ${ + theme.colors.blueGray[theme.colorScheme === "dark" ? 7 : 2] + }`, + boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)", + // fontSize: "16px !important", + background: + theme.colorScheme === "dark" + ? theme.colors.dark[5] + : theme.fn.lighten(theme.colors.blueGray[0], 0.5), + "&:focus, &:focus-within": { + outline: "none", + borderColor: `${ + theme.colors[theme.primaryColor][ + theme.colorScheme === "dark" ? 8 : 5 + ] + } !important`, + }, + }, + + required: { + display: "none", + // ":before": { marginLeft: "auto", content: '" required"' }, + }, + + error: { + fontSize: 14, + }, + item: { + fontWeight: 600, + marginTop: 3, + }, + }, + lg: { + root: { + position: "relative", + }, + + input: { + fontWeight: 600, + color: + theme.colorScheme === "light" + ? theme.colors.cyan[7] + : theme.colors.dark[0], + height: "auto", + paddingTop: 18, + paddingLeft: 13, + border: `1px solid ${ + theme.colors.blueGray[theme.colorScheme === "dark" ? 7 : 2] + }`, + boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)", + // fontSize: "16px !important", + background: + theme.colorScheme === "dark" + ? theme.colors.dark[5] + : theme.fn.lighten(theme.colors.blueGray[0], 0.5), + "&:focus, &:focus-within": { + outline: "none", + borderColor: `${ + theme.colors[theme.primaryColor][ + theme.colorScheme === "dark" ? 8 : 5 + ] + } !important`, + }, + }, + + required: { + display: "none", + // ":before": { marginLeft: "auto", content: '" required"' }, + }, + + error: { + fontSize: 14, + }, + + label: { + position: "absolute", + pointerEvents: "none", + color: theme.colors.blueGray[theme.colorScheme === "dark" ? 2 : 6], + fontSize: theme.fontSizes.xs, + paddingLeft: 14, + paddingTop: 6, + zIndex: 1, + }, + item: { + fontWeight: 600, + marginTop: 3, + }, + }, + }; + + return ( + } + {...form.getInputProps("status")} + /> + {shopData?.shops.length > 0 && ( + { + form.setFieldValue("lineItems", formList([])); + form.getInputProps("shopId").onChange(event); + }} + error={form.getInputProps("shopId").error} + shops={shopData?.shops} + form={form} + mt="md" + size="md" + label="Shop" + /> + )} + + {shop && shop.searchProductsEndpoint ? ( + + ) : ( + + + + + + )} + {channel && channel.searchProductsEndpoint && ( + 0 && ( + { + form.getInputProps("channelId").onChange(event); + }} + error={form.getInputProps("channelId").error} + channels={channelData?.channels} + form={form} + type="sm" + size="xs" + rightSection={ + + } + /> + ) + } + /> + )} + + + + + + +
+ ); +}; diff --git a/components/CreateViews/CreateShopView/ShopForm.js b/components/CreateViews/CreateShopView/ShopForm.js new file mode 100644 index 0000000..3f46083 --- /dev/null +++ b/components/CreateViews/CreateShopView/ShopForm.js @@ -0,0 +1,78 @@ +import { useForm } from "@mantine/form"; +import { TextInput, Button, useMantineTheme, Box } from "@mantine/core"; + +export const ShopForm = ({ + fields, + handleSubmit, + buttonText, + renderFields, + loading, +}) => { + const theme = useMantineTheme(); + + const arrayToObject = (array, key) => + array.reduce( + (obj, item) => ({ + ...obj, + [item[key]]: "", + }), + {} + ); + + const form = useForm({ + initialValues: arrayToObject(fields, "name"), + }); + + return ( +
+ {fields.map(({ name, title, placeholder, rightSection }) => ( + + ))} + {renderFields} + + + + + ); +}; diff --git a/components/CreateViews/CreateShopView/index.js b/components/CreateViews/CreateShopView/index.js new file mode 100644 index 0000000..30fdee5 --- /dev/null +++ b/components/CreateViews/CreateShopView/index.js @@ -0,0 +1,223 @@ +import { useState } from "react"; +import { Text, useMantineTheme, Select, Modal } from "@mantine/core"; +import { mutate } from "swr"; +import { request } from "graphql-request"; +import { CREATE_SHOP_MUTATION, SHOPS_QUERY } from "@graphql/shops"; +import { useNotifications } from "@mantine/notifications"; +import { useRouter } from "next/router"; +import { ShopForm } from "./ShopForm"; + +//hardcoded endpoints fetched through state type +const endpoints = { + shopify: { + searchProductsEndpoint: "/api/search-products/shopify", + searchOrdersEndpoint: "/api/search-orders/shopify", + updateProductEndpoint: "/api/update-product/shopify", + getWebhooksEndpoint: "/api/get-webhooks/shopify", + createWebhookEndpoint: "/api/create-webhook/shopify", + deleteWebhookEndpoint: "/api/delete-webhook/shopify", + }, + demo: { + domain: "https://openship-shop.vercel.app", + accessToken: "supersecret", + searchProductsEndpoint: "https://openship-shop.vercel.app/api/search-products", + searchOrdersEndpoint: "https://openship-shop.vercel.app/api/search-orders", + }, +}; + +export const CreateShopView = ({ showModal, setShowModal }) => { + const notifications = useNotifications(); + const router = useRouter(); + const theme = useMantineTheme(); + const [type, setType] = useState("shopify"); + const [loading, setLoading] = useState(false); + // const { data: channelData, error: channelError } = useSWR(CHANNELS_QUERY, gqlFetcher); + + const createShop = async (values) => { + setLoading(true); + // setError(null); + + const res = await request("/api/graphql", CREATE_SHOP_MUTATION, { + data: { + ...values, + }, + }) + .then(async () => { + await mutate(SHOPS_QUERY); + + notifications.showNotification({ + title: `Shop has been added.`, + }); + setShowModal(false); + }) + .catch((error) => { + setLoading(false); + notifications.showNotification({ + title: error.response.errors[0].extensions.code, + message: error.response.errors[0].message, + color: "red", + }); + }); + }; + + const ShopForms = { + shopify: { + label: "Shopify", + fields: [ + { + title: "URL", + name: "shop", + placeholder: "centralbikeshop", + rightSection: ".myshopify.com", + }, + ], + handleSubmit: (values) => + router.push(`/api/o-auth/shop/shopify?shop=${values.shop}`), + buttonText: "Connect Shopify", + }, + shopifycustom: { + label: "Shopify Custom", + fields: [ + { title: "Name", name: "name", placeholder: "Central Bike Shop" }, + { title: "Domain", name: "domain", placeholder: "centralbikeshop.com" }, + { + title: "Access Token", + name: "accessToken", + placeholder: "supersecret", + }, + ], + handleSubmit: (values) => + createShop({ + type: "shopify", + ...values, + ...endpoints.shopify, + }), + }, + custom: { + label: "Custom", + fields: [ + { title: "Name", name: "name", placeholder: "Central Bike Shop" }, + { title: "Domain", name: "domain", placeholder: "centralbikeshop.com" }, + { + title: "Access Token", + name: "accessToken", + placeholder: "supersecret", + }, + ], + handleSubmit: (values) => + createShop({ + type: "custom", + ...values, + }), + }, + demo: { + label: "Demo", + fields: [ + { title: "Name", name: "name", placeholder: "Central Bike Shop" }, + ], + handleSubmit: (values) => + createShop({ + type: "demo", + ...values, + ...endpoints.demo, + }), + }, + }; + + return ( + setShowModal(false)} + // size="xl" + title={ + + Create Shop + + } + > + } + {...form.getInputProps("status")} + /> + {shopData?.shops.length > 0 && ( + { + form.setFieldValue("lineItems", formList([])); + form.getInputProps("shopId").onChange(event); + }} + error={form.getInputProps("shopId").error} + shops={shopData?.shops} + form={form} + mt="md" + size="md" + label="Shop" + /> + )} + + {shop?.searchProductsEndpoint ? ( + + ) : ( + + + + + + )} + {channel?.searchProductsEndpoint && ( + 0 && ( + { + form.getInputProps("channelId").onChange(event); + }} + error={form.getInputProps("channelId").error} + channels={channelData?.channels} + form={form} + type="sm" + size="xs" + rightSection={ + + } + /> + ) + } + /> + )} + + + + + + + + ); +} diff --git a/components/OSOrders/index.js b/components/OSOrders/index.js new file mode 100644 index 0000000..2f985a1 --- /dev/null +++ b/components/OSOrders/index.js @@ -0,0 +1,425 @@ +import React, { useState } from "react"; +import { Option } from "@primitives/option"; +import { + ArrowRightIcon, + PlusIcon, + SearchIcon, + DashIcon, + CheckIcon, +} from "@primer/octicons-react"; +import useSWR, { mutate } from "swr"; +import Link from "next/link"; +import { gqlFetcher } from "@lib/gqlFetcher"; +import { ORDER_COUNT_QUERY } from "@graphql/orders"; +import { ORDERS_QUERY } from "@graphql/orders"; +import { BULK_UPDATE_ORDERS } from "@graphql/orders"; +import request from "graphql-request"; +import { PLACE_ORDERS } from "@graphql/orders"; +import { + Box, + Button, + Divider, + Group, + Input, + Menu, + NumberInput, + Paper, + Skeleton, + ActionIcon, + Text, + useMantineTheme, + Container, +} from "@mantine/core"; +import { OsOrderList } from "./osOrderList"; +import { useSharedState } from "@lib/useSharedState"; + +export const OSOrders = ({ shops, status, defaultPerPage = 1 }) => { + const theme = useMantineTheme(); + const [selectedShop, setSelectedShop] = useState(); + const [searchEntry, setSearchEntry] = useState(""); + const [orderPerPage, setOrderPerPage] = useState(defaultPerPage); + const [skip, setSkip] = useState(0); + const [numberOfOrder, setNumberOfOrder] = useState({ + name: "All", + value: null, + }); + + const [showModal, setShowModal] = useSharedState("createModal"); + + const orderPPList = [ + ...(status === "PENDING" ? [1] : []), + 25, + 50, + 100, + 200, + 1000, + ]; + + // const { isOpen, onToggle } = useDisclosure(); + + const filteredShop = + shops?.length > 0 && shops.filter(({ id }) => id === selectedShop)[0]; + + const { + data, + error, + mutate: mutateOrders, + } = useSWR( + [ + ORDERS_QUERY, + JSON.stringify({ + where: { + OR: [ + { orderName: { contains: searchEntry, mode: "insensitive" } }, + { first_name: { contains: searchEntry, mode: "insensitive" } }, + { last_name: { contains: searchEntry, mode: "insensitive" } }, + { streetAddress1: { contains: searchEntry, mode: "insensitive" } }, + { streetAddress2: { contains: searchEntry, mode: "insensitive" } }, + { city: { contains: searchEntry, mode: "insensitive" } }, + { state: { contains: searchEntry, mode: "insensitive" } }, + { zip: { contains: searchEntry, mode: "insensitive" } }, + ], + shop: { id: { equals: filteredShop?.id } }, + status: { equals: status }, + }, + skip: parseInt(skip), + take: parseInt(orderPerPage), + }), + ], + gqlFetcher + ); + + const { data: orderCount, mutate: orderCountMutate } = useSWR( + [ + ORDER_COUNT_QUERY, + JSON.stringify({ + where: { + orderName: { contains: searchEntry }, + shop: { id: { equals: filteredShop?.id } }, + status: { equals: status }, + }, + }), + ], + gqlFetcher + ); + + const ordersWithCart = data?.orders?.filter( + ({ cartItems, orderError }) => + !orderError && + cartItems?.filter( + (cItem) => cItem.status !== "CANCELLED" && !cItem.purchaseId + ).length > 0 + ); + + const count = orderCount?.ordersCount; + + const pageCount = Math.ceil(count / orderPerPage); + + const processOrders = async () => { + const updateStatusToINPROCESS = await request( + "/api/graphql", + BULK_UPDATE_ORDERS, + { + data: ordersWithCart.map(({ id }) => ({ + id, + data: { status: { equals: "INPROCESS" } }, + })), + } + ) + .then(async () => { + await mutateOrders(); + await orderCountMutate(); + await mutate([ + ORDER_COUNT_QUERY, + JSON.stringify({ where: { status: { equals: "PENDING" } } }), + ]); + await mutate([ + ORDER_COUNT_QUERY, + JSON.stringify({ where: { status: { equals: "INPROCESS" } } }), + ]); + }) + .catch((error) => { + notifications.showNotification({ + title: error.response.errors[0].extensions.code, + message: error.response.errors[0].message, + color: "red", + }); + }); + + const placeOrders = await request("/api/graphql", PLACE_ORDERS, { + ids: ordersWithCart.map(({ id }) => id), + }) + .then(async () => { + await mutate([ + ORDER_COUNT_QUERY, + JSON.stringify({ where: { status: { equals: "INPROCESS" } } }), + ]); + await mutate([ + ORDER_COUNT_QUERY, + JSON.stringify({ where: { status: { equals: "AWAITING" } } }), + ]); + }) + .catch((error) => { + notifications.showNotification({ + title: error.response.errors[0].extensions.code, + message: error.response.errors[0].message, + color: "red", + }); + }); + }; + + const ShopSelect = ({ shops }) => { + if (!shops) + return ( + + + Shops + + + + + + + + ); + if (shops.length === 0) + return ( + + + + ); + + return ( +