import Button from "@material-ui/core/Button"; import DialogContentText from "@material-ui/core/DialogContentText"; import { useChannelsList } from "@saleor/channels/queries"; import { createCollectionChannels, createCollectionChannelsData } from "@saleor/channels/utils"; import ActionDialog from "@saleor/components/ActionDialog"; import AssignProductDialog from "@saleor/components/AssignProductDialog"; import ChannelsAvailabilityDialog from "@saleor/components/ChannelsAvailabilityDialog"; import NotFoundPage from "@saleor/components/NotFoundPage"; import { WindowTitle } from "@saleor/components/WindowTitle"; import { DEFAULT_INITIAL_SEARCH_DATA, PAGINATE_BY } from "@saleor/config"; import useBulkActions from "@saleor/hooks/useBulkActions"; import useChannels from "@saleor/hooks/useChannels"; import useLocalStorage from "@saleor/hooks/useLocalStorage"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import usePaginator, { createPaginationState } from "@saleor/hooks/usePaginator"; import { commonMessages } from "@saleor/intl"; import useProductSearch from "@saleor/searches/useProductSearch"; import { getParsedDataForJsonStringField } from "@saleor/translations/utils"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createMetadataUpdateHandler from "@saleor/utils/handlers/metadataUpdateHandler"; import { useMetadataUpdate, usePrivateMetadataUpdate } from "@saleor/utils/metadata/updateMetadata"; import { diff } from "fast-array-diff"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; import { getMutationState, maybe } from "../../misc"; import { productUrl } from "../../products/urls"; import { CollectionInput } from "../../types/globalTypes"; import CollectionDetailsPage from "../components/CollectionDetailsPage/CollectionDetailsPage"; import { CollectionUpdateData } from "../components/CollectionDetailsPage/form"; import { useCollectionAssignProductMutation, useCollectionChannelListingUpdate, useCollectionRemoveMutation, useCollectionUpdateMutation, useUnassignCollectionProductMutation } from "../mutations"; import { TypedCollectionDetailsQuery } from "../queries"; import { CollectionUpdate } from "../types/CollectionUpdate"; import { collectionListUrl, collectionUrl, CollectionUrlDialog, CollectionUrlQueryParams } from "../urls"; interface CollectionDetailsProps { id: string; params: CollectionUrlQueryParams; } export const CollectionDetails: React.FC = ({ id, params }) => { const navigate = useNavigator(); const notify = useNotifier(); const { isSelected, listElements, reset, toggle, toggleAll } = useBulkActions( params.ids ); const paginate = usePaginator(); const intl = useIntl(); const { search, loadMore, result } = useProductSearch({ variables: DEFAULT_INITIAL_SEARCH_DATA }); const [openModal, closeModal] = createDialogActionHandlers< CollectionUrlDialog, CollectionUrlQueryParams >(navigate, params => collectionUrl(id, params), params); const [updateMetadata] = useMetadataUpdate({}); const [updatePrivateMetadata] = usePrivateMetadataUpdate({}); const [ updateChannels, updateChannelsOpts ] = useCollectionChannelListingUpdate({}); const { data: channelsData } = useChannelsList({}); const handleCollectionUpdate = (data: CollectionUpdate) => { if (data.collectionUpdate.errors.length === 0) { notify({ status: "success", text: intl.formatMessage(commonMessages.savedChanges) }); navigate(collectionUrl(id)); } else { const backgroundImageError = data.collectionUpdate.errors.find( error => error.field === ("backgroundImage" as keyof CollectionInput) ); if (backgroundImageError) { notify({ status: "error", text: intl.formatMessage(commonMessages.somethingWentWrong) }); } } }; const [updateCollection, updateCollectionOpts] = useCollectionUpdateMutation({ onCompleted: handleCollectionUpdate }); const [assignProduct, assignProductOpts] = useCollectionAssignProductMutation( { onCompleted: data => { if (data.collectionAddProducts.errors.length === 0) { notify({ status: "success", text: intl.formatMessage({ defaultMessage: "Added product to collection" }) }); navigate(collectionUrl(id), true); } } } ); const [ unassignProduct, unassignProductOpts ] = useUnassignCollectionProductMutation({ onCompleted: data => { if (data.collectionRemoveProducts.errors.length === 0) { notify({ status: "success", text: intl.formatMessage({ defaultMessage: "Deleted product from collection" }) }); reset(); closeModal(); } } }); const [removeCollection, removeCollectionOpts] = useCollectionRemoveMutation({ onCompleted: data => { if (data.collectionDelete.errors.length === 0) { notify({ status: "success", text: intl.formatMessage({ defaultMessage: "Deleted collection" }) }); navigate(collectionListUrl()); } } }); const paginationState = createPaginationState(PAGINATE_BY, params); const handleBack = () => navigate(collectionListUrl()); const [selectedChannel] = useLocalStorage("collectionListChannel", ""); return ( {({ data, loading }) => { const collection = data?.collection; if (collection === null) { return ; } const allChannels = createCollectionChannels( channelsData?.channels )?.sort((channel, nextChannel) => channel.name.localeCompare(nextChannel.name) ); const collectionChannelsChoices = createCollectionChannelsData( collection ); const { channelListElements, channelsToggle, currentChannels, handleChannelsConfirm, handleChannelsModalClose, handleChannelsModalOpen, isChannelSelected, isChannelsModalOpen, setCurrentChannels, toggleAllChannels } = useChannels(collectionChannelsChoices, params?.action, { closeModal, openModal }); const handleUpdate = async (formData: CollectionUpdateData) => { const input: CollectionInput = { backgroundImageAlt: formData.backgroundImageAlt, description: getParsedDataForJsonStringField(formData.description), name: formData.name, seo: { description: formData.seoDescription, title: formData.seoTitle }, slug: formData.slug }; const result = await updateCollection({ variables: { id, input } }); const diffChannels = diff( collectionChannelsChoices, formData.channelListings, (a, b) => a.id === b.id ); updateChannels({ variables: { id: collection.id, input: { addChannels: formData.channelListings.map(channel => ({ channelId: channel.id, isPublished: channel.isPublished, publicationDate: channel.publicationDate })), removeChannels: diffChannels.removed?.map( removedChannel => removedChannel.id ) || [] } } }); return result.data.collectionUpdate.errors; }; const handleSubmit = createMetadataUpdateHandler( data?.collection, handleUpdate, variables => updateMetadata({ variables }), variables => updatePrivateMetadata({ variables }) ); const formTransitionState = getMutationState( updateCollectionOpts.called, updateCollectionOpts.loading, updateCollectionOpts.data?.collectionUpdate.errors ); const { loadNextPage, loadPreviousPage, pageInfo } = paginate( data?.collection?.products?.pageInfo, paginationState, params ); return ( <> {!!allChannels?.length && ( )} openModal("assign")} onBack={handleBack} disabled={loading || updateChannelsOpts.loading} collection={data?.collection} channelsErrors={ updateChannelsOpts?.data?.collectionChannelListingUpdate .errors || [] } errors={updateCollectionOpts?.data?.collectionUpdate.errors || []} onCollectionRemove={() => openModal("remove")} onImageDelete={() => openModal("removeImage")} onImageUpload={file => updateCollection({ variables: { id, input: { backgroundImage: file } } }) } onSubmit={handleSubmit} onNextPage={loadNextPage} onPreviousPage={loadPreviousPage} pageInfo={pageInfo} onProductUnassign={(productId, event) => { event.stopPropagation(); unassignProduct({ variables: { collectionId: id, productIds: [productId], ...paginationState } }); }} onRowClick={id => () => navigate(productUrl(id))} saveButtonBarState={formTransitionState} toolbar={ } isChecked={isSelected} selected={listElements.length} toggle={toggle} toggleAll={toggleAll} currentChannels={currentChannels} hasChannelChanged={ collectionChannelsChoices?.length !== currentChannels?.length } channelsCount={channelsData?.channels?.length} selectedChannelId={selectedChannel} openChannelsModal={handleChannelsModalOpen} onChannelsChange={setCurrentChannels} /> assignProduct({ variables: { ...paginationState, collectionId: id, productIds: products.map(product => product.id) } }) } products={maybe(() => result.data.search.edges .map(edge => edge.node) .filter(suggestedProduct => suggestedProduct.id) )} /> removeCollection({ variables: { id } }) } open={params.action === "remove"} title={intl.formatMessage({ defaultMessage: "Delete Collection", description: "dialog title" })} variant="delete" > {maybe(() => data.collection.name, "...")} ) }} /> unassignProduct({ variables: { ...paginationState, collectionId: id, productIds: params.ids } }) } open={params.action === "unassign"} title={intl.formatMessage({ defaultMessage: "Unassign products from collection", description: "dialog title" })} > params.ids.length), displayQuantity: ( {maybe(() => params.ids.length)} ) }} /> updateCollection({ variables: { id, input: { backgroundImage: null } } }) } open={params.action === "removeImage"} title={intl.formatMessage({ defaultMessage: "Delete image", description: "dialog title" })} variant="delete" > ); }} ); }; export default CollectionDetails;