From ff63cdca8b8511d648c3f5d1b54e6bb9261514c8 Mon Sep 17 00:00:00 2001 From: isra el Date: Sat, 2 Mar 2024 22:20:05 +0300 Subject: [PATCH] feat(web): improve dashboard stats --- web/components/dashboard/UserStats.tsx | 31 ++++++++++---- web/services/statsService.ts | 10 +++++ web/store/statsSlice.ts | 56 ++++++++++++++++++++++++++ web/store/store.ts | 2 + 4 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 web/services/statsService.ts create mode 100644 web/store/statsSlice.ts diff --git a/web/components/dashboard/UserStats.tsx b/web/components/dashboard/UserStats.tsx index 380d078..08ef988 100644 --- a/web/components/dashboard/UserStats.tsx +++ b/web/components/dashboard/UserStats.tsx @@ -1,15 +1,27 @@ import { Box, SimpleGrid, chakra } from '@chakra-ui/react' -import React from 'react' +import React, { useEffect } from 'react' import { useSelector } from 'react-redux' -import { selectApiKeyList } from '../../store/apiKeySlice' import { selectAuthUser } from '../../store/authSlice' -import { selectDeviceList } from '../../store/deviceSlice' import UserStatsCard from './UserStatsCard' +import { + fetchStats, + selectStatsData, + selectStatsLoading, +} from '../../store/statsSlice' +import { useAppDispatch, useAppSelector } from '../../store/hooks' const UserStats = () => { const authUser = useSelector(selectAuthUser) - const deviceList = useSelector(selectDeviceList) - const apiKeyList = useSelector(selectApiKeyList) + + const { totalApiKeyCount, totalDeviceCount, totalSMSCount } = + useAppSelector(selectStatsData) + const statsLoading = useAppSelector(selectStatsLoading) + + const dispatch = useAppDispatch() + + useEffect(() => { + dispatch(fetchStats()) + }, [dispatch]) return ( <> @@ -26,13 +38,16 @@ const UserStats = () => { + - diff --git a/web/services/statsService.ts b/web/services/statsService.ts new file mode 100644 index 0000000..b83c763 --- /dev/null +++ b/web/services/statsService.ts @@ -0,0 +1,10 @@ +import httpClient from '../lib/httpClient' + +class StatsService { + async getStats() { + const res = await httpClient.get(`/gateway/stats`) + return res.data.data + } +} + +export const statsService = new StatsService() diff --git a/web/store/statsSlice.ts b/web/store/statsSlice.ts new file mode 100644 index 0000000..896044f --- /dev/null +++ b/web/store/statsSlice.ts @@ -0,0 +1,56 @@ +import { createAsyncThunk, createSlice, isAnyOf } from '@reduxjs/toolkit' +import type { PayloadAction } from '@reduxjs/toolkit' +import { createStandaloneToast } from '@chakra-ui/react' +import { RootState } from './store' +import { statsService } from '../services/statsService' + +const { toast } = createStandaloneToast() + +const initialState = { + loading: false, + data: { + totalApiKeyCount: 0, + totalDeviceCount: 0, + totalSMSCount: 0, + }, +} + +export const fetchStats = createAsyncThunk( + 'gateway/fetchStats', + async (_, { rejectWithValue }) => { + try { + const res = await statsService.getStats() + return res + } catch (e) { + toast({ + title: e.response.data.error || 'Failed to Fetch stats', + status: 'error', + }) + return rejectWithValue(e.response.data) + } + } +) + +export const statsSlice = createSlice({ + name: 'stats', + initialState, + reducers: {}, + extraReducers: (builder) => { + builder + .addCase(fetchStats.fulfilled, (state, action: PayloadAction) => { + state.loading = false + state.data = action.payload + }) + .addCase(fetchStats.rejected, (state) => { + state.loading = false + }) + .addMatcher(isAnyOf(fetchStats.pending), (state) => { + state.loading = true + }) + }, +}) + +export const selectStatsLoading = (state: RootState) => state.stats.loading +export const selectStatsData = (state: RootState) => state.stats.data + +export default statsSlice.reducer diff --git a/web/store/store.ts b/web/store/store.ts index 94f0589..1581172 100644 --- a/web/store/store.ts +++ b/web/store/store.ts @@ -2,12 +2,14 @@ import { configureStore } from '@reduxjs/toolkit' import apiKeyReducer from './apiKeySlice' import authReducer from './authSlice' import deviceReducer from './deviceSlice' +import statsReducer from './statsSlice' export const store = configureStore({ reducer: { auth: authReducer, apiKey: apiKeyReducer, device: deviceReducer, + stats: statsReducer, }, })