Refactor searches

This commit is contained in:
dominik-zeglen 2019-10-15 14:17:35 +02:00
parent 1a58d4ab80
commit d295804778
38 changed files with 409 additions and 254 deletions

View file

@ -299,7 +299,7 @@ export const CollectionDetails: React.StatelessComponent<
})
}
products={maybe(() =>
result.data.products.edges
result.data.search.edges
.map(edge => edge.node)
.filter(suggestedProduct => suggestedProduct.id)
)}

View file

@ -19,11 +19,11 @@ import ConfirmButton, {
import FormSpacer from "@saleor/components/FormSpacer";
import useSearchQuery from "@saleor/hooks/useSearchQuery";
import { buttonMessages } from "@saleor/intl";
import { SearchCategories_categories_edges_node } from "../../containers/SearchCategories/types/SearchCategories";
import { SearchCategories_search_edges_node } from "../../containers/SearchCategories/types/SearchCategories";
import Checkbox from "../Checkbox";
export interface FormData {
categories: SearchCategories_categories_edges_node[];
categories: SearchCategories_search_edges_node[];
query: string;
}
@ -45,22 +45,20 @@ const styles = createStyles({
});
interface AssignCategoriesDialogProps extends WithStyles<typeof styles> {
categories: SearchCategories_categories_edges_node[];
categories: SearchCategories_search_edges_node[];
confirmButtonState: ConfirmButtonTransitionState;
open: boolean;
loading: boolean;
onClose: () => void;
onFetch: (value: string) => void;
onSubmit: (data: SearchCategories_categories_edges_node[]) => void;
onSubmit: (data: SearchCategories_search_edges_node[]) => void;
}
function handleCategoryAssign(
product: SearchCategories_categories_edges_node,
product: SearchCategories_search_edges_node,
isSelected: boolean,
selectedCategories: SearchCategories_categories_edges_node[],
setSelectedCategories: (
data: SearchCategories_categories_edges_node[]
) => void
selectedCategories: SearchCategories_search_edges_node[],
setSelectedCategories: (data: SearchCategories_search_edges_node[]) => void
) {
if (isSelected) {
setSelectedCategories(
@ -89,7 +87,7 @@ const AssignCategoriesDialog = withStyles(styles, {
const intl = useIntl();
const [query, onQueryChange] = useSearchQuery(onFetch);
const [selectedCategories, setSelectedCategories] = React.useState<
SearchCategories_categories_edges_node[]
SearchCategories_search_edges_node[]
>([]);
const handleSubmit = () => onSubmit(selectedCategories);

View file

@ -15,7 +15,7 @@ import { FormattedMessage, useIntl } from "react-intl";
import useSearchQuery from "@saleor/hooks/useSearchQuery";
import { buttonMessages } from "@saleor/intl";
import { SearchCollections_collections_edges_node } from "../../containers/SearchCollections/types/SearchCollections";
import { SearchCollections_search_edges_node } from "../../containers/SearchCollections/types/SearchCollections";
import Checkbox from "../Checkbox";
import ConfirmButton, {
ConfirmButtonTransitionState
@ -23,7 +23,7 @@ import ConfirmButton, {
import FormSpacer from "../FormSpacer";
export interface FormData {
collections: SearchCollections_collections_edges_node[];
collections: SearchCollections_search_edges_node[];
query: string;
}
@ -45,22 +45,20 @@ const styles = createStyles({
});
interface AssignCollectionDialogProps extends WithStyles<typeof styles> {
collections: SearchCollections_collections_edges_node[];
collections: SearchCollections_search_edges_node[];
confirmButtonState: ConfirmButtonTransitionState;
open: boolean;
loading: boolean;
onClose: () => void;
onFetch: (value: string) => void;
onSubmit: (data: SearchCollections_collections_edges_node[]) => void;
onSubmit: (data: SearchCollections_search_edges_node[]) => void;
}
function handleCollectionAssign(
product: SearchCollections_collections_edges_node,
product: SearchCollections_search_edges_node,
isSelected: boolean,
selectedCollections: SearchCollections_collections_edges_node[],
setSelectedCollections: (
data: SearchCollections_collections_edges_node[]
) => void
selectedCollections: SearchCollections_search_edges_node[],
setSelectedCollections: (data: SearchCollections_search_edges_node[]) => void
) {
if (isSelected) {
setSelectedCollections(
@ -89,7 +87,7 @@ const AssignCollectionDialog = withStyles(styles, {
const intl = useIntl();
const [query, onQueryChange] = useSearchQuery(onFetch);
const [selectedCollections, setSelectedCollections] = React.useState<
SearchCollections_collections_edges_node[]
SearchCollections_search_edges_node[]
>([]);
const handleSubmit = () => onSubmit(selectedCollections);

View file

@ -21,11 +21,11 @@ import TableCellAvatar from "@saleor/components/TableCellAvatar";
import useSearchQuery from "@saleor/hooks/useSearchQuery";
import { buttonMessages } from "@saleor/intl";
import { maybe } from "@saleor/misc";
import { SearchProducts_products_edges_node } from "../../containers/SearchProducts/types/SearchProducts";
import { SearchProducts_search_edges_node } from "../../containers/SearchProducts/types/SearchProducts";
import Checkbox from "../Checkbox";
export interface FormData {
products: SearchProducts_products_edges_node[];
products: SearchProducts_search_edges_node[];
query: string;
}
@ -53,18 +53,18 @@ const styles = createStyles({
export interface AssignProductDialogProps {
confirmButtonState: ConfirmButtonTransitionState;
open: boolean;
products: SearchProducts_products_edges_node[];
products: SearchProducts_search_edges_node[];
loading: boolean;
onClose: () => void;
onFetch: (value: string) => void;
onSubmit: (data: SearchProducts_products_edges_node[]) => void;
onSubmit: (data: SearchProducts_search_edges_node[]) => void;
}
function handleProductAssign(
product: SearchProducts_products_edges_node,
product: SearchProducts_search_edges_node,
isSelected: boolean,
selectedProducts: SearchProducts_products_edges_node[],
setSelectedProducts: (data: SearchProducts_products_edges_node[]) => void
selectedProducts: SearchProducts_search_edges_node[],
setSelectedProducts: (data: SearchProducts_search_edges_node[]) => void
) {
if (isSelected) {
setSelectedProducts(
@ -93,7 +93,7 @@ const AssignProductDialog = withStyles(styles, {
const intl = useIntl();
const [query, onQueryChange] = useSearchQuery(onFetch);
const [selectedProducts, setSelectedProducts] = React.useState<
SearchProducts_products_edges_node[]
SearchProducts_search_edges_node[]
>([]);
const handleSubmit = () => onSubmit(selectedProducts);

View file

@ -10,26 +10,30 @@ export interface SearchQueryVariables {
query: string;
}
interface BaseSearchProps<
TQuery,
TQueryVariables extends SearchQueryVariables
> {
children: (props: {
loadMore: () => void;
search: (query: string) => void;
result: TypedQueryResult<TQuery, TQueryVariables>;
}) => React.ReactElement<any>;
variables: TQueryVariables;
}
function BaseSearch<TQuery, TQueryVariables extends SearchQueryVariables>(
query: DocumentNode
query: DocumentNode,
loadMoreFn: (result: TypedQueryResult<TQuery, TQueryVariables>) => void
) {
const Query = TypedQuery<TQuery, TQueryVariables>(query);
interface BaseSearchProps {
children: (props: {
search: (query: string) => void;
result: TypedQueryResult<TQuery, TQueryVariables>;
}) => React.ReactElement<any>;
variables: TQueryVariables;
}
interface BaseSearchState {
query: string;
}
class BaseSearchComponent extends React.Component<
BaseSearchProps,
BaseSearchState
BaseSearchProps<TQuery, TQueryVariables>,
SearchQueryVariables
> {
state: BaseSearchState = {
state: SearchQueryVariables = {
first: this.props.variables.first,
query: this.props.variables.query
};
@ -54,7 +58,13 @@ function BaseSearch<TQuery, TQueryVariables extends SearchQueryVariables>(
query: this.state.query
}}
>
{result => children({ search, result })}
{result =>
children({
loadMore: () => loadMoreFn(result),
result,
search
})
}
</Query>
)}
</Debounce>
@ -63,4 +73,5 @@ function BaseSearch<TQuery, TQueryVariables extends SearchQueryVariables>(
}
return BaseSearchComponent;
}
export default BaseSearch;

View file

@ -1,24 +1,29 @@
import gql from "graphql-tag";
import BaseSearch from "../BaseSearch";
import { pageInfoFragment } from "@saleor/queries";
import TopLevelSearch from "../TopLevelSearch";
import {
SearchCategories,
SearchCategoriesVariables
} from "./types/SearchCategories";
export const searchCategories = gql`
${pageInfoFragment}
query SearchCategories($after: String, $first: Int!, $query: String!) {
categories(after: $after, first: $first, query: $query) {
search: categories(after: $after, first: $first, query: $query) {
edges {
node {
id
name
}
}
pageInfo {
...PageInfoFragment
}
}
}
`;
export default BaseSearch<SearchCategories, SearchCategoriesVariables>(
export default TopLevelSearch<SearchCategories, SearchCategoriesVariables>(
searchCategories
);

View file

@ -6,24 +6,33 @@
// GraphQL query operation: SearchCategories
// ====================================================
export interface SearchCategories_categories_edges_node {
export interface SearchCategories_search_edges_node {
__typename: "Category";
id: string;
name: string;
}
export interface SearchCategories_categories_edges {
export interface SearchCategories_search_edges {
__typename: "CategoryCountableEdge";
node: SearchCategories_categories_edges_node;
node: SearchCategories_search_edges_node;
}
export interface SearchCategories_categories {
export interface SearchCategories_search_pageInfo {
__typename: "PageInfo";
endCursor: string | null;
hasNextPage: boolean;
hasPreviousPage: boolean;
startCursor: string | null;
}
export interface SearchCategories_search {
__typename: "CategoryCountableConnection";
edges: SearchCategories_categories_edges[];
edges: SearchCategories_search_edges[];
pageInfo: SearchCategories_search_pageInfo;
}
export interface SearchCategories {
categories: SearchCategories_categories | null;
search: SearchCategories_search | null;
}
export interface SearchCategoriesVariables {

View file

@ -1,24 +1,29 @@
import gql from "graphql-tag";
import BaseSearch from "../BaseSearch";
import { pageInfoFragment } from "@saleor/queries";
import TopLevelSearch from "../TopLevelSearch";
import {
SearchCollections,
SearchCollectionsVariables
} from "./types/SearchCollections";
export const searchCollections = gql`
${pageInfoFragment}
query SearchCollections($after: String, $first: Int!, $query: String!) {
collections(after: $after, first: $first, query: $query) {
search: collections(after: $after, first: $first, query: $query) {
edges {
node {
id
name
}
}
pageInfo {
...PageInfoFragment
}
}
}
`;
export default BaseSearch<SearchCollections, SearchCollectionsVariables>(
export default TopLevelSearch<SearchCollections, SearchCollectionsVariables>(
searchCollections
);

View file

@ -6,24 +6,33 @@
// GraphQL query operation: SearchCollections
// ====================================================
export interface SearchCollections_collections_edges_node {
export interface SearchCollections_search_edges_node {
__typename: "Collection";
id: string;
name: string;
}
export interface SearchCollections_collections_edges {
export interface SearchCollections_search_edges {
__typename: "CollectionCountableEdge";
node: SearchCollections_collections_edges_node;
node: SearchCollections_search_edges_node;
}
export interface SearchCollections_collections {
export interface SearchCollections_search_pageInfo {
__typename: "PageInfo";
endCursor: string | null;
hasNextPage: boolean;
hasPreviousPage: boolean;
startCursor: string | null;
}
export interface SearchCollections_search {
__typename: "CollectionCountableConnection";
edges: SearchCollections_collections_edges[];
edges: SearchCollections_search_edges[];
pageInfo: SearchCollections_search_pageInfo;
}
export interface SearchCollections {
collections: SearchCollections_collections | null;
search: SearchCollections_search | null;
}
export interface SearchCollectionsVariables {

View file

@ -1,24 +1,29 @@
import gql from "graphql-tag";
import BaseSearch from "../BaseSearch";
import { pageInfoFragment } from "@saleor/queries";
import TopLevelSearch from "../TopLevelSearch";
import {
SearchCustomers,
SearchCustomersVariables
} from "./types/SearchCustomers";
export const searchCustomers = gql`
${pageInfoFragment}
query SearchCustomers($after: String, $first: Int!, $query: String!) {
customers(after: $after, first: $first, query: $query) {
search: customers(after: $after, first: $first, query: $query) {
edges {
node {
id
email
}
}
pageInfo {
...PageInfoFragment
}
}
}
`;
export default BaseSearch<SearchCustomers, SearchCustomersVariables>(
export default TopLevelSearch<SearchCustomers, SearchCustomersVariables>(
searchCustomers
);

View file

@ -6,24 +6,33 @@
// GraphQL query operation: SearchCustomers
// ====================================================
export interface SearchCustomers_customers_edges_node {
export interface SearchCustomers_search_edges_node {
__typename: "User";
id: string;
email: string;
}
export interface SearchCustomers_customers_edges {
export interface SearchCustomers_search_edges {
__typename: "UserCountableEdge";
node: SearchCustomers_customers_edges_node;
node: SearchCustomers_search_edges_node;
}
export interface SearchCustomers_customers {
export interface SearchCustomers_search_pageInfo {
__typename: "PageInfo";
endCursor: string | null;
hasNextPage: boolean;
hasPreviousPage: boolean;
startCursor: string | null;
}
export interface SearchCustomers_search {
__typename: "UserCountableConnection";
edges: SearchCustomers_customers_edges[];
edges: SearchCustomers_search_edges[];
pageInfo: SearchCustomers_search_pageInfo;
}
export interface SearchCustomers {
customers: SearchCustomers_customers | null;
search: SearchCustomers_search | null;
}
export interface SearchCustomersVariables {

View file

@ -1,19 +1,24 @@
import gql from "graphql-tag";
import BaseSearch from "../BaseSearch";
import { pageInfoFragment } from "@saleor/queries";
import TopLevelSearch from "../TopLevelSearch";
import { SearchPages, SearchPagesVariables } from "./types/SearchPages";
export const searchPages = gql`
${pageInfoFragment}
query SearchPages($after: String, $first: Int!, $query: String!) {
pages(after: $after, first: $first, query: $query) {
search: pages(after: $after, first: $first, query: $query) {
edges {
node {
id
title
}
}
pageInfo {
...PageInfoFragment
}
}
}
`;
export default BaseSearch<SearchPages, SearchPagesVariables>(searchPages);
export default TopLevelSearch<SearchPages, SearchPagesVariables>(searchPages);

View file

@ -6,24 +6,33 @@
// GraphQL query operation: SearchPages
// ====================================================
export interface SearchPages_pages_edges_node {
export interface SearchPages_search_edges_node {
__typename: "Page";
id: string;
title: string;
}
export interface SearchPages_pages_edges {
export interface SearchPages_search_edges {
__typename: "PageCountableEdge";
node: SearchPages_pages_edges_node;
node: SearchPages_search_edges_node;
}
export interface SearchPages_pages {
export interface SearchPages_search_pageInfo {
__typename: "PageInfo";
endCursor: string | null;
hasNextPage: boolean;
hasPreviousPage: boolean;
startCursor: string | null;
}
export interface SearchPages_search {
__typename: "PageCountableConnection";
edges: SearchPages_pages_edges[];
edges: SearchPages_search_edges[];
pageInfo: SearchPages_search_pageInfo;
}
export interface SearchPages {
pages: SearchPages_pages | null;
search: SearchPages_search | null;
}
export interface SearchPagesVariables {

View file

@ -1,14 +1,20 @@
import gql from "graphql-tag";
import BaseSearch from "../BaseSearch";
import { pageInfoFragment } from "@saleor/queries";
import TopLevelSearch from "../TopLevelSearch";
import {
SearchProductTypes,
SearchProductTypesVariables
} from "./types/SearchProductTypes";
export const searchProductTypes = gql`
${pageInfoFragment}
query SearchProductTypes($after: String, $first: Int!, $query: String!) {
productTypes(after: $after, first: $first, filter: { search: $query }) {
search: productTypes(
after: $after
first: $first
filter: { search: $query }
) {
edges {
node {
id
@ -28,10 +34,13 @@ export const searchProductTypes = gql`
}
}
}
pageInfo {
...PageInfoFragment
}
}
}
`;
export default BaseSearch<SearchProductTypes, SearchProductTypesVariables>(
export default TopLevelSearch<SearchProductTypes, SearchProductTypesVariables>(
searchProductTypes
);

View file

@ -8,43 +8,52 @@ import { AttributeInputTypeEnum } from "./../../../types/globalTypes";
// GraphQL query operation: SearchProductTypes
// ====================================================
export interface SearchProductTypes_productTypes_edges_node_productAttributes_values {
export interface SearchProductTypes_search_edges_node_productAttributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface SearchProductTypes_productTypes_edges_node_productAttributes {
export interface SearchProductTypes_search_edges_node_productAttributes {
__typename: "Attribute";
id: string;
inputType: AttributeInputTypeEnum | null;
slug: string | null;
name: string | null;
valueRequired: boolean;
values: (SearchProductTypes_productTypes_edges_node_productAttributes_values | null)[] | null;
values: (SearchProductTypes_search_edges_node_productAttributes_values | null)[] | null;
}
export interface SearchProductTypes_productTypes_edges_node {
export interface SearchProductTypes_search_edges_node {
__typename: "ProductType";
id: string;
name: string;
hasVariants: boolean;
productAttributes: (SearchProductTypes_productTypes_edges_node_productAttributes | null)[] | null;
productAttributes: (SearchProductTypes_search_edges_node_productAttributes | null)[] | null;
}
export interface SearchProductTypes_productTypes_edges {
export interface SearchProductTypes_search_edges {
__typename: "ProductTypeCountableEdge";
node: SearchProductTypes_productTypes_edges_node;
node: SearchProductTypes_search_edges_node;
}
export interface SearchProductTypes_productTypes {
export interface SearchProductTypes_search_pageInfo {
__typename: "PageInfo";
endCursor: string | null;
hasNextPage: boolean;
hasPreviousPage: boolean;
startCursor: string | null;
}
export interface SearchProductTypes_search {
__typename: "ProductTypeCountableConnection";
edges: SearchProductTypes_productTypes_edges[];
edges: SearchProductTypes_search_edges[];
pageInfo: SearchProductTypes_search_pageInfo;
}
export interface SearchProductTypes {
productTypes: SearchProductTypes_productTypes | null;
search: SearchProductTypes_search | null;
}
export interface SearchProductTypesVariables {

View file

@ -1,14 +1,16 @@
import gql from "graphql-tag";
import BaseSearch from "../BaseSearch";
import { pageInfoFragment } from "@saleor/queries";
import TopLevelSearch from "../TopLevelSearch";
import {
SearchProducts,
SearchProductsVariables
} from "./types/SearchProducts";
export const searchProducts = gql`
${pageInfoFragment}
query SearchProducts($after: String, $first: Int!, $query: String!) {
products(after: $after, first: $first, query: $query) {
search: products(after: $after, first: $first, query: $query) {
edges {
node {
id
@ -18,10 +20,13 @@ export const searchProducts = gql`
}
}
}
pageInfo {
...PageInfoFragment
}
}
}
`;
export default BaseSearch<SearchProducts, SearchProductsVariables>(
export default TopLevelSearch<SearchProducts, SearchProductsVariables>(
searchProducts
);

View file

@ -6,30 +6,39 @@
// GraphQL query operation: SearchProducts
// ====================================================
export interface SearchProducts_products_edges_node_thumbnail {
export interface SearchProducts_search_edges_node_thumbnail {
__typename: "Image";
url: string;
}
export interface SearchProducts_products_edges_node {
export interface SearchProducts_search_edges_node {
__typename: "Product";
id: string;
name: string;
thumbnail: SearchProducts_products_edges_node_thumbnail | null;
thumbnail: SearchProducts_search_edges_node_thumbnail | null;
}
export interface SearchProducts_products_edges {
export interface SearchProducts_search_edges {
__typename: "ProductCountableEdge";
node: SearchProducts_products_edges_node;
node: SearchProducts_search_edges_node;
}
export interface SearchProducts_products {
export interface SearchProducts_search_pageInfo {
__typename: "PageInfo";
endCursor: string | null;
hasNextPage: boolean;
hasPreviousPage: boolean;
startCursor: string | null;
}
export interface SearchProducts_search {
__typename: "ProductCountableConnection";
edges: SearchProducts_products_edges[];
edges: SearchProducts_search_edges[];
pageInfo: SearchProducts_search_pageInfo;
}
export interface SearchProducts {
products: SearchProducts_products | null;
search: SearchProducts_search | null;
}
export interface SearchProductsVariables {

View file

@ -0,0 +1,47 @@
import { DocumentNode } from "graphql";
import { PageInfoFragment } from "@saleor/types/PageInfoFragment";
import BaseSearch, { SearchQueryVariables } from "./BaseSearch";
export interface SearchQuery {
search: {
edges: Array<{
node: any;
}>;
pageInfo: PageInfoFragment;
};
}
function TopLevelSearch<
TQuery extends SearchQuery,
TQueryVariables extends SearchQueryVariables
>(query: DocumentNode) {
return BaseSearch<TQuery, TQueryVariables>(query, result => {
if (result.data.search.pageInfo.hasNextPage) {
result.loadMore(
(prev, next) => {
if (
prev.search.pageInfo.endCursor === next.search.pageInfo.endCursor
) {
return prev;
}
return {
...prev,
search: {
...prev.search,
edges: [...prev.search.edges, ...next.search.edges],
pageInfo: next.search.pageInfo
}
};
},
{
...result.variables,
after: result.data.search.pageInfo.endCursor
}
);
}
});
}
export default TopLevelSearch;

View file

@ -371,7 +371,7 @@ export const SaleDetails: React.StatelessComponent<SaleDetailsProps> = ({
})
}
products={maybe(() =>
searchProductsOpts.data.products.edges
searchProductsOpts.data.search.edges
.map(edge => edge.node)
.filter(
suggestedProduct => suggestedProduct.id
@ -389,7 +389,7 @@ export const SaleDetails: React.StatelessComponent<SaleDetailsProps> = ({
}) => (
<AssignCategoriesDialog
categories={maybe(() =>
searchCategoriesOpts.data.categories.edges
searchCategoriesOpts.data.search.edges
.map(edge => edge.node)
.filter(
suggestedCategory =>
@ -426,7 +426,7 @@ export const SaleDetails: React.StatelessComponent<SaleDetailsProps> = ({
}) => (
<AssignCollectionDialog
collections={maybe(() =>
searchCollectionsOpts.data.collections.edges
searchCollectionsOpts.data.search.edges
.map(edge => edge.node)
.filter(
suggestedCategory =>

View file

@ -429,7 +429,7 @@ export const VoucherDetails: React.StatelessComponent<VoucherDetailsProps> = ({
}) => (
<AssignCategoriesDialog
categories={maybe(() =>
searchCategoriesOpts.data.categories.edges
searchCategoriesOpts.data.search.edges
.map(edge => edge.node)
.filter(
suggestedCategory =>
@ -466,7 +466,7 @@ export const VoucherDetails: React.StatelessComponent<VoucherDetailsProps> = ({
}) => (
<AssignCollectionDialog
collections={maybe(() =>
searchCollectionsOpts.data.collections.edges
searchCollectionsOpts.data.search.edges
.map(edge => edge.node)
.filter(
suggestedCategory =>
@ -544,7 +544,7 @@ export const VoucherDetails: React.StatelessComponent<VoucherDetailsProps> = ({
})
}
products={maybe(() =>
searchProductsOpts.data.products.edges
searchProductsOpts.data.search.edges
.map(edge => edge.node)
.filter(
suggestedProduct => suggestedProduct.id

View file

@ -14,9 +14,9 @@ import ConfirmButton, {
ConfirmButtonTransitionState
} from "@saleor/components/ConfirmButton";
import FormSpacer from "@saleor/components/FormSpacer";
import { SearchCategories_categories_edges_node } from "@saleor/containers/SearchCategories/types/SearchCategories";
import { SearchCollections_collections_edges_node } from "@saleor/containers/SearchCollections/types/SearchCollections";
import { SearchPages_pages_edges_node } from "@saleor/containers/SearchPages/types/SearchPages";
import { SearchCategories_search_edges_node } from "@saleor/containers/SearchCategories/types/SearchCategories";
import { SearchCollections_search_edges_node } from "@saleor/containers/SearchCollections/types/SearchCollections";
import { SearchPages_search_edges_node } from "@saleor/containers/SearchPages/types/SearchPages";
import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors";
import useModalDialogOpen from "@saleor/hooks/useModalDialogOpen";
import useStateFromProps from "@saleor/hooks/useStateFromProps";
@ -43,9 +43,9 @@ export interface MenuItemDialogProps {
initialDisplayValue?: string;
loading: boolean;
open: boolean;
collections: SearchCollections_collections_edges_node[];
categories: SearchCategories_categories_edges_node[];
pages: SearchPages_pages_edges_node[];
collections: SearchCollections_search_edges_node[];
categories: SearchCategories_search_edges_node[];
pages: SearchPages_search_edges_node[];
onClose: () => void;
onSubmit: (data: MenuItemDialogFormData) => void;
onQueryChange: (query: string) => void;

View file

@ -111,7 +111,7 @@ const MenuDetails: React.FC<MenuDetailsProps> = ({ id, params }) => {
const categories = maybe(
() =>
categorySearch.result.data.categories.edges.map(
categorySearch.result.data.search.edges.map(
edge => edge.node
),
[]
@ -119,7 +119,7 @@ const MenuDetails: React.FC<MenuDetailsProps> = ({ id, params }) => {
const collections = maybe(
() =>
collectionSearch.result.data.collections.edges.map(
collectionSearch.result.data.search.edges.map(
edge => edge.node
),
[]
@ -127,7 +127,7 @@ const MenuDetails: React.FC<MenuDetailsProps> = ({ id, params }) => {
const pages = maybe(
() =>
pageSearch.result.data.pages.edges.map(
pageSearch.result.data.search.edges.map(
edge => edge.node
),
[]

View file

@ -20,8 +20,9 @@ import SingleAutocompleteSelectField from "@saleor/components/SingleAutocomplete
import Skeleton from "@saleor/components/Skeleton";
import useStateFromProps from "@saleor/hooks/useStateFromProps";
import { buttonMessages } from "@saleor/intl";
import { FetchMoreProps } from "@saleor/types";
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
import { SearchCustomers_customers_edges_node } from "../../../containers/SearchCustomers/types/SearchCustomers";
import { SearchCustomers_search_edges_node } from "../../../containers/SearchCustomers/types/SearchCustomers";
import { customerUrl } from "../../../customers/urls";
import { createHref, maybe } from "../../../misc";
import { OrderDetails_order } from "../../types/OrderDetails";
@ -48,9 +49,9 @@ const styles = (theme: Theme) =>
}
});
export interface OrderCustomerProps extends WithStyles<typeof styles> {
export interface OrderCustomerProps extends Partial<FetchMoreProps> {
order: OrderDetails_order;
users?: SearchCustomers_customers_edges_node[];
users?: SearchCustomers_search_edges_node[];
loading?: boolean;
canEditAddresses: boolean;
canEditCustomer: boolean;
@ -67,14 +68,16 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
canEditAddresses,
canEditCustomer,
fetchUsers,
hasMore: hasMoreUsers,
loading,
order,
users,
onCustomerEdit,
onBillingAddressEdit,
onFetchMore: onFetchMoreUsers,
onProfileView,
onShippingAddressEdit
}: OrderCustomerProps) => {
}: OrderCustomerProps & WithStyles<typeof styles>) => {
const intl = useIntl();
const user = maybe(() => order.user);
@ -138,11 +141,13 @@ const OrderCustomer = withStyles(styles, { name: "OrderCustomer" })(
choices={userChoices}
displayValue={userDisplayName}
fetchChoices={fetchUsers}
hasMore={hasMoreUsers}
loading={loading}
placeholder={intl.formatMessage({
defaultMessage: "Search Customers"
})}
onChange={handleUserChange}
onFetchMore={onFetchMoreUsers}
name="query"
value={data.query}
/>

View file

@ -18,7 +18,8 @@ import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar";
import Skeleton from "@saleor/components/Skeleton";
import { sectionNames } from "@saleor/intl";
import { SearchCustomers_customers_edges_node } from "../../../containers/SearchCustomers/types/SearchCustomers";
import { FetchMoreProps } from "@saleor/types";
import { SearchCustomers_search_edges_node } from "../../../containers/SearchCustomers/types/SearchCustomers";
import { maybe } from "../../../misc";
import { DraftOrderInput } from "../../../types/globalTypes";
import { OrderDetails_order } from "../../types/OrderDetails";
@ -38,10 +39,10 @@ const styles = (theme: Theme) =>
}
});
export interface OrderDraftPageProps extends WithStyles<typeof styles> {
export interface OrderDraftPageProps extends FetchMoreProps {
disabled: boolean;
order: OrderDetails_order;
users: SearchCustomers_customers_edges_node[];
users: SearchCustomers_search_edges_node[];
usersLoading: boolean;
countries: Array<{
code: string;
@ -72,12 +73,14 @@ const OrderDraftPage = withStyles(styles, { name: "OrderDraftPage" })(
classes,
disabled,
fetchUsers,
hasMore,
saveButtonBarState,
onBack,
onBillingAddressEdit,
onCustomerEdit,
onDraftFinalize,
onDraftRemove,
onFetchMore,
onNoteAdd,
onOrderLineAdd,
onOrderLineChange,
@ -88,7 +91,7 @@ const OrderDraftPage = withStyles(styles, { name: "OrderDraftPage" })(
order,
users,
usersLoading
}: OrderDraftPageProps) => {
}: OrderDraftPageProps & WithStyles<typeof styles>) => {
const intl = useIntl();
return (
@ -139,14 +142,16 @@ const OrderDraftPage = withStyles(styles, { name: "OrderDraftPage" })(
<OrderCustomer
canEditAddresses={true}
canEditCustomer={true}
fetchUsers={fetchUsers}
hasMore={hasMore}
loading={usersLoading}
order={order}
users={users}
loading={usersLoading}
fetchUsers={fetchUsers}
onBillingAddressEdit={onBillingAddressEdit}
onCustomerEdit={onCustomerEdit}
onShippingAddressEdit={onShippingAddressEdit}
onFetchMore={onFetchMore}
onProfileView={onProfileView}
onShippingAddressEdit={onShippingAddressEdit}
/>
</div>
</Grid>

View file

@ -30,8 +30,8 @@ import { buttonMessages } from "@saleor/intl";
import { maybe, renderCollection } from "@saleor/misc";
import { FetchMoreProps } from "@saleor/types";
import {
SearchOrderVariant_products_edges_node,
SearchOrderVariant_products_edges_node_variants
SearchOrderVariant_search_edges_node,
SearchOrderVariant_search_edges_node_variants
} from "../../types/SearchOrderVariant";
const styles = (theme: Theme) =>
@ -79,21 +79,21 @@ const styles = (theme: Theme) =>
});
type SetVariantsAction = (
data: SearchOrderVariant_products_edges_node_variants[]
data: SearchOrderVariant_search_edges_node_variants[]
) => void;
interface OrderProductAddDialogProps extends FetchMoreProps {
confirmButtonState: ConfirmButtonTransitionState;
open: boolean;
products: SearchOrderVariant_products_edges_node[];
products: SearchOrderVariant_search_edges_node[];
onClose: () => void;
onFetch: (query: string) => void;
onSubmit: (data: SearchOrderVariant_products_edges_node_variants[]) => void;
onSubmit: (data: SearchOrderVariant_search_edges_node_variants[]) => void;
}
function hasAllVariantsSelected(
productVariants: SearchOrderVariant_products_edges_node_variants[],
selectedVariantsToProductsMap: SearchOrderVariant_products_edges_node_variants[]
productVariants: SearchOrderVariant_search_edges_node_variants[],
selectedVariantsToProductsMap: SearchOrderVariant_search_edges_node_variants[]
): boolean {
return productVariants.reduce(
(acc, productVariant) =>
@ -106,8 +106,8 @@ function hasAllVariantsSelected(
}
function isVariantSelected(
variant: SearchOrderVariant_products_edges_node_variants,
selectedVariantsToProductsMap: SearchOrderVariant_products_edges_node_variants[]
variant: SearchOrderVariant_search_edges_node_variants,
selectedVariantsToProductsMap: SearchOrderVariant_search_edges_node_variants[]
): boolean {
return !!selectedVariantsToProductsMap.find(
selectedVariant => selectedVariant.id === variant.id
@ -115,10 +115,10 @@ function isVariantSelected(
}
const onProductAdd = (
product: SearchOrderVariant_products_edges_node,
product: SearchOrderVariant_search_edges_node,
productIndex: number,
productsWithAllVariantsSelected: boolean[],
variants: SearchOrderVariant_products_edges_node_variants[],
variants: SearchOrderVariant_search_edges_node_variants[],
setVariants: SetVariantsAction
) =>
productsWithAllVariantsSelected[productIndex]
@ -141,10 +141,10 @@ const onProductAdd = (
]);
const onVariantAdd = (
variant: SearchOrderVariant_products_edges_node_variants,
variant: SearchOrderVariant_search_edges_node_variants,
variantIndex: number,
productIndex: number,
variants: SearchOrderVariant_products_edges_node_variants[],
variants: SearchOrderVariant_search_edges_node_variants[],
selectedVariantsToProductsMap: boolean[][],
setVariants: SetVariantsAction
) =>
@ -172,7 +172,7 @@ const OrderProductAddDialog = withStyles(styles, {
const intl = useIntl();
const [query, onQueryChange] = useSearchQuery(onFetch);
const [variants, setVariants] = React.useState<
SearchOrderVariant_products_edges_node_variants[]
SearchOrderVariant_search_edges_node_variants[]
>([]);
const selectedVariantsToProductsMap = products

View file

@ -1,5 +1,5 @@
import { MessageDescriptor } from "react-intl";
import { SearchCustomers_customers_edges_node } from "../containers/SearchCustomers/types/SearchCustomers";
import { SearchCustomers_search_edges_node } from "../containers/SearchCustomers/types/SearchCustomers";
import { transformOrderStatus, transformPaymentStatus } from "../misc";
import {
FulfillmentStatus,
@ -11,7 +11,7 @@ import {
import { OrderDetails_order } from "./types/OrderDetails";
import { OrderList_orders_edges_node } from "./types/OrderList";
export const clients: SearchCustomers_customers_edges_node[] = [
export const clients: SearchCustomers_search_edges_node[] = [
{
__typename: "User" as "User",
email: "test.client1@example.com",

View file

@ -1,6 +1,6 @@
import gql from "graphql-tag";
import BaseSearch from "../containers/BaseSearch";
import TopLevelSearch from "../containers/TopLevelSearch";
import { TypedQuery } from "../queries";
import { OrderDetails, OrderDetailsVariables } from "./types/OrderDetails";
import {
@ -286,7 +286,7 @@ export const TypedOrderDetailsQuery = TypedQuery<
export const searchOrderVariant = gql`
query SearchOrderVariant($first: Int!, $query: String!, $after: String) {
products(query: $query, first: $first, after: $after) {
search: products(query: $query, first: $first, after: $after) {
edges {
node {
id
@ -314,7 +314,7 @@ export const searchOrderVariant = gql`
}
}
`;
export const SearchOrderVariant = BaseSearch<
export const SearchOrderVariant = TopLevelSearch<
SearchOrderVariantType,
SearchOrderVariantVariables
>(searchOrderVariant);

View file

@ -6,39 +6,39 @@
// GraphQL query operation: SearchOrderVariant
// ====================================================
export interface SearchOrderVariant_products_edges_node_thumbnail {
export interface SearchOrderVariant_search_edges_node_thumbnail {
__typename: "Image";
url: string;
}
export interface SearchOrderVariant_products_edges_node_variants_price {
export interface SearchOrderVariant_search_edges_node_variants_price {
__typename: "Money";
amount: number;
currency: string;
}
export interface SearchOrderVariant_products_edges_node_variants {
export interface SearchOrderVariant_search_edges_node_variants {
__typename: "ProductVariant";
id: string;
name: string;
sku: string;
price: SearchOrderVariant_products_edges_node_variants_price | null;
price: SearchOrderVariant_search_edges_node_variants_price | null;
}
export interface SearchOrderVariant_products_edges_node {
export interface SearchOrderVariant_search_edges_node {
__typename: "Product";
id: string;
name: string;
thumbnail: SearchOrderVariant_products_edges_node_thumbnail | null;
variants: (SearchOrderVariant_products_edges_node_variants | null)[] | null;
thumbnail: SearchOrderVariant_search_edges_node_thumbnail | null;
variants: (SearchOrderVariant_search_edges_node_variants | null)[] | null;
}
export interface SearchOrderVariant_products_edges {
export interface SearchOrderVariant_search_edges {
__typename: "ProductCountableEdge";
node: SearchOrderVariant_products_edges_node;
node: SearchOrderVariant_search_edges_node;
}
export interface SearchOrderVariant_products_pageInfo {
export interface SearchOrderVariant_search_pageInfo {
__typename: "PageInfo";
endCursor: string | null;
hasNextPage: boolean;
@ -46,14 +46,14 @@ export interface SearchOrderVariant_products_pageInfo {
startCursor: string | null;
}
export interface SearchOrderVariant_products {
export interface SearchOrderVariant_search {
__typename: "ProductCountableConnection";
edges: SearchOrderVariant_products_edges[];
pageInfo: SearchOrderVariant_products_pageInfo;
edges: SearchOrderVariant_search_edges[];
pageInfo: SearchOrderVariant_search_pageInfo;
}
export interface SearchOrderVariant {
products: SearchOrderVariant_products | null;
search: SearchOrderVariant_search | null;
}
export interface SearchOrderVariantVariables {

View file

@ -93,7 +93,11 @@ export const OrderDetails: React.StatelessComponent<OrderDetailsProps> = ({
);
return (
<SearchCustomers variables={DEFAULT_INITIAL_SEARCH_DATA}>
{({ search: searchUsers, result: users }) => (
{({
loadMore: loadMoreCustomers,
search: searchUsers,
result: users
}) => (
<OrderDetailsMessages>
{orderMessages => (
<OrderOperations
@ -400,12 +404,18 @@ export const OrderDetails: React.StatelessComponent<OrderDetailsProps> = ({
}
users={maybe(
() =>
users.data.customers.edges.map(
users.data.search.edges.map(
edge => edge.node
),
[]
)}
hasMore={maybe(
() => users.data.search.pageInfo.hasNextPage,
false
)}
onFetchMore={loadMoreCustomers}
fetchUsers={searchUsers}
loading={users.loading}
usersLoading={users.loading}
onCustomerEdit={data =>
orderDraftUpdate.mutate({
@ -511,74 +521,46 @@ export const OrderDetails: React.StatelessComponent<OrderDetailsProps> = ({
variables={DEFAULT_INITIAL_SEARCH_DATA}
>
{({
loadMore,
search: variantSearch,
result: variantSearchOpts
}) => {
const fetchMore = () =>
variantSearchOpts.loadMore(
(prev, next) => {
if (
prev.products.pageInfo.endCursor ===
next.products.pageInfo.endCursor
) {
return prev;
}
return {
...prev,
products: {
...prev.products,
edges: [
...prev.products.edges,
...next.products.edges
],
pageInfo: next.products.pageInfo
}
};
},
{
after:
variantSearchOpts.data.products
.pageInfo.endCursor
}
);
return (
<OrderProductAddDialog
confirmButtonState={getMutationState(
orderLinesAdd.opts.called,
orderLinesAdd.opts.loading,
maybe(
() =>
orderLinesAdd.opts.data
.draftOrderLinesCreate.errors
)
)}
loading={variantSearchOpts.loading}
open={params.action === "add-order-line"}
hasMore={maybe(
}) => (
<OrderProductAddDialog
confirmButtonState={getMutationState(
orderLinesAdd.opts.called,
orderLinesAdd.opts.loading,
maybe(
() =>
variantSearchOpts.data.products
.pageInfo.hasNextPage
)}
products={maybe(() =>
variantSearchOpts.data.products.edges.map(
edge => edge.node
)
)}
onClose={closeModal}
onFetch={variantSearch}
onFetchMore={fetchMore}
onSubmit={variants =>
orderLinesAdd.mutate({
id,
input: variants.map(variant => ({
quantity: 1,
variantId: variant.id
}))
})
}
/>
);
}}
orderLinesAdd.opts.data
.draftOrderLinesCreate.errors
)
)}
loading={variantSearchOpts.loading}
open={params.action === "add-order-line"}
hasMore={maybe(
() =>
variantSearchOpts.data.search.pageInfo
.hasNextPage
)}
products={maybe(() =>
variantSearchOpts.data.search.edges.map(
edge => edge.node
)
)}
onClose={closeModal}
onFetch={variantSearch}
onFetchMore={loadMore}
onSubmit={variants =>
orderLinesAdd.mutate({
id,
input: variants.map(variant => ({
quantity: 1,
variantId: variant.id
}))
})
}
/>
)}
</SearchOrderVariant>
</>
)}

View file

@ -38,5 +38,25 @@ export const searchAttributes = gql`
`;
export default BaseSearch<SearchAttributes, SearchAttributesVariables>(
searchAttributes
searchAttributes,
result =>
result.loadMore(
(prev, next) => ({
...prev,
productType: {
...prev.productType,
availableAttributes: {
...prev.productType.availableAttributes,
edges: [
...prev.productType.availableAttributes.edges,
...next.productType.availableAttributes.edges
],
pageInfo: next.productType.availableAttributes.pageInfo
}
}
}),
{
after: result.data.productType.availableAttributes.pageInfo.endCursor
}
)
);

View file

@ -1,12 +1,12 @@
import {
SearchProductTypes_productTypes_edges_node,
SearchProductTypes_productTypes_edges_node_productAttributes
SearchProductTypes_search_edges_node,
SearchProductTypes_search_edges_node_productAttributes
} from "@saleor/containers/SearchProductTypes/types/SearchProductTypes";
import { AttributeInputTypeEnum } from "../types/globalTypes";
import { ProductTypeDetails_productType } from "./types/ProductTypeDetails";
import { ProductTypeList_productTypes_edges_node } from "./types/ProductTypeList";
export const attributes: SearchProductTypes_productTypes_edges_node_productAttributes[] = [
export const attributes: SearchProductTypes_search_edges_node_productAttributes[] = [
{
node: {
__typename: "Attribute" as "Attribute",
@ -469,8 +469,7 @@ export const attributes: SearchProductTypes_productTypes_edges_node_productAttri
].map(edge => edge.node);
export const productTypes: Array<
SearchProductTypes_productTypes_edges_node &
ProductTypeList_productTypes_edges_node
SearchProductTypes_search_edges_node & ProductTypeList_productTypes_edges_node
> = [
{
__typename: "ProductType" as "ProductType",

View file

@ -13,9 +13,9 @@ import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar";
import SeoForm from "@saleor/components/SeoForm";
import VisibilityCard from "@saleor/components/VisibilityCard";
import { SearchCategories_categories_edges_node } from "@saleor/containers/SearchCategories/types/SearchCategories";
import { SearchCollections_collections_edges_node } from "@saleor/containers/SearchCollections/types/SearchCollections";
import { SearchProductTypes_productTypes_edges_node_productAttributes } from "@saleor/containers/SearchProductTypes/types/SearchProductTypes";
import { SearchCategories_search_edges_node } from "@saleor/containers/SearchCategories/types/SearchCategories";
import { SearchCollections_search_edges_node } from "@saleor/containers/SearchCollections/types/SearchCollections";
import { SearchProductTypes_search_edges_node_productAttributes } from "@saleor/containers/SearchProductTypes/types/SearchProductTypes";
import useDateLocalize from "@saleor/hooks/useDateLocalize";
import useFormset from "@saleor/hooks/useFormset";
import useStateFromProps from "@saleor/hooks/useStateFromProps";
@ -63,15 +63,15 @@ export interface ProductCreatePageSubmitData extends FormData {
interface ProductCreatePageProps {
errors: UserError[];
collections: SearchCollections_collections_edges_node[];
categories: SearchCategories_categories_edges_node[];
collections: SearchCollections_search_edges_node[];
categories: SearchCategories_search_edges_node[];
currency: string;
disabled: boolean;
productTypes?: Array<{
id: string;
name: string;
hasVariants: boolean;
productAttributes: SearchProductTypes_productTypes_edges_node_productAttributes[];
productAttributes: SearchProductTypes_search_edges_node_productAttributes[];
}>;
header: string;
saveButtonBarState: ConfirmButtonTransitionState;

View file

@ -12,8 +12,8 @@ import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar";
import SeoForm from "@saleor/components/SeoForm";
import VisibilityCard from "@saleor/components/VisibilityCard";
import { SearchCategories_categories_edges_node } from "@saleor/containers/SearchCategories/types/SearchCategories";
import { SearchCollections_collections_edges_node } from "@saleor/containers/SearchCollections/types/SearchCollections";
import { SearchCategories_search_edges_node } from "@saleor/containers/SearchCategories/types/SearchCategories";
import { SearchCollections_search_edges_node } from "@saleor/containers/SearchCollections/types/SearchCollections";
import useDateLocalize from "@saleor/hooks/useDateLocalize";
import useFormset from "@saleor/hooks/useFormset";
import useStateFromProps from "@saleor/hooks/useStateFromProps";
@ -50,8 +50,8 @@ import ProductVariants from "../ProductVariants";
export interface ProductUpdatePageProps extends ListActions {
errors: UserError[];
placeholderImage: string;
collections: SearchCollections_collections_edges_node[];
categories: SearchCategories_categories_edges_node[];
collections: SearchCollections_search_edges_node[];
categories: SearchCategories_search_edges_node[];
disabled: boolean;
variants: ProductDetails_product_variants[];
images: ProductDetails_product_images[];

View file

@ -2,7 +2,7 @@ import { RawDraftContentState } from "draft-js";
import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField";
import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField";
import { SearchProductTypes_productTypes_edges_node_productAttributes } from "@saleor/containers/SearchProductTypes/types/SearchProductTypes";
import { SearchProductTypes_search_edges_node_productAttributes } from "@saleor/containers/SearchProductTypes/types/SearchProductTypes";
import { maybe } from "@saleor/misc";
import {
ProductDetails_product,
@ -35,7 +35,7 @@ export interface ProductType {
hasVariants: boolean;
id: string;
name: string;
productAttributes: SearchProductTypes_productTypes_edges_node_productAttributes[];
productAttributes: SearchProductTypes_search_edges_node_productAttributes[];
}
export function getAttributeInputFromProduct(

View file

@ -121,11 +121,11 @@ export const ProductUpdate: React.StatelessComponent<
<ProductCreatePage
currency={maybe(() => shop.defaultCurrency)}
categories={maybe(
() => searchCategoryOpts.data.categories.edges,
() => searchCategoryOpts.data.search.edges,
[]
).map(edge => edge.node)}
collections={maybe(
() => searchCollectionOpts.data.collections.edges,
() => searchCollectionOpts.data.search.edges,
[]
).map(edge => edge.node)}
disabled={productCreateDataLoading}
@ -141,7 +141,7 @@ export const ProductUpdate: React.StatelessComponent<
description: "page header"
})}
productTypes={maybe(() =>
searchProductTypesOpts.data.productTypes.edges.map(
searchProductTypesOpts.data.search.edges.map(
edge => edge.node
)
)}

View file

@ -228,11 +228,11 @@ export const ProductUpdate: React.StatelessComponent<ProductUpdateProps> = ({
);
const categories = maybe(
() => searchCategoriesOpts.data.categories.edges,
() => searchCategoriesOpts.data.search.edges,
[]
).map(edge => edge.node);
const collections = maybe(
() => searchCollectionsOpts.data.collections.edges,
() => searchCollectionsOpts.data.search.edges,
[]
).map(edge => edge.node);
const errors = maybe(

View file

@ -15,7 +15,7 @@ import { RequireAtLeastOne } from "./misc";
export interface LoadMore<TData, TVariables> {
loadMore: (
mergeFunc: (prev: TData, next: TData) => TData,
extraVariables: RequireAtLeastOne<TVariables>
extraVariables: Partial<TVariables>
) => Promise<ApolloQueryResult<TData>>;
}

View file

@ -3,6 +3,7 @@ import { storiesOf } from "@storybook/react";
import React from "react";
import placeholderImage from "@assets/images/placeholder60x60.png";
import { fetchMoreProps } from "@saleor/fixtures";
import OrderDraftPage, {
OrderDraftPageProps
} from "../../../orders/components/OrderDraftPage";
@ -12,6 +13,7 @@ import Decorator from "../../Decorator";
const order = draftOrder(placeholderImage);
const props: Omit<OrderDraftPageProps, "classes"> = {
...fetchMoreProps,
countries,
disabled: false,
fetchUsers: () => undefined,