Skip to content

Commit 8d74b8c

Browse files
authored
Fix Minor Issues (#2104)
1 parent ce2306a commit 8d74b8c

3 files changed

Lines changed: 120 additions & 23 deletions

File tree

workspaces/redhat-resource-optimization/plugins/redhat-resource-optimization/src/hooks/useOpenShiftFilters.ts

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
1818
import { useLocation, useNavigate } from 'react-router-dom';
1919
import { useDebouncedCallbackWithAbort } from './useDebouncedCallbackWithAbort';
2020

21+
/**
22+
* Filter operation types
23+
*/
24+
export type FilterOperation = 'includes' | 'excludes' | 'exact';
25+
2126
/**
2227
* OpenShift filter state
2328
*/
@@ -27,6 +32,7 @@ export interface OpenShiftFilters {
2732
timeRange: string;
2833
currency: string;
2934
filterBy: string;
35+
filterOperation: FilterOperation;
3036
filterValue: string;
3137
currentPage: number;
3238
pageSize: number;
@@ -70,6 +76,7 @@ const DEFAULT_FILTERS: OpenShiftFilters = {
7076
timeRange: 'month-to-date',
7177
currency: 'USD',
7278
filterBy: 'project',
79+
filterOperation: 'includes',
7380
filterValue: '',
7481
currentPage: 0,
7582
pageSize: 5,
@@ -90,16 +97,44 @@ function parseFiltersFromUrl(search: string): Partial<OpenShiftFilters> {
9097
}
9198
}
9299

93-
// Extract filterBy and filterValue
100+
// Extract filterBy, filterOperation, and filterValue
101+
// Check for exact filter first (filter[exact:project], filter[exact:cluster], etc.)
94102
for (const key of ['project', 'cluster', 'node']) {
95-
const filterVal = params.get(`filter[${key}]`);
96-
if (filterVal) {
103+
const exactFilterVal = params.get(`filter[exact:${key}]`);
104+
if (exactFilterVal) {
97105
filters.filterBy = key;
98-
filters.filterValue = filterVal;
106+
filters.filterOperation = 'exact';
107+
filters.filterValue = exactFilterVal;
99108
break;
100109
}
101110
}
102111

112+
// Check for exclude filter (exclude[project], exclude[cluster], etc.)
113+
if (!filters.filterValue) {
114+
for (const key of ['project', 'cluster', 'node']) {
115+
const excludeFilterVal = params.get(`exclude[${key}]`);
116+
if (excludeFilterVal) {
117+
filters.filterBy = key;
118+
filters.filterOperation = 'excludes';
119+
filters.filterValue = excludeFilterVal;
120+
break;
121+
}
122+
}
123+
}
124+
125+
// Check for regular filter (filter[project], filter[cluster], etc.)
126+
if (!filters.filterValue) {
127+
for (const key of ['project', 'cluster', 'node']) {
128+
const filterVal = params.get(`filter[${key}]`);
129+
if (filterVal) {
130+
filters.filterBy = key;
131+
filters.filterOperation = 'includes';
132+
filters.filterValue = filterVal;
133+
break;
134+
}
135+
}
136+
}
137+
103138
// Determine overheadDistribution from order_by
104139
const hasDistributedCost = params.has('order_by[distributed_cost]');
105140
filters.overheadDistribution = hasDistributedCost
@@ -162,7 +197,15 @@ function buildUrlFromFilters(filters: OpenShiftFilters): string {
162197

163198
// Add filter parameter if filterValue is set
164199
if (filters.filterValue && filters.filterBy) {
165-
params.set(`filter[${filters.filterBy}]`, filters.filterValue);
200+
const operation = filters.filterOperation || 'includes';
201+
if (operation === 'exact') {
202+
params.set(`filter[exact:${filters.filterBy}]`, filters.filterValue);
203+
} else if (operation === 'excludes') {
204+
params.set(`exclude[${filters.filterBy}]`, filters.filterValue);
205+
} else {
206+
// 'includes' - default behavior
207+
params.set(`filter[${filters.filterBy}]`, filters.filterValue);
208+
}
166209
}
167210

168211
return params.toString();

workspaces/redhat-resource-optimization/plugins/redhat-resource-optimization/src/pages/openshift/OpenShiftPage.tsx

Lines changed: 71 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ const formatCurrency = (value: number, currencyCode: string): string => {
4646
interface ProjectCost {
4747
id: string;
4848
projectName: string;
49+
clusterId?: string;
4950
cost: number;
5051
costPercentage: number;
5152
monthOverMonthChange: number;
@@ -181,12 +182,22 @@ function buildCostManagementQueryParams(
181182
queryParams[`filter[${groupBy}]`] = specificItemName;
182183
} else if (filterBy === 'tag' && selectedTagKey && selectedTagValue) {
183184
// Tag filtering
184-
const filterPrefix = filterOperation === 'excludes' ? 'exclude' : 'filter';
185-
queryParams[`${filterPrefix}[tag:${selectedTagKey}]`] = selectedTagValue;
185+
if (filterOperation === 'exact') {
186+
queryParams[`filter[exact:tag:${selectedTagKey}]`] = selectedTagValue;
187+
} else if (filterOperation === 'excludes') {
188+
queryParams[`exclude[tag:${selectedTagKey}]`] = selectedTagValue;
189+
} else {
190+
queryParams[`filter[tag:${selectedTagKey}]`] = selectedTagValue;
191+
}
186192
} else if (filterValue) {
187193
// Regular filtering
188-
const filterPrefix = filterOperation === 'excludes' ? 'exclude' : 'filter';
189-
queryParams[`${filterPrefix}[${filterBy}]`] = filterValue;
194+
if (filterOperation === 'exact') {
195+
queryParams[`filter[exact:${filterBy}]`] = filterValue;
196+
} else if (filterOperation === 'excludes') {
197+
queryParams[`exclude[${filterBy}]`] = filterValue;
198+
} else {
199+
queryParams[`filter[${filterBy}]`] = filterValue;
200+
}
190201
}
191202

192203
// Add sorting
@@ -380,8 +391,24 @@ export function OpenShiftPage() {
380391
delta_value?: number;
381392
}) || {};
382393

383-
const nameField = groupBy as keyof typeof item;
384-
const itemName = (item[nameField] as string) || 'Unknown';
394+
// For clusters, prefer values[0].clusters[0] (human-readable) over item.cluster (UUID)
395+
let itemName: string;
396+
let clusterId: string | undefined;
397+
if (groupBy === 'cluster') {
398+
const valueItem = item.values?.[0] as
399+
| { clusters?: string[]; cluster?: string }
400+
| undefined;
401+
const clustersArray = valueItem?.clusters;
402+
itemName =
403+
clustersArray && clustersArray.length > 0
404+
? clustersArray[0]
405+
: (item.cluster as string) || 'Unknown';
406+
// Store the cluster UUID for display
407+
clusterId = (item.cluster as string) || undefined;
408+
} else {
409+
const nameField = groupBy as keyof typeof item;
410+
itemName = (item[nameField] as string) || 'Unknown';
411+
}
385412

386413
const costField =
387414
groupBy === 'project' && overheadDistribution === 'distribute'
@@ -399,6 +426,7 @@ export function OpenShiftPage() {
399426
return {
400427
id: `${index}`,
401428
projectName: itemName,
429+
clusterId,
402430
cost: costValue,
403431
costPercentage: 0,
404432
monthOverMonthChange: deltaPercent,
@@ -647,21 +675,41 @@ export function OpenShiftPage() {
647675
</Typography>
648676
),
649677
field: 'projectName',
678+
cellStyle: { minWidth: 350 },
679+
headerStyle: { minWidth: 350 },
650680
render: data => (
651-
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
652-
<Typography variant="body2">{data.projectName}</Typography>
653-
{data.includesOverhead && (
681+
<div>
682+
<div
683+
style={{
684+
display: 'flex',
685+
alignItems: 'center',
686+
justifyContent: 'space-between',
687+
gap: '8px',
688+
}}
689+
>
690+
<Typography variant="body2">{data.projectName}</Typography>
691+
{groupBy === 'project' && data.includesOverhead && (
692+
<Typography
693+
variant="caption"
694+
style={{
695+
padding: '2px 6px',
696+
backgroundColor: '#F5F5F5',
697+
border: '1px solid #D2D2D2',
698+
borderRadius: '16px',
699+
color: 'black',
700+
whiteSpace: 'nowrap',
701+
}}
702+
>
703+
Includes overhead
704+
</Typography>
705+
)}
706+
</div>
707+
{groupBy === 'cluster' && data.clusterId && (
654708
<Typography
655709
variant="caption"
656-
style={{
657-
padding: '2px 6px',
658-
backgroundColor: '#F5F5F5',
659-
border: '1px solid #D2D2D2',
660-
borderRadius: '16px',
661-
color: 'black',
662-
}}
710+
style={{ color: '#666', display: 'block', marginTop: 10 }}
663711
>
664-
Includes overhead
712+
{data.clusterId}
665713
</Typography>
666714
)}
667715
</div>
@@ -858,6 +906,12 @@ export function OpenShiftPage() {
858906
if (value !== 'project') {
859907
setShowPlatformSum(false);
860908
}
909+
// Sync filterBy with groupBy and reset filter value
910+
setFilterBy(value);
911+
setFilterValue('');
912+
// Reset tag-related filter fields
913+
setSelectedTagKey('');
914+
setSelectedTagValue('');
861915
}}
862916
selectedTag={selectedTag}
863917
onSelectedTagChange={value => {

workspaces/redhat-resource-optimization/plugins/redhat-resource-optimization/src/pages/openshift/components/Filters.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ export function Filters(props: FiltersProps) {
390390
handleHomeEndKeys
391391
filterSelectedOptions
392392
label=""
393-
options={['includes', 'excludes']}
393+
options={['includes', 'excludes', 'exact']}
394394
value={filterOperation}
395395
placeholder=""
396396
onChange={(event): void => {

0 commit comments

Comments
 (0)