import * as React from 'react'; import { useNotification, useTracking, useAPIErrorHandler } from '@strapi/admin/strapi-admin'; import { useIntl } from 'react-intl'; import { useNavigate } from 'react-router-dom'; import { useRelationModal } from '../pages/EditView/components/FormInputs/Relations/RelationModal.mjs'; import { usePreviewContext } from '../preview/pages/Preview.mjs'; import { useDeleteDocumentMutation, useDeleteManyDocumentsMutation, useDiscardDocumentMutation, usePublishDocumentMutation, usePublishManyDocumentsMutation, useUpdateDocumentMutation, useUnpublishDocumentMutation, useUnpublishManyDocumentsMutation, useCreateDocumentMutation, useAutoCloneDocumentMutation, useCloneDocumentMutation, useLazyGetDocumentQuery } from '../services/documents.mjs'; import { getTranslation } from '../utils/translations.mjs'; const DEFAULT_UNEXPECTED_ERROR_MSG = { id: 'notification.error', defaultMessage: 'An error occurred, please try again' }; /** * @alpha * @public * @description Contains all the operations that can be performed on a single document. * Designed to be able to be used anywhere within a Strapi app. The hooks will handle * notifications should the operation fail, however the response is always returned incase * the user needs to handle side-effects. * @example * ```tsx * import { Form } from '@strapi/admin/admin'; * * const { id, model, collectionType } = useParams<{ id: string; model: string; collectionType: string }>(); * const { update } = useDocumentActions(); * * const handleSubmit = async (data) => { * await update({ collectionType, model, documentId: id }, data); * } * * return
* ``` * * @see {@link https://contributor.strapi.io/docs/core/content-manager/hooks/use-document-operations} for more information */ const useDocumentActions = ()=>{ const { toggleNotification } = useNotification(); const { formatMessage } = useIntl(); const { trackUsage } = useTracking(); const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler(); const navigate = useNavigate(); // Get metadata from context providers for tracking purposes const previewContext = usePreviewContext('useDocumentActions', ()=>true, false); const relationContext = useRelationModal('useDocumentActions', ()=>true, false); const fromPreview = previewContext != undefined; const fromRelationModal = relationContext != undefined; const [deleteDocument, { isLoading: isDeleting }] = useDeleteDocumentMutation(); const _delete = React.useCallback(async ({ collectionType, model, documentId, params }, trackerProperty)=>{ try { trackUsage('willDeleteEntry', trackerProperty); const res = await deleteDocument({ collectionType, model, documentId, params }); if ('error' in res) { toggleNotification({ type: 'danger', message: formatAPIError(res.error) }); return { error: res.error }; } toggleNotification({ type: 'success', message: formatMessage({ id: getTranslation('success.record.delete'), defaultMessage: 'Deleted document' }) }); trackUsage('didDeleteEntry', trackerProperty); return res.data; } catch (err) { toggleNotification({ type: 'danger', message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG) }); trackUsage('didNotDeleteEntry', { error: err, ...trackerProperty }); throw err; } }, [ trackUsage, deleteDocument, toggleNotification, formatMessage, formatAPIError ]); const [deleteManyDocuments, { isLoading: isDeletingMany }] = useDeleteManyDocumentsMutation(); const deleteMany = React.useCallback(async ({ model, documentIds, params })=>{ try { trackUsage('willBulkDeleteEntries'); const res = await deleteManyDocuments({ model, documentIds, params }); if ('error' in res) { toggleNotification({ type: 'danger', message: formatAPIError(res.error) }); return { error: res.error }; } toggleNotification({ type: 'success', title: formatMessage({ id: getTranslation('success.records.delete'), defaultMessage: 'Successfully deleted.' }), message: '' }); trackUsage('didBulkDeleteEntries'); return res.data; } catch (err) { toggleNotification({ type: 'danger', message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG) }); trackUsage('didNotBulkDeleteEntries'); throw err; } }, [ trackUsage, deleteManyDocuments, toggleNotification, formatMessage, formatAPIError ]); const [discardDocument, { isLoading: isDiscardingDocument }] = useDiscardDocumentMutation(); const discard = React.useCallback(async ({ collectionType, model, documentId, params })=>{ try { const res = await discardDocument({ collectionType, model, documentId, params }); if ('error' in res) { toggleNotification({ type: 'danger', message: formatAPIError(res.error) }); return { error: res.error }; } toggleNotification({ type: 'success', message: formatMessage({ id: 'content-manager.success.record.discard', defaultMessage: 'Changes discarded' }) }); return res.data; } catch (err) { toggleNotification({ type: 'danger', message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG) }); throw err; } }, [ discardDocument, formatAPIError, formatMessage, toggleNotification ]); const [publishDocument, { isLoading: isPublishing }] = usePublishDocumentMutation(); const publish = React.useCallback(async ({ collectionType, model, documentId, params }, data)=>{ try { trackUsage('willPublishEntry', { documentId }); const res = await publishDocument({ collectionType, model, documentId, data, params }); if ('error' in res) { toggleNotification({ type: 'danger', message: formatAPIError(res.error) }); return { error: res.error }; } trackUsage('didPublishEntry', { documentId, fromPreview, fromRelationModal }); toggleNotification({ type: 'success', message: formatMessage({ id: getTranslation('success.record.publish'), defaultMessage: 'Published document' }) }); return res.data; } catch (err) { toggleNotification({ type: 'danger', message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG) }); throw err; } }, [ trackUsage, publishDocument, fromPreview, fromRelationModal, toggleNotification, formatMessage, formatAPIError ]); const [publishManyDocuments, { isLoading: isPublishingMany }] = usePublishManyDocumentsMutation(); const publishMany = React.useCallback(async ({ model, documentIds, params })=>{ try { // TODO Confirm tracking events for bulk publish? const res = await publishManyDocuments({ model, documentIds, params }); if ('error' in res) { toggleNotification({ type: 'danger', message: formatAPIError(res.error) }); return { error: res.error }; } toggleNotification({ type: 'success', message: formatMessage({ id: getTranslation('success.record.publish'), defaultMessage: 'Published document' }) }); return res.data; } catch (err) { toggleNotification({ type: 'danger', message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG) }); throw err; } }, [ // trackUsage, publishManyDocuments, toggleNotification, formatMessage, formatAPIError ]); const [updateDocument, { isLoading: isUpdating }] = useUpdateDocumentMutation(); const update = React.useCallback(async ({ collectionType, model, documentId, params }, data, trackerProperty)=>{ try { trackUsage('willEditEntry', trackerProperty); const res = await updateDocument({ collectionType, model, documentId, data, params }); if ('error' in res) { toggleNotification({ type: 'danger', message: formatAPIError(res.error) }); trackUsage('didNotEditEntry', { error: res.error, ...trackerProperty }); return { error: res.error }; } trackUsage('didEditEntry', { ...trackerProperty, documentId: res.data.data.documentId, fromPreview, fromRelationModal }); toggleNotification({ type: 'success', message: formatMessage({ id: getTranslation('success.record.save'), defaultMessage: 'Saved document' }) }); return res.data; } catch (err) { trackUsage('didNotEditEntry', { error: err, ...trackerProperty }); toggleNotification({ type: 'danger', message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG) }); throw err; } }, [ trackUsage, updateDocument, fromPreview, fromRelationModal, toggleNotification, formatMessage, formatAPIError ]); const [unpublishDocument] = useUnpublishDocumentMutation(); const unpublish = React.useCallback(async ({ collectionType, model, documentId, params }, discardDraft = false)=>{ try { trackUsage('willUnpublishEntry'); const res = await unpublishDocument({ collectionType, model, documentId, params, data: { discardDraft } }); if ('error' in res) { toggleNotification({ type: 'danger', message: formatAPIError(res.error) }); return { error: res.error }; } trackUsage('didUnpublishEntry'); toggleNotification({ type: 'success', message: formatMessage({ id: getTranslation('success.record.unpublish'), defaultMessage: 'Unpublished document' }) }); return res.data; } catch (err) { toggleNotification({ type: 'danger', message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG) }); throw err; } }, [ trackUsage, unpublishDocument, toggleNotification, formatMessage, formatAPIError ]); const [unpublishManyDocuments, { isLoading: isUnpublishingMany }] = useUnpublishManyDocumentsMutation(); const unpublishMany = React.useCallback(async ({ model, documentIds, params })=>{ try { trackUsage('willBulkUnpublishEntries'); const res = await unpublishManyDocuments({ model, documentIds, params }); if ('error' in res) { toggleNotification({ type: 'danger', message: formatAPIError(res.error) }); return { error: res.error }; } trackUsage('didBulkUnpublishEntries'); toggleNotification({ type: 'success', title: formatMessage({ id: getTranslation('success.records.unpublish'), defaultMessage: 'Successfully unpublished.' }), message: '' }); return res.data; } catch (err) { toggleNotification({ type: 'danger', message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG) }); trackUsage('didNotBulkUnpublishEntries'); throw err; } }, [ trackUsage, unpublishManyDocuments, toggleNotification, formatMessage, formatAPIError ]); const [createDocument] = useCreateDocumentMutation(); const create = React.useCallback(async ({ model, params }, data, trackerProperty)=>{ try { const res = await createDocument({ model, data, params }); if ('error' in res) { toggleNotification({ type: 'danger', message: formatAPIError(res.error) }); trackUsage('didNotCreateEntry', { error: res.error, ...trackerProperty }); return { error: res.error }; } trackUsage('didCreateEntry', { ...trackerProperty, documentId: res.data.data.documentId, fromPreview, fromRelationModal }); toggleNotification({ type: 'success', message: formatMessage({ id: getTranslation('success.record.save'), defaultMessage: 'Saved document' }) }); return res.data; } catch (err) { toggleNotification({ type: 'danger', message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG) }); trackUsage('didNotCreateEntry', { error: err, ...trackerProperty }); throw err; } }, [ createDocument, formatAPIError, formatMessage, fromPreview, fromRelationModal, toggleNotification, trackUsage ]); const [autoCloneDocument] = useAutoCloneDocumentMutation(); const autoClone = React.useCallback(async ({ model, sourceId, locale })=>{ try { const res = await autoCloneDocument({ model, sourceId, params: locale ? { locale } : undefined }); if ('error' in res) { return { error: res.error }; } toggleNotification({ type: 'success', message: formatMessage({ id: getTranslation('success.record.clone'), defaultMessage: 'Cloned document' }) }); return res.data; } catch (err) { toggleNotification({ type: 'danger', message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG) }); throw err; } }, [ autoCloneDocument, formatMessage, toggleNotification ]); const [cloneDocument] = useCloneDocumentMutation(); const clone = React.useCallback(async ({ model, documentId, params }, body, trackerProperty)=>{ try { // Omit id and documentId so they are not copied to the clone const { id: _id, documentId: _documentId, ...restBody } = body; /** * If we're cloning we want to post directly to this endpoint * so that the relations even if they're not listed in the EditView * are correctly attached to the entry. */ const res = await cloneDocument({ model, sourceId: documentId, data: restBody, params }); if ('error' in res) { toggleNotification({ type: 'danger', message: formatAPIError(res.error) }); trackUsage('didNotCreateEntry', { error: res.error, ...trackerProperty }); return { error: res.error }; } trackUsage('didCreateEntry', trackerProperty); toggleNotification({ type: 'success', message: formatMessage({ id: getTranslation('success.record.clone'), defaultMessage: 'Cloned document' }) }); // Redirect to normal edit view navigate(`../../${res.data.data.documentId}`, { relative: 'path' }); return res.data; } catch (err) { toggleNotification({ type: 'danger', message: formatMessage(DEFAULT_UNEXPECTED_ERROR_MSG) }); trackUsage('didNotCreateEntry', { error: err, ...trackerProperty }); throw err; } }, [ cloneDocument, trackUsage, toggleNotification, formatMessage, formatAPIError, navigate ]); const [getDoc] = useLazyGetDocumentQuery(); const getDocument = React.useCallback(async (args)=>{ const { data } = await getDoc(args); return data; }, [ getDoc ]); return { isLoading: isPublishing || isUpdating || isDiscardingDocument || isDeleting || isDeletingMany || isUnpublishingMany || isPublishingMany, autoClone, clone, create, delete: _delete, deleteMany, discard, getDocument, publish, publishMany, unpublish, unpublishMany, update }; }; export { useDocumentActions }; //# sourceMappingURL=useDocumentActions.mjs.map