fix: format admin url inside forgot pw email (#11509)

### What?
Supersedes https://github.com/payloadcms/payload/pull/11490.

Refactors imports of `formatAdminURL` to import from `payload/shared`
instead of `@payloadcms/ui/shared`. The ui package now imports and
re-exports the function to prevent this from being a breaking change.

### Why?
This makes it easier for other packages/plugins to consume the
`formatAdminURL` function instead of needing to implement their own or
rely on the ui package for the utility.
This commit is contained in:
Jarrod Flesch
2025-03-04 11:55:36 -05:00
committed by GitHub
parent 1d168318d0
commit 56dec13820
44 changed files with 135 additions and 102 deletions

View File

@@ -2,8 +2,8 @@
import type { SanitizedConfig } from 'payload'
import { Link } from '@payloadcms/ui'
import { formatAdminURL } from '@payloadcms/ui/shared'
import { useParams, usePathname, useSearchParams } from 'next/navigation.js'
import { formatAdminURL } from 'payload/shared'
import React from 'react'
export const DocumentTabLink: React.FC<{

View File

@@ -5,8 +5,9 @@ import type { NavPreferences } from 'payload'
import { getTranslation } from '@payloadcms/translations'
import { Link, NavGroup, useConfig, useTranslation } from '@payloadcms/ui'
import { EntityType, formatAdminURL } from '@payloadcms/ui/shared'
import { EntityType } from '@payloadcms/ui/shared'
import { usePathname } from 'next/navigation.js'
import { formatAdminURL } from 'payload/shared'
import React, { Fragment } from 'react'
const baseClass = 'nav'

View File

@@ -1,6 +1,6 @@
import type { User } from 'payload'
import { formatAdminURL } from '@payloadcms/ui/shared'
import { formatAdminURL } from 'payload/shared'
import * as qs from 'qs-esm'
type Args = {

View File

@@ -4,7 +4,8 @@ import type { ClientUser, Locale, ServerProps } from 'payload'
import { getTranslation } from '@payloadcms/translations'
import { Button, Card, Gutter, Locked } from '@payloadcms/ui'
import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent'
import { EntityType, formatAdminURL } from '@payloadcms/ui/shared'
import { EntityType } from '@payloadcms/ui/shared'
import { formatAdminURL } from 'payload/shared'
import React, { Fragment } from 'react'
import './index.scss'

View File

@@ -9,10 +9,11 @@ import type {
import { DocumentInfoProvider, EditDepthProvider, HydrateAuthProvider } from '@payloadcms/ui'
import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent'
import { formatAdminURL, isEditing as getIsEditing } from '@payloadcms/ui/shared'
import { isEditing as getIsEditing } from '@payloadcms/ui/shared'
import { buildFormState } from '@payloadcms/ui/utilities/buildFormState'
import { notFound, redirect } from 'next/navigation.js'
import { logError } from 'payload'
import { formatAdminURL } from 'payload/shared'
import React from 'react'
import type { GenerateEditViewMetadata } from './getMetaBySegment.js'

View File

@@ -1,7 +1,8 @@
import type { AdminViewServerProps } from 'payload'
import { Button, Link } from '@payloadcms/ui'
import { formatAdminURL, Translation } from '@payloadcms/ui/shared'
import { Translation } from '@payloadcms/ui/shared'
import { formatAdminURL } from 'payload/shared'
import React, { Fragment } from 'react'
import { FormHeader } from '../../elements/FormHeader/index.js'

View File

@@ -1,7 +1,7 @@
import { DefaultListView, HydrateAuthProvider, ListQueryProvider } from '@payloadcms/ui'
import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent'
import { renderFilters, renderTable, upsertPreferences } from '@payloadcms/ui/rsc'
import { formatAdminURL, mergeListSearchAndWhere } from '@payloadcms/ui/shared'
import { mergeListSearchAndWhere } from '@payloadcms/ui/shared'
import { notFound } from 'next/navigation.js'
import {
type AdminViewServerProps,
@@ -12,7 +12,7 @@ import {
type ListViewServerPropsOnly,
type Where,
} from 'payload'
import { isNumber, transformColumnsToPreferences } from 'payload/shared'
import { formatAdminURL, isNumber, transformColumnsToPreferences } from 'payload/shared'
import React, { Fragment } from 'react'
import { renderListViewSlots } from './renderListViewSlots.js'

View File

@@ -35,13 +35,13 @@ import {
} from '@payloadcms/ui'
import {
abortAndIgnore,
formatAdminURL,
handleAbortRef,
handleBackToDashboard,
handleGoBack,
handleTakeOver,
} from '@payloadcms/ui/shared'
import { useRouter, useSearchParams } from 'next/navigation.js'
import { formatAdminURL } from 'payload/shared'
import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'
import { useLivePreviewContext } from './Context/context.js'

View File

@@ -16,8 +16,7 @@ import {
useConfig,
useTranslation,
} from '@payloadcms/ui'
import { formatAdminURL } from '@payloadcms/ui/shared'
import { getLoginOptions } from 'payload/shared'
import { formatAdminURL, getLoginOptions } from 'payload/shared'
import type { LoginFieldProps } from '../LoginField/index.js'

View File

@@ -7,8 +7,8 @@ import {
useRouteTransition,
useTranslation,
} from '@payloadcms/ui'
import { formatAdminURL } from '@payloadcms/ui/shared'
import { useRouter } from 'next/navigation.js'
import { formatAdminURL } from 'payload/shared'
import React, { useEffect } from 'react'
import './index.scss'

View File

@@ -2,7 +2,7 @@ import type { I18n } from '@payloadcms/translations'
import type { Metadata } from 'next'
import type { AdminViewServerProps, ImportMap, SanitizedConfig } from 'payload'
import { formatAdminURL } from '@payloadcms/ui/shared'
import { formatAdminURL } from 'payload/shared'
import React from 'react'
import { DefaultTemplate } from '../../templates/Default/index.js'

View File

@@ -9,9 +9,9 @@ import {
useConfig,
useTranslation,
} from '@payloadcms/ui'
import { formatAdminURL } from '@payloadcms/ui/shared'
import { useRouter } from 'next/navigation.js'
import { type FormState } from 'payload'
import { formatAdminURL } from 'payload/shared'
import React from 'react'
type Args = {

View File

@@ -1,7 +1,8 @@
import type { AdminViewServerProps } from 'payload'
import { Button, Link } from '@payloadcms/ui'
import { formatAdminURL, Translation } from '@payloadcms/ui/shared'
import { Translation } from '@payloadcms/ui/shared'
import { formatAdminURL } from 'payload/shared'
import React from 'react'
import { FormHeader } from '../../elements/FormHeader/index.js'

View File

@@ -9,7 +9,7 @@ import type {
} from 'payload'
import type React from 'react'
import { formatAdminURL } from '@payloadcms/ui/shared'
import { formatAdminURL } from 'payload/shared'
import type { initPage } from '../../utilities/initPage/index.js'

View File

@@ -8,9 +8,9 @@ import type {
} from 'payload'
import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent'
import { formatAdminURL } from '@payloadcms/ui/shared'
import { getClientConfig } from '@payloadcms/ui/utilities/getClientConfig'
import { notFound, redirect } from 'next/navigation.js'
import { formatAdminURL } from 'payload/shared'
import React, { Fragment } from 'react'
import { DefaultTemplate } from '../../templates/Default/index.js'
@@ -56,7 +56,7 @@ export const RootPage = async ({
const currentRoute = formatAdminURL({
adminRoute,
path: `${Array.isArray(params.segments) ? `/${params.segments.join('/')}` : ''}`,
path: Array.isArray(params.segments) ? `/${params.segments.join('/')}` : null,
})
const segments = Array.isArray(params.segments) ? params.segments : []

View File

@@ -1,7 +1,7 @@
import type { AdminViewServerProps } from 'payload'
import { Button } from '@payloadcms/ui'
import { formatAdminURL } from '@payloadcms/ui/shared'
import { formatAdminURL } from 'payload/shared'
import React from 'react'
import { FormHeader } from '../../elements/FormHeader/index.js'

View File

@@ -1,6 +1,6 @@
import type { AdminViewServerProps } from 'payload'
import { formatAdminURL } from '@payloadcms/ui/shared'
import { formatAdminURL } from 'payload/shared'
import React from 'react'
import { Logo } from '../../elements/Logo/index.js'

View File

@@ -5,8 +5,8 @@ import type React from 'react'
import { getTranslation } from '@payloadcms/translations'
import { useConfig, useLocale, useStepNav, useTranslation } from '@payloadcms/ui'
import { formatAdminURL, formatDate } from '@payloadcms/ui/shared'
import { fieldAffectsData } from 'payload/shared'
import { formatDate } from '@payloadcms/ui/shared'
import { fieldAffectsData, formatAdminURL } from 'payload/shared'
import { useEffect } from 'react'
export const SetStepNav: React.FC<{

View File

@@ -11,8 +11,9 @@ import {
useRouteTransition,
useTranslation,
} from '@payloadcms/ui'
import { formatAdminURL, requests } from '@payloadcms/ui/shared'
import { requests } from '@payloadcms/ui/shared'
import { useRouter } from 'next/navigation.js'
import { formatAdminURL } from 'payload/shared'
import React, { Fragment, useCallback, useState } from 'react'
import type { Props } from './types.js'

View File

@@ -1,6 +1,7 @@
'use client'
import { Link, useConfig, useTranslation } from '@payloadcms/ui'
import { formatAdminURL, formatDate } from '@payloadcms/ui/shared'
import { formatDate } from '@payloadcms/ui/shared'
import { formatAdminURL } from 'payload/shared'
import React from 'react'
type CreatedAtCellProps = {

View File

@@ -14,6 +14,7 @@ import { buildAfterOperation } from '../../collections/operations/utils.js'
import { APIError } from '../../errors/index.js'
import { Forbidden } from '../../index.js'
import { commitTransaction } from '../../utilities/commitTransaction.js'
import { formatAdminURL } from '../../utilities/formatAdminURL.js'
import { initTransaction } from '../../utilities/initTransaction.js'
import { killTransaction } from '../../utilities/killTransaction.js'
import { getLoginOptions } from '../getLoginOptions.js'
@@ -155,9 +156,13 @@ export const forgotPasswordOperation = async <TSlug extends CollectionSlug>(
config.serverURL !== null && config.serverURL !== ''
? config.serverURL
: `${protocol}//${req.headers.get('host')}`
const forgotURL = formatAdminURL({
adminRoute: config.routes.admin,
path: `${config.admin.routes.reset}/${token}`,
serverURL,
})
let html = `${req.t('authentication:youAreReceivingResetPassword')}
<a href="${serverURL}${config.routes.admin}${config.admin.routes.reset}/${token}">${serverURL}${config.routes.admin}${config.admin.routes.reset}/${token}</a>
<a href="${forgotURL}">${forgotURL}</a>
${req.t('authentication:youDidNotRequestPassword')}`
if (typeof collectionConfig.auth.forgotPassword?.generateEmailHTML === 'function') {

View File

@@ -881,22 +881,46 @@ export type Config = {
/** Base meta data to use for the Admin Panel. Included properties are titleSuffix, ogImage, and favicon. */
meta?: MetaConfig
routes?: {
/** The route for the account page. */
account?: string
/** The route for the create first user page. */
createFirstUser?: string
/** The route for the forgot password page. */
forgot?: string
/** The route the user will be redirected to after being inactive for too long. */
inactivity?: string
/** The route for the login page. */
login?: string
/** The route for the logout page. */
logout?: string
/** The route for the reset password page. */
reset?: string
/** The route for the unauthorized page. */
unauthorized?: string
/** The route for the account page.
*
* @default '/account'
*/
account?: `/${string}`
/** The route for the create first user page.
*
* @default '/create-first-user'
*/
createFirstUser?: `/${string}`
/** The route for the forgot password page.
*
* @default '/forgot'
*/
forgot?: `/${string}`
/** The route the user will be redirected to after being inactive for too long.
*
* @default '/logout-inactivity'
*/
inactivity?: `/${string}`
/** The route for the login page.
*
* @default '/login'
*/
login?: `/${string}`
/** The route for the logout page.
*
* @default '/logout'
*/
logout?: `/${string}`
/** The route for the reset password page.
*
* @default '/reset'
*/
reset?: `/${string}`
/** The route for the unauthorized page.
*
* @default '/unauthorized'
*/
unauthorized?: `/${string}`
}
/**
* Suppresses React hydration mismatch warnings during the hydration of the root <html> tag.

View File

@@ -64,10 +64,12 @@ export { fieldSchemaToJSON } from '../utilities/fieldSchemaToJSON.js'
export { flattenAllFields } from '../utilities/flattenAllFields.js'
export { default as flattenTopLevelFields } from '../utilities/flattenTopLevelFields.js'
export { getDataByPath } from '../utilities/getDataByPath.js'
export { formatAdminURL } from '../utilities/formatAdminURL.js'
export { getDataByPath } from '../utilities/getDataByPath.js'
export { getSelectMode } from '../utilities/getSelectMode.js'
export { getSiblingData } from '../utilities/getSiblingData.js'
export { getUniqueListBy } from '../utilities/getUniqueListBy.js'
export { isNextBuild } from '../utilities/isNextBuild.js'

View File

@@ -0,0 +1,24 @@
import type { Config } from '../config/types.js'
/** Will read the `routes.admin` config and appropriately handle `"/"` admin paths */
export const formatAdminURL = (args: {
adminRoute: NonNullable<Config['routes']>['admin']
basePath?: string
path: '' | `/${string}` | null | undefined
serverURL?: Config['serverURL']
}): string => {
const { adminRoute, basePath = '', path: pathFromArgs, serverURL } = args
const path = pathFromArgs || ''
if (adminRoute) {
if (adminRoute === '/') {
if (!path) {
return `${serverURL || ''}${basePath}${adminRoute}`
}
} else {
return `${serverURL || ''}${basePath}${adminRoute}${path}`
}
}
return `${serverURL || ''}${basePath}${path}`
}

View File

@@ -1,7 +1,7 @@
'use client'
import { CopyToClipboard, Link, useConfig, useField } from '@payloadcms/ui'
import { formatAdminURL } from '@payloadcms/ui/shared'
import { formatAdminURL } from 'payload/shared'
import React from 'react'
export const LinkToDocClient: React.FC = () => {

View File

@@ -3,7 +3,7 @@ import type { Payload } from 'payload'
import { getTranslation, type I18nClient } from '@payloadcms/translations'
import { Link } from '@payloadcms/ui'
import { formatAdminURL } from '@payloadcms/ui/shared'
import { formatAdminURL } from 'payload/shared'
import React from 'react'
import type { SanitizedServerEditorConfig } from '../lexical/config/types.js'

View File

@@ -2,7 +2,7 @@ import type { DefaultServerCellComponentProps, Payload } from 'payload'
import { getTranslation, type I18nClient } from '@payloadcms/translations'
import { Link } from '@payloadcms/ui'
import { formatAdminURL } from '@payloadcms/ui/shared'
import { formatAdminURL } from 'payload/shared'
import React from 'react'
export const RscEntrySlateCell: React.FC<

View File

@@ -1,11 +1,11 @@
'use client'
import { formatAdminURL } from 'payload/shared'
import React, { useEffect, useRef, useState } from 'react'
import { Account } from '../../graphics/Account/index.js'
import { useActions } from '../../providers/Actions/index.js'
import { useConfig } from '../../providers/Config/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
import { formatAdminURL } from '../../utilities/formatAdminURL.js'
import { Hamburger } from '../Hamburger/index.js'
import { Link } from '../Link/index.js'
import { Localizer } from '../Localizer/index.js'

View File

@@ -1,6 +1,7 @@
'use client'
import { useRouter, useSearchParams } from 'next/navigation.js'
import { formatAdminURL } from 'payload/shared'
import React, { useCallback, useEffect } from 'react'
import type { EditFormProps } from './types.js'
@@ -17,7 +18,6 @@ import { useRouteTransition } from '../../../providers/RouteTransition/index.js'
import { useServerFunctions } from '../../../providers/ServerFunctions/index.js'
import { useUploadEdits } from '../../../providers/UploadEdits/index.js'
import { abortAndIgnore, handleAbortRef } from '../../../utilities/abortAndIgnore.js'
import { formatAdminURL } from '../../../utilities/formatAdminURL.js'
import { useDocumentDrawerContext } from '../../DocumentDrawer/Provider.js'
import { DocumentFields } from '../../DocumentFields/index.js'
import { Upload } from '../../Upload/index.js'

View File

@@ -4,6 +4,7 @@ import type { SanitizedCollectionConfig } from 'payload'
import { useModal } from '@faceless-ui/modal'
import { getTranslation } from '@payloadcms/translations'
import { useRouter } from 'next/navigation.js'
import { formatAdminURL } from 'payload/shared'
import React, { useCallback } from 'react'
import { toast } from 'sonner'
@@ -15,7 +16,6 @@ import { useDocumentInfo } from '../../providers/DocumentInfo/index.js'
import { useRouteTransition } from '../../providers/RouteTransition/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
import { requests } from '../../utilities/api.js'
import { formatAdminURL } from '../../utilities/formatAdminURL.js'
import { ConfirmationModal } from '../ConfirmationModal/index.js'
import { PopupList } from '../Popup/index.js'
import { Translation } from '../Translation/index.js'

View File

@@ -7,6 +7,7 @@ import type {
} from 'payload'
import { getTranslation } from '@payloadcms/translations'
import { formatAdminURL } from 'payload/shared'
import React, { Fragment, useEffect } from 'react'
import type { DocumentDrawerContextType } from '../DocumentDrawer/Provider.js'
@@ -15,7 +16,6 @@ import { useFormInitializing, useFormProcessing } from '../../forms/Form/context
import { useConfig } from '../../providers/Config/index.js'
import { useEditDepth } from '../../providers/EditDepth/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
import { formatAdminURL } from '../../utilities/formatAdminURL.js'
import { formatDate } from '../../utilities/formatDate.js'
import { Autosave } from '../Autosave/index.js'
import { Button } from '../Button/index.js'

View File

@@ -5,6 +5,7 @@ import type { SanitizedCollectionConfig } from 'payload'
import { useModal } from '@faceless-ui/modal'
import { getTranslation } from '@payloadcms/translations'
import { useRouter } from 'next/navigation.js'
import { formatAdminURL } from 'payload/shared'
import React, { useCallback } from 'react'
import { toast } from 'sonner'
@@ -16,7 +17,6 @@ import { useLocale } from '../../providers/Locale/index.js'
import { useRouteTransition } from '../../providers/RouteTransition/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
import { requests } from '../../utilities/api.js'
import { formatAdminURL } from '../../utilities/formatAdminURL.js'
import { ConfirmationModal } from '../ConfirmationModal/index.js'
import { PopupList } from '../Popup/index.js'

View File

@@ -1,10 +1,10 @@
'use client'
import { formatAdminURL } from 'payload/shared'
import React from 'react'
import { LogOutIcon } from '../../icons/LogOut/index.js'
import { useConfig } from '../../providers/Config/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
import { formatAdminURL } from '../../utilities/formatAdminURL.js'
import { Link } from '../Link/index.js'
const baseClass = 'nav'

View File

@@ -1,5 +1,6 @@
'use client'
import { useRouter } from 'next/navigation.js'
import { formatAdminURL } from 'payload/shared'
import React, { useCallback } from 'react'
import type { OnCancel } from '../ConfirmationModal/index.js'
@@ -8,7 +9,6 @@ import { useAuth } from '../../providers/Auth/index.js'
import { useConfig } from '../../providers/Config/index.js'
import { useRouteTransition } from '../../providers/RouteTransition/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
import { formatAdminURL } from '../../utilities/formatAdminURL.js'
import { ConfirmationModal } from '../ConfirmationModal/index.js'
export const stayLoggedInModalSlug = 'stay-logged-in'

View File

@@ -1,13 +1,12 @@
'use client'
import type { ClientCollectionConfig, DefaultCellComponentProps, UploadFieldClient } from 'payload'
import type { DefaultCellComponentProps, UploadFieldClient } from 'payload'
import { getTranslation } from '@payloadcms/translations'
import { fieldAffectsData, fieldIsID } from 'payload/shared'
import { fieldAffectsData, fieldIsID, formatAdminURL } from 'payload/shared'
import React from 'react' // TODO: abstract this out to support all routers
import { useConfig } from '../../../providers/Config/index.js'
import { useTranslation } from '../../../providers/Translation/index.js'
import { formatAdminURL } from '../../../utilities/formatAdminURL.js'
import { Link } from '../../Link/index.js'
import { CodeCell } from './fields/Code/index.js'
import { cellComponents } from './fields/index.js'

View File

@@ -1,11 +1,11 @@
'use client'
import { usePathname } from 'next/navigation.js'
import { formatAdminURL } from 'payload/shared'
import React from 'react'
// import { RenderComponent } from '../../elements/RenderComponent/client.js'
import { useAuth } from '../../providers/Auth/index.js'
import { useConfig } from '../../providers/Config/index.js'
import { formatAdminURL } from '../../utilities/formatAdminURL.js'
import { DefaultAccountIcon } from './Default/index.js'
import { GravatarAccountIcon } from './Gravatar/index.js'

View File

@@ -3,6 +3,7 @@ import type { ClientUser, SanitizedPermissions, User } from 'payload'
import { useModal } from '@faceless-ui/modal'
import { usePathname, useRouter } from 'next/navigation.js'
import { formatAdminURL } from 'payload/shared'
import * as qs from 'qs-esm'
import React, { createContext, useCallback, useContext, useEffect, useState } from 'react'
import { toast } from 'sonner'
@@ -11,7 +12,6 @@ import { stayLoggedInModalSlug } from '../../elements/StayLoggedIn/index.js'
import { useDebounce } from '../../hooks/useDebounce.js'
import { useTranslation } from '../../providers/Translation/index.js'
import { requests } from '../../utilities/api.js'
import { formatAdminURL } from '../../utilities/formatAdminURL.js'
import { useConfig } from '../Config/index.js'
import { useRouteTransition } from '../RouteTransition/index.js'

View File

@@ -1,23 +1,2 @@
import type { Config } from 'payload'
/** Will read the `routes.admin` config and appropriately handle `"/"` admin paths */
export const formatAdminURL = (args: {
adminRoute: Config['routes']['admin']
basePath?: string
path: string
serverURL?: Config['serverURL']
}): string => {
const { adminRoute, basePath = '', path, serverURL } = args
if (adminRoute) {
if (adminRoute === '/') {
if (!path) {
return `${serverURL || ''}${basePath}${adminRoute}`
}
} else {
return `${serverURL || ''}${basePath}${adminRoute}${path}`
}
}
return `${serverURL || ''}${basePath}${path}`
}
export { formatAdminURL } from 'payload/shared'

View File

@@ -1,6 +1,6 @@
import type { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime.js'
import { formatAdminURL } from './formatAdminURL.js'
import { formatAdminURL } from 'payload/shared'
type BackToDashboardProps = {
adminRoute: string

View File

@@ -1,6 +1,6 @@
import type { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime.js'
import { formatAdminURL } from './formatAdminURL.js'
import { formatAdminURL } from 'payload/shared'
type GoBackProps = {
adminRoute: string

View File

@@ -2,6 +2,7 @@
import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload'
import { getTranslation } from '@payloadcms/translations'
import { formatAdminURL } from 'payload/shared'
import { useEffect } from 'react'
import type { StepNavItem } from '../../../elements/StepNav/index.js'
@@ -12,7 +13,6 @@ import { useDocumentInfo } from '../../../providers/DocumentInfo/index.js'
import { useEditDepth } from '../../../providers/EditDepth/index.js'
import { useEntityVisibility } from '../../../providers/EntityVisibility/index.js'
import { useTranslation } from '../../../providers/Translation/index.js'
import { formatAdminURL } from '../../../utilities/formatAdminURL.js'
export const SetDocumentStepNav: React.FC<{
collectionSlug?: SanitizedCollectionConfig['slug']

View File

@@ -3,6 +3,7 @@
import type { ClientUser, DocumentViewClientProps, FormState } from 'payload'
import { useRouter, useSearchParams } from 'next/navigation.js'
import { formatAdminURL } from 'payload/shared'
import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import type { FormProps } from '../../forms/Form/index.js'
@@ -27,7 +28,6 @@ import { useRouteTransition } from '../../providers/RouteTransition/index.js'
import { useServerFunctions } from '../../providers/ServerFunctions/index.js'
import { useUploadEdits } from '../../providers/UploadEdits/index.js'
import { abortAndIgnore, handleAbortRef } from '../../utilities/abortAndIgnore.js'
import { formatAdminURL } from '../../utilities/formatAdminURL.js'
import { handleBackToDashboard } from '../../utilities/handleBackToDashboard.js'
import { handleGoBack } from '../../utilities/handleGoBack.js'
import { handleTakeOver } from '../../utilities/handleTakeOver.js'

View File

@@ -17,15 +17,17 @@ import { setTimeout } from 'timers/promises'
import { devUser } from './credentials.js'
import { POLL_TOPASS_TIMEOUT } from './playwright.config.js'
type AdminRoutes = NonNullable<Config['admin']>['routes']
type FirstRegisterArgs = {
customAdminRoutes?: Config['admin']['routes']
customAdminRoutes?: AdminRoutes
customRoutes?: Config['routes']
page: Page
serverURL: string
}
type LoginArgs = {
customAdminRoutes?: Config['admin']['routes']
customAdminRoutes?: AdminRoutes
customRoutes?: Config['routes']
data?: {
email: string
@@ -78,16 +80,14 @@ export async function ensureCompilationIsDone({
noAutoLogin,
readyURL,
}: {
customAdminRoutes?: Config['admin']['routes']
customAdminRoutes?: AdminRoutes
customRoutes?: Config['routes']
noAutoLogin?: boolean
page: Page
readyURL?: string
serverURL: string
}): Promise<void> {
const {
routes: { admin: adminRoute },
} = getRoutes({ customAdminRoutes, customRoutes })
const { routes: { admin: adminRoute } = {} } = getRoutes({ customAdminRoutes, customRoutes })
const adminURL = `${serverURL}${adminRoute}`
@@ -170,9 +170,7 @@ export async function throttleTest({
export async function firstRegister(args: FirstRegisterArgs): Promise<void> {
const { customAdminRoutes, customRoutes, page, serverURL } = args
const {
routes: { admin: adminRoute },
} = getRoutes({ customAdminRoutes, customRoutes })
const { routes: { admin: adminRoute } = {} } = getRoutes({ customAdminRoutes, customRoutes })
await page.goto(`${serverURL}${adminRoute}`)
await page.fill('#field-email', devUser.email)
@@ -187,10 +185,8 @@ export async function login(args: LoginArgs): Promise<void> {
const { customAdminRoutes, customRoutes, data = devUser, page, serverURL } = args
const {
admin: {
routes: { createFirstUser, login: incomingLoginRoute },
},
routes: { admin: incomingAdminRoute },
admin: { routes: { createFirstUser, login: incomingLoginRoute } = {} },
routes: { admin: incomingAdminRoute } = {},
} = getRoutes({ customAdminRoutes, customRoutes })
const adminRoute = formatAdminURL({ serverURL, adminRoute: incomingAdminRoute, path: '' })
@@ -462,8 +458,6 @@ export function describeIfInCIOrHasLocalstack(): jest.Describe {
return describe
}
type AdminRoutes = Config['admin']['routes']
export function getRoutes({
customAdminRoutes,
customRoutes,
@@ -477,7 +471,7 @@ export function getRoutes({
routes: Config['routes']
} {
let routes = defaults.routes
let adminRoutes = defaults.admin.routes
let adminRoutes = defaults.admin?.routes
if (customAdminRoutes) {
adminRoutes = {

View File

@@ -71,7 +71,7 @@ export class AdminUrlUtil {
collection(slug: string): string {
return formatAdminURL({
adminRoute: this.routes.admin,
adminRoute: this.routes?.admin,
path: `/collections/${slug}`,
serverURL: this.serverURL,
})
@@ -83,7 +83,7 @@ export class AdminUrlUtil {
global(slug: string): string {
return formatAdminURL({
adminRoute: this.routes.admin,
adminRoute: this.routes?.admin,
path: `/globals/${slug}`,
serverURL: this.serverURL,
})