diff --git a/apps/web/core/components/common/activity/user.tsx b/apps/web/core/components/common/activity/user.tsx index 1ba3d03b750..595a960fb48 100644 --- a/apps/web/core/components/common/activity/user.tsx +++ b/apps/web/core/components/common/activity/user.tsx @@ -28,7 +28,7 @@ export const User = observer(function User(props: TUser) { return ( <> - {customUserName || actorDetail?.display_name.includes("-intake") ? ( + {customUserName || actorDetail?.display_name?.includes("-intake") ? ( {customUserName || "Plane"} ) : ( void }) { + const { t } = useTranslation(); + + return ( +
+ +

{t("something_went_wrong")}

+ +
+ ); +} + +// Catches render crashes from a single issue layout (list/kanban/spreadsheet/calendar/gantt) +// so a bad group/column shape degrades to a local fallback instead of taking down the whole page. +export class LayoutErrorBoundary extends Component { + state: State = { hasError: false, retryKey: 0 }; + + static getDerivedStateFromError(): Partial { + return { hasError: true }; + } + + componentDidCatch(error: Error, info: ErrorInfo) { + // eslint-disable-next-line no-console + console.error("Issue layout crashed", error, info); + } + + handleRetry = () => { + this.setState((prev) => ({ hasError: false, retryKey: prev.retryKey + 1 })); + }; + + render() { + if (this.state.hasError) { + return ; + } + return {this.props.children}; + } +} diff --git a/apps/web/core/components/core/description-versions/root.tsx b/apps/web/core/components/core/description-versions/root.tsx index 9922cd27ac1..0f1a577b2a0 100644 --- a/apps/web/core/components/core/description-versions/root.tsx +++ b/apps/web/core/components/core/description-versions/root.tsx @@ -50,7 +50,7 @@ export const DescriptionVersionsRoot = observer(function DescriptionVersionsRoot entityId && activeVersionId ? `DESCRIPTION_VERSION_DETAILS_${activeVersionId}` : null, entityId && activeVersionId ? () => fetchHandlers.retrieveDescriptionVersion(entityId, activeVersionId) : null ); - const versions = versionsListResponse?.results; + const versions = Array.isArray(versionsListResponse?.results) ? versionsListResponse.results : undefined; const versionsCount = versions?.length ?? 0; const activeVersionDetails = versions?.find((version) => version.id === activeVersionId); const activeVersionIndex = versions?.findIndex((version) => version.id === activeVersionId); diff --git a/apps/web/core/components/exporter/prev-exports.tsx b/apps/web/core/components/exporter/prev-exports.tsx index 2334a6c159c..f3ebedb41f0 100644 --- a/apps/web/core/components/exporter/prev-exports.tsx +++ b/apps/web/core/components/exporter/prev-exports.tsx @@ -4,7 +4,7 @@ * See the LICENSE file for details. */ -import { useEffect, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { observer } from "mobx-react"; import useSWR, { mutate } from "swr"; import { MoveLeft, MoveRight, RefreshCw } from "lucide-react"; @@ -46,14 +46,24 @@ export const PrevExports = observer(function PrevExports(props: Props) { workspaceSlug && cursor ? () => integrationService.getExportsServicesList(workspaceSlug, cursor, per_page) : null ); - const handleRefresh = () => { + const handleRefresh = useCallback(async () => { setRefreshing(true); - mutate(EXPORT_SERVICES_LIST(workspaceSlug, `${cursor}`, `${per_page}`)).then(() => setRefreshing(false)); - }; + try { + await mutate(EXPORT_SERVICES_LIST(workspaceSlug, `${cursor}`, `${per_page}`)); + } catch (error) { + // eslint-disable-next-line no-console + console.error("Failed to refresh export services list", error); + } finally { + setRefreshing(false); + } + }, [workspaceSlug, cursor, per_page]); useEffect(() => { const interval = setInterval(() => { - if (exporterServices?.results?.some((service) => service.status === "processing")) { + if ( + Array.isArray(exporterServices?.results) && + exporterServices.results.some((service) => service.status === "processing") + ) { handleRefresh(); } else { clearInterval(interval); @@ -61,7 +71,7 @@ export const PrevExports = observer(function PrevExports(props: Props) { }, 3000); return () => clearInterval(interval); - }, [exporterServices]); + }, [exporterServices, handleRefresh]); return (
@@ -73,7 +83,7 @@ export const PrevExports = observer(function PrevExports(props: Props) { {refreshing ? t("refreshing") : t("refresh_status")}
- {!!exporterServices?.results?.length && ( + {Array.isArray(exporterServices?.results) && exporterServices.results.length > 0 && (