From 071d586f295462fe516a4f25a62f52db416b0730 Mon Sep 17 00:00:00 2001 From: yznahmad Date: Sat, 21 Jun 2025 16:17:36 +0300 Subject: [PATCH] Add 30 n12345688400 --- .../(Auth)/dashboard/(home)/page.tsx | 57 ++++++++++++------- webapp/src/redux/features/statistics-slice.ts | 50 +++++++++------- 2 files changed, 66 insertions(+), 41 deletions(-) diff --git a/webapp/src/app/[locale]/(GlobalWrapper)/(Auth)/dashboard/(home)/page.tsx b/webapp/src/app/[locale]/(GlobalWrapper)/(Auth)/dashboard/(home)/page.tsx index f870263..c123834 100644 --- a/webapp/src/app/[locale]/(GlobalWrapper)/(Auth)/dashboard/(home)/page.tsx +++ b/webapp/src/app/[locale]/(GlobalWrapper)/(Auth)/dashboard/(home)/page.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useEffect } from 'react' +import { useEffect, useCallback } from 'react' import GeneralLook from '@/components/dashboard/home/generalLook' import MembersOverviewChart from '@/components/dashboard/home/membersOverviewChart' import ServicesSubscriptions from '@/components/dashboard/home/servicesSubscriptions' @@ -12,41 +12,60 @@ import { AppDispatch } from '@/redux/store'; import { load } from '@/redux/features/statistics-slice' import CircularProgress from '@mui/material/CircularProgress'; import { useTranslations } from 'next-intl'; +import { Button } from '@/components/ui/button'; +import { RefreshCw } from 'lucide-react'; export default function Page() { - const dispatch = useDispatch() const t = useTranslations('statistics'); const report = useAppSelector((state) => state.statisticsReducer.value.report) - const loadedFirstTime = useAppSelector((state) => state.statisticsReducer.value.loadedFirstTime) const isLoading = useAppSelector((state) => state.statisticsReducer.value.isLoading) - useEffect(() => { - if(loadedFirstTime) return; - if(isLoading) return; - async function a() - { - await dispatch(load({})) + const fetchData = useCallback(async () => { + try { + await dispatch(load({ forceRefresh: true })); + } catch (error) { + console.error('Failed to fetch dashboard data:', error); } - a() - }, []) + }, [dispatch]); - if(isLoading) - { + useEffect(() => { + // Always fetch fresh data when the component mounts + fetchData(); + + // Set up interval to refresh data every 5 minutes + const intervalId = setInterval(fetchData, 5 * 60 * 1000); + + // Clean up interval on component unmount + return () => clearInterval(intervalId); + }, [fetchData]); + + if (isLoading && !report) { return ( - <> -
- -

{t('loading')}...

-
- +
+ +

{t('loading')}...

+
) } return ( <>
+
+ +
+
diff --git a/webapp/src/redux/features/statistics-slice.ts b/webapp/src/redux/features/statistics-slice.ts index 25dddee..83e091c 100644 --- a/webapp/src/redux/features/statistics-slice.ts +++ b/webapp/src/redux/features/statistics-slice.ts @@ -1,4 +1,4 @@ -import { createAsyncThunk , createSlice , PayloadAction } from "@reduxjs/toolkit"; +import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit"; import axios from 'axios' import { fireAlert } from "./alert-slice"; @@ -6,13 +6,13 @@ import { fireAlert } from "./alert-slice"; const ACTION_NAME = 'statistics' // slice initial value state type IinitialState = { - value : IValueState; + value: IValueState; } // state value type type IValueState = { - isLoading : boolean, + isLoading: boolean, loadedFirstTime: boolean, - report : + report: { membersCount: { name: string, @@ -53,7 +53,7 @@ type IValueState = { membersGeneralOverview: { name: "generalMembersOverView", value: { - thisWeek : { + thisWeek: { 'year': number, 'month': number, 'week': number, @@ -109,7 +109,7 @@ type IValueState = { } } }, - months : { + months: { 'year': number, 'month': number, 'weeks': { @@ -167,7 +167,7 @@ type IValueState = { }[] }[] }[], - years : { + years: { 'year': number, 'months': { 'month': number, @@ -390,19 +390,19 @@ const initialState = { // load data ability const load = createAsyncThunk( 'statistics/load', - async (actionPayload : {}, thunkAPI) => { + async (actionPayload: { forceRefresh?: boolean } = {}, thunkAPI) => { try { - let { data } = await axios.get(`/api/user/actions/${ACTION_NAME}`) - if(data.success) - { + // Add a timestamp to the URL to prevent caching when forceRefresh is true + const timestamp = actionPayload.forceRefresh ? `?t=${Date.now()}` : ''; + let { data } = await axios.get(`/api/user/actions/${ACTION_NAME}${timestamp}`) + if (data.success) { return data + } else { + return { success: false } } - else - { - return { success : false } - } - }catch(err) { - return { success : false } + } catch (err) { + console.error('Error loading statistics:', err); + return { success: false } } } ) @@ -411,19 +411,25 @@ export const statistics = createSlice({ name: ACTION_NAME, initialState, reducers: { - setCurrentMembersGeneralOverviewDuration : (state , action: PayloadAction<'thisWeek' | 'thisMonth' | 'thisYear'>) => { + setCurrentMembersGeneralOverviewDuration: (state, action: PayloadAction<'thisWeek' | 'thisMonth' | 'thisYear'>) => { state.value.currentMembersGeneralOverviewDuration = action.payload } }, extraReducers: (builder) => { // load ability - builder.addCase(load.pending, (state : IinitialState , action) => { + builder.addCase(load.pending, (state: IinitialState, action) => { state.value.isLoading = true; }) - builder.addCase(load.fulfilled, (state : IinitialState , action) => { + builder.addCase(load.fulfilled, (state: IinitialState, action) => { state.value.isLoading = false; - state.value.loadedFirstTime = true; - state.value.report = action.payload.data; + if (action.payload?.success && action.payload.data) { + state.value.loadedFirstTime = true; + state.value.report = action.payload.data; + } + }) + builder.addCase(load.rejected, (state: IinitialState, action) => { + state.value.isLoading = false; + console.error('Failed to load statistics:', action.error); }) } })