Use exclusively search hooks

This commit is contained in:
dominik-zeglen 2019-11-20 11:24:42 +01:00
parent 13a8d398d2
commit 135658eb11
9 changed files with 127 additions and 292 deletions

View file

@ -1,10 +1,10 @@
import { SearchQueryVariables } from "./containers/BaseSearch"; import { SearchVariables } from "./hooks/makeSearch";
import { ListSettings, ListViews } from "./types"; import { ListSettings, ListViews } from "./types";
export const APP_MOUNT_URI = process.env.APP_MOUNT_URI || "/"; export const APP_MOUNT_URI = process.env.APP_MOUNT_URI || "/";
export const API_URI = process.env.API_URI || "/graphql/"; export const API_URI = process.env.API_URI || "/graphql/";
export const DEFAULT_INITIAL_SEARCH_DATA: SearchQueryVariables = { export const DEFAULT_INITIAL_SEARCH_DATA: SearchVariables = {
after: null, after: null,
first: 20, first: 20,
query: "" query: ""

View file

@ -1,77 +0,0 @@
import { DocumentNode } from "graphql";
import React from "react";
import Debounce from "../components/Debounce";
import { TypedQuery, TypedQueryResult } from "../queries";
export interface SearchQueryVariables {
after?: string;
first: number;
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,
loadMoreFn: (result: TypedQueryResult<TQuery, TQueryVariables>) => void
) {
const Query = TypedQuery<TQuery, TQueryVariables>(query);
class BaseSearchComponent extends React.Component<
BaseSearchProps<TQuery, TQueryVariables>,
SearchQueryVariables
> {
state: SearchQueryVariables = {
first: this.props.variables.first,
query: this.props.variables.query
};
search = (query: string) => {
if (query === undefined) {
this.setState({ query: "" });
} else {
this.setState({ query });
}
};
render() {
const { children, variables } = this.props;
return (
<Debounce debounceFn={this.search} time={200}>
{search => (
<Query
displayLoader={true}
variables={{
...variables,
query: this.state.query
}}
>
{result =>
children({
loadMore: () => loadMoreFn(result),
result,
search
})
}
</Query>
)}
</Debounce>
);
}
}
return BaseSearchComponent;
}
export default BaseSearch;

View file

@ -1,47 +0,0 @@
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

@ -1,6 +1,6 @@
import gql from "graphql-tag"; import gql from "graphql-tag";
import TopLevelSearch from "../containers/TopLevelSearch"; import makeTopLevelSearch from "@saleor/hooks/makeTopLevelSearch";
import { TypedQuery } from "../queries"; import { TypedQuery } from "../queries";
import { OrderDetails, OrderDetailsVariables } from "./types/OrderDetails"; import { OrderDetails, OrderDetailsVariables } from "./types/OrderDetails";
import { import {
@ -314,7 +314,7 @@ export const searchOrderVariant = gql`
} }
} }
`; `;
export const SearchOrderVariant = TopLevelSearch< export const useOrderVariantSearch = makeTopLevelSearch<
SearchOrderVariantType, SearchOrderVariantType,
SearchOrderVariantVariables SearchOrderVariantVariables
>(searchOrderVariant); >(searchOrderVariant);

View file

@ -26,7 +26,7 @@ import OrderPaymentVoidDialog from "../../components/OrderPaymentVoidDialog";
import OrderProductAddDialog from "../../components/OrderProductAddDialog"; import OrderProductAddDialog from "../../components/OrderProductAddDialog";
import OrderShippingMethodEditDialog from "../../components/OrderShippingMethodEditDialog"; import OrderShippingMethodEditDialog from "../../components/OrderShippingMethodEditDialog";
import OrderOperations from "../../containers/OrderOperations"; import OrderOperations from "../../containers/OrderOperations";
import { SearchOrderVariant, TypedOrderDetailsQuery } from "../../queries"; import { TypedOrderDetailsQuery, useOrderVariantSearch } from "../../queries";
import { OrderDetails_order } from "../../types/OrderDetails"; import { OrderDetails_order } from "../../types/OrderDetails";
import { import {
orderListUrl, orderListUrl,
@ -81,6 +81,13 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
} = useCustomerSearch({ } = useCustomerSearch({
variables: DEFAULT_INITIAL_SEARCH_DATA variables: DEFAULT_INITIAL_SEARCH_DATA
}); });
const {
loadMore,
search: variantSearch,
result: variantSearchOpts
} = useOrderVariantSearch({
variables: DEFAULT_INITIAL_SEARCH_DATA
});
return ( return (
<TypedOrderDetailsQuery <TypedOrderDetailsQuery
@ -502,51 +509,41 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
}) })
} }
/> />
<SearchOrderVariant <OrderProductAddDialog
variables={DEFAULT_INITIAL_SEARCH_DATA} confirmButtonState={getMutationState(
> orderLinesAdd.opts.called,
{({ orderLinesAdd.opts.loading,
loadMore, maybe(
search: variantSearch, () =>
result: variantSearchOpts orderLinesAdd.opts.data.draftOrderLinesCreate
}) => ( .errors
<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.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> 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
}))
})
}
/>
</> </>
)} )}
<OrderAddressEditDialog <OrderAddressEditDialog

View file

@ -30,7 +30,7 @@ import useSearchQuery from "@saleor/hooks/useSearchQuery";
import { buttonMessages } from "@saleor/intl"; 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 { SearchAttributes_productType_availableAttributes_edges_node } from "../../containers/SearchAttributes/types/SearchAttributes"; import { SearchAttributes_productType_availableAttributes_edges_node } from "../../hooks/useAvailableAttributeSearch/types/SearchAttributes";
const useStyles = makeStyles(theme => ({ const useStyles = makeStyles(theme => ({
actions: { actions: {

View file

@ -1,7 +1,7 @@
import gql from "graphql-tag"; import gql from "graphql-tag";
import makeSearch from "@saleor/hooks/makeSearch";
import { pageInfoFragment } from "@saleor/queries"; import { pageInfoFragment } from "@saleor/queries";
import BaseSearch from "../../../containers/BaseSearch";
import { import {
SearchAttributes, SearchAttributes,
SearchAttributesVariables SearchAttributesVariables
@ -37,24 +37,33 @@ export const searchAttributes = gql`
} }
`; `;
export default BaseSearch<SearchAttributes, SearchAttributesVariables>( export default makeSearch<SearchAttributes, SearchAttributesVariables>(
searchAttributes, searchAttributes,
result => result =>
result.loadMore( result.loadMore(
(prev, next) => ({ (prev, next) => {
...prev, if (
productType: { prev.productType.availableAttributes.pageInfo.endCursor ===
...prev.productType, next.productType.availableAttributes.pageInfo.endCursor
availableAttributes: { ) {
...prev.productType.availableAttributes, return prev;
edges: [
...prev.productType.availableAttributes.edges,
...next.productType.availableAttributes.edges
],
pageInfo: next.productType.availableAttributes.pageInfo
}
} }
}),
return {
...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 after: result.data.productType.availableAttributes.pageInfo.endCursor
} }

View file

@ -4,6 +4,7 @@ import { FormattedMessage, useIntl } from "react-intl";
import { attributeUrl } from "@saleor/attributes/urls"; import { attributeUrl } from "@saleor/attributes/urls";
import { WindowTitle } from "@saleor/components/WindowTitle"; import { WindowTitle } from "@saleor/components/WindowTitle";
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
import useBulkActions from "@saleor/hooks/useBulkActions"; import useBulkActions from "@saleor/hooks/useBulkActions";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
@ -19,7 +20,7 @@ import ProductTypeDetailsPage, {
ProductTypeForm ProductTypeForm
} from "../../components/ProductTypeDetailsPage"; } from "../../components/ProductTypeDetailsPage";
import ProductTypeOperations from "../../containers/ProductTypeOperations"; import ProductTypeOperations from "../../containers/ProductTypeOperations";
import SearchAttributes from "../../containers/SearchAttributes"; import useAvailableAttributeSearch from "../../hooks/useAvailableAttributeSearch";
import { TypedProductTypeDetailsQuery } from "../../queries"; import { TypedProductTypeDetailsQuery } from "../../queries";
import { AssignAttribute } from "../../types/AssignAttribute"; import { AssignAttribute } from "../../types/AssignAttribute";
import { ProductTypeDelete } from "../../types/ProductTypeDelete"; import { ProductTypeDelete } from "../../types/ProductTypeDelete";
@ -46,6 +47,12 @@ export const ProductTypeUpdate: React.FC<ProductTypeUpdateProps> = ({
const productAttributeListActions = useBulkActions(); const productAttributeListActions = useBulkActions();
const variantAttributeListActions = useBulkActions(); const variantAttributeListActions = useBulkActions();
const intl = useIntl(); const intl = useIntl();
const { loadMore, search, result } = useAvailableAttributeSearch({
variables: {
...DEFAULT_INITIAL_SEARCH_DATA,
id
}
});
return ( return (
<ProductTypeUpdateErrors> <ProductTypeUpdateErrors>
@ -330,109 +337,55 @@ export const ProductTypeUpdate: React.FC<ProductTypeUpdateProps> = ({
) )
}} }}
/> />
{!dataLoading && ( {!dataLoading &&
<SearchAttributes Object.keys(AttributeTypeEnum).map(key => (
variables={{ <AssignAttributeDialog
first: 15, attributes={maybe(() =>
id, result.data.productType.availableAttributes.edges.map(
query: "" edge => edge.node
}} )
> )}
{({ search, result }) => { confirmButtonState={assignTransactionState}
const fetchMore = () => errors={maybe(
result.loadMore( () =>
(prev, next) => { assignAttribute.opts.data.attributeAssign.errors.map(
if ( err => err.message
prev.productType.availableAttributes ),
.pageInfo.endCursor === []
next.productType.availableAttributes )}
.pageInfo.endCursor loading={result.loading}
) { onClose={closeModal}
return prev; onSubmit={handleAssignAttribute}
} onFetch={search}
return { onFetchMore={loadMore}
...prev, onOpen={result.refetch}
productType: { hasMore={maybe(
...prev.productType, () =>
availableAttributes: { result.data.productType.availableAttributes
...prev.productType.availableAttributes, .pageInfo.hasNextPage,
edges: [ false
...prev.productType )}
.availableAttributes.edges, open={
...next.productType params.action === "assign-attribute" &&
.availableAttributes.edges params.type === AttributeTypeEnum[key]
], }
pageInfo: selected={maybe(() => params.ids, [])}
next.productType.availableAttributes onToggle={attributeId => {
.pageInfo const ids = maybe(() => params.ids, []);
} navigate(
} productTypeUrl(id, {
}; ...params,
}, ids: ids.includes(attributeId)
{ ? params.ids.filter(
after: selectedId => selectedId !== attributeId
result.data.productType.availableAttributes
.pageInfo.endCursor
}
);
return (
<>
{Object.keys(AttributeTypeEnum).map(key => (
<AssignAttributeDialog
attributes={maybe(() =>
result.data.productType.availableAttributes.edges.map(
edge => edge.node
) )
)} : [...ids, attributeId]
confirmButtonState={assignTransactionState} })
errors={maybe( );
() => }}
assignAttribute.opts.data.attributeAssign.errors.map( key={key}
err => err.message />
), ))}
[]
)}
loading={result.loading}
onClose={closeModal}
onSubmit={handleAssignAttribute}
onFetch={search}
onFetchMore={fetchMore}
onOpen={result.refetch}
hasMore={maybe(
() =>
result.data.productType
.availableAttributes.pageInfo
.hasNextPage,
false
)}
open={
params.action === "assign-attribute" &&
params.type === AttributeTypeEnum[key]
}
selected={maybe(() => params.ids, [])}
onToggle={attributeId => {
const ids = maybe(() => params.ids, []);
navigate(
productTypeUrl(id, {
...params,
ids: ids.includes(attributeId)
? params.ids.filter(
selectedId =>
selectedId !== attributeId
)
: [...ids, attributeId]
})
);
}}
key={key}
/>
))}
</>
);
}}
</SearchAttributes>
)}
<ProductTypeDeleteDialog <ProductTypeDeleteDialog
confirmButtonState={deleteTransactionState} confirmButtonState={deleteTransactionState}
name={maybe(() => data.productType.name, "...")} name={maybe(() => data.productType.name, "...")}