controllers/dashboard/months_controller.js

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

const monthMapper = {
  1: "Jan", 2: "Feb", 3: "Mar", 4: "Apr", 5: "May", 6: "Jun", 7: "Jul", 8: "Aug", 9: "Sep", 10: "Oct",
  11: "Nov", 12: "Dec"
}

/**
 * @class Dashboard.MonthsController
 * @classdesc Stimulus controller that handles the months buttons data and interaction.
 * @extends Controller
 **/
export default class extends Controller {
  static targets = [ "buttonGroup", "button" ]
  static values = {
    function: String,
    storeId: String,
    yearSelected: Number
  }

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

    this.reconnect()
  }

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

  /** 
   * Triggered when a year is set. Uses a document fragment to fully create the
   * months buttons before removing the load button and adding all the new months buttons. The last
   * button is set as selected
   * 
   * @instance
   * @memberof Dashboard.MonthsController
   **/
  yearSelectedValueChanged() {
    if (this.yearSelectedValue !== 0) {
      const yearAndMonths = this.store("yearsAndMonths").find(data => data.year === this.yearSelectedValue)
      let fragment = document.createDocumentFragment()

      yearAndMonths.month_numbers.forEach(monthNumber => {
        const buttonClone = this.buttonTarget.cloneNode(false)
        const button = fragment.appendChild(buttonClone)
        button.innerHTML = monthMapper[monthNumber]
      });

      this.buttonTargets.forEach(buttonTarget => buttonTarget.remove())
      this.buttonGroupTarget.appendChild(fragment)
      this.buttonTargets.forEach(buttonTarget => buttonTarget.classList.remove("selected"))
      this.buttonTargets[this.buttonTargets.length - 1].classList.add("selected")
    }
  }
  
  /** 
   * Ignores month if already clicked. Set the month clicked as selected in the store and gives the class
   * selected to the clicked month.
   * 
   * @instance
   * @memberof Dashboard.MonthsController
   **/
  monthClicked(event) {
    if (this.store("monthSelected") === parseInt(event.target.innerHTML)) return

    this.editStore(
      "monthSelected",
      parseInt(Object.keys(monthMapper).find(key => monthMapper[key] === event.target.innerHTML))
    )

    this.buttonTargets.forEach(buttonTarget => buttonTarget.classList.remove("selected"))
    event.target.classList.add("selected")
  }

  /** 
   * Fetch all of the data for the selected month. If the data for the month is already present
   * then it does not fetch again.
   * 
   * @instance
   * @memberof Dashboard.MonthsController
   **/
  fetchDataVizData() {
    const year = this.store("yearSelected")
    const month =this.store("monthSelected")

    const data = this.store("dataVizData")[`${year}${month}`]
    if (data) {
      this.editStore("selectedDataVizData", data)
    } else {
      this.editStore("fetchingDataVizData", true)

      fetch(`/.netlify/functions/supabase-get-api?year=${year}&month=${month}`, { 
        headers: { "Function-Name": this.functionValue }
      })
        .then(responseCheck => {
          if (!responseCheck.ok) { throw Error(responseCheck.status); }
          return responseCheck;
        })
        .then(res => res.json())
        .then(fetchedData => {
          this.editStore("fetchingDataVizData", false)
          let dataToUpdate = this.store("dataVizData")
          dataToUpdate[`${year}${month}`] = fetchedData
          this.editStore("dataVizData", dataToUpdate)
          this.editStore("selectedDataVizData", this.store("dataVizData")[`${year}${month}`])
        })
        .catch(error => {
          console.warn(error)
          this.editStore("fetchingDataVizData", false)
        });
    }
  }

  /** 
   * Triggered by the store whenever any store data changes. Fetches the relevant data when a month is
   * selected
   * 
   * @instance
   * @memberof Dashboard.MonthsController
   **/
  storeUpdated(prop, storeId) {
    this.yearSelectedValue = this.store("yearSelected")
    if (prop === "monthSelected" && storeId === this.storeIdValue) this.fetchDataVizData()
  }

  /** 
   * Unsubscribe from the store
   * 
   * @instance
   * @memberof Dashboard.MonthsController
   **/
  disconnect() {
    this.yearSelectedValue = ""
    this.unsubscribe()
  }
}