Compare commits

...

2 Commits

Author SHA1 Message Date
c8828e7e5f Add 30 n1234567 2025-06-21 04:53:43 +03:00
489474e9d7 Add 30 n123456 2025-06-21 04:46:37 +03:00
3 changed files with 100 additions and 51 deletions

View File

@ -11,7 +11,7 @@ export default function IncomeOutcome()
{ {
// get needed redux state // get needed redux state
const report = useAppSelector((state) => state.statisticsReducer.value.report); const report = useAppSelector((state) => state.statisticsReducer.value.report);
const currencySymbol = useAppSelector((state) => state.settingsReducer.value.appGeneralSettings.currencySymbol || '$'); const settings = useAppSelector((state) => state.settingsReducer.value);
const themeType = useAppSelector((state) => state.themeTypeReducer.value.themeType); const themeType = useAppSelector((state) => state.themeTypeReducer.value.themeType);
const [isClient, setIsClient] = useState(false); const [isClient, setIsClient] = useState(false);
@ -27,8 +27,8 @@ export default function IncomeOutcome()
setIsClient(true); setIsClient(true);
}, []); }, []);
// if loading or no report, don't render the chart // if loading or no report or settings not loaded, show loading spinner
if (!report || !isClient) { if (!report || !isClient || !settings?.appGeneralSettings) {
return ( return (
<div className="w-full h-[350px] flex items-center justify-center"> <div className="w-full h-[350px] flex items-center justify-center">
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-gray-900 dark:border-gray-100"></div> <div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-gray-900 dark:border-gray-100"></div>
@ -36,6 +36,9 @@ export default function IncomeOutcome()
); );
} }
// Get currency symbol with fallback to '$' if not available
const currencySymbol = settings.appGeneralSettings.currencySymbol || '$';
// prepare chart data // prepare chart data
const data = [report?.totalIncome || 0, report?.totalOutcome || 0]; const data = [report?.totalIncome || 0, report?.totalOutcome || 0];
// prepare chart labels // prepare chart labels

View File

