fix(next): establishes pattern for preview urls (#5581)
This commit is contained in:
45
packages/next/src/routes/rest/collections/preview.ts
Normal file
45
packages/next/src/routes/rest/collections/preview.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import httpStatus from 'http-status'
|
||||||
|
import { findByIDOperation } from 'payload/operations'
|
||||||
|
import { isNumber } from 'payload/utilities'
|
||||||
|
|
||||||
|
import type { CollectionRouteHandlerWithID } from '../types.js'
|
||||||
|
|
||||||
|
import { routeError } from '../routeError.js'
|
||||||
|
|
||||||
|
export const preview: CollectionRouteHandlerWithID = async ({ id, collection, req }) => {
|
||||||
|
const { searchParams } = req
|
||||||
|
const depth = searchParams.get('depth')
|
||||||
|
|
||||||
|
const result = await findByIDOperation({
|
||||||
|
id,
|
||||||
|
collection,
|
||||||
|
depth: isNumber(depth) ? Number(depth) : undefined,
|
||||||
|
draft: searchParams.get('draft') === 'true',
|
||||||
|
req,
|
||||||
|
})
|
||||||
|
|
||||||
|
let previewURL: string
|
||||||
|
|
||||||
|
const generatePreviewURL = req.payload.config.collections.find(
|
||||||
|
(config) => config.slug === collection.config.slug,
|
||||||
|
)?.admin?.preview
|
||||||
|
|
||||||
|
if (typeof generatePreviewURL === 'function') {
|
||||||
|
try {
|
||||||
|
previewURL = await generatePreviewURL(result, {
|
||||||
|
locale: req.locale,
|
||||||
|
token: req.user?.token,
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
routeError({
|
||||||
|
collection,
|
||||||
|
err,
|
||||||
|
req,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.json(previewURL, {
|
||||||
|
status: httpStatus.OK,
|
||||||
|
})
|
||||||
|
}
|
||||||
44
packages/next/src/routes/rest/globals/preview.ts
Normal file
44
packages/next/src/routes/rest/globals/preview.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import httpStatus from 'http-status'
|
||||||
|
import { findOneOperation } from 'payload/operations'
|
||||||
|
import { isNumber } from 'payload/utilities'
|
||||||
|
|
||||||
|
import type { GlobalRouteHandler } from '../types.js'
|
||||||
|
|
||||||
|
import { routeError } from '../routeError.js'
|
||||||
|
|
||||||
|
export const preview: GlobalRouteHandler = async ({ globalConfig, req }) => {
|
||||||
|
const { searchParams } = req
|
||||||
|
const depth = searchParams.get('depth')
|
||||||
|
|
||||||
|
const result = await findOneOperation({
|
||||||
|
slug: globalConfig.slug,
|
||||||
|
depth: isNumber(depth) ? Number(depth) : undefined,
|
||||||
|
draft: searchParams.get('draft') === 'true',
|
||||||
|
globalConfig,
|
||||||
|
req,
|
||||||
|
})
|
||||||
|
|
||||||
|
let previewURL: string
|
||||||
|
|
||||||
|
const generatePreviewURL = req.payload.config.globals.find(
|
||||||
|
(config) => config.slug === globalConfig.slug,
|
||||||
|
)?.admin?.preview
|
||||||
|
|
||||||
|
if (typeof generatePreviewURL === 'function') {
|
||||||
|
try {
|
||||||
|
previewURL = await generatePreviewURL(result, {
|
||||||
|
locale: req.locale,
|
||||||
|
token: req.user?.token,
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
routeError({
|
||||||
|
err,
|
||||||
|
req,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.json(previewURL, {
|
||||||
|
status: httpStatus.OK,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -34,6 +34,7 @@ import { find } from './collections/find.js'
|
|||||||
import { findByID } from './collections/findByID.js'
|
import { findByID } from './collections/findByID.js'
|
||||||
import { findVersionByID } from './collections/findVersionByID.js'
|
import { findVersionByID } from './collections/findVersionByID.js'
|
||||||
import { findVersions } from './collections/findVersions.js'
|
import { findVersions } from './collections/findVersions.js'
|
||||||
|
import { preview as previewCollection } from './collections/preview.js'
|
||||||
import { restoreVersion } from './collections/restoreVersion.js'
|
import { restoreVersion } from './collections/restoreVersion.js'
|
||||||
import { update } from './collections/update.js'
|
import { update } from './collections/update.js'
|
||||||
import { updateByID } from './collections/updateByID.js'
|
import { updateByID } from './collections/updateByID.js'
|
||||||
@@ -42,6 +43,7 @@ import { docAccess as docAccessGlobal } from './globals/docAccess.js'
|
|||||||
import { findOne } from './globals/findOne.js'
|
import { findOne } from './globals/findOne.js'
|
||||||
import { findVersionByID as findVersionByIdGlobal } from './globals/findVersionByID.js'
|
import { findVersionByID as findVersionByIdGlobal } from './globals/findVersionByID.js'
|
||||||
import { findVersions as findVersionsGlobal } from './globals/findVersions.js'
|
import { findVersions as findVersionsGlobal } from './globals/findVersions.js'
|
||||||
|
import { preview as previewGlobal } from './globals/preview.js'
|
||||||
import { restoreVersion as restoreVersionGlobal } from './globals/restoreVersion.js'
|
import { restoreVersion as restoreVersionGlobal } from './globals/restoreVersion.js'
|
||||||
import { update as updateGlobal } from './globals/update.js'
|
import { update as updateGlobal } from './globals/update.js'
|
||||||
import { routeError } from './routeError.js'
|
import { routeError } from './routeError.js'
|
||||||
@@ -60,6 +62,7 @@ const endpoints = {
|
|||||||
getFile,
|
getFile,
|
||||||
init,
|
init,
|
||||||
me,
|
me,
|
||||||
|
preview: previewCollection,
|
||||||
versions: findVersions,
|
versions: findVersions,
|
||||||
},
|
},
|
||||||
PATCH: {
|
PATCH: {
|
||||||
@@ -88,6 +91,7 @@ const endpoints = {
|
|||||||
'doc-versions': findVersionsGlobal,
|
'doc-versions': findVersionsGlobal,
|
||||||
'doc-versions-by-id': findVersionByIdGlobal,
|
'doc-versions-by-id': findVersionByIdGlobal,
|
||||||
findOne,
|
findOne,
|
||||||
|
preview: previewGlobal,
|
||||||
},
|
},
|
||||||
POST: {
|
POST: {
|
||||||
'doc-access': docAccessGlobal,
|
'doc-access': docAccessGlobal,
|
||||||
@@ -171,6 +175,7 @@ export const GET =
|
|||||||
endpoints: req.payload.config.endpoints,
|
endpoints: req.payload.config.endpoints,
|
||||||
request,
|
request,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (disableEndpoints) return disableEndpoints
|
if (disableEndpoints) return disableEndpoints
|
||||||
|
|
||||||
collection = req.payload.collections?.[slug1]
|
collection = req.payload.collections?.[slug1]
|
||||||
@@ -212,10 +217,16 @@ export const GET =
|
|||||||
if (slug2 === 'file') {
|
if (slug2 === 'file') {
|
||||||
// /:collection/file/:filename
|
// /:collection/file/:filename
|
||||||
res = await endpoints.collection.GET.getFile({ collection, filename: slug3, req })
|
res = await endpoints.collection.GET.getFile({ collection, filename: slug3, req })
|
||||||
|
} else if (slug3 in endpoints.collection.GET) {
|
||||||
|
// /:collection/:id/preview
|
||||||
|
res = await (endpoints.collection.GET[slug3] as CollectionRouteHandlerWithID)({
|
||||||
|
id: slug2,
|
||||||
|
collection,
|
||||||
|
req,
|
||||||
|
})
|
||||||
} else if (`doc-${slug2}-by-id` in endpoints.collection.GET) {
|
} else if (`doc-${slug2}-by-id` in endpoints.collection.GET) {
|
||||||
// /:collection/access/:id
|
// /:collection/access/:id
|
||||||
// /:collection/versions/:id
|
// /:collection/versions/:id
|
||||||
|
|
||||||
res = await (
|
res = await (
|
||||||
endpoints.collection.GET[`doc-${slug2}-by-id`] as CollectionRouteHandlerWithID
|
endpoints.collection.GET[`doc-${slug2}-by-id`] as CollectionRouteHandlerWithID
|
||||||
)({ id: slug3, collection, req })
|
)({ id: slug3, collection, req })
|
||||||
@@ -229,6 +240,7 @@ export const GET =
|
|||||||
endpoints: globalConfig.endpoints,
|
endpoints: globalConfig.endpoints,
|
||||||
request,
|
request,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (disableEndpoints) return disableEndpoints
|
if (disableEndpoints) return disableEndpoints
|
||||||
|
|
||||||
const customEndpointResponse = await handleCustomEndpoints({
|
const customEndpointResponse = await handleCustomEndpoints({
|
||||||
@@ -236,6 +248,7 @@ export const GET =
|
|||||||
entitySlug: `${slug1}/${slug2}`,
|
entitySlug: `${slug1}/${slug2}`,
|
||||||
payloadRequest: req,
|
payloadRequest: req,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (customEndpointResponse) return customEndpointResponse
|
if (customEndpointResponse) return customEndpointResponse
|
||||||
|
|
||||||
switch (slug.length) {
|
switch (slug.length) {
|
||||||
@@ -244,9 +257,16 @@ export const GET =
|
|||||||
res = await endpoints.global.GET.findOne({ globalConfig, req })
|
res = await endpoints.global.GET.findOne({ globalConfig, req })
|
||||||
break
|
break
|
||||||
case 3:
|
case 3:
|
||||||
if (`doc-${slug3}` in endpoints.global.GET) {
|
if (slug3 in endpoints.global.GET) {
|
||||||
|
// /globals/:slug/preview
|
||||||
|
res = await (endpoints.global.GET[slug3] as GlobalRouteHandler)({
|
||||||
|
globalConfig,
|
||||||
|
req,
|
||||||
|
})
|
||||||
|
} else if (`doc-${slug3}` in endpoints.global.GET) {
|
||||||
// /globals/:slug/access
|
// /globals/:slug/access
|
||||||
// /globals/:slug/versions
|
// /globals/:slug/versions
|
||||||
|
// /globals/:slug/preview
|
||||||
res = await (endpoints.global.GET?.[`doc-${slug3}`] as GlobalRouteHandler)({
|
res = await (endpoints.global.GET?.[`doc-${slug3}`] as GlobalRouteHandler)({
|
||||||
globalConfig,
|
globalConfig,
|
||||||
req,
|
req,
|
||||||
|
|||||||
@@ -1,11 +1 @@
|
|||||||
export type CustomPreviewButtonProps = React.ComponentType<
|
export type CustomPreviewButton = React.ComponentType
|
||||||
DefaultPreviewButtonProps & {
|
|
||||||
DefaultButton: React.ComponentType<DefaultPreviewButtonProps>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
|
|
||||||
export type DefaultPreviewButtonProps = {
|
|
||||||
disabled: boolean
|
|
||||||
label: string
|
|
||||||
preview: () => void
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export type CustomPublishButtonProps = React.ComponentType
|
export type CustomPublishButton = React.ComponentType
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export type CustomSaveButtonProps = React.ComponentType
|
export type CustomSaveButton = React.ComponentType
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export type CustomSaveDraftButtonProps = React.ComponentType
|
export type CustomSaveDraftButton = React.ComponentType
|
||||||
|
|||||||
@@ -2,11 +2,10 @@ export type { RichTextAdapter, RichTextFieldProps } from './RichText.js'
|
|||||||
export type { CellComponentProps, DefaultCellComponentProps } from './elements/Cell.js'
|
export type { CellComponentProps, DefaultCellComponentProps } from './elements/Cell.js'
|
||||||
export type { ConditionalDateProps } from './elements/DatePicker.js'
|
export type { ConditionalDateProps } from './elements/DatePicker.js'
|
||||||
export type { DayPickerProps, SharedProps, TimePickerProps } from './elements/DatePicker.js'
|
export type { DayPickerProps, SharedProps, TimePickerProps } from './elements/DatePicker.js'
|
||||||
export type { DefaultPreviewButtonProps } from './elements/PreviewButton.js'
|
export type { CustomPreviewButton } from './elements/PreviewButton.js'
|
||||||
export type { CustomPreviewButtonProps } from './elements/PreviewButton.js'
|
export type { CustomPublishButton } from './elements/PublishButton.js'
|
||||||
export type { CustomPublishButtonProps } from './elements/PublishButton.js'
|
export type { CustomSaveButton } from './elements/SaveButton.js'
|
||||||
export type { CustomSaveButtonProps } from './elements/SaveButton.js'
|
export type { CustomSaveDraftButton } from './elements/SaveDraftButton.js'
|
||||||
export type { CustomSaveDraftButtonProps } from './elements/SaveDraftButton.js'
|
|
||||||
export type {
|
export type {
|
||||||
DocumentTab,
|
DocumentTab,
|
||||||
DocumentTabComponent,
|
DocumentTabComponent,
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import type { GraphQLInputObjectType, GraphQLNonNull, GraphQLObjectType } from '
|
|||||||
import type { DeepRequired } from 'ts-essentials'
|
import type { DeepRequired } from 'ts-essentials'
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
CustomPreviewButtonProps,
|
CustomPreviewButton,
|
||||||
CustomPublishButtonProps,
|
CustomPublishButton,
|
||||||
CustomSaveButtonProps,
|
CustomSaveButton,
|
||||||
CustomSaveDraftButtonProps,
|
CustomSaveDraftButton,
|
||||||
} from '../../admin/types.js'
|
} from '../../admin/types.js'
|
||||||
import type { Auth, ClientUser, IncomingAuthType } from '../../auth/types.js'
|
import type { Auth, ClientUser, IncomingAuthType } from '../../auth/types.js'
|
||||||
import type {
|
import type {
|
||||||
@@ -211,23 +211,23 @@ export type CollectionAdminOptions = {
|
|||||||
/**
|
/**
|
||||||
* Replaces the "Preview" button
|
* Replaces the "Preview" button
|
||||||
*/
|
*/
|
||||||
PreviewButton?: CustomPreviewButtonProps
|
PreviewButton?: CustomPreviewButton
|
||||||
/**
|
/**
|
||||||
* Replaces the "Publish" button
|
* Replaces the "Publish" button
|
||||||
* + drafts must be enabled
|
* + drafts must be enabled
|
||||||
*/
|
*/
|
||||||
PublishButton?: CustomPublishButtonProps
|
PublishButton?: CustomPublishButton
|
||||||
/**
|
/**
|
||||||
* Replaces the "Save" button
|
* Replaces the "Save" button
|
||||||
* + drafts must be disabled
|
* + drafts must be disabled
|
||||||
*/
|
*/
|
||||||
SaveButton?: CustomSaveButtonProps
|
SaveButton?: CustomSaveButton
|
||||||
/**
|
/**
|
||||||
* Replaces the "Save Draft" button
|
* Replaces the "Save Draft" button
|
||||||
* + drafts must be enabled
|
* + drafts must be enabled
|
||||||
* + autosave must be disabled
|
* + autosave must be disabled
|
||||||
*/
|
*/
|
||||||
SaveDraftButton?: CustomSaveDraftButtonProps
|
SaveDraftButton?: CustomSaveDraftButton
|
||||||
}
|
}
|
||||||
views?: {
|
views?: {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import type { GraphQLNonNull, GraphQLObjectType } from 'graphql'
|
|||||||
import type { DeepRequired } from 'ts-essentials'
|
import type { DeepRequired } from 'ts-essentials'
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
CustomPreviewButtonProps,
|
CustomPreviewButton,
|
||||||
CustomPublishButtonProps,
|
CustomPublishButton,
|
||||||
CustomSaveButtonProps,
|
CustomSaveButton,
|
||||||
CustomSaveDraftButtonProps,
|
CustomSaveDraftButton,
|
||||||
} from '../../admin/types.js'
|
} from '../../admin/types.js'
|
||||||
import type { User } from '../../auth/types.js'
|
import type { User } from '../../auth/types.js'
|
||||||
import type {
|
import type {
|
||||||
@@ -79,23 +79,23 @@ export type GlobalAdminOptions = {
|
|||||||
/**
|
/**
|
||||||
* Replaces the "Preview" button
|
* Replaces the "Preview" button
|
||||||
*/
|
*/
|
||||||
PreviewButton?: CustomPreviewButtonProps
|
PreviewButton?: CustomPreviewButton
|
||||||
/**
|
/**
|
||||||
* Replaces the "Publish" button
|
* Replaces the "Publish" button
|
||||||
* + drafts must be enabled
|
* + drafts must be enabled
|
||||||
*/
|
*/
|
||||||
PublishButton?: CustomPublishButtonProps
|
PublishButton?: CustomPublishButton
|
||||||
/**
|
/**
|
||||||
* Replaces the "Save" button
|
* Replaces the "Save" button
|
||||||
* + drafts must be disabled
|
* + drafts must be disabled
|
||||||
*/
|
*/
|
||||||
SaveButton?: CustomSaveButtonProps
|
SaveButton?: CustomSaveButton
|
||||||
/**
|
/**
|
||||||
* Replaces the "Save Draft" button
|
* Replaces the "Save Draft" button
|
||||||
* + drafts must be enabled
|
* + drafts must be enabled
|
||||||
* + autosave must be disabled
|
* + autosave must be disabled
|
||||||
*/
|
*/
|
||||||
SaveDraftButton?: CustomSaveDraftButtonProps
|
SaveDraftButton?: CustomSaveDraftButton
|
||||||
}
|
}
|
||||||
views?: {
|
views?: {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -154,15 +154,9 @@ export const DocumentControls: React.FC<{
|
|||||||
</div>
|
</div>
|
||||||
<div className={`${baseClass}__controls-wrapper`}>
|
<div className={`${baseClass}__controls-wrapper`}>
|
||||||
<div className={`${baseClass}__controls`}>
|
<div className={`${baseClass}__controls`}>
|
||||||
{/* {(collectionConfig?.admin?.preview || globalConfig?.admin?.preview) && (
|
{componentMap?.isPreviewEnabled && (
|
||||||
<PreviewButton
|
<PreviewButton CustomComponent={componentMap.PreviewButton} />
|
||||||
CustomComponent={
|
)}
|
||||||
collectionConfig?.admin?.components?.edit?.PreviewButton ||
|
|
||||||
globalConfig?.admin?.components?.elements?.PreviewButton
|
|
||||||
}
|
|
||||||
generatePreviewURL={collectionConfig?.admin?.preview || globalConfig?.admin?.preview}
|
|
||||||
/>
|
|
||||||
)} */}
|
|
||||||
{hasSavePermission && (
|
{hasSavePermission && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{collectionConfig?.versions?.drafts || globalConfig?.versions?.drafts ? (
|
{collectionConfig?.versions?.drafts || globalConfig?.versions?.drafts ? (
|
||||||
|
|||||||
@@ -1,31 +1,24 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { GeneratePreviewURL } from 'payload/config'
|
import React from 'react'
|
||||||
import type { CustomPreviewButtonProps, DefaultPreviewButtonProps } from 'payload/types'
|
|
||||||
|
|
||||||
import React, { useCallback, useRef, useState } from 'react'
|
|
||||||
import { toast } from 'react-toastify'
|
|
||||||
|
|
||||||
import { RenderCustomComponent } from '../../elements/RenderCustomComponent/index.js'
|
|
||||||
import { useAuth } from '../../providers/Auth/index.js'
|
|
||||||
import { useConfig } from '../../providers/Config/index.js'
|
|
||||||
import { useDocumentInfo } from '../../providers/DocumentInfo/index.js'
|
|
||||||
import { useLocale } from '../../providers/Locale/index.js'
|
|
||||||
import { useTranslation } from '../../providers/Translation/index.js'
|
|
||||||
import { Button } from '../Button/index.js'
|
import { Button } from '../Button/index.js'
|
||||||
|
import { usePreviewURL } from './usePreviewURL.js'
|
||||||
|
|
||||||
const baseClass = 'preview-btn'
|
const baseClass = 'preview-btn'
|
||||||
|
|
||||||
const DefaultPreviewButton: React.FC<DefaultPreviewButtonProps> = ({
|
const DefaultPreviewButton: React.FC = () => {
|
||||||
disabled,
|
const { generatePreviewURL, label } = usePreviewURL()
|
||||||
label,
|
|
||||||
preview,
|
|
||||||
}) => {
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
buttonStyle="secondary"
|
buttonStyle="secondary"
|
||||||
className={baseClass}
|
className={baseClass}
|
||||||
disabled={disabled}
|
// disabled={disabled}
|
||||||
onClick={preview}
|
onClick={() =>
|
||||||
|
generatePreviewURL({
|
||||||
|
openPreviewWindow: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
size="small"
|
size="small"
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
@@ -33,66 +26,12 @@ const DefaultPreviewButton: React.FC<DefaultPreviewButtonProps> = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PreviewButtonProps = {
|
type Props = {
|
||||||
CustomComponent?: CustomPreviewButtonProps
|
CustomComponent?: React.ReactNode
|
||||||
generatePreviewURL?: GeneratePreviewURL
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PreviewButton: React.FC<PreviewButtonProps> = ({
|
export const PreviewButton: React.FC<Props> = ({ CustomComponent }) => {
|
||||||
CustomComponent,
|
if (CustomComponent) return CustomComponent
|
||||||
generatePreviewURL,
|
|
||||||
}) => {
|
|
||||||
const { id, collectionSlug, globalSlug } = useDocumentInfo()
|
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
return <DefaultPreviewButton />
|
||||||
const { code: locale } = useLocale()
|
|
||||||
const { token } = useAuth()
|
|
||||||
const {
|
|
||||||
routes: { api },
|
|
||||||
serverURL,
|
|
||||||
} = useConfig()
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const isGeneratingPreviewURL = useRef(false)
|
|
||||||
|
|
||||||
// we need to regenerate the preview URL every time the button is clicked
|
|
||||||
// to do this we need to fetch the document data fresh from the API
|
|
||||||
// this will ensure the latest data is used when generating the preview URL
|
|
||||||
const preview = useCallback(async () => {
|
|
||||||
if (!generatePreviewURL || isGeneratingPreviewURL.current) return
|
|
||||||
isGeneratingPreviewURL.current = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
setIsLoading(true)
|
|
||||||
|
|
||||||
let url = `${serverURL}${api}`
|
|
||||||
if (collectionSlug) url = `${url}/${collectionSlug}/${id}`
|
|
||||||
if (globalSlug) url = `${url}/globals/${globalSlug}`
|
|
||||||
|
|
||||||
const data = await fetch(`${url}?draft=true&locale=${locale}&fallback-locale=null`).then(
|
|
||||||
(res) => res.json(),
|
|
||||||
)
|
|
||||||
const previewURL = await generatePreviewURL(data, { locale, token })
|
|
||||||
if (!previewURL) throw new Error()
|
|
||||||
setIsLoading(false)
|
|
||||||
isGeneratingPreviewURL.current = false
|
|
||||||
window.open(previewURL, '_blank')
|
|
||||||
} catch (err) {
|
|
||||||
setIsLoading(false)
|
|
||||||
isGeneratingPreviewURL.current = false
|
|
||||||
toast.error(t('error:previewing'))
|
|
||||||
}
|
|
||||||
}, [serverURL, api, collectionSlug, globalSlug, id, generatePreviewURL, locale, token, t])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<RenderCustomComponent
|
|
||||||
CustomComponent={CustomComponent}
|
|
||||||
DefaultComponent={DefaultPreviewButton}
|
|
||||||
componentProps={{
|
|
||||||
DefaultButton: DefaultPreviewButton,
|
|
||||||
disabled: isLoading || !generatePreviewURL,
|
|
||||||
label: isLoading ? t('general:loading') : t('version:preview'),
|
|
||||||
preview,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
70
packages/ui/src/elements/PreviewButton/usePreviewURL.tsx
Normal file
70
packages/ui/src/elements/PreviewButton/usePreviewURL.tsx
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
'use client'
|
||||||
|
import { useCallback, useRef, useState } from 'react'
|
||||||
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
|
import { useConfig } from '../../providers/Config/index.js'
|
||||||
|
import { useDocumentInfo } from '../../providers/DocumentInfo/index.js'
|
||||||
|
import { useLocale } from '../../providers/Locale/index.js'
|
||||||
|
import { useTranslation } from '../../providers/Translation/index.js'
|
||||||
|
|
||||||
|
export const usePreviewURL = (): {
|
||||||
|
generatePreviewURL: ({ openPreviewWindow }: { openPreviewWindow?: boolean }) => void
|
||||||
|
isLoading: boolean
|
||||||
|
label: string
|
||||||
|
previewURL: string
|
||||||
|
} => {
|
||||||
|
const { id, collectionSlug, globalSlug } = useDocumentInfo()
|
||||||
|
|
||||||
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
const [previewURL, setPreviewURL] = useState('')
|
||||||
|
const { code: locale } = useLocale()
|
||||||
|
|
||||||
|
const {
|
||||||
|
routes: { api },
|
||||||
|
serverURL,
|
||||||
|
} = useConfig()
|
||||||
|
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const isGeneratingPreviewURL = useRef(false)
|
||||||
|
|
||||||
|
// we need to regenerate the preview URL every time the button is clicked
|
||||||
|
// to do this we need to fetch the document data fresh from the API
|
||||||
|
// this will ensure the latest data is used when generating the preview URL
|
||||||
|
const generatePreviewURL = useCallback(
|
||||||
|
async ({ openPreviewWindow = false }) => {
|
||||||
|
if (isGeneratingPreviewURL.current) return
|
||||||
|
isGeneratingPreviewURL.current = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
setIsLoading(true)
|
||||||
|
|
||||||
|
let url = `${serverURL}${api}`
|
||||||
|
if (collectionSlug) url = `${url}/${collectionSlug}/${id}/preview`
|
||||||
|
if (globalSlug) url = `${url}/globals/${globalSlug}/preview`
|
||||||
|
|
||||||
|
const res = await fetch(`${url}${locale ? `?locale=${locale}` : ''}`)
|
||||||
|
|
||||||
|
if (!res.ok) throw new Error()
|
||||||
|
const newPreviewURL = await res.json()
|
||||||
|
if (!newPreviewURL) throw new Error()
|
||||||
|
setPreviewURL(newPreviewURL)
|
||||||
|
setIsLoading(false)
|
||||||
|
isGeneratingPreviewURL.current = false
|
||||||
|
if (openPreviewWindow) window.open(newPreviewURL, '_blank')
|
||||||
|
} catch (err) {
|
||||||
|
setIsLoading(false)
|
||||||
|
isGeneratingPreviewURL.current = false
|
||||||
|
toast.error(t('error:previewing'))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[serverURL, api, collectionSlug, globalSlug, id, locale, t],
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
generatePreviewURL,
|
||||||
|
isLoading,
|
||||||
|
label: isLoading ? t('general:loading') : t('version:preview'),
|
||||||
|
previewURL,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -70,8 +70,8 @@ export const buildComponentMap = (args: {
|
|||||||
const SaveDraftButtonComponent = collectionConfig?.admin?.components?.edit?.SaveDraftButton
|
const SaveDraftButtonComponent = collectionConfig?.admin?.components?.edit?.SaveDraftButton
|
||||||
const SaveDraftButton = SaveDraftButtonComponent ? <SaveDraftButtonComponent /> : undefined
|
const SaveDraftButton = SaveDraftButtonComponent ? <SaveDraftButtonComponent /> : undefined
|
||||||
|
|
||||||
/* const PreviewButtonComponent = collectionConfig?.admin?.components?.edit?.PreviewButton
|
const PreviewButtonComponent = collectionConfig?.admin?.components?.edit?.PreviewButton
|
||||||
const PreviewButton = PreviewButtonComponent ? <PreviewButtonComponent /> : undefined */
|
const PreviewButton = PreviewButtonComponent ? <PreviewButtonComponent /> : undefined
|
||||||
|
|
||||||
const PublishButtonComponent = collectionConfig?.admin?.components?.edit?.PublishButton
|
const PublishButtonComponent = collectionConfig?.admin?.components?.edit?.PublishButton
|
||||||
const PublishButton = PublishButtonComponent ? <PublishButtonComponent /> : undefined
|
const PublishButton = PublishButtonComponent ? <PublishButtonComponent /> : undefined
|
||||||
@@ -111,7 +111,7 @@ export const buildComponentMap = (args: {
|
|||||||
BeforeListTable,
|
BeforeListTable,
|
||||||
Edit: <Edit collectionSlug={collectionConfig.slug} />,
|
Edit: <Edit collectionSlug={collectionConfig.slug} />,
|
||||||
List: <List collectionSlug={collectionConfig.slug} />,
|
List: <List collectionSlug={collectionConfig.slug} />,
|
||||||
/* PreviewButton, */
|
PreviewButton,
|
||||||
PublishButton,
|
PublishButton,
|
||||||
SaveButton,
|
SaveButton,
|
||||||
SaveDraftButton,
|
SaveDraftButton,
|
||||||
@@ -123,6 +123,7 @@ export const buildComponentMap = (args: {
|
|||||||
fieldSchema: fields,
|
fieldSchema: fields,
|
||||||
readOnly: readOnlyOverride,
|
readOnly: readOnlyOverride,
|
||||||
}),
|
}),
|
||||||
|
isPreviewEnabled: !!collectionConfig?.admin?.preview,
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -143,8 +144,8 @@ export const buildComponentMap = (args: {
|
|||||||
const SaveDraftButton = globalConfig?.admin?.components?.elements?.SaveDraftButton
|
const SaveDraftButton = globalConfig?.admin?.components?.elements?.SaveDraftButton
|
||||||
const SaveDraftButtonComponent = SaveDraftButton ? <SaveDraftButton /> : undefined
|
const SaveDraftButtonComponent = SaveDraftButton ? <SaveDraftButton /> : undefined
|
||||||
|
|
||||||
/* const PreviewButton = globalConfig?.admin?.components?.elements?.PreviewButton
|
const PreviewButton = globalConfig?.admin?.components?.elements?.PreviewButton
|
||||||
const PreviewButtonComponent = PreviewButton ? <PreviewButton /> : undefined */
|
const PreviewButtonComponent = PreviewButton ? <PreviewButton /> : undefined
|
||||||
|
|
||||||
const PublishButton = globalConfig?.admin?.components?.elements?.PublishButton
|
const PublishButton = globalConfig?.admin?.components?.elements?.PublishButton
|
||||||
const PublishButtonComponent = PublishButton ? <PublishButton /> : undefined
|
const PublishButtonComponent = PublishButton ? <PublishButton /> : undefined
|
||||||
@@ -164,7 +165,7 @@ export const buildComponentMap = (args: {
|
|||||||
|
|
||||||
const componentMap: GlobalComponentMap = {
|
const componentMap: GlobalComponentMap = {
|
||||||
Edit: <Edit globalSlug={globalConfig.slug} />,
|
Edit: <Edit globalSlug={globalConfig.slug} />,
|
||||||
/* PreviewButton: PreviewButtonComponent, */
|
PreviewButton: PreviewButtonComponent,
|
||||||
PublishButton: PublishButtonComponent,
|
PublishButton: PublishButtonComponent,
|
||||||
SaveButton: SaveButtonComponent,
|
SaveButton: SaveButtonComponent,
|
||||||
SaveDraftButton: SaveDraftButtonComponent,
|
SaveDraftButton: SaveDraftButtonComponent,
|
||||||
@@ -176,6 +177,7 @@ export const buildComponentMap = (args: {
|
|||||||
fieldSchema: fields,
|
fieldSchema: fields,
|
||||||
readOnly: readOnlyOverride,
|
readOnly: readOnlyOverride,
|
||||||
}),
|
}),
|
||||||
|
isPreviewEnabled: !!globalConfig?.admin?.preview,
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -103,12 +103,13 @@ export type GlobalComponentMap = ConfigComponentMapBase
|
|||||||
|
|
||||||
export type ConfigComponentMapBase = {
|
export type ConfigComponentMapBase = {
|
||||||
Edit: React.ReactNode
|
Edit: React.ReactNode
|
||||||
/* PreviewButton: React.ReactNode */
|
PreviewButton: React.ReactNode
|
||||||
PublishButton: React.ReactNode
|
PublishButton: React.ReactNode
|
||||||
SaveButton: React.ReactNode
|
SaveButton: React.ReactNode
|
||||||
SaveDraftButton: React.ReactNode
|
SaveDraftButton: React.ReactNode
|
||||||
actionsMap: ActionMap
|
actionsMap: ActionMap
|
||||||
fieldMap: FieldMap
|
fieldMap: FieldMap
|
||||||
|
isPreviewEnabled: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComponentMap = {
|
export type ComponentMap = {
|
||||||
|
|||||||
Reference in New Issue
Block a user