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(() => products={maybe(() =>
result.data.products.edges result.data.search.edges
.map(edge => edge.node) .map(edge => edge.node)
.filter(suggestedProduct => suggestedProduct.id) .filter(suggestedProduct => suggestedProduct.id)
)} )}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -6,24 +6,33 @@
// GraphQL query operation: SearchCategories // GraphQL query operation: SearchCategories
// ==================================================== // ====================================================
export interface SearchCategories_categories_edges_node { export interface SearchCategories_search_edges_node {
__typename: "Category"; __typename: "Category";
id: string; id: string;
name: string; name: string;
} }
export interface SearchCategories_categories_edges { export interface SearchCategories_search_edges {
__typename: "CategoryCountableEdge"; __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"; __typename: "CategoryCountableConnection";
edges: SearchCategories_categories_edges[]; edges: SearchCategories_search_edges[];
pageInfo: SearchCategories_search_pageInfo;
} }
export interface SearchCategories { export interface SearchCategories {
categories: SearchCategories_categories | null; search: SearchCategories_search | null;
} }
export interface SearchCategoriesVariables { export interface SearchCategoriesVariables {

View file

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

View file

@ -6,24 +6,33 @@
// GraphQL query operation: SearchCollections // GraphQL query operation: SearchCollections
// ==================================================== // ====================================================
export interface SearchCollections_collections_edges_node { export interface SearchCollections_search_edges_node {
__typename: "Collection"; __typename: "Collection";
id: string; id: string;
name: string; name: string;
} }
export interface SearchCollections_collections_edges { export interface SearchCollections_search_edges {
__typename: "CollectionCountableEdge"; __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"; __typename: "CollectionCountableConnection";
edges: SearchCollections_collections_edges[]; edges: SearchCollections_search_edges[];
pageInfo: SearchCollections_search_pageInfo;
} }
export interface SearchCollections { export interface SearchCollections {
collections: SearchCollections_collections | null; search: SearchCollections_search | null;
} }
export interface SearchCollectionsVariables { export interface SearchCollectionsVariables {

View file

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

View file

@ -6,24 +6,33 @@
// GraphQL query operation: SearchCustomers // GraphQL query operation: SearchCustomers
// ==================================================== // ====================================================
export interface SearchCustomers_customers_edges_node { export interface SearchCustomers_search_edges_node {
__typename: "User"; __typename: "User";
id: string; id: string;
email: string; email: string;
} }
export interface SearchCustomers_customers_edges { export interface SearchCustomers_search_edges {
__typename: "UserCountableEdge"; __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"; __typename: "UserCountableConnection";
edges: SearchCustomers_customers_edges[]; edges: SearchCustomers_search_edges[];
pageInfo: SearchCustomers_search_pageInfo;
} }
export interface SearchCustomers { export interface SearchCustomers {
customers: SearchCustomers_customers | null; search: SearchCustomers_search | null;
} }
export interface SearchCustomersVariables { export interface SearchCustomersVariables {

View file

@ -1,19 +1,24 @@
import gql from "graphql-tag"; import gql from "graphql-tag";
import BaseSearch from "../BaseSearch"; import { pageInfoFragment } from "@saleor/queries";
import TopLevelSearch from "../TopLevelSearch";
import { SearchPages, SearchPagesVariables } from "./types/SearchPages"; import { SearchPages, SearchPagesVariables } from "./types/SearchPages";
export const searchPages = gql` export const searchPages = gql`
${pageInfoFragment}
query SearchPages($after: String, $first: Int!, $query: String!) { query SearchPages($after: String, $first: Int!, $query: String!) {
pages(after: $after, first: $first, query: $query) { search: pages(after: $after, first: $first, query: $query) {
edges { edges {
node { node {
id id
title 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 // GraphQL query operation: SearchPages
// ==================================================== // ====================================================
export interface SearchPages_pages_edges_node { export interface SearchPages_search_edges_node {
__typename: "Page"; __typename: "Page";
id: string; id: string;
title: string; title: string;
} }
export interface SearchPages_pages_edges { export interface SearchPages_search_edges {
__typename: "PageCountableEdge"; __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"; __typename: "PageCountableConnection";
edges: SearchPages_pages_edges[]; edges: SearchPages_search_edges[];
pageInfo: SearchPages_search_pageInfo;
} }
export interface SearchPages { export interface SearchPages {
pages: SearchPages_pages | null; search: SearchPages_search | null;
} }
export interface SearchPagesVariables { export interface SearchPagesVariables {

View file

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

View file

@ -8,43 +8,52 @@ import { AttributeInputTypeEnum } from "./../../../types/globalTypes";
// GraphQL query operation: SearchProductTypes // GraphQL query operation: SearchProductTypes
// ==================================================== // ====================================================
export interface SearchProductTypes_productTypes_edges_node_productAttributes_values { export interface SearchProductTypes_search_edges_node_productAttributes_values {
__typename: "AttributeValue"; __typename: "AttributeValue";
id: string; id: string;
name: string | null; name: string | null;
slug: string | null; slug: string | null;
} }
export interface SearchProductTypes_productTypes_edges_node_productAttributes { export interface SearchProductTypes_search_edges_node_productAttributes {
__typename: "Attribute"; __typename: "Attribute";
id: string; id: string;
inputType: AttributeInputTypeEnum | null; inputType: AttributeInputTypeEnum | null;
slug: string | null; slug: string | null;
name: string | null; name: string | null;
valueRequired: boolean; 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"; __typename: "ProductType";
id: string; id: string;
name: string; name: string;
hasVariants: boolean; 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"; __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"; __typename: "ProductTypeCountableConnection";
edges: SearchProductTypes_productTypes_edges[]; edges: SearchProductTypes_search_edges[];
pageInfo: SearchProductTypes_search_pageInfo;
} }
export interface SearchProductTypes { export interface SearchProductTypes {
productTypes: SearchProductTypes_productTypes | null; search: SearchProductTypes_search | null;
} }
export interface SearchProductTypesVariables { export interface SearchProductTypesVariables {

View file

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

View file

@ -6,30 +6,39 @@
// GraphQL query operation: SearchProducts // GraphQL query operation: SearchProducts
// ==================================================== // ====================================================
export interface SearchProducts_products_edges_node_thumbnail { export interface SearchProducts_search_edges_node_thumbnail {
__typename: "Image"; __typename: "Image";
url: string; url: string;
} }
export interface SearchProducts_products_edges_node { export interface SearchProducts_search_edges_node {
__typename: "Product"; __typename: "Product";
id: string; id: string;
name: 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"; __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"; __typename: "ProductCountableConnection";
edges: SearchProducts_products_edges[]; edges: SearchProducts_search_edges[];
pageInfo: SearchProducts_search_pageInfo;
} }
export interface SearchProducts { export interface SearchProducts {
products: SearchProducts_products | null; search: SearchProducts_search | null;
} }
export interface SearchProductsVariables { 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(() => products={maybe(() =>
searchProductsOpts.data.products.edges searchProductsOpts.data.search.edges
.map(edge => edge.node) .map(edge => edge.node)
.filter( .filter(
suggestedProduct => suggestedProduct.id suggestedProduct => suggestedProduct.id
@ -389,7 +389,7 @@ export const SaleDetails: React.StatelessComponent<SaleDetailsProps> = ({
}) => ( }) => (
<AssignCategoriesDialog <AssignCategoriesDialog
categories={maybe(() => categories={maybe(() =>
searchCategoriesOpts.data.categories.edges searchCategoriesOpts.data.search.edges
.map(edge => edge.node) .map(edge => edge.node)
.filter( .filter(
suggestedCategory => suggestedCategory =>
@ -426,7 +426,7 @@ export const SaleDetails: React.StatelessComponent<SaleDetailsProps> = ({
}) => ( }) => (
<AssignCollectionDialog <AssignCollectionDialog
collections={maybe(() => collections={maybe(() =>
searchCollectionsOpts.data.collections.edges searchCollectionsOpts.data.search.edges
.map(edge => edge.node) .map(edge => edge.node)
.filter( .filter(
suggestedCategory => suggestedCategory =>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
import { MessageDescriptor } from "react-intl"; 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 { transformOrderStatus, transformPaymentStatus } from "../misc";
import { import {
FulfillmentStatus, FulfillmentStatus,
@ -11,7 +11,7 @@ import {
import { OrderDetails_order } from "./types/OrderDetails"; import { OrderDetails_order } from "./types/OrderDetails";
import { OrderList_orders_edges_node } from "./types/OrderList"; 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", __typename: "User" as "User",
email: "test.client1@example.com", email: "test.client1@example.com",

View file

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

View file

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

View file

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

View file

@ -38,5 +38,25 @@ export const searchAttributes = gql`
`; `;
export default BaseSearch<SearchAttributes, SearchAttributesVariables>( 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 { import {
SearchProductTypes_productTypes_edges_node, SearchProductTypes_search_edges_node,
SearchProductTypes_productTypes_edges_node_productAttributes SearchProductTypes_search_edges_node_productAttributes
} from "@saleor/containers/SearchProductTypes/types/SearchProductTypes"; } from "@saleor/containers/SearchProductTypes/types/SearchProductTypes";
import { AttributeInputTypeEnum } from "../types/globalTypes"; import { AttributeInputTypeEnum } from "../types/globalTypes";
import { ProductTypeDetails_productType } from "./types/ProductTypeDetails"; import { ProductTypeDetails_productType } from "./types/ProductTypeDetails";
import { ProductTypeList_productTypes_edges_node } from "./types/ProductTypeList"; 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: { node: {
__typename: "Attribute" as "Attribute", __typename: "Attribute" as "Attribute",
@ -469,8 +469,7 @@ export const attributes: SearchProductTypes_productTypes_edges_node_productAttri
].map(edge => edge.node); ].map(edge => edge.node);
export const productTypes: Array< export const productTypes: Array<
SearchProductTypes_productTypes_edges_node & SearchProductTypes_search_edges_node & ProductTypeList_productTypes_edges_node
ProductTypeList_productTypes_edges_node
> = [ > = [
{ {
__typename: "ProductType" as "ProductType", __typename: "ProductType" as "ProductType",

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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