2121 * @property {number } lookback_end
2222 * @property {string? } project_code
2323 * @property {string? } table_group_id
24+ *
25+ * @typedef SummaryOptions
26+ * @type {object }
27+ * @property {function(string)? } onTagClick
28+ * @property {object? } activeTypes
2429 */
25- import { emitEvent } from '../utils.js' ;
30+ import { emitEvent , getValue , loadStylesheet } from '../utils.js' ;
2631import { formatDuration , humanReadableDuration } from '../display_utils.js' ;
2732import { withTooltip } from './tooltip.js' ;
2833import van from '../van.min.js' ;
@@ -31,49 +36,63 @@ const { a, div, i, span } = van.tags;
3136
3237/**
3338 * @param {MonitorSummary } summary
34- * @param {any? } topLabel
39+ * @param {string? } label
40+ * @param {SummaryOptions? } options
3541 */
36- const AnomaliesSummary = ( summary , label = 'Anomalies' ) => {
42+ const AnomaliesSummary = ( summary , label = 'Anomalies' , options = { } ) => {
43+ loadStylesheet ( 'anomalies-summary' , summaryStylesheet ) ;
44+
3745 if ( ! summary . lookback ) {
3846 return span ( { class : 'text-secondary mt-3 mb-2' } , 'No monitor runs yet' ) ;
3947 }
4048
41- const SummaryTag = ( label , value , hasErrors , isTraining , isPending ) => div (
42- { class : 'flex-row fx-gap-1' } ,
43- div (
44- { class : `flex-row fx-justify-center anomaly-tag ${ value > 0 ? 'has-anomalies' : hasErrors ? 'has-errors' : isTraining ? 'is-training' : isPending ? 'is-pending' : '' } ` } ,
45- value > 0
46- ? value
47- : hasErrors
48- ? withTooltip (
49- i ( { class : 'material-symbols-rounded' } , 'warning' ) ,
50- { text : 'Execution error' , position : 'top-right' } ,
51- )
52- : isTraining
49+ const SummaryTag = ( typeKey , tagLabel , value , hasErrors , isTraining , isPending ) => {
50+ const isClickable = ! ! options . onTagClick ;
51+ const isActive = van . derive ( ( ) => ( getValue ( options . activeTypes ) ?? [ ] ) . includes ( typeKey ) ) ;
52+
53+ return div (
54+ {
55+ class : ( ) => `flex-row fx-gap-1 p-1 border-radius-1 summary-tag ${ isClickable ? 'clickable' : '' } ${ isActive . val ? 'active' : '' } ` ,
56+ onclick : isClickable ? ( event ) => {
57+ event . stopPropagation ( ) ;
58+ options . onTagClick ( typeKey ) ;
59+ } : undefined ,
60+ } ,
61+ div (
62+ { class : `flex-row fx-justify-center anomaly-tag ${ value > 0 ? 'has-anomalies' : hasErrors ? 'has-errors' : isTraining ? 'is-training' : isPending ? 'is-pending' : '' } ` } ,
63+ value > 0
64+ ? value
65+ : hasErrors
5366 ? withTooltip (
54- i ( { class : 'material-symbols-rounded' } , 'more_horiz ' ) ,
55- { text : 'Training model ' , position : 'top-right' } ,
67+ i ( { class : 'material-symbols-rounded' } , 'warning ' ) ,
68+ { text : 'Execution error ' , position : 'top-right' } ,
5669 )
57- : isPending
70+ : isTraining
5871 ? withTooltip (
59- span ( { class : 'pl-2 pr-2' , style : 'position: relative;' } , '- ' ) ,
60- { text : 'No results yet or not configured ' } ,
72+ i ( { class : 'material-symbols-rounded' } , 'more_horiz ' ) ,
73+ { text : 'Training model' , position : 'top-right '} ,
6174 )
62- : i ( { class : 'material-symbols-rounded' } , 'check' ) ,
63- ) ,
64- span ( { } , label ) ,
65- ) ;
75+ : isPending
76+ ? withTooltip (
77+ span ( { class : 'pl-2 pr-2' , style : 'position: relative;' } , '-' ) ,
78+ { text : 'No results yet or not configured' } ,
79+ )
80+ : i ( { class : 'material-symbols-rounded' } , 'check' ) ,
81+ ) ,
82+ span ( { } , tagLabel ) ,
83+ ) ;
84+ } ;
6685
6786 const numRuns = summary . lookback === 1 ? 'run' : `${ summary . lookback } runs` ;
6887 const duration = humanReadableDuration ( formatDuration ( summary . lookback_start , new Date ( ) ) , true )
6988 const labelElement = span ( { class : 'text-small text-secondary' } , `${ label } in last ${ numRuns } (${ duration } )` ) ;
7089
7190 const contentElement = div (
7291 { class : 'flex-row fx-gap-5' } ,
73- SummaryTag ( 'Freshness' , summary . freshness_anomalies , summary . freshness_has_errors , summary . freshness_is_training , summary . freshness_is_pending ) ,
74- SummaryTag ( 'Volume' , summary . volume_anomalies , summary . volume_has_errors , summary . volume_is_training , summary . volume_is_pending ) ,
75- SummaryTag ( 'Schema' , summary . schema_anomalies , summary . schema_has_errors , false , summary . schema_is_pending ) ,
76- SummaryTag ( 'Metrics' , summary . metric_anomalies , summary . metric_has_errors , summary . metric_is_training , summary . metric_is_pending ) ,
92+ SummaryTag ( 'freshness' , ' Freshness', summary . freshness_anomalies , summary . freshness_has_errors , summary . freshness_is_training , summary . freshness_is_pending ) ,
93+ SummaryTag ( 'volume' , ' Volume', summary . volume_anomalies , summary . volume_has_errors , summary . volume_is_training , summary . volume_is_pending ) ,
94+ SummaryTag ( 'schema' , ' Schema', summary . schema_anomalies , summary . schema_has_errors , false , summary . schema_is_pending ) ,
95+ SummaryTag ( 'metrics' , ' Metrics', summary . metric_anomalies , summary . metric_has_errors , summary . metric_is_training , summary . metric_is_pending ) ,
7796 ) ;
7897
7998 if ( summary . project_code && summary . table_group_id ) {
@@ -96,4 +115,12 @@ const AnomaliesSummary = (summary, label = 'Anomalies') => {
96115 return div ( { class : 'flex-column fx-gap-2' } , labelElement , contentElement ) ;
97116} ;
98117
118+ const summaryStylesheet = new CSSStyleSheet ( ) ;
119+ summaryStylesheet . replace ( `
120+ .summary-tag.clickable:hover,
121+ .summary-tag.active {
122+ background: var(--select-hover-background);
123+ }
124+ ` ) ;
125+
99126export { AnomaliesSummary } ;
0 commit comments