diff --git a/src/components/Navigator/Navigator.tsx b/src/components/Navigator/Navigator.tsx index c8cd11f83..0614b2888 100644 --- a/src/components/Navigator/Navigator.tsx +++ b/src/components/Navigator/Navigator.tsx @@ -4,7 +4,14 @@ import hotkeys from "hotkeys-js"; import React from "react"; import { useIntl } from "react-intl"; -import { getActions, getViews, hasActions, hasViews } from "./modes/utils"; +import { + getActions, + getCustomers, + getViews, + hasActions, + hasCustomers, + hasViews +} from "./modes/utils"; import NavigatorInput from "./NavigatorInput"; import NavigatorSection from "./NavigatorSection"; import { QuickSearchAction } from "./types"; @@ -12,6 +19,13 @@ import useQuickSearch from "./useQuickSearch"; const navigatorHotkey = "ctrl+m, command+m"; +function getItemOffset( + actions: QuickSearchAction[], + cbs: Array +): number { + return cbs.reduce((acc, cb) => cb(actions).length + acc, 0); +} + const Navigator: React.FC = () => { const [visible, setVisible] = React.useState(false); const input = React.useRef(null); @@ -78,7 +92,19 @@ const Navigator: React.FC = () => { getItemProps={getItemProps} highlightedIndex={highlightedIndex} items={getActions(actions)} - offset={getViews(actions).length} + offset={getItemOffset(actions, [getViews])} + /> + )} + {hasCustomers(actions) && ( + )} diff --git a/src/components/Navigator/NavigatorInput.tsx b/src/components/Navigator/NavigatorInput.tsx index f32c90f9b..6a3156cb1 100644 --- a/src/components/Navigator/NavigatorInput.tsx +++ b/src/components/Navigator/NavigatorInput.tsx @@ -52,7 +52,7 @@ const NavigatorInput = React.forwardRef(
{mode !== "default" && ( - {mode === "orders" ? "#" : ">"} + {mode === "orders" ? "#" : mode === "customers" ? "@" : ">"} )} ({ + label: + customer.firstName && customer.lastName + ? intl.formatMessage(messages.customerWithName, { + firstName: customer.firstName, + lastName: customer.lastName + }) + : customer.email, + onClick: () => navigate(customerUrl(customer.id)), + score: 1, + type: "customer" + })); +} + +function getCustomersModeActions( + intl: IntlShape, + navigate: UseNavigatorResult, + customers: SearchCustomers_search_edges_node[] +): QuickSearchAction[] { + return searchInCustomers(intl, navigate, customers); +} + +export default getCustomersModeActions; diff --git a/src/components/Navigator/modes/default/default.ts b/src/components/Navigator/modes/default/default.ts index d56a44e79..8ed15741a 100644 --- a/src/components/Navigator/modes/default/default.ts +++ b/src/components/Navigator/modes/default/default.ts @@ -3,8 +3,10 @@ import { IntlShape } from "react-intl"; import { UseNavigatorResult } from "@saleor/hooks/useNavigator"; import { OrderDraftCreate } from "@saleor/orders/types/OrderDraftCreate"; +import { SearchCustomers_search_edges_node } from "@saleor/searches/types/SearchCustomers"; import { QuickSearchAction } from "../../types"; import { searchInCommands } from "../commands"; +import { searchInCustomers } from "../customers"; import searchInViews from "./views"; const threshold = 0.05; @@ -14,15 +16,22 @@ function getDefaultModeActions( query: string, intl: IntlShape, navigate: UseNavigatorResult, + customers: SearchCustomers_search_edges_node[], createOrder: MutationFunction ): QuickSearchAction[] { - return [ + const actions = [ ...searchInViews(query, intl, navigate), ...searchInCommands(query, intl, navigate, createOrder) ] .filter(action => action.score >= threshold) .sort((a, b) => (a.score <= b.score ? 1 : -1)) .slice(0, maxActions); + + if (query !== "") { + return [...actions, ...searchInCustomers(intl, navigate, customers)]; + } + + return actions; } export default getDefaultModeActions; diff --git a/src/components/Navigator/modes/index.ts b/src/components/Navigator/modes/index.ts index 4bd08f23d..c26d4c8bf 100644 --- a/src/components/Navigator/modes/index.ts +++ b/src/components/Navigator/modes/index.ts @@ -5,6 +5,7 @@ import { OrderDraftCreate } from "@saleor/orders/types/OrderDraftCreate"; import { MutationFunction } from "react-apollo"; import { QuickSearchAction, QuickSearchMode } from "../types"; import getCommandModeActions from "./commands"; +import getCustomersModeActions from "./customers"; import getDefaultModeActions from "./default"; import getOrdersModeActions from "./orders"; import { ActionQueries } from "./types"; @@ -22,10 +23,18 @@ function getModeActions( switch (mode) { case "commands": return getCommandModeActions(query, intl, cbs.navigate, cbs.createOrder); + case "customers": + return getCustomersModeActions(intl, cbs.navigate, queries.customers); case "orders": return getOrdersModeActions(query, intl, cbs.navigate, queries.order); default: - return getDefaultModeActions(query, intl, cbs.navigate, cbs.createOrder); + return getDefaultModeActions( + query, + intl, + cbs.navigate, + queries.customers, + cbs.createOrder + ); } } diff --git a/src/components/Navigator/modes/messages.ts b/src/components/Navigator/modes/messages.ts index 07c6df3d0..edeef81b0 100644 --- a/src/components/Navigator/modes/messages.ts +++ b/src/components/Navigator/modes/messages.ts @@ -25,6 +25,9 @@ const messages = defineMessages({ defaultMessage: "Create Order", description: "button" }, + customerWithName: { + defaultMessage: "{firstName} {lastName}" + }, goToOrder: { defaultMessage: "Go to order #{orderNumber}", description: "navigator action" diff --git a/src/components/Navigator/modes/types.ts b/src/components/Navigator/modes/types.ts index ad61899c2..b207d7e35 100644 --- a/src/components/Navigator/modes/types.ts +++ b/src/components/Navigator/modes/types.ts @@ -1,5 +1,7 @@ +import { SearchCustomers_search_edges_node } from "@saleor/searches/types/SearchCustomers"; import { CheckIfOrderExists_order } from "../queries/types/CheckIfOrderExists"; export interface ActionQueries { + 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 ceabc81ca..c625f0ddd 100644 --- a/src/components/Navigator/modes/utils.ts +++ b/src/components/Navigator/modes/utils.ts @@ -13,3 +13,12 @@ export function getViews(actions: QuickSearchAction[]): QuickSearchAction[] { export function hasViews(actions: QuickSearchAction[]): boolean { return getViews(actions).length > 0; } + +export function getCustomers( + actions: QuickSearchAction[] +): QuickSearchAction[] { + return actions.filter(action => action.type === "customer"); +} +export function hasCustomers(actions: QuickSearchAction[]): boolean { + return getCustomers(actions).length > 0; +} diff --git a/src/components/Navigator/types.ts b/src/components/Navigator/types.ts index 12279daf5..ddfab5999 100644 --- a/src/components/Navigator/types.ts +++ b/src/components/Navigator/types.ts @@ -1,4 +1,4 @@ -export type QuickSearchActionType = "action" | "view"; +export type QuickSearchActionType = "action" | "customer" | "view"; export interface QuickSearchAction { label: string; diff --git a/src/components/Navigator/useQuickSearch.ts b/src/components/Navigator/useQuickSearch.ts index fee527cf5..316540fbb 100644 --- a/src/components/Navigator/useQuickSearch.ts +++ b/src/components/Navigator/useQuickSearch.ts @@ -1,12 +1,14 @@ import { RefObject, useEffect, useState } from "react"; import { useIntl } from "react-intl"; +import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; import { ChangeEvent, FormChange } from "@saleor/hooks/useForm"; import useModalDialogOpen from "@saleor/hooks/useModalDialogOpen"; import useNavigator from "@saleor/hooks/useNavigator"; import { maybe } from "@saleor/misc"; import { useOrderDraftCreateMutation } from "@saleor/orders/mutations"; import { orderUrl } from "@saleor/orders/urls"; +import useCustomerSearch from "@saleor/searches/useCustomerSearch"; import getModeActions from "./modes"; import { getGqlOrderId, isQueryValidOrderNumber } from "./modes/orders"; import useCheckIfOrderExists from "./queries/useCheckIfOrderExists"; @@ -27,6 +29,12 @@ function useQuickSearch( const intl = useIntl(); const navigate = useNavigator(); const [{ data: orderData }, getOrderData] = useCheckIfOrderExists(); + const { result: customers, search: searchCustomers } = useCustomerSearch({ + variables: { + ...DEFAULT_INITIAL_SEARCH_DATA, + first: 5 + } + }); const [createOrder] = useOrderDraftCreateMutation({ onCompleted: result => { if (result.draftOrderCreate.errors.length === 0) { @@ -72,6 +80,9 @@ function useQuickSearch( case "> ": setMode("commands"); break; + case "@ ": + setMode("customers"); + break; case "# ": setMode("orders"); break; @@ -84,6 +95,10 @@ function useQuickSearch( } setQuery(value); } + + if ((["customers", "default"] as QuickSearchMode[]).includes(mode)) { + searchCustomers(value); + } }; return [ @@ -95,6 +110,10 @@ function useQuickSearch( query, intl, { + customers: maybe( + () => customers.data.search.edges.map(edge => edge.node), + [] + ), order: maybe(() => orderData.order) }, { diff --git a/src/orders/fixtures.ts b/src/orders/fixtures.ts index 4463ec60c..754d8628d 100644 --- a/src/orders/fixtures.ts +++ b/src/orders/fixtures.ts @@ -15,22 +15,30 @@ export const clients: SearchCustomers_search_edges_node[] = [ { __typename: "User" as "User", email: "test.client1@example.com", - id: "c1" + firstName: "John", + id: "c1", + lastName: "Doe" }, { __typename: "User" as "User", email: "test.client2@example.com", - id: "c2" + firstName: "Dough", + id: "c2", + lastName: "Jones" }, { __typename: "User" as "User", email: "test.client3@example.com", - id: "c3" + firstName: "Jonas", + id: "c3", + lastName: "Dough" }, { __typename: "User" as "User", email: "test.client4@example.com", - id: "c4" + firstName: "Bill", + id: "c4", + lastName: "Jonas" } ]; export const orders: OrderList_orders_edges_node[] = [ diff --git a/src/searches/types/SearchCustomers.ts b/src/searches/types/SearchCustomers.ts index c63a1a4b6..37c864c0b 100644 --- a/src/searches/types/SearchCustomers.ts +++ b/src/searches/types/SearchCustomers.ts @@ -10,6 +10,8 @@ export interface SearchCustomers_search_edges_node { __typename: "User"; id: string; email: string; + firstName: string; + lastName: string; } export interface SearchCustomers_search_edges { diff --git a/src/searches/useCustomerSearch.ts b/src/searches/useCustomerSearch.ts index bd046f0ab..92f4165d9 100644 --- a/src/searches/useCustomerSearch.ts +++ b/src/searches/useCustomerSearch.ts @@ -15,6 +15,8 @@ export const searchCustomers = gql` node { id email + firstName + lastName } } pageInfo {