go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/analysis/frontend/ui/src/components/clusters_table/hooks.tsx (about) 1 // Copyright 2023 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 import { 16 ParamKeyValuePair, 17 useSearchParams, 18 } from 'react-router-dom'; 19 20 import { TimeInterval } from '@/hooks/use_fetch_clusters'; 21 import { ProjectMetric } from '@/proto/go.chromium.org/luci/analysis/proto/v1/metrics.pb'; 22 import { MetricId } from '@/types/metric_id'; 23 24 export interface OrderBy { 25 metric: MetricId, 26 isAscending: boolean, 27 } 28 29 export function useFilterParam(): [string, (failureFilter: string, replace?: boolean) => void] { 30 const [searchParams, setSearchParams] = useSearchParams(); 31 const failureFilter = searchParams.get('q') || ''; 32 33 function updateFailureFilterParam(failureFilter: string, replace = false) { 34 const params: ParamKeyValuePair[] = []; 35 36 for (const [k, v] of searchParams.entries()) { 37 if (k !== 'q') { 38 params.push([k, v]); 39 } 40 } 41 42 if (failureFilter !== '') { 43 params.push(['q', failureFilter]); 44 } 45 setSearchParams(params, { 46 replace, 47 }); 48 } 49 50 return [failureFilter, updateFailureFilterParam]; 51 } 52 53 export function useIntervalParam(intervals: TimeInterval[]): [TimeInterval | undefined, (selectedInterval: TimeInterval, replace?: boolean) => void] { 54 const [searchParams, setSearchParams] = useSearchParams(); 55 const intervalParam = searchParams.get('interval') || ''; 56 let interval: TimeInterval | undefined = undefined; 57 if (intervalParam) { 58 interval = intervals.find((option) => option.id === intervalParam); 59 } 60 61 function updateIntervalParam(selectedInterval: TimeInterval, replace = false) { 62 const params: ParamKeyValuePair[] = []; 63 64 for (const [k, v] of searchParams.entries()) { 65 if (k !== 'interval') { 66 params.push([k, v]); 67 } 68 } 69 70 params.push(['interval', selectedInterval.id]); 71 72 setSearchParams(params, { 73 replace, 74 }); 75 } 76 77 return [interval, updateIntervalParam]; 78 } 79 80 export function useOrderByParam(metrics: ProjectMetric[]): [OrderBy | undefined, (orderBy: OrderBy, replace?: boolean) => void] { 81 const [searchParams, setSearchParams] = useSearchParams(); 82 const orderByParam = searchParams.get('orderBy') || ''; 83 const orderDir = searchParams.get('orderDir') || ''; 84 85 let orderBy: OrderBy | undefined = undefined; 86 87 if (orderByParam) { 88 // Ensure the metric we are being asked to order by 89 // is one of the metrics we are querying. 90 if (metrics.some((metric) => metric.metricId == orderByParam)) { 91 orderBy = { 92 metric: orderByParam, 93 isAscending: orderDir === 'asc', 94 }; 95 } 96 } 97 98 function updateOrderByParams(orderBy: OrderBy, replace = false) { 99 const params: ParamKeyValuePair[] = []; 100 101 for (const [k, v] of searchParams.entries()) { 102 if (k !== 'orderBy' && k !== 'orderDir') { 103 params.push([k, v]); 104 } 105 } 106 if (orderBy) { 107 params.push(['orderBy', orderBy.metric]); 108 if (orderBy.isAscending) { 109 params.push(['orderDir', 'asc']); 110 } 111 } 112 setSearchParams(params, { 113 replace, 114 }); 115 } 116 117 return [orderBy, updateOrderByParams]; 118 } 119 120 export function useSelectedMetricsParam(metrics: ProjectMetric[]): [ProjectMetric[], (selectedMetrics: ProjectMetric[], replace?: boolean) => void] { 121 const [searchParams, setSearchParams] = useSearchParams(); 122 const selectedMetricsParam = searchParams.get('selectedMetrics') || ''; 123 const selectedMetricsIds = selectedMetricsParam.split(','); 124 125 const selectedMetrics = metrics.filter((metric) => selectedMetricsIds.indexOf(metric.metricId) > -1); 126 127 function updateSelectedMetricsParam(selectedMetrics: ProjectMetric[], replace = false) { 128 const params: ParamKeyValuePair[] = []; 129 130 const selectedMetricsIds = selectedMetrics.map((metric) => metric.metricId).join(','); 131 params.push(['selectedMetrics', selectedMetricsIds]); 132 133 const orderByParam = searchParams.get('orderBy'); 134 let addedOrderBy = false; 135 if (selectedMetrics.findIndex((m) => m.metricId === orderByParam) < 0) { 136 let orderByValue = ''; 137 if (selectedMetrics.length > 0) { 138 let highestMetric = selectedMetrics[0]; 139 selectedMetrics.forEach((m) => { 140 if (m.sortPriority > highestMetric.sortPriority) { 141 highestMetric = m; 142 } 143 }); 144 orderByValue = highestMetric.metricId; 145 } 146 params.push(['orderBy', orderByValue]); 147 params.push(['orderDir', 'desc']); 148 addedOrderBy = true; 149 } 150 151 for (const [k, v] of searchParams.entries()) { 152 if (((k === 'orderBy' || k === 'orderDir') && addedOrderBy) || 153 k === 'selectedMetrics') { 154 continue; 155 } 156 params.push([k, v]); 157 } 158 setSearchParams(params, { 159 replace, 160 }); 161 } 162 163 return [selectedMetrics, updateSelectedMetricsParam]; 164 }