Use category search hook

This commit is contained in:
dominik-zeglen 2019-11-19 16:47:12 +01:00
parent 0a7f65727a
commit e67727af03
11 changed files with 835 additions and 871 deletions

View file

@ -19,7 +19,7 @@ import FormSpacer from "@saleor/components/FormSpacer";
import ResponsiveTable from "@saleor/components/ResponsiveTable"; import ResponsiveTable from "@saleor/components/ResponsiveTable";
import useSearchQuery from "@saleor/hooks/useSearchQuery"; import useSearchQuery from "@saleor/hooks/useSearchQuery";
import { buttonMessages } from "@saleor/intl"; 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"; import Checkbox from "../Checkbox";
export interface FormData { export interface FormData {

View file

@ -16,10 +16,10 @@ import usePaginator, {
} from "@saleor/hooks/usePaginator"; } from "@saleor/hooks/usePaginator";
import useShop from "@saleor/hooks/useShop"; import useShop from "@saleor/hooks/useShop";
import { commonMessages, sectionNames } from "@saleor/intl"; import { commonMessages, sectionNames } from "@saleor/intl";
import useCategorySearch from "@saleor/searches/useCategorySearch";
import { categoryUrl } from "../../categories/urls"; import { categoryUrl } from "../../categories/urls";
import { collectionUrl } from "../../collections/urls"; import { collectionUrl } from "../../collections/urls";
import { DEFAULT_INITIAL_SEARCH_DATA, PAGINATE_BY } from "../../config"; import { DEFAULT_INITIAL_SEARCH_DATA, PAGINATE_BY } from "../../config";
import SearchCategories from "../../containers/SearchCategories";
import SearchCollections from "../../containers/SearchCollections"; import SearchCollections from "../../containers/SearchCollections";
import SearchProducts from "../../containers/SearchProducts"; import SearchProducts from "../../containers/SearchProducts";
import { decimal, getMutationState, joinDateTime, maybe } from "../../misc"; import { decimal, getMutationState, joinDateTime, maybe } from "../../misc";
@ -66,6 +66,12 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
params.ids params.ids
); );
const intl = useIntl(); const intl = useIntl();
const {
search: searchCategories,
result: searchCategoriesOpts
} = useCategorySearch({
variables: DEFAULT_INITIAL_SEARCH_DATA
});
const paginationState = createPaginationState(PAGINATE_BY, params); const paginationState = createPaginationState(PAGINATE_BY, params);
const changeTab = (tab: SaleDetailsPageTab) => { const changeTab = (tab: SaleDetailsPageTab) => {
@ -377,43 +383,33 @@ export const SaleDetails: React.FC<SaleDetailsProps> = ({ id, params }) => {
/> />
)} )}
</SearchProducts> </SearchProducts>
<SearchCategories <AssignCategoriesDialog
variables={DEFAULT_INITIAL_SEARCH_DATA} categories={maybe(() =>
> searchCategoriesOpts.data.search.edges
{({ .map(edge => edge.node)
search: searchCategories, .filter(
result: searchCategoriesOpts 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
)
}
}
})
}
/>
)} )}
</SearchCategories> 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
)
}
}
})
}
/>
<SearchCollections <SearchCollections
variables={DEFAULT_INITIAL_SEARCH_DATA} variables={DEFAULT_INITIAL_SEARCH_DATA}
> >

View file

