go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/analysis/frontend/ui/src/components/rule/rule_info/rule_info.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 { useState } from 'react'; 16 import { Link as RouterLink } from 'react-router-dom'; 17 18 import Archive from '@mui/icons-material/Archive'; 19 import Unarchive from '@mui/icons-material/Unarchive'; 20 import LoadingButton from '@mui/lab/LoadingButton'; 21 import Box from '@mui/material/Box'; 22 import Container from '@mui/material/Container'; 23 import Grid from '@mui/material/Grid'; 24 import Link from '@mui/material/Link'; 25 import Paper from '@mui/material/Paper'; 26 import PanelHeading from '@/components/headings/panel_heading/panel_heading'; 27 28 import ConfirmDialog from '@/components/confirm_dialog/confirm_dialog'; 29 import GridLabel from '@/components/grid_label/grid_label'; 30 import HelpTooltip from '@/components/help_tooltip/help_tooltip'; 31 import RuleEditDialog from '@/components/rule/rule_edit_dialog/rule_edit_dialog'; 32 import { useMutateRule } from '@/hooks/use_mutate_rule'; 33 import { Rule, UpdateRuleRequest } from '@/proto/go.chromium.org/luci/analysis/proto/v1/rules.pb'; 34 import { linkToCluster } from '@/tools/urlHandling/links'; 35 import RuleDefinition from '../rule_definition/rule_definition'; 36 37 const definitionTooltipText = 'The failures matched by this rule.'; 38 const archivedTooltipText = 'Archived failure association rules do not match failures. If a rule is no longer needed, it should be archived.'; 39 const sourceClusterTooltipText = 'The cluster this rule was originally created from.'; 40 41 interface Props { 42 project: string; 43 rule: Rule; 44 } 45 46 const RuleInfo = ({ project, rule }: Props) => { 47 const [editDialogOpen, setEditDialogOpen] = useState(false); 48 const [confirmDialogOpen, setConfirmDialogOpen] = useState(false); 49 50 const mutateRule = useMutateRule(); 51 52 const toggleArchived = () => { 53 const request: UpdateRuleRequest = UpdateRuleRequest.create({ 54 rule: { 55 name: rule.name, 56 isActive: !rule.isActive, 57 }, 58 updateMask: Object.freeze(['isActive']), 59 etag: rule.etag, 60 }); 61 mutateRule.mutate(request); 62 }; 63 64 const onArchiveConfirm = () => { 65 setConfirmDialogOpen(false); 66 toggleArchived(); 67 }; 68 69 const onArchiveCancel = () => { 70 setConfirmDialogOpen(false); 71 }; 72 73 return ( 74 <Paper data-cy="rule-info" elevation={3} sx={{ pt: 2, pb: 2, mt: 1 }} > 75 <Container maxWidth={false}> 76 <PanelHeading> 77 Rule Details 78 </PanelHeading> 79 <Grid container rowGap={1}> 80 <GridLabel text="Rule definition"> 81 <HelpTooltip text={definitionTooltipText} /> 82 </GridLabel> 83 <Grid item xs={10} alignItems="center"> 84 <RuleDefinition definition={rule.ruleDefinition} onEditClicked={() => setEditDialogOpen(true)} /> 85 </Grid> 86 <GridLabel text="Source cluster"> 87 <HelpTooltip text={sourceClusterTooltipText} /> 88 </GridLabel> 89 <Grid item xs={10} alignItems="center"> 90 <Box sx={{ display: 'inline-block' }} paddingTop={1}> 91 { 92 rule.sourceCluster?.algorithm && rule.sourceCluster?.id ? ( 93 <Link aria-label='source cluster link' component={RouterLink} to={linkToCluster(project, rule.sourceCluster)}> 94 {rule.sourceCluster.algorithm}/{rule.sourceCluster.id} 95 </Link> 96 ) : ( 97 'None' 98 ) 99 } 100 </Box> 101 </Grid> 102 <GridLabel text="Archived"> 103 <HelpTooltip text={archivedTooltipText} /> 104 </GridLabel> 105 <Grid item xs={10} alignItems="center" columnGap={1}> 106 <Box data-testid="rule-archived" sx={{ display: 'inline-block' }} paddingTop={1} paddingRight={1}> 107 {rule.isActive ? 'No' : 'Yes'} 108 </Box> 109 <LoadingButton 110 data-testid="rule-archived-toggle" 111 loading={mutateRule.isLoading} 112 variant="outlined" 113 startIcon={rule.isActive ? (<Archive />) : (<Unarchive />)} 114 onClick={() => setConfirmDialogOpen(true)}> 115 {rule.isActive ? 'Archive' : 'Restore'} 116 </LoadingButton> 117 </Grid> 118 </Grid> 119 </Container> 120 <ConfirmDialog 121 open={confirmDialogOpen} 122 message={ 123 rule.isActive? 124 'Impact and recent failures are not available for archived rules.'+ 125 ' Automatic bug priority updates and auto-closure will also cease.'+ 126 ' You can restore archived rules at any time.' : 127 'LUCI Analysis automatically archives rules when the associated'+ 128 ' bug has been closed for 30 days. Please make sure the associated'+ 129 ' bug is no longer closed to avoid this rule being automatically'+ 130 ' re-archived.' 131 } 132 onConfirm={onArchiveConfirm} 133 onCancel={onArchiveCancel}/> 134 <RuleEditDialog 135 open={editDialogOpen} 136 setOpen={setEditDialogOpen} 137 rule={rule}/> 138 </Paper> 139 ); 140 }; 141 142 export default RuleInfo;