aboutsummaryrefslogtreecommitdiff
path: root/kamon-status-page/src/main/vue/src/components/MetricList.vue
blob: f0f4637bf04d4f72e9daa081246a611f0c5d1032 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
<template>
  <div class="row no-gutters">
    <div class="col-12">
      <div class="search-box mb-3">
        <span class="search-icon"><i class="fas fa-search fa-fw fa-flip-horizontal"></i></span>
        <input class="w-100" v-model="filterPattern" type="text">
        <span class="search-stats">{{ searchStats }}</span>
      </div>
    </div>

    <div class="col-12" v-if="matchedMetrics.length > 0">
      <div class="row no-gutters" v-for="(group, index) in groups" :key="group.name">
        <div class="col-12">
          <metric-list-item :group="group"/>
        </div>
        <hr v-if="index < (groups.length - 1)" class="w-100">
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'
import {Metric} from '../api/StatusApi'
import Card from './Card.vue'
import MetricListItem, {MetricGroup} from './MetricListItem.vue'
import StatusCard from './StatusCard.vue'
import _ from 'underscore'

@Component({
  components: {
    'card': Card,
    'status-card': StatusCard,
    'metric-list-item': MetricListItem
  }
})
export default class MetricList extends Vue {
  @Prop( { default: [] }) private metrics!: Metric[]
  private filterPattern: string = ''

  get totalMetrics(): number {
    return this.metrics.length
  }

  get groups(): MetricGroup[] {
    const gropedByName = _.groupBy(this.matchedMetrics, m => m.name)
    const metricGroups: MetricGroup[] = []

    Object.keys(gropedByName).forEach(metricName => {
      const metrics = gropedByName[metricName]

      // All metrics with the same name must have the same unit (constrained in Kamon) so
      // we can safely assume the first unit is the same for all.
      metricGroups.push({
        name: metricName,
        type: metrics[0].type,
        unitDimension: metrics[0].unitDimension,
        unitMagnitude: metrics[0].unitMagnitude,
        metrics
      })
    })

    return _.sortBy(metricGroups, mg => mg.metrics.length).reverse()
  }

  get filterRegex(): RegExp {
    return new RegExp(this.filterPattern)
  }

  get searchStats(): string {
    if (this.filterPattern.length > 0) {
      return 'showing ' + this.matchedMetrics.length + ' out of ' + this.totalMetrics + ' series'
    } else {
      return this.totalMetrics + ' series'
    }
  }

  get matchedMetrics(): Metric[] {
    if (this.filterPattern.length > 0) {
      return this.metrics.filter(m => m.search.match(this.filterRegex) != null)
    } else {
      return this.metrics
    }
  }
}
</script>

<style lang="scss">
.search-box {
  input {
    color: #676767;
    caret-color: #676767;
    height: 3rem;
    border: none;
    border-radius: 0.4rem;
    background-color: #efefef;
    padding-left: 3.5rem;
    font-size: 1.1rem;
    box-shadow: 0 2px 4px 1px rgba(0, 0, 0, 0.1);


    &:focus {
      outline: none;
    }
  }

  ::placeholder {
    color: #929292;
  }

  .search-icon {
    color: #c0c0c0;
    line-height: 3rem;
    font-size: 1.4rem;
    position: absolute;
    left: 1rem;
  }

  .search-stats {
    color: #a2a2a2;
    font-size: 1.1rem;
    position: absolute;
    line-height: 3rem;
    right: 0;
    padding-right: 1rem;
  }
}

</style>