@ -16,10 +16,10 @@ import usePaginator, {
} from "@saleor/hooks/usePaginator"; } from "@saleor/hooks/usePaginator";
import useShop from "@saleor/hooks/useShop"; import useShop from "@saleor/hooks/useShop";
import { commonMessages, sectionNames } from "@saleor/intl"; import { commonMessages, sectionNames } from "@saleor/intl";
import useCategorySearch from "@saleor/searches/useCategorySearch";
import { categoryUrl } from "../../categories/urls"; import { categoryUrl } from "../../categories/urls";
import { collectionUrl } from "../../collections/urls"; import { collectionUrl } from "../../collections/urls";
import { DEFAULT_INITIAL_SEARCH_DATA, PAGINATE_BY } from "../../config"; import { DEFAULT_INITIAL_SEARCH_DATA, PAGINATE_BY } from "../../config";
import SearchCategories from "../../containers/SearchCategories";
import SearchCollections from "../../containers/SearchCollections"; import SearchCollections from "../../containers/SearchCollections";
import SearchProducts from "../../containers/SearchProducts"; import SearchProducts from "../../containers/SearchProducts";
import { decimal, getMutationState, joinDateTime, maybe } from "../../misc"; import { decimal, getMutationState, joinDateTime, maybe } from "../../misc";
@ -68,6 +68,12 @@ export const VoucherDetails: React.FC<VoucherDetailsProps> = ({
params.ids params.ids
); );
const intl = useIntl(); const intl = useIntl();
const {
search: searchCategories,
result: searchCategoriesOpts
} = useCategorySearch({
variables: DEFAULT_INITIAL_SEARCH_DATA
});
const paginationState = createPaginationState(PAGINATE_BY, params); const paginationState = createPaginationState(PAGINATE_BY, params);
const changeTab = (tab: VoucherDetailsPageTab) => { const changeTab = (tab: VoucherDetailsPageTab) => {
@ -420,43 +426,33 @@ export const VoucherDetails: React.FC<VoucherDetailsProps> = ({
toggle={toggle} toggle={toggle}
toggleAll={toggleAll} toggleAll={toggleAll}
/> />
<SearchCategories <AssignCategoriesDialog
variables={DEFAULT_INITIAL_SEARCH_DATA} categories={maybe(() =>
> searchCategoriesOpts.data.search.edges
{({ .map(edge => edge.node)
search: searchCategories, .filter(
result: searchCategoriesOpts 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 =>
voucherCataloguesAdd({
variables: {
...paginationState,
id,
input: {
categories: categories.map(
product => product.id
)
}
}
})
}
/>
)} )}
</SearchCategories> 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
)
}
}
})
}
/>
<SearchCollections <SearchCollections
variables={DEFAULT_INITIAL_SEARCH_DATA} variables={DEFAULT_INITIAL_SEARCH_DATA}
> >

View file

@ -14,13 +14,13 @@ import ConfirmButton, {
ConfirmButtonTransitionState ConfirmButtonTransitionState
} from "@saleor/components/ConfirmButton"; } from "@saleor/components/ConfirmButton";
import FormSpacer from "@saleor/components/FormSpacer"; import FormSpacer from "@saleor/components/FormSpacer";
import { SearchCategories_search_edges_node } from "@saleor/containers/SearchCategories/types/SearchCategories";
import { SearchCollections_search_edges_node } from "@saleor/containers/SearchCollections/types/SearchCollections"; import { SearchCollections_search_edges_node } from "@saleor/containers/SearchCollections/types/SearchCollections";
import { SearchPages_search_edges_node } from "@saleor/containers/SearchPages/types/SearchPages"; import { SearchPages_search_edges_node } from "@saleor/containers/SearchPages/types/SearchPages";
import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors"; import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors";
import useModalDialogOpen from "@saleor/hooks/useModalDialogOpen"; import useModalDialogOpen from "@saleor/hooks/useModalDialogOpen";
import useStateFromProps from "@saleor/hooks/useStateFromProps"; import useStateFromProps from "@saleor/hooks/useStateFromProps";
import { buttonMessages, sectionNames } from "@saleor/intl"; import { buttonMessages, sectionNames } from "@saleor/intl";
import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories";
import { UserError } from "@saleor/types"; import { UserError } from "@saleor/types";
import { getErrors, getFieldError } from "@saleor/utils/errors"; import { getErrors, getFieldError } from "@saleor/utils/errors";
import { getMenuItemByValue, IMenu } from "@saleor/utils/menu"; import { getMenuItemByValue, IMenu } from "@saleor/utils/menu";

View file

