diff --git a/apps/activitypub/src/utils/content-formatters.ts b/apps/activitypub/src/utils/content-formatters.ts index 2661a6b783d..080b77693e2 100644 --- a/apps/activitypub/src/utils/content-formatters.ts +++ b/apps/activitypub/src/utils/content-formatters.ts @@ -24,27 +24,18 @@ export function sanitizeHtml(html: string): string { return DOMPurify.sanitize(html); } -// Must stay in sync with allowedScriptHostnames in the backend sanitizer -// (https://github.com/TryGhost/ActivityPub/blob/main/src/helpers/html.ts) -const ALLOWED_SCRIPT_HOSTNAMES = ['platform.twitter.com', 'platform.x.com']; - -// Article content needs looser rules than sanitizeHtml (iframes for YouTube -// embeds, scripts for Twitter embeds), so it gets its own DOMPurify instance -const articlePurify = DOMPurify(window); - -articlePurify.addHook('uponSanitizeElement', (node, data) => { - if (data.tagName !== 'script') { - return; - } - - const element = node as Element; - const src = element.getAttribute('src') || ''; +// Removes any " onload="window.__xss=true"> + + `); const iframes = renderHtml(result).querySelectorAll('iframe'); + expect(iframes).toHaveLength(2); iframes.forEach((iframe) => { - expect(iframe.getAttribute('src')).toBeNull(); - expect(iframe.hasAttribute('srcdoc')).toBe(false); - expect(iframe.hasAttribute('onload')).toBe(false); + // Overridden to our fixed set (attacker's allow-top-navigation is gone) + expect(iframe.getAttribute('sandbox')).toBe(sandbox); }); + // Arbitrary embed hosts are kept (not host-filtered), just sandboxed + expect(iframes[0].getAttribute('src')).toBe('https://codepen.io/x/embed/abc'); + expect(iframes[1].getAttribute('src')).toBe('https://evil.example/phish'); + }); + + it('does not preserve author-supplied referrerpolicy on non-iframe elements', function () { + const result = sanitizeArticleContent( + '' + ); + + const img = renderHtml(result).querySelector('img') as HTMLImageElement; + + expect(img).not.toBeNull(); + expect(img.hasAttribute('referrerpolicy')).toBe(false); + }); + + it('removes iframes with unsafe or non-http(s) sources', function () { + const result = sanitizeArticleContent(` + + + + `); + + // None are absolute cross-origin http(s) embeds, so all are dropped + expect(renderHtml(result).querySelectorAll('iframe')).toHaveLength(0); + }); + + it('strips event handlers and srcdoc from surviving cross-origin iframes', function () { + const result = sanitizeArticleContent( + '' + ); + + const iframe = renderHtml(result).querySelector('iframe') as HTMLIFrameElement; + + expect(iframe).not.toBeNull(); + expect(iframe.hasAttribute('srcdoc')).toBe(false); + expect(iframe.hasAttribute('onload')).toBe(false); + }); + + it('removes relative and same-origin iframes, keeping cross-origin embeds', function () { + // A same-origin frame would run same-origin with Ghost Admin, where + // allow-scripts + allow-same-origin can defeat the sandbox + const result = sanitizeArticleContent(` + + + + + `); + + const iframes = renderHtml(result).querySelectorAll('iframe'); + + expect(iframes).toHaveLength(1); + expect(iframes[0].getAttribute('src')).toBe('https://player.vimeo.com/video/123'); }); it('keeps Twitter embed scripts from allowed hostnames', function () { diff --git a/apps/admin-x-design-system/src/global/chrome/desktop-chrome.stories.tsx b/apps/admin-x-design-system/src/global/chrome/desktop-chrome.stories.tsx deleted file mode 100644 index f28afed48e6..00000000000 --- a/apps/admin-x-design-system/src/global/chrome/desktop-chrome.stories.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import {ReactNode} from 'react'; -import type {Meta, StoryObj} from '@storybook/react-vite'; - -import DesktopChrome from './desktop-chrome'; - -const meta = { - title: 'Global / Chrome / Desktop Chrome', - component: DesktopChrome, - tags: ['autodocs'], - decorators: [(_story: () => ReactNode) => (
{_story()}
)] -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - children: ( -
-

This is a desktop chrome with lots of text. This is a desktop chrome with lots of text. This is a desktop chrome with lots of text. This is a desktop chrome with lots of text. This is a desktop chrome with lots of text. This is a desktop chrome with lots of text. This is a desktop chrome with lots of text. This is a desktop chrome with lots of text. This is a desktop chrome with lots of text. This is a desktop chrome with lots of text.

- - Testimage - -

This is a desktop chrome with lots of text. This is a desktop chrome with lots of text. This is a desktop chrome with lots of text. This is a desktop chrome with lots of text. This is a desktop chrome with lots of text. This is a desktop chrome with lots of text. This is a desktop chrome with lots of text. This is a desktop chrome with lots of text. This is a desktop chrome with lots of text. This is a desktop chrome with lots of text.

-
- ) - } -}; diff --git a/apps/admin-x-design-system/src/global/chrome/desktop-chrome.tsx b/apps/admin-x-design-system/src/global/chrome/desktop-chrome.tsx deleted file mode 100644 index e4ac2ba2063..00000000000 --- a/apps/admin-x-design-system/src/global/chrome/desktop-chrome.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; - -export interface DesktopChromeProps { - children?: React.ReactNode; -} - -const DesktopChrome: React.FC> = ({children, ...props}) => { - return ( -
-
- {children} -
-
- ); -}; - -export default DesktopChrome; diff --git a/apps/admin-x-design-system/src/global/chrome/mobile-chrome.stories.tsx b/apps/admin-x-design-system/src/global/chrome/mobile-chrome.stories.tsx deleted file mode 100644 index 443d7d14a98..00000000000 --- a/apps/admin-x-design-system/src/global/chrome/mobile-chrome.stories.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import {ReactNode} from 'react'; -import type {Meta, StoryObj} from '@storybook/react-vite'; - -import MobileChrome from './mobile-chrome'; - -const meta = { - title: 'Global / Chrome / Mobile Chrome', - component: MobileChrome, - tags: ['autodocs'], - decorators: [(_story: () => ReactNode) => (
{_story()}
)] -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - children: ( -
-

This is a mobile chrome with lots of text. This is a mobile chrome with lots of text. This is a mobile chrome with lots of text. This is a mobile chrome with lots of text. This is a mobile chrome with lots of text. This is a mobile chrome with lots of text. This is a mobile chrome with lots of text. This is a mobile chrome with lots of text. This is a mobile chrome with lots of text. This is a mobile chrome with lots of text.

- - Testimage - -

This is a mobile chrome with lots of text. This is a mobile chrome with lots of text. This is a mobile chrome with lots of text. This is a mobile chrome with lots of text. This is a mobile chrome with lots of text. This is a mobile chrome with lots of text. This is a mobile chrome with lots of text. This is a mobile chrome with lots of text. This is a mobile chrome with lots of text. This is a mobile chrome with lots of text.

-
- ) - } -}; diff --git a/apps/admin-x-design-system/src/global/chrome/mobile-chrome.tsx b/apps/admin-x-design-system/src/global/chrome/mobile-chrome.tsx deleted file mode 100644 index 1637f54defb..00000000000 --- a/apps/admin-x-design-system/src/global/chrome/mobile-chrome.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; - -export interface MobileChromeProps { - children?: React.ReactNode; -} - -const MobileChrome: React.FC> = ({children, ...props}) => { - return ( -
-
- {children} -
-
- ); -}; - -export default MobileChrome; diff --git a/apps/admin-x-design-system/src/global/modal/confirmation-modal.stories.tsx b/apps/admin-x-design-system/src/global/modal/confirmation-modal.stories.tsx deleted file mode 100644 index b4b53f93eba..00000000000 --- a/apps/admin-x-design-system/src/global/modal/confirmation-modal.stories.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import type {Meta, StoryContext, StoryObj} from '@storybook/react-vite'; -import {ReactNode} from 'react'; - -import NiceModal from '@ebay/nice-modal-react'; -import ConfirmationModal, {ConfirmationModalProps} from './confirmation-modal'; - -const ConfirmationModalContainer: React.FC = ({...props}) => { - return ( - - ); -}; - -const meta = { - title: 'Global / Modal / Confirmation Modal', - component: ConfirmationModal, - tags: ['autodocs'], - decorators: [(_story: () => ReactNode, context: StoryContext) => ( - - - - )] -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - title: 'Are you sure?', - prompt: 'Watch out, you\'re doing something super-super dangerous. Don\'t press the red button (you know you will).' - } -}; - -export const CustomButtons: Story = { - args: { - ...Default.args, - title: 'You want to delete?', - cancelLabel: 'Meh', - okLabel: 'Alrite', - okVariant: 'destructive' - } -}; diff --git a/apps/admin-x-design-system/src/global/modal/confirmation-modal.tsx b/apps/admin-x-design-system/src/global/modal/confirmation-modal.tsx deleted file mode 100644 index 7ec2ba47737..00000000000 --- a/apps/admin-x-design-system/src/global/modal/confirmation-modal.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import Modal from './modal'; -import NiceModal, {useModal} from '@ebay/nice-modal-react'; -import React, {useState} from 'react'; -import type {ButtonProps} from '@tryghost/shade/components'; - -export interface ConfirmationModalProps { - title?: React.ReactNode; - prompt?: React.ReactNode; - cancelLabel?: string; - okLabel?: string; - okRunningLabel?: string; - okVariant?: ButtonProps['variant']; - onCancel?: () => void; - onOk?: (modal?: { - remove: () => void; - }) => void | Promise; - customFooter?: boolean | React.ReactNode; - formSheet?: boolean; - stickyFooter?: boolean; -} - -export const ConfirmationModalContent: React.FC = ({ - title = 'Are you sure?', - prompt, - cancelLabel = 'Cancel', - okLabel = 'OK', - okRunningLabel = '...', - okVariant = 'default', - onCancel, - onOk, - customFooter, - formSheet = true, - stickyFooter = false -}) => { - const modal = useModal(); - const [taskState, setTaskState] = useState<'running' | ''>(''); - return ( - { - setTaskState('running'); - - try { - await onOk?.(modal); - } catch (e) { - // eslint-disable-next-line no-console - console.error('Unhandled Promise Rejection. Make sure you catch errors in your onOk handler.', e); - } - setTaskState(''); - }} - > -
- {prompt} -
-
- ); -}; - -export default NiceModal.create(ConfirmationModalContent); diff --git a/apps/admin-x-design-system/src/global/modal/limit-modal.stories.tsx b/apps/admin-x-design-system/src/global/modal/limit-modal.stories.tsx deleted file mode 100644 index 8fcf9d493ef..00000000000 --- a/apps/admin-x-design-system/src/global/modal/limit-modal.stories.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import type {Meta, StoryContext, StoryObj} from '@storybook/react-vite'; -import {ReactNode} from 'react'; - -import NiceModal from '@ebay/nice-modal-react'; -import LimitModal, {LimitModalProps} from './limit-modal'; - -const LimitModalContainer: React.FC = ({...props}) => { - return ( - - ); -}; - -const meta = { - title: 'Global / Modal / Limit Modal', - component: LimitModal, - tags: ['autodocs'], - decorators: [(_story: () => ReactNode, context: StoryContext) => ( - - - - )] -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - title: 'You need to upgrade your plan', - prompt: 'Your current plan only supports free integrations including Slack, Unsplash, and FirstPromoter. To add a custom integration, upgrade to a different plan.' - } -}; diff --git a/apps/admin-x-design-system/src/global/modal/modal-page.stories.tsx b/apps/admin-x-design-system/src/global/modal/modal-page.stories.tsx deleted file mode 100644 index 3250b0ae00d..00000000000 --- a/apps/admin-x-design-system/src/global/modal/modal-page.stories.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import type {Meta, StoryObj} from '@storybook/react-vite'; - -import ModalPage from './modal-page'; - -const meta = { - title: 'Global / Modal / Modal page contents', - component: ModalPage, - tags: ['autodocs'] -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - heading: 'Here\'s a modal page', - children: <> -

Use this component to in full-width or bleed modals in which you build a complete page (e.g. Theme grid)

- - } -}; diff --git a/apps/admin-x-design-system/src/global/modal/modal-page.tsx b/apps/admin-x-design-system/src/global/modal/modal-page.tsx deleted file mode 100644 index 66cedc539c9..00000000000 --- a/apps/admin-x-design-system/src/global/modal/modal-page.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import clsx from 'clsx'; -import React from 'react'; -import {Text} from '@tryghost/shade/primitives'; - -export interface ModalPageProps { - heading?: string; - children?: React.ReactNode; - className?: string; -} - -const ModalPage: React.FC = ({heading, children, className}) => { - className = clsx( - 'w-full p-[8vmin] pt-5', - className - ); - return ( -
- {heading && {heading}} - {children} -
- ); -}; - -export default ModalPage; diff --git a/apps/admin-x-design-system/src/global/modal/modal.stories.tsx b/apps/admin-x-design-system/src/global/modal/modal.stories.tsx index 635668ea92e..e90afa48a0e 100644 --- a/apps/admin-x-design-system/src/global/modal/modal.stories.tsx +++ b/apps/admin-x-design-system/src/global/modal/modal.stories.tsx @@ -3,7 +3,6 @@ import {ReactNode} from 'react'; import NiceModal from '@ebay/nice-modal-react'; import Modal, {ModalProps} from './modal'; -import ModalPage from './modal-page'; const ModalContainer: React.FC = ({children, ...props}) => { const modal = NiceModal.create(() => { @@ -176,9 +175,7 @@ export const CompletePage: Story = { footer: <>, padding: false, children: <> - -

This is a full page in a modal

-
+
Full-page modal content
} }; diff --git a/apps/admin-x-design-system/src/index.ts b/apps/admin-x-design-system/src/index.ts index 11580ca4008..350d99264ff 100644 --- a/apps/admin-x-design-system/src/index.ts +++ b/apps/admin-x-design-system/src/index.ts @@ -3,21 +3,11 @@ export {default as GoogleLogo} from './assets/images/google-logo.svg?react'; export {default as TwitterLogo} from './assets/images/twitter-logo.svg?react'; export {default as XLogo} from './assets/images/x-logo.svg?react'; -export {default as DesktopChrome} from './global/chrome/desktop-chrome'; -export type {DesktopChromeProps} from './global/chrome/desktop-chrome'; export {default as DesktopChromeHeader} from './global/chrome/desktop-chrome-header'; export type {DesktopChromeHeaderProps} from './global/chrome/desktop-chrome-header'; -export {default as MobileChrome} from './global/chrome/mobile-chrome'; -export type {MobileChromeProps} from './global/chrome/mobile-chrome'; -export {default as ConfirmationModal, ConfirmationModalContent} from './global/modal/confirmation-modal'; -export type {ConfirmationModalProps} from './global/modal/confirmation-modal'; -export {default as LimitModal, LimitModalContent} from './global/modal/limit-modal'; -export type {LimitModalProps} from './global/modal/limit-modal'; export {default as Modal, topLevelBackdropClasses} from './global/modal/modal'; export type {ModalProps} from './global/modal/modal'; -export {default as ModalPage} from './global/modal/modal-page'; -export type {ModalPageProps} from './global/modal/modal-page'; export {default as PreviewModal, PreviewModalContent} from './global/modal/preview-modal'; export type {PreviewModalProps} from './global/modal/preview-modal'; diff --git a/apps/admin-x-framework/src/api/config.ts b/apps/admin-x-framework/src/api/config.ts index fc095a9033a..c3ce0026861 100644 --- a/apps/admin-x-framework/src/api/config.ts +++ b/apps/admin-x-framework/src/api/config.ts @@ -70,7 +70,11 @@ export type Config = { }, publicSiteAccess?: { disabled: boolean, - error?: string + // Copy shown in the pre-launch banner when public site access is disabled. + // Managed hosting providers can override these; each falls back to Ghost's default. + error?: string, // Banner message + title?: string, // Banner heading + upgradeUrl?: string // Destination for the banner's upgrade button } } billing?: { diff --git a/apps/admin-x-settings/src/components/confirmation-modal.tsx b/apps/admin-x-settings/src/components/confirmation-modal.tsx new file mode 100644 index 00000000000..552d32e1864 --- /dev/null +++ b/apps/admin-x-settings/src/components/confirmation-modal.tsx @@ -0,0 +1,123 @@ +import NiceModal, {useModal} from '@ebay/nice-modal-react'; +import React, {useState} from 'react'; +import { + AlertDialog, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + Button, + type ButtonProps, + StickyFooter +} from '@tryghost/shade/components'; +import {cn} from '@tryghost/shade/utils'; + +export interface ConfirmationModalProps { + title?: React.ReactNode; + prompt?: React.ReactNode; + cancelLabel?: string; + okLabel?: string; + okRunningLabel?: string; + okVariant?: ButtonProps['variant']; + onCancel?: () => void; + onOk?: (modal?: { + remove: () => void; + }) => void | Promise; + customFooter?: boolean | React.ReactNode; + formSheet?: boolean; + stickyFooter?: boolean; + testId?: string; +} + +export const ConfirmationModalContent: React.FC = ({ + title = 'Are you sure?', + prompt, + cancelLabel = 'Cancel', + okLabel = 'OK', + okRunningLabel = '...', + okVariant = 'default', + onCancel, + onOk, + customFooter, + formSheet = true, + stickyFooter = false, + testId = 'confirmation-modal' +}) => { + const modal = useModal(); + const [taskState, setTaskState] = useState<'running' | ''>(''); + const isRunning = taskState === 'running'; + + const handleCancel = () => { + if (isRunning) { + return; + } + + if (onCancel) { + onCancel(); + } else { + modal.remove(); + } + }; + + const handleConfirm = async () => { + setTaskState('running'); + + try { + await onOk?.(modal); + } catch (error) { + // eslint-disable-next-line no-console + console.error('Unhandled Promise Rejection. Make sure you catch errors in your onOk handler.', error); + } + + setTaskState(''); + }; + + const defaultFooter = ( + + {cancelLabel && ( + + )} + {okLabel && ( + + )} + + ); + + const footer = customFooter === undefined ? defaultFooter : customFooter; + + return ( + !open && handleCancel()}> + event.stopPropagation()} + > + + {title} + +
{prompt}
+
+
+ {footer && (stickyFooter ? ( + + {footer} + + ) : footer)} +
+
+ ); +}; + +export default NiceModal.create(ConfirmationModalContent); diff --git a/apps/admin-x-design-system/src/global/modal/limit-modal.tsx b/apps/admin-x-settings/src/components/limit-modal.tsx similarity index 60% rename from apps/admin-x-design-system/src/global/modal/limit-modal.tsx rename to apps/admin-x-settings/src/components/limit-modal.tsx index ef3085775d7..20f5b4bd033 100644 --- a/apps/admin-x-design-system/src/global/modal/limit-modal.tsx +++ b/apps/admin-x-settings/src/components/limit-modal.tsx @@ -1,6 +1,7 @@ import NiceModal from '@ebay/nice-modal-react'; import React from 'react'; -import Modal from './modal'; + +import {ConfirmationModalContent} from './confirmation-modal'; export interface LimitModalProps { title?: string; @@ -19,25 +20,19 @@ export const LimitModalContent: React.FC = ({ formSheet = false, onOk }) => { + const promptContent = typeof prompt === 'string' && prompt.includes('<') ? ( +
+ ) : prompt; + return ( - {promptContent}
} testId='limit-modal' title={title} - width={540} onOk={onOk} - > -
- {typeof prompt === 'string' && prompt.includes('<') ? ( -
- ) : ( - prompt - )} -
- + /> ); }; diff --git a/apps/admin-x-settings/src/components/settings/advanced/danger-zone.tsx b/apps/admin-x-settings/src/components/settings/advanced/danger-zone.tsx index be14060704c..5e96fa0cf70 100644 --- a/apps/admin-x-settings/src/components/settings/advanced/danger-zone.tsx +++ b/apps/admin-x-settings/src/components/settings/advanced/danger-zone.tsx @@ -1,10 +1,10 @@ +import ConfirmationModal from '../../confirmation-modal'; import NiceModal from '@ebay/nice-modal-react'; import React from 'react'; import TopLevelGroup from '../../top-level-group'; import trackEvent from '../../../utils/analytics'; import useStaffUsers from '../../../hooks/use-staff-users'; import {ActionList, ActionListItem, ActionListItemActions, ActionListItemContent, Button} from '@tryghost/shade/components'; -import {ConfirmationModal} from '@tryghost/admin-x-design-system'; import {formatNumber} from '@tryghost/shade/utils'; import {getGhostPaths} from '@tryghost/admin-x-framework/helpers'; import {toast} from 'sonner'; diff --git a/apps/admin-x-settings/src/components/settings/advanced/integrations.tsx b/apps/admin-x-settings/src/components/settings/advanced/integrations.tsx index 73a99ec2aa2..9d65ca7cf0c 100644 --- a/apps/admin-x-settings/src/components/settings/advanced/integrations.tsx +++ b/apps/admin-x-settings/src/components/settings/advanced/integrations.tsx @@ -1,11 +1,11 @@ import BrandIcon from '../../icons/brand-icon'; +import ConfirmationModal from '../../confirmation-modal'; import IntegrationsSettingsImg from '../../../assets/images/integrations-settings.png'; import NiceModal from '@ebay/nice-modal-react'; import React, {useState} from 'react'; import TopLevelGroup from '../../top-level-group'; import usePinturaEditor from '../../../hooks/use-pintura-editor'; import {ActionList, ActionListItem, ActionListItemActions, ActionListItemContent, Button, NoValueLabel, NoValueLabelIcon, Tabs, TabsContent, TabsList, TabsTrigger} from '@tryghost/shade/components'; -import {ConfirmationModal} from '@tryghost/admin-x-design-system'; import {type Integration, useBrowseIntegrations, useDeleteIntegration} from '@tryghost/admin-x-framework/api/integrations'; import {LucideIcon} from '@tryghost/shade/utils'; import {Plug} from 'lucide-react'; diff --git a/apps/admin-x-settings/src/components/settings/advanced/integrations/add-integration-modal.tsx b/apps/admin-x-settings/src/components/settings/advanced/integrations/add-integration-modal.tsx index e654d6e3dfd..512587631d1 100644 --- a/apps/admin-x-settings/src/components/settings/advanced/integrations/add-integration-modal.tsx +++ b/apps/admin-x-settings/src/components/settings/advanced/integrations/add-integration-modal.tsx @@ -1,8 +1,9 @@ +import LimitModal from '../../../limit-modal'; import NiceModal, {useModal} from '@ebay/nice-modal-react'; import React, {useEffect, useState} from 'react'; import {Field, FieldError, FieldGroup, FieldLabel, Input} from '@tryghost/shade/components'; import {HostLimitError, useLimiter} from '../../../../hooks/use-limiter'; -import {LimitModal, Modal} from '@tryghost/admin-x-design-system'; +import {Modal} from '@tryghost/admin-x-design-system'; import {type RoutingModalProps, useRouting} from '@tryghost/admin-x-framework/routing'; import {useCreateIntegration} from '@tryghost/admin-x-framework/api/integrations'; import {useHandleError} from '@tryghost/admin-x-framework/hooks'; diff --git a/apps/admin-x-settings/src/components/settings/advanced/integrations/custom-integration-modal.tsx b/apps/admin-x-settings/src/components/settings/advanced/integrations/custom-integration-modal.tsx index 10f6d575633..c93efd8f09c 100644 --- a/apps/admin-x-settings/src/components/settings/advanced/integrations/custom-integration-modal.tsx +++ b/apps/admin-x-settings/src/components/settings/advanced/integrations/custom-integration-modal.tsx @@ -1,13 +1,14 @@ import APIKeys from './api-keys'; +import ConfirmationModal from '../../../confirmation-modal'; import NiceModal, {useModal} from '@ebay/nice-modal-react'; import React, {useEffect, useState} from 'react'; import WebhooksTable from './webhooks-table'; import {APIError} from '@tryghost/admin-x-framework/errors'; import {type APIKey, useRefreshAPIKey} from '@tryghost/admin-x-framework/api/api-keys'; -import {ConfirmationModal, Modal} from '@tryghost/admin-x-design-system'; import {Field, FieldError, FieldGroup, FieldLabel, Input} from '@tryghost/shade/components'; import {ImageUpload, ImageUploadAction, ImageUploadActions, ImageUploadDropzone, ImageUploadImage, ImageUploadPreview} from '@tryghost/shade/patterns'; import {type Integration, useBrowseIntegrations, useEditIntegration} from '@tryghost/admin-x-framework/api/integrations'; +import {Modal} from '@tryghost/admin-x-design-system'; import {type RoutingModalProps, useRouting} from '@tryghost/admin-x-framework/routing'; import {Trash2} from 'lucide-react'; import {getGhostPaths} from '@tryghost/admin-x-framework/helpers'; diff --git a/apps/admin-x-settings/src/components/settings/advanced/integrations/transistor-modal.tsx b/apps/admin-x-settings/src/components/settings/advanced/integrations/transistor-modal.tsx index cec55096df9..a036cb57ccb 100644 --- a/apps/admin-x-settings/src/components/settings/advanced/integrations/transistor-modal.tsx +++ b/apps/admin-x-settings/src/components/settings/advanced/integrations/transistor-modal.tsx @@ -1,10 +1,11 @@ import APIKeys from './api-keys'; import BookmarkThumb from '../../../../assets/images/integrations/ghost-transistor.png'; import BrandIcon from '../../../icons/brand-icon'; +import ConfirmationModal from '../../../confirmation-modal'; import IntegrationHeader from './integration-header'; import NiceModal from '@ebay/nice-modal-react'; -import {ConfirmationModal, Modal} from '@tryghost/admin-x-design-system'; import {Field, FieldContent, FieldDescription, FieldGroup, FieldLabel, FieldLegend, FieldSet, Switch} from '@tryghost/shade/components'; +import {Modal} from '@tryghost/admin-x-design-system'; import {type Setting, getSettingValues, useEditSettings} from '@tryghost/admin-x-framework/api/settings'; import {getGhostPaths} from '@tryghost/admin-x-framework/helpers'; import {useBrowseIntegrations} from '@tryghost/admin-x-framework/api/integrations'; diff --git a/apps/admin-x-settings/src/components/settings/advanced/integrations/webhooks-table.tsx b/apps/admin-x-settings/src/components/settings/advanced/integrations/webhooks-table.tsx index 65887af14f0..3d4fcb8185d 100644 --- a/apps/admin-x-settings/src/components/settings/advanced/integrations/webhooks-table.tsx +++ b/apps/admin-x-settings/src/components/settings/advanced/integrations/webhooks-table.tsx @@ -1,7 +1,7 @@ +import ConfirmationModal from '../../../confirmation-modal'; import NiceModal from '@ebay/nice-modal-react'; import WebhookModal from './webhook-modal'; import {Button, Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from '@tryghost/shade/components'; -import {ConfirmationModal} from '@tryghost/admin-x-design-system'; import {type Integration} from '@tryghost/admin-x-framework/api/integrations'; import {LucideIcon, formatNumber} from '@tryghost/shade/utils'; import {getWebhookEventLabel} from './webhook-event-options'; diff --git a/apps/admin-x-settings/src/components/settings/advanced/integrations/zapier-modal.tsx b/apps/admin-x-settings/src/components/settings/advanced/integrations/zapier-modal.tsx index 7303a48d8fc..9d948c1487d 100644 --- a/apps/admin-x-settings/src/components/settings/advanced/integrations/zapier-modal.tsx +++ b/apps/admin-x-settings/src/components/settings/advanced/integrations/zapier-modal.tsx @@ -1,11 +1,12 @@ import APIKeys from './api-keys'; import BrandIcon from '../../../icons/brand-icon'; +import ConfirmationModal from '../../../confirmation-modal'; import IntegrationHeader from './integration-header'; import NiceModal from '@ebay/nice-modal-react'; import ZapierLogo from '../../../../assets/images/zapier-logo.svg'; import {ActionList, ActionListItem, ActionListItemActions, ActionListItemContent, Button} from '@tryghost/shade/components'; -import {ConfirmationModal, Modal} from '@tryghost/admin-x-design-system'; import {LucideIcon} from '@tryghost/shade/utils'; +import {Modal} from '@tryghost/admin-x-design-system'; import {getGhostPaths} from '@tryghost/admin-x-framework/helpers'; import {useBrowseIntegrations} from '@tryghost/admin-x-framework/api/integrations'; import {useEffect, useState} from 'react'; diff --git a/apps/admin-x-settings/src/components/settings/advanced/labs/migration-options.tsx b/apps/admin-x-settings/src/components/settings/advanced/labs/migration-options.tsx index a1cf288e6e9..c2b037a5da8 100644 --- a/apps/admin-x-settings/src/components/settings/advanced/labs/migration-options.tsx +++ b/apps/admin-x-settings/src/components/settings/advanced/labs/migration-options.tsx @@ -1,8 +1,8 @@ +import ConfirmationModal from '../../../confirmation-modal'; import LabItem from './lab-item'; import NiceModal, {useModal} from '@ebay/nice-modal-react'; import React, {useState} from 'react'; import {ActionList, Button, Dropzone} from '@tryghost/shade/components'; -import {ConfirmationModal} from '@tryghost/admin-x-design-system'; import {downloadAllContent, useDeleteAllContent, useImportContent} from '@tryghost/admin-x-framework/api/db'; import {toast} from 'sonner'; import {useHandleError} from '@tryghost/admin-x-framework/hooks'; diff --git a/apps/admin-x-settings/src/components/settings/advanced/migration-tools/universal-import-modal.tsx b/apps/admin-x-settings/src/components/settings/advanced/migration-tools/universal-import-modal.tsx index 2b8ee04a699..6a9263a5262 100644 --- a/apps/admin-x-settings/src/components/settings/advanced/migration-tools/universal-import-modal.tsx +++ b/apps/admin-x-settings/src/components/settings/advanced/migration-tools/universal-import-modal.tsx @@ -1,7 +1,8 @@ +import ConfirmationModal from '../../../confirmation-modal'; import NiceModal, {useModal} from '@ebay/nice-modal-react'; import React, {useState} from 'react'; import {Button, Dropzone} from '@tryghost/shade/components'; -import {ConfirmationModal, Modal} from '@tryghost/admin-x-design-system'; +import {Modal} from '@tryghost/admin-x-design-system'; import {useHandleError} from '@tryghost/admin-x-framework/hooks'; import {useImportContent} from '@tryghost/admin-x-framework/api/db'; diff --git a/apps/admin-x-settings/src/components/settings/email/emails.tsx b/apps/admin-x-settings/src/components/settings/email/emails.tsx index d87a83012d9..07a46e07ffd 100644 --- a/apps/admin-x-settings/src/components/settings/email/emails.tsx +++ b/apps/admin-x-settings/src/components/settings/email/emails.tsx @@ -1,3 +1,4 @@ +import ConfirmationModal from '../../confirmation-modal'; import DefaultRecipients from './default-recipients'; import EnableNewsletters from './enable-newsletters'; import MailGun from './mailgun'; @@ -10,7 +11,6 @@ import WelcomeEmailCustomizeModal from '../membership/member-emails/welcome-emai import useQueryParams from '../../../hooks/use-query-params'; import {APIError} from '@tryghost/admin-x-framework/errors'; import {ActionList, ActionListItem, ActionListItemActions, ActionListItemContent, Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Tabs, TabsContent, TabsList, TabsTrigger} from '@tryghost/shade/components'; -import {ConfirmationModal} from '@tryghost/admin-x-design-system'; import {LucideIcon} from '@tryghost/shade/utils'; import {getSettingValues} from '@tryghost/admin-x-framework/api/settings'; import {useGlobalData} from '../../providers/global-data-provider'; diff --git a/apps/admin-x-settings/src/components/settings/email/newsletters.tsx b/apps/admin-x-settings/src/components/settings/email/newsletters.tsx index ec1ae707a24..add779a1c3b 100644 --- a/apps/admin-x-settings/src/components/settings/email/newsletters.tsx +++ b/apps/admin-x-settings/src/components/settings/email/newsletters.tsx @@ -1,3 +1,4 @@ +import ConfirmationModal from '../../confirmation-modal'; import NewslettersList from './newsletters/newsletters-list'; import NiceModal, {useModal} from '@ebay/nice-modal-react'; import React, {type ReactNode, useEffect, useState} from 'react'; @@ -5,7 +6,6 @@ import TopLevelGroup from '../../top-level-group'; import useQueryParams from '../../../hooks/use-query-params'; import {APIError} from '@tryghost/admin-x-framework/errors'; import {Button, Tabs, TabsContent, TabsList, TabsTrigger} from '@tryghost/shade/components'; -import {ConfirmationModal} from '@tryghost/admin-x-design-system'; import {type InfiniteData, useQueryClient} from '@tryghost/admin-x-framework'; import {type Newsletter, type NewslettersResponseType, newslettersDataType, useBrowseNewsletters, useEditNewsletter, useVerifyNewsletterEmail} from '@tryghost/admin-x-framework/api/newsletters'; import {arrayMove} from '@dnd-kit/sortable'; diff --git a/apps/admin-x-settings/src/components/settings/email/newsletters/add-newsletter-modal.tsx b/apps/admin-x-settings/src/components/settings/email/newsletters/add-newsletter-modal.tsx index 6118728ecd6..6d573a01c8d 100644 --- a/apps/admin-x-settings/src/components/settings/email/newsletters/add-newsletter-modal.tsx +++ b/apps/admin-x-settings/src/components/settings/email/newsletters/add-newsletter-modal.tsx @@ -1,9 +1,10 @@ +import LimitModal from '../../../limit-modal'; import NiceModal, {useModal} from '@ebay/nice-modal-react'; import React, {useEffect, useState} from 'react'; import useFeatureFlag from '../../../../hooks/use-feature-flag'; import {Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, Input, Switch, Textarea} from '@tryghost/shade/components'; import {HostLimitError, useLimiter} from '../../../../hooks/use-limiter'; -import {LimitModal, Modal} from '@tryghost/admin-x-design-system'; +import {Modal} from '@tryghost/admin-x-design-system'; import {type RoutingModalProps, useRouting} from '@tryghost/admin-x-framework/routing'; import {formatNumber} from '@tryghost/shade/utils'; import {useAddNewsletter} from '@tryghost/admin-x-framework/api/newsletters'; diff --git a/apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx b/apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx index 404bb4b11c8..8a3011f6b27 100644 --- a/apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx +++ b/apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx @@ -1,5 +1,7 @@ import ColorPickerField from '../../../color-picker-field'; +import ConfirmationModal from '../../../confirmation-modal'; import HtmlField from '../../../html-field'; +import LimitModal from '../../../limit-modal'; import NewsletterPreview from './newsletter-preview'; import NiceModal from '@ebay/nice-modal-react'; import React, {useCallback, useEffect, useState} from 'react'; @@ -7,12 +9,12 @@ import useFeatureFlag from '../../../../hooks/use-feature-flag'; import useSettingGroup from '../../../../hooks/use-setting-group'; import validator from 'validator'; import {Button, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSet, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Separator, Switch, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipTrigger} from '@tryghost/shade/components'; -import {ConfirmationModal, LimitModal, PreviewModalContent} from '@tryghost/admin-x-design-system'; import {type ErrorMessages, useForm, useHandleError} from '@tryghost/admin-x-framework/hooks'; import {HostLimitError, useLimiter} from '../../../../hooks/use-limiter'; import {ImageUpload, ImageUploadAction, ImageUploadActions, ImageUploadDropzone, ImageUploadImage, ImageUploadPreview} from '@tryghost/shade/patterns'; import {LucideIcon, formatNumber} from '@tryghost/shade/utils'; import {type Newsletter, useBrowseNewsletters, useEditNewsletter} from '@tryghost/admin-x-framework/api/newsletters'; +import {PreviewModalContent} from '@tryghost/admin-x-design-system'; import {type RoutingModalProps, useRouting} from '@tryghost/admin-x-framework/routing'; import {Stack, Text} from '@tryghost/shade/primitives'; import {Trash2} from 'lucide-react'; diff --git a/apps/admin-x-settings/src/components/settings/email/newsletters/newsletters-tab-content.tsx b/apps/admin-x-settings/src/components/settings/email/newsletters/newsletters-tab-content.tsx index 46388f6597e..66b8e0dd11b 100644 --- a/apps/admin-x-settings/src/components/settings/email/newsletters/newsletters-tab-content.tsx +++ b/apps/admin-x-settings/src/components/settings/email/newsletters/newsletters-tab-content.tsx @@ -1,10 +1,10 @@ +import ConfirmationModal from '../../../confirmation-modal'; import NewslettersList from './newsletters-list'; import NiceModal, {useModal} from '@ebay/nice-modal-react'; import React, {type ReactNode, useEffect, useState} from 'react'; import useQueryParams from '../../../../hooks/use-query-params'; import {APIError} from '@tryghost/admin-x-framework/errors'; import {Button} from '@tryghost/shade/components'; -import {ConfirmationModal} from '@tryghost/admin-x-design-system'; import {type InfiniteData, useQueryClient} from '@tryghost/admin-x-framework'; import {type Newsletter, type NewslettersResponseType, newslettersDataType, useBrowseNewsletters, useEditNewsletter, useVerifyNewsletterEmail} from '@tryghost/admin-x-framework/api/newsletters'; import {arrayMove} from '@dnd-kit/sortable'; diff --git a/apps/admin-x-settings/src/components/settings/general/user-detail-modal.tsx b/apps/admin-x-settings/src/components/settings/general/user-detail-modal.tsx index 32ae51f93cd..162dd4185eb 100644 --- a/apps/admin-x-settings/src/components/settings/general/user-detail-modal.tsx +++ b/apps/admin-x-settings/src/components/settings/general/user-detail-modal.tsx @@ -1,4 +1,6 @@ +import ConfirmationModal from '../../confirmation-modal'; import EmailNotificationsTab from './users/email-notifications-tab'; +import LimitModal from '../../limit-modal'; import NiceModal, {useModal} from '@ebay/nice-modal-react'; import ProfileTab from './users/profile-tab'; import React, {useCallback, useEffect, useRef, useState} from 'react'; @@ -9,11 +11,11 @@ import useStaffUsers from '../../../hooks/use-staff-users'; import validator from 'validator'; import {APIError} from '@tryghost/admin-x-framework/errors'; import {Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, Dropzone, Tabs, TabsContent, TabsList, TabsTrigger} from '@tryghost/shade/components'; -import {ConfirmationModal, LimitModal, Modal} from '@tryghost/admin-x-design-system'; import {type ErrorMessages, useForm, useHandleError} from '@tryghost/admin-x-framework/hooks'; import {HostLimitError, useLimiter} from '../../../hooks/use-limiter'; import {ImageUpload, ImageUploadAction, ImageUploadActions, ImageUploadDropzone, ImageUploadImage, ImageUploadPreview} from '@tryghost/shade/patterns'; import {LucideIcon} from '@tryghost/shade/utils'; +import {Modal} from '@tryghost/admin-x-design-system'; import {Pencil, Trash2} from 'lucide-react'; import {type RoutingModalProps, useRouting} from '@tryghost/admin-x-framework/routing'; import {SOCIAL_PLATFORM_CONFIGS, SOCIAL_PLATFORM_KEYS, getSocialValidationError} from '../../../utils/social-urls/index'; @@ -441,9 +443,9 @@ const UserDetailModalContent: React.FC<{user: User; onDeletingUserChange: (isDel Social Links Email Notifications - - - + + +
diff --git a/apps/admin-x-settings/src/components/settings/general/users/staff-token.tsx b/apps/admin-x-settings/src/components/settings/general/users/staff-token.tsx index 13f3c9a3bd9..14c30e48ed5 100644 --- a/apps/admin-x-settings/src/components/settings/general/users/staff-token.tsx +++ b/apps/admin-x-settings/src/components/settings/general/users/staff-token.tsx @@ -1,6 +1,6 @@ import APIKeys from '../../advanced/integrations/api-keys'; +import ConfirmationModal from '../../../confirmation-modal'; import NiceModal from '@ebay/nice-modal-react'; -import {ConfirmationModal} from '@tryghost/admin-x-design-system'; import {Text} from '@tryghost/shade/primitives'; import {genStaffToken, getStaffToken} from '@tryghost/admin-x-framework/api/staff-token'; import {useEffect, useState} from 'react'; diff --git a/apps/admin-x-settings/src/components/settings/growth/offers/edit-offer-modal.tsx b/apps/admin-x-settings/src/components/settings/growth/offers/edit-offer-modal.tsx index 144ea01254e..e32eea9eeff 100644 --- a/apps/admin-x-settings/src/components/settings/growth/offers/edit-offer-modal.tsx +++ b/apps/admin-x-settings/src/components/settings/growth/offers/edit-offer-modal.tsx @@ -1,11 +1,12 @@ +import ConfirmationModal from '../../../confirmation-modal'; import NiceModal from '@ebay/nice-modal-react'; import PortalFrame from '../../membership/portal/portal-frame'; import SettingsBreadcrumbs from '../../settings-breadcrumbs'; import {Button, Field, FieldDescription, FieldError, FieldGroup, FieldLabel, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, Textarea} from '@tryghost/shade/components'; -import {ConfirmationModal, PreviewModalContent} from '@tryghost/admin-x-design-system'; import {type ErrorMessages, useForm, useHandleError} from '@tryghost/admin-x-framework/hooks'; import {JSONError} from '@tryghost/admin-x-framework/errors'; import {type Offer, useBrowseOffersById, useEditOffer} from '@tryghost/admin-x-framework/api/offers'; +import {PreviewModalContent} from '@tryghost/admin-x-design-system'; import {createOfferRedemptionFilterUrl} from './offer-helpers'; import {formatNumber} from '@tryghost/shade/utils'; import {getHomepageUrl} from '@tryghost/admin-x-framework/api/site'; diff --git a/apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx b/apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx index 7e29dd02474..56154d0977d 100644 --- a/apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx +++ b/apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx @@ -1,8 +1,9 @@ +import ConfirmationModal from '../../../confirmation-modal'; import NiceModal, {useModal} from '@ebay/nice-modal-react'; import React from 'react'; import RecommendationDescriptionForm, {validateDescriptionForm} from './recommendation-description-form'; import {Button} from '@tryghost/shade/components'; -import {ConfirmationModal, Modal} from '@tryghost/admin-x-design-system'; +import {Modal} from '@tryghost/admin-x-design-system'; import {type Recommendation, useDeleteRecommendation, useEditRecommendation} from '@tryghost/admin-x-framework/api/recommendations'; import {type RoutingModalProps, useRouting} from '@tryghost/admin-x-framework/routing'; import {toast} from 'sonner'; diff --git a/apps/admin-x-settings/src/components/settings/membership/access.tsx b/apps/admin-x-settings/src/components/settings/membership/access.tsx index 86dbcfc43f5..c20ab631dd8 100644 --- a/apps/admin-x-settings/src/components/settings/membership/access.tsx +++ b/apps/admin-x-settings/src/components/settings/membership/access.tsx @@ -88,6 +88,10 @@ const COMMENTS_ENABLED_OPTIONS = [ } ]; +const DEFAULT_PRELAUNCH_TITLE = 'Pre-launch mode'; +const DEFAULT_PRELAUNCH_MESSAGE = 'During your free trial, a private access code is required to browse your site. When you\'re ready to launch, pick a plan to upgrade your account and make everything public.'; +const DEFAULT_PRELAUNCH_UPGRADE_URL = '#/pro/billing/plans'; + const renderAccessOptions = (options: Array<{value: string; label: string; hint: string}>) => options.map(option => ( @@ -101,9 +105,13 @@ const getAccessOptionLabel = (options: Array<{value: string; label: string}>, va const Access: React.FC<{ keywords: string[] }> = ({keywords}) => { const [tiersOpen, setTiersOpen] = React.useState(false); - const {settings} = useGlobalData(); + const {settings, config} = useGlobalData(); const limiter = useLimiter(); const isTrialMode = limiter?.isDisabled('publicSiteAccess'); + const publicSiteAccessLimit = config.hostSettings?.limits?.publicSiteAccess; + const preLaunchTitle = publicSiteAccessLimit?.title || DEFAULT_PRELAUNCH_TITLE; + const preLaunchMessage = publicSiteAccessLimit?.error || DEFAULT_PRELAUNCH_MESSAGE; + const preLaunchUpgradeUrl = publicSiteAccessLimit?.upgradeUrl || DEFAULT_PRELAUNCH_UPGRADE_URL; const isPrivateLocked = isSettingReadOnly(settings, 'is_private') || isSettingReadOnly(settings, 'password'); const {mutateAsync: regenerateAccessCode} = useRegenerateAccessCode(); const [isRegenerating, setIsRegenerating] = React.useState(false); @@ -180,12 +188,12 @@ const Access: React.FC<{ keywords: string[] }> = ({keywords}) => {
-
Pre-launch mode
+
{preLaunchTitle}
- During your free trial, a private access code is required to browse your site. When you're ready to launch, pick a plan to upgrade your account and make everything public. + {preLaunchMessage}
- Upgrade now + Upgrade now
)} diff --git a/apps/admin-x-settings/src/components/settings/membership/custom-fields/custom-field-modal.tsx b/apps/admin-x-settings/src/components/settings/membership/custom-fields/custom-field-modal.tsx index 934f7ce7efd..bb8c8da04fa 100644 --- a/apps/admin-x-settings/src/components/settings/membership/custom-fields/custom-field-modal.tsx +++ b/apps/admin-x-settings/src/components/settings/membership/custom-fields/custom-field-modal.tsx @@ -1,9 +1,10 @@ +import ConfirmationModal from '../../../confirmation-modal'; import CustomFieldIcon from './custom-field-icon'; import NiceModal, {useModal} from '@ebay/nice-modal-react'; import React from 'react'; import {Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, Field, FieldDescription, FieldError, FieldGroup, FieldLabel, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from '@tryghost/shade/components'; -import {ConfirmationModal, Modal} from '@tryghost/admin-x-design-system'; import {LucideIcon} from '@tryghost/shade/utils'; +import {Modal} from '@tryghost/admin-x-design-system'; import {ValidationError, getErrorMessage} from '@tryghost/admin-x-framework/errors'; import {memberCustomFieldUserTypes, useCreateMemberCustomField, useDeleteMemberCustomField, useEditMemberCustomField, userTypeForField} from '@tryghost/admin-x-framework/api/member-custom-fields'; import {toast} from 'sonner'; diff --git a/apps/admin-x-settings/src/components/settings/membership/member-emails.tsx b/apps/admin-x-settings/src/components/settings/membership/member-emails.tsx index ef79057903f..8572f108c34 100644 --- a/apps/admin-x-settings/src/components/settings/membership/member-emails.tsx +++ b/apps/admin-x-settings/src/components/settings/membership/member-emails.tsx @@ -1,3 +1,4 @@ +import ConfirmationModal from '../../confirmation-modal'; import NiceModal from '@ebay/nice-modal-react'; import React, {useEffect, useRef} from 'react'; import TopLevelGroup from '../../top-level-group'; @@ -7,7 +8,6 @@ import useQueryParams from '../../../hooks/use-query-params'; import {APIError} from '@tryghost/admin-x-framework/errors'; import {ActionList, ActionListItem, ActionListItemActions, ActionListItemContent, Switch} from '@tryghost/shade/components'; import {Button} from '@tryghost/shade/components'; -import {ConfirmationModal} from '@tryghost/admin-x-design-system'; import {LucideIcon} from '@tryghost/shade/utils'; import {WELCOME_EMAIL_SLUGS, type WelcomeEmailType, getDefaultWelcomeEmailRecord, getDefaultWelcomeEmailValues} from './member-emails/default-welcome-email-values'; import {checkStripeEnabled, getSettingValues} from '@tryghost/admin-x-framework/api/settings'; diff --git a/apps/admin-x-settings/src/components/settings/membership/portal/portal-links.tsx b/apps/admin-x-settings/src/components/settings/membership/portal/portal-links.tsx index 3d0894f351f..49c74ddbb03 100644 --- a/apps/admin-x-settings/src/components/settings/membership/portal/portal-links.tsx +++ b/apps/admin-x-settings/src/components/settings/membership/portal/portal-links.tsx @@ -1,7 +1,7 @@ import React, {useEffect, useId, useState} from 'react'; import {ActionList, ActionListItem, ActionListItemActions, ActionListItemContent, Field, FieldLabel, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from '@tryghost/shade/components'; import {Button} from '@tryghost/shade/components'; -import {ModalPage} from '@tryghost/admin-x-design-system'; +import {ModalPage} from '@tryghost/shade/page-templates'; import {getHomepageUrl} from '@tryghost/admin-x-framework/api/site'; import {getPaidActiveTiers, useBrowseTiers} from '@tryghost/admin-x-framework/api/tiers'; import {getSettingValues} from '@tryghost/admin-x-framework/api/settings'; @@ -65,7 +65,8 @@ const PortalLinks: React.FC = () => { const homePageURL = getHomepageUrl(siteData!); return ( - + + Links

Use these {isDataAttributes ? 'data attributes' : 'links'} in your theme to show pages of Portal.

diff --git a/apps/admin-x-settings/src/components/settings/membership/portal/portal-modal.tsx b/apps/admin-x-settings/src/components/settings/membership/portal/portal-modal.tsx index d79cb46c3d5..721773d4023 100644 --- a/apps/admin-x-settings/src/components/settings/membership/portal/portal-modal.tsx +++ b/apps/admin-x-settings/src/components/settings/membership/portal/portal-modal.tsx @@ -1,12 +1,13 @@ import AccountPage from './account-page'; +import ConfirmationModal from '../../../confirmation-modal'; import LookAndFeel from './look-and-feel'; import NiceModal from '@ebay/nice-modal-react'; import PortalPreview from './portal-preview'; import React, {useEffect, useState} from 'react'; import SignupOptions from './signup-options'; import useQueryParams from '../../../../hooks/use-query-params'; -import {ConfirmationModal, PreviewModalContent} from '@tryghost/admin-x-design-system'; import {type Dirtyable, useForm, useHandleError} from '@tryghost/admin-x-framework/hooks'; +import {PreviewModalContent} from '@tryghost/admin-x-design-system'; import {type Setting, type SettingValue, getSettingValues, useEditSettings} from '@tryghost/admin-x-framework/api/settings'; import {Tabs, TabsContent, TabsList, TabsTrigger} from '@tryghost/shade/components'; import {type Tier, useBrowseTiers, useEditTier} from '@tryghost/admin-x-framework/api/tiers'; @@ -15,6 +16,17 @@ import {useGlobalData} from '../../../providers/global-data-provider'; import {useRouting} from '@tryghost/admin-x-framework/routing'; import {verifyEmailToken} from '@tryghost/admin-x-framework/api/email-verification'; +type PreviewTab = 'signup' | 'account' | 'links'; +type SidebarTab = 'signupOptions' | 'lookAndFeel' | 'accountPage'; + +const previewTabForSidebar: Record = { + signupOptions: 'signup', + lookAndFeel: 'signup', + accountPage: 'account' +}; + +const portalSidebarTabClassName = 'border-b-2 border-transparent after:hidden data-[state=active]:border-foreground'; + const Sidebar: React.FC<{ localSettings: Setting[] updateSetting: (key: string, setting: SettingValue) => void @@ -22,16 +34,16 @@ const Sidebar: React.FC<{ updateTier: (tier: Tier) => void errors: Record setError: (key: string, error: string | undefined) => void - selectedTab: string - onTabChange: (id: string) => void + selectedTab: SidebarTab + onTabChange: (id: SidebarTab) => void }> = ({localSettings, updateSetting, localTiers, updateTier, errors, setError, selectedTab, onTabChange}) => { return (
- + onTabChange(value as SidebarTab)}> - Signup options - Look & feel - Account page + Signup options + Look & feel + Account page { const {updateRoute} = useRouting(); - const [selectedPreviewTab, setSelectedPreviewTab] = useState('signup'); - const [selectedSidebarTab, setSelectedSidebarTab] = useState('signupOptions'); + const [selectedPreviewTab, setSelectedPreviewTab] = useState('signup'); + const [selectedSidebarTab, setSelectedSidebarTab] = useState('signupOptions'); const handleError = useHandleError(); const {settings, siteData, config} = useGlobalData(); @@ -178,24 +190,19 @@ const PortalModal: React.FC = () => { })); }; - const onSelectURL = (id: string) => { + const onSelectURL = (id: PreviewTab) => { setSelectedPreviewTab(id); - // Sync sidebar tab with preview tab - if (id === 'signup') { - setSelectedSidebarTab('signupOptions'); - } else if (id === 'account') { + + if (id === 'account') { setSelectedSidebarTab('accountPage'); + } else if (id === 'signup' && selectedSidebarTab === 'accountPage') { + setSelectedSidebarTab('signupOptions'); } }; - const onSidebarTabChange = (id: string) => { + const onSidebarTabChange = (id: SidebarTab) => { setSelectedSidebarTab(id); - // Sync preview tab with sidebar tab - if (id === 'signupOptions') { - setSelectedPreviewTab('signup'); - } else if (id === 'accountPage') { - setSelectedPreviewTab('account'); - } + setSelectedPreviewTab(previewTabForSidebar[id]); }; const sidebar = { />; const previewTabs = ( - + onSelectURL(value as PreviewTab)}> Signup Account page diff --git a/apps/admin-x-settings/src/components/settings/membership/stripe/stripe-connect-modal.tsx b/apps/admin-x-settings/src/components/settings/membership/stripe/stripe-connect-modal.tsx index 8ffae8e87a5..ef006400c59 100644 --- a/apps/admin-x-settings/src/components/settings/membership/stripe/stripe-connect-modal.tsx +++ b/apps/admin-x-settings/src/components/settings/membership/stripe/stripe-connect-modal.tsx @@ -1,6 +1,8 @@ import BookmarkThumb from '../../../../assets/images/stripe-thumb.jpg'; +import ConfirmationModal from '../../../confirmation-modal'; import GhostLogo from '../../../../assets/images/orb-squircle.png'; import GhostLogoPink from '../../../../assets/images/orb-pink.png'; +import LimitModal from '../../../limit-modal'; import NiceModal, {useModal} from '@ebay/nice-modal-react'; import React, {useEffect, useState} from 'react'; import StripeButton from '../../../stripe-button'; @@ -8,10 +10,10 @@ import StripeLogo from '../../../../assets/images/stripe-emblem.svg'; import StripeVerifiedBadge from '../../../../assets/images/stripe-verified.svg'; import useSettingGroup from '../../../../hooks/use-setting-group'; import {Button, Field, FieldError, FieldGroup, FieldLabel, Input, Switch, Textarea} from '@tryghost/shade/components'; -import {ConfirmationModal, LimitModal, Modal} from '@tryghost/admin-x-design-system'; import {HostLimitError, useLimiter} from '../../../../hooks/use-limiter'; import {JSONError} from '@tryghost/admin-x-framework/errors'; import {LucideIcon} from '@tryghost/shade/utils'; +import {Modal} from '@tryghost/admin-x-design-system'; import {Text} from '@tryghost/shade/primitives'; import {checkStripeEnabled, getSettingValue, getSettingValues, useDeleteStripeSettings, useEditSettings} from '@tryghost/admin-x-framework/api/settings'; import {getGhostPaths} from '@tryghost/admin-x-framework/helpers'; diff --git a/apps/admin-x-settings/src/components/settings/membership/tiers.tsx b/apps/admin-x-settings/src/components/settings/membership/tiers.tsx index 7806c14d7b3..32694fe6380 100644 --- a/apps/admin-x-settings/src/components/settings/membership/tiers.tsx +++ b/apps/admin-x-settings/src/components/settings/membership/tiers.tsx @@ -1,3 +1,4 @@ +import LimitModal from '../../limit-modal'; import NiceModal from '@ebay/nice-modal-react'; import React, {useState} from 'react'; import StripeButton from '../../stripe-button'; @@ -6,7 +7,6 @@ import TopLevelGroup from '../../top-level-group'; import clsx from 'clsx'; import {Button, Indicator, Tabs, TabsContent, TabsList, TabsTrigger} from '@tryghost/shade/components'; import {HostLimitError, useLimiter} from '../../../hooks/use-limiter'; -import {LimitModal} from '@tryghost/admin-x-design-system'; import {type Tier, getActiveTiers, getArchivedTiers, useBrowseTiers} from '@tryghost/admin-x-framework/api/tiers'; import {checkStripeEnabled} from '@tryghost/admin-x-framework/api/settings'; import {formatNumber} from '@tryghost/shade/utils'; diff --git a/apps/admin-x-settings/src/components/settings/membership/tiers/tier-detail-modal.tsx b/apps/admin-x-settings/src/components/settings/membership/tiers/tier-detail-modal.tsx index 08267abeb8a..c092f9f17ee 100644 --- a/apps/admin-x-settings/src/components/settings/membership/tiers/tier-detail-modal.tsx +++ b/apps/admin-x-settings/src/components/settings/membership/tiers/tier-detail-modal.tsx @@ -1,3 +1,4 @@ +import ConfirmationModal from '../../../confirmation-modal'; import NiceModal from '@ebay/nice-modal-react'; import React, {useEffect, useRef} from 'react'; import TierDetailPreview from './tier-detail-preview'; @@ -6,9 +7,9 @@ import useSettingGroup from '../../../../hooks/use-setting-group'; import useSortableIndexedList from '../../../../hooks/use-sortable-indexed-list'; import useUrlInput from '../../../../hooks/use-url-input'; import {Button, Combobox, ComboboxContent, ComboboxTrigger, ComboboxValue, Field, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSet, Input, InputGroup, InputGroupAddon, InputGroupInput, InputGroupText, MultiSelectCombobox, SortableList, Switch} from '@tryghost/shade/components'; -import {ConfirmationModal, Modal} from '@tryghost/admin-x-design-system'; import {type ErrorMessages, useForm, useHandleError} from '@tryghost/admin-x-framework/hooks'; import {LucideIcon} from '@tryghost/shade/utils'; +import {Modal} from '@tryghost/admin-x-design-system'; import {type RoutingModalProps, useRouting} from '@tryghost/admin-x-framework/routing'; import {Text} from '@tryghost/shade/primitives'; import {type Tier, useAddTier, useBrowseTiers, useEditTier} from '@tryghost/admin-x-framework/api/tiers'; diff --git a/apps/admin-x-settings/src/components/settings/site/announcement-bar-modal.tsx b/apps/admin-x-settings/src/components/settings/site/announcement-bar-modal.tsx index 8e68accc513..f0598d1dac8 100644 --- a/apps/admin-x-settings/src/components/settings/site/announcement-bar-modal.tsx +++ b/apps/admin-x-settings/src/components/settings/site/announcement-bar-modal.tsx @@ -4,9 +4,9 @@ import HtmlField from '../../html-field'; import NiceModal from '@ebay/nice-modal-react'; import React, {useRef, useState} from 'react'; import useSettingGroup from '../../../hooks/use-setting-group'; -import {Checkbox, Field, FieldGroup, FieldLabel, FieldLegend, FieldSet, Tabs, TabsList, TabsTrigger, ToggleGroup, ToggleGroupItem} from '@tryghost/shade/components'; -import {DesktopChrome, MobileChrome, PreviewModalContent} from '@tryghost/admin-x-design-system'; +import {Checkbox, Field, FieldGroup, FieldLabel, FieldLegend, FieldSet, PreviewChrome, Tabs, TabsList, TabsTrigger, ToggleGroup, ToggleGroupItem} from '@tryghost/shade/components'; import {Laptop, Smartphone} from 'lucide-react'; +import {PreviewModalContent} from '@tryghost/admin-x-design-system'; import {debounce} from '../../../utils/debounce'; import {getHomepageUrl} from '@tryghost/admin-x-framework/api/site'; import {getSettingValues} from '@tryghost/admin-x-framework/api/settings'; @@ -196,9 +196,9 @@ const AnnouncementBarModal: React.FC = () => { visibility={visibilitySettings} />; const preview = previewDevice === 'desktop' ? ( - {rawPreview} + {rawPreview} ) : ( - {rawPreview} + {rawPreview} ); const previewTabs = latestPost ? ( diff --git a/apps/admin-x-settings/src/components/settings/site/announcement-bar/announcement-bar-preview.tsx b/apps/admin-x-settings/src/components/settings/site/announcement-bar/announcement-bar-preview.tsx index f8fb81802f3..fb3d0bb1448 100644 --- a/apps/admin-x-settings/src/components/settings/site/announcement-bar/announcement-bar-preview.tsx +++ b/apps/admin-x-settings/src/components/settings/site/announcement-bar/announcement-bar-preview.tsx @@ -74,10 +74,10 @@ const AnnouncementBarPreview: React.FC = ({announcement return ( diff --git a/apps/admin-x-settings/src/components/settings/site/change-theme.tsx b/apps/admin-x-settings/src/components/settings/site/change-theme.tsx index a3ef2fc227a..109512720a2 100644 --- a/apps/admin-x-settings/src/components/settings/site/change-theme.tsx +++ b/apps/admin-x-settings/src/components/settings/site/change-theme.tsx @@ -1,8 +1,8 @@ +import LimitModal from '../../limit-modal'; import NiceModal from '@ebay/nice-modal-react'; import React, {useEffect, useState} from 'react'; import TopLevelGroup from '../../top-level-group'; import {Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger} from '@tryghost/shade/components'; -import {LimitModal} from '@tryghost/admin-x-design-system'; import {LucideIcon} from '@tryghost/shade/utils'; import {SettingGroupContent} from '@tryghost/shade/patterns'; import {Text} from '@tryghost/shade/primitives'; diff --git a/apps/admin-x-settings/src/components/settings/site/design-and-branding/theme-preview.tsx b/apps/admin-x-settings/src/components/settings/site/design-and-branding/theme-preview.tsx index 49aa7c7b6e0..071c55d1310 100644 --- a/apps/admin-x-settings/src/components/settings/site/design-and-branding/theme-preview.tsx +++ b/apps/admin-x-settings/src/components/settings/site/design-and-branding/theme-preview.tsx @@ -116,10 +116,10 @@ const ThemePreview: React.FC = ({settings,url}) => { return ( diff --git a/apps/admin-x-settings/src/components/settings/site/design-and-theme-modal.tsx b/apps/admin-x-settings/src/components/settings/site/design-and-theme-modal.tsx index 62fb9f31247..1685b2d4425 100644 --- a/apps/admin-x-settings/src/components/settings/site/design-and-theme-modal.tsx +++ b/apps/admin-x-settings/src/components/settings/site/design-and-theme-modal.tsx @@ -1,9 +1,9 @@ import ChangeThemeModal from './theme-modal'; import DesignModal from './design-modal'; +import LimitModal from '../../limit-modal'; import NiceModal, {useModal} from '@ebay/nice-modal-react'; import React, {useCallback, useEffect, useState} from 'react'; import ThemeCodeEditorModal from './theme/theme-code-editor-modal'; -import {LimitModal} from '@tryghost/admin-x-design-system'; import {type RoutingModalProps, useRouting} from '@tryghost/admin-x-framework/routing'; import {parseEditingThemeRoute} from './theme/theme-editor-utils'; import {useCheckThemeLimitError} from '../../../hooks/use-check-theme-limit-error'; diff --git a/apps/admin-x-settings/src/components/settings/site/design-modal.tsx b/apps/admin-x-settings/src/components/settings/site/design-modal.tsx index e33b3d0cc11..411130567d7 100644 --- a/apps/admin-x-settings/src/components/settings/site/design-modal.tsx +++ b/apps/admin-x-settings/src/components/settings/site/design-modal.tsx @@ -4,10 +4,10 @@ import ThemePreview from './design-and-branding/theme-preview'; import ThemeSettings from './design-and-branding/theme-settings'; import useQueryParams from '../../../hooks/use-query-params'; import {type CustomThemeSetting, useBrowseCustomThemeSettings, useEditCustomThemeSettings} from '@tryghost/admin-x-framework/api/custom-theme-settings'; -import {DesktopChrome, MobileChrome, PreviewModalContent} from '@tryghost/admin-x-design-system'; import {Laptop, Smartphone} from 'lucide-react'; +import {PreviewChrome, Tabs, TabsContent, TabsList, TabsTrigger, ToggleGroup, ToggleGroupItem} from '@tryghost/shade/components'; +import {PreviewModalContent} from '@tryghost/admin-x-design-system'; import {type Setting, type SettingValue, getSettingValues, useEditSettings} from '@tryghost/admin-x-framework/api/settings'; -import {Tabs, TabsContent, TabsList, TabsTrigger, ToggleGroup, ToggleGroupItem} from '@tryghost/shade/components'; import {getHomepageUrl} from '@tryghost/admin-x-framework/api/site'; import {useBrowsePosts} from '@tryghost/admin-x-framework/api/posts'; import {useForm, useHandleError} from '@tryghost/admin-x-framework/hooks'; @@ -176,9 +176,9 @@ const DesignModal: React.FC = () => { url={selectedTabURL} />; const previewContent = previewDevice === 'desktop' ? ( - {rawPreviewContent} + {rawPreviewContent} ) : ( - {rawPreviewContent} + {rawPreviewContent} ); const previewTabs = latestPost ? ( diff --git a/apps/admin-x-settings/src/components/settings/site/theme-modal.tsx b/apps/admin-x-settings/src/components/settings/site/theme-modal.tsx index 849fb1ca5ca..f002f527ee2 100644 --- a/apps/admin-x-settings/src/components/settings/site/theme-modal.tsx +++ b/apps/admin-x-settings/src/components/settings/site/theme-modal.tsx @@ -1,14 +1,16 @@ import AdvancedThemeSettings from './theme/advanced-theme-settings'; +import ConfirmationModal from '../../confirmation-modal'; import InvalidThemeModal, {type FatalErrors} from './theme/invalid-theme-modal'; +import LimitModal from '../../limit-modal'; import NiceModal, {type NiceModalHandler, useModal} from '@ebay/nice-modal-react'; import OfficialThemes from './theme/official-themes'; import React, {useEffect, useState} from 'react'; import ThemeInstalledModal from './theme/theme-installed-modal'; import ThemePreview from './theme/theme-preview'; import {Button, Dropzone, LoadingIndicator, Tabs, TabsList, TabsTrigger} from '@tryghost/shade/components'; -import {ConfirmationModal, LimitModal, Modal, PageHeader} from '@tryghost/admin-x-design-system'; import {type InstalledTheme, type Theme, type ThemesInstallResponseType, isDefaultOrLegacyTheme, useActivateTheme, useBrowseThemes, useInstallTheme, useUploadTheme} from '@tryghost/admin-x-framework/api/themes'; import {JSONError} from '@tryghost/admin-x-framework/errors'; +import {Modal, PageHeader} from '@tryghost/admin-x-design-system'; import {type OfficialTheme} from '../../providers/settings-app-provider'; import {toast} from 'sonner'; import {useCheckThemeLimitError} from '../../../hooks/use-check-theme-limit-error'; diff --git a/apps/admin-x-settings/src/components/settings/site/theme/advanced-theme-settings.tsx b/apps/admin-x-settings/src/components/settings/site/theme/advanced-theme-settings.tsx index 3fc7bd4d461..a492d7dd731 100644 --- a/apps/admin-x-settings/src/components/settings/site/theme/advanced-theme-settings.tsx +++ b/apps/admin-x-settings/src/components/settings/site/theme/advanced-theme-settings.tsx @@ -1,11 +1,13 @@ +import ConfirmationModal from '../../../confirmation-modal'; import InvalidThemeModal, {type FatalErrors} from './invalid-theme-modal'; +import LimitModal from '../../../limit-modal'; import NiceModal from '@ebay/nice-modal-react'; import React from 'react'; import useCustomFonts from '../../../../hooks/use-custom-fonts'; import {ActionList, ActionListItem, ActionListItemActions, ActionListItemContent, Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger} from '@tryghost/shade/components'; -import {ConfirmationModal, LimitModal, ModalPage} from '@tryghost/admin-x-design-system'; import {JSONError} from '@tryghost/admin-x-framework/errors'; import {LucideIcon} from '@tryghost/shade/utils'; +import {ModalPage} from '@tryghost/shade/page-templates'; import {type Theme, isActiveTheme, isDefaultTheme, isDeletableTheme, isLegacyTheme, useActivateTheme, useDeleteTheme} from '@tryghost/admin-x-framework/api/themes'; import {downloadFile, getGhostPaths} from '@tryghost/admin-x-framework/helpers'; import {toast} from 'sonner'; @@ -161,7 +163,7 @@ const ThemeActions: React.FC = ({ - {/* legacy ModalPage overlay is z-[1000]; keep the portalled menu above it */} + {/* legacy Modal overlay is z-[1000]; keep the portalled menu above it */} Edit code Download diff --git a/apps/admin-x-settings/src/components/settings/site/theme/invalid-theme-modal.tsx b/apps/admin-x-settings/src/components/settings/site/theme/invalid-theme-modal.tsx index 5b61f862025..da261307423 100644 --- a/apps/admin-x-settings/src/components/settings/site/theme/invalid-theme-modal.tsx +++ b/apps/admin-x-settings/src/components/settings/site/theme/invalid-theme-modal.tsx @@ -1,6 +1,6 @@ import NiceModal from '@ebay/nice-modal-react'; import React, {type ReactNode} from 'react'; -import {ConfirmationModalContent} from '@tryghost/admin-x-design-system'; +import {ConfirmationModalContent} from '../../../confirmation-modal'; import {ErrorTextCard, type FatalErrors, ThemeValidationDetailsDisclosure, ValidationProblemCard, getIssuesFromFatalErrors} from './theme-validation-details'; import {useBrowseConfig} from '@tryghost/admin-x-framework/api/config'; diff --git a/apps/admin-x-settings/src/components/settings/site/theme/official-themes.tsx b/apps/admin-x-settings/src/components/settings/site/theme/official-themes.tsx index 534554cd9c4..282c863932b 100644 --- a/apps/admin-x-settings/src/components/settings/site/theme/official-themes.tsx +++ b/apps/admin-x-settings/src/components/settings/site/theme/official-themes.tsx @@ -1,7 +1,7 @@ import MarketplaceBgImage from '../../../../assets/images/footer-marketplace-bg.png'; import React, {useEffect, useState} from 'react'; import clsx from 'clsx'; -import {ModalPage} from '@tryghost/admin-x-design-system'; +import {ModalPage} from '@tryghost/shade/page-templates'; import {type OfficialTheme, type ThemeVariant, useOfficialThemes} from '../../../providers/settings-app-provider'; import {Text} from '@tryghost/shade/primitives'; @@ -53,7 +53,8 @@ const OfficialThemes: React.FC<{ }, [variantLoopTheme, visibleVariantIdx]); return ( - + + Themes
{officialThemes.map((theme) => { const showVariants = hasVariants(theme); diff --git a/apps/admin-x-settings/src/components/settings/site/theme/theme-installed-modal.tsx b/apps/admin-x-settings/src/components/settings/site/theme/theme-installed-modal.tsx index 41b51e65b2d..83a84ee60f3 100644 --- a/apps/admin-x-settings/src/components/settings/site/theme/theme-installed-modal.tsx +++ b/apps/admin-x-settings/src/components/settings/site/theme/theme-installed-modal.tsx @@ -1,7 +1,7 @@ import NiceModal from '@ebay/nice-modal-react'; import React, {type ReactNode} from 'react'; import useCustomFonts from '../../../../hooks/use-custom-fonts'; -import {ConfirmationModalContent} from '@tryghost/admin-x-design-system'; +import {ConfirmationModalContent} from '../../../confirmation-modal'; import {type InstalledTheme, useActivateTheme} from '@tryghost/admin-x-framework/api/themes'; import {OutcomeBanner, ThemeValidationDetailsDisclosure, getIssuesFromInstalledTheme} from './theme-validation-details'; import {getHomepageUrl, useBrowseSite} from '@tryghost/admin-x-framework/api/site'; diff --git a/apps/admin-x-settings/src/components/settings/site/theme/theme-preview.tsx b/apps/admin-x-settings/src/components/settings/site/theme/theme-preview.tsx index a3a3701aca5..04ea46f00dd 100644 --- a/apps/admin-x-settings/src/components/settings/site/theme/theme-preview.tsx +++ b/apps/admin-x-settings/src/components/settings/site/theme/theme-preview.tsx @@ -1,10 +1,10 @@ import React, {useState} from 'react'; import SettingsBreadcrumbs from '../../settings-breadcrumbs'; -import {Button, Field, FieldLabel, Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from '@tryghost/shade/components'; -import {DesktopChrome, MobileChrome, PageHeader} from '@tryghost/admin-x-design-system'; +import {Button, Field, FieldLabel, PreviewChrome, Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from '@tryghost/shade/components'; import {Inline} from '@tryghost/shade/primitives'; import {LucideIcon} from '@tryghost/shade/utils'; import {type OfficialTheme, type ThemeVariant} from '../../../providers/settings-app-provider'; +import {PageHeader} from '@tryghost/admin-x-design-system'; import {type Theme, isDefaultOrLegacyTheme} from '@tryghost/admin-x-framework/api/themes'; const hasVariants = (theme: OfficialTheme) => theme.variants && theme.variants.length > 0; @@ -125,21 +125,21 @@ const ThemePreview: React.FC<{
{previewMode === 'desktop' ? - +