Merge pull request #262 from mirumee/ref/hook-searches
Use searches as hooks instead of components
This commit is contained in:
commit
29bd1a43a8
49 changed files with 1812 additions and 1990 deletions
|
@ -12,6 +12,7 @@ All notable, unreleased changes to this project will be documented in this file.
|
|||
- Use Apollo Hooks - #254 by @dominik-zeglen
|
||||
- Fix disappearing products description - #259 by @dominik-zeglen
|
||||
- Improve mobile appearance - #240 by @benekex2 and @dominik-zeglen
|
||||
- Use searches as hooks instead of components - #262 by @dominik-zeglen
|
||||
|
||||
## 2.0.0
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import { FormattedMessage, useIntl } from "react-intl";
|
|||
import ActionDialog from "@saleor/components/ActionDialog";
|
||||
import AssignProductDialog from "@saleor/components/AssignProductDialog";
|
||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA, PAGINATE_BY } from "@saleor/config";
|
||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
|
@ -13,8 +14,7 @@ import usePaginator, {
|
|||
createPaginationState
|
||||
} from "@saleor/hooks/usePaginator";
|
||||
import { commonMessages } from "@saleor/intl";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA, PAGINATE_BY } from "../../config";
|
||||
import SearchProducts from "../../containers/SearchProducts";
|
||||
import useProductSearch from "@saleor/searches/useProductSearch";
|
||||
import { getMutationState, maybe } from "../../misc";
|
||||
import { productUrl } from "../../products/urls";
|
||||
import { CollectionInput } from "../../types/globalTypes";
|
||||
|
@ -50,6 +50,9 @@ export const CollectionDetails: React.FC<CollectionDetailsProps> = ({
|
|||
);
|
||||
const paginate = usePaginator();
|
||||
const intl = useIntl();
|
||||
const { search, result } = useProductSearch({
|
||||
variables: DEFAULT_INITIAL_SEARCH_DATA
|
||||
});
|
||||
|
||||
const closeModal = () =>
|
||||
navigate(
|
||||
|
@ -284,29 +287,25 @@ export const CollectionDetails: React.FC<CollectionDetailsProps> = ({
|
|||
toggle={toggle}
|
||||
toggleAll={toggleAll}
|
||||
/>
|
||||
<SearchProducts variables={DEFAULT_INITIAL_SEARCH_DATA}>
|
||||
{({ search, result }) => (
|
||||
<AssignProductDialog
|
||||
confirmButtonState={assignTransitionState}
|
||||
open={params.action === "assign"}
|
||||
onFetch={search}
|
||||
loading={result.loading}
|
||||
onClose={closeModal}
|
||||
onSubmit={products =>
|
||||
assignProduct.mutate({
|
||||
...paginationState,
|
||||
collectionId: id,
|
||||
productIds: products.map(product => product.id)
|
||||
})
|
||||
}
|
||||
products={maybe(() =>
|
||||
result.data.search.edges
|
||||
.map(edge => edge.node)
|
||||
.filter(suggestedProduct => suggestedProduct.id)
|
||||
)}
|
||||
/>
|
||||
<AssignProductDialog
|
||||
confirmButtonState={assignTransitionState}
|
||||
open={params.action === "assign"}
|
||||
onFetch={search}
|
||||
loading={result.loading}
|
||||
onClose={closeModal}
|
||||
onSubmit={products =>
|
||||
assignProduct.mutate({
|
||||
...paginationState,
|
||||
collectionId: id,
|
||||
productIds: products.map(product => product.id)
|
||||
})
|
||||
}
|
||||
products={maybe(() =>
|
||||
result.data.search.edges
|
||||
.map(edge => edge.node)
|
||||
.filter(suggestedProduct => suggestedProduct.id)
|
||||
)}
|
||||
</SearchProducts>
|
||||
/>
|
||||
<ActionDialog
|
||||
confirmButtonState={removeTransitionState}
|
||||
onClose={closeModal}
|
||||
|
|
|
@ -19,7 +19,7 @@ import FormSpacer from "@saleor/components/FormSpacer";
|
|||
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
||||
import useSearchQuery from "@saleor/hooks/useSearchQuery";
|
||||
import { buttonMessages } from "@saleor/intl";
|
||||
import { SearchCategories_search_edges_node } from "../../containers/SearchCategories/types/SearchCategories";
|
||||
import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories";
|
||||
import Checkbox from "../Checkbox";
|
||||
|
||||
export interface FormData {
|
||||
|
|
|
@ -15,7 +15,7 @@ import { FormattedMessage, useIntl } from "react-intl";
|
|||
import ResponsiveTable from "@saleor/components/ResponsiveTable";
|
||||
import useSearchQuery from "@saleor/hooks/useSearchQuery";
|
||||
import { buttonMessages } from "@saleor/intl";
|
||||
import { SearchCollections_search_edges_node } from "../../containers/SearchCollections/types/SearchCollections";
|
||||
import { SearchCollections_search_edges_node } from "@saleor/searches/types/SearchCollections";
|
||||
import Checkbox from "../Checkbox";
|
||||
import ConfirmButton, {
|
||||
ConfirmButtonTransitionState
|
||||
|
|
|
@ -21,7 +21,7 @@ import TableCellAvatar from "@saleor/components/TableCellAvatar";
|
|||
import useSearchQuery from "@saleor/hooks/useSearchQuery";
|
||||
import { buttonMessages } from "@saleor/intl";
|
||||
import { maybe } from "@saleor/misc";
|
||||
import { SearchProducts_search_edges_node } from "../../containers/SearchProducts/types/SearchProducts";
|
||||
import { SearchProducts_search_edges_node } from "@saleor/searches/types/SearchProducts";
|
||||
import Checkbox from "../Checkbox";
|
||||
|
||||
export interface FormData {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { SearchQueryVariables } from "./containers/BaseSearch";
|
||||
import { SearchVariables } from "./hooks/makeSearch";
|
||||
import { ListSettings, ListViews } from "./types";
|
||||
|
||||
export const APP_MOUNT_URI = process.env.APP_MOUNT_URI || "/";
|
||||
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,
|
||||
first: 20,
|
||||
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;
|
|
@ -8,6 +8,7 @@ import AssignCategoriesDialog from "@saleor/components/AssignCategoryDialog";
|
|||
import AssignCollectionDialog from "@saleor/components/AssignCollectionDialog";
|
||||
import AssignProductDialog from "@saleor/components/AssignProductDialog";
|
||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA, PAGINATE_BY } from "@saleor/config";
|
||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
|
@ -16,12 +17,11 @@ import usePaginator, {
|
|||
} from "@saleor/hooks/usePaginator";
|
||||
import useShop from "@saleor/hooks/useShop";
|
||||
import { commonMessages, sectionNames } from "@saleor/intl";
|
||||
import useCategorySearch from "@saleor/searches/useCategorySearch";
|
||||
import useCollectionSearch from "@saleor/searches/useCollectionSearch";
|
||||
import useProductSearch from "@saleor/searches/useProductSearch";
|
||||
import { categoryUrl } from "../../categories/urls";
|
||||
import { collectionUrl } from "../../collections/urls";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA, PAGINATE_BY } from "../../config";
|
||||
import SearchCategories from "../../containers/SearchCategories";
|
||||
import SearchCollections from "../../containers/SearchCollections";
|
||||
import SearchProducts from "../../containers/SearchProducts";
|
||||
import { decimal, getMutationState, joinDateTime, maybe } from "../../misc";
|
||||
import { productUrl } from "../../products/urls";
|
||||
import { DiscountValueTypeEnum, SaleType } from "../../types/globalTypes";
|
||||
|
@ -66,6 +66,24 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
|
|||
params.ids
|
||||
);
|
||||
const intl = useIntl();
|
||||
const {
|
||||
search: searchCategories,
|
||||
result: searchCategoriesOpts
|
||||
} = useCategorySearch({
|
||||
variables: DEFAULT_INITIAL_SEARCH_DATA
|
||||
});
|
||||
const {
|
||||
search: searchCollections,
|
||||
result: searchCollectionsOpts
|
||||
} = useCollectionSearch({
|
||||
variables: DEFAULT_INITIAL_SEARCH_DATA
|
||||
});
|
||||
const {
|
||||
search: searchProducts,
|
||||
result: searchProductsOpts
|
||||
} = useProductSearch({
|
||||
variables: DEFAULT_INITIAL_SEARCH_DATA
|
||||
});
|
||||
|
||||
const paginationState = createPaginationState(PAGINATE_BY, params);
|
||||
const changeTab = (tab: SaleDetailsPageTab) => {
|
||||
|
@ -341,116 +359,87 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
|
|||
toggle={toggle}
|
||||
toggleAll={toggleAll}
|
||||
/>
|
||||
<SearchProducts
|
||||
variables={DEFAULT_INITIAL_SEARCH_DATA}
|
||||
>
|
||||
{({
|
||||
search: searchProducts,
|
||||
result: searchProductsOpts
|
||||
}) => (
|
||||
<AssignProductDialog
|
||||
confirmButtonState={assignTransitionState}
|
||||
open={params.action === "assign-product"}
|
||||
onFetch={searchProducts}
|
||||
loading={searchProductsOpts.loading}
|
||||
onClose={closeModal}
|
||||
onSubmit={products =>
|
||||
saleCataloguesAdd({
|
||||
variables: {
|
||||
...paginationState,
|
||||
id,
|
||||
input: {
|
||||
products: products.map(
|
||||
product => product.id
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
products={maybe(() =>
|
||||
searchProductsOpts.data.search.edges
|
||||
.map(edge => edge.node)
|
||||
.filter(
|
||||
suggestedProduct => suggestedProduct.id
|
||||
<AssignProductDialog
|
||||
confirmButtonState={assignTransitionState}
|
||||
open={params.action === "assign-product"}
|
||||
onFetch={searchProducts}
|
||||
loading={searchProductsOpts.loading}
|
||||
onClose={closeModal}
|
||||
onSubmit={products =>
|
||||
saleCataloguesAdd({
|
||||
variables: {
|
||||
...paginationState,
|
||||
id,
|
||||
input: {
|
||||
products: products.map(
|
||||
product => product.id
|
||||
)
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</SearchProducts>
|
||||
<SearchCategories
|
||||
variables={DEFAULT_INITIAL_SEARCH_DATA}
|
||||
>
|
||||
{({
|
||||
search: searchCategories,
|
||||
result: searchCategoriesOpts
|
||||
}) => (
|
||||
<AssignCategoriesDialog
|
||||
categories={maybe(() =>
|
||||
searchCategoriesOpts.data.search.edges
|
||||
.map(edge => edge.node)
|
||||
.filter(
|
||||
suggestedCategory =>
|
||||
suggestedCategory.id
|
||||
)
|
||||
)}
|
||||
confirmButtonState={assignTransitionState}
|
||||
open={params.action === "assign-category"}
|
||||
onFetch={searchCategories}
|
||||
loading={searchCategoriesOpts.loading}
|
||||
onClose={closeModal}
|
||||
onSubmit={categories =>
|
||||
saleCataloguesAdd({
|
||||
variables: {
|
||||
...paginationState,
|
||||
id,
|
||||
input: {
|
||||
categories: categories.map(
|
||||
product => product.id
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
/>
|
||||
})
|
||||
}
|
||||
products={maybe(() =>
|
||||
searchProductsOpts.data.search.edges
|
||||
.map(edge => edge.node)
|
||||
.filter(
|
||||
suggestedProduct => suggestedProduct.id
|
||||
)
|
||||
)}
|
||||
</SearchCategories>
|
||||
<SearchCollections
|
||||
variables={DEFAULT_INITIAL_SEARCH_DATA}
|
||||
>
|
||||
{({
|
||||
search: searchCollections,
|
||||
result: searchCollectionsOpts
|
||||
}) => (
|
||||
<AssignCollectionDialog
|
||||
collections={maybe(() =>
|
||||
searchCollectionsOpts.data.search.edges
|
||||
.map(edge => edge.node)
|
||||
.filter(
|
||||
suggestedCategory =>
|
||||
suggestedCategory.id
|
||||
/>
|
||||
<AssignCategoriesDialog
|
||||
categories={maybe(() =>
|
||||
searchCategoriesOpts.data.search.edges
|
||||
.map(edge => edge.node)
|
||||
.filter(
|
||||
suggestedCategory => suggestedCategory.id
|
||||
)
|
||||
)}
|
||||
confirmButtonState={assignTransitionState}
|
||||
open={params.action === "assign-category"}
|
||||
onFetch={searchCategories}
|
||||
loading={searchCategoriesOpts.loading}
|
||||
onClose={closeModal}
|
||||
onSubmit={categories =>
|
||||
saleCataloguesAdd({
|
||||
variables: {
|
||||
...paginationState,
|
||||
id,
|
||||
input: {
|
||||
categories: categories.map(
|
||||
product => product.id
|
||||
)
|
||||
)}
|
||||
confirmButtonState={assignTransitionState}
|
||||
open={params.action === "assign-collection"}
|
||||
onFetch={searchCollections}
|
||||
loading={searchCollectionsOpts.loading}
|
||||
onClose={closeModal}
|
||||
onSubmit={collections =>
|
||||
saleCataloguesAdd({
|
||||
variables: {
|
||||
...paginationState,
|
||||
id,
|
||||
input: {
|
||||
collections: collections.map(
|
||||
product => product.id
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
/>
|
||||
})
|
||||
}
|
||||
/>
|
||||
<AssignCollectionDialog
|
||||
collections={maybe(() =>
|
||||
searchCollectionsOpts.data.search.edges
|
||||
.map(edge => edge.node)
|
||||
.filter(
|
||||
suggestedCategory => suggestedCategory.id
|
||||
)
|
||||
)}
|
||||
</SearchCollections>
|
||||
confirmButtonState={assignTransitionState}
|
||||
open={params.action === "assign-collection"}
|
||||
onFetch={searchCollections}
|
||||
loading={searchCollectionsOpts.loading}
|
||||
onClose={closeModal}
|
||||
onSubmit={collections =>
|
||||
saleCataloguesAdd({
|
||||
variables: {
|
||||
...paginationState,
|
||||
id,
|
||||
input: {
|
||||
collections: collections.map(
|
||||
product => product.id
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
/>
|
||||
<ActionDialog
|
||||
open={
|
||||
params.action === "unassign-category" &&
|
||||
|
|
|
@ -8,6 +8,7 @@ import AssignCategoriesDialog from "@saleor/components/AssignCategoryDialog";
|
|||
import AssignCollectionDialog from "@saleor/components/AssignCollectionDialog";
|
||||
import AssignProductDialog from "@saleor/components/AssignProductDialog";
|
||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA, PAGINATE_BY } from "@saleor/config";
|
||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
|
@ -16,12 +17,11 @@ import usePaginator, {
|
|||
} from "@saleor/hooks/usePaginator";
|
||||
import useShop from "@saleor/hooks/useShop";
|
||||
import { commonMessages, sectionNames } from "@saleor/intl";
|
||||
import useCategorySearch from "@saleor/searches/useCategorySearch";
|
||||
import useCollectionSearch from "@saleor/searches/useCollectionSearch";
|
||||
import useProductSearch from "@saleor/searches/useProductSearch";
|
||||
import { categoryUrl } from "../../categories/urls";
|
||||
import { collectionUrl } from "../../collections/urls";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA, PAGINATE_BY } from "../../config";
|
||||
import SearchCategories from "../../containers/SearchCategories";
|
||||
import SearchCollections from "../../containers/SearchCollections";
|
||||
import SearchProducts from "../../containers/SearchProducts";
|
||||
import { decimal, getMutationState, joinDateTime, maybe } from "../../misc";
|
||||
import { productUrl } from "../../products/urls";
|
||||
import {
|
||||
|
@ -68,6 +68,24 @@ export const VoucherDetails: React.FC<VoucherDetailsProps> = ({
|
|||
params.ids
|
||||
);
|
||||
const intl = useIntl();
|
||||
const {
|
||||
search: searchCategories,
|
||||
result: searchCategoriesOpts
|
||||
} = useCategorySearch({
|
||||
variables: DEFAULT_INITIAL_SEARCH_DATA
|
||||
});
|
||||
const {
|
||||
search: searchCollections,
|
||||
result: searchCollectionsOpts
|
||||
} = useCollectionSearch({
|
||||
variables: DEFAULT_INITIAL_SEARCH_DATA
|
||||
});
|
||||
const {
|
||||
search: searchProducts,
|
||||
result: searchProductsOpts
|
||||
} = useProductSearch({
|
||||
variables: DEFAULT_INITIAL_SEARCH_DATA
|
||||
});
|
||||
|
||||
const paginationState = createPaginationState(PAGINATE_BY, params);
|
||||
const changeTab = (tab: VoucherDetailsPageTab) => {
|
||||
|
@ -420,80 +438,60 @@ export const VoucherDetails: React.FC<VoucherDetailsProps> = ({
|
|||
toggle={toggle}
|
||||
toggleAll={toggleAll}
|
||||
/>
|
||||
<SearchCategories
|
||||
variables={DEFAULT_INITIAL_SEARCH_DATA}
|
||||
>
|
||||
{({
|
||||
search: searchCategories,
|
||||
result: searchCategoriesOpts
|
||||
}) => (
|
||||
<AssignCategoriesDialog
|
||||
categories={maybe(() =>
|
||||
searchCategoriesOpts.data.search.edges
|
||||
.map(edge => edge.node)
|
||||
.filter(
|
||||
suggestedCategory =>
|
||||
suggestedCategory.id
|
||||
)
|
||||
)}
|
||||
confirmButtonState={assignTransitionState}
|
||||
open={params.action === "assign-category"}
|
||||
onFetch={searchCategories}
|
||||
loading={searchCategoriesOpts.loading}
|
||||
onClose={closeModal}
|
||||
onSubmit={categories =>
|
||||
voucherCataloguesAdd({
|
||||
variables: {
|
||||
...paginationState,
|
||||
id,
|
||||
input: {
|
||||
categories: categories.map(
|
||||
product => product.id
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
/>
|
||||
<AssignCategoriesDialog
|
||||
categories={maybe(() =>
|
||||
searchCategoriesOpts.data.search.edges
|
||||
.map(edge => edge.node)
|
||||
.filter(
|
||||
suggestedCategory => suggestedCategory.id
|
||||
)
|
||||
)}
|
||||
</SearchCategories>
|
||||
<SearchCollections
|
||||
variables={DEFAULT_INITIAL_SEARCH_DATA}
|
||||
>
|
||||
{({
|
||||
search: searchCollections,
|
||||
result: searchCollectionsOpts
|
||||
}) => (
|
||||
<AssignCollectionDialog
|
||||
collections={maybe(() =>
|
||||
searchCollectionsOpts.data.search.edges
|
||||
.map(edge => edge.node)
|
||||
.filter(
|
||||
suggestedCategory =>
|
||||
suggestedCategory.id
|
||||
confirmButtonState={assignTransitionState}
|
||||
open={params.action === "assign-category"}
|
||||
onFetch={searchCategories}
|
||||
loading={searchCategoriesOpts.loading}
|
||||
onClose={closeModal}
|
||||
onSubmit={categories =>
|
||||
voucherCataloguesAdd({
|
||||
variables: {
|
||||
...paginationState,
|
||||
id,
|
||||
input: {
|
||||
categories: categories.map(
|
||||
product => product.id
|
||||
)
|
||||
)}
|
||||
confirmButtonState={assignTransitionState}
|
||||
open={params.action === "assign-collection"}
|
||||
onFetch={searchCollections}
|
||||
loading={searchCollectionsOpts.loading}
|
||||
onClose={closeModal}
|
||||
onSubmit={collections =>
|
||||
voucherCataloguesAdd({
|
||||
variables: {
|
||||
...paginationState,
|
||||
id,
|
||||
input: {
|
||||
collections: collections.map(
|
||||
product => product.id
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
/>
|
||||
})
|
||||
}
|
||||
/>
|
||||
<AssignCollectionDialog
|
||||
collections={maybe(() =>
|
||||
searchCollectionsOpts.data.search.edges
|
||||
.map(edge => edge.node)
|
||||
.filter(
|
||||
suggestedCategory => suggestedCategory.id
|
||||
)
|
||||
)}
|
||||
</SearchCollections>
|
||||
confirmButtonState={assignTransitionState}
|
||||
open={params.action === "assign-collection"}
|
||||
onFetch={searchCollections}
|
||||
loading={searchCollectionsOpts.loading}
|
||||
onClose={closeModal}
|
||||
onSubmit={collections =>
|
||||
voucherCataloguesAdd({
|
||||
variables: {
|
||||
...paginationState,
|
||||
id,
|
||||
input: {
|
||||
collections: collections.map(
|
||||
product => product.id
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
/>
|
||||
<DiscountCountrySelectDialog
|
||||
confirmButtonState={formTransitionState}
|
||||
countries={maybe(() => shop.countries, [])}
|
||||
|
@ -517,42 +515,33 @@ export const VoucherDetails: React.FC<VoucherDetailsProps> = ({
|
|||
[]
|
||||
)}
|
||||
/>
|
||||
<SearchProducts
|
||||
variables={DEFAULT_INITIAL_SEARCH_DATA}
|
||||
>
|
||||
{({
|
||||
search: searchProducts,
|
||||
result: searchProductsOpts
|
||||
}) => (
|
||||
<AssignProductDialog
|
||||
confirmButtonState={assignTransitionState}
|
||||
open={params.action === "assign-product"}
|
||||
onFetch={searchProducts}
|
||||
loading={searchProductsOpts.loading}
|
||||
onClose={closeModal}
|
||||
onSubmit={products =>
|
||||
voucherCataloguesAdd({
|
||||
variables: {
|
||||
...paginationState,
|
||||
id,
|
||||
input: {
|
||||
products: products.map(
|
||||
product => product.id
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
products={maybe(() =>
|
||||
searchProductsOpts.data.search.edges
|
||||
.map(edge => edge.node)
|
||||
.filter(
|
||||
suggestedProduct => suggestedProduct.id
|
||||
<AssignProductDialog
|
||||
confirmButtonState={assignTransitionState}
|
||||
open={params.action === "assign-product"}
|
||||
onFetch={searchProducts}
|
||||
loading={searchProductsOpts.loading}
|
||||
onClose={closeModal}
|
||||
onSubmit={products =>
|
||||
voucherCataloguesAdd({
|
||||
variables: {
|
||||
...paginationState,
|
||||
id,
|
||||
input: {
|
||||
products: products.map(
|
||||
product => product.id
|
||||
)
|
||||
)}
|
||||
/>
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
products={maybe(() =>
|
||||
searchProductsOpts.data.search.edges
|
||||
.map(edge => edge.node)
|
||||
.filter(
|
||||
suggestedProduct => suggestedProduct.id
|
||||
)
|
||||
)}
|
||||
</SearchProducts>
|
||||
/>
|
||||
<ActionDialog
|
||||
open={
|
||||
params.action === "unassign-category" &&
|
||||
|
|
|
@ -16,7 +16,7 @@ export interface LoadMore<TData, TVariables> {
|
|||
) => Promise<ApolloQueryResult<TData>>;
|
||||
}
|
||||
|
||||
type UseQuery<TData, TVariables> = QueryResult<TData, TVariables> &
|
||||
export type UseQueryResult<TData, TVariables> = QueryResult<TData, TVariables> &
|
||||
LoadMore<TData, TVariables>;
|
||||
type UseQueryOpts<TData, TVariables> = Partial<{
|
||||
displayLoader: boolean;
|
||||
|
@ -26,17 +26,17 @@ type UseQueryOpts<TData, TVariables> = Partial<{
|
|||
}>;
|
||||
type UseQueryHook<TData, TVariables> = (
|
||||
opts: UseQueryOpts<TData, TVariables>
|
||||
) => UseQuery<TData, TVariables>;
|
||||
) => UseQueryResult<TData, TVariables>;
|
||||
|
||||
function makeQuery<TData, TVariables>(
|
||||
query: DocumentNode
|
||||
): UseQueryHook<TData, TVariables> {
|
||||
function useQuery<TData, TVariables>({
|
||||
function useQuery({
|
||||
displayLoader,
|
||||
require,
|
||||
skip,
|
||||
variables
|
||||
}: UseQueryOpts<TData, TVariables>): UseQuery<TData, TVariables> {
|
||||
}: UseQueryOpts<TData, TVariables>): UseQueryResult<TData, TVariables> {
|
||||
const notify = useNotifier();
|
||||
const intl = useIntl();
|
||||
const [, dispatchAppState] = useAppState();
|
||||
|
|
57
src/hooks/makeSearch.ts
Normal file
57
src/hooks/makeSearch.ts
Normal file
|
@ -0,0 +1,57 @@
|
|||
import { DocumentNode } from "graphql";
|
||||
import { useState } from "react";
|
||||
import { QueryResult } from "react-apollo";
|
||||
|
||||
import makeQuery, { UseQueryResult } from "./makeQuery";
|
||||
import useDebounce from "./useDebounce";
|
||||
|
||||
export interface SearchVariables {
|
||||
after?: string;
|
||||
first: number;
|
||||
query: string;
|
||||
}
|
||||
|
||||
export interface UseSearchResult<TData, TVariables extends SearchVariables> {
|
||||
loadMore: () => void;
|
||||
result: QueryResult<TData, TVariables>;
|
||||
search: (query: string) => void;
|
||||
}
|
||||
export type UseSearchOpts<TVariables extends SearchVariables> = Partial<{
|
||||
skip: boolean;
|
||||
variables: TVariables;
|
||||
}>;
|
||||
export type UseSearchHook<TData, TVariables extends SearchVariables> = (
|
||||
opts: UseSearchOpts<TVariables>
|
||||
) => UseSearchResult<TData, TVariables>;
|
||||
|
||||
function makeSearch<TData, TVariables extends SearchVariables>(
|
||||
query: DocumentNode,
|
||||
loadMoreFn: (result: UseQueryResult<TData, TVariables>) => void
|
||||
): UseSearchHook<TData, TVariables> {
|
||||
const useSearchQuery = makeQuery<TData, TVariables>(query);
|
||||
|
||||
function useSearch(
|
||||
opts: UseSearchOpts<TVariables>
|
||||
): UseSearchResult<TData, TVariables> {
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const debouncedSearch = useDebounce(setSearchQuery);
|
||||
const result = useSearchQuery({
|
||||
...opts,
|
||||
displayLoader: true,
|
||||
variables: {
|
||||
...opts.variables,
|
||||
query: searchQuery
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
loadMore: () => loadMoreFn(result),
|
||||
result,
|
||||
search: debouncedSearch
|
||||
};
|
||||
}
|
||||
|
||||
return useSearch;
|
||||
}
|
||||
|
||||
export default makeSearch;
|
|
@ -1,9 +1,9 @@
|
|||
import { DocumentNode } from "graphql";
|
||||
|
||||
import { PageInfoFragment } from "@saleor/types/PageInfoFragment";
|
||||
import BaseSearch, { SearchQueryVariables } from "./BaseSearch";
|
||||
import makeSearch, { SearchVariables, UseSearchHook } from "./makeSearch";
|
||||
|
||||
export interface SearchQuery {
|
||||
export interface SearchData {
|
||||
search: {
|
||||
edges: Array<{
|
||||
node: any;
|
||||
|
@ -12,11 +12,11 @@ export interface SearchQuery {
|
|||
};
|
||||
}
|
||||
|
||||
function TopLevelSearch<
|
||||
TQuery extends SearchQuery,
|
||||
TQueryVariables extends SearchQueryVariables
|
||||
>(query: DocumentNode) {
|
||||
return BaseSearch<TQuery, TQueryVariables>(query, result => {
|
||||
function makeTopLevelSearch<
|
||||
TData extends SearchData,
|
||||
TVariables extends SearchVariables
|
||||
>(query: DocumentNode): UseSearchHook<TData, TVariables> {
|
||||
return makeSearch<TData, TVariables>(query, result => {
|
||||
if (result.data.search.pageInfo.hasNextPage) {
|
||||
result.loadMore(
|
||||
(prev, next) => {
|
||||
|
@ -44,4 +44,4 @@ function TopLevelSearch<
|
|||
});
|
||||
}
|
||||
|
||||
export default TopLevelSearch;
|
||||
export default makeTopLevelSearch;
|
19
src/hooks/useDebounce.ts
Normal file
19
src/hooks/useDebounce.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { useEffect, useRef } from "react";
|
||||
|
||||
export type UseDebounceFn<T> = (...args: T[]) => void;
|
||||
function useDebounce<T>(
|
||||
debounceFn: UseDebounceFn<T>,
|
||||
time = 200
|
||||
): UseDebounceFn<T> {
|
||||
const timer = useRef(null);
|
||||
useEffect(() => () => clearTimeout(timer.current));
|
||||
|
||||
return (...args: T[]) => {
|
||||
if (timer.current) {
|
||||
clearTimeout(timer.current);
|
||||
}
|
||||
timer.current = setTimeout(() => debounceFn(...args), time);
|
||||
};
|
||||
}
|
||||
|
||||
export default useDebounce;
|
|
@ -14,13 +14,13 @@ import ConfirmButton, {
|
|||
ConfirmButtonTransitionState
|
||||
} from "@saleor/components/ConfirmButton";
|
||||
import FormSpacer from "@saleor/components/FormSpacer";
|
||||
import { SearchCategories_search_edges_node } from "@saleor/containers/SearchCategories/types/SearchCategories";
|
||||
import { SearchCollections_search_edges_node } from "@saleor/containers/SearchCollections/types/SearchCollections";
|
||||
import { SearchPages_search_edges_node } from "@saleor/containers/SearchPages/types/SearchPages";
|
||||
import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors";
|
||||
import useModalDialogOpen from "@saleor/hooks/useModalDialogOpen";
|
||||
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
||||
import { buttonMessages, sectionNames } from "@saleor/intl";
|
||||
import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories";
|
||||
import { SearchCollections_search_edges_node } from "@saleor/searches/types/SearchCollections";
|
||||
import { SearchPages_search_edges_node } from "@saleor/searches/types/SearchPages";
|
||||
import { UserError } from "@saleor/types";
|
||||
import { getErrors, getFieldError } from "@saleor/utils/errors";
|
||||
import { getMenuItemByValue, IMenu } from "@saleor/utils/menu";
|
||||
|
|
|
@ -3,14 +3,14 @@ import React from "react";
|
|||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import ActionDialog from "@saleor/components/ActionDialog";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
import useCategorySearch from "@saleor/searches/useCategorySearch";
|
||||
import useCollectionSearch from "@saleor/searches/useCollectionSearch";
|
||||
import usePageSearch from "@saleor/searches/usePageSearch";
|
||||
import { categoryUrl } from "../../../categories/urls";
|
||||
import { collectionUrl } from "../../../collections/urls";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "../../../config";
|
||||
import SearchCategories from "../../../containers/SearchCategories";
|
||||
import SearchCollections from "../../../containers/SearchCollections";
|
||||
import SearchPages from "../../../containers/SearchPages";
|
||||
import { getMutationState, maybe } from "../../../misc";
|
||||
import { pageUrl } from "../../../pages/urls";
|
||||
import MenuDetailsPage, {
|
||||
|
@ -59,6 +59,15 @@ const MenuDetails: React.FC<MenuDetailsProps> = ({ id, params }) => {
|
|||
const navigate = useNavigator();
|
||||
const notify = useNotifier();
|
||||
const intl = useIntl();
|
||||
const categorySearch = useCategorySearch({
|
||||
variables: DEFAULT_INITIAL_SEARCH_DATA
|
||||
});
|
||||
const collectionSearch = useCollectionSearch({
|
||||
variables: DEFAULT_INITIAL_SEARCH_DATA
|
||||
});
|
||||
const pageSearch = usePageSearch({
|
||||
variables: DEFAULT_INITIAL_SEARCH_DATA
|
||||
});
|
||||
|
||||
const closeModal = () =>
|
||||
navigate(
|
||||
|
@ -95,328 +104,267 @@ const MenuDetails: React.FC<MenuDetailsProps> = ({ id, params }) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<SearchPages variables={DEFAULT_INITIAL_SEARCH_DATA}>
|
||||
{pageSearch => (
|
||||
<SearchCategories variables={DEFAULT_INITIAL_SEARCH_DATA}>
|
||||
{categorySearch => (
|
||||
<SearchCollections variables={DEFAULT_INITIAL_SEARCH_DATA}>
|
||||
{collectionSearch => (
|
||||
<MenuDetailsQuery displayLoader variables={{ id }}>
|
||||
{({ data, loading, refetch }) => {
|
||||
const handleQueryChange = (query: string) => {
|
||||
categorySearch.search(query);
|
||||
collectionSearch.search(query);
|
||||
pageSearch.search(query);
|
||||
};
|
||||
<MenuDetailsQuery displayLoader variables={{ id }}>
|
||||
{({ data, loading, refetch }) => {
|
||||
const handleQueryChange = (query: string) => {
|
||||
categorySearch.search(query);
|
||||
collectionSearch.search(query);
|
||||
pageSearch.search(query);
|
||||
};
|
||||
|
||||
const categories = maybe(
|
||||
() =>
|
||||
categorySearch.result.data.search.edges.map(
|
||||
edge => edge.node
|
||||
),
|
||||
[]
|
||||
);
|
||||
const categories = maybe(
|
||||
() => categorySearch.result.data.search.edges.map(edge => edge.node),
|
||||
[]
|
||||
);
|
||||
|
||||
const collections = maybe(
|
||||
() =>
|
||||
collectionSearch.result.data.search.edges.map(
|
||||
edge => edge.node
|
||||
),
|
||||
[]
|
||||
);
|
||||
const collections = maybe(
|
||||
() =>
|
||||
collectionSearch.result.data.search.edges.map(edge => edge.node),
|
||||
[]
|
||||
);
|
||||
|
||||
const pages = maybe(
|
||||
() =>
|
||||
pageSearch.result.data.search.edges.map(
|
||||
edge => edge.node
|
||||
),
|
||||
[]
|
||||
);
|
||||
const pages = maybe(
|
||||
() => pageSearch.result.data.search.edges.map(edge => edge.node),
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<MenuDeleteMutation
|
||||
return (
|
||||
<MenuDeleteMutation
|
||||
onCompleted={data => handleDelete(data, navigate, notify, intl)}
|
||||
>
|
||||
{(menuDelete, menuDeleteOpts) => (
|
||||
<MenuUpdateMutation
|
||||
onCompleted={data => handleUpdate(data, notify, refetch, intl)}
|
||||
>
|
||||
{(menuUpdate, menuUpdateOpts) => {
|
||||
const deleteState = getMutationState(
|
||||
menuDeleteOpts.called,
|
||||
menuDeleteOpts.loading,
|
||||
maybe(() => menuDeleteOpts.data.menuDelete.errors)
|
||||
);
|
||||
|
||||
const updateState = getMutationState(
|
||||
menuUpdateOpts.called,
|
||||
menuUpdateOpts.loading,
|
||||
maybe(() => menuUpdateOpts.data.menuUpdate.errors),
|
||||
maybe(() => menuUpdateOpts.data.menuItemMove.errors)
|
||||
);
|
||||
|
||||
// This is a workaround to let know <MenuDetailsPage />
|
||||
// that it should clean operation stack if mutations
|
||||
// were successful
|
||||
const handleSubmit = async (data: MenuDetailsSubmitData) => {
|
||||
try {
|
||||
const result = await menuUpdate({
|
||||
variables: {
|
||||
id,
|
||||
moves: getMoves(data),
|
||||
name: data.name,
|
||||
removeIds: getRemoveIds(data)
|
||||
}
|
||||
});
|
||||
if (result) {
|
||||
if (
|
||||
result.data.menuItemBulkDelete.errors.length > 0 ||
|
||||
result.data.menuItemMove.errors.length > 0 ||
|
||||
result.data.menuUpdate.errors.length > 0
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<MenuDetailsPage
|
||||
disabled={loading}
|
||||
menu={maybe(() => data.menu)}
|
||||
onBack={() => navigate(menuListUrl())}
|
||||
onDelete={() =>
|
||||
navigate(
|
||||
menuUrl(id, {
|
||||
action: "remove"
|
||||
})
|
||||
)
|
||||
}
|
||||
onItemAdd={() =>
|
||||
navigate(
|
||||
menuUrl(id, {
|
||||
action: "add-item"
|
||||
})
|
||||
)
|
||||
}
|
||||
onItemClick={handleItemClick}
|
||||
onItemEdit={itemId =>
|
||||
navigate(
|
||||
menuUrl(id, {
|
||||
action: "edit-item",
|
||||
id: itemId
|
||||
})
|
||||
)
|
||||
}
|
||||
onSubmit={handleSubmit}
|
||||
saveButtonState={updateState}
|
||||
/>
|
||||
<ActionDialog
|
||||
open={params.action === "remove"}
|
||||
onClose={closeModal}
|
||||
confirmButtonState={deleteState}
|
||||
onConfirm={() => menuDelete({ variables: { id } })}
|
||||
variant="delete"
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Delete Menu",
|
||||
description: "dialog header",
|
||||
id: "menuDetailsDeleteMenuHeader"
|
||||
})}
|
||||
>
|
||||
<DialogContentText>
|
||||
<FormattedMessage
|
||||
defaultMessage="Are you sure you want to delete menu {menuName}?"
|
||||
id="menuDetailsDeleteMenuContent"
|
||||
values={{
|
||||
menuName: (
|
||||
<strong>
|
||||
{maybe(() => data.menu.name, "...")}
|
||||
</strong>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</DialogContentText>
|
||||
</ActionDialog>
|
||||
|
||||
<MenuItemCreateMutation
|
||||
onCompleted={data =>
|
||||
handleDelete(data, navigate, notify, intl)
|
||||
handleItemCreate(data, notify, closeModal, intl)
|
||||
}
|
||||
>
|
||||
{(menuDelete, menuDeleteOpts) => (
|
||||
<MenuUpdateMutation
|
||||
onCompleted={data =>
|
||||
handleUpdate(data, notify, refetch, intl)
|
||||
}
|
||||
>
|
||||
{(menuUpdate, menuUpdateOpts) => {
|
||||
const deleteState = getMutationState(
|
||||
menuDeleteOpts.called,
|
||||
menuDeleteOpts.loading,
|
||||
maybe(
|
||||
() => menuDeleteOpts.data.menuDelete.errors
|
||||
)
|
||||
);
|
||||
{(menuItemCreate, menuItemCreateOpts) => {
|
||||
const handleSubmit = (
|
||||
data: MenuItemDialogFormData
|
||||
) => {
|
||||
const variables: MenuItemCreateVariables = {
|
||||
input: getMenuItemCreateInputData(id, data)
|
||||
};
|
||||
|
||||
const updateState = getMutationState(
|
||||
menuUpdateOpts.called,
|
||||
menuUpdateOpts.loading,
|
||||
maybe(
|
||||
() => menuUpdateOpts.data.menuUpdate.errors
|
||||
),
|
||||
maybe(
|
||||
() => menuUpdateOpts.data.menuItemMove.errors
|
||||
)
|
||||
);
|
||||
menuItemCreate({ variables });
|
||||
};
|
||||
|
||||
// This is a workaround to let know <MenuDetailsPage />
|
||||
// that it should clean operation stack if mutations
|
||||
// were successful
|
||||
const handleSubmit = async (
|
||||
data: MenuDetailsSubmitData
|
||||
) => {
|
||||
try {
|
||||
const result = await menuUpdate({
|
||||
variables: {
|
||||
id,
|
||||
moves: getMoves(data),
|
||||
name: data.name,
|
||||
removeIds: getRemoveIds(data)
|
||||
}
|
||||
});
|
||||
if (result) {
|
||||
if (
|
||||
result.data.menuItemBulkDelete.errors
|
||||
.length > 0 ||
|
||||
result.data.menuItemMove.errors.length >
|
||||
0 ||
|
||||
result.data.menuUpdate.errors.length > 0
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
const formTransitionState = getMutationState(
|
||||
menuItemCreateOpts.called,
|
||||
menuItemCreateOpts.loading,
|
||||
maybe(
|
||||
() =>
|
||||
menuItemCreateOpts.data.menuItemCreate.errors
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<MenuDetailsPage
|
||||
disabled={loading}
|
||||
menu={maybe(() => data.menu)}
|
||||
onBack={() => navigate(menuListUrl())}
|
||||
onDelete={() =>
|
||||
navigate(
|
||||
menuUrl(id, {
|
||||
action: "remove"
|
||||
})
|
||||
)
|
||||
}
|
||||
onItemAdd={() =>
|
||||
navigate(
|
||||
menuUrl(id, {
|
||||
action: "add-item"
|
||||
})
|
||||
)
|
||||
}
|
||||
onItemClick={handleItemClick}
|
||||
onItemEdit={itemId =>
|
||||
navigate(
|
||||
menuUrl(id, {
|
||||
action: "edit-item",
|
||||
id: itemId
|
||||
})
|
||||
)
|
||||
}
|
||||
onSubmit={handleSubmit}
|
||||
saveButtonState={updateState}
|
||||
/>
|
||||
<ActionDialog
|
||||
open={params.action === "remove"}
|
||||
onClose={closeModal}
|
||||
confirmButtonState={deleteState}
|
||||
onConfirm={() =>
|
||||
menuDelete({ variables: { id } })
|
||||
}
|
||||
variant="delete"
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Delete Menu",
|
||||
description: "dialog header",
|
||||
id: "menuDetailsDeleteMenuHeader"
|
||||
})}
|
||||
>
|
||||
<DialogContentText>
|
||||
<FormattedMessage
|
||||
defaultMessage="Are you sure you want to delete menu {menuName}?"
|
||||
id="menuDetailsDeleteMenuContent"
|
||||
values={{
|
||||
menuName: (
|
||||
<strong>
|
||||
{maybe(
|
||||
() => data.menu.name,
|
||||
"..."
|
||||
)}
|
||||
</strong>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</DialogContentText>
|
||||
</ActionDialog>
|
||||
return (
|
||||
<MenuItemDialog
|
||||
open={params.action === "add-item"}
|
||||
categories={categories}
|
||||
collections={collections}
|
||||
errors={maybe(
|
||||
() =>
|
||||
menuItemCreateOpts.data.menuItemCreate.errors,
|
||||
[]
|
||||
)}
|
||||
pages={pages}
|
||||
loading={
|
||||
categorySearch.result.loading ||
|
||||
collectionSearch.result.loading
|
||||
}
|
||||
confirmButtonState={formTransitionState}
|
||||
disabled={menuItemCreateOpts.loading}
|
||||
onClose={closeModal}
|
||||
onSubmit={handleSubmit}
|
||||
onQueryChange={handleQueryChange}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</MenuItemCreateMutation>
|
||||
<MenuItemUpdateMutation
|
||||
onCompleted={data =>
|
||||
handleItemUpdate(data, id, navigate, notify, intl)
|
||||
}
|
||||
>
|
||||
{(menuItemUpdate, menuItemUpdateOpts) => {
|
||||
const handleSubmit = (
|
||||
data: MenuItemDialogFormData
|
||||
) => {
|
||||
const variables: MenuItemUpdateVariables = {
|
||||
id: params.id,
|
||||
input: getMenuItemInputData(data)
|
||||
};
|
||||
|
||||
<MenuItemCreateMutation
|
||||
onCompleted={data =>
|
||||
handleItemCreate(
|
||||
data,
|
||||
notify,
|
||||
closeModal,
|
||||
intl
|
||||
)
|
||||
}
|
||||
>
|
||||
{(menuItemCreate, menuItemCreateOpts) => {
|
||||
const handleSubmit = (
|
||||
data: MenuItemDialogFormData
|
||||
) => {
|
||||
const variables: MenuItemCreateVariables = {
|
||||
input: getMenuItemCreateInputData(
|
||||
id,
|
||||
data
|
||||
)
|
||||
};
|
||||
menuItemUpdate({ variables });
|
||||
};
|
||||
|
||||
menuItemCreate({ variables });
|
||||
};
|
||||
const menuItem = maybe(() =>
|
||||
getNode(
|
||||
data.menu.items,
|
||||
findNode(data.menu.items, params.id)
|
||||
)
|
||||
);
|
||||
|
||||
const formTransitionState = getMutationState(
|
||||
menuItemCreateOpts.called,
|
||||
menuItemCreateOpts.loading,
|
||||
maybe(
|
||||
() =>
|
||||
menuItemCreateOpts.data
|
||||
.menuItemCreate.errors
|
||||
)
|
||||
);
|
||||
const formTransitionState = getMutationState(
|
||||
menuItemUpdateOpts.called,
|
||||
menuItemUpdateOpts.loading,
|
||||
maybe(
|
||||
() =>
|
||||
menuItemUpdateOpts.data.menuItemUpdate.errors
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
<MenuItemDialog
|
||||
open={params.action === "add-item"}
|
||||
categories={categories}
|
||||
collections={collections}
|
||||
errors={maybe(
|
||||
() =>
|
||||
menuItemCreateOpts.data
|
||||
.menuItemCreate.errors,
|
||||
[]
|
||||
)}
|
||||
pages={pages}
|
||||
loading={
|
||||
categorySearch.result.loading ||
|
||||
collectionSearch.result.loading
|
||||
}
|
||||
confirmButtonState={
|
||||
formTransitionState
|
||||
}
|
||||
disabled={menuItemCreateOpts.loading}
|
||||
onClose={closeModal}
|
||||
onSubmit={handleSubmit}
|
||||
onQueryChange={handleQueryChange}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</MenuItemCreateMutation>
|
||||
<MenuItemUpdateMutation
|
||||
onCompleted={data =>
|
||||
handleItemUpdate(
|
||||
data,
|
||||
id,
|
||||
navigate,
|
||||
notify,
|
||||
intl
|
||||
)
|
||||
}
|
||||
>
|
||||
{(menuItemUpdate, menuItemUpdateOpts) => {
|
||||
const handleSubmit = (
|
||||
data: MenuItemDialogFormData
|
||||
) => {
|
||||
const variables: MenuItemUpdateVariables = {
|
||||
id: params.id,
|
||||
input: getMenuItemInputData(data)
|
||||
};
|
||||
const initialFormData: MenuItemDialogFormData = {
|
||||
id: maybe(() => getItemId(menuItem)),
|
||||
name: maybe(() => menuItem.name, "..."),
|
||||
type: maybe<MenuItemType>(
|
||||
() => getItemType(menuItem),
|
||||
"category"
|
||||
)
|
||||
};
|
||||
|
||||
menuItemUpdate({ variables });
|
||||
};
|
||||
|
||||
const menuItem = maybe(() =>
|
||||
getNode(
|
||||
data.menu.items,
|
||||
findNode(data.menu.items, params.id)
|
||||
)
|
||||
);
|
||||
|
||||
const formTransitionState = getMutationState(
|
||||
menuItemUpdateOpts.called,
|
||||
menuItemUpdateOpts.loading,
|
||||
maybe(
|
||||
() =>
|
||||
menuItemUpdateOpts.data
|
||||
.menuItemUpdate.errors
|
||||
)
|
||||
);
|
||||
|
||||
const initialFormData: MenuItemDialogFormData = {
|
||||
id: maybe(() => getItemId(menuItem)),
|
||||
name: maybe(() => menuItem.name, "..."),
|
||||
type: maybe<MenuItemType>(
|
||||
() => getItemType(menuItem),
|
||||
"category"
|
||||
)
|
||||
};
|
||||
|
||||
return (
|
||||
<MenuItemDialog
|
||||
open={params.action === "edit-item"}
|
||||
categories={categories}
|
||||
collections={collections}
|
||||
errors={maybe(
|
||||
() =>
|
||||
menuItemUpdateOpts.data
|
||||
.menuItemUpdate.errors,
|
||||
[]
|
||||
)}
|
||||
pages={pages}
|
||||
initial={initialFormData}
|
||||
initialDisplayValue={getInitialDisplayValue(
|
||||
menuItem
|
||||
)}
|
||||
loading={
|
||||
categorySearch.result.loading ||
|
||||
collectionSearch.result.loading
|
||||
}
|
||||
confirmButtonState={
|
||||
formTransitionState
|
||||
}
|
||||
disabled={menuItemUpdateOpts.loading}
|
||||
onClose={closeModal}
|
||||
onSubmit={handleSubmit}
|
||||
onQueryChange={handleQueryChange}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</MenuItemUpdateMutation>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</MenuUpdateMutation>
|
||||
)}
|
||||
</MenuDeleteMutation>
|
||||
);
|
||||
}}
|
||||
</MenuDetailsQuery>
|
||||
)}
|
||||
</SearchCollections>
|
||||
)}
|
||||
</SearchCategories>
|
||||
)}
|
||||
</SearchPages>
|
||||
return (
|
||||
<MenuItemDialog
|
||||
open={params.action === "edit-item"}
|
||||
categories={categories}
|
||||
collections={collections}
|
||||
errors={maybe(
|
||||
() =>
|
||||
menuItemUpdateOpts.data.menuItemUpdate.errors,
|
||||
[]
|
||||
)}
|
||||
pages={pages}
|
||||
initial={initialFormData}
|
||||
initialDisplayValue={getInitialDisplayValue(
|
||||
menuItem
|
||||
)}
|
||||
loading={
|
||||
categorySearch.result.loading ||
|
||||
collectionSearch.result.loading
|
||||
}
|
||||
confirmButtonState={formTransitionState}
|
||||
disabled={menuItemUpdateOpts.loading}
|
||||
onClose={closeModal}
|
||||
onSubmit={handleSubmit}
|
||||
onQueryChange={handleQueryChange}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</MenuItemUpdateMutation>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</MenuUpdateMutation>
|
||||
)}
|
||||
</MenuDeleteMutation>
|
||||
);
|
||||
}}
|
||||
</MenuDetailsQuery>
|
||||
);
|
||||
};
|
||||
MenuDetails.displayName = "MenuDetails";
|
||||
|
|
|
@ -16,10 +16,10 @@ import SingleAutocompleteSelectField from "@saleor/components/SingleAutocomplete
|
|||
import Skeleton from "@saleor/components/Skeleton";
|
||||
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
||||
import { buttonMessages } from "@saleor/intl";
|
||||
import { SearchCustomers_search_edges_node } from "@saleor/searches/types/SearchCustomers";
|
||||
import { FetchMoreProps, UserPermissionProps } from "@saleor/types";
|
||||
import { PermissionEnum } from "@saleor/types/globalTypes";
|
||||
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
|
||||
import { SearchCustomers_search_edges_node } from "../../../containers/SearchCustomers/types/SearchCustomers";
|
||||
import { customerUrl } from "../../../customers/urls";
|
||||
import { createHref, maybe } from "../../../misc";
|
||||
import { OrderDetails_order } from "../../types/OrderDetails";
|
||||
|
|
|
@ -13,8 +13,8 @@ import PageHeader from "@saleor/components/PageHeader";
|
|||
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
||||
import Skeleton from "@saleor/components/Skeleton";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { SearchCustomers_search_edges_node } from "@saleor/searches/types/SearchCustomers";
|
||||
import { FetchMoreProps, UserPermissionProps } from "@saleor/types";
|
||||
import { SearchCustomers_search_edges_node } from "../../../containers/SearchCustomers/types/SearchCustomers";
|
||||
import { maybe } from "../../../misc";
|
||||
import { DraftOrderInput } from "../../../types/globalTypes";
|
||||
import { OrderDetails_order } from "../../types/OrderDetails";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { SearchCustomers_search_edges_node } from "@saleor/searches/types/SearchCustomers";
|
||||
import { MessageDescriptor } from "react-intl";
|
||||
import { SearchCustomers_search_edges_node } from "../containers/SearchCustomers/types/SearchCustomers";
|
||||
import { transformOrderStatus, transformPaymentStatus } from "../misc";
|
||||
import {
|
||||
FulfillmentStatus,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import gql from "graphql-tag";
|
||||
|
||||
import TopLevelSearch from "../containers/TopLevelSearch";
|
||||
import makeTopLevelSearch from "@saleor/hooks/makeTopLevelSearch";
|
||||
import { TypedQuery } from "../queries";
|
||||
import { OrderDetails, OrderDetailsVariables } from "./types/OrderDetails";
|
||||
import {
|
||||
|
@ -314,7 +314,7 @@ export const searchOrderVariant = gql`
|
|||
}
|
||||
}
|
||||
`;
|
||||
export const SearchOrderVariant = TopLevelSearch<
|
||||
export const useOrderVariantSearch = makeTopLevelSearch<
|
||||
SearchOrderVariantType,
|
||||
SearchOrderVariantVariables
|
||||
>(searchOrderVariant);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -30,7 +30,7 @@ import useSearchQuery from "@saleor/hooks/useSearchQuery";
|
|||
import { buttonMessages } from "@saleor/intl";
|
||||
import { maybe, renderCollection } from "@saleor/misc";
|
||||
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 => ({
|
||||
actions: {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {
|
||||
SearchProductTypes_search_edges_node,
|
||||
SearchProductTypes_search_edges_node_productAttributes
|
||||
} from "@saleor/containers/SearchProductTypes/types/SearchProductTypes";
|
||||
} from "@saleor/searches/types/SearchProductTypes";
|
||||
import { AttributeInputTypeEnum } from "../types/globalTypes";
|
||||
import { ProductTypeDetails_productType } from "./types/ProductTypeDetails";
|
||||
import { ProductTypeList_productTypes_edges_node } from "./types/ProductTypeList";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import gql from "graphql-tag";
|
||||
|
||||
import makeSearch from "@saleor/hooks/makeSearch";
|
||||
import { pageInfoFragment } from "@saleor/queries";
|
||||
import BaseSearch from "../../../containers/BaseSearch";
|
||||
import {
|
||||
SearchAttributes,
|
||||
SearchAttributesVariables
|
||||
|
@ -37,24 +37,33 @@ export const searchAttributes = gql`
|
|||
}
|
||||
`;
|
||||
|
||||
export default BaseSearch<SearchAttributes, SearchAttributesVariables>(
|
||||
export default makeSearch<SearchAttributes, SearchAttributesVariables>(
|
||||
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
|
||||
}
|
||||
(prev, next) => {
|
||||
if (
|
||||
prev.productType.availableAttributes.pageInfo.endCursor ===
|
||||
next.productType.availableAttributes.pageInfo.endCursor
|
||||
) {
|
||||
return prev;
|
||||
}
|
||||
}),
|
||||
|
||||
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
|
||||
}
|
|
@ -4,6 +4,7 @@ import { FormattedMessage, useIntl } from "react-intl";
|
|||
|
||||
import { attributeUrl } from "@saleor/attributes/urls";
|
||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
|
||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
|
@ -19,7 +20,7 @@ import ProductTypeDetailsPage, {
|
|||
ProductTypeForm
|
||||
} from "../../components/ProductTypeDetailsPage";
|
||||
import ProductTypeOperations from "../../containers/ProductTypeOperations";
|
||||
import SearchAttributes from "../../containers/SearchAttributes";
|
||||
import useAvailableAttributeSearch from "../../hooks/useAvailableAttributeSearch";
|
||||
import { TypedProductTypeDetailsQuery } from "../../queries";
|
||||
import { AssignAttribute } from "../../types/AssignAttribute";
|
||||
import { ProductTypeDelete } from "../../types/ProductTypeDelete";
|
||||
|
@ -46,6 +47,12 @@ export const ProductTypeUpdate: React.FC<ProductTypeUpdateProps> = ({
|
|||
const productAttributeListActions = useBulkActions();
|
||||
const variantAttributeListActions = useBulkActions();
|
||||
const intl = useIntl();
|
||||
const { loadMore, search, result } = useAvailableAttributeSearch({
|
||||
variables: {
|
||||
...DEFAULT_INITIAL_SEARCH_DATA,
|
||||
id
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<ProductTypeUpdateErrors>
|
||||
|
@ -330,109 +337,55 @@ export const ProductTypeUpdate: React.FC<ProductTypeUpdateProps> = ({
|
|||
)
|
||||
}}
|
||||
/>
|
||||
{!dataLoading && (
|
||||
<SearchAttributes
|
||||
variables={{
|
||||
first: 15,
|
||||
id,
|
||||
query: ""
|
||||
}}
|
||||
>
|
||||
{({ search, result }) => {
|
||||
const fetchMore = () =>
|
||||
result.loadMore(
|
||||
(prev, next) => {
|
||||
if (
|
||||
prev.productType.availableAttributes
|
||||
.pageInfo.endCursor ===
|
||||
next.productType.availableAttributes
|
||||
.pageInfo.endCursor
|
||||
) {
|
||||
return prev;
|
||||
}
|
||||
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
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{Object.keys(AttributeTypeEnum).map(key => (
|
||||
<AssignAttributeDialog
|
||||
attributes={maybe(() =>
|
||||
result.data.productType.availableAttributes.edges.map(
|
||||
edge => edge.node
|
||||
{!dataLoading &&
|
||||
Object.keys(AttributeTypeEnum).map(key => (
|
||||
<AssignAttributeDialog
|
||||
attributes={maybe(() =>
|
||||
result.data.productType.availableAttributes.edges.map(
|
||||
edge => edge.node
|
||||
)
|
||||
)}
|
||||
confirmButtonState={assignTransactionState}
|
||||
errors={maybe(
|
||||
() =>
|
||||
assignAttribute.opts.data.attributeAssign.errors.map(
|
||||
err => err.message
|
||||
),
|
||||
[]
|
||||
)}
|
||||
loading={result.loading}
|
||||
onClose={closeModal}
|
||||
onSubmit={handleAssignAttribute}
|
||||
onFetch={search}
|
||||
onFetchMore={loadMore}
|
||||
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
|
||||
)
|
||||
)}
|
||||
confirmButtonState={assignTransactionState}
|
||||
errors={maybe(
|
||||
() =>
|
||||
assignAttribute.opts.data.attributeAssign.errors.map(
|
||||
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>
|
||||
)}
|
||||
: [...ids, attributeId]
|
||||
})
|
||||
);
|
||||
}}
|
||||
key={key}
|
||||
/>
|
||||
))}
|
||||
<ProductTypeDeleteDialog
|
||||
confirmButtonState={deleteTransactionState}
|
||||
name={maybe(() => data.productType.name, "...")}
|
||||
|
|
|
@ -13,9 +13,6 @@ import PageHeader from "@saleor/components/PageHeader";
|
|||
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
||||
import SeoForm from "@saleor/components/SeoForm";
|
||||
import VisibilityCard from "@saleor/components/VisibilityCard";
|
||||
import { SearchCategories_search_edges_node } from "@saleor/containers/SearchCategories/types/SearchCategories";
|
||||
import { SearchCollections_search_edges_node } from "@saleor/containers/SearchCollections/types/SearchCollections";
|
||||
import { SearchProductTypes_search_edges_node_productAttributes } from "@saleor/containers/SearchProductTypes/types/SearchProductTypes";
|
||||
import useDateLocalize from "@saleor/hooks/useDateLocalize";
|
||||
import useFormset from "@saleor/hooks/useFormset";
|
||||
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
||||
|
@ -25,6 +22,9 @@ import {
|
|||
ProductAttributeValueChoices,
|
||||
ProductType
|
||||
} from "@saleor/products/utils/data";
|
||||
import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories";
|
||||
import { SearchCollections_search_edges_node } from "@saleor/searches/types/SearchCollections";
|
||||
import { SearchProductTypes_search_edges_node_productAttributes } from "@saleor/searches/types/SearchProductTypes";
|
||||
import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler";
|
||||
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
|
||||
import { FetchMoreProps, UserError } from "../../../types";
|
||||
|
|
|
@ -12,13 +12,13 @@ import PageHeader from "@saleor/components/PageHeader";
|
|||
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
||||
import SeoForm from "@saleor/components/SeoForm";
|
||||
import VisibilityCard from "@saleor/components/VisibilityCard";
|
||||
import { SearchCategories_search_edges_node } from "@saleor/containers/SearchCategories/types/SearchCategories";
|
||||
import { SearchCollections_search_edges_node } from "@saleor/containers/SearchCollections/types/SearchCollections";
|
||||
import useDateLocalize from "@saleor/hooks/useDateLocalize";
|
||||
import useFormset from "@saleor/hooks/useFormset";
|
||||
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { maybe } from "@saleor/misc";
|
||||
import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories";
|
||||
import { SearchCollections_search_edges_node } from "@saleor/searches/types/SearchCollections";
|
||||
import { FetchMoreProps, ListActions, UserError } from "@saleor/types";
|
||||
import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler";
|
||||
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
|
||||
|
|
|
@ -2,13 +2,13 @@ import { RawDraftContentState } from "draft-js";
|
|||
|
||||
import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField";
|
||||
import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField";
|
||||
import { SearchProductTypes_search_edges_node_productAttributes } from "@saleor/containers/SearchProductTypes/types/SearchProductTypes";
|
||||
import { maybe } from "@saleor/misc";
|
||||
import {
|
||||
ProductDetails_product,
|
||||
ProductDetails_product_collections,
|
||||
ProductDetails_product_variants
|
||||
} from "@saleor/products/types/ProductDetails";
|
||||
import { SearchProductTypes_search_edges_node_productAttributes } from "@saleor/searches/types/SearchProductTypes";
|
||||
import { ProductAttributeInput } from "../components/ProductAttributes";
|
||||
import { VariantAttributeInput } from "../components/ProductVariantAttributes";
|
||||
import { ProductVariant } from "../types/ProductVariant";
|
||||
|
|
|
@ -2,13 +2,13 @@ import React from "react";
|
|||
import { useIntl } from "react-intl";
|
||||
|
||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||
import SearchProductTypes from "@saleor/containers/SearchProductTypes";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
import useShop from "@saleor/hooks/useShop";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "../../config";
|
||||
import SearchCategories from "../../containers/SearchCategories";
|
||||
import SearchCollections from "../../containers/SearchCollections";
|
||||
import useCategorySearch from "@saleor/searches/useCategorySearch";
|
||||
import useCollectionSearch from "@saleor/searches/useCollectionSearch";
|
||||
import useProductTypeSearch from "@saleor/searches/useProductTypeSearch";
|
||||
import { decimal, getMutationState, maybe } from "../../misc";
|
||||
import ProductCreatePage, {
|
||||
ProductCreatePageSubmitData
|
||||
|
@ -26,174 +26,152 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = () => {
|
|||
const notify = useNotifier();
|
||||
const shop = useShop();
|
||||
const intl = useIntl();
|
||||
const {
|
||||
loadMore: loadMoreCategories,
|
||||
search: searchCategory,
|
||||
result: searchCategoryOpts
|
||||
} = useCategorySearch({
|
||||
variables: DEFAULT_INITIAL_SEARCH_DATA
|
||||
});
|
||||
const {
|
||||
loadMore: loadMoreCollections,
|
||||
search: searchCollection,
|
||||
result: searchCollectionOpts
|
||||
} = useCollectionSearch({
|
||||
variables: DEFAULT_INITIAL_SEARCH_DATA
|
||||
});
|
||||
const {
|
||||
loadMore: loadMoreProductTypes,
|
||||
search: searchProductTypes,
|
||||
result: searchProductTypesOpts
|
||||
} = useProductTypeSearch({
|
||||
variables: DEFAULT_INITIAL_SEARCH_DATA
|
||||
});
|
||||
|
||||
const handleBack = () => navigate(productListUrl());
|
||||
|
||||
const handleSuccess = (data: ProductCreate) => {
|
||||
if (data.productCreate.errors.length === 0) {
|
||||
notify({
|
||||
text: intl.formatMessage({
|
||||
defaultMessage: "Product created"
|
||||
})
|
||||
});
|
||||
navigate(productUrl(data.productCreate.product.id));
|
||||
} else {
|
||||
const attributeError = data.productCreate.errors.find(
|
||||
err => err.field === "attributes"
|
||||
);
|
||||
if (!!attributeError) {
|
||||
notify({ text: attributeError.message });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<SearchCategories variables={DEFAULT_INITIAL_SEARCH_DATA}>
|
||||
{({
|
||||
loadMore: loadMoreCategories,
|
||||
search: searchCategory,
|
||||
result: searchCategoryOpts
|
||||
}) => (
|
||||
<SearchCollections variables={DEFAULT_INITIAL_SEARCH_DATA}>
|
||||
{({
|
||||
loadMore: loadMoreCollections,
|
||||
search: searchCollection,
|
||||
result: searchCollectionOpts
|
||||
}) => (
|
||||
<SearchProductTypes variables={DEFAULT_INITIAL_SEARCH_DATA}>
|
||||
{({
|
||||
loadMore: loadMoreProductTypes,
|
||||
search: searchProductTypes,
|
||||
result: searchProductTypesOpts
|
||||
}) => {
|
||||
const handleSuccess = (data: ProductCreate) => {
|
||||
if (data.productCreate.errors.length === 0) {
|
||||
notify({
|
||||
text: intl.formatMessage({
|
||||
defaultMessage: "Product created"
|
||||
})
|
||||
});
|
||||
navigate(productUrl(data.productCreate.product.id));
|
||||
} else {
|
||||
const attributeError = data.productCreate.errors.find(
|
||||
err => err.field === "attributes"
|
||||
);
|
||||
if (!!attributeError) {
|
||||
notify({ text: attributeError.message });
|
||||
}
|
||||
}
|
||||
};
|
||||
<TypedProductCreateMutation onCompleted={handleSuccess}>
|
||||
{(
|
||||
productCreate,
|
||||
{
|
||||
called: productCreateCalled,
|
||||
data: productCreateData,
|
||||
loading: productCreateDataLoading
|
||||
}
|
||||
) => {
|
||||
const handleSubmit = (formData: ProductCreatePageSubmitData) => {
|
||||
productCreate({
|
||||
variables: {
|
||||
attributes: formData.attributes.map(attribute => ({
|
||||
id: attribute.id,
|
||||
values: attribute.value
|
||||
})),
|
||||
basePrice: decimal(formData.basePrice),
|
||||
category: formData.category,
|
||||
chargeTaxes: formData.chargeTaxes,
|
||||
collections: formData.collections,
|
||||
descriptionJson: JSON.stringify(formData.description),
|
||||
isPublished: formData.isPublished,
|
||||
name: formData.name,
|
||||
productType: formData.productType,
|
||||
publicationDate:
|
||||
formData.publicationDate !== ""
|
||||
? formData.publicationDate
|
||||
: null,
|
||||
seo: {
|
||||
description: formData.seoDescription,
|
||||
title: formData.seoTitle
|
||||
},
|
||||
sku: formData.sku,
|
||||
stockQuantity:
|
||||
formData.stockQuantity !== null ? formData.stockQuantity : 0
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<TypedProductCreateMutation onCompleted={handleSuccess}>
|
||||
{(
|
||||
productCreate,
|
||||
{
|
||||
called: productCreateCalled,
|
||||
data: productCreateData,
|
||||
loading: productCreateDataLoading
|
||||
}
|
||||
) => {
|
||||
const handleSubmit = (
|
||||
formData: ProductCreatePageSubmitData
|
||||
) => {
|
||||
productCreate({
|
||||
variables: {
|
||||
attributes: formData.attributes.map(attribute => ({
|
||||
id: attribute.id,
|
||||
values: attribute.value
|
||||
})),
|
||||
basePrice: decimal(formData.basePrice),
|
||||
category: formData.category,
|
||||
chargeTaxes: formData.chargeTaxes,
|
||||
collections: formData.collections,
|
||||
descriptionJson: JSON.stringify(
|
||||
formData.description
|
||||
),
|
||||
isPublished: formData.isPublished,
|
||||
name: formData.name,
|
||||
productType: formData.productType,
|
||||
publicationDate:
|
||||
formData.publicationDate !== ""
|
||||
? formData.publicationDate
|
||||
: null,
|
||||
seo: {
|
||||
description: formData.seoDescription,
|
||||
title: formData.seoTitle
|
||||
},
|
||||
sku: formData.sku,
|
||||
stockQuantity:
|
||||
formData.stockQuantity !== null
|
||||
? formData.stockQuantity
|
||||
: 0
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const formTransitionState = getMutationState(
|
||||
productCreateCalled,
|
||||
productCreateDataLoading,
|
||||
maybe(() => productCreateData.productCreate.errors)
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<WindowTitle
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Create Product",
|
||||
description: "window title"
|
||||
})}
|
||||
/>
|
||||
<ProductCreatePage
|
||||
currency={maybe(() => shop.defaultCurrency)}
|
||||
categories={maybe(
|
||||
() => searchCategoryOpts.data.search.edges,
|
||||
[]
|
||||
).map(edge => edge.node)}
|
||||
collections={maybe(
|
||||
() => searchCollectionOpts.data.search.edges,
|
||||
[]
|
||||
).map(edge => edge.node)}
|
||||
disabled={productCreateDataLoading}
|
||||
errors={maybe(
|
||||
() => productCreateData.productCreate.errors,
|
||||
[]
|
||||
)}
|
||||
fetchCategories={searchCategory}
|
||||
fetchCollections={searchCollection}
|
||||
fetchProductTypes={searchProductTypes}
|
||||
header={intl.formatMessage({
|
||||
defaultMessage: "New Product",
|
||||
description: "page header"
|
||||
})}
|
||||
productTypes={maybe(() =>
|
||||
searchProductTypesOpts.data.search.edges.map(
|
||||
edge => edge.node
|
||||
)
|
||||
)}
|
||||
onBack={handleBack}
|
||||
onSubmit={handleSubmit}
|
||||
saveButtonBarState={formTransitionState}
|
||||
fetchMoreCategories={{
|
||||
hasMore: maybe(
|
||||
() =>
|
||||
searchCategoryOpts.data.search.pageInfo
|
||||
.hasNextPage
|
||||
),
|
||||
loading: searchCategoryOpts.loading,
|
||||
onFetchMore: loadMoreCategories
|
||||
}}
|
||||
fetchMoreCollections={{
|
||||
hasMore: maybe(
|
||||
() =>
|
||||
searchCollectionOpts.data.search.pageInfo
|
||||
.hasNextPage
|
||||
),
|
||||
loading: searchCollectionOpts.loading,
|
||||
onFetchMore: loadMoreCollections
|
||||
}}
|
||||
fetchMoreProductTypes={{
|
||||
hasMore: maybe(
|
||||
() =>
|
||||
searchProductTypesOpts.data.search.pageInfo
|
||||
.hasNextPage
|
||||
),
|
||||
loading: searchProductTypesOpts.loading,
|
||||
onFetchMore: loadMoreProductTypes
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</TypedProductCreateMutation>
|
||||
);
|
||||
const formTransitionState = getMutationState(
|
||||
productCreateCalled,
|
||||
productCreateDataLoading,
|
||||
maybe(() => productCreateData.productCreate.errors)
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<WindowTitle
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Create Product",
|
||||
description: "window title"
|
||||
})}
|
||||
/>
|
||||
<ProductCreatePage
|
||||
currency={maybe(() => shop.defaultCurrency)}
|
||||
categories={maybe(
|
||||
() => searchCategoryOpts.data.search.edges,
|
||||
[]
|
||||
).map(edge => edge.node)}
|
||||
collections={maybe(
|
||||
() => searchCollectionOpts.data.search.edges,
|
||||
[]
|
||||
).map(edge => edge.node)}
|
||||
disabled={productCreateDataLoading}
|
||||
errors={maybe(() => productCreateData.productCreate.errors, [])}
|
||||
fetchCategories={searchCategory}
|
||||
fetchCollections={searchCollection}
|
||||
fetchProductTypes={searchProductTypes}
|
||||
header={intl.formatMessage({
|
||||
defaultMessage: "New Product",
|
||||
description: "page header"
|
||||
})}
|
||||
productTypes={maybe(() =>
|
||||
searchProductTypesOpts.data.search.edges.map(edge => edge.node)
|
||||
)}
|
||||
onBack={handleBack}
|
||||
onSubmit={handleSubmit}
|
||||
saveButtonBarState={formTransitionState}
|
||||
fetchMoreCategories={{
|
||||
hasMore: maybe(
|
||||
() => searchCategoryOpts.data.search.pageInfo.hasNextPage
|
||||
),
|
||||
loading: searchCategoryOpts.loading,
|
||||
onFetchMore: loadMoreCategories
|
||||
}}
|
||||
</SearchProductTypes>
|
||||
)}
|
||||
</SearchCollections>
|
||||
)}
|
||||
</SearchCategories>
|
||||
fetchMoreCollections={{
|
||||
hasMore: maybe(
|
||||
() => searchCollectionOpts.data.search.pageInfo.hasNextPage
|
||||
),
|
||||
loading: searchCollectionOpts.loading,
|
||||
onFetchMore: loadMoreCollections
|
||||
}}
|
||||
fetchMoreProductTypes={{
|
||||
hasMore: maybe(
|
||||
() => searchProductTypesOpts.data.search.pageInfo.hasNextPage
|
||||
),
|
||||
loading: searchProductTypesOpts.loading,
|
||||
onFetchMore: loadMoreProductTypes
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</TypedProductCreateMutation>
|
||||
);
|
||||
};
|
||||
export default ProductUpdate;
|
||||
|
|
|
@ -7,6 +7,7 @@ import { FormattedMessage, useIntl } from "react-intl";
|
|||
import placeholderImg from "@assets/images/placeholder255x255.png";
|
||||
import ActionDialog from "@saleor/components/ActionDialog";
|
||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
|
||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
|
@ -14,9 +15,8 @@ import useShop from "@saleor/hooks/useShop";
|
|||
import { commonMessages } from "@saleor/intl";
|
||||
import ProductVariantCreateDialog from "@saleor/products/components/ProductVariantCreateDialog/ProductVariantCreateDialog";
|
||||
import { ProductVariantBulkCreate } from "@saleor/products/types/ProductVariantBulkCreate";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "../../../config";
|
||||
import SearchCategories from "../../../containers/SearchCategories";
|
||||
import SearchCollections from "../../../containers/SearchCollections";
|
||||
import useCategorySearch from "@saleor/searches/useCategorySearch";
|
||||
import useCollectionSearch from "@saleor/searches/useCollectionSearch";
|
||||
import { getMutationState, maybe } from "../../../misc";
|
||||
import ProductUpdatePage from "../../components/ProductUpdatePage";
|
||||
import ProductUpdateOperations from "../../containers/ProductUpdateOperations";
|
||||
|
@ -55,6 +55,20 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
|
|||
);
|
||||
const intl = useIntl();
|
||||
const shop = useShop();
|
||||
const {
|
||||
loadMore: loadMoreCategories,
|
||||
search: searchCategories,
|
||||
result: searchCategoriesOpts
|
||||
} = useCategorySearch({
|
||||
variables: DEFAULT_INITIAL_SEARCH_DATA
|
||||
});
|
||||
const {
|
||||
loadMore: loadMoreCollections,
|
||||
search: searchCollections,
|
||||
result: searchCollectionsOpts
|
||||
} = useCollectionSearch({
|
||||
variables: DEFAULT_INITIAL_SEARCH_DATA
|
||||
});
|
||||
|
||||
const openModal = (action: ProductUrlDialog) =>
|
||||
navigate(
|
||||
|
@ -64,356 +78,320 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
|
|||
);
|
||||
|
||||
return (
|
||||
<SearchCategories variables={DEFAULT_INITIAL_SEARCH_DATA}>
|
||||
{({
|
||||
loadMore: loadMoreCategories,
|
||||
search: searchCategories,
|
||||
result: searchCategoriesOpts
|
||||
}) => (
|
||||
<SearchCollections variables={DEFAULT_INITIAL_SEARCH_DATA}>
|
||||
{({
|
||||
loadMore: loadMoreCollections,
|
||||
search: searchCollections,
|
||||
result: searchCollectionsOpts
|
||||
}) => (
|
||||
<TypedProductDetailsQuery
|
||||
displayLoader
|
||||
require={["product"]}
|
||||
variables={{ id }}
|
||||
>
|
||||
{({ data, loading, refetch }) => {
|
||||
const handleDelete = () => {
|
||||
notify({
|
||||
text: intl.formatMessage({
|
||||
defaultMessage: "Product removed"
|
||||
})
|
||||
});
|
||||
navigate(productListUrl());
|
||||
};
|
||||
const handleUpdate = (data: ProductUpdateMutationResult) => {
|
||||
if (data.productUpdate.errors.length === 0) {
|
||||
notify({
|
||||
text: intl.formatMessage(commonMessages.savedChanges)
|
||||
});
|
||||
} else {
|
||||
const attributeError = data.productUpdate.errors.find(
|
||||
err => err.field === "attributes"
|
||||
);
|
||||
if (!!attributeError) {
|
||||
notify({ text: attributeError.message });
|
||||
}
|
||||
}
|
||||
};
|
||||
<TypedProductDetailsQuery
|
||||
displayLoader
|
||||
require={["product"]}
|
||||
variables={{ id }}
|
||||
>
|
||||
{({ data, loading, refetch }) => {
|
||||
const handleDelete = () => {
|
||||
notify({
|
||||
text: intl.formatMessage({
|
||||
defaultMessage: "Product removed"
|
||||
})
|
||||
});
|
||||
navigate(productListUrl());
|
||||
};
|
||||
const handleUpdate = (data: ProductUpdateMutationResult) => {
|
||||
if (data.productUpdate.errors.length === 0) {
|
||||
notify({
|
||||
text: intl.formatMessage(commonMessages.savedChanges)
|
||||
});
|
||||
} else {
|
||||
const attributeError = data.productUpdate.errors.find(
|
||||
err => err.field === "attributes"
|
||||
);
|
||||
if (!!attributeError) {
|
||||
notify({ text: attributeError.message });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleImageCreate = (data: ProductImageCreate) => {
|
||||
const imageError = data.productImageCreate.errors.find(
|
||||
error =>
|
||||
error.field ===
|
||||
("image" as keyof ProductImageCreateVariables)
|
||||
);
|
||||
if (imageError) {
|
||||
notify({
|
||||
text: imageError.message
|
||||
});
|
||||
}
|
||||
};
|
||||
const handleImageDeleteSuccess = () =>
|
||||
notify({
|
||||
text: intl.formatMessage(commonMessages.savedChanges)
|
||||
});
|
||||
const handleVariantAdd = () =>
|
||||
navigate(productVariantAddUrl(id));
|
||||
const handleImageCreate = (data: ProductImageCreate) => {
|
||||
const imageError = data.productImageCreate.errors.find(
|
||||
error =>
|
||||
error.field === ("image" as keyof ProductImageCreateVariables)
|
||||
);
|
||||
if (imageError) {
|
||||
notify({
|
||||
text: imageError.message
|
||||
});
|
||||
}
|
||||
};
|
||||
const handleImageDeleteSuccess = () =>
|
||||
notify({
|
||||
text: intl.formatMessage(commonMessages.savedChanges)
|
||||
});
|
||||
const handleVariantAdd = () => navigate(productVariantAddUrl(id));
|
||||
|
||||
const handleBulkProductVariantCreate = (
|
||||
data: ProductVariantBulkCreate
|
||||
) => {
|
||||
if (data.productVariantBulkCreate.errors.length === 0) {
|
||||
navigate(productUrl(id), true);
|
||||
refetch();
|
||||
}
|
||||
};
|
||||
const handleBulkProductVariantCreate = (
|
||||
data: ProductVariantBulkCreate
|
||||
) => {
|
||||
if (data.productVariantBulkCreate.errors.length === 0) {
|
||||
navigate(productUrl(id), true);
|
||||
refetch();
|
||||
}
|
||||
};
|
||||
|
||||
const handleBulkProductVariantDelete = (
|
||||
data: ProductVariantBulkDelete
|
||||
) => {
|
||||
if (data.productVariantBulkDelete.errors.length === 0) {
|
||||
navigate(productUrl(id), true);
|
||||
reset();
|
||||
refetch();
|
||||
}
|
||||
};
|
||||
const handleBulkProductVariantDelete = (
|
||||
data: ProductVariantBulkDelete
|
||||
) => {
|
||||
if (data.productVariantBulkDelete.errors.length === 0) {
|
||||
navigate(productUrl(id), true);
|
||||
reset();
|
||||
refetch();
|
||||
}
|
||||
};
|
||||
|
||||
const handleVariantCreatorOpen = () =>
|
||||
navigate(
|
||||
productUrl(id, {
|
||||
...params,
|
||||
action: "create-variants"
|
||||
})
|
||||
);
|
||||
const handleVariantCreatorOpen = () =>
|
||||
navigate(
|
||||
productUrl(id, {
|
||||
...params,
|
||||
action: "create-variants"
|
||||
})
|
||||
);
|
||||
|
||||
const product = data ? data.product : undefined;
|
||||
return (
|
||||
<ProductUpdateOperations
|
||||
const product = data ? data.product : undefined;
|
||||
return (
|
||||
<ProductUpdateOperations
|
||||
product={product}
|
||||
onBulkProductVariantCreate={handleBulkProductVariantCreate}
|
||||
onBulkProductVariantDelete={handleBulkProductVariantDelete}
|
||||
onDelete={handleDelete}
|
||||
onImageCreate={handleImageCreate}
|
||||
onImageDelete={handleImageDeleteSuccess}
|
||||
onUpdate={handleUpdate}
|
||||
>
|
||||
{({
|
||||
bulkProductVariantCreate,
|
||||
bulkProductVariantDelete,
|
||||
createProductImage,
|
||||
deleteProduct,
|
||||
deleteProductImage,
|
||||
reorderProductImages,
|
||||
updateProduct,
|
||||
updateSimpleProduct
|
||||
}) => {
|
||||
const handleImageDelete = (id: string) => () =>
|
||||
deleteProductImage.mutate({ id });
|
||||
const handleImageEdit = (imageId: string) => () =>
|
||||
navigate(productImageUrl(id, imageId));
|
||||
const handleSubmit = createUpdateHandler(
|
||||
product,
|
||||
updateProduct.mutate,
|
||||
updateSimpleProduct.mutate
|
||||
);
|
||||
const handleImageUpload = createImageUploadHandler(
|
||||
id,
|
||||
createProductImage.mutate
|
||||
);
|
||||
const handleImageReorder = createImageReorderHandler(
|
||||
product,
|
||||
reorderProductImages.mutate
|
||||
);
|
||||
|
||||
const disableFormSave =
|
||||
createProductImage.opts.loading ||
|
||||
deleteProduct.opts.loading ||
|
||||
reorderProductImages.opts.loading ||
|
||||
updateProduct.opts.loading ||
|
||||
loading;
|
||||
const formTransitionState = getMutationState(
|
||||
updateProduct.opts.called || updateSimpleProduct.opts.called,
|
||||
updateProduct.opts.loading || updateSimpleProduct.opts.loading,
|
||||
maybe(() => updateProduct.opts.data.productUpdate.errors),
|
||||
maybe(() => updateSimpleProduct.opts.data.productUpdate.errors),
|
||||
maybe(
|
||||
() =>
|
||||
updateSimpleProduct.opts.data.productVariantUpdate.errors
|
||||
)
|
||||
);
|
||||
const deleteTransitionState = getMutationState(
|
||||
deleteProduct.opts.called,
|
||||
deleteProduct.opts.loading,
|
||||
maybe(() => deleteProduct.opts.data.productDelete.errors)
|
||||
);
|
||||
|
||||
const bulkProductVariantDeleteTransitionState = getMutationState(
|
||||
bulkProductVariantDelete.opts.called,
|
||||
bulkProductVariantDelete.opts.loading,
|
||||
maybe(
|
||||
() =>
|
||||
bulkProductVariantDelete.opts.data.productVariantBulkDelete
|
||||
.errors
|
||||
)
|
||||
);
|
||||
|
||||
const categories = maybe(
|
||||
() => searchCategoriesOpts.data.search.edges,
|
||||
[]
|
||||
).map(edge => edge.node);
|
||||
const collections = maybe(
|
||||
() => searchCollectionsOpts.data.search.edges,
|
||||
[]
|
||||
).map(edge => edge.node);
|
||||
const errors = maybe(
|
||||
() => updateProduct.opts.data.productUpdate.errors,
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WindowTitle title={maybe(() => data.product.name)} />
|
||||
<ProductUpdatePage
|
||||
categories={categories}
|
||||
collections={collections}
|
||||
disabled={disableFormSave}
|
||||
errors={errors}
|
||||
fetchCategories={searchCategories}
|
||||
fetchCollections={searchCollections}
|
||||
saveButtonBarState={formTransitionState}
|
||||
images={maybe(() => data.product.images)}
|
||||
header={maybe(() => product.name)}
|
||||
placeholderImage={placeholderImg}
|
||||
product={product}
|
||||
onBulkProductVariantCreate={handleBulkProductVariantCreate}
|
||||
onBulkProductVariantDelete={handleBulkProductVariantDelete}
|
||||
onDelete={handleDelete}
|
||||
onImageCreate={handleImageCreate}
|
||||
onImageDelete={handleImageDeleteSuccess}
|
||||
onUpdate={handleUpdate}
|
||||
>
|
||||
{({
|
||||
bulkProductVariantCreate,
|
||||
bulkProductVariantDelete,
|
||||
createProductImage,
|
||||
deleteProduct,
|
||||
deleteProductImage,
|
||||
reorderProductImages,
|
||||
updateProduct,
|
||||
updateSimpleProduct
|
||||
}) => {
|
||||
const handleImageDelete = (id: string) => () =>
|
||||
deleteProductImage.mutate({ id });
|
||||
const handleImageEdit = (imageId: string) => () =>
|
||||
navigate(productImageUrl(id, imageId));
|
||||
const handleSubmit = createUpdateHandler(
|
||||
product,
|
||||
updateProduct.mutate,
|
||||
updateSimpleProduct.mutate
|
||||
);
|
||||
const handleImageUpload = createImageUploadHandler(
|
||||
id,
|
||||
createProductImage.mutate
|
||||
);
|
||||
const handleImageReorder = createImageReorderHandler(
|
||||
product,
|
||||
reorderProductImages.mutate
|
||||
);
|
||||
|
||||
const disableFormSave =
|
||||
createProductImage.opts.loading ||
|
||||
deleteProduct.opts.loading ||
|
||||
reorderProductImages.opts.loading ||
|
||||
updateProduct.opts.loading ||
|
||||
loading;
|
||||
const formTransitionState = getMutationState(
|
||||
updateProduct.opts.called ||
|
||||
updateSimpleProduct.opts.called,
|
||||
updateProduct.opts.loading ||
|
||||
updateSimpleProduct.opts.loading,
|
||||
maybe(
|
||||
() => updateProduct.opts.data.productUpdate.errors
|
||||
),
|
||||
maybe(
|
||||
() =>
|
||||
updateSimpleProduct.opts.data.productUpdate.errors
|
||||
),
|
||||
maybe(
|
||||
() =>
|
||||
updateSimpleProduct.opts.data.productVariantUpdate
|
||||
.errors
|
||||
)
|
||||
);
|
||||
const deleteTransitionState = getMutationState(
|
||||
deleteProduct.opts.called,
|
||||
deleteProduct.opts.loading,
|
||||
maybe(
|
||||
() => deleteProduct.opts.data.productDelete.errors
|
||||
)
|
||||
);
|
||||
|
||||
const bulkProductVariantDeleteTransitionState = getMutationState(
|
||||
bulkProductVariantDelete.opts.called,
|
||||
bulkProductVariantDelete.opts.loading,
|
||||
maybe(
|
||||
() =>
|
||||
bulkProductVariantDelete.opts.data
|
||||
.productVariantBulkDelete.errors
|
||||
)
|
||||
);
|
||||
|
||||
const categories = maybe(
|
||||
() => searchCategoriesOpts.data.search.edges,
|
||||
[]
|
||||
).map(edge => edge.node);
|
||||
const collections = maybe(
|
||||
() => searchCollectionsOpts.data.search.edges,
|
||||
[]
|
||||
).map(edge => edge.node);
|
||||
const errors = maybe(
|
||||
() => updateProduct.opts.data.productUpdate.errors,
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WindowTitle title={maybe(() => data.product.name)} />
|
||||
<ProductUpdatePage
|
||||
categories={categories}
|
||||
collections={collections}
|
||||
disabled={disableFormSave}
|
||||
errors={errors}
|
||||
fetchCategories={searchCategories}
|
||||
fetchCollections={searchCollections}
|
||||
saveButtonBarState={formTransitionState}
|
||||
images={maybe(() => data.product.images)}
|
||||
header={maybe(() => product.name)}
|
||||
placeholderImage={placeholderImg}
|
||||
product={product}
|
||||
variants={maybe(() => product.variants)}
|
||||
onBack={() => {
|
||||
navigate(productListUrl());
|
||||
}}
|
||||
onDelete={() => openModal("remove")}
|
||||
onProductShow={() => {
|
||||
if (product) {
|
||||
window.open(product.url);
|
||||
}
|
||||
}}
|
||||
onImageReorder={handleImageReorder}
|
||||
onSubmit={handleSubmit}
|
||||
onVariantAdd={handleVariantAdd}
|
||||
onVariantsAdd={handleVariantCreatorOpen}
|
||||
onVariantShow={variantId => () =>
|
||||
navigate(
|
||||
productVariantEditUrl(product.id, variantId)
|
||||
)}
|
||||
onImageUpload={handleImageUpload}
|
||||
onImageEdit={handleImageEdit}
|
||||
onImageDelete={handleImageDelete}
|
||||
toolbar={
|
||||
<IconButton
|
||||
color="primary"
|
||||
onClick={() =>
|
||||
navigate(
|
||||
productUrl(id, {
|
||||
action: "remove-variants",
|
||||
ids: listElements
|
||||
})
|
||||
)
|
||||
}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
}
|
||||
isChecked={isSelected}
|
||||
selected={listElements.length}
|
||||
toggle={toggle}
|
||||
toggleAll={toggleAll}
|
||||
fetchMoreCategories={{
|
||||
hasMore: maybe(
|
||||
() =>
|
||||
searchCategoriesOpts.data.search.pageInfo
|
||||
.hasNextPage
|
||||
),
|
||||
loading: searchCategoriesOpts.loading,
|
||||
onFetchMore: loadMoreCategories
|
||||
}}
|
||||
fetchMoreCollections={{
|
||||
hasMore: maybe(
|
||||
() =>
|
||||
searchCollectionsOpts.data.search.pageInfo
|
||||
.hasNextPage
|
||||
),
|
||||
loading: searchCollectionsOpts.loading,
|
||||
onFetchMore: loadMoreCollections
|
||||
}}
|
||||
/>
|
||||
<ActionDialog
|
||||
open={params.action === "remove"}
|
||||
onClose={() => navigate(productUrl(id), true)}
|
||||
confirmButtonState={deleteTransitionState}
|
||||
onConfirm={() => deleteProduct.mutate({ id })}
|
||||
variant="delete"
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Delete Product",
|
||||
description: "dialog header"
|
||||
})}
|
||||
>
|
||||
<DialogContentText>
|
||||
<FormattedMessage
|
||||
defaultMessage="Are you sure you want to delete {name}?"
|
||||
description="delete product"
|
||||
values={{
|
||||
name: product ? product.name : undefined
|
||||
}}
|
||||
/>
|
||||
</DialogContentText>
|
||||
</ActionDialog>
|
||||
<ActionDialog
|
||||
open={params.action === "remove-variants"}
|
||||
onClose={() => navigate(productUrl(id), true)}
|
||||
confirmButtonState={
|
||||
bulkProductVariantDeleteTransitionState
|
||||
}
|
||||
onConfirm={() =>
|
||||
bulkProductVariantDelete.mutate({
|
||||
ids: params.ids
|
||||
})
|
||||
}
|
||||
variant="delete"
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Delete Product Variants",
|
||||
description: "dialog header"
|
||||
})}
|
||||
>
|
||||
<DialogContentText>
|
||||
<FormattedMessage
|
||||
defaultMessage="Are you sure you want to delete {counter,plural,one{this variant} other{{displayQuantity} variants}}?"
|
||||
description="dialog content"
|
||||
values={{
|
||||
counter: maybe(() => params.ids.length),
|
||||
displayQuantity: (
|
||||
<strong>
|
||||
{maybe(() => params.ids.length)}
|
||||
</strong>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</DialogContentText>
|
||||
</ActionDialog>
|
||||
<ProductVariantCreateDialog
|
||||
defaultPrice={maybe(() =>
|
||||
data.product.basePrice.amount.toFixed(2)
|
||||
)}
|
||||
errors={maybe(
|
||||
() =>
|
||||
bulkProductVariantCreate.opts.data
|
||||
.productVariantBulkCreate.bulkProductErrors,
|
||||
[]
|
||||
)}
|
||||
open={params.action === "create-variants"}
|
||||
attributes={maybe(
|
||||
() => data.product.productType.variantAttributes,
|
||||
[]
|
||||
)}
|
||||
currencySymbol={maybe(() => shop.defaultCurrency)}
|
||||
onClose={() =>
|
||||
navigate(
|
||||
productUrl(id, {
|
||||
...params,
|
||||
action: undefined
|
||||
})
|
||||
)
|
||||
}
|
||||
onSubmit={inputs =>
|
||||
bulkProductVariantCreate.mutate({
|
||||
id,
|
||||
inputs
|
||||
})
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
variants={maybe(() => product.variants)}
|
||||
onBack={() => {
|
||||
navigate(productListUrl());
|
||||
}}
|
||||
</ProductUpdateOperations>
|
||||
);
|
||||
}}
|
||||
</TypedProductDetailsQuery>
|
||||
)}
|
||||
</SearchCollections>
|
||||
)}
|
||||
</SearchCategories>
|
||||
onDelete={() => openModal("remove")}
|
||||
onProductShow={() => {
|
||||
if (product) {
|
||||
window.open(product.url);
|
||||
}
|
||||
}}
|
||||
onImageReorder={handleImageReorder}
|
||||
onSubmit={handleSubmit}
|
||||
onVariantAdd={handleVariantAdd}
|
||||
onVariantsAdd={handleVariantCreatorOpen}
|
||||
onVariantShow={variantId => () =>
|
||||
navigate(productVariantEditUrl(product.id, variantId))}
|
||||
onImageUpload={handleImageUpload}
|
||||
onImageEdit={handleImageEdit}
|
||||
onImageDelete={handleImageDelete}
|
||||
toolbar={
|
||||
<IconButton
|
||||
color="primary"
|
||||
onClick={() =>
|
||||
navigate(
|
||||
productUrl(id, {
|
||||
action: "remove-variants",
|
||||
ids: listElements
|
||||
})
|
||||
)
|
||||
}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
}
|
||||
isChecked={isSelected}
|
||||
selected={listElements.length}
|
||||
toggle={toggle}
|
||||
toggleAll={toggleAll}
|
||||
fetchMoreCategories={{
|
||||
hasMore: maybe(
|
||||
() =>
|
||||
searchCategoriesOpts.data.search.pageInfo.hasNextPage
|
||||
),
|
||||
loading: searchCategoriesOpts.loading,
|
||||
onFetchMore: loadMoreCategories
|
||||
}}
|
||||
fetchMoreCollections={{
|
||||
hasMore: maybe(
|
||||
() =>
|
||||
searchCollectionsOpts.data.search.pageInfo.hasNextPage
|
||||
),
|
||||
loading: searchCollectionsOpts.loading,
|
||||
onFetchMore: loadMoreCollections
|
||||
}}
|
||||
/>
|
||||
<ActionDialog
|
||||
open={params.action === "remove"}
|
||||
onClose={() => navigate(productUrl(id), true)}
|
||||
confirmButtonState={deleteTransitionState}
|
||||
onConfirm={() => deleteProduct.mutate({ id })}
|
||||
variant="delete"
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Delete Product",
|
||||
description: "dialog header"
|
||||
})}
|
||||
>
|
||||
<DialogContentText>
|
||||
<FormattedMessage
|
||||
defaultMessage="Are you sure you want to delete {name}?"
|
||||
description="delete product"
|
||||
values={{
|
||||
name: product ? product.name : undefined
|
||||
}}
|
||||
/>
|
||||
</DialogContentText>
|
||||
</ActionDialog>
|
||||
<ActionDialog
|
||||
open={params.action === "remove-variants"}
|
||||
onClose={() => navigate(productUrl(id), true)}
|
||||
confirmButtonState={bulkProductVariantDeleteTransitionState}
|
||||
onConfirm={() =>
|
||||
bulkProductVariantDelete.mutate({
|
||||
ids: params.ids
|
||||
})
|
||||
}
|
||||
variant="delete"
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Delete Product Variants",
|
||||
description: "dialog header"
|
||||
})}
|
||||
>
|
||||
<DialogContentText>
|
||||
<FormattedMessage
|
||||
defaultMessage="Are you sure you want to delete {counter,plural,one{this variant} other{{displayQuantity} variants}}?"
|
||||
description="dialog content"
|
||||
values={{
|
||||
counter: maybe(() => params.ids.length),
|
||||
displayQuantity: (
|
||||
<strong>{maybe(() => params.ids.length)}</strong>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</DialogContentText>
|
||||
</ActionDialog>
|
||||
<ProductVariantCreateDialog
|
||||
defaultPrice={maybe(() =>
|
||||
data.product.basePrice.amount.toFixed(2)
|
||||
)}
|
||||
errors={maybe(
|
||||
() =>
|
||||
bulkProductVariantCreate.opts.data
|
||||
.productVariantBulkCreate.bulkProductErrors,
|
||||
[]
|
||||
)}
|
||||
open={params.action === "create-variants"}
|
||||
attributes={maybe(
|
||||
() => data.product.productType.variantAttributes,
|
||||
[]
|
||||
)}
|
||||
currencySymbol={maybe(() => shop.defaultCurrency)}
|
||||
onClose={() =>
|
||||
navigate(
|
||||
productUrl(id, {
|
||||
...params,
|
||||
action: undefined
|
||||
})
|
||||
)
|
||||
}
|
||||
onSubmit={inputs =>
|
||||
bulkProductVariantCreate.mutate({
|
||||
id,
|
||||
inputs
|
||||
})
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</ProductUpdateOperations>
|
||||
);
|
||||
}}
|
||||
</TypedProductDetailsQuery>
|
||||
);
|
||||
};
|
||||
export default ProductUpdate;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AttributeInputTypeEnum } from "./../../../types/globalTypes";
|
||||
import { AttributeInputTypeEnum } from "./../../types/globalTypes";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: SearchProductTypes
|
||||
|
@ -22,7 +22,9 @@ export interface SearchProductTypes_search_edges_node_productAttributes {
|
|||
slug: string | null;
|
||||
name: string | null;
|
||||
valueRequired: boolean;
|
||||
values: (SearchProductTypes_search_edges_node_productAttributes_values | null)[] | null;
|
||||
values:
|
||||
| (SearchProductTypes_search_edges_node_productAttributes_values | null)[]
|
||||
| null;
|
||||
}
|
||||
|
||||
export interface SearchProductTypes_search_edges_node {
|
||||
|
@ -30,7 +32,9 @@ export interface SearchProductTypes_search_edges_node {
|
|||
id: string;
|
||||
name: string;
|
||||
hasVariants: boolean;
|
||||
productAttributes: (SearchProductTypes_search_edges_node_productAttributes | null)[] | null;
|
||||
productAttributes:
|
||||
| (SearchProductTypes_search_edges_node_productAttributes | null)[]
|
||||
| null;
|
||||
}
|
||||
|
||||
export interface SearchProductTypes_search_edges {
|
|
@ -1,7 +1,7 @@
|
|||
import gql from "graphql-tag";
|
||||
|
||||
import makeTopLevelSearch from "@saleor/hooks/makeTopLevelSearch";
|
||||
import { pageInfoFragment } from "@saleor/queries";
|
||||
import TopLevelSearch from "../TopLevelSearch";
|
||||
import {
|
||||
SearchCategories,
|
||||
SearchCategoriesVariables
|
||||
|
@ -24,6 +24,6 @@ export const searchCategories = gql`
|
|||
}
|
||||
`;
|
||||
|
||||
export default TopLevelSearch<SearchCategories, SearchCategoriesVariables>(
|
||||
export default makeTopLevelSearch<SearchCategories, SearchCategoriesVariables>(
|
||||
searchCategories
|
||||
);
|
|
@ -1,7 +1,7 @@
|
|||
import gql from "graphql-tag";
|
||||
|
||||
import makeTopLevelSearch from "@saleor/hooks/makeTopLevelSearch";
|
||||
import { pageInfoFragment } from "@saleor/queries";
|
||||
import TopLevelSearch from "../TopLevelSearch";
|
||||
import {
|
||||
SearchCollections,
|
||||
SearchCollectionsVariables
|
||||
|
@ -24,6 +24,7 @@ export const searchCollections = gql`
|
|||
}
|
||||
`;
|
||||
|
||||
export default TopLevelSearch<SearchCollections, SearchCollectionsVariables>(
|
||||
searchCollections
|
||||
);
|
||||
export default makeTopLevelSearch<
|
||||
SearchCollections,
|
||||
SearchCollectionsVariables
|
||||
>(searchCollections);
|
|
@ -1,7 +1,7 @@
|
|||
import gql from "graphql-tag";
|
||||
|
||||
import makeTopLevelSearch from "@saleor/hooks/makeTopLevelSearch";
|
||||
import { pageInfoFragment } from "@saleor/queries";
|
||||
import TopLevelSearch from "../TopLevelSearch";
|
||||
import {
|
||||
SearchCustomers,
|
||||
SearchCustomersVariables
|
||||
|
@ -24,6 +24,6 @@ export const searchCustomers = gql`
|
|||
}
|
||||
`;
|
||||
|
||||
export default TopLevelSearch<SearchCustomers, SearchCustomersVariables>(
|
||||
export default makeTopLevelSearch<SearchCustomers, SearchCustomersVariables>(
|
||||
searchCustomers
|
||||
);
|
|
@ -1,7 +1,7 @@
|
|||
import gql from "graphql-tag";
|
||||
|
||||
import makeTopLevelSearch from "@saleor/hooks/makeTopLevelSearch";
|
||||
import { pageInfoFragment } from "@saleor/queries";
|
||||
import TopLevelSearch from "../TopLevelSearch";
|
||||
import { SearchPages, SearchPagesVariables } from "./types/SearchPages";
|
||||
|
||||
export const searchPages = gql`
|
||||
|
@ -21,4 +21,6 @@ export const searchPages = gql`
|
|||
}
|
||||
`;
|
||||
|
||||
export default TopLevelSearch<SearchPages, SearchPagesVariables>(searchPages);
|
||||
export default makeTopLevelSearch<SearchPages, SearchPagesVariables>(
|
||||
searchPages
|
||||
);
|
|
@ -1,7 +1,7 @@
|
|||
import gql from "graphql-tag";
|
||||
|
||||
import makeTopLevelSearch from "@saleor/hooks/makeTopLevelSearch";
|
||||
import { pageInfoFragment } from "@saleor/queries";
|
||||
import TopLevelSearch from "../TopLevelSearch";
|
||||
import {
|
||||
SearchProducts,
|
||||
SearchProductsVariables
|
||||
|
@ -27,6 +27,6 @@ export const searchProducts = gql`
|
|||
}
|
||||
`;
|
||||
|
||||
export default TopLevelSearch<SearchProducts, SearchProductsVariables>(
|
||||
export default makeTopLevelSearch<SearchProducts, SearchProductsVariables>(
|
||||
searchProducts
|
||||
);
|
|
@ -1,7 +1,7 @@
|
|||
import gql from "graphql-tag";
|
||||
|
||||
import makeTopLevelSearch from "@saleor/hooks/makeTopLevelSearch";
|
||||
import { pageInfoFragment } from "@saleor/queries";
|
||||
import TopLevelSearch from "../TopLevelSearch";
|
||||
import {
|
||||
SearchProductTypes,
|
||||
SearchProductTypesVariables
|
||||
|
@ -41,6 +41,7 @@ export const searchProductTypes = gql`
|
|||
}
|
||||
`;
|
||||
|
||||
export default TopLevelSearch<SearchProductTypes, SearchProductTypesVariables>(
|
||||
searchProductTypes
|
||||
);
|
||||
export default makeTopLevelSearch<
|
||||
SearchProductTypes,
|
||||
SearchProductTypesVariables
|
||||
>(searchProductTypes);
|
|
@ -1,7 +1,7 @@
|
|||
import gql from "graphql-tag";
|
||||
|
||||
import makeTopLevelSearch from "@saleor/hooks/makeTopLevelSearch";
|
||||
import { pageInfoFragment } from "@saleor/queries";
|
||||
import TopLevelSearch from "../TopLevelSearch";
|
||||
import {
|
||||
SearchServiceAccount,
|
||||
SearchServiceAccountVariables
|
||||
|
@ -28,7 +28,7 @@ export const searchServiceAccount = gql`
|
|||
}
|
||||
`;
|
||||
|
||||
export default TopLevelSearch<
|
||||
export default makeTopLevelSearch<
|
||||
SearchServiceAccount,
|
||||
SearchServiceAccountVariables
|
||||
>(searchServiceAccount);
|
|
@ -125505,6 +125505,7 @@ exports[`Storyshots Views / Webhooks / Create webhook default 1`] = `
|
|||
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
|
||||
name="name"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<fieldset
|
||||
aria-hidden="true"
|
||||
|
@ -126037,6 +126038,7 @@ exports[`Storyshots Views / Webhooks / Create webhook form errors 1`] = `
|
|||
class="MuiInputBase-input-id MuiOutlinedInput-input-id"
|
||||
name="name"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<fieldset
|
||||
aria-hidden="true"
|
||||
|
@ -126575,6 +126577,7 @@ exports[`Storyshots Views / Webhooks / Create webhook loading 1`] = `
|
|||
disabled=""
|
||||
name="name"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<fieldset
|
||||
aria-hidden="true"
|
||||
|
|
|
@ -6,9 +6,9 @@ import FormSpacer from "@saleor/components/FormSpacer";
|
|||
import Grid from "@saleor/components/Grid";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
||||
import { SearchServiceAccount_search_edges_node } from "@saleor/containers/SearchServiceAccount/types/SearchServiceAccount";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { maybe } from "@saleor/misc";
|
||||
import { SearchServiceAccount_search_edges_node } from "@saleor/searches/types/SearchServiceAccount";
|
||||
import { WebhookEventTypeEnum } from "@saleor/types/globalTypes";
|
||||
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
|
||||
import WebhookEvents from "@saleor/webhooks/components/WebhookEvents";
|
||||
|
@ -19,7 +19,6 @@ import React from "react";
|
|||
import { useIntl } from "react-intl";
|
||||
|
||||
export interface FormData {
|
||||
id: string;
|
||||
events: WebhookEventTypeEnum[];
|
||||
isActive: boolean;
|
||||
name: string;
|
||||
|
@ -52,9 +51,8 @@ const WebhookCreatePage: React.FC<WebhookCreatePageProps> = ({
|
|||
const initialForm: FormData = {
|
||||
allEvents: false,
|
||||
events: [],
|
||||
id: null,
|
||||
isActive: false,
|
||||
name: null,
|
||||
name: "",
|
||||
secretKey: "",
|
||||
serviceAccount: "",
|
||||
targetUrl: ""
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import AppHeader from "@saleor/components/AppHeader";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
|
||||
import Container from "@saleor/components/Container";
|
||||
|
@ -6,10 +9,10 @@ import FormSpacer from "@saleor/components/FormSpacer";
|
|||
import Grid from "@saleor/components/Grid";
|
||||
import PageHeader from "@saleor/components/PageHeader";
|
||||
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
||||
import { SearchServiceAccount_search_edges_node } from "@saleor/containers/SearchServiceAccount/types/SearchServiceAccount";
|
||||
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
||||
import { sectionNames } from "@saleor/intl";
|
||||
import { maybe } from "@saleor/misc";
|
||||
import { SearchServiceAccount_search_edges_node } from "@saleor/searches/types/SearchServiceAccount";
|
||||
import { WebhookEventTypeEnum } from "@saleor/types/globalTypes";
|
||||
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
|
||||
import WebhookEvents from "@saleor/webhooks/components/WebhookEvents";
|
||||
|
@ -18,11 +21,7 @@ import WebhookStatus from "@saleor/webhooks/components/WebhookStatus";
|
|||
import { WebhookCreate_webhookCreate_webhookErrors } from "@saleor/webhooks/types/WebhookCreate";
|
||||
import { WebhookDetails_webhook } from "@saleor/webhooks/types/WebhookDetails";
|
||||
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
export interface FormData {
|
||||
id: string;
|
||||
events: WebhookEventTypeEnum[];
|
||||
isActive: boolean;
|
||||
name: string;
|
||||
|
@ -63,7 +62,6 @@ const WebhooksDetailsPage: React.FC<WebhooksDetailsPageProps> = ({
|
|||
events: maybe(() => webhook.events, [])
|
||||
.map(event => event.eventType)
|
||||
.filter(event => event !== WebhookEventTypeEnum.ANY_EVENTS),
|
||||
id: maybe(() => webhook.id, null),
|
||||
isActive: maybe(() => webhook.isActive, false),
|
||||
name: maybe(() => webhook.name, ""),
|
||||
secretKey: maybe(() => webhook.secretKey, ""),
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||
import SearchServiceAccount from "@saleor/containers/SearchServiceAccount";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
import { commonMessages } from "@saleor/intl";
|
||||
import useServiceAccountSearch from "@saleor/searches/useServiceAccountSearch";
|
||||
import { WebhookEventTypeEnum } from "@saleor/types/globalTypes";
|
||||
import { WebhookCreate as WebhookCreateData } from "@saleor/webhooks/types/WebhookCreate";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "../../config";
|
||||
import { getMutationState, maybe } from "../../misc";
|
||||
import WebhookCreatePage, { FormData } from "../components/WebhookCreatePage";
|
||||
import { TypedWebhookCreate } from "../mutations";
|
||||
|
@ -26,6 +26,12 @@ export const WebhooksCreate: React.FC<WebhooksCreateProps> = () => {
|
|||
const navigate = useNavigator();
|
||||
const notify = useNotifier();
|
||||
const intl = useIntl();
|
||||
const {
|
||||
search: searchServiceAccount,
|
||||
result: searchServiceAccountOpt
|
||||
} = useServiceAccountSearch({
|
||||
variables: DEFAULT_INITIAL_SEARCH_DATA
|
||||
});
|
||||
|
||||
const onSubmit = (data: WebhookCreateData) => {
|
||||
if (data.webhookCreate.webhookErrors.length === 0) {
|
||||
|
@ -39,62 +45,56 @@ export const WebhooksCreate: React.FC<WebhooksCreateProps> = () => {
|
|||
const handleBack = () => navigate(webhooksListUrl());
|
||||
|
||||
return (
|
||||
<SearchServiceAccount variables={DEFAULT_INITIAL_SEARCH_DATA}>
|
||||
{({ search: searchServiceAccount, result: searchServiceAccountOpt }) => (
|
||||
<TypedWebhookCreate onCompleted={onSubmit}>
|
||||
{(webhookCreate, webhookCreateOpts) => {
|
||||
const handleSubmit = (data: FormData) =>
|
||||
webhookCreate({
|
||||
variables: {
|
||||
input: {
|
||||
events: data.allEvents
|
||||
? [WebhookEventTypeEnum.ANY_EVENTS]
|
||||
: data.events,
|
||||
isActive: data.isActive,
|
||||
name: data.name,
|
||||
secretKey: data.secretKey,
|
||||
serviceAccount: data.serviceAccount,
|
||||
targetUrl: data.targetUrl
|
||||
}
|
||||
}
|
||||
});
|
||||
<TypedWebhookCreate onCompleted={onSubmit}>
|
||||
{(webhookCreate, webhookCreateOpts) => {
|
||||
const handleSubmit = (data: FormData) =>
|
||||
webhookCreate({
|
||||
variables: {
|
||||
input: {
|
||||
events: data.allEvents
|
||||
? [WebhookEventTypeEnum.ANY_EVENTS]
|
||||
: data.events,
|
||||
isActive: data.isActive,
|
||||
name: data.name,
|
||||
secretKey: data.secretKey,
|
||||
serviceAccount: data.serviceAccount,
|
||||
targetUrl: data.targetUrl
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const formTransitionState = getMutationState(
|
||||
webhookCreateOpts.called,
|
||||
webhookCreateOpts.loading,
|
||||
maybe(() => webhookCreateOpts.data.webhookCreate.webhookErrors)
|
||||
);
|
||||
const formTransitionState = getMutationState(
|
||||
webhookCreateOpts.called,
|
||||
webhookCreateOpts.loading,
|
||||
maybe(() => webhookCreateOpts.data.webhookCreate.webhookErrors)
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WindowTitle
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Create Webhook",
|
||||
description: "window title"
|
||||
})}
|
||||
/>
|
||||
<WebhookCreatePage
|
||||
disabled={false}
|
||||
errors={maybe(
|
||||
() => webhookCreateOpts.data.webhookCreate.webhookErrors,
|
||||
[]
|
||||
)}
|
||||
fetchServiceAccounts={searchServiceAccount}
|
||||
services={maybe(() =>
|
||||
searchServiceAccountOpt.data.search.edges.map(
|
||||
edge => edge.node
|
||||
)
|
||||
)}
|
||||
onBack={handleBack}
|
||||
onSubmit={handleSubmit}
|
||||
saveButtonBarState={formTransitionState}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</TypedWebhookCreate>
|
||||
)}
|
||||
</SearchServiceAccount>
|
||||
return (
|
||||
<>
|
||||
<WindowTitle
|
||||
title={intl.formatMessage({
|
||||
defaultMessage: "Create Webhook",
|
||||
description: "window title"
|
||||
})}
|
||||
/>
|
||||
<WebhookCreatePage
|
||||
disabled={false}
|
||||
errors={maybe(
|
||||
() => webhookCreateOpts.data.webhookCreate.webhookErrors,
|
||||
[]
|
||||
)}
|
||||
fetchServiceAccounts={searchServiceAccount}
|
||||
services={maybe(() =>
|
||||
searchServiceAccountOpt.data.search.edges.map(edge => edge.node)
|
||||
)}
|
||||
onBack={handleBack}
|
||||
onSubmit={handleSubmit}
|
||||
saveButtonBarState={formTransitionState}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</TypedWebhookCreate>
|
||||
);
|
||||
};
|
||||
WebhooksCreate.displayName = "WebhooksCreate";
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||
import SearchServiceAccount from "@saleor/containers/SearchServiceAccount";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
|
||||
import useNavigator from "@saleor/hooks/useNavigator";
|
||||
import useNotifier from "@saleor/hooks/useNotifier";
|
||||
import { commonMessages } from "@saleor/intl";
|
||||
import useServiceAccountSearch from "@saleor/searches/useServiceAccountSearch";
|
||||
import { WebhookEventTypeEnum } from "@saleor/types/globalTypes";
|
||||
import WebhookDeleteDialog from "@saleor/webhooks/components/WebhookDeleteDialog";
|
||||
import { WebhookDelete } from "@saleor/webhooks/types/WebhookDelete";
|
||||
import { WebhookUpdate } from "@saleor/webhooks/types/WebhookUpdate";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "../../config";
|
||||
import { getMutationState, maybe } from "../../misc";
|
||||
import WebhooksDetailsPage from "../components/WebhooksDetailsPage";
|
||||
import { TypedWebhookDelete, TypedWebhookUpdate } from "../mutations";
|
||||
|
@ -33,6 +34,12 @@ export const WebhooksDetails: React.FC<WebhooksDetailsProps> = ({
|
|||
const navigate = useNavigator();
|
||||
const notify = useNotifier();
|
||||
const intl = useIntl();
|
||||
const {
|
||||
search: searchServiceAccount,
|
||||
result: searchServiceAccountOpt
|
||||
} = useServiceAccountSearch({
|
||||
variables: DEFAULT_INITIAL_SEARCH_DATA
|
||||
});
|
||||
|
||||
const closeModal = () =>
|
||||
navigate(
|
||||
|
@ -72,96 +79,92 @@ export const WebhooksDetails: React.FC<WebhooksDetailsProps> = ({
|
|||
};
|
||||
|
||||
return (
|
||||
<SearchServiceAccount variables={DEFAULT_INITIAL_SEARCH_DATA}>
|
||||
{({ search: searchServiceAccount, result: searchServiceAccountOpt }) => (
|
||||
<TypedWebhookUpdate onCompleted={onWebhookUpdate}>
|
||||
{(webhookUpdate, webhookUpdateOpts) => (
|
||||
<TypedWebhookDelete onCompleted={onWebhookDelete}>
|
||||
{(webhookDelete, webhookDeleteOpts) => (
|
||||
<TypedWebhooksDetailsQuery variables={{ id }}>
|
||||
{webhookDetails => {
|
||||
const formTransitionState = getMutationState(
|
||||
webhookUpdateOpts.called,
|
||||
webhookUpdateOpts.loading,
|
||||
maybe(
|
||||
() => webhookUpdateOpts.data.webhookUpdate.webhookErrors
|
||||
)
|
||||
);
|
||||
<TypedWebhookUpdate onCompleted={onWebhookUpdate}>
|
||||
{(webhookUpdate, webhookUpdateOpts) => (
|
||||
<TypedWebhookDelete onCompleted={onWebhookDelete}>
|
||||
{(webhookDelete, webhookDeleteOpts) => (
|
||||
<TypedWebhooksDetailsQuery variables={{ id }}>
|
||||
{webhookDetails => {
|
||||
const formTransitionState = getMutationState(
|
||||
webhookUpdateOpts.called,
|
||||
webhookUpdateOpts.loading,
|
||||
maybe(
|
||||
() => webhookUpdateOpts.data.webhookUpdate.webhookErrors
|
||||
)
|
||||
);
|
||||
|
||||
const handleRemoveConfirm = () =>
|
||||
webhookDelete({
|
||||
variables: {
|
||||
id
|
||||
}
|
||||
});
|
||||
const handleRemoveConfirm = () =>
|
||||
webhookDelete({
|
||||
variables: {
|
||||
id
|
||||
}
|
||||
});
|
||||
|
||||
const formErrors = maybe(
|
||||
() => webhookUpdateOpts.data.webhookUpdate.webhookErrors,
|
||||
[]
|
||||
);
|
||||
const formErrors = maybe(
|
||||
() => webhookUpdateOpts.data.webhookUpdate.webhookErrors,
|
||||
[]
|
||||
);
|
||||
|
||||
const deleteTransitionState = getMutationState(
|
||||
webhookDeleteOpts.called,
|
||||
webhookDeleteOpts.loading,
|
||||
maybe(() => webhookDeleteOpts.data.webhookDelete.errors)
|
||||
);
|
||||
const deleteTransitionState = getMutationState(
|
||||
webhookDeleteOpts.called,
|
||||
webhookDeleteOpts.loading,
|
||||
maybe(() => webhookDeleteOpts.data.webhookDelete.errors)
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WindowTitle
|
||||
title={maybe(() => webhookDetails.data.webhook.name)}
|
||||
/>
|
||||
<WebhooksDetailsPage
|
||||
disabled={webhookDetails.loading}
|
||||
errors={formErrors}
|
||||
saveButtonBarState={formTransitionState}
|
||||
webhook={maybe(() => webhookDetails.data.webhook)}
|
||||
fetchServiceAccounts={searchServiceAccount}
|
||||
services={maybe(() =>
|
||||
searchServiceAccountOpt.data.search.edges.map(
|
||||
edge => edge.node
|
||||
)
|
||||
)}
|
||||
onBack={() => navigate(webhooksListUrl())}
|
||||
onDelete={() => openModal("remove")}
|
||||
onSubmit={data => {
|
||||
webhookUpdate({
|
||||
variables: {
|
||||
id,
|
||||
input: {
|
||||
events: data.allEvents
|
||||
? [WebhookEventTypeEnum.ANY_EVENTS]
|
||||
: data.events,
|
||||
isActive: data.isActive,
|
||||
name: data.name,
|
||||
secretKey: data.secretKey,
|
||||
serviceAccount: data.serviceAccount,
|
||||
targetUrl: data.targetUrl
|
||||
}
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<WebhookDeleteDialog
|
||||
confirmButtonState={deleteTransitionState}
|
||||
name={maybe(
|
||||
() => webhookDetails.data.webhook.name,
|
||||
"..."
|
||||
)}
|
||||
onClose={closeModal}
|
||||
onConfirm={handleRemoveConfirm}
|
||||
open={params.action === "remove"}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</TypedWebhooksDetailsQuery>
|
||||
)}
|
||||
</TypedWebhookDelete>
|
||||
return (
|
||||
<>
|
||||
<WindowTitle
|
||||
title={maybe(() => webhookDetails.data.webhook.name)}
|
||||
/>
|
||||
<WebhooksDetailsPage
|
||||
disabled={webhookDetails.loading}
|
||||
errors={formErrors}
|
||||
saveButtonBarState={formTransitionState}
|
||||
webhook={maybe(() => webhookDetails.data.webhook)}
|
||||
fetchServiceAccounts={searchServiceAccount}
|
||||
services={maybe(() =>
|
||||
searchServiceAccountOpt.data.search.edges.map(
|
||||
edge => edge.node
|
||||
)
|
||||
)}
|
||||
onBack={() => navigate(webhooksListUrl())}
|
||||
onDelete={() => openModal("remove")}
|
||||
onSubmit={data => {
|
||||
webhookUpdate({
|
||||
variables: {
|
||||
id,
|
||||
input: {
|
||||
events: data.allEvents
|
||||
? [WebhookEventTypeEnum.ANY_EVENTS]
|
||||
: data.events,
|
||||
isActive: data.isActive,
|
||||
name: data.name,
|
||||
secretKey: data.secretKey,
|
||||
serviceAccount: data.serviceAccount,
|
||||
targetUrl: data.targetUrl
|
||||
}
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<WebhookDeleteDialog
|
||||
confirmButtonState={deleteTransitionState}
|
||||
name={maybe(
|
||||
() => webhookDetails.data.webhook.name,
|
||||
"..."
|
||||
)}
|
||||
onClose={closeModal}
|
||||
onConfirm={handleRemoveConfirm}
|
||||
open={params.action === "remove"}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</TypedWebhooksDetailsQuery>
|
||||
)}
|
||||
</TypedWebhookUpdate>
|
||||
</TypedWebhookDelete>
|
||||
)}
|
||||
</SearchServiceAccount>
|
||||
</TypedWebhookUpdate>
|
||||
);
|
||||
};
|
||||
WebhooksDetails.displayName = "WebhooksDetails";
|
||||
|
|
Loading…
Reference in a new issue