@ -5,10 +5,10 @@ import { FormattedMessage, useIntl } from "react-intl";
import ActionDialog from "@saleor/components/ActionDialog"; import ActionDialog from "@saleor/components/ActionDialog";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
import useCategorySearch from "@saleor/searches/useCategorySearch";
import { categoryUrl } from "../../../categories/urls"; import { categoryUrl } from "../../../categories/urls";
import { collectionUrl } from "../../../collections/urls"; import { collectionUrl } from "../../../collections/urls";
import { DEFAULT_INITIAL_SEARCH_DATA } from "../../../config"; import { DEFAULT_INITIAL_SEARCH_DATA } from "../../../config";
import SearchCategories from "../../../containers/SearchCategories";
import SearchCollections from "../../../containers/SearchCollections"; import SearchCollections from "../../../containers/SearchCollections";
import SearchPages from "../../../containers/SearchPages"; import SearchPages from "../../../containers/SearchPages";
import { getMutationState, maybe } from "../../../misc"; import { getMutationState, maybe } from "../../../misc";
@ -59,6 +59,9 @@ const MenuDetails: React.FC<MenuDetailsProps> = ({ id, params }) => {
const navigate = useNavigator(); const navigate = useNavigator();
const notify = useNotifier(); const notify = useNotifier();
const intl = useIntl(); const intl = useIntl();
const categorySearch = useCategorySearch({
variables: DEFAULT_INITIAL_SEARCH_DATA
});
const closeModal = () => const closeModal = () =>
navigate( navigate(
@ -97,324 +100,304 @@ const MenuDetails: React.FC<MenuDetailsProps> = ({ id, params }) => {
return ( return (
<SearchPages variables={DEFAULT_INITIAL_SEARCH_DATA}> <SearchPages variables={DEFAULT_INITIAL_SEARCH_DATA}>
{pageSearch => ( {pageSearch => (
<SearchCategories variables={DEFAULT_INITIAL_SEARCH_DATA}> <SearchCollections variables={DEFAULT_INITIAL_SEARCH_DATA}>
{categorySearch => ( {collectionSearch => (
<SearchCollections variables={DEFAULT_INITIAL_SEARCH_DATA}> <MenuDetailsQuery displayLoader variables={{ id }}>
{collectionSearch => ( {({ data, loading, refetch }) => {
<MenuDetailsQuery displayLoader variables={{ id }}> const handleQueryChange = (query: string) => {
{({ data, loading, refetch }) => { categorySearch.search(query);
const handleQueryChange = (query: string) => { collectionSearch.search(query);
categorySearch.search(query); pageSearch.search(query);
collectionSearch.search(query); };
pageSearch.search(query);
};
const categories = maybe( const categories = maybe(
() => () =>
categorySearch.result.data.search.edges.map( categorySearch.result.data.search.edges.map(
edge => edge.node edge => edge.node
), ),
[] []
); );
const collections = maybe( const collections = maybe(
() => () =>
collectionSearch.result.data.search.edges.map( collectionSearch.result.data.search.edges.map(
edge => edge.node edge => edge.node
), ),
[] []
); );
const pages = maybe( const pages = maybe(
() => () =>
pageSearch.result.data.search.edges.map( pageSearch.result.data.search.edges.map(edge => edge.node),
edge => edge.node []
), );
[]
);
return ( return (
<MenuDeleteMutation <MenuDeleteMutation
onCompleted={data =>
handleDelete(data, navigate, notify, intl)
}
>
{(menuDelete, menuDeleteOpts) => (
<MenuUpdateMutation
onCompleted={data => onCompleted={data =>
handleDelete(data, navigate, notify, intl) handleUpdate(data, notify, refetch, intl)
} }
> >
{(menuDelete, menuDeleteOpts) => ( {(menuUpdate, menuUpdateOpts) => {
<MenuUpdateMutation const deleteState = getMutationState(
onCompleted={data => menuDeleteOpts.called,
handleUpdate(data, notify, refetch, intl) menuDeleteOpts.loading,
} maybe(() => menuDeleteOpts.data.menuDelete.errors)
> );
{(menuUpdate, menuUpdateOpts) => {
const deleteState = getMutationState(
menuDeleteOpts.called,
menuDeleteOpts.loading,
maybe(
() => menuDeleteOpts.data.menuDelete.errors
)
);
const updateState = getMutationState( const updateState = getMutationState(
menuUpdateOpts.called, menuUpdateOpts.called,
menuUpdateOpts.loading, menuUpdateOpts.loading,
maybe( maybe(() => menuUpdateOpts.data.menuUpdate.errors),
() => menuUpdateOpts.data.menuUpdate.errors maybe(() => menuUpdateOpts.data.menuItemMove.errors)
), );
maybe(
() => menuUpdateOpts.data.menuItemMove.errors
)
);
// This is a workaround to let know <MenuDetailsPage /> // This is a workaround to let know <MenuDetailsPage />
// that it should clean operation stack if mutations // that it should clean operation stack if mutations
// were successful // were successful
const handleSubmit = async ( const handleSubmit = async (
data: MenuDetailsSubmitData data: MenuDetailsSubmitData
) => { ) => {
try { try {
const result = await menuUpdate({ const result = await menuUpdate({
variables: { variables: {
id, id,
moves: getMoves(data), moves: getMoves(data),
name: data.name, name: data.name,
removeIds: getRemoveIds(data) removeIds: getRemoveIds(data)
} }
}); });
if (result) { if (result) {
if ( if (
result.data.menuItemBulkDelete.errors result.data.menuItemBulkDelete.errors.length >
.length > 0 || 0 ||
result.data.menuItemMove.errors.length > result.data.menuItemMove.errors.length > 0 ||
0 || result.data.menuUpdate.errors.length > 0
result.data.menuUpdate.errors.length > 0 ) {
) {
return false;
}
}
return true;
} catch {
return false; return false;
} }
}; }
return true;
} catch {
return false;
}
};
return ( return (
<> <>
<MenuDetailsPage <MenuDetailsPage
disabled={loading} disabled={loading}
menu={maybe(() => data.menu)} menu={maybe(() => data.menu)}
onBack={() => navigate(menuListUrl())} onBack={() => navigate(menuListUrl())}
onDelete={() => onDelete={() =>
navigate( navigate(
menuUrl(id, { menuUrl(id, {
action: "remove" 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>
) )
} }}
onItemAdd={() =>
navigate(
menuUrl(id, {
action: "add-item"
})
)
}
onItemClick={handleItemClick}
onItemEdit={itemId =>
navigate(
menuUrl(id, {
action: "edit-item",
id: itemId
})
)
}
onSubmit={handleSubmit}
saveButtonState={updateState}
/> />
<ActionDialog </DialogContentText>
open={params.action === "remove"} </ActionDialog>
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 <MenuItemCreateMutation
onCompleted={data => onCompleted={data =>
handleItemCreate( handleItemCreate(
data, data,
notify, notify,
closeModal, closeModal,
intl intl
) )
} }
> >
{(menuItemCreate, menuItemCreateOpts) => { {(menuItemCreate, menuItemCreateOpts) => {
const handleSubmit = ( const handleSubmit = (
data: MenuItemDialogFormData data: MenuItemDialogFormData
) => { ) => {
const variables: MenuItemCreateVariables = { const variables: MenuItemCreateVariables = {
input: getMenuItemCreateInputData( input: getMenuItemCreateInputData(
id,
data
)
};
menuItemCreate({ variables });
};
const formTransitionState = getMutationState(
menuItemCreateOpts.called,
menuItemCreateOpts.loading,
maybe(
() =>
menuItemCreateOpts.data
.menuItemCreate.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, id,
navigate, data
notify,
intl
) )
} };
>
{(menuItemUpdate, menuItemUpdateOpts) => {
const handleSubmit = (
data: MenuItemDialogFormData
) => {
const variables: MenuItemUpdateVariables = {
id: params.id,
input: getMenuItemInputData(data)
};
menuItemUpdate({ variables }); menuItemCreate({ variables });
}; };
const menuItem = maybe(() => const formTransitionState = getMutationState(
getNode( menuItemCreateOpts.called,
data.menu.items, menuItemCreateOpts.loading,
findNode(data.menu.items, params.id) maybe(
) () =>
); menuItemCreateOpts.data.menuItemCreate
.errors
)
);
const formTransitionState = getMutationState( return (
menuItemUpdateOpts.called, <MenuItemDialog
menuItemUpdateOpts.loading, open={params.action === "add-item"}
maybe( categories={categories}
() => collections={collections}
menuItemUpdateOpts.data errors={maybe(
.menuItemUpdate.errors () =>
) 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 = { menuItemUpdate({ variables });
id: maybe(() => getItemId(menuItem)), };
name: maybe(() => menuItem.name, "..."),
type: maybe<MenuItemType>(
() => getItemType(menuItem),
"category"
)
};
return ( const menuItem = maybe(() =>
<MenuItemDialog getNode(
open={params.action === "edit-item"} data.menu.items,
categories={categories} findNode(data.menu.items, params.id)
collections={collections} )
errors={maybe( );
() =>
menuItemUpdateOpts.data const formTransitionState = getMutationState(
.menuItemUpdate.errors, menuItemUpdateOpts.called,
[] menuItemUpdateOpts.loading,
)} maybe(
pages={pages} () =>
initial={initialFormData} menuItemUpdateOpts.data.menuItemUpdate
initialDisplayValue={getInitialDisplayValue( .errors
menuItem )
)} );
loading={
categorySearch.result.loading || const initialFormData: MenuItemDialogFormData = {
collectionSearch.result.loading id: maybe(() => getItemId(menuItem)),
} name: maybe(() => menuItem.name, "..."),
confirmButtonState={ type: maybe<MenuItemType>(
formTransitionState () => getItemType(menuItem),
} "category"
disabled={menuItemUpdateOpts.loading} )
onClose={closeModal} };
onSubmit={handleSubmit}
onQueryChange={handleQueryChange} return (
/> <MenuItemDialog
); open={params.action === "edit-item"}
}} categories={categories}
</MenuItemUpdateMutation> collections={collections}
</> errors={maybe(
); () =>
}} menuItemUpdateOpts.data.menuItemUpdate
</MenuUpdateMutation> .errors,
)} []
</MenuDeleteMutation> )}
); pages={pages}
}} initial={initialFormData}
</MenuDetailsQuery> initialDisplayValue={getInitialDisplayValue(
)} menuItem
</SearchCollections> )}
loading={
categorySearch.result.loading ||
collectionSearch.result.loading
}
confirmButtonState={formTransitionState}
disabled={menuItemUpdateOpts.loading}
onClose={closeModal}
onSubmit={handleSubmit}
onQueryChange={handleQueryChange}
/>
);
}}
</MenuItemUpdateMutation>
</>
);
}}
</MenuUpdateMutation>
)}
</MenuDeleteMutation>
);
}}
</MenuDetailsQuery>
)} )}
</SearchCategories> </SearchCollections>
)} )}
</SearchPages> </SearchPages>
); );

View file

@ -13,7 +13,6 @@ import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar"; import SaveButtonBar from "@saleor/components/SaveButtonBar";
import SeoForm from "@saleor/components/SeoForm"; import SeoForm from "@saleor/components/SeoForm";
import VisibilityCard from "@saleor/components/VisibilityCard"; import VisibilityCard from "@saleor/components/VisibilityCard";
import { SearchCategories_search_edges_node } from "@saleor/containers/SearchCategories/types/SearchCategories";
import { SearchCollections_search_edges_node } from "@saleor/containers/SearchCollections/types/SearchCollections"; import { SearchCollections_search_edges_node } from "@saleor/containers/SearchCollections/types/SearchCollections";
import { SearchProductTypes_search_edges_node_productAttributes } from "@saleor/containers/SearchProductTypes/types/SearchProductTypes"; import { SearchProductTypes_search_edges_node_productAttributes } from "@saleor/containers/SearchProductTypes/types/SearchProductTypes";
import useDateLocalize from "@saleor/hooks/useDateLocalize"; import useDateLocalize from "@saleor/hooks/useDateLocalize";
@ -25,6 +24,7 @@ import {
ProductAttributeValueChoices, ProductAttributeValueChoices,
ProductType ProductType
} from "@saleor/products/utils/data"; } from "@saleor/products/utils/data";
import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories";
import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler"; import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler";
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
import { FetchMoreProps, UserError } from "../../../types"; import { FetchMoreProps, UserError } from "../../../types";

View file

@ -12,13 +12,13 @@ import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar"; import SaveButtonBar from "@saleor/components/SaveButtonBar";
import SeoForm from "@saleor/components/SeoForm"; import SeoForm from "@saleor/components/SeoForm";
import VisibilityCard from "@saleor/components/VisibilityCard"; import VisibilityCard from "@saleor/components/VisibilityCard";
import { SearchCategories_search_edges_node } from "@saleor/containers/SearchCategories/types/SearchCategories";
import { SearchCollections_search_edges_node } from "@saleor/containers/SearchCollections/types/SearchCollections"; import { SearchCollections_search_edges_node } from "@saleor/containers/SearchCollections/types/SearchCollections";
import useDateLocalize from "@saleor/hooks/useDateLocalize"; import useDateLocalize from "@saleor/hooks/useDateLocalize";
import useFormset from "@saleor/hooks/useFormset"; import useFormset from "@saleor/hooks/useFormset";
import useStateFromProps from "@saleor/hooks/useStateFromProps"; import useStateFromProps from "@saleor/hooks/useStateFromProps";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { maybe } from "@saleor/misc"; import { maybe } from "@saleor/misc";
import { SearchCategories_search_edges_node } from "@saleor/searches/types/SearchCategories";
import { FetchMoreProps, ListActions, UserError } from "@saleor/types"; import { FetchMoreProps, ListActions, UserError } from "@saleor/types";
import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler"; import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler";
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler"; import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";

View file

@ -6,8 +6,8 @@ import SearchProductTypes from "@saleor/containers/SearchProductTypes";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
import useShop from "@saleor/hooks/useShop"; import useShop from "@saleor/hooks/useShop";
import useCategorySearch from "@saleor/searches/useCategorySearch";
import { DEFAULT_INITIAL_SEARCH_DATA } from "../../config"; import { DEFAULT_INITIAL_SEARCH_DATA } from "../../config";
import SearchCategories from "../../containers/SearchCategories";
import SearchCollections from "../../containers/SearchCollections"; import SearchCollections from "../../containers/SearchCollections";
import { decimal, getMutationState, maybe } from "../../misc"; import { decimal, getMutationState, maybe } from "../../misc";
import ProductCreatePage, { import ProductCreatePage, {
@ -26,174 +26,171 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = () => {
const notify = useNotifier(); const notify = useNotifier();
const shop = useShop(); const shop = useShop();
const intl = useIntl(); const intl = useIntl();
const {
loadMore: loadMoreCategories,
search: searchCategory,
result: searchCategoryOpts
} = useCategorySearch({
variables: DEFAULT_INITIAL_SEARCH_DATA
});
const handleBack = () => navigate(productListUrl()); const handleBack = () => navigate(productListUrl());
return ( return (
<SearchCategories variables={DEFAULT_INITIAL_SEARCH_DATA}> <SearchCollections variables={DEFAULT_INITIAL_SEARCH_DATA}>
{({ {({
loadMore: loadMoreCategories, loadMore: loadMoreCollections,
search: searchCategory, search: searchCollection,
result: searchCategoryOpts result: searchCollectionOpts
}) => ( }) => (
<SearchCollections variables={DEFAULT_INITIAL_SEARCH_DATA}> <SearchProductTypes variables={DEFAULT_INITIAL_SEARCH_DATA}>
{({ {({
loadMore: loadMoreCollections, loadMore: loadMoreProductTypes,
search: searchCollection, search: searchProductTypes,
result: searchCollectionOpts result: searchProductTypesOpts
}) => ( }) => {
<SearchProductTypes variables={DEFAULT_INITIAL_SEARCH_DATA}> const handleSuccess = (data: ProductCreate) => {
{({ if (data.productCreate.errors.length === 0) {
loadMore: loadMoreProductTypes, notify({
search: searchProductTypes, text: intl.formatMessage({
result: searchProductTypesOpts defaultMessage: "Product created"
}) => { })
const handleSuccess = (data: ProductCreate) => { });
if (data.productCreate.errors.length === 0) { navigate(productUrl(data.productCreate.product.id));
notify({ } else {
text: intl.formatMessage({ const attributeError = data.productCreate.errors.find(
defaultMessage: "Product created" err => err.field === "attributes"
})
});
navigate(productUrl(data.productCreate.product.id));
} else {
const attributeError = data.productCreate.errors.find(
err => err.field === "attributes"
);
if (!!attributeError) {
notify({ text: attributeError.message });
}
}
};
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>
); );
}} if (!!attributeError) {
</SearchProductTypes> notify({ text: attributeError.message });
)} }
</SearchCollections> }
};
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>
);
}}
</SearchProductTypes>
)} )}
</SearchCategories> </SearchCollections>
); );
}; };
export default ProductUpdate; export default ProductUpdate;

View file

@ -14,8 +14,8 @@ import useShop from "@saleor/hooks/useShop";
import { commonMessages } from "@saleor/intl"; import { commonMessages } from "@saleor/intl";
import ProductVariantCreateDialog from "@saleor/products/components/ProductVariantCreateDialog/ProductVariantCreateDialog"; import ProductVariantCreateDialog from "@saleor/products/components/ProductVariantCreateDialog/ProductVariantCreateDialog";
import { ProductVariantBulkCreate } from "@saleor/products/types/ProductVariantBulkCreate"; import { ProductVariantBulkCreate } from "@saleor/products/types/ProductVariantBulkCreate";
import useCategorySearch from "@saleor/searches/useCategorySearch";
import { DEFAULT_INITIAL_SEARCH_DATA } from "../../../config"; import { DEFAULT_INITIAL_SEARCH_DATA } from "../../../config";
import SearchCategories from "../../../containers/SearchCategories";
import SearchCollections from "../../../containers/SearchCollections"; import SearchCollections from "../../../containers/SearchCollections";
import { getMutationState, maybe } from "../../../misc"; import { getMutationState, maybe } from "../../../misc";
import ProductUpdatePage from "../../components/ProductUpdatePage"; import ProductUpdatePage from "../../components/ProductUpdatePage";
@ -55,6 +55,13 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
); );
const intl = useIntl(); const intl = useIntl();
const shop = useShop(); const shop = useShop();
const {
loadMore: loadMoreCategories,
search: searchCategories,
result: searchCategoriesOpts
} = useCategorySearch({
variables: DEFAULT_INITIAL_SEARCH_DATA
});
const openModal = (action: ProductUrlDialog) => const openModal = (action: ProductUrlDialog) =>
navigate( navigate(
@ -64,356 +71,341 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
); );
return ( return (
<SearchCategories variables={DEFAULT_INITIAL_SEARCH_DATA}> <SearchCollections variables={DEFAULT_INITIAL_SEARCH_DATA}>
{({ {({
loadMore: loadMoreCategories, loadMore: loadMoreCollections,
search: searchCategories, search: searchCollections,
result: searchCategoriesOpts result: searchCollectionsOpts
}) => ( }) => (
<SearchCollections variables={DEFAULT_INITIAL_SEARCH_DATA}> <TypedProductDetailsQuery
{({ displayLoader
loadMore: loadMoreCollections, require={["product"]}
search: searchCollections, variables={{ id }}
result: searchCollectionsOpts >
}) => ( {({ data, loading, refetch }) => {
<TypedProductDetailsQuery const handleDelete = () => {
displayLoader notify({
require={["product"]} text: intl.formatMessage({
variables={{ id }} defaultMessage: "Product removed"
> })
{({ data, loading, refetch }) => { });
const handleDelete = () => { navigate(productListUrl());
notify({ };
text: intl.formatMessage({ const handleUpdate = (data: ProductUpdateMutationResult) => {
defaultMessage: "Product removed" if (data.productUpdate.errors.length === 0) {
}) notify({
}); text: intl.formatMessage(commonMessages.savedChanges)
navigate(productListUrl()); });
}; } else {
const handleUpdate = (data: ProductUpdateMutationResult) => { const attributeError = data.productUpdate.errors.find(
if (data.productUpdate.errors.length === 0) { err => err.field === "attributes"
notify({ );
text: intl.formatMessage(commonMessages.savedChanges) if (!!attributeError) {
}); notify({ text: attributeError.message });
} else { }
const attributeError = data.productUpdate.errors.find( }
err => err.field === "attributes" };
);
if (!!attributeError) {
notify({ text: attributeError.message });
}
}
};
const handleImageCreate = (data: ProductImageCreate) => { const handleImageCreate = (data: ProductImageCreate) => {
const imageError = data.productImageCreate.errors.find( const imageError = data.productImageCreate.errors.find(
error => error =>
error.field === error.field === ("image" as keyof ProductImageCreateVariables)
("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 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 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
); );
if (imageError) { const handleImageUpload = createImageUploadHandler(
notify({ id,
text: imageError.message createProductImage.mutate
}); );
} const handleImageReorder = createImageReorderHandler(
}; product,
const handleImageDeleteSuccess = () => reorderProductImages.mutate
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 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 product = data ? data.product : undefined; const disableFormSave =
return ( createProductImage.opts.loading ||
<ProductUpdateOperations deleteProduct.opts.loading ||
product={product} reorderProductImages.opts.loading ||
onBulkProductVariantCreate={handleBulkProductVariantCreate} updateProduct.opts.loading ||
onBulkProductVariantDelete={handleBulkProductVariantDelete} loading;
onDelete={handleDelete} const formTransitionState = getMutationState(
onImageCreate={handleImageCreate} updateProduct.opts.called ||
onImageDelete={handleImageDeleteSuccess} updateSimpleProduct.opts.called,
onUpdate={handleUpdate} updateProduct.opts.loading ||
> updateSimpleProduct.opts.loading,
{({ maybe(() => updateProduct.opts.data.productUpdate.errors),
bulkProductVariantCreate, maybe(
bulkProductVariantDelete, () => updateSimpleProduct.opts.data.productUpdate.errors
createProductImage, ),
deleteProduct, maybe(
deleteProductImage, () =>
reorderProductImages, updateSimpleProduct.opts.data.productVariantUpdate
updateProduct, .errors
updateSimpleProduct )
}) => { );
const handleImageDelete = (id: string) => () => const deleteTransitionState = getMutationState(
deleteProductImage.mutate({ id }); deleteProduct.opts.called,
const handleImageEdit = (imageId: string) => () => deleteProduct.opts.loading,
navigate(productImageUrl(id, imageId)); maybe(() => deleteProduct.opts.data.productDelete.errors)
const handleSubmit = createUpdateHandler( );
product,
updateProduct.mutate,
updateSimpleProduct.mutate
);
const handleImageUpload = createImageUploadHandler(
id,
createProductImage.mutate
);
const handleImageReorder = createImageReorderHandler(
product,
reorderProductImages.mutate
);
const disableFormSave = const bulkProductVariantDeleteTransitionState = getMutationState(
createProductImage.opts.loading || bulkProductVariantDelete.opts.called,
deleteProduct.opts.loading || bulkProductVariantDelete.opts.loading,
reorderProductImages.opts.loading || maybe(
updateProduct.opts.loading || () =>
loading; bulkProductVariantDelete.opts.data
const formTransitionState = getMutationState( .productVariantBulkDelete.errors
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( const categories = maybe(
bulkProductVariantDelete.opts.called, () => searchCategoriesOpts.data.search.edges,
bulkProductVariantDelete.opts.loading, []
maybe( ).map(edge => edge.node);
() => const collections = maybe(
bulkProductVariantDelete.opts.data () => searchCollectionsOpts.data.search.edges,
.productVariantBulkDelete.errors []
) ).map(edge => edge.node);
); const errors = maybe(
() => updateProduct.opts.data.productUpdate.errors,
[]
);
const categories = maybe( return (
() => searchCategoriesOpts.data.search.edges, <>
[] <WindowTitle title={maybe(() => data.product.name)} />
).map(edge => edge.node); <ProductUpdatePage
const collections = maybe( categories={categories}
() => searchCollectionsOpts.data.search.edges, collections={collections}
[] disabled={disableFormSave}
).map(edge => edge.node); errors={errors}
const errors = maybe( fetchCategories={searchCategories}
() => updateProduct.opts.data.productUpdate.errors, fetchCollections={searchCollections}
[] saveButtonBarState={formTransitionState}
); images={maybe(() => data.product.images)}
header={maybe(() => product.name)}
return ( placeholderImage={placeholderImg}
<> product={product}
<WindowTitle title={maybe(() => data.product.name)} /> variants={maybe(() => product.variants)}
<ProductUpdatePage onBack={() => {
categories={categories} navigate(productListUrl());
collections={collections} }}
disabled={disableFormSave} onDelete={() => openModal("remove")}
errors={errors} onProductShow={() => {
fetchCategories={searchCategories} if (product) {
fetchCollections={searchCollections} window.open(product.url);
saveButtonBarState={formTransitionState} }
images={maybe(() => data.product.images)} }}
header={maybe(() => product.name)} onImageReorder={handleImageReorder}
placeholderImage={placeholderImg} onSubmit={handleSubmit}
product={product} onVariantAdd={handleVariantAdd}
variants={maybe(() => product.variants)} onVariantsAdd={handleVariantCreatorOpen}
onBack={() => { onVariantShow={variantId => () =>
navigate(productListUrl()); navigate(
}} productVariantEditUrl(product.id, variantId)
onDelete={() => openModal("remove")} )}
onProductShow={() => { onImageUpload={handleImageUpload}
if (product) { onImageEdit={handleImageEdit}
window.open(product.url); onImageDelete={handleImageDelete}
} toolbar={
}} <IconButton
onImageReorder={handleImageReorder} color="primary"
onSubmit={handleSubmit} onClick={() =>
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( navigate(
productUrl(id, { productUrl(id, {
...params, action: "remove-variants",
action: undefined ids: listElements
}) })
) )
} }
onSubmit={inputs => >
bulkProductVariantCreate.mutate({ <DeleteIcon />
id, </IconButton>
inputs }
}) 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
</ProductUpdateOperations> open={params.action === "remove-variants"}
); onClose={() => navigate(productUrl(id), true)}
}} confirmButtonState={
</TypedProductDetailsQuery> bulkProductVariantDeleteTransitionState
)} }
</SearchCollections> 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>
)} )}
</SearchCategories> </SearchCollections>
); );
}; };
export default ProductUpdate; export default ProductUpdate;

View file

@ -1,7 +1,7 @@
import gql from "graphql-tag"; import gql from "graphql-tag";
import makeTopLevelSearch from "@saleor/hooks/makeTopLevelSearch";
import { pageInfoFragment } from "@saleor/queries"; import { pageInfoFragment } from "@saleor/queries";
import TopLevelSearch from "../TopLevelSearch";
import { import {
SearchCategories, SearchCategories,
SearchCategoriesVariables SearchCategoriesVariables
@ -24,6 +24,6 @@ export const searchCategories = gql`
} }
`; `;
export default TopLevelSearch<SearchCategories, SearchCategoriesVariables>( export default makeTopLevelSearch<SearchCategories, SearchCategoriesVariables>(
searchCategories searchCategories
); );