diff --git a/docs/content/guides/carts-in-medusa.md b/docs/content/guides/carts-in-medusa.md index 7d7e673954..c742989ec9 100644 --- a/docs/content/guides/carts-in-medusa.md +++ b/docs/content/guides/carts-in-medusa.md @@ -1,8 +1,8 @@ --- -title: Carts in Medusa +title: Carts --- -# Carts in Medusa +# Carts In Medusa a Cart serves the purpose of collecting the information needed to create an Order, including what products to purchase, what address to send the products to and which payment method the purchase will be processed by. diff --git a/docs/content/how-to/plugins.md b/docs/content/guides/plugins.md similarity index 99% rename from docs/content/how-to/plugins.md rename to docs/content/guides/plugins.md index 8d8c837123..db5b6435f5 100644 --- a/docs/content/how-to/plugins.md +++ b/docs/content/guides/plugins.md @@ -1,5 +1,5 @@ --- -title: Plugins in Medusa +title: Plugins --- # Plugins diff --git a/docs/content/homepage.mdx b/docs/content/homepage.mdx new file mode 100644 index 0000000000..f5162ce65c --- /dev/null +++ b/docs/content/homepage.mdx @@ -0,0 +1,89 @@ +--- +id: homepage +title: Introduction +description: 'What is Medusa?' +slug: / +hide_table_of_contents: true +--- + +import useBaseUrl from '@docusaurus/useBaseUrl' +import Link from '@docusaurus/Link' +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' + +Medusa is an open-source Shopify alternative. + +It provides you with the primitives to create amazing digital commerce experiences. + +
+
+
+ +
+

Tutorial

+

Set up your local development environment

+
+ +
+
+ +
+

Make it your own

+

Create custom endpoints, services, or subscribers.

+
+ +
+
+ +
+

Plugins

+

Add or build a plugin to make your engine more powerful.

+
+ +
+
+ +
+

Deploy in seconds

+

Use one of our guides to deploy your Medusa project in seconds.

+
+ +
+
+
+ +## Quickstart + +Visit our [Quickstart](https://github.com/medusajs/medusa#-quickstart) to get up and running in minutes with only a couple of commands. + +## What you'll find here + +
+
+
+ +
+

Quickstart

+

A short guide to get you quickly started.

+
+ +
+
+ +
+

How-to and guides

+

Guides and walkthroughs of concepts, tools, deployment and APIs.

