Skip to content

Commit 9e72aaf

Browse files
fix changes requested
Signed-off-by: Philip Colares Carneiro <philip.colares@gmail.com>
1 parent 3d1ffd1 commit 9e72aaf

9 files changed

Lines changed: 132 additions & 98 deletions

File tree

clients/ui/frontend/src/app/context/mcpCatalog/McpCatalogContext.tsx

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import useModelCatalogAPIState, {
77
import { useCatalogSources } from '~/app/hooks/modelCatalog/useCatalogSources';
88
import { useCatalogLabels } from '~/app/hooks/modelCatalog/useCatalogLabels';
99
import { useMcpServerFilterOptionListWithAPI } from '~/app/hooks/mcpServerCatalog/useMcpServerFilterOptionList';
10+
import useEmptyCategoryTracking from '~/app/hooks/useEmptyCategoryTracking';
1011
import type {
1112
McpCatalogContextType,
1213
McpCatalogPaginationState,
@@ -91,25 +92,7 @@ export const McpCatalogContextProvider: React.FC<McpCatalogContextProviderProps>
9192
const [selectedSourceLabel, setSelectedSourceLabel] = React.useState<string | undefined>(
9293
initialState.selectedSourceLabel,
9394
);
94-
const [emptyCategoryLabels, setEmptyCategoryLabels] = React.useState<Set<string>>(
95-
() => new Set<string>(),
96-
);
97-
const emptyCategoryLabelsRef = React.useRef(emptyCategoryLabels);
98-
emptyCategoryLabelsRef.current = emptyCategoryLabels;
99-
100-
const reportCategoryEmpty = React.useCallback((label: string, isEmpty: boolean) => {
101-
const { current } = emptyCategoryLabelsRef;
102-
const hasLabel = current.has(label);
103-
if (isEmpty && !hasLabel) {
104-
const next = new Set(current);
105-
next.add(label);
106-
setEmptyCategoryLabels(next);
107-
} else if (!isEmpty && hasLabel) {
108-
const next = new Set(current);
109-
next.delete(label);
110-
setEmptyCategoryLabels(next);
111-
}
112-
}, []);
95+
const { emptyCategoryLabels, reportCategoryEmpty } = useEmptyCategoryTracking();
11396

11497
React.useEffect(() => {
11598
syncToUrl({ searchQuery, filters, selectedSourceLabel });

clients/ui/frontend/src/app/context/modelCatalog/ModelCatalogContext.tsx

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { useCatalogSources } from '~/app/hooks/modelCatalog/useCatalogSources';
88
import useModelCatalogAPIState, {
99
ModelCatalogAPIState,
1010
} from '~/app/hooks/modelCatalog/useModelCatalogAPIState';
11+
import useEmptyCategoryTracking from '~/app/hooks/useEmptyCategoryTracking';
1112
import {
1213
CatalogFilterOptionsList,
1314
CatalogLabelList,
@@ -154,25 +155,7 @@ export const ModelCatalogContextProvider: React.FC<ModelCatalogContextProviderPr
154155
React.useState(false);
155156
const [lastViewedModelName, setLastViewedModelName] = React.useState<string | null>(null);
156157
const [sortBy, setSortBy] = React.useState<ModelCatalogSortOption | null>(null);
157-
const [emptyCategoryLabels, setEmptyCategoryLabels] = React.useState<Set<string>>(
158-
() => new Set<string>(),
159-
);
160-
const emptyCategoryLabelsRef = React.useRef(emptyCategoryLabels);
161-
emptyCategoryLabelsRef.current = emptyCategoryLabels;
162-
163-
const reportCategoryEmpty = React.useCallback((label: string, isEmpty: boolean) => {
164-
const { current } = emptyCategoryLabelsRef;
165-
const hasLabel = current.has(label);
166-
if (isEmpty && !hasLabel) {
167-
const next = new Set(current);
168-
next.add(label);
169-
setEmptyCategoryLabels(next);
170-
} else if (!isEmpty && hasLabel) {
171-
const next = new Set(current);
172-
next.delete(label);
173-
setEmptyCategoryLabels(next);
174-
}
175-
}, []);
158+
const { emptyCategoryLabels, reportCategoryEmpty } = useEmptyCategoryTracking();
176159

177160
const location = useLocation();
178161
const isOnDetailsPage = location.pathname.includes(ModelDetailsTab.PERFORMANCE_INSIGHTS);
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import * as React from 'react';
2+
import { CatalogLabelList, CatalogSourceList } from '~/app/modelCatalogTypes';
3+
import { getActiveSourceLabels } from '~/app/pages/modelCatalog/utils/modelCatalogUtils';
4+
5+
type UseEffectiveCategoriesResult = {
6+
activeCategories: string[];
7+
effectiveActiveCategories: string[];
8+
isSingleCategory: boolean;
9+
hasNoCategories: boolean;
10+
};
11+
12+
const useEffectiveCategories = (
13+
catalogSources: CatalogSourceList | null,
14+
catalogLabels: CatalogLabelList | null,
15+
emptyCategoryLabels: Set<string>,
16+
catalogSourcesLoaded: boolean,
17+
updateSelectedSourceLabel: (label: string | undefined) => void,
18+
): UseEffectiveCategoriesResult => {
19+
const activeCategories = React.useMemo(
20+
() => getActiveSourceLabels(catalogSources, catalogLabels),
21+
[catalogSources, catalogLabels],
22+
);
23+
24+
const effectiveActiveCategories = React.useMemo(
25+
() => activeCategories.filter((c) => !emptyCategoryLabels.has(c)),
26+
[activeCategories, emptyCategoryLabels],
27+
);
28+
29+
const isSingleCategory = effectiveActiveCategories.length === 1;
30+
const hasNoCategories = effectiveActiveCategories.length === 0;
31+
32+
const effectiveCategoriesKey = effectiveActiveCategories.join(',');
33+
34+
React.useEffect(() => {
35+
if (catalogSourcesLoaded && isSingleCategory) {
36+
updateSelectedSourceLabel(effectiveActiveCategories[0]);
37+
}
38+
// eslint-disable-next-line react-hooks/exhaustive-deps
39+
}, [catalogSourcesLoaded, isSingleCategory, effectiveCategoriesKey, updateSelectedSourceLabel]);
40+
41+
return { activeCategories, effectiveActiveCategories, isSingleCategory, hasNoCategories };
42+
};
43+
44+
export default useEffectiveCategories;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import * as React from 'react';
2+
3+
type UseEmptyCategoryTrackingResult = {
4+
emptyCategoryLabels: Set<string>;
5+
reportCategoryEmpty: (label: string, isEmpty: boolean) => void;
6+
};
7+
8+
const useEmptyCategoryTracking = (): UseEmptyCategoryTrackingResult => {
9+
const [emptyCategoryLabels, setEmptyCategoryLabels] = React.useState<Set<string>>(
10+
() => new Set<string>(),
11+
);
12+
const emptyCategoryLabelsRef = React.useRef(emptyCategoryLabels);
13+
emptyCategoryLabelsRef.current = emptyCategoryLabels;
14+
15+
const reportCategoryEmpty = React.useCallback((label: string, isEmpty: boolean) => {
16+
const { current } = emptyCategoryLabelsRef;
17+
const hasLabel = current.has(label);
18+
if (isEmpty && !hasLabel) {
19+
const next = new Set(current);
20+
next.add(label);
21+
setEmptyCategoryLabels(next);
22+
} else if (!isEmpty && hasLabel) {
23+
const next = new Set(current);
24+
next.delete(label);
25+
setEmptyCategoryLabels(next);
26+
}
27+
}, []);
28+
29+
return { emptyCategoryLabels, reportCategoryEmpty };
30+
};
31+
32+
export default useEmptyCategoryTracking;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import * as React from 'react';
2+
3+
const useReportCategoryEmpty = (
4+
reportCategoryEmpty: (label: string, isEmpty: boolean) => void,
5+
label: string,
6+
isLoaded: boolean,
7+
itemCount: number,
8+
searchTerm: string,
9+
): void => {
10+
const reportTimerRef = React.useRef<ReturnType<typeof setTimeout>>();
11+
12+
React.useEffect(() => {
13+
clearTimeout(reportTimerRef.current);
14+
if (isLoaded && !searchTerm) {
15+
reportTimerRef.current = setTimeout(() => {
16+
reportCategoryEmpty(label, itemCount === 0);
17+
}, 100);
18+
}
19+
return () => clearTimeout(reportTimerRef.current);
20+
}, [isLoaded, itemCount, label, searchTerm, reportCategoryEmpty]);
21+
};
22+
23+
export default useReportCategoryEmpty;

clients/ui/frontend/src/app/pages/mcpCatalog/screens/McpCatalog.tsx

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { McpCatalogContext } from '~/app/context/mcpCatalog/McpCatalogContext';
77
import { hasMcpFiltersApplied } from '~/app/pages/mcpCatalog/utils/mcpCatalogUtils';
88
import McpCatalogFilters from '~/app/pages/mcpCatalog/components/McpCatalogFilters';
99
import { MCP_CATALOG_TITLE, MCP_CATALOG_DESCRIPTION } from '~/app/pages/mcpCatalog/const';
10-
import { getActiveSourceLabels } from '~/app/pages/modelCatalog/utils/modelCatalogUtils';
10+
import useEffectiveCategories from '~/app/hooks/useEffectiveCategories';
1111
import EmptyModelCatalogState from '~/app/pages/modelCatalog/EmptyModelCatalogState';
1212
import McpCatalogSourceLabelSelector from './McpCatalogSourceLabelSelector';
1313
import McpCatalogAllServersView from './McpCatalogAllServersView';
@@ -30,25 +30,14 @@ const McpCatalog: React.FC = () => {
3030
const filtersApplied = hasMcpFiltersApplied(filters, searchQuery);
3131
const isAllServersView = selectedSourceLabel === undefined && !filtersApplied;
3232

33-
const activeCategories = React.useMemo(
34-
() => getActiveSourceLabels(catalogSources, catalogLabels),
35-
[catalogSources, catalogLabels],
36-
);
37-
38-
const effectiveActiveCategories = React.useMemo(
39-
() => activeCategories.filter((c) => !emptyCategoryLabels.has(c)),
40-
[activeCategories, emptyCategoryLabels],
33+
const { isSingleCategory, hasNoCategories } = useEffectiveCategories(
34+
catalogSources,
35+
catalogLabels,
36+
emptyCategoryLabels,
37+
catalogSourcesLoaded,
38+
setSelectedSourceLabel,
4139
);
4240

43-
const isSingleCategory = effectiveActiveCategories.length === 1;
44-
const hasNoCategories = effectiveActiveCategories.length === 0;
45-
46-
React.useEffect(() => {
47-
if (catalogSourcesLoaded && isSingleCategory) {
48-
setSelectedSourceLabel(effectiveActiveCategories[0]);
49-
}
50-
}, [catalogSourcesLoaded, isSingleCategory, effectiveActiveCategories, setSelectedSourceLabel]);
51-
5241
const handleSearch = React.useCallback(
5342
(term: string) => {
5443
setSearchQuery(term);

clients/ui/frontend/src/app/pages/mcpCatalog/screens/McpCatalogCategorySection.tsx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
} from '@patternfly/react-core';
1414
import { ArrowRightIcon, SearchIcon } from '@patternfly/react-icons';
1515
import { useMcpServersBySourceLabelWithAPI } from '~/app/hooks/mcpServerCatalog/useMcpServersBySourceLabel';
16+
import useReportCategoryEmpty from '~/app/hooks/useReportCategoryEmpty';
1617
import {
1718
getLabelDescription,
1819
getLabelDisplayName,
@@ -58,16 +59,13 @@ const McpCatalogCategorySection: React.FC<McpCatalogCategorySectionProps> = ({
5859
);
5960
const description = getLabelDescription(label, catalogLabels);
6061

61-
const reportTimerRef = React.useRef<ReturnType<typeof setTimeout>>();
62-
React.useEffect(() => {
63-
clearTimeout(reportTimerRef.current);
64-
if (mcpServersLoaded && !searchTerm) {
65-
reportTimerRef.current = setTimeout(() => {
66-
reportCategoryEmpty(label, mcpServers.items.length === 0);
67-
}, 100);
68-
}
69-
return () => clearTimeout(reportTimerRef.current);
70-
}, [mcpServersLoaded, mcpServers.items.length, label, searchTerm, reportCategoryEmpty]);
62+
useReportCategoryEmpty(
63+
reportCategoryEmpty,
64+
label,
65+
mcpServersLoaded,
66+
mcpServers.items.length,
67+
searchTerm,
68+
);
7169

7270
if (mcpServersLoaded && mcpServers.items.length === 0 && !searchTerm) {
7371
return null;

clients/ui/frontend/src/app/pages/modelCatalog/screens/CatalogCategorySection.tsx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import React from 'react';
1414
import { ArrowRightIcon, SearchIcon } from '@patternfly/react-icons';
1515
import { CatalogSourceList } from '~/app/modelCatalogTypes';
1616
import { useCatalogModelsBySources } from '~/app/hooks/modelCatalog/useCatalogModelsBySource';
17+
import useReportCategoryEmpty from '~/app/hooks/useReportCategoryEmpty';
1718
import EmptyModelCatalogState from '~/app/pages/modelCatalog/EmptyModelCatalogState';
1819
import {
1920
getLabelDescription,
@@ -51,16 +52,13 @@ const CatalogCategorySection: React.FC<CategorySectionProps> = ({
5152
const categoryTitle = getLabelDisplayName(label, catalogLabels);
5253
const description = getLabelDescription(label, catalogLabels);
5354

54-
const reportTimerRef = React.useRef<ReturnType<typeof setTimeout>>();
55-
React.useEffect(() => {
56-
clearTimeout(reportTimerRef.current);
57-
if (catalogModelsLoaded && !searchTerm) {
58-
reportTimerRef.current = setTimeout(() => {
59-
reportCategoryEmpty(label, catalogModels.items.length === 0);
60-
}, 100);
61-
}
62-
return () => clearTimeout(reportTimerRef.current);
63-
}, [catalogModelsLoaded, catalogModels.items.length, label, searchTerm, reportCategoryEmpty]);
55+
useReportCategoryEmpty(
56+
reportCategoryEmpty,
57+
label,
58+
catalogModelsLoaded,
59+
catalogModels.items.length,
60+
searchTerm,
61+
);
6462

6563
if (catalogModelsLoaded && catalogModels.items.length === 0 && !searchTerm) {
6664
return null;

clients/ui/frontend/src/app/pages/modelCatalog/screens/ModelCatalog.tsx

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import ModelCatalogFilters from '~/app/pages/modelCatalog/components/ModelCatalo
77
import { ModelCatalogContext } from '~/app/context/modelCatalog/ModelCatalogContext';
88
import { CategoryName } from '~/app/modelCatalogTypes';
99
import { useHasVisibleFiltersApplied } from '~/app/hooks/modelCatalog/useHasVisibleFiltersApplied';
10-
import { getActiveSourceLabels } from '~/app/pages/modelCatalog/utils/modelCatalogUtils';
10+
import useEffectiveCategories from '~/app/hooks/useEffectiveCategories';
1111
import EmptyModelCatalogState from '~/app/pages/modelCatalog/EmptyModelCatalogState';
1212
import ModelCatalogSourceLabelSelectorNavigator from './ModelCatalogSourceLabelSelectorNavigator';
1313
import ModelCatalogAllModelsView from './ModelCatalogAllModelsView';
@@ -26,29 +26,13 @@ const ModelCatalog: React.FC = () => {
2626
} = React.useContext(ModelCatalogContext);
2727
const filtersApplied = useHasVisibleFiltersApplied();
2828

29-
const activeCategories = React.useMemo(
30-
() => getActiveSourceLabels(catalogSources, catalogLabels),
31-
[catalogSources, catalogLabels],
32-
);
33-
34-
const effectiveActiveCategories = React.useMemo(
35-
() => activeCategories.filter((c) => !emptyCategoryLabels.has(c)),
36-
[activeCategories, emptyCategoryLabels],
37-
);
38-
39-
const isSingleCategory = effectiveActiveCategories.length === 1;
40-
const hasNoCategories = effectiveActiveCategories.length === 0;
41-
42-
React.useEffect(() => {
43-
if (catalogSourcesLoaded && isSingleCategory) {
44-
updateSelectedSourceLabel(effectiveActiveCategories[0]);
45-
}
46-
}, [
29+
const { isSingleCategory, hasNoCategories } = useEffectiveCategories(
30+
catalogSources,
31+
catalogLabels,
32+
emptyCategoryLabels,
4733
catalogSourcesLoaded,
48-
isSingleCategory,
49-
effectiveActiveCategories,
5034
updateSelectedSourceLabel,
51-
]);
35+
);
5236

5337
const isAllModelsView =
5438
selectedSourceLabel === CategoryName.allModels && !searchTerm && !filtersApplied;

0 commit comments

Comments
 (0)