@ -2,6 +2,7 @@ import { useAppSelector } from '@/redux/store';
import { useTranslations } from 'next-intl'; import { useTranslations } from 'next-intl';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Cookies from 'universal-cookie'; import Cookies from 'universal-cookie';
import { useState, useEffect } from 'react';
const ReactApexChart = dynamic(() => import('react-apexcharts'), { const ReactApexChart = dynamic(() => import('react-apexcharts'), {
ssr: false, ssr: false,
}); });
@ -9,34 +10,56 @@ const ReactApexChart = dynamic(() => import('react-apexcharts'), {
export default function ServicesSubscriptions() export default function ServicesSubscriptions()
{ {
// get redux needed state // get redux needed state
const report = useAppSelector((state) => state.statisticsReducer.value.report) const report = useAppSelector((state) => state.statisticsReducer.value.report);
const themeType = useAppSelector((state) => state.themeTypeReducer.value.themeType) const themeType = useAppSelector((state) => state.themeTypeReducer.value.themeType);
const [isClient, setIsClient] = useState(false);
// declare global variables // declare global variables
const cookies = new Cookies(); const cookies = new Cookies();
const t = useTranslations('statistics'); const t = useTranslations('statistics');
const locale = cookies.get("NEXT_LOCALE") const locale = cookies.get("NEXT_LOCALE");
const isRtl = locale == 'ar' ? true : false const isRtl = locale === 'ar';
const isDark = themeType == 'dark' ? true : false const isDark = themeType === 'dark';
// if loading show load screen
if(!report) return // Set isClient to true after component mounts
// setup chart data useEffect(() => {
let data = report?.servicesNameAndSubscribers.value.map((v : { setIsClient(true);
_id: string, }, []);
name: string,
totalSubscribers: number, // if loading or no report, show loading spinner
} , i : number) => { if (!report || !isClient) {
return v?.totalSubscribers; return (
}) <div className="w-full h-[350px] flex items-center justify-center">
// setup chart labels <div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-gray-900 dark:border-gray-100"></div>
let labels = report?.servicesNameAndSubscribers.value.map((v : { </div>
_id: string, );
name: string, }
totalSubscribers: number,
} , i : number) => { // setup chart data with null checks
return v?.name; const data = report?.servicesNameAndSubscribers?.value?.map((v: {
}) _id: string;
name: string;
totalSubscribers: number;
}) => v?.totalSubscribers) || [];
// setup chart labels with null checks
const labels = report?.servicesNameAndSubscribers?.value?.map((v: {
_id: string;
name: string;
totalSubscribers: number;
}) => v?.name) || [];
// Return empty state if no data
if (data.length === 0 || labels.length === 0) {
return (
<div className="w-full h-[350px] flex items-center justify-center">
<p>{t('noDataAvailable')}</p>
</div>
);
}
// setup chart options // setup chart options
var options = { const options = {
series: [{ series: [{
name: t('subscription'), name: t('subscription'),
data: data data: data

View File

@ -2,6 +2,7 @@ import { useAppSelector } from '@/redux/store';
import { useTranslations } from 'next-intl'; import { useTranslations } from 'next-intl';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import Cookies from 'universal-cookie'; import Cookies from 'universal-cookie';
import { useState, useEffect } from 'react';
const ReactApexChart = dynamic(() => import('react-apexcharts'), { const ReactApexChart = dynamic(() => import('react-apexcharts'), {
ssr: false, ssr: false,
}); });
@ -9,34 +10,56 @@ const ReactApexChart = dynamic(() => import('react-apexcharts'), {
export default function WorkersJobTypes() export default function WorkersJobTypes()
{ {
// get needed redux state // get needed redux state
const report = useAppSelector((state) => state.statisticsReducer.value.report) const report = useAppSelector((state) => state.statisticsReducer.value.report);
const themeType = useAppSelector((state) => state.themeTypeReducer.value.themeType) const themeType = useAppSelector((state) => state.themeTypeReducer.value.themeType);
const [isClient, setIsClient] = useState(false);
// declare global variables // declare global variables
const cookies = new Cookies(); const cookies = new Cookies();
const t = useTranslations('statistics'); const t = useTranslations('statistics');
const locale = cookies.get("NEXT_LOCALE") const locale = cookies.get("NEXT_LOCALE");
const isRtl = locale == 'ar' ? true : false const isRtl = locale === 'ar';
const isDark = themeType == 'dark' ? true : false const isDark = themeType === 'dark';
// if loading show load screen
if(!report) return // Set isClient to true after component mounts
// prepare chart data useEffect(() => {
let data = report?.workersNameAndJobType.value.map((v : { setIsClient(true);
_id: string, }, []);
jobType: string,
count: number, // if loading or no report, show loading spinner
} , i : number) => { if (!report || !isClient) {
return v?.count; return (
}) <div className="w-full h-[350px] flex items-center justify-center">
// prepare chart labels <div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-gray-900 dark:border-gray-100"></div>
let labels = report?.workersNameAndJobType.value.map((v : { </div>
_id: string, );
jobType: string, }
count: number,
} , i : number) => { // prepare chart data with null checks
return v?.jobType; const data = report?.workersNameAndJobType?.value?.map((v: {
}) _id: string;
jobType: string;
count: number;
}) => v?.count) || [];
// prepare chart labels with null checks
const labels = report?.workersNameAndJobType?.value?.map((v: {
_id: string;
jobType: string;
count: number;
}) => v?.jobType) || [];
// Return empty state if no data
if (data.length === 0 || labels.length === 0) {
return (
<div className="w-full h-[350px] flex items-center justify-center">
<p>{t('noDataAvailable')}</p>
</div>
);
}
// prepare chart options // prepare chart options
var options = { const options = {
series: [{ series: [{
name: t('worker'), name: t('worker'),
data: data data: data