cardamon 0.0.2

Cardamon is a tool to help development teams measure the power consumption and carbon emissions of their software.
<template>
  <MainLayout>
    <template #title>
      <h1 class="main-layout__title">Scenario Detail</h1>
    </template>
    <template #breadcrumbs>
      <AppBreadcrumb />
    </template>
    <template #dashboard>
      <div ref="gridContainer" class="grid-stack pd-0">
        <component
          v-for="widget in widgets"
          :is="getComponent(widget.type)"
          :key="widget.id"
          :data="widget"
          @updatePage="handlePageChange"
        />
      </div>
    </template>
  </MainLayout>
</template>

<script setup lang="ts">
import { ref, onMounted, watch, nextTick, onBeforeUnmount } from 'vue'
import { useRoute } from 'vue-router'
import MainLayout from '@/layouts/MainLayout.vue'
import AppBreadcrumb from '@/components/Common/Breadcrumb/AppBreadcrumb.vue'
import { useWidgetStore } from '@/stores/widgets'
import { useScenarioStore } from '@/stores/scenario'
import { WidgetType, type Widget } from '@/types/widgets.types'
import { GridStack, type GridStackNode } from 'gridstack'
import 'gridstack/dist/gridstack.min.css'
import 'gridstack/dist/gridstack-extra.min.css'
import MetricCard from '@/components/widgets/MetricCard/MetricCard.vue'
import DynamicChart from '@/components/widgets/DynamicChart/DynamicChart.vue'
import CpuUsageChart from '@/components/widgets/CpuUsageChart/CpuUsageChart.vue'
import DataTable from '@/components/widgets/Table/DataTable.vue'

const route = useRoute()
const scenarioName = route.params.scenarioName

const scenarioStore = useScenarioStore()
const widgetStore = useWidgetStore()

const loading = ref(true)
const widgets = ref([] as any[])

const fetchAllData = async (page: number) => {
  try {
    loading.value = true
    await scenarioStore.fetchScenarioDetails(scenarioName as string, { page })
    await widgetStore.initializeWidgets(scenarioName as string)
  } finally {
    widgets.value = widgetStore.scenarioWidgets[scenarioName as string] || []
    loading.value = false
  }
}

const gridContainer = ref<HTMLElement | null>(null)
const grid = ref<GridStack | null>(null)

const getComponent = (type: string) => {
  switch (type) {
    case WidgetType.METRIC:
      return MetricCard
    case WidgetType.CHART:
      return DynamicChart
    case WidgetType.CPU_USAGE:
      return CpuUsageChart
    case WidgetType.TABLE:
      return DataTable
    default:
      return null
  }
}

const initGridStack = async () => {
  await nextTick()
  if (gridContainer.value) {
    grid.value = GridStack.init({
      column: 6,
      cellHeight: 100,
      margin: 10
    })
    grid.value.on('change', onChange)
    makeWidgets(widgets.value)
  }
}

const makeWidgets = (widgets: Array<Widget>) => {
  widgets.forEach((widget) => {
    makeWidget(widget)
  })
}

const makeWidget = (widget: Widget) => {
  const element = gridContainer.value?.querySelector(`[gs-id="${widget.id}"]`) as HTMLElement
  if (element && grid.value) {
    grid.value.addWidget(element)
  }
}

const onChange = (event: Event, items: Array<GridStackNode>) => {
  const updatedPositions = items.map((item) => ({
    id: item.id,
    x: item.x,
    y: item.y,
    w: item.w,
    h: item.h
  }))
  widgetStore.updateWidgetsPos(scenarioName as string, updatedPositions)
}

const handlePageChange = async (page: number) => {
  await scenarioStore.fetchScenarioDetails(scenarioName as string, { page })
  await widgetStore.updateWidgetsData(
    scenarioName as string,
    scenarioStore.scenarioDetails[scenarioName as string]
  )
}

watch(
  () => widgets.value,
  async () => {
    if (grid.value) {
      await nextTick()
      makeWidget(widgets.value[widgets.value.length - 1])
    }
  },
  { deep: true }
)

watch(
  () => widgetStore.scenarioWidgets[scenarioName as string],
  (newWidgets) => {
    widgets.value = newWidgets || []
  }
)

onBeforeUnmount(() => {
  if (grid.value) {
    grid.value.destroy(false)
  }
})

onMounted(async () => {
  await fetchAllData(1)
  initGridStack()
})
</script>

<style scoped>
.main-layout__title {
  @apply text-2xl font-bold dark:text-white ml-3;
}

.main-layout__loading-spinner {
  @apply flex items-center justify-center h-full;
}

.grid-stack {
  height: auto !important;
}
</style>