controllers/dashboard/select_context_controller.js

import { Controller } from "@hotwired/stimulus"
import { subscription } from "~/javascripts/store/mixins/subscription"
import { metricsInPercentile } from "~/javascripts/dashboard/utils"

/**
 * @class Dashboard.SelectContextController
 * @classdesc Stimulus controller that handles the buttons for the various contexts.
 * @extends Controller
 **/
export default class extends Controller {
  static targets = [ "button" ]
  static values = {
    contextKey: String,
    defaultContext: String,
    storeId: String
  }

  /** 
   * Subscribe to the store.
   * 
   * @instance
   * @memberof Dashboard.SelectContextController
   **/
  connect() {
    subscription(this)
    this.subscribe()
    this.reconnect()
  }

  /** 
   * Handles a repeated Turbo visit to the dashboard page.
   * 
   * @instance
   * @memberof Dashboard.SelectContextController
   **/
  reconnect() {
    if (this.store("selectedDataVizData")) {
      this.storeUpdated("selectedDataVizData", this.storeIdValue)
    }
  }

  /** 
   * Sets the selected class on the clicked button and updates the store with the context clicked and
   * the data for the context
   * 
   * @instance
   * @memberof Dashboard.SelectContextController
   **/
  contextClicked(event) {    
    this.buttonTargets.forEach(buttonTarget => buttonTarget.classList.remove("selected"))
    event.target.classList.add("selected")
    const buttonText = event.target.innerHTML
    this.editStore("contextSelected", this.contextSelected(buttonText))
    this.editStore("selectedContextData", this.selectContextData(this.contextSelected(buttonText)))
  }

  /** 
   * Get the data for the selected context. Also set the value to be used on the x axis.
   * If there is no data for the context then return a dummy object in an array.
   * 
   * @instance
   * @memberof Dashboard.SelectContextController
   **/
  selectContextData(context) {
    let selectedDataVizData = !this.store("selectedDataVizData") ? [] : this.store("selectedDataVizData") 
    let contextData =  selectedDataVizData.filter(data => data[this.contextKeyValue] === context)
    
    if(this.storeIdValue === "builds_") {
      if(contextData.length === 0) return [{ deploy_time: 0, build_number: 0 }]
      return contextData.map((data, index) => Object.assign(data, { build_number: index + 1 }))
    } else if (this.storeIdValue === "vitals_") {
      if (this.store("frameSelected") === undefined) this.editStore("frameSelected", "daily")

      contextData = metricsInPercentile(contextData, "data_float", 0.75)
      if (this.store("frameSelected") === "daily") {
        if(contextData.length === 0) return [{ value: 0, day: 0 }]

        const groupSumCount = contextData.reduce((acc , data) => {
          if (!acc.get(data.date)) {
            acc.set(
              data.date, { date: data.date, metric: data.metric, data_float: data.data_float, count: 1 }
            )
            return acc
          }
          const dateContent = acc.get(data.date)
          dateContent.data_float += data.data_float
          dateContent.count += 1
          return acc
        }, new Map())

        for (let result of groupSumCount.values()) {
          result.value = Math.round(((result.data_float/result.count) + Number.EPSILON) * 10000) / 10000
        }

        return [...groupSumCount.values()].map((data) => {
          return Object.assign(data, { day: new Date(data.date).getDate() })
        })
      } else if (this.store("frameSelected") === "frequency") {
        if(contextData.length === 0) return []
        return contextData.map(data => { 
          return { value: data.data_float }
        })
      }
    }
  }

  /** 
   * Use the button text to set the context.
   * 
   * @instance
   * @memberof Dashboard.SelectContextController
   **/
  contextSelected(buttonText) {
    return buttonText.toLowerCase().replace(" ", "-")
  }

  /** 
   * Triggered by the store whenever any store data changes. In this case it checks for
   * selectedDataVizData to be set and then sets production as the initially selected context
   * button.
   * 
   * @instance
   * @memberof Dashboard.SelectContextController
   **/
  storeUpdated(prop, storeId) {
    if ((prop === "frameLoaded" || prop === "selectedDataVizData") && storeId === this.storeIdValue) {
      this.editStore("contextSelected", this.contextSelected(this.defaultContextValue))
      this.editStore("selectedContextData", this.selectContextData(this.defaultContextValue))
      this.buttonTargets.forEach(buttonTarget => buttonTarget.classList.remove("selected"))
      this.buttonTargets[0].classList.add("selected")
    }
  }

  /** 
   * Unsubscribe from the store
   * 
   * @instance
   * @memberof Dashboard.SelectContextController
   **/
  disconnect() {
    this.unsubscribe()
  }
}