fix: dashboard api calls, multi profile fetch

This commit is contained in:
Sajad Mirjalili
2025-08-21 14:51:56 +03:30
parent 4d84a29bda
commit 16fa6615c9
24 changed files with 656 additions and 679 deletions

View File

@@ -9,7 +9,6 @@ import {
Button,
Link,
CircularProgress,
Typography,
InputAdornment,
} from '@mui/material';
import { CloseCircle, Eye, EyeSlash } from 'iconsax-react';
@@ -18,6 +17,7 @@ import { type PasswordDialogProps } from '../../types/settingsType';
import { PasswordValidationItem } from './PasswordValidation';
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
export function PasswordDialog({
open,
@@ -34,13 +34,13 @@ export function PasswordDialog({
loading,
handleSubmit,
changePassword,
apiError,
t,
hasMinLength,
hasNumber,
hasUpperAndLower,
hasSpecialChar,
}: PasswordDialogProps) {
const { t } = useTranslation('setting');
const [showCurrent, setShowCurrent] = useState(false);
const [showNew, setShowNew] = useState(false);
const [showConfirm, setShowConfirm] = useState(false);
@@ -77,12 +77,6 @@ export function PasswordDialog({
px: { xs: 2, sm: 3 },
}}
>
{apiError && (
<Typography color="error" variant="body2" textAlign="center">
{apiError}
</Typography>
)}
{changePassword && (
<>
<TextField

View File

@@ -1,4 +1,4 @@
import { useState, useEffect, useMemo } from 'react';
import { useState, useEffect } from 'react';
import {
Box,
Typography,
@@ -12,16 +12,13 @@ import { CardContainer } from '@/components/CardContainer';
import { PageWrapper } from '../PageWrapper';
import { PasswordDialog } from './PasswordDialog';
import { useApi } from '@/hooks/useApi';
import {
addPassword,
changePassword,
fetchProfile,
} from '../../api/settingsApi';
import { addPassword, changePassword } from '../../api/settingsApi';
import { containsNumber } from '@/utils/regexes/containsNumber';
import { least8Chars } from '@/utils/regexes/least8Chars';
import { hasUpperAndLowerLetter } from '@/utils/regexes/hasUpperAndLowerLetter';
import { containsSymbol } from '@/utils/regexes/containsSymbol';
import { useToast } from '@rkheftan/harmony-ui';
import { useProfile } from '../../hooks/useProfile';
export function PasswordSecurity() {
const theme = useTheme();
@@ -45,31 +42,33 @@ export function PasswordSecurity() {
const matchPassword = password === confirmPassword;
const showToast = useToast();
const {
data: profileData,
loading: isFetching,
error: fetchError,
} = useApi(fetchProfile, { immediate: true });
const { isLoadingProfile, refetchProfile } = useProfile();
const {
data: addData,
loading: isAdding,
error: addError,
execute: executeAddPassword,
} = useApi(addPassword);
const { loading: isAdding, execute: executeAddPassword } =
useApi(addPassword);
const {
data: changeData,
loading: isChanging,
error: changeError,
execute: executeChangePassword,
} = useApi(changePassword);
const { loading: isChanging, execute: executeChangePassword } =
useApi(changePassword);
useEffect(() => {
if (profileData?.success) {
setChangePasswordUI(profileData.hasPassword || false);
}
}, [profileData]);
const loadProfile = async () => {
const profileData = await refetchProfile();
if (!profileData) return;
if (profileData.success) {
setChangePasswordUI(profileData.hasPassword || false);
} else {
showToast({
message: profileData.message || t('message.serverError'),
severity: 'error',
});
}
};
loadProfile();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (password) {
@@ -79,30 +78,6 @@ export function PasswordSecurity() {
}
}, [password, validPassword]);
useEffect(() => {
if (addData?.success || changeData?.success) {
showToast({
message: changePasswordUI
? t('securityForm.passwordChanged')
: t('securityForm.passwordAdded'),
severity: 'success',
});
if (!changePasswordUI) {
setChangePasswordUI(true);
}
setOpen(false);
setPassword('');
setConfirmPassword('');
setCurrentPassword('');
} else if (addData || changeData) {
showToast({
message:
addData?.message || changeData?.message || t('securityForm.error'),
severity: 'error',
});
}
}, [addData, changeData, changePasswordUI, showToast, t]);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
@@ -115,26 +90,48 @@ export function PasswordSecurity() {
return;
}
if (changePasswordUI) {
await executeChangePassword({
const changeData = await executeChangePassword({
oldPassword: currentPassword,
newPassword: password,
});
if (!changeData) return;
if (!changeData.success) {
showToast({
message: changeData?.message || t('securityForm.error'),
severity: 'error',
});
return;
}
} else {
await executeAddPassword({ newPassword: password });
const addData = await executeAddPassword({ newPassword: password });
if (!addData) return;
if (!addData.success) {
showToast({
message: addData?.message || t('securityForm.error'),
severity: 'error',
});
return;
}
}
showToast({
message: changePasswordUI
? t('securityForm.passwordChanged')
: t('securityForm.passwordAdded'),
severity: 'success',
});
if (!changePasswordUI) {
setChangePasswordUI(true);
}
setOpen(false);
setPassword('');
setConfirmPassword('');
setCurrentPassword('');
};
const getErrorMessage = (error: unknown): string | null => {
if (!error) return null;
if (error instanceof Error) return error.message;
return String(error);
};
const apiError = useMemo(
() => addError || changeError,
[addError, changeError],
);
return (
<PageWrapper>
<CardContainer
@@ -153,7 +150,7 @@ export function PasswordSecurity() {
</Button>
}
>
{isFetching ? (
{isLoadingProfile ? (
<Box
sx={{
display: 'flex',
@@ -165,12 +162,6 @@ export function PasswordSecurity() {
>
<CircularProgress />
</Box>
) : fetchError ? (
<Box sx={{ textAlign: 'center', p: 4 }}>
<Typography color="error.main">
{getErrorMessage(fetchError)}
</Typography>
</Box>
) : (
<Box sx={{ px: 4, py: 2 }}>
{changePasswordUI ? (
@@ -208,8 +199,6 @@ export function PasswordSecurity() {
loading={isAdding || isChanging}
handleSubmit={handlePasswordSubmit}
changePassword={changePasswordUI}
apiError={getErrorMessage(apiError)}
t={t}
hasMinLength={hasMinLength}
hasNumber={hasNumber}
hasUpperAndLower={hasUpperAndLower}

View File

@@ -9,11 +9,10 @@ import {
import { useTranslation } from 'react-i18next';
import { CardContainer } from '@/components/CardContainer';
import { PageWrapper } from '../PageWrapper';
import { fetchProfile } from '../../api/settingsApi';
import type { LoginLog } from '../../types/settingsApiType';
import { useApi } from '@/hooks/useApi';
import { formatDate } from '@/utils/formatSessionDate';
import { useToast } from '@rkheftan/harmony-ui';
import { useProfile } from '../../hooks/useProfile';
export function RecentLogins() {
const { t, i18n } = useTranslation('setting');
@@ -22,31 +21,43 @@ export function RecentLogins() {
const isXsup = useMediaQuery(theme.breakpoints.up('xs'));
const showToast = useToast();
const {
data: profileData,
loading: isLoading,
error: fetchError,
} = useApi(fetchProfile, { immediate: true });
const { isLoadingProfile, refetchProfile } = useProfile();
// useEffect(() => {
// if (profileData?.success && Array.isArray(profileData.loginLogs)) {
// setLogs(profileData.loginLogs);
// }
// }, [profileData]);
// useEffect(() => {
// if (fetchError) {
// showToast({
// message: getErrorMessage(fetchError) || t('active.errorFetch'),
// severity: 'error',
// });
// }
// }, [fetchError, t, showToast]);
useEffect(() => {
if (profileData?.success && Array.isArray(profileData.loginLogs)) {
setLogs(profileData.loginLogs);
}
}, [profileData]);
const loadProfile = async () => {
const profileData = await refetchProfile();
useEffect(() => {
if (fetchError) {
showToast({
message: getErrorMessage(fetchError) || t('active.errorFetch'),
severity: 'error',
});
}
}, [fetchError, t, showToast]);
const getErrorMessage = (error: unknown): string | null => {
if (!error) return null;
if (error instanceof Error) return error.message;
return String(error);
};
if (!profileData) return;
if (profileData.success) {
if (!Array.isArray(profileData.loginLogs)) return;
setLogs(profileData.loginLogs);
} else {
showToast({
message: profileData.message || t('active.errorFetch'),
severity: 'error',
});
}
};
loadProfile();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<PageWrapper>
@@ -54,7 +65,7 @@ export function RecentLogins() {
title={t('securityForm.recentLogins')}
subtitle={t('securityForm.description')}
>
{isLoading ? (
{isLoadingProfile ? (
<Box
sx={{
display: 'flex',
@@ -66,12 +77,6 @@ export function RecentLogins() {
>
<CircularProgress />
</Box>
) : fetchError ? (
<Box sx={{ textAlign: 'center', p: 4 }}>
<Typography color="error.main">
{getErrorMessage(fetchError) || t('active.errorFetch')}
</Typography>
</Box>
) : (
<Box sx={{ px: 4 }}>
{logs.map((log, index) => (