Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions src/frontend/src/ExportedRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,61 @@ export const exportedRoutes = [
},
],
},
{
path: "",
component: ChildRouteWrapper,
meta: {
sidebar: {
enabled: true,
name: "Mini-Grid",
icon: "bolt",
},
},
children: [
{
path: "mini-grids",
component: ChildRouteWrapper,
meta: {
layout: "default",
sidebar: {
enabled: true,
name: "Overview",
},
},
children: [
{
path: "",
component: MiniGridOverviewPage,
meta: {
layout: "default",
breadcrumb: {
level: "base",
name: "Mini-Grids",
link: "/mini-grids",
},
sidebar: {
enabled: true,
name: "Overview",
},
},
},
{
path: ":id",
component: MiniGridDetailPage,
meta: {
layout: "default",
breadcrumb: {
level: "base",
name: "Mini-Grid",
link: "/mini-grids",
target: "id",
},
},
},
],
},
],
},
{
// FIXME: Should this be part of the Customer route?
path: "/sold-appliance-detail",
Expand Down
5 changes: 5 additions & 0 deletions src/frontend/src/assets/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@
"agentDashboard": "Agent Overview",
"agentDeletedSuccess": "Agent Deleted Successfully",
"agentList": "Agent List",
"miniGridDashboard": "Mini-Grid Overview",
"miniGridList": "Mini-Grid List",
"agentPerformanceDashboard": "Agent Performance Dashboard",
"agentTicket": "Agent Ticket | Agent Tickets",
"agentTransaction": "Agent Transaction | Agent Transactions",
Expand Down Expand Up @@ -432,6 +434,9 @@
"topPerformingAgents": "Top Performing Agents",
"topUp": "Top Up",
"totalAgents": "Total Agents",
"totalClusters": "Total Clusters",
"totalCities": "Total Cities",
"totalMiniGrids": "Total Mini-Grids",
"totalCommission": "Total Commission",
"totalCost": "Total Cost",
"totalCustomers": "Total Customers",
Expand Down
246 changes: 246 additions & 0 deletions src/frontend/src/modules/Dashboard/MiniGridDashboard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
<template>
<div>
<md-toolbar class="md-dense">
<h3 class="md-title" style="flex: 1">
{{ $tc("phrases.miniGridDashboard") }}
</h3>
<md-button class="md-raised" @click="refreshData" :disabled="loading">
<md-icon>update</md-icon>
{{ $tc("phrases.refreshData") }}
<md-progress-bar
v-if="loading"
md-mode="indeterminate"
></md-progress-bar>
</md-button>
</md-toolbar>

<div>
<div class="md-layout md-gutter" style="margin-top: 3rem">
<!-- Basic Mini-Grid Metrics -->
<div class="md-layout-item md-size-100">
<div class="md-layout md-gutter">
<div
class="md-layout-item md-size-25 md-small-size-50 md-xsmall-size-100"
>
<box
:box-color="'blue'"
:center-text="true"
:header-text="$tc('phrases.totalMiniGrids')"
:sub-text="totalMiniGrids.toString()"
:box-icon="'bolt'"
/>
</div>
<div
class="md-layout-item md-size-25 md-small-size-50 md-xsmall-size-100"
>
<box
:box-color="'green'"
:center-text="true"
:header-text="$tc('phrases.totalClusters')"
:sub-text="totalClusters.toString()"
:box-icon="'account_tree'"
/>
</div>
<div
class="md-layout-item md-size-25 md-small-size-50 md-xsmall-size-100"
>
<box
:box-color="'orange'"
:center-text="true"
:header-text="$tc('phrases.totalCities')"
:sub-text="totalCities.toString()"
:box-icon="'location_city'"
/>
</div>
<div
class="md-layout-item md-size-25 md-small-size-50 md-xsmall-size-100"
>
<box
:box-color="'red'"
:center-text="true"
:header-text="$tc('phrases.totalAgents')"
:sub-text="totalAgents.toString()"
:box-icon="'supervisor_account'"
/>
</div>
</div>
</div>

<!-- Mini-Grid List -->
<div class="md-layout-item md-size-100">
<widget :title="$tc('phrases.miniGridList')" id="mini-grid-list">
<div class="search-section">
<md-field>
<label>{{ $tc("words.search") }}</label>
<md-input v-model="searchTerm" @input="filterMiniGrids" />
<md-icon>search</md-icon>
</md-field>
</div>

<md-table md-card style="margin-left: 0">
<md-table-row>
<md-table-head>{{ $tc("words.name") }}</md-table-head>
<md-table-head>{{ $tc("words.cluster") }}</md-table-head>
<md-table-head>{{ $tc("words.createdAt") }}</md-table-head>
<md-table-head>{{ $tc("words.cities") }}</md-table-head>
<md-table-head>{{ $tc("words.agents") }}</md-table-head>
</md-table-row>
<md-table-row
v-for="miniGrid in filteredMiniGrids"
:key="miniGrid.id"
style="cursor: pointer"
@click="viewMiniGridDetail(miniGrid.id)"
>
<md-table-cell>
{{ miniGrid.name }}
</md-table-cell>
<md-table-cell>
{{ miniGrid.cluster?.name || "-" }}
</md-table-cell>
<md-table-cell>
{{ formatDate(miniGrid.created_at) }}
</md-table-cell>
<md-table-cell>
{{ miniGrid.cities_count || 0 }}
</md-table-cell>
<md-table-cell>
{{ miniGrid.agents_count || 0 }}
</md-table-cell>
</md-table-row>
</md-table>
</widget>
</div>
</div>
</div>
</div>
</template>

<script>
import Box from "@/shared/Box.vue"
import Widget from "@/shared/Widget.vue"
import { MiniGridService } from "@/services/MiniGridService"
import { notify } from "@/mixins/notify"
import moment from "moment"

export default {
name: "MiniGridDashboard",
components: { Box, Widget },
mixins: [notify],
data() {
return {
loading: false,
searchTerm: "",
miniGridService: new MiniGridService(),
miniGrids: [],
}
},
computed: {
totalMiniGrids() {
return this.miniGrids.length
},
totalClusters() {
const uniqueClusters = new Set(
this.miniGrids
.map((mg) => mg.cluster_id)
.filter((id) => id !== null && id !== undefined),
)
return uniqueClusters.size
},
totalCities() {
// If cities_count is available, use it; otherwise count cities array if available
return this.miniGrids.reduce((sum, mg) => {
if (mg.cities_count !== undefined) {
return sum + mg.cities_count
}
if (mg.cities && Array.isArray(mg.cities)) {
return sum + mg.cities.length
}
return sum
}, 0)
},
totalAgents() {
// If agents_count is available, use it; otherwise count agents array if available
return this.miniGrids.reduce((sum, mg) => {
if (mg.agents_count !== undefined) {
return sum + mg.agents_count
}
if (mg.agents && Array.isArray(mg.agents)) {
return sum + mg.agents.length
}
return sum
}, 0)
},
filteredMiniGrids() {
if (!this.searchTerm) return this.miniGrids

const searchLower = this.searchTerm.toLowerCase()
return this.miniGrids.filter((miniGrid) => {
const name = miniGrid.name || ""
const clusterName = miniGrid.cluster?.name || ""
return (
name.toLowerCase().includes(searchLower) ||
clusterName.toLowerCase().includes(searchLower)
)
})
},
},
mounted() {
this.loadMiniGridData()
},
methods: {
async loadMiniGridData() {
this.loading = true
try {
const miniGrids = await this.miniGridService.getMiniGrids()
if (miniGrids instanceof Error) {
this.alertNotify("error", "Failed to load mini-grid data")
this.miniGrids = []
} else {
this.miniGrids = miniGrids || []
}
} catch (error) {
this.alertNotify("error", "Failed to load mini-grid data")
this.miniGrids = []
} finally {
this.loading = false
}
},
async refreshData() {
await this.loadMiniGridData()
this.alertNotify("success", "Mini-grid data refreshed successfully")
},
filterMiniGrids() {
// Filtering is handled by computed property
},
viewMiniGridDetail(miniGridId) {
this.$router.push({ path: `/mini-grids/${miniGridId}` })
},
formatDate(date) {
if (!date) return "-"
return moment(date).format("YYYY-MM-DD")
},
},
}
</script>

<style lang="scss" scoped>
.search-section {
margin-bottom: 1rem;
padding: 0 1rem;
}

.md-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
}

