From d2cf72067df4028eaea9eaf7d76d2ceed5737358 Mon Sep 17 00:00:00 2001 From: ilia makarov Date: Sat, 18 Nov 2023 17:49:33 +0200 Subject: [PATCH 1/2] Rewrote ViewCustomer page to use PageBuilder --- manifest.json | 4 +- src/components/PageBuilder/GenerateBlock.tsx | 55 +++++++++++++ src/components/PageBuilder/PageBuilder.tsx | 47 +++++++++++ .../PageBuilder/blocks/Tags/Tags.tsx | 52 ++++++++++++ .../PageBuilder/blocks/Tags/index.ts | 1 + .../PageBuilder/blocks/Text/Text.tsx | 51 ++++++++++++ .../PageBuilder/blocks/Text/index.ts | 1 + .../PageBuilder/blocks/Title/Title.tsx | 28 +++++++ .../PageBuilder/blocks/Title/index.ts | 1 + .../PageBuilder/blocks/Toggle/Toggle.tsx | 20 +++++ .../PageBuilder/blocks/Toggle/index.ts | 1 + src/components/PageBuilder/blocks/index.ts | 13 +++ src/components/PageBuilder/index.ts | 2 + src/components/PageBuilder/types.ts | 30 +++++++ src/components/ViewCustomer/ViewCustomer.tsx | 64 --------------- src/components/ViewCustomer/index.ts | 1 - .../ViewCustomerPage/ViewCustomerPage.tsx | 79 ++++++++++++++----- 17 files changed, 364 insertions(+), 86 deletions(-) create mode 100644 src/components/PageBuilder/GenerateBlock.tsx create mode 100644 src/components/PageBuilder/PageBuilder.tsx create mode 100644 src/components/PageBuilder/blocks/Tags/Tags.tsx create mode 100644 src/components/PageBuilder/blocks/Tags/index.ts create mode 100644 src/components/PageBuilder/blocks/Text/Text.tsx create mode 100644 src/components/PageBuilder/blocks/Text/index.ts create mode 100644 src/components/PageBuilder/blocks/Title/Title.tsx create mode 100644 src/components/PageBuilder/blocks/Title/index.ts create mode 100644 src/components/PageBuilder/blocks/Toggle/Toggle.tsx create mode 100644 src/components/PageBuilder/blocks/Toggle/index.ts create mode 100644 src/components/PageBuilder/blocks/index.ts create mode 100644 src/components/PageBuilder/index.ts create mode 100644 src/components/PageBuilder/types.ts delete mode 100644 src/components/ViewCustomer/ViewCustomer.tsx delete mode 100644 src/components/ViewCustomer/index.ts diff --git a/manifest.json b/manifest.json index 1c0e3a7..146889a 100644 --- a/manifest.json +++ b/manifest.json @@ -38,12 +38,12 @@ "proxy": { "whitelist": [ { - "url": "https://(.*).myshopify.com/admin/api/2022-04/.*", + "url": "https://(.*).myshopify.com/admin/api/.*", "methods": ["GET", "POST", "PUT", "DELETE"], "timeout": 30 }, { - "url": "https://(.*).myshopify.com/admin/api/2022-04/graphql.json", + "url": "https://(.*).myshopify.com/admin/api/.*", "methods": ["POST"], "timeout": 30 } diff --git a/src/components/PageBuilder/GenerateBlock.tsx b/src/components/PageBuilder/GenerateBlock.tsx new file mode 100644 index 0000000..883a19f --- /dev/null +++ b/src/components/PageBuilder/GenerateBlock.tsx @@ -0,0 +1,55 @@ +import get from "lodash/get"; +import styled from "styled-components"; +import { Property } from "@deskpro/app-sdk"; +import type { FC, ComponentType } from "react"; +import type { Dict } from "../../types"; +import type { BlockSet } from "./types"; + +type Props = { + blockName: string, + blockConfig: BlockSet, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + Component: ComponentType<{ value: any } & Dict>, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + store?: Dict, +}; + +const PropertyWrapper = styled.div` + margin-left: 8px; + margin-right: 8px; +`; + +const GenerateBlock: FC = ({ blockConfig, Component, store }) => { + const blockType = get(blockConfig, ["type"]); + const { value, ...blockProps } = get(blockConfig, ["props"]) || {}; + const label = get(blockConfig, ["label"]); + const pathInStore = get(blockConfig, ["pathInStore"], ""); + + if (!blockConfig) { + // eslint-disable-next-line no-console + console.error("PageBuilder: wrong config - block config not found"); + return null; + } + + if (!Component) { + // eslint-disable-next-line no-console + console.error("PageBuilder: can't find component for block type", blockType); + return null; + } + + return ( + + + )} + /> + + ); +}; + +export { GenerateBlock }; diff --git a/src/components/PageBuilder/PageBuilder.tsx b/src/components/PageBuilder/PageBuilder.tsx new file mode 100644 index 0000000..8d5737c --- /dev/null +++ b/src/components/PageBuilder/PageBuilder.tsx @@ -0,0 +1,47 @@ +import get from "lodash/get"; +import isEmpty from "lodash/isEmpty"; +import { GenerateBlock } from "./GenerateBlock"; +import { defaultBlocksMap } from "./blocks"; +import type { FC } from "react"; +import type { PageBuilderProps } from "./types"; + +const PageBuilder: FC = ({ + store, + config: { structure, blocks }, + blocksMap, +}) => { + if (!Array.isArray(structure) ||isEmpty(structure)) { + // eslint-disable-next-line no-console + console.error("PageBuilder: wrong config - empty structure"); + return null; + } + + if (isEmpty(blocks)) { + // eslint-disable-next-line no-console + console.error("PageBuilder: wrong config - empty block"); + return null; + } + + return ( + <> + {structure.map((blockName) => { + const name = blockName[0]; + const blockConfig = get(blocks, name); + const blockType = get(blockConfig, ["type"]); + const Component = get(blocksMap || defaultBlocksMap, [blockType]); + + return ( + + ); + })} + + ); +}; + +export { PageBuilder }; diff --git a/src/components/PageBuilder/blocks/Tags/Tags.tsx b/src/components/PageBuilder/blocks/Tags/Tags.tsx new file mode 100644 index 0000000..a5c3e1e --- /dev/null +++ b/src/components/PageBuilder/blocks/Tags/Tags.tsx @@ -0,0 +1,52 @@ +import { match } from "ts-pattern"; +import size from "lodash/size"; +import { faTimes } from "@fortawesome/free-solid-svg-icons"; +import { Stack, Tag, lightTheme } from "@deskpro/deskpro-ui"; +import type { FC } from "react"; +import type { AnyIcon, DeskproTheme } from "@deskpro/deskpro-ui"; + +type Props = { + value: string[]; +}; + +const getTagColorSchema = ({ colors }: DeskproTheme, tag: string) => { + return match(tag.trim().toLowerCase()) + .with('vip', () => ({ + borderColor: colors.orange100, + backgroundColor: `${colors.orange100}33`, + textColor: colors.orange100, + })) + .with('development', () => ({ + borderColor: colors.turquoise100, + backgroundColor: `${colors.turquoise100}33`, + textColor: colors.turquoise100, + })) + .otherwise(() => ({ + borderColor: colors.grey80, + backgroundColor: `${colors.grey80}33`, + textColor: colors.grey80, + })); +}; + +const Tags: FC = ({ value }) => { + if (!Array.isArray(value) || !size(value)) { + return ( + <>- + ); + } + + return ( + + {value.map((tag) => ( + + ))} + + ); +}; + +export { Tags }; diff --git a/src/components/PageBuilder/blocks/Tags/index.ts b/src/components/PageBuilder/blocks/Tags/index.ts new file mode 100644 index 0000000..62fc186 --- /dev/null +++ b/src/components/PageBuilder/blocks/Tags/index.ts @@ -0,0 +1 @@ +export { Tags } from "./Tags"; diff --git a/src/components/PageBuilder/blocks/Text/Text.tsx b/src/components/PageBuilder/blocks/Text/Text.tsx new file mode 100644 index 0000000..4eae15b --- /dev/null +++ b/src/components/PageBuilder/blocks/Text/Text.tsx @@ -0,0 +1,51 @@ +import styled, { css } from "styled-components"; +import { P5 } from "@deskpro/deskpro-ui"; +import type { FC } from "react"; + +export const dpNormalize = css` + p { + margin-top: 0; + white-space: pre-wrap; + overflow: hidden; + text-overflow: ellipsis; + } + + p:first-child, + ol:first-child, + ul:first-child { + margin-top: 0; + } + + ol, ul { + padding-inline-start: 20px; + } + + img { + width: 100%; + height: auto; + } + + a, a:hover { + color: ${({ theme }) => theme.colors.cyan100}; + } +`; + +type Props = { + value: string|number; +}; + +const DPNormalize = styled.div` + width: 100%; + + ${dpNormalize} +`; + +const Text: FC = ({ value }) => { + return ( + + + + ); +} + +export { Text }; diff --git a/src/components/PageBuilder/blocks/Text/index.ts b/src/components/PageBuilder/blocks/Text/index.ts new file mode 100644 index 0000000..7afe56f --- /dev/null +++ b/src/components/PageBuilder/blocks/Text/index.ts @@ -0,0 +1 @@ +export { Text } from "./Text"; diff --git a/src/components/PageBuilder/blocks/Title/Title.tsx b/src/components/PageBuilder/blocks/Title/Title.tsx new file mode 100644 index 0000000..23aafd1 --- /dev/null +++ b/src/components/PageBuilder/blocks/Title/Title.tsx @@ -0,0 +1,28 @@ +import get from "lodash/get"; +import * as faIcons from "@fortawesome/free-solid-svg-icons"; +import { Title as TitleSDK } from "@deskpro/app-sdk"; +import { ShopifyLogo } from "../../../common"; +import type { FC } from "react"; + +type Props = { + value: string, + icon?: string, + link?: string, +}; + +const Title: FC = ({ value, icon, link, ...props }) => { + const linkIcon = !icon + ? + : get(faIcons, [icon]) || ; + + return ( + + ); +}; + +export { Title }; diff --git a/src/components/PageBuilder/blocks/Title/index.ts b/src/components/PageBuilder/blocks/Title/index.ts new file mode 100644 index 0000000..c658a5e --- /dev/null +++ b/src/components/PageBuilder/blocks/Title/index.ts @@ -0,0 +1 @@ +export { Title } from "./Title"; diff --git a/src/components/PageBuilder/blocks/Toggle/Toggle.tsx b/src/components/PageBuilder/blocks/Toggle/Toggle.tsx new file mode 100644 index 0000000..2f3d9d8 --- /dev/null +++ b/src/components/PageBuilder/blocks/Toggle/Toggle.tsx @@ -0,0 +1,20 @@ +import get from "lodash/get"; +import toLower from "lodash/toLower"; +import { Toggle as ToggleUI } from "@deskpro/deskpro-ui"; +import type { FC } from "react"; +import type { ToggleProps } from "@deskpro/deskpro-ui"; +import type { BlockRules } from "../../types"; + +type Props = ToggleProps & { + rules: BlockRules, + value: string, +}; + +const Toggle: FC = ({ value, rules, ...props}) => ( + +); + +export { Toggle }; diff --git a/src/components/PageBuilder/blocks/Toggle/index.ts b/src/components/PageBuilder/blocks/Toggle/index.ts new file mode 100644 index 0000000..ec866c8 --- /dev/null +++ b/src/components/PageBuilder/blocks/Toggle/index.ts @@ -0,0 +1 @@ +export { Toggle } from "./Toggle"; diff --git a/src/components/PageBuilder/blocks/index.ts b/src/components/PageBuilder/blocks/index.ts new file mode 100644 index 0000000..77ec7ce --- /dev/null +++ b/src/components/PageBuilder/blocks/index.ts @@ -0,0 +1,13 @@ +import { Text } from "./Text"; +import { Tags } from "./Tags"; +import { Toggle } from "./Toggle"; +import { Title } from "./Title"; + +const defaultBlocksMap = { + text: Text, + tags: Tags, + toggle: Toggle, + title: Title, +}; + +export { defaultBlocksMap }; diff --git a/src/components/PageBuilder/index.ts b/src/components/PageBuilder/index.ts new file mode 100644 index 0000000..8c0e583 --- /dev/null +++ b/src/components/PageBuilder/index.ts @@ -0,0 +1,2 @@ +export { PageBuilder } from "./PageBuilder"; +export { defaultBlocksMap } from "./blocks"; diff --git a/src/components/PageBuilder/types.ts b/src/components/PageBuilder/types.ts new file mode 100644 index 0000000..ce40ebf --- /dev/null +++ b/src/components/PageBuilder/types.ts @@ -0,0 +1,30 @@ +import type { FC } from "react"; +import type { Dict } from "../../types"; + +export type BlockSet = { + type: string; + label?: string, + pathInStore?: string|string[], + props?: { value?: unknown } & Dict; +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type BlockMap = Dict>; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type Store = Dict; + +type Config = { + structure: Array, + blocks: Record, +}; + +export type BlockRules = { + eq?: string, +}; + +export type PageBuilderProps = { + config: Config, + store?: Store, + blocksMap?: BlockMap, +}; diff --git a/src/components/ViewCustomer/ViewCustomer.tsx b/src/components/ViewCustomer/ViewCustomer.tsx deleted file mode 100644 index 6b70950..0000000 --- a/src/components/ViewCustomer/ViewCustomer.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import get from "lodash/get"; -import { faTimes } from "@fortawesome/free-solid-svg-icons"; -import { Stack, Tag, Toggle } from "@deskpro/deskpro-ui"; -import { Property } from "@deskpro/app-sdk"; -import { getTagColorSchema } from "../../utils"; -import { Container } from "../common"; -import type { FC } from "react"; -import type { DeskproTheme, AnyIcon } from "@deskpro/deskpro-ui"; -import type { CustomerType } from "../../services/shopify/types"; -import type { Maybe } from "../../types"; - -type Props = { - theme: DeskproTheme, - customer: Maybe, -}; - -const ViewCustomer: FC = ({ customer, theme }) => { - return ( - - - - - {customer?.tags.map((tag) => ( - - ))} - - )} - /> - - )} - /> - - - ); -}; - -export { ViewCustomer }; diff --git a/src/components/ViewCustomer/index.ts b/src/components/ViewCustomer/index.ts deleted file mode 100644 index 96145f7..0000000 --- a/src/components/ViewCustomer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { ViewCustomer } from "./ViewCustomer"; diff --git a/src/pages/ViewCustomerPage/ViewCustomerPage.tsx b/src/pages/ViewCustomerPage/ViewCustomerPage.tsx index bfb163c..1c86478 100644 --- a/src/pages/ViewCustomerPage/ViewCustomerPage.tsx +++ b/src/pages/ViewCustomerPage/ViewCustomerPage.tsx @@ -1,23 +1,21 @@ import { useMemo } from "react"; import { useSearchParams } from "react-router-dom"; -import { - LoadingSpinner, - useDeskproAppTheme, - useDeskproAppClient, -} from "@deskpro/app-sdk"; +import { LoadingSpinner, useDeskproAppClient } from "@deskpro/app-sdk"; import { useSetTitle, useExternalLink, useRegisterElements, } from "../../hooks"; import { useCustomer } from "../../hooks"; -import { ViewCustomer } from "../../components"; +import { + PageBuilder, + defaultBlocksMap, +} from "../../components/PageBuilder"; import type { FC } from "react"; const ViewCustomerPage: FC = () => { const [searchParams] = useSearchParams(); const customerId = searchParams.get("customerId"); - const { theme } = useDeskproAppTheme(); const { client } = useDeskproAppClient(); const { getCustomerLink } = useExternalLink(); const { isLoading, customer } = useCustomer(customerId); @@ -25,7 +23,7 @@ const ViewCustomerPage: FC = () => { return getCustomerLink(customer?.legacyResourceId); }, [getCustomerLink, customer?.legacyResourceId]); - useSetTitle(customer?.displayName || "Shopify"); + useSetTitle("Shopify"); useRegisterElements(({ registerElement }) => { registerElement("refresh", { type: "refresh_button" }); @@ -43,14 +41,6 @@ const ViewCustomerPage: FC = () => { } }, }); - - if (customerLink) { - registerElement("external", { - type: "cta_external_link", - url: customerLink, - hasIcon: true, - }); - } }, [client, customer]); @@ -59,9 +49,60 @@ const ViewCustomerPage: FC = () => { } return ( - ); }; From 79ecde7069e3e696d08842596b1c7f8fb7d881b7 Mon Sep 17 00:00:00 2001 From: ilia makarov Date: Wed, 21 Feb 2024 18:49:05 +0200 Subject: [PATCH 2/2] Migrate to app-builder --- package.json | 1 + .../ErrorFallback/ErrorFallback.tsx | 13 +- src/components/Home/Home.tsx | 14 +- src/components/PageBuilder/GenerateBlock.tsx | 55 -- src/components/PageBuilder/PageBuilder.tsx | 47 -- .../PageBuilder/blocks/Tags/Tags.tsx | 52 -- .../PageBuilder/blocks/Tags/index.ts | 1 - .../PageBuilder/blocks/Text/Text.tsx | 51 -- .../PageBuilder/blocks/Text/index.ts | 1 - .../PageBuilder/blocks/Title/Title.tsx | 28 - .../PageBuilder/blocks/Title/index.ts | 1 - .../PageBuilder/blocks/Toggle/Toggle.tsx | 20 - .../PageBuilder/blocks/Toggle/index.ts | 1 - src/components/PageBuilder/blocks/index.ts | 13 - src/components/PageBuilder/index.ts | 2 - src/components/PageBuilder/types.ts | 30 - src/components/common/Error/ErrorBlock.tsx | 5 +- src/components/index.ts | 1 - src/pages/HomePage/HomePage.tsx | 111 ++- .../ViewCustomerPage/ViewCustomerPage.tsx | 117 ++- src/services/shopify/getCustomer.ts | 3 +- src/utils/__tests__/formatPrice.test.ts | 9 + src/utils/formatPrice.ts | 21 + src/utils/index.ts | 1 + testing/mocks/index.ts | 3 + testing/mocks/mockCustomer.json | 669 ++++++++++++++++++ testing/mocks/mockCustomersSearch.json | 55 ++ testing/mocks/mockGetCustomersNotExist.json | 18 + 28 files changed, 946 insertions(+), 397 deletions(-) delete mode 100644 src/components/PageBuilder/GenerateBlock.tsx delete mode 100644 src/components/PageBuilder/PageBuilder.tsx delete mode 100644 src/components/PageBuilder/blocks/Tags/Tags.tsx delete mode 100644 src/components/PageBuilder/blocks/Tags/index.ts delete mode 100644 src/components/PageBuilder/blocks/Text/Text.tsx delete mode 100644 src/components/PageBuilder/blocks/Text/index.ts delete mode 100644 src/components/PageBuilder/blocks/Title/Title.tsx delete mode 100644 src/components/PageBuilder/blocks/Title/index.ts delete mode 100644 src/components/PageBuilder/blocks/Toggle/Toggle.tsx delete mode 100644 src/components/PageBuilder/blocks/Toggle/index.ts delete mode 100644 src/components/PageBuilder/blocks/index.ts delete mode 100644 src/components/PageBuilder/index.ts delete mode 100644 src/components/PageBuilder/types.ts create mode 100644 src/utils/__tests__/formatPrice.test.ts create mode 100644 src/utils/formatPrice.ts create mode 100644 testing/mocks/mockCustomer.json create mode 100644 testing/mocks/mockCustomersSearch.json create mode 100644 testing/mocks/mockGetCustomersNotExist.json diff --git a/package.json b/package.json index bdfdb70..bdc9fa4 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "dependencies": { "@deskpro/app-sdk": "^3.0.12", "@deskpro/deskpro-ui": "^7.1.0", + "@deskpro/app-builder": "^0.0.1", "@fortawesome/free-solid-svg-icons": "^6.4.2", "@heroicons/react": "1.0.6", "@tanstack/react-query": "^4.36.1", diff --git a/src/components/ErrorFallback/ErrorFallback.tsx b/src/components/ErrorFallback/ErrorFallback.tsx index f7750c1..4dd29bc 100644 --- a/src/components/ErrorFallback/ErrorFallback.tsx +++ b/src/components/ErrorFallback/ErrorFallback.tsx @@ -1,5 +1,4 @@ -import get from "lodash/get"; -import { Stack } from "@deskpro/deskpro-ui"; +import { get, map } from "lodash"; import { DEFAULT_ERROR } from "../../constants"; import { ShopifyError } from "../../services/shopify"; import { Container, ErrorBlock } from "../common"; @@ -11,24 +10,20 @@ type Props = Omit & { }; const ErrorFallback: FC = ({ error }) => { - let message = DEFAULT_ERROR; + let message: string|string[] = DEFAULT_ERROR; // eslint-disable-next-line no-console console.error(error); if (error instanceof ShopifyError) { - message = get(error, ["data", "errors"]) + message = map(get(error, ["data", "errors"], []), "message") || DEFAULT_ERROR; } return ( - {message} - - )} + text={message} /> ); diff --git a/src/components/Home/Home.tsx b/src/components/Home/Home.tsx index ff1b3b2..1d1f3ee 100644 --- a/src/components/Home/Home.tsx +++ b/src/components/Home/Home.tsx @@ -1,7 +1,7 @@ import get from "lodash/get"; import { HorizontalDivider } from "@deskpro/app-sdk"; import { Container } from "../common"; -import { CustomerInfo, Orders, Comments } from "./block"; +import { Orders, Comments } from "./block"; import type { FC } from "react"; import type { Maybe } from "../../types"; import type { CustomerType, Order } from "../../services/shopify/types"; @@ -20,25 +20,13 @@ type Props = { const Home: FC = ({ orders, customer, - onNavigateToCustomer, onNavigateToOrders, onNavigateToOrder, getOrderLink, getOrdersLink, - getCustomerLink, }) => { return ( <> - - - - - - >, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - store?: Dict, -}; - -const PropertyWrapper = styled.div` - margin-left: 8px; - margin-right: 8px; -`; - -const GenerateBlock: FC = ({ blockConfig, Component, store }) => { - const blockType = get(blockConfig, ["type"]); - const { value, ...blockProps } = get(blockConfig, ["props"]) || {}; - const label = get(blockConfig, ["label"]); - const pathInStore = get(blockConfig, ["pathInStore"], ""); - - if (!blockConfig) { - // eslint-disable-next-line no-console - console.error("PageBuilder: wrong config - block config not found"); - return null; - } - - if (!Component) { - // eslint-disable-next-line no-console - console.error("PageBuilder: can't find component for block type", blockType); - return null; - } - - return ( - - - )} - /> - - ); -}; - -export { GenerateBlock }; diff --git a/src/components/PageBuilder/PageBuilder.tsx b/src/components/PageBuilder/PageBuilder.tsx deleted file mode 100644 index 8d5737c..0000000 --- a/src/components/PageBuilder/PageBuilder.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import get from "lodash/get"; -import isEmpty from "lodash/isEmpty"; -import { GenerateBlock } from "./GenerateBlock"; -import { defaultBlocksMap } from "./blocks"; -import type { FC } from "react"; -import type { PageBuilderProps } from "./types"; - -const PageBuilder: FC = ({ - store, - config: { structure, blocks }, - blocksMap, -}) => { - if (!Array.isArray(structure) ||isEmpty(structure)) { - // eslint-disable-next-line no-console - console.error("PageBuilder: wrong config - empty structure"); - return null; - } - - if (isEmpty(blocks)) { - // eslint-disable-next-line no-console - console.error("PageBuilder: wrong config - empty block"); - return null; - } - - return ( - <> - {structure.map((blockName) => { - const name = blockName[0]; - const blockConfig = get(blocks, name); - const blockType = get(blockConfig, ["type"]); - const Component = get(blocksMap || defaultBlocksMap, [blockType]); - - return ( - - ); - })} - - ); -}; - -export { PageBuilder }; diff --git a/src/components/PageBuilder/blocks/Tags/Tags.tsx b/src/components/PageBuilder/blocks/Tags/Tags.tsx deleted file mode 100644 index a5c3e1e..0000000 --- a/src/components/PageBuilder/blocks/Tags/Tags.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { match } from "ts-pattern"; -import size from "lodash/size"; -import { faTimes } from "@fortawesome/free-solid-svg-icons"; -import { Stack, Tag, lightTheme } from "@deskpro/deskpro-ui"; -import type { FC } from "react"; -import type { AnyIcon, DeskproTheme } from "@deskpro/deskpro-ui"; - -type Props = { - value: string[]; -}; - -const getTagColorSchema = ({ colors }: DeskproTheme, tag: string) => { - return match(tag.trim().toLowerCase()) - .with('vip', () => ({ - borderColor: colors.orange100, - backgroundColor: `${colors.orange100}33`, - textColor: colors.orange100, - })) - .with('development', () => ({ - borderColor: colors.turquoise100, - backgroundColor: `${colors.turquoise100}33`, - textColor: colors.turquoise100, - })) - .otherwise(() => ({ - borderColor: colors.grey80, - backgroundColor: `${colors.grey80}33`, - textColor: colors.grey80, - })); -}; - -const Tags: FC = ({ value }) => { - if (!Array.isArray(value) || !size(value)) { - return ( - <>- - ); - } - - return ( - - {value.map((tag) => ( - - ))} - - ); -}; - -export { Tags }; diff --git a/src/components/PageBuilder/blocks/Tags/index.ts b/src/components/PageBuilder/blocks/Tags/index.ts deleted file mode 100644 index 62fc186..0000000 --- a/src/components/PageBuilder/blocks/Tags/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Tags } from "./Tags"; diff --git a/src/components/PageBuilder/blocks/Text/Text.tsx b/src/components/PageBuilder/blocks/Text/Text.tsx deleted file mode 100644 index 4eae15b..0000000 --- a/src/components/PageBuilder/blocks/Text/Text.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import styled, { css } from "styled-components"; -import { P5 } from "@deskpro/deskpro-ui"; -import type { FC } from "react"; - -export const dpNormalize = css` - p { - margin-top: 0; - white-space: pre-wrap; - overflow: hidden; - text-overflow: ellipsis; - } - - p:first-child, - ol:first-child, - ul:first-child { - margin-top: 0; - } - - ol, ul { - padding-inline-start: 20px; - } - - img { - width: 100%; - height: auto; - } - - a, a:hover { - color: ${({ theme }) => theme.colors.cyan100}; - } -`; - -type Props = { - value: string|number; -}; - -const DPNormalize = styled.div` - width: 100%; - - ${dpNormalize} -`; - -const Text: FC = ({ value }) => { - return ( - - - - ); -} - -export { Text }; diff --git a/src/components/PageBuilder/blocks/Text/index.ts b/src/components/PageBuilder/blocks/Text/index.ts deleted file mode 100644 index 7afe56f..0000000 --- a/src/components/PageBuilder/blocks/Text/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Text } from "./Text"; diff --git a/src/components/PageBuilder/blocks/Title/Title.tsx b/src/components/PageBuilder/blocks/Title/Title.tsx deleted file mode 100644 index 23aafd1..0000000 --- a/src/components/PageBuilder/blocks/Title/Title.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import get from "lodash/get"; -import * as faIcons from "@fortawesome/free-solid-svg-icons"; -import { Title as TitleSDK } from "@deskpro/app-sdk"; -import { ShopifyLogo } from "../../../common"; -import type { FC } from "react"; - -type Props = { - value: string, - icon?: string, - link?: string, -}; - -const Title: FC = ({ value, icon, link, ...props }) => { - const linkIcon = !icon - ? - : get(faIcons, [icon]) || ; - - return ( - - ); -}; - -export { Title }; diff --git a/src/components/PageBuilder/blocks/Title/index.ts b/src/components/PageBuilder/blocks/Title/index.ts deleted file mode 100644 index c658a5e..0000000 --- a/src/components/PageBuilder/blocks/Title/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Title } from "./Title"; diff --git a/src/components/PageBuilder/blocks/Toggle/Toggle.tsx b/src/components/PageBuilder/blocks/Toggle/Toggle.tsx deleted file mode 100644 index 2f3d9d8..0000000 --- a/src/components/PageBuilder/blocks/Toggle/Toggle.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import get from "lodash/get"; -import toLower from "lodash/toLower"; -import { Toggle as ToggleUI } from "@deskpro/deskpro-ui"; -import type { FC } from "react"; -import type { ToggleProps } from "@deskpro/deskpro-ui"; -import type { BlockRules } from "../../types"; - -type Props = ToggleProps & { - rules: BlockRules, - value: string, -}; - -const Toggle: FC = ({ value, rules, ...props}) => ( - -); - -export { Toggle }; diff --git a/src/components/PageBuilder/blocks/Toggle/index.ts b/src/components/PageBuilder/blocks/Toggle/index.ts deleted file mode 100644 index ec866c8..0000000 --- a/src/components/PageBuilder/blocks/Toggle/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Toggle } from "./Toggle"; diff --git a/src/components/PageBuilder/blocks/index.ts b/src/components/PageBuilder/blocks/index.ts deleted file mode 100644 index 77ec7ce..0000000 --- a/src/components/PageBuilder/blocks/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Text } from "./Text"; -import { Tags } from "./Tags"; -import { Toggle } from "./Toggle"; -import { Title } from "./Title"; - -const defaultBlocksMap = { - text: Text, - tags: Tags, - toggle: Toggle, - title: Title, -}; - -export { defaultBlocksMap }; diff --git a/src/components/PageBuilder/index.ts b/src/components/PageBuilder/index.ts deleted file mode 100644 index 8c0e583..0000000 --- a/src/components/PageBuilder/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { PageBuilder } from "./PageBuilder"; -export { defaultBlocksMap } from "./blocks"; diff --git a/src/components/PageBuilder/types.ts b/src/components/PageBuilder/types.ts deleted file mode 100644 index ce40ebf..0000000 --- a/src/components/PageBuilder/types.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { FC } from "react"; -import type { Dict } from "../../types"; - -export type BlockSet = { - type: string; - label?: string, - pathInStore?: string|string[], - props?: { value?: unknown } & Dict; -}; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type BlockMap = Dict>; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type Store = Dict; - -type Config = { - structure: Array, - blocks: Record, -}; - -export type BlockRules = { - eq?: string, -}; - -export type PageBuilderProps = { - config: Config, - store?: Store, - blocksMap?: BlockMap, -}; diff --git a/src/components/common/Error/ErrorBlock.tsx b/src/components/common/Error/ErrorBlock.tsx index f042d91..9d3b17b 100644 --- a/src/components/common/Error/ErrorBlock.tsx +++ b/src/components/common/Error/ErrorBlock.tsx @@ -1,9 +1,10 @@ import styled from "styled-components"; import { P5 } from "@deskpro/deskpro-ui"; +import { DEFAULT_ERROR } from "../../../constants"; import type { FC, JSX } from "react"; import type { Maybe } from "../../../types"; -type Props = { +export type Props = { text?: Maybe>, } @@ -15,7 +16,7 @@ const StyledErrorBlock = styled(P5)` background-color: ${({ theme }) => theme.colors.red100}; `; -const ErrorBlock: FC = ({ text = "An error occurred" }) => ( +const ErrorBlock: FC = ({ text = DEFAULT_ERROR }) => ( <> {Array.isArray(text) ? text.map((msg, idx) => ({msg})) diff --git a/src/components/index.ts b/src/components/index.ts index 41ec81e..bc56618 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -2,4 +2,3 @@ export { Home } from "./Home"; export { LinkCustomer } from "./LinkCustomer"; export { ListOrders } from "./ListOrders"; export { ViewOrder } from "./ViewOrder"; -export { ViewCustomer } from "./ViewCustomer"; diff --git a/src/pages/HomePage/HomePage.tsx b/src/pages/HomePage/HomePage.tsx index 66f959f..187416c 100644 --- a/src/pages/HomePage/HomePage.tsx +++ b/src/pages/HomePage/HomePage.tsx @@ -1,6 +1,7 @@ import { FC, useCallback } from "react"; import { useNavigate } from "react-router-dom"; import { LoadingSpinner } from "@deskpro/app-sdk"; +import { PageBuilder } from "@deskpro/app-builder"; import { useSetTitle, useExternalLink, @@ -51,18 +52,108 @@ const HomePage: FC = () => { ); } + // return ( + // <>HomePage + // ); return ( - + ); + /*return ( + <> + + + );*/ }; export { HomePage }; diff --git a/src/pages/ViewCustomerPage/ViewCustomerPage.tsx b/src/pages/ViewCustomerPage/ViewCustomerPage.tsx index 1c86478..26f7f28 100644 --- a/src/pages/ViewCustomerPage/ViewCustomerPage.tsx +++ b/src/pages/ViewCustomerPage/ViewCustomerPage.tsx @@ -1,16 +1,13 @@ import { useMemo } from "react"; import { useSearchParams } from "react-router-dom"; import { LoadingSpinner, useDeskproAppClient } from "@deskpro/app-sdk"; +import { PageBuilder } from "@deskpro/app-builder"; import { useSetTitle, useExternalLink, useRegisterElements, } from "../../hooks"; import { useCustomer } from "../../hooks"; -import { - PageBuilder, - defaultBlocksMap, -} from "../../components/PageBuilder"; import type { FC } from "react"; const ViewCustomerPage: FC = () => { @@ -49,62 +46,64 @@ const ViewCustomerPage: FC = () => { } return ( - + <>ViewCustomerPage ); + // return ( + // + // ); }; export { ViewCustomerPage }; diff --git a/src/services/shopify/getCustomer.ts b/src/services/shopify/getCustomer.ts index 3b47e4e..95e8061 100644 --- a/src/services/shopify/getCustomer.ts +++ b/src/services/shopify/getCustomer.ts @@ -1,9 +1,10 @@ /* eslint-disable */ -import get from "lodash/get"; +import { get, random } from "lodash"; import { IDeskproClient } from "@deskpro/app-sdk"; import { baseGraphQLRequest } from "./baseGraphQLRequest"; import { gql } from "../../utils"; import type { CustomerType } from "./types"; +import { mockCustomer } from "../../../testing"; const getCustomer = ( client: IDeskproClient, diff --git a/src/utils/__tests__/formatPrice.test.ts b/src/utils/__tests__/formatPrice.test.ts new file mode 100644 index 0000000..335c67d --- /dev/null +++ b/src/utils/__tests__/formatPrice.test.ts @@ -0,0 +1,9 @@ +import { formatPrice } from "../formatPrice"; + +describe("formatPrice", () => { + test("should return formatted price", () => { + expect(formatPrice(0, { currency: "UAH" })).toBe("₴0.00"); + expect(formatPrice(10, { currency: "GBP" })).toBe("£10.00"); + expect(formatPrice(23.5, { style: "decimal" })).toBe("23.50"); + }); +}); diff --git a/src/utils/formatPrice.ts b/src/utils/formatPrice.ts new file mode 100644 index 0000000..f389cd7 --- /dev/null +++ b/src/utils/formatPrice.ts @@ -0,0 +1,21 @@ +type Options = { + currency?: string, + style?: + | "decimal" // for plain number formatting + | "currency" // for currency formatting + | "percent" // for percent formatting + | "unit" // for unit formatting +}; + +const formatPrice = (price?: string|number, options?: Options): string => { + const formatter = new Intl.NumberFormat("en-GB", { + style: options?.style || "currency", + currency: options?.currency || "GBP", + currencyDisplay: "narrowSymbol", + minimumFractionDigits: 2, + }); + + return formatter.format(Number(price || 0)); +}; + +export { formatPrice }; diff --git a/src/utils/index.ts b/src/utils/index.ts index c417550..b6eea34 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -17,3 +17,4 @@ export { isLast } from "./isLast"; export { isForm } from "./isForm"; export { getQueryParams } from "./getQueryParams"; export { gql } from "./gql"; +export { formatPrice } from "./formatPrice"; diff --git a/testing/mocks/index.ts b/testing/mocks/index.ts index 9f7f2ed..64f3843 100644 --- a/testing/mocks/index.ts +++ b/testing/mocks/index.ts @@ -1 +1,4 @@ export { default as mockContext } from "./mockContext.json"; +export { default as mockCustomer } from "./mockCustomer.json"; +export { default as mockCustomersSearch } from "./mockCustomersSearch.json"; +export { default as mockGetCustomersNotExist } from "./mockGetCustomersNotExist.json"; diff --git a/testing/mocks/mockCustomer.json b/testing/mocks/mockCustomer.json new file mode 100644 index 0000000..c1401ee --- /dev/null +++ b/testing/mocks/mockCustomer.json @@ -0,0 +1,669 @@ +{ + "data": { + "customer": { + "id": "gid://shopify/Customer/7982046085440", + "legacyResourceId": "7982046085440", + "displayName": "Christian Hatfield", + "createdAt": "2024-01-18T17:31:36Z", + "email": "egnition_sample_60@egnition.com", + "hasTimelineComment": false, + "locale": "en", + "note": null, + "phone": "+12423337689", + "firstName": "Christian", + "lastName": "Hatfield", + "numberOfOrders": "20", + "tags": [ + "egnition-sample-data", + "VIP" + ], + "amountSpent": { + "amount": "2.3", + "currencyCode": "UAH" + }, + "emailMarketingConsent": { + "marketingState": "NOT_SUBSCRIBED", + "consentUpdatedAt": null, + "marketingOptInLevel": "SINGLE_OPT_IN" + }, + "orders": { + "edges": [ + { + "node": { + "id": "gid://shopify/Order/5661348102464", + "legacyResourceId": "5661348102464", + "createdAt": "2024-01-18T17:31:51Z", + "displayFinancialStatus": "PENDING", + "displayFulfillmentStatus": "UNFULFILLED", + "name": "#1021", + "customer": { + "lastName": "Hatfield", + "firstName": "Christian", + "displayName": "Christian Hatfield" + }, + "lineItems": { + "edges": [ + { + "node": { + "id": "gid://shopify/LineItem/14438955745600", + "title": "THE CREATION OF ADAM | MICHELANGELO" + } + }, + { + "node": { + "id": "gid://shopify/LineItem/14438955778368", + "title": "THE NIGHT WATCH | REMBRANDT VAN RIJN" + } + } + ] + } + } + }, + { + "node": { + "id": "gid://shopify/Order/5661348233536", + "legacyResourceId": "5661348233536", + "createdAt": "2024-01-18T17:31:57Z", + "displayFinancialStatus": "PAID", + "displayFulfillmentStatus": "UNFULFILLED", + "name": "#1022", + "customer": { + "lastName": "Hatfield", + "firstName": "Christian", + "displayName": "Christian Hatfield" + }, + "lineItems": { + "edges": [ + { + "node": { + "id": "gid://shopify/LineItem/14438955876672", + "title": "GIRL WITH A PEARL EARRING | JOHANNES VERMEER" + } + } + ] + } + } + }, + { + "node": { + "id": "gid://shopify/Order/5661348528448", + "legacyResourceId": "5661348528448", + "createdAt": "2024-01-18T17:32:03Z", + "displayFinancialStatus": "PAID", + "displayFulfillmentStatus": "UNFULFILLED", + "name": "#1023", + "customer": { + "lastName": "Hatfield", + "firstName": "Christian", + "displayName": "Christian Hatfield" + }, + "lineItems": { + "edges": [ + { + "node": { + "id": "gid://shopify/LineItem/14438956433728", + "title": "GUERNICA | PABLO PICASSO" + } + } + ] + } + } + }, + { + "node": { + "id": "gid://shopify/Order/5661348692288", + "legacyResourceId": "5661348692288", + "createdAt": "2024-01-18T17:32:09Z", + "displayFinancialStatus": "REFUNDED", + "displayFulfillmentStatus": "UNFULFILLED", + "name": "#1024", + "customer": { + "lastName": "Hatfield", + "firstName": "Christian", + "displayName": "Christian Hatfield" + }, + "lineItems": { + "edges": [ + { + "node": { + "id": "gid://shopify/LineItem/14438956663104", + "title": "GIRL WITH A PEARL EARRING | JOHANNES VERMEER" + } + }, + { + "node": { + "id": "gid://shopify/LineItem/14438956695872", + "title": "THE PERSISTENCE OF MEMORY | SALVADOR DALI" + } + }, + { + "node": { + "id": "gid://shopify/LineItem/14438956728640", + "title": "THE CREATION OF ADAM | MICHELANGELO" + } + } + ] + } + } + }, + { + "node": { + "id": "gid://shopify/Order/5661349904704", + "legacyResourceId": "5661349904704", + "createdAt": "2024-01-18T17:32:55Z", + "displayFinancialStatus": "PAID", + "displayFulfillmentStatus": "UNFULFILLED", + "name": "#1025", + "customer": { + "lastName": "Hatfield", + "firstName": "Christian", + "displayName": "Christian Hatfield" + }, + "lineItems": { + "edges": [ + { + "node": { + "id": "gid://shopify/LineItem/14438959481152", + "title": "THE NIGHT WATCH | REMBRANDT VAN RIJN" + } + } + ] + } + } + }, + { + "node": { + "id": "gid://shopify/Order/5661350199616", + "legacyResourceId": "5661350199616", + "createdAt": "2024-01-18T17:33:01Z", + "displayFinancialStatus": "PAID", + "displayFulfillmentStatus": "UNFULFILLED", + "name": "#1026", + "customer": { + "lastName": "Hatfield", + "firstName": "Christian", + "displayName": "Christian Hatfield" + }, + "lineItems": { + "edges": [ + { + "node": { + "id": "gid://shopify/LineItem/14438960234816", + "title": "STARRY NIGHT | VINCENT VAN GOGH" + } + } + ] + } + } + }, + { + "node": { + "id": "gid://shopify/Order/5661350396224", + "legacyResourceId": "5661350396224", + "createdAt": "2024-01-18T17:33:07Z", + "displayFinancialStatus": "PAID", + "displayFulfillmentStatus": "UNFULFILLED", + "name": "#1027", + "customer": { + "lastName": "Hatfield", + "firstName": "Christian", + "displayName": "Christian Hatfield" + }, + "lineItems": { + "edges": [ + { + "node": { + "id": "gid://shopify/LineItem/14438960496960", + "title": "THE CREATION OF ADAM | MICHELANGELO" + } + } + ] + } + } + }, + { + "node": { + "id": "gid://shopify/Order/5661350527296", + "legacyResourceId": "5661350527296", + "createdAt": "2024-01-18T17:33:13Z", + "displayFinancialStatus": "REFUNDED", + "displayFulfillmentStatus": "UNFULFILLED", + "name": "#1028", + "customer": { + "lastName": "Hatfield", + "firstName": "Christian", + "displayName": "Christian Hatfield" + }, + "lineItems": { + "edges": [ + { + "node": { + "id": "gid://shopify/LineItem/14438960660800", + "title": "THE LAST SUPPER | LEONARDO DA VINCI" + } + }, + { + "node": { + "id": "gid://shopify/LineItem/14438960693568", + "title": "MONA LISA | LEONARDO DA VINCI" + } + }, + { + "node": { + "id": "gid://shopify/LineItem/14438960726336", + "title": "THE PERSISTENCE OF MEMORY | SALVADOR DALI" + } + } + ] + } + } + }, + { + "node": { + "id": "gid://shopify/Order/5661351575872", + "legacyResourceId": "5661351575872", + "createdAt": "2024-01-18T17:33:59Z", + "displayFinancialStatus": "REFUNDED", + "displayFulfillmentStatus": "UNFULFILLED", + "name": "#1029", + "customer": { + "lastName": "Hatfield", + "firstName": "Christian", + "displayName": "Christian Hatfield" + }, + "lineItems": { + "edges": [ + { + "node": { + "id": "gid://shopify/LineItem/14438961938752", + "title": "GIRL WITH A PEARL EARRING | JOHANNES VERMEER" + } + }, + { + "node": { + "id": "gid://shopify/LineItem/14438961971520", + "title": "THE SCREAM | EDVARD MUNCH" + } + } + ] + } + } + }, + { + "node": { + "id": "gid://shopify/Order/5661351641408", + "legacyResourceId": "5661351641408", + "createdAt": "2024-01-18T17:34:05Z", + "displayFinancialStatus": "PENDING", + "displayFulfillmentStatus": "UNFULFILLED", + "name": "#1030", + "customer": { + "lastName": "Hatfield", + "firstName": "Christian", + "displayName": "Christian Hatfield" + }, + "lineItems": { + "edges": [ + { + "node": { + "id": "gid://shopify/LineItem/14438962069824", + "title": "THE NIGHT WATCH | REMBRANDT VAN RIJN" + } + }, + { + "node": { + "id": "gid://shopify/LineItem/14438962102592", + "title": "THE PERSISTENCE OF MEMORY | SALVADOR DALI" + } + }, + { + "node": { + "id": "gid://shopify/LineItem/14438962135360", + "title": "GIRL WITH A PEARL EARRING | JOHANNES VERMEER" + } + } + ] + } + } + }, + { + "node": { + "id": "gid://shopify/Order/5661351903552", + "legacyResourceId": "5661351903552", + "createdAt": "2024-01-18T17:34:10Z", + "displayFinancialStatus": "PENDING", + "displayFulfillmentStatus": "UNFULFILLED", + "name": "#1031", + "customer": { + "lastName": "Hatfield", + "firstName": "Christian", + "displayName": "Christian Hatfield" + }, + "lineItems": { + "edges": [ + { + "node": { + "id": "gid://shopify/LineItem/14438962397504", + "title": "SELF-PORTRAIT WITHOUT BEARD | VINCENT VAN GOGH" + } + }, + { + "node": { + "id": "gid://shopify/LineItem/14438962430272", + "title": "GUERNICA | PABLO PICASSO" + } + }, + { + "node": { + "id": "gid://shopify/LineItem/14438962463040", + "title": "STARRY NIGHT | VINCENT VAN GOGH" + } + } + ] + } + } + }, + { + "node": { + "id": "gid://shopify/Order/5661352067392", + "legacyResourceId": "5661352067392", + "createdAt": "2024-01-18T17:34:16Z", + "displayFinancialStatus": "PAID", + "displayFulfillmentStatus": "UNFULFILLED", + "name": "#1032", + "customer": { + "lastName": "Hatfield", + "firstName": "Christian", + "displayName": "Christian Hatfield" + }, + "lineItems": { + "edges": [ + { + "node": { + "id": "gid://shopify/LineItem/14438962692416", + "title": "SELF-PORTRAIT WITHOUT BEARD | VINCENT VAN GOGH" + } + } + ] + } + } + }, + { + "node": { + "id": "gid://shopify/Order/5661352952128", + "legacyResourceId": "5661352952128", + "createdAt": "2024-01-18T17:35:02Z", + "displayFinancialStatus": "REFUNDED", + "displayFulfillmentStatus": "UNFULFILLED", + "name": "#1033", + "customer": { + "lastName": "Hatfield", + "firstName": "Christian", + "displayName": "Christian Hatfield" + }, + "lineItems": { + "edges": [ + { + "node": { + "id": "gid://shopify/LineItem/14438963970368", + "title": "MONA LISA | LEONARDO DA VINCI" + } + }, + { + "node": { + "id": "gid://shopify/LineItem/14438964003136", + "title": "THE LAST SUPPER | LEONARDO DA VINCI" + } + }, + { + "node": { + "id": "gid://shopify/LineItem/14438964035904", + "title": "THE SCREAM | EDVARD MUNCH" + } + } + ] + } + } + }, + { + "node": { + "id": "gid://shopify/Order/5661353115968", + "legacyResourceId": "5661353115968", + "createdAt": "2024-01-18T17:35:08Z", + "displayFinancialStatus": "PENDING", + "displayFulfillmentStatus": "UNFULFILLED", + "name": "#1034", + "customer": { + "lastName": "Hatfield", + "firstName": "Christian", + "displayName": "Christian Hatfield" + }, + "lineItems": { + "edges": [ + { + "node": { + "id": "gid://shopify/LineItem/14438964232512", + "title": "MONA LISA | LEONARDO DA VINCI" + } + } + ] + } + } + }, + { + "node": { + "id": "gid://shopify/Order/5661353345344", + "legacyResourceId": "5661353345344", + "createdAt": "2024-01-18T17:35:14Z", + "displayFinancialStatus": "REFUNDED", + "displayFulfillmentStatus": "UNFULFILLED", + "name": "#1035", + "customer": { + "lastName": "Hatfield", + "firstName": "Christian", + "displayName": "Christian Hatfield" + }, + "lineItems": { + "edges": [ + { + "node": { + "id": "gid://shopify/LineItem/14438964527424", + "title": "THE NIGHT WATCH | REMBRANDT VAN RIJN" + } + }, + { + "node": { + "id": "gid://shopify/LineItem/14438964560192", + "title": "GIRL WITH A PEARL EARRING | JOHANNES VERMEER" + } + }, + { + "node": { + "id": "gid://shopify/LineItem/14438964592960", + "title": "SELF-PORTRAIT WITHOUT BEARD | VINCENT VAN GOGH" + } + } + ] + } + } + }, + { + "node": { + "id": "gid://shopify/Order/5661353443648", + "legacyResourceId": "5661353443648", + "createdAt": "2024-01-18T17:35:19Z", + "displayFinancialStatus": "PENDING", + "displayFulfillmentStatus": "UNFULFILLED", + "name": "#1036", + "customer": { + "lastName": "Hatfield", + "firstName": "Christian", + "displayName": "Christian Hatfield" + }, + "lineItems": { + "edges": [ + { + "node": { + "id": "gid://shopify/LineItem/14438964756800", + "title": "STARRY NIGHT | VINCENT VAN GOGH" + } + } + ] + } + } + }, + { + "node": { + "id": "gid://shopify/Order/5661354066240", + "legacyResourceId": "5661354066240", + "createdAt": "2024-01-18T17:36:06Z", + "displayFinancialStatus": "REFUNDED", + "displayFulfillmentStatus": "UNFULFILLED", + "name": "#1037", + "customer": { + "lastName": "Hatfield", + "firstName": "Christian", + "displayName": "Christian Hatfield" + }, + "lineItems": { + "edges": [ + { + "node": { + "id": "gid://shopify/LineItem/14438966001984", + "title": "THE SCREAM | EDVARD MUNCH" + } + }, + { + "node": { + "id": "gid://shopify/LineItem/14438966034752", + "title": "STARRY NIGHT | VINCENT VAN GOGH" + } + }, + { + "node": { + "id": "gid://shopify/LineItem/14438966067520", + "title": "MONA LISA | LEONARDO DA VINCI" + } + } + ] + } + } + }, + { + "node": { + "id": "gid://shopify/Order/5661354164544", + "legacyResourceId": "5661354164544", + "createdAt": "2024-01-18T17:36:12Z", + "displayFinancialStatus": "PAID", + "displayFulfillmentStatus": "UNFULFILLED", + "name": "#1038", + "customer": { + "lastName": "Hatfield", + "firstName": "Christian", + "displayName": "Christian Hatfield" + }, + "lineItems": { + "edges": [ + { + "node": { + "id": "gid://shopify/LineItem/14438966198592", + "title": "STARRY NIGHT | VINCENT VAN GOGH" + } + }, + { + "node": { + "id": "gid://shopify/LineItem/14438966231360", + "title": "GUERNICA | PABLO PICASSO" + } + }, + { + "node": { + "id": "gid://shopify/LineItem/14438966264128", + "title": "THE LAST SUPPER | LEONARDO DA VINCI" + } + } + ] + } + } + }, + { + "node": { + "id": "gid://shopify/Order/5661354197312", + "legacyResourceId": "5661354197312", + "createdAt": "2024-01-18T17:36:17Z", + "displayFinancialStatus": "PENDING", + "displayFulfillmentStatus": "UNFULFILLED", + "name": "#1039", + "customer": { + "lastName": "Hatfield", + "firstName": "Christian", + "displayName": "Christian Hatfield" + }, + "lineItems": { + "edges": [ + { + "node": { + "id": "gid://shopify/LineItem/14438966296896", + "title": "GIRL WITH A PEARL EARRING | JOHANNES VERMEER" + } + } + ] + } + } + }, + { + "node": { + "id": "gid://shopify/Order/5661354361152", + "legacyResourceId": "5661354361152", + "createdAt": "2024-01-18T17:36:23Z", + "displayFinancialStatus": "PAID", + "displayFulfillmentStatus": "UNFULFILLED", + "name": "#1040", + "customer": { + "lastName": "Hatfield", + "firstName": "Christian", + "displayName": "Christian Hatfield" + }, + "lineItems": { + "edges": [ + { + "node": { + "id": "gid://shopify/LineItem/14438966526272", + "title": "STARRY NIGHT | VINCENT VAN GOGH" + } + }, + { + "node": { + "id": "gid://shopify/LineItem/14438966559040", + "title": "STARRY NIGHT | VINCENT VAN GOGH" + } + }, + { + "node": { + "id": "gid://shopify/LineItem/14438966591808", + "title": "GUERNICA | PABLO PICASSO" + } + } + ] + } + } + } + ] + }, + "events": { + "edges": [] + } + } + }, + "extensions": { + "cost": { + "requestedQueryCost": 606, + "actualQueryCost": 126, + "throttleStatus": { + "maximumAvailable": 1000.0, + "currentlyAvailable": 874, + "restoreRate": 50.0 + } + } + } +} diff --git a/testing/mocks/mockCustomersSearch.json b/testing/mocks/mockCustomersSearch.json new file mode 100644 index 0000000..89ec009 --- /dev/null +++ b/testing/mocks/mockCustomersSearch.json @@ -0,0 +1,55 @@ +{ + "data": { + "customers": { + "edges": [ + { + "node": { + "id": "gid://shopify/Customer/7982046085440", + "createdAt": "2024-01-18T17:31:36Z", + "displayName": "Christian Hatfield", + "email": "egnition_sample_60@egnition.com", + "hasTimelineComment": false, + "locale": "en", + "note": null, + "phone": "+12423337689" + } + }, + { + "node": { + "id": "gid://shopify/Customer/7982046183744", + "createdAt": "2024-01-18T17:31:37Z", + "displayName": "Hamish Orr", + "email": "egnition_sample_96@egnition.com", + "hasTimelineComment": false, + "locale": "en", + "note": null, + "phone": "+38614296549" + } + }, + { + "node": { + "id": "gid://shopify/Customer/7982046347584", + "createdAt": "2024-01-18T17:31:38Z", + "displayName": "Ulric Lynch", + "email": "egnition_sample_98@egnition.com", + "hasTimelineComment": false, + "locale": "en", + "note": null, + "phone": "+262269600412" + } + } + ] + } + }, + "extensions": { + "cost": { + "requestedQueryCost": 102, + "actualQueryCost": 5, + "throttleStatus": { + "maximumAvailable": 1000.0, + "currentlyAvailable": 995, + "restoreRate": 50.0 + } + } + } +} diff --git a/testing/mocks/mockGetCustomersNotExist.json b/testing/mocks/mockGetCustomersNotExist.json new file mode 100644 index 0000000..3e4aab4 --- /dev/null +++ b/testing/mocks/mockGetCustomersNotExist.json @@ -0,0 +1,18 @@ +{ + "data": { + "customers": { + "edges": [] + } + }, + "extensions": { + "cost": { + "requestedQueryCost": 102, + "actualQueryCost": 2, + "throttleStatus": { + "maximumAvailable": 1000.0, + "currentlyAvailable": 998, + "restoreRate": 50.0 + } + } + } +}