diff --git a/apps/frontend/src/features/login/LoginPage.tsx b/apps/frontend/src/features/login/LoginPage.tsx index b1fa97d796..6e5a5ff2e1 100644 --- a/apps/frontend/src/features/login/LoginPage.tsx +++ b/apps/frontend/src/features/login/LoginPage.tsx @@ -114,8 +114,13 @@ export const LoginPage = (): JSX.Element => { return t('features.login.LoginPage.forbidden') case StatusCodes.UNAUTHORIZED.toString(): return t('features.login.LoginPage.expiredSession') - default: + default: { + const code = parseInt(statusCode ?? '') + if (code >= 500 && code < 600) { + return t('features.common.errors.serverError') + } return t('features.common.errors.generic') + } } }, [statusCode]) diff --git a/apps/frontend/src/features/login/SelectProfilePage.tsx b/apps/frontend/src/features/login/SelectProfilePage.tsx index 03e5b08450..f574e8114b 100644 --- a/apps/frontend/src/features/login/SelectProfilePage.tsx +++ b/apps/frontend/src/features/login/SelectProfilePage.tsx @@ -29,7 +29,7 @@ import { DASHBOARD_ROUTE, LOGIN_ROUTE } from '~constants/routes' import { useIsMobile } from '~hooks/useIsMobile' import { useLocalStorage } from '~hooks/useLocalStorage' import { useToast } from '~hooks/useToast' -import { ApiService } from '~services/ApiService' +import { ApiService, HttpError } from '~services/ApiService' import Button from '~components/Button' import { ModalCloseButton } from '~components/Modal' @@ -132,7 +132,21 @@ export const SelectProfilePage = (): JSX.Element => { // If redirected back here but already authed, redirect to dashboard. if (user) window.location.replace(DASHBOARD_ROUTE) // User doesn't have any profiles, should reattempt to login - if (profilesResponse.error) window.location.replace(LOGIN_ROUTE) + + if (profilesResponse.error) { + const err = profilesResponse.error + const statusCode = err instanceof HttpError ? err.code : undefined + + if (statusCode === StatusCodes.UNAUTHORIZED) { + // 401 — session invalid, redirect quietly + window.location.replace(LOGIN_ROUTE) + } else { + // all other HTTP errors — redirect with status so LoginPage can surface the appropriate toast. + // For non-HTTP errors (e.g. network failures), use a non-5xx sentinel so we fall back to the generic toast. + const statusParam = statusCode ?? 0 + window.location.replace(`${LOGIN_ROUTE}?status=${statusParam}`) + } + } useEffect(() => { if (profilesResponse.data?.profiles.length === 0) { diff --git a/apps/frontend/src/i18n/locales/features/common/en-sg.ts b/apps/frontend/src/i18n/locales/features/common/en-sg.ts index 9523252f33..99b890f741 100644 --- a/apps/frontend/src/i18n/locales/features/common/en-sg.ts +++ b/apps/frontend/src/i18n/locales/features/common/en-sg.ts @@ -62,6 +62,7 @@ export const enSG: Common = { }, pageNotFound: 'This page could not be found.', generic: 'Something went wrong. Please try again later.', + serverError: 'A server error occurred. Please try again later.', }, tooltip: { deleteField: 'Delete field', diff --git a/apps/frontend/src/i18n/locales/features/common/index.ts b/apps/frontend/src/i18n/locales/features/common/index.ts index ec4e0fa352..9e4a6192e6 100644 --- a/apps/frontend/src/i18n/locales/features/common/index.ts +++ b/apps/frontend/src/i18n/locales/features/common/index.ts @@ -62,6 +62,7 @@ export interface Common { } pageNotFound: string generic: string + serverError: string } tooltip: { deleteField: string