From 3043c27d5ac005f08f539d050ecf73d8b267c51b Mon Sep 17 00:00:00 2001 From: Peter Zimon Date: Fri, 15 Sep 2023 19:35:16 +0300 Subject: [PATCH] AdminX UI fixes (#18167) refs. https://github.com/TryGhost/Product/issues/3349 --- .../src/admin-x-ds/assets/icons/laptop.svg | 2 +- .../src/admin-x-ds/assets/icons/mobile.svg | 2 +- .../src/admin-x-ds/assets/icons/picture.svg | 1 + .../src/admin-x-ds/global/Button.tsx | 63 +++++++++++++------ .../src/admin-x-ds/global/ButtonGroup.tsx | 5 +- .../global/chrome/DesktopChrome.tsx | 4 +- .../src/admin-x-ds/settings/SettingGroup.tsx | 2 +- .../settings/SettingSectionHeader.tsx | 2 +- .../src/components/Sidebar.tsx | 2 +- .../components/settings/advanced/History.tsx | 2 +- .../settings/advanced/Integrations.tsx | 2 +- .../src/components/settings/advanced/Labs.tsx | 4 +- .../advanced/integrations/APIKeys.tsx | 24 +++---- .../integrations/CustomIntegrationModal.tsx | 38 ++++++----- .../integrations/IntegrationHeader.tsx | 6 +- .../advanced/integrations/ZapierModal.tsx | 33 +++++----- .../settings/email/EmailSettings.tsx | 4 +- .../components/settings/email/Newsletters.tsx | 2 +- .../newsletters/NewsletterDetailModal.tsx | 56 +++++++---------- .../email/newsletters/NewslettersList.tsx | 9 +-- .../components/settings/general/TimeZone.tsx | 8 +-- .../src/components/settings/general/Users.tsx | 2 +- .../settings/membership/Analytics.tsx | 16 ++--- .../membership/MembershipSettings.tsx | 2 +- .../components/settings/membership/Portal.tsx | 2 +- .../membership/stripe/StripeConnectModal.tsx | 2 +- .../settings/site/AnnouncementBar.tsx | 2 +- .../settings/site/DesignSetting.tsx | 2 +- .../components/settings/site/Navigation.tsx | 2 +- .../settings/site/NavigationModal.tsx | 4 +- .../site/navigation/NavigationEditForm.tsx | 24 ++++--- .../test/e2e/membership/analytics.test.ts | 2 +- 32 files changed, 176 insertions(+), 155 deletions(-) create mode 100644 apps/admin-x-settings/src/admin-x-ds/assets/icons/picture.svg diff --git a/apps/admin-x-settings/src/admin-x-ds/assets/icons/laptop.svg b/apps/admin-x-settings/src/admin-x-ds/assets/icons/laptop.svg index c55c5a7b16..be93d4f979 100644 --- a/apps/admin-x-settings/src/admin-x-ds/assets/icons/laptop.svg +++ b/apps/admin-x-settings/src/admin-x-ds/assets/icons/laptop.svg @@ -1 +1 @@ -laptop \ No newline at end of file +Desktop \ No newline at end of file diff --git a/apps/admin-x-settings/src/admin-x-ds/assets/icons/mobile.svg b/apps/admin-x-settings/src/admin-x-ds/assets/icons/mobile.svg index 2365d3a09d..4f191e42f3 100644 --- a/apps/admin-x-settings/src/admin-x-ds/assets/icons/mobile.svg +++ b/apps/admin-x-settings/src/admin-x-ds/assets/icons/mobile.svg @@ -1 +1 @@ - \ No newline at end of file +Mobile \ No newline at end of file diff --git a/apps/admin-x-settings/src/admin-x-ds/assets/icons/picture.svg b/apps/admin-x-settings/src/admin-x-ds/assets/icons/picture.svg new file mode 100644 index 0000000000..77845d324e --- /dev/null +++ b/apps/admin-x-settings/src/admin-x-ds/assets/icons/picture.svg @@ -0,0 +1 @@ +picture-sun \ No newline at end of file diff --git a/apps/admin-x-settings/src/admin-x-ds/global/Button.tsx b/apps/admin-x-settings/src/admin-x-ds/global/Button.tsx index 2e18302d36..e37c9c9b86 100644 --- a/apps/admin-x-settings/src/admin-x-ds/global/Button.tsx +++ b/apps/admin-x-settings/src/admin-x-ds/global/Button.tsx @@ -1,5 +1,6 @@ import Icon from './Icon'; import React, {HTMLProps} from 'react'; +import clsx from 'clsx'; import {LoadingIndicator, LoadingIndicatorColor, LoadingIndicatorSize} from './LoadingIndicator'; export type ButtonColor = 'clear' | 'grey' | 'black' | 'green' | 'red' | 'white' | 'outline'; @@ -15,6 +16,7 @@ export interface ButtonProps extends Omit, 'label' color?: ButtonColor; fullWidth?: boolean; link?: boolean; + linkWithPadding?: boolean; disabled?: boolean; unstyled?: boolean; className?: string; @@ -34,6 +36,7 @@ const Button: React.FC = ({ color = 'clear', fullWidth, link, + linkWithPadding = false, disabled, unstyled = false, className = '', @@ -47,50 +50,74 @@ const Button: React.FC = ({ color = 'clear'; } - let styles = ''; - if (!unstyled) { - styles += ' transition whitespace-nowrap flex items-center justify-center rounded-sm text-sm'; - styles += ((link && color !== 'clear' && color !== 'black') || (!link && color !== 'clear')) ? ' font-bold' : ' font-semibold'; - styles += !link ? `${size === 'sm' ? ' px-3 h-7 ' : ' px-4 h-[34px] '}` : ''; + className = clsx( + 'flex items-center justify-center whitespace-nowrap rounded-sm text-sm transition', + ((link && color !== 'clear' && color !== 'black') || (!link && color !== 'clear')) ? 'font-bold' : 'font-semibold', + !link ? `${size === 'sm' ? ' h-7 px-3 ' : ' h-[34px] px-4 '}` : '', + (link && linkWithPadding) && '-m-1 p-1', + className + ); switch (color) { case 'black': - styles += link ? ' text-black dark:text-white hover:text-grey-800' : ` bg-black text-white dark:bg-white dark:text-black ${!disabled && 'hover:bg-grey-900'}`; + className = clsx( + link ? 'text-black hover:text-grey-800 dark:text-white' : `bg-black text-white dark:bg-white dark:text-black ${!disabled && 'hover:bg-grey-900'}`, + className + ); loadingIndicatorColor = 'light'; break; case 'grey': - styles += link ? ' text-black dark:text-white hover:text-grey-800' : ` bg-grey-100 text-black dark:bg-grey-900 dark:text-white ${!disabled && 'hover:!bg-grey-300 dark:hover:!bg-grey-800'}`; + className = clsx( + link ? 'text-black hover:text-grey-800 dark:text-white' : `bg-grey-100 text-black dark:bg-grey-900 dark:text-white ${!disabled && 'hover:!bg-grey-300 dark:hover:!bg-grey-800'}`, + className + ); loadingIndicatorColor = 'dark'; break; case 'green': - styles += link ? ' text-green hover:text-green-400' : ` bg-green text-white ${!disabled && 'hover:bg-green-400'}`; + className = clsx( + link ? ' text-green hover:text-green-400' : ` bg-green text-white ${!disabled && 'hover:bg-green-400'}`, + className + ); loadingIndicatorColor = 'light'; break; case 'red': - styles += link ? ' text-red hover:text-red-400' : ` bg-red text-white ${!disabled && 'hover:bg-red-400'}`; + className = clsx( + link ? 'text-red hover:text-red-400' : `bg-red text-white ${!disabled && 'hover:bg-red-400'}`, + className + ); loadingIndicatorColor = 'light'; break; case 'white': - styles += link ? ' text-white hover:text-white dark:text-black dark:hover:text-grey-800' : ` bg-white dark:bg-black text-black dark:text-white`; + className = clsx( + link ? 'text-white hover:text-white dark:text-black dark:hover:text-grey-800' : `bg-white text-black dark:bg-black dark:text-white`, + className + ); loadingIndicatorColor = 'dark'; break; case 'outline': - styles += link ? ' text-black dark:text-white hover:text-grey-800' : `text-black border border-grey-300 bg-transparent dark:border-grey-800 dark:text-white ${!disabled && 'hover:!border-black dark:hover:!border-white'}`; + className = clsx( + link ? 'text-black hover:text-grey-800 dark:text-white' : `border border-grey-300 bg-transparent text-black dark:border-grey-800 dark:text-white ${!disabled && 'hover:!border-black dark:hover:!border-white'}`, + className + ); loadingIndicatorColor = 'dark'; break; default: - styles += link ? ' text-black dark:text-white hover:text-grey-800' : ` text-black dark:text-white dark:hover:bg-grey-900 ${!disabled && 'hover:bg-grey-200'}`; + className = clsx( + link ? ' text-black hover:text-grey-800 dark:text-white' : ` text-black dark:text-white dark:hover:bg-grey-900 ${!disabled && 'hover:bg-grey-200'}`, + className + ); loadingIndicatorColor = 'dark'; break; } - styles += (fullWidth && !link) ? ' w-full' : ''; - styles += (disabled) ? ' opacity-40' : ' cursor-pointer'; + className = clsx( + (fullWidth && !link) && ' w-full', + disabled ? 'opacity-40' : 'cursor-pointer', + className + ); } - styles += ` ${className}`; - const iconClasses = label && icon && !hideLabel ? 'mr-1.5' : ''; let labelClasses = ''; @@ -102,8 +129,8 @@ const Button: React.FC = ({ {label} {loading &&
Loading...
} ; - - const buttonElement = React.createElement(tag, {className: styles, + + const buttonElement = React.createElement(tag, {className: className, disabled: disabled, type: 'button', onClick: onClick, diff --git a/apps/admin-x-settings/src/admin-x-ds/global/ButtonGroup.tsx b/apps/admin-x-settings/src/admin-x-ds/global/ButtonGroup.tsx index 5b068ea8d6..1c8e54cd5d 100644 --- a/apps/admin-x-settings/src/admin-x-ds/global/ButtonGroup.tsx +++ b/apps/admin-x-settings/src/admin-x-ds/global/ButtonGroup.tsx @@ -6,14 +6,15 @@ import {ButtonProps} from './Button'; interface ButtonGroupProps { buttons: Array; link?: boolean; + linkWithPadding?: boolean; className?: string; } -const ButtonGroup: React.FC = ({buttons, link, className}) => { +const ButtonGroup: React.FC = ({buttons, link, linkWithPadding, className}) => { return (
{buttons.map(({key, ...props}) => ( -
); diff --git a/apps/admin-x-settings/src/admin-x-ds/global/chrome/DesktopChrome.tsx b/apps/admin-x-settings/src/admin-x-ds/global/chrome/DesktopChrome.tsx index 3d607db880..73ed6323b0 100644 --- a/apps/admin-x-settings/src/admin-x-ds/global/chrome/DesktopChrome.tsx +++ b/apps/admin-x-settings/src/admin-x-ds/global/chrome/DesktopChrome.tsx @@ -6,8 +6,8 @@ interface DesktopChromeProps { const DesktopChrome: React.FC> = ({children, ...props}) => { return ( -
-
+
+
{children}
diff --git a/apps/admin-x-settings/src/admin-x-ds/settings/SettingGroup.tsx b/apps/admin-x-settings/src/admin-x-ds/settings/SettingGroup.tsx index 5e832311f3..88cc90bf8f 100644 --- a/apps/admin-x-settings/src/admin-x-ds/settings/SettingGroup.tsx +++ b/apps/admin-x-settings/src/admin-x-ds/settings/SettingGroup.tsx @@ -163,7 +163,7 @@ const SettingGroup: React.FC = ({ {customHeader ? customHeader : {customButtons ? customButtons : - (onEditingChange && )} + (onEditingChange && )} } {children} diff --git a/apps/admin-x-settings/src/admin-x-ds/settings/SettingSectionHeader.tsx b/apps/admin-x-settings/src/admin-x-ds/settings/SettingSectionHeader.tsx index 8c4df9e5c6..8bc303df40 100644 --- a/apps/admin-x-settings/src/admin-x-ds/settings/SettingSectionHeader.tsx +++ b/apps/admin-x-settings/src/admin-x-ds/settings/SettingSectionHeader.tsx @@ -6,7 +6,7 @@ interface Props { } const SettingSectionHeader: React.FC = ({title, sticky = false}) => { - let styles = 'pb-4 text-2xs font-semibold uppercase tracking-wider text-grey-700 z-10 '; + let styles = 'pb-4 text-2xs font-semibold uppercase tracking-wider text-grey-700 z-20 '; if (sticky) { styles += ' sticky top-0 -mt-4 pt-4 bg-white dark:bg-black'; } diff --git a/apps/admin-x-settings/src/components/Sidebar.tsx b/apps/admin-x-settings/src/components/Sidebar.tsx index bd4c4f712f..81173ac9b1 100644 --- a/apps/admin-x-settings/src/components/Sidebar.tsx +++ b/apps/admin-x-settings/src/components/Sidebar.tsx @@ -63,8 +63,8 @@ const Sidebar: React.FC = () => { - + {hasTipsAndDonations && } diff --git a/apps/admin-x-settings/src/components/settings/advanced/History.tsx b/apps/admin-x-settings/src/components/settings/advanced/History.tsx index 0fdc6664ea..5ff10839a3 100644 --- a/apps/admin-x-settings/src/components/settings/advanced/History.tsx +++ b/apps/admin-x-settings/src/components/settings/advanced/History.tsx @@ -11,7 +11,7 @@ const History: React.FC<{ keywords: string[] }> = ({keywords}) => { return ( } + customButtons={
- ; + ; }; -const APIKeys: React.FC<{hasLabel?: boolean; keys: APIKeyFieldProps[];}> = ({hasLabel = true, keys}) => { +const APIKeys: React.FC<{hasLabel?: boolean; keys: APIKeyFieldProps[];}> = ({keys}) => { return ( -
+
{keys.map(key => )}
); }; -export default APIKeys; +export default APIKeys; \ No newline at end of file diff --git a/apps/admin-x-settings/src/components/settings/advanced/integrations/CustomIntegrationModal.tsx b/apps/admin-x-settings/src/components/settings/advanced/integrations/CustomIntegrationModal.tsx index d4341c8733..26186423d3 100644 --- a/apps/admin-x-settings/src/components/settings/advanced/integrations/CustomIntegrationModal.tsx +++ b/apps/admin-x-settings/src/components/settings/advanced/integrations/CustomIntegrationModal.tsx @@ -123,26 +123,24 @@ const CustomIntegrationModalContent: React.FC<{integration: Integration}> = ({in onKeyDown={() => clearError('name')} /> updateForm(state => ({...state, description: e.target.value}))} /> -
- Content API Key was successfully regenerated
: undefined, - onRegenerate: () => contentApiKey && handleRegenerate(contentApiKey, setContentKeyRegenerated) - }, - { - label: 'Admin API key', - text: adminApiKey?.secret, - hint: adminKeyRegenerated ?
Admin API Key was successfully regenerated
: undefined, - onRegenerate: () => adminApiKey && handleRegenerate(adminApiKey, setAdminKeyRegenerated) - }, - { - label: 'API URL', - text: window.location.origin + getGhostPaths().subdir - } - ]} /> -
+ Content API Key was successfully regenerated : undefined, + onRegenerate: () => contentApiKey && handleRegenerate(contentApiKey, setContentKeyRegenerated) + }, + { + label: 'Admin API key', + text: adminApiKey?.secret, + hint: adminKeyRegenerated ?
Admin API Key was successfully regenerated
: undefined, + onRegenerate: () => adminApiKey && handleRegenerate(adminApiKey, setAdminKeyRegenerated) + }, + { + label: 'API URL', + text: window.location.origin + getGhostPaths().subdir + } + ]} /> diff --git a/apps/admin-x-settings/src/components/settings/advanced/integrations/IntegrationHeader.tsx b/apps/admin-x-settings/src/components/settings/advanced/integrations/IntegrationHeader.tsx index 9eb89945fa..8bae7e5b41 100644 --- a/apps/admin-x-settings/src/components/settings/advanced/integrations/IntegrationHeader.tsx +++ b/apps/admin-x-settings/src/components/settings/advanced/integrations/IntegrationHeader.tsx @@ -14,11 +14,11 @@ const IntegrationHeader: React.FC = ({ extra }) => { return ( -
+
{icon}
-
+

{title}

-
{detail}
+
{detail}
{extra && (
{extra}
)} diff --git a/apps/admin-x-settings/src/components/settings/advanced/integrations/ZapierModal.tsx b/apps/admin-x-settings/src/components/settings/advanced/integrations/ZapierModal.tsx index 4c97e9923d..e9eb0b1f61 100644 --- a/apps/admin-x-settings/src/components/settings/advanced/integrations/ZapierModal.tsx +++ b/apps/admin-x-settings/src/components/settings/advanced/integrations/ZapierModal.tsx @@ -70,10 +70,25 @@ const ZapierModal = NiceModal.create(() => { updateRoute('integrations'); }} cancelLabel='' + footer={ +
+ + View more Ghost integrations powered by + +
+ } okColor='black' okLabel='Close' testId='zapier-modal' title='' + stickyFooter onOk={() => { updateRoute('integrations'); modal.remove(); @@ -81,7 +96,7 @@ const ZapierModal = NiceModal.create(() => { > { onRegenerate: handleRegenerate }, {label: 'API URL', text: window.location.origin + getGhostPaths().subdir} - ]} />} + ]} />
} icon={} title='Zapier' /> - + {zapierTemplates.map(template => ( } bgOnHover={false} - className='flex items-center gap-3 py-2' + className='flex items-center gap-3 py-2 pl-3' title={
@@ -114,16 +129,6 @@ const ZapierModal = NiceModal.create(() => { /> ))} - - ); }); diff --git a/apps/admin-x-settings/src/components/settings/email/EmailSettings.tsx b/apps/admin-x-settings/src/components/settings/email/EmailSettings.tsx index 5089bc0479..f869b16a4e 100644 --- a/apps/admin-x-settings/src/components/settings/email/EmailSettings.tsx +++ b/apps/admin-x-settings/src/components/settings/email/EmailSettings.tsx @@ -19,12 +19,12 @@ const EmailSettings: React.FC = () => { const [newslettersEnabled] = getSettingValues(settings, ['editor_default_email_recipients']) as [string]; return ( - + {newslettersEnabled !== 'disabled' && ( <> - + {!config.mailgunIsConfigured && } )} 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 d1e6d2658d..9d46df9756 100644 --- a/apps/admin-x-settings/src/components/settings/email/Newsletters.tsx +++ b/apps/admin-x-settings/src/components/settings/email/Newsletters.tsx @@ -15,7 +15,7 @@ const Newsletters: React.FC<{ keywords: string[] }> = ({keywords}) => { const {data: {newsletters} = {}} = useBrowseNewsletters(); const buttons = ( -
}, { @@ -136,9 +156,9 @@ const Sidebar: React.FC<{ updateNewsletter({header_image: imageUrl}); }} > - Upload header image + - Optional, recommended size 1200x600 + 1200x600, optional
@@ -146,21 +166,18 @@ const Sidebar: React.FC<{ checked={newsletter.show_header_icon} direction="rtl" label='Publication icon' - labelStyle='heading' onChange={e => updateNewsletter({show_header_icon: e.target.checked})} />} updateNewsletter({show_header_title: e.target.checked})} /> updateNewsletter({show_header_name: e.target.checked})} /> @@ -292,28 +309,24 @@ const Sidebar: React.FC<{ checked={newsletter.feedback_enabled} direction="rtl" label='Ask your readers for feedback' - labelStyle='heading' onChange={e => updateNewsletter({feedback_enabled: e.target.checked})} /> updateNewsletter({show_comment_cta: e.target.checked})} /> updateNewsletter({show_latest_posts: e.target.checked})} /> updateNewsletter({show_subscription_details: e.target.checked})} /> @@ -336,31 +349,10 @@ const Sidebar: React.FC<{ }; return ( -
+
- -
- - - -
- - Promote independent publishing - Show you’re a part of the indie publishing movement with a small badge in the footer -
- } - labelStyle='value' - onChange={e => updateNewsletter({show_badge: e.target.checked})} - /> - -
-
); }; diff --git a/apps/admin-x-settings/src/components/settings/email/newsletters/NewslettersList.tsx b/apps/admin-x-settings/src/components/settings/email/newsletters/NewslettersList.tsx index c795406735..97c8882567 100644 --- a/apps/admin-x-settings/src/components/settings/email/newsletters/NewslettersList.tsx +++ b/apps/admin-x-settings/src/components/settings/email/newsletters/NewslettersList.tsx @@ -10,6 +10,7 @@ import TableRow from '../../../../admin-x-ds/global/TableRow'; import useRouting from '../../../../hooks/useRouting'; import {HostLimitError, useLimiter} from '../../../../hooks/useLimiter'; import {Newsletter, useEditNewsletter} from '../../../../api/newsletters'; +import {numberWithCommas} from '../../../../utils/helpers'; interface NewslettersListProps { newsletters: Newsletter[] @@ -21,7 +22,7 @@ const NewsletterItem: React.FC<{newsletter: Newsletter, onlyOne: boolean}> = ({n const limiter = useLimiter(); const action = newsletter.status === 'active' ? ( -
); diff --git a/apps/admin-x-settings/src/components/settings/membership/MembershipSettings.tsx b/apps/admin-x-settings/src/components/settings/membership/MembershipSettings.tsx index 9727999975..6e385affbc 100644 --- a/apps/admin-x-settings/src/components/settings/membership/MembershipSettings.tsx +++ b/apps/admin-x-settings/src/components/settings/membership/MembershipSettings.tsx @@ -25,8 +25,8 @@ const MembershipSettings: React.FC = () => { return ( - + {hasTipsAndDonations && } diff --git a/apps/admin-x-settings/src/components/settings/membership/Portal.tsx b/apps/admin-x-settings/src/components/settings/membership/Portal.tsx index 6fca072fe0..e65036c1a4 100644 --- a/apps/admin-x-settings/src/components/settings/membership/Portal.tsx +++ b/apps/admin-x-settings/src/components/settings/membership/Portal.tsx @@ -17,7 +17,7 @@ const Portal: React.FC<{ keywords: string[] }> = ({keywords}) => { return ( } + customButtons={
diff --git a/apps/admin-x-settings/src/components/settings/site/AnnouncementBar.tsx b/apps/admin-x-settings/src/components/settings/site/AnnouncementBar.tsx index 049f50cbe7..4cee7c4ed1 100644 --- a/apps/admin-x-settings/src/components/settings/site/AnnouncementBar.tsx +++ b/apps/admin-x-settings/src/components/settings/site/AnnouncementBar.tsx @@ -11,7 +11,7 @@ const AnnouncementBar: React.FC<{ keywords: string[] }> = ({keywords}) => { return ( } + customButtons={
; }; diff --git a/apps/admin-x-settings/test/e2e/membership/analytics.test.ts b/apps/admin-x-settings/test/e2e/membership/analytics.test.ts index 20de2a596d..f254167d51 100644 --- a/apps/admin-x-settings/test/e2e/membership/analytics.test.ts +++ b/apps/admin-x-settings/test/e2e/membership/analytics.test.ts @@ -49,7 +49,7 @@ test.describe('Analytics settings', async () => { const section = page.getByTestId('analytics'); - await section.getByRole('button', {name: 'Export post analytics'}).click(); + await section.getByRole('button', {name: 'Export'}).click(); const hasDownloadUrl = lastApiRequests.postsExport?.url?.includes('/posts/export/?limit=1000'); expect(hasDownloadUrl).toBe(true);