Skip to content

Commit

Permalink
fix(error handling): add all api error message from backend (#105)
Browse files Browse the repository at this point in the history
  • Loading branch information
nidhigarg-bmw authored Jan 25, 2024
1 parent 79455b4 commit 5c02f13
Show file tree
Hide file tree
Showing 11 changed files with 292 additions and 66 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## Unreleased
- Add error handling of api errors

## 1.6.0-RC2

### Bugfix
Expand Down
176 changes: 176 additions & 0 deletions src/components/Snackbar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/********************************************************************************
* Copyright (c) 2021, 2024 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/

import { Box, IconButton, Slide } from '@mui/material'
import { Close } from '@mui/icons-material'
import { type SlideProps } from '@mui/material/Slide/Slide'
import { useCallback, useEffect, useRef, useState } from 'react'
import Snackbar from '@mui/material/Snackbar'
import CheckIcon from '@mui/icons-material/Check'
import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline'

const AUTO_CLOSE_DELAY_MS = 3000

const SlideTransition = (props: SlideProps) => (
<Slide {...props} direction="left" />
)

export enum SeverityType {
SUCCESS = 'success',
ERROR = 'error',
}

export interface PageSnackbarProps {
severity?: SeverityType
open: boolean
onCloseNotification?: () => void
title?: string | JSX.Element
description?: string | JSX.Element
showIcon?: boolean
autoClose?: boolean
}

export const PageSnackbar = ({
severity = SeverityType.SUCCESS,
onCloseNotification,
open,
autoClose,
title,
description,
showIcon = true,
...props
}: PageSnackbarProps) => {
const [isOpen, setIsOpen] = useState(open)

const autoCloseTimeoutRef = useRef<ReturnType<typeof setTimeout>>()

useEffect(() => {
setIsOpen(open)
}, [open])

const cancelAutoClose = useCallback(() => {
clearTimeout(autoCloseTimeoutRef.current)
}, [])

const doClose = useCallback(() => {
cancelAutoClose()
setIsOpen(false)

onCloseNotification?.()
}, [cancelAutoClose, onCloseNotification])

const handleAutoClose = useCallback(() => {
cancelAutoClose()

if (autoClose) {
autoCloseTimeoutRef.current = setTimeout(doClose, AUTO_CLOSE_DELAY_MS)
}
}, [autoClose, cancelAutoClose, doClose])

useEffect(handleAutoClose, [autoClose, handleAutoClose])

const renderIcon = () => {
switch (severity) {
case 'success':
return <CheckIcon sx={{ color: '#00AA55' }} />
case 'error':
return <ErrorOutlineIcon sx={{ color: '#D91E18' }} />
}
}

return (
<Snackbar
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
open={isOpen}
key={'bottom right'}
TransitionComponent={SlideTransition}
{...props}
onMouseEnter={cancelAutoClose}
onMouseLeave={handleAutoClose}
message={
<Box sx={{ display: 'flex', width: '100%', alignItems: 'flex-start' }}>
{showIcon && (
<Box
sx={{
flex: '0 0 auto',
marginRight: 2,
marginTop: '2px',
marginBottom: '-2px',
}}
>
{renderIcon()}
</Box>
)}
<Box sx={{ flex: '1 1 auto', alignSelf: 'center' }}>
{title && (
<Box
component="span"
sx={{ marginRight: 0.5, fontWeight: 'bold' }}
>
{title}
</Box>
)}
{description}
</Box>
<Box
sx={{
flex: '0 0 auto',
marginLeft: 1.5,
marginTop: '2px',
marginRight: '-3px',
}}
>
<IconButton size="small" aria-label="close" onClick={doClose}>
<Close fontSize="small" sx={{ color: 'text.primary' }} />
</IconButton>
</Box>
</Box>
}
sx={{
'.MuiSnackbarContent-root': {
width: '390px',
typography: 'body3',
backgroundColor: 'common.white',
color: 'text.primary',
borderWidth: '1px',
borderStyle: 'solid',
borderColor: 'action.disabledBackground',
borderRadius: '8px',
boxShadow: '0px 10px 20px 0px rgba(80, 80, 80, 0.3)',
},
'.MuiSnackbarContent-message': {
width: '100%',
},
}}
/>
)
}


export const Notify = ({ message }: { message: string }) => {
return (
<PageSnackbar
open={true}
severity={SeverityType.ERROR}
description={message}
showIcon={true}
autoClose={true}
/>
)
}
28 changes: 24 additions & 4 deletions src/components/cax-companyData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import { useTranslation } from 'react-i18next'
import { useEffect, useState } from 'react'
import { FooterButton } from './footerButton'
import { useDispatch, useSelector } from 'react-redux'
import { toast } from 'react-toastify'
import { isBPN, isCity, isStreet } from '../types/Patterns'
import {
useFetchApplicationsQuery,
Expand All @@ -40,6 +39,7 @@ import {
addCurrentStep,
getCurrentStep,
} from '../state/features/user/userApiSlice'
import { Notify } from './Snackbar'

const initialErrors = {
legalEntity: '',
Expand Down Expand Up @@ -83,14 +83,20 @@ export const CompanyDataCax = () => {
const [changedCountryValue, setChangedCountryValue] = useState<boolean>(false)
const [errors, setErrors] = useState(initialErrors)

const { data: companyDetails } =
const [submitError, setSubmitError] = useState(false)
const [identifierError, setIdentifierError] = useState(false)

const { data: companyDetails, error: companyDataError } =
useFetchCompanyDetailsWithAddressQuery(applicationId)
const [addCompanyDetailsWithAddress, { error: saveError, isLoading }] =
useAddCompanyDetailsWithAddressMutation()

useEffect(() => {
setSubmitError(false)
nextClicked && !isLoading && (
saveError ? toast.error(t('registrationStepOne.submitError')) : dispatch(addCurrentStep(currentActiveStep + 1))
saveError ?
setSubmitError(true) :
dispatch(addCurrentStep(currentActiveStep + 1))
)
}, [nextClicked, isLoading, saveError, currentActiveStep])

Expand All @@ -101,12 +107,13 @@ export const CompanyDataCax = () => {
} = useFetchUniqueIdentifierQuery(country)

useEffect(() => {
setIdentifierError(false)
setIdentifierDetails(error ? [] : identifierData)
if (identifierData?.length > 0) {
setShowIdentifiers(!error)
}
if (country && country.length === 2 && error)
toast.error(t('registrationStepOne.identifierError'))
setIdentifierError(true)
}, [identifierData, country, error])

useEffect(() => {
Expand Down Expand Up @@ -320,6 +327,18 @@ export const CompanyDataCax = () => {
setNextClicked(true)
}

const renderSnackbar = () => {
let message = t('registration.apiError')
if(identifierError){
message = t('registrationStepOne.identifierError')
}else if(submitError){
message = t('registrationStepOne.submitError')
}
return (
<Notify message={message} />
)
}

return (
<>
<div className="mx-auto col-9 container-registration">
Expand Down Expand Up @@ -589,6 +608,7 @@ export const CompanyDataCax = () => {
)}
</div>
</div>
{(companyDataError || submitError || identifierError) && renderSnackbar()}
<FooterButton
labelNext={t('button.confirm')}
handleBackClick={() => backClick()}
Expand Down
43 changes: 24 additions & 19 deletions src/components/cax-companyRole.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,13 @@ import { useState, useEffect } from 'react'
import { Row } from 'react-bootstrap'
import 'react-datepicker/dist/react-datepicker.css'
import { useTranslation } from 'react-i18next'
import { toast } from 'react-toastify'
import { FooterButton } from './footerButton'
import { useDispatch, useSelector } from 'react-redux'
import { companyRole } from '../state/features/applicationCompanyRole/types'
import { download } from '../helpers/utils'
import UserService from '../services/UserService'
import { getApiBase } from '../services/EnvironmentService'
import '../styles/newApp.css'
import { Notify } from './Snackbar'
import {
useFetchAgreementConsentsQuery,
useFetchAgreementDataQuery,
Expand All @@ -40,6 +39,7 @@ import {
addCurrentStep,
getCurrentStep,
} from '../state/features/user/userApiSlice'
import '../styles/newApp.css'

export const CompanyRoleCax = () => {
const { t, i18n } = useTranslation()
Expand All @@ -48,6 +48,7 @@ export const CompanyRoleCax = () => {
const currentActiveStep = useSelector(getCurrentStep)
const [companyRoleChecked, setCompanyRoleChecked] = useState({})
const [agreementChecked, setAgreementChecked] = useState({})
const [submitError, setSubmitError] = useState(false)

const { data: status } = useFetchApplicationsQuery()

Expand All @@ -56,22 +57,14 @@ export const CompanyRoleCax = () => {

const {
data: allConsentData,
error: allConsentError,
isLoading: allConsentLoading,
error: allConsentError
} = useFetchAgreementDataQuery()
const {
data: consentData,
error: consentError,
isLoading: consentLoading,
} = useFetchAgreementConsentsQuery(applicationId)
const [updateAgreementConsents] = useUpdateAgreementConsentsMutation()

if (
(allConsentLoading && allConsentError) ||
(consentLoading && consentError)
)
toast.error('')

useEffect(() => {
updateSelectedRolesAndAgreement()
}, [consentData])
Expand Down Expand Up @@ -208,13 +201,13 @@ export const CompanyRoleCax = () => {
)

const agreements = Object.keys(agreementChecked)
.filter(agreementId => agreementChecked[agreementId])
.map((agreementId) => {
return {
agreementId: agreementId,
consentStatus: 'ACTIVE'
}
})
.filter(agreementId => agreementChecked[agreementId])
.map((agreementId) => {
return {
agreementId: agreementId,
consentStatus: 'ACTIVE'
}
})

const data = {
companyRoles: companyRoles,
Expand All @@ -228,10 +221,16 @@ export const CompanyRoleCax = () => {
})
.catch((errors) => {
console.log('errors', errors)
toast.error(t('companyRole.submitError'))
setSubmitError(true)
})
}

const renderSnackbar = (message: string) => {
return (
<Notify message={message} />
)
}

return (
<>
<div className="mx-auto col-9 container-registration">
Expand Down Expand Up @@ -270,6 +269,12 @@ export const CompanyRoleCax = () => {
))}
</div>
</div>
{
(consentError || allConsentError) && renderSnackbar(t('registration.apiError'))
}
{
submitError && renderSnackbar(t('companyRole.submitError'))
}
<FooterButton
labelBack={t('button.back')}
labelNext={t('button.confirm')}
Expand Down
7 changes: 1 addition & 6 deletions src/components/cax-registration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import Footer from './footer'
import { Header } from './cax-header'
import ReactTooltip from 'react-tooltip'
import { useSelector } from 'react-redux'
import { toast } from 'react-toastify'
import 'react-datepicker/dist/react-datepicker.css'
import { CompanyDataCax } from './cax-companyData'
import { ResponsibilitiesCax } from './cax-responsibilities'
Expand All @@ -42,13 +41,9 @@ import { getCurrentStep } from '../state/features/user/userApiSlice'
export const RegistrationCax = () => {
const { t } = useTranslation()
const currentActiveStep = useSelector(getCurrentStep)
const { data: status, error: statusError } = useFetchApplicationsQuery()
const { data: status } = useFetchApplicationsQuery()
const [updateInvitation] = useUpdateInvitationMutation()

if (statusError) {
toast.error(t('registration.statusApplicationError'))
}

useEffect(() => {
if (status.length <= 0) {
updateInvitation()
Expand Down
Loading

0 comments on commit 5c02f13

Please sign in to comment.