Skip to content

Commit

Permalink
Improve technical errors (#20046)
Browse files Browse the repository at this point in the history
ref DES-229

Some of the error messages in Ghost and more specifically in Settings
were very technical, e.g.
`ValidationError: Validation (isEmpty) failed for locale`

This PR deals with some of the occurances for a more human error
communication.
  • Loading branch information
peterzimon committed Apr 24, 2024
1 parent 571171b commit 9f19e33
Show file tree
Hide file tree
Showing 24 changed files with 93 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,6 @@ export const ResizeDisabled: Story = {
}
};

export const MaxLength: Story = {
args: {
title: 'Description',
placeholder: 'Try to enter more than 80 characters, I dare you...',
value: 'This is a nice text area that only accepts up to 80 characters. Try to add more:',
maxLength: 80
}
};

export const Error: Story = {
args: {
title: 'Description',
Expand Down
1 change: 0 additions & 1 deletion apps/admin-x-design-system/src/global/form/TextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ const TextArea: React.FC<TextAreaProps> = ({
</textarea>
{title && <Heading className={'order-1'} htmlFor={id} useLabelTag={true}>{title}</Heading>}
{hint && <Hint className='order-3' color={error ? 'red' : ''}>{hint}</Hint>}
{maxLength && <Hint>Max length is {maxLength}</Hint>}
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ const AddIntegrationModal: React.FC<RoutingModalProps> = () => {
autoFocus={true}
error={!!errors.name}
hint={errors.name}
maxLength={191}
placeholder='Custom integration'
title='Name'
value={name}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import APIKeys from './APIKeys';
import NiceModal, {useModal} from '@ebay/nice-modal-react';
import React, {useEffect, useState} from 'react';
import WebhooksTable from './WebhooksTable';
import {APIError} from '@tryghost/admin-x-framework/errors';
import {APIKey, useRefreshAPIKey} from '@tryghost/admin-x-framework/api/apiKeys';
import {ConfirmationModal, Form, ImageUpload, Modal, TextField, showToast} from '@tryghost/admin-x-design-system';
import {Integration, useBrowseIntegrations, useEditIntegration} from '@tryghost/admin-x-framework/api/integrations';
Expand Down Expand Up @@ -112,7 +113,11 @@ const CustomIntegrationModalContent: React.FC<{integration: Integration}> = ({in
const imageUrl = getImageUrl(await uploadImage({file}));
updateForm(state => ({...state, icon_image: imageUrl}));
} catch (e) {
handleError(e);
const error = e as APIError;
if (error.response!.status === 415) {
error.message = 'Unsupported file type';
}
handleError(error);
}
}}
>
Expand All @@ -124,12 +129,13 @@ const CustomIntegrationModalContent: React.FC<{integration: Integration}> = ({in
<TextField
error={Boolean(errors.name)}
hint={errors.name}
maxLength={191}
title='Title'
value={formState.name}
onChange={e => updateForm(state => ({...state, name: e.target.value}))}
onKeyDown={() => clearError('name')}
/>
<TextField title='Description' value={formState.description || ''} onChange={e => updateForm(state => ({...state, description: e.target.value}))} />
<TextField maxLength={2000} title='Description' value={formState.description || ''} onChange={e => updateForm(state => ({...state, description: e.target.value}))} />
<APIKeys keys={[
{
label: 'Content API key',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ const WebhookModal: React.FC<WebhookModalProps> = ({webhook, integrationId}) =>
<TextField
error={Boolean(errors.name)}
hint={errors.name}
maxLength={191}
placeholder='Custom webhook'
title='Name'
value={formState.name}
Expand All @@ -101,6 +102,7 @@ const WebhookModal: React.FC<WebhookModalProps> = ({webhook, integrationId}) =>
<TextField
error={Boolean(errors.target_url)}
hint={errors.target_url}
maxLength={2000}
placeholder='https://example.com'
title='Target URL'
type='url'
Expand All @@ -109,6 +111,7 @@ const WebhookModal: React.FC<WebhookModalProps> = ({webhook, integrationId}) =>
onKeyDown={() => clearError('target_url')}
/>
<TextField
maxLength={191}
placeholder='https://example.com'
title='Secret'
value={formState.secret || undefined}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ const Newsletters: React.FC<{ keywords: string[] }> = ({keywords}) => {
onOk: confirmModal => confirmModal?.remove()
});
} catch (e) {
let prompt = 'There was an error verifying your email address. Please try again.';
let prompt = 'There was an error verifying your email address. Try again later.';

if (e instanceof APIError && e.message === 'Token expired') {
prompt = 'The verification link has expired. Please try again.';
prompt = 'Verification link has expired.';
}
NiceModal.show(ConfirmationModal, {
title: 'Error verifying email address',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,15 @@ const AddNewsletterModal: React.FC<RoutingModalProps> = () => {
autoFocus={true}
error={Boolean(errors.name)}
hint={errors.name}
maxLength={191}
placeholder='Weekly roundup'
title='Name'
value={formState.name}
onChange={e => updateForm(state => ({...state, name: e.target.value}))}
onKeyDown={() => clearError('name')}
/>
<TextArea
maxLength={2000}
title='Description'
value={formState.description}
onChange={e => updateForm(state => ({...state, description: e.target.value}))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ const ReplyToEmailField: React.FC<{
<TextField
error={Boolean(errors.sender_reply_to)}
hint={errors.sender_reply_to}
maxLength={191}
placeholder={newsletterAddress || ''}
title="Reply-to email"
value={senderReplyTo}
Expand Down Expand Up @@ -201,6 +202,7 @@ const Sidebar: React.FC<{
<TextField
error={Boolean(errors.sender_email)}
hint={errors.sender_email}
maxLength={191}
placeholder={defaultEmailAddress}
title="Sender email address"
value={newsletter.sender_email || ''}
Expand All @@ -226,16 +228,17 @@ const Sidebar: React.FC<{
<TextField
error={Boolean(errors.name)}
hint={errors.name}
maxLength={191}
placeholder="Weekly Roundup"
title="Name"
value={newsletter.name || ''}
onChange={e => updateNewsletter({name: e.target.value})}
onKeyDown={() => clearError('name')}
/>
<TextArea rows={2} title="Description" value={newsletter.description || ''} onChange={e => updateNewsletter({description: e.target.value})} />
<TextArea maxLength={2000} rows={2} title="Description" value={newsletter.description || ''} onChange={e => updateNewsletter({description: e.target.value})} />
</Form>
<Form className='mt-6' gap='sm' margins='lg' title='Email info'>
<TextField placeholder={siteTitle} title="Sender name" value={newsletter.sender_name || ''} onChange={e => updateNewsletter({sender_name: e.target.value})} />
<TextField maxLength={191} placeholder={siteTitle} title="Sender name" value={newsletter.sender_name || ''} onChange={e => updateNewsletter({sender_name: e.target.value})} />
{renderSenderEmailField()}
<ReplyToEmailField clearError={clearError} errors={errors} newsletter={newsletter} updateNewsletter={updateNewsletter} validate={validate} />
</Form>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import TopLevelGroup from '../../TopLevelGroup';
import usePinturaEditor from '../../../hooks/usePinturaEditor';
import useSettingGroup from '../../../hooks/useSettingGroup';
import {APIError} from '@tryghost/admin-x-framework/errors';
import {FacebookLogo, ImageUpload, SettingGroupContent, TextField, withErrorBoundary} from '@tryghost/admin-x-design-system';
import {getImageUrl, useUploadImage} from '@tryghost/admin-x-framework/api/images';
import {getSettingValues} from '@tryghost/admin-x-framework/api/settings';
Expand Down Expand Up @@ -43,7 +44,11 @@ const Facebook: React.FC<{ keywords: string[] }> = ({keywords}) => {
const imageUrl = getImageUrl(await uploadImage({file}));
updateSetting('og_image', imageUrl);
} catch (e) {
handleError(e);
const error = e as APIError;
if (error.response!.status === 415) {
error.message = 'Unsupported file type';
}
handleError(error);
}
};

Expand Down Expand Up @@ -95,12 +100,14 @@ const Facebook: React.FC<{ keywords: string[] }> = ({keywords}) => {
<div className="flex flex-col gap-x-6 gap-y-7 px-4 pb-7">
<TextField
inputRef={focusRef}
maxLength={300}
placeholder={siteTitle}
title="Facebook title"
value={facebookTitle}
onChange={handleTitleChange}
/>
<TextField
maxLength={300}
placeholder={siteDescription}
title="Facebook description"
value={facebookDescription}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const LockSite: React.FC<{ keywords: string[] }> = ({keywords}) => {
onValidate: () => {
if (passwordEnabled && !password) {
return {
password: 'Password must be supplied'
password: 'Enter a password'
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,15 @@ const Metadata: React.FC<{ keywords: string[] }> = ({keywords}) => {
<TextField
hint="Recommended: 70 characters"
inputRef={focusRef}
maxLength={300}
placeholder={siteTitle}
title="Meta title"
value={metaTitle}
onChange={handleTitleChange}
/>
<TextField
hint="Recommended: 156 characters"
maxLength={500}
placeholder={siteDescription}
title="Meta description"
value={metaDescription}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,15 @@ const TitleAndDescription: React.FC<{ keywords: string[] }> = ({keywords}) => {
<TextField
hint="The name of your site"
inputRef={focusRef}
maxLength={150}
placeholder="Site title"
title="Site title"
value={title}
onChange={handleTitleChange}
/>
<TextField
hint="A short description, used in your theme, meta data and search results"
maxLength={200}
placeholder="Site description"
title="Site description"
value={description}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import TopLevelGroup from '../../TopLevelGroup';
import usePinturaEditor from '../../../hooks/usePinturaEditor';
import useSettingGroup from '../../../hooks/useSettingGroup';
import {APIError} from '@tryghost/admin-x-framework/errors';
import {ImageUpload, SettingGroupContent, TextField, XLogo, withErrorBoundary} from '@tryghost/admin-x-design-system';
import {getImageUrl, useUploadImage} from '@tryghost/admin-x-framework/api/images';
import {getSettingValues} from '@tryghost/admin-x-framework/api/settings';
Expand Down Expand Up @@ -41,7 +42,11 @@ const Twitter: React.FC<{ keywords: string[] }> = ({keywords}) => {
const imageUrl = getImageUrl(await uploadImage({file}));
updateSetting('twitter_image', imageUrl);
} catch (e) {
handleError(e);
const error = e as APIError;
if (error.response!.status === 415) {
error.message = 'Unsupported file type';
}
handleError(error);
}
};

Expand Down Expand Up @@ -91,12 +96,14 @@ const Twitter: React.FC<{ keywords: string[] }> = ({keywords}) => {
<div className="flex flex-col gap-x-6 gap-y-7 px-4 pb-7">
<TextField
inputRef={focusRef}
maxLength={300}
placeholder={siteTitle}
title="X title"
value={twitterTitle}
onChange={handleTitleChange}
/>
<TextField
maxLength={300}
placeholder={siteDescription}
title="X description"
value={twitterDescription}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import clsx from 'clsx';
import usePinturaEditor from '../../../hooks/usePinturaEditor';
import useStaffUsers from '../../../hooks/useStaffUsers';
import validator from 'validator';
import {APIError} from '@tryghost/admin-x-framework/errors';
import {ConfirmationModal, Heading, Icon, ImageUpload, LimitModal, Menu, MenuItem, Modal, showToast} from '@tryghost/admin-x-design-system';
import {ErrorMessages, useForm, useHandleError} from '@tryghost/admin-x-framework/hooks';
import {HostLimitError, useLimiter} from '../../../hooks/useLimiter';
Expand Down Expand Up @@ -267,7 +268,11 @@ const UserDetailModalContent: React.FC<{user: User}> = ({user}) => {
break;
}
} catch (e) {
handleError(e);
const error = e as APIError;
if (error.response!.status === 415) {
error.message = 'Unsupported file type';
}
handleError(error);
}
};

Expand Down Expand Up @@ -374,7 +379,7 @@ const UserDetailModalContent: React.FC<{user: User}> = ({user}) => {
<div className='flex w-full max-w-[620px] flex-col gap-5 p-8 md:max-w-[auto] md:flex-row md:items-center'>
<div>
<ImageUpload
deleteButtonClassName='md:invisible absolute pr-3 -right-2 -top-2 flex h-8 w-16 cursor-pointer items-center justify-end rounded-full bg-[rgba(0,0,0,0.75)] text-white group-hover:!visible'
deleteButtonClassName='md:invisible absolute pr-3 -right-2 -top-2 flex h-8 w-10 cursor-pointer items-center justify-end rounded-full bg-[rgba(0,0,0,0.75)] text-white group-hover:!visible'
deleteButtonContent={<Icon colorClass='text-white' name='trash' size='sm' />}
editButtonClassName='md:invisible absolute right-[22px] -top-2 flex h-8 w-8 cursor-pointer items-center justify-center text-white group-hover:!visible z-20'
fileUploadClassName='rounded-full bg-black flex items-center justify-center opacity-80 transition hover:opacity-100 -ml-2 cursor-pointer h-[80px] w-[80px]'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const BasicInputs: React.FC<UserDetailProps> = ({errors, validateField, clearErr
<TextField
error={!!errors?.name}
hint={errors?.name || 'Use real name so people can recognize you'}
maxLength={191}
title="Full name"
value={user.name}
onBlur={(e) => {
Expand All @@ -26,6 +27,7 @@ const BasicInputs: React.FC<UserDetailProps> = ({errors, validateField, clearErr
<TextField
error={!!errors?.email}
hint={errors?.email || 'Used for notifications'}
maxLength={191}
title="Email"
value={user.email}
onBlur={(e) => {
Expand All @@ -38,6 +40,7 @@ const BasicInputs: React.FC<UserDetailProps> = ({errors, validateField, clearErr
/>
<TextField
hint="https://example.com/author"
maxLength={191}
title="Slug"
value={user.slug}
onChange={(e) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const DetailsInputs: React.FC<UserDetailProps> = ({errors, clearError, va
<TextField
error={!!errors?.location}
hint={errors?.location || 'Where in the world do you live?'}
maxLength={65535}
title="Location"
value={user.location || ''}
onBlur={(e) => {
Expand All @@ -25,6 +26,7 @@ export const DetailsInputs: React.FC<UserDetailProps> = ({errors, clearError, va
<TextField
error={!!errors?.url}
hint={errors?.url || 'Have a website or blog other than this one? Link it!'}
maxLength={2000}
title="Website"
value={user.website || ''}
onBlur={(e) => {
Expand All @@ -37,6 +39,7 @@ export const DetailsInputs: React.FC<UserDetailProps> = ({errors, clearError, va
<TextField
error={!!errors?.facebook}
hint={errors?.facebook || 'URL of your personal Facebook Profile'}
maxLength={2000}
title="Facebook profile"
value={facebookUrl}
onBlur={(e) => {
Expand All @@ -53,6 +56,7 @@ export const DetailsInputs: React.FC<UserDetailProps> = ({errors, clearError, va
<TextField
error={!!errors?.twitter}
hint={errors?.twitter || 'URL of your X profile'}
maxLength={2000}
title="X (formerly Twitter) profile"
value={twitterUrl}
onBlur={(e) => {
Expand All @@ -69,6 +73,7 @@ export const DetailsInputs: React.FC<UserDetailProps> = ({errors, clearError, va
<TextArea
error={!!errors?.bio}
hint={errors?.bio || <>Recommended: 200 characters. You&lsquo;ve used <span className='font-bold'>{user.bio?.length || 0}</span></>}
maxLength={65535}
title="Bio"
value={user.bio || ''}
onBlur={(e) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ const AddRecommendationModal: React.FC<RoutingModalProps & AddRecommendationModa
autoFocus={true}
error={Boolean(errors.url)}
hint={errors.url || <>Need inspiration? <a className='text-green' href="https://www.ghost.org/explore" rel="noopener noreferrer" target='_blank'>Explore thousands of sites</a> to recommend</>}
maxLength={2000}
placeholder='https://www.example.com'
title='URL'
value={formState.url}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ const RecommendationDescriptionForm: React.FC<Props<EditOrAddRecommendation | Re
autoFocus={true}
error={Boolean(errors.title)}
hint={errors.title}
maxLength={2000}
title="Title"
value={formState.title ?? ''}
onChange={(e) => {
Expand Down
Loading

0 comments on commit 9f19e33

Please sign in to comment.