Add 30 n12345688400

This commit is contained in:
yznahmad 2025-06-21 16:17:36 +03:00
parent 672b965f4d
commit 071d586f29
2 changed files with 66 additions and 41 deletions

View File

@ -1,6 +1,6 @@
'use client'; 'use client';
import { useEffect } from 'react' import { useEffect, useCallback } from 'react'
import GeneralLook from '@/components/dashboard/home/generalLook' import GeneralLook from '@/components/dashboard/home/generalLook'
import MembersOverviewChart from '@/components/dashboard/home/membersOverviewChart' import MembersOverviewChart from '@/components/dashboard/home/membersOverviewChart'
import ServicesSubscriptions from '@/components/dashboard/home/servicesSubscriptions' import ServicesSubscriptions from '@/components/dashboard/home/servicesSubscriptions'
@ -12,41 +12,60 @@ import { AppDispatch } from '@/redux/store';
import { load } from '@/redux/features/statistics-slice' import { load } from '@/redux/features/statistics-slice'
import CircularProgress from '@mui/material/CircularProgress'; import CircularProgress from '@mui/material/CircularProgress';
import { useTranslations } from 'next-intl'; import { useTranslations } from 'next-intl';
import { Button } from '@/components/ui/button';
import { RefreshCw } from 'lucide-react';
export default function Page() { export default function Page() {
const dispatch = useDispatch<AppDispatch>() const dispatch = useDispatch<AppDispatch>()
const t = useTranslations('statistics'); const t = useTranslations('statistics');
const report = useAppSelector((state) => state.statisticsReducer.value.report) const report = useAppSelector((state) => state.statisticsReducer.value.report)
const loadedFirstTime = useAppSelector((state) => state.statisticsReducer.value.loadedFirstTime)
const isLoading = useAppSelector((state) => state.statisticsReducer.value.isLoading) const isLoading = useAppSelector((state) => state.statisticsReducer.value.isLoading)
useEffect(() => { const fetchData = useCallback(async () => {
if(loadedFirstTime) return; try {
if(isLoading) return; await dispatch(load({ forceRefresh: true }));
async function a() } catch (error) {
{ console.error('Failed to fetch dashboard data:', error);
await dispatch(load({}))
} }
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 ( return (
<> <div className="w-full h-[100vh] flex items-center justify-center gap-5">
<div className="w-full h-[100vh] flex items-center justify-center gap-5"> <CircularProgress color="secondary" size={22} />
<CircularProgress color="secondary" size={22} /> <h1 className="dark:text-text-light text-text">{t('loading')}...</h1>
<h1 className="dark:text-text-light text-text">{t('loading')}...</h1> </div>
</div>
</>
) )
} }
return ( return (
<> <>
<main className="min-h-[250px] max-h-[750px]"> <main className="min-h-[250px] max-h-[750px]">
<div className="w-full flex justify-end mb-4">
<Button
variant="outline"
size="sm"
onClick={fetchData}
disabled={isLoading}
className="flex items-center gap-2"
>
<RefreshCw className={`h-4 w-4 ${isLoading ? 'animate-spin' : ''}`} />
{t('refresh')}
</Button>
</div>
<main className="w-full min-w-screen min-h-screen h-auto flex flex-col lg:gap-7 gap-3 pb-[50px]"> <main className="w-full min-w-screen min-h-screen h-auto flex flex-col lg:gap-7 gap-3 pb-[50px]">
<section className="w-full flex lg:flex-row flex-col-reverse lg:gap-7 gap-3 lg:h-auto lg:min-h-[500px] items-stretch"> <section className="w-full flex lg:flex-row flex-col-reverse lg:gap-7 gap-3 lg:h-auto lg:min-h-[500px] items-stretch">
<GeneralLook/> <GeneralLook/>

View File

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