+
+ +
+ {/* Ref */} + +
+
diff --git a/docs/content/tutorial/2-adding-custom-functionality.md b/docs/content/tutorial/2-adding-custom-functionality.md index e04d84303a..5aac5b1c51 100644 --- a/docs/content/tutorial/2-adding-custom-functionality.md +++ b/docs/content/tutorial/2-adding-custom-functionality.md @@ -19,11 +19,11 @@ The custom functionality will do a number of things: We will begin our custom implementation by adding a custom service. In you project create a new file at `/src/services/welcome.js`. Open the newly created file and add a class: ```javascript -import { BaseService } from "medusa-interfaces"; +import { BaseService } from "medusa-interfaces" class WelcomeService extends BaseService { constructor({}) { - super(); + super() } async registerOptin(cartId, optin) {} @@ -31,7 +31,7 @@ class WelcomeService extends BaseService { async sendWelcome(orderId) {} } -export default WelcomeService; +export default WelcomeService ``` We will be filling out each of the methods in turn, but before we get to that it should be noted that placing files in `/src/services` has a special meaning in Medusa projects. When Medusa starts up it will look for files in this folder and register exports from these files to the global container. The global container holds all services and repositories in your Medusa project allowing for dependency injection. Dependency injection is a software development technique in which objects only receive other objects that it depends upon. @@ -126,18 +126,18 @@ Similarly to the `/src/services` directory, the `/src/api` directory has a speci Create a new file at `/src/api/index.js` and add the following controller: ```javascript -import { Router } from "express"; -import bodyParser from "body-parser"; +import { Router } from "express" +import bodyParser from "body-parser" export default () => { - const app = Router(); + const app = Router() app.post("/welcome/:cart_id", bodyParser.json(), async (req, res) => { // TODO - }); + }) - return app; -}; + return app +} ``` ### Controller implementation @@ -146,33 +146,33 @@ Our endpoint controller's implementation will be very simple. It will extract th ```javascript app.post("/welcome/:cart_id", bodyParser.json(), async (req, res) => { - const { cart_id } = req.params; - const { optin } = req.body; + const { cart_id } = req.params + const { optin } = req.body // Validate that the optin value was provided. // If not respond with a Bad Request status if (typeof optin !== "boolean") { res.status(400).json({ message: "You must provide an boolean optin value in the request body", - }); - return; + }) + return } - const welcomeService = req.scope.resolve("welcomeService"); + const welcomeService = req.scope.resolve("welcomeService") try { - await welcomeService.registerOptin(cart_id, optin); + await welcomeService.registerOptin(cart_id, optin) res.status(200).json({ success: true, - }); + }) } catch (err) { // This is not supposed to happen. res.status(500).json({ message: "Something unexpected happened.", - }); + }) } -}); +}) ``` In the implementation above we are first validating that the request body is structured correctly so that we can proceed with our opt-in registration. If the validation fails we respond with 400 Bad Request which is an HTTP code that indicates that the client that sent the request has not provided the correct values. @@ -223,17 +223,17 @@ The final thing that we will add in this part of the tutorial is the subscriber ```javascript class WelcomeSubscriber { constructor({ welcomeService, eventBusService }) { - this.welcomeService_ = welcomeService; + this.welcomeService_ = welcomeService - eventBusService.subscribe("order.placed", this.handleWelcome); + eventBusService.subscribe("order.placed", this.handleWelcome) } handleWelcome = async (data) => { - return await this.welcomeService_.sendWelcome(data.id); - }; + return await this.welcomeService_.sendWelcome(data.id) + } } -export default WelcomeSubscriber; +export default WelcomeSubscriber ``` The implementation above is all that is needed to automate the `sendWelcome` function to be called every time a new order is created. The subscriber class here delegates all of the business logic to the `sendWelcome` function, where we are checking for opt-in and first time buyers. diff --git a/integration-tests/api/__tests__/admin/customer.js b/integration-tests/api/__tests__/admin/customer.js index 36f5b0a168..8661c1780c 100644 --- a/integration-tests/api/__tests__/admin/customer.js +++ b/integration-tests/api/__tests__/admin/customer.js @@ -132,6 +132,55 @@ describe("/admin/customers", () => { }) }) + describe("POST /admin/customers", () => { + beforeEach(async () => { + try { + await adminSeeder(dbConnection) + } catch (err) { + console.log(err) + throw err + } + }) + + afterEach(async () => { + const db = useDb() + await db.teardown() + }) + + it("Correctly creates customer", async () => { + const api = useApi() + const response = await api + .post( + "/admin/customers", + { + first_name: "newf", + last_name: "newl", + email: "new@email.com", + password: "newpassword", + metadata: { foo: "bar" }, + }, + { + headers: { + Authorization: "Bearer test_token", + }, + } + ) + .catch((err) => { + console.log(err) + }) + + expect(response.status).toEqual(201) + expect(response.data.customer).toEqual( + expect.objectContaining({ + first_name: "newf", + last_name: "newl", + email: "new@email.com", + metadata: { foo: "bar" }, + }) + ) + }) + }) + describe("POST /admin/customers/:id", () => { beforeEach(async () => { try { diff --git a/packages/medusa/src/api/routes/admin/customers/create-customer.ts b/packages/medusa/src/api/routes/admin/customers/create-customer.ts index 34ee33283c..a54476b38e 100644 --- a/packages/medusa/src/api/routes/admin/customers/create-customer.ts +++ b/packages/medusa/src/api/routes/admin/customers/create-customer.ts @@ -27,7 +27,7 @@ import { validator } from "../../../../utils/validator" * $ref: "#/components/schemas/customer" */ export default async (req, res) => { - const validated = await validator(AdminPostCustomersReq, req.bodyn) + const validated = await validator(AdminPostCustomersReq, req.body) const customerService: CustomerService = req.scope.resolve("customerService") const customer = await customerService.create(validated) diff --git a/www/docs/docusaurus.config.js b/www/docs/docusaurus.config.js index f7c074c4a9..69e4e1abc6 100644 --- a/www/docs/docusaurus.config.js +++ b/www/docs/docusaurus.config.js @@ -1,6 +1,3 @@ -const lightCodeTheme = require("prism-react-renderer/themes/github") -const darkCodeTheme = require("prism-react-renderer/themes/dracula") - const path = require("path") const docsPath = path.join(__dirname, "../../docs/content") @@ -35,6 +32,12 @@ module.exports = { placeholder: "Search docs...", appId: algoliaAppId, }, + prism: { + defaultLanguage: "js", + plugins: ["line-numbers", "show-language"], + theme: require("@kiwicopple/prism-react-renderer/themes/vsDark"), + darkTheme: require("@kiwicopple/prism-react-renderer/themes/vsDark"), + }, navbar: { hideOnScroll: true, logo: { @@ -44,30 +47,31 @@ module.exports = { }, items: [ { - type: "search", - position: "left", + href: "https://docs.medusa-commerce.com", + label: "Overview", }, { type: "doc", docId: "tutorial/set-up-your-development-environment", - position: "right", label: "Tutorial", }, { href: `https://docs.medusa-commerce.com/api/store`, target: "_self", - position: "right", - label: "API Reference", + label: "Reference", }, { + href: "https://github.com/medusajs/medusa", className: "navbar-github-link", - href: "https://github.com/medusajs/medusa/", + position: "right", + }, + { + type: "search", position: "right", }, ], }, footer: { - style: "dark", links: [ { title: "Docs", @@ -115,9 +119,6 @@ module.exports = { ], copyright: `© ${new Date().getFullYear()} Medusa Commerce`, }, - prism: { - theme: darkCodeTheme, - }, }, presets: [ [ diff --git a/www/docs/package.json b/www/docs/package.json index bc7378156a..63e220a332 100644 --- a/www/docs/package.json +++ b/www/docs/package.json @@ -17,6 +17,7 @@ "@docusaurus/core": "2.0.0-beta.3", "@docusaurus/preset-classic": "2.0.0-beta.3", "@docusaurus/theme-search-algolia": "^2.0.0-beta.3", + "@kiwicopple/prism-react-renderer": "github:kiwicopple/prism-react-renderer", "@mdx-js/react": "^1.6.21", "@svgr/webpack": "^5.5.0", "clsx": "^1.1.1", @@ -43,4 +44,4 @@ "devDependencies": { "prettier": "^2.3.2" } -} +} \ No newline at end of file diff --git a/www/docs/sidebars.js b/www/docs/sidebars.js index c499b84373..e1793cb50b 100644 --- a/www/docs/sidebars.js +++ b/www/docs/sidebars.js @@ -11,25 +11,20 @@ module.exports = { tutorialSidebar: [ + { + type: "doc", + id: "homepage", + label: "Overview", + }, { type: "doc", id: "quickstart/quick-start", label: "Quickstart", }, - // { - // type: 'category', - // label: 'Quickstart', - // items: [ - // { - // type: 'doc', - // id: 'quickstart/quick-start-docker', - // label: 'Quickstart w. Docker (Coming soon!)', - // }, - // ], - // }, { type: "category", label: "Tutorials", + collapsed: false, items: [ { type: "doc", @@ -43,20 +38,12 @@ module.exports = { type: "doc", id: "tutorial/adding-custom-functionality", }, - // { - // type: "doc", - // id: "tutorial/linking-your-local-project-with-medusa-cloud", - // }, ], }, { type: "category", label: "How to", items: [ - { - type: "doc", - id: "how-to/plugins", - }, { type: "doc", id: "how-to/notification-api", @@ -101,6 +88,10 @@ module.exports = { type: "doc", id: "guides/fulfillment-api", }, + { + type: "doc", + id: "guides/plugins", + }, { type: "doc", id: "guides/checkouts", diff --git a/www/docs/src/css/custom.css b/www/docs/src/css/custom.css index 967b86c5cd..2ee18a2cc4 100644 --- a/www/docs/src/css/custom.css +++ b/www/docs/src/css/custom.css @@ -20,14 +20,14 @@ --ifm-medusa-gray: #f0f0f0; /* Fonts */ - --ifm-code-font-size: 90%; - --ifm-font-family-base: "Open Sans"; + --ifm-code-font-size: 80%; + --ifm-font-family-base: "custom-font",BlinkMacSystemFont,-apple-system,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue","Helvetica","Arial",sans-serif; /* Sidebar */ --doc-sidebar-width: 350px !important; /* Docs Page */ - --ifm-docs-page-max-width: 700px; + --ifm-docs-page-max-width: 850px; /* Toc */ --ifm-toc-border-color: #f0f0f0; @@ -48,6 +48,18 @@ html[data-theme="dark"] { --ifm-toc-border-color: #333; } +h1 { + font-size: 2.5em; +} + +h1, h2, h3 { + font-weight: normal; +} + +p { + font-weight: 450; +} + /* DocSearch */ html[data-theme="light"] .DocSearch-Button { @@ -73,6 +85,16 @@ html[data-theme="dark"] .docusaurus-highlight-code-line { background-color: rgba(0, 0, 0, 0.3); } +.navbar { + z-index: 1000; + font-size: 14px; +} + +/* Medusa logo */ +.navbar__brand { + width: 100px; +} + .navbar-github-link:before { content: ""; width: 24px; @@ -90,8 +112,14 @@ html[data-theme="dark"] .docusaurus-highlight-code-line { article { max-width: var(--ifm-docs-page-max-width); - margin-left: auto; - margin-right: auto; +} + +@media screen and (min-width: 966px) { + article { + margin-left: 50px; + margin-right: 50px; + max-width: none + } } .docusaurus-highlight-code-line { @@ -129,8 +157,14 @@ article { } .sidebar-bg { - background: var(--ifm-medusa-gray); height: 100%; + font-size: 14px; + font-weight: 400; +} + +/* Medusa logo */ +.sidebar-bg img { + width: 100px; } .padding-top--md { @@ -138,8 +172,16 @@ article { } a.menu__link.menu__link--active.active { - border-left: 4px solid var(--ifm-color-primary); - padding-left: 12px; + padding-left: 16px; +} + +a.menu__link--sublist::after { + background: var(--ifm-menu-link-sublist-icon) 50% / 1rem 2rem; +} + +.menu__list-item > a { + font-weight: 450; + color: #1f1f1f; } /* Pagination */ @@ -181,10 +223,35 @@ footer .footer__title { width: 150px; } -.footer--dark { - --ifm-footer-background-color: #333; +footer { + background-color: #ffffff !important; + border-top: 1px solid var(--ifm-toc-border-color); } .react-toggle { display: none; } + +/* Cards */ + +.card { + border: 1px solid #1f1f1f; + border-radius: 8px; +} + +.col { + margin-top: 10px !important; + margin-bottom: 10px !important; +} + +.prism-code { + background-color: none; + word-break: break-word; + font-size: 0.8rem; + outline: none !important; +} +.prism-code div, +.prism-code div:focus, +.prism-code div:active { + outline: none !important; +} diff --git a/www/docs/src/pages/index.js b/www/docs/src/pages/index.js deleted file mode 100644 index 4bda6136f8..0000000000 --- a/www/docs/src/pages/index.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from "react" -import { Banner } from "../components/Banner/" -import { Intro } from "../components/Intro/" -import { Layout } from "../components/Layout/" -import styles from "./index.module.css" - -export default function Home() { - return ( - -
- - - {/* */} -
-
- ) -} diff --git a/www/docs/src/theme/SearchBar/index.js b/www/docs/src/theme/SearchBar/index.js index 67cca9ece3..d89f2aa655 100644 --- a/www/docs/src/theme/SearchBar/index.js +++ b/www/docs/src/theme/SearchBar/index.js @@ -5,17 +5,17 @@ * LICENSE file in the root directory of this source tree. */ -import React, { useState, useRef, useCallback, useMemo } from "react" -import { createPortal } from "react-dom" -import useDocusaurusContext from "@docusaurus/useDocusaurusContext" -import { useHistory } from "@docusaurus/router" -import { useBaseUrlUtils } from "@docusaurus/useBaseUrl" -import Link from "@docusaurus/Link" -import Head from "@docusaurus/Head" -import useSearchQuery from "@theme/hooks/useSearchQuery" import { DocSearchButton, useDocSearchKeyboardEvents } from "@docsearch/react" -import useAlgoliaContextualFacetFilters from "@theme/hooks/useAlgoliaContextualFacetFilters" +import Head from "@docusaurus/Head" +import Link from "@docusaurus/Link" +import { useHistory } from "@docusaurus/router" import { translate } from "@docusaurus/Translate" +import { useBaseUrlUtils } from "@docusaurus/useBaseUrl" +import useDocusaurusContext from "@docusaurus/useDocusaurusContext" +import useAlgoliaContextualFacetFilters from "@theme/hooks/useAlgoliaContextualFacetFilters" +import useSearchQuery from "@theme/hooks/useSearchQuery" +import React, { useCallback, useMemo, useRef, useState } from "react" +import { createPortal } from "react-dom" import styles from "./styles.module.css" let DocSearchModal = null @@ -46,7 +46,7 @@ const replaceUrl = (item) => { function Hit({ hit, children }) { if (hit.url.includes("/api/store") || hit.url.includes("/api/admin")) { - let url = replaceUrl(hit) + const url = replaceUrl(hit) return {children} } @@ -131,13 +131,13 @@ function DocSearch({ contextualSearch, ...props }) { const navigator = useRef({ navigate({ item }) { - let url = replaceUrl(item) - //Need to type out the entire URL to prevent it from attempting to open the page - //as part of the docusaurus project. Which will fail. + const url = replaceUrl(item) + // Need to type out the entire URL to prevent it from attempting to open the page + // as part of the docusaurus project. Which will fail. window.location = `https://docs.medusa-commerce.com${url}` }, navigateNewTab({ item }) { - let url = replaceUrl(item) + const url = replaceUrl(item) const windowReference = window.open(url, "_blank", "noopener") if (windowReference) { diff --git a/www/docs/src/theme/SearchBar/styles.css b/www/docs/src/theme/SearchBar/styles.css index 7af75450a2..27a850a4ce 100644 --- a/www/docs/src/theme/SearchBar/styles.css +++ b/www/docs/src/theme/SearchBar/styles.css @@ -15,6 +15,9 @@ transition: all var(--ifm-transition-fast) var(--ifm-transition-timing-default); width: 100%; + max-width: 175px; + border-radius: 8px; + background-color: #f5f6f7; } .DocSearch-Container { diff --git a/www/docs/src/theme/SearchBar/styles.module.css b/www/docs/src/theme/SearchBar/styles.module.css index 8cb3edaf09..ab42acf69b 100644 --- a/www/docs/src/theme/SearchBar/styles.module.css +++ b/www/docs/src/theme/SearchBar/styles.module.css @@ -17,8 +17,6 @@ padding: var(--ifm-navbar-item-padding-vertical) var(--ifm-navbar-item-padding-horizontal); flex: 1; - margin-left: 200px; - padding-right: 100px; } .searchBox button { diff --git a/www/docs/static/img/logo.png b/www/docs/static/img/logo.png index 44aeece132..f380a6c7f5 100644 Binary files a/www/docs/static/img/logo.png and b/www/docs/static/img/logo.png differ diff --git a/www/docs/static/img/logo.svg b/www/docs/static/img/logo.svg index 82e00acdb7..e9d0f26fc5 100644 --- a/www/docs/static/img/logo.svg +++ b/www/docs/static/img/logo.svg @@ -1,14 +1,10 @@ - - medusa-logo-one-colour-rgb - - - - - - - - - - - + + + + + + + + + diff --git a/www/docs/yarn.lock b/www/docs/yarn.lock index 04efa2dd5c..8c6fc1cfcb 100644 --- a/www/docs/yarn.lock +++ b/www/docs/yarn.lock @@ -1519,6 +1519,12 @@ dependencies: "@hapi/hoek" "^9.0.0" +"@kiwicopple/prism-react-renderer@github:kiwicopple/prism-react-renderer": + version "1.0.2" + resolved "https://codeload.github.com/kiwicopple/prism-react-renderer/tar.gz/4a09100a587bce2d94d7ac8ed3564a61c6e70781" + dependencies: + prismjs "^1.22.0" + "@mdx-js/mdx@^1.6.21": version "1.6.22" resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba" @@ -6609,6 +6615,11 @@ prism-react-renderer@^1.2.1: resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.2.1.tgz#392460acf63540960e5e3caa699d851264e99b89" integrity sha512-w23ch4f75V1Tnz8DajsYKvY5lF7H1+WvzvLUcF0paFxkTHSp42RS0H5CttdN2Q8RR3DRGZ9v5xD/h3n8C8kGmg== +prismjs@^1.22.0: + version "1.25.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.25.0.tgz#6f822df1bdad965734b310b315a23315cf999756" + integrity sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg== + prismjs@^1.23.0: version "1.24.1" resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.24.1.tgz#c4d7895c4d6500289482fa8936d9cdd192684036"