diff --git a/.eslintrc.js b/.eslintrc.js index 214ecd86af..e60712ad57 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -35,6 +35,7 @@ const config = { }, sourceType: 'module', allowImportExportEverywhere: true, + project: ['tsconfig.json'], }, rules: { strict: 1, @@ -53,6 +54,8 @@ const config = { '@typescript-eslint/explicit-function-return-type': 0, '@typescript-eslint/explicit-module-boundary-types': 0, + '@typescript-eslint/no-unnecessary-condition': 'warn', + 'no-console': 1, indent: ['error', 4, { SwitchCase: 1 }], diff --git a/app/Base/components/Navbar/index.tsx b/app/Base/components/Navbar/index.tsx index ecf670850a..7f7a6df03b 100644 --- a/app/Base/components/Navbar/index.tsx +++ b/app/Base/components/Navbar/index.tsx @@ -124,7 +124,7 @@ function Navbar(props: Props) { }); const handleCloseNotificationClick = useCallback(() => { - notificationRef?.current?.setShowPopup(false); + notificationRef.current?.setShowPopup(false); }, []); useEffect( diff --git a/app/Base/index.tsx b/app/Base/index.tsx index 6af7df1ae3..8462c1d449 100644 --- a/app/Base/index.tsx +++ b/app/Base/index.tsx @@ -33,6 +33,7 @@ import { } from '#base/utils/restRequest'; import 'mapbox-gl/dist/mapbox-gl.css'; +import { find } from '#base/utils/common'; import localforageInstance from '#base/configs/localforage'; import { mapboxToken } from '#base/configs/env'; @@ -119,7 +120,7 @@ function Base() { setAlerts((prevAlerts) => unique( [...prevAlerts, alert], (a) => a.name, - ) ?? prevAlerts); + )); }, [setAlerts], ); @@ -144,18 +145,18 @@ function Base() { const updateAlertContent = React.useCallback( (name: string, children: React.ReactNode) => { setAlerts((prevAlerts) => { - const i = prevAlerts.findIndex((a) => a.name === name); - if (i === -1) { + const prevAlert = find(prevAlerts, (a) => a.name === name); + if (prevAlert.index === undefined) { return prevAlerts; } const updatedAlert = { - ...prevAlerts[i], + ...prevAlert.value, children, }; const newAlerts = [...prevAlerts]; - newAlerts.splice(i, 1, updatedAlert); + newAlerts.splice(prevAlert.index, 1, updatedAlert); return newAlerts; }); diff --git a/app/Base/utils/apollo.ts b/app/Base/utils/apollo.ts index 8c28ad43bf..96c9e012ec 100644 --- a/app/Base/utils/apollo.ts +++ b/app/Base/utils/apollo.ts @@ -6,7 +6,7 @@ export function isArrayEqual(foo: readonly T[], bar: T[]) { export function checkErrorCode(errors: ApolloError['graphQLErrors'], path: (string | number)[], code: string) { return errors.some((error) => ( - error.path && error.extensions?.code + error.path && error.extensions.code && isArrayEqual(error.path, path) && code === error.extensions.code )); } diff --git a/app/Base/utils/common.ts b/app/Base/utils/common.ts new file mode 100644 index 0000000000..6226f7f05f --- /dev/null +++ b/app/Base/utils/common.ts @@ -0,0 +1,14 @@ +// eslint-disable-next-line import/prefer-default-export +export function find(list: T[], selector: (value: T, index: number, obj: T[]) => boolean) { + const index = list.findIndex(selector); + if (index === -1) { + return { + index: undefined, + value: undefined, + }; + } + return { + index, + value: list[index] as T, + }; +} diff --git a/app/Base/utils/restRequest.ts b/app/Base/utils/restRequest.ts index eec917809c..4149be0039 100644 --- a/app/Base/utils/restRequest.ts +++ b/app/Base/utils/restRequest.ts @@ -45,9 +45,9 @@ type FormDataCompatibleObj = Record { - const value = jsonData?.[key]; + const value = jsonData[key]; if (value && Array.isArray(value)) { value.forEach((v) => { formData.append(key, v instanceof Blob ? v : String(v)); @@ -178,9 +178,7 @@ export const processDeepOptions: DeepContextInterface['transformOptions'] = ( const csrftoken = getCookie(`deep-${deepEnvironment}-csrftoken`); finalOptions.credentials = 'include'; - if (finalOptions.headers) { - finalOptions.headers['X-CSRFToken'] = csrftoken; - } + finalOptions.headers['X-CSRFToken'] = csrftoken; } return finalOptions; @@ -250,7 +248,7 @@ export const processDeepError = ( const faramErrors = alterResponse(res.errors); const messageForNotification = ( - faramErrors?.$internal + faramErrors.$internal ?? 'Some error occurred while performing this action.' ); diff --git a/app/components/GeoLocationInput/GeoLocationModal/GeoLocationMapInput/index.tsx b/app/components/GeoLocationInput/GeoLocationModal/GeoLocationMapInput/index.tsx index 94b5a42c20..a0f0f26b7c 100644 --- a/app/components/GeoLocationInput/GeoLocationModal/GeoLocationMapInput/index.tsx +++ b/app/components/GeoLocationInput/GeoLocationModal/GeoLocationMapInput/index.tsx @@ -97,13 +97,24 @@ function GeoLocationMapInput(props: Props) { skip: !projectId, variables, onCompleted: (data) => { - const [topRegion] = data.project?.regions?.filter( + if (!data.project?.regions) { + return; + } + + const regions = data.project.regions.filter( (region) => region.isPublished, - ) ?? []; - const topAdminLevel = topRegion?.adminLevels?.find((v) => v.level === 0) - ?? topRegion?.adminLevels?.[0]; + ); + const topRegion = regions[0]; + if (!topRegion) { + // eslint-disable-next-line no-console + console.error('There must be at least on region'); + return; + } + + const topAdminLevel = topRegion.adminLevels?.find((v) => v.level === 0) + ?? topRegion.adminLevels?.[0]; - setSelectedRegion(topRegion?.id); + setSelectedRegion(topRegion.id); setActiveAdminLevel(topAdminLevel?.id); }, }, diff --git a/app/components/NonFieldError/index.tsx b/app/components/NonFieldError/index.tsx index e8486fac01..62ca13b94a 100644 --- a/app/components/NonFieldError/index.tsx +++ b/app/components/NonFieldError/index.tsx @@ -29,7 +29,7 @@ function NonFieldError(props: Props) { ); } - const internalError = error?.[internal]; + const internalError = error[internal]; if (!internalError) { return null; diff --git a/app/components/Notifications/NotificationContainer/index.tsx b/app/components/Notifications/NotificationContainer/index.tsx index 2454c7bfd2..85809cf917 100644 --- a/app/components/Notifications/NotificationContainer/index.tsx +++ b/app/components/Notifications/NotificationContainer/index.tsx @@ -93,8 +93,8 @@ function NotificationContainer(props: Props) { { refetchQueries: ['UserNotificationsCount'], onCompleted: (response) => { - if (response?.notificationStatusUpdate?.ok) { - const newStatus = response.notificationStatusUpdate?.result?.statusDisplay; + if (response.notificationStatusUpdate?.ok) { + const newStatus = response.notificationStatusUpdate.result?.statusDisplay; alert.show( `Successfully updated notification status as ${newStatus?.toLowerCase() ?? 'required'}.`, { diff --git a/app/components/Notifications/NotificationItem/ProjectJoinRequestItem/index.tsx b/app/components/Notifications/NotificationItem/ProjectJoinRequestItem/index.tsx index 308a737bd3..60831fa582 100644 --- a/app/components/Notifications/NotificationItem/ProjectJoinRequestItem/index.tsx +++ b/app/components/Notifications/NotificationItem/ProjectJoinRequestItem/index.tsx @@ -68,8 +68,8 @@ function ProjectJoinRequestItem(props: Props) { { refetchQueries: ['UserNotificationsCount'], onCompleted: (response) => { - if (response?.project?.acceptRejectProject?.ok) { - const status = response.project.acceptRejectProject?.result?.status; + if (response.project?.acceptRejectProject?.ok) { + const status = response.project.acceptRejectProject.result?.status; alert.show( status === 'ACCEPTED' ? 'Successfully added user to the project.' @@ -129,20 +129,20 @@ function ProjectJoinRequestItem(props: Props) { {data?.requested_by?.display_name}), - projectTitle: ({data?.project?.title}), + requestorName: ({data.requested_by.display_name}), + projectTitle: ({data.project.title}), }, ) } - actions={data?.status === 'pending' && ( + actions={data.status === 'pending' && ( <>