go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/analysis/frontend/ui/src/components/rules_table/rules_table.tsx (about) 1 // Copyright 2022 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 Box from '@mui/material/Box'; 16 import LinearProgress from '@mui/material/LinearProgress'; 17 import Table from '@mui/material/Table'; 18 import TableBody from '@mui/material/TableBody'; 19 import TableCell from '@mui/material/TableCell'; 20 import TableContainer from '@mui/material/TableContainer'; 21 import TableHead from '@mui/material/TableHead'; 22 import TableRow from '@mui/material/TableRow'; 23 import FormControl from '@mui/material/FormControl'; 24 import InputLabel from '@mui/material/InputLabel'; 25 import MenuItem from '@mui/material/MenuItem'; 26 import Select, { SelectChangeEvent } from '@mui/material/Select'; 27 28 import LoadErrorAlert from '@/components/load_error_alert/load_error_alert'; 29 import { useFetchProjectConfig } from '@/hooks/use_fetch_project_config'; 30 import useFetchRules from '@/hooks/use_fetch_rules'; 31 32 import { useProblemFilterParam } from './hooks'; 33 import RuleRow from './rule_row/rule_row'; 34 import ProblemChip from './problem_chip/problem_chip'; 35 36 interface Props { 37 project: string; 38 } 39 40 const RulesTable = ({ project } : Props ) => { 41 const { 42 isLoading: isConfigLoading, 43 data: projectConfig, 44 error: configError, 45 } = useFetchProjectConfig(project); 46 47 const { isLoading, data: rules, error } = useFetchRules(project); 48 49 const policyIDs = projectConfig?.bugManagement?.policies?.map((p) => p.id) || []; 50 const [problemFilter, setProblemFilter] = useProblemFilterParam(policyIDs); 51 52 if (error) { 53 return <LoadErrorAlert 54 entityName='rules' 55 error={error} 56 />; 57 } 58 if (configError) { 59 return <LoadErrorAlert 60 entityName='project config' 61 error={configError} 62 />; 63 } 64 if (isLoading || isConfigLoading) { 65 return <LinearProgress />; 66 } 67 68 69 const handleProblemFilterChange = (event: SelectChangeEvent<string>) => { 70 const value = event.target.value; 71 setProblemFilter(value, true); 72 }; 73 74 const colorIndexFunc = (policyID: string): number => { 75 return policyIDs.indexOf(policyID); 76 }; 77 78 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 79 const filteredRules = (rules || []).filter((r) => problemFilter == '' || r.bugManagementState!.policyState?.some((ps) => ps.policyId == problemFilter && ps.lastActivationTime)); 80 81 return ( 82 <> 83 <Box sx={{ paddingTop: '8px', paddingBottom: '12px' }}> 84 <FormControl fullWidth data-testid="problem_filter"> 85 <InputLabel id="problem_filter_label">Problem filter</InputLabel> 86 <Select 87 labelId="problem_filter_label" 88 id="problem_filter" 89 label="Problem filter" 90 value={problemFilter} 91 onChange={handleProblemFilterChange} 92 inputProps={{ 'data-testid': 'problem_filter_input' }}> 93 <MenuItem value="">All rules</MenuItem> 94 {projectConfig?.bugManagement?.policies?.map((policy) => ( 95 <MenuItem 96 key={policy.id} 97 value={policy.id}> 98 Only rules with problem 99 <ProblemChip 100 key={policy.id} 101 policy={policy} 102 active 103 colorIndex={colorIndexFunc(policy.id)} 104 /> / {policy.humanReadableName} 105 </MenuItem> 106 ))} 107 </Select> 108 </FormControl> 109 </Box> 110 <TableContainer component={Box}> 111 <Table data-testid="impact-table" size="small" sx={{ overflowWrap: 'anywhere' }}> 112 <TableHead> 113 <TableRow> 114 <TableCell>Rule Definition</TableCell> 115 <TableCell>Problems</TableCell> 116 <TableCell sx={{ width: '180px' }}>Bug</TableCell> 117 <TableCell sx={{ width: '100px' }}>Last Updated</TableCell> 118 </TableRow> 119 </TableHead> 120 <TableBody> 121 { 122 filteredRules.map((rule) => ( 123 <RuleRow 124 key={rule.ruleId} 125 rule={rule} 126 bugManagementConfig={projectConfig?.bugManagement} 127 focusPolicyID={problemFilter} 128 colorIndexFunc={colorIndexFunc} /> 129 )) 130 } 131 </TableBody> 132 </Table> 133 </TableContainer> 134 </> 135 ); 136 }; 137 138 export default RulesTable;