11<template >
22 <v-badge
3- :model-value =" pendingDevicesCount > 0"
4- :content =" pendingDevicesCount "
3+ :model-value =" pendingDevices > 0"
4+ :content =" pendingDevices "
55 offset-y =" -5"
66 location =" top right"
77 color =" success"
88 size =" x-small"
99 data-test =" device-dropdown-badge"
10- :class =" { 'mr-1': pendingDevicesCount > 0 }"
10+ :class =" { 'mr-1': pendingDevices > 0 }"
1111 >
1212 <v-icon
1313 color =" primary"
4747 variant =" tonal"
4848 data-test =" total-devices-card"
4949 >
50- <div class =" text-h4 font-weight-bold" >
51- {{ stats.registered_devices }}
52- </div >
50+ <div class =" text-h4 font-weight-bold" >{{ totalDevices }}</div >
5351 <div class =" text-caption text-medium-emphasis" >Total</div >
5452 </v-card >
5553 </v-col >
54+
5655 <v-col
5756 cols =" 6"
5857 sm =" 3"
6261 variant =" tonal"
6362 data-test =" online-devices-card"
6463 >
65- <div class =" text-h4 font-weight-bold" >
66- {{ stats.online_devices }}
67- </div >
64+ <div class =" text-h4 font-weight-bold" >{{ onlineDevices }}</div >
6865 <div class =" text-caption text-medium-emphasis" >Online</div >
6966 </v-card >
7067 </v-col >
68+
7169 <v-col
7270 cols =" 6"
7371 sm =" 3"
7775 variant =" tonal"
7876 data-test =" pending-devices-card"
7977 >
80- <div class =" text-h4 font-weight-bold" >
81- {{ stats.pending_devices }}
82- </div >
78+ <div class =" text-h4 font-weight-bold" >{{ pendingDevices }}</div >
8379 <div class =" text-caption text-medium-emphasis" >Pending</div >
8480 </v-card >
8581 </v-col >
82+
8683 <v-col
8784 cols =" 6"
8885 sm =" 3"
9289 variant =" tonal"
9390 data-test =" offline-devices-card"
9491 >
95- <div class =" text-h4 font-weight-bold" >
96- {{ offlineDevices }}
97- </div >
92+ <div class =" text-h4 font-weight-bold" >{{ offlineDevices }}</div >
9893 <div class =" text-caption text-medium-emphasis" >Offline</div >
9994 </v-card >
10095 </v-col >
10196 </v-row >
97+
10298 <v-btn-toggle
10399 v-model =" activeTab"
104100 mandatory
120116 />
121117 <span v-if =" smAndUp" >Pending Approval</span >
122118 <v-chip
123- v-if =" stats.pending_devices > 0"
119+ v-if =" pendingDevices > 0"
124120 color =" warning"
125121 size =" x-small"
126122 class =" ml-2"
127123 >
128- {{ stats.pending_devices }}
124+ {{ pendingDevices }}
129125 </v-chip >
130126 </v-btn >
127+
131128 <v-btn
132129 value =" recent"
133130 data-test =" recent-tab"
141138 <span v-if =" smAndUp" >Recent Activity</span >
142139 </v-btn >
143140 </v-btn-toggle >
141+
144142 <v-window
145143 v-model =" activeTab"
146144 class =" overflow-visible"
151149 class =" overflow-y-auto border"
152150 >
153151 <v-list
154- v-if =" pendingDevicesCount > 0"
152+ v-if =" pendingDevicesList.length > 0"
155153 density =" compact"
156154 class =" bg-v-theme-surface pa-0"
157155 >
232230 color =" success"
233231 class =" opacity-50 mb-3"
234232 />
235- <p class =" text-body-2 text-medium-emphasis" >
236- No pending devices
237- </p >
238- <p class =" text-caption text-disabled mt-1" >
239- All devices have been approved
240- </p >
233+ <p class =" text-body-2 text-medium-emphasis" >No pending devices</p >
234+ <p class =" text-caption text-disabled mt-1" >All devices have been approved</p >
241235 </div >
242236 </v-card >
243237 </v-window-item >
292286 color =" primary"
293287 class =" opacity-50 mb-3"
294288 />
295- <p class =" text-body-2 text-medium-emphasis" >
296- No recent activity
297- </p >
289+ <p class =" text-body-2 text-medium-emphasis" >No recent activity</p >
298290 </div >
299291 </v-card >
300292 </v-window-item >
322314<script setup lang="ts">
323315import { computed , onBeforeMount , ref } from " vue" ;
324316import { useDisplay } from " vuetify" ;
325- import useStatsStore from " @/store/modules/stats" ;
326317import useDevicesStore from " @/store/modules/devices" ;
327318import handleError from " @/utils/handleError" ;
328319import useSnackbar from " @/helpers/snackbar" ;
329320import moment from " moment" ;
330- import { IDevice } from " @/interfaces/IDevice" ;
321+ import type { IDevice } from " @/interfaces/IDevice" ;
331322import DeviceActionButton from " @/components/Devices/DeviceActionButton.vue" ;
332323
333324const { smAndUp, thresholds } = useDisplay ();
334- const statsStore = useStatsStore ();
335325const devicesStore = useDevicesStore ();
336326const snackbar = useSnackbar ();
337327
338328const isDrawerOpen = ref (false );
339329const activeTab = ref <" pending" | " recent" >(" pending" );
330+
331+ const totalDevices = computed (() => devicesStore .totalDevicesCount );
332+ const onlineDevices = computed (() => devicesStore .onlineDevicesCount );
333+ const offlineDevices = computed (() => devicesStore .offlineDevicesCount );
334+ const pendingDevices = computed (() => devicesStore .pendingDevicesCount );
335+
340336const pendingDevicesList = ref <IDevice []>([]);
341- const pendingDevicesCount = computed (() => pendingDevicesList .value .length );
342337const recentDevicesList = ref <IDevice []>([]);
343- const stats = computed (() => statsStore .stats );
344- const offlineDevices = computed (
345- () => stats .value .registered_devices - stats .value .online_devices ,
346- );
338+
347339const toggleDrawer = () => {
348340 isDrawerOpen .value = ! isDrawerOpen .value ;
349341};
350342
351- const formatTimeAgo = (date : string | Date ) => {
352- if (! date ) return " Unknown" ;
353- return moment (date ).fromNow ();
354- };
355-
356- const handleUpdate = async () => {
357- await fetchStats ();
358- await fetchPendingDevices ();
359- await fetchRecentDevices ();
360- };
361-
362- const fetchStats = async () => {
363- try {
364- await statsStore .fetchStats ();
365- } catch (error : unknown ) {
366- snackbar .showError (" Failed to load device statistics" );
367- handleError (error );
368- }
369- };
343+ const formatTimeAgo = (date : string | Date ) =>
344+ date ? moment (date ).fromNow () : " Unknown" ;
370345
371346const fetchPendingDevices = async () => {
372347 try {
373- await devicesStore .fetchDeviceList ({ status: " pending" , perPage: 100 });
348+ await devicesStore .fetchDeviceList ({
349+ status: " pending" ,
350+ perPage: 100 ,
351+ filter: undefined ,
352+ });
374353 pendingDevicesList .value = [... devicesStore .devices ];
375- } catch (error : unknown ) {
376- handleError (error );
354+ } catch (e ) {
355+ snackbar .showError (" Failed to load pending devices" );
356+ handleError (e );
377357 }
378358};
379359
380360const fetchRecentDevices = async () => {
381361 try {
382- await devicesStore .fetchDeviceList ({ status: " accepted" });
362+ await devicesStore .fetchDeviceList ({
363+ status: " accepted" ,
364+ perPage: 100 ,
365+ filter: undefined ,
366+ });
383367 recentDevicesList .value = [... devicesStore .devices ].sort (
384368 (a , b ) =>
385369 new Date (b .last_seen ).getTime () - new Date (a .last_seen ).getTime (),
386370 );
387- } catch (error : unknown ) {
388- handleError (error );
371+ } catch (e ) {
372+ snackbar .showError (" Failed to load recent devices" );
373+ handleError (e );
374+ }
375+ };
376+
377+ const handleUpdate = async () => {
378+ try {
379+ await devicesStore .fetchDeviceCounts ();
380+ await fetchPendingDevices ();
381+ await fetchRecentDevices ();
382+ } catch (e ) {
383+ snackbar .showError (" Failed to update device data" );
384+ handleError (e );
389385 }
390386};
391387
392388onBeforeMount (async () => {
393- await fetchStats ();
394- await fetchPendingDevices ();
395- await fetchRecentDevices ();
389+ await handleUpdate ();
396390});
397391
398392defineExpose ({
@@ -403,7 +397,9 @@ defineExpose({
403397 activeTab ,
404398 pendingDevicesList ,
405399 recentDevicesList ,
406- stats ,
400+ totalDevices ,
401+ onlineDevices ,
407402 offlineDevices ,
403+ pendingDevices ,
408404});
409405 </script >
0 commit comments