.md-toolbar .md-title {
flex: 1;
}

.md-toolbar .md-button {
margin-left: auto;
}
</style>

2 changes: 1 addition & 1 deletion src/frontend/src/modules/Map/ClusterMap.vue
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ export default {

const parent = this
miniGridMarker.on("click", function () {
parent.routeToDetail("/dashboards/mini-grid", markingInfo.id)
parent.routeToDetail("/mini-grids", markingInfo.id)
})

miniGridMarker.addTo(this.markersLayer)
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/src/modules/Map/DashboardMap.vue
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export default {

const parent = this
miniGridMarker.on("click", function () {
parent.routeToDetail("/dashboards/mini-grid", markingInfo.id)
parent.routeToDetail("/mini-grids", markingInfo.id)
})

miniGridMarker.addTo(this.markersLayer)
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/src/modules/MiniGrid/Dashboard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ export default {
}
},
setMiniGrid(miniGridId) {
this.$router.replace("/dashboards/mini-grid/" + miniGridId)
this.$router.replace("/mini-grids/" + miniGridId)
},
togglePeriod() {
this.period = {
Expand Down
25 changes: 3 additions & 22 deletions src/frontend/src/modules/MiniGrid/MiniGridOverviewPage.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div>
<Loader />
<mini-grid-dashboard />
</div>
</template>

Expand All @@ -9,27 +9,8 @@ import { MiniGridService } from "@/services/MiniGridService.js"
import Loader from "@/shared/Loader.vue"

export default {
name: "MiniGridsPage",
components: { Loader },
created() {
const miniGridId = this.$route.params.id
if (miniGridId === undefined) {
this.redirectToFirstMiniGrid()
}
},
data() {
return {
miniGridService: new MiniGridService(),
}
},
methods: {
async redirectToFirstMiniGrid() {
const miniGrids = await this.miniGridService.getMiniGrids()
if (miniGrids.length > 0) {
this.$router.replace("/dashboards/mini-grid/" + miniGrids[0].id)
}
},
},
name: "MiniGridDashboardPage",
components: { MiniGridDashboard },
}
</script>

Expand Down
Loading