Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code 9b1e6d5a9d fix(views): handle missing object metadata in useFiltersFromQueryParams
https://sonarly.com/issue/33231?type=bug

When a user navigates to a deleted custom object URL (e.g., `/objects/accommodations`), the `QueryParamsFiltersEffect` component crashes because `useFiltersFromQueryParams` reads the object name from URL params and calls `useObjectNameSingularFromPlural` which throws for non-existent objects, before the page-level redirect to `/not-found` takes effect.
2026-05-01 04:51:24 +00:00
2 changed files with 10 additions and 13 deletions
@@ -1,8 +1,6 @@
import { useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
@@ -18,10 +16,10 @@ export const QueryParamsFiltersEffect = () => {
useFiltersFromQueryParams();
const { hasFiltersQueryParams } = useHasFiltersInQueryParams();
const { objectNamePlural = '' } = useParams();
const { objectNameSingular } = useObjectNameSingularFromPlural({
objectNamePlural,
});
// Use objectNameSingular from validated RecordIndex context instead of raw
// URL params to avoid crashes when navigating to non-existent objects
const { objectNameSingular, recordIndexId } =
useRecordIndexContextOrThrow();
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
});
@@ -30,7 +28,6 @@ export const QueryParamsFiltersEffect = () => {
const { mapViewFiltersToRecordFilters } = useMapViewFiltersToFilters();
const { recordIndexId } = useRecordIndexContextOrThrow();
const setCurrentRecordFilters = useSetAtomComponentState(
currentRecordFiltersComponentState,
recordIndexId,
@@ -2,7 +2,6 @@ import { isNonEmptyString, isObject } from '@sniptt/guards';
import qs from 'qs';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
import { isCompositeFieldType } from '@/object-record/object-filter-dropdown/utils/isCompositeFieldType';
import { type RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
@@ -10,17 +9,18 @@ import { filterUrlQueryParamsSchema } from '@/views/schemas/filterUrlQueryParams
import { type ViewFilter } from '@/views/types/ViewFilter';
import { deserializeUrlRecursiveFilterGroup } from '@/views/utils/deserializeUrlRecursiveFilterGroup';
import { splitFieldNameIntoBaseAndSubField } from '@/views/utils/splitFieldNameIntoBaseAndSubField';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { useCallback } from 'react';
import { useParams, useSearchParams } from 'react-router-dom';
import { useSearchParams } from 'react-router-dom';
import { type ViewFilterOperand } from 'twenty-shared/types';
import { isDefined, isExpectedSubFieldName } from 'twenty-shared/utils';
export const useFiltersFromQueryParams = () => {
const [searchParams] = useSearchParams();
const { objectNamePlural = '' } = useParams();
const { objectNameSingular } = useObjectNameSingularFromPlural({
objectNamePlural,
});
// Use objectNameSingular from validated RecordIndex context instead of raw
// URL params to avoid crashes when navigating to non-existent objects
const { objectNameSingular } = useRecordIndexContextOrThrow();
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
});