diff --git a/src/components/Navigator/Navigator.tsx b/src/components/Navigator/Navigator.tsx index 0614b2888..90eb55ff1 100644 --- a/src/components/Navigator/Navigator.tsx +++ b/src/components/Navigator/Navigator.tsx @@ -6,9 +6,11 @@ import { useIntl } from "react-intl"; import { getActions, + getCatalog, getCustomers, getViews, hasActions, + hasCatalog, hasCustomers, hasViews } from "./modes/utils"; @@ -107,6 +109,18 @@ const Navigator: React.FC = () => { offset={getItemOffset(actions, [getViews, getActions])} /> )} + {hasCatalog(actions) && ( + + )} )} diff --git a/src/components/Navigator/NavigatorInput.tsx b/src/components/Navigator/NavigatorInput.tsx index 6a3156cb1..74961502b 100644 --- a/src/components/Navigator/NavigatorInput.tsx +++ b/src/components/Navigator/NavigatorInput.tsx @@ -52,7 +52,13 @@ const NavigatorInput = React.forwardRef(
{mode !== "default" && ( - {mode === "orders" ? "#" : mode === "customers" ? "@" : ">"} + {mode === "orders" + ? "#" + : mode === "customers" + ? "@" + : mode === "catalog" + ? "$" + : ">"} )} catalog.categories.edges.map(edge => edge.node), + [] + ).map(category => ({ + caption: intl.formatMessage(messages.category), + label: category.name, + onClick: () => navigate(categoryUrl(category.id)), + score: score(category.name, search), + text: category.name, + type: "catalog" + })); + + const collections: QuickSearchActionInput[] = maybe( + () => catalog.collections.edges.map(edge => edge.node), + [] + ).map(collection => ({ + caption: intl.formatMessage(messages.collection), + label: collection.name, + onClick: () => navigate(collectionUrl(collection.id)), + score: score(collection.name, search), + text: collection.name, + type: "catalog" + })); + + const products: QuickSearchActionInput[] = maybe( + () => catalog.products.edges.map(edge => edge.node), + [] + ).map(product => ({ + caption: intl.formatMessage(messages.product), + label: product.name, + onClick: () => navigate(productUrl(product.id)), + score: score(product.name, search), + text: product.name, + type: "catalog" + })); + + return [...categories, ...collections, ...products]; +} + +function getCatalogModeActions( + query: string, + intl: IntlShape, + navigate: UseNavigatorResult, + catalog: SearchCatalog +): QuickSearchAction[] { + return searchInCatalog(query, intl, navigate, catalog); +} + +export default getCatalogModeActions; diff --git a/src/components/Navigator/modes/index.ts b/src/components/Navigator/modes/index.ts index c26d4c8bf..6acb95daf 100644 --- a/src/components/Navigator/modes/index.ts +++ b/src/components/Navigator/modes/index.ts @@ -4,6 +4,7 @@ import { UseNavigatorResult } from "@saleor/hooks/useNavigator"; import { OrderDraftCreate } from "@saleor/orders/types/OrderDraftCreate"; import { MutationFunction } from "react-apollo"; import { QuickSearchAction, QuickSearchMode } from "../types"; +import getCatalogModeActions from "./catalog"; import getCommandModeActions from "./commands"; import getCustomersModeActions from "./customers"; import getDefaultModeActions from "./default"; @@ -21,6 +22,8 @@ function getModeActions( } ): QuickSearchAction[] { switch (mode) { + case "catalog": + return getCatalogModeActions(query, intl, cbs.navigate, queries.catalog); case "commands": return getCommandModeActions(query, intl, cbs.navigate, cbs.createOrder); case "customers": diff --git a/src/components/Navigator/modes/messages.ts b/src/components/Navigator/modes/messages.ts index edeef81b0..df0881147 100644 --- a/src/components/Navigator/modes/messages.ts +++ b/src/components/Navigator/modes/messages.ts @@ -21,6 +21,14 @@ const messages = defineMessages({ defaultMessage: "Add Voucher", description: "button" }, + category: { + defaultMessage: "Category", + description: "catalog item type" + }, + collection: { + defaultMessage: "Collection", + description: "catalog item type" + }, createOrder: { defaultMessage: "Create Order", description: "button" @@ -31,6 +39,10 @@ const messages = defineMessages({ goToOrder: { defaultMessage: "Go to order #{orderNumber}", description: "navigator action" + }, + product: { + defaultMessage: "Product", + description: "catalog item type" } }); diff --git a/src/components/Navigator/modes/types.ts b/src/components/Navigator/modes/types.ts index b207d7e35..13bab413f 100644 --- a/src/components/Navigator/modes/types.ts +++ b/src/components/Navigator/modes/types.ts @@ -1,7 +1,9 @@ import { SearchCustomers_search_edges_node } from "@saleor/searches/types/SearchCustomers"; import { CheckIfOrderExists_order } from "../queries/types/CheckIfOrderExists"; +import { SearchCatalog } from "../queries/types/SearchCatalog"; export interface ActionQueries { + catalog: SearchCatalog; customers: SearchCustomers_search_edges_node[]; order: CheckIfOrderExists_order; } diff --git a/src/components/Navigator/modes/utils.ts b/src/components/Navigator/modes/utils.ts index c625f0ddd..646647a65 100644 --- a/src/components/Navigator/modes/utils.ts +++ b/src/components/Navigator/modes/utils.ts @@ -22,3 +22,10 @@ export function getCustomers( export function hasCustomers(actions: QuickSearchAction[]): boolean { return getCustomers(actions).length > 0; } + +export function getCatalog(actions: QuickSearchAction[]): QuickSearchAction[] { + return actions.filter(action => action.type === "catalog"); +} +export function hasCatalog(actions: QuickSearchAction[]): boolean { + return getCatalog(actions).length > 0; +} diff --git a/src/components/Navigator/queries/types/SearchCatalog.ts b/src/components/Navigator/queries/types/SearchCatalog.ts new file mode 100644 index 000000000..0fd38ea68 --- /dev/null +++ b/src/components/Navigator/queries/types/SearchCatalog.ts @@ -0,0 +1,75 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL query operation: SearchCatalog +// ==================================================== + +export interface SearchCatalog_categories_edges_node { + __typename: "Category"; + id: string; + name: string; +} + +export interface SearchCatalog_categories_edges { + __typename: "CategoryCountableEdge"; + node: SearchCatalog_categories_edges_node; +} + +export interface SearchCatalog_categories { + __typename: "CategoryCountableConnection"; + edges: SearchCatalog_categories_edges[]; +} + +export interface SearchCatalog_collections_edges_node { + __typename: "Collection"; + id: string; + name: string; + isPublished: boolean; + publicationDate: any | null; +} + +export interface SearchCatalog_collections_edges { + __typename: "CollectionCountableEdge"; + node: SearchCatalog_collections_edges_node; +} + +export interface SearchCatalog_collections { + __typename: "CollectionCountableConnection"; + edges: SearchCatalog_collections_edges[]; +} + +export interface SearchCatalog_products_edges_node_category { + __typename: "Category"; + id: string; + name: string; +} + +export interface SearchCatalog_products_edges_node { + __typename: "Product"; + id: string; + category: SearchCatalog_products_edges_node_category; + name: string; +} + +export interface SearchCatalog_products_edges { + __typename: "ProductCountableEdge"; + node: SearchCatalog_products_edges_node; +} + +export interface SearchCatalog_products { + __typename: "ProductCountableConnection"; + edges: SearchCatalog_products_edges[]; +} + +export interface SearchCatalog { + categories: SearchCatalog_categories | null; + collections: SearchCatalog_collections | null; + products: SearchCatalog_products | null; +} + +export interface SearchCatalogVariables { + first: number; + query: string; +} diff --git a/src/components/Navigator/queries/useCatalogSearch.ts b/src/components/Navigator/queries/useCatalogSearch.ts new file mode 100644 index 000000000..acf80c47d --- /dev/null +++ b/src/components/Navigator/queries/useCatalogSearch.ts @@ -0,0 +1,66 @@ +import gql from "graphql-tag"; +import { useState } from "react"; + +import makeQuery, { UseQueryResult } from "@saleor/hooks/makeQuery"; +import useDebounce from "@saleor/hooks/useDebounce"; +import { SearchCatalog, SearchCatalogVariables } from "./types/SearchCatalog"; + +const searchCatalog = gql` + query SearchCatalog($first: Int!, $query: String!) { + categories(first: $first, filter: { search: $query }) { + edges { + node { + id + name + } + } + } + + collections(first: $first, filter: { search: $query }) { + edges { + node { + id + name + isPublished + publicationDate + } + } + } + + products(first: $first, filter: { search: $query }) { + edges { + node { + id + category { + id + name + } + name + } + } + } + } +`; + +const useSearchCatalogQuery = makeQuery( + searchCatalog +); + +type UseSearchCatalog = [ + UseQueryResult, + (query: string) => void +]; +function useSearchCatalog(first: number): UseSearchCatalog { + const [query, setQuery] = useState(""); + const setQueryDebounced = useDebounce(setQuery); + const result = useSearchCatalogQuery({ + skip: query === "", + variables: { + first, + query + } + }); + + return [result, setQueryDebounced]; +} +export default useSearchCatalog; diff --git a/src/components/Navigator/types.ts b/src/components/Navigator/types.ts index 6a9a505e1..1f7421860 100644 --- a/src/components/Navigator/types.ts +++ b/src/components/Navigator/types.ts @@ -1,4 +1,4 @@ -export type QuickSearchActionType = "action" | "customer" | "view"; +export type QuickSearchActionType = "action" | "catalog" | "customer" | "view"; export interface QuickSearchAction { caption?: string; @@ -14,4 +14,9 @@ export interface QuickSearchActionInput extends QuickSearchAction { text: string; } -export type QuickSearchMode = "default" | "commands" | "orders" | "customers"; +export type QuickSearchMode = + | "default" + | "catalog" + | "commands" + | "orders" + | "customers"; diff --git a/src/components/Navigator/useQuickSearch.ts b/src/components/Navigator/useQuickSearch.ts index 316540fbb..63f9bbc6b 100644 --- a/src/components/Navigator/useQuickSearch.ts +++ b/src/components/Navigator/useQuickSearch.ts @@ -11,6 +11,7 @@ import { orderUrl } from "@saleor/orders/urls"; import useCustomerSearch from "@saleor/searches/useCustomerSearch"; import getModeActions from "./modes"; import { getGqlOrderId, isQueryValidOrderNumber } from "./modes/orders"; +import useSearchCatalog from "./queries/useCatalogSearch"; import useCheckIfOrderExists from "./queries/useCheckIfOrderExists"; import { QuickSearchAction, QuickSearchMode } from "./types"; @@ -35,6 +36,7 @@ function useQuickSearch( first: 5 } }); + const [{ data: catalog }, searchCatalog] = useSearchCatalog(5); const [createOrder] = useOrderDraftCreateMutation({ onCompleted: result => { if (result.draftOrderCreate.errors.length === 0) { @@ -86,6 +88,9 @@ function useQuickSearch( case "# ": setMode("orders"); break; + case "$ ": + setMode("catalog"); + break; default: setQuery(value); } @@ -93,6 +98,9 @@ function useQuickSearch( if (mode === "orders" && isQueryValidOrderNumber(value)) { getOrderData(getGqlOrderId(value)); } + if (mode === "catalog") { + searchCatalog(value); + } setQuery(value); } @@ -110,6 +118,7 @@ function useQuickSearch( query, intl, { + catalog, customers: maybe( () => customers.data.search.edges.map(edge => edge.node), []