github.com/thanos-io/thanos@v0.32.5/pkg/ui/react-app/src/pages/rules/RulesContent.tsx (about) 1 import React, { FC } from 'react'; 2 import { RouteComponentProps } from '@reach/router'; 3 import { APIResponse } from '../../hooks/useFetch'; 4 import { UncontrolledAlert, Table, Badge } from 'reactstrap'; 5 import { formatRelative, createExternalExpressionLink, humanizeDuration, formatDuration } from '../../utils'; 6 import { Rule } from '../../types/types'; 7 import { now } from 'moment'; 8 9 interface RulesContentProps { 10 response: APIResponse<RulesMap>; 11 } 12 13 interface RuleGroup { 14 name: string; 15 file: string; 16 rules: Rule[]; 17 evaluationTime: string; 18 lastEvaluation: string; 19 } 20 21 export interface RulesMap { 22 groups: RuleGroup[]; 23 } 24 25 const GraphExpressionLink: FC<{ expr: string; text: string; title: string }> = (props) => { 26 return ( 27 <> 28 <strong>{props.title}:</strong> 29 <a className="ml-4" href={createExternalExpressionLink(props.expr)}> 30 {props.text} 31 </a> 32 <br /> 33 </> 34 ); 35 }; 36 37 export const RulesContent: FC<RouteComponentProps & RulesContentProps> = ({ response }) => { 38 const getBadgeColor = (state: string) => { 39 switch (state) { 40 case 'ok': 41 return 'success'; 42 43 case 'err': 44 return 'danger'; 45 46 case 'unknown': 47 return 'warning'; 48 } 49 }; 50 51 if (response.data) { 52 const groups: RuleGroup[] = response.data.groups; 53 return ( 54 <> 55 <h2>Rules</h2> 56 {groups.map((g, i) => { 57 return ( 58 <Table bordered key={i}> 59 <thead> 60 <tr> 61 <td colSpan={3}> 62 <a href={'#' + g.name}> 63 <h2 id={g.name}>{g.name}</h2> 64 </a> 65 </td> 66 <td> 67 <h2>{formatRelative(g.lastEvaluation, now())} ago</h2> 68 </td> 69 <td> 70 <h2>{humanizeDuration(parseFloat(g.evaluationTime) * 1000)}</h2> 71 </td> 72 </tr> 73 </thead> 74 <tbody> 75 <tr className="font-weight-bold"> 76 <td>Rule</td> 77 <td>State</td> 78 <td>Error</td> 79 <td>Last Evaluation</td> 80 <td>Evaluation Time</td> 81 </tr> 82 {g.rules.map((r, i) => { 83 return ( 84 <tr key={i}> 85 {r.alerts ? ( 86 <td className="rule-cell"> 87 <GraphExpressionLink title="alert" text={r.name} expr={`ALERTS{alertname="${r.name}"}`} /> 88 <GraphExpressionLink title="expr" text={r.query} expr={r.query} /> 89 {r.duration > 0 && ( 90 <div> 91 <strong>for:</strong> {formatDuration(r.duration * 1000)} 92 </div> 93 )} 94 95 <div> 96 <strong>labels:</strong> 97 {Object.entries(r.labels).map(([key, value]) => ( 98 <div className="ml-4" key={key}> 99 {key}: {value} 100 </div> 101 ))} 102 </div> 103 <div> 104 <strong>annotations:</strong> 105 {Object.entries(r.annotations).map(([key, value]) => ( 106 <div className="ml-4" key={key}> 107 {key}: {value} 108 </div> 109 ))} 110 </div> 111 </td> 112 ) : ( 113 <td> 114 <GraphExpressionLink title="record" text={r.name} expr={r.name} /> 115 <GraphExpressionLink title="expr" text={r.query} expr={r.query} /> 116 </td> 117 )} 118 <td> 119 <Badge color={getBadgeColor(r.health)}>{r.health.toUpperCase()}</Badge> 120 </td> 121 <td>{r.lastError ? <UncontrolledAlert color="danger">{r.lastError}</UncontrolledAlert> : null}</td> 122 <td>{formatRelative(r.lastEvaluation, now())} ago</td> 123 <td>{humanizeDuration(parseFloat(r.evaluationTime) * 1000)}</td> 124 </tr> 125 ); 126 })} 127 </tbody> 128 </Table> 129 ); 130 })} 131 </> 132 ); 133 } 134 135 return null; 136 };