Skip to content

Commit

Permalink
Feat/toast notification errors (#281)
Browse files Browse the repository at this point in the history
* feat: add error toasts when saving and deleting images/files

This commit adds error toast notifications when the api call to
save or delete an image or file fails.

* feat: add error toasts when saving settings

This commit adds error toast notifications when the api call to
save a repo's setting config fails.

* feat: add error toasts for FolderCard

This commit adds error toast notifications when the api call to rename
or to delete a folder (collection or resource category) fails

* feat: add error toasts for OverviewCard

This commit adds error toast notifications when the api call to delete
or to move a page (normal page, collection page, or resource page) fails

* feat: add error toast notifications to ComponentSettingsModal operations

This commit adds error toast notifications when the ComponentSettingsModal
tries but fails to retrieve page setting information. It also modifies the
component so that an error toast is displayed when the api call to save
page settings or to delete the page fails.

* feat: add error toast when we fail to retrieve third nav options

This commit adds an error toast when we fail to retrieve third nav
options in the ComponentSettingsModal

* feat: add error toasts for EditPage and EditHomepage

This commit adds error toasts when the api calls to save EditHomepage
fails, or when the api calls to save or delete the EditPage fails.

* fix: undefined exception when handling errors

When handling errors in some cases, we attempt to check for a http
error response status. However, since the error might not come from
a http error, the `status` attribute might not be present in the error
object, which leads to an undefined attribute exception.

This commit fixes that bug by using optional chaining.

* chore: typo

* chore: store default error toast message as a constant

This commit modifies all applicable existing usage of the Toast
component to use the default error toast message that is now
defined as a constant in the main utils file. This is so that if
we need to reword our error message, we only need to do it in one
place, instead of in every single file.

Co-authored-by: Jie Hao Kwa <[email protected]>
  • Loading branch information
kwajiehao and Jie Hao Kwa authored Dec 7, 2020
1 parent aefeae3 commit 380e6b5
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 22 deletions.
1 change: 1 addition & 0 deletions src/components/CollectionPagesSection.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ const CollectionPagesSection = ({ collectionName, pages, siteName, isResource })
}
collectionPageData={collectionPageData}
loadThirdNavOptions={loadThirdNavOptions}
setIsComponentSettingsActive={setIsComponentSettingsActive}
/>
)
}
Expand Down
30 changes: 27 additions & 3 deletions src/components/ComponentSettingsModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as _ from 'lodash';
import { Redirect } from 'react-router-dom';
import FormField from './FormField';
import {
DEFAULT_ERROR_TOAST_MSG,
frontMatterParser,
dequoteString,
generatePageFileName,
Expand All @@ -21,6 +22,8 @@ import { validatePageSettings, validateResourceSettings } from '../utils/validat
import DeleteWarningModal from './DeleteWarningModal';
import ResourceFormFields from './ResourceFormFields';
import SaveDeleteButtons from './SaveDeleteButtons';
import { toast } from 'react-toastify';
import Toast from './Toast';

// Import utils
import { retrieveThirdNavOptions } from '../utils/dropdownUtils'
Expand Down Expand Up @@ -56,6 +59,7 @@ const ComponentSettingsModal = ({
siteName,
type,
loadThirdNavOptions,
setIsComponentSettingsActive,
}) => {
// Errors
const [errors, setErrors] = useState({
Expand Down Expand Up @@ -184,7 +188,14 @@ const ComponentSettingsModal = ({
}
}

fetchData().catch((err) => console.log(err))
fetchData().catch((err) => {
setIsComponentSettingsActive((prevState) => !prevState)
toast(
<Toast notificationType='error' text={`There was a problem retrieving data from your repo. ${DEFAULT_ERROR_TOAST_MSG}`}/>,
{className: `${elementStyles.toastError} ${elementStyles.toastLong}`},
);
console.log(err)
})
return () => { _isMounted = false }
}, [])

Expand Down Expand Up @@ -245,12 +256,21 @@ const ComponentSettingsModal = ({
setNewPageUrl(redirectUrl)
setRedirectToNewPage(true)
} catch (err) {
if (err.response.status === 409) {
if (err?.response?.status === 409) {
// Error due to conflict in name
setErrors((prevState) => ({
...prevState,
title: 'This title is already in use. Please choose a different one.',
}));
toast(
<Toast notificationType='error' text={`Another ${type === 'image' ? 'image' : 'file'} with the same name exists. Please choose a different name.`}/>,
{className: `${elementStyles.toastError} ${elementStyles.toastLong}`},
);
} else {
toast(
<Toast notificationType='error' text={`There was a problem saving your page settings. ${DEFAULT_ERROR_TOAST_MSG}`}/>,
{className: `${elementStyles.toastError} ${elementStyles.toastLong}`},
);
}
console.log(err);
}
Expand All @@ -266,7 +286,11 @@ const ComponentSettingsModal = ({
// Refresh page
window.location.reload();
} catch (err) {
console.log(err);
toast(
<Toast notificationType='error' text={`There was a problem trying to delete this file. ${DEFAULT_ERROR_TOAST_MSG}.`}/>,
{className: `${elementStyles.toastError} ${elementStyles.toastLong}`},
);
console.log(err);
}
}

Expand Down
7 changes: 7 additions & 0 deletions src/components/FolderCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ import axios from 'axios';

import FolderModal from './FolderModal';
import DeleteWarningModal from './DeleteWarningModal'
import { toast } from 'react-toastify';
import Toast from './Toast';

import elementStyles from '../styles/isomer-cms/Elements.module.scss';
import contentStyles from '../styles/isomer-cms/pages/Content.module.scss';

import {
DEFAULT_ERROR_TOAST_MSG,
checkIsOutOfViewport,
} from '../utils'

Expand Down Expand Up @@ -91,6 +94,10 @@ const FolderCard = ({
// Refresh page
window.location.reload();
} catch (err) {
toast(
<Toast notificationType='error' text={`There was a problem trying to delete this folder. ${DEFAULT_ERROR_TOAST_MSG}`}/>,
{className: `${elementStyles.toastError} ${elementStyles.toastLong}`}
);
console.log(err);
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/components/FolderModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import PropTypes from 'prop-types';
import elementStyles from '../styles/isomer-cms/Elements.module.scss';
import SaveDeleteButtons from './SaveDeleteButtons';
import FormField from './FormField';
import { toast } from 'react-toastify';
import Toast from './Toast';
import {
DEFAULT_ERROR_TOAST_MSG,
} from '../utils'

// axios settings
axios.defaults.withCredentials = true
Expand All @@ -22,6 +27,10 @@ const FolderModal = ({ displayTitle, displayText, onClose, category, siteName, i
// Refresh page
window.location.reload();
} catch (err) {
toast(
<Toast notificationType='error' text={`There was a problem trying to rename this folder. ${DEFAULT_ERROR_TOAST_MSG}`}/>,
{className: `${elementStyles.toastError} ${elementStyles.toastLong}`}
);
console.log(err);
}
}
Expand Down
12 changes: 11 additions & 1 deletion src/components/OverviewCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import GenericWarningModal from './GenericWarningModal'
import LoadingButton from './LoadingButton'
import Toast from './Toast';
import {
DEFAULT_ERROR_TOAST_MSG,
frontMatterParser,
saveFileAndRetrieveUrl,
checkIsOutOfViewport,
Expand Down Expand Up @@ -100,12 +101,17 @@ const OverviewCard = ({
// Refresh page
window.location.reload();
} catch (err) {
if (err.response.status === 409) {
if (err?.response?.status === 409) {
// Error due to conflict in name
toast(
<Toast notificationType='error' text='This file name already exists in the category you are trying to move to. Please rename the file before proceeding.'/>,
{className: `${elementStyles.toastError} ${elementStyles.toastLong}`}
);
} else {
toast(
<Toast notificationType='error' text={`There was a problem trying to move this file. ${DEFAULT_ERROR_TOAST_MSG}`}/>,
{className: `${elementStyles.toastError} ${elementStyles.toastLong}`}
);
}
setCanShowGenericWarningModal(false)
console.log(err);
Expand All @@ -126,6 +132,10 @@ const OverviewCard = ({
// Refresh page
window.location.reload();
} catch (err) {
toast(
<Toast notificationType='error' text="There was a problem trying to delete this file. Please try again or check your internet connection."/>,
{className: `${elementStyles.toastError} ${elementStyles.toastLong}`}
);
console.log(err);
}
}
Expand Down
16 changes: 14 additions & 2 deletions src/components/media/MediaSettingsModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import SaveDeleteButtons from '../SaveDeleteButtons';
import { validateFileName } from '../../utils/validators';
import { toast } from 'react-toastify';
import Toast from '../Toast';
import {
DEFAULT_ERROR_TOAST_MSG,
} from '../../utils'

export default class MediaSettingsModal extends Component {
constructor(props) {
Expand Down Expand Up @@ -85,20 +88,25 @@ export default class MediaSettingsModal extends Component {
}
onSave()
} catch (err) {
if (err.response.status === 409) {
if (err?.response?.status === 409) {
// Error due to conflict in name
toast(
<Toast notificationType='error' text={`Another ${type === 'image' ? 'image' : 'file'} with the same name exists. Please choose a different name.`}/>,
{className: `${elementStyles.toastError} ${elementStyles.toastLong}`}
);
} else {
toast(
<Toast notificationType='error' text={`There was a problem trying to save this ${type === 'image' ? 'image' : 'file'}. ${DEFAULT_ERROR_TOAST_MSG}`}/>,
{className: `${elementStyles.toastError} ${elementStyles.toastLong}`}
);
}
console.log(err);
}
}

deleteFile = async () => {
const { siteName, media: { fileName }, type } = this.props;
try {
const { siteName, media: { fileName }, type } = this.props;
const { sha } = this.state;
const params = {
sha,
Expand All @@ -111,6 +119,10 @@ export default class MediaSettingsModal extends Component {

window.location.reload();
} catch (err) {
toast(
<Toast notificationType='error' text={`There was a problem trying to delete this ${type === 'image' ? 'image' : 'file'}. ${DEFAULT_ERROR_TOAST_MSG}`}/>,
{className: `${elementStyles.toastError} ${elementStyles.toastLong}`}
);
console.log(err);
}
}
Expand Down
8 changes: 7 additions & 1 deletion src/layouts/EditHomepage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import TemplateInfobarSection from '../templates/homepage/InfobarSection';
import TemplateInfopicLeftSection from '../templates/homepage/InfopicLeftSection';
import TemplateInfopicRightSection from '../templates/homepage/InfopicRightSection';
import TemplateResourcesSection from '../templates/homepage/ResourcesSection';
import { frontMatterParser, concatFrontMatterMdBody } from '../utils';
import { DEFAULT_ERROR_TOAST_MSG, frontMatterParser, concatFrontMatterMdBody } from '../utils';
import EditorInfobarSection from '../components/homepage/InfobarSection';
import EditorInfopicSection from '../components/homepage/InfopicSection';
import EditorResourcesSection from '../components/homepage/ResourcesSection';
Expand All @@ -24,6 +24,8 @@ import Header from '../components/Header';
import LoadingButton from '../components/LoadingButton';
import { validateSections, validateHighlights, validateDropdownElems } from '../utils/validators';
import DeleteWarningModal from '../components/DeleteWarningModal';
import { toast } from 'react-toastify';
import Toast from '../components/Toast';

/* eslint-disable react/jsx-props-no-spreading */
/* eslint-disable react/no-array-index-key */
Expand Down Expand Up @@ -921,6 +923,10 @@ export default class EditHomepage extends Component {

window.location.reload();
} catch (err) {
toast(
<Toast notificationType='error' text={`There was a problem trying to save your homepage. ${DEFAULT_ERROR_TOAST_MSG}`}/>,
{className: `${elementStyles.toastError} ${elementStyles.toastLong}`}
);
console.log(err);
}
}
Expand Down
11 changes: 11 additions & 0 deletions src/layouts/EditPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ import SimplePage from '../templates/SimplePage';
import LeftNavPage from '../templates/LeftNavPage';
import { checkCSP } from '../utils/cspUtils';
import Policy from 'csp-parse';
import { toast } from 'react-toastify';
import Toast from '../components/Toast';

import {
DEFAULT_ERROR_TOAST_MSG,
frontMatterParser,
concatFrontMatterMdBody,
prependImageSrc,
Expand Down Expand Up @@ -188,6 +191,10 @@ export default class EditPage extends Component {

window.location.reload();
} catch (err) {
toast(
<Toast notificationType='error' text={`There was a problem saving your page. ${DEFAULT_ERROR_TOAST_MSG}`}/>,
{className: `${elementStyles.toastError} ${elementStyles.toastLong}`}
);
console.log(err);
}
}
Expand All @@ -202,6 +209,10 @@ export default class EditPage extends Component {
});
history.goBack();
} catch (err) {
toast(
<Toast notificationType='error' text={`There was a problem deleting your page. ${DEFAULT_ERROR_TOAST_MSG}`}/>,
{className: `${elementStyles.toastError} ${elementStyles.toastLong}`}
);
console.log(err);
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/layouts/Settings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ import FormFieldHorizontal from '../components/FormFieldHorizontal';
import elementStyles from '../styles/isomer-cms/Elements.module.scss';
import contentStyles from '../styles/isomer-cms/pages/Content.module.scss';
import { validateSocialMedia } from '../utils/validators';
import { toast } from 'react-toastify';
import Toast from '../components/Toast';
import {
DEFAULT_ERROR_TOAST_MSG,
} from '../utils'

const stateFields = {
title: '',
Expand Down Expand Up @@ -292,6 +297,10 @@ export default class Settings extends Component {

window.location.reload();
} catch (err) {
toast(
<Toast notificationType='error' text={`There was a problem trying to save your settings. ${DEFAULT_ERROR_TOAST_MSG}`}/>,
{className: `${elementStyles.toastError} ${elementStyles.toastLong}`}
);
console.log(err);
}
}
Expand Down
1 change: 1 addition & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ axios.defaults.withCredentials = true
const ALPHANUM_REGEX = /^[0-9]+[a-z]*$/ // at least one number, followed by 0 or more lower-cased alphabets
const NUM_REGEX = /^[0-9]+$/
const NUM_IDENTIFIER_REGEX = /^[0-9]+/
export const DEFAULT_ERROR_TOAST_MSG = 'Please try again or check your internet connection.'

// extracts yaml front matter from a markdown file path
export function frontMatterParser(content) {
Expand Down
43 changes: 28 additions & 15 deletions src/utils/dropdownUtils.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,37 @@
import axios from 'axios';
import React from 'react';
import elementStyles from '../styles/isomer-cms/Elements.module.scss';
import { toast } from 'react-toastify';
import Toast from '../components/Toast';
import { DEFAULT_ERROR_TOAST_MSG } from '../utils'

// axios settings
axios.defaults.withCredentials = true

export const retrieveThirdNavOptions = async (siteName, collectionName, isExistingCollection) => {
let thirdNavArr = [], allCollectionPages = []
if (isExistingCollection) {
const endpoint = `${process.env.REACT_APP_BACKEND_URL}/sites/${siteName}/collections/${collectionName}/pages`
const { data : { collectionPages } } = await axios.get(endpoint)
thirdNavArr = collectionPages.filter((elem) => elem.type === 'third-nav')
allCollectionPages = collectionPages
}
const thirdNavOptions = [''].concat(thirdNavArr).map((thirdNav) => (
{
value:thirdNav.title,
label:thirdNav.title ? thirdNav.title : 'None',
try {
let thirdNavArr = [], allCollectionPages = []
if (isExistingCollection) {
const endpoint = `${process.env.REACT_APP_BACKEND_URL}/sites/${siteName}/collections/${collectionName}/pages`
const { data : { collectionPages } } = await axios.get(endpoint)
thirdNavArr = collectionPages.filter((elem) => elem.type === 'third-nav')
allCollectionPages = collectionPages
}
const thirdNavOptions = [''].concat(thirdNavArr).map((thirdNav) => (
{
value:thirdNav.title,
label:thirdNav.title ? thirdNav.title : 'None',
}
))
return {
collectionPages: allCollectionPages,
thirdNavOptions,
}
))
return {
collectionPages: allCollectionPages,
thirdNavOptions,
} catch (err) {
toast(
<Toast notificationType='error' text={`There was a problem trying to retrieve data from your repo. ${DEFAULT_ERROR_TOAST_MSG}`}/>,
{className: `${elementStyles.toastError} ${elementStyles.toastLong}`},
);
console.log(err);
}
}

0 comments on commit 380e6b5

Please sign in to comment.