Use exclusively search hooks
This commit is contained in:
parent
13a8d398d2
commit
135658eb11
9 changed files with 127 additions and 292 deletions
|
@ -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: ""
|
||||||
|
|
|
@ -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;
|
|
|
@ -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;
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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: {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
|
@ -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, "...")}
|
||||||
|
|
Loading…
Reference in a new issue